/* * Copyright (C) 2008-2017 TrinityCore * * 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, see . */ #include "Arena.h" #include "ArenaScore.h" #include "ArenaTeamMgr.h" #include "GuildMgr.h" #include "Guild.h" #include "Language.h" #include "Log.h" #include "ObjectAccessor.h" #include "Player.h" #include "World.h" #include "WorldStatePackets.h" #include "WorldSession.h" Arena::Arena() { StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; } void Arena::AddPlayer(Player* player) { Battleground::AddPlayer(player); PlayerScores[player->GetGUID()] = new ArenaScore(player->GetGUID(), player->GetBGTeam()); if (player->GetBGTeam() == ALLIANCE) // gold { if (player->GetTeam() == HORDE) player->CastSpell(player, SPELL_HORDE_GOLD_FLAG, true); else player->CastSpell(player, SPELL_ALLIANCE_GOLD_FLAG, true); } else // green { if (player->GetTeam() == HORDE) player->CastSpell(player, SPELL_HORDE_GREEN_FLAG, true); else player->CastSpell(player, SPELL_ALLIANCE_GREEN_FLAG, true); } UpdateArenaWorldState(); } void Arena::RemovePlayer(Player* /*player*/, ObjectGuid /*guid*/, uint32 /*team*/) { if (GetStatus() == STATUS_WAIT_LEAVE) return; UpdateArenaWorldState(); CheckWinConditions(); } void Arena::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet) { packet.Worldstates.emplace_back(uint32(ARENA_WORLD_STATE_ALIVE_PLAYERS_GREEN), int32(GetAlivePlayersCountByTeam(HORDE))); packet.Worldstates.emplace_back(uint32(ARENA_WORLD_STATE_ALIVE_PLAYERS_GOLD), int32(GetAlivePlayersCountByTeam(ALLIANCE))); } void Arena::UpdateArenaWorldState() { UpdateWorldState(ARENA_WORLD_STATE_ALIVE_PLAYERS_GREEN, GetAlivePlayersCountByTeam(HORDE)); UpdateWorldState(ARENA_WORLD_STATE_ALIVE_PLAYERS_GOLD, GetAlivePlayersCountByTeam(ALLIANCE)); } void Arena::HandleKillPlayer(Player* player, Player* killer) { if (GetStatus() != STATUS_IN_PROGRESS) return; Battleground::HandleKillPlayer(player, killer); UpdateArenaWorldState(); CheckWinConditions(); } void Arena::RemovePlayerAtLeave(ObjectGuid guid, bool transport, bool sendPacket) { if (isRated() && GetStatus() == STATUS_IN_PROGRESS) { BattlegroundPlayerMap::const_iterator itr = m_Players.find(guid); if (itr != m_Players.end()) // check if the player was a participant of the match, or only entered through gm command (appear) { // if the player was a match participant, calculate rating uint32 team = itr->second.Team; ArenaTeam* winnerArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); ArenaTeam* loserArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(team)); // left a rated match while the encounter was in progress, consider as loser if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam) { if (Player* player = _GetPlayer(itr->first, itr->second.OfflineRemoveTime != 0, "Arena::RemovePlayerAtLeave")) loserArenaTeam->MemberLost(player, GetArenaMatchmakerRating(GetOtherTeam(team))); else loserArenaTeam->OfflineMemberLost(guid, GetArenaMatchmakerRating(GetOtherTeam(team))); } } } // remove player Battleground::RemovePlayerAtLeave(guid, transport, sendPacket); } void Arena::CheckWinConditions() { if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) EndBattleground(HORDE); else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) EndBattleground(ALLIANCE); } void Arena::EndBattleground(uint32 winner) { // arena rating calculation if (isRated()) { uint32 loserTeamRating = 0; uint32 loserMatchmakerRating = 0; int32 loserChange = 0; int32 loserMatchmakerChange = 0; uint32 winnerTeamRating = 0; uint32 winnerMatchmakerRating = 0; int32 winnerChange = 0; int32 winnerMatchmakerChange = 0; bool guildAwarded = false; // In case of arena draw, follow this logic: // winnerArenaTeam => ALLIANCE, loserArenaTeam => HORDE ArenaTeam* winnerArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(winner == 0 ? uint32(ALLIANCE) : winner)); ArenaTeam* loserArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(winner == 0 ? uint32(HORDE) : GetOtherTeam(winner))); if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam) { // In case of arena draw, follow this logic: // winnerMatchmakerRating => ALLIANCE, loserMatchmakerRating => HORDE loserTeamRating = loserArenaTeam->GetRating(); loserMatchmakerRating = GetArenaMatchmakerRating(winner == 0 ? uint32(HORDE) : GetOtherTeam(winner)); winnerTeamRating = winnerArenaTeam->GetRating(); winnerMatchmakerRating = GetArenaMatchmakerRating(winner == 0 ? uint32(ALLIANCE) : winner); if (winner != 0) { winnerMatchmakerChange = winnerArenaTeam->WonAgainst(winnerMatchmakerRating, loserMatchmakerRating, winnerChange); loserMatchmakerChange = loserArenaTeam->LostAgainst(loserMatchmakerRating, winnerMatchmakerRating, loserChange); TC_LOG_DEBUG("bg.arena", "match Type: %u --- Winner: old rating: %u, rating gain: %d, old MMR: %u, MMR gain: %d --- Loser: old rating: %u, rating loss: %d, old MMR: %u, MMR loss: %d ---", GetArenaType(), winnerTeamRating, winnerChange, winnerMatchmakerRating, winnerMatchmakerChange, loserTeamRating, loserChange, loserMatchmakerRating, loserMatchmakerChange); SetArenaMatchmakerRating(winner, winnerMatchmakerRating + winnerMatchmakerChange); SetArenaMatchmakerRating(GetOtherTeam(winner), loserMatchmakerRating + loserMatchmakerChange); // bg team that the client expects is different to TeamId // alliance 1, horde 0 uint8 winnerTeam = winner == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; uint8 loserTeam = winner == ALLIANCE ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE; _arenaTeamScores[winnerTeam].Assign(winnerTeamRating, winnerTeamRating + winnerChange, winnerMatchmakerRating); _arenaTeamScores[loserTeam].Assign(loserTeamRating, loserTeamRating + loserChange, loserMatchmakerRating); TC_LOG_DEBUG("bg.arena", "Arena match Type: %u for Team1Id: %u - Team2Id: %u ended. WinnerTeamId: %u. Winner rating: +%d, Loser rating: %d", GetArenaType(), GetArenaTeamIdByIndex(TEAM_ALLIANCE), GetArenaTeamIdByIndex(TEAM_HORDE), winnerArenaTeam->GetId(), winnerChange, loserChange); if (sWorld->getBoolConfig(CONFIG_ARENA_LOG_EXTENDED_INFO)) for (auto const& score : PlayerScores) if (Player* player = ObjectAccessor::FindConnectedPlayer(score.first)) { TC_LOG_DEBUG("bg.arena", "Statistics match Type: %u for %s (%s, Team: %d, IP: %s): %s", GetArenaType(), player->GetName().c_str(), score.first.ToString().c_str(), player->GetArenaTeamId(GetArenaType() == 5 ? 2 : GetArenaType() == 3), player->GetSession()->GetRemoteAddress().c_str(), score.second->ToString().c_str()); } } // Deduct 16 points from each teams arena-rating if there are no winners after 45+2 minutes else { _arenaTeamScores[BG_TEAM_ALLIANCE].Assign(winnerTeamRating, winnerTeamRating + ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating); _arenaTeamScores[BG_TEAM_HORDE].Assign(loserTeamRating, loserTeamRating + ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating); winnerArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); loserArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); } uint8 aliveWinners = GetAlivePlayersCountByTeam(winner); for (auto const& i : GetPlayers()) { uint32 team = i.second.Team; if (i.second.OfflineRemoveTime) { // if rated arena match - make member lost! if (team == winner) winnerArenaTeam->OfflineMemberLost(i.first, loserMatchmakerRating, winnerMatchmakerChange); else { if (winner == 0) winnerArenaTeam->OfflineMemberLost(i.first, loserMatchmakerRating, winnerMatchmakerChange); loserArenaTeam->OfflineMemberLost(i.first, winnerMatchmakerRating, loserMatchmakerChange); } continue; } Player* player = _GetPlayer(i.first, i.second.OfflineRemoveTime != 0, "Arena::EndBattleground"); if (!player) continue; // per player calculation if (team == winner) { // update achievement BEFORE personal rating update uint32 rating = player->GetArenaPersonalRating(winnerArenaTeam->GetSlot()); player->UpdateCriteria(CRITERIA_TYPE_WIN_RATED_ARENA, rating ? rating : 1); player->UpdateCriteria(CRITERIA_TYPE_WIN_ARENA, GetMapId()); // Last standing - Rated 5v5 arena & be solely alive player if (GetArenaType() == ARENA_TYPE_5v5 && aliveWinners == 1 && player->IsAlive()) player->CastSpell(player, SPELL_LAST_MAN_STANDING, true); if (!guildAwarded) { guildAwarded = true; if (ObjectGuid::LowType guildId = GetBgMap()->GetOwnerGuildId(player->GetBGTeam())) if (Guild* guild = sGuildMgr->GetGuildById(guildId)) guild->UpdateCriteria(CRITERIA_TYPE_WIN_RATED_ARENA, std::max(winnerArenaTeam->GetRating(), 1), 0, 0, NULL, player); } winnerArenaTeam->MemberWon(player, loserMatchmakerRating, winnerMatchmakerChange); } else { if (winner == 0) winnerArenaTeam->MemberLost(player, loserMatchmakerRating, winnerMatchmakerChange); loserArenaTeam->MemberLost(player, winnerMatchmakerRating, loserMatchmakerChange); // Arena lost => reset the win_rated_arena having the "no_lose" condition player->ResetCriteria(CRITERIA_TYPE_WIN_RATED_ARENA, CRITERIA_CONDITION_NO_LOSE); } } // save the stat changes winnerArenaTeam->SaveToDB(); loserArenaTeam->SaveToDB(); // send updated arena team stats to players // this way all arena team members will get notified, not only the ones who participated in this match winnerArenaTeam->NotifyStatsChanged(); loserArenaTeam->NotifyStatsChanged(); } } // end battleground Battleground::EndBattleground(winner); }