From f58a2441aa87c82228f61edc75f95ddc1835ad4f Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sun, 3 Jul 2016 14:21:02 +0200 Subject: [PATCH] Scripts/Firelands: Baleroc Encounter By Krudor --- sql/updates/world/2016_07_03_00_world.sql | 39 + src/server/game/Entities/Vehicle/Vehicle.cpp | 3 +- src/server/game/Spells/SpellMgr.cpp | 9 + .../Kalimdor/Firelands/boss_baleroc.cpp | 883 ++++++++++++++++++ .../scripts/Kalimdor/Firelands/firelands.h | 19 + .../Kalimdor/Firelands/instance_firelands.cpp | 109 +++ .../Kalimdor/kalimdor_script_loader.cpp | 2 + 7 files changed, 1063 insertions(+), 1 deletion(-) create mode 100644 sql/updates/world/2016_07_03_00_world.sql create mode 100644 src/server/scripts/Kalimdor/Firelands/boss_baleroc.cpp diff --git a/sql/updates/world/2016_07_03_00_world.sql b/sql/updates/world/2016_07_03_00_world.sql new file mode 100644 index 00000000000..bb530845055 --- /dev/null +++ b/sql/updates/world/2016_07_03_00_world.sql @@ -0,0 +1,39 @@ +-- +DELETE FROM `creature` WHERE `guid` =250021; +INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`) VALUES +(250021, 53494, 720, 15, 89.9669, -62.8343, 54.9484, 3.16296, 7200); + +DELETE FROM `achievement_criteria_data` WHERE `criteria_id` = 17577; +INSERT INTO `achievement_criteria_data` VALUES (17577, 11, 0, 0, 'achievement_share_the_pain'); + +UPDATE `creature_template` SET `ScriptName`='boss_baleroc' WHERE `entry`=53494; +UPDATE `creature_template` SET `ScriptName`='npc_shard_of_torment' WHERE `entry`=53495; + +DELETE FROM `creature_text` WHERE `entry`=53494; +INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(53494, 4, 2, 'Behold your weakness.', 14, 0, 100, 0, 0, 24451, 0, 0, 'Baleroc'), +(53494, 0, 0, 'You are forbidden from my master\'s domain, mortals.', 14, 0, 100, 0, 0, 24441, 0, 0, 'Baleroc'), +(53494, 1, 0, 'Fool mortals. Hurl yourselves into your own demise!', 14, 0, 100, 0, 0, 24446, 0, 0, 'Baleroc'), +(53494, 2, 0, 'Burn beneath my molten fury!', 14, 0, 100, 0, 0, 24459, 0, 0, 'Baleroc'), +(53494, 3, 0, 'By the Firelord\'s command, you, too, shall perish!', 14, 0, 100, 0, 0, 24447, 0, 0, 'Baleroc'), +(53494, 4, 0, 'None shall pass!', 14, 0, 100, 0, 0, 24452, 0, 0, 'Baleroc'), +(53494, 4, 1, 'You have been judged.', 14, 0, 100, 0, 0, 24449, 0, 0, 'Baleroc'), +(53494, 5, 0, 'Your flesh is forfeit to the fires of this realm.', 14, 0, 100, 0, 0, 24450, 0, 0, 'Baleroc'), +(53494, 7, 0, 'Mortal filth... the master\'s keep is forbidden...', 14, 0, 100, 0, 0, 24444, 0, 0, 'Baleroc'), +(53494, 6, 0, '%s goes into a berserker rage!', 16, 0, 100, 0, 0, 0, 0, 0, 'Baleroc'), +(53494, 8, 0, '|TInterface\\Icons\\inv_sword_09.blp:20|t%s readies his |cFFFF0000Inferno Blade|r!', 41, 0, 100, 0, 0, 0, 0, 0, 'Baleroc'), +(53494, 9, 0, '|TInterface\\Icons\\spell_shadow_curse:20|t%s readies his |cFF551A8BDecimation Blade|r!', 41, 0, 100, 0, 0, 0, 0, 0, 'Baleroc'); + +DELETE FROM `spell_script_names` WHERE `spell_id` IN (99253,99256,99259,99353,99489,99515,99516,99517,100230,100231,100232); +INSERT INTO `spell_script_names` VALUES +(99253, 'spell_baleroc_torment'), +(99256, 'spell_baleroc_tormented'), +(99259, 'spell_shards_of_torment'), +(99353, 'spell_decimating_strike'), +(99489, 'spell_baleroc_tormented_heroic'), +(99515, 'spell_countdown_p1'), +(99516, 'spell_countdown_p2'), +(99517, 'spell_countdown_p3'), +(100230, 'spell_baleroc_tormented'), +(100231, 'spell_baleroc_tormented'), +(100232, 'spell_baleroc_tormented'); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index b091a790349..3310fb50db7 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -504,7 +504,8 @@ Vehicle* Vehicle::RemovePassenger(Unit* unit) // only for flyable vehicles if (unit->IsFlying()) - _me->CastSpell(unit, VEHICLE_SPELL_PARACHUTE, true); + if (_me->IsFriendlyTo(unit)) + _me->CastSpell(unit, VEHICLE_SPELL_PARACHUTE, true); if (_me->GetTypeId() == TYPEID_UNIT && _me->ToCreature()->IsAIEnabled) _me->ToCreature()->AI()->PassengerBoarded(unit, seat->first, false); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 694ecd4c0bb..ce75cb7e2c6 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3817,6 +3817,15 @@ void SpellMgr::LoadSpellInfoCorrections() break; // ENDOF ISLE OF CONQUEST SPELLS // + // Firelands Spells + // + case 99256: // Torment + case 100230: //Torment + case 100231: //Torment + case 100232: //Torment + spellInfo->Attributes |= SPELL_ATTR0_NEGATIVE_1; + break; + // End of Firelands Spells default: break; } diff --git a/src/server/scripts/Kalimdor/Firelands/boss_baleroc.cpp b/src/server/scripts/Kalimdor/Firelands/boss_baleroc.cpp new file mode 100644 index 00000000000..ecd161188bd --- /dev/null +++ b/src/server/scripts/Kalimdor/Firelands/boss_baleroc.cpp @@ -0,0 +1,883 @@ +/* + * Copyright (C) 2008-2014 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 . + */ + +/* +***To-do list*** +*** +***Implement Vital Spark - http://www.wowhead.com/spell=99262 +***Implement Vital Flame - http://www.wowhead.com/spell=99263 +*** +***Redesign achievement data storage system +*/ + +#include "firelands.h" + +enum Spells +{ + SPELL_INFERNO_BLADE = 99350, + SPELL_INFERNO_STRIKE = 99351, + SPELL_DECIMATION_BLADE = 99352, + SPELL_DECIMATION_BLADE_2 = 99405, + SPELL_DECIMATING_STRIKE = 99353, + + SPELL_BLAZE_OF_GLORY = 99252, + SPELL_INCENDIARY_SOUL = 99369, + + SPELL_SHARDS_OF_TORMENT = 99259, + SPELL_SHARDS_OF_TORMENT_2 = 99260, + SPELL_TORMENT_COSMETIC_1 = 99258, + SPELL_TORMENT = 99254, + SPELL_TORMENT_PERIODIC = 99255, + SPELL_WAVE_OF_TORMENT = 99261, + SPELL_TORMENTED_20 = 99257, + SPELL_TORMENTED_30 = 99402, + SPELL_TORMENTED_40 = 99403, + SPELL_TORMENTED_60 = 99404, + + SPELL_COUNTDOWN = 99515, + SPELL_COUNTDOWN_2 = 99516, + SPELL_COUNTDOWN_3 = 99517, + SPELL_COUNTDOWN_4 = 99518, + SPELL_COUNTDOWN_5 = 99519, + + SPELL_BERSERK = 26662, +}; + +enum Events +{ + EVENT_BLADE = 1, + EVENT_RESTORE_WEAPONS = 2, + EVENT_INCENDIARY_SOUL = 3, + EVENT_SHARDS_OF_TORMENT = 4, + EVENT_COUNTDOWN = 5, + EVENT_BERSERK = 6, + + EVENT_SHARD_SPAWN_EFFECT = 7, + EVENT_UNLOCK_YELLSPAM = 8, +}; + +enum Emotes +{ + EMOTE_AGGRO = 0, + EMOTE_SHARDS_OF_TORMENT = 1, + EMOTE_INFERNO_BLADE = 2, + EMOTE_DECIMATION_BLADE = 3, + EMOTE_KILL = 4, + EMOTE_ENRAGE = 5, + EMOTE_ENRAGE_2 = 6, + EMOTE_DEATH = 7, + ABILITY_INFERNO_BLADE = 8, + ABILITY_DECIMATION_BLADE = 9, +}; + +enum Guids +{ + GUID_TORMENTED = 1, +}; + +enum Misc +{ + EQUIP_DECIMATION_BLADE = 71082, + EQUIP_INFERNO_BLADE = 71138, +}; + +class boss_baleroc : public CreatureScript +{ + public: + boss_baleroc() : CreatureScript("boss_baleroc") { } + + struct boss_balerocAI : public BossAI + { + boss_balerocAI(Creature* creature) : BossAI(creature, DATA_BALEROC) + { + } + + bool _canYellKilledPlayer; + + void Reset() OVERRIDE + { + _Reset(); + me->SetMaxPower(POWER_RAGE, 0); + SetEquipmentSlots(true); + me->SetCanDualWield(true); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) OVERRIDE + { + switch (spell->Id) + { + case SPELL_INFERNO_BLADE: + SetEquipmentSlots(false, EQUIP_INFERNO_BLADE, EQUIP_UNEQUIP); + me->SetCanDualWield(false); + events.ScheduleEvent(EVENT_RESTORE_WEAPONS, 15*IN_MILLISECONDS); + break; + case SPELL_DECIMATION_BLADE: + case SPELL_DECIMATION_BLADE_2: + SetEquipmentSlots(false, EQUIP_DECIMATION_BLADE, EQUIP_UNEQUIP); + me->SetCanDualWield(false); + events.ScheduleEvent(EVENT_RESTORE_WEAPONS, 15*IN_MILLISECONDS); + break; + default: + break; + } + } + + void EnterCombat(Unit* target) OVERRIDE + { + _EnterCombat(); + Talk(EMOTE_AGGRO); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + events.ScheduleEvent(EVENT_INCENDIARY_SOUL, 8.5*IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SHARDS_OF_TORMENT, 5*IN_MILLISECONDS); + if (me->GetMap()->IsHeroic()) + events.ScheduleEvent(EVENT_COUNTDOWN, 26*IN_MILLISECONDS); + events.ScheduleEvent(EVENT_BLADE, 30.5*IN_MILLISECONDS); + events.ScheduleEvent(EVENT_BERSERK, 6*MINUTE*IN_MILLISECONDS); + + //Reset our achievement list. We do this here and not in reset, as the debuff may have been spread after the boss has reset. + for (int i = 0; i < 25; i++) + { + _sharedThePain[i].player = 0; + _sharedThePain[i].tormented = 0; + } + } + + void KilledUnit(Unit* who) OVERRIDE + { + if (who->GetTypeId() == TYPEID_PLAYER && _canYellKilledPlayer) + { + _canYellKilledPlayer = false; + events.ScheduleEvent(EVENT_UNLOCK_YELLSPAM, 8000); + Talk(EMOTE_KILL); + } + } + + void JustDied(Unit* /*killer*/) OVERRIDE + { + _JustDied(); + Talk(EMOTE_DEATH); + SetEquipmentSlots(true); + me->SetCanDualWield(true);; + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + if (Map* map = me->GetMap()) + { + Map::PlayerList const &PlayerList = map->GetPlayers(); + if (!PlayerList.isEmpty()) + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + if (i->GetSource()->ToPlayer()->HasQuestForItem(69848)) + { + DoCast(101093); + break; + } + } + } + + void EnterEvadeMode() OVERRIDE + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_BLAZE_OF_GLORY); + me->GetMotionMaster()->MoveTargetedHome(); + summons.DespawnAll(); + _DespawnAtEvade(); + } + + void DoMeleeAttackIfReady() OVERRIDE + { + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + Unit* victim = me->GetVictim(); + if (me->isAttackReady(BASE_ATTACK) && me->IsWithinMeleeRange(victim)) + { + if (me->HasAura(SPELL_DECIMATION_BLADE) || me->HasAura(SPELL_DECIMATION_BLADE_2)) + { + me->CastSpell(me->GetVictim(), SPELL_DECIMATING_STRIKE, false); + me->resetAttackTimer(BASE_ATTACK); + } + else if (me->HasAura(SPELL_INFERNO_BLADE)) + { + me->CastSpell(me->GetVictim(), SPELL_INFERNO_STRIKE, false); + me->AttackerStateUpdate(victim); + me->resetAttackTimer(BASE_ATTACK); + } + else + { + me->AttackerStateUpdate(victim); + me->resetAttackTimer(BASE_ATTACK); + } + } + if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK) && me->IsWithinMeleeRange(victim)) + { + me->AttackerStateUpdate(victim, OFF_ATTACK); + me->resetAttackTimer(OFF_ATTACK); + } + } + + void UpdateAI(uint32 diff) OVERRIDE + { + if (me->IsAlive()) + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BLADE: + switch (urand(1, 2)) + { + case 1: + DoCast(SPELL_INFERNO_BLADE); + Talk(EMOTE_INFERNO_BLADE); + Talk(ABILITY_INFERNO_BLADE); + break; + case 2: + DoCast(SPELL_DECIMATION_BLADE); + Talk(EMOTE_DECIMATION_BLADE); + Talk(ABILITY_DECIMATION_BLADE); + break; + } + events.ScheduleEvent(EVENT_BLADE, 47*IN_MILLISECONDS); + break; + case EVENT_RESTORE_WEAPONS: + SetEquipmentSlots(true); + me->SetCanDualWield(true); + break; + case EVENT_INCENDIARY_SOUL: + if (me->GetVictim()) + { + DoCast(me->GetVictim(), SPELL_BLAZE_OF_GLORY, false); + DoCast(SPELL_INCENDIARY_SOUL); + } + events.ScheduleEvent(EVENT_INCENDIARY_SOUL, 11.5*IN_MILLISECONDS); + break; + case EVENT_SHARDS_OF_TORMENT: + Talk(EMOTE_SHARDS_OF_TORMENT); + DoCast(SPELL_SHARDS_OF_TORMENT); + events.ScheduleEvent(EVENT_SHARDS_OF_TORMENT, 34*IN_MILLISECONDS); + break; + case EVENT_COUNTDOWN: + DoCast(SPELL_COUNTDOWN); + events.ScheduleEvent(EVENT_COUNTDOWN, 48*IN_MILLISECONDS); + break; + case EVENT_BERSERK: + DoCast(SPELL_BERSERK); + Talk(EMOTE_ENRAGE); + Talk(EMOTE_ENRAGE_2); + break; + case EVENT_UNLOCK_YELLSPAM: + _canYellKilledPlayer = true; + break; + default: + break; + } + } + DoMeleeAttackIfReady(); + } + + void SetGUID(uint64 guid, int32 type = 0) OVERRIDE + { + switch (type) + { + case GUID_TORMENTED: + for (int i = 0; i < 25; i++) + { + if (_sharedThePain[i].player == guid) + { + _sharedThePain[i].tormented++; + break; + } + else if (_sharedThePain[i].player == 0) + { + _sharedThePain[i].player = guid; + _sharedThePain[i].tormented++; + break; + } + } + break; + default: + break; + } + } + + bool SharedThePain() + { + for (int i = 0; _sharedThePain[i].player != 0; i++) + if (_sharedThePain[i].tormented > 3) + return false; + return true; + } + + private: + struct _sharedThePain + { + uint64 player; + uint32 tormented; + } + _sharedThePain[25]; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetFirelandsAI(creature); + } +}; + +//Used for achievements +typedef boss_baleroc::boss_balerocAI BalerocAI; + +class npc_shard_of_torment : public CreatureScript +{ + public: + npc_shard_of_torment() : CreatureScript("npc_shard_of_torment") { } + + struct npc_shard_of_tormentAI : public ScriptedAI + { + npc_shard_of_tormentAI(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + me->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISABLE_TURN); + me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); + _instance = creature->GetInstanceScript(); + } + + void IsSummonedBy(Unit* summoner) OVERRIDE + { + if (summoner->GetEntry() == NPC_BALEROC) + { + if (_instance->GetBossState(DATA_BALEROC) != IN_PROGRESS) + me->DespawnOrUnsummon(); + DoCast(SPELL_TORMENT_COSMETIC_1); + _events.ScheduleEvent(EVENT_SHARD_SPAWN_EFFECT, 5000); + me->SetInCombatWithZone(); + _baleroc = summoner->ToCreature(); + } + else + me->DespawnOrUnsummon(); + } + + void KilledUnit(Unit* who) OVERRIDE + { + if (who->GetTypeId() == TYPEID_PLAYER) + if (_baleroc) + _baleroc->AI()->KilledUnit(who); + } + + void UpdateAI(uint32 diff) + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHARD_SPAWN_EFFECT: + me->RemoveAurasDueToSpell(SPELL_TORMENT_COSMETIC_1); + DoCast(SPELL_TORMENT); + break; + default: + break; + } + } + } + + private: + InstanceScript* _instance; + EventMap _events; + Creature* _baleroc; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_shard_of_tormentAI(creature); + } +}; + +class spell_countdown_p1 : public SpellScriptLoader +{ + public: + spell_countdown_p1() : SpellScriptLoader("spell_countdown_p1") { } + + class spell_countdown_p1_SpellScript : public SpellScript + { + PrepareSpellScript(spell_countdown_p1_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void CastSpellLink() + { + if (target1->ToPlayer() && target2->ToPlayer()) + target1->ToPlayer()->CastSpell(target2->ToPlayer(), SPELL_COUNTDOWN_5, true); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + GetCaster()->CastSpell(target, SPELL_COUNTDOWN_2, false); + } + + void FilterTargets(std::list& targets) + { + + //Remove current tank if we have one + if (GetCaster()->GetVictim()) + targets.remove(GetCaster()->ToCreature()->GetVictim()); + + if (targets.size() < 2) + { + FinishCast(SPELL_FAILED_NO_VALID_TARGETS); + return; + } + + Trinity::Containers::RandomResizeList(targets, 2); + + std::list::const_iterator itr = targets.begin(); + target1 = (*itr); + itr++; + target2 = (*itr); + } + + void Register() OVERRIDE + { + AfterCast += SpellCastFn(spell_countdown_p1_SpellScript::CastSpellLink); + OnEffectHitTarget += SpellEffectFn(spell_countdown_p1_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_countdown_p1_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + + WorldObject* target1; + WorldObject* target2; + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_countdown_p1_SpellScript(); + } +}; + +class spell_countdown_p2 : public SpellScriptLoader +{ + public: + spell_countdown_p2() : SpellScriptLoader("spell_countdown_p2") { } + + class spell_countdown_p2_AuraScript : public AuraScript + { + PrepareAuraScript(spell_countdown_p2_AuraScript); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + GetTarget()->CastSpell((Unit*)NULL, SPELL_COUNTDOWN_4, true); + GetTarget()->ToPlayer()->RemoveAurasDueToSpell(SPELL_COUNTDOWN_5); + } + + void Register() OVERRIDE + { + AfterEffectRemove += AuraEffectRemoveFn(spell_countdown_p2_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_countdown_p2_AuraScript(); + } +}; + +class spell_countdown_p3 : public SpellScriptLoader +{ + public: + spell_countdown_p3() : SpellScriptLoader("spell_countdown_p3") { } + + class spell_countdown_p3_SpellScript : public SpellScript + { + PrepareSpellScript(spell_countdown_p3_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void FilterTargets(std::list& targets) + { + targets.remove_if(Trinity::UnitAuraCheck(false, SPELL_COUNTDOWN_2)); + targets.remove(GetCaster()); + + if (targets.empty()) + return; + + for (std::list::iterator itr = targets.begin(); itr != targets.end(); ++itr) + { + (*itr)->ToPlayer()->RemoveAurasDueToSpell(SPELL_COUNTDOWN_2); + (*itr)->ToPlayer()->RemoveAurasDueToSpell(SPELL_COUNTDOWN_5); + } + + GetCaster()->RemoveAurasDueToSpell(SPELL_COUNTDOWN_2); + GetCaster()->RemoveAurasDueToSpell(SPELL_COUNTDOWN_5); + } + + void Register() OVERRIDE + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_countdown_p3_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_countdown_p3_SpellScript(); + } +}; + +class spell_decimating_strike : public SpellScriptLoader +{ + public: + spell_decimating_strike() : SpellScriptLoader("spell_decimating_strike") { } + + class spell_decimating_strike_SpellScript : public SpellScript + { + PrepareSpellScript(spell_decimating_strike_SpellScript); + + bool Load() + { + if (GetCaster()->GetTypeId() != TYPEID_UNIT) + return false; + return true; + } + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_DECIMATING_STRIKE)) + return false; + return true; + } + + void ChangeDamage() + { + if (GetCaster()->GetVictim()) + { + uint32 health = GetCaster()->GetVictim()->GetMaxHealth(); + if (health*0.9 < 250000) + SetHitDamage(uint32(250000)); + else + SetHitDamage(uint32(health*0.9)); + } + else + SetHitDamage(uint32(250000)); + } + + void Register() + { + OnHit += SpellHitFn(spell_decimating_strike_SpellScript::ChangeDamage); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_decimating_strike_SpellScript(); + } +}; + +class spell_shards_of_torment : public SpellScriptLoader +{ + public: + spell_shards_of_torment() : SpellScriptLoader("spell_shards_of_torment") { } + + class spell_shards_of_torment_SpellScript : public SpellScript + { + PrepareSpellScript(spell_shards_of_torment_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetCaster()->CastSpell(GetHitUnit(), SPELL_SHARDS_OF_TORMENT_2, true); + } + + void FilterTargets(std::list& targets) + { + uint8 numtargets; + if (GetCaster()->GetMap()->Is25ManRaid()) + numtargets = 2; + else + numtargets = 1; + + while(targets.size() < numtargets) + numtargets--; + + + if ((targets.size() > numtargets) && GetCaster()->GetVictim()) + targets.remove(GetCaster()->ToCreature()->GetVictim()); //Safe to remove tank from list + + Trinity::Containers::RandomResizeList(targets, numtargets); + } + + void Register() OVERRIDE + { + OnEffectHitTarget += SpellEffectFn(spell_shards_of_torment_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_shards_of_torment_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_shards_of_torment_SpellScript(); + } +}; + +class PlayerCheck +{ + public: + bool operator()(WorldObject* object) const + { + if (object->GetTypeId() != TYPEID_PLAYER) + if (!object->ToPlayer()->IsAlive()) + if (object->ToPlayer()->IsGameMaster()) + return true; + + return false; + } +}; + +class spell_baleroc_torment : public SpellScriptLoader +{ + public: + spell_baleroc_torment() : SpellScriptLoader("spell_baleroc_torment") { } + + class spell_baleroc_torment_SpellScript : public SpellScript + { + PrepareSpellScript(spell_baleroc_torment_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void FilterTargets(std::list& targets) + { + targets.remove_if(PlayerCheck()); + + if (targets.empty()) + { + //No targets found, start pulsating immediately. + GetCaster()->ToCreature()->AI()->DoCast(SPELL_WAVE_OF_TORMENT); + return; + } + + targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster(), true)); + targets.resize(1); + + if ((*targets.begin())->GetDistance2d(GetCaster()) > 15.0f) + GetCaster()->ToCreature()->AI()->DoCast(SPELL_WAVE_OF_TORMENT); + else + { + if ((*targets.begin())->ToPlayer()->GetAura(SPELL_TORMENT_PERIODIC)) + if ((*targets.begin())->ToPlayer()->GetAura(SPELL_TORMENT_PERIODIC)->GetCaster() != GetCaster()) + GetCaster()->CastSpell((*targets.begin())->ToPlayer(), SPELL_TORMENT_PERIODIC, false); + else{ + } + else + GetCaster()->CastSpell((*targets.begin())->ToPlayer(), SPELL_TORMENT_PERIODIC, false); + } + } + + void Register() OVERRIDE + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_baleroc_torment_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_baleroc_torment_SpellScript(); + } +}; + +class spell_baleroc_tormented : public SpellScriptLoader +{ + public: + spell_baleroc_tormented() : SpellScriptLoader("spell_baleroc_tormented") { } + + class spell_baleroc_tormented_SpellScript : public SpellScript + { + PrepareSpellScript(spell_baleroc_tormented_SpellScript); + + void ChangeDamage() + { + //SetHitDamage(GetHitDamage()*GetHitUnit()->GetAuraCount(m_scriptSpellId)); + + //The above example seems wrong, wowhead say the damage is 3000 per tick on normal, and 4250 on heroic, + //while logs from retail say its 4000 normal, and 5000 heroic. + if (GetHitUnit()->GetMap()->IsHeroic()) + { + float damageMultiplier = 1.0f+((GetHitDamage()-4250)/4250); + SetHitDamage((5000*GetHitUnit()->GetAuraCount(m_scriptSpellId))*damageMultiplier); + } + else + { + float damageMultiplier = 1.0f+((GetHitDamage()-3000)/3000); + SetHitDamage((4000*GetHitUnit()->GetAuraCount(m_scriptSpellId))*damageMultiplier); + } + } + + void Register() + { + OnHit += SpellHitFn(spell_baleroc_tormented_SpellScript::ChangeDamage); + } + + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_baleroc_tormented_SpellScript(); + } + + class spell_baleroc_tormented_AuraScript : public AuraScript + { + PrepareAuraScript(spell_baleroc_tormented_AuraScript); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH) + { + if (GetTarget()->GetMap()->IsHeroic()) + GetTarget()->CastSpell(GetTarget(), SPELL_TORMENTED_40, true); + else + GetTarget()->CastSpell(GetTarget(), SPELL_TORMENTED_20, true); + } + } + + void Register() OVERRIDE + { + AfterEffectRemove += AuraEffectRemoveFn(spell_baleroc_tormented_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_baleroc_tormented_AuraScript(); + } +}; + +class spell_baleroc_tormented_debuff : public SpellScriptLoader +{ + public: + spell_baleroc_tormented_debuff() : SpellScriptLoader("spell_baleroc_tormented_debuff") { } + + class spell_baleroc_tormented_debuff_AuraScript : public AuraScript + { + PrepareAuraScript(spell_baleroc_tormented_debuff_AuraScript); + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (InstanceScript* instance = GetTarget()->GetInstanceScript()) + if (Creature* baleroc = ObjectAccessor::GetCreature(*GetTarget(), instance->GetData64(DATA_BALEROC))) + baleroc->AI()->SetGUID(GetTarget()->GetGUID(), GUID_TORMENTED); + } + + void Register() OVERRIDE + { + OnEffectApply += AuraEffectApplyFn(spell_baleroc_tormented_debuff_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_baleroc_tormented_debuff_AuraScript(); + } +}; + +class spell_baleroc_tormented_heroic : public SpellScriptLoader +{ + public: + spell_baleroc_tormented_heroic() : SpellScriptLoader("spell_baleroc_tormented_heroic") { } + + class spell_baleroc_tormented_heroic_SpellScript : public SpellScript + { + PrepareSpellScript(spell_baleroc_tormented_heroic_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + if (GetCaster()->GetMap()->IsHeroic()) + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_TORMENTED_40, true); + } + + void Register() OVERRIDE + { + OnEffectHitTarget += SpellEffectFn(spell_baleroc_tormented_heroic_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_baleroc_tormented_heroic_SpellScript(); + } +}; + +class achievement_share_the_pain : public AchievementCriteriaScript +{ + public: + achievement_share_the_pain() : AchievementCriteriaScript("achievement_share_the_pain") { } + + bool OnCheck(Player* /*source*/, Unit* target) OVERRIDE + { + if (!target) + return false; + + if (BalerocAI* balerocAI = CAST_AI(BalerocAI, target->GetAI())) + return balerocAI->SharedThePain(); + + return false; + } +}; + +void AddSC_boss_baleroc() +{ + new boss_baleroc(); + + new npc_shard_of_torment(); + + new spell_countdown_p1(); + new spell_countdown_p2(); + new spell_countdown_p3(); + + new spell_decimating_strike(); + + new spell_shards_of_torment(); + new spell_baleroc_torment(); + new spell_baleroc_tormented(); + new spell_baleroc_tormented_heroic(); + new spell_baleroc_tormented_debuff(); + + new achievement_share_the_pain(); +}; diff --git a/src/server/scripts/Kalimdor/Firelands/firelands.h b/src/server/scripts/Kalimdor/Firelands/firelands.h index 3e5057ea6f7..4e98ebffd08 100644 --- a/src/server/scripts/Kalimdor/Firelands/firelands.h +++ b/src/server/scripts/Kalimdor/Firelands/firelands.h @@ -39,6 +39,16 @@ enum DataTypes enum CreatureIds { + //Bosses + NPC_SHANNOX = 53691, + NPC_LORD_RHYOLITH = 52558, + NPC_BETH_TILAC = 52498, + NPC_ALYSRAZOR = 52530, + NPC_BALEROC = 53494, + NPC_MAJORDOMO_STAGHELM = 52571, + NPC_RAGNAROS = 52409, + + //Alysrazor NPC_BLAZING_MONSTROSITY_LEFT = 53786, NPC_BLAZING_MONSTROSITY_RIGHT = 53791, NPC_EGG_PILE = 53795, @@ -47,6 +57,15 @@ enum CreatureIds NPC_SMOULDERING_HATCHLING = 53794, }; +enum GameobjectIds +{ + GO_LORD_RHYOLITH_BRIDGE = 209255, + GO_BETH_TILAC_DOOR = 208877, + GO_BALEROC_FIREWALL = 209066, + GO_MAJORDOMO_FIREWALL = 208906, + GO_RAGNAROS_DOOR = 209073, +}; + class DelayedAttackStartEvent : public BasicEvent { public: diff --git a/src/server/scripts/Kalimdor/Firelands/instance_firelands.cpp b/src/server/scripts/Kalimdor/Firelands/instance_firelands.cpp index 34cd2d2982a..f730f49f467 100644 --- a/src/server/scripts/Kalimdor/Firelands/instance_firelands.cpp +++ b/src/server/scripts/Kalimdor/Firelands/instance_firelands.cpp @@ -19,6 +19,16 @@ #include "InstanceScript.h" #include "firelands.h" +DoorData const doorData[] = +{ + {GO_LORD_RHYOLITH_BRIDGE, DATA_LORD_RHYOLITH, DOOR_TYPE_ROOM, BOUNDARY_E }, + {GO_BETH_TILAC_DOOR, DATA_BETH_TILAC, DOOR_TYPE_ROOM, BOUNDARY_SE }, + //{GO_BALEROC_FIREWALL, DATA_BALEROC, DOOR_TYPE_ROOM, BOUNDARY_S }, + {GO_MAJORDOMO_FIREWALL, DATA_MAJORDOMO_STAGHELM, DOOR_TYPE_PASSAGE, BOUNDARY_N }, + {GO_RAGNAROS_DOOR, DATA_RAGNAROS, DOOR_TYPE_ROOM, BOUNDARY_S }, + {0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE }, //END +}; //Baleroc door is special, it depends on the health status of the other bosses in the instance + class instance_firelands : public InstanceMapScript { public: @@ -30,6 +40,9 @@ class instance_firelands : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(EncounterCount); + LoadDoorData(doorData); + + BalerocGUID = 0; } void OnCreatureCreate(Creature* creature) override @@ -40,8 +53,104 @@ class instance_firelands : public InstanceMapScript // Cannot directly start attacking here as the creature is not yet on map creature->m_Events.AddEvent(new DelayedAttackStartEvent(creature), creature->m_Events.CalculateTime(500)); break; + case NPC_BALEROC: + BalerocGUID = creature->GetGUID(); + break; } } + + void OnGameObjectCreate(GameObject* go) OVERRIDE + { + switch(go->GetEntry()) + { + case GO_LORD_RHYOLITH_BRIDGE: + case GO_BETH_TILAC_DOOR: + //case GO_BALEROC_FIREWALL: + case GO_MAJORDOMO_FIREWALL: + case GO_RAGNAROS_DOOR: + AddDoor(go, true); + break; + default: + break; + } + } + + void OnGameObjectRemove(GameObject* go) OVERRIDE + { + switch (go->GetEntry()) + { + case GO_LORD_RHYOLITH_BRIDGE: + case GO_BETH_TILAC_DOOR: + //case GO_BALEROC_FIREWALL: + case GO_MAJORDOMO_FIREWALL: + case GO_RAGNAROS_DOOR: + AddDoor(go, false); + break; + default: + break; + } + } + + uint64 GetData64(uint32 type) const OVERRIDE + { + switch (type) + { + case DATA_BALEROC: + return BalerocGUID; + } + + return 0; + } + + std::string GetSaveData() OVERRIDE + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << "F L " << GetBossSaveData(); + + OUT_SAVE_INST_DATA_COMPLETE; + return saveStream.str(); + } + + void Load(const char* str) OVERRIDE + { + if (!str) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(str); + + char dataHead1, dataHead2; + + std::istringstream loadStream(str); + loadStream >> dataHead1 >> dataHead2; + + if (dataHead1 == 'F' && dataHead2 == 'L') + { + for (uint32 i = 0; i < EncounterCount; ++i) + { + uint32 tmpState; + loadStream >> tmpState; + if (tmpState == IN_PROGRESS || tmpState > SPECIAL) + tmpState = NOT_STARTED; + SetBossState(i, EncounterState(tmpState)); + } + + uint32 temp = 0; + loadStream >> temp; + } + else + OUT_LOAD_INST_DATA_FAIL; + + OUT_LOAD_INST_DATA_COMPLETE; + } + + private: + uint64 BalerocGUID; + }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp index 1f8258f78a6..fab3fdb2757 100644 --- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp +++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp @@ -96,6 +96,7 @@ void AddSC_boss_altairus(); void AddSC_boss_asaad(); void AddSC_instance_firelands(); //Firelands void AddSC_boss_alysrazor(); +void AddSC_boss_baleroc(); void AddSC_ashenvale(); void AddSC_azshara(); @@ -201,6 +202,7 @@ void AddKalimdorScripts() AddSC_boss_asaad(); AddSC_instance_firelands(); //Firelands AddSC_boss_alysrazor(); + AddSC_boss_baleroc(); AddSC_ashenvale(); AddSC_azshara();