/* * Copyright (C) 2012-2017 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 . */ using Framework.Constants; using Game.Chat; using Game.Entities; using Game.Network; using Game.Network.Packets; using Game.Spells; using System; using System.Collections.Generic; using System.Linq; namespace Game.Maps { public abstract class Notifier { public virtual void Visit(ICollection objs) { } public virtual void Visit(ICollection objs) { } public virtual void Visit(ICollection objs) { } public virtual void Visit(ICollection objs) { } public virtual void Visit(ICollection objs) { } public virtual void Visit(ICollection objs) { } public virtual void Visit(ICollection objs) { } public virtual void Visit(ICollection 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, false, true)) c.GetAI().MoveInLineOfSight_Safe(u); else { if (u.IsTypeId(TypeId.Player) && u.HasStealthAura() && c.IsAIEnabled && c.CanSeeOrDetect(u, false, true, true)) c.GetAI().TriggerAlert(u); } } } } public class Visitor { public Visitor(Notifier notifier, GridMapTypeMask mask) { _notifier = notifier; _mask = mask; } public void Visit(ICollection collection) { _notifier.Visit(collection); } public void Visit(ICollection creatures) { _notifier.Visit(creatures); } public void Visit(ICollection areatriggers) { _notifier.Visit(areatriggers); } public void Visit(ICollection conversations) { _notifier.Visit(conversations); } public void Visit(ICollection gameobjects) { _notifier.Visit(gameobjects); } public void Visit(ICollection dynamicobjects) { _notifier.Visit(dynamicobjects); } public void Visit(ICollection corpses) { _notifier.Visit(corpses); } public void Visit(ICollection players) { _notifier.Visit(players); } public void Visit(Dictionary dict) { Visit(dict.Values); } Notifier _notifier; internal GridMapTypeMask _mask; } public class VisibleNotifier : Notifier { public VisibleNotifier(Player pl) { i_player = pl; i_data = new UpdateData(pl.GetMapId()); vis_guids = new List(pl.m_clientGUIDs); i_visibleNow = new List(); } public override void Visit(ICollection objs) { foreach (var obj in objs) { 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(); if (transport) { foreach (var obj in transport.GetPassengers()) { if (vis_guids.Contains(obj.GetGUID())) { vis_guids.Remove(obj.GetGUID()); switch (obj.GetTypeId()) { case TypeId.GameObject: i_player.UpdateVisibilityOf(obj.ToGameObject(), i_data, i_visibleNow); break; case TypeId.Player: i_player.UpdateVisibilityOf(obj.ToPlayer(), i_data, i_visibleNow); if (!obj.isNeedNotify(NotifyFlags.VisibilityChanged)) obj.ToPlayer().UpdateVisibilityOf(i_player); break; case TypeId.Unit: i_player.UpdateVisibilityOf(obj.ToCreature(), i_data, i_visibleNow); break; case TypeId.DynamicObject: i_player.UpdateVisibilityOf(obj.ToDynamicObject(), i_data, i_visibleNow); break; default: break; } } } } foreach (var guid in vis_guids) { i_player.m_clientGUIDs.Remove(guid); i_data.AddOutOfRangeGUID(guid); if (guid.IsPlayer()) { Player pl = Global.ObjAccessor.FindPlayer(guid); if (pl != null && pl.IsInWorld && !pl.isNeedNotify(NotifyFlags.VisibilityChanged)) pl.UpdateVisibilityOf(i_player); } } if (!i_data.HasData()) return; UpdateObject packet; i_data.BuildPacket(out packet); i_player.SendPacket(packet); foreach (var obj in i_visibleNow) i_player.SendInitialVisiblePackets(obj); } internal Player i_player; internal UpdateData i_data; internal List vis_guids; internal List i_visibleNow; } public class VisibleChangesNotifier : Notifier { public VisibleChangesNotifier(WorldObject obj) { i_object = obj; } public override void Visit(ICollection objs) { foreach (var player in objs) { if (player.GetGUID() == i_object.GetGUID()) return; player.UpdateVisibilityOf(i_object); if (player.HasSharedVision()) { foreach (var visionPlayer in player.GetSharedVisionList()) { if (visionPlayer.seerView == player) visionPlayer.UpdateVisibilityOf(i_object); } } } } public override void Visit(ICollection objs) { foreach (var creature in objs) { if (creature.HasSharedVision()) { foreach (var visionPlayer in creature.GetSharedVisionList()) if (visionPlayer.seerView == creature) visionPlayer.UpdateVisibilityOf(i_object); } } } public override void Visit(ICollection objs) { foreach (var dynamicObj in objs) { Unit caster = dynamicObj.GetCaster(); if (caster) { Player pl = caster.ToPlayer(); if (pl && pl.seerView == dynamicObj) pl.UpdateVisibilityOf(i_object); } } } WorldObject i_object; } public class PlayerRelocationNotifier : VisibleNotifier { public PlayerRelocationNotifier(Player player) : base(player) { } public override void Visit(ICollection objs) { base.Visit(objs); foreach (var player in objs) { 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(ICollection objs) { base.Visit(objs); bool relocated_for_ai = (i_player == i_player.seerView); foreach (var creature in objs) { 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(ICollection objs) { foreach (var player in objs) { if (!player.seerView.isNeedNotify(NotifyFlags.VisibilityChanged)) player.UpdateVisibilityOf(i_creature); CreatureUnitRelocationWorker(i_creature, player); } } public override void Visit(ICollection objs) { if (!i_creature.IsAlive()) return; foreach (var creature in objs) { 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(ICollection objs) { foreach (var player in objs) { 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(ICollection objs) { foreach (var creature in objs) { if (!creature.isNeedNotify(NotifyFlags.VisibilityChanged)) continue; CreatureRelocationNotifier relocate = new CreatureRelocationNotifier(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(ICollection objs) { foreach (var creature in objs) { CreatureUnitRelocationWorker(creature, i_unit); if (isCreature) CreatureUnitRelocationWorker(i_unit.ToCreature(), creature); } } Unit i_unit; bool isCreature; } public class MessageDistDeliverer : Notifier { public MessageDistDeliverer(WorldObject src, ServerPacket msg, float dist, bool own_team_only = false, Player skipped = null) { i_source = src; i_message = msg; i_distSq = dist * dist; team = (uint)((own_team_only && src.IsTypeId(TypeId.Player)) ? ((Player)src).GetTeam() : 0); skipped_receiver = skipped; } public override void Visit(ICollection objs) { foreach (var player in objs) { if (!player.IsInPhase(i_source)) continue; if (player.GetExactDist2dSq(i_source.GetPosition()) > 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(ICollection objs) { foreach (var creature in objs) { if (creature.GetCreatureTemplate().Entry == 1) { } if (!creature.IsInPhase(i_source)) continue; if (creature.GetExactDist2dSq(i_source.GetPosition()) > 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(ICollection objs) { foreach (var obj in objs) { if (!obj.IsInPhase(i_source)) continue; if (obj.GetExactDist2dSq(i_source.GetPosition()) > i_distSq) continue; // Send packet back to the caster if the caster has vision of dynamic object Unit caster = obj.GetCaster(); if (caster) { Player player = caster.ToPlayer(); if (player && player.seerView == obj) SendPacket(player); } } } void SendPacket(Player player) { // never send packet to self if (i_source == player || (team != 0 && (uint)player.GetTeam() != team) || skipped_receiver == player) return; if (!player.HaveAtClient(i_source)) return; player.SendPacket(i_message); } WorldObject i_source; ServerPacket i_message; float i_distSq; uint team; Player skipped_receiver; } public class UpdaterNotifier : Notifier { public UpdaterNotifier(uint diff) { i_timeDiff = diff; } public override void Visit(ICollection objs) { foreach (var obj in objs.ToList()) { if (obj.IsTypeId(TypeId.Player) || obj.IsTypeId(TypeId.Corpse)) continue; if (obj.IsInWorld) obj.Update(i_timeDiff); } } uint i_timeDiff; } public class PlayerWorker : Notifier { PlayerWorker(WorldObject searcher, Action _action) { _searcher = searcher; action = _action; } public override void Visit(ICollection objs) { foreach (var player in objs) if (player.IsInPhase(_searcher)) action.Invoke(player); } WorldObject _searcher; Action action; } public class CreatureWorker : Notifier { public CreatureWorker(WorldObject searcher, IDoWork _Do) { _searcher = searcher; Do = _Do; } public override void Visit(ICollection objs) { foreach (var creature in objs) { if (creature.IsInPhase(_searcher)) Do.Invoke(creature); } } WorldObject _searcher; IDoWork Do; } public class GameObjectWorker : Notifier { public GameObjectWorker(WorldObject searcher, IDoWork _Do) { Do = _Do; _searcher = searcher; } public override void Visit(ICollection objs) { foreach (var obj in objs) if (obj.IsInPhase(_searcher)) Do.Invoke(obj); } WorldObject _searcher; IDoWork Do; } public class WorldObjectWorker : Notifier { public WorldObjectWorker(WorldObject searcher, IDoWork _do, GridMapTypeMask mapTypeMask = GridMapTypeMask.All) { i_mapTypeMask = mapTypeMask; _searcher = searcher; i_do = _do; } public override void Visit(ICollection objs) { foreach (var obj in objs) { switch (obj.GetTypeId()) { case TypeId.GameObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.GameObject)) return; break; case TypeId.Player: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Player)) return; break; case TypeId.Unit: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Creature)) return; break; case TypeId.Corpse: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Corpse)) return; break; case TypeId.DynamicObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.DynamicObject)) return; break; case TypeId.AreaTrigger: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.AreaTrigger)) return; break; case TypeId.Conversation: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) return; break; } if (obj.IsInPhase(_searcher)) i_do.Invoke(obj); } } GridMapTypeMask i_mapTypeMask; WorldObject _searcher; IDoWork i_do; } public class ResetNotifier : Notifier { public override void Visit(ICollection objs) { foreach (var player in objs) player.ResetAllNotifies(); } public override void Visit(ICollection objs) { foreach (var creature in objs) creature.ResetAllNotifies(); } } public class WorldObjectChangeAccumulator : Notifier { public WorldObjectChangeAccumulator(WorldObject obj, Dictionary d) { updateData = d; worldObject = obj; } public override void Visit(ICollection objs) { foreach (var player in objs) { BuildPacket(player); if (!player.GetSharedVisionList().Empty()) { foreach (var visionPlayer in player.GetSharedVisionList()) BuildPacket(visionPlayer); } } } public override void Visit(ICollection objs) { foreach (var creature in objs) { if (!creature.GetSharedVisionList().Empty()) { foreach (var visionPlayer in creature.GetSharedVisionList()) BuildPacket(visionPlayer); } } } public override void Visit(ICollection objs) { foreach (var obj in objs) { ObjectGuid guid = obj.GetCasterGUID(); if (guid.IsPlayer()) { //Caster may be NULL if DynObj is in removelist Player caster = Global.ObjAccessor.FindPlayer(guid); if (caster != null) if (caster.GetGuidValue(PlayerFields.Farsight) == obj.GetGUID()) BuildPacket(caster); } } } void BuildPacket(Player player) { // Only send update once to a player if (!plr_list.Contains(player.GetGUID()) && player.HaveAtClient(worldObject)) { worldObject.BuildFieldsUpdate(player, updateData); plr_list.Add(player.GetGUID()); } } Dictionary updateData; WorldObject worldObject; List plr_list = new List(); } public class PlayerDistWorker : Notifier { public PlayerDistWorker(WorldObject searcher, float _dist, IDoWork _Do) { i_searcher = searcher; i_dist = _dist; Do = _Do; } public override void Visit(ICollection objs) { foreach (var player in objs) { if (player.IsInPhase(i_searcher) && player.IsWithinDist(i_searcher, i_dist)) Do.Invoke(player); } } WorldObject i_searcher; float i_dist; IDoWork Do; } public class CallOfHelpCreatureInRangeDo : IDoWork { public CallOfHelpCreatureInRangeDo(Unit funit, Unit enemy, float range) { i_funit = funit; i_enemy = enemy; i_range = range; } public void Invoke(Creature u) { if (u == i_funit) return; if (!u.CanAssistTo(i_funit, i_enemy, false)) return; // too far if (!u.IsWithinDistInMap(i_funit, i_range)) return; // only if see assisted creature's enemy if (!u.IsWithinLOSInMap(i_enemy)) return; if (u.GetAI() != null) u.GetAI().AttackStart(i_enemy); } Unit i_funit; Unit i_enemy; float i_range; } public class LocalizedPacketDo : IDoWork { public LocalizedPacketDo(MessageBuilder builder) { Builder = builder; } public void Invoke(Player player) { LocaleConstant loc_idx = player.GetSession().GetSessionDbLocaleIndex(); int cache_idx = (int)loc_idx + 1; ServerPacket data; // create if not cached yet if (i_data_cache.Length < cache_idx + 1 || i_data_cache[cache_idx] == null) { if (i_data_cache.Length < cache_idx + 1) Array.Resize(ref i_data_cache, cache_idx + 1); data = Builder.Invoke(loc_idx); i_data_cache[cache_idx] = data; } else data = i_data_cache[cache_idx]; player.SendPacket(data); } MessageBuilder Builder; ServerPacket[] i_data_cache = new ServerPacket[(int)LocaleConstant.Max]; // 0 = default, i => i-1 locale index } public class LocalizedPacketListDo : IDoWork { public LocalizedPacketListDo(MessageBuilder builder) { i_builder = builder; } public void Invoke(Player p) { LocaleConstant loc_idx = p.GetSession().GetSessionDbLocaleIndex(); int cache_idx = (int)loc_idx + 1; List data_list = new List(); // create if not cached yet if (i_data_cache.Count < cache_idx + 1 || i_data_cache[cache_idx].Empty()) { data_list = i_data_cache[cache_idx]; i_builder.Invoke(data_list, loc_idx); } else data_list = i_data_cache[cache_idx]; foreach (var packet in data_list) p.SendPacket(packet); } MessageBuilder i_builder; MultiMap i_data_cache = new MultiMap(); // 0 = default, i => i-1 locale index } public class RespawnDo : IDoWork { public void Invoke(WorldObject obj) { switch (obj.GetTypeId()) { case TypeId.Unit: obj.ToCreature().Respawn(); break; case TypeId.GameObject: obj.ToGameObject().Respawn(); break; } } } //Searchers public class WorldObjectSearcher : Notifier { public WorldObjectSearcher(WorldObject searcher, ICheck check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All) { i_mapTypeMask = mapTypeMask; _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { // already found if (i_object) return; foreach (var obj in objs) { switch (obj.GetTypeId()) { case TypeId.Player: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Player)) continue; break; case TypeId.GameObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.GameObject)) continue; break; case TypeId.Corpse: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Corpse)) continue; break; case TypeId.Unit: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Creature)) continue; break; case TypeId.DynamicObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.DynamicObject)) continue; break; case TypeId.AreaTrigger: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.AreaTrigger)) continue; break; case TypeId.Conversation: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) continue; break; } if (!obj.IsInPhase(_searcher)) continue; if (i_check.Invoke(obj)) { i_object = obj; return; } } } public WorldObject GetTarget() { return i_object; } GridMapTypeMask i_mapTypeMask; WorldObject i_object; WorldObject _searcher; ICheck i_check; } public class WorldObjectLastSearcher : Notifier { public WorldObjectLastSearcher(WorldObject searcher, ICheck check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All) { i_mapTypeMask = mapTypeMask; _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { foreach (var obj in objs) { switch (obj.GetTypeId()) { case TypeId.Player: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Player)) continue; break; case TypeId.GameObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.GameObject)) continue; break; case TypeId.Corpse: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Corpse)) continue; break; case TypeId.Unit: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Creature)) continue; break; case TypeId.DynamicObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.DynamicObject)) continue; break; case TypeId.AreaTrigger: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.AreaTrigger)) continue; break; case TypeId.Conversation: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) continue; break; } if (!obj.IsInPhase(_searcher)) continue; if (i_check.Invoke(obj)) i_object = obj; } } public WorldObject GetTarget() { return i_object; } GridMapTypeMask i_mapTypeMask; WorldObject i_object; WorldObject _searcher; ICheck i_check; } public class WorldObjectListSearcher : Notifier { public WorldObjectListSearcher(WorldObject searcher, List objects, ICheck check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All) { i_mapTypeMask = mapTypeMask; _searcher = searcher; i_objects = objects; i_check = check; } public override void Visit(ICollection objs) { foreach (var obj in objs) { switch (obj.GetTypeId()) { case TypeId.Player: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Player)) continue; break; case TypeId.GameObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.GameObject)) continue; break; case TypeId.Corpse: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Corpse)) continue; break; case TypeId.Unit: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Creature)) continue; break; case TypeId.DynamicObject: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.DynamicObject)) continue; break; case TypeId.AreaTrigger: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.AreaTrigger)) continue; break; case TypeId.Conversation: if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) continue; break; } if (i_check.Invoke(obj)) i_objects.Add(obj); } } GridMapTypeMask i_mapTypeMask; List i_objects; WorldObject _searcher; ICheck i_check; } public class GameObjectSearcher : Notifier { public GameObjectSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { // already found if (i_object) return; foreach (var go in objs) { if (!go.IsInPhase(_searcher)) continue; if (i_check.Invoke(go)) { i_object = go; return; } } } public GameObject GetTarget() { return i_object; } WorldObject _searcher; GameObject i_object; ICheck i_check; } public class GameObjectLastSearcher : Notifier { public GameObjectLastSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { foreach (var go in objs) { if (!go.IsInPhase(_searcher)) continue; if (i_check.Invoke(go)) i_object = go; } } public GameObject GetTarget() { return i_object; } WorldObject _searcher; GameObject i_object; ICheck i_check; } public class GameObjectListSearcher : Notifier { public GameObjectListSearcher(WorldObject searcher, List objects, ICheck check) { _searcher = searcher; i_objects = objects; i_check = check; } public override void Visit(ICollection objs) { foreach (var obj in objs) { if (obj.IsInPhase(_searcher)) if (i_check.Invoke(obj)) i_objects.Add(obj); } } WorldObject _searcher; List i_objects; ICheck i_check; } public class UnitSearcher : Notifier { public UnitSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { foreach (var player in objs) { if (!player.IsInPhase(_searcher)) continue; if (i_check.Invoke(player)) { i_object = player; return; } } } public override void Visit(ICollection objs) { foreach (var creature in objs) { if (!creature.IsInPhase(_searcher)) continue; if (i_check.Invoke(creature)) { i_object = creature; return; } } } public Unit GetTarget() { return i_object; } WorldObject _searcher; Unit i_object; ICheck i_check; } public class UnitLastSearcher : Notifier { public UnitLastSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { foreach (var player in objs) { if (!player.IsInPhase(_searcher)) continue; if (i_check.Invoke(player)) i_object = player; } } public override void Visit(ICollection objs) { foreach (var creature in objs) { if (!creature.IsInPhase(_searcher)) continue; if (i_check.Invoke(creature)) i_object = creature; } } public Unit GetTarget() { return i_object; } WorldObject _searcher; Unit i_object; ICheck i_check; } public class UnitListSearcher : Notifier { public UnitListSearcher(WorldObject searcher, List objects, ICheck check) { _searcher = searcher; i_objects = objects; i_check = check; } public override void Visit(ICollection objs) { foreach (var player in objs) { if (player.IsInPhase(_searcher)) if (i_check.Invoke(player)) i_objects.Add(player); } } public override void Visit(ICollection objs) { foreach (var creature in objs) { if (creature.IsInPhase(_searcher)) if (i_check.Invoke(creature)) i_objects.Add(creature); } } WorldObject _searcher; List i_objects; ICheck i_check; } public class CreatureSearcher : Notifier { public CreatureSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { // already found if (i_object) return; foreach (var creature in objs) { if (!creature.IsInPhase(_searcher)) continue; if (i_check.Invoke(creature)) { i_object = creature; return; } } } public Creature GetTarget() { return i_object; } WorldObject _searcher; Creature i_object; ICheck i_check; } public class CreatureLastSearcher : Notifier { public CreatureLastSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { foreach (var creature in objs) { if (!creature.IsInPhase(_searcher)) continue; if (i_check.Invoke(creature)) i_object = creature; } } public Creature GetTarget() { return i_object; } WorldObject _searcher; Creature i_object; ICheck i_check; } public class CreatureListSearcher : Notifier { public CreatureListSearcher(WorldObject searcher, List objects, ICheck check) { _searcher = searcher; i_objects = objects; i_check = check; } public override void Visit(ICollection objs) { foreach (var creature in objs) if (creature.IsInPhase(_searcher)) if (i_check.Invoke(creature)) i_objects.Add(creature); } WorldObject _searcher; List i_objects; ICheck i_check; } public class PlayerSearcher : Notifier { public PlayerSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { // already found if (i_object) return; foreach (var player in objs) { if (!player.IsInPhase(_searcher)) continue; if (i_check.Invoke(player)) { i_object = player; return; } } } public Player GetTarget() { return i_object; } WorldObject _searcher; Player i_object; ICheck i_check; } public class PlayerLastSearcher : Notifier { public PlayerLastSearcher(WorldObject searcher, ICheck check) { _searcher = searcher; i_check = check; } public override void Visit(ICollection objs) { foreach (var player in objs) { if (!player.IsInPhase(_searcher)) continue; if (i_check.Invoke(player)) i_object = player; } } public Player GetTarget() { return i_object; } WorldObject _searcher; Player i_object; ICheck i_check; } public class PlayerListSearcher : Notifier { public PlayerListSearcher(WorldObject searcher, List objects, ICheck check) { _searcher = searcher; i_objects = objects; i_check = check; } public override void Visit(ICollection objs) { foreach (var player in objs) if (player.IsInPhase(_searcher)) if (i_check.Invoke(player)) i_objects.Add(player); } WorldObject _searcher; List i_objects; ICheck i_check; } //Checks #region Checks public class MostHPMissingInRange : ICheck where T : Unit { public MostHPMissingInRange(Unit obj, float range, uint hp) { i_obj = obj; i_range = range; i_hp = hp; } public bool Invoke(T u) { if (u.IsAlive() && u.IsInCombat() && !i_obj.IsHostileTo(u) && i_obj.IsWithinDistInMap(u, i_range) && u.GetMaxHealth() - u.GetHealth() > i_hp) { i_hp = (uint)(u.GetMaxHealth() - u.GetHealth()); return true; } return false; } Unit i_obj; float i_range; ulong i_hp; } public class FriendlyCCedInRange : ICheck { public FriendlyCCedInRange(Unit obj, float range) { i_obj = obj; i_range = range; } public bool Invoke(Creature u) { if (u.IsAlive() && u.IsInCombat() && !i_obj.IsHostileTo(u) && i_obj.IsWithinDistInMap(u, i_range) && (u.isFeared() || u.IsCharmed() || u.isFrozen() || u.HasUnitState(UnitState.Stunned) || u.HasUnitState(UnitState.Confused))) return true; return false; } Unit i_obj; float i_range; } public class FriendlyMissingBuffInRange : ICheck { public FriendlyMissingBuffInRange(Unit obj, float range, uint spellid) { i_obj = obj; i_range = range; i_spell = spellid; } public bool Invoke(Creature u) { if (u.IsAlive() && u.IsInCombat() && !i_obj.IsHostileTo(u) && i_obj.IsWithinDistInMap(u, i_range) && !(u.HasAura(i_spell))) { return true; } return false; } Unit i_obj; float i_range; uint i_spell; } public class AnyUnfriendlyUnitInObjectRangeCheck : ICheck { public AnyUnfriendlyUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range) { i_obj = obj; i_funit = funit; i_range = range; } public bool Invoke(Unit u) { if (u.IsAlive() && i_obj.IsWithinDistInMap(u, i_range) && !i_funit.IsFriendlyTo(u)) return true; else return false; } WorldObject i_obj; Unit i_funit; float i_range; } public class AnyUnfriendlyNoTotemUnitInObjectRangeCheck : ICheck { public AnyUnfriendlyNoTotemUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range) { i_obj = obj; i_funit = funit; i_range = range; } 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; return i_obj.IsWithinDistInMap(u, i_range) && !i_funit.IsFriendlyTo(u); } WorldObject i_obj; Unit i_funit; float i_range; } public class AnyFriendlyUnitInObjectRangeCheck : ICheck { public AnyFriendlyUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range, bool playerOnly = false) { i_obj = obj; i_funit = funit; i_range = range; i_playerOnly = playerOnly; } public bool Invoke(Unit u) { if (u.IsAlive() && i_obj.IsWithinDistInMap(u, i_range) && i_funit.IsFriendlyTo(u) && (!i_playerOnly || u.IsTypeId(TypeId.Player))) return true; else return false; } WorldObject i_obj; Unit i_funit; float i_range; bool i_playerOnly; } public class AnyGroupedUnitInObjectRangeCheck : ICheck { public AnyGroupedUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range, bool raid, bool playerOnly = false) { _source = obj; _refUnit = funit; _range = range; _raid = raid; _playerOnly = playerOnly; } 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; return !_refUnit.IsHostileTo(u) && u.IsAlive() && _source.IsWithinDistInMap(u, _range); } WorldObject _source; Unit _refUnit; float _range; bool _raid; bool _playerOnly; } public class AnyUnitInObjectRangeCheck : ICheck { public AnyUnitInObjectRangeCheck(WorldObject obj, float range, bool check3D = true) { i_obj = obj; i_range = range; i_check3D = check3D; } public bool Invoke(Unit u) { if (u.IsAlive() && i_obj.IsWithinDistInMap(u, i_range, i_check3D)) return true; return false; } WorldObject i_obj; float i_range; bool i_check3D; } // Success at unit in range, range update for next check (this can be use with UnitLastSearcher to find nearest unit) public class NearestAttackableUnitInObjectRangeCheck : ICheck { public NearestAttackableUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range) { i_obj = obj; i_funit = funit; i_range = range; } public bool Invoke(Unit u) { if (u.isTargetableForAttack() && i_obj.IsWithinDistInMap(u, i_range) && !i_funit.IsFriendlyTo(u) && i_funit.CanSeeOrDetect(u)) { i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check return true; } return false; } WorldObject i_obj; Unit i_funit; float i_range; } public class AnyAoETargetUnitInObjectRangeCheck : ICheck { public AnyAoETargetUnitInObjectRangeCheck(WorldObject obj, Unit funit, float range, SpellInfo spellInfo = null) { i_obj = obj; i_funit = funit; _spellInfo = spellInfo; i_range = range; if (_spellInfo == null) _spellInfo = i_obj.ToDynamicObject().GetSpellInfo(); } public bool Invoke(Unit u) { // Check contains checks for: live, non-selectable, non-attackable flags, flight check and GM check, ignore totems if (u.IsTypeId(TypeId.Unit) && u.IsTotem()) return false; if (_spellInfo != null && _spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers) && !u.IsPlayer()) return false; return i_funit._IsValidAttackTarget(u, _spellInfo, i_obj.IsTypeId(TypeId.DynamicObject) ? i_obj : null) && i_obj.IsWithinDistInMap(u, i_range); } WorldObject i_obj; Unit i_funit; SpellInfo _spellInfo; float i_range; } public class AnyDeadUnitCheck : ICheck { public bool Invoke(Unit u) { return !u.IsAlive(); } } public class NearestHostileUnitCheck : ICheck { 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.IsWithinDistInMap(u, m_range)) return false; if (!me.IsValidAttackTarget(u)) return false; if (i_playerOnly && !u.IsTypeId(TypeId.Player)) return false; m_range = me.GetDistance(u); // use found unit range as new range limit for next check return true; } Creature me; float m_range; bool i_playerOnly; } class NearestHostileUnitInAttackDistanceCheck : ICheck { public NearestHostileUnitInAttackDistanceCheck(Creature creature, float dist = 0) { me = creature; m_range = (dist == 0 ? 9999 : dist); m_force = (dist == 0 ? false : true); } public bool Invoke(Unit u) { if (!me.IsWithinDistInMap(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; } Creature me; float m_range; bool m_force; } class NearestHostileUnitInAggroRangeCheck : ICheck { public NearestHostileUnitInAggroRangeCheck(Creature creature, bool useLOS = false) { _me = creature; _useLOS = useLOS; } public bool Invoke(Unit u) { if (!u.IsHostileTo(_me)) return false; if (!u.IsWithinDistInMap(_me, _me.GetAggroRange(u))) return false; if (!_me.IsValidAttackTarget(u)) return false; if (_useLOS && !u.IsWithinLOSInMap(_me)) return false; return true; } Creature _me; bool _useLOS; } class AnyAssistCreatureInRangeCheck : ICheck { public AnyAssistCreatureInRangeCheck(Unit funit, Unit enemy, float range) { i_funit = funit; i_enemy = enemy; i_range = range; } public bool Invoke(Creature u) { if (u == i_funit) return false; if (!u.CanAssistTo(i_funit, i_enemy)) return false; // too far if (!i_funit.IsWithinDistInMap(u, i_range)) return false; // only if see assisted creature if (!i_funit.IsWithinLOSInMap(u)) return false; return true; } Unit i_funit; Unit i_enemy; float i_range; } class NearestAssistCreatureInCreatureRangeCheck : ICheck { public NearestAssistCreatureInCreatureRangeCheck(Creature obj, Unit enemy, float range) { i_obj = obj; i_enemy = enemy; i_range = range; } public bool Invoke(Creature u) { if (u == i_obj) return false; if (!u.CanAssistTo(i_obj, i_enemy)) return false; if (!i_obj.IsWithinDistInMap(u, i_range)) return false; if (!i_obj.IsWithinLOSInMap(u)) return false; i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check return true; } Creature i_obj; Unit i_enemy; float i_range; } // Success at unit in range, range update for next check (this can be use with CreatureLastSearcher to find nearest creature) class NearestCreatureEntryWithLiveStateInObjectRangeCheck : ICheck { 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 && i_obj.IsWithinDistInMap(u, i_range)) { i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check return true; } return false; } WorldObject i_obj; uint i_entry; bool i_alive; float i_range; } public class AnyPlayerInObjectRangeCheck : ICheck { public AnyPlayerInObjectRangeCheck(WorldObject obj, float range, bool reqAlive = true) { _obj = obj; _range = range; _reqAlive = reqAlive; } public bool Invoke(Player pl) { if (_reqAlive && !pl.IsAlive()) return false; if (!_obj.IsWithinDistInMap(pl, _range)) return false; return true; } WorldObject _obj; float _range; bool _reqAlive; } class NearestPlayerInObjectRangeCheck : ICheck { public NearestPlayerInObjectRangeCheck(WorldObject obj, float range) { i_obj = obj; i_range = range; } public bool Invoke(Player pl) { if (pl.IsAlive() && i_obj.IsWithinDistInMap(pl, i_range)) { i_range = i_obj.GetDistance(pl); return true; } return false; } WorldObject i_obj; float i_range; } class AllFriendlyCreaturesInGrid : ICheck { public AllFriendlyCreaturesInGrid(Unit obj) { unit = obj; } public bool Invoke(Unit u) { if (u.IsAlive() && u.IsVisible() && u.IsFriendlyTo(unit)) return true; return false; } Unit unit; } class AllGameObjectsWithEntryInRange : ICheck { public AllGameObjectsWithEntryInRange(WorldObject obj, uint entry, float maxRange) { m_pObject = obj; m_uiEntry = entry; m_fRange = maxRange; } public bool Invoke(GameObject go) { if (m_uiEntry == 0 || go.GetEntry() == m_uiEntry && m_pObject.IsWithinDist(go, m_fRange, false)) return true; return false; } WorldObject m_pObject; uint m_uiEntry; float m_fRange; } class AllCreaturesOfEntryInRange : ICheck { public AllCreaturesOfEntryInRange(WorldObject obj, uint entry, float maxRange) { m_pObject = obj; m_uiEntry = entry; m_fRange = maxRange; } public bool Invoke(Creature creature) { if (m_uiEntry == 0 || creature.GetEntry() == m_uiEntry && m_pObject.IsWithinDist(creature, m_fRange, false)) return true; return false; } WorldObject m_pObject; uint m_uiEntry; float m_fRange; } class PlayerAtMinimumRangeAway : ICheck { public PlayerAtMinimumRangeAway(Unit _unit, float fMinRange) { unit = _unit; fRange = fMinRange; } 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, fRange, false)) return true; return false; } Unit unit; float fRange; } class GameObjectInRangeCheck : ICheck { 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); else return false; } float x, y, z, range; uint entry; } public class AllWorldObjectsInRange : ICheck { public AllWorldObjectsInRange(WorldObject obj, float maxRange) { m_pObject = obj; m_fRange = maxRange; } public bool Invoke(WorldObject go) { return m_pObject.IsWithinDist(go, m_fRange, false) && m_pObject.IsInPhase(go); } WorldObject m_pObject; float m_fRange; } class ObjectTypeIdCheck : ICheck { public ObjectTypeIdCheck(TypeId typeId, bool equals) { _typeId = typeId; _equals = equals; } public bool Invoke(WorldObject obj) { return (obj.GetTypeId() == _typeId) == _equals; } TypeId _typeId; bool _equals; } public class ObjectGUIDCheck : ICheck { public ObjectGUIDCheck(ObjectGuid GUID) { _GUID = GUID; } public bool Invoke(WorldObject obj) { return obj.GetGUID() == _GUID; } public static implicit operator Predicate(ObjectGUIDCheck check) { return check.Invoke; } ObjectGuid _GUID; } class HeightDifferenceCheck : ICheck { public HeightDifferenceCheck(WorldObject go, float diff, bool reverse) { _baseObject = go; _difference = diff; _reverse = reverse; } public bool Invoke(WorldObject unit) { return (unit.GetPositionZ() - _baseObject.GetPositionZ() > _difference) != _reverse; } WorldObject _baseObject; float _difference; bool _reverse; } public class UnitAuraCheck : ICheck where T : WorldObject { public UnitAuraCheck(bool present, uint spellId, ObjectGuid casterGUID = default(ObjectGuid)) { _present = present; _spellId = spellId; _casterGUID = casterGUID; } public bool Invoke(T obj) { return obj.ToUnit() && obj.ToUnit().HasAura(_spellId, _casterGUID) == _present; } public static implicit operator Predicate(UnitAuraCheck unit) { return unit.Invoke; } bool _present; uint _spellId; ObjectGuid _casterGUID; } class GameObjectFocusCheck : ICheck { public GameObjectFocusCheck(Unit unit, uint focusId) { i_unit = unit; i_focusId = focusId; } public bool Invoke(GameObject go) { if (go.GetGoInfo().type != GameObjectTypes.SpellFocus) return false; if (go.GetGoInfo().SpellFocus.spellFocusType != i_focusId) return false; if (!go.isSpawned()) return false; float dist = go.GetGoInfo().SpellFocus.radius / 2.0f; return go.IsWithinDistInMap(i_unit, dist); } Unit i_unit; uint i_focusId; } // Find the nearest Fishing hole and return true only if source object is in range of hole class NearestGameObjectFishingHole : ICheck { public NearestGameObjectFishingHole(WorldObject obj, float range) { i_obj = obj; i_range = range; } public bool Invoke(GameObject go) { if (go.GetGoInfo().type == GameObjectTypes.FishingHole && go.isSpawned() && i_obj.IsWithinDistInMap(go, i_range) && i_obj.IsWithinDistInMap(go, go.GetGoInfo().FishingHole.radius)) { i_range = i_obj.GetDistance(go); return true; } return false; } WorldObject i_obj; float i_range; } class NearestGameObjectCheck : ICheck { public NearestGameObjectCheck(WorldObject obj) { i_obj = obj; i_range = 999; } public bool Invoke(GameObject go) { if (i_obj.IsWithinDistInMap(go, i_range)) { i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check return true; } return false; } WorldObject i_obj; float i_range; } // Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO) class NearestGameObjectEntryInObjectRangeCheck : ICheck { public NearestGameObjectEntryInObjectRangeCheck(WorldObject obj, uint entry, float range) { i_obj = obj; i_entry = entry; i_range = range; } public bool Invoke(GameObject go) { if (go.GetEntry() == i_entry && i_obj.IsWithinDistInMap(go, i_range)) { i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check return true; } return false; } WorldObject i_obj; uint i_entry; float i_range; } // 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 : ICheck { public NearestGameObjectTypeInObjectRangeCheck(WorldObject obj, GameObjectTypes type, float range) { i_obj = obj; i_type = type; i_range = range; } public bool Invoke(GameObject go) { if (go.GetGoType() == i_type && i_obj.IsWithinDistInMap(go, i_range)) { i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check return true; } return false; } WorldObject i_obj; GameObjectTypes i_type; float i_range; } public class AnyDeadUnitObjectInRangeCheck : ICheck where T : WorldObject { public AnyDeadUnitObjectInRangeCheck(Unit searchObj, float range) { i_searchObj = searchObj; i_range = range; } public virtual bool Invoke(T obj) { Player player = obj.ToPlayer(); if (player) return !player.IsAlive() && !player.HasAuraType(AuraType.Ghost) && i_searchObj.IsWithinDistInMap(player, i_range); Creature creature = obj.ToCreature(); if (creature) return !creature.IsAlive() && i_searchObj.IsWithinDistInMap(creature, i_range); Corpse corpse = obj.ToCorpse(); if (corpse) return corpse.GetCorpseType() != CorpseType.Bones && i_searchObj.IsWithinDistInMap(corpse, i_range); return false; } Unit i_searchObj; float i_range; } public class AnyDeadUnitSpellTargetInRangeCheck : AnyDeadUnitObjectInRangeCheck where T : WorldObject { public AnyDeadUnitSpellTargetInRangeCheck(Unit searchObj, float range, SpellInfo spellInfo, SpellTargetCheckTypes check) : base(searchObj, range) { i_spellInfo = spellInfo; i_check = new WorldObjectSpellTargetCheck(searchObj, searchObj, spellInfo, check, null); } public override bool Invoke(T obj) { return base.Invoke(obj) && i_check.Invoke(obj); } SpellInfo i_spellInfo; WorldObjectSpellTargetCheck i_check; } public class PlayerOrPetCheck : ICheck { public bool Invoke(WorldObject obj) { if (obj.IsTypeId(TypeId.Player)) return false; Creature creature = obj.ToCreature(); if (creature) return !creature.IsPet(); return true; } } #endregion }