From 474a63f44bc25db15c72a8e7660b3156fc8dd89b Mon Sep 17 00:00:00 2001 From: SylvaniaCore deploy Date: Sun, 12 Jul 2026 18:50:42 +0000 Subject: [PATCH] Framework scenarios d artefact : runner generique pilote par tables world MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scenario_artifact_runner.cpp : InstanceScript generique lisant scenario_artifact_config et scenario_artifact_stage (entree, etapes/vagues/boss, teleports inter-zones, niveaux forces, credit et sortie configurables). Kills comptes via OnUnitDeath. - Ajouter un scenario = 1 ligne config + N lignes stage + macro REGISTER_ARTIFACT_RUNNER(map) - 1re config livree : The Thundering Heavens (WW moine, Skywall 1528, boss Typhinius 100760, ancres officielles WorldSafeLocs) — SQL : ~/dc-patches/artifact_runner.sql Co-Authored-By: Claude Fable 5 --- .../Scenario/scenario_artifact_runner.cpp | 249 ++++++++++++++++++ .../BrokenIsles/brokenisles_script_loader.cpp | 2 + 2 files changed, 251 insertions(+) create mode 100644 src/server/scripts/BrokenIsles/Scenario/scenario_artifact_runner.cpp diff --git a/src/server/scripts/BrokenIsles/Scenario/scenario_artifact_runner.cpp b/src/server/scripts/BrokenIsles/Scenario/scenario_artifact_runner.cpp new file mode 100644 index 0000000..910094b --- /dev/null +++ b/src/server/scripts/BrokenIsles/Scenario/scenario_artifact_runner.cpp @@ -0,0 +1,249 @@ +/* + * Runner generique de scenarios d'artefact — pilote par les tables world + * `scenario_artifact_config` (map, entree, sortie, credit) et + * `scenario_artifact_stage` (map, etape, invocations, teleport optionnel). + * + * Ajouter un scenario = 1 ligne config + N lignes stage + 1 macro + * REGISTER_ARTIFACT_RUNNER(map) + instance_template.script adapte. + * Deroule : intro -> etapes (vagues/boss, kills comptes via OnUnitDeath) + * -> CompleteScenario -> credit optionnel -> teleport de sortie. + */ + +#include "ScriptMgr.h" +#include "InstanceScript.h" +#include "Scenario.h" +#include "InstanceScenario.h" +#include "ScriptedCreature.h" +#include "PhasingHandler.h" +#include "TemporarySummon.h" +#include "Player.h" +#include "DatabaseEnv.h" +#include "TaskScheduler.h" +#include + +namespace ArtifactRunner +{ + struct StageSpawn + { + uint32 entry = 0; + float x = 0.f, y = 0.f, z = 0.f, o = 0.f; + uint8 count = 1; + uint8 level = 0; + bool teleport = false; + }; + + struct Config + { + Position entrance; + uint32 exitMap = 0; + Position exit; + uint32 creditEntry = 0; + std::map> stages; + }; + + static std::unordered_map _configs; + static bool _loaded = false; + + void LoadAll() + { + if (_loaded) + return; + _loaded = true; + + if (QueryResult result = WorldDatabase.Query("SELECT map, entrance_x, entrance_y, entrance_z, entrance_o, exit_map, exit_x, exit_y, exit_z, exit_o, credit_entry FROM scenario_artifact_config")) + { + do + { + Field* f = result->Fetch(); + Config& c = _configs[f[0].GetUInt16()]; + c.entrance.Relocate(f[1].GetFloat(), f[2].GetFloat(), f[3].GetFloat(), f[4].GetFloat()); + c.exitMap = f[5].GetUInt16(); + c.exit.Relocate(f[6].GetFloat(), f[7].GetFloat(), f[8].GetFloat(), f[9].GetFloat()); + c.creditEntry = f[10].GetUInt32(); + } while (result->NextRow()); + } + + if (QueryResult result = WorldDatabase.Query("SELECT map, stage, entry, x, y, z, o, cnt, lvl, teleport FROM scenario_artifact_stage ORDER BY map, stage, idx")) + { + do + { + Field* f = result->Fetch(); + auto itr = _configs.find(f[0].GetUInt16()); + if (itr == _configs.end()) + continue; + StageSpawn s; + s.entry = f[2].GetUInt32(); + s.x = f[3].GetFloat(); s.y = f[4].GetFloat(); s.z = f[5].GetFloat(); s.o = f[6].GetFloat(); + s.count = f[7].GetUInt8(); + s.level = f[8].GetUInt8(); + s.teleport = f[9].GetUInt8() != 0; + itr->second.stages[f[1].GetUInt8()].push_back(s); + } while (result->NextRow()); + } + } + + Config const* GetConfig(uint32 mapId) + { + LoadAll(); + auto itr = _configs.find(mapId); + return itr != _configs.end() ? &itr->second : nullptr; + } +} + +struct scenario_artifact_runner : public InstanceScript +{ + scenario_artifact_runner(InstanceMap* map) : InstanceScript(map) { } + + void Initialize() override + { + SetBossNumber(12); + config = ArtifactRunner::GetConfig(instance->GetId()); + stage = 0; + aliveCount = 0; + introDone = false; + } + + void OnPlayerEnter(Player* player) override + { + InstanceScript::OnPlayerEnter(player); + PhasingHandler::AddPhase(player, 169, true); + + if (!introDone && config) + { + introDone = true; + scheduler.Schedule(Seconds(8), [this](TaskContext /*context*/) + { + CompleteStep(); // etape d'intro du scenario + StartStage(1); + }); + } + } + + void Update(uint32 diff) override + { + InstanceScript::Update(diff); + scheduler.Update(diff); + } + + void CompleteStep() + { + if (Scenario* scenario = instance->GetInstanceScenario()) + scenario->CompleteCurrStep(); + } + + void StartStage(uint8 newStage) + { + if (!config) + return; + + auto itr = config->stages.find(newStage); + if (itr == config->stages.end()) + { + Finish(); + return; + } + + stage = newStage; + aliveCount = 0; + stageEntries.clear(); + + bool teleported = false; + for (ArtifactRunner::StageSpawn const& s : itr->second) + { + if (s.teleport && !teleported) + { + teleported = true; + float tx = s.x, ty = s.y, tz = s.z, to = s.o; + DoOnPlayers([tx, ty, tz, to](Player* player) + { + player->NearTeleportTo(tx, ty, tz, to); + }); + } + for (uint8 i = 0; i < s.count; ++i) + { + float dx = (i % 3) * 6.0f - 6.0f; + float dy = (i / 3) * 6.0f - 3.0f; + Position pos = { s.x + dx + 12.0f, s.y + dy + 8.0f, s.z, s.o }; + if (TempSummon* summon = instance->SummonCreature(s.entry, pos)) + { + if (s.level) + { + summon->SetLevel(s.level); + summon->SetFullHealth(); + } + stageEntries.insert(s.entry); + ++aliveCount; + } + } + } + + if (!aliveCount) // etape vide : on enchaine + { + CompleteStep(); + StartStage(stage + 1); + } + } + + void OnUnitDeath(Unit* unit) override + { + Creature* creature = unit->ToCreature(); + if (!creature || !config || !stage) + return; + if (stageEntries.find(creature->GetEntry()) == stageEntries.end()) + return; + + if (aliveCount && --aliveCount == 0) + { + CompleteStep(); + StartStage(stage + 1); + } + } + + void Finish() + { + if (Scenario* scenario = instance->GetInstanceScenario()) + scenario->CompleteScenario(); + + uint32 credit = config ? config->creditEntry : 0; + DoOnPlayers([credit](Player* player) + { + if (credit) + player->KilledMonsterCredit(credit); + }); + + if (config && config->exitMap) + { + uint32 exitMap = config->exitMap; + Position out = config->exit; + scheduler.Schedule(Seconds(8), [this, exitMap, out](TaskContext /*context*/) + { + DoOnPlayers([exitMap, out](Player* player) + { + player->TeleportTo(exitMap, out.GetPositionX(), out.GetPositionY(), out.GetPositionZ(), out.GetOrientation()); + }); + }); + } + } + +private: + ArtifactRunner::Config const* config = nullptr; + uint8 stage = 0; + uint32 aliveCount = 0; + std::set stageEntries; + bool introDone = false; + TaskScheduler scheduler; +}; + +// Une sous-classe par map configuree ; le nom doit correspondre a instance_template.script. +#define REGISTER_ARTIFACT_RUNNER(mapid) \ + struct scenario_artifact_runner_##mapid : public scenario_artifact_runner \ + { \ + scenario_artifact_runner_##mapid(InstanceMap* map) : scenario_artifact_runner(map) { } \ + }; \ + RegisterInstanceScript(scenario_artifact_runner_##mapid, mapid) + +void AddSC_scenario_artifact_runner() +{ + // Marche-vent moine - « The Thundering Heavens » (Skywall, boss Typhinius) + REGISTER_ARTIFACT_RUNNER(1528); +} diff --git a/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp b/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp index 71911a4..7c890a4 100644 --- a/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp +++ b/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp @@ -219,6 +219,7 @@ void AddSC_scenario_the_violet_hold_dh(); void AddSC_scenario_mage_art(); void AddSC_scenario_monk_serenitys_end(); void AddSC_scenario_broken_shore_intro(); +void AddSC_scenario_artifact_runner(); // The name of this function should match: // void Add${NameOfDirectory}Scripts() @@ -419,4 +420,5 @@ void AddBrokenIslesScripts() AddSC_scenario_mage_art(); AddSC_scenario_monk_serenitys_end(); AddSC_scenario_broken_shore_intro(); + AddSC_scenario_artifact_runner(); }