diff options
author | ccrs <ccrs@users.noreply.github.com> | 2019-10-26 23:33:30 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-26 23:33:30 +0200 |
commit | 06c6b30ed484850228c57313dc2c024c38e2055f (patch) | |
tree | b38bf9e9326fcde9936ca12d06bb35797d6e023f | |
parent | 00703ee238729a8d5b3d17edf8dc16d6c6c6b601 (diff) |
Scripts/ScarletMonastery: instance script cleanup (#23889)
Adapt InstanceScript into newer structure and style
Adapt existing boss scripts into newer structure and style
Improve existing boss related definitions
Should make each boss more functionally reliable, but nothing new has been added
16 files changed, 1707 insertions, 1885 deletions
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 1de18af19b7..4b45ca6af85 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -97,7 +97,7 @@ enum MovementGeneratorType : uint8; typedef std::list<Unit*> UnitList; -class DispelableAura +class TC_GAME_API DispelableAura { public: DispelableAura(Aura* aura, int32 dispelChance, uint8 dispelCharges); diff --git a/src/server/game/Maps/MapRefManager.h b/src/server/game/Maps/MapRefManager.h index 324b96fbd60..5b029ab2ea5 100644 --- a/src/server/game/Maps/MapRefManager.h +++ b/src/server/game/Maps/MapRefManager.h @@ -29,13 +29,13 @@ class MapRefManager : public RefManager<Map, Player> typedef LinkedListHead::Iterator<MapReference> iterator; typedef LinkedListHead::Iterator<MapReference const> const_iterator; - MapReference* getFirst() { return (MapReference*)RefManager<Map, Player>::getFirst(); } + MapReference* getFirst() { return (MapReference*)RefManager<Map, Player>::getFirst(); } MapReference const* getFirst() const { return (MapReference const*)RefManager<Map, Player>::getFirst(); } iterator begin() { return iterator(getFirst()); } - iterator end() { return iterator(nullptr); } + iterator end() { return iterator(nullptr); } const_iterator begin() const { return const_iterator(getFirst()); } - const_iterator end() const { return const_iterator(nullptr); } + const_iterator end() const { return const_iterator(nullptr); } }; #endif diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index b9677ff70b4..b0dea1e36b6 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -1108,7 +1108,7 @@ class GenericCreatureScript : public CreatureScript }; #define RegisterCreatureAI(ai_name) new GenericCreatureScript<ai_name>(#ai_name) -template <class AI, AI*(*AIFactory)(Creature*)> +template <class AI, AI* (*AIFactory)(Creature*)> class FactoryCreatureScript : public CreatureScript { public: @@ -1126,6 +1126,15 @@ class GenericGameObjectScript : public GameObjectScript }; #define RegisterGameObjectAI(ai_name) new GenericGameObjectScript<ai_name>(#ai_name) +template <class AI, AI* (*AIFactory)(GameObject*)> +class FactoryGameObjectScript : public GameObjectScript +{ + public: + FactoryGameObjectScript(char const* name) : GameObjectScript(name) { } + GameObjectAI* GetAI(GameObject* me) const override { return AIFactory(me); } +}; +#define RegisterGameObjectAIWithFactory(ai_name, factory_fn) new FactoryGameObjectScript<ai_name, &factory_fn>(#ai_name) + #define sScriptMgr ScriptMgr::instance() #endif diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp index cfc6cd3c0be..80cfec4d37d 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp @@ -15,123 +15,107 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" -#include "ScriptedCreature.h" #include "scarlet_monastery.h" +#include "ScriptedCreature.h" +#include "ScriptMgr.h" -enum Yells +enum ArcanistDoanYells { - SAY_AGGRO = 0, - SAY_SPECIALAE = 1 + SAY_AGGRO = 0, + SAY_SPECIALAE = 1 }; -enum Spells +enum ArcanistDoanSpells { - SPELL_SILENCE = 8988, - SPELL_ARCANE_EXPLOSION = 9433, - SPELL_DETONATION = 9435, - SPELL_ARCANE_BUBBLE = 9438, - SPELL_POLYMORPH = 13323 + SPELL_SILENCE = 8988, + SPELL_ARCANE_EXPLOSION = 9433, + SPELL_DETONATION = 9435, + SPELL_ARCANE_BUBBLE = 9438, + SPELL_POLYMORPH = 13323 }; -enum Events +enum ArcanistDoanEvents { - EVENT_SILENCE = 1, - EVENT_ARCANE_EXPLOSION = 2, - EVENT_ARCANE_BUBBLE = 3, - EVENT_POLYMORPH = 4 + EVENT_SILENCE = 1, + EVENT_ARCANE_EXPLOSION, + EVENT_ARCANE_BUBBLE, + EVENT_POLYMORPH }; -class boss_arcanist_doan : public CreatureScript +struct boss_arcanist_doan : public BossAI { - public: - boss_arcanist_doan() : CreatureScript("boss_arcanist_doan") { } - - struct boss_arcanist_doanAI : public BossAI + boss_arcanist_doan(Creature* creature) : BossAI(creature, DATA_ARCANIST_DOAN) + { + _healthAbove50Pct = true; + } + + void Reset() override + { + _Reset(); + _healthAbove50Pct = true; + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + Talk(SAY_AGGRO); + + events.ScheduleEvent(EVENT_SILENCE, 15s); + events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 3s); + events.ScheduleEvent(EVENT_POLYMORPH, 30s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - boss_arcanist_doanAI(Creature* creature) : BossAI(creature, DATA_ARCANIST_DOAN) + switch (eventId) { - _healthAbove50Pct = true; + case EVENT_SILENCE: + DoCastVictim(SPELL_SILENCE); + events.Repeat(15s, 20s); + break; + case EVENT_ARCANE_EXPLOSION: + DoCastVictim(SPELL_ARCANE_EXPLOSION); + events.Repeat(8s); + break; + case EVENT_POLYMORPH: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 30.0f, true)) + DoCast(target, SPELL_POLYMORPH); + events.Repeat(20s); + break; + default: + break; } - void Reset() override - { - _Reset(); - _healthAbove50Pct = true; - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - Talk(SAY_AGGRO); - - events.ScheduleEvent(EVENT_SILENCE, 15 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 3s); - events.ScheduleEvent(EVENT_POLYMORPH, 30 * IN_MILLISECONDS); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SILENCE: - DoCastVictim(SPELL_SILENCE); - events.ScheduleEvent(EVENT_SILENCE, 15s, 20s); - break; - case EVENT_ARCANE_EXPLOSION: - DoCastVictim(SPELL_ARCANE_EXPLOSION); - events.ScheduleEvent(EVENT_SILENCE, 8s); - break; - case EVENT_POLYMORPH: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 30.0f, true)) - DoCast(target, SPELL_POLYMORPH); - events.ScheduleEvent(EVENT_POLYMORPH, 20s); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - if (_healthAbove50Pct && HealthBelowPct(50)) - { - _healthAbove50Pct = false; - Talk(SAY_SPECIALAE); - DoCast(me, SPELL_ARCANE_BUBBLE); - DoCastAOE(SPELL_DETONATION); - } - - DoMeleeAttackIfReady(); - } - - private: - bool _healthAbove50Pct; - }; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - CreatureAI* GetAI(Creature* creature) const override + if (_healthAbove50Pct && HealthBelowPct(50)) { - return GetScarletMonasteryAI<boss_arcanist_doanAI>(creature); + _healthAbove50Pct = false; + Talk(SAY_SPECIALAE); + DoCastSelf(SPELL_ARCANE_BUBBLE); + DoCastAOE(SPELL_DETONATION); } + + DoMeleeAttackIfReady(); + } + +private: + bool _healthAbove50Pct; }; void AddSC_boss_arcanist_doan() { - new boss_arcanist_doan(); + RegisterScarletMonasteryCreatureAI(boss_arcanist_doan); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_azshir_the_sleepless.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_azshir_the_sleepless.cpp index 6b2098cc390..3bf122c0575 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_azshir_the_sleepless.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_azshir_the_sleepless.cpp @@ -15,112 +15,96 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" -#include "ScriptedCreature.h" #include "scarlet_monastery.h" +#include "ScriptedCreature.h" +#include "ScriptMgr.h" -enum Spells +enum AzshirTheSleeplessSpells { - SPELL_CALL_OF_THE_GRAVE = 17831, - SPELL_TERRIFY = 7399, - SPELL_SOUL_SIPHON = 7290 + SPELL_CALL_OF_THE_GRAVE = 17831, + SPELL_TERRIFY = 7399, + SPELL_SOUL_SIPHON = 7290 }; -enum Events +enum AzshirTheSleeplessEvents { - EVENT_CALL_OF_GRAVE = 1, + EVENT_CALL_OF_GRAVE = 1, EVENT_TERRIFY, EVENT_SOUL_SIPHON }; -class boss_azshir_the_sleepless : public CreatureScript +struct boss_azshir_the_sleepless : public BossAI { - public: - boss_azshir_the_sleepless() : CreatureScript("boss_azshir_the_sleepless") { } - - struct boss_azshir_the_sleeplessAI : public BossAI + boss_azshir_the_sleepless(Creature* creature) : BossAI(creature, DATA_AZSHIR) + { + _siphon = false; + } + + void Reset() override + { + _Reset(); + _siphon = false; + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(EVENT_CALL_OF_GRAVE, 30s); + events.ScheduleEvent(EVENT_TERRIFY, 20s); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (!_siphon && me->HealthBelowPctDamaged(50, damage)) { - boss_azshir_the_sleeplessAI(Creature* creature) : BossAI(creature, DATA_AZSHIR) - { - _siphon = false; - } + DoCastVictim(SPELL_SOUL_SIPHON); + events.ScheduleEvent(EVENT_SOUL_SIPHON, 20s); + _siphon = true; + } + } - void Reset() override - { - _Reset(); - _siphon = false; - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_CALL_OF_GRAVE, 30s); - events.ScheduleEvent(EVENT_TERRIFY, 20s); - } + events.Update(diff); - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void DamageTaken(Unit* /*attacker*/, uint32& damage) override + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (!_siphon && me->HealthBelowPctDamaged(50, damage)) - { + case EVENT_CALL_OF_GRAVE: + DoCastVictim(SPELL_CALL_OF_THE_GRAVE); + events.Repeat(30s); + break; + case EVENT_TERRIFY: + DoCastVictim(SPELL_TERRIFY); + events.Repeat(20s); + break; + case EVENT_SOUL_SIPHON: DoCastVictim(SPELL_SOUL_SIPHON); - events.ScheduleEvent(EVENT_SOUL_SIPHON, 20s); - _siphon = true; - } + events.Repeat(20s); + break; + default: + break; } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_CALL_OF_GRAVE: - DoCastVictim(SPELL_CALL_OF_THE_GRAVE); - events.ScheduleEvent(EVENT_CALL_OF_GRAVE, 30s); - break; - case EVENT_TERRIFY: - DoCastVictim(SPELL_TERRIFY); - events.ScheduleEvent(EVENT_TERRIFY, 20s); - break; - case EVENT_SOUL_SIPHON: - DoCastVictim(SPELL_SOUL_SIPHON); - events.ScheduleEvent(EVENT_SOUL_SIPHON, 20s); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - private: - bool _siphon; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetScarletMonasteryAI<boss_azshir_the_sleeplessAI>(creature); - } +private: + bool _siphon; }; void AddSC_boss_azshir_the_sleepless() { - new boss_azshir_the_sleepless(); + RegisterScarletMonasteryCreatureAI(boss_azshir_the_sleepless); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp index 73506414fb4..667491f84c5 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp @@ -15,116 +15,101 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" -#include "ScriptedCreature.h" #include "scarlet_monastery.h" +#include "ScriptedCreature.h" +#include "ScriptMgr.h" -enum Yells +enum BloodmageThalnosYells { - SAY_AGGRO = 0, - SAY_HEALTH = 1, - SAY_KILL = 2 + SAY_AGGRO = 0, + SAY_HEALTH = 1, + SAY_KILL = 2 }; -enum Spells +enum BloodmageThalnosSpells { - SPELL_FLAMESHOCK = 8053, - SPELL_SHADOWBOLT = 1106, - SPELL_FLAMESPIKE = 8814, - SPELL_FIRENOVA = 16079 + SPELL_FLAMESHOCK = 8053, + SPELL_SHADOWBOLT = 1106, + SPELL_FLAMESPIKE = 8814, + SPELL_FIRENOVA = 16079 }; -enum Events +enum BloodmageThalnosEvents { - EVENT_FLAME_SHOCK = 1, + EVENT_FLAME_SHOCK = 1, EVENT_SHADOW_BOLT, EVENT_FLAME_SPIKE, EVENT_FIRE_NOVA }; -class boss_bloodmage_thalnos : public CreatureScript +struct boss_bloodmage_thalnos : public BossAI { - public: - boss_bloodmage_thalnos() : CreatureScript("boss_bloodmage_thalnos") { } - - struct boss_bloodmage_thalnosAI : public BossAI - { - boss_bloodmage_thalnosAI(Creature* creature) : BossAI(creature, DATA_BLOODMAGE_THALNOS) - { - _hpYell = false; - } + boss_bloodmage_thalnos(Creature* creature) : BossAI(creature, DATA_BLOODMAGE_THALNOS) + { + _hpYell = false; + } - void Reset() override - { - _hpYell = false; - _Reset(); - } + void Reset() override + { + _hpYell = false; + _Reset(); + } - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - _JustEngagedWith(); - events.ScheduleEvent(EVENT_FLAME_SHOCK, 10s); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 2s); - events.ScheduleEvent(EVENT_FLAME_SPIKE, 8s); - events.ScheduleEvent(EVENT_FIRE_NOVA, 40s); - } + void JustEngagedWith(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _JustEngagedWith(); + events.ScheduleEvent(EVENT_FLAME_SHOCK, 10s); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 2s); + events.ScheduleEvent(EVENT_FLAME_SPIKE, 8s); + events.ScheduleEvent(EVENT_FIRE_NOVA, 40s); + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + } - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_KILL); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage) override - { - if (!_hpYell && me->HealthBelowPctDamaged(35, damage)) - { - Talk(SAY_HEALTH); - _hpYell = true; - } - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_FLAME_SHOCK: - DoCastVictim(SPELL_FLAMESHOCK); - events.ScheduleEvent(EVENT_FLAME_SHOCK, 10s, 15s); - break; - case EVENT_SHADOW_BOLT: - DoCastVictim(SPELL_SHADOWBOLT); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 2s); - break; - case EVENT_FLAME_SPIKE: - DoCastVictim(SPELL_FLAMESPIKE); - events.ScheduleEvent(EVENT_FLAME_SPIKE, 30s); - break; - case EVENT_FIRE_NOVA: - DoCastVictim(SPELL_FIRENOVA); - events.ScheduleEvent(EVENT_FIRE_NOVA, 40s); - break; - default: - break; - } - } - - private: - bool _hpYell; - }; + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (!_hpYell && me->HealthBelowPctDamaged(35, damage)) + { + Talk(SAY_HEALTH); + _hpYell = true; + } + } - CreatureAI* GetAI(Creature* creature) const override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - return GetScarletMonasteryAI<boss_bloodmage_thalnosAI>(creature); + case EVENT_FLAME_SHOCK: + DoCastVictim(SPELL_FLAMESHOCK); + events.Repeat(10s, 15s); + break; + case EVENT_SHADOW_BOLT: + DoCastVictim(SPELL_SHADOWBOLT); + events.Repeat(2s); + break; + case EVENT_FLAME_SPIKE: + DoCastVictim(SPELL_FLAMESPIKE); + events.Repeat(30s); + break; + case EVENT_FIRE_NOVA: + DoCastVictim(SPELL_FIRENOVA); + events.Repeat(40s); + break; + default: + break; } + } + +private: + bool _hpYell; }; void AddSC_boss_bloodmage_thalnos() { - new boss_bloodmage_thalnos(); + RegisterScarletMonasteryCreatureAI(boss_bloodmage_thalnos); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp index 56ed8179a18..0bd6a25fe84 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp @@ -16,14 +16,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Headless_Horseman -SD%Complete: -SDComment: -SDCategory: Scarlet Monastery -EndScriptData */ - -#include "ScriptMgr.h" +#include "scarlet_monastery.h" #include "GameObject.h" #include "GameObjectAI.h" #include "Group.h" @@ -33,107 +26,143 @@ EndScriptData */ #include "MotionMaster.h" #include "ObjectAccessor.h" #include "Player.h" -#include "scarlet_monastery.h" #include "ScriptedCreature.h" +#include "ScriptMgr.h" #include "SpellInfo.h" #include "SpellMgr.h" #include "TemporarySummon.h" +#include "Timer.h" -//this texts are already used by 3975 and 3976 -enum Says +// this texts are already used by 3975 and 3976 +enum HeadlessHorsemanSays { - SAY_LOST_HEAD = 0, - SAY_PLAYER_DEATH = 1, - - SAY_ENTRANCE = 0, - SAY_REJOINED = 1, - SAY_CONFLAGRATION = 2, - SAY_SPROUTING_PUMPKINS = 3, - SAY_DEATH = 4, + SAY_LOST_HEAD = 0, + SAY_PLAYER_DEATH = 1, + + SAY_ENTRANCE = 0, + SAY_REJOINED = 1, + SAY_CONFLAGRATION = 2, + SAY_SPROUTING_PUMPKINS = 3, + SAY_DEATH = 4, }; -uint32 RandomLaugh[] = {11965, 11975, 11976}; +std::vector<uint32> HeadlessHorsemanRandomLaughSound = { 11965u, 11975u, 11976u }; -enum Entry +enum HeadlessHorsemanEntry { - HH_MOUNTED = 23682, - HH_DISMOUNTED = 23800, - HEAD = 23775, - PULSING_PUMPKIN = 23694, - PUMPKIN_FIEND = 23545, - HELPER = 23686, - WISP_INVIS = 24034 + NPC_HEADLESS_HORSEMAN_MOUNTED = 23682, + NPC_HEADLESS_HORSEMAN_DISMOUNTED = 23800, + NPC_HEADLESS_HORSEMAN_HEAD = 23775, + NPC_PULSING_PUMPKIN = 23694, + NPC_PUMPKIN_FIEND = 23545, + NPC_HELPER = 23686, + NPC_WISP_INVIS = 24034 }; -enum Spells +enum HeadlessHorsemanSpells { - SPELL_CLEAVE = 42587, - SPELL_CONFLAGRATION = 42380, //Phase 2, can't find real spell(Dim Fire?) - // SPELL_CONFL_SPEED = 22587, //8% increase speed, value 22587 from SPELL_CONFLAGRATION mains that spell? - SPELL_SUMMON_PUMPKIN = 42394, - - SPELL_WHIRLWIND = 43116, - SPELL_IMMUNE = 42556, - SPELL_BODY_REGEN = 42403, - SPELL_CONFUSE = 43105, - - SPELL_FLYING_HEAD = 42399, //visual flying head - SPELL_HEAD = 42413, //visual buff, "head" - SPELL_HEAD_IS_DEAD = 42428, //at killing head, Phase 3 - - SPELL_PUMPKIN_AURA = 42280, - SPELL_PUMPKIN_AURA_GREEN = 42294, - SPELL_SQUASH_SOUL = 42514, - SPELL_SPROUTING = 42281, - SPELL_SPROUT_BODY = 42285, - - //Effects - SPELL_RHYME_BIG = 42909, - // SPELL_RHYME_SMALL = 42910, - SPELL_HEAD_SPEAKS = 43129, - SPELL_HEAD_LANDS = 42400, - SPELL_BODY_FLAME = 42074, - SPELL_HEAD_FLAME = 42971, - // SPELL_ENRAGE_VISUAL = 42438, // he uses this spell? - SPELL_WISP_BLUE = 42821, - SPELL_WISP_FLIGHT_PORT = 42818, - // SPELL_WISP_INVIS = 42823, - SPELL_SMOKE = 42355, - SPELL_DEATH = 42566 //not correct spell + SPELL_CONFLAGRATION = 42380, + SPELL_HORSEMANS_CONFLAGRATION = 42381, // Triggered from SPELL_CONFLAGRATION + SPELL_HORSEMANS_SUMMON = 42394, + SPELL_HEADLESS_HORSEMAN_CLIMAX___HORSEMANS_WHIRLWIND = 43116, + SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_REMOVED_ON_DEATH = 42556, + SPELL_HORSEMANS_CLEAVE = 42587, // Triggered from SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_REMOVED_ON_DEATH + SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN = 42403, + SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_CONFUSE_ONLY_REMOVED_ON_DEATH = 43105, + SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD = 42399, // Visual flying head + SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_VISUAL = 42413, // Visual buff, "head" + SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_IS_DEAD = 42428, // At killing head, Phase 3 + SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_IS_DEAD_TRIGGERED = 42566, // Triggered from SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_IS_DEAD + SPELL_PUMPKIN_LIFE_CYCLE = 42280, + SPELL_HEADLESS_HORSEMAN___PUMPKIN_AURA = 42294, + SPELL_SQUASH_SOUL = 42514, + SPELL_SPROUTING = 42281, + SPELL_SPROUT_BODY = 42285, + SPELL_HEADLESS_HORSEMAN_CLIMAX___SUMMONING_RHYME_SHAKE_MEDIUM = 42909, + SPELL_HEADLESS_HORSEMAN_CLIMAX___SUMMONING_RHYME_SHAKE_SMALL = 42910, + SPELL_HEADLESS_HORSEMAN___SPEAKS = 43129, + SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_LANDS = 42400, + SPELL_HEADLESS_HORSEMAN___FIRE = 42074, + SPELL_HEADLESS_HORSEMAN___BURNING_COSMETIC = 42971, + SPELL_HEADLESS_HORSEMAN_CLIMAX___ENRAGED_VISUAL = 42438, // Is this used? + SPELL_HEADLESS_HORSEMAN___WISP_FLIGHT_MISSILE = 42821, + SPELL_HEADLESS_HORSEMAN___WISP_FLIGHT_PORT = 42818, // Triggered from SPELL_HEADLESS_HORSEMAN___WISP_FLIGHT_MISSILE + SPELL_HEADLESS_HORSEMAN___WISP_INVIS = 42823, + SPELL_HEADLESS_HORSEMAN___SMOKE = 42355, +}; + +enum HeadlessHorsemanMisc +{ + DISPLAYID_INVIS_WISP_MAN = 2027, + DISPLAYID_INVIS_WISP_INVISIBLE = 21908, + + DATA_INVIS_WISP_CREATURE_TYPE = 0, + DATA_HEAD_TALK, + DATA_HEAD_PHASE, + + INVIS_WISP_CREATURE_TYPE_PUMPKIN = 1, + INVIS_WISP_CREATURE_TYPE_FLAME, + INVIS_WISP_CREATURE_TYPE_SMOKE, + INVIS_WISP_CREATURE_TYPE_BLUE, + + ACTION_HEAD_RETURN_TO_BODY = 0, + ACTION_HEAD_KILLED, + ACTION_HORSEMAN_EVENT_START, + + PHASE_HEAD_1 = 1, + PHASE_HEAD_2, + PHASE_HEAD_3, + + PHASE_BODY_0 = 0, + PHASE_BODY_1, + PHASE_BODY_2, + PHASE_BODY_3, + + TASK_GROUP_COMBAT = 1, + TASK_GROUP_WITHOUT_HEAD, + + POINT_HORSEMAN_0 = 0, + POINT_HORSEMAN_1 = 1, + POINT_HORSEMAN_6 = 6, + POINT_HORSEMAN_19 = 19, + POINT_HORSEMAN_20 = 20, + + LFG_DUNGEONID_THE_HEADLESS_HORSEMAN = 285, }; -Position const FlightPoint[]= +std::vector<Position> const HeadlessHorsemanFlightPoints = { - {1754.00f, 1346.00f, 17.50f}, - {1765.00f, 1347.00f, 19.00f}, - {1784.00f, 1346.80f, 25.40f}, - {1803.30f, 1347.60f, 33.00f}, - {1824.00f, 1350.00f, 42.60f}, - {1838.80f, 1353.20f, 49.80f}, - {1852.00f, 1357.60f, 55.70f}, - {1861.30f, 1364.00f, 59.40f}, - {1866.30f, 1374.80f, 61.70f}, - {1864.00f, 1387.30f, 63.20f}, - {1854.80f, 1399.40f, 64.10f}, - {1844.00f, 1406.90f, 64.10f}, - {1824.30f, 1411.40f, 63.30f}, - {1801.00f, 1412.30f, 60.40f}, - {1782.00f, 1410.10f, 55.50f}, - {1770.50f, 1405.20f, 50.30f}, - {1765.20f, 1400.70f, 46.60f}, - {1761.40f, 1393.40f, 41.70f}, - {1759.10f, 1386.70f, 36.60f}, - {1757.80f, 1378.20f, 29.00f}, - {1758.00f, 1367.00f, 19.51f} + { 1754.00f, 1346.00f, 17.50f }, + { 1765.00f, 1347.00f, 19.00f }, + { 1784.00f, 1346.80f, 25.40f }, + { 1803.30f, 1347.60f, 33.00f }, + { 1824.00f, 1350.00f, 42.60f }, + { 1838.80f, 1353.20f, 49.80f }, + { 1852.00f, 1357.60f, 55.70f }, + { 1861.30f, 1364.00f, 59.40f }, + { 1866.30f, 1374.80f, 61.70f }, + { 1864.00f, 1387.30f, 63.20f }, + { 1854.80f, 1399.40f, 64.10f }, + { 1844.00f, 1406.90f, 64.10f }, + { 1824.30f, 1411.40f, 63.30f }, + { 1801.00f, 1412.30f, 60.40f }, + { 1782.00f, 1410.10f, 55.50f }, + { 1770.50f, 1405.20f, 50.30f }, + { 1765.20f, 1400.70f, 46.60f }, + { 1761.40f, 1393.40f, 41.70f }, + { 1759.10f, 1386.70f, 36.60f }, + { 1757.80f, 1378.20f, 29.00f }, + { 1758.00f, 1367.00f, 19.51f } }; -Position const Spawn[]= +std::vector<Position> const HeadlessHorsemanSpawnPoints = { - {1776.27f, 1348.74f, 19.20f}, //spawn point for pumpkin shrine mob - {1765.28f, 1347.46f, 17.55f} //spawn point for smoke + { 1776.27f, 1348.74f, 19.20f }, // spawn point for pumpkin shrine mob + { 1765.28f, 1347.46f, 17.55f } // spawn point for smoke }; -static char const* Text[]= +//@todo Dear Lord, please someone have mercy and let this die soon +static char const* HeadlessHorsemanInitialPlayerTexts[] = { "Horseman rise...", "Your time is nigh...", @@ -141,805 +170,755 @@ static char const* Text[]= "Now, know demise!" }; -class npc_wisp_invis : public CreatureScript +struct npc_wisp_invis : public ScriptedAI { -public: - npc_wisp_invis() : CreatureScript("npc_wisp_invis") { } - - CreatureAI* GetAI(Creature* creature) const override + npc_wisp_invis(Creature* creature) : ScriptedAI(creature), _timer(0), _creatureType(0), _firstSpell(0), _secondSpell(0) { - return GetScarletMonasteryAI<npc_wisp_invisAI>(creature); + creature->SetDisplayId(DISPLAYID_INVIS_WISP_INVISIBLE); } - struct npc_wisp_invisAI : public ScriptedAI + void SetData(uint32 type, uint32 value) override { - npc_wisp_invisAI(Creature* creature) : ScriptedAI(creature) - { - Creaturetype = delay = _spell = _spell2 = 0; - } + if (type != DATA_INVIS_WISP_CREATURE_TYPE) + return; + + switch (_creatureType = value) + { + case INVIS_WISP_CREATURE_TYPE_PUMPKIN: + _firstSpell = SPELL_HEADLESS_HORSEMAN___PUMPKIN_AURA; + break; + case INVIS_WISP_CREATURE_TYPE_FLAME: + _timer.Reset(15 * IN_MILLISECONDS); + _firstSpell = SPELL_HEADLESS_HORSEMAN___FIRE; + _secondSpell = SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_IS_DEAD; + break; + case INVIS_WISP_CREATURE_TYPE_SMOKE: + _timer.Reset(15 * IN_MILLISECONDS); + _firstSpell = SPELL_HEADLESS_HORSEMAN___SMOKE; + break; + case INVIS_WISP_CREATURE_TYPE_BLUE: + _timer.Reset(7 * IN_MILLISECONDS); + _secondSpell = SPELL_HEADLESS_HORSEMAN___WISP_FLIGHT_MISSILE; + break; + default: + break; + } + if (_firstSpell) + DoCastSelf(_firstSpell); + } - uint32 Creaturetype; - uint32 delay; - uint32 _spell; - uint32 _spell2; - void Reset() override { } - void JustEngagedWith(Unit* /*who*/) override { } - void SetType(uint32 _type) - { - switch (Creaturetype = _type) - { - case 1: - _spell = SPELL_PUMPKIN_AURA_GREEN; - break; - case 2: - delay = 15000; - _spell = SPELL_BODY_FLAME; - _spell2 = SPELL_DEATH; - break; - case 3: - delay = 15000; - _spell = SPELL_SMOKE; - break; - case 4: - delay = 7000; - _spell2 = SPELL_WISP_BLUE; - break; - } - if (_spell) - DoCast(me, _spell); - } + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override + { + if (spellInfo->Id == SPELL_HEADLESS_HORSEMAN___WISP_FLIGHT_PORT && _creatureType == 4) + me->SetDisplayId(DISPLAYID_INVIS_WISP_MAN); + } - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - if (spell->Id == SPELL_WISP_FLIGHT_PORT && Creaturetype == 4) - me->SetDisplayId(2027); - } + void MoveInLineOfSight(Unit* who) override + { + if (!who || _creatureType != INVIS_WISP_CREATURE_TYPE_PUMPKIN || !who->isTargetableForAttack()) + return; - void MoveInLineOfSight(Unit* who) override - { - if (!who || Creaturetype != 1 || !who->isTargetableForAttack()) - return; + if (me->IsWithinDist(who, 0.1f, false) && !who->HasAura(SPELL_SQUASH_SOUL)) + DoCast(who, SPELL_SQUASH_SOUL); + } - if (me->IsWithinDist(who, 0.1f, false) && !who->HasAura(SPELL_SQUASH_SOUL)) - DoCast(who, SPELL_SQUASH_SOUL); - } + void UpdateAI(uint32 diff) override + { + if (_timer.Passed()) + return; - void UpdateAI(uint32 diff) override + _timer.Update(diff); + if (_timer.Passed()) { - if (delay) - { - if (delay <= diff) - { - me->RemoveAurasDueToSpell(SPELL_SMOKE); - if (_spell2) - DoCast(me, _spell2); - delay = 0; - } else delay -= diff; - } + me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN___SMOKE); + if (_secondSpell) + DoCast(me, _secondSpell); + _timer.Reset(0); } - }; + } + +private: + TimeTrackerSmall _timer; + uint32 _creatureType; + uint32 _firstSpell; + uint32 _secondSpell; }; -class npc_head : public CreatureScript +struct npc_head : public ScriptedAI { -public: - npc_head() : CreatureScript("npc_head") { } - - CreatureAI* GetAI(Creature* creature) const override + npc_head(Creature* creature) : ScriptedAI(creature), _laughTimer(urand(15 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)) { - return GetScarletMonasteryAI<npc_headAI>(creature); + creature->SetReactState(REACT_PASSIVE); + Initialize(); } - struct npc_headAI : public ScriptedAI + void Initialize() { - npc_headAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - Phase = 0; - bodyGUID.Clear(); - die = false; - withbody = true; - wait = 1000; - laugh = urand(15000, 30000); - } - - ObjectGuid bodyGUID; - - uint32 Phase; - uint32 laugh; - uint32 wait; - - bool withbody; - bool die; + _bodyGUID = ObjectGuid::Empty; + _phase = 0; + _withBody = true; + _die = false; + } - void Reset() override - { - Initialize(); - } + void Reset() override + { + Initialize(); + _laughTimer.Reset(urand(15 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); + _scheduler.CancelAll(); - void JustEngagedWith(Unit* /*who*/) override { } + // Just to be sure it's MOTION_SLOT_DEFAULT is static + me->GetMotionMaster()->MoveIdle(); + } - void SaySound(uint8 textEntry, Unit* target = 0) + void SetData(uint32 type, uint32 value) override + { + switch (type) { - Talk(textEntry, target); - - //DoCast(me, SPELL_HEAD_SPEAKS, true); - if (Creature* speaker = DoSpawnCreature(HELPER, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 1000)) - speaker->CastSpell(speaker, SPELL_HEAD_SPEAKS, false); - laugh += 3000; + case DATA_HEAD_TALK: + DoTalk(value); + break; + case DATA_HEAD_PHASE: + _phase = value; + break; + default: + break; } + } - void DamageTaken(Unit* /*done_by*/, uint32 &damage) override - { - if (withbody) - return; - - switch (Phase) - { - case 1: - if (me->HealthBelowPctDamaged(67, damage)) - Disappear(); - break; - case 2: - if (me->HealthBelowPctDamaged(34, damage)) - Disappear(); - break; - case 3: - if (damage >= me->GetHealth()) - { - die = true; - withbody = true; - wait = 300; - damage = me->GetHealth() - me->CountPctFromMaxHealth(1); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->StopMoving(); - //me->GetMotionMaster()->MoveIdle(); - DoCast(me, SPELL_HEAD_IS_DEAD); - } - break; - } - } + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (_withBody) + return; - void SpellHit(Unit* caster, SpellInfo const* spell) override + if (_die) { - if (!withbody) - return; - - if (spell->Id == SPELL_FLYING_HEAD) - { - if (Phase < 3) - ++Phase; - else - Phase = 3; - - withbody = false; - if (!bodyGUID) - bodyGUID = caster->GetGUID(); - me->RemoveAllAuras(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - DoCast(me, SPELL_HEAD_LANDS, true); - DoCast(me, SPELL_HEAD, false); - SaySound(SAY_LOST_HEAD); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFleeing(caster->GetVictim()); - } + damage = 0; + return; } - void Disappear(); - void UpdateAI(uint32 diff) override + switch (_phase) { - if (!withbody) - { - if (wait <= diff) + case PHASE_HEAD_1: + if (me->HealthBelowPctDamaged(67, damage)) + ReturnToBody(true); + break; + case PHASE_HEAD_2: + if (me->HealthBelowPctDamaged(34, damage)) + ReturnToBody(true); + break; + case PHASE_HEAD_3: + if (!_die && damage >= me->GetHealth()) { - wait = 1000; - if (!me->GetVictim()) - return; + _die = true; + damage = 0; + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFleeing(me->GetVictim()); - } - else wait -= diff; - if (laugh <= diff) - { - laugh = urand(15000, 30000); - DoPlaySoundToSet(me, RandomLaugh[urand(0, 2)]); - //DoCast(me, SPELL_HEAD_SPEAKS, true); //this spell remove buff "head" - Creature* speaker = DoSpawnCreature(HELPER, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 1000); - if (speaker) - speaker->CastSpell(speaker, SPELL_HEAD_SPEAKS, false); - } - else laugh -= diff; - } - else - { - if (die) - { - if (wait <= diff) + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_IS_DEAD); + + _scheduler.Schedule(3s, [this](TaskContext /*context*/) { - die = false; - if (Unit* body = ObjectAccessor::GetUnit(*me, bodyGUID)) + if (Unit* body = ObjectAccessor::GetUnit(*me, _bodyGUID)) body->KillSelf(); me->KillSelf(); - } - else wait -= diff; + }); } - } + break; + default: + break; } - }; -}; - -class boss_headless_horseman : public CreatureScript -{ -public: - boss_headless_horseman() : CreatureScript("boss_headless_horseman") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetScarletMonasteryAI<boss_headless_horsemanAI>(creature); } - struct boss_headless_horsemanAI : public ScriptedAI + void SpellHit(Unit* caster, SpellInfo const* spell) override { - boss_headless_horsemanAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - id = 0; - whirlwind = 0; - wp_reached = false; - } + if (!_withBody) + return; - void Initialize() + if (spell->Id == SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD) { - Phase = 1; - conflagrate = 15000; - summonadds = 15000; - laugh = urand(16000, 20000); - cleave = 2000; - regen = 1000; - burn = 6000; - count = 0; - say_timer = 3000; - - withhead = true; - returned = true; - burned = false; - IsFlying = false; - } - - InstanceScript* instance; + _withBody = false; - ObjectGuid headGUID; - ObjectGuid PlayerGUID; + if (!_bodyGUID) + _bodyGUID = caster->GetGUID(); - uint32 Phase; - uint32 id; - uint32 count; - uint32 say_timer; + me->RemoveAllAuras(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->GetMotionMaster()->Clear(); - uint32 conflagrate; - uint32 summonadds; - uint32 cleave; - uint32 regen; - uint32 whirlwind; - uint32 laugh; - uint32 burn; + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_LANDS, true); + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_VISUAL); - bool withhead; - bool returned; - bool IsFlying; - bool wp_reached; - bool burned; + DoTalk(SAY_LOST_HEAD); - void Reset() override - { - Initialize(); - DoCast(me, SPELL_HEAD); - if (headGUID) + _scheduler.Schedule(2s, [caster, this](TaskContext /*context*/) { - if (Creature* Head = ObjectAccessor::GetCreature((*me), headGUID)) - Head->DespawnOrUnsummon(); - - headGUID.Clear(); - } - - me->SetImmuneToPC(false); - //instance->SetBossState(DATA_HORSEMAN_EVENT, NOT_STARTED); + me->GetMotionMaster()->MoveFleeing(caster); + }); } + } - void FlyMode() + void DoAction(int32 param) override + { + switch (param) { - me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->SetDisableGravity(true); - me->SetSpeedRate(MOVE_WALK, 5.0f); - wp_reached = false; - count = 0; - say_timer = 3000; - id = 0; - Phase = 0; + case ACTION_HEAD_RETURN_TO_BODY: + ReturnToBody(false); + break; + default: + break; } + } - void MovementInform(uint32 type, uint32 i) override - { - if (type != POINT_MOTION_TYPE || !IsFlying || i != id) - return; + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); - wp_reached = true; + if (_withBody) + return; - switch (id) - { - case 0: - me->SetVisible(true); - break; - case 1: - { - if (Creature* smoke = me->SummonCreature(HELPER, Spawn[1], TEMPSUMMON_TIMED_DESPAWN, 20000)) - ENSURE_AI(npc_wisp_invis::npc_wisp_invisAI, smoke->AI())->SetType(3); - DoCast(me, SPELL_RHYME_BIG); - break; - } - case 6: - instance->SetData(DATA_PUMPKIN_SHRINE, 0); //hide gameobject - break; - case 19: - me->SetDisableGravity(false); - break; - case 20: - { - Phase = 1; - IsFlying = false; - wp_reached = false; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - SaySound(SAY_ENTRANCE); - if (Unit* player = ObjectAccessor::GetUnit(*me, PlayerGUID)) - DoStartMovement(player); - break; - } - } - ++id; - } + if (!_laughTimer.Passed()) + _laughTimer.Update(diff); - void JustEngagedWith(Unit* /*who*/) override + if (_laughTimer.Passed()) { - instance->SetBossState(DATA_HORSEMAN_EVENT, IN_PROGRESS); - DoZoneInCombat(); - } + _laughTimer.Reset(urand(15 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); - void AttackStart(Unit* who) override - { - ScriptedAI::AttackStart(who); - } + DoPlaySoundToSet(me, Trinity::Containers::SelectRandomContainerElement(HeadlessHorsemanRandomLaughSound)); - void MoveInLineOfSight(Unit* who) override - { - if (withhead && Phase != 0) - ScriptedAI::MoveInLineOfSight(who); + if (Creature* speaker = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_DESPAWN, 1 * IN_MILLISECONDS)) + speaker->CastSpell(speaker, SPELL_HEADLESS_HORSEMAN___SPEAKS, false); } + } - void KilledUnit(Unit* player) override - { - if (player->GetTypeId() == TYPEID_PLAYER) - { - if (withhead) - SaySound(SAY_PLAYER_DEATH); - //maybe possible when player dies from conflagration - else if (Creature* Head = ObjectAccessor::GetCreature((*me), headGUID)) - ENSURE_AI(npc_head::npc_headAI, Head->AI())->SaySound(SAY_PLAYER_DEATH); - } - } +private: + void ReturnToBody(bool advance) + { + if (_withBody || _bodyGUID.IsEmpty()) + return; - void SaySound(uint8 textEntry, Unit* target = 0) + Creature* body = ObjectAccessor::GetCreature(*me, _bodyGUID); + if (!body || !body->IsAlive()) { - Talk(textEntry, target); - laugh += 4000; + me->DespawnOrUnsummon(); + return; } - Player* SelectRandomPlayer(float range = 0.0f, bool checkLoS = true) - { - Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); - if (PlayerList.isEmpty()) - return nullptr; + _withBody = true; + me->RemoveAllAuras(); + me->SetFullHealth(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->GetMotionMaster()->MoveIdle(); - std::list<Player*> temp; - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if ((me->IsWithinLOSInMap(i->GetSource()) || !checkLoS) && me->GetVictim() != i->GetSource() && - me->IsWithinDistInMap(i->GetSource(), range) && i->GetSource()->IsAlive()) - temp.push_back(i->GetSource()); + if (advance) + body->AI()->DoAction(ACTION_HEAD_KILLED); - if (!temp.empty()) - { - std::list<Player*>::const_iterator j = temp.begin(); - advance(j, rand32() % temp.size()); - return (*j); - } - return nullptr; - } + body->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_REMOVED_ON_DEATH); // hack, SpellHit doesn't calls if body has immune aura + DoCast(body, SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD); + } - void SpellHitTarget(Unit* unit, SpellInfo const* spell) override - { - if (spell->Id == SPELL_CONFLAGRATION && unit->HasAura(SPELL_CONFLAGRATION)) - SaySound(SAY_CONFLAGRATION, unit); - } + void DoTalk(uint32 entry) + { + Talk(entry); + _laughTimer.Reset(3 * IN_MILLISECONDS); - void JustDied(Unit* /*killer*/) override + if (Creature* speaker = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_DESPAWN, 1 * IN_MILLISECONDS)) + speaker->CastSpell(speaker, SPELL_HEADLESS_HORSEMAN___SPEAKS, false); + } + + TaskScheduler _scheduler; + TimeTrackerSmall _laughTimer; + ObjectGuid _bodyGUID; + uint32 _phase; + bool _withBody; + bool _die; +}; + +struct boss_headless_horseman : public ScriptedAI +{ + boss_headless_horseman(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _laughTimer(0), _phase(0), _id(0) + { + Initialize(); + + _scheduler.SetValidator([this] { - me->StopMoving(); - //me->GetMotionMaster()->MoveIdle(); - SaySound(SAY_DEATH); - if (Creature* flame = DoSpawnCreature(HELPER, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 60000)) - flame->CastSpell(flame, SPELL_BODY_FLAME, false); - if (Creature* wisp = DoSpawnCreature(WISP_INVIS, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 60000)) - ENSURE_AI(npc_wisp_invis::npc_wisp_invisAI, wisp->AI())->SetType(4); - instance->SetBossState(DATA_HORSEMAN_EVENT, DONE); - - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - if (!players.isEmpty()) - { - if (Group* group = players.begin()->GetSource()->GetGroup()) - if (group->isLFGGroup()) - sLFGMgr->FinishDungeon(group->GetGUID(), 285, me->GetMap()); - } - } + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } + + void Initialize() + { + _withHead = true; + } + + void InitializeAI() override + { + me->SetImmuneToPC(false); + ScriptedAI::InitializeAI(); + } - void SpellHit(Unit* caster, SpellInfo const* spell) override + void Reset() override + { + _laughTimer.Reset(0); + Initialize(); + + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_VISUAL); + if (_headGUID) { - if (withhead) - return; + if (Creature* head = ObjectAccessor::GetCreature(*me, _headGUID)) + head->DespawnOrUnsummon(); - if (spell->Id == SPELL_FLYING_HEAD) - { - if (Phase < 3) - ++Phase; - else - Phase = 3; - withhead = true; - me->RemoveAllAuras(); - me->SetName("Headless Horseman"); - me->SetFullHealth(); - SaySound(SAY_REJOINED); - DoCast(me, SPELL_HEAD); - caster->GetMotionMaster()->Clear(); - caster->GetMotionMaster()->MoveFollow(me, 6, float(urand(0, 5))); - } + _headGUID.Clear(); } - void DamageTaken(Unit* /*done_by*/, uint32 &damage) override - { - if (damage >= me->GetHealth() && withhead) - { - withhead = false; - returned = false; - damage = me->GetHealth() - me->CountPctFromMaxHealth(1); - me->RemoveAllAuras(); - me->SetName("Headless Horseman, Unhorsed"); + me->SetImmuneToPC(false); - if (!headGUID) - headGUID = DoSpawnCreature(HEAD, float(rand32() % 6), float(rand32() % 6), 0, 0, TEMPSUMMON_DEAD_DESPAWN, 0)->GetGUID(); + // Just to be sure it's MOTION_SLOT_DEFAULT is static + me->GetMotionMaster()->MoveIdle(); - Unit* Head = ObjectAccessor::GetUnit(*me, headGUID); - if (Head && Head->IsAlive()) - { - Head->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - //Head->CastSpell(Head, SPELL_HEAD_INVIS, false); - me->InterruptNonMeleeSpells(false); - DoCast(me, SPELL_IMMUNE, true); - DoCast(me, SPELL_BODY_REGEN, true); - DoCast(Head, SPELL_FLYING_HEAD, true); - DoCast(me, SPELL_CONFUSE, false); //test - whirlwind = urand(4000, 8000); - regen = 0; - } - } - } + _instance->SetBossState(DATA_HORSEMAN_EVENT, NOT_STARTED); + } + + void EnterEvadeMode(EvadeReason why) override + { + _phase = PHASE_BODY_1; + ScriptedAI::EnterEvadeMode(why); + } - void UpdateAI(uint32 diff) override + void DoAction(int32 param) override + { + switch (param) { - if (withhead) - { - switch (Phase) + case ACTION_HORSEMAN_EVENT_START: + me->SetVisible(false); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetCanFly(true); + me->SetWalk(false); + + _id = 0; + _phase = PHASE_BODY_0; + + _scheduler.Schedule(3s, [this](TaskContext talkContext) { - case 0: + if (talkContext.GetRepeatCounter() < 3) { - if (!IsFlying) + if (me->GetMap()->HavePlayers()) { - if (say_timer <= diff) - { - say_timer = 3000; - Player* player = SelectRandomPlayer(100.0f, false); - if (count < 3) - { - if (player) - player->Say(Text[count], LANG_UNIVERSAL); - } - else - { - DoCast(me, SPELL_RHYME_BIG); - if (player) - { - player->Say(Text[count], LANG_UNIVERSAL); - player->HandleEmoteCommand(ANIM_EMOTE_SHOUT); - } - wp_reached = true; - IsFlying = true; - count = 0; - break; - } - ++count; - } - else say_timer -= diff; + RefManager<Map, Player> const& players = me->GetMap()->GetPlayers(); + LinkedListHead::Iterator<Reference<Map, Player> const> it = players.RefManager<Map, Player>::begin(); + std::advance(it, urand(0, uint32(me->GetMap()->GetPlayers().getSize()) - 1)); + if (Player* player = it->GetSource()) + player->Say(HeadlessHorsemanInitialPlayerTexts[talkContext.GetRepeatCounter()], LANG_UNIVERSAL); } - else + talkContext.Repeat(3s); + } + else + { + DoCast(SPELL_HEADLESS_HORSEMAN_CLIMAX___SUMMONING_RHYME_SHAKE_MEDIUM); + if (me->GetMap()->HavePlayers()) { - if (wp_reached) + RefManager<Map, Player> const& players = me->GetMap()->GetPlayers(); + LinkedListHead::Iterator<Reference<Map, Player> const> it = players.RefManager<Map, Player>::begin(); + std::advance(it, urand(0, uint32(me->GetMap()->GetPlayers().getSize()) - 1)); + if (Player* player = it->GetSource()) { - wp_reached = false; - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(id, FlightPoint[id]); + player->Say(HeadlessHorsemanInitialPlayerTexts[talkContext.GetRepeatCounter()], LANG_UNIVERSAL); + player->HandleEmoteCommand(ANIM_EMOTE_SHOUT); } } + + me->GetMotionMaster()->MovePoint(_id, HeadlessHorsemanFlightPoints[_id]); } - break; - case 1: - if (burned) - break; - if (burn <= diff) - { - if (Creature* flame = me->SummonCreature(HELPER, Spawn[0], TEMPSUMMON_TIMED_DESPAWN, 17000)) - ENSURE_AI(npc_wisp_invis::npc_wisp_invisAI, flame->AI())->SetType(2); - burned = true; - } - else burn -= diff; - break; - case 2: - if (conflagrate <= diff) - { - if (Unit* player = SelectRandomPlayer(30.0f)) - DoCast(player, SPELL_CONFLAGRATION, false); - conflagrate = urand(10000, 16000); - } - else conflagrate -= diff; - break; - case 3: - if (summonadds <= diff) - { - me->InterruptNonMeleeSpells(false); - DoCast(me, SPELL_SUMMON_PUMPKIN); - SaySound(SAY_SPROUTING_PUMPKINS); - summonadds = urand(25000, 35000); - } - else summonadds -= diff; - break; - } + }); + break; + case ACTION_HEAD_KILLED: + if (_phase < 3) + ++_phase; + else + _phase = 3; + break; + default: + break; + } + } - if (laugh <= diff) - { - laugh = urand(11000, 22000); - DoPlaySoundToSet(me, RandomLaugh[rand32() % 3]); - } - else laugh -= diff; + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE || _phase != PHASE_BODY_0 || id != _id) + return; + + switch (id) + { + case POINT_HORSEMAN_0: + me->SetVisible(true); + break; + case POINT_HORSEMAN_1: + if (Creature* smoke = me->SummonCreature(NPC_HELPER, HeadlessHorsemanSpawnPoints[1], TEMPSUMMON_TIMED_DESPAWN, 20 * IN_MILLISECONDS)) + smoke->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_SMOKE); + DoCast(SPELL_HEADLESS_HORSEMAN_CLIMAX___SUMMONING_RHYME_SHAKE_MEDIUM); + break; + case POINT_HORSEMAN_6: + _instance->HandleGameObject(ObjectGuid::Empty, false, _instance->GetGameObject(DATA_PUMPKIN_SHRINE)); + break; + case POINT_HORSEMAN_19: + me->SetCanFly(false); + break; + case POINT_HORSEMAN_20: + _phase = PHASE_BODY_1; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetHomePosition(me->GetPosition()); + DoTalk(SAY_ENTRANCE); + DoZoneInCombat(); + break; + default: + break; + } + + ++_id; + if (_id <= POINT_HORSEMAN_20) + me->GetMotionMaster()->MovePoint(_id, HeadlessHorsemanFlightPoints[_id]); + } - if (UpdateVictim()) - { - DoMeleeAttackIfReady(); - if (cleave <= diff) - { - DoCastVictim(SPELL_CLEAVE); - cleave = urand(2000, 6000); //1 cleave per 2.0f-6.0fsec - } - else cleave -= diff; - } - } - else - { - if (regen <= diff) - { - regen = 1000; //"body calls head" - if (me->IsFullHealth() && !returned) - { - if (Phase > 1) - --Phase; - else - Phase = 1; - Creature* Head = ObjectAccessor::GetCreature((*me), headGUID); - if (Head && Head->IsAlive()) - { - ENSURE_AI(npc_head::npc_headAI, Head->AI())->Phase = Phase; - ENSURE_AI(npc_head::npc_headAI, Head->AI())->Disappear(); - } - return; - } - } - else regen -= diff; + void JustEngagedWith(Unit* /*who*/) override + { + _instance->SetBossState(DATA_HORSEMAN_EVENT, IN_PROGRESS); + DoZoneInCombat(); - if (whirlwind <= diff) - { - whirlwind = urand(4000, 8000); - if (urand(0, 1)) - { - me->RemoveAurasDueToSpell(SPELL_CONFUSE); - DoCast(me, SPELL_WHIRLWIND, true); - DoCast(me, SPELL_CONFUSE); - } - else - me->RemoveAurasDueToSpell(SPELL_WHIRLWIND); - } - else whirlwind -= diff; - } - } - }; -}; + _scheduler.Schedule(2s, uint32(TASK_GROUP_COMBAT), [this](TaskContext cleaveContext) + { + DoCastVictim(SPELL_HORSEMANS_CLEAVE); + cleaveContext.Repeat(2s, 6s); + }).Schedule(6s, uint32(TASK_GROUP_COMBAT), [this](TaskContext /*burnContext*/) + { + if (Creature* flame = me->SummonCreature(NPC_HELPER, HeadlessHorsemanSpawnPoints[0], TEMPSUMMON_TIMED_DESPAWN, 17 * IN_MILLISECONDS)) + flame->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_FLAME); + }); + } -class npc_pulsing_pumpkin : public CreatureScript -{ -public: - npc_pulsing_pumpkin() : CreatureScript("npc_pulsing_pumpkin") { } + void MoveInLineOfSight(Unit* who) override + { + if (_withHead && _phase != PHASE_BODY_0) + ScriptedAI::MoveInLineOfSight(who); + } + + void KilledUnit(Unit* player) override + { + if (player->GetTypeId() != TYPEID_PLAYER) + return; + + if (_withHead) + DoTalk(SAY_PLAYER_DEATH); + else if (Creature* head = ObjectAccessor::GetCreature(*me, _headGUID)) + head->AI()->SetData(DATA_HEAD_TALK, SAY_PLAYER_DEATH); + } - CreatureAI* GetAI(Creature* creature) const override + void SpellHitTarget(Unit* unit, SpellInfo const* spell) override { - return GetScarletMonasteryAI<npc_pulsing_pumpkinAI>(creature); + if (spell->Id == SPELL_CONFLAGRATION && unit->HasAura(SPELL_CONFLAGRATION)) + DoTalk(SAY_CONFLAGRATION, unit); } - struct npc_pulsing_pumpkinAI : public ScriptedAI + void JustDied(Unit* /*killer*/) override { - npc_pulsing_pumpkinAI(Creature* creature) : ScriptedAI(creature) + DoTalk(SAY_DEATH); + if (Creature* flame = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_DESPAWN, 60 * IN_MILLISECONDS)) + flame->CastSpell(flame, SPELL_HEADLESS_HORSEMAN___FIRE); + if (Creature* wisp = DoSpawnCreature(NPC_WISP_INVIS, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_DESPAWN, 60 * IN_MILLISECONDS)) + wisp->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_BLUE); + + _instance->SetBossState(DATA_HORSEMAN_EVENT, DONE); + + if (me->GetMap()->HavePlayers()) { - sprouted = false; + if (Group* group = me->GetMap()->GetPlayers().begin()->GetSource()->GetGroup()) + if (group->isLFGGroup()) + sLFGMgr->FinishDungeon(group->GetGUID(), LFG_DUNGEONID_THE_HEADLESS_HORSEMAN, me->GetMap()); } + } + + void SpellHit(Unit* caster, SpellInfo const* spellInfo) override + { + if (_withHead) + return; + + if (spellInfo->Id != SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD) + return; + + _laughTimer.Reset(urand(2 * IN_MILLISECONDS, 5 * IN_MILLISECONDS)); + _withHead = true; + _scheduler.CancelGroup(TASK_GROUP_WITHOUT_HEAD); - bool sprouted; - ObjectGuid debuffGUID; + me->InterruptNonMeleeSpells(true); + me->RemoveAllAuras(); + me->SetName("Headless Horseman"); //@todo THIS can't be serious + me->SetFullHealth(); - void Reset() override + DoTalk(SAY_REJOINED); + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_VISUAL); + caster->GetMotionMaster()->Clear(); + caster->GetMotionMaster()->MoveFollow(me, 6.f, 0.f); + + switch (_phase) { - float x, y, z; - me->GetPosition(x, y, z); //this visual aura some under ground - me->UpdatePosition(x, y, z + 0.35f, 0.0f); - debuffGUID.Clear(); - Despawn(); - Creature* debuff = DoSpawnCreature(HELPER, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 14500); - if (debuff) - { - debuff->SetDisplayId(me->GetDisplayId()); - debuff->CastSpell(debuff, SPELL_PUMPKIN_AURA_GREEN, false); - ENSURE_AI(npc_wisp_invis::npc_wisp_invisAI, debuff->AI())->SetType(1); - debuffGUID = debuff->GetGUID(); - } - sprouted = false; - DoCast(me, SPELL_PUMPKIN_AURA, true); - DoCast(me, SPELL_SPROUTING); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); + case PHASE_BODY_1: + _scheduler.Schedule(2s, uint32(TASK_GROUP_COMBAT), [this](TaskContext cleaveContext) + { + DoCastVictim(SPELL_HORSEMANS_CLEAVE); + cleaveContext.Repeat(2s, 6s); + }).Schedule(6s, uint32(TASK_GROUP_COMBAT), [this](TaskContext /*burnContext*/) + { + if (Creature* flame = me->SummonCreature(NPC_HELPER, HeadlessHorsemanSpawnPoints[0], TEMPSUMMON_TIMED_DESPAWN, 17 * IN_MILLISECONDS)) + flame->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_FLAME); + }); + break; + case PHASE_BODY_2: + _scheduler.Schedule(2s, uint32(TASK_GROUP_COMBAT), [this](TaskContext cleaveContext) + { + DoCastVictim(SPELL_HORSEMANS_CLEAVE); + cleaveContext.Repeat(2s, 6s); + }).Schedule(15s, uint32(TASK_GROUP_COMBAT), [this](TaskContext clonfragateContext) + { + if (Unit* player = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.f, true, false, -SPELL_CONFLAGRATION)) + DoCast(player, SPELL_CONFLAGRATION, false); + clonfragateContext.Repeat(10s, 16s); + }); + break; + case PHASE_BODY_3: + _scheduler.Schedule(2s, uint32(TASK_GROUP_COMBAT), [this](TaskContext cleaveContext) + { + DoCastVictim(SPELL_HORSEMANS_CLEAVE); + cleaveContext.Repeat(2s, 6s); + }).Schedule(15s, uint32(TASK_GROUP_COMBAT), [this](TaskContext summonAddsContext) + { + me->InterruptNonMeleeSpells(false); + DoCastSelf(SPELL_HORSEMANS_SUMMON); + DoTalk(SAY_SPROUTING_PUMPKINS); + summonAddsContext.Repeat(25s, 35s); + }); + break; + default: + break; } - void JustEngagedWith(Unit* /*who*/) override { } + } - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (damage < me->GetHealth() || !_withHead) { - if (spell->Id == SPELL_SPROUTING) - { - sprouted = true; - me->RemoveAllAuras(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - DoCast(me, SPELL_SPROUT_BODY, true); - me->UpdateEntry(PUMPKIN_FIEND); - DoStartMovement(me->GetVictim()); - } + if (damage >= me->GetHealth() && !_withHead) + damage = 0; + return; } - void Despawn() - { - if (!debuffGUID) - return; + damage = 0; + _withHead = false; + _scheduler.CancelGroup(TASK_GROUP_COMBAT); + me->InterruptNonMeleeSpells(true); + me->RemoveAllAuras(); + me->SetName("Headless Horseman, Unhorsed"); //@todo THIS can't be serious + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_REMOVED_ON_DEATH, true); + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN, true); + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_CONFUSE_ONLY_REMOVED_ON_DEATH, false); // test - Unit* debuff = ObjectAccessor::GetUnit(*me, debuffGUID); - if (debuff) + Creature* head = nullptr; + if (!_headGUID) + { + if (Creature* newHead = DoSpawnCreature(NPC_HEADLESS_HORSEMAN_HEAD, frand(0.f, 5.f), frand(0.f, 5.f), 0, 0, TEMPSUMMON_DEAD_DESPAWN, 0)) { - debuff->SetVisible(false); - debuffGUID.Clear(); + _headGUID = newHead->GetGUID(); + head = newHead; } } - void JustDied(Unit* /*killer*/) override + if (!head) + head = ObjectAccessor::GetCreature(*me, _headGUID); + if (head && head->IsAlive()) { - if (!sprouted) - Despawn(); + head->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + head->AI()->SetData(DATA_HEAD_PHASE, _phase); + DoCast(head, SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD, true); } - void MoveInLineOfSight(Unit* who) override + _scheduler.Schedule(2s, uint32(TASK_GROUP_WITHOUT_HEAD), [this](TaskContext whirlwindContext) + { + if (roll_chance_i(50)) + { + me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_CONFUSE_ONLY_REMOVED_ON_DEATH); + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HORSEMANS_WHIRLWIND, true); + DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___BODY_REGEN_CONFUSE_ONLY_REMOVED_ON_DEATH); + } + else + me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_CLIMAX___HORSEMANS_WHIRLWIND); + + if (!_withHead) + whirlwindContext.Repeat(4s, 8s); + }).Schedule(1s, uint32(TASK_GROUP_WITHOUT_HEAD), [this](TaskContext regenerateContext) { - if (!who || !me->IsValidAttackTarget(who) || me->GetVictim()) - return; + if (me->IsFullHealth() && !_withHead) + { + Creature* head = ObjectAccessor::GetCreature(*me, _headGUID); + if (head && head->IsAlive()) + head->AI()->DoAction(ACTION_HEAD_RETURN_TO_BODY); + } + else + regenerateContext.Repeat(1s); + }); + } - AddThreat(who, 0.0f); - if (sprouted) - DoStartMovement(who); + void UpdateAI(uint32 diff) override + { + if (!_laughTimer.Passed()) + _laughTimer.Update(diff); + + if (_withHead && _laughTimer.Passed()) + { + _laughTimer.Reset(urand(11 * IN_MILLISECONDS, 22 * IN_MILLISECONDS)); + DoPlaySoundToSet(me, Trinity::Containers::SelectRandomContainerElement(HeadlessHorsemanRandomLaughSound)); } - void UpdateAI(uint32 /*diff*/) override + if (UpdateVictim()) { - if (sprouted && UpdateVictim()) - DoMeleeAttackIfReady(); + _scheduler.Update(diff, [this] + { + if (_withHead) + DoMeleeAttackIfReady(); + }); } - }; -}; + else + _scheduler.Update(diff); + } -enum LooselyTurnedSoil -{ - QUEST_CALL_THE_HEADLESS_HORSEMAN = 11405 +private: + void DoTalk(uint8 textEntry, Unit* target = nullptr) + { + Talk(textEntry, target); + _laughTimer.Reset(std::min<uint32>(10 * IN_MILLISECONDS, _laughTimer.GetExpiry() + 4 * IN_MILLISECONDS)); + } + + InstanceScript* _instance; + TaskScheduler _scheduler; + TimeTrackerSmall _laughTimer; + ObjectGuid _headGUID; + uint32 _phase; + uint32 _id; + bool _withHead; }; -class go_loosely_turned_soil : public GameObjectScript +struct npc_pulsing_pumpkin : public ScriptedAI { - public: - go_loosely_turned_soil() : GameObjectScript("go_loosely_turned_soil") { } - - struct go_loosely_turned_soilAI : public GameObjectAI - { - go_loosely_turned_soilAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { } + npc_pulsing_pumpkin(Creature* creature) : ScriptedAI(creature) + { + _sprouted = false; + } - InstanceScript* instance; + void Reset() override + { + _debuffGUID.Clear(); - bool GossipHello(Player* player) override - { - if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS || player->GetQuestStatus(QUEST_CALL_THE_HEADLESS_HORSEMAN) != QUEST_STATUS_COMPLETE) - return true; + Despawn(); - return false; - } + Creature* debuff = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 14 * IN_MILLISECONDS + 500); + if (debuff) + { + debuff->SetDisplayId(me->GetDisplayId()); + debuff->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_PUMPKIN); + _debuffGUID = debuff->GetGUID(); + } - void QuestReward(Player* player, Quest const* /*quest*/, uint32 /*opt*/) override - { - if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS) - return; + _sprouted = false; - player->AreaExploredOrEventHappens(11405); - if (Creature* horseman = me->SummonCreature(HH_MOUNTED, FlightPoint[20], TEMPSUMMON_MANUAL_DESPAWN, 0)) - { - ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->PlayerGUID = player->GetGUID(); - ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->FlyMode(); - } - } - }; + DoCastSelf(SPELL_PUMPKIN_LIFE_CYCLE, true); + DoCastSelf(SPELL_SPROUTING); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); + } - GameObjectAI* GetAI(GameObject* go) const override + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SPROUTING) { - return GetScarletMonasteryAI<go_loosely_turned_soilAI>(go); + _sprouted = true; + me->RemoveAllAuras(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); + DoCastSelf(SPELL_SPROUT_BODY, true); + DoZoneInCombat(); } + } + + void JustDied(Unit* /*killer*/) override + { + if (!_sprouted) + Despawn(); + } + + void MoveInLineOfSight(Unit* who) override + { + if (_sprouted) + ScriptedAI::MoveInLineOfSight(who); + } + + void UpdateAI(uint32 /*diff*/) override + { + if (_sprouted && UpdateVictim()) + DoMeleeAttackIfReady(); + } + +private: + void Despawn() + { + if (!_debuffGUID) + return; + + Creature* debuff = ObjectAccessor::GetCreature(*me, _debuffGUID); + if (debuff) + debuff->DespawnOrUnsummon(); + + _debuffGUID.Clear(); + } + + ObjectGuid _debuffGUID; + bool _sprouted; +}; + +enum LooselyTurnedSoil +{ + QUEST_CALL_THE_HEADLESS_HORSEMAN = 11405 }; -void npc_head::npc_headAI::Disappear() +struct go_loosely_turned_soil : public GameObjectAI { - if (withbody) - return; + go_loosely_turned_soil(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { } - if (bodyGUID) + bool GossipHello(Player* player) override { - Creature* body = ObjectAccessor::GetCreature((*me), bodyGUID); - if (body && body->IsAlive()) + if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS || player->GetQuestStatus(QUEST_CALL_THE_HEADLESS_HORSEMAN) != QUEST_STATUS_COMPLETE) + return true; + + return false; + } + + void QuestReward(Player* player, Quest const* /*quest*/, uint32 /*opt*/) override + { + if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS) + return; + + player->AreaExploredOrEventHappens(11405); + + if (TempSummon* horseman = me->GetMap()->SummonCreature(NPC_HEADLESS_HORSEMAN_MOUNTED, HeadlessHorsemanFlightPoints[20])) { - withbody = true; - me->RemoveAllAuras(); - body->RemoveAurasDueToSpell(SPELL_IMMUNE);//hack, SpellHit doesn't calls if body has immune aura - DoCast(body, SPELL_FLYING_HEAD); - me->SetFullHealth(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->GetMotionMaster()->MoveIdle(); - ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, body->AI())->returned = true; + horseman->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN); + horseman->AI()->DoAction(ACTION_HORSEMAN_EVENT_START); } } -} + +private: + InstanceScript* instance; +}; void AddSC_boss_headless_horseman() { - new boss_headless_horseman(); - new npc_head(); - new npc_pulsing_pumpkin(); - new npc_wisp_invis(); - new go_loosely_turned_soil(); + RegisterScarletMonasteryCreatureAI(boss_headless_horseman); + RegisterScarletMonasteryCreatureAI(npc_head); + RegisterScarletMonasteryCreatureAI(npc_pulsing_pumpkin); + RegisterScarletMonasteryCreatureAI(npc_wisp_invis); + RegisterScarletMonasteryGameObjectAI(go_loosely_turned_soil); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp index 49b451eee05..a6e24aa42ef 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp @@ -16,169 +16,145 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Herod -SD%Complete: 95 -SDComment: Should in addition spawn Myrmidons in the hallway outside -SDCategory: Scarlet Monastery -EndScriptData */ - -#include "ScriptMgr.h" #include "scarlet_monastery.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" +#include "ScriptMgr.h" -enum Says +enum HerodSays { - SAY_AGGRO = 0, - SAY_WHIRLWIND = 1, - SAY_ENRAGE = 2, - SAY_KILL = 3, - EMOTE_ENRAGE = 4 + SAY_AGGRO = 0, + SAY_WHIRLWIND = 1, + SAY_ENRAGE = 2, + SAY_KILL = 3, + EMOTE_ENRAGE = 4 }; -enum Spells +enum HerodSpells { - SPELL_RUSHINGCHARGE = 8260, - SPELL_CLEAVE = 15496, - SPELL_WHIRLWIND = 8989, - SPELL_FRENZY = 8269 + SPELL_RUSHINGCHARGE = 8260, + SPELL_CLEAVE = 15496, + SPELL_WHIRLWIND = 8989, + SPELL_FRENZY = 8269 }; -enum Npcs +enum HerodNpcs { - NPC_SCARLET_TRAINEE = 6575, - NPC_SCARLET_MYRMIDON = 4295 + NPC_SCARLET_TRAINEE = 6575, + NPC_SCARLET_MYRMIDON = 4295 }; -enum Events +enum HerodEvents { - EVENT_CLEAVE = 1, + EVENT_CLEAVE = 1, EVENT_WHIRLWIND }; Position const ScarletTraineePos = { 1939.18f, -431.58f, 17.09f, 6.22f }; -class boss_herod : public CreatureScript +struct boss_herod : public BossAI { - public: - boss_herod() : CreatureScript("boss_herod") { } - - struct boss_herodAI : public BossAI - { - boss_herodAI(Creature* creature) : BossAI(creature, DATA_HEROD) - { - _enrage = false; - } - - void Reset() override - { - _enrage = false; - _Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - DoCast(me, SPELL_RUSHINGCHARGE); - _JustEngagedWith(); - - events.ScheduleEvent(EVENT_CLEAVE, 12s); - events.ScheduleEvent(EVENT_WHIRLWIND, 1min); - } - - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_KILL); - } + boss_herod(Creature* creature) : BossAI(creature, DATA_HEROD) + { + _enrage = false; + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); + void Reset() override + { + _enrage = false; + _Reset(); + } - for (uint8 i = 0; i < 20; ++i) - me->SummonCreature(NPC_SCARLET_TRAINEE, ScarletTraineePos, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 600000); - } + void JustEngagedWith(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + DoCast(me, SPELL_RUSHINGCHARGE); + _JustEngagedWith(); - void DamageTaken(Unit* /*attacker*/, uint32& damage) override - { - if (!_enrage && me->HealthBelowPctDamaged(30, damage)) - { - Talk(EMOTE_ENRAGE); - Talk(SAY_ENRAGE); - DoCast(me, SPELL_FRENZY); - _enrage = true; - } - } + events.ScheduleEvent(EVENT_CLEAVE, 12s); + events.ScheduleEvent(EVENT_WHIRLWIND, 1min); + } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, 12s); - break; - case EVENT_WHIRLWIND: - Talk(SAY_WHIRLWIND); - DoCastVictim(SPELL_WHIRLWIND); - events.ScheduleEvent(EVENT_WHIRLWIND, 30s); - break; - default: - break; - } - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + } - private: - bool _enrage; - }; + void JustDied(Unit* /*killer*/) override + { + _JustDied(); - CreatureAI* GetAI(Creature* creature) const override + for (uint8 itr = 0; itr < 20; ++itr) { - return GetScarletMonasteryAI<boss_herodAI>(creature); + Position randomNearPosition = me->GetRandomPoint(ScarletTraineePos, 5.f); + randomNearPosition.SetOrientation(ScarletTraineePos.GetOrientation()); + me->SummonCreature(NPC_SCARLET_TRAINEE, randomNearPosition, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 600 * IN_MILLISECONDS); } -}; - -class npc_scarlet_trainee : public CreatureScript -{ -public: - npc_scarlet_trainee() : CreatureScript("npc_scarlet_trainee") { } + } - CreatureAI* GetAI(Creature* creature) const override + void DamageTaken(Unit* /*attacker*/, uint32& damage) override { - return GetScarletMonasteryAI<npc_scarlet_traineeAI>(creature); + if (!_enrage && me->HealthBelowPctDamaged(30, damage)) + { + Talk(EMOTE_ENRAGE); + Talk(SAY_ENRAGE); + DoCastSelf(SPELL_FRENZY); + _enrage = true; + } } - struct npc_scarlet_traineeAI : public EscortAI + void ExecuteEvent(uint32 eventId) override { - npc_scarlet_traineeAI(Creature* creature) : EscortAI(creature) + switch (eventId) { - Start_Timer = urand(1000, 6000); + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.Repeat(12s); + break; + case EVENT_WHIRLWIND: + Talk(SAY_WHIRLWIND); + DoCastVictim(SPELL_WHIRLWIND); + events.Repeat(30s); + break; + default: + break; } + } - uint32 Start_Timer; +private: + bool _enrage; +}; - void Reset() override { } - void JustEngagedWith(Unit* /*who*/) override { } +struct npc_scarlet_trainee : public EscortAI +{ + npc_scarlet_trainee(Creature* creature) : EscortAI(creature) + { + _startTimer = urand(1000, 6000); + } - void UpdateAI(uint32 diff) override + void UpdateAI(uint32 diff) override + { + if (_startTimer) { - if (Start_Timer) + if (_startTimer <= diff) { - if (Start_Timer <= diff) - { - Start(true, true); - Start_Timer = 0; - } else Start_Timer -= diff; + Start(true, true); + _startTimer = 0; } - - EscortAI::UpdateAI(diff); + else + _startTimer -= diff; } - }; + + EscortAI::UpdateAI(diff); + } + +private: + uint32 _startTimer; }; void AddSC_boss_herod() { - new boss_herod(); - new npc_scarlet_trainee(); + RegisterScarletMonasteryCreatureAI(boss_herod); + RegisterScarletMonasteryCreatureAI(npc_scarlet_trainee); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_high_inquisitor_fairbanks.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_high_inquisitor_fairbanks.cpp index f24365f2c52..5ee58a213c1 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_high_inquisitor_fairbanks.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_high_inquisitor_fairbanks.cpp @@ -16,148 +16,148 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_High_Inquisitor_Fairbanks -SD%Complete: 100 -SDComment: @todo if this guy not involved in some special event, remove (and let ACID script) -SDCategory: Scarlet Monastery -EndScriptData */ - -#include "ScriptMgr.h" #include "scarlet_monastery.h" -#include "InstanceScript.h" #include "ScriptedCreature.h" +#include "ScriptMgr.h" +#include "Timer.h" -enum Spells +enum HighInquisitorFairbanksSpells { - SPELL_CURSEOFBLOOD = 8282, - SPELL_DISPELMAGIC = 15090, - SPELL_FEAR = 12096, - SPELL_HEAL = 12039, - SPELL_POWERWORDSHIELD = 11647, - SPELL_SLEEP = 8399 + SPELL_CURSEOFBLOOD = 8282, + SPELL_DISPEL_MAGIC = 15090, + SPELL_FEAR = 12096, + SPELL_HEAL = 12039, + SPELL_POWERWORDSHIELD = 11647, + SPELL_SLEEP = 8399 }; -class boss_high_inquisitor_fairbanks : public CreatureScript +enum HighInquisitorFairbanksEvents +{ + EVENT_CURSE_BLOOD = 1, + EVENT_DIPEL_MAGIC, + EVENT_FEAR, + EVENT_HEAL, + EVENT_SLEEP +}; + +class HighInquisitorFairbanksDispelMagicTargetSelector { public: - boss_high_inquisitor_fairbanks() : CreatureScript("boss_high_inquisitor_fairbanks") { } + HighInquisitorFairbanksDispelMagicTargetSelector(Unit* owner) : _me(owner) { } - CreatureAI* GetAI(Creature* creature) const override + bool operator()(Unit* unit) const { - return GetScarletMonasteryAI<boss_high_inquisitor_fairbanksAI>(creature); - } + if (unit->GetTypeId() != TYPEID_PLAYER || _me->GetDistance(unit) > 30.f) + return false; - struct boss_high_inquisitor_fairbanksAI : public ScriptedAI - { - boss_high_inquisitor_fairbanksAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + DispelChargesList dispelList; + unit->GetDispellableAuraList(_me, DISPEL_MAGIC, dispelList); + if (dispelList.empty()) + return false; - void Initialize() - { - CurseOfBlood_Timer = 10000; - DispelMagic_Timer = 30000; - Fear_Timer = 40000; - Heal_Timer = 30000; - Sleep_Timer = 30000; - Dispel_Timer = 20000; - PowerWordShield = false; - } + return true; + } - uint32 CurseOfBlood_Timer; - uint32 DispelMagic_Timer; - uint32 Fear_Timer; - uint32 Heal_Timer; - uint32 Sleep_Timer; - uint32 Dispel_Timer; - bool PowerWordShield; - InstanceScript* instance; +private: + Unit const* _me; +}; - void Reset() override - { - Initialize(); - me->SetStandState(UNIT_STAND_STATE_DEAD); - instance->SetBossState(DATA_HIGH_INQUISITOR_FAIRBANKS, NOT_STARTED); - } +struct boss_high_inquisitor_fairbanks : public BossAI +{ + boss_high_inquisitor_fairbanks(Creature* creature) : BossAI(creature, DATA_HIGH_INQUISITOR_FAIRBANKS), _healTimer(0), _powerWordShield(false) { } - void JustEngagedWith(Unit* /*who*/) override - { - me->SetStandState(UNIT_STAND_STATE_STAND); - instance->SetBossState(DATA_HIGH_INQUISITOR_FAIRBANKS, IN_PROGRESS); - } + void Reset() override + { + _Reset(); + _healTimer.Reset(0); + _powerWordShield = false; + me->SetStandState(UNIT_STAND_STATE_DEAD); + } - void JustDied(Unit* /*killer*/) override - { - instance->SetBossState(DATA_HIGH_INQUISITOR_FAIRBANKS, DONE); - } + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(EVENT_CURSE_BLOOD, 10s); + events.ScheduleEvent(EVENT_DIPEL_MAGIC, 30s); + events.ScheduleEvent(EVENT_FEAR, 40s); + events.ScheduleEvent(EVENT_SLEEP, 25s); + me->SetStandState(UNIT_STAND_STATE_STAND); + } - void UpdateAI(uint32 diff) override + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPctDamaged(25, damage)) { - if (!UpdateVictim()) - return; - - //If we are <25% hp cast Heal - if (!HealthAbovePct(25) && !me->IsNonMeleeSpellCast(false) && Heal_Timer <= diff) + if (!_powerWordShield) { - DoCast(me, SPELL_HEAL); - Heal_Timer = 30000; + DoCastSelf(SPELL_POWERWORDSHIELD); + _powerWordShield = true; } - else Heal_Timer -= diff; - //Fear_Timer - if (Fear_Timer <= diff) + if (!me->IsNonMeleeSpellCast(false) && _healTimer.Passed()) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) - DoCast(target, SPELL_FEAR); - - Fear_Timer = 40000; + _healTimer.Reset(30 * IN_MILLISECONDS); + DoCastSelf(SPELL_HEAL); } - else Fear_Timer -= diff; + } + } - //Sleep_Timer - if (Sleep_Timer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_MAXTHREAT, 0)) - DoCast(target, SPELL_SLEEP); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - Sleep_Timer = 30000; - } - else Sleep_Timer -= diff; + events.Update(diff); - //PowerWordShield_Timer - if (!PowerWordShield && !HealthAbovePct(25)) - { - DoCast(me, SPELL_POWERWORDSHIELD); - PowerWordShield = true; - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - //Dispel_Timer - if (Dispel_Timer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_DISPELMAGIC); + while (uint32 eventId = events.ExecuteEvent()) + { + ExecuteEvent(eventId); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - DispelMagic_Timer = 30000; - } - else DispelMagic_Timer -= diff; + if (!_healTimer.Passed()) + _healTimer.Update(diff); - //CurseOfBlood_Timer - if (CurseOfBlood_Timer <= diff) - { - DoCastVictim(SPELL_CURSEOFBLOOD); - CurseOfBlood_Timer = 25000; - } - else CurseOfBlood_Timer -= diff; + DoMeleeAttackIfReady(); + } - DoMeleeAttackIfReady(); + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_CURSE_BLOOD: + DoCastVictim(SPELL_CURSEOFBLOOD); + events.Repeat(25s); + break; + case EVENT_DIPEL_MAGIC: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, HighInquisitorFairbanksDispelMagicTargetSelector(me))) + DoCast(target, SPELL_DISPEL_MAGIC); + events.Repeat(30s); + break; + case EVENT_FEAR: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 20.f, true)) + DoCast(target, SPELL_FEAR); + events.Repeat(40s); + break; + case EVENT_SLEEP: + if (Unit* target = SelectTarget(SELECT_TARGET_MAXTHREAT, 0, 30.f, true, false)) + DoCast(target, SPELL_SLEEP); + events.Repeat(30s); + default: + break; } - }; + } + +private: + TimeTrackerSmall _healTimer; + bool _powerWordShield; }; void AddSC_boss_high_inquisitor_fairbanks() { - new boss_high_inquisitor_fairbanks(); + RegisterScarletMonasteryCreatureAI(boss_high_inquisitor_fairbanks); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_houndmaster_loksey.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_houndmaster_loksey.cpp index 7db8be4bef7..ff95ffccb12 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_houndmaster_loksey.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_houndmaster_loksey.cpp @@ -15,73 +15,58 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" #include "scarlet_monastery.h" #include "ScriptedCreature.h" +#include "ScriptMgr.h" -enum Yells +enum HoundmasterLokseyYells { - SAY_AGGRO = 0, + SAY_AGGRO = 0, }; -enum Spells +enum HoundmasterLokseySpells { - SPELL_SUMMONSCARLETHOUND = 17164, - SPELL_BLOODLUST = 6742 + SPELL_SUMMON_SCARLET_HOUND = 17164, + SPELL_BLOODLUST = 6742 }; -enum Events +enum HoundmasterLokseyEvents { - EVENT_BLOODLUST = 1 + EVENT_BLOODLUST = 1 }; -class boss_houndmaster_loksey : public CreatureScript +struct boss_houndmaster_loksey : public BossAI { - public: - boss_houndmaster_loksey() : CreatureScript("boss_houndmaster_loksey") { } - - struct boss_houndmaster_lokseyAI : public BossAI - { - boss_houndmaster_lokseyAI(Creature* creature) : BossAI(creature, DATA_HOUNDMASTER_LOKSEY) { } - - void Reset() override - { - _Reset(); - } + boss_houndmaster_loksey(Creature* creature) : BossAI(creature, DATA_HOUNDMASTER_LOKSEY) { } - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - _JustEngagedWith(); - events.ScheduleEvent(EVENT_BLOODLUST, 20s); - } + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + Talk(SAY_AGGRO); + DoCast(SPELL_SUMMON_SCARLET_HOUND); + events.ScheduleEvent(EVENT_BLOODLUST, 20s); + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_BLOODLUST: + if (me->HealthBelowPct(60)) { - case EVENT_BLOODLUST: - DoCast(me, SPELL_BLOODLUST); - events.ScheduleEvent(EVENT_BLOODLUST, 20s); - break; - default: - break; + DoCastSelf(SPELL_BLOODLUST); + events.Repeat(60s); } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetScarletMonasteryAI<boss_houndmaster_lokseyAI>(creature); + else + events.Repeat(1s); + break; + default: + break; } + } }; void AddSC_boss_houndmaster_loksey() { - new boss_houndmaster_loksey(); + RegisterScarletMonasteryCreatureAI(boss_houndmaster_loksey); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp index 1637e61b3ae..28a1eb85c84 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp @@ -15,113 +15,104 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" #include "scarlet_monastery.h" #include "InstanceScript.h" -#include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "ScriptMgr.h" -enum Says +enum InterrogatorVishasSays { - SAY_AGGRO = 0, - SAY_HEALTH1 = 1, - SAY_HEALTH2 = 2, - SAY_KILL = 3, - SAY_TRIGGER_VORREL = 0 + SAY_AGGRO = 0, + SAY_HEALTH1 = 1, + SAY_HEALTH2 = 2, + SAY_KILL = 3, + SAY_TRIGGER_VORREL = 0 }; -enum Spells +enum InterrogatorVishasSpells { - SPELL_SHADOW_WORD_PAIN = 2767 + SPELL_SHADOW_WORD_PAIN = 2767 }; -enum Events +enum InterrogatorVishasEvents { - EVENT_SHADOW_WORD_PAIN = 1 + EVENT_SHADOW_WORD_PAIN = 1 }; -class boss_interrogator_vishas : public CreatureScript +struct boss_interrogator_vishas : public BossAI { - public: - boss_interrogator_vishas() : CreatureScript("boss_interrogator_vishas") { } - - struct boss_interrogator_vishasAI : public BossAI + boss_interrogator_vishas(Creature* creature) : BossAI(creature, DATA_INTERROGATOR_VISHAS) + { + Initialize(); + } + + void Initialize() + { + _yellCount = 0; + } + + void Reset() override + { + Initialize(); + _Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _JustEngagedWith(); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + if (Creature* vorrel = instance->GetCreature(DATA_VORREL)) { - boss_interrogator_vishasAI(Creature* creature) : BossAI(creature, DATA_INTERROGATOR_VISHAS) - { - Initialize(); - } - - void Initialize() - { - _yellCount = 0; - } - - void Reset() override - { - Initialize(); - _Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - _JustEngagedWith(); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_KILL); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - if (Creature* vorrel = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VORREL))) - vorrel->AI()->Talk(SAY_TRIGGER_VORREL); - } - - void DamageTaken(Unit* /*attacker*/, uint32 &damage) override - { - if (me->HealthBelowPctDamaged(60, damage) && _yellCount < 1) - { - Talk(SAY_HEALTH1); - ++_yellCount; - } - - if (me->HealthBelowPctDamaged(30, damage) && _yellCount < 2) - { - Talk(SAY_HEALTH2); - ++_yellCount; - } - } + if (vorrel->AI()) + vorrel->AI()->Talk(SAY_TRIGGER_VORREL); + } + } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_SHADOW_WORD_PAIN: - DoCastVictim(SPELL_SHADOW_WORD_PAIN); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s, 15s); - break; - default: - break; - } - } + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override + { + if (me->HealthBelowPctDamaged(60, damage) && _yellCount < 1) + { + Talk(SAY_HEALTH1); + ++_yellCount; + } - private: - uint8 _yellCount; - }; + if (me->HealthBelowPctDamaged(30, damage) && _yellCount < 2) + { + Talk(SAY_HEALTH2); + ++_yellCount; + } + } - CreatureAI* GetAI(Creature* creature) const override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - return GetScarletMonasteryAI<boss_interrogator_vishasAI>(creature); + case EVENT_SHADOW_WORD_PAIN: + DoCastVictim(SPELL_SHADOW_WORD_PAIN); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s, 15s); + break; + default: + break; } + } + +private: + uint8 _yellCount; }; void AddSC_boss_interrogator_vishas() { - new boss_interrogator_vishas(); + RegisterScarletMonasteryCreatureAI(boss_interrogator_vishas); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_mograine_and_whitemane.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_mograine_and_whitemane.cpp index 6390fff76f7..92fe59c58f7 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_mograine_and_whitemane.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_mograine_and_whitemane.cpp @@ -15,429 +15,426 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "GameTime.h" +#include "scarlet_monastery.h" #include "InstanceScript.h" #include "MotionMaster.h" -#include "ObjectAccessor.h" -#include "scarlet_monastery.h" -#include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "ScriptMgr.h" +#include "SpellAuras.h" #include "SpellInfo.h" +#include "Timer.h" -enum Says +enum MograineAndWhitemaneSays { - /// Mograine - SAY_MO_AGGRO = 0, - SAY_MO_KILL = 1, - SAY_MO_RESURRECTED = 2, - - /// Whitemaine - SAY_WH_INTRO = 0, - SAY_WH_KILL = 1, - SAY_WH_RESURRECT = 2, + // Mograine + SAY_MO_AGGRO = 0, + SAY_MO_KILL = 1, + SAY_MO_RESURRECTED = 2, + + // Whitemaine + SAY_WH_INTRO = 0, + SAY_WH_KILL = 1, + SAY_WH_RESURRECT = 2, }; -enum Spells +enum MograineAndWhitemaneSpells { - /// Mograine Spells - SPELL_CRUSADER_STRIKE = 14518, - SPELL_HAMMER_OF_JUSTICE = 5589, - SPELL_LAYONHANDS = 9257, - SPELL_RETRIBUTIONAURA = 8990, - - /// Whitemane Spells - SPELL_DEEPSLEEP = 9256, - SPELL_SCARLETRESURRECTION = 9232, - SPELL_DOMINATEMIND = 14515, - SPELL_HOLYSMITE = 9481, - SPELL_HEAL = 12039, - SPELL_POWERWORDSHIELD = 22187 + // Mograine Spells + SPELL_CRUSADER_STRIKE = 14518, + SPELL_HAMMER_OF_JUSTICE = 5589, + SPELL_LAY_ONHANDS = 9257, + SPELL_RETRIBUTION_AURA = 8990, + + // Whitemane Spells + SPELL_DEEP_SLEEP = 9256, + SPELL_SCARLET_RESURRECTION = 9232, + SPELL_DOMINATE_MIND = 14515, + SPELL_HOLY_SMITE = 9481, + SPELL_HEAL = 12039, + SPELL_POWER_WORD_SHIELD = 22187 }; -enum Events +enum MograineAndWhitemaneEvents { - /// Mograine + // Mograine EVENT_CRUSADER_STRIKE = 1, EVENT_HAMMER_OF_JUSTICE, - /// Whitemane + // Whitemane EVENT_HEAL = 1, EVENT_POWER_WORD_SHIELD, EVENT_HOLY_SMITE, }; -enum Points +enum MograineAndWhitemanePoints { POINT_WHITEMANE_MOVE_TO_MOGRAINE = 1, }; -const Position WhitemaneIntroMovePos(1163.113370f, 1398.856812f, 32.527786f); +Position const WhitemaneIntroMovePos = { 1163.113370f, 1398.856812f, 32.527786f, 0.f }; -/// Scarlet Commander Mograine - 3976 -struct boss_scarlet_commander_mograine : public ScriptedAI +// Scarlet Commander Mograine - 3976 +struct boss_scarlet_commander_mograine : public BossAI { - public: - boss_scarlet_commander_mograine(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } +public: + boss_scarlet_commander_mograine(Creature* creature) : BossAI(creature, DATA_MOGRAINE_AND_WHITE_EVENT), _killYellTimer(0) + { + Initialize(); + } - void Initialize() - { - m_LastKillTime = 0; - m_CanDie = false; - m_FakeDeath = false; - } + void Initialize() + { + _fakeDeath = false; + _canDie = true; + } - void Reset() override - { - Initialize(); + void Reset() override + { + Initialize(); - events.Reset(); - scheduler.CancelAll(); + _Reset(); + _killYellTimer.Reset(0); - DoCast(me, SPELL_RETRIBUTIONAURA); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetReactState(REACT_AGGRESSIVE); + DoCastSelf(SPELL_RETRIBUTION_AURA, true); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); - // Close the door - if (auto instance = me->GetInstanceScript()) - instance->HandleGameObject(instance->GetGuidData(GO_HIGH_INQUISITORS_DOOR), false); - } + instance->HandleGameObject(ObjectGuid::Empty, false, instance->GetGameObject(DATA_HIGH_INQUISITORS_DOOR)); + } - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_MO_AGGRO); + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); - // Call for help (from old script) - me->CallForHelp(VISIBLE_RANGE); + Talk(SAY_MO_AGGRO); - // Schedule events - events.ScheduleEvent(EVENT_CRUSADER_STRIKE, Seconds(10), Seconds(15)); - events.ScheduleEvent(EVENT_HAMMER_OF_JUSTICE, Seconds(10), Seconds(15)); - } + // Call for help (from old script) + me->CallForHelp(VISIBLE_RANGE); - void KilledUnit(Unit* who) override - { - if (who->GetTypeId() != TYPEID_PLAYER) - return; + // Just to be sure it's MOTION_SLOT_DEFAULT is static + me->GetMotionMaster()->MoveIdle(); - // Cooldown between talking - time_t currTime = GameTime::GetGameTime(); + // Schedule events + events.ScheduleEvent(EVENT_CRUSADER_STRIKE, 10s, 15s); + events.ScheduleEvent(EVENT_HAMMER_OF_JUSTICE, 10s, 15s); + } - if (m_LastKillTime < currTime) - { - Talk(SAY_MO_KILL); - m_LastKillTime = currTime + 5; - } + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() != TYPEID_PLAYER) + return; + + if (_killYellTimer.Passed()) + { + Talk(SAY_MO_KILL); + _killYellTimer.Reset(5 * IN_MILLISECONDS); } + } - void UpdateAI(uint32 diff) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - if (!UpdateVictim()) - return; + case EVENT_CRUSADER_STRIKE: + DoCastVictim(SPELL_CRUSADER_STRIKE); + events.Repeat(10s); + break; + case EVENT_HAMMER_OF_JUSTICE: + DoCastVictim(SPELL_HAMMER_OF_JUSTICE); + events.Repeat(60s); + break; + default: + break; + } + } - scheduler.Update(diff); - events.Update(diff); + void DamageTaken(Unit* /*who*/, uint32& damage) override + { + if (damage >= me->GetHealth() && !_fakeDeath) + { + _fakeDeath = true; + _canDie = false; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + // Open the door + instance->HandleGameObject(ObjectGuid::Empty, true, instance->GetGameObject(DATA_HIGH_INQUISITORS_DOOR)); - if (uint32 eventId = events.ExecuteEvent()) + // Tell whitemane to move + if (Creature* whitemane = instance->GetCreature(DATA_WHITEMANE)) { - switch (eventId) - { - case EVENT_CRUSADER_STRIKE: - DoCastVictim(SPELL_CRUSADER_STRIKE); - events.Repeat(Seconds(10)); - break; - case EVENT_HAMMER_OF_JUSTICE: - DoCastVictim(SPELL_HAMMER_OF_JUSTICE); - events.Repeat(Seconds(60)); - break; - default: - break; - } + whitemane->GetMotionMaster()->MovePoint(0, WhitemaneIntroMovePos); + DoZoneInCombat(whitemane); } - DoMeleeAttackIfReady(); + me->InterruptNonMeleeSpells(true); + me->ClearComboPointHolders(); + me->RemoveAllAuras(); + me->ClearAllReactives(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + me->SetStandState(UNIT_STAND_STATE_DEAD); + me->SetReactState(REACT_PASSIVE); // prevent Mograine from attacking while fake death + + // Stop moving + me->GetMotionMaster()->Clear(); } - void DamageTaken(Unit* /*who*/, uint32& damage) override + if (!_canDie && damage >= me->GetHealth()) + damage = 0; + } + + void SpellHit(Unit* /*who*/, SpellInfo const* spell) override + { + // Casted from Whitemane + if (spell->Id == SPELL_SCARLET_RESURRECTION) { - if (damage >= me->GetHealth() && !m_FakeDeath) + scheduler.Schedule(3s, [this](TaskContext /*context*/) { - m_FakeDeath = true; - - // Get instance script - if (auto instance = me->GetInstanceScript()) - { - // Stop moving - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - - // Open the door - instance->HandleGameObject(instance->GetGuidData(GO_HIGH_INQUISITORS_DOOR), true); + // Say text + Talk(SAY_MO_RESURRECTED); - // Tell whitemane to move - if (auto whitemane = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_WHITEMANE))) - { - DoZoneInCombat(whitemane); - - whitemane->GetMotionMaster()->Clear(); - whitemane->GetMotionMaster()->MovePoint(0, WhitemaneIntroMovePos); - } + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetStandState(UNIT_STAND_STATE_STAND); + }); - // Interrupt spells and clear auras - if (me->IsNonMeleeSpellCast(false)) - me->InterruptNonMeleeSpells(false); + scheduler.Schedule(5s, [this](TaskContext /*context*/) + { + // Schedule events after ressurrect + events.ScheduleEvent(EVENT_CRUSADER_STRIKE, 10s, 15s); + events.ScheduleEvent(EVENT_HAMMER_OF_JUSTICE, 10s, 15s); + + // We can now die + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_AGGRESSIVE); + _canDie = true; + DoCastSelf(SPELL_RETRIBUTION_AURA, true); + }); + } + } - me->ClearComboPointHolders(); - me->RemoveAllAuras(); - me->ClearAllReactives(); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - // Set fake death flags - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetStandState(UNIT_STAND_STATE_DEAD); - me->SetReactState(REACT_PASSIVE); // prevent mograine from attacking while fake death - } - } + events.Update(diff); + scheduler.Update(diff); - if (!m_CanDie && damage >= me->GetHealth()) - damage = 0; - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void SpellHit(Unit* /*who*/, SpellInfo const* spell) override + while (uint32 eventId = events.ExecuteEvent()) { - // Casted from Whitemane - if (spell->Id == SPELL_SCARLETRESURRECTION) - { - scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) - { - // Say text - Talk(SAY_MO_RESURRECTED); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetStandState(UNIT_STAND_STATE_STAND); - }); - - scheduler.Schedule(Seconds(5), [this](TaskContext /*context*/) - { - // Schedule events after ressurrect - events.ScheduleEvent(EVENT_CRUSADER_STRIKE, Seconds(10), Seconds(15)); - events.ScheduleEvent(EVENT_HAMMER_OF_JUSTICE, Seconds(10), Seconds(15)); - - // We can now die - me->SetReactState(REACT_AGGRESSIVE); - m_CanDie = true; - }); - } + ExecuteEvent(eventId); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - private: - /// Timers - EventMap events; - TaskScheduler scheduler; + DoMeleeAttackIfReady(); + } - /// Variables - time_t m_LastKillTime; - bool m_FakeDeath; - bool m_CanDie; +private: + TimeTrackerSmall _killYellTimer; + bool _fakeDeath; + bool _canDie; }; -/// High Inquisitor Whitemane - 3977 +// High Inquisitor Whitemane - 3977 struct boss_high_inquisitor_whitemane : public ScriptedAI { - public: - boss_high_inquisitor_whitemane(Creature* creature) : ScriptedAI(creature) +public: + boss_high_inquisitor_whitemane(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _killYellTimer(0) + { + Initialize(); + } + + void Initialize() + { + _ressurectionInProgress = false; + _canDie = true; + } + + void Reset() override + { + Initialize(); + + _events.Reset(); + _scheduler.CancelAll(); + _killYellTimer.Reset(0); + + DoCastSelf(SPELL_RETRIBUTION_AURA); + me->SetReactState(REACT_AGGRESSIVE); + } + + void JustEngagedWith(Unit* /*who*/) override + { + Talk(SAY_WH_INTRO); + + // Just to be sure it's MOTION_SLOT_DEFAULT is static + me->GetMotionMaster()->MoveIdle(); + + // Start events after 5 seconds + _scheduler.Schedule(5s, [this](TaskContext /*context*/) { - Initialize(); - } - - void Initialize() + _events.ScheduleEvent(EVENT_HEAL, 10s); + _events.ScheduleEvent(EVENT_POWER_WORD_SHIELD, 15s); + _events.ScheduleEvent(EVENT_HOLY_SMITE, 6s); + }); + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() != TYPEID_PLAYER) + return; + + if (_killYellTimer.Passed()) { - m_LastKillTime = 0; - m_RessurectionInProgress = false; - m_CanDie = false; + Talk(SAY_WH_KILL); + _killYellTimer.Reset(5 * IN_MILLISECONDS); } + } - void Reset() override - { - events.Reset(); - scheduler.CancelAll(); + void UpdateAI(const uint32 diff) override + { + if (!UpdateVictim()) + return; - DoCast(me, SPELL_RETRIBUTIONAURA); - me->SetReactState(REACT_AGGRESSIVE); - } + _events.Update(diff); + _scheduler.Update(diff); - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_WH_INTRO); + if (!_killYellTimer.Passed()) + _killYellTimer.Update(diff); - // Start events after 5 seconds - scheduler.Schedule(Seconds(5), [this](TaskContext /*context*/) - { - events.ScheduleEvent(EVENT_HEAL, Seconds(10)); - events.ScheduleEvent(EVENT_POWER_WORD_SHIELD, Seconds(15)); - events.ScheduleEvent(EVENT_HOLY_SMITE, Seconds(6)); - }); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void KilledUnit(Unit* who) override + while (uint32 eventId = _events.ExecuteEvent()) { - if (who->GetTypeId() != TYPEID_PLAYER) - return; - - // Cooldown between talking - time_t currTime = GameTime::GetGameTime(); - - if (m_LastKillTime < currTime) + switch (eventId) { - Talk(SAY_MO_KILL); - m_LastKillTime = currTime + 5; - } - } - - void UpdateAI(const uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - scheduler.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - if (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) + case EVENT_HEAL: { - case EVENT_HEAL: + Creature* target = nullptr; + if (HealthBelowPct(75)) + target = me; + else if (Creature* mograine = _instance->GetCreature(DATA_MOGRAINE)) { - Creature* target = nullptr; - - if (HealthBelowPct(75)) - target = me; - else if (auto instance = me->GetInstanceScript()) - { - if (auto mograine = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MOGRAINE))) - if (mograine->IsAlive() && mograine->HealthBelowPct(75)) - target = mograine; - } - - if (target) - DoCast(target, SPELL_HEAL); - - events.Repeat(Seconds(13)); - break; + if (mograine->IsAlive() && mograine->HealthBelowPct(75)) + target = mograine; } - case EVENT_POWER_WORD_SHIELD: - DoCastSelf(SPELL_POWERWORDSHIELD); - events.Repeat(Seconds(15)); - break; - case EVENT_HOLY_SMITE: - DoCastVictim(SPELL_HOLYSMITE); - events.Repeat(Seconds(6)); - break; - default: - break; + + if (target) + DoCast(target, SPELL_HEAL); + + _events.Repeat(13s); + break; } + case EVENT_POWER_WORD_SHIELD: + DoCastSelf(SPELL_POWER_WORD_SHIELD); + _events.Repeat(15s); + break; + case EVENT_HOLY_SMITE: + DoCastVictim(SPELL_HOLY_SMITE); + _events.Repeat(6s); + break; + default: + break; } - DoMeleeAttackIfReady(); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - void DamageTaken(Unit* /*who*/, uint32& damage) override - { - // When Whitemane falls below 50% cast Deep sleep and schedule to ressurrect - if (me->HealthBelowPctDamaged(50, damage) && !m_RessurectionInProgress) - { - m_RessurectionInProgress = true; + if (me->HasReactState(REACT_AGGRESSIVE)) + DoMeleeAttackIfReady(); + } - // Cancel all combat events - events.CancelEvent(EVENT_HEAL); - events.CancelEvent(EVENT_POWER_WORD_SHIELD); - events.CancelEvent(EVENT_HOLY_SMITE); + void DamageTaken(Unit* /*who*/, uint32& damage) override + { + // When Whitemane falls below 50% cast Deep sleep and schedule to ressurrect + if (me->HealthBelowPctDamaged(50, damage) && !_ressurectionInProgress) + { + _ressurectionInProgress = true; + _canDie = false; - if (me->IsNonMeleeSpellCast(false)) - me->InterruptNonMeleeSpells(false); + // Cancel all combat events + _events.CancelEvent(EVENT_HEAL); + _events.CancelEvent(EVENT_POWER_WORD_SHIELD); + _events.CancelEvent(EVENT_HOLY_SMITE); - // Sleep all players - DoCastAOE(SPELL_DEEPSLEEP); + me->InterruptNonMeleeSpells(true); - // Move to mograine - if (auto instance = me->GetInstanceScript()) - { - if (auto mograine = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MOGRAINE))) - { - // Stop moving - me->SetReactState(REACT_PASSIVE); - me->GetMotionMaster()->Clear(); + // Sleep all players + DoCastAOE(SPELL_DEEP_SLEEP); - Position pos = *mograine; + if (Creature* mograine = _instance->GetCreature(DATA_MOGRAINE)) + { + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->Clear(MOTION_PRIORITY_NORMAL); - // Get a position within 2 yards of mograine, and facing him - me->MovePosition(pos, 2.0f, me->GetRelativeAngle(*mograine)); + Position movePosition = mograine->GetPosition(); - me->GetMotionMaster()->MovePoint(POINT_WHITEMANE_MOVE_TO_MOGRAINE, pos); - } - } + // Get a position within 2 yards of mograine, and facing him + me->MovePosition(movePosition, 2.0f, me->GetRelativeAngle(movePosition)); + me->GetMotionMaster()->MovePoint(POINT_WHITEMANE_MOVE_TO_MOGRAINE, movePosition); } - - if (!m_CanDie && damage >= me->GetHealth()) - damage = 0; } - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - return; - - if (id == POINT_WHITEMANE_MOVE_TO_MOGRAINE) - { - if (auto instance = me->GetInstanceScript()) - if (auto mograine = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MOGRAINE))) - me->SetFacingToObject(mograine); - - // After 3 seconds cast scarlet ressurection - scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) - { - if (auto instance = me->GetInstanceScript()) - if (auto mograine = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MOGRAINE))) - DoCast(mograine, SPELL_SCARLETRESURRECTION); - }); + if (!_canDie && damage >= me->GetHealth()) + damage = 0; + } - // After 5 seconds or when finish cast spell, say Arise my champion.. (Maybe this belongs in an OnSuccessfulCastSpell hook? .. if that exists - scheduler.Schedule(Seconds(5), [this](TaskContext /*context*/) - { - Talk(SAY_WH_RESURRECT); - - // Schedule events again - events.ScheduleEvent(EVENT_HEAL, Seconds(10)); - events.ScheduleEvent(EVENT_POWER_WORD_SHIELD, Seconds(15)); - events.ScheduleEvent(EVENT_HOLY_SMITE, Seconds(6)); - - m_CanDie = true; - me->SetReactState(REACT_AGGRESSIVE); - }); - } - } + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE || id != POINT_WHITEMANE_MOVE_TO_MOGRAINE) + return; - private: - /// Timers - EventMap events; - TaskScheduler scheduler; + if (Creature* mograine = _instance->GetCreature(DATA_MOGRAINE)) + me->SetFacingToObject(mograine); - /// Variables - time_t m_LastKillTime; - bool m_RessurectionInProgress; - bool m_CanDie; + // After 3 seconds cast scarlet ressurection + _scheduler.Schedule(3s, [this](TaskContext /*context*/) + { + if (Creature* mograine = _instance->GetCreature(DATA_MOGRAINE)) + DoCast(mograine, SPELL_SCARLET_RESURRECTION); + else + MograineResurrected(); + }); + } + + void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override + { + if (target->GetEntry() == NPC_MOGRAINE && spellInfo->Id == SPELL_SCARLET_RESURRECTION) + MograineResurrected(); + } + +private: + void MograineResurrected() + { + Talk(SAY_WH_RESURRECT); + + // Schedule events again + _events.ScheduleEvent(EVENT_HEAL, 10s); + _events.ScheduleEvent(EVENT_POWER_WORD_SHIELD, 15s); + _events.ScheduleEvent(EVENT_HOLY_SMITE, 6s); + + _canDie = true; + + me->SetReactState(REACT_AGGRESSIVE); + + if (me->GetVictim()) + me->GetMotionMaster()->MoveChase(me->GetVictim()); + } + + InstanceScript* _instance; + EventMap _events; + TaskScheduler _scheduler; + TimeTrackerSmall _killYellTimer; + bool _ressurectionInProgress; + bool _canDie; }; void AddSC_boss_mograine_and_whitemane() { - RegisterScarletMonastaryCreatureAI(boss_scarlet_commander_mograine); - RegisterScarletMonastaryCreatureAI(boss_high_inquisitor_whitemane); + RegisterScarletMonasteryCreatureAI(boss_scarlet_commander_mograine); + RegisterScarletMonasteryCreatureAI(boss_high_inquisitor_whitemane); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_scorn.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_scorn.cpp index d33c0e47d16..ee37e277fcd 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_scorn.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_scorn.cpp @@ -15,87 +15,66 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" -#include "ScriptedCreature.h" #include "scarlet_monastery.h" +#include "ScriptedCreature.h" +#include "ScriptMgr.h" -enum Spells +enum ScornSpells { - SPELL_LICHSLAP = 28873, - SPELL_FROSTBOLT_VOLLEY = 8398, - SPELL_MINDFLAY = 17313, - SPELL_FROSTNOVA = 15531 + SPELL_LICHSLAP = 28873, + SPELL_FROSTBOLT_VOLLEY = 8398, + SPELL_MINDFLAY = 17313, + SPELL_FROSTNOVA = 15531 }; -enum Events +enum ScornEvents { - EVENT_LICH_SLAP = 1, + EVENT_LICH_SLAP = 1, EVENT_FROSTBOLT_VOLLEY, EVENT_MIND_FLAY, EVENT_FROST_NOVA }; -class boss_scorn : public CreatureScript +struct boss_scorn : public BossAI { - public: - boss_scorn() : CreatureScript("boss_scorn") { } - - struct boss_scornAI : public BossAI - { - boss_scornAI(Creature* creature) : BossAI(creature, DATA_SCORN) { } - - void Reset() override - { - _Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_LICH_SLAP, 45s); - events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 30s); - events.ScheduleEvent(EVENT_MIND_FLAY, 30s); - events.ScheduleEvent(EVENT_FROST_NOVA, 30s); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - } + boss_scorn(Creature* creature) : BossAI(creature, DATA_SCORN) { } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_LICH_SLAP: - DoCastVictim(SPELL_LICHSLAP); - events.ScheduleEvent(EVENT_LICH_SLAP, 45s); - break; - case EVENT_FROSTBOLT_VOLLEY: - DoCastVictim(SPELL_FROSTBOLT_VOLLEY); - events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 20s); - break; - case EVENT_MIND_FLAY: - DoCastVictim(SPELL_MINDFLAY); - events.ScheduleEvent(EVENT_MIND_FLAY, 20s); - break; - case EVENT_FROST_NOVA: - DoCastVictim(SPELL_FROSTNOVA); - events.ScheduleEvent(EVENT_FROST_NOVA, 15s); - break; - default: - break; - } - } - }; + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(EVENT_LICH_SLAP, 45s); + events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 30s); + events.ScheduleEvent(EVENT_MIND_FLAY, 30s); + events.ScheduleEvent(EVENT_FROST_NOVA, 30s); + } - CreatureAI* GetAI(Creature* creature) const override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - return GetScarletMonasteryAI<boss_scornAI>(creature); + case EVENT_LICH_SLAP: + DoCastVictim(SPELL_LICHSLAP); + events.Repeat(45s); + break; + case EVENT_FROSTBOLT_VOLLEY: + DoCastVictim(SPELL_FROSTBOLT_VOLLEY); + events.Repeat(20s); + break; + case EVENT_MIND_FLAY: + DoCastVictim(SPELL_MINDFLAY); + events.Repeat(20s); + break; + case EVENT_FROST_NOVA: + DoCastVictim(SPELL_FROSTNOVA); + events.Repeat(15s); + break; + default: + break; } + } }; void AddSC_boss_scorn() { - new boss_scorn(); + RegisterScarletMonasteryCreatureAI(boss_scorn); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp index ed2263bea31..ac79ec42da0 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp @@ -15,12 +15,29 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" +#include "scarlet_monastery.h" #include "Creature.h" #include "GameObject.h" #include "InstanceScript.h" #include "Map.h" -#include "scarlet_monastery.h" +#include "ScriptMgr.h" + +ObjectData const creatureData[] = +{ + { NPC_HEAD, DATA_HEAD }, + { NPC_HORSEMAN, DATA_HORSEMAN }, + { NPC_MOGRAINE, DATA_MOGRAINE }, + { NPC_VORREL, DATA_VORREL }, + { NPC_WHITEMANE, DATA_WHITEMANE }, + { 0, 0 } // END +}; + +ObjectData const gameObjectData[] = +{ + { GO_PUMPKIN_SHRINE, DATA_PUMPKIN_SHRINE }, + { GO_HIGH_INQUISITORS_DOOR, DATA_HIGH_INQUISITORS_DOOR }, + { 0, 0 } // END +}; class instance_scarlet_monastery : public InstanceMapScript { @@ -33,62 +50,21 @@ class instance_scarlet_monastery : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(EncounterCount); - } - - void OnGameObjectCreate(GameObject* go) override - { - InstanceScript::OnGameObjectCreate(go); - - switch (go->GetEntry()) - { - case GO_PUMPKIN_SHRINE: - PumpkinShrineGUID = go->GetGUID(); - break; - case GO_HIGH_INQUISITORS_DOOR: - HighInquistorDoorGUID = go->GetGUID(); - break; - default: - break; - } + LoadObjectData(creatureData, gameObjectData); } void OnCreatureCreate(Creature* creature) override { switch (creature->GetEntry()) { - case NPC_HORSEMAN: - HorsemanGUID = creature->GetGUID(); - break; - case NPC_HEAD: - HeadGUID = creature->GetGUID(); - break; case NPC_PUMPKIN: HorsemanAdds.insert(creature->GetGUID()); break; - case NPC_MOGRAINE: - MograineGUID = creature->GetGUID(); - break; - case NPC_WHITEMANE: - WhitemaneGUID = creature->GetGUID(); - break; - case NPC_VORREL: - VorrelGUID = creature->GetGUID(); - break; default: break; } - } - void SetData(uint32 type, uint32 /*data*/) override - { - switch (type) - { - case DATA_PUMPKIN_SHRINE: - HandleGameObject(PumpkinShrineGUID, false); - break; - default: - break; - } + InstanceScript::OnCreatureCreate(creature); } bool SetBossState(uint32 type, EncounterState state) override @@ -99,16 +75,18 @@ class instance_scarlet_monastery : public InstanceMapScript switch (type) { case DATA_HORSEMAN_EVENT: - if (state == DONE) + if (state == DONE || state == FAIL || state == NOT_STARTED) { - for (ObjectGuid guid : HorsemanAdds) + for (ObjectGuid const& guid : HorsemanAdds) { Creature* add = instance->GetCreature(guid); - if (add && add->IsAlive()) - add->KillSelf(); + if (add) + add->DespawnOrUnsummon(); } HorsemanAdds.clear(); - HandleGameObject(PumpkinShrineGUID, false); + + if (state == DONE) + HandleGameObject(ObjectGuid::Empty, false, GetGameObject(DATA_PUMPKIN_SHRINE)); } break; default: @@ -117,39 +95,8 @@ class instance_scarlet_monastery : public InstanceMapScript return true; } - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - /// Creatures - case DATA_MOGRAINE: - return MograineGUID; - case DATA_WHITEMANE: - return WhitemaneGUID; - case DATA_VORREL: - return VorrelGUID; - /// GameObjects - case GO_HIGH_INQUISITORS_DOOR: - return HighInquistorDoorGUID; - default: - break; - } - return ObjectGuid::Empty; - } - protected: - /// Creatures - ObjectGuid HorsemanGUID; - ObjectGuid HeadGUID; - ObjectGuid MograineGUID; - ObjectGuid WhitemaneGUID; - ObjectGuid VorrelGUID; - - /// GameObjects - ObjectGuid PumpkinShrineGUID; - ObjectGuid HighInquistorDoorGUID; - - GuidSet HorsemanAdds; + GuidUnorderedSet HorsemanAdds; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h b/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h index bd2c3f39833..6cd2584f6c9 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h @@ -27,39 +27,42 @@ uint32 const EncounterCount = 10; enum SMDataTypes { - DATA_MOGRAINE_AND_WHITE_EVENT = 1, - DATA_MOGRAINE = 2, - DATA_WHITEMANE = 3, + DATA_INTERROGATOR_VISHAS = 0, + DATA_BLOODMAGE_THALNOS, + DATA_HOUNDMASTER_LOKSEY, + DATA_ARCANIST_DOAN, + DATA_HEROD, + DATA_HIGH_INQUISITOR_FAIRBANKS, + DATA_MOGRAINE_AND_WHITE_EVENT, // Last DungeonEncounter.dbc entry - DATA_HORSEMAN_EVENT = 4, - DATA_PUMPKIN_SHRINE = 5, + DATA_AZSHIR, + DATA_SCORN, + DATA_HORSEMAN_EVENT, // Last defined encounter - DATA_VORREL = 6, - DATA_ARCANIST_DOAN = 7, - DATA_AZSHIR = 8, - DATA_BLOODMAGE_THALNOS = 9, - DATA_HEROD = 10, - DATA_HIGH_INQUISITOR_FAIRBANKS = 11, - DATA_HOUNDMASTER_LOKSEY = 12, - DATA_INTERROGATOR_VISHAS = 13, - DATA_SCORN = 14 + DATA_HEAD, + DATA_HORSEMAN, + DATA_MOGRAINE, + DATA_VORREL, + DATA_WHITEMANE, + + DATA_PUMPKIN_SHRINE, + DATA_HIGH_INQUISITORS_DOOR, }; enum SMCreatureIds { - NPC_MOGRAINE = 3976, - NPC_WHITEMANE = 3977, - NPC_VORREL = 3981, - - NPC_HORSEMAN = 23682, - NPC_HEAD = 23775, - NPC_PUMPKIN = 23694 + NPC_MOGRAINE = 3976, + NPC_WHITEMANE = 3977, + NPC_VORREL = 3981, + NPC_HORSEMAN = 23682, + NPC_HEAD = 23775, + NPC_PUMPKIN = 23694 }; enum SMGameObjectIds { - GO_HIGH_INQUISITORS_DOOR = 104600, - GO_PUMPKIN_SHRINE = 186267 + GO_HIGH_INQUISITORS_DOOR = 104600, + GO_PUMPKIN_SHRINE = 186267 }; template <class AI, class T> @@ -68,6 +71,7 @@ inline AI* GetScarletMonasteryAI(T* obj) return GetInstanceAI<AI>(obj, SMScriptName); } -#define RegisterScarletMonastaryCreatureAI(ai) RegisterCreatureAIWithFactory(ai, GetScarletMonasteryAI) +#define RegisterScarletMonasteryCreatureAI(ai) RegisterCreatureAIWithFactory(ai, GetScarletMonasteryAI) +#define RegisterScarletMonasteryGameObjectAI(ai) RegisterGameObjectAIWithFactory(ai, GetScarletMonasteryAI) #endif // SCARLET_M_ diff --git a/src/server/shared/Dynamic/LinkedReference/RefManager.h b/src/server/shared/Dynamic/LinkedReference/RefManager.h index 208bb75e182..acc3d33f397 100644 --- a/src/server/shared/Dynamic/LinkedReference/RefManager.h +++ b/src/server/shared/Dynamic/LinkedReference/RefManager.h @@ -18,7 +18,6 @@ #ifndef _REFMANAGER_H #define _REFMANAGER_H -//===================================================== #include "Dynamic/LinkedList.h" #include "Dynamic/LinkedReference/Reference.h" @@ -26,28 +25,31 @@ template <class TO, class FROM> class RefManager : public LinkedListHead { - public: - typedef LinkedListHead::Iterator<Reference<TO, FROM>> iterator; - RefManager() { } +public: + typedef LinkedListHead::Iterator<Reference<TO, FROM>> iterator; + typedef LinkedListHead::Iterator<Reference<TO, FROM> const> const_iterator; + RefManager() { } - Reference<TO, FROM>* getFirst() { return static_cast<Reference<TO, FROM>*>(LinkedListHead::getFirst()); } + Reference<TO, FROM>* getFirst() { return static_cast<Reference<TO, FROM>*>(LinkedListHead::getFirst()); } - Reference<TO, FROM> const* getFirst() const { return static_cast<Reference<TO, FROM> const*>(LinkedListHead::getFirst()); } + Reference<TO, FROM> const* getFirst() const { return static_cast<Reference<TO, FROM> const*>(LinkedListHead::getFirst()); } - iterator begin() { return iterator(getFirst()); } - iterator end() { return iterator(nullptr); } + iterator begin() { return iterator(getFirst()); } + iterator end() { return iterator(nullptr); } - virtual ~RefManager() - { - clearReferences(); - } + const_iterator begin() const { return const_iterator(getFirst()); } + const_iterator end() const { return const_iterator(nullptr); } - void clearReferences() - { - while (Reference<TO, FROM>* ref = getFirst()) - ref->invalidate(); - } + virtual ~RefManager() + { + clearReferences(); + } + + void clearReferences() + { + while (Reference<TO, FROM>* ref = getFirst()) + ref->invalidate(); + } }; -//===================================================== #endif |