diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp | 146 |
1 files changed, 88 insertions, 58 deletions
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 44dff329433..54b10dbde6e 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -19,13 +19,9 @@ SDName: Boss Malygos Script Data End */ -/* Check TO DOs in the script, but here is one essential hack to be removed only if core changes are made: - At Wyrmrest Skytalon script I make it kill player if the drake dies, because at 100.0f z yards the map is - supposed to handle the air as ground or something to which you will fall and die. However, currently maps/movements - stop flying below it, however they don't stop movementflag FALLING. Which leads to % player falling at - 1200000.0 z, - Which leads due to some desynch to not being able to press "release spirit". - Also the teleport using while on vehicle is lacking support, but soon will have. - You can still click afer leaving vehicle. */ +/* Add support for using Exit Portal while on drake and find why at 100 Z ground + falling creature/player bodies aren't stopped (when players reach it should die). + Also there is release button unavailability sometimes not sure why. */ #include "ScriptMgr.h" #include "ScriptedCreature.h" @@ -123,8 +119,8 @@ enum Spells SPELL_ARCANE_BOMB_TRIGGER = 56430, SPELL_ARCANE_BOMB_KNOCKBACK_DAMAGE = 56431, SPELL_ARCANE_OVERLOAD_1 = 56432, // casted by npc Arcane Overload ID: 30282 - // SPELL_ARCANE_OVERLOAD_2 = 56435, // Triggered by 56432 - // SPELL_ARCANE_OVERLOAD_3 = 56438, // Triggered by 56432 + // SPELL_ARCANE_OVERLOAD_2 = 56435, // Triggered by 56432 - resizing target + // SPELL_ARCANE_OVERLOAD_3 = 56438, // Triggered by 56432 - damage reduction SPELL_SURGE_OF_POWER_P_II = 56505, // SPELL_SURGE_OF_POWER_TRIGGERED = 56548, SPELL_ARCANE_SHOCK = 57058, // used by Nexus Lords @@ -287,9 +283,9 @@ Position const MeleeHoverDisksWaypoints[MAX_MELEE_HOVER_DISK_WAYPOINTS] = Position const MalygosPositions[MAX_MALYGOS_POS] = { { 754.544f, 1301.71f, 320.01f, 0.0f }, // Point destroy platform - { 754.393f, 1301.27f, 292.91f, 0.0f }, // Point vortex - { 754.362f, 1301.61f, 266.17f, 0.0f }, // Land after vortex - { 754.695f, 1301.66f, 316.65f, 0.0f }, // Point surge of Power phase II + { 754.393f, 1301.27f, 292.91f, 0.0f }, // Point Vortex + { 754.362f, 1301.61f, 266.17f, 0.0f }, // Point land after Vortex + { 754.695f, 1301.66f, 316.65f, 0.0f }, // Point Surge of Power phase II { 755.681f, 1298.41f, 220.06f, 0.0f } // Point idle phase III }; @@ -324,8 +320,9 @@ enum MiscData // Target guids DATA_LAST_OVERLOAD_GUID = 13, // used to store last Arcane Overload guid DATA_FIRST_SURGE_TARGET_GUID = 14, - //DATA_SECOND_SURGE_TARGET_GUID = 15, - //DATA_THIRD_SURGE_TARGET_GUID = 16 + // DATA_SECOND_SURGE_TARGET_GUID = 15, + // DATA_THIRD_SURGE_TARGET_GUID = 16, + DATA_LAST_TARGET_BARRAGE_GUID = 17, NUM_MAX_SURGE_TARGETS = 3, }; @@ -359,6 +356,7 @@ public: _summonDeaths = 0; _preparingPulsesChecker = 0; _arcaneOverloadGUID = NULL; + _lastHitByArcaneBarrageGUID = NULL; memset(_surgeTargetGUID, 0, sizeof(_surgeTargetGUID)); _killSpamFilter = false; @@ -425,6 +423,8 @@ public: { if (type >= DATA_FIRST_SURGE_TARGET_GUID && type < DATA_FIRST_SURGE_TARGET_GUID + NUM_MAX_SURGE_TARGETS) return _surgeTargetGUID[type - DATA_FIRST_SURGE_TARGET_GUID]; + else if (type == DATA_LAST_TARGET_BARRAGE_GUID) + return _lastHitByArcaneBarrageGUID; return 0; } @@ -441,6 +441,8 @@ public: case DATA_FIRST_SURGE_TARGET_GUID + 2: _surgeTargetGUID[type - DATA_FIRST_SURGE_TARGET_GUID] = guid; break; + case DATA_LAST_TARGET_BARRAGE_GUID: + _lastHitByArcaneBarrageGUID = guid; } } @@ -486,13 +488,13 @@ public: events.CancelEventGroup(0); events.CancelEventGroup(1); events.CancelEventGroup(2); - me->GetMotionMaster()->Initialize(); - me->StopMoving(); // Vehicles shouldn't be despawned with 0 delay if the call comes from virtual function that overrides PassengerBoarded. // Aside from that he doesn't despawn both vehicles and arcane overloads right away, but with some delay. DummyEntryCheckPredicate pred; summons.DoAction(ACTION_DELAYED_DESPAWN, pred); Talk(SAY_END_P_TWO); + me->GetMotionMaster()->Initialize(); + me->StopMoving(); me->GetMotionMaster()->MovePoint(POINT_DESTROY_PLATFORM_P_TWO, MalygosPositions[0]); events.ScheduleEvent(EVENT_LIGHT_DIMENSION_CHANGE, 1*IN_MILLISECONDS, 0, PHASE_TWO); break; @@ -507,7 +509,7 @@ public: break; case ACTION_CYCLIC_MOVEMENT: Movement::MoveSplineInit init(me); - FillCirclePath(MalygosPositions[3], 130.0f, 283.2763f, init.Path(), true); + FillCirclePath(MalygosPositions[3], 120.0f, 283.2763f, init.Path(), true); init.SetFly(); init.SetCyclic(); init.Launch(); @@ -826,12 +828,12 @@ public: break; } - DoCast(me, SPELL_ARCANE_STORM_P_I, true); + DoCastAOE(SPELL_ARCANE_STORM_P_I, true); events.ScheduleEvent(EVENT_ARCANE_STORM, 10*IN_MILLISECONDS, 0, PHASE_ONE); } else if (_phase == PHASE_THREE) { - DoCast(me, SPELL_ARCANE_STORM_P_III, true); + DoCastAOE(SPELL_ARCANE_STORM_P_III, true); events.ScheduleEvent(EVENT_ARCANE_STORM, urand(6, 12)*IN_MILLISECONDS, 0, PHASE_THREE); } break; @@ -841,7 +843,7 @@ public: Position randomPosOnRadius; // Hardcodded retail value, reason is Z getters can fail... (TO DO: Change to getter when height calculation works on 100%!) randomPosOnRadius.m_positionZ = 283.0521f; - alexstraszaBunny->GetNearPoint2D(randomPosOnRadius.m_positionX, randomPosOnRadius.m_positionY, 130.0f, alexstraszaBunny->GetAngle(me)); + alexstraszaBunny->GetNearPoint2D(randomPosOnRadius.m_positionX, randomPosOnRadius.m_positionY, 120.0f, alexstraszaBunny->GetAngle(me)); me->GetMotionMaster()->MovePoint(POINT_FLY_OUT_OF_PLATFORM_P_TWO, randomPosOnRadius); _flyingOutOfPlatform = true; } @@ -952,7 +954,7 @@ public: if (passenger->GetTypeId() == TYPEID_PLAYER) { Talk(EMOTE_SURGE_OF_POWER_WARNING_P3, passenger->GetGUID()); - DoCast(tempSurgeTarget, SPELL_SURGE_OF_POWER_PHASE_3_10); + DoCast(tempSurgeTarget, SPELL_SURGE_OF_POWER_PHASE_3_10, true); } } } @@ -961,14 +963,14 @@ public: else if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) { memset(_surgeTargetGUID, 0, sizeof(_surgeTargetGUID)); - DoCastAOE(SPELL_SURGE_OF_POWER_WARNING_SELECTOR_25); + DoCastAOE(SPELL_SURGE_OF_POWER_WARNING_SELECTOR_25, true); } events.ScheduleEvent(EVENT_SURGE_OF_POWER_P_THREE, urand(9, 18)*IN_MILLISECONDS, 0, PHASE_THREE); break; case EVENT_STATIC_FIELD: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, false, SPELL_RIDE_RED_DRAGON_BUDDY)) - DoCast(target, SPELL_STATIC_FIELD_MISSLE); + DoCast(target, SPELL_STATIC_FIELD_MISSLE, true); events.ScheduleEvent(EVENT_STATIC_FIELD, urand(15, 30)*IN_MILLISECONDS, 0, PHASE_THREE); break; @@ -992,10 +994,10 @@ public: { if (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_10, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(), - HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); + HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); else if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_25, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(), - HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); + HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); } me->SummonCreature(NPC_ALEXSTRASZA, AlexstraszaSpawnPos, TEMPSUMMON_MANUAL_DESPAWN); @@ -1044,7 +1046,8 @@ public: uint8 _summonDeaths; // Keeps count of arcane trash. uint8 _preparingPulsesChecker; // In retail they use 2 preparing pulses with 7 sec CD, after they pass 2 seconds. uint64 _arcaneOverloadGUID; // Last Arcane Overload summoned to know to which should visual be cast to (the purple ball, not bubble). - uint64 _surgeTargetGUID[3]; // All these three are used to keep current tagets to which warning should be sent + uint64 _lastHitByArcaneBarrageGUID; // Last hit player by Arcane Barrage, will be removed if targets > 1. + uint64 _surgeTargetGUID[3]; // All these three are used to keep current tagets to which warning should be sent. bool _killSpamFilter; // Prevent text spamming on killed player by helping implement a CD. bool _canAttack; // Used to control attacking (Move Chase not being applied after Stop Attack, only few times should act like this). @@ -1386,15 +1389,11 @@ class npc_nexus_lord : public CreatureScript { } - void IsSummonedBy(Unit* /*summoner*/) - { - _events.ScheduleEvent(EVENT_ARCANE_SHOCK, 2*IN_MILLISECONDS); - _events.ScheduleEvent(EVENT_HASTE_BUFF, 12*IN_MILLISECONDS); - } - void DoAction(int32 /*action*/) { _events.ScheduleEvent(EVENT_NUKE_DUMMY, 1); + _events.ScheduleEvent(EVENT_ARCANE_SHOCK, 2*IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_HASTE_BUFF, 12*IN_MILLISECONDS); } void UpdateAI(uint32 diff) @@ -1409,8 +1408,9 @@ class npc_nexus_lord : public CreatureScript switch (eventId) { case EVENT_ARCANE_SHOCK: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 5.0f, true), SPELL_ARCANE_SHOCK); - _events.ScheduleEvent(EVENT_ARCANE_SHOCK, 3*IN_MILLISECONDS); + if (Unit* victim = SelectTarget(SELECT_TARGET_RANDOM, 0, 5.0f, true)) + DoCast(victim, SPELL_ARCANE_SHOCK); + _events.ScheduleEvent(EVENT_ARCANE_SHOCK, urand(7, 15)*IN_MILLISECONDS); break; case EVENT_HASTE_BUFF: DoCast(me, SPELL_HASTE); @@ -1463,8 +1463,7 @@ class npc_scion_of_eternity : public CreatureScript void IsSummonedBy(Unit* /*summoner*/) { - _events.SetPhase(PHASE_TWO); - _events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(14, 24)*IN_MILLISECONDS, 0, PHASE_TWO); + _events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(14, 29)*IN_MILLISECONDS); } void EnterCombat(Unit* /*who*/) @@ -1489,7 +1488,7 @@ class npc_scion_of_eternity : public CreatureScript { case EVENT_ARCANE_BARRAGE: DoCast(me, SPELL_ARCANE_BARRAGE); - _events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(3, 12)*IN_MILLISECONDS, 0, PHASE_TWO); + _events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(3, 15)*IN_MILLISECONDS); break; } } @@ -1536,7 +1535,6 @@ public: void UpdateAI (uint32 /*diff*/) { - } void DoAction(int32 /*action*/) @@ -1625,12 +1623,6 @@ public: } } - void JustDied(Unit* /*killer*/) - { - // TO DOs: check script beginning for more info. - _summoner->Kill(_summoner, true); - } - private: Player* _summoner; EventMap _events; @@ -1756,16 +1748,19 @@ class spell_malygos_random_portal : public SpellScriptLoader class IsCreatureVehicleCheck { public: - IsCreatureVehicleCheck() { } + IsCreatureVehicleCheck(bool isVehicle) : _isVehicle(isVehicle) { } bool operator()(WorldObject* obj) { if (Unit* unit = obj->ToUnit()) if (unit->GetTypeId() == TYPEID_UNIT && unit->GetVehicleKit()) - return true; + return _isVehicle; - return false; + return !_isVehicle; } + + private: + bool _isVehicle; }; class spell_malygos_arcane_storm : public SpellScriptLoader @@ -1798,26 +1793,34 @@ class spell_malygos_arcane_storm : public SpellScriptLoader Creature* malygos = GetCaster()->ToCreature(); if (GetSpellInfo()->Id == SPELL_ARCANE_STORM_P_III) { - IsCreatureVehicleCheck check; + // Resize list only to objects that are vehicles. + IsCreatureVehicleCheck check(true); Trinity::Containers::RandomResizeList(targets, check, (malygos->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL ? 4 : 10)); } else Trinity::Containers::RandomResizeList(targets, (malygos->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL ? 4 : 10)); + + // Both casts should start approx at same time (with SPELL_ARCANE_STORM_EXTRA_VISUAL having advantage - it should lead) + // and not when purple light visual has already hit target. + filteredTargets = targets; } void HandleVisual() { - if (!GetHitUnit()) + if (filteredTargets.empty()) return; - GetCaster()->CastSpell(GetHitUnit(), SPELL_ARCANE_STORM_EXTRA_VISUAL, true); + for (std::list<WorldObject*>::iterator itr = filteredTargets.begin(); itr != filteredTargets.end(); ++itr) + GetCaster()->CastSpell((*itr)->ToUnit(), SPELL_ARCANE_STORM_EXTRA_VISUAL, true); } void Register() { OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_malygos_arcane_storm_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - AfterHit += SpellHitFn(spell_malygos_arcane_storm_SpellScript::HandleVisual); + OnCast += SpellCastFn(spell_malygos_arcane_storm_SpellScript::HandleVisual); } + + std::list<WorldObject*> filteredTargets; }; SpellScript* GetSpellScript() const @@ -1992,7 +1995,6 @@ class spell_nexus_lord_align_disk_aggro : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { Creature* caster = GetCaster()->ToCreature(); - if (Creature* target = GetHitCreature()) target->GetMotionMaster()->MoveChase(caster->getVictim()); } @@ -2038,7 +2040,7 @@ class spell_scion_of_eternity_arcane_barrage : public SpellScriptLoader bool Load() { - return GetCaster()->GetTypeId() == TYPEID_UNIT; + return GetCaster()->GetTypeId() == TYPEID_UNIT && GetCaster()->GetInstanceScript() != NULL; } void FilterMeleeHoverDiskPassangers(std::list<WorldObject*>& targets) @@ -2046,6 +2048,17 @@ class spell_scion_of_eternity_arcane_barrage : public SpellScriptLoader if (targets.empty()) return; + Creature* caster = GetCaster()->ToCreature(); + InstanceScript* instance = caster->GetInstanceScript(); + Creature* malygos = caster->GetMap()->GetCreature(instance->GetData64(DATA_MALYGOS)); + + // If max possible targets are more than 1 then Scions wouldn't select previosly selected target, + // in longer terms this means if spell picks target X then 2nd cast of this spell will pick smth else + // and if 3rd picks X again 4th will pick smth else (by not limiting the cast to certain caster). + if (targets.size() > 1) + if (malygos && malygos->AI()->GetGUID(DATA_LAST_TARGET_BARRAGE_GUID) != NULL) + targets.remove_if(Trinity::ObjectGUIDCheck(malygos->AI()->GetGUID(DATA_LAST_TARGET_BARRAGE_GUID))); + // Remove players not on Hover Disk from second list std::list<WorldObject*> playersWithoutDisk; IsPlayerOnHoverDisk check(false); @@ -2061,14 +2074,30 @@ class spell_scion_of_eternity_arcane_barrage : public SpellScriptLoader // and have 2 seconds long aura still lasting. Used to give healers some time. if (!targets.empty()) targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_ARCANE_BARRAGE_DAMAGE)); + + // Now we resize the list to max output targets which can be only 1 + // to take it's guid and send/store it to DATA_LAST_TARGET_BARRAGE_GUID. + // Same target is never picked until next pick pass. This doesn't mean + // that it can't be hit more than once. In fact all is chance and raid speed. + if (!targets.empty()) + { + if (targets.size() > 1) + Trinity::Containers::RandomResizeList(targets, 1); + + if (WorldObject* filteredTarget = targets.front()) + if (malygos) + malygos->AI()->SetGUID(filteredTarget->GetGUID(), DATA_LAST_TARGET_BARRAGE_GUID); + } } void TriggerDamageSpellFromPlayer() { - Player* hitTarget = GetHitPlayer(); - // There is some proc in this spell I have absolutely no idea of use, but just in case... - TriggerCastFlags triggerFlags = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_DISALLOW_PROC_EVENTS); - hitTarget->CastSpell(hitTarget, SPELL_ARCANE_BARRAGE_DAMAGE, triggerFlags, NULL, NULL, GetCaster()->GetGUID()); + if (Player* hitTarget = GetHitPlayer()) + { + // There is some proc in this spell I have absolutely no idea of use, but just in case... + TriggerCastFlags triggerFlags = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_DISALLOW_PROC_EVENTS); + hitTarget->CastSpell(hitTarget, SPELL_ARCANE_BARRAGE_DAMAGE, triggerFlags, NULL, NULL, GetCaster()->GetGUID()); + } } void Register() @@ -2303,7 +2332,8 @@ class spell_malygos_surge_of_power_warning_selector_25 : public SpellScriptLoade { // This spell hits only vehicles (SMSG_SPELL_GO target) Creature* caster = GetCaster()->ToCreature(); - targets.remove_if(IsCreatureVehicleCheck()); + // Remove all objects that aren't* vehicles. + targets.remove_if(IsCreatureVehicleCheck(false)); if (targets.empty()) return; |