diff --git a/sql/updates/world/4.3.4/custom_2018_02_17_00_world.sql b/sql/updates/world/4.3.4/custom_2018_02_17_00_world.sql new file mode 100644 index 00000000000..f6eaf46138d --- /dev/null +++ b/sql/updates/world/4.3.4/custom_2018_02_17_00_world.sql @@ -0,0 +1,63 @@ +-- Template Updates +-- Helix Gearbreaker +UPDATE `creature_template` SET `scriptname`= 'boss_helix_gearbreaker', `flags_extra`= `flags_extra`|1 WHERE `entry`= 47296; +-- Lumbering Oaf +UPDATE `creature_template` SET `scriptname`= 'npc_helix_lumbering_oaf' WHERE `entry`= 47297; +UPDATE `creature_template` SET `minlevel`= 87, `maxlevel`= 87 WHERE `entry`= 48939; +-- Sticky Bomb +UPDATE `creature_template` SET `difficulty_entry_1`= 49134, `scriptname`= 'npc_helix_sticky_bomb' WHERE `entry`= 47314; +UPDATE `creature_template` SET `minlevel`= 85, `maxlevel`= 85, `faction`= 14, `unit_flags`= 33554432 WHERE `entry`= 49134; +-- Helix' Crew +UPDATE `creature_template` SET `scriptname`= 'npc_helix_crew' WHERE `entry`= 49139; + +-- Texts +DELETE FROM `creature_text` WHERE `CreatureID` IN (47296, 47297, 49139); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `Comment`) VALUES +-- Helix Gearbreaker +(47296, 0, 0, 'The mistress will pay me handsomely for your heads!', 14, 0, 100, 0, 0, 20849, 47474, 'Helix Gearbreaker - Aggro'), +(47296, 1, 0, 'Bombs away!', 14, 0, 100, 0, 0, 20847, 47475, 'Helix Gearbreaker - Bomb'), +(47296, 2, 0, 'Ready Oafie? Throw!', 14, 0, 100, 0, 0, 20848, 47476, 'Helix Gearbreaker - Throw Helix'), +(47296, 3, 0, 'Helix attaches a bomb to $n''s chest!', 41, 0, 100, 0, 0, 0, 47544, 'Helix Gearbreaker - Announce Bomb'), +(47296, 4, 0, 'I didn''t need him! Not when I''ve got YOU oafs!', 14, 0, 100, 0, 0, 20846, 47573, 'Helix Gearbreaker - Oaf Death'), +(47296, 5, 0, 'Only ten copper? You''re not even worth killing!', 14, 0, 100, 0, 0, 20845, 47574, 'Helix Gearbreaker - Slay'), +(47296, 6, 0, 'The scales...have...tipped...', 14, 0, 100, 0, 0, 20844, 47575, 'Helix Gearbreaker - Death'), +(47297, 0, 1, 'No...NO!', 14, 0, 100, 0, 0, 20854, 47480, 'Lumbering Oaf to Helix Gearbreaker'), +(47297, 1, 2, 'OAF SMASH!!', 14, 0, 100, 0, 0, 0, 48117, 'Lumbering Oaf to Helix Gearbreaker'), +(49139, 0, 0, 'Blowin'' em'' to bits, boss!', 12, 0, 100, 0, 0, 0, 49191, 'Helix Crew - Throw Bomb'); + +-- Spells +DELETE FROM `spell_script_names` WHERE `ScriptName` IN +('spell_helix_throw_bomb_targeting', +'spell_helix_sticky_bomb_periodic_trigger', +'spell_helix_explode', +'spell_helix_oaf_grab_targeting', +'spell_helix_force_player_to_ride_oaf', +'spell_helix_oaf_smash', +'spell_helix_ride_face_targeting', +'spell_helix_ride_vehicle', +'spell_helix_ride_face_timer_aura', +'spell_helix_chest_bomb_emote', +'spell_helix_chest_bomb'); + +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(88268, 'spell_helix_throw_bomb_targeting'), +(88329, 'spell_helix_sticky_bomb_periodic_trigger'), +(88321, 'spell_helix_explode'), +(91567, 'spell_helix_explode'), +(88974, 'spell_helix_explode'), +(91566, 'spell_helix_explode'), +(88289, 'spell_helix_oaf_grab_targeting'), +(88278, 'spell_helix_force_player_to_ride_oaf'), +(88300, 'spell_helix_oaf_smash'), +(91568, 'spell_helix_oaf_smash'), +(88349, 'spell_helix_ride_face_targeting'), +(88360, 'spell_helix_ride_vehicle'), +(88351, 'spell_helix_ride_face_timer_aura'), +(91572, 'spell_helix_chest_bomb_emote'), +(88352, 'spell_helix_chest_bomb'); + +-- Conditions +DELETE FROM `conditions` WHERE `SourceEntry`= 88295 AND `SourceTypeOrReferenceId`= 13; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ScriptName`, `Comment`) VALUES +(13, 1, 88295, 0, 0, 31, 0, 3, 45979, 0, 0, 0, '', 'Charge - Target General Purpose Bunny'), +(13, 1, 88295, 0, 0, 35, 0, 1, 20, 3, 0, 0, '', 'Charge - Target Distance Must be Higher than 15 yards'); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 6873cedb619..9d01d2d8f7b 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -4144,6 +4144,13 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(2); // Combat Range }); + // Helix Gearbreaker 88295 + // Fists of Frost + ApplySpellFix({ 88295 }, [](SpellInfo* spellInfo) + { + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_100_YARDS); + }); + // END OF DEADMINES SPELLS // diff --git a/src/server/scripts/EasternKingdoms/Deadmines/boss_helix_gearbreaker.cpp b/src/server/scripts/EasternKingdoms/Deadmines/boss_helix_gearbreaker.cpp new file mode 100644 index 00000000000..2ee192a03b3 --- /dev/null +++ b/src/server/scripts/EasternKingdoms/Deadmines/boss_helix_gearbreaker.cpp @@ -0,0 +1,1018 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#include "ObjectMgr.h" +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" +#include "Player.h" +#include "Vehicle.h" +#include "deadmines.h" + +enum Texts +{ + // Helix Gearbreaker + SAY_AGGRO = 0, + SAY_STICKY_BOMB = 1, + SAY_THROW_HELIX = 2, + SAY_ANNOUNCE_CHEST_BOMB = 3, + SAY_OAF_DEAD = 4, + SAY_SLAY = 5, + SAY_DEATH = 6, + + // Lumbering Oaf + SAY_OAF_SMASH_1 = 0, + SAY_OAF_SMASH_2 = 1, + + // Helix' Crew + SAY_CREW_JOIN_FIGHT = 0 +}; + +enum Spells +{ + // Helix Gearbreaker + SPELL_THROW_BOMB_TARGETING = 88268, + SPELL_RIDE_FACE_TARGETING = 88349, + SPELL_RIDE_VEHICLE_OAF = 52391, + SPELL_RIDE_VEHICLE = 88360, + SPELL_HELIX_RIDE_FACE_TIMER_AURA = 88351, + SPELL_HELIX_RIDE = 88337, + SPELL_HELIX_CHEST_BOMB_EMOTE = 91572, + SPELL_CHEST_BOMB = 88352, + SPELL_CHEST_BOMB_DAMAGE = 88250, + SPELL_OAFGUARD = 90546, + SPELL_EMOTE_TALK = 79506, + + // Lumbering Oaf + SPELL_OAF_GRAB_TARGETING = 88289, + SPELL_CHARGE = 88295, + SPELL_OAF_SMASH = 88300, + + // Sticky Bomb + SPELL_ARMING_VISUAL_YELLOW = 88315, + SPELL_ARMING_VISUAL_ORANGE = 88316, + SPELL_ARMING_VISUAL_RED = 88317, + SPELL_STICKY_BOMB_ARMED_STATE = 88319, + SPELL_STICKY_BOMB_PERIODIC_TRIGGER = 88329, + SPELL_EXPLODE = 88974, +}; + +enum DisplayIds +{ + DISPLAY_ID_BOMB_INVISIBLE = 11686, +}; + +enum Events +{ + // Helix Gearbreaker + EVENT_STICKY_BOMB = 1, + EVENT_OAF_SMASH, + EVENT_THROW_HELIX, + EVENT_RIDE_FACE, + + // Lumbering Oaf + EVENT_MOVE_TO_PREPARE_POSITION, + EVENT_CHARGE_DEST, + + // Sticky Bomb + EVENT_ARMING_VISUAL_YELLOW, + EVENT_ARMING_VISUAL_ORANGE, + EVENT_ARMING_VISUAL_RED, + EVENT_PREPARED_STATE, + EVENT_PERIODIC_TRIGGER, + EVENT_EXPLOSION_COUNTDOWN, + EVENT_COUNTDOWN, + + // Helix' Crew + EVENT_TALK_READY, +}; + +enum Actions +{ + ACTION_OAF_SMASH = 1, + ACTION_OAF_DEAD, + ACTION_TALK_CREW, +}; + +enum Points +{ + POINT_CHARGE_PREP = 1 +}; + +enum Phases +{ + PHASE_1 = 1, + PHASE_2 = 2 +}; + +Position const ChargePreparationPos = { -289.8343f, -526.2162f, 49.83808f }; +Position const ChargePos = { -289.5868f, -489.5746f, 49.91263f }; + +Position const HelixCrewPositions[] = +{ + { -283.8438f, -503.3698f, 60.512f, 1.902409f }, + { -285.8681f, -503.8264f, 60.55348f, 5.009095f }, + { -292.6788f, -503.7274f, 60.27357f, 4.468043f }, + { -289.8316f, -503.4063f, 60.36356f, 1.815142f } +}; + +class boss_helix_gearbreaker : public CreatureScript +{ + public: + boss_helix_gearbreaker() : CreatureScript("boss_helix_gearbreaker") { } + + struct boss_helix_gearbreakerAI : public BossAI + { + boss_helix_gearbreakerAI(Creature* creature) : BossAI(creature, DATA_HELIX_GEARBREAKER) + { + Initialize(); + } + + void Initialize() + { + _crewSummoned = false; + } + + void Reset() override + { + _Reset(); + Initialize(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + Talk(SAY_AGGRO); + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + + if (Creature* lumberingOaf = instance->GetCreature(DATA_LUMBERING_OAF)) + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, lumberingOaf); + + events.SetPhase(PHASE_1); + events.ScheduleEvent(EVENT_STICKY_BOMB, Seconds(5), 0, PHASE_1); + events.ScheduleEvent(EVENT_OAF_SMASH, Seconds(16), 0, PHASE_1); + events.ScheduleEvent(EVENT_THROW_HELIX, Seconds(34), 0, PHASE_1); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + summons.DespawnAll(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + _EnterEvadeMode(); + summons.DespawnAll(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + if (Creature* lumberingOaf = instance->GetCreature(DATA_LUMBERING_OAF)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, lumberingOaf); + lumberingOaf->DespawnOrUnsummon(0, Seconds(30)); + } + me->DespawnOrUnsummon(); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_OAF_DEAD: + Talk(SAY_OAF_DEAD); + DoCastSelf(SPELL_EMOTE_TALK); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveAurasDueToSpell(SPELL_OAFGUARD); + events.SetPhase(PHASE_2); + if (!me->GetVehicleBase()) + events.ScheduleEvent(EVENT_RIDE_FACE, Seconds(2) + Milliseconds(500), 0, PHASE_2); + break; + default: + break; + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (damage >= me->GetHealth() && me->HasAura(SPELL_OAFGUARD)) + damage = me->GetHealth() - 1; + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_STICKY_BOMB: + if (!_crewSummoned && IsHeroic()) + { + for (uint8 i = 0; i < 4; i++) + { + if (Creature* crew = DoSummon(NPC_HELIX_CREW, HelixCrewPositions[i], 0, TEMPSUMMON_MANUAL_DESPAWN)) + if (i == 1) + crew->AI()->DoAction(ACTION_TALK_CREW); + } + _crewSummoned = true; + } + + DoCastAOE(SPELL_THROW_BOMB_TARGETING, true); + events.Repeat(Seconds(3) + Milliseconds(500)); + break; + case EVENT_OAF_SMASH: + if (Creature* lumberingOaf = me->GetVehicleCreatureBase()) + { + lumberingOaf->AI()->DoAction(ACTION_OAF_SMASH); + events.RescheduleEvent(EVENT_STICKY_BOMB, Seconds(10), 0, PHASE_1); + events.Repeat(Seconds(52)); + } + break; + case EVENT_THROW_HELIX: + Talk(SAY_THROW_HELIX); + events.ScheduleEvent(EVENT_RIDE_FACE, Seconds(2)); + events.Repeat(Seconds(52)); + break; + case EVENT_RIDE_FACE: + DoCastAOE(SPELL_RIDE_FACE_TARGETING, true); + events.RescheduleEvent(EVENT_STICKY_BOMB, Seconds(20), 0, PHASE_1); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } + private: + bool _crewSummoned; + }; + + CreatureAI* GetAI(Creature *creature) const override + { + return GetDeadminesAI(creature); + } +}; + +class npc_helix_lumbering_oaf : public CreatureScript +{ + public: + npc_helix_lumbering_oaf() : CreatureScript("npc_helix_lumbering_oaf") { } + + struct npc_helix_lumbering_oafAI : public ScriptedAI + { + npc_helix_lumbering_oafAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void JustEngagedWith(Unit* who) override + { + if (Creature* helix = _instance->GetCreature(DATA_HELIX_GEARBREAKER)) + if (!helix->IsInCombat()) + helix->AI()->AttackStart(who); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_OAF_SMASH: + Talk(SAY_OAF_SMASH_1); + DoCastAOE(SPELL_OAF_GRAB_TARGETING, true); + break; + default: + break; + } + } + + void PassengerBoarded(Unit* passenger, int8 /*seatId*/, bool apply) override + { + if (!passenger) + return; + + if (apply && passenger->GetTypeId() == TYPEID_PLAYER) + { + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_MOVE_TO_PREPARE_POSITION, Milliseconds(500)); + } + + if (!apply && passenger->GetEntry() == BOSS_HELIX_GEARBREAKER && me->IsInCombat()) + _instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, passenger); + else if (apply && passenger->GetEntry() == BOSS_HELIX_GEARBREAKER && me->IsInCombat()) + _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, passenger); + } + + void MovementInform(uint32 type, uint32 point) override + { + if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) + return; + + switch (point) + { + case POINT_CHARGE_PREP: + me->SetFacingTo(1.570796f); + _events.ScheduleEvent(EVENT_CHARGE_DEST, Seconds(1)); + break; + default: + break; + } + } + + void JustDied(Unit* /*killer*/) override + { + _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + if (Creature* helix = _instance->GetCreature(DATA_HELIX_GEARBREAKER)) + helix->AI()->DoAction(ACTION_OAF_DEAD); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_MOVE_TO_PREPARE_POSITION: + Talk(SAY_OAF_SMASH_2); + me->GetMotionMaster()->MovePoint(POINT_CHARGE_PREP, ChargePreparationPos, true); + break; + case EVENT_CHARGE_DEST: + DoCastAOE(SPELL_CHARGE); + break; + default: + break; + } + } + DoMeleeAttackIfReady(); + } + private: + EventMap _events; + InstanceScript* _instance; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetDeadminesAI(creature); + } +}; + +class npc_helix_sticky_bomb : public CreatureScript +{ + public: + npc_helix_sticky_bomb() : CreatureScript("npc_helix_sticky_bomb") { } + + struct npc_helix_sticky_bombAI : public PassiveAI + { + npc_helix_sticky_bombAI(Creature* creature) : PassiveAI(creature) + { + Initialize(); + } + + void Initialize() + { + _countdown = 0; + } + + void IsSummonedBy(Unit* /*summoner*/) override + { + _events.ScheduleEvent(EVENT_ARMING_VISUAL_YELLOW, Seconds(2) + Milliseconds(500)); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ARMING_VISUAL_YELLOW: + DoCastSelf(SPELL_ARMING_VISUAL_YELLOW); + _events.ScheduleEvent(EVENT_ARMING_VISUAL_ORANGE, Seconds(1)); + break; + case EVENT_ARMING_VISUAL_ORANGE: + DoCastSelf(SPELL_ARMING_VISUAL_ORANGE); + _events.ScheduleEvent(EVENT_ARMING_VISUAL_RED, Seconds(2)); + break; + case EVENT_ARMING_VISUAL_RED: + DoCastSelf(SPELL_ARMING_VISUAL_RED); + _events.ScheduleEvent(EVENT_PREPARED_STATE, Seconds(1)); + break; + case EVENT_PREPARED_STATE: + DoCastSelf(SPELL_STICKY_BOMB_ARMED_STATE); + _events.ScheduleEvent(EVENT_PERIODIC_TRIGGER, Seconds(1)); + break; + case EVENT_PERIODIC_TRIGGER: + DoCastSelf(SPELL_STICKY_BOMB_PERIODIC_TRIGGER); + _events.ScheduleEvent(EVENT_EXPLOSION_COUNTDOWN, Seconds(15) + Milliseconds(500)); + break; + case EVENT_EXPLOSION_COUNTDOWN: + DoCastSelf(SPELL_ARMING_VISUAL_RED); + _events.ScheduleEvent(EVENT_COUNTDOWN, Seconds(1)); + break; + case EVENT_COUNTDOWN: + if (_countdown != 3) + { + DoCastSelf(SPELL_ARMING_VISUAL_RED); + _countdown++; + _events.Repeat(Seconds(1)); + } + else + { + me->RemoveAllAuras(); + DoCastSelf(SPELL_EXPLODE, true); + } + break; + default: + break; + } + } + } + private: + EventMap _events; + uint8 _countdown; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetDeadminesAI(creature); + } +}; + +class npc_helix_crew : public CreatureScript +{ + public: + npc_helix_crew() : CreatureScript("npc_helix_crew") { } + + struct npc_helix_crewAI : public PassiveAI + { + npc_helix_crewAI(Creature* creature) : PassiveAI(creature) { } + + void IsSummonedBy(Unit* /*summoner*/) override + { + _events.ScheduleEvent(EVENT_STICKY_BOMB, Seconds(1)); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_TALK_CREW: + _events.ScheduleEvent(EVENT_TALK_READY, Seconds(1)); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_STICKY_BOMB: + DoCastAOE(SPELL_THROW_BOMB_TARGETING, true); + _events.Repeat(Seconds(5)); + break; + case EVENT_TALK_READY: + Talk(SAY_CREW_JOIN_FIGHT); + break; + default: + break; + } + } + } + private: + EventMap _events; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetDeadminesAI(creature); + } +}; + +class spell_helix_throw_bomb_targeting : public SpellScriptLoader +{ + public: + spell_helix_throw_bomb_targeting() : SpellScriptLoader("spell_helix_throw_bomb_targeting") { } + + class spell_helix_throw_bomb_targeting_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_throw_bomb_targeting_SpellScript); + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + Trinity::Containers::RandomResize(targets, 1); + } + + void HandleHit(SpellEffIndex effIndex) + { + GetCaster()->CastSpell(GetHitUnit(), GetSpellInfo()->Effects[effIndex].BasePoints, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_helix_throw_bomb_targeting_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_helix_throw_bomb_targeting_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_helix_throw_bomb_targeting_SpellScript(); + } +}; + +class spell_helix_sticky_bomb_periodic_trigger : public SpellScriptLoader +{ + public: + spell_helix_sticky_bomb_periodic_trigger() : SpellScriptLoader("spell_helix_sticky_bomb_periodic_trigger") { } + + class spell_helix_sticky_bomb_periodic_trigger_AuraScript : public AuraScript + { + PrepareAuraScript(spell_helix_sticky_bomb_periodic_trigger_AuraScript); + + void HandleTick(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + if (Unit* caster = GetCaster()) + if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell)) + if (Player* player = caster->SelectNearestPlayer(spell->Effects[EFFECT_0].CalcRadius())) + if (!player->GetVehicleBase()) + caster->CastSpell(GetCaster()->GetVictim(), GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_helix_sticky_bomb_periodic_trigger_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_helix_sticky_bomb_periodic_trigger_AuraScript(); + } +}; + +class spell_helix_explode : public SpellScriptLoader +{ + public: + spell_helix_explode() : SpellScriptLoader("spell_helix_explode") { } + + class spell_helix_explode_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_explode_SpellScript); + + void HandleLaunch(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + if (caster->GetTypeId() == TYPEID_UNIT) + { + caster->RemoveAllAuras(); + caster->SetDisplayId(DISPLAY_ID_BOMB_INVISIBLE); + caster->ToCreature()->DespawnOrUnsummon(Seconds(2) + Milliseconds(400)); + } + } + } + + void Register() override + { + OnEffectLaunch += SpellEffectFn(spell_helix_explode_SpellScript::HandleLaunch, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } + private: + uint8 _targetsCounter; + }; + + SpellScript* GetSpellScript() const override + { + return new spell_helix_explode_SpellScript(); + } +}; + +class spell_helix_oaf_grab_targeting : public SpellScriptLoader +{ + public: + spell_helix_oaf_grab_targeting() : SpellScriptLoader("spell_helix_oaf_grab_targeting") { } + + class spell_helix_oaf_grab_targeting_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_oaf_grab_targeting_SpellScript); + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + Trinity::Containers::RandomResize(targets, 1); + } + + void HandleHit(SpellEffIndex effIndex) + { + GetCaster()->CastSpell(GetHitUnit(), GetSpellInfo()->Effects[effIndex].BasePoints, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_helix_oaf_grab_targeting_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_helix_oaf_grab_targeting_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_helix_oaf_grab_targeting_SpellScript(); + } +}; + +class spell_helix_force_player_to_ride_oaf : public SpellScriptLoader +{ + public: + spell_helix_force_player_to_ride_oaf() : SpellScriptLoader("spell_helix_force_player_to_ride_oaf") { } + + class spell_helix_force_player_to_ride_oaf_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_force_player_to_ride_oaf_SpellScript); + + void HandleHit(SpellEffIndex effIndex) + { + if (Unit* caster = GetCaster()) + if (Unit* target = GetHitUnit()) + if (caster->GetVehicleKit()) + { + target->CastStop(); + target->CastSpell(caster, GetSpellInfo()->Effects[effIndex].BasePoints, false); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_helix_force_player_to_ride_oaf_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_helix_force_player_to_ride_oaf_SpellScript(); + } +}; + +class spell_helix_oaf_smash : public SpellScriptLoader +{ + public: + spell_helix_oaf_smash() : SpellScriptLoader("spell_helix_oaf_smash") { } + + class spell_helix_oaf_smash_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_oaf_smash_SpellScript); + + void HandleLaunch(SpellEffIndex effIndex) + { + // We do our own knockback here because the boss is immune to knockback effects + // ... and because we have to use some additional spellinfo data + if (Unit* caster = GetCaster()) + { + float angle = caster->GetOrientation(); + float x = caster->GetPositionX() + cos(angle) * 1; + float y = caster->GetPositionX() + sin(angle) * 1; + float speedxy = float(GetSpellInfo()->Effects[effIndex].MiscValue) * 0.1; + float speedz = float(GetSpellInfo()->Effects[effIndex].MiscValueB) * 0.1; + + caster->KnockbackFrom(x, y, speedxy, speedz); + + if (Creature* creature = caster->ToCreature()) + { + creature->SetReactState(REACT_AGGRESSIVE); + creature->setAttackTimer(BASE_ATTACK, creature->GetFloatValue(UNIT_FIELD_BASEATTACKTIME)); + } + } + } + + void Register() + { + OnEffectLaunch += SpellEffectFn(spell_helix_oaf_smash_SpellScript::HandleLaunch, EFFECT_0, SPELL_EFFECT_KNOCK_BACK); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_helix_oaf_smash_SpellScript(); + } +}; + +class spell_helix_ride_face_targeting : public SpellScriptLoader +{ + public: + spell_helix_ride_face_targeting() : SpellScriptLoader("spell_helix_ride_face_targeting") { } + + class spell_helix_ride_face_targeting_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_ride_face_targeting_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_HELIX_RIDE, + SPELL_HELIX_RIDE_FACE_TIMER_AURA + }); + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_HELIX_RIDE)); + + if (targets.empty()) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(caster, SPELL_HELIX_RIDE_FACE_TIMER_AURA, true); + return; + } + + Trinity::Containers::RandomResize(targets, 1); + } + + void HandleHit(SpellEffIndex effIndex) + { + if (Unit* caster = GetCaster()) + { + if (Unit* target = GetHitUnit()) + { + if (Creature* creature = caster->ToCreature()) + if (creature->IsAIEnabled) + creature->getThreatManager().resetAllAggro(); + + caster->CastSpell(caster, SPELL_HELIX_RIDE_FACE_TIMER_AURA, true); + // Until we have core support for hiding vehicle datas clientside, we handle it like this + target->AddAura(SPELL_HELIX_RIDE, target); + caster->CastSpell(target, GetSpellInfo()->Effects[effIndex].BasePoints, true); + } + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_helix_ride_face_targeting_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_helix_ride_face_targeting_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_helix_ride_face_targeting_SpellScript(); + } +}; + +class spell_helix_ride_vehicle : public SpellScriptLoader +{ + public: + spell_helix_ride_vehicle() : SpellScriptLoader("spell_helix_ride_vehicle") { } + + class spell_helix_ride_vehicle_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_ride_vehicle_SpellScript); + + void HandleHit(SpellEffIndex effIndex) + { + if (Unit* caster = GetCaster()) + { + if (Creature* creature = caster->ToCreature()) + { + if (creature->IsAIEnabled) + { + creature->SetReactState(REACT_AGGRESSIVE); + creature->AI()->AttackStart(GetHitUnit()); + } + } + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_helix_ride_vehicle_SpellScript::HandleHit, EFFECT_1, SPELL_EFFECT_THREAT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_helix_ride_vehicle_SpellScript(); + } + + class spell_helix_ride_vehicle_AuraScript : public AuraScript + { + PrepareAuraScript(spell_helix_ride_vehicle_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_RIDE_VEHICLE }); + } + + void AfterRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Unit* owner = GetOwner()->ToUnit()) + owner->RemoveAurasDueToSpell(SPELL_HELIX_RIDE); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_helix_ride_vehicle_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_CONTROL_VEHICLE, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_helix_ride_vehicle_AuraScript(); + } +}; + +class spell_helix_ride_face_timer_aura: public SpellScriptLoader +{ + public: + spell_helix_ride_face_timer_aura() : SpellScriptLoader("spell_helix_ride_face_timer_aura") { } + + class spell_helix_ride_face_timer_aura_AuraScript : public AuraScript + { + PrepareAuraScript(spell_helix_ride_face_timer_aura_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_RIDE_VEHICLE_OAF, + SPELL_CHEST_BOMB, + SPELL_HELIX_CHEST_BOMB_EMOTE + }); + } + + void AfterRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Unit* owner = GetOwner()->ToUnit()) + { + if (InstanceScript* instance = owner->GetInstanceScript()) + { + if (owner->GetMap() && owner->GetMap()->IsHeroic()) + if (Unit* vehicle = owner->GetVehicleBase()) + if (vehicle->GetTypeId() == TYPEID_PLAYER) + { + owner->CastSpell(vehicle, SPELL_CHEST_BOMB, true); + owner->CastSpell(vehicle, SPELL_HELIX_CHEST_BOMB_EMOTE, true); + } + + if (Creature* oaf = instance->GetCreature(DATA_LUMBERING_OAF)) + { + if (oaf->IsAlive()) + { + owner->CastSpell(oaf, SPELL_RIDE_VEHICLE_OAF); + if (Creature* creature = owner->ToCreature()) + { + creature->AttackStop(); + creature->SetReactState(REACT_PASSIVE); + } + } + else + owner->CastSpell(owner, SPELL_RIDE_FACE_TARGETING, true); + } + + } + } + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_helix_ride_face_timer_aura_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_helix_ride_face_timer_aura_AuraScript(); + } +}; + +class spell_helix_chest_bomb_emote : public SpellScriptLoader +{ + public: + spell_helix_chest_bomb_emote() : SpellScriptLoader("spell_helix_chest_bomb_emote") { } + + class spell_helix_chest_bomb_emote_SpellScript : public SpellScript + { + PrepareSpellScript(spell_helix_chest_bomb_emote_SpellScript); + + void HandleScriptEffect(SpellEffIndex effIndex) + { + if (Unit* caster = GetCaster()) + if (Creature* creature = caster->ToCreature()) + if (creature->IsAIEnabled) + creature->AI()->Talk(SAY_ANNOUNCE_CHEST_BOMB, GetHitUnit()); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_helix_chest_bomb_emote_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_helix_chest_bomb_emote_SpellScript(); + } +}; + +class spell_helix_chest_bomb : public SpellScriptLoader +{ + public: + spell_helix_chest_bomb() : SpellScriptLoader("spell_helix_chest_bomb") { } + + class spell_helix_chest_bomb_AuraScript : public AuraScript + { + PrepareAuraScript(spell_helix_chest_bomb_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_CHEST_BOMB_DAMAGE }); + } + + void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) + { + if (Unit* owner = GetOwner()->ToUnit()) + owner->CastSpell(owner, SPELL_CHEST_BOMB_DAMAGE, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_helix_chest_bomb_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_helix_chest_bomb_AuraScript(); + } +}; + +void AddSC_boss_helix_gearbreaker() +{ + new boss_helix_gearbreaker(); + new npc_helix_lumbering_oaf(); + new npc_helix_sticky_bomb(); + new npc_helix_crew(); + new spell_helix_throw_bomb_targeting(); + new spell_helix_sticky_bomb_periodic_trigger(); + new spell_helix_explode(); + new spell_helix_oaf_grab_targeting(); + new spell_helix_force_player_to_ride_oaf(); + new spell_helix_oaf_smash(); + new spell_helix_ride_face_targeting(); + new spell_helix_ride_vehicle(); + new spell_helix_ride_face_timer_aura(); + new spell_helix_chest_bomb_emote(); + new spell_helix_chest_bomb(); +} diff --git a/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h b/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h index d1f49fe28d5..0fc63eb56ef 100644 --- a/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h +++ b/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h @@ -27,7 +27,7 @@ enum DMDataTypes { // Encounters DATA_GLUBTOK = 0, - DATA_HELUX_GEARBREAKER = 1, + DATA_HELIX_GEARBREAKER = 1, DATA_FOE_REAPER_5000 = 2, DATA_ADMIRAL_RIPSNARL = 3, DATA_CAPTAIN_COOKIE = 4, @@ -35,12 +35,14 @@ enum DMDataTypes DATA_TEAM_IN_INSTANCE = 6, DATA_FIREWALL_PLATTER = 7, + DATA_LUMBERING_OAF = 8 }; enum DMCreatures { // Bosses BOSS_GLUBTOK = 47162, + BOSS_HELIX_GEARBREAKER = 47296, // Horde Creatures NPC_SLINKY_SHARPSHIV = 46906, @@ -68,6 +70,9 @@ enum DMCreatures NPC_GENERAL_PURPOSE_BUNNY_L2 = 47242, NPC_FIRE_BLOSSOM_BUNNY = 47282, NPC_FROST_BLOSSOM_BUNNY = 47284, + NPC_LUMBERING_OAF = 47297, + NPC_HELIX_CREW = 49139, + NPC_STICKY_BOMB = 47314 }; enum DMGameObjects @@ -76,7 +81,8 @@ enum DMGameObjects GO_IRONCLAD_DOOR = 16397, GO_DEFIAS_CANNON = 16398, GO_DOOR_LEVER = 101833, - GO_MR_SMITE_CHEST = 144111 + GO_MAST_ROOM_DOOR = 16400, + GO_HEAVY_DOOR = 17153 }; diff --git a/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp b/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp index aa848eea3c1..795d75c0c86 100644 --- a/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp +++ b/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp @@ -24,8 +24,10 @@ ObjectData const creatureData[] = { - { BOSS_GLUBTOK, DATA_GLUBTOK }, - { 0, 0 }, // END + { BOSS_GLUBTOK, DATA_GLUBTOK }, + { BOSS_HELIX_GEARBREAKER, DATA_HELIX_GEARBREAKER }, + { NPC_LUMBERING_OAF, DATA_LUMBERING_OAF }, + { 0, 0 }, // END }; ObjectData const gameobjectData[] = @@ -35,8 +37,10 @@ ObjectData const gameobjectData[] = DoorData const doorData[] = { - { GO_FACTORY_DOOR, DATA_GLUBTOK, DOOR_TYPE_PASSAGE }, - { 0, 0, DOOR_TYPE_ROOM }, // END + { GO_FACTORY_DOOR, DATA_GLUBTOK, DOOR_TYPE_PASSAGE }, + { GO_MAST_ROOM_DOOR, DATA_HELIX_GEARBREAKER, DOOR_TYPE_PASSAGE }, + { GO_HEAVY_DOOR, DATA_HELIX_GEARBREAKER, DOOR_TYPE_ROOM }, + { 0, 0, DOOR_TYPE_ROOM }, // END }; class instance_deadmines : public InstanceMapScript @@ -94,6 +98,14 @@ class instance_deadmines : public InstanceMapScript if (Creature* glubtok = GetCreature(DATA_GLUBTOK)) glubtok->AI()->JustSummoned(creature); break; + case NPC_STICKY_BOMB: + if (Creature* helix = GetCreature(DATA_HELIX_GEARBREAKER)) + helix->AI()->JustSummoned(creature); + break; + case NPC_LUMBERING_OAF: + if (creature->isDead() && GetBossState(DATA_HELIX_GEARBREAKER) != DONE) + creature->Respawn(); + break; default: break; } @@ -135,7 +147,6 @@ class instance_deadmines : public InstanceMapScript protected: uint32 _teamInInstance; - GuidSet _arcaneBeamBunnyGUIDList; }; diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index 5c8857e1e39..06549bd23dd 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -70,6 +70,7 @@ void AddSC_instance_blackwing_lair(); void AddSC_deadmines(); //Deadmines void AddSC_instance_deadmines(); void AddSC_boss_glubtok(); +void AddSC_boss_helix_gearbreaker(); void AddSC_gilneas_c1(); //Gilneas void AddSC_gnomeregan(); //Gnomeregan void AddSC_instance_gnomeregan(); @@ -272,6 +273,7 @@ void AddEasternKingdomsScripts() AddSC_deadmines(); //Deadmines AddSC_instance_deadmines(); AddSC_boss_glubtok(); + AddSC_boss_helix_gearbreaker(); AddSC_gilneas_c1(); //Gilneas AddSC_gnomeregan(); //Gnomeregan AddSC_instance_gnomeregan();