Core: Combat/threat system rewrite
Port From (https://github.com/TrinityCore/TrinityCore/commit/34c7810fe507eca1b8b9389630db5d5d26d92e77)
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Game.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Game.Combat
|
||||
{
|
||||
public class CombatManager
|
||||
{
|
||||
Unit _owner;
|
||||
Dictionary<ObjectGuid, CombatReference> _pveRefs = new();
|
||||
Dictionary<ObjectGuid, PvPCombatReference> _pvpRefs = new();
|
||||
|
||||
public CombatManager(Unit owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
public static bool CanBeginCombat(Unit a, Unit b)
|
||||
{
|
||||
// Checks combat validity before initial reference creation.
|
||||
// For the combat to be valid...
|
||||
// ...the two units need to be different
|
||||
if (a == b)
|
||||
return false;
|
||||
// ...the two units need to be in the world
|
||||
if (!a.IsInWorld || !b.IsInWorld)
|
||||
return false;
|
||||
// ...the two units need to both be alive
|
||||
if (!a.IsAlive() || !b.IsAlive())
|
||||
return false;
|
||||
// ...the two units need to be on the same map
|
||||
if (a.GetMap() != b.GetMap())
|
||||
return false;
|
||||
// ...the two units need to be in the same phase
|
||||
if (!WorldObject.InSamePhase(a, b))
|
||||
return false;
|
||||
if (a.HasUnitState(UnitState.Evade) || b.HasUnitState(UnitState.Evade))
|
||||
return false;
|
||||
if (a.HasUnitState(UnitState.InFlight) || b.HasUnitState(UnitState.InFlight))
|
||||
return false;
|
||||
if (a.IsControlledByPlayer() || b.IsControlledByPlayer())
|
||||
{
|
||||
// PvSomething, only block friendly fire
|
||||
if (a.IsFriendlyTo(b) || b.IsFriendlyTo(a))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// CvC, need hostile reaction to start a fight
|
||||
if (!a.IsHostileTo(b) && !b.IsHostileTo(a))
|
||||
return false;
|
||||
}
|
||||
Player playerA = a.GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
Player playerB = b.GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
// ...neither of the two units must be (owned by) a player with .gm on
|
||||
if ((playerA && playerA.IsGameMaster()) || (playerB && playerB.IsGameMaster()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update(uint tdiff)
|
||||
{
|
||||
foreach(var pair in _pvpRefs.ToList())
|
||||
{
|
||||
PvPCombatReference refe = pair.Value;
|
||||
if (refe.first == _owner && !refe.Update(tdiff)) // only update if we're the first unit involved (otherwise double decrement)
|
||||
{
|
||||
_pvpRefs.Remove(pair.Key);
|
||||
refe.EndCombat(); // this will remove it from the other side
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasPvPCombat()
|
||||
{
|
||||
foreach (var pair in _pvpRefs)
|
||||
if (!pair.Value.IsSuppressedFor(_owner))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit GetAnyTarget()
|
||||
{
|
||||
if (!_pveRefs.Empty())
|
||||
return _pveRefs.First().Value.GetOther(_owner);
|
||||
|
||||
foreach (var pair in _pvpRefs)
|
||||
if (!pair.Value.IsSuppressedFor(_owner))
|
||||
return pair.Value.GetOther(_owner);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool SetInCombatWith(Unit who)
|
||||
{
|
||||
// Are we already in combat? If yes, refresh pvp combat
|
||||
var pvpRefe = _pvpRefs.LookupByKey(who.GetGUID());
|
||||
if (pvpRefe != null)
|
||||
{
|
||||
pvpRefe.Refresh();
|
||||
return true;
|
||||
}
|
||||
else if (_pveRefs.ContainsKey(who.GetGUID()))
|
||||
return true;
|
||||
|
||||
// Otherwise, check validity...
|
||||
if (!CombatManager.CanBeginCombat(_owner, who))
|
||||
return false;
|
||||
|
||||
// ...then create new reference
|
||||
CombatReference refe;
|
||||
if (_owner.IsControlledByPlayer() && who.IsControlledByPlayer())
|
||||
refe = new PvPCombatReference(_owner, who);
|
||||
else
|
||||
refe = new CombatReference(_owner, who);
|
||||
|
||||
// ...and insert it into both managers
|
||||
PutReference(who.GetGUID(), refe);
|
||||
who.GetCombatManager().PutReference(_owner.GetGUID(), refe);
|
||||
|
||||
// now, sequencing is important - first we update the combat state, which will set both units in combat and do non-AI combat start stuff
|
||||
bool needSelfAI = UpdateOwnerCombatState();
|
||||
bool needOtherAI = who.GetCombatManager().UpdateOwnerCombatState();
|
||||
|
||||
// then, we finally notify the AI (if necessary) and let it safely do whatever it feels like
|
||||
if (needSelfAI)
|
||||
NotifyAICombat(_owner, who);
|
||||
if (needOtherAI)
|
||||
NotifyAICombat(who, _owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsInCombatWith(ObjectGuid guid)
|
||||
{
|
||||
return _pveRefs.ContainsKey(guid) || _pvpRefs.ContainsKey(guid);
|
||||
}
|
||||
|
||||
public bool IsInCombatWith(Unit who)
|
||||
{
|
||||
return IsInCombatWith(who.GetGUID());
|
||||
}
|
||||
|
||||
public void InheritCombatStatesFrom(Unit who)
|
||||
{
|
||||
CombatManager mgr = who.GetCombatManager();
|
||||
foreach (var refe in mgr._pveRefs)
|
||||
{
|
||||
if (!IsInCombatWith(refe.Key))
|
||||
{
|
||||
Unit target = refe.Value.GetOther(who);
|
||||
if ((_owner.HasUnitFlag(UnitFlags.ImmuneToPc) && target.HasUnitFlag(UnitFlags.PvpAttackable)) ||
|
||||
(_owner.HasUnitFlag(UnitFlags.ImmuneToNpc) && !target.HasUnitFlag(UnitFlags.PvpAttackable)))
|
||||
continue;
|
||||
SetInCombatWith(target);
|
||||
}
|
||||
}
|
||||
foreach (var refe in mgr._pvpRefs)
|
||||
{
|
||||
if (!IsInCombatWith(refe.Key))
|
||||
{
|
||||
Unit target = refe.Value.GetOther(who);
|
||||
if ((_owner.HasUnitFlag(UnitFlags.ImmuneToPc) && target.HasUnitFlag(UnitFlags.PvpAttackable)) ||
|
||||
(_owner.HasUnitFlag(UnitFlags.ImmuneToNpc) && !target.HasUnitFlag(UnitFlags.PvpAttackable)))
|
||||
continue;
|
||||
SetInCombatWith(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EndCombatBeyondRange(float range, bool includingPvP)
|
||||
{
|
||||
foreach (var pair in _pveRefs.ToList())
|
||||
{
|
||||
CombatReference refe = pair.Value;
|
||||
if (!refe.first.IsWithinDistInMap(refe.second, range))
|
||||
{
|
||||
_pveRefs.Remove(pair.Key);
|
||||
refe.EndCombat();
|
||||
}
|
||||
}
|
||||
|
||||
if (!includingPvP)
|
||||
return;
|
||||
|
||||
foreach (var pair in _pvpRefs.ToList())
|
||||
{
|
||||
CombatReference refe = pair.Value;
|
||||
if (!refe.first.IsWithinDistInMap(refe.second, range))
|
||||
{
|
||||
_pvpRefs.Remove(pair.Key);
|
||||
refe.EndCombat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SuppressPvPCombat()
|
||||
{
|
||||
foreach (var pair in _pvpRefs)
|
||||
pair.Value.Suppress(_owner);
|
||||
|
||||
if (UpdateOwnerCombatState())
|
||||
if (_owner.IsAIEnabled)
|
||||
_owner.GetAI().JustExitedCombat();
|
||||
}
|
||||
|
||||
public void EndAllPvECombat()
|
||||
{
|
||||
// cannot have threat without combat
|
||||
_owner.GetThreatManager().RemoveMeFromThreatLists();
|
||||
_owner.GetThreatManager().ClearAllThreat();
|
||||
while (!_pveRefs.Empty())
|
||||
_pveRefs.First().Value.EndCombat();
|
||||
}
|
||||
|
||||
void EndAllPvPCombat()
|
||||
{
|
||||
while (!_pvpRefs.Empty())
|
||||
_pvpRefs.First().Value.EndCombat();
|
||||
}
|
||||
|
||||
public static void NotifyAICombat(Unit me, Unit other)
|
||||
{
|
||||
if (!me.IsAIEnabled)
|
||||
return;
|
||||
me.GetAI().JustEnteredCombat(other);
|
||||
|
||||
Creature cMe = me.ToCreature();
|
||||
if (cMe != null)
|
||||
if (!cMe.CanHaveThreatList())
|
||||
cMe.GetAI().JustEngagedWith(other);
|
||||
}
|
||||
|
||||
void PutReference(ObjectGuid guid, CombatReference refe)
|
||||
{
|
||||
if (refe._isPvP)
|
||||
{
|
||||
Cypher.Assert(!_pvpRefs.ContainsKey(guid), "Duplicate combat state detected!");
|
||||
_pvpRefs[guid] = (PvPCombatReference)refe;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cypher.Assert(!_pveRefs.ContainsKey(guid), "Duplicate combat state detected!");
|
||||
_pveRefs[guid] = refe;
|
||||
}
|
||||
}
|
||||
|
||||
public void PurgeReference(ObjectGuid guid, bool pvp)
|
||||
{
|
||||
if (pvp)
|
||||
_pvpRefs.Remove(guid);
|
||||
else
|
||||
_pveRefs.Remove(guid);
|
||||
}
|
||||
|
||||
public bool UpdateOwnerCombatState()
|
||||
{
|
||||
bool combatState = HasCombat();
|
||||
if (combatState == _owner.IsInCombat())
|
||||
return false;
|
||||
|
||||
if (combatState)
|
||||
{
|
||||
_owner.AddUnitFlag(UnitFlags.InCombat);
|
||||
_owner.AtEnterCombat();
|
||||
}
|
||||
else
|
||||
{
|
||||
_owner.RemoveUnitFlag(UnitFlags.InCombat);
|
||||
_owner.AtExitCombat();
|
||||
}
|
||||
|
||||
Unit master = _owner.GetCharmerOrOwner();
|
||||
if (master != null)
|
||||
master.UpdatePetCombatState();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Unit GetOwner() { return _owner; }
|
||||
|
||||
public bool HasCombat() { return HasPvECombat() || HasPvPCombat(); }
|
||||
|
||||
public bool HasPvECombat() { return !_pveRefs.Empty(); }
|
||||
|
||||
public Dictionary<ObjectGuid, CombatReference> GetPvECombatRefs() { return _pveRefs; }
|
||||
|
||||
public Dictionary<ObjectGuid, PvPCombatReference> GetPvPCombatRefs() { return _pvpRefs; }
|
||||
|
||||
public void EndAllCombat()
|
||||
{
|
||||
EndAllPvECombat();
|
||||
EndAllPvPCombat();
|
||||
}
|
||||
}
|
||||
|
||||
public class CombatReference
|
||||
{
|
||||
public Unit first;
|
||||
public Unit second;
|
||||
public bool _isPvP;
|
||||
|
||||
public CombatReference(Unit a, Unit b, bool pvp = false)
|
||||
{
|
||||
first = a;
|
||||
second = b;
|
||||
_isPvP = pvp;
|
||||
}
|
||||
|
||||
public void EndCombat()
|
||||
{
|
||||
// sequencing matters here - AI might do nasty stuff, so make sure refs are in a consistent state before you hand off!
|
||||
|
||||
// first, get rid of any threat that still exists...
|
||||
first.GetThreatManager().ClearThreat(second);
|
||||
second.GetThreatManager().ClearThreat(first);
|
||||
|
||||
// ...then, remove the references from both managers...
|
||||
first.GetCombatManager().PurgeReference(second.GetGUID(), _isPvP);
|
||||
second.GetCombatManager().PurgeReference(first.GetGUID(), _isPvP);
|
||||
|
||||
// ...update the combat state, which will potentially remove IN_COMBAT...
|
||||
bool needFirstAI = first.GetCombatManager().UpdateOwnerCombatState();
|
||||
bool needSecondAI = second.GetCombatManager().UpdateOwnerCombatState();
|
||||
|
||||
// ...and if that happened, also notify the AI of it...
|
||||
if (needFirstAI && first.IsAIEnabled)
|
||||
first.GetAI().JustExitedCombat();
|
||||
if (needSecondAI && second.IsAIEnabled)
|
||||
second.GetAI().JustExitedCombat();
|
||||
}
|
||||
|
||||
public Unit GetOther(Unit me) { return (first == me) ? second : first; }
|
||||
}
|
||||
|
||||
public class PvPCombatReference : CombatReference
|
||||
{
|
||||
public static uint PVP_COMBAT_TIMEOUT = 5 * Time.InMilliseconds;
|
||||
|
||||
uint _combatTimer = PVP_COMBAT_TIMEOUT;
|
||||
bool _suppressFirst = false;
|
||||
bool _suppressSecond = false;
|
||||
|
||||
public PvPCombatReference(Unit first, Unit second) : base(first, second, true) { }
|
||||
|
||||
public bool Update(uint tdiff)
|
||||
{
|
||||
if (_combatTimer <= tdiff)
|
||||
return false;
|
||||
_combatTimer -= tdiff;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
_combatTimer = PVP_COMBAT_TIMEOUT;
|
||||
|
||||
bool needFirstAI = false, needSecondAI = false;
|
||||
if (_suppressFirst)
|
||||
{
|
||||
_suppressFirst = false;
|
||||
needFirstAI = first.GetCombatManager().UpdateOwnerCombatState();
|
||||
}
|
||||
if (_suppressSecond)
|
||||
{
|
||||
_suppressSecond = false;
|
||||
needSecondAI = second.GetCombatManager().UpdateOwnerCombatState();
|
||||
}
|
||||
|
||||
if (needFirstAI)
|
||||
CombatManager.NotifyAICombat(first, second);
|
||||
if (needSecondAI)
|
||||
CombatManager.NotifyAICombat(second, first);
|
||||
}
|
||||
|
||||
void SuppressFor(Unit who)
|
||||
{
|
||||
Suppress(who);
|
||||
if (who.GetCombatManager().UpdateOwnerCombatState())
|
||||
if (who.IsAIEnabled)
|
||||
who.GetAI().JustExitedCombat();
|
||||
}
|
||||
|
||||
// suppressed combat refs do not generate a combat state for one side of the relation
|
||||
// (used by: vanish, feign death)
|
||||
public bool IsSuppressedFor(Unit who) { return (who == first) ? _suppressFirst : _suppressSecond; }
|
||||
|
||||
public void Suppress(Unit who)
|
||||
{
|
||||
if (who == first)
|
||||
_suppressFirst = true;
|
||||
else
|
||||
_suppressSecond = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Game.Combat
|
||||
{
|
||||
public class UnitBaseEvent
|
||||
{
|
||||
public UnitBaseEvent(UnitEventTypes pType)
|
||||
{
|
||||
iType = pType;
|
||||
}
|
||||
public UnitEventTypes GetEventType()
|
||||
{
|
||||
return iType;
|
||||
}
|
||||
bool MatchesTypeMask(uint pMask)
|
||||
{
|
||||
return Convert.ToBoolean((uint)iType & pMask);
|
||||
}
|
||||
|
||||
void SetEventType(UnitEventTypes pType)
|
||||
{
|
||||
iType = pType;
|
||||
}
|
||||
|
||||
UnitEventTypes iType;
|
||||
}
|
||||
|
||||
public class ThreatRefStatusChangeEvent : UnitBaseEvent
|
||||
{
|
||||
public ThreatRefStatusChangeEvent(UnitEventTypes pType)
|
||||
: base(pType)
|
||||
{
|
||||
iHostileReference = null;
|
||||
}
|
||||
public ThreatRefStatusChangeEvent(UnitEventTypes pType, HostileReference pHostileReference)
|
||||
: base(pType)
|
||||
{
|
||||
iHostileReference = pHostileReference;
|
||||
}
|
||||
public ThreatRefStatusChangeEvent(UnitEventTypes pType, HostileReference pHostileReference, float pValue)
|
||||
: base(pType)
|
||||
{
|
||||
iHostileReference = pHostileReference;
|
||||
iFValue = pValue;
|
||||
}
|
||||
public ThreatRefStatusChangeEvent(UnitEventTypes pType, HostileReference pHostileReference, bool pValue)
|
||||
: base(pType)
|
||||
{
|
||||
iHostileReference = pHostileReference;
|
||||
iBValue = pValue;
|
||||
}
|
||||
|
||||
public float GetFValue()
|
||||
{
|
||||
return iFValue;
|
||||
}
|
||||
|
||||
bool GetBValue()
|
||||
{
|
||||
return iBValue;
|
||||
}
|
||||
|
||||
void SetBValue(bool pValue)
|
||||
{
|
||||
iBValue = pValue;
|
||||
}
|
||||
|
||||
public HostileReference GetReference()
|
||||
{
|
||||
return iHostileReference;
|
||||
}
|
||||
|
||||
public void SetThreatManager(ThreatManager pThreatManager)
|
||||
{
|
||||
iThreatManager = pThreatManager;
|
||||
}
|
||||
|
||||
ThreatManager GetThreatManager()
|
||||
{
|
||||
return iThreatManager;
|
||||
}
|
||||
|
||||
float iFValue;
|
||||
bool iBValue;
|
||||
HostileReference iHostileReference;
|
||||
ThreatManager iThreatManager;
|
||||
}
|
||||
|
||||
public enum UnitEventTypes
|
||||
{
|
||||
// Player/Pet Changed On/Offline Status
|
||||
ThreatRefOnlineStatus = 1 << 0,
|
||||
|
||||
// Threat For Player/Pet Changed
|
||||
ThreatRefThreatChange = 1 << 1,
|
||||
|
||||
// Player/Pet Will Be Removed From List (Dead) [For Internal Use]
|
||||
ThreatRefRemoveFromList = 1 << 2,
|
||||
|
||||
// Player/Pet Entered/Left Water Or Some Other Place Where It Is/Was Not Accessible For The Creature
|
||||
ThreatRefAccessibleStatus = 1 << 3,
|
||||
|
||||
// Threat List Is Going To Be Sorted (If Dirty Flag Is Set)
|
||||
ThreatSortList = 1 << 4,
|
||||
|
||||
// New Target Should Be Fetched, Could Tbe The Current Target As Well
|
||||
ThreatSetNextTarget = 1 << 5,
|
||||
|
||||
// A New Victim (Target) Was Set. Could Be Null
|
||||
ThreatVictimChanged = 1 << 6
|
||||
|
||||
// Future Use
|
||||
//Unit_Killed = 1<<7,
|
||||
|
||||
//Future Use
|
||||
//Unit_Health_Change = 1<<8,
|
||||
}
|
||||
}
|
||||
@@ -1,398 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Framework.Dynamic;
|
||||
using Game.Entities;
|
||||
using Game.Spells;
|
||||
|
||||
namespace Game.Combat
|
||||
{
|
||||
public class HostileRefManager : RefManager<Unit, ThreatManager>
|
||||
{
|
||||
Unit Owner;
|
||||
|
||||
public HostileRefManager(Unit owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
Unit GetOwner() { return Owner; }
|
||||
|
||||
// send threat to all my haters for the victim
|
||||
// The victim is then hated by them as well
|
||||
// use for buffs and healing threat functionality
|
||||
public void ThreatAssist(Unit victim, float baseThreat, SpellInfo threatSpell = null)
|
||||
{
|
||||
float threat = ThreatManager.CalcThreat(victim, Owner, baseThreat, (threatSpell != null ? threatSpell.GetSchoolMask() : SpellSchoolMask.Normal), threatSpell);
|
||||
threat /= GetSize();
|
||||
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
if (ThreatManager.IsValidProcess(victim, refe.GetSource().GetOwner(), threatSpell))
|
||||
refe.GetSource().DoAddThreat(victim, threat);
|
||||
|
||||
refe = refe.Next();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTempThreat(float threat, bool apply)
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
if (refe.GetTempThreatModifier() == 0.0f)
|
||||
refe.AddTempThreat(threat);
|
||||
}
|
||||
else
|
||||
refe.ResetTempThreat();
|
||||
|
||||
refe = refe.Next();
|
||||
}
|
||||
}
|
||||
|
||||
void AddThreatPercent(int percent)
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
refe.AddThreatPercent(percent);
|
||||
refe = refe.Next();
|
||||
}
|
||||
}
|
||||
|
||||
// The references are not needed anymore
|
||||
// tell the source to remove them from the list and free the mem
|
||||
public void DeleteReferences()
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
HostileReference nextRef = refe.Next();
|
||||
refe.RemoveReference();
|
||||
refe = nextRef;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove specific faction references
|
||||
public void DeleteReferencesForFaction(uint faction)
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
HostileReference nextRef = refe.Next();
|
||||
if (refe.GetSource().GetOwner().GetFactionTemplateEntry().Faction == faction)
|
||||
{
|
||||
refe.RemoveReference();
|
||||
}
|
||||
refe = nextRef;
|
||||
}
|
||||
}
|
||||
|
||||
// delete all references out of specified range
|
||||
public void DeleteReferencesOutOfRange(float range)
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
range = range * range;
|
||||
while (refe != null)
|
||||
{
|
||||
HostileReference nextRef = refe.Next();
|
||||
Unit owner = refe.GetSource().GetOwner();
|
||||
if (!owner.IsActiveObject() && owner.GetExactDist2dSq(GetOwner()) > range)
|
||||
{
|
||||
refe.RemoveReference();
|
||||
}
|
||||
refe = nextRef;
|
||||
}
|
||||
}
|
||||
|
||||
public new HostileReference GetFirst() { return (HostileReference)base.GetFirst(); }
|
||||
|
||||
public void UpdateThreatTables()
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
refe.UpdateOnlineStatus();
|
||||
refe = refe.Next();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOnlineOfflineState(bool isOnline)
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
refe.SetOnlineOfflineState(isOnline);
|
||||
refe = refe.Next();
|
||||
}
|
||||
}
|
||||
|
||||
// set state for one reference, defined by Unit
|
||||
public void SetOnlineOfflineState(Unit creature, bool isOnline)
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
HostileReference nextRef = refe.Next();
|
||||
if (refe.GetSource().GetOwner() == creature)
|
||||
{
|
||||
refe.SetOnlineOfflineState(isOnline);
|
||||
break;
|
||||
}
|
||||
refe = nextRef;
|
||||
}
|
||||
}
|
||||
|
||||
// delete one reference, defined by Unit
|
||||
public void DeleteReference(Unit creature)
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
HostileReference nextRef = refe.Next();
|
||||
if (refe.GetSource().GetOwner() == creature)
|
||||
{
|
||||
refe.RemoveReference();
|
||||
break;
|
||||
}
|
||||
refe = nextRef;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateVisibility()
|
||||
{
|
||||
HostileReference refe = GetFirst();
|
||||
while (refe != null)
|
||||
{
|
||||
HostileReference nextRef = refe.Next();
|
||||
if (!refe.GetSource().GetOwner().CanSeeOrDetect(GetOwner()))
|
||||
{
|
||||
nextRef = refe.Next();
|
||||
refe.RemoveReference();
|
||||
}
|
||||
refe = nextRef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HostileReference : Reference<Unit, ThreatManager>
|
||||
{
|
||||
public HostileReference(Unit refUnit, ThreatManager threatManager, float threat)
|
||||
{
|
||||
iThreat = threat;
|
||||
iTempThreatModifier = 0.0f;
|
||||
Link(refUnit, threatManager);
|
||||
iUnitGuid = refUnit.GetGUID();
|
||||
iOnline = true;
|
||||
iAccessible = true;
|
||||
}
|
||||
|
||||
public override void TargetObjectBuildLink()
|
||||
{
|
||||
GetTarget().AddHatedBy(this);
|
||||
}
|
||||
|
||||
public override void TargetObjectDestroyLink()
|
||||
{
|
||||
GetTarget().RemoveHatedBy(this);
|
||||
}
|
||||
|
||||
public override void SourceObjectDestroyLink()
|
||||
{
|
||||
SetOnlineOfflineState(false);
|
||||
}
|
||||
|
||||
void FireStatusChanged(ThreatRefStatusChangeEvent threatRefStatusChangeEvent)
|
||||
{
|
||||
if (GetSource() != null)
|
||||
GetSource().ProcessThreatEvent(threatRefStatusChangeEvent);
|
||||
}
|
||||
|
||||
public void AddThreat(float modThreat)
|
||||
{
|
||||
if (modThreat == 0.0f)
|
||||
return;
|
||||
|
||||
iThreat += modThreat;
|
||||
|
||||
// the threat is changed. Source and target unit have to be available
|
||||
// if the link was cut before relink it again
|
||||
if (!IsOnline())
|
||||
UpdateOnlineStatus();
|
||||
|
||||
ThreatRefStatusChangeEvent Event = new(UnitEventTypes.ThreatRefThreatChange, this, modThreat);
|
||||
FireStatusChanged(Event);
|
||||
|
||||
if (IsValid() && modThreat > 0.0f)
|
||||
{
|
||||
Unit victimOwner = GetTarget().GetCharmerOrOwner();
|
||||
if (victimOwner != null && victimOwner.IsAlive())
|
||||
GetSource().AddThreat(victimOwner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
|
||||
}
|
||||
}
|
||||
|
||||
public void AddThreatPercent(int percent)
|
||||
{
|
||||
AddThreat(MathFunctions.CalculatePct(iThreat, percent));
|
||||
}
|
||||
|
||||
// check, if source can reach target and set the status
|
||||
public void UpdateOnlineStatus()
|
||||
{
|
||||
bool online = false;
|
||||
bool accessible = false;
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
Unit target = Global.ObjAccessor.GetUnit(GetSourceUnit(), GetUnitGuid());
|
||||
if (target != null)
|
||||
Link(target, GetSource());
|
||||
}
|
||||
|
||||
// only check for online status if
|
||||
// ref is valid
|
||||
// target is no player or not gamemaster
|
||||
// target is not in flight
|
||||
if (IsValid()
|
||||
&& (GetTarget().IsTypeId(TypeId.Player) || !GetTarget().ToPlayer().IsGameMaster())
|
||||
&& !GetTarget().HasUnitState(UnitState.InFlight)
|
||||
&& GetTarget().IsInMap(GetSourceUnit())
|
||||
&& GetTarget().IsInPhase(GetSourceUnit())
|
||||
)
|
||||
{
|
||||
Creature creature = GetSourceUnit().ToCreature();
|
||||
online = GetTarget().IsInAccessiblePlaceFor(creature);
|
||||
if (!online)
|
||||
{
|
||||
if (creature.IsWithinCombatRange(GetTarget(), creature.m_CombatDistance))
|
||||
online = true; // not accessible but stays online
|
||||
}
|
||||
else
|
||||
accessible = true;
|
||||
}
|
||||
SetAccessibleState(accessible);
|
||||
SetOnlineOfflineState(online);
|
||||
}
|
||||
|
||||
public void SetOnlineOfflineState(bool isOnline)
|
||||
{
|
||||
if (iOnline != isOnline)
|
||||
{
|
||||
iOnline = isOnline;
|
||||
if (!iOnline)
|
||||
SetAccessibleState(false); // if not online that not accessable as well
|
||||
|
||||
ThreatRefStatusChangeEvent Event = new(UnitEventTypes.ThreatRefOnlineStatus, this);
|
||||
FireStatusChanged(Event);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAccessibleState(bool isAccessible)
|
||||
{
|
||||
if (iAccessible != isAccessible)
|
||||
{
|
||||
iAccessible = isAccessible;
|
||||
|
||||
ThreatRefStatusChangeEvent Event = new(UnitEventTypes.ThreatRefAccessibleStatus, this);
|
||||
FireStatusChanged(Event);
|
||||
}
|
||||
}
|
||||
|
||||
// reference is not needed anymore. realy delete it !
|
||||
public void RemoveReference()
|
||||
{
|
||||
Invalidate();
|
||||
|
||||
ThreatRefStatusChangeEvent Event = new(UnitEventTypes.ThreatRefRemoveFromList, this);
|
||||
FireStatusChanged(Event);
|
||||
}
|
||||
|
||||
Unit GetSourceUnit()
|
||||
{
|
||||
return GetSource().GetOwner();
|
||||
}
|
||||
|
||||
public void SetThreat(float threat)
|
||||
{
|
||||
AddThreat(threat - iThreat);
|
||||
}
|
||||
|
||||
public float GetThreat()
|
||||
{
|
||||
return iThreat + iTempThreatModifier;
|
||||
}
|
||||
|
||||
public bool IsOnline()
|
||||
{
|
||||
return iOnline;
|
||||
}
|
||||
|
||||
// The Unit might be in water and the creature can not enter the water, but has range attack
|
||||
// in this case online = true, but accessible = false
|
||||
bool IsAccessible()
|
||||
{
|
||||
return iAccessible;
|
||||
}
|
||||
|
||||
// used for temporary setting a threat and reducing it later again.
|
||||
// the threat modification is stored
|
||||
public void SetTempThreat(float threat)
|
||||
{
|
||||
AddTempThreat(threat - iTempThreatModifier);
|
||||
}
|
||||
|
||||
public void AddTempThreat(float threat)
|
||||
{
|
||||
if (threat == 0.0f)
|
||||
return;
|
||||
|
||||
iTempThreatModifier += threat;
|
||||
|
||||
ThreatRefStatusChangeEvent Event = new(UnitEventTypes.ThreatRefThreatChange, this, threat);
|
||||
FireStatusChanged(Event);
|
||||
}
|
||||
|
||||
public void ResetTempThreat()
|
||||
{
|
||||
AddTempThreat(-iTempThreatModifier);
|
||||
}
|
||||
|
||||
public float GetTempThreatModifier()
|
||||
{
|
||||
return iTempThreatModifier;
|
||||
}
|
||||
|
||||
public ObjectGuid GetUnitGuid()
|
||||
{
|
||||
return iUnitGuid;
|
||||
}
|
||||
|
||||
public Unit GetVictim() { return GetTarget(); }
|
||||
|
||||
public new HostileReference Next() { return (HostileReference)base.Next(); }
|
||||
|
||||
float iThreat;
|
||||
float iTempThreatModifier; // used for SPELL_AURA_MOD_TOTAL_THREAT
|
||||
ObjectGuid iUnitGuid;
|
||||
bool iOnline;
|
||||
bool iAccessible;
|
||||
}
|
||||
}
|
||||
+798
-461
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user