refactor(Core/Combat): Port TrinityCore heap-based threat system (#24715)
Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> Co-authored-by: Treeston <treeston.mmoc@gmail.com> Co-authored-by: killerwife <killerwife@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -329,11 +329,11 @@ Unit::Unit() : WorldObject(),
|
||||
m_removedAurasCount(0),
|
||||
i_motionMaster(new MotionMaster(this)),
|
||||
m_regenTimer(0),
|
||||
m_ThreatMgr(this),
|
||||
m_threatManager(this),
|
||||
m_combatManager(this),
|
||||
m_vehicle(nullptr),
|
||||
m_vehicleKit(nullptr),
|
||||
m_unitTypeMask(UNIT_MASK_NONE),
|
||||
m_HostileRefMgr(this),
|
||||
m_comboTarget(nullptr),
|
||||
m_comboPoints(0)
|
||||
{
|
||||
@@ -406,16 +406,11 @@ Unit::Unit() : WorldObject(),
|
||||
m_CombatTimer = 0;
|
||||
m_lastManaUse = 0;
|
||||
|
||||
for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
|
||||
m_threatModifier[i] = 1.0f;
|
||||
|
||||
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
|
||||
m_speed_rate[i] = 1.0f;
|
||||
|
||||
m_charmInfo = nullptr;
|
||||
|
||||
_redirectThreatInfo = RedirectThreatInfo();
|
||||
|
||||
// remove aurastates allowing special moves
|
||||
for (uint8 i = 0; i < MAX_REACTIVE; ++i)
|
||||
m_reactiveTimer[i] = 0;
|
||||
@@ -439,6 +434,7 @@ Unit::Unit() : WorldObject(),
|
||||
_oldFactionId = 0;
|
||||
|
||||
_isWalkingBeforeCharm = false;
|
||||
_isCombatDisallowed = false;
|
||||
|
||||
_lastExtraAttackSpell = 0;
|
||||
}
|
||||
@@ -529,16 +525,13 @@ void Unit::Update(uint32 p_time)
|
||||
|
||||
_UpdateSpells( p_time );
|
||||
|
||||
if (CanHaveThreatList() && GetThreatMgr().isNeedUpdateToClient(p_time))
|
||||
SendThreatListUpdate();
|
||||
|
||||
// update combat timer only for players and pets (only pets with PetAI)
|
||||
if (IsInCombat() && (IsPlayer() || ((IsPet() || HasUnitTypeMask(UNIT_MASK_CONTROLLABLE_GUARDIAN)) && IsControlledByPlayer())))
|
||||
{
|
||||
// Check UNIT_STATE_MELEE_ATTACKING or UNIT_STATE_CHASE (without UNIT_STATE_FOLLOW in this case) so pets can reach far away
|
||||
// targets without stopping half way there and running off.
|
||||
// These flags are reset after target dies or another command is given.
|
||||
if (m_HostileRefMgr.IsEmpty())
|
||||
if (!GetCombatManager().HasCombat())
|
||||
{
|
||||
// m_CombatTimer set at aura start and it will be freeze until aura removing
|
||||
if (m_CombatTimer <= p_time)
|
||||
@@ -548,6 +541,8 @@ void Unit::Update(uint32 p_time)
|
||||
}
|
||||
}
|
||||
|
||||
m_combatManager.Update(p_time);
|
||||
|
||||
_lastDamagedTargetGuid = ObjectGuid::Empty;
|
||||
if (_lastExtraAttackSpell)
|
||||
{
|
||||
@@ -1197,9 +1192,6 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
|
||||
|
||||
if (attacker && attacker != victim)
|
||||
{
|
||||
if (spellProto && victim->CanHaveThreatList() && !victim->HasUnitState(UNIT_STATE_EVADE) && !victim->IsInCombatWith(attacker))
|
||||
victim->CombatStart(attacker, !(spellProto->AttributesEx3 & SPELL_ATTR3_SUPPRESS_TARGET_PROCS));
|
||||
|
||||
victim->AddThreat(attacker, float(damage), damageSchoolMask, spellProto);
|
||||
}
|
||||
}
|
||||
@@ -2701,7 +2693,7 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BASE_A
|
||||
// CombatStart puts the target into stand state, so we need to cache sit state here to know if we should crit later
|
||||
const bool sittingVictim = victim->IsPlayer() && (victim->IsSitState() || victim->getStandState() == UNIT_STAND_STATE_SLEEP);
|
||||
|
||||
CombatStart(victim);
|
||||
AtTargetAttacked(victim, true);
|
||||
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MELEE_ATTACK);
|
||||
|
||||
if (attType != BASE_ATTACK && attType != OFF_ATTACK)
|
||||
@@ -7348,14 +7340,10 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
|
||||
// set position before any AI calls/assistance
|
||||
//if (IsCreature())
|
||||
// ToCreature()->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());
|
||||
if (creature && !(IsControllableGuardian() && IsControlledByPlayer()))
|
||||
if (creature && !IsControlledByPlayer())
|
||||
{
|
||||
// should not let player enter combat by right clicking target - doesn't helps
|
||||
SetInCombatWith(victim);
|
||||
if (victim->IsPlayer())
|
||||
victim->SetInCombatWith(this);
|
||||
|
||||
AddThreat(victim, 0.0f);
|
||||
EngageWithTarget(victim);
|
||||
|
||||
creature->SendAIReaction(AI_REACTION_HOSTILE);
|
||||
|
||||
@@ -7415,7 +7403,7 @@ bool Unit::AttackStop()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Unit::CombatStop(bool includingCast)
|
||||
void Unit::CombatStop(bool includingCast, bool mutualPvP)
|
||||
{
|
||||
if (includingCast && IsNonMeleeSpellCast(false))
|
||||
InterruptNonMeleeSpells(false);
|
||||
@@ -7426,7 +7414,14 @@ void Unit::CombatStop(bool includingCast)
|
||||
ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
|
||||
if (Creature* pCreature = ToCreature())
|
||||
pCreature->ClearLastLeashExtensionTimePtr();
|
||||
ClearInCombat();
|
||||
|
||||
if (mutualPvP)
|
||||
ClearInCombat();
|
||||
else
|
||||
{
|
||||
GetCombatManager().EndAllPvECombat();
|
||||
GetCombatManager().SuppressPvPCombat();
|
||||
}
|
||||
|
||||
// xinef: just in case
|
||||
if (IsPetInCombat() && !IsPlayer())
|
||||
@@ -7441,6 +7436,16 @@ void Unit::CombatStopWithPets(bool includingCast)
|
||||
(*itr)->CombatStop(includingCast);
|
||||
}
|
||||
|
||||
void Unit::EngageWithTarget(Unit* who)
|
||||
{
|
||||
if (!who)
|
||||
return;
|
||||
if (CanHaveThreatList())
|
||||
m_threatManager.AddThreat(who, 0.0f, nullptr, true, true);
|
||||
else
|
||||
SetInCombatWith(who);
|
||||
}
|
||||
|
||||
bool Unit::isAttackingPlayer() const
|
||||
{
|
||||
if (HasUnitState(UNIT_STATE_ATTACK_PLAYER))
|
||||
@@ -7460,6 +7465,38 @@ bool Unit::isAttackingPlayer() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void Unit::UpdatePetCombatState()
|
||||
{
|
||||
ASSERT(!IsPet()); // player pets do not use UNIT_FLAG_PET_IN_COMBAT for this purpose
|
||||
|
||||
bool state = false;
|
||||
for (Unit* minion : m_Controlled)
|
||||
if (minion->IsInCombat())
|
||||
{
|
||||
state = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state)
|
||||
SetUnitFlag(UNIT_FLAG_PET_IN_COMBAT);
|
||||
else
|
||||
RemoveUnitFlag(UNIT_FLAG_PET_IN_COMBAT);
|
||||
}
|
||||
|
||||
void Unit::AtExitCombat()
|
||||
{
|
||||
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LEAVE_COMBAT);
|
||||
}
|
||||
|
||||
void Unit::AtEngage(Unit* /*target*/)
|
||||
{
|
||||
if (HasUnitState(UNIT_STATE_DISTRACTED))
|
||||
{
|
||||
ClearUnitState(UNIT_STATE_DISTRACTED);
|
||||
GetMotionMaster()->MovementExpiredOnSlot(MOTION_SLOT_CONTROLLED, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove all units in m_attackers list and send them AttackStop()
|
||||
*/
|
||||
@@ -8262,13 +8299,10 @@ void Unit::SendEnergizeSpellLog(Unit* victim, uint32 spellID, uint32 damage, Pow
|
||||
|
||||
void Unit::EnergizeBySpell(Unit* victim, uint32 spellID, uint32 damage, Powers powerType)
|
||||
{
|
||||
int32 gainedPower = victim->ModifyPower(powerType, damage, false);
|
||||
victim->ModifyPower(powerType, damage, false);
|
||||
|
||||
if (powerType != POWER_HAPPINESS && gainedPower)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID);
|
||||
victim->getHostileRefMgr().threatAssist(this, float(gainedPower) * 0.5f, spellInfo);
|
||||
}
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID))
|
||||
victim->GetThreatMgr().ForwardThreatForAssistingMe(this, float(damage) / 2.0f, spellInfo, true);
|
||||
|
||||
SendEnergizeSpellLog(victim, spellID, damage, powerType);
|
||||
}
|
||||
@@ -10519,245 +10553,56 @@ void Unit::Dismount()
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::SetInCombatWith(Unit* enemy, uint32 duration)
|
||||
{
|
||||
// Xinef: Dont allow to start combat with triggers
|
||||
if (enemy->IsCreature() && enemy->ToCreature()->IsTrigger())
|
||||
return;
|
||||
|
||||
Unit* eOwner = enemy->GetCharmerOrOwnerOrSelf();
|
||||
if (eOwner->IsPvP() || eOwner->IsFFAPvP())
|
||||
{
|
||||
SetInCombatState(true, enemy, duration);
|
||||
return;
|
||||
}
|
||||
|
||||
// check for duel
|
||||
if (eOwner->IsPlayer() && eOwner->ToPlayer()->duel)
|
||||
{
|
||||
Unit const* myOwner = GetCharmerOrOwnerOrSelf();
|
||||
if (((Player const*)eOwner)->duel->Opponent == myOwner)
|
||||
{
|
||||
SetInCombatState(true, enemy, duration);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetInCombatState(false, enemy, duration);
|
||||
}
|
||||
|
||||
void Unit::SetImmuneToPC(bool apply, bool keepCombat)
|
||||
{
|
||||
(void)keepCombat;
|
||||
if (apply)
|
||||
{
|
||||
SetUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
|
||||
if (!keepCombat)
|
||||
{
|
||||
std::vector<CombatReference*> toEnd;
|
||||
for (auto const& pair : m_combatManager.GetPvECombatRefs())
|
||||
if (pair.second->GetOther(this)->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
toEnd.push_back(pair.second);
|
||||
for (auto const& pair : m_combatManager.GetPvPCombatRefs())
|
||||
toEnd.push_back(pair.second);
|
||||
for (CombatReference* ref : toEnd)
|
||||
ref->EndCombat();
|
||||
}
|
||||
}
|
||||
else
|
||||
RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
|
||||
}
|
||||
|
||||
void Unit::SetImmuneToNPC(bool apply, bool keepCombat)
|
||||
{
|
||||
(void)keepCombat;
|
||||
if (apply)
|
||||
{
|
||||
SetUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
|
||||
if (!keepCombat)
|
||||
{
|
||||
std::vector<CombatReference*> toEnd;
|
||||
for (auto const& pair : m_combatManager.GetPvECombatRefs())
|
||||
if (!pair.second->GetOther(this)->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
toEnd.push_back(pair.second);
|
||||
for (CombatReference* ref : toEnd)
|
||||
ref->EndCombat();
|
||||
}
|
||||
}
|
||||
else
|
||||
RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
|
||||
}
|
||||
|
||||
void Unit::CombatStart(Unit* victim, bool initialAggro)
|
||||
{
|
||||
// Xinef: Dont allow to start combat with triggers
|
||||
if (victim->IsCreature() && victim->ToCreature()->IsTrigger())
|
||||
return;
|
||||
|
||||
if (initialAggro)
|
||||
{
|
||||
// Make player victim stand up automatically
|
||||
if (victim->getStandState() && victim->IsPlayer())
|
||||
{
|
||||
victim->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
}
|
||||
|
||||
if (!victim->IsInCombat() && !victim->IsPlayer() && !victim->ToCreature()->HasReactState(REACT_PASSIVE) && victim->ToCreature()->IsAIEnabled)
|
||||
{
|
||||
if (victim->IsPet())
|
||||
victim->ToCreature()->AI()->AttackedBy(this); // PetAI has special handler before AttackStart()
|
||||
else
|
||||
{
|
||||
victim->ToCreature()->AI()->AttackStart(this);
|
||||
// if the target is an NPC with a pet or minion, pet should react.
|
||||
if (Unit* victimControlledUnit = victim->GetFirstControlled())
|
||||
{
|
||||
victimControlledUnit->SetInCombatWith(this);
|
||||
SetInCombatWith(victimControlledUnit);
|
||||
victimControlledUnit->AddThreat(this, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// if unit has an owner, put owner in combat.
|
||||
if (Unit* victimOwner = victim->GetOwner())
|
||||
{
|
||||
if (!(victimOwner->IsInCombatWith(this)))
|
||||
{
|
||||
/* warding off to not take over aggro for no reason
|
||||
Using only AddThreat causes delay in attack */
|
||||
if (!victimOwner->IsInCombat() && victimOwner->IsAIEnabled)
|
||||
{
|
||||
victimOwner->ToCreature()->AI()->AttackStart(this);
|
||||
}
|
||||
victimOwner->SetInCombatWith(this);
|
||||
SetInCombatWith(victimOwner);
|
||||
victimOwner->AddThreat(this, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool alreadyInCombat = IsInCombat();
|
||||
|
||||
SetInCombatWith(victim);
|
||||
victim->SetInCombatWith(this);
|
||||
|
||||
// Update leash timer when attacking creatures
|
||||
if (victim->IsCreature() && this != victim)
|
||||
victim->ToCreature()->UpdateLeashExtensionTime();
|
||||
|
||||
// Xinef: If pet started combat - put owner in combat
|
||||
if (!alreadyInCombat && IsInCombat())
|
||||
{
|
||||
if (Unit* owner = GetOwner())
|
||||
{
|
||||
owner->SetInCombatWith(victim);
|
||||
victim->SetInCombatWith(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Unit* who = victim->GetCharmerOrOwnerOrSelf();
|
||||
if (who->IsPlayer())
|
||||
SetContestedPvP(who->ToPlayer());
|
||||
|
||||
Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
if (player && who->IsPvP() && (!who->IsPlayer() || !player->duel || player->duel->Opponent != who))
|
||||
{
|
||||
player->UpdatePvP(true);
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::CombatStartOnCast(Unit* target, bool initialAggro, uint32 duration)
|
||||
{
|
||||
// Xinef: Dont allow to start combat with triggers
|
||||
if (target->IsCreature() && target->ToCreature()->IsTrigger())
|
||||
return;
|
||||
|
||||
if (initialAggro)
|
||||
{
|
||||
SetInCombatWith(target, duration);
|
||||
|
||||
// Xinef: If pet started combat - put owner in combat
|
||||
if (Unit* owner = GetOwner())
|
||||
owner->SetInCombatWith(target, duration);
|
||||
|
||||
// Update leash timer when attacking creatures
|
||||
if (target->IsCreature())
|
||||
target->ToCreature()->UpdateLeashExtensionTime();
|
||||
else if (ToCreature()) // Reset leash if it is a spell caster, else it may evade inbetween casts
|
||||
ToCreature()->UpdateLeashExtensionTime();
|
||||
}
|
||||
|
||||
Unit* who = target->GetCharmerOrOwnerOrSelf();
|
||||
if (who->IsPlayer())
|
||||
SetContestedPvP(who->ToPlayer());
|
||||
|
||||
Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
if (player && who->IsPvP() && (!who->IsPlayer() || !player->duel || player->duel->Opponent != who))
|
||||
{
|
||||
player->UpdatePvP(true);
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::SetInCombatState(bool PvP, Unit* enemy, uint32 duration)
|
||||
{
|
||||
// only alive units can be in combat
|
||||
if (!IsAlive())
|
||||
return;
|
||||
|
||||
if (PvP)
|
||||
m_CombatTimer = std::max<uint32>(GetCombatTimer(), std::max<uint32>(5500, duration));
|
||||
else if (duration)
|
||||
m_CombatTimer = std::max<uint32>(GetCombatTimer(), duration);
|
||||
|
||||
if (HasUnitState(UNIT_STATE_EVADE) || GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET)
|
||||
return;
|
||||
|
||||
// xinef: if we somehow engage in combat (scripts, dunno) with player, remove this flag so he can fight back
|
||||
if (IsCreature() && enemy && IsImmuneToPC() && enemy->GetCharmerOrOwnerPlayerOrPlayerItself())
|
||||
SetImmuneToPC(false); // unit has engaged in combat, remove immunity so players can fight back
|
||||
|
||||
if (IsInCombat())
|
||||
return;
|
||||
|
||||
SetUnitFlag(UNIT_FLAG_IN_COMBAT);
|
||||
|
||||
if (Creature* creature = ToCreature())
|
||||
{
|
||||
// Set home position at place of engaging combat for escorted creatures
|
||||
if ((IsAIEnabled && creature->AI()->IsEscorted()) ||
|
||||
GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE ||
|
||||
GetMotionMaster()->GetCurrentMovementGeneratorType() == ESCORT_MOTION_TYPE)
|
||||
creature->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
|
||||
|
||||
if (enemy)
|
||||
{
|
||||
creature->UpdateLeashExtensionTime();
|
||||
|
||||
if (IsAIEnabled)
|
||||
creature->AI()->JustEngagedWith(enemy);
|
||||
|
||||
if (creature->GetFormation())
|
||||
creature->GetFormation()->MemberEngagingTarget(creature, enemy);
|
||||
|
||||
sScriptMgr->OnUnitEnterCombat(creature, enemy);
|
||||
}
|
||||
|
||||
creature->RefreshSwimmingFlag();
|
||||
|
||||
if (IsPet())
|
||||
{
|
||||
UpdateSpeed(MOVE_RUN, true);
|
||||
UpdateSpeed(MOVE_SWIM, true);
|
||||
UpdateSpeed(MOVE_FLIGHT, true);
|
||||
}
|
||||
|
||||
if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT))
|
||||
Dismount();
|
||||
if (!IsStandState()) // pussywizard: already done in CombatStart(target, initialAggro) for the target, but when aggro'ing from MoveInLOS CombatStart is not called!
|
||||
SetStandState(UNIT_STAND_STATE_STAND);
|
||||
}
|
||||
|
||||
for (Unit::ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();)
|
||||
{
|
||||
Unit* controlled = *itr;
|
||||
++itr;
|
||||
|
||||
// Xinef: Dont set combat for passive units, they will evade in next update...
|
||||
if (controlled->IsCreature() && controlled->ToCreature()->HasReactState(REACT_PASSIVE))
|
||||
continue;
|
||||
|
||||
controlled->SetInCombatState(PvP, enemy, duration);
|
||||
}
|
||||
|
||||
if (Player* player = this->ToPlayer())
|
||||
{
|
||||
sScriptMgr->OnPlayerEnterCombat(player, enemy);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::ClearInCombat()
|
||||
{
|
||||
m_CombatTimer = 0;
|
||||
RemoveUnitFlag(UNIT_FLAG_IN_COMBAT);
|
||||
// Let CombatManager handle flag removal through UpdateOwnerCombatState
|
||||
// so that AtExitCombat/AtDisengage callbacks properly fire.
|
||||
GetCombatManager().EndAllCombat();
|
||||
// If flag is still set (e.g. set by old code path without CombatReference),
|
||||
// remove it directly.
|
||||
if (IsInCombat())
|
||||
RemoveUnitFlag(UNIT_FLAG_IN_COMBAT);
|
||||
|
||||
// Player's state will be cleared in Player::UpdateContestedPvP
|
||||
if (Creature* creature = ToCreature())
|
||||
@@ -11454,8 +11299,6 @@ void Unit::setDeathState(DeathState s, bool despawn)
|
||||
if (s != DeathState::Alive && s != DeathState::JustRespawned)
|
||||
{
|
||||
CombatStop();
|
||||
GetThreatMgr().ClearAllThreat();
|
||||
getHostileRefMgr().deleteReferences();
|
||||
ClearComboPointHolders(); // any combo points pointed to unit lost at it death
|
||||
|
||||
if (IsNonMeleeSpellCast(false))
|
||||
@@ -11508,118 +11351,56 @@ void Unit::setDeathState(DeathState s, bool despawn)
|
||||
########################################*/
|
||||
bool Unit::CanHaveThreatList(bool skipAliveCheck) const
|
||||
{
|
||||
// only creatures can have threat list
|
||||
if (!IsCreature())
|
||||
// Delegate to ThreatManager's cached value for consistency
|
||||
if (!m_threatManager.CanHaveThreatList())
|
||||
return false;
|
||||
|
||||
// only alive units can have threat list
|
||||
if (!skipAliveCheck && !IsAlive())
|
||||
return false;
|
||||
|
||||
// totems can not have threat list
|
||||
if (ToCreature()->IsTotem())
|
||||
return false;
|
||||
|
||||
// vehicles can not have threat list
|
||||
// vehicles can not have threat list in battlegrounds
|
||||
if (ToCreature()->IsVehicle() && GetMap()->IsBattlegroundOrArena())
|
||||
return false;
|
||||
|
||||
// summons can not have a threat list, unless they are controlled by a creature
|
||||
if (HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLLABLE_GUARDIAN) && ((Pet*)this)->GetOwnerGUID().IsPlayer())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
float Unit::ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask)
|
||||
{
|
||||
if (!HasThreatAura() || fThreat < 0)
|
||||
return fThreat;
|
||||
|
||||
SpellSchools school = GetFirstSchoolInMask(schoolMask);
|
||||
|
||||
return fThreat * m_threatModifier[school];
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell)
|
||||
void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask /*schoolMask*/, SpellInfo const* threatSpell)
|
||||
{
|
||||
// Only mobs can manage threat lists
|
||||
if (CanHaveThreatList() && !HasUnitState(UNIT_STATE_EVADE))
|
||||
{
|
||||
m_ThreatMgr.AddThreat(victim, fThreat, schoolMask, threatSpell);
|
||||
m_threatManager.AddThreat(victim, fThreat, threatSpell);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void Unit::TauntApply(Unit* taunter)
|
||||
void Unit::AtTargetAttacked(Unit* target, bool canInitialAggro)
|
||||
{
|
||||
ASSERT(IsCreature());
|
||||
|
||||
if (!taunter || (taunter->IsPlayer() && taunter->ToPlayer()->IsGameMaster()))
|
||||
if (!target->IsEngaged() && !canInitialAggro)
|
||||
return;
|
||||
|
||||
if (!CanHaveThreatList())
|
||||
return;
|
||||
target->EngageWithTarget(this);
|
||||
if (Unit* targetOwner = target->GetCharmerOrOwner())
|
||||
targetOwner->EngageWithTarget(this);
|
||||
|
||||
Creature* creature = ToCreature();
|
||||
// Patch 3.0.8: All player spells which cause a creature to become aggressive
|
||||
// to you will now also immediately cause the creature to be tapped.
|
||||
if (Creature* creature = target->ToCreature())
|
||||
if (!creature->hasLootRecipient() && IsPlayer())
|
||||
creature->SetLootRecipient(this);
|
||||
|
||||
if (creature->HasReactState(REACT_PASSIVE))
|
||||
return;
|
||||
|
||||
Unit* target = GetVictim();
|
||||
if (target && target == taunter)
|
||||
return;
|
||||
|
||||
SetInFront(taunter);
|
||||
SetGuidValue(UNIT_FIELD_TARGET, taunter->GetGUID());
|
||||
|
||||
if (creature->IsAIEnabled)
|
||||
creature->AI()->AttackStart(taunter);
|
||||
|
||||
//m_ThreatMgr.tauntApply(taunter);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void Unit::TauntFadeOut(Unit* taunter)
|
||||
{
|
||||
ASSERT(IsCreature());
|
||||
|
||||
if (!taunter || (taunter->IsPlayer() && taunter->ToPlayer()->IsGameMaster()))
|
||||
return;
|
||||
|
||||
if (!CanHaveThreatList())
|
||||
return;
|
||||
|
||||
Creature* creature = ToCreature();
|
||||
|
||||
if (creature->HasReactState(REACT_PASSIVE))
|
||||
return;
|
||||
|
||||
Unit* target = GetVictim();
|
||||
if (!target || target != taunter)
|
||||
return;
|
||||
|
||||
if (m_ThreatMgr.isThreatListEmpty())
|
||||
Player* myPlayerOwner = GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
Player* targetPlayerOwner = target->GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
if (myPlayerOwner && targetPlayerOwner && !(myPlayerOwner->duel && myPlayerOwner->duel->Opponent == targetPlayerOwner))
|
||||
{
|
||||
if (creature->IsAIEnabled)
|
||||
creature->AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
|
||||
return;
|
||||
}
|
||||
|
||||
target = creature->SelectVictim(); // might have more taunt auras remaining
|
||||
|
||||
if (target && target != taunter)
|
||||
{
|
||||
SetGuidValue(UNIT_FIELD_TARGET, target->GetGUID());
|
||||
SetInFront(target);
|
||||
if (creature->IsAIEnabled)
|
||||
creature->AI()->AttackStart(target);
|
||||
myPlayerOwner->UpdatePvP(true);
|
||||
myPlayerOwner->SetContestedPvP(targetPlayerOwner);
|
||||
myPlayerOwner->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11633,21 +11414,11 @@ Unit* Creature::SelectVictim()
|
||||
|
||||
Unit* target = nullptr;
|
||||
|
||||
// First checking if we have some taunt on us
|
||||
AuraEffectList const& tauntAuras = GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT);
|
||||
if (!tauntAuras.empty())
|
||||
for (Unit::AuraEffectList::const_reverse_iterator itr = tauntAuras.rbegin(); itr != tauntAuras.rend(); ++itr)
|
||||
if (Unit* caster = (*itr)->GetCaster())
|
||||
if (CanCreatureAttack(caster) && !caster->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, GetGUID()))
|
||||
{
|
||||
target = caster;
|
||||
break;
|
||||
}
|
||||
|
||||
// Taunt handling is now done inside ThreatManager via TauntState
|
||||
if (CanHaveThreatList())
|
||||
{
|
||||
if (!target && !m_ThreatMgr.isThreatListEmpty())
|
||||
target = m_ThreatMgr.getHostileTarget();
|
||||
if (!m_threatManager.IsThreatListEmpty())
|
||||
target = m_threatManager.GetCurrentVictim();
|
||||
}
|
||||
else if (!HasReactState(REACT_PASSIVE))
|
||||
{
|
||||
@@ -11671,9 +11442,10 @@ Unit* Creature::SelectVictim()
|
||||
else
|
||||
return nullptr;
|
||||
|
||||
if (target && CanCreatureAttack(target))
|
||||
if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target))
|
||||
{
|
||||
SetInFront(target);
|
||||
if (!HasSpellFocus())
|
||||
SetInFront(target);
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -12776,8 +12548,6 @@ void Unit::CleanupBeforeRemoveFromMap(bool finalCleanup)
|
||||
CombatStop();
|
||||
ClearComboPoints();
|
||||
ClearComboPointHolders();
|
||||
GetThreatMgr().ClearAllThreat();
|
||||
getHostileRefMgr().deleteReferences();
|
||||
GetMotionMaster()->Clear(false); // remove different non-standard movement generators.
|
||||
}
|
||||
|
||||
@@ -14290,7 +14060,6 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
|
||||
|
||||
// Stop attacks
|
||||
victim->CombatStop();
|
||||
victim->getHostileRefMgr().deleteReferences();
|
||||
|
||||
// restore for use at real death
|
||||
victim->SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);
|
||||
@@ -14352,8 +14121,6 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
|
||||
|
||||
if (!creature->IsPet() && creature->GetLootMode() > 0)
|
||||
{
|
||||
creature->GetThreatMgr().ClearAllThreat();
|
||||
|
||||
// must be after setDeathState which resets dynamic flags
|
||||
if (!creature->loot.isLooted())
|
||||
{
|
||||
@@ -14814,10 +14581,6 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
|
||||
CastStop();
|
||||
AttackStop();
|
||||
|
||||
// Xinef: dont reset threat and combat, put them on offline list, moved down after faction changes
|
||||
// CombatStop(); /// @todo: CombatStop(true) may cause crash (interrupt spells)
|
||||
// DeleteThreatList();
|
||||
|
||||
Player* playerCharmer = charmer->ToPlayer();
|
||||
|
||||
// Charmer stop charming
|
||||
@@ -15027,6 +14790,10 @@ void Unit::RemoveCharmedBy(Unit* charmer)
|
||||
ASSERT(type != CHARM_TYPE_POSSESS || charmer->IsPlayer());
|
||||
ASSERT(type != CHARM_TYPE_VEHICLE || (IsCreature() && IsVehicle()));
|
||||
|
||||
// Vehicle should not attack its passenger after he exits the seat
|
||||
if (type != CHARM_TYPE_VEHICLE)
|
||||
LastCharmerGUID = charmer->GetGUID();
|
||||
|
||||
charmer->SetCharm(this, false);
|
||||
|
||||
StopAttackingInvalidTarget();
|
||||
@@ -15478,41 +15245,29 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update)
|
||||
if (!sScriptMgr->CanSetPhaseMask(this, newPhaseMask, update))
|
||||
return;
|
||||
|
||||
// modify hostile references for new phasemask, some special cases deal with hostile references themselves
|
||||
if (IsCreature() || (!ToPlayer()->IsGameMaster() && !ToPlayer()->GetSession()->PlayerLogout()))
|
||||
{
|
||||
HostileRefMgr& refMgr = getHostileRefMgr();
|
||||
HostileReference* ref = refMgr.getFirst();
|
||||
|
||||
while (ref)
|
||||
{
|
||||
if (Unit* unit = ref->GetSource()->GetOwner())
|
||||
if (Creature* creature = unit->ToCreature())
|
||||
refMgr.setOnlineOfflineState(creature, creature->InSamePhase(newPhaseMask));
|
||||
|
||||
ref = ref->next();
|
||||
}
|
||||
|
||||
// modify threat lists for new phasemask
|
||||
if (!IsPlayer())
|
||||
{
|
||||
ThreatContainer::StorageType threatList = GetThreatMgr().GetThreatList();
|
||||
ThreatContainer::StorageType offlineThreatList = GetThreatMgr().GetOfflineThreatList();
|
||||
|
||||
// merge expects sorted lists
|
||||
threatList.sort();
|
||||
offlineThreatList.sort();
|
||||
threatList.merge(offlineThreatList);
|
||||
|
||||
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
|
||||
if (Unit* unit = (*itr)->getTarget())
|
||||
unit->getHostileRefMgr().setOnlineOfflineState(ToCreature(), unit->InSamePhase(newPhaseMask));
|
||||
}
|
||||
}
|
||||
// Phase-related threat updates are done AFTER the phase change below
|
||||
}
|
||||
|
||||
WorldObject::SetPhaseMask(newPhaseMask, false);
|
||||
|
||||
// Now update threat online states with the new phase mask applied
|
||||
if (IsCreature() || (IsPlayer() && !ToPlayer()->IsGameMaster() && !ToPlayer()->GetSession()->PlayerLogout()))
|
||||
{
|
||||
// Update online state for units that have me on their threat list
|
||||
for (auto const& pair : GetThreatMgr().GetThreatenedByMeList())
|
||||
{
|
||||
if (ThreatReference* ref = pair.second)
|
||||
ref->UpdateOffline();
|
||||
}
|
||||
|
||||
// Update online state for units on my threat list
|
||||
if (!IsPlayer())
|
||||
{
|
||||
for (ThreatReference* ref : GetThreatMgr().GetModifiableThreatList())
|
||||
ref->UpdateOffline();
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsInWorld())
|
||||
{
|
||||
return;
|
||||
@@ -15655,11 +15410,6 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId)
|
||||
return modelid;
|
||||
}
|
||||
|
||||
Unit* Unit::GetRedirectThreatTarget() const
|
||||
{
|
||||
return _redirectThreatInfo.GetTargetGUID() ? ObjectAccessor::GetUnit(*this, _redirectThreatInfo.GetTargetGUID()) : nullptr;
|
||||
}
|
||||
|
||||
bool Unit::IsAttackSpeedOverridenShapeShift() const
|
||||
{
|
||||
// Mirroring clientside gameplay logic
|
||||
@@ -16221,64 +15971,6 @@ void Unit::UpdateHeight(float newZ)
|
||||
GetVehicleKit()->RelocatePassengers();
|
||||
}
|
||||
|
||||
void Unit::SendThreatListUpdate()
|
||||
{
|
||||
if (!GetThreatMgr().isThreatListEmpty())
|
||||
{
|
||||
uint32 count = GetThreatMgr().GetThreatList().size();
|
||||
|
||||
//LOG_DEBUG("entities.unit", "WORLD: Send SMSG_THREAT_UPDATE Message");
|
||||
WorldPacket data(SMSG_THREAT_UPDATE, 8 + count * 8);
|
||||
data << GetPackGUID();
|
||||
data << uint32(count);
|
||||
ThreatContainer::StorageType const& tlist = GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr)
|
||||
{
|
||||
data << (*itr)->getUnitGuid().WriteAsPacked();
|
||||
data << uint32((*itr)->GetThreat() * 100);
|
||||
}
|
||||
SendMessageToSet(&data, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::SendChangeCurrentVictimOpcode(HostileReference* pHostileReference)
|
||||
{
|
||||
if (!GetThreatMgr().isThreatListEmpty())
|
||||
{
|
||||
uint32 count = GetThreatMgr().GetThreatList().size();
|
||||
|
||||
LOG_DEBUG("entities.unit", "WORLD: Send SMSG_HIGHEST_THREAT_UPDATE Message");
|
||||
WorldPacket data(SMSG_HIGHEST_THREAT_UPDATE, 8 + 8 + count * 8);
|
||||
data << GetPackGUID();
|
||||
data << pHostileReference->getUnitGuid().WriteAsPacked();
|
||||
data << uint32(count);
|
||||
ThreatContainer::StorageType const& tlist = GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr)
|
||||
{
|
||||
data << (*itr)->getUnitGuid().WriteAsPacked();
|
||||
data << uint32((*itr)->GetThreat() * 100);
|
||||
}
|
||||
SendMessageToSet(&data, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::SendClearThreatListOpcode()
|
||||
{
|
||||
LOG_DEBUG("entities.unit", "WORLD: Send SMSG_THREAT_CLEAR Message");
|
||||
WorldPacket data(SMSG_THREAT_CLEAR, 8);
|
||||
data << GetPackGUID();
|
||||
SendMessageToSet(&data, false);
|
||||
}
|
||||
|
||||
void Unit::SendRemoveFromThreatListOpcode(HostileReference* pHostileReference)
|
||||
{
|
||||
LOG_DEBUG("entities.unit", "WORLD: Send SMSG_THREAT_REMOVE Message");
|
||||
WorldPacket data(SMSG_THREAT_REMOVE, 8 + 8);
|
||||
data << GetPackGUID();
|
||||
data << pHostileReference->getUnitGuid().WriteAsPacked();
|
||||
SendMessageToSet(&data, false);
|
||||
}
|
||||
|
||||
void Unit::RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker)
|
||||
{
|
||||
// Rage formulae https://wowwiki-archive.fandom.com/wiki/Rage#Formulae
|
||||
@@ -16340,7 +16032,13 @@ void Unit::StopAttackFaction(uint32 faction_id)
|
||||
++itr;
|
||||
}
|
||||
|
||||
getHostileRefMgr().deleteReferencesForFaction(faction_id);
|
||||
// End combat and threat references with creatures in this faction
|
||||
std::vector<CombatReference*> refsToEnd;
|
||||
for (auto const& pair : m_combatManager.GetPvECombatRefs())
|
||||
if (pair.second->GetOther(this)->GetFactionTemplateEntry()->faction == faction_id)
|
||||
refsToEnd.push_back(pair.second);
|
||||
for (CombatReference* ref : refsToEnd)
|
||||
ref->EndCombat();
|
||||
|
||||
for (ControlSet::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
|
||||
(*itr)->StopAttackFaction(faction_id);
|
||||
@@ -17300,20 +16998,14 @@ DisplayRace Unit::GetDisplayRaceFromModelId(uint32 modelId) const
|
||||
// Check if unit in combat with specific unit
|
||||
bool Unit::IsInCombatWith(Unit const* who) const
|
||||
{
|
||||
// Check target exists
|
||||
if (!who)
|
||||
return false;
|
||||
// Search in threat list
|
||||
ObjectGuid guid = who->GetGUID();
|
||||
for (ThreatContainer::StorageType::const_iterator i = m_ThreatMgr.GetThreatList().begin(); i != m_ThreatMgr.GetThreatList().end(); ++i)
|
||||
{
|
||||
HostileReference* ref = (*i);
|
||||
// Return true if the unit matches
|
||||
if (ref && ref->getUnitGuid() == guid)
|
||||
return true;
|
||||
}
|
||||
// Nothing found, false.
|
||||
return false;
|
||||
return GetCombatManager().IsInCombatWith(who);
|
||||
}
|
||||
|
||||
bool Unit::IsThreatened() const
|
||||
{
|
||||
return !m_threatManager.IsThreatListEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "EnumFlag.h"
|
||||
#include "EventProcessor.h"
|
||||
#include "HostileRefMgr.h"
|
||||
#include "CombatManager.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "Object.h"
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "SharedDefines.h"
|
||||
#include "SpellAuraDefines.h"
|
||||
#include "SpellDefines.h"
|
||||
#include "ThreatMgr.h"
|
||||
#include "ThreatManager.h"
|
||||
#include "UnitDefines.h"
|
||||
#include "UnitUtils.h"
|
||||
#include <functional>
|
||||
@@ -902,7 +902,7 @@ public:
|
||||
[[nodiscard]] bool isAttackingPlayer() const;
|
||||
[[nodiscard]] Unit* GetVictim() const { return m_attacking; }
|
||||
|
||||
void CombatStop(bool includingCast = false);
|
||||
void CombatStop(bool includingCast = false, bool mutualPvP = true);
|
||||
void CombatStopWithPets(bool includingCast = false);
|
||||
void StopAttackFaction(uint32 faction_id);
|
||||
void StopAttackingInvalidTarget();
|
||||
@@ -928,17 +928,15 @@ public:
|
||||
void SetImmuneToNPC(bool apply, bool keepCombat = false);
|
||||
bool IsImmuneToNPC() const { return HasUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); }
|
||||
|
||||
bool IsEngaged() const { return IsInCombat(); }
|
||||
bool IsEngagedBy(Unit const* who) const { return IsInCombatWith(who); }
|
||||
virtual bool IsEngaged() const { return IsInCombat(); }
|
||||
bool IsEngagedBy(Unit const* who) const { return CanHaveThreatList() ? IsThreatenedBy(who) : IsInCombatWith(who); }
|
||||
void EngageWithTarget(Unit* who);
|
||||
|
||||
[[nodiscard]] bool IsInCombat() const { return HasUnitFlag(UNIT_FLAG_IN_COMBAT); }
|
||||
bool IsInCombatWith(Unit const* who) const;
|
||||
|
||||
[[nodiscard]] bool IsPetInCombat() const { return HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT); }
|
||||
void CombatStart(Unit* target, bool initialAggro = true);
|
||||
void CombatStartOnCast(Unit* target, bool initialAggro = true, uint32 duration = 0);
|
||||
void SetInCombatState(bool PvP, Unit* enemy = nullptr, uint32 duration = 0);
|
||||
void SetInCombatWith(Unit* enemy, uint32 duration = 0);
|
||||
void SetInCombatWith(Unit* enemy, bool addSecondUnitSuppressed = false) { if (enemy) m_combatManager.SetInCombatWith(enemy, addSecondUnitSuppressed); }
|
||||
void ClearInCombat();
|
||||
void ClearInPetCombat();
|
||||
[[nodiscard]] uint32 GetCombatTimer() const { return m_CombatTimer; }
|
||||
@@ -947,21 +945,28 @@ public:
|
||||
// Threat related methods
|
||||
[[nodiscard]] bool CanHaveThreatList(bool skipAliveCheck = false) const;
|
||||
void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr);
|
||||
float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
|
||||
void TauntApply(Unit* victim);
|
||||
void TauntFadeOut(Unit* taunter);
|
||||
ThreatMgr& GetThreatMgr() { return m_ThreatMgr; }
|
||||
ThreatMgr const& GetThreatMgr() const { return m_ThreatMgr; }
|
||||
void addHatedBy(HostileReference* pHostileReference) { m_HostileRefMgr.insertFirst(pHostileReference); };
|
||||
void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ }
|
||||
HostileRefMgr& getHostileRefMgr() { return m_HostileRefMgr; }
|
||||
void AtTargetAttacked(Unit* target, bool canInitialAggro);
|
||||
|
||||
// Redirect Threat
|
||||
void SetRedirectThreat(ObjectGuid guid, uint32 pct) { _redirectThreatInfo.Set(guid, pct); }
|
||||
void ResetRedirectThreat() { SetRedirectThreat(ObjectGuid::Empty, 0); }
|
||||
void ModifyRedirectThreat(int32 amount) { _redirectThreatInfo.ModifyThreatPct(amount); }
|
||||
uint32 GetRedirectThreatPercent() { return _redirectThreatInfo.GetThreatPct(); }
|
||||
[[nodiscard]] Unit* GetRedirectThreatTarget() const;
|
||||
// ThreatManager/CombatManager accessors
|
||||
ThreatManager& GetThreatMgr() { return m_threatManager; }
|
||||
ThreatManager const& GetThreatMgr() const { return m_threatManager; }
|
||||
CombatManager& GetCombatManager() { return m_combatManager; }
|
||||
CombatManager const& GetCombatManager() const { return m_combatManager; }
|
||||
|
||||
// Combat state methods
|
||||
[[nodiscard]] bool IsThreatened() const;
|
||||
[[nodiscard]] bool IsThreatenedBy(Unit const* who) const { return who && m_threatManager.IsThreatenedBy(who, true); }
|
||||
void UpdatePetCombatState();
|
||||
|
||||
virtual void AtEnterCombat() {}
|
||||
virtual void AtExitCombat();
|
||||
|
||||
// Engagement callbacks - override in Creature for creature-specific behavior
|
||||
virtual void AtEngage(Unit* target);
|
||||
virtual void AtDisengage() {}
|
||||
|
||||
[[nodiscard]] bool IsCombatDisallowed() const { return _isCombatDisallowed; }
|
||||
void SetIsCombatDisallowed(bool value) { _isCombatDisallowed = value; }
|
||||
|
||||
void SetLastDamagedTargetGuid(ObjectGuid const& guid) { _lastDamagedTargetGuid = guid; }
|
||||
[[nodiscard]] ObjectGuid const& GetLastDamagedTargetGuid() const { return _lastDamagedTargetGuid; }
|
||||
@@ -1818,7 +1823,7 @@ public:
|
||||
[[nodiscard]] bool HasIgnoreTargetResistAura() const { return HasAuraType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); }
|
||||
[[nodiscard]] bool HasIncreaseMountedSpeedAura() const { return HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED); }
|
||||
[[nodiscard]] bool HasIncreaseMountedFlightSpeedAura() const { return HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED); }
|
||||
[[nodiscard]] bool HasThreatAura() const { return HasAuraType(SPELL_AURA_MOD_THREAT); }
|
||||
|
||||
[[nodiscard]] bool HasAttackerSpellCritChanceAura() const { return HasAuraType(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE); }
|
||||
[[nodiscard]] bool HasUnattackableAura() const { return HasAuraType(SPELL_AURA_MOD_UNATTACKABLE); }
|
||||
[[nodiscard]] bool HasHealthRegenInCombatAura() const { return HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT); }
|
||||
@@ -2044,10 +2049,6 @@ public:
|
||||
void SendMovementFeatherFall(Player* sendTo);
|
||||
void SendMovementHover(Player* sendTo);
|
||||
|
||||
void SendChangeCurrentVictimOpcode(HostileReference* pHostileReference);
|
||||
void SendClearThreatListOpcode();
|
||||
void SendRemoveFromThreatListOpcode(HostileReference* pHostileReference);
|
||||
void SendThreatListUpdate();
|
||||
void SendClearTarget();
|
||||
|
||||
// Misc functions
|
||||
@@ -2077,7 +2078,6 @@ public:
|
||||
float m_modSpellHitChance;
|
||||
int32 m_baseSpellCritChance;
|
||||
|
||||
float m_threatModifier[MAX_SPELL_SCHOOL];
|
||||
float m_modAttackSpeedPct[3];
|
||||
|
||||
SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
|
||||
@@ -2087,6 +2087,7 @@ public:
|
||||
typedef std::set<PetAura const*> PetAuraSet;
|
||||
PetAuraSet m_petAuras;
|
||||
|
||||
ObjectGuid LastCharmerGUID;
|
||||
bool IsAIEnabled;
|
||||
bool NeedChangeAI;
|
||||
|
||||
@@ -2184,7 +2185,8 @@ protected:
|
||||
uint32 m_reactiveTimer[MAX_REACTIVE];
|
||||
int32 m_regenTimer;
|
||||
|
||||
ThreatMgr m_ThreatMgr;
|
||||
ThreatManager m_threatManager;
|
||||
CombatManager m_combatManager;
|
||||
typedef std::map<ObjectGuid, float> CharmThreatMap;
|
||||
CharmThreatMap _charmThreatInfo;
|
||||
|
||||
@@ -2225,8 +2227,6 @@ private:
|
||||
//TimeTrackerSmall m_movesplineTimer;
|
||||
|
||||
Diminishing m_Diminishing;
|
||||
// Manage all Units that are threatened by us
|
||||
HostileRefMgr m_HostileRefMgr;
|
||||
|
||||
std::unordered_set<AbstractFollower*> m_followingMe;
|
||||
|
||||
@@ -2234,13 +2234,12 @@ private:
|
||||
int8 m_comboPoints;
|
||||
std::unordered_set<Unit*> m_ComboPointHolders;
|
||||
|
||||
RedirectThreatInfo _redirectThreatInfo;
|
||||
|
||||
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
|
||||
|
||||
uint32 _oldFactionId; ///< faction before charm
|
||||
bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed?
|
||||
bool _isCombatDisallowed;
|
||||
|
||||
uint32 _lastExtraAttackSpell;
|
||||
std::unordered_map<ObjectGuid /*guid*/, uint32 /*count*/> extraAttacksTargets;
|
||||
|
||||
Reference in New Issue
Block a user