From 80437d3c79853c27ffa60b1988d1459586d18cbb Mon Sep 17 00:00:00 2001 From: BandyscTC Date: Sun, 7 Sep 2025 19:09:44 +0100 Subject: Core/SAI: Add storedTargetId param to summon actions (#31142) --- src/server/game/AI/SmartScripts/SmartScript.cpp | 66 +++++++++++++++++----- src/server/game/AI/SmartScripts/SmartScript.h | 1 + src/server/game/AI/SmartScripts/SmartScriptMgr.cpp | 19 ++++++- src/server/game/AI/SmartScripts/SmartScriptMgr.h | 14 +++-- 4 files changed, 80 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index d5407d5f131..e1715f787b6 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -22,6 +22,7 @@ #include "Creature.h" #include "CreatureTextMgr.h" #include "CreatureTextMgrImpl.h" +#include "DB2Stores.h" #include "GameEventMgr.h" #include "GameEventSender.h" #include "GameObject.h" @@ -41,6 +42,7 @@ #include "Random.h" #include "ScriptActions.h" #include "SmartAI.h" +#include "SpellMgr.h" #include "SpellAuras.h" #include "TemporarySummon.h" #include "Vehicle.h" @@ -116,6 +118,11 @@ bool SmartScript::IsSmart(bool silent) const return false; } +void SmartScript::ClearTargetList(uint32 id) +{ + _storedTargets.erase(id); +} + void SmartScript::StoreTargetList(ObjectVector const& targets, uint32 id) { // insert or replace @@ -1241,6 +1248,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { EnumFlag flags(static_cast(e.action.summonCreature.flags)); bool preferUnit = flags.HasFlag(SmartActionSummonCreatureFlags::PreferUnit); + bool attackInvoker = flags.HasFlag(SmartActionSummonCreatureFlags::AttackInvoker); + WorldObject* summoner = preferUnit ? unit : GetBaseObjectOrUnitInvoker(unit); if (!summoner) break; @@ -1250,6 +1259,35 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u privateObjectOwner = summoner->IsPrivateObject() ? summoner->GetPrivateObjectOwner() : summoner->GetGUID(); uint32 spawnsCount = std::max(e.action.summonCreature.count, 1u); + if (e.action.summonCreature.storedTargetId) + ClearTargetList(e.action.summonCreature.storedTargetId); + + SummonPropertiesEntry const* summonProperties = [&]() -> SummonPropertiesEntry const* + { + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.summonCreature.createdBySpell, DIFFICULTY_NONE)) + for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects()) + if (spellEffectInfo.IsEffect(SPELL_EFFECT_SUMMON)) + if (SummonPropertiesEntry const* summonProps = sSummonPropertiesStore.LookupEntry(spellEffectInfo.MiscValueB)) + return summonProps; + + return nullptr; + }(); + + auto DoSummon = [&](float x, float y, float z, float o, Unit* attackTarget) + { + for (uint32 counter = 0; counter < spawnsCount; counter++) + { + if (TempSummon* summon = summoner->GetMap()->SummonCreature(e.action.summonCreature.creature, { x, y, z, o }, summonProperties, Milliseconds(e.action.summonCreature.duration), summoner, e.action.summonCreature.createdBySpell, 0, privateObjectOwner)) + { + summon->SetTempSummonType((TempSummonType)e.action.summonCreature.type); + if (e.action.summonCreature.storedTargetId) + AddToStoredTargetList({ summon }, e.action.summonCreature.storedTargetId); + if (attackInvoker && attackTarget) + summon->AI()->AttackStart(attackTarget); + } + } + }; + float x, y, z, o; for (WorldObject* target : targets) { @@ -1258,23 +1296,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u y += e.target.y; z += e.target.z; o += e.target.o; - for (uint32 counter = 0; counter < spawnsCount; counter++) - { - if (Creature* summon = summoner->SummonCreature(e.action.summonCreature.creature, x, y, z, o, (TempSummonType)e.action.summonCreature.type, Milliseconds(e.action.summonCreature.duration), privateObjectOwner)) - if (e.action.summonCreature.attackInvoker) - summon->AI()->AttackStart(target->ToUnit()); - } + DoSummon(x, y, z, o, target->ToUnit()); } if (e.GetTargetType() != SMART_TARGET_POSITION) break; - for (uint32 counter = 0; counter < spawnsCount; counter++) - { - if (Creature* summon = summoner->SummonCreature(e.action.summonCreature.creature, e.target.x, e.target.y, e.target.z, e.target.o, (TempSummonType)e.action.summonCreature.type, Milliseconds(e.action.summonCreature.duration), privateObjectOwner)) - if (unit && e.action.summonCreature.attackInvoker) - summon->AI()->AttackStart(unit); - } + DoSummon(e.target.x, e.target.y, e.target.z, e.target.o, unit); break; } case SMART_ACTION_SUMMON_GO: @@ -1283,18 +1311,25 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (!summoner) break; + if (e.action.summonGO.storedTargetId) + ClearTargetList(e.action.summonGO.storedTargetId); + for (WorldObject* target : targets) { Position pos = target->GetPositionWithOffset(Position(e.target.x, e.target.y, e.target.z, e.target.o)); QuaternionData rot = QuaternionData::fromEulerAnglesZYX(pos.GetOrientation(), 0.f, 0.f); - summoner->SummonGameObject(e.action.summonGO.entry, pos, rot, Seconds(e.action.summonGO.despawnTime), GOSummonType(e.action.summonGO.summonType)); + GameObject* summon = summoner->SummonGameObject(e.action.summonGO.entry, pos, rot, Seconds(e.action.summonGO.despawnTime), GOSummonType(e.action.summonGO.summonType)); + if (e.action.summonGO.storedTargetId && summon) + AddToStoredTargetList({ summon }, e.action.summonGO.storedTargetId); } if (e.GetTargetType() != SMART_TARGET_POSITION) break; QuaternionData rot = QuaternionData::fromEulerAnglesZYX(e.target.o, 0.f, 0.f); - summoner->SummonGameObject(e.action.summonGO.entry, Position(e.target.x, e.target.y, e.target.z, e.target.o), rot, Seconds(e.action.summonGO.despawnTime), GOSummonType(e.action.summonGO.summonType)); + GameObject* summon = summoner->SummonGameObject(e.action.summonGO.entry, Position(e.target.x, e.target.y, e.target.z, e.target.o), rot, Seconds(e.action.summonGO.despawnTime), GOSummonType(e.action.summonGO.summonType)); + if (e.action.summonGO.storedTargetId && summon) + AddToStoredTargetList({ summon }, e.action.summonGO.storedTargetId); break; } case SMART_ACTION_KILL_UNIT: @@ -2031,6 +2066,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u for (TempSummon* summon : summonList) if (unit && e.action.creatureGroup.attackInvoker) summon->AI()->AttackStart(unit); + + if (e.action.creatureGroup.storedTargetId) + StoreTargetList({ summonList.begin(), summonList.end() }, e.action.creatureGroup.storedTargetId); break; } case SMART_ACTION_SET_POWER: diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h index e4c4c8d3907..fd4730829ef 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.h +++ b/src/server/game/AI/SmartScripts/SmartScript.h @@ -81,6 +81,7 @@ class TC_GAME_API SmartScript bool IsSmart(GameObject* g, bool silent = false) const; bool IsSmart(bool silent = false) const; + void ClearTargetList(uint32 id); void StoreTargetList(ObjectVector const& targets, uint32 id); void AddToStoredTargetList(ObjectVector const& targets, uint32 id); ObjectVector const* GetStoredTargetVector(uint32 id, WorldObject const& ref) const; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 3c69ddd193f..82ec392bfdb 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -1858,7 +1858,24 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) return false; } - TC_SAI_IS_BOOLEAN_VALID(e, e.action.summonCreature.attackInvoker); + if (e.action.summonCreature.createdBySpell != 0) + { + if (!IsSpellValid(e, e.action.summonCreature.createdBySpell)) + return false; + + bool propertiesFound = std::ranges::any_of(sSpellMgr->AssertSpellInfo(e.action.summonCreature.createdBySpell, DIFFICULTY_NONE)->GetEffects(), + [](SpellEffectInfo const& spellEffectInfo) + { + return spellEffectInfo.IsEffect(SPELL_EFFECT_SUMMON) && sSummonPropertiesStore.HasRecord(spellEffectInfo.MiscValueB); + }); + + if (!propertiesFound) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} Spell {} is not a summon creature spell.", + e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonCreature.createdBySpell); + return false; + } + } break; } case SMART_ACTION_CALL_KILLEDMONSTER: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 97dabdf1997..0c57aaf5415 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -465,7 +465,7 @@ enum SMART_ACTION SMART_ACTION_ACTIVATE_GOBJECT = 9, // SMART_ACTION_RANDOM_EMOTE = 10, // EmoteId1, EmoteId2, EmoteId3... SMART_ACTION_CAST = 11, // SpellId, CastFlags, TriggeredFlags - SMART_ACTION_SUMMON_CREATURE = 12, // CreatureID, summonType, duration in ms, attackInvoker, flags(SmartActionSummonCreatureFlags) + SMART_ACTION_SUMMON_CREATURE = 12, // CreatureID, summonType, duration in ms, stored target id, flags(SmartActionSummonCreatureFlags), count, createdBySpell SMART_ACTION_THREAT_SINGLE_PCT = 13, // Threat% SMART_ACTION_THREAT_ALL_PCT = 14, // Threat% SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS = 15, // UNUSED, DO NOT REUSE @@ -503,7 +503,7 @@ enum SMART_ACTION SMART_ACTION_SET_VISIBILITY = 47, // on/off SMART_ACTION_SET_ACTIVE = 48, // on/off SMART_ACTION_ATTACK_START = 49, // - SMART_ACTION_SUMMON_GO = 50, // GameObjectID, DespawnTime in s + SMART_ACTION_SUMMON_GO = 50, // GameObjectID, DespawnTime in s, summon type, stored target id SMART_ACTION_KILL_UNIT = 51, // SMART_ACTION_ACTIVATE_TAXI = 52, // TaxiID SMART_ACTION_WP_START = 53, // run/walk, pathID, canRepeat, quest, despawntime @@ -560,7 +560,7 @@ enum SMART_ACTION SMART_ACTION_SET_GO_FLAG = 104, // UNUSED, DO NOT REUSE SMART_ACTION_ADD_GO_FLAG = 105, // UNUSED, DO NOT REUSE SMART_ACTION_REMOVE_GO_FLAG = 106, // UNUSED, DO NOT REUSE - SMART_ACTION_SUMMON_CREATURE_GROUP = 107, // Group, attackInvoker + SMART_ACTION_SUMMON_CREATURE_GROUP = 107, // Group, attackInvoker, stored target id SMART_ACTION_SET_POWER = 108, // PowerType, newPower SMART_ACTION_ADD_POWER = 109, // PowerType, newPower SMART_ACTION_REMOVE_POWER = 110, // PowerType, newPower @@ -619,8 +619,9 @@ enum class SmartActionSummonCreatureFlags None = 0, PersonalSpawn = 1, PreferUnit = 2, + AttackInvoker = 4, - All = PersonalSpawn | PreferUnit, + All = PersonalSpawn | PreferUnit | AttackInvoker, }; DEFINE_ENUM_FLAG(SmartActionSummonCreatureFlags); @@ -713,9 +714,10 @@ struct SmartAction uint32 creature; uint32 type; uint32 duration; - SAIBool attackInvoker; + uint32 storedTargetId; uint32 flags; // SmartActionSummonCreatureFlags uint32 count; + uint32 createdBySpell; } summonCreature; struct @@ -857,6 +859,7 @@ struct SmartAction uint32 entry; uint32 despawnTime; uint32 summonType; + uint32 storedTargetId; } summonGO; struct @@ -1059,6 +1062,7 @@ struct SmartAction { uint32 group; uint32 attackInvoker; + uint32 storedTargetId; } creatureGroup; struct -- cgit v1.2.3