mysterymagination / ryddelmyst

cutethulhu meets Castlevania on the battlefields of Final Fantasy Tactics!
GNU General Public License v3.0
2 stars 0 forks source link

Reaper Treant needs to damage player with attack animations #45

Closed mysterymagination closed 1 year ago

mysterymagination commented 2 years ago

For this we'll need separate hit boxes on the arms/hands or something; the single capsulecomponent around the treant isn't good enough.

mysterymagination commented 2 years ago

I added other collision shapes to the arms, head, torso, and legs. I think with collision channel fiddling that should allow for hit and hurtboxes (with extra damage for headshots or shots in the eyes or something). Maybe even something to shut down his arms and/or legs by targeting them.
todo: how do I get the hitboxes and hurtboxes to move with his animations? They are children of his mesh, but does the animation actually count as a mesh transform? Even if it does, there's currently no association between e.g. specifically the right arm section of the mesh and the right arm hitbox; maybe sockets or bone associations for the collision shapes need to be employed? EDIT: yep, just needed to assign bones in the parent socket fields of the collision shapes

mysterymagination commented 2 years ago

todo: add collision channels to allow for hurtboxes (treant takes damage when these are hit by an attack), hitboxes (player takes damage when these connect, at last during an attack action/animation), and both (e.g. arms can both inflict and receive damage)

mysterymagination commented 2 years ago

todo: add attack animation based on behavior tree state

mysterymagination commented 2 years ago

todo: wtf can't I use a TPair in IDefender?! I keep getting the dreaded Error: Unrecognized type 'TPair' - type must be a UCLASS, USTRUCT, UENUM, or global delegate. from UHT. I guess if I can't use that for no reason, then I should change the API so it accepts a damagetype as input and it's up to the impl to have a mapping or something that returns the scaling factor for the given damagetype EDIT: switched over to an input damage type paradigm, which makes more sense anyway. TBD why I couldn't use TPair...

mysterymagination commented 1 year ago

todo: extracting from above, create Weapon and Armor classes that can be IAttacker and IDefenders so that we can effectively equip common weapons e.g. a claw to different primitive constructs like the treant's hands and feet. This way we can have common logic that just gets modified by params despite having to have different PrimitiveComponent subclasses.

mysterymagination commented 1 year ago

Well most of the horrendous build errors I introduced with the refactor are sorted now... except I'm getting a bizarre linker error from anyone implementing IAttacker:

[2/3] Link UnrealEditor-Ryddelmyst.dylib
Undefined symbols for architecture x86_64:
  "vtable for UExtremityUnit", referenced from:
      void InternalConstructor<UExtremityUnit>(FObjectInitializer const&) in Module.Ryddelmyst.gen.1_of_3.cpp.o
      UExtremityUnit::UExtremityUnit(FVTableHelper&) in Module.Ryddelmyst.gen.1_of_3.cpp.o
      UExtremityUnit::UExtremityUnit(FVTableHelper&) in Module.Ryddelmyst.gen.1_of_3.cpp.o
      UExtremityUnit::__VTableCtorCaller(FVTableHelper&) in Module.Ryddelmyst.gen.1_of_3.cpp.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "vtable for UClawCapsuleComponent", referenced from:
      UClawCapsuleComponent::UClawCapsuleComponent() in Module.Ryddelmyst.cpp.o
      UClawCapsuleComponent::UClawCapsuleComponent(FVTableHelper&) in Module.Ryddelmyst.gen.1_of_3.cpp.o
      UClawCapsuleComponent::UClawCapsuleComponent(FVTableHelper&) in Module.Ryddelmyst.gen.1_of_3.cpp.o
      UClawCapsuleComponent::__VTableCtorCaller(FVTableHelper&) in Module.Ryddelmyst.gen.1_of_3.cpp.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

From what I can tell the IAttacker functions are all (both) implemented, so I don't know what the problem could be. I checked the generated code for the UClawCapsuleComponent class with a missing vtable and the generated code for IAttacker, and it looks ok AFAICS (basically I was just looking for the functions to be the new ones rather than the old ones for that interface, which they were. The rest was generated moonspeak.)

EDIT: I tried removing all the blueprint metadata crap and _Implementation rot from the equation and now things are fine. So the problem is somehow coming from that idiot dance they make you do on https://docs.unrealengine.com/5.0/en-US/interfaces-in-unreal-engine/

EDIT2: aHA!!! Turns out this crazy issue was caused by me duplicating the UFUNCTION metadata in both the interface definition and the implementation function declaration; you need the _Implementation fellas to not have a UFUNCTION macro above them at all apparently.

mysterymagination commented 1 year ago

Hmm, snowballs are not running their OnComponentHit delegate function EDIT: huh turns out you need a function to be decorated with a UFUNCTION() macro for it to work as a function pointer in a function delegate

mysterymagination commented 1 year ago

I added allowprivateaccess=true to the meta tag of armor and exunit props in ExtremityCapsuleComponent and for some reason now they are assignable in-editor, although without the pretty asset preview picture box... whatevs.

