From 630b90a63340c80080c6c0e45b2908096fb5e092 Mon Sep 17 00:00:00 2001 From: Ludwig Date: Wed, 18 Oct 2023 14:22:26 +0200 Subject: [PATCH] fix(Scripts/Midsummer): Implement Ribbon Pole dance effects (#17417) * fix(Scripts/Midsummer): Implement Ribbon Pole dance effects * midsummer.cpp: remove whitespaces at end of lines * midsummer.cpp: fix unused parameter error * add spacing after 0.0f * replace Milliseconds(...) by ...ms and Seconds(...) by ...s * cleanup comments * remove manual cleanups / despawns from DoCleanupChecks() - rely on NPC despawn timers - manual cleanup / removal actually is a bit excessive * set INTERVAL_DANCING_FLAMES to 15s * set despawn time to 1min for big dancing flames according to spell 46827 * set spawnDist to 12 for big dancing flames according so spell 46827 * use library function Acore::Containers::RandomShuffle() to shuffle _danclerList * sql: do not set SmartAI for NPC 17066 already using ScriptName and can't have both * combine 3 sql files into 1 * remove INTERVAL_... enum values * remove unnecessary checks of if(_bunny) * sql: fix smartscript comment * rename spell define to correct spell name --- .../rev_1696767584445542100.sql | 14 + src/server/scripts/Events/midsummer.cpp | 266 +++++++++++++++++- 2 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 data/sql/updates/pending_db_world/rev_1696767584445542100.sql diff --git a/data/sql/updates/pending_db_world/rev_1696767584445542100.sql b/data/sql/updates/pending_db_world/rev_1696767584445542100.sql new file mode 100644 index 000000000..05d00091d --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1696767584445542100.sql @@ -0,0 +1,14 @@ +-- midsummer creatureAI npc_midsummer_ribbon_pole_target +UPDATE `creature_template` SET `ScriptName` = 'npc_midsummer_ribbon_pole_target' WHERE `entry` = 17066; + +-- midsummer NPC Big Dancing Flame - SmartAI - fire dance spell +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 26267; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 26267); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(26267, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 45418, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Big Dancing Flames - On Just Summoned - Cast Spell \'Fire Dancing\''); + +-- midsummer add spell script spell_midsummer_ribbon_pole_firework +DELETE FROM `spell_script_names` WHERE `spell_id` = 46847; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(46847, 'spell_midsummer_ribbon_pole_firework'); diff --git a/src/server/scripts/Events/midsummer.cpp b/src/server/scripts/Events/midsummer.cpp index 998a6211b..d82b54d99 100644 --- a/src/server/scripts/Events/midsummer.cpp +++ b/src/server/scripts/Events/midsummer.cpp @@ -23,6 +23,7 @@ #include "Spell.h" #include "SpellAuras.h" #include "SpellScript.h" +#include enum eBonfire { @@ -210,15 +211,276 @@ class spell_gen_crab_disguise : public AuraScript enum RibbonPole { + GO_RIBBON_POLE = 181605, + SPELL_RIBBON_POLE_CHANNEL_VISUAL = 29172, SPELL_RIBBON_POLE_CHANNEL_VISUAL_2 = 29531, SPELL_TEST_RIBBON_POLE_CHANNEL_BLUE = 29705, SPELL_TEST_RIBBON_POLE_CHANNEL_RED = 29726, SPELL_TEST_RIBBON_POLE_CHANNEL_PINK = 29727, + // player spinning/rorating around himself + SPELL_RIBBON_POLE_PERIODIC_VISUAL = 45406, + // spew lava trails + SPELL_RIBBON_POLE_FIRE_SPIRAL_VISUAL= 45421, + // blue fire ring, duration 5s + SPELL_FLAME_RING = 46842, + // red fire ring, duration 5s + SPELL_FLAME_PATCH = 46836, + // single firework explosion + SPELL_RIBBON_POLE_FIREWORK = 46847, + SPELL_RIBBON_POLE_GROUND_FLOWER = 46969, SPELL_RIBBON_POLE_XP = 29175, - SPELL_RIBBON_POLE_FIREWORKS = 46971, NPC_RIBBON_POLE_DEBUG_TARGET = 17066, + NPC_GROUND_FLOWER = 25518, + NPC_BIG_DANCING_FLAMES = 26267, + NPC_RIBBON_POLE_FIRE_SPIRAL_BUNNY = 25303, + + // dancing players count + THRESHOLD_FLAME_CIRCLE = 1, + THRESHOLD_FIREWORK = 2, + THRESHOLD_FIREWORK_3 = 3, + THRESHOLD_FIREWORK_5 = 5, + THRESHOLD_GROUND_FLOWERS = 3, + THRESHOLD_SPEW_LAVA = 6, + THRESHOLD_DANCING_FLAMES = 7, + + MAX_COUNT_GROUND_FLOWERS = 3, + MAX_COUNT_SPEW_LAVA_TARGETS = 2, + MAX_COUNT_DANCING_FLAMES = 4, +}; + +struct npc_midsummer_ribbon_pole_target : public ScriptedAI +{ + npc_midsummer_ribbon_pole_target(Creature* creature) : ScriptedAI(creature) + { + // ribbonPole trap also spawns this NPC (currently unwanted) + if (me->ToTempSummon()) + me->DespawnOrUnsummon(); + + _ribbonPole = nullptr; + _bunny = nullptr; + _dancerList.clear(); + + LocateRibbonPole(); + SpawnFireSpiralBunny(); + + _scheduler.Schedule(1s, [this](TaskContext context) + { + DoCleanupChecks(); + context.Repeat(); + }) + .Schedule(5s, [this](TaskContext context) + { + DoFlameCircleChecks(); + context.Repeat(); + }) + .Schedule(15s, [this](TaskContext context) + { + DoFireworkChecks(); + context.Repeat(); + }) + .Schedule(10s, [this](TaskContext context) + { + DoGroundFlowerChecks(); + context.Repeat(); + }) + .Schedule(10s, [this](TaskContext context) + { + DoSpewLavaChecks(); + context.Repeat(); + }) + .Schedule(15s, [this](TaskContext context) + { + DoDancingFLameChecks(); + context.Repeat(); + }); + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + Player* dancer = caster->ToPlayer(); + if (!dancer) + return; + + switch (spell->Id) + { + case SPELL_TEST_RIBBON_POLE_CHANNEL_BLUE: + case SPELL_TEST_RIBBON_POLE_CHANNEL_RED: + case SPELL_TEST_RIBBON_POLE_CHANNEL_PINK: + break; + default: + return; + } + + // prevent duplicates + if (std::find(_dancerList.begin(), _dancerList.end(), dancer) != _dancerList.end()) + return; + + _dancerList.push_back(dancer); + } + + void LocateRibbonPole() + { + _scheduler.Schedule(420ms, [this](TaskContext context) + { + _ribbonPole = me->FindNearestGameObject(GO_RIBBON_POLE, 10.0f); + + if (!_ribbonPole) + context.Repeat(420ms); + }); + } + + void SpawnFireSpiralBunny() + { + _bunny = me->FindNearestCreature(NPC_RIBBON_POLE_FIRE_SPIRAL_BUNNY, 10.0f); + + if (!_bunny) + _bunny = DoSpawnCreature(NPC_RIBBON_POLE_FIRE_SPIRAL_BUNNY, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN, 0); + } + + void DoCleanupChecks() + { + if (_dancerList.empty()) + return; + + // remove non-dancing players from list + std::erase_if(_dancerList, [](Player* dancer) + { + return !dancer->HasAura(SPELL_RIBBON_POLE_PERIODIC_VISUAL); + }); + } + + void DoFlameCircleChecks() + { + if (!_ribbonPole) + return; + if (_dancerList.size() >= THRESHOLD_FLAME_CIRCLE) + { + // random blue / red circle + if (urand(0, 1)) + _ribbonPole->CastSpell(me, SPELL_FLAME_RING); + else + _ribbonPole->CastSpell(me, SPELL_FLAME_PATCH); + } + } + + void DoFireworkChecks() + { + if (!_bunny) + return; + + if (_dancerList.size() >= THRESHOLD_FIREWORK) + { + _bunny->CastSpell(nullptr, SPELL_RIBBON_POLE_FIREWORK); + } + if (_dancerList.size() >= THRESHOLD_FIREWORK_3) + { + _scheduler.Schedule(500ms, [this](TaskContext /*context*/) + { + _bunny->CastSpell(nullptr, SPELL_RIBBON_POLE_FIREWORK); + }) + .Schedule(1s, [this](TaskContext /*context*/) + { + _bunny->CastSpell(nullptr, SPELL_RIBBON_POLE_FIREWORK); + }); + } + if (_dancerList.size() >= THRESHOLD_FIREWORK_5) + { + _scheduler.Schedule(1500ms, [this](TaskContext /*context*/) + { + _bunny->CastSpell(nullptr, SPELL_RIBBON_POLE_FIREWORK); + }) + .Schedule(2s, [this](TaskContext /*context*/) + { + _bunny->CastSpell(nullptr, SPELL_RIBBON_POLE_FIREWORK); + }); + } + } + + void DoGroundFlowerChecks() + { + if (!_bunny) + return; + + if (_dancerList.size() >= THRESHOLD_GROUND_FLOWERS) + { + std::list crList; + me->GetCreaturesWithEntryInRange(crList, 20.0f, NPC_GROUND_FLOWER); + + if (crList.size() < MAX_COUNT_GROUND_FLOWERS) + _bunny->CastSpell(nullptr, SPELL_RIBBON_POLE_GROUND_FLOWER); + } + } + + void DoSpewLavaChecks() + { + if (!_bunny) + return; + + if (_dancerList.size() >= THRESHOLD_SPEW_LAVA) + { + if (!_dancerList.empty()) + { + Acore::Containers::RandomShuffle(_dancerList); + + for (uint8 i = 0; (i < MAX_COUNT_SPEW_LAVA_TARGETS) && (i < _dancerList.size()); i++) + { + Player* dancerTarget = _dancerList[i]; + + if (dancerTarget) + { + Creature* fireSpiralBunny = dancerTarget->SummonCreature(NPC_RIBBON_POLE_FIRE_SPIRAL_BUNNY, dancerTarget->GetPositionX(), dancerTarget->GetPositionY(), dancerTarget->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000); + if (fireSpiralBunny) + fireSpiralBunny->CastSpell(_bunny, SPELL_RIBBON_POLE_FIRE_SPIRAL_VISUAL, true); + } + } + } + } + } + + void DoDancingFLameChecks() + { + if (_dancerList.size() >= THRESHOLD_DANCING_FLAMES) + { + std::list crList; + me->GetCreaturesWithEntryInRange(crList, 20.0f, NPC_BIG_DANCING_FLAMES); + + if (crList.size() < MAX_COUNT_DANCING_FLAMES) + { + float spawnDist = 12.0f; + float angle = rand_norm() * 2 * M_PI; + DoSpawnCreature(NPC_BIG_DANCING_FLAMES, spawnDist * cos(angle), spawnDist * std::sin(angle), 0, angle + M_PI, TEMPSUMMON_TIMED_DESPAWN, 60000); + } + } + } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } + +private: + TaskScheduler _scheduler; + std::vector _dancerList; + GameObject* _ribbonPole; + Creature* _bunny; +}; + +class spell_midsummer_ribbon_pole_firework : public SpellScript +{ + PrepareSpellScript(spell_midsummer_ribbon_pole_firework) + + void ModDestHeight(SpellDestination& dest) + { + Position const offset = { 0.0f, 0.0f, 20.0f , 0.0f }; + dest.RelocateOffset(offset); + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_midsummer_ribbon_pole_firework::ModDestHeight, EFFECT_0, TARGET_DEST_CASTER_RANDOM); + } }; class spell_midsummer_ribbon_pole : public AuraScript @@ -617,9 +879,11 @@ void AddSC_event_midsummer_scripts() // NPCs new go_midsummer_bonfire(); RegisterCreatureAI(npc_midsummer_torch_target); + RegisterCreatureAI(npc_midsummer_ribbon_pole_target); // Spells RegisterSpellScript(spell_gen_crab_disguise); + RegisterSpellScript(spell_midsummer_ribbon_pole_firework); RegisterSpellScript(spell_midsummer_ribbon_pole); RegisterSpellScript(spell_midsummer_ribbon_pole_visual); RegisterSpellScript(spell_midsummer_torch_quest);