From b035d1f914fddf01276b4b13ed68eaca38a6c4b0 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:11:52 -0300 Subject: [PATCH] feat(Core/SmartScripts): Implement Target Type for Formations (#24811) --- .../game/AI/SmartScripts/SmartScript.cpp | 44 +++++++++++++++++++ .../game/AI/SmartScripts/SmartScriptMgr.cpp | 13 ++++++ .../game/AI/SmartScripts/SmartScriptMgr.h | 10 ++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index f80a36d7a..ec4bf27d6 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -19,6 +19,7 @@ #include "Cell.h" #include "CellImpl.h" #include "ChatTextBuilder.h" +#include "CreatureGroups.h" #include "CreatureTextMgr.h" #include "GameEventMgr.h" #include "GossipDef.h" @@ -4062,6 +4063,49 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e, break; } + case SMART_TARGET_FORMATION: + { + if (me) + { + if (CreatureGroup* group = me->GetFormation()) + { + uint32 formationType = e.target.formation.type; + uint32 entry = e.target.formation.entry; + bool excludeSelf = e.target.formation.excludeSelf; + + if (formationType == 1) // Leader only + { + if (Creature* leader = group->GetLeader()) + { + if ((!excludeSelf || leader != me) && (!entry || leader->GetEntry() == entry)) + targets.push_back(leader); + } + } + else // 0 = Members only, 2 = All + { + for (auto const& itr : group->GetMembers()) + { + Creature* member = itr.first; + + if (!member) + continue; + + if (excludeSelf && member == me) + continue; + + if (entry && member->GetEntry() != entry) + continue; + + if (formationType == 0 && member == group->GetLeader()) + continue; + + targets.push_back(member); + } + } + } + } + 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 52faaef4b..3f0cbdef0 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -531,6 +531,18 @@ bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e) } break; } + case SMART_TARGET_FORMATION: + { + if (e.target.formation.type > 2) + { + LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has invalid formation target type ({}, must be 0-2).", + e.entryOrGuid, e.GetScriptType(), e.GetEventType(), e.GetActionType(), e.target.formation.type); + return false; + } + if (e.target.formation.entry && !IsCreatureValid(e, e.target.formation.entry)) + return false; + return IsSAIBoolValid(e, e.target.formation.excludeSelf); + } case SMART_TARGET_HOSTILE_SECOND_AGGRO: case SMART_TARGET_HOSTILE_LAST_AGGRO: case SMART_TARGET_HOSTILE_RANDOM: @@ -955,6 +967,7 @@ bool SmartAIMgr::CheckUnusedTargetParams(SmartScriptHolder const& e) case SMART_TARGET_RANDOM_POINT: return sizeof(SmartTarget::randomPoint); 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); 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 2601f5bd1..e87e01e62 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -1581,8 +1581,9 @@ enum SMARTAI_TARGETS SMART_TARGET_ROLE_SELECTION = 203, // Range Max, TargetMask (Tanks (1), Healer (2) Damage (4)), resize list 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_AC_END = 206 // placeholder + SMART_TARGET_AC_END = 207 // placeholder }; struct SmartTarget @@ -1759,6 +1760,13 @@ struct SmartTarget uint32 type; } instanceStorage; + struct + { + uint32 type; // 0: members only, 1: leader only, 2: all + uint32 entry; // creature entry filter, 0 = any + SAIBool excludeSelf; + } formation; + struct { SAIBool includePets;