diff --git a/Source/Framework/Constants/ConditionConst.cs b/Source/Framework/Constants/ConditionConst.cs index 4819ddf69..8d1ca5b21 100644 --- a/Source/Framework/Constants/ConditionConst.cs +++ b/Source/Framework/Constants/ConditionConst.cs @@ -108,6 +108,7 @@ namespace Framework.Constants AreaTrigger = 28, ConversationLine = 29, AreatriggerClientTriggered = 30, + TrainerSpell = 31, Max } diff --git a/Source/Game/Conditions/ConditionManager.cs b/Source/Game/Conditions/ConditionManager.cs index 4d15ee877..afe0528cf 100644 --- a/Source/Game/Conditions/ConditionManager.cs +++ b/Source/Game/Conditions/ConditionManager.cs @@ -161,7 +161,8 @@ namespace Game sourceType == ConditionSourceType.SmartEvent || sourceType == ConditionSourceType.NpcVendor || sourceType == ConditionSourceType.Phase || - sourceType == ConditionSourceType.AreaTrigger); + sourceType == ConditionSourceType.AreaTrigger || + sourceType == ConditionSourceType.TrainerSpell); } public bool CanHaveSourceIdSet(ConditionSourceType sourceType) @@ -270,7 +271,7 @@ namespace Game var conditions = multiMap.LookupByKey(itemId); if (!conditions.Empty()) { - Log.outDebug(LogFilter.Condition, "GetConditionsForNpcVendorEvent: found conditions for creature entry {0} item {1}", creatureId, itemId); + Log.outDebug(LogFilter.Condition, "GetConditionsForNpcVendor: found conditions for creature entry {0} item {1}", creatureId, itemId); ConditionSourceInfo sourceInfo = new(player, vendor); return IsObjectMeetToConditions(sourceInfo, conditions); } @@ -282,6 +283,21 @@ namespace Game { return areaTriggerConditionContainerStorage.LookupByKey(Tuple.Create(areaTriggerId, isServerSide)); } + + public bool IsObjectMeetingTrainerSpellConditions(uint trainerId, uint spellId, Player player) + { + var multiMap = trainerSpellConditionContainerStorage.LookupByKey(trainerId); + if (multiMap != null) + { + var conditionList = multiMap.LookupByKey(spellId); + if (!conditionList.Empty()) + { + Log.outDebug(LogFilter.Condition, $"GetConditionsForTrainerSpell: found conditions for trainer id {trainerId} spell {spellId}"); + return IsObjectMeetToConditions(player, conditionList); + } + } + return true; + } public void LoadConditions(bool isReload = false) { @@ -512,6 +528,15 @@ namespace Game areaTriggerConditionContainerStorage.Add(Tuple.Create(cond.SourceGroup, cond.SourceEntry != 0), cond); ++count; continue; + case ConditionSourceType.TrainerSpell: + { + if (!trainerSpellConditionContainerStorage.ContainsKey(cond.SourceGroup)) + trainerSpellConditionContainerStorage[cond.SourceGroup] = new MultiMap(); + + trainerSpellConditionContainerStorage[cond.SourceGroup].Add((uint)cond.SourceEntry, cond); + ++count; + continue; + } default: break; } @@ -947,7 +972,7 @@ namespace Game SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo((uint)cond.SourceEntry, Difficulty.None); if (spellInfo == null) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.dbc`, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.db2`, ignoring.", cond.ToString()); return false; } @@ -1014,7 +1039,7 @@ namespace Game SpellInfo spellProto = Global.SpellMgr.GetSpellInfo((uint)cond.SourceEntry, Difficulty.None); if (spellProto == null) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.dbc`, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.db2`, ignoring.", cond.ToString()); return false; } break; @@ -1029,26 +1054,26 @@ namespace Game case ConditionSourceType.VehicleSpell: if (Global.ObjectMgr.GetCreatureTemplate(cond.SourceGroup) == null) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `creature_template`, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceGroup in `condition` table does not exist in `creature_template`, ignoring.", cond.ToString()); return false; } if (!Global.SpellMgr.HasSpellInfo((uint)cond.SourceEntry, Difficulty.None)) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.dbc`, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.db2`, ignoring.", cond.ToString()); return false; } break; case ConditionSourceType.SpellClickEvent: if (Global.ObjectMgr.GetCreatureTemplate(cond.SourceGroup) == null) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `creature_template`, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceGroup in `condition` table does not exist in `creature_template`, ignoring.", cond.ToString()); return false; } if (!Global.SpellMgr.HasSpellInfo((uint)cond.SourceEntry, Difficulty.None)) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.dbc`, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `spell.db2`, ignoring.", cond.ToString()); return false; } break; @@ -1056,7 +1081,7 @@ namespace Game { if (Global.ObjectMgr.GetCreatureTemplate(cond.SourceGroup) == null) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in `creature_template`, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceGroup in `condition` table does not exist in `creature_template`, ignoring.", cond.ToString()); return false; } ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate((uint)cond.SourceEntry); @@ -1070,14 +1095,14 @@ namespace Game case ConditionSourceType.TerrainSwap: if (!CliDB.MapStorage.ContainsKey((uint)cond.SourceEntry)) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in Map.dbc, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in Map.db2, ignoring.", cond.ToString()); return false; } break; case ConditionSourceType.Phase: if (cond.SourceEntry != 0 && !CliDB.AreaTableStorage.ContainsKey(cond.SourceEntry)) { - Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in AreaTable.dbc, ignoring.", cond.ToString()); + Log.outError(LogFilter.Sql, "{0} SourceEntry in `condition` table does not exist in AreaTable.db2, ignoring.", cond.ToString()); return false; } break; @@ -1118,6 +1143,20 @@ namespace Game return false; } break; + case ConditionSourceType.TrainerSpell: + { + if (Global.ObjectMgr.GetTrainer(cond.SourceGroup) == null) + { + Log.outError(LogFilter.Sql, $"{cond.ToString()} SourceGroup in `condition` table, does not exist in `trainer`, ignoring."); + return false; + } + if (Global.SpellMgr.GetSpellInfo((uint)cond.SourceEntry, Difficulty.None) == null) + { + Log.outError(LogFilter.Sql, $"{cond.ToString()} SourceEntry in `condition` table does not exist in `Spell.db2`, ignoring."); + return false; + } + break; + } default: Log.outError(LogFilter.Sql, $"{cond.ToString()} Invalid ConditionSourceType in `condition` table, ignoring."); return false; @@ -1660,6 +1699,10 @@ namespace Game spellClickEventConditionStorage.Clear(); npcVendorConditionContainerStorage.Clear(); + + areaTriggerConditionContainerStorage.Clear(); + + trainerSpellConditionContainerStorage.Clear(); } static bool PlayerConditionCompare(int comparisonType, int value1, int value2) @@ -2442,6 +2485,7 @@ namespace Game Dictionary> npcVendorConditionContainerStorage = new(); Dictionary, MultiMap> smartEventConditionStorage = new(); MultiMap, Condition> areaTriggerConditionContainerStorage = new(); + Dictionary> trainerSpellConditionContainerStorage = new(); public string[] StaticSourceTypeData = { @@ -2473,7 +2517,10 @@ namespace Game "Terrain Swap", "Phase", "Graveyard", - "AreaTrigger" + "AreaTrigger", + "ConversationLine", + "AreaTrigger Client Triggered", + "Trainer Spell" }; public ConditionTypeInfo[] StaticConditionTypeData = diff --git a/Source/Game/Entities/Creature/Trainer.cs b/Source/Game/Entities/Creature/Trainer.cs index 933c5a9b1..95b8c6ec4 100644 --- a/Source/Game/Entities/Creature/Trainer.cs +++ b/Source/Game/Entities/Creature/Trainer.cs @@ -45,6 +45,12 @@ namespace Game.Entities if (!player.IsSpellFitByClassAndRace(trainerSpell.SpellId)) continue; + if (!Global.ConditionMgr.IsObjectMeetingTrainerSpellConditions(_id, trainerSpell.SpellId, player)) + { + Log.outDebug(LogFilter.Condition, $"SendSpells: conditions not met for trainer id {_id} spell {trainerSpell.SpellId} player '{player.GetName()}' ({player.GetGUID()})"); + continue; + } + TrainerListSpell trainerListSpell = new(); trainerListSpell.SpellID = trainerSpell.SpellId; trainerListSpell.MoneyCost = (uint)(trainerSpell.MoneyCost * reputationDiscount);