diff --git a/data/sql/updates/db_world/2023_07_18_01.sql b/data/sql/updates/db_world/2023_07_18_01.sql new file mode 100644 index 000000000..832c72e7d --- /dev/null +++ b/data/sql/updates/db_world/2023_07_18_01.sql @@ -0,0 +1,4 @@ +-- DB update 2023_07_18_00 -> 2023_07_18_01 + +SET @HELP_TEXT := 'Syntax: .wp show $option\nOptions:\non $pathid (or selected creature with loaded path) - Show path\noff - Hide path\ninfo $selected_waypoint - Show info for selected waypoint.'; +UPDATE `command` SET `help` = @HELP_TEXT WHERE `name` = 'wp show'; diff --git a/data/sql/updates/db_world/2023_07_18_02.sql b/data/sql/updates/db_world/2023_07_18_02.sql new file mode 100644 index 000000000..dc03217fc --- /dev/null +++ b/data/sql/updates/db_world/2023_07_18_02.sql @@ -0,0 +1,4 @@ +-- DB update 2023_07_18_01 -> 2023_07_18_02 +-- Darting Hatchling (pet) +UPDATE `creature_template_addon` SET `auras` = 62586 WHERE `entry` = 35396; +UPDATE `creature_template` SET `ScriptName` = 'npc_pet_darting_hatchling' WHERE `entry` = 35396; diff --git a/data/sql/updates/db_world/2023_07_19_00.sql b/data/sql/updates/db_world/2023_07_19_00.sql new file mode 100644 index 000000000..4b589b943 --- /dev/null +++ b/data/sql/updates/db_world/2023_07_19_00.sql @@ -0,0 +1,4 @@ +-- DB update 2023_07_18_02 -> 2023_07_19_00 +-- +UPDATE `spell_proc_event` SET `ppmRate` = 10 WHERE `entry` = 26480; +UPDATE `spell_proc_event` SET `procFlags` = 68 WHERE `entry` = 26480; diff --git a/data/sql/updates/db_world/2023_07_19_01.sql b/data/sql/updates/db_world/2023_07_19_01.sql new file mode 100644 index 000000000..7a37f29bb --- /dev/null +++ b/data/sql/updates/db_world/2023_07_19_01.sql @@ -0,0 +1,3 @@ +-- DB update 2023_07_19_00 -> 2023_07_19_01 +-- +UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 38334; diff --git a/data/sql/updates/db_world/2023_07_19_02.sql b/data/sql/updates/db_world/2023_07_19_02.sql new file mode 100644 index 000000000..9d72b4266 --- /dev/null +++ b/data/sql/updates/db_world/2023_07_19_02.sql @@ -0,0 +1,3 @@ +-- DB update 2023_07_19_01 -> 2023_07_19_02 +-- +UPDATE `spell_proc_event` SET `procPhase` = 1 WHERE `entry` = 23688; diff --git a/data/sql/updates/db_world/2023_07_19_03.sql b/data/sql/updates/db_world/2023_07_19_03.sql new file mode 100644 index 000000000..d04cdf7de --- /dev/null +++ b/data/sql/updates/db_world/2023_07_19_03.sql @@ -0,0 +1,2 @@ +-- DB update 2023_07_19_02 -> 2023_07_19_03 +UPDATE `spell_proc_event` SET `procEx` = 262144 WHERE `entry` = 28200; diff --git a/data/sql/updates/db_world/2023_07_19_04.sql b/data/sql/updates/db_world/2023_07_19_04.sql new file mode 100644 index 000000000..d61f8750f --- /dev/null +++ b/data/sql/updates/db_world/2023_07_19_04.sql @@ -0,0 +1,9 @@ +-- DB update 2023_07_19_03 -> 2023_07_19_04 +-- +UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 37655; +UPDATE `spell_proc_event` SET `Cooldown` = 40000 WHERE `entry` = 37247; +UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 60066; +UPDATE `spell_proc_event` SET `Cooldown` = 2000 WHERE `entry` = 15600; + +UPDATE `spell_proc_event` SET `procEx` = 262144 WHERE `entry` = 37655; +UPDATE `item_template` SET `spellcooldown_2` = -1 WHERE (`entry` = 28823); diff --git a/data/sql/updates/db_world/2023_07_22_00.sql b/data/sql/updates/db_world/2023_07_22_00.sql new file mode 100644 index 000000000..f914b1895 --- /dev/null +++ b/data/sql/updates/db_world/2023_07_22_00.sql @@ -0,0 +1,44 @@ +-- DB update 2023_07_19_04 -> 2023_07_22_00 +UPDATE `creature_template` SET `AIName` = 'SmartAI', `ScriptName`='' WHERE `entry` IN (18445,18369,20812); +UPDATE `gameobject_template` SET `Data2`=300000, `AIName`='SmartGameObjectAI', `ScriptName`='' WHERE `entry` IN (182521,182349,182350); +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (182521,182349,182350) AND `source_type`=1; +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (18445,18369,20812) AND `source_type`=0; +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (1844500,1836900,2081200) AND `source_type`=9; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(182521, 1, 0 ,1,70, 0, 100, 0, 2, 0, 0,0,64,1,0,0,0,0,0,16,0,0,0,0, 0, 0, 0, 'Corkis Prison - On State Changed - Store Targetlist'), +(182521, 1, 1 ,2,61, 0, 100, 0, 0, 0, 0,0,100,1,0,0,0,0,0,19,18445,0,0,0, 0, 0, 0, 'Corkis Prison - Script - Send Targetlist'), +(182521, 1, 2 ,0,61, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,19,18445,0,0,0, 0, 0, 0, 'Corkis Prison - Script - Set Data'), +(18445, 0, 0 ,0,38, 0, 100, 0, 1, 1, 0,0,80,1844500,0,0,0,0,0,1,0,0,0,0, 0, 0, 0, 'Corki - On Data Set - Action list'), +(1844500, 9, 0 ,0,0, 0, 100, 0, 0, 0, 0,0,1,0,0,0,0,0,0,12,1,0,0,0, 0, 0, 0, 'Corki - Action list - Say'), +(1844500, 9, 1 ,0,0, 0, 100, 0, 0, 0, 0,0,33,18444,0,0,0,0,0,12,1,0,0,0, 0, 0, 0, 'Corki - Action list - Kill Credit'), +(1844500, 9, 2 ,0,0, 0, 100, 0, 4000, 4000, 0,0,69,0,0,0,0,0,0,8,0,0,0,-896.082, 8687.346, 170.455, 3.81311, 'Corki - Action listt - Move Forward'), +(1844500, 9, 3 ,0,0, 0, 100, 0, 4000, 4000, 0,0,41,0,0,0,0,0,0,1,0,0,0,0, 0, 0, 0, 'Corki - Action list - Despawn after 5 seconds'), +(182349, 1, 0 ,1,70, 0, 100, 0, 2, 0, 0,0,64,1,0,0,0,0,0,16,0,0,0,0, 0, 0, 0, 'Corkis Prison - On State Changed - Store Targetlist'), +(182349, 1, 1 ,2,61, 0, 100, 0, 0, 0, 0,0,100,1,0,0,0,0,0,19,18369,0,0,0, 0, 0, 0, 'Corkis Prison - Script - Send Targetlist'), +(182349, 1, 2 ,0,61, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,19,18369,0,0,0, 0, 0, 0, 'Corkis Prison - Script - Set Data'), +(18369, 0, 0 ,0,38, 0, 100, 0, 1, 1, 0,0,80,1836900,0,0,0,0,0,1,0,0,0,0, 0, 0, 0, 'Corki - On Data Set - Action list'), +(1836900, 9, 0 , 0, 0, 0, 100, 0, 0, 0, 0,0,1,0,0,0,0,0,0,12,1,0,0,0, 0, 0, 0, 'Corki - Action list - Say'), +(1836900, 9, 1 , 0, 0, 0, 100, 0, 0, 0, 0,0,33,18369,0,0,0,0,0,12,1,0,0,0, 0, 0, 0, 'Corki - Action list - Kill Credit'), +(1836900, 9, 2 ,0,0, 0, 100, 0, 4000, 4000, 0,0,69,0,0,0,0,0,0,8,0,0,0,-2547.684, 6271.637, 14.767, 5.349, 'Corki - Action list - Move Forward'), +(1836900, 9, 3 , 0, 0, 0, 100, 0, 4000, 4000, 0,0,41,0,0,0,0,0,0,1,0,0,0,0, 0, 0, 0, 'Corki - Action list - Despawn after 5 seconds'), +(182350, 1, 0 ,1,70, 0, 100, 0, 2, 0, 0,0,64,1,0,0,0,0,0,16,0,0,0,0, 0, 0, 0, 'Corkis Prison - On State Changed - Store Targetlist'), +(182350, 1, 1 ,2,61, 0, 100, 0, 0, 0, 0,0,100,1,0,0,0,0,0,19,20812,0,0,0, 0, 0, 0, 'Corkis Prison - Script - Send Targetlist'), +(182350, 1, 2 ,0,61, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,19,20812,0,0,0, 0, 0, 0, 'Corkis Prison - Script - Set Data'), +(20812, 0, 0 ,0,38, 0, 100, 0, 1, 1, 0,0,80,2081200,0,0,0,0,0,1,0,0,0,0, 0, 0, 0, 'Corki - On Data Set - Action list'), +(2081200, 9, 0 , 0, 0, 0, 100, 0, 0, 0, 0,0,1,0,0,0,0,0,0,12,1,0,0,0, 0, 0, 0, 'Corki - Action list - Say'), +(2081200, 9, 1 , 0, 0, 0, 100, 0, 0, 0, 0,0,33,20812,0,0,0,0,0,12,1,0,0,0, 0, 0, 0, 'Corki - Action list - Kill Credit'), +(2081200, 9, 2 , 0, 0, 0, 100, 0, 4000, 4000, 0,0,69,0,0,0,0,0,0,8,0,0,0,-1001.022, 8113.366, -95.849, 0.352908, 'Corki - Action list - Move Forward'), +(2081200, 9, 3 , 0, 0, 0, 100, 0, 4000, 4000, 0,0,41,0,0,0,0,0,0,1,0,0,0,0, 0, 0, 0, 'Corki - Action list - Despawn after 5 seconds'); + +UPDATE `creature` SET `position_x`=-2563.89,`position_y`=6288.29,`position_z`=15.295,`orientation`=5.23599 WHERE `guid`=65786 AND `id1`=18369; +UPDATE `creature` SET `position_x`=-918.143,`position_y`=8663.94,`position_z`=172.542,`orientation`=0.523599 WHERE `guid`=65849 AND `id1`=18445; + +DELETE FROM `creature_questender` WHERE `id` IN (18369,18445); +INSERT INTO `creature_questender` (`id`, `quest`) VALUES +(18445, 9954); + +DELETE FROM `creature_queststarter` WHERE `id` IN (18369,18445); +INSERT INTO `creature_queststarter` (`id`, `quest`) VALUES +(18369, 9923), +(18445, 9955); + diff --git a/data/sql/updates/db_world/2023_07_23_00.sql b/data/sql/updates/db_world/2023_07_23_00.sql new file mode 100644 index 000000000..3918be95c --- /dev/null +++ b/data/sql/updates/db_world/2023_07_23_00.sql @@ -0,0 +1,13 @@ +-- DB update 2023_07_22_00 -> 2023_07_23_00 +-- +DELETE FROM `smart_scripts` WHERE `entryorguid` = 17256 AND `source_type` = 0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(17256, 0, 0, 0, 0, 0, 100, 0, 20900, 28200, 12100, 19400, 0, 11, 30510, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - In Combat - Cast \'Shadow Bolt Volley\''), +(17256, 0, 1, 0, 74, 0, 100, 0, 0, 50, 14500, 15000, 30, 11, 30528, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - On Friendly Between 0-50% Health - Cast \'Dark Mending\''), +(17256, 0, 2, 0, 0, 0, 100, 0, 6000, 12000, 17000, 28000, 0, 11, 30530, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - In Combat - Cast \'Fear\''), +(17256, 0, 3, 0, 0, 0, 100, 0, 19650, 63350, 60000, 60000, 0, 11, 30511, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - In Combat - Cast \'Burning Abyssal\''), +(17256, 0, 4, 0, 1, 0, 100, 0, 3600, 3600, 3600, 3600, 0, 11, 30207, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - Out of Combat - Cast \'Shadow Grasp\''), +(17256, 0, 5, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 11, 30531, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - On Just Died - Cast \'Soul Transfer\''), +(17256, 0, 6, 0, 6, 0, 100, 512, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 10, 91254, 17257, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - On Just Died - Do Action on Magtheridon'), +(17256, 0, 7, 0, 4, 0, 100, 512, 0, 0, 0, 0, 0, 34, 10, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - On Aggro - Set Instance Data 10 to 1'), +(17256, 0, 8, 0, 25, 0, 100, 512, 0, 0, 0, 0, 0, 8, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Hellfire Channeler - On Reset - Set Reactstate Defensive'); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index f93a918cf..ecfaeeda4 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -660,7 +660,7 @@ void WorldSession::LogoutPlayer(bool save) guild->HandleMemberLogout(this); ///- Remove pet - _player->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true); + _player->RemovePet(nullptr, PET_SAVE_AS_CURRENT); // pussywizard: on logout remove auras that are removed at map change (before saving to db) // there are some positive auras from boss encounters that can be kept by logging out and logging in after boss is dead, and may be used on next bosses diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index bff534f0a..f94e31d44 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -4547,6 +4547,12 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->MaxAffectedTargets = 1; }); + // Mulgore Hatchling (periodic) + ApplySpellFix({ 62586 }, [](SpellInfo* spellInfo) + { + spellInfo->Effects[EFFECT_0].TriggerSpell = 62585; // Mulgore Hatchling (fear) + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp index b64c25a28..82061fcb8 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp @@ -24,18 +24,18 @@ enum Texts { - SAY_KILL = 0, - SAY_RANDOM = 1, - SAY_DISARMED = 2, - SAY_MIDNIGHT_KILL = 3, - SAY_APPEAR = 4, - SAY_MOUNT = 5, + SAY_KILL = 0, + SAY_RANDOM = 1, + SAY_DISARMED = 2, + SAY_MIDNIGHT_KILL = 3, + SAY_APPEAR = 4, + SAY_MOUNT = 5, - SAY_DEATH = 3, + SAY_DEATH = 3, // Midnight - EMOTE_CALL_ATTUMEN = 0, - EMOTE_MOUNT_UP = 1 + EMOTE_CALL_ATTUMEN = 0, + EMOTE_MOUNT_UP = 1 }; enum Spells @@ -45,7 +45,6 @@ enum Spells SPELL_INTANGIBLE_PRESENCE = 29833, SPELL_SPAWN_SMOKE = 10389, SPELL_CHARGE = 29847, - // Midnight SPELL_KNOCKDOWN = 29711, SPELL_SUMMON_ATTUMEN = 29714, @@ -62,406 +61,357 @@ enum Phases enum Actions { - ACTION_SET_MIDNIGHT_PHASE, + ACTION_SET_MIDNIGHT_PHASE }; -class boss_attumen : public CreatureScript +struct boss_attumen : public BossAI { -public: - boss_attumen() : CreatureScript("boss_attumen") { } - - struct boss_attumenAI : public BossAI + boss_attumen(Creature* creature) : BossAI(creature, DATA_ATTUMEN) { - boss_attumenAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN) - { - Initialize(); - } + Initialize(); + } - void Initialize() + void Initialize() + { + _phase = PHASE_NONE; + } + + void Reset() override + { + Initialize(); + } + + bool CanMeleeHit() + { + return me->GetVictim() && (me->GetVictim()->GetPositionZ() < 53.0f || me->GetVictim()->GetDistance(me->GetHomePosition()) < 50.0f); + } + + void EnterEvadeMode(EvadeReason why) override + { + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + midnight->AI()->EnterEvadeMode(why); + } + me->DespawnOrUnsummon(); + } + + void ScheduleTasks() override + { + scheduler.Schedule(15s, 25s, [this](TaskContext task) + { + DoCastVictim(SPELL_SHADOWCLEAVE); + task.Repeat(15s, 25s); + }); + scheduler.Schedule(25s, 45s, [this](TaskContext task) + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + DoCast(target, SPELL_INTANGIBLE_PRESENCE); + } + + task.Repeat(25s, 45s); + }); + scheduler.Schedule(30s, 1min, [this](TaskContext task) + { + Talk(SAY_RANDOM); + task.Repeat(30s, 1min); + }); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override + { + // Attumen does not die until he mounts Midnight, let health fall to 1 and prevent further damage. + if (damage >= me->GetHealth() && _phase != PHASE_MOUNTED) + { + damage = me->GetHealth() - 1; + } + if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) { _phase = PHASE_NONE; - } - void Reset() override - { - Initialize(); - } - - bool CanMeleeHit() - { - return me->GetVictim() && (me->GetVictim()->GetPositionZ() < 53.0f || me->GetVictim()->GetDistance(me->GetHomePosition()) < 50.0f); - } - - void EnterEvadeMode(EvadeReason why) override - { if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) { - midnight->AI()->EnterEvadeMode(why); - } - - me->DespawnOrUnsummon(); - } - - void ScheduleTasks() override - { - scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task) - { - DoCastVictim(SPELL_SHADOWCLEAVE); - task.Repeat(Seconds(15), Seconds(25)); - }); - - scheduler.Schedule(Seconds(25), Seconds(45), [this](TaskContext task) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - DoCast(target, SPELL_INTANGIBLE_PRESENCE); - } - - task.Repeat(Seconds(25), Seconds(45)); - }); - - scheduler.Schedule(Seconds(30), Seconds(60), [this](TaskContext task) - { - Talk(SAY_RANDOM); - task.Repeat(Seconds(30), Seconds(60)); - }); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override - { - // Attumen does not die until he mounts Midnight, let health fall to 1 and prevent further damage. - if (damage >= me->GetHealth() && _phase != PHASE_MOUNTED) - { - damage = me->GetHealth() - 1; - } - - if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) - { - _phase = PHASE_NONE; - - if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) - { - midnight->AI()->DoCastAOE(SPELL_MOUNT, true); - } + midnight->AI()->DoCastAOE(SPELL_MOUNT, true); } } + } - void KilledUnit(Unit* /*victim*/) override + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) { Talk(SAY_KILL); } + } - void JustSummoned(Creature* summon) override + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED) { - if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED) - { - if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) - { - if (midnight->GetHealth() > me->GetHealth()) - { - summon->SetHealth(midnight->GetHealth()); - } - else - { - summon->SetHealth(me->GetHealth()); - } - - summon->AI()->DoZoneInCombat(); - } - } - - BossAI::JustSummoned(summon); - } - - void IsSummonedBy(WorldObject* summoner) override - { - if (summoner->GetEntry() == NPC_MIDNIGHT) - { - _phase = PHASE_ATTUMEN_ENGAGES; - } - - if (summoner->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN) - { - _phase = PHASE_MOUNTED; - DoCastSelf(SPELL_SPAWN_SMOKE); - - scheduler.Schedule(Seconds(10), Seconds(25), [this](TaskContext task) - { - Unit* target = nullptr; - std::vector target_list; - - for (auto* ref : me->GetThreatMgr().GetUnsortedThreatList()) - { - target = ref->GetVictim(); - if (target && !target->IsWithinDist(me, 8.00f, false) && target->IsWithinDist(me, 25.0f, false)) - target_list.push_back(target); - - target = nullptr; - } - - if (!target_list.empty()) - { - target = Acore::Containers::SelectRandomContainerElement(target_list); - } - - DoCast(target, SPELL_CHARGE); - task.Repeat(Seconds(10), Seconds(25)); - }); - - scheduler.Schedule(Seconds(25), Seconds(35), [this](TaskContext task) - { - DoCastVictim(SPELL_KNOCKDOWN); - task.Repeat(Seconds(25), Seconds(35)); - }); - } - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) { - midnight->KillSelf(); + if (midnight->GetHealth() > me->GetHealth()) + { + summon->SetHealth(midnight->GetHealth()); + } + else + { + summon->SetHealth(me->GetHealth()); + } + summon->AI()->DoZoneInCombat(); } + } + BossAI::JustSummoned(summon); + } - _JustDied(); + void IsSummonedBy(WorldObject* summoner) override + { + if (summoner->GetEntry() == NPC_MIDNIGHT) + { + _phase = PHASE_ATTUMEN_ENGAGES; + } + if (summoner->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN) + { + _phase = PHASE_MOUNTED; + DoCastSelf(SPELL_SPAWN_SMOKE); + scheduler.Schedule(10s, 25s, [this](TaskContext task) + { + Unit* target = nullptr; + std::vector target_list; + for (auto* ref : me->GetThreatMgr().GetUnsortedThreatList()) + { + target = ref->GetVictim(); + if (target && !target->IsWithinDist(me, 8.00f, false) && target->IsWithinDist(me, 25.0f, false)) + { + target_list.push_back(target); + } + target = nullptr; + } + if (!target_list.empty()) + { + target = Acore::Containers::SelectRandomContainerElement(target_list); + } + DoCast(target, SPELL_CHARGE); + task.Repeat(10s, 25s); + }); + scheduler.Schedule(25s, 35s, [this](TaskContext task) + { + DoCastVictim(SPELL_KNOCKDOWN); + task.Repeat(25s, 35s); + }); + } + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + midnight->KillSelf(); + } + _JustDied(); + } + + void UpdateAI(uint32 diff) override + { + if (_phase != PHASE_NONE) + { + if (!UpdateVictim()) + { + return; + } + } + if (!CanMeleeHit()) + { + BossAI::EnterEvadeMode(EvadeReason::EVADE_REASON_BOUNDARY); + } + scheduler.Update(diff, std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override + { + if (spellInfo->Mechanic == MECHANIC_DISARM) + { + Talk(SAY_DISARMED); } - void UpdateAI(uint32 diff) override + if (spellInfo->Id == SPELL_MOUNT) { - if (_phase != PHASE_NONE) + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) { - if (!UpdateVictim()) + _phase = PHASE_NONE; + scheduler.CancelAll(); + midnight->AI()->DoAction(ACTION_SET_MIDNIGHT_PHASE); + midnight->AttackStop(); + midnight->RemoveAllAttackers(); + midnight->SetReactState(REACT_PASSIVE); + midnight->GetMotionMaster()->MoveFollow(me, 2.0f, 0.0f); + midnight->AI()->Talk(EMOTE_MOUNT_UP); + me->AttackStop(); + me->RemoveAllAttackers(); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MoveFollow(midnight, 2.0f, 0.0f); + Talk(SAY_MOUNT); + scheduler.Schedule(1s, [this](TaskContext task) { - return; - } + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + if (me->IsWithinDist2d(midnight, 5.0f)) + { + DoCastAOE(SPELL_SUMMON_ATTUMEN_MOUNTED); + me->DespawnOrUnsummon(1s, 0s); + midnight->SetVisible(false); + } + else + { + midnight->GetMotionMaster()->MoveFollow(me, 2.0f, 0.0f); + me->GetMotionMaster()->MoveFollow(midnight, 2.0f, 0.0f); + task.Repeat(); + } + } + }); } + } + } +private: + uint8 _phase; +}; + +struct boss_midnight : public BossAI +{ + boss_midnight(Creature* creature) : BossAI(creature, DATA_ATTUMEN), _phase(PHASE_NONE) { } + + void Reset() override + { + BossAI::Reset(); + me->SetVisible(true); + me->SetReactState(REACT_DEFENSIVE); + } + + bool CanMeleeHit() + { + return me->GetVictim() && (me->GetVictim()->GetPositionZ() < 53.0f || me->GetVictim()->GetDistance(me->GetHomePosition()) < 50.0f); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override + { + // Midnight never dies, let health fall to 1 and prevent further damage. + if (damage >= me->GetHealth()) + { + damage = me->GetHealth() - 1; + } + + if (_phase == PHASE_NONE && me->HealthBelowPctDamaged(95, damage)) + { + _phase = PHASE_ATTUMEN_ENGAGES; + Talk(EMOTE_CALL_ATTUMEN); + DoCastAOE(SPELL_SUMMON_ATTUMEN); + } + else if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) + { + _phase = PHASE_MOUNTED; + DoCastAOE(SPELL_MOUNT, true); + } + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN) + { + summon->AI()->AttackStart(me->GetVictim()); + summon->AI()->Talk(SAY_APPEAR); + } + BossAI::JustSummoned(summon); + } + + void DoAction(int32 actionId) override + { + if (actionId == ACTION_SET_MIDNIGHT_PHASE) + { + _phase = PHASE_MOUNTED; + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + scheduler.Schedule(15s, 25s, [this](TaskContext task) + { + DoCastVictim(SPELL_KNOCKDOWN); + task.Repeat(15s, 25s); + }); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + me->DespawnOnEvade(10s); + _phase = PHASE_NONE; + } + + void KilledUnit(Unit* /*victim*/) override + { + if (_phase == PHASE_ATTUMEN_ENGAGES) + { + if (Creature* attumen = instance->GetCreature(DATA_ATTUMEN)) + { + Talk(SAY_MIDNIGHT_KILL, attumen); + } + } + } + + void UpdateAI(uint32 diff) override + { + if (_phase != PHASE_MOUNTED) + { + if (!UpdateVictim()) + { + return; + } if (!CanMeleeHit()) { BossAI::EnterEvadeMode(EvadeReason::EVADE_REASON_BOUNDARY); } - - scheduler.Update(diff, - std::bind(&BossAI::DoMeleeAttackIfReady, this)); } - - void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override - { - if (spellInfo->Mechanic == MECHANIC_DISARM) - { - Talk(SAY_DISARMED); - } - - if (spellInfo->Id == SPELL_MOUNT) - { - if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) - { - _phase = PHASE_NONE; - scheduler.CancelAll(); - - midnight->AI()->DoAction(ACTION_SET_MIDNIGHT_PHASE); - midnight->AttackStop(); - midnight->RemoveAllAttackers(); - midnight->SetReactState(REACT_PASSIVE); - midnight->GetMotionMaster()->MoveFollow(me, 2.0f, 0.0f); - midnight->AI()->Talk(EMOTE_MOUNT_UP); - - me->AttackStop(); - me->RemoveAllAttackers(); - me->SetReactState(REACT_PASSIVE); - me->GetMotionMaster()->MoveFollow(midnight, 2.0f, 0.0f); - Talk(SAY_MOUNT); - - scheduler.Schedule(Seconds(1), [this](TaskContext task) - { - if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) - { - if (me->IsWithinDist2d(midnight, 5.0f)) - { - DoCastAOE(SPELL_SUMMON_ATTUMEN_MOUNTED); - me->DespawnOrUnsummon(1s, 0s); - midnight->SetVisible(false); - } - else - { - midnight->GetMotionMaster()->MoveFollow(me, 2.0f, 0.0f); - me->GetMotionMaster()->MoveFollow(midnight, 2.0f, 0.0f); - task.Repeat(); - } - } - }); - } - } - } - - private: - uint8 _phase; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetKarazhanAI(creature); + scheduler.Update(diff, std::bind(&BossAI::DoMeleeAttackIfReady, this)); } + +private: + uint8 _phase; }; -class boss_midnight : public CreatureScript +class spell_midnight_fixate : public AuraScript { -public: - boss_midnight() : CreatureScript("boss_midnight") { } + PrepareAuraScript(spell_midnight_fixate) - struct boss_midnightAI : public BossAI + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - boss_midnightAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN), _phase(PHASE_NONE) { } - - void Reset() override + Unit* target = GetTarget(); + if (Unit* caster = GetCaster()) { - BossAI::Reset(); - me->SetVisible(true); - me->SetReactState(REACT_DEFENSIVE); + caster->TauntApply(target); } - - bool CanMeleeHit() - { - return me->GetVictim() && (me->GetVictim()->GetPositionZ() < 53.0f || me->GetVictim()->GetDistance(me->GetHomePosition()) < 50.0f); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override - { - // Midnight never dies, let health fall to 1 and prevent further damage. - if (damage >= me->GetHealth()) - { - damage = me->GetHealth() - 1; - } - - if (_phase == PHASE_NONE && me->HealthBelowPctDamaged(95, damage)) - { - _phase = PHASE_ATTUMEN_ENGAGES; - Talk(EMOTE_CALL_ATTUMEN); - DoCastAOE(SPELL_SUMMON_ATTUMEN); - } - else if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) - { - _phase = PHASE_MOUNTED; - DoCastAOE(SPELL_MOUNT, true); - } - } - - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN) - { - summon->AI()->AttackStart(me->GetVictim()); - summon->AI()->Talk(SAY_APPEAR); - } - - BossAI::JustSummoned(summon); - } - - void DoAction(int32 actionId) override - { - if (actionId == ACTION_SET_MIDNIGHT_PHASE) - { - _phase = PHASE_MOUNTED; - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - - scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task) - { - DoCastVictim(SPELL_KNOCKDOWN); - task.Repeat(Seconds(15), Seconds(25)); - }); - } - - void EnterEvadeMode(EvadeReason /*why*/) override - { - me->DespawnOnEvade(10s); - _phase = PHASE_NONE; - } - - void KilledUnit(Unit* /*victim*/) override - { - if (_phase == PHASE_ATTUMEN_ENGAGES) - { - if (Creature* attumen = instance->GetCreature(DATA_ATTUMEN)) - { - Talk(SAY_MIDNIGHT_KILL, attumen); - } - } - } - - void UpdateAI(uint32 diff) override - { - if (_phase != PHASE_MOUNTED) - { - if (!UpdateVictim()) - { - return; - } - - if (!CanMeleeHit()) - { - BossAI::EnterEvadeMode(EvadeReason::EVADE_REASON_BOUNDARY); - } - } - - scheduler.Update(diff, - std::bind(&BossAI::DoMeleeAttackIfReady, this)); - } - - private: - uint8 _phase; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetKarazhanAI(creature); } -}; -class spell_midnight_fixate : public SpellScriptLoader -{ -public: - spell_midnight_fixate() : SpellScriptLoader("spell_midnight_fixate") { } - - class spell_midnight_fixate_AuraScript : public AuraScript + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - PrepareAuraScript(spell_midnight_fixate_AuraScript); - - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + Unit* target = GetTarget(); + if (Unit* caster = GetCaster()) { - Unit* target = GetTarget(); - if (Unit* caster = GetCaster()) - caster->TauntApply(target); + caster->TauntFadeOut(target); } + } - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (Unit* caster = GetCaster()) - caster->TauntFadeOut(target); - } - - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_midnight_fixate_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - OnEffectRemove += AuraEffectRemoveFn(spell_midnight_fixate_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_midnight_fixate_AuraScript(); + OnEffectApply += AuraEffectApplyFn(spell_midnight_fixate::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_midnight_fixate::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; void AddSC_boss_attumen() { - new boss_midnight(); - new boss_attumen(); - new spell_midnight_fixate(); + RegisterKarazhanCreatureAI(boss_midnight); + RegisterKarazhanCreatureAI(boss_attumen); + RegisterSpellScript(spell_midnight_fixate); } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp index 708d27836..c78c7fa7c 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp @@ -46,19 +46,15 @@ enum Spells enum Misc { - EVENT_GUEST_TALK = 1, - EVENT_GUEST_TALK2 = 2, - EVENT_SPELL_VANISH = 3, - EVENT_SPELL_GARROTE = 4, - EVENT_SPELL_BLIND = 5, - EVENT_SPELL_GOUGE = 6, - EVENT_SPELL_ENRAGE = 7, - EVENT_KILL_TALK = 8, - ACTIVE_GUEST_COUNT = 4, MAX_GUEST_COUNT = 6 }; +enum Groups +{ + GROUP_PRECOMBAT_TALK = 0 +}; + const Position GuestsPosition[4] = { {-10987.38f, -1883.38f, 81.73f, 1.50f}, @@ -78,6 +74,10 @@ struct boss_moroes : public BossAI boss_moroes(Creature* creature) : BossAI(creature, DATA_MOROES) { _activeGuests = 0; + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } void InitializeAI() override @@ -111,46 +111,83 @@ struct boss_moroes : public BossAI me->SummonCreature(GuestEntries[i], GuestsPosition[summons.size()], TEMPSUMMON_MANUAL_DESPAWN); } } - _events2.Reset(); - _events2.ScheduleEvent(EVENT_GUEST_TALK, 10s); + + scheduler.Schedule(10s, GROUP_PRECOMBAT_TALK, [this](TaskContext context) + { + if(Creature* guest = GetRandomGuest()) + { + guest->AI()->Talk(SAY_GUEST); + } + context.Repeat(5s); + }).Schedule(1min, 2min, GROUP_PRECOMBAT_TALK, [this](TaskContext context) + { + //this was not scheduled in the previous commit + //does this have to be removed? + Talk(SAY_OUT_OF_COMBAT); + context.Repeat(1min, 2min); + }); } void Reset() override { BossAI::Reset(); DoCastSelf(SPELL_DUAL_WIELD, true); + _recentlySpoken = false; + _vanished = false; + + ScheduleHealthCheckEvent(30, [&] { + DoCastSelf(SPELL_FRENZY, true); + }); } void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_SPELL_VANISH, 30s); - events.ScheduleEvent(EVENT_SPELL_BLIND, 20s); - events.ScheduleEvent(EVENT_SPELL_GOUGE, 13s); - events.ScheduleEvent(EVENT_SPELL_ENRAGE, 10min); - _events2.Reset(); me->CallForHelp(20.0f); DoZoneInCombat(); - } + scheduler.CancelGroup(GROUP_PRECOMBAT_TALK); - void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override - { - if (HealthBelowPct(30)) + scheduler.Schedule(30s, [this](TaskContext context) { - DoCastSelf(SPELL_FRENZY, true); - } + scheduler.DelayAll(9s); + _vanished = true; + Talk(SAY_SPECIAL); + DoCastSelf(SPELL_VANISH); + me->SetImmuneToAll(true); + scheduler.Schedule(5s, 7s, [this](TaskContext) + { + me->SetImmuneToAll(false); + DoCastRandomTarget(SPELL_GARROTE, 0, 100.0f, true, true); + DoCastSelf(SPELL_VANISH_TELEPORT); + _vanished = false; + }); + + context.Repeat(30s); + }).Schedule(20s, [this](TaskContext context) + { + DoCastMaxThreat(SPELL_BLIND, 1, 10.0f, true); + context.Repeat(25s, 40s); + }).Schedule(13s, [this](TaskContext context) + { + DoCastVictim(SPELL_GOUGE); + context.Repeat(25s, 40s); + }).Schedule(10min, [this](TaskContext) + { + DoCastSelf(SPELL_BERSERK, true); + }); } void KilledUnit(Unit* victim) override { - if (events.GetNextEventTime(EVENT_KILL_TALK) == 0) + if(!_recentlySpoken && victim->GetTypeId() == TYPEID_PLAYER) { - if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + _recentlySpoken = true; + scheduler.Schedule(5s, [this](TaskContext) { - Talk(SAY_KILL); - events.ScheduleEvent(EVENT_KILL_TALK, 5s); - } + _recentlySpoken = false; + }); } } @@ -176,66 +213,22 @@ struct boss_moroes : public BossAI void UpdateAI(uint32 diff) override { - _events2.Update(diff); - switch (_events2.ExecuteEvent()) - { - case EVENT_GUEST_TALK: - if (Creature* guest = GetRandomGuest()) - { - guest->AI()->Talk(SAY_GUEST); - } - _events2.Repeat(5s); - break; - case EVENT_GUEST_TALK2: - Talk(SAY_OUT_OF_COMBAT); - _events2.Repeat(1min, 2min); - break; - } + scheduler.Update(diff); if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) + if (_vanished == false) { - case EVENT_SPELL_ENRAGE: - DoCastSelf(SPELL_BERSERK, true); - break; - case EVENT_SPELL_BLIND: - if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 10.0f, true)) - { - DoCast(target, SPELL_BLIND); - } - events.Repeat(25s, 40s); - break; - case EVENT_SPELL_GOUGE: - DoCastVictim(SPELL_GOUGE); - events.Repeat(25s, 40s); - return; - case EVENT_SPELL_VANISH: - events.DelayEvents(9s); - events.SetPhase(1); - DoCastSelf(SPELL_VANISH); - events.Repeat(30s); - events.ScheduleEvent(EVENT_SPELL_GARROTE, 5s, 7s); - return; - case EVENT_SPELL_GARROTE: - Talk(SAY_SPECIAL); - DoCastRandomTarget(SPELL_GARROTE, 0, 100.0f, true, true); - DoCastSelf(SPELL_VANISH_TELEPORT); - events.SetPhase(0); - break; - } - if (events.GetPhaseMask() == 0) // Xinef: not in vanish DoMeleeAttackIfReady(); + } } private: EventMap _events2; uint8 _activeGuests; + bool _recentlySpoken; + bool _vanished; }; class spell_moroes_vanish : public SpellScript diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_servant_quarters.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_servant_quarters.cpp index 82c548fa9..0c23ed406 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_servant_quarters.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_servant_quarters.cpp @@ -19,7 +19,7 @@ #include "ScriptedCreature.h" #include "karazhan.h" -enum ServantQuartersSpells +enum Spells { SPELL_SNEAK = 22766, SPELL_ACIDIC_FANG = 29901, @@ -33,41 +33,37 @@ enum ServantQuartersSpells SPELL_RAVAGE = 29906 }; -enum ServantQuertersMisc -{ - EVENT_SPELL_ACIDIC_FANG = 1, - EVENT_SPELL_HYAKISS_WEB = 2, - - EVENT_SPELL_DIVE = 10, - EVENT_SPELL_SONIC_BURST = 11, - EVENT_SPELL_WING_BUFFET = 12, - EVENT_SPELL_FEAR = 13, - - EVENT_SPELL_RAVAGE = 20, - - EVENT_CHECK_VISIBILITY = 30 -}; - struct boss_servant_quarters : public BossAI { boss_servant_quarters(Creature* creature) : BossAI(creature, DATA_SERVANT_QUARTERS) { } void Reset() override { - events.Reset(); + _scheduler.CancelAll(); me->SetVisible(false); me->SetReactState(REACT_PASSIVE); me->SetFaction(FACTION_FRIENDLY); - _events2.Reset(); - _events2.ScheduleEvent(EVENT_CHECK_VISIBILITY, 5s); - + _scheduler.Schedule(5s, [this](TaskContext context) + { + if (instance->GetBossState(DATA_SERVANT_QUARTERS) == DONE) + { + me->SetVisible(true); + me->SetReactState(REACT_AGGRESSIVE); + me->RestoreFaction(); + } + else + { + context.Repeat(5s); + } + }); if (me->GetEntry() == NPC_HYAKISS_THE_LURKER) { DoCastSelf(SPELL_SNEAK, true); } - if (instance->GetData(DATA_SELECTED_RARE) != me->GetEntry()) + { me->DespawnOrUnsummon(1); + } } void JustEngagedWith(Unit* /*who*/) override @@ -75,18 +71,42 @@ struct boss_servant_quarters : public BossAI me->setActive(true); if (me->GetEntry() == NPC_HYAKISS_THE_LURKER) { - events.ScheduleEvent(EVENT_SPELL_ACIDIC_FANG, 5s); - events.ScheduleEvent(EVENT_SPELL_HYAKISS_WEB, 9s); + _scheduler.Schedule(5s, [this](TaskContext context) + { + DoCastVictim(SPELL_ACIDIC_FANG); + context.Repeat(12s, 18s); + }).Schedule(9s, [this](TaskContext context) + { + DoCastRandomTarget(SPELL_HYAKISS_WEB, 0, 30.0f); + context.Repeat(15s); + }); } else if (me->GetEntry() == NPC_SHADIKITH_THE_GLIDER) { - events.ScheduleEvent(EVENT_SPELL_SONIC_BURST, 4s); - events.ScheduleEvent(EVENT_SPELL_WING_BUFFET, 7s); - events.ScheduleEvent(EVENT_SPELL_DIVE, 10s); + _scheduler.Schedule(4s, [this](TaskContext context) + { + DoCastSelf(SPELL_SONIC_BURST); + context.Repeat(12s, 18s); + }).Schedule(7s, [this](TaskContext context) + { + DoCastSelf(SPELL_WING_BUFFET); + context.Repeat(12s, 18s); + }).Schedule(10s, [this](TaskContext context) + { + if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, FarthestTargetSelector(me, 40.0f, false, true))) + { + me->CastSpell(target, SPELL_DIVE); + } + context.Repeat(20s); + }); } else // if (me->GetEntry() == NPC_ROKAD_THE_RAVAGER) { - events.ScheduleEvent(EVENT_SPELL_RAVAGE, 3s); + _scheduler.Schedule(3s, [this](TaskContext context) + { + DoCastVictim(SPELL_RAVAGE); + context.Repeat(10500ms); + }); } } @@ -97,70 +117,28 @@ struct boss_servant_quarters : public BossAI void MovementInform(uint32 type, uint32 point) override { if (type == POINT_MOTION_TYPE && point == EVENT_CHARGE) - events.ScheduleEvent(EVENT_SPELL_FEAR, 0); + { + _scheduler.Schedule(1ms, [this](TaskContext /*context*/) + { + DoCastVictim(SPELL_FEAR); + }); + } } void UpdateAI(uint32 diff) override { - _events2.Update(diff); - switch (_events2.ExecuteEvent()) - { - case EVENT_CHECK_VISIBILITY: - if (instance->GetBossState(DATA_SERVANT_QUARTERS) == DONE) - { - me->SetVisible(true); - me->SetReactState(REACT_AGGRESSIVE); - me->RestoreFaction(); - } - else - _events2.ScheduleEvent(EVENT_CHECK_VISIBILITY, 5s); - break; - } - if (!UpdateVictim()) return; - events.Update(diff); + _scheduler.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - switch (events.ExecuteEvent()) - { - case EVENT_SPELL_ACIDIC_FANG: - me->CastSpell(me->GetVictim(), SPELL_ACIDIC_FANG, false); - events.Repeat(12s, 18s); - break; - case EVENT_SPELL_HYAKISS_WEB: - DoCastRandomTarget(SPELL_HYAKISS_WEB, 0, 30.0f); - events.Repeat(15s); - break; - case EVENT_SPELL_SONIC_BURST: - DoCastSelf(SPELL_SONIC_BURST); - events.Repeat(12s, 18s); - break; - case EVENT_SPELL_WING_BUFFET: - DoCastSelf(SPELL_WING_BUFFET); - events.Repeat(12s, 18s); - break; - case EVENT_SPELL_DIVE: - if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, FarthestTargetSelector(me, 40.0f, false, true))) - me->CastSpell(target, SPELL_DIVE, false); - events.Repeat(20s); - break; - case EVENT_SPELL_FEAR: - DoCastVictim(SPELL_FEAR); - break; - case EVENT_SPELL_RAVAGE: - me->CastSpell(me->GetVictim(), SPELL_RAVAGE, false); - events.ScheduleEvent(EVENT_SPELL_RAVAGE, 10500); - break; - } - DoMeleeAttackIfReady(); } private: - EventMap _events2; + TaskScheduler _scheduler; }; void AddSC_boss_servant_quarters() diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp index 063b3937c..50fab68d7 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp @@ -20,6 +20,7 @@ #include "ScriptedCreature.h" #include "SpellInfo.h" #include "karazhan.h" +#include "TaskScheduler.h" enum ShadeOfAran { @@ -58,9 +59,9 @@ enum ShadeOfAran SPELL_SHADOW_PYRO = 29978, //Creatures - CREATURE_WATER_ELEMENTAL = 17167, - CREATURE_SHADOW_OF_ARAN = 18254, - CREATURE_ARAN_BLIZZARD = 17161, + NPC_WATER_ELEMENTAL = 17167, + NPC_SHADOW_OF_ARAN = 18254, + NPC_ARAN_BLIZZARD = 17161, }; enum SuperSpell @@ -70,560 +71,458 @@ enum SuperSpell SUPER_AE, }; -class boss_shade_of_aran : public CreatureScript +enum Groups { -public: - boss_shade_of_aran() : CreatureScript("boss_shade_of_aran") { } + GROUP_FLAMEWREATH = 0, + GROUP_DRINKING = 1 +}; - struct boss_aranAI : public BossAI +Position const elementalPos[4] = +{ + {-11168.1f, -1939.29f, 232.092f, 1.46f}, + {-11138.2f, -1915.38f, 232.092f, 3.00f}, + {-11161.7f, -1885.36f, 232.092f, 4.59f}, + {-11192.4f, -1909.36f, 232.092f, 6.19f} +}; + +struct boss_shade_of_aran : public BossAI +{ + boss_shade_of_aran(Creature* creature) : BossAI(creature, DATA_ARAN) { - boss_aranAI(Creature* creature) : BossAI(creature, DATA_ARAN) + scheduler.SetValidator([this] { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } + + uint8 LastSuperSpell; + + ObjectGuid FlameWreathTarget[3]; + float FWTargPosX[3]; + float FWTargPosY[3]; + + uint32 CurrentNormalSpell; + + bool Drinking; + + void Reset() override + { + BossAI::Reset(); + drinkScheduler.CancelAll(); + LastSuperSpell = rand() % 3; + + for (uint8 i = 0; i < 3; ++i) + FlameWreathTarget[i].Clear(); + + CurrentNormalSpell = 0; + + _arcaneCooledDown = true; + _fireCooledDown = true; + _frostCooledDown = true; + + Drinking = false; + + // Not in progress + instance->SetData(DATA_ARAN, NOT_STARTED); + + if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR))) + { + libraryDoor->SetGoState(GO_STATE_ACTIVE); + libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); } - uint32 SecondarySpellTimer; - uint32 NormalCastTimer; - uint32 SuperCastTimer; - uint32 BerserkTimer; - uint32 CloseDoorTimer; // Don't close the door right on aggro in case some people are still entering. + ScheduleHealthCheckEvent(40, [&]{ + Talk(SAY_ELEMENTALS); - uint8 LastSuperSpell; - - uint32 FlameWreathTimer; - uint32 FlameWreathCheckTime; - ObjectGuid FlameWreathTarget[3]; - float FWTargPosX[3]; - float FWTargPosY[3]; - - uint32 CurrentNormalSpell; - uint32 ArcaneCooldown; - uint32 FireCooldown; - uint32 FrostCooldown; - - uint32 DrinkInterruptTimer; - - bool ElementalsSpawned; - bool Drinking; - bool DrinkInturrupted; - void Reset() override - { - SecondarySpellTimer = 5000; - NormalCastTimer = 0; - SuperCastTimer = 35000; - BerserkTimer = 720000; - CloseDoorTimer = 15000; - - LastSuperSpell = rand() % 3; - - FlameWreathTimer = 0; - FlameWreathCheckTime = 0; - - for (uint8 i = 0; i < 3; ++i) - FlameWreathTarget[i].Clear(); - - CurrentNormalSpell = 0; - ArcaneCooldown = 0; - FireCooldown = 0; - FrostCooldown = 0; - - DrinkInterruptTimer = 10000; - - ElementalsSpawned = false; - Drinking = false; - DrinkInturrupted = false; - - // Not in progress - instance->SetData(DATA_ARAN, NOT_STARTED); - - if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR))) + for(Position pos : elementalPos) { - libraryDoor->SetGoState(GO_STATE_ACTIVE); - libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + if(Creature* elemental = me->SummonCreature(NPC_WATER_ELEMENTAL, pos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000)) + { + if(Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true)) + { + DoStartNoMovement(target); + elemental->SetInCombatWithZone(); + elemental->CombatStart(target); + } + } } - } + }); + } - void KilledUnit(Unit* /*victim*/) override + void KilledUnit(Unit* /*victim*/) override + { + Talk(SAY_KILL); + } + + void TriggerArcaneCooldown() + { + scheduler.Schedule(5s, [this](TaskContext) { - Talk(SAY_KILL); - } + _arcaneCooledDown = true; + }); + } - void JustDied(Unit* /*killer*/) override + void TriggerFireCooldown() + { + scheduler.Schedule(5s, [this](TaskContext) { - Talk(SAY_DEATH); + _fireCooledDown = true; + }); + } - instance->SetData(DATA_ARAN, DONE); - - if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR))) - { - libraryDoor->SetGoState(GO_STATE_ACTIVE); - libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - } - - void JustEngagedWith(Unit* /*who*/) override + void TriggerFrostCooldown() + { + scheduler.Schedule(5s, [this](TaskContext) { - Talk(SAY_AGGRO); + _frostCooledDown = true; + }); + } - instance->SetData(DATA_ARAN, IN_PROGRESS); + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + instance->SetData(DATA_ARAN, DONE); + + if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR))) + { + libraryDoor->SetGoState(GO_STATE_ACTIVE); + libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + } + } + + void JustEngagedWith(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + + instance->SetData(DATA_ARAN, IN_PROGRESS); + + DoZoneInCombat(); + + //handle timed closing door + scheduler.Schedule(15s, [this](TaskContext) + { if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR))) { libraryDoor->SetGoState(GO_STATE_READY); libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); } - - DoZoneInCombat(); - } - - void FlameWreathEffect() + }).Schedule(1ms, [this](TaskContext context) { - std::vector targets; - ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); - - if (t_list.empty()) - return; - - //store the threat list in a different container - for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) + if (!me->IsNonMeleeSpellCast(false)) { - Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - //only on alive players - if (target && target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER) - targets.push_back(target); - } + Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true); + if (!target) + return; - //cut down to size if we have more than 3 targets - while (targets.size() > 3) - targets.erase(targets.begin() + rand() % targets.size()); + uint32 Spells[3]; + uint8 AvailableSpells = 0; - uint32 i = 0; - for (std::vector::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) - { - if (*itr) + //Check for what spells are not on cooldown + if (_arcaneCooledDown) { - FlameWreathTarget[i] = (*itr)->GetGUID(); - FWTargPosX[i] = (*itr)->GetPositionX(); - FWTargPosY[i] = (*itr)->GetPositionY(); - DoCast((*itr), SPELL_FLAME_WREATH, true); - ++i; + Spells[AvailableSpells] = SPELL_ARCMISSLE; + ++AvailableSpells; + } + if (_fireCooledDown) + { + Spells[AvailableSpells] = SPELL_FIREBALL; + ++AvailableSpells; + } + if (_frostCooledDown) + { + Spells[AvailableSpells] = SPELL_FROSTBOLT; + ++AvailableSpells; + } + + //If no available spells wait 1 second and try again + if (AvailableSpells) + { + CurrentNormalSpell = Spells[rand() % AvailableSpells]; + DoCast(target, CurrentNormalSpell); } } - } - - void UpdateAI(uint32 diff) override + context.Repeat(10s); + }).Schedule(5s, [this](TaskContext context) { - if (!UpdateVictim()) - return; - - if (CloseDoorTimer) + switch (urand(0, 1)) { - if (CloseDoorTimer <= diff) - { - if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR))) + case 0: + DoCastSelf(SPELL_AOE_CS); + break; + case 1: + DoCastRandomTarget(SPELL_CHAINSOFICE); + break; + } + context.Repeat(5s, 20s); + }).Schedule(35s, [this](TaskContext context) + { + uint8 Available[2]; + + switch (LastSuperSpell) + { + case SUPER_AE: + Available[0] = SUPER_FLAME; + Available[1] = SUPER_BLIZZARD; + break; + case SUPER_FLAME: + Available[0] = SUPER_AE; + Available[1] = SUPER_BLIZZARD; + break; + case SUPER_BLIZZARD: + Available[0] = SUPER_FLAME; + Available[1] = SUPER_AE; + break; + } + + LastSuperSpell = Available[urand(0, 1)]; + + switch (LastSuperSpell) + { + case SUPER_AE: + Talk(SAY_EXPLOSION); + + DoCastSelf(SPELL_BLINK_CENTER, true); + DoCastSelf(SPELL_PLAYERPULL, true); + DoCastSelf(SPELL_MASSSLOW, true); + DoCastSelf(SPELL_AEXPLOSION, false); + break; + + case SUPER_FLAME: + Talk(SAY_FLAMEWREATH); + + scheduler.Schedule(20s, GROUP_FLAMEWREATH, [this](TaskContext) { - libraryDoor->SetGoState(GO_STATE_READY); - libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - CloseDoorTimer = 0; - } - else - CloseDoorTimer -= diff; - } - - //Cooldowns for casts - if (ArcaneCooldown) - { - if (ArcaneCooldown >= diff) - ArcaneCooldown -= diff; - else - ArcaneCooldown = 0; - } - - if (FireCooldown) - { - if (FireCooldown >= diff) - FireCooldown -= diff; - else - FireCooldown = 0; - } - - if (FrostCooldown) - { - if (FrostCooldown >= diff) - FrostCooldown -= diff; - else - FrostCooldown = 0; - } - - if (!Drinking && me->GetMaxPower(POWER_MANA) && (me->GetPower(POWER_MANA) * 100 / me->GetMaxPower(POWER_MANA)) < 20) - { - Drinking = true; - me->InterruptNonMeleeSpells(false); - - Talk(SAY_DRINK); - - if (!DrinkInturrupted) - { - DoCast(me, SPELL_MASS_POLY, true); - DoCast(me, SPELL_CONJURE, false); - DoCast(me, SPELL_DRINK, false); - me->SetStandState(UNIT_STAND_STATE_SIT); - DrinkInterruptTimer = 10000; - } - } - - //Drink Interrupt - if (Drinking && DrinkInturrupted) - { - Drinking = false; - me->RemoveAurasDueToSpell(SPELL_DRINK); - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000); - DoCast(me, SPELL_POTION, false); - } - - //Drink Interrupt Timer - if (Drinking && !DrinkInturrupted) - { - if (DrinkInterruptTimer >= diff) - DrinkInterruptTimer -= diff; - else - { - me->SetStandState(UNIT_STAND_STATE_STAND); - DoCast(me, SPELL_POTION, true); - DoCast(me, SPELL_AOE_PYROBLAST, false); - DrinkInturrupted = true; - Drinking = false; - } - } - - //Don't execute any more code if we are drinking - if (Drinking) - return; - - //Normal casts - if (NormalCastTimer <= diff) - { - if (!me->IsNonMeleeSpellCast(false)) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true); - if (!target) - return; - - uint32 Spells[3]; - uint8 AvailableSpells = 0; - - //Check for what spells are not on cooldown - if (!ArcaneCooldown) + scheduler.CancelGroup(GROUP_FLAMEWREATH); + }).Schedule(500ms, GROUP_FLAMEWREATH, [this](TaskContext context) { - Spells[AvailableSpells] = SPELL_ARCMISSLE; - ++AvailableSpells; - } - if (!FireCooldown) - { - Spells[AvailableSpells] = SPELL_FIREBALL; - ++AvailableSpells; - } - if (!FrostCooldown) - { - Spells[AvailableSpells] = SPELL_FROSTBOLT; - ++AvailableSpells; - } - - //If no available spells wait 1 second and try again - if (AvailableSpells) - { - CurrentNormalSpell = Spells[rand() % AvailableSpells]; - DoCast(target, CurrentNormalSpell); - } - } - NormalCastTimer = 1000; - } - else - NormalCastTimer -= diff; - - if (SecondarySpellTimer <= diff) - { - switch (urand(0, 1)) - { - case 0: - DoCast(me, SPELL_AOE_CS); - break; - case 1: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - DoCast(target, SPELL_CHAINSOFICE); - break; - } - SecondarySpellTimer = urand(5000, 20000); - } - else - SecondarySpellTimer -= diff; - - if (SuperCastTimer <= diff) - { - uint8 Available[2]; - - switch (LastSuperSpell) - { - case SUPER_AE: - Available[0] = SUPER_FLAME; - Available[1] = SUPER_BLIZZARD; - break; - case SUPER_FLAME: - Available[0] = SUPER_AE; - Available[1] = SUPER_BLIZZARD; - break; - case SUPER_BLIZZARD: - Available[0] = SUPER_FLAME; - Available[1] = SUPER_AE; - break; - } - - LastSuperSpell = Available[urand(0, 1)]; - - switch (LastSuperSpell) - { - case SUPER_AE: - Talk(SAY_EXPLOSION); - - DoCast(me, SPELL_BLINK_CENTER, true); - DoCast(me, SPELL_PLAYERPULL, true); - DoCast(me, SPELL_MASSSLOW, true); - DoCast(me, SPELL_AEXPLOSION, false); - break; - - case SUPER_FLAME: - Talk(SAY_FLAMEWREATH); - - FlameWreathTimer = 20000; - FlameWreathCheckTime = 500; - - FlameWreathTarget[0].Clear(); - FlameWreathTarget[1].Clear(); - FlameWreathTarget[2].Clear(); - - FlameWreathEffect(); - break; - - case SUPER_BLIZZARD: - Talk(SAY_BLIZZARD); - - if (Creature* pSpawn = me->SummonCreature(CREATURE_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000)) + for (uint8 i = 0; i < 3; ++i) { - pSpawn->SetFaction(me->GetFaction()); - pSpawn->CastSpell(pSpawn, SPELL_CIRCULAR_BLIZZARD, false); + if (!FlameWreathTarget[i]) + continue; + + Unit* unit = ObjectAccessor::GetUnit(*me, FlameWreathTarget[i]); + if (unit && !unit->IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3)) + { + unit->CastSpell(unit, 20476, true, 0, 0, me->GetGUID()); + unit->CastSpell(unit, 11027, true); + FlameWreathTarget[i].Clear(); + } } - break; - } + context.Repeat(500ms); + }); - SuperCastTimer = urand(35000, 40000); - } - else - SuperCastTimer -= diff; + FlameWreathTarget[0].Clear(); + FlameWreathTarget[1].Clear(); + FlameWreathTarget[2].Clear(); - if (!ElementalsSpawned && HealthBelowPct(40)) - { - ElementalsSpawned = true; + FlameWreathEffect(); + break; - Creature* ElementalOne = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11168.1f, -1939.29f, 232.092f, 1.46f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000); - Creature* ElementalTwo = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11138.2f, -1915.38f, 232.092f, 3.00f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000); - Creature* ElementalThree = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11161.7f, -1885.36f, 232.092f, 4.59f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000); - Creature* ElementalFour = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11192.4f, -1909.36f, 232.092f, 6.19f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000); + case SUPER_BLIZZARD: + Talk(SAY_BLIZZARD); - if (ElementalOne) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true); - if (!target) - return; - - DoStartNoMovement(target); - ElementalOne->SetInCombatWithZone(); - ElementalOne->CombatStart(target); - ElementalOne->SetFaction(me->GetFaction()); - ElementalOne->SetUnitMovementFlags(MOVEMENTFLAG_ROOT); - ElementalOne->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0); - } - - if (ElementalTwo) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true); - if (!target) - return; - - DoStartNoMovement(target); - ElementalTwo->SetInCombatWithZone(); - ElementalTwo->CombatStart(target); - ElementalTwo->SetFaction(me->GetFaction()); - ElementalTwo->SetUnitMovementFlags(MOVEMENTFLAG_ROOT); - ElementalTwo->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0); - } - - if (ElementalThree) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true); - if (!target) - return; - - DoStartNoMovement(target); - ElementalThree->SetInCombatWithZone(); - ElementalThree->CombatStart(target); - ElementalThree->SetFaction(me->GetFaction()); - ElementalThree->SetUnitMovementFlags(MOVEMENTFLAG_ROOT); - ElementalThree->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0); - } - - if (ElementalFour) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true); - if (!target) - return; - - DoStartNoMovement(target); - ElementalFour->SetInCombatWithZone(); - ElementalFour->CombatStart(target); - ElementalFour->SetFaction(me->GetFaction()); - ElementalFour->SetUnitMovementFlags(MOVEMENTFLAG_ROOT); - ElementalFour->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0); - } - - Talk(SAY_ELEMENTALS); - } - - if (BerserkTimer <= diff) - { - for (uint32 i = 0; i < 5; ++i) - { - if (Creature* unit = me->SummonCreature(CREATURE_SHADOW_OF_ARAN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000)) + if (Creature* pSpawn = me->SummonCreature(NPC_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000)) { - unit->Attack(me->GetVictim(), true); - unit->SetFaction(me->GetFaction()); + pSpawn->SetFaction(me->GetFaction()); + pSpawn->CastSpell(me, SPELL_CIRCULAR_BLIZZARD, false); } - } - - Talk(SAY_TIMEOVER); - - BerserkTimer = 60000; + break; } - else - BerserkTimer -= diff; - - //Flame Wreath check - if (FlameWreathTimer) + context.Repeat(35s, 40s); + }).Schedule(12min, [this](TaskContext context) + { + for (uint32 i = 0; i < 5; ++i) { - if (FlameWreathTimer >= diff) - FlameWreathTimer -= diff; - else - FlameWreathTimer = 0; - - if (FlameWreathCheckTime <= diff) + if (Creature* unit = me->SummonCreature(NPC_SHADOW_OF_ARAN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000)) { - for (uint8 i = 0; i < 3; ++i) - { - if (!FlameWreathTarget[i]) - continue; - - Unit* unit = ObjectAccessor::GetUnit(*me, FlameWreathTarget[i]); - if (unit && !unit->IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3)) - { - unit->CastSpell(unit, 20476, true, 0, 0, me->GetGUID()); - unit->CastSpell(unit, 11027, true); - FlameWreathTarget[i].Clear(); - } - } - FlameWreathCheckTime = 500; + unit->Attack(me->GetVictim(), true); + unit->SetFaction(me->GetFaction()); } - else - FlameWreathCheckTime -= diff; } - if (ArcaneCooldown && FireCooldown && FrostCooldown) - DoMeleeAttackIfReady(); + Talk(SAY_TIMEOVER); + + context.Repeat(1min); + }); + } + + void FlameWreathEffect() + { + std::vector targets; + ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); + + if (t_list.empty()) + return; + + //store the threat list in a different container + for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) + { + Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); + //only on alive players + if (target && target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER) + targets.push_back(target); } - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + //cut down to size if we have more than 3 targets + while (targets.size() > 3) + targets.erase(targets.begin() + rand() % targets.size()); + + uint32 i = 0; + for (std::vector::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) { - if (!DrinkInturrupted && Drinking && damage) - DrinkInturrupted = true; + if (*itr) + { + FlameWreathTarget[i] = (*itr)->GetGUID(); + FWTargPosX[i] = (*itr)->GetPositionX(); + FWTargPosY[i] = (*itr)->GetPositionY(); + DoCast((*itr), SPELL_FLAME_WREATH, true); + ++i; + } } + } - void SpellHit(Unit* /*pAttacker*/, SpellInfo const* Spell) override + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + drinkScheduler.Update(diff); + + if (!UpdateVictim()) + return; + + if (!Drinking && me->GetMaxPower(POWER_MANA) && (me->GetPower(POWER_MANA) * 100 / me->GetMaxPower(POWER_MANA)) < 20) { - //We only care about interrupt effects and only if they are durring a spell currently being cast - if ((Spell->Effects[0].Effect != SPELL_EFFECT_INTERRUPT_CAST && - Spell->Effects[1].Effect != SPELL_EFFECT_INTERRUPT_CAST && - Spell->Effects[2].Effect != SPELL_EFFECT_INTERRUPT_CAST) || !me->IsNonMeleeSpellCast(false)) - return; - - //Interrupt effect + Drinking = true; me->InterruptNonMeleeSpells(false); - //Normally we would set the cooldown equal to the spell duration - //but we do not have access to the DurationStore + Talk(SAY_DRINK); - switch (CurrentNormalSpell) + scheduler.DelayAll(10s); + DoCastSelf(SPELL_MASS_POLY, true); + DoCastSelf(SPELL_CONJURE, false); + me->SetReactState(REACT_PASSIVE); + me->SetStandState(UNIT_STAND_STATE_SIT); + DoCastSelf(SPELL_DRINK, true); + _currentHealth = me->GetHealth(); + drinkScheduler.Schedule(500ms, GROUP_DRINKING, [this](TaskContext context) { - case SPELL_ARCMISSLE: - ArcaneCooldown = 5000; - break; - case SPELL_FIREBALL: - FireCooldown = 5000; - break; - case SPELL_FROSTBOLT: - FrostCooldown = 5000; - break; + //check for damage to interrupt + if(CheckDamageDuringDrinking(_currentHealth)) + { + Drinking = false; + me->RemoveAurasDueToSpell(SPELL_DRINK); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); + me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000); + DoCastSelf(SPELL_POTION, false); + DoCastSelf(SPELL_AOE_PYROBLAST, false); + drinkScheduler.CancelGroup(GROUP_DRINKING); + } else { + context.Repeat(500ms); + } + }).Schedule(10s, GROUP_DRINKING, [this](TaskContext) + { + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); + me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000); + DoCastSelf(SPELL_POTION, true); + DoCastSelf(SPELL_AOE_PYROBLAST, false); + + Drinking = false; + drinkScheduler.CancelGroup(GROUP_DRINKING); + }); + } + + if (_arcaneCooledDown && _fireCooledDown && _frostCooledDown && !Drinking) + DoMeleeAttackIfReady(); + } + + bool CheckDamageDuringDrinking(uint32 oldHealth) + { + if (Drinking) + { + if (me->GetHealth() < oldHealth) + { + return true; } } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetKarazhanAI(creature); + return false; } + + void SpellHit(Unit* /*pAttacker*/, SpellInfo const* Spell) override + { + //We only care about interrupt effects and only if they are durring a spell currently being cast + if ((Spell->Effects[0].Effect != SPELL_EFFECT_INTERRUPT_CAST && + Spell->Effects[1].Effect != SPELL_EFFECT_INTERRUPT_CAST && + Spell->Effects[2].Effect != SPELL_EFFECT_INTERRUPT_CAST) || !me->IsNonMeleeSpellCast(false)) + return; + + //Interrupt effect + me->InterruptNonMeleeSpells(false); + + //Normally we would set the cooldown equal to the spell duration + //but we do not have access to the DurationStore + + switch (CurrentNormalSpell) + { + case SPELL_ARCMISSLE: + TriggerArcaneCooldown(); + break; + case SPELL_FIREBALL: + TriggerFireCooldown(); + break; + case SPELL_FROSTBOLT: + TriggerFrostCooldown(); + break; + } + } +private: + TaskScheduler drinkScheduler; + + bool _arcaneCooledDown; + bool _fireCooledDown; + bool _frostCooledDown; + uint32 _currentHealth; }; -class npc_aran_elemental : public CreatureScript +struct npc_aran_elemental : public ScriptedAI { -public: - npc_aran_elemental() : CreatureScript("npc_aran_elemental") { } - - CreatureAI* GetAI(Creature* creature) const override + npc_aran_elemental(Creature* creature) : ScriptedAI(creature) { - return new water_elementalAI(creature); + SetCombatMovement(false); + _scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } - struct water_elementalAI : public ScriptedAI + void Reset() override { - water_elementalAI(Creature* creature) : ScriptedAI(creature) + _scheduler.CancelAll(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.Schedule(2s, [this](TaskContext context) { - } + DoCastVictim(SPELL_WATERBOLT); + context.Repeat(2s); + }); + } - uint32 CastTimer; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void Reset() override - { - CastTimer = 2000 + (rand() % 3000); - } - - void JustEngagedWith(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (CastTimer <= diff) - { - DoCastVictim(SPELL_WATERBOLT); - CastTimer = urand(2000, 5000); - } - else - CastTimer -= diff; - } - }; + _scheduler.Update(diff); + } +private: + TaskScheduler _scheduler; }; void AddSC_boss_shade_of_aran() { - new boss_shade_of_aran(); - new npc_aran_elemental(); + RegisterKarazhanCreatureAI(boss_shade_of_aran); + RegisterKarazhanCreatureAI(npc_aran_elemental); } diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp index df615ef0d..306c6c569 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp @@ -242,7 +242,11 @@ struct boss_eye_of_cthun : public BossAI { scheduler.Schedule(5s, [this](TaskContext task) { - DoCastRandomTarget(SPELL_GREEN_BEAM); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) + { + DoCast(target, SPELL_GREEN_BEAM); + DarkGlareAngle = me->GetAngle(target); //keep as the location dark glare will be at + } task.SetGroup(GROUP_BEAM_PHASE); task.Repeat(3s); @@ -284,21 +288,18 @@ struct boss_eye_of_cthun : public BossAI scheduler.Schedule(1s, [this](TaskContext /*task*/) { - //Select random target for dark beam to start on - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) - { - //Face our target - DarkGlareAngle = me->GetAngle(target); - DarkGlareTick = 0; - ClockWise = RAND(true, false); + //Select last target that had a beam cast on it + //Face our target - //Add red coloration to C'thun - DoCast(me, SPELL_RED_COLORATION, true); + DarkGlareTick = 0; + ClockWise = RAND(true, false); - me->StopMoving(); - me->SetFacingToObject(target); - me->SetOrientation(DarkGlareAngle); - } + //Add red coloration to C'thun + DoCast(me, SPELL_RED_COLORATION, true); + + me->StopMoving(); + me->SetOrientation(DarkGlareAngle); + me->SetFacingTo(DarkGlareAngle); scheduler.Schedule(3s, [this](TaskContext tasker) { diff --git a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp index d537d9a50..45db5585b 100644 --- a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp @@ -59,7 +59,13 @@ enum Spells enum Groups { - GROUP_INTERRUPT_CHECK = 0 + GROUP_INTERRUPT_CHECK = 0, + GROUP_EARLY_RELEASE_CHECK = 1 +}; + +enum Actions +{ + ACTION_INCREASE_HELLFIRE_CHANNELER_DEATH_COUNT = 1 }; class DealDebrisDamage : public BasicEvent @@ -92,8 +98,10 @@ struct boss_magtheridon : public BossAI void Reset() override { BossAI::Reset(); + _channelersKilled = 0; _currentPhase = 0; _recentlySpoken = false; + _magReleased = false; _interruptScheduler.CancelAll(); scheduler.Schedule(90s, [this](TaskContext context) { @@ -154,60 +162,86 @@ struct boss_magtheridon : public BossAI BossAI::JustDied(killer); } + void ScheduleCombatEvents() + { + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); + me->SetReactState(REACT_AGGRESSIVE); + instance->SetData(DATA_ACTIVATE_CUBES, 1); + me->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE); + + scheduler.Schedule(9s, [this](TaskContext context) + { + DoCastVictim(SPELL_CLEAVE); + context.Repeat(1200ms, 16300ms); + }).Schedule(20s, [this](TaskContext context) + { + me->CastCustomSpell(SPELL_BLAZE, SPELLVALUE_MAX_TARGETS, 1); + context.Repeat(11s, 39s); + }).Schedule(40s, [this](TaskContext context) + { + DoCastSelf(SPELL_QUAKE); //needs fixes with custom spell + scheduler.Schedule(7s, [this](TaskContext /*context*/) + { + DoCastSelf(SPELL_BLAST_NOVA); + + _interruptScheduler.Schedule(50ms, GROUP_INTERRUPT_CHECK, [this](TaskContext context) + { + if (me->GetAuraCount(SPELL_SHADOW_GRASP_VISUAL) == 5) + { + Talk(SAY_BANISH); + me->InterruptNonMeleeSpells(true); + scheduler.CancelGroup(GROUP_INTERRUPT_CHECK); + } + else + context.Repeat(50ms); + }).Schedule(12s, GROUP_INTERRUPT_CHECK, [this](TaskContext /*context*/) + { + _interruptScheduler.CancelGroup(GROUP_INTERRUPT_CHECK); + }); + }); + context.Repeat(53s, 56s); + }).Schedule(22min, [this](TaskContext /*context*/) + { + DoCastSelf(SPELL_BERSERK, true); + }); + } + + void DoAction(int32 action) override + { + if (action == ACTION_INCREASE_HELLFIRE_CHANNELER_DEATH_COUNT) + { + _channelersKilled++; + + if (_channelersKilled >= 5 && !_magReleased) + { + Talk(SAY_EMOTE_FREE); + Talk(SAY_FREE); + scheduler.CancelGroup(GROUP_EARLY_RELEASE_CHECK); //cancel regular countdown + scheduler.Schedule(3s, [this](TaskContext) + { + _magReleased = true; //redundancy + ScheduleCombatEvents(); + }); + } + } + } + void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); Talk(SAY_EMOTE_BEGIN); - scheduler.Schedule(60s, [this](TaskContext /*context*/) + scheduler.Schedule(60s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/) { Talk(SAY_EMOTE_NEARLY); - }).Schedule(120s, [this](TaskContext /*context*/) + }).Schedule(120s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/) { Talk(SAY_EMOTE_FREE); - }).Schedule(123s, [this](TaskContext /*context*/) + Talk(SAY_FREE); + }).Schedule(123s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/) { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToPC(false); - me->SetReactState(REACT_AGGRESSIVE); - instance->SetData(DATA_ACTIVATE_CUBES, 1); - me->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE); - - scheduler.Schedule(9s, [this](TaskContext context) - { - DoCastVictim(SPELL_CLEAVE); - context.Repeat(1200ms, 16300ms); - }).Schedule(20s, [this](TaskContext context) - { - me->CastCustomSpell(SPELL_BLAZE, SPELLVALUE_MAX_TARGETS, 1); - context.Repeat(11s, 39s); - }).Schedule(40s, [this](TaskContext context) - { - DoCastSelf(SPELL_QUAKE); //needs fixes with custom spell - scheduler.Schedule(7s, [this](TaskContext /*context*/) - { - DoCastSelf(SPELL_BLAST_NOVA); - - _interruptScheduler.Schedule(50ms, GROUP_INTERRUPT_CHECK, [this](TaskContext context) - { - if (me->GetAuraCount(SPELL_SHADOW_GRASP_VISUAL) == 5) - { - Talk(SAY_BANISH); - me->InterruptNonMeleeSpells(true); - scheduler.CancelGroup(GROUP_INTERRUPT_CHECK); - } - else - context.Repeat(50ms); - }).Schedule(12s, GROUP_INTERRUPT_CHECK, [this](TaskContext /*context*/) - { - _interruptScheduler.CancelGroup(GROUP_INTERRUPT_CHECK); - }); - }); - context.Repeat(53s, 56s); - }).Schedule(1320s, [this](TaskContext /*context*/) - { - DoCastSelf(SPELL_BERSERK, true); - }); + ScheduleCombatEvents(); }); } @@ -227,7 +261,9 @@ struct boss_magtheridon : public BossAI private: bool _recentlySpoken; + bool _magReleased; uint8 _currentPhase; + uint8 _channelersKilled; TaskScheduler _interruptScheduler; }; diff --git a/src/server/scripts/Outland/zone_nagrand.cpp b/src/server/scripts/Outland/zone_nagrand.cpp index 39d7d95bd..5b8590169 100644 --- a/src/server/scripts/Outland/zone_nagrand.cpp +++ b/src/server/scripts/Outland/zone_nagrand.cpp @@ -259,127 +259,6 @@ public: }; }; -/*###### -## go_corkis_prison and npc_corki -######*/ - -enum CorkiData -{ - // first quest - QUEST_HELP = 9923, - NPC_CORKI = 18445, - NPC_CORKI_CREDIT_1 = 18369, - GO_CORKIS_PRISON = 182349, - CORKI_SAY_THANKS = 0, - // 2nd quest - QUEST_CORKIS_GONE_MISSING_AGAIN = 9924, - NPC_CORKI_2 = 20812, - GO_CORKIS_PRISON_2 = 182350, - CORKI_SAY_PROMISE = 0, - // 3rd quest - QUEST_CHOWAR_THE_PILLAGER = 9955, - NPC_CORKI_3 = 18369, - NPC_CORKI_CREDIT_3 = 18444, - GO_CORKIS_PRISON_3 = 182521, - CORKI_SAY_LAST = 0 -}; - -class go_corkis_prison : public GameObjectScript -{ -public: - go_corkis_prison() : GameObjectScript("go_corkis_prison") { } - - bool OnGossipHello(Player* player, GameObject* go) override - { - go->SetGoState(GO_STATE_READY); - if (go->GetEntry() == GO_CORKIS_PRISON) - { - if (Creature* corki = go->FindNearestCreature(NPC_CORKI, 25, true)) - { - corki->GetMotionMaster()->MovePoint(1, go->GetPositionX() + 5, go->GetPositionY(), go->GetPositionZ()); - if (player) - player->KilledMonsterCredit(NPC_CORKI_CREDIT_1); - } - } - - if (go->GetEntry() == GO_CORKIS_PRISON_2) - { - if (Creature* corki = go->FindNearestCreature(NPC_CORKI_2, 25, true)) - { - corki->GetMotionMaster()->MovePoint(1, go->GetPositionX() - 5, go->GetPositionY(), go->GetPositionZ()); - if (player) - player->KilledMonsterCredit(NPC_CORKI_2); - } - } - - if (go->GetEntry() == GO_CORKIS_PRISON_3) - { - if (Creature* corki = go->FindNearestCreature(NPC_CORKI_3, 25, true)) - { - corki->GetMotionMaster()->MovePoint(1, go->GetPositionX() + 4, go->GetPositionY(), go->GetPositionZ()); - if (player) - player->KilledMonsterCredit(NPC_CORKI_CREDIT_3); - } - } - - return true; - } -}; - -class npc_corki : public CreatureScript -{ -public: - npc_corki() : CreatureScript("npc_corki") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_corkiAI(creature); - } - - struct npc_corkiAI : public ScriptedAI - { - npc_corkiAI(Creature* creature) : ScriptedAI(creature) { } - - uint32 Say_Timer; - bool ReleasedFromCage; - - void Reset() override - { - Say_Timer = 5000; - ReleasedFromCage = false; - } - - void UpdateAI(uint32 diff) override - { - if (ReleasedFromCage) - { - if (Say_Timer <= diff) - { - me->DespawnOrUnsummon(); - ReleasedFromCage = false; - } - else - Say_Timer -= diff; - } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type == POINT_MOTION_TYPE && id == 1) - { - Say_Timer = 5000; - ReleasedFromCage = true; - if (me->GetEntry() == NPC_CORKI) - Talk(CORKI_SAY_THANKS); - if (me->GetEntry() == NPC_CORKI_2) - Talk(CORKI_SAY_PROMISE); - if (me->GetEntry() == NPC_CORKI_3) - Talk(CORKI_SAY_LAST); - } - }; - }; -}; - /*##### ## npc_kurenai_captive #####*/ @@ -608,8 +487,6 @@ void AddSC_nagrand() { new npc_maghar_captive(); new npc_creditmarker_visit_with_ancestors(); - new npc_corki(); - new go_corkis_prison(); new npc_kurenai_captive(); new go_warmaul_prison(); } diff --git a/src/server/scripts/Pet/pet_generic.cpp b/src/server/scripts/Pet/pet_generic.cpp index 34621293f..7ecae8d6b 100644 --- a/src/server/scripts/Pet/pet_generic.cpp +++ b/src/server/scripts/Pet/pet_generic.cpp @@ -774,6 +774,49 @@ struct npc_pet_gen_moth : public NullCreatureAI } }; +// Darting Hatchling +enum Darting +{ + SPELL_DARTING_ON_SPAWN = 62586, // Applied on spawn via creature_template_addon + SPELL_DARTING_FEAR = 62585, // Applied every 20s from SPELL_DARTING_ON_SPAWN +}; + +struct npc_pet_darting_hatchling : public NullCreatureAI +{ + npc_pet_darting_hatchling(Creature* c) : NullCreatureAI(c) + { + goFast = false; + checkTimer = 0; + } + + bool goFast; + uint32 checkTimer; + + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override + { + if (spellInfo->Id == SPELL_DARTING_FEAR) + { + goFast = true; + } + } + + void UpdateAI(uint32 diff) override + { + if (!goFast) + { + return; + } + + checkTimer += diff; + if (checkTimer >= 2000) + { + me->RemoveAurasDueToSpell(SPELL_DARTING_FEAR); + checkTimer = 0; + goFast = false; + } + } +}; + void AddSC_generic_pet_scripts() { RegisterCreatureAI(npc_pet_gen_soul_trader_beacon); @@ -788,4 +831,5 @@ void AddSC_generic_pet_scripts() RegisterCreatureAI(npc_pet_gen_toxic_wasteling); RegisterCreatureAI(npc_pet_gen_fetch_ball); RegisterCreatureAI(npc_pet_gen_moth); + RegisterCreatureAI(npc_pet_darting_hatchling); }