Scripts/Arenas: Rework Dalaran Sewers (#31215)

This commit is contained in:
Jeremy
2025-09-28 11:55:26 +02:00
committed by GitHub
parent c5d4dec24e
commit 974816b041
2 changed files with 263 additions and 147 deletions

View File

@@ -25,64 +25,74 @@
#include "ObjectAccessor.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "TaskScheduler.h"
namespace DalaranSewers
{
namespace Creatures
{
static constexpr uint32 WaterSpout = 28567;
}
namespace Events
{
static constexpr uint32 WaterfallWarning = 1;
static constexpr uint32 WaterfallOn = 2;
static constexpr uint32 WaterfallOff = 3;
static constexpr uint32 WaterfallKnockBack = 4;
}
namespace GameObjects
{
static constexpr uint32 Door01 = 192642;
static constexpr uint32 Door02 = 192643;
static constexpr uint32 WaterCollision = 194395;
static constexpr uint32 Waterfall = 191877;
}
namespace MapIds
{
static constexpr uint32 DalaranSewers = 617;
}
namespace Spells
{
static constexpr uint32 PipeFlushKnockbackSearchTrigger = 96539;
static constexpr uint32 DalaranSewersPetTeleport = 254013;
static constexpr uint32 WaterSpout = 58873; // Knock back
static constexpr uint32 Flush = 57405;
static constexpr uint32 WarlockDemonicCircle = 48018;
}
namespace StringIds
{
static constexpr std::string_view WaterSpoutPipe = "arena_dalaran_sewers_water_spout_pipe";
static constexpr std::string_view WaterSpoutCenter = "arena_dalaran_sewers_water_spout_center";
}
namespace Timers
{
static constexpr Seconds Waterfall = 30s;
static constexpr Seconds WaterfallWarningDuration = 5s;
static constexpr Seconds WaterfallDuration = 30s;
static constexpr Milliseconds WaterfallKnockBack = 1500ms;
}
}
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) { }
explicit arena_dalaran_sewers(BattlegroundMap* map) : ArenaScript(map) { }
void OnUpdate(uint32 diff) override
{
ArenaScript::OnUpdate(diff);
if (battleground->GetStatus() != STATUS_IN_PROGRESS)
return;
@@ -93,75 +103,44 @@ struct arena_dalaran_sewers : ArenaScript
{
switch (eventId)
{
case BG_DS_EVENT_WATERFALL_WARNING:
case DalaranSewers::Events::WaterfallWarning:
// Add the water
if (GameObject* go = battlegroundMap->GetGameObject(_water2GUID))
go->ResetDoorOrButton();
_events.ScheduleEvent(BG_DS_EVENT_WATERFALL_ON, BG_DS_WATERFALL_WARNING_DURATION);
if (GameObject* go = battlegroundMap->GetGameObject(_waterfallGUID))
go->UseDoorOrButton();
_events.ScheduleEvent(DalaranSewers::Events::WaterfallOn, DalaranSewers::Timers::WaterfallWarningDuration);
break;
case BG_DS_EVENT_WATERFALL_ON:
case DalaranSewers::Events::WaterfallOn:
// 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);
if (GameObject* go = battlegroundMap->GetGameObject(_waterCollisionGUID))
go->UseDoorOrButton();
_events.ScheduleEvent(DalaranSewers::Events::WaterfallOff, DalaranSewers::Timers::WaterfallDuration);
_events.ScheduleEvent(DalaranSewers::Events::WaterfallKnockBack, DalaranSewers::Timers::WaterfallKnockBack);
break;
case BG_DS_EVENT_WATERFALL_OFF:
case DalaranSewers::Events::WaterfallOff:
// 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);
if (GameObject* go = battlegroundMap->GetGameObject(_waterCollisionGUID))
go->ResetDoorOrButton();
if (GameObject* go = battlegroundMap->GetGameObject(_waterfallGUID))
go->ResetDoorOrButton();
_events.CancelEvent(DalaranSewers::Events::WaterfallKnockBack);
_events.ScheduleEvent(DalaranSewers::Events::WaterfallWarning, DalaranSewers::Timers::Waterfall);
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);
case DalaranSewers::Events::WaterfallKnockBack:
// Repeat knock back while the waterfall still active
if (Creature* waterSpout = battlegroundMap->GetCreature(_waterSpoutCenterGUID))
waterSpout->CastSpell(waterSpout, DalaranSewers::Spells::WaterSpout, true);
_events.ScheduleEvent(eventId, DalaranSewers::Timers::WaterfallKnockBack);
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
{
ArenaScript::OnStart();
for (ObjectGuid const& guid : _doorGUIDs)
{
if (GameObject* door = battlegroundMap->GetGameObject(guid))
@@ -171,57 +150,79 @@ struct arena_dalaran_sewers : ArenaScript
}
}
_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;
_events.ScheduleEvent(DalaranSewers::Events::WaterfallWarning, DalaranSewers::Timers::Waterfall);
// Remove collision and water
if (GameObject* go = battlegroundMap->GetGameObject(_water1GUID))
go->UseDoorOrButton();
if (GameObject* go = battlegroundMap->GetGameObject(_water2GUID))
go->UseDoorOrButton();
if (GameObject* go = battlegroundMap->GetGameObject(_waterCollisionGUID))
go->ResetDoorOrButton();
if (GameObject* go = battlegroundMap->GetGameObject(_waterfallGUID))
go->ResetDoorOrButton();
for (const auto& [playerGuid, _] : battleground->GetPlayers())
if (Player* player = ObjectAccessor::FindPlayer(playerGuid))
player->RemoveAurasDueToSpell(SPELL_WARL_DEMONIC_CIRCLE);
player->RemoveAurasDueToSpell(DalaranSewers::Spells::WarlockDemonicCircle);
_scheduler.Schedule(6s, [&](TaskContext)
{
for (ObjectGuid const& guid : _waterSpoutEntranceGUIDs)
{
if (Creature* creature = battlegroundMap->GetCreature(guid))
{
creature->CastSpell(nullptr, DalaranSewers::Spells::Flush, CastSpellExtraArgsInit{
.TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR
});
creature->CastSpell(creature, DalaranSewers::Spells::PipeFlushKnockbackSearchTrigger, CastSpellExtraArgsInit{
.TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR
});
}
}
});
}
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
void OnCreatureCreate(Creature* creature) override
{
if (GameObject const* go = CreateObject(entry, x, y, z, o, rotation0, rotation1, rotation2, rotation3, goState))
guidVector.emplace_back(go->GetGUID());
switch (creature->GetEntry())
{
case DalaranSewers::Creatures::WaterSpout:
if (creature->HasStringId(DalaranSewers::StringIds::WaterSpoutPipe))
_waterSpoutEntranceGUIDs.push_back(creature->GetGUID());
else if (creature->HasStringId(DalaranSewers::StringIds::WaterSpoutCenter))
_waterSpoutCenterGUID = creature->GetGUID();
break;
default:
break;
}
}
void SetData(uint32 dataId, uint32 value) override
void OnGameObjectCreate(GameObject* gameobject) 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);
switch (gameobject->GetEntry())
{
case DalaranSewers::GameObjects::Door01:
case DalaranSewers::GameObjects::Door02:
_doorGUIDs.push_back(gameobject->GetGUID());
break;
case DalaranSewers::GameObjects::WaterCollision:
_waterCollisionGUID = gameobject->GetGUID();
break;
case DalaranSewers::GameObjects::Waterfall:
_waterfallGUID = gameobject->GetGUID();
break;
default:
break;
}
}
private:
GuidVector _doorGUIDs;
ObjectGuid _water1GUID;
ObjectGuid _water2GUID;
ObjectGuid _waterfallCreatureGUID;
GuidVector _pipeCreatureGUIDs;
TaskScheduler _scheduler;
ObjectGuid _waterCollisionGUID;
ObjectGuid _waterfallGUID;
EventMap _events;
uint32 _pipeKnockBackTimer;
uint8 _pipeKnockBackCount;
GuidVector _waterSpoutEntranceGUIDs;
ObjectGuid _waterSpoutCenterGUID;
TaskScheduler _scheduler;
};
class at_ds_pipe_knockback : public AreaTriggerScript
@@ -229,7 +230,7 @@ class at_ds_pipe_knockback : public AreaTriggerScript
public:
at_ds_pipe_knockback() : AreaTriggerScript("at_ds_pipe_knockback") { }
void Trigger(Player* player) const
static void Trigger(Player* player)
{
if (Battleground const* battleground = player->GetBattleground())
{
@@ -237,12 +238,8 @@ public:
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);
player->RemoveAurasDueToSpell(DalaranSewers::Spells::WarlockDemonicCircle);
player->CastSpell(nullptr, DalaranSewers::Spells::DalaranSewersPetTeleport);
}
}
@@ -259,8 +256,52 @@ public:
}
};
// 96538 - Pipe Flush Knockback Search Effect
class spell_arena_dalaran_sewers_pipe_flush_knockback_search_trigger : public SpellScript
{
bool Validate(SpellInfo const* spellInfo) override
{
return ValidateSpellInfo({
static_cast<uint32>(spellInfo->GetEffect(EFFECT_0).CalcValue())
});
}
void HandleDummy(SpellEffIndex /*effIndex*/) const
{
GetCaster()->CastSpell(nullptr, GetSpellInfo()->GetEffect(EFFECT_0).CalcValue(), CastSpellExtraArgsInit
{
.TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR
});
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_arena_dalaran_sewers_pipe_flush_knockback_search_trigger::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
// 61698 - Flush - Knockback effect (SERVERSIDE)
class spell_arena_dalaran_sewers_flush_knock_back_effect : public SpellScript
{
void HandleDummy(SpellEffIndex /*effIndex*/) const
{
static constexpr float SpeedXY = 30.0f;
static constexpr float SpeedZ = 19.0f;
Unit* caster = GetCaster();
Unit const* target = GetHitUnit();
caster->KnockbackFrom(target->GetPosition(), SpeedXY, SpeedZ);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_arena_dalaran_sewers_flush_knock_back_effect::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
void AddSC_arena_dalaran_sewers()
{
RegisterBattlegroundMapScript(arena_dalaran_sewers, 617);
RegisterBattlegroundMapScript(arena_dalaran_sewers, DalaranSewers::MapIds::DalaranSewers);
new at_ds_pipe_knockback();
RegisterSpellScript(spell_arena_dalaran_sewers_flush_knock_back_effect);
}