aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoroffl <11556157+offl@users.noreply.github.com>2025-09-15 15:44:19 +0300
committerGitHub <noreply@github.com>2025-09-15 14:44:19 +0200
commit5e1f7e40134ecc56293c9a92ee531af5d217d700 (patch)
treebea723fbbde979d6ccaecfaad87ec918b6ef752a /src
parent7c9bea1e904217b26d0b8540fb62f70d0b191ca1 (diff)
Scripts/Zul'Aman: Rewrite Zul'jin (#31229)
Partially based on CMaNGOS script
Diffstat (limited to 'src')
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp1109
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp20
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/zulaman.h17
3 files changed, 640 insertions, 506 deletions
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
index e1c4f20c7fd..b6f3891a1c0 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
@@ -15,597 +15,714 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Boss_ZulJin
-SD%Complete: 85%
-SDComment:
-EndScriptData */
+/*
+ * Timers requires to be revisited
+ * SAY_INTRO is NYI, Zul'jin should say it when player opens Malacrass' door
+ * Berserk requires additional research. Is it really used?
+ * SPELL_ENERGY_STORM doesn't get removed on phase end
+ * Damage of spell 43150 doesn't increase
+ * SPELL_OVERPOWER should be used after the target dodges (?)
+ */
#include "ScriptMgr.h"
+#include "InstanceScript.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
+#include "SpellAuraEffects.h"
#include "SpellInfo.h"
-#include "TemporarySummon.h"
+#include "SpellScript.h"
#include "zulaman.h"
-enum Says
+enum ZuljinTexts
{
- YELL_INTRO = 0,
- YELL_AGGRO = 1,
- YELL_TRANSFORM_TO_BEAR = 2,
- YELL_TRANSFORM_TO_EAGLE = 3,
- YELL_TRANSFORM_TO_LYNX = 4,
- YELL_TRANSFORM_TO_DRAGONHAWK = 5,
- YELL_FIRE_BREATH = 6,
- YELL_BERSERK = 7,
- YELL_KILL = 8,
- YELL_DEATH = 9
+ SAY_INTRO = 0,
+ SAY_AGGRO = 1,
+ SAY_TRANSFORM_TO_BEAR = 2,
+ SAY_TRANSFORM_TO_EAGLE = 3,
+ SAY_TRANSFORM_TO_LYNX = 4,
+ SAY_TRANSFORM_TO_DRAGONHAWK = 5,
+ SAY_FIRE_BREATH = 6,
+ SAY_BERSERK = 7,
+ SAY_SLAY = 8,
+ SAY_DEATH = 9,
+
+ EMOTE_BEAR_SPIRIT = 10,
+ EMOTE_EAGLE_SPIRIT = 11,
+ EMOTE_LYNX_SPIRIT = 12,
+ EMOTE_DRAGONHAWK_SPIRIT = 13,
+
+ EMOTE_FADE_AWAY = 0
};
-enum Spells
+enum ZuljinSpells
{
// Troll Form
SPELL_WHIRLWIND = 17207,
- SPELL_GRIEVOUS_THROW = 43093, // remove debuff after full healed
+ SPELL_GRIEVOUS_THROW = 43093,
+
// Bear Form
- SPELL_CREEPING_PARALYSIS = 43095, // should cast on the whole raid
- SPELL_OVERPOWER = 43456, // use after melee attack dodged
+ SPELL_CREEPING_PARALYSIS = 43095,
+ SPELL_OVERPOWER = 43456,
+
// Eagle Form
- SPELL_ENERGY_STORM = 43983, // enemy area aura, trigger 42577
- SPELL_ZAP_INFORM = 42577,
- SPELL_ZAP_DAMAGE = 43137, // 1250 damage
- SPELL_SUMMON_CYCLONE = 43112, // summon four feather vortex
- CREATURE_FEATHER_VORTEX = 24136,
- SPELL_CYCLONE_VISUAL = 43119, // trigger 43147 visual
- SPELL_CYCLONE_PASSIVE = 43120, // trigger 43121 (4y aoe) every second
+ SPELL_SUMMON_CYCLONE = 43112,
+ SPELL_ENERGY_STORM = 43983,
+
// Lynx Form
- SPELL_CLAW_RAGE_HASTE = 42583,
- SPELL_CLAW_RAGE_TRIGGER = 43149,
- SPELL_CLAW_RAGE_DAMAGE = 43150,
- SPELL_LYNX_RUSH_HASTE = 43152,
- SPELL_LYNX_RUSH_DAMAGE = 43153,
+ SPELL_CLAW_RAGE = 42583,
+ SPELL_LYNX_RUSH = 43152,
+
// Dragonhawk Form
- SPELL_FLAME_WHIRL = 43213, // trigger two spells
+ SPELL_FLAME_WHIRL = 43213,
SPELL_FLAME_BREATH = 43215,
- SPELL_SUMMON_PILLAR = 43216, // summon 24187
- CREATURE_COLUMN_OF_FIRE = 24187,
- SPELL_PILLAR_TRIGGER = 43218, // trigger 43217
- // Cosmetic
- SPELL_SPIRIT_AURA = 42466,
- SPELL_SIPHON_SOUL = 43501,
- // Transforms:
- SPELL_SHAPE_OF_THE_BEAR = 42594, // 15% dmg
+ SPELL_PILLAR_OF_FIRE = 43216,
+
+ // All forms
+ SPELL_BERSERK = 45078,
+
+ // Spirits
+ SPELL_SPIRIT_DRAIN = 42542,
+ SPELL_SPIRIT_DRAINED = 42520,
+ SPELL_SPIRIT_REALM = 44035,
+ SPELL_SPIRIT_FADE = 44036,
+
+ // Transforms
+ SPELL_SHAPE_OF_THE_BEAR = 42594,
SPELL_SHAPE_OF_THE_EAGLE = 42606,
- SPELL_SHAPE_OF_THE_LYNX = 42607, // haste melee 30%
+ SPELL_SHAPE_OF_THE_LYNX = 42607,
SPELL_SHAPE_OF_THE_DRAGONHAWK = 42608,
- SPELL_BERSERK = 45078
+ // Feather Vortex
+ SPELL_DREAM_FOG = 24780,
+ SPELL_BALL_OF_ENERGY = 43457,
+ SPELL_CYCLONE_VISUAL = 43119,
+ SPELL_CYCLONE = 43120,
+
+ // Scripts
+ SPELL_ZAP_DAMAGE = 43137,
+ SPELL_CLAW_RAGE_PERIODIC = 43149,
+ SPELL_LYNX_RUSH_DAMAGE = 43153,
+
+ // Misc
+ SPELL_CYCLONE_EFFECT = 43121,
+ SPELL_INCINERATE_BLUE = 42567
};
-enum Phase
+enum ZuljinEvents
{
- PHASE_BEAR = 0,
- PHASE_EAGLE = 1,
- PHASE_LYNX = 2,
- PHASE_DRAGONHAWK = 3,
- PHASE_TROLL = 4
+ // Troll Form
+ EVENT_WHIRLWIND = 1,
+ EVENT_GRIEVOUS_THROW,
+
+ // Bear Form
+ EVENT_CREEPING_PARALYSIS,
+ EVENT_OVERPOWER,
+
+ // Eagle Form
+ EVENT_SUMMON_CYCLONE,
+ EVENT_ENERGY_STORM,
+
+ // Lynx Form
+ EVENT_CLAW_RAGE,
+ EVENT_LYNX_RUSH,
+
+ // Dragonhawk Form
+ EVENT_FLAME_WHIRL,
+ EVENT_FLAME_BREATH,
+ EVENT_PILLAR_OF_FIRE,
+
+ // All forms
+ EVENT_BERSERK,
+
+ // Transition
+ EVENT_TRANSITION_1,
+ EVENT_TRANSITION_2,
+ EVENT_TRANSITION_3,
+ EVENT_TRANSITION_4
};
-//coords for going for changing form
-#define CENTER_X 120.148811f
-#define CENTER_Y 703.713684f
-#define CENTER_Z 45.111477f
+enum ZuljinEventGroups
+{
+ EVENT_GROUP_TROLL_PHASE = 1,
+ EVENT_GROUP_BEAR_PHASE = 2,
+ EVENT_GROUP_EAGLE_PHASE = 3,
+ EVENT_GROUP_LYNX_PHASE = 4
+};
-struct SpiritInfoStruct
+enum ZuljinPhases
{
- uint32 entry;
- Position pos;
+ PHASE_TROLL = 0,
+ PHASE_BEAR = 1,
+ PHASE_EAGLE = 2,
+ PHASE_LYNX = 3,
+ PHASE_DRAGONHAWK = 4
};
-static SpiritInfoStruct const SpiritInfo[4] =
+enum ZuljinActions
{
- { 23878, { 147.87f, 706.51f, 45.11f, 3.04f } },
- { 23880, { 88.950f, 705.49f, 45.11f, 6.11f } },
- { 23877, { 137.23f, 725.98f, 45.11f, 3.71f } },
- { 23879, { 104.29f, 726.43f, 45.11f, 5.43f } }
+ ACTION_CLEAR_FIXATE = 1,
+ ACTION_INTERRUPT_SPIRIT_DRAIN = 2,
+ ACTION_CAST_SPIRIT_DRAIN = 3,
+ ACTION_CANCEL_SPIRIT_DRAINED = 4
};
-struct TransformStruct
+enum ZuljinMisc
{
- uint8 text;
- uint32 spell, unaura;
+ POINT_CENTER = 0,
+ NPC_FEATHER_VORTEX = 24136
};
-static TransformStruct const Transform[4] =
+static Position const CenterPos = { 120.172f, 706.444f, 45.111374f, 0.0f };
+
+struct BossPhase
{
- { YELL_TRANSFORM_TO_BEAR, SPELL_SHAPE_OF_THE_BEAR, SPELL_WHIRLWIND },
- { YELL_TRANSFORM_TO_EAGLE, SPELL_SHAPE_OF_THE_EAGLE, SPELL_SHAPE_OF_THE_BEAR },
- { YELL_TRANSFORM_TO_LYNX, SPELL_SHAPE_OF_THE_LYNX, SPELL_SHAPE_OF_THE_EAGLE },
- { YELL_TRANSFORM_TO_DRAGONHAWK, SPELL_SHAPE_OF_THE_DRAGONHAWK, SPELL_SHAPE_OF_THE_LYNX }
+ uint32 spiritSpellId;
+ uint8 sayId, emoteId;
+ uint8 phaseGroup;
+ uint32 spiritId;
};
-class boss_zuljin : public CreatureScript
+static constexpr BossPhase ZuljinPhases[] =
{
- public:
- boss_zuljin() : CreatureScript("boss_zuljin") { }
+ { SPELL_SHAPE_OF_THE_BEAR, SAY_TRANSFORM_TO_BEAR, EMOTE_BEAR_SPIRIT, EVENT_GROUP_TROLL_PHASE, DATA_BEAR_SPIRIT },
+ { SPELL_SHAPE_OF_THE_EAGLE, SAY_TRANSFORM_TO_EAGLE, EMOTE_EAGLE_SPIRIT, EVENT_GROUP_BEAR_PHASE, DATA_EAGLE_SPIRIT },
+ { SPELL_SHAPE_OF_THE_LYNX, SAY_TRANSFORM_TO_LYNX, EMOTE_LYNX_SPIRIT, EVENT_GROUP_EAGLE_PHASE, DATA_LYNX_SPIRIT },
+ { SPELL_SHAPE_OF_THE_DRAGONHAWK, SAY_TRANSFORM_TO_DRAGONHAWK, EMOTE_DRAGONHAWK_SPIRIT, EVENT_GROUP_LYNX_PHASE, DATA_DRAGONHAWK_SPIRIT }
+};
- struct boss_zuljinAI : public BossAI
- {
- boss_zuljinAI(Creature* creature) : BossAI(creature, BOSS_ZULJIN)
- {
- Initialize();
- health_20 = 0;
- }
+static constexpr uint32 SpiritData[] =
+{
+ DATA_BEAR_SPIRIT,
+ DATA_EAGLE_SPIRIT,
+ DATA_LYNX_SPIRIT,
+ DATA_DRAGONHAWK_SPIRIT
+};
- void Initialize()
- {
- Phase = 0;
+// 23863 - Zul'jin
+struct boss_zuljin : public BossAI
+{
+ boss_zuljin(Creature* creature) : BossAI(creature, BOSS_ZULJIN),
+ _phase(PHASE_TROLL), _healthCheck(0), _rushCounter(0), _currentRushCount(0), _isInTransition(false) { }
+
+ void Reset() override
+ {
+ _Reset();
+ _phase = PHASE_TROLL;
+ _healthCheck = 80;
+ _rushCounter = urand(4, 10);
+ _currentRushCount = 0;
+ _isInTransition = false;
+ }
+
+ void JustEngagedWith(Unit* who) override
+ {
+ BossAI::JustEngagedWith(who);
+
+ Talk(SAY_AGGRO);
+
+ ScheduleEventsForPhase();
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
+ {
+ // Let creature finish current transition
+ if (_isInTransition)
+ return;
+
+ if (me->HealthBelowPctDamaged(_healthCheck, damage))
+ {
+ _isInTransition = true;
+ events.ScheduleEvent(EVENT_TRANSITION_1, 0s);
+ }
+ }
- Intro_Timer = 37000;
- Berserk_Timer = 600000;
+ void MovementInform(uint32 type, uint32 pointId) override
+ {
+ if (type == POINT_MOTION_TYPE && pointId == POINT_CENTER)
+ events.ScheduleEvent(EVENT_TRANSITION_2, 0s);
+ }
- Whirlwind_Timer = 7000;
- Grievous_Throw_Timer = 8000;
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ switch (spellInfo->Id)
+ {
+ case SPELL_SHAPE_OF_THE_BEAR:
+ case SPELL_SHAPE_OF_THE_EAGLE:
+ case SPELL_SHAPE_OF_THE_LYNX:
+ case SPELL_SHAPE_OF_THE_DRAGONHAWK:
+ events.ScheduleEvent(EVENT_TRANSITION_4, 0s);
+ break;
+ default:
+ break;
+ }
+ }
- Creeping_Paralysis_Timer = 7000;
- Overpower_Timer = 0;
+ void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override
+ {
+ switch (spellInfo->Id)
+ {
+ case SPELL_LYNX_RUSH_DAMAGE:
+ ++_currentRushCount;
+ if (_currentRushCount != _rushCounter)
+ {
+ DoCastSelf(SPELL_LYNX_RUSH, true);
+ }
+ else
+ {
+ _rushCounter = urand(4, 10);
+ _currentRushCount = 0;
+ }
+ break;
+ case SPELL_CLAW_RAGE:
+ if (target->IsPlayer())
+ {
+ _clawRageVictimGUID = target->GetGUID();
+ me->GetThreatManager().AddThreat(target->ToUnit(), 50000000.0f, nullptr, true, true);
+ }
+ break;
+ }
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_CLEAR_FIXATE)
+ if (Unit* target = ObjectAccessor::GetUnit(*me, _clawRageVictimGUID))
+ me->GetThreatManager().AddThreat(target, -50000000.0f, nullptr, true, true);
+ }
+
+ void ScheduleEventsForPhase()
+ {
+ switch (_phase)
+ {
+ case PHASE_TROLL:
+ events.ScheduleEvent(EVENT_WHIRLWIND, 15s, 20s, EVENT_GROUP_TROLL_PHASE);
+ events.ScheduleEvent(EVENT_GRIEVOUS_THROW, 10s, 20s, EVENT_GROUP_TROLL_PHASE);
+ events.ScheduleEvent(EVENT_BERSERK, 10min);
+ break;
+ case PHASE_BEAR:
+ events.ScheduleEvent(EVENT_CREEPING_PARALYSIS, 0s, 5s, EVENT_GROUP_BEAR_PHASE);
+ events.ScheduleEvent(EVENT_OVERPOWER, 5s, 10s, EVENT_GROUP_BEAR_PHASE);
+ break;
+ case PHASE_EAGLE:
+ events.ScheduleEvent(EVENT_SUMMON_CYCLONE, 0s, EVENT_GROUP_EAGLE_PHASE);
+ events.ScheduleEvent(EVENT_ENERGY_STORM, 0s, EVENT_GROUP_EAGLE_PHASE);
+ break;
+ case PHASE_LYNX:
+ events.ScheduleEvent(EVENT_CLAW_RAGE, 5s, 15s, EVENT_GROUP_LYNX_PHASE);
+ events.ScheduleEvent(EVENT_LYNX_RUSH, 15s, 20s, EVENT_GROUP_LYNX_PHASE);
+ break;
+ case PHASE_DRAGONHAWK:
+ events.ScheduleEvent(EVENT_FLAME_WHIRL, 0s, 15s);
+ events.ScheduleEvent(EVENT_FLAME_BREATH, 5s, 10s);
+ events.ScheduleEvent(EVENT_PILLAR_OF_FIRE, 5s, 10s);
+ break;
+ default:
+ break;
+ }
+ }
- Claw_Rage_Timer = 5000;
- Lynx_Rush_Timer = 14000;
- Claw_Loop_Timer = 0;
- Claw_Counter = 0;
+ void OnSpellCast(SpellInfo const* spell) override
+ {
+ switch (spell->Id)
+ {
+ case SPELL_FLAME_BREATH:
+ Talk(SAY_FIRE_BREATH);
+ break;
+ case SPELL_BERSERK:
+ Talk(SAY_BERSERK);
+ break;
+ default:
+ break;
+ }
+ }
- Flame_Whirl_Timer = 5000;
- Flame_Breath_Timer = 6000;
- Pillar_Of_Fire_Timer = 7000;
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ for (uint32 spiritId : SpiritData)
+ if (Creature* spirit = instance->GetCreature(spiritId))
+ spirit->AI()->DoAction(ACTION_CANCEL_SPIRIT_DRAINED);
- ClawTargetGUID.Clear();
- TankGUID.Clear();
- }
+ summons.DespawnAll();
+ _DespawnAtEvade();
+ }
- ObjectGuid SpiritGUID[4];
- ObjectGuid ClawTargetGUID;
- ObjectGuid TankGUID;
+ void KilledUnit(Unit* /*victim*/) override
+ {
+ Talk(SAY_SLAY);
+ }
- uint32 Phase;
- uint32 health_20;
+ void JustDied(Unit* /*killer*/) override
+ {
+ // Last spirit, needs to be handled here
+ if (Creature* spirit = instance->GetCreature(ZuljinPhases[_phase - 1].spiritId))
+ spirit->AI()->DoAction(ACTION_INTERRUPT_SPIRIT_DRAIN);
- uint32 Intro_Timer;
- uint32 Berserk_Timer;
+ _JustDied();
- uint32 Whirlwind_Timer;
- uint32 Grievous_Throw_Timer;
+ Talk(SAY_DEATH);
+ DoCastSelf(SPELL_INCINERATE_BLUE, true);
+ }
- uint32 Creeping_Paralysis_Timer;
- uint32 Overpower_Timer;
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- uint32 Claw_Rage_Timer;
- uint32 Lynx_Rush_Timer;
- uint32 Claw_Counter;
- uint32 Claw_Loop_Timer;
+ events.Update(diff);
- uint32 Flame_Whirl_Timer;
- uint32 Flame_Breath_Timer;
- uint32 Pillar_Of_Fire_Timer;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- void Reset() override
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- _Reset();
+ // Troll Form
+ case EVENT_WHIRLWIND:
+ DoCastSelf(SPELL_WHIRLWIND);
+ events.Repeat(10s, 20s);
+ break;
+ case EVENT_GRIEVOUS_THROW:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 80.0f, true))
+ DoCast(target, SPELL_GRIEVOUS_THROW);
+ events.Repeat(10s, 20s);
+ break;
+
+ // Bear Form
+ case EVENT_CREEPING_PARALYSIS:
+ DoCastSelf(SPELL_CREEPING_PARALYSIS);
+ events.Repeat(20s);
+ break;
+ case EVENT_OVERPOWER:
+ // Cast as triggered, otherwise it will not work
+ DoCastVictim(SPELL_OVERPOWER, true);
+ events.Repeat(10s, 15s);
+ break;
+
+ // Eagle Form
+ case EVENT_SUMMON_CYCLONE:
+ DoCastSelf(SPELL_SUMMON_CYCLONE);
+ break;
+ case EVENT_ENERGY_STORM:
+ DoCastSelf(SPELL_ENERGY_STORM);
+ break;
+
+ // Lynx Form
+ case EVENT_CLAW_RAGE:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 80.0f, true))
+ DoCast(target, SPELL_CLAW_RAGE);
+ events.Repeat(15s, 20s);
+ break;
+ case EVENT_LYNX_RUSH:
+ // For unknown reason Lynx Rush sequence works only if init spell was casted as triggered, but make sure we don't use it if Claw Rage is active
+ if (!me->HasAura(SPELL_CLAW_RAGE))
+ DoCastSelf(SPELL_LYNX_RUSH, true);
+ events.Repeat(20s, 25s);
+ break;
+
+ // Dragonhawk Form
+ case EVENT_FLAME_WHIRL:
+ DoCastSelf(SPELL_FLAME_WHIRL);
+ events.Repeat(10s, 15s);
+ break;
+ case EVENT_FLAME_BREATH:
+ DoCastSelf(SPELL_FLAME_BREATH);
+ events.Repeat(5s, 10s);
+ break;
+ case EVENT_PILLAR_OF_FIRE:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 80.0f, true))
+ DoCast(target, SPELL_PILLAR_OF_FIRE);
+ events.Repeat(10s, 15s);
+ break;
+
+ // All forms
+ case EVENT_BERSERK:
+ DoCastSelf(SPELL_BERSERK);
+ break;
+
+ // Transition
+ case EVENT_TRANSITION_1:
+ {
+ if (_phase == PHASE_EAGLE)
+ {
+ summons.DespawnEntry(NPC_FEATHER_VORTEX);
+ me->RemoveAurasDueToSpell(SPELL_ENERGY_STORM);
+ }
- health_20 = me->CountPctFromMaxHealth(20);
+ _healthCheck -= 20;
+ Talk(ZuljinPhases[_phase].sayId);
+ me->RemoveAurasDueToSpell(ZuljinPhases[_phase - 1].spiritSpellId);
+ me->SetReactState(REACT_PASSIVE);
+ me->GetMotionMaster()->MovePoint(POINT_CENTER, CenterPos);
- Initialize();
+ events.CancelEventGroup(ZuljinPhases[_phase].phaseGroup);
+ break;
+ }
+ case EVENT_TRANSITION_2:
+ {
+ if (Creature* spirit = instance->GetCreature(ZuljinPhases[_phase - 1].spiritId))
+ spirit->AI()->DoAction(ACTION_INTERRUPT_SPIRIT_DRAIN);
- me->SetVirtualItem(0, 33975);
- //me->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, 218172674);
- //me->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE);
- }
+ if (Creature* spirit = instance->GetCreature(ZuljinPhases[_phase].spiritId))
+ {
+ me->SetFacingToObject(spirit);
+ Talk(ZuljinPhases[_phase].emoteId);
+ spirit->AI()->DoAction(ACTION_CAST_SPIRIT_DRAIN);
+ }
+ events.ScheduleEvent(EVENT_TRANSITION_3, 3s);
+ break;
+ }
+ case EVENT_TRANSITION_3:
+ DoCastSelf(ZuljinPhases[_phase].spiritSpellId);
+ break;
+ case EVENT_TRANSITION_4:
+ {
+ ++_phase;
- void JustEngagedWith(Unit* who) override
- {
- BossAI::JustEngagedWith(who);
+ ScheduleEventsForPhase();
- Talk(YELL_INTRO);
- SpawnAdds();
- EnterPhase(0);
- }
+ ResetThreatList();
+ SetEquipmentSlots(false, EQUIP_UNEQUIP);
- void KilledUnit(Unit* /*victim*/) override
- {
- if (Intro_Timer)
- return;
+ if (_phase != PHASE_EAGLE)
+ me->SetReactState(REACT_AGGRESSIVE);
- Talk(YELL_KILL);
+ _isInTransition = false;
+ break;
+ }
+ default:
+ break;
}
- void JustDied(Unit* /*killer*/) override
- {
- _JustDied();
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
- Talk(YELL_DEATH);
+ DoMeleeAttackIfReady();
+ }
- if (Unit* Temp = ObjectAccessor::GetUnit(*me, SpiritGUID[3]))
- Temp->SetStandState(UNIT_STAND_STATE_DEAD);
- }
+private:
+ uint8 _phase;
+ uint8 _healthCheck;
+ uint8 _rushCounter;
+ uint8 _currentRushCount;
+ bool _isInTransition;
+ ObjectGuid _clawRageVictimGUID;
+};
- void AttackStart(Unit* who) override
- {
- if (Phase == 2)
- AttackStartNoMove(who);
- else
- ScriptedAI::AttackStart(who);
- }
+// 24136 - Feather Vortex
+struct npc_zuljin_vortex : public ScriptedAI
+{
+ npc_zuljin_vortex(Creature* creature) : ScriptedAI(creature) { }
- void DoMeleeAttackIfReady()
- {
- if (!me->IsNonMeleeSpellCast(false))
- {
- if (me->isAttackReady() && me->IsWithinMeleeRange(me->GetVictim()))
- {
- if (Phase == 1 && !Overpower_Timer)
- {
- uint32 health = me->EnsureVictim()->GetHealth();
- me->AttackerStateUpdate(me->GetVictim());
- if (me->GetVictim() && health == me->EnsureVictim()->GetHealth())
- {
- DoCastVictim(SPELL_OVERPOWER, false);
- Overpower_Timer = 5000;
- }
- }
- else
- me->AttackerStateUpdate(me->GetVictim());
- me->resetAttackTimer();
- }
- }
- }
+ void JustAppeared() override
+ {
+ /// @todo: After first tick makes creature stop movement, investigate this
+ // DoCastSelf(SPELL_DREAM_FOG);
- void SpawnAdds()
+ _scheduler
+ .Schedule(2s, [this](TaskContext /*task*/)
{
- for (uint8 i = 0; i < 4; ++i)
- {
- if (Creature* creature = me->SummonCreature(SpiritInfo[i].entry, SpiritInfo[i].pos, TEMPSUMMON_DEAD_DESPAWN))
- {
- creature->CastSpell(creature, SPELL_SPIRIT_AURA, true);
- creature->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
- creature->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
- SpiritGUID[i] = creature->GetGUID();
- }
- }
- }
-
- void DespawnAdds()
+ DoCastSelf(SPELL_BALL_OF_ENERGY);
+ DoCastSelf(SPELL_CYCLONE_VISUAL);
+ DoCastSelf(SPELL_CYCLONE);
+ })
+ .Schedule(3s, [this](TaskContext /*task*/)
{
- for (uint8 i = 0; i < 4; ++i)
- {
- if (SpiritGUID[i])
- {
- if (Unit* temp = ObjectAccessor::GetUnit(*me, SpiritGUID[i]))
- {
- temp->SetVisible(false);
- temp->setDeathState(DEAD);
- }
- }
- SpiritGUID[i].Clear();
- }
- }
+ DoZoneInCombat();
+ FixateRandomTarget();
+ });
+ }
+
+ void FixateRandomTarget()
+ {
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
+ {
+ ResetThreatList();
+ AddThreat(target, 1000000.0f);
+ AttackStart(target);
+ }
+ }
- void EnterPhase(uint32 NextPhase)
- {
- switch (NextPhase)
- {
- case 0:
- break;
- case 1:
- case 2:
- case 3:
- case 4:
- DoTeleportTo(CENTER_X, CENTER_Y, CENTER_Z, 100);
- ResetThreatList();
- me->SetVirtualItem(0, 0);
- me->RemoveAurasDueToSpell(Transform[Phase].unaura);
- DoCast(me, Transform[Phase].spell);
- Talk(Transform[Phase].text);
- if (Phase > 0)
- {
- if (Unit* Temp = ObjectAccessor::GetUnit(*me, SpiritGUID[Phase - 1]))
- Temp->SetStandState(UNIT_STAND_STATE_DEAD);
- }
- if (Unit* Temp = ObjectAccessor::GetUnit(*me, SpiritGUID[NextPhase - 1]))
- Temp->CastSpell(me, SPELL_SIPHON_SOUL, false); // should m cast on temp
-
- if (NextPhase == 2)
- {
- me->GetMotionMaster()->Clear();
- DoCast(me, SPELL_ENERGY_STORM, true); // enemy aura
- for (uint8 i = 0; i < 4; ++i)
- {
- Creature* Vortex = DoSpawnCreature(CREATURE_FEATHER_VORTEX, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0s);
- if (Vortex)
- {
- Vortex->CastSpell(Vortex, SPELL_CYCLONE_PASSIVE, true);
- Vortex->CastSpell(Vortex, SPELL_CYCLONE_VISUAL, true);
- Vortex->AI()->AttackStart(SelectTarget(SelectTargetMethod::Random, 0));
- DoZoneInCombat(Vortex);
- }
- }
- }
- else
- AttackStart(me->GetVictim());
-
- if (NextPhase == 3)
- {
- me->RemoveAurasDueToSpell(SPELL_ENERGY_STORM);
- summons.DespawnEntry(CREATURE_FEATHER_VORTEX);
- me->GetMotionMaster()->MoveChase(me->GetVictim());
- }
- break;
- default:
- break;
- }
- Phase = NextPhase;
- }
+ void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override
+ {
+ if (spellInfo->Id == SPELL_CYCLONE_EFFECT && target == me->GetVictim())
+ FixateRandomTarget();
+ }
- void UpdateAI(uint32 diff) override
- {
- if (!TankGUID)
- {
- if (!UpdateVictim())
- return;
+ void UpdateAI(uint32 diff) override
+ {
+ UpdateVictim();
- if (me->GetHealth() < health_20 * (4 - Phase))
- EnterPhase(Phase + 1);
- }
+ _scheduler.Update(diff);
+ }
- if (Berserk_Timer <= diff)
- {
- DoCast(me, SPELL_BERSERK, true);
- Talk(YELL_BERSERK);
- Berserk_Timer = 60000;
- }
- else
- Berserk_Timer -= diff;
+private:
+ TaskScheduler _scheduler;
+};
- switch (Phase)
- {
- case 0:
- if (Intro_Timer)
- {
- if (Intro_Timer <= diff)
- {
- Talk(YELL_AGGRO);
- Intro_Timer = 0;
- }
- else
- Intro_Timer -= diff;
- }
-
- if (Whirlwind_Timer <= diff)
- {
- DoCast(me, SPELL_WHIRLWIND);
- Whirlwind_Timer = urand(15000, 20000);
- }
- else
- Whirlwind_Timer -= diff;
-
- if (Grievous_Throw_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
- DoCast(target, SPELL_GRIEVOUS_THROW, false);
- Grievous_Throw_Timer = 10000;
- }
- else
- Grievous_Throw_Timer -= diff;
- break;
-
- case 1:
- if (Creeping_Paralysis_Timer <= diff)
- {
- DoCast(me, SPELL_CREEPING_PARALYSIS);
- Creeping_Paralysis_Timer = 20000;
- }
- else
- Creeping_Paralysis_Timer -= diff;
-
- if (Overpower_Timer <= diff)
- {
- // implemented in DoMeleeAttackIfReady()
- Overpower_Timer = 0;
- }
- else
- Overpower_Timer -= diff;
- break;
-
- case 2:
- return;
-
- case 3:
- if (Claw_Rage_Timer <= diff)
- {
- if (!TankGUID)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- {
- if (me->GetVictim())
- TankGUID = me->EnsureVictim()->GetGUID();
-
- me->SetSpeedRate(MOVE_RUN, 5.0f);
- AttackStart(target); // change victim
- Claw_Rage_Timer = 0;
- Claw_Loop_Timer = 500;
- Claw_Counter = 0;
- }
- }
- else if (!Claw_Rage_Timer) // do not do this when Lynx_Rush
- {
- if (Claw_Loop_Timer <= diff)
- {
- Unit* target = me->GetVictim();
- if (!target || !target->isTargetableForAttack())
- target = ObjectAccessor::GetUnit(*me, TankGUID);
- if (!target || !target->isTargetableForAttack())
- target = SelectTarget(SelectTargetMethod::Random, 0);
- if (target)
- {
- AttackStart(target);
- if (me->IsWithinMeleeRange(target))
- {
- DoCast(target, SPELL_CLAW_RAGE_DAMAGE, true);
- ++Claw_Counter;
- if (Claw_Counter == 12)
- {
- Claw_Rage_Timer = urand(15000, 20000);
- me->SetSpeedRate(MOVE_RUN, 1.2f);
- AttackStart(ObjectAccessor::GetUnit(*me, TankGUID));
- TankGUID.Clear();
- return;
- }
- else
- Claw_Loop_Timer = 500;
- }
- }
- else
- {
- EnterEvadeMode(); // if (target)
- return;
- }
- }
- else Claw_Loop_Timer -= diff;
- } //if (TankGUID)
- }
- else
- Claw_Rage_Timer -= diff;
-
- if (Lynx_Rush_Timer <= diff)
- {
- if (!TankGUID)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- {
- TankGUID = me->EnsureVictim()->GetGUID();
- me->SetSpeedRate(MOVE_RUN, 5.0f);
- AttackStart(target); // change victim
- Lynx_Rush_Timer = 0;
- Claw_Counter = 0;
- }
- }
- else if (!Lynx_Rush_Timer)
- {
- Unit* target = me->GetVictim();
- if (!target || !target->isTargetableForAttack())
- {
- target = SelectTarget(SelectTargetMethod::Random, 0);
- AttackStart(target);
- }
- if (target)
- {
- if (me->IsWithinMeleeRange(target))
- {
- DoCast(target, SPELL_LYNX_RUSH_DAMAGE, true);
- ++Claw_Counter;
- if (Claw_Counter == 9)
- {
- Lynx_Rush_Timer = urand(15000, 20000);
- me->SetSpeedRate(MOVE_RUN, 1.2f);
- AttackStart(ObjectAccessor::GetUnit(*me, TankGUID));
- TankGUID.Clear();
- }
- else
- AttackStart(SelectTarget(SelectTargetMethod::Random, 0));
- }
- }
- else
- {
- EnterEvadeMode(); // if (target)
- return;
- }
- } //if (TankGUID)
- }
- else
- Lynx_Rush_Timer -= diff;
- break;
- case 4:
- if (Flame_Whirl_Timer <= diff)
- {
- DoCast(me, SPELL_FLAME_WHIRL);
- Flame_Whirl_Timer = 12000;
- }
- else
- Flame_Whirl_Timer -= diff;
-
- if (Pillar_Of_Fire_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- DoCast(target, SPELL_SUMMON_PILLAR);
- Pillar_Of_Fire_Timer = 10000;
- }
- else
- Pillar_Of_Fire_Timer -= diff;
-
- if (Flame_Breath_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- me->SetFacingToObject(target);
- DoCast(me, SPELL_FLAME_BREATH);
- Flame_Breath_Timer = 10000;
- }
- else
- Flame_Breath_Timer -= diff;
- break;
- default:
- break;
- }
+// 23877 - Amani Lynx Spirit
+// 23878 - Amani Bear Spirit
+// 23879 - Amani Dragonhawk Spirit
+// 23880 - Amani Eagle Spirit
+struct npc_zuljin_spirit : public ScriptedAI
+{
+ npc_zuljin_spirit(Creature* creature) : ScriptedAI(creature) { }
- if (!TankGUID)
- DoMeleeAttackIfReady();
- }
- };
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_CAST_SPIRIT_DRAIN:
+ DoCastSelf(SPELL_SPIRIT_DRAIN);
+ break;
+ case ACTION_INTERRUPT_SPIRIT_DRAIN:
+ me->InterruptNonMeleeSpells(false);
+ DoCastSelf(SPELL_SPIRIT_DRAINED);
+ break;
+ case ACTION_CANCEL_SPIRIT_DRAINED:
+ me->RemoveAurasDueToSpell(SPELL_SPIRIT_DRAINED);
+ break;
+ }
+ }
- CreatureAI* GetAI(Creature* creature) const override
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ if (spellInfo->Id == SPELL_INCINERATE_BLUE)
{
- return GetZulAmanAI<boss_zuljinAI>(creature);
+ me->RemoveAurasDueToSpell(SPELL_SPIRIT_DRAINED);
+
+ _scheduler.Schedule(13s, [this](TaskContext /*task*/)
+ {
+ DoCastSelf(SPELL_SPIRIT_REALM);
+ Talk(EMOTE_FADE_AWAY);
+
+ _scheduler.Schedule(5s, [this](TaskContext /*task*/)
+ {
+ DoCastSelf(SPELL_SPIRIT_FADE);
+ });
+ });
}
-};
+ }
-class npc_zuljin_vortex : public CreatureScript
-{
- public:
- npc_zuljin_vortex() : CreatureScript("npc_zuljin_vortex") { }
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
+ }
- struct npc_zuljin_vortexAI : public ScriptedAI
- {
- npc_zuljin_vortexAI(Creature* creature) : ScriptedAI(creature) { }
+private:
+ TaskScheduler _scheduler;
+};
- void Reset() override { }
+// 42577 - Zap
+class spell_zuljin_zap : public SpellScript
+{
+ PrepareSpellScript(spell_zuljin_zap);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_ZAP_DAMAGE });
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetHitUnit()->CastSpell(GetCaster(), SPELL_ZAP_DAMAGE);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_zuljin_zap::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
- void JustEngagedWith(Unit* /*target*/) override { }
+// 42583 - Claw Rage
+class spell_zuljin_claw_rage : public SpellScript
+{
+ PrepareSpellScript(spell_zuljin_claw_rage);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_CLAW_RAGE_PERIODIC });
+ }
+
+ void HandleAfterCast()
+ {
+ GetCaster()->CastSpell(GetCaster(), SPELL_CLAW_RAGE_PERIODIC, true);
+ }
+
+ void Register() override
+ {
+ AfterCast += SpellCastFn(spell_zuljin_claw_rage::HandleAfterCast);
+ }
+};
- void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override
- {
- Unit* unitCaster = caster->ToUnit();
- if (!unitCaster)
- return;
+// 43149 - Claw Rage
+class spell_zuljin_claw_rage_periodic : public AuraScript
+{
+ PrepareAuraScript(spell_zuljin_claw_rage_periodic);
- if (spellInfo->Id == SPELL_ZAP_INFORM)
- DoCast(unitCaster, SPELL_ZAP_DAMAGE, true);
- }
+ bool Validate(SpellInfo const* spellInfo) override
+ {
+ return ValidateSpellInfo({ spellInfo->GetEffect(EFFECT_0).TriggerSpell });
+ }
- void UpdateAI(uint32 /*diff*/) override
- {
- //if the vortex reach the target, it change his target to another player
- if (me->IsWithinMeleeRange(me->GetVictim()))
- AttackStart(SelectTarget(SelectTargetMethod::Random, 0));
- }
- };
+ void OnPeriodic(AuraEffect const* aurEff)
+ {
+ PreventDefaultAction();
- CreatureAI* GetAI(Creature* creature) const override
+ Unit* target = GetTarget();
+ Unit* victim = target->GetVictim();
+ if (victim)
{
- return GetZulAmanAI<npc_zuljin_vortexAI>(creature);
+ uint32 triggerSpell = aurEff->GetSpellEffectInfo().TriggerSpell;
+ target->CastSpell(victim, triggerSpell, true);
}
+ }
+
+ void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Creature* target = GetTarget()->ToCreature())
+ target->AI()->DoAction(ACTION_CLEAR_FIXATE);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_zuljin_claw_rage_periodic::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_zuljin_claw_rage_periodic::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
+ }
+};
+
+// 43152 - Lynx Rush
+class spell_zuljin_lynx_rush : public SpellScript
+{
+ PrepareSpellScript(spell_zuljin_lynx_rush);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_LYNX_RUSH_DAMAGE });
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ if (Creature* caster = GetCaster()->ToCreature())
+ if (Unit* target = caster->AI()->SelectTarget(SelectTargetMethod::Random, 0, 80.0f, true))
+ caster->CastSpell(target, SPELL_LYNX_RUSH_DAMAGE, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_zuljin_lynx_rush::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
};
void AddSC_boss_zuljin()
{
- new boss_zuljin();
- new npc_zuljin_vortex();
+ RegisterZulAmanCreatureAI(boss_zuljin);
+ RegisterZulAmanCreatureAI(npc_zuljin_vortex);
+ RegisterZulAmanCreatureAI(npc_zuljin_spirit);
+ RegisterSpellScript(spell_zuljin_zap);
+ RegisterSpellScript(spell_zuljin_claw_rage);
+ RegisterSpellScript(spell_zuljin_claw_rage_periodic);
+ RegisterSpellScript(spell_zuljin_lynx_rush);
}
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp
index 736cdd27286..b1a795d2616 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp
@@ -74,14 +74,18 @@ static DoorData const doorData[] =
static ObjectData const creatureData[] =
{
- { NPC_HARRISON_JONES, NPC_HARRISON_JONES },
- { NPC_NALORAKK, BOSS_NALORAKK },
- { NPC_AKILZON, BOSS_AKILZON },
- { NPC_JANALAI, BOSS_JANALAI },
- { NPC_HALAZZI, BOSS_HALAZZI },
- { NPC_HEXLORD, BOSS_HEXLORD },
- { NPC_ZULJIN, BOSS_ZULJIN },
- { 0, 0 } // END
+ { NPC_HARRISON_JONES, NPC_HARRISON_JONES },
+ { NPC_NALORAKK, BOSS_NALORAKK },
+ { NPC_AKILZON, BOSS_AKILZON },
+ { NPC_JANALAI, BOSS_JANALAI },
+ { NPC_HALAZZI, BOSS_HALAZZI },
+ { NPC_HEXLORD, BOSS_HEXLORD },
+ { NPC_ZULJIN, BOSS_ZULJIN },
+ { NPC_BEAR_SPIRIT, DATA_BEAR_SPIRIT },
+ { NPC_EAGLE_SPIRIT, DATA_EAGLE_SPIRIT },
+ { NPC_LYNX_SPIRIT, DATA_LYNX_SPIRIT },
+ { NPC_DRAGONHAWK_SPIRIT, DATA_DRAGONHAWK_SPIRIT },
+ { 0, 0 } // END
};
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h
index be5b9c3ba2a..b5c738e978f 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h
+++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h
@@ -36,7 +36,12 @@ enum ZADataTypes
DATA_GONGEVENT,
DATA_CHESTLOOTED,
TYPE_RAND_VENDOR_1,
- TYPE_RAND_VENDOR_2
+ TYPE_RAND_VENDOR_2,
+
+ DATA_BEAR_SPIRIT,
+ DATA_EAGLE_SPIRIT,
+ DATA_LYNX_SPIRIT,
+ DATA_DRAGONHAWK_SPIRIT
};
enum ZACreatureIds
@@ -47,7 +52,13 @@ enum ZACreatureIds
NPC_JANALAI = 23578,
NPC_HALAZZI = 23577,
NPC_HEXLORD = 24239,
- NPC_ZULJIN = 23863
+ NPC_ZULJIN = 23863,
+
+ // Zul'jin
+ NPC_BEAR_SPIRIT = 23878,
+ NPC_EAGLE_SPIRIT = 23880,
+ NPC_LYNX_SPIRIT = 23877,
+ NPC_DRAGONHAWK_SPIRIT = 23879
};
enum ZAGameObjectIds
@@ -72,4 +83,6 @@ inline AI* GetZulAmanAI(T* obj)
return GetInstanceAI<AI>(obj, ZulamanScriptName);
}
+#define RegisterZulAmanCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetZulAmanAI)
+
#endif