aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Entities/Player/Player.cpp4
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp48
-rw-r--r--src/server/game/Entities/Unit/Unit.h13
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp17
-rw-r--r--src/server/game/Spells/Spell.cpp35
-rw-r--r--src/server/game/Spells/Spell.h4
-rw-r--r--src/server/game/Spells/SpellEffects.cpp5
7 files changed, 75 insertions, 51 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index a309f972ac0..f2549dcf621 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -7979,10 +7979,6 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT
continue;
}
- // not allow proc extra attack spell at extra attack
- if (m_extraAttacks && spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
- return;
-
float chance = (float)spellInfo->ProcChance;
if (spellData.SpellPPMRate)
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index e515a048830..2daf345215c 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -314,7 +314,6 @@ Unit::Unit(bool isWorldObject) :
m_modAttackSpeedPct[OFF_ATTACK] = 1.0f;
m_modAttackSpeedPct[RANGED_ATTACK] = 1.0f;
- m_extraAttacks = 0;
m_canDualWield = false;
m_movementCounter = 0;
@@ -389,6 +388,8 @@ Unit::Unit(bool isWorldObject) :
_isWalkingBeforeCharm = false;
_instantCast = false;
_isCombatDisallowed = false;
+
+ _lastExtraAttackSpell = 0;
}
////////////////////////////////////////////////////////////
@@ -445,6 +446,21 @@ void Unit::Update(uint32 p_time)
m_combatManager.Update(p_time);
+ _lastDamagedTargetGuid = ObjectGuid::Empty;
+ if (_lastExtraAttackSpell)
+ {
+ while (!extraAttacksTargets.empty())
+ {
+ auto itr = extraAttacksTargets.begin();
+ ObjectGuid targetGuid = itr->first;
+ uint32 count = itr->second;
+ extraAttacksTargets.erase(itr);
+ if (Unit* victim = ObjectAccessor::GetUnit(*this, targetGuid))
+ HandleProcExtraAttackFor(victim, count);
+ }
+ _lastExtraAttackSpell = 0;
+ }
+
// not implemented before 3.0.2
if (uint32 base_att = getAttackTimer(BASE_ATTACK))
setAttackTimer(BASE_ATTACK, (p_time >= base_att ? 0 : base_att - p_time));
@@ -2056,7 +2072,10 @@ void Unit::HandleEmoteCommand(Emote emoteId)
void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra)
{
- if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED))
+ if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED))
+ return;
+
+ if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) && !extra)
return;
if (!victim->IsAlive())
@@ -2071,6 +2090,9 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr
if (attType != BASE_ATTACK && attType != OFF_ATTACK)
return; // ignore ranged case
+ if (!extra && _lastExtraAttackSpell)
+ _lastExtraAttackSpell = 0;
+
if (GetTypeId() == TYPEID_UNIT && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED) && !HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_CANNOT_TURN))
SetFacingToObject(victim, false); // update client side facing to face the target (prevents visual glitches when casting untargeted spells)
@@ -2090,6 +2112,8 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr
Unit::DealDamageMods(victim, damageInfo.Damages[i].Damage, &damageInfo.Damages[i].Absorb);
SendAttackStateUpdate(&damageInfo);
+ _lastDamagedTargetGuid = victim->GetGUID();
+
DealMeleeDamage(&damageInfo, true);
DamageInfo dmgInfo(damageInfo);
@@ -2104,15 +2128,29 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr
}
}
-void Unit::HandleProcExtraAttackFor(Unit* victim)
+void Unit::HandleProcExtraAttackFor(Unit* victim, uint32 count)
{
- while (m_extraAttacks)
+ while (count)
{
+ --count;
AttackerStateUpdate(victim, BASE_ATTACK, true);
- --m_extraAttacks;
}
}
+void Unit::AddExtraAttacks(uint32 count)
+{
+ ObjectGuid targetGUID = _lastDamagedTargetGuid;
+ if (!targetGUID)
+ {
+ if (ObjectGuid selection = GetTarget())
+ targetGUID = selection; // Spell was cast directly (not triggered by aura)
+ else
+ return;
+ }
+
+ extraAttacksTargets[targetGUID] += count;
+}
+
MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType) const
{
if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index cffd683d28e..ea63c22ea3a 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -834,7 +834,6 @@ class TC_GAME_API Unit : public WorldObject
bool IsWithinMeleeRangeAt(Position const& pos, Unit const* obj) const;
float GetMeleeRange(Unit const* target) const;
virtual SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const = 0;
- uint32 m_extraAttacks;
bool m_canDualWield;
void _addAttacker(Unit* pAttacker); // must be called only from Unit::Attack(Unit*)
@@ -991,7 +990,13 @@ class TC_GAME_API Unit : public WorldObject
void CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, WeaponAttackType attackType = BASE_ATTACK);
void DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss);
- void HandleProcExtraAttackFor(Unit* victim);
+ void HandleProcExtraAttackFor(Unit* victim, uint32 count);
+
+ void SetLastExtraAttackSpell(uint32 spellId) { _lastExtraAttackSpell = spellId; }
+ uint32 GetLastExtraAttackSpell() const { return _lastExtraAttackSpell; }
+ void AddExtraAttacks(uint32 count);
+ void SetLastDamagedTargetGuid(ObjectGuid guid) { _lastDamagedTargetGuid = guid; }
+ ObjectGuid GetLastDamagedTargetGuid() const { return _lastDamagedTargetGuid; }
void CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = BASE_ATTACK, bool crit = false, Spell* spell = nullptr);
void DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss);
@@ -1884,6 +1889,10 @@ class TC_GAME_API Unit : public WorldObject
int8 m_comboPoints;
std::unordered_set<Unit*> m_ComboPointHolders;
+ uint32 _lastExtraAttackSpell;
+ std::unordered_map<ObjectGuid /*guid*/, uint32 /*count*/> extraAttacksTargets;
+ ObjectGuid _lastDamagedTargetGuid;
+
bool m_cleanupDone; // lock made to not add stuff after cleanup before delete
bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world
bool _instantCast;
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 9e2275f43e4..a60e6b44645 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -989,8 +989,21 @@ bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventIn
// Don't proc extra attacks while already processing extra attack spell
uint32 triggerSpellId = GetSpellEffectInfo().TriggerSpell;
if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(triggerSpellId))
- if (aurApp->GetTarget()->m_extraAttacks && triggeredSpellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
- return false;
+ {
+ if (triggeredSpellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
+ {
+ uint32 lastExtraAttackSpell = eventInfo.GetActor()->GetLastExtraAttackSpell();
+
+ // Patch 1.12.0(?) extra attack abilities can no longer chain proc themselves
+ if (lastExtraAttackSpell == triggerSpellId)
+ return false;
+
+ // Patch 2.2.0 Sword Specialization (Warrior, Rogue) extra attack can no longer proc additional extra attacks
+ // 3.3.5 Sword Specialization (Warrior), Hack and Slash (Rogue)
+ if (lastExtraAttackSpell == 16459 || lastExtraAttackSpell == 66923)
+ return false;
+ }
+ }
break;
}
case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 2e7c93c9d80..450957a5ca9 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -215,28 +215,6 @@ void SpellCastTargets::Write(WorldPackets::Spells::SpellTargetData& data)
data.Name = m_strTarget;
}
-ObjectGuid SpellCastTargets::GetOrigUnitTargetGUID() const
-{
- switch (m_origObjectTargetGUID.GetHigh())
- {
- case HighGuid::Player:
- case HighGuid::Vehicle:
- case HighGuid::Unit:
- case HighGuid::Pet:
- return m_origObjectTargetGUID;
- default:
- return ObjectGuid();
- }
-}
-
-void SpellCastTargets::SetOrigUnitTarget(Unit* target)
-{
- if (!target)
- return;
-
- m_origObjectTargetGUID = target->GetGUID();
-}
-
ObjectGuid SpellCastTargets::GetUnitTargetGUID() const
{
if (m_objectTargetGUID.IsUnit())
@@ -651,7 +629,7 @@ Spell::~Spell()
void Spell::InitExplicitTargets(SpellCastTargets const& targets)
{
m_targets = targets;
- m_targets.SetOrigUnitTarget(m_targets.GetUnitTarget());
+
// this function tries to correct spell explicit targets for spell
// client doesn't send explicit targets correctly sometimes - we need to fix such spells serverside
// this also makes sure that we correctly send explicit targets to client (removes redundant data)
@@ -2515,6 +2493,8 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell)
}
else
{
+ caster->SetLastDamagedTargetGuid(spell->unitTarget->GetGUID());
+
// Add bonuses and fill damageInfo struct
caster->CalculateSpellDamageTaken(&damageInfo, spell->m_damage, spell->m_spellInfo, spell->m_attackType, IsCrit, spell);
Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
@@ -3711,13 +3691,8 @@ void Spell::_handle_finish_phase()
unitCaster->AddComboPoints(m_comboTarget, m_comboPointGain);
}
- if (unitCaster->m_extraAttacks && m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
- {
- if (Unit* victim = ObjectAccessor::GetUnit(*unitCaster, m_targets.GetOrigUnitTargetGUID()))
- unitCaster->HandleProcExtraAttackFor(victim);
- else
- unitCaster->m_extraAttacks = 0;
- }
+ if (m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
+ unitCaster->SetLastExtraAttackSpell(m_spellInfo->Id);
}
// Handle procs on finish
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 238007a0178..c9f7b38cc69 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -150,9 +150,6 @@ class TC_GAME_API SpellCastTargets
void SetTargetFlag(SpellCastTargetFlags flag) { m_targetMask |= flag; }
- ObjectGuid GetOrigUnitTargetGUID() const;
- void SetOrigUnitTarget(Unit* target);
-
ObjectGuid GetUnitTargetGUID() const;
Unit* GetUnitTarget() const;
void SetUnitTarget(Unit* target);
@@ -217,7 +214,6 @@ class TC_GAME_API SpellCastTargets
Item* m_itemTarget;
// object GUID/etc, can be used always
- ObjectGuid m_origObjectTargetGUID;
ObjectGuid m_objectTargetGUID;
ObjectGuid m_itemTargetGUID;
uint32 m_itemTargetEntry;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 5712f1f92c1..1a910fc16d1 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -4122,10 +4122,7 @@ void Spell::EffectAddExtraAttacks()
if (!unitTarget || !unitTarget->IsAlive())
return;
- if (unitTarget->m_extraAttacks)
- return;
-
- unitTarget->m_extraAttacks = damage;
+ unitTarget->AddExtraAttacks(damage);
ExecuteLogEffectExtraAttacks(effectInfo->EffectIndex, unitTarget, damage);
}