diff options
| author | Treeston <treeston.mmoc@gmail.com> | 2018-01-03 20:04:19 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-01-03 20:04:19 +0100 |
| commit | 532ab1c7f8653d1a2e48aa1f1f8a9ba1041d4bb7 (patch) | |
| tree | 81e2f7eb89b3144c14dd488ea6304f6d44d19848 /src/server/game/Spells | |
| parent | 425b181544a21d2246fdf0261ba76a37e2510883 (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.cpp | 79 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.h | 1 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 9 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 61 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 4 |
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 |
