diff options
Diffstat (limited to 'src')
28 files changed, 5671 insertions, 14 deletions
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp new file mode 100644 index 00000000000..cdb8e1999fe --- /dev/null +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -0,0 +1,1110 @@ +/* + * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#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_IsEnabled = true; + m_isActive = 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_uiKickAfkPlayersTimer = 1000; + + m_LastResurectTimer = 30 * IN_MILLISECONDS; + m_StartGroupingTimer = 0; + m_StartGrouping = false; + StalkerGuid = 0; +} + +Battlefield::~Battlefield() +{ +} + +// 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(NULL) + 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(m_BattleId); + 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 (!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) + { + 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)) + KickPlayerFromBattlefield((*itr).first); + + InvitePlayersInZoneToWar(); + 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)) + 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_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::InvitePlayersInZoneToQueue() +{ + for (uint8 team = 0; team < 2; ++team) + for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + if (Player* player = sObjectAccessor->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(m_BattleId); +} + +void Battlefield::InvitePlayersInQueueToWar() +{ + 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* player = sObjectAccessor->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 (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + { + if (Player* player = sObjectAccessor->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(NULL) + 10; + } + } +} + +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(NULL) + 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(NULL) + m_TimeForAcceptInvite; + player->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(LOG_FILTER_GENERAL, "Battlefield::InitStalker: could not spawn Stalker (Creature entry %u), zone messeges will be un-available", entry); +} + +void Battlefield::KickAfkPlayers() +{ + 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* player = sObjectAccessor->FindPlayer(*itr)) + if (player->isAFK()) + KickPlayerFromBattlefield(*itr); +} + +void Battlefield::KickPlayerFromBattlefield(uint64 guid) +{ + if (Player* player = sObjectAccessor->FindPlayer(guid)) + if (player->GetZoneId() == GetZoneId()) + player->TeleportTo(KickPosition); +} + +void Battlefield::StartBattle() +{ + if (m_isActive) + return; + + for (int team = 0; team < BG_TEAMS_COUNT; team++) + { + m_PlayersInWar[team].clear(); + m_Groups[team].clear(); + } + + m_Timer = m_BattleTime; + m_isActive = true; + + InvitePlayersInZoneToWar(); + InvitePlayersInQueueToWar(); + + DoPlaySoundToAll(BF_START); + + OnBattleStart(); +} + +void Battlefield::EndBattle(bool endByTimer) +{ + if (!m_isActive) + return; + + m_isActive = false; + + m_StartGrouping = false; + + if (!endByTimer) + SetDefenderTeam(GetAttackerTeam()); + + if (GetDefenderTeam() == TEAM_ALLIANCE) + DoPlaySoundToAll(BF_ALLIANCE_WINS); + else + DoPlaySoundToAll(BF_HORDE_WINS); + + OnBattleEnd(endByTimer); + + // Reset battlefield timer + m_Timer = m_NoWarBattleTime; + SendInitWorldStatesToAll(); +} + +void Battlefield::DoPlaySoundToAll(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* player = sObjectAccessor->FindPlayer(*itr)) + player->GetSession()->SendPacket(&data); +} + +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(m_BattleId, m_ZoneId); +} + +// Called in WorldSession::HandleBfExitRequest +void Battlefield::AskToLeaveQueue(Player* player) +{ + // Remove player from queue + m_PlayersInQueue[player->GetTeamId()].erase(player->GetGUID()); +} + +// Called in WorldSession::HandleBfEntryInviteResponse +void Battlefield::PlayerAcceptInviteToWar(Player* player) +{ + if (!IsWarTime()) + return; + + if (AddOrSetPlayerToCorrectBfGroup(player)) + { + player->GetSession()->SendBfEntered(m_BattleId); + 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 (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + player->CastSpell(player, uint32(spellId), true); + else + for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + player->RemoveAuraFromStack(uint32(-spellId)); +} + +void Battlefield::BroadcastPacketToZone(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* player = sObjectAccessor->FindPlayer(*itr)) + player->GetSession()->SendPacket(&data); +} + +void Battlefield::BroadcastPacketToQueue(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* player = sObjectAccessor->FindPlayer(*itr)) + player->GetSession()->SendPacket(&data); +} + +void Battlefield::BroadcastPacketToWar(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* player = sObjectAccessor->FindPlayer(*itr)) + player->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* player, uint32 entry) +{ + if (player) + if (Unit* unit = sObjectAccessor->FindUnit(StalkerGuid)) + if (Creature* stalker = unit->ToCreature()) + sCreatureTextMgr->SendChat(stalker, (uint8)entry, player->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* player = sObjectAccessor->FindPlayer(*itr)) + player->SendUpdateWorldState(field, value); +} + +void Battlefield::RegisterZone(uint32 zoneId) +{ + sBattlefieldMgr->AddZone(zoneId, this); +} + +void Battlefield::HideNpc(Creature* creature) +{ + creature->CombatStop(); + creature->SetReactState(REACT_PASSIVE); + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + creature->SetPhaseMask(2, true); + creature->DisappearAndDie(); + creature->SetVisible(false); +} + +void Battlefield::ShowNpc(Creature* creature, bool aggressive) +{ + creature->SetPhaseMask(1, true); + creature->SetVisible(true); + creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + if (!creature->isAlive()) + creature->Respawn(true); + if (aggressive) + creature->SetReactState(REACT_AGGRESSIVE); + else + { + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + creature->SetReactState(REACT_PASSIVE); + } +} + +// **************************************************** +// ******************* Group System ******************* +// **************************************************** +Group* Battlefield::GetFreeBfRaid(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->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* 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->SetBattlegroundRaid(group, subgroup); + } + else + group->AddMember(player); + + 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(LOG_FILTER_GENERAL, "Battlefield::GetGraveyardById Id:%u not existed", id); + } + else + sLog->outError(LOG_FILTER_GENERAL, "Battlefield::GetGraveyardById Id:%u cant be found", id); + + return NULL; +} + +WorldSafeLocsEntry const * Battlefield::GetClosestGraveYard(Player* player) +{ + BfGraveyard* closestGY = NULL; + 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 NULL; +} + +void Battlefield::AddPlayerToResurrectQueue(uint64 npcGuid, uint64 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(uint64 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, const uint64 &guid) +{ + WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); + uint32 time = m_LastResurectTimer; // resurrect every 30 seconds + + data << guid << time; + ASSERT(player && player->GetSession()); + player->GetSession()->SendPacket(&data); +} + +// ---------------------- +// - BfGraveyard Method - +// ---------------------- +BfGraveyard::BfGraveyard(Battlefield* battlefield) +{ + m_Bf = battlefield; + m_GraveyardId = 0; + m_ControlTeam = TEAM_NEUTRAL; + m_SpiritGuide[0] = NULL; + m_SpiritGuide[1] = NULL; + m_ResurrectQueue.clear(); +} + +void BfGraveyard::Initialize(TeamId startControl, uint32 graveyardId) +{ + m_ControlTeam = startControl; + m_GraveyardId = graveyardId; +} + +void BfGraveyard::SetSpirit(Creature* spirit, TeamId team) +{ + if (!spirit) + { + sLog->outError(LOG_FILTER_GENERAL, "BfGraveyard::SetSpirit: Invalid Spirit."); + return; + } + + m_SpiritGuide[team] = spirit->GetGUID(); + spirit->SetReactState(REACT_PASSIVE); +} + +float BfGraveyard::GetDistance(Player* player) +{ + const WorldSafeLocsEntry* safeLoc = sWorldSafeLocsStore.LookupEntry(m_GraveyardId); + return player->GetDistance2d(safeLoc->x, safeLoc->y); +} + +void BfGraveyard::AddPlayer(uint64 playerGuid) +{ + if (!m_ResurrectQueue.count(playerGuid)) + { + m_ResurrectQueue.insert(playerGuid); + + if (Player* player = sObjectAccessor->FindPlayer(playerGuid)) + player->CastSpell(player, SPELL_WAITING_FOR_RESURRECT, true); + } +} + +void BfGraveyard::RemovePlayer(uint64 playerGuid) +{ + m_ResurrectQueue.erase(m_ResurrectQueue.find(playerGuid)); + + if (Player* player = sObjectAccessor->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 = sObjectAccessor->FindPlayer(*itr); + if (!player) + continue; + + // Check if the player is in world and on the good graveyard + if (player->IsInWorld()) + if (Unit* spirit = sObjectAccessor->FindUnit(m_SpiritGuide[m_ControlTeam])) + spirit->CastSpell(spirit, SPELL_SPIRIT_HEAL, true); + + // Resurect 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); + + sObjectAccessor->ConvertCorpseForPlayer(player->GetGUID()); + } + + 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 = NULL; + for (GuidSet::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr) + { + Player* player = sObjectAccessor->FindPlayer(*itr); + if (!player) + continue; + + if (closestGrave) + player->TeleportTo(player->GetMapId(), closestGrave->x, closestGrave->y, closestGrave->z, player->GetOrientation()); + else + { + closestGrave = m_Bf->GetClosestGraveYard(player); + if (closestGrave) + player->TeleportTo(player->GetMapId(), closestGrave->x, closestGrave->y, closestGrave->z, player->GetOrientation()); + } + } +} + +// ******************************************************* +// *************** End Spirit Guide system *************** +// ******************************************************* +// ********************** Misc *************************** +// ******************************************************* + +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(LOG_FILTER_GENERAL, "Battlefield::SpawnCreature: Can't create creature entry: %u map not found", entry); + return 0; + } + + Creature* creature = new Creature; + if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, 0, team, x, y, z, o)) + { + sLog->outError(LOG_FILTER_GENERAL, "Battlefield::SpawnCreature: Can't create creature entry: %u", entry); + delete creature; + return NULL; + } + + creature->SetHomePosition(x, y, z, o); + + CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry); + if (!cinfo) + { + sLog->outError(LOG_FILTER_BATTLEFIELD, "Battlefield::SpawnCreature: entry %u does not exist.", entry); + return NULL; + } + // force using DB speeds -- do we really need this? + creature->SetSpeed(MOVE_WALK, cinfo->speed_walk); + creature->SetSpeed(MOVE_RUN, cinfo->speed_run); + + // Set creature in world + map->AddToMap(creature); + creature->setActive(true); + + return creature; +} + +// 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)); // *vomits* + 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->outError(LOG_FILTER_BATTLEFIELD, "Battlefield::SpawnGameObject: Gameobject template %u not found in database! Battlefield not created!", entry); + sLog->outError(LOG_FILTER_BATTLEFIELD, "Battlefield::SpawnGameObject: Cannot create gameobject template %u! Battlefield not created!", entry); + delete go; + return NULL; + } + + // Add to world + map->AddToMap(go); + go->setActive(true); + + return go; +} + +// ******************************************************* +// ******************* CapturePoint ********************** +// ******************************************************* + +BfCapturePoint::BfCapturePoint(Battlefield* battlefield) : m_Bf(battlefield), 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* player) +{ + if (m_capturePoint) + { + player->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldState1, 1); + player->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldstate2, uint32(ceil((m_value + m_maxValue) / (2 * m_maxValue) * 100.0f))); + player->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldstate3, m_neutralValuePct); + } + return m_activePlayers[player->GetTeamId()].insert(player->GetGUID()).second; +} + +GuidSet::iterator BfCapturePoint::HandlePlayerLeave(Player* player) +{ + if (m_capturePoint) + player->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.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_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(GameObject* capturePoint) +{ + ASSERT(capturePoint); + + sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Creating capture point %u", capturePoint->GetEntry()); + + m_capturePoint = capturePoint; + + // check info existence + GameObjectTemplate const* goinfo = capturePoint->GetGOInfo(); + if (goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT) + { + sLog->outError(LOG_FILTER_GENERAL, "OutdoorPvP: GO %u is not capture point!", capturePoint->GetEntry()); + return false; + } + + // 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 = capturePoint->GetEntry(); + 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; +} + +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();) + { + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + { + if (!m_capturePoint->IsWithinDistInMap(player, radius) || !player->IsOutdoorPvPActive()) + itr = HandlePlayerLeave(player); + else + ++itr; + } + else + ++itr; + } + } + + 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(LOG_FILTER_GENERAL, "%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* player = sObjectAccessor->FindPlayer(*itr)) + player->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* player = sObjectAccessor->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(); +} diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h new file mode 100644 index 00000000000..5fa8d6b1bc8 --- /dev/null +++ b/src/server/game/Battlefield/Battlefield.h @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef BATTLEFIELD_H_ +#define BATTLEFIELD_H_ + +#include "Utilities/Util.h" +#include "SharedDefines.h" +#include "ZoneScript.h" +#include "WorldPacket.h" +#include "GameObject.h" +#include "Battleground.h" +#include "ObjectAccessor.h" + +enum BattlefieldTypes +{ + BATTLEFIELD_WG, // Wintergrasp + BATTLEFIELD_TB, // Tol Barad (cataclysm) +}; + +enum BattlefieldIDs +{ + BATTLEFIELD_BATTLEID_WG = 1, // Wintergrasp battle +}; + +enum BattlefieldObjectiveStates +{ + BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL = 0, + BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE, + BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE, + BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE, + BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE, + BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE, + BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE, +}; + +enum BattlefieldSounds +{ + BF_HORDE_WINS = 8454, + BF_ALLIANCE_WINS = 8455, + BF_START = 3439 +}; + +enum BattlefieldTimers +{ + BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL = 1000 +}; + +// some class predefs +class Player; +class GameObject; +class WorldPacket; +class Creature; +class Unit; + +class Battlefield; +class BfGraveyard; + +typedef std::set<uint64> GuidSet; +typedef std::vector<BfGraveyard*> GraveyardVect; +typedef std::map<uint64, uint32> PlayerTimerMap; + +class BfCapturePoint +{ + public: + BfCapturePoint(Battlefield* bf); + + virtual void FillInitialWorldStates(WorldPacket& /*data*/) {} + + // Send world state update to all players present + void SendUpdateWorldState(uint32 field, uint32 value); + + // Send kill notify to players in the controlling faction + void SendObjectiveComplete(uint32 id, uint64 guid); + + // Used when player is activated/inactivated in the area + virtual bool HandlePlayerEnter(Player* player); + virtual GuidSet::iterator HandlePlayerLeave(Player* player); + //virtual void HandlePlayerActivityChanged(Player* player); + + // Checks if player is in range of a capture credit marker + bool IsInsideObjective(Player* player) const; + + // Returns true if the state of the objective has changed, in this case, the OutdoorPvP must send a world state ui update. + virtual bool Update(uint32 diff); + virtual void ChangeTeam(TeamId /*oldTeam*/) {} + virtual void SendChangePhase(); + + bool SetCapturePointData(GameObject* capturePoint); + GameObject* GetCapturePointGo() { return m_capturePoint; } + + TeamId GetTeamId() { return m_team; } + protected: + bool DelCapturePoint(); + + // active Players in the area of the objective, 0 - alliance, 1 - horde + GuidSet m_activePlayers[2]; + + // Total shift needed to capture the objective + float m_maxValue; + float m_minValue; + + // Maximum speed of capture + float m_maxSpeed; + + // The status of the objective + float m_value; + TeamId m_team; + + // Objective states + BattlefieldObjectiveStates m_OldState; + BattlefieldObjectiveStates m_State; + + // Neutral value on capture bar + uint32 m_neutralValuePct; + + // Pointer to the Battlefield this objective belongs to + Battlefield* m_Bf; + + // Capture point entry + uint32 m_capturePointEntry; + + // Gameobject related to that capture point + GameObject* m_capturePoint; +}; + +class BfGraveyard +{ + public: + BfGraveyard(Battlefield* Bf); + + // Method to changing who controls the graveyard + void GiveControlTo(TeamId team); + TeamId GetControlTeamId() { return m_ControlTeam; } + + // Find the nearest graveyard to a player + float GetDistance(Player* player); + + // Initialize the graveyard + void Initialize(TeamId startcontrol, uint32 gy); + + // Set spirit service for the graveyard + void SetSpirit(Creature* spirit, TeamId team); + + // Add a player to the graveyard + void AddPlayer(uint64 player_guid); + + // Remove a player from the graveyard + void RemovePlayer(uint64 player_guid); + + // Resurrect players + void Resurrect(); + + // Move players waiting to that graveyard on the nearest one + void RelocateDeadPlayers(); + + // Check if this graveyard has a spirit guide + bool HasNpc(uint64 guid) + { + if (!m_SpiritGuide[0] || !m_SpiritGuide[1]) + return false; + + if (!sObjectAccessor->FindUnit(m_SpiritGuide[0]) || + !sObjectAccessor->FindUnit(m_SpiritGuide[1])) + return false; + + return (m_SpiritGuide[0] == guid || m_SpiritGuide[1] == guid); + } + + // Check if a player is in this graveyard's ressurect queue + bool HasPlayer(uint64 guid) { return m_ResurrectQueue.find(guid) != m_ResurrectQueue.end(); } + + // Get the graveyard's ID. + uint32 GetGraveyardId() { return m_GraveyardId; } + + protected: + TeamId m_ControlTeam; + uint32 m_GraveyardId; + uint64 m_SpiritGuide[2]; + GuidSet m_ResurrectQueue; + Battlefield* m_Bf; +}; + +class Battlefield : public ZoneScript +{ + friend class BattlefieldMgr; + + public: + /// Constructor + Battlefield(); + /// Destructor + virtual ~Battlefield(); + + /// typedef of map witch store capturepoint and the associate gameobject entry + typedef std::map<uint32 /*lowguid */, BfCapturePoint*> BfCapturePointMap; + + /// Call this to init the Battlefield + virtual bool SetupBattlefield() { return true; } + + /// Generate packet which contain all worldstatedata of area + virtual void FillInitialWorldStates(WorldPacket& /*data*/) {} + + /// Update data of a worldstate to all players present in zone + void SendUpdateWorldState(uint32 field, uint32 value); + + /** + * \brief Called every time for update bf data and time + * - Update timer for start/end battle + * - Invite player in zone to queue m_StartGroupingTimer minutes before start + * - Kick Afk players + * \param diff : time ellapsed since last call (in ms) + */ + virtual bool Update(uint32 diff); + + /// Invite all players in zone to join the queue, called x minutes before battle start in Update() + void InvitePlayersInZoneToQueue(); + /// Invite all players in queue to join battle on battle start + void InvitePlayersInQueueToWar(); + /// Invite all players in zone to join battle on battle start + void InvitePlayersInZoneToWar(); + + /// Called when a Unit is kill in battlefield zone + virtual void HandleKill(Player* /*killer*/, Unit* /*killed*/) {}; + + uint32 GetTypeId() { return m_TypeId; } + uint32 GetZoneId() { return m_ZoneId; } + + void TeamApplyBuff(TeamId team, uint32 spellId, uint32 spellId2 = 0); + + /// Return true if battle is start, false if battle is not started + bool IsWarTime() { return m_isActive; } + + /// Enable or Disable battlefield + void ToggleBattlefield(bool enable) { m_IsEnabled = enable; } + /// Return if battlefield is enable + bool IsEnabled() { return m_IsEnabled; } + + /** + * \brief Kick player from battlefield and teleport him to kick-point location + * \param guid : guid of player who must be kick + */ + void KickPlayerFromBattlefield(uint64 guid); + + /// Called when player (player) enter in zone + void HandlePlayerEnterZone(Player* player, uint32 zone); + /// Called when player (player) leave the zone + void HandlePlayerLeaveZone(Player* player, uint32 zone); + + // All-purpose data storage 64 bit + virtual uint64 GetData64(uint32 dataId) { return m_Data64[dataId]; } + virtual void SetData64(uint32 dataId, uint64 value) { m_Data64[dataId] = value; } + + // All-purpose data storage 32 bit + virtual uint32 GetData(uint32 dataId) { return m_Data32[dataId]; } + virtual void SetData(uint32 dataId, uint32 value) { m_Data32[dataId] = value; } + virtual void UpdateData(uint32 index, int32 pad) { m_Data32[index] += pad; } + + // Battlefield - generic methods + TeamId GetDefenderTeam() { return m_DefenderTeam; } + TeamId GetAttackerTeam() { return TeamId(1 - m_DefenderTeam); } + TeamId GetOtherTeam(TeamId team) { return (team == TEAM_HORDE ? TEAM_ALLIANCE : TEAM_HORDE); } + void SetDefenderTeam(TeamId team) { m_DefenderTeam = team; } + + // Group methods + /** + * \brief Find a not full battlefield group, if there is no, create one + * \param TeamId : Id of player team for who we search a group (player->GetTeamId()) + */ + Group* GetFreeBfRaid(TeamId TeamId); + /// Return battlefield group where player is. + Group* GetGroupPlayer(uint64 guid, TeamId TeamId); + /// Force player to join a battlefield group + bool AddOrSetPlayerToCorrectBfGroup(Player* player); + + // Graveyard methods + // Find which graveyard the player must be teleported to to be resurrected by spiritguide + WorldSafeLocsEntry const * GetClosestGraveYard(Player* player); + + virtual void AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid); + void RemovePlayerFromResurrectQueue(uint64 player_guid); + void SetGraveyardNumber(uint32 number) { m_GraveyardList.resize(number); } + BfGraveyard* GetGraveyardById(uint32 id); + + // Misc methods + Creature* SpawnCreature(uint32 entry, float x, float y, float z, float o, TeamId team); + Creature* SpawnCreature(uint32 entry, Position pos, TeamId team); + GameObject* SpawnGameObject(uint32 entry, float x, float y, float z, float o); + + // Script-methods + + /// Called on start + virtual void OnBattleStart() {}; + /// Called at the end of battle + virtual void OnBattleEnd(bool /*endByTimer*/) {}; + /// Called x minutes before battle start when player in zone are invite to join queue + virtual void OnStartGrouping() {}; + /// Called when a player accept to join the battle + virtual void OnPlayerJoinWar(Player* /*player*/) {}; + /// Called when a player leave the battle + virtual void OnPlayerLeaveWar(Player* /*player*/) {}; + /// Called when a player leave battlefield zone + virtual void OnPlayerLeaveZone(Player* /*player*/) {}; + /// Called when a player enter in battlefield zone + virtual void OnPlayerEnterZone(Player* /*player*/) {}; + + WorldPacket BuildWarningAnnPacket(std::string msg); + void SendWarningToAllInZone(uint32 entry); + //void SendWarningToAllInWar(int32 entry, ...); -- UNUSED + void SendWarningToPlayer(Player* player, uint32 entry); + + void PlayerAcceptInviteToQueue(Player* player); + void PlayerAcceptInviteToWar(Player* player); + uint32 GetBattleId() { return m_BattleId; } + void AskToLeaveQueue(Player* player); + + virtual void DoCompleteOrIncrementAchievement(uint32 /*achievement*/, Player* /*player*/, uint8 /*incrementNumber = 1*/) {}; + + /// Send all worldstate data to all player in zone. + virtual void SendInitWorldStatesToAll() {}; + + /// Return if we can use mount in battlefield + bool CanFlyIn() { return !m_isActive; } + + void SendAreaSpiritHealerQueryOpcode(Player* player, const uint64 & guid); + + void StartBattle(); + void EndBattle(bool endByTimer); + + void HideNpc(Creature* creature); + void ShowNpc(Creature* creature, bool aggressive); + + GraveyardVect GetGraveyardVector() { return m_GraveyardList; } + + uint32 GetTimer() { return m_Timer; } + void SetTimer(uint32 timer) { m_Timer = timer; } + + void DoPlaySoundToAll(uint32 SoundID); + + void InvitePlayerToQueue(Player* player); + void InvitePlayerToWar(Player* player); + + void InitStalker(uint32 entry, float x, float y, float z, float o); + + protected: + uint64 StalkerGuid; + uint32 m_Timer; // Global timer for event + bool m_IsEnabled; + bool m_isActive; + TeamId m_DefenderTeam; + + // Map of the objectives belonging to this OutdoorPvP + BfCapturePointMap m_capturePoints; + + // Players info maps + GuidSet m_players[BG_TEAMS_COUNT]; // Players in zone + GuidSet m_PlayersInQueue[BG_TEAMS_COUNT]; // Players in the queue + GuidSet m_PlayersInWar[BG_TEAMS_COUNT]; // Players in WG combat + PlayerTimerMap m_InvitedPlayers[BG_TEAMS_COUNT]; + PlayerTimerMap m_PlayersWillBeKick[BG_TEAMS_COUNT]; + + // Variables that must exist for each battlefield + uint32 m_TypeId; // See enum BattlefieldTypes + uint32 m_BattleId; // BattleID (for packet) + uint32 m_ZoneId; // ZoneID of Wintergrasp = 4197 + uint32 m_MapId; // MapId where is Battlefield + uint32 m_MaxPlayer; // Maximum number of player that participated to Battlefield + uint32 m_MinPlayer; // Minimum number of player for Battlefield start + uint32 m_MinLevel; // Required level to participate at Battlefield + uint32 m_BattleTime; // Length of a battle + uint32 m_NoWarBattleTime; // Time between two battles + uint32 m_RestartAfterCrash; // Delay to restart Wintergrasp if the server crashed during a running battle. + uint32 m_TimeForAcceptInvite; + uint32 m_uiKickDontAcceptTimer; + WorldLocation KickPosition; // Position where players are teleported if they switch to afk during the battle or if they don't accept invitation + + uint32 m_uiKickAfkPlayersTimer; // Timer for check Afk in war + + // Graveyard variables + GraveyardVect m_GraveyardList; // Vector witch contain the different GY of the battle + uint32 m_LastResurectTimer; // Timer for resurect player every 30 sec + + uint32 m_StartGroupingTimer; // Timer for invite players in area 15 minute before start battle + bool m_StartGrouping; // bool for know if all players in area has been invited + + GuidSet m_Groups[BG_TEAMS_COUNT]; // Contain different raid group + + std::vector<uint64> m_Data64; + std::vector<uint32> m_Data32; + + void KickAfkPlayers(); + + // use for switch off all worldstate for client + virtual void SendRemoveWorldStates(Player* /*player*/) {} + + // use for send a packet for all player list + void BroadcastPacketToZone(WorldPacket& data) const; + void BroadcastPacketToQueue(WorldPacket& data) const; + void BroadcastPacketToWar(WorldPacket& data) const; + + // CapturePoint system + void AddCapturePoint(BfCapturePoint* cp) { m_capturePoints[cp->GetCapturePointGo()->GetEntry()] = cp; } + + BfCapturePoint* GetCapturePoint(uint32 lowguid) const + { + Battlefield::BfCapturePointMap::const_iterator itr = m_capturePoints.find(lowguid); + if (itr != m_capturePoints.end()) + return itr->second; + return NULL; + } + + void RegisterZone(uint32 zoneid); + bool HasPlayer(Player* player) const; + void TeamCastSpell(TeamId team, int32 spellId); +}; + +#endif diff --git a/src/server/game/Battlefield/BattlefieldHandler.cpp b/src/server/game/Battlefield/BattlefieldHandler.cpp new file mode 100644 index 00000000000..09c6f18f796 --- /dev/null +++ b/src/server/game/Battlefield/BattlefieldHandler.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Common.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +#include "Battlefield.h" +#include "BattlefieldMgr.h" +#include "Opcodes.h" + +//This send to player windows for invite player to join the war +//Param1:(BattleId) the BattleId of Bf +//Param2:(ZoneId) the zone where the battle is (4197 for wg) +//Param3:(time) Time in second that the player have for accept +void WorldSession::SendBfInvitePlayerToWar(uint32 BattleId, uint32 ZoneId, uint32 p_time) +{ + //Send packet + WorldPacket data(SMSG_BATTLEFIELD_MGR_ENTRY_INVITE, 12); + data << uint32(BattleId); + data << uint32(ZoneId); + data << uint32((time(NULL) + p_time)); + + //Sending the packet to player + SendPacket(&data); +} + +//This send invitation to player to join the queue +//Param1:(BattleId) the BattleId of Bf +void WorldSession::SendBfInvitePlayerToQueue(uint32 BattleId) +{ + WorldPacket data(SMSG_BATTLEFIELD_MGR_QUEUE_INVITE, 5); + + data << uint32(BattleId); + data << uint8(1); //warmup ? used ? + + //Sending packet to player + SendPacket(&data); +} + +//This send packet for inform player that he join queue +//Param1:(BattleId) the BattleId of Bf +//Param2:(ZoneId) the zone where the battle is (4197 for wg) +//Param3:(CanQueue) if able to queue +//Param4:(Full) on log in is full +void WorldSession::SendBfQueueInviteResponse(uint32 BattleId,uint32 ZoneId, bool CanQueue, bool Full) +{ + WorldPacket data(SMSG_BATTLEFIELD_MGR_QUEUE_REQUEST_RESPONSE, 11); + data << uint32(BattleId); + data << uint32(ZoneId); + data << uint8((CanQueue ? 1 : 0)); //Accepted //0 you cannot queue wg //1 you are queued + data << uint8((Full ? 0 : 1)); //Logging In //0 wg full //1 queue for upcoming + data << uint8(1); //Warmup + SendPacket(&data); +} + +//This is call when player accept to join war +//Param1:(BattleId) the BattleId of Bf +void WorldSession::SendBfEntered(uint32 BattleId) +{ +// m_PlayerInWar[player->GetTeamId()].insert(player->GetGUID()); + WorldPacket data(SMSG_BATTLEFIELD_MGR_ENTERED, 7); + data << uint32(BattleId); + data << uint8(1); //unk + data << uint8(1); //unk + data << uint8(_player->isAFK() ? 1 : 0); //Clear AFK + SendPacket(&data); +} + +void WorldSession::SendBfLeaveMessage(uint32 BattleId, BFLeaveReason reason) +{ + WorldPacket data(SMSG_BATTLEFIELD_MGR_EJECTED, 7); + data << uint32(BattleId); + data << uint8(reason);//byte Reason + data << uint8(2);//byte BattleStatus + data << uint8(0);//bool Relocated + SendPacket(&data); +} + +//Send by client when he click on accept for queue +void WorldSession::HandleBfQueueInviteResponse(WorldPacket & recv_data) +{ + uint32 BattleId; + uint8 Accepted; + + recv_data >> BattleId >> Accepted; + sLog->outError(LOG_FILTER_GENERAL, "HandleQueueInviteResponse: BattleID:%u Accepted:%u", BattleId, Accepted); + Battlefield* Bf = sBattlefieldMgr->GetBattlefieldByBattleId(BattleId); + if (!Bf) + return; + + if (Accepted) + { + Bf->PlayerAcceptInviteToQueue(_player); + } +} + +//Send by client on clicking in accept or refuse of invitation windows for join game +void WorldSession::HandleBfEntryInviteResponse(WorldPacket & recv_data) +{ + uint32 BattleId; + uint8 Accepted; + + recv_data >> BattleId >> Accepted; + sLog->outError(LOG_FILTER_GENERAL, "HandleBattlefieldInviteResponse: BattleID:%u Accepted:%u", BattleId, Accepted); + Battlefield* Bf = sBattlefieldMgr->GetBattlefieldByBattleId(BattleId); + if (!Bf) + return; + + //If player accept invitation + if (Accepted) + { + Bf->PlayerAcceptInviteToWar(_player); + } + else + { + if (_player->GetZoneId() == Bf->GetZoneId()) + Bf->KickPlayerFromBattlefield(_player->GetGUID()); + } +} + +void WorldSession::HandleBfExitRequest(WorldPacket & recv_data) +{ + uint32 BattleId; + + recv_data >> BattleId; + sLog->outError(LOG_FILTER_GENERAL, "HandleBfExitRequest: BattleID:%u ", BattleId); + Battlefield* Bf = sBattlefieldMgr->GetBattlefieldByBattleId(BattleId); + if (!Bf) + return; + + Bf->AskToLeaveQueue(_player); +} diff --git a/src/server/game/Battlefield/BattlefieldMgr.cpp b/src/server/game/Battlefield/BattlefieldMgr.cpp new file mode 100644 index 00000000000..6122b25e8e8 --- /dev/null +++ b/src/server/game/Battlefield/BattlefieldMgr.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BattlefieldMgr.h" +#include "Zones/BattlefieldWG.h" +#include "ObjectMgr.h" +#include "Player.h" + +BattlefieldMgr::BattlefieldMgr() +{ + m_UpdateTimer = 0; + //sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Instantiating BattlefieldMgr"); +} + +BattlefieldMgr::~BattlefieldMgr() +{ + //sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Deleting BattlefieldMgr"); + for (BattlefieldSet::iterator itr = m_BattlefieldSet.begin(); itr != m_BattlefieldSet.end(); ++itr) + delete *itr; +} + +void BattlefieldMgr::InitBattlefield() +{ + Battlefield* pBf = new BattlefieldWG; + // respawn, init variables + if (!pBf->SetupBattlefield()) + { + sLog->outInfo(LOG_FILTER_GENERAL, "Battlefield : Wintergrasp init failed."); + delete pBf; + } + else + { + m_BattlefieldSet.push_back(pBf); + sLog->outInfo(LOG_FILTER_GENERAL, "Battlefield : Wintergrasp successfully initiated."); + } + + /* For Cataclysm: Tol Barad + pBf = new BattlefieldTB; + // respawn, init variables + if(!pBf->SetupBattlefield()) + { + sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Battlefield : Tol Barad init failed."); + delete pBf; + } + else + { + m_BattlefieldSet.push_back(pBf); + sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Battlefield : Tol Barad successfully initiated."); + } */ +} + +void BattlefieldMgr::AddZone(uint32 zoneid, Battlefield *handle) +{ + m_BattlefieldMap[zoneid] = handle; +} + +void BattlefieldMgr::HandlePlayerEnterZone(Player * player, uint32 zoneid) +{ + BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid); + if (itr == m_BattlefieldMap.end()) + return; + + if (itr->second->HasPlayer(player) || !itr->second->IsEnabled()) + return; + + itr->second->HandlePlayerEnterZone(player, zoneid); + sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Player %u entered outdoorpvp id %u", player->GetGUIDLow(), itr->second->GetTypeId()); +} + +void BattlefieldMgr::HandlePlayerLeaveZone(Player * player, uint32 zoneid) +{ + BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid); + if (itr == m_BattlefieldMap.end()) + return; + + // teleport: remove once in removefromworld, once in updatezone + if (!itr->second->HasPlayer(player)) + return; + itr->second->HandlePlayerLeaveZone(player, zoneid); + sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Player %u left outdoorpvp id %u", player->GetGUIDLow(), itr->second->GetTypeId()); +} + +Battlefield *BattlefieldMgr::GetBattlefieldToZoneId(uint32 zoneid) +{ + BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid); + if (itr == m_BattlefieldMap.end()) + { + // no handle for this zone, return + return NULL; + } + if (!itr->second->IsEnabled()) + return NULL; + return itr->second; +} + +Battlefield *BattlefieldMgr::GetBattlefieldByBattleId(uint32 battleid) +{ + for (BattlefieldSet::iterator itr = m_BattlefieldSet.begin(); itr != m_BattlefieldSet.end(); ++itr) + { + if ((*itr)->GetBattleId() == battleid) + return (*itr); + } + return NULL; +} + +void BattlefieldMgr::Update(uint32 diff) +{ + m_UpdateTimer += diff; + if (m_UpdateTimer > BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL) + { + for (BattlefieldSet::iterator itr = m_BattlefieldSet.begin(); itr != m_BattlefieldSet.end(); ++itr) + if ((*itr)->IsEnabled()) + (*itr)->Update(m_UpdateTimer); + m_UpdateTimer = 0; + } +} + +ZoneScript *BattlefieldMgr::GetZoneScript(uint32 zoneId) +{ + BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneId); + if (itr != m_BattlefieldMap.end()) + return itr->second; + else + return NULL; +} diff --git a/src/server/game/Battlefield/BattlefieldMgr.h b/src/server/game/Battlefield/BattlefieldMgr.h new file mode 100644 index 00000000000..4ee37e424fd --- /dev/null +++ b/src/server/game/Battlefield/BattlefieldMgr.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef BATTLEFIELD_MGR_H_ +#define BATTLEFIELD_MGR_H_ + +#include "Battlefield.h" +#include "ace/Singleton.h" + +class Player; +class GameObject; +class Creature; +class ZoneScript; +struct GossipMenuItems; + +// class to handle player enter / leave / areatrigger / GO use events +class BattlefieldMgr +{ + public: + // ctor + BattlefieldMgr(); + // dtor + ~BattlefieldMgr(); + + // create battlefield events + void InitBattlefield(); + // called when a player enters an battlefield area + void HandlePlayerEnterZone(Player * player, uint32 areaflag); + // called when player leaves an battlefield area + void HandlePlayerLeaveZone(Player * player, uint32 areaflag); + // called when player resurrects + void HandlePlayerResurrects(Player * player, uint32 areaflag); + // return assigned battlefield + Battlefield *GetBattlefieldToZoneId(uint32 zoneid); + Battlefield *GetBattlefieldByBattleId(uint32 battleid); + + ZoneScript *GetZoneScript(uint32 zoneId); + + void AddZone(uint32 zoneid, Battlefield * handle); + + void Update(uint32 diff); + + void HandleGossipOption(Player * player, uint64 guid, uint32 gossipid); + + bool CanTalkTo(Player * player, Creature * creature, GossipMenuItems gso); + + void HandleDropFlag(Player * player, uint32 spellId); + + typedef std::vector < Battlefield * >BattlefieldSet; + typedef std::map < uint32 /* zoneid */ , Battlefield * >BattlefieldMap; + private: + // contains all initiated battlefield events + // used when initing / cleaning up + BattlefieldSet m_BattlefieldSet; + // maps the zone ids to an battlefield event + // used in player event handling + BattlefieldMap m_BattlefieldMap; + // update interval + uint32 m_UpdateTimer; +}; + +#define sBattlefieldMgr ACE_Singleton<BattlefieldMgr, ACE_Null_Mutex>::instance() + +#endif diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp new file mode 100644 index 00000000000..48d6b59d007 --- /dev/null +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -0,0 +1,1097 @@ +/* + * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +// TODO: Implement proper support for vehicle+player teleportation +// TODO: Use spell victory/defeat in wg instead of RewardMarkOfHonor() && RewardHonor +// TODO: Add proper implement of achievement + +#include "ObjectMgr.h" +#include "BattlefieldWG.h" +#include "SpellAuras.h" +#include "Vehicle.h" + +enum WintergrastData +{ + BATTLEFIELD_WG_ZONEID = 4197, // Wintergrasp + BATTLEFIELD_WG_MAPID = 571, // Northrend +}; + +enum WGVehicles +{ + NPC_WG_SEIGE_ENGINE_ALLIANCE = 28312, + NPC_WG_SEIGE_ENGINE_HORDE = 32627, + NPC_WG_DEMOLISHER = 28094, + NPC_WG_CATAPULT = 27881, +}; + +bool BattlefieldWG::SetupBattlefield() +{ + InitStalker(BATTLEFIELD_WG_NPC_STALKER, WintergraspStalkerPos[0], WintergraspStalkerPos[1], WintergraspStalkerPos[2], WintergraspStalkerPos[3]); + + m_TypeId = BATTLEFIELD_WG; // See enum BattlefieldTypes + m_BattleId = BATTLEFIELD_BATTLEID_WG; + m_ZoneId = BATTLEFIELD_WG_ZONEID; + m_MapId = BATTLEFIELD_WG_MAPID; + + m_MaxPlayer = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MAX); + m_IsEnabled = sWorld->getBoolConfig(CONFIG_WINTERGRASP_ENABLE); + m_MinPlayer = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MIN); + m_MinLevel = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MIN_LVL); + m_BattleTime = sWorld->getIntConfig(CONFIG_WINTERGRASP_BATTLETIME) * MINUTE * IN_MILLISECONDS; + m_NoWarBattleTime = sWorld->getIntConfig(CONFIG_WINTERGRASP_NOBATTLETIME) * MINUTE * IN_MILLISECONDS; + m_RestartAfterCrash = sWorld->getIntConfig(CONFIG_WINTERGRASP_RESTART_AFTER_CRASH) * MINUTE * IN_MILLISECONDS; + + m_TimeForAcceptInvite = 20; + m_StartGroupingTimer = 15 * MINUTE * IN_MILLISECONDS; + m_StartGrouping = false; + + m_tenacityStack = 0; + + KickPosition.Relocate(5728.117f, 2714.346f, 697.733f, 0); + KickPosition.m_mapId = m_MapId; + + RegisterZone(m_ZoneId); + + m_Data32.resize(BATTLEFIELD_WG_DATA_MAX); + + m_saveTimer = 60000; + + // Init GraveYards + SetGraveyardNumber(BATTLEFIELD_WG_GRAVEYARD_MAX); + + // Load from db + if ((sWorld->getWorldState(BATTLEFIELD_WG_WORLD_STATE_ACTIVE) == 0) && (sWorld->getWorldState(BATTLEFIELD_WG_WORLD_STATE_DEFENDER) == 0) + && (sWorld->getWorldState(ClockWorldState[0]) == 0)) + { + sWorld->setWorldState(BATTLEFIELD_WG_WORLD_STATE_ACTIVE, uint64(false)); + sWorld->setWorldState(BATTLEFIELD_WG_WORLD_STATE_DEFENDER, uint64(urand(0, 1))); + sWorld->setWorldState(ClockWorldState[0], uint64(m_NoWarBattleTime)); + } + + m_isActive = bool(sWorld->getWorldState(BATTLEFIELD_WG_WORLD_STATE_ACTIVE)); + m_DefenderTeam = TeamId(sWorld->getWorldState(BATTLEFIELD_WG_WORLD_STATE_DEFENDER)); + + m_Timer = sWorld->getWorldState(ClockWorldState[0]); + if (m_isActive) + { + m_isActive = false; + m_Timer = m_RestartAfterCrash; + } + + for (uint8 i = 0; i < BATTLEFIELD_WG_GRAVEYARD_MAX; i++) + { + BfGraveyardWG* graveyard = new BfGraveyardWG(this); + + // When between games, the graveyard is controlled by the defending team + if (WGGraveYard[i].startcontrol == TEAM_NEUTRAL) + graveyard->Initialize(m_DefenderTeam, WGGraveYard[i].gyid); + else + graveyard->Initialize(WGGraveYard[i].startcontrol, WGGraveYard[i].gyid); + + graveyard->SetTextId(WGGraveYard[i].textid); + m_GraveyardList[i] = graveyard; + } + + + // Spawn workshop creatures and gameobjects + for (uint8 i = 0; i < WG_MAX_WORKSHOP; i++) + { + WGWorkshop* workshop = new WGWorkshop(this, i); + if (i < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) + workshop->GiveControlTo(GetAttackerTeam(), true); + else + workshop->GiveControlTo(GetDefenderTeam(), true); + + // Note: Capture point is added once the gameobject is created. + WorkshopsList.insert(workshop); + } + + // Spawn NPCs in the defender's keep, both Horde and Alliance + for (uint8 i = 0; i < WG_MAX_KEEP_NPC; i++) + { + // Horde npc + if (Creature* creature = SpawnCreature(WGKeepNPC[i].entryHorde, WGKeepNPC[i].x, WGKeepNPC[i].y, WGKeepNPC[i].z, WGKeepNPC[i].o, TEAM_HORDE)) + KeepCreature[TEAM_HORDE].insert(creature->GetGUID()); + // Alliance npc + if (Creature* creature = SpawnCreature(WGKeepNPC[i].entryAlliance, WGKeepNPC[i].x, WGKeepNPC[i].y, WGKeepNPC[i].z, WGKeepNPC[i].o, TEAM_ALLIANCE)) + KeepCreature[TEAM_ALLIANCE].insert(creature->GetGUID()); + } + + // Hide NPCs from the Attacker's team in the keep + for (GuidSet::const_iterator itr = KeepCreature[GetAttackerTeam()].begin(); itr != KeepCreature[GetAttackerTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + HideNpc(creature); + + // Spawn Horde NPCs outside the keep + for (uint8 i = 0; i < WG_OUTSIDE_ALLIANCE_NPC; i++) + if (Creature* creature = SpawnCreature(WGOutsideNPC[i].entryHorde, WGOutsideNPC[i].x, WGOutsideNPC[i].y, WGOutsideNPC[i].z, WGOutsideNPC[i].o, TEAM_HORDE)) + OutsideCreature[TEAM_HORDE].insert(creature->GetGUID()); + + // Spawn Alliance NPCs outside the keep + for (uint8 i = WG_OUTSIDE_ALLIANCE_NPC; i < WG_MAX_OUTSIDE_NPC; i++) + if (Creature* creature = SpawnCreature(WGOutsideNPC[i].entryAlliance, WGOutsideNPC[i].x, WGOutsideNPC[i].y, WGOutsideNPC[i].z, WGOutsideNPC[i].o, TEAM_ALLIANCE)) + OutsideCreature[TEAM_ALLIANCE].insert(creature->GetGUID()); + + // Hide units outside the keep that are defenders + for (GuidSet::const_iterator itr = OutsideCreature[GetDefenderTeam()].begin(); itr != OutsideCreature[GetDefenderTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + HideNpc(creature); + + // Spawn turrets and hide them per default + for (uint8 i = 0; i < WG_MAX_TURRET; i++) + { + Position towerCannonPos; + WGTurret[i].GetPosition(&towerCannonPos); + if (Creature* creature = SpawnCreature(NPC_TOWER_CANNON, towerCannonPos, TEAM_ALLIANCE)) + { + CanonList.insert(creature->GetGUID()); + HideNpc(creature); + } + } + + // Spawn all gameobjects + for (uint8 i = 0; i < WG_MAX_OBJ; i++) + { + GameObject* go = SpawnGameObject(WGGameObjectBuilding[i].entry, WGGameObjectBuilding[i].x, WGGameObjectBuilding[i].y, WGGameObjectBuilding[i].z, WGGameObjectBuilding[i].o); + BfWGGameObjectBuilding* b = new BfWGGameObjectBuilding(this); + b->Init(go, WGGameObjectBuilding[i].type, WGGameObjectBuilding[i].WorldState, WGGameObjectBuilding[i].nameId); + BuildingsInZone.insert(b); + } + + // Spawning portal defender + for (uint8 i = 0; i < WG_MAX_TELEPORTER; i++) + { + GameObject* go = SpawnGameObject(WGPortalDefenderData[i].entry, WGPortalDefenderData[i].x, WGPortalDefenderData[i].y, WGPortalDefenderData[i].z, WGPortalDefenderData[i].o); + DefenderPortalList.insert(go); + go->SetUInt32Value(GAMEOBJECT_FACTION, WintergraspFaction[GetDefenderTeam()]); + } + + // Spawn banners in the keep + for (uint8 i = 0; i < WG_KEEPGAMEOBJECT_MAX; i++) + { + if (GameObject* go = SpawnGameObject(WGKeepGameObject[i].entryHorde, WGKeepGameObject[i].x, WGKeepGameObject[i].y, WGKeepGameObject[i].z, WGKeepGameObject[i].o)) + { + go->SetRespawnTime(GetDefenderTeam()? RESPAWN_ONE_DAY : RESPAWN_IMMEDIATELY); + m_KeepGameObject[1].insert(go); + } + if (GameObject* go = SpawnGameObject(WGKeepGameObject[i].entryAlliance, WGKeepGameObject[i].x, WGKeepGameObject[i].y, WGKeepGameObject[i].z, WGKeepGameObject[i].o)) + { + go->SetRespawnTime(GetDefenderTeam()? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + m_KeepGameObject[0].insert(go); + } + } + + // Show defender banner in keep + for (GameObjectSet::const_iterator itr = m_KeepGameObject[GetDefenderTeam()].begin(); itr != m_KeepGameObject[GetDefenderTeam()].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_IMMEDIATELY); + + // Hide attackant banner in keep + for (GameObjectSet::const_iterator itr = m_KeepGameObject[GetAttackerTeam()].begin(); itr != m_KeepGameObject[GetAttackerTeam()].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_ONE_DAY); + + UpdateCounterVehicle(true); + return true; +} + +bool BattlefieldWG::Update(uint32 diff) +{ + bool m_return = Battlefield::Update(diff); + if (m_saveTimer <= diff) + { + sWorld->setWorldState(BATTLEFIELD_WG_WORLD_STATE_ACTIVE, m_isActive); + sWorld->setWorldState(BATTLEFIELD_WG_WORLD_STATE_DEFENDER, m_DefenderTeam); + sWorld->setWorldState(ClockWorldState[0], m_Timer); + m_saveTimer = 60 * IN_MILLISECONDS; + } + else + m_saveTimer -= diff; + + return m_return; +} + +void BattlefieldWG::OnBattleStart() +{ + // Spawn titan relic + m_titansRelic = SpawnGameObject(GO_WINTERGRASP_TITAN_S_RELIC, 5440.0f, 2840.8f, 430.43f, 0); + if (m_titansRelic) + { + // Update faction of relic, only attacker can click on + m_titansRelic->SetUInt32Value(GAMEOBJECT_FACTION, WintergraspFaction[GetAttackerTeam()]); + // Set in use (not allow to click on before last door is broken) + m_titansRelic->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + } + else + sLog->outError(LOG_FILTER_GENERAL, "WG: Failed to spawn titan relic."); + + + // Update tower visibility and update faction + for (GuidSet::const_iterator itr = CanonList.begin(); itr != CanonList.end(); ++itr) + { + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + { + if (Creature* creature = unit->ToCreature()) + { + ShowNpc(creature, true); + creature->setFaction(WintergraspFaction[GetDefenderTeam()]); + } + } + } + + // Rebuild all wall + for (GameObjectBuilding::const_iterator itr = BuildingsInZone.begin(); itr != BuildingsInZone.end(); ++itr) + { + if (*itr) + { + (*itr)->Rebuild(); + (*itr)->UpdateTurretAttack(false); + } + } + + SetData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT, 0); + SetData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_DEF, 0); + SetData(BATTLEFIELD_WG_DATA_DAMAGED_TOWER_ATT, 0); + SetData(BATTLEFIELD_WG_DATA_DAMAGED_TOWER_DEF, 0); + + // Update graveyard (in no war time all graveyard is to deffender, in war time, depend of base) + for (Workshop::const_iterator itr = WorkshopsList.begin(); itr != WorkshopsList.end(); ++itr) + if (*itr) + (*itr)->UpdateGraveyardAndWorkshop(); + + for (uint8 team = 0; team < 2; ++team) + for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + { + // Kick player in orb room, TODO: offline player ? + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + { + float x, y, z; + player->GetPosition(x, y, z); + if (5500 > x && x > 5392 && y < 2880 && y > 2800 && z < 480) + player->TeleportTo(571, 5349.8686f, 2838.481f, 409.240f, 0.046328f); + SendInitWorldStatesTo(player); + } + } + // Initialize vehicle counter + UpdateCounterVehicle(true); + // Send start warning to all players + SendWarningToAllInZone(BATTLEFIELD_WG_TEXT_START); +} + +void BattlefieldWG::UpdateCounterVehicle(bool init) +{ + if (init) + { + SetData(BATTLEFIELD_WG_DATA_VEHICLE_H, 0); + SetData(BATTLEFIELD_WG_DATA_VEHICLE_A, 0); + } + SetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_H, 0); + SetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_A, 0); + + for (Workshop::const_iterator itr = WorkshopsList.begin(); itr != WorkshopsList.end(); ++itr) + { + if (WGWorkshop* workshop = (*itr)) + { + if (workshop->teamControl == TEAM_ALLIANCE) + UpdateData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_A, 4); + else if (workshop->teamControl == TEAM_HORDE) + UpdateData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_H, 4); + } + } + + UpdateVehicleCountWG(); +} + +void BattlefieldWG::OnBattleEnd(bool endByTimer) +{ + // Remove relic + if (m_titansRelic) + m_titansRelic->RemoveFromWorld(); + m_titansRelic = NULL; + + // Remove turret + for (GuidSet::const_iterator itr = CanonList.begin(); itr != CanonList.end(); ++itr) + { + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + { + if (Creature* creature = unit->ToCreature()) + { + if (!endByTimer) + creature->setFaction(WintergraspFaction[GetDefenderTeam()]); + HideNpc(creature); + } + } + } + + if (!endByTimer) // One player triggered the relic + { + // Change all npc in keep + for (GuidSet::const_iterator itr = KeepCreature[GetAttackerTeam()].begin(); itr != KeepCreature[GetAttackerTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + HideNpc(creature); + + for (GuidSet::const_iterator itr = KeepCreature[GetDefenderTeam()].begin(); itr != KeepCreature[GetDefenderTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + ShowNpc(creature, true); + + // Change all npc out of keep + for (GuidSet::const_iterator itr = OutsideCreature[GetDefenderTeam()].begin(); itr != OutsideCreature[GetDefenderTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + HideNpc(creature); + + for (GuidSet::const_iterator itr = OutsideCreature[GetAttackerTeam()].begin(); itr != OutsideCreature[GetAttackerTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + ShowNpc(creature, true); + } + + // Update all graveyard, control is to defender when no wartime + for (uint8 i = 0; i < BATTLEFIELD_WG_GY_HORDE; i++) + if (BfGraveyard* graveyard = GetGraveyardById(i)) + graveyard->GiveControlTo(GetDefenderTeam()); + + for (GameObjectSet::const_iterator itr = m_KeepGameObject[GetDefenderTeam()].begin(); itr != m_KeepGameObject[GetDefenderTeam()].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_IMMEDIATELY); + + for (GameObjectSet::const_iterator itr = m_KeepGameObject[GetAttackerTeam()].begin(); itr != m_KeepGameObject[GetAttackerTeam()].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_ONE_DAY); + + // Update portal defender faction + for (GameObjectSet::const_iterator itr = DefenderPortalList.begin(); itr != DefenderPortalList.end(); ++itr) + (*itr)->SetUInt32Value(GAMEOBJECT_FACTION, WintergraspFaction[GetDefenderTeam()]); + + // Saving data + for (GameObjectBuilding::const_iterator itr = BuildingsInZone.begin(); itr != BuildingsInZone.end(); ++itr) + (*itr)->Save(); + for (Workshop::const_iterator itr = WorkshopsList.begin(); itr != WorkshopsList.end(); ++itr) + (*itr)->Save(); + + for (GuidSet::const_iterator itr = m_PlayersInWar[GetDefenderTeam()].begin(); itr != m_PlayersInWar[GetDefenderTeam()].end(); ++itr) + { + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + { + player->CastSpell(player, SPELL_ESSENCE_OF_WINTERGRASP, true); + player->CastSpell(player, SPELL_VICTORY_REWARD, true); + // Send Wintergrasp victory achievement + DoCompleteOrIncrementAchievement(ACHIEVEMENTS_WIN_WG, player); + // Award achievement for succeeding in Wintergrasp in 10 minutes or less + if (!endByTimer && GetTimer() <= 10000) + DoCompleteOrIncrementAchievement(ACHIEVEMENTS_WIN_WG_TIMER_10, player); + } + } + + for (GuidSet::const_iterator itr = m_PlayersInWar[GetAttackerTeam()].begin(); itr != m_PlayersInWar[GetAttackerTeam()].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + player->CastSpell(player, SPELL_DEFEAT_REWARD, true); + + for (uint8 team = 0; team < 2; ++team) + { + for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + RemoveAurasFromPlayer(player); + + m_PlayersInWar[team].clear(); + + for (GuidSet::const_iterator itr = m_vehicles[team].begin(); itr != m_vehicles[team].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + if (creature->IsVehicle()) + creature->GetVehicleKit()->Dismiss(); + + m_vehicles[team].clear(); + } + + if (!endByTimer) + { + for (uint8 team = 0; team < 2; ++team) + { + for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + { + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + { + player->RemoveAurasDueToSpell(m_DefenderTeam == TEAM_ALLIANCE ? SPELL_HORDE_CONTROL_PHASE_SHIFT : SPELL_ALLIANCE_CONTROL_PHASE_SHIFT, player->GetGUID()); + player->AddAura(m_DefenderTeam == TEAM_HORDE ? SPELL_HORDE_CONTROL_PHASE_SHIFT : SPELL_ALLIANCE_CONTROL_PHASE_SHIFT, player); + } + } + } + } + + if (!endByTimer) // win alli/horde + SendWarningToAllInZone((GetDefenderTeam() == TEAM_ALLIANCE) ? BATTLEFIELD_WG_TEXT_WIN_KEEP : BATTLEFIELD_WG_TEXT_WIN_KEEP + 1); + else // defend alli/horde + SendWarningToAllInZone((GetDefenderTeam() == TEAM_ALLIANCE) ? BATTLEFIELD_WG_TEXT_DEFEND_KEEP : BATTLEFIELD_WG_TEXT_DEFEND_KEEP + 1); +} + +// ******************************************************* +// ******************* Reward System ********************* +// ******************************************************* +void BattlefieldWG::DoCompleteOrIncrementAchievement(uint32 achievement, Player* player, uint8 /*incrementNumber*/) +{ + AchievementEntry const* achievementEntry = sAchievementStore.LookupEntry(achievement); + + if (!achievementEntry) + return; + + switch (achievement) + { + case ACHIEVEMENTS_WIN_WG_100: + { + // player->GetAchievementMgr().UpdateAchievementCriteria(); + } + default: + { + if (player) + player->CompletedAchievement(achievementEntry); + break; + } + } + +} + +void BattlefieldWG::OnStartGrouping() +{ + SendWarningToAllInZone(BATTLEFIELD_WG_TEXT_WILL_START); +} + +uint8 BattlefieldWG::GetSpiritGraveyardId(uint32 areaId) +{ + switch (areaId) + { + case AREA_WINTERGRASP_FORTRESS: + return BATTLEFIELD_WG_GY_KEEP; + case AREA_THE_SUNKEN_RING: + return BATTLEFIELD_WG_GY_WORKSHOP_NE; + case AREA_THE_BROKEN_TEMPLATE: + return BATTLEFIELD_WG_GY_WORKSHOP_NW; + case AREA_WESTPARK_WORKSHOP: + return BATTLEFIELD_WG_GY_WORKSHOP_SW; + case AREA_EASTPARK_WORKSHOP: + return BATTLEFIELD_WG_GY_WORKSHOP_SE; + case AREA_WINTERGRASP: + return BATTLEFIELD_WG_GY_ALLIANCE; + case AREA_THE_CHILLED_QUAGMIRE: + return BATTLEFIELD_WG_GY_HORDE; + default: + sLog->outError(LOG_FILTER_GENERAL, "BattlefieldWG::GetSpiritGraveyardId: Unexpected Area Id %u", areaId); + break; + } + + return 0; +} + +void BattlefieldWG::OnCreatureCreate(Creature* creature) +{ + // Accessing to db spawned creatures + switch (creature->GetEntry()) + { + case NPC_DWARVEN_SPIRIT_GUIDE: + case NPC_TAUNKA_SPIRIT_GUIDE: + { + TeamId teamId = (creature->GetEntry() == NPC_DWARVEN_SPIRIT_GUIDE ? TEAM_ALLIANCE : TEAM_HORDE); + uint8 graveyardId = GetSpiritGraveyardId(creature->GetAreaId()); + if (m_GraveyardList[graveyardId]) + m_GraveyardList[graveyardId]->SetSpirit(creature, teamId); + break; + } + } + + // untested code - not sure if it is valid. + if (IsWarTime()) + { + switch (creature->GetEntry()) + { + case NPC_WINTERGRASP_SIEGE_ENGINE_ALLIANCE: + case NPC_WINTERGRASP_SIEGE_ENGINE_HORDE: + case NPC_WINTERGRASP_CATAPULT: + case NPC_WINTERGRASP_DEMOLISHER: + { + if (!creature->GetCreatorGUID() || !sObjectAccessor->FindPlayer(creature->GetCreatorGUID())) + { + creature->setDeathState(DEAD); + creature->RemoveFromWorld(); + return; + } + Player* creator = sObjectAccessor->FindPlayer(creature->GetCreatorGUID()); + TeamId team = creator->GetTeamId(); + + if (team == TEAM_HORDE) + { + if (GetData(BATTLEFIELD_WG_DATA_VEHICLE_H) < GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_H)) + { + UpdateData(BATTLEFIELD_WG_DATA_VEHICLE_H, 1); + creature->AddAura(SPELL_HORDE_FLAG, creature); + creature->setFaction(creator->getFaction()); + m_vehicles[team].insert(creature->GetGUID()); + UpdateVehicleCountWG(); + } + else + { + creature->setDeathState(DEAD); + creature->RemoveFromWorld(); + return; + } + } + else + { + if (GetData(BATTLEFIELD_WG_DATA_VEHICLE_A) < GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_A)) + { + UpdateData(BATTLEFIELD_WG_DATA_VEHICLE_A, 1); + creature->AddAura(SPELL_ALLIANCE_FLAG,creature); + creature->setFaction(creator->getFaction()); + m_vehicles[team].insert(creature->GetGUID()); + UpdateVehicleCountWG(); + } + else + { + creature->setDeathState(DEAD); + creature->RemoveFromWorld(); + return; + } + } + + creature->CastSpell(creator, SPELL_GRAB_PASSENGER, true); + break; + } + } + } +} + +void BattlefieldWG::OnCreatureRemove(Creature* /*creature*/) +{ +/* possibly can be used later + if (IsWarTime()) + { + switch (creature->GetEntry()) + { + case NPC_WINTERGRASP_SIEGE_ENGINE_ALLIANCE: + case NPC_WINTERGRASP_SIEGE_ENGINE_HORDE: + case NPC_WINTERGRASP_CATAPULT: + case NPC_WINTERGRASP_DEMOLISHER: + { + uint8 team; + if (creature->getFaction() == WintergraspFaction[TEAM_ALLIANCE]) + team = TEAM_ALLIANCE; + else if (creature->getFaction() == WintergraspFaction[TEAM_HORDE]) + team = TEAM_HORDE; + else + return; + + m_vehicles[team].erase(creature->GetGUID()); + if (team == TEAM_HORDE) + UpdateData(BATTLEFIELD_WG_DATA_VEHICLE_H, -1); + else + UpdateData(BATTLEFIELD_WG_DATA_VEHICLE_A, -1); + UpdateVehicleCountWG(); + + break; + } + } + }*/ +} + +void BattlefieldWG::OnGameObjectCreate(GameObject* go) +{ + bool isWorkshop = false; + uint8 workshopId = 0; + + switch (go->GetEntry()) + { + case GO_WINTERGRASP_FACTORY_BANNER_NE: + isWorkshop = true; + workshopId = BATTLEFIELD_WG_WORKSHOP_NE; + break; + case GO_WINTERGRASP_FACTORY_BANNER_NW: + isWorkshop = true; + workshopId = BATTLEFIELD_WG_WORKSHOP_NW; + break; + case GO_WINTERGRASP_FACTORY_BANNER_SE: + isWorkshop = true; + workshopId = BATTLEFIELD_WG_WORKSHOP_SE; + break; + case GO_WINTERGRASP_FACTORY_BANNER_SW: + isWorkshop = true; + workshopId = BATTLEFIELD_WG_WORKSHOP_SW; + break; + + } + + if (!isWorkshop) + return; + + for (Workshop::const_iterator itr = WorkshopsList.begin(); itr != WorkshopsList.end(); ++itr) + { + if (WGWorkshop* workshop = (*itr)) + { + if (workshop->workshopId == workshopId) + { + WintergraspCapturePoint* capturePoint = new WintergraspCapturePoint(this, GetAttackerTeam()); + + capturePoint->SetCapturePointData(go); + capturePoint->LinkToWorkshop(workshop); + AddCapturePoint(capturePoint); + break; + } + } + } +} + +// Called when player kill a unit in wg zone +void BattlefieldWG::HandleKill(Player* killer, Unit* victim) +{ + if (killer == victim) + return; + + bool again = false; + TeamId killerTeam = killer->GetTeamId(); + + if (victim->GetTypeId() == TYPEID_PLAYER) + { + for (GuidSet::const_iterator itr = m_PlayersInWar[killerTeam].begin(); itr != m_PlayersInWar[killerTeam].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + if (player->GetDistance2d(killer) < 40) + PromotePlayer(player); + return; + } + + for (GuidSet::const_iterator itr = KeepCreature[GetOtherTeam(killerTeam)].begin(); + itr != KeepCreature[GetOtherTeam(killerTeam)].end(); ++itr) + { + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + { + if (Creature* creature = unit->ToCreature()) + { + if (victim->GetEntry() == creature->GetEntry() && !again) + { + again = true; + for (GuidSet::const_iterator iter = m_PlayersInWar[killerTeam].begin(); iter != m_PlayersInWar[killerTeam].end(); ++iter) + if (Player* player = sObjectAccessor->FindPlayer(*iter)) + if (player->GetDistance2d(killer) < 40.0f) + PromotePlayer(player); + } + } + } + } + // TODO:Recent PvP activity worldstate +} + +bool BattlefieldWG::FindAndRemoveVehicleFromList(Unit* vehicle) +{ + for (uint32 itr = 0; itr < 2; ++itr) + { + if (m_vehicles[itr].find(vehicle->GetGUID()) != m_vehicles[itr].end()) + { + m_vehicles[itr].erase(vehicle->GetGUID()); + if (itr == WintergraspFaction[TEAM_HORDE]) + UpdateData(BATTLEFIELD_WG_DATA_VEHICLE_H,-1); + else + UpdateData(BATTLEFIELD_WG_DATA_VEHICLE_A,-1); + return true; + } + } + return false; +} + +void BattlefieldWG::OnUnitDeath(Unit* unit) +{ + if (IsWarTime()) + if (unit->IsVehicle()) + if (FindAndRemoveVehicleFromList(unit)) + UpdateVehicleCountWG(); +} + +// Update rank for player +void BattlefieldWG::PromotePlayer(Player* killer) +{ + if (!m_isActive) + return; + // Updating rank of player + if (Aura* aur = killer->GetAura(SPELL_RECRUIT)) + { + if (aur->GetStackAmount() >= 5) + { + killer->RemoveAura(SPELL_RECRUIT); + killer->CastSpell(killer, SPELL_CORPORAL, true); + SendWarningToPlayer(killer, BATTLEFIELD_WG_TEXT_FIRSTRANK); + } + else + killer->CastSpell(killer, SPELL_RECRUIT, true); + } + else if (Aura* aur = killer->GetAura(SPELL_CORPORAL)) + { + if (aur->GetStackAmount() >= 5) + { + killer->RemoveAura(SPELL_CORPORAL); + killer->CastSpell(killer, SPELL_LIEUTENANT, true); + SendWarningToPlayer(killer, BATTLEFIELD_WG_TEXT_SECONDRANK); + } + else + killer->CastSpell(killer, SPELL_CORPORAL, true); + } +} + +void BattlefieldWG::RemoveAurasFromPlayer(Player* player) +{ + player->RemoveAurasDueToSpell(SPELL_RECRUIT); + player->RemoveAurasDueToSpell(SPELL_CORPORAL); + player->RemoveAurasDueToSpell(SPELL_LIEUTENANT); + player->RemoveAurasDueToSpell(SPELL_TOWER_CONTROL); + player->RemoveAurasDueToSpell(SPELL_SPIRITUAL_IMMUNITY); + player->RemoveAurasDueToSpell(SPELL_TENACITY); + player->RemoveAurasDueToSpell(SPELL_ESSENCE_OF_WINTERGRASP); + player->RemoveAurasDueToSpell(SPELL_WINTERGRASP_RESTRICTED_FLIGHT_AREA); +} + +void BattlefieldWG::OnPlayerJoinWar(Player* player) +{ + RemoveAurasFromPlayer(player); + + player->CastSpell(player, SPELL_RECRUIT, true); + + if (player->GetZoneId() != m_ZoneId) + { + if (player->GetTeamId() == GetDefenderTeam()) + player->TeleportTo(571, 5345, 2842, 410, 3.14f); + else + { + if (player->GetTeamId() == TEAM_HORDE) + player->TeleportTo(571, 5025.857422f, 3674.628906f, 362.737122f, 4.135169f); + else + player->TeleportTo(571, 5101.284f, 2186.564f, 373.549f, 3.812f); + } + } + + UpdateTenacity(); + + if (player->GetTeamId() == GetAttackerTeam()) + { + if (GetData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT) < 3) + player->SetAuraStack(SPELL_TOWER_CONTROL, player, 3 - GetData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT)); + } + else + { + if (GetData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT) > 0) + player->SetAuraStack(SPELL_TOWER_CONTROL, player, GetData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT)); + } + SendInitWorldStatesTo(player); +} + +void BattlefieldWG::OnPlayerLeaveWar(Player* player) +{ + // Remove all aura from WG // TODO: false we can go out of this zone on retail and keep Rank buff, remove on end of WG + if (!player->GetSession()->PlayerLogout()) + { + if (player->GetVehicle()) // Remove vehicle of player if he go out. + player->GetVehicle()->Dismiss(); + RemoveAurasFromPlayer(player); + } + + player->RemoveAurasDueToSpell(SPELL_HORDE_CONTROLS_FACTORY_PHASE_SHIFT); + player->RemoveAurasDueToSpell(SPELL_ALLIANCE_CONTROLS_FACTORY_PHASE_SHIFT); + player->RemoveAurasDueToSpell(SPELL_HORDE_CONTROL_PHASE_SHIFT); + player->RemoveAurasDueToSpell(SPELL_ALLIANCE_CONTROL_PHASE_SHIFT); +} + +void BattlefieldWG::OnPlayerLeaveZone(Player* player) +{ + if (!m_isActive) + RemoveAurasFromPlayer(player); + + player->RemoveAurasDueToSpell(SPELL_HORDE_CONTROLS_FACTORY_PHASE_SHIFT); + player->RemoveAurasDueToSpell(SPELL_ALLIANCE_CONTROLS_FACTORY_PHASE_SHIFT); + player->RemoveAurasDueToSpell(SPELL_HORDE_CONTROL_PHASE_SHIFT); + player->RemoveAurasDueToSpell(SPELL_ALLIANCE_CONTROL_PHASE_SHIFT); +} + +void BattlefieldWG::OnPlayerEnterZone(Player* player) +{ + if (!m_isActive) + RemoveAurasFromPlayer(player); + + player->AddAura(m_DefenderTeam == TEAM_HORDE ? SPELL_HORDE_CONTROL_PHASE_SHIFT : SPELL_ALLIANCE_CONTROL_PHASE_SHIFT, player); + // Send worldstate to player + SendInitWorldStatesTo(player); +} + +uint32 BattlefieldWG::GetData(uint32 data) +{ + switch (data) + { + // Used to determine when the phasing spells must be casted + // See: SpellArea::IsFitToRequirements + case AREA_THE_SUNKEN_RING: + case AREA_THE_BROKEN_TEMPLATE: + case AREA_WESTPARK_WORKSHOP: + case AREA_EASTPARK_WORKSHOP: + // Graveyards and Workshops are controlled by the same team. + if (m_GraveyardList[GetSpiritGraveyardId(data)]) + return m_GraveyardList[GetSpiritGraveyardId(data)]->GetControlTeamId(); + } + + return Battlefield::GetData(data); +} + +// Method sending worldsate to player +WorldPacket BattlefieldWG::BuildInitWorldStates() +{ + WorldPacket data(SMSG_INIT_WORLD_STATES, (4 + 4 + 4 + 2 + (BuildingsInZone.size() * 8) + (WorkshopsList.size() * 8))); + + data << uint32(m_MapId); + data << uint32(m_ZoneId); + data << uint32(0); + data << uint16(4 + 2 + 4 + BuildingsInZone.size() + WorkshopsList.size()); + + data << uint32(BATTLEFIELD_WG_WORLD_STATE_ATTACKER) << uint32(GetAttackerTeam()); + data << uint32(BATTLEFIELD_WG_WORLD_STATE_DEFENDER) << uint32(GetDefenderTeam()); + data << uint32(BATTLEFIELD_WG_WORLD_STATE_ACTIVE) << uint32(IsWarTime()? 0 : 1); // Note: cleanup these two, their names look awkward + data << uint32(BATTLEFIELD_WG_WORLD_STATE_SHOW_WORLDSTATE) << uint32(IsWarTime()? 1 : 0); + + for (uint32 i = 0; i < 2; ++i) + data << ClockWorldState[i] << uint32(time(NULL) + (m_Timer / 1000)); + + data << uint32(BATTLEFIELD_WG_WORLD_STATE_VEHICLE_H) << uint32(GetData(BATTLEFIELD_WG_DATA_VEHICLE_H)); + data << uint32(BATTLEFIELD_WG_WORLD_STATE_MAX_VEHICLE_H) << GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_H); + data << uint32(BATTLEFIELD_WG_WORLD_STATE_VEHICLE_A) << uint32(GetData(BATTLEFIELD_WG_DATA_VEHICLE_A)); + data << uint32(BATTLEFIELD_WG_WORLD_STATE_MAX_VEHICLE_A) << GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_A); + + for (GameObjectBuilding::const_iterator itr = BuildingsInZone.begin(); itr != BuildingsInZone.end(); ++itr) + data << (*itr)->m_WorldState << (*itr)->m_State; + + for (Workshop::const_iterator itr = WorkshopsList.begin(); itr != WorkshopsList.end(); ++itr) + if (*itr) + data << WorkshopsData[(*itr)->workshopId].worldstate << (*itr)->state; + + return data; +} + +void BattlefieldWG::SendInitWorldStatesTo(Player* player) +{ + WorldPacket data = BuildInitWorldStates(); + player->GetSession()->SendPacket(&data); +} + +void BattlefieldWG::SendInitWorldStatesToAll() +{ + WorldPacket data = BuildInitWorldStates(); + for (uint8 team = 0; team < 2; team++) + for (GuidSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + player->GetSession()->SendPacket(&data); +} + +void BattlefieldWG::BrokenWallOrTower(TeamId /*team*/) +{ +// might be some use for this in the future. old code commented out below. KL +/* if (team == GetDefenderTeam()) + { + for (GuidSet::const_iterator itr = m_PlayersInWar[GetAttackerTeam()].begin(); itr != m_PlayersInWar[GetAttackerTeam()].end(); ++itr) + { + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + IncrementQuest(player, WGQuest[player->GetTeamId()][2], true); + } + }*/ +} + +// Called when a tower is broke +void BattlefieldWG::UpdatedDestroyedTowerCount(TeamId team) +{ + // Destroy an attack tower + if (team == GetAttackerTeam()) + { + // Update counter + UpdateData(BATTLEFIELD_WG_DATA_DAMAGED_TOWER_ATT, -1); + UpdateData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT, 1); + + // Remove buff stack on attackers + for (GuidSet::const_iterator itr = m_PlayersInWar[GetAttackerTeam()].begin(); itr != m_PlayersInWar[GetAttackerTeam()].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + player->RemoveAuraFromStack(SPELL_TOWER_CONTROL); + + // Add buff stack to defenders + for (GuidSet::const_iterator itr = m_PlayersInWar[GetDefenderTeam()].begin(); itr != m_PlayersInWar[GetDefenderTeam()].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + { + player->CastSpell(player, SPELL_TOWER_CONTROL, true); + DoCompleteOrIncrementAchievement(ACHIEVEMENTS_WG_TOWER_DESTROY, player); + } + + // If all three south towers are destroyed (ie. all attack towers), remove ten minutes from battle time + if (GetData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT) == 3) + { + if (int32(m_Timer - 600000) < 0) + m_Timer = 0; + else + m_Timer -= 600000; + SendInitWorldStatesToAll(); + } + } + else + { + UpdateData(BATTLEFIELD_WG_DATA_DAMAGED_TOWER_DEF, -1); + UpdateData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_DEF, 1); + } +} + +void BattlefieldWG::ProcessEvent(WorldObject *obj, uint32 eventId) +{ + if (!obj || !IsWarTime()) + return; + + // We handle only gameobjects here + GameObject* go = obj->ToGameObject(); + if (!go) + return; + + // On click on titan relic + if (go->GetEntry() == GO_WINTERGRASP_TITAN_S_RELIC) + { + if (CanInteractWithRelic()) + EndBattle(false); + else + GetRelic()->SetRespawnTime(RESPAWN_IMMEDIATELY); + } + + // if destroy or damage event, search the wall/tower and update worldstate/send warning message + for (GameObjectBuilding::const_iterator itr = BuildingsInZone.begin(); itr != BuildingsInZone.end(); ++itr) + { + if (go->GetEntry() == (*itr)->m_Build->GetEntry()) + { + if ((*itr)->m_Build->GetGOInfo()->building.damagedEvent == eventId) + (*itr)->Damaged(); + + if ((*itr)->m_Build->GetGOInfo()->building.destroyedEvent == eventId) + (*itr)->Destroyed(); + + break; + } + } +} + +// Called when a tower is damaged, used for honor reward calcul +void BattlefieldWG::UpdateDamagedTowerCount(TeamId team) +{ + if (team == GetAttackerTeam()) + UpdateData(BATTLEFIELD_WG_DATA_DAMAGED_TOWER_ATT, 1); + else + UpdateData(BATTLEFIELD_WG_DATA_DAMAGED_TOWER_DEF, 1); +} + +// Update vehicle count WorldState to player +void BattlefieldWG::UpdateVehicleCountWG() +{ + SendUpdateWorldState(BATTLEFIELD_WG_WORLD_STATE_VEHICLE_H, GetData(BATTLEFIELD_WG_DATA_VEHICLE_H)); + SendUpdateWorldState(BATTLEFIELD_WG_WORLD_STATE_MAX_VEHICLE_H, GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_H)); + SendUpdateWorldState(BATTLEFIELD_WG_WORLD_STATE_VEHICLE_A, GetData(BATTLEFIELD_WG_DATA_VEHICLE_A)); + SendUpdateWorldState(BATTLEFIELD_WG_WORLD_STATE_MAX_VEHICLE_A, GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_A)); +} + +void BattlefieldWG::UpdateTenacity() +{ + TeamId team = TEAM_NEUTRAL; + uint32 alliancePlayers = m_PlayersInWar[TEAM_ALLIANCE].size(); + uint32 hordePlayers = m_PlayersInWar[TEAM_HORDE].size(); + int32 newStack = 0; + + if (alliancePlayers && hordePlayers) + { + if (alliancePlayers < hordePlayers) + newStack = int32((float(hordePlayers / alliancePlayers) - 1) * 4); // positive, should cast on alliance + else if (alliancePlayers > hordePlayers) + newStack = int32((1 - float(alliancePlayers / hordePlayers)) * 4); // negative, should cast on horde + } + + if (newStack == int32(m_tenacityStack)) + return; + + if (m_tenacityStack > 0 && newStack <= 0) // old buff was on alliance + team = TEAM_ALLIANCE; + else if (newStack >= 0) // old buff was on horde + team = TEAM_HORDE; + + m_tenacityStack = newStack; + // Remove old buff + if (team != TEAM_NEUTRAL) + { + for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + if (player->getLevel() >= m_MinLevel) + player->RemoveAurasDueToSpell(SPELL_TENACITY); + + for (GuidSet::const_iterator itr = m_vehicles[team].begin(); itr != m_vehicles[team].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + creature->RemoveAurasDueToSpell(SPELL_TENACITY_VEHICLE); + } + + // Apply new buff + if (newStack) + { + team = newStack > 0 ? TEAM_ALLIANCE : TEAM_HORDE; + + if (newStack < 0) + newStack = -newStack; + if (newStack > 20) + newStack = 20; + + uint32 buff_honor = SPELL_GREATEST_HONOR; + if (newStack < 15) + buff_honor = SPELL_GREATER_HONOR; + if (newStack < 10) + buff_honor = SPELL_GREAT_HONOR; + if (newStack < 5) + buff_honor = 0; + + for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + player->SetAuraStack(SPELL_TENACITY, player, newStack); + + for (GuidSet::const_iterator itr = m_vehicles[team].begin(); itr != m_vehicles[team].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + creature->SetAuraStack(SPELL_TENACITY_VEHICLE, creature, newStack); + + if (buff_honor != 0) + { + for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr) + if (Player* player = sObjectAccessor->FindPlayer(*itr)) + player->CastSpell(player, buff_honor, true); + for (GuidSet::const_iterator itr = m_vehicles[team].begin(); itr != m_vehicles[team].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + creature->CastSpell(creature, buff_honor, true); + } + } +} + +WintergraspCapturePoint::WintergraspCapturePoint(BattlefieldWG* battlefield, TeamId teamInControl) : BfCapturePoint(battlefield) +{ + m_Bf = battlefield; + m_team = teamInControl; +} + +void WintergraspCapturePoint::ChangeTeam(TeamId /*oldTeam*/) +{ + m_Workshop->GiveControlTo(m_team, false); +} + +BfGraveyardWG::BfGraveyardWG(BattlefieldWG* battlefield) : BfGraveyard(battlefield) +{ + m_Bf = battlefield; +} diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.h b/src/server/game/Battlefield/Zones/BattlefieldWG.h new file mode 100644 index 00000000000..738d82135d2 --- /dev/null +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.h @@ -0,0 +1,1759 @@ +/* + * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef BATTLEFIELD_WG_ +#define BATTLEFIELD_WG_ + +#include "ObjectAccessor.h" +#include "WorldPacket.h" +#include "World.h" +#include "Group.h" +#include "GroupMgr.h" +#include "Battlefield.h" + +const uint32 VehNumWorldState[2] = { 3680, 3490 }; +const uint32 MaxVehNumWorldState[2] = { 3681, 3491 }; +const uint32 ClockWorldState[2] = { 3781, 4354 }; +const uint32 WintergraspFaction[3] = { 1, 2, 35 }; +const float WintergraspStalkerPos[4] = { 0, 0, 0, 0 }; + +class BattlefieldWG; +class WintergraspCapturePoint; + +struct BfWGGameObjectBuilding; +struct WGWorkshop; + +typedef std::set<GameObject*> GameObjectSet; +typedef std::set<BfWGGameObjectBuilding*> GameObjectBuilding; +typedef std::set<WGWorkshop*> Workshop; +typedef std::set<Group*> GroupSet; +//typedef std::set<WintergraspCapturePoint *> CapturePointSet; unused ? + +enum WintergraspSpells +{ + // Wartime auras + SPELL_RECRUIT = 37795, + SPELL_CORPORAL = 33280, + SPELL_LIEUTENANT = 55629, + SPELL_TENACITY = 58549, + SPELL_TENACITY_VEHICLE = 59911, + SPELL_TOWER_CONTROL = 62064, + SPELL_SPIRITUAL_IMMUNITY = 58729, + SPELL_GREAT_HONOR = 58555, + SPELL_GREATER_HONOR = 58556, + SPELL_GREATEST_HONOR = 58557, + SPELL_ALLIANCE_FLAG = 14268, + SPELL_HORDE_FLAG = 14267, + SPELL_GRAB_PASSENGER = 61178, + + // Reward spells + SPELL_VICTORY_REWARD = 56902, + SPELL_DEFEAT_REWARD = 58494, + SPELL_DAMAGED_TOWER = 59135, + SPELL_DESTROYED_TOWER = 59136, + SPELL_DAMAGED_BUILDING = 59201, + SPELL_INTACT_BUILDING = 59203, + + SPELL_TELEPORT_BRIDGE = 59096, + SPELL_TELEPORT_FORTRESS = 60035, + + SPELL_TELEPORT_DALARAN = 53360, + SPELL_VICTORY_AURA = 60044, + + // Other spells + SPELL_WINTERGRASP_WATER = 36444, + SPELL_ESSENCE_OF_WINTERGRASP = 58045, + SPELL_WINTERGRASP_RESTRICTED_FLIGHT_AREA = 58730, + + // Phasing spells + SPELL_HORDE_CONTROLS_FACTORY_PHASE_SHIFT = 56618,// ADDS PHASE 16 + SPELL_ALLIANCE_CONTROLS_FACTORY_PHASE_SHIFT = 56617,// ADDS PHASE 32 + + SPELL_HORDE_CONTROL_PHASE_SHIFT = 55773,// ADDS PHASE 64 + SPELL_ALLIANCE_CONTROL_PHASE_SHIFT = 55774,// ADDS PHASE 128 +}; + +enum WintergraspData +{ + BATTLEFIELD_WG_DATA_DAMAGED_TOWER_DEF, + BATTLEFIELD_WG_DATA_BROKEN_TOWER_DEF, + BATTLEFIELD_WG_DATA_DAMAGED_TOWER_ATT, + BATTLEFIELD_WG_DATA_BROKEN_TOWER_ATT, + BATTLEFIELD_WG_DATA_MAX_VEHICLE_A, + BATTLEFIELD_WG_DATA_MAX_VEHICLE_H, + BATTLEFIELD_WG_DATA_VEHICLE_A, + BATTLEFIELD_WG_DATA_VEHICLE_H, + BATTLEFIELD_WG_DATA_MAX, +}; + +enum WintergraspAchievements +{ + ACHIEVEMENTS_WIN_WG = 1717, + ACHIEVEMENTS_WIN_WG_100 = 1718, // todo + ACHIEVEMENTS_WG_GNOMESLAUGHTER = 1723, // todo + ACHIEVEMENTS_WG_TOWER_DESTROY = 1727, + ACHIEVEMENTS_DESTRUCTION_DERBY_A = 1737, // todo + ACHIEVEMENTS_WG_TOWER_CANNON_KILL = 1751, // todo + ACHIEVEMENTS_WG_MASTER_A = 1752, // todo + ACHIEVEMENTS_WIN_WG_TIMER_10 = 1755, + ACHIEVEMENTS_STONE_KEEPER_50 = 2085, // todo + ACHIEVEMENTS_STONE_KEEPER_100 = 2086, // todo + ACHIEVEMENTS_STONE_KEEPER_250 = 2087, // todo + ACHIEVEMENTS_STONE_KEEPER_500 = 2088, // todo + ACHIEVEMENTS_STONE_KEEPER_1000 = 2089, // todo + ACHIEVEMENTS_WG_RANGER = 2199, // todo + ACHIEVEMENTS_DESTRUCTION_DERBY_H = 2476, // todo + ACHIEVEMENTS_WG_MASTER_H = 2776, // todo +}; + +enum WintergraspWorldStates +{ + BATTLEFIELD_WG_WORLD_STATE_VEHICLE_H = 3490, + BATTLEFIELD_WG_WORLD_STATE_MAX_VEHICLE_H = 3491, + BATTLEFIELD_WG_WORLD_STATE_VEHICLE_A = 3680, + BATTLEFIELD_WG_WORLD_STATE_MAX_VEHICLE_A = 3681, + BATTLEFIELD_WG_WORLD_STATE_ACTIVE = 3801, + BATTLEFIELD_WG_WORLD_STATE_DEFENDER = 3802, + BATTLEFIELD_WG_WORLD_STATE_ATTACKER = 3803, + BATTLEFIELD_WG_WORLD_STATE_SHOW_WORLDSTATE = 3710, +}; + +enum WintergraspAreaIds +{ + AREA_WINTERGRASP_FORTRESS = 4575, + AREA_THE_SUNKEN_RING = 4538, + AREA_THE_BROKEN_TEMPLATE = 4539, + AREA_WESTPARK_WORKSHOP = 4611, + AREA_EASTPARK_WORKSHOP = 4612, + AREA_WINTERGRASP = 4197, + AREA_THE_CHILLED_QUAGMIRE = 4589, +}; + +/*######################### + *####### Graveyards ###### + *#########################*/ + +class BfGraveyardWG : public BfGraveyard +{ + public: + BfGraveyardWG(BattlefieldWG *Bf); + + void SetTextId(uint32 textid) { m_GossipTextId = textid; } + uint32 GetTextId() { return m_GossipTextId; } + protected: + uint32 m_GossipTextId; +}; + +enum WGGraveyardId +{ + BATTLEFIELD_WG_GY_WORKSHOP_NE, + BATTLEFIELD_WG_GY_WORKSHOP_NW, + BATTLEFIELD_WG_GY_WORKSHOP_SE, + BATTLEFIELD_WG_GY_WORKSHOP_SW, + BATTLEFIELD_WG_GY_KEEP, + BATTLEFIELD_WG_GY_HORDE, + BATTLEFIELD_WG_GY_ALLIANCE, + BATTLEFIELD_WG_GRAVEYARD_MAX, +}; + +enum eWGGossipText +{ + BATTLEFIELD_WG_GOSSIPTEXT_GY_NE = -1850501, + BATTLEFIELD_WG_GOSSIPTEXT_GY_NW = -1850502, + BATTLEFIELD_WG_GOSSIPTEXT_GY_SE = -1850504, + BATTLEFIELD_WG_GOSSIPTEXT_GY_SW = -1850503, + BATTLEFIELD_WG_GOSSIPTEXT_GY_KEEP = -1850500, + BATTLEFIELD_WG_GOSSIPTEXT_GY_HORDE = -1850505, + BATTLEFIELD_WG_GOSSIPTEXT_GY_ALLIANCE = -1850506, +}; + +enum WintergraspNpcs +{ + BATTLEFIELD_WG_NPC_GUARD_H = 30739, + BATTLEFIELD_WG_NPC_GUARD_A = 30740, + BATTLEFIELD_WG_NPC_STALKER = 15214, + + BATTLEFIELD_WG_NPC_VIERON_BLAZEFEATHER = 31102, + BATTLEFIELD_WG_NPC_STONE_GUARD_MUKAR = 32296,// <WINTERGRASP QUARTERMASTER> + BATTLEFIELD_WG_NPC_HOODOO_MASTER_FU_JIN = 31101,// <MASTER HEXXER> + BATTLEFIELD_WG_NPC_CHAMPION_ROS_SLAI = 39173,// <WINTERGRASP QUARTERMASTER> + BATTLEFIELD_WG_NPC_COMMANDER_DARDOSH = 31091, + BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_KILRATH = 31151, + BATTLEFIELD_WG_NPC_SIEGESMITH_STRONGHOOF = 31106, + BATTLEFIELD_WG_NPC_PRIMALIST_MULFORT = 31053, + BATTLEFIELD_WG_NPC_LIEUTENANT_MURP = 31107, + + BATTLEFIELD_WG_NPC_BOWYER_RANDOLPH = 31052, + BATTLEFIELD_WG_NPC_KNIGHT_DAMERON = 32294,// <WINTERGRASP QUARTERMASTER> + BATTLEFIELD_WG_NPC_SORCERESS_KAYLANA = 31051,// <ENCHANTRESS> + BATTLEFIELD_WG_NPC_MARSHAL_MAGRUDER = 39172,// <WINTERGRASP QUARTERMASTER> + BATTLEFIELD_WG_NPC_COMMANDER_ZANNETH = 31036, + BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_AHBRAMIS = 31153, + BATTLEFIELD_WG_NPC_SIEGE_MASTER_STOUTHANDLE = 31108, + BATTLEFIELD_WG_NPC_ANCHORITE_TESSA = 31054, + BATTLEFIELD_WG_NPC_SENIOR_DEMOLITIONIST_LEGOSO = 31109, + + NPC_TAUNKA_SPIRIT_GUIDE = 31841, // Horde spirit guide for Wintergrasp + NPC_DWARVEN_SPIRIT_GUIDE = 31842, // Alliance spirit guide for Wintergrasp + NPC_TOWER_CANNON = 28366, + + NPC_WINTERGRASP_SIEGE_ENGINE_ALLIANCE = 28312, + NPC_WINTERGRASP_SIEGE_ENGINE_HORDE = 32627, + NPC_WINTERGRASP_CATAPULT = 27881, + NPC_WINTERGRASP_DEMOLISHER = 28094, + NPC_WINTERGRASP_TOWER_CANNON = 28366, +}; + +struct BfWGCoordGY +{ + float x; + float y; + float z; + float o; + uint32 gyid; + uint8 type; + uint32 textid; // for gossip menu + TeamId startcontrol; +}; + +const uint32 WGQuest[2][6] = { + { 13186, 13181, 13222, 13538, 13177, 13179 }, + { 13185, 13183, 13223, 13539, 13178, 13180 }, +}; +// 7 in sql, 7 in header +const BfWGCoordGY WGGraveYard[BATTLEFIELD_WG_GRAVEYARD_MAX] = { + { 5104.750f, 2300.940f, 368.579f, 0.733038f, 1329, BATTLEFIELD_WG_GY_WORKSHOP_NE, BATTLEFIELD_WG_GOSSIPTEXT_GY_NE, TEAM_NEUTRAL }, + { 5099.120f, 3466.036f, 368.484f, 5.317802f, 1330, BATTLEFIELD_WG_GY_WORKSHOP_NW, BATTLEFIELD_WG_GOSSIPTEXT_GY_NW, TEAM_NEUTRAL }, + { 4314.648f, 2408.522f, 392.642f, 6.268125f, 1333, BATTLEFIELD_WG_GY_WORKSHOP_SE, BATTLEFIELD_WG_GOSSIPTEXT_GY_SE, TEAM_NEUTRAL }, + { 4331.716f, 3235.695f, 390.251f, 0.008500f, 1334, BATTLEFIELD_WG_GY_WORKSHOP_SW, BATTLEFIELD_WG_GOSSIPTEXT_GY_SW, TEAM_NEUTRAL }, + { 5537.986f, 2897.493f, 517.057f, 4.819249f, 1285, BATTLEFIELD_WG_GY_KEEP, BATTLEFIELD_WG_GOSSIPTEXT_GY_KEEP, TEAM_NEUTRAL }, + { 5032.454f, 3711.382f, 372.468f, 3.971623f, 1331, BATTLEFIELD_WG_GY_HORDE, BATTLEFIELD_WG_GOSSIPTEXT_GY_HORDE, TEAM_HORDE }, + { 5140.790f, 2179.120f, 390.950f, 1.972220f, 1332, BATTLEFIELD_WG_GY_ALLIANCE, BATTLEFIELD_WG_GOSSIPTEXT_GY_ALLIANCE, TEAM_ALLIANCE }, +}; + +/* ######################### * + * WintergraspCapturePoint * + * ######################### */ + +class WintergraspCapturePoint : public BfCapturePoint +{ + public: + WintergraspCapturePoint(BattlefieldWG* battlefield, TeamId teamInControl); + + void LinkToWorkshop(WGWorkshop* workshop) { m_Workshop = workshop; } + + void ChangeTeam(TeamId oldteam); + TeamId GetTeam() const { return m_team; } + + protected: + WGWorkshop* m_Workshop; +}; + +/* ######################### * + * WinterGrasp Battlefield * + * ######################### */ + +class BattlefieldWG : public Battlefield +{ + public: + /** + * \brief Called when the battle start + * - Spawn relic and turret + * - Rebuild tower and wall + * - Invite player to war + */ + void OnBattleStart(); + + /** + * \brief Called when battle end + * - Remove relic and turret + * - Change banner/npc in keep if it needed + * - Saving battlestate + * - Reward honor/mark to player + * - Remove vehicle + * \param endByTimer : true if battle ended when timer is at 00:00, false if battle ended by clicking on relic + */ + void OnBattleEnd(bool endByTimer); + + /** + * \brief Called when grouping starts (15 minutes before battlestart) + * - Invite all player in zone to join queue + */ + void OnStartGrouping(); + + /** + * \brief Called when player accept invite to join battle + * - Update aura + * - Teleport if it needed + * - Update worldstate + * - Update tenacity + * \param player: Player who accepted invite + */ + void OnPlayerJoinWar(Player* player); + + /** + * \brief Called when player left the battle + * - Update player aura + * \param player : Player who left the battle + */ + void OnPlayerLeaveWar(Player* player); + + /** + * \brief Called when player left the WG zone + * \param player : Player who left the zone + */ + void OnPlayerLeaveZone(Player* player); + + /** + * \brief Called when player enters in WG zone + * - Update aura + * - Update worldstate + * \param player : Player who enters the zone + */ + void OnPlayerEnterZone(Player* player); + + /** + * \brief Called for update battlefield data + * - Save battle timer in database every minutes + * - Update imunity aura from graveyard + * \param diff : time elapsed since the last call (in ms) + */ + bool Update(uint32 diff); + + /** + * \brief Called when a creature is created + * - Update vehicle count + */ + void OnCreatureCreate(Creature* creature); + + /** + * \brief Called when a creature is removed + * - Update vehicle count + */ + void OnCreatureRemove(Creature* creature); + + /** + * \brief Called when a gameobject is created + */ + void OnGameObjectCreate(GameObject* go); + + /** + * \brief Called when a wall/tower is broken + * - Update quest + */ + void BrokenWallOrTower(TeamId team); + + /** + * \brief Called when a tower is damaged + * - Update tower count (for reward calcul) + */ + void UpdateDamagedTowerCount(TeamId team); + + /** + * \brief Called when tower is broken + * - Update tower buff + * - check if three south tower is down for remove 10 minutes to wg + */ + void UpdatedDestroyedTowerCount(TeamId team); + + void DoCompleteOrIncrementAchievement(uint32 achievement, Player* player, uint8 incrementNumber = 1); + + void RemoveAurasFromPlayer(Player* player); + + /** + * \brief Called when battlefield is setup, at server start + */ + bool SetupBattlefield(); + + /// Return pointer to relic object + GameObject* GetRelic() { return m_titansRelic; } + + /// Define relic object + void SetRelic(GameObject* relic) { m_titansRelic = relic; } + + /// Check if players can interact with the relic (Only if the last door has been broken) + bool CanInteractWithRelic() { return m_isRelicInteractible; } + + /// Define if player can interact with the relic + void SetRelicInteractible(bool allow) { m_isRelicInteractible = allow; } + + void UpdateVehicleCountWG(); + void UpdateCounterVehicle(bool init); + + WorldPacket BuildInitWorldStates(); + void SendInitWorldStatesTo(Player* player); + void SendInitWorldStatesToAll(); + + void HandleKill(Player* killer, Unit* victim); + void OnUnitDeath(Unit* unit); + void PromotePlayer(Player* killer); + + void UpdateTenacity(); + void ProcessEvent(WorldObject *obj, uint32 eventId); + + bool FindAndRemoveVehicleFromList(Unit* vehicle); + + // returns the graveyardId in the specified area. + uint8 GetSpiritGraveyardId(uint32 areaId); + + uint32 GetData(uint32 data); + protected: + bool m_isRelicInteractible; + + Workshop WorkshopsList; + + GameObjectSet DefenderPortalList; + GameObjectSet m_KeepGameObject[2]; + GameObjectBuilding BuildingsInZone; + + GuidSet m_vehicles[2]; + GuidSet CanonList; + GuidSet KeepCreature[2]; + GuidSet OutsideCreature[2]; + + uint32 m_tenacityStack; + uint32 m_saveTimer; + + GameObject* m_titansRelic; +}; + +const uint32 NORTHREND_WINTERGRASP = 4197; +const uint8 WG_MAX_OBJ = 32; +const uint8 WG_KEEPGAMEOBJECT_MAX = 44; +const uint8 WG_MAX_TURRET = 15; +const uint8 WG_MAX_KEEP_NPC = 39; +const uint8 WG_MAX_OUTSIDE_NPC = 14; +const uint8 WG_OUTSIDE_ALLIANCE_NPC = 7; +const uint8 WG_MAX_TELEPORTER = 12; + +enum WintergraspGameObjectBuildingType +{ + BATTLEFIELD_WG_OBJECTTYPE_DOOR, + BATTLEFIELD_WG_OBJECTTYPE_TITANRELIC, + BATTLEFIELD_WG_OBJECTTYPE_WALL, + BATTLEFIELD_WG_OBJECTTYPE_DOOR_LAST, + BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER, + BATTLEFIELD_WG_OBJECTTYPE_TOWER, +}; + +enum WintergraspGameObjectState +{ + BATTLEFIELD_WG_OBJECTSTATE_NONE, + BATTLEFIELD_WG_OBJECTSTATE_NEUTRAL_INTACT, + BATTLEFIELD_WG_OBJECTSTATE_NEUTRAL_DAMAGE, + BATTLEFIELD_WG_OBJECTSTATE_NEUTRAL_DESTROY, + BATTLEFIELD_WG_OBJECTSTATE_HORDE_INTACT, + BATTLEFIELD_WG_OBJECTSTATE_HORDE_DAMAGE, + BATTLEFIELD_WG_OBJECTSTATE_HORDE_DESTROY, + BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT, + BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DAMAGE, + BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DESTROY, +}; + +enum WintergraspWorkshopIds +{ + BATTLEFIELD_WG_WORKSHOP_NE, + BATTLEFIELD_WG_WORKSHOP_NW, + BATTLEFIELD_WG_WORKSHOP_SE, + BATTLEFIELD_WG_WORKSHOP_SW, + BATTLEFIELD_WG_WORKSHOP_KEEP_WEST, + BATTLEFIELD_WG_WORKSHOP_KEEP_EAST, +}; + +enum WintergraspWorldstates +{ + WORLDSTATE_WORKSHOP_NE = 3701, + WORLDSTATE_WORKSHOP_NW = 3700, + WORLDSTATE_WORKSHOP_SE = 3703, + WORLDSTATE_WORKSHOP_SW = 3702, + WORLDSTATE_WORKSHOP_K_W = 3698, + WORLDSTATE_WORKSHOP_K_E = 3699 +}; + +enum eWGTeamControl +{ + BATTLEFIELD_WG_TEAM_ALLIANCE, + BATTLEFIELD_WG_TEAM_HORDE, + BATTLEFIELD_WG_TEAM_NEUTRAL, +}; + +// TODO: Handle this with creature_text ? +enum eWGText +{ + BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_NE = 12055, + BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_NW = 12052, + BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_SE = 12053, + BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_SW = 12054, + BATTLEFIELD_WG_TEXT_WORKSHOP_ATTACK = 12051, + BATTLEFIELD_WG_TEXT_WORKSHOP_TAKEN = 12050, + BATTLEFIELD_WG_TEXT_ALLIANCE = 12057, + BATTLEFIELD_WG_TEXT_HORDE = 12056, + BATTLEFIELD_WG_TEXT_WILL_START = 12058, + BATTLEFIELD_WG_TEXT_START = 12067, + BATTLEFIELD_WG_TEXT_FIRSTRANK = 12059, + BATTLEFIELD_WG_TEXT_SECONDRANK = 12060, + BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_NE = 12062, + BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_NW = 12064, + BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_SE = 12061, + BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_SW = 12063, + BATTLEFIELD_WG_TEXT_TOWER_DAMAGE = 12065, + BATTLEFIELD_WG_TEXT_TOWER_DESTROY = 12066, + BATTLEFIELD_WG_TEXT_TOWER_NAME_S = 12069, + BATTLEFIELD_WG_TEXT_TOWER_NAME_E = 12070, + BATTLEFIELD_WG_TEXT_TOWER_NAME_W = 12071, + BATTLEFIELD_WG_TEXT_DEFEND_KEEP = 12068, + BATTLEFIELD_WG_TEXT_WIN_KEEP = 12072, +}; + +enum WintergraspGameObject +{ + GO_WINTERGRASP_FACTORY_BANNER_NE = 190475, + GO_WINTERGRASP_FACTORY_BANNER_NW = 190487, + GO_WINTERGRASP_FACTORY_BANNER_SE = 194959, + GO_WINTERGRASP_FACTORY_BANNER_SW = 194962, + + GO_WINTERGRASP_TITAN_S_RELIC = 192829, + + GO_WINTERGRASP_FORTRESS_TOWER_1 = 190221, + GO_WINTERGRASP_FORTRESS_TOWER_2 = 190373, + GO_WINTERGRASP_FORTRESS_TOWER_3 = 190377, + GO_WINTERGRASP_FORTRESS_TOWER_4 = 190378, + + GO_WINTERGRASP_SHADOWSIGHT_TOWER = 190356, + GO_WINTERGRASP_WINTER_S_EDGE_TOWER = 190357, + GO_WINTERGRASP_FLAMEWATCH_TOWER = 190358, +}; + +struct WintergraspObjectPositionData +{ + float x; + float y; + float z; + float o; + uint32 entryHorde; + uint32 entryAlliance; +}; + +// ***************************************************** +// ************ Destructible (Wall,Tower..) ************ +// ***************************************************** + +struct WintergraspBuildingSpawnData +{ + uint32 entry; + uint32 WorldState; + float x; + float y; + float z; + float o; + uint32 type; + uint32 nameId; +}; + +const WintergraspBuildingSpawnData WGGameObjectBuilding[WG_MAX_OBJ] = { + // Wall (Not spawned in db) + // Entry WS X Y Z O type NameID + { 190219, 3749, 5371.46f, 3047.47f, 407.571f, 3.14159f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 190220, 3750, 5331.26f, 3047.1f, 407.923f, 0.052359f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191795, 3764, 5385.84f, 2909.49f, 409.713f, 0.00872f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191796, 3772, 5384.45f, 2771.84f, 410.27f, 3.14159f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191799, 3762, 5371.44f, 2630.61f, 408.816f, 3.13286f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191800, 3766, 5301.84f, 2909.09f, 409.866f, 0.008724f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191801, 3770, 5301.06f, 2771.41f, 409.901f, 3.14159f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191802, 3751, 5280.2f, 2995.58f, 408.825f, 1.61443f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191803, 3752, 5279.14f, 2956.02f, 408.604f, 1.5708f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191804, 3767, 5278.69f, 2882.51f, 409.539f, 1.5708f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191806, 3769, 5279.5f, 2798.94f, 409.998f, 1.5708f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191807, 3759, 5279.94f, 2724.77f, 409.945f, 1.56207f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191808, 3760, 5279.6f, 2683.79f, 409.849f, 1.55334f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191809, 3761, 5330.96f, 2630.78f, 409.283f, 3.13286f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 190369, 3753, 5256.08f, 2933.96f, 409.357f, 3.13286f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 190370, 3758, 5257.46f, 2747.33f, 409.743f, -3.13286f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 190371, 3754, 5214.96f, 2934.09f, 409.19f, -0.008724f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 190372, 3757, 5215.82f, 2747.57f, 409.188f, -3.13286f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 190374, 3755, 5162.27f, 2883.04f, 410.256f, 1.57952f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 190376, 3756, 5163.72f, 2799.84f, 409.227f, 1.57952f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + + // Tower of keep (Not spawned in db) + { 190221, 3711, 5281.15f, 3044.59f, 407.843f, 3.11539f, BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER, BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_NW }, + { 190373, 3713, 5163.76f, 2932.23f, 409.19f, 3.12412f, BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER, BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_SW }, + { 190377, 3714, 5166.4f, 2748.37f, 409.188f, -1.5708f, BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER, BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_SE }, + { 190378, 3712, 5281.19f, 2632.48f, 409.099f, -1.58825f, BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER, BATTLEFIELD_WG_TEXT_KEEPTOWER_NAME_NE }, + + // Wall (with passage) (Not spawned in db) + { 191797, 3765, 5343.29f, 2908.86f, 409.576f, 0.008724f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191798, 3771, 5342.72f, 2771.39f, 409.625f, 3.14159f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + { 191805, 3768, 5279.13f, 2840.8f, 409.783f, 1.57952f, BATTLEFIELD_WG_OBJECTTYPE_WALL, 0 }, + + // South tower (Not spawned in db) + { 190356, 3704, 4557.17f, 3623.94f, 395.883f, 1.67552f, BATTLEFIELD_WG_OBJECTTYPE_TOWER, BATTLEFIELD_WG_TEXT_TOWER_NAME_W }, + { 190357, 3705, 4398.17f, 2822.5f, 405.627f, -3.12412f, BATTLEFIELD_WG_OBJECTTYPE_TOWER, BATTLEFIELD_WG_TEXT_TOWER_NAME_S }, + { 190358, 3706, 4459.1f, 1944.33f, 434.991f, -2.00276f, BATTLEFIELD_WG_OBJECTTYPE_TOWER, BATTLEFIELD_WG_TEXT_TOWER_NAME_E }, + + // Door of forteress (Not spawned in db) + { 190375, 3763, 5162.99f, 2841.23f, 410.162f, -3.13286f, BATTLEFIELD_WG_OBJECTTYPE_DOOR, 0 }, + + // Last door (Not spawned in db) + { 191810, 3773, 5397.11f, 2841.54f, 425.899f, 3.14159f, BATTLEFIELD_WG_OBJECTTYPE_DOOR_LAST, 0 }, +}; + + +// ********************************************************* +// **********Keep Element(GameObject,Creature)************** +// ********************************************************* + +// Keep gameobject +// 192488 : 10 in sql, 19 in header +// 192501 : 12 in sql, 17 in header +// 192416 : 1 in sql, 33 in header +// 192374 : 1 in sql, 1 in header +// 192375 : 1 in sql, 1 in header +// 192336 : 1 in sql, 1 in header +// 192255 : 1 in sql, 1 in header +// 192269 : 1 in sql, 7 in header +// 192254 : 1 in sql, 1 in header +// 192349 : 1 in sql, 1 in header +// 192366 : 1 in sql, 3 in header +// 192367 : 1 in sql, 1 in header +// 192364 : 1 in sql, 1 in header +// 192370 : 1 in sql, 1 in header +// 192369 : 1 in sql, 1 in header +// 192368 : 1 in sql, 1 in header +// 192362 : 1 in sql, 1 in header +// 192363 : 1 in sql, 1 in header +// 192379 : 1 in sql, 1 in header +// 192378 : 1 in sql, 1 in header +// 192355 : 1 in sql, 1 in header +// 192354 : 1 in sql, 1 in header +// 192358 : 1 in sql, 1 in header +// 192359 : 1 in sql, 1 in header +// 192338 : 1 in sql, 1 in header +// 192339 : 1 in sql, 1 in header +// 192284 : 1 in sql, 1 in header +// 192285 : 1 in sql, 1 in header +// 192371 : 1 in sql, 1 in header +// 192372 : 1 in sql, 1 in header +// 192373 : 1 in sql, 1 in header +// 192360 : 1 in sql, 1 in header +// 192361 : 1 in sql, 1 in header +// 192356 : 1 in sql, 1 in header +// 192352 : 1 in sql, 1 in header +// 192353 : 1 in sql, 1 in header +// 192357 : 1 in sql, 1 in header +// 192350 : 1 in sql, 1 in header +// 192351 : 1 in sql, 1 in header +const WintergraspObjectPositionData WGKeepGameObject[WG_KEEPGAMEOBJECT_MAX] = { + { 5262.540039f, 3047.949951f, 432.054993f, 3.106650f, 192488, 192501 }, // Flag on tower + { 5272.939941f, 2976.550049f, 444.492004f, 3.124120f, 192374, 192416 }, // Flag on Wall Intersect + { 5235.189941f, 2941.899902f, 444.278015f, 1.588250f, 192375, 192416 }, // Flag on Wall Intersect + { 5163.129883f, 2952.590088f, 433.502991f, 1.535890f, 192488, 192501 }, // Flag on tower + { 5145.109863f, 2935.000000f, 433.385986f, 3.141590f, 192488, 192501 }, // Flag on tower + { 5158.810059f, 2883.129883f, 431.618011f, 3.141590f, 192488, 192416 }, // Flag on wall + { 5154.490234f, 2862.149902f, 445.011993f, 3.141590f, 192336, 192416 }, // Flag on Wall Intersect + { 5154.520020f, 2853.310059f, 409.183014f, 3.141590f, 192255, 192269 }, // Flag on the floor + { 5154.459961f, 2828.939941f, 409.188995f, 3.141590f, 192254, 192269 }, // Flag on the floor + { 5155.310059f, 2820.739990f, 444.979004f, -3.13286f, 192349, 192416 }, // Flag on wall intersect + { 5160.339844f, 2798.610107f, 430.769012f, 3.141590f, 192488, 192416 }, // Flag on wall + { 5146.040039f, 2747.209961f, 433.584015f, 3.071770f, 192488, 192501 }, // Flag on tower + { 5163.779785f, 2729.679932f, 433.394012f, -1.58825f, 192488, 192501 }, // Flag on tower + { 5236.270020f, 2739.459961f, 444.992004f, -1.59698f, 192366, 192416 }, // Flag on wall intersect + { 5271.799805f, 2704.870117f, 445.183014f, -3.13286f, 192367, 192416 }, // Flag on wall intersect + { 5260.819824f, 2631.800049f, 433.324005f, 3.054330f, 192488, 192501 }, // Flag on tower + { 5278.379883f, 2613.830078f, 433.408997f, -1.58825f, 192488, 192501 }, // Flag on tower + { 5350.879883f, 2622.719971f, 444.686005f, -1.57080f, 192364, 192416 }, // Flag on wall intersect + { 5392.270020f, 2639.739990f, 435.330994f, 1.509710f, 192370, 192416 }, // Flag on wall intersect + { 5350.950195f, 2640.360107f, 435.407990f, 1.570800f, 192369, 192416 }, // Flag on wall intersect + { 5289.459961f, 2704.679932f, 435.875000f, -0.01745f, 192368, 192416 }, // Flag on wall intersect + { 5322.120117f, 2763.610107f, 444.973999f, -1.55334f, 192362, 192416 }, // Flag on wall intersect + { 5363.609863f, 2763.389893f, 445.023987f, -1.54462f, 192363, 192416 }, // Flag on wall intersect + { 5363.419922f, 2781.030029f, 435.763000f, 1.570800f, 192379, 192416 }, // Flag on wall intersect + { 5322.020020f, 2781.129883f, 435.811005f, 1.570800f, 192378, 192416 }, // Flag on wall intersect + { 5288.919922f, 2820.219971f, 435.721008f, 0.017452f, 192355, 192416 }, // Flag on wall intersect + { 5288.410156f, 2861.790039f, 435.721008f, 0.017452f, 192354, 192416 }, // Flag on wall intersect + { 5322.229980f, 2899.429932f, 435.808014f, -1.58825f, 192358, 192416 }, // Flag on wall intersect + { 5364.350098f, 2899.399902f, 435.838989f, -1.57080f, 192359, 192416 }, // Flag on wall intersect + { 5397.759766f, 2873.080078f, 455.460999f, 3.106650f, 192338, 192416 }, // Flag on keep + { 5397.390137f, 2809.330078f, 455.343994f, 3.106650f, 192339, 192416 }, // Flag on keep + { 5372.479980f, 2862.500000f, 409.049011f, 3.141590f, 192284, 192269 }, // Flag on floor + { 5371.490234f, 2820.800049f, 409.177002f, 3.141590f, 192285, 192269 }, // Flag on floor + { 5364.290039f, 2916.939941f, 445.330994f, 1.579520f, 192371, 192416 }, // Flag on wall intersect + { 5322.859863f, 2916.949951f, 445.153992f, 1.562070f, 192372, 192416 }, // Flag on wall intersect + { 5290.350098f, 2976.560059f, 435.221008f, 0.017452f, 192373, 192416 }, // Flag on wall intersect + { 5352.370117f, 3037.090088f, 435.252014f, -1.57080f, 192360, 192416 }, // Flag on wall intersect + { 5392.649902f, 3037.110107f, 433.713013f, -1.52716f, 192361, 192416 }, // Flag on wall intersect + { 5237.069824f, 2757.030029f, 435.795990f, 1.518440f, 192356, 192416 }, // Flag on wall intersect + { 5173.020020f, 2820.929932f, 435.720001f, 0.017452f, 192352, 192416 }, // Flag on wall intersect + { 5172.109863f, 2862.570068f, 435.721008f, 0.017452f, 192353, 192416 }, // Flag on wall intersect + { 5235.339844f, 2924.340088f, 435.040009f, -1.57080f, 192357, 192416 }, // Flag on wall intersect + { 5270.689941f, 2861.780029f, 445.058014f, -3.11539f, 192350, 192416 }, // Flag on wall intersect + { 5271.279785f, 2820.159912f, 445.200989f, -3.13286f, 192351, 192416 } // Flag on wall intersect +}; + +const Position WGTurret[WG_MAX_TURRET] = { + { 5391.19f, 3060.8f, 419.616f, 1.69557f }, + { 5266.75f, 2976.5f, 421.067f, 3.20354f }, + { 5234.86f, 2948.8f, 420.88f, 1.61311f }, + { 5323.05f, 2923.7f, 421.645f, 1.5817f }, + { 5363.82f, 2923.87f, 421.709f, 1.60527f }, + { 5264.04f, 2861.34f, 421.587f, 3.21142f }, + { 5264.68f, 2819.78f, 421.656f, 3.15645f }, + { 5322.16f, 2756.69f, 421.646f, 4.69978f }, + { 5363.78f, 2756.77f, 421.629f, 4.78226f }, + { 5236.2f, 2732.68f, 421.649f, 4.72336f }, + { 5265.02f, 2704.63f, 421.7f, 3.12507f }, + { 5350.87f, 2616.03f, 421.243f, 4.72729f }, + { 5390.95f, 2615.5f, 421.126f, 4.6409f }, + { 5148.8f, 2820.24f, 421.621f, 3.16043f }, + { 5147.98f, 2861.93f, 421.63f, 3.18792f }, +}; + +// Here there is all npc keeper spawn point +const WintergraspObjectPositionData WGKeepNPC[WG_MAX_KEEP_NPC] = +{ + // X Y Z O horde alliance + // North East + { 5326.203125f, 2660.026367f, 409.100891f, 2.543383f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Roaming Guard + { 5298.430176f, 2738.760010f, 409.316010f, 3.971740f, BATTLEFIELD_WG_NPC_VIERON_BLAZEFEATHER, BATTLEFIELD_WG_NPC_BOWYER_RANDOLPH }, // Vieron Blazefeather + { 5335.310059f, 2764.110107f, 409.274994f, 4.834560f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5349.810059f, 2763.629883f, 409.333008f, 4.660030f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + // North + { 5373.470215f, 2789.060059f, 409.322998f, 2.600540f, BATTLEFIELD_WG_NPC_STONE_GUARD_MUKAR, BATTLEFIELD_WG_NPC_KNIGHT_DAMERON }, // Stone Guard Mukar + { 5296.560059f, 2789.870117f, 409.274994f, 0.733038f, BATTLEFIELD_WG_NPC_HOODOO_MASTER_FU_JIN, BATTLEFIELD_WG_NPC_SORCERESS_KAYLANA }, // Voodoo Master Fu'jin + { 5372.670000f, 2786.740000f, 409.442000f, 2.809980f, BATTLEFIELD_WG_NPC_CHAMPION_ROS_SLAI, BATTLEFIELD_WG_NPC_MARSHAL_MAGRUDER }, // Wintergrasp Quartermaster + { 5368.709961f, 2856.360107f, 409.322998f, 2.949610f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5367.910156f, 2826.520020f, 409.322998f, 3.333580f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5389.270020f, 2847.370117f, 418.759003f, 3.106690f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5388.560059f, 2834.770020f, 418.759003f, 3.071780f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5359.129883f, 2837.989990f, 409.364014f, 4.698930f, BATTLEFIELD_WG_NPC_COMMANDER_DARDOSH, BATTLEFIELD_WG_NPC_COMMANDER_ZANNETH }, // Commander Dardosh + { 5366.129883f, 2833.399902f, 409.322998f, 3.141590f, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_KILRATH, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_AHBRAMIS }, // Tactical Officer Kilrath + // X Y Z O horde alliance + // North West + { 5350.680176f, 2917.010010f, 409.274994f, 1.466080f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5335.120117f, 2916.800049f, 409.444000f, 1.500980f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5295.560059f, 2926.669922f, 409.274994f, 0.872665f, BATTLEFIELD_WG_NPC_SIEGESMITH_STRONGHOOF, BATTLEFIELD_WG_NPC_SIEGE_MASTER_STOUTHANDLE }, // Stronghoof + { 5371.399902f, 3026.510010f, 409.205994f, 3.250030f, BATTLEFIELD_WG_NPC_PRIMALIST_MULFORT, BATTLEFIELD_WG_NPC_ANCHORITE_TESSA }, // Primalist Mulfort + { 5392.123535f, 3031.110352f, 409.187683f, 3.677212f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Roaming Guard + // South + { 5270.060059f, 2847.550049f, 409.274994f, 3.071780f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5270.160156f, 2833.479980f, 409.274994f, 3.124140f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5179.109863f, 2837.129883f, 409.274994f, 3.211410f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5179.669922f, 2846.600098f, 409.274994f, 3.089230f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5234.970215f, 2883.399902f, 409.274994f, 4.293510f, BATTLEFIELD_WG_NPC_LIEUTENANT_MURP, BATTLEFIELD_WG_NPC_SENIOR_DEMOLITIONIST_LEGOSO }, // Lieutenant Murp + // X Y Z O horde alliance + // Portal guards (from around the fortress) + { 5319.209473f, 3055.947754f, 409.176636f, 1.020201f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5311.612305f, 3061.207275f, 408.734161f, 0.965223f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5264.713379f, 3017.283447f, 408.479706f, 3.482424f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5269.096191f, 3008.315918f, 408.826294f, 3.843706f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5201.414551f, 2945.096924f, 409.190735f, 0.945592f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5193.386230f, 2949.617188f, 409.190735f, 1.145859f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5148.116211f, 2904.761963f, 409.193756f, 3.368532f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5153.355957f, 2895.501465f, 409.199310f, 3.549174f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5154.353027f, 2787.349365f, 409.250183f, 2.555644f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5150.066406f, 2777.876953f, 409.343903f, 2.708797f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5193.706543f, 2732.882812f, 409.189514f, 4.845073f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5202.126953f, 2737.570557f, 409.189514f, 5.375215f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5269.181152f, 2671.174072f, 409.098999f, 2.457459f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5264.960938f, 2662.332520f, 409.098999f, 2.598828f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5307.111816f, 2616.006836f, 409.095734f, 5.355575f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 5316.770996f, 2619.430176f, 409.027740f, 5.363431f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A } // Standing Guard +}; + +const WintergraspObjectPositionData WGOutsideNPC[WG_MAX_OUTSIDE_NPC] = +{ + { 5032.04f, 3681.79f, 362.980f, 4.210f, BATTLEFIELD_WG_NPC_VIERON_BLAZEFEATHER, 0 }, + { 5020.71f, 3626.19f, 360.150f, 4.640f, BATTLEFIELD_WG_NPC_HOODOO_MASTER_FU_JIN, 0 }, + { 4994.85f, 3660.51f, 359.150f, 2.260f, BATTLEFIELD_WG_NPC_COMMANDER_DARDOSH, 0 }, + { 5015.46f, 3677.11f, 362.970f, 6.009f, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_KILRATH, 0 }, + { 5031.12f, 3663.77f, 363.500f, 3.110f, BATTLEFIELD_WG_NPC_SIEGESMITH_STRONGHOOF, 0 }, + { 5042.74f, 3675.82f, 363.060f, 3.358f, BATTLEFIELD_WG_NPC_PRIMALIST_MULFORT, 0 }, + { 5014.45f, 3640.87f, 361.390f, 3.280f, BATTLEFIELD_WG_NPC_LIEUTENANT_MURP, 0 }, + { 5100.07f, 2168.89f, 365.779f, 1.972f, 0, BATTLEFIELD_WG_NPC_BOWYER_RANDOLPH }, + { 5081.70f, 2173.73f, 365.878f, 0.855f, 0, BATTLEFIELD_WG_NPC_SORCERESS_KAYLANA }, + { 5078.28f, 2183.70f, 365.029f, 1.466f, 0, BATTLEFIELD_WG_NPC_COMMANDER_ZANNETH }, + { 5088.49f, 2188.18f, 365.647f, 5.253f, 0, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_AHBRAMIS }, + { 5095.67f, 2193.28f, 365.924f, 4.939f, 0, BATTLEFIELD_WG_NPC_SIEGE_MASTER_STOUTHANDLE }, + { 5088.61f, 2167.66f, 365.689f, 0.680f, 0, BATTLEFIELD_WG_NPC_ANCHORITE_TESSA }, + { 5080.40f, 2199.00f, 359.489f, 2.967f, 0, BATTLEFIELD_WG_NPC_SENIOR_DEMOLITIONIST_LEGOSO }, +}; + +struct WintergraspTeleporterData +{ + uint32 entry; + float x; + float y; + float z; + float o; +}; + +const WintergraspTeleporterData WGPortalDefenderData[WG_MAX_TELEPORTER] = +{ + // Player teleporter + { 190763, 5153.41f, 2901.35f, 409.191f, -0.069f }, + { 190763, 5268.70f, 2666.42f, 409.099f, -0.715f }, + { 190763, 5197.05f, 2944.81f, 409.191f, 2.3387f }, + { 190763, 5196.67f, 2737.34f, 409.189f, -2.932f }, + { 190763, 5314.58f, 3055.85f, 408.862f, 0.5410f }, + { 190763, 5391.28f, 2828.09f, 418.675f, -2.164f }, + { 190763, 5153.93f, 2781.67f, 409.246f, 1.6580f }, + { 190763, 5311.44f, 2618.93f, 409.092f, -2.373f }, + { 190763, 5269.21f, 3013.84f, 408.828f, -1.762f }, + { 190763, 5401.62f, 2853.66f, 418.674f, 2.6354f }, + // Vehicle teleporter + { 192951, 5314.51f, 2703.69f, 408.550f, -0.890f }, + { 192951, 5316.25f, 2977.04f, 408.539f, -0.820f }, +}; + +// ********************************************************* +// **********Tower Element(GameObject,Creature)************* +// ********************************************************* + +struct WintergraspTowerData +{ + uint32 towerEntry; // Gameobject id of tower + uint8 nbObject; // Number of gameobjects spawned on this point + WintergraspObjectPositionData GameObject[6]; // Gameobject position and entry (Horde/Alliance) + + // Creature : Turrets and Guard, TODO: check if killed on tower destruction? tower damage? + uint8 nbCreatureBottom; + WintergraspObjectPositionData CreatureBottom[9]; + uint8 nbCreatureTop; + WintergraspObjectPositionData CreatureTop[5]; +}; + +uint8 const WG_MAX_ATTACKTOWERS = 3; +// 192414 : 0 in sql, 1 in header +// 192278 : 0 in sql, 3 in header +const WintergraspTowerData AttackTowers[WG_MAX_ATTACKTOWERS] = { + // West tower + { + 190356, + 6, + { + { 4559.109863f, 3606.219971f, 419.998993f, -1.483530f, 192488, 192501 }, // Flag on tower + { 4539.419922f, 3622.489990f, 420.033997f, -3.071770f, 192488, 192501 }, // Flag on tower + { 4555.259766f, 3641.649902f, 419.973999f, 1.675510f, 192488, 192501 }, // Flag on tower + { 4574.870117f, 3625.909912f, 420.079010f, 0.080117f, 192488, 192501 }, // Flag on tower + { 4433.899902f, 3534.139893f, 360.274994f, -1.850050f, 192269, 192278 }, // Flag near workshop + { 4572.930176f, 3475.520020f, 363.009003f, 1.42240f, 192269, 192278 } // Flag near bridge + }, + 1, + { + { 4418.688477f, 3506.251709f, 358.975494f, 4.293305f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Roaming Guard + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + }, + 0, + { + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + } + }, + + // South Tower + { + 190357, + 5, + { + { 4416.000000f, 2822.669922f, 429.851013f, -0.017452f, 192488, 192501 }, // Flag on tower + { 4398.819824f, 2804.699951f, 429.791992f, -1.588250f, 192488, 192501 }, // Flag on tower + { 4387.620117f, 2719.570068f, 389.934998f, -1.544620f, 192366, 192414 }, // Flag near tower + { 4464.120117f, 2855.449951f, 406.110992f, 0.829032f, 192366, 192429 }, // Flag near tower + { 4526.459961f, 2810.179932f, 391.200012f, -2.993220f, 192269, 192278 }, // Flag near bridge + { 0, 0, 0, 0, 0, 0 }, + }, + 6, + { + { 4452.859863f, 2808.870117f, 402.604004f, 6.056290f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4455.899902f, 2835.958008f, 401.122559f, 0.034907f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4412.649414f, 2953.792236f, 374.799957f, 0.980838f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Roaming Guard + { 4362.089844f, 2811.510010f, 407.337006f, 3.193950f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4412.290039f, 2753.790039f, 401.015015f, 5.829400f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4421.939941f, 2773.189941f, 400.894989f, 5.707230f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0}, + }, + 0, + { + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + }, + }, + + // East Tower + { + 190358, + 4, + { + { 4466.790039f, 1960.420044f, 459.144012f, 1.151920f, 192488, 192501 }, // Flag on tower + { 4475.350098f, 1937.030029f, 459.070007f, -0.43633f, 192488, 192501 }, // Flag on tower + { 4451.759766f, 1928.099976f, 459.075989f, -2.00713f, 192488, 192501 }, // Flag on tower + { 4442.990234f, 1951.900024f, 459.092987f, 2.740160f, 192488, 192501 }, // Flag on tower + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + }, + 5, + { + { 4501.060059f, 1990.280029f, 431.157013f, 1.029740f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4463.830078f, 2015.180054f, 430.299988f, 1.431170f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4494.580078f, 1943.760010f, 435.627014f, 6.195920f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4450.149902f, 1897.579956f, 435.045013f, 4.398230f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 4428.870117f, 1906.869995f, 432.648010f, 3.996800f, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + }, + 0, + { + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + }, + }, +}; + +struct WintergraspTowerCannonData +{ + uint32 towerEntry; + uint8 nbTowerCannonBottom; + Position TowerCannonBottom[5]; + uint8 nbTurretTop; + Position TurretTop[5]; +}; + +const uint8 WG_MAX_TOWER_CANNON = 7; + +const WintergraspTowerCannonData TowerCannon[WG_MAX_TOWER_CANNON] = +{ + { + 190221, + 0, + { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + 2, + { + { 5255.88f, 3047.63f, 438.499f, 3.13677f }, + { 5280.9f, 3071.32f, 438.499f, 1.62879f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + }, + { + 190373, + 0, + { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + 2, + { + { 5138.59f, 2935.16f, 439.845f, 3.11723f }, + { 5163.06f, 2959.52f, 439.846f, 1.47258f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + }, + { + 190377, + 0, + { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + 2, + { + { 5163.84f, 2723.74f, 439.844f, 1.3994f }, + { 5139.69f, 2747.4f, 439.844f, 3.17221f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + }, + { + 190378, + 0, + { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + 2, + { + { 5278.21f, 2607.23f, 439.755f, 4.71944f }, + { 5255.01f, 2631.98f, 439.755f, 3.15257f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + }, + { + 190356, + 2, + { + {4537.380371f, 3599.531738f, 402.886993f, 3.998462f}, + {4581.497559f, 3604.087158f, 402.886963f, 5.651723f}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + 2, + { + {4469.448242f, 1966.623779f, 465.647217f, 1.153573f}, + {4581.895996f, 3626.438477f, 426.539062f, 0.117806f}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + }, + { + 190357, + 2, + { + { 4421.640137f, 2799.935791f, 412.630920f, 5.459298f }, + { 4420.263184f, 2845.340332f, 412.630951f, 0.742197f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + 3, + { + { 4423.430664f, 2822.762939f, 436.283142f, 6.223487f }, + { 4397.825684f, 2847.629639f, 436.283325f, 1.579430f }, + { 4398.814941f, 2797.266357f, 436.283051f, 4.703747f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + }, + { + 190358, + 2, + { + { 4448.138184f, 1974.998779f, 441.995911f, 1.967238f }, + { 4448.713379f, 1955.148682f, 441.995178f, 0.380733f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + 2, + { + { 4469.448242f, 1966.623779f, 465.647217f, 1.153573f }, + { 4481.996582f, 1933.658325f, 465.647186f, 5.873029f }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }, + }, +}; + +// ********************************************************* +// *****************WorkShop Data & Element***************** +// ********************************************************* + +uint8 const WG_MAX_WORKSHOP = 6; + +struct WGWorkshopData +{ + uint8 id; + uint32 worldstate; + uint32 text; +}; + +const WGWorkshopData WorkshopsData[WG_MAX_WORKSHOP] = +{ + // NE + {BATTLEFIELD_WG_WORKSHOP_NE, WORLDSTATE_WORKSHOP_NE, BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_NE}, + // NW + {BATTLEFIELD_WG_WORKSHOP_NW, WORLDSTATE_WORKSHOP_NW, BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_NW}, + // SE + {BATTLEFIELD_WG_WORKSHOP_SE, WORLDSTATE_WORKSHOP_SE, BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_SE}, + // SW + {BATTLEFIELD_WG_WORKSHOP_SW, WORLDSTATE_WORKSHOP_SW, BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_SW}, + // KEEP WEST - It can't be taken, so it doesn't have a textid + {BATTLEFIELD_WG_WORKSHOP_KEEP_WEST, WORLDSTATE_WORKSHOP_K_W, NULL}, + // KEEP EAST - It can't be taken, so it doesn't have a textid + {BATTLEFIELD_WG_WORKSHOP_KEEP_EAST, WORLDSTATE_WORKSHOP_K_E, NULL} +}; + +// ******************************************************************** +// * Structs using for Building,Graveyard,Workshop * +// ******************************************************************** +// Structure for different buildings that can be destroyed during battle +struct BfWGGameObjectBuilding +{ + BfWGGameObjectBuilding(BattlefieldWG *WG) + { + m_WG = WG; + m_Team = 0; + m_Build = NULL; + m_Type = 0; + m_WorldState = 0; + m_State = 0; + m_NameId = 0; + } + + // the team that controls this point + uint8 m_Team; + + // WG object + BattlefieldWG *m_WG; + + // Linked gameobject + GameObject* m_Build; + + // eWGGameObjectBuildingType + uint32 m_Type; + + // WorldState + uint32 m_WorldState; + + // eWGGameObjectState + uint32 m_State; + + // Name id for warning text + uint32 m_NameId; + + // GameObject associations + GameObjectSet m_GameObjectList[2]; + + // Creature associations + GuidSet m_CreatureBottomList[2]; + GuidSet m_CreatureTopList[2]; + GuidSet m_TowerCannonBottomList; + GuidSet m_TurretTopList; + + void Rebuild() + { + switch (m_Type) + { + case BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER: + case BATTLEFIELD_WG_OBJECTTYPE_DOOR_LAST: + case BATTLEFIELD_WG_OBJECTTYPE_DOOR: + case BATTLEFIELD_WG_OBJECTTYPE_WALL: + m_Team = m_WG->GetDefenderTeam(); // Objects that are part of the keep should be the defender's + break; + case BATTLEFIELD_WG_OBJECTTYPE_TOWER: + m_Team = m_WG->GetAttackerTeam(); // The towers in the south should be the attacker's + break; + default: + m_Team = TEAM_NEUTRAL; + break; + } + + // Rebuild gameobject + m_Build->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING, NULL, true); + + // Update worldstate + m_State = BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT - (m_Team * 3); + m_WG->SendUpdateWorldState(m_WorldState, m_State); + UpdateCreatureAndGo(); + m_Build->SetUInt32Value(GAMEOBJECT_FACTION, WintergraspFaction[m_Team]); + } + + // Called when associated gameobject is damaged + void Damaged() + { + // Update worldstate + m_State = BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DAMAGE - (m_Team * 3); + m_WG->SendUpdateWorldState(m_WorldState, m_State); + + // Send warning message + if (m_NameId) // tower damage + name + m_WG->SendWarningToAllInZone(m_NameId); + + for (GuidSet::const_iterator itr = m_CreatureTopList[m_WG->GetAttackerTeam()].begin(); itr != m_CreatureTopList[m_WG->GetAttackerTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->HideNpc(creature); + + for (GuidSet::const_iterator itr = m_TurretTopList.begin(); itr != m_TurretTopList.end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->HideNpc(creature); + + if (m_Type == BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER) + m_WG->UpdateDamagedTowerCount(m_WG->GetDefenderTeam()); + else if (m_Type == BATTLEFIELD_WG_OBJECTTYPE_TOWER) + m_WG->UpdateDamagedTowerCount(m_WG->GetAttackerTeam()); + } + + // Called when associated gameobject is destroyed + void Destroyed() + { + // Update worldstate + m_State = BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DESTROY - (m_Team * 3); + m_WG->SendUpdateWorldState(m_WorldState, m_State); + + // Warn players + if (m_NameId) + m_WG->SendWarningToAllInZone(m_NameId); + + switch (m_Type) + { + // Inform the global wintergrasp script of the destruction of this object + case BATTLEFIELD_WG_OBJECTTYPE_TOWER: + case BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER: + m_WG->UpdatedDestroyedTowerCount(TeamId(m_Team)); + break; + case BATTLEFIELD_WG_OBJECTTYPE_DOOR_LAST: + m_WG->SetRelicInteractible(true); + if (m_WG->GetRelic()) + m_WG->GetRelic()->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + else + sLog->outError(LOG_FILTER_GENERAL, "BattlefieldWG: Relic not found."); + break; + } + + m_WG->BrokenWallOrTower(TeamId(m_Team)); + } + + void Init(GameObject *go, uint32 type, uint32 worldstate, uint32 nameid) + { + // GameObject associated to object + m_Build = go; + + // Type of building (WALL/TOWER/DOOR) + m_Type = type; + + // WorldState for client (icon on map) + m_WorldState = worldstate; + + // NameId for Warning text + m_NameId = nameid; + + switch (m_Type) + { + case BATTLEFIELD_WG_OBJECTTYPE_KEEP_TOWER: + case BATTLEFIELD_WG_OBJECTTYPE_DOOR_LAST: + case BATTLEFIELD_WG_OBJECTTYPE_DOOR: + case BATTLEFIELD_WG_OBJECTTYPE_WALL: + m_Team = m_WG->GetDefenderTeam(); // Objects that are part of the keep should be the defender's + break; + case BATTLEFIELD_WG_OBJECTTYPE_TOWER: + m_Team = m_WG->GetAttackerTeam(); // The towers in the south should be the attacker's + break; + default: + m_Team = TEAM_NEUTRAL; + break; + } + + m_State = sWorld->getWorldState(m_WorldState); + if (m_Build) + { + switch (m_State) + { + case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT: + case BATTLEFIELD_WG_OBJECTSTATE_HORDE_INTACT: + m_Build->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING, NULL, true); + break; + case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DESTROY: + case BATTLEFIELD_WG_OBJECTSTATE_HORDE_DESTROY: + m_Build->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED); + break; + case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DAMAGE: + case BATTLEFIELD_WG_OBJECTSTATE_HORDE_DAMAGE: + m_Build->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED); + break; + } + } + + int32 towerid = -1; + switch (go->GetEntry()) + { + case GO_WINTERGRASP_FORTRESS_TOWER_1: + towerid = 0; + break; + case GO_WINTERGRASP_FORTRESS_TOWER_2: + towerid = 1; + break; + case GO_WINTERGRASP_FORTRESS_TOWER_3: + towerid = 2; + break; + case GO_WINTERGRASP_FORTRESS_TOWER_4: + towerid = 3; + break; + case GO_WINTERGRASP_SHADOWSIGHT_TOWER: + towerid = 4; + break; + case GO_WINTERGRASP_WINTER_S_EDGE_TOWER: + towerid = 5; + break; + case GO_WINTERGRASP_FLAMEWATCH_TOWER: + towerid = 6; + break; + } + + if (towerid > 3) // Attacker towers + { + // Spawn associate gameobjects + for (uint8 i = 0; i < AttackTowers[towerid - 4].nbObject; i++) + { + WintergraspObjectPositionData gobData = AttackTowers[towerid - 4].GameObject[i]; + if (GameObject* go = m_WG->SpawnGameObject(gobData.entryHorde, gobData.x, gobData.y, gobData.z, gobData.o)) + m_GameObjectList[TEAM_HORDE].insert(go); + if (GameObject* go = m_WG->SpawnGameObject(gobData.entryAlliance, gobData.x, gobData.y, gobData.z, gobData.o)) + m_GameObjectList[TEAM_ALLIANCE].insert(go); + } + + // Spawn associate npc bottom + for (uint8 i = 0; i < AttackTowers[towerid - 4].nbCreatureBottom; i++) + { + WintergraspObjectPositionData creatureData = AttackTowers[towerid - 4].CreatureBottom[i]; + if (Creature* creature = m_WG->SpawnCreature(creatureData.entryHorde, creatureData.x, creatureData.y, creatureData.z, creatureData.o, TEAM_HORDE)) + m_CreatureBottomList[TEAM_HORDE].insert(creature->GetGUID()); + if (Creature* creature = m_WG->SpawnCreature(creatureData.entryAlliance, creatureData.x, creatureData.y, creatureData.z, creatureData.o, TEAM_ALLIANCE)) + m_CreatureBottomList[TEAM_ALLIANCE].insert(creature->GetGUID()); + } + + // Spawn associate npc top + for (uint8 i = 0; i < AttackTowers[towerid - 4].nbCreatureTop; i++) + { + WintergraspObjectPositionData creatureData = AttackTowers[towerid - 4].CreatureTop[i]; + if (Creature* creature = m_WG->SpawnCreature(creatureData.entryHorde, creatureData.x, creatureData.y, creatureData.z, creatureData.o, TEAM_HORDE)) + m_CreatureTopList[TEAM_HORDE].insert(creature->GetGUID()); + if (Creature* creature = m_WG->SpawnCreature(creatureData.entryAlliance, creatureData.x, creatureData.y, creatureData.z, creatureData.o, TEAM_ALLIANCE)) + m_CreatureTopList[TEAM_ALLIANCE].insert(creature->GetGUID()); + } + } + + if (towerid >= 0) + { + // Spawn Turret bottom + for (uint8 i = 0; i < TowerCannon[towerid].nbTowerCannonBottom; i++) + { + Position turretPos; + TowerCannon[towerid].TowerCannonBottom[i].GetPosition(&turretPos); + if (Creature* turret = m_WG->SpawnCreature(NPC_WINTERGRASP_TOWER_CANNON, turretPos, TEAM_ALLIANCE)) + { + m_TowerCannonBottomList.insert(turret->GetGUID()); + switch (go->GetEntry()) + { + case GO_WINTERGRASP_FORTRESS_TOWER_1: + case GO_WINTERGRASP_FORTRESS_TOWER_2: + case GO_WINTERGRASP_FORTRESS_TOWER_3: + case GO_WINTERGRASP_FORTRESS_TOWER_4: + turret->setFaction(WintergraspFaction[m_WG->GetDefenderTeam()]); + break; + case GO_WINTERGRASP_SHADOWSIGHT_TOWER: + case GO_WINTERGRASP_WINTER_S_EDGE_TOWER: + case GO_WINTERGRASP_FLAMEWATCH_TOWER: + turret->setFaction(WintergraspFaction[m_WG->GetAttackerTeam()]); + break; + } + m_WG->HideNpc(turret); + } + } + + // Spawn Turret top + for (uint8 i = 0; i < TowerCannon[towerid].nbTurretTop; i++) + { + Position towerCannonPos; + TowerCannon[towerid].TurretTop[i].GetPosition(&towerCannonPos); + if (Creature *turret = m_WG->SpawnCreature(28366, towerCannonPos, TeamId(0))) + { + m_TurretTopList.insert(turret->GetGUID()); + switch (go->GetEntry()) + { + case GO_WINTERGRASP_FORTRESS_TOWER_1: + case GO_WINTERGRASP_FORTRESS_TOWER_2: + case GO_WINTERGRASP_FORTRESS_TOWER_3: + case GO_WINTERGRASP_FORTRESS_TOWER_4: + turret->setFaction(WintergraspFaction[m_WG->GetDefenderTeam()]); + break; + case GO_WINTERGRASP_SHADOWSIGHT_TOWER: + case GO_WINTERGRASP_WINTER_S_EDGE_TOWER: + case GO_WINTERGRASP_FLAMEWATCH_TOWER: + turret->setFaction(WintergraspFaction[m_WG->GetAttackerTeam()]); + break; + } + m_WG->HideNpc(turret); + } + } + UpdateCreatureAndGo(); + } + } + + void UpdateCreatureAndGo() + { + for (GuidSet::const_iterator itr = m_CreatureTopList[m_WG->GetDefenderTeam()].begin(); itr != m_CreatureTopList[m_WG->GetDefenderTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->HideNpc(creature); + + for (GuidSet::const_iterator itr = m_CreatureTopList[m_WG->GetAttackerTeam()].begin(); itr != m_CreatureTopList[m_WG->GetAttackerTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->ShowNpc(creature, true); + + for (GuidSet::const_iterator itr = m_CreatureBottomList[m_WG->GetDefenderTeam()].begin(); itr != m_CreatureBottomList[m_WG->GetDefenderTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->HideNpc(creature); + + for (GuidSet::const_iterator itr = m_CreatureBottomList[m_WG->GetAttackerTeam()].begin(); itr != m_CreatureBottomList[m_WG->GetAttackerTeam()].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->ShowNpc(creature, true); + + for (GameObjectSet::const_iterator itr = m_GameObjectList[m_WG->GetDefenderTeam()].begin(); itr != m_GameObjectList[m_WG->GetDefenderTeam()].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_ONE_DAY); + + for (GameObjectSet::const_iterator itr = m_GameObjectList[m_WG->GetAttackerTeam()].begin(); itr != m_GameObjectList[m_WG->GetAttackerTeam()].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_IMMEDIATELY); + } + + void UpdateTurretAttack(bool disable) + { + for (GuidSet::const_iterator itr = m_TowerCannonBottomList.begin(); itr != m_TowerCannonBottomList.end(); ++itr) + { + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + { + if (Creature* creature = unit->ToCreature()) + { + if (m_Build) + { + if (disable) + m_WG->HideNpc(creature); + else + m_WG->ShowNpc(creature, true); + + switch (m_Build->GetEntry()) + { + case 190221: + case 190373: + case 190377: + case 190378: + { + creature->setFaction(WintergraspFaction[m_WG->GetDefenderTeam()]); + break; + } + case 190356: + case 190357: + case 190358: + { + creature->setFaction(WintergraspFaction[m_WG->GetAttackerTeam()]); + break; + } + } + } + } + } + } + + for (GuidSet::const_iterator itr = m_TurretTopList.begin(); itr != m_TurretTopList.end(); ++itr) + { + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + { + if (Creature* creature = unit->ToCreature()) + { + if (m_Build) + { + if (disable) + m_WG->HideNpc(creature); + else + m_WG->ShowNpc(creature, true); + + switch (m_Build->GetEntry()) + { + case 190221: + case 190373: + case 190377: + case 190378: + { + creature->setFaction(WintergraspFaction[m_WG->GetDefenderTeam()]); + break; + } + case 190356: + case 190357: + case 190358: + { + creature->setFaction(WintergraspFaction[m_WG->GetAttackerTeam()]); + break; + } + } + } + } + } + } + } + + void Save() + { + sWorld->setWorldState(m_WorldState, m_State); + } +}; + +struct WGWorkshop +{ + // pointer to the battlefield that the workshop belongs to + BattlefieldWG* bf; + // id of the workshop, useful to retrieve data of the WorkshopsData array + uint8 workshopId; + // team that controls the node + uint8 teamControl; + // for worldstate + uint32 state; + + WGWorkshop(BattlefieldWG* _bf, uint8 _workshopId) + { + ASSERT(_bf || _workshopId < WG_MAX_WORKSHOP); + + bf = _bf; + workshopId = _workshopId; + } + + void GiveControlTo(uint8 team, bool init /* for first call in setup*/) + { + switch (team) + { + case BATTLEFIELD_WG_TEAM_NEUTRAL: + { + // Send warning message to all player to inform a faction attack to a workshop + // alliance / horde attacking a workshop + bf->SendWarningToAllInZone(teamControl ? WorkshopsData[workshopId].text : WorkshopsData[workshopId].text + 1); + break; + } + case BATTLEFIELD_WG_TEAM_ALLIANCE: + case BATTLEFIELD_WG_TEAM_HORDE: + { + // Updating worldstate + state = team == BATTLEFIELD_WG_TEAM_ALLIANCE ? BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT : BATTLEFIELD_WG_OBJECTSTATE_HORDE_INTACT; + bf->SendUpdateWorldState(WorkshopsData[workshopId].worldstate, state); + + // Warning message + if (!init) // workshop taken - alliance + bf->SendWarningToAllInZone(team == BATTLEFIELD_WG_TEAM_ALLIANCE ? WorkshopsData[workshopId].text : WorkshopsData[workshopId].text+1); + + // Found associate graveyard and update it + if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) + if (bf->GetGraveyardById(workshopId)) + bf->GetGraveyardById(workshopId)->GiveControlTo(team == BATTLEFIELD_WG_TEAM_ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE); + + teamControl = team; + break; + } + } + + if (!init) + bf->UpdateCounterVehicle(false); + } + + void UpdateGraveyardAndWorkshop() + { + if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) + bf->GetGraveyardById(workshopId)->GiveControlTo(TeamId(teamControl)); + else + GiveControlTo(bf->GetDefenderTeam(), true); + } + + void Save() + { + sWorld->setWorldState(WorkshopsData[workshopId].worldstate, state); + } +}; + +// Structure for the 6 workshop +struct WintergraspWorkshopData +{ + BattlefieldWG* m_WG; // Pointer to wintergrasp + GameObject* m_Build; + uint32 m_Type; + uint32 m_State; // For worldstate + uint32 m_WorldState; + uint32 m_TeamControl; // Team witch control the workshop + GuidSet m_CreatureOnPoint[2]; // Contain all Creature associate to this point + GameObjectSet m_GameObjectOnPoint[2]; // Contain all Gameobject associate to this point + uint32 m_NameId; // Id of trinity_string witch contain name of this node, using for alert message + + WintergraspWorkshopData(BattlefieldWG * WG) + { + m_WG = WG; + m_Build = NULL; + m_Type = 0; + m_State = 0; + m_WorldState = 0; + m_TeamControl = 0; + m_NameId = 0; + } + + // Spawning associate creature and store them + void AddCreature(WintergraspObjectPositionData obj) + { + if (Creature* creature = m_WG->SpawnCreature(obj.entryHorde, obj.x, obj.y, obj.z, obj.o, TEAM_HORDE)) + m_CreatureOnPoint[TEAM_HORDE].insert(creature->GetGUID()); + + if (Creature* creature = m_WG->SpawnCreature(obj.entryAlliance, obj.x, obj.y, obj.z, obj.o, TEAM_ALLIANCE)) + m_CreatureOnPoint[TEAM_ALLIANCE].insert(creature->GetGUID()); + } + + // Spawning Associate gameobject and store them + void AddGameObject(WintergraspObjectPositionData obj) + { + if (GameObject *gameobject = m_WG->SpawnGameObject(obj.entryHorde, obj.x, obj.y, obj.z, obj.o)) + m_GameObjectOnPoint[TEAM_HORDE].insert(gameobject); + if (GameObject *gameobject = m_WG->SpawnGameObject(obj.entryAlliance, obj.x, obj.y, obj.z, obj.o)) + m_GameObjectOnPoint[TEAM_ALLIANCE].insert(gameobject); + } + + // Init method, setup variable + void Init(uint32 worldstate, uint32 type, uint32 nameid) + { + m_WorldState = worldstate; + m_Type = type; + m_NameId = nameid; + } + + // Called on change faction in CapturePoint class + void GiveControlTo(uint8 team, bool init /* for first call in setup*/) + { + switch (team) + { + case BATTLEFIELD_WG_TEAM_NEUTRAL: + { + // Send warning message to all player for inform a faction attack a workshop + // alliance / horde attacking workshop + m_WG->SendWarningToAllInZone(m_TeamControl ? m_NameId : m_NameId + 1); + break; + } + case BATTLEFIELD_WG_TEAM_ALLIANCE: + { + // Show Alliance creature + for (GuidSet::const_iterator itr = m_CreatureOnPoint[TEAM_ALLIANCE].begin(); itr != m_CreatureOnPoint[TEAM_ALLIANCE].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->ShowNpc(creature, creature->GetEntry() != 30499); + + // Hide Horde creature + for (GuidSet::const_iterator itr = m_CreatureOnPoint[TEAM_HORDE].begin(); itr != m_CreatureOnPoint[TEAM_HORDE].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->HideNpc(creature); + + // Show Alliance gameobject + for (GameObjectSet::const_iterator itr = m_GameObjectOnPoint[TEAM_ALLIANCE].begin(); itr != m_GameObjectOnPoint[TEAM_ALLIANCE].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_IMMEDIATELY); + + // Hide Horde gameobject + for (GameObjectSet::const_iterator itr = m_GameObjectOnPoint[TEAM_HORDE].begin(); itr != m_GameObjectOnPoint[TEAM_HORDE].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_ONE_DAY); + + + // Updating worldstate + m_State = BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT; + m_WG->SendUpdateWorldState(m_WorldState, m_State); + + // Warning message + if (!init) // workshop taken - alliance + m_WG->SendWarningToAllInZone(m_NameId); + + // Found associate graveyard and update it + if (m_Type < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) + if (m_WG && m_WG->GetGraveyardById(m_Type)) + m_WG->GetGraveyardById(m_Type)->GiveControlTo(TEAM_ALLIANCE); + + m_TeamControl = team; + break; + } + case BATTLEFIELD_WG_TEAM_HORDE: + { + // Show Horde creature + for (GuidSet::const_iterator itr = m_CreatureOnPoint[TEAM_HORDE].begin(); itr != m_CreatureOnPoint[TEAM_HORDE].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->ShowNpc(creature, creature->GetEntry() != 30400); + + // Hide Alliance creature + for (GuidSet::const_iterator itr = m_CreatureOnPoint[TEAM_ALLIANCE].begin(); itr != m_CreatureOnPoint[TEAM_ALLIANCE].end(); ++itr) + if (Unit* unit = sObjectAccessor->FindUnit(*itr)) + if (Creature* creature = unit->ToCreature()) + m_WG->HideNpc(creature); + + // Hide Alliance gameobject + for (GameObjectSet::const_iterator itr = m_GameObjectOnPoint[TEAM_ALLIANCE].begin(); itr != m_GameObjectOnPoint[TEAM_ALLIANCE].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_ONE_DAY); + + // Show Horde gameobject + for (GameObjectSet::const_iterator itr = m_GameObjectOnPoint[TEAM_HORDE].begin(); itr != m_GameObjectOnPoint[TEAM_HORDE].end(); ++itr) + (*itr)->SetRespawnTime(RESPAWN_IMMEDIATELY); + + // Update worlstate + m_State = BATTLEFIELD_WG_OBJECTSTATE_HORDE_INTACT; + m_WG->SendUpdateWorldState(m_WorldState, m_State); + + // Warning message + if (!init) // workshop taken - horde + m_WG->SendWarningToAllInZone(m_NameId + 1); + + // Update graveyard control + if (m_Type < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) + if (m_WG && m_WG->GetGraveyardById(m_Type)) + m_WG->GetGraveyardById(m_Type)->GiveControlTo(TEAM_HORDE); + + m_TeamControl = team; + break; + } + } + if (!init) + m_WG->UpdateCounterVehicle(false); + } + + void UpdateGraveyardAndWorkshop() + { + if (m_Type < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) + m_WG->GetGraveyardById(m_Type)->GiveControlTo(TeamId(m_TeamControl)); + else + GiveControlTo(m_WG->GetDefenderTeam(), true); + } + + void Save() + { + sWorld->setWorldState(m_WorldState, m_State); + } +}; + +#endif diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index f41cd75d127..874332d2364 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -17,6 +17,7 @@ file(GLOB_RECURSE sources_Achievements Achievements/*.cpp Achievements/*.h) file(GLOB_RECURSE sources_Addons Addons/*.cpp Addons/*.h) file(GLOB_RECURSE sources_AI AI/*.cpp AI/*.h) file(GLOB_RECURSE sources_AuctionHouse AuctionHouse/*.cpp AuctionHouse/*.h) +file(GLOB_RECURSE sources_Battlefield Battlefield/*.cpp Battlefield/*.h) file(GLOB_RECURSE sources_Battlegrounds Battlegrounds/*.cpp Battlegrounds/*.h) file(GLOB_RECURSE sources_Calendar Calendar/*.cpp Calendar/*.h) file(GLOB_RECURSE sources_Chat Chat/*.cpp Chat/*.h) @@ -68,6 +69,7 @@ set(game_STAT_SRCS ${sources_Addons} ${sources_AI} ${sources_AuctionHouse} + ${sources_Battlefield} ${sources_Battlegrounds} ${sources_Calendar} ${sources_Chat} @@ -135,6 +137,8 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/AI/ScriptedAI ${CMAKE_CURRENT_SOURCE_DIR}/AI/SmartScripts ${CMAKE_CURRENT_SOURCE_DIR}/AuctionHouse + ${CMAKE_CURRENT_SOURCE_DIR}/Battlefield + ${CMAKE_CURRENT_SOURCE_DIR}/Battlefield/Zones ${CMAKE_CURRENT_SOURCE_DIR}/Battlegrounds ${CMAKE_CURRENT_SOURCE_DIR}/Battlegrounds/Zones ${CMAKE_CURRENT_SOURCE_DIR}/Calendar diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index edbc40767f9..fb1a5ccb9b6 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -49,6 +49,8 @@ #include "DynamicTree.h" #include "Unit.h" #include "Group.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" uint32 GuidHigh2TypeId(uint32 guid_hi) { @@ -2483,7 +2485,12 @@ void WorldObject::SetZoneScript() if (map->IsDungeon()) m_zoneScript = (ZoneScript*)((InstanceMap*)map)->GetInstanceScript(); else if (!map->IsBattlegroundOrArena()) - m_zoneScript = sOutdoorPvPMgr->GetZoneScript(GetZoneId()); + { + if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(GetZoneId())) + m_zoneScript = bf; + else + m_zoneScript = sOutdoorPvPMgr->GetZoneScript(GetZoneId()); + } } } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6830a7a0db0..98a424f1b72 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -75,6 +75,9 @@ #include <cmath> #include "AccountMgr.h" #include "DB2Stores.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" +#include "BattlefieldMgr.h" #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS) @@ -2453,6 +2456,7 @@ void Player::RemoveFromWorld() StopCastingBindSight(); UnsummonPetTemporaryIfAny(); sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); + sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); } ///- Do not add/remove the player from the object storage @@ -5515,7 +5519,12 @@ void Player::RepopAtGraveyard() if (Battleground* bg = GetBattleground()) ClosestGrave = bg->GetClosestGraveYard(this); else - ClosestGrave = sObjectMgr->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam()); + { + if (sBattlefieldMgr->GetBattlefieldToZoneId(GetZoneId())) + ClosestGrave = sBattlefieldMgr->GetBattlefieldToZoneId(GetZoneId())->GetClosestGraveYard(this); + else + ClosestGrave = sObjectMgr->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam()); + } // stop countdown until repop m_deathTimer = 0; @@ -7571,6 +7580,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) { sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone); + sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); + sBattlefieldMgr->HandlePlayerEnterZone(this, newZone); SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange... } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 727262d2e17..76644b1aa99 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -57,6 +57,8 @@ #include "MoveSpline.h" #include "ConditionMgr.h" #include "UpdateFieldFlags.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" #include <math.h> @@ -14856,9 +14858,14 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) // outdoor pvp things, do these after setting the death state, else the player activity notify won't work... doh... // handle player kill only if not suicide (spirit of redemption for example) if (player && this != victim) + { if (OutdoorPvP* pvp = player->GetOutdoorPvP()) pvp->HandleKill(player, victim); + if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetZoneId())) + bf->HandleKill(player, victim); + } + //if (victim->GetTypeId() == TYPEID_PLAYER) // if (OutdoorPvP* pvp = victim->ToPlayer()->GetOutdoorPvP()) // pvp->HandlePlayerActivityChangedpVictim->ToPlayer(); diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 120de2199c2..60dbe64f7f1 100755 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -59,7 +59,7 @@ Loot* Roll::getLoot() Group::Group() : m_leaderGuid(0), m_leaderName(""), m_groupType(GROUPTYPE_NORMAL), m_dungeonDifficulty(DUNGEON_DIFFICULTY_NORMAL), m_raidDifficulty(RAID_DIFFICULTY_10MAN_NORMAL), -m_bgGroup(NULL), m_lootMethod(FREE_FOR_ALL), m_lootThreshold(ITEM_QUALITY_UNCOMMON), m_looterGuid(0), +m_bgGroup(NULL), m_bfGroup(NULL), m_lootMethod(FREE_FOR_ALL), m_lootThreshold(ITEM_QUALITY_UNCOMMON), m_looterGuid(0), m_subGroupsCounts(NULL), m_guid(0), m_counter(0), m_maxEnchantingLevel(0), m_dbStoreId(0) { for (uint8 i = 0; i < TARGETICONCOUNT; ++i) @@ -2196,6 +2196,11 @@ bool Group::isBGGroup() const return m_bgGroup != NULL; } +bool Group::isBFGroup() const +{ + return m_bfGroup != NULL; +} + bool Group::IsCreated() const { return GetMembersCount() > 0; @@ -2297,6 +2302,11 @@ void Group::SetBattlegroundGroup(Battleground* bg) m_bgGroup = bg; } +void Group::SetBattlefieldGroup(Battlefield *bg) +{ + m_bfGroup = bg; +} + void Group::SetGroupMemberFlag(uint64 guid, bool apply, GroupMemberFlags flag) { // Assistants, main assistants and main tanks are only available in raid groups diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index b0543359a6b..f11cdcdbcf4 100755 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -26,6 +26,8 @@ #include "QueryResult.h" #include "SharedDefines.h" #include "Player.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" class Creature; class GroupReference; @@ -207,6 +209,7 @@ class Group bool isLFGGroup() const; bool isRaidGroup() const; bool isBGGroup() const; + bool isBFGroup() const; bool IsCreated() const; uint64 GetLeaderGUID() const; uint64 GetGUID() const; @@ -244,6 +247,7 @@ class Group void ConvertToGroup(); void SetBattlegroundGroup(Battleground* bg); + void SetBattlefieldGroup(Battlefield *bf); GroupJoinBattlegroundResult CanJoinBattlegroundQueue(Battleground const* bgOrTemplate, BattlegroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot); void ChangeMembersGroup(uint64 guid, uint8 group); @@ -328,6 +332,7 @@ class Group Difficulty m_dungeonDifficulty; Difficulty m_raidDifficulty; Battleground* m_bgGroup; + Battlefield* m_bfGroup; uint64 m_targetIcons[TARGETICONCOUNT]; LootMethod m_lootMethod; ItemQualities m_lootThreshold; diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 6836d5b3e9f..e46aa4c39b7 100755 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -34,6 +34,8 @@ #include "Opcodes.h" #include "DisableMgr.h" #include "Group.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket & recvData) { @@ -577,6 +579,9 @@ void WorldSession::HandleAreaSpiritHealerQueryOpcode(WorldPacket & recvData) if (bg) sBattlegroundMgr->SendAreaSpiritHealerQueryOpcode(_player, bg, guid); + + if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId())) + bf->SendAreaSpiritHealerQueryOpcode(_player,guid); } void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket & recvData) @@ -597,8 +602,12 @@ void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket & recvData) if (bg) bg->AddPlayerToResurrectQueue(guid, _player->GetGUID()); + + if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId())) + bf->AddPlayerToResurrectQueue(guid, _player->GetGUID()); } + void WorldSession::HandleBattlemasterJoinArena(WorldPacket & recvData) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_BATTLEMASTER_JOIN_ARENA"); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 7803a69587f..c177d8b2888 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -52,6 +52,8 @@ #include "Group.h" #include "AccountMgr.h" #include "Spell.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" void WorldSession::HandleRepopRequestOpcode(WorldPacket& recvData) { @@ -1730,6 +1732,12 @@ void WorldSession::HandleHearthAndResurrect(WorldPacket& /*recvData*/) if (_player->isInFlight()) return; + if(Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId())) + { + // bf->PlayerAskToLeave(_player); FIXME + return; + } + AreaTableEntry const* atEntry = GetAreaEntryByAreaID(_player->GetAreaId()); if (!atEntry || !(atEntry->flags & AREA_FLAG_WINTERGRASP_2)) return; diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 4c44427cf14..2f5c031e336 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -122,7 +122,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvData) // no or incorrect quest giver if (!object || (object->GetTypeId() != TYPEID_PLAYER && !object->hasQuest(questId)) || - (object->GetTypeId() == TYPEID_PLAYER && !object->ToPlayer()->CanShareQuest(questId))) + (object->GetTypeId() == TYPEID_PLAYER && object != _player && !object->ToPlayer()->CanShareQuest(questId))) { _player->PlayerTalkClass->SendCloseGossip(); _player->SetDivider(0); @@ -582,7 +582,7 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket) continue; } - player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, _player->GetGUID(), true); + player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GetGUID(), true); player->SetDivider(_player->GetGUID()); } } diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index a8767382e03..0337a60a459 100755 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -501,6 +501,7 @@ void AddSC_howling_fjord(); void AddSC_icecrown(); void AddSC_sholazar_basin(); void AddSC_storm_peaks(); +void AddSC_wintergrasp(); void AddSC_zuldrak(); void AddSC_crystalsong_forest(); void AddSC_isle_of_conquest(); @@ -1211,6 +1212,7 @@ void AddNorthrendScripts() AddSC_icecrown(); AddSC_sholazar_basin(); AddSC_storm_peaks(); + AddSC_wintergrasp(); AddSC_zuldrak(); AddSC_crystalsong_forest(); AddSC_isle_of_conquest(); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 671cba2e2c2..589eb6fc179 100755 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -723,6 +723,14 @@ bool ScriptMgr::OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effInd return tmpscript->OnDummyEffect(caster, spellId, effIndex, target); } +GameObjectAI* ScriptMgr::GetGameObjectAI(GameObject* go) +{ + ASSERT(go); + + GET_SCRIPT_RET(GameObjectScript, go->GetScriptId(), tmpscript, NULL); + return tmpscript->GetAI(go); +} + bool ScriptMgr::OnQuestAccept(Player* player, Item* item, Quest const* quest) { ASSERT(player); @@ -972,14 +980,6 @@ bool ScriptMgr::OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effInd return tmpscript->OnDummyEffect(caster, spellId, effIndex, target); } -GameObjectAI* ScriptMgr::GetGameObjectAI(GameObject* go) -{ - ASSERT(go); - - GET_SCRIPT_RET(GameObjectScript, go->GetScriptId(), tmpscript, NULL); - return tmpscript->GetAI(go); -} - bool ScriptMgr::OnAreaTrigger(Player* player, AreaTriggerEntry const* trigger) { ASSERT(player); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 6cb38a7e12a..78af675a348 100755 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -128,6 +128,16 @@ enum PartyResult ERR_PARTY_LFG_TELEPORT_IN_COMBAT = 30 }; + +enum BFLeaveReason +{ + BF_LEAVE_REASON_CLOSE = 0x00000001, + //BF_LEAVE_REASON_UNK1 = 0x00000002, (not used) + //BF_LEAVE_REASON_UNK2 = 0x00000004, (not used) + BF_LEAVE_REASON_EXITED = 0x00000008, + BF_LEAVE_REASON_LOW_LEVEL = 0x00000010, +}; + enum ChatRestrictionType { ERR_CHAT_RESTRICTED = 0, @@ -798,7 +808,16 @@ class WorldSession void HandleResetInstancesOpcode(WorldPacket& recvData); void HandleHearthAndResurrect(WorldPacket& recvData); void HandleInstanceLockResponse(WorldPacket& recvPacket); - void HandleUpdateMissileTrajectory(WorldPacket& recvPacket); + + // Battlefield + void SendBfInvitePlayerToWar(uint32 BattleId,uint32 ZoneId,uint32 time); + void SendBfInvitePlayerToQueue(uint32 BattleId); + void SendBfQueueInviteResponse(uint32 BattleId,uint32 ZoneId, bool CanQueue = true, bool Full = false); + void SendBfEntered(uint32 BattleId); + void SendBfLeaveMessage(uint32 BattleId, BFLeaveReason reason = BF_LEAVE_REASON_EXITED); + void HandleBfQueueInviteResponse(WorldPacket &recv_data); + void HandleBfEntryInviteResponse(WorldPacket &recv_data); + void HandleBfExitRequest(WorldPacket &recv_data); // Looking for Dungeon/Raid void HandleLfgSetCommentOpcode(WorldPacket& recvData); @@ -944,6 +963,7 @@ class WorldSession void HandleEnterPlayerVehicle(WorldPacket& data); void HandleUpdateProjectilePosition(WorldPacket& recvPacket); void HandleRequestHotfix(WorldPacket& recvPacket); + void HandleUpdateMissileTrajectory(WorldPacket& recvPacket); void HandleViolenceLevel(WorldPacket& recvPacket); void HandleObjectUpdateFailedOpcode(WorldPacket& recvPacket); int32 HandleEnableNagleAlgorithm(); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 62472652423..a25d8905f14 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -36,6 +36,8 @@ #include "CellImpl.h" #include "ScriptMgr.h" #include "Vehicle.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" class Aura; // @@ -4918,8 +4920,12 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool case 2584: // Waiting to Resurrect // Waiting to resurrect spell cancel, we must remove player from resurrect queue if (target->GetTypeId() == TYPEID_PLAYER) + { if (Battleground* bg = target->ToPlayer()->GetBattleground()) bg->RemovePlayerFromResurrectQueue(target->GetGUID()); + if(Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(target->GetZoneId())) + bf->RemovePlayerFromResurrectQueue(target->GetGUID()); + } break; case 36730: // Flame Strike { diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 448c9dd2176..97789626987 100755 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -54,6 +54,8 @@ #include "InstanceScript.h" #include "SpellInfo.h" #include "DB2Stores.h" +#include "Battlefield.h" +#include "BattlefieldMgr.h" extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -5437,6 +5439,7 @@ SpellCastResult Spell::CheckCast(bool strict) // allow always ghost flight spells if (m_originalCaster && m_originalCaster->GetTypeId() == TYPEID_PLAYER && m_originalCaster->isAlive()) { + Battlefield* Bf = sBattlefieldMgr->GetBattlefieldToZoneId(m_originalCaster->GetZoneId()); if (AreaTableEntry const* pArea = GetAreaEntryByAreaID(m_originalCaster->GetAreaId())) if (pArea->flags & AREA_FLAG_NO_FLY_ZONE) return (_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_NOT_HERE; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index c9f90bf9e1d..4cc72cc3660 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -30,6 +30,8 @@ #include "CreatureAI.h" #include "MapManager.h" #include "BattlegroundIC.h" +#include "BattlefieldWG.h" +#include "BattlefieldMgr.h" bool IsPrimaryProfessionSkill(uint32 skill) { @@ -1120,6 +1122,16 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 return false; break; } + case 58730: // No fly Zone - Wintergrasp + { + if (!player) + return false; + + Battlefield* Bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetZoneId()); + if (!Bf || Bf->CanFlyIn() || (!player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !player->HasAuraType(SPELL_AURA_FLY))) + return false; + break; + } case 68719: // Oil Refinery - Isle of Conquest. case 68720: // Quarry - Isle of Conquest. { @@ -1135,6 +1147,26 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 return false; } + case 56618: // Horde Controls Factory Phase Shift + case 56617: // Alliance Controls Factory Phase Shift + { + if (!player) + return false; + + Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetZoneId()); + + if (!bf || bf->GetTypeId() != BATTLEFIELD_WG) + return false; + + // team that controls the workshop in the specified area + uint32 team = bf->GetData(newArea); + + if (team == TEAM_HORDE) + return spellId == 56618; + else if (team == TEAM_ALLIANCE) + return spellId == 56617; + } + break; } return true; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 05a67a2090e..273820d02ff 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -77,6 +77,7 @@ #include "WardenCheckMgr.h" #include "Warden.h" #include "CalendarMgr.h" +#include "BattlefieldMgr.h" ACE_Atomic_Op<ACE_Thread_Mutex, bool> World::m_stopEvent = false; uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; @@ -1201,6 +1202,14 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_PDUMP_NO_OVERWRITE] = ConfigMgr::GetBoolDefault("PlayerDump.DisallowOverwrite", true); // call ScriptMgr if we're reloading the configuration + m_bool_configs[CONFIG_WINTERGRASP_ENABLE] = ConfigMgr::GetBoolDefault("Wintergrasp.Enable", false); + m_int_configs[CONFIG_WINTERGRASP_PLR_MAX] = ConfigMgr::GetIntDefault("Wintergrasp.PlayerMax", 100); + m_int_configs[CONFIG_WINTERGRASP_PLR_MIN] = ConfigMgr::GetIntDefault("Wintergrasp.PlayerMin", 0); + m_int_configs[CONFIG_WINTERGRASP_PLR_MIN_LVL] = ConfigMgr::GetIntDefault("Wintergrasp.PlayerMinLvl", 77); + m_int_configs[CONFIG_WINTERGRASP_BATTLETIME] = ConfigMgr::GetIntDefault("Wintergrasp.BattleTimer", 30); + m_int_configs[CONFIG_WINTERGRASP_NOBATTLETIME] = ConfigMgr::GetIntDefault("Wintergrasp.NoBattleTimer", 150); + m_int_configs[CONFIG_WINTERGRASP_RESTART_AFTER_CRASH] = ConfigMgr::GetIntDefault("Wintergrasp.CrashRestartTimer", 10); + if (reload) sScriptMgr->OnConfigLoad(reload); } @@ -1710,6 +1719,10 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Starting Outdoor PvP System"); sOutdoorPvPMgr->InitOutdoorPvP(); + ///- Initialize Battlefield + sLog->outInfo(LOG_FILTER_GENERAL, "Starting Battlefield System"); + sBattlefieldMgr->InitBattlefield(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Transports..."); sMapMgr->LoadTransports(); @@ -1979,6 +1992,9 @@ void World::Update(uint32 diff) sOutdoorPvPMgr->Update(diff); RecordTimeDiff("UpdateOutdoorPvPMgr"); + sBattlefieldMgr->Update(diff); + RecordTimeDiff("BattlefieldMgr"); + ///- Delete all characters which have been deleted X days before if (m_timers[WUPDATE_DELETECHARS].Passed()) { diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 65377eacf4b..841ae2530ca 100755 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -172,6 +172,7 @@ enum WorldBoolConfigs CONFIG_QUEST_IGNORE_AUTO_ACCEPT, CONFIG_QUEST_IGNORE_AUTO_COMPLETE, CONFIG_WARDEN_ENABLED, + CONFIG_WINTERGRASP_ENABLE, BOOL_CONFIG_VALUE_COUNT }; @@ -328,6 +329,12 @@ enum WorldIntConfigs CONFIG_WARDEN_CLIENT_BAN_DURATION, CONFIG_WARDEN_NUM_MEM_CHECKS, CONFIG_WARDEN_NUM_OTHER_CHECKS, + CONFIG_WINTERGRASP_PLR_MAX, + CONFIG_WINTERGRASP_PLR_MIN, + CONFIG_WINTERGRASP_PLR_MIN_LVL, + CONFIG_WINTERGRASP_BATTLETIME, + CONFIG_WINTERGRASP_NOBATTLETIME, + CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt index 6d0d8ec8189..3dfb5b39c24 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -79,6 +79,8 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/server/game/AI/ScriptedAI ${CMAKE_SOURCE_DIR}/src/server/game/AI/SmartScripts ${CMAKE_SOURCE_DIR}/src/server/game/AuctionHouse + ${CMAKE_SOURCE_DIR}/src/server/game/Battlefield + ${CMAKE_SOURCE_DIR}/src/server/game/Battlefield/Zones ${CMAKE_SOURCE_DIR}/src/server/game/Battlegrounds ${CMAKE_SOURCE_DIR}/src/server/game/Battlegrounds/Zones ${CMAKE_SOURCE_DIR}/src/server/game/Calendar diff --git a/src/server/scripts/Commands/cs_bf.cpp b/src/server/scripts/Commands/cs_bf.cpp new file mode 100644 index 00000000000..7284e6ad6b7 --- /dev/null +++ b/src/server/scripts/Commands/cs_bf.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/* ScriptData +Name: bf_commandscript +%Complete: 100 +Comment: All bf related commands +Category: commandscripts +EndScriptData */ + +#include "ScriptMgr.h" +#include "Chat.h" +#include "BattlefieldMgr.h" + +class bf_commandscript : public CommandScript +{ +public: + bf_commandscript() : CommandScript("bf_commandscript") { } + + ChatCommand* GetCommands() const + { + static ChatCommand battlefieldcommandTable[] = + { + { "start", SEC_ADMINISTRATOR, false, &HandleBattlefieldStart, "", NULL }, + { "stop", SEC_ADMINISTRATOR, false, &HandleBattlefieldEnd, "", NULL }, + { "switch", SEC_ADMINISTRATOR, false, &HandleBattlefieldSwitch, "", NULL }, + { "timer", SEC_ADMINISTRATOR, false, &HandleBattlefieldTimer, "", NULL }, + { "enable", SEC_ADMINISTRATOR, false, &HandleBattlefieldEnable, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand commandTable[] = + { + { "bf", SEC_ADMINISTRATOR, false, NULL, "", battlefieldcommandTable }, + { NULL, 0, false, NULL, "", NULL } + }; + return commandTable; + } + + static bool HandleBattlefieldStart(ChatHandler* handler, const char* args) + { + uint32 battleid = 0; + char* battleid_str = strtok((char*)args, " "); + if (!battleid_str) + return false; + + battleid = atoi(battleid_str); + + Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleid); + + if (!bf) + return false; + + bf->StartBattle(); + + if (battleid == 1) + handler->SendGlobalGMSysMessage("Wintergrasp (Command start used)"); + + return true; + } + + static bool HandleBattlefieldEnd(ChatHandler* handler, const char* args) + { + uint32 battleid = 0; + char* battleid_str = strtok((char*)args, " "); + if (!battleid_str) + return false; + + battleid = atoi(battleid_str); + + Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleid); + + if (!bf) + return false; + + bf->EndBattle(true); + + if (battleid == 1) + handler->SendGlobalGMSysMessage("Wintergrasp (Command stop used)"); + + return true; + } + + static bool HandleBattlefieldEnable(ChatHandler* handler, const char* args) + { + uint32 battleid = 0; + char* battleid_str = strtok((char*)args, " "); + if (!battleid_str) + return false; + + battleid = atoi(battleid_str); + + Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleid); + + if (!bf) + return false; + + if (bf->IsEnabled()) + { + bf->ToggleBattlefield(false); + if (battleid == 1) + handler->SendGlobalGMSysMessage("Wintergrasp is disabled"); + } + else + { + bf->ToggleBattlefield(true); + if (battleid == 1) + handler->SendGlobalGMSysMessage("Wintergrasp is enabled"); + } + + return true; + } + + static bool HandleBattlefieldSwitch(ChatHandler* handler, const char* args) + { + uint32 battleid = 0; + char* battleid_str = strtok((char*)args, " "); + if (!battleid_str) + return false; + + battleid = atoi(battleid_str); + + Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleid); + + if (!bf) + return false; + + bf->EndBattle(false); + if (battleid == 1) + handler->SendGlobalGMSysMessage("Wintergrasp (Command switch used)"); + + return true; + } + + static bool HandleBattlefieldTimer(ChatHandler* handler, const char* args) + { + uint32 battleid = 0; + uint32 time = 0; + char* battleid_str = strtok((char*)args, " "); + if (!battleid_str) + return false; + char* time_str = strtok(NULL, " "); + if (!time_str) + return false; + + battleid = atoi(battleid_str); + + time = atoi(time_str); + + Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleid); + + if (!bf) + return false; + + bf->SetTimer(time * IN_MILLISECONDS); + bf->SendInitWorldStatesToAll(); + if (battleid == 1) + handler->SendGlobalGMSysMessage("Wintergrasp (Command timer used)"); + + return true; + } +}; + +void AddSC_bf_commandscript() +{ + new bf_commandscript(); +} diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt index 3502e7fb104..dd8dd17c947 100644 --- a/src/server/scripts/Northrend/CMakeLists.txt +++ b/src/server/scripts/Northrend/CMakeLists.txt @@ -10,6 +10,7 @@ set(scripts_STAT_SRCS ${scripts_STAT_SRCS} + Northrend/wintergrasp.cpp Northrend/isle_of_conquest.cpp Northrend/storm_peaks.cpp Northrend/Ulduar/HallsOfLightning/instance_halls_of_lightning.cpp diff --git a/src/server/scripts/Northrend/wintergrasp.cpp b/src/server/scripts/Northrend/wintergrasp.cpp new file mode 100644 index 00000000000..eba5b7f69d4 --- /dev/null +++ b/src/server/scripts/Northrend/wintergrasp.cpp @@ -0,0 +1,561 @@ +/* Copyright (C) 2008 - 2009 Trinity <http://www.trinitycore.org/> + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattlefieldMgr.h" +#include "BattlefieldWG.h" +#include "Battlefield.h" +#include "ScriptSystem.h" +#include "WorldSession.h" +#include "ObjectMgr.h" +#include "Vehicle.h" +#include "GameObjectAI.h" + +#define GOSSIP_HELLO_DEMO1 "Build catapult." +#define GOSSIP_HELLO_DEMO2 "Build demolisher." +#define GOSSIP_HELLO_DEMO3 "Build siege engine." +#define GOSSIP_HELLO_DEMO4 "I cannot build more!" + +enum eWGqueuenpctext +{ + WG_NPCQUEUE_TEXT_H_NOWAR = 14775, + WG_NPCQUEUE_TEXT_H_QUEUE = 14790, + WG_NPCQUEUE_TEXT_H_WAR = 14777, + WG_NPCQUEUE_TEXT_A_NOWAR = 14782, + WG_NPCQUEUE_TEXT_A_QUEUE = 14791, + WG_NPCQUEUE_TEXT_A_WAR = 14781, + WG_NPCQUEUE_TEXTOPTION_JOIN = -1850507, +}; + +enum Spells +{ + // Demolisher engineers spells + SPELL_BUILD_SIEGE_VEHICLE_FORCE_HORDE = 61409, + SPELL_BUILD_SIEGE_VEHICLE_FORCE_ALLIANCE = 56662, + SPELL_BUILD_CATAPULT_FORCE = 56664, + SPELL_BUILD_DEMOLISHER_FORCE = 56659, + SPELL_ACTIVATE_CONTROL_ARMS = 49899, + SPELL_RIDE_WG_VEHICLE = 60968, + + SPELL_VEHICLE_TELEPORT = 49759, + + // Spirit guide + SPELL_CHANNEL_SPIRIT_HEAL = 22011, +}; + +enum CreatureIds +{ + NPC_GOBLIN_MECHANIC = 30400, + NPC_GNOMISH_ENGINEER = 30499, + + NPC_WINTERGRASP_CONTROL_ARMS = 27852, + + NPC_WORLD_TRIGGER_LARGE_AOI_NOT_IMMUNE_PC_NPC = 23472, +}; + +enum QuestIds +{ + QUEST_BONES_AND_ARROWS_HORDE_ATT = 13193, + QUEST_JINXING_THE_WALLS_HORDE_ATT = 13202, + QUEST_SLAY_THEM_ALL_HORDE_ATT = 13180, + QUEST_FUELING_THE_DEMOLISHERS_HORDE_ATT = 13200, + QUEST_HEALING_WITH_ROSES_HORDE_ATT = 13201, + QUEST_DEFEND_THE_SIEGE_HORDE_ATT = 13223, + + QUEST_BONES_AND_ARROWS_HORDE_DEF = 13199, + QUEST_WARDING_THE_WALLS_HORDE_DEF = 13192, + QUEST_SLAY_THEM_ALL_HORDE_DEF = 13178, + QUEST_FUELING_THE_DEMOLISHERS_HORDE_DEF = 13191, + QUEST_HEALING_WITH_ROSES_HORDE_DEF = 13194, + QUEST_TOPPLING_THE_TOWERS_HORDE_DEF = 13539, + QUEST_STOP_THE_SIEGE_HORDE_DEF = 13185, + + QUEST_BONES_AND_ARROWS_ALLIANCE_ATT = 13196, + QUEST_WARDING_THE_WARRIORS_ALLIANCE_ATT = 13198, + QUEST_NO_MERCY_FOR_THE_MERCILESS_ALLIANCE_ATT = 13179, + QUEST_DEFEND_THE_SIEGE_ALLIANCE_ATT = 13222, + QUEST_A_RARE_HERB_ALLIANCE_ATT = 13195, + + QUEST_BONES_AND_ARROWS_ALLIANCE_DEF = 13154, + QUEST_WARDING_THE_WARRIORS_ALLIANCE_DEF = 13153, + QUEST_NO_MERCY_FOR_THE_MERCILESS_ALLIANCE_DEF = 13177, + QUEST_SHOUTHERN_SABOTAGE_ALLIANCE_DEF = 13538, + QUEST_STOP_THE_SIEGE_ALLIANCE_DEF = 13186, + QUEST_A_RARE_HERB_ALLIANCE_DEF = 13156, +}; + +uint8 const MAX_WINTERGRASP_VEHICLES = 4; + +uint32 const vehiclesList[MAX_WINTERGRASP_VEHICLES] = { + NPC_WINTERGRASP_CATAPULT, + NPC_WINTERGRASP_DEMOLISHER, + NPC_WINTERGRASP_SIEGE_ENGINE_ALLIANCE, + NPC_WINTERGRASP_SIEGE_ENGINE_HORDE +}; + +class npc_wg_demolisher_engineer : public CreatureScript +{ + public: + npc_wg_demolisher_engineer() : CreatureScript("npc_wg_demolisher_engineer") { } + + bool OnGossipHello(Player* player, Creature* creature) + { + if (creature->isQuestGiver()) + player->PrepareQuestMenu(creature->GetGUID()); + + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + + if (canBuild(creature)) + { + if (player->HasAura(SPELL_CORPORAL)) + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_DEMO1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + else if (player->HasAura(SPELL_LIEUTENANT)) + { + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_DEMO1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_DEMO2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_DEMO3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + } + } + else + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_DEMO4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + + player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); + return true; + } + + bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender */ , uint32 action) + { + player->CLOSE_GOSSIP_MENU(); + + Battlefield* wintergrasp= sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + + if (canBuild(creature)) + { + switch (action - GOSSIP_ACTION_INFO_DEF) + { + case 0: + creature->CastSpell(player, SPELL_BUILD_CATAPULT_FORCE, true); + break; + case 1: + creature->CastSpell(player, SPELL_BUILD_DEMOLISHER_FORCE, true); + break; + case 2: + creature->CastSpell(player, player->GetTeamId() == TEAM_ALLIANCE ? SPELL_BUILD_SIEGE_VEHICLE_FORCE_ALLIANCE : SPELL_BUILD_SIEGE_VEHICLE_FORCE_HORDE, true); + break; + } + if (Creature* controlArms = creature->FindNearestCreature(NPC_WINTERGRASP_CONTROL_ARMS, 30.0f, true)) + creature->CastSpell(controlArms, SPELL_ACTIVATE_CONTROL_ARMS, true); + } + return true; + } + + private: + bool canBuild(Creature* creature) + { + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + + if (!wintergrasp) + return false; + switch (creature->GetEntry()) + { + case NPC_GOBLIN_MECHANIC: + return (wintergrasp->GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_H) > wintergrasp->GetData(BATTLEFIELD_WG_DATA_VEHICLE_H)); + case NPC_GNOMISH_ENGINEER: + return (wintergrasp->GetData(BATTLEFIELD_WG_DATA_MAX_VEHICLE_A) > wintergrasp->GetData(BATTLEFIELD_WG_DATA_VEHICLE_A)); + default: + return false; + } + } +}; + +class npc_wg_spirit_guide : public CreatureScript +{ + public: + npc_wg_spirit_guide() : CreatureScript("npc_wg_spirit_guide") { } + + bool OnGossipHello(Player* player, Creature* creature) + { + if (creature->isQuestGiver()) + player->PrepareQuestMenu(creature->GetGUID()); + + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + if (!wintergrasp) + return true; + + GraveyardVect graveyard = wintergrasp->GetGraveyardVector(); + for (uint8 i = 0; i < graveyard.size(); i++) + if (graveyard[i]->GetControlTeamId() == player->GetTeamId()) + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, sObjectMgr->GetTrinityStringForDBCLocale(((BfGraveyardWG*)graveyard[i])->GetTextId()), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + i); + + player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); + return true; + } + + bool OnGossipSelect(Player* player, Creature* /*creature */ , uint32 /*sender */ , uint32 action) + { + player->CLOSE_GOSSIP_MENU(); + + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + if (wintergrasp) + { + GraveyardVect gy = wintergrasp->GetGraveyardVector(); + for (uint8 i = 0; i < gy.size(); i++) + if (action - GOSSIP_ACTION_INFO_DEF == i && gy[i]->GetControlTeamId() == player->GetTeamId()) + if (WorldSafeLocsEntry const* safeLoc = sWorldSafeLocsStore.LookupEntry(gy[i]->GetGraveyardId())) + player->TeleportTo(safeLoc->map_id, safeLoc->x, safeLoc->y, safeLoc->z, 0); + } + return true; + } + + struct npc_wg_spirit_guideAI : public ScriptedAI + { + npc_wg_spirit_guideAI(Creature* creature) : ScriptedAI(creature) + { } + + void UpdateAI(const uint32 /* diff */) + { + if (!me->HasUnitState(UNIT_STATE_CASTING)) + DoCast(me, SPELL_CHANNEL_SPIRIT_HEAL); + } + }; + + CreatureAI *GetAI(Creature* creature) const + { + return new npc_wg_spirit_guideAI(creature); + } +}; + +class npc_wg_queue : public CreatureScript +{ + public: + npc_wg_queue() : CreatureScript("npc_wg_queue") { } + + bool OnGossipHello(Player* player, Creature* creature) + { + if (creature->isQuestGiver()) + player->PrepareQuestMenu(creature->GetGUID()); + + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + if (!wintergrasp) + return true; + + if (wintergrasp->IsWarTime()) + { + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, sObjectMgr->GetTrinityStringForDBCLocale(WG_NPCQUEUE_TEXTOPTION_JOIN), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(wintergrasp->GetDefenderTeam()? WG_NPCQUEUE_TEXT_H_WAR : WG_NPCQUEUE_TEXT_A_WAR, creature->GetGUID()); + } + else + { + uint32 timer = wintergrasp->GetTimer() / 1000; + player->SendUpdateWorldState(4354, time(NULL) + timer); + if (timer < 15 * MINUTE) + { + player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, sObjectMgr->GetTrinityStringForDBCLocale(WG_NPCQUEUE_TEXTOPTION_JOIN), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(wintergrasp->GetDefenderTeam() ? WG_NPCQUEUE_TEXT_H_QUEUE : WG_NPCQUEUE_TEXT_A_QUEUE, creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(wintergrasp->GetDefenderTeam() ? WG_NPCQUEUE_TEXT_H_NOWAR : WG_NPCQUEUE_TEXT_A_NOWAR, creature->GetGUID()); + } + return true; + } + + bool OnGossipSelect(Player* player, Creature* /*creature */ , uint32 /*sender */ , uint32 /*action*/) + { + player->CLOSE_GOSSIP_MENU(); + + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + if (!wintergrasp) + return true; + + if (wintergrasp->IsWarTime()) + wintergrasp->InvitePlayerToWar(player); + else + { + uint32 timer = wintergrasp->GetTimer() / 1000; + if (timer < 15 * MINUTE) + wintergrasp->InvitePlayerToQueue(player); + } + return true; + } +}; + +class go_wg_vehicle_teleporter : public GameObjectScript +{ + public: + go_wg_vehicle_teleporter() : GameObjectScript("go_wg_vehicle_teleporter") { } + + struct go_wg_vehicle_teleporterAI : public GameObjectAI + { + go_wg_vehicle_teleporterAI(GameObject* gameObject) : GameObjectAI(gameObject), + _checkTimer(1000) + { } + + void UpdateAI(uint32 diff) + { + if (_checkTimer <= diff) + { + // Tabulation madness in the hole! + for (uint8 i = 0; i < MAX_WINTERGRASP_VEHICLES; i++) + if (Creature* vehicleCreature = go->FindNearestCreature(vehiclesList[i], 3.0f, true)) + if (!vehicleCreature->HasAura(SPELL_VEHICLE_TELEPORT)) + if (Vehicle* vehicle = vehicleCreature->GetVehicle()) + if (Unit* passenger = vehicle->GetPassenger(0)) + if (go->GetUInt32Value(GAMEOBJECT_FACTION) == passenger->getFaction()) + if (Creature* teleportTrigger = vehicleCreature->FindNearestCreature(NPC_WORLD_TRIGGER_LARGE_AOI_NOT_IMMUNE_PC_NPC, 100.0f, true)) + teleportTrigger->CastSpell(vehicleCreature, SPELL_VEHICLE_TELEPORT, true); + + _checkTimer = 1000; + } + else _checkTimer -= diff; + } + private: + uint32 _checkTimer; + }; + + GameObjectAI* GetGameObjectAI(GameObject* go) const + { + return new go_wg_vehicle_teleporterAI(go); + } +}; + +class npc_wg_quest_giver : public CreatureScript +{ + public: + npc_wg_quest_giver() : CreatureScript("npc_wg_quest_giver") { } + + bool OnGossipHello(Player* player, Creature* creature) + { + if (creature->isQuestGiver()) + player->PrepareQuestMenu(creature->GetGUID()); + + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + if (!wintergrasp) + return true; + + if (creature->isQuestGiver()) + { + QuestRelationBounds objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry()); + QuestRelationBounds objectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(creature->GetEntry()); + + QuestMenu& qm = player->PlayerTalkClass->GetQuestMenu(); + qm.ClearMenu(); + + for (QuestRelations::const_iterator i = objectQIR.first; i != objectQIR.second; ++i) + { + uint32 quest_id = i->second; + QuestStatus status = player->GetQuestStatus(quest_id); + if (status == QUEST_STATUS_COMPLETE) + qm.AddMenuItem(quest_id, 4); + else if (status == QUEST_STATUS_INCOMPLETE) + qm.AddMenuItem(quest_id, 4); + //else if (status == QUEST_STATUS_AVAILABLE) + // qm.AddMenuItem(quest_id, 2); + } + + for (QuestRelations::const_iterator i = objectQR.first; i != objectQR.second; ++i) + { + uint32 questId = i->second; + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); + if (!quest) + continue; + + switch (questId) + { + // Horde attacker + case QUEST_BONES_AND_ARROWS_HORDE_ATT: + case QUEST_JINXING_THE_WALLS_HORDE_ATT: + case QUEST_SLAY_THEM_ALL_HORDE_ATT: + case QUEST_FUELING_THE_DEMOLISHERS_HORDE_ATT: + case QUEST_HEALING_WITH_ROSES_HORDE_ATT: + case QUEST_DEFEND_THE_SIEGE_HORDE_ATT: + if (wintergrasp->GetAttackerTeam() == TEAM_HORDE) + { + QuestStatus status = player->GetQuestStatus(questId); + + if (quest->IsAutoComplete() && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 4); + else if (status == QUEST_STATUS_NONE && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 2); + } + break; + // Horde defender + case QUEST_BONES_AND_ARROWS_HORDE_DEF: + case QUEST_WARDING_THE_WALLS_HORDE_DEF: + case QUEST_SLAY_THEM_ALL_HORDE_DEF: + case QUEST_FUELING_THE_DEMOLISHERS_HORDE_DEF: + case QUEST_HEALING_WITH_ROSES_HORDE_DEF: + case QUEST_TOPPLING_THE_TOWERS_HORDE_DEF: + case QUEST_STOP_THE_SIEGE_HORDE_DEF: + if (wintergrasp->GetDefenderTeam() == TEAM_HORDE) + { + QuestStatus status = player->GetQuestStatus(questId); + + if (quest->IsAutoComplete() && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 4); + else if (status == QUEST_STATUS_NONE && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 2); + } + break; + // Alliance attacker + case QUEST_BONES_AND_ARROWS_ALLIANCE_ATT: + case QUEST_WARDING_THE_WARRIORS_ALLIANCE_ATT: + case QUEST_NO_MERCY_FOR_THE_MERCILESS_ALLIANCE_ATT: + case QUEST_DEFEND_THE_SIEGE_ALLIANCE_ATT: + case QUEST_A_RARE_HERB_ALLIANCE_ATT: + if (wintergrasp->GetAttackerTeam() == TEAM_ALLIANCE) + { + QuestStatus status = player->GetQuestStatus(questId); + + if (quest->IsAutoComplete() && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 4); + else if (status == QUEST_STATUS_NONE && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 2); + } + break; + // Alliance defender + case QUEST_BONES_AND_ARROWS_ALLIANCE_DEF: + case QUEST_WARDING_THE_WARRIORS_ALLIANCE_DEF: + case QUEST_NO_MERCY_FOR_THE_MERCILESS_ALLIANCE_DEF: + case QUEST_SHOUTHERN_SABOTAGE_ALLIANCE_DEF: + case QUEST_STOP_THE_SIEGE_ALLIANCE_DEF: + case QUEST_A_RARE_HERB_ALLIANCE_DEF: + if (wintergrasp->GetDefenderTeam() == TEAM_ALLIANCE) + { + QuestStatus status = player->GetQuestStatus(questId); + + if (quest->IsAutoComplete() && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 4); + else if (status == QUEST_STATUS_NONE && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 2); + } + break; + default: + QuestStatus status = player->GetQuestStatus(questId); + + if (quest->IsAutoComplete() && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 4); + else if (status == QUEST_STATUS_NONE && player->CanTakeQuest(quest, false)) + qm.AddMenuItem(questId, 2); + break; + } + } + } + player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); + return true; + } +}; + +class spell_wintergrasp_force_building : public SpellScriptLoader +{ + public: + spell_wintergrasp_force_building() : SpellScriptLoader("spell_wintergrasp_force_building") { } + + class spell_wintergrasp_force_building_SpellScript : public SpellScript + { + PrepareSpellScript(spell_wintergrasp_force_building_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_BUILD_CATAPULT_FORCE) + || !sSpellMgr->GetSpellInfo(SPELL_BUILD_DEMOLISHER_FORCE) + || !sSpellMgr->GetSpellInfo(SPELL_BUILD_SIEGE_VEHICLE_FORCE_HORDE) + || !sSpellMgr->GetSpellInfo(SPELL_BUILD_SIEGE_VEHICLE_FORCE_ALLIANCE)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetHitUnit()->CastSpell(GetHitUnit(), GetEffectValue(), false); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_force_building_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_wintergrasp_force_building_SpellScript(); + } +}; + +class spell_wintergrasp_grab_passenger : public SpellScriptLoader +{ + public: + spell_wintergrasp_grab_passenger() : SpellScriptLoader("spell_wintergrasp_grab_passenger") { } + + class spell_wintergrasp_grab_passenger_SpellScript : public SpellScript + { + PrepareSpellScript(spell_wintergrasp_grab_passenger_SpellScript); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Player* target = GetHitPlayer()) + target->CastSpell(GetCaster(), SPELL_RIDE_WG_VEHICLE, true); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_grab_passenger_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_wintergrasp_grab_passenger_SpellScript(); + } +}; + +class achievement_wg_didnt_stand_a_chance : public AchievementCriteriaScript +{ +public: + achievement_wg_didnt_stand_a_chance() : AchievementCriteriaScript("achievement_wg_didnt_stand_a_chance") { } + + bool OnCheck(Player* source, Unit* target) + { + if (!target) + return false; + + if (Player* victim = target->ToPlayer()) + { + if (!victim->IsMounted()) + return false; + + if (Vehicle* vehicle = source->GetVehicle()) + if (vehicle->GetVehicleInfo()->m_ID == 244) // Wintergrasp Tower Cannon + return true; + } + + return false; + } +}; + + + +void AddSC_wintergrasp() +{ + new npc_wg_queue(); + new npc_wg_spirit_guide(); + new npc_wg_demolisher_engineer(); + new npc_wg_quest_giver(); + new achievement_wg_didnt_stand_a_chance(); + new spell_wintergrasp_force_building(); + new spell_wintergrasp_grab_passenger(); + + new go_wg_vehicle_teleporter(); +} |