diff options
Diffstat (limited to 'src')
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 |