/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "OutdoorPvPNA.h"
#include "CellImpl.h"
#include "Creature.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "Map.h"
#include "ObjectMgr.h"
#include "PhasingHandler.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "WorldStatePackets.h"
uint32 const NA_CREDIT_MARKER = 24867; // kill credit for pks
uint32 const NA_GUARDS_MAX = 15;
uint32 const NA_BUFF_ZONE = 3518;
uint32 const NA_HALAA_GRAVEYARD_ZONE = 3518; // need to add zone id, not area id
uint32 const NA_RESPAWN_TIME = 3600000; // one hour to capture after defeating all guards
uint32 const NA_GUARD_CHECK_TIME = 500; // every half second
uint32 const FLIGHT_NODES_NUM = 4;
uint32 const FlightPathStartNodes[FLIGHT_NODES_NUM] = { 103, 105, 107, 109 };
uint32 const FlightPathEndNodes[FLIGHT_NODES_NUM] = { 104, 106, 108, 110 };
OutdoorPvPNA::OutdoorPvPNA(Map* map) : OutdoorPvP(map)
{
m_TypeId = OUTDOOR_PVP_NA;
ControlZoneHandlers[182210] = std::make_unique(this);
}
void OutdoorPvPNA::OnGameObjectCreate(GameObject* go)
{
switch (go->GetEntry())
{
case 182210:
m_obj->SetControlZoneGUID(go->GetGUID());
break;
}
OutdoorPvP::OnGameObjectCreate(go);
}
void OutdoorPvPNA::HandleKillImpl(Player* player, Unit* killed)
{
if (killed->GetTypeId() == TYPEID_PLAYER && player->GetTeam() != killed->ToPlayer()->GetTeam())
{
player->KilledMonsterCredit(NA_CREDIT_MARKER); // 0 guid, btw it isn't even used in killedmonster function :S
if (player->GetTeam() == ALLIANCE)
player->CastSpell(player, NA_KILL_TOKEN_ALLIANCE, true);
else if (player->GetTeam() == HORDE)
player->CastSpell(player, NA_KILL_TOKEN_HORDE, true);
}
}
NAControlZoneHandler::NAControlZoneHandler(OutdoorPvPNA* pvp) : OutdoorPvPControlZoneHandler(pvp)
{
}
void NAControlZoneHandler::HandleCaptureEventHorde(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleCaptureEventHorde(controlZone);
if (GetOutdoorPvPNA()->GetCapturePoint()->GetControllingFaction() != HORDE)
{
GetOutdoorPvPNA()->SendMapWorldStates(0, 0, 0, 1, 0);
GetOutdoorPvPNA()->GetCapturePoint()->FactionTakeOver(HORDE);
}
}
void NAControlZoneHandler::HandleCaptureEventAlliance(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleCaptureEventAlliance(controlZone);
if (GetOutdoorPvPNA()->GetCapturePoint()->GetControllingFaction() != ALLIANCE)
{
GetOutdoorPvPNA()->SendMapWorldStates(0, 0, 0, 0, 1);
GetOutdoorPvPNA()->GetCapturePoint()->FactionTakeOver(ALLIANCE);
}
}
void NAControlZoneHandler::HandleContestedEventHorde(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleContestedEventHorde(controlZone);
GetOutdoorPvPNA()->SendMapWorldStates(0, 1, 0, 0, 0);
}
void NAControlZoneHandler::HandleContestedEventAlliance(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleContestedEventAlliance(controlZone);
GetOutdoorPvPNA()->SendMapWorldStates(0, 0, 1, 0, 0);
}
void NAControlZoneHandler::HandleProgressEventHorde(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleProgressEventHorde(controlZone);
controlZone->SetGoArtKit(1);
GetOutdoorPvPNA()->SendMapWorldStates(0, 1, 0, 0, 0);
}
void NAControlZoneHandler::HandleProgressEventAlliance(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleProgressEventAlliance(controlZone);
controlZone->SetGoArtKit(2);
GetOutdoorPvPNA()->SendMapWorldStates(0, 0, 1, 0, 0);
}
void NAControlZoneHandler::HandleNeutralEventHorde(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleNeutralEventHorde(controlZone);
}
void NAControlZoneHandler::HandleNeutralEventAlliance(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleNeutralEventAlliance(controlZone);
}
void NAControlZoneHandler::HandleNeutralEvent(GameObject* controlZone)
{
OutdoorPvPControlZoneHandler::HandleNeutralEvent(controlZone);
}
OutdoorPvPNA* NAControlZoneHandler::GetOutdoorPvPNA() const
{
return static_cast(OutdoorPvPControlZoneHandler::GetOutdoorPvP());
}
void OutdoorPvPNA::SendMapWorldStates(int32 neutral, int32 progressHorde, int32 progressAlliance, int32 capturedHorde, int32 captureAlliance)
{
SetWorldState(NA_MAP_HALAA_NEUTRAL, neutral);
SetWorldState(NA_MAP_HALAA_NEU_H, progressHorde);
SetWorldState(NA_MAP_HALAA_NEU_A, progressAlliance);
SetWorldState(NA_MAP_HALAA_HORDE, capturedHorde);
SetWorldState(NA_MAP_HALAA_ALLIANCE, captureAlliance);
}
uint32 OPvPCapturePointNA::GetAliveGuardsCount() const
{
Position searchCenter = { -1572.57f, 7945.3f, -22.475f, 2.05949f };
std::vector guards;
Trinity::ObjectEntryAndPrivateOwnerIfExistsCheck check(ObjectGuid::Empty, GetControllingFaction() == HORDE ? 18192 : 18256);
Trinity::CreatureListSearcher searcher(PhasingHandler::GetEmptyPhaseShift(), guards, check);
Cell::VisitGridObjects(searchCenter.GetPositionX(), searchCenter.GetPositionY(), m_PvP->GetMap(), searcher, SIZE_OF_GRIDS);
return std::ranges::count_if(guards, [](Creature const* guard) { return guard->IsAlive(); });
}
Team OPvPCapturePointNA::GetControllingFaction() const
{
return m_ControllingFaction;
}
void OPvPCapturePointNA::FactionTakeOver(Team team)
{
m_ControllingFaction = team;
m_GuardsAlive = NA_GUARDS_MAX;
m_capturable = false;
if (GameObject* gameObject = m_PvP->GetMap()->GetGameObject(_controlZoneGUID))
gameObject->ActivateObject(GameObjectActions::MakeInert, 0);
if (team == ALLIANCE)
{
m_WyvernStateSouth = WYVERN_NEU_HORDE;
m_WyvernStateNorth = WYVERN_NEU_HORDE;
m_WyvernStateEast = WYVERN_NEU_HORDE;
m_WyvernStateWest = WYVERN_NEU_HORDE;
m_PvP->TeamApplyBuff(TEAM_ALLIANCE, NA_CAPTURE_BUFF);
m_PvP->SetWorldState(NA_UI_HORDE_GUARDS_SHOW, 0);
m_PvP->SetWorldState(NA_UI_ALLIANCE_GUARDS_SHOW, 1);
m_PvP->SetWorldState(NA_UI_GUARDS_LEFT, m_GuardsAlive);
m_PvP->SendDefenseMessage(NA_HALAA_GRAVEYARD_ZONE, TEXT_HALAA_TAKEN_ALLIANCE);
}
else
{
m_WyvernStateSouth = WYVERN_NEU_ALLIANCE;
m_WyvernStateNorth = WYVERN_NEU_ALLIANCE;
m_WyvernStateEast = WYVERN_NEU_ALLIANCE;
m_WyvernStateWest = WYVERN_NEU_ALLIANCE;
m_PvP->TeamApplyBuff(TEAM_HORDE, NA_CAPTURE_BUFF);
m_PvP->SetWorldState(NA_UI_HORDE_GUARDS_SHOW, 1);
m_PvP->SetWorldState(NA_UI_ALLIANCE_GUARDS_SHOW, 0);
m_PvP->SetWorldState(NA_UI_GUARDS_LEFT, m_GuardsAlive);
m_PvP->SendDefenseMessage(NA_HALAA_GRAVEYARD_ZONE, TEXT_HALAA_TAKEN_HORDE);
}
UpdateWyvernRoostWorldState(NA_ROOST_S);
UpdateWyvernRoostWorldState(NA_ROOST_N);
UpdateWyvernRoostWorldState(NA_ROOST_W);
UpdateWyvernRoostWorldState(NA_ROOST_E);
}
OPvPCapturePointNA::OPvPCapturePointNA(OutdoorPvP* pvp) : OPvPCapturePoint(pvp), m_capturable(true), m_GuardsAlive(0), m_ControllingFaction(TEAM_OTHER), m_WyvernStateNorth(0), m_WyvernStateSouth(0), m_WyvernStateEast(0),
m_WyvernStateWest(0), m_RespawnTimer(NA_RESPAWN_TIME), m_GuardCheckTimer(NA_GUARD_CHECK_TIME)
{
}
bool OutdoorPvPNA::SetupOutdoorPvP()
{
// m_TypeId = OUTDOOR_PVP_NA; _MUST_ be set in ctor, because of spawns cleanup
// add the zones affected by the pvp buff
RegisterZone(NA_BUFF_ZONE);
// halaa
m_obj = std::make_unique(this);
return true;
}
void OutdoorPvPNA::HandlePlayerEnterZone(Player* player, uint32 zone)
{
// add buffs
if (player->GetTeam() == m_obj->GetControllingFaction())
player->CastSpell(player, NA_CAPTURE_BUFF, true);
OutdoorPvP::HandlePlayerEnterZone(player, zone);
}
void OutdoorPvPNA::HandlePlayerLeaveZone(Player* player, uint32 zone)
{
// remove buffs
player->RemoveAurasDueToSpell(NA_CAPTURE_BUFF);
OutdoorPvP::HandlePlayerLeaveZone(player, zone);
}
void OutdoorPvPNA::SendRemoveWorldStates(Player* player)
{
WorldPackets::WorldState::InitWorldStates initWorldStates;
initWorldStates.MapID = player->GetMapId();
initWorldStates.AreaID = player->GetZoneId();
initWorldStates.SubareaID = player->GetAreaId();
initWorldStates.Worldstates.emplace_back(NA_UI_HORDE_GUARDS_SHOW, 0);
initWorldStates.Worldstates.emplace_back(NA_UI_ALLIANCE_GUARDS_SHOW, 0);
initWorldStates.Worldstates.emplace_back(NA_UI_GUARDS_MAX, 0);
initWorldStates.Worldstates.emplace_back(NA_UI_GUARDS_LEFT, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_NORTH_NEU_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_NORTH_NEU_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_NORTH_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_NORTH_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_SOUTH_NEU_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_SOUTH_NEU_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_SOUTH_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_SOUTH_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_WEST_NEU_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_WEST_NEU_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_WEST_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_WEST_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_EAST_NEU_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_EAST_NEU_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_EAST_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_WYVERN_EAST_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_HALAA_NEUTRAL, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_HALAA_NEU_A, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_HALAA_NEU_H, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_HALAA_HORDE, 0);
initWorldStates.Worldstates.emplace_back(NA_MAP_HALAA_ALLIANCE, 0);
player->SendDirectMessage(initWorldStates.Write());
}
void OutdoorPvPNA::Update(uint32 diff)
{
m_obj->Update(diff);
}
bool OPvPCapturePointNA::HandleCustomSpell(Player* player, uint32 spellId, GameObject* /*go*/)
{
std::vector nodes;
nodes.resize(2);
bool retval = false;
switch (spellId)
{
case NA_SPELL_FLY_NORTH:
nodes[0] = FlightPathStartNodes[NA_ROOST_N];
nodes[1] = FlightPathEndNodes[NA_ROOST_N];
player->ActivateTaxiPathTo(nodes);
player->SetPlayerFlag(PLAYER_FLAGS_IN_PVP);
player->UpdatePvP(true, true);
retval = true;
break;
case NA_SPELL_FLY_SOUTH:
nodes[0] = FlightPathStartNodes[NA_ROOST_S];
nodes[1] = FlightPathEndNodes[NA_ROOST_S];
player->ActivateTaxiPathTo(nodes);
player->SetPlayerFlag(PLAYER_FLAGS_IN_PVP);
player->UpdatePvP(true, true);
retval = true;
break;
case NA_SPELL_FLY_WEST:
nodes[0] = FlightPathStartNodes[NA_ROOST_W];
nodes[1] = FlightPathEndNodes[NA_ROOST_W];
player->ActivateTaxiPathTo(nodes);
player->SetPlayerFlag(PLAYER_FLAGS_IN_PVP);
player->UpdatePvP(true, true);
retval = true;
break;
case NA_SPELL_FLY_EAST:
nodes[0] = FlightPathStartNodes[NA_ROOST_E];
nodes[1] = FlightPathEndNodes[NA_ROOST_E];
player->ActivateTaxiPathTo(nodes);
player->SetPlayerFlag(PLAYER_FLAGS_IN_PVP);
player->UpdatePvP(true, true);
retval = true;
break;
default:
break;
}
if (retval)
{
//Adding items
uint32 noSpaceForCount = 0;
// check space and find places
ItemPosCountVec dest;
int32 count = 10;
uint32 itemid = 24538;
// bomb id count
InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemid, count, &noSpaceForCount);
if (msg != EQUIP_ERR_OK) // convert to possible store amount
count -= noSpaceForCount;
if (count == 0 || dest.empty()) // can't add any
{
return true;
}
Item* item = player->StoreNewItem(dest, itemid, true);
if (count > 0 && item)
{
player->SendNewItem(item, count, true, false);
}
return true;
}
return false;
}
int32 OPvPCapturePointNA::HandleOpenGo(Player* player, GameObject* go)
{
switch (go->GetEntry())
{
case 182222:
m_WyvernStateSouth = WYVERN_NEU_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_S);
break;
case 182272:
m_WyvernStateWest = WYVERN_NEU_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_W);
break;
case 182273:
m_WyvernStateNorth = WYVERN_NEU_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_N);
break;
case 182274:
m_WyvernStateEast = WYVERN_NEU_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_E);
break;
case 182266:
m_WyvernStateSouth = WYVERN_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_S);
break;
case 182275:
m_WyvernStateWest = WYVERN_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_W);
break;
case 182276:
m_WyvernStateNorth = WYVERN_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_N);
break;
case 182277:
m_WyvernStateEast = WYVERN_ALLIANCE;
UpdateWyvernRoostWorldState(NA_ROOST_E);
break;
case 182305:
m_WyvernStateSouth = WYVERN_NEU_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_S);
break;
case 182306:
m_WyvernStateWest = WYVERN_NEU_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_W);
break;
case 182307:
m_WyvernStateNorth = WYVERN_NEU_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_N);
break;
case 182308:
m_WyvernStateEast = WYVERN_NEU_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_E);
break;
case 182297:
m_WyvernStateSouth = WYVERN_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_S);
break;
case 182298:
m_WyvernStateWest = WYVERN_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_W);
break;
case 182299:
m_WyvernStateNorth = WYVERN_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_N);
break;
case 182300:
m_WyvernStateEast = WYVERN_HORDE;
UpdateWyvernRoostWorldState(NA_ROOST_E);
break;
default:
return OPvPCapturePoint::HandleOpenGo(player, go);
}
return 0;
}
void OPvPCapturePointNA::Update(uint32 diff)
{
if (m_GuardCheckTimer < diff)
{
m_GuardCheckTimer = NA_GUARD_CHECK_TIME;
m_GuardsAlive = GetAliveGuardsCount();
if (m_GuardsAlive == 0)
{
m_capturable = true;
m_RespawnTimer = NA_RESPAWN_TIME;
if (GameObject* gameObject = m_PvP->GetMap()->GetGameObject(_controlZoneGUID))
gameObject->ActivateObject(GameObjectActions::MakeActive, 0);
}
else if (GameObject* gameObject = m_PvP->GetMap()->GetGameObject(_controlZoneGUID))
gameObject->ActivateObject(GameObjectActions::MakeInert, 0);
// update the guard count for the players in zone
m_PvP->SetWorldState(NA_UI_GUARDS_LEFT, m_GuardsAlive);
}
else
m_GuardCheckTimer -= diff;
if (m_capturable)
{
if (m_RespawnTimer < diff)
{
// if the guards have been killed, then the challenger has one hour to take over halaa.
// in case they fail to do it, the guards are respawned, and they have to start again.
if (m_ControllingFaction)
{
FactionTakeOver(m_ControllingFaction);
m_PvP->GetMap()->SpawnGroupSpawn(m_ControllingFaction == HORDE ? SPAWN_GROUP_HORDE_GUARDS : SPAWN_GROUP_ALLIANCE_GUARDS, true);
}
}
else
m_RespawnTimer -= diff;
}
if (m_capturable)
OPvPCapturePoint::Update(diff);
}
void OPvPCapturePointNA::UpdateWyvernRoostWorldState(uint32 roost)
{
switch (roost)
{
case NA_ROOST_S:
m_PvP->SetWorldState(NA_MAP_WYVERN_SOUTH_NEU_H, uint32((m_WyvernStateSouth & WYVERN_NEU_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_SOUTH_NEU_A, uint32((m_WyvernStateSouth & WYVERN_NEU_ALLIANCE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_SOUTH_H, uint32((m_WyvernStateSouth & WYVERN_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_SOUTH_A, uint32((m_WyvernStateSouth & WYVERN_ALLIANCE) != 0));
break;
case NA_ROOST_N:
m_PvP->SetWorldState(NA_MAP_WYVERN_NORTH_NEU_H, uint32((m_WyvernStateNorth & WYVERN_NEU_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_NORTH_NEU_A, uint32((m_WyvernStateNorth & WYVERN_NEU_ALLIANCE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_NORTH_H, uint32((m_WyvernStateNorth & WYVERN_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_NORTH_A, uint32((m_WyvernStateNorth & WYVERN_ALLIANCE) != 0));
break;
case NA_ROOST_W:
m_PvP->SetWorldState(NA_MAP_WYVERN_WEST_NEU_H, uint32((m_WyvernStateWest & WYVERN_NEU_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_WEST_NEU_A, uint32((m_WyvernStateWest & WYVERN_NEU_ALLIANCE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_WEST_H, uint32((m_WyvernStateWest & WYVERN_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_WEST_A, uint32((m_WyvernStateWest & WYVERN_ALLIANCE) != 0));
break;
case NA_ROOST_E:
m_PvP->SetWorldState(NA_MAP_WYVERN_EAST_NEU_H, uint32((m_WyvernStateEast & WYVERN_NEU_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_EAST_NEU_A, uint32((m_WyvernStateEast & WYVERN_NEU_ALLIANCE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_EAST_H, uint32((m_WyvernStateEast & WYVERN_HORDE) != 0));
m_PvP->SetWorldState(NA_MAP_WYVERN_EAST_A, uint32((m_WyvernStateEast & WYVERN_ALLIANCE) != 0));
break;
}
}
class OutdoorPvP_nagrand : public OutdoorPvPScript
{
public:
OutdoorPvP_nagrand() : OutdoorPvPScript("outdoorpvp_na") { }
OutdoorPvP* GetOutdoorPvP(Map* map) const override
{
return new OutdoorPvPNA(map);
}
};
void AddSC_outdoorpvp_na()
{
new OutdoorPvP_nagrand();
}