/* * Copyright (C) 2005-2009 MaNGOS * * Copyright (C) 2008-2010 Trinity * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Common.h" #include "ObjectMgr.h" #include "World.h" #include "WorldPacket.h" #include "ArenaTeam.h" #include "BattlegroundMgr.h" #include "BattlegroundAV.h" #include "BattlegroundAB.h" #include "BattlegroundEY.h" #include "BattlegroundWS.h" #include "BattlegroundNA.h" #include "BattlegroundBE.h" #include "BattlegroundAA.h" #include "BattlegroundRL.h" #include "BattlegroundSA.h" #include "BattlegroundDS.h" #include "BattlegroundRV.h" #include "BattlegroundIC.h" #include "BattlegroundRB.h" #include "Chat.h" #include "Map.h" #include "MapInstanced.h" #include "MapManager.h" #include "Player.h" #include "GameEventMgr.h" #include "ProgressBar.h" #include "SharedDefines.h" #include "Formulas.h" #include "DisableMgr.h" /*********************************************************/ /*** BATTLEGROUND MANAGER ***/ /*********************************************************/ BattlegroundMgr::BattlegroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false) { for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) m_Battlegrounds[i].clear(); m_NextRatingDiscardUpdate = sWorld.getIntConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); m_Testing=false; } BattlegroundMgr::~BattlegroundMgr() { DeleteAllBattlegrounds(); } void BattlegroundMgr::DeleteAllBattlegrounds() { for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i) { for (BattlegroundSet::iterator itr = m_Battlegrounds[i].begin(); itr != m_Battlegrounds[i].end();) { Battleground * bg = itr->second; m_Battlegrounds[i].erase(itr++); if (!m_ClientBattlegroundIds[i][bg->GetBracketId()].empty()) m_ClientBattlegroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID()); delete bg; } } // destroy template battlegrounds that listed only in queues (other already terminated) for (uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId) { // ~Battleground call unregistring BG from queue while (!BGFreeSlotQueue[bgTypeId].empty()) delete BGFreeSlotQueue[bgTypeId].front(); } } // used to update running battlegrounds, and delete finished ones void BattlegroundMgr::Update(uint32 diff) { BattlegroundSet::iterator itr, next; for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i) { itr = m_Battlegrounds[i].begin(); // skip updating battleground template if (itr != m_Battlegrounds[i].end()) ++itr; for (; itr != m_Battlegrounds[i].end(); itr = next) { next = itr; ++next; itr->second->Update(diff); // use the SetDeleteThis variable // direct deletion caused crashes if (itr->second->m_SetDeleteThis) { Battleground * bg = itr->second; m_Battlegrounds[i].erase(itr); if (!m_ClientBattlegroundIds[i][bg->GetBracketId()].empty()) m_ClientBattlegroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID()); delete bg; } } } // update scheduled queues if (!m_QueueUpdateScheduler.empty()) { std::vector scheduled; { //copy vector and clear the other scheduled = std::vector(m_QueueUpdateScheduler); m_QueueUpdateScheduler.clear(); //release lock } for (uint8 i = 0; i < scheduled.size(); i++) { uint32 arenaMMRating = scheduled[i] >> 32; uint8 arenaType = 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[bgQueueTypeId].Update(bgTypeId, bracket_id, arenaType, arenaMMRating > 0, arenaMMRating); } } // if rating difference counts, maybe force-update queues if (sWorld.getIntConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getIntConfig(CONFIG_ARENA_RATING_DISCARD_TIMER)) { // it's time to force update if (m_NextRatingDiscardUpdate < diff) { // forced update for rated arenas (scan all, but skipped non rated) sLog.outDebug("BattlegroundMgr: UPDATING ARENA QUEUES"); for (int qtype = BATTLEGROUND_QUEUE_2v2; qtype <= BATTLEGROUND_QUEUE_5v5; ++qtype) for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) m_BattlegroundQueues[qtype].Update( BATTLEGROUND_AA, BattlegroundBracketId(bracket), BattlegroundMgr::BGArenaType(BattlegroundQueueTypeId(qtype)), true, 0); m_NextRatingDiscardUpdate = sWorld.getIntConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); } else m_NextRatingDiscardUpdate -= diff; } if (sWorld.getBoolConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) { if (m_AutoDistributionTimeChecker < diff) { if (time(NULL) > m_NextAutoDistributionTime) { DistributeArenaPoints(); m_NextAutoDistributionTime = m_NextAutoDistributionTime + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getIntConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); sWorld.setWorldState(WS_ARENA_DISTRIBUTION_TIME, uint64(m_NextAutoDistributionTime)); } m_AutoDistributionTimeChecker = 600000; // check 10 minutes } else m_AutoDistributionTimeChecker -= diff; } } void BattlegroundMgr::BuildBattlegroundStatusPacket(WorldPacket *data, Battleground *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype, uint8 uiFrame) { // we can be in 2 queues in same time... if (StatusID == 0 || !bg) { data->Initialize(SMSG_BATTLEFIELD_STATUS, 4+8); *data << uint32(QueueSlot); // queue id (0...1) *data << uint64(0); return; } data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+8+1+1+4+1+4+4+4)); *data << uint32(QueueSlot); // queue id (0...1) - player can be in 2 queues in time // The following segment is read as uint64 in client but can be appended as their original type. *data << uint8(arenatype); sLog.outDebug("BattlegroundMgr::BuildBattlegroundStatusPacket: arenatype = %u for bg instanceID %u, TypeID %u.", arenatype, bg->GetClientInstanceID(), bg->GetTypeID()); *data << uint8(bg->isArena() ? 0xC : 0x2); *data << uint32(bg->GetTypeID()); *data << uint16(0x1F90); // End of uint64 segment, decomposed this way for simplicity *data << uint8(0); // 3.3.0, some level, only saw 80... *data << uint8(0); // 3.3.0, some level, only saw 80... *data << uint32(bg->GetClientInstanceID()); // alliance/horde for BG and skirmish/rated for Arenas // following displays the minimap-icon 0 = faction icon 1 = arenaicon *data << uint8(bg->isRated()); // 1 for rated match, 0 for bg or non rated match *data << uint32(StatusID); // status switch(StatusID) { case STATUS_WAIT_QUEUE: // status_in_queue *data << uint32(Time1); // average wait time, milliseconds *data << uint32(Time2); // time in queue, updated every minute!, milliseconds break; case STATUS_WAIT_JOIN: // status_invite *data << uint32(bg->GetMapId()); // map id *data << uint64(0); // 3.3.5, unknown *data << uint32(Time1); // time to remove from queue, milliseconds break; case STATUS_IN_PROGRESS: // status_in_progress *data << uint32(bg->GetMapId()); // map id *data << uint64(0); // 3.3.5, unknown *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds *data << uint32(Time2); // time from bg start, milliseconds *data << uint8(uiFrame); break; default: sLog.outError("Unknown BG status!"); break; } } void BattlegroundMgr::BuildPvpLogDataPacket(WorldPacket *data, Battleground *bg) { uint8 type = (bg->isArena() ? 1 : 0); // last check on 3.0.3 data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); *data << uint8(type); // type (battleground=0/arena=1) if (type) // arena { // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H for (int8 i = 1; i >= 0; --i) { uint32 pointsLost = bg->m_ArenaTeamRatingChanges[i] < 0 ? abs(bg->m_ArenaTeamRatingChanges[i]) : 0; uint32 pointsGained = bg->m_ArenaTeamRatingChanges[i] > 0 ? bg->m_ArenaTeamRatingChanges[i] : 0; uint32 MatchmakerRating = bg->m_ArenaTeamMMR[i]; *data << uint32(pointsLost); // Rating Lost *data << uint32(pointsGained); // Rating gained *data << uint32(MatchmakerRating); // Matchmaking Value sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); } for (int8 i = 1; i >= 0; --i) { uint32 at_id = bg->m_ArenaTeamIds[i]; ArenaTeam* at = sObjectMgr.GetArenaTeamById(at_id); if (at) *data << at->GetName(); else *data << uint8(0); } } if (bg->GetStatus() != STATUS_WAIT_LEAVE) *data << uint8(0); // bg not ended else { *data << uint8(1); // bg ended *data << uint8(bg->GetWinner()); // who win } size_t wpos = data->wpos(); uint32 scoreCount = 0; *data << uint32(scoreCount); // placeholder Battleground::BattlegroundScoreMap::const_iterator itr2 = bg->GetPlayerScoresBegin(); for (Battleground::BattlegroundScoreMap::const_iterator itr = itr2; itr != bg->GetPlayerScoresEnd();) { itr2 = itr++; if (!bg->IsPlayerInBattleground(itr2->first)) { sLog.outError("Player %llu has scoreboard entry for battleground %u but is not in battleground!", itr->first, bg->GetTypeID(true)); continue; } *data << uint64(itr2->first); *data << uint32(itr2->second->KillingBlows); if (type == 0) { *data << uint32(itr2->second->HonorableKills); *data << uint32(itr2->second->Deaths); *data << uint32(itr2->second->BonusHonor); } else { Player *plr = sObjectMgr.GetPlayer(itr2->first); uint32 team = bg->GetPlayerTeam(itr2->first); if (!team && plr) team = plr->GetBGTeam(); *data << uint8(team == ALLIANCE ? 1 : 0); // green or yellow } *data << uint32(itr2->second->DamageDone); // damage done *data << uint32(itr2->second->HealingDone); // healing done switch(bg->GetTypeID(true)) // battleground specific things { case BATTLEGROUND_RB: switch(bg->GetMapId()) { case 489: *data << uint32(0x00000002); // count of next fields *data << uint32(((BattlegroundWGScore*)itr2->second)->FlagCaptures); // flag captures *data << uint32(((BattlegroundWGScore*)itr2->second)->FlagReturns); // flag returns break; case 566: *data << uint32(0x00000001); // count of next fields *data << uint32(((BattlegroundEYScore*)itr2->second)->FlagCaptures); // flag captures break; case 529: *data << uint32(0x00000002); // count of next fields *data << uint32(((BattlegroundABScore*)itr2->second)->BasesAssaulted); // bases asssulted *data << uint32(((BattlegroundABScore*)itr2->second)->BasesDefended); // bases defended break; case 30: *data << uint32(0x00000005); // count of next fields *data << uint32(((BattlegroundAVScore*)itr2->second)->GraveyardsAssaulted); // GraveyardsAssaulted *data << uint32(((BattlegroundAVScore*)itr2->second)->GraveyardsDefended); // GraveyardsDefended *data << uint32(((BattlegroundAVScore*)itr2->second)->TowersAssaulted); // TowersAssaulted *data << uint32(((BattlegroundAVScore*)itr2->second)->TowersDefended); // TowersDefended *data << uint32(((BattlegroundAVScore*)itr2->second)->MinesCaptured); // MinesCaptured break; case 607: *data << uint32(0x00000002); // count of next fields *data << uint32(((BattlegroundSAScore*)itr2->second)->demolishers_destroyed); *data << uint32(((BattlegroundSAScore*)itr2->second)->gates_destroyed); break; default: *data << uint32(0); break; } case BATTLEGROUND_AV: *data << uint32(0x00000005); // count of next fields *data << uint32(((BattlegroundAVScore*)itr2->second)->GraveyardsAssaulted); // GraveyardsAssaulted *data << uint32(((BattlegroundAVScore*)itr2->second)->GraveyardsDefended); // GraveyardsDefended *data << uint32(((BattlegroundAVScore*)itr2->second)->TowersAssaulted); // TowersAssaulted *data << uint32(((BattlegroundAVScore*)itr2->second)->TowersDefended); // TowersDefended *data << uint32(((BattlegroundAVScore*)itr2->second)->MinesCaptured); // MinesCaptured break; case BATTLEGROUND_WS: *data << uint32(0x00000002); // count of next fields *data << uint32(((BattlegroundWGScore*)itr2->second)->FlagCaptures); // flag captures *data << uint32(((BattlegroundWGScore*)itr2->second)->FlagReturns); // flag returns break; case BATTLEGROUND_AB: *data << uint32(0x00000002); // count of next fields *data << uint32(((BattlegroundABScore*)itr2->second)->BasesAssaulted); // bases asssulted *data << uint32(((BattlegroundABScore*)itr2->second)->BasesDefended); // bases defended break; case BATTLEGROUND_EY: *data << uint32(0x00000001); // count of next fields *data << uint32(((BattlegroundEYScore*)itr2->second)->FlagCaptures); // flag captures break; case BATTLEGROUND_SA: *data << uint32(0x00000002); // count of next fields *data << uint32(((BattlegroundSAScore*)itr2->second)->demolishers_destroyed); *data << uint32(((BattlegroundSAScore*)itr2->second)->gates_destroyed); break; case BATTLEGROUND_NA: case BATTLEGROUND_BE: case BATTLEGROUND_AA: case BATTLEGROUND_RL: case BATTLEGROUND_DS: // wotlk case BATTLEGROUND_RV: // wotlk case BATTLEGROUND_IC: // wotlk *data << uint32(0); break; default: sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID()); *data << uint32(0); break; } // should never happen if (++scoreCount >= bg->GetMaxPlayers() && itr != bg->GetPlayerScoresEnd()) { sLog.outError("Battleground %u scoreboard has more entries (%u) than allowed players in this bg (%u)", bg->GetTypeID(true), bg->GetPlayerScoresSize(), bg->GetMaxPlayers()); break; } } data->put(wpos, scoreCount); } void BattlegroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, GroupJoinBattlegroundResult result) { data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); *data << int32(result); if (result == ERR_BATTLEGROUND_JOIN_TIMED_OUT || result == ERR_BATTLEGROUND_JOIN_FAILED) *data << uint64(0); // player guid } void BattlegroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value) { data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4); *data << uint32(field); *data << uint32(value); } void BattlegroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid) { data->Initialize(SMSG_PLAY_SOUND, 4); *data << uint32(soundid); } void BattlegroundMgr::BuildPlayerLeftBattlegroundPacket(WorldPacket *data, const uint64& guid) { data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8); *data << uint64(guid); } void BattlegroundMgr::BuildPlayerJoinedBattlegroundPacket(WorldPacket *data, Player *plr) { data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8); *data << uint64(plr->GetGUID()); } Battleground * BattlegroundMgr::GetBattlegroundThroughClientInstance(uint32 instanceId, BattlegroundTypeId bgTypeId) { //cause at HandleBattlegroundJoinOpcode the clients sends the instanceid he gets from //SMSG_BATTLEFIELD_LIST we need to find the battleground with this clientinstance-id Battleground* bg = GetBattlegroundTemplate(bgTypeId); if (!bg) return NULL; if (bg->isArena()) return GetBattleground(instanceId, bgTypeId); for (BattlegroundSet::iterator itr = m_Battlegrounds[bgTypeId].begin(); itr != m_Battlegrounds[bgTypeId].end(); ++itr) { if (itr->second->GetClientInstanceID() == instanceId) return itr->second; } return NULL; } Battleground * BattlegroundMgr::GetBattleground(uint32 InstanceID, BattlegroundTypeId bgTypeId) { if (!InstanceID) return NULL; //search if needed BattlegroundSet::iterator itr; if (bgTypeId == BATTLEGROUND_TYPE_NONE) { for (uint32 i = BATTLEGROUND_AV; i < MAX_BATTLEGROUND_TYPE_ID; i++) { itr = m_Battlegrounds[i].find(InstanceID); if (itr != m_Battlegrounds[i].end()) return itr->second; } return NULL; } itr = m_Battlegrounds[bgTypeId].find(InstanceID); return ((itr != m_Battlegrounds[bgTypeId].end()) ? itr->second : NULL); } Battleground * BattlegroundMgr::GetBattlegroundTemplate(BattlegroundTypeId bgTypeId) { //map is sorted and we can be sure that lowest instance id has only BG template return m_Battlegrounds[bgTypeId].empty() ? NULL : m_Battlegrounds[bgTypeId].begin()->second; } uint32 BattlegroundMgr::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 uint32 lastId = 0; for (std::set::iterator itr = m_ClientBattlegroundIds[bgTypeId][bracket_id].begin(); itr != m_ClientBattlegroundIds[bgTypeId][bracket_id].end();) { if ((++lastId) != *itr) //if there is a gap between the ids, we will break.. break; lastId = *itr; } m_ClientBattlegroundIds[bgTypeId][bracket_id].insert(lastId + 1); return lastId + 1; } // create a new battleground that will really be used to play Battleground * BattlegroundMgr::CreateNewBattleground(BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated) { // get the template BG Battleground *bg_template = GetBattlegroundTemplate(bgTypeId); BattlegroundSelectionWeightMap *selectionWeights = NULL; if (!bg_template) { sLog.outError("Battleground: CreateNewBattleground - bg template not found for %u", bgTypeId); return NULL; } bool isRandom = false; if (bg_template->isArena()) selectionWeights = &m_ArenaSelectionWeights; else if (bgTypeId == BATTLEGROUND_RB) { selectionWeights = &m_BGSelectionWeights; isRandom = true; } if (selectionWeights) { if (!selectionWeights->size()) return NULL; uint32 Weight = 0; uint32 selectedWeight = 0; bgTypeId = BATTLEGROUND_TYPE_NONE; // Get sum of all weights for (BattlegroundSelectionWeightMap::const_iterator it = selectionWeights->begin(); it != selectionWeights->end(); ++it) Weight += it->second; if (!Weight) return NULL; // Select a random value selectedWeight = urand(0, Weight-1); // Select the correct bg (if we have in DB A(10), B(20), C(10), D(15) --> [0---A---9|10---B---29|30---C---39|40---D---54]) Weight = 0; for (BattlegroundSelectionWeightMap::const_iterator it = selectionWeights->begin(); it != selectionWeights->end(); ++it) { Weight += it->second; if (selectedWeight < Weight) { bgTypeId = it->first; break; } } bg_template = GetBattlegroundTemplate(bgTypeId); if (!bg_template) { sLog.outError("Battleground: CreateNewBattleground - bg template not found for %u", bgTypeId); return NULL; } } Battleground *bg = NULL; // create a copy of the BG template switch(bgTypeId) { case BATTLEGROUND_AV: bg = new BattlegroundAV(*(BattlegroundAV*)bg_template); break; case BATTLEGROUND_WS: bg = new BattlegroundWS(*(BattlegroundWS*)bg_template); break; case BATTLEGROUND_AB: bg = new BattlegroundAB(*(BattlegroundAB*)bg_template); break; case BATTLEGROUND_NA: bg = new BattlegroundNA(*(BattlegroundNA*)bg_template); break; case BATTLEGROUND_BE: bg = new BattlegroundBE(*(BattlegroundBE*)bg_template); break; case BATTLEGROUND_AA: bg = new BattlegroundAA(*(BattlegroundAA*)bg_template); break; case BATTLEGROUND_EY: bg = new BattlegroundEY(*(BattlegroundEY*)bg_template); break; case BATTLEGROUND_RL: bg = new BattlegroundRL(*(BattlegroundRL*)bg_template); break; case BATTLEGROUND_SA: bg = new BattlegroundSA(*(BattlegroundSA*)bg_template); break; case BATTLEGROUND_DS: bg = new BattlegroundDS(*(BattlegroundDS*)bg_template); break; case BATTLEGROUND_RV: bg = new BattlegroundRV(*(BattlegroundRV*)bg_template); break; case BATTLEGROUND_IC: bg = new BattlegroundIC(*(BattlegroundIC*)bg_template); break; case BATTLEGROUND_RB: bg = new BattlegroundRB(*(BattlegroundRB*)bg_template); break; default: //error, but it is handled few lines above return 0; } // set battelground difficulty before initialization bg->SetBracket(bracketEntry); // generate a new instance id bg->SetInstanceID(sMapMgr.GenerateInstanceId()); // set instance id bg->SetClientInstanceID(CreateClientVisibleInstanceId(isRandom ? BATTLEGROUND_RB : bgTypeId, bracketEntry->GetBracketId())); // reset the new bg (set status to status_wait_queue from status_none) bg->Reset(); // start the joining of the bg bg->SetStatus(STATUS_WAIT_JOIN); bg->SetArenaType(arenaType); bg->SetRated(isRated); bg->SetRandom(isRandom); bg->SetTypeID(isRandom ? BATTLEGROUND_RB : bgTypeId); bg->SetRandomTypeID(bgTypeId); return bg; } // used to create the BG templates uint32 BattlegroundMgr::CreateBattleground(BattlegroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattlegroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO, uint32 scriptId) { // Create the BG Battleground *bg = NULL; switch(bgTypeId) { case BATTLEGROUND_AV: bg = new BattlegroundAV; break; case BATTLEGROUND_WS: bg = new BattlegroundWS; break; case BATTLEGROUND_AB: bg = new BattlegroundAB; break; case BATTLEGROUND_NA: bg = new BattlegroundNA; break; case BATTLEGROUND_BE: bg = new BattlegroundBE; break; case BATTLEGROUND_AA: bg = new BattlegroundAA; break; case BATTLEGROUND_EY: bg = new BattlegroundEY; break; case BATTLEGROUND_RL: bg = new BattlegroundRL; break; case BATTLEGROUND_SA: bg = new BattlegroundSA; break; case BATTLEGROUND_DS: bg = new BattlegroundDS; break; case BATTLEGROUND_RV: bg = new BattlegroundRV; break; case BATTLEGROUND_IC: bg = new BattlegroundIC; break; case BATTLEGROUND_RB: bg = new BattlegroundRB; break; default: bg = new Battleground; break; } bg->SetMapId(MapID); bg->SetTypeID(bgTypeId); bg->SetInstanceID(0); bg->SetArenaorBGType(IsArena); bg->SetMinPlayersPerTeam(MinPlayersPerTeam); bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); bg->SetMinPlayers(MinPlayersPerTeam * 2); bg->SetMaxPlayers(MaxPlayersPerTeam * 2); bg->SetName(BattlegroundName); bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); bg->SetLevelRange(LevelMin, LevelMax); bg->SetScriptId(scriptId); // add bg to update list AddBattleground(bg->GetInstanceID(), bg->GetTypeID(), bg); // return some not-null value, bgTypeId is good enough for me return bgTypeId; } void BattlegroundMgr::CreateInitialBattlegrounds() { float AStartLoc[4]; float HStartLoc[4]; uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2; uint8 selectionWeight; BattlemasterListEntry const *bl; WorldSafeLocsEntry const *start; bool IsArena; uint32 scriptId = 0; uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO,Weight,ScriptName FROM battleground_template"); if (!result) { barGoLink bar(1); bar.step(); sLog.outString(); sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); return; } barGoLink bar(result->GetRowCount()); do { Field *fields = result->Fetch(); bar.step(); uint32 bgTypeID_ = fields[0].GetUInt32(); if (sDisableMgr.IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeID_, NULL)) continue; // can be overwrite by values from DB bl = sBattlemasterListStore.LookupEntry(bgTypeID_); if (!bl) { sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_); continue; } BattlegroundTypeId bgTypeID = BattlegroundTypeId(bgTypeID_); IsArena = (bl->type == TYPE_ARENA); MinPlayersPerTeam = fields[1].GetUInt32(); MaxPlayersPerTeam = fields[2].GetUInt32(); MinLvl = fields[3].GetUInt32(); MaxLvl = fields[4].GetUInt32(); //check values from DB if (MaxPlayersPerTeam == 0 || MinPlayersPerTeam == 0 || MinPlayersPerTeam > MaxPlayersPerTeam) { MinPlayersPerTeam = 0; // by default now expected strong full bg requirement MaxPlayersPerTeam = 40; } if (MinLvl == 0 || MaxLvl == 0 || MinLvl > MaxLvl) { //TO-DO: FIX ME MinLvl = 0;//bl->minlvl; MaxLvl = 80;//bl->maxlvl; } start1 = fields[5].GetUInt32(); start = sWorldSafeLocsStore.LookupEntry(start1); if (start) { AStartLoc[0] = start->x; AStartLoc[1] = start->y; AStartLoc[2] = start->z; AStartLoc[3] = fields[6].GetFloat(); } else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_RB) { AStartLoc[0] = 0; AStartLoc[1] = 0; AStartLoc[2] = 0; AStartLoc[3] = fields[6].GetFloat(); } else { sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTypeID, start1); continue; } start2 = fields[7].GetUInt32(); start = sWorldSafeLocsStore.LookupEntry(start2); if (start) { HStartLoc[0] = start->x; HStartLoc[1] = start->y; HStartLoc[2] = start->z; HStartLoc[3] = fields[8].GetFloat(); } else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_RB) { HStartLoc[0] = 0; HStartLoc[1] = 0; HStartLoc[2] = 0; HStartLoc[3] = fields[8].GetFloat(); } else { sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTypeID, start2); continue; } selectionWeight = fields[9].GetUInt8(); scriptId = sObjectMgr.GetScriptId(fields[10].GetString()); //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl); if (!CreateBattleground(bgTypeID, IsArena, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3], scriptId)) continue; if (IsArena) { if (bgTypeID != BATTLEGROUND_AA) m_ArenaSelectionWeights[bgTypeID] = selectionWeight; } else if (bgTypeID != BATTLEGROUND_RB) m_BGSelectionWeights[bgTypeID] = selectionWeight; ++count; } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u battlegrounds", count); } void BattlegroundMgr::InitAutomaticArenaPointDistribution() { if (!sWorld.getBoolConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) return; time_t wstime = time_t(sWorld.getWorldState(WS_ARENA_DISTRIBUTION_TIME)); time_t curtime = time(NULL); sLog.outDebug("Initializing Automatic Arena Point Distribution"); if (wstime < curtime) { m_NextAutoDistributionTime = curtime; // reset will be called in the next update sLog.outDebug("Battleground: Next arena point distribution time in the past, reseting it now."); } else m_NextAutoDistributionTime = wstime; sLog.outDebug("Automatic Arena Point Distribution initialized."); } void BattlegroundMgr::DistributeArenaPoints() { // used to distribute arena points based on last week's stats sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START); sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START); //temporary structure for storing maximum points to add values for all players std::map PlayerPoints; //at first update all points for all team members for (ObjectMgr::ArenaTeamMap::iterator team_itr = sObjectMgr.GetArenaTeamMapBegin(); team_itr != sObjectMgr.GetArenaTeamMapEnd(); ++team_itr) if (ArenaTeam * at = team_itr->second) at->UpdateArenaPointsHelper(PlayerPoints); //cycle that gives points to all players for (std::map::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr) { //update to database CharacterDatabase.PExecute("UPDATE characters SET arenaPoints = arenaPoints + '%u' WHERE guid = '%u'", plr_itr->second, plr_itr->first); //add points to player if online Player* pl = sObjectMgr.GetPlayer(plr_itr->first); if (pl) pl->ModifyArenaPoints(plr_itr->second); } PlayerPoints.clear(); sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END); sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START); for (ObjectMgr::ArenaTeamMap::iterator titr = sObjectMgr.GetArenaTeamMapBegin(); titr != sObjectMgr.GetArenaTeamMapEnd(); ++titr) { if (ArenaTeam * at = titr->second) { at->FinishWeek(); // set played this week etc values to 0 in memory, too at->SaveToDB(); // save changes at->NotifyStatsChanged(); // notify the players of the changes } } sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END); sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END); } void BattlegroundMgr::BuildBattlegroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattlegroundTypeId bgTypeId, uint8 fromWhere) { if (!plr) return; uint32 win_kills = plr->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; uint32 win_arena = plr->GetRandomWinner() ? BG_REWARD_WINNER_ARENA_LAST : BG_REWARD_WINNER_ARENA_FIRST; uint32 loos_kills = plr->GetRandomWinner() ? BG_REWARD_LOOSER_HONOR_LAST : BG_REWARD_LOOSER_HONOR_FIRST; win_kills = Trinity::Honor::hk_honor_at_level(plr->getLevel(), win_kills); loos_kills = Trinity::Honor::hk_honor_at_level(plr->getLevel(), loos_kills); data->Initialize(SMSG_BATTLEFIELD_LIST); *data << uint64(guid); // battlemaster guid *data << uint8(fromWhere); // from where you joined *data << uint32(bgTypeId); // battleground id *data << uint8(0); // unk *data << uint8(0); // unk // Rewards *data << uint8(plr->GetRandomWinner()); // 3.3.3 hasWin *data << uint32(win_kills); // 3.3.3 winHonor *data << uint32(win_arena); // 3.3.3 winArena *data << uint32(loos_kills); // 3.3.3 lossHonor uint8 isRandom = bgTypeId == BATTLEGROUND_RB; *data << uint8(isRandom); // 3.3.3 isRandom if (isRandom) { // Rewards (random) *data << uint8(plr->GetRandomWinner()); // 3.3.3 hasWin_Random *data << uint32(win_kills); // 3.3.3 winHonor_Random *data << uint32(win_arena); // 3.3.3 winArena_Random *data << uint32(loos_kills); // 3.3.3 lossHonor_Random } if (bgTypeId == BATTLEGROUND_AA) // arena { *data << uint32(0); // unk (count?) } else // battleground { size_t count_pos = data->wpos(); uint32 count = 0; *data << uint32(0); // number of bg instances if (Battleground* bgTemplate = sBattlegroundMgr.GetBattlegroundTemplate(bgTypeId)) { // expected bracket entry if (PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgTemplate->GetMapId(),plr->getLevel())) { BattlegroundBracketId bracketId = bracketEntry->GetBracketId(); for (std::set::iterator itr = m_ClientBattlegroundIds[bgTypeId][bracketId].begin(); itr != m_ClientBattlegroundIds[bgTypeId][bracketId].end();++itr) { *data << uint32(*itr); ++count; } data->put(count_pos , count); } } } } void BattlegroundMgr::SendToBattleground(Player *pl, uint32 instanceId, BattlegroundTypeId bgTypeId) { Battleground *bg = GetBattleground(instanceId, bgTypeId); if (bg) { uint32 mapid = bg->GetMapId(); float x, y, z, O; uint32 team = pl->GetBGTeam(); if (team == 0) team = pl->GetTeam(); bg->GetTeamStartLoc(team, x, y, z, O); sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O); pl->TeleportTo(mapid, x, y, z, O); } else { sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId); } } void BattlegroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, Battleground *bg, const uint64& guid) { WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds if (time_ == uint32(-1)) time_ = 0; data << guid << time_; pl->GetSession()->SendPacket(&data); } bool BattlegroundMgr::IsArenaType(BattlegroundTypeId bgTypeId) { return (bgTypeId == BATTLEGROUND_AA || bgTypeId == BATTLEGROUND_BE || bgTypeId == BATTLEGROUND_NA || bgTypeId == BATTLEGROUND_DS || bgTypeId == BATTLEGROUND_RV || bgTypeId == BATTLEGROUND_RL || bgTypeId == BATTLEGROUND_DS); } BattlegroundQueueTypeId BattlegroundMgr::BGQueueTypeId(BattlegroundTypeId bgTypeId, uint8 arenaType) { switch(bgTypeId) { case BATTLEGROUND_WS: return BATTLEGROUND_QUEUE_WS; case BATTLEGROUND_AB: return BATTLEGROUND_QUEUE_AB; case BATTLEGROUND_AV: return BATTLEGROUND_QUEUE_AV; case BATTLEGROUND_EY: return BATTLEGROUND_QUEUE_EY; case BATTLEGROUND_SA: return BATTLEGROUND_QUEUE_SA; case BATTLEGROUND_IC: return BATTLEGROUND_QUEUE_IC; case BATTLEGROUND_RB: return BATTLEGROUND_QUEUE_RB; case BATTLEGROUND_AA: case BATTLEGROUND_NA: case BATTLEGROUND_RL: case BATTLEGROUND_BE: case BATTLEGROUND_DS: case BATTLEGROUND_RV: switch(arenaType) { case ARENA_TYPE_2v2: return BATTLEGROUND_QUEUE_2v2; case ARENA_TYPE_3v3: return BATTLEGROUND_QUEUE_3v3; case ARENA_TYPE_5v5: return BATTLEGROUND_QUEUE_5v5; default: return BATTLEGROUND_QUEUE_NONE; } default: return BATTLEGROUND_QUEUE_NONE; } } BattlegroundTypeId BattlegroundMgr::BGTemplateId(BattlegroundQueueTypeId bgQueueTypeId) { switch(bgQueueTypeId) { case BATTLEGROUND_QUEUE_WS: return BATTLEGROUND_WS; case BATTLEGROUND_QUEUE_AB: return BATTLEGROUND_AB; case BATTLEGROUND_QUEUE_AV: return BATTLEGROUND_AV; case BATTLEGROUND_QUEUE_EY: return BATTLEGROUND_EY; case BATTLEGROUND_QUEUE_SA: return BATTLEGROUND_SA; case BATTLEGROUND_QUEUE_IC: return BATTLEGROUND_IC; case BATTLEGROUND_QUEUE_RB: return BATTLEGROUND_RB; case BATTLEGROUND_QUEUE_2v2: case BATTLEGROUND_QUEUE_3v3: case BATTLEGROUND_QUEUE_5v5: return BATTLEGROUND_AA; default: return BattlegroundTypeId(0); // used for unknown template (it existed and do nothing) } } uint8 BattlegroundMgr::BGArenaType(BattlegroundQueueTypeId bgQueueTypeId) { switch(bgQueueTypeId) { case BATTLEGROUND_QUEUE_2v2: return ARENA_TYPE_2v2; case BATTLEGROUND_QUEUE_3v3: return ARENA_TYPE_3v3; case BATTLEGROUND_QUEUE_5v5: return ARENA_TYPE_5v5; default: return 0; } } void BattlegroundMgr::ToggleTesting() { m_Testing = !m_Testing; if (m_Testing) sWorld.SendWorldText(LANG_DEBUG_BG_ON); else sWorld.SendWorldText(LANG_DEBUG_BG_OFF); } void BattlegroundMgr::ToggleArenaTesting() { m_ArenaTesting = !m_ArenaTesting; if (m_ArenaTesting) sWorld.SendWorldText(LANG_DEBUG_ARENA_ON); else sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF); } void BattlegroundMgr::SetHolidayWeekends(uint32 mask) { for (uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype) { if (Battleground * bg = GetBattlegroundTemplate(BattlegroundTypeId(bgtype))) { bg->SetHoliday(mask & (1 << bgtype)); } } } void BattlegroundMgr::ScheduleQueueUpdate(uint32 arenaMatchmakerRating, uint8 arenaType, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id) { //This method must be atomic, TODO add mutex //we will use only 1 number created of bgTypeId and bracket_id uint64 schedule_id = ((uint64)arenaMatchmakerRating << 32) | (arenaType << 24) | (bgQueueTypeId << 16) | (bgTypeId << 8) | bracket_id; bool found = false; for (uint8 i = 0; i < m_QueueUpdateScheduler.size(); i++) { if (m_QueueUpdateScheduler[i] == schedule_id) { found = true; break; } } if (!found) m_QueueUpdateScheduler.push_back(schedule_id); } uint32 BattlegroundMgr::GetMaxRatingDifference() const { // this is for stupid people who can't use brain and set max rating difference to 0 uint32 diff = sWorld.getIntConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); if (diff == 0) diff = 5000; return diff; } uint32 BattlegroundMgr::GetRatingDiscardTimer() const { return sWorld.getIntConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); } uint32 BattlegroundMgr::GetPrematureFinishTime() const { return sWorld.getIntConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); } void BattlegroundMgr::LoadBattleMastersEntry() { mBattleMastersMap.clear(); // need for reload case QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,bg_template FROM battlemaster_entry"); uint32 count = 0; if (!result) { barGoLink bar(1); bar.step(); sLog.outString(); sLog.outString(">> Loaded 0 battlemaster entries - table is empty!"); return; } barGoLink bar(result->GetRowCount()); do { ++count; bar.step(); Field *fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); uint32 bgTypeId = fields[1].GetUInt32(); if (!sBattlemasterListStore.LookupEntry(bgTypeId)) { sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId); continue; } mBattleMastersMap[entry] = BattlegroundTypeId(bgTypeId); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u battlemaster entries", count); } HolidayIds BattlegroundMgr::BGTypeToWeekendHolidayId(BattlegroundTypeId bgTypeId) { switch (bgTypeId) { case BATTLEGROUND_AV: return HOLIDAY_CALL_TO_ARMS_AV; case BATTLEGROUND_EY: return HOLIDAY_CALL_TO_ARMS_EY; case BATTLEGROUND_WS: return HOLIDAY_CALL_TO_ARMS_WS; case BATTLEGROUND_SA: return HOLIDAY_CALL_TO_ARMS_SA; case BATTLEGROUND_AB: return HOLIDAY_CALL_TO_ARMS_AB; default: return HOLIDAY_NONE; } } BattlegroundTypeId BattlegroundMgr::WeekendHolidayIdToBGType(HolidayIds holiday) { switch (holiday) { case HOLIDAY_CALL_TO_ARMS_AV: return BATTLEGROUND_AV; case HOLIDAY_CALL_TO_ARMS_EY: return BATTLEGROUND_EY; case HOLIDAY_CALL_TO_ARMS_WS: return BATTLEGROUND_WS; case HOLIDAY_CALL_TO_ARMS_SA: return BATTLEGROUND_SA; case HOLIDAY_CALL_TO_ARMS_AB: return BATTLEGROUND_AB; default: return BATTLEGROUND_TYPE_NONE; } } bool BattlegroundMgr::IsBGWeekend(BattlegroundTypeId bgTypeId) { return IsHolidayActive(BGTypeToWeekendHolidayId(bgTypeId)); } void BattlegroundMgr::DoCompleteAchievement(uint32 achievement, Player * player) { AchievementEntry const* AE = GetAchievementStore()->LookupEntry(achievement); if (!player) { //Map::PlayerList const &PlayerList = this->GetPlayers(); //GroupsQueueType::iterator group = SelectedGroups.begin(); //if (!PlayerList.isEmpty()) //for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) // for (GroupsQueueType::iterator itr = group; itr != SelectedGroups.end(); ++itr) // if (Player *pPlayer = itr->getSource()) // pPlayer->CompletedAchievement(AE); } else { player->CompletedAchievement(AE); } }