aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/world/3.3.5/2025_11_03_00_world.sql46
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp1261
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp37
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