diff options
4 files changed, 667 insertions, 235 deletions
diff --git a/sql/updates/world/3.3.5/2025_09_22_00_world.sql b/sql/updates/world/3.3.5/2025_09_22_00_world.sql new file mode 100644 index 00000000000..81cbd82d6e1 --- /dev/null +++ b/sql/updates/world/3.3.5/2025_09_22_00_world.sql @@ -0,0 +1,44 @@ +-- Intro +DELETE FROM `areatrigger_scripts` WHERE `entry` = 4853; +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(4853, 'at_the_dead_scar'); + +DELETE FROM `spawn_group` WHERE `spawnType` = 0 AND `spawnId` IN (SELECT `guid` FROM `creature` WHERE `id` = 24895); +DELETE FROM `creature` WHERE `id` = 24895; + +UPDATE `creature_template_movement` SET `Flight` = 1 WHERE `CreatureId` = 24895; + +UPDATE `creature_template` SET `ScriptName` = 'npc_madrigosa' WHERE `entry` = 24895; + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` IN (46609,46610,44872,44844,44883,44884,46637,46638); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,46609,0,0,31,0,3,19871,0,0,0,0,"","Group 0: Spell 'Freeze' (Effect 0) targets creature 'World Trigger (Not Immune NPC)'"), +(13,1,46610,0,0,31,0,5,188119,0,0,0,0,"","Group 0: Spell 'Freeze' (Effect 0) targets object 'Doodad_Sunwell_Ice_Barrier01'"), +(13,1,44872,0,0,31,0,3,24882,0,0,0,0,"","Group 0: Spell 'Frost Blast' (Effect 0) targets creature 'Brutallus'"), +(13,3,44844,0,0,31,0,3,24895,0,0,0,0,"","Group 0: Spell 'Fel Fireball' (Effect 0, 1) targets creature 'Madrigosa'"), +(13,1,44883,0,0,31,0,3,24882,0,0,0,0,"","Group 0: Spell 'Encapsulate' (Effect 0) targets creature 'Brutallus'"), +(13,1,44884,0,0,31,0,3,24895,0,0,0,0,"","Group 0: Spell 'Charge' (Effect 0) targets creature 'Madrigosa'"), +(13,1,46637,0,0,31,0,3,19871,0,0,0,0,"","Group 0: Spell 'Break Ice' (Effect 0) targets creature 'World Trigger (Not Immune NPC)'"), +(13,1,46638,0,0,31,0,5,188119,0,0,0,0,"","Group 0: Spell 'Break Ice' (Effect 0) targets object 'Doodad_Sunwell_Ice_Barrier01'"); + +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ( +'spell_brutallus_freeze', +'spell_brutallus_break_ice', +'spell_brutallus_burn_primer', +'spell_brutallus_burn_ally'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(46609, 'spell_brutallus_freeze'), +(46637, 'spell_brutallus_break_ice'), +(45141, 'spell_brutallus_burn_primer'), +(45151, 'spell_brutallus_burn_ally'); + +UPDATE `creature_text` SET `Emote` = 1 WHERE `CreatureID` = 24882 AND `GroupID` = 0; +UPDATE `creature_text` SET `BroadcastTextId` = 25224 WHERE `CreatureID` = 24882 AND `GroupID` = 2; +UPDATE `creature_text` SET `Text` = "That was fun, but I still await a true challenge!", `Language` = 0, `BroadcastTextId` = 25225 WHERE `CreatureID` = 24882 AND `GroupID` = 3; + +-- Outro +UPDATE `creature_template` SET `AIName` = 'SmartAI', `flags_extra` = `flags_extra` |128 WHERE `entry` = 25703; + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 25703 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 +(25703,0,0,0,60,0,100,1,7000,7000,0,0,0,11,45212,0,0,0,0,0,1,0,0,0,0,0,0,0,0,"Brutallus Death Cloud - On Update - Cast 'Brutallus Death Cloud' (No Repeat)"); diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_brutallus.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_brutallus.cpp index 33a133b37cf..b1536cbc077 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_brutallus.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_brutallus.cpp @@ -15,317 +15,604 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Brutallus -SD%Complete: 80 -SDComment: Find a way to start the intro, best code for the intro -EndScriptData */ +/* + * Intro requires additional re-checks, something may be missing or implemented wrongly. + Visually it doesn't look good because of several core bugs \ wrong implementation + * Everything related to Burn requires additional re-checks since something may be implemented wrongly + * Effect of SPELL_CLEAR_RETURN_STATE is NYI + * SPELL_TAUNT_HIT_CHANCE has no effect currently + */ #include "ScriptMgr.h" +#include "GameObject.h" #include "InstanceScript.h" -#include "Log.h" +#include "MotionMaster.h" +#include "Player.h" #include "ScriptedCreature.h" #include "SpellAuraEffects.h" +#include "SpellInfo.h" #include "SpellScript.h" #include "sunwell_plateau.h" -enum Quotes +enum BrutallusTexts { - YELL_INTRO = 0, - YELL_INTRO_BREAK_ICE = 1, - YELL_INTRO_CHARGE = 2, - YELL_INTRO_KILL_MADRIGOSA = 3, - YELL_INTRO_TAUNT = 4, - - YELL_AGGRO = 5, - YELL_KILL = 6, - YELL_LOVE = 7, - YELL_BERSERK = 8, - YELL_DEATH = 9, - - YELL_MADR_ICE_BARRIER = 0, - YELL_MADR_INTRO = 1, - YELL_MADR_ICE_BLOCK = 2, - YELL_MADR_TRAP = 3, - YELL_MADR_DEATH = 4 + SAY_INTRO = 0, + SAY_INTRO_BREAK_ICE = 1, + SAY_INTRO_CHARGE = 2, + SAY_INTRO_KILL_MADRIGOSA = 3, + SAY_INTRO_TAUNT = 4, + + SAY_AGGRO = 5, + SAY_SLAY = 6, + SAY_LOVE = 7, + SAY_BERSERK = 8, + SAY_DEATH = 9, + + SAY_MADR_ICE_BARRIER = 0, + SAY_MADR_INTRO = 1, + SAY_MADR_ICE_BLOCK = 2, + SAY_MADR_TRAP = 3, + SAY_MADR_DEATH = 4 }; -enum Spells +enum BrutallusSpells { + // Passive + SPELL_DUAL_WIELD_PASSIVE = 42459, + SPELL_TAUNT_HIT_CHANCE = 45210, + + // Combat SPELL_METEOR_SLASH = 45150, - SPELL_BURN = 46394, SPELL_STOMP = 45185, + SPELL_BURN_PRIMER = 45141, SPELL_BERSERK = 26662, - SPELL_DUAL_WIELD = 42459, - SPELL_INTRO_FROST_BLAST = 45203, - SPELL_INTRO_FROSTBOLT = 44843, - SPELL_INTRO_ENCAPSULATE = 45665, - SPELL_INTRO_ENCAPSULATE_CHANELLING = 45661 + // Intro - Madrigosa + SPELL_FREEZE = 46609, + SPELL_FROST_BREATH = 45065, + SPELL_FROST_BLAST = 44872, + SPELL_FROSTBOLT = 44843, + SPELL_ENCAPSULATE = 44883, + SPELL_SELF_STUN = 45066, + SPELL_PERMANENT_FEIGN_DEATH = 29266, + + // Intro - Brutallus + SPELL_FLAME_RING_1 = 44873, + SPELL_FLAME_RING_2 = 44874, + SPELL_FEL_FIREBALL = 44844, + SPELL_ARCANE_EXPLOSION_VISUAL = 35426, + SPELL_CHARGE = 44884, + SPELL_CLEAR_RETURN_STATE = 38289, + SPELL_FULL_HEAL = 17683, + SPELL_BREAK_ICE = 46637, + + // Intro - Shared + SPELL_CLEAR_ALL_DEBUFFS = 34098, + + // Outro - Brutallus + SPELL_SUMMON_DEATH_CLOUD = 45884, + + // Outro - Madrigosa + SPELL_SUMMON_FELBLAZE_PRE_VISUAL = 44885, + SPELL_SUMMON_FELBLAZE = 45069, + + // Scripts + SPELL_BURN_DAMAGE = 46394, + SPELL_FREEZE_OBJECT = 46610, + SPELL_BREAK_ICE_OBJECT = 46638, + SPELL_BREAK_ICE_KNOCK_BACK = 47030 }; -struct boss_brutallus : public BossAI +enum BrutallusEvents { - boss_brutallus(Creature* creature) : BossAI(creature, DATA_BRUTALLUS) - { - Initialize(); - Intro = true; - } - - void Initialize() - { - SlashTimer = 11000; - StompTimer = 30000; - BurnTimer = 60000; - BerserkTimer = 360000; - - IntroPhase = 0; - IntroPhaseTimer = 0; - IntroFrostBoltTimer = 0; + EVENT_METEOR_SLASH = 1, + EVENT_STOMP, + EVENT_BURN, + EVENT_BERSERK, + + EVENT_INTRO_1, + EVENT_INTRO_2, + EVENT_INTRO_3, + EVENT_INTRO_4, + EVENT_INTRO_5, + EVENT_INTRO_6, + EVENT_INTRO_7, + EVENT_INTRO_8, + EVENT_INTRO_9, + EVENT_INTRO_10, + EVENT_INTRO_11, + EVENT_INTRO_12, + EVENT_INTRO_13, + EVENT_INTRO_14, + EVENT_INTRO_15, + EVENT_INTRO_16, + EVENT_INTRO_17, + EVENT_INTRO_18, + EVENT_INTRO_19, + EVENT_INTRO_20, + EVENT_INTRO_21, + EVENT_INTRO_22, + EVENT_INTRO_23, + EVENT_INTRO_24, + EVENT_INTRO_25, + EVENT_INTRO_26, + + EVENT_FROSTBOLT, + + EVENT_OUTRO_1, + EVENT_OUTRO_2, + EVENT_OUTRO_3 +}; - IsIntro = false; - Enraged = false; - } +enum BrutallusActions +{ + ACTION_START_OUTRO = 0 +}; - uint32 SlashTimer; - uint32 BurnTimer; - uint32 StompTimer; - uint32 BerserkTimer; +enum BrutallusPoints +{ + POINT_MADRIGOSA_LAND_1 = 0, + POINT_MADRIGOSA_LIFTOFF = 1, + POINT_MADRIGOSA_LAND_2 = 2, + POINT_MADRIGOSA_LAND_3 = 3, + POINT_BRUTALLUS_OFFSET = 4, + POINT_BRUTALLUS_COMBAT = 5 +}; - uint32 IntroPhase; - uint32 IntroPhaseTimer; - uint32 IntroFrostBoltTimer; +Position const MadrigosaSpawnPos = { 1470.3624f, 738.1818f, 64.166770f, 4.625122547149658203f }; +Position const MadrigosaMoveLandPos1 = { 1463.8200f, 661.2120f, 19.797100f, 0.0f }; +Position const MadrigosaMoveLiftoffPos = { 1464.6943f, 652.1426f, 39.277885f, 0.0f }; +Position const MadrigosaMoveLandPos2 = { 1464.6943f, 652.1426f, 20.819180f, 0.0f }; +Position const MadrigosaMoveLandPos3 = { 1464.6943f, 652.1426f, 19.819180f, 0.0f }; +Position const BrutallusMoveOffsetPos = { 1464.4820f, 583.9001f, 44.392480f, 0.0f }; +Position const BrutallusMoveCombatPos = { 1478.7400f, 621.8980f, 22.654501f, 0.0f }; - bool Intro; - bool IsIntro; - bool Enraged; +// 24882 - Brutallus +struct boss_brutallus : public BossAI +{ + boss_brutallus(Creature* creature) : BossAI(creature, DATA_BRUTALLUS) { } void Reset() override { - Initialize(); - - DoCast(me, SPELL_DUAL_WIELD, true); + _Reset(); - BossAI::Reset(); + /// @todo: Dual Wield doesn't get apply after evade for unknown reason. Investigate this + DoCastSelf(SPELL_DUAL_WIELD_PASSIVE); + DoCastSelf(SPELL_TAUNT_HIT_CHANCE); } void JustEngagedWith(Unit* who) override { - Talk(YELL_AGGRO); + if (who->GetEntry() == NPC_MADRIGOSA) + return; BossAI::JustEngagedWith(who); + + Talk(SAY_AGGRO); + + events.ScheduleEvent(EVENT_METEOR_SLASH, 10s); + events.ScheduleEvent(EVENT_STOMP, 30s); + events.ScheduleEvent(EVENT_BURN, 20s); + events.ScheduleEvent(EVENT_BERSERK, 6min); } - void KilledUnit(Unit* /*victim*/) override + void OnSpellCast(SpellInfo const* spell) override { - Talk(YELL_KILL); + switch (spell->Id) + { + case SPELL_STOMP: + Talk(SAY_LOVE); + break; + case SPELL_BERSERK: + Talk(SAY_BERSERK); + break; + default: + break; + } } - void JustDied(Unit* killer) override + void KilledUnit(Unit* /*victim*/) override { - Talk(YELL_DEATH); - - instance->SetBossState(DATA_FELMYST, SPECIAL); - BossAI::JustDied(killer); + Talk(SAY_SLAY); } - void EnterEvadeMode(EvadeReason why) override + void JustDied(Unit* /*killer*/) override { - if (!Intro) - BossAI::EnterEvadeMode(why); + _JustDied(); + + Talk(SAY_DEATH); + + DoCastSelf(SPELL_SUMMON_DEATH_CLOUD, true); + + if (Creature* madrigosa = instance->GetCreature(DATA_MADRIGOSA)) + madrigosa->AI()->DoAction(ACTION_START_OUTRO); } - void StartIntro() + void UpdateAI(uint32 diff) override { - if (!Intro || IsIntro) + if (!UpdateVictim()) return; - if (Creature* Madrigosa = instance->GetCreature(DATA_MADRIGOSA)) - { - Madrigosa->Respawn(); - Madrigosa->setActive(true); - Madrigosa->SetFarVisible(true); - IsIntro = true; - Madrigosa->SetMaxHealth(me->GetMaxHealth()); - Madrigosa->SetHealth(me->GetMaxHealth()); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->Attack(Madrigosa, true); - Madrigosa->Attack(me, true); - } - else + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - // Madrigosa not found, end intro - TC_LOG_ERROR("scripts", "Madrigosa was not found"); - EndIntro(); + switch (eventId) + { + case EVENT_METEOR_SLASH: + DoCastSelf(SPELL_METEOR_SLASH); + events.Repeat(12s); + break; + case EVENT_STOMP: + DoCastVictim(SPELL_STOMP); + events.Repeat(32s); + break; + case EVENT_BURN: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true, true, -SPELL_BURN_DAMAGE)) + DoCast(target, SPELL_BURN_PRIMER); + events.Repeat(20s); + break; + case EVENT_BERSERK: + DoCastSelf(SPELL_BERSERK); + break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - } - void EndIntro() - { - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - Intro = false; - IsIntro = false; + DoMeleeAttackIfReady(); } +}; - void AttackStart(Unit* who) override +// 24895 - Madrigosa +struct npc_madrigosa : public ScriptedAI +{ + npc_madrigosa(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void JustAppeared() override { - if (!who || Intro || IsIntro) - return; - BossAI::AttackStart(who); + _events.ScheduleEvent(EVENT_INTRO_1, 2500ms); } - void DoIntro() + void MovementInform(uint32 type, uint32 id) override { - Creature* Madrigosa = instance->GetCreature(DATA_MADRIGOSA); - if (!Madrigosa) - return; - - switch (IntroPhase) + if (type == POINT_MOTION_TYPE) { - case 0: - Madrigosa->AI()->Talk(YELL_MADR_ICE_BARRIER); - IntroPhaseTimer = 7000; - ++IntroPhase; - break; - case 1: - me->SetFacingToObject(Madrigosa); - Madrigosa->SetFacingToObject(me); - Madrigosa->AI()->Talk(YELL_MADR_INTRO, me); - IntroPhaseTimer = 9000; - ++IntroPhase; - break; - case 2: - Talk(YELL_INTRO, Madrigosa); - IntroPhaseTimer = 13000; - ++IntroPhase; - break; - case 3: - DoCast(me, SPELL_INTRO_FROST_BLAST); - Madrigosa->SetDisableGravity(true); - me->AttackStop(); - Madrigosa->AttackStop(); - IntroFrostBoltTimer = 3000; - IntroPhaseTimer = 28000; - ++IntroPhase; - break; - case 4: - Talk(YELL_INTRO_BREAK_ICE); - IntroPhaseTimer = 6000; - ++IntroPhase; - break; - case 5: - Madrigosa->CastSpell(me, SPELL_INTRO_ENCAPSULATE_CHANELLING, false); - Madrigosa->AI()->Talk(YELL_MADR_TRAP); - DoCast(me, SPELL_INTRO_ENCAPSULATE); - IntroPhaseTimer = 11000; - ++IntroPhase; - break; - case 6: - Talk(YELL_INTRO_CHARGE); - IntroPhaseTimer = 5000; - ++IntroPhase; - break; - case 7: - Unit::Kill(me, Madrigosa); - Madrigosa->AI()->Talk(YELL_MADR_DEATH); - me->SetFullHealth(); - me->AttackStop(); - IntroPhaseTimer = 4000; - ++IntroPhase; - break; - case 8: - Talk(YELL_INTRO_KILL_MADRIGOSA); - me->SetOrientation(0.14f); - me->StopMoving(); - Madrigosa->setDeathState(CORPSE); - IntroPhaseTimer = 8000; - ++IntroPhase; - break; - case 9: - Talk(YELL_INTRO_TAUNT); - IntroPhaseTimer = 5000; - ++IntroPhase; - break; - case 10: - EndIntro(); - break; + switch (id) + { + case POINT_MADRIGOSA_LAND_1: + _events.ScheduleEvent(EVENT_INTRO_2, 0s); + break; + case POINT_MADRIGOSA_LIFTOFF: + _events.ScheduleEvent(EVENT_INTRO_9, 0s); + break; + case POINT_MADRIGOSA_LAND_2: + _events.ScheduleEvent(EVENT_INTRO_13, 0s); + break; + case POINT_MADRIGOSA_LAND_3: + _events.ScheduleEvent(EVENT_INTRO_14, 1s); + break; + default: + break; + } } } - void MoveInLineOfSight(Unit* who) override + void DoAction(int32 action) override { - if (!me->IsValidAttackTarget(who)) - return; - - if (Intro) - instance->SetBossState(DATA_BRUTALLUS, SPECIAL); - - if (Intro && !IsIntro) - StartIntro(); + if (action == ACTION_START_OUTRO) + _events.ScheduleEvent(EVENT_OUTRO_1, 1min); + } - if (!Intro) - BossAI::MoveInLineOfSight(who); + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + { + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; } + /// @temporary: Find a better solution to end combat without breaking visuals (Feign Death) + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 diff) override { - if (IsIntro) - { - if (IntroPhaseTimer <= diff) - DoIntro(); - else IntroPhaseTimer -= diff; + _events.Update(diff); - if (IntroPhase == 3 + 1) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - if (IntroFrostBoltTimer <= diff) + case EVENT_INTRO_1: + me->GetMotionMaster()->MovePoint(POINT_MADRIGOSA_LAND_1, MadrigosaMoveLandPos1); + break; + case EVENT_INTRO_2: + Talk(SAY_MADR_ICE_BARRIER); + if (Creature* trigger = _instance->GetCreature(DATA_WORLD_TRIGGER)) + me->SetFacingToObject(trigger); + DoCastSelf(SPELL_FREEZE); + _events.ScheduleEvent(EVENT_INTRO_3, 7s); + break; + case EVENT_INTRO_3: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + me->SetFacingToObject(brutallus); + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->SetDisableGravity(false); + me->SetHover(false); + Talk(SAY_MADR_INTRO); + _events.ScheduleEvent(EVENT_INTRO_4, 6s); + break; + case EVENT_INTRO_4: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + brutallus->AI()->Talk(SAY_INTRO); + _events.ScheduleEvent(EVENT_INTRO_5, 4s); + break; + case EVENT_INTRO_5: + me->SetImmuneToNPC(false); + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + AttackStart(brutallus); + _events.ScheduleEvent(EVENT_INTRO_6, 5s); + break; + case EVENT_INTRO_6: + DoCastSelf(SPELL_FROST_BREATH); + _events.ScheduleEvent(EVENT_INTRO_7, 5s); + break; + case EVENT_INTRO_7: + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + me->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_INTRO_8, 2500ms); + break; + case EVENT_INTRO_8: { - if (Creature* Madrigosa = instance->GetCreature(DATA_MADRIGOSA)) + me->SetDisableGravity(true); + me->SetHover(true); + me->GetMotionMaster()->MovePoint(POINT_MADRIGOSA_LIFTOFF, MadrigosaMoveLiftoffPos); + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) { - Madrigosa->CastSpell(me, SPELL_INTRO_FROSTBOLT, true); - IntroFrostBoltTimer = 2000; + brutallus->SetDisableGravity(true); + brutallus->SetControlled(true, UNIT_STATE_ROOT); } + break; } - else - IntroFrostBoltTimer -= diff; - } + case EVENT_INTRO_9: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + me->SetFacingToObject(brutallus); + Talk(SAY_MADR_ICE_BLOCK); + DoCastSelf(SPELL_FROST_BLAST); + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + brutallus->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_FROSTBOLT, RAND(1200ms, 2400ms, 3600ms)); + _events.ScheduleEvent(EVENT_INTRO_10, 8500ms); + break; + case EVENT_INTRO_10: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + brutallus->CastSpell(brutallus, SPELL_FLAME_RING_1); + _events.ScheduleEvent(EVENT_INTRO_11, 6s); + break; + case EVENT_INTRO_11: + { + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->AI()->Talk(SAY_INTRO_BREAK_ICE); + /// @temporary: SPELL_CLEAR_ALL_DEBUFFS doesn't remove that aura + brutallus->RemoveAurasDueToSpell(SPELL_FROST_BLAST); + brutallus->CastSpell(brutallus, SPELL_CLEAR_ALL_DEBUFFS); + brutallus->CastSpell(brutallus, SPELL_FLAME_RING_2); + brutallus->SetFacingTo(1.383456826210021972f); + brutallus->CastSpell(brutallus, SPELL_FEL_FIREBALL); + brutallus->SetImmuneToNPC(true); + } + _events.ScheduleEvent(EVENT_INTRO_12, 6s); + break; + } + case EVENT_INTRO_12: + me->GetMotionMaster()->MovePoint(POINT_MADRIGOSA_LAND_2, MadrigosaMoveLandPos2); + _events.CancelEvent(EVENT_FROSTBOLT); + break; + case EVENT_INTRO_13: + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->GetMotionMaster()->MovePoint(POINT_MADRIGOSA_LAND_3, MadrigosaMoveLandPos3); + me->SetAnimTier(AnimTier::Ground); + break; + case EVENT_INTRO_14: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + me->SetFacingToObject(brutallus); + _events.ScheduleEvent(EVENT_INTRO_15, 2500ms); + break; + case EVENT_INTRO_15: + { + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS); + DoCastSelf(SPELL_ENCAPSULATE); + Talk(SAY_MADR_TRAP); + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->SetDisableGravity(false); + brutallus->SetControlled(false, UNIT_STATE_ROOT); + } + _events.ScheduleEvent(EVENT_INTRO_16, 1500ms); + break; + } + case EVENT_INTRO_16: + { + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->SetDisableGravity(true); + brutallus->SetHover(true); + } + _events.ScheduleEvent(EVENT_INTRO_17, 2s); + break; + } + case EVENT_INTRO_17: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + brutallus->GetMotionMaster()->MovePoint(POINT_BRUTALLUS_OFFSET, BrutallusMoveOffsetPos); + _events.ScheduleEvent(EVENT_INTRO_18, 9s); + break; + case EVENT_INTRO_18: + { + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->CastSpell(brutallus, SPELL_ARCANE_EXPLOSION_VISUAL); + brutallus->CastSpell(brutallus, SPELL_CLEAR_ALL_DEBUFFS); + brutallus->AI()->Talk(SAY_INTRO_CHARGE); + brutallus->GetMotionMaster()->MoveFall(); + } + me->InterruptNonMeleeSpells(false); + _events.ScheduleEvent(EVENT_INTRO_19, 1s); + break; + } + case EVENT_INTRO_19: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + me->SetFacingToObject(brutallus); + DoCastSelf(SPELL_SELF_STUN); + me->SetImmuneToNPC(true); + _events.ScheduleEvent(EVENT_INTRO_20, 1500ms); + break; + case EVENT_INTRO_20: + { + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->SetDisableGravity(false); + brutallus->SetHover(false); + brutallus->CastSpell(brutallus, SPELL_CHARGE); + } + _events.ScheduleEvent(EVENT_INTRO_21, 1s); + break; + } + case EVENT_INTRO_21: + me->RemoveAurasDueToSpell(SPELL_SELF_STUN); + DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH); + Talk(SAY_MADR_DEATH); + _events.ScheduleEvent(EVENT_INTRO_22, 2s); + break; + case EVENT_INTRO_22: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + brutallus->CastSpell(brutallus, SPELL_CLEAR_RETURN_STATE); + _events.ScheduleEvent(EVENT_INTRO_23, 1s); + break; + case EVENT_INTRO_23: + { + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->CastSpell(brutallus, SPELL_FULL_HEAL); + brutallus->GetMotionMaster()->MovePoint(POINT_BRUTALLUS_COMBAT, BrutallusMoveCombatPos); + } + _events.ScheduleEvent(EVENT_INTRO_24, 3500ms); + break; + } + case EVENT_INTRO_24: + { + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->AI()->Talk(SAY_INTRO_KILL_MADRIGOSA); + brutallus->SetFacingTo(0.069813169538974761f); + } + _events.ScheduleEvent(EVENT_INTRO_25, 7s); + break; + } + case EVENT_INTRO_25: + { + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->AI()->Talk(SAY_INTRO_TAUNT); + brutallus->CastSpell(brutallus, SPELL_BREAK_ICE); + } + _events.ScheduleEvent(EVENT_INTRO_26, 2500ms); + break; + } + case EVENT_INTRO_26: + { + me->SetHomePosition(me->GetPosition()); - if (!UpdateVictim()) - return; + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + { + brutallus->SetImmuneToNPC(false); + brutallus->SetHomePosition(brutallus->GetPosition()); + brutallus->SetReactState(REACT_AGGRESSIVE); + /// @temporary: Should not be called, find a better solution to end combat + brutallus->AI()->EnterEvadeMode(); + } + break; + } - DoMeleeAttackIfReady(); + case EVENT_FROSTBOLT: + if (Creature* brutallus = _instance->GetCreature(DATA_BRUTALLUS)) + DoCast(brutallus, SPELL_FROSTBOLT); + _events.Repeat(RAND(1200ms, 2400ms, 3600ms)); + break; + + case EVENT_OUTRO_1: + DoCastSelf(SPELL_SUMMON_FELBLAZE_PRE_VISUAL); + _events.ScheduleEvent(EVENT_OUTRO_2, 3s); + break; + case EVENT_OUTRO_2: + // We are not ready to summon Felmyst (currently it creates double spawn) + // DoCastSelf(SPELL_SUMMON_FELBLAZE); + _events.ScheduleEvent(EVENT_OUTRO_3, 9s); + break; + case EVENT_OUTRO_3: + me->DespawnOrUnsummon(); + break; + default: + break; + } } - if (!UpdateVictim() || IsIntro) - return; + if (UpdateVictim()) + DoMeleeAttackIfReady(); + } - if (SlashTimer <= diff) - { - DoCastVictim(SPELL_METEOR_SLASH); - SlashTimer = 11000; - } else SlashTimer -= diff; +private: + EventMap _events; + InstanceScript* _instance; +}; - if (StompTimer <= diff) - { - Talk(YELL_LOVE); - DoCastVictim(SPELL_STOMP); - StompTimer = 30000; - } else StompTimer -= diff; +// 45141 - Burn +class spell_brutallus_burn_primer : public SpellScript +{ + PrepareSpellScript(spell_brutallus_burn_primer); - if (BurnTimer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, true, -SPELL_BURN)) - target->CastSpell(target, SPELL_BURN, true); - BurnTimer = urand(60000, 180000); - } else BurnTimer -= diff; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_BURN_DAMAGE }); + } - if (BerserkTimer < diff && !Enraged) - { - Talk(YELL_BERSERK); - DoCast(me, SPELL_BERSERK); - Enraged = true; - } else BerserkTimer -= diff; + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + if (!target->HasAura(SPELL_BURN_DAMAGE)) + target->CastSpell(target, SPELL_BURN_DAMAGE, true); + } - DoMeleeAttackIfReady(); + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_brutallus_burn_primer::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 45151 - Burn +class spell_brutallus_burn_ally : public SpellScript +{ + PrepareSpellScript(spell_brutallus_burn_ally); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_BURN_DAMAGE }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + if (!target->HasAura(SPELL_BURN_DAMAGE)) + target->CastSpell(target, SPELL_BURN_DAMAGE, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_brutallus_burn_ally::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; @@ -353,12 +640,12 @@ class spell_brutallus_stomp : public SpellScript bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_BURN }); + return ValidateSpellInfo({ SPELL_BURN_DAMAGE }); } void HandleScript(SpellEffIndex /*effIndex*/) { - GetHitUnit()->RemoveAurasDueToSpell(SPELL_BURN); + GetHitUnit()->RemoveAurasDueToSpell(SPELL_BURN_DAMAGE); } void Register() override @@ -367,9 +654,96 @@ class spell_brutallus_stomp : public SpellScript } }; +// 46609 - Freeze +class spell_brutallus_freeze : public SpellScript +{ + PrepareSpellScript(spell_brutallus_freeze); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FREEZE_OBJECT }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_FREEZE_OBJECT); + + /// @temporary: ActivateObject from the spell above doesn't work + if (InstanceScript* instance = GetHitUnit()->GetInstanceScript()) + { + if (GameObject* iceBarrier = instance->GetGameObject(DATA_ICE_BARRIER)) + { + iceBarrier->SetLootState(GO_READY); + iceBarrier->UseDoorOrButton(); + } + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_brutallus_freeze::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 46637 - Break Ice +class spell_brutallus_break_ice : public AuraScript +{ + PrepareAuraScript(spell_brutallus_break_ice); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_BREAK_ICE_OBJECT, SPELL_BREAK_ICE_KNOCK_BACK }); + } + + void AfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->CastSpell(GetTarget(), SPELL_BREAK_ICE_OBJECT); + + /// @temporary: ActivateObject from the spell above doesn't work + if (InstanceScript* instance = GetTarget()->GetInstanceScript()) + { + if (GameObject* iceBarrier = instance->GetGameObject(DATA_ICE_BARRIER)) + { + iceBarrier->SetLootState(GO_READY); + iceBarrier->UseDoorOrButton(); + } + } + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->CastSpell(GetTarget(), SPELL_BREAK_ICE_KNOCK_BACK); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_brutallus_break_ice::AfterApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_brutallus_break_ice::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 4853 +class at_the_dead_scar : public OnlyOnceAreaTriggerScript +{ +public: + at_the_dead_scar() : OnlyOnceAreaTriggerScript("at_the_dead_scar") { } + + bool TryHandleOnce(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + { + player->SummonCreature(NPC_MADRIGOSA, MadrigosaSpawnPos, TEMPSUMMON_MANUAL_DESPAWN); + return true; + } +}; + void AddSC_boss_brutallus() { RegisterSunwellPlateauCreatureAI(boss_brutallus); + RegisterSunwellPlateauCreatureAI(npc_madrigosa); + RegisterSpellScript(spell_brutallus_burn_primer); + RegisterSpellScript(spell_brutallus_burn_ally); RegisterSpellScript(spell_brutallus_burn); RegisterSpellScript(spell_brutallus_stomp); + RegisterSpellScript(spell_brutallus_freeze); + RegisterSpellScript(spell_brutallus_break_ice); + new at_the_dead_scar(); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp index c2960845be9..baad3460be8 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp @@ -50,6 +50,7 @@ ObjectData const creatureData[] = { NPC_SATHROVARR, DATA_SATHROVARR }, { NPC_BRUTALLUS, DATA_BRUTALLUS }, { NPC_MADRIGOSA, DATA_MADRIGOSA }, + { NPC_WORLD_TRIGGER, DATA_WORLD_TRIGGER }, { NPC_FELMYST, DATA_FELMYST }, { NPC_GRAND_WARLOCK_ALYTHESS, DATA_ALYTHESS }, { NPC_LADY_SACROLASH, DATA_SACROLASH }, @@ -61,6 +62,12 @@ ObjectData const creatureData[] = { 0, 0 } // END }; +ObjectData const gameObjectData[] = +{ + { GO_ICE_BARRIER, DATA_ICE_BARRIER }, + { 0, 0 } //END +}; + BossBoundaryData const boundaries = { { DATA_KALECGOS, new BoundaryUnionBoundary(new CircleBoundary(Position(1704.9f, 928.4f), 34.0), new RectangleBoundary(1689.2f, 1713.3f, 762.2f, 1074.8f)) } @@ -78,7 +85,7 @@ class instance_sunwell_plateau : public InstanceMapScript SetHeaders(DataHeader); SetBossNumber(EncounterCount); LoadDoorData(doorData); - LoadObjectData(creatureData, nullptr); + LoadObjectData(creatureData, gameObjectData); LoadBossBoundaries(boundaries); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h index 2ddb4233a18..7dfc6e55329 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h @@ -39,6 +39,7 @@ enum SWPDataTypes DATA_KALECGOS_HUMAN, DATA_SATHROVARR, DATA_MADRIGOSA, + DATA_WORLD_TRIGGER, DATA_ALYTHESS, DATA_SACROLASH, DATA_KILJAEDEN_CONTROLLER, @@ -51,6 +52,8 @@ enum SWPDataTypes DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_3, DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_4, + DATA_ICE_BARRIER, + // Misc DATA_PLAYER_GUID }; @@ -63,9 +66,12 @@ enum SWPCreatureIds NPC_KALECGOS_HUMAN = 24891, NPC_SATHROVARR = 24892, NPC_BRUTALLUS = 24882, - NPC_MADRIGOSA = 24895, NPC_FELMYST = 25038, + // Brutallus + NPC_MADRIGOSA = 24895, + NPC_WORLD_TRIGGER = 19871, + NPC_DEAD = 25268, NPC_FLIGHT_LEFT = 25357, NPC_FLIGHT_RIGHT = 25358, @@ -112,7 +118,8 @@ enum SWPGameObjectIds GO_FIRE_BARRIER = 188075, GO_MURUS_GATE_1 = 187990, GO_MURUS_GATE_2 = 188118, - GO_SPECTRAL_RIFT = 187055 + GO_SPECTRAL_RIFT = 187055, + GO_ICE_BARRIER = 188119 }; template <class AI, class T> |