/*
* Copyright (C) 2008-2019 TrinityCore
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "Battlefield.h"
#include "BattlefieldMgr.h"
#include "Battleground.h"
#include "BattlegroundPackets.h"
#include "CellImpl.h"
#include "CreatureTextMgr.h"
#include "DB2Stores.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "GroupMgr.h"
#include "Log.h"
#include "Map.h"
#include "MapManager.h"
#include "MiscPackets.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "WorldSession.h"
#include "WorldStatePackets.h"
#include
Battlefield::Battlefield()
{
m_Timer = 0;
m_IsEnabled = true;
m_isActive = false;
m_DefenderTeam = TEAM_NEUTRAL;
m_TypeId = 0;
m_BattleId = 0;
m_ZoneId = 0;
m_Map = nullptr;
m_MapId = 0;
m_MaxPlayer = 0;
m_MinPlayer = 0;
m_MinLevel = 0;
m_BattleTime = 0;
m_NoWarBattleTime = 0;
m_RestartAfterCrash = 0;
m_TimeForAcceptInvite = 20;
m_uiKickDontAcceptTimer = 1000;
m_uiKickAfkPlayersTimer = 1000;
m_LastResurrectTimer = 30 * IN_MILLISECONDS;
m_StartGroupingTimer = 0;
m_StartGrouping = false;
}
Battlefield::~Battlefield()
{
for (BfCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
delete itr->second;
for (GraveyardVect::const_iterator itr = m_GraveyardList.begin(); itr != m_GraveyardList.end(); ++itr)
delete *itr;
}
// Called when a player enters the zone
void Battlefield::HandlePlayerEnterZone(Player* player, uint32 /*zone*/)
{
// If battle is started,
// If not full of players > invite player to join the war
// If full of players > announce to player that BF is full and kick him after a few second if he desn't leave
if (IsWarTime())
{
if (m_PlayersInWar[player->GetTeamId()].size() + m_InvitedPlayers[player->GetTeamId()].size() < m_MaxPlayer) // Vacant spaces
InvitePlayerToWar(player);
else // No more vacant places
{
/// @todo Send a packet to announce it to player
m_PlayersWillBeKick[player->GetTeamId()][player->GetGUID()] = time(nullptr) + 10;
InvitePlayerToQueue(player);
}
}
else
{
// If time left is < 15 minutes invite player to join queue
if (m_Timer <= m_StartGroupingTimer)
InvitePlayerToQueue(player);
}
// Add player in the list of player in zone
m_players[player->GetTeamId()].insert(player->GetGUID());
OnPlayerEnterZone(player);
}
// Called when a player leave the zone
void Battlefield::HandlePlayerLeaveZone(Player* player, uint32 /*zone*/)
{
if (IsWarTime())
{
// If the player is participating to the battle
if (m_PlayersInWar[player->GetTeamId()].find(player->GetGUID()) != m_PlayersInWar[player->GetTeamId()].end())
{
m_PlayersInWar[player->GetTeamId()].erase(player->GetGUID());
player->GetSession()->SendBfLeaveMessage(GetQueueId(), GetState(), player->GetZoneId() == GetZoneId());
if (Group* group = player->GetGroup()) // Remove the player from the raid group
group->RemoveMember(player->GetGUID());
OnPlayerLeaveWar(player);
}
}
for (BfCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
itr->second->HandlePlayerLeave(player);
m_InvitedPlayers[player->GetTeamId()].erase(player->GetGUID());
m_PlayersWillBeKick[player->GetTeamId()].erase(player->GetGUID());
m_players[player->GetTeamId()].erase(player->GetGUID());
SendRemoveWorldStates(player);
RemovePlayerFromResurrectQueue(player->GetGUID());
OnPlayerLeaveZone(player);
}
bool Battlefield::Update(uint32 diff)
{
if (m_Timer <= diff)
{
// Battlefield ends on time
if (IsWarTime())
EndBattle(true);
else // Time to start a new battle!
StartBattle();
}
else
m_Timer -= diff;
// Invite players a few minutes before the battle's beginning
if (!IsWarTime() && !m_StartGrouping && m_Timer <= m_StartGroupingTimer)
{
m_StartGrouping = true;
InvitePlayersInZoneToQueue();
OnStartGrouping();
}
bool objective_changed = false;
if (IsWarTime())
{
if (m_uiKickAfkPlayersTimer <= diff)
{
m_uiKickAfkPlayersTimer = 1000;
KickAfkPlayers();
}
else
m_uiKickAfkPlayersTimer -= diff;
// Kick players who chose not to accept invitation to the battle
if (m_uiKickDontAcceptTimer <= diff)
{
time_t now = time(nullptr);
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (PlayerTimerMap::iterator itr = m_InvitedPlayers[team].begin(); itr != m_InvitedPlayers[team].end(); ++itr)
if (itr->second <= now)
KickPlayerFromBattlefield(itr->first);
InvitePlayersInZoneToWar();
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (PlayerTimerMap::iterator itr = m_PlayersWillBeKick[team].begin(); itr != m_PlayersWillBeKick[team].end(); ++itr)
if (itr->second <= now)
KickPlayerFromBattlefield(itr->first);
m_uiKickDontAcceptTimer = 1000;
}
else
m_uiKickDontAcceptTimer -= diff;
for (BfCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if (itr->second->Update(diff))
objective_changed = true;
}
if (m_LastResurrectTimer <= diff)
{
for (uint8 i = 0; i < m_GraveyardList.size(); i++)
if (GetGraveyardById(i))
m_GraveyardList[i]->Resurrect();
m_LastResurrectTimer = RESURRECTION_INTERVAL;
}
else
m_LastResurrectTimer -= diff;
return objective_changed;
}
void Battlefield::InvitePlayersInZoneToQueue()
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (auto itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(*itr))
InvitePlayerToQueue(player);
}
void Battlefield::InvitePlayerToQueue(Player* player)
{
if (m_PlayersInQueue[player->GetTeamId()].count(player->GetGUID()))
return;
if (m_PlayersInQueue[player->GetTeamId()].size() <= m_MinPlayer || m_PlayersInQueue[GetOtherTeam(player->GetTeamId())].size() >= m_MinPlayer)
player->GetSession()->SendBfInvitePlayerToQueue(GetQueueId(), GetState());
}
void Battlefield::InvitePlayersInQueueToWar()
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
{
for (auto itr = m_PlayersInQueue[team].begin(); itr != m_PlayersInQueue[team].end(); ++itr)
{
if (Player* player = ObjectAccessor::FindPlayer(*itr))
{
if (m_PlayersInWar[player->GetTeamId()].size() + m_InvitedPlayers[player->GetTeamId()].size() < m_MaxPlayer)
InvitePlayerToWar(player);
else
{
//Full
}
}
}
m_PlayersInQueue[team].clear();
}
}
void Battlefield::InvitePlayersInZoneToWar()
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
{
for (auto itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
{
if (Player* player = ObjectAccessor::FindPlayer(*itr))
{
if (m_PlayersInWar[player->GetTeamId()].count(player->GetGUID()) || m_InvitedPlayers[player->GetTeamId()].count(player->GetGUID()))
continue;
if (m_PlayersInWar[player->GetTeamId()].size() + m_InvitedPlayers[player->GetTeamId()].size() < m_MaxPlayer)
InvitePlayerToWar(player);
else // Battlefield is full of players
m_PlayersWillBeKick[player->GetTeamId()][player->GetGUID()] = time(nullptr) + 10;
}
}
}
}
uint64 Battlefield::GetQueueId() const
{
return MAKE_PAIR64(m_BattleId | 0x20000, 0x1F100000);
}
void Battlefield::InvitePlayerToWar(Player* player)
{
if (!player)
return;
/// @todo needed ?
if (player->IsInFlight())
return;
if (player->InArena() || player->GetBattleground())
{
m_PlayersInQueue[player->GetTeamId()].erase(player->GetGUID());
return;
}
// If the player does not match minimal level requirements for the battlefield, kick him
if (player->getLevel() < m_MinLevel)
{
if (m_PlayersWillBeKick[player->GetTeamId()].count(player->GetGUID()) == 0)
m_PlayersWillBeKick[player->GetTeamId()][player->GetGUID()] = time(nullptr) + 10;
return;
}
// Check if player is not already in war
if (m_PlayersInWar[player->GetTeamId()].count(player->GetGUID()) || m_InvitedPlayers[player->GetTeamId()].count(player->GetGUID()))
return;
m_PlayersWillBeKick[player->GetTeamId()].erase(player->GetGUID());
m_InvitedPlayers[player->GetTeamId()][player->GetGUID()] = time(nullptr) + m_TimeForAcceptInvite;
player->GetSession()->SendBfInvitePlayerToWar(GetQueueId(), m_ZoneId, m_TimeForAcceptInvite);
}
void Battlefield::InitStalker(uint32 entry, Position const& pos)
{
if (Creature* creature = SpawnCreature(entry, pos))
StalkerGuid = creature->GetGUID();
else
TC_LOG_ERROR("bg.battlefield", "Battlefield::InitStalker: Could not spawn Stalker (Creature entry %u), zone messages will be unavailable!", entry);
}
void Battlefield::KickAfkPlayers()
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (auto itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(*itr))
if (player->isAFK())
KickPlayerFromBattlefield(*itr);
}
void Battlefield::KickPlayerFromBattlefield(ObjectGuid guid)
{
if (Player* player = ObjectAccessor::FindPlayer(guid))
if (player->GetZoneId() == GetZoneId())
player->TeleportTo(KickPosition);
}
void Battlefield::StartBattle()
{
if (m_isActive)
return;
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
{
m_PlayersInWar[team].clear();
m_Groups[team].clear();
}
m_Timer = m_BattleTime;
m_isActive = true;
InvitePlayersInZoneToWar();
InvitePlayersInQueueToWar();
OnBattleStart();
}
void Battlefield::EndBattle(bool endByTimer)
{
if (!m_isActive)
return;
m_isActive = false;
m_StartGrouping = false;
if (!endByTimer)
SetDefenderTeam(GetAttackerTeam());
OnBattleEnd(endByTimer);
// Reset battlefield timer
m_Timer = m_NoWarBattleTime;
SendInitWorldStatesToAll();
}
void Battlefield::DoPlaySoundToAll(uint32 soundID)
{
BroadcastPacketToWar(WorldPackets::Misc::PlaySound(ObjectGuid::Empty, soundID).Write());
}
bool Battlefield::HasPlayer(Player* player) const
{
return m_players[player->GetTeamId()].find(player->GetGUID()) != m_players[player->GetTeamId()].end();
}
// Called in WorldSession::HandleBfQueueInviteResponse
void Battlefield::PlayerAcceptInviteToQueue(Player* player)
{
// Add player in queue
m_PlayersInQueue[player->GetTeamId()].insert(player->GetGUID());
// Send notification
player->GetSession()->SendBfQueueInviteResponse(GetQueueId(), m_ZoneId, GetState());
}
// Called in WorldSession::HandleBfExitRequest
void Battlefield::AskToLeaveQueue(Player* player)
{
// Remove player from queue
m_PlayersInQueue[player->GetTeamId()].erase(player->GetGUID());
}
// Called in WorldSession::HandleHearthAndResurrect
void Battlefield::PlayerAskToLeave(Player* player)
{
// Player leaving Wintergrasp, teleport to Dalaran.
// ToDo: confirm teleport destination.
player->TeleportTo(571, 5804.1499f, 624.7710f, 647.7670f, 1.6400f);
}
// Called in WorldSession::HandleBfEntryInviteResponse
void Battlefield::PlayerAcceptInviteToWar(Player* player)
{
if (!IsWarTime())
return;
if (AddOrSetPlayerToCorrectBfGroup(player))
{
player->GetSession()->SendBfEntered(GetQueueId(), player->GetZoneId() != GetZoneId(), player->GetTeamId() == GetAttackerTeam());
m_PlayersInWar[player->GetTeamId()].insert(player->GetGUID());
m_InvitedPlayers[player->GetTeamId()].erase(player->GetGUID());
if (player->isAFK())
player->ToggleAFK();
OnPlayerJoinWar(player); //for scripting
}
}
void Battlefield::TeamCastSpell(TeamId team, int32 spellId)
{
if (spellId > 0)
{
for (auto itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(*itr))
player->CastSpell(player, uint32(spellId), true);
}
else
{
for (auto itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(*itr))
player->RemoveAuraFromStack(uint32(-spellId));
}
}
void Battlefield::BroadcastPacketToZone(WorldPacket const* data) const
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (auto itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(*itr))
player->SendDirectMessage(data);
}
void Battlefield::BroadcastPacketToQueue(WorldPacket const* data) const
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (auto itr = m_PlayersInQueue[team].begin(); itr != m_PlayersInQueue[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindConnectedPlayer(*itr))
player->SendDirectMessage(data);
}
void Battlefield::BroadcastPacketToWar(WorldPacket const* data) const
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (auto itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(*itr))
player->SendDirectMessage(data);
}
void Battlefield::SendWarning(uint8 id, WorldObject const* target /*= nullptr*/)
{
if (Creature* stalker = GetCreature(StalkerGuid))
sCreatureTextMgr->SendChat(stalker, id, target);
}
void Battlefield::SendUpdateWorldState(uint32 variable, uint32 value, bool hidden /*= false*/)
{
WorldPackets::WorldState::UpdateWorldState worldstate;
worldstate.VariableID = variable;
worldstate.Value = value;
worldstate.Hidden = hidden;
BroadcastPacketToZone(worldstate.Write());
}
void Battlefield::AddCapturePoint(BfCapturePoint* cp)
{
Battlefield::BfCapturePointMap::iterator i = m_capturePoints.find(cp->GetCapturePointEntry());
if (i != m_capturePoints.end())
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::AddCapturePoint: CapturePoint %u already exists!", cp->GetCapturePointEntry());
delete i->second;
}
m_capturePoints[cp->GetCapturePointEntry()] = cp;
}
BfCapturePoint* Battlefield::GetCapturePoint(uint32 entry) const
{
Battlefield::BfCapturePointMap::const_iterator itr = m_capturePoints.find(entry);
if (itr != m_capturePoints.end())
return itr->second;
return nullptr;
}
void Battlefield::RegisterZone(uint32 zoneId)
{
sBattlefieldMgr->AddZone(zoneId, this);
}
void Battlefield::HideNpc(Creature* creature)
{
creature->CombatStop();
creature->SetReactState(REACT_PASSIVE);
creature->AddUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE));
creature->DisappearAndDie();
creature->SetVisible(false);
}
void Battlefield::ShowNpc(Creature* creature, bool aggressive)
{
creature->SetVisible(true);
creature->RemoveUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE));
if (!creature->IsAlive())
creature->Respawn(true);
if (aggressive)
creature->SetReactState(REACT_AGGRESSIVE);
else
{
creature->AddUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
creature->SetReactState(REACT_PASSIVE);
}
}
// ****************************************************
// ******************* Group System *******************
// ****************************************************
Group* Battlefield::GetFreeBfRaid(TeamId TeamId)
{
for (auto itr = m_Groups[TeamId].begin(); itr != m_Groups[TeamId].end(); ++itr)
if (Group* group = sGroupMgr->GetGroupByGUID(*itr))
if (!group->IsFull())
return group;
return nullptr;
}
Group* Battlefield::GetGroupPlayer(ObjectGuid guid, TeamId TeamId)
{
for (auto itr = m_Groups[TeamId].begin(); itr != m_Groups[TeamId].end(); ++itr)
if (Group* group = sGroupMgr->GetGroupByGUID(*itr))
if (group->IsMember(guid))
return group;
return nullptr;
}
bool Battlefield::AddOrSetPlayerToCorrectBfGroup(Player* player)
{
if (!player->IsInWorld())
return false;
if (Group* group = player->GetGroup())
group->RemoveMember(player->GetGUID());
Group* group = GetFreeBfRaid(player->GetTeamId());
if (!group)
{
group = new Group;
group->SetBattlefieldGroup(this);
group->Create(player);
sGroupMgr->AddGroup(group);
m_Groups[player->GetTeamId()].insert(group->GetGUID());
}
else if (group->IsMember(player->GetGUID()))
{
uint8 subgroup = group->GetMemberGroup(player->GetGUID());
player->SetBattlegroundOrBattlefieldRaid(group, subgroup);
}
else
group->AddMember(player);
return true;
}
//***************End of Group System*******************
//*****************************************************
//***************Spirit Guide System*******************
//*****************************************************
//--------------------
//-Battlefield Method-
//--------------------
BfGraveyard* Battlefield::GetGraveyardById(uint32 id) const
{
if (id < m_GraveyardList.size())
{
if (BfGraveyard* graveyard = m_GraveyardList.at(id))
return graveyard;
else
TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u does not exist.", id);
}
else
TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u could not be found.", id);
return nullptr;
}
WorldSafeLocsEntry const* Battlefield::GetClosestGraveYard(Player* player)
{
BfGraveyard* closestGY = nullptr;
float maxdist = -1;
for (uint8 i = 0; i < m_GraveyardList.size(); i++)
{
if (m_GraveyardList[i])
{
if (m_GraveyardList[i]->GetControlTeamId() != player->GetTeamId())
continue;
float dist = m_GraveyardList[i]->GetDistance(player);
if (dist < maxdist || maxdist < 0)
{
closestGY = m_GraveyardList[i];
maxdist = dist;
}
}
}
if (closestGY)
return sWorldSafeLocsStore.LookupEntry(closestGY->GetGraveyardId());
return nullptr;
}
void Battlefield::AddPlayerToResurrectQueue(ObjectGuid npcGuid, ObjectGuid playerGuid)
{
for (uint8 i = 0; i < m_GraveyardList.size(); i++)
{
if (!m_GraveyardList[i])
continue;
if (m_GraveyardList[i]->HasNpc(npcGuid))
{
m_GraveyardList[i]->AddPlayer(playerGuid);
break;
}
}
}
void Battlefield::RemovePlayerFromResurrectQueue(ObjectGuid playerGuid)
{
for (uint8 i = 0; i < m_GraveyardList.size(); i++)
{
if (!m_GraveyardList[i])
continue;
if (m_GraveyardList[i]->HasPlayer(playerGuid))
{
m_GraveyardList[i]->RemovePlayer(playerGuid);
break;
}
}
}
void Battlefield::SendAreaSpiritHealerQueryOpcode(Player* player, ObjectGuid const& guid)
{
WorldPackets::Battleground::AreaSpiritHealerTime areaSpiritHealerTime;
areaSpiritHealerTime.HealerGuid = guid;
areaSpiritHealerTime.TimeLeft = m_LastResurrectTimer;
player->SendDirectMessage(areaSpiritHealerTime.Write());
}
// ----------------------
// - BfGraveyard Method -
// ----------------------
BfGraveyard::BfGraveyard(Battlefield* battlefield)
{
m_Bf = battlefield;
m_GraveyardId = 0;
m_ControlTeam = TEAM_NEUTRAL;
}
void BfGraveyard::Initialize(TeamId startControl, uint32 graveyardId)
{
m_ControlTeam = startControl;
m_GraveyardId = graveyardId;
}
void BfGraveyard::SetSpirit(Creature* spirit, TeamId team)
{
if (!spirit)
{
TC_LOG_ERROR("bg.battlefield", "BfGraveyard::SetSpirit: Invalid Spirit.");
return;
}
m_SpiritGuide[team] = spirit->GetGUID();
spirit->SetReactState(REACT_PASSIVE);
}
float BfGraveyard::GetDistance(Player* player)
{
WorldSafeLocsEntry const* safeLoc = sWorldSafeLocsStore.LookupEntry(m_GraveyardId);
return player->GetDistance2d(safeLoc->Loc.X, safeLoc->Loc.Y);
}
void BfGraveyard::AddPlayer(ObjectGuid playerGuid)
{
if (!m_ResurrectQueue.count(playerGuid))
{
m_ResurrectQueue.insert(playerGuid);
if (Player* player = ObjectAccessor::FindPlayer(playerGuid))
player->CastSpell(player, SPELL_WAITING_FOR_RESURRECT, true);
}
}
void BfGraveyard::RemovePlayer(ObjectGuid playerGuid)
{
m_ResurrectQueue.erase(m_ResurrectQueue.find(playerGuid));
if (Player* player = ObjectAccessor::FindPlayer(playerGuid))
player->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
}
void BfGraveyard::Resurrect()
{
if (m_ResurrectQueue.empty())
return;
for (GuidSet::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
{
// Get player object from his guid
Player* player = ObjectAccessor::FindPlayer(*itr);
if (!player)
continue;
// Check if the player is in world and on the good graveyard
if (player->IsInWorld())
if (Creature* spirit = m_Bf->GetCreature(m_SpiritGuide[m_ControlTeam]))
spirit->CastSpell(spirit, SPELL_SPIRIT_HEAL, true);
// Resurrect player
player->CastSpell(player, SPELL_RESURRECTION_VISUAL, true);
player->ResurrectPlayer(1.0f);
player->CastSpell(player, 6962, true);
player->CastSpell(player, SPELL_SPIRIT_HEAL_MANA, true);
player->SpawnCorpseBones(false);
}
m_ResurrectQueue.clear();
}
// For changing graveyard control
void BfGraveyard::GiveControlTo(TeamId team)
{
// Guide switching
// Note: Visiblity changes are made by phasing
/*if (m_SpiritGuide[1 - team])
m_SpiritGuide[1 - team]->SetVisible(false);
if (m_SpiritGuide[team])
m_SpiritGuide[team]->SetVisible(true);*/
m_ControlTeam = team;
// Teleport to other graveyard, player witch were on this graveyard
RelocateDeadPlayers();
}
void BfGraveyard::RelocateDeadPlayers()
{
WorldSafeLocsEntry const* closestGrave = nullptr;
for (GuidSet::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
{
Player* player = ObjectAccessor::FindPlayer(*itr);
if (!player)
continue;
if (closestGrave)
player->TeleportTo(player->GetMapId(), closestGrave->Loc.X, closestGrave->Loc.Y, closestGrave->Loc.Z, player->GetOrientation());
else
{
closestGrave = m_Bf->GetClosestGraveYard(player);
if (closestGrave)
player->TeleportTo(player->GetMapId(), closestGrave->Loc.X, closestGrave->Loc.Y, closestGrave->Loc.Z, player->GetOrientation());
}
}
}
bool BfGraveyard::HasNpc(ObjectGuid guid)
{
if (!m_SpiritGuide[TEAM_ALLIANCE] || !m_SpiritGuide[TEAM_HORDE])
return false;
if (!m_Bf->GetCreature(m_SpiritGuide[TEAM_ALLIANCE]) ||
!m_Bf->GetCreature(m_SpiritGuide[TEAM_HORDE]))
return false;
return (m_SpiritGuide[TEAM_ALLIANCE] == guid || m_SpiritGuide[TEAM_HORDE] == guid);
}
// *******************************************************
// *************** End Spirit Guide system ***************
// *******************************************************
// ********************** Misc ***************************
// *******************************************************
Creature* Battlefield::SpawnCreature(uint32 entry, Position const& pos)
{
//Get map object
Map* map = sMapMgr->CreateBaseMap(m_MapId);
if (!map)
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u, map not found.", entry);
return nullptr;
}
if (!sObjectMgr->GetCreatureTemplate(entry))
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: entry %u does not exist.", entry);
return nullptr;
}
Creature* creature = Creature::CreateCreature(entry, map, pos);
if (!creature)
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u", entry);
return nullptr;
}
creature->SetHomePosition(pos);
// Set creature in world
map->AddToMap(creature);
creature->setActive(true);
return creature;
}
// Method for spawning gameobject on map
GameObject* Battlefield::SpawnGameObject(uint32 entry, Position const& pos, QuaternionData const& rot)
{
// Get map object
Map* map = sMapMgr->CreateBaseMap(m_MapId);
if (!map)
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Can't create GameObject (Entry: %u). Map not found.", entry);
return nullptr;
}
if (!sObjectMgr->GetGameObjectTemplate(entry))
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: GameObject template %u not found in database! Battlefield not created!", entry);
return nullptr;
}
// Create gameobject
GameObject* go = GameObject::CreateGameObject(entry, map, pos, rot, 255, GO_STATE_READY);
if (!go)
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry);
return nullptr;
}
// Add to world
map->AddToMap(go);
go->setActive(true);
return go;
}
Creature* Battlefield::GetCreature(ObjectGuid guid)
{
if (!m_Map)
return nullptr;
return m_Map->GetCreature(guid);
}
GameObject* Battlefield::GetGameObject(ObjectGuid guid)
{
if (!m_Map)
return nullptr;
return m_Map->GetGameObject(guid);
}
// *******************************************************
// ******************* CapturePoint **********************
// *******************************************************
BfCapturePoint::BfCapturePoint(Battlefield* battlefield) : m_Bf(battlefield), m_capturePointGUID()
{
m_team = TEAM_NEUTRAL;
m_value = 0;
m_minValue = 0.0f;
m_maxValue = 0.0f;
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL;
m_OldState = BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL;
m_capturePointEntry = 0;
m_neutralValuePct = 0;
m_maxSpeed = 0;
}
bool BfCapturePoint::HandlePlayerEnter(Player* player)
{
if (!m_capturePointGUID.IsEmpty())
{
if (GameObject* capturePoint = m_Bf->GetGameObject(m_capturePointGUID))
{
player->SendUpdateWorldState(capturePoint->GetGOInfo()->controlZone.worldState1, 1);
player->SendUpdateWorldState(capturePoint->GetGOInfo()->controlZone.worldstate2, uint32(ceil((m_value + m_maxValue) / (2 * m_maxValue) * 100.0f)));
player->SendUpdateWorldState(capturePoint->GetGOInfo()->controlZone.worldstate3, m_neutralValuePct);
}
}
return m_activePlayers[player->GetTeamId()].insert(player->GetGUID()).second;
}
GuidSet::iterator BfCapturePoint::HandlePlayerLeave(Player* player)
{
if (!m_capturePointGUID.IsEmpty())
if (GameObject* capturePoint = m_Bf->GetGameObject(m_capturePointGUID))
player->SendUpdateWorldState(capturePoint->GetGOInfo()->controlZone.worldState1, 0);
GuidSet::iterator current = m_activePlayers[player->GetTeamId()].find(player->GetGUID());
if (current == m_activePlayers[player->GetTeamId()].end())
return current; // return end()
m_activePlayers[player->GetTeamId()].erase(current++);
return current;
}
void BfCapturePoint::SendChangePhase()
{
if (!m_capturePointGUID)
return;
if (GameObject* capturePoint = m_Bf->GetGameObject(m_capturePointGUID))
{
// send this too, sometimes the slider disappears, dunno why :(
SendUpdateWorldState(capturePoint->GetGOInfo()->controlZone.worldState1, 1);
// send these updates to only the ones in this objective
SendUpdateWorldState(capturePoint->GetGOInfo()->controlZone.worldstate2, (uint32)std::ceil((m_value + m_maxValue) / (2 * m_maxValue) * 100.0f));
// send this too, sometimes it resets :S
SendUpdateWorldState(capturePoint->GetGOInfo()->controlZone.worldstate3, m_neutralValuePct);
}
}
bool BfCapturePoint::SetCapturePointData(GameObject* capturePoint)
{
ASSERT(capturePoint);
TC_LOG_DEBUG("bg.battlefield", "Creating capture point %u", capturePoint->GetEntry());
m_capturePointGUID = capturePoint->GetGUID();
m_capturePointEntry = capturePoint->GetEntry();
// check info existence
GameObjectTemplate const* goinfo = capturePoint->GetGOInfo();
if (goinfo->type != GAMEOBJECT_TYPE_CONTROL_ZONE)
{
TC_LOG_ERROR("misc", "OutdoorPvP: GO %u is not a capture point!", capturePoint->GetEntry());
return false;
}
// get the needed values from goinfo
m_maxValue = goinfo->controlZone.maxTime;
m_maxSpeed = m_maxValue / (goinfo->controlZone.minTime ? goinfo->controlZone.minTime : 60);
m_neutralValuePct = goinfo->controlZone.neutralPercent;
m_minValue = m_maxValue * goinfo->controlZone.neutralPercent / 100;
if (m_team == TEAM_ALLIANCE)
{
m_value = m_maxValue;
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE;
}
else
{
m_value = -m_maxValue;
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE;
}
return true;
}
GameObject* BfCapturePoint::GetCapturePointGo()
{
return m_Bf->GetGameObject(m_capturePointGUID);
}
bool BfCapturePoint::DelCapturePoint()
{
if (!m_capturePointGUID.IsEmpty())
{
if (GameObject* capturePoint = m_Bf->GetGameObject(m_capturePointGUID))
{
capturePoint->SetRespawnTime(0); // not save respawn time
capturePoint->Delete();
capturePoint = nullptr;
}
m_capturePointGUID.Clear();
}
return true;
}
bool BfCapturePoint::Update(uint32 diff)
{
if (!m_capturePointGUID)
return false;
if (GameObject* capturePoint = m_Bf->GetGameObject(m_capturePointGUID))
{
float radius = capturePoint->GetGOInfo()->controlZone.radius;
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
{
for (GuidSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end();)
{
if (Player* player = ObjectAccessor::FindPlayer(*itr))
{
if (!capturePoint->IsWithinDistInMap(player, radius) || !player->IsOutdoorPvPActive())
itr = HandlePlayerLeave(player);
else
++itr;
}
else
++itr;
}
}
std::list players;
Trinity::AnyPlayerInObjectRangeCheck checker(capturePoint, radius);
Trinity::PlayerListSearcher searcher(capturePoint, players, checker);
Cell::VisitWorldObjects(capturePoint, searcher, radius);
for (std::list::iterator itr = players.begin(); itr != players.end(); ++itr)
if ((*itr)->IsOutdoorPvPActive())
if (m_activePlayers[(*itr)->GetTeamId()].insert((*itr)->GetGUID()).second)
HandlePlayerEnter(*itr);
}
// get the difference of numbers
float fact_diff = ((float) m_activePlayers[TEAM_ALLIANCE].size() - (float) m_activePlayers[TEAM_HORDE].size()) * diff / BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL;
if (G3D::fuzzyEq(fact_diff, 0.0f))
return false;
uint32 Challenger = 0;
float maxDiff = m_maxSpeed * diff;
if (fact_diff < 0)
{
// horde is in majority, but it's already horde-controlled -> no change
if (m_State == BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE && m_value <= -m_maxValue)
return false;
if (fact_diff < -maxDiff)
fact_diff = -maxDiff;
Challenger = HORDE;
}
else
{
// ally is in majority, but it's already ally-controlled -> no change
if (m_State == BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE && m_value >= m_maxValue)
return false;
if (fact_diff > maxDiff)
fact_diff = maxDiff;
Challenger = ALLIANCE;
}
float oldValue = m_value;
TeamId oldTeam = m_team;
m_OldState = m_State;
m_value += fact_diff;
if (m_value < -m_minValue) // red
{
if (m_value < -m_maxValue)
m_value = -m_maxValue;
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE;
m_team = TEAM_HORDE;
}
else if (m_value > m_minValue) // blue
{
if (m_value > m_maxValue)
m_value = m_maxValue;
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE;
m_team = TEAM_ALLIANCE;
}
else if (oldValue * m_value <= 0) // grey, go through mid point
{
// if challenger is ally, then n->a challenge
if (Challenger == ALLIANCE)
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE;
// if challenger is horde, then n->h challenge
else if (Challenger == HORDE)
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE;
m_team = TEAM_NEUTRAL;
}
else // grey, did not go through mid point
{
// old phase and current are on the same side, so one team challenges the other
if (Challenger == ALLIANCE && (m_OldState == BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE || m_OldState == BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE))
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE;
else if (Challenger == HORDE && (m_OldState == BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE || m_OldState == BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE))
m_State = BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE;
m_team = TEAM_NEUTRAL;
}
if (G3D::fuzzyNe(m_value, oldValue))
SendChangePhase();
if (m_OldState != m_State)
{
//TC_LOG_ERROR("bg.battlefield", "%u->%u", m_OldState, m_State);
if (oldTeam != m_team)
ChangeTeam(oldTeam);
return true;
}
return false;
}
void BfCapturePoint::SendUpdateWorldState(uint32 field, uint32 value)
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (GuidSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr) // send to all players present in the area
if (Player* player = ObjectAccessor::FindPlayer(*itr))
player->SendUpdateWorldState(field, value);
}
void BfCapturePoint::SendObjectiveComplete(uint32 id, ObjectGuid guid)
{
uint8 team;
switch (m_State)
{
case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE:
team = TEAM_ALLIANCE;
break;
case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE:
team = TEAM_HORDE;
break;
default:
return;
}
// send to all players present in the area
for (GuidSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr)
if (Player* player = ObjectAccessor::FindPlayer(*itr))
player->KilledMonsterCredit(id, guid);
}
bool BfCapturePoint::IsInsideObjective(Player* player) const
{
return m_activePlayers[player->GetTeamId()].find(player->GetGUID()) != m_activePlayers[player->GetTeamId()].end();
}