diff options
4 files changed, 476 insertions, 235 deletions
diff --git a/sql/updates/world/3.3.5/2025_12_20_01_world.sql b/sql/updates/world/3.3.5/2025_12_20_01_world.sql new file mode 100644 index 00000000000..5503c5f56d7 --- /dev/null +++ b/sql/updates/world/3.3.5/2025_12_20_01_world.sql @@ -0,0 +1,95 @@ +-- +UPDATE `creature_template` SET `ScriptName` = 'npc_wild_shadow_fissure' WHERE `entry` = 18370; +UPDATE `creature_template` SET `minlevel` = 70, `maxlevel` = 70, `unit_flags` = 0x02000000 WHERE `entry` = 18370; +UPDATE `creature_template` SET `minlevel` = 71, `maxlevel` = 71, `unit_flags` = 0x02000000 WHERE `entry` = 20598; + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` IN (30735,30741,32251,30745); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`ConditionStringValue1`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,30735,0,0,58,0,0,0,0,"FelOrcConvertNethekurseEvent",0,0,0,"","Group 0: Spell 'Shadow Sear' (Effect 0) targets creature 'Fel Orc Convert'"), +(13,3,30741,0,0,58,0,0,0,0,"FelOrcConvertNethekurseEvent",0,0,0,"","Group 0: Spell 'Death Coil' (Effect 0, 1) targets creature 'Fel Orc Convert'"), +(13,1,32251,0,0,58,0,0,0,0,"FelOrcConvertNethekurseEvent",0,0,0,"","Group 0: Spell 'Consumption' (Effect 0) targets creature 'Fel Orc Convert'"), +(13,1,30745,0,0,58,0,0,0,0,"FelOrcConvertNethekurseEvent",0,0,0,"","Group 0: Spell 'Target Fissures' (Effect 0) targets creature 'Fel Orc Convert'"); + +UPDATE `creature` SET `StringId` = 'FelOrcConvertNethekurseEvent' WHERE `guid` IN (59478,59479,59480,59481) AND `id` = 17083; + +UPDATE `creature` SET `position_x` = 164.71588134765625, `position_y` = 266.263031005859375, `position_z` = -13.1121978759765625, `orientation` = 1.047197580337524414, `VerifiedBuild` = 14007 WHERE `guid` = 59478 AND `id` = 17083; +UPDATE `creature` SET `position_x` = 174.04949951171875, `position_y` = 269.132476806640625, `position_z` = -13.0549201965332031, `orientation` = 1.448623299598693847, `VerifiedBuild` = 14007 WHERE `guid` = 59479 AND `id` = 17083; +UPDATE `creature` SET `position_x` = 184.4263458251953125, `position_y` = 269.293731689453125, `position_z` = -13.0555877685546875, `orientation` = 1.727875947952270507, `VerifiedBuild` = 14007 WHERE `guid` = 59480 AND `id` = 17083; +UPDATE `creature` SET `position_x` = 194.48394775390625, `position_y` = 267.07208251953125, `position_z` = -13.1039943695068359, `orientation` = 2.181661605834960937, `VerifiedBuild` = 14007 WHERE `guid` = 59481 AND `id` = 17083; + +DELETE FROM `creature_text` WHERE `CreatureID` = 17083; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(17083,0,0,"We are the true Horde!",12,0,100,0,0,0,16697,0,"Fel Orc Convert"), +(17083,0,1,"For Kargath! For Victory!",12,0,100,0,0,0,16698,0,"Fel Orc Convert"), +(17083,0,2,"Gakarah ma!",12,0,100,0,0,0,16699,0,"Fel Orc Convert"), +(17083,0,3,"The blood is our power!",12,0,100,0,0,0,16700,0,"Fel Orc Convert"), +(17083,0,4,"Lok'tar Illadari! +",12,0,100,0,0,0,16701,0,"Fel Orc Convert"), +(17083,0,5,"This world is OURS!",12,0,100,0,0,0,16702,0,"Fel Orc Convert"), +(17083,0,6,"Lok narash!",12,0,100,0,0,0,16703,0,"Fel Orc Convert"), +-- Not sure if these are grouped correctly, based on few sniffs +-- By the way, language is correct, verified, not a typo +-- Text 14143 seems to be unused, not added here +(17083,1,0,"It hurt!",14,0,100,0,0,0,14136,0,"Fel Orc Convert - Shadow Sear"), +(17083,1,1,"Augh! No more hurt!",14,0,100,0,0,0,14137,0,"Fel Orc Convert - Shadow Sear"), +(17083,1,2,"This not good tickle!",14,0,100,0,0,0,14138,0,"Fel Orc Convert - Shadow Sear"), +(17083,1,3,"Skin on fire!",14,0,100,0,0,0,14139,0,"Fel Orc Convert - Shadow Sear"), + +(17083,2,0,"It hurt!",14,1,100,0,0,0,14140,0,"Fel Orc Convert - Death Coil"), +(17083,2,1,"Aahhh!",14,1,100,0,0,0,14141,0,"Fel Orc Convert - Death Coil"), +(17083,2,2,"No more scary!",14,1,100,0,0,0,14142,0,"Fel Orc Convert - Death Coil"), +(17083,2,3,"No more!",14,1,100,0,0,0,14144,0,"Fel Orc Convert - Death Coil"), + +(17083,3,0,"Pain!",14,1,100,0,0,0,14149,0,"Fel Orc Convert - Target Fissures"), +(17083,3,1,"It hurts!",14,1,100,0,0,0,14150,0,"Fel Orc Convert - Target Fissures"), +(17083,3,2,"Graaagggh!!",14,1,100,0,0,0,14151,0,"Fel Orc Convert - Target Fissures"), +(17083,3,3,"No more!!",14,1,100,0,0,0,14152,0,"Fel Orc Convert - Target Fissures"); + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_grand_warlock_nethekurse_target_fissures'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(30745, 'spell_grand_warlock_nethekurse_target_fissures'); + +DELETE FROM `areatrigger_scripts` WHERE `entry` = 4347; +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(4347, 'at_shattered_halls_the_sewer'); + +UPDATE `gameobject_template` SET `ScriptName` = 'go_grand_warlock_chamber_door' WHERE `entry` = 182539; + +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,"Beg for your pitiful life!",14,0,100,11,0,10259,14130,0,"nethekurse SAY_TAUNT_SHADOW_SEAR"), +(16807,1,0,"Run, coward, run! ",14,0,100,11,0,10260,14132,0,"nethekurse SAY_TAUNT_DEATH_COIL"), +(16807,2,0,"Your pain amuses me!",14,0,100,11,0,10261,14148,0,"nethekurse SAY_TAUNT_SHADOW_FISSURE"), + +(16807,3,0,"You wish to fight us all at once? This should be amusing! +",14,0,100,0,0,10262,15594,0,"nethekurse SAY_AGGRO_ALL_ALIVE"), +(16807,4,0,"Come on, show me a real fight!",14,0,100,0,0,10272,15595,0,"nethekurse SAY_AGGRO_SOME_ALIVE"), +(16807,5,0,"Thank you for saving me the trouble. Now it's my turn to have some fun! +",14,0,100,0,0,10270,15589,0,"nethekurse SAY_AGGRO_ALL_DEAD"), + +(16807,6,0,"You can have that one, I no longer need him!",14,0,100,11,0,10263,15569,0,"nethekurse SAY_PEON_ENGAGED_1"), +(16807,6,1,"Yes, beat him mercilessly! His skull is as thick as an ogre's! +",14,0,100,11,0,10264,15575,0,"nethekurse SAY_PEON_ENGAGED_2"), +(16807,6,2,"Don't waste your time on that one, he's weak!",14,0,100,11,0,10265,15573,0,"nethekurse SAY_PEON_ENGAGED_3"), +(16807,6,3,"You want him? Very well, take him!",14,0,100,11,0,10266,15572,0,"nethekurse SAY_PEON_ENGAGED_4"), + +(16807,7,0,"One pitiful wretch down. Go on, take another one! ",14,0,100,0,0,10267,15579,0,"nethekurse SAY_PEON_DEFEATED_1"), +(16807,7,1,"Ah, what a waste... next!",14,0,100,0,0,10268,15584,0,"nethekurse SAY_PEON_DEFEATED_2"), +(16807,7,2,"I was going to kill him anyway!",14,0,100,0,0,10269,15582,0,"nethekurse SAY_PEON_DEFEATED_3"), + +(16807,8,0,"I'm already bored!",14,0,100,0,0,10271,16864,0,"nethekurse SAY_SLAY_1"), +(16807,8,1,"I had more fun torturing the peons!",14,0,100,0,0,10273,16863,0,"nethekurse SAY_SLAY_2"), +(16807,8,2,"You lose.",14,0,100,0,0,10274,16865,0,"nethekurse SAY_SLAY_3"), +(16807,8,3,"Oh, just die!",14,0,100,0,0,10275,16866,0,"nethekurse SAY_SLAY_4"), + +(16807,9,0,"What... a shame.",14,0,100,0,0,10276,16862,0,"nethekurse SAY_DEATH"); + +UPDATE `creature` SET `MovementType` = 2 WHERE `id` = 16807; +UPDATE `creature_template_addon` SET `path_id` = 578530, `SheathState` = 1 WHERE `entry` IN (16807,20568); + +DELETE FROM `waypoint_data` WHERE `id` = 578530; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `delay`, `action`, `orientation`, `move_type`, `action_chance`) VALUES +(578530,1,178.51125,287.97794,-8.183065,0,0,NULL,0,100), +(578530,2,171.82281,289.97687,-8.185595,0,0,NULL,0,100), +(578530,3,178.51125,287.97794,-8.183065,0,0,NULL,0,100), +(578530,4,184.78966,290.36990,-8.181390,0,0,NULL,0,100); diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp index c470c6a867e..f4816f8af10 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp @@ -15,349 +15,495 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Grand_Warlock_Nethekurse -SD%Complete: 75 -SDComment: encounter not fully completed. missing part where boss kill minions. -SDCategory: Hellfire Citadel, Shattered Halls -EndScriptData */ - -/* ContentData -boss_grand_warlock_nethekurse -npc_fel_orc_convert -npc_lesser_shadow_fissure -EndContentData */ +/* + * Combat timers requires to be revisited + * Clearly seen one peon died from Shadow Fissure, doesn't work because health regenerates, + it should regenerate in case of other spells, no sniffs with Wild Shadow Fissure + * Dark Spin gets interrupted after first tick + * Death Coil is a buff instead of a debuff + */ #include "ScriptMgr.h" +#include "Containers.h" +#include "GameObject.h" +#include "GameObjectAI.h" #include "InstanceScript.h" -#include "ObjectAccessor.h" +#include "Player.h" #include "ScriptedCreature.h" #include "shattered_halls.h" +#include "SpellInfo.h" +#include "SpellScript.h" -enum Says +enum NethekurseTexts { - SAY_INTRO = 0, - SAY_PEON_ATTACKED = 1, - SAY_PEON_DIES = 2, - SAY_TAUNT = 3, - SAY_AGGRO = 4, - SAY_SLAY = 5, - SAY_DIE = 6 + SAY_TAUNT_SHADOW_SEAR = 0, + SAY_TAUNT_DEATH_COIL = 1, + SAY_TAUNT_SHADOW_FISSURE = 2, + + SAY_AGGRO_ALL_ALIVE = 3, + SAY_AGGRO_SOME_ALIVE = 4, + SAY_AGGRO_ALL_DEAD = 5, + + SAY_PEON_ENGAGED = 6, + SAY_PEON_DEFEATED = 7, + + SAY_SLAY = 8, + SAY_DEATH = 9, + + // Fel Orc Convert + SAY_PEON_AGGRO = 0, + SAY_PEON_SHADOW_SEAR = 1, + SAY_PEON_DEATH_COIL = 2, + SAY_PEON_SHADOW_FISSURE = 3 }; -enum Spells +enum NethekurseSpells { - SPELL_DEATH_COIL = 30500, // 30741 heroic - SPELL_DARK_SPIN = 30502, // core bug spell attack caster :D - SPELL_SHADOW_FISSURE = 30496, // Summon the ShadowFissure NPC + // Combat + SPELL_SHADOW_FISSURE = 30496, + SPELL_DEATH_COIL = 30500, SPELL_SHADOW_CLEAVE = 30495, - H_SPELL_SHADOW_SLAM = 35953, + SPELL_SHADOW_SLAM = 35953, + SPELL_DARK_SPIN = 30502, + + // Intro + SPELL_PEON_SHADOW_SEAR = 30735, + SPELL_PEON_DEATH_COIL = 30741, + SPELL_PEON_TARGET_FISSURES = 30745, + + // Fel Orc Convert SPELL_HEMORRHAGE = 30478, + + // Lesser Shadow Fissure SPELL_CONSUMPTION = 30497, - SPELL_TEMPORARY_VISUAL = 39312, // this is wrong, a temporary solution. spell consumption already has the purple visual, but doesn't display as it should + SPELL_CONSUMPTION_H = 35952, - SPELL_SHADOW_SEAR = 30735 // cast on entry 17083 which then makes sound 1343 - // 30948 cast on self by 17687 + // Wild Shadow Fissure + SPELL_CONSUMPTION_WILD = 32250, + + // Scripts + SPELL_SHADOW_FISSURE_2 = 30744 }; -enum SetData +enum NethekurseEvents { - SETDATA_DATA = 1, - SETDATA_PEON_AGGRO = 1, - SETDATA_PEON_DEATH = 2 + EVENT_TAUNT_PEONS = 1, + EVENT_INTERNAL_COOLDOWN, + EVENT_CLEAR_EMOTE_STATE, + + EVENT_SHADOW_FISSURE, + EVENT_DEATH_COIL, + EVENT_SHADOW_CLEAVE, + EVENT_SHADOW_SLAM, + EVENT_DARK_SPIN }; -enum Events +enum NethekurseActions { - // Fel Orc Convert - EVENT_HEMORRHAGE = 1 + ACTION_START_INTRO = 0, + ACTION_PEON_ENGAGED = 1, + ACTION_PEON_KILLED = 2 }; -// ######################################################## -// Grand Warlock Nethekurse -// ######################################################## +enum NethekurseMisc +{ + SOUND_ID_ROAR = 9110, + MAX_KILLED_PEONS = 4 +}; +// 16807 - Grand Warlock Nethekurse struct boss_grand_warlock_nethekurse : public BossAI { - boss_grand_warlock_nethekurse(Creature* creature) : BossAI(creature, DATA_NETHEKURSE) - { - Initialize(); - } - - void Initialize() - { - IsIntroEvent = false; - IntroOnce = false; - IsMainEvent = false; - //HasTaunted = false; - SpinOnce = false; - Phase = false; - - PeonEngagedCount = 0; - PeonKilledCount = 0; - - IntroEvent_Timer = 90000; // how long before getting bored and kills his minions? - DeathCoil_Timer = 20000; - ShadowFissure_Timer = 8000; - Cleave_Timer = 5000; - } + boss_grand_warlock_nethekurse(Creature* creature) : BossAI(creature, DATA_NETHEKURSE), + _peonKilledCount(0), _isEventOnCooldown(false), _isDarkSpinStarted(false) { } void Reset() override { _Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - Initialize(); + _isEventOnCooldown = false; + _isDarkSpinStarted = false; } - void JustDied(Unit* /*killer*/) override + void JustEngagedWith(Unit* who) override { - _JustDied(); - Talk(SAY_DIE); + BossAI::JustEngagedWith(who); + + events.Reset(); + + events.ScheduleEvent(EVENT_SHADOW_FISSURE, 8s); + events.ScheduleEvent(EVENT_DEATH_COIL, 10s, 15s); + + if (!IsHeroic()) + events.ScheduleEvent(EVENT_SHADOW_CLEAVE, 15s, 20s); + else + events.ScheduleEvent(EVENT_SHADOW_SLAM, 15s, 20s); + + switch (_peonKilledCount) + { + case 0: + Talk(SAY_AGGRO_ALL_ALIVE); + break; + case 1: + case 2: + case 3: + Talk(SAY_AGGRO_SOME_ALIVE); + break; + case 4: + Talk(SAY_AGGRO_ALL_DEAD); + break; + default: + break; + } } - void SetData(uint32 data, uint32 value) override + void DoAction(int32 action) override { - if (data != SETDATA_DATA) + if (!me->IsAlive()) return; - switch (value) + switch (action) { - case SETDATA_PEON_AGGRO: - if (PeonEngagedCount >= 4) - return; + case ACTION_START_INTRO: + if (!me->IsInCombat() && _peonKilledCount != MAX_KILLED_PEONS) + events.ScheduleEvent(EVENT_TAUNT_PEONS, 0s); + break; + case ACTION_PEON_ENGAGED: + if (!me->IsInCombat() && !_isEventOnCooldown) + { + Talk(SAY_PEON_ENGAGED); + me->PauseMovement(5000); + me->SetFacingTo(4.537856101989746093f); + _isEventOnCooldown = true; - Talk(SAY_PEON_ATTACKED); - ++PeonEngagedCount; + events.ScheduleEvent(EVENT_INTERNAL_COOLDOWN, 5s); + } break; - case SETDATA_PEON_DEATH: - if (PeonKilledCount >= 4) - return; + case ACTION_PEON_KILLED: + { + ++_peonKilledCount; - Talk(SAY_PEON_DIES); - ++PeonKilledCount; + if (!me->IsInCombat() && _peonKilledCount == MAX_KILLED_PEONS) + DoZoneInCombat(); - if (PeonKilledCount == 4) + if (!me->IsInCombat() && !_isEventOnCooldown && _peonKilledCount != MAX_KILLED_PEONS) { - IsIntroEvent = false; - IsMainEvent = true; - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + Talk(SAY_PEON_DEFEATED); + me->PauseMovement(5000); + me->SetFacingTo(4.572762489318847656f); + me->SetEmoteState(EMOTE_STATE_APPLAUD); + _isEventOnCooldown = true; + + events.ScheduleEvent(EVENT_INTERNAL_COOLDOWN, 5s); + events.ScheduleEvent(EVENT_CLEAR_EMOTE_STATE, 5s); } break; + } default: break; } } - void DoTauntPeons() + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override { - Talk(SAY_TAUNT); - - /// @todo kill the peons first - IsIntroEvent = false; - PeonEngagedCount = 4; - PeonKilledCount = 4; - IsMainEvent = true; - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + if (!_isDarkSpinStarted && me->HealthBelowPctDamaged(25, damage)) + { + _isDarkSpinStarted = true; + events.ScheduleEvent(EVENT_DARK_SPIN, 0s); + } } - void AttackStart(Unit* who) override + void KilledUnit(Unit* /*victim*/) override { - if (IsIntroEvent || !IsMainEvent) - return; + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } + + void UpdateOutOfCombatEvents(uint32 diff) + { + events.Update(diff); - if (me->Attack(who, true)) + while (uint32 eventId = events.ExecuteEvent()) { - if (Phase) - DoStartNoMovement(who); - else - DoStartMovement(who); + switch (eventId) + { + // ... intro event starts every time player enters area trigger + case EVENT_TAUNT_PEONS: + { + switch (urand(0, 2)) + { + case 0: + // This one targets two (?) peons + Talk(SAY_TAUNT_SHADOW_SEAR); + DoCastSelf(SPELL_PEON_SHADOW_SEAR, { SPELLVALUE_MAX_TARGETS, 2 }); + break; + case 1: + // This one targets all four peons + Talk(SAY_TAUNT_DEATH_COIL); + DoCastSelf(SPELL_PEON_DEATH_COIL); + break; + case 2: + // This one targets one peon + Talk(SAY_TAUNT_SHADOW_FISSURE); + DoCastSelf(SPELL_PEON_TARGET_FISSURES); + break; + default: + break; + } + + // ... cancel copies of event every time it is scheduled, then schedule new to repeat it, only one should be active + events.CancelEvent(EVENT_TAUNT_PEONS); + if (_peonKilledCount != MAX_KILLED_PEONS) + events.ScheduleEvent(EVENT_TAUNT_PEONS, 25s, 35s); + break; + } + case EVENT_INTERNAL_COOLDOWN: + _isEventOnCooldown = false; + break; + case EVENT_CLEAR_EMOTE_STATE: + me->SetEmoteState(EMOTE_ONESHOT_NONE); + break; + default: + break; + } } } - void MoveInLineOfSight(Unit* who) override + void UpdateAI(uint32 diff) override { - if (!IntroOnce && me->IsWithinDistInMap(who, 30.0f)) + if (!UpdateVictim()) { - if (who->GetTypeId() != TYPEID_PLAYER) - return; + UpdateOutOfCombatEvents(diff); + return; + } + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - Talk(SAY_INTRO); - IntroOnce = true; - IsIntroEvent = true; + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHADOW_FISSURE: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true)) + DoCast(target, SPELL_SHADOW_FISSURE); + events.Repeat(8s); + break; + case EVENT_DEATH_COIL: + if (Unit* target = SelectTarget(SelectTargetMethod::Random)) + DoCast(target, SPELL_DEATH_COIL); + events.Repeat(10s, 20s); + break; + case EVENT_SHADOW_CLEAVE: + DoCastVictim(SPELL_SHADOW_CLEAVE); + events.Repeat(15s, 20s); + break; + case EVENT_SHADOW_SLAM: + DoCastVictim(SPELL_SHADOW_SLAM); + events.Repeat(15s, 20s); + break; + case EVENT_DARK_SPIN: + DoCastSelf(SPELL_DARK_SPIN); + break; + default: + break; + } - instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - if (IsIntroEvent || !IsMainEvent) - return; + DoMeleeAttackIfReady(); + } + +private: + uint32 _peonKilledCount; + bool _isEventOnCooldown; + bool _isDarkSpinStarted; +}; + +// 17083 - Fel Orc Convert +struct npc_fel_orc_convert : public ScriptedAI +{ + npc_fel_orc_convert(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - ScriptedAI::MoveInLineOfSight(who); + void Reset() override + { + _scheduler.CancelAll(); } void JustEngagedWith(Unit* /*who*/) override { - Talk(SAY_AGGRO); + if (roll_chance_i(15)) + Talk(SAY_PEON_AGGRO); + + _scheduler.Schedule(5s, 10s, [this](TaskContext task) + { + DoCastVictim(SPELL_HEMORRHAGE); + task.Repeat(5s, 10s); + }); + + if (!me->HasStringId("FelOrcConvertNethekurseEvent")) + return; + + if (Creature* nethekurse = _instance->GetCreature(DATA_NETHEKURSE)) + nethekurse->AI()->DoAction(ACTION_PEON_ENGAGED); } - void JustSummoned(Creature* summoned) override + void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override { - //triggered spell of consumption does not properly show it's SpellVisual, wrong spellid? - summoned->CastSpell(summoned, SPELL_TEMPORARY_VISUAL, true); - summoned->CastSpell(summoned, SPELL_CONSUMPTION, CastSpellExtraArgs().SetOriginalCaster(me->GetGUID())); + // Not sure if these are grouped correctly, based on few sniffs + // By the way, language is correct, verified, not a typo + // Text 14143 seems to be unused, not added here + switch (spellInfo->Id) + { + case SPELL_PEON_SHADOW_SEAR: + if (roll_chance_i(30)) + Talk(SAY_PEON_SHADOW_SEAR); + break; + case SPELL_PEON_DEATH_COIL: + if (roll_chance_i(30)) + { + Talk(SAY_PEON_DEATH_COIL); + DoPlaySoundToSet(me, SOUND_ID_ROAR); + } + break; + case SPELL_PEON_TARGET_FISSURES: + if (roll_chance_i(30)) + Talk(SAY_PEON_SHADOW_FISSURE); + break; + default: + break; + } } - void KilledUnit(Unit* /*victim*/) override + void JustDied(Unit* /*killer*/) override { - Talk(SAY_SLAY); + if (!me->HasStringId("FelOrcConvertNethekurseEvent")) + return; + + if (Creature* nethekurse = _instance->GetCreature(DATA_NETHEKURSE)) + nethekurse->AI()->DoAction(ACTION_PEON_KILLED); } void UpdateAI(uint32 diff) override { - if (IsIntroEvent) - { - if (instance->GetBossState(DATA_NETHEKURSE) == IN_PROGRESS) - { - if (IntroEvent_Timer <= diff) - DoTauntPeons(); - else - IntroEvent_Timer -= diff; - } - } - if (!UpdateVictim()) return; - if (!IsMainEvent) - return; - - if (Phase) - { - if (!SpinOnce) - { - DoCastVictim(SPELL_DARK_SPIN); - SpinOnce = true; - } - - if (Cleave_Timer <= diff) - { - DoCastVictim(SPELL_SHADOW_CLEAVE); - Cleave_Timer = 6000 + rand32() % 2500; - } - else - Cleave_Timer -= diff; - } - else + _scheduler.Update(diff, [this] { - if (ShadowFissure_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - DoCast(target, SPELL_SHADOW_FISSURE); - ShadowFissure_Timer = urand(7500, 15000); - } - else - ShadowFissure_Timer -= diff; + DoMeleeAttackIfReady(); + }); + } - if (DeathCoil_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - DoCast(target, SPELL_DEATH_COIL); - DeathCoil_Timer = urand(15000, 20000); - } - else - DeathCoil_Timer -= diff; +private: + InstanceScript* _instance; + TaskScheduler _scheduler; +}; - if (!HealthAbovePct(20)) - Phase = true; +// 17471 - Lesser Shadow Fissure +struct npc_lesser_shadow_fissure : public ScriptedAI +{ + npc_lesser_shadow_fissure(Creature* creature) : ScriptedAI(creature) { } - DoMeleeAttackIfReady(); - } + void InitializeAI() override + { + me->SetReactState(REACT_PASSIVE); } - private: - uint32 PeonEngagedCount; - uint32 PeonKilledCount; - uint32 IntroEvent_Timer; - uint32 DeathCoil_Timer; - uint32 ShadowFissure_Timer; - uint32 Cleave_Timer; - bool IntroOnce; - bool IsIntroEvent; - bool IsMainEvent; - bool SpinOnce; - //bool HasTaunted; - bool Phase; + void JustAppeared() override + { + DoCastSelf(IsHeroic() ? SPELL_CONSUMPTION_H : SPELL_CONSUMPTION); + } }; -// ######################################################## -// Fel Orc Convert -// ######################################################## - -struct npc_fel_orc_convert : public ScriptedAI +// 18370 - Wild Shadow Fissure +struct npc_wild_shadow_fissure : public ScriptedAI { - npc_fel_orc_convert(Creature* creature) : ScriptedAI(creature) + npc_wild_shadow_fissure(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override { - instance = creature->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); } - void Reset() override + void JustAppeared() override { - me->SetNoCallAssistance(true); //we don't want any assistance (WE R HEROZ!) + DoCastSelf(SPELL_CONSUMPTION_WILD); } +}; - void MoveInLineOfSight(Unit* /*who*/) override { } +// 182539 - Grand Warlock Chamber Door +struct go_grand_warlock_chamber_door : public GameObjectAI +{ + go_grand_warlock_chamber_door(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()) { } - void JustEngagedWith(Unit* /*who*/) override + bool OnGossipHello(Player* /*player*/) override { - events.ScheduleEvent(EVENT_HEMORRHAGE, 3s); + if (Creature* nethekurse = _instance->GetCreature(DATA_NETHEKURSE)) + nethekurse->AI()->DoAction(ACTION_START_INTRO); - if (Creature* Kurse = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GRAND_WARLOCK_NETHEKURSE))) - if (me->IsWithinDist(Kurse, 45.0f)) - Kurse->AI()->SetData(SETDATA_DATA, SETDATA_PEON_AGGRO); + return true; } - void JustDied(Unit* /*killer*/) override - { - if (instance->GetBossState(DATA_NETHEKURSE) != IN_PROGRESS) - return; +private: + InstanceScript* _instance; +}; + +// 30745 - Target Fissures +class spell_grand_warlock_nethekurse_target_fissures : public SpellScript +{ + PrepareSpellScript(spell_grand_warlock_nethekurse_target_fissures); - if (Creature* Kurse = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GRAND_WARLOCK_NETHEKURSE))) - Kurse->AI()->SetData(SETDATA_DATA, SETDATA_PEON_DEATH); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SHADOW_FISSURE_2 }); } - void UpdateAI(uint32 diff) override + void FilterTargets(std::list<WorldObject*>& targets) { - if (!UpdateVictim()) + if (targets.empty()) return; - events.Update(diff); - - if (events.ExecuteEvent() == EVENT_HEMORRHAGE) - { - DoCastVictim(SPELL_HEMORRHAGE); - events.ScheduleEvent(EVENT_HEMORRHAGE, 15s); - } + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } - DoMeleeAttackIfReady(); + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_SHADOW_FISSURE_2); } - private: - InstanceScript* instance; - EventMap events; + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_grand_warlock_nethekurse_target_fissures::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_grand_warlock_nethekurse_target_fissures::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -// ######################################################## -// Lesser Shadow Fissure -// ######################################################## - -struct npc_lesser_shadow_fissure : public ScriptedAI +// 4347 +class at_shattered_halls_the_sewer : public AreaTriggerScript { - npc_lesser_shadow_fissure(Creature* creature) : ScriptedAI(creature) { } +public: + at_shattered_halls_the_sewer() : AreaTriggerScript("at_shattered_halls_the_sewer") { } - void Reset() override { } - void MoveInLineOfSight(Unit* /*who*/) override { } - void AttackStart(Unit* /*who*/) override { } - void JustEngagedWith(Unit* /*who*/) override { } + bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + { + if (InstanceScript* instance = player->GetInstanceScript()) + if (Creature* nethekurse = instance->GetCreature(DATA_NETHEKURSE)) + nethekurse->AI()->DoAction(ACTION_START_INTRO); + + return true; + } }; void AddSC_boss_grand_warlock_nethekurse() @@ -365,4 +511,8 @@ void AddSC_boss_grand_warlock_nethekurse() RegisterShatteredHallsCreatureAI(boss_grand_warlock_nethekurse); RegisterShatteredHallsCreatureAI(npc_fel_orc_convert); RegisterShatteredHallsCreatureAI(npc_lesser_shadow_fissure); + RegisterShatteredHallsCreatureAI(npc_wild_shadow_fissure); + RegisterShatteredHallsGameObjectAI(go_grand_warlock_chamber_door); + RegisterSpellScript(spell_grand_warlock_nethekurse_target_fissures); + new at_shattered_halls_the_sewer(); } 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 13c990eeb34..e04b341b03c 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp @@ -42,9 +42,10 @@ DoorData const doorData[] = ObjectData const creatureData[] = { - { NPC_LEFT_HEAD, DATA_LEFT_HEAD }, - { NPC_RIGHT_HEAD, DATA_RIGHT_HEAD }, - { 0, 0 } // END + { NPC_GRAND_WARLOCK_NETHEKURSE, DATA_NETHEKURSE }, + { NPC_LEFT_HEAD, DATA_LEFT_HEAD }, + { NPC_RIGHT_HEAD, DATA_RIGHT_HEAD }, + { 0, 0 } // END }; class instance_shattered_halls : public InstanceMapScript @@ -115,9 +116,6 @@ class instance_shattered_halls : public InstanceMapScript switch (creature->GetEntry()) { - case NPC_GRAND_WARLOCK_NETHEKURSE: - nethekurseGUID = creature->GetGUID(); - break; case NPC_KARGATH_BLADEFIST: kargathGUID = creature->GetGUID(); break; @@ -181,8 +179,6 @@ class instance_shattered_halls : public InstanceMapScript { switch (data) { - case NPC_GRAND_WARLOCK_NETHEKURSE: - return nethekurseGUID; case NPC_KARGATH_BLADEFIST: return kargathGUID; case NPC_SHATTERED_EXECUTIONER: @@ -285,7 +281,6 @@ class instance_shattered_halls : public InstanceMapScript } private: - ObjectGuid nethekurseGUID; ObjectGuid kargathGUID; ObjectGuid executionerGUID; ObjectGuid victimsGUID[3]; diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h index 02044e4ca1d..52c69b4d60e 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h @@ -128,5 +128,6 @@ inline AI* GetShatteredHallsAI(T* obj) } #define RegisterShatteredHallsCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetShatteredHallsAI) +#define RegisterShatteredHallsGameObjectAI(ai_name) RegisterGameObjectAIWithFactory(ai_name, GetShatteredHallsAI) #endif |
