-
Notifications
You must be signed in to change notification settings - Fork 501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement Elapsed Time compensation to Attack Timer adjustements and DW adjustment #2410
base: development
Are you sure you want to change the base?
Changes from 2 commits
0624b0b
7d96096
0d4a0f6
fb5d382
e01d16a
ac79eb1
a1f7206
851688a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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)) | ||
else if (HaveOffhandWeapon() && IsAttackReady(OFF_ATTACK)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't this change cause off hand attack to never go off when server has high update time and main hand attack speed is lower than it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it would in the case where the server update time is longer than mainhand attack speed. It probably shouldn't happen, but I have no retail wow videos of a DW unit attacking when the server is that laggy. Note though "DW never hits if the server update time is longer than MH attack speed" is already true for how vmangos is before this PR, because of this:
Making every world update when its very high update time, the mainhand lands and delays the offhand by 200 ms so it's no longer ready within that same update, then the next update happens and, the MH is ready so it attacks with it and delays the offhand by 200ms again, and so on making the offhand never hit. I'm not sure what the retail wow would do in this situation. I don't have enough data to conclude if an offhand can hit in the same world update as a mainhand. People in classic discords describe a situation where if offhand and mainhand hit very close then the warriors with the talent flurry only lose one charge of the buff. Is that a spellbatching thing or were melee attacks batched as well in classic era, I don't know. |
||
{ | ||
// 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to add a separate if statement below instead of doing it like that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I try to do it like this,. m_attackTimer was already set to the attackspeed in the line above it, so this will always add 0
So I can first save it then add the saved value after setting to 0 like this:
Is this method better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about
|
||
} | ||
|
||
void Unit::RemoveSpellsCausingAura(AuraType auraType, AuraRemoveMode mode) | ||
|
@@ -1599,23 +1596,23 @@ 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 | ||
{ | ||
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) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -908,9 +908,10 @@ class Unit : public SpellCaster | |
HostileRefManager m_HostileRefManager; // Manage all Units that are threatened by us | ||
std::vector<ObjectGuid> 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You forgot to initialize it in the constructor. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also member variables need to start with m_ before the name. |
||
uint32 m_reactiveTimer[MAX_REACTIVE]; | ||
ObjectGuid m_reactiveTarget[MAX_REACTIVE]; | ||
typedef std::map<ObjectGuid /*attackerGuid*/, uint32 /*damage*/ > 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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You forgot indentation.