diff options
Diffstat (limited to 'src/server/scripts')
31 files changed, 6434 insertions, 74 deletions
diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/alterac_valley.cpp b/src/server/scripts/Battlegrounds/AlteracValley/alterac_valley.cpp index 629be86a525..94af6a25a53 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/alterac_valley.cpp +++ b/src/server/scripts/Battlegrounds/AlteracValley/alterac_valley.cpp @@ -15,8 +15,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "alterac_valley.h" #include "ScriptMgr.h" -#include "BattlegroundAV.h" +#include "Battleground.h" #include "GameObject.h" #include "GameObjectAI.h" #include "Player.h" @@ -192,10 +193,36 @@ public: } }; +template<AlteracValleySharedActions ActionId> +class quest_alterac_valley : public QuestScript +{ +public: + quest_alterac_valley(char const* scriptName) : QuestScript(scriptName) { } + + void OnQuestStatusChange(Player* player, Quest const* /*quest*/, QuestStatus /*oldStatus*/, QuestStatus newStatus) override + { + if (newStatus != QUEST_STATUS_REWARDED) + return; + + if (ZoneScript* zoneScript = player->FindZoneScript()) + zoneScript->DoAction(ActionId, player, player); + } +}; + void AddSC_alterac_valley() { RegisterCreatureAI(npc_av_marshal_or_warmaster); RegisterGameObjectAI(go_av_capturable_object); RegisterGameObjectAI(go_av_contested_object); new at_av_exploit(); + new quest_alterac_valley<ACTION_TURN_IN_SCRAPS>("quest_alterac_valley_armor_scraps"); + new quest_alterac_valley<ACTION_TURN_IN_COMMANDER_1>("quest_alterac_valley_call_of_air_slidore_guse"); + new quest_alterac_valley<ACTION_TURN_IN_COMMANDER_2>("quest_alterac_valley_call_of_air_vipore_jeztor"); + new quest_alterac_valley<ACTION_TURN_IN_COMMANDER_3>("quest_alterac_valley_call_of_air_ichman_mulverick"); + new quest_alterac_valley<ACTION_TURN_IN_BOSS_1>("quest_alterac_valley_boss_5"); + new quest_alterac_valley<ACTION_TURN_IN_BOSS_2>("quest_alterac_valley_boss_1"); + new quest_alterac_valley<ACTION_TURN_IN_NEAR_MINE>("quest_alterac_valley_near_mine"); + new quest_alterac_valley<ACTION_TURN_IN_OTHER_MINE>("quest_alterac_valley_other_mine"); + new quest_alterac_valley<ACTION_TURN_IN_RIDER_HIDE>("quest_alterac_valley_ram_harnesses"); + new quest_alterac_valley<ACTION_TURN_IN_RIDER_TAME>("quest_alterac_valley_empty_stables"); } diff --git a/src/server/scripts/Battlegrounds/AlteracValley/alterac_valley.h b/src/server/scripts/Battlegrounds/AlteracValley/alterac_valley.h new file mode 100644 index 00000000000..4bd5dea255a --- /dev/null +++ b/src/server/scripts/Battlegrounds/AlteracValley/alterac_valley.h @@ -0,0 +1,39 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __ALTERAC_VALLEY_H +#define __ALTERAC_VALLEY_H + +enum AlteracValleySharedActions +{ + ACTION_BUFF_YELL = -30001, + ACTION_AV_INTERACT_CAPTURABLE_OBJECT = 1, + ACTION_AV_CAPTURE_CAPTURABLE_OBJECT = 2, + + ACTION_TURN_IN_SCRAPS = 3, + ACTION_TURN_IN_COMMANDER_1 = 4, + ACTION_TURN_IN_COMMANDER_2 = 5, + ACTION_TURN_IN_COMMANDER_3 = 6, + ACTION_TURN_IN_BOSS_1, + ACTION_TURN_IN_BOSS_2, + ACTION_TURN_IN_NEAR_MINE, + ACTION_TURN_IN_OTHER_MINE, + ACTION_TURN_IN_RIDER_HIDE, + ACTION_TURN_IN_RIDER_TAME +}; + +#endif diff --git a/src/server/scripts/Battlegrounds/AlteracValley/battleground_alterac_valley.cpp b/src/server/scripts/Battlegrounds/AlteracValley/battleground_alterac_valley.cpp new file mode 100644 index 00000000000..1616b77ec1e --- /dev/null +++ b/src/server/scripts/Battlegrounds/AlteracValley/battleground_alterac_valley.cpp @@ -0,0 +1,1369 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BattlegroundScript.h" +#include "alterac_valley.h" +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "BattlegroundPackets.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "GameObject.h" +#include "GameTime.h" +#include "Log.h" +#include "Map.h" +#include "Player.h" +#include "ScriptMgr.h" + +constexpr uint32 BG_AV_SCORE_INITIAL_POINTS = 700; +constexpr uint32 BG_AV_EVENT_START_BATTLE = 9166; // Achievement: The Alterac Blitz + +enum BG_AV_BroadcastTexts +{ + BG_AV_TEXT_START_ONE_MINUTE = 10638, + BG_AV_TEXT_START_HALF_MINUTE = 10639, + BG_AV_TEXT_BATTLE_HAS_BEGUN = 10640, + + BG_AV_TEXT_ALLIANCE_NEAR_LOSE = 23210, + BG_AV_TEXT_HORDE_NEAR_LOSE = 23211 +}; + +enum BG_AV_Sounds +{ + AV_SOUND_NEAR_VICTORY = 8456, /// @todo: Not confirmed yet + + AV_SOUND_ALLIANCE_ASSAULTS = 8212, //tower, grave + enemy boss if someone tries to attack him + AV_SOUND_HORDE_ASSAULTS = 8174, + AV_SOUND_ALLIANCE_GOOD = 8173, //if something good happens for the team: wins(maybe only through killing the boss), captures mine or grave, destroys tower and defends grave + AV_SOUND_HORDE_GOOD = 8213, + AV_SOUND_BOTH_TOWER_DEFEND = 8192, + + AV_SOUND_ALLIANCE_CAPTAIN = 8232, //gets called when someone attacks them and at the beginning after 5min+rand(x)*10sec (maybe buff) + AV_SOUND_HORDE_CAPTAIN = 8333 +}; + +constexpr Seconds BG_AV_MINE_RESOURCE_TIMER = 45s; + +enum class AlteracValleyMine : uint8 +{ + North = 0, + South +}; + +enum BG_AV_CreatureIds +{ + BG_AV_CREATURE_VANNDAR = 11948, + BG_AV_CREATURE_DREKTHAR = 11946, + BG_AV_CREATURE_BALINDA = 11949, + BG_AV_CREATURE_GALVANGAR = 11947, + BG_AV_CREATURE_MORLOCH = 11657, + BG_AV_CREATURE_UMI_THORSON = 13078, + BG_AV_CREATURE_KEETAR = 13079, + BG_AV_CREATURE_TASKMASTER_SNIVVLE = 11677, + BG_AV_CREATURE_AGI_RUMBLESTOMP = 13086, + BG_AV_CREATURE_MASHA_SWIFTCUT = 13088, + BG_AV_CREATURE_HERALD = 14848, + + BG_AV_CREATURE_STORMPIKE_DEFENDER = 12050, + BG_AV_CREATURE_FROSTWOLF_GUARDIAN = 12053, + BG_AV_CREATURE_SEASONED_DEFENDER = 13326, + BG_AV_CREATURE_SEASONED_GUARDIAN = 13328, + BG_AV_CREATURE_VETERAN_DEFENDER = 13331, + BG_AV_CREATURE_VETERAN_GUARDIAN = 13332, + BG_AV_CREATURE_CHAMPION_DEFENDER = 13422, + BG_AV_CREATURE_CHAMPION_GUARDIAN = 13421 +}; + +enum BG_AV_ObjectIds +{ + //cause the mangos-system is a bit different, we don't use the right go-ids for every node.. if we want to be 100% like another big server, we must take one object for every node + //snowfall 4flags as eyecandy 179424 (alliance neutral) + //Banners - stolen from battleground_AB.h ;-) + BG_AV_OBJECTID_BANNER_A = 178925, // can only be used by horde + BG_AV_OBJECTID_BANNER_H = 178943, // can only be used by alliance + BG_AV_OBJECTID_BANNER_CONT_A = 178940, // can only be used by horde + BG_AV_OBJECTID_BANNER_CONT_H = 179435, // can only be used by alliance + + BG_AV_OBJECTID_BANNER_A_B = 178365, + BG_AV_OBJECTID_BANNER_H_B = 178364, + BG_AV_OBJECTID_BANNER_CONT_A_B = 179286, + BG_AV_OBJECTID_BANNER_CONT_H_B = 179287, + BG_AV_OBJECTID_BANNER_SNOWFALL_N = 180418, + + //snowfall eyecandy banner: + BG_AV_OBJECTID_SNOWFALL_CANDY_A = 179044, + BG_AV_OBJECTID_SNOWFALL_CANDY_PA = 179424, + BG_AV_OBJECTID_SNOWFALL_CANDY_H = 179064, + BG_AV_OBJECTID_SNOWFALL_CANDY_PH = 179425, + + //banners on top of towers: + BG_AV_OBJECTID_TOWER_BANNER_A = 178927, //[PH] Alliance A1 Tower Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_H = 178955, //[PH] Horde H1 Tower Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_PA = 179446, //[PH] Alliance H1 Tower Pre-Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_PH = 179436, //[PH] Horde A1 Tower Pre-Banner BIG + + //Auras + BG_AV_OBJECTID_AURA_A = 180421, + BG_AV_OBJECTID_AURA_H = 180422, + BG_AV_OBJECTID_AURA_N = 180423, + BG_AV_OBJECTID_AURA_A_S = 180100, + BG_AV_OBJECTID_AURA_H_S = 180101, + BG_AV_OBJECTID_AURA_N_S = 180102, + + BG_AV_OBJECTID_GATE = 180424, + BG_AV_GHOST_GATE = 180322, + + //mine supplies + BG_AV_OBJECTID_MINE_N = 178785, + BG_AV_OBJECTID_MINE_S = 178784, + + BG_AV_OBJECTID_FIRE = 179065, + BG_AV_OBJECTID_SMOKE = 179066, + + // Towers + BG_AV_OBJECTID_SOUTH_BUNKER_CONTROLLED_TOWER_BANNER = 178927, + BG_AV_OBJECTID_SOUTH_BUNKER_CONTROLLED_BANNER = 178925, + BG_AV_OBJECTID_SOUTH_BUNKER_CONTESTED_BANNER = 179435, + BG_AV_OBJECTID_SOUTH_BUNKER_CONTESTED_TOWER_BANNER = 179436, + + BG_AV_OBJECTID_NORTH_BUNKER_CONTROLLED_TOWER_BANNER = 178932, + BG_AV_OBJECTID_NORTH_BUNKER_CONTROLLED_BANNER = 178929, + BG_AV_OBJECTID_NORTH_BUNKER_CONTESTED_BANNER = 179439, + BG_AV_OBJECTID_NORTH_BUNKER_CONTESTED_TOWER_BANNER = 179440, + + BG_AV_OBJECTID_EAST_TOWER_CONTROLLED_TOWER_BANNER = 178956, + BG_AV_OBJECTID_EAST_TOWER_CONTROLLED_BANNER = 178944, + BG_AV_OBJECTID_EAST_TOWER_CONTESTED_BANNER = 179449, + BG_AV_OBJECTID_EAST_TOWER_CONTESTED_TOWER_BANNER = 179450, + + BG_AV_OBJECTID_WEST_TOWER_CONTROLLED_TOWER_BANNER = 178955, + BG_AV_OBJECTID_WEST_TOWER_CONTROLLED_BANNER = 178943, + BG_AV_OBJECTID_WEST_TOWER_CONTESTED_BANNER = 179445, + BG_AV_OBJECTID_WEST_TOWER_CONTESTED_TOWER_BANNER = 179446, + + BG_AV_OBJECTID_TOWER_POINT_CONTROLLED_TOWER_BANNER = 178957, + BG_AV_OBJECTID_TOWER_POINT_CONTROLLED_BANNER = 178945, + BG_AV_OBJECTID_TOWER_POINT_CONTESTED_BANNER = 179453, + BG_AV_OBJECTID_TOWER_POINT_CONTESTED_TOWER_BANNER = 179454, + + BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTROLLED_TOWER_BANNER = 178958, + BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTROLLED_BANNER = 178946, + BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTESTED_BANNER = 178940, + BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTESTED_TOWER_BANNER = 179458, + + BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTROLLED_TOWER_BANNER = 178948, + BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTROLLED_BANNER = 178936, + BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTESTED_BANNER = 179443, + BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTESTED_TOWER_BANNER = 179444, + + BG_AV_OBJECTID_ICEWING_BUNKER_CONTROLLED_TOWER_BANNER = 178947, + BG_AV_OBJECTID_ICEWING_BUNKER_CONTROLLED_BANNER = 178935, + BG_AV_OBJECTID_ICEWING_BUNKER_CONTESTED_BANNER = 179441, + BG_AV_OBJECTID_ICEWING_BUNKER_CONTESTED_TOWER_BANNER = 179442, + + // Graveyards + BG_AV_OBJECTID_AID_STATION_ALLIANCE_CONTROLLED = 179465, + BG_AV_OBJECTID_AID_STATION_HORDE_CONTESTED = 179468, + BG_AV_OBJECTID_AID_STATION_HORDE_CONTROLLED = 179467, + BG_AV_OBJECTID_AID_STATION_ALLIANCE_CONTESTED = 179466, + + BG_AV_OBJECTID_STORMPIKE_ALLIANCE_CONTROLLED = 178389, + BG_AV_OBJECTID_STORMPIKE_HORDE_CONTESTED = 179287, + BG_AV_OBJECTID_STORMPIKE_HORDE_CONTROLLED = 178388, + BG_AV_OBJECTID_STORMPIKE_ALLIANCE_CONTESTED = 179286, + + BG_AV_OBJECTID_STONEHEARTH_HORDE_CONTESTED = 179310, + BG_AV_OBJECTID_STONEHEARTH_HORDE_CONTROLLED = 179285, + BG_AV_OBJECTID_STONEHEARTH_ALLIANCE_CONTESTED = 179308, + BG_AV_OBJECTID_STONEHEARTH_ALLIANCE_CONTROLLED = 179284, + + BG_AV_OBJECTID_SNOWFALL_NEUTRAL = 180418, + BG_AV_OBJECTID_SNOWFALL_HORDE_CONTESTED = 180420, + BG_AV_OBJECTID_SNOWFALL_ALLIANCE_CONTESTED = 180419, + BG_AV_OBJECTID_SNOWFALL_HORDE_CONTROLLED = 178364, + BG_AV_OBJECTID_SNOWFALL_ALLIANCE_CONTROLLED = 178365, + + BG_AV_OBJECTID_ICEBLOOD_HORDE_CONTROLLED = 179483, + BG_AV_OBJECTID_ICEBLOOD_ALLIANCE_CONTESTED = 179482, + BG_AV_OBJECTID_ICEBLOOD_ALLIANCE_CONTROLLED = 179481, + BG_AV_OBJECTID_ICEBLOOD_HORDE_CONTESTED = 179484, + + BG_AV_OBJECTID_FROSTWOLF_HORDE_CONTROLLED = 178393, + BG_AV_OBJECTID_FROSTWOLF_ALLIANCE_CONTESTED = 179304, + BG_AV_OBJECTID_FROSTWOLF_ALLIANCE_CONTROLLED = 178394, + BG_AV_OBJECTID_FROSTWOLF_HORDE_CONTESTED = 179305, + + BG_AV_OBJECTID_FROSTWOLF_HUT_HORDE_CONTROLLED = 179472, + BG_AV_OBJECTID_FROSTWOLF_HUT_ALLIANCE_CONTESTED = 179471, + BG_AV_OBJECTID_FROSTWOLF_HUT_ALLIANCE_CONTROLLED = 179470, + BG_AV_OBJECTID_FROSTWOLF_HUT_HORDE_CONTESTED = 179473 +}; + +enum BG_AV_Nodes +{ + BG_AV_NODES_FIRSTAID_STATION = 0, + BG_AV_NODES_STORMPIKE_GRAVE = 1, + BG_AV_NODES_STONEHEART_GRAVE = 2, + BG_AV_NODES_SNOWFALL_GRAVE = 3, + BG_AV_NODES_ICEBLOOD_GRAVE = 4, + BG_AV_NODES_FROSTWOLF_GRAVE = 5, + BG_AV_NODES_FROSTWOLF_HUT = 6, + BG_AV_NODES_DUNBALDAR_SOUTH = 7, + BG_AV_NODES_DUNBALDAR_NORTH = 8, + BG_AV_NODES_ICEWING_BUNKER = 9, + BG_AV_NODES_STONEHEART_BUNKER = 10, + BG_AV_NODES_ICEBLOOD_TOWER = 11, + BG_AV_NODES_TOWER_POINT = 12, + BG_AV_NODES_FROSTWOLF_ETOWER = 13, + BG_AV_NODES_FROSTWOLF_WTOWER = 14, + + BG_AV_NODES_MAX = 15 +}; + +enum BG_AV_BUFF +{ /// @todo: Add all other buffs here + AV_BUFF_ARMOR = 21163, + AV_BUFF_A_CAPTAIN = 23693, //the buff which the alliance captain does + AV_BUFF_H_CAPTAIN = 22751 //the buff which the horde captain does +}; +enum BG_AV_States +{ + POINT_NEUTRAL = 0, + POINT_ASSAULTED = 1, + POINT_DESTROYED = 2, + POINT_CONTROLED = 3 +}; + +enum BG_AV_WorldStates +{ + AV_WS_ALLIANCE_REINFORCEMENTS = 3127, + AV_WS_HORDE_REINFORCEMENTS = 3128, + AV_WS_SHOW_HORDE_REINFORCEMENTS = 3133, + AV_WS_SHOW_ALLIANCE_REINFORCEMENTS = 3134, + AV_WS_MAX_REINFORCEMENTS = 3136, + +// Graves + // Alliance + //Stormpike first aid station + AV_WS_STORMPIKE_AID_STATION_ALLIANCE_CONTROLLED = 1325, + AV_WS_STORMPIKE_AID_STATION_IN_CONFLICT_ALLIANCE_ATTACKING = 1326, + AV_WS_STORMPIKE_AID_STATION_HORDE_CONTROLLED = 1327, + AV_WS_STORMPIKE_AID_STATION_IN_CONFLICT_HORDE_ATTACKING = 1328, + //Stormpike Graveyard + AV_WS_STORMPIKE_GRAVEYARD_ALLIANCE_CONTROLLED = 1333, + AV_WS_STORMPIKE_GRAVEYARD_IN_CONFLICT_ALLIANCE_ATTACKING = 1335, + AV_WS_STORMPIKE_GRAVEYARD_HORDE_CONTROLLED = 1334, + AV_WS_STORMPIKE_GRAVEYARD_IN_CONFLICT_HORDE_ATTACKING = 1336, + //Stoneheart Grave + AV_WS_STONEHEARTH_GRAVEYARD_ALLIANCE_CONTROLLED = 1302, + AV_WS_STONEHEARTH_GRAVEYARD_IN_CONFLICT_ALLIANCE_ATTACKING = 1304, + AV_WS_STONEHEARTH_GRAVEYARD_HORDE_CONTROLLED = 1301, + AV_WS_STONEHEARTH_GRAVEYARD_IN_CONFLICT_HORDE_ATTACKING = 1303, + //Neutral + //Snowfall Grave + AV_WS_SNOWFALL_GRAVEYARD_UNCONTROLLED = 1966, + AV_WS_SNOWFALL_GRAVEYARD_ALLIANCE_CONTROLLED = 1341, + AV_WS_SNOWFALL_GRAVEYARD_IN_CONFLICT_ALLIANCE_ATTACKING = 1343, + AV_WS_SNOWFALL_GRAVEYARD_HORDE_CONTROLLED = 1342, + AV_WS_SNOWFALL_GRAVEYARD_IN_CONFLICT_HORDE_ATTACKING = 1344, + //Horde + //Iceblood grave + AV_WS_ICEBLOOD_GRAVEYARD_ALLIANCE_CONTROLLED = 1346, + AV_WS_ICEBLOOD_GRAVEYARD_IN_CONFLICT_ALLIANCE_ATTACKING = 1348, + AV_WS_ICEBLOOD_GRAVEYARD_HORDE_CONTROLLED = 1347, + AV_WS_ICEBLOOD_GRAVEYARD_IN_CONFLICT_HORDE_ATTACKING = 1349, + //Frostwolf Grave + AV_WS_FROSTWOLF_GRAVEYARD_ALLIANCE_CONTROLLED = 1337, + AV_WS_FROSTWOLF_GRAVEYARD_IN_CONFLICT_ALLIANCE_ATTACKING = 1339, + AV_WS_FROSTWOLF_GRAVEYARD_HORDE_CONTROLLED = 1338, + AV_WS_FROSTWOLF_GRAVEYARD_IN_CONFLICT_HORDE_ATTACKING = 1340, + //Frostwolf Hut + AV_WS_FROSTWOLF_RELIEF_HUT_ALLIANCE_CONTROLLED = 1329, + AV_WS_FROSTWOLF_RELIEF_HUT_IN_CONFLICT_ALLIANCE_ATTACKING = 1331, + AV_WS_FROSTWOLF_RELIEF_HUT_HORDE_CONTROLLED = 1330, + AV_WS_FROSTWOLF_RELIEF_HUT_IN_CONFLICT_HORDE_ATTACKING = 1332, + +//Towers + //Alliance + //Dunbaldar South Bunker + AV_WS_DUN_BALDAR_SOUTH_BUNKER_OWNER = 1181, + AV_WS_DUN_BALDAR_SOUTH_BUNKER_ALLIANCE_CONTROLLED = 1361, + AV_WS_DUN_BALDAR_SOUTH_BUNKER_DESTROYED = 1370, + AV_WS_DUN_BALDAR_SOUTH_BUNKER_IN_CONFLICT_HORDE_ATTACKING = 1378, + AV_WS_DUN_BALDAR_SOUTH_BUNKER_IN_CONFLICT_ALLIANCE_ATTACKING = 1374, // unused + //Dunbaldar North Bunker + AV_WS_DUN_BALDAR_NORTH_BUNKER_OWNER = 1182, + AV_WS_DUN_BALDAR_NORTH_BUNKER_ALLIANCE_CONTROLLED = 1362, + AV_WS_DUN_BALDAR_NORTH_BUNKER_DESTROYED = 1371, + AV_WS_DUN_BALDAR_NORTH_BUNKER_IN_CONFLICT_HORDE_ATTACKING = 1379, + AV_WS_DUN_BALDAR_NORTH_BUNKER_IN_CONFLICT_ALLIANCE_ATTACKING = 1375, // unused + //Icewing Bunker + AV_WS_ICEWING_BUNKER_OWNER = 1183, + AV_WS_ICEWING_BUNKER_ALLIANCE_CONTROLLED = 1363, + AV_WS_ICEWING_BUNKER_DESTROYED = 1372, + AV_WS_ICEWING_BUNKER_IN_CONFLICT_HORDE_ATTACKING = 1380, + AV_WS_ICEWING_BUNKER_IN_CONFLICT_ALLIANCE_ATTACKING = 1376, // unused + //Stoneheart Bunker + AV_WS_STONEHEARTH_BUNKER_OWNER = 1184, + AV_WS_STONEHEARTH_BUNKER_ALLIANCE_CONTROLLED = 1364, + AV_WS_STONEHEARTH_BUNKER_DESTROYED = 1373, + AV_WS_STONEHEARTH_BUNKER_IN_CONFLICT_HORDE_ATTACKING = 1381, + AV_WS_STONEHEARTH_BUNKER_IN_CONFLICT_ALLIANCE_ATTACKING = 1377, // unused + //Horde + //Iceblood Tower + AV_WS_ICEBLOOD_TOWER_OWNER = 1187, + AV_WS_ICEBLOOD_TOWER_DESTROYED = 1368, + AV_WS_ICEBLOOD_TOWER_HORDE_CONTROLLED = 1385, + AV_WS_ICEBLOOD_TOWER_IN_CONFLICT_ALLIANCE_ATTACKING = 1390, + AV_WS_ICEBLOOD_TOWER_IN_CONFLICT_HORDE_ATTACKING = 1395, // unused + //Tower Point + AV_WS_TOWER_POINT_OWNER = 1188, + AV_WS_TOWER_POINT_DESTROYED = 1367, + AV_WS_TOWER_POINT_HORDE_CONTROLLED = 1384, + AV_WS_TOWER_POINT_IN_CONFLICT_ALLIANCE_ATTACKING = 1389, + AV_WS_TOWER_POINT_IN_CONFLICT_HORDE_ATTACKING = 1394, // unused + //Frostwolf West + AV_WS_WEST_FROSTWOLF_TOWER_OWNER = 1185, + AV_WS_WEST_FROSTWOLF_TOWER_DESTROYED = 1365, + AV_WS_WEST_FROSTWOLF_TOWER_HORDE_CONTROLLED = 1382, + AV_WS_WEST_FROSTWOLF_TOWER_IN_CONFLICT_ALLIANCE_ATTACKING = 1387, + AV_WS_WEST_FROSTWOLF_TOWER_IN_CONFLICT_HORDE_ATTACKING = 1392, // unused + //Frostwolf East + AV_WS_EAST_FROSTWOLF_TOWER_OWNER = 1186, + AV_WS_EAST_FROSTWOLF_TOWER_DESTROYED = 1366, + AV_WS_EAST_FROSTWOLF_TOWER_HORDE_CONTROLLED = 1383, + AV_WS_EAST_FROSTWOLF_TOWER_IN_CONFLICT_ALLIANCE_ATTACKING = 1388, + AV_WS_EAST_FROSTWOLF_TOWER_IN_CONFLICT_HORDE_ATTACKING = 1393, // unused + +//Mines + AV_WS_IRONDEEP_MINE_OWNER = 801, + AV_WS_IRONDEEP_MINE_TROGG_CONTROLLED = 1360, + AV_WS_IRONDEEP_MINE_ALLIANCE_CONTROLLED = 1358, + AV_WS_IRONDEEP_MINE_HORDE_CONTROLLED = 1359, + + AV_WS_COLDTOOTH_MINE_OWNER = 804, + AV_WS_COLDTOOTH_MINE_KOBOLD_CONTROLLED = 1357, + AV_WS_COLDTOOTH_MINE_ALLIANCE_CONTROLLED = 1355, + AV_WS_COLDTOOTH_MINE_HORDE_CONTROLLED = 1356, + +//Turnins + AV_WS_IVUS_STORM_CRYSTAL_COUNT = 1043, + AV_WS_IVUS_STORM_CRYSTAL_MAX = 1044, + AV_WS_LOKHOLAR_STORMPIKE_SOLDIERS_BLOOD_COUNT = 923, + AV_WS_LOKHOLAR_STORMPIKE_SOLDIERS_BLOOD_MAX = 922, + +//Bosses + AV_WS_DREKTHAR_ALIVE = 601, + AV_WS_VANDAAR_ALIVE = 602, + +//Captains + AV_WS_GALVAGAR_ALIVE = 1352, + AV_WS_BALINDA_ALIVE = 1351, +}; + +struct StaticMineInfo +{ + int32 WorldStateOwner; + int32 WorldStateAllianceControlled; + int32 WorldStateHordeControlled; + int32 WorldStateNeutralControlled; + uint8 TextIdAlliance; + uint8 TextIdHorde; +}; + +struct AlteracValleyMineInfo +{ + Team Owner; + + StaticMineInfo StaticInfo; +}; + +struct StaticNodeInfo +{ + BG_AV_Nodes NodeId; + + struct + { + uint8 AllianceCapture; + uint8 AllianceAttack; + uint8 HordeCapture; + uint8 HordeAttack; + } TextIds; + + struct + { + int32 AllianceControl; + int32 AllianceAssault; + int32 HordeControl; + int32 HordeAssault; + int32 Owner; + } WorldStateIds; + + struct + { + std::string AllianceOrDefend; + std::string HordeOrDestroy; + } StringIds; +}; + +static StaticNodeInfo const BGAVNodeInfo[] = +{ + { BG_AV_NODES_FIRSTAID_STATION, { 47, 48, 45, 46 }, { 1325, 1326, 1327, 1328, 0 }, { "bg_av_herald_stormpike_aid_station_alliance", "bg_av_herald_stormpike_aid_station_horde" } }, // Stormpike First Aid Station + { BG_AV_NODES_STORMPIKE_GRAVE, { 1, 2, 3, 4 }, { 1333, 1335, 1334, 1336, 0 }, { "bg_av_herald_stormpike_alliance", "bg_av_herald_stormpike_horde" } }, // Stormpike Graveyard + { BG_AV_NODES_STONEHEART_GRAVE, { 55, 56, 53, 54 }, { 1302, 1304, 1301, 1303, 0 }, { "bg_av_herald_stonehearth_alliance", "bg_av_herald_stonehearth_horde" } }, // Stoneheart Graveyard + { BG_AV_NODES_SNOWFALL_GRAVE, { 5, 6, 7, 8 }, { 1341, 1343, 1342, 1344, 0 }, { "bg_av_herald_snowfall_alliance", "bg_av_herald_snowfall_horde" } }, // Snowfall Graveyard + { BG_AV_NODES_ICEBLOOD_GRAVE, { 59, 60, 57, 58 }, { 1346, 1348, 1347, 1349, 0 }, { "bg_av_herald_iceblood_alliance", "bg_av_herald_iceblood_horde" } }, // Iceblood Graveyard + { BG_AV_NODES_FROSTWOLF_GRAVE, { 9, 10, 11, 12 }, { 1337, 1339, 1338, 1340, 0 }, { "bg_av_herald_frostwolf_alliance", "bg_av_herald_frostwolf_horde" } }, // Frostwolf Graveyard + { BG_AV_NODES_FROSTWOLF_HUT, { 51, 52, 49, 50 }, { 1329, 1331, 1330, 1332, 0 }, { "bg_av_herald_frostwolf_hut_alliance", "bg_av_herald_frostwolf_hut_horde" } }, // Frostwolf Hut + { BG_AV_NODES_DUNBALDAR_SOUTH, { 16, 15, 14, 13 }, { 1361, 1375, 1370, 1378, 1181 }, { "bg_av_herald_south_bunker_defend", "bg_av_herald_south_bunker_attack" } }, // Dunbaldar South Bunker + { BG_AV_NODES_DUNBALDAR_NORTH, { 20, 19, 18, 17 }, { 1362, 1374, 1371, 1379, 1182 }, { "bg_av_herald_north_bunker_defend", "bg_av_herald_south_bunker_attack" } }, // Dunbaldar North Bunker + { BG_AV_NODES_ICEWING_BUNKER, { 24, 23, 22, 21 }, { 1363, 1376, 1372, 1380, 1183 }, { "bg_av_herald_icewing_bunker_defend", "bg_av_herald_icewing_bunker_attack" } }, // Icewing Bunker + { BG_AV_NODES_STONEHEART_BUNKER, { 28, 27, 26, 25 }, { 1364, 1377, 1373, 1381, 1184 }, { "bg_av_herald_stonehearth_bunker_defend", "bg_av_herald_stonehearth_bunker_attack" } }, // Stoneheart Bunker + { BG_AV_NODES_ICEBLOOD_TOWER, { 44, 43, 42, 41 }, { 1368, 1390, 1385, 1395, 1188 }, { "bg_av_herald_iceblood_tower_defend", "bg_av_herald_iceblood_tower_attack" } }, // Iceblood Tower + { BG_AV_NODES_TOWER_POINT, { 40, 39, 38, 37 }, { 1367, 1389, 1384, 1394, 1187 }, { "bg_av_herald_tower_point_defend", "bg_av_herald_tower_point_attack" } }, // Tower Point + { BG_AV_NODES_FROSTWOLF_ETOWER, { 36, 35, 34, 33 }, { 1366, 1388, 1383, 1393, 1186 }, { "bg_av_herald_east_tower_defend", "bg_av_herald_east_tower_attack" } }, // Frostwolf East Tower + { BG_AV_NODES_FROSTWOLF_WTOWER, { 32, 31, 30, 29 }, { 1365, 1387, 1382, 1392, 1185 }, { "bg_av_herald_west_tower_defend", "bg_av_herald_west_tower_attack" } }, // Frostwolf West Tower +}; + +enum Texts +{ + // Herold + // Towers/Graveyards = 1 - 60 + TEXT_COLDTOOTH_MINE_ALLIANCE_TAKEN = 61, + TEXT_IRONDEEP_MINE_ALLIANCE_TAKEN = 62, + TEXT_COLDTOOTH_MINE_HORDE_TAKEN = 63, + TEXT_IRONDEEP_MINE_HORDE_TAKEN = 64, + TEXT_FROSTWOLF_GENERAL_DEAD = 65, /// @todo: sound is missing + TEXT_STORMPIKE_GENERAL_DEAD = 66, /// @todo: sound is missing + TEXT_ALLIANCE_WINS = 67, // NYI /// @todo: sound is missing + TEXT_HORDE_WINS = 68, // NYI /// @todo: sound is missing + + // Taskmaster Snivvle + TEXT_SNIVVLE_RANDOM = 0 +}; + +enum BG_AV_ExploitTeleportLocations +{ + AV_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3664, + AV_EXPLOIT_TELEPORT_LOCATION_HORDE = 3665 +}; + +struct BG_AV_NodeInfo +{ + BG_AV_States State; + BG_AV_States PrevState; + uint16 TotalOwner; + Team Owner; + uint16 PrevOwner; + bool Tower; +}; + +inline BG_AV_Nodes& operator++(BG_AV_Nodes& i) { return i = BG_AV_Nodes(i + 1); } + +struct battleground_alterac_valley : BattlegroundScript +{ + enum QuestIds + { + AV_QUEST_A_SCRAPS1 = 7223, + AV_QUEST_A_SCRAPS2 = 6781, + AV_QUEST_H_SCRAPS1 = 7224, + AV_QUEST_H_SCRAPS2 = 6741, + AV_QUEST_A_COMMANDER1 = 6942, //soldier + AV_QUEST_H_COMMANDER1 = 6825, + AV_QUEST_A_COMMANDER2 = 6941, //leutnant + AV_QUEST_H_COMMANDER2 = 6826, + AV_QUEST_A_COMMANDER3 = 6943, //commander + AV_QUEST_H_COMMANDER3 = 6827, + AV_QUEST_A_BOSS1 = 7386, // 5 cristal/blood + AV_QUEST_H_BOSS1 = 7385, + AV_QUEST_A_BOSS2 = 6881, // 1 + AV_QUEST_H_BOSS2 = 6801, + AV_QUEST_A_NEAR_MINE = 5892, //the mine near start location of team + AV_QUEST_H_NEAR_MINE = 5893, + AV_QUEST_A_OTHER_MINE = 6982, //the other mine ;) + AV_QUEST_H_OTHER_MINE = 6985, + AV_QUEST_A_RIDER_HIDE = 7026, + AV_QUEST_H_RIDER_HIDE = 7002, + AV_QUEST_A_RIDER_TAME = 7027, + AV_QUEST_H_RIDER_TAME = 7001 + }; + + enum Data : uint32 + { + DATA_DEFENDER_TIER_HORDE = 1, + DATA_DEFENDER_TIER_ALLIANCE = 2, + }; + + enum DefenderTier : uint32 + { + BG_AV_DEFENDER_TIER_DEFENDER, + BG_AV_DEFENDER_TIER_SEASONED, + BG_AV_DEFENDER_TIER_VETERAN, + BG_AV_DEFENDER_TIER_CHAMPION + }; + + enum PvpStats + { + PVP_STAT_TOWERS_ASSAULTED = 61, + PVP_STAT_GRAVEYARDS_ASSAULTED = 63, + PVP_STAT_TOWERS_DEFENDED = 64, + PVP_STAT_GRAVEYARDS_DEFENDED = 65, + PVP_STAT_SECONDARY_OBJECTIVES = 82 + }; + + enum Misc + { + NEAR_LOSE_POINTS = 140 + }; + + enum HonorKillBonus + { + HONOR_KILL_BONUS_BOSS = 4, + HONOR_KILL_BONUS_CAPTAIN = 3, + HONOR_KILL_BONUS_SURVIVING_TOWER = 2, + HONOR_KILL_BONUS_SURVIVING_CAPTAIN = 2, + HONOR_KILL_BONUS_DESTROY_TOWER = 3 + }; + + enum ReputationGains + { + REP_GAIN_BOSS = 350, + REP_GAIN_CAPTAIN = 125, + REP_GAIN_DESTROY_TOWER = 12, + REP_GAIN_SURVIVING_TOWER = 12, + REP_GAIN_SURVIVING_CAPTAIN = 125 + }; + + enum ResourceLoss + { + RESOURCE_LOSS_TOWER = -75, + RESOURCE_LOSS_CAPTAIN = -100 + }; + + enum Spells + { + SPELL_COMPLETE_ALTERAC_VALLEY_QUEST = 23658, + }; + + enum Factions + { + FACTION_FROSTWOLF_CLAN = 729, + FACTION_STORMPIKE_GUARD = 730, + }; + + explicit battleground_alterac_valley(BattlegroundMap* map) : BattlegroundScript(map) + { + _teamResources = { BG_AV_SCORE_INITIAL_POINTS, BG_AV_SCORE_INITIAL_POINTS }; + _isInformedNearVictory = { false, false }; + + for (uint8 i = 0; i < 2; i++) //forloop for both teams (it just make 0 == alliance and 1 == horde also for both mines 0=north 1=south + { + for (uint8 j = 0; j < 9; j++) + _teamQuestStatus[i][j] = 0; + + _captainBuffTimer[i].Reset(120000 + urand(0, 4) * 60); //as far as i could see, the buff is randomly so i make 2minutes (thats the duration of the buff itself) + 0-4minutes @todo get the right times + } + + _mineInfo[uint8(AlteracValleyMine::North)] = { TEAM_OTHER, { AV_WS_IRONDEEP_MINE_OWNER, AV_WS_IRONDEEP_MINE_ALLIANCE_CONTROLLED, AV_WS_IRONDEEP_MINE_HORDE_CONTROLLED, AV_WS_IRONDEEP_MINE_TROGG_CONTROLLED, TEXT_IRONDEEP_MINE_ALLIANCE_TAKEN, TEXT_IRONDEEP_MINE_HORDE_TAKEN } }; + _mineInfo[uint8(AlteracValleyMine::South)] = { TEAM_OTHER, { AV_WS_COLDTOOTH_MINE_OWNER, AV_WS_COLDTOOTH_MINE_ALLIANCE_CONTROLLED, AV_WS_COLDTOOTH_MINE_HORDE_CONTROLLED, AV_WS_COLDTOOTH_MINE_KOBOLD_CONTROLLED, TEXT_COLDTOOTH_MINE_ALLIANCE_TAKEN, TEXT_COLDTOOTH_MINE_HORDE_TAKEN } }; + + for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_STONEHEART_GRAVE; ++i) //alliance graves + InitNode(i, ALLIANCE, false); + for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_STONEHEART_BUNKER; ++i) //alliance towers + InitNode(i, ALLIANCE, true); + for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_GRAVE; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i) //horde graves + InitNode(i, HORDE, false); + for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_TOWER; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) //horde towers + InitNode(i, HORDE, true); + InitNode(BG_AV_NODES_SNOWFALL_GRAVE, TEAM_OTHER, false); //give snowfall neutral owner + + _mineResourceTimer.Reset(BG_AV_MINE_RESOURCE_TIMER); + } + + void OnUpdate(uint32 diff) override + { + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + _mineResourceTimer.Update(diff); + if (_mineResourceTimer.Passed()) + { + for (AlteracValleyMineInfo const& info : _mineInfo) + { + if (info.Owner == TEAM_OTHER) + continue; + + UpdateScore(info.Owner, 1); + } + + _mineResourceTimer.Reset(BG_AV_MINE_RESOURCE_TIMER); + } + + for (uint8 i = TEAM_ALLIANCE; i <= TEAM_HORDE; i++) + { + if (!IsCaptainAlive(TeamId(i))) + continue; + + _captainBuffTimer[i].Update(diff); + if (_captainBuffTimer[i].Passed()) + { + if (i == 0) + { + battleground->CastSpellOnTeam(AV_BUFF_A_CAPTAIN, ALLIANCE); + if (Creature const* creature = battlegroundMap->GetCreature(_balindaGUID)) + creature->AI()->DoAction(ACTION_BUFF_YELL); + } + else + { + battleground->CastSpellOnTeam(AV_BUFF_H_CAPTAIN, HORDE); + if (Creature const* creature = battlegroundMap->GetCreature(_galvangarGUID)) + creature->AI()->DoAction(ACTION_BUFF_YELL); + } + + _captainBuffTimer[i].Reset(120000 + urand(0, 4) * 60000); //as far as i could see, the buff is randomly so i make 2minutes (thats the duration of the buff itself) + 0-4minutes @todo get the right times + } + } + } + + void OnPlayerKilled(Player* victim, Player* /*killer*/) override + { + UpdateScore(battleground->GetPlayerTeam(victim->GetGUID()), -1); + } + + void OnUnitKilled(Creature* victim, Unit* killer) override + { + switch (victim->GetEntry()) + { + case BG_AV_CREATURE_VANNDAR: + { + UpdateWorldState(AV_WS_VANDAAR_ALIVE, 0); + battleground->CastSpellOnTeam(SPELL_COMPLETE_ALTERAC_VALLEY_QUEST, HORDE); //this is a spell which finishes a quest where a player has to kill the boss + battleground->RewardReputationToTeam(FACTION_FROSTWOLF_CLAN, REP_GAIN_BOSS, HORDE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(HONOR_KILL_BONUS_BOSS), HORDE); + battleground->EndBattleground(HORDE); + break; + } + case BG_AV_CREATURE_DREKTHAR: + { + UpdateWorldState(AV_WS_DREKTHAR_ALIVE, 0); + battleground->CastSpellOnTeam(SPELL_COMPLETE_ALTERAC_VALLEY_QUEST, ALLIANCE); //this is a spell which finishes a quest where a player has to kill the boss + battleground->RewardReputationToTeam(FACTION_STORMPIKE_GUARD, REP_GAIN_BOSS, ALLIANCE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(HONOR_KILL_BONUS_BOSS), ALLIANCE); + battleground->EndBattleground(ALLIANCE); + break; + } + case BG_AV_CREATURE_BALINDA: + { + UpdateWorldState(AV_WS_BALINDA_ALIVE, 0); + battleground->RewardReputationToTeam(FACTION_FROSTWOLF_CLAN, REP_GAIN_CAPTAIN, HORDE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(HONOR_KILL_BONUS_CAPTAIN), HORDE); + UpdateScore(ALLIANCE, RESOURCE_LOSS_CAPTAIN); + if (Creature const* herald = FindHerald("bg_av_herald_horde_win")) + herald->AI()->Talk(TEXT_STORMPIKE_GENERAL_DEAD); + break; + } + case BG_AV_CREATURE_GALVANGAR: + { + UpdateWorldState(AV_WS_GALVAGAR_ALIVE, 0); + battleground->RewardReputationToTeam(FACTION_STORMPIKE_GUARD, REP_GAIN_CAPTAIN, ALLIANCE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(HONOR_KILL_BONUS_CAPTAIN), ALLIANCE); + UpdateScore(HORDE, RESOURCE_LOSS_CAPTAIN); + if (Creature const* herald = FindHerald("bg_av_herald_alliance_win")) + herald->AI()->Talk(TEXT_FROSTWOLF_GENERAL_DEAD); + break; + } + case BG_AV_CREATURE_MORLOCH: + { + // if mine is not owned by morloch, then nothing happens + if (_mineInfo[uint8(AlteracValleyMine::North)].Owner != TEAM_OTHER) + break; + + Team const killerTeam = battleground->GetPlayerTeam(Coalesce<Unit>(killer->GetCharmerOrOwnerPlayerOrPlayerItself(), killer)->GetGUID()); + ChangeMineOwner(AlteracValleyMine::North, killerTeam); + break; + } + case BG_AV_CREATURE_TASKMASTER_SNIVVLE: + { + if (_mineInfo[uint8(AlteracValleyMine::South)].Owner != TEAM_OTHER) + break; + + Team const killerTeam = battleground->GetPlayerTeam(Coalesce<Unit>(killer->GetCharmerOrOwnerPlayerOrPlayerItself(), killer)->GetGUID()); + ChangeMineOwner(AlteracValleyMine::South, killerTeam); + break; + } + case BG_AV_CREATURE_UMI_THORSON: + case BG_AV_CREATURE_KEETAR: + { + Team const killerTeam = battleground->GetPlayerTeam(Coalesce<Unit>(killer->GetCharmerOrOwnerPlayerOrPlayerItself(), killer)->GetGUID()); + ChangeMineOwner(AlteracValleyMine::North, killerTeam); + break; + } + case BG_AV_CREATURE_AGI_RUMBLESTOMP: + case BG_AV_CREATURE_MASHA_SWIFTCUT: + { + Team const killerTeam = battleground->GetPlayerTeam(Coalesce<Unit>(killer->GetCharmerOrOwnerPlayerOrPlayerItself(), killer)->GetGUID()); + ChangeMineOwner(AlteracValleyMine::South, killerTeam); + break; + } + } + } + + bool IsCaptainAlive(TeamId teamId) const + { + if (teamId == TEAM_HORDE) + return battlegroundMap->GetWorldStateValue(AV_WS_GALVAGAR_ALIVE) == 1; + + if (teamId == TEAM_ALLIANCE) + return battlegroundMap->GetWorldStateValue(AV_WS_BALINDA_ALIVE) == 1; + + return false; + } + + void OnStart() override + { + UpdateWorldState(AV_WS_SHOW_HORDE_REINFORCEMENTS, 1); + UpdateWorldState(AV_WS_SHOW_ALLIANCE_REINFORCEMENTS, 1); + + // Achievement: The Alterac Blitz + TriggerGameEvent(BG_AV_EVENT_START_BATTLE); + + for (ObjectGuid const& guid : _doorGUIDs) + { + if (GameObject* gameObject = battlegroundMap->GetGameObject(guid)) + { + gameObject->UseDoorOrButton(); + Seconds delay = gameObject->GetEntry() == BG_AV_GHOST_GATE ? 0s : 3s; + gameObject->DespawnOrUnsummon(delay); + } + } + } + + void OnEnd(Team winner) override + { + BattlegroundScript::OnEnd(winner); + //calculate bonuskills for both teams: + //first towers: + std::array<uint8, PVP_TEAMS_COUNT> kills = { 0, 0 }; + std::array<uint8, PVP_TEAMS_COUNT> rep = { 0, 0 }; + + for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) + { + if (_nodes[i].State == POINT_CONTROLED) + { + if (_nodes[i].Owner == ALLIANCE) + { + rep[TEAM_ALLIANCE] += REP_GAIN_SURVIVING_TOWER; + kills[TEAM_ALLIANCE] += HONOR_KILL_BONUS_SURVIVING_TOWER; + } + else + { + rep[TEAM_HORDE] += REP_GAIN_SURVIVING_TOWER; + kills[TEAM_HORDE] += HONOR_KILL_BONUS_SURVIVING_TOWER; + } + } + } + + for (uint8 i = TEAM_ALLIANCE; i <= TEAM_HORDE; ++i) + { + if (IsCaptainAlive(TeamId(i))) + { + kills[i] += HONOR_KILL_BONUS_SURVIVING_CAPTAIN; + rep[i] += REP_GAIN_SURVIVING_CAPTAIN; + } + if (rep[i] != 0) + battleground->RewardReputationToTeam(i == 0 ? FACTION_STORMPIKE_GUARD : FACTION_FROSTWOLF_CLAN, rep[i], i == 0 ? ALLIANCE : HORDE); + if (kills[i] != 0) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(kills[i]), i == 0 ? ALLIANCE : HORDE); + } + } + + void OnPlayerLeft(Player* player) override + { + BattlegroundScript::OnPlayerLeft(player); + if (!player) + return; + + player->RemoveAurasDueToSpell(AV_BUFF_ARMOR); + } + + void EventPlayerDestroyedPoint(GameObject* gameobject) + { + if (!gameobject) + return; + + BG_AV_Nodes const node = GetNodeThroughObject(gameobject->GetEntry()); + DestroyNode(node); + UpdateNodeWorldState(node); + + Team const owner = _nodes[node].Owner; + if (IsTower(node)) + { + UpdateScore((owner == ALLIANCE) ? HORDE : ALLIANCE, RESOURCE_LOSS_TOWER); + battleground->RewardReputationToTeam(owner == ALLIANCE ? FACTION_STORMPIKE_GUARD : FACTION_FROSTWOLF_CLAN, REP_GAIN_DESTROY_TOWER, owner); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(HONOR_KILL_BONUS_DESTROY_TOWER), owner); + } + + if (StaticNodeInfo const* nodeInfo = GetStaticNodeInfo(node)) + if (Creature const* herald = FindHerald(nodeInfo->StringIds.HordeOrDestroy)) + herald->AI()->Talk(owner == ALLIANCE ? nodeInfo->TextIds.AllianceCapture : nodeInfo->TextIds.HordeCapture); + + battlegroundMap->UpdateSpawnGroupConditions(); + } + + void DoAction(uint32 actionId, WorldObject* source, WorldObject* target) override + { + Team const team = battleground->GetPlayerTeam(source->GetGUID()); + TeamId const teamIndex = Battleground::GetTeamIndexByTeamId(team); + + switch (actionId) + { + case ACTION_AV_CAPTURE_CAPTURABLE_OBJECT: + EventPlayerDestroyedPoint(source->ToGameObject()); + break; + case ACTION_AV_INTERACT_CAPTURABLE_OBJECT: + if (target && source && source->IsPlayer()) + HandleInteractCapturableObject(source->ToPlayer(), target->ToGameObject()); + break; + case ACTION_TURN_IN_SCRAPS: + _teamQuestStatus[teamIndex][0] += 20; + break; + case ACTION_TURN_IN_COMMANDER_1: + _teamQuestStatus[teamIndex][1]++; + battleground->RewardReputationToTeam(teamIndex, 1, team); + if (_teamQuestStatus[teamIndex][1] == 30) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + break; + case ACTION_TURN_IN_COMMANDER_2: + _teamQuestStatus[teamIndex][2]++; + battleground->RewardReputationToTeam(teamIndex, 1, team); + if (_teamQuestStatus[teamIndex][2] == 60) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + break; + case ACTION_TURN_IN_COMMANDER_3: + _teamQuestStatus[teamIndex][3]++; + battleground->RewardReputationToTeam(teamIndex, 1, team); + if (_teamQuestStatus[teamIndex][3] == 120) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + break; + case ACTION_TURN_IN_BOSS_1: + _teamQuestStatus[teamIndex][4] += 4; //you can turn in 5 or 1 item.. + [[fallthrough]]; + case ACTION_TURN_IN_BOSS_2: + _teamQuestStatus[teamIndex][4]++; + if (_teamQuestStatus[teamIndex][4] >= 200) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + UpdateWorldState(teamIndex == TEAM_ALLIANCE ? AV_WS_IVUS_STORM_CRYSTAL_COUNT : AV_WS_LOKHOLAR_STORMPIKE_SOLDIERS_BLOOD_COUNT, _teamQuestStatus[teamIndex][4]); + break; + case ACTION_TURN_IN_NEAR_MINE: + _teamQuestStatus[teamIndex][5]++; + if (_teamQuestStatus[teamIndex][5] == 28) + { + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + if (_teamQuestStatus[teamIndex][6] == 7) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here - ground assault ready", actionId); + } + break; + case ACTION_TURN_IN_OTHER_MINE: + _teamQuestStatus[teamIndex][6]++; + if (_teamQuestStatus[teamIndex][6] == 7) + { + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + if (_teamQuestStatus[teamIndex][5] == 20) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here - ground assault ready", actionId); + } + break; + case ACTION_TURN_IN_RIDER_HIDE: + _teamQuestStatus[teamIndex][7]++; + if (_teamQuestStatus[teamIndex][7] == 25) + { + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + if (_teamQuestStatus[teamIndex][8] == 25) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here - rider assault ready", actionId); + } + break; + case ACTION_TURN_IN_RIDER_TAME: + _teamQuestStatus[teamIndex][8]++; + if (_teamQuestStatus[teamIndex][8] == 25) + { + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here", actionId); + if (_teamQuestStatus[teamIndex][7] == 25) + TC_LOG_DEBUG("bg.battleground", "BG_AV action {} completed (need to implement some events here - rider assault ready", actionId); + } + break; + default: + TC_LOG_ERROR("bg.battleground", "BattlegroundAV::DoAction: {}. Unhandled action.", actionId); + break; + } + } + + void ChangeMineOwner(AlteracValleyMine mine, Team team, bool initial = false) + { + if (team != ALLIANCE && team != HORDE) + team = TEAM_OTHER; + + AlteracValleyMineInfo& mineInfo = _mineInfo[uint8(mine)]; + + if (mineInfo.Owner == team && !initial) + return; + + mineInfo.Owner = team; + + SendMineWorldStates(mine); + + uint8 const textId = team == ALLIANCE ? mineInfo.StaticInfo.TextIdAlliance : mineInfo.StaticInfo.TextIdHorde; + + std::string const stringId = team == ALLIANCE ? "bg_av_herald_mine_alliance" : "bg_av_herald_mine_horde"; + + if (Creature const* herald = FindHerald(stringId)) + herald->AI()->Talk(textId); + } + + static BG_AV_Nodes GetNodeThroughObject(uint32 object) + { + switch (object) + { + case BG_AV_OBJECTID_AID_STATION_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_AID_STATION_HORDE_CONTESTED: + case BG_AV_OBJECTID_AID_STATION_HORDE_CONTROLLED: + case BG_AV_OBJECTID_AID_STATION_ALLIANCE_CONTESTED: + return BG_AV_NODES_FIRSTAID_STATION; + case BG_AV_OBJECTID_STORMPIKE_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_STORMPIKE_HORDE_CONTESTED: + case BG_AV_OBJECTID_STORMPIKE_HORDE_CONTROLLED: + case BG_AV_OBJECTID_STORMPIKE_ALLIANCE_CONTESTED: + return BG_AV_NODES_STORMPIKE_GRAVE; + case BG_AV_OBJECTID_STONEHEARTH_HORDE_CONTESTED: + case BG_AV_OBJECTID_STONEHEARTH_HORDE_CONTROLLED: + case BG_AV_OBJECTID_STONEHEARTH_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_STONEHEARTH_ALLIANCE_CONTROLLED: + return BG_AV_NODES_STONEHEART_GRAVE; + case BG_AV_OBJECTID_SNOWFALL_NEUTRAL: + case BG_AV_OBJECTID_SNOWFALL_HORDE_CONTESTED: + case BG_AV_OBJECTID_SNOWFALL_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_SNOWFALL_HORDE_CONTROLLED: + case BG_AV_OBJECTID_SNOWFALL_ALLIANCE_CONTROLLED: + return BG_AV_NODES_SNOWFALL_GRAVE; + case BG_AV_OBJECTID_ICEBLOOD_HORDE_CONTROLLED: + case BG_AV_OBJECTID_ICEBLOOD_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_ICEBLOOD_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_ICEBLOOD_HORDE_CONTESTED: + return BG_AV_NODES_ICEBLOOD_GRAVE; + case BG_AV_OBJECTID_FROSTWOLF_HORDE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_FROSTWOLF_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_HORDE_CONTESTED: + return BG_AV_NODES_FROSTWOLF_GRAVE; + case BG_AV_OBJECTID_FROSTWOLF_HUT_HORDE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_HUT_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_FROSTWOLF_HUT_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_HUT_HORDE_CONTESTED: + return BG_AV_NODES_FROSTWOLF_HUT; + case BG_AV_OBJECTID_SOUTH_BUNKER_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_SOUTH_BUNKER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_SOUTH_BUNKER_CONTESTED_BANNER: + case BG_AV_OBJECTID_SOUTH_BUNKER_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_DUNBALDAR_SOUTH; + case BG_AV_OBJECTID_NORTH_BUNKER_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_NORTH_BUNKER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_NORTH_BUNKER_CONTESTED_BANNER: + case BG_AV_OBJECTID_NORTH_BUNKER_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_DUNBALDAR_NORTH; + case BG_AV_OBJECTID_EAST_TOWER_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_EAST_TOWER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_EAST_TOWER_CONTESTED_BANNER: + case BG_AV_OBJECTID_EAST_TOWER_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_FROSTWOLF_ETOWER; + case BG_AV_OBJECTID_WEST_TOWER_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_WEST_TOWER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_WEST_TOWER_CONTESTED_BANNER: + case BG_AV_OBJECTID_WEST_TOWER_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_FROSTWOLF_WTOWER; + case BG_AV_OBJECTID_TOWER_POINT_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_TOWER_POINT_CONTROLLED_BANNER: + case BG_AV_OBJECTID_TOWER_POINT_CONTESTED_BANNER: + case BG_AV_OBJECTID_TOWER_POINT_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_TOWER_POINT; + case BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTESTED_BANNER: + case BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_ICEBLOOD_TOWER; + case BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTESTED_BANNER: + case BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_STONEHEART_BUNKER; + case BG_AV_OBJECTID_ICEWING_BUNKER_CONTROLLED_TOWER_BANNER: + case BG_AV_OBJECTID_ICEWING_BUNKER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_ICEWING_BUNKER_CONTESTED_BANNER: + case BG_AV_OBJECTID_ICEWING_BUNKER_CONTESTED_TOWER_BANNER: + return BG_AV_NODES_ICEWING_BUNKER; + default: + TC_LOG_ERROR("bg.battleground", "BattlegroundAV: ERROR! GetPlace got a wrong object :("); + ABORT(); + return BG_AV_Nodes(0); + } + } + + void HandleInteractCapturableObject(Player* player, GameObject* target) + { + if (!player || !target) + return; + + switch (target->GetEntry()) + { + // graveyards + case BG_AV_OBJECTID_AID_STATION_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_AID_STATION_HORDE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_HORDE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_HUT_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_FROSTWOLF_HUT_HORDE_CONTROLLED: + case BG_AV_OBJECTID_ICEBLOOD_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_ICEBLOOD_HORDE_CONTROLLED: + case BG_AV_OBJECTID_STONEHEARTH_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_STONEHEARTH_HORDE_CONTROLLED: + case BG_AV_OBJECTID_STORMPIKE_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_STORMPIKE_HORDE_CONTROLLED: + // Snowfall + case BG_AV_OBJECTID_SNOWFALL_NEUTRAL: + case BG_AV_OBJECTID_SNOWFALL_ALLIANCE_CONTROLLED: + case BG_AV_OBJECTID_SNOWFALL_HORDE_CONTROLLED: + // towers + case BG_AV_OBJECTID_EAST_TOWER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_WEST_TOWER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_TOWER_POINT_CONTROLLED_BANNER: + case BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_ICEWING_BUNKER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_SOUTH_BUNKER_CONTROLLED_BANNER: + case BG_AV_OBJECTID_NORTH_BUNKER_CONTROLLED_BANNER: + EventPlayerAssaultsPoint(player, target->GetEntry()); + break; + // graveyards + case BG_AV_OBJECTID_AID_STATION_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_AID_STATION_HORDE_CONTESTED: + case BG_AV_OBJECTID_FROSTWOLF_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_FROSTWOLF_HORDE_CONTESTED: + case BG_AV_OBJECTID_FROSTWOLF_HUT_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_FROSTWOLF_HUT_HORDE_CONTESTED: + case BG_AV_OBJECTID_ICEBLOOD_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_ICEBLOOD_HORDE_CONTESTED: + case BG_AV_OBJECTID_STONEHEARTH_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_STONEHEARTH_HORDE_CONTESTED: + case BG_AV_OBJECTID_STORMPIKE_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_STORMPIKE_HORDE_CONTESTED: + // towers + case BG_AV_OBJECTID_EAST_TOWER_CONTESTED_BANNER: + case BG_AV_OBJECTID_WEST_TOWER_CONTESTED_BANNER: + case BG_AV_OBJECTID_TOWER_POINT_CONTESTED_BANNER: + case BG_AV_OBJECTID_ICEBLOOD_TOWER_CONTESTED_BANNER: + case BG_AV_OBJECTID_STONEHEARTH_BUNKER_CONTESTED_BANNER: + case BG_AV_OBJECTID_ICEWING_BUNKER_CONTESTED_BANNER: + case BG_AV_OBJECTID_SOUTH_BUNKER_CONTESTED_BANNER: + case BG_AV_OBJECTID_NORTH_BUNKER_CONTESTED_BANNER: + EventPlayerDefendsPoint(player, target->GetEntry()); + break; + // Snowfall special cases (either defend/assault) + case BG_AV_OBJECTID_SNOWFALL_ALLIANCE_CONTESTED: + case BG_AV_OBJECTID_SNOWFALL_HORDE_CONTESTED: + { + BG_AV_Nodes const node = GetNodeThroughObject(target->GetEntry()); + if (_nodes[node].TotalOwner == TEAM_OTHER) + EventPlayerAssaultsPoint(player, target->GetEntry()); + else + EventPlayerDefendsPoint(player, target->GetEntry()); + break; + } + default: + break; + } + } + + void EventPlayerDefendsPoint(Player* player, uint32 object) + { + BG_AV_Nodes node = GetNodeThroughObject(object); + + Team const owner = _nodes[node].Owner; + Team const team = battleground->GetPlayerTeam(player->GetGUID()); + + if (owner == team || _nodes[node].State != POINT_ASSAULTED) + return; + + TC_LOG_DEBUG("bg.battleground", "player defends point object: {} node: {}", object, node); + if (_nodes[node].PrevOwner != team) + { + TC_LOG_ERROR("bg.battleground", "BG_AV: player defends point which doesn't belong to his team {}", node); + return; + } + + DefendNode(node, team); + UpdateNodeWorldState(node); + + if (StaticNodeInfo const* nodeInfo = GetStaticNodeInfo(node)) + { + std::string stringId; + + if (IsTower(node)) + stringId = nodeInfo->StringIds.AllianceOrDefend; + else + stringId = team == ALLIANCE ? nodeInfo->StringIds.AllianceOrDefend : nodeInfo->StringIds.HordeOrDestroy; + + if (Creature const* herald = FindHerald(stringId)) + herald->AI()->Talk(team == ALLIANCE ? nodeInfo->TextIds.AllianceCapture : nodeInfo->TextIds.HordeCapture); + } + + // update the statistic for the defending player + battleground->UpdatePvpStat(player, IsTower(node) ? PVP_STAT_TOWERS_DEFENDED : PVP_STAT_GRAVEYARDS_DEFENDED, 1); + battlegroundMap->UpdateSpawnGroupConditions(); + } + + void EventPlayerAssaultsPoint(Player* player, uint32 object) + { + BG_AV_Nodes node = GetNodeThroughObject(object); + Team const owner = _nodes[node].Owner; //maybe name it prevowner + Team const team = battleground->GetPlayerTeam(player->GetGUID()); + + TC_LOG_DEBUG("bg.battleground", "bg_av: player assaults point object {} node {}", object, node); + if (owner == team || team == _nodes[node].TotalOwner) + return; //surely a gm used this object + + AssaultNode(node, team); + UpdateNodeWorldState(node); + + if (StaticNodeInfo const* nodeInfo = GetStaticNodeInfo(node)) + { + std::string stringId; + if (IsTower(node)) + stringId = nodeInfo->StringIds.HordeOrDestroy; + else + stringId = team == ALLIANCE ? nodeInfo->StringIds.AllianceOrDefend : nodeInfo->StringIds.HordeOrDestroy; + + if (Creature const* herald = FindHerald(stringId)) + herald->AI()->Talk(team == ALLIANCE ? nodeInfo->TextIds.AllianceAttack : nodeInfo->TextIds.HordeAttack); + } + + // update the statistic for the assaulting player + battleground->UpdatePvpStat(player, (IsTower(node)) ? PVP_STAT_TOWERS_ASSAULTED : PVP_STAT_GRAVEYARDS_ASSAULTED, 1); + battlegroundMap->UpdateSpawnGroupConditions(); + } + + void UpdateNodeWorldState(BG_AV_Nodes node) const + { + if (StaticNodeInfo const* nodeInfo = GetStaticNodeInfo(node)) + { + uint16 const owner = _nodes[node].Owner; + BG_AV_States const state = _nodes[node].State; + + UpdateWorldState(nodeInfo->WorldStateIds.AllianceAssault, owner == ALLIANCE && state == POINT_ASSAULTED); + UpdateWorldState(nodeInfo->WorldStateIds.AllianceControl, owner == ALLIANCE && state >= POINT_DESTROYED); + UpdateWorldState(nodeInfo->WorldStateIds.HordeAssault, owner == HORDE && state == POINT_ASSAULTED); + UpdateWorldState(nodeInfo->WorldStateIds.HordeControl, owner == HORDE && state >= POINT_DESTROYED); + if (nodeInfo->WorldStateIds.Owner) + UpdateWorldState(nodeInfo->WorldStateIds.Owner, owner == HORDE ? 2 : owner == ALLIANCE ? 1 : 0); + } + + if (node == BG_AV_NODES_SNOWFALL_GRAVE) + UpdateWorldState(AV_WS_SNOWFALL_GRAVEYARD_UNCONTROLLED, _nodes[node].Owner == TEAM_OTHER); + } + + void SendMineWorldStates(AlteracValleyMine mine) const + { + AlteracValleyMineInfo const& mineInfo = _mineInfo[uint8(mine)]; + UpdateWorldState(mineInfo.StaticInfo.WorldStateHordeControlled, mineInfo.Owner == HORDE); + UpdateWorldState(mineInfo.StaticInfo.WorldStateAllianceControlled, mineInfo.Owner == ALLIANCE); + UpdateWorldState(mineInfo.StaticInfo.WorldStateNeutralControlled, mineInfo.Owner == TEAM_OTHER); + UpdateWorldState(mineInfo.StaticInfo.WorldStateOwner, mineInfo.Owner == HORDE ? 2 : mineInfo.Owner == ALLIANCE ? 1 : 0); + } + + void AssaultNode(BG_AV_Nodes node, Team team) + { + _nodes[node].PrevOwner = _nodes[node].Owner; + _nodes[node].Owner = team; + _nodes[node].PrevState = _nodes[node].State; + _nodes[node].State = POINT_ASSAULTED; + } + + void DestroyNode(BG_AV_Nodes node) + { + _nodes[node].TotalOwner = _nodes[node].Owner; + _nodes[node].PrevOwner = _nodes[node].Owner; + _nodes[node].PrevState = _nodes[node].State; + _nodes[node].State = (_nodes[node].Tower) ? POINT_DESTROYED : POINT_CONTROLED; + } + + void InitNode(BG_AV_Nodes node, Team team, bool tower) + { + _nodes[node].TotalOwner = team; + _nodes[node].Owner = team; + _nodes[node].PrevOwner = 0; + _nodes[node].State = POINT_CONTROLED; + _nodes[node].PrevState = _nodes[node].State; + _nodes[node].State = POINT_CONTROLED; + _nodes[node].Tower = tower; + } + + void DefendNode(BG_AV_Nodes node, Team team) + { + _nodes[node].PrevOwner = _nodes[node].Owner; + _nodes[node].Owner = team; + _nodes[node].PrevState = _nodes[node].State; + _nodes[node].State = POINT_CONTROLED; + } + + Team GetPrematureWinner() override + { + uint32 const allianceScore = _teamResources[Battleground::GetTeamIndexByTeamId(ALLIANCE)]; + uint32 const hordeScore = _teamResources[Battleground::GetTeamIndexByTeamId(HORDE)]; + + if (allianceScore > hordeScore) + return ALLIANCE; + if (hordeScore > allianceScore) + return HORDE; + + return BattlegroundScript::GetPrematureWinner(); + } + + void OnGameObjectCreate(GameObject* gameObject) override + { + switch (gameObject->GetEntry()) + { + case BG_AV_GHOST_GATE: + case BG_AV_OBJECTID_GATE: + _doorGUIDs.insert(gameObject->GetGUID()); + break; + default: + break; + } + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case BG_AV_CREATURE_GALVANGAR: + _galvangarGUID = creature->GetGUID(); + break; + case BG_AV_CREATURE_BALINDA: + _balindaGUID = creature->GetGUID(); + break; + case BG_AV_CREATURE_HERALD: + _heraldGUIDs.insert(creature->GetGUID()); + break; + default: + break; + } + } + + uint32 GetData(uint32 dataId) const override + { + auto getDefenderTierForTeam = [&](TeamId teamId) -> DefenderTier + { + if (_teamQuestStatus[teamId][0] < 500) + return BG_AV_DEFENDER_TIER_DEFENDER; + + if (_teamQuestStatus[teamId][0] < 1000) + return BG_AV_DEFENDER_TIER_SEASONED; + + if (_teamQuestStatus[teamId][0] < 1500) + return BG_AV_DEFENDER_TIER_VETERAN; + + return BG_AV_DEFENDER_TIER_CHAMPION; + }; + + switch (dataId) + { + case DATA_DEFENDER_TIER_ALLIANCE: + return getDefenderTierForTeam(TEAM_ALLIANCE); + case DATA_DEFENDER_TIER_HORDE: + return getDefenderTierForTeam(TEAM_HORDE); + default: + return BattlegroundScript::GetData(dataId); + } + } + + Creature* FindHerald(std::string_view stringId) const + { + for (ObjectGuid const& guid : _heraldGUIDs) + if (Creature* creature = battlegroundMap->GetCreature(guid)) + if (creature->HasStringId(stringId)) + return creature; + + return nullptr; + } + + static StaticNodeInfo const* GetStaticNodeInfo(BG_AV_Nodes node) + { + for (const auto& nodeInfo : BGAVNodeInfo) + if (nodeInfo.NodeId == node) + return &nodeInfo; + + return nullptr; + } + + bool IsTower(BG_AV_Nodes node) const { return _nodes[node].Tower; } + + void UpdateScore(Team team, int16 points) + { + ASSERT(team == ALLIANCE || team == HORDE); + TeamId const teamindex = Battleground::GetTeamIndexByTeamId(team); + _teamResources[teamindex] += points; + + UpdateWorldState(teamindex == TEAM_HORDE ? AV_WS_HORDE_REINFORCEMENTS : AV_WS_ALLIANCE_REINFORCEMENTS, _teamResources[teamindex]); + if (points < 0) + { + if (_teamResources[teamindex] < 1) + { + _teamResources[teamindex] = 0; + battleground->EndBattleground(teamindex == TEAM_HORDE ? ALLIANCE : HORDE); + } + else if (!_isInformedNearVictory[teamindex] && _teamResources[teamindex] < NEAR_LOSE_POINTS) + { + if (teamindex == TEAM_ALLIANCE) + battleground->SendBroadcastText(BG_AV_TEXT_ALLIANCE_NEAR_LOSE, CHAT_MSG_BG_SYSTEM_ALLIANCE); + else + battleground->SendBroadcastText(BG_AV_TEXT_HORDE_NEAR_LOSE, CHAT_MSG_BG_SYSTEM_HORDE); + battleground->PlaySoundToAll(AV_SOUND_NEAR_VICTORY); + _isInformedNearVictory[teamindex] = true; + } + } + } + +private: + /*variables */ + std::array<int32, PVP_TEAMS_COUNT> _teamResources; + uint32 _teamQuestStatus[PVP_TEAMS_COUNT][9]; //[x][y] x=team y=questcounter + + std::array<BG_AV_NodeInfo, BG_AV_NODES_MAX> _nodes; + + TimeTracker _mineResourceTimer; //ticks for both teams + + std::array<AlteracValleyMineInfo, 2> _mineInfo; + + std::array<TimeTracker, PVP_TEAMS_COUNT> _captainBuffTimer; + + std::array<bool, PVP_TEAMS_COUNT> _isInformedNearVictory; + GuidUnorderedSet _doorGUIDs; + ObjectGuid _balindaGUID; + ObjectGuid _galvangarGUID; + GuidUnorderedSet _heraldGUIDs; +}; + +void AddSC_battleground_alterac_valley() +{ + RegisterBattlegroundMapScript(battleground_alterac_valley, 30); +} diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp b/src/server/scripts/Battlegrounds/AlteracValley/boss_balinda.cpp index 38987c82428..38987c82428 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp +++ b/src/server/scripts/Battlegrounds/AlteracValley/boss_balinda.cpp diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp b/src/server/scripts/Battlegrounds/AlteracValley/boss_drekthar.cpp index f7ec0bb3da9..f7ec0bb3da9 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp +++ b/src/server/scripts/Battlegrounds/AlteracValley/boss_drekthar.cpp diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_galvangar.cpp b/src/server/scripts/Battlegrounds/AlteracValley/boss_galvangar.cpp index 741387ec033..741387ec033 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_galvangar.cpp +++ b/src/server/scripts/Battlegrounds/AlteracValley/boss_galvangar.cpp diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_vanndar.cpp b/src/server/scripts/Battlegrounds/AlteracValley/boss_vanndar.cpp index 83c1fcc813e..83c1fcc813e 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_vanndar.cpp +++ b/src/server/scripts/Battlegrounds/AlteracValley/boss_vanndar.cpp diff --git a/src/server/scripts/EasternKingdoms/ArathiBasin/arathi_basin.cpp b/src/server/scripts/Battlegrounds/ArathiBasin/arathi_basin.cpp index 4a30195833f..4a30195833f 100644 --- a/src/server/scripts/EasternKingdoms/ArathiBasin/arathi_basin.cpp +++ b/src/server/scripts/Battlegrounds/ArathiBasin/arathi_basin.cpp diff --git a/src/server/scripts/Battlegrounds/ArathiBasin/battleground_arathi_basin.cpp b/src/server/scripts/Battlegrounds/ArathiBasin/battleground_arathi_basin.cpp new file mode 100644 index 00000000000..f30216bf067 --- /dev/null +++ b/src/server/scripts/Battlegrounds/ArathiBasin/battleground_arathi_basin.cpp @@ -0,0 +1,605 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BattlegroundScript.h" +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "BattlegroundPackets.h" +#include "Creature.h" +#include "GameObject.h" +#include "GameTime.h" +#include "Log.h" +#include "Map.h" +#include "ScriptMgr.h" + +struct battleground_arathi_basin : BattlegroundScript +{ + // Tick intervals and given points: case 0, 1, 2, 3, 4, 5 captured nodes + static constexpr uint32 BG_AB_TickInterval = 2000; + static constexpr uint32 BG_AB_TickPoints[6] = { 0, 2, 3, 4, 7, 60 }; + static constexpr uint32 NORMAL_HONOR_TICKS = 160; + static constexpr uint32 WEEKEND_HONOR_TICKS = 260; + static constexpr uint32 NORMAL_REPUTATION_TICKS = 120; + static constexpr uint32 WEEKEND_REPUTATION_TICKS = 160; + + enum PvpStats + { + PVP_STAT_BASES_ASSAULTED = 926, + PVP_STAT_BASES_DEFENDED = 927, + }; + + enum Events + { + AB_EVENT_START_BATTLE = 9158, // Achievement: Let's Get This Done + + AB_EVENT_CONTESTED_STABLES_HORDE = 28523, + AB_EVENT_CAPTURE_STABLES_HORDE = 28527, + AB_EVENT_DEFENDED_STABLES_HORDE = 28525, + AB_EVENT_CONTESTED_STABLES_ALLIANCE = 28522, + AB_EVENT_CAPTURE_STABLES_ALLIANCE = 28526, + AB_EVENT_DEFENDED_STABLES_ALLIANCE = 28524, + + AB_EVENT_CONTESTED_BLACKSMITH_HORDE = 8876, + AB_EVENT_CAPTURE_BLACKSMITH_HORDE = 8773, + AB_EVENT_DEFENDED_BLACKSMITH_HORDE = 8770, + AB_EVENT_CONTESTED_BLACKSMITH_ALLIANCE = 8874, + AB_EVENT_CAPTURE_BLACKSMITH_ALLIANCE = 8769, + AB_EVENT_DEFENDED_BLACKSMITH_ALLIANCE = 8774, + + AB_EVENT_CONTESTED_FARM_HORDE = 39398, + AB_EVENT_CAPTURE_FARM_HORDE = 39399, + AB_EVENT_DEFENDED_FARM_HORDE = 39400, + AB_EVENT_CONTESTED_FARM_ALLIANCE = 39401, + AB_EVENT_CAPTURE_FARM_ALLIANCE = 39402, + AB_EVENT_DEFENDED_FARM_ALLIANCE = 39403, + + AB_EVENT_CONTESTED_GOLD_MINE_HORDE = 39404, + AB_EVENT_CAPTURE_GOLD_MINE_HORDE = 39405, + AB_EVENT_DEFENDED_GOLD_MINE_HORDE = 39406, + AB_EVENT_CONTESTED_GOLD_MINE_ALLIANCE = 39407, + AB_EVENT_CAPTURE_GOLD_MINE_ALLIANCE = 39408, + AB_EVENT_DEFENDED_GOLD_MINE_ALLIANCE = 39409, + + AB_EVENT_CONTESTED_LUMBER_MILL_HORDE = 39387, + AB_EVENT_CAPTURE_LUMBER_MILL_HORDE = 39388, + AB_EVENT_DEFENDED_LUMBER_MILL_HORDE = 39389, + AB_EVENT_CONTESTED_LUMBER_MILL_ALLIANCE = 39390, + AB_EVENT_CAPTURE_LUMBER_MILL_ALLIANCE = 39391, + AB_EVENT_DEFENDED_LUMBER_MILL_ALLIANCE = 39392 + }; + + enum Sounds + { + BG_AB_SOUND_NODE_CLAIMED = 8192, + BG_AB_SOUND_NODE_CAPTURED_ALLIANCE = 8173, + BG_AB_SOUND_NODE_CAPTURED_HORDE = 8213, + BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE = 8212, + BG_AB_SOUND_NODE_ASSAULTED_HORDE = 8174, + BG_AB_SOUND_NEAR_VICTORY_ALLIANCE = 8456, + BG_AB_SOUND_NEAR_VICTORY_HORDE = 8457 + }; + + enum BroadcastTexts + { + BG_AB_TEXT_ALLIANCE_NEAR_VICTORY = 10598, + BG_AB_TEXT_HORDE_NEAR_VICTORY = 10599, + }; + + enum Score + { + BG_AB_WARNING_NEAR_VICTORY_SCORE = 1200, + BG_AB_MAX_TEAM_SCORE = 1500 + }; + + enum Creatures + { + BG_AB_NPC_THE_BLACK_BRIDE = 150501, + BG_AB_NPC_RADULF_LEDER = 150505 + }; + + enum ObjectTypes + { + BG_AB_OBJECTID_CAPTURE_POINT_STABLES = 227420, + BG_AB_OBJECTID_CAPTURE_POINT_BLACKSMITH = 227522, + BG_AB_OBJECTID_CAPTURE_POINT_FARM = 227536, + BG_AB_OBJECTID_CAPTURE_POINT_GOLD_MINE = 227538, + BG_AB_OBJECTID_CAPTURE_POINT_LUMBER_MILL = 227544, + + BG_AB_OBJECTID_GHOST_GATE = 180322, + BG_AB_OBJECTID_ALLIANCE_DOOR = 322273, + BG_AB_OBJECTID_HORDE_DOOR = 322274 + }; + + enum WorldStates + { + BG_AB_WS_OCCUPIED_BASES_HORDE = 1778, + BG_AB_WS_OCCUPIED_BASES_ALLY = 1779, + BG_AB_WS_RESOURCES_ALLY = 1776, + BG_AB_WS_RESOURCES_HORDE = 1777, + BG_AB_WS_RESOURCES_MAX = 1780, + BG_AB_WS_RESOURCES_WARNING = 1955, + + BG_AB_WS_STABLE_ICON = 1842, // Stable map icon (NONE) + BG_AB_WS_STABLE_STATE_ALIENCE = 1767, // Stable map state (ALIENCE) + BG_AB_WS_STABLE_STATE_HORDE = 1768, // Stable map state (HORDE) + BG_AB_WS_STABLE_STATE_CON_ALI = 1769, // Stable map state (CON ALIENCE) + BG_AB_WS_STABLE_STATE_CON_HOR = 1770, // Stable map state (CON HORDE) + BG_AB_WS_FARM_ICON = 1845, // Farm map icon (NONE) + BG_AB_WS_FARM_STATE_ALIENCE = 1772, // Farm state (ALIENCE) + BG_AB_WS_FARM_STATE_HORDE = 1773, // Farm state (HORDE) + BG_AB_WS_FARM_STATE_CON_ALI = 1774, // Farm state (CON ALIENCE) + BG_AB_WS_FARM_STATE_CON_HOR = 1775, // Farm state (CON HORDE) + BG_AB_WS_BLACKSMITH_ICON = 1846, // Blacksmith map icon (NONE) + BG_AB_WS_BLACKSMITH_STATE_ALIENCE = 1782, // Blacksmith map state (ALIENCE) + BG_AB_WS_BLACKSMITH_STATE_HORDE = 1783, // Blacksmith map state (HORDE) + BG_AB_WS_BLACKSMITH_STATE_CON_ALI = 1784, // Blacksmith map state (CON ALIENCE) + BG_AB_WS_BLACKSMITH_STATE_CON_HOR = 1785, // Blacksmith map state (CON HORDE) + BG_AB_WS_LUMBERMILL_ICON = 1844, // Lumber Mill map icon (NONE) + BG_AB_WS_LUMBERMILL_STATE_ALIENCE = 1792, // Lumber Mill map state (ALIENCE) + BG_AB_WS_LUMBERMILL_STATE_HORDE = 1793, // Lumber Mill map state (HORDE) + BG_AB_WS_LUMBERMILL_STATE_CON_ALI = 1794, // Lumber Mill map state (CON ALIENCE) + BG_AB_WS_LUMBERMILL_STATE_CON_HOR = 1795, // Lumber Mill map state (CON HORDE) + BG_AB_WS_GOLDMINE_ICON = 1843, // Gold Mine map icon (NONE) + BG_AB_WS_GOLDMINE_STATE_ALIENCE = 1787, // Gold Mine map state (ALIENCE) + BG_AB_WS_GOLDMINE_STATE_HORDE = 1788, // Gold Mine map state (HORDE) + BG_AB_WS_GOLDMINE_STATE_CON_ALI = 1789, // Gold Mine map state (CON ALIENCE + BG_AB_WS_GOLDMINE_STATE_CON_HOR = 1790, // Gold Mine map state (CON HORDE) + + BG_AB_WS_HAD_500_DISADVANTAGE_ALLIANCE = 3644, + BG_AB_WS_HAD_500_DISADVANTAGE_HORDE = 3645, + + BG_AB_WS_FARM_ICON_NEW = 8808, // Farm map icon + BG_AB_WS_LUMBER_MILL_ICON_NEW = 8805, // Lumber Mill map icon + BG_AB_WS_BLACKSMITH_ICON_NEW = 8799, // Blacksmith map icon + BG_AB_WS_GOLD_MINE_ICON_NEW = 8809, // Gold Mine map icon + BG_AB_WS_STABLES_ICON_NEW = 5834, // Stable map icon + + BG_AB_WS_FARM_HORDE_CONTROL_STATE = 17328, + BG_AB_WS_FARM_ALLIANCE_CONTROL_STATE = 17325, + BG_AB_WS_LUMBER_MILL_HORDE_CONTROL_STATE = 17330, + BG_AB_WS_LUMBER_MILL_ALLIANCE_CONTROL_STATE = 17326, + BG_AB_WS_BLACKSMITH_HORDE_CONTROL_STATE = 17327, + BG_AB_WS_BLACKSMITH_ALLIANCE_CONTROL_STATE = 17324, + BG_AB_WS_GOLD_MINE_HORDE_CONTROL_STATE = 17329, + BG_AB_WS_GOLD_MINE_ALLIANCE_CONTROL_STATE = 17323, + BG_AB_WS_STABLES_HORDE_CONTROL_STATE = 17331, + BG_AB_WS_STABLES_ALLIANCE_CONTROL_STATE = 17322, + }; + + explicit battleground_arathi_basin(BattlegroundMap* map) : BattlegroundScript(map), _lastTick(0), _isInformedNearVictory(false) + { + bool const isBGWeekend = sBattlegroundMgr->IsBGWeekend(battleground->GetTypeID()); + + _honorTics = (isBGWeekend) ? WEEKEND_HONOR_TICKS : NORMAL_HONOR_TICKS; + _reputationTics = (isBGWeekend) ? WEEKEND_REPUTATION_TICKS : NORMAL_REPUTATION_TICKS; + _honorScoreTics = { { 0, 0 } }; + _reputationScoreTics = { { 0, 0 } }; + } + + void OnInit() override + { + BattlegroundScript::OnInit(); + + UpdateWorldState(BG_AB_WS_RESOURCES_MAX, BG_AB_MAX_TEAM_SCORE); + UpdateWorldState(BG_AB_WS_RESOURCES_WARNING, BG_AB_WARNING_NEAR_VICTORY_SCORE); + } + + void OnUpdate(uint32 diff) override + { + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + // Accumulate points + _lastTick += diff; + if (_lastTick > BG_AB_TickInterval) + { + _lastTick -= BG_AB_TickInterval; + + uint8 ally = 0, horde = 0; + _CalculateTeamNodes(ally, horde); + uint8 points[PVP_TEAMS_COUNT] = { ally, horde }; + + for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team) + { + if (!points[team]) + continue; + + battleground->AddPoint(team == TEAM_HORDE ? HORDE : ALLIANCE, BG_AB_TickPoints[points[team]]); + _honorScoreTics[team] += BG_AB_TickPoints[points[team]]; + _reputationScoreTics[team] += BG_AB_TickPoints[points[team]]; + + if (_reputationScoreTics[team] >= _reputationTics) + { + (team == TEAM_ALLIANCE) ? battleground->RewardReputationToTeam(509, 10, ALLIANCE) : battleground->RewardReputationToTeam(510, 10, HORDE); + _reputationScoreTics[team] -= _reputationTics; + } + + if (_honorScoreTics[team] >= _honorTics) + { + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), (team == TEAM_ALLIANCE) ? ALLIANCE : HORDE); + _honorScoreTics[team] -= _honorTics; + } + + const uint32 teamScore = battleground->GetTeamScore(static_cast<TeamId>(team)); + if (!_isInformedNearVictory && teamScore > BG_AB_WARNING_NEAR_VICTORY_SCORE) + { + if (team == TEAM_ALLIANCE) + { + battleground->SendBroadcastText(BG_AB_TEXT_ALLIANCE_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + battleground->PlaySoundToAll(BG_AB_SOUND_NEAR_VICTORY_ALLIANCE); + } + else + { + battleground->SendBroadcastText(BG_AB_TEXT_HORDE_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + battleground->PlaySoundToAll(BG_AB_SOUND_NEAR_VICTORY_HORDE); + } + _isInformedNearVictory = true; + } + + if (teamScore > BG_AB_MAX_TEAM_SCORE) + battleground->SetTeamPoint(team == TEAM_HORDE ? HORDE : ALLIANCE, BG_AB_MAX_TEAM_SCORE); + + if (team == TEAM_ALLIANCE) + UpdateWorldState(BG_AB_WS_RESOURCES_ALLY, teamScore); + else + UpdateWorldState(BG_AB_WS_RESOURCES_HORDE, teamScore); + + // update achievement flags + // we increased m_TeamScores[team] so we just need to check if it is 500 more than other teams resources + uint8 otherTeam = (team + 1) % PVP_TEAMS_COUNT; + if (teamScore > battleground->GetTeamScore(static_cast<TeamId>(otherTeam)) + 500) + { + if (team == TEAM_ALLIANCE) + UpdateWorldState(BG_AB_WS_HAD_500_DISADVANTAGE_HORDE, 1); + else + UpdateWorldState(BG_AB_WS_HAD_500_DISADVANTAGE_ALLIANCE, 1); + } + } + + UpdateWorldState(BG_AB_WS_OCCUPIED_BASES_ALLY, ally); + UpdateWorldState(BG_AB_WS_OCCUPIED_BASES_HORDE, horde); + } + + // Test win condition + if (battleground->GetTeamScore(TEAM_ALLIANCE) >= BG_AB_MAX_TEAM_SCORE && battleground->GetTeamScore(TEAM_HORDE) >= BG_AB_MAX_TEAM_SCORE) + battleground->EndBattleground(TEAM_OTHER); // draw + else if (battleground->GetTeamScore(TEAM_ALLIANCE) >= BG_AB_MAX_TEAM_SCORE) + battleground->EndBattleground(ALLIANCE); + else if (battleground->GetTeamScore(TEAM_HORDE) >= BG_AB_MAX_TEAM_SCORE) + battleground->EndBattleground(HORDE); + } + + void OnStart() override + { + // Achievement: Let's Get This Done + TriggerGameEvent(AB_EVENT_START_BATTLE); + } + + void _CalculateTeamNodes(uint8& alliance, uint8& horde) const + { + alliance = 0; + horde = 0; + + for (ObjectGuid const& guid : _capturePoints) + { + if (GameObject const* capturePoint = battlegroundMap->GetGameObject(guid)) + { + int32 const wsValue = battlegroundMap->GetWorldStateValue(capturePoint->GetGOInfo()->capturePoint.worldState1); + switch (WorldPackets::Battleground::BattlegroundCapturePointState(wsValue)) + { + case WorldPackets::Battleground::BattlegroundCapturePointState::AllianceCaptured: + ++alliance; + break; + case WorldPackets::Battleground::BattlegroundCapturePointState::HordeCaptured: + ++horde; + break; + default: + break; + } + } + } + } + + Team GetPrematureWinner() override + { + // How many bases each team owns + uint8 ally = 0, horde = 0; + _CalculateTeamNodes(ally, horde); + + if (ally > horde) + return ALLIANCE; + if (horde > ally) + return HORDE; + + // If the values are equal, fall back to the original result (based on number of players on each team) + return BattlegroundScript::GetPrematureWinner(); + } + + void ProcessEvent(WorldObject* /*source*/, uint32 eventId, WorldObject* invoker) override + { + switch (eventId) + { + case AB_EVENT_START_BATTLE: + { + for (ObjectGuid const& guid : _creaturesToRemoveOnMatchStart) + if (Creature* creature = battlegroundMap->GetCreature(guid)) + creature->DespawnOrUnsummon(); + + for (ObjectGuid const& guid : _gameobjectsToRemoveOnMatchStart) + if (GameObject* gameObject = battlegroundMap->GetGameObject(guid)) + gameObject->DespawnOrUnsummon(); + + for (ObjectGuid const& guid : _doors) + { + if (GameObject* gameObject = battlegroundMap->GetGameObject(guid)) + { + gameObject->UseDoorOrButton(); + gameObject->DespawnOrUnsummon(3s); + } + } + break; + } + case AB_EVENT_CONTESTED_BLACKSMITH_ALLIANCE: + UpdateWorldState(BG_AB_WS_BLACKSMITH_ALLIANCE_CONTROL_STATE, 1); + UpdateWorldState(BG_AB_WS_BLACKSMITH_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_BLACKSMITH_ALLIANCE: + UpdateWorldState(BG_AB_WS_BLACKSMITH_ALLIANCE_CONTROL_STATE, 2); + UpdateWorldState(BG_AB_WS_BLACKSMITH_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_BLACKSMITH_ALLIANCE: + UpdateWorldState(BG_AB_WS_BLACKSMITH_ALLIANCE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + break; + case AB_EVENT_CONTESTED_BLACKSMITH_HORDE: + UpdateWorldState(BG_AB_WS_BLACKSMITH_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_BLACKSMITH_HORDE_CONTROL_STATE, 1); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_BLACKSMITH_HORDE: + UpdateWorldState(BG_AB_WS_BLACKSMITH_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_BLACKSMITH_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_BLACKSMITH_HORDE: + UpdateWorldState(BG_AB_WS_BLACKSMITH_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + break; + case AB_EVENT_CONTESTED_FARM_ALLIANCE: + UpdateWorldState(BG_AB_WS_FARM_ALLIANCE_CONTROL_STATE, 1); + UpdateWorldState(BG_AB_WS_FARM_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_FARM_ALLIANCE: + UpdateWorldState(BG_AB_WS_FARM_ALLIANCE_CONTROL_STATE, 2); + UpdateWorldState(BG_AB_WS_FARM_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_FARM_ALLIANCE: + UpdateWorldState(BG_AB_WS_FARM_ALLIANCE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + break; + case AB_EVENT_CONTESTED_FARM_HORDE: + UpdateWorldState(BG_AB_WS_FARM_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_FARM_HORDE_CONTROL_STATE, 1); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_FARM_HORDE: + UpdateWorldState(BG_AB_WS_FARM_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_FARM_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_FARM_HORDE: + UpdateWorldState(BG_AB_WS_FARM_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + break; + case AB_EVENT_CONTESTED_GOLD_MINE_ALLIANCE: + UpdateWorldState(BG_AB_WS_GOLD_MINE_ALLIANCE_CONTROL_STATE, 1); + UpdateWorldState(BG_AB_WS_GOLD_MINE_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_GOLD_MINE_ALLIANCE: + UpdateWorldState(BG_AB_WS_GOLD_MINE_ALLIANCE_CONTROL_STATE, 2); + UpdateWorldState(BG_AB_WS_GOLD_MINE_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_GOLD_MINE_ALLIANCE: + UpdateWorldState(BG_AB_WS_GOLD_MINE_ALLIANCE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + break; + case AB_EVENT_CONTESTED_GOLD_MINE_HORDE: + UpdateWorldState(BG_AB_WS_GOLD_MINE_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_GOLD_MINE_HORDE_CONTROL_STATE, 1); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_GOLD_MINE_HORDE: + UpdateWorldState(BG_AB_WS_GOLD_MINE_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_GOLD_MINE_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_GOLD_MINE_HORDE: + UpdateWorldState(BG_AB_WS_GOLD_MINE_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + break; + case AB_EVENT_CONTESTED_LUMBER_MILL_ALLIANCE: + UpdateWorldState(BG_AB_WS_LUMBER_MILL_ALLIANCE_CONTROL_STATE, 1); + UpdateWorldState(BG_AB_WS_LUMBER_MILL_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_LUMBER_MILL_ALLIANCE: + UpdateWorldState(BG_AB_WS_LUMBER_MILL_ALLIANCE_CONTROL_STATE, 2); + UpdateWorldState(BG_AB_WS_LUMBER_MILL_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_LUMBER_MILL_ALLIANCE: + UpdateWorldState(BG_AB_WS_LUMBER_MILL_ALLIANCE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + break; + case AB_EVENT_CONTESTED_LUMBER_MILL_HORDE: + UpdateWorldState(BG_AB_WS_LUMBER_MILL_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_LUMBER_MILL_HORDE_CONTROL_STATE, 1); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_LUMBER_MILL_HORDE: + UpdateWorldState(BG_AB_WS_LUMBER_MILL_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_LUMBER_MILL_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_LUMBER_MILL_HORDE: + UpdateWorldState(BG_AB_WS_LUMBER_MILL_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + break; + case AB_EVENT_CONTESTED_STABLES_ALLIANCE: + UpdateWorldState(BG_AB_WS_STABLES_ALLIANCE_CONTROL_STATE, 1); + UpdateWorldState(BG_AB_WS_STABLES_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_STABLES_ALLIANCE: + UpdateWorldState(BG_AB_WS_STABLES_ALLIANCE_CONTROL_STATE, 2); + UpdateWorldState(BG_AB_WS_STABLES_HORDE_CONTROL_STATE, 0); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_STABLES_ALLIANCE: + UpdateWorldState(BG_AB_WS_STABLES_ALLIANCE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + break; + case AB_EVENT_CONTESTED_STABLES_HORDE: + UpdateWorldState(BG_AB_WS_STABLES_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_STABLES_HORDE_CONTROL_STATE, 1); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_ASSAULTED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + break; + case AB_EVENT_DEFENDED_STABLES_HORDE: + UpdateWorldState(BG_AB_WS_STABLES_ALLIANCE_CONTROL_STATE, 0); + UpdateWorldState(BG_AB_WS_STABLES_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + if (Player* player = invoker->ToPlayer()) + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + break; + case AB_EVENT_CAPTURE_STABLES_HORDE: + UpdateWorldState(BG_AB_WS_STABLES_HORDE_CONTROL_STATE, 2); + battleground->PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + break; + default: + TC_LOG_WARN("bg.events", "BattlegroundAB::ProcessEvent: Unhandled event %u.", eventId); + break; + } + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case BG_AB_NPC_THE_BLACK_BRIDE: + case BG_AB_NPC_RADULF_LEDER: + _creaturesToRemoveOnMatchStart.push_back(creature->GetGUID()); + break; + default: + break; + } + } + + void OnGameObjectCreate(GameObject* gameObject) override + { + if (gameObject->GetGOInfo()->type == GAMEOBJECT_TYPE_CAPTURE_POINT) + _capturePoints.push_back(gameObject->GetGUID()); + + switch (gameObject->GetEntry()) + { + case BG_AB_OBJECTID_GHOST_GATE: + _gameobjectsToRemoveOnMatchStart.push_back(gameObject->GetGUID()); + break; + case BG_AB_OBJECTID_ALLIANCE_DOOR: + case BG_AB_OBJECTID_HORDE_DOOR: + _doors.push_back(gameObject->GetGUID()); + break; + default: + break; + } + } + + void OnEnd(Team winner) override + { + BattlegroundScript::OnEnd(winner); + + // Win reward + if (winner == ALLIANCE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), ALLIANCE); + if (winner == HORDE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), HORDE); + // Complete map_end rewards (even if no team wins) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), HORDE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), ALLIANCE); + } + +private: + uint32 _lastTick; + std::array<uint32, PVP_TEAMS_COUNT> _honorScoreTics; + std::array<uint32, PVP_TEAMS_COUNT> _reputationScoreTics; + bool _isInformedNearVictory; + uint32 _honorTics; + uint32 _reputationTics; + + GuidVector _gameobjectsToRemoveOnMatchStart; + GuidVector _creaturesToRemoveOnMatchStart; + GuidVector _doors; + GuidVector _capturePoints; +}; + +void AddSC_battleground_arathi_basin() +{ + RegisterBattlegroundMapScript(battleground_arathi_basin, 2107); +} diff --git a/src/server/scripts/Battlegrounds/BattleForGilneas/battleground_battle_for_gilneas.cpp b/src/server/scripts/Battlegrounds/BattleForGilneas/battleground_battle_for_gilneas.cpp new file mode 100644 index 00000000000..ff049d2dd11 --- /dev/null +++ b/src/server/scripts/Battlegrounds/BattleForGilneas/battleground_battle_for_gilneas.cpp @@ -0,0 +1,35 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BattlegroundScript.h" +#include "ScriptMgr.h" + +struct battleground_battle_for_gilneas : BattlegroundScript +{ + enum PvpStats : uint32 + { + BFG_OBJECTIVE_BASES_ASSAULTED = 370, + BFG_OBJECTIVE_BASES_DEFENDED = 371 + }; + + explicit battleground_battle_for_gilneas(BattlegroundMap* map) : BattlegroundScript(map) { } +}; + +void AddSC_battleground_battle_for_gilneas() +{ + RegisterBattlegroundMapScript(battleground_battle_for_gilneas, 761); +} diff --git a/src/server/scripts/Battlegrounds/BladesEdge/arena_blades_edge.cpp b/src/server/scripts/Battlegrounds/BladesEdge/arena_blades_edge.cpp new file mode 100644 index 00000000000..f0ac3bfc212 --- /dev/null +++ b/src/server/scripts/Battlegrounds/BladesEdge/arena_blades_edge.cpp @@ -0,0 +1,84 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Battleground.h" +#include "BattlegroundScript.h" +#include "GameObject.h" +#include "Map.h" +#include "ScriptMgr.h" +#include "TaskScheduler.h" + +struct arena_blades_edge : ArenaScript +{ + enum GameObjects + { + BG_BE_OBJECT_TYPE_DOOR_1 = 183971, + BG_BE_OBJECT_TYPE_DOOR_2 = 183973, + BG_BE_OBJECT_TYPE_DOOR_3 = 183970, + BG_BE_OBJECT_TYPE_DOOR_4 = 183972, + BG_BE_OBJECT_TYPE_BUFF_1 = 184663, + BG_BE_OBJECT_TYPE_BUFF_2 = 184664 + }; + + explicit arena_blades_edge(BattlegroundMap* map) : ArenaScript(map) { } + + void OnUpdate(uint32 diff) override + { + _scheduler.Update(diff); + } + + void OnInit() override + { + AddDoor(BG_BE_OBJECT_TYPE_DOOR_1, 6287.277f, 282.1877f, 3.810925f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f); + AddDoor(BG_BE_OBJECT_TYPE_DOOR_2, 6189.546f, 241.7099f, 3.101481f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f); + AddDoor(BG_BE_OBJECT_TYPE_DOOR_3, 6299.116f, 296.5494f, 3.308032f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f); + AddDoor(BG_BE_OBJECT_TYPE_DOOR_4, 6177.708f, 227.3481f, 3.604374f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f); + } + + void OnStart() override + { + for (ObjectGuid const& guid : _doorGUIDs) + { + if (GameObject* door = battlegroundMap->GetGameObject(guid)) + { + door->UseDoorOrButton(); + door->DespawnOrUnsummon(5s); + } + } + + _scheduler.Schedule(1min, [&](TaskContext) + { + CreateObject(BG_BE_OBJECT_TYPE_BUFF_1, 6249.042f, 275.3239f, 11.22033f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f); + CreateObject(BG_BE_OBJECT_TYPE_BUFF_2, 6228.26f, 249.566f, 11.21812f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f); + }); + } + + void AddDoor(uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, GOState goState = GO_STATE_READY) + { + if (GameObject const* go = CreateObject(entry, x, y, z, o, rotation0, rotation1, rotation2, rotation3, goState)) + _doorGUIDs.emplace_back(go->GetGUID()); + } + +private: + GuidVector _doorGUIDs; + TaskScheduler _scheduler; +}; + +void AddSC_arena_blades_edge() +{ + RegisterBattlegroundMapScript(arena_blades_edge, 1672); +} diff --git a/src/server/scripts/Battlegrounds/DalaranSewers/arena_dalaran_sewers.cpp b/src/server/scripts/Battlegrounds/DalaranSewers/arena_dalaran_sewers.cpp new file mode 100644 index 00000000000..bbdf5d4ab3b --- /dev/null +++ b/src/server/scripts/Battlegrounds/DalaranSewers/arena_dalaran_sewers.cpp @@ -0,0 +1,266 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Battleground.h" +#include "BattlegroundScript.h" +#include "Creature.h" +#include "Duration.h" +#include "EventMap.h" +#include "GameObject.h" +#include "Map.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "TaskScheduler.h" + +struct arena_dalaran_sewers : ArenaScript +{ + enum GameObjects + { + BG_DS_OBJECT_TYPE_DOOR_1 = 192642, + BG_DS_OBJECT_TYPE_DOOR_2 = 192643, + BG_DS_OBJECT_TYPE_WATER_1 = 194395, // Collision + BG_DS_OBJECT_TYPE_WATER_2 = 191877, + BG_DS_OBJECT_TYPE_BUFF_1 = 184663, + BG_DS_OBJECT_TYPE_BUFF_2 = 184664 + }; + + enum Events + { + BG_DS_EVENT_WATERFALL_WARNING = 1, // Water starting to fall, but no LoS Blocking nor movement blocking + BG_DS_EVENT_WATERFALL_ON = 2, // LoS and Movement blocking active + BG_DS_EVENT_WATERFALL_OFF = 3, + BG_DS_EVENT_WATERFALL_KNOCKBACK = 4, + + BG_DS_EVENT_PIPE_KNOCKBACK = 5 + }; + + enum Creatures + { + BG_DS_NPC_TYPE_WATER_SPOUT = 28567 + }; + + enum Spells + { + BG_DS_SPELL_FLUSH = 57405, // Visual and target selector for the starting knockback from the pipe + BG_DS_SPELL_FLUSH_KNOCKBACK = 61698, // Knockback effect for previous spell (triggered, not needed to be cast) + BG_DS_SPELL_WATER_SPOUT = 58873, // Knockback effect of the central waterfall + + SPELL_WARL_DEMONIC_CIRCLE = 48018 // Demonic Circle Summon + }; + + enum Data + { + // These values are NOT blizzlike... need the correct data! + BG_DS_PIPE_KNOCKBACK_FIRST_DELAY = 5000, + BG_DS_PIPE_KNOCKBACK_DELAY = 3000 + }; + + // These values are NOT blizzlike... need the correct data! + static constexpr Seconds BG_DS_WATERFALL_TIMER_MIN = 30s; + static constexpr Seconds BG_DS_WATERFALL_TIMER_MAX = 60s; + static constexpr Seconds BG_DS_WATERFALL_WARNING_DURATION = 5s; + static constexpr Seconds BG_DS_WATERFALL_DURATION = 30s; + static constexpr Milliseconds BG_DS_WATERFALL_KNOCKBACK_TIMER = 1500ms; + static constexpr uint32 BG_DS_DATA_PIPE_KNOCKBACK_COUNT = 1; + static constexpr uint32 BG_DS_PIPE_KNOCKBACK_TOTAL_COUNT = 2; + + explicit arena_dalaran_sewers(BattlegroundMap* map) : ArenaScript(map), _pipeKnockBackTimer(BG_DS_PIPE_KNOCKBACK_FIRST_DELAY), _pipeKnockBackCount(0) { } + + void OnUpdate(uint32 diff) override + { + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + _events.Update(diff); + _scheduler.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case BG_DS_EVENT_WATERFALL_WARNING: + // Add the water + if (GameObject* go = battlegroundMap->GetGameObject(_water2GUID)) + go->ResetDoorOrButton(); + _events.ScheduleEvent(BG_DS_EVENT_WATERFALL_ON, BG_DS_WATERFALL_WARNING_DURATION); + break; + case BG_DS_EVENT_WATERFALL_ON: + // Active collision and start knockback timer + if (GameObject* go = battlegroundMap->GetGameObject(_water1GUID)) + go->ResetDoorOrButton(); + _events.ScheduleEvent(BG_DS_EVENT_WATERFALL_OFF, BG_DS_WATERFALL_DURATION); + _events.ScheduleEvent(BG_DS_EVENT_WATERFALL_KNOCKBACK, BG_DS_WATERFALL_KNOCKBACK_TIMER); + break; + case BG_DS_EVENT_WATERFALL_OFF: + // Remove collision and water + if (GameObject* go = battlegroundMap->GetGameObject(_water1GUID)) + go->UseDoorOrButton(); + if (GameObject* go = battlegroundMap->GetGameObject(_water2GUID)) + go->UseDoorOrButton(); + _events.CancelEvent(BG_DS_EVENT_WATERFALL_KNOCKBACK); + _events.ScheduleEvent(BG_DS_EVENT_WATERFALL_WARNING, BG_DS_WATERFALL_TIMER_MIN, BG_DS_WATERFALL_TIMER_MAX); + break; + case BG_DS_EVENT_WATERFALL_KNOCKBACK: + // Repeat knockback while the waterfall still active + if (Creature* waterSpout = battlegroundMap->GetCreature(_waterfallCreatureGUID)) + waterSpout->CastSpell(waterSpout, BG_DS_SPELL_WATER_SPOUT, true); + _events.ScheduleEvent(eventId, BG_DS_WATERFALL_KNOCKBACK_TIMER); + break; + case BG_DS_EVENT_PIPE_KNOCKBACK: + for (ObjectGuid const& guid : _pipeCreatureGUIDs) + if (Creature* waterSpout = battlegroundMap->GetCreature(guid)) + waterSpout->CastSpell(waterSpout, BG_DS_SPELL_FLUSH, true); + break; + default: + break; + } + } + + if (_pipeKnockBackCount < BG_DS_PIPE_KNOCKBACK_TOTAL_COUNT) + { + if (_pipeKnockBackTimer < diff) + { + for (ObjectGuid const& guid : _pipeCreatureGUIDs) + if (Creature* waterSpout = battlegroundMap->GetCreature(guid)) + waterSpout->CastSpell(waterSpout, BG_DS_SPELL_FLUSH, true); + + ++_pipeKnockBackCount; + _pipeKnockBackTimer = BG_DS_PIPE_KNOCKBACK_DELAY; + } + else + _pipeKnockBackTimer -= diff; + } + + } + + void OnInit() override + { + AddObject(BG_DS_OBJECT_TYPE_DOOR_1, 1350.95f, 817.2f, 20.8096f, 3.15f, 0, 0, 0.99627f, 0.0862864f, GO_STATE_READY, _doorGUIDs); + AddObject(BG_DS_OBJECT_TYPE_DOOR_2, 1232.65f, 764.913f, 20.0729f, 6.3f, 0, 0, 0.0310211f, -0.999519f, GO_STATE_READY, _doorGUIDs); + + if (GameObject const* go = CreateObject(BG_DS_OBJECT_TYPE_WATER_1, 1291.56f, 790.837f, 7.1f, 3.14238f, 0, 0, 0.694215f, -0.719768f, GO_STATE_READY)) + _water1GUID = go->GetGUID(); + + if (GameObject const* go = CreateObject(BG_DS_OBJECT_TYPE_WATER_2, 1291.56f, 790.837f, 7.1f, 3.14238f, 0, 0, 0.694215f, -0.719768f, GO_STATE_READY)) + _water2GUID = go->GetGUID(); + } + + void OnStart() override + { + for (ObjectGuid const& guid : _doorGUIDs) + { + if (GameObject* door = battlegroundMap->GetGameObject(guid)) + { + door->UseDoorOrButton(); + door->DespawnOrUnsummon(5s); + } + } + + _scheduler.Schedule(1min, [&](TaskContext) + { + CreateObject(BG_DS_OBJECT_TYPE_BUFF_1, 1291.7f, 813.424f, 7.11472f, 4.64562f, 0, 0, 0.730314f, -0.683111f); + CreateObject(BG_DS_OBJECT_TYPE_BUFF_2, 1291.7f, 768.911f, 7.11472f, 1.55194f, 0, 0, 0.700409f, 0.713742f); + }); + _events.ScheduleEvent(BG_DS_EVENT_WATERFALL_WARNING, BG_DS_WATERFALL_TIMER_MIN, BG_DS_WATERFALL_TIMER_MAX); + _pipeKnockBackTimer = BG_DS_PIPE_KNOCKBACK_FIRST_DELAY; + + // Remove collision and water + if (GameObject* go = battlegroundMap->GetGameObject(_water1GUID)) + go->UseDoorOrButton(); + if (GameObject* go = battlegroundMap->GetGameObject(_water2GUID)) + go->UseDoorOrButton(); + + for (const auto& [playerGuid, _] : battleground->GetPlayers()) + if (Player* player = ObjectAccessor::FindPlayer(playerGuid)) + player->RemoveAurasDueToSpell(SPELL_WARL_DEMONIC_CIRCLE); + } + + void AddObject(uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, GOState goState, GuidVector guidVector) const + { + if (GameObject const* go = CreateObject(entry, x, y, z, o, rotation0, rotation1, rotation2, rotation3, goState)) + guidVector.emplace_back(go->GetGUID()); + } + + void SetData(uint32 dataId, uint32 value) override + { + ArenaScript::SetData(dataId, value); + if (dataId == BG_DS_DATA_PIPE_KNOCKBACK_COUNT) + _pipeKnockBackCount = value; + } + + uint32 GetData(uint32 dataId) const override + { + if (dataId == BG_DS_DATA_PIPE_KNOCKBACK_COUNT) + return _pipeKnockBackCount; + + return ArenaScript::GetData(dataId); + } + +private: + GuidVector _doorGUIDs; + ObjectGuid _water1GUID; + ObjectGuid _water2GUID; + ObjectGuid _waterfallCreatureGUID; + GuidVector _pipeCreatureGUIDs; + TaskScheduler _scheduler; + EventMap _events; + + uint32 _pipeKnockBackTimer; + uint8 _pipeKnockBackCount; +}; + +class at_ds_pipe_knockback : public AreaTriggerScript +{ +public: + at_ds_pipe_knockback() : AreaTriggerScript("at_ds_pipe_knockback") { } + + void Trigger(Player* player) const + { + if (Battleground const* battleground = player->GetBattleground()) + { + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + // Remove effects of Demonic Circle Summon + player->RemoveAurasDueToSpell(arena_dalaran_sewers::SPELL_WARL_DEMONIC_CIRCLE); + + // Someone has get back into the pipes and the knockback has already been performed, + // so we reset the knockback count for kicking the player again into the arena. + if (battleground->GetBgMap()->GetBattlegroundScript()->GetData(arena_dalaran_sewers::BG_DS_DATA_PIPE_KNOCKBACK_COUNT) >= arena_dalaran_sewers::BG_DS_PIPE_KNOCKBACK_TOTAL_COUNT) + battleground->GetBgMap()->GetBattlegroundScript()->SetData(arena_dalaran_sewers::BG_DS_DATA_PIPE_KNOCKBACK_COUNT, 0); + } + } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override + { + Trigger(player); + return true; + } + + bool OnExit(Player* player, AreaTriggerEntry const* /*trigger*/) override + { + Trigger(player); + return true; + } +}; + +void AddSC_arena_dalaran_sewers() +{ + RegisterBattlegroundMapScript(arena_dalaran_sewers, 617); + new at_ds_pipe_knockback(); +} diff --git a/src/server/scripts/Battlegrounds/EyeOfTheStorm/battleground_eye_of_the_storm.cpp b/src/server/scripts/Battlegrounds/EyeOfTheStorm/battleground_eye_of_the_storm.cpp new file mode 100644 index 00000000000..3c8ef48438e --- /dev/null +++ b/src/server/scripts/Battlegrounds/EyeOfTheStorm/battleground_eye_of_the_storm.cpp @@ -0,0 +1,677 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "AreaTrigger.h" +#include "BattlegroundScript.h" +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "GameObject.h" +#include "GameTime.h" +#include "Map.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "SpellAuras.h" +#include "Timer.h" + +enum EyeOfTheStormWorldStates +{ + EY_ALLIANCE_RESOURCES = 1776, + EY_HORDE_RESOURCES = 1777, + EY_MAX_RESOURCES = 1780, + EY_ALLIANCE_BASE = 2752, + EY_HORDE_BASE = 2753, + DRAENEI_RUINS_HORDE_CONTROL = 2733, + DRAENEI_RUINS_ALLIANCE_CONTROL = 2732, + DRAENEI_RUINS_UNCONTROL = 2731, + MAGE_TOWER_ALLIANCE_CONTROL = 2730, + MAGE_TOWER_HORDE_CONTROL = 2729, + MAGE_TOWER_UNCONTROL = 2728, + FEL_REAVER_HORDE_CONTROL = 2727, + FEL_REAVER_ALLIANCE_CONTROL = 2726, + FEL_REAVER_UNCONTROL = 2725, + BLOOD_ELF_HORDE_CONTROL = 2724, + BLOOD_ELF_ALLIANCE_CONTROL = 2723, + BLOOD_ELF_UNCONTROL = 2722, + PROGRESS_BAR_PERCENT_GREY = 2720, //100 = empty (only grey), 0 = blue|red (no grey) + PROGRESS_BAR_STATUS = 2719, //50 init!, 48 ... hordak bere .. 33 .. 0 = full 100% hordacky, 100 = full alliance + PROGRESS_BAR_SHOW = 2718, //1 init, 0 druhy send - bez messagu, 1 = controlled aliance + NETHERSTORM_FLAG = 8863, + //set to 2 when flag is picked up, and to 1 if it is dropped + NETHERSTORM_FLAG_STATE_ALLIANCE = 9808, + NETHERSTORM_FLAG_STATE_HORDE = 9809, + + DRAENEI_RUINS_HORDE_CONTROL_STATE = 17362, + DRAENEI_RUINS_ALLIANCE_CONTROL_STATE = 17366, + MAGE_TOWER_HORDE_CONTROL_STATE = 17361, + MAGE_TOWER_ALLIANCE_CONTROL_STATE = 17368, + FEL_REAVER_HORDE_CONTROL_STATE = 17364, + FEL_REAVER_ALLIANCE_CONTROL_STATE = 17367, + BLOOD_ELF_HORDE_CONTROL_STATE = 17363, + BLOOD_ELF_ALLIANCE_CONTROL_STATE = 17365, +}; + +enum EyeOfTheStormSounds +{ + //strange ids, but sure about them + BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, + BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, + BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, + BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + BG_EY_SOUND_FLAG_RESET = 8192 +}; + +enum EyeOfTheStormSpells +{ + BG_EY_NETHERSTORM_FLAG_SPELL = 34976, + BG_EY_PLAYER_DROPPED_FLAG_SPELL = 34991, + + // Focused/Brutal Assault + BG_EY_FOCUSED_ASSAULT_SPELL = 46392, + BG_EY_BRUTAL_ASSAULT_SPELL = 46393 +}; + +enum EyeOfTheStormObjectEntry +{ + BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door + BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door + BG_OBJECT_FLAG2_EY_ENTRY = 208977, //Netherstorm flag (flagstand) + BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt + BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt + BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt + BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083, //Draenei Tower Cap Pt +}; + +enum EyeOfTheStormPointsTrigger +{ + AREATRIGGER_CAPTURE_FLAG = 33 +}; + +enum EyeOfTheStormPoints +{ + FEL_REAVER = 0, + BLOOD_ELF = 1, + DRAENEI_RUINS = 2, + MAGE_TOWER = 3, + + EY_PLAYERS_OUT_OF_POINTS = 4, + EY_POINTS_MAX = 4 +}; + +constexpr uint32 BG_EY_NotEYWeekendHonorTicks = 260; +constexpr uint32 BG_EY_EYWeekendHonorTicks = 160; + +enum BG_EY_Score +{ + BG_EY_WARNING_NEAR_VICTORY_SCORE = 1200, + BG_EY_MAX_TEAM_SCORE = 1500 +}; + +enum BG_EY_FlagState +{ + BG_EY_FLAG_STATE_ON_BASE = 0, + BG_EY_FLAG_STATE_WAIT_RESPAWN = 1, + BG_EY_FLAG_STATE_ON_PLAYER = 2, + BG_EY_FLAG_STATE_ON_GROUND = 3 +}; + +enum EYBattlegroundPointState +{ + EY_POINT_NO_OWNER = 0, + EY_POINT_STATE_UNCONTROLLED = 0, + EY_POINT_UNDER_CONTROL = 3 +}; + +enum BG_EY_ExploitTeleportLocations +{ + EY_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3773, + EY_EXPLOIT_TELEPORT_LOCATION_HORDE = 3772 +}; + +enum EyeOFtheStormBroadcastTexts +{ + BG_EY_TEXT_ALLIANCE_TAKEN_FEL_REAVER_RUINS = 17828, + BG_EY_TEXT_HORDE_TAKEN_FEL_REAVER_RUINS = 17829, + BG_EY_TEXT_ALLIANCE_LOST_FEL_REAVER_RUINS = 91961, + BG_EY_TEXT_HORDE_LOST_FEL_REAVER_RUINS = 91962, + + BG_EY_TEXT_ALLIANCE_TAKEN_BLOOD_ELF_TOWER = 17819, + BG_EY_TEXT_HORDE_TAKEN_BLOOD_ELF_TOWER = 17823, + BG_EY_TEXT_ALLIANCE_LOST_BLOOD_ELF_TOWER = 91957, + BG_EY_TEXT_HORDE_LOST_BLOOD_ELF_TOWER = 91958, + + BG_EY_TEXT_ALLIANCE_TAKEN_DRAENEI_RUINS = 17827, + BG_EY_TEXT_HORDE_TAKEN_DRAENEI_RUINS = 91917, + BG_EY_TEXT_ALLIANCE_LOST_DRAENEI_RUINS = 91959, + BG_EY_TEXT_HORDE_LOST_DRAENEI_RUINS = 91960, + + BG_EY_TEXT_ALLIANCE_TAKEN_MAGE_TOWER = 17824, + BG_EY_TEXT_HORDE_TAKEN_MAGE_TOWER = 17825, + BG_EY_TEXT_ALLIANCE_LOST_MAGE_TOWER = 91963, + BG_EY_TEXT_HORDE_LOST_MAGE_TOWER = 91964, + + BG_EY_TEXT_TAKEN_FLAG = 18359, + BG_EY_TEXT_FLAG_DROPPED = 18361, + BG_EY_TEXT_FLAG_RESET = 18364, + BG_EY_TEXT_ALLIANCE_CAPTURED_FLAG = 18375, + BG_EY_TEXT_HORDE_CAPTURED_FLAG = 18384, +}; + +struct BattlegroundEYPointIconsStruct +{ + BattlegroundEYPointIconsStruct(uint32 _WorldStateControlIndex, uint32 _WorldStateAllianceControlledIndex, uint32 _WorldStateHordeControlledIndex, + uint32 worldStateAllianceStatusBarIcon, uint32 worldStateHordeStatusBarIcon) + : WorldStateControlIndex(_WorldStateControlIndex), WorldStateAllianceControlledIndex(_WorldStateAllianceControlledIndex), WorldStateHordeControlledIndex(_WorldStateHordeControlledIndex), + WorldStateAllianceStatusBarIcon(worldStateAllianceStatusBarIcon), WorldStateHordeStatusBarIcon(worldStateHordeStatusBarIcon) { } + uint32 WorldStateControlIndex; + uint32 WorldStateAllianceControlledIndex; + uint32 WorldStateHordeControlledIndex; + uint32 WorldStateAllianceStatusBarIcon; + uint32 WorldStateHordeStatusBarIcon; +}; + +struct BattlegroundEYLosingPointStruct +{ + BattlegroundEYLosingPointStruct(uint32 _MessageIdAlliance, uint32 _MessageIdHorde) + : MessageIdAlliance(_MessageIdAlliance), MessageIdHorde(_MessageIdHorde) + { } + + uint32 MessageIdAlliance; + uint32 MessageIdHorde; +}; + +struct BattlegroundEYCapturingPointStruct +{ + BattlegroundEYCapturingPointStruct(uint32 _MessageIdAlliance, uint32 _MessageIdHorde) + : MessageIdAlliance(_MessageIdAlliance), MessageIdHorde(_MessageIdHorde) + { } + + uint32 MessageIdAlliance; + uint32 MessageIdHorde; +}; + +class BattlegroundEYControlZoneHandler : public ControlZoneHandler +{ +public: + explicit BattlegroundEYControlZoneHandler(uint32 point) : _point(point) { } + + uint32 GetPoint() const { return _point; } + +private: + uint32 _point; +}; + +const std::array<BattlegroundEYPointIconsStruct, EY_POINTS_MAX> m_PointsIconStruct = +{ + BattlegroundEYPointIconsStruct(FEL_REAVER_UNCONTROL, FEL_REAVER_ALLIANCE_CONTROL, FEL_REAVER_HORDE_CONTROL, FEL_REAVER_ALLIANCE_CONTROL_STATE, FEL_REAVER_HORDE_CONTROL_STATE), + BattlegroundEYPointIconsStruct(BLOOD_ELF_UNCONTROL, BLOOD_ELF_ALLIANCE_CONTROL, BLOOD_ELF_HORDE_CONTROL, BLOOD_ELF_ALLIANCE_CONTROL_STATE, BLOOD_ELF_HORDE_CONTROL_STATE), + BattlegroundEYPointIconsStruct(DRAENEI_RUINS_UNCONTROL, DRAENEI_RUINS_ALLIANCE_CONTROL, DRAENEI_RUINS_HORDE_CONTROL, DRAENEI_RUINS_ALLIANCE_CONTROL_STATE, DRAENEI_RUINS_HORDE_CONTROL_STATE), + BattlegroundEYPointIconsStruct(MAGE_TOWER_UNCONTROL, MAGE_TOWER_ALLIANCE_CONTROL, MAGE_TOWER_HORDE_CONTROL, MAGE_TOWER_ALLIANCE_CONTROL_STATE, MAGE_TOWER_HORDE_CONTROL_STATE) +}; + +const std::array<BattlegroundEYLosingPointStruct, EY_POINTS_MAX> m_LosingPointTypes = +{ + BattlegroundEYLosingPointStruct(BG_EY_TEXT_ALLIANCE_LOST_FEL_REAVER_RUINS, BG_EY_TEXT_HORDE_LOST_FEL_REAVER_RUINS), + BattlegroundEYLosingPointStruct(BG_EY_TEXT_ALLIANCE_LOST_BLOOD_ELF_TOWER, BG_EY_TEXT_HORDE_LOST_BLOOD_ELF_TOWER), + BattlegroundEYLosingPointStruct(BG_EY_TEXT_ALLIANCE_LOST_DRAENEI_RUINS, BG_EY_TEXT_HORDE_LOST_DRAENEI_RUINS), + BattlegroundEYLosingPointStruct(BG_EY_TEXT_ALLIANCE_LOST_MAGE_TOWER, BG_EY_TEXT_HORDE_LOST_MAGE_TOWER) +}; + +const std::array<BattlegroundEYCapturingPointStruct, EY_POINTS_MAX> m_CapturingPointTypes = +{ + BattlegroundEYCapturingPointStruct(BG_EY_TEXT_ALLIANCE_TAKEN_FEL_REAVER_RUINS, BG_EY_TEXT_HORDE_TAKEN_FEL_REAVER_RUINS), + BattlegroundEYCapturingPointStruct(BG_EY_TEXT_ALLIANCE_TAKEN_BLOOD_ELF_TOWER, BG_EY_TEXT_HORDE_TAKEN_BLOOD_ELF_TOWER), + BattlegroundEYCapturingPointStruct(BG_EY_TEXT_ALLIANCE_TAKEN_DRAENEI_RUINS, BG_EY_TEXT_HORDE_TAKEN_DRAENEI_RUINS), + BattlegroundEYCapturingPointStruct(BG_EY_TEXT_ALLIANCE_TAKEN_MAGE_TOWER, BG_EY_TEXT_HORDE_TAKEN_MAGE_TOWER) +}; + +struct battleground_eye_of_the_storm : BattlegroundScript +{ + enum PvpStats + { + PVP_STAT_FLAG_CAPTURES = 183 + }; + + static constexpr Seconds POINTS_TICK_TIME = 2s; + static constexpr Seconds BG_EY_FLAG_ASSAULT_TIMER = 30s; + static constexpr uint16 BG_EY_FLAG_BRUTAL_ASSAULT_STACK_COUNT = 5; + static constexpr uint32 BG_EY_EVENT_START_BATTLE = 13180; + + static constexpr std::array<uint8, EY_POINTS_MAX> BG_EY_TickPoints = { 1, 2, 5, 10 }; + static constexpr std::array<uint32, EY_POINTS_MAX> BG_EY_FlagPoints = { 75, 85, 100, 500 }; + + explicit battleground_eye_of_the_storm(BattlegroundMap* map) : BattlegroundScript(map) + { + _honorScoreTics = { 0, 0 }; + _honorTics = 0; + _pointsTimer.Reset(POINTS_TICK_TIME); + _assaultEnabled = false; + _assaultStackCount = 0; + _flagAssaultTimer.Reset(BG_EY_FLAG_ASSAULT_TIMER); + + _controlZoneHandlers[BG_OBJECT_FR_TOWER_CAP_EY_ENTRY] = std::make_unique<BattlegroundEYControlZoneHandler>(FEL_REAVER); + _controlZoneHandlers[BG_OBJECT_BE_TOWER_CAP_EY_ENTRY] = std::make_unique<BattlegroundEYControlZoneHandler>(BLOOD_ELF); + _controlZoneHandlers[BG_OBJECT_DR_TOWER_CAP_EY_ENTRY] = std::make_unique<BattlegroundEYControlZoneHandler>(DRAENEI_RUINS); + _controlZoneHandlers[BG_OBJECT_HU_TOWER_CAP_EY_ENTRY] = std::make_unique<BattlegroundEYControlZoneHandler>(MAGE_TOWER); + + bool isBGWeekend = sBattlegroundMgr->IsBGWeekend(battleground->GetTypeID()); + _honorTics = (isBGWeekend) ? BG_EY_EYWeekendHonorTicks : BG_EY_NotEYWeekendHonorTicks; + } + + void OnInit() override + { + UpdateWorldState(EY_MAX_RESOURCES, BG_EY_MAX_TEAM_SCORE); + } + + void OnUpdate(uint32 diff) override + { + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + _pointsTimer.Update(diff); + if (_pointsTimer.Passed()) + { + _pointsTimer.Reset(POINTS_TICK_TIME); + + uint8 baseCountAlliance = GetControlledBaseCount(TEAM_ALLIANCE); + uint8 baseCountHorde = GetControlledBaseCount(TEAM_HORDE); + if (baseCountAlliance > 0) + AddPoint(ALLIANCE, BG_EY_TickPoints[baseCountAlliance - 1]); + if (baseCountHorde > 0) + AddPoint(HORDE, BG_EY_TickPoints[baseCountHorde - 1]); + } + + if (_assaultEnabled) + { + _flagAssaultTimer.Update(diff); + if (_flagAssaultTimer.Passed()) + { + _flagAssaultTimer.Reset(BG_EY_FLAG_ASSAULT_TIMER); + if (_assaultStackCount < std::numeric_limits<uint8>::max()) + { + _assaultStackCount++; + + // update assault debuff stacks + DoForFlagKeepers([&](Player* player) -> void + { + ApplyAssaultDebuffToPlayer(player); + }); + } + } + } + } + + void OnStart() override + { + for (ObjectGuid const& door : _doorGUIDs) + { + if (GameObject* gameObject = battlegroundMap->GetGameObject(door)) + { + gameObject->UseDoorOrButton(); + gameObject->DespawnOrUnsummon(3s); + } + } + + // Achievement: Flurry + TriggerGameEvent(BG_EY_EVENT_START_BATTLE); + } + + void AddPoint(Team team, uint32 points) + { + battleground->AddPoint(team, points); + TeamId const team_index = Battleground::GetTeamIndexByTeamId(team); + _honorScoreTics[team_index] += points; + if (_honorScoreTics[team_index] >= _honorTics) + { + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), team); + _honorScoreTics[team_index] -= _honorTics; + } + + UpdateTeamScore(team_index); + } + + uint8 GetControlledBaseCount(TeamId teamId) const + { + uint8 baseCount = 0; + for (auto const& controlZoneHandler : _controlZoneHandlers) + { + uint32 point = controlZoneHandler.second->GetPoint(); + switch (teamId) + { + case TEAM_ALLIANCE: + if (battlegroundMap->GetWorldStateValue(m_PointsIconStruct[point].WorldStateAllianceControlledIndex) == 1) + baseCount++; + break; + case TEAM_HORDE: + if (battlegroundMap->GetWorldStateValue(m_PointsIconStruct[point].WorldStateHordeControlledIndex) == 1) + baseCount++; + break; + default: + break; + } + } + return baseCount; + } + + void DoForFlagKeepers(std::function<void(Player*)> const& action) const + { + if (GameObject const* flag = battlegroundMap->GetGameObject(_flagGUID)) + if (Player* carrier = ObjectAccessor::FindPlayer(flag->GetFlagCarrierGUID())) + action(carrier); + } + + void ResetAssaultDebuff() + { + _assaultEnabled = false; + _assaultStackCount = 0; + _flagAssaultTimer.Reset(BG_EY_FLAG_ASSAULT_TIMER); + DoForFlagKeepers([&](Player* player) -> void + { + RemoveAssaultDebuffFromPlayer(player); + }); + } + + void ApplyAssaultDebuffToPlayer(Player* player) + { + if (_assaultStackCount == 0) + return; + + uint32 spellId = BG_EY_FOCUSED_ASSAULT_SPELL; + if (_assaultStackCount >= BG_EY_FLAG_BRUTAL_ASSAULT_STACK_COUNT) + { + player->RemoveAurasDueToSpell(BG_EY_FOCUSED_ASSAULT_SPELL); + spellId = BG_EY_BRUTAL_ASSAULT_SPELL; + } + + Aura* aura = player->GetAura(spellId); + if (!aura) + { + player->CastSpell(player, spellId, true); + aura = player->GetAura(spellId); + } + + if (aura) + aura->SetStackAmount(_assaultStackCount); + } + + void RemoveAssaultDebuffFromPlayer(Player* player) + { + player->RemoveAurasDueToSpell(BG_EY_FOCUSED_ASSAULT_SPELL); + player->RemoveAurasDueToSpell(BG_EY_BRUTAL_ASSAULT_SPELL); + } + + void UpdateTeamScore(TeamId Team) const + { + uint32 score = battleground->GetTeamScore(Team); + + if (score >= BG_EY_MAX_TEAM_SCORE) + { + score = BG_EY_MAX_TEAM_SCORE; + if (Team == TEAM_ALLIANCE) + battleground->EndBattleground(ALLIANCE); + else + battleground->EndBattleground(HORDE); + } + + if (Team == TEAM_ALLIANCE) + UpdateWorldState(EY_ALLIANCE_RESOURCES, score); + else + UpdateWorldState(EY_HORDE_RESOURCES, score); + } + + void OnEnd(Team winner) override + { + BattlegroundScript::OnEnd(winner); + // Win reward + if (winner == ALLIANCE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), ALLIANCE); + if (winner == HORDE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), HORDE); + + // Complete map reward + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), ALLIANCE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), HORDE); + } + + void UpdatePointsCount(TeamId teamId) const + { + if (teamId == TEAM_ALLIANCE) + UpdateWorldState(EY_ALLIANCE_BASE, GetControlledBaseCount(TEAM_ALLIANCE)); + else + UpdateWorldState(EY_HORDE_BASE, GetControlledBaseCount(TEAM_HORDE)); + } + + void OnGameObjectCreate(GameObject* gameObject) override + { + switch (gameObject->GetEntry()) + { + case BG_OBJECT_A_DOOR_EY_ENTRY: + case BG_OBJECT_H_DOOR_EY_ENTRY: + _doorGUIDs.insert(gameObject->GetGUID()); + break; + case BG_OBJECT_FLAG2_EY_ENTRY: + _flagGUID = gameObject->GetGUID(); + break; + default: + break; + } + } + + bool CanCaptureFlag(AreaTrigger* areaTrigger, Player* player) override + { + if (areaTrigger->GetEntry() != AREATRIGGER_CAPTURE_FLAG) + return false; + + if (GameObject const* flag = battlegroundMap->GetGameObject(_flagGUID)) + { + if (flag->GetFlagCarrierGUID() != player->GetGUID()) + return false; + } + + if (GameObject const* controlzone = player->FindNearestGameObjectWithOptions(40.0f, { .StringId = "bg_eye_of_the_storm_control_zone" })) + { + uint32 const point = _controlZoneHandlers[controlzone->GetEntry()]->GetPoint(); + switch (battleground->GetPlayerTeam(player->GetGUID())) + { + case ALLIANCE: + return battlegroundMap->GetWorldStateValue(m_PointsIconStruct[point].WorldStateAllianceControlledIndex) == 1; + case HORDE: + return battlegroundMap->GetWorldStateValue(m_PointsIconStruct[point].WorldStateHordeControlledIndex) == 1; + default: + return false; + } + } + + return false; + } + + void OnCaptureFlag(AreaTrigger* areaTrigger, Player* player) override + { + if (areaTrigger->GetEntry() != AREATRIGGER_CAPTURE_FLAG) + return; + + uint32 const baseCount = GetControlledBaseCount(Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(player->GetGUID()))); + + if (GameObject const* gameObject = battlegroundMap->GetGameObject(_flagGUID)) + gameObject->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::Respawning, player)); + + Team const team = Team(battleground->GetPlayerTeam(player->GetGUID())); + if (team == ALLIANCE) + { + battleground->SendBroadcastText(BG_EY_TEXT_ALLIANCE_CAPTURED_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + battleground->PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE); + } + else + { + battleground->SendBroadcastText(BG_EY_TEXT_HORDE_CAPTURED_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, player); + battleground->PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_HORDE); + } + + if (baseCount > 0) + AddPoint(team, BG_EY_FlagPoints[baseCount - 1]); + + UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_BASE); + UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_BASE); + + battleground->UpdatePvpStat(player, PVP_STAT_FLAG_CAPTURES, 1); + + ResetAssaultDebuff(); + player->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); + player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::PvPActive); + } + + void OnFlagStateChange(GameObject* /*flagInBase*/, FlagState /*oldValue*/, FlagState newValue, Player* player) override + { + switch (newValue) + { + case FlagState::InBase: + ResetAssaultDebuff(); + break; + case FlagState::Dropped: + player->CastSpell(player, SPELL_RECENTLY_DROPPED_NEUTRAL_FLAG, true); + RemoveAssaultDebuffFromPlayer(player); + + UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_WAIT_RESPAWN); + UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_WAIT_RESPAWN); + + if (battleground->GetPlayerTeam(player->GetGUID()) == ALLIANCE) + battleground->SendBroadcastText(BG_EY_TEXT_FLAG_DROPPED, CHAT_MSG_BG_SYSTEM_ALLIANCE); + else + battleground->SendBroadcastText(BG_EY_TEXT_FLAG_DROPPED, CHAT_MSG_BG_SYSTEM_HORDE); + break; + case FlagState::Taken: + if (battleground->GetPlayerTeam(player->GetGUID()) == ALLIANCE) + { + UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_PLAYER); + battleground->PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE); + battleground->SendBroadcastText(BG_EY_TEXT_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + } + else + { + UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_PLAYER); + battleground->PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_HORDE); + battleground->SendBroadcastText(BG_EY_TEXT_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, player); + } + + ApplyAssaultDebuffToPlayer(player); + _assaultEnabled = true; + + player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::PvPActive); + break; + case FlagState::Respawning: + ResetAssaultDebuff(); + break; + default: + break; + } + + UpdateWorldState(NETHERSTORM_FLAG, AsUnderlyingType(newValue)); + } + + void EventTeamLostPoint(TeamId teamId, uint32 point, GameObject* controlZone) const + { + if (teamId == TEAM_ALLIANCE) + { + battleground->SendBroadcastText(m_LosingPointTypes[point].MessageIdAlliance, CHAT_MSG_BG_SYSTEM_ALLIANCE, controlZone); + UpdateWorldState(m_PointsIconStruct[point].WorldStateAllianceControlledIndex, 0); + } + else if (teamId == TEAM_HORDE) + { + battleground->SendBroadcastText(m_LosingPointTypes[point].MessageIdHorde, CHAT_MSG_BG_SYSTEM_HORDE, controlZone); + UpdateWorldState(m_PointsIconStruct[point].WorldStateHordeControlledIndex, 0); + } + + UpdateWorldState(m_PointsIconStruct[point].WorldStateControlIndex, 1); + UpdatePointsCount(teamId); + } + + void EventTeamCapturedPoint(TeamId teamId, uint32 point, GameObject* controlZone) const + { + if (teamId == TEAM_ALLIANCE) + { + battleground->SendBroadcastText(m_CapturingPointTypes[point].MessageIdAlliance, CHAT_MSG_BG_SYSTEM_ALLIANCE, controlZone); + UpdateWorldState(m_PointsIconStruct[point].WorldStateAllianceControlledIndex, 1); + } + else if (teamId == TEAM_HORDE) + { + battleground->SendBroadcastText(m_CapturingPointTypes[point].MessageIdHorde, CHAT_MSG_BG_SYSTEM_HORDE, controlZone); + UpdateWorldState(m_PointsIconStruct[point].WorldStateHordeControlledIndex, 1); + } + + UpdateWorldState(m_PointsIconStruct[point].WorldStateControlIndex, 0); + UpdatePointsCount(teamId); + } + + Team GetPrematureWinner() override + { + if (battleground->GetTeamScore(TEAM_ALLIANCE) > battleground->GetTeamScore(TEAM_HORDE)) + return ALLIANCE; + + if (battleground->GetTeamScore(TEAM_HORDE) > battleground->GetTeamScore(TEAM_ALLIANCE)) + return HORDE; + + return BattlegroundScript::GetPrematureWinner(); + } + + void ProcessEvent(WorldObject* obj, uint32 eventId, WorldObject* invoker) override + { + BattlegroundScript::ProcessEvent(obj, eventId, invoker); + + if (invoker) + { + if (GameObject* gameobject = invoker->ToGameObject()) + { + if (gameobject->GetGoType() == GAMEOBJECT_TYPE_CONTROL_ZONE) + { + if (!_controlZoneHandlers.contains(gameobject->GetEntry())) + return; + + auto controlzone = gameobject->GetGOInfo()->controlZone; + BattlegroundEYControlZoneHandler& handler = *_controlZoneHandlers[invoker->GetEntry()]; + if (eventId == controlzone.NeutralEventAlliance) + EventTeamLostPoint(TEAM_ALLIANCE, handler.GetPoint(), gameobject); + else if (eventId == controlzone.NeutralEventHorde) + EventTeamLostPoint(TEAM_HORDE, handler.GetPoint(), gameobject); + else if (eventId == controlzone.ProgressEventAlliance) + EventTeamCapturedPoint(TEAM_ALLIANCE, handler.GetPoint(), gameobject); + else if (eventId == controlzone.ProgressEventHorde) + EventTeamCapturedPoint(TEAM_HORDE, handler.GetPoint(), gameobject); + } + } + } + } + +private: + std::array<uint32, PVP_TEAMS_COUNT> _honorScoreTics; + + TimeTracker _pointsTimer; + uint32 _honorTics; + + std::unordered_map<uint32, std::unique_ptr<BattlegroundEYControlZoneHandler>> _controlZoneHandlers; + GuidUnorderedSet _doorGUIDs; + ObjectGuid _flagGUID; + + // Focused/Brutal Assault + bool _assaultEnabled; + TimeTracker _flagAssaultTimer; + uint8 _assaultStackCount; +}; + +void AddSC_battleground_eye_of_the_storm() +{ + RegisterBattlegroundMapScript(battleground_eye_of_the_storm, 566); +} diff --git a/src/server/scripts/Battlegrounds/IsleOfConquest/battleground_isle_of_conquest.cpp b/src/server/scripts/Battlegrounds/IsleOfConquest/battleground_isle_of_conquest.cpp new file mode 100644 index 00000000000..5d55df8d6b7 --- /dev/null +++ b/src/server/scripts/Battlegrounds/IsleOfConquest/battleground_isle_of_conquest.cpp @@ -0,0 +1,902 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BattlegroundScript.h" +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "GameTime.h" +#include "isle_of_conquest.h" +#include "Map.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "TaskScheduler.h" +#include "TemporarySummon.h" +#include "Timer.h" +#include "Transport.h" +#include "TransportMgr.h" +#include "Vehicle.h" + +inline constexpr uint16 MAX_REINFORCEMENTS = 400; + +enum BannersTypes +{ + BANNER_A_CONTROLLED, + BANNER_A_CONTESTED, + BANNER_H_CONTROLLED, + BANNER_H_CONTESTED +}; + +enum BG_IC_ExploitTeleportLocations +{ + IC_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3986, + IC_EXPLOIT_TELEPORT_LOCATION_HORDE = 3983 +}; + +enum BG_IC_GateState +{ + BG_IC_GATE_OK = 1, + BG_IC_GATE_DAMAGED = 2, + BG_IC_GATE_DESTROYED = 3 +}; + +enum ICDoorList +{ + BG_IC_H_FRONT, + BG_IC_H_WEST, + BG_IC_H_EAST, + BG_IC_A_FRONT, + BG_IC_A_WEST, + BG_IC_A_EAST, + BG_IC_MAXDOOR +}; + +enum ICNodePointType +{ + NODE_TYPE_REFINERY, + NODE_TYPE_QUARRY, + NODE_TYPE_DOCKS, + NODE_TYPE_HANGAR, + NODE_TYPE_WORKSHOP, + + // Graveyards + NODE_TYPE_GRAVEYARD_A, + NODE_TYPE_GRAVEYARD_H, + + MAX_NODE_TYPES +}; + +enum class IsleOfConquestNodeState +{ + Neutral, + ConflictA, + ConflictH, + ControlledA, + ControlledH +}; + +enum ICBroadcastTexts +{ + BG_IC_TEXT_FRONT_GATE_HORDE_DESTROYED = 35409, + BG_IC_TEXT_FRONT_GATE_ALLIANCE_DESTROYED = 35410, + BG_IC_TEXT_WEST_GATE_HORDE_DESTROYED = 35411, + BG_IC_TEXT_WEST_GATE_ALLIANCE_DESTROYED = 35412, + BG_IC_TEXT_EAST_GATE_HORDE_DESTROYED = 35413, + BG_IC_TEXT_EAST_GATE_ALLIANCE_DESTROYED = 35414 +}; + +// I.E: Hangar, Quarry, Graveyards .. etc +struct IoCStaticNodeInfo +{ + ICNodePointType NodeType; + + struct + { + uint32 Assaulted; + uint32 Defended; + uint32 AllianceTaken; + uint32 HordeTaken; + } TextIds; + + struct + { + int32 Uncontrolled; + int32 ConflictA; + int32 ConflictH; + int32 ControlledA; + int32 ControlledH; + } WorldStateIds; +}; + +class ICNodePoint +{ +public: + explicit ICNodePoint(IsleOfConquestNodeState state, IoCStaticNodeInfo const& nodeInfo) : _state(state), _nodeInfo(nodeInfo) + { + switch (state) + { + case IsleOfConquestNodeState::ControlledH: + _lastControlled = TEAM_HORDE; + break; + case IsleOfConquestNodeState::ControlledA: + _lastControlled = TEAM_ALLIANCE; + break; + case IsleOfConquestNodeState::ConflictA: + case IsleOfConquestNodeState::ConflictH: + case IsleOfConquestNodeState::Neutral: + _lastControlled = TEAM_NEUTRAL; + break; + } + } + + IsleOfConquestNodeState GetState() const { return _state; } + + bool IsContested() const + { + return _state == IsleOfConquestNodeState::ConflictA || _state == IsleOfConquestNodeState::ConflictH; + } + + TeamId GetLastControlledTeam() const { return _lastControlled; } + + IoCStaticNodeInfo const& GetNodeInfo() const { return _nodeInfo; } + + void UpdateState(IsleOfConquestNodeState state) + { + switch (state) + { + case IsleOfConquestNodeState::ControlledA: + _lastControlled = TEAM_ALLIANCE; + break; + case IsleOfConquestNodeState::ControlledH: + _lastControlled = TEAM_HORDE; + break; + case IsleOfConquestNodeState::Neutral: + _lastControlled = TEAM_NEUTRAL; + break; + case IsleOfConquestNodeState::ConflictA: + case IsleOfConquestNodeState::ConflictH: + break; + } + + _state = state; + } +private: + IsleOfConquestNodeState _state; + TeamId _lastControlled; + IoCStaticNodeInfo _nodeInfo; +}; + +const IoCStaticNodeInfo nodePointInitial[MAX_NODE_TYPES] = +{ + { NODE_TYPE_REFINERY, { 35377, 35378, 35379, 35380 }, { BG_IC_REFINERY_UNCONTROLLED, BG_IC_REFINERY_CONFLICT_A, BG_IC_REFINERY_CONFLICT_H, BG_IC_REFINERY_CONTROLLED_A, BG_IC_REFINERY_CONTROLLED_H } }, + { NODE_TYPE_QUARRY, { 35373, 35374, 35375, 35376 }, { BG_IC_QUARRY_UNCONTROLLED, BG_IC_QUARRY_CONFLICT_A, BG_IC_QUARRY_CONFLICT_H, BG_IC_QUARRY_CONTROLLED_A, BG_IC_QUARRY_CONTROLLED_H } }, + { NODE_TYPE_DOCKS, { 35365, 35366, 35367, 35368 }, { BG_IC_DOCKS_UNCONTROLLED, BG_IC_DOCKS_CONFLICT_A, BG_IC_DOCKS_CONFLICT_H, BG_IC_DOCKS_CONTROLLED_A, BG_IC_DOCKS_CONTROLLED_H } }, + { NODE_TYPE_HANGAR, { 35369, 35370, 35371, 35372 }, { BG_IC_HANGAR_UNCONTROLLED, BG_IC_HANGAR_CONFLICT_A, BG_IC_HANGAR_CONFLICT_H, BG_IC_HANGAR_CONTROLLED_A, BG_IC_HANGAR_CONTROLLED_H } }, + { NODE_TYPE_WORKSHOP, { 35278, 35286, 35279, 35280 }, { BG_IC_WORKSHOP_UNCONTROLLED, BG_IC_WORKSHOP_CONFLICT_A, BG_IC_WORKSHOP_CONFLICT_H, BG_IC_WORKSHOP_CONTROLLED_A, BG_IC_WORKSHOP_CONTROLLED_H } }, + { NODE_TYPE_GRAVEYARD_A, { 35461, 35459, 35463, 35466 }, { BG_IC_ALLIANCE_KEEP_UNCONTROLLED, BG_IC_ALLIANCE_KEEP_CONFLICT_A, BG_IC_ALLIANCE_KEEP_CONFLICT_H, BG_IC_ALLIANCE_KEEP_CONTROLLED_A, BG_IC_ALLIANCE_KEEP_CONTROLLED_H } }, + { NODE_TYPE_GRAVEYARD_H, { 35462, 35460, 35464, 35465 }, { BG_IC_HORDE_KEEP_UNCONTROLLED, BG_IC_HORDE_KEEP_CONFLICT_A, BG_IC_HORDE_KEEP_CONFLICT_H, BG_IC_HORDE_KEEP_CONTROLLED_A, BG_IC_HORDE_KEEP_CONTROLLED_H } } +}; + +enum HonorRewards +{ + RESOURCE_HONOR_AMOUNT = 12, + WINNER_HONOR_AMOUNT = 500 +}; + +enum IsleOfConquestPvpStats +{ + PVP_STAT_BASES_ASSAULTED = 245, + PVP_STAT_BASES_DEFENDED = 246 +}; + +enum IsleOfConquestGameObjects +{ + GO_TELEPORTER_1 = 195314, // 195314 H-OUT 66549 + GO_TELEPORTER_2 = 195313, // 195313 H-IN 66548 + + GO_TELEPORTER_3 = 195315, // 195315 A-OUT 66549 + GO_TELEPORTER_4 = 195316, // 195316 A-IN 66548 + + GO_TELEPORTER_EFFECTS_A = 195701, + GO_TELEPORTER_EFFECTS_H = 195702, + + GO_DOODAD_HU_PORTCULLIS01 = 195436, + GO_DOODAD_ND_HUMAN_GATE_CLOSEDFX_DOOR01 = 195703, + GO_DOODAD_PORTCULLISACTIVE02 = 195452, + GO_DOODAD_VR_PORTCULLIS01 = 195437, + + GO_HORDE_GATE_1 = 195494, + GO_HORDE_GATE_2 = 195495, + GO_HORDE_GATE_3 = 195496, + + GO_ALLIANCE_GATE_1 = 195699, + GO_ALLIANCE_GATE_2 = 195700, + GO_ALLIANCE_GATE_3 = 195698, + + GO_DOODAD_ND_WINTERORC_WALL_GATEFX_DOOR01 = 195491, + + // banners + GO_BANNER_WORKSHOP_CONTROLLED_H = 195130, + GO_BANNER_WORKSHOP_CONTROLLED_A = 195132, + GO_BANNER_WORKSHOP_CONTROLLED_N = 195133, + GO_BANNER_WORKSHOP_CONTESTED_A = 195144, + GO_BANNER_WORKSHOP_CONTESTED_H = 195145, + + GO_BANNER_DOCKS_CONTROLLED_A = 195149, + GO_BANNER_DOCKS_CONTESTED_A = 195150, + GO_BANNER_DOCKS_CONTROLLED_H = 195151, + GO_BANNER_DOCKS_CONTESTED_H = 195152, + GO_BANNER_DOCKS_CONTROLLED_N = 195157, + + GO_BANNER_HANGAR_CONTROLLED_A = 195153, + GO_BANNER_HANGAR_CONTESTED_A = 195154, + GO_BANNER_HANGAR_CONTROLLED_H = 195155, + GO_BANNER_HANGAR_CONTESTED_H = 195156, + GO_BANNER_HANGAR_CONTROLLED_N = 195158, + + GO_BANNER_QUARRY_CONTROLLED_A = 195334, + GO_BANNER_QUARRY_CONTROLLED_H = 195336, + GO_BANNER_QUARRY_CONTESTED_A = 195335, + GO_BANNER_QUARRY_CONTESTED_H = 195337, + GO_BANNER_QUARRY_CONTROLLED_N = 195338, + + GO_BANNER_REFINERY_CONTROLLED_A = 195339, + GO_BANNER_REFINERY_CONTROLLED_H = 195341, + GO_BANNER_REFINERY_CONTESTED_A = 195340, + GO_BANNER_REFINERY_CONTESTED_H = 195342, + GO_BANNER_REFINERY_CONTROLLED_N = 195343, + + GO_BANNER_HORDE_KEEP_CONTROLLED_A = 195391, + GO_BANNER_HORDE_KEEP_CONTROLLED_H = 195393, + GO_BANNER_HORDE_KEEP_CONTESTED_A = 195392, + GO_BANNER_HORDE_KEEP_CONTESTED_H = 195394, + + GO_BANNER_ALLIANCE_KEEP_CONTROLLED_A = 195396, + GO_BANNER_ALLIANCE_KEEP_CONTROLLED_H = 195398, + GO_BANNER_ALLIANCE_KEEP_CONTESTED_A = 195397, + GO_BANNER_ALLIANCE_KEEP_CONTESTED_H = 195399, + + GO_KEEP_GATE_H = 195223, + GO_KEEP_GATE_A = 195451, + GO_KEEP_GATE_2_A = 195452, + + GO_HORDE_GUNSHIP = 195276, + GO_ALLIANCE_GUNSHIP = 195121 +}; + +static constexpr Seconds IOC_RESOURCE_TIMER = 45s; + +static constexpr Position GunshipTeleportTriggerPosition[2] = +{ + { 11.69964981079101562f, 0.034145999699831008f, 20.62075996398925781f, 3.211405754089355468f }, + { 7.30560922622680664f, -0.09524600207805633f, 34.51021575927734375f, 3.159045934677124023f } +}; + +struct battleground_isle_of_conquest : BattlegroundScript +{ + explicit battleground_isle_of_conquest(BattlegroundMap* map) : BattlegroundScript(map) + { + _factionReinforcements = { MAX_REINFORCEMENTS, MAX_REINFORCEMENTS }; + + _gateStatus = { BG_IC_GATE_OK, BG_IC_GATE_OK, BG_IC_GATE_OK, BG_IC_GATE_OK, BG_IC_GATE_OK, BG_IC_GATE_OK }; + + _gunshipGUIDs = { }; + _cannonGUIDs = { }; + _nodePoints = { }; + _keepGateGUIDs = { }; + _keepBannerGUIDs = { }; + + _nodePoints[NODE_TYPE_REFINERY] = std::make_unique<ICNodePoint>(IsleOfConquestNodeState::Neutral, nodePointInitial[NODE_TYPE_REFINERY]); + _nodePoints[NODE_TYPE_QUARRY] = std::make_unique<ICNodePoint>(IsleOfConquestNodeState::Neutral, nodePointInitial[NODE_TYPE_QUARRY]); + _nodePoints[NODE_TYPE_DOCKS] = std::make_unique<ICNodePoint>(IsleOfConquestNodeState::Neutral, nodePointInitial[NODE_TYPE_DOCKS]); + _nodePoints[NODE_TYPE_HANGAR] = std::make_unique<ICNodePoint>(IsleOfConquestNodeState::Neutral, nodePointInitial[NODE_TYPE_HANGAR]); + _nodePoints[NODE_TYPE_WORKSHOP] = std::make_unique<ICNodePoint>(IsleOfConquestNodeState::Neutral, nodePointInitial[NODE_TYPE_WORKSHOP]); + _nodePoints[NODE_TYPE_GRAVEYARD_A] = std::make_unique<ICNodePoint>(IsleOfConquestNodeState::ControlledA, nodePointInitial[NODE_TYPE_GRAVEYARD_A]); + _nodePoints[NODE_TYPE_GRAVEYARD_H] = std::make_unique<ICNodePoint>(IsleOfConquestNodeState::ControlledH, nodePointInitial[NODE_TYPE_GRAVEYARD_H]); + + _resourceTimer.Reset(IOC_RESOURCE_TIMER); + } + + void OnUpdate(uint32 diff) override + { + BattlegroundScript::OnUpdate(diff); + + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + _scheduler.Update(diff); + _resourceTimer.Update(diff); + if (_resourceTimer.Passed()) + { + for (uint8 i = 0; i < NODE_TYPE_DOCKS; ++i) + { + if (_nodePoints[i]->GetLastControlledTeam() != TEAM_NEUTRAL && !_nodePoints[i]->IsContested()) + { + _factionReinforcements[_nodePoints[i]->GetLastControlledTeam()] += 1; + battleground->RewardHonorToTeam(RESOURCE_HONOR_AMOUNT, _nodePoints[i]->GetLastControlledTeam() == TEAM_ALLIANCE ? ALLIANCE : HORDE); + UpdateWorldState((_nodePoints[i]->GetLastControlledTeam() == TEAM_ALLIANCE ? BG_IC_ALLIANCE_REINFORCEMENTS : BG_IC_HORDE_REINFORCEMENTS), _factionReinforcements[_nodePoints[i]->GetLastControlledTeam()]); + } + } + + _resourceTimer.Reset(IOC_RESOURCE_TIMER); + } + } + + void OnStart() override + { + BattlegroundScript::OnStart(); + + auto gameobjectAction = [&](GuidVector const& guids, std::function<void(GameObject*)> const& action) -> void + { + for (ObjectGuid const& guid : guids) + if (GameObject* gameObject = battlegroundMap->GetGameObject(guid)) + action(gameObject); + }; + + gameobjectAction(_mainGateDoorGUIDs, [&](GameObject* gameobject) -> void + { + gameobject->UseDoorOrButton(); + gameobject->DespawnOrUnsummon(20s); + }); + + gameobjectAction(_portcullisGUIDs, [&](GameObject* gameobject) -> void + { + gameobject->UseDoorOrButton(); + }); + + gameobjectAction(_teleporterGUIDs, [&](GameObject* gameobject) -> void + { + gameobject->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + }); + + gameobjectAction(_teleporterEffectGUIDs, [&](GameObject* gameobject) -> void + { + gameobject->SetGoState(GO_STATE_ACTIVE); + }); + + _scheduler.Schedule(20s, [&](TaskContext) + { + for (ObjectGuid const& guid : _wallGUIDs) + if (GameObject* gameobject = battlegroundMap->GetGameObject(guid)) + gameobject->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED); + }); + } + + void OnUnitKilled(Creature* unit, Unit* killer) override + { + BattlegroundScript::OnUnitKilled(unit, killer); + + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + uint32 entry = unit->GetEntry(); + if (entry == NPC_HIGH_COMMANDER_HALFORD_WYRMBANE) + { + battleground->RewardHonorToTeam(WINNER_HONOR_AMOUNT, HORDE); + battleground->EndBattleground(HORDE); + } + else if (entry == NPC_OVERLORD_AGMAR) + { + battleground->RewardHonorToTeam(WINNER_HONOR_AMOUNT, ALLIANCE); + battleground->EndBattleground(ALLIANCE); + } + + //Achievement Mowed Down + // TO-DO: This should be done on the script of each vehicle of the BG. + if (unit->IsVehicle()) + { + if (Player* killerPlayer = killer->GetCharmerOrOwnerPlayerOrPlayerItself()) + killerPlayer->CastSpell(killerPlayer, SPELL_DESTROYED_VEHICLE_ACHIEVEMENT, true); + } + } + + void OnPlayerKilled(Player* player, Player* killer) override + { + BattlegroundScript::OnPlayerKilled(player, killer); + + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + TeamId const victimTeamId = Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(player->GetGUID())); + _factionReinforcements[victimTeamId] -= 1; + + UpdateWorldState((battleground->GetPlayerTeam(player->GetGUID()) == ALLIANCE ? BG_IC_ALLIANCE_REINFORCEMENTS : BG_IC_HORDE_REINFORCEMENTS), _factionReinforcements[victimTeamId]); + + // we must end the battleground + if (_factionReinforcements[victimTeamId] < 1) + battleground->EndBattleground(battleground->GetPlayerTeam(killer->GetGUID())); + } + + static uint32 GetGateIDFromEntry(uint32 id) + { + switch (id) + { + case GO_HORDE_GATE_1: + return BG_IC_H_FRONT; + case GO_HORDE_GATE_2: + return BG_IC_H_WEST; + case GO_HORDE_GATE_3: + return BG_IC_H_EAST; + case GO_ALLIANCE_GATE_3: + return BG_IC_A_FRONT; + case GO_ALLIANCE_GATE_1: + return BG_IC_A_WEST; + case GO_ALLIANCE_GATE_2: + return BG_IC_A_EAST; + default: + return 0; + } + } + + static int32 GetWorldStateFromGateEntry(uint32 id, bool open) + { + int32 uws = 0; + + switch (id) + { + case GO_HORDE_GATE_1: + uws = (open ? BG_IC_GATE_FRONT_H_WS_OPEN : BG_IC_GATE_FRONT_H_WS_CLOSED); + break; + case GO_HORDE_GATE_2: + uws = (open ? BG_IC_GATE_WEST_H_WS_OPEN : BG_IC_GATE_WEST_H_WS_CLOSED); + break; + case GO_HORDE_GATE_3: + uws = (open ? BG_IC_GATE_EAST_H_WS_OPEN : BG_IC_GATE_EAST_H_WS_CLOSED); + break; + case GO_ALLIANCE_GATE_3: + uws = (open ? BG_IC_GATE_FRONT_A_WS_OPEN : BG_IC_GATE_FRONT_A_WS_CLOSED); + break; + case GO_ALLIANCE_GATE_1: + uws = (open ? BG_IC_GATE_WEST_A_WS_OPEN : BG_IC_GATE_WEST_A_WS_CLOSED); + break; + case GO_ALLIANCE_GATE_2: + uws = (open ? BG_IC_GATE_EAST_A_WS_OPEN : BG_IC_GATE_EAST_A_WS_CLOSED); + break; + default: + break; + } + return uws; + } + + void UpdateNodeWorldState(ICNodePoint const& node) const + { + UpdateWorldState(node.GetNodeInfo().WorldStateIds.ConflictA, node.GetState() == IsleOfConquestNodeState::ConflictA); + UpdateWorldState(node.GetNodeInfo().WorldStateIds.ConflictH, node.GetState() == IsleOfConquestNodeState::ConflictH); + UpdateWorldState(node.GetNodeInfo().WorldStateIds.ControlledA, node.GetState() == IsleOfConquestNodeState::ControlledA); + UpdateWorldState(node.GetNodeInfo().WorldStateIds.ControlledH, node.GetState() == IsleOfConquestNodeState::ControlledH); + UpdateWorldState(node.GetNodeInfo().WorldStateIds.Uncontrolled, node.GetState() == IsleOfConquestNodeState::Neutral); + } + + static ICNodePointType BannerToNodeType(uint32 bannerId) + { + switch (bannerId) + { + case GO_BANNER_ALLIANCE_KEEP_CONTESTED_A: + case GO_BANNER_ALLIANCE_KEEP_CONTESTED_H: + case GO_BANNER_ALLIANCE_KEEP_CONTROLLED_A: + case GO_BANNER_ALLIANCE_KEEP_CONTROLLED_H: + return NODE_TYPE_GRAVEYARD_A; + case GO_BANNER_HORDE_KEEP_CONTESTED_A: + case GO_BANNER_HORDE_KEEP_CONTESTED_H: + case GO_BANNER_HORDE_KEEP_CONTROLLED_A: + case GO_BANNER_HORDE_KEEP_CONTROLLED_H: + return NODE_TYPE_GRAVEYARD_H; + case GO_BANNER_DOCKS_CONTESTED_A: + case GO_BANNER_DOCKS_CONTESTED_H: + case GO_BANNER_DOCKS_CONTROLLED_A: + case GO_BANNER_DOCKS_CONTROLLED_H: + case GO_BANNER_DOCKS_CONTROLLED_N: + return NODE_TYPE_DOCKS; + case GO_BANNER_HANGAR_CONTESTED_A: + case GO_BANNER_HANGAR_CONTESTED_H: + case GO_BANNER_HANGAR_CONTROLLED_A: + case GO_BANNER_HANGAR_CONTROLLED_H: + case GO_BANNER_HANGAR_CONTROLLED_N: + return NODE_TYPE_HANGAR; + case GO_BANNER_WORKSHOP_CONTESTED_A: + case GO_BANNER_WORKSHOP_CONTESTED_H: + case GO_BANNER_WORKSHOP_CONTROLLED_A: + case GO_BANNER_WORKSHOP_CONTROLLED_H: + case GO_BANNER_WORKSHOP_CONTROLLED_N: + return NODE_TYPE_WORKSHOP; + case GO_BANNER_QUARRY_CONTESTED_A: + case GO_BANNER_QUARRY_CONTESTED_H: + case GO_BANNER_QUARRY_CONTROLLED_A: + case GO_BANNER_QUARRY_CONTROLLED_H: + case GO_BANNER_QUARRY_CONTROLLED_N: + return NODE_TYPE_QUARRY; + case GO_BANNER_REFINERY_CONTESTED_A: + case GO_BANNER_REFINERY_CONTESTED_H: + case GO_BANNER_REFINERY_CONTROLLED_A: + case GO_BANNER_REFINERY_CONTROLLED_H: + case GO_BANNER_REFINERY_CONTROLLED_N: + return NODE_TYPE_REFINERY; + default: + break; + } + + return MAX_NODE_TYPES; + } + + void HandleCapturedNodes(ICNodePoint& node) + { + if (node.GetLastControlledTeam() == TEAM_NEUTRAL) + return; + + switch (node.GetNodeInfo().NodeType) + { + case NODE_TYPE_QUARRY: + case NODE_TYPE_REFINERY: + battlegroundMap->UpdateAreaDependentAuras(); + break; + case NODE_TYPE_HANGAR: + if (Transport* transport = battlegroundMap->GetTransport(_gunshipGUIDs[node.GetLastControlledTeam()])) + { + // Can't have this in spawngroup, creature is on a transport + if (TempSummon* trigger = transport->SummonPassenger(NPC_WORLD_TRIGGER_NOT_FLOATING, GunshipTeleportTriggerPosition[node.GetLastControlledTeam()], TEMPSUMMON_MANUAL_DESPAWN)) + _gunshipTeleportTarget = trigger->GetGUID(); + + transport->EnableMovement(true); + } + + for (ObjectGuid const& guid : _cannonGUIDs[node.GetLastControlledTeam()]) + if (Creature* cannon = battlegroundMap->GetCreature(guid)) + cannon->SetUninteractible(false); + break; + default: + break; + } + } + + void OnCreatureCreate(Creature* creature) override + { + BattlegroundScript::OnCreatureCreate(creature); + + if (creature->HasStringId("bg_ioc_faction_1735")) + creature->SetFaction(FACTION_HORDE_GENERIC_WG); + else if (creature->HasStringId("bg_ioc_faction_1732")) + creature->SetFaction(FACTION_ALLIANCE_GENERIC_WG); + + switch (creature->GetEntry()) + { + case NPC_ALLIANCE_GUNSHIP_CANNON: + _cannonGUIDs[TEAM_ALLIANCE].emplace_back(creature->GetGUID()); + creature->SetUninteractible(true); + creature->SetControlled(true, UNIT_STATE_ROOT); + break; + case NPC_HORDE_GUNSHIP_CANNON: + _cannonGUIDs[TEAM_HORDE].emplace_back(creature->GetGUID()); + creature->SetUninteractible(true); + creature->SetControlled(true, UNIT_STATE_ROOT); + break; + default: + break; + } + } + + void OnGameObjectCreate(GameObject* gameobject) override + { + BattlegroundScript::OnGameObjectCreate(gameobject); + + if (gameobject->IsDestructibleBuilding()) + _wallGUIDs.emplace_back(gameobject->GetGUID()); + + if (gameobject->HasStringId("bg_ioc_faction_1735")) + gameobject->SetFaction(FACTION_HORDE_GENERIC_WG); + else if (gameobject->HasStringId("bg_ioc_faction_1732")) + gameobject->SetFaction(FACTION_ALLIANCE_GENERIC_WG); + + switch (gameobject->GetEntry()) + { + case GO_TELEPORTER_1: + case GO_TELEPORTER_2: + case GO_TELEPORTER_3: + case GO_TELEPORTER_4: + _teleporterGUIDs.emplace_back(gameobject->GetGUID()); + break; + case GO_TELEPORTER_EFFECTS_A: + case GO_TELEPORTER_EFFECTS_H: + _teleporterEffectGUIDs.emplace_back(gameobject->GetGUID()); + break; + case GO_DOODAD_ND_HUMAN_GATE_CLOSEDFX_DOOR01: + case GO_DOODAD_ND_WINTERORC_WALL_GATEFX_DOOR01: + _mainGateDoorGUIDs.emplace_back(gameobject->GetGUID()); + break; + case GO_DOODAD_HU_PORTCULLIS01: + case GO_DOODAD_VR_PORTCULLIS01: + _portcullisGUIDs.emplace_back(gameobject->GetGUID()); + break; + case GO_KEEP_GATE_H: + _keepGateGUIDs[TEAM_HORDE].emplace_back(gameobject->GetGUID()); + break; + case GO_KEEP_GATE_A: + case GO_KEEP_GATE_2_A: + _keepGateGUIDs[TEAM_ALLIANCE].emplace_back(gameobject->GetGUID()); + break; + case GO_BANNER_ALLIANCE_KEEP_CONTROLLED_A: + _keepBannerGUIDs[TEAM_ALLIANCE] = gameobject->GetGUID(); + break; + case GO_BANNER_HORDE_KEEP_CONTROLLED_H: + _keepBannerGUIDs[TEAM_HORDE] = gameobject->GetGUID(); + break; + default: + break; + } + } + + void OnInit() override + { + BattlegroundScript::OnInit(); + + if (Transport* transport = sTransportMgr->CreateTransport(GO_HORDE_GUNSHIP, battlegroundMap)) + { + _gunshipGUIDs[TEAM_HORDE] = transport->GetGUID(); + transport->EnableMovement(false); + } + + if (Transport* transport = sTransportMgr->CreateTransport(GO_ALLIANCE_GUNSHIP, battlegroundMap)) + { + _gunshipGUIDs[TEAM_ALLIANCE] = transport->GetGUID(); + transport->EnableMovement(false); + } + } + + void DoAction(uint32 actionId, WorldObject* source, WorldObject* target) override + { + BattlegroundScript::DoAction(actionId, source, target); + + switch (actionId) + { + case ACTION_IOC_INTERACT_CAPTURABLE_OBJECT: + OnPlayerInteractWithBanner(WorldObject::ToPlayer(source), WorldObject::ToGameObject(target)); + break; + case ACTION_IOC_CAPTURE_CAPTURABLE_OBJECT: + HandleCaptureNodeAction(WorldObject::ToGameObject(target)); + break; + default: + break; + } + } + + void OnPlayerInteractWithBanner(Player* player, GameObject* banner) + { + if (!player || !banner) + return; + + Team const playerTeam = battleground->GetPlayerTeam(player->GetGUID()); + TeamId const playerTeamId = Battleground::GetTeamIndexByTeamId(playerTeam); + ICNodePointType const nodeType = BannerToNodeType(banner->GetEntry()); + if (nodeType == MAX_NODE_TYPES) + return; + + ICNodePoint& node = *_nodePoints[nodeType]; + + bool assault = false; + bool defend = false; + + switch (node.GetState()) + { + case IsleOfConquestNodeState::Neutral: + assault = true; + break; + case IsleOfConquestNodeState::ControlledH: + assault = playerTeamId != TEAM_HORDE; + break; + case IsleOfConquestNodeState::ControlledA: + assault = playerTeamId != TEAM_ALLIANCE; + break; + case IsleOfConquestNodeState::ConflictA: + defend = playerTeamId == node.GetLastControlledTeam(); + assault = !defend && playerTeamId == TEAM_HORDE; + break; + case IsleOfConquestNodeState::ConflictH: + defend = playerTeamId == node.GetLastControlledTeam(); + assault = !defend && playerTeamId == TEAM_ALLIANCE; + break; + } + + if (assault) + OnPlayerAssaultNode(player, node); + else if (defend) + OnPlayerDefendNode(player, node); + + battlegroundMap->UpdateSpawnGroupConditions(); + } + + void OnPlayerAssaultNode(Player* player, ICNodePoint& node) + { + if (!player) + return; + + Team const playerTeam = battleground->GetPlayerTeam(player->GetGUID()); + TeamId const playerTeamId = Battleground::GetTeamIndexByTeamId(playerTeam); + + IsleOfConquestNodeState const newState = playerTeamId == TEAM_HORDE ? IsleOfConquestNodeState::ConflictH : IsleOfConquestNodeState::ConflictA; + node.UpdateState(newState); + + battleground->UpdatePvpStat(player, PVP_STAT_BASES_ASSAULTED, 1); + + ChatMsg const messageType = playerTeamId == TEAM_ALLIANCE ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE; + battleground->SendBroadcastText(node.GetNodeInfo().TextIds.Assaulted, messageType, player); + UpdateNodeWorldState(node); + + // apply side effects of each node, only if it wasn't neutral before + if (node.GetLastControlledTeam() == TEAM_NEUTRAL) + return; + + switch (node.GetNodeInfo().NodeType) + { + case NODE_TYPE_HANGAR: + if (Transport* transport = battlegroundMap->GetTransport(_gunshipGUIDs[node.GetLastControlledTeam()])) + transport->EnableMovement(false); + + for (ObjectGuid const& guid : _cannonGUIDs[node.GetLastControlledTeam()]) + { + if (Creature* cannon = battlegroundMap->GetCreature(guid)) + { + cannon->GetVehicleKit()->RemoveAllPassengers(); + cannon->SetUninteractible(true); + } + } + + // Despawn teleport trigger target + if (Creature* creature = battlegroundMap->GetCreature(_gunshipTeleportTarget)) + creature->DespawnOrUnsummon(); + break; + default: + break; + } + } + + void OnPlayerDefendNode(Player* player, ICNodePoint& node) + { + if (!player) + return; + + Team const playerTeam = battleground->GetPlayerTeam(player->GetGUID()); + TeamId const playerTeamId = Battleground::GetTeamIndexByTeamId(playerTeam); + + node.UpdateState(playerTeamId == TEAM_HORDE ? IsleOfConquestNodeState::ControlledH : IsleOfConquestNodeState::ControlledA); + HandleCapturedNodes(node); + battleground->UpdatePvpStat(player, PVP_STAT_BASES_DEFENDED, 1); + + ChatMsg const messageType = playerTeamId == TEAM_ALLIANCE ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE; + battleground->SendBroadcastText(node.GetNodeInfo().TextIds.Defended, messageType, player); + UpdateNodeWorldState(node); + } + + void ProcessEvent(WorldObject* target, uint32 eventId, WorldObject* invoker) override + { + BattlegroundScript::ProcessEvent(target, eventId, invoker); + + if (GameObject* obj = Object::ToGameObject(target)) + if (obj->GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) + if (obj->GetGOInfo()->destructibleBuilding.DestroyedEvent == eventId) + OnGateDestroyed(obj, invoker); + } + + void HandleCaptureNodeAction(GameObject* banner) + { + if (!banner) + return; + + ICNodePointType const nodeType = BannerToNodeType(banner->GetEntry()); + if (nodeType == MAX_NODE_TYPES) + return; + + ICNodePoint& node = *_nodePoints[nodeType]; + if (node.GetState() == IsleOfConquestNodeState::ConflictH) + node.UpdateState(IsleOfConquestNodeState::ControlledH); + else if (node.GetState() == IsleOfConquestNodeState::ConflictA) + node.UpdateState(IsleOfConquestNodeState::ControlledA); + + HandleCapturedNodes(node); + + ChatMsg const messageType = node.GetLastControlledTeam() == TEAM_ALLIANCE ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE; + uint32 const textId = node.GetLastControlledTeam() == TEAM_ALLIANCE ? node.GetNodeInfo().TextIds.AllianceTaken : node.GetNodeInfo().TextIds.HordeTaken; + battleground->SendBroadcastText(textId, messageType); + UpdateNodeWorldState(node); + } + + void OnGateDestroyed(GameObject* gate, WorldObject* destroyer) + { + _gateStatus[GetGateIDFromEntry(gate->GetEntry())] = BG_IC_GATE_DESTROYED; + int32 const wsGateOpen = GetWorldStateFromGateEntry(gate->GetEntry(), true); + int32 const wsGateClosed = GetWorldStateFromGateEntry(gate->GetEntry(), false); + if (wsGateOpen) + { + UpdateWorldState(wsGateClosed, 0); + UpdateWorldState(wsGateOpen, 1); + } + + TeamId teamId = TEAM_NEUTRAL; + uint32 textId; + ChatMsg msgType; + switch (gate->GetEntry()) + { + case GO_HORDE_GATE_1: + textId = BG_IC_TEXT_FRONT_GATE_HORDE_DESTROYED; + msgType = CHAT_MSG_BG_SYSTEM_ALLIANCE; + teamId = TEAM_HORDE; + break; + case GO_HORDE_GATE_2: + textId = BG_IC_TEXT_WEST_GATE_HORDE_DESTROYED; + msgType = CHAT_MSG_BG_SYSTEM_ALLIANCE; + teamId = TEAM_HORDE; + break; + case GO_HORDE_GATE_3: + textId = BG_IC_TEXT_EAST_GATE_HORDE_DESTROYED; + msgType = CHAT_MSG_BG_SYSTEM_ALLIANCE; + teamId = TEAM_HORDE; + break; + case GO_ALLIANCE_GATE_1: + textId = BG_IC_TEXT_WEST_GATE_ALLIANCE_DESTROYED; + msgType = CHAT_MSG_BG_SYSTEM_HORDE; + teamId = TEAM_ALLIANCE; + break; + case GO_ALLIANCE_GATE_2: + textId = BG_IC_TEXT_EAST_GATE_ALLIANCE_DESTROYED; + msgType = CHAT_MSG_BG_SYSTEM_HORDE; + teamId = TEAM_ALLIANCE; + break; + case GO_ALLIANCE_GATE_3: + textId = BG_IC_TEXT_FRONT_GATE_ALLIANCE_DESTROYED; + msgType = CHAT_MSG_BG_SYSTEM_HORDE; + teamId = TEAM_ALLIANCE; + break; + default: + return; + } + + if (teamId != TEAM_NEUTRAL) + { + GuidVector const keepGates = _keepGateGUIDs[teamId]; + ObjectGuid const bannerGuid = _keepBannerGUIDs[teamId]; + + for (ObjectGuid const& guid : keepGates) + if (GameObject* keepGate = battlegroundMap->GetGameObject(guid)) + keepGate->UseDoorOrButton(); + + if (GameObject* banner = battlegroundMap->GetGameObject(bannerGuid)) + banner->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + } + + battleground->SendBroadcastText(textId, msgType, destroyer); + } + +private: + std::array<uint16, PVP_TEAMS_COUNT> _factionReinforcements; + std::array<BG_IC_GateState, 6> _gateStatus; + std::array<std::unique_ptr<ICNodePoint>, 7> _nodePoints; + std::array<ObjectGuid, PVP_TEAMS_COUNT> _gunshipGUIDs; + GuidVector _teleporterGUIDs; + GuidVector _teleporterEffectGUIDs; + GuidVector _mainGateDoorGUIDs; + GuidVector _portcullisGUIDs; + GuidVector _wallGUIDs; + std::array<GuidVector, PVP_TEAMS_COUNT> _cannonGUIDs; + std::array<GuidVector, PVP_TEAMS_COUNT> _keepGateGUIDs; + std::array<ObjectGuid, PVP_TEAMS_COUNT> _keepBannerGUIDs; + ObjectGuid _gunshipTeleportTarget; + + TaskScheduler _scheduler; + TimeTracker _resourceTimer; +}; + +void AddSC_battleground_isle_of_conquest() +{ + RegisterBattlegroundMapScript(battleground_isle_of_conquest, 628); +} diff --git a/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp b/src/server/scripts/Battlegrounds/IsleOfConquest/boss_ioc_horde_alliance.cpp index 77f5e53207e..05a13de842b 100644 --- a/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp +++ b/src/server/scripts/Battlegrounds/IsleOfConquest/boss_ioc_horde_alliance.cpp @@ -16,7 +16,7 @@ */ #include "ScriptMgr.h" -#include "BattlegroundIC.h" +#include "isle_of_conquest.h" #include "ScriptedCreature.h" enum BossSpells diff --git a/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp b/src/server/scripts/Battlegrounds/IsleOfConquest/isle_of_conquest.cpp index 1492335ce15..2c9fdce6596 100644 --- a/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp +++ b/src/server/scripts/Battlegrounds/IsleOfConquest/isle_of_conquest.cpp @@ -15,8 +15,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "isle_of_conquest.h" #include "ScriptMgr.h" -#include "BattlegroundIC.h" +#include "Battleground.h" #include "GameObject.h" #include "GameObjectAI.h" #include "Map.h" diff --git a/src/server/scripts/Battlegrounds/IsleOfConquest/isle_of_conquest.h b/src/server/scripts/Battlegrounds/IsleOfConquest/isle_of_conquest.h new file mode 100644 index 00000000000..ab4c92abae6 --- /dev/null +++ b/src/server/scripts/Battlegrounds/IsleOfConquest/isle_of_conquest.h @@ -0,0 +1,132 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITYCORE_ISLE_OF_CONQUEST_H +#define TRINITYCORE_ISLE_OF_CONQUEST_H + +enum CreaturesIC +{ + NPC_HIGH_COMMANDER_HALFORD_WYRMBANE = 34924, // Alliance Boss + NPC_OVERLORD_AGMAR = 34922, // Horde Boss + NPC_KOR_KRON_GUARD = 34918, // horde guard + NPC_SEVEN_TH_LEGION_INFANTRY = 34919, // alliance guard + NPC_KEEP_CANNON = 34944, + NPC_DEMOLISHER = 34775, + NPC_SIEGE_ENGINE_H = 35069, + NPC_SIEGE_ENGINE_A = 34776, + NPC_GLAIVE_THROWER_A = 34802, + NPC_GLAIVE_THROWER_H = 35273, + NPC_CATAPULT = 34793, + NPC_HORDE_GUNSHIP_CANNON = 34935, + NPC_ALLIANCE_GUNSHIP_CANNON = 34929, + NPC_HORDE_GUNSHIP_CAPTAIN = 35003, + NPC_ALLIANCE_GUNSHIP_CAPTAIN = 34960, + NPC_WORLD_TRIGGER_NOT_FLOATING = 34984, + NPC_WORLD_TRIGGER_ALLIANCE_FRIENDLY = 20213, + NPC_WORLD_TRIGGER_HORDE_FRIENDLY = 20212 +}; + +enum Actions +{ + ACTION_GUNSHIP_READY = 1, + ACTION_IOC_INTERACT_CAPTURABLE_OBJECT = 2, + ACTION_IOC_CAPTURE_CAPTURABLE_OBJECT = 3 +}; + +enum Spells +{ + SPELL_OIL_REFINERY = 68719, + SPELL_QUARRY = 68720, + SPELL_PARACHUTE = 66656, + SPELL_SLOW_FALL = 12438, + SPELL_DESTROYED_VEHICLE_ACHIEVEMENT = 68357, + SPELL_BACK_DOOR_JOB_ACHIEVEMENT = 68502, + SPELL_DRIVING_CREDIT_DEMOLISHER = 68365, + SPELL_DRIVING_CREDIT_GLAIVE = 68363, + SPELL_DRIVING_CREDIT_SIEGE = 68364, + SPELL_DRIVING_CREDIT_CATAPULT = 68362, + SPELL_SIMPLE_TELEPORT = 12980, + SPELL_TELEPORT_VISUAL_ONLY = 51347, + SPELL_PARACHUTE_IC = 66657, + SPELL_LAUNCH_NO_FALLING_DAMAGE = 66251 +}; + +enum ICWorldStates +{ + BG_IC_ALLIANCE_REINFORCEMENTS_SET = 4221, + BG_IC_HORDE_REINFORCEMENTS_SET = 4222, + BG_IC_ALLIANCE_REINFORCEMENTS = 4226, + BG_IC_HORDE_REINFORCEMENTS = 4227, + BG_IC_MAX_REINFORCEMENTS = 17377, + + BG_IC_GATE_FRONT_H_WS_CLOSED = 4317, + BG_IC_GATE_WEST_H_WS_CLOSED = 4318, + BG_IC_GATE_EAST_H_WS_CLOSED = 4319, + BG_IC_GATE_FRONT_A_WS_CLOSED = 4328, + BG_IC_GATE_WEST_A_WS_CLOSED = 4327, + BG_IC_GATE_EAST_A_WS_CLOSED = 4326, + BG_IC_GATE_FRONT_H_WS_OPEN = 4322, + BG_IC_GATE_WEST_H_WS_OPEN = 4321, + BG_IC_GATE_EAST_H_WS_OPEN = 4320, + BG_IC_GATE_FRONT_A_WS_OPEN = 4323, + BG_IC_GATE_WEST_A_WS_OPEN = 4324, + BG_IC_GATE_EAST_A_WS_OPEN = 4325, + + BG_IC_DOCKS_UNCONTROLLED = 4301, + BG_IC_DOCKS_CONFLICT_A = 4305, + BG_IC_DOCKS_CONFLICT_H = 4302, + BG_IC_DOCKS_CONTROLLED_A = 4304, + BG_IC_DOCKS_CONTROLLED_H = 4303, + + BG_IC_HANGAR_UNCONTROLLED = 4296, + BG_IC_HANGAR_CONFLICT_A = 4300, + BG_IC_HANGAR_CONFLICT_H = 4297, + BG_IC_HANGAR_CONTROLLED_A = 4299, + BG_IC_HANGAR_CONTROLLED_H = 4298, + + BG_IC_QUARRY_UNCONTROLLED = 4306, + BG_IC_QUARRY_CONFLICT_A = 4310, + BG_IC_QUARRY_CONFLICT_H = 4307, + BG_IC_QUARRY_CONTROLLED_A = 4309, + BG_IC_QUARRY_CONTROLLED_H = 4308, + + BG_IC_REFINERY_UNCONTROLLED = 4311, + BG_IC_REFINERY_CONFLICT_A = 4315, + BG_IC_REFINERY_CONFLICT_H = 4312, + BG_IC_REFINERY_CONTROLLED_A = 4314, + BG_IC_REFINERY_CONTROLLED_H = 4313, + + BG_IC_WORKSHOP_UNCONTROLLED = 4294, + BG_IC_WORKSHOP_CONFLICT_A = 4228, + BG_IC_WORKSHOP_CONFLICT_H = 4293, + BG_IC_WORKSHOP_CONTROLLED_A = 4229, + BG_IC_WORKSHOP_CONTROLLED_H = 4230, + + BG_IC_ALLIANCE_KEEP_UNCONTROLLED = 4341, + BG_IC_ALLIANCE_KEEP_CONFLICT_A = 4342, + BG_IC_ALLIANCE_KEEP_CONFLICT_H = 4343, + BG_IC_ALLIANCE_KEEP_CONTROLLED_A = 4339, + BG_IC_ALLIANCE_KEEP_CONTROLLED_H = 4340, + + BG_IC_HORDE_KEEP_UNCONTROLLED = 4346, + BG_IC_HORDE_KEEP_CONFLICT_A = 4347, + BG_IC_HORDE_KEEP_CONFLICT_H = 4348, + BG_IC_HORDE_KEEP_CONTROLLED_A = 4344, + BG_IC_HORDE_KEEP_CONTROLLED_H = 4345 +}; + +#endif diff --git a/src/server/scripts/Battlegrounds/NagrandArena/arena_nagrand_arena.cpp b/src/server/scripts/Battlegrounds/NagrandArena/arena_nagrand_arena.cpp new file mode 100644 index 00000000000..e3f83c6acb2 --- /dev/null +++ b/src/server/scripts/Battlegrounds/NagrandArena/arena_nagrand_arena.cpp @@ -0,0 +1,84 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Battleground.h" +#include "BattlegroundScript.h" +#include "GameObject.h" +#include "Map.h" +#include "ScriptMgr.h" +#include "TaskScheduler.h" + +struct arena_nagrand : ArenaScript +{ + enum GameObjects + { + BG_NA_OBJECT_TYPE_DOOR_1 = 183978, + BG_NA_OBJECT_TYPE_DOOR_2 = 183980, + BG_NA_OBJECT_TYPE_DOOR_3 = 183977, + BG_NA_OBJECT_TYPE_DOOR_4 = 183979, + BG_NA_OBJECT_TYPE_BUFF_1 = 184663, + BG_NA_OBJECT_TYPE_BUFF_2 = 184664 + }; + + explicit arena_nagrand(BattlegroundMap* map) : ArenaScript(map) { } + + void OnUpdate(uint32 diff) override + { + _scheduler.Update(diff); + } + + void OnInit() override + { + AddDoor(BG_NA_OBJECT_TYPE_DOOR_1, 4031.854f, 2966.833f, 12.6462f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f); + AddDoor(BG_NA_OBJECT_TYPE_DOOR_2, 4081.179f, 2874.97f, 12.39171f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f); + AddDoor(BG_NA_OBJECT_TYPE_DOOR_3, 4023.709f, 2981.777f, 10.70117f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f); + AddDoor(BG_NA_OBJECT_TYPE_DOOR_4, 4090.064f, 2858.438f, 10.23631f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f); + } + + void OnStart() override + { + for (ObjectGuid const& guid : _doorGUIDs) + { + if (GameObject* door = battlegroundMap->GetGameObject(guid)) + { + door->UseDoorOrButton(); + door->DespawnOrUnsummon(5s); + } + } + + _scheduler.Schedule(1min, [&](TaskContext) + { + CreateObject(BG_NA_OBJECT_TYPE_BUFF_1, 4009.189941f, 2895.250000f, 13.052700f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f); + CreateObject(BG_NA_OBJECT_TYPE_BUFF_2, 4103.330078f, 2946.350098f, 13.051300f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f); + }); + } + + void AddDoor(uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, GOState goState = GO_STATE_READY) + { + if (GameObject const* go = CreateObject(entry, x, y, z, o, rotation0, rotation1, rotation2, rotation3, goState)) + _doorGUIDs.emplace_back(go->GetGUID()); + } + +private: + GuidVector _doorGUIDs; + TaskScheduler _scheduler; +}; + +void AddSC_arena_nagrand() +{ + RegisterBattlegroundMapScript(arena_nagrand, 1505); +} diff --git a/src/server/scripts/Battlegrounds/RingOfValor/arena_ring_of_valor.cpp b/src/server/scripts/Battlegrounds/RingOfValor/arena_ring_of_valor.cpp new file mode 100644 index 00000000000..3c2dbdc473a --- /dev/null +++ b/src/server/scripts/Battlegrounds/RingOfValor/arena_ring_of_valor.cpp @@ -0,0 +1,302 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Battleground.h" +#include "BattlegroundScript.h" +#include "GameObject.h" +#include "Map.h" +#include "ObjectAccessor.h" +#include "ScriptMgr.h" +#include "TaskScheduler.h" + +struct arena_ring_of_valor : ArenaScript +{ + enum BattlegroundRVGameObjects + { + BG_RV_OBJECT_TYPE_BUFF_1 = 184663, + BG_RV_OBJECT_TYPE_BUFF_2 = 184664, + BG_RV_OBJECT_TYPE_FIRE_1 = 192704, + BG_RV_OBJECT_TYPE_FIRE_2 = 192705, + + BG_RV_OBJECT_TYPE_FIREDOOR_2 = 192387, + BG_RV_OBJECT_TYPE_FIREDOOR_1 = 192388, + BG_RV_OBJECT_TYPE_PULLEY_1 = 192389, + BG_RV_OBJECT_TYPE_PULLEY_2 = 192390, + BG_RV_OBJECT_TYPE_GEAR_1 = 192393, + BG_RV_OBJECT_TYPE_GEAR_2 = 192394, + BG_RV_OBJECT_TYPE_ELEVATOR_1 = 194582, + BG_RV_OBJECT_TYPE_ELEVATOR_2 = 194586, + + BG_RV_OBJECT_TYPE_PILAR_COLLISION_1 = 194580, // axe + BG_RV_OBJECT_TYPE_PILAR_COLLISION_2 = 194579, // arena // big + BG_RV_OBJECT_TYPE_PILAR_COLLISION_3 = 194581, // lightning + BG_RV_OBJECT_TYPE_PILAR_COLLISION_4 = 194578, // ivory // big + + BG_RV_OBJECT_TYPE_PILAR_1 = 194583, // axe + BG_RV_OBJECT_TYPE_PILAR_2 = 194584, // arena + BG_RV_OBJECT_TYPE_PILAR_3 = 194585, // lightning + BG_RV_OBJECT_TYPE_PILAR_4 = 194587 // ivory + }; + + enum BattlegroundRVData + { + BG_RV_STATE_OPEN_FENCES, + BG_RV_STATE_SWITCH_PILLARS, + BG_RV_STATE_CLOSE_FIRE, + + BG_RV_PILLAR_SWITCH_TIMER = 25000, + BG_RV_FIRE_TO_PILLAR_TIMER = 20000, + BG_RV_CLOSE_FIRE_TIMER = 5000, + BG_RV_FIRST_TIMER = 20133, + }; + + explicit arena_ring_of_valor(BattlegroundMap* map) : ArenaScript(map), _timer(0), _state(0), _pillarCollision(false) { } + + void OnUpdate(uint32 diff) override + { + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return; + + _scheduler.Update(diff); + + if (_timer < diff) + { + switch (_state) + { + case BG_RV_STATE_OPEN_FENCES: + // Open fire (only at game start) + for (ObjectGuid const& guid : _fireGUIDs) + if (GameObject* go = battlegroundMap->GetGameObject(guid)) + go->UseDoorOrButton(); + for (ObjectGuid const& guid : _firedoorGUIDs) + if (GameObject* go = battlegroundMap->GetGameObject(guid)) + go->UseDoorOrButton(); + _timer = BG_RV_CLOSE_FIRE_TIMER; + _state = BG_RV_STATE_CLOSE_FIRE; + break; + case BG_RV_STATE_CLOSE_FIRE: + for (ObjectGuid const& guid : _fireGUIDs) + if (GameObject* go = battlegroundMap->GetGameObject(guid)) + go->ResetDoorOrButton(); + for (ObjectGuid const& guid : _firedoorGUIDs) + if (GameObject* go = battlegroundMap->GetGameObject(guid)) + go->ResetDoorOrButton(); + // Fire got closed after five seconds, leaves twenty seconds before toggling pillars + _timer = BG_RV_FIRE_TO_PILLAR_TIMER; + _state = BG_RV_STATE_SWITCH_PILLARS; + break; + case BG_RV_STATE_SWITCH_PILLARS: + TogglePillarCollision(); + _timer = BG_RV_PILLAR_SWITCH_TIMER; + break; + default: + break; + } + } + else + _timer -= diff; + } + + void OnInit() override + { + CreateObject(BG_RV_OBJECT_TYPE_ELEVATOR_1, 763.536377f, -294.535767f, 0.505383f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_ELEVATOR_2, 763.506348f, -273.873352f, 0.505383f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + + CreateObject(BG_RV_OBJECT_TYPE_FIRE_1, 743.543457f, -283.799469f, 28.286655f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_FIRE_2, 782.971802f, -283.799469f, 28.286655f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_FIREDOOR_1, 743.711060f, -284.099609f, 27.542587f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_FIREDOOR_2, 783.221252f, -284.133362f, 27.535686f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + + CreateObject(BG_RV_OBJECT_TYPE_GEAR_1, 763.664551f, -261.872986f, 26.686588f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_GEAR_2, 763.578979f, -306.146149f, 26.665222f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PULLEY_1, 700.722290f, -283.990662f, 39.517582f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PULLEY_2, 826.303833f, -283.996429f, 39.517582f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_1, 763.632385f, -306.162384f, 25.909504f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_2, 723.644287f, -284.493256f, 24.648525f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_3, 763.611145f, -261.856750f, 25.909504f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_4, 802.211609f, -284.493256f, 24.648525f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_COLLISION_1, 763.632385f, -306.162384f, 30.639660f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_COLLISION_2, 723.644287f, -284.493256f, 32.382710f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_COLLISION_3, 763.611145f, -261.856750f, 30.639660f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_PILAR_COLLISION_4, 802.211609f, -284.493256f, 32.382710f, 3.141593f, 0.0f, 0.0f, 0.0f, 0.0f); + } + + void OnStart() override + { + for (ObjectGuid const& guid : _elevatorGUIDs) + { + if (GameObject* door = battlegroundMap->GetGameObject(guid)) + { + door->UseDoorOrButton(); + door->DespawnOrUnsummon(5s); + } + } + + _state = BG_RV_STATE_OPEN_FENCES; + _timer = BG_RV_FIRST_TIMER; + + // Should be false at first, TogglePillarCollision will do it. + _pillarCollision = true; + TogglePillarCollision(); + + _scheduler.Schedule(1min, [&](TaskContext) + { + CreateObject(BG_RV_OBJECT_TYPE_BUFF_1, 735.551819f, -284.794678f, 28.276682f, 0.034906f, 0.0f, 0.0f, 0.0f, 0.0f); + CreateObject(BG_RV_OBJECT_TYPE_BUFF_2, 791.224487f, -284.794464f, 28.276682f, 2.600535f, 0.0f, 0.0f, 0.0f, 0.0f); + }); + } + + void TogglePillarCollision() + { + // Toggle visual pillars, pulley, gear, and collision based on previous state + + GuidVector smallPillarGuids; + smallPillarGuids.insert(std::end(smallPillarGuids), std::begin(_pillarSmallGUIDs), std::end(_pillarSmallGUIDs)); + smallPillarGuids.insert(std::end(smallPillarGuids), std::begin(_gearGUIDs), std::end(_gearGUIDs)); + + for (ObjectGuid const& guid : smallPillarGuids) + { + if (GameObject* go = battlegroundMap->GetGameObject(guid)) + { + if (_pillarCollision) + go->UseDoorOrButton(); + else + go->ResetDoorOrButton(); + } + } + + GuidVector bigPillarGuids; + bigPillarGuids.insert(std::end(bigPillarGuids), std::begin(_pillarBigGUIDs), std::end(_pillarBigGUIDs)); + bigPillarGuids.insert(std::end(bigPillarGuids), std::begin(_pulleyGUIDs), std::end(_pulleyGUIDs)); + + for (ObjectGuid const& guid : bigPillarGuids) + { + if (GameObject* go = battlegroundMap->GetGameObject(guid)) + { + if (_pillarCollision) + go->ResetDoorOrButton(); + else + go->UseDoorOrButton(); + } + } + + GuidVector allObjects; + allObjects.insert(std::end(allObjects), std::begin(smallPillarGuids), std::end(smallPillarGuids)); + allObjects.insert(std::end(allObjects), std::begin(bigPillarGuids), std::end(bigPillarGuids)); + allObjects.insert(std::end(allObjects), std::begin(_pillarSmallCollisionGUIDs), std::end(_pillarSmallCollisionGUIDs)); + allObjects.insert(std::end(allObjects), std::begin(_pillarBigCollisionGUIDs), std::end(_pillarBigCollisionGUIDs)); + + for (ObjectGuid const& guid : allObjects) + { + if (GameObject* go = battlegroundMap->GetGameObject(guid)) + { + bool isCollision = false; + switch (go->GetEntry()) + { + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_1: + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_2: + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_3: + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_4: + isCollision = true; + break; + default: + break; + } + + if (isCollision) + { + GOState state = ((go->GetGOInfo()->door.startOpen != 0) == _pillarCollision) ? GO_STATE_ACTIVE : GO_STATE_READY; + go->SetGoState(state); + } + + for (auto const& [playerGuid, _]: battleground->GetPlayers()) + if (Player* player = ObjectAccessor::FindPlayer(playerGuid)) + go->SendUpdateToPlayer(player); + } + } + + _pillarCollision = !_pillarCollision; + } + + void OnGameObjectCreate(GameObject* gameobject) override + { + ArenaScript::OnGameObjectCreate(gameobject); + + switch (gameobject->GetEntry()) + { + case BG_RV_OBJECT_TYPE_ELEVATOR_1: + case BG_RV_OBJECT_TYPE_ELEVATOR_2: + _elevatorGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_FIRE_1: + case BG_RV_OBJECT_TYPE_FIRE_2: + _fireGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_FIREDOOR_1: + case BG_RV_OBJECT_TYPE_FIREDOOR_2: + _firedoorGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_GEAR_1: + case BG_RV_OBJECT_TYPE_GEAR_2: + _gearGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_PILAR_1: + case BG_RV_OBJECT_TYPE_PILAR_3: + _pillarSmallGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_PILAR_2: + case BG_RV_OBJECT_TYPE_PILAR_4: + _pillarBigGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_1: + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_3: + _pillarSmallCollisionGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_2: + case BG_RV_OBJECT_TYPE_PILAR_COLLISION_4: + _pillarBigCollisionGUIDs.emplace_back(gameobject->GetGUID()); + break; + case BG_RV_OBJECT_TYPE_PULLEY_1: + case BG_RV_OBJECT_TYPE_PULLEY_2: + _pulleyGUIDs.emplace_back(gameobject->GetGUID()); + break; + default: + break; + } + } + +private: + GuidVector _elevatorGUIDs; + GuidVector _gearGUIDs; + GuidVector _fireGUIDs; + GuidVector _firedoorGUIDs; + GuidVector _pillarSmallCollisionGUIDs; + GuidVector _pillarBigCollisionGUIDs; + GuidVector _pillarSmallGUIDs; + GuidVector _pillarBigGUIDs; + GuidVector _pulleyGUIDs; + TaskScheduler _scheduler; + + uint32 _timer; + uint32 _state; + bool _pillarCollision; +}; + +void AddSC_arena_ring_of_valor() +{ + RegisterBattlegroundMapScript(arena_ring_of_valor, 618); +} diff --git a/src/server/scripts/Battlegrounds/RuinsOfLordaeron/arena_ruins_of_lordaeron.cpp b/src/server/scripts/Battlegrounds/RuinsOfLordaeron/arena_ruins_of_lordaeron.cpp new file mode 100644 index 00000000000..75f9c6caef1 --- /dev/null +++ b/src/server/scripts/Battlegrounds/RuinsOfLordaeron/arena_ruins_of_lordaeron.cpp @@ -0,0 +1,80 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Battleground.h" +#include "BattlegroundScript.h" +#include "GameObject.h" +#include "Map.h" +#include "ScriptMgr.h" +#include "TaskScheduler.h" + +struct arena_ruins_of_lordaeron : ArenaScript +{ + enum BattlegroundRLGameObjects + { + BG_RL_OBJECT_TYPE_DOOR_1 = 185918, + BG_RL_OBJECT_TYPE_DOOR_2 = 185917, + BG_RL_OBJECT_TYPE_BUFF_1 = 184663, + BG_RL_OBJECT_TYPE_BUFF_2 = 184664 + }; + + explicit arena_ruins_of_lordaeron(BattlegroundMap* map) : ArenaScript(map) { } + + void OnUpdate(uint32 diff) override + { + _scheduler.Update(diff); + } + + void OnInit() override + { + AddDoor(BG_RL_OBJECT_TYPE_DOOR_1, 1293.561f, 1601.938f, 31.60557f, -1.457349f, 0, 0, -0.6658813f, 0.7460576f); + AddDoor(BG_RL_OBJECT_TYPE_DOOR_2, 1278.648f, 1730.557f, 31.60557f, 1.684245f, 0, 0, 0.7460582f, 0.6658807f); + } + + void OnStart() override + { + for (ObjectGuid const& guid : _doorGUIDs) + { + if (GameObject* door = battlegroundMap->GetGameObject(guid)) + { + door->UseDoorOrButton(); + door->DespawnOrUnsummon(5s); + } + } + + _scheduler.Schedule(1min, [&](TaskContext) + { + CreateObject(BG_RL_OBJECT_TYPE_BUFF_1, 1328.719971f, 1632.719971f, 36.730400f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f); + CreateObject(BG_RL_OBJECT_TYPE_BUFF_2, 1243.300049f, 1699.170044f, 34.872601f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f); + }); + } + + void AddDoor(uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, GOState goState = GO_STATE_READY) + { + if (GameObject const* go = CreateObject(entry, x, y, z, o, rotation0, rotation1, rotation2, rotation3, goState)) + _doorGUIDs.emplace_back(go->GetGUID()); + } + +private: + GuidVector _doorGUIDs; + TaskScheduler _scheduler; +}; + +void AddSC_arena_ruins_of_lordaeron() +{ + RegisterBattlegroundMapScript(arena_ruins_of_lordaeron, 572); +} diff --git a/src/server/scripts/Battlegrounds/StrandOfTheAncients/battleground_strand_of_the_ancients.cpp b/src/server/scripts/Battlegrounds/StrandOfTheAncients/battleground_strand_of_the_ancients.cpp new file mode 100644 index 00000000000..dba78903864 --- /dev/null +++ b/src/server/scripts/Battlegrounds/StrandOfTheAncients/battleground_strand_of_the_ancients.cpp @@ -0,0 +1,1059 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BattlegroundScript.h" +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "BattlegroundScore.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "GameObject.h" +#include "GameTime.h" +#include "Map.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "strand_of_the_ancients.h" +#include "VehicleDefines.h" +#include "WorldStateMgr.h" + +enum BG_SA_Status +{ + BG_SA_NOT_STARTED = 0, + BG_SA_WARMUP, + BG_SA_ROUND_ONE, + BG_SA_SECOND_WARMUP, + BG_SA_ROUND_TWO, + BG_SA_BONUS_ROUND +}; + +enum BG_SA_GateState +{ + // alliance is defender + BG_SA_ALLIANCE_GATE_OK = 1, + BG_SA_ALLIANCE_GATE_DAMAGED = 2, + BG_SA_ALLIANCE_GATE_DESTROYED = 3, + // horde is defender + BG_SA_HORDE_GATE_OK = 4, + BG_SA_HORDE_GATE_DAMAGED = 5, + BG_SA_HORDE_GATE_DESTROYED = 6, +}; + +enum StrandOfTheAncientsGameObjects +{ + GO_SEAFORIUM_BOMB_H = 194086, // Used by horde players + GO_SEAFORIUM_BOMB_A = 190753, // Used by alliance players + GO_SEAFORIUM_CHARGE_H = 257572, + GO_SEAFORIUM_CHARGE_A = 257565, + + GO_GRAVEYARD_WEST_H = 191307, + GO_GRAVEYARD_WEST_A = 191308, + + GO_GRAVEYARD_EAST_H = 191305, + GO_GRAVEYARD_EAST_A = 191306, + + GO_GRAVEYARD_CENTRAL_H = 191309, + GO_GRAVEYARD_CENTRAL_A = 191310, + + GO_COLLISION_DOOR = 194162, + GO_TITAN_RELIC_A = 194083, + GO_TITAN_RELIC_H = 194082, + + GO_GATE_OF_THE_GREEN_EMERALD = 190722, + GO_GATE_OF_THE_PURPLE_AMETHYST = 190723, + GO_GATE_OF_THE_BLUE_SAPPHIRE = 190724, + GO_GATE_OF_THE_RED_SUN = 190726, + GO_GATE_OF_THE_YELLOW_MOON = 190727, + GO_CHAMBER_OF_ANCIENT_RELICS = 192549 +}; + +enum BG_SA_Timers +{ + BG_SA_BOAT_START = 60 * IN_MILLISECONDS, + BG_SA_WARMUPLENGTH = 120 * IN_MILLISECONDS, + BG_SA_ROUNDLENGTH = 600 * IN_MILLISECONDS +}; + +enum SASounds +{ + SOUND_GRAVEYARD_TAKEN_HORDE = 8174, + SOUND_GRAVEYARD_TAKEN_ALLIANCE = 8212, + SOUND_DEFEAT_HORDE = 15905, + SOUND_VICTORY_HORDE = 15906, + SOUND_VICTORY_ALLIANCE = 15907, + SOUND_DEFEAT_ALLIANCE = 15908, + SOUND_WALL_DESTROYED_ALLIANCE = 15909, + SOUND_WALL_DESTROYED_HORDE = 15910, + SOUND_WALL_ATTACKED_HORDE = 15911, + SOUND_WALL_ATTACKED_ALLIANCE = 15912 +}; + +enum SATexts +{ + // Kanrethad + TEXT_ROUND_STARTED = 1, + TEXT_ROUND_1_FINISHED = 2, + + // Rigger Sparklight / Gorgril Rigspark + TEXT_SPARKLIGHT_RIGSPARK_SPAWN = 1, + + // World Trigger + TEXT_BLUE_GATE_UNDER_ATTACK = 1, + TEXT_GREEN_GATE_UNDER_ATTACK = 2, + TEXT_RED_GATE_UNDER_ATTACK = 3, + TEXT_PURPLE_GATE_UNDER_ATTACK = 4, + TEXT_YELLOW_GATE_UNDER_ATTACK = 5, + TEXT_YELLOW_GATE_DESTROYED = 6, + TEXT_PURPLE_GATE_DESTROYED = 7, + TEXT_RED_GATE_DESTROYED = 8, + TEXT_GREEN_GATE_DESTROYED = 9, + TEXT_BLUE_GATE_DESTROYED = 10, + TEXT_EAST_GRAVEYARD_CAPTURED_A = 11, + TEXT_WEST_GRAVEYARD_CAPTURED_A = 12, + TEXT_SOUTH_GRAVEYARD_CAPTURED_A = 13, + TEXT_EAST_GRAVEYARD_CAPTURED_H = 14, + TEXT_WEST_GRAVEYARD_CAPTURED_H = 15, + TEXT_SOUTH_GRAVEYARD_CAPTURED_H = 16, + TEXT_ANCIENT_GATE_UNDER_ATTACK = 17, + TEXT_ANCIENT_GATE_DESTROYED = 18 +}; + +enum SAWorldStates +{ + BG_SA_TIMER = 3557, + BG_SA_ALLY_ATTACKS = 4352, + BG_SA_HORDE_ATTACKS = 4353, + BG_SA_PURPLE_GATEWS = 3614, + BG_SA_RED_GATEWS = 3617, + BG_SA_BLUE_GATEWS = 3620, + BG_SA_GREEN_GATEWS = 3623, + BG_SA_YELLOW_GATEWS = 3638, + BG_SA_ANCIENT_GATEWS = 3849, + + BG_SA_LEFT_GY_ALLIANCE = 3635, + BG_SA_RIGHT_GY_ALLIANCE = 3636, + BG_SA_CENTER_GY_ALLIANCE = 3637, + + BG_SA_RIGHT_ATT_TOKEN_ALL = 3627, + BG_SA_LEFT_ATT_TOKEN_ALL = 3626, + BG_SA_LEFT_ATT_TOKEN_HRD = 3629, + BG_SA_RIGHT_ATT_TOKEN_HRD = 3628, + BG_SA_HORDE_DEFENCE_TOKEN = 3631, + BG_SA_ALLIANCE_DEFENCE_TOKEN = 3630, + + BG_SA_RIGHT_GY_HORDE = 3632, + BG_SA_LEFT_GY_HORDE = 3633, + BG_SA_CENTER_GY_HORDE = 3634, + + BG_SA_BONUS_TIMER = 3571, + + BG_SA_ENABLE_TIMER = 3564, + BG_SA_ATTACKER_TEAM = 3690, + BG_SA_DESTROYED_ALLIANCE_VEHICLES = 3955, + BG_SA_DESTROYED_HORDE_VEHICLES = 3956, +}; + +enum BG_SA_Boat +{ + BG_SA_BOAT_ONE_A = 208000, + BG_SA_BOAT_TWO_H = 208001, + BG_SA_BOAT_ONE_H = 193184, + BG_SA_BOAT_TWO_A = 193185 +}; + +static constexpr uint32 BG_SA_Factions[2] = +{ + 1732, + 1735, +}; + +enum class StrandOfTheAncientsGraveyard +{ + West, + East, + Central +}; + +enum BG_SA_BroadcastTexts +{ + BG_SA_TEXT_ALLIANCE_CAPTURED_TITAN_PORTAL = 28944, + BG_SA_TEXT_HORDE_CAPTURED_TITAN_PORTAL = 28945, + + BG_SA_TEXT_ROUND_TWO_START_ONE_MINUTE = 29448, + BG_SA_TEXT_ROUND_TWO_START_HALF_MINUTE = 29449 +}; + +enum class DefenseLine +{ + First, + Second, + Third, + Last +}; + +struct GateInfo +{ + uint32 GameObjectId; + uint32 WorldState; + uint8 DamagedText; + uint8 DestroyedText; + DefenseLine LineOfDefense; +}; + +GateInfo const Gates[] = +{ + { GO_GATE_OF_THE_GREEN_EMERALD, BG_SA_GREEN_GATEWS, TEXT_GREEN_GATE_UNDER_ATTACK, TEXT_GREEN_GATE_DESTROYED, DefenseLine::First }, + { GO_GATE_OF_THE_YELLOW_MOON, BG_SA_YELLOW_GATEWS, TEXT_YELLOW_GATE_UNDER_ATTACK, TEXT_YELLOW_GATE_DESTROYED, DefenseLine::Third }, + { GO_GATE_OF_THE_BLUE_SAPPHIRE, BG_SA_BLUE_GATEWS, TEXT_BLUE_GATE_UNDER_ATTACK, TEXT_BLUE_GATE_DESTROYED, DefenseLine::First }, + { GO_GATE_OF_THE_RED_SUN, BG_SA_RED_GATEWS, TEXT_RED_GATE_UNDER_ATTACK, TEXT_RED_GATE_DESTROYED, DefenseLine::Second }, + { GO_GATE_OF_THE_PURPLE_AMETHYST, BG_SA_PURPLE_GATEWS, TEXT_PURPLE_GATE_UNDER_ATTACK, TEXT_PURPLE_GATE_DESTROYED, DefenseLine::Second }, + { GO_CHAMBER_OF_ANCIENT_RELICS, BG_SA_ANCIENT_GATEWS, TEXT_ANCIENT_GATE_UNDER_ATTACK, TEXT_ANCIENT_GATE_DESTROYED, DefenseLine::Last } +}; + +struct BG_SA_RoundScore +{ + TeamId winner; + uint32 time; +}; + +enum StrandOfTheAncientsPvpStats +{ + PVP_STAT_GATES_DESTROYED = 231, + PVP_STAT_DEMOLISHERS_DESTROYED = 232 +}; + +enum StrandOfTheAncientsEvents +{ + EVENT_HORDE_ASSAULT_STARTED = 21702, + EVENT_ALLIANCE_ASSAULT_STARTED = 23748, + EVENT_TITAN_RELIC_ACTIVATED = 20572 +}; + +enum StrandOfTheAncientsCreatures +{ + NPC_KANRETHAD = 29, + NPC_DEMOLISHER = 28781, + NPC_ANTIPERSONNEL_CANNON = 27894, + NPC_RIGGER_SPARKLIGHT = 29260, + NPC_GORGRIL_RIGSPARK = 29262, + NPC_WORLD_TRIGGER = 22515, + NPC_DEMOLISHER_SA = 28781 +}; + +enum StrandOfTheAncientsSpawnPositions +{ + SPAWN_DEFENDERS = 1399 +}; + +enum StrandOfTheAncientsData +{ + DATA_ATTACKERS = 1, + DATA_STATUS = 2 +}; + +enum StrandOfTheAncientSpells +{ + SPELL_TELEPORT_DEFENDER = 52364, + SPELL_TELEPORT_ATTACKERS = 60178, + SPELL_END_OF_ROUND = 52459, + SPELL_REMOVE_SEAFORIUM = 59077, + SPELL_ALLIANCE_CONTROL_PHASE_SHIFT = 60027, + SPELL_HORDE_CONTROL_PHASE_SHIFT = 60028, + SPELL_CARRYING_SEAFORIUM_CHARGE = 52415 +}; + +static constexpr Position spawnPositionOnTransport[] = +{ + { 0.0f, 5.0f, 9.6f, 3.14f }, + { -6.0f, -3.0f, 8.6f, 0.0f } +}; + +struct battleground_strand_of_the_ancients : BattlegroundScript +{ + explicit battleground_strand_of_the_ancients(BattlegroundMap* map) : BattlegroundScript(map) + { + _timerEnabled = false; + _updateWaitTimer = 0; + _signaledRoundTwo = false; + _signaledRoundTwoHalfMin = false; + _initSecondRound = false; + _attackers = static_cast<TeamId>(urand(TEAM_ALLIANCE, TEAM_HORDE)); + _totalTime = 0; + _endRoundTimer = 0; + _shipsStarted = false; + _status = BG_SA_NOT_STARTED; + + _roundScores = { }; + + for (BG_SA_RoundScore& roundScore : _roundScores) + { + roundScore.winner = TEAM_ALLIANCE; + roundScore.time = 0; + } + + _boatGUIDs = { }; + _staticBombGUIDs = { }; + } + + void OnInit() override + { + BattlegroundScript::OnInit(); + _status = BG_SA_WARMUP; + ResetObjs(); + } + + void OnUpdate(uint32 diff) override + { + BattlegroundScript::OnUpdate(diff); + if (_initSecondRound) + { + if (_updateWaitTimer < diff) + { + if (!_signaledRoundTwo) + { + _signaledRoundTwo = true; + _initSecondRound = false; + battleground->SendBroadcastText(BG_SA_TEXT_ROUND_TWO_START_ONE_MINUTE, CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + } + else + { + _updateWaitTimer -= diff; + return; + } + } + _totalTime += diff; + + if (_status == BG_SA_WARMUP) + { + _endRoundTimer = BG_SA_ROUNDLENGTH; + UpdateWorldState(BG_SA_TIMER, GameTime::GetGameTime() + (_endRoundTimer / 1000)); + if (_totalTime >= BG_SA_WARMUPLENGTH) + { + if (Creature* c = FindKanrethad()) + battleground->SendChatMessage(c, TEXT_ROUND_STARTED); + + _totalTime = 0; + ToggleTimer(); + _status = BG_SA_ROUND_ONE; + TriggerGameEvent((_attackers == TEAM_ALLIANCE) ? EVENT_ALLIANCE_ASSAULT_STARTED : EVENT_HORDE_ASSAULT_STARTED); + } + if (_totalTime >= BG_SA_BOAT_START) + StartShips(); + return; + } + else if (_status == BG_SA_SECOND_WARMUP) + { + if (_roundScores[0].time < BG_SA_ROUNDLENGTH) + _endRoundTimer = _roundScores[0].time; + else + _endRoundTimer = BG_SA_ROUNDLENGTH; + + UpdateWorldState(BG_SA_TIMER, GameTime::GetGameTime() + (_endRoundTimer / 1000)); + if (_totalTime >= 60000) + { + if (Creature* c = FindKanrethad()) + battleground->SendChatMessage(c, TEXT_ROUND_STARTED); + + _totalTime = 0; + ToggleTimer(); + _status = BG_SA_ROUND_TWO; + TriggerGameEvent((_attackers == TEAM_ALLIANCE) ? EVENT_ALLIANCE_ASSAULT_STARTED : EVENT_HORDE_ASSAULT_STARTED); + // status was set to STATUS_WAIT_JOIN manually for Preparation, set it back now + battleground->SetStatus(STATUS_IN_PROGRESS); + for (const auto& [playerGuid, bp] : battleground->GetPlayers()) + if (Player* p = ObjectAccessor::FindPlayer(playerGuid)) + p->RemoveAurasDueToSpell(SPELL_PREPARATION); + } + if (_totalTime >= 30000) + { + if (!_signaledRoundTwoHalfMin) + { + _signaledRoundTwoHalfMin = true; + battleground->SendBroadcastText(BG_SA_TEXT_ROUND_TWO_START_HALF_MINUTE, CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + } + StartShips(); + return; + } + else if (battleground->GetStatus() == STATUS_IN_PROGRESS) + { + if (_status == BG_SA_ROUND_ONE) + { + if (_totalTime >= BG_SA_ROUNDLENGTH) + { + EndRound(); + return; + } + } + else if (_status == BG_SA_ROUND_TWO) + { + if (_totalTime >= _endRoundTimer) + { + battleground->CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE); + battleground->CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE); + _roundScores[1].time = BG_SA_ROUNDLENGTH; + _roundScores[1].winner = (_attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; + if (_roundScores[0].time == _roundScores[1].time) + battleground->EndBattleground(TEAM_OTHER); + else if (_roundScores[0].time < _roundScores[1].time) + battleground->EndBattleground(_roundScores[0].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + else + battleground->EndBattleground(_roundScores[1].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + return; + } + } + } + } + + void Reset() + { + _totalTime = 0; + _shipsStarted = false; + _status = BG_SA_WARMUP; + } + + void ResetObjs() + { + for (ObjectGuid const& bombGuid : _dynamicBombGUIDs) + if (GameObject* bomb = battlegroundMap->GetGameObject(bombGuid)) + bomb->Delete(); + + _dynamicBombGUIDs.clear(); + + TeamId const defenders = _attackers == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE; + + _totalTime = 0; + _shipsStarted = false; + + UpdateWorldState(BG_SA_ALLY_ATTACKS, _attackers == TEAM_ALLIANCE); + UpdateWorldState(BG_SA_HORDE_ATTACKS, _attackers == TEAM_HORDE); + + UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_ALL, _attackers == TEAM_ALLIANCE); + UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_ALL, _attackers == TEAM_ALLIANCE); + + UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_HRD, _attackers == TEAM_HORDE); + UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_HRD, _attackers == TEAM_HORDE); + + UpdateWorldState(BG_SA_HORDE_DEFENCE_TOKEN, defenders == TEAM_HORDE); + UpdateWorldState(BG_SA_ALLIANCE_DEFENCE_TOKEN, defenders == TEAM_ALLIANCE); + + CaptureGraveyard(StrandOfTheAncientsGraveyard::Central, defenders); + CaptureGraveyard(StrandOfTheAncientsGraveyard::West, defenders); + CaptureGraveyard(StrandOfTheAncientsGraveyard::East, defenders); + + UpdateWorldState(BG_SA_ATTACKER_TEAM, _attackers); + + for (ObjectGuid const& guid : _gateGUIDs) + if (GameObject* gate = battlegroundMap->GetGameObject(guid)) + gate->SetDestructibleState(GO_DESTRUCTIBLE_INTACT, nullptr, true); + + BG_SA_GateState const state = _attackers == TEAM_ALLIANCE ? BG_SA_HORDE_GATE_OK : BG_SA_ALLIANCE_GATE_OK; + UpdateWorldState(BG_SA_PURPLE_GATEWS, state); + UpdateWorldState(BG_SA_RED_GATEWS, state); + UpdateWorldState(BG_SA_BLUE_GATEWS, state); + UpdateWorldState(BG_SA_GREEN_GATEWS, state); + UpdateWorldState(BG_SA_YELLOW_GATEWS, state); + UpdateWorldState(BG_SA_ANCIENT_GATEWS, state); + + battlegroundMap->UpdateSpawnGroupConditions(); + + if (GameObject* door = battlegroundMap->GetGameObject(_collisionDoorGUID)) + door->ResetDoorOrButton(); + + battleground->SetStatus(STATUS_WAIT_JOIN); + battlegroundMap->DoOnPlayers([&](Player* player) + { + SendTransportInit(player); + }); + + TeleportPlayers(); + } + + void StartShips() + { + if (_shipsStarted) + return; + + for (ObjectGuid const& guid : _boatGUIDs[_attackers]) + { + if (GameObject* boat = battlegroundMap->GetGameObject(guid)) + { + boat->SetGoState(GO_STATE_TRANSPORT_STOPPED); + + // make sure every player knows the transport exists & is moving + for (auto const& [playerGuid, _] : battleground->GetPlayers()) + if (Player* player = ObjectAccessor::FindPlayer(playerGuid)) + boat->SendUpdateToPlayer(player); + } + } + + _shipsStarted = true; + } + + void OnPlayerJoined(Player* player, bool inBattleground) override + { + BattlegroundScript::OnPlayerJoined(player, inBattleground); + SendTransportInit(player); + if (!inBattleground) + TeleportToEntrancePosition(player); + } + + void TeleportPlayers() const + { + for (auto const& [playerGuid, bp] : battleground->GetPlayers()) + { + if (Player* player = ObjectAccessor::FindPlayer(playerGuid)) + { + // should remove spirit of redemption + if (player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + player->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + + if (!player->IsAlive()) + { + player->ResurrectPlayer(1.0f); + player->SpawnCorpseBones(); + } + + player->ResetAllPowers(); + player->CombatStopWithPets(true); + + player->CastSpell(player, SPELL_PREPARATION, true); + + TeleportToEntrancePosition(player); + } + } + } + + // This function teleports player to entrance position, but it's not implemented correctly + // It is also used on round swap; which should be handled by the following spells: 52365, 52528, 53464, 53465 (Split Teleport (FACTION) (Boat X)) + // This spell however cannot work with current system because of grid limitations. + // On battleground start, this function should work fine, except that it is called to late and we need a NearTeleport to solve this. + void TeleportToEntrancePosition(Player* player) const + { + if (Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(player->GetGUID())) == _attackers) + { + ObjectGuid const boatGUID = _boatGUIDs[_attackers][urand(0, 1)]; + + if (GameObject* boat = battlegroundMap->GetGameObject(boatGUID)) + { + if (TransportBase* transport = boat->ToTransportBase()) + { + player->Relocate(spawnPositionOnTransport[_attackers]); + transport->AddPassenger(player); + player->m_movementInfo.transport.pos.Relocate(spawnPositionOnTransport[_attackers]); + float x, y, z, o; + spawnPositionOnTransport[_attackers].GetPosition(x, y, z, o); + transport->CalculatePassengerPosition(x, y, z, &o); + player->Relocate(x, y, z, o); + + if (player->IsInWorld()) + player->NearTeleportTo({ x, y, z, o }); + } + } + } + else if (WorldSafeLocsEntry const* defenderSpawn = sObjectMgr->GetWorldSafeLoc(SPAWN_DEFENDERS)) + { + if (player->IsInWorld()) + player->TeleportTo(defenderSpawn->Loc); + else + player->WorldRelocate(defenderSpawn->Loc); + } + } + + void ProcessEvent(WorldObject* obj, uint32 eventId, WorldObject* invoker) override + { + BattlegroundScript::ProcessEvent(obj, eventId, invoker); + + switch (eventId) + { + case EVENT_ALLIANCE_ASSAULT_STARTED: + for (ObjectGuid const& bombGuid : _staticBombGUIDs[TEAM_ALLIANCE]) + if (GameObject* bomb = battlegroundMap->GetGameObject(bombGuid)) + bomb->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + break; + case EVENT_HORDE_ASSAULT_STARTED: + for (ObjectGuid const& bombGuid : _staticBombGUIDs[TEAM_HORDE]) + if (GameObject* bomb = battlegroundMap->GetGameObject(bombGuid)) + bomb->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + break; + default: + break; + } + + if (GameObject* go = Object::ToGameObject(obj)) + { + switch (go->GetGoType()) + { + case GAMEOBJECT_TYPE_GOOBER: + if (invoker) + if (eventId == EVENT_TITAN_RELIC_ACTIVATED) + TitanRelicActivated(invoker->ToPlayer()); + break; + case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: + { + if (GateInfo const* gate = GetGate(obj->GetEntry())) + { + uint32 gateId = gate->GameObjectId; + + // damaged + if (eventId == go->GetGOInfo()->destructibleBuilding.DamagedEvent) + { + BG_SA_GateState gateState = _attackers == TEAM_HORDE ? BG_SA_ALLIANCE_GATE_DAMAGED : BG_SA_HORDE_GATE_DAMAGED; + + if (Creature* c = obj->FindNearestCreature(NPC_WORLD_TRIGGER, 500.0f)) + battleground->SendChatMessage(c, gate->DamagedText, invoker); + + battleground->PlaySoundToAll(_attackers == TEAM_ALLIANCE ? SOUND_WALL_ATTACKED_ALLIANCE : SOUND_WALL_ATTACKED_HORDE); + + UpdateWorldState(gate->WorldState, gateState); + } + // destroyed + else if (eventId == go->GetGOInfo()->destructibleBuilding.DestroyedEvent) + { + BG_SA_GateState gateState = _attackers == TEAM_HORDE ? BG_SA_ALLIANCE_GATE_DESTROYED : BG_SA_HORDE_GATE_DESTROYED; + if (Creature* c = obj->FindNearestCreature(NPC_WORLD_TRIGGER, 500.0f)) + battleground->SendChatMessage(c, gate->DestroyedText, invoker); + + battleground->PlaySoundToAll(_attackers == TEAM_ALLIANCE ? SOUND_WALL_DESTROYED_ALLIANCE : SOUND_WALL_DESTROYED_HORDE); + + // check if other gate from same line of defense is already destroyed for honor reward + bool rewardHonor = true; + switch (gateId) + { + case GO_GATE_OF_THE_GREEN_EMERALD: + if (IsGateDestroyed(GetGate(GO_GATE_OF_THE_BLUE_SAPPHIRE))) + rewardHonor = false; + break; + case GO_GATE_OF_THE_BLUE_SAPPHIRE: + if (IsGateDestroyed(GetGate(GO_GATE_OF_THE_GREEN_EMERALD))) + rewardHonor = false; + break; + case GO_GATE_OF_THE_RED_SUN: + if (IsGateDestroyed(GetGate(GO_GATE_OF_THE_PURPLE_AMETHYST))) + rewardHonor = false; + break; + case GO_GATE_OF_THE_PURPLE_AMETHYST: + if (IsGateDestroyed(GetGate(GO_GATE_OF_THE_RED_SUN))) + rewardHonor = false; + break; + default: + break; + } + + if (invoker) + { + if (Unit* unit = invoker->ToUnit()) + { + if (Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + battleground->UpdatePvpStat(player, PVP_STAT_GATES_DESTROYED, 1); + if (rewardHonor) + battleground->UpdatePlayerScore(player, SCORE_BONUS_HONOR, battleground->GetBonusHonorFromKill(1)); + } + } + } + + if (rewardHonor) + MakeObjectsInteractable(gate->LineOfDefense); + + UpdateWorldState(gate->WorldState, gateState); + battlegroundMap->UpdateSpawnGroupConditions(); + } + } + break; + } + default: + break; + } + } + } + + void OnUnitKilled(Creature* victim, Unit* killer) override + { + if (victim->GetEntry() == NPC_DEMOLISHER_SA) + { + if (Player* killerPlayer = killer->GetCharmerOrOwnerPlayerOrPlayerItself()) + battleground->UpdatePvpStat(killerPlayer, PVP_STAT_DEMOLISHERS_DESTROYED, 1); + int32 worldStateId = _attackers == TEAM_HORDE ? BG_SA_DESTROYED_HORDE_VEHICLES : BG_SA_DESTROYED_ALLIANCE_VEHICLES; + int32 currentDestroyedVehicles = sWorldStateMgr->GetValue(worldStateId, battlegroundMap); + UpdateWorldState(worldStateId, currentDestroyedVehicles + 1); + } + } + + void CaptureGraveyard(StrandOfTheAncientsGraveyard graveyard, TeamId teamId) + { + switch (graveyard) + { + case StrandOfTheAncientsGraveyard::West: + UpdateWorldState(BG_SA_LEFT_GY_ALLIANCE, teamId == TEAM_ALLIANCE); + UpdateWorldState(BG_SA_LEFT_GY_HORDE, teamId == TEAM_HORDE); + break; + case StrandOfTheAncientsGraveyard::East: + UpdateWorldState(BG_SA_RIGHT_GY_ALLIANCE, teamId == TEAM_ALLIANCE); + UpdateWorldState(BG_SA_RIGHT_GY_HORDE, teamId == TEAM_HORDE); + break; + case StrandOfTheAncientsGraveyard::Central: + UpdateWorldState(BG_SA_CENTER_GY_ALLIANCE, teamId == TEAM_ALLIANCE); + UpdateWorldState(BG_SA_CENTER_GY_HORDE, teamId == TEAM_HORDE); + + CaptureGraveyard(StrandOfTheAncientsGraveyard::East, teamId); + CaptureGraveyard(StrandOfTheAncientsGraveyard::West, teamId); + break; + default: + break; + } + } + + void TitanRelicActivated(Player const* clicker) + { + if (!clicker) + return; + + TeamId const clickerTeamId = Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(clicker->GetGUID())); + if (clickerTeamId == _attackers) + { + if (clickerTeamId == TEAM_ALLIANCE) + battleground->SendBroadcastText(BG_SA_TEXT_ALLIANCE_CAPTURED_TITAN_PORTAL, CHAT_MSG_BG_SYSTEM_ALLIANCE); + else + battleground->SendBroadcastText(BG_SA_TEXT_HORDE_CAPTURED_TITAN_PORTAL, CHAT_MSG_BG_SYSTEM_HORDE); + + if (_status == BG_SA_ROUND_ONE) + { + EndRound(); + // Achievement Storm the Beach (1310) + for (auto itr = battleground->GetPlayers().begin(); itr != battleground->GetPlayers().end(); ++itr) + { + if (Player* player = ObjectAccessor::FindPlayer(itr->first)) + if (Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(player->GetGUID())) == _attackers) + player->UpdateCriteria(CriteriaType::BeSpellTarget, 65246); + } + + if (Creature* c = FindKanrethad()) + battleground->SendChatMessage(c, TEXT_ROUND_1_FINISHED); + } + else if (_status == BG_SA_ROUND_TWO) + { + _roundScores[1].winner = _attackers; + _roundScores[1].time = _totalTime; + ToggleTimer(); + // Achievement Storm the Beach (1310) + for (auto itr = battleground->GetPlayers().begin(); itr != battleground->GetPlayers().end(); ++itr) + { + if (Player* player = ObjectAccessor::FindPlayer(itr->first)) + if (Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(player->GetGUID())) == _attackers && _roundScores[1].winner == _attackers) + player->UpdateCriteria(CriteriaType::BeSpellTarget, 65246); + } + + if (_roundScores[0].time == _roundScores[1].time) + battleground->EndBattleground(TEAM_OTHER); + else if (_roundScores[0].time < _roundScores[1].time) + battleground->EndBattleground(_roundScores[0].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + else + battleground->EndBattleground(_roundScores[1].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + } + } + } + + void ToggleTimer() + { + _timerEnabled = !_timerEnabled; + UpdateWorldState(BG_SA_ENABLE_TIMER, _timerEnabled); + } + + void OnEnd(Team winner) override + { + BattlegroundScript::OnEnd(winner); + + // honor reward for winning + if (winner == ALLIANCE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), ALLIANCE); + else if (winner == HORDE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(1), HORDE); + + // complete map_end rewards (even if no team wins) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(2), ALLIANCE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(2), HORDE); + } + + void SendTransportInit(Player* player) const + { + for (ObjectGuid const& boatGuid : _boatGUIDs[_attackers]) + if (GameObject* boat = battlegroundMap->GetGameObject(boatGuid)) + boat->SendUpdateToPlayer(player); + } + + bool IsGateDestroyed(GateInfo const* gateInfo) const + { + if (!gateInfo) + return false; + + int32 value = battlegroundMap->GetWorldStateValue(gateInfo->WorldState); + return value == BG_SA_ALLIANCE_GATE_DESTROYED || value == BG_SA_HORDE_GATE_DESTROYED; + } + + void HandleCaptureGraveyardAction(GameObject const* graveyardBanner, Player const* player) + { + if (!graveyardBanner || !player) + return; + + TeamId const teamId = Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(player->GetGUID())); + // Only attackers can capture graveyard by gameobject action + if (teamId != _attackers) + return; + + switch (graveyardBanner->GetEntry()) + { + case GO_GRAVEYARD_WEST_A: + case GO_GRAVEYARD_WEST_H: + CaptureGraveyard(StrandOfTheAncientsGraveyard::West, teamId); + break; + case GO_GRAVEYARD_EAST_A: + case GO_GRAVEYARD_EAST_H: + CaptureGraveyard(StrandOfTheAncientsGraveyard::East, teamId); + break; + case GO_GRAVEYARD_CENTRAL_A: + case GO_GRAVEYARD_CENTRAL_H: + CaptureGraveyard(StrandOfTheAncientsGraveyard::Central, teamId); + break; + default: + break; + } + } + + void MakeObjectsInteractable(DefenseLine defenseLine) const + { + auto makeInteractable = [&](ObjectGuid const& guid) -> void + { + if (GameObject* gameObject = battlegroundMap->GetGameObject(guid)) + gameObject->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + }; + + switch (defenseLine) + { + case DefenseLine::First: + makeInteractable(_graveyardWest); + makeInteractable(_graveyardEast); + break; + case DefenseLine::Second: + makeInteractable(_graveyardCentral); + break; + case DefenseLine::Third: + break; + case DefenseLine::Last: + // make titan orb interactable + if (GameObject* door = battlegroundMap->GetGameObject(_collisionDoorGUID)) + door->UseDoorOrButton(); + + makeInteractable(_titanRelicGUID); + break; + default: + break; + } + } + + Creature* FindKanrethad() const + { + return battlegroundMap->GetCreature(_kanrethadGUID); + } + + void EndRound() + { + _roundScores[0].winner = _attackers; + _roundScores[0].time = std::min<uint32>(_totalTime, BG_SA_ROUNDLENGTH); + + _attackers = (_attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; + _status = BG_SA_SECOND_WARMUP; + _totalTime = 0; + ToggleTimer(); + + _updateWaitTimer = 5000; + _signaledRoundTwo = false; + _signaledRoundTwoHalfMin = false; + _initSecondRound = true; + ResetObjs(); + battlegroundMap->UpdateAreaDependentAuras(); + + battleground->CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE); + battleground->CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE); + + battleground->RemoveAuraOnTeam(SPELL_CARRYING_SEAFORIUM_CHARGE, HORDE); + battleground->RemoveAuraOnTeam(SPELL_CARRYING_SEAFORIUM_CHARGE, ALLIANCE); + } + + void OnGameObjectCreate(GameObject* gameobject) override + { + BattlegroundScript::OnGameObjectCreate(gameobject); + + if (gameobject->GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) + _gateGUIDs.insert(gameobject->GetGUID()); + + switch (gameobject->GetEntry()) + { + case BG_SA_BOAT_ONE_A: + _boatGUIDs[TEAM_ALLIANCE][0] = gameobject->GetGUID(); + break; + case BG_SA_BOAT_TWO_A: + _boatGUIDs[TEAM_ALLIANCE][1] = gameobject->GetGUID(); + break; + case BG_SA_BOAT_ONE_H: + _boatGUIDs[TEAM_HORDE][0] = gameobject->GetGUID(); + break; + case BG_SA_BOAT_TWO_H: + _boatGUIDs[TEAM_HORDE][1] = gameobject->GetGUID(); + break; + case GO_SEAFORIUM_BOMB_A: + _staticBombGUIDs[TEAM_ALLIANCE].insert(gameobject->GetGUID()); + if (_status != BG_SA_SECOND_WARMUP && _status != BG_SA_WARMUP) + gameobject->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + break; + case GO_SEAFORIUM_BOMB_H: + _staticBombGUIDs[TEAM_HORDE].insert(gameobject->GetGUID()); + if (_status != BG_SA_SECOND_WARMUP && _status != BG_SA_WARMUP) + gameobject->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + break; + case GO_SEAFORIUM_CHARGE_A: + case GO_SEAFORIUM_CHARGE_H: + _dynamicBombGUIDs.insert(gameobject->GetGUID()); + break; + case GO_GRAVEYARD_EAST_A: + case GO_GRAVEYARD_EAST_H: + _graveyardEast = gameobject->GetGUID(); + break; + case GO_GRAVEYARD_WEST_A: + case GO_GRAVEYARD_WEST_H: + _graveyardWest = gameobject->GetGUID(); + break; + case GO_GRAVEYARD_CENTRAL_A: + case GO_GRAVEYARD_CENTRAL_H: + _graveyardCentral = gameobject->GetGUID(); + break; + case GO_COLLISION_DOOR: + _collisionDoorGUID = gameobject->GetGUID(); + break; + case GO_TITAN_RELIC_A: + case GO_TITAN_RELIC_H: + _titanRelicGUID = gameobject->GetGUID(); + break; + default: + break; + } + } + + void DoAction(uint32 actionId, WorldObject* source, WorldObject* target) override + { + BattlegroundScript::DoAction(actionId, source, target); + + switch (actionId) + { + case ACTION_SOTA_CAPTURE_GRAVEYARD: + HandleCaptureGraveyardAction(Object::ToGameObject(target), Object::ToPlayer(source)); + break; + default: + break; + } + } + + void OnCreatureCreate(Creature* creature) override + { + BattlegroundScript::OnCreatureCreate(creature); + switch (creature->GetEntry()) + { + case NPC_DEMOLISHER: + creature->SetFaction(BG_SA_Factions[_attackers]); + break; + case NPC_ANTIPERSONNEL_CANNON: + creature->SetFaction(BG_SA_Factions[_attackers == TEAM_HORDE ? TEAM_ALLIANCE : TEAM_HORDE]); + break; + case NPC_KANRETHAD: + _kanrethadGUID = creature->GetGUID(); + break; + case NPC_RIGGER_SPARKLIGHT: + case NPC_GORGRIL_RIGSPARK: + creature->AI()->Talk(TEXT_SPARKLIGHT_RIGSPARK_SPAWN); + break; + default: + break; + } + } + + uint32 GetData(uint32 dataId) const override + { + switch (dataId) + { + case DATA_ATTACKERS: + return _attackers; + case DATA_STATUS: + return _status; + default: + return BattlegroundScript::GetData(dataId); + } + } + + static GateInfo const* GetGate(uint32 entry) + { + for (GateInfo const& gate : Gates) + if (gate.GameObjectId == entry) + return &gate; + + return nullptr; + } + +private: + /// Id of attacker team + TeamId _attackers; + + /// Totale elapsed time of current round + uint32 _totalTime; + /// Max time of round + uint32 _endRoundTimer; + /// For know if boats has start moving or not yet + bool _shipsStarted; + /// Statu of battle (Start or not, and what round) + BG_SA_Status _status; + /// Score of each round + std::array<BG_SA_RoundScore, 2> _roundScores; + /// used for know we are in timer phase or not (used for worldstate update) + bool _timerEnabled; + /// 5secs before starting the 1min countdown for second round + uint32 _updateWaitTimer; + /// for know if warning about second round start has been sent + bool _signaledRoundTwo; + /// for know if warning about second round start has been sent + bool _signaledRoundTwoHalfMin; + /// for know if second round has been init + bool _initSecondRound; + + // [team][boat_idx] + std::array<std::array<ObjectGuid, 2 /*BOAT_COUNT*/>, PVP_TEAMS_COUNT> _boatGUIDs; + std::array<GuidUnorderedSet, PVP_TEAMS_COUNT> _staticBombGUIDs; // bombs ready to be picked up + GuidUnorderedSet _dynamicBombGUIDs; // bombs thrown by players, ready to explode/disarm + + ObjectGuid _graveyardWest; + ObjectGuid _graveyardEast; + ObjectGuid _graveyardCentral; + GuidUnorderedSet _gateGUIDs; + ObjectGuid _collisionDoorGUID; + ObjectGuid _kanrethadGUID; + ObjectGuid _titanRelicGUID; +}; + +void AddSC_battleground_strand_of_the_ancients() +{ + RegisterBattlegroundMapScript(battleground_strand_of_the_ancients, 607); +} diff --git a/src/server/scripts/Northrend/StrandOfTheAncients/strand_of_the_ancients.cpp b/src/server/scripts/Battlegrounds/StrandOfTheAncients/strand_of_the_ancients.cpp index ed268327760..0ad0c7381e2 100644 --- a/src/server/scripts/Northrend/StrandOfTheAncients/strand_of_the_ancients.cpp +++ b/src/server/scripts/Battlegrounds/StrandOfTheAncients/strand_of_the_ancients.cpp @@ -15,8 +15,8 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "strand_of_the_ancients.h" #include "ScriptMgr.h" -#include "BattlegroundSA.h" #include "GameObject.h" #include "GameObjectAI.h" #include "Player.h" diff --git a/src/server/scripts/Battlegrounds/StrandOfTheAncients/strand_of_the_ancients.h b/src/server/scripts/Battlegrounds/StrandOfTheAncients/strand_of_the_ancients.h new file mode 100644 index 00000000000..4d86099136d --- /dev/null +++ b/src/server/scripts/Battlegrounds/StrandOfTheAncients/strand_of_the_ancients.h @@ -0,0 +1,26 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITYCORE_STRAND_OF_THE_ANCIENTS_H +#define TRINITYCORE_STRAND_OF_THE_ANCIENTS_H + +enum StrandOfTheAncientSharedActions +{ + ACTION_SOTA_CAPTURE_GRAVEYARD +}; + +#endif diff --git a/src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp b/src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp new file mode 100644 index 00000000000..73f8db98aab --- /dev/null +++ b/src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp @@ -0,0 +1,35 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BattlegroundScript.h" +#include "ScriptMgr.h" + +struct battleground_twin_peaks : BattlegroundScript +{ + enum PvpStats : uint32 + { + BG_TP_FLAG_CAPTURES = 290, + BG_TP_FLAG_RETURNS = 291 + }; + + explicit battleground_twin_peaks(BattlegroundMap* map) : BattlegroundScript(map) { } +}; + +void AddSC_battleground_twin_peaks() +{ + RegisterBattlegroundMapScript(battleground_twin_peaks, 726); +} diff --git a/src/server/scripts/Battlegrounds/WarsongGulch/battleground_warsong_gulch.cpp b/src/server/scripts/Battlegrounds/WarsongGulch/battleground_warsong_gulch.cpp new file mode 100644 index 00000000000..be76b69abf1 --- /dev/null +++ b/src/server/scripts/Battlegrounds/WarsongGulch/battleground_warsong_gulch.cpp @@ -0,0 +1,608 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "AreaTrigger.h" +#include "BattlegroundScript.h" +#include "Battleground.h" +#include "BattlegroundMgr.h" +#include "GameObject.h" +#include "GameTime.h" +#include "Map.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "SpellAuras.h" +#include "Timer.h" + +struct battleground_warsong_gulch : BattlegroundScript +{ + static constexpr Seconds FLAG_ASSAULT_TIMER = 30s; + + enum Misc + { + MAX_TEAM_SCORE = 3, + FLAG_BRUTAL_ASSAULT_STACK_COUNT = 5 + }; + + enum Spells : uint32 + { + SPELL_FOCUSED_ASSAULT = 46392, + SPELL_BRUTAL_ASSAULT = 46393, + SPELL_CAPTURED_ALLIANCE_COSMETIC_FX = 262508, + SPELL_CAPTURED_HORDE_COSMETIC_FX = 262512, + SPELL_WARSONG_FLAG = 23333, + SPELL_WARSONG_FLAG_DROPPED = 23334, + SPELL_SILVERWING_FLAG = 23335, + SPELL_SILVERWING_FLAG_DROPPED = 23336, + SPELL_QUICK_CAP_TIMER = 183317, + }; + + enum AreaTriggers : uint32 + { + AT_CAPTURE_POINT_ALLIANCE = 30, + AT_CAPTURE_POINT_HORDE = 31 + }; + + enum Events : uint32 + { + EVENT_START_BATTLE = 35912 + }; + + enum PvpStats : uint32 + { + PVP_STAT_FLAG_CAPTURES = 928, + PVP_STAT_FLAG_RETURNS = 929 + }; + + enum WorldStates : int32 + { + WORLD_STATE_FLAG_STATE_ALLIANCE = 1545, + WORLD_STATE_FLAG_STATE_HORDE = 1546, + WORLD_STATE_FLAG_STATE_NEUTRAL = 1547, + WORLD_STATE_HORDE_FLAG_COUNT_PICKED_UP = 17712, + WORLD_STATE_ALLIANCE_FLAG_COUNT_PICKED_UP = 17713, + WORLD_STATE_FLAG_CAPTURES_ALLIANCE = 1581, + WORLD_STATE_FLAG_CAPTURES_HORDE = 1582, + WORLD_STATE_FLAG_CAPTURES_MAX = 1601, + WORLD_STATE_FLAG_CAPTURES_MAX_NEW = 17303, + WORLD_STATE_FLAG_CONTROL_HORDE = 2338, + WORLD_STATE_FLAG_CONTROL_ALLIANCE = 2339, + WORLD_STATE_STATE_TIMER = 4248, + WORLD_STATE_STATE_TIMER_ACTIVE = 4247 + }; + + enum Texts + { + TEXT_START_ONE_MINUTE = 10015, + TEXT_START_HALF_MINUTE = 10016, + TEXT_BATTLE_HAS_BEGUN = 10014, + TEXT_CAPTURED_HORDE_FLAG = 9801, + TEXT_CAPTURED_ALLIANCE_FLAG = 9802, + TEXT_FLAGS_PLACED = 9803, + TEXT_ALLIANCE_FLAG_PICKED_UP = 9804, + TEXT_ALLIANCE_FLAG_DROPPED = 9805, + TEXT_HORDE_FLAG_PICKED_UP = 9807, + TEXT_HORDE_FLAG_DROPPED = 9806, + TEXT_ALLIANCE_FLAG_RETURNED = 9808, + TEXT_HORDE_FLAG_RETURNED = 9809 + }; + + enum Sounds + { + SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + SOUND_FLAG_CAPTURED_HORDE = 8213, + SOUND_FLAG_PLACED = 8232, + SOUND_FLAG_RETURNED = 8192, + SOUND_HORDE_FLAG_PICKED_UP = 8212, + SOUND_ALLIANCE_FLAG_PICKED_UP = 8174, + SOUND_FLAGS_RESPAWNED = 8232 + }; + + enum GameObjects + { + OBJECT_ALLIANCE_DOOR = 309704, + OBJECT_PORTCULLIS_009 = 309705, + OBJECT_PORTCULLIS_002 = 309883, + OBJECT_COLLISION_PC_SIZE = 242273, + OBJECT_HORDE_GATE_1 = 352709, + OBJECT_HORDE_GATE_2 = 352710, + OBJECT_ALLIANCE_FLAG_IN_BASE = 227741, + OBJECT_HORDE_FLAG_IN_BASE = 227740 + }; + + static constexpr std::array<std::array<uint32, 3>, 2> HONOR_REWARDS = + {{ + { 20, 40, 40 }, + { 60, 40, 80 } + }}; + + explicit battleground_warsong_gulch(BattlegroundMap* map) : BattlegroundScript(map), _lastFlagCaptureTeam(TEAM_OTHER), _bothFlagsKept(false), _flags({ }), _assaultStackCount(0), _capturePointAreaTriggers({ }) + { + _flagAssaultTimer.Reset(FLAG_ASSAULT_TIMER); + + if (sBattlegroundMgr->IsBGWeekend(battleground->GetTypeID())) + { + _reputationCapture = 45; + _honorWinKills = 3; + _honorEndKills = 4; + } + else + { + _reputationCapture = 35; + _honorWinKills = 1; + _honorEndKills = 2; + } + } + + void OnUpdate(uint32 diff) override + { + BattlegroundScript::OnUpdate(diff); + + if (battleground->GetStatus() == STATUS_IN_PROGRESS) + { + if (battleground->GetElapsedTime() >= 17 * MINUTE * IN_MILLISECONDS) + { + if (battleground->GetTeamScore(TEAM_ALLIANCE) == 0) + { + if (battleground->GetTeamScore(TEAM_HORDE) == 0) // No one scored - result is tie + battleground->EndBattleground(TEAM_OTHER); + else // Horde has more points and thus wins + battleground->EndBattleground(HORDE); + } + else if (battleground->GetTeamScore(TEAM_HORDE) == 0) // Alliance has > 0, Horde has 0, alliance wins + battleground->EndBattleground(ALLIANCE); + else if (battleground->GetTeamScore(TEAM_HORDE) == battleground->GetTeamScore(TEAM_ALLIANCE)) // Team score equal, winner is team that scored the last flag + battleground->EndBattleground(_lastFlagCaptureTeam); + else if (battleground->GetTeamScore(TEAM_HORDE) > battleground->GetTeamScore(TEAM_ALLIANCE)) // Last but not least, check who has the higher score + battleground->EndBattleground(HORDE); + else + battleground->EndBattleground(ALLIANCE); + } + } + + if (_bothFlagsKept) + { + _flagAssaultTimer.Update(diff); + if (_flagAssaultTimer.Passed()) + { + _flagAssaultTimer.Reset(FLAG_ASSAULT_TIMER); + if (_assaultStackCount < std::numeric_limits<uint8>::max()) + { + _assaultStackCount++; + + // update assault debuff stacks + DoForFlagKeepers([&](Player* player) -> void + { + ApplyAssaultDebuffToPlayer(player); + }); + } + } + } + } + + void OnStart() override + { + BattlegroundScript::OnStart(); + for (ObjectGuid door : _doors) + { + if (GameObject* gameObject = battlegroundMap->GetGameObject(door)) + { + gameObject->UseDoorOrButton(); + gameObject->DespawnOrUnsummon(3s); + } + } + + UpdateWorldState(WORLD_STATE_STATE_TIMER_ACTIVE, 1); + UpdateWorldState(WORLD_STATE_STATE_TIMER, std::chrono::system_clock::to_time_t(GameTime::GetSystemTime() + 15min)); + + // players joining later are not eligibles + TriggerGameEvent(EVENT_START_BATTLE); + } + + void OnEnd(Team winner) override + { + BattlegroundScript::OnEnd(winner); + // Win reward + if (winner == ALLIANCE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(_honorWinKills), ALLIANCE); + if (winner == HORDE) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(_honorWinKills), HORDE); + + // Complete map_end rewards (even if no team wins) + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(_honorEndKills), ALLIANCE); + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(_honorEndKills), HORDE); + } + + void DoForFlagKeepers(std::function<void(Player*)> const& action) const + { + for (ObjectGuid flagGUID : _flags) + if (GameObject const* flag = battlegroundMap->GetGameObject(flagGUID)) + if (Player* carrier = ObjectAccessor::FindPlayer(flag->GetFlagCarrierGUID())) + action(carrier); + } + + void ResetAssaultDebuff() + { + _bothFlagsKept = false; + _assaultStackCount = 0; + _flagAssaultTimer.Reset(FLAG_ASSAULT_TIMER); + DoForFlagKeepers([&](Player* player) -> void + { + RemoveAssaultDebuffFromPlayer(player); + }); + } + + void ApplyAssaultDebuffToPlayer(Player* player) const + { + if (_assaultStackCount == 0) + return; + + uint32 spellId = SPELL_FOCUSED_ASSAULT; + if (_assaultStackCount >= FLAG_BRUTAL_ASSAULT_STACK_COUNT) + { + player->RemoveAurasDueToSpell(SPELL_FOCUSED_ASSAULT); + spellId = SPELL_BRUTAL_ASSAULT; + } + + Aura* aura = player->GetAura(spellId); + if (!aura) + { + player->CastSpell(player, spellId, true); + aura = player->GetAura(spellId); + } + + if (aura) + aura->SetStackAmount(_assaultStackCount); + } + + void RemoveAssaultDebuffFromPlayer(Player* player) const + { + player->RemoveAurasDueToSpell(SPELL_FOCUSED_ASSAULT); + player->RemoveAurasDueToSpell(SPELL_BRUTAL_ASSAULT); + } + + FlagState GetFlagState(TeamId team) const + { + if (GameObject const* flag = battlegroundMap->GetGameObject(_flags[team])) + return flag->GetFlagState(); + + return FlagState(0); + } + + ObjectGuid const& GetFlagCarrierGUID(TeamId team) const + { + if (GameObject const* flag = battlegroundMap->GetGameObject(_flags[team])) + return flag->GetFlagCarrierGUID(); + + return ObjectGuid::Empty; + } + + void HandleFlagRoomCapturePoint() + { + DoForFlagKeepers([&](Player* player) -> void + { + TeamId const team = Battleground::GetTeamIndexByTeamId(battleground->GetPlayerTeam(player->GetGUID())); + if (AreaTrigger* trigger = battlegroundMap->GetAreaTrigger(_capturePointAreaTriggers[team])) + if (trigger->GetInsideUnits().contains(player->GetGUID())) + if (CanCaptureFlag(trigger, player)) + OnCaptureFlag(trigger, player); + }); + } + + void UpdateFlagState(uint32 team, FlagState value) const + { + auto transformValueToOtherTeamControlWorldState = [](FlagState state) + { + switch (state) + { + case FlagState::InBase: + case FlagState::Dropped: + case FlagState::Respawning: + return 1; + case FlagState::Taken: + return 2; + default: + return 0; + } + }; + + if (team == ALLIANCE) + { + UpdateWorldState(WORLD_STATE_FLAG_STATE_ALLIANCE, AsUnderlyingType(value)); + UpdateWorldState(WORLD_STATE_FLAG_CONTROL_HORDE, transformValueToOtherTeamControlWorldState(value)); + } + else + { + UpdateWorldState(WORLD_STATE_FLAG_STATE_HORDE, AsUnderlyingType(value)); + UpdateWorldState(WORLD_STATE_FLAG_CONTROL_ALLIANCE, transformValueToOtherTeamControlWorldState(value)); + } + } + + void UpdateTeamScore(TeamId team) const + { + if (team == TEAM_ALLIANCE) + UpdateWorldState(WORLD_STATE_FLAG_CAPTURES_ALLIANCE, battleground->GetTeamScore(team)); + else + UpdateWorldState(WORLD_STATE_FLAG_CAPTURES_HORDE, battleground->GetTeamScore(team)); + } + + void OnGameObjectCreate(GameObject* gameObject) override + { + BattlegroundScript::OnGameObjectCreate(gameObject); + switch (gameObject->GetEntry()) + { + case OBJECT_ALLIANCE_DOOR: + case OBJECT_PORTCULLIS_009: + case OBJECT_PORTCULLIS_002: + case OBJECT_COLLISION_PC_SIZE: + case OBJECT_HORDE_GATE_1: + case OBJECT_HORDE_GATE_2: + _doors.insert(gameObject->GetGUID()); + break; + case OBJECT_ALLIANCE_FLAG_IN_BASE: + _flags[TEAM_ALLIANCE] = gameObject->GetGUID(); + break; + case OBJECT_HORDE_FLAG_IN_BASE: + _flags[TEAM_HORDE] = gameObject->GetGUID(); + break; + default: + break; + } + } + + void OnAreaTriggerCreate(AreaTrigger* areaTrigger) override + { + BattlegroundScript::OnAreaTriggerCreate(areaTrigger); + if (!areaTrigger->IsStaticSpawn()) + return; + + switch (areaTrigger->GetEntry()) + { + case AT_CAPTURE_POINT_ALLIANCE: + _capturePointAreaTriggers[TEAM_ALLIANCE] = areaTrigger->GetGUID(); + break; + case AT_CAPTURE_POINT_HORDE: + _capturePointAreaTriggers[TEAM_HORDE] = areaTrigger->GetGUID(); + break; + default: + break; + } + } + + void OnFlagStateChange(GameObject* flagInBase, FlagState oldValue, FlagState newValue, Player* player) override + { + BattlegroundScript::OnFlagStateChange(flagInBase, oldValue, newValue, player); + + Team const team = flagInBase->GetEntry() == OBJECT_HORDE_FLAG_IN_BASE ? HORDE : ALLIANCE; + TeamId const otherTeamId = Battleground::GetTeamIndexByTeamId(GetOtherTeam(team)); + + UpdateFlagState(team, newValue); + + switch (newValue) + { + case FlagState::InBase: + { + if (battleground->GetStatus() == STATUS_IN_PROGRESS) + { + ResetAssaultDebuff(); + if (player) + { + // flag got returned to base by player interaction + battleground->UpdatePvpStat(player, PVP_STAT_FLAG_RETURNS, 1); // +1 flag returns + + if (team == ALLIANCE) + { + battleground->SendBroadcastText(TEXT_ALLIANCE_FLAG_RETURNED, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + battleground->PlaySoundToAll(SOUND_FLAG_RETURNED); + } + else + { + battleground->SendBroadcastText(TEXT_HORDE_FLAG_RETURNED, CHAT_MSG_BG_SYSTEM_HORDE, player); + battleground->PlaySoundToAll(SOUND_FLAG_RETURNED); + } + } + // Flag respawned due to timeout/capture + else if (GetFlagState(otherTeamId) != FlagState::Respawning) + { + // if other flag is respawning, we will let that one handle the message and sound to prevent double message/sound. + battleground->SendBroadcastText(TEXT_FLAGS_PLACED, CHAT_MSG_BG_SYSTEM_NEUTRAL); + battleground->PlaySoundToAll(SOUND_FLAGS_RESPAWNED); + } + + HandleFlagRoomCapturePoint(); + } + break; + } + case FlagState::Dropped: + { + player->RemoveAurasDueToSpell(SPELL_QUICK_CAP_TIMER); + RemoveAssaultDebuffFromPlayer(player); + + uint32 recentlyDroppedSpellId = SPELL_RECENTLY_DROPPED_HORDE_FLAG; + if (team == ALLIANCE) + { + recentlyDroppedSpellId = SPELL_RECENTLY_DROPPED_ALLIANCE_FLAG; + battleground->SendBroadcastText(TEXT_ALLIANCE_FLAG_DROPPED, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + } + else + battleground->SendBroadcastText(TEXT_HORDE_FLAG_DROPPED, CHAT_MSG_BG_SYSTEM_HORDE, player); + + player->CastSpell(player, recentlyDroppedSpellId, true); + break; + } + case FlagState::Taken: + { + if (team == HORDE) + { + battleground->SendBroadcastText(TEXT_HORDE_FLAG_PICKED_UP, CHAT_MSG_BG_SYSTEM_HORDE, player); + battleground->PlaySoundToAll(SOUND_HORDE_FLAG_PICKED_UP); + } + else + { + battleground->SendBroadcastText(TEXT_ALLIANCE_FLAG_PICKED_UP, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + battleground->PlaySoundToAll(SOUND_ALLIANCE_FLAG_PICKED_UP); + } + + if (GetFlagState(otherTeamId) == FlagState::Taken) + _bothFlagsKept = true; + + ApplyAssaultDebuffToPlayer(player); + + flagInBase->CastSpell(player, SPELL_QUICK_CAP_TIMER, true); + player->StartCriteria(CriteriaStartEvent::BeSpellTarget, SPELL_QUICK_CAP_TIMER, Seconds(GameTime::GetGameTime() - flagInBase->GetFlagTakenFromBaseTime())); + break; + } + case FlagState::Respawning: + ResetAssaultDebuff(); + break; + default: + break; + } + } + + bool CanCaptureFlag(AreaTrigger* areaTrigger, Player* player) override + { + if (battleground->GetStatus() != STATUS_IN_PROGRESS) + return false; + + Team const team = battleground->GetPlayerTeam(player->GetGUID()); + TeamId const teamId = Battleground::GetTeamIndexByTeamId(team); + TeamId const otherTeamId = Battleground::GetTeamIndexByTeamId(GetOtherTeam(team)); + + if (areaTrigger->GetGUID() != _capturePointAreaTriggers[teamId]) + return false; + + // check if enemy flag's carrier is this player + if (GetFlagCarrierGUID(otherTeamId) != player->GetGUID()) + return false; + + // check that team's flag is in base + return GetFlagState(teamId) == FlagState::InBase; + } + + void OnCaptureFlag(AreaTrigger* areaTrigger, Player* player) override + { + BattlegroundScript::OnCaptureFlag(areaTrigger, player); + + Team winner = TEAM_OTHER; + + Team const team = battleground->GetPlayerTeam(player->GetGUID()); + TeamId const teamId = Battleground::GetTeamIndexByTeamId(team); + TeamId const otherTeamId = Battleground::GetTeamIndexByTeamId(GetOtherTeam(team)); + + /* + 1. Update flag states & score world states + 2. update points + 3. chat message & sound + 4. update criterias & achievements + 5. remove all related auras + ?. Reward honor & reputation + */ + + // 1. update the flag states + for (ObjectGuid const& flagGuid: _flags) + if (GameObject const* flag = battlegroundMap->GetGameObject(flagGuid)) + flag->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::Respawning, player)); + + // 2. update points + if (battleground->GetTeamScore(teamId) < MAX_TEAM_SCORE) + battleground->AddPoint(team, 1); + + UpdateTeamScore(teamId); + + // 3. chat message & sound + if (team == ALLIANCE) + { + battleground->SendBroadcastText(TEXT_CAPTURED_HORDE_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, player); + battleground->PlaySoundToAll(SOUND_FLAG_CAPTURED_ALLIANCE); + battleground->RewardReputationToTeam(890, _reputationCapture, ALLIANCE); + player->CastSpell(player, SPELL_CAPTURED_ALLIANCE_COSMETIC_FX); + } + else + { + battleground->SendBroadcastText(TEXT_CAPTURED_ALLIANCE_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + battleground->PlaySoundToAll(SOUND_FLAG_CAPTURED_HORDE); + battleground->RewardReputationToTeam(889, _reputationCapture, HORDE); + player->CastSpell(player, SPELL_CAPTURED_HORDE_COSMETIC_FX); + } + + // 4. update criteria's for achievement, player score etc. + battleground->UpdatePvpStat(player, PVP_STAT_FLAG_CAPTURES, 1); // +1 flag captures + + // 5. Remove all related auras + RemoveAssaultDebuffFromPlayer(player); + + if (GameObject const* flag = battlegroundMap->GetGameObject(_flags[otherTeamId])) + player->RemoveAurasDueToSpell(flag->GetGOInfo()->newflag.pickupSpell, flag->GetGUID()); + + player->RemoveAurasDueToSpell(SPELL_QUICK_CAP_TIMER); + + player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::PvPActive); + + battleground->RewardHonorToTeam(battleground->GetBonusHonorFromKill(2), team); + + // update last flag capture to be used if teamscore is equal + SetLastFlagCapture(team); + + if (battleground->GetTeamScore(teamId) == MAX_TEAM_SCORE) + winner = team; + + if (winner) + { + UpdateWorldState(WORLD_STATE_FLAG_STATE_ALLIANCE, 1); + UpdateWorldState(WORLD_STATE_FLAG_STATE_HORDE, 1); + UpdateWorldState(WORLD_STATE_STATE_TIMER_ACTIVE, 0); + + battleground->RewardHonorToTeam(HONOR_REWARDS[BattlegroundMgr::IsBGWeekend(battleground->GetTypeID()) ? 1 : 0][0], winner); + battleground->EndBattleground(winner); + } + } + + Team GetPrematureWinner() override + { + if (battleground->GetTeamScore(TEAM_ALLIANCE) > battleground->GetTeamScore(TEAM_HORDE)) + return ALLIANCE; + if (battleground->GetTeamScore(TEAM_HORDE) > battleground->GetTeamScore(TEAM_ALLIANCE)) + return HORDE; + + return BattlegroundScript::GetPrematureWinner(); + } + + void SetLastFlagCapture(Team team) + { + _lastFlagCaptureTeam = team; + } + +private: + Team _lastFlagCaptureTeam; + bool _bothFlagsKept; + GuidSet _doors; + std::array<ObjectGuid, PVP_TEAMS_COUNT> _flags; + + TimeTracker _flagAssaultTimer; + uint8 _assaultStackCount; + std::array<ObjectGuid, PVP_TEAMS_COUNT> _capturePointAreaTriggers; + + uint32 _honorWinKills; + uint32 _honorEndKills; + uint32 _reputationCapture; +}; + +void AddSC_battleground_warsong_gulch() +{ + RegisterBattlegroundMapScript(battleground_warsong_gulch, 2106); +} diff --git a/src/server/scripts/Battlegrounds/battlegrounds_script_loader.cpp b/src/server/scripts/Battlegrounds/battlegrounds_script_loader.cpp new file mode 100644 index 00000000000..da8062fee38 --- /dev/null +++ b/src/server/scripts/Battlegrounds/battlegrounds_script_loader.cpp @@ -0,0 +1,95 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +// This is where scripts' loading functions should be declared: +// Alterac Valley +void AddSC_alterac_valley(); +void AddSC_boss_balinda(); +void AddSC_boss_drekthar(); +void AddSC_boss_galvangar(); +void AddSC_boss_vanndar(); +void AddSC_battleground_alterac_valley(); + +// Arathi Basin +void AddSC_arathi_basin(); +void AddSC_battleground_arathi_basin(); + +// Eye of the storm +void AddSC_battleground_eye_of_the_storm(); + +// Warsong Gulch +void AddSC_battleground_warsong_gulch(); + +// Isle of Conquest +void AddSC_battleground_isle_of_conquest(); +void AddSC_isle_of_conquest(); +void AddSC_boss_ioc_horde_alliance(); + +// Strand of the Ancients +void AddSC_battleground_strand_of_the_ancients(); +void AddSC_strand_of_the_ancients(); + +// Blade's Edge Arena +void AddSC_arena_blades_edge(); +void AddSC_arena_dalaran_sewers(); +void AddSC_arena_nagrand(); +void AddSC_arena_ruins_of_lordaeron(); +void AddSC_arena_ring_of_valor(); + +void AddSC_battleground_twin_peaks(); +void AddSC_battleground_battle_for_gilneas(); + +// The name of this function should match: +// void Add${NameOfDirectory}Scripts() +void AddBattlegroundsScripts() +{ + // Alterac Valley + AddSC_alterac_valley(); + AddSC_boss_balinda(); + AddSC_boss_drekthar(); + AddSC_boss_galvangar(); + AddSC_boss_vanndar(); + AddSC_battleground_alterac_valley(); + + // Arathi Basin + AddSC_arathi_basin(); + AddSC_battleground_arathi_basin(); + + // Eye of the Storm + AddSC_battleground_eye_of_the_storm(); + + // Warsong Gulch + AddSC_battleground_warsong_gulch(); + + // Isle of Conquest + AddSC_battleground_isle_of_conquest(); + AddSC_isle_of_conquest(); + AddSC_boss_ioc_horde_alliance(); + + // Strand of the Ancients + AddSC_battleground_strand_of_the_ancients(); + AddSC_strand_of_the_ancients(); + + AddSC_arena_blades_edge(); + AddSC_arena_dalaran_sewers(); + AddSC_arena_nagrand(); + AddSC_arena_ruins_of_lordaeron(); + AddSC_arena_ring_of_valor(); + + AddSC_battleground_twin_peaks(); + AddSC_battleground_battle_for_gilneas(); +} diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index d03dd7b96cb..7f4468dbdb9 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -1418,15 +1418,11 @@ public: if (linked) { - if (Battleground* bg = player->GetBattleground()) - nearestLoc = bg->GetClosestGraveyard(player); + + if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetMap(), player->GetZoneId())) + nearestLoc = bf->GetClosestGraveyard(player); else - { - if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetMap(), player->GetZoneId())) - nearestLoc = bf->GetClosestGraveyard(player); - else - nearestLoc = sObjectMgr->GetClosestGraveyard(*player, player->GetTeam(), player); - } + nearestLoc = sObjectMgr->GetClosestGraveyard(*player, player->GetTeam(), player); } else { diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index 673f1e9bfad..b818c1e6f10 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -16,12 +16,6 @@ */ // This is where scripts' loading functions should be declared: -void AddSC_alterac_valley(); //Alterac Valley -void AddSC_boss_balinda(); -void AddSC_boss_drekthar(); -void AddSC_boss_galvangar(); -void AddSC_boss_vanndar(); -void AddSC_arathi_basin(); //Arathi Basin void AddSC_boss_alizabal(); //Baradin Hold void AddSC_boss_occuthar(); void AddSC_boss_argaloth(); @@ -211,12 +205,6 @@ void AddSC_undercity(); // void Add${NameOfDirectory}Scripts() void AddEasternKingdomsScripts() { - AddSC_alterac_valley(); //Alterac Valley - AddSC_boss_balinda(); - AddSC_boss_drekthar(); - AddSC_boss_galvangar(); - AddSC_boss_vanndar(); - AddSC_arathi_basin(); //Arathi Basin AddSC_boss_alizabal(); //Baradin Hold AddSC_boss_occuthar(); AddSC_boss_argaloth(); diff --git a/src/server/scripts/Northrend/northrend_script_loader.cpp b/src/server/scripts/Northrend/northrend_script_loader.cpp index fe2514d804e..639aa223fa8 100644 --- a/src/server/scripts/Northrend/northrend_script_loader.cpp +++ b/src/server/scripts/Northrend/northrend_script_loader.cpp @@ -194,9 +194,6 @@ void AddSC_boss_baltharus_the_warborn(); void AddSC_boss_saviana_ragefire(); void AddSC_boss_general_zarithrian(); void AddSC_boss_halion(); -// Isle of Conquest -void AddSC_isle_of_conquest(); -void AddSC_boss_ioc_horde_alliance(); void AddSC_dalaran(); void AddSC_borean_tundra(); @@ -210,8 +207,6 @@ void AddSC_wintergrasp(); void AddSC_zuldrak(); void AddSC_crystalsong_forest(); -void AddSC_strand_of_the_ancients(); - // The name of this function should match: // void Add${NameOfDirectory}Scripts() void AddNorthrendScripts() @@ -394,9 +389,6 @@ void AddNorthrendScripts() AddSC_boss_saviana_ragefire(); AddSC_boss_general_zarithrian(); AddSC_boss_halion(); - // Isle of Conquest - AddSC_isle_of_conquest(); - AddSC_boss_ioc_horde_alliance(); AddSC_dalaran(); AddSC_borean_tundra(); @@ -408,6 +400,4 @@ void AddNorthrendScripts() AddSC_storm_peaks(); AddSC_wintergrasp(); AddSC_zuldrak(); - - AddSC_strand_of_the_ancients(); } diff --git a/src/server/scripts/Northrend/zone_dalaran.cpp b/src/server/scripts/Northrend/zone_dalaran.cpp index 46460521b6b..92fc14b2c64 100644 --- a/src/server/scripts/Northrend/zone_dalaran.cpp +++ b/src/server/scripts/Northrend/zone_dalaran.cpp @@ -24,7 +24,6 @@ SDCategory: Dalaran Script Data End */ #include "ScriptMgr.h" -#include "BattlegroundDS.h" #include "Containers.h" #include "DatabaseEnv.h" #include "Mail.h" @@ -254,44 +253,8 @@ private: EventMap events; }; -class at_ds_pipe_knockback : public AreaTriggerScript -{ -public: - at_ds_pipe_knockback() : AreaTriggerScript("at_ds_pipe_knockback") { } - - void Trigger(Player* player) const - { - if (Battleground* battleground = player->GetBattleground()) - { - if (battleground->GetStatus() != STATUS_IN_PROGRESS) - return; - - // Remove effects of Demonic Circle Summon - player->RemoveAurasDueToSpell(SPELL_WARL_DEMONIC_CIRCLE); - - // Someone has get back into the pipes and the knockback has already been performed, - // so we reset the knockback count for kicking the player again into the arena. - if (battleground->GetData(BG_DS_DATA_PIPE_KNOCKBACK_COUNT) >= BG_DS_PIPE_KNOCKBACK_TOTAL_COUNT) - battleground->SetData(BG_DS_DATA_PIPE_KNOCKBACK_COUNT, 0); - } - } - - bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override - { - Trigger(player); - return true; - } - - bool OnExit(Player* player, AreaTriggerEntry const* /*trigger*/) override - { - Trigger(player); - return true; - } -}; - void AddSC_dalaran() { RegisterCreatureAI(npc_mageguard_dalaran); RegisterCreatureAI(npc_minigob_manabonk); - new at_ds_pipe_knockback(); } diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 828523e0bf3..f0c1e460815 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -3081,9 +3081,6 @@ class spell_item_rocket_boots : public SpellScript void HandleDummy(SpellEffIndex /* effIndex */) { Player* caster = GetCaster()->ToPlayer(); - if (Battleground* bg = caster->GetBattleground()) - bg->EventPlayerDroppedFlag(caster); - caster->GetSpellHistory()->ResetCooldown(SPELL_ROCKET_BOOTS_PROC); caster->CastSpell(caster, SPELL_ROCKET_BOOTS_PROC, true); } |