diff options
author | Trista <aconstantgoal@abv.bg> | 2013-03-06 04:03:07 +0200 |
---|---|---|
committer | Trista <aconstantgoal@abv.bg> | 2013-03-06 04:10:26 +0200 |
commit | a79143fc88b3061a6747ad84a58dbf18cb2ebe43 (patch) | |
tree | 9c8989dce34ad0ecab3e624d14f0827fba0ba9ac | |
parent | 856551c9817b4aa0f8d2770326bf03ebe616388f (diff) |
Scripts/Eye of Eternity: Fix Arcane Barrage handling and a lot other mechanics
* Nexus Lords had a bit imba timers on Arcane Shork, sorry for it wrong information. Now have retail timers.
* Scion of Eternity Arcane Barrage had wrong timers also for intial random cast and for each after. They were a bit lower.
* Implemented source mechanic that will always differ target of cast X to target of next cast. All rest is % chance based on various cases can happen.
* Removed condition for Arcane Overlaod bubbles now aura will never ("disappear"), if two are at one place they will override each other, but damage will never be leaked test enough times so don't bother stating opposite. Damage in ALL cases is reduced.
* Made Malygos range outside of platform a bit shorter to match retail real distance that also fixed timings of bubble mechanic, now they will always be there, if you are fast enough.
* And finally fixed some slight stuff on Malygos spell. Now all will work and Arcane Storm has it's correct visual mechanic again.
Closes #9321
-rw-r--r-- | sql/updates/world/2013_03_06_00_world_conditions.sql | 3 | ||||
-rw-r--r-- | src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp | 146 |
2 files changed, 91 insertions, 58 deletions
diff --git a/sql/updates/world/2013_03_06_00_world_conditions.sql b/sql/updates/world/2013_03_06_00_world_conditions.sql new file mode 100644 index 00000000000..b7faaa379e6 --- /dev/null +++ b/sql/updates/world/2013_03_06_00_world_conditions.sql @@ -0,0 +1,3 @@ +-- Delete condition that can cause some damage reduction "spikes" leading to leaking damage, +-- or in simple language some delay can cause not applying. +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=56438; 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; |