diff options
-rw-r--r-- | sql/updates/world/3.3.5/2019_06_27_03_world.sql | 7 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 1 | ||||
-rw-r--r-- | src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp | 706 |
3 files changed, 375 insertions, 339 deletions
diff --git a/sql/updates/world/3.3.5/2019_06_27_03_world.sql b/sql/updates/world/3.3.5/2019_06_27_03_world.sql new file mode 100644 index 00000000000..61d250858d0 --- /dev/null +++ b/sql/updates/world/3.3.5/2019_06_27_03_world.sql @@ -0,0 +1,7 @@ +DELETE FROM `creature_text` WHERE `CreatureID` IN (14509, 11347, 11348) AND `GroupId`=2; +DELETE FROM `creature_text` WHERE `CreatureID`=14509 AND `GroupId`=3; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(14509, 2, 0, "%s dies.", 16, 0, 100, 0, 0, 0, 12195, 0, "High Priest Thekal"), +(14509, 3, 0, "%s goes into a frenzy!", 16, 0, 100, 0, 0, 0, 1191, 0, "High Priest Thekal"), +(11347, 2, 0, "%s dies.", 16, 0, 100, 0, 0, 0, 10453, 0, "Zealoth Lor'khan"), +(11348, 2, 0, "%s dies.", 16, 0, 100, 0, 0, 0, 12195, 0, "Zealot Zath"); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 910fbcfc557..b8c3d17b1e1 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1573,7 +1573,6 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a case 2584: // Waiting to Resurrect case 22011: // Spirit Heal Channel case 22012: // Spirit Heal - case 24171: // Resurrection Impact Visual case 42792: // Recently Dropped Flag case 43681: // Inactive case 44535: // Spirit Heal (mana) diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp index 3d839e7bd0f..4474c7e7a45 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp @@ -17,52 +17,66 @@ */ #include "zulgurub.h" +#include "CellImpl.h" +#include "GridNotifiersImpl.h" #include "InstanceScript.h" +#include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" #include "ScriptMgr.h" enum Says { - SAY_AGGRO = 0, - SAY_DEATH = 1 + TALK_TIGER_PHASE = 0, + TALK_DEATH = 1, + TALK_FAKE_DEATH = 2, + TALK_FRENZY = 3 }; enum Spells { - SPELL_MORTALCLEAVE = 22859, // Phase 1 - SPELL_SILENCE = 22666, // Phase 1 - SPELL_TIGER_FORM = 24169, // Phase 1 - SPELL_RESURRECT = 24173, // Phase 1 // Not used in script. - SPELL_FRENZY = 8269, // Phase 2 - SPELL_FORCEPUNCH = 24189, // Phase 2 - SPELL_CHARGE = 24193, // Phase 2 - SPELL_ENRAGE = 8269, // Phase 2 - SPELL_SUMMONTIGERS = 24183, // Phase 2 - // Zealot Lor'Khan Spells + SPELL_RESURRECT = 24173, // ToDo: find out how this should be used + SPELL_RESURRECT_VISUAL = 24171, + + // High Priest Thekal + // Phase 1 + SPELL_MORTALCLEAVE = 22859, + SPELL_SILENCE = 22666, + // Phase 2 + SPELL_TIGER_FORM = 24169, + SPELL_FRENZY = 8269, + SPELL_FORCEPUNCH = 24189, + SPELL_CHARGE = 24193, + SPELL_SUMMONTIGERS = 24183, + + // Zealot Lor'Khan SPELL_SHIELD = 20545, SPELL_BLOODLUST = 24185, SPELL_GREATERHEAL = 24208, SPELL_DISARM = 6713, - // Zealot Zath Spells + + // Zealot Zath SPELL_SWEEPINGSTRIKES = 18765, SPELL_SINISTERSTRIKE = 15581, SPELL_GOUGE = 12540, SPELL_KICK = 15614, - SPELL_BLIND = 21060 + SPELL_BLIND = 21060, + + SPELL_PERMANENT_FEIGN_DEATH = 29266 }; -enum Events +enum ThekalEvents { - EVENT_MORTALCLEAVE = 1, // Phase 1 - EVENT_SILENCE = 2, // Phase 1 - EVENT_CHECK_TIMER = 3, // Phase 1 - EVENT_RESURRECT_TIMER = 4, // Phase 1 - EVENT_FRENZY = 5, // Phase 2 - EVENT_FORCEPUNCH = 6, // Phase 2 - EVENT_SPELL_CHARGE = 7, // Phase 2 - EVENT_ENRAGE = 8, // Phase 2 - EVENT_SUMMONTIGERS = 9 // Phase 2 + EVENT_MORTALCLEAVE = 1, + EVENT_SILENCE, + EVENT_RESURRECT_TIMER, + EVENT_CHANGE_PHASE_1, + EVENT_CHANGE_PHASE_2, + EVENT_CHANGE_PHASE_3, + + EVENT_FORCEPUNCH, + EVENT_SPELL_CHARGE, + EVENT_SUMMONTIGERS }; enum Phases @@ -71,10 +85,13 @@ enum Phases PHASE_TWO = 2 }; -// AWFUL HACK WARNING -// To whoever reads this: Zul'Gurub needs your love -// Need to do this calculation to increase/decrease Thekal's damage by 40% (probably some aura missing) -// This is only to compile the scripts after the aura calculation revamp +// Resurrection is handled by the main boss' script, dispatching resurrection as needed +enum Data +{ + DATA_FAKE_DEATH = 1, + DATA_RESURRECTED +}; + float const DamageIncrease = 40.0f; float const DamageDecrease = 100.f / (1.f + DamageIncrease / 100.f) - 100.f; @@ -93,43 +110,134 @@ class boss_thekal : public CreatureScript void Initialize() { _enraged = false; - _wasDead = false; + _isThekalDead = false; + _isLorkhanDead = false; + _isZathDead = false; + _isResurrectTimerActive = false; + _isChangingPhase = false; + } + + void EnterEvadeMode(EvadeReason why) override + { + if (!_EnterEvadeMode(why)) + return; + me->AddUnitState(UNIT_STATE_EVADE); + me->GetMotionMaster()->MoveTargetedHome(); + Reset(); } void Reset() override { if (events.IsInPhase(PHASE_TWO)) - me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease); // hack + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease); + me->SetControlled(false, UNIT_STATE_ROOT); + events.Reset(); _Reset(); Initialize(); + me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); } void JustDied(Unit* /*killer*/) override { _JustDied(); - Talk(SAY_DEATH); + Talk(TALK_DEATH); + + // Adds are still feign-deathing, so kill them when the encounter is over + if (Creature* creature = instance->GetCreature(DATA_LORKHAN)) + creature->KillSelf(); + if (Creature* creature = instance->GetCreature(DATA_ZATH)) + creature->KillSelf(); + instance->SetBossState(DATA_LORKHAN, DONE); + instance->SetBossState(DATA_ZATH, DONE); } void JustEngagedWith(Unit* /*who*/) override { _JustEngagedWith(); - events.ScheduleEvent(EVENT_MORTALCLEAVE, 4s, 0, PHASE_ONE); // Phase 1 - events.ScheduleEvent(EVENT_SILENCE, 9s, 0, PHASE_ONE); // Phase 1 - events.ScheduleEvent(EVENT_CHECK_TIMER, 10s, 0, PHASE_ONE); // Phase 1 - events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10s, 0, PHASE_ONE); // Phase 1 - Talk(SAY_AGGRO); + events.SetPhase(PHASE_ONE); + events.ScheduleEvent(EVENT_MORTALCLEAVE, 4s, 0, PHASE_ONE); + events.ScheduleEvent(EVENT_SILENCE, 9s, 0, PHASE_ONE); } - void JustReachedHome() override + void SetData(uint32 type, uint32 data) override { - instance->SetBossState(DATA_THEKAL, NOT_STARTED); + if (type == DATA_FAKE_DEATH) + { + // A mob died + switch (data) + { + case NPC_HIGH_PRIEST_THEKAL: + _isThekalDead = true; + break; + case NPC_ZEALOT_LORKHAN: + _isLorkhanDead = true; + break; + case NPC_ZEALOT_ZATH: + _isZathDead = true; + break; + } + + if (_isThekalDead && _isLorkhanDead && _isZathDead) + { + _isResurrectTimerActive = false; + events.Reset(); + events.ScheduleEvent(EVENT_CHANGE_PHASE_1, 3s); + } + else + { + // Start resurrection timer if not already started, otherwise ignore + if (!_isResurrectTimerActive) + { + events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10s); + _isResurrectTimerActive = true; + } + } + } + else if (type == DATA_RESURRECTED) + { + Creature* creature = nullptr; + if (data == NPC_HIGH_PRIEST_THEKAL) + creature = me; + else if (data == NPC_ZEALOT_LORKHAN) + creature = instance->GetCreature(DATA_LORKHAN); + else if (data == NPC_ZEALOT_ZATH) + creature = instance->GetCreature(DATA_ZATH); + + // Resurrect + if (creature) + { + creature->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + creature->SetFullHealth(); + creature->SetImmuneToPC(false, true); + creature->SetImmuneToNPC(false, true); + } + } } - void UpdateAI(uint32 diff) override + void DamageTaken(Unit* /*attacker*/, uint32& damage) override { - if (!UpdateVictim()) - return; + if (damage >= me->GetHealth() && events.IsInPhase(PHASE_ONE)) + { + Talk(TALK_FAKE_DEATH); + me->RemoveAllAuras(); + me->SetImmuneToPC(true, true); + me->SetImmuneToNPC(true, true); + DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true); + events.DelayEvents(10s); + SetData(DATA_FAKE_DEATH, me->GetEntry()); + damage = 0; + } + else if (events.IsInPhase(PHASE_TWO) && !_enraged && me->HealthBelowPct(10)) + { + DoCastSelf(SPELL_FRENZY); + Talk(TALK_FRENZY); + _enraged = true; + } + } + void UpdateAI(uint32 diff) override + { events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) @@ -140,74 +248,72 @@ class boss_thekal : public CreatureScript switch (eventId) { case EVENT_MORTALCLEAVE: - DoCastVictim(SPELL_MORTALCLEAVE, true); + DoCastVictim(SPELL_MORTALCLEAVE); events.ScheduleEvent(EVENT_MORTALCLEAVE, 15s, 20s, 0, PHASE_ONE); break; case EVENT_SILENCE: - DoCastVictim(SPELL_SILENCE, true); + DoCastVictim(SPELL_SILENCE); events.ScheduleEvent(EVENT_SILENCE, 20s, 25s, 0, PHASE_ONE); break; case EVENT_RESURRECT_TIMER: - // Thekal will transform to Tiger if he died and was not resurrected after 10 seconds. - if (_wasDead) + { + // If only one or two of the three mobs were killed, resurrect them + _isResurrectTimerActive = false; + + if (_isThekalDead) { - DoCast(me, SPELL_TIGER_FORM); // SPELL_AURA_TRANSFORM - me->SetObjectScale(2.00f); - me->SetStandState(UNIT_STAND_STATE_STAND); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - /* - CreatureTemplate const* cinfo = me->GetCreatureTemplate(); - me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 40))); - me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 40))); - me->UpdateDamagePhysical(BASE_ATTACK); - */ - me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageIncrease); // hack - ResetThreatList(); - events.ScheduleEvent(EVENT_FRENZY, 30s, 0, PHASE_TWO); // Phase 2 - events.ScheduleEvent(EVENT_FORCEPUNCH, 4s, 0, PHASE_TWO); // Phase 2 - events.ScheduleEvent(EVENT_SPELL_CHARGE, 12s, 0, PHASE_TWO); // Phase 2 - events.ScheduleEvent(EVENT_ENRAGE, 32s, 0, PHASE_TWO); // Phase 2 - events.ScheduleEvent(EVENT_SUMMONTIGERS, 25s, 0, PHASE_TWO); // Phase 2 - events.SetPhase(PHASE_TWO); + DoCastSelf(SPELL_RESURRECT_VISUAL); + SetData(DATA_RESURRECTED, me->GetEntry()); + _isThekalDead = false; } - events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10s, 0, PHASE_ONE); - break; - case EVENT_CHECK_TIMER: - // Check_Timer for the death of LorKhan and Zath. - if (!_wasDead) + + if (_isLorkhanDead) { - if (instance->GetBossState(DATA_LORKHAN) == SPECIAL) + if (Creature* lorkhan = instance->GetCreature(DATA_LORKHAN)) { - // Resurrect LorKhan - if (Creature* lorKhan = instance->GetCreature(DATA_LORKHAN)) - { - lorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - lorKhan->SetFaction(FACTION_MONSTER); - lorKhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - lorKhan->SetFullHealth(); - instance->SetData(DATA_LORKHAN, DONE); - } + lorkhan->AI()->DoCastSelf(SPELL_RESURRECT_VISUAL); + SetData(DATA_RESURRECTED, lorkhan->GetEntry()); } + _isLorkhanDead = false; + } - if (instance->GetBossState(DATA_ZATH) == SPECIAL) + if (_isZathDead) + { + if (Creature* zath = instance->GetCreature(DATA_ZATH)) { - // Resurrect Zath - if (Creature* zath = instance->GetCreature(DATA_ZATH)) - { - zath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - zath->SetFaction(FACTION_MONSTER); - zath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - zath->SetFullHealth(); - instance->SetBossState(DATA_ZATH, DONE); - } + zath->AI()->DoCastSelf(SPELL_RESURRECT_VISUAL); + SetData(DATA_RESURRECTED, zath->GetEntry()); } + _isZathDead = false; } - events.ScheduleEvent(EVENT_CHECK_TIMER, 5s, 0, PHASE_ONE); break; - case EVENT_FRENZY: - DoCast(me, SPELL_FRENZY); - events.ScheduleEvent(EVENT_FRENZY, 30s, 0, PHASE_TWO); + } + case EVENT_CHANGE_PHASE_1: + _isChangingPhase = true; + me->SetControlled(true, UNIT_STATE_ROOT); + me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + me->SetFullHealth(); + DoCastSelf(SPELL_RESURRECT_VISUAL); + events.ScheduleEvent(EVENT_CHANGE_PHASE_2, 1s); + break; + case EVENT_CHANGE_PHASE_2: + Talk(TALK_TIGER_PHASE); + events.ScheduleEvent(EVENT_CHANGE_PHASE_3, 1s); break; + case EVENT_CHANGE_PHASE_3: + { + // Trigger phase change + _isChangingPhase = false; + DoCastSelf(SPELL_TIGER_FORM); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageIncrease); + ResetThreatList(); + me->SetControlled(false, UNIT_STATE_ROOT); + events.ScheduleEvent(EVENT_FORCEPUNCH, 4s, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_SPELL_CHARGE, 12s, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_SUMMONTIGERS, 25s, 0, PHASE_TWO); + events.SetPhase(PHASE_TWO); + } case EVENT_FORCEPUNCH: DoCastVictim(SPELL_FORCEPUNCH, true); events.ScheduleEvent(EVENT_FORCEPUNCH, 16s, 21s, 0, PHASE_TWO); @@ -221,14 +327,6 @@ class boss_thekal : public CreatureScript } events.ScheduleEvent(EVENT_CHARGE, 15s, 22s, 0, PHASE_TWO); break; - case EVENT_ENRAGE: - if (HealthBelowPct(11) && !_enraged) - { - DoCast(me, SPELL_ENRAGE); - _enraged = true; - } - events.ScheduleEvent(EVENT_ENRAGE, 30s); - break; case EVENT_SUMMONTIGERS: DoCastVictim(SPELL_SUMMONTIGERS, true); events.ScheduleEvent(EVENT_SUMMONTIGERS, 10s, 14s, 0, PHASE_TWO); @@ -236,32 +334,24 @@ class boss_thekal : public CreatureScript default: break; } + } - if (me->IsFullHealth() && _wasDead) - _wasDead = false; - - if ((events.IsInPhase(PHASE_ONE)) && !_wasDead && !HealthAbovePct(5)) - { - me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); - me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetStandState(UNIT_STAND_STATE_SLEEP); - me->AttackStop(); - instance->SetBossState(DATA_THEKAL, SPECIAL); - _wasDead = true; - } + if (_isChangingPhase) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } + if (!UpdateVictim()) + return; DoMeleeAttackIfReady(); } private: bool _enraged; - bool _wasDead; + bool _isThekalDead; + bool _isLorkhanDead; + bool _isZathDead; + bool _isResurrectTimerActive; + bool _isChangingPhase; }; CreatureAI* GetAI(Creature* creature) const override @@ -270,147 +360,135 @@ class boss_thekal : public CreatureScript } }; +enum LorkhanEvents +{ + EVENT_SHIELD = 1, + EVENT_BLOODLUST, + EVENT_GREATER_HEAL, + EVENT_DISARM +}; + +// Find which one of the three creatures Lor'Khan should heal (self, Thekal or Zath) +class LorKhanSelectTargetToHeal +{ + public: + LorKhanSelectTargetToHeal(Unit const* obj, float range) : i_obj(obj), i_range(range), i_hp(0) { } + + bool operator()(Unit* u) + { + if (u->GetTypeId() != TYPEID_UNIT || !u->IsAlive() || !u->IsInCombat()) + return false; + + if (u->ToCreature()->GetEntry() != NPC_HIGH_PRIEST_THEKAL && u->GetEntry() != NPC_ZEALOT_LORKHAN && u->GetEntry() != NPC_ZEALOT_ZATH) + return false; + + // Don't allow to heal a target that is waiting for resurrection + if (u->HasAura(SPELL_PERMANENT_FEIGN_DEATH)) + return false; + + if ((u->GetMaxHealth() - u->GetHealth() > i_hp) && i_obj->IsWithinDistInMap(u, i_range)) + { + i_hp = u->GetMaxHealth() - u->GetHealth(); + return true; + } + + return false; + } + + private: + Unit const* i_obj; + float i_range; + uint32 i_hp; +}; + // Zealot Lor'Khan class npc_zealot_lorkhan : public CreatureScript { - public: npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { } +public: npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { } struct npc_zealot_lorkhanAI : public ScriptedAI { - npc_zealot_lorkhanAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + npc_zealot_lorkhanAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() override { - Initialize(); + _events.Reset(); + me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC); } - void Initialize() + void DamageTaken(Unit* /*attacker*/, uint32& damage) override { - _shieldTimer = 1000; - _bloodLustTimer = 16000; - _greaterHealTimer = 32000; - _disarmTimer = 6000; - _checkTimer = 10000; - _fakeDeath = false; + if (damage >= me->GetHealth()) + { + Talk(TALK_FAKE_DEATH); + me->RemoveAllAuras(); + me->SetImmuneToPC(true, true); + me->SetImmuneToNPC(true, true); + DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true); + me->AttackStop(); + if (Creature* thekal = _instance->GetCreature(DATA_THEKAL)) + thekal->AI()->SetData(DATA_FAKE_DEATH, me->GetEntry()); + _events.DelayEvents(10s); + damage = 0; + } } - void Reset() override + void JustEngagedWith(Unit* /*who*/) override { - Initialize(); - - _instance->SetBossState(DATA_LORKHAN, NOT_STARTED); - me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + _events.ScheduleEvent(EVENT_SHIELD, 1s); + _events.ScheduleEvent(EVENT_BLOODLUST, 16s); + _events.ScheduleEvent(EVENT_GREATER_HEAL, 32s); + _events.ScheduleEvent(EVENT_DISARM, 6s); } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) - return; + _events.Update(diff); - if (_shieldTimer <= diff) - { - DoCast(me, SPELL_SHIELD); - _shieldTimer = 61000; - } - else - _shieldTimer -= diff; - - if (_bloodLustTimer <= diff) - { - DoCast(me, SPELL_BLOODLUST); - _bloodLustTimer = 20000 + rand32() % 8000; - } - else - _bloodLustTimer -= diff; - - // Casting Greaterheal to Thekal or Zath if they are in meele range. - if (_greaterHealTimer <= diff) - { - Creature* thekal = _instance->GetCreature(DATA_THEKAL); - Creature* zath = _instance->GetCreature(DATA_ZATH); - if (!thekal && !zath) - return; - - bool roll = roll_chance_i(50); - Creature* target = roll ? (thekal ? thekal : zath) : (zath ? zath : thekal); - // As we check already above to ensure at least 1 out of Thekal/Zath is not null, target must be not null - ASSERT(target); - - if (!me->IsWithinMeleeRange(target)) - target = roll ? zath : thekal; - - if (me->IsWithinMeleeRange(target)) - DoCast(target, SPELL_GREATERHEAL); - - _greaterHealTimer = 15000 + rand32() % 5000; - } - else - _greaterHealTimer -= diff; - - if (_disarmTimer <= diff) - { - DoCastVictim(SPELL_DISARM); - _disarmTimer = 15000 + rand32() % 10000; - } - else - _disarmTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - // Check for the death of LorKhan and Zath. - if (!_fakeDeath && _checkTimer <= diff) + if (uint32 eventId = _events.ExecuteEvent()) { - if (_instance->GetBossState(DATA_THEKAL) == SPECIAL) + switch (eventId) { - // Resurrect Thekal - if (Creature* thekal = _instance->GetCreature(DATA_THEKAL)) + case EVENT_SHIELD: + DoCastSelf(SPELL_SHIELD); + _events.ScheduleEvent(EVENT_SHIELD, 61s); + break; + case EVENT_BLOODLUST: + DoCastSelf(SPELL_BLOODLUST); + _events.ScheduleEvent(EVENT_BLOODLUST, 20s, 28s); + break; + case EVENT_GREATER_HEAL: { - thekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - thekal->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - thekal->SetFaction(FACTION_MONSTER); - thekal->SetFullHealth(); - } - } + Unit* target = nullptr; + LorKhanSelectTargetToHeal check(me, 100.0f); + Trinity::UnitLastSearcher<LorKhanSelectTargetToHeal> searcher(me, target, check); + Cell::VisitAllObjects(me, searcher, 100.0f); - if (_instance->GetBossState(DATA_ZATH) == SPECIAL) - { - // Resurrect Zath - if (Creature* zath = _instance->GetCreature(DATA_ZATH)) - { - zath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - zath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - zath->SetFaction(FACTION_MONSTER); - zath->SetFullHealth(); + if (target) + DoCast(target, SPELL_GREATERHEAL); + + _events.ScheduleEvent(EVENT_GREATER_HEAL, 15s, 20s); + break; } + case EVENT_DISARM: + DoCastVictim(SPELL_DISARM); + _events.ScheduleEvent(EVENT_DISARM, 15s, 25s); + break; } - - _checkTimer = 5000; } - else - _checkTimer -= diff; - if (!HealthAbovePct(5)) - { - me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); - me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetStandState(UNIT_STAND_STATE_SLEEP); - me->SetFaction(FACTION_FRIENDLY); - me->AttackStop(); - - _instance->SetBossState(DATA_LORKHAN, SPECIAL); - - _fakeDeath = true; - } + if (!UpdateVictim()) + return; DoMeleeAttackIfReady(); } private: - uint32 _shieldTimer; - uint32 _bloodLustTimer; - uint32 _greaterHealTimer; - uint32 _disarmTimer; - uint32 _checkTimer; - bool _fakeDeath; + EventMap _events; InstanceScript* _instance; }; @@ -420,6 +498,15 @@ class npc_zealot_lorkhan : public CreatureScript } }; +enum ZathEvents +{ + EVENT_SWEEPING_STRIKES = 1, + EVENT_SINISTER_STRIKE, + EVENT_GOUGE, + EVENT_KICK, + EVENT_BLIND, +}; + // Zealot Zath class npc_zealot_zath : public CreatureScript { @@ -428,139 +515,82 @@ class npc_zealot_zath : public CreatureScript struct npc_zealot_zathAI : public ScriptedAI { - npc_zealot_zathAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + npc_zealot_zathAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() override { - Initialize(); + _events.Reset(); + me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); } - void Initialize() + void DamageTaken(Unit* /*attacker*/, uint32& damage) override { - _sweepingStrikesTimer = 13000; - _sinisterStrikeTimer = 8000; - _gougeTimer = 25000; - _kickTimer = 18000; - _blindTimer = 5000; - _checkTimer = 10000; - _fakeDeath = false; + if (damage >= me->GetHealth()) + { + Talk(TALK_FAKE_DEATH); + me->RemoveAllAuras(); + me->SetImmuneToPC(true, true); + me->SetImmuneToNPC(true, true); + DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true); + me->AttackStop(); + if (Creature* thekal = _instance->GetCreature(DATA_THEKAL)) + thekal->AI()->SetData(DATA_FAKE_DEATH, me->GetEntry()); + _events.DelayEvents(10s); + damage = 0; + } } - void Reset() override + void JustEngagedWith(Unit* /*who*/) override { - Initialize(); - - _instance->SetBossState(DATA_ZATH, NOT_STARTED); - - me->SetStandState(UNIT_STAND_STATE_STAND); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + _events.ScheduleEvent(EVENT_SWEEPING_STRIKES, 13s); + _events.ScheduleEvent(EVENT_SINISTER_STRIKE, 8s); + _events.ScheduleEvent(EVENT_GOUGE, 25s); + _events.ScheduleEvent(EVENT_KICK, 18s); + _events.ScheduleEvent(EVENT_BLIND, 5s); } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) - return; - - if (_sweepingStrikesTimer <= diff) - { - DoCastVictim(SPELL_SWEEPINGSTRIKES); - _sweepingStrikesTimer = 22000 + rand32() % 4000; - } - else - _sweepingStrikesTimer -= diff; - - if (_sinisterStrikeTimer <= diff) - { - DoCastVictim(SPELL_SINISTERSTRIKE); - _sinisterStrikeTimer = 8000 + rand32() % 8000; - } - else - _sinisterStrikeTimer -= diff; - - if (_gougeTimer <= diff) - { - DoCastVictim(SPELL_GOUGE); - - if (GetThreat(me->GetVictim())) - ModifyThreatByPercent(me->GetVictim(), -100); - - _gougeTimer = 17000 + rand32() % 10000; - } - else - _gougeTimer -= diff; - - if (_kickTimer <= diff) - { - DoCastVictim(SPELL_KICK); - _kickTimer = 15000 + rand32() % 10000; - } - else - _kickTimer -= diff; - - if (_blindTimer <= diff) - { - DoCastVictim(SPELL_BLIND); - _blindTimer = 10000 + rand32() % 10000; - } - else - _blindTimer -= diff; + _events.Update(diff); - // Check for the death of LorKhan and Thekal. - if (!_fakeDeath && _checkTimer <= diff) + if (uint32 eventId = _events.ExecuteEvent()) { - if (_instance->GetBossState(DATA_LORKHAN) == SPECIAL) - { - // Resurrect LorKhan - if (Creature* lorkhan = _instance->GetCreature(DATA_LORKHAN)) - { - lorkhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - lorkhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - lorkhan->SetFaction(14); - lorkhan->SetFullHealth(); - } - } - - if (_instance->GetBossState(DATA_THEKAL) == SPECIAL) + switch (eventId) { - // Resurrect Thekal - if (Creature* thekal = _instance->GetCreature(DATA_THEKAL)) - { - thekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - thekal->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - thekal->SetFaction(FACTION_MONSTER); - thekal->SetFullHealth(); - } + case EVENT_SWEEPING_STRIKES: + DoCastVictim(SPELL_SWEEPINGSTRIKES); + _events.ScheduleEvent(EVENT_SWEEPING_STRIKES, 22s, 26s); + break; + case EVENT_SINISTER_STRIKE: + DoCastVictim(SPELL_SINISTERSTRIKE); + _events.ScheduleEvent(EVENT_SINISTER_STRIKE, 8s, 16s); + break; + case EVENT_GOUGE: + DoCastVictim(SPELL_GOUGE); + if (GetThreat(me->GetVictim())) + ModifyThreatByPercent(me->GetVictim(), -100); + _events.ScheduleEvent(EVENT_GOUGE, 17s, 27s); + break; + case EVENT_KICK: + DoCastVictim(SPELL_KICK); + _events.ScheduleEvent(EVENT_KICK, 15s, 25s); + break; + case EVENT_BLIND: + DoCastVictim(SPELL_BLIND); + _events.ScheduleEvent(EVENT_BLIND, 10s, 20s); + break; } - - _checkTimer = 5000; } - else - _checkTimer -= diff; - if (!HealthAbovePct(5)) - { - me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); - me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetStandState(UNIT_STAND_STATE_SLEEP); - me->SetFaction(35); - me->AttackStop(); - - _instance->SetBossState(DATA_ZATH, SPECIAL); - - _fakeDeath = true; - } + if (!UpdateVictim()) + return; DoMeleeAttackIfReady(); } private: - uint32 _sweepingStrikesTimer; - uint32 _sinisterStrikeTimer; - uint32 _gougeTimer; - uint32 _kickTimer; - uint32 _blindTimer; - uint32 _checkTimer; - bool _fakeDeath; + EventMap _events; InstanceScript* _instance; }; |