diff options
author | Jeremy <Golrag@users.noreply.github.com> | 2024-04-16 19:14:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-16 19:14:11 +0200 |
commit | 091094aa1390b8619e63f555ccbe7f9520727fac (patch) | |
tree | 5bf578d949e3a0cf9f1837b19c5d4bf194148e2a /src | |
parent | c63e0565c4b6aba737ede7119f8e1e45351d2301 (diff) |
Scripts/Battlegrounds: Implement Twin Peaks (#29924)
Closes #8894
Diffstat (limited to 'src')
-rw-r--r-- | src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp | 547 |
1 files changed, 542 insertions, 5 deletions
diff --git a/src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp b/src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp index 73f8db98aab..0a0b37e4a16 100644 --- a/src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp +++ b/src/server/scripts/Battlegrounds/TwinPeaks/battleground_twin_peaks.cpp @@ -15,18 +15,555 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "AreaTrigger.h" +#include "Battleground.h" #include "BattlegroundScript.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" + +namespace TwinPeaks +{ + namespace AreaTriggers + { + static constexpr uint32 CapturePointAlliance = 30; + static constexpr uint32 CapturePointHorde = 31; + } + + namespace Events + { + static constexpr uint32 StartBattle = 35912; + } + + namespace GameObjects + { + static constexpr uint32 GhostGate = 180322; + static constexpr uint32 Gate1 = 208205; + static constexpr uint32 Gate2 = 208207; + static constexpr uint32 Gate3 = 208206; + static constexpr uint32 Portcullis = 203710; + static constexpr uint32 HordeFlag = 227740;; + static constexpr uint32 AllianceFlag = 227741; + static constexpr uint32 WildHammerGate1 = 206653; + static constexpr uint32 WildHammerGate2 = 206654; + static constexpr uint32 WildHammerGate3 = 206655; + } + + namespace Misc + { + static constexpr uint8 MaxTeamScore = 3; + static constexpr uint8 FlagBrutalAssaultStackCount = 5; + } + + namespace PvpStats + { + static constexpr uint32 FlagCaptures = 290; + static constexpr uint32 FlagReturns = 291; + } + + namespace Sounds + { + static constexpr uint32 PvpFlagCapturedAlliance = 8173; + static constexpr uint32 PvpFlagCapturedHorde = 8213; + static constexpr uint32 FlagReturned = 8192; + static constexpr uint32 PvpFlagTakenHorde = 8212; + static constexpr uint32 PvpFlagTakenAlliance = 8174; + static constexpr uint32 FlagsRespawned = 8232; + } + + namespace Spells + { + static constexpr uint32 FocusedAssault = 46392; + static constexpr uint32 BrutalAssault = 46393; + static constexpr uint32 QuickCapTimer = 183317; + static constexpr uint32 CapturedAllianceCosmeticFx = 262508; + static constexpr uint32 CapturedHordeCosmeticFx = 262512; + } + + namespace Texts + { + static constexpr uint32 CapturedHordeFlag = 9801; + static constexpr uint32 CapturedAllianceFlag = 9802; + static constexpr uint32 FlagsPlaced = 9803; + static constexpr uint32 AllianceFlagPickedUp = 9804; + static constexpr uint32 AllianceFlagDropped = 9805; + static constexpr uint32 HordeFlagPickedUp = 9807; + static constexpr uint32 HordeFlagDropped = 9806; + static constexpr uint32 AllianceFlagReturned = 9808; + static constexpr uint32 HordeFlagReturned = 9809; + } + + namespace Timers + { + static constexpr Seconds FlagAssaultTimer = 30s; + } + + namespace WorldStates + { + static constexpr uint32 FlagStateAlliance = 1545; + static constexpr uint32 FlagStateHorde = 1546; + static constexpr uint32 FlagCapturesAlliance = 1581; + static constexpr uint32 FlagCapturesHorde = 1582; + static constexpr uint32 FlagControlHorde = 2338; + static constexpr uint32 FlagControlAlliance = 2339; + static constexpr uint32 Timer = 4248; + static constexpr uint32 TimerActive = 4247; + static constexpr uint32 DoubleJeopardyAllianceEnabled = 5746; + static constexpr uint32 DoubleJeopardyHordeEnabled = 5747; + } +} struct battleground_twin_peaks : BattlegroundScript { - enum PvpStats : uint32 + explicit battleground_twin_peaks(BattlegroundMap* map) : BattlegroundScript(map), _lastFlagCaptureTeam(TEAM_OTHER), _bothFlagsKept(false), _flags({ }), _assaultStackCount(0), _capturePointAreaTriggers({ }) + { + _flagAssaultTimer.Reset(TwinPeaks::Timers::FlagAssaultTimer); + } + + 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(TwinPeaks::Timers::FlagAssaultTimer); + 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(TwinPeaks::WorldStates::TimerActive, 1); + UpdateWorldState(TwinPeaks::WorldStates::Timer, std::chrono::system_clock::to_time_t(GameTime::GetSystemTime() + 15min)); + + // players joining later are not eligibles + TriggerGameEvent(TwinPeaks::Events::StartBattle); + } + + 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(TwinPeaks::Timers::FlagAssaultTimer); + DoForFlagKeepers([&](Player* player) -> void + { + RemoveAssaultDebuffFromPlayer(player); + }); + } + + void ApplyAssaultDebuffToPlayer(Player* player) const + { + if (_assaultStackCount == 0) + return; + + uint32 spellId = TwinPeaks::Spells::FocusedAssault; + if (_assaultStackCount >= TwinPeaks::Misc::FlagBrutalAssaultStackCount) + { + player->RemoveAurasDueToSpell(TwinPeaks::Spells::FocusedAssault); + spellId = TwinPeaks::Spells::BrutalAssault; + } + + 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(TwinPeaks::Spells::FocusedAssault); + player->RemoveAurasDueToSpell(TwinPeaks::Spells::BrutalAssault); + } + + 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 { - BG_TP_FLAG_CAPTURES = 290, - BG_TP_FLAG_RETURNS = 291 - }; + 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(TwinPeaks::WorldStates::FlagStateAlliance, AsUnderlyingType(value)); + UpdateWorldState(TwinPeaks::WorldStates::FlagControlHorde, transformValueToOtherTeamControlWorldState(value)); + } + else + { + UpdateWorldState(TwinPeaks::WorldStates::FlagStateHorde, AsUnderlyingType(value)); + UpdateWorldState(TwinPeaks::WorldStates::FlagControlAlliance, transformValueToOtherTeamControlWorldState(value)); + } + } + + void UpdateTeamScore(TeamId team) const + { + if (team == TEAM_ALLIANCE) + UpdateWorldState(TwinPeaks::WorldStates::FlagCapturesAlliance, battleground->GetTeamScore(team)); + else + UpdateWorldState(TwinPeaks::WorldStates::FlagCapturesHorde, battleground->GetTeamScore(team)); + + TeamId const otherTeam = GetOtherTeam(team); + uint32 const teamScore = battleground->GetTeamScore(team); + uint32 const otherTeamScore = battleground->GetTeamScore(otherTeam); + if (teamScore == 2 && otherTeamScore == 0) + UpdateWorldState(team == TEAM_ALLIANCE ? TwinPeaks::WorldStates::DoubleJeopardyHordeEnabled : TwinPeaks::WorldStates::DoubleJeopardyAllianceEnabled, 1, true); + } + + void OnGameObjectCreate(GameObject* gameObject) override + { + BattlegroundScript::OnGameObjectCreate(gameObject); + switch (gameObject->GetEntry()) + { + case TwinPeaks::GameObjects::Gate1: + case TwinPeaks::GameObjects::Gate2: + case TwinPeaks::GameObjects::Gate3: + case TwinPeaks::GameObjects::GhostGate: + case TwinPeaks::GameObjects::Portcullis: + case TwinPeaks::GameObjects::WildHammerGate1: + case TwinPeaks::GameObjects::WildHammerGate2: + case TwinPeaks::GameObjects::WildHammerGate3: + _doors.insert(gameObject->GetGUID()); + break; + case TwinPeaks::GameObjects::AllianceFlag: + _flags[TEAM_ALLIANCE] = gameObject->GetGUID(); + break; + case TwinPeaks::GameObjects::HordeFlag: + _flags[TEAM_HORDE] = gameObject->GetGUID(); + break; + default: + break; + } + } + + void OnAreaTriggerCreate(AreaTrigger* areaTrigger) override + { + BattlegroundScript::OnAreaTriggerCreate(areaTrigger); + if (!areaTrigger->IsStaticSpawn()) + return; + + switch (areaTrigger->GetEntry()) + { + case TwinPeaks::AreaTriggers::CapturePointAlliance: + _capturePointAreaTriggers[TEAM_ALLIANCE] = areaTrigger->GetGUID(); + break; + case TwinPeaks::AreaTriggers::CapturePointHorde: + _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() == TwinPeaks::GameObjects::HordeFlag ? 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, TwinPeaks::PvpStats::FlagReturns, 1); // +1 flag returns + + if (team == ALLIANCE) + { + battleground->SendBroadcastText(TwinPeaks::Texts::AllianceFlagReturned, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + battleground->PlaySoundToAll(TwinPeaks::Sounds::FlagReturned); + } + else + { + battleground->SendBroadcastText(TwinPeaks::Texts::HordeFlagReturned, CHAT_MSG_BG_SYSTEM_HORDE, player); + battleground->PlaySoundToAll(TwinPeaks::Sounds::FlagReturned); + } + } + // 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(TwinPeaks::Texts::FlagsPlaced, CHAT_MSG_BG_SYSTEM_NEUTRAL); + battleground->PlaySoundToAll(TwinPeaks::Sounds::FlagsRespawned); + } + + HandleFlagRoomCapturePoint(); + } + break; + } + case FlagState::Dropped: + { + player->RemoveAurasDueToSpell(TwinPeaks::Spells::QuickCapTimer); + RemoveAssaultDebuffFromPlayer(player); + + uint32 recentlyDroppedSpellId = SPELL_RECENTLY_DROPPED_HORDE_FLAG; + if (team == ALLIANCE) + { + recentlyDroppedSpellId = SPELL_RECENTLY_DROPPED_ALLIANCE_FLAG; + battleground->SendBroadcastText(TwinPeaks::Texts::AllianceFlagDropped, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + } + else + battleground->SendBroadcastText(TwinPeaks::Texts::HordeFlagDropped, CHAT_MSG_BG_SYSTEM_HORDE, player); + + player->CastSpell(player, recentlyDroppedSpellId, true); + break; + } + case FlagState::Taken: + { + if (team == HORDE) + { + battleground->SendBroadcastText(TwinPeaks::Texts::HordeFlagPickedUp, CHAT_MSG_BG_SYSTEM_HORDE, player); + battleground->PlaySoundToAll(TwinPeaks::Sounds::PvpFlagTakenHorde); + } + else + { + battleground->SendBroadcastText(TwinPeaks::Texts::AllianceFlagPickedUp, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + battleground->PlaySoundToAll(TwinPeaks::Sounds::PvpFlagTakenAlliance); + } + + if (GetFlagState(otherTeamId) == FlagState::Taken) + _bothFlagsKept = true; + + ApplyAssaultDebuffToPlayer(player); + + flagInBase->CastSpell(player, TwinPeaks::Spells::QuickCapTimer, true); + player->StartCriteria(CriteriaStartEvent::BeSpellTarget, TwinPeaks::Spells::QuickCapTimer, 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) < TwinPeaks::Misc::MaxTeamScore) + battleground->AddPoint(team, 1); + + UpdateTeamScore(teamId); + + // 3. chat message & sound + if (team == ALLIANCE) + { + battleground->SendBroadcastText(TwinPeaks::Texts::CapturedHordeFlag, CHAT_MSG_BG_SYSTEM_HORDE, player); + battleground->PlaySoundToAll(TwinPeaks::Sounds::PvpFlagCapturedAlliance); + player->CastSpell(player, TwinPeaks::Spells::CapturedAllianceCosmeticFx); + } + else + { + battleground->SendBroadcastText(TwinPeaks::Texts::CapturedAllianceFlag, CHAT_MSG_BG_SYSTEM_ALLIANCE, player); + battleground->PlaySoundToAll(TwinPeaks::Sounds::PvpFlagCapturedHorde); + player->CastSpell(player, TwinPeaks::Spells::CapturedHordeCosmeticFx); + } + + // 4. update criteria's for achievement, player score etc. + battleground->UpdatePvpStat(player, TwinPeaks::PvpStats::FlagCaptures, 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(TwinPeaks::Spells::QuickCapTimer); + + 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) == TwinPeaks::Misc::MaxTeamScore) + winner = team; + + if (winner) + { + UpdateWorldState(TwinPeaks::WorldStates::FlagStateAlliance, 1); + UpdateWorldState(TwinPeaks::WorldStates::FlagStateHorde, 1); + UpdateWorldState(TwinPeaks::WorldStates::TimerActive, 0); + + 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; - explicit battleground_twin_peaks(BattlegroundMap* map) : BattlegroundScript(map) { } + TimeTracker _flagAssaultTimer; + uint8 _assaultStackCount; + std::array<ObjectGuid, PVP_TEAMS_COUNT> _capturePointAreaTriggers; }; void AddSC_battleground_twin_peaks() |