#pragma once #include #include #include #include #include "rendering/animation/footstep_driver.hpp" #include "rendering/animation/sfx_state_driver.hpp" #include "rendering/animation/weapon_type.hpp" #include "rendering/animation/anim_capability_set.hpp" #include "rendering/animation/character_animator.hpp" namespace wowee { namespace rendering { class Renderer; // ============================================================================ // AnimationController — thin adapter wrapping CharacterAnimator // // Bridges the Renderer world (camera state, CharacterRenderer, audio) // and the pure-logic CharacterAnimator + sub-FSMs. Public API unchanged. // // Responsibilities: // · Collect inputs from renderer/camera → CharacterAnimator::FrameInput // · Forward state-changing calls → CharacterAnimator // · Read AnimOutput from CharacterAnimator → apply via CharacterRenderer // · Mount discovery (needs renderer for sequence queries) // · Mount positioning (needs renderer for attachment queries) // · Melee/ranged resolution (needs renderer for sequence queries) // · Footstep and SFX drivers (already extracted) // ============================================================================ class AnimationController { public: AnimationController(); ~AnimationController(); void initialize(Renderer* renderer); /// Probe (or re-probe) animation capabilities for the current character model. /// Called once during initialize() / onCharacterFollow() and after model changes. void probeCapabilities(); const AnimCapabilitySet& getCapabilities() const { return characterAnimator_.getCapabilities(); } // ── Per-frame update hooks (called from Renderer::update) ────────────── // Runs the character animation state machine (mounted + unmounted). void updateCharacterAnimation(); // Processes animation-driven footstep events (player + mount). void updateFootsteps(float deltaTime); // Tracks state transitions for activity SFX (jump, landing, swim) and // mount ambient sounds. void updateSfxState(float deltaTime); // Decrements melee swing timer / cooldown. void updateMeleeTimers(float deltaTime); // Store per-frame delta time (used inside animation state machine). void setDeltaTime(float dt) { lastDeltaTime_ = dt; } // ── Character follow ─────────────────────────────────────────────────── void onCharacterFollow(uint32_t instanceId); // ── Emote support ────────────────────────────────────────────────────── void playEmote(const std::string& emoteName); void cancelEmote(); bool isEmoteActive() const { return characterAnimator_.getActivity().isEmoteActive(); } static std::string getEmoteText(const std::string& emoteName, const std::string* targetName = nullptr); static uint32_t getEmoteDbcId(const std::string& emoteName); static std::string getEmoteTextByDbcId(uint32_t dbcId, const std::string& senderName, const std::string* targetName = nullptr); static uint32_t getEmoteAnimByDbcId(uint32_t dbcId); // ── Targeting / combat ───────────────────────────────────────────────── void setTargetPosition(const glm::vec3* pos); void setInCombat(bool combat); bool isInCombat() const { return inCombat_; } const glm::vec3* getTargetPosition() const { return targetPosition_; } void resetCombatVisualState(); bool isMoving() const; // ── Melee combat ─────────────────────────────────────────────────────── void triggerMeleeSwing(); /// inventoryType: WoW inventory type (0=unarmed, 13=1H, 17=2H, 21=main-hand, …) /// is2HLoose: true for polearms/staves (use ATTACK_2H_LOOSE instead of ATTACK_2H) void setEquippedWeaponType(uint32_t inventoryType, bool is2HLoose = false, bool isFist = false, bool isDagger = false, bool hasOffHand = false, bool hasShield = false); /// Play a special attack animation for a melee ability (spellId → SPECIAL_1H/2H/SHIELD_BASH/WHIRLWIND) void triggerSpecialAttack(uint32_t spellId); // ── Sprint aura animation ──────────────────────────────────────────── void setSprintAuraActive(bool active); // ── Ranged combat ────────────────────────────────────────────────────── void setEquippedRangedType(RangedWeaponType type); /// Trigger a ranged shot animation (Auto Shot, Shoot, Throw) void triggerRangedShot(); RangedWeaponType getEquippedRangedType() const { return weaponLoadout_.rangedType; } void setCharging(bool charging); bool isCharging() const { return charging_; } // ── Spell casting ────────────────────────────────────────────────────── /// Enter spell cast animation sequence: /// precastAnimId (one-shot wind-up) → castAnimId (looping hold) → finalizeAnimId (one-shot release) /// Any phase can be 0 to skip it. void startSpellCast(uint32_t precastAnimId, uint32_t castAnimId, bool castLoop, uint32_t finalizeAnimId = 0); /// Leave spell cast animation state → plays finalization anim then idle. void stopSpellCast(); // ── Loot animation ───────────────────────────────────────────────────── void startLooting(); void stopLooting(); // ── Hit reactions ────────────────────────────────────────────────────── /// Play a one-shot hit reaction animation (wound, dodge, block, etc.) /// on the player character. The state machine returns to the previous /// state once the reaction animation finishes. void triggerHitReaction(uint32_t animId); // ── Crowd control ────────────────────────────────────────────────────── /// Enter/exit stunned state (loops STUN animation until cleared). void setStunned(bool stunned); bool isStunned() const { return stunned_; } // ── Health-based idle ────────────────────────────────────────────────── /// When true, idle/combat-idle will prefer STAND_WOUND if the model has it. void setLowHealth(bool low); // ── Stand state (sit/sleep/kneel transitions) ────────────────────────── // WoW UnitStandStateType constants static constexpr uint8_t STAND_STATE_STAND = 0; static constexpr uint8_t STAND_STATE_SIT = 1; static constexpr uint8_t STAND_STATE_SIT_CHAIR = 2; static constexpr uint8_t STAND_STATE_SLEEP = 3; static constexpr uint8_t STAND_STATE_SIT_LOW = 4; static constexpr uint8_t STAND_STATE_SIT_MED = 5; static constexpr uint8_t STAND_STATE_SIT_HIGH = 6; static constexpr uint8_t STAND_STATE_DEAD = 7; static constexpr uint8_t STAND_STATE_KNEEL = 8; static constexpr uint8_t STAND_STATE_SUBMERGED = 9; void setStandState(uint8_t state); // ── Stealth ──────────────────────────────────────────────────────────── /// When true, idle/walk/run use stealth animation variants. void setStealthed(bool stealth); // ── Effect triggers ──────────────────────────────────────────────────── void triggerLevelUpEffect(const glm::vec3& position); void startChargeEffect(const glm::vec3& position, const glm::vec3& direction); void emitChargeEffect(const glm::vec3& position, const glm::vec3& direction); void stopChargeEffect(); // ── Mount ────────────────────────────────────────────────────────────── void setMounted(uint32_t mountInstId, uint32_t mountDisplayId, float heightOffset, const std::string& modelPath = ""); void setTaxiFlight(bool onTaxi) { taxiFlight_ = onTaxi; } void setMountPitchRoll(float pitch, float roll) { mountPitch_ = pitch; mountRoll_ = roll; } void clearMount(); bool isMounted() const { return mountInstanceId_ != 0; } uint32_t getMountInstanceId() const { return mountInstanceId_; } // ── Query helpers (used by Renderer) ─────────────────────────────────── bool isFootstepAnimationState() const; float getMeleeSwingTimer() const { return meleeSwingTimer_; } float getMountHeightOffset() const { return mountHeightOffset_; } bool isTaxiFlight() const { return taxiFlight_; } private: Renderer* renderer_ = nullptr; // ── CharacterAnimator: owns the complete character animation FSM ───── CharacterAnimator characterAnimator_; bool capabilitiesProbed_ = false; // ── Animation change detection ─────────────────────────────────────── uint32_t lastPlayerAnimRequest_ = UINT32_MAX; bool lastPlayerAnimLoopRequest_ = true; float lastDeltaTime_ = 0.0f; // ── Externally-queried state (mirrored to CharacterAnimator) ──────────── const glm::vec3* targetPosition_ = nullptr; bool inCombat_ = false; bool stunned_ = false; bool charging_ = false; // ── Footstep event tracking (delegated to FootstepDriver) ──────────── FootstepDriver footstepDriver_; // ── SFX transition state (delegated to SfxStateDriver) ─────────────── SfxStateDriver sfxStateDriver_; // ── Melee combat (needs renderer for sequence queries) ─────────────── float meleeSwingTimer_ = 0.0f; float meleeSwingCooldown_ = 0.0f; float meleeAnimDurationMs_ = 0.0f; uint32_t meleeAnimId_ = 0; uint32_t specialAttackAnimId_ = 0; WeaponLoadout weaponLoadout_; bool meleeOffHandTurn_ = false; // ── Ranged weapon state ────────────────────────────────────────────── float rangedShootTimer_ = 0.0f; uint32_t rangedAnimId_ = 0; // ── Mount state (discovery + positioning need renderer) ────────────── uint32_t mountInstanceId_ = 0; float mountHeightOffset_ = 0.0f; float mountPitch_ = 0.0f; float mountRoll_ = 0.0f; // External roll for taxi flights (lean roll computed by MountFSM) int mountSeatAttachmentId_ = -1; glm::vec3 smoothedMountSeatPos_ = glm::vec3(0.0f); bool mountSeatSmoothingInit_ = false; bool taxiFlight_ = false; bool sprintAuraActive_ = false; // ── Private helpers ────────────────────────────────────────────────── uint32_t resolveMeleeAnimId(); void updateMountedAnimation(float deltaTime); void applyMountPositioning(float mountBob, float mountRoll, float characterYaw); }; } // namespace rendering } // namespace wowee