diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs index f033dac75..83a0c125e 100644 --- a/Source/Framework/Constants/SmartAIConst.cs +++ b/Source/Framework/Constants/SmartAIConst.cs @@ -363,7 +363,8 @@ namespace Framework.Constants // 131 - 134 : 3.3.5 reserved PlayCinematic = 135, // reserved for future uses SetMovementSpeed = 136, // movementType, speedInteger, speedFraction - End = 131 + PlaySpellVisualKit = 137, // spellVisualKitId, kitType (unknown values, copypaste from packet dumps), duration + End = 138 } public enum SmartTargets diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs index 68fec74cc..de94b83e6 100644 --- a/Source/Framework/Database/Databases/HotfixDatabase.cs +++ b/Source/Framework/Database/Databases/HotfixDatabase.cs @@ -963,6 +963,10 @@ namespace Framework.Database PrepareStatement(HotfixStatements.SEL_SPELL_TOTEMS, "SELECT ID, SpellID, RequiredTotemCategoryID1, RequiredTotemCategoryID2, Totem1, Totem2" + " FROM spell_totems ORDER BY ID DESC"); + // SpellVisualKit.db2 + PrepareStatement(HotfixStatements.SEL_SPELL_VISUAL_KIT, "SELECT ID, Flags, FallbackPriority, FallbackSpellVisualKitId, DelayMin, DelayMax" + + " FROM spell_visual_kit ORDER BY ID DESC"); + // SpellXSpellVisual.db2 PrepareStatement(HotfixStatements.SEL_SPELL_X_SPELL_VISUAL, "SELECT ID, DifficultyID, SpellVisualID, Probability, Flags, Priority, SpellIconFileID, " + "ActiveIconFileID, ViewerUnitConditionID, ViewerPlayerConditionID, CasterUnitConditionID, CasterPlayerConditionID, SpellID" + @@ -1569,6 +1573,8 @@ namespace Framework.Database SEL_SPELL_TOTEMS, + SEL_SPELL_VISUAL_KIT, + SEL_SPELL_X_SPELL_VISUAL, SEL_SUMMON_PROPERTIES, diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index e3a55b82b..f2b064c00 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -430,6 +430,16 @@ namespace Game.AI return true; } + bool IsSpellVisualKitValid(SmartScriptHolder e, uint entry) + { + if (!CliDB.SpellVisualKitStorage.ContainsKey(entry)) + { + Log.outError(LogFilter.Sql, $"SmartAIMgr: Entry {e.entryOrGuid} SourceType {e.GetScriptType()} Event {e.event_id} Action {e.GetActionType()} uses non-existent SpellVisualKit entry {entry}, skipped."); + return false; + } + return true; + } + bool IsEventValid(SmartScriptHolder e) { if (e.Event.type >= SmartEvents.End) @@ -862,6 +872,10 @@ namespace Game.AI return false; } break; + case SmartActions.PlaySpellVisualKit: + if (e.Action.spellVisualKit.spellVisualKitId != 0 && !IsSpellVisualKitValid(e, e.Action.spellVisualKit.spellVisualKitId)) + return false; + break; case SmartActions.FailQuest: case SmartActions.OfferQuest: if (e.Action.quest.questId == 0 || !IsQuestValid(e, e.Action.quest.questId)) @@ -2344,6 +2358,9 @@ namespace Game.AI [FieldOffset(4)] public MovementSpeed movementSpeed; + [FieldOffset(4)] + public SpellVisualKit spellVisualKit; + [FieldOffset(4)] public Raw raw; @@ -2841,6 +2858,12 @@ namespace Game.AI public uint speedInteger; public uint speedFraction; } + public struct SpellVisualKit + { + public uint spellVisualKitId; + public uint kitType; + public uint duration; + } public struct Raw { public uint param1; diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 6ab8b6240..dc06a014b 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -2640,6 +2640,23 @@ namespace Game.AI break; } + case SmartActions.PlaySpellVisualKit: + { + var targets = GetTargets(e, unit); + if (targets.Empty()) + break; + + foreach (var obj in targets) + { + if (IsUnit(obj)) + { + obj.ToUnit().SendPlaySpellVisualKit(e.Action.spellVisualKit.spellVisualKitId, e.Action.spellVisualKit.kitType, e.Action.spellVisualKit.duration); + + Log.outDebug(LogFilter.ScriptsAi, $"SmartScript.ProcessAction:: SMART_ACTION_PLAY_SPELL_VISUAL_KIT: target: {obj.GetName()} ({obj.GetGUID().ToString()}), SpellVisualKit: {e.Action.spellVisualKit.spellVisualKitId}"); + } + } + break; + } default: Log.outError(LogFilter.Sql, "SmartScript.ProcessAction: Entry {0} SourceType {1}, Event {2}, Unhandled Action type {3}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); break; diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index 3239b54b5..fdbd1311d 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -248,6 +248,7 @@ namespace Game.DataStorage SpellShapeshiftFormStorage = DBReader.Read("SpellShapeshiftForm.db2", HotfixStatements.SEL_SPELL_SHAPESHIFT_FORM, HotfixStatements.SEL_SPELL_SHAPESHIFT_FORM_LOCALE); SpellTargetRestrictionsStorage = DBReader.Read("SpellTargetRestrictions.db2", HotfixStatements.SEL_SPELL_TARGET_RESTRICTIONS); SpellTotemsStorage = DBReader.Read("SpellTotems.db2", HotfixStatements.SEL_SPELL_TOTEMS); + SpellVisualKitStorage = DBReader.Read("SpellVisualKit.db2", HotfixStatements.SEL_SPELL_VISUAL_KIT); SpellXSpellVisualStorage = DBReader.Read("SpellXSpellVisual.db2", HotfixStatements.SEL_SPELL_X_SPELL_VISUAL); SummonPropertiesStorage = DBReader.Read("SummonProperties.db2", HotfixStatements.SEL_SUMMON_PROPERTIES); //TactKeyStorage = DBReader.Read("TactKey.db2", HotfixStatements.SEL_TACT_KEY); @@ -593,6 +594,7 @@ namespace Game.DataStorage public static DB6Storage SpellShapeshiftFormStorage; public static DB6Storage SpellTargetRestrictionsStorage; public static DB6Storage SpellTotemsStorage; + public static DB6Storage SpellVisualKitStorage; public static DB6Storage SpellXSpellVisualStorage; public static DB6Storage SummonPropertiesStorage; //public static DB6Storage TactKeyStorage; diff --git a/Source/Game/DataStorage/Structs/S_Records.cs b/Source/Game/DataStorage/Structs/S_Records.cs index 93281ad60..d1c132939 100644 --- a/Source/Game/DataStorage/Structs/S_Records.cs +++ b/Source/Game/DataStorage/Structs/S_Records.cs @@ -527,6 +527,16 @@ namespace Game.DataStorage public uint[] Totem = new uint[SpellConst.MaxTotems]; } + public sealed class SpellVisualKitRecord + { + public uint Id; + public int Flags; + public sbyte FallbackPriority; + public uint FallbackSpellVisualKitId; + public ushort DelayMin; + public ushort DelayMax; + } + public sealed class SpellXSpellVisualRecord { public uint Id;