/* * Copyright (C) 2012-2018 CypherCore * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using Framework.Constants; using Framework.Database; using Framework.GameMath; using Game.Achievements; using Game.DataStorage; using Game.Maps; using System; using System.Collections.Generic; namespace Game.Scenarios { public class ScenarioManager : Singleton { ScenarioManager() { } public InstanceScenario CreateInstanceScenario(Map map, int team) { var dbData = _scenarioDBData.LookupByKey(Tuple.Create(map.GetId(), (byte)map.GetDifficultyID())); // No scenario registered for this map and difficulty in the database if (dbData == null) return null; uint scenarioID = 0; switch (team) { case TeamId.Alliance: scenarioID = dbData.Scenario_A; break; case TeamId.Horde: scenarioID = dbData.Scenario_H; break; default: break; } var scenarioData = _scenarioData.LookupByKey(scenarioID); if (scenarioData == null) { Log.outError(LogFilter.Scenario, "Table `scenarios` contained data linking scenario (Id: {0}) to map (Id: {1}), difficulty (Id: {2}) but no scenario data was found related to that scenario Id.", scenarioID, map.GetId(), map.GetDifficultyID()); return null; } return new InstanceScenario(map, scenarioData); } public void LoadDBData() { _scenarioDBData.Clear(); uint oldMSTime = Time.GetMSTime(); SQLResult result = DB.World.Query("SELECT map, difficulty, scenario_A, scenario_H FROM scenarios"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 scenarios. DB table `scenarios` is empty!"); return; } do { uint mapId = result.Read(0); byte difficulty = result.Read(1); uint scenarioAllianceId = result.Read(2); if (scenarioAllianceId > 0 && !_scenarioData.ContainsKey(scenarioAllianceId)) { Log.outError(LogFilter.Sql, "ScenarioMgr.LoadDBData: DB Table `scenarios`, column scenario_A contained an invalid scenario (Id: {0})!", scenarioAllianceId); continue; } uint scenarioHordeId = result.Read(3); if (scenarioHordeId > 0 && !_scenarioData.ContainsKey(scenarioHordeId)) { Log.outError(LogFilter.Sql, "ScenarioMgr.LoadDBData: DB Table `scenarios`, column scenario_H contained an invalid scenario (Id: {0})!", scenarioHordeId); continue; } if (scenarioHordeId == 0) scenarioHordeId = scenarioAllianceId; ScenarioDBData data = new ScenarioDBData(); data.MapID = mapId; data.DifficultyID = difficulty; data.Scenario_A = scenarioAllianceId; data.Scenario_H = scenarioHordeId; _scenarioDBData[Tuple.Create(mapId, difficulty)] = data; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} instance scenario entries in {1} ms", _scenarioDBData.Count, Time.GetMSTimeDiffToNow(oldMSTime)); } public void LoadDB2Data() { _scenarioData.Clear(); Dictionary> scenarioSteps = new Dictionary>(); uint deepestCriteriaTreeSize = 0; foreach (ScenarioStepRecord step in CliDB.ScenarioStepStorage.Values) { if (!scenarioSteps.ContainsKey(step.ScenarioID)) scenarioSteps[step.ScenarioID] = new Dictionary(); scenarioSteps[step.ScenarioID][step.OrderIndex] = step; CriteriaTree tree = Global.CriteriaMgr.GetCriteriaTree(step.CriteriaTreeId); if (tree != null) { uint criteriaTreeSize = 0; CriteriaManager.WalkCriteriaTree(tree, treeFunc => { ++criteriaTreeSize; }); deepestCriteriaTreeSize = Math.Max(deepestCriteriaTreeSize, criteriaTreeSize); } } //ASSERT(deepestCriteriaTreeSize < MAX_ALLOWED_SCENARIO_POI_QUERY_SIZE, "MAX_ALLOWED_SCENARIO_POI_QUERY_SIZE must be at least {0}", deepestCriteriaTreeSize + 1); foreach (ScenarioRecord scenario in CliDB.ScenarioStorage.Values) { ScenarioData data = new ScenarioData(); data.Entry = scenario; data.Steps = scenarioSteps.LookupByKey(scenario.Id); _scenarioData[scenario.Id] = data; } } public void LoadScenarioPOI() { uint oldMSTime = Time.GetMSTime(); _scenarioPOIStore.Clear(); // need for reload case uint count = 0; // 0 1 2 6 7 8 9 10 11 12 SQLResult result = DB.World.Query("SELECT CriteriaTreeID, BlobIndex, Idx1, MapID, WorldMapAreaId, Floor, Priority, Flags, WorldEffectID, PlayerConditionID FROM scenario_poi ORDER BY CriteriaTreeID, Idx1"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 scenario POI definitions. DB table `scenario_poi` is empty."); return; } // 0 1 2 3 SQLResult points = DB.World.Query("SELECT CriteriaTreeID, Idx1, X, Y FROM scenario_poi_points ORDER BY CriteriaTreeID DESC, Idx1, Idx2"); List[][] POIs = new List[0][]; if (!points.IsEmpty()) { // The first result should have the highest criteriaTreeId uint criteriaTreeIdMax = result.Read(0); POIs = new List[criteriaTreeIdMax + 1][]; do { int CriteriaTreeID = points.Read(0); int Idx1 = points.Read(1); int X = points.Read(2); int Y = points.Read(3); if (POIs[CriteriaTreeID].Length <= Idx1 + 1) POIs[CriteriaTreeID] = new List[Idx1 + 10]; POIs[CriteriaTreeID][Idx1].Add(new Vector2(X, Y)); } while (points.NextRow()); } do { int CriteriaTreeID = result.Read(0); int BlobIndex = result.Read(1); int Idx1 = result.Read(2); int MapID = result.Read(3); int WorldMapAreaId = result.Read(4); int Floor = result.Read(5); int Priority = result.Read(6); int Flags = result.Read(7); int WorldEffectID = result.Read(8); int PlayerConditionID = result.Read(9); if (Global.CriteriaMgr.GetCriteriaTree((uint)CriteriaTreeID) == null) Log.outError(LogFilter.Sql, "`scenario_poi` CriteriaTreeID ({0}) Idx1 ({1}) does not correspond to a valid criteria tree", CriteriaTreeID, Idx1); if (CriteriaTreeID < POIs.Length && Idx1 < POIs[CriteriaTreeID].Length) _scenarioPOIStore.Add((uint)CriteriaTreeID, new ScenarioPOI(BlobIndex, MapID, WorldMapAreaId, Floor, Priority, Flags, WorldEffectID, PlayerConditionID, POIs[CriteriaTreeID][Idx1])); else Log.outError(LogFilter.ServerLoading, "Table scenario_poi references unknown scenario poi points for criteria tree id {0} POI id {1}", CriteriaTreeID, BlobIndex); ++count; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} scenario POI definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } public List GetScenarioPOIs(uint CriteriaTreeID) { if (!_scenarioPOIStore.ContainsKey(CriteriaTreeID)) return null; return _scenarioPOIStore[CriteriaTreeID]; } Dictionary _scenarioData = new Dictionary(); MultiMap _scenarioPOIStore = new MultiMap(); Dictionary, ScenarioDBData> _scenarioDBData = new Dictionary, ScenarioDBData>(); } public class ScenarioData { public ScenarioRecord Entry; public Dictionary Steps = new Dictionary(); } class ScenarioDBData { public uint MapID; public byte DifficultyID; public uint Scenario_A; public uint Scenario_H; } public class ScenarioPOI { public ScenarioPOI(int blobIndex, int mapID, int worldMapAreaID, int floor, int priority, int flags, int worldEffectID, int playerConditionID, List points) { BlobIndex = blobIndex; MapID = mapID; WorldMapAreaID = worldMapAreaID; Floor = floor; Priority = priority; Flags = flags; WorldEffectID = worldEffectID; PlayerConditionID = playerConditionID; Points = points; } public int BlobIndex; public int MapID; public int WorldMapAreaID; public int Floor; public int Priority; public int Flags; public int WorldEffectID; public int PlayerConditionID; public List Points = new List(); } }