/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* 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 "StdAfx.h"
initialiseSingleton(CBattlegroundManager);
typedef CBattleground*(*CreateBattlegroundFunc)(MapMgr* mgr,uint32 iid,uint32 group, uint32 type);
const static uint32 BGMapIds[BATTLEGROUND_NUM_TYPES] = {
0, // 0
30, // AV
489, // WSG
529, // AB
0, // 2v2
0, // 3v3
0, // 5v5
566, // Netherstorm BG
};
const static CreateBattlegroundFunc BGCFuncs[BATTLEGROUND_NUM_TYPES] = {
NULL, // 0
NULL, // AV
&WarsongGulch::Create, // WSG
NULL, // AB
NULL, // 2v2
NULL, // 3v3
NULL, // 5v5
NULL, // Netherstorm
};
CBattlegroundManager::CBattlegroundManager() : EventableObject()
{
m_maxBattlegroundId = 0;
sEventMgr.AddEvent(this, &CBattlegroundManager::EventQueueUpdate, EVENT_BATTLEGROUND_QUEUE_UPDATE, 15000, 0,0);
}
CBattlegroundManager::~CBattlegroundManager()
{
}
void CBattlegroundManager::HandleBattlegroundListPacket(WorldSession * m_session, uint32 BattlegroundType)
{
if(BattlegroundType == BATTLEGROUND_ARENA_2V2 || BattlegroundType == BATTLEGROUND_ARENA_3V3 || BattlegroundType == BATTLEGROUND_ARENA_5V5)
{
WorldPacket data(SMSG_BATTLEFIELD_LIST, 17);
data << m_session->GetPlayer()->GetGUID() << uint32(6) << uint32(0xC) << uint8(0);
m_session->SendPacket(&data);
return;
}
uint32 LevelGroup = GetLevelGrouping(m_session->GetPlayer()->getLevel());
uint32 Count = 0;
WorldPacket data(SMSG_BATTLEFIELD_LIST, 200);
data << m_session->GetPlayer()->GetGUID();
data << BattlegroundType;
data << uint8(2);
data << uint32(0); // Count
/* Append the battlegrounds */
m_instanceLock.AcquireReadLock();
for(map::iterator itr = m_instances[BattlegroundType].begin(); itr != m_instances[BattlegroundType].end(); ++itr)
{
if(itr->second->GetLevelGroup() == LevelGroup)
{
data << itr->first;
++Count;
}
}
m_instanceLock.ReleaseReadLock();
#ifdef USING_BIG_ENDIAN
*(uint32*)&data.contents()[13] = swap32(Count);
#else
*(uint32*)&data.contents()[13] = Count;
#endif
m_session->SendPacket(&data);
}
void CBattlegroundManager::HandleBattlegroundJoin(WorldSession * m_session, WorldPacket & pck)
{
uint64 guid;
uint32 pguid = m_session->GetPlayer()->GetGUIDLow();
uint32 lgroup = GetLevelGrouping(m_session->GetPlayer()->getLevel());
uint32 bgtype;
uint32 instance;
pck >> guid >> bgtype >> instance;
if(bgtype >= BATTLEGROUND_NUM_TYPES)
return; // cheater!
/* Check the instance id */
if(instance)
{
/* We haven't picked the first instance. This means we've specified an instance to join. */
m_instanceLock.AcquireReadLock();
map::iterator itr = m_instances[bgtype].find(instance);
if(itr == m_instances[bgtype].end())
{
sChatHandler.SystemMessage(m_session, "You have tried to join an invalid instance id.");
m_instanceLock.ReleaseReadLock();
return;
}
m_instanceLock.ReleaseReadLock();
}
/* Queue him! */
m_queueLock.Acquire();
map >::iterator itr = m_queuedPlayers[bgtype][lgroup].find(instance);
if(itr != m_queuedPlayers[bgtype][lgroup].end())
itr->second.push_back(pguid);
else
{
Log.Debug("BattlegroundManager", "Created queue for battleground type %u in levelgroup %u", bgtype, lgroup);
list tmp;
tmp.push_back(pguid);
m_queuedPlayers[bgtype][lgroup].insert( make_pair( instance, tmp ) );
}
Log.Success("BattlegroundManager", "Player %u is now in battleground queue for instance %u", m_session->GetPlayer()->GetGUIDLow(), instance );
/* send the battleground status packet */
SendBattlefieldStatus(m_session->GetPlayer(), 1, bgtype, instance, 0, BGMapIds[bgtype]);
m_session->GetPlayer()->m_bgIsQueued = true;
m_session->GetPlayer()->m_bgQueueInstanceId = instance;
m_session->GetPlayer()->m_bgQueueType = bgtype;
m_queueLock.Release();
/* We will get updated next few seconds =) */
}
void CBattlegroundManager::EventQueueUpdate()
{
vector tempPlayerVec[2];
uint32 i,j,k;
Player * plr;
CBattleground * bg;
map::iterator itr;
map >::iterator it2, it5;
list::iterator it3, it4;
vector::iterator it6;
m_queueLock.Acquire();
for(i = 0; i < BATTLEGROUND_NUM_TYPES; ++i)
{
for(j = 0; j < MAX_LEVEL_GROUP; ++j)
{
if(!m_queuedPlayers[i][j].size())
continue;
/* Process instance id 0 first. */
if( (it2 = m_queuedPlayers[i][j].find( 0 )) != m_queuedPlayers[i][j].end() )
{
/* These players have chosen to join the "first available" instance. */
/* Try and find a free instance. */
bg=0;
m_instanceLock.AcquireReadLock();
for(itr = m_instances[i].begin(); itr != m_instances[i].end(); ++itr)
{
if(itr->second->GetLevelGroup() != j)
{
Log.Debug("BgMgr", "Bad level group");
continue;
}
Log.Debug("BattlegroundMgr", "Trying instance %u", itr->second->GetId());
if(!itr->second->IsFull())
{
bg = itr->second;
/* Add as many players as possible to this battleground. */
for(it4 = it2->second.begin(); it4 != it2->second.end();)
{
it3 = it4;
++it4;
plr = objmgr.GetPlayer(*it3);
if(!plr)
{
it2->second.erase(it3);
continue;
}
Log.Debug("BattlegroundMgr", "Trying player %u", plr->GetGUIDLow());
if(bg->HasFreeSlots(plr->GetTeam()) && bg->CanPlayerJoin(plr))
{
plr->m_bgIsQueued = false;
if(GetLevelGrouping(plr->getLevel()) == j)
bg->AddPlayer(plr);
it2->second.erase(it3);
Log.Debug("BattlegroundMgr", "Added!");
}
else
Log.Debug("BattlegroundMgr", "Fail.");
}
/* No players left? */
if(!it2->second.size())
break;
}
else
{
Log.Debug("BattlegroundMgr", "Instance is full.");
}
}
m_instanceLock.ReleaseReadLock();
/* Do we still have players left over? */
if(it2->second.size())
{
if(CanCreateInstance(i, j))
{
// No free instances.
// Do we have enough players to create a new instance?
for(it4 = it2->second.begin(); it4 != it2->second.end();)
{
it3 = it4;
++it4;
plr = objmgr.GetPlayer(*it3);
if(!plr || GetLevelGrouping(plr->getLevel()) != j)
{
it2->second.erase(it3);
continue;
}
tempPlayerVec[plr->GetTeam()].push_back(plr);
}
if(tempPlayerVec[0].size() >= MINIMUM_PLAYERS_ON_EACH_SIDE_FOR_BG &&
tempPlayerVec[1].size() >= MINIMUM_PLAYERS_ON_EACH_SIDE_FOR_BG)
{
Log.Debug("BattlegroundManager", "Enough players to start battleground type %u for level group %u. Creating.", i, j);
/* Woot! Let's create a new instance! */
bg = CreateInstance(i, j);
/* Dump all the players into the bg */
for(k=0;k<2;++k)
{
for(it6=tempPlayerVec[k].begin(); it6 != tempPlayerVec[k].end(); ++it6)
{
Log.Debug("BattlegroundManager", "Trying to add player %u to bg", (*it6)->GetGUIDLow());
if(!bg->HasFreeSlots(k) || !bg->CanPlayerJoin(*it6))
{
Log.Debug("BattlegroundManager", "FAIL!", (*it6)->GetGUIDLow());
break;
}
bg->AddPlayer(*it6);
/* This is gonna be costly. :P */
for(it4 = it2->second.begin(); it4 != it2->second.end(); ++it4)
{
if((*it4) == (*it6)->GetGUIDLow())
{
it2->second.erase(it4);
break;
}
}
}
}
}
tempPlayerVec[0].clear();
tempPlayerVec[1].clear();
}
}
/* If there is no players queued on instance id 0 anymore, kill the iterator (Saves memory) */
if(!it2->second.size())
m_queuedPlayers[i][j].erase(it2);
}
// Now process the players bound to a specific instance id.
m_instanceLock.AcquireReadLock();
for(it5 = m_queuedPlayers[i][j].begin(); it5 != m_queuedPlayers[i][j].end();)
{
it2 = it5;
++it5;
/* Skip the '0' instance id (handled differently) */
if(it2->first == 0)
continue;
if(!it2->second.size()) // No queued players on this instance any more
{
/* Kill the iterator */
m_queuedPlayers[i][j].erase(it2);
continue;
}
/* Find the instance that these players are queued on. */
itr = m_instances[i].find(it2->first);
if(itr == m_instances[i].end())
{
/* Whoops! That instance is no longer valid. */
/* Kill all the players on it. */
for(it3 = it2->second.begin(); it3 != it2->second.end(); ++it3)
{
plr = objmgr.GetPlayer(*it3);
if(plr)
{
sChatHandler.SystemMessageToPlr(plr, "Your queue on battleground instance %u is no longer valid, the instance no longer exists.", it2->first);
SendBattlefieldStatus(plr, 0, 0, 0, 0, 0);
plr->m_bgIsQueued = false;
}
}
m_queuedPlayers[i][j].erase(it2);
continue;
}
else
{
/* Do we have free slots? */
if(!itr->second->IsFull())
{
/* Add as many players as possible. */
for(it4 = it2->second.begin(); it4 != it2->second.end();)
{
it3 = it4;
++it4;
plr = objmgr.GetPlayer(*it3);
if(!plr)
it2->second.erase(it3);
else
{
if(itr->second->HasFreeSlots(plr->GetTeam()))
{
plr->m_bgIsQueued = false;
if(GetLevelGrouping(plr->getLevel()) == j)
{
itr->second->AddPlayer(plr);
}
}
}
}
}
}
}
m_instanceLock.ReleaseReadLock();
}
}
m_queueLock.Release();
}
void CBattlegroundManager::RemovePlayerFromQueues(Player * plr)
{
uint32 lgroup = GetLevelGrouping(plr->getLevel());
m_queueLock.Acquire();
ASSERT(plr->m_bgQueueType < BATTLEGROUND_NUM_TYPES);
map >::iterator itr = m_queuedPlayers[plr->m_bgQueueType][lgroup].find(plr->m_bgQueueInstanceId);
list::iterator it2;
if(itr != m_queuedPlayers[plr->m_bgQueueType][lgroup].end())
{
for(it2 = itr->second.begin(); it2 != itr->second.end(); ++it2) {
if((*it2) == plr->GetGUIDLow())
{
Log.Debug("BattlegroundManager", "Removing player %u from queue in instance %u type %u group %u", plr->GetGUIDLow(), plr->m_bgQueueInstanceId, plr->m_bgQueueType, lgroup);
itr->second.erase(it2);
break;
}
}
}
plr->m_bgIsQueued = false;
m_queueLock.Release();
}
bool CBattlegroundManager::CanCreateInstance(uint32 Type, uint32 LevelGroup)
{
uint32 lc = 0;
for(map::iterator itr = m_instances[Type].begin(); itr != m_instances[Type].end(); ++itr)
{
if(itr->second->GetLevelGroup() == LevelGroup)
{
lc++;
if(lc >= MAXIMUM_BATTLEGROUNDS_PER_LEVEL_GROUP)
return false;
}
}
return true;
}
void CBattleground::SendWorldStates(Player * plr)
{
if(!m_worldStates.size())
return;
uint32 bflag = 0;
uint32 bflag2 = 0;
switch(m_mapMgr->GetMapId())
{
case 489: bflag = 0x0CCD; bflag2 = 0x0CF9; break;
case 529: bflag = 0x0D1E; break;
case 30: bflag = 0x0A25; break;
case 559: bflag = 3698; break;
default: /* arenas */
bflag = 0x0E76;
bflag2 = 0;
break;
}
WorldPacket data(SMSG_INIT_WORLD_STATES, 10 + (m_worldStates.size() * 8));
data << m_mapMgr->GetMapId();
data << bflag;
data << bflag2;
data << uint16(m_worldStates.size());
for(map::iterator itr = m_worldStates.begin(); itr != m_worldStates.end(); ++itr)
data << itr->first << itr->second;
plr->GetSession()->SendPacket(&data);
}
CBattleground::CBattleground(MapMgr * mgr, uint32 id, uint32 levelgroup, uint32 type) : m_mapMgr(mgr), m_id(id), m_levelGroup(levelgroup), m_type(type)
{
m_nextPvPUpdateTime = 0;
m_countdownStage = 0;
m_ended = false;
m_winningteam = 0;
m_startTime = World::UNIXTIME;
m_lastResurrect = World::UNIXTIME;
sEventMgr.AddEvent(this, &CBattleground::EventResurrectPlayers, EVENT_BATTLEGROUND_QUEUE_UPDATE, 30000, 0,0);
/* create raid groups */
for(uint32 i = 0; i < 2; ++i)
{
m_groups[i] = new Group();
m_groups[i]->m_disbandOnNoMembers = false;
m_groups[i]->ExpandToRaid();
}
}
CBattleground::~CBattleground()
{
sEventMgr.RemoveEvents(this);
for(uint32 i = 0; i < 2; ++i)
{
Player * plr;
PlayerInfo *inf;
for(uint32 j = 0; j < m_groups[i]->GetSubGroupCount(); ++j) {
for(GroupMembersSet::iterator itr = m_groups[i]->GetSubGroup(j)->GetGroupMembersBegin(); itr != m_groups[i]->GetSubGroup(j)->GetGroupMembersEnd();) {
plr = itr->player;
inf = itr->player_info;
++itr;
m_groups[i]->RemovePlayer(inf, plr, true);
}
}
delete m_groups[i];
}
}
void CBattleground::UpdatePvPData()
{
if(World::UNIXTIME >= m_nextPvPUpdateTime)
{
m_mainLock.Acquire();
WorldPacket data(MSG_PVP_LOG_DATA, 50);
BGScore * bs;
/*if(m_type >= BATTLEGROUND_ARENA_2V2 && m_type <= BATTLEGROUND_ARENA_5V5 && !m_ended)
{
data << uint8(1);
data << uint32(0x6C0EF0F4);
data << uint32(0x00000400);
data << uint32(0x01010000);
data << uint32(0x00000009);
data << uint32(0x001A070E);
data << uint32(0x00000000);
}
else*/
data << uint8(0);
if(m_ended)
{
data << uint8(1);
data << uint8(m_winningteam);
data << uint32(m_players[0].size() + m_players[1].size());
for(uint32 i = 0; i < 2; ++i)
{
for(set::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
{
data << (*itr)->GetGUID();
bs = &(*itr)->m_bgScore;
data << bs->KillingBlows;
data << bs->HonorableKills;
data << bs->Deaths;
data << bs->BonusHonor;
data << bs->DamageDone;
data << bs->HealingDone;
data << uint32(0x2);
data << bs->Misc1;
data << bs->Misc2;
(*itr)->Root();
}
}
}
else
{
data << uint8(0); // If the game has ended - this will be 1
data << uint32(m_players[0].size() + m_players[1].size());
for(uint32 i = 0; i < 2; ++i)
{
for(set::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
{
data << (*itr)->GetGUID();
bs = &(*itr)->m_bgScore;
data << bs->KillingBlows;
data << bs->HonorableKills;
data << bs->Deaths;
data << bs->BonusHonor;
data << bs->DamageDone;
data << bs->HealingDone;
data << uint32(0x2);
data << bs->Misc1;
data << bs->Misc2;
}
}
}
DistributePacketToAll(&data);
m_mainLock.Release();
m_nextPvPUpdateTime = World::UNIXTIME + 2;
}
}
void CBattleground::AddPlayer(Player * plr)
{
m_mainLock.Acquire();
/* This is called when the player is added, not when they port. So, they're essentially still queued, but not inside the bg yet */
m_pendPlayers[plr->GetTeam()].insert(plr->GetGUIDLow());
/* Send a packet telling them that they can enter */
BattlegroundManager.SendBattlefieldStatus(plr, 2, m_type, m_id, 120000, m_mapMgr->GetMapId()); // You will be removed from the queue in 2 minutes.
/* Add an event to remove them in 2 minutes time. */
sEventMgr.AddEvent(plr, &Player::RemoveFromBattlegroundQueue, EVENT_BATTLEGROUND_QUEUE_UPDATE, 120000, 1,0);
plr->m_pendingBattleground = this;
m_mainLock.Release();
}
void CBattleground::RemovePendingPlayer(Player * plr)
{
m_mainLock.Acquire();
m_pendPlayers[plr->GetTeam()].erase(plr->GetGUIDLow());
/* send a null bg update (so they don't join) */
BattlegroundManager.SendBattlefieldStatus(plr, 0, 0, 0, 0, 0);
plr->m_pendingBattleground =0;
m_mainLock.Release();
}
void CBattleground::PortPlayer(Player * plr, bool skip_teleport /* = false*/)
{
m_mainLock.Acquire();
if(m_ended)
{
sChatHandler.SystemMessage(plr->GetSession(), "You cannot join this battleground as it has already ended.");
BattlegroundManager.SendBattlefieldStatus(plr, 0, 0, 0, 0, 0);
plr->m_pendingBattleground = 0;
m_mainLock.Release();
return;
}
/* remove from any auto queue remove events */
sEventMgr.RemoveEvents(plr, EVENT_BATTLEGROUND_QUEUE_UPDATE);
WorldPacket data(SMSG_BATTLEGROUND_PLAYER_JOINED, 8);
data << plr->GetGUID();
DistributePacketToAll(&data);
if(!skip_teleport)
{
/* This is where we actually teleport the player to the battleground. */
plr->m_bgEntryPointX = plr->GetPositionX();
plr->m_bgEntryPointY = plr->GetPositionY();
plr->m_bgEntryPointZ = plr->GetPositionZ();
plr->m_bgEntryPointMap = plr->GetMapId();
plr->m_bgEntryPointInstance = plr->GetInstanceID();
plr->SafeTeleport(m_mapMgr->GetMapId(), m_mapMgr->GetInstanceID(), GetStartingCoords(plr->GetTeam()));
BattlegroundManager.SendBattlefieldStatus(plr, 3, m_type, m_id, World::UNIXTIME - m_startTime, m_mapMgr->GetMapId()); // Elapsed time is the last argument
}
m_pendPlayers[plr->GetTeam()].erase(plr->GetGUIDLow());
m_players[plr->GetTeam()].insert(plr);
plr->m_pendingBattleground = 0;
plr->m_bg = this;
if(!plr->IsPvPFlagged())
plr->SetPvPFlag();
/* Reset the score */
memset(&plr->m_bgScore, 0, sizeof(BGScore));
/* send him the world states */
SendWorldStates(plr);
/* update pvp data */
UpdatePvPData();
/* add the player to the group */
if(!plr->GetGroup())
m_groups[plr->GetTeam()]->AddMember(plr->m_playerInfo, plr);
if(!m_countdownStage)
{
m_countdownStage = 1;
sEventMgr.AddEvent(this, &CBattleground::EventCountdown, EVENT_BATTLEGROUND_COUNTDOWN, 30000, 0,0);
sEventMgr.ModifyEventTimeLeft(this, EVENT_BATTLEGROUND_COUNTDOWN, 10000);
}
sEventMgr.RemoveEvents(this, EVENT_BATTLEGROUND_CLOSE);
OnAddPlayer(plr);
m_mainLock.Release();
}
CBattleground * CBattlegroundManager::CreateInstance(uint32 Type, uint32 LevelGroup)
{
CreateBattlegroundFunc cfunc = BGCFuncs[Type];
MapMgr * mgr = 0;
CBattleground * bg;
uint32 iid;
if(Type == BATTLEGROUND_ARENA_2V2 || Type == BATTLEGROUND_ARENA_3V3 || Type == BATTLEGROUND_ARENA_5V5)
{
/* arenas follow a different procedure. */
static const uint32 arena_map_ids[3] = { 559, 562, 572 };
uint32 mapid = /*arena_map_ids[sRand.randInt(3)]*/562;
uint32 players_per_side;
if(sWorldCreator.CreateInstance(mapid, 0, &mgr) == false || !mgr)
{
Log.Error("BattlegroundManager", "Arena CreateInstance() call failed for map %u, type %u, level group %u", mapid, Type, LevelGroup);
return NULL; // Shouldn't happen
}
switch(Type)
{
case BATTLEGROUND_ARENA_2V2:
players_per_side = 2;
break;
case BATTLEGROUND_ARENA_3V3:
players_per_side = 3;
break;
case BATTLEGROUND_ARENA_5V5:
players_per_side = 5;
break;
default:
players_per_side = 0;
break;
}
iid = ++m_maxBattlegroundId;
bg = new Arena(mgr, iid, LevelGroup, Type, players_per_side);
mgr->m_battleground = bg;
Log.Success("BattlegroundManager", "Created arena battleground type %u for level group %u on map %u.", Type, LevelGroup, mapid);
sEventMgr.AddEvent(bg, &CBattleground::EventCreate, EVENT_BATTLEGROUND_QUEUE_UPDATE, 1, 1,0);
m_instanceLock.AcquireWriteLock();
m_instances[Type].insert( make_pair(iid, bg) );
m_instanceLock.ReleaseWriteLock();
return bg;
}
if(cfunc == NULL)
{
Log.Error("BattlegroundManager", "Could not find CreateBattlegroundFunc pointer for type %u level group %u", Type, LevelGroup);
return NULL;
}
/* Create Map Manager */
if(sWorldCreator.CreateInstance(BGMapIds[Type], 0, &mgr) == false || !mgr)
{
Log.Error("BattlegroundManager", "CreateInstance() call failed for map %u, type %u, level group %u", BGMapIds[Type], Type, LevelGroup);
return NULL; // Shouldn't happen
}
/* Call the create function */
iid = ++m_maxBattlegroundId;
bg = cfunc(mgr, iid, LevelGroup, Type);
mgr->m_battleground = bg;
sEventMgr.AddEvent(bg, &CBattleground::EventCreate, EVENT_BATTLEGROUND_QUEUE_UPDATE, 1, 1,0);
Log.Success("BattlegroundManager", "Created battleground type %u for level group %u.", Type, LevelGroup);
m_instanceLock.AcquireWriteLock();
m_instances[Type].insert( make_pair(iid, bg) );
m_instanceLock.ReleaseWriteLock();
return bg;
}
void CBattlegroundManager::DeleteBattleground(CBattleground * bg)
{
uint32 i = bg->GetType();
uint32 j = bg->GetLevelGroup();
Player * plr;
m_instanceLock.AcquireWriteLock();
m_queueLock.Acquire();
m_instances[i].erase(bg->GetId());
/* erase any queued players */
map >::iterator itr = m_queuedPlayers[i][j].find(bg->GetId());
list::iterator it2;
if(itr != m_queuedPlayers[i][j].end())
{
/* kill him! */
for(it2 = itr->second.begin(); it2 != itr->second.end(); ++it2)
{
plr = objmgr.GetPlayer(*it2);
if(plr)
{
sChatHandler.SystemMessageToPlr(plr, "Your queue on battleground instance %u is no longer valid, the instance no longer exists.", bg->GetId());
SendBattlefieldStatus(plr, 0, 0, 0, 0, 0);
plr->m_bgIsQueued = false;
}
}
m_queuedPlayers[i][j].erase(itr);
}
m_queueLock.Release();
m_instanceLock.ReleaseWriteLock();
}
GameObject * CBattleground::SpawnGameObject(uint32 entry,uint32 MapId , float x, float y, float z, float o, uint32 flags, uint32 faction, float scale)
{
GameObject *go = m_mapMgr->CreateGameObject();
go->CreateFromProto(entry, MapId, x, y, z, o);
go->SetUInt32Value(GAMEOBJECT_FACTION,faction);
go->SetFloatValue(OBJECT_FIELD_SCALE_X,scale);
go->SetUInt32Value(GAMEOBJECT_FLAGS, flags);
go->SetFloatValue(GAMEOBJECT_POS_X, x);
go->SetFloatValue(GAMEOBJECT_POS_Y, y);
go->SetFloatValue(GAMEOBJECT_POS_Z, z);
go->SetFloatValue(GAMEOBJECT_FACING, o);
go->SetInstanceID(m_mapMgr->GetInstanceID());
return go;
}
void CBattleground::SendChatMessage(uint32 Type, uint64 Guid, const char * Format, ...)
{
char msg[500];
va_list ap;
va_start(ap, Format);
vsnprintf(msg, 500, Format, ap);
va_end(ap);
WorldPacket * data = sChatHandler.FillMessageData(Type, 0, msg, Guid, 0);
DistributePacketToAll(data);
delete data;
}
void CBattleground::DistributePacketToAll(WorldPacket * packet)
{
m_mainLock.Acquire();
for(int i = 0; i < 2; ++i)
{
for(set::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
(*itr)->GetSession()->SendPacket(packet);
}
m_mainLock.Release();
}
void CBattleground::DistributePacketToTeam(WorldPacket * packet, uint32 Team)
{
m_mainLock.Acquire();
for(set::iterator itr = m_players[Team].begin(); itr != m_players[Team].end(); ++itr)
(*itr)->GetSession()->SendPacket(packet);
m_mainLock.Release();
}
void CBattleground::PlaySoundToAll(uint32 Sound)
{
WorldPacket data(SMSG_PLAY_SOUND, 4);
data << Sound;
DistributePacketToAll(&data);
}
void CBattleground::PlaySoundToTeam(uint32 Team, uint32 Sound)
{
WorldPacket data(SMSG_PLAY_SOUND, 4);
data << Sound;
DistributePacketToTeam(&data, Team);
}
void CBattlegroundManager::SendBattlefieldStatus(Player * plr, uint32 Status, uint32 Type, uint32 InstanceID, uint32 Time, uint32 MapId)
{
WorldPacket data(SMSG_BATTLEFIELD_STATUS, 30);
if(Status == 0)
data << uint64(0) << uint32(0);
else
{
if(Type >= BATTLEGROUND_ARENA_2V2 && Type <= BATTLEGROUND_ARENA_5V5)
{
data << uint32(1);
switch(Type)
{
case BATTLEGROUND_ARENA_2V2:
data << uint8(2);
break;
case BATTLEGROUND_ARENA_3V3:
data << uint8(3);
break;
case BATTLEGROUND_ARENA_5V5:
data << uint8(5);
break;
}
data << uint8(0xC);
data << uint32(6);
data << uint16(0x1F90);
data << uint32(11);
data << uint8(0); // 1 = rated match
}
else
{
data << uint32(0);
data << uint8(0) << uint8(2);
data << Type;
data << uint16(0x1F90);
data << InstanceID;
data << uint8(plr->GetTeam());
}
data << Status;
switch(Status)
{
case 1: // Waiting in queue
data << uint32(60) << uint32(0); // Time / Elapsed time
break;
case 2: // Ready to join!
data << MapId << Time;
break;
case 3:
/*if(Type >= BATTLEGROUND_ARENA_2V2 && Type <= BATTLEGROUND_ARENA_5V5)
data << MapId << uint32(0x0001D4C0) << uint32(0x0002FC8A) << uint8(0);
else*/
data << MapId << uint32(0) << Time << uint8(1);
break;
}
}
plr->GetSession()->SendPacket(&data);
}
void CBattleground::RemovePlayer(Player * plr, bool logout)
{
WorldPacket data(SMSG_BATTLEGROUND_PLAYER_LEFT, 30);
data << plr->GetGUID();
m_mainLock.Acquire();
m_players[plr->GetTeam()].erase(plr);
DistributePacketToAll(&data);
memset(&plr->m_bgScore, 0, sizeof(BGScore));
OnRemovePlayer(plr);
plr->m_bg = 0;
/* are we in the group? */
if(plr->GetGroup() == m_groups[plr->GetTeam()])
plr->GetGroup()->RemovePlayer(plr->m_playerInfo, plr, true);
/* revive the player if he is dead */
if(!plr->isAlive())
plr->ResurrectPlayer();
/* teleport out */
if(!logout)
{
LocationVector vec(plr->m_bgEntryPointX, plr->m_bgEntryPointY, plr->m_bgEntryPointZ, plr->m_bgEntryPointO);
plr->SafeTeleport(plr->m_bgEntryPointMap, plr->m_bgEntryPointInstance, vec);
BattlegroundManager.SendBattlefieldStatus(plr, 0, 0, 0, 0, 0);
/* send some null world states */
data.Initialize(SMSG_INIT_WORLD_STATES);
data << uint32(plr->m_bgEntryPointMap) << uint32(0) << uint32(0);
plr->GetSession()->SendPacket(&data);
}
if(!m_ended && m_players[0].size() == 0 && m_players[1].size() == 0)
{
/* create an inactive event */
sEventMgr.RemoveEvents(this, EVENT_BATTLEGROUND_CLOSE); // 10mins
sEventMgr.AddEvent(this, &CBattleground::Close, EVENT_BATTLEGROUND_CLOSE, 600000, 1,0);
}
m_mainLock.Release();
}
void CBattleground::SendPVPData(Player * plr)
{
m_mainLock.Acquire();
WorldPacket data(MSG_PVP_LOG_DATA, 50);
BGScore * bs;
/*if(m_type >= BATTLEGROUND_ARENA_2V2 && m_type <= BATTLEGROUND_ARENA_5V5 && !m_ended)
{
data << uint8(1);
data << uint32(0x6C0EF0F4);
data << uint32(0x00000400);
data << uint32(0x01010000);
data << uint32(0x00000009);
data << uint32(0x001A070E);
data << uint32(0x00000000);
}
else*/
data << uint8(0);
if(m_ended)
{
data << uint8(1);
data << uint8(m_winningteam);
}
else
{
data << uint8(0); // If the game has ended - this will be 1
data << uint32(m_players[0].size() + m_players[1].size());
for(uint32 i = 0; i < 2; ++i)
{
for(set::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
{
data << (*itr)->GetGUID();
bs = &(*itr)->m_bgScore;
data << bs->KillingBlows;
data << bs->HonorableKills;
data << bs->Deaths;
data << bs->BonusHonor;
data << bs->DamageDone;
data << bs->HealingDone;
data << uint32(0x2);
data << bs->Misc1;
data << bs->Misc2;
}
}
}
plr->GetSession()->SendPacket(&data);
m_mainLock.Release();
}
void CBattleground::EventCreate()
{
OnCreate();
}
int32 CBattleground::event_GetInstanceID()
{
return m_mapMgr->GetInstanceID();
}
void CBattleground::EventCountdown()
{
if(m_countdownStage == 1)
{
m_countdownStage = 2;
SendChatMessage(CHAT_MSG_BATTLEGROUND_EVENT, 0, "One minute until the battle for %s begins!", GetName());
}
else if(m_countdownStage == 2)
{
m_countdownStage = 3;
SendChatMessage(CHAT_MSG_BATTLEGROUND_EVENT, 0, "Thirty seconds until the battle for %s begins!", GetName());
}
else if(m_countdownStage == 3)
{
m_countdownStage = 4;
SendChatMessage(CHAT_MSG_BATTLEGROUND_EVENT, 0, "Fifteen seconds until the battle for %s begins!", GetName());
sEventMgr.ModifyEventTime(this, EVENT_BATTLEGROUND_COUNTDOWN, 15000);
sEventMgr.ModifyEventTimeLeft(this, EVENT_BATTLEGROUND_COUNTDOWN, 15000);
}
else
{
SendChatMessage(CHAT_MSG_BATTLEGROUND_EVENT, 0, "The battle for %s has begun!", GetName());
sEventMgr.RemoveEvents(this, EVENT_BATTLEGROUND_COUNTDOWN);
Start();
}
}
void CBattleground::Start()
{
OnStart();
}
void CBattleground::SetWorldState(uint32 Index, uint32 Value)
{
map::iterator itr = m_worldStates.find(Index);
if(itr == m_worldStates.end())
m_worldStates.insert( make_pair( Index, Value ) );
else
itr->second = Value;
WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8);
data << itr->first << itr->second;
DistributePacketToAll(&data);
}
void CBattleground::Close()
{
/* remove all players from the battleground */
m_mainLock.Acquire();
m_ended = true;
for(uint32 i = 0; i < 2; ++i)
{
set::iterator itr;
set::iterator it2;
uint32 guid;
Player * plr;
for(itr = m_players[i].begin(); itr != m_players[i].end();)
{
plr = *itr;
++itr;
RemovePlayer(plr, false);
}
for(it2 = m_pendPlayers[i].begin(); it2 != m_pendPlayers[i].end();)
{
guid = *it2;
++it2;
plr = objmgr.GetPlayer(guid);
if(plr)
RemovePendingPlayer(plr);
else
m_pendPlayers[i].erase(guid);
}
}
/* call the virtual onclose for cleanup etc */
OnClose();
/* shut down the map thread. this will delete the battleground from the corrent context. */
m_mapMgr->SetThreadState(THREADSTATE_TERMINATE);
m_mapMgr->delete_pending = true;
m_mainLock.Release();
}
void CBattleground::SpawnSpiritGuide(float x, float y, float z, float o, uint32 horde)
{
if(horde > 1)
horde = 1;
CreatureInfo * pInfo = CreatureNameStorage.LookupEntry(13116 + horde);
if(pInfo == 0)
{
return;
}
Creature * pCreature = m_mapMgr->CreateCreature();
pCreature->Create(pInfo->Name, m_mapMgr->GetMapId(), x, y, z, o);
pCreature->SetInstanceID(m_mapMgr->GetInstanceID());
pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, 13116 + horde);
pCreature->SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f);
pCreature->SetUInt32Value(UNIT_FIELD_HEALTH, 100000);
pCreature->SetUInt32Value(UNIT_FIELD_POWER1, 4868);
pCreature->SetUInt32Value(UNIT_FIELD_POWER3, 200);
pCreature->SetUInt32Value(UNIT_FIELD_POWER5, 2000000);
pCreature->SetUInt32Value(UNIT_FIELD_MAXHEALTH, 10000);
pCreature->SetUInt32Value(UNIT_FIELD_MAXPOWER1, 4868);
pCreature->SetUInt32Value(UNIT_FIELD_MAXPOWER3, 200);
pCreature->SetUInt32Value(UNIT_FIELD_MAXPOWER5, 2000000);
pCreature->SetUInt32Value(UNIT_FIELD_LEVEL, 60);
pCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, 84 - horde);
pCreature->SetUInt32Value(UNIT_FIELD_BYTES_0, 0 | (2 << 8) | (1 << 16));
pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 22802);
pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, 2 | (0xA << 8) | (2 << 16) | (0x11 << 24));
pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_01, 2);
pCreature->SetUInt32Value(UNIT_FIELD_FLAGS, 4928);
pCreature->SetUInt32Value(UNIT_FIELD_AURA, 22011);
pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 9);
pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x3C);
pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0xFF);
pCreature->SetUInt32Value(UNIT_FIELD_BASEATTACKTIME, 2000);
pCreature->SetUInt32Value(UNIT_FIELD_BASEATTACKTIME_01, 2000);
pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 0.208f);
pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);
pCreature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 13337 + horde);
pCreature->SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, 13337 + horde);
pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, 22011);
pCreature->SetUInt32Value(UNIT_MOD_CAST_SPEED, 1065353216);
pCreature->SetUInt32Value(UNIT_NPC_FLAGS, 64);
pCreature->SetUInt32Value(UNIT_FIELD_BYTES_2, 1 | (0x10 << 8));
pCreature->DisableAI();
pCreature->PushToWorld(m_mapMgr);
}
void CBattleground::QueuePlayerForResurrect(Player * plr)
{
m_resurrectQueue[plr->GetTeam()].insert(plr->GetGUIDLow());
}
#define RESURRECT_SPELL 21074 // Spirit Healer Res
void CBattleground::EventResurrectPlayers()
{
Player * plr;
set::iterator itr;
WorldPacket data(50);
for(uint32 i = 0; i < 2; ++i)
{
for(itr = m_resurrectQueue[i].begin(); itr != m_resurrectQueue[i].end(); ++itr)
{
plr = m_mapMgr->GetPlayer(*itr);
if(plr && plr->isDead())
{
data.Initialize(SMSG_SPELL_START);
data << plr->GetNewGUID() << plr->GetNewGUID() << uint32(RESURRECT_SPELL) << uint16(0) << uint32(0) << uint16(2) << plr->GetGUID();
plr->SendMessageToSet(&data, true);
data.Initialize(SMSG_SPELL_GO);
data << plr->GetNewGUID() << plr->GetNewGUID() << uint32(RESURRECT_SPELL) << uint8(0) << uint8(1) << uint8(1) << plr->GetGUID() << uint8(0) << uint16(2)
<< plr->GetGUID();
plr->SendMessageToSet(&data, true);
plr->ResurrectPlayer();
plr->SetUInt32Value(UNIT_FIELD_HEALTH, plr->GetUInt32Value(UNIT_FIELD_HEALTH));
plr->SetUInt32Value(UNIT_FIELD_POWER1, plr->GetUInt32Value(UNIT_FIELD_MAXPOWER1));
plr->SetUInt32Value(UNIT_FIELD_POWER4, plr->GetUInt32Value(UNIT_FIELD_MAXPOWER4));
}
}
m_resurrectQueue[i].clear();
}
m_lastResurrect = World::UNIXTIME;
}
void CBattlegroundManager::HandleArenaJoin(WorldSession * m_session, uint32 BattlegroundType)
{
uint32 pguid = m_session->GetPlayer()->GetGUIDLow();
uint32 lgroup = GetLevelGrouping(m_session->GetPlayer()->getLevel());
/* Queue him! */
m_queueLock.Acquire();
map >::iterator itr = m_queuedPlayers[BattlegroundType][lgroup].find(0);
if(itr != m_queuedPlayers[BattlegroundType][lgroup].end())
itr->second.push_back(pguid);
else
{
Log.Debug("BattlegroundManger", "Created queue for battleground type %u in levelgroup %u", BattlegroundType, lgroup);
list tmp;
tmp.push_back(pguid);
m_queuedPlayers[BattlegroundType][lgroup].insert( make_pair( (uint32)0, tmp ) );
}
Log.Success("BattlegroundMgr", "Player %u is now in battleground queue for {Arena %u}", m_session->GetPlayer()->GetGUIDLow(), BattlegroundType );
/* send the battleground status packet */
SendBattlefieldStatus(m_session->GetPlayer(), 1, BattlegroundType, 0 , 0, 0);
m_session->GetPlayer()->m_bgIsQueued = true;
m_session->GetPlayer()->m_bgQueueInstanceId = 0;
m_session->GetPlayer()->m_bgQueueType = BattlegroundType;
m_queueLock.Release();
}