From 6b6338efc9181eb3641e39b2a1781aad533ca9bb Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Thu, 27 Nov 2025 13:39:38 -0500 Subject: [PATCH] Core/SAI: Add storedTargetId param to summon actions Port From (https://github.com/TrinityCore/TrinityCore/commit/80437d3c79853c27ffa60b1988d1459586d18cbb) --- Source/Framework/Constants/SmartAIConst.cs | 9 ++- Source/Game/AI/SmartScripts/SmartAIManager.cs | 23 +++++- Source/Game/AI/SmartScripts/SmartScript.cs | 81 ++++++++++++++----- 3 files changed, 87 insertions(+), 26 deletions(-) diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs index 7bdb01e35..c8bc471b3 100644 --- a/Source/Framework/Constants/SmartAIConst.cs +++ b/Source/Framework/Constants/SmartAIConst.cs @@ -136,8 +136,9 @@ namespace Framework.Constants None = 0, PersonalSpawn = 1, PreferUnit = 2, + AttackInvoker = 4, - All = PersonalSpawn | PreferUnit, + All = PersonalSpawn | PreferUnit | AttackInvoker, } public enum SmartEvents @@ -249,7 +250,7 @@ namespace Framework.Constants ActivateGobject = 9, // RandomEmote = 10, // Emoteid1, Emoteid2, Emoteid3... Cast = 11, // Spellid, Castflags, TriggeredFlags - SummonCreature = 12, // Creatureid, Summontype, Duration In Ms, Storageid, Attackinvoker, flags(SmartActionSummonCreatureFlags) + SummonCreature = 12, // reatureID, summonType, duration in ms, stored target id, flags(SmartActionSummonCreatureFlags), count, createdBySpell ThreatSinglePct = 13, // Threat% ThreatAllPct = 14, // Threat% CallAreaexploredoreventhappens = 15, // UNUSED, DO NOT REUSE @@ -287,7 +288,7 @@ namespace Framework.Constants SetVisibility = 47, // On/Off SetActive = 48, // No Params AttackStart = 49, // - SummonGo = 50, // Gameobjectid, Despawntime In Ms, + SummonGo = 50, // GameObjectID, DespawnTime in s, summon type, stored target id KillUnit = 51, // ActivateTaxi = 52, // Taxiid WpStart = 53, // Run/Walk, Pathid, Canrepeat, Quest, Despawntime @@ -344,7 +345,7 @@ namespace Framework.Constants SetGoFlag = 104, // UNUSED, DO NOT REUSE AddGoFlag = 105, // UNUSED, DO NOT REUSE RemoveGoFlag = 106, // UNUSED, DO NOT REUSE - SummonCreatureGroup = 107, // Group, Attackinvoker + SummonCreatureGroup = 107, // Group, attackInvoker, stored target id SetPower = 108, // PowerType, newPower AddPower = 109, // PowerType, newPower RemovePower = 110, // PowerType, newPower diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index a59dcb181..ce8d220d2 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -1633,7 +1633,22 @@ namespace Game.AI 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 = Global.SpellMgr.GetSpellInfo(e.Action.summonCreature.createdBySpell, Difficulty.None).GetEffects().Any(spellEffectInfo => + { + return spellEffectInfo.IsEffect(SpellEffectName.Summon) && CliDB.SummonPropertiesStorage.HasRecord((uint)spellEffectInfo.MiscValueB); + }); + + if (!propertiesFound) + { + Log.outError(LogFilter.Sql, $"SmartAIMgr: {e} Spell {e.Action.summonCreature.createdBySpell} is not a summon creature spell."); + return false; + } + } break; case SmartActions.CallKilledmonster: if (!IsCreatureValid(e, e.Action.killedMonster.creature)) @@ -3376,10 +3391,10 @@ namespace Game.AI public uint creature; public uint type; public uint duration; - public uint storageID; - public uint attackInvoker; + public uint storedTargetId; public uint flags; // SmartActionSummonCreatureFlags public uint count; + public uint createdBySpell; } public struct ThreatPCT { @@ -3512,6 +3527,7 @@ namespace Game.AI public uint entry; public uint despawnTime; public uint summonType; + public uint storedTargetId; } public struct Active { @@ -3686,6 +3702,7 @@ namespace Game.AI { public uint group; public uint attackInvoker; + public uint storedTargetId; } public struct Power { diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 874ffb308..5b139152a 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -1129,6 +1129,8 @@ namespace Game.AI { SmartActionSummonCreatureFlags flags = (SmartActionSummonCreatureFlags)e.Action.summonCreature.flags; bool preferUnit = flags.HasAnyFlag(SmartActionSummonCreatureFlags.PreferUnit); + bool attackInvoker = flags.HasFlag(SmartActionSummonCreatureFlags.AttackInvoker); + WorldObject summoner = preferUnit ? unit : GetBaseObjectOrUnitInvoker(unit); if (summoner == null) break; @@ -1138,6 +1140,44 @@ namespace Game.AI privateObjectOwner = summoner.IsPrivateObject() ? summoner.GetPrivateObjectOwner() : summoner.GetGUID(); uint spawnsCount = Math.Max(e.Action.summonCreature.count, 1u); + if (e.Action.summonCreature.storedTargetId != 0) + ClearTargetList(e.Action.summonCreature.storedTargetId); + + SummonPropertiesRecord summonProperties() + { + SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Action.summonCreature.createdBySpell, Difficulty.None); + if (spellInfo != null) + { + foreach (SpellEffectInfo spellEffectInfo in spellInfo.GetEffects()) + { + if (spellEffectInfo.IsEffect(SpellEffectName.Summon)) + { + var summonProps = CliDB.SummonPropertiesStorage.LookupByKey(spellEffectInfo.MiscValueB); + if (summonProps != null) + return summonProps; + } + } + } + + return null; + } + + void DoSummon(float x, float y, float z, float o, Unit attackTarget) + { + for (uint counter = 0; counter < spawnsCount; counter++) + { + TempSummon summon = summoner.GetMap().SummonCreature(e.Action.summonCreature.creature, new Position(x, y, z, o), summonProperties(), TimeSpan.FromMilliseconds(e.Action.summonCreature.duration), summoner, e.Action.summonCreature.createdBySpell, 0, privateObjectOwner); + if (summon != null) + { + summon.SetTempSummonType((TempSummonType)e.Action.summonCreature.type); + if (e.Action.summonCreature.storedTargetId != 0) + AddToStoredTargetList([summon], e.Action.summonCreature.storedTargetId); + if (attackInvoker && attackTarget != null) + summon.GetAI().AttackStart(attackTarget); + } + } + } + float x, y, z, o; foreach (var target in targets) { @@ -1146,25 +1186,13 @@ namespace Game.AI y += e.Target.y; z += e.Target.z; o += e.Target.o; - for (uint counter = 0; counter < spawnsCount; counter++) - { - Creature summon = summoner.SummonCreature(e.Action.summonCreature.creature, x, y, z, o, (TempSummonType)e.Action.summonCreature.type, TimeSpan.FromMilliseconds(e.Action.summonCreature.duration), privateObjectOwner); - if (summon != null) - if (e.Action.summonCreature.attackInvoker != 0) - summon.GetAI().AttackStart(target.ToUnit()); - } + DoSummon(x, y, z, o, target.ToUnit()); } if (e.GetTargetType() != SmartTargets.Position) break; - for (uint counter = 0; counter < spawnsCount; counter++) - { - 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, TimeSpan.FromMilliseconds(e.Action.summonCreature.duration), privateObjectOwner); - if (summon != null) - if (unit != null && e.Action.summonCreature.attackInvoker != 0) - summon.GetAI().AttackStart(unit); - } + DoSummon(e.Target.x, e.Target.y, e.Target.z, e.Target.o, unit); break; } case SmartActions.SummonGo: @@ -1173,18 +1201,25 @@ namespace Game.AI if (summoner == null) break; + if (e.Action.summonGO.storedTargetId != 0) + ClearTargetList(e.Action.summonGO.storedTargetId); + foreach (var target in targets) { Position pos = target.GetPositionWithOffset(new Position(e.Target.x, e.Target.y, e.Target.z, e.Target.o)); Quaternion rot = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(pos.GetOrientation(), 0f, 0f)); - summoner.SummonGameObject(e.Action.summonGO.entry, pos, rot, TimeSpan.FromSeconds(e.Action.summonGO.despawnTime), (GameObjectSummonType)e.Action.summonGO.summonType); + GameObject summon = summoner.SummonGameObject(e.Action.summonGO.entry, pos, rot, TimeSpan.FromSeconds(e.Action.summonGO.despawnTime), (GameObjectSummonType)e.Action.summonGO.summonType); + if (e.Action.summonGO.storedTargetId != 0 && summon != null) + AddToStoredTargetList([summon], e.Action.summonGO.storedTargetId); } if (e.GetTargetType() != SmartTargets.Position) break; - Quaternion _rot = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(e.Target.o, 0f, 0f)); - summoner.SummonGameObject(e.Action.summonGO.entry, new Position(e.Target.x, e.Target.y, e.Target.z, e.Target.o), _rot, TimeSpan.FromSeconds(e.Action.summonGO.despawnTime), (GameObjectSummonType)e.Action.summonGO.summonType); + Quaternion rot1 = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(e.Target.o, 0f, 0f)); + GameObject summon1 = summoner.SummonGameObject(e.Action.summonGO.entry, new Position(e.Target.x, e.Target.y, e.Target.z, e.Target.o), rot1, TimeSpan.FromSeconds(e.Action.summonGO.despawnTime), (GameObjectSummonType)e.Action.summonGO.summonType); + if (e.Action.summonGO.storedTargetId != 0 && summon1 != null) + AddToStoredTargetList([summon1], e.Action.summonGO.storedTargetId); break; } case SmartActions.KillUnit: @@ -1946,7 +1981,7 @@ namespace Game.AI { SmartAI ai = (SmartAI)creatureTarget.GetAI(); if (ai != null) - ai.GetScript().StoreTargetList(new List(storedTargets), e.Action.sendTargetToTarget.id); // store a copy of target list + ai.GetScript().StoreTargetList(storedTargets, e.Action.sendTargetToTarget.id); // store a copy of target list else Log.outError(LogFilter.Sql, "SmartScript: Action target for SMART_ACTION_SEND_TARGET_TO_TARGET is not using SmartAI, skipping"); } @@ -1957,7 +1992,7 @@ namespace Game.AI { SmartGameObjectAI ai = (SmartGameObjectAI)gameObject.GetAI(); if (ai != null) - ai.GetScript().StoreTargetList(new List(storedTargets), e.Action.sendTargetToTarget.id); // store a copy of target list + ai.GetScript().StoreTargetList(storedTargets, e.Action.sendTargetToTarget.id); // store a copy of target list else Log.outError(LogFilter.Sql, "SmartScript: Action target for SMART_ACTION_SEND_TARGET_TO_TARGET is not using SmartGameObjectAI, skipping"); } @@ -2052,6 +2087,9 @@ namespace Game.AI foreach (var summon in summonList) if (unit == null && e.Action.creatureGroup.attackInvoker != 0) summon.GetAI().AttackStart(unit); + + if (e.Action.creatureGroup.storedTargetId != 0) + StoreTargetList(summonList.Cast().ToList(), e.Action.creatureGroup.storedTargetId); break; } case SmartActions.SetPower: @@ -4442,6 +4480,11 @@ namespace Game.AI return false; } + void ClearTargetList(uint id) + { + _storedTargets.Remove(id); + } + void StoreTargetList(List targets, uint id) { // insert or replace