From faa937a9da180e5dc9b2848cfdda843aa34a6a66 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sun, 29 May 2022 23:42:17 -0400 Subject: [PATCH] Core/Combat Improve extra attacks handling Port From (https://github.com/TrinityCore/TrinityCore/commit/6c9bde098b40f38905ea7407d0cd77437b0b953c) --- Source/Game/Entities/Player/Player.Spells.cs | 4 -- Source/Game/Entities/Unit/Unit.Combat.cs | 41 +++++++++++++++++--- Source/Game/Entities/Unit/Unit.Fields.cs | 5 ++- Source/Game/Entities/Unit/Unit.cs | 15 +++++++ Source/Game/Spells/Auras/AuraEffect.cs | 12 +++++- Source/Game/Spells/Spell.cs | 14 +++---- Source/Game/Spells/SpellCastTargets.cs | 23 ----------- Source/Game/Spells/SpellEffects.cs | 5 +-- 8 files changed, 71 insertions(+), 48 deletions(-) diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index ad84382b7..fad8f638f 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -3304,10 +3304,6 @@ namespace Game.Entities continue; } - // not allow proc extra attack spell at extra attack - if (ExtraAttacks != 0 && spellInfo.HasEffect(SpellEffectName.AddExtraAttacks)) - return; - float chance = spellInfo.ProcChance; if (proto.SpellPPMRate != 0) diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index 24348feee..d809f4083 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -229,15 +229,30 @@ namespace Game.Entities minion.StopAttackFaction(factionId); } - public void HandleProcExtraAttackFor(Unit victim) + public void HandleProcExtraAttackFor(Unit victim, uint count) { - while (ExtraAttacks != 0) + while (count != 0) { + --count; AttackerStateUpdate(victim, WeaponAttackType.BaseAttack, true); - --ExtraAttacks; } } + public void AddExtraAttacks(uint count) + { + ObjectGuid targetGUID = _lastDamagedTargetGuid; + if (!targetGUID.IsEmpty()) + { + ObjectGuid selection = GetTarget(); + if (!selection.IsEmpty()) + targetGUID = selection; // Spell was cast directly (not triggered by aura) + else + return; + } + + extraAttacksTargets[targetGUID] += count; + } + public bool Attack(Unit victim, bool meleeAttack) { if (victim == null || victim.GetGUID() == GetGUID()) @@ -408,6 +423,14 @@ namespace Game.Entities attackerList.Remove(pAttacker); } + public void SetLastExtraAttackSpell(uint spellId) { _lastExtraAttackSpell = spellId; } + + public uint GetLastExtraAttackSpell() { return _lastExtraAttackSpell; } + + public void SetLastDamagedTargetGuid(ObjectGuid guid) { _lastDamagedTargetGuid = guid; } + + ObjectGuid GetLastDamagedTargetGuid() { return _lastDamagedTargetGuid; } + public Unit GetVictim() { return attacking; @@ -481,7 +504,10 @@ namespace Game.Entities public void AttackerStateUpdate(Unit victim, WeaponAttackType attType = WeaponAttackType.BaseAttack, bool extra = false) { - if (HasUnitState(UnitState.CannotAutoattack) || HasUnitFlag(UnitFlags.Pacified)) + if (HasUnitFlag(UnitFlags.Pacified)) + return; + + if (HasUnitState(UnitState.CannotAutoattack) && !extra) return; if (HasAuraType(AuraType.DisableAttackingExceptAbilities)) @@ -500,6 +526,9 @@ namespace Game.Entities if (attType != WeaponAttackType.BaseAttack && attType != WeaponAttackType.OffAttack) return; + if (!extra && _lastExtraAttackSpell != 0) + _lastExtraAttackSpell = 0; + if (IsTypeId(TypeId.Unit) && !HasUnitFlag(UnitFlags.Possessed) && !HasUnitFlag2(UnitFlags2.CannotTurn)) SetFacingToObject(victim, false); // update client side facing to face the target (prevents visual glitches when casting untargeted spells) @@ -544,6 +573,8 @@ namespace Game.Entities DealDamageMods(damageInfo.Attacker, victim, ref damageInfo.Damage, ref damageInfo.Absorb); SendAttackStateUpdate(damageInfo); + _lastDamagedTargetGuid = victim.GetGUID(); + DealMeleeDamage(damageInfo, true); DamageInfo dmgInfo = new(damageInfo); @@ -810,7 +841,7 @@ namespace Game.Entities } } } - + // Proc auras on death - must be before aura/combat remove ProcSkillsAndAuras(victim, victim, new ProcFlagsInit(ProcFlags.None), new ProcFlagsInit(ProcFlags.Death), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null); diff --git a/Source/Game/Entities/Unit/Unit.Fields.cs b/Source/Game/Entities/Unit/Unit.Fields.cs index f7a3b34d4..334626771 100644 --- a/Source/Game/Entities/Unit/Unit.Fields.cs +++ b/Source/Game/Entities/Unit/Unit.Fields.cs @@ -69,7 +69,10 @@ namespace Game.Entities public bool m_canDualWield; public int BaseSpellCritChance { get; set; } public uint RegenTimer { get; set; } - public uint ExtraAttacks { get; set; } + + uint _lastExtraAttackSpell; + Dictionary extraAttacksTargets = new(); + ObjectGuid _lastDamagedTargetGuid; //Charm public List m_Controlled = new(); diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index 57f9551ce..9465e0c2b 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -147,6 +147,21 @@ namespace Game.Entities m_combatManager.Update(diff); + _lastDamagedTargetGuid = ObjectGuid.Empty; + if (_lastExtraAttackSpell != 0) + { + while (!extraAttacksTargets.Empty()) + { + var (targetGuid, count) = extraAttacksTargets.FirstOrDefault(); + extraAttacksTargets.Remove(targetGuid); + + Unit victim = Global.ObjAccessor.GetUnit(this, targetGuid); + if (victim != null) + HandleProcExtraAttackFor(victim, count); + } + _lastExtraAttackSpell = 0; + } + uint att; // not implemented before 3.0.2 if ((att = GetAttackTimer(WeaponAttackType.BaseAttack)) != 0) diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index b163ae7bf..aface7448 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -645,8 +645,16 @@ namespace Game.Spells uint triggerSpellId = GetSpellEffectInfo().TriggerSpell; SpellInfo triggeredSpellInfo = Global.SpellMgr.GetSpellInfo(triggerSpellId, GetBase().GetCastDifficulty()); if (triggeredSpellInfo != null) - if (aurApp.GetTarget().ExtraAttacks != 0 && triggeredSpellInfo.HasEffect(SpellEffectName.AddExtraAttacks)) - return false; + { + if (triggeredSpellInfo.HasEffect(SpellEffectName.AddExtraAttacks)) + { + uint lastExtraAttackSpell = eventInfo.GetActor().GetLastExtraAttackSpell(); + + // Patch 1.12.0(?) extra attack abilities can no longer chain proc themselves + if (lastExtraAttackSpell == triggerSpellId) + return false; + } + } break; } case AuraType.ModSpellCritChance: diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 990a1d875..7a8ae94d6 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -140,7 +140,7 @@ namespace Game.Spells void InitExplicitTargets(SpellCastTargets 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) @@ -3132,14 +3132,8 @@ namespace Game.Spells if (m_comboPointGain != 0) unitCaster.AddComboPoints(m_comboPointGain); - if (unitCaster.ExtraAttacks != 0 && m_spellInfo.HasEffect(SpellEffectName.AddExtraAttacks)) - { - Unit victim = Global.ObjAccessor.GetUnit(unitCaster, m_targets.GetOrigUnitTargetGUID()); - if (victim) - unitCaster.HandleProcExtraAttackFor(victim); - else - unitCaster.ExtraAttacks = 0; - } + if (m_spellInfo.HasEffect(SpellEffectName.AddExtraAttacks)) + unitCaster.SetLastExtraAttackSpell(m_spellInfo.Id); } // Handle procs on finish @@ -8337,6 +8331,8 @@ namespace Game.Spells } 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); Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); diff --git a/Source/Game/Spells/SpellCastTargets.cs b/Source/Game/Spells/SpellCastTargets.cs index 252dcca03..baf5ae12c 100644 --- a/Source/Game/Spells/SpellCastTargets.cs +++ b/Source/Game/Spells/SpellCastTargets.cs @@ -114,28 +114,6 @@ namespace Game.Spells data.Name = m_strTarget; } - public ObjectGuid GetOrigUnitTargetGUID() - { - switch (m_origObjectTargetGUID.GetHigh()) - { - case HighGuid.Player: - case HighGuid.Vehicle: - case HighGuid.Creature: - case HighGuid.Pet: - return m_origObjectTargetGUID; - default: - return ObjectGuid.Empty; - } - } - - public void SetOrigUnitTarget(Unit target) - { - if (!target) - return; - - m_origObjectTargetGUID = target.GetGUID(); - } - public ObjectGuid GetUnitTargetGUID() { if (m_objectTargetGUID.IsUnit()) @@ -420,7 +398,6 @@ namespace Game.Spells Item m_itemTarget; // object GUID/etc, can be used always - ObjectGuid m_origObjectTargetGUID; ObjectGuid m_objectTargetGUID; ObjectGuid m_itemTargetGUID; uint m_itemTargetEntry; diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 3ee698a8a..f5df01c4a 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -3487,10 +3487,7 @@ namespace Game.Spells if (!unitTarget || !unitTarget.IsAlive()) return; - if (unitTarget.ExtraAttacks != 0) - return; - - unitTarget.ExtraAttacks = (uint)damage; + unitTarget.AddExtraAttacks((uint)damage); ExecuteLogEffectExtraAttacks(effectInfo.Effect, unitTarget, (uint)damage); }