/*
* Copyright (C) 2008-2014 TrinityCore
* Copyright (C) 2005-2009 MaNGOS
*
* 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 "ArenaScore.h"
#include "ArenaTeam.h"
#include "ArenaTeamMgr.h"
#include "Battleground.h"
#include "BattlegroundMgr.h"
#include "BattlegroundScore.h"
#include "Creature.h"
#include "CreatureTextMgr.h"
#include "Chat.h"
#include "Formulas.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "MapManager.h"
#include "Object.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ReputationMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
namespace Trinity
{
class BattlegroundChatBuilder
{
public:
BattlegroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL)
: _msgtype(msgtype), _textId(textId), _source(source), _args(args) { }
void operator()(WorldPacket& data, LocaleConstant loc_idx)
{
char const* text = sObjectMgr->GetTrinityString(_textId, loc_idx);
if (_args)
{
// we need copy va_list before use or original va_list will corrupted
va_list ap;
va_copy(ap, *_args);
char str[2048];
vsnprintf(str, 2048, text, ap);
va_end(ap);
do_helper(data, &str[0]);
}
else
do_helper(data, text);
}
private:
void do_helper(WorldPacket& data, char const* text)
{
ChatHandler::BuildChatPacket(data, _msgtype, LANG_UNIVERSAL, _source, _source, text);
}
ChatMsg _msgtype;
int32 _textId;
Player const* _source;
va_list* _args;
};
class Battleground2ChatBuilder
{
public:
Battleground2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2)
: _msgtype(msgtype), _textId(textId), _source(source), _arg1(arg1), _arg2(arg2) { }
void operator()(WorldPacket& data, LocaleConstant loc_idx)
{
char const* text = sObjectMgr->GetTrinityString(_textId, loc_idx);
char const* arg1str = _arg1 ? sObjectMgr->GetTrinityString(_arg1, loc_idx) : "";
char const* arg2str = _arg2 ? sObjectMgr->GetTrinityString(_arg2, loc_idx) : "";
char str[2048];
snprintf(str, 2048, text, arg1str, arg2str);
ChatHandler::BuildChatPacket(data, _msgtype, LANG_UNIVERSAL, _source, _source, str);
}
private:
ChatMsg _msgtype;
int32 _textId;
Player const* _source;
int32 _arg1;
int32 _arg2;
};
} // namespace Trinity
template
void Battleground::BroadcastWorker(Do& _do)
{
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayer(itr, "BroadcastWorker"))
_do(player);
}
Battleground::Battleground()
{
m_TypeID = BATTLEGROUND_TYPE_NONE;
m_RandomTypeID = BATTLEGROUND_TYPE_NONE;
m_InstanceID = 0;
m_Status = STATUS_NONE;
m_ClientInstanceID = 0;
m_EndTime = 0;
m_LastResurrectTime = 0;
m_BracketId = BG_BRACKET_ID_FIRST;
m_InvitedAlliance = 0;
m_InvitedHorde = 0;
m_ArenaType = 0;
m_IsArena = false;
m_Winner = 2;
m_StartTime = 0;
m_ResetStatTimer = 0;
m_ValidStartPositionTimer = 0;
m_Events = 0;
m_StartDelayTime = 0;
m_IsRated = false;
m_BuffChange = false;
m_IsRandom = false;
m_Name = "";
m_LevelMin = 0;
m_LevelMax = 0;
m_InBGFreeSlotQueue = false;
m_SetDeleteThis = false;
m_MaxPlayersPerTeam = 0;
m_MaxPlayers = 0;
m_MinPlayersPerTeam = 0;
m_MinPlayers = 0;
m_MapId = 0;
m_Map = NULL;
m_StartMaxDist = 0.0f;
ScriptId = 0;
m_ArenaTeamIds[TEAM_ALLIANCE] = 0;
m_ArenaTeamIds[TEAM_HORDE] = 0;
m_ArenaTeamMMR[TEAM_ALLIANCE] = 0;
m_ArenaTeamMMR[TEAM_HORDE] = 0;
m_BgRaids[TEAM_ALLIANCE] = NULL;
m_BgRaids[TEAM_HORDE] = NULL;
m_PlayersCount[TEAM_ALLIANCE] = 0;
m_PlayersCount[TEAM_HORDE] = 0;
m_TeamScores[TEAM_ALLIANCE] = 0;
m_TeamScores[TEAM_HORDE] = 0;
m_PrematureCountDown = false;
m_PrematureCountDownTimer = 0;
m_HonorMode = BG_NORMAL;
StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE;
//we must set to some default existing values
StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES;
StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
}
Battleground::~Battleground()
{
// remove objects and creatures
// (this is done automatically in mapmanager update, when the instance is reset after the reset time)
uint32 size = uint32(BgCreatures.size());
for (uint32 i = 0; i < size; ++i)
DelCreature(i);
size = uint32(BgObjects.size());
for (uint32 i = 0; i < size; ++i)
DelObject(i);
sBattlegroundMgr->RemoveBattleground(GetTypeID(), GetInstanceID());
// unload map
if (m_Map)
{
m_Map->SetUnload();
//unlink to prevent crash, always unlink all pointer reference before destruction
m_Map->SetBG(NULL);
m_Map = NULL;
}
// remove from bg free slot queue
RemoveFromBGFreeSlotQueue();
for (BattlegroundScoreMap::const_iterator itr = PlayerScores.begin(); itr != PlayerScores.end(); ++itr)
delete itr->second;
}
void Battleground::Update(uint32 diff)
{
if (!PreUpdateImpl(diff))
return;
if (!GetPlayersSize())
{
//BG is empty
// if there are no players invited, delete BG
// this will delete arena or bg object, where any player entered
// [[ but if you use battleground object again (more battles possible to be played on 1 instance)
// then this condition should be removed and code:
// if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
// this->AddToFreeBGObjectsQueue(); // not yet implemented
// should be used instead of current
// ]]
// Battleground Template instance cannot be updated, because it would be deleted
if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
m_SetDeleteThis = true;
return;
}
switch (GetStatus())
{
case STATUS_WAIT_JOIN:
if (GetPlayersSize())
{
_ProcessJoin(diff);
_CheckSafePositions(diff);
}
break;
case STATUS_IN_PROGRESS:
_ProcessOfflineQueue();
// after 47 minutes without one team losing, the arena closes with no winner and no rating change
if (isArena())
{
if (GetStartTime() >= 47 * MINUTE*IN_MILLISECONDS)
{
UpdateArenaWorldState();
CheckArenaAfterTimerConditions();
return;
}
}
else
{
_ProcessResurrect(diff);
if (sBattlegroundMgr->GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
_ProcessProgress(diff);
else if (m_PrematureCountDown)
m_PrematureCountDown = false;
}
break;
case STATUS_WAIT_LEAVE:
_ProcessLeave(diff);
break;
default:
break;
}
// Update start time and reset stats timer
m_StartTime += diff;
m_ResetStatTimer += diff;
PostUpdateImpl(diff);
}
inline void Battleground::_CheckSafePositions(uint32 diff)
{
float maxDist = GetStartMaxDist();
if (!maxDist)
return;
m_ValidStartPositionTimer += diff;
if (m_ValidStartPositionTimer >= CHECK_PLAYER_POSITION_INVERVAL)
{
m_ValidStartPositionTimer = 0;
for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(itr->first))
{
Position pos = player->GetPosition();
Position const* startPos = GetTeamStartPosition(Battleground::GetTeamIndexByTeamId(player->GetBGTeam()));
if (pos.GetExactDistSq(startPos) > maxDist)
{
TC_LOG_DEBUG("bg.battleground", "BATTLEGROUND: Sending %s back to start location (map: %u) (possible exploit)", player->GetName().c_str(), GetMapId());
player->TeleportTo(GetMapId(), startPos->GetPositionX(), startPos->GetPositionY(), startPos->GetPositionZ(), startPos->GetOrientation());
}
}
}
}
inline void Battleground::_ProcessOfflineQueue()
{
// remove offline players from bg after 5 minutes
if (!m_OfflineQueue.empty())
{
BattlegroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
if (itr != m_Players.end())
{
if (itr->second.OfflineRemoveTime <= sWorld->GetGameTime())
{
RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
m_OfflineQueue.pop_front(); // remove from offline queue
//do not use itr for anything, because it is erased in RemovePlayerAtLeave()
}
}
}
}
inline void Battleground::_ProcessResurrect(uint32 diff)
{
// *********************************************************
// *** BATTLEGROUND RESURRECTION SYSTEM ***
// *********************************************************
// this should be handled by spell system
m_LastResurrectTime += diff;
if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
{
if (GetReviveQueueSize())
{
for (std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
{
Creature* sh = NULL;
for (std::vector::const_iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
{
Player* player = ObjectAccessor::FindPlayer(*itr2);
if (!player)
continue;
if (!sh && player->IsInWorld())
{
sh = player->GetMap()->GetCreature(itr->first);
// only for visual effect
if (sh)
// Spirit Heal, effect 117
sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true);
}
// Resurrection visual
player->CastSpell(player, SPELL_RESURRECTION_VISUAL, true);
m_ResurrectQueue.push_back(*itr2);
}
(itr->second).clear();
}
m_ReviveQueue.clear();
m_LastResurrectTime = 0;
}
else
// queue is clear and time passed, just update last resurrection time
m_LastResurrectTime = 0;
}
else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC
{
for (std::vector::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
{
Player* player = ObjectAccessor::FindPlayer(*itr);
if (!player)
continue;
player->ResurrectPlayer(1.0f);
player->CastSpell(player, 6962, true);
player->CastSpell(player, SPELL_SPIRIT_HEAL_MANA, true);
sObjectAccessor->ConvertCorpseForPlayer(*itr);
}
m_ResurrectQueue.clear();
}
}
uint32 Battleground::GetPrematureWinner()
{
uint32 winner = 0;
if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
winner = ALLIANCE;
else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
winner = HORDE;
return winner;
}
inline void Battleground::_ProcessProgress(uint32 diff)
{
// *********************************************************
// *** BATTLEGROUND BALLANCE SYSTEM ***
// *********************************************************
// if less then minimum players are in on one side, then start premature finish timer
if (!m_PrematureCountDown)
{
m_PrematureCountDown = true;
m_PrematureCountDownTimer = sBattlegroundMgr->GetPrematureFinishTime();
}
else if (m_PrematureCountDownTimer < diff)
{
// time's up!
EndBattleground(GetPrematureWinner());
m_PrematureCountDown = false;
}
else if (!sBattlegroundMgr->isTesting())
{
uint32 newtime = m_PrematureCountDownTimer - diff;
// announce every minute
if (newtime > (MINUTE * IN_MILLISECONDS))
{
if (newtime / (MINUTE * IN_MILLISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILLISECONDS))
PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILLISECONDS)));
}
else
{
//announce every 15 seconds
if (newtime / (15 * IN_MILLISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILLISECONDS))
PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILLISECONDS));
}
m_PrematureCountDownTimer = newtime;
}
}
inline void Battleground::_ProcessJoin(uint32 diff)
{
// *********************************************************
// *** BATTLEGROUND STARTING SYSTEM ***
// *********************************************************
ModifyStartDelayTime(diff);
if (m_ResetStatTimer > 5000)
{
m_ResetStatTimer = 0;
for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(itr->first))
player->ResetAllPowers();
}
if (!(m_Events & BG_STARTING_EVENT_1))
{
m_Events |= BG_STARTING_EVENT_1;
if (!FindBgMap())
{
TC_LOG_ERROR("bg.battleground", "Battleground::_ProcessJoin: map (map id: %u, instance id: %u) is not created!", m_MapId, m_InstanceID);
EndNow();
return;
}
// Setup here, only when at least one player has ported to the map
if (!SetupBattleground())
{
EndNow();
return;
}
StartingEventCloseDoors();
SetStartDelayTime(StartDelayTimes[BG_STARTING_EVENT_FIRST]);
// First start warning - 2 or 1 minute
SendMessageToAll(StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
// After 1 minute or 30 seconds, warning is signaled
else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
{
m_Events |= BG_STARTING_EVENT_2;
SendMessageToAll(StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
// After 30 or 15 seconds, warning is signaled
else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
{
m_Events |= BG_STARTING_EVENT_3;
SendMessageToAll(StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
// Delay expired (after 2 or 1 minute)
else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
{
m_Events |= BG_STARTING_EVENT_4;
StartingEventOpenDoors();
SendWarningToAll(StartMessageIds[BG_STARTING_EVENT_FOURTH]);
SetStatus(STATUS_IN_PROGRESS);
SetStartDelayTime(StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
// Remove preparation
if (isArena())
{
/// @todo add arena sound PlaySoundToAll(SOUND_ARENA_START);
for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(itr->first))
{
// BG Status packet
WorldPacket status;
BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(m_TypeID, GetArenaType());
uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId);
sBattlegroundMgr->BuildBattlegroundStatusPacket(&status, this, queueSlot, STATUS_IN_PROGRESS, 0, GetStartTime(), GetArenaType(), player->GetBGTeam());
player->SendDirectMessage(&status);
player->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
player->ResetAllPowers();
if (!player->IsGameMaster())
{
// remove auras with duration lower than 30s
Unit::AuraApplicationMap & auraMap = player->GetAppliedAuras();
for (Unit::AuraApplicationMap::iterator iter = auraMap.begin(); iter != auraMap.end();)
{
AuraApplication * aurApp = iter->second;
Aura* aura = aurApp->GetBase();
if (!aura->IsPermanent()
&& aura->GetDuration() <= 30*IN_MILLISECONDS
&& aurApp->IsPositive()
&& (!(aura->GetSpellInfo()->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY))
&& (!aura->HasEffectType(SPELL_AURA_MOD_INVISIBILITY)))
player->RemoveAura(iter);
else
++iter;
}
}
}
CheckArenaWinConditions();
}
else
{
PlaySoundToAll(SOUND_BG_START);
for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(itr->first))
{
player->RemoveAurasDueToSpell(SPELL_PREPARATION);
player->ResetAllPowers();
}
// Announce BG starting
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
sWorld->SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName().c_str(), GetMinLevel(), GetMaxLevel());
}
}
}
inline void Battleground::_ProcessLeave(uint32 diff)
{
// *********************************************************
// *** BATTLEGROUND ENDING SYSTEM ***
// *********************************************************
// remove all players from battleground after 2 minutes
m_EndTime -= diff;
if (m_EndTime <= 0)
{
m_EndTime = 0;
BattlegroundPlayerMap::iterator itr, next;
for (itr = m_Players.begin(); itr != m_Players.end(); itr = next)
{
next = itr;
++next;
//itr is erased here!
RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
// do not change any battleground's private variables
}
}
}
inline Player* Battleground::_GetPlayer(uint64 guid, bool offlineRemove, char const* context) const
{
Player* player = NULL;
if (!offlineRemove)
{
player = ObjectAccessor::FindPlayer(guid);
if (!player)
TC_LOG_ERROR("bg.battleground", "Battleground::%s: player (GUID: %u) not found for BG (map: %u, instance id: %u)!",
context, GUID_LOPART(guid), m_MapId, m_InstanceID);
}
return player;
}
inline Player* Battleground::_GetPlayer(BattlegroundPlayerMap::iterator itr, char const* context)
{
return _GetPlayer(itr->first, itr->second.OfflineRemoveTime, context);
}
inline Player* Battleground::_GetPlayer(BattlegroundPlayerMap::const_iterator itr, char const* context) const
{
return _GetPlayer(itr->first, itr->second.OfflineRemoveTime, context);
}
inline Player* Battleground::_GetPlayerForTeam(uint32 teamId, BattlegroundPlayerMap::const_iterator itr, char const* context) const
{
Player* player = _GetPlayer(itr, context);
if (player)
{
uint32 team = itr->second.Team;
if (!team)
team = player->GetTeam();
if (team != teamId)
player = NULL;
}
return player;
}
void Battleground::SetTeamStartPosition(TeamId teamId, Position const& pos)
{
ASSERT(teamId < TEAM_NEUTRAL);
StartPosition[teamId] = pos;
}
Position const* Battleground::GetTeamStartPosition(TeamId teamId) const
{
ASSERT(teamId < TEAM_NEUTRAL);
return &StartPosition[teamId];
}
void Battleground::SendPacketToAll(WorldPacket* packet)
{
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayer(itr, "SendPacketToAll"))
player->SendDirectMessage(packet);
}
void Battleground::SendPacketToTeam(uint32 TeamID, WorldPacket* packet, Player* sender, bool self)
{
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (Player* player = _GetPlayerForTeam(TeamID, itr, "SendPacketToTeam"))
{
if (self || sender != player)
player->SendDirectMessage(packet);
}
}
}
void Battleground::SendChatMessage(Creature* source, uint8 textId, WorldObject* target /*= NULL*/)
{
sCreatureTextMgr->SendChat(source, textId, target, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP);
}
void Battleground::PlaySoundToAll(uint32 SoundID)
{
WorldPacket data;
sBattlegroundMgr->BuildPlaySoundPacket(&data, SoundID);
SendPacketToAll(&data);
}
void Battleground::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
{
WorldPacket data;
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayerForTeam(TeamID, itr, "PlaySoundToTeam"))
{
sBattlegroundMgr->BuildPlaySoundPacket(&data, SoundID);
player->SendDirectMessage(&data);
}
}
void Battleground::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
{
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayerForTeam(TeamID, itr, "CastSpellOnTeam"))
player->CastSpell(player, SpellID, true);
}
void Battleground::RemoveAuraOnTeam(uint32 SpellID, uint32 TeamID)
{
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayerForTeam(TeamID, itr, "RemoveAuraOnTeam"))
player->RemoveAura(SpellID);
}
void Battleground::YellToAll(Creature* creature, char const* text, uint32 language)
{
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayer(itr, "YellToAll"))
{
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_MONSTER_YELL, Language(language), creature, player, text);
player->SendDirectMessage(&data);
}
}
void Battleground::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
{
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayerForTeam(TeamID, itr, "RewardHonorToTeam"))
UpdatePlayerScore(player, SCORE_BONUS_HONOR, Honor);
}
void Battleground::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
{
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id))
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayerForTeam(TeamID, itr, "RewardReputationToTeam"))
player->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
}
void Battleground::UpdateWorldState(uint32 Field, uint32 Value)
{
WorldPacket data;
sBattlegroundMgr->BuildUpdateWorldStatePacket(&data, Field, Value);
SendPacketToAll(&data);
}
void Battleground::UpdateWorldStateForPlayer(uint32 field, uint32 value, Player* player)
{
WorldPacket data;
sBattlegroundMgr->BuildUpdateWorldStatePacket(&data, field, value);
player->SendDirectMessage(&data);
}
void Battleground::EndBattleground(uint32 winner)
{
RemoveFromBGFreeSlotQueue();
ArenaTeam* winnerArenaTeam = NULL;
ArenaTeam* loserArenaTeam = NULL;
uint32 loserTeamRating = 0;
uint32 loserMatchmakerRating = 0;
int32 loserChange = 0;
int32 loserMatchmakerChange = 0;
uint32 winnerTeamRating = 0;
uint32 winnerMatchmakerRating = 0;
int32 winnerChange = 0;
int32 winnerMatchmakerChange = 0;
int32 winmsg_id = 0;
if (winner == ALLIANCE)
{
winmsg_id = isBattleground() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
SetWinner(WINNER_ALLIANCE);
}
else if (winner == HORDE)
{
winmsg_id = isBattleground() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
SetWinner(WINNER_HORDE);
}
else
{
SetWinner(3); // weird
}
SetStatus(STATUS_WAIT_LEAVE);
//we must set it this way, because end time is sent in packet!
m_EndTime = TIME_TO_AUTOREMOVE;
// arena rating calculation
if (isArena() && isRated())
{
winnerArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(winner));
loserArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
{
loserTeamRating = loserArenaTeam->GetRating();
loserMatchmakerRating = GetArenaMatchmakerRating(GetOtherTeam(winner));
winnerTeamRating = winnerArenaTeam->GetRating();
winnerMatchmakerRating = GetArenaMatchmakerRating(winner);
if (winner != WINNER_NONE)
{
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 ---", m_ArenaType, 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 ? WINNER_ALLIANCE : WINNER_HORDE;
uint8 loserTeam = winner == ALLIANCE ? WINNER_HORDE : WINNER_ALLIANCE;
_arenaTeamScores[winnerTeam].Assign(winnerChange, winnerMatchmakerRating, winnerArenaTeam->GetName());
_arenaTeamScores[loserTeam].Assign(loserChange, loserMatchmakerRating, loserArenaTeam->GetName());
TC_LOG_DEBUG("bg.arena", "Arena match Type: %u for Team1Id: %u - Team2Id: %u ended. WinnerTeamId: %u. Winner rating: +%d, Loser rating: %d", m_ArenaType, m_ArenaTeamIds[TEAM_ALLIANCE], m_ArenaTeamIds[TEAM_HORDE], winnerArenaTeam->GetId(), winnerChange, loserChange);
if (sWorld->getBoolConfig(CONFIG_ARENA_LOG_EXTENDED_INFO))
for (auto const& score : PlayerScores)
if (Player* player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(score.first, 0, HIGHGUID_PLAYER)))
{
TC_LOG_DEBUG("bg.arena", "Statistics match Type: %u for %s (GUID: %u, Team: %d, IP: %s): %s",
m_ArenaType, player->GetName().c_str(), score.first, player->GetArenaTeamId(m_ArenaType == 5 ? 2 : m_ArenaType == 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[WINNER_ALLIANCE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating, winnerArenaTeam->GetName());
_arenaTeamScores[WINNER_HORDE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating, loserArenaTeam->GetName());
winnerArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS);
loserArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS);
}
}
}
WorldPacket pvpLogData;
BuildPvPLogDataPacket(pvpLogData);
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
uint8 aliveWinners = GetAlivePlayersCountByTeam(winner);
for (BattlegroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
uint32 team = itr->second.Team;
if (itr->second.OfflineRemoveTime)
{
//if rated arena match - make member lost!
if (isArena() && isRated() && winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
{
if (team == winner)
winnerArenaTeam->OfflineMemberLost(itr->first, loserMatchmakerRating, winnerMatchmakerChange);
else
loserArenaTeam->OfflineMemberLost(itr->first, winnerMatchmakerRating, loserMatchmakerChange);
}
continue;
}
Player* player = _GetPlayer(itr, "EndBattleground");
if (!player)
continue;
// should remove spirit of redemption
if (player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
player->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
// Last standing - Rated 5v5 arena & be solely alive player
if (team == winner && isArena() && isRated() && GetArenaType() == ARENA_TYPE_5v5 && aliveWinners == 1 && player->IsAlive())
player->CastSpell(player, SPELL_THE_LAST_STANDING, true);
if (!player->IsAlive())
{
player->ResurrectPlayer(1.0f);
player->SpawnCorpseBones();
}
else
{
//needed cause else in av some creatures will kill the players at the end
player->CombatStop();
player->getHostileRefManager().deleteReferences();
}
// per player calculation
if (isArena() && isRated() && winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
{
if (team == winner)
{
// 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());
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_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOSE);
}
}
uint32 winner_kills = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_HONOR_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_HONOR_FIRST);
uint32 loser_kills = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_LOSER_HONOR_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_LOSER_HONOR_FIRST);
uint32 winner_arena = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_ARENA_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_ARENA_FIRST);
// Reward winner team
if (team == winner)
{
if (IsRandom() || BattlegroundMgr::IsBGWeekend(GetTypeID()))
{
UpdatePlayerScore(player, SCORE_BONUS_HONOR, GetBonusHonorFromKill(winner_kills));
if (CanAwardArenaPoints())
player->ModifyArenaPoints(winner_arena);
if (!player->GetRandomWinner())
player->SetRandomWinner(true);
}
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1);
}
else
{
if (IsRandom() || BattlegroundMgr::IsBGWeekend(GetTypeID()))
UpdatePlayerScore(player, SCORE_BONUS_HONOR, GetBonusHonorFromKill(loser_kills));
}
player->ResetAllPowers();
player->CombatStopWithPets(true);
BlockMovement(player);
player->SendDirectMessage(&pvpLogData);
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType(), player->GetBGTeam());
player->SendDirectMessage(&data);
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
}
if (isArena() && isRated() && winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
{
// 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();
}
if (winmsg_id)
SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
uint32 Battleground::GetBonusHonorFromKill(uint32 kills) const
{
//variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
uint32 maxLevel = std::min(GetMaxLevel(), 80U);
return Trinity::Honor::hk_honor_at_level(maxLevel, float(kills));
}
void Battleground::BlockMovement(Player* player)
{
player->SetClientControl(player, 0); // movement disabled NOTE: the effect will be automatically removed by client when the player is teleported from the battleground, so no need to send with uint8(1) in RemovePlayerAtLeave()
}
void Battleground::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
{
uint32 team = GetPlayerTeam(guid);
bool participant = false;
// Remove from lists/maps
BattlegroundPlayerMap::iterator itr = m_Players.find(guid);
if (itr != m_Players.end())
{
UpdatePlayersCountByTeam(team, true); // -1 player
m_Players.erase(itr);
// check if the player was a participant of the match, or only entered through gm command (goname)
participant = true;
}
BattlegroundScoreMap::iterator itr2 = PlayerScores.find(GUID_LOPART(guid));
if (itr2 != PlayerScores.end())
{
delete itr2->second; // delete player's score
PlayerScores.erase(itr2);
}
RemovePlayerFromResurrectQueue(guid);
Player* player = ObjectAccessor::FindPlayer(guid);
// should remove spirit of redemption
if (player)
{
if (player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
player->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
if (!player->IsAlive()) // resurrect on exit
{
player->ResurrectPlayer(1.0f);
player->SpawnCorpseBones();
}
}
else // try to resurrect the offline player. If he is alive nothing will happen
sObjectAccessor->ConvertCorpseForPlayer(guid);
RemovePlayer(player, guid, team); // BG subclass specific code
if (participant) // if the player was a match participant, remove auras, calc rating, update queue
{
BattlegroundTypeId bgTypeId = GetTypeID();
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
if (player)
{
player->ClearAfkReports();
if (!team) team = player->GetTeam();
// if arena, remove the specific arena auras
if (isArena())
{
bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
// unsummon current and summon old pet if there was one and there isn't a current pet
player->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
player->ResummonPetTemporaryUnSummonedIfAny();
if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
{
//left a rated match while the encounter was in progress, consider as loser
ArenaTeam* winnerArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
ArenaTeam* loserArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(team));
if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam)
loserArenaTeam->MemberLost(player, GetArenaMatchmakerRating(GetOtherTeam(team)));
}
}
if (SendPacket)
{
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0, 0);
player->SendDirectMessage(&data);
}
// this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
player->RemoveBattlegroundQueueId(bgQueueTypeId);
}
else
// removing offline participant
{
if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
{
//left a rated match while the encounter was in progress, consider as loser
ArenaTeam* others_arena_team = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
ArenaTeam* players_arena_team = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(team));
if (others_arena_team && players_arena_team)
players_arena_team->OfflineMemberLost(guid, GetArenaMatchmakerRating(GetOtherTeam(team)));
}
}
// remove from raid group if player is member
if (Group* group = GetBgRaid(team))
{
if (!group->RemoveMember(guid)) // group was disbanded
{
SetBgRaid(team, NULL);
}
}
DecreaseInvitedCount(team);
//we should update battleground queue, but only if bg isn't ending
if (isBattleground() && GetStatus() < STATUS_WAIT_LEAVE)
{
// a player has left the battleground, so there are free slots -> add to queue
AddToBGFreeSlotQueue();
sBattlegroundMgr->ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, GetBracketId());
}
// Let others know
WorldPacket data;
sBattlegroundMgr->BuildPlayerLeftBattlegroundPacket(&data, guid);
SendPacketToTeam(team, &data, player, false);
}
if (player)
{
// Do next only if found in battleground
player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
// reset destination bg team
player->SetBGTeam(0);
if (Transport)
player->TeleportToBGEntryPoint();
TC_LOG_DEBUG("bg.battleground", "Removed player %s from Battleground.", player->GetName().c_str());
}
//battleground object will be deleted next Battleground::Update() call
}
// this method is called when no players remains in battleground
void Battleground::Reset()
{
SetWinner(WINNER_NONE);
SetStatus(STATUS_WAIT_QUEUE);
SetStartTime(0);
SetEndTime(0);
SetLastResurrectTime(0);
m_Events = 0;
if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
TC_LOG_ERROR("bg.battleground", "Battleground::Reset: one of the counters is not 0 (alliance: %u, horde: %u) for BG (map: %u, instance id: %u)!",
m_InvitedAlliance, m_InvitedHorde, m_MapId, m_InstanceID);
m_InvitedAlliance = 0;
m_InvitedHorde = 0;
m_InBGFreeSlotQueue = false;
m_Players.clear();
for (BattlegroundScoreMap::const_iterator itr = PlayerScores.begin(); itr != PlayerScores.end(); ++itr)
delete itr->second;
PlayerScores.clear();
for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i)
_arenaTeamScores[i].Reset();
ResetBGSubclass();
}
void Battleground::StartBattleground()
{
SetStartTime(0);
SetLastResurrectTime(0);
// add BG to free slot queue
AddToBGFreeSlotQueue();
// add bg to update list
// This must be done here, because we need to have already invited some players when first BG::Update() method is executed
// and it doesn't matter if we call StartBattleground() more times, because m_Battlegrounds is a map and instance id never changes
sBattlegroundMgr->AddBattleground(this);
if (m_IsRated)
TC_LOG_DEBUG("bg.arena", "Arena match type: %u for Team1Id: %u - Team2Id: %u started.", m_ArenaType, m_ArenaTeamIds[TEAM_ALLIANCE], m_ArenaTeamIds[TEAM_HORDE]);
}
void Battleground::AddPlayer(Player* player)
{
// remove afk from player
if (player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
player->ToggleAFK();
// score struct must be created in inherited class
uint64 guid = player->GetGUID();
uint32 team = player->GetBGTeam();
BattlegroundPlayer bp;
bp.OfflineRemoveTime = 0;
bp.Team = team;
// Add to list/maps
m_Players[guid] = bp;
UpdatePlayersCountByTeam(team, false); // +1 player
WorldPacket data;
sBattlegroundMgr->BuildPlayerJoinedBattlegroundPacket(&data, player);
SendPacketToTeam(team, &data, player, false);
player->RemoveAurasByType(SPELL_AURA_MOUNTED);
// add arena specific auras
if (isArena())
{
player->RemoveArenaEnchantments(TEMP_ENCHANTMENT_SLOT);
if (team == 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);
}
player->DestroyConjuredItems(true);
player->UnsummonPetTemporaryIfAny();
if (GetStatus() == STATUS_WAIT_JOIN) // not started yet
{
player->CastSpell(player, SPELL_ARENA_PREPARATION, true);
player->ResetAllPowers();
}
}
else
{
if (GetStatus() == STATUS_WAIT_JOIN) // not started yet
player->CastSpell(player, SPELL_PREPARATION, true); // reduces all mana cost of spells.
}
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP, GetMapId(), true);
// setup BG group membership
PlayerAddedToBGCheckIfBGIsRunning(player);
AddOrSetPlayerToCorrectBgGroup(player, team);
}
// this method adds player to his team's bg group, or sets his correct group if player is already in bg group
void Battleground::AddOrSetPlayerToCorrectBgGroup(Player* player, uint32 team)
{
uint64 playerGuid = player->GetGUID();
Group* group = GetBgRaid(team);
if (!group) // first player joined
{
group = new Group;
SetBgRaid(team, group);
group->Create(player);
}
else // raid already exist
{
if (group->IsMember(playerGuid))
{
uint8 subgroup = group->GetMemberGroup(playerGuid);
player->SetBattlegroundOrBattlefieldRaid(group, subgroup);
}
else
{
group->AddMember(player);
if (Group* originalGroup = player->GetOriginalGroup())
if (originalGroup->IsLeader(playerGuid))
{
group->ChangeLeader(playerGuid);
group->SendUpdate();
}
}
}
}
// This method should be called when player logs into running battleground
void Battleground::EventPlayerLoggedIn(Player* player)
{
uint64 guid = player->GetGUID();
// player is correct pointer
for (std::deque::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
{
if (*itr == guid)
{
m_OfflineQueue.erase(itr);
break;
}
}
m_Players[guid].OfflineRemoveTime = 0;
PlayerAddedToBGCheckIfBGIsRunning(player);
// if battleground is starting, then add preparation aura
// we don't have to do that, because preparation aura isn't removed when player logs out
}
// This method should be called when player logs out from running battleground
void Battleground::EventPlayerLoggedOut(Player* player)
{
uint64 guid = player->GetGUID();
if (!IsPlayerInBattleground(guid)) // Check if this player really is in battleground (might be a GM who teleported inside)
return;
// player is correct pointer, it is checked in WorldSession::LogoutPlayer()
m_OfflineQueue.push_back(player->GetGUID());
m_Players[guid].OfflineRemoveTime = sWorld->GetGameTime() + MAX_OFFLINE_TIME;
if (GetStatus() == STATUS_IN_PROGRESS)
{
// drop flag and handle other cleanups
RemovePlayer(player, guid, GetPlayerTeam(guid));
// 1 player is logging out, if it is the last, then end arena!
if (isArena())
if (GetAlivePlayersCountByTeam(player->GetBGTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetBGTeam())))
EndBattleground(GetOtherTeam(player->GetBGTeam()));
}
}
// This method should be called only once ... it adds pointer to queue
void Battleground::AddToBGFreeSlotQueue()
{
if (!m_InBGFreeSlotQueue && isBattleground())
{
sBattlegroundMgr->AddToBGFreeSlotQueue(m_TypeID, this);
m_InBGFreeSlotQueue = true;
}
}
// This method removes this battleground from free queue - it must be called when deleting battleground
void Battleground::RemoveFromBGFreeSlotQueue()
{
if (m_InBGFreeSlotQueue)
{
sBattlegroundMgr->RemoveFromBGFreeSlotQueue(m_TypeID, m_InstanceID);
m_InBGFreeSlotQueue = false;
}
}
// get the number of free slots for team
// returns the number how many players can join battleground to MaxPlayersPerTeam
uint32 Battleground::GetFreeSlotsForTeam(uint32 Team) const
{
// if BG is starting ... invite anyone
if (GetStatus() == STATUS_WAIT_JOIN)
return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
// if BG is already started .. do not allow to join too much players of one faction
uint32 otherTeam;
uint32 otherIn;
if (Team == ALLIANCE)
{
otherTeam = GetInvitedCount(HORDE);
otherIn = GetPlayersCountByTeam(HORDE);
}
else
{
otherTeam = GetInvitedCount(ALLIANCE);
otherIn = GetPlayersCountByTeam(ALLIANCE);
}
if (GetStatus() == STATUS_IN_PROGRESS)
{
// difference based on ppl invited (not necessarily entered battle)
// default: allow 0
uint32 diff = 0;
// allow join one person if the sides are equal (to fill up bg to minplayersperteam)
if (otherTeam == GetInvitedCount(Team))
diff = 1;
// allow join more ppl if the other side has more players
else if (otherTeam > GetInvitedCount(Team))
diff = otherTeam - GetInvitedCount(Team);
// difference based on max players per team (don't allow inviting more)
uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
// difference based on players who already entered
// default: allow 0
uint32 diff3 = 0;
// allow join one person if the sides are equal (to fill up bg minplayersperteam)
if (otherIn == GetPlayersCountByTeam(Team))
diff3 = 1;
// allow join more ppl if the other side has more players
else if (otherIn > GetPlayersCountByTeam(Team))
diff3 = otherIn - GetPlayersCountByTeam(Team);
// or other side has less than minPlayersPerTeam
else if (GetInvitedCount(Team) <= GetMinPlayersPerTeam())
diff3 = GetMinPlayersPerTeam() - GetInvitedCount(Team) + 1;
// return the minimum of the 3 differences
// min of diff and diff 2
diff = std::min(diff, diff2);
// min of diff, diff2 and diff3
return std::min(diff, diff3);
}
return 0;
}
bool Battleground::HasFreeSlots() const
{
return GetPlayersSize() < GetMaxPlayers();
}
void Battleground::BuildPvPLogDataPacket(WorldPacket& data)
{
uint8 type = (isArena() ? 1 : 0);
data.Initialize(MSG_PVP_LOG_DATA, 1 + 1 + 4 + 40 * GetPlayerScoresSize());
data << uint8(type); // type (battleground = 0 / arena = 1)
if (type) // arena
{
for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i)
_arenaTeamScores[i].BuildRatingInfoBlock(data);
for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i)
_arenaTeamScores[i].BuildTeamInfoBlock(data);
}
if (GetStatus() == STATUS_WAIT_LEAVE)
{
data << uint8(1); // bg ended
data << uint8(GetWinner()); // who win
}
else
data << uint8(0); // bg not ended
data << uint32(GetPlayerScoresSize());
for (auto const& score : PlayerScores)
score.second->AppendToPacket(data);
}
bool Battleground::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor)
{
BattlegroundScoreMap::const_iterator itr = PlayerScores.find(player->GetGUIDLow());
if (itr == PlayerScores.end()) // player not found...
return false;
itr->second->UpdateScore(type, value);
if (type == SCORE_BONUS_HONOR && doAddHonor && isBattleground())
player->RewardHonor(NULL, 1, value); // RewardHonor calls UpdatePlayerScore with doAddHonor = false
return true;
}
void Battleground::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
{
m_ReviveQueue[npc_guid].push_back(player_guid);
Player* player = ObjectAccessor::FindPlayer(player_guid);
if (!player)
return;
player->CastSpell(player, SPELL_WAITING_FOR_RESURRECT, true);
}
void Battleground::RemovePlayerFromResurrectQueue(uint64 player_guid)
{
for (std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
{
for (std::vector::iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
{
if (*itr2 == player_guid)
{
(itr->second).erase(itr2);
if (Player* player = ObjectAccessor::FindPlayer(player_guid))
player->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
return;
}
}
}
}
void Battleground::RelocateDeadPlayers(uint64 queueIndex)
{
// Those who are waiting to resurrect at this node are taken to the closest own node's graveyard
std::vector& ghostList = m_ReviveQueue[queueIndex];
if (!ghostList.empty())
{
WorldSafeLocsEntry const* closestGrave = NULL;
for (std::vector::const_iterator itr = ghostList.begin(); itr != ghostList.end(); ++itr)
{
Player* player = ObjectAccessor::FindPlayer(*itr);
if (!player)
continue;
if (!closestGrave)
closestGrave = GetClosestGraveYard(player);
if (closestGrave)
player->TeleportTo(GetMapId(), closestGrave->x, closestGrave->y, closestGrave->z, player->GetOrientation());
}
ghostList.clear();
}
}
bool Battleground::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 /*respawnTime*/)
{
// If the assert is called, means that BgObjects must be resized!
ASSERT(type < BgObjects.size());
Map* map = FindBgMap();
if (!map)
return false;
// Must be created this way, adding to godatamap would add it to the base map of the instance
// and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
// So we must create it specific for this instance
GameObject* go = new GameObject;
if (!go->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_GAMEOBJECT), entry, GetBgMap(),
PHASEMASK_NORMAL, x, y, z, o, rotation0, rotation1, rotation2, rotation3, 100, GO_STATE_READY))
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddObject: cannot create gameobject (entry: %u) for BG (map: %u, instance id: %u)!",
entry, m_MapId, m_InstanceID);
delete go;
return false;
}
/*
uint32 guid = go->GetGUIDLow();
// without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
// iirc that was changed, so adding to go data map is no longer required if that was the only function using godata from GameObject without checking if it existed
GameObjectData& data = sObjectMgr->NewGOData(guid);
data.id = entry;
data.mapid = GetMapId();
data.posX = x;
data.posY = y;
data.posZ = z;
data.orientation = o;
data.rotation0 = rotation0;
data.rotation1 = rotation1;
data.rotation2 = rotation2;
data.rotation3 = rotation3;
data.spawntimesecs = respawnTime;
data.spawnMask = 1;
data.animprogress = 100;
data.go_state = 1;
*/
// Add to world, so it can be later looked up from HashMapHolder
if (!map->AddToMap(go))
{
delete go;
return false;
}
BgObjects[type] = go->GetGUID();
return true;
}
bool Battleground::AddObject(uint32 type, uint32 entry, Position const& pos, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime /*= 0*/)
{
return AddObject(type, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), rotation0, rotation1, rotation2, rotation3, respawnTime);
}
// Some doors aren't despawned so we cannot handle their closing in gameobject::update()
// It would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
void Battleground::DoorClose(uint32 type)
{
if (GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]))
{
// If doors are open, close it
if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
{
obj->SetLootState(GO_READY);
obj->SetGoState(GO_STATE_READY);
}
}
else
TC_LOG_ERROR("bg.battleground", "Battleground::DoorClose: door gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
}
void Battleground::DoorOpen(uint32 type)
{
if (GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]))
{
obj->SetLootState(GO_ACTIVATED);
obj->SetGoState(GO_STATE_ACTIVE);
}
else
TC_LOG_ERROR("bg.battleground", "Battleground::DoorOpen: door gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
}
GameObject* Battleground::GetBGObject(uint32 type, bool logError)
{
GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]);
if (!obj)
{
if (logError)
TC_LOG_ERROR("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
else
TC_LOG_INFO("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
}
return obj;
}
Creature* Battleground::GetBGCreature(uint32 type, bool logError)
{
Creature* creature = GetBgMap()->GetCreature(BgCreatures[type]);
if (!creature)
{
if (logError)
TC_LOG_ERROR("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID);
else
TC_LOG_INFO("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID);
}
return creature;
}
void Battleground::SpawnBGObject(uint32 type, uint32 respawntime)
{
if (Map* map = FindBgMap())
if (GameObject* obj = map->GetGameObject(BgObjects[type]))
{
if (respawntime)
obj->SetLootState(GO_JUST_DEACTIVATED);
else
if (obj->getLootState() == GO_JUST_DEACTIVATED)
// Change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
obj->SetLootState(GO_READY);
obj->SetRespawnTime(respawntime);
map->AddToMap(obj);
}
}
Creature* Battleground::AddCreature(uint32 entry, uint32 type, float x, float y, float z, float o, TeamId /*teamId = TEAM_NEUTRAL*/, uint32 respawntime /*= 0*/)
{
// If the assert is called, means that BgCreatures must be resized!
ASSERT(type < BgCreatures.size());
Map* map = FindBgMap();
if (!map)
return NULL;
Creature* creature = new Creature();
if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, x, y, z, o))
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddCreature: cannot create creature (entry: %u) for BG (map: %u, instance id: %u)!",
entry, m_MapId, m_InstanceID);
delete creature;
return NULL;
}
creature->SetHomePosition(x, y, z, o);
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
if (!cinfo)
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddCreature: creature template (entry: %u) does not exist for BG (map: %u, instance id: %u)!",
entry, m_MapId, m_InstanceID);
delete creature;
return NULL;
}
if (!map->AddToMap(creature))
{
delete creature;
return NULL;
}
BgCreatures[type] = creature->GetGUID();
if (respawntime)
creature->SetRespawnDelay(respawntime);
return creature;
}
Creature* Battleground::AddCreature(uint32 entry, uint32 type, Position const& pos, TeamId teamId /*= TEAM_NEUTRAL*/, uint32 respawntime /*= 0*/)
{
return AddCreature(entry, type, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teamId, respawntime);
}
bool Battleground::DelCreature(uint32 type)
{
if (!BgCreatures[type])
return true;
if (Creature* creature = GetBgMap()->GetCreature(BgCreatures[type]))
{
creature->AddObjectToRemoveList();
BgCreatures[type] = 0;
return true;
}
TC_LOG_ERROR("bg.battleground", "Battleground::DelCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID);
BgCreatures[type] = 0;
return false;
}
bool Battleground::DelObject(uint32 type)
{
if (!BgObjects[type])
return true;
if (GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]))
{
obj->SetRespawnTime(0); // not save respawn time
obj->Delete();
BgObjects[type] = 0;
return true;
}
TC_LOG_ERROR("bg.battleground", "Battleground::DelObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
BgObjects[type] = 0;
return false;
}
bool Battleground::AddSpiritGuide(uint32 type, float x, float y, float z, float o, TeamId teamId /*= TEAM_NEUTRAL*/)
{
uint32 entry = (teamId == TEAM_ALLIANCE) ? BG_CREATURE_ENTRY_A_SPIRITGUIDE : BG_CREATURE_ENTRY_H_SPIRITGUIDE;
if (Creature* creature = AddCreature(entry, type, x, y, z, o, teamId))
{
creature->setDeathState(DEAD);
creature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, creature->GetGUID());
// aura
/// @todo Fix display here
// creature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
// casting visual effect
creature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
// correct cast speed
creature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
//creature->CastSpell(creature, SPELL_SPIRIT_HEAL_CHANNEL, true);
return true;
}
TC_LOG_ERROR("bg.battleground", "Battleground::AddSpiritGuide: cannot create spirit guide (type: %u, entry: %u) for BG (map: %u, instance id: %u)!",
type, entry, m_MapId, m_InstanceID);
EndNow();
return false;
}
bool Battleground::AddSpiritGuide(uint32 type, Position const& pos, TeamId teamId /*= TEAM_NEUTRAL*/)
{
return AddSpiritGuide(type, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teamId);
}
void Battleground::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
{
if (!entry)
return;
Trinity::BattlegroundChatBuilder bg_builder(type, entry, source);
Trinity::LocalizedPacketDo bg_do(bg_builder);
BroadcastWorker(bg_do);
}
void Battleground::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
{
if (!entry)
return;
va_list ap;
va_start(ap, source);
Trinity::BattlegroundChatBuilder bg_builder(type, entry, source, &ap);
Trinity::LocalizedPacketDo bg_do(bg_builder);
BroadcastWorker(bg_do);
va_end(ap);
}
void Battleground::SendWarningToAll(int32 entry, ...)
{
if (!entry)
return;
std::map localizedPackets;
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
if (Player* player = _GetPlayer(itr, "SendWarningToAll"))
{
if (localizedPackets.find(player->GetSession()->GetSessionDbLocaleIndex()) == localizedPackets.end())
{
char const* format = sObjectMgr->GetTrinityString(entry, player->GetSession()->GetSessionDbLocaleIndex());
char str[1024];
va_list ap;
va_start(ap, entry);
vsnprintf(str, 1024, format, ap);
va_end(ap);
ChatHandler::BuildChatPacket(localizedPackets[player->GetSession()->GetSessionDbLocaleIndex()], CHAT_MSG_RAID_BOSS_EMOTE, LANG_UNIVERSAL, NULL, NULL, str);
}
player->SendDirectMessage(&localizedPackets[player->GetSession()->GetSessionDbLocaleIndex()]);
}
}
void Battleground::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
{
Trinity::Battleground2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
Trinity::LocalizedPacketDo bg_do(bg_builder);
BroadcastWorker(bg_do);
}
void Battleground::EndNow()
{
RemoveFromBGFreeSlotQueue();
SetStatus(STATUS_WAIT_LEAVE);
SetEndTime(0);
}
// To be removed
char const* Battleground::GetTrinityString(int32 entry)
{
// FIXME: now we have different DBC locales and need localized message for each target client
return sObjectMgr->GetTrinityStringForDBCLocale(entry);
}
// IMPORTANT NOTICE:
// buffs aren't spawned/despawned when players captures anything
// buffs are in their positions when battleground starts
void Battleground::HandleTriggerBuff(uint64 go_guid)
{
GameObject* obj = GetBgMap()->GetGameObject(go_guid);
if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
return;
// Change buff type, when buff is used:
int32 index = BgObjects.size() - 1;
while (index >= 0 && BgObjects[index] != go_guid)
index--;
if (index < 0)
{
TC_LOG_ERROR("bg.battleground", "Battleground::HandleTriggerBuff: cannot find buff gameobject (GUID: %u, entry: %u, type: %u) in internal data for BG (map: %u, instance id: %u)!",
GUID_LOPART(go_guid), obj->GetEntry(), obj->GetGoType(), m_MapId, m_InstanceID);
return;
}
// Randomly select new buff
uint8 buff = urand(0, 2);
uint32 entry = obj->GetEntry();
if (m_BuffChange && entry != Buff_Entries[buff])
{
// Despawn current buff
SpawnBGObject(index, RESPAWN_ONE_DAY);
// Set index for new one
for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
if (entry == Buff_Entries[currBuffTypeIndex])
{
index -= currBuffTypeIndex;
index += buff;
}
}
SpawnBGObject(index, BUFF_RESPAWN_TIME);
}
void Battleground::HandleKillPlayer(Player* victim, Player* killer)
{
// Keep in mind that for arena this will have to be changed a bit
// Add +1 deaths
UpdatePlayerScore(victim, SCORE_DEATHS, 1);
// Add +1 kills to group and +1 killing_blows to killer
if (killer)
{
// Don't reward credit for killing ourselves, like fall damage of hellfire (warlock)
if (killer == victim)
return;
UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player* creditedPlayer = ObjectAccessor::FindPlayer(itr->first);
if (!creditedPlayer || creditedPlayer == killer)
continue;
if (creditedPlayer->GetTeam() == killer->GetTeam() && creditedPlayer->IsAtGroupRewardDistance(victim))
UpdatePlayerScore(creditedPlayer, SCORE_HONORABLE_KILLS, 1);
}
}
if (!isArena())
{
// To be able to remove insignia -- ONLY IN Battlegrounds
victim->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
RewardXPAtKill(killer, victim);
}
}
// Return the player's team based on battlegroundplayer info
// Used in same faction arena matches mainly
uint32 Battleground::GetPlayerTeam(uint64 guid) const
{
BattlegroundPlayerMap::const_iterator itr = m_Players.find(guid);
if (itr != m_Players.end())
return itr->second.Team;
return 0;
}
uint32 Battleground::GetOtherTeam(uint32 teamId) const
{
return teamId ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0;
}
bool Battleground::IsPlayerInBattleground(uint64 guid) const
{
BattlegroundPlayerMap::const_iterator itr = m_Players.find(guid);
if (itr != m_Players.end())
return true;
return false;
}
void Battleground::PlayerAddedToBGCheckIfBGIsRunning(Player* player)
{
if (GetStatus() != STATUS_WAIT_LEAVE)
return;
WorldPacket data;
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
BlockMovement(player);
BuildPvPLogDataPacket(data);
player->SendDirectMessage(&data);
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType(), player->GetBGTeam());
player->SendDirectMessage(&data);
}
uint32 Battleground::GetAlivePlayersCountByTeam(uint32 Team) const
{
int count = 0;
for (BattlegroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (itr->second.Team == Team)
{
Player* player = ObjectAccessor::FindPlayer(itr->first);
if (player && player->IsAlive() && !player->HasByteFlag(UNIT_FIELD_BYTES_2, 3, FORM_SPIRITOFREDEMPTION))
++count;
}
}
return count;
}
void Battleground::SetHoliday(bool is_holiday)
{
m_HonorMode = is_holiday ? BG_HOLIDAY : BG_NORMAL;
}
int32 Battleground::GetObjectType(uint64 guid)
{
for (uint32 i = 0; i < BgObjects.size(); ++i)
if (BgObjects[i] == guid)
return i;
TC_LOG_ERROR("bg.battleground", "Battleground::GetObjectType: player used gameobject (GUID: %u) which is not in internal data for BG (map: %u, instance id: %u), cheating?",
GUID_LOPART(guid), m_MapId, m_InstanceID);
return -1;
}
void Battleground::HandleKillUnit(Creature* /*victim*/, Player* /*killer*/) { }
void Battleground::CheckArenaAfterTimerConditions()
{
EndBattleground(WINNER_NONE);
}
void Battleground::CheckArenaWinConditions()
{
if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
EndBattleground(HORDE);
else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
EndBattleground(ALLIANCE);
}
void Battleground::UpdateArenaWorldState()
{
UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE));
UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE));
}
void Battleground::SetBgRaid(uint32 TeamID, Group* bg_raid)
{
Group*& old_raid = TeamID == ALLIANCE ? m_BgRaids[TEAM_ALLIANCE] : m_BgRaids[TEAM_HORDE];
if (old_raid)
old_raid->SetBattlegroundGroup(NULL);
if (bg_raid)
bg_raid->SetBattlegroundGroup(this);
old_raid = bg_raid;
}
WorldSafeLocsEntry const* Battleground::GetClosestGraveYard(Player* player)
{
return sObjectMgr->GetClosestGraveYard(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam());
}
void Battleground::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
{
for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(itr->first))
player->StartTimedAchievement(type, entry);
}
void Battleground::SetBracket(PvPDifficultyEntry const* bracketEntry)
{
m_BracketId = bracketEntry->GetBracketId();
SetLevelRange(bracketEntry->minLevel, bracketEntry->maxLevel);
}
void Battleground::RewardXPAtKill(Player* killer, Player* victim)
{
if (sWorld->getBoolConfig(CONFIG_BG_XP_FOR_KILL) && killer && victim)
killer->RewardPlayerAndGroupAtKill(victim, true);
}
uint32 Battleground::GetTeamScore(uint32 teamId) const
{
if (teamId == TEAM_ALLIANCE || teamId == TEAM_HORDE)
return m_TeamScores[teamId];
TC_LOG_ERROR("bg.battleground", "GetTeamScore with wrong Team %u for BG %u", teamId, GetTypeID());
return 0;
}
void Battleground::HandleAreaTrigger(Player* player, uint32 trigger)
{
TC_LOG_DEBUG("bg.battleground", "Unhandled AreaTrigger %u in Battleground %u. Player coords (x: %f, y: %f, z: %f)",
trigger, player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
}
bool Battleground::CheckAchievementCriteriaMeet(uint32 criteriaId, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/)
{
TC_LOG_ERROR("bg.battleground", "Battleground::CheckAchievementCriteriaMeet: No implementation for criteria %u", criteriaId);
return false;
}