From e9cd7af2dfcb7948f27e83e73398da0086190aab Mon Sep 17 00:00:00 2001 From: Gustavo Date: Tue, 8 Aug 2017 22:36:40 -0300 Subject: [PATCH] Core/Scripts: Rewrite Boss Kalecgos (Sunwell Plateau) (#19897) * Core/Scripts: Rewrite Boss Kalecgos (Sunwell Plateau) Scripted all spells Corrected behavior of Spectral Blast and Curse of Boundless Agony Removed wrong wipe behavior Fix problem where bosses would be stuck with banish Added Boundaries Corrected texts Removed old hacks Other minor fixes --- .../world/3.3.5/2017_08_08_00_world.sql | 42 + src/server/game/Spells/Spell.cpp | 5 +- .../SunwellPlateau/boss_kalecgos.cpp | 1383 ++++++++--------- .../instance_sunwell_plateau.cpp | 7 + .../SunwellPlateau/sunwell_plateau.h | 5 +- 5 files changed, 734 insertions(+), 708 deletions(-) create mode 100644 sql/updates/world/3.3.5/2017_08_08_00_world.sql diff --git a/sql/updates/world/3.3.5/2017_08_08_00_world.sql b/sql/updates/world/3.3.5/2017_08_08_00_world.sql new file mode 100644 index 00000000000..c436d342a42 --- /dev/null +++ b/sql/updates/world/3.3.5/2017_08_08_00_world.sql @@ -0,0 +1,42 @@ +UPDATE `creature_template` SET `unit_flags`=32832 WHERE `entry`=24892; +UPDATE `creature_template` SET `flags_extra`= `flags_extra` |512 WHERE `entry`=24850; +UPDATE `creature_template` SET `unit_flags`=32832, `ScriptName`='boss_kalecgos_human' WHERE `entry`=24891; +UPDATE `gameobject_template` SET `ScriptName`='go_kalecgos_spectral_rift' WHERE `entry`=187055; + +DELETE FROM `creature_template_addon` WHERE `entry` IN (24891,24892); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `auras`) VALUES +(24891,0,0,0,1,0,44801), +(24892,0,0,0,1,0,'44800 44801'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=46733; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,46733,0,1,31,0,3,24892,0,0,0,0,'','Spell Tap Check targets Sathrovarr'); + +UPDATE `creature_text` SET `Type`=12 WHERE `CreatureID`=24850 AND `GroupID`=2 AND`ID`=0; +UPDATE `creature_text` SET `TextRange`=2 WHERE `CreatureID` IN(24850,24891,24892); +UPDATE `creature_text` SET `comment`='kalecgos SAY_OUTRO1' WHERE `CreatureID`=24850 and`GroupID`=2 and`ID`=0; +UPDATE `creature_text` SET `comment`='kalecgos SAY_GOOD_NEAR_DEATH1' WHERE `CreatureID`=24891 and`GroupID`=1 and`ID`=0; +DELETE FROM `creature_text` WHERE `CreatureID`=24891 AND `GroupID`=3; +DELETE FROM `creature_text` WHERE `CreatureID`=24850 AND `GroupID` IN(3,4,6); +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(24891,3,0,'There is no time to waste!',14,0,100,0,0,12435,24993,2,'kalecgos SAY_GOOD_DEATH'), +(24850,3,0,'Kil\'jaeden must be destroyed!',14,0,100,0,0,12433,25502,2,'SAY_OUTRO_2'), +(24850,4,0,'Sathrovarr drives Kalecgos into a crazed rage!',41,0,100,0,0,0,24983,2,'kalecgos SAY_ENRAGE'), +(24850,6,0,'I will purge you!',14,0,100,0,0,0,25497,2,'kalecgos SAY_ARCANE_BUFFET'), +(24850,6,1,'Your pain has only begun!',14,0,100,0,0,0,25498,2,'kalecgos SAY_ARCANE_BUFFET'); + +DELETE FROM `spell_linked_spell` WHERE `spell_trigger` IN (44869,46019,46021,-46021); + +DELETE FROM `spell_script_names` WhERE `ScriptName` IN +('spell_kalecgos_spectral_blast', +'spell_kalecgos_spectral_realm_trigger', +'spell_kalecgos_spectral_realm_aura', +'spell_kalecgos_curse_of_boundless_agony', +'spell_kalecgos_tap_check'); +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(44869,'spell_kalecgos_spectral_blast'), +(44811,'spell_kalecgos_spectral_realm_trigger'), +(46021,'spell_kalecgos_spectral_realm_aura'), +(45032,'spell_kalecgos_curse_of_boundless_agony'), +(45034,'spell_kalecgos_curse_of_boundless_agony'), +(46732,'spell_kalecgos_tap_check'); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 48dddb23ca5..be01f88c2d5 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2552,7 +2552,8 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA // for delayed spells ignore negative spells (after duel end) for friendly targets /// @todo this cause soul transfer bugged // 63881 - Malady of the Mind jump spell (Yogg-Saron) - if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->IsPositive() && m_spellInfo->Id != 63881) + // 45034 - Curse of Boundless Agony jump spell (Kalecgos) + if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->IsPositive() && m_spellInfo->Id != 63881 && m_spellInfo->Id != 45034) return SPELL_MISS_EVADE; // assisting case, healing and resurrection @@ -7882,7 +7883,7 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) if (!isInsideCylinder) return false; } - + return WorldObjectSpellTargetCheck::operator ()(target); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp index 5dd4a3248ac..065448261b7 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2017 TrinityCore - * Copyright (C) 2006-2009 ScriptDev2 * * 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 @@ -16,808 +15,782 @@ * with this program. If not, see . */ -/* ScriptData -SDName: Boss_Kalecgos -SD%Complete: 95 -SDComment: -SDCategory: Sunwell_Plateau -EndScriptData */ - #include "ScriptMgr.h" #include "GameObject.h" #include "GameObjectAI.h" #include "InstanceScript.h" -#include "Log.h" -#include "Map.h" #include "MotionMaster.h" -#include "ObjectAccessor.h" #include "Player.h" #include "ScriptedCreature.h" +#include "SpellAuraEffects.h" +#include "SpellScript.h" #include "sunwell_plateau.h" #include "TemporarySummon.h" #include "WorldSession.h" enum Yells { - SAY_SATH_AGGRO = 0, - SAY_SATH_SLAY = 1, - SAY_SATH_DEATH = 2, - SAY_SATH_SPELL1 = 3, - SAY_SATH_SPELL2 = 4, + SAY_SATH_AGGRO = 0, + SAY_SATH_SLAY = 1, + SAY_SATH_DEATH = 2, + SAY_SATH_SPELL1 = 3, + SAY_SATH_SPELL2 = 4, - SAY_EVIL_AGGRO = 0, - SAY_EVIL_SLAY = 1, - SAY_GOOD_PLRWIN = 2, - SAY_EVIL_ENRAGE = 3, + SAY_EVIL_AGGRO = 0, + SAY_EVIL_SLAY = 1, + SAY_OUTRO_1 = 2, + SAY_OUTRO_2 = 3, + EMOTE_ENRAGE = 4, + SAY_ARCANE_BUFFET = 6, - SAY_GOOD_AGGRO = 0, - SAY_GOOD_NEAR_DEATH = 1, - SAY_GOOD_NEAR_DEATH2 = 2 + SAY_GOOD_NEAR_DEATH_0 = 0, + SAY_GOOD_NEAR_DEATH_1 = 1, + SAY_GOOD_NEAR_DEATH_2 = 2, + SAY_GOOD_DEATH = 3 }; enum Spells { - AURA_SUNWELL_RADIANCE = 45769, - AURA_SPECTRAL_EXHAUSTION = 44867, - AURA_SPECTRAL_REALM = 46021, - AURA_SPECTRAL_INVISIBILITY = 44801, - AURA_DEMONIC_VISUAL = 44800, + SPELL_SPECTRAL_BLAST = 44869, + SPELL_ARCANE_BUFFET = 45018, + SPELL_FROST_BREATH = 44799, + SPELL_TAIL_LASH = 45122, + SPELL_WILD_MAGIC_1 = 45001, + SPELL_WILD_MAGIC_2 = 45002, + SPELL_WILD_MAGIC_3 = 45004, + SPELL_WILD_MAGIC_4 = 45006, + SPELL_WILD_MAGIC_5 = 45010, + SPELL_WILD_MAGIC_6 = 44978, + SPELL_BANISH = 44836, + SPELL_ENRAGE = 44807, + SPELL_DEMONIC_VISUAL = 44800, + SPELL_CORRUPTION_STRIKE = 45029, + SPELL_AGONY_CURSE = 45032, + SPELL_SHADOW_BOLT = 45031, + SPELL_TAP_CHECK = 46732, + SPELL_TAP_CHECK_DAMAGE = 46733, + SPELL_AGONY_CURSE_VISUAL_1 = 45083, + SPELL_AGONY_CURSE_VISUAL_2 = 45084, + SPELL_AGONY_CURSE_VISUAL_3 = 45085, + SPELL_AGONY_CURSE_ALLY = 45034, + SPELL_HEROIC_STRIKE = 45026, + SPELL_REVITALIZE = 45027, + SPELL_SPECTRAL_BLAST_EFFECT = 44866, + SPELL_SPECTRAL_BLAST_VISUAL = 46648, + SPELL_SPECTRAL_REALM_TRIGGER = 44811, + SPELL_SPECTRAL_REALM_TELEPORT = 46019, + SPELL_SPECTRAL_REALM_AURA = 46021, + SPELL_SPECTRAL_REALM_2 = 44845, + SPELL_SPECTRAL_REALM_REACTION = 44852, + SPELL_SPECTRAL_EXHAUSTION = 44867, + SPELL_TELEPORT_BACK = 46020 +}; - SPELL_SPECTRAL_BLAST = 44869, - SPELL_TELEPORT_SPECTRAL = 46019, - SPELL_ARCANE_BUFFET = 45018, - SPELL_FROST_BREATH = 44799, - SPELL_TAIL_LASH = 45122, - - SPELL_BANISH = 44836, - SPELL_TRANSFORM_KALEC = 44670, - SPELL_ENRAGE = 44807, - - SPELL_CORRUPTION_STRIKE = 45029, - SPELL_AGONY_CURSE = 45032, - SPELL_SHADOW_BOLT = 45031, - - SPELL_HEROIC_STRIKE = 45026, - SPELL_REVITALIZE = 45027 +enum KalecgosEvents +{ + EVENT_ARCANE_BUFFET = 1, + EVENT_FROST_BREATH, + EVENT_WILD_MAGIC, + EVENT_TAIL_LASH, + EVENT_SPECTRAL_BLAST, + EVENT_CHECK_TIMER, + EVENT_OUTRO_START, + EVENT_OUTRO_1, + EVENT_OUTRO_2, + EVENT_OUTRO_3, + EVENT_REVITALIZE, + EVENT_HEROIC_STRIKE, + EVENT_SHADOWBOLT, + EVENT_AGONY_CURSE, + EVENT_CORRUPTION_STRIKE }; enum SWPActions { - DO_ENRAGE = 1, - DO_BANISH = 2 + ACTION_START_OUTRO = 1, + ACTION_ENRAGE }; -enum Misc +enum KalecSayPhases { - FLY_X = 1679, - FLY_Y = 900, - FLY_Z = 82, - CENTER_X = 1705, - CENTER_Y = 930, - RADIUS = 30, - MAX_PLAYERS_IN_SPECTRAL_REALM = 0 // over this, teleport object won't work, 0 disables check + PHASE_SAY_ONE = 1, + PHASE_SAY_TWO, + PHASE_SAY_THREE, + PHASE_SAY_FOUR, + PHASE_OUTRO }; -#define DRAGON_REALM_Z 53.079f -#define DEMON_REALM_Z -74.558f - -uint32 WildMagic[] = { 44978, 45001, 45002, 45004, 45006, 45010 }; - -class boss_kalecgos : public CreatureScript +enum KalecgosPoints { -public: - boss_kalecgos() : CreatureScript("boss_kalecgos") { } + POINT_OUTRO_1 = 0, + POINT_OUTRO_2 +}; - struct boss_kalecgosAI : public ScriptedAI +Position const KalecgosSummonPos = { 1709.094f, 927.5035f, -74.28364f, 2.932153f }; +Position const FlyPos[2] = +{ + { 1704.18f, 927.999f, 57.888f }, + { 1614.355f, 846.9694f, 119.0971f } +}; + +uint32 const WildMagicSpells[6] = +{ + SPELL_WILD_MAGIC_1, + SPELL_WILD_MAGIC_2, + SPELL_WILD_MAGIC_3, + SPELL_WILD_MAGIC_4, + SPELL_WILD_MAGIC_5, + SPELL_WILD_MAGIC_6 +}; + +struct boss_kalecgos : public BossAI +{ + boss_kalecgos(Creature* creature) : BossAI(creature, DATA_KALECGOS), _isEnraged(false), _isBanished(false) { } + + void Reset() override { - boss_kalecgosAI(Creature* creature) : ScriptedAI(creature) + _isEnraged = false; + _isBanished = false; + _Reset(); + events.ScheduleEvent(EVENT_ARCANE_BUFFET, Seconds(8)); + events.ScheduleEvent(EVENT_FROST_BREATH, Seconds(15)); + events.ScheduleEvent(EVENT_WILD_MAGIC, Seconds(10)); + events.ScheduleEvent(EVENT_TAIL_LASH, Seconds(25)); + events.ScheduleEvent(EVENT_SPECTRAL_BLAST, Seconds(20), Seconds(25)); + events.ScheduleEvent(EVENT_CHECK_TIMER, Seconds(1)); + } + + void EnterEvadeMode(EvadeReason /*why*/) + { + if (events.IsInPhase(PHASE_OUTRO)) + return; + + _EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPECTRAL_REALM_AURA); + summons.DespawnAll(); + DespawnPortals(); + + if (Creature* sathrovar = instance->GetCreature(DATA_SATHROVARR)) + _DespawnAtEvade(Seconds(10), sathrovar); + + _DespawnAtEvade(Seconds(10)); + } + + void DespawnPortals() + { + std::vector portals; + me->GetGameObjectListWithEntryInGrid(portals, GO_SPECTRAL_RIFT); + for (GameObject* portal : portals) + portal->Delete(); + } + + void DoAction(int32 action) override + { + switch (action) { - Initialize(); - instance = creature->GetInstanceScript(); - bJustReset = false; - me->setActive(true); + case ACTION_START_OUTRO: + events.ScheduleEvent(EVENT_OUTRO_START, Seconds(1)); + break; + case ACTION_ENRAGE: + _isEnraged = true; + Talk(EMOTE_ENRAGE); + DoCastSelf(SPELL_ENRAGE, true); + break; + default: + break; } + } - void Initialize() - { - SathGUID.Clear(); - ArcaneBuffetTimer = 8000; - FrostBreathTimer = 15000; - WildMagicTimer = 10000; - TailLashTimer = 25000; - SpectralBlastTimer = urand(20000, 25000); - CheckTimer = 1000; - ResetTimer = 30000; + void DamageTaken(Unit* who, uint32 &damage) override + { + if (damage >= me->GetHealth() && who->GetGUID() != me->GetGUID()) + damage = 0; + } - TalkTimer = 0; - TalkSequence = 0; - isFriendly = false; - isEnraged = false; - isBanished = false; - } + void EnterCombat(Unit* /*who*/) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + Talk(SAY_EVIL_AGGRO); + _EnterCombat(); - InstanceScript* instance; - - uint32 ArcaneBuffetTimer; - uint32 FrostBreathTimer; - uint32 WildMagicTimer; - uint32 SpectralBlastTimer; - uint32 TailLashTimer; - uint32 CheckTimer; - uint32 TalkTimer; - uint32 TalkSequence; - uint32 ResetTimer; - - bool isFriendly; - bool isEnraged; - bool isBanished; - bool bJustReset; - - ObjectGuid SathGUID; - - void Reset() override - { - if (Creature* sath = instance->GetCreature(DATA_SATHROVARR)) - SathGUID = sath->GetGUID(); - - instance->SetBossState(DATA_KALECGOS, NOT_STARTED); - - if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) - Sath->AI()->EnterEvadeMode(); - - me->SetFaction(FACTION_MONSTER); - if (!bJustReset) //first reset at create + if (Creature* kalecgosHuman = me->SummonCreature(NPC_KALECGOS_HUMAN, KalecgosSummonPos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1000)) + if (Creature* sathrovar = instance->GetCreature(DATA_SATHROVARR)) { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - me->SetDisableGravity(false); - me->SetVisible(true); - me->SetStandState(UNIT_STAND_STATE_SLEEP); + sathrovar->SetInCombatWith(kalecgosHuman); + kalecgosHuman->SetInCombatWith(sathrovar); } - me->SetFullHealth(); //dunno why it does not resets health at evade.. - } + } - void EnterEvadeMode(EvadeReason why) override - { - bJustReset = true; - me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - ScriptedAI::EnterEvadeMode(why); - } - - void DoAction(int32 param) override - { - switch (param) - { - case DO_ENRAGE: - isEnraged = true; - me->CastSpell(me, SPELL_ENRAGE, true); - break; - case DO_BANISH: - isBanished = true; - me->CastSpell(me, SPELL_BANISH, true); - break; - } - } - - void UpdateAI(uint32 diff) override - { - if (TalkTimer) - { - if (!TalkSequence) - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - me->InterruptNonMeleeSpells(true); - me->RemoveAllAuras(); - me->GetThreatManager().ClearAllThreat(); - me->CombatStop(); - ++TalkSequence; - } - if (TalkTimer <= diff) - { - if (isFriendly) - GoodEnding(); - else - BadEnding(); - ++TalkSequence; - } else TalkTimer -= diff; - } - else - { - if (bJustReset) - { - if (ResetTimer <= diff) - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->SetDisableGravity(false); - me->SetVisible(true); - me->SetStandState(UNIT_STAND_STATE_SLEEP); - ResetTimer = 10000; - bJustReset = false; - } else ResetTimer -= diff; - return; - } - - if (!UpdateVictim()) - return; - - if (CheckTimer <= diff) - { - if (me->GetDistance(CENTER_X, CENTER_Y, DRAGON_REALM_Z) >= 75) - { - EnterEvadeMode(EVADE_REASON_BOUNDARY); - return; - } - if (HealthBelowPct(10) && !isEnraged) - { - if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) - Sath->AI()->DoAction(DO_ENRAGE); - DoAction(DO_ENRAGE); - } - if (!isBanished && HealthBelowPct(1)) - { - if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) - { - if (Sath->HasAura(SPELL_BANISH)) - { - Sath->DealDamage(Sath, Sath->GetHealth()); - return; - } - else - DoAction(DO_BANISH); - } - else - { - TC_LOG_ERROR("scripts", "Didn't find Shathrowar. Kalecgos event reseted."); - EnterEvadeMode(EVADE_REASON_OTHER); - return; - } - } - CheckTimer = 1000; - } else CheckTimer -= diff; - - if (ArcaneBuffetTimer <= diff) - { - DoCastAOE(SPELL_ARCANE_BUFFET); - ArcaneBuffetTimer = 8000; - } else ArcaneBuffetTimer -= diff; - - if (FrostBreathTimer <= diff) - { - DoCastAOE(SPELL_FROST_BREATH); - FrostBreathTimer = 15000; - } else FrostBreathTimer -= diff; - - if (TailLashTimer <= diff) - { - DoCastAOE(SPELL_TAIL_LASH); - TailLashTimer = 15000; - } else TailLashTimer -= diff; - - if (WildMagicTimer <= diff) - { - DoCastAOE(WildMagic[rand32() % 6]); - WildMagicTimer = 20000; - } else WildMagicTimer -= diff; - - if (SpectralBlastTimer <= diff) - { - ThreatContainer::StorageType const& m_threatlist = me->GetThreatManager().getThreatList(); - std::list targetList; - for (ThreatContainer::StorageType::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) - { - Unit* target = (*itr)->getTarget(); - if (target - && target->GetTypeId() == TYPEID_PLAYER - && (!target->GetVictim() || target->GetGUID() != me->EnsureVictim()->GetGUID()) - && target->GetPositionZ() > me->GetPositionZ() - 5 - && !target->HasAura(AURA_SPECTRAL_EXHAUSTION)) - { - targetList.push_back(target); - } - } - if (targetList.empty()) - { - SpectralBlastTimer = 1000; - return; - } - - std::list::const_iterator i = targetList.begin(); - advance(i, rand32() % targetList.size()); - if ((*i)) - { - (*i)->CastSpell((*i), SPELL_SPECTRAL_BLAST, true); - SpectralBlastTimer = 20000 + rand32() % 5000; - } else SpectralBlastTimer = 1000; - } else SpectralBlastTimer -= diff; - - DoMeleeAttackIfReady(); - } - } - - void MoveInLineOfSight(Unit* who) override - { - if (bJustReset)//boss is invisible, don't attack - return; - - if (!me->GetVictim() && me->IsValidAttackTarget(who)) - { - float attackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, attackRadius)) - AttackStart(who); - } - } - - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (damage >= me->GetHealth() && done_by != me) - damage = 0; - } - - void EnterCombat(Unit* /*who*/) override - { - me->SetStandState(UNIT_STAND_STATE_STAND); - Talk(SAY_EVIL_AGGRO); - DoZoneInCombat(); - - instance->SetBossState(DATA_KALECGOS, IN_PROGRESS); - } - - void KilledUnit(Unit* /*victim*/) override - { + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER && roll_chance_i(50)) Talk(SAY_EVIL_SLAY); - } + } - void MovementInform(uint32 type, uint32 /*id*/) override - { - if (type != POINT_MOTION_TYPE) - return; - me->SetVisible(false); - if (isFriendly) - { - me->setDeathState(JUST_DIED); - me->GetMap()->ToInstanceMap()->PermBindAllPlayers(); - } - else - { - me->GetMotionMaster()->MoveTargetedHome(); - TalkTimer = 1000; - } - } + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + return; - void GoodEnding() + switch (id) { - switch (TalkSequence) + case POINT_OUTRO_1: + Talk(SAY_OUTRO_1); + events.ScheduleEvent(EVENT_OUTRO_3, Seconds(9)); + break; + case POINT_OUTRO_2: + me->SetVisible(false); + DespawnPortals(); + me->KillSelf(); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(PHASE_OUTRO)) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - case 1: + case EVENT_ARCANE_BUFFET: + if (roll_chance_i(20)) + Talk(SAY_ARCANE_BUFFET); + DoCastAOE(SPELL_ARCANE_BUFFET); + events.Repeat(Seconds(8)); + break; + case EVENT_FROST_BREATH: + DoCastAOE(SPELL_FROST_BREATH); + events.Repeat(Seconds(15)); + break; + case EVENT_TAIL_LASH: + DoCastAOE(SPELL_TAIL_LASH); + events.Repeat(Seconds(15)); + break; + case EVENT_WILD_MAGIC: + DoCastAOE(WildMagicSpells[urand(0, 5)], true); + events.Repeat(Seconds(20)); + break; + case EVENT_SPECTRAL_BLAST: + DoCastAOE(SPELL_SPECTRAL_BLAST, true); + events.Repeat(Seconds(20), Seconds(25)); + break; + case EVENT_CHECK_TIMER: + if (!_isEnraged && HealthBelowPct(10)) + DoAction(ACTION_ENRAGE); + + if (HealthBelowPct(1)) + { + if (Creature* sathrovarr = instance->GetCreature(DATA_SATHROVARR)) + { + if (sathrovarr->HasAura(SPELL_BANISH)) + { + sathrovarr->CastSpell(sathrovarr, SPELL_TAP_CHECK, true); + break; + } + } + if (_isBanished) + break; + + _isBanished = true; + DoCastSelf(SPELL_BANISH, true); + events.Reset(); + } + events.Repeat(Seconds(1)); + break; + case EVENT_OUTRO_START: + events.Reset(); + events.SetPhase(PHASE_OUTRO); + me->setRegeneratingHealth(false); + me->SetReactState(REACT_PASSIVE); + me->InterruptNonMeleeSpells(true); + me->RemoveAllAttackers(); + me->AttackStop(); me->SetFaction(FACTION_FRIENDLY); - TalkTimer = 1000; + me->RemoveAllAuras(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + events.ScheduleEvent(EVENT_OUTRO_1, Seconds(3)); break; - case 2: - Talk(SAY_GOOD_PLRWIN); - TalkTimer = 10000; - break; - case 3: + case EVENT_OUTRO_1: me->SetDisableGravity(true); - me->GetMotionMaster()->MovePoint(0, FLY_X, FLY_Y, FLY_Z); - TalkTimer = 600000; + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + events.ScheduleEvent(EVENT_OUTRO_2, Seconds(3)); + break; + case EVENT_OUTRO_2: + me->GetMotionMaster()->MovePoint(POINT_OUTRO_1, FlyPos[0]); + break; + case EVENT_OUTRO_3: + Talk(SAY_OUTRO_2); + me->GetMotionMaster()->MovePoint(POINT_OUTRO_2, FlyPos[1], false); break; default: break; } - } - void BadEnding() - { - switch (TalkSequence) - { - case 1: - Talk(SAY_EVIL_ENRAGE); - TalkTimer = 3000; - break; - case 2: - me->SetDisableGravity(true); - me->GetMotionMaster()->MovePoint(0, FLY_X, FLY_Y, FLY_Z); - TalkTimer = 15000; - break; - case 3: - EnterEvadeMode(EVADE_REASON_OTHER); - break; - default: - break; - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetSunwellPlateauAI(creature); - } -}; - -class boss_kalec : public CreatureScript -{ -public: - boss_kalec() : CreatureScript("boss_kalec") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetSunwellPlateauAI(creature); - } - - struct boss_kalecAI : public ScriptedAI - { - InstanceScript* instance; - - uint32 RevitalizeTimer; - uint32 HeroicStrikeTimer; - uint32 YellTimer; - uint32 YellSequence; - - ObjectGuid SathGUID; - - bool isEnraged; // if demon is enraged - - boss_kalecAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - RevitalizeTimer = 5000; - HeroicStrikeTimer = 3000; - YellTimer = 5000; - YellSequence = 0; - - isEnraged = false; - } - - void Reset() override - { - if (Creature* sath = instance->GetCreature(DATA_SATHROVARR)) - SathGUID = sath->GetGUID(); - - Initialize(); - } - - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (done_by->GetGUID() != SathGUID) - damage = 0; - else if (isEnraged) - damage *= 3; - } - - void UpdateAI(uint32 diff) override - { - if (!me->HasAura(AURA_SPECTRAL_INVISIBILITY)) - me->CastSpell(me, AURA_SPECTRAL_INVISIBILITY, true); - - if (!UpdateVictim()) + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - if (YellTimer <= diff) - { - switch (YellSequence) - { - case 0: - Talk(SAY_GOOD_AGGRO); - ++YellSequence; - break; - case 1: - if (HealthBelowPct(50)) - { - Talk(SAY_GOOD_NEAR_DEATH); - ++YellSequence; - } - break; - case 2: - if (HealthBelowPct(10)) - { - Talk(SAY_GOOD_NEAR_DEATH2); - ++YellSequence; - } - break; - default: - break; - } - YellTimer = 5000; - } - - if (RevitalizeTimer <= diff) - { - DoCast(me, SPELL_REVITALIZE); - RevitalizeTimer = 5000; - } else RevitalizeTimer -= diff; - - if (HeroicStrikeTimer <= diff) - { - DoCastVictim(SPELL_HEROIC_STRIKE); - HeroicStrikeTimer = 2000; - } else HeroicStrikeTimer -= diff; - - DoMeleeAttackIfReady(); } - }; + + DoMeleeAttackIfReady(); + } + +private: + bool _isEnraged; + bool _isBanished; }; -class kalecgos_teleporter : public GameObjectScript +struct boss_kalecgos_human : public ScriptedAI { -public: - kalecgos_teleporter() : GameObjectScript("kalecgos_teleporter") { } + boss_kalecgos_human(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct kalecgos_teleporterAI : public GameObjectAI + void Reset() override { - kalecgos_teleporterAI(GameObject* go) : GameObjectAI(go) { } + _events.Reset(); + _events.SetPhase(PHASE_SAY_ONE); - bool GossipHello(Player* player) override + if (Creature* sath = _instance->GetCreature(DATA_SATHROVARR)) + _sathGUID = sath->GetGUID(); + + _events.ScheduleEvent(EVENT_REVITALIZE, Seconds(5)); + _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(3)); + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_GOOD_DEATH); + } + + void DamageTaken(Unit* who, uint32 &damage) override + { + if (who->GetGUID() != _sathGUID) + damage = 0; + + if (HealthBelowPct(75) && _events.IsInPhase(PHASE_SAY_ONE)) { -#if MAX_PLAYERS_IN_SPECTRAL_REALM > 0 - uint8 SpectralPlayers = 0; - Map::PlayerList const& PlayerList = go->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + Talk(SAY_GOOD_NEAR_DEATH_0); + _events.SetPhase(PHASE_SAY_TWO); + } + else if (HealthBelowPct(50) && _events.IsInPhase(PHASE_SAY_TWO)) + { + _events.SetPhase(PHASE_SAY_THREE); + Talk(SAY_GOOD_NEAR_DEATH_1); + } + else if (HealthBelowPct(10) && _events.IsInPhase(PHASE_SAY_THREE)) + { + _events.SetPhase(PHASE_SAY_FOUR); + Talk(SAY_GOOD_NEAR_DEATH_2); + } + } + + 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) { - if (i->GetSource() && i->GetSource()->GetPositionZ() < DEMON_REALM_Z + 5) - ++SpectralPlayers; + case EVENT_REVITALIZE: + DoCastSelf(SPELL_REVITALIZE); + _events.Repeat(Seconds(5)); + break; + case EVENT_HEROIC_STRIKE: + DoCastVictim(SPELL_HEROIC_STRIKE); + _events.Repeat(Seconds(2)); + break; + default: + break; } - if (player->HasAura(AURA_SPECTRAL_EXHAUSTION) || SpectralPlayers >= MAX_PLAYERS_IN_SPECTRAL_REALM) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + } + +private: + InstanceScript* _instance; + EventMap _events; + ObjectGuid _sathGUID; +}; + +class CurseAgonySelector : NonTankTargetSelector +{ +public: + CurseAgonySelector(Unit* source) : NonTankTargetSelector(source, true) { } + + bool operator()(WorldObject* target) const + { + if (Unit* unitTarget = target->ToUnit()) + return !NonTankTargetSelector::operator()(unitTarget) + || unitTarget->HasAura(SPELL_AGONY_CURSE) || unitTarget->HasAura(SPELL_AGONY_CURSE_ALLY) + || !unitTarget->HasAura(SPELL_SPECTRAL_REALM_AURA); + return false; + } +}; + +struct boss_sathrovarr : public BossAI +{ + boss_sathrovarr(Creature* creature) : BossAI(creature, DATA_KALECGOS), _isEnraged(false), _isBanished(false) { } + + void Reset() override + { + _isEnraged = false; + _isBanished = false; + _Reset(); + events.ScheduleEvent(EVENT_SHADOWBOLT, Seconds(7), Seconds(10)); + events.ScheduleEvent(EVENT_AGONY_CURSE, Seconds(20)); + events.ScheduleEvent(EVENT_CORRUPTION_STRIKE, Seconds(13)); + events.ScheduleEvent(EVENT_CHECK_TIMER, Seconds(1)); + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_SATH_AGGRO); + } + + void EnterEvadeMode(EvadeReason why) + { + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + kalecgos->AI()->EnterEvadeMode(why); + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TAP_CHECK_DAMAGE) + { + DoCastSelf(SPELL_TELEPORT_BACK, true); + caster->Kill(me); + } + } + + void DamageTaken(Unit* who, uint32 &damage) override + { + if (damage >= me->GetHealth() && who->GetGUID() != me->GetGUID()) + damage = 0; + } + + void KilledUnit(Unit* target) override + { + if (target->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SATH_SLAY); + else if (Creature* kalecgosHuman = instance->GetCreature(DATA_KALECGOS_HUMAN)) + { + if (kalecgosHuman->GetGUID() == target->GetGUID()) + EnterEvadeMode(EVADE_REASON_OTHER); + } + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_SATH_DEATH); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPECTRAL_REALM_AURA); + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + kalecgos->AI()->DoAction(ACTION_START_OUTRO); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_SHADOWBOLT: + if (roll_chance_i(20)) + Talk(SAY_SATH_SPELL1); + DoCastAOE(SPELL_SHADOW_BOLT); + events.Repeat(Seconds(7), Seconds(10)); + break; + case EVENT_AGONY_CURSE: { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, CurseAgonySelector(me))) + DoCast(target, SPELL_AGONY_CURSE); + else + DoCastVictim(SPELL_AGONY_CURSE); + events.Repeat(Seconds(20)); + break; + } + case EVENT_CORRUPTION_STRIKE: + if (roll_chance_i(20)) + Talk(SAY_SATH_SPELL2); + DoCastVictim(SPELL_CORRUPTION_STRIKE); + events.Repeat(Seconds(13)); + break; + case EVENT_CHECK_TIMER: + { + if (HealthBelowPct(10) && !_isEnraged) + { + _isEnraged = true; + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + kalecgos->AI()->DoAction(ACTION_ENRAGE); + } + + if (HealthBelowPct(1)) + { + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + { + if (kalecgos->HasAura(SPELL_BANISH)) + { + DoCastSelf(SPELL_TAP_CHECK, true); + break; + } + } + if (_isBanished) + break; + + _isBanished = true; + DoCastSelf(SPELL_BANISH, true); + } + events.Repeat(Seconds(1)); + break; + } + default: + break; + } + } + +private: + bool _isEnraged; + bool _isBanished; +}; + +class go_kalecgos_spectral_rift : public GameObjectScript +{ + public: + go_kalecgos_spectral_rift() : GameObjectScript("go_kalecgos_spectral_rift") { } + + struct go_kalecgos_spectral_riftAI : public GameObjectAI + { + go_kalecgos_spectral_riftAI(GameObject* go) : GameObjectAI(go) { } + + bool GossipHello(Player* player) override + { + if (!player->HasAura(SPELL_SPECTRAL_EXHAUSTION)) + player->CastSpell(player, SPELL_SPECTRAL_REALM_TRIGGER, true); return true; } -#endif + }; - player->CastSpell(player, SPELL_TELEPORT_SPECTRAL, true); - return true; + GameObjectAI* GetAI(GameObject* go) const override + { + return GetSunwellPlateauAI(go); } - }; +}; - GameObjectAI* GetAI(GameObject* go) const override +// 46732 - Tap Check +class spell_kalecgos_tap_check : public SpellScript +{ + PrepareSpellScript(spell_kalecgos_tap_check); + + bool Validate(SpellInfo const* spellInfo) override { - return GetSunwellPlateauAI(go); + return ValidateSpellInfo({ uint32(spellInfo->Effects[EFFECT_0].CalcValue()) }); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetCaster(), (uint32)GetSpellInfo()->Effects[EFFECT_0].CalcValue(), true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_kalecgos_tap_check::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; -class boss_sathrovarr : public CreatureScript +class SpectralBlastSelector : NonTankTargetSelector { -public: - boss_sathrovarr() : CreatureScript("boss_sathrovarr") { } + public: + SpectralBlastSelector(Unit* source) : NonTankTargetSelector(source, true) { } - CreatureAI* GetAI(Creature* creature) const override + bool operator()(WorldObject* target) const + { + if (Unit* unitTarget = target->ToUnit()) + return !NonTankTargetSelector::operator()(unitTarget) || + unitTarget->HasAura(SPELL_SPECTRAL_EXHAUSTION) || unitTarget->HasAura(SPELL_SPECTRAL_REALM_AURA); + return false; + } +}; + +// 44869 - Spectral Blast +class spell_kalecgos_spectral_blast : public SpellScript +{ + PrepareSpellScript(spell_kalecgos_spectral_blast); + + bool Validate(SpellInfo const* /*spell*/) override { - return GetSunwellPlateauAI(creature); + return ValidateSpellInfo( + { + SPELL_SPECTRAL_BLAST_EFFECT, + SPELL_SPECTRAL_BLAST_VISUAL, + SPELL_SPECTRAL_REALM_TRIGGER + }); } - struct boss_sathrovarrAI : public ScriptedAI + void FilterTargets(std::list& targets) { - boss_sathrovarrAI(Creature* creature) : ScriptedAI(creature) + targets.remove_if(SpectralBlastSelector(GetCaster())); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Unit* target = GetHitUnit(); + + target->CastSpell(target, SPELL_SPECTRAL_BLAST_EFFECT, true); + caster->CastSpell(target, SPELL_SPECTRAL_BLAST_VISUAL, true); + caster->CastSpell(target, SPELL_SPECTRAL_REALM_TRIGGER, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kalecgos_spectral_blast::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_kalecgos_spectral_blast::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// 44811 - Spectral Realm +class spell_kalecgos_spectral_realm_trigger : public SpellScript +{ + PrepareSpellScript(spell_kalecgos_spectral_realm_trigger); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - Initialize(); - instance = creature->GetInstanceScript(); - } + SPELL_SPECTRAL_REALM_TELEPORT, + SPELL_SPECTRAL_REALM_AURA, + SPELL_SPECTRAL_REALM_2, + SPELL_SPECTRAL_REALM_REACTION + }); + } - void Initialize() + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->CastSpell(target, SPELL_SPECTRAL_REALM_TELEPORT, true); + target->CastSpell(target, SPELL_SPECTRAL_REALM_AURA, true); + target->CastSpell(target, SPELL_SPECTRAL_REALM_2, true); + target->CastSpell(target, SPELL_SPECTRAL_REALM_REACTION, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_kalecgos_spectral_realm_trigger::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 46021 - Spectral Realm +class spell_kalecgos_spectral_realm_aura : public AuraScript +{ + PrepareAuraScript(spell_kalecgos_spectral_realm_aura); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - ShadowBoltTimer = urand(7, 10) * 1000; - AgonyCurseTimer = 20000; - CorruptionStrikeTimer = 13000; - CheckTimer = 1000; - ResetThreat = 1000; - isEnraged = false; - isBanished = false; - } + SPELL_SPECTRAL_REALM_REACTION, + SPELL_TELEPORT_BACK, + SPELL_SPECTRAL_EXHAUSTION + }); + } - InstanceScript* instance; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->RemoveAurasDueToSpell(SPELL_SPECTRAL_REALM_REACTION); + target->CastSpell(target, SPELL_TELEPORT_BACK, true); + target->CastSpell(target, SPELL_SPECTRAL_EXHAUSTION, true); + } - uint32 CorruptionStrikeTimer; - uint32 AgonyCurseTimer; - uint32 ShadowBoltTimer; - uint32 CheckTimer; - uint32 ResetThreat; + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_kalecgos_spectral_realm_aura::OnRemove, EFFECT_0, SPELL_AURA_MOD_INVISIBILITY_DETECT, AURA_EFFECT_HANDLE_REAL); + } +}; - ObjectGuid KalecGUID; - ObjectGuid KalecgosGUID; +// 45032, 45034 - Curse of Boundless Agony +class spell_kalecgos_curse_of_boundless_agony : public AuraScript +{ + PrepareAuraScript(spell_kalecgos_curse_of_boundless_agony); - bool isEnraged; - bool isBanished; - - void Reset() override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - me->SetFullHealth();//dunno why it does not resets health at evade.. - me->setActive(true); - if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) - KalecgosGUID = kalecgos->GetGUID(); - instance->SetBossState(DATA_KALECGOS, NOT_STARTED); - if (KalecGUID) - { - if (Creature* Kalec = ObjectAccessor::GetCreature(*me, KalecGUID)) - Kalec->setDeathState(JUST_DIED); - KalecGUID.Clear(); - } + SPELL_AGONY_CURSE_VISUAL_1, + SPELL_AGONY_CURSE_VISUAL_2, + SPELL_AGONY_CURSE_VISUAL_3, + SPELL_AGONY_CURSE_ALLY + }); + } - Initialize(); - - me->CastSpell(me, AURA_DEMONIC_VISUAL, true); - TeleportAllPlayersBack(); - } - - void EnterCombat(Unit* /*who*/) override - { - if (Creature* Kalec = me->SummonCreature(NPC_KALECGOS_HUMAN, me->GetPositionX() + 10, me->GetPositionY() + 5, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0)) - { - KalecGUID = Kalec->GetGUID(); - me->CombatStart(Kalec); - AddThreat(Kalec, 100.0f); - Kalec->setActive(true); - } - Talk(SAY_SATH_AGGRO); - } - - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (damage >= me->GetHealth() && done_by != me) - damage = 0; - } - - void KilledUnit(Unit* target) override - { - if (target->GetGUID() == KalecGUID) - { - TeleportAllPlayersBack(); - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - { - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->TalkTimer = 1; - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->isFriendly = false; - } - EnterEvadeMode(); - return; - } - Talk(SAY_SATH_SLAY); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_SATH_DEATH); - me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), DRAGON_REALM_Z, me->GetOrientation()); - TeleportAllPlayersBack(); - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - { - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->TalkTimer = 1; - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->isFriendly = true; - } - - instance->SetBossState(DATA_KALECGOS, DONE); - } - - void TeleportAllPlayersBack() - { - Map::PlayerList const& playerList = me->GetMap()->GetPlayers(); - Position const& homePos = me->GetHomePosition(); - for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) - { - Player* player = itr->GetSource(); - if (player->IsInDist(&homePos, 50.0f) && player->GetPositionZ() <= DEMON_REALM_Z + 10.f) - { - player->RemoveAura(AURA_SPECTRAL_REALM); - player->TeleportTo(me->GetMap()->GetId(), player->GetPositionX(), - player->GetPositionY(), DRAGON_REALM_Z + 5, player->GetOrientation()); - } - } - } - - void DoAction(int32 param) override - { - switch (param) - { - case DO_ENRAGE: - isEnraged = true; - me->CastSpell(me, SPELL_ENRAGE, true); - break; - case DO_BANISH: - isBanished = true; - me->CastSpell(me, SPELL_BANISH, true); - break; - } - } - - void UpdateAI(uint32 diff) override - { - if (!me->HasAura(AURA_SPECTRAL_INVISIBILITY)) - me->CastSpell(me, AURA_SPECTRAL_INVISIBILITY, true); - - if (!UpdateVictim()) + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (InstanceScript* instance = GetTarget()->GetInstanceScript()) + if (instance->GetBossState(DATA_KALECGOS) == IN_PROGRESS) return; - if (CheckTimer <= diff) - { - Creature* Kalec = ObjectAccessor::GetCreature(*me, KalecGUID); - if (!Kalec || !Kalec->IsAlive()) - { - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - Kalecgos->AI()->EnterEvadeMode(); - return; - } + Remove(AURA_REMOVE_BY_CANCEL); + } - if (HealthBelowPct(10) && !isEnraged) - { - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - Kalecgos->AI()->DoAction(DO_ENRAGE); - DoAction(DO_ENRAGE); - } + void OnPeriodic(AuraEffect const* aurEff) + { + if (aurEff->GetTickNumber() <= 5) + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_VISUAL_1, true); + else if (aurEff->GetTickNumber() <= 10) + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_VISUAL_2, true); + else + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_VISUAL_3, true); + } - Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID); - if (Kalecgos && !Kalecgos->IsInCombat()) - { - EnterEvadeMode(); - return; - } + void HandleEffectPeriodicUpdate(AuraEffect* aurEff) + { + if (aurEff->GetTickNumber() > 1 && aurEff->GetTickNumber() % 5 == 1) + aurEff->SetAmount(aurEff->GetAmount() * 2); + } - if (!isBanished && HealthBelowPct(1)) - { - if (Kalecgos) - { - if (Kalecgos->HasAura(SPELL_BANISH)) - { - me->DealDamage(me, me->GetHealth()); - return; - } - DoAction(DO_BANISH); - } - else - { - EnterEvadeMode(); - return; - } - } - CheckTimer = 1000; - } else CheckTimer -= diff; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_CANCEL) + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_ALLY, true); + } - if (ResetThreat <= diff) - { - ThreatContainer::StorageType threatlist = me->GetThreatManager().getThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) - { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) - if (unit->GetPositionZ() > me->GetPositionZ() + 5) - me->GetThreatManager().ModifyThreatByPercent(unit, -100); - } - ResetThreat = 1000; - } else ResetThreat -= diff; - - if (ShadowBoltTimer <= diff) - { - if (!(rand32() % 5)) - Talk(SAY_SATH_SPELL1); - DoCast(me, SPELL_SHADOW_BOLT); - ShadowBoltTimer = 7000 + (rand32() % 3000); - } else ShadowBoltTimer -= diff; - - if (AgonyCurseTimer <= diff) - { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1); - if (!target) - target = me->GetVictim(); - DoCast(target, SPELL_AGONY_CURSE); - AgonyCurseTimer = 20000; - } else AgonyCurseTimer -= diff; - - if (CorruptionStrikeTimer <= diff) - { - if (!(rand32() % 5))Talk(SAY_SATH_SPELL2); - DoCastVictim(SPELL_CORRUPTION_STRIKE); - CorruptionStrikeTimer = 13000; - } else CorruptionStrikeTimer -= diff; - - DoMeleeAttackIfReady(); - } - }; + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_kalecgos_curse_of_boundless_agony::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_kalecgos_curse_of_boundless_agony::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_kalecgos_curse_of_boundless_agony::HandleEffectPeriodicUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + AfterEffectRemove += AuraEffectRemoveFn(spell_kalecgos_curse_of_boundless_agony::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } }; void AddSC_boss_kalecgos() { - new boss_kalecgos(); - new boss_sathrovarr(); - new boss_kalec(); - new kalecgos_teleporter(); + RegisterSunwellPlateauCreatureAI(boss_kalecgos); + RegisterSunwellPlateauCreatureAI(boss_sathrovarr); + RegisterSunwellPlateauCreatureAI(boss_kalecgos_human); + new go_kalecgos_spectral_rift(); + RegisterSpellScript(spell_kalecgos_tap_check); + RegisterSpellScript(spell_kalecgos_spectral_blast); + RegisterSpellScript(spell_kalecgos_spectral_realm_trigger); + RegisterAuraScript(spell_kalecgos_spectral_realm_aura); + RegisterAuraScript(spell_kalecgos_curse_of_boundless_agony); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp index 6b3c2b1bcaf..e5df84f8ba1 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp @@ -16,6 +16,7 @@ */ #include "ScriptMgr.h" +#include "AreaBoundary.h" #include "InstanceScript.h" #include "Log.h" #include "Map.h" @@ -60,6 +61,11 @@ ObjectData const creatureData[] = { 0, 0 } // END }; +BossBoundaryData const boundaries = +{ + { DATA_KALECGOS, new BoundaryUnionBoundary(new CircleBoundary(Position(1704.9f, 928.4f), 34.0), new RectangleBoundary(1689.2f, 1713.3f, 762.2f, 1074.8f)) } +}; + class instance_sunwell_plateau : public InstanceMapScript { public: @@ -73,6 +79,7 @@ class instance_sunwell_plateau : public InstanceMapScript SetBossNumber(EncounterCount); LoadDoorData(doorData); LoadObjectData(creatureData, nullptr); + LoadBossBoundaries(boundaries); } Player const* GetPlayerInMap() const diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h index cec43582958..85fbde015fb 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h @@ -111,7 +111,8 @@ enum SWPGameObjectIds GO_BOSS_COLLISION_2 = 188524, GO_FIRE_BARRIER = 188075, GO_MURUS_GATE_1 = 187990, - GO_MURUS_GATE_2 = 188118 + GO_MURUS_GATE_2 = 188118, + GO_SPECTRAL_RIFT = 187055 }; template @@ -120,4 +121,6 @@ AI* GetSunwellPlateauAI(T* obj) return GetInstanceAI(obj, SunwellPlateauScriptName); } +#define RegisterSunwellPlateauCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetSunwellPlateauAI) + #endif // SUNWELL_PLATEAU_H