diff --git a/data/sql/updates/db_world/2023_06_18_01.sql b/data/sql/updates/db_world/2023_06_18_01.sql new file mode 100644 index 000000000..ac6b9872b --- /dev/null +++ b/data/sql/updates/db_world/2023_06_18_01.sql @@ -0,0 +1,12 @@ +-- DB update 2023_06_18_00 -> 2023_06_18_01 +-- +UPDATE `creature_template` SET `minlevel` = 72, `maxlevel` = 72 WHERE `entry` IN ( +20738, -- Chrono Lord Deja (1) +20745, -- Temporus (1) +21558, -- High Botanist Freywinn (1) +21559, -- Laj (1) +21581, -- Thorngrin the Tender (1) +21582, -- Warp Splinter (1) +21712, -- Infinite Chrono-Lord (1) +22167 -- Infinite Timereaver (1) +); diff --git a/data/sql/updates/db_world/2023_06_18_02.sql b/data/sql/updates/db_world/2023_06_18_02.sql new file mode 100644 index 000000000..71f6603e1 --- /dev/null +++ b/data/sql/updates/db_world/2023_06_18_02.sql @@ -0,0 +1,5 @@ +-- DB update 2023_06_18_01 -> 2023_06_18_02 +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_gargolmar_retalliation' AND `spell_id` = 22857; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(22857, 'spell_gargolmar_retalliation'); diff --git a/data/sql/updates/db_world/2023_06_18_03.sql b/data/sql/updates/db_world/2023_06_18_03.sql new file mode 100644 index 000000000..b9b51f1a5 --- /dev/null +++ b/data/sql/updates/db_world/2023_06_18_03.sql @@ -0,0 +1,4 @@ +-- DB update 2023_06_18_02 -> 2023_06_18_03 +-- + +DELETE FROM `item_enchantment_template` WHERE `entry` = 5173 AND `ench` IN (29,33,91,197,231,927,1399,1913,1952,2067,2068,2069); diff --git a/data/sql/updates/db_world/2023_06_21_00.sql b/data/sql/updates/db_world/2023_06_21_00.sql new file mode 100644 index 000000000..5b1b9ebaf --- /dev/null +++ b/data/sql/updates/db_world/2023_06_21_00.sql @@ -0,0 +1,8 @@ +-- DB update 2023_06_18_03 -> 2023_06_21_00 +DELETE FROM `creature_template_movement` WHERE (`CreatureId` = 22355); +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES +(22355, 1, 0, 0, 1, 0, 0, 0); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 22355 AND `id` IN (8); +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 +(22355, 0, 8, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Netherweb Victim - On Respawn - Set Reactstate Passive'); diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 6e8bdf132..0c0eef805 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -729,6 +729,7 @@ bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 gathering_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_GATHERING); + sScriptMgr->OnUpdateGatheringSkill(this, SkillId, SkillValue, RedLevel + 100, RedLevel + 50, RedLevel + 25, gathering_skill_gain); // For skinning and Mining chance decrease with level. 1-74 - no decrease, // 75-149 - 2 times, 225-299 - 8 times @@ -806,6 +807,7 @@ bool Player::UpdateCraftSkill(uint32 spellid) uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING); + sScriptMgr->OnUpdateCraftingSkill(this, _spell_idx->second, SkillValue, craft_skill_gain); return UpdateSkillPro( _spell_idx->second->SkillLine, diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index 308a83e4b..a2b487147 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -983,6 +983,20 @@ void ScriptMgr::OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, }); } +void ScriptMgr::OnUpdateGatheringSkill(Player *player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32 &gain) { + ExecuteScript([&](PlayerScript* script) + { + script->OnUpdateGatheringSkill(player, skillId, gray, green, yellow, currentLevel, gain); + }); +} + +void ScriptMgr::OnUpdateCraftingSkill(Player *player, SkillLineAbilityEntry const* skill, uint32 currentLevel, uint32& gain) { + ExecuteScript([&](PlayerScript* script) + { + script->OnUpdateCraftingSkill(player, skill, currentLevel, gain); + }); +} + bool ScriptMgr::OnUpdateFishingSkill(Player* player, int32 skill, int32 zone_skill, int32 chance, int32 roll) { auto ret = IsValidBoolScript([&](PlayerScript* script) diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 4c0be23e4..2a8d76079 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -1276,6 +1276,29 @@ public: virtual void OnGetMaxSkillValue(Player* /*player*/, uint32 /*skill*/, int32& /*result*/, bool /*IsPure*/) { } + /** + * @brief This hook called before gathering skill gain is applied to the character. + * + * @param player Contains information about the Player sender + * @param skill_id Contains information about the skill line + * @param current Contains the current skill level for skill + * @param gray Contains the gray skill level for current application + * @param green Contains the green skill level for current application + * @param yellow Contains the yellow skill level for current application + * @param gain Contains the amount of points that should be added to the Player + */ + virtual void OnUpdateGatheringSkill(Player* /*player*/, uint32 /*skill_id*/, uint32 /*current*/, uint32 /*gray*/, uint32 /*green*/, uint32 /*yellow*/, uint32& /*gain*/) { } + + /** + * @brief This hook is called before crafting skill gain is applied to the character. + * + * @param player Contains information about the Player sender + * @param skill Contains information about the skill line + * @param current_level Contains the current skill level for skill + * @param gain Contains the amount of points that should be added to the Player + */ + virtual void OnUpdateCraftingSkill(Player* /*player*/, SkillLineAbilityEntry const* /*skill*/, uint32 /*current_level*/, uint32& /*gain*/) { } + [[nodiscard]] virtual bool OnUpdateFishingSkill(Player* /*player*/, int32 /*skill*/, int32 /*zone_skill*/, int32 /*chance*/, int32 /*roll*/) { return true; } [[nodiscard]] virtual bool CanAreaExploreAndOutdoor(Player* /*player*/) { return true; } @@ -2375,6 +2398,8 @@ public: /* PlayerScript */ void OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid); bool CanRepopAtGraveyard(Player* player); void OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, bool IsPure); + void OnUpdateGatheringSkill(Player* player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32& gain); + void OnUpdateCraftingSkill(Player* player, SkillLineAbilityEntry const* skill, uint32 currentLevel, uint32& gain); bool OnUpdateFishingSkill(Player* player, int32 skill, int32 zone_skill, int32 chance, int32 roll); bool CanAreaExploreAndOutdoor(Player* player); void OnVictimRewardBefore(Player* player, Player* victim, uint32& killer_title, uint32& victim_title); diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp index 439451504..9c8a293f9 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp @@ -119,6 +119,47 @@ struct boss_malchezaar : public BossAI { Initialize(); _Reset(); + + ScheduleHealthCheckEvent(60, [&] { + me->InterruptNonMeleeSpells(false); + _phase = 2; + DoCastSelf(SPELL_EQUIP_AXES); + Talk(SAY_AXE_TOSS1); + DoCastSelf(SPELL_THRASH_AURA, true); + SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE); + me->SetCanDualWield(true); + me->SetAttackTime(OFF_ATTACK, (me->GetAttackTime(BASE_ATTACK) * 150) / 100); + + scheduler.Schedule(5s, 10s, [this](TaskContext context) + { + DoCastVictim(SPELL_SUNDER_ARMOR); + context.Repeat(); + }); + + scheduler.CancelGroup(GROUP_SHADOW_WORD_PAIN); + }); + + ScheduleHealthCheckEvent(30, [&] { + me->RemoveAurasDueToSpell(SPELL_THRASH_AURA); + Talk(SAY_AXE_TOSS2); + _phase = PHASE_THREE; + clearweapons(); + + me->SummonCreature(NPC_MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000); + + scheduler.Schedule(20s, 30s, [this](TaskContext context) + { + DoCastRandomTarget(SPELL_AMPLIFY_DAMAGE, 1); + context.Repeat(); + }).Schedule(20s, [this](TaskContext context) + { + DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN); + context.SetGroup(GROUP_SHADOW_WORD_PAIN); + context.Repeat(); + }); + + scheduler.CancelGroup(GROUP_ENFEEBLE); + }); } void KilledUnit(Unit* /*victim*/) override @@ -183,49 +224,6 @@ struct boss_malchezaar : public BossAI }); } - void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (me->HealthBelowPctDamaged(60, damage) && _phase == PHASE_ONE) - { - me->InterruptNonMeleeSpells(false); - _phase = 2; - DoCastSelf( SPELL_EQUIP_AXES); - Talk(SAY_AXE_TOSS1); - DoCastSelf( SPELL_THRASH_AURA, true); - SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE); - me->SetCanDualWield(true); - me->SetAttackTime(OFF_ATTACK, (me->GetAttackTime(BASE_ATTACK) * 150) / 100); - - scheduler.Schedule(5s, 10s, [this](TaskContext context) - { - DoCastVictim(SPELL_SUNDER_ARMOR); - context.Repeat(); - }); - - scheduler.CancelGroup(GROUP_SHADOW_WORD_PAIN); - } - else if (me->HealthBelowPctDamaged(30, damage) && _phase == PHASE_TWO) - { - me->RemoveAurasDueToSpell(SPELL_THRASH_AURA); - Talk(SAY_AXE_TOSS2); - _phase = PHASE_THREE; - clearweapons(); - - me->SummonCreature(NPC_MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000); - - scheduler.Schedule(20s, 30s, [this](TaskContext context) - { - DoCastRandomTarget(SPELL_AMPLIFY_DAMAGE, 1); - context.Repeat(); - }).Schedule(20s, [this](TaskContext context) - { - DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN); - context.SetGroup(GROUP_SHADOW_WORD_PAIN); - context.Repeat(); - });; - } - } - void EnfeebleHealthEffect() { std::list targetList; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp index 3301df8a4..e4f572fcf 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp @@ -56,7 +56,8 @@ public: _currentRift = 0; _shieldPercent = 100; _encounterNPCs.clear(); - _canSpawnPortal = true; // Delay after bosses + _noBossSpawnDelay = true; // Delay after bosses + _eventStatus = EVENT_PREPARE; } void CleanupInstance() @@ -72,10 +73,24 @@ public: _availableRiftPositions.push_back(pos); } + // prevent getting stuck if event fails during boss break + _noBossSpawnDelay = true; + instance->LoadGrid(-2023.0f, 7121.0f); if (Creature* medivh = GetCreature(DATA_MEDIVH)) { - medivh->DespawnOrUnsummon(0ms, 3s); + medivh->Respawn(); + } + for (ObjectGuid const& guid : _encounterNPCs) + { + if (guid.GetEntry() == NPC_DP_BEAM_STALKER) + { + if (Creature* creature = instance->GetCreature(guid)) + { + creature->Respawn(); + } + break; + } } } @@ -121,8 +136,8 @@ public: case NPC_RIFT_LORD: case NPC_RIFT_LORD_2: case NPC_TIME_RIFT: - case NPC_INFINITE_ASSASIN: - case NPC_INFINITE_ASSASIN_2: + case NPC_INFINITE_ASSASSIN: + case NPC_INFINITE_ASSASSIN_2: case NPC_INFINITE_WHELP: case NPC_INFINITE_CHRONOMANCER: case NPC_INFINITE_CHRONOMANCER_2: @@ -143,14 +158,14 @@ public: case DATA_CHRONO_LORD_DEJA: case DATA_TEMPORUS: { - _canSpawnPortal = false; + _noBossSpawnDelay = false; _scheduler.Schedule(2min + 30s, [this](TaskContext) { - _canSpawnPortal = true; + _noBossSpawnDelay = true; + ScheduleNextPortal(0s, Position(0.0f, 0.0f, 0.0f, 0.0f)); }); - ScheduleNextPortal(2min + 30s, Position(0.0f, 0.0f, 0.0f, 0.0f)); break; } default: @@ -163,26 +178,27 @@ public: void OnPlayerEnter(Player* player) override { - if (instance->GetPlayersCountExceptGMs() <= 1 && GetBossState(DATA_AEONUS) != DONE) + if (instance->GetPlayersCountExceptGMs() <= 1 && GetBossState(DATA_AEONUS) != DONE && _eventStatus != EVENT_IN_PROGRESS) { CleanupInstance(); } - player->SendUpdateWorldState(WORLD_STATE_BM, _currentRift > 0 ? 1 : 0); + player->SendUpdateWorldState(WORLD_STATE_BM, _eventStatus); player->SendUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent); player->SendUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift); } void ScheduleNextPortal(Milliseconds time, Position lastPosition) { + // only one rift can be scheduled at any time _scheduler.CancelGroup(CONTEXT_GROUP_RIFTS); _scheduler.Schedule(time, [this, lastPosition](TaskContext context) { if (GetCreature(DATA_MEDIVH)) { - // Spawning prevented - there's a 150s delay after a boss dies. - if (!_canSpawnPortal) + // Spawning prevented: after-boss-delay or event failed/not started or last portal spawned + if (!_noBossSpawnDelay || _eventStatus == EVENT_PREPARE || _currentRift >= 18) { return; } @@ -208,19 +224,10 @@ public: instance->SummonCreature(NPC_TIME_RIFT, spawnPos); - // Here we check if we have available rift spots. - if (_currentRift < 18) - { - if (!_availableRiftPositions.empty()) - { - context.Repeat((_currentRift >= 13 ? 2min : 90s)); - } - else - { - context.Repeat(4s); - } - } + // queue next portal if group doesn't kill keepers fast enough + context.Repeat((_currentRift >= 13 ? 2min : 90s)); } + // if no rift positions are available, the next rift will be scheduled in OnCreatureRemove } context.SetGroup(CONTEXT_GROUP_RIFTS); @@ -241,8 +248,8 @@ public: case NPC_RIFT_KEEPER_MAGE: case NPC_RIFT_LORD: case NPC_RIFT_LORD_2: - case NPC_INFINITE_ASSASIN: - case NPC_INFINITE_ASSASIN_2: + case NPC_INFINITE_ASSASSIN: + case NPC_INFINITE_ASSASSIN_2: case NPC_INFINITE_WHELP: case NPC_INFINITE_CHRONOMANCER: case NPC_INFINITE_CHRONOMANCER_2: @@ -251,6 +258,7 @@ public: case NPC_INFINITE_VANQUISHER: case NPC_INFINITE_VANQUISHER_2: case NPC_DP_BEAM_STALKER: + case NPC_DP_EMITTER_STALKER: _encounterNPCs.insert(creature->GetGUID()); break; } @@ -263,7 +271,7 @@ public: switch (creature->GetEntry()) { case NPC_TIME_RIFT: - if (_currentRift < 18) + if (_currentRift < 18 && _noBossSpawnDelay && _eventStatus == EVENT_IN_PROGRESS) { if (_availableRiftPositions.size() < 3) { @@ -286,8 +294,8 @@ public: case NPC_RIFT_KEEPER_MAGE: case NPC_RIFT_LORD: case NPC_RIFT_LORD_2: - case NPC_INFINITE_ASSASIN: - case NPC_INFINITE_ASSASIN_2: + case NPC_INFINITE_ASSASSIN: + case NPC_INFINITE_ASSASSIN_2: case NPC_INFINITE_WHELP: case NPC_INFINITE_CHRONOMANCER: case NPC_INFINITE_CHRONOMANCER_2: @@ -295,6 +303,7 @@ public: case NPC_INFINITE_EXECUTIONER_2: case NPC_INFINITE_VANQUISHER: case NPC_INFINITE_VANQUISHER_2: + case NPC_DP_EMITTER_STALKER: _encounterNPCs.erase(creature->GetGUID()); break; } @@ -308,27 +317,14 @@ public: { case DATA_MEDIVH: { - DoUpdateWorldState(WORLD_STATE_BM, 1); + _eventStatus = EVENT_IN_PROGRESS; + + DoUpdateWorldState(WORLD_STATE_BM, _eventStatus); DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent); DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift); ScheduleNextPortal(3s, Position(0.0f, 0.0f, 0.0f, 0.0f)); - for (ObjectGuid const& guid : _encounterNPCs) - { - if (guid.GetEntry() == NPC_DP_BEAM_STALKER) - { - if (Creature* creature = instance->GetCreature(guid)) - { - if (!creature->IsAlive()) - { - creature->Respawn(true); - } - } - break; - } - } - break; } case DATA_DAMAGE_SHIELD: @@ -348,6 +344,8 @@ public: if (!_shieldPercent) { + _eventStatus = EVENT_PREPARE; + if (Creature* medivh = GetCreature(DATA_MEDIVH)) { if (medivh->IsAlive() && medivh->IsAIEnabled) @@ -404,6 +402,12 @@ public: GuidSet encounterNPCSCopy = _encounterNPCs; for (ObjectGuid const& guid : encounterNPCSCopy) { + // Don't despawn permanent visual effect NPC twice or it won't respawn correctly + if (guid.GetEntry() == NPC_DP_BEAM_STALKER) + { + continue; + } + if (Creature* creature = instance->GetCreature(guid)) { creature->CastSpell(creature, SPELL_TELEPORT_VISUAL, true); @@ -412,6 +416,16 @@ public: } _scheduler.CancelAll(); + + // Step 4 - Schedule instance cleanup without player interaction + _scheduler.Schedule(300s, [this](TaskContext) + { + CleanupInstance(); + + DoUpdateWorldState(WORLD_STATE_BM, _eventStatus); + DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent); + DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift); + }); }); }); }); @@ -447,7 +461,8 @@ public: GuidSet _encounterNPCs; uint8 _currentRift; int8 _shieldPercent; - bool _canSpawnPortal; + bool _noBossSpawnDelay; + EventStatus _eventStatus; TaskScheduler _scheduler; }; }; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp index 9df152bd1..18cd30bae 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp @@ -43,9 +43,9 @@ enum medivhMisc EVENT_OUTRO_8 = 17 }; -static std::vector firstWave = { NPC_INFINITE_ASSASIN, NPC_INFINITE_WHELP, NPC_INFINITE_CHRONOMANCER }; -static std::vector secondWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_WHELP, NPC_INFINITE_ASSASIN }; -static std::vector thirdWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_VANQUISHER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_ASSASIN }; +static std::vector firstWave = { NPC_INFINITE_ASSASSIN, NPC_INFINITE_WHELP, NPC_INFINITE_CHRONOMANCER }; +static std::vector secondWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_WHELP, NPC_INFINITE_ASSASSIN }; +static std::vector thirdWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_VANQUISHER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_ASSASSIN }; class NpcRunToHome : public BasicEvent { @@ -323,8 +323,8 @@ struct npc_time_rift : public NullCreatureAI { switch (entry) { - case NPC_INFINITE_ASSASIN: - entry = NPC_INFINITE_ASSASIN_2; + case NPC_INFINITE_ASSASSIN: + entry = NPC_INFINITE_ASSASSIN_2; break; case NPC_INFINITE_CHRONOMANCER: entry = NPC_INFINITE_CHRONOMANCER_2; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.h b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.h index 6b560a505..3a8715a34 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.h +++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.h @@ -36,13 +36,10 @@ enum DataTypes MAX_ENCOUNTER = 3, DATA_MEDIVH = 10, - DATA_RIFT_KILLED = 11, + DATA_DAMAGE_SHIELD = 12, DATA_SHIELD_PERCENT = 13, - DATA_RIFT_NUMBER = 14, - - DATA_SUMMONED_NPC = 20, - DATA_DELETED_NPC = 21 + DATA_RIFT_NUMBER = 14 }; enum WorldStateIds @@ -52,6 +49,12 @@ enum WorldStateIds WORLD_STATE_BM_RIFT = 2784 }; +enum EventStatus +{ + EVENT_PREPARE = 0, + EVENT_IN_PROGRESS = 1 +}; + enum QuestIds { QUEST_OPENING_PORTAL = 10297, @@ -75,13 +78,13 @@ enum CreatureIds NPC_INFINITE_TIMEREAVER = 21698, NPC_AEONUS = 17881, - NPC_INFINITE_ASSASIN = 17835, + NPC_INFINITE_ASSASSIN = 17835, NPC_INFINITE_WHELP = 21818, NPC_INFINITE_CHRONOMANCER = 17892, NPC_INFINITE_EXECUTIONER = 18994, NPC_INFINITE_VANQUISHER = 18995, - NPC_INFINITE_ASSASIN_2 = 21137, + NPC_INFINITE_ASSASSIN_2 = 21137, NPC_INFINITE_CHRONOMANCER_2 = 21136, NPC_INFINITE_EXECUTIONER_2 = 21138, NPC_INFINITE_VANQUISHER_2 = 21139, diff --git a/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp b/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp index ce7a3af22..1689944cf 100644 --- a/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp +++ b/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp @@ -44,6 +44,7 @@ enum blySays enum blySpells { + SPELL_BLYS_BAND_ESCAPE = 11365, SPELL_SHIELD_BASH = 11972, SPELL_REVENGE = 12170 }; @@ -64,6 +65,7 @@ public: void InitializeAI() override { + ableToPortHome = false; startedFight = false; me->SetFaction(FACTION_FRIENDLY); postGossipStep = 0; @@ -74,16 +76,33 @@ public: InstanceScript* instance; bool startedFight; + bool ableToPortHome; uint32 postGossipStep; uint32 Text_Timer; uint32 ShieldBash_Timer; - uint32 Revenge_Timer; //this is wrong, spell should never be used unless me->GetVictim() dodge, parry or block attack. Trinity support required. + uint32 Revenge_Timer; //this is wrong, spell should never be used unless me->GetVictim() dodge, parry or block attack. Trinity support required. + uint32 Porthome_Timer; ObjectGuid PlayerGUID; void Reset() override { ShieldBash_Timer = 5000; Revenge_Timer = 8000; + Porthome_Timer = 156000; + ableToPortHome = false; + startedFight = false; + } + + void EnterEvadeMode(EvadeReason /*reason*/) override + { + if (ableToPortHome) + return; + + if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS) + { + ableToPortHome = true; + Porthome_Timer = 156000; + } } void MovementInform(uint32 type, uint32 /*id*/) override @@ -112,6 +131,7 @@ public: switch (postGossipStep) { case 1: + startedFight = true; //weegli doesn't fight - he goes & blows up the door if (Creature* pWeegli = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_WEEGLI))) { @@ -128,6 +148,7 @@ public: me->SetFaction(FACTION_MONSTER); Player* target = ObjectAccessor::GetPlayer(*me, PlayerGUID); + switchFactionIfAlive(NPC_WEEGLI, target); switchFactionIfAlive(NPC_RAVEN, target); switchFactionIfAlive(NPC_ORO, target); switchFactionIfAlive(NPC_MURTA, target); @@ -146,6 +167,37 @@ public: } } + if (Porthome_Timer <= diff && ableToPortHome == true) + { + if (Creature* weegli = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_WEEGLI))) + { + weegli->CastSpell(weegli, SPELL_BLYS_BAND_ESCAPE); + weegli->DespawnOrUnsummon(10000); + } + if (Creature* raven = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_RAVEN))) + { + raven->CastSpell(raven, SPELL_BLYS_BAND_ESCAPE); + raven->DespawnOrUnsummon(10000); + } + if (Creature* oro = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_ORO))) + { + oro->CastSpell(oro, SPELL_BLYS_BAND_ESCAPE); + oro->DespawnOrUnsummon(10000); + } + if (Creature* murta = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_MURTA))) + { + murta->CastSpell(murta, SPELL_BLYS_BAND_ESCAPE); + murta->DespawnOrUnsummon(10000); + } + DoCastSelf(SPELL_BLYS_BAND_ESCAPE); + me->DespawnOrUnsummon(10000); + Porthome_Timer = 156000; //set timer back so that the event doesn't keep triggering + } + else + { + Porthome_Timer -= diff; + } + if (!UpdateVictim()) { return; @@ -176,6 +228,7 @@ public: void DoAction(int32 /*param*/) override { + ableToPortHome = false; postGossipStep = 1; Text_Timer = 0; } @@ -211,9 +264,8 @@ public: void sGossipHello(Player* player) override { - if (instance->GetData(DATA_PYRAMID) >= PYRAMID_DESTROY_GATES && !startedFight) + if (instance->GetData(DATA_PYRAMID) >= PYRAMID_MOVED_DOWNSTAIRS && !startedFight) { - startedFight = true; AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_BLY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, 1517, me->GetGUID()); } @@ -261,6 +313,17 @@ public: instance->SetData(DATA_PYRAMID, PYRAMID_CAGES_OPEN); + //setting gossip option as soon as the cages open + if(Creature* bly = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BLY))) + { + bly->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + } + + if(Creature* weegli = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_WEEGLI))) + { + weegli->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + } + //set bly & co to aggressive & start moving to top of stairs initBlyCrewMember(NPC_BLY, 1884.99f, 1263, 41.52f); initBlyCrewMember(NPC_RAVEN, 1882.5f, 1263, 41.52f); @@ -281,15 +344,6 @@ public: crew->GetMotionMaster()->MovePoint(1, { x, y, z, 4.78f }); crew->SetFaction(FACTION_ESCORT_N_NEUTRAL_ACTIVE); - switch (entry) - { - case NPC_BLY: - case NPC_WEEGLI: - crew->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - break; - default: - break; - } } } }; @@ -429,13 +483,13 @@ public: { if (instance->GetData(DATA_PYRAMID) == PYRAMID_CAGES_OPEN) { + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); instance->SetData(DATA_PYRAMID, PYRAMID_ARRIVED_AT_STAIR); Talk(SAY_WEEGLI_OHNO); } - else if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS) + else if (instance->GetData(DATA_PYRAMID) >= PYRAMID_KILLED_ALL_TROLLS && instance->GetData(DATA_PYRAMID) < PYRAMID_DESTROY_GATES) { instance->SetData(DATA_PYRAMID, PYRAMID_MOVED_DOWNSTAIRS); - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); } else if (instance->GetData(DATA_PYRAMID) == PYRAMID_DESTROY_GATES) { @@ -452,13 +506,13 @@ public: if (instance->GetData(DATA_PYRAMID) == PYRAMID_CAGES_OPEN) { + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); instance->SetData(DATA_PYRAMID, PYRAMID_ARRIVED_AT_STAIR); Talk(SAY_WEEGLI_OHNO); } - else if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS) + else if (instance->GetData(DATA_PYRAMID) >= PYRAMID_KILLED_ALL_TROLLS && instance->GetData(DATA_PYRAMID) < PYRAMID_DESTROY_GATES) { instance->SetData(DATA_PYRAMID, PYRAMID_MOVED_DOWNSTAIRS); - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); } else if (instance->GetData(DATA_PYRAMID) == PYRAMID_DESTROY_GATES) { @@ -476,10 +530,6 @@ public: me->SetHomePosition(1858.57f, 1146.35f, 14.745f, 3.85f); Talk(SAY_WEEGLI_OK_I_GO); instance->SetData(DATA_PYRAMID, PYRAMID_DESTROY_GATES); - if (Creature* sergeantBly = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BLY))) - { - sergeantBly->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - } } } @@ -501,6 +551,7 @@ public: switch (instance->GetData(DATA_PYRAMID)) { case PYRAMID_MOVED_DOWNSTAIRS: + case PYRAMID_KILLED_ALL_TROLLS: AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_WEEGLI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, 1514, me->GetGUID()); //if event can proceed to end break; @@ -508,7 +559,7 @@ public: SendGossipMenuFor(player, 1511, me->GetGUID()); //if event not started break; default: - SendGossipMenuFor(player, 1513, me->GetGUID()); //if event are in progress + SendGossipMenuFor(player, 1513, me->GetGUID()); //if event is in progress } } }; diff --git a/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp b/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp index d6ef410ce..16eb9e89a 100644 --- a/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp +++ b/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp @@ -58,17 +58,6 @@ enum Spells SPELL_STONED = 33652, }; -enum Events -{ - EVENT_GROWTH = 1, - EVENT_CAVE_IN = 2, - EVENT_GROUND_SLAM = 3, - EVENT_HURTFUL_STRIKE = 4, - EVENT_REVERBERATION = 5, - EVENT_SHATTER = 6, - EVENT_RECENTLY_SPOKEN = 7 -}; - struct boss_gruul : public BossAI { boss_gruul(Creature* creature) : BossAI(creature, DATA_GRUUL) { } @@ -76,7 +65,8 @@ struct boss_gruul : public BossAI void Reset() override { _Reset(); - _caveInTimer = 29000; + _recentlySpoken = false; + _caveInTimer = 29000ms; } void JustEngagedWith(Unit* /*who*/) override @@ -84,20 +74,61 @@ struct boss_gruul : public BossAI _JustEngagedWith(); Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_GROWTH, 30000); - events.ScheduleEvent(EVENT_CAVE_IN, _caveInTimer); - events.ScheduleEvent(EVENT_REVERBERATION, 20000); - events.ScheduleEvent(EVENT_HURTFUL_STRIKE, 10000); - events.ScheduleEvent(EVENT_GROUND_SLAM, 35000); + scheduler.Schedule(30300ms, [this](TaskContext context) + { + Talk(EMOTE_GROW); + DoCastSelf(SPELL_GROWTH); + context.Repeat(30300ms); + }).Schedule(_caveInTimer, [this](TaskContext context) + { + DoCastRandomTarget(SPELL_CAVE_IN); + if (_caveInTimer > 4000ms) + { + _caveInTimer = _caveInTimer - 1500ms; + } + context.Repeat(_caveInTimer); + }).Schedule(20s, [this](TaskContext context) + { + DoCastSelf(SPELL_REVERBERATION); + context.Repeat(22s); + }).Schedule(10s, [this](TaskContext context) + { + if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 5.0f)) + { + DoCast(target, SPELL_HURTFUL_STRIKE); + } + else + { + DoCastVictim(SPELL_HURTFUL_STRIKE); + } + context.Repeat(15s); + }).Schedule(35s, [this](TaskContext context) + { + Talk(SAY_SLAM); + DoCastSelf(SPELL_GROUND_SLAM); + scheduler.DelayAll(9701ms); + scheduler.Schedule(9700ms, [this](TaskContext) + { + Talk(SAY_SHATTER); + me->RemoveAurasDueToSpell(SPELL_LOOK_AROUND); + DoCastSelf(SPELL_SHATTER); + }); + context.Repeat(60s); + }); } void KilledUnit(Unit* /*who*/) override { - if (events.GetNextEventTime(EVENT_RECENTLY_SPOKEN) == 0) + if (!_recentlySpoken) { - events.ScheduleEvent(EVENT_RECENTLY_SPOKEN, 5000); Talk(SAY_SLAY); + _recentlySpoken = true; } + + scheduler.Schedule(5s, [this](TaskContext) + { + _recentlySpoken = false; + }); } void JustDied(Unit* /*killer*/) override @@ -111,52 +142,7 @@ struct boss_gruul : public BossAI if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_GROWTH: - Talk(EMOTE_GROW); - DoCast(me, SPELL_GROWTH); - events.ScheduleEvent(EVENT_GROWTH, 30000); - break; - case EVENT_CAVE_IN: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - me->CastSpell(target, SPELL_CAVE_IN, false); - if (_caveInTimer >= 4000) - _caveInTimer -= 1500; - events.ScheduleEvent(EVENT_CAVE_IN, _caveInTimer); - break; - case EVENT_REVERBERATION: - me->CastSpell(me, SPELL_REVERBERATION, false); - events.ScheduleEvent(EVENT_REVERBERATION, 22000); - break; - case EVENT_HURTFUL_STRIKE: - if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 5.0f)) - { - me->CastSpell(target, SPELL_HURTFUL_STRIKE, false); - } - else - { - me->CastSpell(me->GetVictim(), SPELL_HURTFUL_STRIKE, false); - } - events.ScheduleEvent(EVENT_HURTFUL_STRIKE, 15000); - break; - case EVENT_GROUND_SLAM: - Talk(SAY_SLAM); - me->CastSpell(me, SPELL_GROUND_SLAM, false); - events.DelayEvents(8001); - events.ScheduleEvent(EVENT_GROUND_SLAM, 60000); - events.ScheduleEvent(EVENT_SHATTER, 8000); - break; - case EVENT_SHATTER: - Talk(SAY_SHATTER); - me->RemoveAurasDueToSpell(SPELL_LOOK_AROUND); - me->CastSpell(me, SPELL_SHATTER, false); - break; - } + scheduler.Update(diff); if (!me->HasUnitState(UNIT_STATE_ROOT)) { @@ -165,7 +151,8 @@ struct boss_gruul : public BossAI } private: - uint32 _caveInTimer; + std::chrono::milliseconds _caveInTimer; + bool _recentlySpoken; }; struct npc_invisible_tractor_beam_source : public NullCreatureAI diff --git a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp index c00a40894..b59eeec83 100644 --- a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp +++ b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp @@ -17,6 +17,7 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "TaskScheduler.h" #include "gruuls_lair.h" enum HighKingMaulgar @@ -59,39 +60,49 @@ enum HighKingMaulgar ACTION_ADD_DEATH = 1 }; -enum HKMEvents -{ - EVENT_RECENTLY_SPOKEN = 1, - EVENT_ARCING_SMASH = 2, - EVENT_MIGHTY_BLOW = 3, - EVENT_WHIRLWIND = 4, - EVENT_CHARGING = 5, - EVENT_ROAR = 6, - EVENT_CHECK_HEALTH = 7, - - EVENT_ADD_ABILITY1 = 10, - EVENT_ADD_ABILITY2 = 11, - EVENT_ADD_ABILITY3 = 12, - EVENT_ADD_ABILITY4 = 13 -}; - struct boss_high_king_maulgar : public BossAI { - boss_high_king_maulgar(Creature* creature) : BossAI(creature, DATA_MAULGAR) { } + boss_high_king_maulgar(Creature* creature) : BossAI(creature, DATA_MAULGAR) + { + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } void Reset() override { _Reset(); + _recentlySpoken = false; me->SetLootMode(0); + ScheduleHealthCheckEvent(50, [&]{ + Talk(SAY_ENRAGE); + DoCastSelf(SPELL_FLURRY); + + scheduler.Schedule(0ms, [this](TaskContext context) + { + DoCastRandomTarget(SPELL_BERSERKER_C); + context.Repeat(35s); + }).Schedule(0ms, [this](TaskContext context) + { + DoCastSelf(SPELL_ROAR); + context.Repeat(20600ms, 29100ms); + }); + }); } void KilledUnit(Unit* /*victim*/) override { - if (events.GetNextEventTime(EVENT_RECENTLY_SPOKEN) == 0) + if(!_recentlySpoken) { - events.ScheduleEvent(EVENT_RECENTLY_SPOKEN, 5s); Talk(SAY_SLAY); + _recentlySpoken = true; } + + scheduler.Schedule(5s, [this](TaskContext) + { + _recentlySpoken = false; + }); } void JustDied(Unit* /*killer*/) override @@ -126,10 +137,20 @@ struct boss_high_king_maulgar : public BossAI _JustEngagedWith(); Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_ARCING_SMASH, 10s); - events.ScheduleEvent(EVENT_MIGHTY_BLOW, 15s); - events.ScheduleEvent(EVENT_WHIRLWIND, 54s); - events.ScheduleEvent(EVENT_CHECK_HEALTH, 500ms); + scheduler.Schedule(9500ms, [this](TaskContext context) + { + DoCastVictim(SPELL_ARCING_SMASH); + context.Repeat(9500ms, 12s); + }).Schedule(15700ms, [this](TaskContext context) + { + DoCastVictim(SPELL_MIGHTY_BLOW); + context.Repeat(16200ms, 19s); + }).Schedule(67000ms, [this](TaskContext context) + { + scheduler.DelayAll(15s); + DoCastSelf(SPELL_WHIRLWIND); + context.Repeat(45s, 60s); + }); } void UpdateAI(uint32 diff) override @@ -137,49 +158,12 @@ struct boss_high_king_maulgar : public BossAI if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_ARCING_SMASH: - me->CastSpell(me->GetVictim(), SPELL_ARCING_SMASH, false); - events.ScheduleEvent(EVENT_ARCING_SMASH, 10s); - break; - case EVENT_MIGHTY_BLOW: - me->CastSpell(me->GetVictim(), SPELL_MIGHTY_BLOW, false); - events.ScheduleEvent(EVENT_MIGHTY_BLOW, 15s); - break; - case EVENT_WHIRLWIND: - events.DelayEvents(15s); - me->CastSpell(me, SPELL_WHIRLWIND, false); - events.ScheduleEvent(EVENT_WHIRLWIND, 54s); - break; - case EVENT_CHECK_HEALTH: - if (me->HealthBelowPct(50)) - { - Talk(SAY_ENRAGE); - me->CastSpell(me, SPELL_FLURRY, true); - events.ScheduleEvent(EVENT_CHARGING, 0s); - events.ScheduleEvent(EVENT_ROAR, 30s); - break; - } - events.ScheduleEvent(EVENT_CHECK_HEALTH, 500ms); - break; - case EVENT_ROAR: - me->CastSpell(me, SPELL_ROAR, false); - events.ScheduleEvent(EVENT_ROAR, 40s); - break; - case EVENT_CHARGING: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1)) - me->CastSpell(target, SPELL_BERSERKER_C, false); - events.ScheduleEvent(EVENT_CHARGING, 35s); - break; - } + scheduler.Update(diff); DoMeleeAttackIfReady(); } +private: + bool _recentlySpoken; }; struct boss_olm_the_summoner : public ScriptedAI @@ -187,15 +171,18 @@ struct boss_olm_the_summoner : public ScriptedAI boss_olm_the_summoner(Creature* creature) : ScriptedAI(creature), summons(me) { instance = creature->GetInstanceScript(); + _scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } - EventMap events; SummonList summons; InstanceScript* instance; void Reset() override { - events.Reset(); + _scheduler.CancelAll(); summons.DespawnAll(); instance->SetBossState(DATA_MAULGAR, NOT_STARTED); } @@ -214,9 +201,22 @@ struct boss_olm_the_summoner : public ScriptedAI me->SetInCombatWithZone(); instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - events.ScheduleEvent(EVENT_ADD_ABILITY1, 500ms); - events.ScheduleEvent(EVENT_ADD_ABILITY2, 5s); - events.ScheduleEvent(EVENT_ADD_ABILITY3, 6500ms); + _scheduler.Schedule(1200ms, [this](TaskContext context) + { + DoCastSelf(SPELL_SUMMON_WFH); + context.Repeat(48500ms); + }).Schedule(6050ms, [this](TaskContext context) + { + DoCastVictim(SPELL_DARK_DECAY); + context.Repeat(6050ms); + }).Schedule(6500ms, [this](TaskContext context) + { + if (me->HealthBelowPct(90)) + { + DoCastRandomTarget(SPELL_DEATH_COIL); + } + context.Repeat(6s, 13500ms); + }); } void JustDied(Unit* /*killer*/) override @@ -234,27 +234,12 @@ struct boss_olm_the_summoner : public ScriptedAI if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + _scheduler.Update(diff); - switch (events.ExecuteEvent()) - { - case EVENT_ADD_ABILITY1: - me->CastSpell(me, SPELL_SUMMON_WFH, false); - events.ScheduleEvent(EVENT_ADD_ABILITY1, 50s); - break; - case EVENT_ADD_ABILITY2: - DoCastVictim(SPELL_DARK_DECAY); - events.ScheduleEvent(EVENT_ADD_ABILITY2, 6500ms); - break; - case EVENT_ADD_ABILITY3: - DoCastRandomTarget(SPELL_DEATH_COIL); - events.ScheduleEvent(EVENT_ADD_ABILITY3, 7s); - break; - } DoMeleeAttackIfReady(); } +private: + TaskScheduler _scheduler; }; struct boss_kiggler_the_crazed : public ScriptedAI @@ -262,14 +247,17 @@ struct boss_kiggler_the_crazed : public ScriptedAI boss_kiggler_the_crazed(Creature* creature) : ScriptedAI(creature) { instance = creature->GetInstanceScript(); + _scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } - EventMap events; InstanceScript* instance; void Reset() override { - events.Reset(); + _scheduler.CancelAll(); instance->SetBossState(DATA_MAULGAR, NOT_STARTED); } @@ -278,10 +266,35 @@ struct boss_kiggler_the_crazed : public ScriptedAI me->SetInCombatWithZone(); instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - events.ScheduleEvent(EVENT_ADD_ABILITY1, 1500ms); - events.ScheduleEvent(EVENT_ADD_ABILITY2, 5s); - events.ScheduleEvent(EVENT_ADD_ABILITY3, 25s); - events.ScheduleEvent(EVENT_ADD_ABILITY4, 30s); + _scheduler.Schedule(1200ms, [this](TaskContext context) + { + DoCastVictim(SPELL_LIGHTNING_BOLT); + context.Repeat(2400ms); + }).Schedule(29s, [this](TaskContext context) + { + DoCastVictim(SPELL_ARCANE_SHOCK); + context.Repeat(7200ms, 20600ms); + }).Schedule(23s, [this](TaskContext context) + { + //changed to work similarly to Ikiss poly + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_GREATER_POLYMORPH); + if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, [&] + (Unit* target) -> bool + { + return target && !target->IsImmunedToSpell(spellInfo); + })) + { + DoCast(target, SPELL_GREATER_POLYMORPH); + } + context.Repeat(10900ms); + }).Schedule(30s, [this](TaskContext context) + { + if (me->SelectNearestPlayer(30.0f)) + { + DoCastAOE(SPELL_ARCANE_EXPLOSION); + } + context.Repeat(30s); + }); } void JustDied(Unit* /*killer*/) override @@ -294,36 +307,12 @@ struct boss_kiggler_the_crazed : public ScriptedAI if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_ADD_ABILITY1: - DoCastVictim(SPELL_LIGHTNING_BOLT); - events.ScheduleEvent(EVENT_ADD_ABILITY1, 1500ms); - break; - case EVENT_ADD_ABILITY2: - DoCastVictim(SPELL_ARCANE_SHOCK); - events.ScheduleEvent(EVENT_ADD_ABILITY2, 5s); - break; - case EVENT_ADD_ABILITY3: - if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1)) //target method should perhaps change - me->CastSpell(target, SPELL_GREATER_POLYMORPH, false); - events.ScheduleEvent(EVENT_ADD_ABILITY3, 11s); - break; - case EVENT_ADD_ABILITY4: - if (me->SelectNearestPlayer(30.0f)) - { - DoCastAOE(SPELL_ARCANE_EXPLOSION); - } - events.ScheduleEvent(EVENT_ADD_ABILITY4, 30s); - break; - } + _scheduler.Update(diff); DoMeleeAttackIfReady(); } +private: + TaskScheduler _scheduler; }; struct boss_blindeye_the_seer : public ScriptedAI @@ -331,14 +320,17 @@ struct boss_blindeye_the_seer : public ScriptedAI boss_blindeye_the_seer(Creature* creature) : ScriptedAI(creature) { instance = creature->GetInstanceScript(); + _scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } - EventMap events; InstanceScript* instance; void Reset() override { - events.Reset(); + _scheduler.CancelAll(); instance->SetBossState(DATA_MAULGAR, NOT_STARTED); } @@ -347,9 +339,22 @@ struct boss_blindeye_the_seer : public ScriptedAI me->SetInCombatWithZone(); instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - events.ScheduleEvent(EVENT_ADD_ABILITY1, 11s); - events.ScheduleEvent(EVENT_ADD_ABILITY2, 30s); - events.ScheduleEvent(EVENT_ADD_ABILITY3, 31s); + _scheduler.Schedule(7200ms, [this](TaskContext context) + { + if (Unit* target = DoSelectLowestHpFriendly(60.0f, 50000)) + { + DoCast(target, SPELL_HEAL); + } + context.Repeat(7200ms); + }).Schedule(37500s, [this](TaskContext context) + { + DoCastSelf(SPELL_GREATER_PW_SHIELD); + _scheduler.Schedule(1200ms, [this](TaskContext) + { + DoCastSelf(SPELL_PRAYER_OH); + }); + context.Repeat(54500ms, 63s); + }); } void JustDied(Unit* /*killer*/) override @@ -362,31 +367,12 @@ struct boss_blindeye_the_seer : public ScriptedAI if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_ADD_ABILITY1: - if (Unit* target = DoSelectLowestHpFriendly(60.0f, 50000)) - { - DoCast(target, SPELL_HEAL); - } - events.ScheduleEvent(EVENT_ADD_ABILITY1, 6s); - break; - case EVENT_ADD_ABILITY2: - DoCastSelf(SPELL_GREATER_PW_SHIELD); - events.ScheduleEvent(EVENT_ADD_ABILITY2, 30s); - break; - case EVENT_ADD_ABILITY3: - me->CastSpell(me, SPELL_PRAYER_OH, false); - events.ScheduleEvent(EVENT_ADD_ABILITY3, 30s); - break; - } + _scheduler.Update(diff); DoMeleeAttackIfReady(); } +private: + TaskScheduler _scheduler; }; struct boss_krosh_firehand : public ScriptedAI @@ -394,14 +380,17 @@ struct boss_krosh_firehand : public ScriptedAI boss_krosh_firehand(Creature* creature) : ScriptedAI(creature) { instance = creature->GetInstanceScript(); + _scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } - EventMap events; InstanceScript* instance; void Reset() override { - events.Reset(); + _scheduler.CancelAll(); instance->SetBossState(DATA_MAULGAR, NOT_STARTED); } @@ -419,9 +408,22 @@ struct boss_krosh_firehand : public ScriptedAI me->SetInCombatWithZone(); instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - events.ScheduleEvent(EVENT_ADD_ABILITY1, 1500ms); //spellshield - events.ScheduleEvent(EVENT_ADD_ABILITY2, 3500ms); //greater fireball - events.ScheduleEvent(EVENT_ADD_ABILITY3, 8s); //blast wave (needs to check for players in range) + _scheduler.Schedule(1200ms, [this](TaskContext context) + { + DoCastSelf(SPELL_SPELLSHIELD); + context.Repeat(30300ms); + }).Schedule(3600ms, [this](TaskContext context) + { + DoCastVictim(SPELL_GREATER_FIREBALL); + context.Repeat(3600ms); + }).Schedule(7200ms, [this](TaskContext context) + { + if (me->SelectNearestPlayer(15.0f)) + { + DoCastAOE(SPELL_BLAST_WAVE); + } + context.Repeat(7200ms); + }); } void JustDied(Unit* /*killer*/) override @@ -434,31 +436,12 @@ struct boss_krosh_firehand : public ScriptedAI if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_ADD_ABILITY1: - DoCastSelf(SPELL_SPELLSHIELD); - events.ScheduleEvent(EVENT_ADD_ABILITY1, 30s); - break; - case EVENT_ADD_ABILITY2: - DoCastVictim(SPELL_GREATER_FIREBALL); - events.ScheduleEvent(EVENT_ADD_ABILITY2, 1s); - break; - case EVENT_ADD_ABILITY3: - if (me->SelectNearestPlayer(15.0f)) - { - DoCastAOE(SPELL_BLAST_WAVE); - } - events.ScheduleEvent(EVENT_ADD_ABILITY3, 8s); - break; - } + _scheduler.Update(diff); DoMeleeAttackIfReady(); } +private: + TaskScheduler _scheduler; }; void AddSC_boss_high_king_maulgar() diff --git a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp index 4718cc6fb..91a59b4f6 100644 --- a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp @@ -144,7 +144,28 @@ private: }; +class spell_gargolmar_retalliation : public AuraScript +{ + PrepareAuraScript(spell_gargolmar_retalliation); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (!eventInfo.GetActor() || !eventInfo.GetProcTarget()) + { + return false; + } + + return GetTarget()->isInFront(eventInfo.GetActor(), M_PI); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_gargolmar_retalliation::CheckProc); + } +}; + void AddSC_boss_watchkeeper_gargolmar() { RegisterHellfireRampartsCreatureAI(boss_watchkeeper_gargolmar); + RegisterSpellScript(spell_gargolmar_retalliation); }