diff --git a/data/sql/updates/pending_db_world/root_totem.sql b/data/sql/updates/pending_db_world/root_totem.sql new file mode 100644 index 000000000..77acc4594 --- /dev/null +++ b/data/sql/updates/pending_db_world/root_totem.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `creature_template_movement` WHERE `CreatureId` = 24224; +INSERT INTO `creature_template_movement` (`CreatureId`, `Flight`, `Rooted`) VALUES +(24224, 1, 1); diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp index 6dd0b436c..53d2c3d2d 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp @@ -37,9 +37,13 @@ enum Spells SPELL_SHRED_ARMOR = 43243 // Used by Spirit Lynx }; +enum UniqueEvents +{ + EVENT_BERSERK = 0 +}; + enum Hal_CreatureIds { - NPC_SPIRIT_LYNX = 24143, NPC_TOTEM = 24224 }; @@ -63,326 +67,274 @@ enum Yells SAY_DEATH = 5 }; -class boss_halazzi : public CreatureScript +enum Groups { -public: - boss_halazzi() : CreatureScript("boss_halazzi") { } + GROUP_LYNX = 0, + GROUP_HUMAN = 1, + GROUP_MERGE = 2 +}; - struct boss_halazziAI : public ScriptedAI +struct boss_halazzi : public BossAI +{ + boss_halazzi(Creature* creature) : BossAI(creature, DATA_HALAZZIEVENT) { - boss_halazziAI(Creature* creature) : ScriptedAI(creature), summons(me) + scheduler.SetValidator([this] { - instance = creature->GetInstanceScript(); + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } + + void Reset() override + { + BossAI::Reset(); + _transformCount = 0; + _healthCheckPercentage = 0; + _phase = PHASE_NONE; + _lynxFormHealth = me->GetMaxHealth(); + _healthPortion = _lynxFormHealth/4; + _humanFormHealth = (me->GetMaxHealth())/0.66666666; + EnterPhase(PHASE_LYNX); + DoCastSelf(SPELL_DUAL_WIELD, true); + } + + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); + summon->Attack(me->GetVictim(), false); + summon->SetInCombatWithZone(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + ScheduleUniqueTimedEvent(10min, [&] + { + DoCastSelf(SPELL_BERSERK, true); + }, EVENT_BERSERK); + EnterPhase(PHASE_LYNX); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + if (damage >= me->GetHealth() && _phase != PHASE_ENRAGE) + { + damage = 0; } - - InstanceScript* instance; - SummonList summons; - PhaseHalazzi Phase; - - uint32 FrenzyTimer; - uint32 SaberlashTimer; - uint32 ShockTimer; - uint32 TotemTimer; - uint32 CheckTimer; - uint32 BerserkTimer; - uint32 TransformCount; - - ObjectGuid LynxGUID; - - void Reset() override + else { - instance->SetData(DATA_HALAZZIEVENT, NOT_STARTED); - summons.DespawnAll(); - - LynxGUID.Clear(); - TransformCount = 0; - BerserkTimer = 600000; - CheckTimer = 1000; - - DoCast(me, SPELL_DUAL_WIELD, true); - - Phase = PHASE_NONE; - EnterPhase(PHASE_LYNX); - } - - void JustEngagedWith(Unit* /*who*/) override - { - instance->SetData(DATA_HALAZZIEVENT, IN_PROGRESS); - Talk(SAY_AGGRO); - EnterPhase(PHASE_LYNX); - } - - void JustSummoned(Creature* summon) override - { - summon->AI()->AttackStart(me->GetVictim()); - if (summon->GetEntry() == NPC_SPIRIT_LYNX) - LynxGUID = summon->GetGUID(); - summons.Summon(summon); - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth() && Phase != PHASE_ENRAGE) - damage = 0; - } - - void SpellHit(Unit*, SpellInfo const* spell) override - { - if (spell->Id == SPELL_TRANSFORM_SPLIT2) - EnterPhase(PHASE_HUMAN); - } - - void AttackStart(Unit* who) override - { - if (Phase != PHASE_MERGE) - ScriptedAI::AttackStart(who); - } - - void EnterPhase(PhaseHalazzi NextPhase) - { - switch (NextPhase) + if (_phase == PHASE_LYNX || _phase == PHASE_ENRAGE) { - case PHASE_LYNX: - case PHASE_ENRAGE: - if (Phase == PHASE_MERGE) - { - DoCast(me, SPELL_TRANSFORM_MERGE, true); - me->Attack(me->GetVictim(), true); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } - if (Creature* Lynx = ObjectAccessor::GetCreature(*me, LynxGUID)) - Lynx->DisappearAndDie(); - me->SetMaxHealth(600000); - me->SetHealth(600000 - 150000 * TransformCount); - FrenzyTimer = 16000; - SaberlashTimer = 20000; - ShockTimer = 10000; - TotemTimer = 12000; - break; - case PHASE_SPLIT: - Talk(SAY_SPLIT); - DoCast(me, SPELL_TRANSFORM_SPLIT, true); - break; - case PHASE_HUMAN: - //DoCast(me, SPELL_SUMMON_LYNX, true); - DoSpawnCreature(NPC_SPIRIT_LYNX, 5, 5, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); - me->SetMaxHealth(400000); - me->SetHealth(400000); - ShockTimer = 10000; - TotemTimer = 12000; - break; - case PHASE_MERGE: - if (Unit* pLynx = ObjectAccessor::GetUnit(*me, LynxGUID)) - { - Talk(SAY_MERGE); - pLynx->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - pLynx->GetMotionMaster()->Clear(); - pLynx->GetMotionMaster()->MoveFollow(me, 0, 0); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFollow(pLynx, 0, 0); - ++TransformCount; - } - break; - default: - break; + _healthCheckPercentage = 25 * (3 - _transformCount); + if (!HealthAbovePct(_healthCheckPercentage)) + { + EnterPhase(PHASE_SPLIT); + } } - Phase = NextPhase; - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (BerserkTimer <= diff) + else if (_phase == PHASE_HUMAN) { - DoCast(me, SPELL_BERSERK, true); - BerserkTimer = 60000; + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + if (!HealthAbovePct(20) || !lynx->HealthAbovePct(20)) + { + EnterPhase(PHASE_MERGE); + } + } + else + { + //should not really happen + EnterEvadeMode(); + } } - else BerserkTimer -= diff; + } + } - if (Phase == PHASE_LYNX || Phase == PHASE_ENRAGE) - { - if (SaberlashTimer <= diff) + void SpellHit(Unit*, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TRANSFORM_SPLIT2) + { + EnterPhase(PHASE_HUMAN); + } + } + + void AttackStart(Unit* who) override + { + if (_phase != PHASE_MERGE) + { + BossAI::AttackStart(who); + } + } + + void EnterPhase(PhaseHalazzi nextPhase) + { + switch (nextPhase) + { + case PHASE_LYNX: + case PHASE_ENRAGE: + if (_phase == PHASE_MERGE) + { + DoCastSelf(SPELL_TRANSFORM_MERGE, true); + me->RemoveAurasDueToSpell(SPELL_TRANSFORM_SPLIT2); + me->GetMotionMaster()->MoveChase(me->GetVictim()); + } + summons.DespawnAll(); + me->SetMaxHealth(_lynxFormHealth); + me->SetHealth(_lynxFormHealth - _healthPortion * _transformCount); + scheduler.CancelGroup(GROUP_MERGE); + scheduler.Schedule(16s, GROUP_LYNX, [this](TaskContext context) + { + DoCastSelf(SPELL_FRENZY); + context.Repeat(10s, 15s); + }).Schedule(20s, GROUP_LYNX, [this](TaskContext context) { Talk(SAY_SABER); - // A tank with more than 490 defense skills should receive no critical hit - //DoCast(me, 41296, true); DoCastVictim(SPELL_SABER_LASH, true); - //me->RemoveAurasDueToSpell(41296); - SaberlashTimer = 30000; - } - else SaberlashTimer -= diff; - - if (FrenzyTimer <= diff) - { - DoCast(me, SPELL_FRENZY); - FrenzyTimer = urand(10000, 15000); - } - else FrenzyTimer -= diff; - - if (Phase == PHASE_LYNX) - { - if (CheckTimer <= diff) - { - if (HealthBelowPct(25 * (3 - TransformCount))) - EnterPhase(PHASE_SPLIT); - CheckTimer = 1000; - } - else CheckTimer -= diff; - } - } - - if (Phase == PHASE_HUMAN || Phase == PHASE_ENRAGE) - { - if (TotemTimer <= diff) - { - DoCast(me, SPELL_SUMMON_TOTEM); - TotemTimer = 20000; - } - else TotemTimer -= diff; - - if (ShockTimer <= diff) + context.Repeat(30s); + }); + break; + case PHASE_SPLIT: + Talk(SAY_SPLIT); + DoCastSelf(SPELL_TRANSFORM_SPLIT, true); + break; + case PHASE_HUMAN: + scheduler.CancelGroup(GROUP_MERGE); + DoCastSelf(SPELL_SUMMON_LYNX, true); + me->SetMaxHealth(_humanFormHealth); + me->SetHealth(_humanFormHealth); + scheduler.CancelGroup(GROUP_LYNX); + scheduler.Schedule(10s, GROUP_HUMAN, [this](TaskContext context) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) { if (target->IsNonMeleeSpellCast(false)) + { DoCast(target, SPELL_EARTHSHOCK); + } else + { DoCast(target, SPELL_FLAMESHOCK); - ShockTimer = urand(10000, 15000); - } - } - else ShockTimer -= diff; - - if (Phase == PHASE_HUMAN) - { - if (CheckTimer <= diff) - { - if (!HealthAbovePct(20) /*HealthBelowPct(10)*/) - EnterPhase(PHASE_MERGE); - else - { - Unit* Lynx = ObjectAccessor::GetUnit(*me, LynxGUID); - if (Lynx && !Lynx->HealthAbovePct(20) /*Lynx->HealthBelowPct(10)*/) - EnterPhase(PHASE_MERGE); - } - CheckTimer = 1000; - } - else CheckTimer -= diff; - } - } - - if (Phase == PHASE_MERGE) - { - if (CheckTimer <= diff) - { - Unit* Lynx = ObjectAccessor::GetUnit(*me, LynxGUID); - if (Lynx) - { - Lynx->GetMotionMaster()->MoveFollow(me, 0, 0); - me->GetMotionMaster()->MoveFollow(Lynx, 0, 0); - if (me->IsWithinDistInMap(Lynx, 6.0f)) - { - if (TransformCount < 3) - EnterPhase(PHASE_LYNX); - else - EnterPhase(PHASE_ENRAGE); } } - CheckTimer = 1000; + context.Repeat(10s, 15s); + }).Schedule(12s, GROUP_HUMAN, [this](TaskContext context) + { + DoCastSelf(SPELL_SUMMON_TOTEM); + context.Repeat(20s); + }); + break; + case PHASE_MERGE: + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + Talk(SAY_MERGE); + scheduler.CancelGroup(GROUP_HUMAN); + lynx->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + lynx->GetMotionMaster()->Clear(); + lynx->GetMotionMaster()->MoveFollow(me, 0, 0); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveFollow(lynx, 0, 0); + ++_transformCount; + scheduler.Schedule(2s, GROUP_MERGE, [this](TaskContext context) + { + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + if (me->IsWithinDistInMap(lynx, 6.0f)) + { + if (_transformCount < 3) + { + EnterPhase(PHASE_LYNX); + } + else + { + EnterPhase(PHASE_ENRAGE); + } + } + } + context.Repeat(2s); + }); } - else CheckTimer -= diff; - } - - DoMeleeAttackIfReady(); + break; + default: + break; } + _phase = nextPhase; + } - void KilledUnit(Unit* victim) override + void KilledUnit(Unit* victim) override + { + BossAI::KilledUnit(victim); + if (victim->IsPlayer()) { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - Talk(SAY_KILL); } - - void JustDied(Unit* /*killer*/) override - { - instance->SetData(DATA_HALAZZIEVENT, DONE); - Talk(SAY_DEATH); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulAmanAI(creature); } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } +private: + uint32 _lynxFormHealth; + uint32 _humanFormHealth; + uint32 _healthPortion; + uint8 _transformCount; + uint32 _healthCheckPercentage; + PhaseHalazzi _phase; }; - // Spirits Lynx AI -class npc_halazzi_lynx : public CreatureScript +struct npc_halazzi_lynx : public ScriptedAI { -public: - npc_halazzi_lynx() : CreatureScript("npc_halazzi_lynx") { } + npc_halazzi_lynx(Creature* creature) : ScriptedAI(creature) { } - struct npc_halazzi_lynxAI : public ScriptedAI + void Reset() override { - npc_halazzi_lynxAI(Creature* creature) : ScriptedAI(creature) { } + scheduler.CancelAll(); + } - uint32 FrenzyTimer; - uint32 shredder_timer; - - void Reset() override - { - FrenzyTimer = urand(30000, 50000); //frenzy every 30-50 seconds - shredder_timer = 4000; - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth()) - damage = 0; - } - - void AttackStart(Unit* who) override - { - if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) - ScriptedAI::AttackStart(who); - } - - void JustEngagedWith(Unit* /*who*/) override {/*DoZoneInCombat();*/ } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (FrenzyTimer <= diff) - { - DoCast(me, SPELL_LYNX_FRENZY); - FrenzyTimer = urand(30000, 50000); //frenzy every 30-50 seconds - } - else FrenzyTimer -= diff; - - if (shredder_timer <= diff) - { - DoCastVictim(SPELL_SHRED_ARMOR); - shredder_timer = 4000; - } - else shredder_timer -= diff; - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override { - return GetZulAmanAI(creature); + if (damage >= me->GetHealth()) + { + damage = 0; + } + } + + void AttackStart(Unit* who) override + { + if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) + { + ScriptedAI::AttackStart(who); + } + } + + void JustEngagedWith(Unit* who) override + { + ScriptedAI::JustEngagedWith(who); + + ScheduleTimedEvent(30s, 50s, [&] + { + DoCastSelf(SPELL_LYNX_FRENZY); + }, 30s, 50s); + ScheduleTimedEvent(4s, [&]{ + DoCastVictim(SPELL_SHRED_ARMOR); + }, 4s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + return; + } + + scheduler.Update(diff); + + DoMeleeAttackIfReady(); } }; void AddSC_boss_halazzi() { - new boss_halazzi(); - new npc_halazzi_lynx(); + RegisterZulAmanCreatureAI(boss_halazzi); + RegisterZulAmanCreatureAI(npc_halazzi_lynx); } diff --git a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp index ea7198184..baa83f557 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp @@ -55,6 +55,17 @@ static SHostageInfo HostageInfo[] = Position const HarrisonJonesLoc = {120.687f, 1674.0f, 42.0217f, 1.59044f}; +ObjectData const creatureData[] = +{ + { NPC_SPIRIT_LYNX, DATA_SPIRIT_LYNX }, + { 0, 0 } +}; + +ObjectData const gameObjectData[] = +{ + { 0, 0 } +}; + class instance_zulaman : public InstanceMapScript { public: @@ -92,6 +103,7 @@ public: void Initialize() override { SetHeaders(DataHeader); + LoadObjectData(creatureData, gameObjectData); memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); QuestTimer = 0; @@ -135,6 +147,7 @@ public: default: break; } + InstanceScript::OnCreatureCreate(creature); } void OnGameObjectCreate(GameObject* go) override diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h index d8a5fdd1c..4ca81145b 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h @@ -32,9 +32,10 @@ enum DataTypes DATA_HALAZZIEVENT = 4, DATA_HEXLORDEVENT = 5, DATA_ZULJINEVENT = 6, - DATA_CHESTLOOTED = 7, - TYPE_RAND_VENDOR_1 = 8, - TYPE_RAND_VENDOR_2 = 9 + DATA_SPIRIT_LYNX = 7, + DATA_CHESTLOOTED = 8, + TYPE_RAND_VENDOR_1 = 9, + TYPE_RAND_VENDOR_2 = 10 }; enum CreatureIds @@ -44,7 +45,8 @@ enum CreatureIds NPC_ZULJIN = 23863, NPC_HEXLORD = 24239, NPC_HALAZZI = 23577, - NPC_NALORAKK = 23576 + NPC_NALORAKK = 23576, + NPC_SPIRIT_LYNX = 24143 }; enum GameobjectIds @@ -68,4 +70,6 @@ inline AI* GetZulAmanAI(T* obj) return GetInstanceAI(obj, ZulAmanScriptName); } +#define RegisterZulAmanCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetZulAmanAI) + #endif