Scripts/Battlegrounds: Implement Seething Shore (#29996)

This commit is contained in:
Jeremy
2024-07-10 19:44:23 +02:00
committed by GitHub
parent 1cd7898c01
commit 7c361c7f96
3 changed files with 1607 additions and 0 deletions

View File

@@ -0,0 +1,941 @@
/*
* 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 "AreaTriggerAI.h"
#include "Battleground.h"
#include "BattlegroundPackets.h"
#include "BattlegroundScript.h"
#include "Containers.h"
#include "CreatureAI.h"
#include "G3DPosition.hpp"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "Map.h"
#include "MotionMaster.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "Spell.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "SpellHistory.h"
#include "TaskScheduler.h"
#include "TemporarySummon.h"
#include "WaypointDefines.h"
#include <unordered_map>
namespace SeethingShore
{
namespace Actions
{
static constexpr uint32 CaptureAzeriteNode = 1;
static constexpr uint32 ConsumeBuff = 2;
static constexpr uint32 SpawnBuff = 3;
static constexpr uint32 CommanderText1 = 4;
}
namespace Areas
{
static constexpr uint32 Shipwreck = 9511;
static constexpr uint32 Waterfall = 9512;
static constexpr uint32 CrashSite = 9513;
static constexpr uint32 Overlook = 9514;
static constexpr uint32 TarPits = 9515;
static constexpr uint32 Ruins = 9516;
static constexpr uint32 Tower = 9517;
static constexpr uint32 Plunge = 9518;
static constexpr uint32 TidePools = 9519;
static constexpr uint32 Bonfire = 9520;
static constexpr uint32 Ridge = 9521;
static constexpr uint32 Temple = 9522;
static constexpr std::array<uint32, 12> AzeriteNodes =
{
Ruins, Ridge, Plunge, Shipwreck, CrashSite, TidePools, Tower, Bonfire, TarPits, Temple, Overlook, Waterfall
};
}
namespace Creatures
{
static constexpr uint32 Controller = 125269;
static constexpr uint32 AirSupplies = 133532;
static constexpr uint32 AirSupplyGroundDummy = 133542;
static constexpr uint32 NathanosBlightCaller = 131773;
static constexpr uint32 MathiasShaw = 130532;
static constexpr uint32 AzeriteFissure = 125253;
}
namespace CommanderTexts
{
static constexpr uint8 Intro1 = 0;
static constexpr uint8 Intro2 = 1;
static constexpr uint8 Intro3 = 2;
static constexpr uint8 Intro4 = 3;
static constexpr uint8 SuppliesSpawned = 4;
static constexpr uint8 CapturedAzerite = 5;
}
namespace GameObjects
{
static constexpr uint32 AllianceAirshipPrepCollision = 281224;
static constexpr uint32 HordeAirshipPrepCollision = 281226;
static constexpr uint32 BerserkBuff = 206564;
static constexpr uint32 FoodBuff = 206565;
static constexpr uint32 SpeedBuff = 206566;
static constexpr std::array<uint32, 3> Buffs = { BerserkBuff, FoodBuff, SpeedBuff };
}
namespace PvpStats
{
static constexpr uint32 CapturedAzerite = 744;
}
namespace Sounds
{
static constexpr uint32 PvpFlagCapturedAlliance = 8173;
static constexpr uint32 PvpFlagCapturedHorde = 8213;
}
namespace Spells
{
static constexpr uint32 ActivateAzerite = 248688;
static constexpr uint32 AzeriteGeyser = 248668;
static constexpr uint32 SummonAzeriteCaptureNode1 = 262749;
static constexpr uint32 SummonAzeriteCaptureNode2 = 248674;
static constexpr uint32 SummonAzeriteCaptureNode3 = 262748;
static constexpr uint32 AzeriteKnockBack = 262385;
static constexpr uint32 EarthQuakeCameraShake = 248719;
static constexpr uint32 CapturedAllianceCosmeticFx = 262508;
static constexpr uint32 CapturedHordeCosmeticFx = 262512;
static constexpr uint32 AchievementCredit = 261968; // on capture azerite node
static constexpr uint32 AchievementTrackerDeathFromAbove = 261954; // Death from Above achievement
static constexpr uint32 AchievementCreditClaimJumper = 261953; // to cast when capturing an azerite node when less than 30s on ground
static constexpr uint32 AchievementTrackerCreditClaimJumper = 261955;
static constexpr uint32 RocketParachute = 250917;
static constexpr uint32 RocketParachute2 = 250921;
static constexpr uint32 NoFallingDamage = 262359;
static constexpr uint32 Parachute = 269779;
static constexpr std::array<uint32, 3> SummonAzeriteCaptureNodeSpells = { SummonAzeriteCaptureNode1, SummonAzeriteCaptureNode2, SummonAzeriteCaptureNode3 };
static constexpr uint32 RopeBeam = 115048;
static constexpr uint32 PingVehicle = 115050;
static constexpr uint32 CreateHasteRuneBuffAreaTrigger = 295663;
static constexpr uint32 DustCloudImpactBigger = 54740;
static constexpr uint32 SpeedUp = 294701;
}
namespace SpellVisuals
{
static constexpr uint32 HasteRuneBuff = 85696;
static constexpr uint32 AzeriteBirth = 74145;
}
namespace StringIds
{
static constexpr std::string_view AirSupplyGroundDummy1 = "air_supply_ground_dummy_1";
static constexpr std::string_view AirSupplyGroundDummy2 = "air_supply_ground_dummy_2";
static constexpr std::string_view AirSupplyGroundDummy3 = "air_supply_ground_dummy_3";
static constexpr std::string_view AirSupplyGroundDummy4 = "air_supply_ground_dummy_4";
static constexpr std::string_view AirSupplyGroundDummy5 = "air_supply_ground_dummy_5";
static constexpr std::string_view AirSupplyGroundDummy6 = "air_supply_ground_dummy_6";
static constexpr std::array<std::string_view, 6> AirSupplyGroundDummies = { AirSupplyGroundDummy1, AirSupplyGroundDummy2, AirSupplyGroundDummy3, AirSupplyGroundDummy4, AirSupplyGroundDummy5, AirSupplyGroundDummy6 };
}
namespace WorldStates
{
static constexpr uint32 AllianceScore = 13845;
static constexpr uint32 HordeScore = 13844;
static constexpr uint32 MaxScore = 13846;
}
namespace Texts
{
static constexpr uint32 AzeriteSpawning = 134097;
static constexpr uint32 AllianceNearVictory = 10598;
static constexpr uint32 HordeNearVictory = 10599;
}
}
struct battleground_seething_shore final : BattlegroundScript
{
static constexpr uint32 WARNING_NEAR_VICTORY_SCORE = 1200;
static constexpr uint8 MAX_AZERITE_SPAWNS = 3;
static constexpr uint8 MAX_BUFF_SPAWNS = 3;
explicit battleground_seething_shore(BattlegroundMap* map) : BattlegroundScript(map), _firstSetDone(false)
{
_isInformedNearVictory = { false, false };
_commanderGUIDs = { };
}
void OnUpdate(uint32 diff) override
{
_scheduler.Update(diff);
}
void OnPrepareStage2() override
{
for (ObjectGuid const& guid : _commanderGUIDs)
if (Creature const* creature = battlegroundMap->GetCreature(guid))
creature->AI()->DoAction(SeethingShore::Actions::CommanderText1);
}
void OnPrepareStage3() override
{
_scheduler.Schedule(2s, [this](TaskContext context)
{
if (_activeAzeriteNodes.size() < MAX_AZERITE_SPAWNS)
{
SpawnAzeriteNodes();
context.Repeat(100ms);
}
else
context.Repeat(2s);
});
}
void OnStart() override
{
for (ObjectGuid const& guid : _doors)
{
if (GameObject* gameObject = battlegroundMap->GetGameObject(guid))
{
gameObject->UseDoorOrButton();
gameObject->DespawnOrUnsummon(3s);
}
}
_scheduler.Schedule(30s, 2min, [this](TaskContext context)
{
SpawnBuffs();
context.Repeat();
});
}
void HandleAssaultPoint(GameObject* capturePoint, Player* player)
{
if (!player)
return;
capturePoint->SendCustomAnim(1);
Team const team = battleground->GetPlayerTeam(player->GetGUID());
TeamId const teamId = GetTeamIdForTeam(team);
if (team == ALLIANCE)
{
battleground->PlaySoundToAll(SeethingShore::Sounds::PvpFlagCapturedAlliance);
player->CastSpell(player, SeethingShore::Spells::CapturedAllianceCosmeticFx, true);
}
else if (team == HORDE)
{
battleground->PlaySoundToAll(SeethingShore::Sounds::PvpFlagCapturedHorde);
player->CastSpell(player, SeethingShore::Spells::CapturedHordeCosmeticFx, true);
}
battleground->AddPoint(team, 100);
UpdateWorldState(SeethingShore::WorldStates::AllianceScore, battleground->GetTeamScore(TEAM_ALLIANCE));
UpdateWorldState(SeethingShore::WorldStates::HordeScore, battleground->GetTeamScore(TEAM_HORDE));
player->CastSpell(player, SeethingShore::Spells::AchievementCredit, true);
// the achievement criteria doesn't have a aura condition so we do it manually here
if (player->HasAura(SeethingShore::Spells::AchievementTrackerCreditClaimJumper))
player->CastSpell(player, SeethingShore::Spells::AchievementCreditClaimJumper, true);
if (battleground->GetTeamScore(teamId) >= WARNING_NEAR_VICTORY_SCORE && _isInformedNearVictory[teamId])
{
_isInformedNearVictory[teamId] = true;
if (teamId == TEAM_ALLIANCE)
battleground->SendBroadcastText(SeethingShore::Texts::AllianceNearVictory, CHAT_MSG_BG_SYSTEM_NEUTRAL);
else
battleground->SendBroadcastText(SeethingShore::Texts::HordeNearVictory, CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
battleground->UpdatePvpStat(player, SeethingShore::PvpStats::CapturedAzerite, 1);
capturePoint->SetAnimKitId(2560, false);
capturePoint->DespawnOrUnsummon(2s);
_activeAzeriteNodes.erase(capturePoint->GetAreaId());
if (Creature const* commander = battlegroundMap->GetCreature(_commanderGUIDs[teamId]))
commander->AI()->DoAction(SeethingShore::Actions::CaptureAzeriteNode);
if (team == HORDE && battleground->GetTeamScore(TEAM_HORDE) >= static_cast<uint32>(battlegroundMap->GetWorldStateValue(SeethingShore::WorldStates::MaxScore)))
battleground->EndBattleground(HORDE);
else if (team == ALLIANCE && battleground->GetTeamScore(TEAM_ALLIANCE) >= static_cast<uint32>(battlegroundMap->GetWorldStateValue(SeethingShore::WorldStates::MaxScore)))
battleground->EndBattleground(ALLIANCE);
}
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
{
case SeethingShore::Creatures::Controller:
_controllerGUID = creature->GetGUID();
break;
case SeethingShore::Creatures::AirSupplyGroundDummy:
_airSupplyGroundDummyGUIDs.emplace_back(creature->GetGUID());
break;
case SeethingShore::Creatures::NathanosBlightCaller:
_commanderGUIDs[TEAM_HORDE] = creature->GetGUID();
break;
case SeethingShore::Creatures::MathiasShaw:
_commanderGUIDs[TEAM_ALLIANCE] = creature->GetGUID();
break;
default:
break;
}
}
void OnGameObjectCreate(GameObject* gameobject) override
{
switch (gameobject->GetEntry())
{
case SeethingShore::GameObjects::AllianceAirshipPrepCollision:
case SeethingShore::GameObjects::HordeAirshipPrepCollision:
_doors.emplace_back(gameobject->GetGUID());
break;
default:
break;
}
}
void OnGameObjectRemove(GameObject* gameobject) override
{
switch (gameobject->GetEntry())
{
case SeethingShore::GameObjects::BerserkBuff:
case SeethingShore::GameObjects::FoodBuff:
case SeethingShore::GameObjects::SpeedBuff:
DoAction(SeethingShore::Actions::ConsumeBuff, gameobject, nullptr);
break;
default:
break;
}
}
void DoAction(uint32 actionId, WorldObject* source, WorldObject* target) override
{
switch (actionId)
{
case SeethingShore::Actions::CaptureAzeriteNode:
HandleAssaultPoint(WorldObject::ToGameObject(target), WorldObject::ToPlayer(source));
break;
case SeethingShore::Actions::ConsumeBuff:
if (source)
if (Creature const* groundDummy = source->FindNearestCreature(SeethingShore::Creatures::AirSupplyGroundDummy, 20.0f))
_activeBuffs.erase(groundDummy->GetStringId(StringIdType::Spawn));
break;
default:
break;
}
}
void SpawnAzeriteNodes()
{
if (Creature* controller = battlegroundMap->GetCreature(_controllerGUID))
{
std::vector<uint32> targets = SelectAzeriteNodesToSpawn();
CastSpellExtraArgs const args = CastSpellExtraArgs(true)
.SetCustomArg(targets);
controller->CastSpell(nullptr, SeethingShore::Spells::ActivateAzerite, args);
_firstSetDone = true;
for (uint32 const node : targets)
_activeAzeriteNodes.insert(node);
}
battleground->SendBroadcastText(SeethingShore::Texts::AzeriteSpawning, CHAT_MSG_BG_SYSTEM_NEUTRAL);
battlegroundMap->DoOnPlayers([&](Player* player)
{
player->CastSpell(player, SeethingShore::Spells::EarthQuakeCameraShake, true);
});
}
std::vector<uint32> SelectAzeriteNodesToSpawn() const
{
if (!_firstSetDone)
{
return {
SeethingShore::Areas::Ruins,
SeethingShore::Areas::TarPits,
SeethingShore::Areas::Ridge
};
}
std::vector<uint32> selected;
std::ranges::copy_if(SeethingShore::Areas::AzeriteNodes.begin(), SeethingShore::Areas::AzeriteNodes.end(), std::back_inserter(selected), [this](uint32 node)
{
return !_activeAzeriteNodes.contains(node);
});
uint32 const maxSpawns = MAX_AZERITE_SPAWNS - _activeAzeriteNodes.size();
Trinity::Containers::RandomResize(selected, std::max<uint32>(maxSpawns, 0));
return selected;
}
void SpawnBuffs()
{
std::vector<std::string_view> toSpawn = SelectBuffsToSpawn();
if (toSpawn.empty())
return;
for (ObjectGuid const& guid : _commanderGUIDs)
if (Creature const* creature = battlegroundMap->GetCreature(guid))
creature->AI()->DoAction(SeethingShore::Actions::SpawnBuff);
for (ObjectGuid const& guid : _airSupplyGroundDummyGUIDs)
{
if (Creature* creature = battlegroundMap->GetCreature(guid))
{
bool const isValid = std::ranges::any_of(toSpawn.begin(), toSpawn.end(), [&](std::string_view stringId)
{
return creature->HasStringId(stringId);
});
if (!isValid)
continue;
_activeBuffs.insert(creature->GetStringId(StringIdType::Spawn));
creature->AI()->DoAction(SeethingShore::Actions::SpawnBuff);
}
}
}
std::vector<std::string_view> SelectBuffsToSpawn() const
{
std::vector<std::string_view> selected;
std::ranges::copy_if(SeethingShore::StringIds::AirSupplyGroundDummies.begin(), SeethingShore::StringIds::AirSupplyGroundDummies.end(), std::back_inserter(selected), [this](std::string_view buff)
{
return !_activeBuffs.contains(buff);
});
uint32 const maxSpawns = MAX_BUFF_SPAWNS - _activeBuffs.size();
Trinity::Containers::RandomResize(selected, std::max<uint32>(maxSpawns, 0));
return selected;
}
private:
bool _firstSetDone;
GuidVector _doors;
TaskScheduler _scheduler;
ObjectGuid _controllerGUID;
std::array<bool, PVP_TEAMS_COUNT> _isInformedNearVictory;
std::set<uint32> _activeAzeriteNodes;
std::set<std::string_view> _activeBuffs;
GuidVector _airSupplyGroundDummyGUIDs;
std::array<ObjectGuid, PVP_TEAMS_COUNT> _commanderGUIDs;
};
// 248688 - Activate Azerite
class spell_bg_seething_shore_activate_azerite : public SpellScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo(
{
SeethingShore::Spells::AzeriteGeyser,
SeethingShore::Spells::ActivateAzerite
});
}
void FilterTargets(std::list<WorldObject*>& targets) const
{
std::vector<uint32> const* nodesToActivate = std::any_cast<std::vector<uint32>>(&GetSpell()->m_customArg);
if (!nodesToActivate)
{
targets.clear();
return;
}
auto itr = targets.begin();
while (itr != targets.end())
{
Creature const* creature = (*itr)->ToCreature();
if (!creature)
{
targets.erase(itr++);
continue;
}
bool const shouldActivate = std::ranges::any_of(*nodesToActivate, [&creature](uint32 areaId)
{
return creature->GetAreaId() == areaId;
});
if (!shouldActivate)
targets.erase(itr++);
else
++itr;
}
}
void HandleDummy(SpellEffIndex /*effIndex*/) const
{
GetHitCreature()->CastSpell(GetHitUnit(), SeethingShore::Spells::AzeriteGeyser, true);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_bg_seething_shore_activate_azerite::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
OnEffectLaunchTarget += SpellEffectFn(spell_bg_seething_shore_activate_azerite::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
// 248668 - Azerite Geyser
class spell_bg_seething_shore_azerite_geyser : public AuraScript
{
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) const
{
if (Unit* unitOwner = GetUnitOwner())
{
unitOwner->CastSpell(unitOwner, Trinity::Containers::SelectRandomContainerElement(SeethingShore::Spells::SummonAzeriteCaptureNodeSpells), true);
unitOwner->CastSpell(unitOwner, SeethingShore::Spells::AzeriteKnockBack, true);
unitOwner->GetMotionMaster()->MoveTargetedHome();
}
}
void Register() override
{
OnEffectRemove += AuraEffectRemoveFn(spell_bg_seething_shore_azerite_geyser::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
// 250917 - Rocket Parachute
class spell_bg_seething_shore_rocket_parachute_trigger : public AuraScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo(
{
SeethingShore::Spells::RocketParachute2
});
}
void HandlePeriodic(AuraEffect const* /*auraEffect*/) const
{
CastSpellExtraArgs args;
args.TriggerFlags = TRIGGERED_FULL_MASK;
args.OriginalCastId = GetAura()->GetCastId();
GetTarget()->CastSpell(GetTarget(), SeethingShore::Spells::RocketParachute2, args);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_bg_seething_shore_rocket_parachute_trigger::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
}
};
// 250921 - Rocket Parachute
class spell_bg_seething_shore_rocket_parachute_ground_check : public AuraScript
{
public:
spell_bg_seething_shore_rocket_parachute_ground_check() : AuraScript(), _parachuteDeployed(false) { }
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo(
{
SeethingShore::Spells::Parachute
});
}
void HandlePeriodicTrigger(AuraEffect const* /*auraEffect*/)
{
if (_parachuteDeployed)
return;
Unit* target = GetTarget();
float const delta = target->GetPositionZ() - target->GetFloorZ();
if (delta <= 30.0f)
{
SpellCastResult const castResult = target->CastSpell(target, SeethingShore::Spells::Parachute, true);
_parachuteDeployed = castResult == SPELL_CAST_OK;
}
}
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) const
{
// this should prevent next tick from re-applying this aura
if (Unit* unitOwner = GetUnitOwner())
unitOwner->RemoveAurasDueToSpell(SeethingShore::Spells::RocketParachute);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_bg_seething_shore_rocket_parachute_ground_check::HandlePeriodicTrigger, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
OnEffectRemove += AuraEffectRemoveFn(spell_bg_seething_shore_rocket_parachute_ground_check::HandleRemove, EFFECT_0, SPELL_AURA_CAN_TURN_WHILE_FALLING, AURA_EFFECT_HANDLE_REAL);
}
private:
bool _parachuteDeployed;
};
// 269779 - Parachute
class spell_bg_seething_shore_parachute : public SpellScript
{
bool Validate(SpellInfo const* spellInfo) override
{
return ValidateSpellEffect({ { spellInfo->Id, EFFECT_0 } })
&& ValidateSpellInfo(
{
static_cast<uint32>(spellInfo->GetEffect(EFFECT_0).CalcValue()),
SeethingShore::Spells::AchievementTrackerCreditClaimJumper,
SeethingShore::Spells::AchievementTrackerDeathFromAbove
});
}
void ApplyParachuteAura(SpellEffIndex /*effIndex*/) const
{
if (Player* player = GetHitPlayer())
{
CastSpellExtraArgs args;
args.TriggerFlags = TRIGGERED_FULL_MASK;
args.OriginalCastId = GetSpell()->m_castId;
player->CastSpell(player, static_cast<uint32>(GetEffectInfo().CalcValue(player)), args);
player->CastSpell(player, SeethingShore::Spells::AchievementTrackerCreditClaimJumper, true);
player->CastSpell(player, SeethingShore::Spells::AchievementTrackerDeathFromAbove, true);
}
}
void ResetFallDamage(SpellEffIndex /*effIndex*/) const
{
if (Player* player = GetHitPlayer())
player->SetFallInformation(0, player->GetPositionZ());
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_bg_seething_shore_parachute::ApplyParachuteAura, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
OnEffectHitTarget += SpellEffectFn(spell_bg_seething_shore_parachute::ResetFallDamage, EFFECT_1, SPELL_EFFECT_KNOCK_BACK);
}
};
// 294701 - Speed Up
class spell_bg_seething_shore_speed_up : public SpellScript
{
void ResetCooldowns(SpellEffIndex /*effIndex*/) const
{
// Unsure about which spells should reset and which not
// It's not the same list as the ones that reset on encounter reset
if (Player* player = GetCaster()->ToPlayer())
player->GetSpellHistory()->ResetAllCooldowns();
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_bg_seething_shore_speed_up::ResetCooldowns, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 272471 - Azerite
// 281306 - Azerite
// 281307 - Azerite
struct go_bg_seething_shore_azerite : GameObjectAI
{
explicit go_bg_seething_shore_azerite(GameObject* go) : GameObjectAI(go) { }
bool OnCapturePointAssaulted(Player* player) override
{
if (me->GetGoState() != GO_STATE_READY || me->HasFlag(GO_FLAG_NOT_SELECTABLE))
return true;
if (ZoneScript* zoneScript = me->GetZoneScript())
{
zoneScript->DoAction(SeethingShore::Actions::CaptureAzeriteNode, player, me);
return false;
}
return true;
}
};
// 278407 - Sword of Dawn
// 279254 - The Warbringer
struct transport_seething_shore : TransportScript
{
explicit transport_seething_shore() : TransportScript("transport_seething_shore") { }
void OnRemovePassenger(Transport* /*transport*/, Player* player) override
{
if (Battleground const* bg = player->GetBattleground())
{
if (bg->GetStatus() != STATUS_IN_PROGRESS)
{
WorldSafeLocsEntry const* pos = bg->GetTeamStartPosition(Battleground::GetTeamIndexByTeamId(player->GetBGTeam()));
player->TeleportTo({ .Location = pos->Loc, .TransportGuid = pos->TransportSpawnId ? ObjectGuid::Create<HighGuid::Transport>(*pos->TransportSpawnId) : ObjectGuid::Empty });
return;
}
player->CastSpell(player, SeethingShore::Spells::RocketParachute, true);
player->CastSpell(player, SeethingShore::Spells::NoFallingDamage, true);
}
}
};
// 133533 - Air Supplies
struct npc_bg_seething_shore_air_supplies_crate : ScriptedAI
{
explicit npc_bg_seething_shore_air_supplies_crate(Creature* creature) : ScriptedAI(creature) { }
void JustAppeared() override
{
DoCastAOE(SeethingShore::Spells::RopeBeam);
DoCastAOE(SeethingShore::Spells::PingVehicle);
}
};
struct AirSupplyData
{
Position Spawn;
Position Destination;
};
// 133542 - Air Supply Ground Dummy
struct npc_bg_seething_shore_air_supply_ground_dummy : ScriptedAI
{
explicit npc_bg_seething_shore_air_supply_ground_dummy(Creature* creature) : ScriptedAI(creature) { }
static constexpr uint32 PATH_ID_GROUND = 1;
static WaypointPath ConvertPosToPath(Position const& position)
{
return {
PATH_ID_GROUND,
{
{ 0, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ() }
},
WaypointMoveType::Run,
WaypointPathFlags::FlyingPath
};
}
static inline const std::unordered_map<std::string_view, AirSupplyData> AIR_SUPPLY_DATA =
{
{ SeethingShore::StringIds::AirSupplyGroundDummy1, { { 1398.8298f, 2727.5322f, 121.74067f, 1.789716362953186035f }, { 1398.7153f, 2728.0469f, 28.024399f } } },
{ SeethingShore::StringIds::AirSupplyGroundDummy2, { { 1226.2812f, 2934.0356f, 152.03821f, 2.737864494323730468f }, { 1224.4445f, 2934.8203f, 72.7618f } } },
{ SeethingShore::StringIds::AirSupplyGroundDummy3, { { 1226.276f, 2826.9246f, 145.53375f,5.083248138427734375f }, { 1226.9896f, 2825.0894f, 39.256886f } } },
{ SeethingShore::StringIds::AirSupplyGroundDummy4, { { 1384.7483f, 2847.356f, 113.422775f, 2.29029250144958496f }, { 1384.2257f, 2847.9524f, 38.665867f } } },
{ SeethingShore::StringIds::AirSupplyGroundDummy5, { { 1323.1545f, 2627.9531f, 99.90129f, 3.868808984756469726f }, { 1322.7587f, 2627.6008f, 0.9027778f } } },
{ SeethingShore::StringIds::AirSupplyGroundDummy6, { { 1423.8021f, 2870.2188f, 115.70237f, 1.629547119140625f }, { 1423.7188f, 2871.6362f, 36.968292f } } }
};
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
}
void SpawnAirSupplyParachute() const
{
std::string_view const stringId = me->GetStringId(StringIdType::Spawn);
if (!AIR_SUPPLY_DATA.contains(stringId))
return;
auto const& [spawn, destination] = AIR_SUPPLY_DATA.at(stringId);
if (TempSummon* supplies = me->GetMap()->SummonCreature(SeethingShore::Creatures::AirSupplies, spawn))
supplies->GetMotionMaster()->MovePath(ConvertPosToPath(destination), false);
}
void DoAction(int32 actionId) override
{
if (actionId == SeethingShore::Actions::SpawnBuff)
{
_scheduler.Schedule(5s, [&](TaskContext)
{
SpawnAirSupplyParachute();
});
}
}
private:
TaskScheduler _scheduler;
};
// 133532 - Air Supplies
struct npc_bg_seething_shore_air_supplies_drop : ScriptedAI
{
explicit npc_bg_seething_shore_air_supplies_drop(Creature* creature) : ScriptedAI(creature) { }
void WaypointPathEnded(uint32 /*pointId*/, uint32 pathId) override
{
if (pathId != npc_bg_seething_shore_air_supply_ground_dummy::PATH_ID_GROUND)
return;
Creature* groundDummy = me->FindNearestCreature(SeethingShore::Creatures::AirSupplyGroundDummy, 20.0f);
if (!groundDummy)
return;
QuaternionData const rot = QuaternionData::fromEulerAnglesZYX(groundDummy->GetOrientation(), 0.f, 0.f);
uint8 const action = urand(0, 3);
switch (action)
{
case 0:
case 1:
case 2:
// pretty sure some serverside spell is used, and we could drop this whole switch
if (GameObject* buff = GameObject::CreateGameObject(SeethingShore::GameObjects::Buffs[action], groundDummy->GetMap(), groundDummy->GetPosition(), rot, 255, GO_STATE_READY))
{
buff->SetSpawnedByDefault(false);
buff->SetRespawnTime(300); // this is equal to the haste rune buff area trigger
groundDummy->GetMap()->AddToMap(buff);
}
break;
case 3:
groundDummy->CastSpell(groundDummy, SeethingShore::Spells::CreateHasteRuneBuffAreaTrigger, true);
break;
default:
break;
}
groundDummy->CastSpell(nullptr, SeethingShore::Spells::DustCloudImpactBigger, true);
me->DespawnOrUnsummon();
}
};
struct at_bg_seething_shore_haste_rune_buff : AreaTriggerAI
{
explicit at_bg_seething_shore_haste_rune_buff(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { }
void OnCreate(Spell const* /*creatingSpell*/) override
{
at->PlaySpellVisual(SeethingShore::SpellVisuals::HasteRuneBuff);
}
void OnUnitEnter(Unit* unit) override
{
if (Player* player = unit->ToPlayer())
{
if (ZoneScript* zonescript = at->GetZoneScript())
{
player->CastSpell(player, SeethingShore::Spells::SpeedUp, true);
zonescript->DoAction(SeethingShore::Actions::ConsumeBuff, at);
at->Remove();
}
}
}
};
// 131773 - Nathanos Blightcaller
// 130532 - Master Mathias Shaw
struct npc_bg_seething_shore_commander : ScriptedAI
{
explicit npc_bg_seething_shore_commander(Creature* creature) : ScriptedAI(creature) { }
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
}
void DoAction(int32 actionId) override
{
switch (actionId)
{
case SeethingShore::Actions::CommanderText1:
Talk(SeethingShore::CommanderTexts::Intro1);
_scheduler.Schedule(30s, [this](TaskContext context)
{
Talk(SeethingShore::CommanderTexts::Intro2);
context.Schedule(15s, [this](TaskContext context2)
{
Talk(SeethingShore::CommanderTexts::Intro3);
context2.Schedule(12s, [this](TaskContext)
{
Talk(SeethingShore::CommanderTexts::Intro4);
});
});
});
break;
case SeethingShore::Actions::CaptureAzeriteNode:
if (urand(0, 1))
Talk(SeethingShore::CommanderTexts::CapturedAzerite);
break;
case SeethingShore::Actions::SpawnBuff:
Talk(SeethingShore::CommanderTexts::SuppliesSpawned);
break;
default:
break;
}
}
private:
TaskScheduler _scheduler;
};
// 129344 - Vignette Dummy
struct npc_bg_seething_shore_vignette_dummy : ScriptedAI
{
explicit npc_bg_seething_shore_vignette_dummy(Creature* creature) : ScriptedAI(creature) { }
void JustAppeared() override
{
_scheduler.Schedule(38s, [this](TaskContext)
{
if (Creature* fissure = me->FindNearestCreature(SeethingShore::Creatures::AzeriteFissure, 5.0f))
me->SendPlaySpellVisual(fissure, SeethingShore::SpellVisuals::AzeriteBirth, 0, 0, 0.0f);
});
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
}
private:
TaskScheduler _scheduler;
};
void AddSC_battleground_seething_shore()
{
RegisterBattlegroundMapScript(battleground_seething_shore, 1803);
RegisterSpellScript(spell_bg_seething_shore_activate_azerite);
RegisterSpellScript(spell_bg_seething_shore_azerite_geyser);
RegisterGameObjectAI(go_bg_seething_shore_azerite);
RegisterSpellScript(spell_bg_seething_shore_rocket_parachute_trigger);
RegisterSpellScript(spell_bg_seething_shore_rocket_parachute_ground_check);
RegisterSpellScript(spell_bg_seething_shore_parachute);
RegisterCreatureAI(npc_bg_seething_shore_air_supplies_crate);
RegisterCreatureAI(npc_bg_seething_shore_air_supply_ground_dummy);
RegisterAreaTriggerAI(at_bg_seething_shore_haste_rune_buff);
RegisterCreatureAI(npc_bg_seething_shore_air_supplies_drop);
RegisterCreatureAI(npc_bg_seething_shore_commander);
RegisterSpellScript(spell_bg_seething_shore_speed_up);
RegisterCreatureAI(npc_bg_seething_shore_vignette_dummy);
new transport_seething_shore();
}

View File

@@ -53,6 +53,8 @@ void AddSC_arena_ring_of_valor();
void AddSC_battleground_twin_peaks();
void AddSC_battleground_battle_for_gilneas();
void AddSC_battleground_seething_shore();
// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
void AddBattlegroundsScripts()
@@ -92,4 +94,6 @@ void AddBattlegroundsScripts()
AddSC_battleground_twin_peaks();
AddSC_battleground_battle_for_gilneas();
AddSC_battleground_seething_shore();
}