aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeader <keader.android@gmail.com>2020-10-26 14:04:36 -0300
committerShauren <shauren.trinity@gmail.com>2022-02-28 23:38:36 +0100
commit8d841679fc07162b44500b1577947c22e8e3d7f2 (patch)
treedfea8abb4923c4b5608bf8774e3b4fb511a8d7c9
parent1ba5e17c950deea4e5eb2962b9769261de089c2c (diff)
Scripts/ScarletMonastery: Headless Horseman Rewrite (#25614)
(cherry picked from commit a93abcf8031fc3b4dc6db16aa09d92a221bc6a77)
-rw-r--r--sql/updates/world/master/2022_02_28_52_world_2020_10_26_00_world.sql65
-rw-r--r--src/server/game/Spells/SpellMgr.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp1400
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp118
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h48
5 files changed, 960 insertions, 679 deletions
diff --git a/sql/updates/world/master/2022_02_28_52_world_2020_10_26_00_world.sql b/sql/updates/world/master/2022_02_28_52_world_2020_10_26_00_world.sql
new file mode 100644
index 00000000000..39955f0aa33
--- /dev/null
+++ b/sql/updates/world/master/2022_02_28_52_world_2020_10_26_00_world.sql
@@ -0,0 +1,65 @@
+-- GameObject update
+UPDATE `gameobject_template` SET `ScriptName` = 'go_headless_horseman_pumpkin' WHERE `entry` = 186267;
+-- Creature Updates
+UPDATE `creature_template` SET `flags_extra` = `flags_extra`|512|2097152 WHERE `entry` = 23682;
+UPDATE `creature_template` SET `flags_extra` =`flags_extra`|512, `ScriptName` = 'npc_pulsing_pumpkin' WHERE `entry` = 23694;
+UPDATE `creature_template` SET `ScriptName` = 'npc_flame_bunny' WHERE `entry` = 23686;
+UPDATE `creature_template` SET `ScriptName` = 'npc_headless_horseman_head', `mechanic_immune_mask`=617299827 WHERE `entry` = 23775;
+UPDATE `creature_template` SET `ScriptName` = 'npc_sir_thomas' WHERE `entry` = 23904;
+UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` = 24034;
+-- Model Updates
+UPDATE `creature_model_info` SET `BoundingRadius` = 0.75, `CombatReach` = 2.25 WHERE `DisplayID` = 22351;
+UPDATE `creature_model_info` SET `BoundingRadius` = 0.5, `CombatReach` = 1 WHERE `DisplayID` = 24720;
+UPDATE `creature_model_info` SET `BoundingRadius` = 0.75, `CombatReach` = 1.125 WHERE `DisplayID` = 21822;
+-- AllowableRaces for Sir Thomas' quests
+-- UPDATE `quest_template` SET `AllowableRaces` = 1101 WHERE `ID` = 11242;
+-- UPDATE `quest_template` SET `AllowableRaces` = 690 WHERE `ID` = 11403;
+
+DELETE FROM `spell_script_names` WHERE `ScriptName` IN
+('spell_headless_horseman_yell_timer',
+'spell_headless_horseman_maniacal_laugh',
+'spell_headless_horseman_head_reposition',
+'spell_headless_horseman_send_head',
+'spell_headless_horseman_head_periodic',
+'spell_headless_horseman_command_head_request_body',
+'spell_headless_horseman_return_head',
+'spell_summon_pumpkin_burst_delay',
+'spell_headless_horseman_head_is_dead',
+'spell_headless_horseman_summoning_rhyme_aura',
+'spell_headless_horseman_sprouting',
+'spell_headless_horseman_wisp_teleport');
+INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
+(42432,'spell_headless_horseman_yell_timer'),
+(43893,'spell_headless_horseman_maniacal_laugh'),
+(42410,'spell_headless_horseman_head_reposition'),
+(42399,'spell_headless_horseman_send_head'),
+(42603,'spell_headless_horseman_head_periodic'),
+(43101,'spell_headless_horseman_command_head_request_body'),
+(42401,'spell_headless_horseman_return_head'),
+(52236,'spell_summon_pumpkin_burst_delay'),
+(42428,'spell_headless_horseman_head_is_dead'),
+(42879,'spell_headless_horseman_summoning_rhyme_aura'),
+(42281,'spell_headless_horseman_sprouting'),
+(42821,'spell_headless_horseman_wisp_teleport'),
+(42818,'spell_headless_horseman_wisp_teleport');
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (42410, 43101, 43306);
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES
+(13,1,42410,0,0,31,0,3,23775,0,0,0,0,'','Effect_0 hits Head of the Horseman'), -- Headless Horseman Climax - Command, Head Repositions
+(13,1,43306,0,0,31,0,3,23682,0,0,0,0,'','Effect_0 hits Headless Horseman'), -- Headless Horseman Climax - Command, Head Requests Body
+(13,1,43101,0,0,31,0,3,23682,0,0,0,0,'','Effect_0 hits Headless Horseman'); -- Headless Horseman Climax - Heal Body
+
+DELETE FROM `creature_text` WHERE `CreatureID` IN (23682,23775);
+INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES
+(23682,0,0,'It is over, your search is done. Let fate choose now, the righteous one.',12,0,100,0,0,11961,22261,0,'Headless Horseman SAY_ENTRANCE'),
+(23682,1,0,'Here\'s my body, fit and pure! Now, your blackened souls I\'ll cure!',14,0,100,0,0,12567,22271,0,'Headless Horseman SAY_REJOINED'),
+(23682,2,0,'Harken, cur! Tis you I spurn! Now feel... the burn!',11,0,100,0,0,12573,22587,0,'Headless Horseman SAY_CONFLAGRATION'),
+(23682,3,0,'Soldiers arise, stand and fight! Bring victory at last to this fallen knight!',12,0,100,0,0,11963,23861,0,'Headless Horseman SAY_SPROUTING_PUMPKINS'),
+(23682,4,0,'This end have I reached before. What new adventure lies in store?',12,0,100,0,0,11964,23455,0,'Headless Horseman SAY_DEATH'),
+(23682,5,0,'Your body lies beaten, battered and broken! Let my curse be your own, fate has spoken!',14,0,100,0,0,11962,40546,0,'Headless Horseman SAY_KILL_PLAYER'),
+(23775,0,0,'So eager you are, for my blood to spill. Yet to vanquish me, \'tis my head you must kill!',14,0,100,0,0,11969,22757,0,'Head of the Horseman SAY_LOST_HEAD'),
+(23775,1,0,'Get over here, you idiot!',12,0,100,0,0,12569,22415,0,'Head of the Horseman SAY_REQUEST_BODY'),
+(23775,2,0,'Horseman rise...',1,0,100,0,0,0,22695,0,'Head of the Horseman SAY_PLAYER_RISE'),
+(23775,3,0,'Your time is nigh...',1,0,100,0,0,0,22696,0,'Head of the Horseman SAY_PLAYER_TIME'),
+(23775,4,0,'You felt death once...',1,0,100,22,0,0,22720,0,'Head of the Horseman SAY_PLAYER_DEATH'),
+(23775,5,0,'Now, know demise!',1,0,100,5,0,0,22721,0,'Head of the Horseman SAY_PLAYER_DEMISE');
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index fa7fd7c0357..dc01b6196ed 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -4648,6 +4648,14 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->NegativeEffects[EFFECT_2] = true;
});
+ // Headless Horseman Climax - Return Head (Hallow End)
+ // Headless Horseman Climax - Body Regen (confuse only - removed on death)
+ // Headless Horseman Climax - Head Is Dead
+ ApplySpellFix({ 42401, 43105, 42428 }, [](SpellInfo* spellInfo)
+ {
+ spellInfo->Attributes |= SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY;
+ });
+
for (SpellInfo const& s : mSpellInfoMap)
{
SpellInfo* spellInfo = &const_cast<SpellInfo&>(s);
diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
index 2349733716a..5bb2abc3814 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
@@ -16,6 +16,7 @@
*/
#include "scarlet_monastery.h"
+#include "CreatureTextMgr.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "Group.h"
@@ -24,113 +25,135 @@
#include "Map.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
+#include "PassiveAI.h"
#include "Player.h"
#include "ScriptedCreature.h"
+#include "ScriptedGossip.h"
#include "ScriptMgr.h"
+#include "SpellAuraEffects.h"
+#include "SpellScript.h"
#include "SpellInfo.h"
-#include "TemporarySummon.h"
-#include "Timer.h"
-// this texts are already used by 3975 and 3976
enum HeadlessHorsemanSays
{
+ // Head
SAY_LOST_HEAD = 0,
- SAY_PLAYER_DEATH = 1,
-
- SAY_ENTRANCE = 0,
+ SAY_REQUEST_BODY = 1,
+ SAY_PLAYER_RISE = 2,
+ SAY_PLAYER_TIME = 3,
+ SAY_PLAYER_DEATH = 4,
+ SAY_PLAYER_DEMISE = 5,
+
+ // Boss
+ SAY_HORSEMAN_ENTRANCE = 0,
SAY_REJOINED = 1,
SAY_CONFLAGRATION = 2,
SAY_SPROUTING_PUMPKINS = 3,
SAY_DEATH = 4,
+ SAY_KILL_PLAYER = 5
};
-std::vector<uint32> HeadlessHorsemanRandomLaughSound = { 11965u, 11975u, 11976u };
+enum HeadlessHorsemanSpells
+{
+ // Horseman & Head
+ SPELL_HEADLESS_HORSEMAN_C_HEAD_VISUAL = 42413,
+ SPELL_HEADLESS_HORSEMAN_ONKILL_PROC = 43877,
+ SPELL_HEADLESS_HORSEMAN_YELL_TIMER = 42432,
+ SPELL_HEADLESS_HORSEMAN_MANIACAL_LAUGH = 43893,
+ SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_1 = 42547,
+ SPELL_HEADLESS_HORSEMAN_CLEAVE = 42587,
+ SPELL_HEADLESS_HORSEMAN_C_COMMAND_HEAD_REPOSITIONS = 42410,
+ SPELL_HEADLESS_HORSEMAN_C_SEND_HEAD = 42399,
+ SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_CONFUSE = 43105,
+ SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_IMMUNE = 42556,
+ SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_TRANSFORM = 42403,
+ SPELL_HEADLESS_HORSEMAN_C_HEAD_STUN = 42408,
+ SPELL_HEADLESS_HORSEMAN_C_HEAD_PERIODIC = 42603,
+ SPELL_HEADLESS_HORSEMAN_C_HEAL_BODY = 43306,
+ SPELL_HEADLESS_HORSEMAN_C_RETURN_HEAD = 42401,
+ SPELL_HEADLESS_HORSEMAN_SPEAKS = 43129,
+ SPELL_HEADLESS_HORSEMAN_C_COMMAND_REQUEST_BODY = 43101,
+ SPELL_HEADLESS_HORSEMAN_C_HEAD_PAUSE = 42504,
+ SPELL_HEADLESS_HORSEMAN_C_HEAD_IS_DEAD = 42428,
+ SPELL_RAIN_OF_TREATS = 43344,
+ SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_2 = 42548,
+ SPELL_HORSEMANS_CONFLAGRATION_SOUND = 48149,
+ SPELL_HORSEMANS_CONFLAGRATION_SOUND_THROTTLE = 48148, // missing soundid
+ SPELL_CONFLAGRATION = 42380,
+ SPELL_HEADLESS_HORSEMAN_C_HORSEMANS_WHIRLWIND = 43116,
+ SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_3 = 42549,
+ SPELL_SUMMON_PUMPKIN_BURST_DELAY = 52236,
+ SPELL_HEADLESS_HORSEMAN_C_BODY_DEATH = 42429,
+ SPELL_HEADLESS_HORSEMAN_BURNING_COSMETIC = 42971,
+ SPELL_HEADLESS_HORSEMAN_C_SUMMONING_RHYME_AURA = 42879,
+ SPELL_HEADLESS_HORSEMAN_C_SUMMONING_RHYME_SHAKE_MEDIUM = 42909,
+ SPELL_HEADLESS_HORSEMAN_C_SUMMONING_RHYME_SHAKE_SMALL = 42910,
+
+ // Pumpkin
+ SPELL_PUMPKIN_LIFE_CYCLE = 42280,
+ SPELL_HEADLESS_HORSEMAN_PUMPKIN_AURA = 42294,
+ SPELL_SPROUTING = 42281,
+ SPELL_SPROUT_BODY = 42285,
+ SPELL_SQUASH_SOUL = 42514,
+
+ // Fire Bunny
+ SPELL_HEADLESS_HORSEMAN_BURNING_COSMETIC_BASE = 43184,
+ SPELL_HEADLESS_HORSEMAN_FIRE_SIZE_BIG = 43148,
+
+ // Sir Thomas
+ SPELL_HEADLESS_HORSEMAN_WISP_INVIS = 42823,
+ SPELL_HEADLESS_HORSEMAN_WISP_FLIGHT_MISSILE = 42821,
+ SPELL_HEADLESS_HORSEMAN_WISP_FLIGHT_PORT = 42818,
+ SPELL_HEADLESS_HORSEMAN_C_GHOST_VISUAL = 42575,
+
+ SPELL_HEADLESS_HORSEMAN_C_ENRAGED_VISUAL = 42438 // Not used, maybe a bersek mechanic?
+};
-enum HeadlessHorsemanEntry
+enum HeadlessEvents
{
- 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
+ EVENT_HORSEMAN_CLEAVE = 1,
+ EVENT_START_NEXT_HEADLESS_PHASE,
+ EVENT_START_HEAD_DELAYED,
+ EVENT_STOP_HEAD_PHASE,
+ EVENT_RAIN_OF_TREATS,
+ EVENT_START_NEXT_PHASE_DELAYED,
+ EVENT_CONFLAGRATE,
+ EVENT_SUMMON_PUMPKIN,
+ EVENT_RANDOM_LAUGH
};
-enum HeadlessHorsemanSpells
+enum HeadlessActions
{
- 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,
+ ACTION_HEAD_START_HEAD_PHASE = 1,
+ ACTION_HEAD_HP_67,
+ ACTION_HEAD_HP_34,
+ ACTION_HEAD_IS_DEAD,
+ ACTION_HORSEMAN_REQUEST_BODY,
+ ACTION_HEAD_RETURN_TO_BODY,
+ ACTION_HEAD_PLAYER_TEXT,
+ ACTION_PUMPKIN_SPROUTING_FINISHED,
+ ACTION_OTHER_OBJECT_ACTIVE
};
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,
-
+ PHASE_1 = 1,
+ PHASE_2 = 2,
+ PHASE_3 = 3,
+ PHASE_LAST = 4,
+ PHASE_DEAD = 5,
+ POINT_HORSEMAN_FINISH_PATH = 1,
+ POINT_HEAD = 2,
LFG_DUNGEONID_THE_HEADLESS_HORSEMAN = 285,
+ SOUNDID_MANIACAL_LAUGH = 11975,
+ SOUNDID_MANIACAL_LAUGH_2 = 11965,
+ SOUNDID_MANIACAL_LAUGH_3 = 11976,
+ QUEST_CALL_THE_HEADLESS_HORSEMAN = 11405
};
-std::vector<Position> const HeadlessHorsemanFlightPoints =
+uint32 const HorsemanPathSize = 20;
+Position const HeadlessHorsemanFlightPoints[HorsemanPathSize] =
{
- { 1754.00f, 1346.00f, 17.50f },
{ 1765.00f, 1347.00f, 19.00f },
{ 1784.00f, 1346.80f, 25.40f },
{ 1803.30f, 1347.60f, 33.00f },
@@ -153,218 +176,360 @@ std::vector<Position> const HeadlessHorsemanFlightPoints =
{ 1758.00f, 1367.00f, 19.51f }
};
-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
-};
+std::vector<uint32> HeadlessHorsemanRandomLaughSound = { SOUNDID_MANIACAL_LAUGH, SOUNDID_MANIACAL_LAUGH_2, SOUNDID_MANIACAL_LAUGH_3 };
+Position const GOPumpkinSpawnPosition = { 1776.27f, 1348.74f, 20.4116f, 6.27281f };
+QuaternionData const GOPumpkinSpawnQuat = QuaternionData(0.0f, 0.0f, 0.00518764f, -0.999987f);
+Position const GOSoilSpawnPosition = { 1765.28f, 1347.46f, 17.5514f, 0.100363f };
+QuaternionData const GOSoilSpawnQuat = QuaternionData(0.0f, 0.0f, 0.0501604f, -0.998741f);
-//@todo Dear Lord, please someone have mercy and let this die soon
-static char const* HeadlessHorsemanInitialPlayerTexts[] =
+struct npc_headless_horseman_head : public PassiveAI
{
- "Horseman rise...",
- "Your time is nigh...",
- "You felt death once...",
- "Now, know demise!"
-};
+ npc_headless_horseman_head(Creature* creature) : PassiveAI(creature), _instance(creature->GetInstanceScript()), _phase(PHASE_1)
+ {
+ creature->SetDisplayFromModel(0);
+ }
-struct npc_wisp_invis : public ScriptedAI
-{
- npc_wisp_invis(Creature* creature) : ScriptedAI(creature), _timer(0s), _creatureType(0), _firstSpell(0), _secondSpell(0)
+ void JustAppeared() override
{
- creature->SetDisplayId(DISPLAYID_INVIS_WISP_INVISIBLE);
+ HandleInitialSetup();
}
- void SetData(uint32 type, uint32 value) override
+ void HandleInitialSetup()
{
- if (type != DATA_INVIS_WISP_CREATURE_TYPE)
- return;
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_STUN);
+ _phase = PHASE_1;
+ _events.SetPhase(PHASE_1);
+ }
+
+ void JustReachedHome() override
+ {
+ _events.Reset();
+ me->GetMotionMaster()->Clear();
+ HandleInitialSetup();
+ me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ PassiveAI::JustReachedHome();
+ }
- switch (_creatureType = value)
+ void DoAction(int32 id) override
+ {
+ switch (id)
{
- case INVIS_WISP_CREATURE_TYPE_PUMPKIN:
- _firstSpell = SPELL_HEADLESS_HORSEMAN___PUMPKIN_AURA;
- break;
- case INVIS_WISP_CREATURE_TYPE_FLAME:
- _timer.Reset(15s);
- _firstSpell = SPELL_HEADLESS_HORSEMAN___FIRE;
- _secondSpell = SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_IS_DEAD;
+ case ACTION_HEAD_START_HEAD_PHASE:
+ _events.ScheduleEvent(EVENT_START_HEAD_DELAYED, 1s);
break;
- case INVIS_WISP_CREATURE_TYPE_SMOKE:
- _timer.Reset(15s);
- _firstSpell = SPELL_HEADLESS_HORSEMAN___SMOKE;
+ case ACTION_HEAD_HP_67:
+ if (_phase == PHASE_1)
+ {
+ ++_phase;
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_HEAD_PERIODIC);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAL_BODY);
+ _events.ScheduleEvent(EVENT_STOP_HEAD_PHASE, 1s);
+ }
break;
- case INVIS_WISP_CREATURE_TYPE_BLUE:
- _timer.Reset(7s);
- _secondSpell = SPELL_HEADLESS_HORSEMAN___WISP_FLIGHT_MISSILE;
+ case ACTION_HEAD_HP_34:
+ if (_phase == PHASE_2)
+ {
+ ++_phase;
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_HEAD_PERIODIC);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_PAUSE);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAL_BODY, true);
+ _events.ScheduleEvent(EVENT_STOP_HEAD_PHASE, 1s);
+ }
break;
default:
break;
}
- if (_firstSpell)
- DoCastSelf(_firstSpell);
}
- void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ void SetGUID(ObjectGuid const& guid, int32 id) override
{
- if (spellInfo->Id == SPELL_HEADLESS_HORSEMAN___WISP_FLIGHT_PORT && _creatureType == 4)
- me->SetDisplayId(DISPLAYID_INVIS_WISP_MAN);
+ if (id != ACTION_HEAD_PLAYER_TEXT)
+ return;
+
+ if (Unit* target = ObjectAccessor::GetUnit(*me, guid))
+ DoCast(target, SPELL_HEADLESS_HORSEMAN_C_SUMMONING_RHYME_AURA, true);
}
- void MoveInLineOfSight(Unit* who) override
+ void HandleStartPhase()
{
- if (!who || _creatureType != INVIS_WISP_CREATURE_TYPE_PUMPKIN || !who->isTargetableForAttack())
- return;
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_HEAD_STUN);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_VISUAL, true);
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->GetMotionMaster()->MoveRandom(30.0f);
- if (me->IsWithinDist(who, 0.1f, false) && !who->HasAura(SPELL_SQUASH_SOUL))
- DoCast(who, SPELL_SQUASH_SOUL);
+ switch (_phase)
+ {
+ case PHASE_1:
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_PERIODIC, true);
+ break;
+ case PHASE_2:
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_PERIODIC, true);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_SPEAKS, true);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_COMMAND_REQUEST_BODY, true);
+ Talk(SAY_REQUEST_BODY);
+ break;
+ case PHASE_3:
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_SPEAKS, true);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_COMMAND_REQUEST_BODY, true);
+ Talk(SAY_REQUEST_BODY);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _events.SetPhase(PHASE_DEAD);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_IS_DEAD);
+ _events.ScheduleEvent(EVENT_RAIN_OF_TREATS, 11s);
}
void UpdateAI(uint32 diff) override
{
- if (_timer.Passed())
+ if (!_events.IsInPhase(PHASE_DEAD) && !UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
return;
- _timer.Update(diff);
- if (_timer.Passed())
+ while (uint32 eventId = _events.ExecuteEvent())
{
- me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN___SMOKE);
- if (_secondSpell)
- DoCast(me, _secondSpell);
- _timer.Reset(0s);
+ switch (eventId)
+ {
+ case EVENT_START_HEAD_DELAYED:
+ HandleStartPhase();
+ break;
+ case EVENT_STOP_HEAD_PHASE:
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_STUN);
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_HEAD_VISUAL);
+ if (Creature* horseman = _instance->GetCreature(DATA_HEADLESS_HORSEMAN))
+ DoCast(horseman, SPELL_HEADLESS_HORSEMAN_C_RETURN_HEAD, true);
+ me->RemoveAllAttackers();
+ me->GetMotionMaster()->Clear();
+ me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ break;
+ case EVENT_RAIN_OF_TREATS:
+ DoCastSelf(SPELL_RAIN_OF_TREATS);
+ me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ _instance->SetData(DATA_PREPARE_RESET, 0);
+ if (GameObject* pumpkin = me->SummonGameObject(GO_PUMPKIN_SHRINE, GOPumpkinSpawnPosition, GOPumpkinSpawnQuat, 7_days))
+ me->RemoveGameObject(pumpkin, false);
+ if (GameObject* soil = me->SummonGameObject(GO_LOOSELY_TURNED_SOIL, GOSoilSpawnPosition, GOSoilSpawnQuat, 7_days))
+ me->RemoveGameObject(soil, false);
+ break;
+ default:
+ break;
+ }
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
}
private:
- TimeTracker _timer;
- uint32 _creatureType;
- uint32 _firstSpell;
- uint32 _secondSpell;
+ InstanceScript* _instance;
+ EventMap _events;
+ uint8 _phase;
};
-struct npc_head : public ScriptedAI
+struct boss_headless_horseman : public ScriptedAI
{
- npc_head(Creature* creature) : ScriptedAI(creature), _laughTimer(randtime(15s, 30s))
+ boss_headless_horseman(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _summons(me), _introDone(false) { }
+
+ void InitializeAI() override
{
- creature->SetReactState(REACT_PASSIVE);
- Initialize();
+ me->SetDisableGravity(true);
+ me->SetHover(true);
+ ScriptedAI::InitializeAI();
}
- void Initialize()
+ void HandleInitialSetup()
{
- _bodyGUID = ObjectGuid::Empty;
- _phase = 0;
- _withBody = true;
- _die = false;
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_VISUAL, true);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_ONKILL_PROC, true);
+ }
+
+ void JustAppeared() override
+ {
+ HandleInitialSetup();
}
void Reset() override
{
- Initialize();
- _laughTimer.Reset(randtime(15s, 30s));
- _scheduler.CancelAll();
+ _events.Reset();
+ _events.SetPhase(PHASE_1);
+ _summons.DespawnAll();
+ me->setActive(false);
+ if (_introDone)
+ me->SetImmuneToPC(false);
+ me->SetWalk(false);
+ }
+
+ void JustReachedHome() override
+ {
+ HandleInitialSetup();
+ me->SetReactState(REACT_AGGRESSIVE);
+ ScriptedAI::JustReachedHome();
+ }
+
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ me->SetCombatPulseDelay(5);
+ me->setActive(true);
+ _events.ScheduleEvent(EVENT_HORSEMAN_CLEAVE, 13s, 0, PHASE_1);
+ _events.ScheduleEvent(EVENT_RANDOM_LAUGH, 30s, 60s);
+
+ if (Creature* head = _instance->GetCreature(DATA_HORSEMAN_HEAD))
+ head->AI()->DoZoneInCombat();
+ }
+
+ void JustSummoned(Creature* summon) override
+ {
+ _summons.Summon(summon);
+ DoZoneInCombat(summon);
+ }
- // Just to be sure it's MOTION_SLOT_DEFAULT is static
- me->GetMotionMaster()->MoveIdle();
+ void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
+ {
+ _summons.Despawn(summon);
}
- void SetData(uint32 type, uint32 value) override
+ void KilledUnit(Unit* victim) override
{
- switch (type)
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_KILL_PLAYER);
+ }
+
+ void DoAction(int32 id) override
+ {
+ switch (id)
{
- case DATA_HEAD_TALK:
- DoTalk(value);
+ case ACTION_HORSEMAN_EVENT_START:
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_YELL_TIMER, true);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_MANIACAL_LAUGH, true);
+ me->GetMotionMaster()->MoveSmoothPath(POINT_HORSEMAN_FINISH_PATH, HeadlessHorsemanFlightPoints, HorsemanPathSize, false);
break;
- case DATA_HEAD_PHASE:
- _phase = value;
+ case ACTION_HORSEMAN_REQUEST_BODY:
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_CONFUSE);
+ me->GetMotionMaster()->Clear();
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HORSEMANS_WHIRLWIND, true);
+ if (Creature* head = _instance->GetCreature(DATA_HORSEMAN_HEAD))
+ {
+ me->SetWalk(true);
+ me->GetMotionMaster()->MovePoint(POINT_HEAD, head->GetPosition());
+ }
+ break;
+ case ACTION_HEAD_RETURN_TO_BODY:
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_CONFUSE);
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_IMMUNE);
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_TRANSFORM);
+ Talk(SAY_REJOINED);
+ _events.ScheduleEvent(EVENT_START_NEXT_PHASE_DELAYED, 3s);
+ break;
+ case ACTION_HEAD_IS_DEAD:
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_IMMUNE);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_DEATH, true);
break;
default:
break;
}
}
- void DamageTaken(Unit* /*attacker*/, uint32& damage) override
+ void JustDied(Unit* /*killer*/) override
{
- if (_withBody)
- return;
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_BURNING_COSMETIC);
+ _instance->SetData(DATA_HORSEMAN_EVENT_STATE, DONE);
+ Talk(SAY_DEATH);
+ me->SummonCreature(NPC_SIR_THOMAS, me->GetPosition());
+ // Credit LFG
+ if (me->GetMap()->HavePlayers())
+ {
+ Map* map = me->GetMap();
+ if (Group* group = map->GetPlayers().begin()->GetSource()->GetGroup())
+ if (group->isLFGGroup())
+ sLFGMgr->FinishDungeon(group->GetGUID(), LFG_DUNGEONID_THE_HEADLESS_HORSEMAN, map);
+ }
+ }
- if (_die)
+ void DamageTaken(Unit* who, uint32& damage) override
+ {
+ if (damage >= me->GetHealth() && who != me)
{
damage = 0;
- return;
+
+ if (_events.IsInPhase(PHASE_1))
+ StartPhase(PHASE_2);
+ else if (_events.IsInPhase(PHASE_2))
+ StartPhase(PHASE_3);
+ else if (_events.IsInPhase(PHASE_3))
+ StartPhase(PHASE_LAST);
}
+ }
- switch (_phase)
+ void StartPhase(uint8 nextPhase)
+ {
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_IMMUNE, true);
+ _events.SetPhase(nextPhase);
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_HORSEMANS_WHIRLWIND);
+ me->SetWalk(false);
+
+ switch (nextPhase)
{
- case PHASE_HEAD_1:
- if (me->HealthBelowPctDamaged(67, damage))
- ReturnToBody(true);
+ case PHASE_2:
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_1);
break;
- case PHASE_HEAD_2:
- if (me->HealthBelowPctDamaged(34, damage))
- ReturnToBody(true);
+ case PHASE_3:
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_2);
break;
- case PHASE_HEAD_3:
- if (damage >= me->GetHealth())
- {
- _die = true;
- damage = 0;
- me->AddUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
- me->GetMotionMaster()->Clear();
-
- DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_IS_DEAD);
-
- _scheduler.Schedule(3s, [this](TaskContext /*context*/)
- {
- if (Unit* body = ObjectAccessor::GetUnit(*me, _bodyGUID))
- body->KillSelf();
- me->KillSelf();
- });
- }
+ case PHASE_LAST:
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_3);
+ me->RemoveAurasDueToSpell(SPELL_SUMMON_PUMPKIN_BURST_DELAY);
break;
default:
break;
}
+
+ _events.ScheduleEvent(EVENT_START_NEXT_HEADLESS_PHASE, 1ms, 0, nextPhase);
}
- void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override
+ void SchedulePhase2Events()
{
- Unit* unitCaster = caster->ToUnit();
- if (!unitCaster)
- return;
-
- if (!_withBody)
- return;
-
- if (spellInfo->Id == SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD)
- {
- _withBody = false;
-
- if (!_bodyGUID)
- _bodyGUID = unitCaster->GetGUID();
-
- me->RemoveAllAuras();
- me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->GetMotionMaster()->Clear();
-
- DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_LANDS, true);
- DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_VISUAL);
-
- DoTalk(SAY_LOST_HEAD);
+ _events.ScheduleEvent(EVENT_CONFLAGRATE, 5s, 0, PHASE_2);
+ _events.ScheduleEvent(EVENT_HORSEMAN_CLEAVE, 16s, 0, PHASE_2);
+ }
- _scheduler.Schedule(2s, [unitCaster, this](TaskContext /*context*/)
- {
- me->GetMotionMaster()->MoveFleeing(unitCaster);
- });
- }
+ void SchedulePhase3Events()
+ {
+ _events.ScheduleEvent(EVENT_SUMMON_PUMPKIN, 1s, 0, PHASE_3);
+ _events.ScheduleEvent(EVENT_HORSEMAN_CLEAVE, 9s, 0, PHASE_3);
}
- void DoAction(int32 param) override
+ void MovementInform(uint32 type, uint32 id) override
{
- switch (param)
+ if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE)
+ return;
+
+ switch (id)
{
- case ACTION_HEAD_RETURN_TO_BODY:
- ReturnToBody(false);
+ case POINT_HORSEMAN_FINISH_PATH:
+ _introDone = true;
+ me->SetImmuneToPC(false);
+ me->SetDisableGravity(false);
+ me->SetHover(false);
+ me->SetHomePosition(me->GetPosition());
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_1);
+ me->SetReactState(REACT_AGGRESSIVE);
+ DoZoneInCombat();
+ break;
+ case POINT_HEAD:
+ me->SetWalk(false);
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_HORSEMANS_WHIRLWIND);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_CONFUSE, true);
break;
default:
break;
@@ -373,562 +538,531 @@ struct npc_head : public ScriptedAI
void UpdateAI(uint32 diff) override
{
- _scheduler.Update(diff);
-
- if (_withBody)
+ if (!UpdateVictim())
return;
- if (!_laughTimer.Passed())
- _laughTimer.Update(diff);
+ _events.Update(diff);
- if (_laughTimer.Passed())
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = _events.ExecuteEvent())
{
- _laughTimer.Reset(randtime(15s, 30s));
+ switch (eventId)
+ {
+ case EVENT_HORSEMAN_CLEAVE:
+ DoCastVictim(SPELL_HEADLESS_HORSEMAN_CLEAVE);
+ _events.Repeat(6s, 12s);
+ break;
+ case EVENT_START_NEXT_HEADLESS_PHASE:
+ DoCastAOE(SPELL_HEADLESS_HORSEMAN_C_COMMAND_HEAD_REPOSITIONS);
+ if (Creature* head = _instance->GetCreature(DATA_HORSEMAN_HEAD))
+ DoCast(head, SPELL_HEADLESS_HORSEMAN_C_SEND_HEAD, true);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_CONFUSE, true);
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_C_HEAD_VISUAL);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_REGEN_TRANSFORM, true);
+ break;
+ case EVENT_START_NEXT_PHASE_DELAYED:
+ me->SetReactState(REACT_AGGRESSIVE);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_HEAD_VISUAL);
+ if (_events.IsInPhase(PHASE_2))
+ {
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_2);
+ SchedulePhase2Events();
+ }
+ else if (_events.IsInPhase(PHASE_3))
+ {
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_BODY_STAGE_3);
+ SchedulePhase3Events();
+ }
- DoPlaySoundToSet(me, Trinity::Containers::SelectRandomContainerElement(HeadlessHorsemanRandomLaughSound));
+ if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat))
+ AttackStart(target);
+ break;
+ case EVENT_CONFLAGRATE:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true, false))
+ {
+ DoCastSelf(SPELL_HORSEMANS_CONFLAGRATION_SOUND, true);
+ DoCastSelf(SPELL_HORSEMANS_CONFLAGRATION_SOUND_THROTTLE, true);
+ DoCast(target, SPELL_CONFLAGRATION);
+ Talk(SAY_CONFLAGRATION);
+ }
+ _events.Repeat(18s, 21s);
+ break;
+ case EVENT_SUMMON_PUMPKIN:
+ DoCastSelf(SPELL_SUMMON_PUMPKIN_BURST_DELAY);
+ _events.Repeat(30s);
+ break;
+ case EVENT_RANDOM_LAUGH:
+ DoPlaySoundToSet(me, Trinity::Containers::SelectRandomContainerElement(HeadlessHorsemanRandomLaughSound));
+ _events.Repeat(30s, 60s);
+ break;
+ default:
+ break;
+ }
- if (Creature* speaker = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_DESPAWN, 1s))
- speaker->CastSpell(speaker, SPELL_HEADLESS_HORSEMAN___SPEAKS, false);
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
+
+ DoMeleeAttackIfReady();
}
private:
- void ReturnToBody(bool advance)
- {
- if (_withBody || _bodyGUID.IsEmpty())
- return;
+ InstanceScript* _instance;
+ EventMap _events;
+ SummonList _summons;
+ bool _introDone;
+};
- Creature* body = ObjectAccessor::GetCreature(*me, _bodyGUID);
- if (!body || !body->IsAlive())
- {
- me->DespawnOrUnsummon();
- return;
- }
+struct npc_pulsing_pumpkin : public ScriptedAI
+{
+ npc_pulsing_pumpkin(Creature* creature) : ScriptedAI(creature) { }
- _withBody = true;
- me->RemoveAllAuras();
- me->SetFullHealth();
- me->AddUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE));
- me->GetMotionMaster()->MoveIdle();
+ void InitializeAI() override
+ {
+ me->SetDisableGravity(true);
+ me->SetHover(true);
+ ScriptedAI::InitializeAI();
+ }
- if (advance)
- body->AI()->DoAction(ACTION_HEAD_KILLED);
+ void Reset() override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ DoCastSelf(SPELL_PUMPKIN_LIFE_CYCLE);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_PUMPKIN_AURA);
+ DoCastSelf(SPELL_SPROUTING);
+ }
+
+ void DoAction(int32 id) override
+ {
+ if (id != ACTION_PUMPKIN_SPROUTING_FINISHED)
+ return;
- 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);
+ _scheduler.Schedule(1s, [this](TaskContext /*context*/)
+ {
+ me->RemoveAurasDueToSpell(SPELL_PUMPKIN_LIFE_CYCLE);
+ me->SetDisableGravity(false);
+ me->SetHover(false);
+ DoCastSelf(SPELL_SPROUT_BODY, true);
+ me->UpdateEntry(NPC_PUMPKIN_FIEND, nullptr, false);
+ me->SetImmuneToNPC(true);
+ me->SetReactState(REACT_AGGRESSIVE);
+ DoZoneInCombat();
+ }).Schedule(11s, [this](TaskContext squashSoul)
+ {
+ DoCastVictim(SPELL_SQUASH_SOUL);
+ squashSoul.Repeat(10s);
+ });
}
- void DoTalk(uint32 entry)
+ void UpdateAI(uint32 diff) override
{
- Talk(entry);
- _laughTimer.Reset(3s);
+ if (!UpdateVictim())
+ return;
+
+ _scheduler.Update(diff);
- if (Creature* speaker = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_DESPAWN, 1s))
- speaker->CastSpell(speaker, SPELL_HEADLESS_HORSEMAN___SPEAKS, false);
+ DoMeleeAttackIfReady();
}
+private:
TaskScheduler _scheduler;
- TimeTracker _laughTimer;
- ObjectGuid _bodyGUID;
- uint32 _phase;
- bool _withBody;
- bool _die;
};
-struct boss_headless_horseman : public ScriptedAI
+struct npc_flame_bunny : public PassiveAI
{
- boss_headless_horseman(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _laughTimer(0s), _phase(0), _id(0)
+ npc_flame_bunny(Creature* creature) : PassiveAI(creature)
{
- Initialize();
+ creature->SetDisplayFromModel(1);
+ }
- _scheduler.SetValidator([this]
+ void Reset() override
+ {
+ _scheduler.Schedule(1s, [this](TaskContext /*context*/)
+ {
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_BURNING_COSMETIC_BASE);
+ }).Schedule(3s, [this](TaskContext grow)
{
- return !me->HasUnitState(UNIT_STATE_CASTING);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_FIRE_SIZE_BIG);
+ if (grow.GetRepeatCounter() < 2)
+ grow.Repeat(2s + 200ms);
});
}
- void Initialize()
+ void UpdateAI(uint32 diff) override
{
- _withHead = true;
+ _scheduler.Update(diff);
}
- void InitializeAI() override
- {
- me->SetImmuneToPC(false);
- ScriptedAI::InitializeAI();
- }
+private:
+ TaskScheduler _scheduler;
+};
+
+struct npc_sir_thomas : public PassiveAI
+{
+ npc_sir_thomas(Creature* creature) : PassiveAI(creature) { }
void Reset() override
{
- _laughTimer.Reset(0s);
- Initialize();
+ me->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
+ me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_WISP_INVIS);
- DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_VISUAL);
- if (!_headGUID.IsEmpty())
+ _scheduler.Schedule(9s, [this](TaskContext /*context*/)
{
- if (Creature* head = ObjectAccessor::GetCreature(*me, _headGUID))
- head->DespawnOrUnsummon();
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_WISP_FLIGHT_MISSILE);
+ });
+ }
- _headGUID.Clear();
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ if (spellInfo->Id == SPELL_HEADLESS_HORSEMAN_WISP_FLIGHT_PORT)
+ {
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->AddNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
+ me->RemoveAurasDueToSpell(SPELL_HEADLESS_HORSEMAN_WISP_INVIS);
+ DoCastSelf(SPELL_HEADLESS_HORSEMAN_C_GHOST_VISUAL, true);
}
-
- me->SetImmuneToPC(false);
-
- // Just to be sure it's MOTION_SLOT_DEFAULT is static
- me->GetMotionMaster()->MoveIdle();
-
- _instance->SetBossState(DATA_HORSEMAN_EVENT, NOT_STARTED);
}
- void EnterEvadeMode(EvadeReason why) override
+ void UpdateAI(uint32 diff) override
{
- _phase = PHASE_BODY_1;
- ScriptedAI::EnterEvadeMode(why);
+ _scheduler.Update(diff);
}
- void DoAction(int32 param) override
+private:
+ TaskScheduler _scheduler;
+};
+
+struct go_loosely_turned_soil : public GameObjectAI
+{
+ go_loosely_turned_soil(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()) { }
+
+ bool OnGossipHello(Player* player) override
{
- switch (param)
- {
- case ACTION_HORSEMAN_EVENT_START:
- me->SetVisible(false);
- me->AddUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
- me->SetCanFly(true);
- me->SetWalk(false);
+ if (player->GetQuestStatus(QUEST_CALL_THE_HEADLESS_HORSEMAN) != QUEST_STATUS_COMPLETE)
+ return true;
- _id = 0;
- _phase = PHASE_BODY_0;
+ if (_instance->GetData(DATA_HORSEMAN_EVENT_STATE) != NOT_STARTED)
+ return true;
- _scheduler.Schedule(3s, [this](TaskContext talkContext)
- {
- if (talkContext.GetRepeatCounter() < 3)
- {
- if (me->GetMap()->HavePlayers())
- {
- 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);
- }
- talkContext.Repeat(3s);
- }
- else
- {
- DoCast(SPELL_HEADLESS_HORSEMAN_CLIMAX___SUMMONING_RHYME_SHAKE_MEDIUM);
- if (me->GetMap()->HavePlayers())
- {
- 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);
- player->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT);
- }
- }
-
- me->GetMotionMaster()->MovePoint(_id, HeadlessHorsemanFlightPoints[_id]);
- }
- });
- break;
- case ACTION_HEAD_KILLED:
- if (_phase < 3)
- ++_phase;
- else
- _phase = 3;
- break;
- default:
- break;
- }
+ return false;
}
- void MovementInform(uint32 type, uint32 id) override
+ void OnQuestReward(Player* player, Quest const* /*quest*/, LootItemType /*type*/, uint32 /*opt*/) override
{
- if (type != POINT_MOTION_TYPE || _phase != PHASE_BODY_0 || id != _id)
- return;
+ player->AreaExploredOrEventHappens(QUEST_CALL_THE_HEADLESS_HORSEMAN);
+ _instance->SetData(DATA_START_HORSEMAN_EVENT, 0);
+ if (Creature* head = _instance->GetCreature(DATA_HORSEMAN_HEAD))
+ head->AI()->SetGUID(player->GetGUID(), ACTION_HEAD_PLAYER_TEXT);
+ }
- 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, 20s))
- 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->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
- me->SetHomePosition(me->GetPosition());
- DoTalk(SAY_ENTRANCE);
- DoZoneInCombat();
- break;
- default:
- break;
- }
+private:
+ InstanceScript* _instance;
+};
- ++_id;
- if (_id <= POINT_HORSEMAN_20)
- me->GetMotionMaster()->MovePoint(_id, HeadlessHorsemanFlightPoints[_id]);
- }
+struct go_headless_horseman_pumpkin : public GameObjectAI
+{
+ go_headless_horseman_pumpkin(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()) { }
- void JustEngagedWith(Unit* /*who*/) override
+ bool OnGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
- _instance->SetBossState(DATA_HORSEMAN_EVENT, IN_PROGRESS);
- DoZoneInCombat();
+ ClearGossipMenuFor(player);
- _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 (_instance->GetData(DATA_HORSEMAN_EVENT_STATE) != NOT_STARTED)
{
- if (Creature* flame = me->SummonCreature(NPC_HELPER, HeadlessHorsemanSpawnPoints[0], TEMPSUMMON_TIMED_DESPAWN, 17s))
- flame->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_FLAME);
- });
- }
+ CloseGossipMenuFor(player);
+ return true;
+ }
- void MoveInLineOfSight(Unit* who) override
- {
- if (_withHead && _phase != PHASE_BODY_0)
- ScriptedAI::MoveInLineOfSight(who);
+ player->AreaExploredOrEventHappens(QUEST_CALL_THE_HEADLESS_HORSEMAN);
+ _instance->SetData(DATA_START_HORSEMAN_EVENT, 0);
+ if (Creature* head = _instance->GetCreature(DATA_HORSEMAN_HEAD))
+ head->AI()->SetGUID(player->GetGUID(), ACTION_HEAD_PLAYER_TEXT);
+
+ CloseGossipMenuFor(player);
+ return true;
}
- void KilledUnit(Unit* player) override
- {
- if (player->GetTypeId() != TYPEID_PLAYER)
- return;
+private:
+ InstanceScript* _instance;
+};
- if (_withHead)
- DoTalk(SAY_PLAYER_DEATH);
- else if (Creature* head = ObjectAccessor::GetCreature(*me, _headGUID))
- head->AI()->SetData(DATA_HEAD_TALK, SAY_PLAYER_DEATH);
- }
+// 42432 - Headless Horseman Yell Timer
+class spell_headless_horseman_yell_timer : public AuraScript
+{
+ PrepareAuraScript(spell_headless_horseman_yell_timer);
- void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override
+ void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
- Unit* unitTarget = target->ToUnit();
- if (!unitTarget)
+ Creature* horseman = GetTarget()->ToCreature();
+ if (!horseman || !horseman->IsAIEnabled())
return;
- if (spellInfo->Id == SPELL_CONFLAGRATION && unitTarget->HasAura(SPELL_CONFLAGRATION))
- DoTalk(SAY_CONFLAGRATION, unitTarget);
+ horseman->AI()->Talk(SAY_HORSEMAN_ENTRANCE);
}
- void JustDied(Unit* /*killer*/) override
+ void Register() override
{
- DoTalk(SAY_DEATH);
- if (Creature* flame = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_DESPAWN, 60s))
- 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, 60s))
- wisp->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_BLUE);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_headless_horseman_yell_timer::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+};
- _instance->SetBossState(DATA_HORSEMAN_EVENT, DONE);
+// 43893 - Headless Horseman - Maniacal Laugh, Maniacal, Delayed 8
+class spell_headless_horseman_maniacal_laugh : public AuraScript
+{
+ PrepareAuraScript(spell_headless_horseman_maniacal_laugh);
- if (me->GetMap()->HavePlayers())
- {
- if (Group* group = me->GetMap()->GetPlayers().begin()->GetSource()->GetGroup())
- if (group->isLFGGroup())
- sLFGMgr->FinishDungeon(group->GetGUID(), LFG_DUNGEONID_THE_HEADLESS_HORSEMAN, me->GetMap());
- }
+ void HandleSound(AuraEffect const* /*aurEff*/)
+ {
+ GetTarget()->PlayDirectSound(SOUNDID_MANIACAL_LAUGH);
}
- void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override
+ void Register() override
{
- Unit* unitCaster = caster->ToUnit();
- if (!unitCaster)
- return;
-
- if (_withHead)
- return;
-
- if (spellInfo->Id != SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD)
- return;
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_headless_horseman_maniacal_laugh::HandleSound, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+};
- _laughTimer.Reset(randtime(2s, 5s));
- _withHead = true;
- _scheduler.CancelGroup(TASK_GROUP_WITHOUT_HEAD);
+// 42410 - Headless Horseman Climax - Command, Head Repositions
+class spell_headless_horseman_head_reposition : public SpellScript
+{
+ PrepareSpellScript(spell_headless_horseman_head_reposition);
- me->InterruptNonMeleeSpells(true);
- me->RemoveAllAuras();
- me->SetName("Headless Horseman"); //@todo THIS can't be serious
- me->SetFullHealth();
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ Position random = GetCaster()->GetRandomNearPosition(30.0f);
+ GetHitUnit()->NearTeleportTo(random, false);
+ }
- DoTalk(SAY_REJOINED);
- DoCastSelf(SPELL_HEADLESS_HORSEMAN_CLIMAX___HEAD_VISUAL);
- unitCaster->GetMotionMaster()->Clear();
- unitCaster->GetMotionMaster()->MoveFollow(me, 6.f, 0.f);
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_headless_horseman_head_reposition::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
- switch (_phase)
- {
- 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, 17s))
- 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(SelectTargetMethod::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;
- }
+// 42399 - Headless Horseman Climax - Send Head
+class spell_headless_horseman_send_head : public SpellScript
+{
+ PrepareSpellScript(spell_headless_horseman_send_head);
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ Unit* head = GetHitUnit();
+ if (head->IsAIEnabled())
+ head->GetAI()->DoAction(ACTION_HEAD_START_HEAD_PHASE);
}
- void DamageTaken(Unit* /*attacker*/, uint32& damage) override
+ void Register() override
{
- if (damage < me->GetHealth() || !_withHead)
- {
- if (damage >= me->GetHealth() && !_withHead)
- damage = 0;
- return;
- }
+ OnEffectHitTarget += SpellEffectFn(spell_headless_horseman_send_head::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
- 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
-
- 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, 0s))
- {
- _headGUID = newHead->GetGUID();
- head = newHead;
- }
- }
+// 42603 - Headless Horseman Climax, Head: Periodic
+class spell_headless_horseman_head_periodic : public AuraScript
+{
+ PrepareAuraScript(spell_headless_horseman_head_periodic);
- if (!head)
- head = ObjectAccessor::GetCreature(*me, _headGUID);
- if (head && head->IsAlive())
- {
- head->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
- head->AI()->SetData(DATA_HEAD_PHASE, _phase);
- DoCast(head, SPELL_HEADLESS_HORSEMAN_CLIMAX___SEND_HEAD, true);
- }
+ void HandleHPCheck(AuraEffect const* /*aurEff*/)
+ {
+ Unit* target = GetTarget();
- _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 (target->HealthBelowPct(34))
+ target->GetAI()->DoAction(ACTION_HEAD_HP_34);
+ else if (target->HealthBelowPct(67))
+ target->GetAI()->DoAction(ACTION_HEAD_HP_67);
- if (!_withHead)
- whirlwindContext.Repeat(4s, 8s);
- }).Schedule(1s, uint32(TASK_GROUP_WITHOUT_HEAD), [this](TaskContext regenerateContext)
- {
- 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);
- });
}
- void UpdateAI(uint32 diff) override
+ void Register() override
{
- if (!_laughTimer.Passed())
- _laughTimer.Update(diff);
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_headless_horseman_head_periodic::HandleHPCheck, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+};
- if (_withHead && _laughTimer.Passed())
- {
- _laughTimer.Reset(randtime(11s, 22s));
- DoPlaySoundToSet(me, Trinity::Containers::SelectRandomContainerElement(HeadlessHorsemanRandomLaughSound));
- }
+// 43101 - Headless Horseman Climax - Command, Head Requests Body
+class spell_headless_horseman_command_head_request_body : public SpellScript
+{
+ PrepareSpellScript(spell_headless_horseman_command_head_request_body);
- if (UpdateVictim())
- {
- _scheduler.Update(diff, [this]
- {
- if (_withHead)
- DoMeleeAttackIfReady();
- });
- }
- else
- _scheduler.Update(diff);
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ Unit* horseman = GetHitUnit();
+ if (horseman->IsAIEnabled())
+ horseman->GetAI()->DoAction(ACTION_HORSEMAN_REQUEST_BODY);
}
-private:
- void DoTalk(uint8 textEntry, Unit* target = nullptr)
+ void Register() override
{
- Talk(textEntry, target);
- _laughTimer.Reset(std::min(std::chrono::duration_cast<Milliseconds>(10s), _laughTimer.GetExpiry() + 4s));
+ OnEffectHitTarget += SpellEffectFn(spell_headless_horseman_command_head_request_body::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
-
- InstanceScript* _instance;
- TaskScheduler _scheduler;
- TimeTracker _laughTimer;
- ObjectGuid _headGUID;
- uint32 _phase;
- uint32 _id;
- bool _withHead;
};
-struct npc_pulsing_pumpkin : public ScriptedAI
+// 42401 - Headless Horseman Climax - Return Head
+class spell_headless_horseman_return_head : public SpellScript
{
- npc_pulsing_pumpkin(Creature* creature) : ScriptedAI(creature)
+ PrepareSpellScript(spell_headless_horseman_return_head);
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
- _sprouted = false;
+ Unit* horseman = GetHitUnit();
+ if (horseman->IsAIEnabled())
+ horseman->GetAI()->DoAction(ACTION_HEAD_RETURN_TO_BODY);
}
- void Reset() override
+ void Register() override
{
- _debuffGUID.Clear();
-
- Despawn();
-
- Creature* debuff = DoSpawnCreature(NPC_HELPER, 0.f, 0.f, 0.f, 0.f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 14500ms);
- if (debuff)
- {
- debuff->SetDisplayId(me->GetDisplayId());
- debuff->AI()->SetData(DATA_INVIS_WISP_CREATURE_TYPE, INVIS_WISP_CREATURE_TYPE_PUMPKIN);
- _debuffGUID = debuff->GetGUID();
- }
-
- _sprouted = false;
-
- DoCastSelf(SPELL_PUMPKIN_LIFE_CYCLE, true);
- DoCastSelf(SPELL_SPROUTING);
- me->AddUnitFlag(UNIT_FLAG_STUNNED);
+ OnEffectHitTarget += SpellEffectFn(spell_headless_horseman_return_head::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
+};
- void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+// 52236 - Summon Pumpkin Burst Delay
+class spell_summon_pumpkin_burst_delay : public AuraScript
+{
+ PrepareAuraScript(spell_summon_pumpkin_burst_delay);
+
+ void HandleText(AuraEffect const* /*aurEff*/)
{
- if (spellInfo->Id == SPELL_SPROUTING)
- {
- _sprouted = true;
- me->RemoveAllAuras();
- me->RemoveUnitFlag(UNIT_FLAG_STUNNED);
- DoCastSelf(SPELL_SPROUT_BODY, true);
- DoZoneInCombat();
- }
+ if (Creature* horseman = GetTarget()->ToCreature())
+ horseman->AI()->Talk(SAY_SPROUTING_PUMPKINS);
}
- void JustDied(Unit* /*killer*/) override
+ void Register() override
{
- if (!_sprouted)
- Despawn();
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_summon_pumpkin_burst_delay::HandleText, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
+};
+
+// 42428 - Headless Horseman Climax - Head Is Dead
+class spell_headless_horseman_head_is_dead : public SpellScript
+{
+ PrepareSpellScript(spell_headless_horseman_head_is_dead);
- void MoveInLineOfSight(Unit* who) override
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
- if (_sprouted)
- ScriptedAI::MoveInLineOfSight(who);
+ Creature* target = GetHitCreature();
+ if (!target || !target->IsAIEnabled())
+ return;
+
+ switch (target->GetEntry())
+ {
+ case NPC_HEADLESS_HORSEMAN:
+ target->AI()->DoAction(ACTION_HEAD_IS_DEAD);
+ break;
+ case NPC_PULSING_PUMPKIN:
+ case NPC_PUMPKIN_FIEND:
+ target->KillSelf();
+ break;
+ default:
+ break;
+ }
}
- void UpdateAI(uint32 /*diff*/) override
+ void Register() override
{
- if (_sprouted && UpdateVictim())
- DoMeleeAttackIfReady();
+ OnEffectHitTarget += SpellEffectFn(spell_headless_horseman_head_is_dead::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
+};
-private:
- void Despawn()
+// 42879 - Headless Horseman Climax - Summoning Rhyme Aura
+class spell_headless_horseman_summoning_rhyme_aura : public AuraScript
+{
+ PrepareAuraScript(spell_headless_horseman_summoning_rhyme_aura);
+
+ void PeriodicTick(AuraEffect const* aurEff)
{
- if (!_debuffGUID)
+ if (!GetCaster())
return;
- Creature* debuff = ObjectAccessor::GetCreature(*me, _debuffGUID);
- if (debuff)
- debuff->DespawnOrUnsummon();
+ Creature* caster = GetCaster()->ToCreature();
+ Player* player = GetTarget()->ToPlayer();
+ if (!caster || !player)
+ return;
- _debuffGUID.Clear();
+ switch (aurEff->GetTickNumber())
+ {
+ case 1:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_RISE, nullptr, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, SoundKitPlayType::Normal, TEAM_OTHER, false, player);
+ break;
+ case 3:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_TIME, nullptr, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, SoundKitPlayType::Normal, TEAM_OTHER, false, player);
+ break;
+ case 5:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_DEATH, nullptr, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, SoundKitPlayType::Normal, TEAM_OTHER, false, player);
+ player->CastSpell(player, SPELL_HEADLESS_HORSEMAN_C_SUMMONING_RHYME_SHAKE_SMALL, true);
+ break;
+ case 8:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_DEMISE, nullptr, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, SoundKitPlayType::Normal, TEAM_OTHER, false, player);
+ player->CastSpell(player, SPELL_HEADLESS_HORSEMAN_C_SUMMONING_RHYME_SHAKE_MEDIUM, true);
+ Remove();
+ break;
+ default:
+ break;
+ }
}
- ObjectGuid _debuffGUID;
- bool _sprouted;
-};
-
-enum LooselyTurnedSoil
-{
- QUEST_CALL_THE_HEADLESS_HORSEMAN = 11405
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_headless_horseman_summoning_rhyme_aura::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
};
-struct go_loosely_turned_soil : public GameObjectAI
+// 42281 - Sprouting
+class spell_headless_horseman_sprouting : public SpellScript
{
- go_loosely_turned_soil(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { }
+ PrepareSpellScript(spell_headless_horseman_sprouting);
- bool OnGossipHello(Player* player) override
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
- if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS || player->GetQuestStatus(QUEST_CALL_THE_HEADLESS_HORSEMAN) != QUEST_STATUS_COMPLETE)
- return true;
-
- return false;
+ Unit* pumpkin = GetHitUnit();
+ if (pumpkin->IsAIEnabled())
+ pumpkin->GetAI()->DoAction(ACTION_PUMPKIN_SPROUTING_FINISHED);
}
- void OnQuestReward(Player* player, Quest const* /*quest*/, LootItemType /*type*/, uint32 /*opt*/) override
+ void Register() override
{
- if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS)
- return;
+ OnEffectHitTarget += SpellEffectFn(spell_headless_horseman_sprouting::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
- player->AreaExploredOrEventHappens(11405);
+// 42821 - Headless Horseman - Wisp Flight Missile
+// 42818 - Headless Horseman - Wisp Flight Port
+class spell_headless_horseman_wisp_teleport : public SpellScript
+{
+ PrepareSpellScript(spell_headless_horseman_wisp_teleport);
- if (TempSummon* horseman = me->GetMap()->SummonCreature(NPC_HEADLESS_HORSEMAN_MOUNTED, HeadlessHorsemanFlightPoints[20]))
- {
- horseman->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN);
- horseman->AI()->DoAction(ACTION_HORSEMAN_EVENT_START);
- }
+ void SetDest(SpellDestination& dest)
+ {
+ dest.Relocate(EarthBunnySpawnPosition);
}
-private:
- InstanceScript* instance;
+ void Register() override
+ {
+ OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_headless_horseman_wisp_teleport::SetDest, EFFECT_0, TARGET_DEST_NEARBY_ENTRY);
+ }
};
void AddSC_boss_headless_horseman()
{
RegisterScarletMonasteryCreatureAI(boss_headless_horseman);
- RegisterScarletMonasteryCreatureAI(npc_head);
+ RegisterScarletMonasteryCreatureAI(npc_headless_horseman_head);
RegisterScarletMonasteryCreatureAI(npc_pulsing_pumpkin);
- RegisterScarletMonasteryCreatureAI(npc_wisp_invis);
+ RegisterScarletMonasteryCreatureAI(npc_flame_bunny);
+ RegisterScarletMonasteryCreatureAI(npc_sir_thomas);
RegisterScarletMonasteryGameObjectAI(go_loosely_turned_soil);
+ RegisterScarletMonasteryGameObjectAI(go_headless_horseman_pumpkin);
+ RegisterSpellScript(spell_headless_horseman_yell_timer);
+ RegisterSpellScript(spell_headless_horseman_maniacal_laugh);
+ RegisterSpellScript(spell_headless_horseman_head_reposition);
+ RegisterSpellScript(spell_headless_horseman_send_head);
+ RegisterSpellScript(spell_headless_horseman_head_periodic);
+ RegisterSpellScript(spell_headless_horseman_command_head_request_body);
+ RegisterSpellScript(spell_headless_horseman_return_head);
+ RegisterSpellScript(spell_summon_pumpkin_burst_delay);
+ RegisterSpellScript(spell_headless_horseman_head_is_dead);
+ RegisterSpellScript(spell_headless_horseman_summoning_rhyme_aura);
+ RegisterSpellScript(spell_headless_horseman_sprouting);
+ RegisterSpellScript(spell_headless_horseman_wisp_teleport);
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp
index 23377bf115d..9ddd6aff7a6 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp
@@ -17,24 +17,32 @@
#include "scarlet_monastery.h"
#include "Creature.h"
+#include "CreatureAI.h"
+#include "EventMap.h"
+#include "GameObject.h"
#include "InstanceScript.h"
#include "Map.h"
#include "ScriptMgr.h"
+#include "TemporarySummon.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
+ { NPC_HEADLESS_HORSEMAN_HEAD, DATA_HORSEMAN_HEAD },
+ { NPC_HEADLESS_HORSEMAN, DATA_HEADLESS_HORSEMAN },
+ { NPC_FLAME_BUNNY, DATA_FLAME_BUNNY },
+ { NPC_EARTH_BUNNY, DATA_EARTH_BUNNY },
+ { NPC_SIR_THOMAS, DATA_THOMAS },
+ { 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 },
+ { GO_LOOSELY_TURNED_SOIL, DATA_LOOSELY_TURNED_SOIL },
{ 0, 0 } // END
};
@@ -50,52 +58,92 @@ class instance_scarlet_monastery : public InstanceMapScript
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadObjectData(creatureData, gameObjectData);
+ _horsemanState = NOT_STARTED;
}
- void OnCreatureCreate(Creature* creature) override
+ void HandleStartEvent()
{
- switch (creature->GetEntry())
+ _horsemanState = IN_PROGRESS;
+ for (uint32 data : {DATA_PUMPKIN_SHRINE, DATA_LOOSELY_TURNED_SOIL})
+ if (GameObject* gob = GetGameObject(data))
+ gob->AddFlag(GO_FLAG_NOT_SELECTABLE);
+
+ instance->SummonCreature(NPC_HEADLESS_HORSEMAN_HEAD, HeadlessHorsemanHeadSpawnPosition);
+ instance->SummonCreature(NPC_FLAME_BUNNY, BunnySpawnPosition);
+ instance->SummonCreature(NPC_EARTH_BUNNY, EarthBunnySpawnPosition);
+ _events.ScheduleEvent(EVENT_ACTIVE_EARTH_EXPLOSION, 1s + 500ms);
+ _events.ScheduleEvent(EVENT_SPAWN_HEADLESS_HORSEMAN, 3s);
+ _events.ScheduleEvent(EVENT_DESPAWN_OBJECTS, 10s);
+ if (Creature* thomas = GetCreature(DATA_THOMAS))
+ thomas->DespawnOrUnsummon();
+ }
+
+ void SetData(uint32 type, uint32 data) override
+ {
+ switch (type)
{
- case NPC_PUMPKIN:
- HorsemanAdds.insert(creature->GetGUID());
+ case DATA_START_HORSEMAN_EVENT:
+ if (_horsemanState != IN_PROGRESS)
+ HandleStartEvent();
+ break;
+ case DATA_HORSEMAN_EVENT_STATE:
+ _horsemanState = data;
+ break;
+ case DATA_PREPARE_RESET:
+ _horsemanState = NOT_STARTED;
+ for (uint32 data : {DATA_FLAME_BUNNY, DATA_EARTH_BUNNY})
+ if (Creature* bunny = GetCreature(data))
+ bunny->DespawnOrUnsummon();
break;
default:
break;
}
-
- InstanceScript::OnCreatureCreate(creature);
}
- bool SetBossState(uint32 type, EncounterState state) override
+ uint32 GetData(uint32 type) const override
{
- if (!InstanceScript::SetBossState(type, state))
- return false;
-
switch (type)
{
- case DATA_HORSEMAN_EVENT:
- if (state == DONE || state == FAIL || state == NOT_STARTED)
- {
- for (ObjectGuid const& guid : HorsemanAdds)
- {
- Creature* add = instance->GetCreature(guid);
- if (add)
- add->DespawnOrUnsummon();
- }
- HorsemanAdds.clear();
-
- if (state == DONE)
- HandleGameObject(ObjectGuid::Empty, false, GetGameObject(DATA_PUMPKIN_SHRINE));
- }
- break;
+ case DATA_HORSEMAN_EVENT_STATE:
+ return _horsemanState;
default:
- break;
+ return 0;
+ }
+ }
+
+ void Update(uint32 diff) override
+ {
+ if (_events.Empty())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_ACTIVE_EARTH_EXPLOSION:
+ if (Creature* earthBunny = GetCreature(DATA_EARTH_BUNNY))
+ earthBunny->CastSpell(earthBunny, SPELL_EARTH_EXPLOSION);
+ break;
+ case EVENT_SPAWN_HEADLESS_HORSEMAN:
+ if (TempSummon* horseman = instance->SummonCreature(NPC_HEADLESS_HORSEMAN, HeadlessHorsemanSpawnPosition))
+ horseman->AI()->DoAction(ACTION_HORSEMAN_EVENT_START);
+ break;
+ case EVENT_DESPAWN_OBJECTS:
+ for (uint32 data : {DATA_PUMPKIN_SHRINE, DATA_LOOSELY_TURNED_SOIL})
+ if (GameObject* gob = GetGameObject(data))
+ gob->RemoveFromWorld();
+ break;
+ default:
+ break;
+ }
}
- return true;
}
- protected:
- GuidUnorderedSet HorsemanAdds;
+ private:
+ EventMap _events;
+ uint32 _horsemanState;
};
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 73ab507c359..76c6b1cbe79 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h
@@ -19,12 +19,18 @@
#define SCARLET_M_
#include "CreatureAIImpl.h"
+#include "Position.h"
#define SMScriptName "instance_scarlet_monastery"
#define DataHeader "SM"
uint32 const EncounterCount = 10;
+Position const BunnySpawnPosition = { 1776.27f, 1348.74f, 19.20f };
+Position const EarthBunnySpawnPosition = { 1765.28f, 1347.46f, 18.55f, 6.17f };
+Position const HeadlessHorsemanSpawnPosition = { 1765.00f, 1347.00f, 15.00f };
+Position const HeadlessHorsemanHeadSpawnPosition = { 1788.54f, 1348.05f, 18.88f }; // Guessed
+
enum SMDataTypes
{
DATA_INTERROGATOR_VISHAS = 0,
@@ -37,32 +43,52 @@ enum SMDataTypes
DATA_AZSHIR,
DATA_SCORN,
- DATA_HORSEMAN_EVENT, // Last defined encounter
-
- DATA_HEAD,
- DATA_HORSEMAN,
DATA_MOGRAINE,
DATA_VORREL,
DATA_WHITEMANE,
+ // Headless Horseman
+ DATA_HORSEMAN_HEAD,
+ DATA_HEADLESS_HORSEMAN,
DATA_PUMPKIN_SHRINE,
DATA_HIGH_INQUISITORS_DOOR,
+ DATA_LOOSELY_TURNED_SOIL,
+ DATA_START_HORSEMAN_EVENT,
+ DATA_FLAME_BUNNY,
+ DATA_EARTH_BUNNY,
+ DATA_HORSEMAN_EVENT_STATE,
+ DATA_PREPARE_RESET,
+ DATA_THOMAS
};
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_HEADLESS_HORSEMAN = 23682,
+ NPC_HEADLESS_HORSEMAN_HEAD = 23775,
+ NPC_PULSING_PUMPKIN = 23694,
+ NPC_PUMPKIN_FIEND = 23545,
+ NPC_FLAME_BUNNY = 23686,
+ NPC_EARTH_BUNNY = 23758,
+ NPC_SIR_THOMAS = 23904
+};
+
+enum SMCreatureMisc
+{
+ SPELL_EARTH_EXPLOSION = 42373,
+ EVENT_ACTIVE_EARTH_EXPLOSION = 1,
+ EVENT_SPAWN_HEADLESS_HORSEMAN = 2,
+ EVENT_DESPAWN_OBJECTS = 3,
+ ACTION_HORSEMAN_EVENT_START = 101
};
enum SMGameObjectIds
{
GO_HIGH_INQUISITORS_DOOR = 104600,
- GO_PUMPKIN_SHRINE = 186267
+ GO_PUMPKIN_SHRINE = 186267,
+ GO_LOOSELY_TURNED_SOIL = 186314
};
template <class AI, class T>