For future me, here is the tagging I used: For the UExtremityUnit class: UCLASS(EditInlineNew, Blueprintable, BlueprintType, meta = (DisplayName = "Extremity")) For the ExUnit and Armor properties: UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = "Combat", meta = (AllowPrivateAccess = "true")) and the props are TObjectPtr if that matters.

mysterymagination commented 1 year ago

todo: since the choice of attack event and the attack landing event are separate and async, we can't simply have IAttacker::ExecuteAttack() impls call Weapon::AttackMap["something"]->Attack::OnHit(). We need at least two stages, one in which the attack anim is kicked off and any other immediate effects happen, and then a second where a collision with the attack's associated physicality happens that then calls its OnHit.

mysterymagination commented 1 year ago

todo: bridge anatomy and armor such that armor knows what anatomy is under it; I want to have the armor on the head's impl of calcdmgrx multiply damage by a scaling factor, but I don't currently have any way for the armor function impls to know what anatomy is under them.

mysterymagination commented 1 year ago

todo: implement WoodArmor

mysterymagination commented 1 year ago

todo: add UPrimitive subclasses with IDefender and IAnatomy impls to the Maya character

mysterymagination commented 1 year ago

todo: for some reason I'm not seeing Monster::OnHit() running for Maya's BodyCapsuleHitBox component, only for her CollisionCylinder

todo: maybe just put a pin in this shit for now; maybe it's a UE5 bug? Anyway, we can technically get along with what we have for the gameplay we want vis a vis multi-part natural weapon monster's attacks collide with the player's root cylinder and the player's projectiles collide with the monster's multi-part anatomy. The only thing we couldn't do is enable Maya's might high kicks to hit monster bits or for monsters to in-fight smashing bits upon other bits... but that's not really required for the main gameplay I had in mind.

mysterymagination commented 1 year ago

todo: create a subclass of UActorComponent that implements the common OnHit handling of the Clash API

mysterymagination commented 1 year ago

todo: calling ProcessCosts() in Cast() isn't always right because Cast() is not cast like cast a spell, but rather it's cast like cast a stone into a lake a.k.a. throw something. Some metamagic modifies spells so that we cast more than one bullet AActor per spell instance, and we should only be incurring the spell cost once, not per-bullet. It would be handy for this and for looking up metadata like spell casting cost and weighing against character MP before we go ahead with manifesting the spell to have a wrapper Spell class that contains the Snowball AActor and all its children plus some basic per-spell instance data and behavior like casting costs and processing those costs. The Attack object probably should stick to the Clash API stuff of delivering calculated damage to a defender etc. and leave the bookkeeping to something more abstract at a higher level.

mysterymagination commented 1 year ago

todo: ideally the ProcessCosts() function would take a callback listener (could be bolted on to BattleStatsBearer interface) that would inform e.g. RyddelmystCharacter that her MP stat had just changed by N so she can respond by updating her UI. EDIT: I just hooked up the UI to the battle stats

mysterymagination commented 1 year ago

todo: Snowball Clash API impl is almost complete, but the pipeline is broke somewhere because the Snowball AActors are not getting destroyed after collision, which should happen unconditionally in SnowballAttack::OnHit_Implementation(). EDIT: forgot to decorate OnHit in HitBoxerComponent with UFUNCTION, which causes it to silently fail as a ufunction delegate.

mysterymagination commented 1 year ago

todo: our UAttack::OnHit_Implementation() boilerplate code assumes the striking component's owner AActor is also a BattleStatsBearer, which is not the case in e.g. projectiles like Snowballs. For Snowball, we save a handle to the spellcaster that launched it, and that handle should be used as the BattleStatsBearer. How do we want to address this?

  1. We could have cases like Snowball re-implement the boilerplate from UAttack::OnHit_Implementation() but with the appropriate thing in for BattleStatsBearer.
  2. We could change the UAttack::OnHit_Implementation() API so that it takes a striking BattleStatsBearer param (may also want to do this for the stricken BattleStatsBearer).
    • May want to add a GetBattleStatsBearer() to IAttacker and IDefender so we can have HitBoxerComponent look that up for IAttacker and UAttack for IDefender to make sure we feed in the BattleStatsBearer AActor for each role in the exchange.
mysterymagination commented 1 year ago

todo: add impls of GetBattler() for applicable IAttacker and IDefender implementors, such as ClawCapsuleComponent and BP_ReaperTreantWoodArmor. TBD if we want/can add meaningful impls directly to the CPP or if it should be a BP thing.

mysterymagination commented 1 year ago

todo: wire up HitBoxerComponent in BP_ReaperTreant

mysterymagination commented 1 year ago

todo: now that Snowball implements the Clash API, what happens if we remove simulate physics on it?

mysterymagination commented 1 year ago

todo: nevermind the query collision between my custom uprims; we only actually need snowballs (a physics actor) colliding with the treant's multiple hitboxes and the treant attacks to register against one thing (default collision cylinder) on Maya. So, we need to wire up the Clash API to Maya's collision cylinder and we should be good enough to go

mysterymagination commented 1 year ago

fixed in https://github.com/mysterymagination/ryddelmyst/commit/bf7c29fc2f03a2d462be7ad25804161f6a735758