Core: Combat/threat system rewrite
Port From (https://github.com/TrinityCore/TrinityCore/commit/34c7810fe507eca1b8b9389630db5d5d26d92e77)
This commit is contained in:
@@ -62,10 +62,9 @@ namespace Game.AI
|
|||||||
if (!creature)
|
if (!creature)
|
||||||
creature = me;
|
creature = me;
|
||||||
|
|
||||||
if (!creature.CanHaveThreatList())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Map map = creature.GetMap();
|
Map map = creature.GetMap();
|
||||||
|
if (creature.CanHaveThreatList())
|
||||||
|
{
|
||||||
if (!map.IsDungeon()) //use IsDungeon instead of Instanceable, in case Battlegrounds will be instantiated
|
if (!map.IsDungeon()) //use IsDungeon instead of Instanceable, in case Battlegrounds will be instantiated
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Server, "DoZoneInCombat call for map that isn't an instance (creature entry = {0})", creature.IsTypeId(TypeId.Unit) ? creature.ToCreature().GetEntry() : 0);
|
Log.outError(LogFilter.Server, "DoZoneInCombat call for map that isn't an instance (creature entry = {0})", creature.IsTypeId(TypeId.Unit) ? creature.ToCreature().GetEntry() : 0);
|
||||||
@@ -81,15 +80,16 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
Unit summoner = creature.ToTempSummon().GetSummoner();
|
Unit summoner = creature.ToTempSummon().GetSummoner();
|
||||||
if (summoner != null)
|
if (summoner != null)
|
||||||
|
{
|
||||||
|
if (creature.IsFriendlyTo(summoner))
|
||||||
{
|
{
|
||||||
Unit target = summoner.GetAttackerForHelper();
|
Unit target = summoner.GetAttackerForHelper();
|
||||||
if (target == null && summoner.CanHaveThreatList() && !summoner.GetThreatManager().IsThreatListEmpty())
|
if (target != null && creature.IsHostileTo(target))
|
||||||
target = summoner.GetThreatManager().GetHostilTarget();
|
|
||||||
if (target != null && (creature.IsFriendlyTo(summoner) || creature.IsHostileTo(target)))
|
|
||||||
creature.GetAI().AttackStart(target);
|
creature.GetAI().AttackStart(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Intended duplicated check, the code above this should select a victim
|
// Intended duplicated check, the code above this should select a victim
|
||||||
// If it can't find a suitable attack target then we should error out.
|
// If it can't find a suitable attack target then we should error out.
|
||||||
@@ -98,23 +98,15 @@ namespace Game.AI
|
|||||||
Log.outError(LogFilter.Server, "DoZoneInCombat called for creature that has empty threat list (creature entry = {0})", creature.GetEntry());
|
Log.outError(LogFilter.Server, "DoZoneInCombat called for creature that has empty threat list (creature entry = {0})", creature.GetEntry());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var playerList = map.GetPlayers();
|
var playerList = map.GetPlayers();
|
||||||
if (playerList.Empty())
|
if (playerList.Empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var player in playerList)
|
foreach (var player in playerList)
|
||||||
{
|
if (player != null && player.IsAlive())
|
||||||
if (player.IsGameMaster())
|
creature.EngageWithTarget(player);
|
||||||
continue;
|
|
||||||
|
|
||||||
if (player.IsAlive())
|
|
||||||
{
|
|
||||||
creature.SetInCombatWith(player);
|
|
||||||
player.SetInCombatWith(creature);
|
|
||||||
creature.GetThreatManager().AddThreat(player, 0.0f, null, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void MoveInLineOfSight_Safe(Unit who)
|
public virtual void MoveInLineOfSight_Safe(Unit who)
|
||||||
@@ -247,11 +239,13 @@ namespace Game.AI
|
|||||||
|
|
||||||
return me.GetVictim() != null;
|
return me.GetVictim() != null;
|
||||||
}
|
}
|
||||||
else if (me.GetThreatManager().IsThreatListEmpty())
|
else if (me.GetThreatManager().IsThreatListEmpty(true))
|
||||||
{
|
{
|
||||||
EnterEvadeMode(EvadeReason.NoHostiles);
|
EnterEvadeMode(EvadeReason.NoHostiles);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
me.AttackStop();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ namespace Game.AI
|
|||||||
me.GetMotionMaster().Clear();
|
me.GetMotionMaster().Clear();
|
||||||
me.GetMotionMaster().MoveIdle();
|
me.GetMotionMaster().MoveIdle();
|
||||||
me.CombatStop();
|
me.CombatStop();
|
||||||
me.GetHostileRefManager().DeleteReferences();
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +452,7 @@ namespace Game.AI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
me.ClearInPetCombat();
|
me.RemoveUnitFlag(UnitFlags.PetInCombat); // on player pets, this flag indicates that we're actively going after a target - we're returning, so remove it
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoAttack(Unit target, bool chase)
|
void DoAttack(Unit target, bool chase)
|
||||||
@@ -464,12 +462,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (me.Attack(target, true))
|
if (me.Attack(target, true))
|
||||||
{
|
{
|
||||||
// properly fix fake combat after pet is sent to attack
|
me.AddUnitFlag(UnitFlags.PetInCombat); // on player pets, this flag indicates we're actively going after a target - that's what we're doing, so set it
|
||||||
Unit owner = me.GetOwner();
|
|
||||||
if (owner != null)
|
|
||||||
owner.AddUnitFlag(UnitFlags.PetInCombat);
|
|
||||||
|
|
||||||
me.AddUnitFlag(UnitFlags.PetInCombat);
|
|
||||||
|
|
||||||
// Play sound to let the player know the pet is attacking something it picked on its own
|
// Play sound to let the player know the pet is attacking something it picked on its own
|
||||||
if (me.HasReactState(ReactStates.Aggressive) && !me.GetCharmInfo().IsCommandAttack())
|
if (me.HasReactState(ReactStates.Aggressive) && !me.GetCharmInfo().IsCommandAttack())
|
||||||
|
|||||||
@@ -188,12 +188,12 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (targetType == SelectAggroTarget.MaxDistance || targetType == SelectAggroTarget.MinDistance)
|
if (targetType == SelectAggroTarget.MaxDistance || targetType == SelectAggroTarget.MinDistance)
|
||||||
{
|
{
|
||||||
foreach (HostileReference refe in mgr.GetThreatList())
|
foreach (ThreatReference refe in mgr.GetSortedThreatList())
|
||||||
{
|
{
|
||||||
if (!refe.IsOnline())
|
if (!refe.IsOnline())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
targetList.Add(refe.GetTarget());
|
targetList.Add(refe.GetVictim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -202,12 +202,12 @@ namespace Game.AI
|
|||||||
if (currentVictim != null)
|
if (currentVictim != null)
|
||||||
targetList.Add(currentVictim);
|
targetList.Add(currentVictim);
|
||||||
|
|
||||||
foreach (HostileReference refe in mgr.GetThreatList())
|
foreach (ThreatReference refe in mgr.GetSortedThreatList())
|
||||||
{
|
{
|
||||||
if (!refe.IsOnline())
|
if (!refe.IsOnline())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Unit thisTarget = refe.GetTarget();
|
Unit thisTarget = refe.GetVictim();
|
||||||
if (thisTarget != currentVictim)
|
if (thisTarget != currentVictim)
|
||||||
targetList.Add(thisTarget);
|
targetList.Add(thisTarget);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -526,12 +526,10 @@ namespace Game.AI
|
|||||||
float x, y, z;
|
float x, y, z;
|
||||||
me.GetPosition(out x, out y, out z);
|
me.GetPosition(out x, out y, out z);
|
||||||
|
|
||||||
var threatList = me.GetThreatManager().GetThreatList();
|
foreach (var pair in me.GetCombatManager().GetPvECombatRefs())
|
||||||
foreach (var refe in threatList)
|
|
||||||
{
|
{
|
||||||
Unit target = refe.GetTarget();
|
Unit target = pair.Value.GetOther(me);
|
||||||
if (target)
|
if (target.IsControlledByPlayer() && !CheckBoundary(target))
|
||||||
if (target.IsTypeId(TypeId.Player) && !CheckBoundary(target))
|
|
||||||
target.NearTeleportTo(x, y, z, 0);
|
target.NearTeleportTo(x, y, z, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -370,10 +370,10 @@ namespace Game.AI
|
|||||||
if (_me == null)
|
if (_me == null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
foreach (var refe in _me.GetThreatManager().GetThreatList())
|
foreach (var refe in _me.GetThreatManager().GetModifiableThreatList())
|
||||||
{
|
{
|
||||||
refe.AddThreatPercent(Math.Max(-100, (int)(e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC)));
|
refe.ModifyThreatByPercent(Math.Max(-100, (int)(e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC)));
|
||||||
Log.outDebug(LogFilter.ScriptsAi, $"SmartScript.ProcessAction: SMART_ACTION_THREAT_ALL_PCT: Creature {_me.GetGUID()} modify threat for {refe.GetTarget().GetGUID()}, value {e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC}");
|
Log.outDebug(LogFilter.ScriptsAi, $"SmartScript.ProcessAction: SMART_ACTION_THREAT_ALL_PCT: Creature {_me.GetGUID()} modify threat for {refe.GetVictim().GetGUID()}, value {e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC}");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2245,6 +2245,9 @@ namespace Game.AI
|
|||||||
}
|
}
|
||||||
case SmartActions.AddThreat:
|
case SmartActions.AddThreat:
|
||||||
{
|
{
|
||||||
|
if (!_me.CanHaveThreatList())
|
||||||
|
break;
|
||||||
|
|
||||||
foreach (var target in targets)
|
foreach (var target in targets)
|
||||||
if (IsUnit(target))
|
if (IsUnit(target))
|
||||||
_me.GetThreatManager().AddThreat(target.ToUnit(), (float)(e.Action.threatPCT.threatINC - (float)e.Action.threatPCT.threatDEC), null, true, true);
|
_me.GetThreatManager().AddThreat(target.ToUnit(), (float)(e.Action.threatPCT.threatINC - (float)e.Action.threatPCT.threatDEC), null, true, true);
|
||||||
@@ -2847,9 +2850,9 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
if (_me != null && _me.CanHaveThreatList())
|
if (_me != null && _me.CanHaveThreatList())
|
||||||
{
|
{
|
||||||
foreach (var refe in _me.GetThreatManager().GetThreatList())
|
foreach (var refe in _me.GetThreatManager().GetSortedThreatList())
|
||||||
if (e.Target.hostilRandom.maxDist == 0 || _me.IsWithinCombatRange(refe.GetTarget(), e.Target.hostilRandom.maxDist))
|
if (e.Target.hostilRandom.maxDist == 0 || _me.IsWithinCombatRange(refe.GetVictim(), e.Target.hostilRandom.maxDist))
|
||||||
targets.Add(refe.GetTarget());
|
targets.Add(refe.GetVictim());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,7 +727,6 @@ namespace Game.BattleGrounds
|
|||||||
{
|
{
|
||||||
//needed cause else in av some creatures will kill the players at the end
|
//needed cause else in av some creatures will kill the players at the end
|
||||||
player.CombatStop();
|
player.CombatStop();
|
||||||
player.GetHostileRefManager().DeleteReferences();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove temporary currency bonus auras before rewarding player
|
// remove temporary currency bonus auras before rewarding player
|
||||||
|
|||||||
@@ -441,20 +441,19 @@ namespace Game.Chat
|
|||||||
Unit target = handler.GetSelectedUnit();
|
Unit target = handler.GetSelectedUnit();
|
||||||
if (!target)
|
if (!target)
|
||||||
target = handler.GetSession().GetPlayer();
|
target = handler.GetSession().GetPlayer();
|
||||||
HostileReference refe = target.GetHostileRefManager().GetFirst();
|
|
||||||
uint count = 0;
|
handler.SendSysMessage($"Combat refs: (Combat state: {target.IsInCombat()} | Manager state: {target.GetCombatManager().HasCombat()})");
|
||||||
handler.SendSysMessage("Hostil reference list of {0} (guid {1})", target.GetName(), target.GetGUID().ToString());
|
foreach (var refe in target.GetCombatManager().GetPvPCombatRefs())
|
||||||
while (refe != null)
|
|
||||||
{
|
{
|
||||||
Unit unit = refe.GetSource().GetOwner();
|
Unit unit = refe.Value.GetOther(target);
|
||||||
if (unit)
|
handler.SendSysMessage($"[PvP] {unit.GetName()} (SpawnID {(unit.IsCreature() ? unit.ToCreature().GetSpawnId() : 0)})");
|
||||||
|
}
|
||||||
|
foreach (var refe in target.GetCombatManager().GetPvECombatRefs())
|
||||||
{
|
{
|
||||||
++count;
|
Unit unit = refe.Value.GetOther(target);
|
||||||
handler.SendSysMessage(" {0}. {1} ({2}, SpawnId: {3}) - threat {4}", count, unit.GetName(), unit.GetGUID().ToString(), unit.IsTypeId(TypeId.Unit) ? unit.ToCreature().GetSpawnId() : 0, refe.GetThreat());
|
handler.SendSysMessage($"[PvE] {unit.GetName()} (SpawnID {(unit.IsCreature() ? unit.ToCreature().GetSpawnId() : 0)})");
|
||||||
}
|
}
|
||||||
refe = refe.Next();
|
|
||||||
}
|
|
||||||
handler.SendSysMessage("End of hostil reference list.");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,22 +787,51 @@ namespace Game.Chat
|
|||||||
[Command("threat", RBACPermissions.CommandDebugThreat)]
|
[Command("threat", RBACPermissions.CommandDebugThreat)]
|
||||||
static bool HandleDebugThreatListCommand(StringArguments args, CommandHandler handler)
|
static bool HandleDebugThreatListCommand(StringArguments args, CommandHandler handler)
|
||||||
{
|
{
|
||||||
Creature target = handler.GetSelectedCreature();
|
Unit target = handler.GetSelectedUnit();
|
||||||
if (!target || target.IsTotem() || target.IsPet())
|
if (target == null)
|
||||||
return false;
|
target = handler.GetSession().GetPlayer();
|
||||||
|
|
||||||
var threatList = target.GetThreatManager().GetThreatList();
|
ThreatManager mgr = target.GetThreatManager();
|
||||||
uint count = 0;
|
if (!target.IsAlive())
|
||||||
handler.SendSysMessage("Threat list of {0} (guid {1})", target.GetName(), target.GetGUID().ToString());
|
|
||||||
foreach (var refe in threatList)
|
|
||||||
{
|
{
|
||||||
Unit unit = refe.GetTarget();
|
handler.SendSysMessage($"{target.GetName()} ({target.GetGUID()}) is not alive.");
|
||||||
if (!unit)
|
return true;
|
||||||
continue;
|
}
|
||||||
++count;
|
if (!target.CanHaveThreatList())
|
||||||
handler.SendSysMessage(" {0}. {1} (guid {2}) - threat {3}", count, unit.GetName(), unit.GetGUID().ToString(), refe.GetThreat());
|
handler.SendSysMessage($"{target.GetName()} ({target.GetGUID()}) cannot have a threat list.");
|
||||||
|
|
||||||
|
uint count = 0;
|
||||||
|
var threatenedByMe = target.GetThreatManager().GetThreatenedByMeList();
|
||||||
|
if (threatenedByMe.Empty())
|
||||||
|
handler.SendSysMessage($"{target.GetName()} ({target.GetGUID()}) does not threaten any units.");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handler.SendSysMessage($"List of units threatened by {target.GetName()} ({target.GetGUID()})");
|
||||||
|
foreach (var pair in threatenedByMe)
|
||||||
|
{
|
||||||
|
Unit unit = pair.Value.GetOwner();
|
||||||
|
handler.SendSysMessage($" {++count}. {unit.GetName()} ({unit.GetGUID()}, SpawnID {(unit.IsCreature() ? unit.ToCreature().GetSpawnId() : 0)}) - threat {pair.Value.GetThreat()}");
|
||||||
|
}
|
||||||
|
handler.SendSysMessage("End of threatened-by-me list.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mgr.CanHaveThreatList())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (mgr.IsEngaged())
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
handler.SendSysMessage($"Threat list of {target.GetName()} ({target.GetGUID()}, SpawnID {(target.IsCreature() ? target.ToCreature().GetSpawnId() : 0)})");
|
||||||
|
foreach (ThreatReference refe in mgr.GetSortedThreatList())
|
||||||
|
{
|
||||||
|
Unit unit = refe.GetVictim();
|
||||||
|
handler.SendSysMessage($" {++count}. {unit.GetName()} ({unit.GetGUID()}) - threat {refe.GetThreat()}[{refe.GetTauntState()}][{refe.GetOnlineState()}]");
|
||||||
}
|
}
|
||||||
handler.SendSysMessage("End of threat list.");
|
handler.SendSysMessage("End of threat list.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler.SendSysMessage($"{target.GetName()} ({target.GetGUID()}, SpawnID {(target.IsCreature() ? target.ToCreature().GetSpawnId() : 0)}) is not currently engaged.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -446,7 +446,6 @@ namespace Game.Chat
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
target.CombatStop();
|
target.CombatStop();
|
||||||
target.GetHostileRefManager().DeleteReferences();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+796
-459
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@ using Framework.Constants;
|
|||||||
using Framework.Database;
|
using Framework.Database;
|
||||||
using Framework.Dynamic;
|
using Framework.Dynamic;
|
||||||
using Game.AI;
|
using Game.AI;
|
||||||
|
using Game.Combat;
|
||||||
using Game.DataStorage;
|
using Game.DataStorage;
|
||||||
using Game.Groups;
|
using Game.Groups;
|
||||||
using Game.Loots;
|
using Game.Loots;
|
||||||
@@ -420,6 +421,8 @@ namespace Game.Entities
|
|||||||
UpdateMovementFlags();
|
UpdateMovementFlags();
|
||||||
LoadCreaturesAddon();
|
LoadCreaturesAddon();
|
||||||
LoadTemplateImmunities();
|
LoadTemplateImmunities();
|
||||||
|
|
||||||
|
GetThreatManager().UpdateOnlineStates(true, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,6 +514,8 @@ namespace Game.Entities
|
|||||||
if (!IsAlive())
|
if (!IsAlive())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
GetThreatManager().Update(diff);
|
||||||
|
|
||||||
if (m_shouldReacquireTarget && !IsFocusing(null, true))
|
if (m_shouldReacquireTarget && !IsFocusing(null, true))
|
||||||
{
|
{
|
||||||
SetTarget(m_suppressedTarget);
|
SetTarget(m_suppressedTarget);
|
||||||
@@ -903,10 +908,110 @@ namespace Game.Entities
|
|||||||
ApplySpellImmune(0, SpellImmunity.Effect, SpellEffectName.KnockBackDest, true);
|
ApplySpellImmune(0, SpellImmunity.Effect, SpellEffectName.KnockBackDest, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetThreatManager().Initialize();
|
||||||
|
|
||||||
return true;
|
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())
|
if (IsTotem() || IsTrigger() || IsCritter() || IsSpiritService())
|
||||||
SetReactState(ReactStates.Passive);
|
SetReactState(ReactStates.Passive);
|
||||||
@@ -994,6 +1099,37 @@ namespace Game.Entities
|
|||||||
&& !GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoXpAtKill);
|
&& !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)
|
public bool IsEscortNPC(bool onlyIfActive = true)
|
||||||
{
|
{
|
||||||
if (!IsAIEnabled)
|
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.
|
// 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; }
|
void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
|
||||||
public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
|
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
|
public class VendorItemCount
|
||||||
|
|||||||
@@ -1605,6 +1605,11 @@ namespace Game.Entities
|
|||||||
return GetPhaseShift().CanSee(obj.GetPhaseShift());
|
return GetPhaseShift().CanSee(obj.GetPhaseShift());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool InSamePhase(WorldObject a, WorldObject b)
|
||||||
|
{
|
||||||
|
return a != null && b != null && a.IsInPhase(b);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual float GetCombatReach() { return 0.0f; } // overridden (only) in Unit
|
public virtual float GetCombatReach() { return 0.0f; } // overridden (only) in Unit
|
||||||
public PhaseShift GetPhaseShift() { return _phaseShift; }
|
public PhaseShift GetPhaseShift() { return _phaseShift; }
|
||||||
public void SetPhaseShift(PhaseShift phaseShift) { _phaseShift = new PhaseShift(phaseShift); }
|
public void SetPhaseShift(PhaseShift phaseShift) { _phaseShift = new PhaseShift(phaseShift); }
|
||||||
|
|||||||
@@ -1343,6 +1343,8 @@ namespace Game.Entities
|
|||||||
AddUnitFlag2(UnitFlags2.RegeneratePower);
|
AddUnitFlag2(UnitFlags2.RegeneratePower);
|
||||||
SetSheath(SheathState.Melee);
|
SetSheath(SheathState.Melee);
|
||||||
|
|
||||||
|
GetThreatManager().Initialize();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -342,6 +342,20 @@ namespace Game.Entities
|
|||||||
UpdateDamagePhysical(attType);
|
UpdateDamagePhysical(attType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void AtEnterCombat()
|
||||||
|
{
|
||||||
|
base.AtEnterCombat();
|
||||||
|
if (GetCombatManager().HasPvPCombat())
|
||||||
|
EnablePvpRules(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AtExitCombat()
|
||||||
|
{
|
||||||
|
base.AtExitCombat();
|
||||||
|
UpdatePotionCooldown();
|
||||||
|
m_combatExitTime = Time.GetMSTime();
|
||||||
|
}
|
||||||
|
|
||||||
public override float GetBlockPercent(uint attackerLevel)
|
public override float GetBlockPercent(uint attackerLevel)
|
||||||
{
|
{
|
||||||
float blockArmor = (float)m_activePlayerData.ShieldBlock;
|
float blockArmor = (float)m_activePlayerData.ShieldBlock;
|
||||||
|
|||||||
@@ -475,7 +475,7 @@ namespace Game.Entities
|
|||||||
if (IsInAreaThatActivatesPvpTalents())
|
if (IsInAreaThatActivatesPvpTalents())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (GetCombatTimer() == 0)
|
if (!GetCombatManager().HasPvPCombat())
|
||||||
{
|
{
|
||||||
RemoveAurasDueToSpell(PlayerConst.SpellPvpRulesEnabled);
|
RemoveAurasDueToSpell(PlayerConst.SpellPvpRulesEnabled);
|
||||||
UpdateItemLevelAreaBasedScaling();
|
UpdateItemLevelAreaBasedScaling();
|
||||||
|
|||||||
@@ -316,6 +316,8 @@ namespace Game.Entities
|
|||||||
SetPrimarySpecialization(defaultSpec.Id);
|
SetPrimarySpecialization(defaultSpec.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetThreatManager().Initialize();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public override void Update(uint diff)
|
public override void Update(uint diff)
|
||||||
@@ -358,7 +360,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
UpdateAfkReport(now);
|
UpdateAfkReport(now);
|
||||||
|
|
||||||
if (GetCombatTimer() != 0) // Only set when in pvp combat
|
if (GetCombatManager().HasPvPCombat()) // Only set when in pvp combat
|
||||||
{
|
{
|
||||||
Aura aura = GetAura(PlayerConst.SpellPvpRulesEnabled);
|
Aura aura = GetAura(PlayerConst.SpellPvpRulesEnabled);
|
||||||
if (aura != null)
|
if (aura != null)
|
||||||
@@ -615,7 +617,7 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
m_hostileReferenceCheckTimer = 15 * Time.InMilliseconds;
|
m_hostileReferenceCheckTimer = 15 * Time.InMilliseconds;
|
||||||
if (!GetMap().IsDungeon())
|
if (!GetMap().IsDungeon())
|
||||||
GetHostileRefManager().DeleteReferencesOutOfRange(GetVisibilityRange());
|
GetCombatManager().EndCombatBeyondRange(GetVisibilityRange(), true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_hostileReferenceCheckTimer -= diff;
|
m_hostileReferenceCheckTimer -= diff;
|
||||||
@@ -1908,9 +1910,6 @@ namespace Game.Entities
|
|||||||
|
|
||||||
// Call base
|
// Call base
|
||||||
base.SetInWater(inWater);
|
base.SetInWater(inWater);
|
||||||
|
|
||||||
// Update threat tables
|
|
||||||
GetHostileRefManager().UpdateThreatTables();
|
|
||||||
}
|
}
|
||||||
public void ValidateMovementInfo(MovementInfo mi)
|
public void ValidateMovementInfo(MovementInfo mi)
|
||||||
{
|
{
|
||||||
@@ -2151,15 +2150,11 @@ namespace Game.Entities
|
|||||||
|
|
||||||
Pet pet = GetPet();
|
Pet pet = GetPet();
|
||||||
if (pet != null)
|
if (pet != null)
|
||||||
{
|
|
||||||
pet.SetFaction(35);
|
pet.SetFaction(35);
|
||||||
pet.GetHostileRefManager().SetOnlineOfflineState(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemovePvpFlag(UnitPVPStateFlags.FFAPvp);
|
RemovePvpFlag(UnitPVPStateFlags.FFAPvp);
|
||||||
ResetContestedPvP();
|
ResetContestedPvP();
|
||||||
|
|
||||||
GetHostileRefManager().SetOnlineOfflineState(false);
|
|
||||||
CombatStopWithPets();
|
CombatStopWithPets();
|
||||||
|
|
||||||
PhasingHandler.SetAlwaysVisible(this, true, false);
|
PhasingHandler.SetAlwaysVisible(this, true, false);
|
||||||
@@ -2176,10 +2171,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
Pet pet = GetPet();
|
Pet pet = GetPet();
|
||||||
if (pet != null)
|
if (pet != null)
|
||||||
{
|
|
||||||
pet.SetFaction(GetFaction());
|
pet.SetFaction(GetFaction());
|
||||||
pet.GetHostileRefManager().SetOnlineOfflineState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore FFA PvP Server state
|
// restore FFA PvP Server state
|
||||||
if (Global.WorldMgr.IsFFAPvPRealm())
|
if (Global.WorldMgr.IsFFAPvPRealm())
|
||||||
@@ -2188,7 +2180,6 @@ namespace Game.Entities
|
|||||||
// restore FFA PvP area state, remove not allowed for GM mounts
|
// restore FFA PvP area state, remove not allowed for GM mounts
|
||||||
UpdateArea(m_areaUpdateId);
|
UpdateArea(m_areaUpdateId);
|
||||||
|
|
||||||
GetHostileRefManager().SetOnlineOfflineState(true);
|
|
||||||
m_serverSideVisibilityDetect.SetValue(ServerSideVisibilityType.GM, AccountTypes.Player);
|
m_serverSideVisibilityDetect.SetValue(ServerSideVisibilityType.GM, AccountTypes.Player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3918,14 +3909,6 @@ namespace Game.Entities
|
|||||||
public static bool IsValidClass(Class _class) { return Convert.ToBoolean((1 << ((int)_class - 1)) & (int)Class.ClassMaskAllPlayable); }
|
public static bool IsValidClass(Class _class) { return Convert.ToBoolean((1 << ((int)_class - 1)) & (int)Class.ClassMaskAllPlayable); }
|
||||||
public static bool IsValidRace(Race _race) { return Convert.ToBoolean((ulong)SharedConst.GetMaskForRace(_race) & SharedConst.RaceMaskAllPlayable); }
|
public static bool IsValidRace(Race _race) { return Convert.ToBoolean((ulong)SharedConst.GetMaskForRace(_race) & SharedConst.RaceMaskAllPlayable); }
|
||||||
|
|
||||||
public override void OnCombatExit()
|
|
||||||
{
|
|
||||||
base.OnCombatExit();
|
|
||||||
|
|
||||||
UpdatePotionCooldown();
|
|
||||||
m_combatExitTime = Time.GetMSTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaveLFGChannel()
|
void LeaveLFGChannel()
|
||||||
{
|
{
|
||||||
foreach (var i in m_channels)
|
foreach (var i in m_channels)
|
||||||
@@ -6887,7 +6870,6 @@ namespace Game.Entities
|
|||||||
m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
|
m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
|
||||||
Dismount();
|
Dismount();
|
||||||
RemoveUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
|
RemoveUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
|
||||||
GetHostileRefManager().SetOnlineOfflineState(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ContinueTaxiFlight()
|
public void ContinueTaxiFlight()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -53,15 +53,14 @@ namespace Game.Entities
|
|||||||
protected List<Unit> attackerList = new();
|
protected List<Unit> attackerList = new();
|
||||||
Dictionary<ReactiveType, uint> m_reactiveTimer = new();
|
Dictionary<ReactiveType, uint> m_reactiveTimer = new();
|
||||||
protected float[][] m_weaponDamage = new float[(int)WeaponAttackType.Max][];
|
protected float[][] m_weaponDamage = new float[(int)WeaponAttackType.Max][];
|
||||||
public float[] m_threatModifier = new float[(int)SpellSchools.Max];
|
|
||||||
|
|
||||||
uint[] m_baseAttackSpeed = new uint[(int)WeaponAttackType.Max];
|
uint[] m_baseAttackSpeed = new uint[(int)WeaponAttackType.Max];
|
||||||
float[] m_modAttackSpeedPct = new float[(int)WeaponAttackType.Max];
|
float[] m_modAttackSpeedPct = new float[(int)WeaponAttackType.Max];
|
||||||
protected uint[] m_attackTimer = new uint[(int)WeaponAttackType.Max];
|
protected uint[] m_attackTimer = new uint[(int)WeaponAttackType.Max];
|
||||||
|
|
||||||
ThreatManager threatManager;
|
CombatManager m_combatManager;
|
||||||
HostileRefManager hostileRefManager;
|
ThreatManager m_threatManager;
|
||||||
RedirectThreatInfo _redirectThreatInfo;
|
|
||||||
protected Unit attacking;
|
protected Unit attacking;
|
||||||
|
|
||||||
public float ModMeleeHitChance { get; set; }
|
public float ModMeleeHitChance { get; set; }
|
||||||
@@ -520,27 +519,6 @@ namespace Game.Entities
|
|||||||
byte _chargesRemoved;
|
byte _chargesRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RedirectThreatInfo
|
|
||||||
{
|
|
||||||
ObjectGuid _targetGUID;
|
|
||||||
uint _threatPct;
|
|
||||||
|
|
||||||
public ObjectGuid GetTargetGUID() { return _targetGUID; }
|
|
||||||
public uint GetThreatPct() { return _threatPct; }
|
|
||||||
|
|
||||||
public void Set(ObjectGuid guid, uint pct)
|
|
||||||
{
|
|
||||||
_targetGUID = guid;
|
|
||||||
_threatPct = pct;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ModifyThreatPct(int amount)
|
|
||||||
{
|
|
||||||
amount += (int)_threatPct;
|
|
||||||
_threatPct = (uint)(Math.Max(0, amount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SpellPeriodicAuraLogInfo
|
public class SpellPeriodicAuraLogInfo
|
||||||
{
|
{
|
||||||
public SpellPeriodicAuraLogInfo(AuraEffect _auraEff, uint _damage, uint _originalDamage, uint _overDamage, uint _absorb, uint _resist, float _multiplier, bool _critical)
|
public SpellPeriodicAuraLogInfo(AuraEffect _auraEff, uint _damage, uint _originalDamage, uint _overDamage, uint _absorb, uint _resist, float _multiplier, bool _critical)
|
||||||
|
|||||||
@@ -58,7 +58,10 @@ namespace Game.Entities
|
|||||||
return true;
|
return true;
|
||||||
if (HasUnitFlag2((UnitFlags2)0x1000000))
|
if (HasUnitFlag2((UnitFlags2)0x1000000))
|
||||||
return false;
|
return false;
|
||||||
return HasUnitFlag(UnitFlags.PetInCombat | UnitFlags.Rename | UnitFlags.Unk15);
|
if (IsPet() && HasUnitFlag(UnitFlags.PetInCombat))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return HasUnitFlag(UnitFlags.Rename | UnitFlags.Unk15);
|
||||||
}
|
}
|
||||||
public virtual bool IsInWater()
|
public virtual bool IsInWater()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -274,6 +274,8 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdatePetCombatState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SetCharmedBy(Unit charmer, CharmType type, AuraApplication aurApp = null)
|
public bool SetCharmedBy(Unit charmer, CharmType type, AuraApplication aurApp = null)
|
||||||
@@ -314,7 +316,6 @@ namespace Game.Entities
|
|||||||
|
|
||||||
CastStop();
|
CastStop();
|
||||||
CombatStop(); // @todo CombatStop(true) may cause crash (interrupt spells)
|
CombatStop(); // @todo CombatStop(true) may cause crash (interrupt spells)
|
||||||
GetThreatManager().ClearAllThreat();
|
|
||||||
|
|
||||||
Player playerCharmer = charmer.ToPlayer();
|
Player playerCharmer = charmer.ToPlayer();
|
||||||
|
|
||||||
@@ -463,8 +464,6 @@ namespace Game.Entities
|
|||||||
|
|
||||||
CastStop();
|
CastStop();
|
||||||
CombatStop(); // @todo CombatStop(true) may cause crash (interrupt spells)
|
CombatStop(); // @todo CombatStop(true) may cause crash (interrupt spells)
|
||||||
GetHostileRefManager().DeleteReferences();
|
|
||||||
GetThreatManager().ClearAllThreat();
|
|
||||||
|
|
||||||
if (_oldFactionId != 0)
|
if (_oldFactionId != 0)
|
||||||
{
|
{
|
||||||
@@ -545,6 +544,8 @@ namespace Game.Entities
|
|||||||
player.SetClientControl(this, true);
|
player.SetClientControl(this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EngageWithTarget(charmer);
|
||||||
|
|
||||||
// a guardian should always have charminfo
|
// a guardian should always have charminfo
|
||||||
if (playerCharmer && this != charmer.GetFirstControlled())
|
if (playerCharmer && this != charmer.GetFirstControlled())
|
||||||
playerCharmer.SendRemoveControlBar();
|
playerCharmer.SendRemoveControlBar();
|
||||||
@@ -651,6 +652,8 @@ namespace Game.Entities
|
|||||||
m_Controlled.Remove(charm);
|
m_Controlled.Remove(charm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdatePetCombatState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Unit GetFirstControlled()
|
public Unit GetFirstControlled()
|
||||||
@@ -698,6 +701,8 @@ namespace Game.Entities
|
|||||||
Log.outFatal(LogFilter.Unit, "Unit {0} is not able to release its minion {1}", GetEntry(), GetMinionGUID());
|
Log.outFatal(LogFilter.Unit, "Unit {0} is not able to release its minion {1}", GetEntry(), GetMinionGUID());
|
||||||
if (!GetCharmGUID().IsEmpty())
|
if (!GetCharmGUID().IsEmpty())
|
||||||
Log.outFatal(LogFilter.Unit, "Unit {0} is not able to release its charm {1}", GetEntry(), GetCharmGUID());
|
Log.outFatal(LogFilter.Unit, "Unit {0} is not able to release its charm {1}", GetEntry(), GetCharmGUID());
|
||||||
|
if (!IsPet()) // pets don't use the flag for this
|
||||||
|
RemoveUnitFlag(UnitFlags.PetInCombat); // m_controlled is now empty, so we know none of our minions are in combat
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendPetActionFeedback(PetActionFeedback msg, uint spellId)
|
public void SendPetActionFeedback(PetActionFeedback msg, uint spellId)
|
||||||
@@ -794,5 +799,25 @@ namespace Game.Entities
|
|||||||
pet.SetFullHealth();
|
pet.SetFullHealth();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdatePetCombatState()
|
||||||
|
{
|
||||||
|
Cypher.Assert(!IsPet()); // player pets do not use UNIT_FLAG_PET_IN_COMBAT for this purpose - but player pets should also never have minions of their own to call this
|
||||||
|
|
||||||
|
bool state = false;
|
||||||
|
foreach (Unit minion in m_Controlled)
|
||||||
|
{
|
||||||
|
if (minion.IsInCombat())
|
||||||
|
{
|
||||||
|
state = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
AddUnitFlag(UnitFlags.PetInCombat);
|
||||||
|
else
|
||||||
|
RemoveUnitFlag(UnitFlags.PetInCombat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1514,14 +1514,7 @@ namespace Game.Entities
|
|||||||
SendCombatLogMessage(data);
|
SendCombatLogMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnergizeBySpell(Unit victim, uint spellId, int damage, PowerType powerType)
|
public void EnergizeBySpell(Unit victim, SpellInfo spellInfo, int damage, PowerType powerType)
|
||||||
{
|
|
||||||
SpellInfo info = Global.SpellMgr.GetSpellInfo(spellId, GetMap().GetDifficultyID());
|
|
||||||
if (info != null)
|
|
||||||
EnergizeBySpell(victim, info, damage, powerType);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnergizeBySpell(Unit victim, SpellInfo spellInfo, int damage, PowerType powerType)
|
|
||||||
{
|
{
|
||||||
int gain = victim.ModifyPower(powerType, damage, false);
|
int gain = victim.ModifyPower(powerType, damage, false);
|
||||||
int overEnergize = damage - gain;
|
int overEnergize = damage - gain;
|
||||||
|
|||||||
+1567
-66
File diff suppressed because it is too large
Load Diff
@@ -122,7 +122,6 @@ namespace Game
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
pet.AttackStop();
|
pet.AttackStop();
|
||||||
pet.ClearInPetCombat();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandlePetActionHelper(Unit pet, ObjectGuid guid1, uint spellid, ActiveStates flag, ObjectGuid guid2, float x, float y, float z)
|
void HandlePetActionHelper(Unit pet, ObjectGuid guid1, uint spellid, ActiveStates flag, ObjectGuid guid2, float x, float y, float z)
|
||||||
@@ -156,7 +155,6 @@ namespace Game
|
|||||||
case CommandStates.Follow: //spellid=1792 //FOLLOW
|
case CommandStates.Follow: //spellid=1792 //FOLLOW
|
||||||
pet.AttackStop();
|
pet.AttackStop();
|
||||||
pet.InterruptNonMeleeSpells(false);
|
pet.InterruptNonMeleeSpells(false);
|
||||||
pet.ClearInPetCombat();
|
|
||||||
pet.GetMotionMaster().MoveFollow(GetPlayer(), SharedConst.PetFollowDist, pet.GetFollowAngle());
|
pet.GetMotionMaster().MoveFollow(GetPlayer(), SharedConst.PetFollowDist, pet.GetFollowAngle());
|
||||||
charmInfo.SetCommandState(CommandStates.Follow);
|
charmInfo.SetCommandState(CommandStates.Follow);
|
||||||
|
|
||||||
@@ -271,7 +269,6 @@ namespace Game
|
|||||||
{
|
{
|
||||||
case ReactStates.Passive: //passive
|
case ReactStates.Passive: //passive
|
||||||
pet.AttackStop();
|
pet.AttackStop();
|
||||||
pet.ClearInPetCombat();
|
|
||||||
goto case ReactStates.Defensive;
|
goto case ReactStates.Defensive;
|
||||||
case ReactStates.Defensive: //recovery
|
case ReactStates.Defensive: //recovery
|
||||||
case ReactStates.Aggressive: //activete
|
case ReactStates.Aggressive: //activete
|
||||||
|
|||||||
+6
-15
@@ -762,22 +762,13 @@ namespace Game.Maps
|
|||||||
// Handle updates for creatures in combat with player and are more than 60 yards away
|
// Handle updates for creatures in combat with player and are more than 60 yards away
|
||||||
if (player.IsInCombat())
|
if (player.IsInCombat())
|
||||||
{
|
{
|
||||||
List<Creature> updateList = new();
|
foreach (var pair in player.GetCombatManager().GetPvECombatRefs())
|
||||||
HostileReference refe = player.GetHostileRefManager().GetFirst();
|
|
||||||
|
|
||||||
while (refe != null)
|
|
||||||
{
|
{
|
||||||
Unit unit = refe.GetSource().GetOwner();
|
Creature unit = pair.Value.GetOther(player).ToCreature();
|
||||||
if (unit)
|
if (unit != null)
|
||||||
if (unit.ToCreature() && unit.GetMapId() == player.GetMapId() && !unit.IsWithinDistInMap(player, GetVisibilityRange(), false))
|
if (unit.GetMapId() == player.GetMapId() && !unit.IsWithinDistInMap(player, GetVisibilityRange(), false))
|
||||||
updateList.Add(unit.ToCreature());
|
VisitNearbyCellsOf(unit, grid_object_update, world_object_update);
|
||||||
|
|
||||||
refe = refe.Next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process deferred update list for player
|
|
||||||
foreach (Creature c in updateList)
|
|
||||||
VisitNearbyCellsOf(c, grid_object_update, world_object_update);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -927,7 +918,7 @@ namespace Game.Maps
|
|||||||
player.UpdateZone(MapConst.InvalidZone, 0);
|
player.UpdateZone(MapConst.InvalidZone, 0);
|
||||||
Global.ScriptMgr.OnPlayerLeaveMap(this, player);
|
Global.ScriptMgr.OnPlayerLeaveMap(this, player);
|
||||||
|
|
||||||
player.GetHostileRefManager().DeleteReferences(); // multithreading crashfix
|
player.CombatStop();
|
||||||
|
|
||||||
bool inWorld = player.IsInWorld;
|
bool inWorld = player.IsInWorld;
|
||||||
player.RemoveFromWorld();
|
player.RemoveFromWorld();
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ namespace Game.Maps
|
|||||||
creature.RemoveAllDynObjects();
|
creature.RemoveAllDynObjects();
|
||||||
creature.RemoveAllAreaTriggers();
|
creature.RemoveAllAreaTriggers();
|
||||||
|
|
||||||
if (creature.IsInCombat() || !creature.GetThreatManager().IsThreatListsEmpty())
|
if (creature.IsInCombat())
|
||||||
{
|
{
|
||||||
creature.CombatStop();
|
creature.CombatStop();
|
||||||
creature.GetThreatManager().ClearAllThreat();
|
creature.GetThreatManager().ClearAllThreat();
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ namespace Game.Movement
|
|||||||
|
|
||||||
if (owner.m_taxi.Empty())
|
if (owner.m_taxi.Empty())
|
||||||
{
|
{
|
||||||
owner.GetHostileRefManager().SetOnlineOfflineState(true);
|
|
||||||
// update z position to ground and orientation for landing point
|
// update z position to ground and orientation for landing point
|
||||||
// this prevent cheating with landing point at lags
|
// this prevent cheating with landing point at lags
|
||||||
// when client side flight end early in comparison server side
|
// when client side flight end early in comparison server side
|
||||||
@@ -122,8 +121,8 @@ namespace Game.Movement
|
|||||||
|
|
||||||
public override void DoReset(Player owner)
|
public override void DoReset(Player owner)
|
||||||
{
|
{
|
||||||
owner.GetHostileRefManager().SetOnlineOfflineState(false);
|
|
||||||
owner.AddUnitState(UnitState.InFlight);
|
owner.AddUnitState(UnitState.InFlight);
|
||||||
|
owner.CombatStopWithPets();
|
||||||
owner.AddUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
|
owner.AddUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
|
||||||
|
|
||||||
MoveSplineInit init = new(owner);
|
MoveSplineInit init = new(owner);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ namespace Game
|
|||||||
//FIXME: logout must be delayed in case lost connection with client in time of combat
|
//FIXME: logout must be delayed in case lost connection with client in time of combat
|
||||||
if (GetPlayer().GetDeathTimer() != 0)
|
if (GetPlayer().GetDeathTimer() != 0)
|
||||||
{
|
{
|
||||||
_player.GetHostileRefManager().DeleteReferences();
|
_player.CombatStop();
|
||||||
_player.BuildPlayerRepop();
|
_player.BuildPlayerRepop();
|
||||||
_player.RepopAtGraveyard();
|
_player.RepopAtGraveyard();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1522,6 +1522,8 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModScale)]
|
[AuraEffectHandler(AuraType.ModScale)]
|
||||||
@@ -1596,13 +1598,21 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target.CombatStop();
|
|
||||||
|
if (target.GetMap().IsDungeon()) // feign death does not remove combat in dungeons
|
||||||
|
{
|
||||||
|
target.AttackStop();
|
||||||
|
Player targetPlayer = target.ToPlayer();
|
||||||
|
if (targetPlayer != null)
|
||||||
|
targetPlayer.SendAttackSwingCancelAttack();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
target.CombatStop(false, false);
|
||||||
|
|
||||||
// prevent interrupt message
|
// prevent interrupt message
|
||||||
if (GetCasterGUID() == target.GetGUID() && target.GetCurrentSpell(CurrentSpellTypes.Generic) != null)
|
if (GetCasterGUID() == target.GetGUID() && target.GetCurrentSpell(CurrentSpellTypes.Generic) != null)
|
||||||
target.FinishSpell(CurrentSpellTypes.Generic, false);
|
target.FinishSpell(CurrentSpellTypes.Generic, false);
|
||||||
target.InterruptNonMeleeSpells(true);
|
target.InterruptNonMeleeSpells(true);
|
||||||
target.GetHostileRefManager().DeleteReferences();
|
|
||||||
|
|
||||||
// stop handling the effect if it was removed by linked event
|
// stop handling the effect if it was removed by linked event
|
||||||
if (aurApp.HasRemoveMode())
|
if (aurApp.HasRemoveMode())
|
||||||
@@ -1612,6 +1622,10 @@ namespace Game.Spells
|
|||||||
target.AddUnitFlag2(UnitFlags2.FeignDeath);
|
target.AddUnitFlag2(UnitFlags2.FeignDeath);
|
||||||
target.AddDynamicFlag(UnitDynFlags.Dead);
|
target.AddDynamicFlag(UnitDynFlags.Dead);
|
||||||
target.AddUnitState(UnitState.Died);
|
target.AddUnitState(UnitState.Died);
|
||||||
|
|
||||||
|
Creature creature = target.ToCreature();
|
||||||
|
if (creature != null)
|
||||||
|
creature.SetReactState(ReactStates.Passive);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1619,7 +1633,13 @@ namespace Game.Spells
|
|||||||
target.RemoveUnitFlag2(UnitFlags2.FeignDeath);
|
target.RemoveUnitFlag2(UnitFlags2.FeignDeath);
|
||||||
target.RemoveDynamicFlag(UnitDynFlags.Dead);
|
target.RemoveDynamicFlag(UnitDynFlags.Dead);
|
||||||
target.ClearUnitState(UnitState.Died);
|
target.ClearUnitState(UnitState.Died);
|
||||||
|
|
||||||
|
Creature creature = target.ToCreature();
|
||||||
|
if (creature != null)
|
||||||
|
creature.InitializeReactState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModUnattackable)]
|
[AuraEffectHandler(AuraType.ModUnattackable)]
|
||||||
@@ -1641,8 +1661,18 @@ namespace Game.Spells
|
|||||||
|
|
||||||
// call functions which may have additional effects after chainging state of unit
|
// call functions which may have additional effects after chainging state of unit
|
||||||
if (apply && mode.HasAnyFlag(AuraEffectHandleModes.Real))
|
if (apply && mode.HasAnyFlag(AuraEffectHandleModes.Real))
|
||||||
|
{
|
||||||
|
if (target.GetMap().IsDungeon())
|
||||||
|
{
|
||||||
|
target.AttackStop();
|
||||||
|
Player targetPlayer = target.ToPlayer();
|
||||||
|
if (targetPlayer != null)
|
||||||
|
targetPlayer.SendAttackSwingCancelAttack();
|
||||||
|
}
|
||||||
|
else
|
||||||
target.CombatStop();
|
target.CombatStop();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModDisarm)]
|
[AuraEffectHandler(AuraType.ModDisarm)]
|
||||||
void HandleAuraModDisarm(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply)
|
void HandleAuraModDisarm(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply)
|
||||||
@@ -2239,18 +2269,7 @@ namespace Game.Spells
|
|||||||
if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask))
|
if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Unit target = aurApp.GetTarget();
|
aurApp.GetTarget().GetThreatManager().UpdateMySpellSchoolModifiers();
|
||||||
for (byte i = 0; i < (int)SpellSchools.Max; ++i)
|
|
||||||
if (Convert.ToBoolean(GetMiscValue() & (1 << i)))
|
|
||||||
{
|
|
||||||
if (apply)
|
|
||||||
MathFunctions.AddPct(ref target.m_threatModifier[i], GetAmount());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float amount = target.GetTotalAuraMultiplierByMiscMask(AuraType.ModThreat, 1u << i);
|
|
||||||
target.m_threatModifier[i] = amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModTotalThreat)]
|
[AuraEffectHandler(AuraType.ModTotalThreat)]
|
||||||
@@ -2266,7 +2285,7 @@ namespace Game.Spells
|
|||||||
|
|
||||||
Unit caster = GetCaster();
|
Unit caster = GetCaster();
|
||||||
if (caster != null && caster.IsAlive())
|
if (caster != null && caster.IsAlive())
|
||||||
target.GetHostileRefManager().AddTempThreat(GetAmount(), apply);
|
caster.GetThreatManager().UpdateMyTempModifiers();
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModTaunt)]
|
[AuraEffectHandler(AuraType.ModTaunt)]
|
||||||
@@ -2280,17 +2299,22 @@ namespace Game.Spells
|
|||||||
if (!target.IsAlive() || !target.CanHaveThreatList())
|
if (!target.IsAlive() || !target.CanHaveThreatList())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Unit caster = GetCaster();
|
target.GetThreatManager().TauntUpdate();
|
||||||
if (caster == null || !caster.IsAlive())
|
}
|
||||||
|
|
||||||
|
[AuraEffectHandler(AuraType.ModDetaunt)]
|
||||||
|
void HandleModDetaunt(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply)
|
||||||
|
{
|
||||||
|
if (!mode.HasAnyFlag(AuraEffectHandleModes.Real))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (apply)
|
Unit caster = GetCaster();
|
||||||
target.TauntApply(caster);
|
Unit target = aurApp.GetTarget();
|
||||||
else
|
|
||||||
{
|
if (!caster || !caster.IsAlive() || !target.IsAlive() || !caster.CanHaveThreatList())
|
||||||
// When taunt aura fades out, mob will switch to previous target if current has less than 1.1 * secondthreat
|
return;
|
||||||
target.TauntFadeOut(caster);
|
|
||||||
}
|
caster.GetThreatManager().TauntUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************/
|
/*****************************/
|
||||||
@@ -2305,6 +2329,7 @@ namespace Game.Spells
|
|||||||
Unit target = aurApp.GetTarget();
|
Unit target = aurApp.GetTarget();
|
||||||
|
|
||||||
target.SetControlled(apply, UnitState.Confused);
|
target.SetControlled(apply, UnitState.Confused);
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModFear)]
|
[AuraEffectHandler(AuraType.ModFear)]
|
||||||
@@ -2316,6 +2341,7 @@ namespace Game.Spells
|
|||||||
Unit target = aurApp.GetTarget();
|
Unit target = aurApp.GetTarget();
|
||||||
|
|
||||||
target.SetControlled(apply, UnitState.Fleeing);
|
target.SetControlled(apply, UnitState.Fleeing);
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModStun)]
|
[AuraEffectHandler(AuraType.ModStun)]
|
||||||
@@ -2327,6 +2353,7 @@ namespace Game.Spells
|
|||||||
Unit target = aurApp.GetTarget();
|
Unit target = aurApp.GetTarget();
|
||||||
|
|
||||||
target.SetControlled(apply, UnitState.Stunned);
|
target.SetControlled(apply, UnitState.Stunned);
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.ModRoot)]
|
[AuraEffectHandler(AuraType.ModRoot)]
|
||||||
@@ -2339,6 +2366,7 @@ namespace Game.Spells
|
|||||||
Unit target = aurApp.GetTarget();
|
Unit target = aurApp.GetTarget();
|
||||||
|
|
||||||
target.SetControlled(apply, UnitState.Root);
|
target.SetControlled(apply, UnitState.Root);
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.PreventsFleeing)]
|
[AuraEffectHandler(AuraType.PreventsFleeing)]
|
||||||
@@ -2743,6 +2771,8 @@ namespace Game.Spells
|
|||||||
&& GetSpellInfo().HasAttribute(SpellAttr2.DamageReducedShield))
|
&& GetSpellInfo().HasAttribute(SpellAttr2.DamageReducedShield))
|
||||||
target.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.StealthOrInvis);
|
target.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.StealthOrInvis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.DamageImmunity)]
|
[AuraEffectHandler(AuraType.DamageImmunity)]
|
||||||
@@ -2753,6 +2783,8 @@ namespace Game.Spells
|
|||||||
|
|
||||||
Unit target = aurApp.GetTarget();
|
Unit target = aurApp.GetTarget();
|
||||||
m_spellInfo.ApplyAllSpellImmunitiesTo(target, GetSpellEffectInfo(), apply);
|
m_spellInfo.ApplyAllSpellImmunitiesTo(target, GetSpellEffectInfo(), apply);
|
||||||
|
|
||||||
|
target.GetThreatManager().UpdateOnlineStates(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AuraEffectHandler(AuraType.DispelImmunity)]
|
[AuraEffectHandler(AuraType.DispelImmunity)]
|
||||||
@@ -4466,7 +4498,7 @@ namespace Game.Spells
|
|||||||
player.GetReputationMgr().ApplyForceReaction(factionId, factionRank, apply);
|
player.GetReputationMgr().ApplyForceReaction(factionId, factionRank, apply);
|
||||||
player.GetReputationMgr().SendForceReactions();
|
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 >= ReputationRank.Friendly) || (!apply && player.GetReputationRank(factionId) >= ReputationRank.Friendly))
|
if ((apply && factionRank >= ReputationRank.Friendly) || (!apply && player.GetReputationRank(factionId) >= ReputationRank.Friendly))
|
||||||
player.StopAttackFaction(factionId);
|
player.StopAttackFaction(factionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2033,7 +2033,7 @@ namespace Game.Spells
|
|||||||
// spellHitTarget can be null if spell is missed in DoSpellHitOnUnit
|
// spellHitTarget can be null if spell is missed in DoSpellHitOnUnit
|
||||||
if (missInfo != SpellMissInfo.Evade && spellHitTarget && !m_caster.IsFriendlyTo(unit) && (!IsPositive() || m_spellInfo.HasEffect(SpellEffectName.Dispel)))
|
if (missInfo != SpellMissInfo.Evade && spellHitTarget && !m_caster.IsFriendlyTo(unit) && (!IsPositive() || m_spellInfo.HasEffect(SpellEffectName.Dispel)))
|
||||||
{
|
{
|
||||||
m_caster.CombatStart(unit, m_spellInfo.HasInitialAggro());
|
m_caster.AttackedTarget(unit, m_spellInfo.HasInitialAggro());
|
||||||
|
|
||||||
if (!unit.IsStandState())
|
if (!unit.IsStandState())
|
||||||
unit.SetStandState(UnitStandStateType.Stand);
|
unit.SetStandState(UnitStandStateType.Stand);
|
||||||
@@ -2133,7 +2133,8 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
if (unit.IsInCombat() && m_spellInfo.HasInitialAggro())
|
if (unit.IsInCombat() && m_spellInfo.HasInitialAggro())
|
||||||
{
|
{
|
||||||
m_caster.SetInCombatState(unit.GetCombatTimer() > 0, unit);
|
if (m_caster.HasUnitFlag(UnitFlags.PvpAttackable)) // 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, null, true);
|
unit.GetThreatManager().ForwardThreatForAssistingMe(m_caster, 0.0f, null, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6849,6 +6850,10 @@ namespace Game.Spells
|
|||||||
if (unit == null)
|
if (unit == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// This will only cause combat - the target will engage once the projectile hits (in DoAllEffectOnTarget)
|
||||||
|
if (targetInfo.missCondition != SpellMissInfo.Evade && !m_caster.IsFriendlyTo(unit) && (!m_spellInfo.IsPositive() || m_spellInfo.HasEffect(SpellEffectName.Dispel)) && (m_spellInfo.HasInitialAggro() || unit.IsEngaged()))
|
||||||
|
m_caster.SetInCombatWith(unit);
|
||||||
|
|
||||||
foreach (SpellEffectInfo effect in m_spellInfo.GetEffects())
|
foreach (SpellEffectInfo effect in m_spellInfo.GetEffects())
|
||||||
{
|
{
|
||||||
if (effect != null && Convert.ToBoolean(targetInfo.effectMask & (1 << (int)effect.EffectIndex)))
|
if (effect != null && Convert.ToBoolean(targetInfo.effectMask & (1 << (int)effect.EffectIndex)))
|
||||||
|
|||||||
@@ -788,7 +788,7 @@ namespace Game.Spells
|
|||||||
|
|
||||||
int gain = (int)(newDamage * gainMultiplier);
|
int gain = (int)(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, (uint)newDamage, gainMultiplier);
|
ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, (uint)newDamage, gainMultiplier);
|
||||||
}
|
}
|
||||||
@@ -1229,7 +1229,7 @@ namespace Game.Spells
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_caster.EnergizeBySpell(unitTarget, m_spellInfo.Id, damage, power);
|
m_caster.EnergizeBySpell(unitTarget, m_spellInfo, damage, power);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SpellEffectHandler(SpellEffectName.EnergizePct)]
|
[SpellEffectHandler(SpellEffectName.EnergizePct)]
|
||||||
@@ -1252,7 +1252,7 @@ namespace Game.Spells
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int gain = (int)MathFunctions.CalculatePct(maxPower, damage);
|
int gain = (int)MathFunctions.CalculatePct(maxPower, damage);
|
||||||
m_caster.EnergizeBySpell(unitTarget, m_spellInfo.Id, gain, power);
|
m_caster.EnergizeBySpell(unitTarget, m_spellInfo, gain, power);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendLoot(ObjectGuid guid, LootType loottype)
|
void SendLoot(ObjectGuid guid, LootType loottype)
|
||||||
@@ -2411,28 +2411,22 @@ namespace Game.Spells
|
|||||||
|
|
||||||
// this effect use before aura Taunt apply for prevent taunt already attacking target
|
// this effect use before aura Taunt apply for prevent taunt already attacking target
|
||||||
// for spell as marked "non effective at already attacking target"
|
// for spell as marked "non effective at already attacking target"
|
||||||
if (unitTarget == null || !unitTarget.CanHaveThreatList() || unitTarget.GetVictim() == m_caster)
|
if (unitTarget == null || !unitTarget.CanHaveThreatList())
|
||||||
{
|
{
|
||||||
SendCastResult(SpellCastResult.DontReport);
|
SendCastResult(SpellCastResult.DontReport);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
SendCastResult(SpellCastResult.DontReport);
|
||||||
float myThreat = unitTarget.GetThreatManager().GetThreat(m_caster);
|
return;
|
||||||
float topThreat = unitTarget.GetThreatManager().GetOnlineContainer().GetMostHated().GetThreat();
|
|
||||||
if (topThreat > myThreat)
|
|
||||||
unitTarget.GetThreatManager().AddThreat(m_caster, topThreat - myThreat);
|
|
||||||
|
|
||||||
//Set aggro victim to caster
|
|
||||||
HostileReference forcedVictim = unitTarget.GetThreatManager().GetOnlineContainer().GetReferenceByTarget(m_caster);
|
|
||||||
if (forcedVictim != null)
|
|
||||||
unitTarget.GetThreatManager().SetCurrentVictim(forcedVictim);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unitTarget.ToCreature().IsAIEnabled && !unitTarget.ToCreature().HasReactState(ReactStates.Passive))
|
if (!mgr.IsThreatListEmpty())
|
||||||
unitTarget.ToCreature().GetAI().AttackStart(m_caster);
|
// Set threat equal to highest threat currently on target
|
||||||
|
mgr.MatchUnitThreatToHighestThreat(m_caster);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SpellEffectHandler(SpellEffectName.WeaponDamageNoSchool)]
|
[SpellEffectHandler(SpellEffectName.WeaponDamageNoSchool)]
|
||||||
@@ -2654,13 +2648,13 @@ namespace Game.Spells
|
|||||||
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (unitTarget == null || !unitTarget.IsAlive() || !m_caster.IsAlive())
|
if (unitTarget == null || !m_caster.IsAlive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!unitTarget.CanHaveThreatList())
|
if (!unitTarget.CanHaveThreatList())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unitTarget.GetThreatManager().AddThreat(m_caster, damage);
|
unitTarget.GetThreatManager().AddThreat(m_caster, damage, m_spellInfo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SpellEffectHandler(SpellEffectName.HealMaxHealth)]
|
[SpellEffectHandler(SpellEffectName.HealMaxHealth)]
|
||||||
@@ -3241,19 +3235,19 @@ namespace Game.Spells
|
|||||||
if (unitTarget == null)
|
if (unitTarget == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (unitTarget.IsTypeId(TypeId.Player))
|
if (unitTarget.IsPlayer() && !unitTarget.GetMap().IsDungeon())
|
||||||
unitTarget.ToPlayer().SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
|
|
||||||
|
|
||||||
unitTarget.GetHostileRefManager().UpdateVisibility();
|
|
||||||
|
|
||||||
var attackers = unitTarget.GetAttackers();
|
|
||||||
for (var i = 0; i < attackers.Count; ++i)
|
|
||||||
{
|
{
|
||||||
var unit = attackers[i];
|
// stop all pve combat for players outside dungeons, suppress pvp combat
|
||||||
if (!unit.CanSeeOrDetect(unitTarget))
|
unitTarget.CombatStop(false, false);
|
||||||
unit.AttackStop();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// in dungeons (or for nonplayers), reset this unit on all enemies' threat lists
|
||||||
|
foreach (var pair in unitTarget.GetThreatManager().GetThreatenedByMeList())
|
||||||
|
pair.Value.SetThreat(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makes spells cast before this time fizzle
|
||||||
unitTarget.LastSanctuaryTime = GameTime.GetGameTimeMS();
|
unitTarget.LastSanctuaryTime = GameTime.GetGameTimeMS();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4808,7 +4802,7 @@ namespace Game.Spells
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (unitTarget != null)
|
if (unitTarget != null)
|
||||||
m_caster.SetRedirectThreat(unitTarget.GetGUID(), (uint)damage);
|
m_caster.GetThreatManager().RegisterRedirectThreat(m_spellInfo.Id, unitTarget.GetGUID(), (uint)damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SpellEffectHandler(SpellEffectName.GameObjectDamage)]
|
[SpellEffectHandler(SpellEffectName.GameObjectDamage)]
|
||||||
|
|||||||
@@ -1022,8 +1022,8 @@ namespace Game.Spells
|
|||||||
// creature/player specific target checks
|
// creature/player specific target checks
|
||||||
if (unitTarget != null)
|
if (unitTarget != null)
|
||||||
{
|
{
|
||||||
// spells cannot be cast if player is in fake combat also
|
// spells cannot be cast if target has a pet in combat either
|
||||||
if (HasAttribute(SpellAttr1.CantTargetInCombat) && (unitTarget.IsInCombat() || unitTarget.IsPetInCombat()))
|
if (HasAttribute(SpellAttr1.CantTargetInCombat) && (unitTarget.IsInCombat() || unitTarget.HasUnitFlag(UnitFlags.PetInCombat)))
|
||||||
return SpellCastResult.TargetAffectingCombat;
|
return SpellCastResult.TargetAffectingCombat;
|
||||||
|
|
||||||
// only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts
|
// only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts
|
||||||
|
|||||||
@@ -76,21 +76,13 @@ namespace Scripts.Pets
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// else compare best fit unit with current unit
|
// else compare best fit unit with current unit
|
||||||
var triggers = unit.GetThreatManager().GetThreatList();
|
float threat = unit.GetThreatManager().GetThreat(owner);
|
||||||
foreach (var reference in triggers)
|
|
||||||
{
|
|
||||||
// Try to find threat referenced to owner
|
|
||||||
if (reference.GetTarget() == owner)
|
|
||||||
{
|
|
||||||
// Check if best fit hostile unit hs lower threat than this current unit
|
// Check if best fit hostile unit hs lower threat than this current unit
|
||||||
if (highestThreat < reference.GetThreat())
|
if (highestThreat < threat)
|
||||||
{
|
{
|
||||||
// If so, update best fit unit
|
// If so, update best fit unit
|
||||||
highestThreat = reference.GetThreat();
|
highestThreat = threat;
|
||||||
highestThreatUnit = unit;
|
highestThreatUnit = unit;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// In case no unit with threat was found so far, always check for nearest unit (only for players)
|
// In case no unit with threat was found so far, always check for nearest unit (only for players)
|
||||||
if (unit.IsTypeId(TypeId.Player))
|
if (unit.IsTypeId(TypeId.Player))
|
||||||
@@ -112,30 +104,7 @@ namespace Scripts.Pets
|
|||||||
bool IsInThreatList(Unit target)
|
bool IsInThreatList(Unit target)
|
||||||
{
|
{
|
||||||
Unit owner = me.GetCharmerOrOwner();
|
Unit owner = me.GetCharmerOrOwner();
|
||||||
|
return owner && target.IsThreatenedBy(owner);
|
||||||
List<Unit> targets = new List<Unit>();
|
|
||||||
var u_check = new AnyUnfriendlyUnitInObjectRangeCheck(me, me, 30.0f);
|
|
||||||
var searcher = new UnitListSearcher(me, targets, u_check);
|
|
||||||
Cell.VisitAllObjects(me, searcher, 40.0f);
|
|
||||||
|
|
||||||
foreach (var unit in targets)
|
|
||||||
{
|
|
||||||
if (unit == target)
|
|
||||||
{
|
|
||||||
// Consider only units without CC
|
|
||||||
if (!unit.HasBreakableByDamageCrowdControlAura(unit))
|
|
||||||
{
|
|
||||||
var triggers = unit.GetThreatManager().GetThreatList();
|
|
||||||
foreach (var reference in triggers)
|
|
||||||
{
|
|
||||||
// Try to find threat referenced to owner
|
|
||||||
if (reference.GetTarget() == owner)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InitializeAI()
|
public override void InitializeAI()
|
||||||
|
|||||||
@@ -2179,7 +2179,7 @@ namespace Scripts.Spells.Generic
|
|||||||
if (mana != 0)
|
if (mana != 0)
|
||||||
{
|
{
|
||||||
mana /= 10;
|
mana /= 10;
|
||||||
caster.EnergizeBySpell(caster, GetId(), mana, PowerType.Mana);
|
caster.EnergizeBySpell(caster, GetSpellInfo(), mana, PowerType.Mana);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace Scripts.Spells.Hunter
|
|||||||
public const uint ExhilarationR2 = 231546;
|
public const uint ExhilarationR2 = 231546;
|
||||||
public const uint Lonewolf = 155228;
|
public const uint Lonewolf = 155228;
|
||||||
public const uint MastersCallTriggered = 62305;
|
public const uint MastersCallTriggered = 62305;
|
||||||
|
public const uint Misdirection = 34477;
|
||||||
public const uint MisdirectionProc = 35079;
|
public const uint MisdirectionProc = 35079;
|
||||||
public const uint PetLastStandTriggered = 53479;
|
public const uint PetLastStandTriggered = 53479;
|
||||||
public const uint PetHeartOfThePhoenixTriggered = 54114;
|
public const uint PetHeartOfThePhoenixTriggered = 54114;
|
||||||
@@ -205,12 +206,7 @@ namespace Scripts.Spells.Hunter
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!GetTarget().HasAura(SpellIds.MisdirectionProc))
|
if (!GetTarget().HasAura(SpellIds.MisdirectionProc))
|
||||||
GetTarget().ResetRedirectThreat();
|
GetTarget().GetThreatManager().UnregisterRedirectThreat(SpellIds.Misdirection);
|
||||||
}
|
|
||||||
|
|
||||||
bool CheckProc(ProcEventInfo eventInfo)
|
|
||||||
{
|
|
||||||
return GetTarget().GetRedirectThreatTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo)
|
void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo)
|
||||||
@@ -222,7 +218,6 @@ namespace Scripts.Spells.Hunter
|
|||||||
public override void Register()
|
public override void Register()
|
||||||
{
|
{
|
||||||
AfterEffectRemove.Add(new EffectApplyHandler(OnRemove, 1, AuraType.Dummy, AuraEffectHandleModes.Real));
|
AfterEffectRemove.Add(new EffectApplyHandler(OnRemove, 1, AuraType.Dummy, AuraEffectHandleModes.Real));
|
||||||
DoCheckProc.Add(new CheckProcHandler(CheckProc));
|
|
||||||
OnEffectProc.Add(new EffectProcHandler(HandleProc, 1, AuraType.Dummy));
|
OnEffectProc.Add(new EffectProcHandler(HandleProc, 1, AuraType.Dummy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,7 +228,7 @@ namespace Scripts.Spells.Hunter
|
|||||||
{
|
{
|
||||||
void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
|
void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
|
||||||
{
|
{
|
||||||
GetTarget().ResetRedirectThreat();
|
GetTarget().GetThreatManager().UnregisterRedirectThreat(SpellIds.Misdirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Register()
|
public override void Register()
|
||||||
|
|||||||
@@ -1550,18 +1550,24 @@ namespace Scripts.World.NpcSpecial
|
|||||||
_scheduler.Schedule(TimeSpan.FromSeconds(1), task =>
|
_scheduler.Schedule(TimeSpan.FromSeconds(1), task =>
|
||||||
{
|
{
|
||||||
long now = GameTime.GetGameTime();
|
long now = GameTime.GetGameTime();
|
||||||
foreach (var pair in _damageTimes.ToList())
|
var pveRefs = me.GetCombatManager().GetPvECombatRefs();
|
||||||
|
foreach (var pair in _damageTimes)
|
||||||
{
|
{
|
||||||
// If unit has not dealt damage to training dummy for 5 seconds, Remove him from combat
|
// If unit has not dealt damage to training dummy for 5 seconds, Remove him from combat
|
||||||
if (pair.Value < now - 5)
|
if (pair.Value < now - 5)
|
||||||
{
|
{
|
||||||
Unit unit = Global.ObjAccessor.GetUnit(me, pair.Key);
|
var refe = pveRefs.LookupByKey(pair.Key);
|
||||||
if (unit)
|
if (refe != null)
|
||||||
unit.GetHostileRefManager().DeleteReference(me);
|
refe.EndCombat();
|
||||||
|
|
||||||
_damageTimes.Remove(pair.Key);
|
_damageTimes.Remove(pair.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var pair in pveRefs)
|
||||||
|
if (!_damageTimes.ContainsKey(pair.Key))
|
||||||
|
_damageTimes[pair.Key] = now;
|
||||||
|
|
||||||
task.Repeat();
|
task.Repeat();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user