diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs index 30970bd4c..d1fadf30c 100644 --- a/Source/Framework/Constants/SmartAIConst.cs +++ b/Source/Framework/Constants/SmartAIConst.cs @@ -80,21 +80,22 @@ namespace Framework.Constants Phase11Bit + Phase12Bit } + [Flags] public enum SmartEventFlags { NotRepeatable = 0x01, //Event can not repeat - Difficulty0 = 0x02, //Event only occurs in instance difficulty 0 - Difficulty1 = 0x04, //Event only occurs in instance difficulty 1 - Difficulty2 = 0x08, //Event only occurs in instance difficulty 2 - Difficulty3 = 0x10, //Event only occurs in instance difficulty 3 + Difficulty0_Deprecated = 0x02, //Event only occurs in instance difficulty 0 + Difficulty1_Deprecated = 0x04, //Event only occurs in instance difficulty 1 + Difficulty2_Deprecated = 0x08, //Event only occurs in instance difficulty 2 + Difficulty3_Deprecated = 0x10, //Event only occurs in instance difficulty 3 Reserved5 = 0x20, Reserved6 = 0x40, DebugOnly = 0x80, //Event only occurs in debug build DontReset = 0x100, //Event will not reset in SmartScript.OnReset() WhileCharmed = 0x200, //Event occurs even if AI owner is charmed - DifficultyAll = (Difficulty0 | Difficulty1 | Difficulty2 | Difficulty3), - All = (NotRepeatable | DifficultyAll | Reserved5 | Reserved6 | DebugOnly | DontReset | WhileCharmed), + Deprecated = (Difficulty0_Deprecated | Difficulty1_Deprecated | Difficulty2_Deprecated | Difficulty3_Deprecated), + All = (NotRepeatable | Deprecated | Reserved5 | Reserved6 | DebugOnly | DontReset | WhileCharmed), // Temp flags, used only at runtime, never stored in DB TempIgnoreChanceRoll = 0x40000000, //Event occurs no matter what roll_chance_i(e.event.event_chance) returns. diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs index cb1c82f45..a55c3a8a4 100644 --- a/Source/Framework/Database/Databases/WorldDatabase.cs +++ b/Source/Framework/Database/Databases/WorldDatabase.cs @@ -11,7 +11,7 @@ namespace Framework.Database PrepareStatement(WorldStatements.DEL_LINKED_RESPAWN_MASTER, "DELETE FROM linked_respawn WHERE linkedGuid = ? AND linkType = ?"); PrepareStatement(WorldStatements.REP_LINKED_RESPAWN, "REPLACE INTO linked_respawn (guid, linkedGuid, linkType) VALUES (?, ?, ?)"); PrepareStatement(WorldStatements.SEL_CREATURE_TEXT, "SELECT CreatureID, GroupID, ID, Text, Type, Language, Probability, Emote, Duration, Sound, SoundPlayType, BroadcastTextId, TextRange FROM creature_text"); - PrepareStatement(WorldStatements.SEL_SMART_SCRIPTS, "SELECT entryorguid, source_type, id, link, event_type, event_phase_mask, event_chance, event_flags, event_param1, event_param2, event_param3, event_param4, event_param5, event_param_string, action_type, action_param1, action_param2, action_param3, action_param4, action_param5, action_param6, action_param7, target_type, target_param1, target_param2, target_param3, target_param4, target_x, target_y, target_z, target_o FROM smart_scripts ORDER BY entryorguid, source_type, id, link"); + PrepareStatement(WorldStatements.SEL_SMART_SCRIPTS, "SELECT entryorguid, source_type, id, link, Difficulties, event_type, event_phase_mask, event_chance, event_flags, event_param1, event_param2, event_param3, event_param4, event_param5, event_param_string, action_type, action_param1, action_param2, action_param3, action_param4, action_param5, action_param6, action_param7, target_type, target_param1, target_param2, target_param3, target_param4, target_x, target_y, target_z, target_o FROM smart_scripts ORDER BY entryorguid, source_type, id, link"); PrepareStatement(WorldStatements.DEL_GAMEOBJECT, "DELETE FROM gameobject WHERE guid = ?"); PrepareStatement(WorldStatements.DEL_EVENT_GAMEOBJECT, "DELETE FROM game_event_gameobject WHERE guid = ?"); PrepareStatement(WorldStatements.INS_GRAVEYARD_ZONE, "INSERT INTO graveyard_zone (ID, GhostZone) VALUES (?, ?)"); diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index 968e1d7a9..73c177612 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -1,6 +1,7 @@ // Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. +using Framework.Collections; using Framework.Constants; using Framework.Database; using Game.DataStorage; @@ -55,7 +56,7 @@ namespace Game.AI SmartScriptType source_type = (SmartScriptType)result.Read(1); if (source_type >= SmartScriptType.Max) { - Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: invalid source_type ({0}), skipped loading.", source_type); + Log.outError(LogFilter.Sql, "SmartAIMgr.LoadFromDB: invalid source_type ({0}), skipped loading.", source_type); continue; } if (temp.EntryOrGuid >= 0) @@ -65,7 +66,7 @@ namespace Game.AI case SmartScriptType.Creature: if (Global.ObjectMgr.GetCreatureTemplate((uint)temp.EntryOrGuid) == null) { - Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: Creature entry ({0}) does not exist, skipped loading.", temp.EntryOrGuid); + Log.outError(LogFilter.Sql, "SmartAIMgr.LoadFromDB: Creature entry ({0}) does not exist, skipped loading.", temp.EntryOrGuid); continue; } break; @@ -74,7 +75,7 @@ namespace Game.AI { if (Global.ObjectMgr.GetGameObjectTemplate((uint)temp.EntryOrGuid) == null) { - Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: GameObject entry ({0}) does not exist, skipped loading.", temp.EntryOrGuid); + Log.outError(LogFilter.Sql, "SmartAIMgr.LoadFromDB: GameObject entry ({0}) does not exist, skipped loading.", temp.EntryOrGuid); continue; } break; @@ -83,7 +84,7 @@ namespace Game.AI { if (CliDB.AreaTableStorage.LookupByKey((uint)temp.EntryOrGuid) == null) { - Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: AreaTrigger entry ({0}) does not exist, skipped loading.", temp.EntryOrGuid); + Log.outError(LogFilter.Sql, "SmartAIMgr.LoadFromDB: AreaTrigger entry ({0}) does not exist, skipped loading.", temp.EntryOrGuid); continue; } break; @@ -101,7 +102,7 @@ namespace Game.AI { if (!Global.ObjectMgr.IsValidEvent((uint)temp.EntryOrGuid)) { - Log.outError(LogFilter.Sql, $"SmartAIMgr::LoadSmartAIFromDB: Event id ({temp.EntryOrGuid}) does not exist, skipped loading."); + Log.outError(LogFilter.Sql, $"SmartAIMgr::LoadFromDB: Event id ({temp.EntryOrGuid}) does not exist, skipped loading."); continue; } break; @@ -199,36 +200,60 @@ namespace Game.AI temp.SourceType = source_type; temp.EventId = result.Read(2); temp.Link = result.Read(3); - temp.Event.type = (SmartEvents)result.Read(4); - temp.Event.event_phase_mask = result.Read(5); - temp.Event.event_chance = result.Read(6); - temp.Event.event_flags = (SmartEventFlags)result.Read(7); - temp.Event.raw.param1 = result.Read(8); - temp.Event.raw.param2 = result.Read(9); - temp.Event.raw.param3 = result.Read(10); - temp.Event.raw.param4 = result.Read(11); - temp.Event.raw.param5 = result.Read(12); - temp.Event.param_string = result.Read(13); + bool invalidDifficulties = false; + foreach (string token in new StringArray(result.Read(4), ' ')) + { + if (!Enum.TryParse(token, out Difficulty difficultyId)) + { + invalidDifficulties = true; + Log.outError(LogFilter.Sql, $"SmartAIMgr::LoadFromDB: Invalid difficulties for entryorguid ({temp.EntryOrGuid}) source_type ({temp.GetScriptType()}) id ({temp.EventId}), skipped loading."); + break; + } - temp.Action.type = (SmartActions)result.Read(14); - temp.Action.raw.param1 = result.Read(15); - temp.Action.raw.param2 = result.Read(16); - temp.Action.raw.param3 = result.Read(17); - temp.Action.raw.param4 = result.Read(18); - temp.Action.raw.param5 = result.Read(19); - temp.Action.raw.param6 = result.Read(20); - temp.Action.raw.param7 = result.Read(21); + if (difficultyId != 0 && !CliDB.DifficultyStorage.ContainsKey(difficultyId)) + { + invalidDifficulties = true; + Log.outError(LogFilter.Sql, $"SmartAIMgr::LoadFromDB: Invalid difficulty id ({difficultyId}) for entryorguid ({temp.EntryOrGuid}) source_type ({temp.GetScriptType()}) id ({temp.EventId}), skipped loading."); + break; + } - temp.Target.type = (SmartTargets)result.Read(22); - temp.Target.raw.param1 = result.Read(23); - temp.Target.raw.param2 = result.Read(24); - temp.Target.raw.param3 = result.Read(25); - temp.Target.raw.param4 = result.Read(26); - temp.Target.x = result.Read(27); - temp.Target.y = result.Read(28); - temp.Target.z = result.Read(29); - temp.Target.o = result.Read(30); + temp.Difficulties.Add(difficultyId); + } + + if (invalidDifficulties) + continue; + + temp.Event.type = (SmartEvents)result.Read(5); + temp.Event.event_phase_mask = result.Read(6); + temp.Event.event_chance = result.Read(7); + temp.Event.event_flags = (SmartEventFlags)result.Read(8); + + temp.Event.raw.param1 = result.Read(9); + temp.Event.raw.param2 = result.Read(10); + temp.Event.raw.param3 = result.Read(11); + temp.Event.raw.param4 = result.Read(12); + temp.Event.raw.param5 = result.Read(13); + temp.Event.param_string = result.Read(14); + + temp.Action.type = (SmartActions)result.Read(15); + temp.Action.raw.param1 = result.Read(16); + temp.Action.raw.param2 = result.Read(17); + temp.Action.raw.param3 = result.Read(18); + temp.Action.raw.param4 = result.Read(19); + temp.Action.raw.param5 = result.Read(20); + temp.Action.raw.param6 = result.Read(21); + temp.Action.raw.param7 = result.Read(22); + + temp.Target.type = (SmartTargets)result.Read(23); + temp.Target.raw.param1 = result.Read(24); + temp.Target.raw.param2 = result.Read(25); + temp.Target.raw.param3 = result.Read(26); + temp.Target.raw.param4 = result.Read(27); + temp.Target.x = result.Read(28); + temp.Target.y = result.Read(29); + temp.Target.z = result.Read(30); + temp.Target.o = result.Read(31); //check target if (!IsTargetValid(temp)) @@ -908,6 +933,11 @@ namespace Game.AI Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: EntryOrGuid {0} using event({1}) has invalid event flags ({2}), skipped.", e.EntryOrGuid, e.EventId, e.Event.event_flags); return false; } + if (e.Event.event_flags.HasFlag(SmartEventFlags.Deprecated)) + { + Log.outError(LogFilter.Sql, $"SmartAIMgr: EntryOrGuid {e.EntryOrGuid} using event ({e.EventId}) has deprecated event flags ({e.Event.event_flags}), skipped."); + return false; + } if (e.Link != 0 && e.Link == e.EventId) { Log.outError(LogFilter.Sql, "SmartAIMgr: EntryOrGuid {0} SourceType {1}, Event {2}, Event is linking self (infinite loop), skipped.", e.EntryOrGuid, e.GetScriptType(), e.EventId); @@ -2434,6 +2464,8 @@ namespace Game.AI public SmartScriptType SourceType; public uint EventId; public uint Link; + public List Difficulties = new(); + public SmartEvent Event; public SmartAction Action; public SmartTarget Target; diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 2fb104bed..38c41adc2 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -3887,44 +3887,27 @@ namespace Game.AI Log.outDebug(LogFilter.ScriptsAi, $"SmartScript: EventMap for Event {eventId} is empty but is using SmartScript."); return; } - foreach (var holder in e) + + foreach (var scriptholder in e) { - if (holder.Event.event_flags.HasAnyFlag(SmartEventFlags.DifficultyAll))//if has instance flag add only if in it + if (obj != null && !scriptholder.Difficulties.Empty()) { - if (!(obj != null && obj.GetMap().IsDungeon())) - continue; - - // TODO: fix it for new maps and difficulties - switch (obj.GetMap().GetDifficultyID()) + bool foundValidDifficulty = false; + foreach (Difficulty difficulty in scriptholder.Difficulties) { - - - case Difficulty.Normal: - case Difficulty.Raid10N: - if (holder.Event.event_flags.HasAnyFlag(SmartEventFlags.Difficulty0)) - _events.Add(holder); + if (difficulty == obj.GetMap().GetDifficultyID()) + { + foundValidDifficulty = true; break; - case Difficulty.Heroic: - case Difficulty.Raid25N: - if (holder.Event.event_flags.HasAnyFlag(SmartEventFlags.Difficulty1)) - _events.Add(holder); - break; - case Difficulty.Raid10HC: - if (holder.Event.event_flags.HasAnyFlag(SmartEventFlags.Difficulty2)) - _events.Add(holder); - break; - case Difficulty.Raid25HC: - if (holder.Event.event_flags.HasAnyFlag(SmartEventFlags.Difficulty3)) - _events.Add(holder); - break; - default: - break; - + } } + + if (!foundValidDifficulty) + continue; } - _allEventFlags |= holder.Event.event_flags; - _events.Add(holder);//NOTE: 'world(0)' events still get processed in ANY instance mode + _allEventFlags |= scriptholder.Event.event_flags; + _events.Add(scriptholder); } }