diff options
3 files changed, 193 insertions, 124 deletions
diff --git a/sql/updates/world/3.3.5/2025_07_08_01_world.sql b/sql/updates/world/3.3.5/2025_07_08_01_world.sql new file mode 100644 index 00000000000..f364d63db02 --- /dev/null +++ b/sql/updates/world/3.3.5/2025_07_08_01_world.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_dalliah_the_doomsayer_whirlwind'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(36142, 'spell_dalliah_the_doomsayer_whirlwind'); diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp index d445cd2b7e8..6a5f6358062 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp @@ -15,13 +15,21 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +/* + * Whirlwind gets interrupted in lots of cases + * Conversation between creatures requires rechecks and improvements + */ + #include "ScriptMgr.h" #include "arcatraz.h" #include "InstanceScript.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "SpellInfo.h" +#include "SpellMgr.h" +#include "SpellScript.h" -enum Say +enum DalliahTexts { // Dalliah the Doomsayer SAY_AGGRO = 1, @@ -36,83 +44,112 @@ enum Say SAY_DALLIAH_25_PERCENT = 5 }; -enum Spells +enum DalliahSpells { SPELL_GIFT_OF_THE_DOOMSAYER = 36173, SPELL_WHIRLWIND = 36142, SPELL_HEAL = 36144, - SPELL_SHADOW_WAVE = 39016 // Heroic only + SPELL_SHADOW_WAVE = 39016, // Heroic only + + SPELL_DUMMY = 36177 }; -enum Events +enum DalliahEvents { EVENT_GIFT_OF_THE_DOOMSAYER = 1, - EVENT_WHIRLWIND = 2, - EVENT_HEAL = 3, - EVENT_SHADOW_WAVE = 4, // Heroic only - EVENT_ME_FIRST = 5, - EVENT_SOCCOTHRATES_DEATH = 6 + EVENT_WHIRLWIND, + EVENT_HEAL, + EVENT_SHADOW_WAVE, + + EVENT_ME_FIRST, + EVENT_SOCCOTHRATES_DEATH }; +// 20885 - Dalliah the Doomsayer struct boss_dalliah_the_doomsayer : public BossAI { - boss_dalliah_the_doomsayer(Creature* creature) : BossAI(creature, DATA_DALLIAH) - { - soccothratesTaunt = false; - soccothratesDeath = false; - } + boss_dalliah_the_doomsayer(Creature* creature) : BossAI(creature, DATA_DALLIAH), + _soccothratesTaunt(false), _soccothratesDeath(false) { } void Reset() override { _Reset(); - soccothratesTaunt = false; - soccothratesDeath = false; - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - Talk(SAY_DEATH); - - if (Creature* soccothrates = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SOCCOTHRATES))) - if (soccothrates->IsAlive() && !soccothrates->IsInCombat()) - soccothrates->AI()->SetData(1, 1); + _soccothratesTaunt = false; + _soccothratesDeath = false; } void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); - events.ScheduleEvent(EVENT_GIFT_OF_THE_DOOMSAYER, 1s, 4s); - events.ScheduleEvent(EVENT_WHIRLWIND, 7s, 9s); + events.ScheduleEvent(EVENT_GIFT_OF_THE_DOOMSAYER, 0s, 10s); + events.ScheduleEvent(EVENT_WHIRLWIND, 5s, 10s); if (IsHeroic()) - events.ScheduleEvent(EVENT_SHADOW_WAVE, 11s, 16s); + events.ScheduleEvent(EVENT_SHADOW_WAVE, 10s, 15s); events.ScheduleEvent(EVENT_ME_FIRST, 6s); Talk(SAY_AGGRO); } - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_SLAY); - } - void SetData(uint32 /*type*/, uint32 data) override { switch (data) { case 1: events.ScheduleEvent(EVENT_SOCCOTHRATES_DEATH, 6s); - soccothratesDeath = true; + _soccothratesDeath = true; break; default: break; } } + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + { + if (!_soccothratesTaunt && me->HealthBelowPctDamaged(25, damage)) + { + _soccothratesTaunt = true; + + if (Creature* soccothrates = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SOCCOTHRATES))) + soccothrates->AI()->Talk(SAY_DALLIAH_25_PERCENT); + } + } + + void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override + { + if (spellInfo->Id == SPELL_DUMMY) + events.ScheduleEvent(EVENT_HEAL, 0s); + } + + void OnSpellCast(SpellInfo const* spell) override + { + if (spell->Id == SPELL_WHIRLWIND) + if (roll_chance_i(30)) + Talk(SAY_WHIRLWIND); + + if (spell->Id == sSpellMgr->GetSpellIdForDifficulty(SPELL_HEAL, me)) + if (roll_chance_i(50)) + Talk(SAY_HEAL); + } + + void KilledUnit(Unit* /*victim*/) override + { + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + + if (Creature* soccothrates = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SOCCOTHRATES))) + if (soccothrates->IsAlive() && !soccothrates->IsInCombat()) + soccothrates->AI()->SetData(1, 1); + } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) { - if (soccothratesDeath) + if (_soccothratesDeath) { events.Update(diff); @@ -142,23 +179,21 @@ struct boss_dalliah_the_doomsayer : public BossAI switch (eventId) { case EVENT_GIFT_OF_THE_DOOMSAYER: - DoCastVictim(SPELL_GIFT_OF_THE_DOOMSAYER, true); - events.ScheduleEvent(EVENT_GIFT_OF_THE_DOOMSAYER, 16s, 21s); + DoCastVictim(SPELL_GIFT_OF_THE_DOOMSAYER); + events.Repeat(10s, 20s); break; case EVENT_WHIRLWIND: - DoCast(me, SPELL_WHIRLWIND); - Talk(SAY_WHIRLWIND); - events.ScheduleEvent(EVENT_WHIRLWIND, 19s, 21s); - events.ScheduleEvent(EVENT_HEAL, 6s); + DoCastSelf(SPELL_WHIRLWIND); + events.Repeat(15s, 25s); break; case EVENT_HEAL: - DoCast(me, SPELL_HEAL); - Talk(SAY_HEAL); + DoCastSelf(SPELL_HEAL); break; case EVENT_SHADOW_WAVE: - DoCastVictim(SPELL_SHADOW_WAVE, true); - events.ScheduleEvent(EVENT_SHADOW_WAVE, 11s, 16s); + DoCastVictim(SPELL_SHADOW_WAVE); + events.Repeat(10s, 15s); break; + case EVENT_ME_FIRST: if (Creature* soccothrates = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SOCCOTHRATES))) if (soccothrates->IsAlive() && !soccothrates->IsInCombat()) @@ -172,22 +207,39 @@ struct boss_dalliah_the_doomsayer : public BossAI return; } - if (HealthBelowPct(25) && !soccothratesTaunt) - { - if (Creature* soccothrates = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SOCCOTHRATES))) - soccothrates->AI()->Talk(SAY_DALLIAH_25_PERCENT); - soccothratesTaunt = true; - } - DoMeleeAttackIfReady(); } private: - bool soccothratesTaunt; - bool soccothratesDeath; + bool _soccothratesTaunt; + bool _soccothratesDeath; +}; + +// 36142 - Whirlwind +class spell_dalliah_the_doomsayer_whirlwind : public AuraScript +{ + PrepareAuraScript(spell_dalliah_the_doomsayer_whirlwind); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DUMMY }); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // When Whirlwind ends, creature casts Dummy spell. When Dummy spell hits, + // creature casts Heal spell and this is how Heal after Whirlwind is handled + GetTarget()->CastSpell(GetTarget(), SPELL_DUMMY, true); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_dalliah_the_doomsayer_whirlwind::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } }; void AddSC_boss_dalliah_the_doomsayer() { RegisterArcatrazCreatureAI(boss_dalliah_the_doomsayer); + RegisterSpellScript(spell_dalliah_the_doomsayer_whirlwind); } diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp index 0af123ae289..3334fbc7e9c 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp @@ -15,12 +15,10 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: boss_wrath_scryer_soccothrates -SD%Complete: 95% -SDComment: charge left to script -SDCategory: Tempest Keep, The Arcatraz -EndScriptData */ +/* + * Charge left to script + * Conversation between creatures requires rechecks and improvements + */ #include "ScriptMgr.h" #include "arcatraz.h" @@ -28,8 +26,9 @@ EndScriptData */ #include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "SpellInfo.h" -enum Say +enum SoccothratesTexts { // Wrath-Scryer Soccothrates SAY_AGGRO = 1, @@ -50,75 +49,61 @@ enum Say SAY_DALLIAH_CONVO_3 = 10 }; -enum Spells +enum SoccothratesSpells { SPELL_FELFIRE_SHOCK = 35759, SPELL_KNOCK_AWAY = 36512, + SPELL_FELFIRE_LINE_UP = 35770, + SPELL_TRIGGER_CHARGE_TARGETING = 36564, SPELL_CHARGE_TARGETING = 36038, SPELL_CHARGE = 35754 }; -enum Events +enum SoccothratesEvents { EVENT_FELFIRE_SHOCK = 1, - EVENT_KNOCK_AWAY = 2, - - EVENT_PREFIGHT_1 = 3, - EVENT_PREFIGHT_2 = 4, - EVENT_PREFIGHT_3 = 5, - EVENT_PREFIGHT_4 = 6, - EVENT_PREFIGHT_5 = 7, - EVENT_PREFIGHT_6 = 8, - EVENT_PREFIGHT_7 = 9, - EVENT_PREFIGHT_8 = 10, - EVENT_PREFIGHT_9 = 11, - EVENT_ME_FIRST = 12, - EVENT_DALLIAH_DEATH = 13 + EVENT_KNOCK_AWAY, + + EVENT_PREFIGHT_1, + EVENT_PREFIGHT_2, + EVENT_PREFIGHT_3, + EVENT_PREFIGHT_4, + EVENT_PREFIGHT_5, + EVENT_PREFIGHT_6, + EVENT_PREFIGHT_7, + EVENT_PREFIGHT_8, + EVENT_PREFIGHT_9, + + EVENT_ME_FIRST, + EVENT_DALLIAH_DEATH }; +// 20886 - Wrath-Scryer Soccothrates struct boss_wrath_scryer_soccothrates : public BossAI { - boss_wrath_scryer_soccothrates(Creature* creature) : BossAI(creature, DATA_SOCCOTHRATES) - { - preFight = false; - dalliahTaunt = false; - dalliahDeath = false; - } + boss_wrath_scryer_soccothrates(Creature* creature) : BossAI(creature, DATA_SOCCOTHRATES), + _preFight(false), _dalliahTaunt(false), _dalliahDeath(false) { } void Reset() override { _Reset(); - preFight = false; - dalliahTaunt = false; - dalliahDeath = false; - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - Talk(SAY_DEATH); - - if (Creature* dalliah = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_DALLIAH))) - if (dalliah->IsAlive() && !dalliah->IsInCombat()) - dalliah->AI()->SetData(1, 1); + _preFight = false; + _dalliahTaunt = false; + _dalliahDeath = false; } void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); - events.ScheduleEvent(EVENT_FELFIRE_SHOCK, 12s, 14s); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 11s, 12s); + events.ScheduleEvent(EVENT_FELFIRE_SHOCK, 8s, 18s); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 25s, 30s); events.ScheduleEvent(EVENT_ME_FIRST, 6s); Talk(SAY_AGGRO); - preFight = false; - } - - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_SLAY); + _preFight = false; } + /// @todo: Maybe this is handled by Gameobject 184953 (Tempest Keep Prison - Boss React - Trigger 001) or it belongs to Mellichar's event void MoveInLineOfSight(Unit* who) override { if (instance->GetData(DATA_CONVERSATION) == NOT_STARTED && who->GetTypeId() == TYPEID_PLAYER && me->IsWithinDistInMap(who, 70.0f)) @@ -126,9 +111,11 @@ struct boss_wrath_scryer_soccothrates : public BossAI Talk(SAY_SOCCOTHRATES_CONVO_1); instance->SetData(DATA_CONVERSATION, DONE); - preFight = true; + _preFight = true; events.ScheduleEvent(EVENT_PREFIGHT_1, 2s); } + + BossAI::MoveInLineOfSight(who); } void SetData(uint32 /*type*/, uint32 data) override @@ -137,18 +124,51 @@ struct boss_wrath_scryer_soccothrates : public BossAI { case 1: events.ScheduleEvent(EVENT_DALLIAH_DEATH, 6s); - dalliahDeath = true; + _dalliahDeath = true; break; default: break; } } + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + { + if (!_dalliahTaunt && me->HealthBelowPctDamaged(25, damage)) + { + _dalliahTaunt = true; + + if (Creature* dalliah = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_DALLIAH))) + dalliah->AI()->Talk(SAY_SOCCOTHRATES_25_PERCENT); + } + } + + void OnSpellCast(SpellInfo const* spell) override + { + if (spell->Id == SPELL_KNOCK_AWAY) + if (roll_chance_i(50)) + Talk(SAY_KNOCK_AWAY); + } + + void KilledUnit(Unit* /*victim*/) override + { + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + + if (Creature* dalliah = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_DALLIAH))) + if (dalliah->IsAlive() && !dalliah->IsInCombat()) + dalliah->AI()->SetData(1, 1); + } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) { - if (preFight) + if (_preFight) { events.Update(diff); @@ -199,7 +219,7 @@ struct boss_wrath_scryer_soccothrates : public BossAI me->SetFacingToObject(dalliah); dalliah->SetHomePosition(dalliah->GetPositionX(), dalliah->GetPositionY(), dalliah->GetPositionZ(), 1.51737f); me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 4.725722f); - preFight = false; + _preFight = false; } break; default: @@ -208,7 +228,7 @@ struct boss_wrath_scryer_soccothrates : public BossAI } } - if (dalliahDeath) + if (_dalliahDeath) { events.Update(diff); @@ -238,14 +258,14 @@ struct boss_wrath_scryer_soccothrates : public BossAI switch (eventId) { case EVENT_FELFIRE_SHOCK: - DoCastVictim(SPELL_FELFIRE_SHOCK, true); - events.ScheduleEvent(EVENT_FELFIRE_SHOCK, 12s, 14s); + DoCastVictim(SPELL_FELFIRE_SHOCK); + events.Repeat(10s, 15s); break; case EVENT_KNOCK_AWAY: - DoCast(me, SPELL_KNOCK_AWAY); - Talk(SAY_KNOCK_AWAY); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 11s, 12s); + DoCastSelf(SPELL_KNOCK_AWAY); + events.Repeat(25s, 30s); break; + case EVENT_ME_FIRST: if (Creature* dalliah = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_DALLIAH))) if (dalliah->IsAlive() && !dalliah->IsInCombat()) @@ -259,20 +279,13 @@ struct boss_wrath_scryer_soccothrates : public BossAI return; } - if (HealthBelowPct(25) && !dalliahTaunt) - { - if (Creature* dalliah = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_DALLIAH))) - dalliah->AI()->Talk(SAY_SOCCOTHRATES_25_PERCENT); - dalliahTaunt = true; - } - DoMeleeAttackIfReady(); } private: - bool preFight; - bool dalliahTaunt; - bool dalliahDeath; + bool _preFight; + bool _dalliahTaunt; + bool _dalliahDeath; }; void AddSC_boss_wrath_scryer_soccothrates() |