aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authoroffl <11556157+offl@users.noreply.github.com>2025-07-27 23:33:52 +0300
committerGitHub <noreply@github.com>2025-07-27 22:33:52 +0200
commit994b1df1158a5f4a6c045f8c5b470be04b7ac46f (patch)
tree83f186a5560769d0dd01d73c2ea93721a32dc7b1 /src/server
parent48fd0413046ebc11d59883eaabbfdb99981647d8 (diff)
Scripts/Serpentshrine Cavern: Rewrite Karathress (#31136)
* Create AI for Cyclone & summon it by spell * Create formation for linked aggro & replace scripted linked aggro * Script The Beast Within spell & use correct trigger spell * Create proper AI for Fathom Lurker & Fathom Sporebat * Summon Fathom Lurker & Fathom Sporebat by spells * Create AI for all totems used in encounter * Spawn Seer Olum in DB instead of wrongly summoning him * Remove redundant Karathress' event from instance script * Cleanup headers * Correctly use all Karathress' texts * Cleanup & split enum * Use EventMap and TaskScheduler instead of old events * Fix respawn bugs of advisors * Properly handle abilities Karathress gains from advisors * Properly handle Blessing Of Tides * Remove redundant SetBossState calls from advisors * Update timers of all spells * Update targets of all spells
Diffstat (limited to 'src/server')
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp805
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp5
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/serpent_shrine.h2
3 files changed, 342 insertions, 470 deletions
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp
index 93e2b3dff7c..0d6b6dd2eca 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp
@@ -15,169 +15,181 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Boss_Fathomlord_Karathress
-SD%Complete: 70
-SDComment: Cyclone workaround
-SDCategory: Coilfang Resevoir, Serpent Shrine Cavern
-EndScriptData */
+/*
+ * Greater Poison Cleansing Totem has no passive spell (nothing found in sniffs). What spell should it use?
+ * Seer Olum's event is NYI (should be handled in DB)
+ * Verify Enrage spell, most likely current ID is not correct
+ * Damage of pets requires adjustments (seems too low) (DB issue)
+ * Bestial Wrath cannot be applied on Fathom Lurker and only applies on Fathom Sporebat. The reason is Creature Type Mask = 0x00000001 (BEAST).
+ It is unclear if this is an issue or not
+ * Sharkkis should be able to summon multiple pets, at this moment he can have only one summoned pet per time
+ * Totems and Cyclone probably should despawn in case of wipe. At this moment it is NYI
+ * Transform spells (handled in SAI for each totem) for totems cannot be casted by totems because of Totem::IsImmunedToSpellEffect (SPELL_AURA_TRANSFORM)
+ */
#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
-#include "ScriptedEscortAI.h"
+#include "ScriptedCreature.h"
+#include "SpellInfo.h"
#include "SpellScript.h"
#include "serpent_shrine.h"
-#include "TemporarySummon.h"
-enum FathomlordKarathress
+enum KarathressTexts
{
SAY_AGGRO = 0,
SAY_GAIN_BLESSING = 1,
- SAY_GAIN_ABILITY1 = 2,
- SAY_GAIN_ABILITY2 = 3,
- SAY_GAIN_ABILITY3 = 4,
- SAY_SLAY = 5,
- SAY_DEATH = 6,
+ SAY_GAIN_ABILITY = 2,
+ SAY_SLAY = 3,
+ SAY_DEATH = 4
+};
- //Karathress spells
+enum KarathressSpells
+{
+ // Karathress
SPELL_CATACLYSMIC_BOLT = 38441,
- SPELL_POWER_OF_SHARKKIS = 38455,
- SPELL_POWER_OF_TIDALVESS = 38452,
- SPELL_POWER_OF_CARIBDIS = 38451,
- SPELL_ENRAGE = 24318,
SPELL_SEAR_NOVA = 38445,
- SPELL_BLESSING_OF_THE_TIDES = 38449,
+ SPELL_ENRAGE = 24318,
- //Sharkkis spells
+ // Sharkkis
SPELL_LEECHING_THROW = 29436,
SPELL_THE_BEAST_WITHIN = 38373,
- SPELL_MULTISHOT = 38366,
+ SPELL_MULTI_TOSS = 38366,
SPELL_SUMMON_FATHOM_LURKER = 38433,
SPELL_SUMMON_FATHOM_SPOREBAT = 38431,
- SPELL_PET_ENRAGE = 19574,
- //Tidalvess spells
+ // Tidalvess
+ SPELL_WINDFURY = 38184,
SPELL_FROST_SHOCK = 38234,
SPELL_SPITFIRE_TOTEM = 38236,
- SPELL_POISON_CLEANSING_TOTEM = 38306,
- // Spell obsolete
SPELL_EARTHBIND_TOTEM = 38304,
- SPELL_EARTHBIND_TOTEM_EFFECT = 6474,
- SPELL_WINDFURY_WEAPON = 38184,
+ SPELL_POISON_CLEANSING_TOTEM = 38306,
- //Caribdis Spells
+ // Caribdis
SPELL_WATER_BOLT_VOLLEY = 38335,
SPELL_TIDAL_SURGE = 38358,
- SPELL_TIDAL_SURGE_EFFECT = 38353,
- SPELL_HEAL = 38330,
SPELL_SUMMON_CYCLONE = 38337,
- SPELL_CYCLONE_CYCLONE = 29538,
-
- //Yells and Quotes
- SOUND_MISC = 11283,
-
- //Summoned Unit GUIDs
- CREATURE_CYCLONE = 22104,
- CREATURE_FATHOM_SPOREBAT = 22120,
- CREATURE_FATHOM_LURKER = 22119,
- CREATURE_SPITFIRE_TOTEM = 22091,
- CREATURE_EARTHBIND_TOTEM = 22486,
- CREATURE_POISON_CLEANSING_TOTEM = 22487,
-};
-
-//entry and position for Seer Olum
-#define SEER_OLUM 22820
-#define OLUM_X 446.78f
-#define OLUM_Y -542.76f
-#define OLUM_Z -7.54773f
-#define OLUM_O 0.401581f
+ SPELL_HEALING_WAVE = 38330,
-#define SAY_MISC "Alana be'lendor!" //don't know what use this
+ // Cyclone
+ SPELL_CYCLONE_WATER_VISUAL = 38497,
+ SPELL_CYCLONE_WATER_VISUAL_2 = 38464,
+ SPELL_CYCLONE_KNOCK_BACK = 38516,
+ SPELL_CYCLONE_KNOCK_BACK_2 = 38518,
+ SPELL_DREAM_FOG = 24780,
-#define MAX_ADVISORS 3
-//Fathom-Lord Karathress AI
-struct boss_fathomlord_karathress : public BossAI
-{
- boss_fathomlord_karathress(Creature* creature) : BossAI(creature, BOSS_FATHOM_LORD_KARATHRESS)
- {
- Initialize();
- }
+ // Misc
+ SPELL_POWER_OF_SHARKKIS = 38455,
+ SPELL_POWER_OF_TIDALVESS = 38452,
+ SPELL_POWER_OF_CARIBDIS = 38451,
- void Initialize()
- {
- CataclysmicBolt_Timer = 10000;
- Enrage_Timer = 600000; //10 minutes
- SearNova_Timer = 20000 + rand32() % 40000; // 20 - 60 seconds
+ SPELL_BLESSING_OF_THE_TIDES = 38449,
- BlessingOfTides = false;
- }
+ SPELL_TIDAL_SURGE_EFFECT = 38353,
+ SPELL_BESTIAL_WRATH = 38371
+};
- uint32 CataclysmicBolt_Timer;
- uint32 Enrage_Timer;
- uint32 SearNova_Timer;
+enum KarathressEvents
+{
+ // Karathress
+ EVENT_CATACLYSMIC_BOLT = 1,
+ EVENT_SEAR_NOVA,
+ EVENT_ENRAGE,
+
+ // Sharkkis
+ EVENT_LEECHING_THROW,
+ EVENT_THE_BEAST_WITHIN,
+ EVENT_MULTI_TOSS,
+ EVENT_SUMMON_FATHOM_PET,
+
+ // Tidalvess
+ EVENT_FROST_SHOCK,
+ EVENT_SPITFIRE_TOTEM,
+ EVENT_EARTHBIND_TOTEM,
+ EVENT_POISON_CLEANSING_TOTEM,
+
+ // Caribdis
+ EVENT_WATER_BOLT_VOLLEY,
+ EVENT_TIDAL_SURGE,
+ EVENT_SUMMON_CYCLONE,
+ EVENT_HEALING_WAVE
+};
- bool BlessingOfTides;
+enum KarathressMisc
+{
+ MAX_ADVISORS = 3
+};
- ObjectGuid Advisors[MAX_ADVISORS];
+// 21214 - Fathom-Lord Karathress
+struct boss_fathomlord_karathress : public BossAI
+{
+ boss_fathomlord_karathress(Creature* creature) : BossAI(creature, BOSS_FATHOM_LORD_KARATHRESS), _blessingOfTides(false) { }
void Reset() override
{
- Initialize();
+ _blessingOfTides = false;
- ObjectGuid RAdvisors[MAX_ADVISORS];
- RAdvisors[0] = instance->GetGuidData(DATA_SHARKKIS);
- RAdvisors[1] = instance->GetGuidData(DATA_TIDALVESS);
- RAdvisors[2] = instance->GetGuidData(DATA_CARIBDIS);
- // Respawn of the 3 Advisors
+ _advisors[0] = instance->GetGuidData(DATA_SHARKKIS);
+ _advisors[1] = instance->GetGuidData(DATA_TIDALVESS);
+ _advisors[2] = instance->GetGuidData(DATA_CARIBDIS);
+
+ // Respawn advisors
for (uint8 i = 0; i < MAX_ADVISORS; ++i)
- if (RAdvisors[i])
+ if (_advisors[i])
{
- Creature* advisor = ObjectAccessor::GetCreature(*me, RAdvisors[i]);
+ Creature* advisor = ObjectAccessor::GetCreature(*me, _advisors[i]);
if (advisor && !advisor->IsAlive())
- {
advisor->Respawn();
- advisor->AI()->EnterEvadeMode();
- advisor->GetMotionMaster()->MoveTargetedHome();
- }
}
_Reset();
}
- void EventSharkkisDeath()
- {
- Talk(SAY_GAIN_ABILITY1);
- DoCast(me, SPELL_POWER_OF_SHARKKIS);
- }
-
- void EventTidalvessDeath()
+ void JustEngagedWith(Unit* who) override
{
- Talk(SAY_GAIN_ABILITY2);
- DoCast(me, SPELL_POWER_OF_TIDALVESS);
- }
+ BossAI::JustEngagedWith(who);
+ Talk(SAY_AGGRO);
- void EventCaribdisDeath()
- {
- Talk(SAY_GAIN_ABILITY3);
- DoCast(me, SPELL_POWER_OF_CARIBDIS);
+ events.ScheduleEvent(EVENT_CATACLYSMIC_BOLT, 5s, 10s);
+ events.ScheduleEvent(EVENT_SEAR_NOVA, 40s, 50s);
+ events.ScheduleEvent(EVENT_ENRAGE, 10min);
}
- void GetAdvisors()
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
{
- Advisors[0] = instance->GetGuidData(DATA_SHARKKIS);
- Advisors[1] = instance->GetGuidData(DATA_TIDALVESS);
- Advisors[2] = instance->GetGuidData(DATA_CARIBDIS);
+ if (spellInfo->Id == SPELL_POWER_OF_SHARKKIS || spellInfo->Id == SPELL_POWER_OF_TIDALVESS || spellInfo->Id == SPELL_POWER_OF_CARIBDIS)
+ Talk(SAY_GAIN_ABILITY);
}
- void StartEvent(Unit* who)
+ void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
- GetAdvisors();
-
- Talk(SAY_AGGRO);
+ if (!_blessingOfTides && me->HealthBelowPctDamaged(75, damage))
+ {
+ // It is important to get them again because if we respawned advisors previously,
+ // they were created with new guids, so below will not work second time
+ // We do it here and not in Reset() because Reset() is called before respawn
+ _advisors[0] = instance->GetGuidData(DATA_SHARKKIS);
+ _advisors[1] = instance->GetGuidData(DATA_TIDALVESS);
+ _advisors[2] = instance->GetGuidData(DATA_CARIBDIS);
+
+ _blessingOfTides = true;
+ bool canPerformEmote = true;
+ for (ObjectGuid advisorGuid : _advisors)
+ {
+ Creature* advisor = ObjectAccessor::GetCreature(*me, advisorGuid);
+ if (advisor && advisor->IsAlive())
+ {
+ advisor->CastSpell(advisor, SPELL_BLESSING_OF_THE_TIDES, true);
- instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
+ if (canPerformEmote)
+ {
+ Talk(SAY_GAIN_BLESSING);
+ canPerformEmote = false;
+ }
+ }
+ }
+ }
}
void KilledUnit(Unit* /*victim*/) override
@@ -187,473 +199,317 @@ struct boss_fathomlord_karathress : public BossAI
void JustDied(Unit* /*killer*/) override
{
+ _JustDied();
Talk(SAY_DEATH);
-
- instance->SetData(DATA_FATHOMLORDKARATHRESSEVENT, DONE);
-
- //support for quest 10944
- me->SummonCreature(SEER_OLUM, OLUM_X, OLUM_Y, OLUM_Z, OLUM_O, TEMPSUMMON_TIMED_DESPAWN, 1h);
- }
-
- void JustEngagedWith(Unit* who) override
- {
- _JustEngagedWith(who);
- StartEvent(who);
}
void UpdateAI(uint32 diff) override
{
- //Only if not incombat check if the event is started
- if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
- {
- if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
- {
- AttackStart(target);
- GetAdvisors();
- }
- }
-
- //Return since we have no target
if (!UpdateVictim())
return;
- //someone evaded!
- if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
- {
- EnterEvadeMode();
+ events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
return;
- }
- //CataclysmicBolt_Timer
- if (CataclysmicBolt_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
{
- //select a random unit other than the main tank
- Unit* target = SelectTarget(SelectTargetMethod::Random, 1);
-
- //if there aren't other units, cast on the tank
- if (!target)
- target = me->GetVictim();
+ switch (eventId)
+ {
+ case EVENT_CATACLYSMIC_BOLT:
+ {
+ // Select a random unit other than the main tank
+ Unit* target = SelectTarget(SelectTargetMethod::Random, 1);
- if (target)
- DoCast(target, SPELL_CATACLYSMIC_BOLT);
- CataclysmicBolt_Timer = 10000;
- } else CataclysmicBolt_Timer -= diff;
+ // If there aren't other units, cast on the tank
+ if (!target)
+ target = me->GetVictim();
- //SearNova_Timer
- if (SearNova_Timer <= diff)
- {
- DoCastVictim(SPELL_SEAR_NOVA);
- SearNova_Timer = 20000 + rand32() % 40000;
- } else SearNova_Timer -= diff;
+ if (target)
+ DoCast(target, SPELL_CATACLYSMIC_BOLT);
- //Enrage_Timer
- if (Enrage_Timer <= diff)
- {
- DoCast(me, SPELL_ENRAGE);
- Enrage_Timer = 90000;
- } else Enrage_Timer -= diff;
-
- //Blessing of Tides Trigger
- if (!HealthAbovePct(75) && !BlessingOfTides)
- {
- BlessingOfTides = true;
- bool continueTriggering = false;
- for (uint8 i = 0; i < MAX_ADVISORS; ++i)
- if (Advisors[i])
- {
- Creature* advisor = ObjectAccessor::GetCreature(*me, Advisors[i]);
- if (advisor && advisor->IsAlive())
- {
- continueTriggering = true;
- break;
- }
+ events.Repeat(6s, 12s);
+ break;
}
- if (continueTriggering)
- {
- DoCast(me, SPELL_BLESSING_OF_THE_TIDES);
- Talk(SAY_GAIN_BLESSING);
+ case EVENT_SEAR_NOVA:
+ DoCastSelf(SPELL_SEAR_NOVA);
+ events.Repeat(30s, 40s);
+ break;
+ case EVENT_ENRAGE:
+ DoCastSelf(SPELL_ENRAGE);
+ events.Repeat(90s);
+ break;
+ default:
+ break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
DoMeleeAttackIfReady();
}
+
+private:
+ bool _blessingOfTides;
+ ObjectGuid _advisors[MAX_ADVISORS];
};
-//Fathom-Guard Sharkkis AI
+// 21966 - Fathom-Guard Sharkkis
struct boss_fathomguard_sharkkis : public ScriptedAI
{
- boss_fathomguard_sharkkis(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- LeechingThrow_Timer = 20000;
- TheBeastWithin_Timer = 30000;
- Multishot_Timer = 15000;
- Pet_Timer = 10000;
-
- pet = false;
- }
-
- InstanceScript* instance;
-
- uint32 LeechingThrow_Timer;
- uint32 TheBeastWithin_Timer;
- uint32 Multishot_Timer;
- uint32 Pet_Timer;
-
- bool pet;
-
- ObjectGuid SummonedPet;
+ boss_fathomguard_sharkkis(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
- Initialize();
-
- Creature* Pet = ObjectAccessor::GetCreature(*me, SummonedPet);
- if (Pet && Pet->IsAlive())
- Pet->KillSelf();
-
- SummonedPet.Clear();
-
- instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, NOT_STARTED);
+ _events.Reset();
}
- void JustDied(Unit* /*killer*/) override
+ void JustEngagedWith(Unit* /*who*/) override
{
- if (Creature* Karathress = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KARATHRESS)))
- ENSURE_AI(boss_fathomlord_karathress, Karathress->AI())->EventSharkkisDeath();
+ _events.ScheduleEvent(EVENT_LEECHING_THROW, 5s, 10s);
+ _events.ScheduleEvent(EVENT_THE_BEAST_WITHIN, 30s, 40s);
+ _events.ScheduleEvent(EVENT_MULTI_TOSS, 20s, 30s);
+ _events.ScheduleEvent(EVENT_SUMMON_FATHOM_PET, 10s);
}
- void JustEngagedWith(Unit* who) override
+ void JustDied(Unit* /*killer*/) override
{
- instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
- instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, IN_PROGRESS);
+ DoCastSelf(SPELL_POWER_OF_SHARKKIS);
}
void UpdateAI(uint32 diff) override
{
- //Only if not incombat check if the event is started
- if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
- {
- if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
- AttackStart(target);
- }
-
- //Return since we have no target
if (!UpdateVictim())
return;
- //someone evaded!
- if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
- {
- EnterEvadeMode();
- return;
- }
-
- //LeechingThrow_Timer
- if (LeechingThrow_Timer <= diff)
- {
- DoCastVictim(SPELL_LEECHING_THROW);
- LeechingThrow_Timer = 20000;
- } else LeechingThrow_Timer -= diff;
+ _events.Update(diff);
- //Multishot_Timer
- if (Multishot_Timer <= diff)
- {
- DoCastVictim(SPELL_MULTISHOT);
- Multishot_Timer = 20000;
- } else Multishot_Timer -= diff;
-
- //TheBeastWithin_Timer
- if (TheBeastWithin_Timer <= diff)
- {
- DoCast(me, SPELL_THE_BEAST_WITHIN);
-
- Creature* Pet = ObjectAccessor::GetCreature(*me, SummonedPet);
- if (Pet && Pet->IsAlive())
- Pet->CastSpell(Pet, SPELL_PET_ENRAGE, true);
-
- TheBeastWithin_Timer = 30000;
- } else TheBeastWithin_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //Pet_Timer
- if (Pet_Timer < diff && pet == false)
+ while (uint32 eventId = _events.ExecuteEvent())
{
- pet = true;
- //uint32 spell_id;
- uint32 pet_id;
- if (!urand(0, 1))
+ switch (eventId)
{
- //spell_id = SPELL_SUMMON_FATHOM_LURKER;
- pet_id = CREATURE_FATHOM_LURKER;
+ case EVENT_LEECHING_THROW:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
+ DoCast(target, SPELL_LEECHING_THROW);
+ _events.Repeat(5s, 10s);
+ break;
+ case EVENT_THE_BEAST_WITHIN:
+ DoCastSelf(SPELL_THE_BEAST_WITHIN);
+ _events.Repeat(30s, 40s);
+ break;
+ case EVENT_MULTI_TOSS:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
+ DoCast(target, SPELL_MULTI_TOSS);
+ _events.Repeat(20s, 30s);
+ break;
+ case EVENT_SUMMON_FATHOM_PET:
+ DoCastSelf(RAND(SPELL_SUMMON_FATHOM_LURKER, SPELL_SUMMON_FATHOM_SPOREBAT));
+ _events.Repeat(30s);
+ break;
+ default:
+ break;
}
- else
- {
- //spell_id = SPELL_SUMMON_FATHOM_SPOREBAT;
- pet_id = CREATURE_FATHOM_SPOREBAT;
- }
- //DoCast(me, spell_id, true);
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- {
- if (Creature* Pet = DoSpawnCreature(pet_id, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15s))
- {
- Pet->AI()->AttackStart(target);
- SummonedPet = Pet->GetGUID();
- }
- }
- } else Pet_Timer -= diff;
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
DoMeleeAttackIfReady();
}
+
+private:
+ EventMap _events;
};
-//Fathom-Guard Tidalvess AI
+// 21965 - Fathom-Guard Tidalvess
struct boss_fathomguard_tidalvess : public ScriptedAI
{
- boss_fathomguard_tidalvess(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- FrostShock_Timer = 25000;
- Spitfire_Timer = 60000;
- PoisonCleansing_Timer = 30000;
- Earthbind_Timer = 45000;
- }
-
- InstanceScript* instance;
-
- uint32 FrostShock_Timer;
- uint32 Spitfire_Timer;
- uint32 PoisonCleansing_Timer;
- uint32 Earthbind_Timer;
+ boss_fathomguard_tidalvess(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
- Initialize();
-
- instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, NOT_STARTED);
+ _events.Reset();
+ DoCastSelf(SPELL_WINDFURY);
}
- void JustDied(Unit* /*killer*/) override
+ void JustEngagedWith(Unit* /*who*/) override
{
- if (Creature* Karathress = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KARATHRESS)))
- ENSURE_AI(boss_fathomlord_karathress, Karathress->AI())->EventTidalvessDeath();
+ _events.ScheduleEvent(EVENT_FROST_SHOCK, 10s, 15s);
+ _events.ScheduleEvent(EVENT_SPITFIRE_TOTEM, 10s, 15s);
+ _events.ScheduleEvent(EVENT_EARTHBIND_TOTEM, 15s, 25s);
+ _events.ScheduleEvent(EVENT_POISON_CLEANSING_TOTEM, 20s, 30s);
}
- void JustEngagedWith(Unit* who) override
+ void JustDied(Unit* /*killer*/) override
{
- instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
- instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, IN_PROGRESS);
- DoCast(me, SPELL_WINDFURY_WEAPON);
+ DoCastSelf(SPELL_POWER_OF_TIDALVESS);
}
void UpdateAI(uint32 diff) override
{
- //Only if not incombat check if the event is started
- if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
- {
- if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
- AttackStart(target);
- }
-
- //Return since we have no target
if (!UpdateVictim())
return;
- //someone evaded!
- if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
- {
- EnterEvadeMode();
- return;
- }
-
- if (!me->HasAura(SPELL_WINDFURY_WEAPON))
- {
- DoCast(me, SPELL_WINDFURY_WEAPON);
- }
-
- //FrostShock_Timer
- if (FrostShock_Timer <= diff)
- {
- DoCastVictim(SPELL_FROST_SHOCK);
- FrostShock_Timer = 25000 + rand32() % 5000;
- } else FrostShock_Timer -= diff;
-
- //Spitfire_Timer
- if (Spitfire_Timer <= diff)
- {
- DoCast(me, SPELL_SPITFIRE_TOTEM);
- if (Unit* SpitfireTotem = me->FindNearestCreature(CREATURE_SPITFIRE_TOTEM, 100.0f))
- SpitfireTotem->ToCreature()->AI()->AttackStart(me->GetVictim());
+ _events.Update(diff);
- Spitfire_Timer = 60000;
- }
- else
- Spitfire_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //PoisonCleansing_Timer
- if (PoisonCleansing_Timer <= diff)
+ while (uint32 eventId = _events.ExecuteEvent())
{
- DoCast(me, SPELL_POISON_CLEANSING_TOTEM);
- PoisonCleansing_Timer = 30000;
- }
- else
- PoisonCleansing_Timer -= diff;
+ switch (eventId)
+ {
+ case EVENT_FROST_SHOCK:
+ DoCastVictim(SPELL_FROST_SHOCK);
+ _events.Repeat(10s, 15s);
+ break;
+ case EVENT_SPITFIRE_TOTEM:
+ DoCastSelf(SPELL_SPITFIRE_TOTEM);
+ _events.Repeat(40s, 60s);
+ break;
+ case EVENT_EARTHBIND_TOTEM:
+ DoCastSelf(SPELL_EARTHBIND_TOTEM);
+ _events.Repeat(40s, 60s);
+ break;
+ case EVENT_POISON_CLEANSING_TOTEM:
+ DoCastSelf(SPELL_POISON_CLEANSING_TOTEM);
+ _events.Repeat(30s, 50s);
+ break;
+ default:
+ break;
+ }
- //Earthbind_Timer
- if (Earthbind_Timer <= diff)
- {
- DoCast(me, SPELL_EARTHBIND_TOTEM);
- Earthbind_Timer = 45000;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
- else
- Earthbind_Timer -= diff;
DoMeleeAttackIfReady();
}
+
+private:
+ EventMap _events;
};
-//Fathom-Guard Caribdis AI
+// 21964 - Fathom-Guard Caribdis
struct boss_fathomguard_caribdis : public ScriptedAI
{
- boss_fathomguard_caribdis(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- WaterBoltVolley_Timer = 35000;
- TidalSurge_Timer = 15000 + rand32() % 5000;
- Heal_Timer = 55000;
- Cyclone_Timer = 30000 + rand32() % 10000;
- }
-
- InstanceScript* instance;
-
- uint32 WaterBoltVolley_Timer;
- uint32 TidalSurge_Timer;
- uint32 Heal_Timer;
- uint32 Cyclone_Timer;
+ boss_fathomguard_caribdis(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
- Initialize();
-
- instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, NOT_STARTED);
+ _events.Reset();
}
- void JustDied(Unit* /*killer*/) override
+ void JustEngagedWith(Unit* /*who*/) override
{
- if (Creature* Karathress = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KARATHRESS)))
- ENSURE_AI(boss_fathomlord_karathress, Karathress->AI())->EventCaribdisDeath();
+ _events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, 15s, 25s);
+ _events.ScheduleEvent(EVENT_TIDAL_SURGE, 15s, 20s);
+ _events.ScheduleEvent(EVENT_SUMMON_CYCLONE, 10s, 25s);
+ _events.ScheduleEvent(EVENT_HEALING_WAVE, 15s, 25s);
}
- void JustEngagedWith(Unit* who) override
+ void JustDied(Unit* /*killer*/) override
{
- instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
- instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, IN_PROGRESS);
+ DoCastSelf(SPELL_POWER_OF_CARIBDIS);
}
void UpdateAI(uint32 diff) override
{
- //Only if not incombat check if the event is started
- if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
- {
- if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
- AttackStart(target);
- }
-
- //Return since we have no target
if (!UpdateVictim())
return;
- //someone evaded!
- if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
- {
- EnterEvadeMode();
- return;
- }
-
- //WaterBoltVolley_Timer
- if (WaterBoltVolley_Timer <= diff)
- {
- DoCastVictim(SPELL_WATER_BOLT_VOLLEY);
- WaterBoltVolley_Timer = 30000;
- } else WaterBoltVolley_Timer -= diff;
+ _events.Update(diff);
- //TidalSurge_Timer
- if (TidalSurge_Timer <= diff)
- {
- DoCastSelf(SPELL_TIDAL_SURGE);
- TidalSurge_Timer = 15000 + rand32() % 5000;
- } else TidalSurge_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //Cyclone_Timer
- if (Cyclone_Timer <= diff)
+ while (uint32 eventId = _events.ExecuteEvent())
{
- //DoCast(me, SPELL_SUMMON_CYCLONE); // Doesn't work
- Cyclone_Timer = 30000 + rand32() % 10000;
-
- if (Creature* Cyclone = me->SummonCreature(CREATURE_CYCLONE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), float(rand32() % 5), TEMPSUMMON_TIMED_DESPAWN, 15s))
+ switch (eventId)
{
- Cyclone->SetObjectScale(3.0f);
- Cyclone->SetFaction(me->GetFaction());
- Cyclone->CastSpell(Cyclone, SPELL_CYCLONE_CYCLONE, true);
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- Cyclone->AI()->AttackStart(target);
+ case EVENT_WATER_BOLT_VOLLEY:
+ DoCastSelf(SPELL_WATER_BOLT_VOLLEY);
+ _events.Repeat(10s, 30s);
+ break;
+ case EVENT_TIDAL_SURGE:
+ DoCastSelf(SPELL_TIDAL_SURGE);
+ _events.Repeat(15s, 25s);
+ break;
+ case EVENT_SUMMON_CYCLONE:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
+ DoCast(target, SPELL_SUMMON_CYCLONE);
+ _events.Repeat(40s, 50s);
+ break;
+ case EVENT_HEALING_WAVE:
+ if (Unit* target = DoSelectLowestHpFriendly(250.0f))
+ DoCast(target, SPELL_HEALING_WAVE);
+ _events.Repeat(15s, 20s);
+ break;
+ default:
+ break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
- else
- Cyclone_Timer -= diff;
- //Heal_Timer
- if (Heal_Timer <= diff)
- {
- // It can be cast on any of the mobs
- Unit* unit = nullptr;
+ DoMeleeAttackIfReady();
+ }
- while (unit == nullptr || !unit->IsAlive())
- unit = selectAdvisorUnit();
+private:
+ EventMap _events;
+};
- DoCast(unit, SPELL_HEAL);
- Heal_Timer = 60000;
- }
- else
- Heal_Timer -= diff;
+// 22104 - Cyclone (Karathress)
+struct npc_fathomlord_karathress_cyclone : public ScriptedAI
+{
+ npc_fathomlord_karathress_cyclone(Creature* creature) : ScriptedAI(creature) { }
- DoMeleeAttackIfReady();
+ void Reset() override
+ {
+ _scheduler.CancelAll();
}
- Unit* selectAdvisorUnit()
+ void JustAppeared() override
{
- Unit* unit = nullptr;
- switch (rand32() % 4)
+ DoCastSelf(SPELL_CYCLONE_WATER_VISUAL);
+ DoCastSelf(SPELL_CYCLONE_WATER_VISUAL_2);
+ DoCastSelf(SPELL_CYCLONE_KNOCK_BACK);
+ /// @todo: This requires rechecks. Seems like on retail this aura does not trigger spell despite
+ /// this aura is shown in sniffs in aura update packet. Also using this makes cyclone overpowered and creates behavior we don't see in movies
+ // DoCastSelf(SPELL_CYCLONE_KNOCK_BACK_2);
+ DoCastSelf(SPELL_DREAM_FOG);
+
+ /// @todo: This requires additional research
+ _scheduler.Schedule(10s, [this](TaskContext task)
{
- case 0:
- unit = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESS));
- break;
- case 1:
- unit = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_SHARKKIS));
- break;
- case 2:
- unit = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_TIDALVESS));
- break;
- case 3:
- unit = me;
- break;
- }
- return unit;
+ DoZoneInCombat();
+
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
+ {
+ ResetThreatList();
+ AddThreat(target, 1000000.0f);
+ /// @todo: We are forcing creature to chase target, otherwise it will not chase players. Investigate this
+ me->GetMotionMaster()->MoveChase(target);
+ }
+ task.Repeat(10s);
+ });
}
+
+ void UpdateAI(uint32 diff) override
+ {
+ UpdateVictim();
+
+ _scheduler.Update(diff);
+ }
+
+private:
+ TaskScheduler _scheduler;
};
// 38358 - Tidal Surge
@@ -677,11 +533,34 @@ class spell_fathomlord_karathress_tidal_surge : public SpellScript
}
};
+// 38373 - The Beast Within
+class spell_fathomlord_karathress_the_beast_within : public SpellScript
+{
+ PrepareSpellScript(spell_fathomlord_karathress_the_beast_within);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_BESTIAL_WRATH });
+ }
+
+ void HandleCast()
+ {
+ GetCaster()->CastSpell(GetCaster(), SPELL_BESTIAL_WRATH);
+ }
+
+ void Register() override
+ {
+ OnCast += SpellCastFn(spell_fathomlord_karathress_the_beast_within::HandleCast);
+ }
+};
+
void AddSC_boss_fathomlord_karathress()
{
RegisterSerpentshrineCavernCreatureAI(boss_fathomlord_karathress);
RegisterSerpentshrineCavernCreatureAI(boss_fathomguard_sharkkis);
RegisterSerpentshrineCavernCreatureAI(boss_fathomguard_tidalvess);
RegisterSerpentshrineCavernCreatureAI(boss_fathomguard_caribdis);
+ RegisterSerpentshrineCavernCreatureAI(npc_fathomlord_karathress_cyclone);
RegisterSpellScript(spell_fathomlord_karathress_tidal_surge);
+ RegisterSpellScript(spell_fathomlord_karathress_the_beast_within);
}
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp
index 03ec873ed5c..538b394e9a2 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp
@@ -232,8 +232,6 @@ class instance_serpent_shrine : public InstanceMapScript
void SetGuidData(uint32 type, ObjectGuid data) override
{
- if (type == DATA_KARATHRESSEVENT_STARTER)
- KarathressEvent_Starter = data;
if (type == DATA_LEOTHERAS_EVENT_STARTER)
LeotherasEventStarter = data;
}
@@ -254,8 +252,6 @@ class instance_serpent_shrine : public InstanceMapScript
return LadyVashj;
case DATA_KARATHRESS:
return Karathress;
- case DATA_KARATHRESSEVENT_STARTER:
- return KarathressEvent_Starter;
case DATA_LEOTHERAS:
return LeotherasTheBlind;
case DATA_LEOTHERAS_EVENT_STARTER:
@@ -366,7 +362,6 @@ class instance_serpent_shrine : public InstanceMapScript
ObjectGuid Caribdis;
ObjectGuid LadyVashj;
ObjectGuid Karathress;
- ObjectGuid KarathressEvent_Starter;
ObjectGuid LeotherasTheBlind;
ObjectGuid LeotherasEventStarter;
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/serpent_shrine.h b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/serpent_shrine.h
index 2228a36d8c8..d4cf156f493 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/serpent_shrine.h
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/serpent_shrine.h
@@ -45,7 +45,6 @@ enum SSDataTypes
DATA_CANSTARTPHASE3 = 1,
DATA_CARIBDIS = 2,
DATA_KARATHRESS = 3,
- DATA_KARATHRESSEVENT_STARTER = 4,
DATA_LADYVASHJ = 5,
DATA_SHARKKIS = 6,
DATA_SHIELDGENERATOR1 = 7,
@@ -54,7 +53,6 @@ enum SSDataTypes
DATA_SHIELDGENERATOR4 = 10,
DATA_THELURKERBELOW = 11,
DATA_TIDALVESS = 12,
- DATA_FATHOMLORDKARATHRESSEVENT = 13,
DATA_LEOTHERAS = 14,
DATA_LEOTHERAS_EVENT_STARTER = 15,
DATA_CONTROL_CONSOLE = 16,