diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index 4db4b552af8..4b34ffabf0e 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); @@ -357,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. + { + int32 halfattack = int32(GetAttackTime(OFF_ATTACK) * m_modAttackSpeedPct[OFF_ATTACK] * 0.5); + if (GetAttackTimer(OFF_ATTACK) < halfattack) + SetAttackTimer(OFF_ATTACK, halfattack); + } +} + bool Unit::UpdateMeleeAttackingState() { Unit* pVictim = GetVictim(); @@ -379,24 +394,13 @@ 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, true); } 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, true); } break; } @@ -473,9 +477,12 @@ 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]); + 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) @@ -1599,11 +1606,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 +1618,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 +2873,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); } } @@ -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()); @@ -4639,10 +4648,6 @@ 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) SendMeleeAttackStart(victim); @@ -9274,7 +9279,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..97c4aac9257 100644 --- a/src/game/Objects/Unit.h +++ b/src/game/Objects/Unit.h @@ -908,7 +908,7 @@ 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; uint32 m_reactiveTimer[MAX_REACTIVE]; @@ -922,19 +922,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 @@ -960,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