Framework scenarios d artefact : runner generique pilote par tables world
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -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 <unordered_map>
|
||||||
|
|
||||||
|
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<uint8, std::vector<StageSpawn>> stages;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_map<uint32, Config> _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<uint32> 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);
|
||||||
|
}
|
||||||
@@ -219,6 +219,7 @@ void AddSC_scenario_the_violet_hold_dh();
|
|||||||
void AddSC_scenario_mage_art();
|
void AddSC_scenario_mage_art();
|
||||||
void AddSC_scenario_monk_serenitys_end();
|
void AddSC_scenario_monk_serenitys_end();
|
||||||
void AddSC_scenario_broken_shore_intro();
|
void AddSC_scenario_broken_shore_intro();
|
||||||
|
void AddSC_scenario_artifact_runner();
|
||||||
|
|
||||||
// The name of this function should match:
|
// The name of this function should match:
|
||||||
// void Add${NameOfDirectory}Scripts()
|
// void Add${NameOfDirectory}Scripts()
|
||||||
@@ -419,4 +420,5 @@ void AddBrokenIslesScripts()
|
|||||||
AddSC_scenario_mage_art();
|
AddSC_scenario_mage_art();
|
||||||
AddSC_scenario_monk_serenitys_end();
|
AddSC_scenario_monk_serenitys_end();
|
||||||
AddSC_scenario_broken_shore_intro();
|
AddSC_scenario_broken_shore_intro();
|
||||||
|
AddSC_scenario_artifact_runner();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user