diff --git a/data/sql/updates/db_world/2022_06_02_00.sql b/data/sql/updates/db_world/2022_06_02_00.sql new file mode 100644 index 000000000..b8bfd5339 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_02_00.sql @@ -0,0 +1,11 @@ +-- DB update 2022_05_30_02 -> 2022_06_02_00 +-- +DELETE FROM `command` WHERE `name` IN ("deserter instance add", "deserter instance remove", "deserter instance remove all", "deserter bg add", "deserter bg remove", "deserter bg remove all"); + +INSERT INTO `command` (`name`, `security`, `help`) VALUES +("deserter instance add", 3, "Syntax: .deserter instance add $playerName $time \n\n Adds the instance deserter debuff to a player or your target with $time duration."), +("deserter instance remove", 3, "Syntax: .deserter instance remove $playerName \n\n Removes the instance deserter debuff from a player or your target."), +("deserter instance remove all", 3, "Syntax: .deserter instance remove all \n\n Removes the instance deserter debuff from all online and offline players."), +("deserter bg add", 3, "Syntax: .deserter bg add $playerName $time \n\n Adds the bg deserter debuff to a player or your target with $time duration."), +("deserter bg remove", 3, "Syntax: .deserter bg remove $playerName \n\n Removes the bg deserter debuff from a player or your target."), +("deserter bg remove all", 3, "Syntax: .deserter bg remove all \n\n Removes the bg deserter debuff from all online and offline players."); diff --git a/data/sql/updates/db_world/2022_06_02_01.sql b/data/sql/updates/db_world/2022_06_02_01.sql new file mode 100644 index 000000000..c70b7659f --- /dev/null +++ b/data/sql/updates/db_world/2022_06_02_01.sql @@ -0,0 +1,14 @@ +-- DB update 2022_06_02_00 -> 2022_06_02_01 +-- Condition for Loh'atu gossip +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=3481; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15,3481,0,0,0,8,0,5535,0,0,0,0,0,'','Show gossip option 0 if player has quest ''Spiritual Unrest'' rewarded'), +(15,3481,0,0,0,8,0,5536,0,0,0,0,0,'','Show gossip option 0 if player has quest ''A Land Filled with Hatred'' rewarded'), +(15,3481,1,0,0,8,0,5535,0,0,0,0,0,'','Show gossip option 1 if player has quest ''Spiritual Unrest'' rewarded'), +(15,3481,1,0,0,8,0,5536,0,0,0,0,0,'','Show gossip option 1 if player has quest ''A Land Filled with Hatred'' rewarded'), +(15,3481,2,0,0,8,0,5535,0,0,0,0,0,'','Show gossip option 2 if player has quest ''Spiritual Unrest'' rewarded'), +(15,3481,2,0,0,8,0,5536,0,0,0,0,0,'','Show gossip option 2 if player has quest ''A Land Filled with Hatred'' rewarded'), +(15,3481,3,0,0,8,0,5535,0,0,0,0,0,'','Show gossip option 3 if player has quest ''Spiritual Unrest'' rewarded'), +(15,3481,3,0,0,8,0,5536,0,0,0,0,0,'','Show gossip option 3 if player has quest ''A Land Filled with Hatred'' rewarded'), +(15,3481,4,0,0,8,0,5535,0,0,0,0,0,'','Show gossip option 4 if player has quest ''Spiritual Unrest'' rewarded'), +(15,3481,4,0,0,8,0,5536,0,0,0,0,0,'','Show gossip option 4 if player has quest ''A Land Filled with Hatred'' rewarded'); diff --git a/data/sql/updates/db_world/2022_06_03_00.sql b/data/sql/updates/db_world/2022_06_03_00.sql new file mode 100644 index 000000000..3f9ba24fa --- /dev/null +++ b/data/sql/updates/db_world/2022_06_03_00.sql @@ -0,0 +1,17 @@ +-- DB update 2022_06_02_01 -> 2022_06_03_00 + +DELETE FROM `spell_proc_event` WHERE `entry` = '71602'; +DELETE FROM `spell_proc_event` WHERE `entry` = '75465'; +DELETE FROM `spell_proc_event` WHERE `entry` = '71845'; +DELETE FROM `spell_proc_event` WHERE `entry` = '71846'; +DELETE FROM `spell_proc_event` WHERE `entry` = '72419'; +DELETE FROM `spell_proc_event` WHERE `entry` = '75474'; + +UPDATE `item_template` SET `spellppmRate_1` = 10 WHERE (`entry` = 50353); +UPDATE `item_template` SET `spellppmRate_1` = 10 WHERE (`entry` = 50348); +UPDATE `item_template` SET `spellppmRate_1` = 2 WHERE (`entry` = 49992); +UPDATE `item_template` SET `spellppmRate_1` = 2 WHERE (`entry` = 50648); +UPDATE `item_template` SET `spellppmRate_1` = 10 WHERE (`entry` = 50400); +UPDATE `item_template` SET `spellppmRate_1` = 10 WHERE (`entry` = 50399); +UPDATE `item_template` SET `spellppmRate_1` = 10 WHERE (`entry` = 54572); +UPDATE `item_template` SET `spellppmRate_1` = 10 WHERE (`entry` = 54588); diff --git a/data/sql/updates/db_world/2022_06_03_01.sql b/data/sql/updates/db_world/2022_06_03_01.sql new file mode 100644 index 000000000..884fa1b1e --- /dev/null +++ b/data/sql/updates/db_world/2022_06_03_01.sql @@ -0,0 +1,9 @@ +-- DB update 2022_06_03_00 -> 2022_06_03_01 +-- +UPDATE `creature_template` SET `speed_run` = 1.14286, `speed_walk` = 1.32 WHERE `entry` = 14517; + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_batrider_bomb'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(23970, 'spell_batrider_bomb'); + +UPDATE `gameobject_template` SET `Data2` = 6 WHERE `entry` = 180125; diff --git a/src/server/scripts/Commands/cs_deserter.cpp b/src/server/scripts/Commands/cs_deserter.cpp index 6018cee1b..0cc93a62b 100644 --- a/src/server/scripts/Commands/cs_deserter.cpp +++ b/src/server/scripts/Commands/cs_deserter.cpp @@ -49,13 +49,15 @@ public: { static ChatCommandTable deserterInstanceCommandTable = { - { "add", HandleDeserterInstanceAdd, SEC_ADMINISTRATOR, Console::No }, - { "remove", HandleDeserterInstanceRemove, SEC_ADMINISTRATOR, Console::No } + { "add", HandleDeserterInstanceAdd, SEC_ADMINISTRATOR, Console::Yes }, + { "remove all", HandleDeserterInstanceRemoveAll, SEC_ADMINISTRATOR, Console::Yes }, + { "remove", HandleDeserterInstanceRemove, SEC_ADMINISTRATOR, Console::Yes } }; static ChatCommandTable deserterBGCommandTable = { - { "add", HandleDeserterBGAdd, SEC_ADMINISTRATOR, Console::No }, - { "remove", HandleDeserterBGRemove, SEC_ADMINISTRATOR, Console::No } + { "add", HandleDeserterBGAdd, SEC_ADMINISTRATOR, Console::Yes }, + { "remove all", HandleDeserterBGRemoveAll, SEC_ADMINISTRATOR, Console::Yes }, + { "remove", HandleDeserterBGRemove, SEC_ADMINISTRATOR, Console::Yes } }; static ChatCommandTable deserterCommandTable = @@ -90,9 +92,13 @@ public: * .deserter bg add 3600 (one hour) * @endcode */ - static bool HandleDeserterAdd(ChatHandler* handler, uint32 time, bool isInstance) + static bool HandleDeserterAdd(ChatHandler* handler, Optional player, uint32 time, bool isInstance) { - Player* player = handler->getSelectedPlayer(); + if (!player) + { + player = PlayerIdentifier::FromTargetOrSelf(handler); + } + if (!player) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -107,15 +113,42 @@ public: return false; } - Aura* aura = player->AddAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER, player); + Player* target = player->GetConnectedPlayer(); - if (!aura) + if (target) { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; + Aura* aura = target->AddAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER, target); + + if (!aura) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + aura->SetDuration(time * IN_MILLISECONDS); + + return true; } - aura->SetDuration(time * IN_MILLISECONDS); + + uint8 index = 0; + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AURA); + stmt->SetData(index++, player->GetGUID().GetCounter()); + stmt->SetData(index++, player->GetGUID().GetCounter()); + stmt->SetData(index++, 0); + stmt->SetData(index++, isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER); + stmt->SetData(index++, 1); + stmt->SetData(index++, 1); + stmt->SetData(index++, 1); + stmt->SetData(index++, 0); + stmt->SetData(index++, 0); + stmt->SetData(index++, 0); + stmt->SetData(index++, 0); + stmt->SetData(index++, 0); + stmt->SetData(index++, 0); + stmt->SetData(index++, isInstance ? 1800000 : 900000); + stmt->SetData(index++, time * 1000); + stmt->SetData(index, 0); + CharacterDatabase.Execute(stmt); return true; } @@ -139,9 +172,13 @@ public: * .deserter bg remove * @endcode */ - static bool HandleDeserterRemove(ChatHandler* handler, bool isInstance) + static bool HandleDeserterRemove(ChatHandler* handler, Optional player, bool isInstance) { - Player* player = handler->getSelectedPlayer(); + if (!player) + { + player = PlayerIdentifier::FromTargetOrSelf(handler); + } + if (!player) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -149,33 +186,71 @@ public: return false; } - player->RemoveAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER); + Player* target = player->GetConnectedPlayer(); + + if (target) + { + target->RemoveAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER); + return true; + } + + CharacterDatabase.Query("DELETE FROM character_aura WHERE guid = {} AND spell = {}", player->GetGUID().GetCounter(), isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER); return true; } - /// @sa HandleDeserterAdd() - static bool HandleDeserterInstanceAdd(ChatHandler* handler, uint32 time) + static bool HandleDeserterRemoveAll(ChatHandler* handler, bool isInstance) { - return HandleDeserterAdd(handler, time, true); + CharacterDatabase.Query("DELETE FROM character_aura WHERE spell = {} AND remainTime <= 1800000", isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER); + + std::shared_lock lock(*HashMapHolder::GetLock()); + HashMapHolder::MapType const& onlinePlayerList = ObjectAccessor::GetPlayers(); + for (HashMapHolder::MapType::const_iterator itr = onlinePlayerList.begin(); itr != onlinePlayerList.end(); ++itr) + { + Player* player = itr->second; + Aura* aura = player->GetAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER); + if (aura && aura->GetDuration() <= 1800000) + { + player->RemoveAura(isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER); + } + } + + handler->PSendSysMessage("%s Deserter has been removed from all players", isInstance ? "Instance" : "Battleground"); + return true; } /// @sa HandleDeserterAdd() - static bool HandleDeserterBGAdd(ChatHandler* handler, uint32 time) + static bool HandleDeserterInstanceAdd(ChatHandler* handler, Optional player, uint32 time) { - return HandleDeserterAdd(handler, time, false); + return HandleDeserterAdd(handler, player, time, true); + } + + /// @sa HandleDeserterAdd() + static bool HandleDeserterBGAdd(ChatHandler* handler, Optional player, uint32 time) + { + return HandleDeserterAdd(handler, player, time, false); } /// @sa HandleDeserterRemove() - static bool HandleDeserterInstanceRemove(ChatHandler* handler) + static bool HandleDeserterInstanceRemove(ChatHandler* handler, Optional player) { - return HandleDeserterRemove(handler, true); + return HandleDeserterRemove(handler, player, true); } /// @sa HandleDeserterRemove() - static bool HandleDeserterBGRemove(ChatHandler* handler) + static bool HandleDeserterBGRemove(ChatHandler* handler, Optional player) { - return HandleDeserterRemove(handler, false); + return HandleDeserterRemove(handler, player, false); + } + + static bool HandleDeserterInstanceRemoveAll(ChatHandler* handler) + { + return HandleDeserterRemoveAll(handler, true); + } + + static bool HandleDeserterBGRemoveAll(ChatHandler* handler) + { + return HandleDeserterRemoveAll(handler, false); } }; diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp index b6075f954..ebb69ebee 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp @@ -15,8 +15,11 @@ * with this program. If not, see . */ +#include "GameObjectAI.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "TaskScheduler.h" #include "zulgurub.h" enum Says @@ -49,7 +52,8 @@ enum Spells SPELL_GREATER_HEAL = 23954, // Batriders Spell - SPELL_BOMB = 40332 // Wrong ID but Magmadars bomb is not working... + SPELL_THROW_LIQUID_FIRE = 23970, + SPELL_SUMMON_LIQUID_FIRE = 23971 }; enum BatIds @@ -93,214 +97,227 @@ Position const SpawnBat[6] = { -12293.6220f, -1380.2640f, 144.8304f, 5.483f } }; -class boss_jeklik : public CreatureScript +struct boss_jeklik : public BossAI { -public: - boss_jeklik() : CreatureScript("boss_jeklik") { } + boss_jeklik(Creature* creature) : BossAI(creature, DATA_JEKLIK) { } - struct boss_jeklikAI : public BossAI + void Reset() override { - boss_jeklikAI(Creature* creature) : BossAI(creature, DATA_JEKLIK) { } + DoCastSelf(SPELL_GREEN_CHANNELING); + me->SetHover(false); + me->SetDisableGravity(false); + _Reset(); + } - void Reset() override + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } + + void EnterEvadeMode(EvadeReason why) override + { + const Position homePos = me->GetHomePosition(); + me->NearTeleportTo(homePos.GetPositionX(), homePos.GetPositionY(), homePos.GetPositionZ(), homePos.GetOrientation()); + BossAI::EnterEvadeMode(why); + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + me->RemoveAurasDueToSpell(SPELL_GREEN_CHANNELING); + me->SetHover(true); + me->SetDisableGravity(true); + me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + DoCastSelf(SPELL_BAT_FORM); + events.SetPhase(PHASE_ONE); + + events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(10000, 20000), PHASE_ONE); + events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(5000, 15000), PHASE_ONE); + events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(5000, 15000), PHASE_ONE); + events.ScheduleEvent(EVENT_SONIC_BURST, urand(5000, 15000), PHASE_ONE); + events.ScheduleEvent(EVENT_SWOOP, 20000, PHASE_ONE); + events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE); + } + + void DamageTaken(Unit* /*who*/, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override + { + if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(50)) { - DoCastSelf(SPELL_GREEN_CHANNELING); - _Reset(); + me->RemoveAurasDueToSpell(SPELL_BAT_FORM); + me->SetHover(false); + me->SetDisableGravity(false); + DoResetThreat(); + events.SetPhase(PHASE_TWO); + events.CancelEventGroup(PHASE_ONE); + + events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(5000, 15000), PHASE_TWO); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(10000, 15000), PHASE_TWO); + events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(25000, 35000), PHASE_TWO); + events.ScheduleEvent(EVENT_MIND_FLAY, urand(10000, 30000), PHASE_TWO); + events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO); + events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, 10000, PHASE_TWO); + + return; } + } - void JustDied(Unit* /*killer*/) override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _JustDied(); - Talk(SAY_DEATH); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - Talk(SAY_AGGRO); - me->RemoveAurasDueToSpell(SPELL_GREEN_CHANNELING); - me->SetDisableGravity(true); - DoCastSelf(SPELL_BAT_FORM); - events.SetPhase(PHASE_ONE); - - events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(10000, 20000), PHASE_ONE); - events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(5000, 15000), PHASE_ONE); - events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(5000, 15000), PHASE_ONE); - events.ScheduleEvent(EVENT_SONIC_BURST, urand(5000, 15000), PHASE_ONE); - events.ScheduleEvent(EVENT_SWOOP, 20000, PHASE_ONE); - events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE); - } - - void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override - { - if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(50)) + switch (eventId) { - me->RemoveAurasDueToSpell(SPELL_BAT_FORM); - me->SetDisableGravity(false); - DoResetThreat(); - events.SetPhase(PHASE_TWO); - - events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(5000, 15000), PHASE_TWO); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(10000, 15000), PHASE_TWO); - events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(25000, 35000), PHASE_TWO); - events.ScheduleEvent(EVENT_MIND_FLAY, urand(10000, 30000), PHASE_TWO); - events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO); - events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, 10000, PHASE_TWO); - - return; - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - // Phase one - case EVENT_CHARGE_JEKLIK: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - DoCast(target, SPELL_CHARGE); - AttackStart(target); - } - events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(15000, 30000), PHASE_ONE); - break; - case EVENT_PIERCE_ARMOR: - DoCastVictim(SPELL_PIERCE_ARMOR); - events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(20000, 30000), PHASE_ONE); - break; - case EVENT_BLOOD_LEECH: - DoCastVictim(SPELL_BLOOD_LEECH); - events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(10000, 20000), PHASE_ONE); - break; - case EVENT_SONIC_BURST: - DoCastVictim(SPELL_SONIC_BURST); - events.ScheduleEvent(EVENT_SONIC_BURST, urand(20000, 30000), PHASE_ONE); - break; - case EVENT_SWOOP: - DoCastVictim(SPELL_SWOOP); - events.ScheduleEvent(EVENT_SWOOP, urand(20000, 30000), PHASE_ONE); - break; - case EVENT_SPAWN_BATS: - Talk(EMOTE_SUMMON_BATS); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - for (uint8 i = 0; i < 6; ++i) - if (Creature* bat = me->SummonCreature(NPC_BLOODSEEKER_BAT, SpawnBat[i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000)) - bat->AI()->AttackStart(target); - events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE); - break; + // Phase one + case EVENT_CHARGE_JEKLIK: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + DoCast(target, SPELL_CHARGE); + AttackStart(target); + } + events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(15000, 30000), PHASE_ONE); + break; + case EVENT_PIERCE_ARMOR: + DoCastVictim(SPELL_PIERCE_ARMOR); + events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(20000, 30000), PHASE_ONE); + break; + case EVENT_BLOOD_LEECH: + DoCastVictim(SPELL_BLOOD_LEECH); + events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(10000, 20000), PHASE_ONE); + break; + case EVENT_SONIC_BURST: + DoCastVictim(SPELL_SONIC_BURST); + events.ScheduleEvent(EVENT_SONIC_BURST, urand(20000, 30000), PHASE_ONE); + break; + case EVENT_SWOOP: + DoCastVictim(SPELL_SWOOP); + events.ScheduleEvent(EVENT_SWOOP, urand(20000, 30000), PHASE_ONE); + break; + case EVENT_SPAWN_BATS: + Talk(EMOTE_SUMMON_BATS); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + for (uint8 i = 0; i < 6; ++i) + if (Creature* bat = me->SummonCreature(NPC_BLOODSEEKER_BAT, SpawnBat[i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000)) + bat->AI()->AttackStart(target); + events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE); + break; //Phase two - case EVENT_CURSE_OF_BLOOD: - DoCastSelf(SPELL_CURSE_OF_BLOOD); - events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(25000, 30000), PHASE_TWO); - break; - case EVENT_PSYCHIC_SCREAM: - DoCastVictim(SPELL_PSYCHIC_SCREAM); - events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(35000, 45000), PHASE_TWO); - break; - case EVENT_SHADOW_WORD_PAIN: - DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN, 0, true); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(12000, 18000), PHASE_TWO); - break; - case EVENT_MIND_FLAY: - DoCastVictim(SPELL_MIND_FLAY); - events.ScheduleEvent(EVENT_MIND_FLAY, urand(20000, 40000), PHASE_TWO); - break; - case EVENT_GREATER_HEAL: - Talk(EMOTE_GREAT_HEAL); - me->InterruptNonMeleeSpells(false); - DoCastSelf(SPELL_GREATER_HEAL); - events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO); - break; - case EVENT_SPAWN_FLYING_BATS: - Talk(SAY_CALL_RIDERS); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - if (Creature* flyingBat = me->SummonCreature(NPC_FRENZIED_BAT, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000)) - flyingBat->AI()->AttackStart(target); - events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, urand(10000, 15000), PHASE_TWO); - break; - default: - break; - } + case EVENT_CURSE_OF_BLOOD: + DoCastSelf(SPELL_CURSE_OF_BLOOD); + events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(25000, 30000), PHASE_TWO); + break; + case EVENT_PSYCHIC_SCREAM: + DoCastVictim(SPELL_PSYCHIC_SCREAM); + events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(35000, 45000), PHASE_TWO); + break; + case EVENT_SHADOW_WORD_PAIN: + DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN, 0, true); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(12000, 18000), PHASE_TWO); + break; + case EVENT_MIND_FLAY: + DoCastVictim(SPELL_MIND_FLAY); + events.ScheduleEvent(EVENT_MIND_FLAY, urand(20000, 40000), PHASE_TWO); + break; + case EVENT_GREATER_HEAL: + Talk(EMOTE_GREAT_HEAL); + me->InterruptNonMeleeSpells(false); + DoCastSelf(SPELL_GREATER_HEAL); + events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO); + break; + case EVENT_SPAWN_FLYING_BATS: + Talk(SAY_CALL_RIDERS); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + if (Creature* flyingBat = me->SummonCreature(NPC_FRENZIED_BAT, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000)) + flyingBat->AI()->DoZoneInCombat(); + events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, urand(10000, 15000), PHASE_TWO); + break; + default: + break; } - - DoMeleeAttackIfReady(); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulGurubAI(creature); + DoMeleeAttackIfReady(); } }; // Flying Bat -class npc_batrider : public CreatureScript +struct npc_batrider : public ScriptedAI { -public: - npc_batrider() : CreatureScript("npc_batrider") { } - - struct npc_batriderAI : public ScriptedAI + npc_batrider(Creature* creature) : ScriptedAI(creature) { - npc_batriderAI(Creature* creature) : ScriptedAI(creature) { } - - uint32 Bomb_Timer; - - void Reset() override + _scheduler.SetValidator([this] { - Bomb_Timer = 2000; - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->AddUnitState(UNIT_STATE_ROOT); - } + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } - void EnterCombat(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (Bomb_Timer <= diff) - { - std::list targets; - SelectTargetList(targets, 1, SelectTargetMethod::Random, 500.0f, true); - if (!targets.empty()) - { - if (targets.size() > 1) - { - targets.resize(1); - } - } - - for (std::list::iterator itr = targets.begin(); itr != targets.end(); ++itr) - { - me->CastSpell((*itr), SPELL_BOMB); - } - - Bomb_Timer = 7000; - } - else - Bomb_Timer -= diff; - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetZulGurubAI(creature); + _scheduler.CancelAll(); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetHover(true); + me->SetDisableGravity(true); + me->AddUnitState(UNIT_STATE_ROOT); + } + + void EnterCombat(Unit* /*who*/) override + { + _scheduler.Schedule(2s, [this](TaskContext context) + { + DoCastRandomTarget(SPELL_THROW_LIQUID_FIRE); + context.Repeat(7s); + }); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _scheduler.Update(diff); + } + +private: + TaskScheduler _scheduler; +}; + +class spell_batrider_bomb : public SpellScript +{ + PrepareSpellScript(spell_batrider_bomb); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SUMMON_LIQUID_FIRE }); + } + + void HandleScriptEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + if (Unit* target = GetHitUnit()) + { + target->CastSpell(target, SPELL_SUMMON_LIQUID_FIRE, true); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_batrider_bomb::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); } }; void AddSC_boss_jeklik() { - new boss_jeklik(); - new npc_batrider(); + RegisterCreatureAI(boss_jeklik); + RegisterCreatureAI(npc_batrider); + RegisterSpellScript(spell_batrider_bomb); }