diff --git a/sql/sylvania/tomb_of_sargeras_bindings.sql b/sql/sylvania/tomb_of_sargeras_bindings.sql new file mode 100644 index 0000000..fa1f7a0 --- /dev/null +++ b/sql/sylvania/tomb_of_sargeras_bindings.sql @@ -0,0 +1,139 @@ +-- ===================================================================== +-- Tombe de Sargeras (map 1676) : bindings du module porté depuis +-- mudyx/DestinyCoreNew (même famille de core, scripts lignée uwow). +-- Avant ce patch, AUCUN boss de la Tombe n'était bindé (sauf Goroth). +-- ===================================================================== + +-- ---------- CRÉATURES : boss ---------- +UPDATE creature_template SET ScriptName='boss_goroth' WHERE entry=115844; +UPDATE creature_template SET ScriptName='boss_demonic_inquisition' WHERE entry IN (116689,116691); +UPDATE creature_template SET ScriptName='boss_harjatan' WHERE entry=116407; +UPDATE creature_template SET ScriptName='boss_mistress_sasszine' WHERE entry=115767; +UPDATE creature_template SET ScriptName='boss_sisters_of_the_moon' WHERE entry=118182; +UPDATE creature_template SET ScriptName='boss_the_desolate_host_generic' WHERE entry=120988; +UPDATE creature_template SET ScriptName='boss_maiden_of_vigilance' WHERE entry=118289; +UPDATE creature_template SET ScriptName='boss_fallen_avatar' WHERE entry=116939; +UPDATE creature_template SET ScriptName='boss_tos_kiljaeden' WHERE entry=117269; + +-- ---------- CRÉATURES : adds / triggers ---------- +-- Goroth +UPDATE creature_template SET ScriptName='npc_goroth_ember_stalker' WHERE entry=115892; +UPDATE creature_template SET ScriptName='npc_goroth_infernal_spike' WHERE entry=116976; +UPDATE creature_template SET ScriptName='npc_goroth_lava_stalker' WHERE entry=117931; +UPDATE creature_template SET ScriptName='npc_goroth_brimstone_infernal' WHERE entry=119950; +-- Inquisition démoniaque +UPDATE creature_template SET ScriptName='npc_tos_tormented_soul' WHERE entry=117957; +-- Harjatan (les 3 variantes de têtards partagent la même IA) +UPDATE creature_template SET ScriptName='npc_tos_tadpole' WHERE entry IN (121155,121156,120574); +-- Sassz'ine +UPDATE creature_template SET ScriptName='npc_mistress_sasszine' WHERE entry=121184; +UPDATE creature_template SET ScriptName='npc_elder_murk_eye' WHERE entry=121071; +UPDATE creature_template SET ScriptName='npc_sasszine_abyss_stalker' WHERE entry=115795; +UPDATE creature_template SET ScriptName='npc_sasszine_slicing_tornado' WHERE entry=118286; +UPDATE creature_template SET ScriptName='npc_sasszine_electrifying_jellyfish' WHERE entry=115896; +UPDATE creature_template SET ScriptName='npc_sasszine_razorjaw_waverunner' WHERE entry=115902; +UPDATE creature_template SET ScriptName='npc_sasszine_sarukel' WHERE entry=116843; +UPDATE creature_template SET ScriptName='npc_sasszine_ossunet' WHERE entry=116881; +UPDATE creature_template SET ScriptName='npc_sasszine_piranhado' WHERE entry=116841; +UPDATE creature_template SET ScriptName='npc_sasszine_delicious_bufferfish' WHERE entry=119791; +-- SÅ“urs de la Lune (118182 = contrôleur, les 3 sÅ“urs = IA dédiées) +UPDATE creature_template SET ScriptName='npc_sister_kasparian' WHERE entry=118523; +UPDATE creature_template SET ScriptName='npc_sister_lunaspyre' WHERE entry=118518; +UPDATE creature_template SET ScriptName='npc_sister_yathae' WHERE entry=118374; +UPDATE creature_template SET ScriptName='npc_sistersmoon_moontalon' WHERE entry=119205; +UPDATE creature_template SET ScriptName='npc_sistersmoon_glaive_target' WHERE entry=119054; +UPDATE creature_template SET ScriptName='npc_sistersmoon_twilight_soul' WHERE entry=121498; +-- Hôte affligé (+ miroirs mythiques) +UPDATE creature_template SET ScriptName='npc_tos_engine_of_souls' WHERE entry=118460; +UPDATE creature_template SET ScriptName='npc_tos_soul_queen_dejahna' WHERE entry=118462; +UPDATE creature_template SET ScriptName='npc_tos_desolate_host' WHERE entry=119072; +UPDATE creature_template SET ScriptName='npc_tos_spiritual_font' WHERE entry=118701; +UPDATE creature_template SET ScriptName='npc_tos_reanimated_templar' WHERE entry IN (118715,119938); +UPDATE creature_template SET ScriptName='npc_tos_ghastly_bonewarden' WHERE entry IN (118728,119939); +UPDATE creature_template SET ScriptName='npc_tos_fallen_priestess' WHERE entry IN (118729,119940); +UPDATE creature_template SET ScriptName='npc_tos_soul_residue' WHERE entry IN (118730,119941); +UPDATE creature_template SET ScriptName='npc_tos_tormented_cries' WHERE entry=118924; +-- Jeune fille de vigilance +UPDATE creature_template SET ScriptName='npc_tos_essences' WHERE entry IN (118640,118643); +UPDATE creature_template SET ScriptName='npc_tos_essences_intro' WHERE entry IN (120132,120131); +-- Avatar déchu +UPDATE creature_template SET ScriptName='npc_avatara_maiden' WHERE entry=117264; +UPDATE creature_template SET ScriptName='npc_avatara_pilones' WHERE entry=117279; +UPDATE creature_template SET ScriptName='npc_tos_touch_of_sargeras' WHERE entry=120838; +UPDATE creature_template SET ScriptName='npc_tos_rain_of_destroyer' WHERE entry=120961; +-- Kil'jaeden +UPDATE creature_template SET ScriptName='npc_tos_armageddon_stalker' WHERE entry=120839; +UPDATE creature_template SET ScriptName='npc_tos_erupting_reflection' WHERE entry=119206; +UPDATE creature_template SET ScriptName='npc_tos_wailing_reflection' WHERE entry=119107; +UPDATE creature_template SET ScriptName='npc_tos_hopeless_reflection' WHERE entry=119663; +UPDATE creature_template SET ScriptName='npc_tos_shadowsoul' WHERE entry=121193; +UPDATE creature_template SET ScriptName='npc_tos_stage4_illidan_stormrage' WHERE entry=121227; +UPDATE creature_template SET ScriptName='npc_tos_demonic_obelisk' WHERE entry=120270; +UPDATE creature_template SET ScriptName='npc_tos_nether_rift' WHERE entry=120390; +UPDATE creature_template SET ScriptName='npc_tos_flaming_orb' WHERE entry=120082; +-- Trash / événements d'instance +UPDATE creature_template SET ScriptName='npc_tos_breach' WHERE entry=121605; +UPDATE creature_template SET ScriptName='npc_tos_kadghar_1' WHERE entry=119726; + +-- ---------- GAMEOBJECT ---------- +UPDATE gameobject_template SET ScriptName='go_tos_tele_to_kiljedan' WHERE entry=269783; + +-- ---------- SORTS ---------- +DELETE FROM spell_script_names WHERE ScriptName LIKE 'spell_tos_%' OR ScriptName LIKE 'spell_sasszine_%' OR ScriptName LIKE 'spell_sistersmoon_%' OR ScriptName='spell_egvin_levitation'; +INSERT INTO spell_script_names (spell_id, ScriptName) VALUES +-- Inquisition démoniaque +(233652,'spell_tos_confess'),(233104,'spell_tos_torment'),(235305,'spell_tos_prisoner'), +(233435,'spell_tos_calcified_quils'),(235295,'spell_tos_adherent_fragment'), +-- Goroth +(233024,'spell_tos_goroth_crashing_comet'),(234368,'spell_tos_goroth_fel_eruption_dummy'), +(238588,'spell_tos_goroth_rain_of_brimstone'),(237333,'spell_tos_goroth_energy_tracker'), +(234386,'spell_tos_goroth_fel_periodic_trigger'), +-- Harjatan +(231854,'spell_tos_unchecked_rage'),(234016,'spell_tos_fixate'),(232061,'spell_tos_draw_in'), +-- Sassz'ine +(230143,'spell_sasszine_hydra_shot_filter'),(230201,'spell_sasszine_burden_of_pain'), +(239369,'spell_sasszine_delicious_bufferfish'),(232913,'spell_sasszine_befouling_ink'), +-- SÅ“urs de la Lune +(236306,'spell_sistersmoon_incorporeal_shot_filter'), +(239378,'spell_sistersmoon_glaive_storm_filter'),(239382,'spell_sistersmoon_glaive_storm_filter'),(239385,'spell_sistersmoon_glaive_storm_filter'), +(234998,'spell_sistersmoon_astral_purge'), +(233263,'spell_sistersmoon_embrace_eclipse'),(233264,'spell_sistersmoon_embrace_eclipse'), +(234664,'spell_sistersmoon_side_moon'),(234668,'spell_sistersmoon_side_moon'), +(236717,'spell_sistersmoon_lunar_barrage'), +-- Hôte affligé +(236673,'spell_tos_quietus_filter'),(236465,'spell_tos_soulbind_finder'), +(245611,'spell_tos_soulbind_visual_finder'),(245612,'spell_tos_soulbind_visual_finder'), +(236563,'spell_tos_sundering_doom'),(236564,'spell_tos_sundering_doom'),(236567,'spell_tos_sundering_doom'),(236568,'spell_tos_sundering_doom'), +(239014,'spell_tos_dissonance'),(239015,'spell_tos_dissonance'), +(239006,'spell_tos_dissonance_filter'),(239007,'spell_tos_dissonance_filter'), +(235933,'spell_tos_spear_of_anguish'),(242796,'spell_tos_spear_of_anguish'), +(238585,'spell_tos_bound_essence'),(235923,'spell_tos_spear_of_anguish_filter'), +(235988,'spell_tos_tormented_cries_filter'),(236459,'spell_tos_soulbind'), +(236515,'spell_tos_shattering_scream'), +(235113,'spell_tos_spiritual_barrier_dissonance_visual_aura'),(235620,'spell_tos_spiritual_barrier_dissonance_visual_aura'), +(235732,'spell_tos_spiritual_barrier_dissonance_phase_aura'),(235734,'spell_tos_spiritual_barrier_dissonance_phase_aura'), +-- Jeune fille de vigilance +(235271,'spell_tos_infusion'),(248812,'spell_tos_blowback'), +(234896,'spell_tos_wrath_of_the_creators'),(236432,'spell_tos_wrath_of_the_creators'), +(235117,'spell_tos_unstable_soul'), +(235213,'spell_tos_infusions'),(235240,'spell_tos_infusions'), +(241593,'spell_egvin_levitation'), +-- Avatar déchu +(239132,'spell_tos_rupture_realistic'),(235572,'spell_tos_rupture_realistic'), +(239742,'spell_tos_dark_mark'),(234873,'spell_tos_avatara_energy'), +(238460,'spell_tos_shadow_blades'),(239417,'spell_tos_black_winds'), +(236682,'spell_tos_fel_infusion'),(239739,'spell_tos_black_mark_aura'), +-- Kil'jaeden +(238502,'spell_tos_focused_dreadflame'),(242074,'spell_tos_destabilized_shadowsoul'), +(238999,'spell_tos_darkness_thousand_souls'),(239280,'spell_tos_flaming_orb_fixate'), +(243625,'spell_tos_lingering_wail_jump'),(237728,'spell_tos_hopelessness'), +(239154,'spell_tos_gravity_squeeze'),(243536,'spell_tos_lingering_eruption'), +(243624,'spell_tos_lingering_wail'); + +-- ---------- AREATRIGGERS (template existants seulement ; les Id custom +-- uwow 9xxxxx/1xxxxxx n'existent pas dans notre DB : dégradation douce) ---------- +UPDATE areatrigger_template SET ScriptName='at_goroth_shattering_star' WHERE Id=13412; +UPDATE areatrigger_template SET ScriptName='at_sasszine_devouring_maw' WHERE Id=13368; +UPDATE areatrigger_template SET ScriptName='at_sistersmoon_moon' WHERE Id IN (13559,13568); +UPDATE areatrigger_template SET ScriptName='at_sistersmoon_glaive_storm' WHERE Id IN (14928,14929,14930); +UPDATE areatrigger_template SET ScriptName='at_tos_armageddon' WHERE Id=15115; diff --git a/src/server/game/AI/CoreAI/AreaTriggerAI.h b/src/server/game/AI/CoreAI/AreaTriggerAI.h index 69b20e6..d216d35 100644 --- a/src/server/game/AI/CoreAI/AreaTriggerAI.h +++ b/src/server/game/AI/CoreAI/AreaTriggerAI.h @@ -23,6 +23,21 @@ class AreaTrigger; class Unit; + +// Moments d action des areatriggers (compat scripts DestinyCoreNew ; notre moteur +// n appelle PAS IsValidTarget : le filtrage de cibles de ces scripts est inactif) +enum AreaTriggerActionMoment +{ + AT_ACTION_MOMENT_ENTER = 0x0001, + AT_ACTION_MOMENT_LEAVE = 0x0002, + AT_ACTION_MOMENT_UPDATE_TARGET = 0x0004, + AT_ACTION_MOMENT_DESPAWN = 0x0008, + AT_ACTION_MOMENT_SPAWN = 0x0010, + AT_ACTION_MOMENT_REMOVE = 0x0020, + AT_ACTION_MOMENT_ON_THE_WAY = 0x0040, + AT_ACTION_MOMENT_ON_STOP_MOVE = 0x0080, +}; + class TC_GAME_API AreaTriggerAI { protected: @@ -63,6 +78,8 @@ class TC_GAME_API AreaTriggerAI // Called when the AreaTrigger is update target virtual void ActionOnUpdate(GuidList& affectedPlayers) {} + virtual bool IsValidTarget(Unit* /*caster*/, Unit* /*target*/, AreaTriggerActionMoment /*actionM*/) { return true; } + virtual bool CalculateSpline(Position const* /*pos*/, Position& /*startPos*/, Position& /*endPos*/, std::vector& /*path*/) { return false; } }; class NullAreaTriggerAI : public AreaTriggerAI diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index a47b65a..3a0e930 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -194,6 +194,12 @@ void UnitAI::DoCastVictim(uint32 spellId, bool triggered) me->CastSpell(me->GetVictim(), spellId, triggered); } +void UnitAI::DoCastTopAggro(uint32 spellId, bool triggered, bool onlyPlayer /*= true*/) +{ + if (Unit* victim = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 100.0f, onlyPlayer)) + me->CastSpell(victim, spellId, triggered); +} + void UnitAI::DoCastAOE(uint32 spellId, bool triggered) { if (!triggered && me->HasUnitState(UNIT_STATE_CASTING)) diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 742290b..afe5007 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -23,6 +23,7 @@ #include "EventMap.h" #include "ObjectGuid.h" #include "ThreatManager.h" +#include "SpellAuraDefines.h" #define CAST_AI(a, b) (dynamic_cast(b)) #define ENSURE_AI(a,b) (EnsureAI(b)) @@ -224,6 +225,17 @@ class TC_GAME_API UnitAI // Note: it for recalculation damage or special reaction at damage // for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also virtual void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) { } + // Hook generique guid->bool utilise par les scripts portes de DestinyCoreNew (uwow) + virtual bool GetObjectData(ObjectGuid const& /*guid*/, uint32 /*type*/) { return false; } + // Hooks de notification uwow : notre moteur ne les appelle pas (code mort tolere) + virtual void SpellFinishCast(SpellInfo const* /*spell*/) { } + virtual void OnApplyOrRemoveAura(uint32 /*spellId*/, AuraRemoveMode /*mode*/, bool /*apply*/) { } + virtual void OnRemoveAuraTarget(Unit* /*target*/, uint32 /*spellId*/, AuraRemoveMode /*mode*/) { } + virtual bool IsDisableGenerateLoot() { return false; } + virtual uint32 GetModifyedData(uint32 /*type*/) { return 0; } + virtual void OnAreaTriggerDespawn(uint32 /*spellId*/, Position /*pos*/, bool /*duration*/) { } + virtual void OnAreaTriggerCast(Unit* /*caster*/, Unit* /*target*/, uint32 /*spellId*/, uint32 /*atEntry*/) { } + virtual void OnInterruptCast(Unit* /*caster*/, uint32 /*spellId*/, uint32 /*curSpellID*/, uint32 /*schoolMask*/) { } // Called when the creature receives heal virtual void HealReceived(Unit* /*done_by*/, uint32& /*addhealth*/) { } @@ -242,6 +254,7 @@ class TC_GAME_API UnitAI void DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false); void DoCastSelf(uint32 spellId, bool triggered = false) { DoCast(me, spellId, triggered); } void DoCastVictim(uint32 spellId, bool triggered = false); + void DoCastTopAggro(uint32 spellId, bool triggered = false, bool onlyPlayer = true); void DoCastAOE(uint32 spellId, bool triggered = false); void DoCastRandom(uint32 spellId, float dist, bool triggered = false, int32 aura = 0, uint32 position = 0); diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index bcd07b9..fa94bd7 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -439,3 +439,24 @@ void CreatureAI::SetFlyMode(bool fly) else me->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_HOVER); } + +void CreatureAI::Talk(uint8 id, ObjectGuid whisperGuid) +{ + WorldObject const* target = nullptr; + if (!whisperGuid.IsEmpty()) + target = ObjectAccessor::GetUnit(*me, whisperGuid); + Talk(id, target); +} + +void CreatureAI::Talk(std::initializer_list ids, ObjectGuid whisperGuid /*= ObjectGuid::Empty*/) +{ + if (ids.size() == 0) + return; + uint8 id = *(ids.begin() + urand(0, uint32(ids.size()) - 1)); + Talk(id, whisperGuid); +} + +void CreatureAI::ZoneTalk(uint8 id) +{ + sCreatureTextMgr->SendChat(me, id, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_ZONE); +} diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 5dc42d7..fd5e67f 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -19,6 +19,7 @@ #define CREATUREAI_H #include "UnitAI.h" +#include #include "Common.h" #include "ObjectDefines.h" @@ -100,6 +101,9 @@ class TC_GAME_API CreatureAI : public UnitAI virtual ~CreatureAI(); void Talk(uint8 id, WorldObject const* whisperTarget = nullptr); + void Talk(uint8 id, ObjectGuid whisperGuid); // compat DestinyCoreNew + void ZoneTalk(uint8 id); // texte a toute la zone + void Talk(std::initializer_list ids, ObjectGuid whisperGuid = ObjectGuid::Empty); // choix aleatoire void AddDelayedEvent(uint64 timeOffset, std::function&& function); void KillAllDelayedEvents(); diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index f6e6f1c..e3e48a8 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -1641,3 +1641,48 @@ void ScriptedAI::GetTalkData(uint32 eventId) } } + +bool ScriptedAI::CheckHomeDistToEvade(uint32 diff, float dist, float x, float y, float z, bool onlyZ) +{ + if (!me->IsInCombat()) + return false; + + bool evade = false; + + if (_checkHomeTimer <= diff) + { + _checkHomeTimer = 1500; + + if (onlyZ) + { + if ((me->GetPositionZ() > z + dist) || (me->GetPositionZ() < z - dist)) + evade = true; + } + else if (x != 0.0f || y != 0.0f || z != 0.0f) + { + if (me->GetDistance(x, y, z) >= dist) + evade = true; + } + else if (me->GetDistance(me->GetHomePosition()) >= dist) + evade = true; + + if (evade) + { + EnterEvadeMode(); + return true; + } + } + else + _checkHomeTimer -= diff; + + return false; +} + +Creature* SummonList::GetCreature(uint32 entry) +{ + for (auto const& guid : *this) + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + if (summon->GetEntry() == entry) + return summon; + return nullptr; +} diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index d31b7e4..00623c6 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -86,6 +86,7 @@ public: void Summon(Creature const* summon); void Despawn(Creature const* summon); + Creature* GetCreature(uint32 entry); void DespawnEntry(uint32 entry); void DespawnAll(); @@ -347,6 +348,14 @@ struct TC_GAME_API ScriptedAI : public CreatureAI // - for raid in mode 25-heroic // DO NOT USE to check raid in mode 25-normal. bool IsHeroic() const { return _isHeroic; } + bool IsHeroicRaid() const { return IsHeroic(); } + bool IsHeroicPlusRaid() const { return IsHeroic() || me->GetMap()->IsMythic(); } + bool IsMythicRaid() const { return me->GetMap()->IsMythic(); } + bool IsLfrRaid() const { return _difficulty == DIFFICULTY_LFR || _difficulty == DIFFICULTY_LFR_NEW; } + bool IsNormalRaid() const { return _difficulty == DIFFICULTY_NORMAL_RAID; } + // Evade si trop loin du point d ancrage (helper des scripts portes de DestinyCoreNew) + bool CheckHomeDistToEvade(uint32 diff, float dist = 0.0f, float x = 0.0f, float y = 0.0f, float z = 0.0f, bool onlyZ = false); + uint32 _checkHomeTimer = 1500; // return the dungeon or raid difficulty Difficulty GetDifficulty() const { return _difficulty; } diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index 12c0be4..f145492 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -1001,3 +1001,10 @@ void AreaTrigger::AI_Destroy() { _ai.reset(); } + +float AreaTrigger::GetRadius() const +{ + if (AreaTriggerTemplate const* t = GetTemplate()) + return t->MaxSearchRadius; + return 0.0f; +} diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h index 8135248..fea4dce 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h @@ -80,6 +80,13 @@ class TC_GAME_API AreaTrigger : public WorldObject, public GridObject void { SetReactState(st); }); +} + +void Creature::StopAttack(bool clearMove /*= false*/, bool interruptSpells /*= false*/) +{ + SetReactState(REACT_PASSIVE); + AttackStop(); + if (clearMove) + GetMotionMaster()->Clear(); + if (interruptSpells) + InterruptNonMeleeSpells(false); +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 5d5a46b..33b1059 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -106,6 +106,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool CanFly() const override { return (GetCreatureTemplate()->InhabitType & INHABIT_AIR) != 0; } void SetReactState(ReactStates st) { m_reactState = st; } + void SetReactState(ReactStates st, uint32 delay); // variante differee (compat DestinyCoreNew) + void StopAttack(bool clearMove = false, bool interruptSpells = false); ReactStates GetReactState() const { return m_reactState; } bool HasReactState(ReactStates state) const { return (m_reactState == state); } void InitializeReactState(); diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index e762630..013f379 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -300,6 +300,18 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject /// Event handler EventProcessor m_Events; + // compat DestinyCoreNew : evenement differe via m_Events (mis a jour dans GameObject::Update) + void AddDelayedEvent(uint64 timeOffset, std::function&& function) + { + class LambdaEvent : public BasicEvent + { + std::function _fn; + public: + explicit LambdaEvent(std::function&& fn) : _fn(std::move(fn)) { } + bool Execute(uint64, uint32) override { _fn(); return true; } + }; + m_Events.AddEvent(new LambdaEvent(std::move(function)), m_Events.CalculateTime(timeOffset)); + } uint32 GetWorldEffectID() const { return _worldEffectID; } void SetWorldEffectID(uint32 worldEffectID) { _worldEffectID = worldEffectID; } diff --git a/src/server/game/Entities/Object/Position.cpp b/src/server/game/Entities/Object/Position.cpp index a3c6c41..94883f7 100644 --- a/src/server/game/Entities/Object/Position.cpp +++ b/src/server/game/Entities/Object/Position.cpp @@ -16,6 +16,7 @@ */ #include "Position.h" +#include #include "ByteBuffer.h" #include "GridDefines.h" #include "Random.h" @@ -279,3 +280,73 @@ ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamerGetPositionX(), streamer.Pos->GetPositionY(), streamer.Pos->GetPositionZ()); return buf; } + +void Position::SimplePosXYRelocationByAngle(Position& pos, float dist, float angle, bool relative /*= false*/) const +{ + if (!relative) + angle += GetOrientation(); + + pos.m_positionX = m_positionX + dist * std::cos(angle); + pos.m_positionY = m_positionY + dist * std::sin(angle); + pos.m_positionZ = m_positionZ; + + if (!Trinity::IsValidMapCoord(pos.m_positionX, pos.m_positionY)) + { + pos.Relocate(this); + return; + } + + Trinity::NormalizeMapCoord(pos.m_positionX); + Trinity::NormalizeMapCoord(pos.m_positionY); + pos.SetOrientation(GetOrientation()); +} + +void Position::GenerateNonDuplicatePoints(std::list& randPosList, Position const& centerPos, uint8 maxPoint, float randMin, float randMax, float minDist) const +{ + Position pos; + uint8 traiCount = 0; + + while (randPosList.size() < maxPoint) + { + bool badPos = false; + centerPos.SimplePosXYRelocationByAngle(pos, frand(randMin, randMax), frand(0.0f, 6.28f)); + ++traiCount; + + for (auto const& _pos : randPosList) + { + if (pos.GetExactDist(&_pos) <= minDist) + { + badPos = true; + break; + } + } + + if (!badPos || traiCount > uint8(maxPoint * 10)) + randPosList.push_back(pos); + } +} + +bool Position::IsLinesCross(Position const& pos11, Position const& pos12, Position const& pos21, Position const& pos22, Position* dest /*= nullptr*/) const +{ + // intersection parametrique de deux segments 2D (sans G3D::LineSegment2D, absent de notre g3dlite) + float x1 = pos11.GetPositionX(), y1 = pos11.GetPositionY(); + float x2 = pos12.GetPositionX(), y2 = pos12.GetPositionY(); + float x3 = pos21.GetPositionX(), y3 = pos21.GetPositionY(); + float x4 = pos22.GetPositionX(), y4 = pos22.GetPositionY(); + + float denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + if (std::fabs(denom) < 1e-6f) + return false; // paralleles ou colineaires + + float t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom; + float u = ((x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2)) / denom; + if (t < 0.0f || t > 1.0f || u < 0.0f || u > 1.0f) + return false; // intersection hors des segments + + if (dest) + { + dest->m_positionX = x1 + t * (x2 - x1); + dest->m_positionY = y1 + t * (y2 - y1); + } + return true; +} diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h index 20bedb1..8f820b5 100644 --- a/src/server/game/Entities/Object/Position.h +++ b/src/server/game/Entities/Object/Position.h @@ -19,6 +19,7 @@ #define Trinity_game_Position_h__ #include "Define.h" +#include #include #include #include @@ -103,6 +104,9 @@ public: float GetPositionY() const { return m_positionY; } float GetPositionZ() const { return m_positionZ; } float GetOrientation() const { return m_orientation; } + void SimplePosXYRelocationByAngle(Position& pos, float dist, float angle, bool relative = false) const; + void GenerateNonDuplicatePoints(std::list& randPosList, Position const& centerPos, uint8 maxPoint, float randMin, float randMax, float minDist) const; + bool IsLinesCross(Position const& pos11, Position const& pos12, Position const& pos21, Position const& pos22, Position* dest = nullptr) const; void GetPosition(float &x, float &y) const { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index a2830e9..e180b4c 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15214,3 +15214,45 @@ void Unit::EmoteWithDelay(uint32 Delay, uint8 Id) m_Events.AddEvent(new EmoteWithDelay(this, Id, Delay), m_Events.CalculateTime(Delay)); } + +void Unit::CastSpellDelay(Unit* victim, uint32 spellId, bool triggered, uint32 delay, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster) +{ + if (m_cleanupDone) + return; + + ObjectGuid targetGUID = victim->GetGUID(); + AddDelayedCombat(delay, [this, spellId, triggered, castItem, triggeredByAura, originalCaster, targetGUID]() -> void + { + Unit* target = ObjectAccessor::GetUnit(*this, targetGUID); + if (!target) + return; + + CastSpell(target, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); + }); +} + +void Unit::CastSpellDelay(Position pos, uint32 spellId, bool triggered, uint32 delay) +{ + if (m_cleanupDone) + return; + + if (!delay) + CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), spellId, triggered); + else + AddDelayedCombat(delay, [this, pos, spellId, triggered]() -> void + { + CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), spellId, triggered); + }); +} + +Unit* Unit::GetAnyOwner() const +{ + if (GetCharmerGUID()) + return GetCharmer(); + if (ToTempSummon()) + return ToTempSummon()->GetSummoner(); + if (GetOwner()) + return GetOwner(); + + return nullptr; +} diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 67865b5..d0a9ffb 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1426,6 +1426,7 @@ class TC_GAME_API Unit : public WorldObject BrawlersGuild* GetBrawlerGuild(); Unit* GetOwner() const; + Unit* GetAnyOwner() const; // charmeur > summoner > owner (compat DestinyCoreNew) Guardian* GetGuardianPet() const; Minion* GetFirstMinion() const; Unit* GetCharmer() const; @@ -1702,6 +1703,9 @@ class TC_GAME_API Unit : public WorldObject m_Functions.KillAllFunctions(); } + // Cast differe (helpers des scripts portes de DestinyCoreNew) + void CastSpellDelay(Unit* victim, uint32 spellId, bool triggered, uint32 delay, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpellDelay(Position pos, uint32 spellId, bool triggered, uint32 delay); void AddDelayedCombat(uint64 timeOffset, std::function&& function) { m_CombatFunctions.AddDelayedEvent(timeOffset, std::move(function)); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index b1dd761..0d33f54 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -4702,3 +4702,15 @@ void Map::UpdateAreaDependentAuras() if (player->IsInWorld()) player->UpdateAreaDependentAuras(); } + +void Map::ApplyOnEveryPlayer(std::function function) +{ + auto const& players = GetPlayers(); + if (players.isEmpty()) + return; + + for (auto const& itr : players) + if (Player* player = itr.GetSource()) + if (player->IsInWorld() && player->GetMap() == this) + function(player); +} diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 4b5b1fa..2c3493b 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -416,6 +417,7 @@ class TC_GAME_API Map : public GridRefManager bool IsMythic() const; bool Is25ManRaid() const; bool IsLFR() const; + bool IsHeroicPlusRaid() const { return IsHeroic() || IsMythic(); } bool IsChallengeMode() const; bool IsBattleground() const; bool IsBattleArena() const; @@ -443,6 +445,7 @@ class TC_GAME_API Map : public GridRefManager typedef MapRefManager PlayerList; PlayerList const& GetPlayers() const { return m_mapRefManager; } + void ApplyOnEveryPlayer(std::function function); //per-map script storage void ScriptsStart(std::map > const& scripts, uint32 id, Object* source, Object* target); diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 29129db..8e23672 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -942,3 +942,28 @@ void MotionMaster::DelayedDelete(MovementGenerator* curr) _expireList = new ExpireList(); _expireList->push_back(curr); } + +void MotionMaster::MoveSmoothFlyPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize) +{ + Movement::MoveSplineInit init(_owner); + init.SetSmooth(); + init.SetFly(); + init.SetUncompressed(); + Movement::PointsArray path(pathPoints, pathPoints + pathSize); + init.MovebyPath(path); + init.Launch(); + Mutate(new EffectMovementGenerator(pointId), MOTION_SLOT_ACTIVE); +} + +void MotionMaster::MoveSmoothFlyPath(uint32 pointId, Position const position, float flightSpeed /*= 0.0f*/) +{ + Movement::MoveSplineInit init(_owner); + init.SetSmooth(); + init.SetFly(); + init.SetUncompressed(); + init.MoveTo(position.m_positionX, position.m_positionY, position.m_positionZ, false, false); + if (flightSpeed > 0.0f) + init.SetVelocity(flightSpeed); + init.Launch(); + Mutate(new EffectMovementGenerator(pointId), MOTION_SLOT_ACTIVE); +} diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 10b5f4b..3f4b249 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -180,6 +180,8 @@ class TC_GAME_API MotionMaster void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount); void MoveSmoothPath(uint32 pointId, Position const* pathPoints, size_t pathSize, bool walk = false, bool fly = false); void MoveSmoothPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize, bool walk); + void MoveSmoothFlyPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize); + void MoveSmoothFlyPath(uint32 pointId, Position const position, float flightSpeed = 0.0f); void MoveSmoothPath(uint32 pointId, Movement::PointsArray const& points, bool walk); // Walk along spline chain stored in DB (script_spline_chain_meta and script_spline_chain_waypoints) void MoveAlongSplineChain(uint32 pointId, uint16 dbChainId, bool walk); diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_DemonicInquisition.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_DemonicInquisition.cpp new file mode 100644 index 0000000..eb4bdef --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_DemonicInquisition.cpp @@ -0,0 +1,632 @@ +/* + https://uwow.biz/ + + To-DO: Conversation 5002/5003 when near. Now it don't work =C +*/ + +#include "tomb_of_sargeras.h" + +enum Spells +{ + + // 116689 + SPELL_BONE_SCYTHE = 233423, // aura + SPELL_SCYTHE_SWEEP = 233426, + + SPELL_BONE_SAW = 233441, // 100% energy + SPELL_BONE_SAW_ENERGY = 234550, // proc + + + SPELL_DEBUFF_OVERRIDE = 233103, // 233652 + SPELL_DEBUFF_OVER_AURA = 233104, + SPELL_DEBUFF_OVER_AURA1 = 234364, + SPELL_DEBUFF_UNBEAR_TOR = 233430, + + // not normal + SPELL_CALCIFIED_QUILS = 233431, // 233432(233435) + + // 116691 + SPELL_GET_ENERGY = 246454, + SPELL_ECHOING_ANGUISH = 233983, // 22 + SPELL_FEL_SQUAL = 235230, // 100% + SPELL_FEL_SQUAL_ENERGY = 235237, + + SPELL_TORMENTING_BURST = 234015, // 17 + SPELL_PANGS_OF_GUILT = 239401, // 10 + + // not normal + SPELL_SUFFOCATING_DARK = 233894, // 24 + + SPELL_AOE_SILENCE = 250306, // ???? + SPELL_REGENERATE_ENERGY = 246455, + SPELL_ENRAGE = 248671, // 720 secs + + SPELL_BELAC_PRISONER = 236283, + SPELL_SOUL_CORRUPTION = 248713, +}; + +enum eEvents +{ + EVENT_SCYTHE_SWEEP = 1, + EVENT_CALCIFIED_QUILS, + + EVENT_ECHOING_ANGUISH, + EVENT_PANGS_OF_GUILT, + EVENT_TORMENTING_BURST, + EVENT_SUFFOCATING_DARK, + + EVENT_CHECK_ENERGY, + EVENT_ENRAGE, + EVENT_SUMMON_ADD, +}; + +std::map hitSpells +{ + { SPELL_BONE_SAW , SPELL_BONE_SAW_ENERGY}, + { SPELL_FEL_SQUAL , SPELL_FEL_SQUAL_ENERGY} +}; + +// 116689, 116691 +class boss_demonic_inquisition : public CreatureScript +{ +public: + boss_demonic_inquisition() : CreatureScript("boss_demonic_inquisition") {} + + struct boss_demonic_inquisitionAI : BossAI + { + boss_demonic_inquisitionAI(Creature* creature) : BossAI(creature, DATA_DEMONIC_INQUISITION) + { + if (me->GetEntry() == NPC_BELAK) + SetCombatMovement(false); + } + + std::unordered_map> calcifiedQuilsTargets{}; + + void Reset() override + { + if (me->IsAlive()) + _Reset(); + + me->RemoveAurasDueToSpell(SPELL_DEBUFF_OVERRIDE); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DEBUFF_OVER_AURA); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DEBUFF_OVER_AURA1); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ECHOING_ANGUISH); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_BELAC_PRISONER); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SOUL_CORRUPTION); + me->KillAllDelayedEvents(); + me->RemoveAllAreaTriggers(); + + if (me->IsAlive()) + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DEBUFF_UNBEAR_TOR); + } + + void EnterCombat(Unit* who) override + { + _EnterCombat(); + events.Reset(); + + me->SetPower(me->GetPowerType(), 0); + + switch (me->GetEntry()) + { + case NPC_ATRIGAN: + DoCast(me, SPELL_DEBUFF_OVERRIDE); + events.RescheduleEvent(EVENT_SCYTHE_SWEEP, 5200); + if (IsHeroicRaid() || IsMythicRaid()) + events.RescheduleEvent(EVENT_CALCIFIED_QUILS, 8500); + Talk(0); + events.RescheduleEvent(EVENT_SUMMON_ADD, 1000); + break; + case NPC_BELAK: + DoCast(me, SPELL_GET_ENERGY); + events.RescheduleEvent(EVENT_PANGS_OF_GUILT, 100); + events.RescheduleEvent(EVENT_ECHOING_ANGUISH, 5000); + events.RescheduleEvent(EVENT_TORMENTING_BURST, 9000); + if (IsHeroicRaid() || IsMythicRaid()) + events.RescheduleEvent(EVENT_SUFFOCATING_DARK, 10000); + me->AddDelayedEvent(6500, [this]() -> void + { + Talk(0); + }); + break; + } + + DoCast(me, SPELL_REGENERATE_ENERGY); + events.RescheduleEvent(EVENT_CHECK_ENERGY, 100); // both + + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_ENRAGE, 720000); + + if (Creature* other = instance->instance->GetCreature(instance->GetGuidData(me->GetEntry() == NPC_BELAK ? NPC_ATRIGAN : NPC_BELAK))) + { + other->AddDelayedEvent(100, [other,who]() -> void + { + if (who) + if(!other->IsInCombat()) + other->AI()->EnterCombat(who); + }); + } + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + BossAI::EnterEvadeMode(); + Reset(); + me->NearTeleportTo(me->GetHomePosition()); + + if (Creature* other = instance->instance->GetCreature(instance->GetGuidData(me->GetEntry() == NPC_BELAK ? NPC_ATRIGAN : NPC_BELAK))) + { + other->AddDelayedEvent(100, [other]() -> void + { + if (other->IsInCombat()) + other->AI()->EnterEvadeMode(); + }); + } + } + + void DamageTaken(Unit* attacker, uint32& damage) override + { + if (attacker == me) + return; + + if (Creature* other = instance->instance->GetCreature(instance->GetGuidData(me->GetEntry() == NPC_BELAK ? NPC_ATRIGAN : NPC_BELAK))) + { + if (damage >= me->GetHealth()) + other->Kill(other, true); + else + other->DealDamage(other, damage); + } + + // approximation : notre DamageTaken n expose pas le type de degats, + // le renvoi d epines se declenche donc sur tout degat (l original excluait DoT/split) + + for (const auto& pair : hitSpells) + if (me->HasAura(pair.first)) + attacker->CastSpell(attacker, pair.second, true); + + } + + void JustDied(Unit* killer) override + { + //Talk(SAY_DEATH); + if (me->GetEntry() == NPC_ATRIGAN) + DoCast(240053); + + _JustDied(); + + if (me->GetEntry() == NPC_ATRIGAN) + { + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()) + if (player->HasAura(SPELL_DEBUFF_UNBEAR_TOR)) + { + instance->DoUpdateAchievementCriteria(CRITERIA_TYPE_BE_SPELL_TARGET, 243355); + break; + } + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DEBUFF_UNBEAR_TOR); + + } + + Reset(); + } + + bool GetObjectData(ObjectGuid const& guid, uint32 type) override + { + if (calcifiedQuilsTargets[guid][type]) + return true; + + calcifiedQuilsTargets[guid][type] = true; + return false; + } + + void UpdateAI(uint32 diff) override + { + if (CheckHomeDistToEvade(diff, 50.0f)) + return; + + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasAura(SPELL_CALCIFIED_QUILS) || me->HasAura(SPELL_BONE_SAW) || me->HasAura(SPELL_FEL_SQUAL)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SCYTHE_SWEEP: + DoCast(SPELL_SCYTHE_SWEEP); + events.RescheduleEvent(EVENT_SCYTHE_SWEEP, 24500); + break; + case EVENT_CHECK_ENERGY: + if (me->GetPower(me->GetPowerType()) >= 100) + { + DoCast(me->GetEntry() == NPC_ATRIGAN ? SPELL_BONE_SAW : SPELL_FEL_SQUAL); + Talk(1); + } + + events.RescheduleEvent(EVENT_CHECK_ENERGY, 100); + break; + case EVENT_CALCIFIED_QUILS: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0, true)) + { + calcifiedQuilsTargets.clear(); + + DoCast(target, SPELL_CALCIFIED_QUILS); + me->AddDelayedEvent(5000, [this, target]() + { + if (!target) + return; + + Position pos = target->GetPosition(); + float diff_x = pos.GetPositionX() - me->GetPositionX(); + float diff_y = pos.GetPositionY() - me->GetPositionY(); + float dist = sqrt(diff_x * diff_x + diff_y * diff_y); + for (float mod = 2 / dist, time = 0; mod < 60.0f / dist; mod += (2 / dist), time++) + { + for (float i = -0.5f; i <= 0.5f; i += 0.2f) + { + if (urand(1, 5) == 5) + continue; + + float new_x = mod*diff_x*sqrt(3) / 2 - i * mod *diff_y / 2; + float new_y = i * mod *diff_x / 2 + mod* diff_y * sqrt(3) / 2; + me->AddDelayedEvent(1 + time * 50, [=]() + { + me->CastSpell({ me->GetPositionX() + new_x + frand(-2, 2), me->GetPositionY() + new_y + frand(-2, 2), me->GetPositionZ(), 0 }, 233435, true); + + }); + } + } + + }); + } + events.RescheduleEvent(EVENT_CALCIFIED_QUILS, 20000); + break; + case EVENT_PANGS_OF_GUILT: + DoCast(SPELL_PANGS_OF_GUILT); + events.RescheduleEvent(EVENT_PANGS_OF_GUILT, 10000); + break; + case EVENT_ECHOING_ANGUISH: + DoCast(SPELL_ECHOING_ANGUISH); + events.RescheduleEvent(EVENT_ECHOING_ANGUISH, 22000); + break; + case EVENT_TORMENTING_BURST: + DoCast(SPELL_TORMENTING_BURST); + events.RescheduleEvent(EVENT_TORMENTING_BURST, 17000); + break; + case EVENT_SUFFOCATING_DARK: + { + std::list threatlist = me->getThreatManager().getThreatList(); + + threatlist.remove_if([this](HostileReference* ref) + { + if (!ref->getTarget()->IsPlayer()) + return true; + + return me->GetDistance(ref->getTarget()) >= 60.0f; + }); + + std::list threatListTemp = threatlist; + threatListTemp.remove_if([this](HostileReference* ref) + { + return ref->getTarget()->ToPlayer()->GetRoleForGroup() == ROLE_TANK; // approximation : on ecarte seulement les tanks + }); + + uint8 count = std::min(3, int32(ceil(float(threatlist.size()) / 5.0f))); + if (threatListTemp.size() >= count) + threatlist = threatListTemp; + + Trinity::Containers::RandomResize(threatlist, count); + for (const auto& target : threatlist) + if (Unit* targ = target->getTarget()) + me->CastSpell(targ->GetPositionX(), targ->GetPositionY(), 3011.62f, SPELL_SUFFOCATING_DARK, true); + + events.RescheduleEvent(EVENT_SUFFOCATING_DARK, 24000); + break; + } + case EVENT_ENRAGE: + DoCast(SPELL_ENRAGE); + break; + case EVENT_SUMMON_ADD: + me->SummonCreature(NPC_TORMENTED_SOUL, 6403.00f, -935.00f, 3037.16f); + break; + } + } + + if (me->GetEntry() != NPC_BELAK) + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new boss_demonic_inquisitionAI (creature); + } +}; + +// 117957 +class npc_tos_tormented_soul : public CreatureScript +{ +public: + npc_tos_tormented_soul() : CreatureScript("npc_tos_tormented_soul") {} + + struct npc_tos_tormented_soulAI : public ScriptedAI + { + npc_tos_tormented_soulAI(Creature* creature) : ScriptedAI(creature), summons(me) + { + me->m_SightDistance = 20; + } + + EventMap events; + SummonList summons; + + void Reset() override + { + events.Reset(); + summons.DespawnAll(); + me->SetControlled(true, UNIT_STATE_ROOT); + me->RemoveAurasDueToSpell(239135); + } + + void EnterCombat(Unit* /*who*/) override + { + events.RescheduleEvent(EVENT_1, 6000); + events.RescheduleEvent(EVENT_2, 5000); + } + + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + } + + void DamageTaken(Unit* attacker, uint32& damage) override + { + if (urand(1, 3) == 2) + DoCast(attacker, 235301); + } + + void UpdateAI(uint32 diff) override + { + if (CheckHomeDistToEvade(diff, 17.0f)) + return; + + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + DoCast(SPELL_SOUL_CORRUPTION); + events.RescheduleEvent(EVENT_1, 6000); + break; + case EVENT_2: + DoCast(239135); + events.RescheduleEvent(EVENT_2, urand(16000, 19000)); + break; + } + } + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_tos_tormented_soulAI(creature); + } +}; + +// 233652 +class spell_tos_confess : public SpellScriptLoader +{ +public: + spell_tos_confess() : SpellScriptLoader("spell_tos_confess") { } + + class spell_tos_confess_AuraScript : public AuraScript + { + PrepareAuraScript(spell_tos_confess_AuraScript); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetTarget()) + { + if (!caster->IsPlayer()) + return; + + caster->GetMotionMaster()->MoveSmoothFlyPath(0, { caster->GetPositionX() + frand(-3, 3), caster->GetPositionY() + frand(-3, 3), caster->GetPositionZ() + 10.0f, caster->GetOrientation() }); + caster->AddDelayedEvent(1500, [caster]() -> void { + caster->RemoveAurasDueToSpell(233652); + caster->GetMotionMaster()->Clear(); + caster->ToPlayer()->TeleportTo(caster->GetMapId(), 6403.13f, -935.55f, 3060.23f, caster->GetOrientation(),TELE_TO_SEAMLESS); + }); + } + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_tos_confess_AuraScript::OnApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_tos_confess_AuraScript(); + } +}; + +// 233104 +class spell_tos_torment : public SpellScriptLoader +{ +public: + spell_tos_torment() : SpellScriptLoader("spell_tos_torment") { } + + class spell_tos_torment_AuraScript : public AuraScript + { + PrepareAuraScript(spell_tos_torment_AuraScript); + + void OnTick(AuraEffect const* /*aurEff*/) + { + Unit* target = GetTarget(); + if (!target) + return; + + if (target->GetPower(POWER_ALTERNATE_POWER) >= 100) + target->CastSpell(target, SPELL_DEBUFF_UNBEAR_TOR, true); + else if (target->GetPower(POWER_ALTERNATE_POWER) == 0) + if (target->GetDistance2d(6403.13f, -935.55f) <= 30.0f) + target->CastSpell(target, 243560, true); // 235293 + + if (target->GetDistance2d(6403.13f, -935.55f) > 40.0f && target->HasAura(SPELL_BELAC_PRISONER)) + { + target->RemoveAurasDueToSpell(SPELL_BELAC_PRISONER); + target->RemoveAurasDueToSpell(SPELL_DEBUFF_OVER_AURA1); // clear + target->CastSpell(target, SPELL_DEBUFF_OVER_AURA1, true); + } + } + + void OnRemove(AuraEffect const* /*auraEffect*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (!target || !target->IsPlayer()) + return; + + if (target->GetDistance2d(6403.13f, -935.55f) <= 30.0f) + if (target->IsAlive()) + target->ToPlayer()->TeleportTo(target->GetMapId(), 6401.96f, -795.64f, 3020.17f, 0.01f, TELE_TO_SEAMLESS); + else + target->NearTeleportTo(6401.96f, -795.64f, 3020.17f, 0.01f); + + target->RemoveAurasDueToSpell(SPELL_BELAC_PRISONER); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_torment_AuraScript::OnTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY); + AfterEffectRemove += AuraEffectRemoveFn(spell_tos_torment_AuraScript::OnRemove, SpellEffIndex::EFFECT_1, SPELL_AURA_PERIODIC_DUMMY, AuraEffectHandleModes::AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_tos_torment_AuraScript(); + } +}; + +// 235305 +class spell_tos_prisoner : public SpellScriptLoader +{ +public: + spell_tos_prisoner() : SpellScriptLoader("spell_tos_prisoner") { } + + class spell_tos_prisoner_AuraScript : public AuraScript + { + PrepareAuraScript(spell_tos_prisoner_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool & /*canBeRecalculated*/) + { + amount = -1; + } + + void Absorb(AuraEffect* /*aurEff*/, DamageInfo & dmgInfo, uint32& absorbAmount) + { + absorbAmount = dmgInfo.GetDamage(); + } + + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_tos_prisoner_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_tos_prisoner_AuraScript(); + } +}; + +// 233435 +class spell_tos_calcified_quils : public SpellScript +{ + PrepareSpellScript(spell_tos_calcified_quils); + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + if (GetCaster() && GetHitUnit()) + if (GetCaster()->GetEntry() == NPC_ATRIGAN) + if (GetCaster()->ToCreature()->AI()->GetObjectData(GetHitUnit()->GetGUID(), 0)) + SetHitDamage(0); + } + + void RecalculateDamage(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + if (GetCaster() && GetHitUnit()) + if (GetCaster()->GetEntry() == NPC_ATRIGAN) + if (!GetCaster()->ToCreature()->AI()->GetObjectData(GetHitUnit()->GetGUID(), 1)) + GetHitUnit()->SetPower(POWER_ALTERNATE_POWER, GetHitUnit()->GetPower(POWER_ALTERNATE_POWER) + 10); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_tos_calcified_quils::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + OnEffectHitTarget += SpellEffectFn(spell_tos_calcified_quils::RecalculateDamage, EFFECT_2, SPELL_EFFECT_ENERGIZE); + } +}; + +// 235295 +class spell_tos_adherent_fragment : public SpellScript +{ + PrepareSpellScript(spell_tos_adherent_fragment); + + void HandleAfterCast() + { + if (Unit* caster = GetCaster()) + if (InstanceScript* instance = caster->GetInstanceScript()) + if (instance->instance->IsMythic()) + caster->CastSpellDelay(caster, 241505, true, 1000); + } + + void Register() override + { + AfterCast += SpellCastFn(spell_tos_adherent_fragment::HandleAfterCast); + } +}; + +class achievement_grin_and_bear_it : public AchievementCriteriaScript +{ +public: + achievement_grin_and_bear_it() : AchievementCriteriaScript("achievement_grin_and_bear_it") { } + + bool OnCheck(Player* source, Unit* /*target*/) override + { + if (source->IsAlive() && source->HasAura(233430)) + return true; + + return false; + } +}; + +void AddSC_boss_demonic_inquisition() +{ + new boss_demonic_inquisition(); + new npc_tos_tormented_soul(); + new spell_tos_confess(); + new spell_tos_torment(); + new spell_tos_prisoner(); + RegisterSpellScript(spell_tos_calcified_quils); + RegisterSpellScript(spell_tos_adherent_fragment); + new achievement_grin_and_bear_it(); + +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_FallenAvatar.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_FallenAvatar.cpp new file mode 100644 index 0000000..11400c5 --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_FallenAvatar.cpp @@ -0,0 +1,1360 @@ +/* +https://uwow.biz/ + +*/ + +#include "tomb_of_sargeras.h" + +enum Says +{ + SAY_AGGRO = 0, + SAY_CHAOS = 1, + SAY_BLADES = 2, + SAY_RUPTURE_1 = 3, + SAY_RUPTURE_2 = 4, + SAY_ANNIHILANT = 5, + SAY_DEATH = 6, +}; + +enum Spells +{ + SPELL_UNBOUND_CHAOS = 234057, // + SPELL_DESOLATE = 236494, // + SPELL_SHADOWY_BLADES = 236573, // + SPELL_RUPTURE_REALISTIC = 239132, // + + SPELL_ACTIVATE_PILONES = 240614, // + SPELL_RIPPLE_OF_DARKNESS = 236528, // + + // second phase + SPELL_CONSUME = 240594, // + + SPELL_ANNIHILATION = 235597, // + SPELL_JUMP = 235613, // + + SPELL_SPARK_SECOND_PHASE = 239309, // ? + SPELL_SEAR = 234179, // + SPELL_DARK_MARK = 239825, // + SPELL_RUPTURE_REALISTIC_2 = 235572, // + + SPELL_AURA_WITHOUT_BLOCK = 110470, + SPELL_AURA_PROC = 239830, + SPELL_VISUAL_SWORD = 235582, + + + // heroic + SPELL_TOUCH_OF_SARGERAS_DMG = 239058, + SPELL_TOUCH_OF_SARGERAS_NOT_CLOSE = 239287, + SPELL_TOUCH_OF_SARGERAS_SUM = 239057, // 3 / 5 + SPELL_TOUCH_OF_SARGERAS_AT = 240827, + SPELL_TOUCH_OF_SARGERAS_VIS = 240834, + SPELL_TOUCH_OF_SARGERAS_CAST = 239207, + + SPELL_BLACK_WIND_TICKS = 239417, + SPELL_BLACK_WIND_DMG = 242017, + SPELL_BLACK_WIND_VIS = 241988, + SPELL_BLACK_WIND_AT = 239418, + + SPELL_FEL_INFUSION = 236682, + SPELL_FEL_INFUSION_BUFF = 236684, + + + // add + SPELL_CORRUPTED_BLADE = 236571, // 236604 + SPELL_CORRUPTED_BLADE_1 = 237666, + SPELL_CORRUPTED_BLADE_REMOVE = 236593, // + + // maiden + SPELL_CLEANING_PROTOCOL = 233856, // + SPELL_CLEANING_PROTOCOL_ABSORB = 241008, // + SPELL_MALFUNCTION = 233739, // + + // pilones + SPELL_BEAM_ON_BOSS = 238244, // + SPELL_BEAM_ON_MAIDEN = 238335, // + SPELL_BEAM_COND = 233878, // + SPELL_PILONE_ACTIVATE = 233556, // + SPELL_SPARK_OF_THE_DARK_TITAN = 234873, // + + SPELL_FEL_PILONE_ACTIVATE = 240623, + SPELL_BEAM_ON_PLAYER_FEL = 240631, // (aura) + SPELL_BEAM_ON_PLAYER = 240746, + + SPELL_BEAM_ON_MAIDEN_FEL = 240758, + SPELL_BEAM_ON_BOSS_FEL_VIS = 240630, + SPELL_BEAM_ON_BOSS_FEL = 240675, + SPELL_TAINTED_ESSENCE = 240728, + SPELL_TAINTED_ESSENCE_KILL = 242081, + + SPELL_REMOVE_BEAMS = 238407, // + + SPELL_RAIN_OF_THE_DESTROYER_VIS = 241040, + SPELL_RAIN_OF_THE_DESTROYER_AT = 241039, + SPELL_RAIN_OF_THE_DESTROYER_SUM = 241036, + SPELL_RAIN_OF_THE_DESTROYER_AOE = 240396, + SPELL_RAIN_OF_THE_DESTROYER_CLOSE = 234417, + // 236674 + + + // khadhar 241084 + 235582 + dmg (241123, 241122, 241128) + // velen 241078 + dmg (241470, 241463, heal(241466)) + // illidan 57764 + dmg (241268, 241484) + SPELL_CONVERS_EVENT_1 = 241103, //++ 4955 + + SPELL_BONUS = 240659, +}; + +enum eEvents +{ + EVENT_SHADOWY_BLADES = 1, + EVENT_CHECK_ENERGY, + EVENT_RUPTURE_REALISTIC, + EVENT_UNBOUND_CHAOS, + EVENT_DESOLATE, + EVENT_SELECT_PILONE, + EVENT_RUPTURE_REALISTIC_AND_ENERGY, + EVENT_DARK_MARK, + + EVENT_TOUCH_OF_SARGERAS, + EVENT_SELECT_PILONE_FEL, + EVENT_RAIN, + + EVENT_FORCE_SECOND_PHASE, +}; + + + +Position const pilonesPos[5] +{ + {6547.43f, -760.646f, 1662.98f, 5.75959f }, + {6538.19f, -795.174f, 1662.98f, 0 }, + {6547.38f, -829.529f, 1662.98f, 0.523599f}, + {6572.73f, -854.656f, 1662.98f, 1.0472f }, + {6572.56f, -735.365f, 1662.98f, 5.23599f } +}; + +std::map floorBlocks +{ + { 267700, {6600.41f, -794.17f, 1510.0f, 0}}, + { 267701, {6560.08f, -775.08f, 1510.0f, 0}}, + { 267702, {6600.47f, -774.19f, 1510.0f, 0}}, + { 267703, {6635.91f, -761.15f, 1510.0f, 0}}, + { 267704, {6579.87f, -774.42f, 1510.0f, 0}}, + { 267705, {6642.12f, -795.31f, 1510.0f, 0}}, + { 267706, {6642.62f, -777.85f, 1510.0f, 0}}, + { 267707, {6561.22f, -794.48f, 1510.0f, 0}}, + { 267708, {6581.60f, -794.47f, 1510.0f, 0}}, + { 267709, {6560.60f, -757.41f, 1510.0f, 0}}, + { 267710, {6620.75f, -794.93f, 1510.0f, 0}}, + { 267711, {6597.75f, -753.93f, 1510.0f, 0}}, + { 267712, {6578.06f, -754.54f, 1510.0f, 0}}, + { 267713, {6620.22f, -775.04f, 1510.0f, 0}}, + { 267714, {6617.95f, -756.67f, 1510.0f, 0}}, + { 267715, {6600.41f, -836.14f, 1510.0f, 0}}, + { 267716, {6581.68f, -814.19f, 1510.0f, 0}}, + { 267717, {6614.62f, -836.08f, 1510.0f, 0}}, + { 267718, {6619.10f, -814.93f, 1510.0f, 0}}, + { 267719, {6638.89f, -814.43f, 1510.0f, 0}}, + { 267720, {6631.49f, -830.53f, 1510.0f, 0}}, + { 267721, {6600.83f, -814.48f, 1510.0f, 0}}, + { 267722, {6567.33f, -828.77f, 1510.0f, 0}}, + { 267723, {6582.95f, -832.97f, 1510.0f, 0}}, + { 267724, {6563.81f, -814.08f, 1510.0f, 0}} +}; + +std::map> perephaseEvent +{ + {121171, {{6515.72f, -809.69f, 1663.67f, 0.0f}, {6712.24f, -770.72f, 1519.62f}}}, + {121170, {{6615.99f, -797.28f, 1663.67f, 0.0f}, {6705.52f, -800.44f, 1513.50f}}}, + {121169, {{6514.16f, -785.14f, 1663.67f, 0.0f}, {6708.47f, -782.25f, 1517.22f}}} +}; + +Position const bladesPositions[7] +{ + {6594.51f, -806.92f, 1663.58f, 0.0f}, + {6589.36f, -794.80f, 1663.58f, 0.0f}, + {6597.37f, -779.48f, 1663.58f, 0.0f}, + + {6551.39f, -737.23f, 1663.58f, 0.0f}, + {6529.41f, -771.28f, 1663.58f, 0.0f}, + {6530.16f, -812.10f, 1663.58f, 0.0f}, + {6541.95f, -839.76f, 1663.58f, 0.0f}, +}; + +std::vector sargerasPositions +{ + {6551.96f, -805.34f, 1663.58f, 0.0f}, + {6573.08f, -756.50f, 1663.58f, 0.0f}, + {6578.23f, -779.93f, 1663.58f, 0.0f}, + {6578.44f, -832.90f, 1663.58f, 0.0f}, + {6583.62f, -812.65f, 1663.58f, 0.0f}, + {6562.11f, -794.26f, 1663.58f, 0.0f}, + {6554.01f, -767.29f, 1663.58f, 0.0f}, + {6589.62f, -756.13f, 1663.58f, 0.0f}, + +}; + +std::vector windsPos +{ + { 6538.00f, -754.95f, 1509.91f, 0.0f }, + { 6538.00f, -774.61f, 1509.91f, 0.0f }, + { 6538.00f, -795.38f, 1509.91f, 0.0f }, + { 6538.00f, -814.43f, 1509.91f, 0.0f }, + { 6538.00f, -831.60f, 1509.91f, 0.0f }, // + 118 by x + + { 6642.00f, -741.01f, 1509.91f, 0.0f }, + { 6620.33f, -737.82f, 1509.91f, 0.0f }, + { 6596.41f, -737.06f, 1509.91f, 0.0f }, + { 6577.08f, -736.97f, 1509.91f, 0.0f }, + { 6557.97f, -736.97f, 1509.91f, 0.0f } // -118 by y +}; + +template +struct MySpecialList : public std::list +{ + MySpecialList() = default; + + MySpecialList(const std::initializer_list& _list) : + std::list(_list) {}; + + T popAndSafeLast() + { + if (this->empty()) + throw "MySpecialList is empty!"; + + T val = this->front(); + if (this->size() > 1) + this->pop_front(); + + return val; + } +}; + + +MySpecialList unboundChaosTimers{ 41000, 44000, 36000, 47000, 44000, 35000, 47000 }; +MySpecialList unboundChaosHeroicTimers{ 46000, 35000, 42000, 62000, 35000, 50000 }; +MySpecialList shadowBladesTimers{ 34000, 48000, 43000, 36000, 49000, 43000 }; +MySpecialList shadowBladesHeroicTimers{ 45000, 61000, 37000, 42000, 38000 }; +MySpecialList ruptureRealisticTimersHeroic{ 60000, 80000, 60000}; + +// 116939 +struct boss_fallen_avatar : BossAI +{ + boss_fallen_avatar(Creature* creature) : BossAI(creature, DATA_FALLEN_AVATAR) + { + IntroDone = false; + me->m_SightDistance = 100.0f; + } + + bool IntroDone; + std::vector> pilones; // 0 - free, 1 - usual, 2 -fel + bool isSecondPhase; + std::map localFloorBlocks; + MySpecialList m_unboundChaosTimers; + MySpecialList m_shadowBladesTimers; + MySpecialList m_darkMarkTimers; + MySpecialList m_ruptureRealisticTimers; + + + void Reset() override + { + localFloorBlocks = floorBlocks; + + isSecondPhase = false; + _Reset(); + + pilones.clear(); // just despawned, i think + + if (me->IsAlive()) + if (Creature* maiden = me->SummonCreature(NPC_AVATARA_MAIDEN, 6583.50f, -771.65f, 1663.67f, 3.04f, TEMPSUMMON_MANUAL_DESPAWN)) + for (const auto pos : pilonesPos) + me->SummonCreature(NPC_PILONES, pos, TEMPSUMMON_MANUAL_DESPAWN, 0); + + SetCombatMovement(false); + + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TAINTED_ESSENCE); + + me->SetPower(me->GetPowerType(), 0); + + if (IntroDone) + DoCast(SPELL_VISUAL_SWORD); + + me->SetDisplayId(me->GetNativeDisplayId()); + me->SetCanFly(true); + + if (IsHeroicPlusRaid()) + me->CastSpell(me, SPELL_FEL_INFUSION); + + m_unboundChaosTimers = (IsMythicRaid() ? unboundChaosTimers : unboundChaosHeroicTimers); + m_shadowBladesTimers = (IsMythicRaid() ? shadowBladesTimers : shadowBladesHeroicTimers); + m_ruptureRealisticTimers = (IsHeroicPlusRaid() ? ruptureRealisticTimersHeroic : MySpecialList({ 60000 })); + } + + void EnterCombat(Unit* who) override + { + Talk(SAY_AGGRO); + _EnterCombat(); + + me->SetPower(me->GetPowerType(), 0); + + DoCast(74426); // hack + + events.RescheduleEvent(EVENT_CHECK_ENERGY, urand(4000, 5000)); + events.RescheduleEvent(EVENT_RUPTURE_REALISTIC, 38000); + events.RescheduleEvent(EVENT_UNBOUND_CHAOS, 7000); + events.RescheduleEvent(EVENT_SHADOWY_BLADES, 30000); + events.RescheduleEvent(EVENT_DESOLATE, 13000); + events.RescheduleEvent(EVENT_SELECT_PILONE, 1000); + if (IsHeroicPlusRaid()) + events.RescheduleEvent(EVENT_TOUCH_OF_SARGERAS, 15000); + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_SELECT_PILONE_FEL, 1100); + + if (who) + me->AddDelayedCombat(500, [&]() -> void + { + if (Creature* maiden = summons.GetCreature(NPC_AVATARA_MAIDEN)) + { + if (!maiden->IsInCombat()) + DoZoneInCombat(maiden); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, maiden); + } + }); + } + + void SummonedCreatureDies(Creature* creature, Unit* killer) override + { + if (killer->GetGUID() == me->GetGUID()) + return; + + if (creature->GetEntry() != NPC_AVATARA_MAIDEN) + return; + + events.Reset(); + events.RescheduleEvent(EVENT_FORCE_SECOND_PHASE, 1); + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + // _JustDied(); // without summons + me->RemoveAllAreaTriggers(); + me->m_Events.KillAllEvents(true); + + events.Reset(); + if (instance) + { + instance->SetBossState(DATA_FALLEN_AVATAR, DONE); + instance->SaveToDB(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + ; // frame instance start/end non supporte par notre core + } + + ; // CreateConversation : cinematique de conversation, non portee + me->AddDelayedEvent(30000, [this]() -> void + { + if (Creature* kiljedan = instance->instance->GetCreature(instance->GetGuidData(NPC_KILDJEDAN_AVATARA))) + { + kiljedan->GetMotionMaster()->MovePoint(0, 6748.58f, -795.00f, 1527.00f); + kiljedan->AddDelayedEvent(4000, [kiljedan]() -> void + { + kiljedan->SetVisible(false); + }); + } + }); + + me->AddDelayedEvent(58000, [this]() -> void + { + if (Creature* velen = summons.GetCreature(121170)) + { + velen->SetCanFly(false); + velen->GetMotionMaster()->MovePoint(0, 6748.58f, -795.00f, 1527.00f); + velen->AddDelayedEvent(4000, [velen]() -> void + { + velen->SetVisible(false); + }); + } + }); + + me->AddDelayedEvent(115000, [this]() -> void + { + for (auto id : { 121171, 121169}) + if (Creature* add = summons.GetCreature(id)) + { + add->SetCanFly(false); + add->GetMotionMaster()->MovePoint(0, 6748.58f, -795.00f, 1527.00f); + add->AddDelayedEvent(3000, [add]() -> void + { + add->SetVisible(false); + }); + } + }); + + /* instance->instance->ApplyOnEveryPlayer([&](Player* player) + { + if ((me->getThreatManager().getThreat(player) > 0.0f)) + me->CastSpell(player, SPELL_BONUS, true); + });*/ + } + + void SetGUID(ObjectGuid guid, int32 id) override + { + if (Creature* add = instance->instance->GetCreature(guid)) + { + std::pair* selected = nullptr; + for (auto& pair : localFloorBlocks) + { + float dist = add->GetDistance(pair.second); + if (!selected || dist > add->GetDistance(selected->second)) + { + if (add->GetPositionZ() <= 1509.4f && dist > 12.0f) + continue; + + selected = &pair; + } + } + + if (selected) + { + if (GameObject* go = instance->instance->GetGameObject(instance->GetGuidData(selected->first))) + go->AddDelayedEvent(6000, [go]() -> void + { + go->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED, nullptr, false); + }); + + localFloorBlocks.erase(selected->first); + } + } + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + BossAI::EnterEvadeMode(); + me->NearTeleportTo(me->GetHomePosition()); + + instance->instance->ApplyOnEveryPlayer([](Player* player) -> void + { + if (player->GetPositionZ() < 1660.0f) + player->NearTeleportTo(6502.62f, -795.02f, 1663.58f, 0.0f); + }); + } + + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) + { + case NPC_CORRUPTED_BLADE: + summon->SetReactState(REACT_PASSIVE); + if (Unit* target = summon->GetVictim()) + { + summon->CastSpell(target, SPELL_CORRUPTED_BLADE); + summon->CastSpell(target, SPELL_CORRUPTED_BLADE_1); // 236604 + summon->AddDelayedEvent(7000, [summon]() -> void + { + summon->CastSpell(summon, SPELL_CORRUPTED_BLADE_REMOVE); + summon->DespawnOrUnsummon(2000); + }); + } + break; + case NPC_PILONES: + pilones.push_back({ summon->GetGUID(), 0 }); + break; + } + + BossAI::JustSummoned(summon); + } + + void DamageTaken(Unit* attacker, uint32& damage) override + { + if (!isSecondPhase) + if (me->HealthBelowPct(20)) + { + + events.Reset(); + events.RescheduleEvent(EVENT_FORCE_SECOND_PHASE, 1); + } + } + + void MoveInLineOfSight(Unit* who) override + { + if (!who->IsPlayer()) + return; + + if (me->IsAlive() && !me->IsInCombat() && who->GetDistance(me) <= 19.0f) + AttackStart(who); + + if (me->GetDistance(who) < 100.0f && !IntroDone) + { + IntroDone = true; + if (Creature* kiljedan = instance->instance->GetCreature(instance->GetGuidData(NPC_KILDJEDAN_AVATARA))) + { + kiljedan->CastSpell(kiljedan, 242902); + kiljedan->AddDelayedEvent(27000, [kiljedan, this]() -> void + { + kiljedan->CastSpell(me, 240897); + me->AddDelayedEvent(3000, [this]() -> void + { + me->SetAIAnimKitId(12758); + me->AddDelayedEvent(10000, [this]() -> void + { + DoCast(SPELL_VISUAL_SWORD); + }); + }); + + kiljedan->AddDelayedEvent(13000, [kiljedan]() -> void + { + kiljedan->CastSpell(kiljedan, 240923); + }); + }); + } + } + } + + void SpellFinishCast(const SpellInfo* spell) override + { + switch (spell->Id) + { + case SPELL_CONSUME: + if (Creature* maiden = summons.GetCreature(NPC_AVATARA_MAIDEN)) + { + if (maiden->GetHealth() + me->GetHealth() >= me->GetMaxHealth()) + me->SetHealth(me->GetMaxHealth()); + else + me->SetHealth(maiden->GetHealth() + me->GetHealth()); + + maiden->CastSpell(maiden, SPELL_REMOVE_BEAMS); + me->Kill(maiden); + + summons.DespawnAll(); + } + break; + case SPELL_ANNIHILATION: + Talk(SAY_ANNIHILANT); + + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); // sometimes it's broke after maiden dead + ; // frame instance start/end non supporte par notre core + if (GameObject* go = me->GetMap()->GetGameObject(instance->GetGuidData(GO_AVATARA_FLOOR))) // destroy room + go->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED, nullptr, false); + + me->RemoveAurasDueToSpell(74426); // hack + + me->AddDelayedCombat(500, [this]() -> void + { + SetCombatMovement(true); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveJump({me->GetPositionX(), me->GetPositionY(), 1509.9f}, 40, 40); + me->SetCanFly(false); + + // second phase + events.RescheduleEvent(EVENT_DESOLATE, 19000); + events.RescheduleEvent(EVENT_RUPTURE_REALISTIC_AND_ENERGY, 6000); + events.RescheduleEvent(EVENT_DARK_MARK, (IsMythicRaid() ? 31000 : 21000)); + if (IsHeroicPlusRaid()) + me->CastSpell(me, SPELL_BLACK_WIND_TICKS, true); + + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_RAIN, 16000); + + me->AddDelayedCombat(2000, [this]() -> void + { + DoCast(me, SPELL_SEAR, true); + }); + me->SetDisplayId(74497); + + me->AddDelayedCombat(5000, [this]() -> void + { + // me->GetMotionMaster()->MovementExpired(); + for (auto pair : perephaseEvent) + if (Creature* cre = me->SummonCreature(pair.first, pair.second.first)) + { + cre->SetWalk(false); + cre->GetMotionMaster()->MoveSmoothFlyPath(0, pair.second.second); + cre->SetCanFly(true); + } + me->CastSpellDelay(me, SPELL_CONVERS_EVENT_1, false, 3000); + }); + + me->AddDelayedCombat(7000, [this]() -> void + { + instance->instance->ApplyOnEveryPlayer([](Player* player) -> void + { + if (player->GetPositionZ() >= 1643.75f) + player->NearTeleportTo(6557.89f, -872.24f, 1508.68f, 0.92f); + }); + }); + }); + break; + case SPELL_RUPTURE_REALISTIC_2: + for (auto pair : localFloorBlocks) + if (me->GetDistance(pair.second) <= 30.0f) + { + if (GameObject* go = instance->instance->GetGameObject(instance->GetGuidData(pair.first))) + go->AddDelayedEvent(me->GetDistance(pair.second) * 3 + 1, [go]() -> void + { + go->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED, nullptr, false); + }); + + localFloorBlocks.erase(pair.first); + } + break; + } + } + + uint32 GetModifyedData(uint32) override + { + return m_darkMarkTimers.popAndSafeLast(); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_FORCE_SECOND_PHASE: + isSecondPhase = true; + DoCast(SPELL_REMOVE_BEAMS); + DoCast(SPELL_CONSUME); + me->AddDelayedEvent(500, [this]() -> void + { + DoCast(SPELL_ANNIHILATION); + }); + + me->SetPower(me->GetPowerType(), 0); + break; + case EVENT_SHADOWY_BLADES: + { + Talk(SAY_BLADES); + DoCast(SPELL_SHADOWY_BLADES); + + auto threatlist = me->getThreatManager().getThreatList(); + threatlist.remove_if([](HostileReference* ref) + { + return !ref->getTarget()->IsPlayer(); + }); + + for (uint8 i = 0; i < (IsMythicRaid() ? 5 : 3); ++i) + { + if (threatlist.empty()) + break; + + auto itr = threatlist.begin(); + std::advance(itr, urand(0, threatlist.size() - 1)); + + if (Unit* target = (*itr)->getTarget()) + me->SummonCreature(NPC_CORRUPTED_BLADE, bladesPositions[i], TEMPSUMMON_TIMED_DESPAWN, 11000); + + threatlist.remove(*itr); + } + + events.RescheduleEvent(EVENT_SHADOWY_BLADES, m_shadowBladesTimers.popAndSafeLast()); + break; + } + case EVENT_RUPTURE_REALISTIC: + Talk(SAY_RUPTURE_1); + Talk(SAY_RUPTURE_2); + DoCast(SPELL_RUPTURE_REALISTIC); + events.RescheduleEvent(EVENT_RUPTURE_REALISTIC, m_ruptureRealisticTimers.popAndSafeLast()); + break; + case EVENT_UNBOUND_CHAOS: + DoCast(SPELL_UNBOUND_CHAOS); + Talk(SAY_CHAOS); + events.RescheduleEvent(EVENT_UNBOUND_CHAOS, m_unboundChaosTimers.popAndSafeLast()); + break; + case EVENT_DESOLATE: + DoCastTopAggro(SPELL_DESOLATE, false, true); + events.RescheduleEvent(EVENT_DESOLATE, 13000); + break; + case EVENT_SELECT_PILONE: + DoCast(SPELL_ACTIVATE_PILONES); + if (!pilones.empty()) + { + std::vector*> freePilones{}; + for (auto& pair : pilones) + { + if (pair.second == 1) + pair.second = 0; + + if (pair.second == 0) + freePilones.push_back(&pair); + } + + if (!freePilones.empty()) + { + uint8 i = urand(0, freePilones.size() - 1); + if (Creature* summon = ObjectAccessor::GetCreature(*me, freePilones[i]->first)) + summon->CastSpell(summon, SPELL_PILONE_ACTIVATE); + + freePilones[i]->second = 1; + } + } + + events.RescheduleEvent(EVENT_SELECT_PILONE, (IsMythicRaid() ? 12000 : 40000) + 10000); + break; + case EVENT_SELECT_PILONE_FEL: + { + if (!pilones.empty()) + { + std::vector*> freePilones{}; + for (auto& pair : pilones) + { + if (pair.second == 2) + pair.second = 0; + + if (pair.second == 0) + freePilones.push_back(&pair); + } + + if (!freePilones.empty()) + { + Trinity::Containers::RandomResize(freePilones, 2); + + for (auto& pair : freePilones) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, pair->first)) + summon->CastSpell(summon, SPELL_FEL_PILONE_ACTIVATE); + + pair->second = 2; + } + } + } + + events.RescheduleEvent(EVENT_SELECT_PILONE_FEL, 49000 + 10000); + break; + } + case EVENT_CHECK_ENERGY: + if (isSecondPhase) + break; + + me->SetPower(me->GetPowerType(), me->GetPower(me->GetPowerType()) + 1); + + if ((me->GetPower(me->GetPowerType()) >= 100 && summons.GetCreature(NPC_AVATARA_MAIDEN) && + summons.GetCreature(NPC_AVATARA_MAIDEN)->IsAlive()) + || (summons.GetCreature(NPC_AVATARA_MAIDEN) && !summons.GetCreature( + NPC_AVATARA_MAIDEN)->IsAlive())) + { + isSecondPhase = true; + events.Reset(); + DoCast(SPELL_REMOVE_BEAMS); + DoCast(SPELL_CONSUME); + me->AddDelayedEvent(500, [this]() -> void + { + DoCast(SPELL_ANNIHILATION); + }); + + me->SetPower(me->GetPowerType(), 0); + break; + } + + if (me->GetVictim() && !me->IsWithinMeleeRange(me->GetVictim())) + DoCast(236528); + + if (!isSecondPhase) + events.RescheduleEvent(EVENT_CHECK_ENERGY, urand(4000, 5000)); + break; + case EVENT_RUPTURE_REALISTIC_AND_ENERGY: + + if (isSecondPhase) + if (!me->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) + { + float tz = me->GetMap()->GetHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false, MAX_FALL_DISTANCE); + if (tz <= 1509.7f) // fall under water + me->NearTeleportTo(me->GetPositionX(), me->GetPositionY(), 1508.80f, me->GetOrientation()); + else + me->GetMotionMaster()->MoveFall(0); + } + + + if (me->GetPower(me->GetPowerType()) >= 100) + { + DoCast(SPELL_RUPTURE_REALISTIC_2); + Talk(SAY_RUPTURE_1); + Talk(SAY_RUPTURE_2); + } + else + me->SetPower(me->GetPowerType(), me->GetPower(me->GetPowerType()) + 3); + + events.RescheduleEvent(EVENT_RUPTURE_REALISTIC_AND_ENERGY, 1000); + break; + case EVENT_DARK_MARK: + m_darkMarkTimers = MySpecialList({ 6000, 8000, 10000 }); + DoCast(SPELL_DARK_MARK); + events.RescheduleEvent(EVENT_DARK_MARK, IsMythicRaid() ? 25000 : 34000); + break; + case EVENT_TOUCH_OF_SARGERAS: + { + DoCast(SPELL_TOUCH_OF_SARGERAS_CAST); + me->AddDelayedCombat(2500, [this]() -> void + { + std::vector positions = sargerasPositions; + + Trinity::Containers::RandomResize(positions, IsMythicRaid() ? 4 : 3); + for (const auto& pos : positions) + me->CastSpell(pos.GetPositionX() + frand(-5, 5), pos.GetPositionY() + frand(-5, 5), pos.GetPositionZ(), SPELL_TOUCH_OF_SARGERAS_SUM, true); + }); + events.RescheduleEvent(EVENT_TOUCH_OF_SARGERAS, IsMythicRaid() ? 63000 : 45000); + break; + } + case EVENT_RAIN: + { + auto blocks = localFloorBlocks; + + Trinity::Containers::RandomResize(blocks, 7); + uint8 i = 1; + for (const auto& pair : blocks) + { + me->AddDelayedCombat(i++ * 50 + 10, [this, pair]() -> void + { + me->CastSpell(pair.second.GetPositionX() + frand(-3, 3), pair.second.GetPositionY() + frand(-3, 3), pair.second.GetPositionZ(), SPELL_RAIN_OF_THE_DESTROYER_SUM, true); + }); + } + + events.RescheduleEvent(EVENT_RAIN, 35000); + break; + } + } + } + DoMeleeAttackIfReady(); + } +}; + +// 117264 +struct npc_avatara_maiden : public ScriptedAI +{ + npc_avatara_maiden(Creature* creature) : ScriptedAI(creature) {} + + EventMap events; + + void Reset() override + { + events.Reset(); + me->SetPower(me->GetPowerType(), 0); + } + + void EnterCombat(Unit* who) override + { + me->SetReactState(REACT_DEFENSIVE); + me->SetPower(me->GetPowerType(), 0); + + events.RescheduleEvent(EVENT_1, 1000); + + if (who) + me->AddDelayedCombat(500, [&]() -> void + { + if (Unit* owner = me->GetAnyOwner()) + if (!owner->IsInCombat()) + DoZoneInCombat(owner->ToCreature()); + }); + + me->AddUnitState(UNIT_STATE_CASTING); + } + + void IsSummonedBy(Unit*) override + { + me->SetReactState(REACT_PASSIVE); + } + + void OnApplyOrRemoveAura(uint32 spellId, AuraRemoveMode /*mode*/, bool apply) override + { + if (spellId == SPELL_CLEANING_PROTOCOL_ABSORB && !apply) + { + me->SetPower(me->GetPowerType(), 0); + DoCast(me, SPELL_MALFUNCTION, true); + me->DealDamage(me, me->GetMaxHealth()*0.25); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + if (me->GetPower(me->GetPowerType()) >= 100 && !me->HasAura(SPELL_CLEANING_PROTOCOL_ABSORB)) + { + me->CastCustomSpell(SPELL_CLEANING_PROTOCOL_ABSORB, SPELLVALUE_BASE_POINT0, int32(me->GetMaxHealth() * 0.05f), me); + DoCast(SPELL_CLEANING_PROTOCOL); + Talk(0); + Talk(1); + } + events.RescheduleEvent(EVENT_1, 1000); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + + +// 117279 +struct npc_avatara_pilones : public ScriptedAI +{ + npc_avatara_pilones(Creature* creature) : ScriptedAI(creature) {} + + EventMap events; + ObjectGuid selectedGuid{}; + + void Reset() override + { + events.Reset(); + } + + void OnApplyOrRemoveAura(uint32 spellId, AuraRemoveMode /*mode*/, bool apply) override + { + if (spellId == SPELL_PILONE_ACTIVATE) + { + if (apply) + { + events.RescheduleEvent(EVENT_1, 100); + if (!me->GetMap()->IsMythic()) + if (Aura* aura = me->GetAura(SPELL_PILONE_ACTIVATE)) + aura->SetDuration(39500); + } + else + { + events.Reset(); + if (!selectedGuid.IsEmpty()) + if (Unit* selected = ObjectAccessor::GetUnit(*me, selectedGuid)) + if(selected->IsInWorld()) + selected->CastSpell(selected, SPELL_REMOVE_BEAMS, true); + } + } + else if (spellId == SPELL_FEL_PILONE_ACTIVATE) + { + if (apply) + events.RescheduleEvent(EVENT_2, 100); + else + { + events.Reset(); + if (!selectedGuid.IsEmpty()) + if (Unit* selected = ObjectAccessor::GetUnit(*me, selectedGuid)) + if (selected->IsInWorld()) + selected->CastSpell(selected, SPELL_REMOVE_BEAMS, true); + } + } + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + { + if (Unit* owner = me->GetAnyOwner()) + { + if (Unit* target = me->GetVictim()) + { + ObjectGuid tempSelectedGuid{}; + if (target->IsInBetween(owner, me, 3.0f)) + tempSelectedGuid = target->GetGUID(); + else + tempSelectedGuid = owner->GetGUID(); + + if (!selectedGuid.IsEmpty() && tempSelectedGuid != selectedGuid) + if (Unit* selected = ObjectAccessor::GetUnit(*me, selectedGuid)) + selected->CastSpell(selected, SPELL_REMOVE_BEAMS, true); + + selectedGuid = tempSelectedGuid; + + if (!selectedGuid.IsEmpty()) + if (Unit* selected = ObjectAccessor::GetUnit(*me, selectedGuid)) + { + if (!selected->HasAura((selected == owner ? SPELL_BEAM_ON_BOSS : SPELL_BEAM_ON_MAIDEN))) + me->CastSpell(selected, (selected == owner ? SPELL_BEAM_ON_BOSS : SPELL_BEAM_ON_MAIDEN)); + if (selected != owner && urand(1, 4) == 1) + selected->CastSpell(selected, 233961); + + for (uint32 spell : {SPELL_BEAM_COND, SPELL_SPARK_OF_THE_DARK_TITAN}) + if (!selected->HasAura(spell)) + selected->CastSpell(selected, spell); + } + } + } + events.RescheduleEvent(EVENT_1, 1000); + break; + } + case EVENT_2: + { + auto* owner = me->GetAnyOwner(); + if (!owner) + break; + ObjectGuid tempSelectedGuid = owner->GetGUID(); + if (!selectedGuid.IsEmpty()) + if (Unit* selected = ObjectAccessor::GetUnit(*me, selectedGuid)) + if (selected->IsInBetween(owner, me, 3.0f)) + tempSelectedGuid = selectedGuid; + + const auto& players = me->GetMap()->GetPlayers(); + for (auto& itr : players) + { + Unit* player = itr.GetSource(); + if (player->IsInBetween(owner, me, 3.0f)) + if (Unit* tempSelected = ObjectAccessor::GetUnit(*me, tempSelectedGuid)) + if (me->GetDistance(tempSelected) > me->GetDistance(player)) + tempSelectedGuid = player->GetGUID(); + } + + if (InstanceScript* instance = me->GetInstanceScript()) + if (Creature* maiden = instance->instance->GetCreature(instance->GetGuidData(NPC_AVATARA_MAIDEN))) + if (Unit* tempSelected = ObjectAccessor::GetUnit(*me, tempSelectedGuid)) + if (maiden->IsInBetween(owner, me, 3.0f)) + if (me->GetDistance(tempSelected) > me->GetDistance(maiden)) + tempSelectedGuid = maiden->GetGUID(); + + + if (!selectedGuid.IsEmpty() && tempSelectedGuid != selectedGuid) + if (Unit* selected = ObjectAccessor::GetUnit(*me, selectedGuid)) + { + selected->CastSpell(selected, SPELL_REMOVE_BEAMS, true); + if (selected->IsPlayer()) + if (selected->GetAuraCount(SPELL_TAINTED_ESSENCE) >= 10 ) + selected->CastSpell(selected, SPELL_TAINTED_ESSENCE_KILL); + } + + selectedGuid = tempSelectedGuid; + + if (!selectedGuid.IsEmpty()) + if (Unit* selected = ObjectAccessor::GetUnit(*me, selectedGuid)) + { + if (selected->IsPlayer()) + { + if (!selected->HasAura(SPELL_BEAM_ON_PLAYER_FEL)) + me->CastSpell(selected, SPELL_BEAM_ON_PLAYER_FEL); + + if (!selected->HasAura(SPELL_BEAM_ON_PLAYER)) + selected->AddAura(SPELL_BEAM_ON_PLAYER, selected); + } + else if (selected->GetEntry() == NPC_AVATARA_MAIDEN) + selected->CastSpell(selected, SPELL_BEAM_ON_MAIDEN_FEL); + else if (selected->GetEntry() == NPC_FALLEN_AVATAR) + { + selected->CastSpell(selected, SPELL_BEAM_ON_BOSS_FEL); + me->CastSpell(me, SPELL_BEAM_ON_BOSS_FEL_VIS); + } + } + events.RescheduleEvent(EVENT_2, 1000); + break; + } + } + } + + } +}; + +// 120838 +struct npc_tos_touch_of_sargeras : public ScriptedAI +{ + npc_tos_touch_of_sargeras(Creature* creature) : ScriptedAI(creature) {} + + EventMap events; + + void IsSummonedBy(Unit* owner) override + { + me->SetReactState(REACT_PASSIVE); + DoCast(SPELL_TOUCH_OF_SARGERAS_AT); + DoCast(SPELL_TOUCH_OF_SARGERAS_VIS); + events.RescheduleEvent(EVENT_1, 8000+3000); + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + { + bool hasPlayer = false; + const auto& players = me->GetMap()->GetPlayers(); + for (const auto& player : players) + if (me->GetDistance(player.GetSource()) <= 6) + { + hasPlayer = true; + break; + } + + + DoCast(me, hasPlayer ? SPELL_TOUCH_OF_SARGERAS_DMG : SPELL_TOUCH_OF_SARGERAS_NOT_CLOSE); + me->RemoveAllAuras(); + me->RemoveAllAreaTriggers(); + + me->DespawnOrUnsummon(100); + break; + } + } + } + + } +}; + +// 120961 +struct npc_tos_rain_of_destroyer : public ScriptedAI +{ + npc_tos_rain_of_destroyer(Creature* creature) : ScriptedAI(creature) {} + + EventMap events; + + void IsSummonedBy(Unit* owner) override + { + me->SetReactState(REACT_PASSIVE); + me->CastSpell(me, SPELL_RAIN_OF_THE_DESTROYER_AT); + me->CastSpell(me, SPELL_RAIN_OF_THE_DESTROYER_VIS); + events.RescheduleEvent(EVENT_1, 6000); + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + { + bool hasPlayer = false; + const auto& players = me->GetMap()->GetPlayers(); + for (const auto& player : players) + if (me->GetDistance(player.GetSource()) <= 4) + { + hasPlayer = true; + break; + } + + + DoCast(me, hasPlayer ? SPELL_RAIN_OF_THE_DESTROYER_CLOSE : SPELL_RAIN_OF_THE_DESTROYER_AOE); + if (!hasPlayer) + if (Unit* owner = me->GetAnyOwner()) + owner->ToCreature()->AI()->SetGUID(me->GetGUID()); + + me->DespawnOrUnsummon(6200); + break; + } + } + } + + } +}; + + +// 239132 235572 +class spell_tos_rupture_realistic : public SpellScript +{ + PrepareSpellScript(spell_tos_rupture_realistic); + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + auto caster = GetCaster(); + auto target = GetHitUnit(); + + if (!caster || !target) + return; + + float dist = caster->GetDistance(target); + if (dist < 40.0f) + SetHitDamage(GetHitDamage() * (0.1f + 0.9f*(40.0f - dist) / 40.0f)); + else + SetHitDamage(GetHitDamage() * 0.1f); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tos_rupture_realistic::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +// 239742 +class spell_tos_dark_mark : public SpellScript +{ + PrepareSpellScript(spell_tos_dark_mark); + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + auto caster = GetCaster(); + auto target = GetHitUnit(); + + if (!caster || !target || target == caster) + return; + + caster->CastSpell(target, 239819); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tos_dark_mark::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +// 234873 +class spell_tos_avatara_energy : public AuraScript +{ + PrepareAuraScript(spell_tos_avatara_energy); + + bool isFirstTick = true; + + void OnTick(AuraEffect const* /*aurEff*/) + { + Unit* target = GetTarget(); + if (!target) + return; + + target->SetPower(target->GetPowerType(), target->GetPower(target->GetPowerType()) + ((isFirstTick || urand(1, 100) == 90) ? 3 : 2)); + isFirstTick = false; + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_avatara_energy::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_ENERGIZE); + } +}; + +// 238460 +class spell_tos_shadow_blades : public SpellScript +{ + PrepareSpellScript(spell_tos_shadow_blades); + + void HandleDummy(SpellEffIndex /*effectIndex*/) + { + auto* caster = GetCaster(); + auto* target = GetHitUnit(); + if (!caster || !target) + return; + + caster->CastSpell(target, (caster->GetMap()->IsHeroicPlusRaid() ? 236604 : 240970)); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tos_shadow_blades::HandleDummy, SpellEffIndex::EFFECT_0, SpellEffectName::SPELL_EFFECT_DUMMY); + } +}; + + +// 239417 +class spell_tos_black_winds : public AuraScript +{ + PrepareAuraScript(spell_tos_black_winds); + + void OnTick(AuraEffect const* /*aurEff*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + + uint8 i = urand(0, 9); + Position start = windsPos[i]; + Position end = windsPos[i]; + + if (i <= 4) + end.m_positionX += 118.0f; + else + end.m_positionY -= 118.0f; + + if(urand(1, 2) == 2) + std::swap(start, end); + + + if (Creature* add = caster->SummonCreature(NPC_BLACK_WINDS, start, TEMPSUMMON_TIMED_DESPAWN, 10000)) + { + add->SetReactState(REACT_PASSIVE); + add->CastSpell(add, SPELL_BLACK_WIND_VIS); + add->CastSpell(add, SPELL_BLACK_WIND_AT); + add->AddDelayedEvent(1000, [add, end]() -> void + { + add->GetMotionMaster()->MovePoint(0, end); + }); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_black_winds::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +// 236682 +class spell_tos_fel_infusion : public AuraScript +{ + PrepareAuraScript(spell_tos_fel_infusion); + + void OnTick(AuraEffect const* /*aurEff*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + + if (caster->GetPositionZ() <= 1508.9f || (caster->GetPositionX() >= 6667.59f && caster->GetPositionZ() <= 1540.68f)) + caster->CastSpell(caster, SPELL_FEL_INFUSION_BUFF); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_fel_infusion::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +// 239739 +class spell_tos_black_mark_aura : public AuraScript +{ + PrepareAuraScript(spell_tos_black_mark_aura); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (!GetCaster() || !GetTarget()) + return; + + Creature* cre = GetCaster()->ToCreature(); + Unit* target = GetTarget(); + if (!cre || !target) + return; + + uint32 timer = cre->AI()->GetModifyedData(1); + + GetAura()->SetDuration(timer); + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_tos_black_mark_aura::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + + +void AddSC_boss_fallen_avatar() +{ + RegisterCreatureAI(boss_fallen_avatar); + RegisterCreatureAI(npc_avatara_maiden); + RegisterCreatureAI(npc_avatara_pilones); + RegisterCreatureAI(npc_tos_touch_of_sargeras); + RegisterCreatureAI(npc_tos_rain_of_destroyer); + RegisterSpellScript(spell_tos_rupture_realistic); + RegisterSpellScript(spell_tos_dark_mark); + RegisterAuraScript(spell_tos_avatara_energy); + RegisterSpellScript(spell_tos_shadow_blades); + RegisterAuraScript(spell_tos_black_winds); + RegisterAuraScript(spell_tos_fel_infusion); + RegisterAuraScript(spell_tos_black_mark_aura); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Goroth.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Goroth.cpp new file mode 100644 index 0000000..bc0982e --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Goroth.cpp @@ -0,0 +1,755 @@ +/* +https://uwow.biz/ +*/ + +#include "AreaTriggerAI.h" +#include "tomb_of_sargeras.h" + +enum Spells +{ + SPELL_GOROTH_ENERGIZE = 237333, + SPELL_BURNING_ARMOR = 231363, + SPELL_BURNING_ERUPTION = 231395, + SPELL_BURNING_ERUPTION_DUMMY = 233025, + SPELL_CRASHING_COMET_FILTER = 232249, //Caster Boss + SPELL_CRASHING_COMET_LFR_FILTER = 244548, //Caster Boss. Only LFR + SPELL_CRASHING_COMET_FILTER2 = 232254, //Caster Ember Stalker - 115892 + SPELL_CRASHING_COMET_LFR_FILTER2 = 244580, //Caster Ember Stalker - 115892. Only LFR + SPELL_CRASHING_COMET_MISSILE = 230339, + SPELL_CRASHING_COMET_LFR_MISSILE = 244581, + SPELL_INFERNAL_SPIKE_FILTER = 233050, + SPELL_INFERNAL_SPIKE_SUMMON = 233055, + SPELL_INFERNAL_SPIKE_AT = 233019, + SPELL_INFERNAL_SPIKE_PROTECTION = 234475, + SPELL_SHATTERING_STAR_FILTER = 233269, + SPELL_SHATTERING_STAR_MARK = 233272, + SPELL_SHATTERING_STAR_AURA = 233289, //unk + SPELL_SHATTERING_STAR_SPEED = 233290, + SPELL_SHATTERING_STAR_HIT = 233274, + SPELL_SHATTERING_STAR_AT = 233279, + SPELL_SHATTERING_NOVA = 233283, + SPELL_STAR_BURN = 236329, + SPELL_INFERNAL_BURNING = 233062, + SPELL_INFERNAL_DETONATION = 233900, //Mythic + SPELL_RAIN_OF_BRIMSTONE_DUMMY = 233285, //Mythic + SPELL_RAIN_OF_BRIMSTONE_MISSILE = 238587, //Mythic + SPELL_RAIN_OF_BRIMSTONE_SUMMON = 233266, //Mythic + SPELL_RAIN_OF_BRIMSTONE_SPIKE_DESTROY = 238659, //Mythic + + //Lava Stalker + SPELL_FEL_PERIODIC_TRIGGER = 234386, + SPELL_FEL_ERUPTION_TELEGRAPH = 234366, + SPELL_FEL_ERUPTION_DUMMY = 234368, + SPELL_FEL_ERUPTION_MISSILE = 234387, + SPELL_FEL_ERUPTION_AT = 234330, + + //Brimstone Infernal + SPELL_FEL_FIRE = 241455, +}; + +enum eEvents +{ + EVENT_BURNING_ARMOR = 1, + EVENT_CRASHING_COMET = 2, + EVENT_INFERNAL_SPIKE = 3, + EVENT_SHATTERING_STAR = 4, + EVENT_RAIN_OF_BRIMSTONE = 5, +}; + +enum Misc +{ + SPELLVISUAL_INFERNAL_SPIKE_DESTROY = 66119, + DATA_SPIKES_COUNTER, +}; + +Position const centrPos = {6101.30f, -795.72f, 2971.72f}; + +//115844 +struct boss_goroth : BossAI +{ + explicit boss_goroth(Creature* creature) : BossAI(creature, DATA_GOROTH) {} + + uint8 spiketouched = 0; + uint8 felRand = 0; + uint8 shatteringCounter = 0; + uint8 rainBrimstoneCounter = 0; + uint32 felEruptionTimer = 0; + uint32 shatteringTimer = 0; + uint32 rainBrimstoneTimer = 0; + ObjectGuid shatteringTargetGUID; + + void Reset() override + { + _Reset(); + + spiketouched = 0; + shatteringCounter = 0; + rainBrimstoneCounter = 0; + shatteringTimer = 60000; + rainBrimstoneTimer = 60000; + me->RemoveAurasDueToSpell(SPELL_GOROTH_ENERGIZE); + me->SetMaxPower(POWER_ENERGY, 100); + me->SetPower(POWER_ENERGY, 0); + me->SetReactState(REACT_DEFENSIVE); + me->SummonCreature(NPC_EMBER_STALKER, 6194.41f, -842.93f, 3042.72f, 0.0f); + + if (IsHeroicPlusRaid()) + { + felRand = 0; + felEruptionTimer = 30000; + } + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + + DoCast(me, SPELL_GOROTH_ENERGIZE, true); + events.RescheduleEvent(EVENT_BURNING_ARMOR, 10000); + events.RescheduleEvent(EVENT_CRASHING_COMET, 8000); + events.RescheduleEvent(EVENT_INFERNAL_SPIKE, 4000); + events.RescheduleEvent(EVENT_SHATTERING_STAR, IsMythicRaid() ? 34000 : 24000); + + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_RAIN_OF_BRIMSTONE, 12000); + + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_INFERNAL_SPIKE_PROTECTION); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(12); + + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_INFERNAL_SPIKE_PROTECTION); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_STAR_BURN); + } + + void KilledUnit(Unit* who) override + { + if (!who->IsPlayer()) + return; + + Talk({ 7, 9, 10 }); + } + + void DoAction(int32 const action) override + { + switch (action) + { + case ACTION_1: + if (auto player = ObjectAccessor::GetPlayer(*me, shatteringTargetGUID)) + DoCast(player, SPELL_STAR_BURN, true); + break; + case ACTION_2: + ++spiketouched; + break; + } + } + + uint32 GetData(uint32 type) const override + { + if (type == DATA_SPIKES_COUNTER) + return spiketouched; + + return 0; + } + + void OnRemoveAuraTarget(Unit* target, uint32 spellId, AuraRemoveMode mode) override + { + if (!me->IsInCombat() || !target || mode != AURA_REMOVE_BY_EXPIRE) + return; + + switch (spellId) + { + case SPELL_BURNING_ARMOR: + target->CastSpell(target, SPELL_BURNING_ERUPTION, true); + me->CastSpell(target, SPELL_BURNING_ERUPTION_DUMMY, true); + break; + } + } + + void SpellFinishCast(const SpellInfo* spell) override + { + switch (spell->Id) + { + case SPELL_INFERNAL_BURNING: + { + me->SetPower(POWER_ENERGY, 0); + EntryCheckPredicate pred(NPC_INFERNAL_SPIKE); + summons.DoAction(ACTION_1, pred); + break; + } + case SPELL_RAIN_OF_BRIMSTONE_DUMMY: + { + Position pos; + std::list randPosList; + bool badPos = false; + uint16 traiCount = 0; + while (randPosList.size() < 3) + { + badPos = false; + centrPos.SimplePosXYRelocationByAngle(pos, frand(25.0f, 35.0f), frand(0.0f, 6.28f)); + ++traiCount; + + for (auto _pos : randPosList) + { + if (pos.GetExactDist(&_pos) <= 10.0f) + { + badPos = true; + break; + } + } + if (!badPos || traiCount > 15) + randPosList.push_back(pos); + } + for (auto _pos : randPosList) + me->CastSpell(_pos, SPELL_RAIN_OF_BRIMSTONE_MISSILE, true); + break; + } + } + } + + void SpellHit(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_SHATTERING_STAR_HIT: + if (me->IsInCombat()) + DoCast(target, SPELL_SHATTERING_STAR_AT, true); + break; + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_INFERNAL_SPIKE_FILTER: + DoCast(target, SPELL_INFERNAL_SPIKE_SUMMON, true); + break; + case SPELL_SHATTERING_STAR_FILTER: + shatteringTargetGUID = target->GetGUID(); + DoCast(target, SPELL_SHATTERING_STAR_MARK, true); + DoCast(target, SPELL_SHATTERING_STAR_SPEED, true); + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (CheckHomeDistToEvade(diff, 62.0f, 6100.85f, -795.70f, 2971.72f)) + return; + + if (felEruptionTimer) + { + if (felEruptionTimer <= diff) + { + felEruptionTimer = 30000; + uint8 felRandOld = felRand; + + while (felRandOld == felRand) + { + felRand = urand(2, 5); + } + ; // SummonCreatureGroupDespawn : groupes d invocation uwow, non porte + me->SummonCreatureGroup(felRand); + } + else + felEruptionTimer -= diff; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BURNING_ARMOR: + DoCastTopAggro(SPELL_BURNING_ARMOR); + events.RescheduleEvent(EVENT_BURNING_ARMOR, 24000); + break; + case EVENT_CRASHING_COMET: + { + if (IsLfrRaid()) + DoCast(SPELL_CRASHING_COMET_LFR_FILTER); + else + DoCast(SPELL_CRASHING_COMET_FILTER); + EntryCheckPredicate pred(NPC_EMBER_STALKER); + summons.DoAction(ACTION_1, pred); + events.RescheduleEvent(EVENT_CRASHING_COMET, 18000); + break; + } + case EVENT_INFERNAL_SPIKE: + DoCast(SPELL_INFERNAL_SPIKE_FILTER); + events.RescheduleEvent(EVENT_INFERNAL_SPIKE, 16000); + break; + case EVENT_SHATTERING_STAR: + if (++shatteringCounter == 4) + shatteringTimer = IsMythicRaid() ? 30000 : 50000; + shatteringTargetGUID.Clear(); + DoCast(SPELL_SHATTERING_STAR_FILTER); + events.RescheduleEvent(EVENT_SHATTERING_STAR, shatteringTimer); + break; + case EVENT_RAIN_OF_BRIMSTONE: + if (++rainBrimstoneCounter == 4) + { + rainBrimstoneCounter = 0; + rainBrimstoneTimer = 68000; + } + else + rainBrimstoneTimer = 60000; + DoCast(SPELL_RAIN_OF_BRIMSTONE_DUMMY); + events.RescheduleEvent(EVENT_RAIN_OF_BRIMSTONE, rainBrimstoneTimer); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//115892 +struct npc_goroth_ember_stalker : public ScriptedAI +{ + npc_goroth_ember_stalker(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + me->SetDisplayId(11686); + } + + void Reset() {} + + void DoAction(int32 const action) override + { + if (IsLfrRaid()) + me->CastSpellDelay(me, SPELL_CRASHING_COMET_LFR_FILTER2, true, 500); + else + me->CastSpellDelay(me, SPELL_CRASHING_COMET_FILTER2, true, 500); + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + auto owner = me->GetAnyOwner(); + if (!owner) + return; + + switch (spell->Id) + { + case SPELL_CRASHING_COMET_LFR_FILTER2: + me->CastSpell(target, SPELL_CRASHING_COMET_LFR_MISSILE, true, nullptr, nullptr, owner->GetGUID()); + break; + case SPELL_CRASHING_COMET_FILTER2: + me->CastSpell(target, SPELL_CRASHING_COMET_MISSILE, true, nullptr, nullptr, owner->GetGUID()); + break; + } + } + + void UpdateAI(uint32 diff) {} +}; + +//116976 +struct npc_goroth_infernal_spike : public ScriptedAI +{ + npc_goroth_infernal_spike(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + bool despawn = false; + + void IsSummonedBy(Unit* summoner) override + { + me->CastSpellDelay(me, SPELL_INFERNAL_SPIKE_AT, false, 500); + } + + void Reset() {} + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (despawn || caster->GetEntry() != NPC_GOROTH) + return; + + if (spell->Id == SPELL_BURNING_ERUPTION_DUMMY || spell->Id == SPELL_RAIN_OF_BRIMSTONE_SPIKE_DESTROY) + DoAction(true); + } + + void OnAreaTriggerDespawn(uint32 spellId, Position pos, bool duration) override + { + if (despawn) + return; + + if (spellId == SPELL_INFERNAL_SPIKE_AT) + DoAction(true); + } + + void DoAction(int32 const action) override + { + if (despawn) + return; + + despawn = true; + + if (IsMythicRaid()) + { + if (auto owner = me->GetAnyOwner()) + owner->CastSpell(me, SPELL_INFERNAL_DETONATION, true); + } + + ; // (PlaySpellVisual positionnel : cosmetique uwow, non porte) + me->DespawnOrUnsummon(1000); + } + + void UpdateAI(uint32 diff) {} +}; + +//117931 +struct npc_goroth_lava_stalker : public ScriptedAI +{ + npc_goroth_lava_stalker(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void IsSummonedBy(Unit* summoner) override + { + me->CastSpellDelay(me, SPELL_FEL_ERUPTION_TELEGRAPH, true, 500); + me->CastSpellDelay(me, SPELL_FEL_PERIODIC_TRIGGER, true, 7000); + } + + void Reset() {} + + void UpdateAI(uint32 diff) {} +}; + +//119950 +struct npc_goroth_brimstone_infernal : public ScriptedAI +{ + npc_goroth_brimstone_infernal(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + instance = me->GetInstanceScript(); + } + + InstanceScript* instance; + uint32 felTimer = 0; + + void IsSummonedBy(Unit* summoner) override + { + me->SetReactState(REACT_AGGRESSIVE, 2000); + felTimer = 12000; //Need correct time! + } + + void Reset() {} + + void EnterCombat(Unit* /*who*/) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + } + + void JustDied(Unit* /*killer*/) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + void UpdateAI(uint32 diff) + { + if (!UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (felTimer) + { + if (felTimer <= diff) + { + felTimer = 15000; //Need correct time! + DoCast(SPELL_FEL_FIRE); + } + else + felTimer -= diff; + } + DoMeleeAttackIfReady(); + } +}; + +//233024 +class spell_tos_goroth_crashing_comet : public SpellScript +{ + PrepareSpellScript(spell_tos_goroth_crashing_comet); + + void FilterTargets(std::list& targets) + { + std::list infernalSpikes; + for (auto target : targets) + { + if (target->GetEntry() == NPC_INFERNAL_SPIKE) + infernalSpikes.push_back(target); + } + + for (auto infernalSpike : infernalSpikes) + { + if (auto spike = ObjectAccessor::GetCreature(*GetCaster(), infernalSpike->GetGUID())) + spike->GetAI()->DoAction(true); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_goroth_crashing_comet::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENTRY); + } +}; + +//234368 +class spell_tos_goroth_fel_eruption_dummy : public SpellScript +{ + PrepareSpellScript(spell_tos_goroth_fel_eruption_dummy); + + void HandleScript(SpellEffIndex /*effectIndex*/) + { + if (!GetCaster() || !GetHitDest()) + return; + + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + if (Creature* goroth = instance->instance->GetCreature(instance->GetGuidData(DATA_GOROTH))) + goroth->CastSpell(GetHitDest()->GetPositionX(), GetHitDest()->GetPositionY(), GetHitDest()->GetPositionZ(), SPELL_FEL_ERUPTION_AT, true); + } + + void Register() override + { + OnEffectLaunch += SpellEffectFn(spell_tos_goroth_fel_eruption_dummy::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +//238588 +class spell_tos_goroth_rain_of_brimstone : public SpellScript +{ + PrepareSpellScript(spell_tos_goroth_rain_of_brimstone); + + uint8 targetCount = 0; + + void FilterTargets(std::list& targets) + { + if (!GetCaster() || !GetExplTargetDest()) + return; + + GetCaster()->CastSpell(GetExplTargetDest()->GetPositionX(), GetExplTargetDest()->GetPositionY(), GetExplTargetDest()->GetPositionZ(), SPELL_RAIN_OF_BRIMSTONE_SPIKE_DESTROY, true); + + if (targets.empty()) + GetCaster()->CastSpell(GetExplTargetDest()->GetPositionX(), GetExplTargetDest()->GetPositionY(), GetExplTargetDest()->GetPositionZ(), SPELL_RAIN_OF_BRIMSTONE_SUMMON, true); + else + targetCount = targets.size(); + } + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + if (targetCount) + SetHitDamage(GetHitDamage() / targetCount); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_goroth_rain_of_brimstone::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_tos_goroth_rain_of_brimstone::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +//237333 +class spell_tos_goroth_energy_tracker : public AuraScript +{ + PrepareAuraScript(spell_tos_goroth_energy_tracker); + + uint8 powerTick = 0; + + void OnTick(AuraEffect const* aurEff) + { + auto caster = GetCaster()->ToCreature(); + if (!caster || !caster->IsInCombat()) + return; + + auto powerCount = caster->GetPower(POWER_ENERGY); + if (powerCount < 100) + { + if (powerTick < 8) + { + ++powerTick; + + if (aurEff->GetTickNumber() == 1) + caster->SetPower(POWER_ENERGY, powerCount + 1); + else + caster->SetPower(POWER_ENERGY, powerCount + 2); + } + else + { + powerTick = 0; + caster->SetPower(POWER_ENERGY, powerCount + 1); + } + } + else if (!caster->HasUnitState(UNIT_STATE_CASTING)) + { + caster->CastSpell(caster, SPELL_INFERNAL_BURNING, false); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_goroth_energy_tracker::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +//234386 +class spell_tos_goroth_fel_periodic_trigger : public AuraScript +{ + PrepareAuraScript(spell_tos_goroth_fel_periodic_trigger); + + void OnTick(AuraEffect const* aurEff) + { + PreventDefaultAction(); + + if (auto caster = GetCaster()) + { + Position pos; + float angle = caster->GetRelativeAngle(centrPos.GetPositionX(), centrPos.GetPositionY()) + frand(-1.0f, 1.0f); + pos = caster->GetNearPosition(frand(35.0f, 45.0f), angle); + caster->CastSpell(pos, SPELL_FEL_ERUPTION_MISSILE, true); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_goroth_fel_periodic_trigger::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; + +//955600 +struct at_goroth_infernal_spike_los_blocker : AreaTriggerAI +{ + at_goroth_infernal_spike_los_blocker(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + if (caster && target && target->IsPlayer()) + { + if (auto instance = target->GetInstanceScript()) + { + if (auto goroth = ObjectAccessor::GetCreature(*target, instance->GetGuidData(DATA_GOROTH))) + { + if (at->IsInBetween(target, goroth, 2.5f)) + { + if (!target->HasAura(SPELL_INFERNAL_SPIKE_PROTECTION)) + target->CastSpell(target, SPELL_INFERNAL_SPIKE_PROTECTION, true, nullptr, nullptr, caster->GetGUID()); + } + else if (auto aura = target->GetAura(SPELL_INFERNAL_SPIKE_PROTECTION, caster->GetGUID())) + aura->Remove(); + } + } + } + return true; + } +}; + +//13412 +struct at_goroth_shattering_star : AreaTriggerAI +{ + explicit at_goroth_shattering_star(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + uint8 infernalSpikeTouched = 1; + + void OnUnitEnter(Unit* unit) override + { + auto caster = at->GetCaster(); + if (unit->GetEntry() == NPC_INFERNAL_SPIKE) + { + ++infernalSpikeTouched; + + if (unit->IsAIEnabled) + unit->GetAI()->DoAction(true); + + //Achievement + if (caster->IsAIEnabled) + caster->GetAI()->DoAction(ACTION_2); + } + } + + void OnDestinationReached() override + { + auto caster = at->GetCaster(); + if (!caster) + { + at->Remove(); + return; + } + + if (auto spellInfo = sSpellMgr->GetSpellInfo(SPELL_SHATTERING_NOVA)) + { + if (auto effInfo = spellInfo->GetEffect(EFFECT_0, caster->GetMap()->GetDifficultyID())) + { + float damage = effInfo->BasePoints / std::max(infernalSpikeTouched, uint8(1)); + SpellCastTargets targets; + ; // (SetCaster : le caster est fourni au CastSpell, inutile chez nous) + targets.SetDst(at->GetPosition()); + CustomSpellValues values; + values.AddSpellMod(SPELLVALUE_BASE_POINT0, damage); + + caster->CastSpell(targets, spellInfo, &values, TriggerCastFlags(TRIGGERED_FULL_MASK)); + } + } + if (infernalSpikeTouched < 2 && caster->IsAIEnabled) + caster->GetAI()->DoAction(ACTION_1); + } +}; + +//13526 +struct at_goroth_fel_pool : AreaTriggerAI +{ + explicit at_goroth_fel_pool(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + void OnUnitEnter(Unit* unit) override + { + if (unit->GetEntry() == NPC_INFERNAL_SPIKE) + unit->GetAI()->DoAction(true); + } +}; + +//36337 +class achievement_fel_turkey : public AchievementCriteriaScript +{ +public: + achievement_fel_turkey() : AchievementCriteriaScript("achievement_fel_turkey") {} + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (auto boss = target->ToCreature()) + if (boss->IsAIEnabled && boss->AI()->GetData(DATA_SPIKES_COUNTER) >= 30) + return true; + + return false; + } +}; + +void AddSC_boss_goroth() +{ + RegisterCreatureAI(boss_goroth); + RegisterCreatureAI(npc_goroth_ember_stalker); + RegisterCreatureAI(npc_goroth_infernal_spike); + RegisterCreatureAI(npc_goroth_lava_stalker); + RegisterCreatureAI(npc_goroth_brimstone_infernal); + RegisterSpellScript(spell_tos_goroth_crashing_comet); + RegisterSpellScript(spell_tos_goroth_fel_eruption_dummy); + RegisterSpellScript(spell_tos_goroth_rain_of_brimstone); + RegisterAuraScript(spell_tos_goroth_energy_tracker); + RegisterAuraScript(spell_tos_goroth_fel_periodic_trigger); + RegisterAreaTriggerAI(at_goroth_infernal_spike_los_blocker); + RegisterAreaTriggerAI(at_goroth_shattering_star); + RegisterAreaTriggerAI(at_goroth_fel_pool); + new achievement_fel_turkey(); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Harjatan.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Harjatan.cpp new file mode 100644 index 0000000..cd2d644 --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Harjatan.cpp @@ -0,0 +1,766 @@ +/* +https://uwow.biz/ +*/ + +#include "tomb_of_sargeras.h" + +enum Says +{ + SAY_AGGRO = 0, + SAY_CALL = 1, + SAY_DRAW_IN = 2, + SAY_DEATH = 3, +}; + +enum Spells +{ + SPELL_DRAW_IN = 232061, // + SPELL_FRIGID_BLOWS = 233429, // + SPELL_DRENCHING_SLOUGH = 233526, // dummy + SPELL_FROSTY_DISCHARGE = 232174, // at end link + + SPELL_UNCHECKED_RAGE = 231854, // 100% + SPELL_UNCHECKED_RAGE_VICTIM = 247403, // + SPELL_CALL_ADDS = 232192, // + + SPELL_CANCEL_DRENCHED = 239926, // link + SPELL_CHANGE_ENERGY = 241486, // + SPELL_REGENERATE_ENERGY = 232379, // + + SPELL_ENQUBATION = 240347, + SPELL_HARDENED_SHELL = 240315, + + SPELL_ENQUBATION_RED = 242027, + SPELL_ENQUBATION_BLUE = 242030, + SPELL_ENQUBATION_GREEN = 242031, + + SPELL_ENRAGE = 64238, // + + SPELL_JAGGED_ABRASION = 231998, + + //achievement npc spells + SPELL_SONIC_WAVE = 241436, + SPELL_SPLASH = 241393, + SPELL_ELDERS_RAGE = 241416, +}; + +enum eEvents +{ + EVENT_CHECK_ENERGY = 1, + EVENT_CALL_ADDS, + EVENT_DRAW_IN, + EVENT_HATCHING, + EVENT_HATCHING_CLEAN_UP, + + EVENT_ENRAGE, + + EVENT_ELDER1, + EVENT_ELDER2, + EVENT_ELDER3, +}; + +enum Misc +{ + DATA_COMPLETE, +}; + +enum Action +{ + ACTION_COMPLETE, +}; + +Position const addsPos[4] +{ + {5877.17f, -906.50f, 2920.25f, 6.20f}, + {5880.17f, -896.95f, 2920.35f, 5.73f}, + {5889.86f, -886.83f, 2920.35f, 5.42f}, + {5946.16f, -919.47f, 2919.52f, 2.62f} + +}; + +Position const eggsPos[4] +{ + {5915.40f, -876.02f, 2919.83f, 0.0f}, + {5944.42f, -900.28f, 2919.46f, 0.0f}, + {5918.47f, -945.27f, 2919.86f, 0.0f}, + {5882.26f, -920.99f, 2920.99f, 0.0f} +}; + +uint32 const eggsCasts[3] +{ + SPELL_ENQUBATION_BLUE, + SPELL_ENQUBATION_RED, + SPELL_ENQUBATION_GREEN +}; + +// 116407 +class boss_harjatan : public CreatureScript +{ +public: + boss_harjatan() : CreatureScript("boss_harjatan") {} + + struct boss_harjatanAI : BossAI + { + boss_harjatanAI(Creature* creature) : BossAI(creature, DATA_HARJATAN), achievecomplete(false) + { + me->SetReactState(REACT_DEFENSIVE); + achievecomplete = false; + } + + std::map> eggs{}; + bool achievecomplete; + + void Reset() override + { + if (me->IsAlive()) + { + _Reset(); + achievecomplete = false; + + if (Creature* elder = instance->instance->GetCreature(instance->GetGuidData(NPC_ELDER))) + if (!elder->IsAlive()) + elder->Respawn(); + } + + eggs.clear(); + + instance->DoRemoveAurasDueToSpellOnPlayers(231770); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_JAGGED_ABRASION); + + std::list list; + me->GetAreatriggerListInRange(list, 80.0f); + list.remove_if([](AreaTrigger* at1) { AreaTriggerTemplate const* t1 = at1->GetTemplate(); return !t1 || t1->Id != 13251; }); + for (std::list::const_iterator itr = list.begin(); itr != list.end(); itr++) + (*itr)->RemoveFromWorld(); + if (IsMythicRaid()) + { + for (uint8 j = 0; j < 4; ++j) + { + const auto& pos = eggsPos[j]; + for (uint8 i = 0; i < 5; ++i) + if (Creature* add = me->SummonCreature(NPC_EGGS, pos.GetPositionX() + frand(-4, 4), pos.GetPositionY() + frand(-4, 4), pos.GetPositionZ(), 0.0f)) + { + add->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NOT_ATTACKABLE_1); + add->CastSpell(add, SPELL_HARDENED_SHELL); + eggs[j].push_back(add->GetGUID()); + } + } + } + } + + void DoAction(int32 const action) + { + if (action == ACTION_COMPLETE) + achievecomplete = true; + } + + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_COMPLETE: + return achievecomplete ? 1 : 0; + } + return 0; + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + + Talk(SAY_AGGRO); + + me->SetPower(POWER_ENERGY, 0); + DoCast(SPELL_REGENERATE_ENERGY); + events.RescheduleEvent(EVENT_CHECK_ENERGY, 500); + + events.RescheduleEvent(EVENT_ENRAGE, IsMythicRaid() ? 360000 : 480000); + events.RescheduleEvent(EVENT_DRAW_IN, 58000); + events.RescheduleEvent(EVENT_CALL_ADDS, 17000); + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_HATCHING, 30000); + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + _JustDied(); + Reset(); + } + + void JustSummoned(Creature* creature) override + { + switch(creature->GetEntry()) + { + case NPC_EGGS: + creature->SetReactState(REACT_ATTACK_OFF); + creature->SetControlled(true, UNIT_STATE_ROOT); + break; + case NPC_TADPOLE_3: + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0, true)) + { + creature->SetTarget(target->GetGUID()); + creature->AI()->DoAction(true); + } + break; + } + } + + BossAI::JustSummoned(creature); + } + + void SpellFinishCast(SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_UNCHECKED_RAGE: + DoCastVictim(SPELL_UNCHECKED_RAGE_VICTIM, true); + for (const auto& vector : eggs) + for (const auto& guid : vector.second) + if (Creature* egg = instance->instance->GetCreature(guid)) + if (me->isInFront(egg) && me->GetDistance(egg) <= 20.0f) + egg->RemoveAurasDueToSpell(SPELL_HARDENED_SHELL); + break; + case SPELL_CALL_ADDS: + for (uint8 i = 0; i < 4; ++i) + me->SummonCreature(i < 2 ? 117596 : 116569, addsPos[i]); + break; + case SPELL_FROSTY_DISCHARGE: + events.RescheduleEvent(EVENT_DRAW_IN, 59000); + events.RescheduleEvent(EVENT_CALL_ADDS, 17000); + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_HATCHING, 30000); + break; + } + } + + void OnApplyOrRemoveAura(uint32 spellId, AuraRemoveMode /*mode*/, bool apply) override + { + switch (spellId) + { + case SPELL_FRIGID_BLOWS: + if (apply) + return; + + me->SetPower(me->GetPowerType(), 0); + DoCast(SPELL_FROSTY_DISCHARGE); + break; + case SPELL_DRAW_IN: + if (apply) + return; + if (!me->HasAura(SPELL_FRIGID_BLOWS)) + { + DoCast(SPELL_FROSTY_DISCHARGE); + me->SetPower(me->GetPowerType(), 0); + } + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (CheckHomeDistToEvade(diff, 50.0f)) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_CHECK_ENERGY: + if (me->GetPower(POWER_ENERGY) >= 100) + DoCast(SPELL_UNCHECKED_RAGE); + events.RescheduleEvent(EVENT_CHECK_ENERGY, 500); + break; + case EVENT_CALL_ADDS: + Talk(SAY_CALL); + DoCast(SPELL_CALL_ADDS); + events.RescheduleEvent(EVENT_CALL_ADDS, 36000); + break; + case EVENT_DRAW_IN: + Talk(SAY_DRAW_IN); + DoCast(SPELL_DRAW_IN); + break; + case EVENT_HATCHING: + { + DoCast(SPELL_ENQUBATION); + + std::vector eggsIndexes{ 0, 1, 2, 3 }; + std::list eggsColors{ 0, 1, 2 }; + + Trinity::Containers::RandomResize(eggsIndexes, 2); + Trinity::Containers::RandomResize(eggsColors, 2); + + for (auto i : eggsIndexes) + { + uint8 color = eggsColors.front(); + eggsColors.pop_front(); + if (eggsCasts[color] == SPELL_ENQUBATION_GREEN) + { + for (const auto& guid : eggs[i]) + if (Creature* egg = instance->instance->GetCreature(guid)) + { + if (!egg->IsAlive()) + egg->Respawn(true); + egg->AI()->EnterCombat(me->GetVictim()); + egg->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_ATTACKABLE_1); + egg->CastSpell(egg, SPELL_HARDENED_SHELL); + egg->CastSpellDelay(egg, eggsCasts[color], false, 100); + } + } + else + if (Creature* egg = instance->instance->GetCreature(eggs[i][urand(0, eggs[i].size())])) + { + if (!egg->IsAlive()) + egg->Respawn(true); + egg->AI()->EnterCombat(me->GetVictim()); + egg->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_ATTACKABLE_1); + egg->CastSpell(egg, SPELL_HARDENED_SHELL); + egg->CastSpellDelay(egg, eggsCasts[color], false, 100); + } + } + + events.RescheduleEvent(EVENT_HATCHING_CLEAN_UP, 28000); + break; + } + case EVENT_HATCHING_CLEAN_UP: + { + for (const auto& vector : eggs) + for (const auto& guid : vector.second) + if (Creature* egg = instance->instance->GetCreature(guid)) + { + if (!egg->IsAlive()) + egg->Respawn(true); + else + egg->SetHealth(egg->GetMaxHealth()); + + for (const auto& spell : eggsCasts) + egg->RemoveAurasDueToSpell(spell); + egg->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NOT_ATTACKABLE_1); + + } + break; + } + case EVENT_ENRAGE: + DoCast(SPELL_ENRAGE); + break; + } + } + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new boss_harjatanAI(creature); + } +}; + + +// 121184 +class npc_mistress_sasszine : public CreatureScript +{ +public: + npc_mistress_sasszine() : CreatureScript("npc_mistress_sasszine") {} + + struct npc_mistress_sasszineAI : ScriptedAI + { + npc_mistress_sasszineAI(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + SetCanSeeEvenInPassiveMode(true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + } + + bool _introDone = false; + uint32 introAdds = 0; + + void DoAction(const int32) override + { + if (!introAdds) + introAdds = 1; + + switch (--introAdds) + { + case 1: + Talk(2); + break; + case 0: + me->CastSpell(me, 241744); + + me->CastSpellDelay(me, 241680, false, 11000); + me->DespawnOrUnsummon(13500); + break; + } + } + + void MoveInLineOfSight(Unit* who) override + { + if (!who->IsPlayer()) + return; + + if (me->GetDistance(who) < 100.0f && !_introDone) + { + _introDone = true; + InstanceScript* instance = me->GetInstanceScript(); + if (!instance) + return; + + if (instance->GetBossState(DATA_HARJATAN) != NOT_STARTED) + { + me->DespawnOrUnsummon(); + return; + } + + Talk(0); + + for (auto id : { NPC_EVENT_1, NPC_EVENT_2 }) + if (Creature* add = instance->instance->GetCreature(instance->GetGuidData(id))) + if (add->IsAlive()) + ++introAdds; + + if (introAdds) + return; + + me->CastSpellDelay(me, 241744, false, 11000); + me->CastSpellDelay(me, 241680, false, 21000); + me->DespawnOrUnsummon(23500); + } + } + + void UpdateAI(uint32 diff) override {} + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_mistress_sasszineAI(creature); + } +}; + +// 121155 +struct npc_tos_tadpole : ScriptedAI +{ + npc_tos_tadpole(Creature* creature) : ScriptedAI(creature) {} + + uint32 timer; + bool canDoEvent = false; + + void DoAction(const int32) override + { + me->SetReactState(REACT_PASSIVE); + + me->AddDelayedEvent(1500, [this]()-> void + { + if (Unit* target = me->GetVictim()) + { + me->GetMotionMaster()->MoveFollow(target, 0, 0); + me->CastSpell(target, 241600); + } + + timer = 1000; + canDoEvent = true; + }); + } + + void JustDied(Unit*) override + { + DoCast(me, 241587, true); + } + + void UpdateAI(uint32 diff) override + { + if (canDoEvent) + { + if (timer <= diff) + { + if (Unit* target = me->GetVictim()) + { + if (me->GetDistance2d(target) <= 2.2f) + { + DoCast(241603); + me->DespawnOrUnsummon(100); + } + else + me->GetMotionMaster()->MoveFollow(target, 0, 0); + } + else + me->DespawnOrUnsummon(); + timer = 1000; + } + else + timer -= diff; + } + } +}; + +// 121071 achievement npc +class npc_elder_murk_eye : public CreatureScript +{ +public: + npc_elder_murk_eye() : CreatureScript("npc_elder_murk_eye") {} + + struct npc_elder_murk_eyeAI : ScriptedAI + { + npc_elder_murk_eyeAI(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + me->SetVisible(false); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + instance = me->GetInstanceScript(); + } + + EventMap events; + + InstanceScript* instance; + + uint32 CheckTimer; + uint32 EvadeTimer; + Position const jumppoint = { 5892.11f, -898.21f, 2919.83f, 5.7f }; + + + void Initialize() + { + CheckTimer = 2000; + EvadeTimer = 5000; + } + + void Reset() override + { + events.Reset(); + me->SetReactState(REACT_PASSIVE); + me->SetVisible(false); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + me->RemoveAura(241416); + me->RemoveAura(241393); + } + + void EnterCombat(Unit* /*who*/) override + { + events.RescheduleEvent(EVENT_ELDER1, 6000); //wave + events.RescheduleEvent(EVENT_ELDER2, 8000); //splash + events.RescheduleEvent(EVENT_ELDER3, 25000); //rage + } + + void UpdateAI(uint32 diff) override + { + if ((!me->IsInCombat()) && (instance->GetBossState(DATA_HARJATAN) == FAIL) || (!me->IsInCombat()) && (instance->GetBossState(DATA_HARJATAN) == NOT_STARTED)) + { + if (EvadeTimer <= diff) + { + EnterEvadeMode(); + + EvadeTimer = 5000; + } + + else EvadeTimer -= diff; + } + + if ((!me->IsInCombat()) && (instance->GetBossState(DATA_HARJATAN) == IN_PROGRESS)) + { + if (CheckTimer <= diff) + { + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()) + if (player->HasAura(232454)) + { + me->SetVisible(true); + me->GetMotionMaster()->MoveJump(jumppoint, 10.0f, 10.0f); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + me->SetReactState(REACT_AGGRESSIVE); + break; + } + CheckTimer = 2000; + + } + else CheckTimer -= diff; + } + + if (!UpdateVictim()) + return; + + events.Update(diff); + + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ELDER1: + DoCast(SPELL_SONIC_WAVE); + events.RescheduleEvent(EVENT_ELDER1, 6000); + break; + case EVENT_ELDER2: + DoCast(SPELL_SPLASH); + events.RescheduleEvent(EVENT_ELDER2, 20000); + break; + case EVENT_ELDER3: + DoCast(SPELL_ELDERS_RAGE); + events.RescheduleEvent(EVENT_ELDER3, 45000); + break; + } + } + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + if (Creature* Harjatan = instance->instance->GetCreature(instance->GetGuidData(NPC_HARJATAN))) + { + Harjatan->AI()->DoAction(ACTION_COMPLETE); + } + instance->DoRemoveAurasDueToSpellOnPlayers(231770); + + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_elder_murk_eyeAI(creature); + } +}; + + +// 231854 +class spell_tos_unchecked_rage : public SpellScriptLoader +{ +public: + spell_tos_unchecked_rage() : SpellScriptLoader("spell_tos_unchecked_rage") { } + + class spell_tos_unchecked_rage_SpellScript : public SpellScript + { + PrepareSpellScript(spell_tos_unchecked_rage_SpellScript); + + void CorrectTargets(std::list& targets) + { + if (!GetCaster()) + return; + + auto caster = GetCaster(); + + if (targets.size() < 5) + caster->CastSpell(caster, 247782, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_unchecked_rage_SpellScript::CorrectTargets, SpellEffIndex::EFFECT_0, Targets::TARGET_UNIT_CONE_ENEMY_104); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_tos_unchecked_rage_SpellScript(); + }; +}; + +// 234016 +class spell_tos_fixate : public AuraScript +{ + PrepareAuraScript(spell_tos_fixate); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + Unit* caster = GetCaster(); + if (!target || !caster) + return; + + caster->AddAura(234128, caster); + + caster->AddThreat(target, std::numeric_limits::max()); + caster->TauntApply(target); + } + + void OnRemove(AuraEffect const* /*auraEffect*/, AuraEffectHandleModes /*mode*/) + { + if (GetCaster() && GetTarget()) + GetCaster()->TauntFadeOut(GetTarget()); + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_tos_fixate::OnApply, EFFECT_0, SPELL_AURA_FIXATE, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_tos_fixate::OnRemove, SpellEffIndex::EFFECT_0, SPELL_AURA_FIXATE, AuraEffectHandleModes::AURA_EFFECT_HANDLE_REAL); + + } +}; + + +// 232061 +class spell_tos_draw_in : public AuraScript +{ + PrepareAuraScript(spell_tos_draw_in); + + uint8 counter = 0; + + void OnTick(AuraEffect const* /*aurEff*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + + std::list list; + caster->GetAreatriggerListInRange(list, 80.0f); + list.remove_if([](AreaTrigger* at1) { AreaTriggerTemplate const* t1 = at1->GetTemplate(); return !t1 || t1->Id != 13251; }); + uint8 realCount = 0; + for (std::list::const_iterator itr = list.begin(); itr != list.end(); itr++) + { + if ((*itr)->GetDuration() <= 9000) + continue; + + // adaptation : notre API de creation manuelle d AT differe, on recaste le sort + // createur (231754) a la meme position pour regenerer la zone avec sa duree fraiche + caster->CastSpell((*itr)->GetPositionX(), (*itr)->GetPositionY(), (*itr)->GetPositionZ(), 231754, true); + + (*itr)->RemoveFromWorld(); + ++realCount; + } + + for (uint8 i = 0; i < realCount; ++i) + caster->CastSpellDelay(caster, SPELL_FRIGID_BLOWS, true, (1 + i) * GetDuration() / realCount); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_draw_in::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +class achievement_grand_fin_ale : public AchievementCriteriaScript +{ +public: + achievement_grand_fin_ale() : AchievementCriteriaScript("achievement_grand_fin_ale") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* boss = target->ToCreature()) + if (boss->AI()->GetData(DATA_COMPLETE)) + return true; + + return false; + } +}; + +void AddSC_boss_harjatan() +{ + new boss_harjatan(); + new npc_mistress_sasszine(); + new npc_elder_murk_eye(); + new achievement_grand_fin_ale(); + RegisterCreatureAI(npc_tos_tadpole); + new spell_tos_unchecked_rage(); + RegisterAuraScript(spell_tos_fixate); + RegisterAuraScript(spell_tos_draw_in); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Kiljaeden.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Kiljaeden.cpp new file mode 100644 index 0000000..8e26766 --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_Kiljaeden.cpp @@ -0,0 +1,1797 @@ +/* +https://uwow.biz/ +*/ + +#include "AreaTriggerAI.h" +#include "tomb_of_sargeras.h" + +enum Says +{ + SAY_ILLIDAN_STAGE_4 = 0, + + SAY_AGGRO = 0, + SAY_ARMAGEDDON = 1, + SAY_WARN_ARMAGEDDON = 2, + SAY_REFLECTION_ERUPTING = 3, + SAY_RUPTURING_SINGULARITY = 4, + SAY_WARN_RUPTURING_SINGULARITY = 5, + SAY_STAGE_2_CHANGE = 6, + SAY_FLY_POINT_1 = 7, + SAY_BURSTING_DREADFLAME = 8, + SAY_FOCUSED_DREADFLAME = 9, + SAY_WARN_FOCUSED_DREADFLAME = 10, + SAY_WAILING = 11, + SAY_HOPELESS = 12, + SAY_STAGE_4_CHANGE = 13, + SAY_DECEIVER_VEIL_FINISH_CAST = 14, + SAY_STAGE_5 = 15, + SAY_TEAR_RIFT = 16, + SAY_DARKNESS_THOUSAND_SOULS = 17, + SAY_FLAMING_ORB = 18, +}; + +enum Spells +{ + SPELL_KILJAEDEN_BONUS_ROLL = 240662, + SPELL_BERSERK = 47008, + SPELL_FELCLAWS = 239932, + SPELL_RUPTURING_SINGULARITY = 235059, + SPELL_ARMAGEDDON_DUMMY = 240910, + SPELL_ARMAGEDDON_RAIN_AT = 234295, + SPELL_ARMAGEDDON_RAIN_SELF = 234309, + SPELL_ARMAGEDDON_RAIN_DMG = 234310, + SPELL_ARMAGEDDON_RAIN_VISUAL = 240832, + SPELL_ARMAGEDDON_HAIL_AT = 240911, + SPELL_ARMAGEDDON_HAIL_SELF = 240912, + SPELL_ARMAGEDDON_HAIL_VISUAL = 240915, + SPELL_ARMAGEDDON_HAIL_DMG = 240916, + SPELL_ARMAGEDDON_BLAST = 240908, + SPELL_BURSTING_DREADFLAME_FILTER = 238429, + SPELL_FOCUSED_DREADFLAME_FILTER = 238505, + SPELL_FOCUSED_DREADFLAME_DMG = 238502, + SPELL_FOCUSED_DREADBURST = 238503, //Heroc+ + SPELL_SHADOW_REFLECTION_ERUPTING = 236710, + SPELL_SHADOW_REFLECTION_WAILING = 236378, //Mythic + SPELL_SHADOW_REFLECTION_HOPELESS = 237590, //Mythic + SPELL_SUM_SHADOW_REFLECTION_ERUPTING = 242032, + SPELL_ERUPTING_REFLECTION_CLONE = 236711, + SPELL_DARKNESS_THOUSAND_SOULS = 238999, + SPELL_DARKNESS_THOUSAND_SOULS_DMG = 239112, + SPELL_DARKNESS_THOUSAND_SOULS_DUMMY = 239129, + SPELL_DARKNESS_THOUSAND_SOULS_AURA = 239216, + SPELL_TEAR_RIFT = 243982, + SPELL_TEAR_RIFT_AT = 239130, + SPELL_GRAVITY_SQUEEZE_DUMMY = 239154, + SPELL_GRAVITY_SQUEEZE_DOT = 239155, + SPELL_FLAMING_ORB_FILTER = 244856, + + SPELL_KILJAEDEN_PLAYERS_TELEPOT = 244719, + SPELL_KILJAEDEN_PLAY_MOVIE = 246240, + + //Change phase + SPELL_WINGSPAN_SOUND = 242377, + SPELL_NETHER_GALE = 244834, + + SPELL_DECEIVER_VEIL_AT = 241983, + SPELL_DECEIVER_VEIL_1 = 236555, //Decrease Hit chance + SPELL_DECEIVER_VEIL_2 = 242696, //Hit ok + SPELL_CURSED_SIGHT = 241768, + SPELL_ILLIDAN_SIGHTLESS_GAZE = 241721, + + //Erupting Reflection + SPELL_ERUPTING_DREADFLAME = 235120, + SPELL_LINGERING_ERUPTION = 243536, //Mythic + SPELL_LATENT_ERUPTION = 245014, + SPELL_ERUPTING_ORB_AT = 243542, //Dmg 245017 + + //Wailing Reflection + SPELL_SORROWFUL_WAIL_AT = 241564, + SPELL_SORROWFUL_WAIL_DMG = 236381, + SPELL_LINGERING_WAIL = 243624, + SPELL_WAILING_GRASP_FILTER = 243625, + SPELL_WAILING_GRASP_JUMP = 243626, + + //Hopeless Reflection + SPELL_HOPELESSNESS_DUMMY = 237725, + SPELL_HOPELESSNESS_AT = 237728, + SPELL_LINGERING_HOPE = 243621, + + //Shadowsoul + SPELL_SPAWN_VISUAL = 241905, + SPELL_DESTABILIZED_SHADOWSOUL_DMG = 241702, + SPELL_DESTABILIZED_SHADOWSOUL_TP = 242074, + + //Demonic Obelisk + SPELL_DEMONIC_OBELISK_VISUAL = 239785, + SPELL_DEMONIC_OBELISK_DMG = 239852, + + //Nether Rift + SPELL_TEAR_RIFT_VISUAL = 240020, + + //Flaming Orb + SPELL_FLAMING_ORB_AT = 239256, + SPELL_ORG_FIXATE = 239280, + SPELL_FLAMING_DETONATION = 239267, +}; + +enum eEvents +{ + EVENT_FELCLAWS = 1, + EVENT_RUPTURING_SINGULARITY = 2, + EVENT_ARMAGEDDON = 3, + EVENT_BURSTING_DREADFLAME = 4, + EVENT_FOCUSED_DREADFLAME = 5, + EVENT_SHADOW_REFLECTION_ERUPTING = 6, + EVENT_DECEIVER_VEIL = 7, + EVENT_DARKNESS_THOUSAND_SOULS = 8, + EVENT_DEMONIC_OBELISK = 9, + EVENT_TEAR_RIFT = 10, + EVENT_FLAMING_ORB = 11, + EVENT_SHADOW_REFLECTION_WAILING = 12, //Heroic+ + EVENT_SHADOW_REFLECTION_HOPELESS = 13, //Mythic +}; + +enum Stages +{ + S1_P1 = 1, + S2_CHANGE = 2, + S3_P2 = 3, + S4_CHANGE = 4, + S5_P3 = 5 +}; + +enum eAnim +{ + //Illidan + ILLIDAN_ANIM_1 = 12447, //SMSG_PLAY_ONE_SHOT_ANIM_KIT + ILLIDAN_ANIM_2 = 12448, + OBELISK_ANIM_1 = 4396, + OBELISK_ANIM_2 = 8216, +}; + +enum eMisc +{ + DATA_HOPELESS_HEALTH_EVENT = 1, + DATA_ACHIEVEMENT +}; + +Position const centrPos = { 4500.39f, -1510.12f, 5385.64f }; + +Position const singularPos[5] = +{ + {4538.57f, -1548.27f, 5385.64f}, + {4462.66f, -1547.71f, 5385.64f}, + {4462.46f, -1472.97f, 5385.64f}, + {4538.33f, -1472.74f, 5385.64f}, + {4500.39f, -1510.12f, 5385.64f} //Center +}; + +Position const flyPos[5] = +{ + {4552.45f, -1458.32f, 5395.64f}, + {4552.29f, -1562.31f, 5395.64f}, + {4448.31f, -1561.16f, 5395.64f}, + {4445.37f, -1457.75f, 5395.64f}, + {4500.39f, -1510.12f, 5395.64f} //Center +}; + +Position const riftPos[4] = +{ + {4472.49f, -1449.58f, 5385.99f}, + {4528.95f, -1450.75f, 5385.99f}, + {4532.03f, -1568.90f, 5385.99f}, + {4469.95f, -1570.39f, 5385.99f} +}; + +//117269 +struct boss_tos_kiljaeden : BossAI +{ + explicit boss_tos_kiljaeden(Creature* creature) : BossAI(creature, DATA_KILJAEDEN) {} + + bool flymove = false; + bool darknessThousandEvent = false; + bool achievement = true; + uint8 stage = 0; + uint8 sungularRand = 0; + uint8 healthPct = 0; + uint8 shadowsoulDiedCount = 0; + uint8 obeliskCount = 0; + uint8 obeliskIdx = 0; + uint8 flamingOrbIdx = 0; + uint8 armageddonCount = 0; + uint32 wailingTimers = 0; + uint32 berserkTimer = 0; + std::vector randList; + std::map> singularityTime; + std::map> armageddonTime; + std::map> burstingDreadflameTime; + std::map> focusedDreadFlameTime; + + void Reset() override + { + _Reset(); + flymove = false; + darknessThousandEvent = false; + achievement = true; + stage = 0; + sungularRand = 0; + healthPct = 81; + shadowsoulDiedCount = 0; + obeliskCount = IsHeroicPlusRaid() ? 3 : 1; + obeliskIdx = 0; + flamingOrbIdx = 0; + berserkTimer = (IsMythicRaid() ? 840 : 600) * IN_MILLISECONDS; + randList = { 0, 1, 2, 3}; + Trinity::Containers::RandomResize(randList, 2); + } + + void EnterCombat(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _EnterCombat(); + + if (IsMythicRaid()) + { + armageddonCount = 8; + } + else if (me->GetMap()->GetPlayersCountExceptGMs() <= 15) + { + if (IsHeroicRaid()) + armageddonCount = 5; + else if (IsNormalRaid()) + armageddonCount = 4; + else if (IsLfrRaid()) + armageddonCount = 3; + } + else if (me->GetMap()->GetPlayersCountExceptGMs() <= 20) + { + if (IsHeroicRaid()) + armageddonCount = 6; + else if (IsNormalRaid()) + armageddonCount = 5; + else if (IsLfrRaid()) + armageddonCount = 4; + } + else if (me->GetMap()->GetPlayersCountExceptGMs() <= 25) + { + if (IsHeroicRaid()) + armageddonCount = 7; + else if (IsNormalRaid()) + armageddonCount = 6; + else if (IsLfrRaid()) + armageddonCount = 4; + } + else if (me->GetMap()->GetPlayersCountExceptGMs() <= 30) + { + if (IsHeroicRaid()) + armageddonCount = 8; + else if (IsNormalRaid()) + armageddonCount = 7; + else if (IsLfrRaid()) + armageddonCount = 6; + } + DefaultEvents(); + } + + void EnterEvadeMode() + { + BossAI::EnterEvadeMode(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + SetFlyMode(false); + me->NearTeleportTo(me->GetHomePosition()); + me->SetReactState(REACT_AGGRESSIVE); + RemoveDebuff(); + } + + void RemoveDebuff() + { + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DARKNESS_THOUSAND_SOULS_AURA); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DECEIVER_VEIL_1); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DECEIVER_VEIL_2); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ILLIDAN_SIGHTLESS_GAZE); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + RemoveDebuff(); + DoCast(me, SPELL_KILJAEDEN_PLAY_MOVIE, true); + me->SetLootRecipient(nullptr); + me->GetMap()->LoadGrid(-254.31f, 5950.06f); + + instance->instance->ApplyOnEveryPlayer([&](Player* player) + { + if ((me->getThreatManager().getThreat(player) > 0.0f)) + me->CastSpell(player, SPELL_KILJAEDEN_BONUS_ROLL, true); + + player->CastSpell(player, SPELL_KILJAEDEN_PLAYERS_TELEPOT, true); + }); + + if (auto chest = instance->instance->GetGameObject(instance->GetGuidData(GO_KILJAEDEN_CHEST))) + chest->SetRespawnTime(86400); + } + + void DefaultEvents() + { + ++stage; + events.Reset(); + + if (stage == S2_CHANGE || stage == S4_CHANGE) + return; + + if (IsLfrRaid()) + { + armageddonTime[S1_P1] = {10000, 22000, 42000, 22000, 30000}; + armageddonTime[S3_P2] = {56000, 28000, 56000, 26000, 12000, 20000}; + burstingDreadflameTime[S3_P2] = {58000, 53000, 61000, 57000}; + burstingDreadflameTime[S5_P3] = {42000, 25000, 70000}; + } + else if (IsMythicRaid()) + { + singularityTime[S1_P1] = {55000, 25000, 25000, 28000}; + singularityTime[S3_P2] = {22000, 50000, 67000, 78000, 84000}; + armageddonTime[S1_P1] = {11000, 54000, 38000}; + armageddonTime[S3_P2] = {18500, 32000, 45000, 33000, 36000, 36000, 47000, 32000, 45000}; + burstingDreadflameTime[S3_P2] = {52500, 50000, 45000, 48000, 86000, 50000}; + burstingDreadflameTime[S5_P3] = {30000, 52000, 43000}; + focusedDreadFlameTime[S3_P2] = {30000, 44000, 47000, 138000, 44000}; + focusedDreadFlameTime[S5_P3] = {48000, 36000, 59000}; + } + else if (IsHeroicRaid()) + { + singularityTime[S1_P1] = {58000, 56000}; + singularityTime[S3_P2] = {74000, 26000, 55000, 44000}; + armageddonTime[S1_P1] = {10000, 64000}; + armageddonTime[S3_P2] = {50400, 76000, 35000, 31000}; + burstingDreadflameTime[S3_P2] = {52400, 48000, 55000, 50000}; + burstingDreadflameTime[S5_P3] = {42000, 25000, 70000}; + focusedDreadFlameTime[S3_P2] = {30000, 46000, 53000, 46000}; + focusedDreadFlameTime[S5_P3] = {80000, 95000}; + } + else + { + singularityTime[S1_P1] = {58000, 56000}; + singularityTime[S3_P2] = {74000, 81000}; + armageddonTime[S1_P1] = {10000, 64000}; + armageddonTime[S3_P2] = {50400, 45000, 31000, 35000, 31000, 78000}; + burstingDreadflameTime[S3_P2] = {52400, 48000, 55000, 50000}; + burstingDreadflameTime[S5_P3] = {42000, 25000, 70000}; + focusedDreadFlameTime[S3_P2] = {76000, 99000}; + focusedDreadFlameTime[S5_P3] = {80000, 95000}; + } + + switch (stage) + { + case S1_P1: + events.RescheduleEvent(EVENT_FELCLAWS, 25000); + events.RescheduleEvent(EVENT_ARMAGEDDON, armageddonTime[stage].front()); + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_RUPTURING_SINGULARITY, singularityTime[stage].front()); + if (IsHeroicPlusRaid()) + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_ERUPTING, 18000); + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_WAILING, 56000); + break; + case S3_P2: + if (!IsLfrRaid()) + { + events.RescheduleEvent(EVENT_RUPTURING_SINGULARITY, singularityTime[stage].front()); + events.RescheduleEvent(EVENT_FOCUSED_DREADFLAME, focusedDreadFlameTime[stage].front()); + } + events.RescheduleEvent(EVENT_FELCLAWS, 10000); + events.RescheduleEvent(EVENT_ARMAGEDDON, armageddonTime[stage].front()); + events.RescheduleEvent(EVENT_BURSTING_DREADFLAME, burstingDreadflameTime[stage].front()); + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_ERUPTING, IsMythicRaid() ? 163000 : 12000); + if (IsMythicRaid()) + { + wailingTimers = 168000; + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_WAILING, 50000); + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_HOPELESS, 26000); + } + else if (IsHeroicRaid()) + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_WAILING, 48000); + break; + case S5_P3: + events.RescheduleEvent(EVENT_FELCLAWS, 10000); + events.RescheduleEvent(EVENT_DARKNESS_THOUSAND_SOULS, 1000); + events.RescheduleEvent(EVENT_DEMONIC_OBELISK, 25000); + events.RescheduleEvent(EVENT_TEAR_RIFT, 14000); + events.RescheduleEvent(EVENT_BURSTING_DREADFLAME, burstingDreadflameTime[stage].front()); + if (IsHeroicPlusRaid()) + events.RescheduleEvent(EVENT_FLAMING_ORB, 30000); + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_FOCUSED_DREADFLAME, focusedDreadFlameTime[stage].front()); + break; + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (id == 1) + { + DoCast(me, SPELL_NETHER_GALE, true); + me->SetSpeed(MOVE_FLIGHT, 5.0f); + + me->AddDelayedCombat(4000, [this] () -> void + { + me->GetMotionMaster()->MovePoint(2, flyPos[randList.front()], false); + }); + } + else if (id == 2) + { + uint32 delayTimer = IsMythicRaid() ? 57000 : 24000; + flymove = false; + Talk(SAY_FLY_POINT_1); + me->SetFacingTo(flyPos[4].GetAngle(me) - M_PI); + events.RescheduleEvent(EVENT_ARMAGEDDON, 1000); + events.RescheduleEvent(EVENT_BURSTING_DREADFLAME, IsMythicRaid() ? 4000 : 2000); + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_FOCUSED_DREADFLAME, IsMythicRaid() ? 25000 : 18000); + if (IsHeroicRaid()) + events.RescheduleEvent(EVENT_RUPTURING_SINGULARITY, 7000); + else if (IsMythicRaid()) + { + // Rupturing Singularity + for (auto timer : {8000, 13000, 26000, 31000, 36000, 41000}) + { + me->AddDelayedCombat(timer, [this] () -> void { RupturingSingularity(); }); + } + } + + me->AddDelayedCombat(delayTimer, [this] () -> void + { + flymove = true; + me->GetMotionMaster()->MovePoint(3, flyPos[randList.back()], false); + }); + } + else if (id == 3) + { + uint32 delayTimer = IsMythicRaid() ? 24000 : 16000; + flymove = false; + Talk(SAY_FLY_POINT_1); + me->SetSpeed(MOVE_FLIGHT, 3.0f); + me->SetFacingTo(flyPos[4].GetAngle(me) - M_PI); + events.RescheduleEvent(EVENT_ARMAGEDDON, 1000); + events.RescheduleEvent(EVENT_BURSTING_DREADFLAME, IsMythicRaid() ? 24000 : 20000); + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_FOCUSED_DREADFLAME, IsMythicRaid() ? 4000 : 2000); + if (IsHeroicRaid()) + events.RescheduleEvent(EVENT_RUPTURING_SINGULARITY, 8000); + else if (IsMythicRaid()) + { + // Rupturing Singularity + for (auto timer : {7000, 12000}) + { + me->AddDelayedCombat(timer, [this] () -> void { RupturingSingularity(); }); + } + } + + me->AddDelayedCombat(delayTimer, [this] () -> void + { + flymove = true; + me->GetMotionMaster()->MovePoint(4, flyPos[4], false); + }); + } + else if (id == 4) + { + SetFlyMode(false); + me->SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); + me->GetMotionMaster()->MoveFall(); + me->RemoveAurasDueToSpell(SPELL_NETHER_GALE); + me->SetReactState(REACT_AGGRESSIVE, 3000); + flymove = false; + DefaultEvents(); + } + else if (id == 5) + { + me->CastSpell(me, SPELL_DARKNESS_THOUSAND_SOULS); + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPct(healthPct)) + { + if (healthPct == 81) + { + healthPct = 41; + flymove = true; + Talk(SAY_STAGE_2_CHANGE); + DefaultEvents(); + me->StopAttack(true); + SetFlyMode(true); + me->SetByteValue(UNIT_FIELD_BYTES_1, 3, 3); + me->SetSpeed(MOVE_FLIGHT, 3.0f); + me->GetMotionMaster()->MovePoint(1, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, false); + DoCast(me, SPELL_WINGSPAN_SOUND, true); + } + else if (healthPct == 41) + { + healthPct = 0; + Talk(SAY_STAGE_4_CHANGE); + shadowsoulDiedCount = 0; + DefaultEvents(); + me->StopAttack(true); + events.RescheduleEvent(EVENT_DECEIVER_VEIL, 500); + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_RUPTURING_SINGULARITY, 20000); + } + } + } + + void SpellFinishCast(const SpellInfo* spell) override + { + switch (spell->Id) + { + case SPELL_ARMAGEDDON_DUMMY: + Talk(SAY_WARN_ARMAGEDDON); + Armageddon(); + break; + case SPELL_DECEIVER_VEIL_AT: + Talk(SAY_DECEIVER_VEIL_FINISH_CAST); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + break; + case SPELL_DARKNESS_THOUSAND_SOULS: + darknessThousandEvent = false; + me->SetReactState(REACT_AGGRESSIVE, 500); + break; + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_FOCUSED_DREADFLAME_FILTER: + Talk(SAY_WARN_FOCUSED_DREADFLAME, target->GetGUID()); + me->CastSpell(target, SPELL_FOCUSED_DREADFLAME_DMG, false); + break; + } + } + + void OnRemoveAuraTarget(Unit* target, uint32 spellId, AuraRemoveMode mode) override + { + if (!me->IsInCombat() || mode != AURA_REMOVE_BY_EXPIRE) + return; + + switch (spellId) + { + case SPELL_SHADOW_REFLECTION_ERUPTING: + { + if (auto summon = me->SummonCreature(NPC_ERUPTING_REFLECTION, target->GetPosition())) + target->CastSpell(summon, SPELL_ERUPTING_REFLECTION_CLONE, true); + break; + } + case SPELL_SHADOW_REFLECTION_WAILING: + { + if (auto summon = me->SummonCreature(NPC_WAILING_REFLECTION, target->GetPosition())) + target->CastSpell(summon, SPELL_ERUPTING_REFLECTION_CLONE, true); + break; + } + case SPELL_SHADOW_REFLECTION_HOPELESS: + { + if (auto summon = me->SummonCreature(NPC_HOPELESS_REFLECTION, target->GetPosition())) + target->CastSpell(summon, SPELL_ERUPTING_REFLECTION_CLONE, true); + break; + } + default: + break; + } + } + + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + switch (summon->GetEntry()) + { + case NPC_SHADOWSOUL: + { + if (++shadowsoulDiedCount == 5) + { + Talk(SAY_STAGE_5); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasDueToSpell(SPELL_DECEIVER_VEIL_AT); + me->RemoveAreaTrigger(SPELL_DECEIVER_VEIL_AT); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DECEIVER_VEIL_1); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DECEIVER_VEIL_2); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ILLIDAN_SIGHTLESS_GAZE); + summons.DespawnEntry(NPC_STAGE4_ILLIDAN_STORMRAGE); + DefaultEvents(); + } + break; + } + } + } + + void DoAction(int32 const action) override + { + if (action == ACTION_1) + achievement = false; + } + + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_ACHIEVEMENT: + return achievement ? 1 : 0; + } + return 0; + } + + void RupturingSingularity() + { + uint8 sungularRandOld = sungularRand; + + while (sungularRandOld == sungularRand) + { + sungularRand = urand(0, 3); + } + + me->CastSpell(singularPos[sungularRand], SPELL_RUPTURING_SINGULARITY, true); + } + + void Armageddon() + { + std::list randPosList; + me->GetPosition().GenerateNonDuplicatePoints(randPosList, centrPos, armageddonCount, 15.0f, 38.0f, 8.0f); + uint8 count = 0; + + for (auto _pos : randPosList) + { + if (auto summon = me->SummonCreature(NPC_ARMAGEDDON_STALKER, _pos, TEMPSUMMON_TIMED_DESPAWN, 8000)) + { + if (!count || (count == 1 && IsMythicRaid())) + summon->CastSpell(summon, SPELL_ARMAGEDDON_HAIL_AT, true); + else + summon->CastSpell(summon, SPELL_ARMAGEDDON_RAIN_AT, true); + + ++count; + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (berserkTimer) + { + if (berserkTimer <= diff) + { + berserkTimer = 0; + DoCast(me, SPELL_BERSERK, true); + } + else + berserkTimer -= diff; + } + + if (me->HasUnitState(UNIT_STATE_CASTING) || flymove || darknessThousandEvent) + return; + + if (stage != S2_CHANGE && CheckHomeDistToEvade(diff, 62.0f, 4500.27f, -1510.24f, 5385.64f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_FELCLAWS: + DoCast(SPELL_FELCLAWS); + events.RescheduleEvent(EVENT_FELCLAWS, 25000); + break; + case EVENT_RUPTURING_SINGULARITY: + Talk(SAY_RUPTURING_SINGULARITY); + Talk(SAY_WARN_RUPTURING_SINGULARITY); + RupturingSingularity(); + if (singularityTime[stage].size() > 1) + singularityTime[stage].pop_front(); + if (!singularityTime[stage].empty()) + events.RescheduleEvent(EVENT_RUPTURING_SINGULARITY, singularityTime[stage].front()); + if (stage == S4_CHANGE) + events.RescheduleEvent(EVENT_RUPTURING_SINGULARITY, 10000); + break; + case EVENT_ARMAGEDDON: + DoCast(SPELL_ARMAGEDDON_DUMMY); + if (armageddonTime[stage].size() > 1) + armageddonTime[stage].pop_front(); + if (!armageddonTime[stage].empty()) + { + Talk(SAY_ARMAGEDDON); + events.RescheduleEvent(EVENT_ARMAGEDDON, armageddonTime[stage].front()); + } + break; + case EVENT_BURSTING_DREADFLAME: + Talk(SAY_BURSTING_DREADFLAME); + DoCast(SPELL_BURSTING_DREADFLAME_FILTER); + if (burstingDreadflameTime[stage].size() > 1) + { + if (stage == S5_P3 && burstingDreadflameTime[stage].size() == 2) + burstingDreadflameTime[stage].reverse(); + else + burstingDreadflameTime[stage].pop_front(); + } + if (!burstingDreadflameTime[stage].empty()) + events.RescheduleEvent(EVENT_BURSTING_DREADFLAME, burstingDreadflameTime[stage].front()); + break; + case EVENT_FOCUSED_DREADFLAME: + Talk(SAY_FOCUSED_DREADFLAME); + DoCast(SPELL_FOCUSED_DREADFLAME_FILTER); + if (focusedDreadFlameTime[stage].size() > 1) + { + if (stage == S5_P3 && focusedDreadFlameTime[stage].size() == 2) + focusedDreadFlameTime[stage].reverse(); + else + focusedDreadFlameTime[stage].pop_front(); + } + if (!focusedDreadFlameTime[stage].empty()) + events.RescheduleEvent(EVENT_FOCUSED_DREADFLAME, focusedDreadFlameTime[stage].front()); + break; + case EVENT_SHADOW_REFLECTION_ERUPTING: + Talk(SAY_REFLECTION_ERUPTING); + DoCast(SPELL_SHADOW_REFLECTION_ERUPTING); + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_ERUPTING, IsMythicRaid() ? 163000 : 111000); + break; + case EVENT_DECEIVER_VEIL: + { + DoCast(SPELL_DECEIVER_VEIL_AT); + Position pos; + for (uint8 i = 0; i < 5; ++i) + { + centrPos.SimplePosXYRelocationByAngle(pos, frand(15.0f, 35.0f), frand(0.0f, 6.28f)); + me->AddDelayedCombat(3000, [this, pos] () -> void { me->SummonCreature(NPC_SHADOWSOUL, pos); }); + } + centrPos.SimplePosXYRelocationByAngle(pos, frand(15.0f, 35.0f), frand(0.0f, 6.28f)); + me->AddDelayedCombat(7000, [this, pos] () -> void { me->SummonCreature(NPC_STAGE4_ILLIDAN_STORMRAGE, pos); }); + break; + } + case EVENT_DARKNESS_THOUSAND_SOULS: + Talk(SAY_DARKNESS_THOUSAND_SOULS); + darknessThousandEvent = true; + me->StopAttack(false, false); + me->GetMotionMaster()->MovePoint(5, flyPos[4]); //Center + events.RescheduleEvent(EVENT_DARKNESS_THOUSAND_SOULS, 90000); + break; + case EVENT_DEMONIC_OBELISK: + { + uint32 timer = 36000; + if (++obeliskIdx == 2) + { + obeliskIdx = 0; + ++obeliskCount; + timer = 60000; + } + Position pos; + for (uint8 i = 0; i < obeliskCount; ++i) + { + centrPos.SimplePosXYRelocationByAngle(pos, frand(25.0f, 35.0f), frand(0.0f, 6.28f)); + me->SummonCreature(NPC_DEMONIC_OBELISK, pos); + } + events.RescheduleEvent(EVENT_DEMONIC_OBELISK, timer); + break; + } + case EVENT_TEAR_RIFT: + { + Talk(SAY_TEAR_RIFT); + summons.DespawnEntry(NPC_NETHER_RIFT); + uint8 rand = urand(0, 3); + me->CastSpell(riftPos[rand], SPELL_TEAR_RIFT, false); + me->SummonCreature(NPC_NETHER_RIFT, riftPos[rand]); + events.RescheduleEvent(EVENT_TEAR_RIFT, 94000); + break; + } + case EVENT_FLAMING_ORB: + { + uint32 timer = 30000; + if (++flamingOrbIdx == 2) + { + flamingOrbIdx = 0; + timer = 60000; + } + Talk(SAY_FLAMING_ORB); + DoCast(SPELL_FLAMING_ORB_FILTER); + events.RescheduleEvent(EVENT_FLAMING_ORB, timer); + break; + } + case EVENT_SHADOW_REFLECTION_WAILING: + if (IsMythicRaid()) + wailingTimers = wailingTimers == 168000 ? 60000 : 168000; + else + wailingTimers = 114000; + Talk(SAY_WAILING); + DoCastTopAggro(SPELL_SHADOW_REFLECTION_WAILING); + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_WAILING, stage == S1_P1 ? 114000 : wailingTimers); + break; + case EVENT_SHADOW_REFLECTION_HOPELESS: + Talk(SAY_HOPELESS); + DoCast(SPELL_SHADOW_REFLECTION_HOPELESS); + events.RescheduleEvent(EVENT_SHADOW_REFLECTION_HOPELESS, 196000); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//120839 +struct npc_tos_armageddon_stalker : public ScriptedAI +{ + explicit npc_tos_armageddon_stalker(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void Reset() override {} + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + auto owner = me->GetAnyOwner(); + if (!owner) + return; + + if (spell->Id == SPELL_ARMAGEDDON_RAIN_SELF) + { + owner->CastSpell(me, SPELL_ARMAGEDDON_RAIN_DMG, true); + + if (auto at = me->GetAreaTrigger(SPELL_ARMAGEDDON_RAIN_AT)) + { + if (at->GetInsideUnits().empty()) + owner->CastSpell(me, SPELL_ARMAGEDDON_BLAST, true); + } + me->RemoveAurasDueToSpell(SPELL_ARMAGEDDON_RAIN_AT); + } + + if (spell->Id == SPELL_ARMAGEDDON_HAIL_SELF) + { + owner->CastSpell(me, SPELL_ARMAGEDDON_HAIL_DMG, true); + + if (auto at = me->GetAreaTrigger(SPELL_ARMAGEDDON_HAIL_AT)) + { + if (at->GetInsideUnits().empty()) + owner->CastSpell(me, SPELL_ARMAGEDDON_BLAST, true); + } + me->RemoveAurasDueToSpell(SPELL_ARMAGEDDON_HAIL_AT); + } + } + + void UpdateAI(uint32 /*diff*/) override {} +}; + +//119206 +struct npc_tos_erupting_reflection : public ScriptedAI +{ + explicit npc_tos_erupting_reflection(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + ObjectGuid targetGUID; + + void IsSummonedBy(Unit* summoner) override + { + me->AddDelayedEvent(500, [this] () -> void { me->HandleEmoteCommand(EMOTE_ONESHOT_BATTLE_ROAR); }); + me->SetReactState(REACT_AGGRESSIVE, 2000); + } + + void Reset() override {} + + void JustDied(Unit* /*killer*/) override + { + if (IsMythicRaid()) + { + if (auto target = ObjectAccessor::GetPlayer(*me, targetGUID)) + { + if (target->IsAlive()) + if (auto owner = me->GetAnyOwner()) + owner->CastSpell(target, SPELL_LINGERING_ERUPTION, true); + } + } + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_ERUPTING_REFLECTION_CLONE) + targetGUID = caster->GetGUID(); + } + + void UpdateAI(uint32 /*diff*/) override + { + if (!UpdateVictim()) + return; + + DoSpellAttackIfReady(SPELL_ERUPTING_DREADFLAME); + } +}; + +//119107 +struct npc_tos_wailing_reflection : public ScriptedAI +{ + explicit npc_tos_wailing_reflection(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + ObjectGuid targetGUID; + uint32 sorrowfulWailTimer = 0; + + void IsSummonedBy(Unit* summoner) override + { + me->AddDelayedEvent(500, [this] () -> void { me->HandleEmoteCommand(EMOTE_ONESHOT_BATTLE_ROAR); }); + me->SetReactState(REACT_AGGRESSIVE, 2000); + + sorrowfulWailTimer = 16000; + } + + void Reset() override {} + + void JustDied(Unit* /*killer*/) override + { + if (!IsMythicRaid()) + return; + + if (auto target = ObjectAccessor::GetPlayer(*me, targetGUID)) + { + if (target->IsAlive()) + if (auto owner = me->GetAnyOwner()) + owner->CastSpell(target, SPELL_LINGERING_WAIL, true); + } + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_ERUPTING_REFLECTION_CLONE) + targetGUID = caster->GetGUID(); + } + + void OnApplyOrRemoveAura(uint32 spellId, AuraRemoveMode mode, bool apply) override + { + if (apply || mode != AURA_REMOVE_BY_EXPIRE) + return; + + if (spellId == SPELL_SORROWFUL_WAIL_AT) + DoCast(me, SPELL_SORROWFUL_WAIL_DMG, true); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (sorrowfulWailTimer) + { + if (sorrowfulWailTimer <= diff) + { + sorrowfulWailTimer = 14000; + me->CastSpell(me, SPELL_SORROWFUL_WAIL_AT); + } + else + sorrowfulWailTimer -= diff; + } + DoMeleeAttackIfReady(); + } +}; + +//119663 +struct npc_tos_hopeless_reflection : public ScriptedAI +{ + explicit npc_tos_hopeless_reflection(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + me->setRegeneratingHealth(false); + me->SetHealth(1); + } + + uint32 checkTimer = 0; + uint32 timePassed = 0; + uint32 despawnTimer = 0; + + void IsSummonedBy(Unit* summoner) override + { + me->CastSpellDelay(me, SPELL_HOPELESSNESS_DUMMY, false, 500); + checkTimer = 500; + } + + void Reset() override {} + + void SpellFinishCast(const SpellInfo* spell) override + { + if (spell->Id == SPELL_HOPELESSNESS_DUMMY) + { + checkTimer = 0; + DoCast(me, SPELL_HOPELESSNESS_AT, true); + } + } + + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_HOPELESS_HEALTH_EVENT: + return timePassed / 1000; + } + return 0; + } + + void UpdateAI(uint32 diff) override + { + if (checkTimer) + { + timePassed += diff; + + if (checkTimer <= diff) + { + checkTimer = 500; + + if (me->GetHealthPct() >= 99.9f) + { + checkTimer = 0; + despawnTimer = 62000; + me->InterruptNonMeleeSpells(false); + me->CastSpell(me, SPELL_HOPELESSNESS_AT, true); + me->CastSpell(me, SPELL_LINGERING_HOPE, true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->AddDelayedEvent(500, [this] () -> void { me->SetVisible(false); }); + } + } + else + checkTimer -= diff; + } + if (despawnTimer) + { + if (despawnTimer <= diff) + { + despawnTimer = 0; + me->DespawnOrUnsummon(); + } + else + despawnTimer -= diff; + } + } +}; + +//121193 +struct npc_tos_shadowsoul : public ScriptedAI +{ + explicit npc_tos_shadowsoul(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + SetCombatMovement(false); + me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_FEAR, true); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_ROOT, true); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, true); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_HORROR, true); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_SAPPED, true); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_CHARM, true); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, true); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_CONFUSE, true); + } + + EventMap events; + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_SPAWN_VISUAL, true); + me->SetReactState(REACT_AGGRESSIVE, 3000); + events.RescheduleEvent(EVENT_1, 3000); + } + + void Reset() override {} + + void SpellFinishCast(const SpellInfo* spell) override + { + if (spell->Id == SPELL_DESTABILIZED_SHADOWSOUL_DMG) + { + DoCast(me, SPELL_DESTABILIZED_SHADOWSOUL_TP, true); + + if (auto kj = me->GetAnyOwner()) + if (kj->IsAIEnabled) + kj->GetAI()->DoAction(ACTION_1); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (events.ExecuteEvent() == EVENT_1) + { + me->CastSpell(me, SPELL_DESTABILIZED_SHADOWSOUL_DMG); + events.RescheduleEvent(EVENT_1, 5000); + } + } +}; + +//121227 +struct npc_tos_stage4_illidan_stormrage : public ScriptedAI +{ + explicit npc_tos_stage4_illidan_stormrage(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void IsSummonedBy(Unit* summoner) override + { + me->PlayOneShotAnimKitId(ILLIDAN_ANIM_1); + + me->AddDelayedEvent(500, [this] () -> void + { + Talk(SAY_ILLIDAN_STAGE_4); + me->CastSpell(me, SPELL_CURSED_SIGHT, true); + }); + } + + void Reset() override {} + + void UpdateAI(uint32 /*diff*/) override {} +}; + +//120270 +struct npc_tos_demonic_obelisk : public ScriptedAI +{ + explicit npc_tos_demonic_obelisk(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + uint32 obeliskVisualTimer = 0; + uint32 animTimer = 0; + uint32 obeliskDmgTimer = 0; + + void IsSummonedBy(Unit* summoner) override + { + obeliskVisualTimer = 9000; + animTimer = 12000; + obeliskDmgTimer = 14000; + } + + void Reset() override {} + + void UpdateAI(uint32 diff) override + { + if (obeliskVisualTimer) + { + if (obeliskVisualTimer <= diff) + { + obeliskVisualTimer = 0; + DoCast(me, SPELL_DEMONIC_OBELISK_VISUAL, true); + } + else + obeliskVisualTimer -= diff; + } + if (animTimer) + { + if (animTimer <= diff) + { + animTimer = 0; + me->PlayOneShotAnimKitId(OBELISK_ANIM_1); + } + else + animTimer -= diff; + } + if (obeliskDmgTimer) + { + if (obeliskDmgTimer <= diff) + { + obeliskDmgTimer = 0; + float x, y; + float angle = 0.0f; + for (uint8 i = 0; i < 4; ++i) + { + me->GetNearPoint2D(x, y, 1.0f, angle); + me->CastSpell(x, y, me->GetPositionZ(), SPELL_DEMONIC_OBELISK_DMG, true); + angle += M_PI / 2; + } + me->PlayOneShotAnimKitId(OBELISK_ANIM_2); + me->DespawnOrUnsummon(500); + } + else + obeliskDmgTimer -= diff; + } + } +}; + +//120390 +struct npc_tos_nether_rift : public ScriptedAI +{ + explicit npc_tos_nether_rift(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + bool openRift = false; + uint32 riftSumTimer = 0; + + void IsSummonedBy(Unit* summoner) override + { + me->CastSpellDelay(me, SPELL_TEAR_RIFT_VISUAL, true, 5000); + riftSumTimer = 30000; + } + + void Reset() override {} + + void UpdateAI(uint32 diff) override + { + if (riftSumTimer) + { + if (riftSumTimer <= diff) + { + riftSumTimer = 2000; + + if (auto owner = me->GetAnyOwner()) + { + if (!openRift) + { + openRift = true; + owner->CastSpell(me, SPELL_TEAR_RIFT_AT, true); + } + + std::list listAT; + owner->GetAreaTriggerListWithSpellIDInRange(listAT, SPELL_FLAMING_DETONATION, 300.0f); + (void)listAT; // CastAction : declenchement manuel d action AT uwow, non porte + } + } + else + riftSumTimer -= diff; + } + } +}; + +//120082 +struct npc_tos_flaming_orb : public ScriptedAI +{ + explicit npc_tos_flaming_orb(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + me->SetSpeed(MOVE_RUN, 2.0f); + } + + uint32 startMovingTimer = 0; + ObjectGuid targetGUID; + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_FLAMING_ORB_AT, true); + me->CastSpellDelay(me, SPELL_ORG_FIXATE, true, 500); + startMovingTimer = 2000; + } + + void Reset() override {} + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (spell->Id == SPELL_ORG_FIXATE) + targetGUID = target->GetGUID(); + } + + void SpellFinishCast(const SpellInfo* spell) override + { + if (spell->Id == SPELL_FLAMING_DETONATION) + { + startMovingTimer = 0; + me->RemoveAurasDueToSpell(SPELL_FLAMING_ORB_AT); + me->InterruptNonMeleeSpells(false); + me->GetMotionMaster()->Clear(); + me->DespawnOrUnsummon(500); + } + } + + void UpdateAI(uint32 diff) override + { + if (startMovingTimer) + { + if (startMovingTimer <= diff) + { + startMovingTimer = 500; + + auto target = ObjectAccessor::GetPlayer(*me, targetGUID); + if (!target || !target->IsAlive()) + { + startMovingTimer = 2000; + DoCast(me, SPELL_ORG_FIXATE, true); + me->GetMotionMaster()->Clear(); + } + else + me->GetMotionMaster()->MovePoint(1, target->GetPosition(), false); + } + else + startMovingTimer -= diff; + } + } +}; + +//238502 +class spell_tos_focused_dreadflame : public SpellScript +{ + PrepareSpellScript(spell_tos_focused_dreadflame); + + uint8 count = 0; + + void FilterTargets(std::list& targets) + { + targets.remove_if([this](WorldObject* object) -> bool + { + if (object == nullptr || !object->IsPlayer()) + return true; + + return false; + }); + + count = targets.size(); + } + + void HandleDamage(SpellEffIndex /*effIndex*/) + { + if (count) + SetHitDamage(GetHitDamage() / count); + + if (GetCaster() && GetHitUnit() && GetCaster()->GetMap()->IsHeroicPlusRaid()) + GetCaster()->CastSpell(GetHitUnit(), SPELL_FOCUSED_DREADBURST, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_focused_dreadflame::FilterTargets, EFFECT_0, TARGET_UNIT_ENEMY_BETWEEN_DEST); + OnEffectHitTarget += SpellEffectFn(spell_tos_focused_dreadflame::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +//242074 +class spell_tos_destabilized_shadowsoul : public SpellScript +{ + PrepareSpellScript(spell_tos_destabilized_shadowsoul); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + PreventHitDefaultEffect(EFFECT_0); + + Position pos; + centrPos.SimplePosXYRelocationByAngle(pos, frand(15.0f, 35.0f), frand(0.0f, 6.28f)); + GetHitDest()->Relocate(pos); + } + + void Register() override + { + OnEffectLaunch += SpellEffectFn(spell_tos_destabilized_shadowsoul::HandleDummy, EFFECT_0, SPELL_EFFECT_TELEPORT_L); + } +}; + +//238999 +class spell_tos_darkness_thousand_souls : public SpellScript +{ + PrepareSpellScript(spell_tos_darkness_thousand_souls); + + ObjectGuid RiftGuid; + + void FilterTargets(std::list& targets) + { + std::list tempTargets; + + for (auto target : targets) + { + if (target->IsPlayer()) + tempTargets.push_back(target); + else if (target->GetEntry() == NPC_NETHER_RIFT) + RiftGuid = target->GetGUID(); + } + targets = tempTargets; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (!GetHitUnit()) + return; + + if (auto caster = GetCaster()) + { + if (GetHitUnit()->HasAura(SPELL_GRAVITY_SQUEEZE_DUMMY)) + { + if (auto rift = ObjectAccessor::GetCreature(*caster, RiftGuid)) + caster->CastSpell(rift, SPELL_DARKNESS_THOUSAND_SOULS_DUMMY, true); + } + else + caster->CastSpell(GetHitUnit(), SPELL_DARKNESS_THOUSAND_SOULS_DMG, true); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_darkness_thousand_souls::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_tos_darkness_thousand_souls::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +//239280 +class spell_tos_flaming_orb_fixate : public SpellScript +{ + PrepareSpellScript(spell_tos_flaming_orb_fixate); + + void FilterTargets(std::list& targets) + { + targets.remove_if([this](WorldObject* object) -> bool + { + if (object == nullptr || !object->IsPlayer()) + return true; + + return false; + }); + + targets.sort([caster = GetCaster()](WorldObject* a, WorldObject* b) { return caster->GetDistance(a) < caster->GetDistance(b); }); + + if (targets.size() > 1) + targets.resize(1); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_flaming_orb_fixate::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); + } +}; + +//243625 +class spell_tos_lingering_wail_jump : public SpellScript +{ + PrepareSpellScript(spell_tos_lingering_wail_jump); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (GetCaster() && GetHitUnit()) + GetHitUnit()->CastSpell(GetCaster(), SPELL_WAILING_GRASP_JUMP, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tos_lingering_wail_jump::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +//237728 +class spell_tos_hopelessness : public SpellScript +{ + PrepareSpellScript(spell_tos_hopelessness); + + void HandleDamage(SpellEffIndex /*effIndex*/) + { + if (auto hopelessness = GetCaster()) + { + float pct = float(hopelessness->GetAI()->GetData(DATA_HOPELESS_HEALTH_EVENT)) / 30.0f * 100.0f; + SetHitDamage(CalculatePct(GetHitDamage(), pct)); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tos_hopelessness::HandleDamage, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +//239154 +class spell_tos_gravity_squeeze : public AuraScript +{ + PrepareAuraScript(spell_tos_gravity_squeeze); + + void OnTick(AuraEffect const* /*aurEff*/) + { + if (GetUnitOwner()) + { + if (InstanceScript* instance = GetUnitOwner()->GetInstanceScript()) + if (Creature* kiljaeden = instance->instance->GetCreature(instance->GetGuidData(DATA_KILJAEDEN))) + kiljaeden->CastSpell(GetUnitOwner(), SPELL_GRAVITY_SQUEEZE_DOT, true); + } + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()) + GetTarget()->RemoveAurasDueToSpell(SPELL_GRAVITY_SQUEEZE_DOT); + } + + void Register() + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_gravity_squeeze::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + OnEffectRemove += AuraEffectRemoveFn(spell_tos_gravity_squeeze::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +//243536 +class spell_tos_lingering_eruption : public AuraScript +{ + PrepareAuraScript(spell_tos_lingering_eruption); + + void OnTick(AuraEffect const* /*aurEff*/) + { + if (GetUnitOwner()) + { + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_LATENT_ERUPTION, true); + + Position pos; + float angle = 0.5f; + for (uint8 i = 0; i < 3; ++i) + { + pos = GetUnitOwner()->GetNearPosition(5.0f, angle); + GetUnitOwner()->CastSpell(pos, SPELL_ERUPTING_ORB_AT, true); + pos = GetUnitOwner()->GetNearPosition(5.0f, -angle); + GetUnitOwner()->CastSpell(pos, SPELL_ERUPTING_ORB_AT, true); + angle += 1.0f; + } + } + } + + void Register() + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_lingering_eruption::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } +}; + +//243624 +class spell_tos_lingering_wail : public AuraScript +{ + PrepareAuraScript(spell_tos_lingering_wail); + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL) + return; + + if (GetTarget()) + GetTarget()->CastSpell(GetTarget(), SPELL_WAILING_GRASP_FILTER, true); + } + + void Register() + { + OnEffectRemove += AuraEffectRemoveFn(spell_tos_lingering_wail::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } +}; + +//15115, 15121 +struct at_tos_armageddon : AreaTriggerAI +{ + at_tos_armageddon(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + void ActionOnUpdate(GuidList& affectedPlayers) override + { + auto owner = at->GetCaster(); + if (!owner) + return; + + uint32 spellArmageddon = at->GetSpellId() == SPELL_ARMAGEDDON_RAIN_AT ? SPELL_ARMAGEDDON_RAIN_VISUAL : SPELL_ARMAGEDDON_HAIL_VISUAL; + + if (affectedPlayers.empty() && !owner->HasAura(spellArmageddon)) + { + owner->CastSpell(owner, spellArmageddon, true); + } + else if (!affectedPlayers.empty() && owner->HasAura(spellArmageddon)) + { + owner->RemoveAurasDueToSpell(spellArmageddon); + } + } +}; + +//14906 +struct at_tos_flaming_orb : AreaTriggerAI +{ + at_tos_flaming_orb(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + uint32 scaleTimer = 2000; + float scale = 1.0f; + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + if (!caster) + return false; + + if (InstanceScript* instance = caster->GetInstanceScript()) + { + int32 bp0 = int32(scale); + caster->CastCustomSpell(caster, SPELL_FLAMING_DETONATION, &bp0, nullptr, nullptr, true, nullptr, nullptr, instance->GetGuidData(DATA_KILJAEDEN)); + at->Remove(); + } + + return true; + } + + void OnUpdate(uint32 diff) override + { + if (scaleTimer <= diff) + { + scaleTimer = 200; + + if (at->GetRadius() > 1.0f) + { + scale -= 0.02f; + at->SetSphereScale(scale, 200, true); + } + } + else + scaleTimer -= diff; + } +}; + +//14915 +struct at_tos_flaming_detonation : AreaTriggerAI +{ + at_tos_flaming_detonation(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + float createScale = 0.0f; + float scaleStep = 0.0f; + float maxDist = 0.0f; + uint32 createScaleTimer = 0; + uint32 scaleTimer = 0; + Position finalPos; + + void OnCreate() override + { + if (SpellInfo const* si = sSpellMgr->GetSpellInfo(at->GetSpellId())) + if (SpellEffectInfo const* ei = si->GetEffect(EFFECT_0)) + createScale = CalculatePct(30.0f, ei->BasePoints * 100.0f); + createScaleTimer = 200; + } + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + if (!scaleTimer || at->isMoving()) + return false; + + return true; + } + + bool CalculateSpline(Position const* pos, Position& startPos, Position& endPos, std::vector& path) override + { + auto owner = at->GetCaster(); + auto rift = owner->FindNearestCreature(NPC_NETHER_RIFT, 150.0f); + if (!owner || !rift) + { + at->Despawn(); + return false; + } + + finalPos = rift->GetPosition(); + maxDist = at->GetDistance(finalPos); + + startPos.m_positionX = pos->GetPositionX(); + startPos.m_positionY = pos->GetPositionY(); + startPos.m_positionZ = pos->GetPositionZ(); + + endPos.m_positionX = rift->GetPositionX(); + endPos.m_positionY = rift->GetPositionY(); + endPos.m_positionZ = pos->GetPositionZ(); + return true; + } + + void OnUpdate(uint32 diff) override + { + if (createScaleTimer) + { + if (createScaleTimer <= diff) + { + createScaleTimer = 200; + + if (at->GetRadius() < createScale) + at->SetSphereScale(0.15f, 200, false); + else + { + createScaleTimer = 0; + scaleTimer = 200; + } + } + else + createScaleTimer -= diff; + } + + if (scaleTimer && at->isMoving()) + { + if (scaleTimer <= diff) + { + scaleTimer = 200; + + scaleStep = createScale * (at->GetDistance(finalPos) / maxDist); + at->SetSphereScale(scaleStep, 200, true); + if (scaleStep <= 0.1f) + at->Despawn(); + } + else + scaleTimer -= diff; + } + } +}; + +//1027000 +struct at_tos_hopelessness : AreaTriggerAI +{ + at_tos_hopelessness(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + uint32 scaleTimer = 500; + float scale = 0.0f; + + void OnCreate() override + { + auto owner = at->GetCaster(); + if (!owner) + return; + + float pct = owner->GetAI()->GetData(DATA_HOPELESS_HEALTH_EVENT) / 30.0f; + scale = 20.0f * pct; + } + + void OnUpdate(uint32 diff) override + { + if (scaleTimer) + { + if (scaleTimer <= diff) + { + if (at->GetRadius() >= scale) + { + scaleTimer = 0; + return; + } + else + scaleTimer = 300; + + at->SetSphereScale(0.1f, 300, false); + } + else + scaleTimer -= diff; + } + } +}; + +// 36478 +class achievement_dark_souls : public AchievementCriteriaScript +{ +public: + achievement_dark_souls() : AchievementCriteriaScript("achievement_dark_souls") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* Kj = target->ToCreature()) + if (Kj->AI()->GetData(DATA_ACHIEVEMENT)) + return true; + + return false; + } +}; + +void AddSC_boss_tos_kiljaeden() +{ + RegisterCreatureAI(boss_tos_kiljaeden); + RegisterCreatureAI(npc_tos_armageddon_stalker); + RegisterCreatureAI(npc_tos_erupting_reflection); + RegisterCreatureAI(npc_tos_wailing_reflection); + RegisterCreatureAI(npc_tos_hopeless_reflection); + RegisterCreatureAI(npc_tos_shadowsoul); + RegisterCreatureAI(npc_tos_stage4_illidan_stormrage); + RegisterCreatureAI(npc_tos_demonic_obelisk); + RegisterCreatureAI(npc_tos_nether_rift); + RegisterCreatureAI(npc_tos_flaming_orb); + RegisterSpellScript(spell_tos_focused_dreadflame); + RegisterSpellScript(spell_tos_destabilized_shadowsoul); + RegisterSpellScript(spell_tos_darkness_thousand_souls); + RegisterSpellScript(spell_tos_flaming_orb_fixate); + RegisterSpellScript(spell_tos_lingering_wail_jump); + RegisterSpellScript(spell_tos_hopelessness); + RegisterAuraScript(spell_tos_gravity_squeeze); + RegisterAuraScript(spell_tos_lingering_eruption); + RegisterAuraScript(spell_tos_lingering_wail); + RegisterAreaTriggerAI(at_tos_armageddon); + RegisterAreaTriggerAI(at_tos_flaming_orb); + RegisterAreaTriggerAI(at_tos_flaming_detonation); + RegisterAreaTriggerAI(at_tos_hopelessness); + new achievement_dark_souls(); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_MaidenofVigilance.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_MaidenofVigilance.cpp new file mode 100644 index 0000000..445fb4f --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_MaidenofVigilance.cpp @@ -0,0 +1,785 @@ +/* + * This file is part of the DestinyCore Project. See AUTHORS file for Copyright information + * + * 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 "tomb_of_sargeras.h" + +enum Says +{ + SAY_AGGRO = 0, + SAY_INFUSION_START = 1, + SAY_INFUSION_END = 2, + SAY_SECOND_PHASE = 3, + SAY_SECOND_PHASE_LATER = 4, + SAY_BEFORE_DEATH = 5, // ???????????? + SAY_DEATH = 6, +}; + +enum Spells +{ + // Global + SPELL_INFUSION = 235271, // + SPELL_MASS_INSTABILITY = 235267, // real cast aura (235117) + 8 // + SPELL_UNSTABLE_SOUL = 235117, // maybe need it late 243275 // + + // Light Side + SPELL_LIGHT_INFUSION = 235213, // dmg 235214 // + SPELL_HAMMER_OF_CREATION = 241635, // Aoe 235569 and tank dmg 241624 // + SPELL_HAMMER_OF_CREATION_AOE = 235569, // + SPELL_HAMMER_OF_CREATION_VICT = 241624, // + SPELL_LIGHT_REMANENCE = 237994, // 119825 + SPELL_LIGHT_REMANENCE_SMALL = 238311, // myth + + // Fel side + SPELL_FEL_INFUSION = 235240, // dmg 235253 // + SPELL_HAMMER_OF_OBLITERATION = 241636, // aoe dmg 235573 and tank dmg 241634 + 17 // + SPELL_HAMMER_OF_OBLITERATION_AOE = 235573, // + SPELL_HAMMER_OF_OBLITERATION_VICT = 241634, // + SPELL_FEL_REMANENCE = 238316, // 119826 + SPELL_FEL_REMANENCE_SMALL = 238416, // myth + + // Phase two + SPELL_MOVE_TO_ANOTHER = 235630, + SPELL_BLOWBACK = 248812, // by links; can find auras // + SPELL_TITANIC_BULWARK = 235028, // by links // + + SPELL_WRATH_OF_THE_CREATORS = 234891, // dmg 234896 (holy) and 236432(fel) + SPELL_WRATH_OF_THE_CREATORS_TICK = 235014, // + SPELL_WRATH_OF_THE_CREATORS_TICK_LIGHT = 234917, // + SPELL_WRATH_OF_THE_CREATORS_TICK_FEL = 236433, // + SPELL_WRATH_OF_THE_CREATORS_TICK_LIGHT_DMG = 234896, // + SPELL_WRATH_OF_THE_CREATORS_TICK_FEL_DMG = 236432, // + SPELL_WRATH_OF_THE_CREATORS_BUFF = 237339, // + + SPELL_EXTINGUISH_LIFE = 238475, // + + SPELL_ESSENCE_FRAGMENTS = 236061, // + SPELL_ESSENCE_FRAGMENTS_TICK = 235859, // + SPELL_CREATE_AT_VISUAL_FEL = 236708, // + SPELL_CREATE_AT_VISUAL_LIGHT = 236690, // + + SPELL_ESSENCE_HIT_LIGHT = 235554, // visual + SPELL_ESSENCE_BUFF_LIGHT = 235534, // + SPELL_ESSENCE_HIT_FEL = 235555, // visual + SPELL_ESSENCE_BUFF_FEL = 235538, // + + // myth + SPELL_FRAGMENT_BURST = 239069, + SPELL_FRAGMENT_BURST_DEBUFF = 241729, + + SPELL_FRAGMENTING = 239153, + SPELL_LIGHT_FRAGMENT = 241697, + SPELL_LIGHT_FRAGMENT_FOCUS = 241713, + SPELL_FEL_FRAGMENT = 241728, + SPELL_FEL_FRAGMENT_FOCUS = 241727, + + SPELL_ENRAGE = 64238, // + SPELL_SHIELD = 241593, + + SPELL_UNKWN = 237856, // also 235859 +}; + +enum eEvents +{ + EVENT_INFUSION = 1, + EVENT_LIGHT_HAMMER, + EVENT_FEL_HAMMER, + EVENT_MASS_INSTABILITY, + EVENT_SECOND_PHASE, + + EVENT_FRAGMENTS, + +}; + +Position const fragmentsPos[6] +{ + {6375.67f, -789.47f, 2887.84f, 0.0f}, // original right 1 + {6384.21f, -788.24f, 2887.84f, 0.0f}, // original right 2 + {6394.00f, -786.70f, 2887.84f, 0.0f}, // original right 3 + {6376.05f, -799.67f, 2887.84f, 0.0f}, // original left 1 + {6384.74f, -801.70f, 2887.84f, 0.0f}, // original left 2 + {6393.59f, -803.61f, 2887.84f, 0.0f} // original left 3 + + //6319.85 other side +}; + +// 118289 +struct boss_maiden_of_vigilance : BossAI +{ + boss_maiden_of_vigilance(Creature* creature) : BossAI(creature, DATA_MAIDEN_OF_VIGILANCE) {} + + uint32 wrathOfCreatorsCasts; + uint32 countOfTeleports; + + bool empoweredPhase; + bool isFelNeed = true; + + void Reset() override + { + if (me->IsAlive()) + _Reset(); + + me->KillAllDelayedEvents(); + wrathOfCreatorsCasts = 0; + countOfTeleports = 0; + empoweredPhase = false; // set true on 3 phase (second 1 phase) + SetCombatMovement(true); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_LIGHT_INFUSION); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FEL_INFUSION); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SHIELD); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FRAGMENT_BURST_DEBUFF); + me->SetReactState(REACT_DEFENSIVE); + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_INTERRUPT, false); + } + + void EnterCombat(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _EnterCombat(); + + me->AddDelayedEvent(480000, [this]() -> void + { + DoCast(me, SPELL_ENRAGE, true); + }); + + if (!IsLfrRaid()) + { + events.RescheduleEvent(EVENT_INFUSION, 2000); + events.RescheduleEvent(EVENT_LIGHT_HAMMER, 12000); + events.RescheduleEvent(EVENT_MASS_INSTABILITY, 21000); + events.RescheduleEvent(EVENT_SECOND_PHASE, 40000); + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_FRAGMENTS, 10000); + } + else + { + events.RescheduleEvent(EVENT_MASS_INSTABILITY, 6000); + events.RescheduleEvent(EVENT_INFUSION, 40000); + events.RescheduleEvent(EVENT_SECOND_PHASE, 47000); + } + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + _JustDied(); + Reset(); + } + + void JustSummoned(Creature* creature) override + { + BossAI::JustSummoned(creature); + + switch (creature->GetEntry()) + { + case NPC_REMANENCE_LIGHT: + if (IsHeroicPlusRaid()) + creature->CastSpell(creature, SPELL_LIGHT_REMANENCE_SMALL); + break; + case NPC_REMANENCE_FEL: + if (IsHeroicPlusRaid()) + creature->CastSpell(creature, SPELL_FEL_REMANENCE_SMALL); + break; + } + } + + void SummonedCreatureDespawn(Creature* creature) override + { + if (!IsMythicRaid()) + return; + + switch (creature->GetEntry()) + { + case NPC_ESSENCE_FEL: + case NPC_ESSENCE_LIGHT: + me->CastSpell(me, SPELL_FRAGMENT_BURST, true); + break; + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_HAMMER_OF_CREATION_AOE: + case SPELL_HAMMER_OF_CREATION_VICT: + if (target->HasAura(SPELL_FEL_INFUSION)) + DoCast(target, SPELL_UNSTABLE_SOUL, true); + break; + case SPELL_HAMMER_OF_OBLITERATION_AOE: + case SPELL_HAMMER_OF_OBLITERATION_VICT: + if (target->HasAura(SPELL_LIGHT_INFUSION)) + DoCast(target, SPELL_UNSTABLE_SOUL, true); + break; + } + } + + void SpellFinishCast(SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_MASS_INSTABILITY: + { + uint32 spell = isFelNeed ? SPELL_FEL_INFUSION : SPELL_LIGHT_INFUSION; + if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, + [spell](Unit* targett) + { + if (!targett->IsPlayer() || !targett->IsAlive() || !targett->ToPlayer()->isInTankSpec()) + return false; + + return targett->HasAura(spell); + })) + me->CastSpell(target, SPELL_UNSTABLE_SOUL, false); + for (uint32 spell : {SPELL_LIGHT_INFUSION, SPELL_FEL_INFUSION}) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, + [spell](Unit* targett) + { + if (!targett->IsPlayer() || !targett->IsAlive() || targett->ToPlayer()->isInTankSpec()) + return false; + + return targett->HasAura(spell); + })) + me->CastSpell(target, SPELL_UNSTABLE_SOUL, false); + break; + } + case SPELL_WRATH_OF_THE_CREATORS_TICK: + { + bool lightOrFel = wrathOfCreatorsCasts++ % 2 == 0; + me->CastSpell(me, lightOrFel ? SPELL_WRATH_OF_THE_CREATORS_TICK_LIGHT : SPELL_WRATH_OF_THE_CREATORS_TICK_FEL, true); + instance->instance->ApplyOnEveryPlayer([this, lightOrFel](Player* player) + { + me->CastSpell(player, lightOrFel ? SPELL_WRATH_OF_THE_CREATORS_TICK_LIGHT_DMG : SPELL_WRATH_OF_THE_CREATORS_TICK_FEL_DMG, true); + }); + + if (me->GetAuraCount(SPELL_WRATH_OF_THE_CREATORS_BUFF) >= 30) + { + // me->CastStop(); + instance->instance->ApplyOnEveryPlayer([this](Player* player) + { + me->CastSpell(player, SPELL_EXTINGUISH_LIFE, true); + }); + // OnInterruptCast(me, NULL, SPELL_WRATH_OF_THE_CREATORS, NULL); + } + break; + } + case SPELL_ESSENCE_FRAGMENTS_TICK: + // countOfTeleports % 2 == 1 - other side, == 0 - original side + for (uint8 i =0;i < 6;++i) + { + const auto& pos = fragmentsPos[i]; + uint32 spellid = (urand(1, 2) == 1 ? SPELL_CREATE_AT_VISUAL_FEL : SPELL_CREATE_AT_VISUAL_LIGHT); + me->CastSpell(pos.GetPositionX() - (countOfTeleports % 2 == 1 ? 73.2f : 0), pos.GetPositionY(), pos.GetPositionZ(), spellid, true); + if (Creature* add = me->SummonCreature((spellid == SPELL_CREATE_AT_VISUAL_FEL ? NPC_ESSENCE_FEL : NPC_ESSENCE_LIGHT), pos.GetPositionX() - (countOfTeleports % 2 == 1 ? 73.2f : 0), pos.GetPositionY(), pos.GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 12000)) + { + float change_speed = 1.2f * M_PI*add->GetDistance2d(6348.20f, -795.95f) / 45; + add->SetSpeed(MOVE_WALK, change_speed); + add->GetMotionMaster()->MoveCirclePath(6348.20f, -795.95f, add->GetPositionZ(), add->GetDistance2d(6348.20f, -795.95f), (countOfTeleports % 2 == 0 ? i > 2 : i <= 2), 24); + } + } + break; + + case SPELL_HAMMER_OF_CREATION: + case SPELL_HAMMER_OF_OBLITERATION: + DoCastTopAggro(spell->Id == SPELL_HAMMER_OF_OBLITERATION ? SPELL_HAMMER_OF_OBLITERATION_VICT : SPELL_HAMMER_OF_CREATION_VICT, true); + DoCastTopAggro(spell->Id == SPELL_HAMMER_OF_OBLITERATION ? SPELL_HAMMER_OF_OBLITERATION_AOE : SPELL_HAMMER_OF_CREATION_AOE, true); + if (IsHeroicPlusRaid()) + { + uint32 spellid = spell->Id == SPELL_HAMMER_OF_CREATION ? SPELL_FEL_INFUSION : SPELL_LIGHT_INFUSION; + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, + [spellid](Unit* targett) + { + if (!targett->IsPlayer() || !targett->IsAlive()) + return false; + + return targett->HasAura(spellid); + })) + me->CastSpell(target, spell->Id == SPELL_HAMMER_OF_OBLITERATION ? SPELL_FEL_REMANENCE : SPELL_LIGHT_REMANENCE, true); + } + break; + } + } + + void OnApplyOrRemoveAura(uint32 spellId, AuraRemoveMode /*mode*/, bool apply) override + { + if (spellId != SPELL_BLOWBACK || !me->IsInCombat() || !me->IsAlive()) + return; + + me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_INTERRUPT, apply); + } + + void OnInterruptCast(Unit* /*caster*/, uint32 spellId, uint32 curSpellID, uint32 /*schoolMask*/) override + { + if (curSpellID == SPELL_WRATH_OF_THE_CREATORS) + { + empoweredPhase = true; + me->SetReactState(REACT_DEFENSIVE); + if (!IsLfrRaid()) + { + events.RescheduleEvent(EVENT_INFUSION, 3000); + events.RescheduleEvent(EVENT_LIGHT_HAMMER, 12000); + events.RescheduleEvent(EVENT_MASS_INSTABILITY, 21000); + events.RescheduleEvent(EVENT_SECOND_PHASE, 82000); + if (IsMythicRaid()) + events.RescheduleEvent(EVENT_FRAGMENTS, 8000); + } + else + { + events.RescheduleEvent(EVENT_MASS_INSTABILITY, 8000); + events.RescheduleEvent(EVENT_INFUSION, 21000); + events.RescheduleEvent(EVENT_SECOND_PHASE, 67000); + } + } + } + + + void UpdateAI(uint32 diff) override + { + if (me->HasAura(SPELL_WRATH_OF_THE_CREATORS)) + { + bool needEvade = true; + auto list = me->getThreatManager().getThreatList(); + for (auto& itr: list) + if (Unit* target = itr->getTarget()) + if (target->IsPlayer() && target->IsAlive()) + { + needEvade = false; + break; + } + + if (needEvade) + EnterEvadeMode(); + return; + } + + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_INFUSION: + me->CastSpell(me, SPELL_INFUSION); + if (!empoweredPhase) + Talk(SAY_INFUSION_START); + + if (empoweredPhase) + events.RescheduleEvent(EVENT_INFUSION, 38000); + break; + case EVENT_LIGHT_HAMMER: + DoCast(SPELL_HAMMER_OF_CREATION); + isFelNeed = false; + events.RescheduleEvent(EVENT_FEL_HAMMER, 19000); + break; + case EVENT_FEL_HAMMER: + DoCast(SPELL_HAMMER_OF_OBLITERATION); + isFelNeed = true; + if (empoweredPhase) + events.RescheduleEvent(EVENT_LIGHT_HAMMER, 19000); + break; + case EVENT_MASS_INSTABILITY: + DoCast(SPELL_MASS_INSTABILITY); + if (empoweredPhase) + events.RescheduleEvent(EVENT_MASS_INSTABILITY, 36000); + break; + case EVENT_SECOND_PHASE: + events.Reset(); + Talk(SAY_SECOND_PHASE); + me->AddDelayedEvent(8000, [this]() + { + Talk(SAY_SECOND_PHASE_LATER); + }); + wrathOfCreatorsCasts = 0; + if (++countOfTeleports % 2) + me->CastSpell(6310.30f, -795.36f, 2887.84f, SPELL_MOVE_TO_ANOTHER); + else + me->CastSpell(me->GetHomePosition(), SPELL_MOVE_TO_ANOTHER,false); + SetCombatMovement(false); + me->SetReactState(REACT_PASSIVE); + break; + case EVENT_FRAGMENTS: + { + Unit* target = nullptr; + for (int8 i = 2; i >= 0; i--) + if (target = SelectTarget(SELECT_TARGET_RANDOM, i)) + break; + + if (!target) + break; + + DoCast(SPELL_FRAGMENTING); + bool isFelFragment = target->HasAura(SPELL_LIGHT_INFUSION); + me->CastSpell(target, (isFelFragment ? SPELL_FEL_FRAGMENT : SPELL_LIGHT_FRAGMENT)); + Position pos(target->GetPosition()); + ObjectGuid guid = target->GetGUID(); + me->AddDelayedEvent(3000, [this, guid, isFelFragment, pos]() -> void + { + me->SummonCreature((isFelFragment ? NPC_ESSENCE_FEL : NPC_ESSENCE_LIGHT), pos, TEMPSUMMON_TIMED_DESPAWN, 10000); + }); + break; + } + } + } + DoMeleeAttackIfReady(); + } +}; + +// 118640 light 118643 fel +struct npc_tos_essences : ScriptedAI +{ + npc_tos_essences(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + SetCanSeeEvenInPassiveMode(true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + ; // (m_RateUpdateTimer : optimisation de cadence uwow, non portee) + } + + bool justDoIt = false; + uint32 updateTimer = 200; + + void IsSummonedBy(Unit* cre) override + { + if (me->GetTarget().IsEmpty()) + return; + + Unit* unit = me->GetVictim(); + if (!unit || !unit->IsAlive()) + unit = me->FindNearestPlayer(60, true); + + if (unit) + me->GetMotionMaster()->MoveFollow(unit, 0, 0); + } + + void UpdateAI(uint32 diff) override + { + if (justDoIt) + return; + + if (updateTimer <= diff) + { + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (const auto& itr : players) + { + Player* who = itr.GetSource(); + if (!who->IsAlive()) + continue; + + if (me->GetDistance(who) < 0.22f && !justDoIt) + { + justDoIt = true; + + me->CastSpell(who, me->GetEntry() == NPC_ESSENCE_FEL ? SPELL_ESSENCE_HIT_FEL : SPELL_ESSENCE_HIT_LIGHT); + + if (who->HasAura(SPELL_LIGHT_INFUSION) && me->GetEntry() == NPC_ESSENCE_LIGHT) + { + for (const auto& itr : players) + if (Player* temp = itr.GetSource()) + if ((temp == who || temp->GetDistance(who) <= 10.0f) && temp->HasAura(SPELL_LIGHT_INFUSION)) + temp->CastSpell(temp, SPELL_ESSENCE_BUFF_LIGHT); + } + else if (who->HasAura(SPELL_FEL_INFUSION) && me->GetEntry() == NPC_ESSENCE_FEL) + { + for (const auto& itr : players) + if (Player* temp = itr.GetSource()) + if ((temp == who || temp->GetDistance(who) <= 10.0f) && temp->HasAura(SPELL_FEL_INFUSION)) + temp->CastSpell(temp, SPELL_ESSENCE_BUFF_FEL); + } + else + who->CastSpell(who, SPELL_UNSTABLE_SOUL); + + if (TempSummon* tempSum = me->ToTempSummon()) // we need it for disabling call SummonedCreatureDespawn + ; // (SetSummonerGUID : detachement du summoner, non porte) + + me->DespawnOrUnsummon(100); + break; + } + } + updateTimer = 200; + } + else + updateTimer -= diff; + } +}; + +// 120132, 120131 +struct npc_tos_essences_intro : ScriptedAI +{ + npc_tos_essences_intro(Creature* creature) : ScriptedAI(creature) + { + SetCanSeeEvenInPassiveMode(true); + } + + std::set players{}; + uint32 timer = 500; + + void UpdateAI(uint32 diff) override + { + if (timer <= diff) + { + Map::PlayerList const& pPlayers = me->GetMap()->GetPlayers(); + for (const auto& itr : pPlayers) + { + Player* who = itr.GetSource(); + if (me->GetDistance(who) < 1.0f) + { + if (players.find(who->GetGUID()) != players.end()) + return; + + players.insert(who->GetGUID()); + + uint32 enemySpell = (me->GetEntry() == 120132 ? 239408 : 239407); + if (who->HasAura(enemySpell)) + who->CastSpell(who, 239409); + else + who->CastSpell(who, (me->GetEntry() != 120132 ? 239408 : 239407)); + } + } + + timer = 500; + } + else + timer -= diff; + } +}; + +// 235271 +class spell_tos_infusion : public SpellScript +{ + PrepareSpellScript(spell_tos_infusion); + + std::vector spellsForTank{ SPELL_LIGHT_INFUSION, SPELL_FEL_INFUSION }; + + void CorrectTargets(std::list& targets) + { + if (!GetCaster()) + return; + + auto caster = GetCaster(); + + if (caster->IsCreature()) + caster->ToCreature()->AI()->Talk(SAY_INFUSION_END); + + targets.remove_if([](WorldObject* target) + { + return !target->IsPlayer(); + }); + + for (auto itr = targets.begin(); itr != targets.end(); ) + { + Unit* target = (*itr)->ToUnit(); + if (!target->IsPlayer()) + { + ++itr; + continue; + } + + for (auto spell : { SPELL_LIGHT_INFUSION, SPELL_FEL_INFUSION }) + target->ToUnit()->RemoveAurasDueToSpell(spell); + + if (Player* player = target->ToPlayer()) + if (ROLE_TANK == player->GetRoleForGroup() && !spellsForTank.empty()) // tanks have are different buffs + { + caster->CastSpell(player, spellsForTank.back(), true); + spellsForTank.pop_back(); + itr = targets.erase(itr); + continue; + } + + ++itr; + } + + uint8 i = 0; + for (auto target : targets) + { + if (!target->IsPlayer()) + continue; + + for (auto spell : { SPELL_LIGHT_INFUSION, SPELL_FEL_INFUSION }) + target->ToUnit()->RemoveAurasDueToSpell(spell); + + if (++i <= (targets.size() / 2)) + caster->CastSpell(target->ToUnit(), SPELL_LIGHT_INFUSION, true); + else + caster->CastSpell(target->ToUnit(), SPELL_FEL_INFUSION, true); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_infusion::CorrectTargets, SpellEffIndex::EFFECT_0, Targets::TARGET_UNIT_DEST_AREA_ENEMY); + } +}; + + + +// 248812 +class spell_tos_blowback : public AuraScript +{ + PrepareAuraScript(spell_tos_blowback); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (!GetCaster() || !GetTarget()) + return; + + if (GetCaster()->GetDistance2d(GetTarget()) >= 60.0f) + SetDuration(10); + else + SetDuration(1000*(60-GetCaster()->GetDistance2d(GetTarget())) * 12.0f / 60.0f); + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_tos_blowback::OnApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 234896, 236432 +class spell_tos_wrath_of_the_creators : public SpellScript +{ + PrepareSpellScript(spell_tos_wrath_of_the_creators); + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + if (!GetCaster()) + return; + + if (auto caster = GetCaster()) + { + uint32 count = caster->GetAuraCount(SPELL_WRATH_OF_THE_CREATORS_BUFF); + + SetHitDamage(GetHitDamage() * (1 + 0.05*count)); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tos_wrath_of_the_creators::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +// 235117 +class spell_tos_unstable_soul : public AuraScript +{ + PrepareAuraScript(spell_tos_unstable_soul); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (!GetTarget()) + return; + Unit* target = GetTarget(); + + if (target->GetAuraCount(235117) <= 1) + { + target->CastSpell(target, 249912); + return; + } + + Aura* aura = GetAura(); + + if (target->GetMap()->IsMythic()) + { + target->RemoveAppliedAuras(235117, [aura](AuraApplication const* aplication) -> bool + { + Aura const* curAura = aplication->GetBase(); + return curAura != aura; + }); + target->CastSpell(target, 249912); + } + else + target->RemoveAura(aura); + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_tos_unstable_soul::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 235213 235240 +class spell_tos_infusions : public AuraScript +{ + PrepareAuraScript(spell_tos_infusions); + + bool isFirstTick = true; + + void OnTick(AuraEffect const* /*aurEff*/) + { + Unit* target = GetTarget(); + if (!target) + return; + + + if (isFirstTick) + { + isFirstTick = false; + return; + } + else + { + target->RemoveAurasDueToSpell(GetSpellInfo()->Id == SPELL_FEL_INFUSION ? 241316 : 241315); + if (target->IsAlive()) + target->CastSpell(target, GetSpellInfo()->Id == SPELL_FEL_INFUSION ? 241316 : 241315, true); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_infusions::OnTick, EFFECT_0, SPELL_AURA_ANY); + } +}; + +// 241593 +class spell_egvin_levitation : public AuraScript +{ + PrepareAuraScript(spell_egvin_levitation); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (!GetTarget()) + return; + Unit* target = GetTarget(); + + if (!target->GetMap()->IsHeroicPlusRaid()) + target->CastSpell(target, 241565, true); + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_egvin_levitation::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + + + +void AddSC_boss_maiden_of_vigilance() +{ + RegisterCreatureAI(boss_maiden_of_vigilance); + RegisterCreatureAI(npc_tos_essences); + RegisterCreatureAI(npc_tos_essences_intro); + RegisterSpellScript(spell_tos_infusion); + RegisterAuraScript(spell_tos_blowback); + RegisterSpellScript(spell_tos_wrath_of_the_creators); + RegisterAuraScript(spell_tos_unstable_soul); + RegisterAuraScript(spell_tos_infusions); + RegisterAuraScript(spell_egvin_levitation); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_MistressSasszine.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_MistressSasszine.cpp new file mode 100644 index 0000000..d06e653 --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_MistressSasszine.cpp @@ -0,0 +1,1286 @@ +/* + https://uwow.biz/ +*/ + +#include "AreaTriggerAI.h" +#include "tomb_of_sargeras.h" + +enum Says +{ + SAY_AGGRO = 0, + SAY_THUNDERING_SHOCK = 1, + SAY_HYDRA_SHOT_WARN = 2, + SAY_SLICING_TORNADO = 3, + SAY_SLICING_TORNADO_WARN = 4, + SAY_PHASE_2 = 5, + SAY_CALL_VELLIUS = 6, + SAY_BECKON_SARUKEL = 7, + SAY_BECKON_SARUKEL_WARN = 8, + SAY_PHASE_3 = 9, + SAY_DEATH = 10, +}; + +enum Spells +{ + SPELL_BURDEN_OF_PAIN = 230201, + SPELL_BURDEN_OF_PAIN_SHARE_DMG = 230214, + SPELL_FROM_THE_ABYSS = 230227, + SPELL_HYDRA_SHOT_FILTER = 232753, + SPELL_HYDRA_SHOT_MARK = 230139, + SPELL_HYDRA_SHOT_DMG = 230143, + SPELL_HYDRA_ACID = 232754, + SPELL_HYDRA_ACID_STUN = 234332, + SPELL_SLICING_TORNADO_FILTER = 232722, + SPELL_SLICING_TORNADO_AT_1 = 232723, //unk + SPELL_SLICING_TORNADO_AT_2 = 234809, //Aura + SPELL_THUNDERING_SHOCK_FILTER = 230358, + SPELL_THUNDERING_SHOCK_SUMMON = 230360, + SPELL_CONSUMING_HUNGER_FILTER = 230384, //Set VehicleID to Player + SPELL_CONSUMING_HUNGER_SUMMON = 230855, + SPELL_CONSUMING_HUNGER_RIDE_VEH = 230920, + SPELL_BECKON_SARUKEL = 232746, + SPELL_SUMMON_OSSUNET = 232756, + SPELL_CALL_VELLIUS = 232757, + SPELL_BERSERK = 47008, + + //Mythic + SPELL_DELICIOUS_BUFFERFISH_DUMMY = 239369, + SPELL_DELICIOUS_BUFFERFISH_ENERGIZE = 239362, //Only Healer + SPELL_DELICIOUS_BUFFERFISH_MOD_DMG = 239375, //Only Damage Dealer + SPELL_DELICIOUS_BUFFERFISH_AT = 239388, + SPELL_DELICIOUS_BUFFERFISH_DROP = 239403, //Player drop Bufferfish + SPELL_SLIPPERY = 239420, //Debuff Penalty + SPELL_DREAD_SHARK_DUMMY = 239423, + SPELL_DREAD_SHARK_FILTER_1 = 239434, //Find Player + SPELL_DREAD_SHARK_FILTER_2 = 239435, //Find Npc + SPELL_DREAD_SHARK_DMG = 239436, + + //Abyss Stalker + SPELL_CONCEALING_MURK_AT = 230950, + SPELL_DARK_DEPTHS_FILTER = 230273, //Heroic+ + SPELL_DARK_DEPTHS_TELEPORT = 230274, + SPELL_DARK_DEPTHS_DMG = 230276, //Native name: "Jaws from the Deep" + + //Electrifying Jellyfish + SPELL_THUNDERING_SHOCK = 230362, + + //Razorjaw Waverunner + SPELL_WATER_BLAST = 241509, + SPELL_MURLOC_MASH = 242946, //Mythic + + //Sarukel + SPELL_DEVOURING_MAW_MOVEMENT_AT = 232745, //Force movement. Dmg Spell: 234621 + SPELL_DEVOURING_MAW_CIRCLE_AT = 232886, //Remove Spell: Befouling Ink - 232918 + SPELL_DEVOURING_MAW_CIRCLE_AT_2 = 232884, //Killing players + + //Ossunet + SPELL_OSSUNET_VISUAL = 234178, + SPELL_BEFOULING_INK_FILTER = 232903, + SPELL_BEFOULING_INK_MISSILE = 232902, + SPELL_BEFOULING_INK_MISSILE_2 = 233302, //Player casting + SPELL_BEFOULING_INK_AT = 232905, + SPELL_BEFOULING_DMG = 232913, + SPELL_BEFOULING_REMOVE = 232918, + + //Piranhado + SPELL_CRASHING_WAVE_AT = 240759, + SPELL_CRASHING_WAVE_DMG = 232827, +}; + +enum eEvents +{ + EVENT_BURDEN_OF_PAIN = 1, + EVENT_HYDRA_SHOT = 2, + EVENT_SLICING_TORNADO = 3, + EVENT_THUNDERING_SHOCK = 4, + EVENT_CONSUMING_HUNGER = 5, + EVENT_BECKON_SARUKEL = 6, + EVENT_SUMMON_OSSUNET = 7, + EVENT_CALL_VELLIUS = 8, +}; + +enum Achievement +{ + A_SAUCE, + A_HYDRA, + A_TARTAR, + A_MURLOC, + A_PLAYER, +}; + +Position const centrPos = { 5799.70f, -1248.48f, 2786.24f }; + +Position const sarukelPos[3] = +{ + {5771.40f, -1218.03f, 2785.67f, 5.48f}, + {5826.77f, -1275.80f, 2785.67f, 2.32f}, + {5770.53f, -1275.34f, 2785.67f, 0.76f} +}; + +Position const ossunetPos[3] = +{ + {5833.75f, -1260.85f, 2785.67f, 2.78f}, + {5762.73f, -1233.02f, 2785.67f, 5.91f}, + {5784.01f, -1282.96f, 2785.67f, 1.17f} +}; + +Position const piranhadoPos[8] = +{ + {5813.55f, -1211.53f, 2785.97f, 4.35f}, + {5833.01f, -1227.46f, 2785.97f, 3.70f}, + {5781.11f, -1212.40f, 2786.33f, 5.19f}, + {5761.98f, -1231.07f, 2786.33f, 5.84f}, + {5837.62f, -1261.57f, 2786.33f, 2.81f}, + {5818.79f, -1283.59f, 2786.33f, 2.06f}, + {5763.95f, -1267.11f, 2786.33f, 0.47f}, + {5784.16f, -1286.02f, 2786.33f, 1.17f}, +}; + +//115767 +struct boss_mistress_sasszine : BossAI +{ + explicit boss_mistress_sasszine(Creature* creature) : BossAI(creature, DATA_MISTRESS_SASSZINE) {} + + uint8 phase = 0; + uint8 healthPct = 0; + uint8 dreadSharkHealthPct = 0; + uint32 spawnFishTimer = 0; + uint32 berserkTimer = 0; + bool inkSauce = false; + bool hydra = false; + bool tartar = false; + bool murloc = false; + bool playerS = false; + std::list thunderingShockTimer; + std::list consumingHungerTimer; + std::list slicingTornadoTimer; + std::list summonOssunetTimer; + std::list callVelliusTimer; + + void Reset() override + { + _Reset(); + me->SetReactState(REACT_DEFENSIVE); + phase = 0; + healthPct = 71; + dreadSharkHealthPct = 86; + + for (uint8 i = 0; i < 3; ++i) + { + me->SummonCreature(NPC_SARUKEL_TRIGGER, sarukelPos[i]); + me->SummonCreature(NPC_OSSUNET_TRIGGER, ossunetPos[i]); + } + + for (uint8 i = 0; i < 8; ++i) + me->SummonCreature(NPC_PIRANHADO_TRIGGER, piranhadoPos[i]); + } + + void DoAction(int32 const action) override + { + switch (action) + { + case ACTION_1: + inkSauce = true; + break; + case ACTION_2: + hydra = true; + break; + case ACTION_3: + tartar = true; + break; + case ACTION_4: + murloc = true; + break; + case ACTION_5: + playerS = true; + break; + } + } + + uint32 GetData(uint32 type) const override + { + if (type == A_SAUCE) + return inkSauce ? 1 : 0; + if (type == A_HYDRA) + return hydra ? 1 : 0; + if (type == A_TARTAR) + return tartar ? 1 : 0; + if (type == A_MURLOC) + return murloc ? 1 : 0; + if (type == A_PLAYER) + return playerS ? 1 : 0; + return 0; + } + + void EnterCombat(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _EnterCombat(); + + ChangePhase(); + + if (IsMythicRaid()) + spawnFishTimer = 12000; + + if (!IsLfrRaid()) + berserkTimer = 8 * MINUTE * IN_MILLISECONDS; + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + _JustDied(); + + DoCast(me, 240056, true); + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + BossAI::EnterEvadeMode(); + me->InterruptNonMeleeSpells(false); + me->NearTeleportTo(me->GetHomePosition()); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_HYDRA_SHOT_MARK); + } + + void ChangePhase() + { + ++phase; + events.Reset(); + + switch (phase) + { + case 1: + events.RescheduleEvent(EVENT_THUNDERING_SHOCK, 10000); + events.RescheduleEvent(EVENT_CONSUMING_HUNGER, IsHeroicPlusRaid() ? 20000 : 64000); + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_HYDRA_SHOT, 26000); + + if (IsHeroicRaid()) + { + thunderingShockTimer = {36000, 38000, 32000}; + consumingHungerTimer = {34000, 38000}; + slicingTornadoTimer = {45000, 45000}; + events.RescheduleEvent(EVENT_BURDEN_OF_PAIN, 15000); + events.RescheduleEvent(EVENT_SLICING_TORNADO, 30000); + } + else if (IsMythicRaid()) + { + thunderingShockTimer = {36000, 32000}; + consumingHungerTimer = {36000, 34000}; + slicingTornadoTimer = {34000, 38000}; + events.RescheduleEvent(EVENT_BURDEN_OF_PAIN, 16000); + events.RescheduleEvent(EVENT_SLICING_TORNADO, 30000); + } + else + { + thunderingShockTimer = {44000, 32000}; + consumingHungerTimer = {34000}; + slicingTornadoTimer = {54000, 45000}; + events.RescheduleEvent(EVENT_BURDEN_OF_PAIN, 19000); + events.RescheduleEvent(EVENT_SLICING_TORNADO, 36000); + } + break; + case 2: + Talk(SAY_PHASE_2); + events.RescheduleEvent(EVENT_BURDEN_OF_PAIN, IsHeroicPlusRaid() ? 26000 : 28000); + events.RescheduleEvent(EVENT_BECKON_SARUKEL, IsHeroicPlusRaid() ? 40000 : 42000); + events.RescheduleEvent(EVENT_SUMMON_OSSUNET, 10000); + callVelliusTimer = {42000, 44000}; + + if (IsHeroicRaid()) + { + summonOssunetTimer = {42000}; + events.RescheduleEvent(EVENT_HYDRA_SHOT, 16000); + events.RescheduleEvent(EVENT_CALL_VELLIUS, 30000); + } + else if (IsMythicRaid()) + { + summonOssunetTimer = {42000}; + events.RescheduleEvent(EVENT_HYDRA_SHOT, 16000); + events.RescheduleEvent(EVENT_CALL_VELLIUS, 30000); + } + else + { + summonOssunetTimer = {50000, 38000, 42000}; + events.RescheduleEvent(EVENT_CALL_VELLIUS, 36000); + + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_HYDRA_SHOT, 18000); + } + break; + case 3: + Talk(SAY_PHASE_3); + events.RescheduleEvent(EVENT_BURDEN_OF_PAIN, IsHeroicPlusRaid() ? 26000 : 28000); + events.RescheduleEvent(EVENT_SUMMON_OSSUNET, 10000); + + if (IsHeroicRaid()) + { + consumingHungerTimer = {32000, 48000, 42000}; + slicingTornadoTimer = {46000, 48000, 44000}; + summonOssunetTimer = {38000, 32000, 34000, 32000, 34000}; + callVelliusTimer = {56000, 38000, 44000, 32000}; + events.RescheduleEvent(EVENT_CONSUMING_HUNGER, 40000); + events.RescheduleEvent(EVENT_HYDRA_SHOT, 16000); + events.RescheduleEvent(EVENT_SLICING_TORNADO, 58000); + events.RescheduleEvent(EVENT_CALL_VELLIUS, 32000); + } + else if (IsMythicRaid()) + { + consumingHungerTimer = {70000, 36000, 32000}; + slicingTornadoTimer = {34000, 36000, 34000}; + summonOssunetTimer = {28000, 32000, 34000, 28000, 34000}; + callVelliusTimer = {32000, 34000, 46000, 32000}; + events.RescheduleEvent(EVENT_CONSUMING_HUNGER, 46000); + events.RescheduleEvent(EVENT_HYDRA_SHOT, 16000); + events.RescheduleEvent(EVENT_SLICING_TORNADO, 52000); + events.RescheduleEvent(EVENT_CALL_VELLIUS, 30000); + } + else + { + consumingHungerTimer = {32000}; + slicingTornadoTimer = {82000, 44000, 46000}; + summonOssunetTimer = {34000, 34000, 32000, 32000, 34000}; + callVelliusTimer = {56000, 76000, 44000, 32000}; + events.RescheduleEvent(EVENT_CONSUMING_HUNGER, 52000); + events.RescheduleEvent(EVENT_SLICING_TORNADO, 52000); + events.RescheduleEvent(EVENT_CALL_VELLIUS, 36000); + + if (!IsLfrRaid()) + events.RescheduleEvent(EVENT_HYDRA_SHOT, 18000); + } + break; + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (phase < 3 && me->HealthBelowPct(healthPct)) + { + healthPct -= 30; + ChangePhase(); + + if (!IsMythicRaid()) + me->CastSpell(me, SPELL_DREAD_SHARK_DUMMY, true); + } + + if (IsMythicRaid()) + { + if (me->HealthBelowPct(dreadSharkHealthPct)) + { + dreadSharkHealthPct -= 15; + spawnFishTimer = 22000; + me->CastSpell(me, SPELL_DREAD_SHARK_DUMMY, true); + } + } + } + + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + switch (summon->GetEntry()) + { + case NPC_ABYSS_STALKER: + summon->CastSpell(summon, SPELL_CONCEALING_MURK_AT, true, nullptr, nullptr, me->GetGUID()); + break; + } + } + + void SpellFinishCast(const SpellInfo* spell) override + { + switch (spell->Id) + { + case SPELL_BURDEN_OF_PAIN: + { + for (uint8 i = 0; i < 3; ++i) + DoCast(me, SPELL_FROM_THE_ABYSS, true); + break; + } + case SPELL_BECKON_SARUKEL: + if (auto sarukel = me->FindNearestCreature(NPC_SARUKEL_TRIGGER, 90.0f)) + sarukel->GetAI()->DoAction(ACTION_2); + break; + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_HYDRA_SHOT_FILTER: + DoCast(target, SPELL_HYDRA_SHOT_MARK, true); + break; + case SPELL_SLICING_TORNADO_FILTER: + { + float x, y; + float dist = 2.5f; + float angle = 4.71f; + float orientation = centrPos.GetAngle(target) - M_PI; + + for (uint8 i = 0; i < 12; ++i) + { + if (i == 6) + { + angle = 1.57f; + dist = 2.5f; + } + + target->GetNearPoint2D(x, y, dist, angle + orientation); + me->SummonCreature(NPC_TORNADO_TRIGGER, x, y, target->GetPositionZ(), orientation); + dist += 8.0f; + } + break; + } + case SPELL_THUNDERING_SHOCK_FILTER: + DoCast(target, SPELL_THUNDERING_SHOCK_SUMMON, true); + break; + case SPELL_CONSUMING_HUNGER_FILTER: + if (auto summon = me->SummonCreature(NPC_RAZORJAW_WAVERUNNER, target->GetPosition())) + { + ObjectGuid targetGuid = target->GetGUID(); + summon->AddDelayedEvent(200, [summon, targetGuid] () -> void + { + if (summon) + if (Unit* target = ObjectAccessor::GetUnit(*summon, targetGuid)) + summon->CastSpell(target, SPELL_CONSUMING_HUNGER_RIDE_VEH, true); + }); + } + break; + case SPELL_DREAD_SHARK_FILTER_1: + case SPELL_DREAD_SHARK_FILTER_2: + target->CastSpell(target, SPELL_DREAD_SHARK_DMG, true); + break; + } + } + + void OnRemoveAuraTarget(Unit* target, uint32 spellId, AuraRemoveMode mode) override + { + if (me->IsInCombat() && mode == AURA_REMOVE_BY_EXPIRE && target) + { + switch (spellId) + { + case SPELL_HYDRA_SHOT_MARK: + DoCast(target, SPELL_HYDRA_SHOT_DMG, true); + break; + case SPELL_DREAD_SHARK_DUMMY: + DoCast(me, SPELL_DREAD_SHARK_FILTER_1, true); + DoCast(me, SPELL_DREAD_SHARK_FILTER_2, true); + break; + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (spawnFishTimer) + { + if (spawnFishTimer <= diff) + { + spawnFishTimer = 0; + + Position pos; + float angle = 0.0f; + for (uint8 i = 0; i < 5; ++i) + { + centrPos.SimplePosXYRelocationByAngle(pos, 25.0f, angle); + me->SummonCreature(NPC_DELICIOUS_BUFFERFISH, pos); + angle += 1.256f; + } + } + else + spawnFishTimer -= diff; + } + + if (berserkTimer) + { + if (berserkTimer <= diff) + { + berserkTimer = 0; + DoCast(me, SPELL_BERSERK, true); + } + else + berserkTimer -= diff; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 44.0f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BURDEN_OF_PAIN: + DoCastTopAggro(SPELL_BURDEN_OF_PAIN); + events.RescheduleEvent(EVENT_BURDEN_OF_PAIN, phase == 1 ? 28000 : (IsHeroicPlusRaid() ? 30000 : 40000)); + break; + case EVENT_HYDRA_SHOT: + Talk(SAY_HYDRA_SHOT_WARN); + DoCast(me, SPELL_HYDRA_SHOT_FILTER, true); + events.RescheduleEvent(EVENT_HYDRA_SHOT, IsMythicRaid() ? 30000 : 40000); + break; + case EVENT_SLICING_TORNADO: + Talk(SAY_SLICING_TORNADO); + Talk(SAY_SLICING_TORNADO_WARN); + DoCast(SPELL_SLICING_TORNADO_FILTER); + events.RescheduleEvent(EVENT_SLICING_TORNADO, slicingTornadoTimer.front()); + if (slicingTornadoTimer.size() > 1) + slicingTornadoTimer.pop_front(); + break; + case EVENT_THUNDERING_SHOCK: + Talk(SAY_THUNDERING_SHOCK); + DoCast(SPELL_THUNDERING_SHOCK_FILTER); + events.RescheduleEvent(EVENT_THUNDERING_SHOCK, thunderingShockTimer.front()); + if (thunderingShockTimer.size() > 1) + thunderingShockTimer.pop_front(); + break; + case EVENT_CONSUMING_HUNGER: + DoCast(SPELL_CONSUMING_HUNGER_FILTER); + events.RescheduleEvent(EVENT_CONSUMING_HUNGER, consumingHungerTimer.front()); + if (consumingHungerTimer.size() > 1) + consumingHungerTimer.pop_front(); + break; + case EVENT_BECKON_SARUKEL: + { + Talk(SAY_BECKON_SARUKEL); + Talk(SAY_BECKON_SARUKEL_WARN); + EntryCheckPredicate pred(NPC_SARUKEL_TRIGGER); + summons.DoAction(ACTION_1, pred); + DoCast(SPELL_BECKON_SARUKEL); + events.RescheduleEvent(EVENT_BECKON_SARUKEL, 42000); + break; + } + case EVENT_SUMMON_OSSUNET: + DoCast(SPELL_SUMMON_OSSUNET); + events.RescheduleEvent(EVENT_SUMMON_OSSUNET, summonOssunetTimer.front()); + if (summonOssunetTimer.size() > 1) + summonOssunetTimer.pop_front(); + break; + case EVENT_CALL_VELLIUS: + Talk(SAY_CALL_VELLIUS); + DoCast(SPELL_CALL_VELLIUS); + events.RescheduleEvent(EVENT_CALL_VELLIUS, callVelliusTimer.front()); + if (callVelliusTimer.size() > 1) + callVelliusTimer.pop_front(); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//115795 +struct npc_sasszine_abyss_stalker : public ScriptedAI +{ + explicit npc_sasszine_abyss_stalker(Creature* creature) : ScriptedAI(creature) {} + + EventMap events; + + void Reset() override {} + + void IsSummonedBy(Unit* summoner) override + { + if (IsHeroicPlusRaid()) + events.RescheduleEvent(EVENT_1, 10000); //Dark Depths + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_DARK_DEPTHS_FILTER: + me->CastSpell(target, SPELL_DARK_DEPTHS_TELEPORT, true); + me->CastSpell(target, SPELL_DARK_DEPTHS_DMG, true); + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + DoCast(SPELL_DARK_DEPTHS_FILTER); + events.RescheduleEvent(EVENT_1, 14000); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//118286 +struct npc_sasszine_slicing_tornado : public ScriptedAI +{ + explicit npc_sasszine_slicing_tornado(Creature* creature) : ScriptedAI(creature) + { + me->SetDisplayId(78177); + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetFloatValue(UNIT_FIELD_COMBATREACH, 1.0f); + me->SetSpeed(MOVE_FLIGHT, 0.75f); + } + + uint32 startTimer = 0; + + void Reset() override {} + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_SLICING_TORNADO_AT_2, true); + startTimer = 1000; + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == 1) + me->DespawnOrUnsummon(); + } + + void UpdateAI(uint32 diff) override + { + if (startTimer) + { + if (startTimer <= diff) + { + startTimer = 0; + float x, y; + me->GetNearPoint2D(x, y, 80.0f, me->GetOrientation()); + me->GetMotionMaster()->MovePoint(1, x, y, me->GetPositionZ(), false); + } + else + startTimer -= diff; + } + } +}; + +//115896 +struct npc_sasszine_electrifying_jellyfish : public ScriptedAI +{ + explicit npc_sasszine_electrifying_jellyfish(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void Reset() override {} + + void IsSummonedBy(Unit* summoner) override + { + me->CastSpellDelay(me, SPELL_THUNDERING_SHOCK, false, 100); + } + + void SpellFinishCast(const SpellInfo* spell) override + { + if (spell->Id == SPELL_THUNDERING_SHOCK) + { + me->AddDelayedEvent(1000, [this] () -> void + { + me->SetVisible(false); + }); + } + } + + void UpdateAI(uint32 diff) override {} +}; + +//115902 +struct npc_sasszine_razorjaw_waverunner : public ScriptedAI +{ + explicit npc_sasszine_razorjaw_waverunner(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + } + + EventMap events; + uint32 checkVehicleTimer = 0; + + void Reset() override {} + + void IsSummonedBy(Unit* summoner) override + { + checkVehicleTimer = 3000; + } + + void UpdateAI(uint32 diff) override + { + if (checkVehicleTimer) + { + if (checkVehicleTimer <= diff) + { + checkVehicleTimer = 2000; + + if (!me->IsOnVehicle()) + { + checkVehicleTimer = 0; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + DoZoneInCombat(me, 100.0f); + + if (IsMythicRaid()) + DoCast(me, SPELL_MURLOC_MASH, true); + } + } + else + checkVehicleTimer -= diff; + } + + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + DoSpellAttackIfReady(SPELL_WATER_BLAST); + } +}; + +//116843 +struct npc_sasszine_sarukel : public ScriptedAI +{ + explicit npc_sasszine_sarukel(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + } + + uint32 checkTimer = 300; + InstanceScript* instance; + + void Reset() override {} + + void DoAction(int32 const actionID) + { + if (actionID == ACTION_1) + { + me->RemoveAllAreaTriggers(); + me->RemoveAllAuras(); + me->KillAllDelayedCombats(); + } + + if (actionID == ACTION_2) + { + me->CastSpell(me, SPELL_DEVOURING_MAW_MOVEMENT_AT, true); + me->CastSpell(me, SPELL_DEVOURING_MAW_CIRCLE_AT, true); + me->CastSpellDelay(me, SPELL_DEVOURING_MAW_CIRCLE_AT_2, true, 3000); + } + } + + void OnAreaTriggerDespawn(uint32 spellId, Position pos, bool duration) override + { + if (spellId == SPELL_DEVOURING_MAW_CIRCLE_AT) + { + DoAction(ACTION_1); + } + } + + void UpdateAI(uint32 diff) override + { + if (!me->HasAura(SPELL_DEVOURING_MAW_CIRCLE_AT_2)) + return; + + if (checkTimer <= diff) + { + if (auto stalker = me->FindNearestCreature(NPC_ABYSS_STALKER, 3.0f)) + { + if (auto sasszine = instance->instance->GetCreature(instance->GetGuidData(DATA_MISTRESS_SASSZINE))) + { + if (sasszine->IsAIEnabled) + { + if (sasszine->IsAIEnabled) + { + sasszine->AI()->DoAction(ACTION_3); + stalker->ToCreature()->DespawnOrUnsummon(500); + } + } + } + } + + if (auto waverunner = me->FindNearestCreature(NPC_RAZORJAW_WAVERUNNER, 3.0f)) + { + if (auto sasszine = instance->instance->GetCreature(instance->GetGuidData(DATA_MISTRESS_SASSZINE))) + { + if (sasszine->IsAIEnabled) + { + sasszine->AI()->DoAction(ACTION_4); + waverunner->ToCreature()->DespawnOrUnsummon(500); + } + } + } + + if (me->FindNearestPlayer(3.5f, true)) + { + if (auto sasszine = instance->instance->GetCreature(instance->GetGuidData(DATA_MISTRESS_SASSZINE))) + { + if (sasszine->IsAIEnabled) + { + sasszine->AI()->DoAction(ACTION_5); + } + } + } + + if (auto player = me->FindNearestPlayer(7.0f, true)) + if (player->HasAura(SPELL_HYDRA_ACID)) + if (auto sasszine = instance->instance->GetCreature(instance->GetGuidData(DATA_MISTRESS_SASSZINE))) + if (sasszine->IsAIEnabled) + sasszine->AI()->DoAction(ACTION_2); + + checkTimer = 300; + } + else checkTimer -= diff; + } +}; + +//116881 +struct npc_sasszine_ossunet : public ScriptedAI +{ + explicit npc_sasszine_ossunet(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void Reset() override {} + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SUMMON_OSSUNET) + { + DoCast(me, SPELL_OSSUNET_VISUAL, true); + DoCast(me, SPELL_BEFOULING_INK_FILTER, true); + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (spell->Id == SPELL_BEFOULING_INK_FILTER) + { + if (auto owner = me->GetAnyOwner()) + me->CastSpell(target, SPELL_BEFOULING_INK_MISSILE, true, nullptr, nullptr, owner->GetGUID()); + } + } + + void UpdateAI(uint32 diff) override {} +}; + +//116841 +struct npc_sasszine_piranhado : public ScriptedAI +{ + explicit npc_sasszine_piranhado(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void Reset() override {} + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_CALL_VELLIUS) + { + me->CastSpell(me, SPELL_CRASHING_WAVE_AT, true, nullptr, nullptr, caster->GetGUID()); + me->CastSpell(me, SPELL_CRASHING_WAVE_DMG, false); + } + } + + void UpdateAI(uint32 diff) override {} +}; + +//119791 +struct npc_sasszine_delicious_bufferfish : public ScriptedAI +{ + explicit npc_sasszine_delicious_bufferfish(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void Reset() override {} + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_DELICIOUS_BUFFERFISH_AT, true); + } + + void OnAreaTriggerCast(Unit* caster, Unit* target, uint32 spellId, uint32 createATSpellId) override + { + if (spellId == SPELL_DELICIOUS_BUFFERFISH_DUMMY) + me->DespawnOrUnsummon(100); + } + + void SpellFinishCast(const SpellInfo* spell) override + { + if (spell->Id == SPELL_DREAD_SHARK_DMG) + { + me->RemoveAurasDueToSpell(SPELL_DELICIOUS_BUFFERFISH_AT); + me->DespawnOrUnsummon(500); + } + } + + void UpdateAI(uint32 diff) override {} +}; + +//230143 +class spell_sasszine_hydra_shot_filter : public SpellScript +{ + PrepareSpellScript(spell_sasszine_hydra_shot_filter); + + uint32 targetsSize = 1; + + void FilterTargets(std::list& targets) + { + if (!targets.empty()) + targetsSize = targets.size(); + } + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + SetHitDamage(GetHitDamage() / targetsSize); + + if (GetCaster() && GetHitUnit()) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_HYDRA_ACID, true); + + if (GetHitUnit()->GetAuraCount(SPELL_HYDRA_ACID) >= 2) + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_HYDRA_ACID_STUN, true); + } + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sasszine_hydra_shot_filter::FilterTargets, EFFECT_0, TARGET_UNIT_BETWEEN_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_sasszine_hydra_shot_filter::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +//230201 +class spell_sasszine_burden_of_pain : public AuraScript +{ + PrepareAuraScript(spell_sasszine_burden_of_pain); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + amount = -1; + } + + void Absorb(AuraEffect* /*AuraEffect**/, DamageInfo& dmgInfo, uint32& absorbAmount) + { + absorbAmount = 0; + + auto target = GetUnitOwner(); + auto attacker = dmgInfo.GetAttacker(); + if (!target || !attacker || target->GetGUID() == attacker->GetGUID()) + return; + + if (dmgInfo.GetSchoolMask()== SPELL_SCHOOL_MASK_NORMAL) + { + float damage = dmgInfo.GetDamage(); + int32 bp0 = int32(damage); + target->CastCustomSpell(target, SPELL_BURDEN_OF_PAIN_SHARE_DMG, &bp0, NULL, NULL, true); + } + } + + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_sasszine_burden_of_pain::CalculateAmount, EFFECT_1, SPELL_AURA_SCHOOL_ABSORB); + OnEffectAbsorb += AuraEffectAbsorbFn(spell_sasszine_burden_of_pain::Absorb, EFFECT_1); + } +}; + +//239369 +class spell_sasszine_delicious_bufferfish : public AuraScript +{ + PrepareAuraScript(spell_sasszine_delicious_bufferfish); + + bool specHealer = false; + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (auto player = GetTarget()->ToPlayer()) + { + specHealer = player->GetRoleForGroup() == ROLE_HEALER; + + if (specHealer) + player->CastSpell(player, SPELL_DELICIOUS_BUFFERFISH_ENERGIZE, true); + else + player->CastSpell(player, SPELL_DELICIOUS_BUFFERFISH_MOD_DMG, true); + } + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()) + { + GetTarget()->RemoveAurasDueToSpell(SPELL_DELICIOUS_BUFFERFISH_ENERGIZE); + GetTarget()->RemoveAurasDueToSpell(SPELL_DELICIOUS_BUFFERFISH_MOD_DMG); + + if (InstanceScript* instance = GetTarget()->GetInstanceScript()) + { + if (Creature* sasszine = instance->instance->GetCreature(instance->GetGuidData(DATA_MISTRESS_SASSZINE))) + GetTarget()->CastSpell(GetTarget(), SPELL_DELICIOUS_BUFFERFISH_DROP, true, nullptr, nullptr, sasszine->GetGUID()); + } + } + } + + void OnTick(AuraEffect const* /*aurEff*/) + { + if (Unit* target = GetUnitOwner()) + { + if (auto aura = target->GetAura(specHealer ? SPELL_DELICIOUS_BUFFERFISH_ENERGIZE : SPELL_DELICIOUS_BUFFERFISH_MOD_DMG)) + { + if (aura->GetStackAmount() < 50) + aura->ModStackAmount(1); + } + } + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_sasszine_delicious_bufferfish::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_sasszine_delicious_bufferfish::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_sasszine_delicious_bufferfish::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +//232913 +class spell_sasszine_befouling_ink : public AuraScript +{ + PrepareAuraScript(spell_sasszine_befouling_ink); + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) + return; + + if (GetTarget()) + { + if (auto instance = GetTarget()->GetInstanceScript()) + { + if (instance->GetBossState(DATA_MISTRESS_SASSZINE) == IN_PROGRESS) + { + if (auto sasszine = instance->instance->GetCreature(instance->GetGuidData(DATA_MISTRESS_SASSZINE))) + GetTarget()->CastSpell(GetTarget(), SPELL_BEFOULING_INK_MISSILE_2, true, nullptr, nullptr, sasszine->GetGUID()); + } + } + } + } + + void Register() + { + OnEffectRemove += AuraEffectRemoveFn(spell_sasszine_befouling_ink::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } +}; + +//1044600 +struct at_sasszine_murloc_mash : AreaTriggerAI +{ + at_sasszine_murloc_mash(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + auto unitOwner = at->GetTarget(); + if (!target || unitOwner->GetGUID() == target->GetGUID() || target->IsPlayer() || target->GetEntry() != NPC_RAZORJAW_WAVERUNNER || target->IsOnVehicle()) + return false; + + return true; + } +}; + +//1004800 +struct at_sasszine_delicious_bufferfish : AreaTriggerAI +{ + at_sasszine_delicious_bufferfish(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + if (!target || !target->IsPlayer() || target->HasAura(SPELL_DELICIOUS_BUFFERFISH_DUMMY) || target->HasAura(SPELL_SLIPPERY)) + return false; + + return true; + } +}; + +//13368 +struct at_sasszine_devouring_maw : AreaTriggerAI +{ + at_sasszine_devouring_maw(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + uint8 chargeCount = 0; + uint8 chargeMaxCount = 0; + + void OnCreate() override + { + if (auto caster = at->GetCaster()) + chargeMaxCount = caster->GetMap()->IsMythic() ? 5 : 3; + } + + void ActionOnUpdate(GuidList& affectedPlayers) override + { + auto caster = at->GetCaster(); + if (!caster || chargeCount >= chargeMaxCount) + { + at->Remove(); + return; + } + + for (auto const& guid : at->GetInsideUnits()) + { + if (auto const& player = ObjectAccessor::GetPlayer(*caster, guid)) + { + if (player->HasAura(SPELL_BEFOULING_DMG)) + { + if (auto instance = at->GetInstanceScript()) + if (auto sasszine = instance->instance->GetCreature(instance->GetGuidData(DATA_MISTRESS_SASSZINE))) + if (sasszine->IsAIEnabled) + sasszine->AI()->DoAction(ACTION_1); + } + if (auto aura = player->GetAura(SPELL_BEFOULING_DMG)) + { + aura->Remove(); + caster->CastSpell(player, SPELL_BEFOULING_REMOVE, true); + + + if (++chargeCount >= chargeMaxCount) + { + at->Remove(); + return; + } + } + } + } + } +}; + +// 36143 +class achievement_sasszine_hydra : public AchievementCriteriaScript +{ +public: + achievement_sasszine_hydra() : AchievementCriteriaScript("achievement_sasszine_hydra") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* boss = target->ToCreature()) + if (boss->GetAI()->GetData(A_HYDRA)) + return true; + + return false; + } +}; + +// 36144 +class achievement_sasszine_tartar : public AchievementCriteriaScript +{ +public: + achievement_sasszine_tartar() : AchievementCriteriaScript("achievement_sasszine_tartar") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* boss = target->ToCreature()) + if (boss->GetAI()->GetData(A_HYDRA)) + return true; + + return false; + } +}; + +// 36146 +class achievement_sasszine_sauce : public AchievementCriteriaScript +{ +public: + achievement_sasszine_sauce() : AchievementCriteriaScript("achievement_sasszine_sauce") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* boss = target->ToCreature()) + if (boss->GetAI()->GetData(A_SAUCE)) + return true; + + return false; + } +}; + +// 36145 +class achievement_sasszine_murloc : public AchievementCriteriaScript +{ +public: + achievement_sasszine_murloc() : AchievementCriteriaScript("achievement_sasszine_murloc") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* boss = target->ToCreature()) + if (boss->GetAI()->GetData(A_MURLOC)) + return true; + + return false; + } +}; + +// 36147 +class achievement_sasszine_player : public AchievementCriteriaScript +{ +public: + achievement_sasszine_player() : AchievementCriteriaScript("achievement_sasszine_player") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* boss = target->ToCreature()) + if (boss->GetAI()->GetData(A_PLAYER)) + return true; + + return false; + } +}; + +void AddSC_boss_mistress_sasszine() +{ + RegisterCreatureAI(boss_mistress_sasszine); + RegisterCreatureAI(npc_sasszine_abyss_stalker); + RegisterCreatureAI(npc_sasszine_slicing_tornado); + RegisterCreatureAI(npc_sasszine_electrifying_jellyfish); + RegisterCreatureAI(npc_sasszine_razorjaw_waverunner); + RegisterCreatureAI(npc_sasszine_sarukel); + RegisterCreatureAI(npc_sasszine_ossunet); + RegisterCreatureAI(npc_sasszine_piranhado); + RegisterCreatureAI(npc_sasszine_delicious_bufferfish); + RegisterSpellScript(spell_sasszine_hydra_shot_filter); + RegisterAuraScript(spell_sasszine_burden_of_pain); + RegisterAuraScript(spell_sasszine_delicious_bufferfish); + RegisterAuraScript(spell_sasszine_befouling_ink); + RegisterAreaTriggerAI(at_sasszine_murloc_mash); + RegisterAreaTriggerAI(at_sasszine_delicious_bufferfish); + RegisterAreaTriggerAI(at_sasszine_devouring_maw); + new achievement_sasszine_hydra(); + new achievement_sasszine_tartar(); + new achievement_sasszine_sauce(); + new achievement_sasszine_murloc(); + new achievement_sasszine_player(); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_SistersoftheMoon.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_SistersoftheMoon.cpp new file mode 100644 index 0000000..35dd0a7 --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_SistersoftheMoon.cpp @@ -0,0 +1,1445 @@ +/* + * This file is part of the DestinyCore Project. See AUTHORS file for Copyright information + * + * 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 "AreaTriggerAI.h" +#include "tomb_of_sargeras.h" + +enum Says +{ + //Captain Yathae Moonstrike + SAY_TWILIGHT_VOLLEY = 0, + SAY_INCORPOREAL_SHOT_WARN = 1, + SAY_RAPID_SHOT = 2, + + //Priestess Lunaspyre + SAY_EMBRACE_ECLIPSE_WARN = 0, + SAY_EMBRACE_ECLIPSE = 1, + SAY_MOON_BURN = 2, +}; + +enum Spells +{ + SPELL_SHARED_HEALTH = 235215, + SPELL_PHASE_1_CONVERSATION = 243034, + SPELL_PHASE_2_CONVERSATION = 243044, + SPELL_PHASE_3_CONVERSATION = 243047, + SPELL_INCORPOREAL_TELEPORT = 236115, + SPELL_LUNAR_GHOST = 235268, + SPELL_LUNAR_GHOST_CAT = 235270, + SPELL_CORPOREAL_GHOST = 234999, + SPELL_CORPOREAL_GHOST_MOUNT = 236324, + SPELL_BERSERK = 243262, + + SPELL_FONT_OF_ELUNE_AT = 236357, + SPELL_LIGHT_SIDE_MOON_AT = 234565, + SPELL_LIGHT_SIDE_MOON_WANING = 234587, + SPELL_LIGHT_SIDE_MOON_DUMMY = 234664, + SPELL_LUNAR_SUFFUSION = 234995, + SPELL_DARK_SIDE_MOON_AT = 234657, + SPELL_DARK_SIDE_MOON_WANING_AT = 234659, + SPELL_DARK_SIDE_MOON_DUMMY = 234668, + SPELL_UMBRA_SUFFUSION = 234996, + SPELL_ASTRAL_PURGE = 234998, + SPELL_ASTRAL_VULNERABILITY = 236330, //Mythic + + //Huntress Kasparian + SPELL_MOON_GLAIVE = 236547, + SPELL_DISCORPORATE = 236550, + SPELL_TWILIGHT_GLAIVE_FILTER = 237561, + SPELL_TWILIGHT_GLAIVE_AT = 236529, //Dmg 236541 + SPELL_SABERLEAP = 236344, + SPELL_SPECTRAL_GLAIVE = 237633, + SPELL_GLAIVE_STORM_FILTER_BIG = 239378, + SPELL_GLAIVE_STORM_FILTER_MEDIUM = 239382, + SPELL_GLAIVE_STORM_FILTER_SMALL = 239385, + SPELL_GLAIVE_STORM_AT_BIG = 239379, + SPELL_GLAIVE_STORM_AT_MEDIUM = 239383, + SPELL_GLAIVE_STORM_AT_SMALL = 239386, + + //Priestess Lunaspyre + SPELL_MOON_BURN_FILTER = 236518, + SPELL_MOON_BURN_DMG = 236519, + SPELL_LUNAR_STRIKE = 237632, + SPELL_LUNAR_BEACON = 236712, + SPELL_LUNAR_BARRAGE_MARK = 236717, + SPELL_LUNAR_BARRAGE_AT = 236726, + SPELL_LUNAR_FIRE = 239264, + SPELL_EMBRACE_ECLIPSE = 233263, + SPELL_EMBRACE_ECLIPSE_FRIEND = 233264, + SPELL_LUNAR_DETONATION = 237782, + SPELL_UMBRA_DETONATION = 233284, + + //Captain Yathae Moonstrike + SPELL_INCORPOREAL_SHOT_FILTER = 236304, + SPELL_INCORPOREAL_SHOT_MARK = 236305, + SPELL_INCORPOREAL_SHOT_DMG_BETWEEN = 236306, + SPELL_INCORPOREAL_SHOT_DMG_AOE = 248911, + SPELL_INCORPOREAL_SHOT_VISUAL = 237570, + SPELL_TWILIGHT_VOLLEY_AT = 236442, + SPELL_SHADOW_SHOT = 237630, + SPELL_CALL_MOONTALON = 236694, + SPELL_RAPID_SHOT_FILTER_MARK = 236672, + SPELL_RAPID_SHOT_FILTER_DMG = 236600, + SPELL_RAPID_SHOT_MARK = 236596, + SPELL_RAPID_SHOT_DMG = 236603, + + //Moontalon + SPELL_MOONTALON_DISSOLVE_IN = 239960, + SPELL_DEATHLY_SCREECH = 236697, + + //achieve add + SPELL_CRY_OF_THE_LOST_SOULS = 242494, +}; + +enum eEvents +{ + //Huntress Kasparian + EVENT_MOON_GLAIVE = 1, + EVENT_TWILIGHT_GLAIVE = 2, + EVENT_JUMP_KASPARIAN = 3, + EVENT_SPECTRAL_GLAIVE = 4, + EVENT_GLAIVE_STORM = 5, + + //Priestess Lunaspyre + EVENT_MOON_BURN = 1, + EVENT_LUNAR_STRIKE = 2, + EVENT_TP_LUNASPERE = 3, + EVENT_LUNAR_BEACON = 4, + EVENT_LUNAR_FIRE = 5, + EVENT_EMBRACE_ECLIPSE = 6, + + //Captain Yathae + EVENT_INCORPOREAL_SHOT = 1, + EVENT_TWILIGHT_VOLLEY = 2, + EVENT_SHADOW_SHOT = 3, + EVENT_TP_YATHAE = 4, + EVENT_CALL_MOONTALON = 5, + EVENT_RAPID_SHOT = 6, +}; + +enum Achieve { ACHIEVEMENT = 1 }; + +uint32 const sistersEntry[3] = +{ + NPC_HUNTRESS_KASPARIAN, + NPC_PRIESTESS_LUNASPYRE, + NPC_CAPTAIN_YATHAE +}; + +Position const sistersPos[3] = +{ + {6208.61f, -1026.84f, 2899.56f, 2.77f}, + {6208.68f, -1037.31f, 2899.55f, 2.97f}, + {6215.13f, -1019.96f, 2899.55f, 2.66f} +}; + +Position const centrPos = {6190.06f, -1020.03f, 2899.50f}; + +//118182 +struct boss_sisters_of_the_moon : BossAI +{ + explicit boss_sisters_of_the_moon(Creature* creature) : BossAI(creature, DATA_SISTERS_OF_THEMOON) + { + if (me->IsAlive()) + { + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC); + me->AddDelayedCombat(500, [=] () -> void { DoAction(ACTION_1); }); + } + } + + std::vector sistersVector; + bool lightSideMoon = true; + uint8 sisterDiedCount = 0; + uint32 sideMoonTimer = 0; + uint32 berserkTimer = 0; + + void Reset() override + { + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) != NOT_STARTED) + instance->SetBossState(DATA_SISTERS_OF_THEMOON, NOT_STARTED); + } + + void DoAction(int32 const action) override + { + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) == DONE) + return; + + switch (action) + { + case ACTION_1: //Summon Sister + { + sistersVector.clear(); + sisterDiedCount = 0; + for (uint8 i = 0; i < 3; ++i) + if (auto sister = me->SummonCreature(sistersEntry[i], sistersPos[i])) + { + sistersVector.push_back(sister->GetGUID()); + sister->CastSpell(sister, SPELL_SHARED_HEALTH, true); + } + break; + } + case ACTION_2: //Despawn after evade + { + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) != NOT_STARTED) + { + instance->SetBossState(DATA_SISTERS_OF_THEMOON, NOT_STARTED); + + if (auto kasparian = ObjectAccessor::GetCreature(*me, sistersVector[0])) + ; // frame instance start/end non supporte par notre core + sideMoonTimer = 0; + berserkTimer = 0; + me->RemoveAurasDueToSpell(SPELL_FONT_OF_ELUNE_AT); + me->RemoveAllAreaTriggers(); + + for (auto const& guid : sistersVector) + { + if (auto sister = ObjectAccessor::GetCreature(*me, guid)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, sister); + sister->AI()->DoAction(ACTION_2); //Despawn summons + sister->SetReactState(REACT_PASSIVE); + sister->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + sister->DespawnOrUnsummon(200); + } + } + AddDelayedCombat(5000, [=] () -> void { DoAction(ACTION_1); }); + } + break; + } + case ACTION_3: //Enter Combat + { + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) != IN_PROGRESS) + { + instance->SetBossState(DATA_SISTERS_OF_THEMOON, IN_PROGRESS); + + if (auto kasparian = ObjectAccessor::GetCreature(*me, sistersVector[0])) + ; // frame instance start/end non supporte par notre core + DoCast(me, SPELL_FONT_OF_ELUNE_AT, true); + DoCast(me, SPELL_DARK_SIDE_MOON_AT, true); + lightSideMoon = true; + sideMoonTimer = 48000; + + if (IsMythicRaid()) + berserkTimer = 11 * MINUTE * IN_MILLISECONDS; + + for (auto const& guid : sistersVector) + { + if (auto sister = ObjectAccessor::GetCreature(*me, guid)) + if (sister->IsAlive() && !sister->IsInCombat()) + sister->AI()->DoZoneInCombat(sister, 100.0f); + } + } + break; + } + case ACTION_4: //Phase 2 + { + if (auto lunaspyre = ObjectAccessor::GetCreature(*me, sistersVector[1])) + lunaspyre->AI()->DoAction(ACTION_1); //Lunaspyre set Phase 2 + + if (auto yathae = ObjectAccessor::GetCreature(*me, sistersVector[2])) + yathae->AI()->DoAction(ACTION_1); //Yathae set Phase 2 + break; + } + case ACTION_5: //Phase 3 + { + if (auto kasparian = ObjectAccessor::GetCreature(*me, sistersVector[0])) + kasparian->CastSpell(kasparian, SPELL_PHASE_3_CONVERSATION, true); + + if (auto lunaspyre = ObjectAccessor::GetCreature(*me, sistersVector[1])) + lunaspyre->AI()->DoAction(ACTION_3); //Lunaspyre set Phase 3 + } + } + } + + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + switch (summon->GetEntry()) + { + case NPC_HUNTRESS_KASPARIAN: + case NPC_PRIESTESS_LUNASPYRE: + case NPC_CAPTAIN_YATHAE: + { + if (++sisterDiedCount == 3) + { + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) != DONE) + { + instance->SetBossState(DATA_SISTERS_OF_THEMOON, DONE); + + if (auto kasparian = ObjectAccessor::GetCreature(*me, sistersVector[0])) + ; // frame instance start/end non supporte par notre core + } + me->RemoveAurasDueToSpell(SPELL_FONT_OF_ELUNE_AT); + me->RemoveAllAreaTriggers(); + me->DespawnOrUnsummon(); + } + break; + } + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (sideMoonTimer) + { + if (sideMoonTimer <= diff) + { + DoCast(me, lightSideMoon ? SPELL_LIGHT_SIDE_MOON_AT : SPELL_DARK_SIDE_MOON_AT, true); + lightSideMoon = lightSideMoon ? false : true; + sideMoonTimer = 54000; + + if (auto kasparian = ObjectAccessor::GetCreature(*me, sistersVector[0])) + kasparian->AI()->DoAction(ACTION_4); //Glaive Storm + + if (auto lunaspyre = ObjectAccessor::GetCreature(*me, sistersVector[1])) + lunaspyre->AI()->DoAction(ACTION_4); //Embrace of the Eclipse + + if (auto yathae = ObjectAccessor::GetCreature(*me, sistersVector[2])) + yathae->AI()->DoAction(ACTION_3); //Incorporeal Shot + } + else + sideMoonTimer -= diff; + } + + if (berserkTimer) + { + if (berserkTimer <= diff) + { + berserkTimer = 0; + + for (auto const& guid : sistersVector) + { + if (auto boss = ObjectAccessor::GetCreature(*me, guid)) + boss->CastSpell(boss, SPELL_BERSERK, true); + } + } + else + berserkTimer -= diff; + } + } +}; + +//118523 +struct npc_sister_kasparian : ScriptedAI +{ + explicit npc_sister_kasparian(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + me->SetReactState(REACT_DEFENSIVE); + } + + InstanceScript* instance; + EventMap events; + uint8 phase = 0; + uint8 healthPct = 71; + bool achieveAddData = false; + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_CORPOREAL_GHOST_MOUNT, true); + } + + void Reset() + { + events.Reset(); + } + + void EnterCombat(Unit* who) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + DoCast(me, SPELL_PHASE_1_CONVERSATION, true); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + DefaultEvents(++phase); + achieveAddData = false; + } + + uint32 GetData(uint32 type) const override + { + if (type == ACHIEVEMENT) + return achieveAddData ? 1 : 0; + return 0; + } + + void DefaultEvents(uint8 phase) + { + events.Reset(); + + switch (phase) + { + case 1: + events.RescheduleEvent(EVENT_MOON_GLAIVE, 14000); + events.RescheduleEvent(EVENT_TWILIGHT_GLAIVE, 20000); + break; + case 2: + me->StopAttack(true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE); me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); + DoCast(me, SPELL_LUNAR_GHOST, true); + DoCast(me, SPELL_LUNAR_GHOST_CAT, true); + DoCast(me, SPELL_PHASE_2_CONVERSATION, true); + if (auto summoner = me->GetAnyOwner()) + { + summoner->GetAI()->DoAction(ACTION_4); //Phase 2 + } + me->AddDelayedCombat(2000, [=] () -> void + { + me->CastSpell(me->GetHomePosition(), SPELL_SABERLEAP, true); + }); + events.RescheduleEvent(EVENT_JUMP_KASPARIAN, 20000); + events.RescheduleEvent(EVENT_TWILIGHT_GLAIVE, 8000); + events.RescheduleEvent(EVENT_SPECTRAL_GLAIVE, 5000); + break; + } + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void JustDied(Unit* killer) override + { + me->Dismount(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_TWILIGHT_GLAIVE_FILTER: + me->SetFacingToObject(target); + me->CastSpell(target, SPELL_TWILIGHT_GLAIVE_AT, false); + break; + } + } + + void OnAreaTriggerCast(Unit* caster, Unit* target, uint32 spellId, uint32 createATSpellId) override + { + if (!target || target->GetEntry() != NPC_GLAIVE_TARGET) + return; + + switch (createATSpellId) + { + case SPELL_GLAIVE_STORM_AT_BIG: + target->CastSpell(target, SPELL_GLAIVE_STORM_FILTER_MEDIUM, true, nullptr, nullptr, me->GetGUID()); + break; + } + } + + void MovementInform(uint32 type, uint32 id) + { + if (type == EFFECT_MOTION_TYPE) + { + switch (id) + { + case SPELL_SABERLEAP: + me->AddDelayedCombat(500, [=] () -> void { me->SetFacingTo(centrPos.GetAngle(me) - M_PI); }); + break; + } + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (healthPct && me->HealthBelowPct(healthPct)) + { + healthPct = 0; + DefaultEvents(++phase); + } + + if (me->HealthBelowPct(20) && !achieveAddData) + { + achieveAddData = true; + if (auto add = instance->instance->GetCreature(instance->GetGuidData(NPC_SISTERS_ACH_ADD))) + if (add->IsAIEnabled) + add->AI()->DoAction(ACTION_1); + } + } + + void DoAction(int32 const actionID) override + { + switch (actionID) + { + case ACTION_1: + achieveAddData = true; + break; + case ACTION_4: + if ((phase == 2 && IsHeroicPlusRaid()) || phase == 3) + events.RescheduleEvent(EVENT_GLAIVE_STORM, 500); + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 22.0f, 6190.06f, -1020.03f, 2899.5f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_MOON_GLAIVE: + DoCastTopAggro(SPELL_MOON_GLAIVE); + events.RescheduleEvent(EVENT_MOON_GLAIVE, 18000); + break; + case EVENT_TWILIGHT_GLAIVE: + DoCast(SPELL_TWILIGHT_GLAIVE_FILTER); + if (phase > 1) + events.DelayEvents(3000); + events.RescheduleEvent(EVENT_TWILIGHT_GLAIVE, 18000); + break; + case EVENT_JUMP_KASPARIAN: + { + Position pos; + centrPos.SimplePosXYRelocationByAngle(pos, 18.0f, centrPos.GetAngle(me) + frand(1.5f, 3.14f)); + me->GetMotionMaster()->Clear(); + me->CastSpell(pos, SPELL_SABERLEAP, false); + events.DelayEvents(1500); + events.RescheduleEvent(EVENT_JUMP_KASPARIAN, urand(20, 35) * IN_MILLISECONDS); + break; + } + case EVENT_SPECTRAL_GLAIVE: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true)) + { + me->SetFacingToObject(target); + DoCast(target, SPELL_SPECTRAL_GLAIVE); + } + events.RescheduleEvent(EVENT_SPECTRAL_GLAIVE, 5000); + break; + case EVENT_GLAIVE_STORM: + events.DelayEvents(100); + DoCast(SPELL_GLAIVE_STORM_FILTER_BIG); + break; + } + } + if (phase == 1) + DoMeleeAttackIfReady(); + } +}; + +//118518 +struct npc_sister_lunaspyre : ScriptedAI +{ + explicit npc_sister_lunaspyre(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + } + + InstanceScript* instance; + EventMap events; + uint8 phase = 0; + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_LUNAR_GHOST, true); + } + + void Reset() + { + events.Reset(); + } + + void EnterCombat(Unit* who) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2HL); + DefaultEvents(++phase); + } + + void DefaultEvents(uint8 phase) + { + events.Reset(); + + switch (phase) + { + case 1: + events.RescheduleEvent(EVENT_MOON_BURN, 10000); + events.RescheduleEvent(EVENT_LUNAR_STRIKE, 500); + events.RescheduleEvent(EVENT_TP_LUNASPERE, 16000); + break; + case 3: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE, 2000); + events.RescheduleEvent(EVENT_LUNAR_BEACON, 18000); + events.RescheduleEvent(EVENT_MOON_BURN, 10000); + events.RescheduleEvent(EVENT_LUNAR_FIRE, 7000); + break; + } + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void JustDied(Unit* killer) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + me->RemoveAllAreaTriggers(); + } + + bool IsDisableGenerateLoot() override + { + return true; + } + + void DoAction(int32 const actionID) override + { + switch (actionID) + { + case ACTION_1: + ++phase; + break; + case ACTION_2: + me->RemoveAllAreaTriggers(); + break; + case ACTION_3: + DefaultEvents(++phase); + DoCast(me, SPELL_CORPOREAL_GHOST, true); + me->CastSpell(centrPos, SPELL_INCORPOREAL_TELEPORT, true); + break; + case ACTION_4: + if ((phase == 1 && IsHeroicPlusRaid()) || phase == 2) + events.RescheduleEvent(EVENT_EMBRACE_ECLIPSE, 500); + break; + } + } + + void SpellFinishCast(const SpellInfo* spell) override + { + switch (spell->Id) + { + case SPELL_INCORPOREAL_TELEPORT: + me->AddDelayedCombat(1000, [=] () -> void { me->SetFacingTo(centrPos.GetAngle(me) - M_PI); }); + break; + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_MOON_BURN_FILTER: + me->CastSpell(target, SPELL_MOON_BURN_DMG, true); + break; + } + } + + void OnRemoveAuraTarget(Unit* target, uint32 spellId, AuraRemoveMode mode) override + { + if (me->IsInCombat() && target && mode == AURA_REMOVE_BY_EXPIRE) + { + switch (spellId) + { + case SPELL_LUNAR_BEACON: + target->CastSpell(target, SPELL_LUNAR_BARRAGE_MARK, true); + break; + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 22.0f, 6190.06f, -1020.03f, 2899.5f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_MOON_BURN: + Talk(SAY_MOON_BURN); + DoCast(SPELL_MOON_BURN_FILTER); + events.RescheduleEvent(EVENT_MOON_BURN, 24000); + break; + case EVENT_LUNAR_STRIKE: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true)) + { + me->SetFacingToObject(target); + DoCast(target, SPELL_LUNAR_STRIKE); + } + events.RescheduleEvent(EVENT_LUNAR_STRIKE, 5500); + break; + case EVENT_TP_LUNASPERE: + { + Position pos; + centrPos.SimplePosXYRelocationByAngle(pos, 23.0f, centrPos.GetAngle(me) + frand(1.5f, 3.14f)); + me->GetMotionMaster()->Clear(); + me->CastSpell(pos, SPELL_INCORPOREAL_TELEPORT, false); + events.DelayEvents(1500); + events.RescheduleEvent(EVENT_TP_LUNASPERE, urand(20, 40) * IN_MILLISECONDS); + break; + } + case EVENT_LUNAR_BEACON: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 1, 60.0f, true)) + DoCast(target, SPELL_LUNAR_BEACON); + events.RescheduleEvent(EVENT_LUNAR_BEACON, 22000); + break; + case EVENT_LUNAR_FIRE: + DoCastTopAggro(SPELL_LUNAR_FIRE); + events.RescheduleEvent(EVENT_LUNAR_FIRE, 11000); + break; + case EVENT_EMBRACE_ECLIPSE: + Talk(SAY_EMBRACE_ECLIPSE_WARN); + Talk(SAY_EMBRACE_ECLIPSE); + DoCast(me, SPELL_EMBRACE_ECLIPSE, true); + break; + } + } + if (phase == 3) + DoMeleeAttackIfReady(); + } +}; + +//118374 +struct npc_sister_yathae : ScriptedAI +{ + explicit npc_sister_yathae(Creature* creature) : ScriptedAI(creature), summons(me) + { + instance = me->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + } + + SummonList summons; + InstanceScript* instance; + EventMap events; + uint8 phase = 0; + uint8 healthPct = 0; + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_LUNAR_GHOST, true); + } + + void Reset() + { + events.Reset(); + } + + void EnterCombat(Unit* who) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + DefaultEvents(++phase); + } + + void DefaultEvents(uint8 phase) + { + events.Reset(); + + switch (phase) + { + case 1: + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY_BOW); + me->SetSheath(SHEATH_STATE_RANGED); + events.RescheduleEvent(EVENT_TWILIGHT_VOLLEY, 16000); + events.RescheduleEvent(EVENT_SHADOW_SHOT, 5000); + events.RescheduleEvent(EVENT_TP_YATHAE, 5000); + break; + case 2: + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + me->SetSheath(SHEATH_STATE_MELEE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE, 2000); + events.RescheduleEvent(EVENT_CALL_MOONTALON, 6000); + events.RescheduleEvent(EVENT_TWILIGHT_VOLLEY, 12000); + events.RescheduleEvent(EVENT_RAPID_SHOT, 18000); + break; + case 3: + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY_BOW); + me->SetSheath(SHEATH_STATE_RANGED); + me->StopAttack(true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE); me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); + DoCast(me, SPELL_LUNAR_GHOST, true); + if (auto summoner = me->GetAnyOwner()) + { + summoner->GetAI()->DoAction(ACTION_5); //Phase 3 + } + me->AddDelayedCombat(2000, [=] () -> void + { + me->CastSpell(me->GetHomePosition(), SPELL_INCORPOREAL_TELEPORT, true); + }); + events.RescheduleEvent(EVENT_TWILIGHT_VOLLEY, 28000); + events.RescheduleEvent(EVENT_SHADOW_SHOT, 5000); + break; + } + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void JustDied(Unit* killer) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + me->RemoveAllAreaTriggers(); + summons.DespawnAll(); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + } + + bool IsDisableGenerateLoot() override + { + return true; + } + + void DoAction(int32 const actionID) override + { + switch (actionID) + { + case ACTION_1: + DefaultEvents(++phase); + DoCast(me, SPELL_CORPOREAL_GHOST, true); + me->CastSpell(centrPos, SPELL_INCORPOREAL_TELEPORT, true); + healthPct = 41; + break; + case ACTION_2: + me->RemoveAllAreaTriggers(); + summons.DespawnAll(); + break; + case ACTION_3: + if (phase == 1 || (phase == 3 && IsHeroicPlusRaid())) + events.RescheduleEvent(EVENT_INCORPOREAL_SHOT, 500); + break; + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (healthPct && me->HealthBelowPct(healthPct)) + { + healthPct = 0; + DefaultEvents(++phase); + } + } + + void SpellFinishCast(const SpellInfo* spell) override + { + switch (spell->Id) + { + case SPELL_INCORPOREAL_TELEPORT: + me->AddDelayedCombat(1000, [=] () -> void { me->SetFacingTo(centrPos.GetAngle(me) - M_PI); }); + break; + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_INCORPOREAL_SHOT_FILTER: + me->CastSpell(target, SPELL_INCORPOREAL_SHOT_MARK, true); + break; + case SPELL_RAPID_SHOT_FILTER_MARK: + me->CastSpell(target, SPELL_RAPID_SHOT_MARK, true); + break; + case SPELL_RAPID_SHOT_FILTER_DMG: + me->CastSpell(target, SPELL_RAPID_SHOT_DMG, true); + break; + } + } + + void OnRemoveAuraTarget(Unit* target, uint32 spellId, AuraRemoveMode mode) override + { + if (me->IsInCombat() && target && mode == AURA_REMOVE_BY_EXPIRE) + { + switch (spellId) + { + case SPELL_INCORPOREAL_SHOT_MARK: + me->CastSpell(target, SPELL_INCORPOREAL_SHOT_VISUAL, true); + me->CastSpell(target, SPELL_INCORPOREAL_SHOT_DMG_BETWEEN, true); + break; + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 22.0f, 6190.06f, -1020.03f, 2899.5f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_INCORPOREAL_SHOT: + DoCast(SPELL_INCORPOREAL_SHOT_FILTER); + break; + case EVENT_TWILIGHT_VOLLEY: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit const* t) { return t->IsPlayer() && t->ToPlayer()->GetRoleForGroup() == ROLE_DAMAGE /* approx ranged-dps */; })) + { + me->SetFacingToObject(target); + me->CastSpell(target, SPELL_TWILIGHT_VOLLEY_AT); + } + else if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true)) + { + me->SetFacingToObject(target); + me->CastSpell(target, SPELL_TWILIGHT_VOLLEY_AT); + } + Talk(SAY_TWILIGHT_VOLLEY); + if (phase == 1 || phase == 3) + events.RescheduleEvent(EVENT_TWILIGHT_VOLLEY, 20000); + else if (phase == 2) + events.RescheduleEvent(EVENT_TWILIGHT_VOLLEY, 15000); + break; + case EVENT_SHADOW_SHOT: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true)) + { + me->SetFacingToObject(target); + DoCast(target, SPELL_SHADOW_SHOT, true); + } + events.RescheduleEvent(EVENT_SHADOW_SHOT, 5500); + break; + case EVENT_TP_YATHAE: + { + Position pos; + centrPos.SimplePosXYRelocationByAngle(pos, phase == 1 ? 18.0f : 23.0f, centrPos.GetAngle(me) + frand(1.5f, 3.14f)); + me->GetMotionMaster()->Clear(); + me->CastSpell(pos, SPELL_INCORPOREAL_TELEPORT, false); + events.DelayEvents(1500); + events.RescheduleEvent(EVENT_TP_YATHAE, 18000); + break; + } + case EVENT_CALL_MOONTALON: + DoCast(SPELL_CALL_MOONTALON); + break; + case EVENT_RAPID_SHOT: + Talk(SAY_RAPID_SHOT); + DoCast(SPELL_RAPID_SHOT_FILTER_MARK); + events.RescheduleEvent(EVENT_RAPID_SHOT, 18000); + break; + } + } + if (phase == 2) + DoMeleeAttackIfReady(); + } +}; + +//119205 +struct npc_sistersmoon_moontalon : public ScriptedAI +{ + npc_sistersmoon_moontalon(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + instance = me->GetInstanceScript(); + } + + InstanceScript* instance; + bool screech = false; + uint32 screechTimer = 0; + + void Reset() override {} + + void IsSummonedBy(Unit* summoner) override + { + DoCast(me, SPELL_MOONTALON_DISSOLVE_IN, true); + DoZoneInCombat(me, 100.0f); + me->SetReactState(REACT_AGGRESSIVE, 2000); + } + + void EnterCombat(Unit* who) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + } + + void JustDied(Unit* killer) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (!screech && me->HealthBelowPct(26)) + { + screech = true; + screechTimer = 3000; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (screechTimer) + { + if (screechTimer <= diff) + { + screechTimer = 6000; + DoCast(me, SPELL_DEATHLY_SCREECH, true); + } + else + screechTimer -= diff; + } + DoMeleeAttackIfReady(); + } +}; + +//119054 +struct npc_sistersmoon_glaive_target : public ScriptedAI +{ + npc_sistersmoon_glaive_target(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + instance = me->GetInstanceScript(); + } + + InstanceScript* instance; + + void Reset() override {} + + void OnAreaTriggerCast(Unit* caster, Unit* target, uint32 spellId, uint32 createATSpellId) override + { + if (!target || target->GetEntry() != NPC_GLAIVE_TARGET) + return; + + switch (createATSpellId) + { + case SPELL_GLAIVE_STORM_AT_MEDIUM: + if (auto kasparian = instance->instance->GetCreature(instance->GetGuidData(NPC_HUNTRESS_KASPARIAN))) + target->CastSpell(target, SPELL_GLAIVE_STORM_FILTER_SMALL, true, nullptr, nullptr, kasparian->GetGUID()); + break; + } + } + + void UpdateAI(uint32 diff) override {} +}; + +//121498 +struct npc_sistersmoon_twilight_soul : public ScriptedAI +{ + npc_sistersmoon_twilight_soul(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + DoCast(154441); + } + + InstanceScript* instance; + uint32 castCry = 15000; + + void DoAction(int32 const action) override + { + if (action == ACTION_1) + { + me->RemoveAura(154441); + DoCast(180343); + me->SetObjectScale(1.3f); + } + } + + void JustDied(Unit* killer) override + { + if (me->HasAura(180343)) + if (auto boss = instance->instance->GetCreature(instance->GetGuidData(NPC_HUNTRESS_KASPARIAN))) + if (boss->IsAIEnabled) + boss->AI()->DoAction(ACTION_6); + } + + void Reset() override + { + castCry = 15000; + me->SetObjectScale(1.0f); + DoCast(154441); + me->RemoveAura(180343); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (castCry <= diff) + { + DoCast(SPELL_CRY_OF_THE_LOST_SOULS); + castCry = urand(15000, 18000); + } + else castCry -= diff; + } +}; + +//236306 +class spell_sistersmoon_incorporeal_shot_filter : public SpellScript +{ + PrepareSpellScript(spell_sistersmoon_incorporeal_shot_filter); + + uint8 targetsCount = 0; + + void FilterTargets(std::list& targets) + { + std::list tempTargets; + for (auto object : targets) + { + if (object->IsPlayer()) + tempTargets.push_back(object); + } + + targets = tempTargets; + targetsCount = targets.size(); + + if (GetCaster() && targets.size() < 6) + GetCaster()->CastSpell(GetCaster(), SPELL_INCORPOREAL_SHOT_DMG_AOE, true); + } + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + if (targetsCount) + SetHitDamage(GetHitDamage() / targetsCount); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sistersmoon_incorporeal_shot_filter::FilterTargets, EFFECT_0, TARGET_UNIT_BETWEEN_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_sistersmoon_incorporeal_shot_filter::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +//239378, 239382, 239385 +class spell_sistersmoon_glaive_storm_filter : public SpellScript +{ + PrepareSpellScript(spell_sistersmoon_glaive_storm_filter); + + void FilterTargets(std::list& targets) + { + if (!GetCaster()) + return; + + std::list tempTargets; + + for (auto target : targets) + { + if (GetCaster()->GetDistance(target) > 18.0f) + tempTargets.push_back(target); + } + targets = tempTargets; + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sistersmoon_glaive_storm_filter::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENTRY); + } +}; + +//234998 +class spell_sistersmoon_astral_purge : public SpellScript +{ + PrepareSpellScript(spell_sistersmoon_astral_purge); + + void HandleDummy(SpellEffIndex /*effectIndex*/) + { + if (GetCaster() && GetHitUnit()) + { + if (GetCaster()->GetGUID() == GetHitUnit()->GetGUID()) + { + GetHitUnit()->RemoveAurasDueToSpell(SPELL_MOON_BURN_DMG); + GetHitUnit()->RemoveAurasDueToSpell(SPELL_DISCORPORATE); + } + + if (GetCaster()->GetMap()->IsMythic()) + { + //> Mechanic hack + float mod = 1.0f; + AddPct(mod, GetHitUnit()->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC, 35)); + SetHitDamage(GetHitDamage() * mod); + //< + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_ASTRAL_VULNERABILITY, true); + } + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_sistersmoon_astral_purge::HandleDummy, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +//233263, 233264 +class spell_sistersmoon_embrace_eclipse : public AuraScript +{ + PrepareAuraScript(spell_sistersmoon_embrace_eclipse); + + float damage = 0; + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + damage = amount; + } + + void Absorb(AuraEffect* /*AuraEffect**/, DamageInfo& dmgInfo, uint32& absorbAmount) + { + damage = absorbAmount; + } + + void OnRemove(AuraEffect const* /*auraEffect*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) + return; + + if (GetTarget() && damage) + { + if (GetId() == SPELL_EMBRACE_ECLIPSE) + { int32 bpv = int32(damage); GetTarget()->CastCustomSpell(GetTarget(), SPELL_UMBRA_DETONATION, &bpv, NULL, NULL, true); } + else if (GetId() == SPELL_EMBRACE_ECLIPSE_FRIEND) + { int32 bpv = int32(damage); GetTarget()->CastCustomSpell(GetTarget(), SPELL_LUNAR_DETONATION, &bpv, NULL, NULL, true); } + } + } + + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_sistersmoon_embrace_eclipse::CalculateAmount, EFFECT_0, SPELL_AURA_ANY); + OnEffectRemove += AuraEffectRemoveFn(spell_sistersmoon_embrace_eclipse::OnRemove, EFFECT_0, SPELL_AURA_ANY, AURA_EFFECT_HANDLE_REAL); + OnEffectAbsorb += AuraEffectAbsorbFn(spell_sistersmoon_embrace_eclipse::Absorb, EFFECT_0); + } +}; + +//234664, 234668 +class spell_sistersmoon_side_moon : public AuraScript +{ + PrepareAuraScript(spell_sistersmoon_side_moon); + + void OnTick(AuraEffect const* aurEff) + { + auto target = GetUnitOwner(); + if (!target) + return; + + if (auto instance = target->GetInstanceScript()) + { + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) != IN_PROGRESS) + { + aurEff->GetBase()->Remove(); + return; + } + } + + if (GetId() == SPELL_LIGHT_SIDE_MOON_DUMMY) + { + if (Aura* aura = target->GetAura(SPELL_DARK_SIDE_MOON_DUMMY)) + { + target->CastSpell(target, SPELL_ASTRAL_PURGE, true); + aura->Remove(); + } + target->CastSpell(target, SPELL_LUNAR_SUFFUSION, true); + } + else if (GetId() == SPELL_DARK_SIDE_MOON_DUMMY) + { + if (Aura* aura = target->GetAura(SPELL_LIGHT_SIDE_MOON_DUMMY)) + { + target->CastSpell(target, SPELL_ASTRAL_PURGE, true); + aura->Remove(); + } + target->CastSpell(target, SPELL_UMBRA_SUFFUSION, true); + } + } + + void OnRemove(AuraEffect const* /*auraEffect*/, AuraEffectHandleModes /*mode*/) + { + if (auto target = GetTarget()) + { + target->RemoveAurasDueToSpell(SPELL_LUNAR_SUFFUSION); + target->RemoveAurasDueToSpell(SPELL_UMBRA_SUFFUSION); + } + } + + void Register() + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_sistersmoon_side_moon::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + OnEffectRemove += AuraEffectRemoveFn(spell_sistersmoon_side_moon::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +//236717 +class spell_sistersmoon_lunar_barrage : public AuraScript +{ + PrepareAuraScript(spell_sistersmoon_lunar_barrage); + + void OnTick(AuraEffect const* aurEff) + { + auto target = GetUnitOwner(); + if (!target) + return; + + if (auto instance = target->GetInstanceScript()) + if (auto priest = instance->instance->GetCreature(instance->GetGuidData(NPC_PRIESTESS_LUNASPYRE))) + target->CastSpell(target, SPELL_LUNAR_BARRAGE_AT, true, nullptr, nullptr, priest->GetGUID()); + } + + void Register() + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_sistersmoon_lunar_barrage::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + + +//14928, 14929, 14930 +struct at_sistersmoon_glaive_storm : AreaTriggerAI +{ + at_sistersmoon_glaive_storm(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + std::list guidList; + + void OnCreate() override + { + if (auto caster = at->GetCaster()) + { + std::list closestList; + GetCreatureListWithEntryInGrid(closestList, caster, NPC_GLAIVE_TARGET, 15.0f); + for (auto creature : closestList) + guidList.push_back(creature->GetGUID()); + } + } + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + for (auto badGuid : guidList) + if (badGuid == target->GetGUID()) + return false; + + if (target->GetEntry() != NPC_GLAIVE_TARGET) + return false; + + if (at->GetDistance(target) > 1.0f) + return false; + + return true; + } +}; + +//13559, 13568 +struct at_sistersmoon_moon : AreaTriggerAI +{ + at_sistersmoon_moon(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + if (!target || target->HasAura(27827)) //Spirit of Redemption + return false; + + Position startPos{6169.41f, -1037.37f, 2899.46f}; + Position endPos{6210.92f, -1002.75f, 2899.46f}; + Position end, endLeft, endRight; + float dist = 55.0f; + float angle = endPos.GetAngle(&startPos); + float angleLeft = angle + (-M_PI / 2); + float angleRight = angle + (M_PI / 2); + endPos.SimplePosXYRelocationByAngle(end, dist * (1.0f - at->GetProgressCompat()), angle); + end.SimplePosXYRelocationByAngle(endLeft, dist, angleLeft); + end.SimplePosXYRelocationByAngle(endRight, dist, angleRight); + + uint32 spellId = at->GetSpellId() == SPELL_DARK_SIDE_MOON_AT ? SPELL_LIGHT_SIDE_MOON_DUMMY : SPELL_DARK_SIDE_MOON_DUMMY; + uint32 spellIdMirror = at->GetSpellId() == SPELL_DARK_SIDE_MOON_AT ? SPELL_DARK_SIDE_MOON_DUMMY : SPELL_LIGHT_SIDE_MOON_DUMMY; + + if (end.IsLinesCross(endPos, *target, endLeft, endRight)) + { + if (!target->HasAura(spellId)) + target->CastSpell(target, spellId, true); + } + else + { + if (!target->HasAura(spellIdMirror)) + target->CastSpell(target, spellIdMirror, true); + } + + return true; + } +}; + +class achievement_wax_on_wax_off : public AchievementCriteriaScript +{ +public: + achievement_wax_on_wax_off() : AchievementCriteriaScript("achievement_wax_on_wax_off") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* boss = target->ToCreature()) + if (boss->GetAI()->GetData(ACHIEVEMENT)) + return true; + + return false; + } +}; + +void AddSC_boss_sisters_of_the_moon() +{ + RegisterCreatureAI(boss_sisters_of_the_moon); + RegisterCreatureAI(npc_sister_kasparian); + RegisterCreatureAI(npc_sister_lunaspyre); + RegisterCreatureAI(npc_sister_yathae); + RegisterCreatureAI(npc_sistersmoon_moontalon); + RegisterCreatureAI(npc_sistersmoon_glaive_target); + RegisterCreatureAI(npc_sistersmoon_twilight_soul); + RegisterSpellScript(spell_sistersmoon_incorporeal_shot_filter); + RegisterSpellScript(spell_sistersmoon_glaive_storm_filter); + RegisterSpellScript(spell_sistersmoon_astral_purge); + RegisterAuraScript(spell_sistersmoon_embrace_eclipse); + RegisterAuraScript(spell_sistersmoon_side_moon); + RegisterAuraScript(spell_sistersmoon_lunar_barrage); + RegisterAreaTriggerAI(at_sistersmoon_glaive_storm); + RegisterAreaTriggerAI(at_sistersmoon_moon); + new achievement_wax_on_wax_off(); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_TheDesolateHost.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_TheDesolateHost.cpp new file mode 100644 index 0000000..8244bcc --- /dev/null +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_TheDesolateHost.cpp @@ -0,0 +1,2180 @@ +/* + * This file is part of the DestinyCore Project. See AUTHORS file for Copyright information + * + * 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 "AreaTriggerAI.h" +#include "tomb_of_sargeras.h" + +enum Says +{ + //Engine of Souls + SAY_ENGINE_AGGRO = 0, + SAY_TORMENTED_CRIES = 0, //Trigger Talk + SAY_WARN_SPEAR_OF_ANGUISH = 1, + + //The Desolate Host + SAY_HOST_AGGRO = 0, + SAY_HOST_DEATH = 1, + + //Soul Queen Dejahna + SAY_QUEEN_AGGRO = 0, + SAY_SOULBIND = 1, + SAY_WARN_SOULBIND = 2, + SAY_WARN_WAILING_SOULS = 3, + SAY_WAILING_SOULS = 4, + SAY_QUEEN_DEATH = 5, + + //Generic owner + SAY_PHASE_2 = 0, +}; + +enum Spells +{ + SPELL_SPIRITUAL_FONT_REAL = 241576, + SPELL_SPIRITUAL_FONT_SPIRIT = 241577, + SPELL_SOUL_TRANSFER = 240851, + SPELL_SPIRITUAL_BARRIER_VIS_REAL = 235113, //Only has trash and pets player in Real world + SPELL_SPIRITUAL_BARRIER_VIS_SPIRIT = 235620, //Only has trash and pets player in Spirit world + SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM = 235732, //Spirit world - Phase 8308 + SPELL_SPIRITUAL_BARRIER_REAL_REALM = 235734, //Real world - Phase 8309 + SPELL_SPIRIT_REALM = 235621, + SPELL_SWIRLING_SOULS_VISUAL = 241645, + SPELL_SHARED_HEALTH = 236963, + SPELL_CORPOREAL_REALM_REMOVE = 235590, + SPELL_SPIRIT_PRESENCE_AT = 235589, + SPELL_BERSERK = 47008, + + SPELL_QUIETUS = 236595, + SPELL_QUIETUS_DMG = 236507, + SPELL_QUIETUS_MOD = 236678, + + //Heroic+ + SPELL_DISSONANCE_REAL_FILTER = 239014, + SPELL_DISSONANCE_SPIRIT_FILTER = 239015, + SPELL_DISSONANCE_REAL = 239006, + SPELL_DISSONANCE_SPIRIT = 239007, + + //Mythic + SPELL_BOUND_ESSENCE_AURA = 236351, + SPELL_BOUND_ESSENCE_MISSILE = 238580, + + //Engine of Souls + SPELL_BONE_SHARDS = 236142, + SPELL_COLLAPSING_FISSURE_FILTER = 235885, + SPELL_COLLAPSING_FISSURE_MISSILE = 235889, + SPELL_COLLAPSING_FISSURE_AT = 235890, + SPELL_TORMENTED_CRIES_CHANNEL = 238570, + SPELL_TORMENTED_CRIES_SUMMON = 236065, + SPELL_TORMENTED_CRIES_MARK = 238018, + SPELL_TORMENTED_CRIES_FILTER = 235988, + SPELL_TORMENTED_CRIES_AT = 235989, + SPELL_SPEAR_OF_ANGUISH_FILTER = 235923, //Heroic+ + SPELL_SPEAR_OF_ANGUISH_MARK = 235924, + SPELL_SPEAR_OF_ANGUISH_HEAL_ABSORB = 238442, + + //Reanimated Templar + SPELL_RUPTURING_SLAM_AT = 235927, + SPELL_BONECAGE_ARMOR = 236513, + + //Ghastly Bonewarden + SPELL_SHADOW_CHANNELLING = 173746, + SPELL_GRASPING_DARKNESS = 235968, + + //Soul Queen Dejahna + SPELL_CRUSH_MIND = 241566, + SPELL_SOULBIND = 236449, + SPELL_SOULBIND_DUMMY = 236459, + SPELL_SOULBIND_FIND_PLAYER = 236465, + SPELL_SOULBIND_DMG = 236464, + SPELL_SOULBIND_VISUAL_FIND_SPIRIT = 245611, //Find player on the Spirit world + SPELL_SOULBIND_VISUAL_FIND_REAL = 245612, //Find player on the Real world + SPELL_WAILING_SOULS = 236072, + SPELL_WITHER = 236131, //Heroic+ + SPELL_WITHER_2 = 236138, + + //Fallen Priestess + SPELL_SOUL_REND = 229231, + SPELL_SHATTERING_SCREAM = 235969, + SPELL_SHATTERING_SCREAM_EXPLOSION = 236772, + SPELL_SPIRIT_CHAINS = 236361, + + //Soul Residue + SPELL_FIXATE = 238459, + SPELL_SOUL_ROT_AT = 236235, + SPELL_SOUL_ERUPTION = 236158, + SPELL_SOUL_ERUPTION_TRANSFORM = 236160, //Change model + SPELL_UNRELENTING = 221419, + + //The Desolate Host + SPELL_SUNDERING_DOOM = 236542, + SPELL_DOOMED_SUNDERING = 236544, + SPELL_TORMENT = 236548, +}; + +enum eEvents +{ + //Engine of Souls + EVENT_COLLAPSING_FISSURE = 1, + EVENT_TORMENTED_CRIES = 2, + EVENT_SPEAR_OF_ANGUISH = 3, //Heroic+ + + //Soul Queen Dejahna + EVENT_WAILING_SOULS = 1, + EVENT_SOULBIND = 2, + EVENT_WITHER = 3, + EVENT_WITHER_2 = 4, + + //The Desolate Host + EVENT_SUNDERING_DOOM = 1, + EVENT_DOOMED_SUNDERING = 2, +}; + +enum eMisc +{ + //SMSG_PLAY_SPELL_VISUAL_KIT + VISUAL_KIT_1 = 82907, + VISUAL_KIT_2 = 64342, +}; + +uint32 const bossEntry[3] = +{ + NPC_ENGINE_OF_SOULS, + NPC_SOUL_QUEEN_DEJAHNA, + NPC_DESOLATE_HOST +}; + +Position const centrPos = { 6438.45f, -1089.73f, 2881.52f }; + +Position const BossPos[3] = +{ + {6456.64f, -1105.83f, 2881.60f, 2.50f}, //118460 - NPC_ENGINE_OF_SOULS + {6461.07f, -1088.02f, 2881.60f, 2.73f}, //118462 - NPC_SOUL_QUEEN_DEJAHNA + {6447.46f, -1099.68f, 2881.60f, 2.49f} //119072 - NPC_DESOLATE_HOST +}; + +//120988 +struct boss_the_desolate_host_generic : BossAI +{ + explicit boss_the_desolate_host_generic(Creature* creature) : BossAI(creature, DATA_THE_DESOLATE_HOST), summons(me) + { + if (me->IsAlive()) + { + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->AddDelayedEvent(500, [=] () -> void { DoAction(ACTION_1); }); + } + } + + SummonList summons; + std::vector bossVector; + bool phaseTwo = false; + uint8 bossDiedCount = 0; + uint32 summonEngineTimer = 0; + uint32 summonDejahnaTimer = 0; + uint32 berserkTimer = 0; + + void Reset() override + { + _Reset(); + } + + void JustDied(Unit* who) override + { + _JustDied(); + + DoCast(240057); + } + + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) + { + case NPC_DESOLATE_HOST: + case NPC_SOUL_QUEEN_DEJAHNA: + case NPC_ENGINE_OF_SOULS: + break; + default: + summons.Summon(summon); + break; + } + } + + void DoAction(int32 const action) override + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == DONE) + return; + + switch (action) + { + case ACTION_1: //Summons + { + DoCast(me, SPELL_SPIRIT_PRESENCE_AT, true); + bossVector.clear(); + bossDiedCount = 0; + for (uint8 i = 0; i < 3; ++i) + { + if (auto summon = me->SummonCreature(bossEntry[i], BossPos[i])) + bossVector.push_back(summon->GetGUID()); + } + me->SummonCreatureGroup(CREATURE_SUMMON_GROUP_RESET); + break; + } + case ACTION_2: //Despawn after evade + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) != NOT_STARTED) + { + instance->SetBossState(DATA_THE_DESOLATE_HOST, NOT_STARTED); + + if (auto host = ObjectAccessor::GetCreature(*me, bossVector[2])) + ; // frame instance start/end non supporte par notre core + RemoveDebuff(); + ; // SummonCreatureGroupDespawn : groupes d invocation uwow, non porte + me->RemoveAllAuras(); + me->RemoveAllAreaTriggers(); + summons.DespawnAll(); + phaseTwo = false; + summonEngineTimer = 0; + summonDejahnaTimer = 0; + berserkTimer = 0; + + for (auto const& guid : bossVector) + { + if (auto boss = ObjectAccessor::GetCreature(*me, guid)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, boss); + boss->AI()->DoAction(ACTION_2); //Despawn summons + boss->SetReactState(REACT_PASSIVE); + boss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + boss->DespawnOrUnsummon(200); + } + } + AddDelayedEvent(15000, [=] () -> void { DoAction(ACTION_1); }); + } + break; + } + case ACTION_3: //Enter Combat + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) != IN_PROGRESS) + { + instance->SetBossState(DATA_THE_DESOLATE_HOST, IN_PROGRESS); + + for (auto const& guid : bossVector) + { + if (auto boss = ObjectAccessor::GetCreature(*me, guid)) + { + if (boss->GetEntry() == NPC_DESOLATE_HOST) + ; // frame instance start/end non supporte par notre core + else + boss->AI()->DoZoneInCombat(boss, 100.0f); + } + } + + summons.DoZoneInCombat(); + summonEngineTimer = 60000; + summonDejahnaTimer = 120000; + + if (IsMythicRaid()) + berserkTimer = 8 * MINUTE * IN_MILLISECONDS; + } + break; + } + case ACTION_4: //Phase 2 + { + if (!phaseTwo) + { + phaseTwo = true; + summonEngineTimer = 0; + summonDejahnaTimer = 0; + DoCast(me, SPELL_SWIRLING_SOULS_VISUAL, true); + if (auto host = ObjectAccessor::GetCreature(*me, bossVector[2])) + host->GetAI()->DoAction(ACTION_1); + if (IsMythicRaid()) + summons.DespawnEntry(NPC_SPIRITUAL_FONT); + Talk(SAY_PHASE_2); + } + break; + } + } + } + + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + switch (summon->GetEntry()) + { + case NPC_DESOLATE_HOST: + case NPC_ENGINE_OF_SOULS: + case NPC_SOUL_QUEEN_DEJAHNA: + { + if (++bossDiedCount == 3) + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) != DONE) + { + instance->SetBossState(DATA_THE_DESOLATE_HOST, DONE); + + if (auto host = ObjectAccessor::GetCreature(*me, bossVector[2])) + ; // frame instance start/end non supporte par notre core + if (auto engine = ObjectAccessor::GetCreature(*me, bossVector[0])) + { + if (auto player = killer->GetCharmerOrOwnerPlayerOrPlayerItself()) + ; // GeneratePersonalLoot : distribution perso uwow, le loot passe par la table classique + } + } + RemoveDebuff(); + summons.DespawnAll(); + me->DespawnOrUnsummon(); + } + break; + } + case NPC_REANIMATED_TEMPLAR: + case NPC_GHASTLY_BONEWARDEN: + case NPC_FALLEN_PRIESTESS: + case NPC_SOUL_RESIDUE: + { + if (IsMythicRaid() && instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + { + summon->CastSpell(summon, SPELL_BOUND_ESSENCE_MISSILE, true); + } + break; + } + default: + break; + } + } + + void RemoveDebuff() + { + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SOULBIND_DUMMY); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPIRIT_REALM); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPIRITUAL_BARRIER_REAL_REALM); + } + + void UpdateAI(uint32 diff) override + { + if (summonEngineTimer) + { + if (summonEngineTimer <= diff) + { + summonEngineTimer = 60000; //1 minute + + Position pos; + for (auto entry : {NPC_REANIMATED_TEMPLAR, NPC_GHASTLY_BONEWARDEN, NPC_GHASTLY_BONEWARDEN}) + { + centrPos.SimplePosXYRelocationByAngle(pos, frand(15.0f, 25.0f), frand(0.0f, 6.28f)); + me->SummonCreature(entry, pos); + } + } + else + summonEngineTimer -= diff; + } + + if (summonDejahnaTimer) + { + if (summonDejahnaTimer <= diff) + { + summonDejahnaTimer = 60000; //1 minute + + Position pos; + for (auto entry : {NPC_FALLEN_PRIESTESS, NPC_FALLEN_PRIESTESS, NPC_SOUL_RESIDUE, NPC_SOUL_RESIDUE, NPC_SOUL_RESIDUE, NPC_SOUL_RESIDUE}) + { + centrPos.SimplePosXYRelocationByAngle(pos, frand(15.0f, 25.0f), frand(0.0f, 6.28f)); + me->SummonCreature(entry, pos); + } + } + else + summonDejahnaTimer -= diff; + } + + if (berserkTimer) + { + if (berserkTimer <= diff) + { + berserkTimer = 0; + + for (auto const& guid : bossVector) + { + if (auto boss = ObjectAccessor::GetCreature(*me, guid)) + boss->CastSpell(boss, SPELL_BERSERK, true); + } + } + else + berserkTimer -= diff; + } + } +}; + +//118460 +struct npc_tos_engine_of_souls : ScriptedAI +{ + explicit npc_tos_engine_of_souls(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + SetCombatMovement(false); + } + + InstanceScript* instance; + EventMap events; + bool phaseTwo = false; + + void Reset() override + { + events.Reset(); + DoCast(me, SPELL_SHARED_HEALTH, true); + } + + void EnterCombat(Unit* who) override + { + Talk(SAY_ENGINE_AGGRO); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + DoCast(me, SPELL_QUIETUS, true); + + events.RescheduleEvent(EVENT_COLLAPSING_FISSURE, 7000); + events.RescheduleEvent(EVENT_TORMENTED_CRIES, 2 * MINUTE * IN_MILLISECONDS); + + if (IsHeroicPlusRaid()) + events.RescheduleEvent(EVENT_SPEAR_OF_ANGUISH, 20000); + } + + void OnAddThreat(Unit* victim, float& fThreat, SpellSchoolMask /*schoolMask*/, SpellInfo const* /*threatSpell*/) + { + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? 0.0f : (fThreat ? fThreat : fThreat + 1.0f); + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void JustDied(Unit* killer) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + bool IsDisableGenerateLoot() override + { + return true; + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPct(31) && !phaseTwo) + { + phaseTwo = true; + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_4); //Phase 2 + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_SPEAR_OF_ANGUISH_FILTER: + Talk(SAY_WARN_SPEAR_OF_ANGUISH, target->GetGUID()); + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 45.0f, 6438.27f, -1089.73f, 2881.52f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_COLLAPSING_FISSURE: + DoCast(SPELL_COLLAPSING_FISSURE_FILTER); + events.RescheduleEvent(EVENT_COLLAPSING_FISSURE, 32000); + break; + case EVENT_TORMENTED_CRIES: + DoCast(SPELL_TORMENTED_CRIES_CHANNEL); + events.RescheduleEvent(EVENT_TORMENTED_CRIES, 2 * MINUTE * IN_MILLISECONDS); + break; + case EVENT_SPEAR_OF_ANGUISH: + DoCast(SPELL_SPEAR_OF_ANGUISH_FILTER); + events.RescheduleEvent(EVENT_SPEAR_OF_ANGUISH, 20000); + break; + } + } + DoSpellAttackIfReady(SPELL_BONE_SHARDS); + } +}; + +//118462 +struct npc_tos_soul_queen_dejahna : ScriptedAI +{ + explicit npc_tos_soul_queen_dejahna(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + SetCombatMovement(false); + } + + InstanceScript* instance; + EventMap events; + std::list soulsList; + bool phaseTwo = false; + + void Reset() override + { + events.Reset(); + DoCast(me, SPELL_SHARED_HEALTH, true); + } + + void EnterCombat(Unit* who) override + { + Talk(SAY_QUEEN_AGGRO); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + DoCast(me, SPELL_QUIETUS, true); + events.RescheduleEvent(EVENT_WAILING_SOULS, 1 * MINUTE * IN_MILLISECONDS); + events.RescheduleEvent(EVENT_SOULBIND, 12000); + + if (IsHeroicPlusRaid()) + events.RescheduleEvent(EVENT_WITHER, 20000); + } + + void OnAddThreat(Unit* victim, float& fThreat, SpellSchoolMask /*schoolMask*/, SpellInfo const* /*threatSpell*/) + { + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? (fThreat ? fThreat : fThreat + 1.0f) : 0.0f; + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void JustDied(Unit* killer) override + { + Talk(SAY_QUEEN_DEATH); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + bool IsDisableGenerateLoot() override + { + return true; + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPct(31) && !phaseTwo) + { + phaseTwo = true; + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_4); //Phase 2 + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + switch (spell->Id) + { + case 236454: + case 236455: + { + soulsList.push_back(target->GetGUID()); + if (soulsList.size() > 1) + { + auto guidA = soulsList.front(); + auto guidB = soulsList.back(); + + me->AddDelayedCombat(100, [this, guidA, guidB] () -> void + { + if (me) + { + if (auto playerA = ObjectAccessor::GetPlayer(*me, guidA)) + { + if (auto playerB = ObjectAccessor::GetPlayer(*me, guidB)) + { + playerA->CastSpell(playerA, SPELL_SOULBIND_DUMMY, true, nullptr, nullptr, playerB->GetGUID()); + playerB->CastSpell(playerB, SPELL_SOULBIND_DUMMY, true, nullptr, nullptr, playerA->GetGUID()); + } + } + } + }); + } + break; + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 45.0f, 6438.27f, -1089.73f, 2881.52f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_WAILING_SOULS: + Talk(SAY_WARN_WAILING_SOULS); + Talk(SAY_WAILING_SOULS); + DoCast(SPELL_WAILING_SOULS); + events.RescheduleEvent(EVENT_WAILING_SOULS, 2 * MINUTE * IN_MILLISECONDS); + break; + case EVENT_SOULBIND: + { + soulsList.clear(); + bool foundReal = false; + bool foundSpirit = false; + std::list threatList = me->getThreatManager().getThreatList(); + for (auto ref : threatList) + { + if (auto player = ObjectAccessor::GetPlayer(*me, ref->getUnitGuid())) + { + if (!player->isInTankSpec() && !player->HasAura(SPELL_SOULBIND_DUMMY)) + { + if (player->HasAura(SPELL_SPIRIT_REALM)) + foundSpirit = true; + else + foundReal = true; + } + } + + } + if (foundReal && foundSpirit) + { + Talk(SAY_SOULBIND); + Talk(SAY_WARN_SOULBIND); + DoCast(SPELL_SOULBIND); + } + events.RescheduleEvent(EVENT_SOULBIND, 24000); + break; + } + case EVENT_WITHER: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 1, [this](Unit const* t) { return t->HasAura(SPELL_SPIRIT_REALM) && !t->HasAura(SPELL_SOULBIND_DUMMY); })) + DoCast(target, SPELL_WITHER); + events.RescheduleEvent(EVENT_WITHER, 38000); + events.RescheduleEvent(EVENT_WITHER_2, 10000); + break; + case EVENT_WITHER_2: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 1, [this](Unit const* t) { return t->HasAura(SPELL_SPIRIT_REALM) && !t->HasAura(SPELL_SOULBIND_DUMMY); })) + DoCast(target, SPELL_WITHER_2); + break; + } + } + DoSpellAttackIfReady(SPELL_CRUSH_MIND); + } +}; + +//119072 +struct npc_tos_desolate_host : ScriptedAI +{ + explicit npc_tos_desolate_host(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetVisible(false); + } + + InstanceScript* instance; + EventMap events; + uint32 tormentTimer = 0; + + void Reset() override + { + events.Reset(); + } + + void EnterCombat(Unit* who) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + events.RescheduleEvent(EVENT_SUNDERING_DOOM, 14000); + events.RescheduleEvent(EVENT_DOOMED_SUNDERING, 24000); + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void JustDied(Unit* killer) override + { + Talk(SAY_HOST_DEATH); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + bool IsDisableGenerateLoot() override + { + return true; + } + + void DoAction(int32 const action) override + { + if (action == ACTION_1) + { + me->AddDelayedEvent(4000, [this] () -> void + { + if (me) + { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetVisible(true); + Talk(SAY_HOST_AGGRO); + DoCast(me, SPELL_SHARED_HEALTH, true); + me->SendPlaySpellVisualKit(0, VISUAL_KIT_1, 0); + me->HandleEmoteCommand(EMOTE_ONESHOT_BATTLE_ROAR); + me->SetReactState(REACT_AGGRESSIVE, 2000); + DoZoneInCombat(me, 100.0f); + tormentTimer = 30000; + } + }); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (tormentTimer) + { + if (tormentTimer <= diff) + { + tormentTimer = 20000; + DoCast(me, SPELL_TORMENT, true); + } + else + tormentTimer -= diff; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 45.0f, 6438.27f, -1089.73f, 2881.52f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SUNDERING_DOOM: + me->CastSpell(me, SPELL_SUNDERING_DOOM); + events.RescheduleEvent(EVENT_SUNDERING_DOOM, 26000); + break; + case EVENT_DOOMED_SUNDERING: + me->CastSpell(me, SPELL_DOOMED_SUNDERING); + events.RescheduleEvent(EVENT_DOOMED_SUNDERING, 26000); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//118701 +struct npc_tos_spiritual_font : ScriptedAI +{ + explicit npc_tos_spiritual_font(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + me->SetDisplayId(11686); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC); + } + + void Reset() override + { + DoCast(me, SPELL_SPIRITUAL_FONT_REAL, true); + DoCast(me, SPELL_SPIRITUAL_FONT_SPIRIT, true); + } + + void OnSpellClick(Unit* clicker, bool& /*result*/) override + { + if (!clicker) + return; + + if (clicker->HasAura(SPELL_SOULBIND_DUMMY) || clicker->HasAura(SPELL_SPIRIT_CHAINS) || + clicker->HasAura(SPELL_SOUL_TRANSFER) || clicker->HasAura(SPELL_SPEAR_OF_ANGUISH_HEAL_ABSORB)) + return; + + if (InstanceScript* instance = clicker->GetInstanceScript()) + if (clicker->GetMap()->IsMythic() && clicker->HealthAbovePct(50) && instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + return; + + clicker->CastSpell(clicker, SPELL_SOUL_TRANSFER, true); + + if (!clicker->HasAura(SPELL_SPIRIT_REALM)) + clicker->CastSpell(clicker, SPELL_SPIRIT_REALM, true); + else + { + clicker->RemoveAurasDueToSpell(SPELL_SPIRIT_REALM); + clicker->CastSpell(clicker, SPELL_CORPOREAL_REALM_REMOVE, true); + } + } + + void UpdateAI(uint32 diff) override {} +}; + +//118715, 119938 +struct npc_tos_reanimated_templar : ScriptedAI +{ + explicit npc_tos_reanimated_templar(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + mirror = me->GetEntry() == NPC_REANIMATED_TEMPLAR_MIRROR; + } + + InstanceScript* instance; + EventMap events; + bool armor = false; + bool mirror = false; + + void IsSummonedBy(Unit* summoner) override + { + if (!mirror) + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + me->SendPlaySpellVisualKit(0, VISUAL_KIT_2, 0); + else + me->SetStandState(UNIT_STAND_STATE_KNEEL); + + if (IsMythicRaid()) + DoCast(me, SPELL_BOUND_ESSENCE_AURA, true); + } + + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + DoZoneInCombat(me, 100.0f); + } + + void Reset() override + { + events.Reset(); + } + + void EnterCombat(Unit* who) override + { + events.RescheduleEvent(EVENT_1, 12000); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + if (!mirror) + me->SetStandState(UNIT_STAND_STATE_STAND); + } + + void OnAddThreat(Unit* victim, float& fThreat, SpellSchoolMask /*schoolMask*/, SpellInfo const* /*threatSpell*/) + { + if (!mirror) + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? 0.0f : (fThreat ? fThreat : fThreat + 1.0f); + else + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? (fThreat ? fThreat : fThreat + 1.0f) : 0.0f; + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (IsHeroicPlusRaid() && me->HealthBelowPct(51) && !armor) + { + armor = true; + DoCast(me, SPELL_BONECAGE_ARMOR, true); + } + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SHATTERING_SCREAM_EXPLOSION) + me->RemoveAurasDueToSpell(SPELL_BONECAGE_ARMOR); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 45.0f, 6438.27f, -1089.73f, 2881.52f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + DoCast(SPELL_RUPTURING_SLAM_AT); + events.RescheduleEvent(EVENT_1, 22000); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//118728, 119939 +struct npc_tos_ghastly_bonewarden : ScriptedAI +{ + explicit npc_tos_ghastly_bonewarden(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + mirror = me->GetEntry() == NPC_GHASTLY_BONEWARDEN_MIRROR; + } + + InstanceScript* instance; + EventMap events; + bool armor = false; + bool mirror = false; + + void IsSummonedBy(Unit* summoner) override + { + if (!mirror) + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + me->SendPlaySpellVisualKit(0, VISUAL_KIT_2, 0); + else + DoCast(me, SPELL_SHADOW_CHANNELLING, true); + + if (IsMythicRaid()) + DoCast(me, SPELL_BOUND_ESSENCE_AURA, true); + } + + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + DoZoneInCombat(me, 100.0f); + } + + void Reset() override + { + events.Reset(); + } + + void EnterCombat(Unit* who) override + { + events.RescheduleEvent(EVENT_1, 4000); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + if (!mirror) + me->InterruptNonMeleeSpells(false); + } + + void OnAddThreat(Unit* victim, float& fThreat, SpellSchoolMask /*schoolMask*/, SpellInfo const* /*threatSpell*/) + { + if (!mirror) + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? 0.0f : (fThreat ? fThreat : fThreat + 1.0f); + else + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? (fThreat ? fThreat : fThreat + 1.0f) : 0.0f; + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (IsHeroicPlusRaid() && me->HealthBelowPct(51) && !armor) + { + armor = true; + DoCast(me, SPELL_BONECAGE_ARMOR, true); + } + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SHATTERING_SCREAM_EXPLOSION) + me->RemoveAurasDueToSpell(SPELL_BONECAGE_ARMOR); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 45.0f, 6438.27f, -1089.73f, 2881.52f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + if (!mirror) + { + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit const* t) { return !t->HasAura(SPELL_SPIRIT_REALM); })) + DoCast(target, SPELL_GRASPING_DARKNESS); + } + else + { + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit const* t) { return t->HasAura(SPELL_SPIRIT_REALM); })) + DoCast(target, SPELL_GRASPING_DARKNESS); + } + events.RescheduleEvent(EVENT_1, urand(4,6) * IN_MILLISECONDS); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//118729, 119940 +struct npc_tos_fallen_priestess : ScriptedAI +{ + explicit npc_tos_fallen_priestess(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + mirror = me->GetEntry() == NPC_FALLEN_PRIESTESS_MIRROR; + } + + InstanceScript* instance; + EventMap events; + bool mirror = false; + + void IsSummonedBy(Unit* summoner) override + { + if (!mirror) + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + me->SendPlaySpellVisualKit(0, VISUAL_KIT_2, 0); + else + DoCast(me, SPELL_SOUL_REND, true); + + if (IsMythicRaid()) + DoCast(me, SPELL_BOUND_ESSENCE_AURA, true); + } + + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + DoZoneInCombat(me, 100.0f); + } + + void Reset() override + { + events.Reset(); + } + + void EnterCombat(Unit* who) override + { + events.RescheduleEvent(EVENT_1, 4000); + events.RescheduleEvent(EVENT_2, 14000); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + if (!mirror) + me->InterruptNonMeleeSpells(false); + } + + void OnAddThreat(Unit* victim, float& fThreat, SpellSchoolMask /*schoolMask*/, SpellInfo const* /*threatSpell*/) + { + if (!mirror) + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? (fThreat ? fThreat : fThreat + 1.0f) : 0.0f; + else + fThreat = victim->HasAura(SPELL_SPIRIT_REALM) ? 0.0f : (fThreat ? fThreat : fThreat + 1.0f); + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (CheckHomeDistToEvade(diff, 45.0f, 6438.27f, -1089.73f, 2881.52f)) + return; + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_1: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit const* t) { return (!mirror && t->HasAura(SPELL_SPIRIT_REALM)) || (mirror && !t->HasAura(SPELL_SPIRIT_REALM)); })) + { + DoResetThreat(); + me->AddThreat(target, 10000.0f); + DoCast(target, SPELL_SHATTERING_SCREAM); + } + events.RescheduleEvent(EVENT_1, urand(10, 15) * IN_MILLISECONDS); + break; + case EVENT_2: + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit const* t) { return (!mirror && t->HasAura(SPELL_SPIRIT_REALM)) || (mirror && !t->HasAura(SPELL_SPIRIT_REALM)); })) + { + DoResetThreat(); + me->AddThreat(target, 10000.0f); + DoCast(target, SPELL_SPIRIT_CHAINS); + } + events.RescheduleEvent(EVENT_2, 14000); + break; + } + } + DoMeleeAttackIfReady(); + } +}; + +//118730 +struct npc_tos_soul_residue : ScriptedAI +{ + explicit npc_tos_soul_residue(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + mirror = me->GetEntry() == NPC_SOUL_RESIDUE_MIRROR; + me->SetReactState(REACT_PASSIVE); + me->SetSpeed(MOVE_RUN, 0.5f); + } + + InstanceScript* instance; + bool died = false; + bool mirror = false; + uint32 rotTimer = 0; + uint32 unrelentingTImer = 0; + uint32 fixateTimer = 0; + ObjectGuid targetGUID; + + void IsSummonedBy(Unit* summoner) override + { + if (!mirror) + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + me->SendPlaySpellVisualKit(0, VISUAL_KIT_2, 0); + + if (IsMythicRaid()) + DoCast(me, SPELL_BOUND_ESSENCE_AURA, true); + } + + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + DoZoneInCombat(me, 100.0f); + } + + void Reset() override {} + + void EnterCombat(Unit* who) override + { + rotTimer = 2000; + fixateTimer = 1000; + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_3); + + if (IsMythicRaid()) + unrelentingTImer = 4000; + } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + ScriptedAI::EnterEvadeMode(); + + if (auto summoner = me->GetAnyOwner()) + summoner->GetAI()->DoAction(ACTION_2); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (damage >= me->GetHealth()) + { + damage = 0; + + if (!died) + { + died = true; + me->StopAttack(true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + DoCast(me, SPELL_SOUL_ERUPTION, true); + DoCast(me, SPELL_SOUL_ERUPTION_TRANSFORM, true); + + if (auto summoner = me->GetAnyOwner()) + if (summoner->IsCreature()) + summoner->ToCreature()->AI()->SummonedCreatureDies(me, nullptr); + + me->AddDelayedEvent(5000, [=] () -> void { me->DespawnOrUnsummon(); }); + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (CheckHomeDistToEvade(diff, 45.0f, 6438.27f, -1089.73f, 2881.52f)) + return; + + if (rotTimer) + { + if (rotTimer <= diff) + { + rotTimer = 2000; + if (me->isMoving()) + if (auto owner = me->GetAnyOwner()) + me->CastSpell(me, SPELL_SOUL_ROT_AT, true, nullptr, nullptr, owner->GetGUID()); + } + else + rotTimer -= diff; + } + + if (unrelentingTImer) + { + if (unrelentingTImer <= diff) + { + unrelentingTImer = 4000; + DoCast(me, SPELL_UNRELENTING, true); + } + else + unrelentingTImer -= diff; + } + + if (fixateTimer) + { + if (fixateTimer <= diff) + { + fixateTimer = 2000; + + if (!targetGUID.IsEmpty()) + { + auto player = ObjectAccessor::GetPlayer(*me, targetGUID); + if (!player || !player->IsAlive() || (!mirror && !player->HasAura(SPELL_SPIRIT_REALM)) || (mirror && player->HasAura(SPELL_SPIRIT_REALM))) + { + targetGUID.Clear(); + me->StopAttack(true); + } + } + + if (targetGUID.IsEmpty()) + { + if (auto target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit const* t) { return (!mirror && t->HasAura(SPELL_SPIRIT_REALM)) || (mirror && !t->HasAura(SPELL_SPIRIT_REALM)); })) + { + targetGUID = target->GetGUID(); + DoCast(target, SPELL_FIXATE, true); + me->ClearUnitState(UNIT_STATE_CASTING); + AttackStart(target); + } + } + } + else + fixateTimer -= diff; + } + DoMeleeAttackIfReady(); + } +}; + +//118924 +struct npc_tos_tormented_cries : ScriptedAI +{ + explicit npc_tos_tormented_cries(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void Reset() override {} + + void IsSummonedBy(Unit* summoner) override + { + Talk(SAY_TORMENTED_CRIES); + me->CastSpellDelay(me, SPELL_TORMENTED_CRIES_FILTER, true, 100); + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TORMENTED_CRIES_FILTER) + { + me->SetFacingToObject(target); + DoCast(target, SPELL_TORMENTED_CRIES_MARK, true); + } + } + + void OnRemoveAuraTarget(Unit* target, uint32 spellId, AuraRemoveMode mode) override + { + if (spellId == SPELL_TORMENTED_CRIES_MARK && target && mode == AURA_REMOVE_BY_EXPIRE) + { + me->SetFacingToObject(target); + if (auto owner = me->GetAnyOwner()) + me->CastSpell(target, SPELL_TORMENTED_CRIES_AT, false, nullptr, nullptr, owner->GetGUID()); + } + } + + void UpdateAI(uint32 diff) override {} +}; + +//236673 +class spell_tos_quietus_filter : public SpellScript +{ + PrepareSpellScript(spell_tos_quietus_filter); + + void FilterTargets(std::list& targets) + { + std::list tempTargets; + + if (auto caster = GetCaster()) + { + if (caster->GetEntry() == NPC_ENGINE_OF_SOULS) + { + for (auto const& target : targets) + { + if (auto player = target->ToPlayer()) + if (player->IsAlive() && !player->HasAura(SPELL_SPIRIT_REALM)) + tempTargets.push_back(target); + } + } + else if (caster->GetEntry() == NPC_SOUL_QUEEN_DEJAHNA) + { + for (auto const& target : targets) + { + if (auto player = target->ToPlayer()) + if (player->IsAlive() && player->HasAura(SPELL_SPIRIT_REALM)) + tempTargets.push_back(target); + } + } + + if (tempTargets.empty()) + { + caster->CastSpell(caster, SPELL_QUIETUS_DMG, true); + caster->CastSpell(caster, SPELL_QUIETUS_MOD, true); + } + } + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_quietus_filter::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } +}; + +//236465 +class spell_tos_soulbind_finder : public SpellScript +{ + PrepareSpellScript(spell_tos_soulbind_finder); + + void HandleDummy(SpellEffIndex /*effectIndex*/) + { + auto caster = GetCaster(); + auto target = GetHitUnit(); + if (!caster || !target) + return; + + if (auto aura = target->GetAura(SPELL_SOULBIND_DUMMY, caster->GetGUID())) + { + aura->Remove(); + caster->RemoveAurasDueToSpell(SPELL_SOULBIND_DUMMY); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tos_soulbind_finder::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +//245611, 245612 +class spell_tos_soulbind_visual_finder : public SpellScript +{ + PrepareSpellScript(spell_tos_soulbind_visual_finder); + + void FilterTargets(std::list& targets) + { + if (auto caster = GetCaster()) + { + targets.remove_if([caster](WorldObject* object) -> bool + { + if (object->IsPlayer() && object->ToPlayer()->HasAura(SPELL_SOULBIND_DUMMY, caster->GetGUID())) + return false; + + return true; + }); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_soulbind_visual_finder::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ALLY); + } +}; + +//236563, 236564, 236567, 236568 +class spell_tos_sundering_doom : public SpellScript +{ + PrepareSpellScript(spell_tos_sundering_doom); + + uint8 targetsCount = 0; + + void FilterTargets(std::list& targets) + { + if (!GetCaster()) + return; + + if (!targets.empty()) + { + targetsCount = targets.size(); + } + else + { + if (auto aura = GetCaster()->GetAura(SPELL_TORMENT)) + aura->ModStackAmount(5); + else + GetCaster()->CastSpell(GetCaster(), SPELL_TORMENT, true); + } + } + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + auto caster = GetCaster(); + auto target = GetHitUnit(); + if (!caster || !target) + return; + + if (GetSpellInfo()->Id == 236563 || GetSpellInfo()->Id == 236567) + { + if (targetsCount) + SetHitDamage(GetHitDamage() / targetsCount); + } + else if (GetSpellInfo()->Id == 236564 || GetSpellInfo()->Id == 236568) + { + auto distance = caster->GetExactDist2d(target) * 4.0f; + if (distance > 90.0f) + distance = 90.0f; + auto pctModifier = 100.0f - distance; + + SetHitDamage(CalculatePct(GetHitDamage(), pctModifier)); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_sundering_doom::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_tos_sundering_doom::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +//239014, 239015 +class spell_tos_dissonance : public SpellScript +{ + PrepareSpellScript(spell_tos_dissonance); + + void FilterTargets(std::list& targets) + { + if (!GetCaster()) + return; + + bool find = false; + + if (GetSpellInfo()->Id == SPELL_DISSONANCE_REAL_FILTER) + { + for (auto target : targets) + { + if (auto unit = target->ToUnit()) + if (unit->HasAura(SPELL_SPIRIT_REALM)) + { + find = true; + break; + } + } + } + else if (GetSpellInfo()->Id == SPELL_DISSONANCE_SPIRIT_FILTER) + { + for (auto target : targets) + { + if (auto unit = target->ToUnit()) + if (!unit->HasAura(SPELL_SPIRIT_REALM)) + { + find = true; + break; + } + } + } + + if (find) + { + if (GetSpellInfo()->Id == SPELL_DISSONANCE_REAL_FILTER) + GetCaster()->CastSpell(GetCaster(), SPELL_DISSONANCE_REAL, true); //Dmg spirit realm targets + else if (GetSpellInfo()->Id == SPELL_DISSONANCE_SPIRIT_FILTER) + GetCaster()->CastSpell(GetCaster(), SPELL_DISSONANCE_SPIRIT, true); //Dmg real realm targets + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_dissonance::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENTRY); + } +}; + +//239006, 239007 +class spell_tos_dissonance_filter : public SpellScript +{ + PrepareSpellScript(spell_tos_dissonance_filter); + + void FilterTargets(std::list& targets) + { + if (!GetCaster()) + return; + + std::list tempTargets; + + if (GetSpellInfo()->Id == SPELL_DISSONANCE_REAL) + { + for (auto target : targets) + { + if (auto player = target->ToPlayer()) + if (player->HasAura(SPELL_SPIRIT_REALM)) + { + tempTargets.push_back(target); + } + } + } + else if (GetSpellInfo()->Id == SPELL_DISSONANCE_SPIRIT) + { + for (auto target : targets) + { + if (auto player = target->ToPlayer()) + if (!player->HasAura(SPELL_SPIRIT_REALM)) + { + tempTargets.push_back(target); + } + } + } + targets = tempTargets; + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_dissonance_filter::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENTRY); + } +}; + +//235933, 242796 +class spell_tos_spear_of_anguish : public SpellScript +{ + PrepareSpellScript(spell_tos_spear_of_anguish); + + uint8 targetsCount = 0; + + void FilterTargets(std::list& targets) + { + if (!targetsCount) + targetsCount = targets.size(); + + targets.remove_if([this](WorldObject* object) -> bool + { + if (object == nullptr) + return true; + + if (object->ToUnit()->IsImmunedToSpell(GetSpellInfo(), nullptr)) + return true; + + return false; + }); + } + + void HandleDamage(SpellEffIndex /*effectIndex*/) + { + if (GetSpellInfo()->Id == 242796 && targetsCount) + SetHitDamage(GetHitDamage() / targetsCount); + } + + void HandleDummy(SpellEffIndex /*effectIndex*/) + { + PreventHitDefaultEffect(EFFECT_2); + + auto target = GetHitUnit(); + if (!target || target->IsImmunedToSpell(GetSpellInfo(), nullptr)) + return; + + target->CastSpell(target, SPELL_SOUL_TRANSFER, true); + target->CastSpell(target, SPELL_SPIRIT_REALM, true); + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_BARRIER_REAL_REALM); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_spear_of_anguish::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_spear_of_anguish::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_tos_spear_of_anguish::HandleDamage, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE); + OnEffectHitTarget += SpellEffectFn(spell_tos_spear_of_anguish::HandleDummy, EFFECT_2, SPELL_EFFECT_FORCE_CAST); + } +}; + +//238585 +class spell_tos_bound_essence : public SpellScript +{ + PrepareSpellScript(spell_tos_bound_essence); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (auto caster = GetCaster()) + { + if (auto owner = caster->GetAnyOwner()) + { + WorldLocation* loc = GetHitDest(); + + switch (caster->GetEntry()) + { + case NPC_REANIMATED_TEMPLAR: + owner->SummonCreature(NPC_REANIMATED_TEMPLAR_MIRROR, loc->GetPosition()); + break; + case NPC_GHASTLY_BONEWARDEN: + owner->SummonCreature(NPC_GHASTLY_BONEWARDEN_MIRROR, loc->GetPosition()); + break; + case NPC_FALLEN_PRIESTESS: + owner->SummonCreature(NPC_FALLEN_PRIESTESS_MIRROR, loc->GetPosition()); + break; + case NPC_SOUL_RESIDUE: + owner->SummonCreature(NPC_SOUL_RESIDUE_MIRROR, loc->GetPosition()); + break; + } + } + } + } + + void Register() override + { + OnEffectLaunch += SpellEffectFn(spell_tos_bound_essence::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +//235923 +class spell_tos_spear_of_anguish_filter : public SpellScript +{ + PrepareSpellScript(spell_tos_spear_of_anguish_filter); + + std::list tankList; + + void FilterTargets(std::list& targets) + { + targets.remove_if([this](WorldObject* object) -> bool + { + if (object == nullptr || !object->IsPlayer()) + return true; + + if (object->ToPlayer()->HasAura(SPELL_SPIRIT_REALM) || object->ToPlayer()->HasAura(SPELL_SOULBIND_DUMMY)) + return true; + + if (object->ToPlayer()->isInTankSpec()) + tankList.push_back(object); + + return false; + }); + + if (targets.size() > 1) + { + if (auto topAggro = GetCaster()->GetAI()->SelectTarget(SELECT_TARGET_TOPAGGRO)) + { + if (topAggro->IsPlayer() && topAggro->ToPlayer()->isInTankSpec()) + targets.remove(topAggro); + else if (!tankList.empty()) + targets.remove(Trinity::Containers::SelectRandomContainerElement(tankList)); + } + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_spear_of_anguish_filter::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } +}; + +//235988 +class spell_tos_tormented_cries_filter : public SpellScript +{ + PrepareSpellScript(spell_tos_tormented_cries_filter); + + std::list tankList; + + void FilterTargets(std::list& targets) + { + if (!GetCaster()) + return; + + targets.remove_if([this](WorldObject* object) -> bool + { + if (object == nullptr || !object->IsPlayer()) + return true; + + if (object->ToPlayer()->HasAura(SPELL_SPIRIT_REALM) || object->ToPlayer()->HasAura(SPELL_SPEAR_OF_ANGUISH_MARK)) + return true; + + if (object->ToPlayer()->isInTankSpec()) + tankList.push_back(object); + + return false; + }); + + if (targets.size() > 1) + { + if (auto topAggro = GetCaster()->GetAI()->SelectTarget(SELECT_TARGET_TOPAGGRO)) + { + if (topAggro->IsPlayer() && topAggro->ToPlayer()->isInTankSpec()) + targets.remove(topAggro); + else if (!tankList.empty()) + targets.remove(Trinity::Containers::SelectRandomContainerElement(tankList)); + } + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tos_tormented_cries_filter::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } +}; + +//236459 +class spell_tos_soulbind : public AuraScript +{ + PrepareAuraScript(spell_tos_soulbind); + + uint8 counter = 0; + + void OnTick(AuraEffect const* /*aurEff*/) + { + ++counter; + + if (GetUnitOwner()) + { + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SOULBIND_FIND_PLAYER, true); + + if (counter == 2) + { + if (GetUnitOwner()->HasAura(SPELL_SPIRIT_REALM)) + { + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SOULBIND_VISUAL_FIND_REAL, true); + } + else + { + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SOULBIND_VISUAL_FIND_SPIRIT, true); + } + } + else if (counter == 8) + { + counter = 0; + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SOULBIND_DMG, true); + } + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_soulbind::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +//236515 +class spell_tos_shattering_scream : public AuraScript +{ + PrepareAuraScript(spell_tos_shattering_scream); + + void OnTick(AuraEffect const* aurEff) + { + if (GetUnitOwner() && aurEff->GetBase()->GetStackAmount() == 5) + { + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SHATTERING_SCREAM_EXPLOSION, true); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_tos_shattering_scream::OnTick, EFFECT_2, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +// 235113, 235620 +class spell_tos_spiritual_barrier_dissonance_visual_aura : public AuraScript +{ + PrepareAuraScript(spell_tos_spiritual_barrier_dissonance_visual_aura); + + uint32 tickTimer = 3000; + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()) + { + GetTarget()->SetFlag(OBJECT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NOT_SELECTABLE_MODEL); + + if (GetTarget()->IsPlayer()) + { + ; // UpdateCustomField : rafraichissement client custom uwow, non porte + bool isSpiritRealm = GetId() == SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM; + + HostileRefManager& refManager = GetTarget()->getHostileRefManager(); + HostileReference* ref = refManager.getFirst(); + + while (ref) + { + if (auto unit = ref->GetSource()->GetOwner()) + if (auto creature = unit->ToCreature()) + { + switch (creature->GetEntry()) + { + // Real Realm + case NPC_ENGINE_OF_SOULS: + case NPC_REANIMATED_TEMPLAR: + case NPC_GHASTLY_BONEWARDEN: + case NPC_TORMENTED_CRIES_TRIG: + case NPC_FALLEN_PRIESTESS_MIRROR: + case NPC_SOUL_RESIDUE_MIRROR: + if (isSpiritRealm) + creature->getThreatManager().modifyThreatPercent(GetTarget(), -100); + break; + + // Spirit Realm + case NPC_SOUL_QUEEN_DEJAHNA: + case NPC_FALLEN_PRIESTESS: + case NPC_SOUL_RESIDUE: + case NPC_REANIMATED_TEMPLAR_MIRROR: + case NPC_GHASTLY_BONEWARDEN_MIRROR: + if (!isSpiritRealm) + creature->getThreatManager().modifyThreatPercent(GetTarget(), -100); + break; + default: + break; + } + } + + ref = ref->next(); + } + } + } + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()) + { + GetTarget()->RemoveFlag(OBJECT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NOT_SELECTABLE_MODEL); + + if (GetTarget()->IsPlayer()) + ; // UpdateCustomField : rafraichissement client custom uwow, non porte + } + } + + void OnUpdate(uint32 diff, AuraEffect* /*auraEffect*/) + { + if (tickTimer) + { + if (tickTimer <= diff) + { + tickTimer = 3000; + + if (GetUnitOwner() && GetUnitOwner()->GetMap()->IsHeroicPlusRaid() && GetUnitOwner()->IsAlive()) + { + if (auto instance = GetUnitOwner()->GetInstanceScript()) + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) != IN_PROGRESS) + return; + + if (GetId() == SPELL_SPIRITUAL_BARRIER_VIS_REAL || GetId() == SPELL_SPIRITUAL_BARRIER_REAL_REALM) + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DISSONANCE_REAL_FILTER, true); + else if (GetId() == SPELL_SPIRITUAL_BARRIER_VIS_SPIRIT || GetId() == SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM) + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DISSONANCE_SPIRIT_FILTER, true); + } + else + tickTimer = 0; + } + else + tickTimer -= diff; + } + } + + void Register() override + { + OnEffectApply += AuraEffectRemoveFn(spell_tos_spiritual_barrier_dissonance_visual_aura::OnApply, EFFECT_1, SPELL_AURA_ANY, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_tos_spiritual_barrier_dissonance_visual_aura::OnRemove, EFFECT_1, SPELL_AURA_ANY, AURA_EFFECT_HANDLE_REAL); + /* hook EffectUpdate (tick par diff) non supporte par notre AuraScript */; + } +}; + +// 235732, 235734 +class spell_tos_spiritual_barrier_dissonance_phase_aura : public AuraScript +{ + PrepareAuraScript(spell_tos_spiritual_barrier_dissonance_phase_aura); + + uint32 tickTimer = 3000; + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()) + { + GetTarget()->SetFlag(OBJECT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NOT_SELECTABLE_MODEL); + + if (GetTarget()->IsPlayer()) + { + ; // UpdateCustomField : rafraichissement client custom uwow, non porte + bool isSpiritRealm = GetId() == SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM; + + HostileRefManager& refManager = GetTarget()->getHostileRefManager(); + HostileReference* ref = refManager.getFirst(); + + while (ref) + { + if (auto unit = ref->GetSource()->GetOwner()) + if (auto creature = unit->ToCreature()) + { + switch (creature->GetEntry()) + { + // Real Realm + case NPC_ENGINE_OF_SOULS: + case NPC_REANIMATED_TEMPLAR: + case NPC_GHASTLY_BONEWARDEN: + case NPC_TORMENTED_CRIES_TRIG: + case NPC_FALLEN_PRIESTESS_MIRROR: + case NPC_SOUL_RESIDUE_MIRROR: + if (isSpiritRealm) + creature->getThreatManager().modifyThreatPercent(GetTarget(), -100); + break; + + // Spirit Realm + case NPC_SOUL_QUEEN_DEJAHNA: + case NPC_FALLEN_PRIESTESS: + case NPC_SOUL_RESIDUE: + case NPC_REANIMATED_TEMPLAR_MIRROR: + case NPC_GHASTLY_BONEWARDEN_MIRROR: + if (!isSpiritRealm) + creature->getThreatManager().modifyThreatPercent(GetTarget(), -100); + break; + default: + break; + } + } + + ref = ref->next(); + } + } + } + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()) + { + GetTarget()->RemoveFlag(OBJECT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NOT_SELECTABLE_MODEL); + + if (GetTarget()->IsPlayer()) + ; // UpdateCustomField : rafraichissement client custom uwow, non porte + } + } + + void OnUpdate(uint32 diff, AuraEffect* /*auraEffect*/) + { + if (tickTimer) + { + if (tickTimer <= diff) + { + tickTimer = 3000; + + if (GetUnitOwner() && GetUnitOwner()->GetMap()->IsHeroicPlusRaid() && GetUnitOwner()->IsAlive()) + { + if (auto instance = GetUnitOwner()->GetInstanceScript()) + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) != IN_PROGRESS) + return; + + if (GetId() == SPELL_SPIRITUAL_BARRIER_VIS_REAL || GetId() == SPELL_SPIRITUAL_BARRIER_REAL_REALM) + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DISSONANCE_REAL_FILTER, true); + else if (GetId() == SPELL_SPIRITUAL_BARRIER_VIS_SPIRIT || GetId() == SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM) + GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DISSONANCE_SPIRIT_FILTER, true); + } + else + tickTimer = 0; + } + else + tickTimer -= diff; + } + } + + void Register() override + { + OnEffectApply += AuraEffectRemoveFn(spell_tos_spiritual_barrier_dissonance_phase_aura::OnApply, EFFECT_0, SPELL_AURA_PHASE, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_tos_spiritual_barrier_dissonance_phase_aura::OnRemove, EFFECT_0, SPELL_AURA_PHASE, AURA_EFFECT_HANDLE_REAL); + /* hook EffectUpdate (tick par diff) non supporte par notre AuraScript */; + } +}; + +//966000 +struct at_tos_spirit_presence : AreaTriggerAI +{ + at_tos_spirit_presence(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + bool IsValidTarget(Unit* caster, Unit* target, AreaTriggerActionMoment actionM) override + { + if (!target) + return false; + + if (actionM & (AT_ACTION_MOMENT_ENTER | AT_ACTION_MOMENT_UPDATE_TARGET)) + { + if (target->IsPlayer()) + { + if (target->HasAura(SPELL_SPIRIT_REALM) && !target->HasAura(SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM)) + { + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_BARRIER_REAL_REALM); + target->CastSpell(target, SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM, true); + } + else if (!target->HasAura(SPELL_SPIRIT_REALM) && !target->HasAura(SPELL_SPIRITUAL_BARRIER_REAL_REALM)) + { + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM); + target->CastSpell(target, SPELL_SPIRITUAL_BARRIER_REAL_REALM, true); + } + } + else + { + switch (target->GetEntry()) + { + //Real Realm + case NPC_ENGINE_OF_SOULS: + case NPC_REANIMATED_TEMPLAR: + case NPC_GHASTLY_BONEWARDEN: + case NPC_TORMENTED_CRIES_TRIG: + case NPC_FALLEN_PRIESTESS_MIRROR: + case NPC_SOUL_RESIDUE_MIRROR: + if (!target->HasAura(SPELL_SPIRITUAL_BARRIER_VIS_REAL)) + target->CastSpell(target, SPELL_SPIRITUAL_BARRIER_VIS_REAL, true); + break; + //Spirit Realm + case NPC_SOUL_QUEEN_DEJAHNA: + case NPC_FALLEN_PRIESTESS: + case NPC_SOUL_RESIDUE: + case NPC_REANIMATED_TEMPLAR_MIRROR: + case NPC_GHASTLY_BONEWARDEN_MIRROR: + if (!target->HasAura(SPELL_SPIRITUAL_BARRIER_VIS_SPIRIT)) + { + target->CastSpell(target, SPELL_SPIRITUAL_BARRIER_VIS_SPIRIT, true); + target->CastSpell(target, SPELL_SPIRIT_REALM, true); + } + break; + default: + break; + } + + if (auto owner = target->GetAnyOwner()) + { + if (owner->IsPlayer()) + { + if (owner->HasAura(SPELL_SPIRIT_REALM)) + { + if (!target->HasAura(SPELL_SPIRIT_REALM)) + { + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_BARRIER_VIS_REAL); + target->CastSpell(target, SPELL_SPIRIT_REALM, true); + target->CastSpell(target, SPELL_SPIRITUAL_BARRIER_VIS_SPIRIT, true); + } + } + else + { + if (target->HasAura(SPELL_SPIRIT_REALM)) + { + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_BARRIER_VIS_SPIRIT); + target->RemoveAurasDueToSpell(SPELL_SPIRIT_REALM); + target->CastSpell(target, SPELL_SPIRITUAL_BARRIER_VIS_REAL, true); + } + } + } + } + } + } + else + { + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_BARRIER_SPIRIT_REALM); + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_BARRIER_REAL_REALM); + target->RemoveAurasDueToSpell(SPELL_SPIRIT_REALM); + } + + return true; + } +}; + +void AddSC_boss_the_desolate_host() +{ + RegisterCreatureAI(boss_the_desolate_host_generic); + RegisterCreatureAI(npc_tos_engine_of_souls); + RegisterCreatureAI(npc_tos_soul_queen_dejahna); + RegisterCreatureAI(npc_tos_desolate_host); + RegisterCreatureAI(npc_tos_spiritual_font); + RegisterCreatureAI(npc_tos_reanimated_templar); + RegisterCreatureAI(npc_tos_ghastly_bonewarden); + RegisterCreatureAI(npc_tos_fallen_priestess); + RegisterCreatureAI(npc_tos_soul_residue); + RegisterCreatureAI(npc_tos_tormented_cries); + RegisterSpellScript(spell_tos_quietus_filter); + RegisterSpellScript(spell_tos_soulbind_finder); + RegisterSpellScript(spell_tos_soulbind_visual_finder); + RegisterSpellScript(spell_tos_sundering_doom); + RegisterSpellScript(spell_tos_dissonance); + RegisterSpellScript(spell_tos_dissonance_filter); + RegisterSpellScript(spell_tos_spear_of_anguish); + RegisterSpellScript(spell_tos_bound_essence); + RegisterSpellScript(spell_tos_spear_of_anguish_filter); + RegisterSpellScript(spell_tos_tormented_cries_filter); + RegisterAuraScript(spell_tos_soulbind); + RegisterAuraScript(spell_tos_shattering_scream); + RegisterAuraScript(spell_tos_spiritual_barrier_dissonance_visual_aura); + RegisterAuraScript(spell_tos_spiritual_barrier_dissonance_phase_aura); + RegisterAreaTriggerAI(at_tos_spirit_presence); +} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_demonic_inquisition.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_demonic_inquisition.cpp deleted file mode 100644 index b7f13e4..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_demonic_inquisition.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* -* Copyright (C) 2016-2019 MagicStaorm -* -* Este codigo es de uso exclusivo y privado para la red de gaming -* ColossusWoW está totalmente prohibida su distribución y -* uso en proyectos ajenos, si has obtenido o encontrado este codigo -* publicamente envianos un email a administracion@colossuswow.com -* indicandonos como y donde has obtenido este codigo -* -* Recuerda, no distribuyas, no compartas y no subas este codigo a ningun -* lugar publico, usa siempre nuestros repositorios Git! -*/ - -#include "tomb_of_sargeras.h" - -enum atriganSpells -{ - SPELL_TORMENT = 233104, - SPELL_BONE_SCYTHE = 233423, - SPELL_SCYTHE_SWIP = 233426, - SPELL_BONE_SAW = 233441, - SPELL_UNBEARABLE_TORMENT = 233430, -}; - -enum atriganEvents -{ - EVENT_TORMENT = 1, - EVENT_BONE_SCYTHE, - EVENT_SCYTHE_SWIP, - EVENT_BONE_SAW, - EVENT_CHECK_FOR_PLAYERS, -}; - -class boss_atrigan : public CreatureScript -{ -public: - boss_atrigan() : CreatureScript("boss_atrigan") {} - - struct boss_atriganAI : public BossAI - { - boss_atriganAI(Creature* creature) : BossAI(creature, DATA_ATRIGAN) - { - instance->SetBossState(DATA_ATRIGAN, NOT_STARTED); - } - - Vehicle* vehicle; - bool isCasting; - int8 count = 0; - - void Reset() override - { - _Reset(); - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - instance->SetBossState(DATA_ATRIGAN, FAIL); - } - - events.Reset(); - } - - void EnterEvadeMode(EvadeReason why) override - { - BossAI::EnterEvadeMode(); - - me->GetMotionMaster()->MoveTargetedHome(); - - _DespawnAtEvade(0, nullptr); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - - //Talk(SAY_COMBAT); - - me->SetPower(POWER_ENERGY, 0); - - me->setActive(true); - DoZoneInCombat(); - - SetPlayersInCombat(true); - } - - void JustDied(Unit* /*killer*/) override - { - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _JustDied(); - instance->SetBossState(DATA_ATRIGAN, DONE); - } - - //Talk(SAY_DIED); - } - - void KilledUnit(Unit* victim) override - { - //if (victim->GetTypeId() == TYPEID_PLAYER) - // Talk(SAY_KILL); - } - - void JustReachedHome() override - { - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _JustReachedHome(); - instance->SetBossState(DATA_ATRIGAN, FAIL); - } - } - - void DespawnGameObjects(uint32 entry) - { - std::list gameObjects; - GetGameObjectListWithEntryInGrid(gameObjects, me, entry, 1000.0f); - - if (gameObjects.empty()) - return; - - for (std::list::iterator iter = gameObjects.begin(); iter != gameObjects.end(); ++iter) - (*iter)->Delete(); - } - - void DespawnCreatures(uint32 entry) - { - std::list creatures; - GetCreatureListWithEntryInGrid(creatures, me, entry, 1000.0f); - - if (creatures.empty()) - return; - - for (std::list::iterator iter = creatures.begin(); iter != creatures.end(); ++iter) - (*iter)->DespawnOrUnsummon(); - } - - void SetPlayersInCombat(bool evade) - { - uint8 players = 0; - Map::PlayerList const &PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.isEmpty()) - { - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - { - if (Player *player = i->GetSource()) - { - if (player->IsAlive() && player->GetDistance(me) <= 150.0f && !player->IsGameMaster()) - { - ++players; - - player->SetInCombatWith(me->ToUnit()); - } - } - } - if (players == 0 && evade == true) - _EnterEvadeMode(); - } - } - - 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_TORMENT: - break; - - case EVENT_BONE_SCYTHE: - break; - - case EVENT_SCYTHE_SWIP: - break; - - case EVENT_BONE_SAW: - break; - - case EVENT_CHECK_FOR_PLAYERS: - break; - - default: - break; - } - } - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTombOfSargerasAI(creature); - } -}; - -enum belacSpells -{ - SPELL_PANGS_OF_GUILT = 239401, - SPELL_CONFESS = 233652, - SPELL_TORMENTING_BURST = 234015, - SPELL_ECHOING_ANGUIS = 233983, - SPELL_FEL_SQUAL = 235230, -}; - -enum belacEvents -{ - EVENT_PANGS_OF_GUILT = 1, - EVENT_CONFESS, - EVENT_TORMENTING_BURST, - EVENT_ECHOING_ANGUIS, - EVENT_FEL_SQUAL, -}; - -class boss_belac : public CreatureScript -{ -public: - boss_belac() : CreatureScript("boss_belac") {} - - struct boss_belacAI : public BossAI - { - boss_belacAI(Creature* creature) : BossAI(creature, DATA_BELAC) - { - instance->SetBossState(DATA_BELAC, NOT_STARTED); - } - - void Reset() override - { - _Reset(); - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - instance->SetBossState(DATA_BELAC, FAIL); - } - } - - void EnterEvadeMode(EvadeReason why) override - { - BossAI::EnterEvadeMode(); - - me->GetMotionMaster()->MoveTargetedHome(); - - _DespawnAtEvade(0, nullptr); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - - //Talk(SAY_COMBAT); - - me->SetPower(POWER_ENERGY, 0); - - me->setActive(true); - DoZoneInCombat(); - - SetPlayersInCombat(true); - } - - void JustDied(Unit* /*killer*/) override - { - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _JustDied(); - instance->SetBossState(DATA_BELAC, DONE); - } - - //Talk(SAY_DIED); - } - - void KilledUnit(Unit* victim) override - { - //if (victim->GetTypeId() == TYPEID_PLAYER) - // Talk(SAY_KILL); - } - - void JustReachedHome() override - { - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _JustReachedHome(); - instance->SetBossState(DATA_BELAC, FAIL); - } - } - - void DespawnGameObjects(uint32 entry) - { - std::list gameObjects; - GetGameObjectListWithEntryInGrid(gameObjects, me, entry, 1000.0f); - - if (gameObjects.empty()) - return; - - for (std::list::iterator iter = gameObjects.begin(); iter != gameObjects.end(); ++iter) - (*iter)->Delete(); - } - - void DespawnCreatures(uint32 entry) - { - std::list creatures; - GetCreatureListWithEntryInGrid(creatures, me, entry, 1000.0f); - - if (creatures.empty()) - return; - - for (std::list::iterator iter = creatures.begin(); iter != creatures.end(); ++iter) - (*iter)->DespawnOrUnsummon(); - } - - void SetPlayersInCombat(bool evade) - { - uint8 players = 0; - Map::PlayerList const &PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.isEmpty()) - { - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - { - if (Player *player = i->GetSource()) - { - if (player->IsAlive() && player->GetDistance(me) <= 150.0f && !player->IsGameMaster()) - { - ++players; - - player->SetInCombatWith(me->ToUnit()); - } - } - } - if (players == 0 && evade == true) - _EnterEvadeMode(); - } - } - - 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_PANGS_OF_GUILT: - break; - - case EVENT_CONFESS: - break; - - case EVENT_TORMENTING_BURST: - break; - - case EVENT_ECHOING_ANGUIS: - break; - - case EVENT_FEL_SQUAL: - break; - - - default: - break; - } - } - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTombOfSargerasAI(creature); - } -}; - -void AddSC_boss_demonic_inquisition() -{ - new boss_atrigan(); - new boss_belac(); -} \ No newline at end of file diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_desolate_host.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_desolate_host.cpp deleted file mode 100644 index 1727832..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_desolate_host.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* -* Copyright (C) 2017-2018 AshamaneProject -* -* 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 "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" -#include "SpellAuras.h" -#include "SpellScript.h" -#include "tomb_of_sargeras.h" - -enum Spells -{ - SPELL_SPIRIT_WORLD = 235621 -}; - -void AddSC_boss_desolate_host() -{ -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_fallen_avatar.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_fallen_avatar.cpp deleted file mode 100644 index bd6048f..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_fallen_avatar.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* -lyosky -*/ - -#include "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" -#include "SpellAuras.h" -#include "SpellScript.h" -#include "InstanceScript.h" -#include "GameObject.h" -#include "PhaseShift.h" -#include "PhasingHandler.h" -#include "tomb_of_sargeras.h" - -enum Spells -{ - //Stage One: A Slumber Disturbed - SPELL_TOUCH_OF_SARGERAS = 239207,//?????? 14s 42s mythic 60 - SPELL_TOUCH_OF_SARGERAS_VISUAL = 234012,//239057 summon - SPELL_RUPTURE_REALITIES = 239132,//???? to do 31s 60s phase2 every 37s - //SPELL_RUPTURE_REALITIES = 239132,235572 - //SPELL_RUPTURE_REALITIES = 239152, //???? 236419 dmg 234090 visual - SPELL_UNBOUND_CHAOS = 234059, //7s 35-60 s - SPELL_UNBOUND_CHAOS_MARK = 239482, - SPELL_SHADOWY_BLADES = 236571,//27s 30-46 236591/???? 241588 - SPELL_SHADOWY_BLADES_MOVETO = 236591, - SPELL_SHADOWY_BLADES_DMG = 241588, - SPELL_DESOLATE = 236494,//?? 13s-19s 11.4s - //Maiden of Valor - NPC_MAIDEN_OF_VALOR = 120437, - SPELL_CORRUPTED_MATRIX = 233556,// 10s 40s - SPELL_MATRIX_EMPOWERMENT = 233961,// add - SPELL_CLEANSING_PROTOCOL = 233856, - SPELL_MALFUNCTION = 233739,// on 233856 remove ,damage 25%hp - //Stage Two: An Avatar Awakened - SPELL_DARK_MARK = 239739,//???? mythic 31.6 ,21 every mythic 25 34s - SPELL_RAIN_OF_THE_DESTROYER = 240396, //only mythic 15s 35s - - SPELL_RIPPLE_OF_DARKNESS = 236528,// on no one in range AOE - SPELL_SEAR = 234179, //aura - SPELL_INTRO = 239847,//???? - SPELL_ANNIHILATION = 235597,//PHASE_2 ?? - SPELL_CONSUME = 240594,//?? - - GO_GROUND = 267934, - NPC_CORRUPTED_BLADE = 119158, -}; - -enum Events -{ - EVENT_INTRO = 1, - EVENT_ATTACK_RANGE = 2000, -}; - -struct boss_fallen_avatar : public BossAI -{ - boss_fallen_avatar(Creature* creature) : BossAI(creature, DATA_FALLEN_AVATAR) { Initialize(); } - - void Initialize() - { - PhaseStatus = PHASE_01; - SetCombatMovement(false); - //me->SetDisableGravity(true); - IsLock = false; - } - - void Reset() override - { - _Reset(); - Initialize(); - if (Creature* maidenofvalor = me->FindNearestCreature(NPC_MAIDEN_OF_VALOR, 150.0f, true)) - maidenofvalor->DespawnOrUnsummon(); - //if (!me->FindNearestGameObject(GO_GROUND, 500.0f)) - // me->SummonGameObject(GO_GROUND, Position(5825.93f, -1216.87f, 2787.37f, 0.872664f), QuaternionData(), WEEK); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - instance->DoDelayedConversation(5000, 4955); - } - - void DoAction(int32 param) - { - switch (param) - { - case 1: - SwitchPhase(); - break; - } - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage) override - { - if (me->HealthWillBeBelowPctDamaged(20, damage)) - SwitchPhase(); - } - - void SwitchPhase() - { - if(PhaseStatus == PHASE_01) - { - PhaseStatus = PHASE_02; - SetCombatMovement(true); - //me->SetDisableGravity(false); - if (GameObject* ground = me->FindNearestGameObject(GO_GROUND, 150.0f)) - ground->Delete(); - - events.Reset(); - DoCastSelf(SPELL_ANNIHILATION); - AddTimedDelayedOperation(3500, [this]() -> void - { - me->NearTeleportTo(Position(6621.16f, -794.8966f, 1519.918f, 2.9986f)); - instance->DoNearTeleportPlayers(Position(6561.1f, -791.82f, 1509.91f, 6.2429f)); - DoCastSelf(SPELL_ANNIHILATION); - }); - AddTimedDelayedOperation(5000, [this]() -> void - { - //me->GetMotionMaster()->Clear(); - //me->GetMotionMaster()->MovePoint(0, Position(6561.1f, -791.82f, 1509.91f, 6.2429f), true); - //me->GetMotionMaster()->MoveChase(me->SelectNearestPlayer()); - DoCastSelf(SPELL_SEAR); - events.ScheduleEvent(SPELL_RUPTURE_REALITIES, 37s); - events.ScheduleEvent(SPELL_DESOLATE, 19s); - if (IsMythic()) - events.ScheduleEvent(SPELL_DARK_MARK, 31s, 32s); - else - events.ScheduleEvent(SPELL_DARK_MARK, 21s); - if (IsMythic()) - events.ScheduleEvent(SPELL_RAIN_OF_THE_DESTROYER, 15s); - }); - } - } - - void ScheduleTasks() override - { - PhaseStatus = PHASE_01; - me->RemoveAurasDueToSpell(SPELL_INTRO); - events.ScheduleEvent(SPELL_TOUCH_OF_SARGERAS, 14s, 15s); - events.ScheduleEvent(SPELL_RUPTURE_REALITIES, 31s); - events.ScheduleEvent(SPELL_UNBOUND_CHAOS, 7s); - events.ScheduleEvent(SPELL_SHADOWY_BLADES, 27s); - events.ScheduleEvent(SPELL_DESOLATE, 13s); - events.ScheduleEvent(NPC_MAIDEN_OF_VALOR, 4s); - - //events.ScheduleEvent(SPELL_ANNIHILATION, 120s); - //events.ScheduleEvent(SPELL_FEL_INFUSION, 5s); - //events.ScheduleEvent(SPELL_CONSUME, 5s); - } - - void ExecuteEvent(uint32 eventId) override - { - /* - for (auto threat : me->getThreatManager().getThreatList()) - if (me->IsWithinCombatRange(threat->getTarget(), me->GetCombatReach())) - me->RemoveAurasDueToSpell(SPELL_RIPPLE_OF_DARKNESS); - */ - - switch (eventId) - { - case SPELL_TOUCH_OF_SARGERAS: - { - if (Unit* target1 = SelectTarget(SELECT_TARGET_RANDOM, 0.0, 0.0, true)) - { - me->CastSpell(target1, SPELL_TOUCH_OF_SARGERAS, false); - - AddTimedDelayedOperation(2500, [this, target1]() -> void - { - me->CastSpell(target1, SPELL_TOUCH_OF_SARGERAS_VISUAL, false); - }); - } - - if(IsMythic()) - events.Repeat(60s); - else - events.Repeat(42s); - break; - } - case SPELL_RUPTURE_REALITIES: - { - if (Unit* target1 = SelectTarget(SELECT_TARGET_RANDOM, 0.0, 0.0, true)) - me->CastSpell(target1, SPELL_RUPTURE_REALITIES, false); - for (uint8 i = 0; i < 5; ++i) - me->SummonCreature(NPC_CORRUPTED_BLADE, me->GetRandomNearPosition(10.0f), TEMPSUMMON_MANUAL_DESPAWN, WEEK); - - if (PhaseStatus == PHASE_01) - events.Repeat(60s); - else - events.Repeat(37s); - break; - } - case SPELL_UNBOUND_CHAOS: - { - if (Unit* target1 = SelectTarget(SELECT_TARGET_RANDOM, 0.0, 0.0, true)) - { - me->CastSpell(target1, SPELL_UNBOUND_CHAOS, false); - AddTimedDelayedOperation(500, [this, target1]() -> void - { - me->CastSpell(target1, SPELL_UNBOUND_CHAOS_MARK, false); - }); - AddTimedDelayedOperation(1000, [this, target1]() -> void - { - me->CastSpell(target1, SPELL_UNBOUND_CHAOS_MARK, false); - }); - AddTimedDelayedOperation(1500, [this, target1]() -> void - { - me->CastSpell(target1, SPELL_UNBOUND_CHAOS_MARK, false); - }); - } - - events.Repeat(35s); - break; - } - case SPELL_SHADOWY_BLADES: - { - DoCast(SPELL_SHADOWY_BLADES); - events.Repeat(30s); - break; - } - case SPELL_DESOLATE: - { - DoCast(SPELL_DESOLATE); - events.Repeat(11s, 12s); - break; - } - case NPC_MAIDEN_OF_VALOR: - { - me->SummonCreature(NPC_MAIDEN_OF_VALOR, me->GetPositionWithDistInFront(25.0f), TEMPSUMMON_MANUAL_DESPAWN, WEEK); - break; - } - case SPELL_DARK_MARK: - { - if (Unit* target1 = SelectTarget(SELECT_TARGET_RANDOM, 0.0, 0.0, true)) - me->CastSpell(target1, SPELL_DARK_MARK, false); - if (Unit* target1 = SelectTarget(SELECT_TARGET_RANDOM, 0.0, 0.0, true)) - me->CastSpell(target1, SPELL_DARK_MARK, false); - if (IsMythic()) - events.Repeat(25s); - else - events.Repeat(34s); - break; - } - case SPELL_RAIN_OF_THE_DESTROYER: - { - DoCast(SPELL_RAIN_OF_THE_DESTROYER); - events.Repeat(35s); - break; - } - default: - if (me->GetDistance2d(me->SelectNearestPlayer())>me->GetCombatReach()) - me->CastSpell(me, SPELL_RIPPLE_OF_DARKNESS, false); - break; - } - } - - void MoveInLineOfSight(Unit* who) override - { - if (who->IsPlayer() && me->IsWithinDist(who, 25.0f, false) && !IsLock) - { - IsLock = true; - instance->DoConversation(4899); - if (Creature* jidan = me->FindNearestCreature(120867, 50.0f, true)) - { - AddTimedDelayedOperation(25000, [this, jidan]() -> void - { - jidan->CastSpell(me, 240897, true); - me->RemoveAurasDueToSpell(SPELL_INTRO); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - me->NearTeleportTo(me->GetPositionWithDistInFront(20.0f)); - }); - AddTimedDelayedOperation(30000, [this, jidan]() -> void - { - PhasingHandler::AddPhase(jidan, 180, true); - }); - //jidan->DespawnOrUnsummon(30000); - } - } - } - - void JustSummoned(Creature* summon) override - { - switch (summon->GetEntry()) - { - case NPC_MAIDEN_OF_VALOR: - { - summon->setFaction(me->getFaction()); - uint64 health = (uint64)(me->GetMaxHealth()*0.5f); - summon->SetMaxHealth(health); - summon->SetHealth(health); - break; - } - case NPC_CORRUPTED_BLADE: - { - summon->setFaction(me->getFaction()); - if (Unit* target1 = SelectTarget(SELECT_TARGET_RANDOM, 0.0, 0.0, true)) - { - AddTimedDelayedOperation(3000, [this, summon, target1]() -> void - { - summon->CastSpell(target1, SPELL_SHADOWY_BLADES_MOVETO, false); - }); - AddTimedDelayedOperation(5500, [this, summon, target1]() -> void - { - summon->CastSpell(target1, SPELL_SHADOWY_BLADES_DMG, false); - summon->DespawnOrUnsummon(); - }); - } - break; - } - } - } - /* - - */ - uint8 PhaseStatus; - uint8 PHASE_01; - uint8 PHASE_02; - bool IsLock; -}; - -struct npc_maiden_of_valor_120437 : public ScriptedAI -{ - npc_maiden_of_valor_120437(Creature* creature) : ScriptedAI(creature) { } - - void JustDied(Unit* /*killer*/) override - { - if (Creature* boss = me->FindNearestCreature(NPC_FALLEN_AVATAR, 150.0f, true)) - boss->AI()->DoAction(1); - } - /* - SPELL_CORRUPTED_MATRIX = 233556,// 10s 40s - SPELL_MATRIX_EMPOWERMENT = 233961,// add - SPELL_CLEANSING_PROTOCOL = 233856, - SPELL_MALFUNCTION = 233739,// on 233856 remove ,damage 25%hp - */ - void EnterCombat(Unit* /*victim*/) - { - events.ScheduleEvent(SPELL_CORRUPTED_MATRIX, 10s); - } - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - - if (!UpdateVictim()) - return; - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_CORRUPTED_MATRIX: - { - DoCast(SPELL_CORRUPTED_MATRIX); - events.Repeat(40s); - break; - } - } - } - DoMeleeAttackIfReady(); - } - - int8 IsLock; - - void MoveInLineOfSight(Unit* who) override - { - if (who->IsPlayer() && me->IsWithinDist(who, 25.0f, false) && !IsLock) - { - IsLock = true; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); - } - } -}; - -struct npc_corrupted_blade_119158 : public ScriptedAI -{ - npc_corrupted_blade_119158(Creature* creature) : ScriptedAI(creature) - { - me->SetReactState(REACT_PASSIVE); - SetCombatMovement(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); - } - - void UpdateAI(uint32 diff) override { } -}; - -void AddSC_boss_fallen_avatar() -{ - RegisterCreatureAI(boss_fallen_avatar); - RegisterCreatureAI(npc_maiden_of_valor_120437); - RegisterCreatureAI(npc_corrupted_blade_119158); -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_goroth.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_goroth.cpp deleted file mode 100644 index 41c2280..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_goroth.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* -* Copyright (C) 2017-2018 AshamaneProject -* -* 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 "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "AreaTriggerTemplate.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" -#include "SpellAuras.h" -#include "SpellMgr.h" -#include "SpellScript.h" -#include "tomb_of_sargeras.h" - -enum Spells -{ - // Goroth - SPELL_BURNING_ARMOR = 231363, - SPELL_BURNING_ERUPTION = 231395, // Casted at SPELL_BURNING_ARMOR end - - SPELL_CRASHING_COMET = 232249, - SPELL_CRASHING_COMET_DAMAGE = 230345, - - SPELL_INFERNAL_SPIKE_SUMMON = 233055, - SPELL_INFERNAL_SPIKE = 233019, - SPELL_INFERNAL_SPIKE_PROTECTION = 234475, - - SPELL_SHATTERING_STAR = 233272, - SPELL_SHATTERING_STAR_AT = 233279, - SPELL_SHATTERING_STAR_DAMAGE = 233281, - SPELL_SHATTERING_STAR_FINAL_DAMAGE = 233283, - - SPELL_INFERNAL_BURNING = 233062, - SPELL_INFERNAL_BURNING_REMOVE_SPIKES = 233078, - - // Infernal chaosbringer - SPELL_MASSIVE_ERUPTION = 242909 -}; - -enum Misc -{ - NPC_INFERNAL_SPIKE = 116976, - - SPELLVISUAL_INFERNAL_SPIKE_DESTROY = 66119, - - AT_SHATTERING_STAR = 13412 -}; - -enum Texts -{ - SAY_AGGRO = 1, - SAY_KILL = 2, - SAY_WIPE = 3, - SAY_DEATH = 4, - SAY_INFERNAL_BURNING = 5, - SAY_SHATTERING_STAR = 6, - SAY_INFERNAL_SPIKE = 7 -}; - -enum Events -{ - EVENT_MASSIVE_ERUPTION = 1 -}; - -struct boss_goroth : public BossAI -{ - boss_goroth(Creature* creature) : BossAI(creature, DATA_GOROTH) { } - - void EnterCombat(Unit* /*attacker*/) override - { - _EnterCombat(); - - Talk(SAY_AGGRO); - instance->SetBossState(DATA_GOROTH, IN_PROGRESS); - - events.ScheduleEvent(SPELL_BURNING_ARMOR, 16s); - events.ScheduleEvent(SPELL_CRASHING_COMET, 10s, 20s); - events.ScheduleEvent(SPELL_INFERNAL_SPIKE_SUMMON, 12s); - events.ScheduleEvent(SPELL_SHATTERING_STAR, 10s, 20s); - events.ScheduleEvent(SPELL_INFERNAL_BURNING, 55s); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_KILL); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - instance->SetBossState(DATA_GOROTH, DONE); - } - - void JustReachedHome() override - { - Talk(SAY_WIPE); - instance->SetBossState(DATA_GOROTH, FAIL); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case SPELL_BURNING_ARMOR: - { - if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO)) - DoCast(target, SPELL_BURNING_ARMOR); - - events.Repeat(16s); - break; - } - case SPELL_CRASHING_COMET: - { - UnitList targetList; - SelectTargetList(targetList, 3, SELECT_TARGET_RANDOM, 200.0f, true); - - for (Unit* target : targetList) - DoCast(target, SPELL_CRASHING_COMET, true); - - events.Repeat(10s, 20s); - break; - } - case SPELL_INFERNAL_SPIKE_SUMMON: - { - Talk(SAY_INFERNAL_SPIKE); - UnitList targetList; - SelectTargetList(targetList, 3, SELECT_TARGET_RANDOM, 200.0f, true); - - for (Unit* target : targetList) - DoCast(target, SPELL_INFERNAL_SPIKE_SUMMON); - - events.Repeat(12s); - break; - } - case SPELL_SHATTERING_STAR: - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 2)) - { - Talk(SAY_SHATTERING_STAR); - _shatteringStarTargetGUID = target->GetGUID(); - DoCast(target, SPELL_SHATTERING_STAR); - } - - events.Repeat(10s, 20s); - break; - } - case SPELL_INFERNAL_BURNING: - { - Talk(SAY_INFERNAL_BURNING); - DoCast(SPELL_INFERNAL_BURNING); - events.Repeat(55s); - break; - } - default: - break; - } - } - - void JustSummoned(Creature* summon) override - { - BossAI::JustSummoned(summon); - - if (summon->GetEntry() == NPC_INFERNAL_SPIKE) - summon->CastSpell(summon, SPELL_INFERNAL_SPIKE, false); - } - - ObjectGuid GetGUID(int32 /*id*/ = 0) const override - { - return _shatteringStarTargetGUID; - } - -private: - ObjectGuid _shatteringStarTargetGUID; -}; - -struct infernal_chaosbringer : public ScriptedAI -{ - infernal_chaosbringer(Creature* creature) : ScriptedAI(creature), Summons(me) { } - - void Reset() override - { - _events.Reset(); - me->CombatStop(true); - } - - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_MASSIVE_ERUPTION, 15s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_MASSIVE_ERUPTION: - - DoCastAOE(SPELL_MASSIVE_ERUPTION); - _events.Repeat(15s); - break; - - default: - - break; - } - } - - DoMeleeAttackIfReady(); - } - -private: - EventMap _events; - SummonList Summons; -}; - -// 231363 -class spell_burning_armor : public AuraScript -{ - PrepareAuraScript(spell_burning_armor); - - void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->CastSpell(nullptr, SPELL_BURNING_ERUPTION, true); - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_burning_armor::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } -}; - -// 232249 -class aura_crashing_comet : public AuraScript -{ - PrepareAuraScript(aura_crashing_comet); - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - caster->CastSpell(GetTarget(), SPELL_CRASHING_COMET_DAMAGE, true); - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(aura_crashing_comet::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } -}; - -// 230345 -class spell_crashing_comet_damage : public SpellScript -{ - PrepareSpellScript(spell_crashing_comet_damage); - - void HandleHit(SpellEffIndex /*effIndex*/) - { - Unit* target = GetHitUnit(); - if (!target) - return; - - CreatureList infernalSpikes; - target->GetCreatureListWithEntryInGrid(infernalSpikes, NPC_INFERNAL_SPIKE, GetSpellInfo()->GetEffect(EFFECT_0)->CalcRadius()); - - for (Creature* infernalSpike : infernalSpikes) - { - infernalSpike->SendPlaySpellVisual(infernalSpike->GetGUID(), SPELLVISUAL_INFERNAL_SPIKE_DESTROY); - infernalSpike->DespawnOrUnsummon(); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_crashing_comet_damage::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - } -}; - -// 233062 -class spell_infernal_burning : public SpellScript -{ - PrepareSpellScript(spell_infernal_burning); - - void BeforeCastHandler() - { - Creature* caster = GetCaster()->ToCreature(); - if (!caster) - return; - - CreatureList infernalSpikes; - caster->GetCreatureListWithEntryInGrid(infernalSpikes, NPC_INFERNAL_SPIKE, 200.0f); - - for (auto reference : caster->getThreatManager().getThreatList()) - for (Creature* spike : infernalSpikes) - if (spike->IsInBetween(GetCaster(), reference->getTarget(), 2.0f)) - reference->getTarget()->AddAura(SPELL_INFERNAL_SPIKE_PROTECTION); - } - - void AfterCastHandler() - { - if (Creature* caster = GetCaster()->ToCreature()) - for (auto reference : caster->getThreatManager().getThreatList()) - reference->getTarget()->RemoveAurasDueToSpell(SPELL_INFERNAL_SPIKE_PROTECTION); - } - - void Register() override - { - BeforeCast += SpellCastFn(spell_infernal_burning::BeforeCastHandler); - AfterCast += SpellCastFn(spell_infernal_burning::AfterCastHandler); - } -}; - -// 233078 -class spell_infernal_burning_remove_spikes : public SpellScript -{ - PrepareSpellScript(spell_infernal_burning_remove_spikes); - - void HandleOnCast() - { - CreatureList infernalSpikes; - GetCaster()->GetCreatureListWithEntryInGrid(infernalSpikes, NPC_INFERNAL_SPIKE, GetSpellInfo()->GetMaxRange()); - - for (Creature* infernalSpike : infernalSpikes) - { - infernalSpike->SendPlaySpellVisual(GetCaster()->GetGUID(), SPELLVISUAL_INFERNAL_SPIKE_DESTROY); - infernalSpike->DespawnOrUnsummon(); - } - } - - void Register() override - { - OnCast += SpellCastFn(spell_infernal_burning_remove_spikes::HandleOnCast); - } -}; - -// 233274 -class spell_shattering_star_dummy : public SpellScript -{ - PrepareSpellScript(spell_shattering_star_dummy); - - void HandleHit(SpellEffIndex /*effIndex*/) - { - if (Creature* goroth = GetHitUnit()->ToCreature()) - if (goroth->AI()) - goroth->CastSpell(GetCaster(), SPELL_SHATTERING_STAR_AT, true); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_shattering_star_dummy::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); - } -}; - -//AT : 13412 -//Spell : 233279 -struct at_goroth_shattering_star : AreaTriggerAI -{ - at_goroth_shattering_star(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } - - void OnInitialize() override - { - if (Unit* caster = at->GetCaster()) - if (Creature* creCaster = caster->ToCreature()) - if (creCaster->IsAIEnabled) - if (Unit* target = ObjectAccessor::GetUnit(*creCaster, creCaster->AI()->GetGUID())) - at->SetDestination(*target, 1000); - } - - void OnUnitEnter(Unit* unit) override - { - if (unit->GetEntry() == NPC_INFERNAL_SPIKE) - { - ++_infernalSpikeTouched; - unit->SendPlaySpellVisual(unit->GetGUID(), SPELLVISUAL_INFERNAL_SPIKE_DESTROY); - unit->ToCreature()->DespawnOrUnsummon(); - } - else if (Unit* caster = at->GetCaster()) - { - if (caster->IsValidAttackTarget(unit)) - caster->CastSpell(unit, SPELL_SHATTERING_STAR_DAMAGE, true); - } - } - - void OnDestinationReached() override - { - if (Unit* caster = at->GetCaster()) - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_SHATTERING_STAR_FINAL_DAMAGE)) - if (SpellEffectInfo const* spellEffectInfo = spellInfo->GetEffect(caster->GetMap()->GetDifficultyID(), EFFECT_0)) - caster->CastCustomSpell(SPELL_SHATTERING_STAR_FINAL_DAMAGE, SPELLVALUE_BASE_POINT0, spellEffectInfo->BasePoints / std::max(_infernalSpikeTouched, uint8(1)), nullptr, TRIGGERED_FULL_MASK); - - // TODO : Deal damage to target if _infernalSpikeTouched == 0 - at->Remove(); - } - - uint8 _infernalSpikeTouched = 0; -}; - -void AddSC_boss_goroth() -{ - RegisterCreatureAI(boss_goroth); - RegisterCreatureAI(infernal_chaosbringer); - - RegisterAuraScript(spell_burning_armor); - RegisterAuraScript(aura_crashing_comet); - RegisterSpellScript(spell_crashing_comet_damage); - RegisterSpellScript(spell_infernal_burning); - RegisterSpellScript(spell_infernal_burning_remove_spikes); - RegisterSpellScript(spell_shattering_star_dummy); - - RegisterAreaTriggerAI(at_goroth_shattering_star); -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_harjatan.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_harjatan.cpp deleted file mode 100644 index 9c66b8c..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_harjatan.cpp +++ /dev/null @@ -1,640 +0,0 @@ -/* -* Copyright (C) 2016-2019 MagicStorm -* -* Este codigo es de uso exclusivo y privado para la red de gaming -* ColossusWoW est? totalmente prohibida su distribuci?n y -* uso en proyectos ajenos, si has obtenido o encontrado este codigo -* publicamente envianos un email a administracion@colossuswow.com -* indicandonos como y donde has obtenido este codigo -* -* Recuerda, no distribuyas, no compartas y no subas este codigo a ningun -* lugar publico, usa siempre nuestros repositorios Git! -*/ - -#include "AreaTriggerTemplate.h" -#include "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "SpellAuraEffects.h" -#include "ScriptMgr.h" -#include "GameObject.h" -#include "ScriptedCreature.h" -#include "tomb_of_sargeras.h" -#include "SpellMgr.h" - -enum harjatanSpells -{ - SPELL_ABRASIVE_ARMOR = 233071, - SPELL_JAGGED_ABRASION = 231998, - SPELL_UNCHECKED_RAGE = 231854, - SPELL_COMMANDING_ROAR = 232192, - SPELL_DRENCHED = 231770, - SPELL_WATERY_SPLASH = 233371, - SPELL_AQUEOUS_BURST = 231729, - SPELL_AQUEOUS_BURST_DMG = 231754, - SPELL_DRIPPING_BLADE = 239907, - SPELL_DRIVEN_ASSAULT = 234128, - SPELL_SPLASHY_CLEAVE = 234129, - SPELL_DRENCHING_WATERS = 231768, - SPELL_DRAW_IN = 232061, - SPELL_FRIGID_BLOWS = 233429, - SPELL_GATHER_ENERGY = 232379, - SPELL_ENRAGED = 241587, - SPELL_FROSTY_DISCHARGE = 232174, -}; - -enum harjatanEvents -{ - EVENT_UNCHECKED_RAGE = 1, - EVENT_COMMANDING_ROAR, - EVENT_WATERY_SPLASH, - EVENT_AQUEOUS_BURST, - EVENT_DRIVEN_ASSAULT, - EVENT_SPLASHY_CLEAVE, - EVENT_DRENCHING_WATERS, - EVENT_DRAW_IN, - EVENT_CHECK_FOR_PLAYERS, -}; - -class boss_harjatan : public CreatureScript -{ -public: - boss_harjatan() : CreatureScript("boss_harjatan") {} - - struct boss_harjatanAI : public BossAI - { - boss_harjatanAI(Creature* creature) : BossAI(creature, DATA_HARJATAN) - { - instance->SetBossState(DATA_HARJATAN, NOT_STARTED); - - //me->setPowerType(POWER_ENERGY); - me->SetMaxPower(POWER_ENERGY, 100); - me->SetPower(POWER_ENERGY, 100); - } - - bool isCasting; - std::list waveMender; - std::list gladiator; - - void Reset() override - { - _Reset(); - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - instance->SetBossState(DATA_HARJATAN, FAIL); - } - - if (GameObject* go = me->FindNearestGameObject(269192, 500.0f)) - go->SetLootState(GO_READY); - - isCasting = false; - - me->RemoveAurasDueToSpell(SPELL_GATHER_ENERGY); - - //me->setPowerType(POWER_ENERGY); - me->SetMaxPower(POWER_ENERGY, 100); - me->SetPower(POWER_ENERGY, 0); - - events.Reset(); - } - - void EnterEvadeMode(EvadeReason why) override - { - BossAI::EnterEvadeMode(); - - me->GetMotionMaster()->MoveTargetedHome(); - - _DespawnAtEvade(0, nullptr); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - - DoCast(SPELL_ABRASIVE_ARMOR); - - //Talk(SAY_COMBAT); - - me->SetPower(POWER_ENERGY, 0); - - events.ScheduleEvent(EVENT_COMMANDING_ROAR, 17.3 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_DRAW_IN, 59 * IN_MILLISECONDS); - - if (GameObject* go = me->FindNearestGameObject(269192, 500.0f)) - go->UseDoorOrButton(1000000, true); - - if (Creature* creature = me->FindNearestCreature(121184, 150.0f)) - creature->SetVisible(false); - - me->setActive(true); - DoZoneInCombat(); - - DoCast(SPELL_GATHER_ENERGY); - - SetPlayersInCombat(true); - } - - void JustDied(Unit* /*killer*/) override - { - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _JustDied(); - instance->SetBossState(DATA_HARJATAN, DONE); - } - - if (GameObject* go = me->FindNearestGameObject(269192, 500.0f)) - go->SetLootState(GO_READY); - - if (GameObject* go = me->FindNearestGameObject(269120, 500.0f)) - go->UseDoorOrButton(1000000, true); - - //Talk(SAY_DIED); - } - - void KilledUnit(Unit* victim) override - { - //if (victim->GetTypeId() == TYPEID_PLAYER) - // Talk(SAY_KILL); - } - - void JustReachedHome() override - { - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _JustReachedHome(); - instance->SetBossState(DATA_HARJATAN, FAIL); - } - } - - void DespawnGameObjects(uint32 entry) - { - std::list gameObjects; - GetGameObjectListWithEntryInGrid(gameObjects, me, entry, 1000.0f); - - if (gameObjects.empty()) - return; - - for (std::list::iterator iter = gameObjects.begin(); iter != gameObjects.end(); ++iter) - (*iter)->Delete(); - } - - void DespawnCreatures(uint32 entry) - { - std::list creatures; - GetCreatureListWithEntryInGrid(creatures, me, entry, 1000.0f); - - if (creatures.empty()) - return; - - for (std::list::iterator iter = creatures.begin(); iter != creatures.end(); ++iter) - (*iter)->DespawnOrUnsummon(); - } - - void SetPlayersInCombat(bool evade) - { - uint8 players = 0; - Map::PlayerList const &PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.isEmpty()) - { - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - { - if (Player *player = i->GetSource()) - { - if (player->IsAlive() && player->GetDistance(me) <= 150.0f && !player->IsGameMaster()) - { - ++players; - - player->SetInCombatWith(me->ToUnit()); - } - } - } - if (players == 0 && evade == true) - _EnterEvadeMode(); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - events.Update(diff); - - if (uint32 power = me->GetPower(POWER_ENERGY)) - if (power == 100) - if (!isCasting) - { - events.ScheduleEvent(EVENT_UNCHECKED_RAGE, 1 * IN_MILLISECONDS); - isCasting = true; - } - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_UNCHECKED_RAGE: - DoCast(SPELL_UNCHECKED_RAGE); - - isCasting = false; - break; - - case EVENT_COMMANDING_ROAR: - DoCast(SPELL_COMMANDING_ROAR); - - for (uint8 i = 0; i < 2; ++i) - { - me->SummonCreature(NPC_RAZORJAW_WAVEMENDER, 5898.825f, -879.8507f, 2941.018f, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); - me->SummonCreature(NPC_RAZORJAW_GLADIATOR, 5876.136f, -887.0951f, 2933.093f, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); - } - - events.ScheduleEvent(EVENT_COMMANDING_ROAR, 31.8 * IN_MILLISECONDS); - break; - - case EVENT_DRAW_IN: - DoCast(SPELL_DRAW_IN); - - events.ScheduleEvent(EVENT_DRAW_IN, 59 * IN_MILLISECONDS); - break; - - case EVENT_CHECK_FOR_PLAYERS: - SetPlayersInCombat(true); - - events.ScheduleEvent(EVENT_CHECK_FOR_PLAYERS, 1 * IN_MILLISECONDS); - break; - - default: - break; - } - } - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTombOfSargerasAI(creature); - } -}; - -// 116569 - Razorjaw Wavemender -class npc_razorjaw_wavemender : public CreatureScript -{ -public: - - npc_razorjaw_wavemender() : CreatureScript("npc_razorjaw_wavemender") { } - - struct npc_razorjaw_wavemenderAI : public ScriptedAI - { - npc_razorjaw_wavemenderAI(Creature* creature) : ScriptedAI(creature) - { - } - - void Reset() override - { - events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_WATERY_SPLASH, 10 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_AQUEOUS_BURST, 12 * IN_MILLISECONDS); - } - - 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_WATERY_SPLASH: - if (Unit* target = me->GetVictim()) - me->CastSpell(target, SPELL_WATERY_SPLASH, true); - - events.ScheduleEvent(EVENT_WATERY_SPLASH, 10 * IN_MILLISECONDS); - break; - - case EVENT_AQUEOUS_BURST: - if (Unit* target = me->GetVictim()/*SelectTarget(SELECT_TARGET_RANDOM, 0, 5.0f, true)*/) - me->CastSpell(target, SPELL_AQUEOUS_BURST, true); - - events.ScheduleEvent(EVENT_AQUEOUS_BURST, 16 * IN_MILLISECONDS); - break; - } - } - - DoMeleeAttackIfReady(); - } - private: - EventMap events; - - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTombOfSargerasAI(creature); - } -}; - -// 117596 - Razorjaw Gladiator -class npc_razorjaw_gladiator : public CreatureScript -{ -public: - - npc_razorjaw_gladiator() : CreatureScript("npc_razorjaw_gladiator") { } - - struct npc_razorjaw_gladiatorAI : public ScriptedAI - { - npc_razorjaw_gladiatorAI(Creature* creature) : ScriptedAI(creature) - { - } - - std::list areaTriggerList; - - void Reset() override - { - events.Reset(); - - me->RemoveAllAreaTriggers(); - } - - void EnterCombat(Unit* /*who*/) override - { - DoCast(SPELL_DRIPPING_BLADE); - - events.ScheduleEvent(EVENT_DRIVEN_ASSAULT, 10 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SPLASHY_CLEAVE, 12 * IN_MILLISECONDS); - } - - 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_DRIVEN_ASSAULT: - if (Unit* target = me->GetVictim()/*SelectTarget(SELECT_TARGET_RANDOM, 0, 5.0f, true)*/) - { - if (!target->HasAura(240792)) - { - me->CastSpell(target, 234016, true); - me->CastSpell(me, SPELL_DRIVEN_ASSAULT, true); - } - else - events.ScheduleEvent(EVENT_DRIVEN_ASSAULT, 1 * IN_MILLISECONDS); - } - - events.ScheduleEvent(EVENT_DRIVEN_ASSAULT, 10 * IN_MILLISECONDS); - break; - - case EVENT_SPLASHY_CLEAVE: - DoCast(SPELL_SPLASHY_CLEAVE); - - events.ScheduleEvent(EVENT_SPLASHY_CLEAVE, 12 * IN_MILLISECONDS); - break; - } - } - - DoMeleeAttackIfReady(); - } - - private: - EventMap events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTombOfSargerasAI(creature); - } -}; - -// 233429 - frigid blows -class spell_harjatan_frigid_blows : public SpellScriptLoader -{ -public: - spell_harjatan_frigid_blows() : SpellScriptLoader("spell_harjatan_frigid_blows") { } - - class spell_harjatan_frigid_blows_AuraScript : public AuraScript - { - PrepareAuraScript(spell_harjatan_frigid_blows_AuraScript); - - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* target = GetTarget()) - if (Aura* aura = target->GetAura(GetId())) - if (aura->GetStackAmount() <= 1) - target->CastSpell(target, SPELL_FROSTY_DISCHARGE, true); - } - - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_harjatan_frigid_blows_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_harjatan_frigid_blows_AuraScript(); - } -}; - -// 231729 - Aqueous Burst -class spell_harjatan_aqueous_burst : public SpellScriptLoader -{ -public: - spell_harjatan_aqueous_burst() : SpellScriptLoader("spell_harjatan_aqueous_burst") { } - - class spell_harjatan_aqueous_burst_AuraScript : public AuraScript - { - PrepareAuraScript(spell_harjatan_aqueous_burst_AuraScript); - - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* target = GetTarget()) - target->CastSpell(target, SPELL_AQUEOUS_BURST_DMG, true); - } - - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_harjatan_aqueous_burst_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_harjatan_aqueous_burst_AuraScript(); - } -}; - -// 13251 - Drenching waters -class areatrigger_drenching_waters : public AreaTriggerEntityScript -{ -public: - areatrigger_drenching_waters() : AreaTriggerEntityScript("areatrigger_drenching_waters") { } - - struct areatrigger_drenching_watersAI : AreaTriggerAI - { - areatrigger_drenching_watersAI(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } - - bool isCasting = false; - uint32 checkTime; - uint8 count; - - void OnInitialize() override - { - checkTime = 1000; - count = 0; - } - - void OnUpdate(uint32 diff) override - { - if (checkTime <= diff) - { - if (Creature* boss = at->FindNearestCreature(BOSS_HARJATAN, 500.0f)) - if (boss->HasAura(SPELL_DRAW_IN)) - { - Position pos = at->GetNearPosition(float(-count), at->GetAngle(*boss)); - at->GetMap()->AreaTriggerRelocation(at, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); - } - - if (Creature* boss = at->FindNearestCreature(BOSS_HARJATAN, 5.0f)) - if (boss->HasAura(SPELL_DRAW_IN)) - { - boss->CastSpell(boss, SPELL_FRIGID_BLOWS, true); - at->RemoveFromWorld(); - } - - if (Creature* boss = at->FindNearestCreature(BOSS_HARJATAN, 500.0f, false)) - at->RemoveFromWorld(); - - checkTime = 1000; - } - else - checkTime -= diff; - } - - void OnUnitEnter(Unit* target) override - { - if (!target) - return; - - if (target->GetTypeId() != TYPEID_PLAYER) - return; - - if (Unit* caster = at->GetCaster()) - caster->CastSpell(target, SPELL_DRENCHING_WATERS, true); - } - - void OnUnitExit(Unit* target) override - { - if (!target) - return; - - if (target->GetTypeId() != TYPEID_PLAYER) - return; - - if (target->HasAura(SPELL_DRENCHING_WATERS)) - target->RemoveAurasDueToSpell(SPELL_DRENCHING_WATERS); - } - }; - - - AreaTriggerAI* GetAI(AreaTrigger* areatrigger) const override - { - return new areatrigger_drenching_watersAI(areatrigger); - } -}; - -// 231854 - Unchecked Rage -class spell_harjatan_unchecked_rage : public SpellScriptLoader -{ -public: - spell_harjatan_unchecked_rage() : SpellScriptLoader("spell_harjatan_unchecked_rage") { } - - class spell_harjatan_unchecked_rage_SpellScript : public SpellScript - { - PrepareSpellScript(spell_harjatan_unchecked_rage_SpellScript); - - void FilterTargets(std::list& targets) - { - uint8 count = 0; - for (auto target : targets) - ++count; - - if (count < 5) - if (Unit* caster = GetCaster()) - caster->CastSpell(caster, SPELL_ENRAGED, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_harjatan_unchecked_rage_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_CONE_ENEMY_104); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_harjatan_unchecked_rage_SpellScript(); - } -}; - -// 232174 - Frosty Discharge -class spell_harjatan_frosty_discharge : public SpellScriptLoader -{ -public: - spell_harjatan_frosty_discharge() : SpellScriptLoader("spell_harjatan_frosty_discharge") { } - - class spell_harjatan_frosty_discharge_SpellScript : public SpellScript - { - PrepareSpellScript(spell_harjatan_frosty_discharge_SpellScript); - - void HandleDummy() - { - if (Unit* caster = GetCaster()) - caster->CastSpell(caster, GetSpellInfo()->GetEffect(EFFECT_0)->BasePoints, true); - } - - void Register() override - { - OnCast += SpellCastFn(spell_harjatan_frosty_discharge_SpellScript::HandleDummy); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_harjatan_frosty_discharge_SpellScript(); - } -}; - -void AddSC_boss_harjatan() -{ - new boss_harjatan(); - new npc_razorjaw_wavemender(); - new npc_razorjaw_gladiator(); - new spell_harjatan_frigid_blows(); - new spell_harjatan_aqueous_burst(); - new areatrigger_drenching_waters(); - new spell_harjatan_unchecked_rage(); - new spell_harjatan_frosty_discharge(); -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_kiljaeden.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_kiljaeden.cpp deleted file mode 100644 index ba2792f..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_kiljaeden.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* -* Copyright (C) 2017-2018 AshamaneProject -* -* 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 "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" -#include "SpellAuras.h" -#include "SpellScript.h" -#include "tomb_of_sargeras.h" - -void AddSC_boss_tos_kiljaeden() -{ -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_maiden_of_vigilance.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_maiden_of_vigilance.cpp deleted file mode 100644 index ed2433e..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_maiden_of_vigilance.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* -* Copyright (C) 2017-2018 AshamaneProject -* -* 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 "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" -#include "SpellAuras.h" -#include "SpellScript.h" -#include "tomb_of_sargeras.h" - -void AddSC_boss_maiden_of_vigilance() -{ -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_sasszine.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_sasszine.cpp deleted file mode 100644 index 26efad8..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_sasszine.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* -* Copyright (C) 2017-2018 AshamaneProject -* -* 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 "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" -#include "SpellAuras.h" -#include "SpellScript.h" -#include "tomb_of_sargeras.h" - -void AddSC_boss_sasszine() -{ -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_sisters_of_moon.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/boss_sisters_of_moon.cpp deleted file mode 100644 index 5271f4d..0000000 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/boss_sisters_of_moon.cpp +++ /dev/null @@ -1,901 +0,0 @@ -/* -* Copyright (C) 2017-2019 AshamaneProject -* -* 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 "AreaTrigger.h" -#include "AreaTriggerAI.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" -#include "SpellAuras.h" -#include "InstanceScript.h" -#include "ObjectAccessor.h" -#include "SpellScript.h" -#include "tomb_of_sargeras.h" - - -//constant positions about teleports and Kasparian Jumps -Position const LunaspyreTeleports[5] = -{ - {}, - {}, - {}, - {}, - {}, -}; - -Position const CenterCombat[1] = -{ - {}, -}; - -Position const YathaeTeleports[5] = -{ - {}, - {}, - {}, - {}, - {}, -}; - -Position const KasparianJumps[5] = -{ - {}, - {}, - {}, - {}, - {}, -}; - -enum Says -{ - // Sisters - SAY_SISTERS_AGGRO = 1, - SAY_SISTERS_DEATH = 2, - SAY_SISTERS_EVADE = 3, - SAY_SISTER_KILLS = 4, - - // Kasparian - SAY_KASPARIAN_AGGRO = 1, - SAY_SPELL_GLAIVE = 2, - SAY_KASPARIAN_KILLS = 3, - SAY_KASPARIAN_EVADE = 4, - SAY_KASPARIAN_DIED = 5, - - // Yathae - SAY_YATHAE_AGGRO = 6, - SAY_PHASE_COMBAT = 7, - SAY_SPELLS_ARROW = 8, - SAY_SPELL_AOE = 9, - SAY_YATHAE_KILLS = 10, - SAY_YATHAE_EVADE = 11, - SAY_YATHAE_DIED = 12, - - // Lunaspyre - SAY_LUNASPYRE_AGGRO = 13, - SAY_SPELL = 14, - SAY_FOUNTAIN_OF_ELUNE = 15, - SAY_LUNASPYRE_KILLS = 16, - SAY_LUNASPYRE_EVADE = 17, - SAY_LUNASPYRE_DIED = 18 -}; - -enum SistersSpells -{ - // Generic - SPELL_QUIET_SUICIDE = 3617, // Serverside spell - SPELL_INCORPOREAL_TELEPORT = 236115, // Cast - SPELL_INCORPOREAL_TELEPORT_2 = 236224, // Not sure if this is the teleport or need script. Instant cast. These two teleport spells are the same for Lunaspyre - SPELL_COOLDOWN_CREATURE_SPECIAL = 61207, // To block other spells from interfering with their special - SPELL_GHOST_AURA = 235268, // Cosmetic for Sisters - SPELL_ELUNE_FOUNTAIN = 236357, // Create AT 11284 - SPELL_LUNAR_SUFFUSION = 234995, // Add the aura every 3 seconds on the bright side - SPELL_UMBRA_SUFFUSION = 234996, // Add the aura every 3 seconds on the dark side - SPELL_ASTRAL_PURGE = 234998, // Will trigger on change side in Fountain of Elune, and reset Suffusion marks. - - // Generic Talk - // Kasparian - SPELL_KASPARIAN_KILL_CONVERSATION_A = 243051, // When unactive sister kills a player - SPELL_KASPARIAN_KILL_CONVERSATION_B = 243052, // When unactive sister kills a player - SPELL_KASPARIAN_KILL_CONVERSATION_C = 243053, // When unactive sister kills a player - - // Yathae - SPELL_YATHAE_BECOMES_ACTIVE_CONVERSATION = 243044, // Probably at Start Combat in Active Phase - - // Lunaspyre - SPELL_LUNASPYRE_BECOMES_ACTIVE_CONVERSATION = 243047, // Probably at Start Combat in Active Phase - SPELL_LUNASPYRE_KILL_CONVERSATION_A = 243059, // When unactive sister kills a player - SPELL_LUNASPYPE_KILL_CONVERSATION_B = 243060, // When unactive sister kills a player - SPELL_LUNASPYRE_KILL_CONVERSATION_C = 243061, // When unactive sister kills a player - - // Kasparian - SPELL_TWILIGHT_GLAIVE_AT = 236529, // AT 9785 and add aura to bunny + make bunny got target,then return - SPELL_TWILIGHT_GLAIVE_DAMAGE = 236541, - SPELL_TWILIGHT_GLAIVE_DUMMY = 237561, - SPELL_MOON_GLAIVE = 236547, - SPELL_DISCORPORATE = 236550, // Astral Purge will remove it - SPELL_GLAIVE_STORM = 239379, // Only used in Elune's Fountain Phase 3 (normal), phase 2 and 3 heroic and mythic - - // Yathae - SPELL_SHADOW_SHOT = 237630, - SPELL_INCORPOREAL_SHOT_CAST = 236305, // Only used in Elune's Fountain Phase 2 (normal), phase 1 and 3 heroic and mythic - SPELL_INCORPOREAL_SHOT_DAMAGE = 236306, - SPELL_CALL_MOONTALON = 236694, - SPELL_TWILIGHT_VOLLEY_AT = 236442, // AT 9777 - SPELL_TWILIGHT_VOLLEY_DAMAGE = 236516, // Apply this aura OnUnitEnter(); - SPELL_RAPID_SHOT = 236598, - SPELL_RAPID_SHOT_AURA = 236596, - - // Lunaspyre - SPELL_EMBRACE_OF_THE_ECLIPSE_HEAL = 233263, // Shields Heal - SPELL_EMBRACE_OF_THE_ECLIPSE_BOSS = 233264, // Shield Boss - SPELL_LUNAR_BEACON = 236712, // OnRemove(); actives Lunar barrage. - SPELL_LUNAR_FIRE = 239264, // Only on current target,active. - SPELL_LUNAR_STRIKE = 237632, - SPELL_MOON_BURN = 236518, // Astral Purge will remove this. - SPELL_LUNAR_BARRAGE_AT = 236726, // AT 9807 - SPELL_LUNAR_BARRAGE_DAMAGE = 237351 -}; - -enum SistersEvents -{ - // General - EVENT_INCORPOREAL_TELEPORT = 1, - EVENT_TELEPORT_CENTER_COMBAT, - EVENT_BACK_TO_GHOST, - EVENT_BERSERKER, - - // Kasparian - EVENT_TWILIGHT_GLAIVE, - EVENT_MOON_GLAIVE, - EVENT_GLAIVE_STORM, - EVENT_JUMP_TIGER, - EVENT_KASPARIAN_JUST_DIED, - EVENT_KASPARIAN_AGGRO, - - // Yathae - EVENT_SHADOW_SHOT, - EVENT_INCORPOREAL_SHOT, - EVENT_CALL_MOONTALON, - EVENT_TWILIGHT_VOLLEY, - EVENT_RAPID_SHOT, - EVENT_YATHAE_JUST_DIED, - EVENT_YATHAE_AGGRO, - - // Lunaspyre - EVENT_EMBRACE_OF_THE_ECLIPSE, - EVENT_LUNAR_BEACON, - EVENT_LUNAR_FIRE, - EVENT_LUNAR_STRIKE, - EVENT_MOON_BURN, - EVENT_LUNASPYRE_JUST_DIED, - EVENT_LUNASPYRE_AGGRO -}; - -enum Actions -{ - ACTION_RETURN_PHASE1 = 1, - ACTION_RETURN = 2, - ACTION_SAY_SISTERS_AGGRO = 3, - ACTION_EVADE = 4, - ACTION_PHASE_2 = 5, - ACTION_PHASE_3 = 6, - ACTION_SISTERS_DEAD = 7 -}; - -enum Phases -{ - THE_HUNTRESS = 1, - THE_CAPTAIN = 2, - THE_PRIESTESS = 3, - SISTER_GHOST = 4 -}; - -enum Objects -{ - DATA_HUNTRESS_KASPARIAN, - DATA_CAPTAIN_YATHAE_MOONSTRIKE, - DATA_PRIESTESS_LUNASPYRE, -}; - -class boss_sisters_of_the_moon : public CreatureScript -{ - public: - boss_sisters_of_the_moon() : CreatureScript("boss_sisters_of_the_moon") { } - - struct boss_sisters_of_the_moon_AI : public BossAI - { - boss_sisters_of_the_moon_AI(Creature* creature) : BossAI(creature, DATA_SISTERS_OF_THE_MOON) - { - creature->GetInstanceScript(); - - Creature* kasparian = instance->GetCreature(NPC_HUNTRESS_KASPARIAN); - Creature* yathae = instance->GetCreature(NPC_CAPTAIN_YATHAE_MOONSTRIKE); - Creature* lunaspyre = instance->GetCreature(NPC_PRIESTESS_LUNASPYRE); - - if (instance) - { - EncounterState bossState = instance->GetBossState(DATA_SISTERS_OF_THE_MOON); - if (bossState != DONE && bossState != NOT_STARTED) - instance->SetBossState(DATA_SISTERS_OF_THE_MOON, NOT_STARTED); - } - } - - void Reset() override - { - me->RemoveAllAreaTriggers(); - - events.Reset(); - - if (instance) - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - - switch (me->GetEntry()) - { - case NPC_HUNTRESS_KASPARIAN: - DoAction(ACTION_RETURN_PHASE1); - break; - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - case NPC_PRIESTESS_LUNASPYRE: - DoAction(ACTION_RETURN); - break; - default: - break; - } - } - - void EnterCombat(Unit* /*attacker*/) override - { - if (instance) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); - instance->SetBossState(DATA_SISTERS_OF_THE_MOON, IN_PROGRESS); - } - - events.AddPhase(THE_HUNTRESS); - DoAction(ACTION_SAY_SISTERS_AGGRO); - DoZoneInCombat(); - - switch (me->GetEntry()) - { - case NPC_HUNTRESS_KASPARIAN: - events.ScheduleEvent(EVENT_MOON_GLAIVE, 17s, THE_HUNTRESS); - events.ScheduleEvent(EVENT_TWILIGHT_GLAIVE, 22s); - break; - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - events.ScheduleEvent(EVENT_TWILIGHT_VOLLEY, 15s); - events.ScheduleEvent(EVENT_CALL_MOONTALON, 40s, 0, THE_CAPTAIN); - events.ScheduleEvent(EVENT_INCORPOREAL_TELEPORT, 30s, 38s, 0, ((THE_HUNTRESS), (THE_PRIESTESS))); - events.ScheduleEvent(EVENT_RAPID_SHOT, 25s, 0, THE_CAPTAIN); - events.ScheduleEvent(EVENT_SHADOW_SHOT, 10s, 0, THE_CAPTAIN); - break; - case NPC_PRIESTESS_LUNASPYRE: - events.ScheduleEvent(EVENT_LUNAR_FIRE, 12s); - events.ScheduleEvent(EVENT_LUNAR_BEACON, 17s, 0 , THE_PRIESTESS); - events.ScheduleEvent(EVENT_MOON_BURN, 10s, 0 , THE_PRIESTESS); - events.ScheduleEvent(EVENT_INCORPOREAL_TELEPORT, 30s, 38s, ((THE_HUNTRESS), (THE_CAPTAIN))); - break; - } - } - - void EnterEvadeMode(EvadeReason /*why*/) override - { - _DespawnAtEvade(); - - me->RemoveAllAreaTriggers(); - - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MOON_BURN); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_RAPID_SHOT); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MOON_GLAIVE); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_LUNAR_BARRAGE_DAMAGE); - - switch (me->GetEntry()) - { - case NPC_HUNTRESS_KASPARIAN: - { - if (events.IsInPhase(THE_HUNTRESS)) - Talk(SAY_KASPARIAN_EVADE); - break; - } - - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - { - if (events.IsInPhase(THE_CAPTAIN)) - Talk(SAY_YATHAE_EVADE); - break; - } - - case NPC_PRIESTESS_LUNASPYRE: - { - if (events.IsInPhase(THE_PRIESTESS)) - Talk(SAY_LUNASPYRE_EVADE); - break; - } - - default: - break; - } - } - - void DoAction(const int32 action) override - { - switch (action) - { - case ACTION_EVADE: - { - switch (me->GetEntry()) - { - case NPC_HUNTRESS_KASPARIAN: - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->RemoveAurasDueToSpell(SPELL_GHOST_AURA); - break; - } - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - case NPC_PRIESTESS_LUNASPYRE: - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->AddAura(SPELL_GHOST_AURA, me); - break; - } - default: - break; - } - break; - } - - case ACTION_PHASE_2: - { - events.SetPhase(THE_CAPTAIN); - switch (me->GetEntry()) - { - case NPC_HUNTRESS_KASPARIAN: - { - me->AddAura(SPELL_GHOST_AURA, me); - // Disable Movement - break; - } - - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_TELEPORT_CENTER_COMBAT, 0s); - //Enable Movement - break; - } - - case NPC_PRIESTESS_LUNASPYRE: - break; - default: - break; - } - break; - } - - case ACTION_PHASE_3: - { - events.SetPhase(THE_PRIESTESS); - - switch (me->GetEntry()) - { - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->AddAura(SPELL_GHOST_AURA, me); - events.ScheduleEvent(EVENT_BACK_TO_GHOST, 0s); - // Disable Movement - break; - } - - case NPC_PRIESTESS_LUNASPYRE: - { - me->RemoveAurasDueToSpell(SPELL_GHOST_AURA); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_TELEPORT_CENTER_COMBAT, 0s); - // Enable Movement - break; - } - default: - break; - } - break; - } - - case ACTION_SISTERS_DEAD: - { - events.ScheduleEvent(EVENT_LUNASPYRE_JUST_DIED, 0s); - events.ScheduleEvent(EVENT_YATHAE_JUST_DIED, 8s); - events.ScheduleEvent(EVENT_KASPARIAN_JUST_DIED, 16s); - break; - } - - case ACTION_SAY_SISTERS_AGGRO: - { - events.ScheduleEvent(EVENT_KASPARIAN_AGGRO, 0s); - events.ScheduleEvent(EVENT_LUNASPYRE_AGGRO, 4s); - events.ScheduleEvent(EVENT_YATHAE_AGGRO, 9s); - break; - } - default: - break; - } - } - - void DamageTaken(Unit* attacker, uint32 &damage) override - { - if (me->GetEntry() == NPC_HUNTRESS_KASPARIAN) - { - if (me->HealthBelowPctDamaged(70, damage)) - { - DoAction(ACTION_PHASE_2); - } - } - - if (me->GetEntry() == NPC_CAPTAIN_YATHAE_MOONSTRIKE) - { - if (me->HealthBelowPctDamaged(40, damage)) - { - DoAction(ACTION_PHASE_3); - } - } - } - - void JustDied(Unit* /*killer*/) override - { - DoAction(ACTION_SISTERS_DEAD); - } - - void KilledUnit(Unit* killer) override - { - if (killer->GetTypeId() == TYPEID_PLAYER && !me->IsInEvadeMode()) - { - /*switch (killer) - { - case NPC_HUNTRESS_KASPARIAN: - { - if (events.IsInPhase(THE_HUNTRESS)) - killer->Talk(SAY_KASPARIAN_KILLS); - else if (events.IsInPhase(THE_CAPTAIN)) - yathae->AI()->Talk(SAY_SISTER_KILLS); - else if (events.IsInPhase(THE_PRIESTESS)) - lunaspyre->AI()->Talk(SAY_SISTER_KILLS); - break; - } - - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - { - if (events.IsInPhase(THE_HUNTRESS)) - kasparian->AI()->Talk(SAY_SISTER_KILLS); - else if (events.IsInPhase(THE_CAPTAIN)) - killer->Talk(SAY_YATHAE_KILLS); - else if (events.IsInPhase(THE_PRIESTESS)) - lunaspyre->AI()->Talk(SAY_SISTER_KILLS); - break; - } - - case NPC_PRIESTESS_LUNASPYRE: - { - if (events.IsInPhase(THE_HUNTRESS)) - kasparian->AI()->Talk(SAY_SISTER_KILLS); - else if (events.IsInPhase(THE_CAPTAIN)) - yathae->AI()->Talk(SAY_YATHAE_KILLS); - else if (events.IsInPhase(THE_PRIESTESS)) - killer->Talk(SAY_LUNASPYRE_KILLS); - break; - } - default: - break; - }*/ - } - } - - void UpdateAI(const uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - // Kasparian - case EVENT_KASPARIAN_AGGRO: - Talk(SAY_KASPARIAN_AGGRO); - break; - - case EVENT_MOON_GLAIVE: - { - DoCastVictim(SPELL_MOON_GLAIVE); - events.Repeat(17s); - } - break; - - case EVENT_TWILIGHT_GLAIVE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_TWILIGHT_GLAIVE_AT); - events.Repeat(22s); - break; - - case EVENT_GLAIVE_STORM: - // TODO - break; - - case EVENT_KASPARIAN_JUST_DIED: - Talk(SAY_KASPARIAN_DIED); - break; - - // Yathae - case EVENT_YATHAE_AGGRO: - Talk(SAY_YATHAE_AGGRO); - break; - - case EVENT_CALL_MOONTALON: - DoCast(me, SPELL_CALL_MOONTALON); - events.Repeat(40s); - break; - - case EVENT_TWILIGHT_VOLLEY: - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_TWILIGHT_VOLLEY_AT); - - events.Repeat(15s); - } - break; - - case EVENT_RAPID_SHOT: - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - DoCast(target, SPELL_RAPID_SHOT); - DoCast(me, SPELL_COOLDOWN_CREATURE_SPECIAL, true); - } - events.Repeat(17s); - } - break; - - case EVENT_SHADOW_SHOT: - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - DoCast(target, SPELL_SHADOW_SHOT); - } - events.Repeat(20s); - } - break; - - case EVENT_YATHAE_JUST_DIED: - Talk(SAY_YATHAE_DIED); - break; - - // Lunaspyre - case EVENT_LUNASPYRE_AGGRO: - Talk(SAY_LUNASPYRE_AGGRO); - break; - - case EVENT_LUNAR_BEACON: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_LUNAR_BEACON); - - events.Repeat(12s); - break; - - case EVENT_LUNAR_FIRE: - DoCastVictim(SPELL_LUNAR_FIRE); - events.Repeat(25s); - break; - - case EVENT_MOON_BURN: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_MOON_BURN); - - events.Repeat(14s); - break; - - case EVENT_LUNAR_STRIKE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_LUNAR_STRIKE); - - events.Repeat(16s); - break; - - case EVENT_EMBRACE_OF_THE_ECLIPSE: - // TODO - break; - - case EVENT_LUNASPYRE_JUST_DIED: - Talk(SAY_LUNASPYRE_DIED); - break; - default: - break; - } - } - - switch (me->GetEntry()) - { - case NPC_HUNTRESS_KASPARIAN: - { - if (events.IsInPhase(THE_HUNTRESS)) - DoMeleeAttackIfReady(); - else - DoSpellAttackIfReady(SPELL_MOON_GLAIVE); - break; - } - - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - { - if (events.IsInPhase(THE_CAPTAIN)) - DoMeleeAttackIfReady(); - else - DoSpellAttackIfReady(SPELL_SHADOW_SHOT); - break; - } - - case NPC_PRIESTESS_LUNASPYRE: - { - if (events.IsInPhase(THE_PRIESTESS)) - DoMeleeAttackIfReady(); - else - DoSpellAttackIfReady(SPELL_LUNAR_STRIKE); - break; - } - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_sisters_of_the_moon_AI(creature); - } -}; - -// Kasparian Spells & ATs -struct at_twilight_glaive : AreaTriggerAI -{ - at_twilight_glaive(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } - - void OnInitialize() override - { - if (Unit* caster = at->GetCaster()) - if (Creature* creCaster = caster->ToCreature()) - if (creCaster->IsAIEnabled) - if (Unit* target = ObjectAccessor::GetUnit(*creCaster, creCaster->AI()->GetGUID())) - { - Position targetPos = target->GetPosition(); - float targetDist = caster->GetDistance(target); - float targetAngle = caster->GetAngle(target); - at->MovePosition(targetPos, targetDist, targetAngle); - } - } - - void OnUnitEnter(Unit* /*unit*/) override - { - if (Unit* caster = at->GetCaster()) - if (Unit* target = at->GetTarget()) - if (caster->IsValidAttackTarget(target)) - caster->CastSpell(target, SPELL_TWILIGHT_GLAIVE_DAMAGE, true); - } - - void OnDestinationReached() override - { - if (Unit* caster = at->GetCaster()) - { - Position casterPos = caster->GetPosition(); - float casterDist = at->GetDistance(caster); - float casterAngle = at->GetAngle(caster); - at->MovePosition(casterPos, casterDist, casterAngle); - } - - at->Remove(); - } -}; - -// Yathae Spells & AT -struct at_twilight_volley : AreaTriggerAI -{ - at_twilight_volley(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } - - void OnInitialize() override - { - /*if (Unit* caster = at->GetCaster()) - if (Creature* creCaster = caster->ToCreature()) - if (creCaster->IsAIEnabled) - if (Unit* target = ObjectAccessor::GetUnit(*creCaster, creCaster->AI()->GetGUID())) - at->SetPosition(*target);*/ - } - - void OnUnitEnter(Unit* /*unit*/) override - { - if (Unit* caster = at->GetCaster()) - if (Unit* target = at->GetTarget()) - if (caster->IsValidAttackTarget(target)) - caster->CastSpell(target, SPELL_TWILIGHT_VOLLEY_DAMAGE, true); - } - - void OnUnitExit(Unit* /*unit*/) override - { - if (Unit* caster = at->GetCaster()) - if (Unit* target = at->GetTarget()) - if (caster->IsValidAttackTarget(target)) - target->RemoveAurasDueToSpell(SPELL_TWILIGHT_VOLLEY_DAMAGE); - } -}; - -// Lunasypre Spells & AT -/*class spell_embrace_of_the_eclipse : public SpellScriptLoader -{ - public: - spell_embrace_of_the_eclipse() : SpellScriptLoader("spell_embrace_of_the_eclipse") { } - - class spell_embrace_of_the_eclipse_SpellScript : public SpellScript - { - PrepareSpellScript(spell_embrace_of_the_eclipse_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*//*) override - { - return ValidateSpellInfo({ SPELL_EMBRACE_OF_THE_ECLIPSE_HEAL, SPELL_EMBRACE_OF_THE_ECLIPSE_BOSS }); - } - - void HandleAfterCast() - { - Unit* caster = GetCaster(); - Creature* kasparian = instance->GetCreature(NPC_HUNTRESS_KASPARIAN); - Creature* yathae = instance->GetCreature(NPC_CAPTAIN_YATHAE_MOONSTRIKE); - - if (!caster) - return; - - std::list playerList; - GetPlayerListInGrid(playerList, me, 200.0f); - - for (auto itr : playerList) - itr->RemoveAura(SPELL_EMBRACE_OF_THE_ECLIPSE_HEAL); - } - - void Register() override - { - AfterCast += SpellCastFn(spell_embrace_of_the_eclipse_SpellScript::HandleAfterCast); - } - }; - - class spell_embrace_of_the_eclipse_AuraScript : public AuraScript - { - PrepareAuraScript(spell_embrace_of_the_eclipse_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*//*) override - { - return ValidateSpellInfo({ SPELL_EMBRACE_OF_THE_ECLIPSE_BOSS, SPELL_EMBRACE_OF_THE_ECLIPSE_HEAL }); - } - - void HandleRemove(AuraEffect const* aurEff, uint32 &shieldType, AuraEffectHandleModes /*mode*//*) - { - AuraRemoveMode removeMode = GetTargetApplication()->GetRemoveMode(); - - if (removeMode == AURA_REMOVE_BY_EXPIRE) - { - switch (shieldType) - { - case SPELL_EMBRACE_OF_THE_ECLIPSE_BOSS: - break; - case SPELL_EMBRACE_OF_THE_ECLIPSE_HEAL: - break; - default: - break; - } - } - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_embrace_of_the_eclipse_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_ABSORB, AURA_EFFECT_HANDLE_REAL); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_embrace_of_the_eclipse_SpellScript(); - } - - AuraScript* GetAuraScript() const override - { - return new spell_embrace_of_the_eclipse_AuraScript(); - } -};*/ - -class spell_lunar_beacon : public SpellScriptLoader -{ -public: - spell_lunar_beacon() : SpellScriptLoader("spell_lunar_beacon") { } - - class spell_lunar_beacon_AuraScript : public AuraScript - { - PrepareAuraScript(spell_lunar_beacon_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_LUNAR_BARRAGE_AT }); - } - - void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (GetCaster()) - { - AuraRemoveMode removeMode = GetTargetApplication()->GetRemoveMode(); - - if (removeMode == AURA_REMOVE_BY_EXPIRE) - GetCaster()->CastSpell(GetTarget(), SPELL_LUNAR_BARRAGE_AT, true); - } - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_lunar_beacon_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const - { - return new spell_lunar_beacon_AuraScript(); - } -}; - -struct at_lunar_barrage : AreaTriggerAI -{ - at_lunar_barrage(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } - - void OnInitialize() override - { - if (Unit* caster = at->GetCaster()) - if (Creature* creCaster = caster->ToCreature()) - if (creCaster->IsAIEnabled) - if (Unit* target = ObjectAccessor::GetUnit(*creCaster, creCaster->AI()->GetGUID())) - at->SetDestination(*target, 1000); - } - - void OnUnitEnter(Unit* /*unit*/) override - { - if (Unit* caster = at->GetCaster()) - if (Unit* target = at->GetTarget()) - if (caster->IsValidAttackTarget(target)) - caster->CastSpell(target, SPELL_LUNAR_BARRAGE_DAMAGE, true); - } - - void OnUnitExit(Unit* /*unit*/) override - { - if (Unit* caster = at->GetCaster()) - if (Unit* target = at->GetTarget()) - if (caster->IsValidAttackTarget(target)) - target->RemoveAurasDueToSpell(SPELL_LUNAR_BARRAGE_DAMAGE); - } -}; - -void AddSC_boss_sisters_of_the_moon_debug() -{ - // Boss - new boss_sisters_of_the_moon(); - // Kasparian ATs & Spells - RegisterAreaTriggerAI(at_twilight_glaive); - // Yathae ATs & Spells - RegisterAreaTriggerAI(at_twilight_volley); - // Lunaspyre ATs & Spells - new spell_lunar_beacon(); - //new spell_embrace_of_the_eclipse(); - RegisterAreaTriggerAI(at_lunar_barrage); -} diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/instance_tomb_of_sargeras.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/instance_tomb_of_sargeras.cpp index c54d841..311b8f2 100644 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/instance_tomb_of_sargeras.cpp +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/instance_tomb_of_sargeras.cpp @@ -1,164 +1,605 @@ /* - * Copyright (C) 2017-2018 AshamaneProject - * - * 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 . - */ + https://uwow.biz/ +*/ -#include "Creature.h" #include "tomb_of_sargeras.h" -#include "GameObject.h" -#include "InstanceScript.h" -#include "Map.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" DoorData const doorData[] = { - { GO_DOOR_ROOM_GOROTH_S, DATA_GOROTH, DOOR_TYPE_ROOM }, - { GO_DOOR_ROOM_GOROTH_N, DATA_GOROTH, DOOR_TYPE_PASSAGE }, - { GO_DOOR_ROOM_GOROTH_E, DATA_GOROTH, DOOR_TYPE_PASSAGE }, + //{269164, DATA_GOROTH, DOOR_TYPE_PASSAGE}, + {268514, DATA_GOROTH, DOOR_TYPE_PASSAGE}, + {268580, DATA_GOROTH, DOOR_TYPE_PASSAGE}, + {GO_GOROTH_GATES, DATA_GOROTH, DOOR_TYPE_PASSAGE}, + {269975, DATA_GOROTH, DOOR_TYPE_ROOM}, + {269974, DATA_GOROTH, DOOR_TYPE_ROOM}, + {269973, DATA_GOROTH, DOOR_TYPE_ROOM}, + { GO_INTRODESTROY_2, DATA_GOROTH, DOOR_TYPE_PASSAGE}, + {269120, DATA_HARJATAN, DOOR_TYPE_PASSAGE}, + {269192, DATA_HARJATAN, DOOR_TYPE_ROOM}, + {GO_SASSZINE_DOOR, DATA_MISTRESS_SASSZINE, DOOR_TYPE_ROOM}, + {GO_INQUISITION_DOOR, DATA_DEMONIC_INQUISITION, DOOR_TYPE_ROOM}, + {GO_HAMMER_THRONE, DATA_DEMONIC_INQUISITION, DOOR_TYPE_PASSAGE}, + {GO_ELUNES_THRONE, DATA_THE_DESOLATE_HOST, DOOR_TYPE_PASSAGE}, + {GO_GOLGANETH_THRONE, DATA_MISTRESS_SASSZINE, DOOR_TYPE_PASSAGE}, + {GO_MAIDEN_DOOR, DATA_MAIDEN_OF_VIGILANCE, DOOR_TYPE_ROOM}, + {GO_SISTERS_MOON_DOOR, DATA_SISTERS_OF_THEMOON, DOOR_TYPE_ROOM}, + {GO_AVATARA_DOOR, DATA_FALLEN_AVATAR, DOOR_TYPE_ROOM}, + {GO_DESOLATE_HOST_DOOR_1, DATA_THE_DESOLATE_HOST, DOOR_TYPE_ROOM}, + {GO_DESOLATE_HOST_DOOR_2, DATA_THE_DESOLATE_HOST, DOOR_TYPE_ROOM}, + {0, 0, DOOR_TYPE_ROOM} // END +}; + +ObjectData const creatureData[] = +{ + {NPC_BREACH_IN_WINDOW, NPC_BREACH_IN_WINDOW}, + + {NPC_KHADGAR, NPC_KHADGAR}, + {NPC_VELEN, NPC_VELEN}, + {NPC_ILLIDAN, NPC_ILLIDAN}, + {NPC_MAEIV, NPC_MAEIV}, + {NPC_DURGAN, NPC_DURGAN}, + {NPC_ATRIGAN_MISC, NPC_ATRIGAN_MISC}, + + {NPC_EVENT_1, NPC_EVENT_1}, + {NPC_EVENT_2, NPC_EVENT_2}, + {NPC_SASSZINE_MISC, NPC_SASSZINE_MISC}, + + {NPC_GOROTH, DATA_GOROTH}, + {NPC_SASSZINE, DATA_MISTRESS_SASSZINE}, + {NPC_MOON_STALKER, DATA_SISTERS_OF_THEMOON}, + {NPC_HUNTRESS_KASPARIAN, NPC_HUNTRESS_KASPARIAN}, + {NPC_PRIESTESS_LUNASPYRE, NPC_PRIESTESS_LUNASPYRE}, + {NPC_CAPTAIN_YATHAE, NPC_CAPTAIN_YATHAE}, + {NPC_KILJAEDEN, DATA_KILJAEDEN}, + + {NPC_HAMMER_1, NPC_HAMMER_1}, + {NPC_HAMMER_2, NPC_HAMMER_2}, + {NPC_ELUNES_1, NPC_ELUNES_1}, + {NPC_ELUNES_2, NPC_ELUNES_2}, + {NPC_GOLGANETH_1, NPC_GOLGANETH_1}, + {NPC_GOLGANETH_2, NPC_GOLGANETH_2}, + {NPC_SASSZINE_NPC_OUTRO, NPC_SASSZINE_NPC_OUTRO}, + {NPC_KILDJEDAN_AVATARA, NPC_KILDJEDAN_AVATARA}, + {NPC_ATRIGAN, NPC_ATRIGAN}, + {NPC_BELAK, NPC_BELAK}, + {NPC_HARJATAN, NPC_HARJATAN}, + {NPC_ELDER, NPC_ELDER}, + {NPC_AVATARA_MAIDEN , NPC_AVATARA_MAIDEN}, + {NPC_SISTERS_ACH_ADD, NPC_SISTERS_ACH_ADD}, + {0, 0} // END +}; + +ObjectData const gobjectData[] = +{ + {GO_GOROTH_GATES, GO_GOROTH_GATES}, + {GO_INTRODESTROY_1, GO_INTRODESTROY_1}, + {GO_INTRODESTROY_2, GO_INTRODESTROY_2}, + {GO_INTRODESTROY_3, GO_INTRODESTROY_3}, + {GO_ELEVATOR, GO_ELEVATOR}, + {GO_KILJAEDEN_BRIDGE_1, GO_KILJAEDEN_BRIDGE_1}, + {GO_KILJAEDEN_BRIDGE_2, GO_KILJAEDEN_BRIDGE_2}, + {GO_KILJAEDEN_CHEST, GO_KILJAEDEN_CHEST}, + + {269164, 269164}, + {268580, 268580}, + {GO_AVATARA_FLOOR , GO_AVATARA_FLOOR}, + + // Avataras floor blocks + {267700, 267700}, + {267701, 267701}, + {267702, 267702}, + {267703, 267703}, + {267704, 267704}, + {267705, 267705}, + {267706, 267706}, + {267707, 267707}, + {267708, 267708}, + {267709, 267709}, + {267710, 267710}, + {267711, 267711}, + {267712, 267712}, + {267713, 267713}, + {267714, 267714}, + {267715, 267715}, + {267716, 267716}, + {267717, 267717}, + {267718, 267718}, + {267719, 267719}, + {267720, 267720}, + {267721, 267721}, + {267722, 267722}, + {267723, 267723}, + {267724, 267724}, + + // end Avatara bloks + {0, 0 } // END }; class instance_tomb_of_sargeras : public InstanceMapScript { - public: - instance_tomb_of_sargeras() : InstanceMapScript("instance_tomb_of_sargeras", 1676) { } +public: + instance_tomb_of_sargeras() : InstanceMapScript(ToSScriptName, 1676) {} - struct instance_tomb_of_sargeras_InstanceMapScript : public InstanceScript + InstanceScript* GetInstanceScript(InstanceMap* map) const override + { + return new instance_tomb_of_sargeras_InstanceMapScript(map); + } + + struct instance_tomb_of_sargeras_InstanceMapScript : InstanceScript + { + instance_tomb_of_sargeras_InstanceMapScript(Map* map) : InstanceScript(map->ToInstanceMap()) { - instance_tomb_of_sargeras_InstanceMapScript(InstanceMap* map) : InstanceScript(map) { } + SetBossNumber(MAX_ENCOUNTER); + } - void Initialize() override - { - SetBossNumber(ENCOUNTER_COUNT); - LoadDoorData(doorData); - } + + std::vector addsIntro{}; + std::set lunarArchersEvent{}; + WorldLocation loc_res_pla{}; - void OnCreatureCreate(Creature* creature) override + std::vector avatarasFloors{}; + + uint32 checkTimer = 1000; + uint32 checkPlayersFallTimer = 1000; + + void Initialize() override + { + LoadDoorData(doorData); + LoadObjectData(creatureData, gobjectData); + } + + void OnPlayerEnter(Player* player) override {} + + void OnCreatureCreate(Creature* creature) override + { + InstanceScript::OnCreatureCreate(creature); + + switch (creature->GetEntry()) { - switch (creature->GetEntry()) - { - case BOSS_GOROTH: + case 121024: + case 121023: + if (creature->GetPositionX() >= 6019.0f && creature->GetPositionZ() <= 2972.0f) + { + if (!creature->HasAura(35356) && !false /* flag3 feign death non porte */) + { + creature->AddDelayedEvent(1000, [creature] () -> void {creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK1H);}); + addsIntro.push_back(creature->GetGUID()); + } + } + break; + case NPC_GOROTH: creature->SetVisible(false); - creature->setActive(false); - GorothGUID = creature->GetGUID(); - + creature->SetReactState(REACT_PASSIVE); break; - - case BOSS_ATRIGAN: - AtriganGUID = creature->GetGUID(); + case NPC_MAEIV: + case NPC_ILLIDAN: + creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK1H); break; - - case BOSS_BELAC: - BelacGUID = creature->GetGUID(); + case NPC_VELEN: + creature->CastSpell(creature, 241186); // he don't want use it like aura =C break; - - case BOSS_HARJATAN: - case NPC_HUNTRESS_KASPARIAN: - case NPC_CAPTAIN_YATHAE_MOONSTRIKE: - case NPC_PRIESTESS_LUNASPYRE: - case NPC_SASSZINE: - case NPC_ENGINE_OF_SOULS: - case NPC_SOUL_QUEEN_DEJAHNA: - case NPC_DESOLATE_HOST: - case NPC_MAIDEN_OF_VIGILANCE: - case NPC_FALLEN_AVATAR: - case NPC_KILJAEDEN: - AddObject(creature, true); - break; - default: - break; - } + case NPC_ATRIGAN_MISC: + creature->SetReactState(REACT_PASSIVE); + if (GetBossState(DATA_GOROTH) != NOT_STARTED) + creature->SetVisible(false); + break; + case NPC_HAMMER_1: + case NPC_HAMMER_2: + if (GetBossState(DATA_DEMONIC_INQUISITION) != DONE) + creature->SetVisible(false); + break; + case NPC_ELUNES_1: + case NPC_ELUNES_2: + if (GetBossState(DATA_THE_DESOLATE_HOST) != DONE) + creature->SetVisible(false); + break; + case NPC_GOLGANETH_1: + case NPC_GOLGANETH_2: + if (GetBossState(DATA_MISTRESS_SASSZINE) != DONE) + creature->SetVisible(false); + break; + case NPC_LUNAR_ARCHER: + if (creature->GetDistance(6088.19f, -1030.07f, 2896.31f) <= 10.0f) + lunarArchersEvent.insert(creature->GetGUID()); + break; + case NPC_KILDJEDAN_AVATARA: + if (GetBossState(DATA_FALLEN_AVATAR) == DONE) + creature->SetVisible(false); + break; + case NPC_TADPOLE_1: + case NPC_TADPOLE_2: + case NPC_TADPOLE_3: + if (Creature* harjatan = instance->GetCreature(GetGuidData(NPC_HARJATAN))) + harjatan->AI()->JustSummoned(creature); + break; + case NPC_FRAGMENT_SOUL: + if (!instance->IsMythic()) + creature->DespawnOrUnsummon(100); + else if (Creature* atrigan = instance->GetCreature(GetGuidData(NPC_ATRIGAN))) + { + atrigan->AI()->JustSummoned(creature); + creature->CastSpell(creature, 241521); + } + break; + default: + break; } + } - void OnGameObjectCreate(GameObject* go) override + void OnGameObjectCreate(GameObject* go) override + { + InstanceScript::OnGameObjectCreate(go); + + switch (go->GetEntry()) { - switch (go->GetEntry()) - { - case GO_DESTRUCTIBLE_1: - DestructibleGUID[0] = go->GetGUID(); - - if (GetBossState(DATA_GOROTH) == DONE) - go->UseDoorOrButton(1000000, true); + case GO_ELEVATOR: + if (GetBossState(DATA_MAIDEN_OF_VIGILANCE) != DONE) + // (SetPhaseMask 2 : phasing legacy uwow, non porte) break; - - case GO_DESTRUCTIBLE_2: - DestructibleGUID[1] = go->GetGUID(); - - if (GetBossState(DATA_GOROTH) == DONE) - go->UseDoorOrButton(1000000, true); - + case 267700: + case 267701: + case 267702: + case 267703: + case 267704: + case 267705: + case 267706: + case 267707: + case 267708: + case 267709: + case 267710: + case 267711: + case 267712: + case 267713: + case 267714: + case 267715: + case 267716: + case 267717: + case 267718: + case 267719: + case 267720: + case 267721: + case 267722: + case 267723: + case 267724: + avatarasFloors.push_back(go->GetGUID()); break; - - case GO_DESTRUCTIBLE_3: - DestructibleGUID[2] = go->GetGUID(); - - if (GetBossState(DATA_GOROTH) == DONE) - go->UseDoorOrButton(1000000, true); - - break; - - case GO_DOOR_TO_MISTRESS: - case GO_DOOR_TO_MISTRESS2: - if (GetBossState(DATA_HARJATAN) == DONE) - go->UseDoorOrButton(1000000, true); - - break; - - case GO_DOOR_ROOM_GOROTH_S: - case GO_DOOR_ROOM_GOROTH_N: - case GO_DOOR_ROOM_GOROTH_E: - AddDoor(go, true); - break; - default: - break; - } - } - - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case DATA_GOROTH: - return GorothGUID; - - case DATA_ATRIGAN: - return AtriganGUID; - - case DATA_BELAC: - return BelacGUID; - default: break; - } - - return ObjectGuid::Empty; - } - - protected: - EventMap events; - ObjectGuid GorothGUID; - ObjectGuid AtriganGUID; - ObjectGuid BelacGUID; - ObjectGuid DestructibleGUID[3]; - }; - - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_tomb_of_sargeras_InstanceMapScript(map); + } } + + void OnGameObjectRemove(GameObject* go) override + { + InstanceScript::OnGameObjectRemove(go); + } + + void OnUnitDeath(Unit* creature) override + { + switch (creature->GetEntry()) + { + case NPC_LUNAR_ARCHER: + if (lunarArchersEvent.find(creature->GetGUID()) != lunarArchersEvent.end()) + { + lunarArchersEvent.erase(creature->GetGUID()); + if (lunarArchersEvent.empty()) + instance->SummonCreature(NPC_GUARDIAN_SENTRY, { 6088.50f, -1027.06f, 2896.43f, 4.23f }); + } + break; + case NPC_EVENT_1: + case NPC_EVENT_2: + if (Creature* event = instance->GetCreature(GetGuidData(NPC_SASSZINE_MISC))) + if (event->IsAlive() && event->IsInWorld()) + event->AI()->DoAction(1); + break; + default: + break; + } + } + + bool SetBossState(uint32 type, EncounterState state) override + { + if (!InstanceScript::SetBossState(type, state)) + return false; + + if (type != DATA_MAIDEN_OF_VIGILANCE && type != DATA_FALLEN_AVATAR && type != DATA_KILJAEDEN) + if (GameObject* go = instance->GetGameObject(GetGuidData(269164))) + HandleGameObject(go->GetGUID(), CheckRequiredBosses(DATA_MAIDEN_OF_VIGILANCE), go); + + switch (state) + { + case DONE: + { + switch (type) + { + case DATA_GOROTH: + if (auto khadgar = instance->GetCreature(GetGuidData(NPC_KHADGAR))) + khadgar->AI()->DoAction(true); + break; + case DATA_DEMONIC_INQUISITION: + for (auto id : {NPC_HAMMER_1, NPC_HAMMER_2}) + if (auto hammer = instance->GetCreature(GetGuidData(id))) + hammer->SetVisible(true); + break; + case DATA_HARJATAN: + if (auto sivash = instance->GetCreature(GetGuidData(NPC_SASSZINE_NPC_OUTRO))) + sivash->AddDelayedEvent(2000, [sivash] {sivash->AI()->ZoneTalk(0); }); + break; + case DATA_THE_DESOLATE_HOST: + for (auto id : { NPC_ELUNES_1, NPC_ELUNES_2 }) + if (auto elunes = instance->GetCreature(GetGuidData(id))) + elunes->SetVisible(true); + break; + case DATA_MISTRESS_SASSZINE: + for (auto id : { NPC_GOLGANETH_1, NPC_GOLGANETH_2 }) + if (auto golganeth = instance->GetCreature(GetGuidData(id))) + golganeth->SetVisible(true); + break; + case DATA_MAIDEN_OF_VIGILANCE: + if (auto go = instance->GetGameObject(GetGuidData(GO_ELEVATOR))) + // (SetPhaseMask 1 : phasing legacy uwow, non porte) + break; + default: + break; + } + break; + } + case NOT_STARTED: + { + switch (type) + { + case DATA_FALLEN_AVATAR: + if (GameObject* go = instance->GetGameObject(GetGuidData(GO_AVATARA_FLOOR))) + go->SetDestructibleState(GO_DESTRUCTIBLE_INTACT); + + for (auto i : avatarasFloors) + if (GameObject* go = instance->GetGameObject(i)) + go->SetDestructibleState(GO_DESTRUCTIBLE_INTACT); + break; + } + break; + } + default: + break; + case IN_PROGRESS: + // TODO: handle IN_PROGRESS if needed + break; + case FAIL: + // TODO: handle FAIL if needed + break; + case SPECIAL: + // TODO: handle SPECIAL if needed + break; + case TO_BE_DECIDED: + // TODO: handle TO_BE_DECIDED if needed + break; + } + + if (type == DATA_KILJAEDEN) + { + if (state == IN_PROGRESS) + { + HandleGameObject(GetGuidData(GO_KILJAEDEN_BRIDGE_1), true); + HandleGameObject(GetGuidData(GO_KILJAEDEN_BRIDGE_2), true); + } + else + { + HandleGameObject(GetGuidData(GO_KILJAEDEN_BRIDGE_1), false); + HandleGameObject(GetGuidData(GO_KILJAEDEN_BRIDGE_2), false); + } + } + return true; + } + + void SetData(uint32 type, uint32 data) override + { + switch (type) + { + case 1: // intro + for (auto guid : addsIntro) + if (auto creature = instance->GetCreature(guid)) + { + creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); + creature->CombatStop(); + creature->CastSpell(creature, 35356); + } + break; + default: + break; + } + } + + uint32 GetData(uint32 type) const override + { + //switch (type) + //{ + //default: + return 0; + //} + } + + ObjectGuid GetGuidData(uint32 type) const override + { + //switch (type) + //{ + //default: + return InstanceScript::GetGuidData(type); + //} + } + + bool CheckRequiredBosses(uint32 bossId, Player const* /*player*/ = nullptr) const override + { + if (false) + return true; + + switch (bossId) + { + case DATA_MAIDEN_OF_VIGILANCE: + for (uint32 i = DATA_GOROTH; i < DATA_MAIDEN_OF_VIGILANCE; ++i) + if (GetBossState(i) != DONE) + return false; + return true; + default: + return true; + } + } + + WorldLocation* GetClosestGraveYard(float x, float y, float z) /* signature uwow, la table graveyard_zone fait foi */ + { + Position playerPosition(x, y, z); + uint32 graveyardId = 5889; + + std::vector availableGravs{ 6014, 6017, 6019, 5889 }; + + if (GetBossState(DATA_GOROTH) == DONE) + availableGravs.push_back(6015); + + if (CheckRequiredBosses(DATA_MAIDEN_OF_VIGILANCE)) + availableGravs.push_back(6016); + if (GetBossState(DATA_HARJATAN) == DONE) + availableGravs.push_back(6018); + if (GetBossState(DATA_MAIDEN_OF_VIGILANCE) == DONE) + availableGravs.push_back(6020); + if (GetBossState(DATA_FALLEN_AVATAR) == DONE) + availableGravs.push_back(6210); + + if (GetBossState(DATA_KILJAEDEN) == DONE) + availableGravs.push_back(6117); + + for (auto gravYard : availableGravs) + { + WorldSafeLocsEntry const* temp = sWorldSafeLocsStore.LookupEntry(gravYard); + WorldSafeLocsEntry const* saved = sWorldSafeLocsStore.LookupEntry(graveyardId); + if (!temp) + continue; + if (!saved) + graveyardId = gravYard; + + Position pos1(temp->Loc.X, temp->Loc.Y, temp->Loc.Z); + Position pos2(saved->Loc.X, saved->Loc.Y, saved->Loc.Z); + if (playerPosition.GetExactDist(&pos1) < playerPosition.GetExactDist(&pos2)) + graveyardId = gravYard; + } + + if (GetBossState(DATA_GOROTH) != DONE) + { + WorldSafeLocsEntry const* temp = sWorldSafeLocsStore.LookupEntry(6015); + WorldSafeLocsEntry const* saved = sWorldSafeLocsStore.LookupEntry(graveyardId); + if (temp) + { + Position pos1(temp->Loc.X, temp->Loc.Y, temp->Loc.Z); + Position pos2(saved->Loc.X, saved->Loc.Y, saved->Loc.Z); + if (playerPosition.GetExactDist(&pos1) < playerPosition.GetExactDist(&pos2)) + graveyardId = 6015; + } + } + + if (WorldSafeLocsEntry const* gy = sWorldSafeLocsStore.LookupEntry(graveyardId)) + { + loc_res_pla.Relocate(gy->Loc.X, gy->Loc.Y, gy->Loc.Z); + loc_res_pla.m_mapId = gy->MapID; + } + + return &loc_res_pla; + } + + std::string GetSaveData() override + { + std::ostringstream saveStream; + saveStream << "T O S " << GetSaveData(); + return saveStream.str(); + } + + void Load(const char* data) override + { + if (!data) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(data); + + char dataHead1, dataHead2, dataHead3; + + std::istringstream loadStream(data); + loadStream >> dataHead1 >> dataHead2 >> dataHead3; + + if (dataHead1 == 'T' && dataHead2 == 'O' && dataHead3 == 'S') + { + for (uint32 i = 0; i < MAX_ENCOUNTER; ++i) + { + uint32 tmpState; + loadStream >> tmpState; + if (tmpState == IN_PROGRESS || tmpState > SPECIAL) + tmpState = NOT_STARTED; + SetBossState(i, EncounterState(tmpState)); + } + } + else + OUT_LOAD_INST_DATA_FAIL; + + OUT_LOAD_INST_DATA_COMPLETE; + + } + + void Update(uint32 diff) override + { + if (checkTimer <= diff) + { + checkTimer = 1000; + + instance->ApplyOnEveryPlayer([&](Player* player) + { + if (GetBossState(DATA_FALLEN_AVATAR) == IN_PROGRESS && + (player->GetPositionZ() <= 1509.50f || (player->GetPositionX() >= 6667.59f && player->GetPositionZ() <= 1540.68f))) + { + if (!player->HasAura(240249)) + player->CastSpell(player, 240249, true); + } + else if (player->HasAura(240249)) + player->RemoveAurasDueToSpell(240249); + + if (player->IsAlive()) + if (GetBossState(DATA_MAIDEN_OF_VIGILANCE) == IN_PROGRESS || GetBossState(DATA_MAIDEN_OF_VIGILANCE) == NOT_STARTED) + if (player->GetDistance2d(6348.49f, -796.72f) <= 21.0f) + if (player->GetPositionZ() <= 2855.0f) + player->Kill(player); + }); + } + else + checkTimer -= diff; + + if (checkPlayersFallTimer <= diff) + { + checkPlayersFallTimer = 500; + + instance->ApplyOnEveryPlayer([&](Player* player) + { + if (player->IsFalling()) + { + if (player->IsWithinBox({ 4500.0f, -1510.0f, 5330.0f }, 200.0f, 200.0f, 20.0f)) + { + if (player->IsAlive()) + player->Kill(player); + + player->NearTeleportTo(4500.18f, -1484.60f, 5385.64f, player->GetOrientation()); + } + + if (player->GetDistance(6437.81f, -1089.29f, 2881.52f) < 100.0f && player->GetPositionZ() < 2870.0f) + { + if (player->IsAlive()) + player->Kill(player); + + player->NearTeleportTo(6402.37f, -1064.89f, 2881.05f, player->GetOrientation()); + } + } + }); + } + else + checkPlayersFallTimer -= diff; + } + }; }; void AddSC_instance_tomb_of_sargeras() diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.cpp b/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.cpp index 7d94ef3..4c811bb 100644 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.cpp +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.cpp @@ -1,312 +1,542 @@ /* -* Copyright (C) 2016-2019 MagicStorm -* -* Este codigo es de uso exclusivo y privado para la red de gaming -* ColossusWoW est? totalmente prohibida su distribuci?n y -* uso en proyectos ajenos, si has obtenido o encontrado este codigo -* publicamente envianos un email a administracion@colossuswow.com -* indicandonos como y donde has obtenido este codigo -* -* Recuerda, no distribuyas, no compartas y no subas este codigo a ningun -* lugar publico, usa siempre nuestros repositorios Git! + https://uwow.biz/ */ -#include "GameObject.h" #include "tomb_of_sargeras.h" +#include "WaypointManager.h" +#include "GameObjectAI.h" -const Position infernalChaosbringer[4] = +Position const InfernalsPos[3] = { - { 6130.221f, -801.1302f, 2971.884f, 2.807055f }, - { 6086.503f, -816.4011f, 2971.987f, 2.965497f }, - { 6064.199f, -782.8004f, 2971.644f, 2.827615f }, - { 6131.155f, -773.0943f, 2971.543f, 0.0f }, + { 6130.221f, -801.1302f, 2971.544f, 2.807055f }, + { 6086.503f, -816.4011f, 2971.721f, 2.965497f }, + { 6064.199f, -782.8004f, 2971.544f, 2.827615f }, }; -// 119726 - Archimage Khadgar -class npc_archimage_khadgar_tos : public CreatureScript +// 121605 +struct npc_tos_breach : ScriptedAI { -public: - npc_archimage_khadgar_tos() : CreatureScript("npc_archimage_khadgar_tos") { } + explicit npc_tos_breach(Creature* creature) : ScriptedAI(creature) { } - enum velenEvents + void DoAction(int32 const actionID) override { - EVENT_FALL_BACK = 1, - EVENT_IMPACT, - EVENT_CHECK_FOR_EVENT_START, - EVENT_ILLIDAN_TALK, - EVENT_TALK_ATRIGAN1, - EVENT_CAST_SCENE, - EVENT_SUMMONS, - }; - - enum velenSay - { - SAY_INTRO = 0, - }; - - struct npc_archimage_khadgar_tosAI : public ScriptedAI - { - npc_archimage_khadgar_tosAI(Creature* creature) : ScriptedAI(creature) + me->SetVisible(true); + + switch (actionID) { - //me->SummonGameObject(GO_DESTRUCTIBLE_2, 6146.56f, -762.025f, 2970.9f, 3.14159f, QuaternionData(-0.0f, -0.0f, -0.284929f, -0.958549f), 100000); - } - - uint32 startTimer; - bool eventStarted; - - void Reset() override - { - events.Reset(); - - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - - if (Creature* creature = me->FindNearestCreature(NPC_VELEN, 1000.0f)) + case 1: + for (uint8 i = 0; i < 3; ++i) { - creature->SetReactState(REACT_PASSIVE); - creature->AttackStop(); - } - - if (Creature* creature = me->FindNearestCreature(117931, 1000.0f)) - { - creature->SetReactState(REACT_PASSIVE); - creature->AttackStop(); - creature->setFaction(35); - } - - std::list unitList; - //me->GetAnyUnitListInRange(unitList, 1000.0f); - - if (!unitList.empty()) - { - for (auto unit : unitList) + me->AddDelayedEvent(100 + 600 * i, [this, i]() -> void { - if (unit->GetTypeId() != TYPEID_UNIT) - continue; - - unit->ToCreature()->Respawn(); - } + me->CastSpell(InfernalsPos[i], 242915, false); + }); } - - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - - DoZoneInCombat(); - - events.ScheduleEvent(EVENT_CHECK_FOR_EVENT_START, 1 * IN_MILLISECONDS); - - eventStarted = false; + break; + default: + break; } - - void CheckForStartEvent() - { - if (!eventStarted) - { - Map::PlayerList const &PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.isEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player *player = i->GetSource()) - if (player->IsAlive() && player->GetDistance2d(5967.10f, -795.73f) <= 3.0f) - { - if (eventStarted) - continue; - - me->Yell("Quickly, bolster our ranks! We must push through!", LANG_UNIVERSAL, NULL); - - events.ScheduleEvent(EVENT_ILLIDAN_TALK, 6 * IN_MILLISECONDS); - - events.CancelEvent(EVENT_CHECK_FOR_EVENT_START); - - eventStarted = true; - } - - } - } - - void CastSceneToPlayers() - { - Map::PlayerList const &PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.isEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player *player = i->GetSource()) - if (player->IsAlive()) - player->CastSpell(player, SPELL_GOROTH_INTRO, true); - } - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_IMPACT: - if (Creature* creature = me->FindNearestCreature(NPC_VELEN, 1000.0f)) - creature->DespawnOrUnsummon(); - - if (Creature* creature = me->FindNearestCreature(NPC_ILLIDAN, 1000.0f)) - creature->DespawnOrUnsummon(); - - DoCast(SPELL_IMPACT); - - me->DespawnCreaturesInArea(121024, 1000.0f); - me->DespawnCreaturesInArea(119742, 1000.0f); - me->DespawnCreaturesInArea(119768, 1000.0f); - me->DespawnCreaturesInArea(121005, 1000.0f); - me->DespawnCreaturesInArea(119730, 1000.0f); - me->DespawnCreaturesInArea(117931, 1000.0f); - me->DespawnCreaturesInArea(119777, 1000.0f); - - if (Creature* creature = me->FindNearestCreature(NPC_ATRIGAN, 1000.0f)) - { - creature->SummonCreature(NPC_INFERNAL_CHAOSBRINGER, 6130.221f, -801.1302f, 2971.884f, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); - creature->SummonCreature(NPC_INFERNAL_CHAOSBRINGER, 6086.503f, -816.4011f, 2971.987f, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); - creature->SummonCreature(NPC_INFERNAL_CHAOSBRINGER, 6064.199f, -782.8004f, 2971.644f, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); - - creature->DespawnOrUnsummon(5000); - } - - me->DespawnOrUnsummon(); - break; - - case EVENT_FALL_BACK: - Talk(SAY_INTRO); - - events.ScheduleEvent(EVENT_IMPACT, 100); - break; - - case EVENT_ILLIDAN_TALK: - if (Creature* creature = me->FindNearestCreature(NPC_ILLIDAN, 1000.0f)) - creature->Yell("Leave none standing! If we fall here, Azeroth burns.", LANG_UNIVERSAL, NULL); - - events.ScheduleEvent(EVENT_TALK_ATRIGAN1, 6 * IN_MILLISECONDS); - break; - - case EVENT_TALK_ATRIGAN1: - if (Creature* creature = me->FindNearestCreature(NPC_ATRIGAN, 1000.0f)) - { - creature->Yell("Yes! Let them come! Their bodies will fuel the flames or their burning world!", LANG_UNIVERSAL, NULL); - - CastSceneToPlayers(); - - if (GameObject* go = me->FindNearestGameObject(GO_DESTRUCTIBLE_1, 5000.0f)) - { - /*if (Creature* boss = me->FindNearestCreature(BOSS_GOROTH, 1000.0f)) - go->CastSpell(boss, SPELL_SUMMON, true);*/ - - go->SetLootState(GO_READY); - go->UseDoorOrButton(1000000, true); - } - } - - events.ScheduleEvent(EVENT_CAST_SCENE, 1 * IN_MILLISECONDS); - break; - - case EVENT_CHECK_FOR_EVENT_START: - CheckForStartEvent(); - - events.ScheduleEvent(EVENT_CHECK_FOR_EVENT_START, 1 * IN_MILLISECONDS); - break; - - case EVENT_CAST_SCENE: - - if (GameObject* go = me->FindNearestGameObject(GO_DESTRUCTIBLE_2, 1000.0f)) - { - go->SetLootState(GO_READY); - go->UseDoorOrButton(1000000, false); - } - - if (GameObject* go = me->FindNearestGameObject(GO_DESTRUCTIBLE_3, 1000.0f)) - { - go->SetLootState(GO_READY); - go->UseDoorOrButton(1000000, false); - } - - events.ScheduleEvent(EVENT_SUMMONS, 3 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_FALL_BACK, 5 * IN_MILLISECONDS); - break; - - case EVENT_SUMMONS: - if (Creature* creature = me->FindNearestCreature(BOSS_GOROTH, 1000.0f)) - { - creature->SetVisible(true); - creature->SetAIAnimKitId(8801); - creature->PlayOneShotAnimKitId(8801); - creature->CastSpell(creature, 233081, true); - creature->CastSpell(creature, SPELL_IMPACT, true); - } - - break; - } - } - } - - private: - EventMap events; - - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTombOfSargerasAI(creature); } }; -// 241227 - Summon -class spell_goroth_summon : public SpellScriptLoader +uint32 const introEntries[4] +{ + NPC_VELEN, + NPC_ILLIDAN, + NPC_MAEIV, + NPC_DURGAN +}; + +Position const introPositions[5] +{ + { 5986.409f, -795.8195f, 2978.126f, 6.269365f }, + { 5978.724f, -801.1215f, 2978.127f, 0.1859942f }, + { 5993.796f, -789.9375f, 2976.677f, 5.799626f }, + { 5990.001f, -801.5347f, 2977.88f, 0.3382317f }, + { 5980.707f, -790.559f, 2978.127f, 6.063946f } +}; + +// 119726 +struct npc_tos_kadghar_1 : ScriptedAI +{ + explicit npc_tos_kadghar_1(Creature* creature) : ScriptedAI(creature) + { + if (me->GetMapId() == 1676) + { + instance = me->GetInstanceScript(); + IntroDone = false; + outroDone = false; + } + else + { + IntroDone = true; + outroDone = false; + } + + + SetCanSeeEvenInPassiveMode(true); + } + + InstanceScript* instance; + bool IntroDone, outroDone; + + void DoAction(int32 const actionID) override + { + me->GetMotionMaster()->MovePoint(0, 6087.42f, -796.14f, 2971.72f); + for (uint8 i = 0; i < 4; ++i) + if (auto add = me->GetMap()->GetCreature(instance->GetGuidData(introEntries[i]))) + add->GetMotionMaster()->MovePoint(0, 6087.42f, -796.14f, 2971.72f); + + + me->AddDelayedEvent(8500, [this]() -> void + { + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(0, introPositions[0].GetPositionX() + 217.0f, introPositions[0].GetPositionY(), introPositions[0].GetPositionZ()); + for (uint8 i = 0; i < 4; ++i) + if (auto add = me->GetMap()->GetCreature(instance->GetGuidData(introEntries[i]))) + { + add->GetMotionMaster()->Clear(); + add->GetMotionMaster()->MovePoint(0, introPositions[i + 1].GetPositionX() + 217.0f, introPositions[i + 1].GetPositionY(), introPositions[i + 1].GetPositionZ()); + } + }); + + me->AddDelayedEvent(25000, [this]() -> void + { + if (Creature* egvin = me->SummonCreature(NPC_EGVIN, 6213.44f, -795.67f, 2975.00, 3.18f)) + { + egvin->CastSpell(egvin, 241351); + egvin->CastSpell(me, 245480); // me->CreateConversation(4951); + + egvin->AddDelayedEvent(600, [egvin] { + egvin->SetWalk(true); + egvin->GetMotionMaster()->MovePoint(0, 6209.66f, -795.69f, 2974.36f); + }); + } + }); + } + + void MoveInLineOfSight(Unit* who) override + { + if (!who->IsPlayer()) + return; + + if (!IntroDone && instance->GetBossState(DATA_GOROTH) == DONE) + { + IntroDone = true; + me->CastStop(); + me->CastSpell(introPositions[0], SPELL_INTRO_TELEPORT, false); + me->AddDelayedEvent(1000, [this] {me->SetFacingTo(6.25f); }); + + for (uint8 i = 0; i < 4; ++i) + if (auto add = me->GetMap()->GetCreature(instance->GetGuidData(introEntries[i]))) + { + if (i == 0) + { + add->RemoveAurasDueToSpell(241186); + add->CastStop(); + } + + add->CombatStop(); + add->CastSpell(introPositions[i + 1], SPELL_INTRO_TELEPORT, false); + add->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + add->AddDelayedEvent(1000, [add] {add->SetFacingTo(6.25f); }); + } + + if (auto visual_boss = me->GetMap()->GetCreature(instance->GetGuidData(NPC_ATRIGAN_MISC))) + visual_boss->DespawnOrUnsummon(); + + } + + if(IntroDone) + return; + + if (!instance) + return; + + if (me->GetDistance(who) < 100.0f && !IntroDone) + { + IntroDone = true; + + for (auto goid : { GO_INTRODESTROY_1, GO_INTRODESTROY_2, GO_INTRODESTROY_3}) + if (GameObject* go = me->GetMap()->GetGameObject(instance->GetGuidData(goid))) + go->SetByteValue(GAMEOBJECT_BYTES_1, 0, 0); + + if (GameObject* go = me->GetMap()->GetGameObject(instance->GetGuidData(GO_GOROTH_GATES))) + go->SetByteValue(GAMEOBJECT_BYTES_1, 0, 1); + + + me->AddDelayedEvent(2000, [this] () -> void + { + Talk(1); + me->CastStop(); + me->CastSpell(introPositions[0], SPELL_INTRO_TELEPORT, false); + me->AddDelayedEvent(1000, [this] {me->SetFacingTo(6.25f); }); + + for (uint8 i =0; i < 4; ++i) + if (auto add = me->GetMap()->GetCreature(instance->GetGuidData(introEntries[i]))) + { + if (i == 0) + { + add->RemoveAurasDueToSpell(241186); + add->CastStop(); + } + + add->CombatStop(); + add->CastSpell(introPositions[i + 1], SPELL_INTRO_TELEPORT, false); + add->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + add->AddDelayedEvent(1000, [add] {add->SetFacingTo(6.25f); }); + } + + instance->SetData(1, 1); + + if (GameObject* go = me->GetMap()->GetGameObject(instance->GetGuidData(GO_INQUISITION_DOOR_INTRO))) + { + go->SetByteValue(GAMEOBJECT_BYTES_1, 0, 0); + go->AddDelayedEvent(4000, [go] { + go->SetByteValue(GAMEOBJECT_BYTES_1, 0, 1); + }); + } + + if (auto visual_boss = me->GetMap()->GetCreature(instance->GetGuidData(NPC_ATRIGAN_MISC))) + { + visual_boss->RemoveAllAuras(); + visual_boss->GetMotionMaster()->MovePoint(0, 6370.82f, -795.00f, 3011.62f); + visual_boss->DespawnOrUnsummon(5000); + } + }); + + + + me->AddDelayedEvent(7000, [this]() -> void + { + if (auto boss = me->GetMap()->GetCreature(instance->GetGuidData(DATA_GOROTH))) + { + boss->SetVisible(true); + boss->SetReactState(REACT_DEFENSIVE); + boss->GetMotionMaster()->MovePath(25759848, false); + boss->AI()->Talk(10); + boss->AddDelayedEvent(4000, [boss]() -> void + { + boss->AI()->Talk(0); + boss->SetHomePosition(boss->GetPosition()); + }); + } + + me->AddDelayedEvent(1000, [this]() -> void { + if (auto summoner = me->GetMap()->GetCreature(instance->GetGuidData(NPC_BREACH_IN_WINDOW))) + summoner->AI()->DoAction(1); + }); + + }); + + } + + if (me->GetDistance(who) < 20.0f && !outroDone) + { + bool canUseIt = true; + for (uint8 i = 0; i < DATA_MAIDEN_OF_VIGILANCE; ++i) + if (instance->GetBossState(i) != DONE) + canUseIt = false; + + if (!canUseIt) + return; + + outroDone = true; + Creature* egvin = me->FindNearestCreature(NPC_EGVIN, 40.0f, true); + if (!egvin) + egvin = me->SummonCreature(NPC_EGVIN, 6213.44f, -795.67f, 2975.00, 3.18f); + + if (!egvin) + return; // it's unreal + + egvin->CastSpell(egvin, 240060); + } + } +}; + +// 269783 +struct go_tos_tele_to_kiljedan : GameObjectAI +{ + explicit go_tos_tele_to_kiljedan(GameObject* gameobject) : GameObjectAI(gameobject) + { + instance = go->GetInstanceScript(); + } + + InstanceScript* instance; + uint32 timer = 1000; + + void UpdateAI(uint32 diff) override + { + if (timer <= diff) + { + if (!instance || instance->GetBossState(DATA_FALLEN_AVATAR) != DONE) + return; + + const auto& players = instance->instance->GetPlayers(); + for (auto& pitr : players) + { + Player* who = pitr.GetSource(); + if (who->IsBeingTeleported()) + return; + + if (go->GetDistance(who) < 13) + who->NearTeleportTo(4499.94f, -1072.47f, 5335.96f, 4.70f); + } + timer = 1000; + } + else + timer -= diff; + } +}; + +class spell_vantus_rune_tomb_of_sargeras : public SpellScriptLoader { public: - spell_goroth_summon() : SpellScriptLoader("spell_goroth_summon") { } + spell_vantus_rune_tomb_of_sargeras() : SpellScriptLoader("spell_vantus_rune_tomb_of_sargeras") {} - class spell_goroth_summon_SpellScript : public SpellScript + class spell_vantus_rune_tomb_of_sargeras_AuraScript : public AuraScript { - PrepareSpellScript(spell_goroth_summon_SpellScript); + PrepareAuraScript(spell_vantus_rune_tomb_of_sargeras_AuraScript); - void HandleDummy(SpellEffIndex effIndex) + uint16 checkOnProc; + uint16 checkOnRemove; + + bool Load() { - /*if (Unit* caster = GetCaster()) + checkOnProc = 1000; + checkOnRemove = 1000; + return true; + } + + void OnUpdate(uint32 diff, AuraEffect* /*aurEff*/) + { + Unit* player = GetCaster(); + if (!player) + return; + + InstanceScript* instance = player->GetInstanceScript(); + if (!instance) + return; + + if (checkOnProc <= diff) { - if (GameObject* go = caster->FindNearestGameObject(269839, 1000.0f)) + if (instance->GetBossState(DATA_FALLEN_AVATAR) == IN_PROGRESS) { - go->setActive(true); - go->SetLootState(GO_ACTIVATED); + if (!player->HasAura(237795) && player->HasAura(237820)) + { + player->CastSpell(player, 237795, false); + } } - - if (GameObject* go = caster->FindNearestGameObject(269838, 1000.0f)) + if (instance->GetBossState(DATA_GOROTH) == IN_PROGRESS) { - go->setActive(true); - go->SetLootState(GO_ACTIVATED); + if (!player->HasAura(237796) && player->HasAura(237821)) + { + player->CastSpell(player, 237796, false); + } } - - if (Creature* creature = caster->FindNearestCreature(BOSS_GOROTH, 1000.0f)) + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) == IN_PROGRESS) { - creature->SetVisible(true); - creature->AddAura(233081, creature); + if (!player->HasAura(237797) && player->HasAura(237822)) + { + player->CastSpell(player, 237797, false); + } } + if (instance->GetBossState(DATA_MAIDEN_OF_VIGILANCE) == IN_PROGRESS) + { + if (!player->HasAura(237798) && player->HasAura(237823)) + { + player->CastSpell(player, 237798, false); + } + } + if (instance->GetBossState(DATA_HARJATAN) == IN_PROGRESS) + { + if (!player->HasAura(237799) && player->HasAura(237824)) + { + player->CastSpell(player, 237799, false); + } + } + if (instance->GetBossState(DATA_KILJAEDEN) == IN_PROGRESS) + { + if (!player->HasAura(237800) && player->HasAura(237825)) + { + player->CastSpell(player, 237800, false); + } + } + if (instance->GetBossState(DATA_MISTRESS_SASSZINE) == IN_PROGRESS) + { + if (!player->HasAura(237801) && player->HasAura(237826)) + { + player->CastSpell(player, 237801, false); + } + } + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == IN_PROGRESS) + { + if (!player->HasAura(237802) && player->HasAura(237827)) + { + player->CastSpell(player, 237802, false); + } + } + if (instance->GetBossState(DATA_DEMONIC_INQUISITION) == IN_PROGRESS) + { + if (!player->HasAura(237803) && player->HasAura(237828)) + { + player->CastSpell(player, 237803, false); + } + } + } + else checkOnProc -= diff; - for (uint8 i = 0; i < 4; ++i) - caster->SummonCreature(NPC_INFERNAL_CHAOSBRINGER, infernalChaosbringer[i], TEMPSUMMON_CORPSE_DESPAWN); - - caster->ToCreature()->DespawnOrUnsummon(); - }*/ + if (checkOnRemove <= diff) + { + if (player->HasAura(237795) && player->HasAura(237820)) + { + if (instance->GetBossState(DATA_FALLEN_AVATAR) == DONE || instance->GetBossState(DATA_FALLEN_AVATAR) == NOT_STARTED) + { + player->RemoveAura(237795); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237796) && player->HasAura(237821)) + { + if (instance->GetBossState(DATA_GOROTH) == DONE || instance->GetBossState(DATA_GOROTH) == NOT_STARTED) + { + player->RemoveAura(237796); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237797) && player->HasAura(237822)) + { + if (instance->GetBossState(DATA_SISTERS_OF_THEMOON) == DONE || instance->GetBossState(DATA_SISTERS_OF_THEMOON) == NOT_STARTED) + { + player->RemoveAura(237797); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237798) && player->HasAura(237823)) + { + if (instance->GetBossState(DATA_MAIDEN_OF_VIGILANCE) == DONE || instance->GetBossState(DATA_MAIDEN_OF_VIGILANCE) == NOT_STARTED) + { + player->RemoveAura(237798); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237799) && player->HasAura(237824)) + { + if (instance->GetBossState(DATA_HARJATAN) == DONE || instance->GetBossState(DATA_HARJATAN) == NOT_STARTED) + { + player->RemoveAura(237799); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237800) && player->HasAura(237825)) + { + if (instance->GetBossState(DATA_KILJAEDEN) == DONE || instance->GetBossState(DATA_KILJAEDEN) == NOT_STARTED) + { + player->RemoveAura(237800); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237801) && player->HasAura(237826)) + { + if (instance->GetBossState(DATA_MISTRESS_SASSZINE) == DONE || instance->GetBossState(DATA_MISTRESS_SASSZINE) == NOT_STARTED) + { + player->RemoveAura(237801); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237802) && player->HasAura(237827)) + { + if (instance->GetBossState(DATA_THE_DESOLATE_HOST) == DONE || instance->GetBossState(DATA_THE_DESOLATE_HOST) == NOT_STARTED) + { + player->RemoveAura(237802); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + if (player->HasAura(237803) && player->HasAura(237828)) + { + if (instance->GetBossState(DATA_DEMONIC_INQUISITION) == DONE || instance->GetBossState(DATA_DEMONIC_INQUISITION) == NOT_STARTED) + { + player->RemoveAura(237803); + checkOnProc = 1000; + checkOnRemove = 1000; + } + } + } + else checkOnRemove -= diff; } void Register() override { - OnEffectHitTarget += SpellEffectFn(spell_goroth_summon_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + /* hook EffectUpdate (tick par diff) non supporte par notre AuraScript */; + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_vantus_rune_tomb_of_sargeras_AuraScript(); + } + + class spell_vantus_rune_tomb_of_sargeras_SpellScript : public SpellScript + { + PrepareSpellScript(spell_vantus_rune_tomb_of_sargeras_SpellScript); + + SpellCastResult CheckCast() + { + Unit* caster = GetCaster(); + if (Player* player = caster->ToPlayer()) + { + if (!player->GetQuestRewardStatus(39695)) + return SPELL_CAST_OK; + } + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_YOU_ALREADY_USED_VANTUS_RUNE); + return SPELL_FAILED_CUSTOM_ERROR; + } + + void HandleOnHit() + { + if (!GetCaster()) + return; + + if (Player* player = GetCaster()->ToPlayer()) + { + uint32 questId = 39695; + if (questId) + { + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); + if (!quest) + return; + + if (player->CanTakeQuest(quest, false)) + player->CompleteQuest(questId); + } + } + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_vantus_rune_tomb_of_sargeras_SpellScript::CheckCast); + OnHit += SpellHitFn(spell_vantus_rune_tomb_of_sargeras_SpellScript::HandleOnHit); } }; SpellScript* GetSpellScript() const override { - return new spell_goroth_summon_SpellScript(); + return new spell_vantus_rune_tomb_of_sargeras_SpellScript(); } }; - void AddSC_tomb_of_sargeras() { - new npc_archimage_khadgar_tos(); - new spell_goroth_summon(); -} + RegisterCreatureAI(npc_tos_breach); + RegisterCreatureAI(npc_tos_kadghar_1); + RegisterGameObjectAI(go_tos_tele_to_kiljedan); + new spell_vantus_rune_tomb_of_sargeras(); +} \ No newline at end of file diff --git a/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.h b/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.h index a2db7ab..daedb78 100644 --- a/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.h +++ b/src/server/scripts/BrokenIsles/TombOfSargeras/tomb_of_sargeras.h @@ -1,161 +1,323 @@ /* - * Copyright (C) 2017-2018 AshamaneProject - * - * 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 . - */ + https://uwow.biz/ +*/ -#ifndef TOMB_OF_SARGERAS_H -#define TOMB_OF_SARGERAS_H +#ifndef TombOsSargeasH_ +#define TombOsSargeasH_ -#define DataHeader "TOS" -#define TombOfSargerasScriptName "instance_tomb_of_sargeras" - -#include "InstanceScript.h" -#include "AreaTriggerTemplate.h" +#include "AreaTrigger.h" #include "AreaTriggerAI.h" -#include "ScriptMgr.h" -#include "SpellScript.h" -#include "EventMap.h" -#include "SpellAuras.h" -#include "Unit.h" #include "GameObject.h" -#include "ScriptedCreature.h" -#include "Vehicle.h" -#include "VehicleDefines.h" -#include "MotionMaster.h" -#include "ObjectAccessor.h" -#include "TemporarySummon.h" -#include "Player.h" -#include "Map.h" #include "GameObjectAI.h" -#include "MapManager.h" -#include "GridNotifiers.h" -#include "InstanceSaveMgr.h" +#include "SpellAuras.h" +#include "SpellScript.h" +#include "ObjectAccessor.h" #include "ObjectMgr.h" +#include "TemporarySummon.h" +#include "MotionMaster.h" +#include "Player.h" -uint32 const EncounterCount = 10; +#include "Spell.h" +#include "SpellMgr.h" +#include "SpellAuraEffects.h" -enum tosDataTypes +// Constantes uwow absentes de notre core (valeurs = SharedDefines de DestinyCoreNew). +// Les hooks references sur ces valeurs ne matchent simplement pas si le client n a pas l effet. +#define SPELL_AURA_FIXATE AuraType(128) +#define SPELL_EFFECT_TELEPORT_L SpellEffectName(252) +#define TARGET_UNIT_ENEMY_BETWEEN_DEST Targets(129) +#define TARGET_UNIT_BETWEEN_ENEMY Targets(134) +#define REACT_ATTACK_OFF REACT_PASSIVE +#define UNIT_DYNFLAG_NOT_SELECTABLE_MODEL 0x0002 +#define CREATURE_SUMMON_GROUP_RESET 0 + +enum eData { - DATA_GOROTH, - DATA_HARJATAN, - DATA_MISTRESS_SASSZINE, - DATA_ATRIGAN, - DATA_BELAC, - DATA_SISTERS_OF_THE_MOON, - DATA_THE_DESOLATE_HOST, - DATA_MAIDEN_OF_VIGILANCE, - DATA_FALLEN_AVATAR, - DATA_KILJAEDEN, + DATA_GOROTH = 0, + DATA_HARJATAN = 1, + DATA_MISTRESS_SASSZINE = 2, - ENCOUNTER_COUNT, + DATA_DEMONIC_INQUISITION = 3, + DATA_THE_DESOLATE_HOST = 4, + DATA_SISTERS_OF_THEMOON = 5, + + DATA_MAIDEN_OF_VIGILANCE = 6, + DATA_FALLEN_AVATAR = 7, + DATA_KILJAEDEN = 8, + + MAX_ENCOUNTER }; -enum Creatures +enum eCreatures { - BOSS_GOROTH = 115844, - BOSS_ATRIGAN = 120996, + NPC_BREACH_IN_WINDOW = 121605, - // Demonic Inquisition - BOSS_BELAC = 116691, - BOSS_HARJATAN = 116407, - - // Sisters of the Moon - NPC_HUNTRESS_KASPARIAN = 118523, - NPC_CAPTAIN_YATHAE_MOONSTRIKE = 118374, - NPC_PRIESTESS_LUNASPYRE = 118518, - - NPC_SASSZINE = 115767, - - // Desolate Host - NPC_ENGINE_OF_SOULS = 118460, - NPC_SOUL_QUEEN_DEJAHNA = 118462, - NPC_DESOLATE_HOST = 119072, - - NPC_MAIDEN_OF_VIGILANCE = 118289, - NPC_FALLEN_AVATAR = 120436, - NPC_KILJAEDEN = 117269, - NPC_RAZORJAW_WAVEMENDER = 116569, - NPC_RAZORJAW_GLADIATOR = 117596, - NPC_INFERNAL_CHAOSBRINGER = 118022, - NPC_BRIMSTONE_INFERNAL = 119950, NPC_KHADGAR = 119726, NPC_VELEN = 119728, NPC_ILLIDAN = 119729, - NPC_ATRIGAN = 120996, + NPC_MAEIV = 119730, + NPC_DURGAN = 119777, + + NPC_EGIDA = 121630, + NPC_HAMMER_1 = 121494, + NPC_HAMMER_2 = 121111, + NPC_ELUNES_1 = 121495, + NPC_ELUNES_2 = 121110, + NPC_GOLGANETH_1 = 121496, + NPC_GOLGANETH_2 = 121105, + + NPC_EGVIN = 119723, + + NPC_ATRIGAN_MISC = 120996, //Not boss + NPC_SASSZINE_NPC_OUTRO = 121190, + NPC_SASSZINE_MISC = 121184, + NPC_LUNAR_ARCHER = 120721, + NPC_GUARDIAN_SENTRY = 120777, + NPC_TORMENTED_SOUL = 117957, + NPC_FRAGMENT_SOUL = 121138, + + NPC_KILDJEDAN_AVATARA = 120867, + +//! BOSSES + + //Goroth + NPC_GOROTH = 115844, + NPC_EMBER_STALKER = 115892, + NPC_INFERNAL_SPIKE = 116976, + NPC_LAVA_STALKER = 117931, //Heroic+ + NPC_BRIMSTONE_INFERNAL = 119950, + + //Harajatan + NPC_HARJATAN = 116407, + NPC_EGGS = 120545, + NPC_ELDER = 121071, + NPC_TADPOLE_1 = 120574, + NPC_TADPOLE_2 = 121156, + NPC_TADPOLE_3 = 121155, + NPC_EVENT_1 = 121011, + NPC_EVENT_2 = 117123, + + //Mistress Sassz'ine + NPC_SASSZINE = 115767, + NPC_ABYSS_STALKER = 115795, + NPC_TORNADO_TRIGGER = 118286, + NPC_ELECTRIFYING_JELLYFISH = 115896, + NPC_RAZORJAW_WAVERUNNER = 115902, + NPC_SARUKEL_TRIGGER = 116843, + NPC_OSSUNET_TRIGGER = 116881, + NPC_PIRANHADO_TRIGGER = 116841, + NPC_DELICIOUS_BUFFERFISH = 119791, //Mythic + NPC_TIDESTONE_GOLGANNETH = 121105, //Spawn after death boss + + //Demonic Inquisitor + NPC_ATRIGAN = 116689, + NPC_BELAK = 116691, + + NPC_ESSENCE_LIGHT = 118640, + NPC_ESSENCE_FEL = 118643, + NPC_REMANENCE_LIGHT = 119825, + NPC_REMANENCE_FEL = 119826, + + //Sisters of the Moon + NPC_MOON_STALKER = 118182, + NPC_HUNTRESS_KASPARIAN = 118523, + NPC_PRIESTESS_LUNASPYRE = 118518, + NPC_CAPTAIN_YATHAE = 118374, + NPC_MOONTALON = 119205, + NPC_GLAIVE_TARGET = 119054, + NPC_SISTERS_ACH_ADD = 121498, + + //The Desolate Host + NPC_SWIRLING_SOULS_TRIG = 120988, //Event Manager + NPC_DESOLATE_HOST = 119072, //!Real world + NPC_ENGINE_OF_SOULS = 118460, + NPC_REANIMATED_TEMPLAR = 118715, + NPC_REANIMATED_TEMPLAR_MIRROR = 119938, //Mythic + NPC_GHASTLY_BONEWARDEN = 118728, + NPC_GHASTLY_BONEWARDEN_MIRROR = 119939, //Mythic + NPC_TORMENTED_CRIES_TRIG = 118924, + NPC_SOUL_QUEEN_DEJAHNA = 118462, //!Spirit world + NPC_FALLEN_PRIESTESS = 118729, + NPC_FALLEN_PRIESTESS_MIRROR = 119940, //Mythic + NPC_SOUL_RESIDUE = 118730, + NPC_SOUL_RESIDUE_MIRROR = 119941, //Mythic + NPC_SPIRITUAL_FONT = 118701, //!Other + NPC_SPIRITUAL_BARRIER_TRIG = 118727, + NPC_TEARS_OF_ELUNE = 121110, //Spawn After Dies Boss + + //Avatara + NPC_FALLEN_AVATAR = 116939, + NPC_CORRUPTED_BLADE = 119158, + NPC_PILONES = 117279, + NPC_AVATARA_MAIDEN = 117264, + NPC_BLACK_WINDS = 121294, + NPC_TOUCH_OF_SARGERAS = 120838, + + //Maiden of Vigilance + NPC_MAIDEN_OF_VIGILANCE = 118289, + + //Kil'jaeden + NPC_KILJAEDEN = 117269, + NPC_ARMAGEDDON_STALKER = 120839, + NPC_ERUPTING_REFLECTION = 119206, + NPC_WAILING_REFLECTION = 119107, //Mythic + NPC_HOPELESS_REFLECTION = 119663, //Mythic + NPC_SHADOWSOUL = 121193, + NPC_STAGE4_ILLIDAN_STORMRAGE = 121227, + NPC_DEMONIC_OBELISK = 120270, + NPC_NETHER_RIFT = 120390, + NPC_FLAMING_ORB = 120082, //Heroic+ }; -enum CosmeticSpells +enum eGameObjects { - SPELL_COSMETIC_TELEPORT = 240923, // Used by Kil'jaeden after Fallen Avatar activation - SPELL_AWAKEN = 240897, // Kil'jaeden activate Fallen Avatar spell. - SPELL_FEL_INFUSION = 236682, // Used by Maiden of Valor. + GO_INTRODESTROY_1 = 269839, + GO_INTRODESTROY_2 = 269838, + GO_INTRODESTROY_3 = 269842, + + GO_GOROTH_GATES = 272802, + GO_GOROTH_INFERNAL_SPIKE = 266938, //LOS_BLOCKER + + GO_SASSZINE_DOOR = 269976, + GO_INQUISITION_DOOR = 268579, + GO_INQUISITION_DOOR_INTRO = 268580, + GO_SISTERS_MOON_DOOR = 269045, + + GO_HAMMER_THRONE = 269780, + GO_ELUNES_THRONE = 269779, + GO_GOLGANETH_THRONE = 269782, + + GO_MAIDEN_DOOR = 269988, + GO_ELEVATOR = 268574, + + GO_AVATARA_FLOOR = 267934, + GO_AVATARA_DOOR = 269262, + + GO_DESOLATE_HOST_DOOR_1 = 268749, + GO_DESOLATE_HOST_DOOR_2 = 269781, + + GO_KILJAEDEN_BRIDGE_1 = 271126, + GO_KILJAEDEN_BRIDGE_2 = 271127, + GO_KILJAEDEN_CHEST = 271099, }; -enum tosSpells +enum eSpells { - SPELL_SUMMON = 241227, - SPELL_IMPACT = 242924, - SPELL_PILLAR_COSMETIC = 243149, - SPELL_GOROTH_INTRO = 247931, + SPELL_INTRO_TELEPORT = 241303, }; -enum tosSounds + + +enum TosGenericActions { - SOUND_INTRO = 81787, - SOUND_GOROTH2 = 81789, + ACTION_NONE = 0, + ACTION_1 = 1, + ACTION_2, + ACTION_3, + ACTION_4, + ACTION_5, + ACTION_6, + ACTION_7, + ACTION_8, + ACTION_9, + ACTION_10, + ACTION_11, + ACTION_12, + ACTION_13, + ACTION_14, + ACTION_15, + ACTION_16, + ACTION_17, + ACTION_18, + ACTION_19, + ACTION_20, + ACTION_21, + ACTION_22, + ACTION_23, + ACTION_24, + ACTION_25, + ACTION_26, + ACTION_27, + ACTION_28, + ACTION_29, + ACTION_30, + ACTION_31, + ACTION_32, + ACTION_33, + ACTION_34, + ACTION_35, + ACTION_36, + ACTION_37, + ACTION_38, + ACTION_39, + ACTION_40, + ACTION_41, + ACTION_42, + ACTION_43, + ACTION_44, + ACTION_45, + ACTION_46, + ACTION_47, + ACTION_48, + ACTION_49, + ACTION_50, }; -enum Gameobjects +// Enum generique d evenements (present dans l EventMap.h de DestinyCoreNew, module-local ici) +enum TosGenericEvents { - GO_MOON_CHAMBER_WINDOW = 269842, - - GO_DOOR_ROOM_GOROTH_S = 269974, - GO_DOOR_ROOM_GOROTH_N = 269975, - GO_DOOR_ROOM_GOROTH_E = 269973, - GO_DOOR_ROOM_GOROTH_4 = 268580, - - GO_DOOR_HARJATAN_1 = 269120, - GO_DOOR_HARJATAN_2 = 269192, - - GO_DOOR_AFTER_SASSZINE_1 = 268752, - GO_DOOR_SISTERS_OF_THE_MOON_1 = 268750, - GO_DOOR_SISTERS_OF_THE_MOON_2 = 268514, - GO_DOOR_SISTERS_OF_THE_MOON_3 = 268751, - GO_DOOR_SISTERS_OF_THE_MOON_4 = 268749, - GO_DOOR_DEMONIC_INQUISITION_1 = 268579, - GO_DOOR_MAIDEN_OF_VIGILANCE_1 = 269164, - GO_DOOR_MAIDEN_OF_VIGILANCE_2 = 269261, - GO_DOOR_MAIDEN_OF_VIGILANCE_3 = 269262, - GO_DOOR_FALLEN_AVATAR = 267934, - - GO_DOOR_FELSTORM_COLLISION_01 = 269987, - GO_DESTRUCTIBLE_1 = 269842, - GO_DESTRUCTIBLE_2 = 269839, - GO_DESTRUCTIBLE_3 = 269838, - GO_DOOR_TO_MISTRESS = 269120, - GO_DOOR_TO_MISTRESS2 = 270940, + EVENT_1 = 1, + EVENT_2, + EVENT_3, + EVENT_4, + EVENT_5, + EVENT_6, + EVENT_7, + EVENT_8, + EVENT_9, + EVENT_10, + EVENT_11, + EVENT_12, + EVENT_13, + EVENT_14, + EVENT_15, + EVENT_16, + EVENT_17, + EVENT_18, + EVENT_19, + EVENT_20, + EVENT_21, + EVENT_22, + EVENT_23, + EVENT_24, + EVENT_25, + EVENT_26, + EVENT_27, + EVENT_28, + EVENT_29, + EVENT_30, + EVENT_31, + EVENT_32, + EVENT_33, + EVENT_34, + EVENT_35, + EVENT_36, + EVENT_37, + EVENT_38, + EVENT_39, + EVENT_40, + EVENT_41, + EVENT_42, + EVENT_43, + EVENT_44, + EVENT_45, + EVENT_46, + EVENT_47, + EVENT_48, + EVENT_49, + EVENT_50, }; -template -CreatureAI* GetTombOfSargerasAI(Creature* creature) -{ - if (InstanceMap* instance = creature->GetMap()->ToInstanceMap()) - if (instance->GetInstanceScript()) - if (instance->GetScriptId() == sObjectMgr->GetScriptId(TombOfSargerasScriptName)) - return new AI(creature); - return NULL; -} +#define ToSScriptName "instance_tomb_of_sargeras" #endif diff --git a/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp b/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp index 7c890a4..60142be 100644 --- a/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp +++ b/src/server/scripts/BrokenIsles/brokenisles_script_loader.cpp @@ -175,14 +175,15 @@ void AddSC_instance_emerald_nightmare(); // Tomb of Sargeras void AddSC_boss_demonic_inquisition(); -void AddSC_boss_desolate_host(); +void AddSC_boss_the_desolate_host(); void AddSC_boss_fallen_avatar(); void AddSC_boss_goroth(); +void AddSC_boss_sisters_of_the_moon(); void AddSC_boss_shiwar(); void AddSC_boss_harjatan(); void AddSC_boss_tos_kiljaeden(); void AddSC_boss_maiden_of_vigilance(); -void AddSC_boss_sasszine(); +void AddSC_boss_mistress_sasszine(); void AddSC_tomb_of_sargeras(); void AddSC_instance_tomb_of_sargeras(); @@ -376,14 +377,15 @@ void AddBrokenIslesScripts() // Tomb of Sargeras AddSC_boss_demonic_inquisition(); - AddSC_boss_desolate_host(); + AddSC_boss_the_desolate_host(); AddSC_boss_fallen_avatar(); AddSC_boss_goroth(); + AddSC_boss_sisters_of_the_moon(); AddSC_boss_shiwar(); AddSC_boss_harjatan(); AddSC_boss_tos_kiljaeden(); AddSC_boss_maiden_of_vigilance(); - AddSC_boss_sasszine(); + AddSC_boss_mistress_sasszine(); AddSC_tomb_of_sargeras(); AddSC_instance_tomb_of_sargeras();