diff options
Diffstat (limited to 'src/server/scripts')
16 files changed, 1372 insertions, 557 deletions
diff --git a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp index 46e831b0f83..7bd197774bc 100644 --- a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp +++ b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp @@ -111,6 +111,7 @@ public: instance_zulfarrak_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + GahzRillaEncounter = NOT_STARTED; PyramidPhase = 0; major_wave_Timer = 0; minor_wave_Timer = 0; diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index ad510682e15..04ea31bd1dd 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -26,14 +26,6 @@ #include "ruby_sanctum.h" #include "Player.h" -/* ScriptData -SDName: ruby_sanctum -SDAuthors: Kaelima, Warpten -SD%Complete: 90% -SDComment: Based on Kaelima's initial work (half of it). Corporeality handling is a pure guess, we lack info. -SDCategory: Chamber of Aspects -EndScriptData */ - enum Texts { // Shared @@ -87,6 +79,8 @@ enum Spells // Living Inferno SPELL_BLAZING_AURA = 75885, + SPELL_SPAWN_LIVING_EMBERS = 75880, + SPELL_SUMMON_LIVING_EMBER = 75881, // Halion Controller SPELL_COSMETIC_FIRE_PILLAR = 76006, @@ -143,7 +137,7 @@ enum Events EVENT_CHECK_CORPOREALITY = 14, EVENT_SHADOW_PULSARS_SHOOT = 15, EVENT_TRIGGER_BERSERK = 16, - EVENT_TWILIGHT_MENDING = 17 + EVENT_TWILIGHT_MENDING = 17, }; enum Actions @@ -156,7 +150,13 @@ enum Actions ACTION_MONITOR_CORPOREALITY = 3, // Orb Carrier - ACTION_SHOOT = 4 + ACTION_SHOOT = 4, + + // Living Inferno + ACTION_SUMMON_LIVING_EMBERS = 5, + + // Meteor Flame + ACTION_SUMMON_FLAME = 6 }; enum Phases @@ -174,7 +174,8 @@ enum Misc DATA_MATERIAL_DAMAGE_TAKEN = 2, DATA_STACKS_DISPELLED = 3, DATA_FIGHT_PHASE = 4, - DATA_EVADE_METHOD = 5 + DATA_EVADE_METHOD = 5, + DATA_SPAWNED_FLAMES = 6, }; enum OrbCarrierSeats @@ -703,7 +704,7 @@ class npc_halion_controller : public CreatureScript // The IsInCombat() check is needed because that check should be false when Halion is // not engaged, while it would return true without as UpdateVictim() checks for // combat state. - if (!(_events.IsInPhase(PHASE_INTRO)) && me->IsInCombat() && !UpdateVictim()) + if (!_events.IsInPhase(PHASE_INTRO) && me->IsInCombat() && !UpdateVictim()) { EnterEvadeMode(); return; @@ -894,8 +895,6 @@ class npc_halion_controller : public CreatureScript } }; -typedef npc_halion_controller::npc_halion_controllerAI controllerAI; - class npc_orb_carrier : public CreatureScript { public: @@ -997,7 +996,7 @@ class npc_meteor_strike_initial : public CreatureScript if (!owner) return; - // Let Halion Controller count as summoner + // Let Controller count as summoner if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION_CONTROLLER))) controller->AI()->JustSummoned(me); @@ -1007,11 +1006,13 @@ class npc_meteor_strike_initial : public CreatureScript if (HalionAI* halionAI = CAST_AI(HalionAI, owner->AI())) { Position const* ownerPos = halionAI->GetMeteorStrikePosition(); + // Adjust randomness between 0 and pi. + float randomAdjustment = frand(static_cast<float>(M_PI / 14), static_cast<float>(13 * M_PI / 14)); float angle[4]; angle[0] = me->GetAngle(ownerPos); - angle[1] = me->GetAngle(ownerPos) - static_cast<float>(M_PI/2); - angle[2] = me->GetAngle(ownerPos) - static_cast<float>(-M_PI/2); - angle[3] = me->GetAngle(ownerPos) - static_cast<float>(M_PI); + angle[1] = angle[0] + randomAdjustment; + angle[2] = angle[0] + static_cast<float>(M_PI); + angle[3] = angle[2] + randomAdjustment; _meteorList.clear(); for (uint8 i = 0; i < 4; i++) @@ -1020,7 +1021,10 @@ class npc_meteor_strike_initial : public CreatureScript me->SetOrientation(angle[i]); Position newPos = me->GetNearPosition(10.0f, 0.0f); // Exact distance if (Creature* meteor = me->SummonCreature(NPC_METEOR_STRIKE_NORTH + i, newPos, TEMPSUMMON_TIMED_DESPAWN, 30000)) + { + meteor->SetOrientation(angle[i]); _meteorList.push_back(meteor); + } } } } @@ -1046,11 +1050,8 @@ class npc_meteor_strike : public CreatureScript struct npc_meteor_strikeAI : public ScriptedAI { npc_meteor_strikeAI(Creature* creature) : ScriptedAI(creature), - _instance(creature->GetInstanceScript()) + _instance(creature->GetInstanceScript()), _spawnCount(0) { - _range = 5.0f; - _spawnCount = 0; - SetCombatMovement(false); } @@ -1071,34 +1072,38 @@ class npc_meteor_strike : public CreatureScript controller->AI()->JustSummoned(me); } - void UpdateAI(uint32 diff) override + void SetData(uint32 dataType, uint32 dataCount) override { - if (_spawnCount > 5) - return; + if (dataType == DATA_SPAWNED_FLAMES) + _spawnCount += dataCount; + } - _events.Update(diff); + uint32 GetData(uint32 dataType) const override + { + if (dataType == DATA_SPAWNED_FLAMES) + return _spawnCount; + return 0; + } + void UpdateAI(uint32 diff) override + { + _events.Update(diff); if (_events.ExecuteEvent() == EVENT_SPAWN_METEOR_FLAME) { - Position pos = me->GetNearPosition( _range, 0.0f); - + Position pos = me->GetNearPosition(5.0f, 0.0f); if (Creature* flame = me->SummonCreature(NPC_METEOR_STRIKE_FLAME, pos, TEMPSUMMON_TIMED_DESPAWN, 25000)) { - if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION_CONTROLLER))) - controller->AI()->JustSummoned(flame); + flame->SetOrientation(me->GetOrientation()); - flame->CastSpell(flame, SPELL_METEOR_STRIKE_FIRE_AURA_2, true); - ++_spawnCount; + flame->AI()->SetGUID(GetGUID()); + flame->AI()->DoAction(ACTION_SUMMON_FLAME); } - _range += 5.0f; - _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 800); } } private: InstanceScript* _instance; EventMap _events; - float _range; uint8 _spawnCount; }; @@ -1108,6 +1113,67 @@ class npc_meteor_strike : public CreatureScript } }; +class npc_meteor_strike_flame : public CreatureScript +{ + public: + npc_meteor_strike_flame() : CreatureScript("npc_meteor_strike_flame") { } + + struct npc_meteor_strike_flameAI : public ScriptedAI + { + npc_meteor_strike_flameAI(Creature* creature) : ScriptedAI(creature), + _instance(creature->GetInstanceScript()) + { + SetCombatMovement(false); + } + + void SetGUID(ObjectGuid guid, int32 id /* = 0 */) override + { + _rootOwnerGuid = guid; + } + + void DoAction(int32 action) override + { + if (action != ACTION_SUMMON_FLAME || _rootOwnerGuid.IsEmpty()) + return; + + me->CastSpell(me, SPELL_METEOR_STRIKE_FIRE_AURA_2, true); + + Creature* meteorStrike = ObjectAccessor::GetCreature(*me, _rootOwnerGuid); + if (!meteorStrike || meteorStrike->AI()->GetData(DATA_SPAWNED_FLAMES) > 5) + return; + + me->SetOrientation(me->GetOrientation() + frand(static_cast<float>(-M_PI / 16), static_cast<float>(M_PI / 16))); + Position pos = me->GetNearPosition(5.0f, 0.0f); + + if (Creature* flame = me->SummonCreature(NPC_METEOR_STRIKE_FLAME, pos, TEMPSUMMON_TIMED_DESPAWN, 25000)) + { + flame->AI()->SetGUID(_rootOwnerGuid); + meteorStrike->AI()->SetData(DATA_SPAWNED_FLAMES, 1); + } + } + + void IsSummonedBy(Unit* /*summoner*/) override + { + // Let Halion Controller count as summoner. + if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION_CONTROLLER))) + controller->AI()->JustSummoned(me); + } + + void UpdateAI(uint32 diff) override { } + void EnterEvadeMode() override { } + + private: + InstanceScript* _instance; + EventMap _events; + ObjectGuid _rootOwnerGuid = ObjectGuid::Empty; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetRubySanctumAI<npc_meteor_strike_flameAI>(creature); + } +}; + class npc_combustion_consumption : public CreatureScript { public: @@ -1120,26 +1186,23 @@ class npc_combustion_consumption : public CreatureScript { SetCombatMovement(false); - switch (me->GetEntry()) + switch (creature->GetEntry()) { case NPC_COMBUSTION: _explosionSpell = SPELL_FIERY_COMBUSTION_EXPLOSION; _damageSpell = SPELL_COMBUSTION_DAMAGE_AURA; - me->SetPhaseMask(0x01, true); + creature->SetPhaseMask(IsHeroic() ? 0x21 : 0x01, true); break; case NPC_CONSUMPTION: _explosionSpell = SPELL_SOUL_CONSUMPTION_EXPLOSION; _damageSpell = SPELL_CONSUMPTION_DAMAGE_AURA; - me->SetPhaseMask(0x20, true); + creature->SetPhaseMask(IsHeroic() ? 0x21 : 0x20, true); break; default: // Should never happen _explosionSpell = 0; _damageSpell = 0; break; } - - if (IsHeroic()) - me->SetPhaseMask(0x01 | 0x20, true); } void IsSummonedBy(Unit* summoner) override @@ -1161,7 +1224,7 @@ class npc_combustion_consumption : public CreatureScript me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount, me); DoCast(me, _damageSpell); - int32 damage = 1200 + (stackAmount * 1290); // Needs more researches. + int32 damage = 1200 + (stackAmount * 1290); // Needs more research. summoner->CastCustomSpell(_explosionSpell, SPELLVALUE_BASE_POINT0, damage, summoner); } @@ -1194,6 +1257,10 @@ class npc_living_inferno : public CreatureScript me->SetInCombatWithZone(); me->CastSpell(me, SPELL_BLAZING_AURA, true); + // SMSG_SPELL_GO for the living ember stuff isn't even sent to the client - Blizzard on drugs. + if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC) + me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true); + if (InstanceScript* instance = me->GetInstanceScript()) if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) controller->AI()->JustSummoned(me); @@ -1211,7 +1278,6 @@ class npc_living_inferno : public CreatureScript } }; -//! Need sniff data class npc_living_ember : public CreatureScript { public: @@ -1739,12 +1805,46 @@ class spell_halion_summon_exit_portals : public SpellScriptLoader } }; +class spell_halion_spawn_living_embers : public SpellScriptLoader +{ + public: + spell_halion_spawn_living_embers() : SpellScriptLoader("spell_halion_spawn_living_embers") { } + + class spell_halion_spawn_living_embers_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_spawn_living_embers_SpellScript); + + void SelectMeteorFlames(std::list<WorldObject*>& unitList) + { + if (!unitList.empty()) + Trinity::Containers::RandomResizeList(unitList, 10); + } + + void HandleScript(SpellEffIndex /* effIndex */) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_LIVING_EMBER, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_halion_spawn_living_embers_SpellScript::SelectMeteorFlames, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_halion_spawn_living_embers_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_halion_spawn_living_embers_SpellScript(); + } +}; + void AddSC_boss_halion() { new boss_halion(); new boss_twilight_halion(); new npc_halion_controller(); + new npc_meteor_strike_flame(); new npc_meteor_strike_initial(); new npc_meteor_strike(); new npc_combustion_consumption(); @@ -1766,4 +1866,5 @@ void AddSC_boss_halion() new spell_halion_twilight_phasing(); new spell_halion_twilight_cutter(); new spell_halion_clear_debuffs(); + new spell_halion_spawn_living_embers(); } diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp index c3f5c75e059..737a5d5c982 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp @@ -229,6 +229,21 @@ class instance_oculus : public InstanceMapScript return true; } + uint32 GetData(uint32 type) const override + { + if (type == DATA_CONSTRUCTS) + { + if (CentrifugueConstructCounter == 0) + return KILL_NO_CONSTRUCT; + else if (CentrifugueConstructCounter == 1) + return KILL_ONE_CONSTRUCT; + else if (CentrifugueConstructCounter > 1) + return KILL_MORE_CONSTRUCT; + } + + return KILL_NO_CONSTRUCT; + } + ObjectGuid GetGuidData(uint32 type) const override { switch (type) diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 450f97cb9b7..3ab9814b5b5 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -20,6 +20,7 @@ #include "ScriptedGossip.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellInfo.h" #include "CombatAI.h" #include "Player.h" #include "Vehicle.h" @@ -76,6 +77,11 @@ enum Drakes SPELL_EMERALD_TOUCH_THE_NIGHTMARE = 50341, // (60 yds) - Instant - Consumes 30% of the caster's max health to inflict 25, 000 nature damage to an enemy dragon and reduce the damage it deals by 25% for 30 sec. // you do not have access to until you kill the Mage-Lord Urom SPELL_EMERALD_DREAM_FUNNEL = 50344, // (60 yds) - Channeled - Transfers 5% of the caster's max health to a friendly drake every second for 10 seconds as long as the caster channels. +/* + * All Drakes + * GPS System + */ + SPELL_GPS = 53389, // Misc POINT_LAND = 2, @@ -101,7 +107,13 @@ enum Says WHISPER_DRAKES_WELCOME = 1, WHISPER_DRAKES_ABILITIES = 2, WHISPER_DRAKES_SPECIAL = 3, - WHISPER_DRAKES_LOWHEALTH = 4 + WHISPER_DRAKES_LOWHEALTH = 4, + WHISPER_GPS_10_CONSTRUCTS = 5, + WHISPER_GPS_1_CONSTRUCT = 6, + WHISPER_GPS_VAROS = 7, + WHISPER_GPS_UROM = 8, + WHISPER_GPS_EREGOS = 9, + WHISPER_GPS_END = 10 }; class npc_verdisa_beglaristrasz_eternos : public CreatureScript @@ -250,6 +262,26 @@ class npc_ruby_emerald_amber_drake : public CreatureScript Initialize(); } + void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override + { + if (Unit* creator = ObjectAccessor::GetUnit(*me, me->GetCreatorGUID())) + if (spell->Id == SPELL_GPS) + { + if (_instance->GetBossState(DATA_EREGOS) == DONE) + Talk(WHISPER_GPS_END, creator); + else if (_instance->GetBossState(DATA_UROM) == DONE) + Talk(WHISPER_GPS_EREGOS, creator); + else if (_instance->GetBossState(DATA_VAROS) == DONE) + Talk(WHISPER_GPS_UROM, creator); + else if (_instance->GetData(DATA_CONSTRUCTS) == KILL_NO_CONSTRUCT) + Talk(WHISPER_GPS_VAROS, creator); + else if (_instance->GetData(DATA_CONSTRUCTS) == KILL_ONE_CONSTRUCT) + Talk(WHISPER_GPS_1_CONSTRUCT, creator); + else if (_instance->GetData(DATA_CONSTRUCTS) == KILL_MORE_CONSTRUCT) + Talk(WHISPER_GPS_10_CONSTRUCTS, creator); + } + } + void IsSummonedBy(Unit* summoner) override { if (_instance->GetBossState(DATA_EREGOS) == IN_PROGRESS) diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h index fd46f0a6bcc..08fc15c5752 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h @@ -29,7 +29,9 @@ enum DataTypes DATA_DRAKOS = 0, DATA_VAROS = 1, DATA_UROM = 2, - DATA_EREGOS = 3 + DATA_EREGOS = 3, + // GPS System + DATA_CONSTRUCTS = 4 }; enum CreatureIds @@ -91,6 +93,13 @@ enum InstanceEvents EVENT_EREGOS_INTRO }; +enum ConstructKillState +{ + KILL_NO_CONSTRUCT = 0, + KILL_ONE_CONSTRUCT = 1, + KILL_MORE_CONSTRUCT = 2 +}; + enum Misc { POINT_MOVE_OUT = 1 diff --git a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp index 5c8d4b8691a..1f9fc6d7981 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,11 +41,27 @@ 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: boss_erekem() : CreatureScript("boss_erekem") { } + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_erekemAI>(creature); + } + struct boss_erekemAI : public ScriptedAI { boss_erekemAI(Creature* creature) : ScriptedAI(creature) @@ -55,39 +72,50 @@ public: void Initialize() { - uiBloodlustTimer = 15000; - uiChainHealTimer = 0; - uiEarthShockTimer = urand(2000, 8000); - uiLightningBoltTimer = urand(5000, 10000); - uiEarthShieldTimer = 20000; + 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(); + } } + + events.Reset(); + } + + 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 @@ -104,13 +132,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 +161,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; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); - 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; - - 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,27 +192,118 @@ 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)); + } + } + } - Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)); - if (pGuard2 && pGuard2->IsAlive() && !pGuard2->HealthAbovePct(75)) - return pGuard2->GetGUID(); + events.Update(diff); - return ObjectGuid::Empty; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + 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; + default: + break; + } + + DoMeleeAttackIfReady(); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_erekemAI>(creature); - } + private: + EventMap events; + InstanceScript* instance; + uint8 phase; + int32 breakBondsCd; + }; }; enum GuardSpells @@ -254,6 +318,11 @@ class npc_erekem_guard : public CreatureScript public: npc_erekem_guard() : CreatureScript("npc_erekem_guard") { } + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_erekem_guardAI>(creature); + } + struct npc_erekem_guardAI : public ScriptedAI { npc_erekem_guardAI(Creature* creature) : ScriptedAI(creature) @@ -278,6 +347,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 @@ -322,11 +394,6 @@ public: } else uiGushingWoundTimer -= diff; } }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_erekem_guardAI>(creature); - } }; void AddSC_boss_erekem() diff --git a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp index 9be73febd52..137f63a381d 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 @@ -48,26 +52,48 @@ enum Yells enum Actions { - ACTION_WATER_ELEMENT_HIT = 1, - ACTION_WATER_ELEMENT_KILLED = 2 + ACTION_WATER_ELEMENT_HIT = 1 }; -/// @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 }; + +#define MAX_GLOBULE_PATHS 10 + +Position const globulePaths[MAX_GLOBULE_PATHS] = +{ + // 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 +104,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 +148,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 +175,14 @@ public: switch (param) { case ACTION_WATER_ELEMENT_HIT: - me->ModifyHealth(int32(me->CountPctFromMaxHealth(1))); - + { if (bIsExploded) DoExplodeCompleted(); + me->SetHealth(me->GetHealth() + me->CountPctFromMaxHealth(3)); dehydration = false; - break; - case ACTION_WATER_ELEMENT_KILLED: - uint32 damage = me->CountPctFromMaxHealth(3); - me->ModifyHealth(-int32(damage)); - me->LowerPlayerDamageReq(damage); - break; + } + break; } } @@ -180,6 +196,7 @@ public: void DoExplodeCompleted() { bIsExploded = false; + bIsDrained = false; if (!HealthBelowPct(25)) { @@ -199,74 +216,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 +262,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 +286,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 < MAX_GLOBULE_PATHS; 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 < MAX_GLOBULE_PATHS; 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 @@ -341,48 +448,68 @@ public: void Initialize() { - uiRangeCheck_Timer = 1000; + pathId = 0; } - InstanceScript* instance; - - uint32 uiRangeCheck_Timer; - void Reset() override { Initialize(); - DoCast(me, SPELL_WATER_GLOBULE); + events.Reset(); + DoCast(SPELL_WATER_GLOBULE); + me->SetReactState(REACT_PASSIVE); } - 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()->Clear(); + events.ScheduleEvent(EVENT_GLOBULE_MOVE, 500); + break; + case 1: + me->GetMotionMaster()->Clear(); + 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..8dc0e32fb31 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp @@ -27,41 +27,39 @@ enum Spells SPELL_LAVA_BURN = 54249 }; +enum LavanthorEvents +{ + EVENT_CAUTERIZING_FLAMES = 1, + EVENT_FIREBOLT, + EVENT_FLAME_BREATH, + EVENT_LAVA_BURN +}; + class boss_lavanthor : public CreatureScript { public: boss_lavanthor() : CreatureScript("boss_lavanthor") { } + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_lavanthorAI>(creature); + } + struct boss_lavanthorAI : public ScriptedAI { 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 +70,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 +96,38 @@ 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; - - if (uiFlameBreathTimer <= diff) - { - DoCastVictim(SPELL_FLAME_BREATH); - uiFlameBreathTimer = urand(10000, 15000); - } else uiFlameBreathTimer -= diff; + events.Update(diff); - if (uiLavaBurnTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true)) - DoCast(target, SPELL_LAVA_BURN); - uiLavaBurnTimer = urand(15000, 23000); - } else uiLavaBurnTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (IsHeroic()) + switch (uint32 eventId = events.ExecuteEvent()) { - if (uiCauterizingFlamesTimer <= diff) - { - DoCastVictim(SPELL_CAUTERIZING_FLAMES); - uiCauterizingFlamesTimer = urand(10000, 16000); - } else uiCauterizingFlamesTimer -= diff; + 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); + 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; + default: + break; } DoMeleeAttackIfReady(); @@ -144,12 +146,11 @@ public: instance->SetData(DATA_WAVE_COUNT, 13); } } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_lavanthorAI>(creature); - } + private: + EventMap events; + InstanceScript* instance; + }; }; void AddSC_boss_lavanthor() diff --git a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp index 1c98806b127..f9e223d3bab 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp @@ -17,6 +17,8 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" #include "violet_hold.h" enum Spells @@ -24,7 +26,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 +49,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 +75,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 +98,30 @@ 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; + default: + break; + } DoMeleeAttackIfReady(); } @@ -126,6 +139,10 @@ public: instance->SetData(DATA_WAVE_COUNT, 13); } } + + private: + EventMap events; + InstanceScript* instance; }; CreatureAI* GetAI(Creature* creature) const override @@ -134,7 +151,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()->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()->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..4fb7646558d 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp @@ -17,29 +17,33 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" -#include "violet_hold.h" +#include "SpellInfo.h" +#include "SpellScript.h" #include "Player.h" +#include "violet_hold.h" 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 +57,22 @@ 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 +}; + +enum SphereActions +{ + ACTION_SUMMON = 1, +}; + class boss_xevozz : public CreatureScript { public: @@ -62,23 +82,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 +92,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 +115,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 +147,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 +176,65 @@ 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 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + 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(ACTION_SUMMON); + else if (Creature* sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE2, 150.0f)) + sphere->GetAI()->DoAction(ACTION_SUMMON); + break; + default: + break; + } + + DoMeleeAttackIfReady(); + } + + private: + InstanceScript* instance; + EventMap events; }; CreatureAI* GetAI(Creature* creature) const override @@ -226,71 +253,137 @@ public: npc_ethereal_sphereAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } void Initialize() { - uiSummonPlayers_Timer = urand(33000, 35000); - uiRangeCheck_Timer = 1000; + arcanePower = false; } - InstanceScript* instance; - - uint32 uiSummonPlayers_Timer; - uint32 uiRangeCheck_Timer; - void Reset() override { Initialize(); + events.Reset(); + DoCast(SPELL_POWER_BALL_VISUAL); + DoCast(SPELL_POWER_BALL_DAMAGE_TRIGGER); + 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); + } + + void DoAction(int32 action) override + { + if (action == ACTION_SUMMON) + 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); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_xevozz_summon_players_SpellScript(); + } +}; - uiSummonPlayers_Timer = urand(33000, 35000); +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 +391,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..02e479a22f4 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,7 +56,7 @@ public: struct boss_zuramatAI : public ScriptedAI { - boss_zuramatAI(Creature* creature) : ScriptedAI(creature) + boss_zuramatAI(Creature* creature) : ScriptedAI(creature), sentries(me) { Initialize(); instance = creature->GetInstanceScript(); @@ -61,18 +64,18 @@ public: void Initialize() { - SpellShroudOfDarknessTimer = 22000; - SpellVoidShiftTimer = 15000; - SpellSummonVoidTimer = 12000; voidDance = true; } - InstanceScript* instance; - - uint32 SpellVoidShiftTimer; - uint32 SpellSummonVoidTimer; - uint32 SpellShroudOfDarknessTimer; - bool voidDance; + void DespawnSentries() + { + sentries.DespawnAll(); + std::list<Creature*> sentrylist; + GetCreatureListWithEntryInGrid(sentrylist, me, NPC_VOID_SENTRY_BALL, 200.0f); + if (!sentrylist.empty()) + for (std::list<Creature*>::const_iterator itr = sentrylist.begin(); itr != sentrylist.end(); ++itr) + (*itr)->DespawnOrUnsummon(); + } void Reset() override { @@ -82,6 +85,8 @@ public: instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); Initialize(); + events.Reset(); + DespawnSentries(); } void AttackStart(Unit* who) override @@ -112,36 +117,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 +146,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 +169,44 @@ public: if (victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_SLAY); } + + void UpdateAI(uint32 diff) override + { + 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; + default: + 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..652b4815be0 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,20 @@ 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 +320,9 @@ public: uiRemoveNpc = 0; // might not have been reset after a wipe on a boss. } break; + case DATA_ZURAMAT: + zuramatDead = true; + break; } } @@ -393,19 +402,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 +461,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 +543,7 @@ public: return false; } + zuramatDead = false; return true; } @@ -557,6 +574,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..b05da4b994c 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,21 @@ 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 +}; + +#define MAX_PRE_EVENT_PORTAL 3 + +ObjectGuid preEventPortalGUID[MAX_PRE_EVENT_PORTAL] = { ObjectGuid::Empty }; + +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 { @@ -320,6 +333,9 @@ public: Initialize(); me->SetReactState(REACT_AGGRESSIVE); + for (uint8 i = 0; i < MAX_PRE_EVENT_PORTAL; i++) + if (TempSummon* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[i], TEMPSUMMON_MANUAL_DESPAWN)) + preEventPortalGUID[i] = summon->GetGUID(); std::list<Creature*> GuardList; me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); @@ -347,25 +363,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 +386,6 @@ public: if (Creature* pGuard = *itr) { pGuard->SetVisible(false); - pGuard->SetReactState(REACT_PASSIVE); } } uiTimer = 2000; @@ -391,11 +398,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 +602,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 +632,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 +702,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 +750,23 @@ 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)) + { + ObjectGuid portalGUID = portal->GetGUID(); + for (uint8 i = 0; i < MAX_PRE_EVENT_PORTAL; i++) + if (portalGUID == preEventPortalGUID[i]) + portalLocationID = i * 2; + } + } + else + { + portalLocationID = instance->GetData(DATA_PORTAL_LOCATION); + Reset(); + } } public: @@ -691,7 +781,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 +789,7 @@ struct violet_hold_trashAI : public npc_escortAI break; case 2: if (waypointId == 7) - CreatureStartAttackDoor(); + CreatureStartAttackDoor(); break; case 3: if (waypointId == 8) @@ -1203,7 +1293,7 @@ public: if (uiConeOfColdTimer <= diff) { - DoCast(SPELL_CONE_OF_COLD); + DoCast(SPELL_CONE_OF_COLD); uiConeOfColdTimer = 5000; } else uiConeOfColdTimer -= diff; } @@ -1334,7 +1424,6 @@ public: } }; - class npc_violet_hold_arcane_sphere : public CreatureScript { public: @@ -1391,6 +1480,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 +1522,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 diff --git a/src/server/scripts/Northrend/isle_of_conquest.cpp b/src/server/scripts/Northrend/isle_of_conquest.cpp index ba21b60f11d..f5514cc51a5 100644 --- a/src/server/scripts/Northrend/isle_of_conquest.cpp +++ b/src/server/scripts/Northrend/isle_of_conquest.cpp @@ -161,19 +161,6 @@ class spell_ioc_gunship_portal : public SpellScriptLoader * Position: X: 7.305609 Y: -0.095246 Z: 34.51022 O: 0 */ caster->TeleportTo(GetHitCreature()->GetWorldLocation(), TELE_TO_NOT_LEAVE_TRANSPORT); - /* - * HACK: This aura should be added by 20212 and 20213 but can't find any SMSG_SPELL_GO. Could't find their position. - * ServerToClient: SMSG_AURA_UPDATE (0x0072) - * [0] CasterGUID: Full: xxxxx Type: Unit Entry: 20212 Low: xxx - * [0] Flags: None (0) - * [0] Caster Level: 60 - * [0] Spell ID: 66656 - * [0] Charges: 0 - * [0] Effect Mask: 1 - * [0] Slot: 37 - * Guid: Full: xxxxx Type: Player2 Low: xxxxx - */ - caster->AddAura(SPELL_PARACHUTE, caster); } void Register() override diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index 93e13667cb0..a1b218010aa 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -824,6 +824,60 @@ class spell_brewfest_barker_bunny : public SpellScriptLoader } }; +enum TorchSpells +{ + SPELL_TORCH_TOSSING_TRAINING = 45716, + SPELL_TORCH_TOSSING_PRACTICE = 46630, + SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE = 45719, + SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE = 46651, + SPELL_BRAZIERS_HIT = 45724 +}; + +// 45724 - Braziers Hit! +class spell_midsummer_braziers_hit : public SpellScriptLoader +{ + public: + spell_midsummer_braziers_hit() : SpellScriptLoader("spell_midsummer_braziers_hit") { } + + class spell_midsummer_braziers_hit_AuraScript : public AuraScript + { + PrepareAuraScript(spell_midsummer_braziers_hit_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_TORCH_TOSSING_TRAINING) || !sSpellMgr->GetSpellInfo(SPELL_TORCH_TOSSING_PRACTICE)) + return false; + return true; + } + + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Player* player = GetTarget()->ToPlayer(); + if (!player) + return; + + if ((player->HasAura(SPELL_TORCH_TOSSING_TRAINING) && GetStackAmount() == 8) || (player->HasAura(SPELL_TORCH_TOSSING_PRACTICE) && GetStackAmount() == 20)) + { + if (player->GetTeam() == ALLIANCE) + player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE, true); + else if (player->GetTeam() == HORDE) + player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE, true); + Remove(); + } + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_midsummer_braziers_hit_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAPPLY)); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_midsummer_braziers_hit_AuraScript(); + } +}; + void AddSC_holiday_spell_scripts() { // Love is in the Air @@ -850,4 +904,6 @@ void AddSC_holiday_spell_scripts() new spell_brewfest_relay_race_intro_force_player_to_throw(); new spell_brewfest_dismount_ram(); new spell_brewfest_barker_bunny(); + // Midsummer Fire Festival + new spell_midsummer_braziers_hit(); } |
