Core: Combat/threat system rewrite

Port From (https://github.com/TrinityCore/TrinityCore/commit/34c7810fe507eca1b8b9389630db5d5d26d92e77)
This commit is contained in:
hondacrx
2021-05-18 12:25:40 -04:00
parent 891c3b6478
commit 9851142796
37 changed files with 3454 additions and 3345 deletions
+137 -122
View File
@@ -19,6 +19,7 @@ using Framework.Constants;
using Framework.Database;
using Framework.Dynamic;
using Game.AI;
using Game.Combat;
using Game.DataStorage;
using Game.Groups;
using Game.Loots;
@@ -420,6 +421,8 @@ namespace Game.Entities
UpdateMovementFlags();
LoadCreaturesAddon();
LoadTemplateImmunities();
GetThreatManager().UpdateOnlineStates(true, true);
return true;
}
@@ -511,6 +514,8 @@ namespace Game.Entities
if (!IsAlive())
break;
GetThreatManager().Update(diff);
if (m_shouldReacquireTarget && !IsFocusing(null, true))
{
SetTarget(m_suppressedTarget);
@@ -903,10 +908,110 @@ namespace Game.Entities
ApplySpellImmune(0, SpellImmunity.Effect, SpellEffectName.KnockBackDest, true);
}
GetThreatManager().Initialize();
return true;
}
void InitializeReactState()
public Unit SelectVictim()
{
Unit target = null;
ThreatManager mgr = GetThreatManager();
if (mgr.CanHaveThreatList())
{
target = mgr.SelectVictim();
while (!target)
{
Unit newTarget = null;
// nothing found to attack - try to find something we're in combat with (but don't have a threat entry for yet) and start attacking it
foreach (var pair in GetCombatManager().GetPvECombatRefs())
{
newTarget = pair.Value.GetOther(this);
if (!mgr.IsThreatenedBy(newTarget, true))
{
mgr.AddThreat(newTarget, 0.0f, null, true, true);
break;
}
else
newTarget = null;
}
if (!newTarget)
break;
target = mgr.SelectVictim();
}
}
else if (!HasReactState(ReactStates.Passive))
{
// We're a player pet, probably
target = GetAttackerForHelper();
if (!target && IsSummon())
{
Unit owner = ToTempSummon().GetOwner();
if (owner != null)
{
if (owner.IsInCombat())
target = owner.GetAttackerForHelper();
if (!target)
{
foreach (var itr in owner.m_Controlled)
{
if (itr.IsInCombat())
{
target = itr.GetAttackerForHelper();
if (target)
break;
}
}
}
}
}
}
else
return null;
if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target))
{
if (!IsFocusing(null, true))
SetInFront(target);
return target;
}
/// @todo a vehicle may eat some mob, so mob should not evade
if (GetVehicle())
return null;
// search nearby enemy before enter evade mode
if (HasReactState(ReactStates.Passive))
{
target = SelectNearestTargetInAttackDistance(m_CombatDistance != 0 ? m_CombatDistance : SharedConst.AttackDistance);
if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target))
return target;
}
var iAuras = GetAuraEffectsByType(AuraType.ModInvisibility);
if (!iAuras.Empty())
{
foreach (var itr in iAuras)
{
if (itr.GetBase().IsPermanent())
{
GetAI().EnterEvadeMode(EvadeReason.Other);
break;
}
}
return null;
}
// enter in evade mode in other case
GetAI().EnterEvadeMode(EvadeReason.NoHostiles);
return null;
}
public void InitializeReactState()
{
if (IsTotem() || IsTrigger() || IsCritter() || IsSpiritService())
SetReactState(ReactStates.Passive);
@@ -994,6 +1099,37 @@ namespace Game.Entities
&& !GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoXpAtKill);
}
public override void AtEnterCombat()
{
base.AtEnterCombat();
if (!GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.MountedCombatAllowed))
Dismount();
if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed
{
UpdateSpeed(UnitMoveType.Run);
UpdateSpeed(UnitMoveType.Swim);
UpdateSpeed(UnitMoveType.Flight);
}
}
public override void AtExitCombat()
{
base.AtExitCombat();
ClearUnitState(UnitState.AttackPlayer);
if (HasDynamicFlag(UnitDynFlags.Tapped))
SetDynamicFlags((UnitDynFlags)GetCreatureTemplate().DynamicFlags);
if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed
{
UpdateSpeed(UnitMoveType.Run);
UpdateSpeed(UnitMoveType.Swim);
UpdateSpeed(UnitMoveType.Flight);
}
}
public bool IsEscortNPC(bool onlyIfActive = true)
{
if (!IsAIEnabled)
@@ -3147,127 +3283,6 @@ namespace Game.Entities
// There's many places not ready for dynamic spawns. This allows them to live on for now.
void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
public Unit SelectVictim()
{
// function provides main threat functionality
// next-victim-selection algorithm and evade mode are called
// threat list sorting etc.
Unit target = null;
// First checking if we have some taunt on us
var tauntAuras = GetAuraEffectsByType(AuraType.ModTaunt);
if (!tauntAuras.Empty())
{
Unit caster = tauntAuras.Last().GetCaster();
// The last taunt aura caster is alive an we are happy to attack him
if (caster != null && caster.IsAlive())
return GetVictim();
else if (tauntAuras.Count > 1)
{
// We do not have last taunt aura caster but we have more taunt auras,
// so find first available target
// Auras are pushed_back, last caster will be on the end
for (var i = tauntAuras.Count - 1; i >= 0; i--)
{
caster = tauntAuras[i].GetCaster();
if (caster != null && CanSeeOrDetect(caster, true) && IsValidAttackTarget(caster) && caster.IsInAccessiblePlaceFor(ToCreature()))
{
target = caster;
break;
}
}
}
else
target = GetVictim();
}
if (CanHaveThreatList())
{
if (target == null && !GetThreatManager().IsThreatListEmpty())
// No taunt aura or taunt aura caster is dead standard target selection
target = GetThreatManager().GetHostilTarget();
}
else if (!HasReactState(ReactStates.Passive))
{
// We have player pet probably
target = GetAttackerForHelper();
if (target == null && IsSummon())
{
Unit owner = ToTempSummon().GetOwner();
if (owner != null)
{
if (owner.IsInCombat())
target = owner.GetAttackerForHelper();
if (target == null)
{
foreach (var unit in owner.m_Controlled)
{
if (unit.IsInCombat())
{
target = unit.GetAttackerForHelper();
if (target)
break;
}
}
}
}
}
}
else
return null;
if (target != null && _IsTargetAcceptable(target) && CanCreatureAttack(target))
{
if (!IsFocusing(null, true))
SetInFront(target);
return target;
}
// last case when creature must not go to evade mode:
// it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list
// Note: creature does not have targeted movement generator but has attacker in this case
foreach (var unit in attackerList)
{
if (CanCreatureAttack(unit) && !unit.IsTypeId(TypeId.Player)
&& !unit.ToCreature().HasUnitTypeMask(UnitTypeMask.ControlableGuardian))
return null;
}
// @todo a vehicle may eat some mob, so mob should not evade
if (GetVehicle() != null)
return null;
// search nearby enemy before enter evade mode
if (HasReactState(ReactStates.Aggressive))
{
target = SelectNearestTargetInAttackDistance(m_CombatDistance != 0 ? m_CombatDistance : SharedConst.AttackDistance);
if (target != null && _IsTargetAcceptable(target) && CanCreatureAttack(target))
return target;
}
var iAuras = GetAuraEffectsByType(AuraType.ModInvisibility);
if (!iAuras.Empty())
{
foreach (var aura in iAuras)
{
if (aura.GetBase().IsPermanent())
{
GetAI().EnterEvadeMode();
break;
}
}
return null;
}
// enter in evade mode in other case
GetAI().EnterEvadeMode(EvadeReason.NoHostiles);
return null;
}
}
public class VendorItemCount