diff options
| -rw-r--r-- | sql/updates/world/3.3.5/2025_12_14_03_world.sql | 2 | ||||
| -rw-r--r-- | sql/updates/world/3.3.5/2025_12_15_00_world.sql | 29 | ||||
| -rw-r--r-- | sql/updates/world/3.3.5/2025_12_17_00_world.sql | 63 | ||||
| -rw-r--r-- | src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp | 844 |
4 files changed, 537 insertions, 401 deletions
diff --git a/sql/updates/world/3.3.5/2025_12_14_03_world.sql b/sql/updates/world/3.3.5/2025_12_14_03_world.sql new file mode 100644 index 00000000000..a2e8b2d5de5 --- /dev/null +++ b/sql/updates/world/3.3.5/2025_12_14_03_world.sql @@ -0,0 +1,2 @@ +-- +UPDATE `gameobject` SET `phaseMask`=`phaseMask`|8 WHERE `guid` IN (151102,151103,151104,151105,151106) AND `id` IN (202184,202347,202348,202349,202350); diff --git a/sql/updates/world/3.3.5/2025_12_15_00_world.sql b/sql/updates/world/3.3.5/2025_12_15_00_world.sql new file mode 100644 index 00000000000..693c0d5abca --- /dev/null +++ b/sql/updates/world/3.3.5/2025_12_15_00_world.sql @@ -0,0 +1,29 @@ +-- +UPDATE `quest_offer_reward` SET `Emote1`=0, `Emote2`=0, `Emote3`=0, `Emote4`=0, `RewardText`="Wintergarde is saved because of you, $N. To think that one woman could so swiftly turn the tides of battle is hard for most to comprehend; yet here we are - victorious! You have managed to restore the faith of these people and earned the respect of your commanding officers.$B$B<Halford salutes.>$B$BLord Fordragon has returned to Angrathar to prepare our forces for the destruction of the Wrathgate and has requested that you join him! I couldn't recommend a better soldier for the job, $N.", `VerifiedBuild`=12340 WHERE `ID`=12473; + +UPDATE `gameobject` SET `position_x`=-8750.37, `position_y`=-2258.32, `position_z`=155.634, `orientation`=0.453786 WHERE `guid`=18679 AND `id`=1731; + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=17 AND `SourceEntry`=40969; + +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=10928; +DELETE FROM `smart_scripts` WHERE `entryorguid`=10928 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('10928','0','0','0','0','0','100','0','2000','4000','4000','7000','0','11','32202','0','0','0','0','0','2','0','0','0','0','0','0','0','0',"Succubus Minion - In Combat - Cast 'Lash of Pain'"); + +UPDATE `graveyard_zone` SET `GhostZone`=139, `Comment`="Western Plaguelands, Hearthglen - Eastern Plaguelands" WHERE `ID`=1451; + +UPDATE `gameobject_template_addon` SET `faction`=474 WHERE `entry`=184085; + +UPDATE `creature` SET `spawnMask`=`spawnMask`|1 WHERE `guid` IN (68283,68284) AND `id`=31104; + +UPDATE `quest_template_addon` SET `PrevQuestID`=9898 WHERE `ID`=9899; + +DELETE FROM `creature` WHERE `guid`=3564 AND `id`=1135; +DELETE FROM `spawn_group` WHERE `spawnType`=0 AND `spawnId`=3564; +INSERT INTO `creature` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `StringId`, `VerifiedBuild`) VALUES('3564','1135','0','0','0','1','1','0','0','-5625.61','-178.399','365.835','4.7822','300','0','0','120','0','0','0','0','0','',NULL,'0'); +INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`) VALUES('2','0','3564'); + +UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`|4 WHERE `entry` IN (31368, 29306); + +UPDATE `npc_vendor` SET `incrtime`=9000 WHERE `entry`=14860 AND `item`=11027; + +UPDATE `quest_template_addon` SET `PrevQuestID`=11239 WHERE `ID`=11432; diff --git a/sql/updates/world/3.3.5/2025_12_17_00_world.sql b/sql/updates/world/3.3.5/2025_12_17_00_world.sql new file mode 100644 index 00000000000..33e02ad8e1f --- /dev/null +++ b/sql/updates/world/3.3.5/2025_12_17_00_world.sql @@ -0,0 +1,63 @@ +-- Flame Quills +UPDATE `spell_target_position` SET `PositionX` = 244.1, `PositionY` = 49.9153, `PositionZ` = 20.1795, `VerifiedBuild` = 26365 WHERE `ID` = 34269; +UPDATE `spell_target_position` SET `PositionX` = 260.572, `PositionY` = 67.3991, `PositionZ` = 20.1799, `VerifiedBuild` = 26365 WHERE `ID` = 34270; +UPDATE `spell_target_position` SET `PositionX` = 279.967, `PositionY` = 83.4335, `PositionZ` = 20.1794, `VerifiedBuild` = 26365 WHERE `ID` = 34271; +UPDATE `spell_target_position` SET `PositionX` = 306.56, `PositionY` = 92.0271, `PositionZ` = 20.1798, `VerifiedBuild` = 26365 WHERE `ID` = 34272; +UPDATE `spell_target_position` SET `PositionX` = 332.415, `PositionY` = 86.6608, `PositionZ` = 20.3436, `VerifiedBuild` = 26365 WHERE `ID` = 34273; +UPDATE `spell_target_position` SET `PositionX` = 358.834, `PositionY` = 90.569, `PositionZ` = 20.032, `VerifiedBuild` = 26365 WHERE `ID` = 34274; +UPDATE `spell_target_position` SET `PositionX` = 382.319, `PositionY` = 83.0517, `PositionZ` = 20.1796, `VerifiedBuild` = 26365 WHERE `ID` = 34275; +UPDATE `spell_target_position` SET `PositionX` = 403.761, `PositionY` = 69.5173, `PositionZ` = 20.1796, `VerifiedBuild` = 26365 WHERE `ID` = 34276; +UPDATE `spell_target_position` SET `PositionX` = 402.296, `PositionY` = 44.3146, `PositionZ` = 20.1797, `VerifiedBuild` = 26365 WHERE `ID` = 34277; +UPDATE `spell_target_position` SET `PositionX` = 422.53, `PositionY` = 26.9552, `PositionZ` = 20.1798, `VerifiedBuild` = 26365 WHERE `ID` = 34278; +UPDATE `spell_target_position` SET `PositionX` = 261.468, `PositionY` = -73.6918, `PositionZ` = 20.1795, `VerifiedBuild` = 26365 WHERE `ID` = 34279; +UPDATE `spell_target_position` SET `PositionX` = 249.358, `PositionY` = -52.7987, `PositionZ` = 20.1795, `VerifiedBuild` = 26365 WHERE `ID` = 34280; +UPDATE `spell_target_position` SET `PositionX` = 424.829, `PositionY` = 1.01505, `PositionZ` = 20.18, `VerifiedBuild` = 26365 WHERE `ID` = 34281; +UPDATE `spell_target_position` SET `PositionX` = 423.478, `PositionY` = -23.9648, `PositionZ` = 20.1799, `VerifiedBuild` = 26365 WHERE `ID` = 34282; +UPDATE `spell_target_position` SET `PositionX` = 283.424, `PositionY` = -85.9517, `PositionZ` = 20.1798, `VerifiedBuild` = 26365 WHERE `ID` = 34283; +UPDATE `spell_target_position` SET `PositionX` = 404.622, `PositionY` = -42.1397, `PositionZ` = 20.1798, `VerifiedBuild` = 26365 WHERE `ID` = 34284; +UPDATE `spell_target_position` SET `PositionX` = 309.55, `PositionY` = -89.3632, `PositionZ` = 20.1796, `VerifiedBuild` = 26365 WHERE `ID` = 34285; +UPDATE `spell_target_position` SET `PositionX` = 403.462, `PositionY` = -67.8334, `PositionZ` = 20.18, `VerifiedBuild` = 26365 WHERE `ID` = 34286; +UPDATE `spell_target_position` SET `PositionX` = 335.024, `PositionY` = -83.21, `PositionZ` = 20.388, `VerifiedBuild` = 26365 WHERE `ID` = 34287; +UPDATE `spell_target_position` SET `PositionX` = 384.251, `PositionY` = -84.3709, `PositionZ` = 20.18, `VerifiedBuild` = 26365 WHERE `ID` = 34288; +UPDATE `spell_target_position` SET `PositionX` = 359.997, `PositionY` = -92.7042, `PositionZ` = 20.0127, `VerifiedBuild` = 26365 WHERE `ID` = 34289; +UPDATE `spell_target_position` SET `PositionX` = 241.768, `PositionY` = 24.8276, `PositionY` = 20.3438, `VerifiedBuild` = 26365 WHERE `ID` = 34314; +UPDATE `spell_target_position` SET `PositionX` = 239.111, `PositionY` = -1.59108, `PositionY` = 27.0491, `VerifiedBuild` = 26365 WHERE `ID` = 34315; +UPDATE `spell_target_position` SET `PositionX` = 241.073, `PositionY` = -27.0846, `PositionY` = 20.1794, `VerifiedBuild` = 26365 WHERE `ID` = 34316; + +-- Ashtongue Ruse +UPDATE `spell_dbc` SET `ProcChance` = 101, `Effect1` = 6, `EffectImplicitTargetA1` = 1, `EffectApplyAuraName1` = 4, `DmgMultiplier1` = 1 WHERE `Id` = 39555; +UPDATE `spell_dbc` SET `ProcChance` = 101, `Effect1` = 16, `EffectImplicitTargetA1` = 1, `EffectMiscValue1` = 10946, `DmgMultiplier1` = 1 WHERE `Id` = 39701; + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_alar_ashtongue_ruse_master'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(39555, 'spell_alar_ashtongue_ruse_master'); + +UPDATE `quest_template_addon` SET `SpecialFlags` = 2 WHERE `ID` = 10946; + +-- Flame Patch +UPDATE `spell_dbc` SET `EffectApplyAuraName1` = 0 WHERE `Id` = 29218; + +-- Ember Blast (damage component) +UPDATE `spell_dbc` SET `ProcChance` = 101, `Effect1` = 2, `EffectBasePoints1` = 70000, `EffectImplicitTargetA1` = 38, `DmgMultiplier1` = 1, `DmgMultiplier2` = 1, `DmgMultiplier3` = 1 WHERE `Id` = 41910; + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 41910; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,41910,0,0,31,0,3,19514,0,0,0,0,"","Group 0: Spell 'Ember Blast' (Effect 0) targets creature 'Al'ar'"); + +DELETE FROM `spell_custom_attr` WHERE `entry` = 41910; +INSERT INTO `spell_custom_attr` (`entry`, `attributes`) VALUES +(41910, 32768); -- SPELL_ATTR0_CU_IGNORE_ARMOR + +-- Ember of Al'ar (copied from 39110, matches sniff) +UPDATE `spell_dbc` SET `ProcChance` = 101, `EquippedItemSubClassMask` = -1, `Effect1` = 28, `EffectDieSides1` = 1, `EffectImplicitTargetA1` = 72, `EffectRadiusIndex1` = 13, `EffectMiscValue1` = 19551, `EffectMiscValueB1` = 64, `DmgMultiplier1` = 1, `DmgMultiplier2` = 1, `DmgMultiplier3` = 1 WHERE `Id` = 41824; + +-- Platforms +UPDATE `creature` SET `StringId` = 'AlarPlatformTrigger1' WHERE `guid` = 144097 AND `id` = 15384; +UPDATE `creature` SET `StringId` = 'AlarPlatformTrigger2' WHERE `guid` = 144100 AND `id` = 15384; +UPDATE `creature` SET `StringId` = 'AlarPlatformTrigger3' WHERE `guid` = 144099 AND `id` = 15384; +UPDATE `creature` SET `StringId` = 'AlarPlatformTrigger4' WHERE `guid` = 144098 AND `id` = 15384; +UPDATE `creature` SET `StringId` = 'AlarCenterTrigger' WHERE `guid` = 144096 AND `id` = 15384; + +-- A'lar +UPDATE `creature_template` SET `flags_extra` = `flags_extra` |512 WHERE `entry` = 19514; +UPDATE `creature_template_movement` SET `Ground` = 1 WHERE `CreatureId` = 19514; diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 27df691b571..13863f16b2d 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -15,68 +15,124 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: boss_alar -SD%Complete: 95 -SDComment: -SDCategory: Tempest Keep, The Eye -EndScriptData */ +/* + * Everything related to Flame Quills requires sniff verification + * When moving to triggers, A'lar shouldn't move to exact trigger's position (positions in sniffs are always different) + * Combat timers requires to be revisited + * If summoned not on platform, embers should be summoned on the floor, not fall on the floor + * SPELL_ASHTONGUE_RUSE_MASTER doesn't appear in sniffs despite it's an aura. Maybe it was a wrong decision to use it + */ #include "ScriptMgr.h" #include "InstanceScript.h" #include "MotionMaster.h" -#include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "SpellAuraEffects.h" #include "SpellInfo.h" #include "SpellScript.h" -#include "TemporarySummon.h" #include "the_eye.h" -enum Spells +enum AlarSpells +{ + // Phase 1 + SPELL_SUMMON_PHOENIX_ADDS_1 = 41824, + SPELL_FLAME_QUILLS = 34229, + + // Phase 1 & 2 + SPELL_FLAME_BUFFET = 34121, + + // Transition + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_EMBER_BLAST_INVIS = 34341, + SPELL_FLIGHT_MODE = 31514, + SPELL_REBIRTH = 34342, + + // Phase 2 + SPELL_SUMMON_FLAME_RING = 29218, + SPELL_MELT_ARMOR = 35410, + SPELL_CHARGE = 35412, + SPELL_BERSERK = 26662, + + // Phase 2: Dive Bomb + SPELL_DIVE_BOMB_VISUAL = 35367, + SPELL_DIVE_BOMB = 35181, + SPELL_REBIRTH_2 = 35369, + SPELL_SUMMON_PHOENIX_ADDS_2 = 39110, + + // Ember of Al'ar + SPELL_EMBER_BLAST = 34133, + SPELL_EMBER_BLAST_DAMAGE = 41910, + + // Flame Patch + SPELL_FLAME_PATCH_PERIODIC = 35380, + + // Ruse of the Ashtongue + SPELL_ASHTONGUE_RUSE_DUMMY = 39527, + SPELL_ASHTONGUE_RUSE_MASTER = 39555, + SPELL_ASHTONGUE_RUSE_CREDIT = 39701 +}; + +enum AlarEvents { - SPELL_FLAME_BUFFET = 34121, // Flame Buffet - every 1, 5 secs in phase 1 if there is no victim in melee range and after Dive Bomb in phase 2 with same conditions - SPELL_FLAME_QUILLS = 34229, // Randomly after changing position in phase after watching tons of movies, set probability 20% - SPELL_REBIRTH = 34342, // Rebirth - beginning of second phase(after losing all health in phase 1) - SPELL_REBIRTH_2 = 35369, // Rebirth(another, without healing to full HP) - after Dive Bomb in phase 2 - SPELL_MELT_ARMOR = 35410, // Melt Armor - every 60 sec in phase 2 - SPELL_CHARGE = 35412, // Charge - 30 sec cooldown - SPELL_DIVE_BOMB_VISUAL = 35367, // Bosskillers says 30 sec cooldown, wowwiki says 30 sec colldown, DBM and BigWigs addons says ~47 sec - SPELL_DIVE_BOMB = 35181, // after watching tonns of movies, set cooldown to 40+rand()%5. - SPELL_BERSERK = 45078, // 10 minutes after phase 2 starts(id is wrong, but proper id is unknown) - - CREATURE_EMBER_OF_ALAR = 19551, // Al'ar summons one Ember of Al'ar every position change in phase 1 and two after Dive Bomb. Also in phase 2 when Ember of Al'ar dies, boss loses 3% health. - SPELL_EMBER_BLAST = 34133, // When Ember of Al'ar dies, it casts Ember Blast - - CREATURE_FLAME_PATCH_ALAR = 20602, // Flame Patch - every 30 sec in phase 2 - SPELL_FLAME_PATCH = 35380, // + // Phase 1 + EVENT_MOVE_TO_PLATFORM = 1, + EVENT_FLAME_QUILLS, + + // Phase 1 & 2 + EVENT_FLAME_BUFFET, + + // Transition + EVENT_TRANSITION_1, + EVENT_TRANSITION_2, + EVENT_TRANSITION_3, + EVENT_TRANSITION_4, + EVENT_TRANSITION_5, + + // Phase 2 + EVENT_FLAME_RING, + EVENT_MELT_ARMOR, + EVENT_CHARGE_ALAR, + EVENT_BERSERK, + + // Phase 2: Dive Bomb + EVENT_DIVE_BOMB_1, + EVENT_DIVE_BOMB_2, + EVENT_DIVE_BOMB_3, + EVENT_DIVE_BOMB_4, + EVENT_DIVE_BOMB_5, + EVENT_DIVE_BOMB_6, + EVENT_DIVE_BOMB_7 }; -static float waypoint[6][3] = +enum AlarPoints { - {340.15f, 58.65f, 17.71f}, - {388.09f, 31.54f, 20.18f}, - {388.18f, -32.85f, 20.18f}, - {340.29f, -60.19f, 17.72f}, - {332.0f, 0.01f, 39.0f}, // better not use the same xy coord - {331.0f, 0.01f, -2.39f} + POINT_PLATFORM = 0, + POINT_CENTER = 1, + POINT_RESSURRECTION = 2, + POINT_DIVE_BOMB = 3 }; -enum WaitEventType +enum AlarActions { - WE_NONE = 0, - WE_DUMMY = 1, - WE_PLATFORM = 2, - WE_QUILL = 3, - WE_DIE = 4, - WE_REVIVE = 5, - WE_CHARGE = 6, - WE_METEOR = 7, - WE_DIVE = 8, - WE_LAND = 9, - WE_SUMMON = 10 + ACTION_FLAME_QUILLS_END = 0 }; -uint32 const flameQuillsSpells[] = +enum AlarMisc +{ + MODEL_INVISIBLE = 14501 +}; + +static constexpr std::array<std::string_view, 4> AlarPlatformTrigger = +{ + "AlarPlatformTrigger1", + "AlarPlatformTrigger2", + "AlarPlatformTrigger3", + "AlarPlatformTrigger4" +}; + +static constexpr std::string_view CenterTriggerStringId = "AlarCenterTrigger"; + +static constexpr std::array<uint32, 24> FlameQuillsSpells = { 34269, 34270, @@ -104,449 +160,404 @@ uint32 const flameQuillsSpells[] = 34316 }; +Position const AlarRessurrectionPosition = { 333.589f, -0.768249f, -2.38949f }; + +// 19514 - Al'ar struct boss_alar : public BossAI { - boss_alar(Creature* creature) : BossAI(creature, DATA_ALAR) + boss_alar(Creature* creature) : BossAI(creature, DATA_ALAR), + _isFirstPhase(true), _isInTransition(false), _isFirstPlatform(true), _shouldSpawnEmber(false), _currentPlatform(0) { } + + void JustAppeared() override { - Initialize(); - DefaultMoveSpeedRate = creature->GetSpeedRate(MOVE_RUN); - DiveBomb_Timer = 0; - MeltArmor_Timer = 0; - Charge_Timer = 0; - FlamePatch_Timer = 0; + me->SetDisableGravity(true); } - void Initialize() + void JustEngagedWith(Unit* who) override { - Berserk_Timer = 1200000; - Platforms_Move_Timer = 0; + BossAI::JustEngagedWith(who); - Phase1 = true; - WaitEvent = WE_NONE; - WaitTimer = 0; - AfterMoving = false; - ForceMove = false; - ForceTimer = 5000; + me->GetMotionMaster()->MoveIdle(); + me->SetReactState(REACT_PASSIVE); - cur_wp = 4; + events.ScheduleEvent(EVENT_MOVE_TO_PLATFORM, 0s); } - WaitEventType WaitEvent; - uint32 WaitTimer; - - bool AfterMoving; - - uint32 Platforms_Move_Timer; - uint32 DiveBomb_Timer; - uint32 MeltArmor_Timer; - uint32 Charge_Timer; - uint32 FlamePatch_Timer; - uint32 Berserk_Timer; + void DamageTaken(Unit* /*doneBy*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + { + if (damage >= me->GetHealth() && _isFirstPhase) + { + damage = me->GetHealth() - 1; - float DefaultMoveSpeedRate; + if (_isInTransition) + return; - bool Phase1; - bool ForceMove; - uint32 ForceTimer; + _isInTransition = true; - int8 cur_wp; + events.Reset(); + events.ScheduleEvent(EVENT_TRANSITION_1, 0s); + } + } - void Reset() override + void MovementInform(uint32 type, uint32 id) override { - Initialize(); - _Reset(); - - me->SetDisplayId(me->GetNativeDisplayId()); - me->SetSpeedRate(MOVE_RUN, DefaultMoveSpeedRate); - //me->SetBoundingRadius(10); - //me->SetCombatReach(10); - me->SetDisableGravity(true); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->setActive(false); + if (type == POINT_MOTION_TYPE) + { + switch (id) + { + case POINT_PLATFORM: + events.ScheduleEvent(EVENT_FLAME_BUFFET, 1s); + me->SetControlled(true, UNIT_STATE_ROOT); + me->SetDisableGravity(false); + me->SetHover(false); + me->SetReactState(REACT_AGGRESSIVE); + break; + case POINT_CENTER: + events.ScheduleEvent(EVENT_FLAME_QUILLS, 0s); + break; + case POINT_DIVE_BOMB: + events.ScheduleEvent(EVENT_DIVE_BOMB_2, 2400ms); + break; + default: + break; + } + } } - void JustEngagedWith(Unit* who) override + void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override { - BossAI::JustEngagedWith(who); - me->SetDisableGravity(true); // after enterevademode will be set walk movement - me->setActive(true); + if (spellInfo->Id == SPELL_DIVE_BOMB_VISUAL) + events.ScheduleEvent(EVENT_DIVE_BOMB_3, 0s); } - void JustSummoned(Creature* summon) override + void OnSpellCast(SpellInfo const* spell) override { - if (summon->GetEntry() == CREATURE_EMBER_OF_ALAR) - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - summon->AI()->AttackStart(target); + /// !HACK: Creature is immune to fire spells, we set full health manually + if (spell->Id == SPELL_REBIRTH) + me->SetFullHealth(); } - void MoveInLineOfSight(Unit* /*who*/) override { } + void DoAction(int32 action) override + { + if (action == ACTION_FLAME_QUILLS_END) + { + DoMoveToPlatform(); + events.RescheduleEvent(EVENT_MOVE_TO_PLATFORM, 30s, 40s); + } + } - void AttackStart(Unit* who) override + void DoMoveToPlatform() { - if (Phase1) - AttackStartNoMove(who); - else - ScriptedAI::AttackStart(who); + if (Creature* trigger = me->FindNearestCreatureWithOptions(250.0f, { .StringId = AlarPlatformTrigger[_currentPlatform] })) + me->GetMotionMaster()->MovePoint(POINT_PLATFORM, trigger->GetPositionX(), trigger->GetPositionY(), trigger->GetPositionZ(), false); } - void DamageTaken(Unit* /*killer*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + void DoMoveToCenter() { - if (damage >= me->GetHealth() && Phase1) - { - damage = 0; - if (!WaitEvent) - { - WaitEvent = WE_DIE; - WaitTimer = 0; - me->SetHealth(0); - me->InterruptNonMeleeSpells(true); - me->RemoveAllAuras(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->AttackStop(); - me->SetTarget(ObjectGuid::Empty); - me->SetSpeedRate(MOVE_RUN, 5.0f); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(0, waypoint[5][0], waypoint[5][1], waypoint[5][2]); - } - } + if (Creature* trigger = me->FindNearestCreatureWithOptions(250.0f, { .StringId = CenterTriggerStringId })) + me->GetMotionMaster()->MovePoint(POINT_CENTER, trigger->GetPositionX(), trigger->GetPositionY(), trigger->GetPositionZ()); } - void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override + void EnterEvadeMode(EvadeReason /*why*/) override { - if (spellInfo->Id == SPELL_DIVE_BOMB_VISUAL) - { - me->SetDisplayId(11686); - //me->SendUpdateObjectToAllExcept(nullptr); - } + summons.DespawnAll(); + _DespawnAtEvade(); } - void MovementInform(uint32 type, uint32 /*id*/) override + void JustDied(Unit* /*killer*/) override { - if (type == POINT_MOTION_TYPE) - { - WaitTimer = 1; - AfterMoving = true; - ForceMove = false; - } + instance->DoCastSpellOnPlayers(SPELL_ASHTONGUE_RUSE_MASTER); + + _JustDied(); + + /// @todo: Guessed. Needed if boss dies during Dive Bomb. What should happen? + me->SetDisplayId(me->GetNativeDisplayId()); + me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); } void UpdateAI(uint32 diff) override { - if (!me->IsEngaged()) + if (!UpdateVictim()) return; - if (Berserk_Timer <= diff) - { - DoCast(me, SPELL_BERSERK, true); - Berserk_Timer = 60000; - } - else - Berserk_Timer -= diff; + events.Update(diff); - if (ForceMove) - { - if (ForceTimer <= diff) - { - me->GetMotionMaster()->MovePoint(0, waypoint[cur_wp][0], waypoint[cur_wp][1], waypoint[cur_wp][2]); - ForceTimer = 5000; - } - else - ForceTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - } - if (WaitEvent) + while (uint32 eventId = events.ExecuteEvent()) { - if (WaitTimer) + switch (eventId) { - if (WaitTimer <= diff) + // Phase 1 + case EVENT_MOVE_TO_PLATFORM: { - if (AfterMoving) + // Verified: A'lar can stay on the same platform (even twice in a row), will summon ember in this case + // Verified: After Flame Quills A'lar can move to the old platform + // Verified: After Flame Quills A'lar can move to the next platform + // Not verified: Can he perform more than one Flame Quills in a row? Currently he can + + // Cancel Flame Buffet so it will be not used during moving + events.CancelEvent(EVENT_FLAME_BUFFET); + + // If encounter just started, only move to the first platform + if (_isFirstPlatform) { - me->GetMotionMaster()->MoveIdle(); - AfterMoving = false; - } + DoMoveToPlatform(); - switch (WaitEvent) + _isFirstPlatform = false; + + // We are leaving a small chance to stay on the first platform + if (roll_chance_i(80)) + ++_currentPlatform; + } + else { - case WE_PLATFORM: - Platforms_Move_Timer = 30000 + rand32() % 5000; - break; - case WE_QUILL: - DoCast(me, SPELL_FLAME_QUILLS, true); - Platforms_Move_Timer = 1; - WaitTimer = 10000; - WaitEvent = WE_DUMMY; - return; - case WE_DIE: - ForceMove = false; - me->SetStandState(UNIT_STAND_STATE_DEAD); - WaitTimer = 5000; - WaitEvent = WE_REVIVE; - return; - case WE_REVIVE: - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetFullHealth(); - me->SetSpeedRate(MOVE_RUN, DefaultMoveSpeedRate); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - DoZoneInCombat(); - DoCast(me, SPELL_REBIRTH, true); - MeltArmor_Timer = 60000; - Charge_Timer = 7000; - DiveBomb_Timer = 40000 + rand32() % 5000; - FlamePatch_Timer = 30000; - Phase1 = false; - break; - case WE_METEOR: - DoCast(me, SPELL_DIVE_BOMB_VISUAL, false); - WaitEvent = WE_DIVE; - WaitTimer = 4000; - return; - case WE_DIVE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + me->SetControlled(false, UNIT_STATE_ROOT); + me->SetDisableGravity(true); + me->SetHover(true); + me->SetReactState(REACT_PASSIVE); + + // Else, move either to platform or to center + if (roll_chance_i(80)) { - me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); - DoCast(target, SPELL_DIVE_BOMB, true); - float dist = 3.0f; - if (me->IsWithinDist3d(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 5.0f)) - dist = 5.0f; - WaitTimer = 1000 + uint32(floor(dist / 80 * 1000.0f)); - me->UpdatePosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0.0f); - me->StopMoving(); - WaitEvent = WE_LAND; - return; + DoMoveToPlatform(); + + if (_currentPlatform == 3) + { + // If we are on the last platform, we are leaving a small chance to stay on it + if (roll_chance_i(80)) + _currentPlatform = 0; + } + else + { + // If we are not on the last platform, we leave a small chance to stay on current + if (roll_chance_i(80)) + ++_currentPlatform; + } + + // If we are moving to the next platform or stay on the same, summon ember + _shouldSpawnEmber = true; } else { - EnterEvadeMode(); - return; + DoMoveToCenter(); + + // If we are moving to the center, do not summon ember + _shouldSpawnEmber = false; } - case WE_LAND: - WaitEvent = WE_SUMMON; - WaitTimer = 2000; - return; - case WE_SUMMON: - for (uint8 i = 0; i < 2; ++i) - DoSpawnCreature(CREATURE_EMBER_OF_ALAR, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5s); - me->SetBoundingRadius(10); - me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); - me->SetDisplayId(me->GetNativeDisplayId()); - DoCast(me, SPELL_REBIRTH_2, true); - break; - case WE_DUMMY: - default: - break; } - WaitEvent = WE_NONE; - WaitTimer = 0; - } - else - WaitTimer -= diff; - } - return; - } - - if (Phase1) - { - if (!me->IsThreatened()) - { - EnterEvadeMode(); - return; - } + if (_shouldSpawnEmber) + DoCastSelf(SPELL_SUMMON_PHOENIX_ADDS_1); - if (Platforms_Move_Timer <= diff) - { - if (cur_wp == 4) - { - cur_wp = 0; - WaitEvent = WE_PLATFORM; + events.Repeat(30s, 40s); + break; } - else - { - if (urand(0, 4)) // next platform - { - DoSpawnCreature(CREATURE_EMBER_OF_ALAR, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5s); - if (cur_wp == 3) - cur_wp = 0; - else - ++cur_wp; - WaitEvent = WE_PLATFORM; - } - else // flame quill + case EVENT_FLAME_QUILLS: + DoCastSelf(SPELL_FLAME_QUILLS); + break; + + // Phase 1 & 2 + case EVENT_FLAME_BUFFET: + if (!me->IsWithinMeleeRange(me->GetVictim())) + DoCastSelf(SPELL_FLAME_BUFFET); + events.Repeat(2400ms); + break; + + // Transition + case EVENT_TRANSITION_1: + me->SetControlled(false, UNIT_STATE_ROOT); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + /// @todo: This should not be called. Clear All Debuffs should remove all debuffs. Does it work? Remove this + me->RemoveAllAuras(); + /// @todo: Guessed, this is positive aura, will be not removed by Clear All Debuffs. What should happen if Flame Quills is active here? + me->RemoveAurasDueToSpell(SPELL_FLAME_QUILLS); + me->SetReactState(REACT_PASSIVE); + me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); + me->SetStandState(UNIT_STAND_STATE_DEAD); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS); + DoCastSelf(SPELL_EMBER_BLAST_INVIS); + /// !HACK: Creature is immune to fire spells, we make it invisible without SPELL_EMBER_BLAST_INVIS + me->SetVisible(false); + events.ScheduleEvent(EVENT_TRANSITION_2, 5s); + break; + case EVENT_TRANSITION_2: + me->SetDisableGravity(true); + me->SetHover(true); + DoCastSelf(SPELL_FLIGHT_MODE); + me->GetMotionMaster()->MovePoint(POINT_RESSURRECTION, AlarRessurrectionPosition); + events.ScheduleEvent(EVENT_TRANSITION_3, 10s); + break; + case EVENT_TRANSITION_3: + me->SetDisableGravity(false); + me->SetHover(false); + /// !HACK: Creature is immune to fire spells, we make it visible manually + me->SetVisible(true); + me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST_INVIS); + events.ScheduleEvent(EVENT_TRANSITION_4, 1s); + break; + case EVENT_TRANSITION_4: + me->SetStandState(UNIT_STAND_STATE_STAND); + DoCastSelf(SPELL_REBIRTH); + events.ScheduleEvent(EVENT_TRANSITION_5, 3500ms); + break; + case EVENT_TRANSITION_5: + me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); + me->SetReactState(REACT_AGGRESSIVE); + ResetThreatList(); + DoZoneInCombat(); + + _isFirstPhase = false; + + events.ScheduleEvent(EVENT_FLAME_BUFFET, 1s); + events.ScheduleEvent(EVENT_FLAME_RING, 20s, 30s); + events.ScheduleEvent(EVENT_MELT_ARMOR, 60s); + events.ScheduleEvent(EVENT_CHARGE_ALAR, 25s, 40s); + events.ScheduleEvent(EVENT_BERSERK, 10min); + events.ScheduleEvent(EVENT_DIVE_BOMB_1, 30s, 40s); + break; + + // Phase 2 + case EVENT_FLAME_RING: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + DoCast(target, SPELL_SUMMON_FLAME_RING); + events.Repeat(30s, 45s); + break; + case EVENT_MELT_ARMOR: + DoCastVictim(SPELL_MELT_ARMOR); + events.Repeat(60s); + break; + case EVENT_CHARGE_ALAR: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + DoCast(target, SPELL_CHARGE); + events.Repeat(30s, 35s); + break; + case EVENT_BERSERK: + DoCastSelf(SPELL_BERSERK); + break; + + // Phase 2: Dive Bomb + case EVENT_DIVE_BOMB_1: + me->SetReactState(REACT_PASSIVE); + + events.CancelEvent(EVENT_FLAME_BUFFET); + events.RescheduleEvent(EVENT_CHARGE_ALAR, 30s, 35s); + + me->SetDisableGravity(true); + me->SetHover(true); + + if (Creature* trigger = me->FindNearestCreatureWithOptions(250.0f, { .StringId = CenterTriggerStringId })) + me->GetMotionMaster()->MovePoint(POINT_DIVE_BOMB, trigger->GetPositionX(), trigger->GetPositionY(), trigger->GetPositionZ()); + + events.Repeat(40s, 45s); + break; + case EVENT_DIVE_BOMB_2: + DoCastSelf(SPELL_DIVE_BOMB_VISUAL); + break; + case EVENT_DIVE_BOMB_3: + me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); + me->SetDisplayId(MODEL_INVISIBLE); + events.ScheduleEvent(EVENT_DIVE_BOMB_4, 2400ms); + break; + case EVENT_DIVE_BOMB_4: + me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) { - cur_wp = 4; - WaitEvent = WE_QUILL; + me->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation()); + DoCast(target, SPELL_DIVE_BOMB); } - } - ForceMove = true; - ForceTimer = 5000; - me->GetMotionMaster()->MovePoint(0, waypoint[cur_wp][0], waypoint[cur_wp][1], waypoint[cur_wp][2]); - WaitTimer = 0; - return; - } - else - Platforms_Move_Timer -= diff; - } - else - { - if (Charge_Timer <= diff) - { - Unit* target= SelectTarget(SelectTargetMethod::Random, 1, 100, true); - if (target) - DoCast(target, SPELL_CHARGE); - Charge_Timer = 30000; + events.ScheduleEvent(EVENT_DIVE_BOMB_5, 1200ms); + break; + case EVENT_DIVE_BOMB_5: + DoCastSelf(SPELL_SUMMON_PHOENIX_ADDS_2); + events.ScheduleEvent(EVENT_DIVE_BOMB_6, 2400ms); + break; + case EVENT_DIVE_BOMB_6: + DoCastSelf(SPELL_REBIRTH_2); + me->SetDisplayId(me->GetNativeDisplayId()); + events.ScheduleEvent(EVENT_DIVE_BOMB_7, 2400ms); + break; + case EVENT_DIVE_BOMB_7: + me->SetDisableGravity(false); + me->SetHover(false); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); + events.ScheduleEvent(EVENT_FLAME_BUFFET, 1s); + break; + default: + break; } - else - Charge_Timer -= diff; - if (MeltArmor_Timer <= diff) - { - DoCastVictim(SPELL_MELT_ARMOR); - MeltArmor_Timer = 60000; - } - else - MeltArmor_Timer -= diff; - - if (DiveBomb_Timer <= diff) - { - me->AttackStop(); - me->GetMotionMaster()->MovePoint(6, waypoint[4][0], waypoint[4][1], waypoint[4][2]); - me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); - me->SetBoundingRadius(50); - WaitEvent = WE_METEOR; - WaitTimer = 0; - DiveBomb_Timer = 40000 + rand32() % 5000; + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - } - else - DiveBomb_Timer -= diff; - - if (FlamePatch_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - Creature* Summoned = me->SummonCreature(CREATURE_FLAME_PATCH_ALAR, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 2min); - if (Summoned) - { - Summoned->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); - Summoned->SetObjectScale(Summoned->GetObjectScale() * 2.5f); - Summoned->SetDisplayId(11686); - Summoned->SetFaction(me->GetFaction()); - Summoned->SetLevel(me->GetLevel()); - Summoned->CastSpell(Summoned, SPELL_FLAME_PATCH, false); - } - } - FlamePatch_Timer = 30000; - } - else - FlamePatch_Timer -= diff; } DoMeleeAttackIfReady(); } - void DoMeleeAttackIfReady() - { - if (me->isAttackReady() && !me->IsNonMeleeSpellCast(false)) - { - if (me->IsWithinMeleeRange(me->GetVictim())) - { - me->AttackerStateUpdate(me->GetVictim()); - me->resetAttackTimer(); - } - else - { - if (Unit* target = me->SelectNearestTargetInAttackDistance(5)) - AttackStart(target); - else - { - DoCast(me, SPELL_FLAME_BUFFET, true); - me->setAttackTimer(BASE_ATTACK, 1500); - } - } - } - } +private: + bool _isFirstPhase; + bool _isInTransition; + bool _isFirstPlatform; + bool _shouldSpawnEmber; + uint8 _currentPlatform; }; +// 19551 - Ember of Al'ar struct npc_ember_of_alar : public ScriptedAI { - npc_ember_of_alar(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - creature->SetDisableGravity(true); - } + npc_ember_of_alar(Creature* creature) : ScriptedAI(creature) { } - void Initialize() + void InitializeAI() override { - toDie = false; + me->SetCorpseDelay(5, true); + me->SetReactState(REACT_PASSIVE); } - InstanceScript* instance; - bool toDie; - - void Reset() override + void JustAppeared() override { - Initialize(); + _scheduler.Schedule(2s, [this](TaskContext /*task*/) + { + DoZoneInCombat(); + me->SetReactState(REACT_AGGRESSIVE); + }); } - void JustEngagedWith(Unit* /*who*/) override + void JustDied(Unit* /*killer*/) override { - DoZoneInCombat(); + DoCastSelf(SPELL_EMBER_BLAST, true); + /// @temporary: Should be triggered from the spell above, doesn't work currently + DoCastSelf(SPELL_EMBER_BLAST_DAMAGE, true); } - void EnterEvadeMode(EvadeReason /*why*/) override + void UpdateAI(uint32 diff) override { - me->setDeathState(JUST_DIED); - } + _scheduler.Update(diff); - void DamageTaken(Unit* killer, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override - { - if (damage >= me->GetHealth() && killer != me && !toDie) - { - damage = 0; - DoCast(me, SPELL_EMBER_BLAST, true); - me->SetDisplayId(11686); - me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); - if (instance->GetBossState(DATA_ALAR) == IN_PROGRESS) - { - if (Unit* Alar = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_ALAR))) - { - int32 AlarHealth = int32(Alar->GetHealth()) - int32(Alar->CountPctFromMaxHealth(3)); - if (AlarHealth > 0) - Alar->SetHealth(AlarHealth); - else - Alar->SetHealth(1); - } - } - toDie = true; - } + if (UpdateVictim()) + DoMeleeAttackIfReady(); } - void UpdateAI(uint32 /*diff*/) override - { - if (!UpdateVictim()) - return; - - if (toDie) - { - me->KillSelf(); - //me->SetVisibility(VISIBILITY_OFF); - } - - DoMeleeAttackIfReady(); - } +private: + TaskScheduler _scheduler; }; +// 20602 - Flame Patch (Al'ar) struct npc_flame_patch_alar : public ScriptedAI { npc_flame_patch_alar(Creature* creature) : ScriptedAI(creature) { } - void Reset() override { } - void JustEngagedWith(Unit* /*who*/) override { } - void AttackStart(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } - void UpdateAI(uint32 /*diff*/) override { } + void InitializeAI() override + { + me->SetReactState(REACT_PASSIVE); + } + + void JustAppeared() override + { + DoCastSelf(SPELL_FLAME_PATCH_PERIODIC); + } }; // 34229 - Flame Quills @@ -556,7 +567,7 @@ class spell_alar_flame_quills : public AuraScript bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo(flameQuillsSpells); + return ValidateSpellInfo(FlameQuillsSpells); } bool Load() override @@ -566,16 +577,46 @@ class spell_alar_flame_quills : public AuraScript void PeriodicTick(AuraEffect const* aurEff) { - PreventDefaultAction(); + for (uint32 spell : FlameQuillsSpells) + GetTarget()->CastSpell(nullptr, spell, aurEff); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) + return; - // cast 24 spells 34269-34289, 34314-34316 - for (uint32 spellId : flameQuillsSpells) - GetTarget()->CastSpell(nullptr, spellId, aurEff); + if (Creature* target = GetTarget()->ToCreature()) + target->AI()->DoAction(ACTION_FLAME_QUILLS_END); } void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_alar_flame_quills::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + AfterEffectRemove += AuraEffectRemoveFn(spell_alar_flame_quills::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 39555 - Ashtongue Ruse Master +class spell_alar_ashtongue_ruse_master : public AuraScript +{ + PrepareAuraScript(spell_alar_ashtongue_ruse_master); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_ASHTONGUE_RUSE_DUMMY, SPELL_ASHTONGUE_RUSE_CREDIT }); + } + + void AfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (target->HasAura(SPELL_ASHTONGUE_RUSE_DUMMY)) + target->CastSpell(target, SPELL_ASHTONGUE_RUSE_CREDIT, true); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_alar_ashtongue_ruse_master::AfterApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; @@ -585,4 +626,5 @@ void AddSC_boss_alar() RegisterTheEyeCreatureAI(npc_ember_of_alar); RegisterTheEyeCreatureAI(npc_flame_patch_alar); RegisterSpellScript(spell_alar_flame_quills); + RegisterSpellScript(spell_alar_ashtongue_ruse_master); } |
