diff options
Diffstat (limited to 'src')
9 files changed, 1100 insertions, 492 deletions
diff --git a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp index 5c8d4b8691a..8b83c9bf3bf 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp @@ -27,7 +27,8 @@ enum Spells SPELL_EARTH_SHIELD = 54479, SPELL_EARTH_SHOCK = 54511, SPELL_LIGHTNING_BOLT = 53044, - SPELL_STORMSTRIKE = 51876 + SPELL_STORMSTRIKE = 51876, + SPELL_WINDFURY = 54493 }; enum Yells @@ -40,6 +41,17 @@ enum Yells SAY_BOTH_ADDS_KILLED = 5 }; +enum ErekemEvents +{ + EVENT_EARTH_SHIELD = 1, + EVENT_CHAIN_HEAL, + EVENT_BLOODLUST, + EVENT_LIGHTNING_BOLT, + EVENT_EARTH_SHOCK, + EVENT_WINDFURY, + EVENT_STORMSTRIKE, +}; + class boss_erekem : public CreatureScript { public: @@ -49,47 +61,57 @@ public: { boss_erekemAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { - uiBloodlustTimer = 15000; - uiChainHealTimer = 0; - uiEarthShockTimer = urand(2000, 8000); - uiLightningBoltTimer = urand(5000, 10000); - uiEarthShieldTimer = 20000; + events.Reset(); + phase = 0; + breakBondsCd = 0; } - uint32 uiBloodlustTimer; - uint32 uiChainHealTimer; - uint32 uiEarthShockTimer; - uint32 uiLightningBoltTimer; - uint32 uiEarthShieldTimer; - - InstanceScript* instance; - void Reset() override { Initialize(); + if (instance->GetData(DATA_WAVE_COUNT) == 6) instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) { - if (!pGuard1->IsAlive()) - pGuard1->Respawn(); + if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + pGuard1->DespawnOrUnsummon(); + if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + pGuard2->DespawnOrUnsummon(); } - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + else { - if (!pGuard2->IsAlive()) - pGuard2->Respawn(); + if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + { + if (!pGuard1->IsAlive()) + pGuard1->Respawn(); + } + if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + { + if (!pGuard2->IsAlive()) + pGuard2->Respawn(); + } } } + void JustReachedHome() override + { + if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + pGuard1->Respawn(); + + if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + pGuard2->Respawn(); + + } + void AttackStart(Unit* who) override { if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) @@ -104,13 +126,13 @@ public: if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) { - pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); + pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); if (!pGuard1->GetVictim() && pGuard1->AI()) pGuard1->AI()->AttackStart(who); } if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) { - pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); + pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); if (!pGuard2->GetVictim() && pGuard2->AI()) pGuard2->AI()->AttackStart(who); } @@ -133,68 +155,13 @@ public: instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - } - - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - //spam stormstrike in hc mode if spawns are dead - if (IsHeroic()) - { - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) - { - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) - { - if (!pGuard1->IsAlive() && !pGuard2->IsAlive()) - DoCastVictim(SPELL_STORMSTRIKE); - } - } - } - - if (uiEarthShieldTimer <= diff) - { - DoCast(me, SPELL_EARTH_SHIELD); - uiEarthShieldTimer = 20000; - } else uiEarthShieldTimer -= diff; - - if (uiChainHealTimer <= diff) - { - if (ObjectGuid TargetGUID = GetChainHealTargetGUID()) - { - if (Creature* target = ObjectAccessor::GetCreature(*me, TargetGUID)) - DoCast(target, SPELL_CHAIN_HEAL); - - //If one of the adds is dead spawn heals faster - Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)); - Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)); - uiChainHealTimer = ((pGuard1 && !pGuard1->IsAlive()) || (pGuard2 && !pGuard2->IsAlive()) ? 3000 : 8000) + rand32() % 3000; - } - } else uiChainHealTimer -= diff; - if (uiBloodlustTimer <= diff) - { - DoCast(me, SPELL_BLOODLUST); - uiBloodlustTimer = urand(35000, 45000); - } else uiBloodlustTimer -= diff; - - if (uiEarthShockTimer <= diff) - { - DoCastVictim(SPELL_EARTH_SHOCK); - uiEarthShockTimer = urand(8000, 13000); - } else uiEarthShockTimer -= diff; - - if (uiLightningBoltTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_LIGHTNING_BOLT); - uiLightningBoltTimer = urand(18000, 24000); - } else uiLightningBoltTimer -= diff; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); - DoMeleeAttackIfReady(); + events.ScheduleEvent(EVENT_EARTH_SHIELD, 20000); + events.ScheduleEvent(EVENT_BLOODLUST, 15000); + events.ScheduleEvent(EVENT_CHAIN_HEAL, 10000); + events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 2000); } void JustDied(Unit* /*killer*/) override @@ -219,21 +186,115 @@ public: Talk(SAY_SLAY); } - ObjectGuid GetChainHealTargetGUID() + void UpdateAI(uint32 diff) override { - if (HealthBelowPct(85)) - return me->GetGUID(); + if (!UpdateVictim()) + return; - Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)); - if (pGuard1 && pGuard1->IsAlive() && !pGuard1->HealthAbovePct(75)) - return pGuard1->GetGUID(); + if (phase == 0) + if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + { + if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + { + if (!pGuard1->IsAlive() && !pGuard2->IsAlive()) + { + phase = 1; + DoCastVictim(SPELL_STORMSTRIKE); + DoCast(SPELL_WINDFURY); + events.Reset(); + events.ScheduleEvent(EVENT_EARTH_SHOCK, urand(2000, 8000)); + events.ScheduleEvent(EVENT_WINDFURY, urand(1500, 2000)); + events.ScheduleEvent(EVENT_STORMSTRIKE, urand(1500, 2000)); + } + } + } + + events.Update(diff); - Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)); - if (pGuard2 && pGuard2->IsAlive() && !pGuard2->HealthAbovePct(75)) - return pGuard2->GetGUID(); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - return ObjectGuid::Empty; + if (breakBondsCd <= 0) + { + if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + { + if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + { + if (pGuard1->IsAlive()) + { + if (pGuard1->HasAuraType(SPELL_AURA_MOD_STUN) || pGuard1->HasAuraType(SPELL_AURA_MOD_ROOT) + || pGuard1->HasAuraType(SPELL_AURA_MOD_CONFUSE) || pGuard1->HasAuraType(SPELL_AURA_MOD_PACIFY) + || pGuard1->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) + { + DoCast(SPELL_BREAK_BONDS); + breakBondsCd = 10000; + return; + } + } + if (pGuard2->IsAlive()) + { + if (pGuard2->HasAuraType(SPELL_AURA_MOD_STUN) || pGuard2->HasAuraType(SPELL_AURA_MOD_ROOT) + || pGuard2->HasAuraType(SPELL_AURA_MOD_CONFUSE) || pGuard2->HasAuraType(SPELL_AURA_MOD_PACIFY) + || pGuard2->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) + { + DoCast(SPELL_BREAK_BONDS); + breakBondsCd = 10000; + return; + } + } + } + } + } + else + breakBondsCd -= diff; + + switch (uint32 eventId = events.ExecuteEvent()) + { + case EVENT_EARTH_SHIELD: + if (Unit* ally = DoSelectLowestHpFriendly(30.0f)) + DoCast(ally, SPELL_EARTH_SHIELD); + events.ScheduleEvent(EVENT_EARTH_SHIELD, 20000); + break; + case EVENT_BLOODLUST: + DoCast(SPELL_BLOODLUST); + events.ScheduleEvent(EVENT_BLOODLUST, urand(35000, 45000)); + break; + case EVENT_LIGHTNING_BOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_LIGHTNING_BOLT); + events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 2500); + break; + case EVENT_CHAIN_HEAL: + if (Unit* ally = DoSelectLowestHpFriendly(40.0f)) + DoCast(ally, SPELL_CHAIN_HEAL); + { + Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)); + Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)); + events.ScheduleEvent(EVENT_CHAIN_HEAL, ((pGuard1 && !pGuard1->IsAlive()) || (pGuard2 && !pGuard2->IsAlive()) ? 3000 : 8000 + rand() % 3000)); + } + break; + case EVENT_EARTH_SHOCK: + DoCastVictim(SPELL_EARTH_SHOCK); + events.ScheduleEvent(EVENT_EARTH_SHOCK, urand(8000, 13000)); + break; + case EVENT_WINDFURY: + DoCast(SPELL_WINDFURY); + events.ScheduleEvent(EVENT_WINDFURY, urand(1500, 2000)); + break; + case EVENT_STORMSTRIKE: + DoCastVictim(SPELL_STORMSTRIKE); + events.ScheduleEvent(EVENT_STORMSTRIKE, urand(1500, 2000)); + break; + } + + DoMeleeAttackIfReady(); } + + private: + EventMap events; + InstanceScript* instance; + uint8 phase; + int32 breakBondsCd; }; CreatureAI* GetAI(Creature* creature) const override @@ -278,6 +339,9 @@ public: void Reset() override { Initialize(); + + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); } void AttackStart(Unit* who) override diff --git a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp index 9be73febd52..2de6d1b576f 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp @@ -27,12 +27,16 @@ enum Spells SPELL_WATER_BLAST = 54237, SPELL_WATER_BOLT_VOLLEY = 54241, SPELL_SPLASH = 59516, - SPELL_WATER_GLOBULE = 54268 + SPELL_BURST = 54379, + SPELL_WATER_GLOBULE = 54268, + SPELL_MERGE = 54269, + SPELL_WATER_GLOBULE_VISUAL = 54260 }; enum IchoronCreatures { - NPC_ICHOR_GLOBULE = 29321 + NPC_ICHOR_GLOBULE = 29321, + NPC_ICHORON_SUMMON_TARGET = 29326 }; enum Yells @@ -49,25 +53,44 @@ enum Yells enum Actions { ACTION_WATER_ELEMENT_HIT = 1, - ACTION_WATER_ELEMENT_KILLED = 2 }; -/// @todo get those positions from spawn of creature 29326 -#define MAX_SPAWN_LOC 5 -static Position const SpawnLoc[MAX_SPAWN_LOC]= +enum IchoronEvents { - {1840.64f, 795.407f, 44.079f, 1.676f}, - {1886.24f, 757.733f, 47.750f, 5.201f}, - {1877.91f, 845.915f, 43.417f, 3.560f}, - {1918.97f, 850.645f, 47.225f, 4.136f}, - {1935.50f, 796.224f, 52.492f, 4.224f} + EVENT_WATER_BLAST = 1, + EVENT_WATER_BOLT_VOLLEY, +}; + +enum GlobuleEvents +{ + EVENT_GLOBULE_MOVE = 1, }; enum Misc { + DATA_GLOBULE_PATH = 0, DATA_DEHYDRATION = 1 }; +Position globulePaths[10] = +{ + // first target + { 1861.357f, 804.039f, 44.008f, 6.268f }, + { 1869.375f, 803.976f, 38.781f, 0.009f }, + // second target + { 1888.063f, 763.488f, 47.667f, 1.744f }, + { 1882.865f, 776.385f, 38.824f, 1.882f }, + // third target + { 1935.140f, 817.752f, 52.181f, 1.885f }, + { 1916.642f, 826.337f, 39.139f, 2.851f }, + // fourth target + { 1930.257f, 833.053f, 46.906f, 4.579f }, + { 1916.642f, 826.337f, 39.139f, 2.851f }, + // fifth target + { 1878.248f, 841.883f, 43.334f, 4.717f }, + { 1879.438f, 834.443f, 38.699f, 4.831f } +}; + class boss_ichoron : public CreatureScript { public: @@ -78,33 +101,24 @@ public: boss_ichoronAI(Creature* creature) : ScriptedAI(creature), m_waterElements(creature) { Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } void Initialize() { bIsExploded = false; bIsFrenzy = false; + bIsDrained = false; dehydration = true; - uiBubbleCheckerTimer = 1000; - uiWaterBoltVolleyTimer = urand(10000, 15000); + drainedTimer = 50; + burstTimer = 15000; } - bool bIsExploded; - bool bIsFrenzy; - bool dehydration; - - uint32 uiBubbleCheckerTimer; - uint32 uiWaterBoltVolleyTimer; - - InstanceScript* instance; - - SummonList m_waterElements; - void Reset() override { Initialize(); + events.Reset(); me->SetVisible(true); DespawnWaterElements(); @@ -131,6 +145,9 @@ public: instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + + events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000)); + events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000)); } void AttackStart(Unit* who) override @@ -155,18 +172,16 @@ public: switch (param) { case ACTION_WATER_ELEMENT_HIT: - me->ModifyHealth(int32(me->CountPctFromMaxHealth(1))); - + { if (bIsExploded) DoExplodeCompleted(); - dehydration = false; - break; - case ACTION_WATER_ELEMENT_KILLED: - uint32 damage = me->CountPctFromMaxHealth(3); - me->ModifyHealth(-int32(damage)); - me->LowerPlayerDamageReq(damage); - break; + me->SetHealth(me->GetHealth() + me->CountPctFromMaxHealth(3)); + + if (dehydration) + dehydration = false; + } + break; } } @@ -180,6 +195,7 @@ public: void DoExplodeCompleted() { bIsExploded = false; + bIsDrained = false; if (!HealthBelowPct(25)) { @@ -199,74 +215,24 @@ public: return 0; } - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 uiDiff) override + void MoveInLineOfSight(Unit* who) override { - if (!UpdateVictim()) + if (!who->ToCreature()) return; - if (!bIsFrenzy && HealthBelowPct(25) && !bIsExploded) - { - Talk(SAY_ENRAGE); - DoCast(me, SPELL_FRENZY, true); - bIsFrenzy = true; - } - - if (!bIsFrenzy) - { - if (uiBubbleCheckerTimer <= uiDiff) - { - if (!bIsExploded) - { - if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE)) - { - Talk(SAY_SHATTER); - DoCast(me, SPELL_WATER_BLAST); // wrong target - DoCast(me, SPELL_DRAINED); - bIsExploded = true; - me->AttackStop(); - me->SetVisible(false); - for (uint8 i = 0; i < 10; i++) - { - int tmp = urand(0, MAX_SPAWN_LOC-1); - me->SummonCreature(NPC_ICHOR_GLOBULE, SpawnLoc[tmp], TEMPSUMMON_CORPSE_DESPAWN); - } - } - } - else - { - bool bIsWaterElementsAlive = false; - if (!m_waterElements.empty()) - { - for (SummonList::const_iterator itr = m_waterElements.begin(); itr != m_waterElements.end(); ++itr) - if (Creature* temp = ObjectAccessor::GetCreature(*me, *itr)) - if (temp->IsAlive()) - { - bIsWaterElementsAlive = true; - break; - } - } + if (who->GetEntry() != NPC_ICHOR_GLOBULE) + return; - if (!bIsWaterElementsAlive) - DoExplodeCompleted(); - } - uiBubbleCheckerTimer = 1000; - } - else uiBubbleCheckerTimer -= uiDiff; - } + if (!me->IsWithinDist(who, 4.0f, false)) + return; - if (!bIsExploded) - { - if (uiWaterBoltVolleyTimer <= uiDiff) - { - DoCast(me, SPELL_WATER_BOLT_VOLLEY); - uiWaterBoltVolleyTimer = urand(10000, 15000); - } - else uiWaterBoltVolleyTimer -= uiDiff; + if (who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + return; - DoMeleeAttackIfReady(); - } + who->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + who->CastSpell(who, SPELL_MERGE); + DoAction(ACTION_WATER_ELEMENT_HIT); + who->ToCreature()->DespawnOrUnsummon(1000); } void JustDied(Unit* /*killer*/) override @@ -295,22 +261,23 @@ public: void JustSummoned(Creature* summoned) override { - if (summoned) - { - summoned->SetSpeed(MOVE_RUN, 0.3f); - summoned->GetMotionMaster()->MoveFollow(me, 0, 0); - m_waterElements.Summon(summoned); - instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); - } + summoned->SetSpeed(MOVE_RUN, 0.3f); + m_waterElements.Summon(summoned); + + instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); } void SummonedCreatureDespawn(Creature* summoned) override { - if (summoned) + m_waterElements.Despawn(summoned); + + if (m_waterElements.empty() && bIsExploded) { - m_waterElements.Despawn(summoned); - instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); + me->RemoveAllAuras(); + DoExplodeCompleted(); } + + instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); } void KilledUnit(Unit* victim) override @@ -318,6 +285,145 @@ public: if (victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_SLAY); } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (!bIsFrenzy && HealthBelowPct(25) && !bIsExploded) + { + Talk(SAY_ENRAGE); + DoCast(me, SPELL_FRENZY, true); + bIsFrenzy = true; + } + + if (!bIsFrenzy) + { + if (!bIsExploded) + { + if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE)) + { + bIsExploded = true; + Talk(SAY_SHATTER); + DoCast(SPELL_BURST); + me->RemoveAllAuras(); + burstTimer = 15000; + + std::list<Creature*> summonTargets; + GetCreatureListWithEntryInGrid(summonTargets, me, NPC_ICHORON_SUMMON_TARGET, 200.0f); + std::list<Creature*>::iterator itr = summonTargets.begin(); + + for (uint8 i = 0; i < 10; i++) + { + std::advance(itr, urand(0, summonTargets.size() - 1)); // I take a random minion in the list + Position targetPos = (*itr)->GetRandomNearPosition(10.0f); + itr = summonTargets.begin(); + TempSummon* globule = me->SummonCreature(NPC_ICHOR_GLOBULE, targetPos, TEMPSUMMON_CORPSE_DESPAWN); + DoCast(globule, SPELL_WATER_GLOBULE_VISUAL); + + float minDistance = 1000.0f; + uint8 nextPath = 0; + // I move the globules to next position. the 10 positions are in couples, defined in globulePaths, so i have to increase by 2. + for (uint8 gpath = 0; gpath < 10; gpath += 2) + { + if (globule->GetDistance(globulePaths[gpath]) < minDistance) + { + minDistance = globule->GetDistance(globulePaths[gpath]); + nextPath = gpath; + } + } + + globule->GetAI()->SetData(DATA_GLOBULE_PATH, nextPath); + } + return; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + events.Update(diff); + + switch (uint32 eventId = events.ExecuteEvent()) + { + case EVENT_WATER_BLAST: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_WATER_BLAST); + events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000)); + break; + case EVENT_WATER_BOLT_VOLLEY: + DoCast(SPELL_WATER_BOLT_VOLLEY); + events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000)); + break; + } + + DoMeleeAttackIfReady(); + } + else if (!bIsDrained) + { + if (drainedTimer <= 0) + { + bIsDrained = true; + drainedTimer = 50; + uint32 damage = me->CountPctFromMaxHealth(30); + if (me->GetHealth() < damage) + me->SetHealth(me->CountPctFromMaxHealth(1)); + else + { + me->SetHealth(me->GetHealth() - damage); + me->LowerPlayerDamageReq(damage); + } + DoCast(SPELL_DRAINED); + me->SetVisible(false); + me->AttackStop(); + } + else + drainedTimer -= diff; + } + else if (bIsDrained) + { + if (burstTimer <= 0) + { + DoExplodeCompleted(); + } + else + burstTimer -= diff; + } + } + else + { + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + events.Update(diff); + + switch (uint32 eventId = events.ExecuteEvent()) + { + case EVENT_WATER_BLAST: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_WATER_BLAST); + events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000)); + break; + case EVENT_WATER_BOLT_VOLLEY: + DoCast(SPELL_WATER_BOLT_VOLLEY); + events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000)); + break; + } + + DoMeleeAttackIfReady(); + } + } + + private: + InstanceScript* instance; + SummonList m_waterElements; + EventMap events; + bool bIsExploded; + bool bIsFrenzy; + bool bIsDrained; + bool dehydration; + int32 drainedTimer; + int32 burstTimer; }; CreatureAI* GetAI(Creature* creature) const override @@ -335,54 +441,72 @@ public: { npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); instance = creature->GetInstanceScript(); } - void Initialize() + void Reset() override { - uiRangeCheck_Timer = 1000; + pathId = 0; + events.Reset(); + DoCast(SPELL_WATER_GLOBULE); } - InstanceScript* instance; - - uint32 uiRangeCheck_Timer; - - void Reset() override + void AttackStart(Unit* /*who*/) override { - Initialize(); - DoCast(me, SPELL_WATER_GLOBULE); } - void AttackStart(Unit* /*who*/) override + void SetData(uint32 id, uint32 data) override { + if (id == DATA_GLOBULE_PATH) + { + pathId = data; + me->GetMotionMaster()->MovePoint(0, globulePaths[pathId]); + } } - void UpdateAI(uint32 uiDiff) override + void MovementInform(uint32 type, uint32 id) override { - if (uiRangeCheck_Timer < uiDiff) + if (type != POINT_MOTION_TYPE) + return; + + switch (id) { - if (Creature* ichoron = instance->GetCreature(DATA_ICHORON)) - { - if (me->IsWithinDist(ichoron, 2.0f, false)) - { - if (ichoron->AI()) - ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_HIT); - me->DespawnOrUnsummon(); - } - } - uiRangeCheck_Timer = 1000; + case 0: + me->GetMotionMaster()->MovementExpired(); + events.ScheduleEvent(EVENT_GLOBULE_MOVE, 500); + break; + case 1: + me->GetMotionMaster()->MovementExpired(); + if (Creature* ichoron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ICHORON))) + me->GetMotionMaster()->MoveFollow(ichoron, 0.0f, 0.0f); + break; } - else uiRangeCheck_Timer -= uiDiff; } - void JustDied(Unit* /*killer*/) override + // on retail spell casted on a creature's death are not casted after death but keeping mob at 1 health, casting it and then letting the mob die. + // this feature should be still implemented + void DamageTaken(Unit* attacker, uint32 &damage) override { - DoCast(me, SPELL_SPLASH); - if (Creature* ichoron = instance->GetCreature(DATA_ICHORON)) - if (ichoron->AI()) - ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_KILLED); + int32 actualHp = me->GetHealth(); + actualHp -= damage; + + if (actualHp <= 0) + DoCast(SPELL_SPLASH); } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (events.ExecuteEvent() == EVENT_GLOBULE_MOVE) + me->GetMotionMaster()->MovePoint(1, globulePaths[pathId + 1]); + + } + + private: + InstanceScript* instance; + EventMap events; + uint8 pathId; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp index 5040dccfa36..62ea86c04a4 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp @@ -24,7 +24,15 @@ enum Spells SPELL_CAUTERIZING_FLAMES = 59466, // Only in heroic SPELL_FIREBOLT = 54235, SPELL_FLAME_BREATH = 54282, - SPELL_LAVA_BURN = 54249 + SPELL_LAVA_BURN = 54249, +}; + +enum LavanthorEvents +{ + EVENT_CAUTERIZING_FLAMES = 1, + EVENT_FIREBOLT, + EVENT_FLAME_BREATH, + EVENT_LAVA_BURN, }; class boss_lavanthor : public CreatureScript @@ -36,32 +44,17 @@ public: { boss_lavanthorAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); instance = creature->GetInstanceScript(); } - void Initialize() - { - uiFireboltTimer = 1000; - uiFlameBreathTimer = 5000; - uiLavaBurnTimer = 10000; - uiCauterizingFlamesTimer = 3000; - } - - uint32 uiFireboltTimer; - uint32 uiFlameBreathTimer; - uint32 uiLavaBurnTimer; - uint32 uiCauterizingFlamesTimer; - - InstanceScript* instance; - void Reset() override { - Initialize(); if (instance->GetData(DATA_WAVE_COUNT) == 6) instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); + + events.Reset(); } void EnterCombat(Unit* /*who*/) override @@ -72,11 +65,16 @@ public: EnterEvadeMode(); return; } - if (instance->GetData(DATA_WAVE_COUNT) == 6) instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + + events.ScheduleEvent(EVENT_FIREBOLT, 1000); + events.ScheduleEvent(EVENT_FLAME_BREATH, 5000); + events.ScheduleEvent(EVENT_LAVA_BURN, 10000); + if (IsHeroic()) + events.ScheduleEvent(EVENT_CAUTERIZING_FLAMES, 3000); } void AttackStart(Unit* who) override @@ -93,39 +91,36 @@ public: } } - void MoveInLineOfSight(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - if (uiFireboltTimer <= diff) - { - DoCastVictim(SPELL_FIREBOLT); - uiFireboltTimer = urand(5000, 13000); - } else uiFireboltTimer -= diff; + events.Update(diff); - if (uiFlameBreathTimer <= diff) - { - DoCastVictim(SPELL_FLAME_BREATH); - uiFlameBreathTimer = urand(10000, 15000); - } else uiFlameBreathTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (uiLavaBurnTimer <= diff) + switch (uint32 eventId = events.ExecuteEvent()) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true)) + case EVENT_FIREBOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_FIREBOLT); + events.ScheduleEvent(EVENT_FIREBOLT, urand(5000, 13000)); + break; + case EVENT_FLAME_BREATH: + DoCast(SPELL_FLAME_BREATH); + events.ScheduleEvent(EVENT_FLAME_BREATH, urand(10000, 15000)); + break; + case EVENT_LAVA_BURN: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) DoCast(target, SPELL_LAVA_BURN); - uiLavaBurnTimer = urand(15000, 23000); - } else uiLavaBurnTimer -= diff; - - if (IsHeroic()) - { - if (uiCauterizingFlamesTimer <= diff) - { - DoCastVictim(SPELL_CAUTERIZING_FLAMES); - uiCauterizingFlamesTimer = urand(10000, 16000); - } else uiCauterizingFlamesTimer -= diff; + events.ScheduleEvent(EVENT_LAVA_BURN, urand(15000, 23000)); + break; + case EVENT_CAUTERIZING_FLAMES: + DoCast(SPELL_CAUTERIZING_FLAMES); + events.ScheduleEvent(EVENT_CAUTERIZING_FLAMES, urand(10000, 16000)); + break; } DoMeleeAttackIfReady(); @@ -144,6 +139,10 @@ public: instance->SetData(DATA_WAVE_COUNT, 13); } } + + private: + EventMap events; + InstanceScript* instance; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp index 1c98806b127..df01b0d4a17 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp @@ -24,7 +24,18 @@ enum Spells SPELL_CORROSIVE_SALIVA = 54527, SPELL_OPTIC_LINK = 54396, SPELL_RAY_OF_PAIN = 54438, // NYI missing spelldifficulty - SPELL_RAY_OF_SUFFERING = 54442 // NYI missing spelldifficulty + SPELL_RAY_OF_SUFFERING = 54442, // NYI missing spelldifficulty + + // Visual + SPELL_OPTIC_LINK_LEVEL_1 = 54393, + SPELL_OPTIC_LINK_LEVEL_2 = 54394, + SPELL_OPTIC_LINK_LEVEL_3 = 54395, +}; + +enum MoraggEvents +{ + EVENT_CORROSIVE_SALIVA = 1, + EVENT_OPTIC_LINK, }; class boss_moragg : public CreatureScript @@ -36,24 +47,12 @@ public: { boss_moraggAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); instance = creature->GetInstanceScript(); } - void Initialize() - { - uiOpticLinkTimer = 10000; - uiCorrosiveSalivaTimer = 5000; - } - - uint32 uiOpticLinkTimer; - uint32 uiCorrosiveSalivaTimer; - - InstanceScript* instance; - void Reset() override { - Initialize(); + events.Reset(); if (instance->GetData(DATA_WAVE_COUNT) == 6) instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); @@ -74,6 +73,13 @@ public: instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + + me->SetInCombatWithZone(); + + DoCast(SPELL_RAY_OF_PAIN); + DoCast(SPELL_RAY_OF_SUFFERING); + events.ScheduleEvent(EVENT_OPTIC_LINK, 15000); + events.ScheduleEvent(EVENT_CORROSIVE_SALIVA, 5000); } void AttackStart(Unit* who) override @@ -90,25 +96,28 @@ public: } } - void MoveInLineOfSight(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - if (uiOpticLinkTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_OPTIC_LINK); - uiOpticLinkTimer = 15000; - } else uiOpticLinkTimer -= diff; + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (uiCorrosiveSalivaTimer <= diff) + switch (uint32 eventId = events.ExecuteEvent()) { - DoCastVictim(SPELL_CORROSIVE_SALIVA); - uiCorrosiveSalivaTimer = 10000; - } else uiCorrosiveSalivaTimer -= diff; + case EVENT_OPTIC_LINK: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_OPTIC_LINK); + events.ScheduleEvent(EVENT_OPTIC_LINK, 25000); + break; + case EVENT_CORROSIVE_SALIVA: + DoCastVictim(SPELL_CORROSIVE_SALIVA); + events.ScheduleEvent(EVENT_CORROSIVE_SALIVA, 10000); + break; + } DoMeleeAttackIfReady(); } @@ -126,6 +135,10 @@ public: instance->SetData(DATA_WAVE_COUNT, 13); } } + + private: + EventMap events; + InstanceScript* instance; }; CreatureAI* GetAI(Creature* creature) const override @@ -134,7 +147,149 @@ public: } }; +class spell_moragg_ray_of_suffering : public SpellScriptLoader +{ +public: + spell_moragg_ray_of_suffering() : SpellScriptLoader("spell_moragg_ray_of_suffering") { } + + class spell_moragg_ray_of_suffering_AuraScript : public AuraScript + { + PrepareAuraScript(spell_moragg_ray_of_suffering_AuraScript); + + void OnPeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + std::list<HostileReference*> players = GetTarget()->ToCreature()->getThreatManager().getThreatList(); + if (!players.empty()) + { + std::list<HostileReference*>::iterator itr = players.begin(); + std::advance(itr, urand(0, players.size() - 1)); + + uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; + GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_suffering_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_moragg_ray_of_suffering_AuraScript(); + } +}; + +class spell_moragg_ray_of_pain : public SpellScriptLoader +{ +public: + spell_moragg_ray_of_pain() : SpellScriptLoader("spell_moragg_ray_of_pain") { } + + class spell_moragg_ray_of_pain_AuraScript : public AuraScript + { + PrepareAuraScript(spell_moragg_ray_of_pain_AuraScript); + + void OnPeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + std::list<HostileReference*> players = GetTarget()->ToCreature()->getThreatManager().getThreatList(); + if (!players.empty()) + { + std::list<HostileReference*>::iterator itr = players.begin(); + std::advance(itr, urand(0, players.size() - 1)); + + uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; + GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_pain_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_moragg_ray_of_pain_AuraScript(); + } +}; + +class spell_moragg_optic_link : public SpellScriptLoader +{ +public: + spell_moragg_optic_link() : SpellScriptLoader("spell_moragg_optic_link") { } + + class spell_moragg_optic_link_AuraScript : public AuraScript + { + PrepareAuraScript(spell_moragg_optic_link_AuraScript); + + void OnPeriodic(AuraEffect const* aurEff) + { + switch (aurEff->GetTickNumber()) // Different visual based on tick + { + case 1: + case 2: + case 3: + GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); + break; + case 4: + case 5: + case 6: + case 7: + GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_2, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); + break; + case 8: + case 9: + case 10: + case 11: + GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_2, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_3, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); + break; + default: + break; + } + } + + void OnUpdate(AuraEffect* aurEff) + { + switch (aurEff->GetTickNumber()) + { + case 1: + aurEff->SetAmount(aurEff->GetAmount() + 250); // base amount is 500 + break; + case 4: + aurEff->SetAmount(aurEff->GetAmount() * 2); // goes to 1500 + break; + case 8: + aurEff->SetAmount(aurEff->GetAmount() * 2); // goes to 3000 + break; + default: + break; + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_optic_link_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_moragg_optic_link_AuraScript::OnUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_moragg_optic_link_AuraScript(); + } +}; + void AddSC_boss_moragg() { new boss_moragg(); + new spell_moragg_ray_of_suffering(); + new spell_moragg_ray_of_pain(); + new spell_moragg_optic_link(); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp index d1efcb8ca7a..9deac82ca3a 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp @@ -25,21 +25,23 @@ enum Spells SPELL_ARCANE_BARRAGE_VOLLEY = 54202, SPELL_ARCANE_BUFFET = 54226, SPELL_SUMMON_ETHEREAL_SPHERE_1 = 54102, - SPELL_SUMMON_ETHEREAL_SPHERE_2 = 54137, + SPELL_SUMMON_ETHEREAL_SPHERE_2 = 61337, SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138 }; enum NPCs { NPC_ETHEREAL_SPHERE = 29271, - //NPC_ETHEREAL_SPHERE2 = 32582, // heroic only? + NPC_ETHEREAL_SPHERE2 = 32582 }; enum CreatureSpells { SPELL_ARCANE_POWER = 54160, + H_SPELL_ARCANE_POWER = 59474, SPELL_SUMMON_PLAYERS = 54164, - SPELL_POWER_BALL_VISUAL = 54141 + SPELL_POWER_BALL_VISUAL = 54141, + SPELL_POWER_BALL_DAMAGE_TRIGGER = 54207 }; enum Yells @@ -53,6 +55,17 @@ enum Yells SAY_SUMMON_ENERGY = 6 }; +enum XevozzEvents +{ + EVENT_ARCANE_BARRAGE = 1, + EVENT_ARCANE_BUFFET, + EVENT_SUMMON_SPHERE, + EVENT_SUMMON_SPHERE_2, + EVENT_RANGE_CHECK, + EVENT_SUMMON_PLAYERS, + EVENT_DESPAWN_SPHERE +}; + class boss_xevozz : public CreatureScript { public: @@ -62,23 +75,9 @@ public: { boss_xevozzAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } - void Initialize() - { - uiSummonEtherealSphere_Timer = urand(10000, 12000); - uiArcaneBarrageVolley_Timer = urand(20000, 22000); - uiArcaneBuffet_Timer = uiSummonEtherealSphere_Timer + urand(5000, 6000); - } - - InstanceScript* instance; - - uint32 uiSummonEtherealSphere_Timer; - uint32 uiArcaneBarrageVolley_Timer; - uint32 uiArcaneBuffet_Timer; - void Reset() override { if (instance->GetData(DATA_WAVE_COUNT) == 6) @@ -86,14 +85,15 @@ public: else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - Initialize(); DespawnSphere(); + events.Reset(); } void DespawnSphere() { std::list<Creature*> assistList; GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE, 150.0f); + GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE2, 150.0f); if (assistList.empty()) return; @@ -108,11 +108,7 @@ public: void JustSummoned(Creature* summoned) override { summoned->SetSpeed(MOVE_RUN, 0.5f); - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - summoned->AddThreat(target, 0.00f); - summoned->AI()->AttackStart(target); - } + summoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f); } void AttackStart(Unit* who) override @@ -144,45 +140,10 @@ public: instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - } - - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (uiArcaneBarrageVolley_Timer < diff) - { - DoCast(me, SPELL_ARCANE_BARRAGE_VOLLEY); - uiArcaneBarrageVolley_Timer = urand(20000, 22000); - } - else uiArcaneBarrageVolley_Timer -= diff; - - if (uiArcaneBuffet_Timer) - { - if (uiArcaneBuffet_Timer < diff) - { - DoCastVictim(SPELL_ARCANE_BUFFET); - uiArcaneBuffet_Timer = 0; - } - else uiArcaneBuffet_Timer -= diff; - } - if (uiSummonEtherealSphere_Timer < diff) - { - Talk(SAY_SPAWN); - DoCast(me, SPELL_SUMMON_ETHEREAL_SPHERE_1); - if (IsHeroic()) // extra one for heroic - me->SummonCreature(NPC_ETHEREAL_SPHERE, me->GetPositionX() - 5 + rand32() % 10, me->GetPositionY() - 5 + rand32() % 10, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 40000); - - uiSummonEtherealSphere_Timer = urand(45000, 47000); - uiArcaneBuffet_Timer = urand(5000, 6000); - } - else uiSummonEtherealSphere_Timer -= diff; - - DoMeleeAttackIfReady(); + events.ScheduleEvent(EVENT_SUMMON_SPHERE, 5000); + events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(8000, 10000)); + events.ScheduleEvent(EVENT_ARCANE_BUFFET, urand(10000, 11000)); } void JustDied(Unit* /*killer*/) override @@ -208,6 +169,63 @@ public: if (victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_SLAY); } + + void SpellHit(Unit* who, const SpellInfo* spell) override + { + if (!who->ToCreature()) + return; + + if ((spell->Id == SPELL_ARCANE_POWER) || (spell->Id == H_SPELL_ARCANE_POWER)) + Talk(SAY_SUMMON_ENERGY); + } + + void UpdateAI(uint32 uiDiff) override + { + if (!UpdateVictim()) + return; + + events.Update(uiDiff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (uint32 eventId = events.ExecuteEvent()) + { + case EVENT_ARCANE_BARRAGE: + DoCast(SPELL_ARCANE_BARRAGE_VOLLEY); + events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(8000, 10000)); + break; + case EVENT_ARCANE_BUFFET: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_ARCANE_BUFFET); + events.ScheduleEvent(EVENT_ARCANE_BUFFET, urand(15000, 20000)); + break; + case EVENT_SUMMON_SPHERE: + Talk(SAY_REPEAT_SUMMON); + DoCast(SPELL_SUMMON_ETHEREAL_SPHERE_1); + if (IsHeroic()) + events.ScheduleEvent(EVENT_SUMMON_SPHERE_2, 2500); + events.ScheduleEvent(EVENT_SUMMON_PLAYERS, urand(33000, 35000)); + events.ScheduleEvent(EVENT_SUMMON_SPHERE, urand(45000, 47000)); + break; + case EVENT_SUMMON_SPHERE_2: + Talk(SAY_REPEAT_SUMMON); + DoCast(SPELL_SUMMON_ETHEREAL_SPHERE_2); + break; + case EVENT_SUMMON_PLAYERS: + if (Creature* sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE, 150.0f)) + sphere->GetAI()->DoAction(1); + else if (Creature* sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE2, 150.0f)) + sphere->GetAI()->DoAction(1); + break; + } + + DoMeleeAttackIfReady(); + } + + private: + InstanceScript* instance; + EventMap events; }; CreatureAI* GetAI(Creature* creature) const override @@ -225,72 +243,132 @@ public: { npc_ethereal_sphereAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } - void Initialize() + void Reset() override { - uiSummonPlayers_Timer = urand(33000, 35000); - uiRangeCheck_Timer = 1000; + events.Reset(); + DoCast(SPELL_POWER_BALL_VISUAL); + DoCast(SPELL_POWER_BALL_DAMAGE_TRIGGER); + arcanePower = false; + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->setFaction(16); + + events.ScheduleEvent(EVENT_DESPAWN_SPHERE, 40000); + events.ScheduleEvent(EVENT_RANGE_CHECK, 1000); } - InstanceScript* instance; - - uint32 uiSummonPlayers_Timer; - uint32 uiRangeCheck_Timer; - - void Reset() override + void DoAction(int32 action) override { - Initialize(); + DoCast(SPELL_SUMMON_PLAYERS); } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) - return; + events.Update(diff); - if (!me->HasAura(SPELL_POWER_BALL_VISUAL)) - DoCast(me, SPELL_POWER_BALL_VISUAL); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (uiRangeCheck_Timer < diff) + switch (uint32 eventId = events.ExecuteEvent()) { - if (Creature* pXevozz = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_XEVOZZ))) - { - float fDistance = me->GetDistance2d(pXevozz); - if (fDistance <= 3) - DoCast(pXevozz, SPELL_ARCANE_POWER); - else - DoCast(me, 35845); //Is it blizzlike? - } - uiRangeCheck_Timer = 1000; + case EVENT_RANGE_CHECK: + if (Creature* xevozz = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_XEVOZZ))) + { + if (me->IsWithinDist(xevozz, 3.0f) && !arcanePower) + { + DoCast(SPELL_ARCANE_POWER); + arcanePower = true; + events.ScheduleEvent(EVENT_DESPAWN_SPHERE, 8000); + } + } + events.ScheduleEvent(EVENT_RANGE_CHECK, 1000); + break; + case EVENT_DESPAWN_SPHERE: + me->DespawnOrUnsummon(); + break; } - else uiRangeCheck_Timer -= diff; + } + + private: + InstanceScript* instance; + EventMap events; + bool arcanePower; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_ethereal_sphereAI>(creature); + } +}; + +class spell_xevozz_summon_players : public SpellScriptLoader +{ +public: + spell_xevozz_summon_players() : SpellScriptLoader("spell_xevozz_summon_players") { } + + class spell_xevozz_summon_players_SpellScript : public SpellScript + { + PrepareSpellScript(spell_xevozz_summon_players_SpellScript); + + void HandleScript(SpellEffIndex effIndex) + { + Unit* target = GetHitUnit(); - if (uiSummonPlayers_Timer < diff) + if (target) { - DoCast(me, SPELL_SUMMON_PLAYERS); // not working right + Position pos = GetOriginalCaster()->GetPosition(); - Map* map = me->GetMap(); - if (map && map->IsDungeon()) - { - Map::PlayerList const &PlayerList = map->GetPlayers(); + target->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); + } + } - if (!PlayerList.isEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (i->GetSource()->IsAlive()) - DoTeleportPlayer(i->GetSource(), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), i->GetSource()->GetOrientation()); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_xevozz_summon_players_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; - uiSummonPlayers_Timer = urand(33000, 35000); + SpellScript* GetSpellScript() const override + { + return new spell_xevozz_summon_players_SpellScript(); + } +}; + +class spell_xevozz_summon_ethereal_sphere : public SpellScriptLoader +{ +public: + spell_xevozz_summon_ethereal_sphere() : SpellScriptLoader("spell_xevozz_summon_ethereal_sphere") { } + + class spell_xevozz_summon_ethereal_sphere_SpellScript : public SpellScript + { + PrepareSpellScript(spell_xevozz_summon_ethereal_sphere_SpellScript); + + void HandleScript(SpellDestination& target) + { + Unit* caster = GetOriginalCaster(); + Position pos; + float distance = 0.0f; + + while (distance < 20.0f) + { + pos = caster->GetRandomNearPosition(60.0f); + distance = caster->GetDistance(pos); } - else uiSummonPlayers_Timer -= diff; + + target.Relocate(pos); + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_xevozz_summon_ethereal_sphere_SpellScript::HandleScript, EFFECT_0, TARGET_DEST_DB); } }; - CreatureAI* GetAI(Creature* creature) const override + SpellScript* GetSpellScript() const override { - return GetInstanceAI<npc_ethereal_sphereAI>(creature); + return new spell_xevozz_summon_ethereal_sphere_SpellScript(); } }; @@ -298,4 +376,6 @@ void AddSC_boss_xevozz() { new boss_xevozz(); new npc_ethereal_sphere(); + new spell_xevozz_summon_players(); + new spell_xevozz_summon_ethereal_sphere(); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp index c29861f08a4..32dc18d2cf3 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp @@ -23,12 +23,8 @@ enum Spells { SPELL_SHROUD_OF_DARKNESS = 54524, SPELL_SUMMON_VOID_SENTRY = 54369, - SPELL_VOID_SHIFT = 54361 -}; - -enum Creatures -{ - NPC_VOID_SENTRY = 29364 + SPELL_VOID_SHIFT = 54361, + SPELL_VOID_SHIFTED = 54343, }; enum Yells @@ -46,6 +42,13 @@ enum Misc DATA_VOID_DANCE = 2153 }; +enum ZuramatEvents +{ + EVENT_VOID_SHIFT = 1, + EVENT_SUMMON_VOID, + EVENT_SHROUD_OF_DARKNESS +}; + class boss_zuramat : public CreatureScript { public: @@ -53,26 +56,29 @@ public: struct boss_zuramatAI : public ScriptedAI { - boss_zuramatAI(Creature* creature) : ScriptedAI(creature) + boss_zuramatAI(Creature* creature) : ScriptedAI(creature), sentries(me) { - Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { - SpellShroudOfDarknessTimer = 22000; - SpellVoidShiftTimer = 15000; - SpellSummonVoidTimer = 12000; + events.Reset(); voidDance = true; } - InstanceScript* instance; - - uint32 SpellVoidShiftTimer; - uint32 SpellSummonVoidTimer; - uint32 SpellShroudOfDarknessTimer; - bool voidDance; + void DespawnSentries() + { + sentries.DespawnAll(); + std::list<Creature*> sentries; + GetCreatureListWithEntryInGrid(sentries, me, NPC_VOID_SENTRY_BALL, 200.0f); + if (!sentries.empty()) + { + std::list<Creature*>::iterator itr = sentries.begin(); + for (itr; itr != sentries.end(); ++itr) + (*itr)->DespawnOrUnsummon(); + } + } void Reset() override { @@ -82,6 +88,7 @@ public: instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); Initialize(); + DespawnSentries(); } void AttackStart(Unit* who) override @@ -112,36 +119,17 @@ public: if (instance->GetData(DATA_WAVE_COUNT) == 6) instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - } + instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - void MoveInLineOfSight(Unit* /*who*/) override { } + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_SHROUD_OF_DARKNESS, urand(18000, 20000)); + events.ScheduleEvent(EVENT_VOID_SHIFT, 9000); + events.ScheduleEvent(EVENT_SUMMON_VOID, 4000); + } - void UpdateAI(uint32 diff) override + void JustSummoned(Creature* summon) override { - if (!UpdateVictim()) - return; - - if (SpellSummonVoidTimer <= diff) - { - DoCastVictim(SPELL_SUMMON_VOID_SENTRY, false); - SpellSummonVoidTimer = 20000; - } else SpellSummonVoidTimer -=diff; - - if (SpellVoidShiftTimer <= diff) - { - if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(unit, SPELL_VOID_SHIFT); - SpellVoidShiftTimer = 20000; - } else SpellVoidShiftTimer -=diff; - - if (SpellShroudOfDarknessTimer <= diff) - { - DoCastVictim(SPELL_SHROUD_OF_DARKNESS); - SpellShroudOfDarknessTimer = 20000; - } else SpellShroudOfDarknessTimer -=diff; - - DoMeleeAttackIfReady(); + sentries.Summon(summon); } void SummonedCreatureDies(Creature* summoned, Unit* /*who*/) override @@ -160,8 +148,12 @@ public: void JustDied(Unit* /*killer*/) override { + instance->SetData(DATA_ZURAMAT, 1); + Talk(SAY_DEATH); + DespawnSentries(); + if (instance->GetData(DATA_WAVE_COUNT) == 6) { instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); @@ -179,6 +171,43 @@ public: if (victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_SLAY); } + + void UpdateAI(uint32 diff) override + { + //Return since we have no target + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (uint32 eventId = events.ExecuteEvent()) + { + case EVENT_SUMMON_VOID: + DoCast(SPELL_SUMMON_VOID_SENTRY); + events.ScheduleEvent(EVENT_SUMMON_VOID, urand(7000, 10000)); + break; + case EVENT_VOID_SHIFT: + if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(unit, SPELL_VOID_SHIFT); + events.ScheduleEvent(EVENT_VOID_SHIFT, 15000); + break; + case EVENT_SHROUD_OF_DARKNESS: + DoCast(SPELL_SHROUD_OF_DARKNESS); + events.ScheduleEvent(EVENT_SHROUD_OF_DARKNESS, urand(18000, 20000)); + break; + } + + DoMeleeAttackIfReady(); + } + + private: + InstanceScript* instance; + EventMap events; + SummonList sentries; + bool voidDance; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp index 28c56a02255..e8ea47e09f3 100644 --- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp @@ -145,6 +145,7 @@ public: bCrystalActivated = false; defenseless = true; uiMainEventPhase = NOT_STARTED; + zuramatDead = false; } ObjectGuid uiErekemGuard[2]; @@ -178,6 +179,7 @@ public: bool bIsDoorSpellCast; bool bCrystalActivated; bool defenseless; + bool zuramatDead; std::list<uint8> NpcAtDoorCastingList; @@ -199,16 +201,21 @@ public: break; default: break; + case NPC_VOID_SENTRY: + if (zuramatDead) + { + creature->DespawnOrUnsummon(); + zuramatDead = false; + } + break; } - /* - BEWARE - SHIT. if (creature->GetGUID() == uiFirstBoss || creature->GetGUID() == uiSecondBoss) { creature->AllLootRemovedFromCorpse(); creature->RemoveLootMode(1); } - */ + } void OnGameObjectCreate(GameObject* go) override @@ -314,6 +321,9 @@ public: uiRemoveNpc = 0; // might not have been reset after a wipe on a boss. } break; + case DATA_ZURAMAT: + zuramatDead = true; + break; } } @@ -393,19 +403,23 @@ public: if (Creature* pGuard1 = instance->GetCreature(uiErekemGuard[0])) { if (bForceRespawn) + { pGuard1->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); + pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21); + } else - pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); - pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21); + pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); } if (Creature* pGuard2 = instance->GetCreature(uiErekemGuard[1])) { if (bForceRespawn) - pGuard2->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); + { + pGuard2->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); + pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22); + } else - pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); - pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22); + pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); } break; case BOSS_ICHORON: @@ -448,6 +462,9 @@ public: boss->Respawn(); boss->RemoveLootMode(1); } + else + boss->GetMotionMaster()->MoveTargetedHome(); + boss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); uiWaveCount = 0; } @@ -527,6 +544,7 @@ public: return false; } + zuramatDead = false; return true; } @@ -557,6 +575,7 @@ public: SetData(DATA_MAIN_DOOR, GO_STATE_ACTIVE); SetData(DATA_WAVE_COUNT, 0); uiMainEventPhase = NOT_STARTED; + uiActivationTimer = 5000; for (int i = 0; i < 4; ++i) if (GameObject* crystal = instance->GetGameObject(uiActivationCrystal[i])) diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp index 8bcc80b5a84..1a5ec7ea0d7 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -28,6 +28,7 @@ #define GOSSIP_START_EVENT "Get your people to safety, we'll keep the Blue Dragonflight's forces at bay." #define GOSSIP_ITEM_1 "Activate the crystals when we get in trouble, right" #define GOSSIP_I_WANT_IN "I'm not fighting, so send me in now!" +#define SAY_EVENT_LOCK "I'm locking the door. Good luck, and thank you for doing this." #define SPAWN_TIME 20000 enum PortalCreatures @@ -59,7 +60,7 @@ enum AzureSellbreakerSpells SPELL_ARCANE_BLAST = 58462, SPELL_SLOW = 25603, SPELL_CHAINS_OF_ICE = 58464, - SPELL_CONE_OF_COLD = 58463 + SPELL_CONE_OF_COLD = 58463, }; enum AzureBinderSpells @@ -67,7 +68,7 @@ enum AzureBinderSpells SPELL_ARCANE_BARRAGE = 58456, SPELL_ARCANE_EXPLOSION = 58455, SPELL_FROST_NOVA = 58458, - SPELL_FROSTBOLT = 58457 + SPELL_FROSTBOLT = 58457, }; enum AzureMageSlayerSpells @@ -85,7 +86,7 @@ enum AzureCaptainSpells enum AzureSorcerorSpells { SPELL_ARCANE_STREAM = 60181, - SPELL_MANA_DETONATION = 60182 + SPELL_MANA_DETONATION = 60182, }; enum AzureRaiderSpells @@ -114,7 +115,7 @@ enum TrashDoorSpell enum Spells { SPELL_PORTAL_CHANNEL = 58012, - SPELL_CRYSTAL_ACTIVATION = 57804, + SPELL_CRYSTAL_ACTIVATION = 57804, // visual effect SPELL_ARCANE_SPHERE_PASSIVE = 44263 }; @@ -242,9 +243,19 @@ const float SaboteurFinalPos6[5][3] = {1931.063354f, 848.468445f, 47.190434f} }; -const Position MovePosition = {1806.955566f, 803.851807f, 44.363323f, 0.0f}; -const Position playerTeleportPosition = {1830.531006f, 803.939758f, 44.340508f, 6.281611f}; -const Position sinclariOutsidePosition = {1817.315674f, 804.060608f, 44.363998f, 0.0f}; +const Position PortalLocation[] = +{ + { 1877.51f, 850.104f, 44.6599f, 4.7822f }, // WP 1 + { 1936.07f, 803.198f, 53.3749f, 3.12414f }, // WP 3 + { 1890.64f, 753.471f, 48.7224f, 1.71042f }, // WP 5 +}; + +uint64 preEventPortalGUID[3] = { 0 }; + +const Position MovePosition = { 1806.955566f, 803.851807f, 44.363323f, 0.0f }; +const Position playerTeleportPosition = { 1830.531006f, 803.939758f, 44.340508f, 6.281611f }; +const Position sinclariOutsidePosition = { 1820.429810f, 804.066040f, 44.363998f, 0.0f }; +const Position sinclariCrystalPosition = { 1828.868286f, 798.468811f, 44.363998f, 3.890467f }; class npc_sinclari_vh : public CreatureScript { @@ -321,6 +332,13 @@ public: me->SetReactState(REACT_AGGRESSIVE); + if (TempSummon* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[0], TEMPSUMMON_MANUAL_DESPAWN)) + preEventPortalGUID[0] = summon->GetGUID(); + if (TempSummon* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[1], TEMPSUMMON_MANUAL_DESPAWN)) + preEventPortalGUID[1] = summon->GetGUID(); + if (TempSummon* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[2], TEMPSUMMON_MANUAL_DESPAWN)) + preEventPortalGUID[2] = summon->GetGUID(); + std::list<Creature*> GuardList; me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); if (!GuardList.empty()) @@ -347,25 +365,17 @@ public: switch (uiPhase) { case 1: - Talk(SAY_SINCLARI_1); - uiTimer = 4000; - uiPhase = 2; + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(0, sinclariCrystalPosition); + uiTimer = 1000; + uiPhase = 6; break; case 2: { - std::list<Creature*> GuardList; - me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); - if (!GuardList.empty()) - for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr) - { - if (Creature* pGuard = *itr) - { - pGuard->SetWalk(false); - pGuard->GetMotionMaster()->MovePoint(0, MovePosition); - } - } - uiTimer = 6000; - uiPhase = 3; + me->SetFacingTo(me->GetOrientation() - 3.14f); + Talk(SAY_SINCLARI_1); + uiTimer = 1500; + uiPhase = 7; break; } case 3: @@ -378,7 +388,6 @@ public: if (Creature* pGuard = *itr) { pGuard->SetVisible(false); - pGuard->SetReactState(REACT_PASSIVE); } } uiTimer = 2000; @@ -391,11 +400,58 @@ public: uiPhase = 5; break; case 5: - instance->SetData(DATA_MAIN_EVENT_PHASE, IN_PROGRESS); + me->SetFacingTo(0.006673f); + me->Say(SAY_EVENT_LOCK, LANG_UNIVERSAL, me); // need to change to db say me->SetReactState(REACT_PASSIVE); + uiTimer = 3000; + uiPhase = 8; + break; + case 6: + me->GetMotionMaster()->MovementExpired(); + me->HandleEmoteCommand(EMOTE_STATE_USE_STANDING); + uiTimer = 2000; + uiPhase = 2; + break; + case 7: + { + std::list<Creature*> creatures; + GetCreatureListWithEntryInGrid(creatures, me, NPC_TELEPORTATION_PORTAL, 200.0f); + GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_BINDER_1, 200.0f); + GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_MAGE_SLAYER_1, 200.0f); + GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_INVADER_1, 200.0f); + DoCast(SPELL_CRYSTAL_ACTIVATION); + if (!creatures.empty()) + { + for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + (*itr)->DisappearAndDie(); + } + uiTimer = 500; + uiPhase = 9; + } + break; + case 8: + instance->SetData(DATA_MAIN_EVENT_PHASE, IN_PROGRESS); uiTimer = 0; uiPhase = 0; break; + case 9: + { + std::list<Creature*> GuardList; + me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); + if (!GuardList.empty()) + for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr) + { + if (Creature* pGuard = *itr) + { + pGuard->SetReactState(REACT_PASSIVE); + pGuard->SetWalk(false); + pGuard->GetMotionMaster()->MovePoint(0, MovePosition); + } + } + uiTimer = 4000; + uiPhase = 3; + } + break; } } else uiTimer -= uiDiff; @@ -548,6 +604,9 @@ public: Initialize(); instance = creature->GetInstanceScript(); uiTypeOfMobsPortal = urand(0, 1); // 0 - elite mobs 1 - portal guardian or portal keeper with regular mobs + + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == NOT_STARTED) + uiTypeOfMobsPortal = 2; } void Initialize() @@ -575,10 +634,13 @@ public: void UpdateAI(uint32 diff) override { - if (instance->GetData(DATA_REMOVE_NPC) == 1) + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) { - me->DespawnOrUnsummon(); - instance->SetData(DATA_REMOVE_NPC, 0); + if (instance->GetData(DATA_REMOVE_NPC) == 1) + { + me->DespawnOrUnsummon(); + instance->SetData(DATA_REMOVE_NPC, 0); + } } uint8 uiWaveCount = instance->GetData(DATA_WAVE_COUNT); @@ -642,24 +704,39 @@ public: me->RemoveCorpse(); } break; + case 2: // Pre-event + if (uiSpawnTimer <= diff) + { + uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_BINDER_1); + DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); + uiSpawnTimer = SPAWN_TIME; + } else uiSpawnTimer -= diff; + break; } } void JustDied(Unit* /*killer*/) override { - instance->SetData(DATA_WAVE_COUNT, instance->GetData(DATA_WAVE_COUNT)+1); + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + instance->SetData(DATA_WAVE_COUNT, instance->GetData(DATA_WAVE_COUNT) + 1); } void JustSummoned(Creature* summoned) override { - listOfMobs.Summon(summoned); - instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + { + listOfMobs.Summon(summoned); + instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); + } } void SummonedCreatureDies(Creature* summoned, Unit* /*killer*/) override { - listOfMobs.Despawn(summoned); - instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + { + listOfMobs.Despawn(summoned); + instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); + } } }; @@ -675,8 +752,39 @@ struct violet_hold_trashAI : public npc_escortAI { instance = creature->GetInstanceScript(); bHasGotMovingPoints = false; - portalLocationID = instance->GetData(DATA_PORTAL_LOCATION); - secondPortalRouteID = 0; + + + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == NOT_STARTED) + { + if (Creature* portal = me->FindNearestCreature(NPC_TELEPORTATION_PORTAL, 10.0f)) + { + uint64 portalGUID = portal->GetGUID(); + for (uint8 i = 0; i < 3; i++) + { + if (portalGUID == preEventPortalGUID[i]) + { + switch (i) + { + case 0: + portalLocationID = 0; + break; + case 1: + portalLocationID = 2; + break; + case 2: + portalLocationID = 4; + break; + } + break; + } + } + } + } + else + { + portalLocationID = instance->GetData(DATA_PORTAL_LOCATION); + Reset(); + } } public: @@ -691,7 +799,7 @@ struct violet_hold_trashAI : public npc_escortAI { case 0: if (waypointId == 5) - CreatureStartAttackDoor(); + CreatureStartAttackDoor(); break; case 1: if ((waypointId == 8 && secondPortalRouteID == 0) || (waypointId == 7 && secondPortalRouteID == 1)) @@ -699,7 +807,7 @@ struct violet_hold_trashAI : public npc_escortAI break; case 2: if (waypointId == 7) - CreatureStartAttackDoor(); + CreatureStartAttackDoor(); break; case 3: if (waypointId == 8) @@ -1203,7 +1311,7 @@ public: if (uiConeOfColdTimer <= diff) { - DoCast(SPELL_CONE_OF_COLD); + DoCast(SPELL_CONE_OF_COLD); uiConeOfColdTimer = 5000; } else uiConeOfColdTimer -= diff; } @@ -1391,6 +1499,33 @@ public: } }; +class spell_crystal_activation : public SpellScriptLoader +{ +public: + spell_crystal_activation() : SpellScriptLoader("spell_crystal_activation") { } + + class spell_crystal_activation_SpellScript : public SpellScript + { + PrepareSpellScript(spell_crystal_activation_SpellScript); + + void HandleSendEvent(SpellEffIndex effIndex) + { + if (GetHitUnit()->GetEntry() == NPC_VIOLET_HOLD_GUARD) + PreventHitDefaultEffect(effIndex); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_crystal_activation_SpellScript::HandleSendEvent, EFFECT_0, SPELL_EFFECT_SEND_EVENT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_crystal_activation_SpellScript(); + } +}; + void AddSC_violet_hold() { new npc_sinclari_vh(); @@ -1406,4 +1541,5 @@ void AddSC_violet_hold() new npc_azure_saboteur(); new npc_violet_hold_arcane_sphere(); new go_activation_crystal(); + new spell_crystal_activation(); } diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.h b/src/server/scripts/Northrend/VioletHold/violet_hold.h index 275a7467d83..e8da9576c13 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.h +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.h @@ -101,7 +101,9 @@ enum CreaturesIds NPC_SINCLARI = 30658, NPC_SABOTEOUR = 31079, NPC_VIOLET_HOLD_GUARD = 30659, - NPC_DEFENSE_SYSTEM = 30837 + NPC_DEFENSE_SYSTEM = 30837, + NPC_VOID_SENTRY = 29364, + NPC_VOID_SENTRY_BALL = 29365 }; enum GameObjectIds |