/*
* Copyright (C) 2008-2010 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 "Battlefield.h"
#include "BattlefieldMgr.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Map.h"
#include "MapManager.h"
#include "Group.h"
#include "WorldPacket.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
#include "CreatureTextMgr.h"
#include "GroupMgr.h"
Battlefield::Battlefield()
{
m_Timer = 0;
m_enable = true;
m_BattlefieldActive = false;
m_DefenderTeam = TEAM_NEUTRAL;
m_TypeId = 0;
m_BattleId = 0;
m_ZoneId = 0;
m_MapId = 0;
m_MaxPlayer = 0;
m_MinPlayer = 0;
m_BattleTime = 0;
m_NoWarBattleTime = 0;
m_TimeForAcceptInvite = 20;
m_uiKickDontAcceptTimer = 1000;
m_uiKickAfkTimer = 1000;
m_LastResurectTimer = 30 * IN_MILLISECONDS;
m_StartGroupingTimer = 0;
m_StartGrouping = false;
StalkerGuid = 0;
}
Battlefield::~Battlefield()
{
}
void Battlefield::HandlePlayerEnterZone(Player *plr, uint32 /*zone */ )
{
//If battle is start,
// if it not fully > invite player to join the war
// if it fully > announce to player that BF is full and kick after few second if he dont leave
if (IsWarTime())
{
if (m_PlayersInWar[plr->GetTeamId()].size() + m_InvitedPlayers[plr->GetTeamId()].size() < m_MaxPlayer) //Not fully
{
InvitePlayerToWar(plr);
}
else //Full
{
//TODO:Send packet for announce it to player
m_PlayersWillBeKick[plr->GetTeamId()][plr->GetGUID()] = time(NULL) + 10;
InvitePlayerToQueue(plr);
}
}
else
{
//If time left is <15 minutes invite player to join queue
if (m_Timer <= m_StartGroupingTimer)
InvitePlayerToQueue(plr);
}
//Add player in list of player in zone
m_players[plr->GetTeamId()].insert(plr->GetGUID());
OnPlayerEnterZone(plr); //for scripting
}
//Called when a player leave the zone
void Battlefield::HandlePlayerLeaveZone(Player *plr, uint32 /*zone */ )
{
if (IsWarTime())
{
//if player is in war list
if (m_PlayersInWar[plr->GetTeamId()].find(plr->GetGUID()) != m_PlayersInWar[plr->GetTeamId()].end())
{
m_PlayersInWar[plr->GetTeamId()].erase(plr->GetGUID());
plr->GetSession()->SendBfLeaveMessage(m_BattleId);
if (Group* group = GetGroupPlayer(plr->GetGUID(), plr->GetTeamId())) // remove from raid group if player is member
{
// I think that now is not a hack
if (!group->RemoveMember(plr->GetGUID())) // group was disbanded
{
m_Groups[plr->GetTeamId()].erase(group->GetGUID());
group->SetBattlefieldGroup(NULL);
sGroupMgr->RemoveGroup(group);
delete group;
}
}
OnPlayerLeaveWar(plr); //For scripting
}
}
for (BfCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
itr->second->HandlePlayerLeave(plr);
m_InvitedPlayers[plr->GetTeamId()].erase(plr->GetGUID());
m_PlayersWillBeKick[plr->GetTeamId()].erase(plr->GetGUID());
m_players[plr->GetTeamId()].erase(plr->GetGUID());
SendRemoveWorldStates(plr);
RemovePlayerFromResurrectQueue(plr->GetGUID());
OnPlayerLeaveZone(plr); //For scripting
}
bool Battlefield::Update(uint32 diff)
{
//When global timer is end
if (m_Timer <= diff)
{
//Here end of battle by timer
if (IsWarTime())
EndBattle(true);
//Start of battle
else
StartBattle();
}
else
m_Timer -= diff;
//Some times before battle start invite player to queue
if (!m_StartGrouping && m_Timer <= m_StartGroupingTimer)
{
m_StartGrouping = true;
InvitePlayerInZoneToQueue();
OnStartGrouping(); // for scripting
}
bool objective_changed = false;
if (IsWarTime())
{
if (m_uiKickAfkTimer <= diff)
{
m_uiKickAfkTimer = 1000;
KickAfk();
}
else
m_uiKickAfkTimer -= diff;
//Here kick player witch dont have accept invitation to join the war when time is end (time of windows)
if (m_uiKickDontAcceptTimer <= diff)
{
for (int team = 0; team < 2; team++)
for (PlayerTimerMap::iterator itr = m_InvitedPlayers[team].begin(); itr != m_InvitedPlayers[team].end(); itr++)
if ((*itr).second <= time(NULL))
KickPlayerFromBf((*itr).first);
InvitePlayerInZoneToWar();
for (int team = 0; team < 2; team++)
for (PlayerTimerMap::iterator itr = m_PlayersWillBeKick[team].begin(); itr != m_PlayersWillBeKick[team].end(); itr++)
if ((*itr).second <= time(NULL))
KickPlayerFromBf((*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_LastResurectTimer <= diff)
{
for (uint8 i = 0; i < m_GraveYardList.size(); i++)
if (GetGraveYardById(i))
m_GraveYardList[i]->Resurrect();
m_LastResurectTimer = RESURRECTION_INTERVAL;
}
else
m_LastResurectTimer -= diff;
return objective_changed;
}
void Battlefield::InvitePlayerInZoneToQueue()
{
for (uint8 team = 0; team < 2; ++team)
for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
InvitePlayerToQueue(plr);
}
void Battlefield::InvitePlayerToQueue(Player *plr)
{
if (m_PlayersInQueue[plr->GetTeamId()].count(plr->GetGUID()))
return;
if (m_PlayersInQueue[plr->GetTeam()].size() <= m_MinPlayer || m_PlayersInQueue[plr->GetTeam() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE].size() >= m_MinPlayer)
plr->GetSession()->SendBfInvitePlayerToQueue(m_BattleId);
}
void Battlefield::InvitePlayerInQueueToWar()
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
{
for (GuidSet::const_iterator itr = m_PlayersInQueue[team].begin(); itr != m_PlayersInQueue[team].end(); ++itr)
{
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
{
if (m_PlayersInWar[plr->GetTeamId()].size() + m_InvitedPlayers[plr->GetTeamId()].size() < m_MaxPlayer)
InvitePlayerToWar(plr);
else
{
//Full
}
}
}
m_PlayersInQueue[team].clear();
}
}
void Battlefield::InvitePlayerInZoneToWar()
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
{
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
{
if (m_PlayersInWar[plr->GetTeamId()].count(plr->GetGUID()) || m_InvitedPlayers[plr->GetTeamId()].count(plr->GetGUID()))
continue;
if (m_PlayersInWar[plr->GetTeamId()].size() + m_InvitedPlayers[plr->GetTeamId()].size() < m_MaxPlayer)
InvitePlayerToWar(plr);
else
{
//full
m_PlayersWillBeKick[plr->GetTeamId()][plr->GetGUID()] = time(NULL) + 10;
}
}
}
}
void Battlefield::InvitePlayerToWar(Player *plr)
{
if (!plr)
return;
// TODO : needed ?
if (plr->isInFlight())
return;
if (plr->InArena() || plr->GetBattleground())
{
m_PlayersInQueue[plr->GetTeamId()].erase(plr->GetGUID());
return;
}
if (plr->getLevel() < m_MinLevel)
{
if (m_PlayersWillBeKick[plr->GetTeamId()].count(plr->GetGUID()) == 0)
m_PlayersWillBeKick[plr->GetTeamId()][plr->GetGUID()] = time(NULL) + 10;
return;
}
//Check if player is not already in war
if (m_PlayersInWar[plr->GetTeamId()].count(plr->GetGUID()) || m_InvitedPlayers[plr->GetTeamId()].count(plr->GetGUID()))
return;
m_PlayersWillBeKick[plr->GetTeamId()].erase(plr->GetGUID());
m_InvitedPlayers[plr->GetTeamId()][plr->GetGUID()] = time(NULL) + m_TimeForAcceptInvite;
plr->GetSession()->SendBfInvitePlayerToWar(m_BattleId, m_ZoneId, m_TimeForAcceptInvite);
}
void Battlefield::InitStalker(uint32 entry, float x, float y, float z, float o)
{
if (Creature* creature = SpawnCreature(entry, x, y, z, o, TEAM_NEUTRAL))
StalkerGuid = creature->GetGUID();
else
sLog->outError("Battlefield::InitStalker: could not spawn Stalker (Creature entry %u), zone messeges will be un-available", entry);
}
void Battlefield::KickAfk()
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
if (plr->isAFK())
KickPlayerFromBf(*itr);
}
void Battlefield::KickPlayerFromBf(uint64 guid)
{
if (Player* plr = sObjectAccessor->FindPlayer(guid))
if (plr->GetZoneId() == GetZoneId())
plr->TeleportTo(KickPosition);
}
void Battlefield::StartBattle()
{
if (m_BattlefieldActive)
return;
for (int team = 0; team < BG_TEAMS_COUNT; team++)
{
m_PlayersInWar[team].clear();
m_Groups[team].clear();
}
m_Timer = m_BattleTime;
m_BattlefieldActive = true;
InvitePlayerInZoneToWar();
InvitePlayerInQueueToWar();
PlaySoundToAll(BF_START);
OnBattleStart();
}
void Battlefield::EndBattle(bool endbytimer)
{
m_BattlefieldActive = false;
m_StartGrouping = false;
if (!endbytimer)
SetDefenderTeam(GetAttackerTeam());
if (GetDefenderTeam() == TEAM_ALLIANCE)
PlaySoundToAll(BF_ALLIANCE_WINS); // alliance wins sound
else
PlaySoundToAll(BF_HORDE_WINS); // horde wins sound
OnBattleEnd(endbytimer);
// reset bf timer
m_Timer = m_NoWarBattleTime;
SendInitWorldStatesToAll();
}
void Battlefield::PlaySoundToAll(uint32 SoundID)
{
WorldPacket data;
data.Initialize(SMSG_PLAY_SOUND, 4);
data << uint32(SoundID);
for (int team = 0; team < BG_TEAMS_COUNT; team++)
for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
{
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
plr->GetSession()->SendPacket(&data);
}
}
bool Battlefield::HasPlayer(Player *plr) const
{
return m_players[plr->GetTeamId()].find(plr->GetGUID()) != m_players[plr->GetTeamId()].end();
}
// Called in WorldSession::HandleBfQueueInviteResponse
void Battlefield::PlayerAcceptInviteToQueue(Player *plr)
{
// Add player in queueVenez
m_PlayersInQueue[plr->GetTeamId()].insert(plr->GetGUID());
// Send notification
plr->GetSession()->SendBfQueueInviteResponce(m_BattleId, m_ZoneId);
}
// Called in WorldSession::HandleBfExitRequest
void Battlefield::AskToLeaveQueue(Player *plr)
{
// Remove player from queue
m_PlayersInQueue[plr->GetTeamId()].erase(plr->GetGUID());
}
// Called in WorldSession::HandleBfEntryInviteResponse
void Battlefield::PlayerAcceptInviteToWar(Player *plr)
{
if (!IsWarTime())
return;
if (AddOrSetPlayerToCorrectBfGroup(plr))
{
plr->GetSession()->SendBfEntered(m_BattleId);
m_PlayersInWar[plr->GetTeamId()].insert(plr->GetGUID());
m_InvitedPlayers[plr->GetTeamId()].erase(plr->GetGUID());
//Remove player AFK
if (plr->isAFK())
plr->ToggleAFK();
OnPlayerJoinWar(plr); //for scripting
}
}
void Battlefield::TeamCastSpell(TeamId team, int32 spellId)
{
if (spellId > 0)
for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
plr->CastSpell(plr, (uint32) spellId, true);
else
for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
plr->RemoveAuraFromStack((uint32) - spellId);
}
void Battlefield::BroadcastPacketZone(WorldPacket & data) const
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
plr->GetSession()->SendPacket(&data);
}
void Battlefield::BroadcastPacketQueue(WorldPacket & data) const
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (GuidSet::const_iterator itr = m_PlayersInQueue[team].begin(); itr != m_PlayersInQueue[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
plr->GetSession()->SendPacket(&data);
}
void Battlefield::BroadcastPacketWar(WorldPacket & data) const
{
for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team)
for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
plr->GetSession()->SendPacket(&data);
}
WorldPacket Battlefield::BuildWarningAnnPacket(std::string msg)
{
WorldPacket data(SMSG_MESSAGECHAT, 200);
data << uint8(CHAT_MSG_RAID_BOSS_EMOTE);
data << uint32(LANG_UNIVERSAL);
data << uint64(0);
data << uint32(0); // 2.1.0
data << uint32(1);
data << uint8(0);
data << uint64(0);
data << uint32(strlen(msg.c_str()) + 1);
data << msg.c_str();
data << uint8(0);
return data;
}
void Battlefield::SendWarningToAllInZone(uint32 entry)
{
if (Unit* unit = sObjectAccessor->FindUnit(StalkerGuid))
if (Creature* stalker = unit->ToCreature())
// FIXME: replaced CHAT_TYPE_END with CHAT_MSG_BG_SYSTEM_NEUTRAL to fix compile, it's a guessed change :/
sCreatureTextMgr->SendChat(stalker, (uint8) entry, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_ADDON, TEXT_RANGE_ZONE);
}
/*void Battlefield::SendWarningToAllInWar(int32 entry,...)
{
const char *format = sObjectMgr->GetTrinityStringForDBCLocale(entry);
va_list ap;
char str [1024];
va_start(ap, entry);
vsnprintf(str,1024,format, ap);
va_end(ap);
std::string msg = (std::string)str;
WorldPacket data = BuildWarningAnnPacket(msg);
BroadcastPacketWar(data);
}*/
void Battlefield::SendWarningToPlayer(Player *plr, uint32 entry)
{
if (!plr)
return;
if (Unit* unit = sObjectAccessor->FindUnit(StalkerGuid))
if (Creature* stalker = unit->ToCreature())
sCreatureTextMgr->SendChat(stalker, (uint8)entry, plr->GetGUID());
}
void Battlefield::SendUpdateWorldState(uint32 field, uint32 value)
{
for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i)
for (GuidSet::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
plr->SendUpdateWorldState(field, value);
}
void Battlefield::RegisterZone(uint32 zoneId)
{
sBattlefieldMgr.AddZone(zoneId, this);
}
void Battlefield::HideNpc(Creature *p_Creature)
{
p_Creature->CombatStop();
p_Creature->SetReactState(REACT_PASSIVE);
p_Creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
p_Creature->SetPhaseMask(2, true);
p_Creature->DisappearAndDie();
p_Creature->SetVisible(false);
}
void Battlefield::ShowNpc(Creature *p_Creature, bool p_Aggressive)
{
p_Creature->SetPhaseMask(1, true);
p_Creature->SetVisible(true);
p_Creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
if (!p_Creature->isAlive())
p_Creature->Respawn(true);
if (p_Aggressive)
p_Creature->SetReactState(REACT_AGGRESSIVE);
else
{
p_Creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
p_Creature->SetReactState(REACT_PASSIVE);
}
}
//*****************************************************
//*******************Group System**********************
//*****************************************************
Group *Battlefield::GetFreeBfRaid(TeamId TeamId)
{
//if found free group we return it
for (GuidSet::const_iterator itr = m_Groups[TeamId].begin(); itr != m_Groups[TeamId].end(); ++itr)
if (Group* group = sGroupMgr->GetGroupByGUID(*itr))
if (!group->IsFull())
return group;
return NULL;
}
Group *Battlefield::GetGroupPlayer(uint64 guid, TeamId TeamId)
{
for (GuidSet::const_iterator itr = m_Groups[TeamId].begin(); itr != m_Groups[TeamId].end(); ++itr)
if (Group* group = sGroupMgr->GetGroupByGUID(*itr))
if (group->IsMember(guid))
return group;
return NULL;
}
bool Battlefield::AddOrSetPlayerToCorrectBfGroup(Player *plr)
{
if (!plr->IsInWorld())
return false;
if (Group* group = plr->GetGroup())
group->RemoveMember(plr->GetGUID());
Group* group = GetFreeBfRaid(plr->GetTeamId());
if (!group)
{
group = new Group;
group->SetBattlefieldGroup(this);
group->Create(plr);
sGroupMgr->AddGroup(group);
m_Groups[plr->GetTeamId()].insert(group->GetGUID());
}
else if (group->IsMember(plr->GetGUID()))
{
uint8 subgroup = group->GetMemberGroup(plr->GetGUID());
plr->SetBattlegroundOrBattlefieldRaid(group, subgroup);
}
else
group->AddMember(plr);
return true;
}
//***************End of Group System*******************
//*****************************************************
//***************Spirit Guide System*******************
//*****************************************************
//--------------------
//-Battlefield Method-
//--------------------
BfGraveYard *Battlefield::GetGraveYardById(uint32 id)
{
if (id < m_GraveYardList.size())
{
if (m_GraveYardList[id])
return m_GraveYardList[id];
else
sLog->outError("Battlefield::GetGraveYardById Id:%u not existed", id);
}
else
sLog->outError("Battlefield::GetGraveYardById Id:%u cant be found", id);
return NULL;
}
WorldSafeLocsEntry const *Battlefield::GetClosestGraveYard(Player *plr)
{
BfGraveYard* closestGY = NULL;
float maxdist = -1;
for (uint8 i = 0; i < m_GraveYardList.size(); i++)
{
if (m_GraveYardList[i])
{
if (m_GraveYardList[i]->GetControlTeamId() != plr->GetTeamId())
continue;
float dist = m_GraveYardList[i]->GetDistance(plr);
if (dist < maxdist || maxdist < 0)
{
closestGY = m_GraveYardList[i];
maxdist = dist;
}
}
}
if (closestGY)
return sWorldSafeLocsStore.LookupEntry(closestGY->GetGraveYardId());
return NULL;
}
void Battlefield::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
{
for (uint8 i = 0; i < m_GraveYardList.size(); i++)
{
if (!m_GraveYardList[i])
continue;
if (m_GraveYardList[i]->HasNpc(npc_guid))
{
m_GraveYardList[i]->AddPlayer(player_guid);
break;
}
}
}
void Battlefield::RemovePlayerFromResurrectQueue(uint64 player_guid)
{
for (uint8 i = 0; i < m_GraveYardList.size(); i++)
{
if (!m_GraveYardList[i])
continue;
if (m_GraveYardList[i]->HasPlayer(player_guid))
{
m_GraveYardList[i]->RemovePlayer(player_guid);
break;
}
}
}
void Battlefield::SendAreaSpiritHealerQueryOpcode(Player *pl, const uint64 &guid)
{
sLog->outError("SendAreaSpiritHealerQueryOpcode");
WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12);
uint32 time = m_LastResurectTimer; // resurrect every 30 seconds
data << guid << time;
ASSERT(pl && pl->GetSession());
pl->GetSession()->SendPacket(&data);
}
bool Battlefield::IncrementQuest(Player *player, uint32 quest, bool complete)
{
if (!player)
return false;
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest);
if (!pQuest || player->GetQuestStatus(quest) == QUEST_STATUS_NONE)
return false;
if (complete)
{
player->CompleteQuest(quest);
return true;
}
else
{
for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
{
int32 creature = pQuest->RequiredNpcOrGo[i];
if (uint32 spell_id = pQuest->RequiredSpellCast[i])
{
player->CastedCreatureOrGO(creature, 0, spell_id);
return true;
}
else if (creature > 0)
{
player->KilledMonsterCredit(creature, 0);
return true;
}
else if (creature < 0)
{
player->CastedCreatureOrGO(creature, 0, 0);
return true;
}
}
}
return false;
}
//--------------------
//-BfGraveYard Method-
//--------------------
BfGraveYard::BfGraveYard(Battlefield *Bf)
{
m_Bf = Bf;
m_GraveyardId = 0;
m_ControlTeam = TEAM_NEUTRAL;
m_SpiritGuide[0] = NULL;
m_SpiritGuide[1] = NULL;
m_ResurrectQueue.clear();
}
void BfGraveYard::Init(uint32 horde_entry, uint32 alliance_entry, float x, float y, float z, float o, TeamId startcontrol, uint32 gy)
{
m_ControlTeam = startcontrol;
if (Creature* cre = m_Bf->SpawnCreature(horde_entry, x, y, z, o, TEAM_HORDE))
{
m_SpiritGuide[TEAM_HORDE] = cre;
m_SpiritGuide[TEAM_HORDE]->SetReactState(REACT_PASSIVE);
if (m_ControlTeam == TEAM_ALLIANCE)
m_SpiritGuide[TEAM_HORDE]->SetVisible(false);
}
else
sLog->outError("BfGraveYard::Init can't spawn horde spiritguide %u", horde_entry);
if (Creature* cre = m_Bf->SpawnCreature(alliance_entry, x, y, z, o, TEAM_ALLIANCE))
{
m_SpiritGuide[TEAM_ALLIANCE] = cre;
m_SpiritGuide[TEAM_ALLIANCE]->SetReactState(REACT_PASSIVE);
if (m_ControlTeam == TEAM_HORDE)
m_SpiritGuide[TEAM_ALLIANCE]->SetVisible(false);
}
else
sLog->outError("BfGraveYard::Init can't spawn alliance spiritguide %u", alliance_entry);
m_GraveyardId = gy;
}
float BfGraveYard::GetDistance(Player *plr)
{
const WorldSafeLocsEntry* ws = sWorldSafeLocsStore.LookupEntry(m_GraveyardId);
return plr->GetDistance2d(ws->x, ws->y);
}
void BfGraveYard::AddPlayer(uint64 player_guid)
{
if (!m_ResurrectQueue.count(player_guid))
{
m_ResurrectQueue.insert(player_guid);
if (Player* plr = sObjectAccessor->FindPlayer(player_guid))
plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
}
}
void BfGraveYard::RemovePlayer(uint64 player_guid)
{
m_ResurrectQueue.erase(m_ResurrectQueue.find(player_guid));
if (Player* plr = sObjectAccessor->FindPlayer(player_guid))
plr->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* plr = sObjectAccessor->FindPlayer(*itr);
if (!plr)
continue;
// Check player isinworld and player is on good graveyard
if (plr->IsInWorld())
if (m_SpiritGuide[m_ControlTeam])
m_SpiritGuide[m_ControlTeam]->CastSpell(m_SpiritGuide[m_ControlTeam], SPELL_SPIRIT_HEAL, true);
// Resurect player
plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true);
plr->ResurrectPlayer(1.0f);
plr->CastSpell(plr, 6962, true);
plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true);
sObjectAccessor->ConvertCorpseForPlayer(plr->GetGUID());
}
m_ResurrectQueue.clear();
}
// For changing graveyard control
void BfGraveYard::ChangeControl(TeamId team)
{
// Guide switching
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 = NULL;
for (GuidSet::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
{
Player* plr = sObjectAccessor->FindPlayer(*itr);
if (!plr)
continue;
if (ClosestGrave)
plr->TeleportTo(plr->GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation());
else
{
ClosestGrave = m_Bf->GetClosestGraveYard(plr);
if (ClosestGrave)
plr->TeleportTo(plr->GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation());
}
}
}
//***************End Spirit Guide system***************
//*****************************************************
//**********************Misc***************************
//*****************************************************
//Method for spawn creature on map
Creature *Battlefield::SpawnCreature(uint32 entry, Position pos, TeamId team)
{
return SpawnCreature(entry, pos.m_positionX, pos.m_positionY, pos.m_positionZ, pos.m_orientation, team);
}
Creature *Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, float o, TeamId team)
{
//Get map object
Map* map = const_cast < Map * >(sMapMgr->CreateBaseMap(m_MapId));
if (!map)
{
sLog->outError("Can't create creature entry: %u map not found", entry);
return 0;
}
//Create creature
Creature* pCreature = new Creature;
if (!pCreature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, 0, team, x, y, z, o))
{
sLog->outError("Can't create creature entry: %u", entry);
delete pCreature;
return NULL;
}
pCreature->SetHomePosition(x, y, z, o);
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
if (!cinfo)
{
sLog->outErrorDb("Battleground::AddCreature: entry %u does not exist.", entry);
return NULL;
}
// force using DB speeds -- do we really need this?
pCreature->SetSpeed(MOVE_WALK, cinfo->speed_walk);
pCreature->SetSpeed(MOVE_RUN, cinfo->speed_run);
// Set creature in world
map->AddToMap(pCreature);
pCreature->setActive(true);
return pCreature;
}
// Method for spawning gameobject on map
GameObject *Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z, float o)
{
// Get map object
Map* map = const_cast < Map * >(sMapMgr->CreateBaseMap(571));
if (!map)
return 0;
// Create gameobject
GameObject* go = new GameObject;
if (!go->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_GAMEOBJECT), entry, map, PHASEMASK_NORMAL, x, y, z, o, 0, 0, 0, 0, 100, GO_STATE_READY))
{
sLog->outErrorDb("Gameobject template %u not found in database! Battleground not created!", entry);
sLog->outError("Cannot create gameobject template %u! Battleground not created!", entry);
delete go;
return NULL;
}
// Add in the world
map->AddToMap(go);
go->setActive(true);
return go;
}
//*****************************************************
//*******************CapturePoint**********************
//*****************************************************
BfCapturePoint::BfCapturePoint(Battlefield *Bf):m_Bf(Bf), m_capturePoint(NULL)
{
m_team = TEAM_NEUTRAL;
m_value = 0;
m_maxValue = 0;
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 *plr)
{
if (m_capturePoint)
{
plr->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldState1, 1);
plr->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldstate2, (uint32) ceil((m_value + m_maxValue) / (2 * m_maxValue) * 100.0f));
plr->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldstate3, m_neutralValuePct);
}
return m_activePlayers[plr->GetTeamId()].insert(plr->GetGUID()).second;
}
void BfCapturePoint::HandlePlayerLeave(Player *plr)
{
if (m_capturePoint)
plr->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldState1, 0);
m_activePlayers[plr->GetTeamId()].erase(plr->GetGUID());
}
void BfCapturePoint::SendChangePhase()
{
if (!m_capturePoint)
return;
// send this too, sometimes the slider disappears, dunno why :(
SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldState1, 1);
// send these updates to only the ones in this objective
SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldstate2, (uint32) ceil((m_value + m_maxValue) / (2 * m_maxValue) * 100.0f));
// send this too, sometimes it resets :S
SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldstate3, m_neutralValuePct);
}
bool BfCapturePoint::SetCapturePointData(uint32 entry, uint32 /*map */ , float x, float y, float z, float o)
{
sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Creating capture point %u", entry);
// check info existence
GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry);
if (!goinfo || goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT)
{
sLog->outError("OutdoorPvP: GO %u is not capture point!", entry);
return false;
}
m_capturePoint = m_Bf->SpawnGameObject(entry, x, y, z, o);
if (m_capturePoint)
{
// get the needed values from goinfo
m_maxValue = goinfo->capturePoint.maxTime;
m_maxSpeed = m_maxValue / (goinfo->capturePoint.minTime ? goinfo->capturePoint.minTime : 60);
m_neutralValuePct = goinfo->capturePoint.neutralPercent;
m_minValue = m_maxValue * goinfo->capturePoint.neutralPercent / 100;
m_capturePointEntry = entry;
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;
}
return false;
}
bool BfCapturePoint::DelCapturePoint()
{
if (m_capturePoint)
{
m_capturePoint->SetRespawnTime(0); // not save respawn time
m_capturePoint->Delete();
m_capturePoint = NULL;
}
return true;
}
bool BfCapturePoint::Update(uint32 diff)
{
if (!m_capturePoint)
return false;
float radius = m_capturePoint->GetGOInfo()->capturePoint.radius;
for (uint8 team = 0; team < 2; ++team)
for (GuidSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr)
if (Player* plr = sObjectAccessor->FindPlayer(*itr))
if (!m_capturePoint->IsWithinDistInMap(plr, radius) || !plr->IsOutdoorPvPActive())
HandlePlayerLeave(plr);
std::list < Player * >players;
Trinity::AnyPlayerInObjectRangeCheck checker(m_capturePoint, radius);
Trinity::PlayerListSearcher < Trinity::AnyPlayerInObjectRangeCheck > searcher(m_capturePoint, players, checker);
m_capturePoint->VisitNearbyWorldObject(radius, searcher);
for (std::list < Player * >::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[0].size() - (float) m_activePlayers[1].size()) * diff / BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL;
if (!fact_diff)
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 (m_value != oldValue)
SendChangePhase();
if (m_OldState != m_State)
{
//sLog->outError("%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 < 2; ++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* plr = sObjectAccessor->FindPlayer(*itr))
plr->SendUpdateWorldState(field, value);
}
void BfCapturePoint::SendObjectiveComplete(uint32 id, uint64 guid)
{
uint8 team;
switch (m_State)
{
case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE:
team = 0;
break;
case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE:
team = 1;
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* plr = sObjectAccessor->FindPlayer(*itr))
plr->KilledMonsterCredit(id, guid);
}
bool BfCapturePoint::IsInsideObjective(Player *plr) const
{
return m_activePlayers[plr->GetTeamId()].find(plr->GetGUID()) != m_activePlayers[plr->GetTeamId()].end();
}