/* * 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 Game.Arenas; using Game.DataStorage; using Game.Entities; using Game.Network.Packets; using System; using System.Collections.Generic; using System.Linq; using Game.BattleGrounds.Zones; namespace Game.BattleGrounds { public class BattlegroundManager : Singleton { BattlegroundManager() { m_NextRatedArenaUpdate = WorldConfig.GetUIntValue(WorldCfg.ArenaRatedUpdateTimer); for (var i = 0; i < (int)BattlegroundQueueTypeId.Max; ++i) m_BattlegroundQueues[i] = new BattlegroundQueue(); } public void DeleteAllBattlegrounds() { foreach (var data in bgDataStore.Values) { data.m_Battlegrounds.Clear(); data.BGFreeSlotQueue.Clear(); } bgDataStore.Clear(); } public void Update(uint diff) { m_UpdateTimer += diff; if (m_UpdateTimer > 1000) { foreach (var data in bgDataStore.Values) { var bgs = data.m_Battlegrounds; // first one is template and should not be deleted foreach (var pair in bgs.ToList()) { Battleground bg = pair.Value; bg.Update(m_UpdateTimer); if (bg.ToBeDeleted()) { bgs.Remove(pair.Key); var clients = data.m_ClientBattlegroundIds[(int)bg.GetBracketId()]; if (!clients.Empty()) clients.Remove(bg.GetClientInstanceID()); bg.Dispose(); } } } m_UpdateTimer = 0; } // update events timer for (var qtype = BattlegroundQueueTypeId.None; qtype < BattlegroundQueueTypeId.Max; ++qtype) m_BattlegroundQueues[(int)qtype].UpdateEvents(diff); // update scheduled queues if (!m_QueueUpdateScheduler.Empty()) { List scheduled = new List(); Extensions.Swap(ref scheduled, ref m_QueueUpdateScheduler); for (byte i = 0; i < scheduled.Count; i++) { uint arenaMMRating = (uint)(scheduled[i] >> 32); byte arenaType = (byte)(scheduled[i] >> 24 & 255); BattlegroundQueueTypeId bgQueueTypeId = (BattlegroundQueueTypeId)(scheduled[i] >> 16 & 255); BattlegroundTypeId bgTypeId = (BattlegroundTypeId)((scheduled[i] >> 8) & 255); BattlegroundBracketId bracket_id = (BattlegroundBracketId)(scheduled[i] & 255); m_BattlegroundQueues[(int)bgQueueTypeId].BattlegroundQueueUpdate(diff, bgTypeId, bracket_id, arenaType, arenaMMRating > 0, arenaMMRating); } } // if rating difference counts, maybe force-update queues if (WorldConfig.GetIntValue(WorldCfg.ArenaMaxRatingDifference) != 0 && WorldConfig.GetIntValue(WorldCfg.ArenaRatedUpdateTimer) != 0) { // it's time to force update if (m_NextRatedArenaUpdate < diff) { // forced update for rated arenas (scan all, but skipped non rated) Log.outDebug(LogFilter.Arena, "BattlegroundMgr: UPDATING ARENA QUEUES"); for (var qtype = BattlegroundQueueTypeId.Arena2v2; qtype <= BattlegroundQueueTypeId.Arena5v5; ++qtype) { for (int bracket = (int)BattlegroundBracketId.First; bracket < (int)BattlegroundBracketId.Max; ++bracket) { m_BattlegroundQueues[(int)qtype].BattlegroundQueueUpdate(diff, BattlegroundTypeId.AA, (BattlegroundBracketId)bracket, (byte)BGArenaType(qtype), true, 0); } } m_NextRatedArenaUpdate = WorldConfig.GetUIntValue(WorldCfg.ArenaRatedUpdateTimer); } else m_NextRatedArenaUpdate -= diff; } } void BuildBattlegroundStatusHeader(ref BattlefieldStatusHeader header, Battleground bg, Player player, uint ticketId, uint joinTime, ArenaTypes arenaType) { header.Ticket = new RideTicket(); header.Ticket.RequesterGuid = player.GetGUID(); header.Ticket.Id = ticketId; header.Ticket.Type = RideType.Battlegrounds; header.Ticket.Time = (int)joinTime; header.QueueID = bg.GetQueueId(); header.RangeMin = (byte)bg.GetMinLevel(); header.RangeMax = (byte)bg.GetMaxLevel(); header.TeamSize = (byte)(bg.isArena() ? arenaType : 0); header.InstanceID = bg.GetClientInstanceID(); header.RegisteredMatch = bg.isRated(); header.TournamentRules = false; } public void BuildBattlegroundStatusNone(out BattlefieldStatusNone battlefieldStatus, Player player, uint ticketId, uint joinTime) { battlefieldStatus = new BattlefieldStatusNone(); battlefieldStatus.Ticket.RequesterGuid = player.GetGUID(); battlefieldStatus.Ticket.Id = ticketId; battlefieldStatus.Ticket.Type = RideType.Battlegrounds; battlefieldStatus.Ticket.Time = (int)joinTime; } public void BuildBattlegroundStatusNeedConfirmation(out BattlefieldStatusNeedConfirmation battlefieldStatus, Battleground bg, Player player, uint ticketId, uint joinTime, uint timeout, ArenaTypes arenaType) { battlefieldStatus = new BattlefieldStatusNeedConfirmation(); BuildBattlegroundStatusHeader(ref battlefieldStatus.Hdr, bg, player, ticketId, joinTime, arenaType); battlefieldStatus.Mapid = bg.GetMapId(); battlefieldStatus.Timeout = timeout; battlefieldStatus.Role = 0; } public void BuildBattlegroundStatusActive(out BattlefieldStatusActive battlefieldStatus, Battleground bg, Player player, uint ticketId, uint joinTime, ArenaTypes arenaType) { battlefieldStatus = new BattlefieldStatusActive(); BuildBattlegroundStatusHeader(ref battlefieldStatus.Hdr, bg, player, ticketId, joinTime, arenaType); battlefieldStatus.ShutdownTimer = bg.GetRemainingTime(); battlefieldStatus.ArenaFaction = (byte)(player.GetBGTeam() == Team.Horde ? TeamId.Horde : TeamId.Alliance); battlefieldStatus.LeftEarly = false; battlefieldStatus.StartTimer = bg.GetElapsedTime(); battlefieldStatus.Mapid = bg.GetMapId(); } public void BuildBattlegroundStatusQueued(out BattlefieldStatusQueued battlefieldStatus, Battleground bg, Player player, uint ticketId, uint joinTime, uint avgWaitTime, ArenaTypes arenaType, bool asGroup) { battlefieldStatus = new BattlefieldStatusQueued(); BuildBattlegroundStatusHeader(ref battlefieldStatus.Hdr, bg, player, ticketId, joinTime, arenaType); battlefieldStatus.AverageWaitTime = avgWaitTime; battlefieldStatus.AsGroup = asGroup; battlefieldStatus.SuspendedQueue = false; battlefieldStatus.EligibleForMatchmaking = true; battlefieldStatus.WaitTime = Time.GetMSTimeDiffToNow(joinTime); } public void BuildBattlegroundStatusFailed(out BattlefieldStatusFailed battlefieldStatus, Battleground bg, Player pPlayer, uint ticketId, ArenaTypes arenaType, GroupJoinBattlegroundResult result, ObjectGuid errorGuid = default(ObjectGuid)) { battlefieldStatus = new BattlefieldStatusFailed(); battlefieldStatus.Ticket.RequesterGuid = pPlayer.GetGUID(); battlefieldStatus.Ticket.Id = ticketId; battlefieldStatus.Ticket.Type = RideType.Battlegrounds; battlefieldStatus.Ticket.Time = (int)pPlayer.GetBattlegroundQueueJoinTime(BGQueueTypeId(bg.GetTypeID(), arenaType)); battlefieldStatus.QueueID = bg.GetQueueId(); battlefieldStatus.Reason = (int)result; if (!errorGuid.IsEmpty() && (result == GroupJoinBattlegroundResult.NotInBattleground || result == GroupJoinBattlegroundResult.JoinTimedOut)) battlefieldStatus.ClientID = errorGuid; } public Battleground GetBattleground(uint instanceId, BattlegroundTypeId bgTypeId) { if (instanceId == 0) return null; if (bgTypeId != BattlegroundTypeId.None) { var data = bgDataStore.LookupByKey(bgTypeId); return data.m_Battlegrounds.LookupByKey(instanceId); } foreach (var it in bgDataStore) { var bgs = it.Value.m_Battlegrounds; var bg = bgs.LookupByKey(instanceId); if (bg) return bg; } return null; } public Battleground GetBattlegroundTemplate(BattlegroundTypeId bgTypeId) { if (bgDataStore.ContainsKey(bgTypeId)) return bgDataStore[bgTypeId].Template; return null; } uint CreateClientVisibleInstanceId(BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id) { if (IsArenaType(bgTypeId)) return 0; //arenas don't have client-instanceids // we create here an instanceid, which is just for // displaying this to the client and without any other use.. // the client-instanceIds are unique for each Battleground-type // the instance-id just needs to be as low as possible, beginning with 1 // the following works, because std.set is default ordered with "<" // the optimalization would be to use as bitmask std.vector - but that would only make code unreadable var clientIds = bgDataStore[bgTypeId].m_ClientBattlegroundIds[(int)bracket_id]; uint lastId = 0; foreach (var id in clientIds) { if (++lastId != id) //if there is a gap between the ids, we will break.. break; lastId = id; } clientIds.Add(++lastId); return lastId; } // create a new Battleground that will really be used to play public Battleground CreateNewBattleground(BattlegroundTypeId originalBgTypeId, PvpDifficultyRecord bracketEntry, ArenaTypes arenaType, bool isRated) { BattlegroundTypeId bgTypeId = GetRandomBG(originalBgTypeId); // get the template BG Battleground bg_template = GetBattlegroundTemplate(bgTypeId); if (bg_template == null) { Log.outError(LogFilter.Battleground, "Battleground: CreateNewBattleground - bg template not found for {0}", bgTypeId); return null; } if (bgTypeId == BattlegroundTypeId.RB || bgTypeId == BattlegroundTypeId.AA) return null; // create a copy of the BG template Battleground bg = bg_template.GetCopy(); bool isRandom = bgTypeId != originalBgTypeId && !bg.isArena(); bg.SetBracket(bracketEntry); bg.SetInstanceID(Global.MapMgr.GenerateInstanceId()); bg.SetClientInstanceID(CreateClientVisibleInstanceId(isRandom ? BattlegroundTypeId.RB : bgTypeId, bracketEntry.GetBracketId())); bg.Reset(); // reset the new bg (set status to status_wait_queue from status_none) bg.SetStatus(BattlegroundStatus.WaitJoin); // start the joining of the bg bg.SetArenaType(arenaType); bg.SetTypeID(originalBgTypeId); bg.SetRandomTypeID(bgTypeId); bg.SetRated(isRated); bg.SetRandom(isRandom); bg.SetQueueId((ulong)bgTypeId | 0x1F10000000000000); // Set up correct min/max player counts for scoreboards if (bg.isArena()) { uint maxPlayersPerTeam = 0; switch (arenaType) { case ArenaTypes.Team2v2: maxPlayersPerTeam = 2; break; case ArenaTypes.Team3v3: maxPlayersPerTeam = 3; break; case ArenaTypes.Team5v5: maxPlayersPerTeam = 5; break; } bg.SetMaxPlayersPerTeam(maxPlayersPerTeam); bg.SetMaxPlayers(maxPlayersPerTeam * 2); } return bg; } // used to create the BG templates bool CreateBattleground(BattlegroundTemplate bgTemplate) { Battleground bg = GetBattlegroundTemplate(bgTemplate.Id); if (!bg) { // Create the BG switch (bgTemplate.Id) { //case BattlegroundTypeId.AV: // bg = new BattlegroundAV(); //break; case BattlegroundTypeId.WS: bg = new BgWarsongGluch(); break; case BattlegroundTypeId.AB: bg = new BgArathiBasin(); break; case BattlegroundTypeId.NA: bg = new NagrandArena(); break; case BattlegroundTypeId.BE: bg = new BladesEdgeArena(); break; case BattlegroundTypeId.EY: bg = new BgEyeofStorm(); break; case BattlegroundTypeId.RL: bg = new RuinsofLordaeronArena(); break; case BattlegroundTypeId.SA: bg = new BgStrandOfAncients(); break; case BattlegroundTypeId.DS: bg = new DalaranSewersArena(); break; case BattlegroundTypeId.RV: bg = new RingofValorArena(); break; //case BattlegroundTypeId.IC: //bg = new BattlegroundIC(); //break; case BattlegroundTypeId.AA: bg = new Battleground(); break; case BattlegroundTypeId.RB: bg = new Battleground(); bg.SetRandom(true); break; /* case BattlegroundTypeId.TP: bg = new BattlegroundTP(); break; case BattlegroundTypeId.BFG: bg = new BattlegroundBFG(); break; */ default: return false; } bg.SetTypeID(bgTemplate.Id); } bg.SetMapId((uint)bgTemplate.BattlemasterEntry.MapId[0]); bg.SetName(bgTemplate.BattlemasterEntry.Name[Global.WorldMgr.GetDefaultDbcLocale()]); bg.SetInstanceID(0); bg.SetArenaorBGType(bgTemplate.IsArena()); bg.SetMinPlayersPerTeam(bgTemplate.MinPlayersPerTeam); bg.SetMaxPlayersPerTeam(bgTemplate.MaxPlayersPerTeam); bg.SetMinPlayers(bgTemplate.MinPlayersPerTeam * 2); bg.SetMaxPlayers(bgTemplate.MaxPlayersPerTeam * 2); bg.SetTeamStartPosition(TeamId.Alliance, bgTemplate.StartLocation[TeamId.Alliance]); bg.SetTeamStartPosition(TeamId.Horde, bgTemplate.StartLocation[TeamId.Horde]); bg.SetStartMaxDist(bgTemplate.StartMaxDist); bg.SetLevelRange(bgTemplate.MinLevel, bgTemplate.MaxLevel); bg.SetScriptId(bgTemplate.scriptId); bg.SetQueueId((ulong)bgTemplate.Id | 0x1F10000000000000); if (!bgDataStore.ContainsKey(bg.GetTypeID())) bgDataStore[bg.GetTypeID()] = new BattlegroundData(); bgDataStore[bg.GetTypeID()].Template = bg; return true; } public void LoadBattlegroundTemplates() { uint oldMSTime = Time.GetMSTime(); _BattlegroundMapTemplates.Clear(); _BattlegroundTemplates.Clear(); // 0 1 2 3 4 5 6 7 8 9 SQLResult result = DB.World.Query("SELECT ID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, AllianceStartLoc, HordeStartLoc, StartMaxDist, Weight, ScriptName FROM battleground_template"); if (result.IsEmpty()) { Log.outError(LogFilter.ServerLoading, "Loaded 0 Battlegrounds. DB table `Battleground_template` is empty."); return; } uint count = 0; do { BattlegroundTypeId bgTypeId = (BattlegroundTypeId)result.Read(0); if (Global.DisableMgr.IsDisabledFor(DisableType.Battleground, (uint)bgTypeId, null)) continue; // can be overwrite by values from DB BattlemasterListRecord bl = CliDB.BattlemasterListStorage.LookupByKey(bgTypeId); if (bl == null) { Log.outError(LogFilter.Battleground, "Battleground ID {0} not found in BattlemasterList.dbc. Battleground not created.", bgTypeId); continue; } BattlegroundTemplate bgTemplate = new BattlegroundTemplate(); bgTemplate.Id = bgTypeId; bgTemplate.MinPlayersPerTeam = result.Read(1); bgTemplate.MaxPlayersPerTeam = result.Read(2); bgTemplate.MinLevel = result.Read(3); bgTemplate.MaxLevel = result.Read(4); float dist = result.Read(7); bgTemplate.StartMaxDist = dist * dist; bgTemplate.Weight = result.Read(8); bgTemplate.scriptId = Global.ObjectMgr.GetScriptId(result.Read(9)); bgTemplate.BattlemasterEntry = bl; if (bgTemplate.MaxPlayersPerTeam == 0 || bgTemplate.MinPlayersPerTeam > bgTemplate.MaxPlayersPerTeam) { Log.outError(LogFilter.Sql, "Table `Battleground_template` for Id {0} has bad values for MinPlayersPerTeam ({1}) and MaxPlayersPerTeam({2})", bgTemplate.Id, bgTemplate.MinPlayersPerTeam, bgTemplate.MaxPlayersPerTeam); continue; } if (bgTemplate.MinLevel == 0 || bgTemplate.MaxLevel == 0 || bgTemplate.MinLevel > bgTemplate.MaxLevel) { Log.outError(LogFilter.Sql, "Table `Battleground_template` for Id {0} has bad values for LevelMin ({1}) and LevelMax({2})", bgTemplate.Id, bgTemplate.MinLevel, bgTemplate.MaxLevel); continue; } if (bgTemplate.Id != BattlegroundTypeId.AA && bgTemplate.Id != BattlegroundTypeId.RB) { uint startId = result.Read(5); if (CliDB.WorldSafeLocsStorage.ContainsKey(startId)) { WorldSafeLocsRecord start = CliDB.WorldSafeLocsStorage.LookupByKey(startId); bgTemplate.StartLocation[TeamId.Alliance] = new Position(start.Loc.X, start.Loc.Y, start.Loc.Z, (start.Facing + MathFunctions.PI) / 180); } else { Log.outError(LogFilter.Sql, "Table `Battleground_template` for Id {0} has a non-existed WorldSafeLocs.dbc id {1} in field `AllianceStartLoc`. BG not created.", bgTemplate.Id, startId); continue; } startId = result.Read(6); if (CliDB.WorldSafeLocsStorage.ContainsKey(startId)) { WorldSafeLocsRecord start = CliDB.WorldSafeLocsStorage.LookupByKey(startId); bgTemplate.StartLocation[TeamId.Horde] = new Position(start.Loc.X, start.Loc.Y, start.Loc.Z, result.Read(8)); } else { Log.outError(LogFilter.Sql, "Table `Battleground_template` for Id {0} has a non-existed WorldSafeLocs.dbc id {1} in field `HordeStartLoc`. BG not created.", bgTemplate.Id, startId); continue; } } if (!CreateBattleground(bgTemplate)) continue; _BattlegroundTemplates[bgTypeId] = bgTemplate; if (bgTemplate.BattlemasterEntry.MapId[1] == -1) // in this case we have only one mapId _BattlegroundMapTemplates[(uint)bgTemplate.BattlemasterEntry.MapId[0]] = _BattlegroundTemplates[bgTypeId]; ++count; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Battlegrounds in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } public void SendBattlegroundList(Player player, ObjectGuid guid, BattlegroundTypeId bgTypeId) { BattlegroundTemplate bgTemplate = GetBattlegroundTemplateByTypeId(bgTypeId); if (bgTemplate == null) return; BattlefieldList battlefieldList = new BattlefieldList(); battlefieldList.BattlemasterGuid = guid; battlefieldList.BattlemasterListID = (int)bgTypeId; battlefieldList.MinLevel = (byte)bgTemplate.MinLevel; battlefieldList.MaxLevel = (byte)bgTemplate.MaxLevel; battlefieldList.PvpAnywhere = guid.IsEmpty(); battlefieldList.HasRandomWinToday = player.GetRandomWinner(); player.SendPacket(battlefieldList); } public void SendToBattleground(Player player, uint instanceId, BattlegroundTypeId bgTypeId) { Battleground bg = GetBattleground(instanceId, bgTypeId); if (bg) { uint mapid = bg.GetMapId(); Team team = player.GetBGTeam(); Position pos = bg.GetTeamStartPosition(Battleground.GetTeamIndexByTeamId(team)); Log.outDebug(LogFilter.Battleground, "BattlegroundMgr.SendToBattleground: Sending {0} to map {1}, {2} (bgType {3})", player.GetName(), mapid, pos.ToString(), bgTypeId); player.TeleportTo(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); } else Log.outError(LogFilter.Battleground, "BattlegroundMgr.SendToBattleground: Instance {0} (bgType {1}) not found while trying to teleport player {2}", instanceId, bgTypeId, player.GetName()); } public void SendAreaSpiritHealerQuery(Player player, Battleground bg, ObjectGuid guid) { uint time = 30000 - bg.GetLastResurrectTime(); // resurrect every 30 seconds if (time == 0xFFFFFFFF) time = 0; AreaSpiritHealerTime areaSpiritHealerTime = new AreaSpiritHealerTime(); areaSpiritHealerTime.HealerGuid = guid; areaSpiritHealerTime.TimeLeft = time; player.SendPacket(areaSpiritHealerTime); } bool IsArenaType(BattlegroundTypeId bgTypeId) { return bgTypeId == BattlegroundTypeId.AA || bgTypeId == BattlegroundTypeId.BE || bgTypeId == BattlegroundTypeId.NA || bgTypeId == BattlegroundTypeId.DS || bgTypeId == BattlegroundTypeId.RV || bgTypeId == BattlegroundTypeId.RL; } public BattlegroundQueueTypeId BGQueueTypeId(BattlegroundTypeId bgTypeId, ArenaTypes arenaType) { switch (bgTypeId) { case BattlegroundTypeId.AB: return BattlegroundQueueTypeId.AB; case BattlegroundTypeId.AV: return BattlegroundQueueTypeId.AV; case BattlegroundTypeId.EY: return BattlegroundQueueTypeId.EY; case BattlegroundTypeId.IC: return BattlegroundQueueTypeId.IC; case BattlegroundTypeId.TP: return BattlegroundQueueTypeId.TP; case BattlegroundTypeId.BFG: return BattlegroundQueueTypeId.BFG; case BattlegroundTypeId.RB: return BattlegroundQueueTypeId.RB; case BattlegroundTypeId.SA: return BattlegroundQueueTypeId.SA; case BattlegroundTypeId.WS: return BattlegroundQueueTypeId.WS; case BattlegroundTypeId.AA: case BattlegroundTypeId.BE: case BattlegroundTypeId.DS: case BattlegroundTypeId.NA: case BattlegroundTypeId.RL: case BattlegroundTypeId.RV: switch (arenaType) { case ArenaTypes.Team2v2: return BattlegroundQueueTypeId.Arena2v2; case ArenaTypes.Team3v3: return BattlegroundQueueTypeId.Arena3v3; case ArenaTypes.Team5v5: return BattlegroundQueueTypeId.Arena5v5; default: return BattlegroundQueueTypeId.None; } default: return BattlegroundQueueTypeId.None; } } public BattlegroundTypeId BGTemplateId(BattlegroundQueueTypeId bgQueueTypeId) { switch (bgQueueTypeId) { case BattlegroundQueueTypeId.WS: return BattlegroundTypeId.WS; case BattlegroundQueueTypeId.AB: return BattlegroundTypeId.AB; case BattlegroundQueueTypeId.AV: return BattlegroundTypeId.AV; case BattlegroundQueueTypeId.EY: return BattlegroundTypeId.EY; case BattlegroundQueueTypeId.SA: return BattlegroundTypeId.SA; case BattlegroundQueueTypeId.IC: return BattlegroundTypeId.IC; case BattlegroundQueueTypeId.TP: return BattlegroundTypeId.TP; case BattlegroundQueueTypeId.BFG: return BattlegroundTypeId.BFG; case BattlegroundQueueTypeId.RB: return BattlegroundTypeId.RB; case BattlegroundQueueTypeId.Arena2v2: case BattlegroundQueueTypeId.Arena3v3: case BattlegroundQueueTypeId.Arena5v5: return BattlegroundTypeId.AA; default: return 0; // used for unknown template (it existed and do nothing) } } public ArenaTypes BGArenaType(BattlegroundQueueTypeId bgQueueTypeId) { switch (bgQueueTypeId) { case BattlegroundQueueTypeId.Arena2v2: return ArenaTypes.Team2v2; case BattlegroundQueueTypeId.Arena3v3: return ArenaTypes.Team3v3; case BattlegroundQueueTypeId.Arena5v5: return ArenaTypes.Team5v5; default: return 0; } } public void ToggleTesting() { m_Testing = !m_Testing; Global.WorldMgr.SendWorldText(m_Testing ? CypherStrings.DebugBgOn : CypherStrings.DebugBgOff); } public void ToggleArenaTesting() { m_ArenaTesting = !m_ArenaTesting; Global.WorldMgr.SendWorldText(m_ArenaTesting ? CypherStrings.DebugArenaOn : CypherStrings.DebugArenaOff); } public void SetHolidayWeekends(uint mask) { // The current code supports battlegrounds up to BattlegroundTypeId(31) for (var bgtype = 1; bgtype < (int)BattlegroundTypeId.Max && bgtype < 32; ++bgtype) { Battleground bg = GetBattlegroundTemplate((BattlegroundTypeId)bgtype); if (bg) bg.SetHoliday(Convert.ToBoolean(mask & (1 << bgtype))); } } public void ScheduleQueueUpdate(uint arenaMatchmakerRating, ArenaTypes arenaType, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id) { //we will use only 1 number created of bgTypeId and bracket_id ulong scheduleId = ((ulong)arenaMatchmakerRating << 32) | ((uint)arenaType << 24) | ((uint)bgQueueTypeId << 16) | ((uint)bgTypeId << 8) | (uint)bracket_id; if (!m_QueueUpdateScheduler.Contains(scheduleId)) m_QueueUpdateScheduler.Add(scheduleId); } public uint GetMaxRatingDifference() { // this is for stupid people who can't use brain and set max rating difference to 0 uint diff = WorldConfig.GetUIntValue(WorldCfg.ArenaMaxRatingDifference); if (diff == 0) diff = 5000; return diff; } public uint GetRatingDiscardTimer() { return WorldConfig.GetUIntValue(WorldCfg.ArenaRatingDiscardTimer); } public uint GetPrematureFinishTime() { return WorldConfig.GetUIntValue(WorldCfg.BattlegroundPrematureFinishTimer); } public void LoadBattleMastersEntry() { uint oldMSTime = Time.GetMSTime(); mBattleMastersMap.Clear(); // need for reload case SQLResult result = DB.World.Query("SELECT entry, bg_template FROM battlemaster_entry"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 battlemaster entries. DB table `battlemaster_entry` is empty!"); return; } uint count = 0; do { uint entry = result.Read(0); CreatureTemplate cInfo = Global.ObjectMgr.GetCreatureTemplate(entry); if (cInfo != null) { if (!cInfo.Npcflag.HasAnyFlag(NPCFlags.BattleMaster)) Log.outError(LogFilter.Sql, "Creature (Entry: {0}) listed in `battlemaster_entry` is not a battlemaster.", entry); } else { Log.outError(LogFilter.Sql, "Creature (Entry: {0}) listed in `battlemaster_entry` does not exist.", entry); continue; } uint bgTypeId = result.Read(1); if (!CliDB.BattlemasterListStorage.ContainsKey(bgTypeId)) { Log.outError(LogFilter.Sql, "Table `battlemaster_entry` contain entry {0} for not existed Battleground type {1}, ignored.", entry, bgTypeId); continue; } ++count; mBattleMastersMap[entry] = (BattlegroundTypeId)bgTypeId; } while (result.NextRow()); CheckBattleMasters(); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} battlemaster entries in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } void CheckBattleMasters() { var templates = Global.ObjectMgr.GetCreatureTemplates(); foreach (var creature in templates) { if (creature.Value.Npcflag.HasAnyFlag(NPCFlags.BattleMaster) && !mBattleMastersMap.ContainsKey(creature.Value.Entry)) { Log.outError(LogFilter.Sql, "CreatureTemplate (Entry: {0}) has UNIT_NPC_FLAG_BATTLEMASTER but no data in `battlemaster_entry` table. Removing flag!", creature.Value.Entry); templates[creature.Key].Npcflag &= ~NPCFlags.BattleMaster; } } } HolidayIds BGTypeToWeekendHolidayId(BattlegroundTypeId bgTypeId) { switch (bgTypeId) { case BattlegroundTypeId.AV: return HolidayIds.CallToArmsAv; case BattlegroundTypeId.EY: return HolidayIds.CallToArmsEy; case BattlegroundTypeId.WS: return HolidayIds.CallToArmsWs; case BattlegroundTypeId.SA: return HolidayIds.CallToArmsSa; case BattlegroundTypeId.AB: return HolidayIds.CallToArmsAb; case BattlegroundTypeId.IC: return HolidayIds.CallToArmsIc; case BattlegroundTypeId.TP: return HolidayIds.CallToArmsTp; case BattlegroundTypeId.BFG: return HolidayIds.CallToArmsBfg; default: return HolidayIds.None; } } public BattlegroundTypeId WeekendHolidayIdToBGType(HolidayIds holiday) { switch (holiday) { case HolidayIds.CallToArmsAv: return BattlegroundTypeId.AV; case HolidayIds.CallToArmsEy: return BattlegroundTypeId.EY; case HolidayIds.CallToArmsWs: return BattlegroundTypeId.WS; case HolidayIds.CallToArmsSa: return BattlegroundTypeId.SA; case HolidayIds.CallToArmsAb: return BattlegroundTypeId.AB; case HolidayIds.CallToArmsIc: return BattlegroundTypeId.IC; case HolidayIds.CallToArmsTp: return BattlegroundTypeId.TP; case HolidayIds.CallToArmsBfg: return BattlegroundTypeId.BFG; default: return BattlegroundTypeId.None; } } public bool IsBGWeekend(BattlegroundTypeId bgTypeId) { return Global.GameEventMgr.IsHolidayActive(BGTypeToWeekendHolidayId(bgTypeId)); } BattlegroundTypeId GetRandomBG(BattlegroundTypeId bgTypeId) { BattlegroundTemplate bgTemplate = GetBattlegroundTemplateByTypeId(bgTypeId); if (bgTemplate != null) { Dictionary selectionWeights = new Dictionary(); foreach (var mapId in bgTemplate.BattlemasterEntry.MapId) { if (mapId == -1) break; BattlegroundTemplate bg = GetBattlegroundTemplateByMapId((uint)mapId); if (bg != null) { selectionWeights.Add(bg.Id, bg.Weight); } } return selectionWeights.SelectRandomElementByWeight(i => i.Value).Key; } return BattlegroundTypeId.None; } public List GetBGFreeSlotQueueStore(BattlegroundTypeId bgTypeId) { return bgDataStore[bgTypeId].BGFreeSlotQueue; } public void AddToBGFreeSlotQueue(BattlegroundTypeId bgTypeId, Battleground bg) { bgDataStore[bgTypeId].BGFreeSlotQueue.Insert(0, bg); } public void RemoveFromBGFreeSlotQueue(BattlegroundTypeId bgTypeId, uint instanceId) { var queues = bgDataStore[bgTypeId].BGFreeSlotQueue; foreach (var bg in queues) { if (bg.GetInstanceID() == instanceId) { queues.Remove(bg); return; } } } public void AddBattleground(Battleground bg) { if (bg) bgDataStore[bg.GetTypeID()].m_Battlegrounds[bg.GetInstanceID()] = bg; } public void RemoveBattleground(BattlegroundTypeId bgTypeId, uint instanceId) { bgDataStore[bgTypeId].m_Battlegrounds.Remove(instanceId); } public BattlegroundQueue GetBattlegroundQueue(BattlegroundQueueTypeId bgQueueTypeId) { return m_BattlegroundQueues[(int)bgQueueTypeId]; } public bool isArenaTesting() { return m_ArenaTesting; } public bool isTesting() { return m_Testing; } public BattlegroundTypeId GetBattleMasterBG(uint entry) { return mBattleMastersMap.LookupByKey(entry); } BattlegroundTemplate GetBattlegroundTemplateByTypeId(BattlegroundTypeId id) { return _BattlegroundTemplates.LookupByKey(id); } BattlegroundTemplate GetBattlegroundTemplateByMapId(uint mapId) { return _BattlegroundMapTemplates.LookupByKey(mapId); } Dictionary bgDataStore = new Dictionary(); BattlegroundQueue[] m_BattlegroundQueues = new BattlegroundQueue[(int)BattlegroundQueueTypeId.Max]; Dictionary mBattleMastersMap = new Dictionary(); Dictionary _BattlegroundTemplates = new Dictionary(); Dictionary _BattlegroundMapTemplates = new Dictionary(); List m_QueueUpdateScheduler = new List(); uint m_NextRatedArenaUpdate; uint m_UpdateTimer; bool m_ArenaTesting; bool m_Testing; } public class BattlegroundData { public BattlegroundData() { for (var i = 0; i < (int)BattlegroundBracketId.Max; ++i) m_ClientBattlegroundIds[i] = new List(); } public Dictionary m_Battlegrounds = new Dictionary(); public List[] m_ClientBattlegroundIds = new List[(int)BattlegroundBracketId.Max]; public List BGFreeSlotQueue = new List(); public Battleground Template; } class BattlegroundTemplate { public BattlegroundTypeId Id; public uint MinPlayersPerTeam; public uint MaxPlayersPerTeam; public uint MinLevel; public uint MaxLevel; public Position[] StartLocation = new Position[SharedConst.BGTeamsCount]; public float StartMaxDist; public byte Weight; public uint scriptId; public BattlemasterListRecord BattlemasterEntry; public bool IsArena() { return BattlemasterEntry.InstanceType == (uint)MapTypes.Arena; } } }