From dfbfc42d609ab5a473dfdf01e6d822290fa3429e Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sun, 2 Aug 2026 19:56:32 +0200 Subject: [PATCH] fix(Core/Script): Add SMART_TARGET_SHARED_OWNER_ENTITIES. (#26902) --- .../game/AI/SmartScripts/SmartScript.cpp | 64 +++++++++++++++++++ .../game/AI/SmartScripts/SmartScriptMgr.cpp | 20 ++++++ .../game/AI/SmartScripts/SmartScriptMgr.h | 11 +++- 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index a5cf7b90c2..9e9e882636 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -44,6 +44,27 @@ // see: https://github.com/azerothcore/azerothcore-wotlk/issues/9766 #include "GridNotifiersImpl.h" +namespace +{ + // Returns the GUID of whoever brought this object into the world: its owner/charmer if any, + // its summoner otherwise. Empty for objects that were not summoned by anyone. + ObjectGuid GetSummonerOrOwnerGUID(WorldObject const* obj) + { + if (Creature const* creature = obj->ToCreature()) + { + if (ObjectGuid ownerGUID = creature->GetCharmerOrOwnerGUID()) + return ownerGUID; + + if (TempSummon const* summon = creature->ToTempSummon()) + return summon->GetSummonerGUID(); + } + else if (GameObject const* gameObject = obj->ToGameObject()) + return gameObject->GetOwnerGUID(); + + return ObjectGuid::Empty; + } +} + SmartScript::SmartScript() { go = nullptr; @@ -4158,6 +4179,49 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e, } break; } + case SMART_TARGET_SHARED_OWNER_ENTITIES: + { + WorldObject* ref = GetBaseObject(); + + if (!ref) + { + LOG_ERROR("scripts.ai.sai", "SMART_TARGET_SHARED_OWNER_ENTITIES: Entry {} SourceType {} Event {} Action {} Target {} is missing base object.", + e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType()); + break; + } + + ObjectGuid ownerGUID = GetSummonerOrOwnerGUID(ref); + if (!ownerGUID) + break; + + bool const wantGameObject = e.target.sharedOwnerEntities.type == 2; + uint32 const entry = e.target.sharedOwnerEntities.entry; + + float const dist = e.target.sharedOwnerEntities.maxDist ? (float)e.target.sharedOwnerEntities.maxDist : ref->GetVisibilityRange(); + + ObjectVector units; + GetWorldObjectsInDist(units, dist); + + for (WorldObject* unit : units) + { + // an object is never a sibling of itself + if (unit->GetGUID() == ref->GetGUID()) + continue; + + if (wantGameObject ? !IsGameObject(unit) : !IsCreature(unit)) + continue; + + if (entry && unit->GetEntry() != entry) + continue; + + if (GetSummonerOrOwnerGUID(unit) != ownerGUID) + continue; + + targets.push_back(unit); + } + + break; + } case SMART_TARGET_NONE: case SMART_TARGET_POSITION: default: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index ac02164058..c46c7f373c 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -537,6 +537,25 @@ bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e) return false; return IsSAIBoolValid(e, e.target.formation.excludeSelf); } + case SMART_TARGET_SHARED_OWNER_ENTITIES: + { + if (e.target.sharedOwnerEntities.type != 1 && e.target.sharedOwnerEntities.type != 2) + { + LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has invalid shared owner entities type as target ({}, must be 1 or 2).", + e.entryOrGuid, e.GetScriptType(), e.GetEventType(), e.GetActionType(), e.target.sharedOwnerEntities.type); + return false; + } + + if (e.target.sharedOwnerEntities.entry) + { + if (e.target.sharedOwnerEntities.type == 1 && !IsCreatureValid(e, e.target.sharedOwnerEntities.entry)) + return false; + + if (e.target.sharedOwnerEntities.type == 2 && !IsGameObjectValid(e, e.target.sharedOwnerEntities.entry)) + return false; + } + break; + } case SMART_TARGET_HOSTILE_SECOND_AGGRO: case SMART_TARGET_HOSTILE_LAST_AGGRO: case SMART_TARGET_HOSTILE_RANDOM: @@ -963,6 +982,7 @@ bool SmartAIMgr::CheckUnusedTargetParams(SmartScriptHolder const& e) case SMART_TARGET_SUMMONED_CREATURES: return sizeof(SmartTarget::summonedCreatures); case SMART_TARGET_INSTANCE_STORAGE: return sizeof(SmartTarget::instanceStorage); case SMART_TARGET_FORMATION: return sizeof(SmartTarget::formation); + case SMART_TARGET_SHARED_OWNER_ENTITIES: return sizeof(SmartTarget::sharedOwnerEntities); default: LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using a target {} with no unused params specified in SmartAIMgr::CheckUnusedTargetParams(), please report this.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType()); diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 04804584c7..b8fae0c948 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -1590,8 +1590,9 @@ enum SMARTAI_TARGETS SMART_TARGET_SUMMONED_CREATURES = 204, // Entry SMART_TARGET_INSTANCE_STORAGE = 205, // Instance data index, Type (creature (1), gameobject (2)) SMART_TARGET_FORMATION = 206, // Type (0: members only, 1: leader only, 2: all), CreatureEntry (0: any), ExcludeSelf (0/1) + SMART_TARGET_SHARED_OWNER_ENTITIES = 207, // Type (creature (1), gameobject (2)), Entry (0: any), MaxDist (0: visibility range) - SMART_TARGET_AC_END = 207 // placeholder + SMART_TARGET_AC_END = 208 // placeholder }; struct SmartTarget @@ -1779,6 +1780,14 @@ struct SmartTarget { SAIBool includePets; } invokerParty; + + // entities sharing our owner, charmer or summoner, whether they were summoned by it or not + struct + { + uint32 type; // creature (1), gameobject (2) + uint32 entry; // entry filter, 0 = any + uint32 maxDist; // 0 = visibility range + } sharedOwnerEntities; }; };