From 0624b0becf636d5ecb6bff205f6a02255a975375 Mon Sep 17 00:00:00 2001 From: balakethelock <111737968+balakethelock@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:27:17 +0100 Subject: [PATCH 1/7] Fix attack timer for DW offset and implement update diff compensation --- src/game/Objects/Unit.cpp | 57 ++++++++++++++++++--------------------- src/game/Objects/Unit.h | 10 ++++--- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index 4db4b552af8..bf0067f4ff3 100644 --- a/src/game/Objects/Unit.cpp +++ b/src/game/Objects/Unit.cpp @@ -291,14 +291,17 @@ void Unit::Update(uint32 update_diff, uint32 p_time) ExtraAttacksLocked(false); } - if (uint32 base_att = GetAttackTimer(BASE_ATTACK)) - SetAttackTimer(BASE_ATTACK, (update_diff >= base_att ? 0 : base_att - update_diff)); + if (int32 base_att = GetAttackTimer(BASE_ATTACK)) + if (base_att > 0) + SetAttackTimer(BASE_ATTACK, base_att - update_diff); - if (uint32 base_att = GetAttackTimer(OFF_ATTACK)) - SetAttackTimer(OFF_ATTACK, (update_diff >= base_att ? 0 : base_att - update_diff)); + if (int32 off_att = GetAttackTimer(OFF_ATTACK)) + if (off_att > 0) + SetAttackTimer(OFF_ATTACK, off_att - update_diff); - if (uint32 ranged_att = GetAttackTimer(RANGED_ATTACK)) - SetAttackTimer(RANGED_ATTACK, (update_diff >= ranged_att ? 0 : ranged_att - update_diff)); + if (int32 ranged_att = GetAttackTimer(RANGED_ATTACK)) + if (ranged_att > 0) + SetAttackTimer(RANGED_ATTACK, ranged_att - update_diff); if (IsAlive()) ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, GetHealth() < GetMaxHealth() * 0.20f); @@ -379,24 +382,18 @@ bool Unit::UpdateMeleeAttackingState() if (IsAttackReady(BASE_ATTACK)) { - // prevent base and off attack in same time, delay attack at 0.2 sec - if (HaveOffhandWeapon()) - { - if (GetAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY) - SetAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY); - } AttackerStateUpdate(pVictim, BASE_ATTACK); - ResetAttackTimer(BASE_ATTACK); + ResetAttackTimer(BASE_ATTACK, !openerAttack); + if (openerAttack && HaveOffhandWeapon() && GetAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY) + SetAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY); + openerAttack = false; } if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) { - // prevent base and off attack in same time, delay attack at 0.2 sec - uint32 base_att = GetAttackTimer(BASE_ATTACK); - if (base_att < ATTACK_DISPLAY_DELAY) - SetAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY); // do attack AttackerStateUpdate(pVictim, OFF_ATTACK); - ResetAttackTimer(OFF_ATTACK); + ResetAttackTimer(OFF_ATTACK, !openerAttack); + openerAttack = false; } break; } @@ -473,9 +470,9 @@ void Unit::SendMovementPacket(uint16 opcode, bool includingSelf) SendMovementMessageToSet(std::move(data), includingSelf); } -void Unit::ResetAttackTimer(WeaponAttackType type) +void Unit::ResetAttackTimer(WeaponAttackType type, bool compensateDiff/*= false*/) { - m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]); + m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]) + ((compensateDiff) ? std::min(m_attackTimer[type], 0) : 0); } void Unit::RemoveSpellsCausingAura(AuraType auraType, AuraRemoveMode mode) @@ -1599,11 +1596,11 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) float percent20 = pVictim->GetAttackTime(OFF_ATTACK) * 0.20f; float percent60 = 3.0f * percent20; if (offtime > percent20 && offtime <= percent60) - pVictim->SetAttackTimer(OFF_ATTACK, uint32(percent20)); + pVictim->SetAttackTimer(OFF_ATTACK, int32(percent20)); else if (offtime > percent60) { offtime -= 2.0f * percent20; - pVictim->SetAttackTimer(OFF_ATTACK, uint32(offtime)); + pVictim->SetAttackTimer(OFF_ATTACK, int32(offtime)); } } else @@ -1611,11 +1608,11 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) float percent20 = pVictim->GetAttackTime(BASE_ATTACK) * 0.20f; float percent60 = 3.0f * percent20; if (basetime > percent20 && basetime <= percent60) - pVictim->SetAttackTimer(BASE_ATTACK, uint32(percent20)); + pVictim->SetAttackTimer(BASE_ATTACK, int32(percent20)); else if (basetime > percent60) { basetime -= 2.0f * percent20; - pVictim->SetAttackTimer(BASE_ATTACK, uint32(basetime)); + pVictim->SetAttackTimer(BASE_ATTACK, int32(basetime)); } } } @@ -2866,7 +2863,7 @@ void Unit::_UpdateAutoRepeatSpell() spell->prepare(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets); // all went good, reset attack - ResetAttackTimer(RANGED_ATTACK); + ResetAttackTimer(RANGED_ATTACK, true); //Compensate Diff always because opener Attack delay is handled by m_AutoRepeatFirstCast SetStandState(UNIT_STAND_STATE_STAND); } } @@ -4639,13 +4636,11 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) pGuardian->AI()->OwnerAttacked(victim); } - // delay offhand weapon attack to next attack time - if (HaveOffhandWeapon()) - ResetAttackTimer(OFF_ATTACK); - if (meleeAttack) + { + openerAttack = true; SendMeleeAttackStart(victim); - + } return true; } @@ -9274,7 +9269,7 @@ bool Unit::IsPolymorphed() const bool Unit::IsAttackReady(WeaponAttackType type) const { - return m_attackTimer[type] == 0; + return m_attackTimer[type] <= 0; } void Unit::SetDisplayId(uint32 displayId) diff --git a/src/game/Objects/Unit.h b/src/game/Objects/Unit.h index d0cc3f2a7be..e9b9087e35d 100644 --- a/src/game/Objects/Unit.h +++ b/src/game/Objects/Unit.h @@ -908,9 +908,10 @@ class Unit : public SpellCaster HostileRefManager m_HostileRefManager; // Manage all Units that are threatened by us std::vector m_tauntGuids; protected: - uint32 m_attackTimer[MAX_ATTACK]; + int32 m_attackTimer[MAX_ATTACK]; AttackerSet m_attackers; Unit* m_attacking; + bool openerAttack; // The unit's first attack against an enemy. uint32 m_reactiveTimer[MAX_REACTIVE]; ObjectGuid m_reactiveTarget[MAX_REACTIVE]; typedef std::map DamageTakenHistoryMap; @@ -922,19 +923,20 @@ class Unit : public SpellCaster * @param type The type of weapon that we want to update the time for * @param time the remaining time until we can attack with the WeaponAttackType again */ - void SetAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; } + void SetAttackTimer(WeaponAttackType type, int32 time) { m_attackTimer[type] = time; } /** * Resets the attack timer to the base value decided by Unit::m_modAttackSpeedPct and * Unit::GetAttackTime * @param type The weapon attack type to reset the attack timer for. + * @param compensateDiff Deduct the diff in update tick from the reseted timer. */ - void ResetAttackTimer(WeaponAttackType type = BASE_ATTACK); + void ResetAttackTimer(WeaponAttackType type = BASE_ATTACK, bool compensateDiff = false); /** * Get's the remaining time until we can do an attack * @param type The weapon type to check the remaining time for * @return The remaining time until we can attack with this weapon type. */ - uint32 GetAttackTimer(WeaponAttackType type) const { return m_attackTimer[type]; } + int32 GetAttackTimer(WeaponAttackType type) const { return m_attackTimer[type]; } /** * Checks whether the unit can do an attack. Does this by checking the attacktimer for the * WeaponAttackType, can probably be thought of as a cooldown for each swing/shot From 7d96096cc4dce9a171c7db690bfb04ffc00db684 Mon Sep 17 00:00:00 2001 From: balakethelock <111737968+balakethelock@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:29:56 +0100 Subject: [PATCH 2/7] Small correction There is still some tiny offset when you perfectly sync your weapons. --- src/game/Objects/Unit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index bf0067f4ff3..6a1fadf24d2 100644 --- a/src/game/Objects/Unit.cpp +++ b/src/game/Objects/Unit.cpp @@ -388,7 +388,7 @@ bool Unit::UpdateMeleeAttackingState() SetAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY); openerAttack = false; } - if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) + else if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) { // do attack AttackerStateUpdate(pVictim, OFF_ATTACK); From 0d4a0f6a9f744e24293375117380dfe01fd4cb65 Mon Sep 17 00:00:00 2001 From: balakethelock <111737968+balakethelock@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:27:39 +0100 Subject: [PATCH 3/7] Fixes based on review --- src/game/Objects/Unit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/Objects/Unit.h b/src/game/Objects/Unit.h index e9b9087e35d..f1c470c40d3 100644 --- a/src/game/Objects/Unit.h +++ b/src/game/Objects/Unit.h @@ -911,7 +911,7 @@ class Unit : public SpellCaster int32 m_attackTimer[MAX_ATTACK]; AttackerSet m_attackers; Unit* m_attacking; - bool openerAttack; // The unit's first attack against an enemy. + bool m_openerAttack; // The unit's first attack against an enemy. uint32 m_reactiveTimer[MAX_REACTIVE]; ObjectGuid m_reactiveTarget[MAX_REACTIVE]; typedef std::map DamageTakenHistoryMap; From fb5d3826124c4ba88d612f975f281f493f7d106d Mon Sep 17 00:00:00 2001 From: balakethelock <111737968+balakethelock@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:31:16 +0100 Subject: [PATCH 4/7] Fixes based on review prt2 --- src/game/Objects/Unit.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index 6a1fadf24d2..c6c047b31e6 100644 --- a/src/game/Objects/Unit.cpp +++ b/src/game/Objects/Unit.cpp @@ -146,6 +146,7 @@ Unit::Unit() m_createResistance = 0; m_attacking = nullptr; + m_openerAttack = true; m_modSpellHitChance = 0.0f; m_baseSpellCritChance = 5; @@ -297,11 +298,11 @@ void Unit::Update(uint32 update_diff, uint32 p_time) if (int32 off_att = GetAttackTimer(OFF_ATTACK)) if (off_att > 0) - SetAttackTimer(OFF_ATTACK, off_att - update_diff); + SetAttackTimer(OFF_ATTACK, off_att - update_diff); if (int32 ranged_att = GetAttackTimer(RANGED_ATTACK)) if (ranged_att > 0) - SetAttackTimer(RANGED_ATTACK, ranged_att - update_diff); + SetAttackTimer(RANGED_ATTACK, ranged_att - update_diff); if (IsAlive()) ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, GetHealth() < GetMaxHealth() * 0.20f); @@ -383,17 +384,16 @@ bool Unit::UpdateMeleeAttackingState() if (IsAttackReady(BASE_ATTACK)) { AttackerStateUpdate(pVictim, BASE_ATTACK); - ResetAttackTimer(BASE_ATTACK, !openerAttack); - if (openerAttack && HaveOffhandWeapon() && GetAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY) + ResetAttackTimer(BASE_ATTACK, !m_openerAttack); + if (m_openerAttack && HaveOffhandWeapon() && GetAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY) SetAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY); - openerAttack = false; + m_openerAttack = false; } else if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) { - // do attack AttackerStateUpdate(pVictim, OFF_ATTACK); - ResetAttackTimer(OFF_ATTACK, !openerAttack); - openerAttack = false; + ResetAttackTimer(OFF_ATTACK, !m_openerAttack); + m_openerAttack = false; } break; } @@ -4638,7 +4638,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) if (meleeAttack) { - openerAttack = true; + m_openerAttack = true; SendMeleeAttackStart(victim); } return true; From e01d16a18820729516bdfbea229afceb9cc71876 Mon Sep 17 00:00:00 2001 From: balakethelock <111737968+balakethelock@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:34:33 +0100 Subject: [PATCH 5/7] Better format for ResetAttackTimer function, offhands happen in same game update Should offhands happen in same game update though? --- src/game/Objects/Unit.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index c6c047b31e6..a0dbc05a473 100644 --- a/src/game/Objects/Unit.cpp +++ b/src/game/Objects/Unit.cpp @@ -389,7 +389,7 @@ bool Unit::UpdateMeleeAttackingState() SetAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY); m_openerAttack = false; } - else if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) + if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) { AttackerStateUpdate(pVictim, OFF_ATTACK); ResetAttackTimer(OFF_ATTACK, !m_openerAttack); @@ -472,7 +472,10 @@ void Unit::SendMovementPacket(uint16 opcode, bool includingSelf) void Unit::ResetAttackTimer(WeaponAttackType type, bool compensateDiff/*= false*/) { - m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]) + ((compensateDiff) ? std::min(m_attackTimer[type], 0) : 0); + if (compensateDiff && m_attackTimer[type] < 0) + m_attackTimer[type] += int32(GetAttackTime(type) * m_modAttackSpeedPct[type]); + else + m_attackTimer[type] = int32(GetAttackTime(type) * m_modAttackSpeedPct[type]); } void Unit::RemoveSpellsCausingAura(AuraType auraType, AuraRemoveMode mode) From ac79eb1cd5a2d67baa85c001140bffd121d84bfb Mon Sep 17 00:00:00 2001 From: balakethelock <111737968+balakethelock@users.noreply.github.com> Date: Wed, 10 Jan 2024 19:10:40 +0100 Subject: [PATCH 6/7] Re-rewriting attack timers system --- src/game/Objects/Unit.cpp | 27 +++++++++++++++++---------- src/game/Objects/Unit.h | 6 +++++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index a0dbc05a473..907c1a69962 100644 --- a/src/game/Objects/Unit.cpp +++ b/src/game/Objects/Unit.cpp @@ -146,7 +146,6 @@ Unit::Unit() m_createResistance = 0; m_attacking = nullptr; - m_openerAttack = true; m_modSpellHitChance = 0.0f; m_baseSpellCritChance = 5; @@ -361,6 +360,18 @@ void Unit::DelayAutoAttacks() SetAttackTimer(OFF_ATTACK, 100); } +void Unit::FirstAttackDelay() +{ + if (isAttackReady(BASE_ATTACK)) + SetAttackTimer(BASE_ATTACK, 0); // Erase saved update timer diff from the swing timer + if (HaveOffhandWeapon() // Doing an attack command sets offhand timer equal to half its swing speed. + { + uint32 halfattack = GetAttackTime(OFF_ATTACK) * m_modAttackSpeedPct[OFF_ATTACK] * 0.5 + if GetAttackTimer(OFF_ATTACK) < halfattack + SetAttackTimer(OFF_ATTACK, halfattack) + } +} + bool Unit::UpdateMeleeAttackingState() { Unit* pVictim = GetVictim(); @@ -384,16 +395,12 @@ bool Unit::UpdateMeleeAttackingState() if (IsAttackReady(BASE_ATTACK)) { AttackerStateUpdate(pVictim, BASE_ATTACK); - ResetAttackTimer(BASE_ATTACK, !m_openerAttack); - if (m_openerAttack && HaveOffhandWeapon() && GetAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY) - SetAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY); - m_openerAttack = false; + ResetAttackTimer(BASE_ATTACK, true); } if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) { AttackerStateUpdate(pVictim, OFF_ATTACK); - ResetAttackTimer(OFF_ATTACK, !m_openerAttack); - m_openerAttack = false; + ResetAttackTimer(OFF_ATTACK, true); } break; } @@ -4614,6 +4621,8 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) // remove old target data AttackStop(true); } + else // not fighting already, do swing timer delays. + FirstAttackDelay(); // Set our target SetTargetGuid(victim->GetObjectGuid()); @@ -4640,10 +4649,8 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) } if (meleeAttack) - { - m_openerAttack = true; SendMeleeAttackStart(victim); - } + return true; } diff --git a/src/game/Objects/Unit.h b/src/game/Objects/Unit.h index f1c470c40d3..97c4aac9257 100644 --- a/src/game/Objects/Unit.h +++ b/src/game/Objects/Unit.h @@ -911,7 +911,6 @@ class Unit : public SpellCaster int32 m_attackTimer[MAX_ATTACK]; AttackerSet m_attackers; Unit* m_attacking; - bool m_openerAttack; // The unit's first attack against an enemy. uint32 m_reactiveTimer[MAX_REACTIVE]; ObjectGuid m_reactiveTarget[MAX_REACTIVE]; typedef std::map DamageTakenHistoryMap; @@ -962,6 +961,11 @@ class Unit : public SpellCaster * Called from UpdateMeleeAttackingState if attack can't happen now. */ void DelayAutoAttacks(); + /** + * When a starts autoattacking. Erases the saved update diff on its swing timer + * and delays the offhand attack to half its attack speed + */ + void FirstAttackDelay(); /** * Checks that need to be done before an auto attack swing happens. * Target's faction is only checked for players since its done elsewhere From 851688a39ace5840ab0b93d72f6be50890365ea3 Mon Sep 17 00:00:00 2001 From: balakethelock <111737968+balakethelock@users.noreply.github.com> Date: Wed, 10 Jan 2024 20:46:29 +0100 Subject: [PATCH 7/7] Correct Syntax Accidentally pushed before compiling --- src/game/Objects/Unit.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index 907c1a69962..4b34ffabf0e 100644 --- a/src/game/Objects/Unit.cpp +++ b/src/game/Objects/Unit.cpp @@ -362,13 +362,13 @@ void Unit::DelayAutoAttacks() void Unit::FirstAttackDelay() { - if (isAttackReady(BASE_ATTACK)) + if (IsAttackReady(BASE_ATTACK)) SetAttackTimer(BASE_ATTACK, 0); // Erase saved update timer diff from the swing timer - if (HaveOffhandWeapon() // Doing an attack command sets offhand timer equal to half its swing speed. + if (HaveOffhandWeapon()) // Doing an attack command sets offhand timer equal to half its swing speed. { - uint32 halfattack = GetAttackTime(OFF_ATTACK) * m_modAttackSpeedPct[OFF_ATTACK] * 0.5 - if GetAttackTimer(OFF_ATTACK) < halfattack - SetAttackTimer(OFF_ATTACK, halfattack) + int32 halfattack = int32(GetAttackTime(OFF_ATTACK) * m_modAttackSpeedPct[OFF_ATTACK] * 0.5); + if (GetAttackTimer(OFF_ATTACK) < halfattack) + SetAttackTimer(OFF_ATTACK, halfattack); } }