c5cccdec10
Port From (https://github.com/TrinityCore/TrinityCore/commit/261eb201bf107cef441d9b075e8f23bf0edfbaf0) Port From (https://github.com/TrinityCore/TrinityCore/commit/526b16fea41fb69a302fdebe8a727e175949f04b) Port From (https://github.com/TrinityCore/TrinityCore/commit/8b7dce6521323e788ebf28297ec941a6f9bf876f) Port From (https://github.com/TrinityCore/TrinityCore/commit/5334467f493eb16141f4ad0bce956bc3e19244a0) Port From (https://github.com/TrinityCore/TrinityCore/commit/1e7725c15b149baaf96731231239769e482ec2ed) Port From (https://github.com/TrinityCore/TrinityCore/commit/21712f475383f6f3fae107706f5de5195f75b760)
2310 lines
78 KiB
C#
2310 lines
78 KiB
C#
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
|
|
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
|
|
|
|
using Bgs.Protocol.Notification.V1;
|
|
using Framework.Constants;
|
|
using Game.Chat;
|
|
using Game.Entities;
|
|
using Game.Networking;
|
|
using Game.Networking.Packets;
|
|
using Game.Spells;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Game.Maps
|
|
{
|
|
public class Notifier
|
|
{
|
|
public virtual void Visit(IList<WorldObject> objs) { }
|
|
public virtual void Visit(IList<Creature> objs) { }
|
|
public virtual void Visit(IList<AreaTrigger> objs) { }
|
|
public virtual void Visit(IList<SceneObject> objs) { }
|
|
public virtual void Visit(IList<Conversation> objs) { }
|
|
public virtual void Visit(IList<GameObject> objs) { }
|
|
public virtual void Visit(IList<DynamicObject> objs) { }
|
|
public virtual void Visit(IList<Corpse> objs) { }
|
|
public virtual void Visit(IList<Player> objs) { }
|
|
|
|
public void CreatureUnitRelocationWorker(Creature c, Unit u)
|
|
{
|
|
if (!u.IsAlive() || !c.IsAlive() || c == u || u.IsInFlight())
|
|
return;
|
|
|
|
if (!c.HasUnitState(UnitState.Sightless))
|
|
{
|
|
if (c.IsAIEnabled() && c.CanSeeOrDetect(u, new CanSeeOrDetectExtraArgs() { DistanceCheck = true }))
|
|
c.GetAI().MoveInLineOfSight_Safe(u);
|
|
else
|
|
{
|
|
if (u.IsTypeId(TypeId.Player) && u.HasStealthAura() && c.IsAIEnabled() && c.CanSeeOrDetect(u, new CanSeeOrDetectExtraArgs() { DistanceCheck = true, AlertCheck = true }))
|
|
c.GetAI().TriggerAlert(u);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Visitor
|
|
{
|
|
public Visitor(Notifier notifier, GridMapTypeMask mask)
|
|
{
|
|
_notifier = notifier;
|
|
_mask = mask;
|
|
}
|
|
|
|
public void Visit(IList<WorldObject> collection) { _notifier.Visit(collection); }
|
|
public void Visit(IList<Creature> creatures) { _notifier.Visit(creatures); }
|
|
public void Visit(IList<AreaTrigger> areatriggers) { _notifier.Visit(areatriggers); }
|
|
public void Visit(IList<SceneObject> sceneObjects) { _notifier.Visit(sceneObjects); }
|
|
public void Visit(IList<Conversation> conversations) { _notifier.Visit(conversations); }
|
|
public void Visit(IList<GameObject> gameobjects) { _notifier.Visit(gameobjects); }
|
|
public void Visit(IList<DynamicObject> dynamicobjects) { _notifier.Visit(dynamicobjects); }
|
|
public void Visit(IList<Corpse> corpses) { _notifier.Visit(corpses); }
|
|
public void Visit(IList<Player> players) { _notifier.Visit(players); }
|
|
|
|
Notifier _notifier;
|
|
internal GridMapTypeMask _mask;
|
|
}
|
|
|
|
public class VisibleNotifier : Notifier
|
|
{
|
|
public VisibleNotifier(Player pl)
|
|
{
|
|
i_player = pl;
|
|
i_data = new UpdateData(pl.GetMapId());
|
|
vis_guids = [.. pl.m_clientGUIDs];
|
|
i_visibleNow = new List<WorldObject>();
|
|
}
|
|
|
|
public override void Visit(IList<WorldObject> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
WorldObject obj = objs[i];
|
|
|
|
vis_guids.Remove(obj.GetGUID());
|
|
i_player.UpdateVisibilityOf(obj, i_data, i_visibleNow);
|
|
}
|
|
}
|
|
|
|
public void SendToSelf()
|
|
{
|
|
// at this moment i_clientGUIDs have guids that not iterate at grid level checks
|
|
// but exist one case when this possible and object not out of range: transports
|
|
Transport transport = i_player.GetTransport<Transport>();
|
|
if (transport != null)
|
|
{
|
|
foreach (var passenger in transport.GetPassengers())
|
|
{
|
|
if (vis_guids.Remove(passenger.GetGUID()))
|
|
{
|
|
switch (passenger.GetTypeId())
|
|
{
|
|
case TypeId.GameObject:
|
|
i_player.UpdateVisibilityOf(passenger.ToGameObject(), i_data, i_visibleNow);
|
|
break;
|
|
case TypeId.Player:
|
|
i_player.UpdateVisibilityOf(passenger.ToPlayer(), i_data, i_visibleNow);
|
|
if (!passenger.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
passenger.ToPlayer().UpdateVisibilityOf(i_player);
|
|
break;
|
|
case TypeId.Unit:
|
|
i_player.UpdateVisibilityOf(passenger.ToCreature(), i_data, i_visibleNow);
|
|
break;
|
|
case TypeId.DynamicObject:
|
|
i_player.UpdateVisibilityOf(passenger.ToDynamicObject(), i_data, i_visibleNow);
|
|
break;
|
|
case TypeId.AreaTrigger:
|
|
i_player.UpdateVisibilityOf(passenger.ToAreaTrigger(), i_data, i_visibleNow);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var outOfRangeGuid in vis_guids)
|
|
{
|
|
i_player.m_clientGUIDs.Remove(outOfRangeGuid);
|
|
i_data.AddOutOfRangeGUID(outOfRangeGuid);
|
|
|
|
if (outOfRangeGuid.IsPlayer())
|
|
{
|
|
Player player = Global.ObjAccessor.FindPlayer(outOfRangeGuid);
|
|
if (player != null && !player.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
player.UpdateVisibilityOf(i_player);
|
|
}
|
|
}
|
|
|
|
if (!i_data.HasData())
|
|
return;
|
|
|
|
UpdateObject packet;
|
|
i_data.BuildPacket(out packet);
|
|
i_player.SendPacket(packet);
|
|
|
|
foreach (var visibleObject in i_visibleNow)
|
|
i_player.SendInitialVisiblePackets(visibleObject);
|
|
}
|
|
|
|
internal Player i_player;
|
|
internal UpdateData i_data;
|
|
internal List<ObjectGuid> vis_guids;
|
|
internal List<WorldObject> i_visibleNow;
|
|
}
|
|
|
|
public class VisibleChangesNotifier : Notifier
|
|
{
|
|
ICollection<WorldObject> i_objects;
|
|
|
|
public VisibleChangesNotifier(ICollection<WorldObject> objects)
|
|
{
|
|
i_objects = objects;
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
|
|
player.UpdateVisibilityOf(i_objects);
|
|
|
|
foreach (var visionPlayer in player.GetSharedVisionList())
|
|
{
|
|
if (visionPlayer.seerView == player)
|
|
visionPlayer.UpdateVisibilityOf(i_objects);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
foreach (var visionPlayer in creature.GetSharedVisionList())
|
|
if (visionPlayer.seerView == creature)
|
|
visionPlayer.UpdateVisibilityOf(i_objects);
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<DynamicObject> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
DynamicObject dynamicObject = objs[i];
|
|
Unit caster = dynamicObject.GetCaster();
|
|
if (caster != null)
|
|
{
|
|
Player pl = caster.ToPlayer();
|
|
if (pl != null && pl.seerView == dynamicObject)
|
|
pl.UpdateVisibilityOf(i_objects);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PlayerRelocationNotifier : VisibleNotifier
|
|
{
|
|
public PlayerRelocationNotifier(Player player) : base(player) { }
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
base.Visit(objs);
|
|
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
vis_guids.Remove(player.GetGUID());
|
|
|
|
i_player.UpdateVisibilityOf(player, i_data, i_visibleNow);
|
|
|
|
if (player.seerView.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
continue;
|
|
|
|
player.UpdateVisibilityOf(i_player);
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
base.Visit(objs);
|
|
|
|
bool relocated_for_ai = (i_player == i_player.seerView);
|
|
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
vis_guids.Remove(creature.GetGUID());
|
|
|
|
i_player.UpdateVisibilityOf(creature, i_data, i_visibleNow);
|
|
|
|
if (relocated_for_ai && !creature.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
CreatureUnitRelocationWorker(creature, i_player);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class CreatureRelocationNotifier : Notifier
|
|
{
|
|
public CreatureRelocationNotifier(Creature c)
|
|
{
|
|
i_creature = c;
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
if (!player.seerView.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
player.UpdateVisibilityOf(i_creature);
|
|
|
|
CreatureUnitRelocationWorker(i_creature, player);
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
if (!i_creature.IsAlive())
|
|
return;
|
|
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
CreatureUnitRelocationWorker(i_creature, creature);
|
|
|
|
if (!creature.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
CreatureUnitRelocationWorker(creature, i_creature);
|
|
}
|
|
}
|
|
|
|
Creature i_creature;
|
|
}
|
|
|
|
public class DelayedUnitRelocation : Notifier
|
|
{
|
|
public DelayedUnitRelocation(Cell c, CellCoord pair, Map map, float radius)
|
|
{
|
|
i_map = map;
|
|
cell = c;
|
|
p = pair;
|
|
i_radius = radius;
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
WorldObject viewPoint = player.seerView;
|
|
|
|
if (!viewPoint.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
continue;
|
|
|
|
if (player != viewPoint && !viewPoint.IsPositionValid())
|
|
continue;
|
|
|
|
var relocate = new PlayerRelocationNotifier(player);
|
|
Cell.VisitAllObjects(viewPoint, relocate, i_radius, false);
|
|
|
|
relocate.SendToSelf();
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
if (!creature.IsNeedNotify(NotifyFlags.VisibilityChanged))
|
|
continue;
|
|
|
|
CreatureRelocationNotifier relocate = new(creature);
|
|
|
|
var c2world_relocation = new Visitor(relocate, GridMapTypeMask.AllWorld);
|
|
var c2grid_relocation = new Visitor(relocate, GridMapTypeMask.AllGrid);
|
|
|
|
cell.Visit(p, c2world_relocation, i_map, creature, i_radius);
|
|
cell.Visit(p, c2grid_relocation, i_map, creature, i_radius);
|
|
}
|
|
}
|
|
|
|
Map i_map;
|
|
Cell cell;
|
|
CellCoord p;
|
|
float i_radius;
|
|
}
|
|
|
|
public class AIRelocationNotifier : Notifier
|
|
{
|
|
public AIRelocationNotifier(Unit unit)
|
|
{
|
|
i_unit = unit;
|
|
isCreature = unit.IsTypeId(TypeId.Unit);
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
CreatureUnitRelocationWorker(creature, i_unit);
|
|
if (isCreature)
|
|
CreatureUnitRelocationWorker(i_unit.ToCreature(), creature);
|
|
}
|
|
}
|
|
|
|
Unit i_unit;
|
|
bool isCreature;
|
|
}
|
|
|
|
public class CreatureAggroGracePeriodExpiredNotifier : Notifier
|
|
{
|
|
Creature i_creature;
|
|
|
|
public CreatureAggroGracePeriodExpiredNotifier(Creature c)
|
|
{
|
|
i_creature = c;
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
foreach (var creature in objs)
|
|
{
|
|
CreatureUnitRelocationWorker(creature, i_creature);
|
|
CreatureUnitRelocationWorker(i_creature, creature);
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
foreach (var player in objs)
|
|
CreatureUnitRelocationWorker(i_creature, player);
|
|
}
|
|
}
|
|
|
|
public class PacketSenderRef(ServerPacket message)
|
|
{
|
|
public virtual void Invoke(Player player)
|
|
{
|
|
player.SendPacket(message);
|
|
}
|
|
|
|
public static implicit operator IDoWork<Player>(PacketSenderRef obj) => obj.Invoke;
|
|
}
|
|
|
|
public class PacketSenderOwning<T> where T : ServerPacket, new()
|
|
{
|
|
public T Data = new();
|
|
|
|
public void Invoke(Player player)
|
|
{
|
|
player.SendPacket(Data);
|
|
}
|
|
|
|
public static implicit operator IDoWork<Player>(PacketSenderOwning<T> obj) => obj.Invoke;
|
|
}
|
|
|
|
public class MessageDistDeliverer : Notifier
|
|
{
|
|
WorldObject i_source;
|
|
Action<Player> i_packetSender;
|
|
PhaseShift i_phaseShift;
|
|
float i_distSq;
|
|
Team team;
|
|
Player skipped_receiver;
|
|
bool required3dDist;
|
|
|
|
public MessageDistDeliverer(WorldObject src, IDoWork<Player> packetSender, float dist, bool own_team_only = false, Player skipped = null, bool req3dDist = false)
|
|
{
|
|
i_source = src;
|
|
i_packetSender = packetSender.Invoke;
|
|
i_phaseShift = src.GetPhaseShift();
|
|
i_distSq = dist * dist;
|
|
if (own_team_only && src.IsPlayer())
|
|
team = src.ToPlayer().GetEffectiveTeam();
|
|
|
|
skipped_receiver = skipped;
|
|
required3dDist = req3dDist;
|
|
}
|
|
|
|
public MessageDistDeliverer(WorldObject src, Action<Player> packetSender, float dist, bool own_team_only = false, Player skipped = null, bool req3dDist = false)
|
|
{
|
|
i_source = src;
|
|
i_packetSender = packetSender;
|
|
i_phaseShift = src.GetPhaseShift();
|
|
i_distSq = dist * dist;
|
|
if (own_team_only && src.IsPlayer())
|
|
team = src.ToPlayer().GetEffectiveTeam();
|
|
|
|
skipped_receiver = skipped;
|
|
required3dDist = req3dDist;
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
if (!player.InSamePhase(i_phaseShift))
|
|
continue;
|
|
|
|
if ((!required3dDist ? player.GetExactDist2dSq(i_source) : player.GetExactDistSq(i_source)) > i_distSq)
|
|
continue;
|
|
|
|
// Send packet to all who are sharing the player's vision
|
|
if (player.HasSharedVision())
|
|
{
|
|
foreach (var visionPlayer in player.GetSharedVisionList())
|
|
if (visionPlayer.seerView == player)
|
|
SendPacket(visionPlayer);
|
|
}
|
|
|
|
if (player.seerView == player || player.GetVehicle() != null)
|
|
SendPacket(player);
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
if (!creature.InSamePhase(i_phaseShift))
|
|
continue;
|
|
|
|
if ((!required3dDist ? creature.GetExactDist2dSq(i_source) : creature.GetExactDistSq(i_source)) > i_distSq)
|
|
continue;
|
|
|
|
// Send packet to all who are sharing the creature's vision
|
|
if (creature.HasSharedVision())
|
|
{
|
|
foreach (var visionPlayer in creature.GetSharedVisionList())
|
|
if (visionPlayer.seerView == creature)
|
|
SendPacket(visionPlayer);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<DynamicObject> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
DynamicObject dynamicObject = objs[i];
|
|
if (!dynamicObject.InSamePhase(i_phaseShift))
|
|
continue;
|
|
|
|
if ((!required3dDist ? dynamicObject.GetExactDist2dSq(i_source) : dynamicObject.GetExactDistSq(i_source)) > i_distSq)
|
|
continue;
|
|
|
|
// Send packet back to the caster if the caster has vision of dynamic object
|
|
Unit caster = dynamicObject.GetCaster();
|
|
if (caster != null)
|
|
{
|
|
Player player = caster.ToPlayer();
|
|
if (player != null && player.seerView == dynamicObject)
|
|
SendPacket(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SendPacket(Player player)
|
|
{
|
|
// never send packet to self
|
|
if (i_source == player || (team != 0 && player.GetEffectiveTeam() != team) || skipped_receiver == player)
|
|
return;
|
|
|
|
if (!player.HaveAtClient(i_source))
|
|
return;
|
|
|
|
i_packetSender.Invoke(player);
|
|
}
|
|
}
|
|
|
|
public class MessageDistDelivererToHostile<T> : Notifier
|
|
{
|
|
Unit i_source;
|
|
IDoWork<Player> i_packetSender;
|
|
PhaseShift i_phaseShift;
|
|
float i_distSq;
|
|
|
|
public MessageDistDelivererToHostile(Unit src, IDoWork<Player> packetSender, float dist)
|
|
{
|
|
i_source = src;
|
|
i_packetSender = packetSender;
|
|
i_phaseShift = src.GetPhaseShift();
|
|
i_distSq = dist * dist;
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
if (!player.InSamePhase(i_phaseShift))
|
|
continue;
|
|
|
|
if (player.GetExactDist2dSq(i_source) > i_distSq)
|
|
continue;
|
|
|
|
// Send packet to all who are sharing the player's vision
|
|
if (player.HasSharedVision())
|
|
{
|
|
foreach (var visionPlayer in player.GetSharedVisionList())
|
|
if (visionPlayer.seerView == player)
|
|
SendPacket(visionPlayer);
|
|
}
|
|
|
|
if (player.seerView == player || player.GetVehicle() != null)
|
|
SendPacket(player);
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
if (!creature.InSamePhase(i_phaseShift))
|
|
continue;
|
|
|
|
if (creature.GetExactDist2dSq(i_source) > i_distSq)
|
|
continue;
|
|
|
|
// Send packet to all who are sharing the creature's vision
|
|
if (creature.HasSharedVision())
|
|
{
|
|
foreach (var player in creature.GetSharedVisionList())
|
|
if (player.seerView == creature)
|
|
SendPacket(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<DynamicObject> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
DynamicObject dynamicObject = objs[i];
|
|
if (!dynamicObject.InSamePhase(i_phaseShift))
|
|
continue;
|
|
|
|
if (dynamicObject.GetExactDist2dSq(i_source) > i_distSq)
|
|
continue;
|
|
|
|
Unit caster = dynamicObject.GetCaster();
|
|
if (caster != null)
|
|
{
|
|
// Send packet back to the caster if the caster has vision of dynamic object
|
|
Player player = caster.ToPlayer();
|
|
if (player != null && player.seerView == dynamicObject)
|
|
SendPacket(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SendPacket(Player player)
|
|
{
|
|
// never send packet to self
|
|
if (player == i_source || !player.HaveAtClient(i_source) || player.IsFriendlyTo(i_source))
|
|
return;
|
|
|
|
i_packetSender.Invoke(player);
|
|
}
|
|
}
|
|
|
|
public class ObjectUpdater(uint diff) : Notifier
|
|
{
|
|
public override void Visit(IList<WorldObject> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
WorldObject obj = objs[i];
|
|
|
|
if (obj.IsTypeId(TypeId.Player) || obj.IsTypeId(TypeId.Corpse))
|
|
continue;
|
|
|
|
if (obj.IsInWorld)
|
|
obj.Update(diff);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ResetNotifier : Notifier
|
|
{
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
player.ResetAllNotifies();
|
|
}
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Creature creature = objs[i];
|
|
creature.ResetAllNotifies();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class WorldObjectChangeAccumulator
|
|
{
|
|
Dictionary<Player, UpdateData> updateData;
|
|
WorldObject worldObject;
|
|
HashSet<ObjectGuid> plr_list = new();
|
|
|
|
public WorldObjectChangeAccumulator(WorldObject obj, Dictionary<Player, UpdateData> d)
|
|
{
|
|
updateData = d;
|
|
worldObject = obj;
|
|
}
|
|
|
|
public void Invoke(Player player)
|
|
{
|
|
// Only send update once to a player
|
|
if (player.HaveAtClient(worldObject) && plr_list.Add(player.GetGUID()))
|
|
worldObject.BuildFieldsUpdate(player, updateData);
|
|
}
|
|
|
|
public static implicit operator IDoWork<Player>(WorldObjectChangeAccumulator obj) => obj.Invoke;
|
|
}
|
|
|
|
//Searchers
|
|
public enum WorldObjectSearcherContinuation
|
|
{
|
|
Continue,
|
|
Return
|
|
}
|
|
|
|
public interface IResultInserter<T>
|
|
{
|
|
WorldObjectSearcherContinuation ShouldContinue();
|
|
void Insert(T obj);
|
|
T GetResult();
|
|
}
|
|
|
|
class SearcherFirstObjectResult<T>() : IResultInserter<T>
|
|
{
|
|
T result;
|
|
|
|
public WorldObjectSearcherContinuation ShouldContinue()
|
|
{
|
|
return result != null ? WorldObjectSearcherContinuation.Return : WorldObjectSearcherContinuation.Continue;
|
|
}
|
|
|
|
public void Insert(T obj)
|
|
{
|
|
result = obj;
|
|
}
|
|
|
|
public T GetResult()
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class SearcherLastObjectResult<T>() : IResultInserter<T>
|
|
{
|
|
T result;
|
|
|
|
public WorldObjectSearcherContinuation ShouldContinue()
|
|
{
|
|
return WorldObjectSearcherContinuation.Continue;
|
|
}
|
|
|
|
public void Insert(T obj)
|
|
{
|
|
result = obj;
|
|
}
|
|
|
|
public T GetResult()
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class SearcherContainerResult<T>(List<T> container_) : IResultInserter<T>
|
|
{
|
|
ICollection<T> container = container_;
|
|
|
|
public WorldObjectSearcherContinuation ShouldContinue()
|
|
{
|
|
return WorldObjectSearcherContinuation.Continue;
|
|
}
|
|
|
|
public void Insert(T obj)
|
|
{
|
|
container.Add(obj);
|
|
}
|
|
|
|
public T GetResult()
|
|
{
|
|
return default;
|
|
}
|
|
}
|
|
|
|
struct DynamicGridMapTypeMaskCheck(GridMapTypeMask mask)
|
|
{
|
|
static Dictionary<Type, GridMapTypeMask> GridMapTypeMaskForType = new()
|
|
{
|
|
{ typeof(Corpse), GridMapTypeMask.Corpse },
|
|
{ typeof(Creature), GridMapTypeMask.Creature },
|
|
{ typeof(DynamicObject), GridMapTypeMask.DynamicObject },
|
|
{ typeof(GameObject), GridMapTypeMask.GameObject },
|
|
{ typeof(Player), GridMapTypeMask.Player },
|
|
{ typeof(AreaTrigger), GridMapTypeMask.AreaTrigger },
|
|
{ typeof(SceneObject), GridMapTypeMask.SceneObject },
|
|
{ typeof(Conversation), GridMapTypeMask.Conversation }
|
|
};
|
|
GridMapTypeMask MaskValue = mask;
|
|
|
|
public bool Includes(GridMapTypeMask mapTypeMask)
|
|
{
|
|
return (MaskValue & mapTypeMask) != 0;
|
|
}
|
|
|
|
public static GridMapTypeMask GetTypeMaskByType<T>()
|
|
{
|
|
return GridMapTypeMaskForType.LookupByKey(typeof(T));
|
|
}
|
|
}
|
|
|
|
// WorldObject searchers & workers
|
|
|
|
public class WorldObjectSearcherBase<T> : Notifier where T : WorldObject
|
|
{
|
|
GridMapTypeMask i_mapTypeMask;
|
|
public PhaseShift i_phaseShift;
|
|
ICheck<T> i_check;
|
|
IResultInserter<T> resultInserter;
|
|
|
|
public WorldObjectSearcherBase(PhaseShift phaseShift, IResultInserter<T> result, ICheck<T> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
{
|
|
i_mapTypeMask = mapTypeMask;
|
|
i_phaseShift = phaseShift;
|
|
i_check = check;
|
|
resultInserter = result;
|
|
}
|
|
|
|
public override void Visit(IList<GameObject> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.GameObject))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Player))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
public override void Visit(IList<Creature> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Creature))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
public override void Visit(IList<Corpse> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Corpse))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
public override void Visit(IList<DynamicObject> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.DynamicObject))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
public override void Visit(IList<AreaTrigger> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.AreaTrigger))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
public override void Visit(IList<SceneObject> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.SceneObject))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
public override void Visit(IList<Conversation> objs)
|
|
{
|
|
if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation))
|
|
return;
|
|
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
void VisitImpl<TT>(IList<TT> objs) where TT : WorldObject
|
|
{
|
|
if (resultInserter.ShouldContinue() == WorldObjectSearcherContinuation.Return)
|
|
return;
|
|
|
|
foreach (dynamic obj in objs)
|
|
{
|
|
if (!obj.InSamePhase(i_phaseShift))
|
|
continue;
|
|
|
|
if (i_check.Invoke(obj))
|
|
{
|
|
resultInserter.Insert(obj);
|
|
|
|
if (resultInserter.ShouldContinue() == WorldObjectSearcherContinuation.Return)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
public T GetResult()
|
|
{
|
|
return resultInserter.GetResult();
|
|
}
|
|
}
|
|
|
|
public class WorldObjectWorkerBase<T>(PhaseShift phaseShift, IDoWork<T> work, GridMapTypeMask mapTypeMask = GridMapTypeMask.All) : Notifier where T : WorldObject
|
|
{
|
|
DynamicGridMapTypeMaskCheck i_mapTypeMask = new(mapTypeMask);
|
|
PhaseShift i_phaseShift = phaseShift;
|
|
IDoWork<T> i_work = work;
|
|
|
|
public override void Visit(IList<WorldObject> objs)
|
|
{
|
|
if (i_mapTypeMask.Includes(DynamicGridMapTypeMaskCheck.GetTypeMaskByType<T>()))
|
|
VisitImpl(objs);
|
|
}
|
|
|
|
void VisitImpl(IList<WorldObject> objs)
|
|
{
|
|
foreach (var obj in objs)
|
|
if (obj.InSamePhase(i_phaseShift))
|
|
i_work.Invoke(obj as T);
|
|
}
|
|
}
|
|
|
|
public class WorldObjectSearcher : WorldObjectSearcherBase<WorldObject>
|
|
{
|
|
public WorldObjectSearcher(PhaseShift phaseShift, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: base(phaseShift, new SearcherFirstObjectResult<WorldObject>(), check, mapTypeMask) { }
|
|
|
|
public WorldObjectSearcher(WorldObject searcher, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: this(searcher.GetPhaseShift(), check, mapTypeMask) { }
|
|
}
|
|
|
|
public class WorldObjectLastSearcher : WorldObjectSearcherBase<WorldObject>
|
|
{
|
|
public WorldObjectLastSearcher(PhaseShift phaseShift, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: base(phaseShift, new SearcherLastObjectResult<WorldObject>(), check, mapTypeMask) { }
|
|
|
|
public WorldObjectLastSearcher(WorldObject searcher, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: this(searcher.GetPhaseShift(), check, mapTypeMask) { }
|
|
}
|
|
|
|
public class WorldObjectListSearcher : WorldObjectSearcherBase<WorldObject>
|
|
{
|
|
public WorldObjectListSearcher(PhaseShift phaseShift, List<WorldObject> container, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: base(phaseShift, new SearcherContainerResult<WorldObject>(container), check, mapTypeMask) { }
|
|
|
|
public WorldObjectListSearcher(WorldObject searcher, List<WorldObject> container, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: this(searcher.GetPhaseShift(), container, check, mapTypeMask) { }
|
|
}
|
|
|
|
public class WorldObjectWorker<T> : WorldObjectWorkerBase<T> where T : WorldObject
|
|
{
|
|
public WorldObjectWorker(PhaseShift phaseShift, IDoWork<T> work, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: base(phaseShift, work, mapTypeMask) { }
|
|
|
|
public WorldObjectWorker(WorldObject searcher, IDoWork<T> work, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
|
: this(searcher.GetPhaseShift(), work, mapTypeMask) { }
|
|
}
|
|
|
|
// Gameobject searchers
|
|
|
|
public class GameObjectSearcherBase : WorldObjectSearcherBase<GameObject>
|
|
{
|
|
public GameObjectSearcherBase(PhaseShift phaseShift, IResultInserter<GameObject> result, ICheck<GameObject> check)
|
|
: base(phaseShift, result, check, GridMapTypeMask.GameObject) { }
|
|
}
|
|
|
|
public class GameObjectSearcher : GameObjectSearcherBase
|
|
{
|
|
public GameObjectSearcher(PhaseShift phaseShift, ICheck<GameObject> check)
|
|
: base(phaseShift, new SearcherFirstObjectResult<GameObject>(), check) { }
|
|
|
|
public GameObjectSearcher(WorldObject searcher, ICheck<GameObject> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class GameObjectLastSearcher : GameObjectSearcherBase
|
|
{
|
|
public GameObjectLastSearcher(PhaseShift phaseShift, ICheck<GameObject> check)
|
|
: base(phaseShift, new SearcherLastObjectResult<GameObject>(), check) { }
|
|
|
|
public GameObjectLastSearcher(WorldObject searcher, ICheck<GameObject> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class GameObjectListSearcher : GameObjectSearcherBase
|
|
{
|
|
public GameObjectListSearcher(PhaseShift phaseShift, List<GameObject> container, ICheck<GameObject> check)
|
|
: base(phaseShift, new SearcherContainerResult<GameObject>(container), check) { }
|
|
|
|
public GameObjectListSearcher(WorldObject searcher, List<GameObject> container, ICheck<GameObject> check)
|
|
: this(searcher.GetPhaseShift(), container, check) { }
|
|
}
|
|
|
|
public class GameObjectWorker : WorldObjectWorkerBase<GameObject>
|
|
{
|
|
public GameObjectWorker(PhaseShift phaseShift, IDoWork<GameObject> work)
|
|
: base(phaseShift, work, GridMapTypeMask.GameObject) { }
|
|
|
|
public GameObjectWorker(WorldObject searcher, IDoWork<GameObject> work)
|
|
: this(searcher.GetPhaseShift(), work) { }
|
|
}
|
|
|
|
// Unit searchers
|
|
|
|
public class UnitSearcherBase : WorldObjectSearcherBase<Unit>
|
|
{
|
|
public UnitSearcherBase(PhaseShift phaseShift, IResultInserter<Unit> result, ICheck<Unit> check)
|
|
: base(phaseShift, result, check, GridMapTypeMask.Creature | GridMapTypeMask.Player) { }
|
|
}
|
|
|
|
// First accepted by Check Unit if any
|
|
public class UnitSearcher : UnitSearcherBase
|
|
{
|
|
public UnitSearcher(PhaseShift phaseShift, ICheck<Unit> check)
|
|
: base(phaseShift, new SearcherFirstObjectResult<Unit>(), check) { }
|
|
|
|
public UnitSearcher(WorldObject searcher, ICheck<Unit> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
// Last accepted by Check Unit if any (Check can change requirements at each call)
|
|
public class UnitLastSearcher : UnitSearcherBase
|
|
{
|
|
public UnitLastSearcher(PhaseShift phaseShift, ICheck<Unit> check)
|
|
: base(phaseShift, new SearcherLastObjectResult<Unit>(), check) { }
|
|
|
|
public UnitLastSearcher(WorldObject searcher, ICheck<Unit> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
// All accepted by Check units if any
|
|
public class UnitListSearcher : UnitSearcherBase
|
|
{
|
|
public UnitListSearcher(PhaseShift phaseShift, List<Unit> container, ICheck<Unit> check)
|
|
: base(phaseShift, new SearcherContainerResult<Unit>(container), check) { }
|
|
|
|
public UnitListSearcher(WorldObject searcher, List<Unit> container, ICheck<Unit> check)
|
|
: this(searcher.GetPhaseShift(), container, check) { }
|
|
}
|
|
|
|
public class UnitWorker : WorldObjectWorkerBase<Unit>
|
|
{
|
|
public UnitWorker(PhaseShift phaseShift, IDoWork<Unit> work)
|
|
: base(phaseShift, work, GridMapTypeMask.Creature | GridMapTypeMask.Player) { }
|
|
|
|
public UnitWorker(WorldObject searcher, IDoWork<Unit> work)
|
|
: this(searcher.GetPhaseShift(), work) { }
|
|
}
|
|
|
|
// Creature searchers
|
|
|
|
public class CreatureSearcherBase : WorldObjectSearcherBase<Creature>
|
|
{
|
|
public CreatureSearcherBase(PhaseShift phaseShift, IResultInserter<Creature> result, ICheck<Creature> check)
|
|
: base(phaseShift, result, check, GridMapTypeMask.Creature) { }
|
|
}
|
|
|
|
public class CreatureSearcher : CreatureSearcherBase
|
|
{
|
|
public CreatureSearcher(PhaseShift phaseShift, ICheck<Creature> check)
|
|
: base(phaseShift, new SearcherFirstObjectResult<Creature>(), check) { }
|
|
|
|
public CreatureSearcher(WorldObject searcher, ICheck<Creature> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
// Last accepted by Check Creature if any (Check can change requirements at each call)
|
|
public class CreatureLastSearcher : CreatureSearcherBase
|
|
{
|
|
public CreatureLastSearcher(PhaseShift phaseShift, ICheck<Creature> check)
|
|
: base(phaseShift, new SearcherLastObjectResult<Creature>(), check) { }
|
|
|
|
public CreatureLastSearcher(WorldObject searcher, ICheck<Creature> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class CreatureListSearcher : CreatureSearcherBase
|
|
{
|
|
public CreatureListSearcher(PhaseShift phaseShift, List<Creature> container, ICheck<Creature> check)
|
|
: base(phaseShift, new SearcherContainerResult<Creature>(container), check) { }
|
|
|
|
public CreatureListSearcher(WorldObject searcher, List<Creature> container, ICheck<Creature> check)
|
|
: this(searcher.GetPhaseShift(), container, check) { }
|
|
}
|
|
|
|
public class CreatureWorker : WorldObjectWorkerBase<Creature>
|
|
{
|
|
public CreatureWorker(PhaseShift phaseShift, IDoWork<Creature> work)
|
|
: base(phaseShift, work) { }
|
|
|
|
public CreatureWorker(WorldObject searcher, IDoWork<Creature> work)
|
|
: this(searcher.GetPhaseShift(), work) { }
|
|
}
|
|
|
|
// Player searchers
|
|
|
|
public class PlayerSearcherBase : WorldObjectSearcherBase<Player>
|
|
{
|
|
public PlayerSearcherBase(PhaseShift phaseShift, IResultInserter<Player> result, ICheck<Player> check)
|
|
: base(phaseShift, result, check, GridMapTypeMask.Player) { }
|
|
}
|
|
|
|
public class PlayerSearcher : PlayerSearcherBase
|
|
{
|
|
public PlayerSearcher(PhaseShift phaseShift, ICheck<Player> check)
|
|
: base(phaseShift, new SearcherFirstObjectResult<Player>(), check) { }
|
|
|
|
public PlayerSearcher(WorldObject searcher, ICheck<Player> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class PlayerLastSearcher : PlayerSearcherBase
|
|
{
|
|
public PlayerLastSearcher(PhaseShift phaseShift, ICheck<Player> check)
|
|
: base(phaseShift, new SearcherLastObjectResult<Player>(), check) { }
|
|
|
|
public PlayerLastSearcher(WorldObject searcher, ICheck<Player> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class PlayerListSearcher : PlayerSearcherBase
|
|
{
|
|
public PlayerListSearcher(PhaseShift phaseShift, List<Player> container, ICheck<Player> check)
|
|
: base(phaseShift, new SearcherContainerResult<Player>(container), check) { }
|
|
|
|
|
|
public PlayerListSearcher(WorldObject searcher, List<Player> container, ICheck<Player> check)
|
|
: this(searcher.GetPhaseShift(), container, check) { }
|
|
}
|
|
|
|
public class PlayerWorker : WorldObjectWorkerBase<Player>
|
|
{
|
|
public PlayerWorker(PhaseShift phaseShift, IDoWork<Player> work)
|
|
: base(phaseShift, work, GridMapTypeMask.Player) { }
|
|
|
|
public PlayerWorker(WorldObject searcher, IDoWork<Player> work)
|
|
: this(searcher.GetPhaseShift(), work) { }
|
|
}
|
|
|
|
public class PlayerDistWorker : Notifier
|
|
{
|
|
WorldObject i_searcher;
|
|
float i_dist;
|
|
IDoWork<Player> i_work;
|
|
|
|
public PlayerDistWorker(WorldObject searcher, float _dist, IDoWork<Player> work)
|
|
{
|
|
i_searcher = searcher;
|
|
i_dist = _dist;
|
|
i_work = work.Invoke;
|
|
}
|
|
|
|
public override void Visit(IList<Player> objs)
|
|
{
|
|
for (var i = 0; i < objs.Count; ++i)
|
|
{
|
|
Player player = objs[i];
|
|
if (player.InSamePhase(i_searcher) && player.IsWithinDist(i_searcher, i_dist))
|
|
i_work(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
// AreaTrigger searchers
|
|
|
|
public class AreaTriggerSearcherBase : WorldObjectSearcherBase<AreaTrigger>
|
|
{
|
|
public AreaTriggerSearcherBase(PhaseShift phaseShift, IResultInserter<AreaTrigger> result, ICheck<AreaTrigger> check)
|
|
: base(phaseShift, result, check, GridMapTypeMask.AreaTrigger) { }
|
|
}
|
|
|
|
public class AreaTriggerSearcher : AreaTriggerSearcherBase
|
|
{
|
|
public AreaTriggerSearcher(PhaseShift phaseShift, ICheck<AreaTrigger> check)
|
|
: base(phaseShift, new SearcherFirstObjectResult<AreaTrigger>(), check) { }
|
|
|
|
public AreaTriggerSearcher(WorldObject searcher, ICheck<AreaTrigger> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class AreaTriggerLastSearcher : AreaTriggerSearcherBase
|
|
{
|
|
public AreaTriggerLastSearcher(PhaseShift phaseShift, ICheck<AreaTrigger> check)
|
|
: base(phaseShift, new SearcherLastObjectResult<AreaTrigger>(), check) { }
|
|
|
|
public AreaTriggerLastSearcher(WorldObject searcher, ICheck<AreaTrigger> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class AreaTriggerListSearcher : AreaTriggerSearcherBase
|
|
{
|
|
public AreaTriggerListSearcher(PhaseShift phaseShift, List<AreaTrigger> container, ICheck<AreaTrigger> check)
|
|
: base(phaseShift, new SearcherContainerResult<AreaTrigger>(container), check) { }
|
|
|
|
public AreaTriggerListSearcher(WorldObject searcher, List<AreaTrigger> container, ICheck<AreaTrigger> check)
|
|
: this(searcher.GetPhaseShift(), container, check) { }
|
|
}
|
|
|
|
public class AreaTriggerWorker : WorldObjectWorkerBase<AreaTrigger>
|
|
{
|
|
public AreaTriggerWorker(PhaseShift phaseShift, IDoWork<AreaTrigger> work)
|
|
: base(phaseShift, work, GridMapTypeMask.AreaTrigger) { }
|
|
|
|
public AreaTriggerWorker(WorldObject searcher, IDoWork<AreaTrigger> work)
|
|
: this(searcher.GetPhaseShift(), work) { }
|
|
}
|
|
|
|
// SceneObject searchers
|
|
|
|
public class SceneObjectSearcherBase : WorldObjectSearcherBase<SceneObject>
|
|
{
|
|
public SceneObjectSearcherBase(PhaseShift phaseShift, IResultInserter<SceneObject> result, ICheck<SceneObject> check)
|
|
: base(phaseShift, result, check, GridMapTypeMask.SceneObject) { }
|
|
}
|
|
|
|
public class SceneObjectSearcher : SceneObjectSearcherBase
|
|
{
|
|
public SceneObjectSearcher(PhaseShift phaseShift, ICheck<SceneObject> check)
|
|
: base(phaseShift, new SearcherFirstObjectResult<SceneObject>(), check) { }
|
|
|
|
public SceneObjectSearcher(WorldObject searcher, ICheck<SceneObject> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class SceneObjectLastSearcher : SceneObjectSearcherBase
|
|
{
|
|
public SceneObjectLastSearcher(PhaseShift phaseShift, ICheck<SceneObject> check)
|
|
: base(phaseShift, new SearcherLastObjectResult<SceneObject>(), check) { }
|
|
|
|
public SceneObjectLastSearcher(WorldObject searcher, ICheck<SceneObject> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class SceneObjectListSearcher : SceneObjectSearcherBase
|
|
{
|
|
public SceneObjectListSearcher(PhaseShift phaseShift, List<SceneObject> container, ICheck<SceneObject> check)
|
|
: base(phaseShift, new SearcherContainerResult<SceneObject>(container), check) { }
|
|
|
|
public SceneObjectListSearcher(WorldObject searcher, List<SceneObject> container, ICheck<SceneObject> check)
|
|
: this(searcher.GetPhaseShift(), container, check) { }
|
|
}
|
|
|
|
public class SceneObjectWorker : WorldObjectWorkerBase<SceneObject>
|
|
{
|
|
public SceneObjectWorker(PhaseShift phaseShift, IDoWork<SceneObject> work)
|
|
: base(phaseShift, work, GridMapTypeMask.SceneObject) { }
|
|
|
|
public SceneObjectWorker(WorldObject searcher, IDoWork<SceneObject> work)
|
|
: this(searcher.GetPhaseShift(), work) { }
|
|
}
|
|
|
|
// Conversation searchers
|
|
|
|
public class ConversationSearcherBase : WorldObjectSearcherBase<Conversation>
|
|
{
|
|
public ConversationSearcherBase(PhaseShift phaseShift, IResultInserter<Conversation> result, ICheck<Conversation> check)
|
|
: base(phaseShift, result, check, GridMapTypeMask.Conversation) { }
|
|
}
|
|
|
|
public class ConversationSearcher : ConversationSearcherBase
|
|
{
|
|
public ConversationSearcher(PhaseShift phaseShift, ICheck<Conversation> check)
|
|
: base(phaseShift, new SearcherFirstObjectResult<Conversation>(), check) { }
|
|
|
|
public ConversationSearcher(WorldObject searcher, ICheck<Conversation> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class ConversationLastSearcher : ConversationSearcherBase
|
|
{
|
|
public ConversationLastSearcher(PhaseShift phaseShift, ICheck<Conversation> check)
|
|
: base(phaseShift, new SearcherLastObjectResult<Conversation>(), check) { }
|
|
|
|
public ConversationLastSearcher(WorldObject searcher, ICheck<Conversation> check)
|
|
: this(searcher.GetPhaseShift(), check) { }
|
|
}
|
|
|
|
public class ConversationListSearcher : ConversationSearcherBase
|
|
{
|
|
public ConversationListSearcher(PhaseShift phaseShift, List<Conversation> container, ICheck<Conversation> check)
|
|
: base(phaseShift, new SearcherContainerResult<Conversation>(container), check) { }
|
|
|
|
public ConversationListSearcher(WorldObject searcher, List<Conversation> container, ICheck<Conversation> check)
|
|
: this(searcher.GetPhaseShift(), container, check) { }
|
|
}
|
|
|
|
public class ConversationWorker : WorldObjectWorkerBase<Conversation>
|
|
{
|
|
public ConversationWorker(PhaseShift phaseShift, IDoWork<Conversation> work)
|
|
: base(phaseShift, work, GridMapTypeMask.Conversation) { }
|
|
|
|
public ConversationWorker(WorldObject searcher, IDoWork<Conversation> work)
|
|
: this(searcher.GetPhaseShift(), work) { }
|
|
}
|
|
|
|
// CHECKS && DO classes
|
|
|
|
// CHECK modifiers
|
|
public class InRangeCheckCustomizer
|
|
{
|
|
WorldObject i_obj;
|
|
float i_range;
|
|
|
|
public InRangeCheckCustomizer(WorldObject obj, float range)
|
|
{
|
|
i_obj = obj;
|
|
i_range = range;
|
|
}
|
|
|
|
public virtual bool Test(WorldObject obj)
|
|
{
|
|
return i_obj.IsWithinDist(obj, i_range);
|
|
}
|
|
|
|
public virtual void Update(WorldObject obj) { }
|
|
}
|
|
|
|
public class NearestCheckCustomizer : InRangeCheckCustomizer
|
|
{
|
|
WorldObject i_obj;
|
|
float i_range;
|
|
|
|
public NearestCheckCustomizer(WorldObject obj, float range) : base(obj, range)
|
|
{
|
|
i_obj = obj;
|
|
i_range = range;
|
|
}
|
|
|
|
public override bool Test(WorldObject obj)
|
|
{
|
|
return i_obj.IsWithinDist(obj, i_range);
|
|
}
|
|
|
|
public override void Update(WorldObject obj)
|
|
{
|
|
i_range = i_obj.GetDistance(obj);
|
|
}
|
|
}
|
|
|
|
// WorldObject check classes
|
|
|
|
public class AnyDeadUnitObjectInRangeCheck<T>(WorldObject searchObj, float range) : ICheck<T> where T : WorldObject
|
|
{
|
|
public virtual bool Invoke(T obj)
|
|
{
|
|
Player player = obj.ToPlayer();
|
|
if (player != null)
|
|
return !player.IsAlive() && !player.HasAuraType(AuraType.Ghost) && searchObj.IsWithinDistInMap(player, range);
|
|
|
|
Creature creature = obj.ToCreature();
|
|
if (creature != null)
|
|
return !creature.IsAlive() && searchObj.IsWithinDistInMap(creature, range);
|
|
|
|
Corpse corpse = obj.ToCorpse();
|
|
if (corpse != null)
|
|
return corpse.GetCorpseType() != CorpseType.Bones && searchObj.IsWithinDistInMap(corpse, range);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class AnyDeadUnitSpellTargetInRangeCheck<T> : AnyDeadUnitObjectInRangeCheck<T> where T : WorldObject
|
|
{
|
|
WorldObjectSpellTargetCheck i_check;
|
|
|
|
public AnyDeadUnitSpellTargetInRangeCheck(WorldObject searchObj, float range, SpellInfo spellInfo, SpellTargetCheckTypes check, SpellTargetObjectTypes objectType) : base(searchObj, range)
|
|
{
|
|
i_check = new WorldObjectSpellTargetCheck(searchObj, searchObj, spellInfo, check, null, objectType);
|
|
}
|
|
|
|
public override bool Invoke(T obj)
|
|
{
|
|
return base.Invoke(obj) && i_check.Invoke(obj);
|
|
}
|
|
}
|
|
|
|
// WorldObject do classes
|
|
|
|
public class RespawnDo
|
|
{
|
|
public void Invoke(Creature obj)
|
|
{
|
|
obj.Respawn();
|
|
}
|
|
|
|
public void Invoke(GameObject obj)
|
|
{
|
|
obj.Respawn();
|
|
}
|
|
|
|
public static implicit operator IDoWork<Creature>(RespawnDo obj) => obj.Invoke;
|
|
public static implicit operator IDoWork<GameObject>(RespawnDo obj) => obj.Invoke;
|
|
}
|
|
|
|
// GameObject checks
|
|
|
|
class GameObjectFocusCheck(WorldObject caster, uint focusId) : ICheck<GameObject>
|
|
{
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (go.GetGoInfo().GetSpellFocusType() != focusId)
|
|
return false;
|
|
|
|
if (!go.IsSpawned())
|
|
return false;
|
|
|
|
float dist = go.GetGoInfo().GetSpellFocusRadius();
|
|
return go.IsWithinDist(caster, dist);
|
|
}
|
|
}
|
|
|
|
// Find the nearest Fishing hole and return true only if source object is in range of hole
|
|
class NearestGameObjectFishingHole(WorldObject obj, float range) : ICheck<GameObject>
|
|
{
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (go.GetGoInfo().type == GameObjectTypes.FishingHole && go.IsSpawned() && obj.IsWithinDist(go, range) && obj.IsWithinDist(go, go.GetGoInfo().FishingHole.radius))
|
|
{
|
|
range = obj.GetDistance(go);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class NearestGameObjectCheck(WorldObject obj) : ICheck<GameObject>
|
|
{
|
|
float i_range = 999f;
|
|
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (obj.IsWithinDist(go, i_range))
|
|
{
|
|
i_range = obj.GetDistance(go); // use found GO range as new range limit for next check
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO)
|
|
class NearestGameObjectEntryInObjectRangeCheck(WorldObject obj, uint entry, float range, bool spawnedOnly = true) : ICheck<GameObject>
|
|
{
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if ((!spawnedOnly || go.IsSpawned()) && go.GetEntry() == entry && go.GetGUID() != obj.GetGUID() && obj.IsWithinDist(go, range))
|
|
{
|
|
range = obj.GetDistance(go); // use found GO range as new range limit for next check
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest unspawned GO)
|
|
class NearestUnspawnedGameObjectEntryInObjectRangeCheck(WorldObject obj, uint entry, float range) : ICheck<GameObject>
|
|
{
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (!go.IsSpawned() && go.GetEntry() == entry && go.GetGUID() != obj.GetGUID() && obj.IsWithinDist(go, range))
|
|
{
|
|
range = obj.GetDistance(go); // use found GO range as new range limit for next check
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO with a certain type)
|
|
class NearestGameObjectTypeInObjectRangeCheck(WorldObject obj, GameObjectTypes type, float range) : ICheck<GameObject>
|
|
{
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (go.GetGoType() == type && obj.IsWithinDist(go, range))
|
|
{
|
|
range = obj.GetDistance(go); // use found GO range as new range limit for next check
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Unit checks
|
|
|
|
public class MostHPMissingInRange<T>(Unit obj, float range, uint hp) : ICheck<T> where T : Unit
|
|
{
|
|
public bool Invoke(T u)
|
|
{
|
|
if (u.IsAlive() && u.IsInCombat() && !obj.IsHostileTo(u) && obj.IsWithinDist(u, range) && u.GetMaxHealth() - u.GetHealth() > hp)
|
|
{
|
|
hp = (uint)(u.GetMaxHealth() - u.GetHealth());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class MostHPPercentMissingInRange(Unit obj, float range, uint minHpPct, uint maxHpPct) : ICheck<Unit>
|
|
{
|
|
float _hpPct = 101.0f;
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (u.IsAlive() && u.IsInCombat() && !obj.IsHostileTo(u) && obj.IsWithinDist(u, range) && minHpPct <= u.GetHealthPct() && u.GetHealthPct() <= maxHpPct && u.GetHealthPct() < _hpPct)
|
|
{
|
|
_hpPct = u.GetHealthPct();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class FriendlyBelowHpPctEntryInRange(Unit obj, uint entry, float range, byte pct, bool excludeSelf) : ICheck<Unit>
|
|
{
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (excludeSelf && obj.GetGUID() == u.GetGUID())
|
|
return false;
|
|
|
|
if (u.GetEntry() == entry && u.IsAlive() && u.IsInCombat() && !obj.IsHostileTo(u) && obj.IsWithinDist(u, range) && u.HealthBelowPct(pct))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class FriendlyCCedInRange(Unit obj, float range) : ICheck<Creature>
|
|
{
|
|
public bool Invoke(Creature u)
|
|
{
|
|
if (u.IsAlive() && u.IsInCombat() && !obj.IsHostileTo(u) && obj.IsWithinDist(u, range) &&
|
|
(u.IsFeared() || u.IsCharmed() || u.HasRootAura() || u.HasUnitState(UnitState.Stunned) || u.HasUnitState(UnitState.Confused)))
|
|
return true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class FriendlyMissingBuffInRange(Unit obj, float range, uint spellid) : ICheck<Creature>
|
|
{
|
|
public bool Invoke(Creature u)
|
|
{
|
|
if (u.IsAlive() && u.IsInCombat() && !obj.IsHostileTo(u) && obj.IsWithinDist(u, range) && !u.HasAura(spellid))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class AnyUnfriendlyUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range) : ICheck<Unit>
|
|
{
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (u.IsAlive() && obj.IsWithinDist(u, range) && !funit.IsFriendlyTo(u))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class NearestAttackableNoTotemUnitInObjectRangeCheck(WorldObject obj, float range) : ICheck<Unit>
|
|
{
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (!u.IsAlive())
|
|
return false;
|
|
|
|
if (u.GetCreatureType() == CreatureType.NonCombatPet)
|
|
return false;
|
|
|
|
if (u.IsTypeId(TypeId.Unit) && u.IsTotem())
|
|
return false;
|
|
|
|
if (!u.IsTargetableForAttack(false))
|
|
return false;
|
|
|
|
if (!obj.IsWithinDist(u, range) || obj.IsValidAttackTarget(u))
|
|
return false;
|
|
|
|
range = obj.GetDistance(u);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public class AnyFriendlyUnitInObjectRangeCheck : ICheck<Unit>
|
|
{
|
|
WorldObject i_obj;
|
|
Unit i_funit;
|
|
float i_range;
|
|
bool i_playerOnly;
|
|
bool i_incOwnRadius;
|
|
bool i_incTargetRadius;
|
|
|
|
public AnyFriendlyUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true)
|
|
{
|
|
i_obj = obj;
|
|
i_funit = funit;
|
|
i_range = range;
|
|
i_playerOnly = playerOnly;
|
|
i_incOwnRadius = incOwnRadius;
|
|
i_incTargetRadius = incTargetRadius;
|
|
}
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (!u.IsAlive())
|
|
return false;
|
|
|
|
float searchRadius = i_range;
|
|
if (i_incOwnRadius)
|
|
searchRadius += i_obj.GetCombatReach();
|
|
if (i_incTargetRadius)
|
|
searchRadius += u.GetCombatReach();
|
|
|
|
if (!u.IsInMap(i_obj) || !u.InSamePhase(i_obj) || !u.IsWithinVerticalCylinder(i_obj, searchRadius, searchRadius, true))
|
|
return false;
|
|
|
|
if (!i_funit.IsFriendlyTo(u))
|
|
return false;
|
|
|
|
return !i_playerOnly || u.IsPlayer();
|
|
}
|
|
}
|
|
|
|
public class AnyGroupedUnitInObjectRangeCheck : ICheck<Unit>
|
|
{
|
|
WorldObject _source;
|
|
Unit _refUnit;
|
|
float _range;
|
|
bool _raid;
|
|
bool _playerOnly;
|
|
bool i_incOwnRadius;
|
|
bool i_incTargetRadius;
|
|
|
|
public AnyGroupedUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range, bool raid, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true)
|
|
{
|
|
_source = obj;
|
|
_refUnit = funit;
|
|
_range = range;
|
|
_raid = raid;
|
|
_playerOnly = playerOnly;
|
|
i_incOwnRadius = incOwnRadius;
|
|
i_incTargetRadius = incTargetRadius;
|
|
}
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (_playerOnly && !u.IsPlayer())
|
|
return false;
|
|
|
|
if (_raid)
|
|
{
|
|
if (!_refUnit.IsInRaidWith(u))
|
|
return false;
|
|
}
|
|
else if (!_refUnit.IsInPartyWith(u))
|
|
return false;
|
|
|
|
if (_refUnit.IsHostileTo(u))
|
|
return false;
|
|
|
|
if (!u.IsAlive())
|
|
return false;
|
|
|
|
float searchRadius = _range;
|
|
if (i_incOwnRadius)
|
|
searchRadius += _source.GetCombatReach();
|
|
if (i_incTargetRadius)
|
|
searchRadius += u.GetCombatReach();
|
|
|
|
return u.IsInMap(_source) && u.InSamePhase(_source) && u.IsWithinVerticalCylinder(_source, searchRadius, searchRadius, true);
|
|
}
|
|
}
|
|
|
|
public class AnyUnitInObjectRangeCheck : ICheck<Unit>
|
|
{
|
|
WorldObject i_obj;
|
|
float i_range;
|
|
bool i_check3D;
|
|
bool i_reqAlive;
|
|
|
|
public AnyUnitInObjectRangeCheck(WorldObject obj, float range, bool check3D = true, bool reqAlive = true)
|
|
{
|
|
i_obj = obj;
|
|
i_range = range;
|
|
i_check3D = check3D;
|
|
i_reqAlive = reqAlive;
|
|
}
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (i_reqAlive && !u.IsAlive())
|
|
return false;
|
|
|
|
if (!i_obj.IsWithinDist(u, i_range, i_check3D))
|
|
return false;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Success at unit in range, range update for next check (this can be use with UnitLastSearcher to find nearest unit)
|
|
public class NearestAttackableUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range) : ICheck<Unit>
|
|
{
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (u.IsTargetableForAttack() && obj.IsWithinDist(u, range) &&
|
|
(funit.IsInCombatWith(u) || funit.IsHostileTo(u)) && obj.CanSeeOrDetect(u))
|
|
{
|
|
range = obj.GetDistance(u); // use found unit range as new range limit for next check
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class AnyAoETargetUnitInObjectRangeCheck : ICheck<Unit>
|
|
{
|
|
WorldObject i_obj;
|
|
Unit i_funit;
|
|
SpellInfo _spellInfo;
|
|
float i_range;
|
|
bool i_incOwnRadius;
|
|
bool i_incTargetRadius;
|
|
|
|
public AnyAoETargetUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range, SpellInfo spellInfo = null, bool incOwnRadius = true, bool incTargetRadius = true)
|
|
{
|
|
i_obj = obj;
|
|
i_funit = funit;
|
|
_spellInfo = spellInfo;
|
|
i_range = range;
|
|
i_incOwnRadius = incOwnRadius;
|
|
i_incTargetRadius = incTargetRadius;
|
|
}
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
// Check contains checks for: live, uninteractible, non-attackable flags, flight check and GM check, ignore totems
|
|
if (u.IsTypeId(TypeId.Unit) && u.IsTotem())
|
|
return false;
|
|
|
|
if (_spellInfo != null)
|
|
{
|
|
if (!u.IsPlayer())
|
|
{
|
|
if (_spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer))
|
|
return false;
|
|
|
|
if (_spellInfo.HasAttribute(SpellAttr5.NotOnPlayerControlledNpc) && u.IsControlledByPlayer())
|
|
return false;
|
|
}
|
|
else if (_spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
|
return false;
|
|
}
|
|
|
|
if (!i_funit.IsValidAttackTarget(u, _spellInfo))
|
|
return false;
|
|
|
|
float searchRadius = i_range;
|
|
if (i_incOwnRadius)
|
|
searchRadius += i_obj.GetCombatReach();
|
|
if (i_incTargetRadius)
|
|
searchRadius += u.GetCombatReach();
|
|
|
|
return u.IsInMap(i_obj) && u.InSamePhase(i_obj) && u.IsWithinVerticalCylinder(i_obj, searchRadius, searchRadius, true);
|
|
}
|
|
}
|
|
|
|
public class CallOfHelpCreatureInRangeDo(Unit funit, Unit enemy, float range)
|
|
{
|
|
public void Invoke(Creature u)
|
|
{
|
|
if (u == funit)
|
|
return;
|
|
|
|
if (!u.CanAssistTo(funit, enemy, false))
|
|
return;
|
|
|
|
// too far
|
|
// Don't use combat reach distance, range must be an absolute value, otherwise the chain aggro range will be too big
|
|
if (!u.IsWithinDist(funit, range, true, false, false))
|
|
return;
|
|
|
|
// only if see assisted creature's enemy
|
|
if (!u.IsWithinLOSInMap(enemy))
|
|
return;
|
|
|
|
u.EngageWithTarget(enemy);
|
|
}
|
|
|
|
public static implicit operator IDoWork<Creature>(CallOfHelpCreatureInRangeDo obj) => obj.Invoke;
|
|
}
|
|
|
|
public class AnyDeadUnitCheck : ICheck<Unit>
|
|
{
|
|
public bool Invoke(Unit u) { return !u.IsAlive(); }
|
|
}
|
|
|
|
// Creature checks
|
|
|
|
public class NearestHostileUnitCheck : ICheck<Unit>
|
|
{
|
|
Creature me;
|
|
float m_range;
|
|
bool i_playerOnly;
|
|
|
|
public NearestHostileUnitCheck(Creature creature, float dist = 0, bool playerOnly = false)
|
|
{
|
|
me = creature;
|
|
i_playerOnly = playerOnly;
|
|
|
|
m_range = (dist == 0 ? 9999 : dist);
|
|
}
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (!me.IsWithinDist(u, m_range))
|
|
return false;
|
|
|
|
if (!me.IsValidAttackTarget(u))
|
|
return false;
|
|
|
|
if (i_playerOnly && !u.IsPlayer())
|
|
return false;
|
|
|
|
m_range = me.GetDistance(u); // use found unit range as new range limit for next check
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class NearestHostileUnitInAttackDistanceCheck : ICheck<Unit>
|
|
{
|
|
Creature me;
|
|
float m_range;
|
|
bool m_force;
|
|
|
|
public NearestHostileUnitInAttackDistanceCheck(Creature creature, float dist = 0)
|
|
{
|
|
me = creature;
|
|
m_range = (dist == 0 ? 9999 : dist);
|
|
m_force = (dist != 0);
|
|
}
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (!me.IsWithinDist(u, m_range))
|
|
return false;
|
|
|
|
if (!me.CanSeeOrDetect(u))
|
|
return false;
|
|
|
|
if (m_force)
|
|
{
|
|
if (!me.IsValidAttackTarget(u))
|
|
return false;
|
|
}
|
|
else if (!me.CanStartAttack(u, false))
|
|
return false;
|
|
|
|
m_range = me.GetDistance(u); // use found unit range as new range limit for next check
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class NearestHostileUnitInAggroRangeCheck : ICheck<Unit>
|
|
{
|
|
Creature _me;
|
|
bool _useLOS;
|
|
bool _ignoreCivilians;
|
|
|
|
public NearestHostileUnitInAggroRangeCheck(Creature creature, bool useLOS = false, bool ignoreCivilians = false)
|
|
{
|
|
_me = creature;
|
|
_useLOS = useLOS;
|
|
_ignoreCivilians = ignoreCivilians;
|
|
}
|
|
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (!u.IsHostileTo(_me))
|
|
return false;
|
|
|
|
if (!u.IsWithinDist(_me, _me.GetAggroRange(u)))
|
|
return false;
|
|
|
|
if (!_me.IsValidAttackTarget(u))
|
|
return false;
|
|
|
|
if (_useLOS && !u.IsWithinLOSInMap(_me))
|
|
return false;
|
|
|
|
// pets in aggressive do not attack civilians
|
|
if (_ignoreCivilians)
|
|
{
|
|
Creature creature = u.ToCreature();
|
|
if (creature != null && creature.IsCivilian())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class AnyAssistCreatureInRangeCheck(Unit funit, Unit enemy, float range) : ICheck<Creature>
|
|
{
|
|
public bool Invoke(Creature u)
|
|
{
|
|
if (u == funit)
|
|
return false;
|
|
|
|
if (!u.CanAssistTo(funit, enemy))
|
|
return false;
|
|
|
|
// too far
|
|
// Don't use combat reach distance, range must be an absolute value, otherwise the chain aggro range will be too big
|
|
if (!funit.IsWithinDist(u, range, true, false, false))
|
|
return false;
|
|
|
|
// only if see assisted creature
|
|
if (!funit.IsWithinLOSInMap(u))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class NearestAssistCreatureInCreatureRangeCheck(Creature obj, Unit enemy, float range) : ICheck<Creature>
|
|
{
|
|
public bool Invoke(Creature u)
|
|
{
|
|
if (u == obj)
|
|
return false;
|
|
|
|
if (!u.CanAssistTo(obj, enemy))
|
|
return false;
|
|
|
|
// Don't use combat reach distance, range must be an absolute value, otherwise the chain aggro range will be too big
|
|
if (!obj.IsWithinDist(u, range, true, false, false))
|
|
return false;
|
|
|
|
if (!obj.IsWithinLOSInMap(u))
|
|
return false;
|
|
|
|
range = obj.GetDistance(u); // use found unit range as new range limit for next check
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Success at unit in range, range update for next check (this can be use with CreatureLastSearcher to find nearest creature)
|
|
class NearestCreatureEntryWithLiveStateInObjectRangeCheck : ICheck<Creature>
|
|
{
|
|
WorldObject i_obj;
|
|
uint i_entry;
|
|
bool i_alive;
|
|
float i_range;
|
|
|
|
public NearestCreatureEntryWithLiveStateInObjectRangeCheck(WorldObject obj, uint entry, bool alive, float range)
|
|
{
|
|
i_obj = obj;
|
|
i_entry = entry;
|
|
i_alive = alive;
|
|
i_range = range;
|
|
}
|
|
|
|
public bool Invoke(Creature u)
|
|
{
|
|
if (u.GetDeathState() != DeathState.Dead && u.GetEntry() == i_entry && u.IsAlive() == i_alive && u.GetGUID() != i_obj.GetGUID() && i_obj.IsWithinDist(u, i_range) && u.CheckPrivateObjectOwnerVisibility(i_obj))
|
|
{
|
|
i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class CreatureWithOptionsInObjectRangeCheck<T> : ICheck<Creature> where T : InRangeCheckCustomizer
|
|
{
|
|
WorldObject i_obj;
|
|
FindCreatureOptions i_args;
|
|
T i_customizer;
|
|
|
|
public CreatureWithOptionsInObjectRangeCheck(WorldObject obj, T customizer, FindCreatureOptions args)
|
|
{
|
|
i_obj = obj;
|
|
i_args = args;
|
|
i_customizer = customizer;
|
|
}
|
|
|
|
public bool Invoke(Creature u)
|
|
{
|
|
if (u.GetDeathState() == DeathState.Dead) // Despawned
|
|
return false;
|
|
|
|
if (u.GetGUID() == i_obj.GetGUID())
|
|
return false;
|
|
|
|
if (!i_customizer.Test(u))
|
|
return false;
|
|
|
|
if (i_args.CreatureId.HasValue && u.GetEntry() != i_args.CreatureId)
|
|
return false;
|
|
|
|
if (i_args.StringId != null && !u.HasStringId(i_args.StringId))
|
|
return false;
|
|
|
|
if (i_args.IsAlive.HasValue)
|
|
{
|
|
switch (i_args.IsAlive.Value)
|
|
{
|
|
case FindCreatureAliveState.Alive:
|
|
{
|
|
if (!u.IsAlive())
|
|
return false;
|
|
break;
|
|
}
|
|
case FindCreatureAliveState.Dead:
|
|
{
|
|
if (u.IsAlive())
|
|
return false;
|
|
break;
|
|
}
|
|
case FindCreatureAliveState.EffectivelyAlive:
|
|
{
|
|
if (!u.IsAlive() || u.HasUnitFlag2(UnitFlags2.FeignDeath))
|
|
return false;
|
|
break;
|
|
}
|
|
case FindCreatureAliveState.EffectivelyDead:
|
|
{
|
|
if (u.IsAlive() && !u.HasUnitFlag2(UnitFlags2.FeignDeath))
|
|
return false;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i_args.IsSummon.HasValue && u.IsSummon() != i_args.IsSummon)
|
|
return false;
|
|
|
|
if (i_args.IsInCombat.HasValue && u.IsInCombat() != i_args.IsInCombat)
|
|
return false;
|
|
|
|
if ((i_args.OwnerGuid.HasValue && u.GetOwnerGUID() != i_args.OwnerGuid)
|
|
|| (i_args.CharmerGuid.HasValue && u.GetCharmerGUID() != i_args.CharmerGuid)
|
|
|| (i_args.CreatorGuid.HasValue && u.GetCreatorGUID() != i_args.CreatorGuid)
|
|
|| (i_args.DemonCreatorGuid.HasValue && u.GetDemonCreatorGUID() != i_args.DemonCreatorGuid)
|
|
|| (i_args.PrivateObjectOwnerGuid.HasValue && u.GetPrivateObjectOwner() != i_args.PrivateObjectOwnerGuid))
|
|
return false;
|
|
|
|
if (i_args.IgnorePrivateObjects && u.IsPrivateObject())
|
|
return false;
|
|
|
|
if (i_args.IgnoreNotOwnedPrivateObjects && !u.CheckPrivateObjectOwnerVisibility(i_obj))
|
|
return false;
|
|
|
|
if (i_args.AuraSpellId.HasValue && !u.HasAura((uint)i_args.AuraSpellId))
|
|
return false;
|
|
|
|
i_customizer.Update(u);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class GameObjectWithOptionsInObjectRangeCheck<T> : ICheck<GameObject> where T : InRangeCheckCustomizer
|
|
{
|
|
WorldObject _obj;
|
|
FindGameObjectOptions _args;
|
|
T _customizer;
|
|
|
|
public GameObjectWithOptionsInObjectRangeCheck(WorldObject obj, T customizer, FindGameObjectOptions args)
|
|
{
|
|
_obj = obj;
|
|
_args = args;
|
|
_customizer = customizer;
|
|
}
|
|
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (_args.IsSpawned.HasValue && _args.IsSpawned != go.IsSpawned()) // Despawned
|
|
return false;
|
|
|
|
if (go.GetGUID() == _obj.GetGUID())
|
|
return false;
|
|
|
|
if (!_customizer.Test(go))
|
|
return false;
|
|
|
|
if (_args.GameObjectId.HasValue && go.GetEntry() != _args.GameObjectId)
|
|
return false;
|
|
|
|
if (!_args.StringId.IsEmpty() && !go.HasStringId(_args.StringId))
|
|
return false;
|
|
|
|
if (_args.IsSummon.HasValue && (go.GetSpawnId() == 0) != _args.IsSummon)
|
|
return false;
|
|
|
|
if ((_args.OwnerGuid.HasValue && go.GetOwnerGUID() != _args.OwnerGuid)
|
|
|| (_args.PrivateObjectOwnerGuid.HasValue && go.GetPrivateObjectOwner() != _args.PrivateObjectOwnerGuid))
|
|
return false;
|
|
|
|
if (_args.IgnorePrivateObjects && go.IsPrivateObject())
|
|
return false;
|
|
|
|
if (_args.IgnoreNotOwnedPrivateObjects && !go.CheckPrivateObjectOwnerVisibility(_obj))
|
|
return false;
|
|
|
|
if (_args.GameObjectType.HasValue && go.GetGoType() != _args.GameObjectType)
|
|
return false;
|
|
|
|
_customizer.Update(go);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class AnyPlayerInPositionRangeCheck(Position pos, float range, bool reqAlive = true) : ICheck<Player>
|
|
{
|
|
public bool Invoke(Player u)
|
|
{
|
|
if (reqAlive && !u.IsAlive())
|
|
return false;
|
|
|
|
if (!u.IsWithinDist3d(pos, range))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class NearestPlayerInObjectRangeCheck(WorldObject obj, float range) : ICheck<Player>
|
|
{
|
|
public bool Invoke(Player pl)
|
|
{
|
|
if (pl.IsAlive() && obj.IsWithinDist(pl, range))
|
|
{
|
|
range = obj.GetDistance(pl);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class AllFriendlyCreaturesInGrid(Unit obj) : ICheck<Unit>
|
|
{
|
|
public bool Invoke(Unit u)
|
|
{
|
|
if (u.IsAlive() && u.IsVisible() && u.IsFriendlyTo(obj))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class AllGameObjectsWithEntryInRange(WorldObject obj, uint entry, float maxRange) : ICheck<GameObject>
|
|
{
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (entry == 0 || go.GetEntry() == entry && obj.IsWithinDist(go, maxRange, false))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class AllCreaturesOfEntryInRange(WorldObject obj, uint entry, float maxRange = 0f) : ICheck<Creature>
|
|
{
|
|
public bool Invoke(Creature creature)
|
|
{
|
|
if (entry != 0)
|
|
{
|
|
if (creature.GetEntry() != entry)
|
|
return false;
|
|
}
|
|
|
|
if (maxRange != 0f)
|
|
{
|
|
if (maxRange > 0.0f && !obj.IsWithinDist(creature, maxRange, false))
|
|
return false;
|
|
if (maxRange < 0.0f && obj.IsWithinDist(creature, maxRange, false))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class PlayerAtMinimumRangeAway(Unit unit, float fMinRange) : ICheck<Player>
|
|
{
|
|
public bool Invoke(Player player)
|
|
{
|
|
//No threat list check, must be done explicit if expected to be in combat with creature
|
|
if (!player.IsGameMaster() && player.IsAlive() && !unit.IsWithinDist(player, fMinRange, false))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class GameObjectInRangeCheck : ICheck<GameObject>
|
|
{
|
|
float x, y, z, range;
|
|
uint entry;
|
|
|
|
public GameObjectInRangeCheck(float _x, float _y, float _z, float _range, uint _entry = 0)
|
|
{
|
|
x = _x;
|
|
y = _y;
|
|
z = _z;
|
|
range = _range;
|
|
entry = _entry;
|
|
}
|
|
|
|
public bool Invoke(GameObject go)
|
|
{
|
|
if (entry == 0 || (go.GetGoInfo() != null && go.GetGoInfo().entry == entry))
|
|
return go.IsInRange(x, y, z, range);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class AllWorldObjectsInRange(WorldObject obj, float maxRange) : ICheck<WorldObject>
|
|
{
|
|
public bool Invoke(WorldObject go)
|
|
{
|
|
return obj.IsWithinDist(go, maxRange, false) && obj.InSamePhase(go);
|
|
}
|
|
}
|
|
|
|
public class ObjectTypeIdCheck(TypeId typeId, bool equals) : ICheck<WorldObject>
|
|
{
|
|
public bool Invoke(WorldObject obj)
|
|
{
|
|
return (obj.GetTypeId() == typeId) == equals;
|
|
}
|
|
}
|
|
|
|
public class ObjectGUIDCheck(ObjectGuid Guid) : ICheck<WorldObject>
|
|
{
|
|
public bool Invoke(WorldObject obj)
|
|
{
|
|
return obj.GetGUID() == Guid;
|
|
}
|
|
|
|
//public static implicit operator Predicate<WorldObject>(ObjectGUIDCheck check) => check.Invoke;
|
|
}
|
|
|
|
public class HeightDifferenceCheck(WorldObject go, float diff, bool reverse) : ICheck<WorldObject>
|
|
{
|
|
public bool Invoke(WorldObject unit)
|
|
{
|
|
return (unit.GetPositionZ() - go.GetPositionZ() > diff) != reverse;
|
|
}
|
|
}
|
|
|
|
public class UnitAuraCheck(bool present, uint spellId, ObjectGuid casterGUID = default) : ICheck<WorldObject>
|
|
{
|
|
public bool Invoke(WorldObject obj)
|
|
{
|
|
return obj.ToUnit() != null && obj.ToUnit().HasAura(spellId, casterGUID) == present;
|
|
}
|
|
|
|
//public static implicit operator Predicate<WorldObject>(UnitAuraCheck unit) => unit.Invoke;
|
|
}
|
|
|
|
class ObjectEntryAndPrivateOwnerIfExistsCheck(ObjectGuid ownerGUID, uint entry) : ICheck<WorldObject>
|
|
{
|
|
public bool Invoke(WorldObject obj)
|
|
{
|
|
return obj.GetEntry() == entry && (!obj.IsPrivateObject() || obj.GetPrivateObjectOwner() == ownerGUID);
|
|
}
|
|
}
|
|
|
|
class NearestAreaTriggerEntryInObjectRangeCheck : ICheck<AreaTrigger>
|
|
{
|
|
WorldObject i_obj;
|
|
uint i_entry;
|
|
float i_range;
|
|
bool i_spawnedOnly;
|
|
|
|
public NearestAreaTriggerEntryInObjectRangeCheck(WorldObject obj, uint entry, float range, bool spawnedOnly = false)
|
|
{
|
|
i_obj = obj;
|
|
i_entry = entry;
|
|
i_range = range;
|
|
i_spawnedOnly = spawnedOnly;
|
|
}
|
|
|
|
public bool Invoke(AreaTrigger at)
|
|
{
|
|
if ((!i_spawnedOnly || at.IsStaticSpawn()) && at.GetEntry() == i_entry && at.GetGUID() != i_obj.GetGUID() && i_obj.IsWithinDist(at, i_range))
|
|
{
|
|
i_range = i_obj.GetDistance(at);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class LocalizedDo(MessageBuilder localizer)
|
|
{
|
|
IDoWork<Player>[] _localizedCache = new IDoWork<Player>[(int)Locale.Total]; // 0 = default, i => i-1 locale index
|
|
|
|
public void Invoke(Player player)
|
|
{
|
|
Locale loc_idx = player.GetSession().GetSessionDbLocaleIndex();
|
|
int cache_idx = (int)loc_idx + 1;
|
|
IDoWork<Player> action;
|
|
|
|
// create if not cached yet
|
|
if (_localizedCache.Length < cache_idx + 1 || _localizedCache[cache_idx] == null)
|
|
{
|
|
if (_localizedCache.Length < cache_idx + 1)
|
|
Array.Resize(ref _localizedCache, cache_idx + 1);
|
|
|
|
action = localizer.Invoke(loc_idx);
|
|
_localizedCache[cache_idx] = action;
|
|
}
|
|
else
|
|
action = _localizedCache[cache_idx];
|
|
|
|
action.Invoke(player);
|
|
}
|
|
|
|
public static implicit operator IDoWork<Player>(LocalizedDo obj) => obj.Invoke;
|
|
}
|
|
} |