#pragma once #include "rendering/animation/i_character_animator.hpp" #include "rendering/animation/anim_capability_set.hpp" #include "rendering/animation/locomotion_fsm.hpp" #include "rendering/animation/combat_fsm.hpp" #include "rendering/animation/activity_fsm.hpp" #include "rendering/animation/mount_fsm.hpp" #include namespace wowee { namespace rendering { // ============================================================================ // CharacterAnimator // // Generic animator for any character (player, NPC, companion). // Composes LocomotionFSM, CombatFSM, ActivityFSM, and MountFSM with // a priority resolver. Implements ICharacterAnimator. // // Priority order: Stun > HitReaction > Spell > Charge > Combat > Activity > Locomotion // // No idle fallback: if all FSMs return {valid=false}, last animation continues. // // Overlay layer (stealth, sprint) substitutes the resolved anim without // changing sub-FSM state. // ============================================================================ class CharacterAnimator final : public ICharacterAnimator { public: CharacterAnimator(); // ── IAnimator ─────────────────────────────────────────────────────── void onEvent(AnimEvent event) override; void update(float dt) override; // ── ICharacterAnimator ────────────────────────────────────────────── void startSpellCast(uint32_t precast, uint32_t cast, bool loop, uint32_t finalize) override; void stopSpellCast() override; void triggerMeleeSwing() override; void triggerRangedShot() override; void triggerHitReaction(uint32_t animId) override; void triggerSpecialAttack(uint32_t spellId) override; void setEquippedWeaponType(const WeaponLoadout& loadout) override; void setEquippedRangedType(RangedWeaponType type) override; void playEmote(uint32_t animId, bool loop) override; void cancelEmote() override; void startLooting() override; void stopLooting() override; void setStunned(bool stunned) override; void setCharging(bool charging) override; void setStandState(uint8_t state) override; void setStealthed(bool stealth) override; void setInCombat(bool combat) override; void setLowHealth(bool low) override; void setSprintAuraActive(bool active) override; // ── Configuration ─────────────────────────────────────────────────── void setCapabilities(const AnimCapabilitySet& caps) { caps_ = caps; } const AnimCapabilitySet& getCapabilities() const { return caps_; } void setWeaponLoadout(const WeaponLoadout& loadout) { loadout_ = loadout; } const WeaponLoadout& getWeaponLoadout() const { return loadout_; } // ── Mount ─────────────────────────────────────────────────────────── void configureMountFSM(const MountFSM::MountAnimSet& anims, bool taxiFlight); void clearMountFSM(); bool isMountActive() const { return mount_.isActive(); } MountFSM& getMountFSM() { return mount_; } // ── Sub-FSM access (for transition queries) ───────────────────────── LocomotionFSM& getLocomotion() { return locomotion_; } const LocomotionFSM& getLocomotion() const { return locomotion_; } CombatFSM& getCombat() { return combat_; } const CombatFSM& getCombat() const { return combat_; } ActivityFSM& getActivity() { return activity_; } const ActivityFSM& getActivity() const { return activity_; } // ── Last resolved output ──────────────────────────────────────────── AnimOutput getLastOutput() const { return lastOutput_; } // ── Input injection (set per-frame from AnimationController) ──────── struct FrameInput { // From camera controller bool moving = false; bool sprinting = false; bool movingForward = false; bool movingBackward = false; bool autoRunning = false; bool strafeLeft = false; bool strafeRight = false; bool grounded = true; bool jumping = false; bool swimming = false; bool sitting = false; bool flyingActive = false; bool ascending = false; bool descending = false; bool jumpKeyPressed = false; float characterYaw = 0.0f; // Melee/ranged timers float meleeSwingTimer = 0.0f; float rangedShootTimer = 0.0f; uint32_t specialAttackAnimId = 0; uint32_t rangedAnimId = 0; // Animation state query uint32_t currentAnimId = 0; float currentAnimTime = 0.0f; float currentAnimDuration = 0.0f; bool haveAnimState = false; // Mount state query uint32_t curMountAnim = 0; float curMountTime = 0.0f; float curMountDuration = 0.0f; bool haveMountState = false; }; void setFrameInput(const FrameInput& input) { frameInput_ = input; } private: AnimCapabilitySet caps_; WeaponLoadout loadout_; LocomotionFSM locomotion_; CombatFSM combat_; ActivityFSM activity_; MountFSM mount_; // Overlay flags bool stealthed_ = false; bool sprintAura_ = false; bool lowHealth_ = false; bool inCombat_ = false; float lastDt_ = 0.0f; FrameInput frameInput_; AnimOutput lastOutput_; /// Priority resolver: highest-priority active FSM wins. AnimOutput resolveAnimation(); /// Apply stealth/sprint overlays to the resolved animation. AnimOutput applyOverlays(AnimOutput base) const; }; } // namespace rendering } // namespace wowee