From 0c782c60c258d17994b476cc20fd0fa789a4ce2a Mon Sep 17 00:00:00 2001 From: hondacrx Date: Thu, 5 Jan 2023 16:44:30 -0500 Subject: [PATCH] Core/Creatures: Implement StringId for Creatures, a custom identifier to make finding specific creatures in script easier Port From (https://github.com/TrinityCore/TrinityCore/commit/61c51b76c00d932a9180bc6781a244dc18375ef7) --- Source/Framework/Constants/Language.cs | 3 +- .../Database/Databases/WorldDatabase.cs | 2 +- Source/Game/AI/ScriptedAI/ScriptedAI.cs | 5 ++ Source/Game/Chat/Commands/NPCCommands.cs | 1 + .../Game/Entities/Creature/Creature.Fields.cs | 3 + Source/Game/Entities/Creature/Creature.cs | 25 ++++++++ Source/Game/Entities/Creature/CreatureData.cs | 1 + Source/Game/Entities/Object/WorldObject.cs | 24 ++++++-- Source/Game/Globals/ObjectManager.cs | 6 +- Source/Game/Maps/GridNotifiers.cs | 61 ++++++++++++++----- Source/Game/Maps/SpawnData.cs | 1 + 11 files changed, 110 insertions(+), 22 deletions(-) diff --git a/Source/Framework/Constants/Language.cs b/Source/Framework/Constants/Language.cs index a533a3478..a14ed5aab 100644 --- a/Source/Framework/Constants/Language.cs +++ b/Source/Framework/Constants/Language.cs @@ -1187,8 +1187,9 @@ namespace Framework.Constants NpcinfoNpcFlags = 5086, NpcinfoPhaseIds = 5087, Scenario = 5088, + ObjectinfoStringIds = 5089, - // Room For More Trinity Strings 5089-6603 + // Room For More Trinity Strings 5090-6603 // Level Requirement Notifications SayReq = 6604, diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs index a3f3162cd..f54c9b155 100644 --- a/Source/Framework/Database/Databases/WorldDatabase.cs +++ b/Source/Framework/Database/Databases/WorldDatabase.cs @@ -75,7 +75,7 @@ namespace Framework.Database PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?"); PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?"); PrepareStatement(WorldStatements.SEL_COMMANDS, "SELECT name, help FROM command"); - PrepareStatement(WorldStatements.SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?"); + PrepareStatement(WorldStatements.SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?"); PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?"); PrepareStatement(WorldStatements.SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?"); PrepareStatement(WorldStatements.SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_"); diff --git a/Source/Game/AI/ScriptedAI/ScriptedAI.cs b/Source/Game/AI/ScriptedAI/ScriptedAI.cs index f5bb2d2ac..ac3611394 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedAI.cs @@ -456,6 +456,11 @@ namespace Game.AI return source.FindNearestCreature(entry, maxSearchRange, alive); } + public static Creature GetClosestCreatureWithOptions(WorldObject source, float maxSearchRange, FindCreatureOptions options) + { + return source.FindNearestCreatureWithOptions(maxSearchRange, options); + } + public static GameObject GetClosestGameObjectWithEntry(WorldObject source, uint entry, float maxSearchRange, bool spawnedOnly = true) { return source.FindNearestGameObject(entry, maxSearchRange, spawnedOnly); diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index b4df0beb4..f6f996fc3 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -150,6 +150,7 @@ namespace Game.Chat handler.SendSysMessage(CypherStrings.NpcinfoArmor, target.GetArmor()); handler.SendSysMessage(CypherStrings.NpcinfoPosition, target.GetPositionX(), target.GetPositionY(), target.GetPositionZ()); handler.SendSysMessage(CypherStrings.ObjectinfoAiInfo, target.GetAIName(), target.GetScriptName()); + handler.SendSysMessage(CypherStrings.ObjectinfoStringIds, target.GetStringIds()[0], target.GetStringIds()[1], target.GetStringIds()[2]); handler.SendSysMessage(CypherStrings.NpcinfoReactstate, target.GetReactState()); var ai = target.GetAI(); if (ai != null) diff --git a/Source/Game/Entities/Creature/Creature.Fields.cs b/Source/Game/Entities/Creature/Creature.Fields.cs index 3aa6616f2..f048989b4 100644 --- a/Source/Game/Entities/Creature/Creature.Fields.cs +++ b/Source/Game/Entities/Creature/Creature.Fields.cs @@ -27,6 +27,9 @@ namespace Game.Entities CreatureTemplate m_creatureInfo; CreatureData m_creatureData; + string[] m_stringIds = new string[3]; + string m_scriptStringId; + SpellFocusInfo _spellFocusInfo; long _lastDamagedTime; // Part of Evade mechanics diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 43ae8b55c..e83a0b1e7 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -433,6 +433,8 @@ namespace Game.Entities //We must update last scriptId or it looks like we reloaded a script, breaking some things such as gossip temporarily LastUsedScriptID = GetScriptId(); + m_stringIds[0] = cInfo.StringId; + return true; } @@ -2759,6 +2761,27 @@ namespace Game.Entities return Global.ObjectMgr.GetCreatureTemplate(GetEntry()) != null ? Global.ObjectMgr.GetCreatureTemplate(GetEntry()).ScriptID : 0; } + public bool HasStringId(string id) + { + return m_stringIds.Contains(id); + } + + void SetScriptStringId(string id) + { + if (!id.IsEmpty()) + { + m_scriptStringId = id; + m_stringIds[2] = m_scriptStringId; + } + else + { + m_scriptStringId = null; + m_stringIds[2] = null; + } + } + + public string[] GetStringIds() { return m_stringIds; } + public VendorItemData GetVendorItems() { return Global.ObjectMgr.GetNpcVendorItemList(GetEntry()); @@ -3293,6 +3316,8 @@ namespace Game.Entities // checked at creature_template loading DefaultMovementType = (MovementGeneratorType)data.movementType; + m_stringIds[1] = data.StringId; + if (addToMap && !GetMap().AddToMap(this)) return false; return true; diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs index af789cc5f..532d45f88 100644 --- a/Source/Game/Entities/Creature/CreatureData.cs +++ b/Source/Game/Entities/Creature/CreatureData.cs @@ -92,6 +92,7 @@ namespace Game.Entities public uint SpellSchoolImmuneMask; public CreatureFlagsExtra FlagsExtra; public uint ScriptID; + public string StringId; public QueryCreatureResponse QueryData; diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 99770ea43..87ad65313 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -1666,8 +1666,9 @@ namespace Game.Entities public Creature FindNearestCreatureWithOptions(float range, FindCreatureOptions options) { - var checker = new NearestCreatureEntryWithLiveStateAndAuraInObjectRangeCheck(this, range, options); - var searcher = new CreatureLastSearcher(this, checker); + NearestCheckCustomizer checkCustomizer = new(this, range); + CreatureWithOptionsInObjectRangeCheck checker = new(this, checkCustomizer, options); + CreatureLastSearcher searcher = new(this, checker); if (options.IgnorePhases) searcher.i_phaseShift = PhasingHandler.GetAlwaysVisiblePhaseShift(); @@ -2571,7 +2572,6 @@ namespace Game.Entities SendMessageToSet(cancelSpellVisualKit, true); } - // function based on function Unit::CanAttack from 13850 client public bool IsValidAttackTarget(WorldObject target, SpellInfo bySpell = null) { @@ -2883,7 +2883,7 @@ namespace Game.Entities { return spellInfo.GetSpellXSpellVisualId(this); } - + public List GetGameObjectListWithEntryInGrid(uint entry = 0, float maxSearchRange = 250.0f) { List gameobjectList = new(); @@ -2904,6 +2904,20 @@ namespace Game.Entities return creatureList; } + public List GetCreatureListWithOptionsInGrid(float maxSearchRange, FindCreatureOptions options) + { + List creatureList = new(); + NoopCheckCustomizer checkCustomizer = new(); + CreatureWithOptionsInObjectRangeCheck check = new(this, checkCustomizer, options); + CreatureListSearcher searcher = new(this, creatureList, check); + if (options.IgnorePhases) + searcher.i_phaseShift = PhasingHandler.GetAlwaysVisiblePhaseShift(); + + Cell.VisitGridObjects(this, searcher, maxSearchRange); + return creatureList; + } + + public List GetPlayerListInGrid(float maxSearchRange, bool alive = true) { List playerList = new(); @@ -4050,6 +4064,7 @@ namespace Game.Entities public struct FindCreatureOptions { public FindCreatureOptions SetCreatureId(uint creatureId) { CreatureId = creatureId; return this; } + public FindCreatureOptions SetStringId(string stringId) { StringId = stringId; return this; } public FindCreatureOptions SetIsAlive(bool isAlive) { IsAlive = isAlive; return this; } public FindCreatureOptions SetIsInCombat(bool isInCombat) { IsInCombat = isInCombat; return this; } @@ -4067,6 +4082,7 @@ namespace Game.Entities public FindCreatureOptions SetPrivateObjectOwner(ObjectGuid privateObjectOwnerGuid) { PrivateObjectOwnerGuid = privateObjectOwnerGuid; return this; } public uint? CreatureId; + public string StringId; public bool? IsAlive; public bool? IsInCombat; diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 91c051656..040737fb1 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -1918,6 +1918,7 @@ namespace Game creature.SpellSchoolImmuneMask = fields.Read(68); creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(69); creature.ScriptID = GetScriptId(fields.Read(70)); + creature.StringId = fields.Read(71); creatureTemplateStorage[entry] = creature; } @@ -3540,8 +3541,8 @@ namespace Game SQLResult result = DB.World.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, " + //11 12 13 14 15 16 17 18 19 20 21 "currentwaypoint, curhealth, curmana, MovementType, spawnDifficulties, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, " + - //22 23 24 25 26 27 - "creature.dynamicflags, creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName " + + // 22 23 24 25 26 27 28 + "creature.dynamicflags, creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName, creature.StringId " + "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId"); if (result.IsEmpty()) @@ -3604,6 +3605,7 @@ namespace Game data.PhaseGroup = result.Read(25); data.terrainSwapMap = result.Read(26); data.ScriptId = GetScriptId(result.Read(27)); + data.StringId = result.Read(28); data.spawnGroupData = _spawnGroupDataStorage[IsTransportMap(data.MapId) ? 1 : 0u]; // transport spawns default to compatibility group var mapEntry = CliDB.MapStorage.LookupByKey(data.MapId); diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index 5d8ba6f4b..65f26e1ed 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -189,7 +189,7 @@ namespace Game.Maps { if (visionPlayer.seerView == player) visionPlayer.UpdateVisibilityOf(i_objects); - } + } } } @@ -1741,7 +1741,7 @@ namespace Game.Maps public class CreatureListSearcher : Notifier { - PhaseShift i_phaseShift; + internal PhaseShift i_phaseShift; List i_objects; ICheck i_check; @@ -1911,7 +1911,7 @@ namespace Game.Maps return false; } } - + public class FriendlyBelowHpPctEntryInRange : ICheck { public FriendlyBelowHpPctEntryInRange(Unit obj, uint entry, float range, byte pct, bool excludeSelf) @@ -2094,7 +2094,7 @@ namespace Game.Maps public bool Invoke(Unit u) { if (_playerOnly && !u.IsPlayer()) - return false; + return false; if (_raid) { @@ -2433,17 +2433,17 @@ namespace Game.Maps float i_range; } - public class NearestCreatureEntryWithOptionsInObjectRangeCheck : ICheck + public class CreatureWithOptionsInObjectRangeCheck : ICheck where T : NoopCheckCustomizer { WorldObject i_obj; FindCreatureOptions i_args; - float i_range; + T i_customizer; - public NearestCreatureEntryWithOptionsInObjectRangeCheck(WorldObject obj, float range, FindCreatureOptions args) + public CreatureWithOptionsInObjectRangeCheck(WorldObject obj, T customizer, FindCreatureOptions args) { i_obj = obj; i_args = args; - i_range = range; + i_customizer = customizer; } public bool Invoke(Creature u) @@ -2454,12 +2454,15 @@ namespace Game.Maps if (u.GetGUID() == i_obj.GetGUID()) return false; - if (!i_obj.IsWithinDistInMap(u, i_range)) + 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 && u.IsAlive() != i_args.IsAlive) return false; @@ -2485,11 +2488,11 @@ namespace Game.Maps 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 + i_customizer.Update(u); return true; } } - + public class AnyPlayerInObjectRangeCheck : ICheck { public AnyPlayerInObjectRangeCheck(WorldObject obj, float range, bool reqAlive = true) @@ -2539,7 +2542,7 @@ namespace Game.Maps float _range; bool _reqAlive; } - + class NearestPlayerInObjectRangeCheck : ICheck { public NearestPlayerInObjectRangeCheck(WorldObject obj, float range) @@ -2793,7 +2796,7 @@ namespace Game.Maps return obj.GetEntry() == _entry && (!obj.IsPrivateObject() || obj.GetPrivateObjectOwner() == _ownerGUID); } } - + class GameObjectFocusCheck : ICheck { public GameObjectFocusCheck(WorldObject caster, uint focusId) @@ -2914,7 +2917,7 @@ namespace Game.Maps 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 : ICheck { @@ -2940,6 +2943,36 @@ namespace Game.Maps float i_range; } + // CHECK modifiers + public class NoopCheckCustomizer + { + public virtual bool Test(WorldObject o) { return true; } + + public virtual void Update(WorldObject o) { } + } + + class NearestCheckCustomizer : NoopCheckCustomizer + { + WorldObject i_obj; + float i_range; + + public NearestCheckCustomizer(WorldObject obj, float range) + { + i_obj = obj; + i_range = range; + } + + public override bool Test(WorldObject o) + { + return i_obj.IsWithinDistInMap(o, i_range); + } + + public override void Update(WorldObject o) + { + i_range = i_obj.GetDistance(o); + } + } + public class AnyDeadUnitObjectInRangeCheck : ICheck where T : WorldObject { public AnyDeadUnitObjectInRangeCheck(WorldObject searchObj, float range) diff --git a/Source/Game/Maps/SpawnData.cs b/Source/Game/Maps/SpawnData.cs index 65a40f1d0..802558dd4 100644 --- a/Source/Game/Maps/SpawnData.cs +++ b/Source/Game/Maps/SpawnData.cs @@ -41,6 +41,7 @@ namespace Game.Maps public int spawntimesecs; public List SpawnDifficulties; public uint ScriptId; + public string StringId; public SpawnData(SpawnObjectType t) : base(t) {