diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 15e2de2a0..99770ea43 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -243,7 +243,7 @@ namespace Game.Entities updateData.BuildPacket(out UpdateObject packet); target.SendPacket(packet); } - + public void BuildMovementUpdate(WorldPacket data, CreateObjectBits flags, Player target) { List PauseTimes = null; @@ -402,7 +402,7 @@ namespace Game.Entities if (flags.CombatVictim) data.WritePackedGuid(ToUnit().GetVictim().GetGUID()); // CombatVictim - + if (flags.ServerTime) data.WriteUInt32(GameTime.GetGameTimeMS()); @@ -816,11 +816,11 @@ namespace Game.Entities public override string GetDebugInfo() { - return $"{base.GetDebugInfo()}\n{GetGUID()} Entry: {GetEntry()}\nName: { GetName()}"; + return $"{base.GetDebugInfo()}\n{GetGUID()} Entry: {GetEntry()}\nName: {GetName()}"; } public virtual Loot GetLootForPlayer(Player player) { return null; } - + public abstract void BuildValuesCreate(WorldPacket data, Player target); public abstract void BuildValuesUpdate(WorldPacket data, Player target); @@ -1163,7 +1163,7 @@ namespace Game.Entities } public SmoothPhasing GetSmoothPhasing() { return _smoothPhasing; } - + public bool CanSeeOrDetect(WorldObject obj, bool ignoreStealth = false, bool distanceCheck = false, bool checkAlert = false) { if (this == obj) @@ -1504,7 +1504,7 @@ namespace Game.Entities BattleField bf = Global.BattleFieldMgr.GetBattlefieldToZoneId(map, GetZoneId()); if (bf != null) return bf; - + return Global.OutdoorPvPMgr.GetOutdoorPvPToZoneId(map, GetZoneId()); } } @@ -1664,10 +1664,13 @@ namespace Game.Entities return searcher.GetTarget(); } - public Creature FindNearestCreatureWithAura(uint entry, uint spellId, float range, bool alive = true) + public Creature FindNearestCreatureWithOptions(float range, FindCreatureOptions options) { - var checker = new NearestCreatureEntryWithLiveStateAndAuraInObjectRangeCheck(this, entry, spellId, alive, range); + var checker = new NearestCreatureEntryWithLiveStateAndAuraInObjectRangeCheck(this, range, options); var searcher = new CreatureLastSearcher(this, checker); + if (options.IgnorePhases) + searcher.i_phaseShift = PhasingHandler.GetAlwaysVisiblePhaseShift(); + Cell.VisitAllObjects(this, searcher, range); return searcher.GetTarget(); } @@ -2627,7 +2630,7 @@ namespace Game.Entities Unit unitOrOwner = unit; GameObject go = ToGameObject(); if (go?.GetGoType() == GameObjectTypes.Trap) - unitOrOwner = go.GetOwner(); + unitOrOwner = go.GetOwner(); // ignore immunity flags when assisting if (unitOrOwner != null && unitTarget != null && !(isPositiveSpell && bySpell.HasAttribute(SpellAttr6.CanAssistImmunePc))) @@ -2928,7 +2931,7 @@ namespace Game.Entities { return GetPhaseShift().CanSee(obj.GetPhaseShift()); } - + public static bool InSamePhase(WorldObject a, WorldObject b) { return a != null && b != null && a.InSamePhase(b); @@ -3056,7 +3059,7 @@ namespace Game.Entities public void SetIsNewObject(bool enable) { _isNewObject = enable; } public bool IsDestroyedObject() { return _isDestroyedObject; } public void SetDestroyedObject(bool destroyed) { _isDestroyedObject = destroyed; } - + public bool IsCreature() { return GetTypeId() == TypeId.Unit; } public bool IsPlayer() { return GetTypeId() == TypeId.Player; } public bool IsGameObject() { return GetTypeId() == TypeId.GameObject; } @@ -3066,7 +3069,7 @@ namespace Game.Entities public bool IsAreaTrigger() { return GetTypeId() == TypeId.AreaTrigger; } public bool IsConversation() { return GetTypeId() == TypeId.Conversation; } public bool IsSceneObject() { return GetTypeId() == TypeId.SceneObject; } - + public Creature ToCreature() { return IsCreature() ? (this as Creature) : null; } public Player ToPlayer() { return IsPlayer() ? (this as Player) : null; } public GameObject ToGameObject() { return IsGameObject() ? (this as GameObject) : null; } @@ -3678,7 +3681,7 @@ namespace Game.Entities dist = MathF.Sqrt((pos.posX - destx) * (pos.posX - destx) + (pos.posY - desty) * (pos.posY - desty)); } } - + // check dynamic collision col = GetMap().GetObjectHitPos(GetPhaseShift(), pos.posX, pos.posY, pos.posZ + halfHeight, destx, desty, destz + halfHeight, out destx, out desty, out destz, -0.5f); @@ -4043,4 +4046,41 @@ namespace Game.Entities player.SendPacket(i_message); } } + + public struct FindCreatureOptions + { + public FindCreatureOptions SetCreatureId(uint creatureId) { CreatureId = creatureId; return this; } + + public FindCreatureOptions SetIsAlive(bool isAlive) { IsAlive = isAlive; return this; } + public FindCreatureOptions SetIsInCombat(bool isInCombat) { IsInCombat = isInCombat; return this; } + public FindCreatureOptions SetIsSummon(bool isSummon) { IsSummon = isSummon; return this; } + + public FindCreatureOptions SetIgnorePhases(bool ignorePhases) { IgnorePhases = ignorePhases; return this; } + public FindCreatureOptions SetIgnoreNotOwnedPrivateObjects(bool ignoreNotOwnedPrivateObjects) { IgnoreNotOwnedPrivateObjects = ignoreNotOwnedPrivateObjects; return this; } + public FindCreatureOptions SetIgnorePrivateObjects(bool ignorePrivateObjects) { IgnorePrivateObjects = ignorePrivateObjects; return this; } + + public FindCreatureOptions SetHasAura(uint spellId) { AuraSpellId = spellId; return this; } + public FindCreatureOptions SetOwner(ObjectGuid ownerGuid) { OwnerGuid = ownerGuid; return this; } + public FindCreatureOptions SetCharmer(ObjectGuid charmerGuid) { CharmerGuid = charmerGuid; return this; } + public FindCreatureOptions SetCreator(ObjectGuid creatorGuid) { CreatorGuid = creatorGuid; return this; } + public FindCreatureOptions SetDemonCreator(ObjectGuid demonCreatorGuid) { DemonCreatorGuid = demonCreatorGuid; return this; } + public FindCreatureOptions SetPrivateObjectOwner(ObjectGuid privateObjectOwnerGuid) { PrivateObjectOwnerGuid = privateObjectOwnerGuid; return this; } + + public uint? CreatureId; + + public bool? IsAlive; + public bool? IsInCombat; + public bool? IsSummon; + + public bool IgnorePhases; + public bool IgnoreNotOwnedPrivateObjects; + public bool IgnorePrivateObjects; + + public uint? AuraSpellId; + public ObjectGuid? OwnerGuid; + public ObjectGuid? CharmerGuid; + public ObjectGuid? CreatorGuid; + public ObjectGuid? DemonCreatorGuid; + public ObjectGuid? PrivateObjectOwnerGuid; + } } \ No newline at end of file diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index a66698b8d..5d8ba6f4b 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -1713,7 +1713,7 @@ namespace Game.Maps public class CreatureLastSearcher : Notifier { - PhaseShift i_phaseShift; + internal PhaseShift i_phaseShift; Creature i_object; ICheck i_check; @@ -2433,37 +2433,60 @@ namespace Game.Maps float i_range; } - public class NearestCreatureEntryWithLiveStateAndAuraInObjectRangeCheck : ICheck + public class NearestCreatureEntryWithOptionsInObjectRangeCheck : ICheck { WorldObject i_obj; - uint i_entry; - uint i_spellId; - bool i_alive; + FindCreatureOptions i_args; float i_range; - public NearestCreatureEntryWithLiveStateAndAuraInObjectRangeCheck(WorldObject obj, uint entry, uint spellId, bool alive, float range) + public NearestCreatureEntryWithOptionsInObjectRangeCheck(WorldObject obj, float range, FindCreatureOptions args) { i_obj = obj; - i_entry = entry; - i_spellId = spellId; - i_alive = alive; + i_args = args; i_range = range; } public bool Invoke(Creature u) { - if (u.GetDeathState() != DeathState.Dead - && u.GetEntry() == i_entry - && u.HasAura(i_spellId) - && u.IsAlive() == i_alive - && u.GetGUID() != i_obj.GetGUID() - && i_obj.IsWithinDistInMap(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; + if (u.GetDeathState() == DeathState.Dead) // Despawned + return false; + + if (u.GetGUID() == i_obj.GetGUID()) + return false; + + if (!i_obj.IsWithinDistInMap(u, i_range)) + return false; + + if (i_args.CreatureId.HasValue && u.GetEntry() != i_args.CreatureId) + return false; + + if (i_args.IsAlive.HasValue && u.IsAlive() != i_args.IsAlive) + return false; + + 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_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check + return true; } } diff --git a/Source/Game/Phasing/PhasingHandler.cs b/Source/Game/Phasing/PhasingHandler.cs index 291206c61..83dd5b2e6 100644 --- a/Source/Game/Phasing/PhasingHandler.cs +++ b/Source/Game/Phasing/PhasingHandler.cs @@ -33,6 +33,13 @@ namespace Game public class PhasingHandler { public static PhaseShift EmptyPhaseShift = new(); + public static PhaseShift AlwaysVisible; + + static PhasingHandler() + { + AlwaysVisible = new(); + InitDbPhaseShift(AlwaysVisible, PhaseUseFlagsValues.AlwaysVisible, 0, 0); + } public static PhaseFlags GetPhaseFlags(uint phaseId) { @@ -500,6 +507,11 @@ namespace Game partyMemberPhases.List.Add(new PartyMemberPhase((uint)pair.Value.Flags, pair.Key)); } + public static PhaseShift GetAlwaysVisiblePhaseShift() + { + return AlwaysVisible; + } + public static void InitDbPhaseShift(PhaseShift phaseShift, PhaseUseFlagsValues phaseUseFlags, uint phaseId, uint phaseGroupId) { phaseShift.ClearPhases();