aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2018-01-03 20:04:19 +0100
committerGitHub <noreply@github.com>2018-01-03 20:04:19 +0100
commit532ab1c7f8653d1a2e48aa1f1f8a9ba1041d4bb7 (patch)
tree81e2f7eb89b3144c14dd488ea6304f6d44d19848 /src/server/game/Spells
parent425b181544a21d2246fdf0261ba76a37e2510883 (diff)
Core: Combat/threat system rewrite (PR #19930)
- PvE combat is now always mutual. UNIT_FLAG_IN_COMBAT is backed by actual references to the units we're in combat with. - PvP combat is now also tracked, and almost always mutual; spells like Vanish and Feign Death can break this rule. That means we can easily determine a list of players we're fighting. - By extension, IsInCombatWith now has sensible behavior when invoked on nonplayers. - Threat and combat systems are no longer the same. - They still have an enforced relationship (threat implies combat - clearing combat clears threat)... - ...but we can have combat without threat. A creature (with threat list) isn't considered to be engaged until it has an entry on its threat list... - ...which means we can now faithfully replicate retail engage behavior. Combat on projectile launch - engagement start on projectile impact. Yay for progress! - AI method refactor, as already ported in 6113b9d - `JustEngagedWith`, `JustEnteredCombat` and `JustExitedCombat`. - Vehicle threat is now properly pooled on the main vehicle body (fixes #16542). - Various edge case bug fixes for threat redirects (Misdirection "cancelling" Vigilance and similar). - Target re-selection is now significantly faster. - Fixed a ton of other smaller edge case bugs, probably. Closes #7951 and #19998.
Diffstat (limited to 'src/server/game/Spells')
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp79
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.h1
-rw-r--r--src/server/game/Spells/Spell.cpp9
-rw-r--r--src/server/game/Spells/SpellEffects.cpp61
-rw-r--r--src/server/game/Spells/SpellInfo.cpp4
5 files changed, 86 insertions, 68 deletions
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index fffa102f7b2..fb0e1d978ca 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -280,7 +280,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED
&AuraEffect::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT
&AuraEffect::HandleModRatingFromStat, //220 SPELL_AURA_MOD_RATING_FROM_STAT
- &AuraEffect::HandleNULL, //221 SPELL_AURA_MOD_DETAUNT
+ &AuraEffect::HandleModDetaunt, //221 SPELL_AURA_MOD_DETAUNT
&AuraEffect::HandleUnused, //222 unused (3.2.0) only for spell 44586 that not used in real spell cast
&AuraEffect::HandleNoImmediateEffect, //223 SPELL_AURA_RAID_PROC_FROM_CHARGE
&AuraEffect::HandleUnused, //224 unused (3.0.8a)
@@ -2083,6 +2083,8 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode,
}
}
}
+
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandleAuraModScale(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2160,14 +2162,22 @@ void AuraEffect::HandleFeignDeath(AuraApplication const* aurApp, uint8 mode, boo
}
}
}
- target->CombatStop();
+
+ if (target->GetMap()->IsDungeon()) // feign death does not remove combat in dungeons
+ {
+ target->AttackStop();
+ if (Player* targetPlayer = target->ToPlayer())
+ targetPlayer->SendAttackSwingCancelAttack();
+ }
+ else
+ target->CombatStop(false, false);
+
target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
// prevent interrupt message
if (GetCasterGUID() == target->GetGUID() && target->GetCurrentSpell(CURRENT_GENERIC_SPELL))
target->FinishSpell(CURRENT_GENERIC_SPELL, false);
target->InterruptNonMeleeSpells(true);
- target->getHostileRefManager().deleteReferences();
// stop handling the effect if it was removed by linked event
if (aurApp->GetRemoveMode())
@@ -2198,6 +2208,7 @@ void AuraEffect::HandleFeignDeath(AuraApplication const* aurApp, uint8 mode, boo
if (Creature* creature = target->ToCreature())
creature->InitializeReactState();
}
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandleModUnattackable(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2216,7 +2227,14 @@ void AuraEffect::HandleModUnattackable(AuraApplication const* aurApp, uint8 mode
// call functions which may have additional effects after chainging state of unit
if (apply && (mode & AURA_EFFECT_HANDLE_REAL))
{
- target->CombatStop();
+ if (target->GetMap()->IsDungeon())
+ {
+ target->AttackStop();
+ if (Player* targetPlayer = target->ToPlayer())
+ targetPlayer->SendAttackSwingCancelAttack();
+ }
+ else
+ target->CombatStop();
target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
}
}
@@ -2678,28 +2696,15 @@ void AuraEffect::HandleForceMoveForward(AuraApplication const* aurApp, uint8 mod
/*** THREAT ***/
/****************************/
-void AuraEffect::HandleModThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const
+void AuraEffect::HandleModThreat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
{
if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
return;
- Unit* target = aurApp->GetTarget();
- for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
- {
- if (GetMiscValue() & (1 << i))
- {
- if (apply)
- AddPct(target->m_threatModifier[i], GetAmount());
- else
- {
- float amount = target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_THREAT, 1 << i);
- target->m_threatModifier[i] = amount;
- }
- }
- }
+ aurApp->GetTarget()->GetThreatManager().UpdateMySpellSchoolModifiers();
}
-void AuraEffect::HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const
+void AuraEffect::HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
{
if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
return;
@@ -2711,10 +2716,10 @@ void AuraEffect::HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 m
Unit* caster = GetCaster();
if (caster && caster->IsAlive())
- target->getHostileRefManager().addTempThreat((float)GetAmount(), apply);
+ caster->GetThreatManager().UpdateMyTempModifiers();
}
-void AuraEffect::HandleModTaunt(AuraApplication const* aurApp, uint8 mode, bool apply) const
+void AuraEffect::HandleModTaunt(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
{
if (!(mode & AURA_EFFECT_HANDLE_REAL))
return;
@@ -2724,17 +2729,21 @@ void AuraEffect::HandleModTaunt(AuraApplication const* aurApp, uint8 mode, bool
if (!target->IsAlive() || !target->CanHaveThreatList())
return;
+ target->GetThreatManager().TauntUpdate();
+}
+
+void AuraEffect::HandleModDetaunt(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
+{
+ if (!(mode & AURA_EFFECT_HANDLE_REAL))
+ return;
+
Unit* caster = GetCaster();
- if (!caster || !caster->IsAlive())
+ Unit* target = aurApp->GetTarget();
+
+ if (!caster || !caster->IsAlive() || !target->IsAlive() || !caster->CanHaveThreatList())
return;
- if (apply)
- target->TauntApply(caster);
- else
- {
- // When taunt aura fades out, mob will switch to previous target if current has less than 1.1 * secondthreat
- target->TauntFadeOut(caster);
- }
+ caster->GetThreatManager().TauntUpdate();
}
/*****************************/
@@ -2749,6 +2758,7 @@ void AuraEffect::HandleModConfuse(AuraApplication const* aurApp, uint8 mode, boo
Unit* target = aurApp->GetTarget();
target->SetControlled(apply, UNIT_STATE_CONFUSED);
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandleModFear(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2759,6 +2769,7 @@ void AuraEffect::HandleModFear(AuraApplication const* aurApp, uint8 mode, bool a
Unit* target = aurApp->GetTarget();
target->SetControlled(apply, UNIT_STATE_FLEEING);
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandleAuraModStun(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2769,6 +2780,7 @@ void AuraEffect::HandleAuraModStun(AuraApplication const* aurApp, uint8 mode, bo
Unit* target = aurApp->GetTarget();
target->SetControlled(apply, UNIT_STATE_STUNNED);
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandleAuraModRoot(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2779,6 +2791,7 @@ void AuraEffect::HandleAuraModRoot(AuraApplication const* aurApp, uint8 mode, bo
Unit* target = aurApp->GetTarget();
target->SetControlled(apply, UNIT_STATE_ROOT);
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandlePreventFleeing(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -3132,6 +3145,8 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint
if (GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)
&& GetSpellInfo()->HasAttribute(SPELL_ATTR2_DAMAGE_REDUCED_SHIELD))
target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
+
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -3141,6 +3156,8 @@ void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 m
Unit* target = aurApp->GetTarget();
m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply);
+
+ target->GetThreatManager().UpdateOnlineStates(true, false);
}
void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -4744,7 +4761,7 @@ void AuraEffect::HandleForceReaction(AuraApplication const* aurApp, uint8 mode,
player->GetReputationMgr().ApplyForceReaction(factionId, factionRank, apply);
player->GetReputationMgr().SendForceReactions();
- // stop fighting if at apply forced rank friendly or at remove real rank friendly
+ // stop fighting at apply (if forced rank friendly) or at remove (if real rank friendly)
if ((apply && factionRank >= REP_FRIENDLY) || (!apply && player->GetReputationRank(factionId) >= REP_FRIENDLY))
player->StopAttackFaction(factionId);
}
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h
index cc643dcbfb5..befe0d7a2f6 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.h
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.h
@@ -185,6 +185,7 @@ class TC_GAME_API AuraEffect
void HandleModThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleModTaunt(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+ void HandleModDetaunt(AuraApplication const* aurApp, uint8 mode, bool apply) const;
// control
void HandleModConfuse(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleModFear(AuraApplication const* aurApp, uint8 mode, bool apply) const;
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index b6f368d3a56..3bdce758016 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -2480,7 +2480,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
// spellHitTarget can be null if spell is missed in DoSpellHitOnUnit
if (missInfo != SPELL_MISS_EVADE && spellHitTarget && !m_caster->IsFriendlyTo(unit) && (!IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)))
{
- m_caster->CombatStart(unit, m_spellInfo->HasInitialAggro());
+ m_caster->AttackedTarget(unit, m_spellInfo->HasInitialAggro());
if (!unit->IsStandState())
unit->SetStandState(UNIT_STAND_STATE_STAND);
@@ -2582,7 +2582,8 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
}
if (unit->IsInCombat() && m_spellInfo->HasInitialAggro())
{
- m_caster->SetInCombatState(unit->GetCombatTimer() > 0, unit);
+ if (m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) // only do explicit combat forwarding for PvP enabled units
+ m_caster->GetCombatManager().InheritCombatStatesFrom(unit); // for creature v creature combat, the threat forward does it for us
unit->GetThreatManager().ForwardThreatForAssistingMe(m_caster, 0.0f, nullptr, true);
}
}
@@ -7290,6 +7291,10 @@ void Spell::DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier)
if (!unit)
return;
+ // This will only cause combat - the target will engage once the projectile hits (in DoAllEffectOnTarget)
+ if (targetInfo.missCondition != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged()))
+ m_caster->SetInCombatWith(unit);
+
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (targetInfo.effectMask & (1<<i))
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 45ecb4a8e15..59f5d9b764a 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -1229,7 +1229,7 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex)
int32 gain = int32(newDamage* gainMultiplier);
- m_caster->EnergizeBySpell(m_caster, m_spellInfo->Id, gain, powerType);
+ m_caster->EnergizeBySpell(m_caster, m_spellInfo, gain, powerType);
}
ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, newDamage, gainMultiplier);
}
@@ -1781,7 +1781,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex)
if (damage < 0)
return;
- m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, damage, power);
+ m_caster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power);
}
void Spell::EffectEnergizePct(SpellEffIndex effIndex)
@@ -1807,7 +1807,7 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex)
return;
uint32 gain = CalculatePct(maxPower, damage);
- m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, gain, power);
+ m_caster->EnergizeBySpell(unitTarget, m_spellInfo, gain, power);
}
void Spell::SendLoot(ObjectGuid guid, LootType loottype)
@@ -3040,30 +3040,26 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/)
// this effect use before aura Taunt apply for prevent taunt already attacking target
// for spell as marked "non effective at already attacking target"
- if (!unitTarget || !unitTarget->CanHaveThreatList() || unitTarget->GetVictim() == m_caster)
+ if (!unitTarget || !unitTarget->CanHaveThreatList())
{
SendCastResult(SPELL_FAILED_DONT_REPORT);
return;
}
- if (m_spellInfo->Id == 62124 && (!unitTarget->IsPet() || !unitTarget->GetOwnerGUID().IsPlayer()))
- m_caster->CastSpell(unitTarget, 67485, true);
-
- if (!unitTarget->GetThreatManager().getOnlineContainer().empty())
+ ThreatManager& mgr = unitTarget->GetThreatManager();
+ if (mgr.GetCurrentVictim() == m_caster)
{
- // Also use this effect to set the taunter's threat to the taunted creature's highest value
- float myThreat = unitTarget->GetThreatManager().getThreat(m_caster);
- float topThreat = unitTarget->GetThreatManager().getOnlineContainer().getMostHated()->getThreat();
- if (topThreat > myThreat)
- unitTarget->GetThreatManager().doAddThreat(m_caster, topThreat - myThreat);
-
- //Set aggro victim to caster
- if (HostileReference* forcedVictim = unitTarget->GetThreatManager().getOnlineContainer().getReferenceByTarget(m_caster))
- unitTarget->GetThreatManager().setCurrentVictim(forcedVictim);
+ SendCastResult(SPELL_FAILED_DONT_REPORT);
+ return;
}
- if (unitTarget->ToCreature()->IsAIEnabled && !unitTarget->ToCreature()->HasReactState(REACT_PASSIVE))
- unitTarget->ToCreature()->AI()->AttackStart(m_caster);
+ // Hand of Reckoning
+ if (m_spellInfo->Id == 62124)
+ m_caster->CastSpell(unitTarget, 67485, true);
+
+ if (!mgr.IsThreatListEmpty())
+ // Set threat equal to highest threat currently on target
+ mgr.MatchUnitThreatToHighestThreat(m_caster);
}
void Spell::EffectWeaponDmg(SpellEffIndex effIndex)
@@ -3354,13 +3350,13 @@ void Spell::EffectThreat(SpellEffIndex /*effIndex*/)
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
- if (!unitTarget || !unitTarget->IsAlive() || !m_caster->IsAlive())
+ if (!unitTarget || !m_caster->IsAlive())
return;
if (!unitTarget->CanHaveThreatList())
return;
- unitTarget->GetThreatManager().AddThreat(m_caster, float(damage));
+ unitTarget->GetThreatManager().AddThreat(m_caster, float(damage), m_spellInfo, true);
}
void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/)
@@ -3925,20 +3921,19 @@ void Spell::EffectSanctuary(SpellEffIndex /*effIndex*/)
if (!unitTarget)
return;
- if (unitTarget->GetTypeId() == TYPEID_PLAYER)
- unitTarget->ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
-
- unitTarget->getHostileRefManager().UpdateVisibility();
-
- Unit::AttackerSet const& attackers = unitTarget->getAttackers();
- for (Unit::AttackerSet::const_iterator itr = attackers.begin(); itr != attackers.end();)
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER && !unitTarget->GetMap()->IsDungeon())
{
- if (!(*itr)->CanSeeOrDetect(unitTarget))
- (*(itr++))->AttackStop();
- else
- ++itr;
+ // stop all pve combat for players outside dungeons, suppress pvp combat
+ unitTarget->CombatStop(false, false);
+ }
+ else
+ {
+ // in dungeons (or for nonplayers), reset this unit on all enemies' threat lists
+ for (auto const& pair : unitTarget->GetThreatManager().GetThreatenedByMeList())
+ pair.second->SetThreat(0.0f);
}
+ // makes spells cast before this time fizzle
unitTarget->m_lastSanctuaryTime = GameTime::GetGameTimeMS();
}
@@ -5478,7 +5473,7 @@ void Spell::EffectRedirectThreat(SpellEffIndex /*effIndex*/)
return;
if (unitTarget)
- m_caster->SetRedirectThreat(unitTarget->GetGUID(), uint32(damage));
+ m_caster->GetThreatManager().RegisterRedirectThreat(m_spellInfo->Id, unitTarget->GetGUID(), uint32(damage));
}
void Spell::EffectGameObjectDamage(SpellEffIndex /*effIndex*/)
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index f17b2481ea3..873bfbefdcf 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -1645,8 +1645,8 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta
// creature/player specific target checks
if (unitTarget)
{
- // spells cannot be cast if player is in fake combat also
- if (HasAttribute(SPELL_ATTR1_CANT_TARGET_IN_COMBAT) && (unitTarget->IsInCombat() || unitTarget->IsPetInCombat()))
+ // spells cannot be cast if target has a pet in combat either
+ if (HasAttribute(SPELL_ATTR1_CANT_TARGET_IN_COMBAT) && (unitTarget->IsInCombat() || unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT)))
return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
// only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts