mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-22 05:06:24 +00:00
375 lines
15 KiB
C++
375 lines
15 KiB
C++
/*
|
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Affero 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 Affero 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Arena.h"
|
|
#include "ArenaTeamMgr.h"
|
|
#include "GroupMgr.h"
|
|
#include "Log.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "Player.h"
|
|
#include "World.h"
|
|
#include "WorldSession.h"
|
|
#include "Pet.h"
|
|
#include "ScriptMgr.h"
|
|
//#include "WorldStatePackets.h"
|
|
|
|
void ArenaScore::AppendToPacket(WorldPacket& data)
|
|
{
|
|
data << PlayerGuid;
|
|
|
|
data << uint32(KillingBlows);
|
|
data << uint8(PvPTeamId);
|
|
data << uint32(DamageDone);
|
|
data << uint32(HealingDone);
|
|
|
|
BuildObjectivesBlock(data);
|
|
}
|
|
|
|
void ArenaScore::BuildObjectivesBlock(WorldPacket& data)
|
|
{
|
|
data << uint32(0); // Objectives Count
|
|
}
|
|
|
|
void ArenaTeamScore::BuildRatingInfoBlock(WorldPacket& data)
|
|
{
|
|
uint32 ratingLost = std::abs(std::min(RatingChange, 0));
|
|
uint32 ratingWon = std::max(RatingChange, 0);
|
|
|
|
// should be old rating, new rating, and client will calculate rating change itself
|
|
data << uint32(ratingLost);
|
|
data << uint32(ratingWon);
|
|
data << uint32(MatchmakerRating);
|
|
}
|
|
|
|
void ArenaTeamScore::BuildTeamInfoBlock(WorldPacket& data)
|
|
{
|
|
data << TeamName;
|
|
}
|
|
|
|
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] = ARENA_TEXT_START_ONE_MINUTE;
|
|
StartMessageIds[BG_STARTING_EVENT_SECOND] = ARENA_TEXT_START_THIRTY_SECONDS;
|
|
StartMessageIds[BG_STARTING_EVENT_THIRD] = ARENA_TEXT_START_FIFTEEN_SECONDS;
|
|
StartMessageIds[BG_STARTING_EVENT_FOURTH] = ARENA_TEXT_START_BATTLE_HAS_BEGUN;
|
|
}
|
|
|
|
void Arena::AddPlayer(Player* player)
|
|
{
|
|
Battleground::AddPlayer(player);
|
|
PlayerScores.emplace(player->GetGUID().GetCounter(), new ArenaScore(player->GetGUID(), player->GetBgTeamId()));
|
|
|
|
if (player->GetBgTeamId() == TEAM_ALLIANCE) // gold
|
|
{
|
|
if (player->GetTeamId() == TEAM_HORDE)
|
|
player->CastSpell(player, SPELL_HORDE_GOLD_FLAG, true);
|
|
else
|
|
player->CastSpell(player, SPELL_ALLIANCE_GOLD_FLAG, true);
|
|
}
|
|
else // green
|
|
{
|
|
if (player->GetTeamId() == TEAM_HORDE)
|
|
player->CastSpell(player, SPELL_HORDE_GREEN_FLAG, true);
|
|
else
|
|
player->CastSpell(player, SPELL_ALLIANCE_GREEN_FLAG, true);
|
|
}
|
|
|
|
UpdateArenaWorldState();
|
|
|
|
Group* group = player->GetGroup();
|
|
if (group)
|
|
{
|
|
// Hackfix for crossfaction arenas, recreate group when joining
|
|
// Without this, players in a crossfaction arena group would not be able to cast beneficial spells on their teammates
|
|
|
|
std::vector<Player*> members;
|
|
bool isCrossfaction = false;
|
|
for (Group::member_citerator mitr = group->GetMemberSlots().begin(); mitr != group->GetMemberSlots().end(); ++mitr)
|
|
{
|
|
Player* member = ObjectAccessor::FindPlayer(mitr->guid);
|
|
if (!member || member->GetGUID() == player->GetGUID())
|
|
{
|
|
continue;
|
|
}
|
|
members.push_back(member);
|
|
if (member->GetTeamId(true) != player->GetTeamId(true))
|
|
{
|
|
isCrossfaction = true;
|
|
}
|
|
}
|
|
|
|
if (isCrossfaction)
|
|
{
|
|
for (Player* member : members)
|
|
{
|
|
member->RemoveFromGroup();
|
|
}
|
|
group->Disband();
|
|
|
|
group = new Group();
|
|
SetBgRaid(player->GetBgTeamId(), group);
|
|
group->Create(player);
|
|
sGroupMgr->AddGroup(group);
|
|
for (Player* member : members)
|
|
{
|
|
group->AddMember(member);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Arena::RemovePlayer(Player* /*player*/)
|
|
{
|
|
if (GetStatus() == STATUS_WAIT_LEAVE)
|
|
return;
|
|
|
|
UpdateArenaWorldState();
|
|
CheckWinConditions();
|
|
}
|
|
|
|
void Arena::FillInitialWorldStates(WorldPacket& data)
|
|
{
|
|
data << uint32(ARENA_WORLD_STATE_ALIVE_PLAYERS_GREEN) << uint32(GetAlivePlayersCountByTeam(TEAM_HORDE));
|
|
data << uint32(ARENA_WORLD_STATE_ALIVE_PLAYERS_GOLD) << uint32(GetAlivePlayersCountByTeam(TEAM_ALLIANCE));
|
|
}
|
|
|
|
void Arena::UpdateArenaWorldState()
|
|
{
|
|
UpdateWorldState(ARENA_WORLD_STATE_ALIVE_PLAYERS_GREEN, GetAlivePlayersCountByTeam(TEAM_HORDE));
|
|
UpdateWorldState(ARENA_WORLD_STATE_ALIVE_PLAYERS_GOLD, GetAlivePlayersCountByTeam(TEAM_ALLIANCE));
|
|
}
|
|
|
|
void Arena::HandleKillPlayer(Player* player, Player* killer)
|
|
{
|
|
if (GetStatus() != STATUS_IN_PROGRESS)
|
|
return;
|
|
|
|
Battleground::HandleKillPlayer(player, killer);
|
|
|
|
UpdateArenaWorldState();
|
|
CheckWinConditions();
|
|
}
|
|
|
|
void Arena::RemovePlayerAtLeave(Player* player)
|
|
{
|
|
if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
|
|
{
|
|
if (auto const& member = Acore::Containers::MapGetValuePtr(m_Players, player->GetGUID()))
|
|
{
|
|
// if the player was a match participant, calculate rating
|
|
auto teamId = member->GetBgTeamId();
|
|
|
|
ArenaTeam* winnerArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeamId(teamId)));
|
|
ArenaTeam* loserArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(teamId));
|
|
|
|
if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
|
|
loserArenaTeam->MemberLost(player, GetArenaMatchmakerRating(GetOtherTeamId(teamId)));
|
|
}
|
|
}
|
|
|
|
// remove player
|
|
Battleground::RemovePlayerAtLeave(player);
|
|
}
|
|
|
|
void Arena::CheckWinConditions()
|
|
{
|
|
if (!GetAlivePlayersCountByTeam(TEAM_ALLIANCE) && GetPlayersCountByTeam(TEAM_HORDE))
|
|
EndBattleground(TEAM_HORDE);
|
|
else if (GetPlayersCountByTeam(TEAM_ALLIANCE) && !GetAlivePlayersCountByTeam(TEAM_HORDE))
|
|
EndBattleground(TEAM_ALLIANCE);
|
|
}
|
|
|
|
void Arena::EndBattleground(TeamId winnerTeamId)
|
|
{
|
|
if (isRated())
|
|
{
|
|
uint32 startDelay = GetStartDelayTime();
|
|
bool bValidArena = GetStatus() == STATUS_IN_PROGRESS && GetStartTime() >= startDelay + 15000; // pussywizard: only if arena lasted at least 15 secs
|
|
|
|
uint32 loserTeamRating = 0;
|
|
uint32 loserMatchmakerRating = 0;
|
|
int32 loserChange = 0;
|
|
int32 loserMatchmakerChange = 0;
|
|
uint32 winnerTeamRating = 0;
|
|
uint32 winnerMatchmakerRating = 0;
|
|
int32 winnerChange = 0;
|
|
int32 winnerMatchmakerChange = 0;
|
|
|
|
ArenaTeam* winnerArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(winnerTeamId == TEAM_NEUTRAL ? TEAM_HORDE : winnerTeamId));
|
|
ArenaTeam* loserArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(winnerTeamId == TEAM_NEUTRAL ? TEAM_ALLIANCE : GetOtherTeamId(winnerTeamId)));
|
|
|
|
auto SaveArenaLogs = [&]()
|
|
{
|
|
// pussywizard: arena logs in database
|
|
uint32 fightId = sArenaTeamMgr->GetNextArenaLogId();
|
|
uint32 currOnline = sWorld->GetActiveSessionCount();
|
|
|
|
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
|
CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT);
|
|
stmt2->SetData(0, fightId);
|
|
stmt2->SetData(1, GetArenaType());
|
|
stmt2->SetData(2, ((GetStartTime() <= startDelay ? 0 : GetStartTime() - startDelay) / 1000));
|
|
stmt2->SetData(3, winnerArenaTeam->GetId());
|
|
stmt2->SetData(4, loserArenaTeam->GetId());
|
|
stmt2->SetData(5, (uint16)winnerTeamRating);
|
|
stmt2->SetData(6, (uint16)winnerMatchmakerRating);
|
|
stmt2->SetData(7, (int16)winnerChange);
|
|
stmt2->SetData(8, (uint16)loserTeamRating);
|
|
stmt2->SetData(9, (uint16)loserMatchmakerRating);
|
|
stmt2->SetData(10, (int16)loserChange);
|
|
stmt2->SetData(11, currOnline);
|
|
trans->Append(stmt2);
|
|
|
|
uint8 memberId = 0;
|
|
for (auto const& [playerGuid, arenaLogEntryData] : ArenaLogEntries)
|
|
{
|
|
stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_MEMBERSTATS);
|
|
stmt2->SetData(0, fightId);
|
|
stmt2->SetData(1, ++memberId);
|
|
stmt2->SetData(2, arenaLogEntryData.Name);
|
|
stmt2->SetData(3, arenaLogEntryData.Guid);
|
|
stmt2->SetData(4, arenaLogEntryData.ArenaTeamId);
|
|
stmt2->SetData(5, arenaLogEntryData.Acc);
|
|
stmt2->SetData(6, arenaLogEntryData.IP);
|
|
stmt2->SetData(7, arenaLogEntryData.DamageDone);
|
|
stmt2->SetData(8, arenaLogEntryData.HealingDone);
|
|
stmt2->SetData(9, arenaLogEntryData.KillingBlows);
|
|
trans->Append(stmt2);
|
|
}
|
|
|
|
CharacterDatabase.CommitTransaction(trans);
|
|
};
|
|
|
|
if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
|
|
{
|
|
loserTeamRating = loserArenaTeam->GetRating();
|
|
loserMatchmakerRating = GetArenaMatchmakerRating(GetOtherTeamId(winnerTeamId));
|
|
winnerTeamRating = winnerArenaTeam->GetRating();
|
|
winnerMatchmakerRating = GetArenaMatchmakerRating(winnerTeamId);
|
|
|
|
if (winnerTeamId != TEAM_NEUTRAL)
|
|
{
|
|
winnerMatchmakerChange = bValidArena ? winnerArenaTeam->WonAgainst(winnerMatchmakerRating, loserMatchmakerRating, winnerChange, GetBgMap()) : 0;
|
|
loserMatchmakerChange = loserArenaTeam->LostAgainst(loserMatchmakerRating, winnerMatchmakerRating, loserChange, GetBgMap());
|
|
|
|
sScriptMgr->OnAfterArenaRatingCalculation(this, winnerMatchmakerChange, loserMatchmakerChange, winnerChange, loserChange);
|
|
|
|
LOG_DEBUG("bg.arena", "match Type: {} --- Winner: old rating: {}, rating gain: {}, old MMR: {}, MMR gain: {} --- Loser: old rating: {}, rating loss: {}, old MMR: {}, MMR loss: {} ---",
|
|
GetArenaType(), winnerTeamRating, winnerChange, winnerMatchmakerRating, winnerMatchmakerChange,
|
|
loserTeamRating, loserChange, loserMatchmakerRating, loserMatchmakerChange);
|
|
|
|
SetArenaMatchmakerRating(winnerTeamId, winnerMatchmakerRating + winnerMatchmakerChange);
|
|
SetArenaMatchmakerRating(GetOtherTeamId(winnerTeamId), loserMatchmakerRating + loserMatchmakerChange);
|
|
|
|
// bg team that the client expects is different to TeamId
|
|
// alliance 1, horde 0
|
|
uint8 winnerTeam = winnerTeamId == TEAM_ALLIANCE ? PVP_TEAM_ALLIANCE : PVP_TEAM_HORDE;
|
|
uint8 loserTeam = winnerTeamId == TEAM_ALLIANCE ? PVP_TEAM_HORDE : PVP_TEAM_ALLIANCE;
|
|
|
|
_arenaTeamScores[winnerTeam].Assign(winnerChange, winnerMatchmakerRating, winnerArenaTeam->GetName());
|
|
_arenaTeamScores[loserTeam].Assign(loserChange, loserMatchmakerRating, loserArenaTeam->GetName());
|
|
|
|
LOG_DEBUG("bg.arena", "Arena match Type: {} for Team1Id: {} - Team2Id: {} ended. WinnerTeamId: {}. Winner rating: +{}, Loser rating: {}",
|
|
GetArenaType(), GetArenaTeamIdForTeam(TEAM_ALLIANCE), GetArenaTeamIdForTeam(TEAM_HORDE), winnerArenaTeam->GetId(), winnerChange, loserChange);
|
|
}
|
|
else // Deduct 16 points from each teams arena-rating if there are no winners after 45+2 minutes
|
|
{
|
|
// pussywizard: in case of a draw, the following is always true:
|
|
// winnerArenaTeam => TEAM_HORDE, loserArenaTeam => TEAM_ALLIANCE
|
|
|
|
winnerTeamRating = winnerArenaTeam->GetRating();
|
|
winnerMatchmakerRating = GetArenaMatchmakerRating(TEAM_HORDE);
|
|
loserTeamRating = loserArenaTeam->GetRating();
|
|
loserMatchmakerRating = GetArenaMatchmakerRating(TEAM_ALLIANCE);
|
|
winnerMatchmakerChange = 0;
|
|
loserMatchmakerChange = 0;
|
|
winnerChange = ARENA_TIMELIMIT_POINTS_LOSS;
|
|
loserChange = ARENA_TIMELIMIT_POINTS_LOSS;
|
|
|
|
_arenaTeamScores[PVP_TEAM_ALLIANCE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating, winnerArenaTeam->GetName());
|
|
_arenaTeamScores[PVP_TEAM_HORDE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating, loserArenaTeam->GetName());
|
|
|
|
winnerArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS, GetBgMap());
|
|
loserArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS, GetBgMap());
|
|
}
|
|
}
|
|
|
|
SaveArenaLogs();
|
|
|
|
uint8 aliveWinners = GetAlivePlayersCountByTeam(winnerTeamId);
|
|
|
|
for (auto const& [playerGuid, player] : GetPlayers())
|
|
{
|
|
auto const& bgTeamId = player->GetBgTeamId();
|
|
|
|
// per player calculation
|
|
if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
|
|
{
|
|
if (bgTeamId == winnerTeamId)
|
|
{
|
|
if (bValidArena)
|
|
{
|
|
// update achievement BEFORE personal rating update
|
|
uint32 rating = player->GetArenaPersonalRating(winnerArenaTeam->GetSlot());
|
|
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, rating ? rating : 1);
|
|
player->UpdateAchievementCriteria(ACHIEVEMENT_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);
|
|
}
|
|
|
|
winnerArenaTeam->MemberWon(player, loserMatchmakerRating, winnerMatchmakerChange);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
loserArenaTeam->MemberLost(player, winnerMatchmakerRating, loserMatchmakerChange);
|
|
|
|
// Arena lost => reset the win_rated_arena having the "no_lose" condition
|
|
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_NO_LOSE, 0);
|
|
}
|
|
|
|
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA, GetMapId());
|
|
}
|
|
}
|
|
|
|
// update previous opponents for arena queue
|
|
winnerArenaTeam->SetPreviousOpponents(loserArenaTeam->GetId());
|
|
loserArenaTeam->SetPreviousOpponents(winnerArenaTeam->GetId());
|
|
|
|
// save the stat changes
|
|
if (bValidArena)
|
|
{
|
|
winnerArenaTeam->SaveToDB();
|
|
winnerArenaTeam->NotifyStatsChanged();
|
|
}
|
|
|
|
loserArenaTeam->SaveToDB();
|
|
loserArenaTeam->NotifyStatsChanged();
|
|
}
|
|
|
|
// end battleground
|
|
Battleground::EndBattleground(winnerTeamId);
|
|
}
|