diff options
3 files changed, 781 insertions, 563 deletions
diff --git a/sql/updates/world/3.3.5/2025_11_03_00_world.sql b/sql/updates/world/3.3.5/2025_11_03_00_world.sql new file mode 100644 index 00000000000..222910a9cb4 --- /dev/null +++ b/sql/updates/world/3.3.5/2025_11_03_00_world.sql @@ -0,0 +1,46 @@ +-- +DELETE FROM `areatrigger_scripts` WHERE `entry` = 4937; +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(4937, 'at_witchs_sanctum'); + +DELETE FROM `creature_text` WHERE `CreatureID` IN (25165,25166); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(25165,0,0,"Misery...confusion...mistrust. These are the hallmarks.",12,0,100,0,0,0,25512,0,"Sacrolash SAY_INTRO_SAC"), +(25165,1,0,"Shadow to the aid of fire!",14,0,100,0,0,12485,24423,0,"Sacrolash - SAY_SHADOW_NOVA"), +(25165,2,0,"Shadows engulf.",14,0,100,0,0,12486,25519,0,"Sacrolash - SAY_SLAY_SAC_1"), +(25165,2,1,"Ee-nok Kryul!",14,0,100,0,0,12487,25520,0,"Sacrolash - SAY_SLAY_SAC_2"), +(25165,3,0,"Alythess! Your fire burns within me!",14,0,100,0,0,12488,24452,0,"Sacrolash - SAY_ALYTHESS_DEAD"), +(25165,4,0,"Time is a luxury you no longer possess!",14,0,100,0,0,0,25521,0,"Sacrolash - SAY_BERSERK_SAC"), +(25165,5,0,"I... fade.",12,0,100,0,0,0,25522,0,"Sacrolash - SAY_DEATH_SAC"), +(25165,6,0,"Sacrolash directs Shadow Nova at $n.",41,0,100,0,0,0,24424,0,"Sacrolash - EMOTE_SHADOW_NOVA_SAC"), +(25165,7,0,"Alythess directs Conflagration at $n.",41,0,100,0,0,0,24426,0,"Sacrolash - EMOTE_CONFLAGRATION_SAC"), + +(25166,0,0,"Depravity...hatred...chaos. These are the pillars.",12,0,100,0,0,0,25511,0,"Alythess - SAY_INTRO_ALY"), +(25166,1,0,"Fire to the aid of shadow!",14,0,100,0,0,12489,25513,0,"Alythess - SAY_CONFLAGRATION"), +(25166,2,0,"Fires consume.",14,0,100,0,0,12490,25514,0,"Alythess - SAY_SLAY_ALY_1"), +(25166,2,1,"Edir harach!",14,0,100,0,0,12491,25515,0,"Alythess - SAY_SLAY_ALY_2"), +(25166,3,0,"Sacrolash!",14,0,100,0,0,12492,25516,0,"Alythess - SAY_SACROLASH_DEAD"), +(25166,4,0,"Your luck has run its course!",14,0,100,0,0,12493,25517,0,"Alythess - SAY_BERSERK_ALY"), +(25166,5,0,"Der'ek... manul.",12,0,100,0,0,12494,25518,0,"Alythess - SAY_DEATH_ALY"), +(25166,6,0,"Alythess directs Conflagration at $n.",41,0,100,0,0,0,24426,0,"Alythess - EMOTE_CONFLAGRATION_ALY"), +(25166,7,0,"Sacrolash directs Shadow Nova at $n.",41,0,100,0,0,0,24424,0,"Alythess - EMOTE_SHADOW_NOVA_ALY"); + +DELETE FROM `creature_formations` WHERE `leaderGUID` = 53687; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`, `point_1`, `point_2`) VALUES +(53687,53687,0,0,3,0,0), +(53687,53668,0,0,3,0,0); + +UPDATE `creature_template_addon` SET `auras` = '' WHERE `entry` IN (25165,25166); + +UPDATE `spell_dbc` SET `ProcChance` = 101, `Effect1` = 28, `EffectBasePoints1` = 3, `EffectImplicitTargetA1` = 18, `EffectMiscValue1` = 25214, `EffectMiscValueB1` = 64 WHERE `Id` = 45258; + +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ( +'spell_eredar_twins_blaze', +'spell_eredar_twins_dark_flame_aura_shadow', +'spell_eredar_twins_dark_flame_aura_fire'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(45235, 'spell_eredar_twins_blaze'), +(45343, 'spell_eredar_twins_dark_flame_aura_shadow'), +(47300, 'spell_eredar_twins_dark_flame_aura_fire'); + +DELETE FROM `spell_linked_spell` WHERE `spell_trigger` IN (45347,45348,45270,45271,45256,45248,45329,45246,45342,46771); diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp index 4a40289eb12..baec40cb2aa 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp @@ -15,733 +15,870 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +/* + * Timers requires to be revisited + * The way Shadow Nova \ Conflagration is handled requires re-checks, + there are spells with unknown purpose. Also maybe it should be handled in one event instead of two + */ + #include "ScriptMgr.h" #include "InstanceScript.h" +#include "Map.h" +#include "Player.h" #include "ScriptedCreature.h" #include "SpellInfo.h" +#include "SpellScript.h" #include "sunwell_plateau.h" -enum Quotes +enum SacrolashTexts { - YELL_INTRO_SAC_1 = 0, - YELL_INTRO_SAC_3 = 1, - YELL_INTRO_SAC_5 = 2, - YELL_INTRO_SAC_7 = 3, - YELL_SAC_DEAD = 4, - EMOTE_SHADOW_NOVA = 5, - YELL_ENRAGE = 6, - YELL_SISTER_ALYTHESS_DEAD = 7, - YELL_SAC_KILL = 8, - YELL_SHADOW_NOVA = 9, - - YELL_INTRO_ALY_2 = 0, - YELL_INTRO_ALY_4 = 1, - YELL_INTRO_ALY_6 = 2, - YELL_INTRO_ALY_8 = 3, - EMOTE_CONFLAGRATION = 4, - YELL_ALY_KILL = 5, - YELL_ALY_DEAD = 6, - YELL_SISTER_SACROLASH_DEAD = 7, - YELL_CANFLAGRATION = 8, - YELL_BERSERK = 9 + // Sacrolash + SAY_INTRO_SAC = 0, + SAY_SHADOW_NOVA = 1, + SAY_SLAY_SAC = 2, + SAY_ALYTHESS_DEAD = 3, + SAY_BERSERK_SAC = 4, + SAY_DEATH_SAC = 5, + EMOTE_SHADOW_NOVA_SAC = 6, + EMOTE_CONFLAGRATION_SAC = 7, + + // Alythess + SAY_INTRO_ALY = 0, + SAY_CONFLAGRATION = 1, + SAY_SLAY_ALY = 2, + SAY_SACROLASH_DEAD = 3, + SAY_BERSERK_ALY = 4, + SAY_DEATH_ALY = 5, + EMOTE_CONFLAGRATION_ALY = 6, + EMOTE_SHADOW_NOVA_ALY = 7 }; -enum Spells +enum SacrolashSpells { - //Lady Sacrolash spells - SPELL_DARK_TOUCHED = 45347, - SPELL_SHADOW_BLADES = 45248, //10 secs - SPELL_DARK_STRIKE = 45271, - SPELL_SHADOW_NOVA = 45329, //30-35 secs - SPELL_CONFOUNDING_BLOW = 45256, //25 secs + // Sacrolash - Passive + SPELL_DUAL_WIELD_PASSIVE = 42459, + SPELL_SHADOWFORM = 45455, + + // Sacrolash - Combat + SPELL_SHADOW_BLADES = 45248, + SPELL_CONFOUNDING_BLOW = 45256, + SPELL_SUMMON_SHADOW_IMAGES = 45258, + SPELL_SHADOW_NOVA = 45329, + SPELL_SHADOW_NOVA_DUMMY = 45332, // NYI, related to how Shadow Nova \ Conflagration is handled + + // Alythess - Passive + SPELL_FIREFORM = 45457, + + // Alythess - Combat + SPELL_PYROGENICS = 45230, + SPELL_BLAZE = 45235, + SPELL_FLAME_SEAR = 46771, + SPELL_CONFLAGRATION = 45342, + SPELL_CONFLAGRATION_DUMMY = 45333, // NYI, related to how Shadow Nova \ Conflagration is handled + + // Shared + SPELL_DARK_FLAME_AURA_SAC = 45343, + SPELL_DARK_FLAME_AURA_ALY = 47300, + SPELL_EMPOWER = 45366, + SPELL_BERSERK = 46587, + SPELL_INSTAKILL_SELF = 29878, - //Shadow Image spells + // Shadow Image + SPELL_SHADOW_IMAGE_VISUAL = 45263, SPELL_SHADOW_FURY = 45270, - SPELL_IMAGE_VISUAL = 45263, + SPELL_DARK_STRIKE = 45271, + SPELL_SHADOWSTEP = 45273, - //Misc spells - SPELL_ENRAGE = 46587, - SPELL_EMPOWER = 45366, + // Scripts + SPELL_BLAZE_SUMMON = 45236, SPELL_DARK_FLAME = 45345, - - //Grand Warlock Alythess spells - SPELL_PYROGENICS = 45230, //15secs + SPELL_DARK_TOUCHED = 45347, SPELL_FLAME_TOUCHED = 45348, - SPELL_CONFLAGRATION = 45342, //30-35 secs - SPELL_BLAZE = 45235, //on main target every 3 secs - SPELL_FLAME_SEAR = 46771, - SPELL_BLAZE_SUMMON = 45236, //187366 GO - SPELL_BLAZE_BURN = 45246 + SPELL_BURN = 45246, + SPELL_CONFLAGRATION_TRIGGER = 46768 }; -class boss_sacrolash : public CreatureScript +enum SacrolashEvents { -public: - boss_sacrolash() : CreatureScript("boss_sacrolash") { } - - struct boss_sacrolashAI : public ScriptedAI - { - boss_sacrolashAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + // Sacrolash - Intro + EVENT_SAC_INTRO = 1, + + // Alythess - Intro + EVENT_ALY_INTRO_1, + EVENT_ALY_INTRO_2, + + // Sacrolash - Combat + EVENT_SHADOW_BLADES, + EVENT_CONFOUNDING_BLOW, + EVENT_SUMMON_SHADOW_IMAGES, + + // Alythess - Combat + EVENT_PYROGENICS, + EVENT_BLAZE, + EVENT_FLAME_SEAR, + + // Shared + EVENT_BERSERK, + EVENT_SISTER_DEAD, + EVENT_CONFLAGRATION, + EVENT_SHADOW_NOVA, + EVENT_DEATH_1, + EVENT_DEATH_2 +}; - void Initialize() - { - ShadowbladesTimer = 10000; - ShadownovaTimer = 30000; - ConfoundingblowTimer = 25000; - ShadowimageTimer = 20000; - ConflagrationTimer = 30000; - EnrageTimer = 360000; - SisterDeath = false; - Enraged = false; - } +enum SacrolashMisc +{ + ACTION_START_INTRO = 0, + ACTION_SISTER_DEAD = 1, + SOUND_INTRO = 12484 +}; - InstanceScript* instance; +// 25165 - Lady Sacrolash +struct boss_sacrolash : public BossAI +{ + boss_sacrolash(Creature* creature) : BossAI(creature, DATA_EREDAR_TWINS), _sisterIsDead(false), _isAboutToDie(false) { } - bool SisterDeath; - bool Enraged; + void Reset() override + { + _Reset(); - uint32 ShadowbladesTimer; - uint32 ShadownovaTimer; - uint32 ConfoundingblowTimer; - uint32 ShadowimageTimer; - uint32 ConflagrationTimer; - uint32 EnrageTimer; + DoCastSelf(SPELL_DUAL_WIELD_PASSIVE); + DoCastSelf(SPELL_SHADOWFORM); - void Reset() override - { - Enraged = false; + _sisterIsDead = false; + _isAboutToDie = false; - if (Creature* temp = instance->GetCreature(DATA_ALYTHESS)) - { - if (temp->isDead()) - temp->Respawn(); - else if (temp->GetVictim()) - AddThreat(temp->GetVictim(), 0.0f); - } + me->GetMap()->Respawn(SPAWN_TYPE_CREATURE, instance->GetData64(DATA_ALYTHESS)); + } - if (!me->IsInCombat()) - { - Initialize(); - } + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); - instance->SetBossState(DATA_EREDAR_TWINS, NOT_STARTED); - } + // Prevents sending Badge of Justice from each boss + me->SetLootMode(0); - void JustEngagedWith(Unit* who) override - { - DoZoneInCombat(); + DoCastSelf(SPELL_DARK_FLAME_AURA_SAC); + DoCastSelf(SPELL_DARK_FLAME_AURA_ALY); - Creature* temp = instance->GetCreature(DATA_ALYTHESS); - if (temp && temp->IsAlive() && !temp->GetVictim()) - temp->AI()->AttackStart(who); + events.ScheduleEvent(EVENT_SHADOW_BLADES, 10s, 15s); + events.ScheduleEvent(EVENT_CONFOUNDING_BLOW, 15s, 20s); + events.ScheduleEvent(EVENT_SUMMON_SHADOW_IMAGES, 10s, 30s); + events.ScheduleEvent(EVENT_SHADOW_NOVA, 30s, 35s); + events.ScheduleEvent(EVENT_BERSERK, 6min); + } - instance->SetBossState(DATA_EREDAR_TWINS, IN_PROGRESS); - } + void DamageTaken(Unit* who, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + { + if (_sisterIsDead) + return; - void KilledUnit(Unit* /*victim*/) override + if (damage >= me->GetHealth() && who != me) { - if (rand32() % 4 == 0) - Talk(YELL_SAC_KILL); - } + damage = me->GetHealth() -1; - void JustDied(Unit* /*killer*/) override - { - // only if ALY death - if (SisterDeath) + if (!_isAboutToDie) { - Talk(YELL_SAC_DEAD); + _isAboutToDie = true; + + if (Creature* alythess = instance->GetCreature(DATA_ALYTHESS)) + if (alythess->IsAlive()) + alythess->AI()->DoAction(ACTION_SISTER_DEAD); - instance->SetBossState(DATA_EREDAR_TWINS, DONE); + events.Reset(); + me->InterruptNonMeleeSpells(false); + events.ScheduleEvent(EVENT_DEATH_1, 0s); } - else - me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); } + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + summons.DespawnAll(); + _DespawnAtEvade(); + if (Creature* alythess = instance->GetCreature(DATA_ALYTHESS)) + alythess->RemoveCorpse(false); + } - void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override + void OnSpellCast(SpellInfo const* spell) override + { + switch (spell->Id) { - Unit* unitTarget = target->ToUnit(); - if (!unitTarget) - return; + case SPELL_BERSERK: + Talk(SAY_BERSERK_SAC); + break; + case SPELL_EMPOWER: + Talk(SAY_ALYTHESS_DEAD); + break; + default: + break; + } + } - switch (spellInfo->Id) - { - case SPELL_SHADOW_BLADES: - case SPELL_SHADOW_NOVA: - case SPELL_CONFOUNDING_BLOW: - case SPELL_SHADOW_FURY: - HandleTouchedSpells(unitTarget, SPELL_DARK_TOUCHED); - break; - case SPELL_CONFLAGRATION: - HandleTouchedSpells(unitTarget, SPELL_FLAME_TOUCHED); - break; - } + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_START_INTRO: + events.ScheduleEvent(EVENT_SAC_INTRO, 0s); + break; + case ACTION_SISTER_DEAD: + me->ResetLootMode(); + _sisterIsDead = true; + events.ScheduleEvent(EVENT_SISTER_DEAD, 0s); + events.ScheduleEvent(EVENT_CONFLAGRATION, 15s, 20s); + events.CancelEvent(EVENT_SHADOW_NOVA); + break; + default: + break; } + } + + void KilledUnit(Unit* victim) override + { + // Do not say if killed self by spell + if (victim != me) + Talk(SAY_SLAY_SAC); + } - void HandleTouchedSpells(Unit* target, uint32 TouchedType) + void JustDied(Unit* /*killer*/) override + { + if (_sisterIsDead) { - switch (TouchedType) - { - case SPELL_FLAME_TOUCHED: - if (!target->HasAura(SPELL_DARK_FLAME)) - { - if (target->HasAura(SPELL_DARK_TOUCHED)) - { - target->RemoveAurasDueToSpell(SPELL_DARK_TOUCHED); - target->CastSpell(target, SPELL_DARK_FLAME, true); - } else target->CastSpell(target, SPELL_FLAME_TOUCHED, true); - } - break; - case SPELL_DARK_TOUCHED: - if (!target->HasAura(SPELL_DARK_FLAME)) - { - if (target->HasAura(SPELL_FLAME_TOUCHED)) - { - target->RemoveAurasDueToSpell(SPELL_FLAME_TOUCHED); - target->CastSpell(target, SPELL_DARK_FLAME, true); - } else target->CastSpell(target, SPELL_DARK_TOUCHED, true); - } - break; - } + // It is important to make encounter complete only if both sisters are defeated + // Otherwise if only one sister will be defeated and wipe occured, second sister will despawn on evade and never respawn + _JustDied(); + Talk(SAY_DEATH_SAC); } + else + me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); + } - void UpdateAI(uint32 diff) override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) { - if (!SisterDeath) + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - Unit* Temp = instance->GetCreature(DATA_ALYTHESS); - if (Temp && Temp->isDead()) + switch (eventId) { - Talk(YELL_SISTER_ALYTHESS_DEAD); - DoCast(me, SPELL_EMPOWER); - me->InterruptSpell(CURRENT_GENERIC_SPELL); - SisterDeath = true; + case EVENT_SAC_INTRO: + DoPlaySoundToSet(me, SOUND_INTRO); + Talk(SAY_INTRO_SAC); + break; + default: + break; } } + return; + } - if (!UpdateVictim()) - return; + events.Update(diff); - if (SisterDeath) - { - if (ConflagrationTimer <= diff) - { - if (!me->IsNonMeleeSpellCast(false)) - { - me->InterruptSpell(CURRENT_GENERIC_SPELL); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - DoCast(target, SPELL_CONFLAGRATION); - ConflagrationTimer = 30000 + (rand32() % 5000); - } - } else ConflagrationTimer -= diff; - } - else - { - if (ShadownovaTimer <= diff) - { - if (!me->IsNonMeleeSpellCast(false)) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (target) - DoCast(target, SPELL_SHADOW_NOVA); - - if (!SisterDeath) - { - if (target) - Talk(EMOTE_SHADOW_NOVA, target); - Talk(YELL_SHADOW_NOVA); - } - ShadownovaTimer = 30000 + (rand32() % 5000); - } - } else ShadownovaTimer -=diff; - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (ConfoundingblowTimer <= diff) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (!me->IsNonMeleeSpellCast(false)) - { + case EVENT_SHADOW_BLADES: + DoCastSelf(SPELL_SHADOW_BLADES); + events.Repeat(10s, 15s); + break; + case EVENT_CONFOUNDING_BLOW: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) DoCast(target, SPELL_CONFOUNDING_BLOW); - ConfoundingblowTimer = 20000 + (rand32() % 5000); - } - } else ConfoundingblowTimer -=diff; - - if (ShadowimageTimer <= diff) - { - Unit* target = nullptr; - Creature* temp = nullptr; - for (uint8 i = 0; i<3; ++i) + events.Repeat(20s, 30s); + break; + case EVENT_SUMMON_SHADOW_IMAGES: + DoCastSelf(SPELL_SUMMON_SHADOW_IMAGES); + events.Repeat(8s, 20s); + break; + case EVENT_SHADOW_NOVA: { - target = SelectTarget(SelectTargetMethod::Random, 0); - temp = DoSpawnCreature(NPC_SHADOW_IMAGE, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 10s); - if (temp && target) + Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true); + + if (target) { - AddThreat(target, 1000000.0f, temp); //don't change target(healers) - temp->AI()->AttackStart(target); + DoCast(target, SPELL_SHADOW_NOVA); + Talk(EMOTE_SHADOW_NOVA_SAC, target); + Talk(SAY_SHADOW_NOVA); } + events.Repeat(30s, 35s); + break; } - ShadowimageTimer = 20000; - } else ShadowimageTimer -=diff; - - if (ShadowbladesTimer <= diff) - { - if (!me->IsNonMeleeSpellCast(false)) + case EVENT_CONFLAGRATION: { - DoCast(me, SPELL_SHADOW_BLADES); - ShadowbladesTimer = 10000; - } - } else ShadowbladesTimer -=diff; - - if (EnrageTimer < diff && !Enraged) - { - me->InterruptSpell(CURRENT_GENERIC_SPELL); - Talk(YELL_ENRAGE); - DoCast(me, SPELL_ENRAGE); - Enraged = true; - } else EnrageTimer -= diff; + Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true); - if (me->isAttackReady() && !me->IsNonMeleeSpellCast(false)) - { - //If we are within range melee the target - if (me->IsWithinMeleeRange(me->GetVictim())) - { - HandleTouchedSpells(me->GetVictim(), SPELL_DARK_TOUCHED); - me->AttackerStateUpdate(me->GetVictim()); - me->resetAttackTimer(); + if (target) + { + DoCast(target, SPELL_CONFLAGRATION); + Talk(EMOTE_CONFLAGRATION_SAC, target); + } + events.Repeat(30s, 35s); + break; } + case EVENT_BERSERK: + DoCastSelf(SPELL_BERSERK); + break; + case EVENT_SISTER_DEAD: + DoCastSelf(SPELL_EMPOWER); + break; + case EVENT_DEATH_1: + me->SetReactState(REACT_PASSIVE); + events.ScheduleEvent(EVENT_DEATH_2, 3s); + break; + case EVENT_DEATH_2: + me->SetCorpseDelay(60, true); + DoCastSelf(SPELL_INSTAKILL_SELF); + break; + default: + break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetSunwellPlateauAI<boss_sacrolashAI>(creature); - }; + DoMeleeAttackIfReady(); + } + +private: + bool _sisterIsDead; + bool _isAboutToDie; }; -class boss_alythess : public CreatureScript +// 25166 - Grand Warlock Alythess +struct boss_alythess : public BossAI { -public: - boss_alythess() : CreatureScript("boss_alythess") { } + boss_alythess(Creature* creature) : BossAI(creature, DATA_EREDAR_TWINS), _sisterIsDead(false), _isAboutToDie(false) { } - struct boss_alythessAI : public ScriptedAI + void InitializeAI() override { - boss_alythessAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - SetCombatMovement(false); + BossAI::InitializeAI(); + SetCombatMovement(false); + } - instance = creature->GetInstanceScript(); - IntroStepCounter = 10; - } + void Reset() override + { + _Reset(); - void Initialize() - { - ConflagrationTimer = 45000; - BlazeTimer = 100; - PyrogenicsTimer = 15000; - ShadownovaTimer = 40000; - EnrageTimer = 360000; - FlamesearTimer = 15000; - IntroYellTimer = 10000; - - SisterDeath = false; - Enraged = false; - } + DoCastSelf(SPELL_FIREFORM); + + _sisterIsDead = false; + _isAboutToDie = false; - InstanceScript* instance; + me->GetMap()->Respawn(SPAWN_TYPE_CREATURE, instance->GetData64(DATA_SACROLASH)); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); - bool SisterDeath; - bool Enraged; + // Prevents sending Badge of Justice from each boss + me->SetLootMode(0); - uint32 IntroStepCounter; - uint32 IntroYellTimer; + DoCastSelf(SPELL_DARK_FLAME_AURA_SAC); + DoCastSelf(SPELL_DARK_FLAME_AURA_ALY); - uint32 ConflagrationTimer; - uint32 BlazeTimer; - uint32 PyrogenicsTimer; - uint32 ShadownovaTimer; - uint32 FlamesearTimer; - uint32 EnrageTimer; + events.ScheduleEvent(EVENT_PYROGENICS, 15s, 30s); + events.ScheduleEvent(EVENT_BLAZE, 0s); + events.ScheduleEvent(EVENT_FLAME_SEAR, 10s, 12s); + events.ScheduleEvent(EVENT_CONFLAGRATION, 15s, 20s); + events.ScheduleEvent(EVENT_BERSERK, 6min); + } - void Reset() override + void DamageTaken(Unit* who, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + { + if (_sisterIsDead) + return; + + if (damage >= me->GetHealth() && who != me) { - Enraged = false; + damage = me->GetHealth() -1; - if (Creature* temp = instance->GetCreature(DATA_SACROLASH)) + if (!_isAboutToDie) { - if (temp->isDead()) - temp->Respawn(); - else if (temp->GetVictim()) - AddThreat(temp->GetVictim(), 0.0f); - } + _isAboutToDie = true; - if (!me->IsInCombat()) - { - Initialize(); - } + if (Creature* sacrolash = instance->GetCreature(DATA_SACROLASH)) + if (sacrolash->IsAlive()) + sacrolash->AI()->DoAction(ACTION_SISTER_DEAD); - instance->SetBossState(DATA_EREDAR_TWINS, NOT_STARTED); + events.Reset(); + me->InterruptNonMeleeSpells(false); + events.ScheduleEvent(EVENT_DEATH_1, 0s); + } } + } - void JustEngagedWith(Unit* who) override - { - DoZoneInCombat(); + void EnterEvadeMode(EvadeReason /*why*/) override + { + summons.DespawnAll(); + _DespawnAtEvade(); + if (Creature* sacrolash = instance->GetCreature(DATA_SACROLASH)) + sacrolash->RemoveCorpse(false); + } - Creature* temp = instance->GetCreature(DATA_SACROLASH); - if (temp && temp->IsAlive() && !temp->GetVictim()) - temp->AI()->AttackStart(who); + void OnSpellCast(SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_BERSERK: + Talk(SAY_BERSERK_ALY); + break; + case SPELL_EMPOWER: + Talk(SAY_SACROLASH_DEAD); + break; + default: + break; + } + } - instance->SetBossState(DATA_EREDAR_TWINS, IN_PROGRESS); + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_START_INTRO: + events.ScheduleEvent(EVENT_ALY_INTRO_1, 0s); + break; + case ACTION_SISTER_DEAD: + me->ResetLootMode(); + _sisterIsDead = true; + events.ScheduleEvent(EVENT_SISTER_DEAD, 0s); + events.ScheduleEvent(EVENT_SHADOW_NOVA, 15s, 20s); + events.CancelEvent(EVENT_CONFLAGRATION); + break; + default: + break; } + } + + void KilledUnit(Unit* victim) override + { + // Do not say if killed self by spell + if (victim != me) + Talk(SAY_SLAY_ALY); + } - void AttackStart(Unit* who) override + void JustDied(Unit* /*killer*/) override + { + if (_sisterIsDead) { - if (!me->IsInCombat()) - ScriptedAI::AttackStart(who); + // It is important to make encounter complete only if both sisters are defeated + // Otherwise if only one sister will be defeated and wipe occured, second sister will despawn on evade and never respawn + _JustDied(); + Talk(SAY_DEATH_ALY); } + else + me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); + } - void MoveInLineOfSight(Unit* who) override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) { - if (!who || me->GetVictim()) - return; + events.Update(diff); - if (me->CanCreatureAttack(who)) + while (uint32 eventId = events.ExecuteEvent()) { - float attackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, attackRadius) && me->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && me->IsWithinLOSInMap(who)) + switch (eventId) { - if (!me->IsInCombat()) - { - DoStartNoMovement(who); - } + case EVENT_ALY_INTRO_1: + DoPlaySoundToSet(me, SOUND_INTRO); + events.ScheduleEvent(EVENT_ALY_INTRO_2, 2s); + break; + case EVENT_ALY_INTRO_2: + Talk(SAY_INTRO_ALY); + break; + default: + break; } } - else if (IntroStepCounter == 10 && me->IsWithinLOSInMap(who)&& me->IsWithinDistInMap(who, 30)) - IntroStepCounter = 0; + return; } - void KilledUnit(Unit* /*victim*/) override - { - if (rand32() % 4 == 0) - Talk(YELL_ALY_KILL); - } + events.Update(diff); - void JustDied(Unit* /*killer*/) override - { - if (SisterDeath) - { - Talk(YELL_ALY_DEAD); - instance->SetBossState(DATA_EREDAR_TWINS, DONE); - } - else - me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override + while (uint32 eventId = events.ExecuteEvent()) { - Unit* unitTarget = target->ToUnit(); - if (!unitTarget) - return; - - switch (spellInfo->Id) + switch (eventId) { - case SPELL_BLAZE: - target->CastSpell(unitTarget, SPELL_BLAZE_SUMMON, true); + case EVENT_PYROGENICS: + DoCastSelf(SPELL_PYROGENICS); + events.Repeat(20s, 40s); break; - case SPELL_CONFLAGRATION: - case SPELL_FLAME_SEAR: - HandleTouchedSpells(unitTarget, SPELL_FLAME_TOUCHED); + case EVENT_BLAZE: + DoCastVictim(SPELL_BLAZE); + events.Repeat(3s, 4s); break; - case SPELL_SHADOW_NOVA: - HandleTouchedSpells(unitTarget, SPELL_DARK_TOUCHED); + case EVENT_FLAME_SEAR: + DoCastSelf(SPELL_FLAME_SEAR, { SPELLVALUE_MAX_TARGETS, 5 }); + events.Repeat(8s, 13s); break; - } - } + case EVENT_CONFLAGRATION: + { + Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true); - void HandleTouchedSpells(Unit* target, uint32 TouchedType) - { - switch (TouchedType) - { - case SPELL_FLAME_TOUCHED: - if (!target->HasAura(SPELL_DARK_FLAME)) + if (target) { - if (target->HasAura(SPELL_DARK_TOUCHED)) - { - target->RemoveAurasDueToSpell(SPELL_DARK_TOUCHED); - target->CastSpell(target, SPELL_DARK_FLAME, true); - } - else - target->CastSpell(target, SPELL_FLAME_TOUCHED, true); + DoCast(target, SPELL_CONFLAGRATION); + Talk(EMOTE_CONFLAGRATION_ALY, target); + Talk(SAY_CONFLAGRATION); } + events.Repeat(30s, 35s); break; - case SPELL_DARK_TOUCHED: - if (!target->HasAura(SPELL_DARK_FLAME)) + } + case EVENT_SHADOW_NOVA: + { + Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true); + + if (target) { - if (target->HasAura(SPELL_FLAME_TOUCHED)) - { - target->RemoveAurasDueToSpell(SPELL_FLAME_TOUCHED); - target->CastSpell(target, SPELL_DARK_FLAME, true); - } - else - target->CastSpell(target, SPELL_DARK_TOUCHED, true); + DoCast(target, SPELL_SHADOW_NOVA); + Talk(EMOTE_SHADOW_NOVA_ALY, target); } + events.Repeat(30s, 35s); + break; + } + case EVENT_BERSERK: + DoCastSelf(SPELL_BERSERK); + break; + case EVENT_SISTER_DEAD: + DoCastSelf(SPELL_EMPOWER); + break; + case EVENT_DEATH_1: + me->SetReactState(REACT_PASSIVE); + events.ScheduleEvent(EVENT_DEATH_2, 3s); + break; + case EVENT_DEATH_2: + me->SetCorpseDelay(60, true); + DoCastSelf(SPELL_INSTAKILL_SELF); + break; + default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } + } + +private: + bool _sisterIsDead; + bool _isAboutToDie; +}; - uint32 IntroStep(uint32 step) +// 25214 - Shadow Image +struct npc_shadow_image : public ScriptedAI +{ + npc_shadow_image(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + me->SetCorpseDelay(0); + } + + void JustAppeared() override + { + DoCastSelf(SPELL_SHADOW_IMAGE_VISUAL); + + DoZoneInCombat(); + + _scheduler.Schedule(1s, [this](TaskContext /*task*/) { - Creature* Sacrolash = instance->GetCreature(DATA_SACROLASH); - switch (step) - { - case 0: - return 0; - case 1: - if (Sacrolash) - Sacrolash->AI()->Talk(YELL_INTRO_SAC_1); - return 1000; - case 2: - Talk(YELL_INTRO_ALY_2); - return 1000; - case 3: - if (Sacrolash) - Sacrolash->AI()->Talk(YELL_INTRO_SAC_3); - return 2000; - case 4: - Talk(YELL_INTRO_ALY_4); - return 1000; - case 5: - if (Sacrolash) - Sacrolash->AI()->Talk(YELL_INTRO_SAC_5); - return 2000; - case 6: - Talk(YELL_INTRO_ALY_6); - return 1000; - case 7: - if (Sacrolash) - Sacrolash->AI()->Talk(YELL_INTRO_SAC_7); - return 3000; - case 8: - Talk(YELL_INTRO_ALY_8); - return 900000; - } - return 10000; - } + AttackRandomTarget(); + }); - void UpdateAI(uint32 diff) override + _scheduler.Schedule(10s, [this](TaskContext /*task*/) { - if (IntroStepCounter < 9) - { - if (IntroYellTimer <= diff) - { - IntroYellTimer = IntroStep(++IntroStepCounter); - } else IntroYellTimer -= diff; - } + me->KillSelf(); + }); + } - if (!SisterDeath) - { - Unit* Temp = instance->GetCreature(DATA_SACROLASH); - if (Temp && Temp->isDead()) - { - Talk(YELL_SISTER_SACROLASH_DEAD); - DoCast(me, SPELL_EMPOWER); - me->InterruptSpell(CURRENT_GENERIC_SPELL); - SisterDeath = true; - } - } - if (!me->GetVictim()) - { - Creature* sisiter = instance->GetCreature(DATA_SACROLASH); - if (sisiter && !sisiter->isDead() && sisiter->GetVictim()) - { - AddThreat(sisiter->GetVictim(), 0.0f); - DoStartNoMovement(sisiter->GetVictim()); - me->Attack(sisiter->GetVictim(), false); - } - } + void Reset() override + { + _scheduler.CancelAll(); + } - if (!UpdateVictim()) - return; + void AttackRandomTarget() + { + ResetThreatList(); + DoZoneInCombat(); - if (SisterDeath) + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) + { + AddThreat(target, 1000000.0f); + AttackStart(target); + } + } + + void JustEngagedWith(Unit* /*who*/) override + { + switch (urand(0, 2)) + { + case 0: { - if (ShadownovaTimer <= diff) + _scheduler.Schedule(2s, [this](TaskContext task) { - if (!me->IsNonMeleeSpellCast(false)) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - DoCast(target, SPELL_SHADOW_NOVA); - ShadownovaTimer = 30000 + (rand32() % 5000); - } - } else ShadownovaTimer -=diff; + if (me->IsWithinMeleeRange(me->GetVictim())) + DoCastSelf(SPELL_SHADOW_FURY); + else + task.Repeat(100ms); + }); + break; } - else + case 1: { - if (ConflagrationTimer <= diff) + _scheduler.Schedule(2s, [this](TaskContext task) { - if (!me->IsNonMeleeSpellCast(false)) - { - me->InterruptSpell(CURRENT_GENERIC_SPELL); - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (target) - DoCast(target, SPELL_CONFLAGRATION); - ConflagrationTimer = 30000 + (rand32() % 5000); - - if (!SisterDeath) - { - if (target) - Talk(EMOTE_CONFLAGRATION, target); - Talk(YELL_CANFLAGRATION); - } - - BlazeTimer = 4000; - } - } else ConflagrationTimer -= diff; + DoCastVictim(SPELL_DARK_STRIKE); + task.Repeat(1s); + }); + break; } - - if (FlamesearTimer <= diff) - { - if (!me->IsNonMeleeSpellCast(false)) - { - DoCast(me, SPELL_FLAME_SEAR); - FlamesearTimer = 15000; - } - } else FlamesearTimer -=diff; - - if (PyrogenicsTimer <= diff) + case 2: { - if (!me->IsNonMeleeSpellCast(false)) + _scheduler.Schedule(2s, [this](TaskContext task) { - DoCast(me, SPELL_PYROGENICS, true); - PyrogenicsTimer = 15000; - } - } else PyrogenicsTimer -= diff; + DoCastVictim(SPELL_DARK_STRIKE); + task.Repeat(1s); + }); - if (BlazeTimer <= diff) - { - if (!me->IsNonMeleeSpellCast(false)) + _scheduler.Schedule(3s, [this](TaskContext task) { - DoCastVictim(SPELL_BLAZE); - BlazeTimer = 3800; - } - } else BlazeTimer -= diff; + DoCastVictim(SPELL_SHADOWSTEP); + task.Repeat(1s); + }); + break; + } + default: + break; + } + } - if (EnrageTimer < diff && !Enraged) - { - me->InterruptSpell(CURRENT_GENERIC_SPELL); - Talk(YELL_BERSERK); - DoCast(me, SPELL_ENRAGE); - Enraged = true; - } else EnrageTimer -= diff; + void OnSpellCast(SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_SHADOW_FURY: + me->DespawnOrUnsummon(1s); + break; + case SPELL_DARK_STRIKE: + // Not sure where exactly this should be called. What is known is it is called not always, + // before Dark Strike, in both scenarios with Dark Strike. What should be checked is the exact place where it should be called. + // Also looks like it is called every time before first Dark Strike, but that should be re-checked + if (roll_chance_i(50)) + AttackRandomTarget(); + break; + default: + break; } - }; + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetSunwellPlateauAI<boss_alythessAI>(creature); - }; + _scheduler.Update(diff); + + if (UpdateVictim()) + DoMeleeAttackIfReady(); + } + +private: + TaskScheduler _scheduler; }; -class npc_shadow_image : public CreatureScript +// 45235 - Blaze +class spell_eredar_twins_blaze : public SpellScript { -public: - npc_shadow_image() : CreatureScript("npc_shadow_image") { } + PrepareSpellScript(spell_eredar_twins_blaze); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_BLAZE_SUMMON }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_BLAZE_SUMMON, true); + } - CreatureAI* GetAI(Creature* creature) const override + void Register() override { - return GetSunwellPlateauAI<npc_shadow_imageAI>(creature); - }; + OnEffectHitTarget += SpellEffectFn(spell_eredar_twins_blaze::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; - struct npc_shadow_imageAI : public ScriptedAI +// 45343 - Dark Flame Aura +class spell_eredar_twins_dark_flame_aura_shadow : public AuraScript +{ + PrepareAuraScript(spell_eredar_twins_dark_flame_aura_shadow); + + bool Validate(SpellInfo const* /*spellInfo*/) override { - npc_shadow_imageAI(Creature* creature) : ScriptedAI(creature) + return ValidateSpellInfo( { - Initialize(); - } + SPELL_DARK_FLAME, + SPELL_FLAME_TOUCHED, + SPELL_DARK_TOUCHED, + SPELL_SHADOW_BLADES, + SPELL_CONFOUNDING_BLOW, + SPELL_SHADOW_NOVA, + SPELL_SHADOW_FURY, + SPELL_DARK_STRIKE + }); + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Don't allow to proc if target has Dark Flame + if (eventInfo.GetActionTarget()->HasAura(SPELL_DARK_FLAME)) + return false; - void Initialize() + // Allow proc from melee damage (Shadow Image deals no melee damage (see on-spawn aura), only Sacrolash does) + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return true; + + // Allow proc from shadow spells + switch (spellInfo->Id) { - ShadowfuryTimer = 5000 + (rand32() % 15000); - DarkstrikeTimer = 3000; - KillTimer = 15000; + case SPELL_SHADOW_BLADES: + case SPELL_CONFOUNDING_BLOW: + case SPELL_SHADOW_NOVA: + case SPELL_SHADOW_FURY: + case SPELL_DARK_STRIKE: + return true; + default: + return false; } - uint32 ShadowfuryTimer; - uint32 KillTimer; - uint32 DarkstrikeTimer; + return false; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + Unit* target = eventInfo.GetActionTarget(); - void Reset() override + if (target->HasAura(SPELL_FLAME_TOUCHED)) { - Initialize(); + target->RemoveAurasDueToSpell(SPELL_FLAME_TOUCHED); + target->CastSpell(target, SPELL_DARK_FLAME, true); } + else + target->CastSpell(target, SPELL_DARK_TOUCHED, true); + } - void JustEngagedWith(Unit* /*who*/) override { } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_eredar_twins_dark_flame_aura_shadow::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_eredar_twins_dark_flame_aura_shadow::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } +}; + +// 47300 - Dark Flame Aura +class spell_eredar_twins_dark_flame_aura_fire : public AuraScript +{ + PrepareAuraScript(spell_eredar_twins_dark_flame_aura_fire); - void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - Unit* unitTarget = target->ToUnit(); - if (!unitTarget) - return; + SPELL_DARK_FLAME, + SPELL_FLAME_TOUCHED, + SPELL_DARK_TOUCHED, + SPELL_BLAZE, + SPELL_FLAME_SEAR, + SPELL_CONFLAGRATION, + SPELL_BURN, + SPELL_CONFLAGRATION_TRIGGER + }); + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Don't allow to proc if target has Dark Flame + if (eventInfo.GetActionTarget()->HasAura(SPELL_DARK_FLAME)) + return false; - switch (spellInfo->Id) - { - case SPELL_SHADOW_FURY: - case SPELL_DARK_STRIKE: - if (!unitTarget->HasAura(SPELL_DARK_FLAME)) - { - if (unitTarget->HasAura(SPELL_FLAME_TOUCHED)) - { - unitTarget->RemoveAurasDueToSpell(SPELL_FLAME_TOUCHED); - unitTarget->CastSpell(unitTarget, SPELL_DARK_FLAME, true); - } - else - unitTarget->CastSpell(unitTarget, SPELL_DARK_TOUCHED, true); - } - break; - } + // Don't allow to proc from melee damage, Alythess any way doesn't do melee damage + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return false; + + // Allow proc from fire spells + switch (spellInfo->Id) + { + case SPELL_BLAZE: + case SPELL_FLAME_SEAR: + case SPELL_CONFLAGRATION: + case SPELL_BURN: + case SPELL_CONFLAGRATION_TRIGGER: + return true; + default: + return false; } - void UpdateAI(uint32 diff) override + return false; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + Unit* target = eventInfo.GetActionTarget(); + + if (target->HasAura(SPELL_DARK_TOUCHED)) { - if (!me->HasAura(SPELL_IMAGE_VISUAL)) - DoCast(me, SPELL_IMAGE_VISUAL); + target->RemoveAurasDueToSpell(SPELL_DARK_TOUCHED); + target->CastSpell(target, SPELL_DARK_FLAME, true); + } + else + target->CastSpell(target, SPELL_FLAME_TOUCHED, true); + } - if (KillTimer <= diff) - { - me->KillSelf(); - KillTimer = 9999999; - } else KillTimer -= diff; + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_eredar_twins_dark_flame_aura_fire::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_eredar_twins_dark_flame_aura_fire::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } +}; - if (!UpdateVictim()) - return; +// 4937 +class at_witchs_sanctum : public OnlyOnceAreaTriggerScript +{ +public: + at_witchs_sanctum() : OnlyOnceAreaTriggerScript("at_witchs_sanctum") { } - if (ShadowfuryTimer <= diff) - { - DoCast(me, SPELL_SHADOW_FURY); - ShadowfuryTimer = 10000; - } else ShadowfuryTimer -=diff; + bool TryHandleOnce(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + { + if (InstanceScript* instance = player->GetInstanceScript()) + { + if (Creature* sacrolash = instance->GetCreature(DATA_SACROLASH)) + sacrolash->AI()->DoAction(ACTION_START_INTRO); - if (DarkstrikeTimer <= diff) - { - if (!me->IsNonMeleeSpellCast(false)) - { - //If we are within range melee the target - if (me->IsWithinMeleeRange(me->GetVictim())) - DoCastVictim(SPELL_DARK_STRIKE); - } - DarkstrikeTimer = 3000; - } else DarkstrikeTimer -= diff; + if (Creature* alythess = instance->GetCreature(DATA_ALYTHESS)) + alythess->AI()->DoAction(ACTION_START_INTRO); } - }; + + return true; + } }; void AddSC_boss_eredar_twins() { - new boss_sacrolash(); - new boss_alythess(); - new npc_shadow_image(); + RegisterSunwellPlateauCreatureAI(boss_sacrolash); + RegisterSunwellPlateauCreatureAI(boss_alythess); + RegisterSunwellPlateauCreatureAI(npc_shadow_image); + RegisterSpellScript(spell_eredar_twins_blaze); + RegisterSpellScript(spell_eredar_twins_dark_flame_aura_shadow); + RegisterSpellScript(spell_eredar_twins_dark_flame_aura_fire); + new at_witchs_sanctum(); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp index baad3460be8..248480c65ff 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp @@ -17,6 +17,7 @@ #include "ScriptMgr.h" #include "AreaBoundary.h" +#include "Creature.h" #include "InstanceScript.h" #include "Log.h" #include "Map.h" @@ -108,6 +109,38 @@ class instance_sunwell_plateau : public InstanceMapScript return nullptr; } + void OnCreatureCreate(Creature* creature) override + { + InstanceScript::OnCreatureCreate(creature); + + switch (creature->GetEntry()) + { + case NPC_LADY_SACROLASH: + EredarTwinsSpawnId[0] = creature->GetSpawnId(); + break; + case NPC_GRAND_WARLOCK_ALYTHESS: + EredarTwinsSpawnId[1] = creature->GetSpawnId(); + break; + default: + break; + } + } + + uint64 GetData64(uint32 type) const override + { + switch (type) + { + case DATA_SACROLASH: + return EredarTwinsSpawnId[0]; + case DATA_ALYTHESS: + return EredarTwinsSpawnId[1]; + default: + break; + } + + return InstanceScript::GetData64(type); + } + ObjectGuid GetGuidData(uint32 id) const override { switch (id) @@ -120,8 +153,10 @@ class instance_sunwell_plateau : public InstanceMapScript default: break; } - return ObjectGuid::Empty; + return InstanceScript::GetGuidData(id); } + + ObjectGuid::LowType EredarTwinsSpawnId[2] = { }; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override |
