Fix classic combat desync and separate attack intent from confirmed combat

Combat state/UI:

- Split attack intent from server-confirmed auto attack in GameHandler.

- Add explicit combat state helpers (intent, confirmed, combat-with-target).

- Update player/target frame and renderer in-combat indicators to use confirmed combat.

- Add intent-only visual state in UI to avoid false combat feedback.

Animation correctness:

- Correct combat-ready animation IDs to canonical ready stances (22/23/24/25).

- Remove hit/wound-like stance behavior caused by incorrect ready IDs.

Classic/TBC/Turtle melee reliability:

- Tighten melee sync while attack intent is active: faster swing resend, tighter facing threshold, and faster facing cadence for classic-like expansions.

- Send immediate movement heartbeat after facing corrections and during stationary melee sync windows.

- Handle melee error opcodes explicitly (NOTINRANGE/BADFACING/NOTSTANDING/CANT_ATTACK) and immediately resync facing/swing where appropriate.

- On ATTACKSTOP with persistent intent, immediately reassert ATTACKSWING.

- Increase movement heartbeat cadence during active classic-like melee intent.

Server compatibility/anti-cheat stability:

- Restrict legacy force-movement/speed/teleport ACK behavior for classic-like flows to avoid unsolicited order acknowledgements.

- Preserve local movement state updates while preventing bad-order ACK noise.
This commit is contained in:
Kelsi 2026-02-20 03:38:12 -08:00
parent cb044e3985
commit eaba378b5b
4 changed files with 129 additions and 48 deletions

View file

@ -413,6 +413,14 @@ public:
void startAutoAttack(uint64_t targetGuid);
void stopAutoAttack();
bool isAutoAttacking() const { return autoAttacking; }
bool hasAutoAttackIntent() const { return autoAttackRequested_; }
bool isInCombat() const { return autoAttacking || !hostileAttackers_.empty(); }
bool isInCombatWith(uint64_t guid) const {
return guid != 0 &&
((autoAttacking && autoAttackTarget == guid) ||
(hostileAttackers_.count(guid) > 0));
}
uint64_t getAutoAttackTargetGuid() const { return autoAttackTarget; }
bool isAggressiveTowardPlayer(uint64_t guid) const { return hostileAttackers_.count(guid) > 0; }
const std::vector<CombatTextEntry>& getCombatText() const { return combatText; }
void updateCombatText(float deltaTime);
@ -1359,6 +1367,7 @@ private:
// ---- Phase 2: Combat ----
bool autoAttacking = false;
bool autoAttackRequested_ = false; // local intent (CMSG_ATTACKSWING sent)
uint64_t autoAttackTarget = 0;
bool autoAttackOutOfRange_ = false;
float autoAttackResendTimer_ = 0.0f; // Re-send CMSG_ATTACKSWING every ~1s while attacking