mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-07-30 17:29:14 -04:00
fix(animation): keep combat weapons in sync
This commit is contained in:
@@ -1340,7 +1340,27 @@ void Application::update(float deltaTime) {
|
||||
updateCheckpoint = "in_game: auto-unsheathe";
|
||||
if (gameHandler) {
|
||||
const bool autoAttacking = gameHandler->isAutoAttacking();
|
||||
if (autoAttacking && !wasAutoAttacking_ && appearanceComposer_ && appearanceComposer_->isWeaponsSheathed()) {
|
||||
// Keep the attachment state consistent with the ongoing attack, not
|
||||
// just the initial false -> true transition. Z can be pressed after
|
||||
// combat has already started, and pre-WotLK servers briefly send
|
||||
// ATTACKSTOP while the client retains attack intent for a retry.
|
||||
const bool attackWeaponNeeded = autoAttacking || gameHandler->hasAutoAttackIntent();
|
||||
const auto& inventory = gameHandler->getInventory();
|
||||
const auto& mainHand = inventory.getEquipSlot(game::EquipSlot::MAIN_HAND);
|
||||
const auto& offHand = inventory.getEquipSlot(game::EquipSlot::OFF_HAND);
|
||||
const auto& ranged = inventory.getEquipSlot(game::EquipSlot::RANGED);
|
||||
const bool hasOffHandWeapon = !offHand.empty() &&
|
||||
offHand.item.inventoryType == game::InvType::ONE_HAND;
|
||||
const bool hasRangedWeapon = !ranged.empty() &&
|
||||
(ranged.item.inventoryType == game::InvType::RANGED_BOW ||
|
||||
ranged.item.inventoryType == game::InvType::RANGED_GUN ||
|
||||
ranged.item.inventoryType == game::InvType::THROWN);
|
||||
const bool hasDrawableWeapon = !mainHand.empty() || hasOffHandWeapon || hasRangedWeapon;
|
||||
if (attackWeaponNeeded && hasDrawableWeapon && appearanceComposer_ &&
|
||||
appearanceComposer_->isWeaponsSheathed()) {
|
||||
if (renderer && renderer->getAnimationController()) {
|
||||
renderer->getAnimationController()->playWeaponSheathAnimation(false);
|
||||
}
|
||||
appearanceComposer_->setWeaponsSheathed(false);
|
||||
appearanceComposer_->loadEquippedWeapons();
|
||||
}
|
||||
|
||||
@@ -147,8 +147,14 @@ AnimOutput CharacterAnimator::resolveAnimation() {
|
||||
combatIn.currentAnimTime = fi.currentAnimTime;
|
||||
combatIn.currentAnimDuration = fi.currentAnimDuration;
|
||||
combatIn.haveAnimState = fi.haveAnimState;
|
||||
combatIn.hasUnsheathe = caps_.resolvedUnsheathe != 0;
|
||||
combatIn.hasSheathe = caps_.resolvedSheathe != 0;
|
||||
// A character model supporting draw/sheath animations does not mean the
|
||||
// character currently has something to draw. Empty-handed combat must go
|
||||
// directly to READY_UNARMED / ATTACK_UNARMED. Equipped fist weapons still
|
||||
// have inventoryType ONE_HAND and retain their dedicated fist animation path.
|
||||
const bool hasDrawableWeapon = loadout_.inventoryType != game::InvType::NON_EQUIP ||
|
||||
(rangedWeaponActive_ && loadout_.rangedType != RangedWeaponType::NONE);
|
||||
combatIn.hasUnsheathe = hasDrawableWeapon && caps_.resolvedUnsheathe != 0;
|
||||
combatIn.hasSheathe = hasDrawableWeapon && caps_.resolvedSheathe != 0;
|
||||
|
||||
// ── Combat FSM (highest priority for non-mount) ─────────────────────
|
||||
auto combatOut = combat_.resolve(combatIn, caps_, loadout_);
|
||||
|
||||
@@ -14,6 +14,7 @@ static AnimCapabilitySet makeCombatCaps() {
|
||||
caps.resolvedMelee1H = anim::ATTACK_1H;
|
||||
caps.resolvedMelee2H = anim::ATTACK_2H;
|
||||
caps.resolvedMeleeUnarmed = anim::ATTACK_UNARMED;
|
||||
caps.resolvedReadyUnarmed = anim::READY_UNARMED;
|
||||
caps.resolvedStun = anim::STUN;
|
||||
caps.resolvedUnsheathe = anim::UNSHEATHE;
|
||||
caps.resolvedSheathe = anim::SHEATHE;
|
||||
@@ -38,6 +39,42 @@ TEST_CASE("CombatFSM: INACTIVE by default", "[combat]") {
|
||||
CHECK_FALSE(fsm.isActive());
|
||||
}
|
||||
|
||||
TEST_CASE("CombatFSM: unarmed combat uses fist-ready and fist-swing animations", "[combat][unarmed]") {
|
||||
CombatFSM fsm;
|
||||
auto caps = makeCombatCaps();
|
||||
auto loadout = unarmedLoadout();
|
||||
auto in = combatInput();
|
||||
in.hasUnsheathe = false;
|
||||
|
||||
auto ready = fsm.resolve(in, caps, loadout);
|
||||
REQUIRE(ready.valid);
|
||||
CHECK(fsm.getState() == CombatFSM::State::COMBAT_IDLE);
|
||||
CHECK(ready.animId == anim::READY_UNARMED);
|
||||
|
||||
in.meleeSwingTimer = 0.5f;
|
||||
auto swing = fsm.resolve(in, caps, loadout);
|
||||
REQUIRE(swing.valid);
|
||||
CHECK(fsm.getState() == CombatFSM::State::MELEE_SWING);
|
||||
CHECK(swing.animId == anim::ATTACK_UNARMED);
|
||||
}
|
||||
|
||||
TEST_CASE("CombatFSM: equipped fist weapons retain fist-weapon animations", "[combat][fist-weapon]") {
|
||||
CombatFSM fsm;
|
||||
auto caps = makeCombatCaps();
|
||||
caps.resolvedReadyFist = anim::READY_FIST;
|
||||
caps.resolvedMeleeFist = anim::ATTACK_FIST_1H;
|
||||
WeaponLoadout loadout;
|
||||
loadout.inventoryType = wowee::game::InvType::ONE_HAND;
|
||||
loadout.isFist = true;
|
||||
auto in = combatInput();
|
||||
|
||||
fsm.setState(CombatFSM::State::COMBAT_IDLE);
|
||||
CHECK(fsm.resolve(in, caps, loadout).animId == anim::READY_FIST);
|
||||
|
||||
in.meleeSwingTimer = 0.5f;
|
||||
CHECK(fsm.resolve(in, caps, loadout).animId == anim::ATTACK_FIST_1H);
|
||||
}
|
||||
|
||||
TEST_CASE("CombatFSM: INACTIVE → UNSHEATHE on COMBAT_ENTER event", "[combat]") {
|
||||
CombatFSM fsm;
|
||||
fsm.onEvent(AnimEvent::COMBAT_ENTER);
|
||||
|
||||
Reference in New Issue
Block a user