diff --git a/data/sql/updates/pending_db_world/add_areatrigger_nethekurse.sql b/data/sql/updates/pending_db_world/add_areatrigger_nethekurse.sql new file mode 100644 index 000000000..5a2cc0208 --- /dev/null +++ b/data/sql/updates/pending_db_world/add_areatrigger_nethekurse.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `areatrigger_scripts` WHERE `entry` = 4347 AND `ScriptName` = 'at_rp_nethekurse'; +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(4347, 'at_rp_nethekurse'); diff --git a/data/sql/updates/pending_db_world/gully_netherkurse_sql_lines.sql b/data/sql/updates/pending_db_world/gully_netherkurse_sql_lines.sql new file mode 100644 index 000000000..3e2638473 --- /dev/null +++ b/data/sql/updates/pending_db_world/gully_netherkurse_sql_lines.sql @@ -0,0 +1,26 @@ +DELETE FROM `creature_text` WHERE `CreatureID` = 16807; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(16807, 0, 0, 'You wish to fight us all at once? This should be amusing!\n', 14, 0, 100, 0, 0, 10262, 15594, 0, 'Netherkurse AGGRO_1'), +(16807, 1, 0, 'Thank you for saving me the trouble. Now it\'s my turn to have some fun!\n', 14, 0, 100, 0, 0, 10270, 15589, 0, 'Netherkurse AGGRO_2'), +(16807, 2, 0, 'You can have that one, I no longer need him!', 14, 0, 100, 0, 0, 10263, 15569, 0, 'Netherkurse PEON_ATTACK_1'), +(16807, 2, 1, 'Yes, beat him mercilessly! His skull is as thick as an ogre\'s!\n', 14, 0, 100, 0, 0, 10264, 15575, 0, 'Netherkurse PEON_ATTACK_2'), +(16807, 2, 2, 'Don\'t waste your time on that one, he\'s weak!', 14, 0, 100, 0, 0, 10265, 15573, 0, 'Netherkurse PEON_ATTACK_3'), +(16807, 2, 3, 'You want him? Very well, take him!', 14, 0, 100, 0, 0, 10266, 15572, 0, 'Netherkurse PEON_ATTACK_4'), +(16807, 3, 0, 'One pitiful wretch down. Go on, take another one! ', 14, 0, 100, 0, 0, 10267, 15579, 0, 'Netherkurse PEON_DIE_1'), +(16807, 3, 1, 'Ah, what a waste... next!', 14, 0, 100, 0, 0, 10268, 15584, 0, 'Netherkurse PEON_DIE_2'), +(16807, 3, 2, 'I was going to kill him anyway!', 14, 0, 100, 0, 0, 10269, 15582, 0, 'Netherkurse PEON_DIE_3'), +(16807, 4, 0, 'Beg for your pitiful life!', 14, 0, 100, 0, 0, 10259, 14130, 0, 'Netherkurse SAY_SHADOW_SEAR'), +(16807, 5, 0, 'Your pain amuses me!', 14, 0, 100, 0, 0, 10261, 14148, 0, 'Netherkurse SAY_SHADOW_FISSURE'), +(16807, 6, 0, 'Run, coward, run! ', 14, 0, 100, 0, 0, 10260, 14132, 0, 'Netherkurse SAY_DEATH_COIL'), +(16807, 7, 0, 'I\'m already bored!', 14, 0, 100, 0, 0, 10271, 16864, 0, 'Netherkurse SAY_SLAY_1'), +(16807, 7, 1, 'Come on, show me a real fight!', 14, 0, 100, 0, 0, 10272, 15595, 0, 'Netherkurse SAY_SLAY_2'), +(16807, 7, 2, 'I had more fun torturing the peons!', 14, 0, 100, 0, 0, 10273, 16863, 0, 'Netherkurse SAY_SLAY_3'), +(16807, 7, 3, 'You lose.', 14, 0, 100, 0, 0, 10274, 16865, 0, 'Netherkurse SAY_SLAY_4'), +(16807, 7, 4, 'Oh, just die!', 14, 0, 100, 0, 0, 10275, 16866, 0, 'Netherkurse SAY_SLAY_5'), +(16807, 8, 0, 'What... a shame.', 14, 0, 100, 0, 0, 10276, 16862, 0, 'Netherkurse SAY_DIE'); + +-- Auras for Shadow Fissures +DELETE FROM `creature_template_addon` WHERE `entry` IN (18370, 20598); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(18370, 0, 0, 0, 0, 0, 0, '32250'), +(20598, 0, 0, 0, 0, 0, 0, '32250'); -- Same aura in heroic diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp index f41f81900..f74bb1aa3 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp @@ -23,12 +23,14 @@ enum eGrandWarlockNethekurse { SAY_INTRO = 0, - SAY_PEON_ATTACKED = 1, - SAY_PEON_DIES = 2, - SAY_TAUNT = 3, - SAY_AGGRO = 4, - SAY_SLAY = 5, - SAY_DIE = 6, + SAY_INTRO_2 = 1, + SAY_PEON_ATTACKED = 2, + SAY_PEON_DIES = 3, + SAY_SHADOW_SEAR = 4, + SAY_SHADOW_FISSURE = 5, + SAY_DEATH_COIL = 6, + SAY_SLAY = 7, + SAY_DIE = 8, SPELL_DEATH_COIL_N = 30500, SPELL_DEATH_COIL_H = 35954, @@ -40,13 +42,10 @@ enum eGrandWarlockNethekurse // Spells used exclusively in RP SPELL_SHADOW_SEAR = 30735, SPELL_DEATH_COIL = 30741, + SPELL_SHADOW_FISSURE_RP = 30745, EVENT_INTRO = 1, - EVENT_SPELL_DEATH_COIL = 2, - EVENT_SPELL_SHADOW_FISSURE = 3, - EVENT_SPELL_CLEAVE = 4, - EVENT_CHECK_HEALTH = 5, - EVENT_START_ATTACK = 6, + EVENT_START_ATTACK = 2, EVENT_STAGE_NONE = 0, EVENT_STAGE_INTRO = 1, @@ -55,13 +54,38 @@ enum eGrandWarlockNethekurse SETDATA_DATA = 1, SETDATA_PEON_AGGRO = 1, - SETDATA_PEON_DEATH = 2 + SETDATA_PEON_DEATH = 2, +}; + +enum Creatures +{ + NPC_PEON = 17083 +}; + +enum Groups +{ + GROUP_RP = 0, +}; + +enum Actions +{ + ACTION_START_INTRO = 0, + ACTION_CANCEL_INTRO = 1, + ACTION_START_COMBAT = 2, }; // ######################################################## // Grand Warlock Nethekurse // ######################################################## +float NethekurseIntroPath[4][3] = +{ + {184.78966f, 290.3699f, -8.18139f}, + {178.51125f, 278.779022f, -8.183065f}, + {171.82281f, 289.97687f, -8.185595f}, + {178.51125f, 287.97794f, -8.183065f} +}; + class boss_grand_warlock_nethekurse : public CreatureScript { public: @@ -69,7 +93,13 @@ public: struct boss_grand_warlock_nethekurseAI : public BossAI { - boss_grand_warlock_nethekurseAI(Creature* creature) : BossAI(creature, DATA_NETHEKURSE) { } + boss_grand_warlock_nethekurseAI(Creature* creature) : BossAI(creature, DATA_NETHEKURSE) + { + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } EventMap events2; void Reset() override @@ -77,6 +107,10 @@ public: EventStage = EVENT_STAGE_NONE; _Reset(); events2.Reset(); + + ScheduleHealthCheckEvent(25, [&] { + DoCastSelf(SPELL_DARK_SPIN); + }); } void JustDied(Unit* /*killer*/) override @@ -97,21 +131,35 @@ public: return; if (EventStage < EVENT_STAGE_TAUNT) + { Talk(SAY_PEON_ATTACKED); + } break; case SETDATA_PEON_DEATH: if (PeonKilledCount >= 4) return; if (EventStage < EVENT_STAGE_TAUNT) - Talk(SAY_PEON_DIES); - + { + PeonDieRP(); + } if (++PeonKilledCount == 4) - events2.ScheduleEvent(EVENT_START_ATTACK, 5000); + DoAction(ACTION_CANCEL_INTRO); break; } } + void PeonDieRP() + { + me->GetMotionMaster()->Clear(); + me->SetFacingTo(4.572762489318847656f); + scheduler.Schedule(500ms, GROUP_RP, [this](TaskContext /*context*/) + { + me->HandleEmoteCommand(EMOTE_ONESHOT_APPLAUD); + Talk(SAY_PEON_DIES); + }); + } + void AttackStart(Unit* who) override { if (EventStage < EVENT_STAGE_MAIN) @@ -120,40 +168,87 @@ public: if (me->Attack(who, true)) { DoStartMovement(who); + CombatEventScheduler(); } } - void MoveInLineOfSight(Unit* who) override + void CombatEventScheduler() { - if (me->IsWithinDistInMap(who, 30.0f)) + scheduler.Schedule(12150ms, 19850ms, [this](TaskContext context) { - if (who->GetTypeId() != TYPEID_PLAYER) - return; - - if (EventStage == EVENT_STAGE_NONE && PeonKilledCount < 4) + if (me->HealthBelowPct(90)) { - events2.ScheduleEvent(EVENT_INTRO, 90000); - Talk(SAY_INTRO); - EventStage = EVENT_STAGE_INTRO; - instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS); - me->SetInCombatWithZone(); + DoCastRandomTarget(DUNGEON_MODE(SPELL_DEATH_COIL_N, SPELL_DEATH_COIL_H), 0, 30.0f, true); } - else if (PeonKilledCount >= 4) + context.Repeat(); + }).Schedule(8100ms, 17300ms, [this](TaskContext context) + { + DoCastRandomTarget(SPELL_SHADOW_FISSURE, 0, 60.0f, true); + context.Repeat(8450ms, 9450ms); + }).Schedule(10950ms, 21850ms, [this](TaskContext context) + { + DoCastVictim(DUNGEON_MODE(SPELL_SHADOW_CLEAVE_N, SPELL_SHADOW_SLAM_H)); + context.Repeat(1200ms, 23900ms); + }); + } + + void MoveInLineOfSight(Unit* /*who*/) override + { + if (EventStage == EVENT_STAGE_NONE) + { + if (me->SelectNearestPlayer(30.0f)) { - events2.ScheduleEvent(EVENT_START_ATTACK, 1000); - instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS); - me->SetInCombatWithZone(); + DoAction(ACTION_CANCEL_INTRO); } } + } - if (EventStage < EVENT_STAGE_MAIN) - return; - - ScriptedAI::MoveInLineOfSight(who); + void IntroRP() + { + scheduler.Schedule(500ms, GROUP_RP, [this](TaskContext context) + { + me->GetMotionMaster()->Clear(); + scheduler.Schedule(500ms, GROUP_RP, [this](TaskContext /*context*/) + { + uint32 choicelocation = urand(1, 3); + me->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->MovePoint(0, NethekurseIntroPath[choicelocation][0], NethekurseIntroPath[choicelocation][1], NethekurseIntroPath[choicelocation][2]); + scheduler.Schedule(2500ms, GROUP_RP, [this, choicelocation](TaskContext /*context*/) + { + CastRandomPeonSpell(choicelocation); + }); + }); + context.Repeat(16400ms, 28500ms); + }); } void JustEngagedWith(Unit* /*who*/) override { + _JustEngagedWith(); + if (EventStage == EVENT_STAGE_NONE) + { + DoAction(ACTION_CANCEL_INTRO); + CombatEventScheduler(); + } + } + + void CastRandomPeonSpell(uint32 choice) + { + if (choice == 1) + { + Talk(SAY_DEATH_COIL); + me->CastSpell(me, SPELL_DEATH_COIL, false); + } + else if (choice == 2) + { + Talk(SAY_SHADOW_FISSURE); + me->CastSpell(me, SPELL_SHADOW_FISSURE_RP, false); + } + else if (choice == 3) + { + Talk(SAY_SHADOW_SEAR); + me->CastSpell(me, SPELL_SHADOW_SEAR, false); + } } void KilledUnit(Unit* /*victim*/) override @@ -161,29 +256,59 @@ public: Talk(SAY_SLAY); } + void DoAction(int32 action) override + { + if (action == ACTION_CANCEL_INTRO) + { + introDone = true; + scheduler.CancelGroup(GROUP_RP); + events2.ScheduleEvent(EVENT_START_ATTACK, 1000); + instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS); + me->SetInCombatWithZone(); + Talk(SAY_INTRO_2); + me->SetHomePosition(NethekurseIntroPath[3][0], NethekurseIntroPath[3][1], NethekurseIntroPath[3][2], 4.572762489318847656f); + me->RemoveUnitFlag(UNIT_FLAG_NOT_ATTACKABLE_1); + return; + } + + if (action != ACTION_START_INTRO) + { + return; + } + + if (ATreached == true) + { + return; + } + + ATreached = true; + me->SetUnitFlag(UNIT_FLAG_NOT_ATTACKABLE_1); + events2.ScheduleEvent(EVENT_INTRO, 90000); + Talk(SAY_INTRO); + EventStage = EVENT_STAGE_INTRO; + instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS); + me->SetInCombatWithZone(); + IntroRP(); + } + void UpdateAI(uint32 diff) override { events2.Update(diff); + scheduler.Update(diff); uint32 eventId = events2.ExecuteEvent(); if (EventStage < EVENT_STAGE_MAIN && instance->GetBossState(DATA_NETHEKURSE) == IN_PROGRESS) { if (eventId == EVENT_INTRO) { - Talk(SAY_TAUNT); EventStage = EVENT_STAGE_TAUNT; - me->CastSpell(me, SPELL_SHADOW_SEAR, false); } else if (eventId == EVENT_START_ATTACK) { - Talk(SAY_AGGRO); EventStage = EVENT_STAGE_MAIN; if (Unit* target = me->SelectNearestPlayer(50.0f)) AttackStart(target); - - events.ScheduleEvent(EVENT_SPELL_DEATH_COIL, 20000); - events.ScheduleEvent(EVENT_SPELL_SHADOW_FISSURE, 8000); - events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000); + DoAction(ACTION_CANCEL_INTRO); return; } } @@ -191,40 +316,10 @@ public: if (!UpdateVictim()) return; - events.Update(diff); if (EventStage < EVENT_STAGE_MAIN || me->HasUnitState(UNIT_STATE_CASTING)) return; - switch (events.ExecuteEvent()) - { - case EVENT_SPELL_SHADOW_FISSURE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - me->CastSpell(target, SPELL_SHADOW_FISSURE, false); - events.RescheduleEvent(EVENT_SPELL_SHADOW_FISSURE, urand(7500, 10000)); - break; - case EVENT_SPELL_DEATH_COIL: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - me->CastSpell(target, DUNGEON_MODE(SPELL_DEATH_COIL_N, SPELL_DEATH_COIL_H), false); - events.RescheduleEvent(EVENT_SPELL_DEATH_COIL, urand(15000, 20000)); - break; - case EVENT_SPELL_CLEAVE: - me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_SHADOW_CLEAVE_N, SPELL_SHADOW_SLAM_H), false); - events.RescheduleEvent(EVENT_SPELL_CLEAVE, urand(6000, 8000)); - break; - case EVENT_CHECK_HEALTH: - if (me->HealthBelowPct(21)) - { - events.Reset(); - me->CastSpell(me, SPELL_DARK_SPIN, false); - } - else - { - events.RescheduleEvent(EVENT_CHECK_HEALTH, 1000); - } - break; - } - - if (!me->HealthBelowPct(21)) + if (!me->HealthBelowPct(25)) DoMeleeAttackIfReady(); } @@ -232,6 +327,8 @@ public: uint8 PeonEngagedCount = 0; uint8 PeonKilledCount = 0; uint8 EventStage; + bool introDone; + bool ATreached = false; }; CreatureAI* GetAI(Creature* creature) const override @@ -251,7 +348,7 @@ public: void CalculateDamageAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) { - amount = 1000; + amount = 0; } void Register() override @@ -302,10 +399,36 @@ public: return new spell_tsh_shadow_bolt_SpellScript(); } }; +class at_rp_nethekurse : public AreaTriggerScript +{ +public: + at_rp_nethekurse() : AreaTriggerScript + ("at_rp_nethekurse") { } + + bool OnTrigger(Player* player, AreaTrigger const* /*at*/) override + { + if (player->IsGameMaster()) + { + return false; + } + if (InstanceScript* instance = player->GetInstanceScript()) + { + if (instance->GetBossState(DATA_NETHEKURSE) != DONE && instance->GetBossState(DATA_NETHEKURSE) != IN_PROGRESS) + { + if (Creature* nethekurse = instance->GetCreature(DATA_NETHEKURSE)) + { + nethekurse->AI()->DoAction(ACTION_START_INTRO); + } + } + } + return false; + } +}; void AddSC_boss_grand_warlock_nethekurse() { new boss_grand_warlock_nethekurse(); new spell_tsh_shadow_sear(); new spell_tsh_shadow_bolt(); + new at_rp_nethekurse(); } diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp index 161393275..4860b4207 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp @@ -20,6 +20,13 @@ #include "ScriptMgr.h" #include "shattered_halls.h" +ObjectData const creatureData[] = +{ + { NPC_GRAND_WARLOCK_NETHEKURSE , DATA_NETHEKURSE }, + { NPC_WARCHIEF_KARGATH , DATA_KARGATH }, + { 0, 0 } +}; + class instance_shattered_halls : public InstanceMapScript { public: @@ -37,6 +44,7 @@ public: void Initialize() override { SetBossNumber(ENCOUNTER_COUNT); + LoadObjectData(creatureData, nullptr); TeamIdInInstance = TEAM_NEUTRAL; RescueTimer = 100 * MINUTE * IN_MILLISECONDS; @@ -101,6 +109,7 @@ public: prisonerGUID[2] = creature->GetGUID(); break; } + InstanceScript::OnCreatureCreate(creature); } bool SetBossState(uint32 type, EncounterState state) override @@ -198,6 +207,7 @@ public: protected: ObjectGuid warchiefKargathGUID; + ObjectGuid grandWarlockNethekurseGUID; ObjectGuid nethekurseDoor1GUID; ObjectGuid nethekurseDoor2GUID;