diff options
5 files changed, 899 insertions, 0 deletions
diff --git a/sql/updates/world/master/2025_01_30_00_world.sql b/sql/updates/world/master/2025_01_30_00_world.sql new file mode 100644 index 00000000000..78e5a274a2e --- /dev/null +++ b/sql/updates/world/master/2025_01_30_00_world.sql @@ -0,0 +1,85 @@ +-- Creature +UPDATE `creature_template_difficulty` SET `HealthScalingExpansion`=10, `CreatureDifficultyID`=274040, `TypeFlags2`=128 WHERE (`Entry`=223237 AND `DifficultyID`=23); -- Volatile Spike Stalker +UPDATE `creature_template_difficulty` SET `LevelScalingDeltaMin`=2, `LevelScalingDeltaMax`=2, `ContentTuningID`=2722, `StaticFlags1`=0x10000000, `VerifiedBuild`=58238 WHERE (`Entry`=210108 AND `DifficultyID`=23); -- 210108 (E.D.N.A.) - CanSwim + +UPDATE `creature_template` SET `faction`=14, `BaseAttackTime`=2000, `unit_flags`=0x2000000, `unit_flags2`=0x800, `unit_flags3`=0x41080001, `AIName`='SmartAI' WHERE `entry`=223237; -- Volatile Spike Stalker +UPDATE `creature_template` SET `ScriptName`='boss_edna' WHERE `entry`=210108; -- 210108 (E.D.N.A.) +UPDATE `creature_template` SET `ScriptName`='npc_skardyn_invader' WHERE `entry`=224516; -- 224516 (Skardyn Invader) +UPDATE `creature_template` SET `StringId`='edna_intro_trash' WHERE `entry`=210109; -- 210109 (Earth Infused Golem) + +DELETE FROM `creature_template_addon` WHERE `entry`=210108; +INSERT INTO `creature_template_addon` (`entry`, `PathId`, `mount`, `MountCreatureID`, `StandState`, `AnimTier`, `VisFlags`, `SheathState`, `PvPFlags`, `emote`, `aiAnimKit`, `movementAnimKit`, `meleeAnimKit`, `visibilityDistanceType`, `auras`) VALUES +(210108, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, '451704 447230 422356'); + +-- Instance +DELETE FROM `instance_template` WHERE `map`=2652; +INSERT INTO `instance_template` (`map`, `parent`, `script`) VALUES +(2652, 0, 'instance_the_stonevault'); + +-- Areatriggers +DELETE FROM `areatrigger_template` WHERE (`IsCustom`=0 AND `Id` IN (33881, 19401)); +INSERT INTO `areatrigger_template` (`Id`, `IsCustom`, `Flags`, `VerifiedBuild`) VALUES +(33881, 0, 0, 58238), +(19401, 0, 0, 58238); + +DELETE FROM `areatrigger_create_properties` WHERE (`IsCustom`=0 AND `Id` IN (33613, 30324)); +INSERT INTO `areatrigger_create_properties` (`Id`, `IsCustom`, `AreaTriggerId`, `IsAreatriggerCustom`, `Flags`, `MoveCurveId`, `ScaleCurveId`, `MorphCurveId`, `FacingCurveId`, `AnimId`, `AnimKitId`, `DecalPropertiesId`, `SpellForVisuals`, `TimeToTargetScale`, `Speed`, `Shape`, `ShapeData0`, `ShapeData1`, `ShapeData2`, `ShapeData3`, `ShapeData4`, `ShapeData5`, `ShapeData6`, `ShapeData7`, `ScriptName`, `VerifiedBuild`) VALUES +(33613, 0, 33881, 0, 4, 0, 0, 0, 0, -1, 0, 0, 424909, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 'at_edna_volatile_spike', 58238), -- SpellForVisuals: 424909 (Volatile Spike) +(30324, 0, 19401, 0, 4, 0, 0, 0, 0, -1, 0, 0, 424909, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, '', 58238); -- SpellForVisuals: 424909 (Volatile Spike) Unknown purpose (maybe testing?) + +-- Spells +DELETE FROM `spell_script_names` WHERE `spell_id` IN (451705, 447230, 451728, 424889, 424903, 452738, 424805, 424879); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(451705, 'spell_edna_energize'), +(447230, 'spell_edna_skarden_spawn_rp_periodic'), +(451728, 'spell_edna_skarden_spawn_rp'), +(424889, 'spell_edna_seismic_reverberation'), +(424903, 'spell_edna_volatile_spike_selector'), +(452738, 'spell_edna_refracting_beam_selector'), +(424805, 'spell_edna_refracting_beam_instakill'), +(424879, 'spell_edna_earth_shatterer'); + +-- Volatile Spike SAI +DELETE FROM `smart_scripts` WHERE `entryorguid`=223237 AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `Difficulties`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param_string`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `action_param7`, `action_param_string`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_param_string`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(223237, 0, 0, 0, '', 63, 0, 100, 0, 0, 0, 0, 0, 0, '', 85, 424909, 2, 0, 0, 0, 0, 0, NULL, 1, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 'Volatile Spike Stalker - On Summoned - Cast Self: Volatile Spike'), +(223237, 0, 1, 2, '', 8, 0, 100, 0, 448207, 0, 0, 0, 0, '', 85, 424913, 0, 0, 0, 0, 0, 0, NULL, 1, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 'Volatile Spike Stalker - On Spellhit \'Refracting Beam\' - Cast Self: Volatile Explosion'), +(223237, 0, 2, 0, '', 61, 0, 100, 0, 0, 0, 0, 0, 0, '', 41, 0, 0, 0, 0, 0, 0, 0, NULL, 1, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 'Volatile Spike Stalker - On Spellhit \'Refracting Beam\' - Despawn'), +(223237, 0, 3, 4, '', 8, 0, 100, 0, 448220, 0, 0, 0, 0, '', 85, 424913, 0, 0, 0, 0, 0, 0, NULL, 1, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 'Volatile Spike Stalker - On Spellhit \'Earth Shatterer\' - Cast Self: Volatile Explosion'), +(223237, 0, 4, 0, '', 61, 0, 100, 0, 0, 0, 0, 0, 0, '', 41, 0, 0, 0, 0, 0, 0, 0, NULL, 1, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 'Volatile Spike Stalker - On Spellhit \'Earth Shatterer\' - Despawn'); + +-- Conditions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` = 424805; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `ConditionStringValue1`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 4, 424805, 0, 0, 51, 0, 5, 224516, 0, '', 0, 0, 0, '', 'Spell \'Refracting Beam\' can only hit \'Skardyn Invader\''); + +-- Texts +DELETE FROM `creature_text` WHERE `CreatureID`= 210108; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(210108, 0, 0, 'Intruders approaching. Systems on.', 14, 0, 100, 0, 0, 247879, 262600, 0, 'E.D.N.A.'), +(210108, 1, 0, 'Activating defense protocols.', 14, 0, 100, 0, 0, 247879, 262601, 0, 'E.D.N.A.'), +(210108, 2, 0, 'Seismic protocol activated.', 14, 0, 100, 0, 0, 247883, 262604, 0, 'E.D.N.A.'), +(210108, 3, 0, 'Offensive measures engaged.', 14, 0, 100, 0, 0, 250762, 262608, 0, 'E.D.N.A.'), +(210108, 4, 0, 'Dominant intruder identified.', 14, 0, 100, 0, 0, 247884, 262605, 0, 'E.D.N.A.'), +(210108, 5, 0, '|TInterface\\ICONS\\INV_ElementalEarth2.blp:20|t %s begins to cast |cFFFF0000|Hspell:424888|h[Seismic Smash]|h|r.', 41, 0, 100, 0, 0, 247884, 0, 0, 'E.D.N.A.'), -- Unknown BroadcastTextId +(210108, 6, 0, 'Engaging offensive protocols.', 14, 0, 100, 0, 0, 247885, 262606, 0, 'E.D.N.A.'), +(210108, 7, 0, 'Intruder terminated.', 14, 0, 100, 0, 0, 247881, 262602, 0, 'E.D.N.A.'), +(210108, 8, 0, 'All intruders terminated.', 14, 0, 100, 0, 0, 247889, 262610, 0, 'E.D.N.A.'), +(210108, 9, 0, 'All systems... shutting down...', 14, 0, 100, 0, 0, 247890, 262611, 0, 'E.D.N.A.'); + +-- Conversations +DELETE FROM `conversation_actors` WHERE (`Idx`=0 AND `ConversationId` IN (25769,25768)); +INSERT INTO `conversation_actors` (`ConversationId`, `ConversationActorId`, `Idx`, `CreatureId`, `CreatureDisplayInfoId`, `NoActorObject`, `ActivePlayerObject`, `VerifiedBuild`) VALUES +(25769, 99466, 0, 224736, 119519, 0, 0, 58630), +(25768, 99466, 0, 224736, 119519, 0, 0, 58630); + +DELETE FROM `conversation_line_template` WHERE `Id` IN (70041, 70040, 70039); +INSERT INTO `conversation_line_template` (`Id`, `UiCameraID`, `ActorIdx`, `Flags`, `ChatType`, `VerifiedBuild`) VALUES +(70041, 1218, 0, 0, 0, 58630), +(70040, 1218, 0, 0, 0, 58630), +(70039, 1218, 0, 0, 0, 58630); + +DELETE FROM `conversation_template` WHERE `Id` IN (25814, 25775, 25773, 25772, 25771, 25770, 25769, 25768, 25767, 26511); +INSERT INTO `conversation_template` (`Id`, `FirstLineID`, `TextureKitId`, `VerifiedBuild`) VALUES +(25769, 70041, 0, 58630), +(25768, 70039, 0, 58630); diff --git a/src/server/scripts/KhazAlgar/TheStoneVault/boss_edna.cpp b/src/server/scripts/KhazAlgar/TheStoneVault/boss_edna.cpp new file mode 100644 index 00000000000..b4e139cc3ca --- /dev/null +++ b/src/server/scripts/KhazAlgar/TheStoneVault/boss_edna.cpp @@ -0,0 +1,593 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "AreaTrigger.h" +#include "AreaTriggerAI.h" +#include "Containers.h" +#include "Conversation.h" +#include "Creature.h" +#include "InstanceScript.h" +#include "MotionMaster.h" +#include "ObjectAccessor.h" +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "Spell.h" +#include "SpellAuraEffects.h" +#include "SpellMgr.h" +#include "SpellScript.h" +#include "TaskScheduler.h" +#include "TemporarySummon.h" +#include "the_stonevault.h" + +enum EdnaSpells +{ + // Intro + SPELL_REFRACTING_BEAM_INTRO = 464888, + SPELL_SKARDEN_SPAWN_PERIODIC = 447230, + SPELL_SKARDEN_SPAWN_RP = 451728, + SPELL_SHADOW_DISSOLVE_IN = 448168, + + // Combat + SPELL_EDNA_ENERGIZE = 451705, + SPELL_EDNA_START_ENERGY = 456814, + SPELL_EARTH_SHATTERER = 424879, + SPELL_EARTH_SHATTERER_MISSILE = 448218, + SPELL_REFRACTING_BEAM = 424795, + SPELL_REFRACTING_BEAM_DAMAGE = 424805, + SPELL_REFRACTING_BEAM_SELECTOR = 452738, + SPELL_VOLATILE_SPIKE_SELECTOR = 424903, + SPELL_VOLATILE_SPIKE_MISSILE = 424908, + SPELL_VOLATILE_EXPLOSION = 424913, + SPELL_SEISMIC_SMASH = 424888, + SPELL_STONE_SHIELD = 424893, + SPELL_EDNA_DEFEATED = 464827, + + SPELL_ANCHOR_HERE = 45313 +}; + +enum EdnaEvents +{ + EVENT_VOLATILE_SPIKE = 1, + EVENT_REFRACTING_BEAM, + EVENT_SEISMIC_SMASH, + EVENT_CHECK_ENERGY +}; + +enum EdnaTexts +{ + SAY_INTRO = 0, + SAY_AGGRO = 1, + SAY_VOLATILE_SPIKE = 2, + SAY_REFRACTING_BEAM = 3, + SAY_SEISMIC_SMASH = 4, + SAY_SEISMIC_SMASH_ALERT = 5, + SAY_EARTH_SHATTERER = 6, + SAY_SLAY = 7, + SAY_WIPE = 8, + SAY_DEATH = 9 +}; + +enum EdnaMisc +{ + POINT_EDNA = 0, + POINT_START_COMBAT = 1, + + CONVERSATION_INTRO = 25768, + + NPC_VOLATILE_SPIKE = 223237 +}; + +static constexpr Position EdnaCombatPosition = { 1.94097f, 0.512153f, 361.66537f }; +static constexpr Position SkardenSpawnPositions[4] = +{ + { -11.276042f, -19.234375f, 361.8286f, 2.490876f }, + { -11.395833f, 19.628473f, 361.8286f, 3.805543f }, + { -25.248264f, 34.609375f, 361.8286f, 4.397980f }, + { -20.77257f, -34.930557f, 361.82837f, 1.994346f } +}; + +// 210108 - E.D.N.A. +struct boss_edna : public BossAI +{ + boss_edna(Creature* creature) : BossAI(creature, DATA_EDNA), _refractingBeamCount(1), _seismicSmashCount(1), _volatileSpikeCount(1) { } + + void InitializeAI() override + { + if (instance->GetBossState(DATA_EDNA) != NOT_STARTED) + me->Relocate(EdnaCombatPosition); + } + + void Reset() override + { + BossAI::Reset(); + + _refractingBeamCount = 1; + _seismicSmashCount = 1; + _volatileSpikeCount = 1; + } + + void JustAppeared() override + { + if (instance->GetData(DATA_EDNA_INTRO_STATE) == DONE) + { + me->RemoveAurasDueToSpell(SPELL_SKARDEN_SPAWN_PERIODIC); + me->SetImmuneToPC(false); + } + + me->SetPowerType(POWER_ENERGY); + me->SetPower(POWER_ENERGY, 4); + + if (IsMythic() || IsMythicPlus()) + DoCastSelf(SPELL_EDNA_START_ENERGY); + } + + void KilledUnit(Unit* victim) override + { + if (!victim->IsPlayer()) + return; + + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + Talk(SAY_DEATH); + DoCast(SPELL_EDNA_DEFEATED); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + Talk(SAY_WIPE); + + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + summons.DespawnAll(); + _EnterEvadeMode(); + _DespawnAtEvade(); + } + + void DoAction(int32 actionId) override + { + if (actionId != ACTION_START_EDNA_INTRO) + return; + + Conversation::CreateConversation(CONVERSATION_INTRO, me, me->GetPosition(), ObjectGuid::Empty); + scheduler.Schedule(3s, [this](TaskContext /*context*/) + { + me->RemoveAurasDueToSpell(SPELL_SKARDEN_SPAWN_PERIODIC); + me->GetMotionMaster()->MovePoint(POINT_START_COMBAT, EdnaCombatPosition, true, {}, {}, MovementWalkRunSpeedSelectionMode::ForceWalk); + }); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (id != POINT_START_COMBAT) + return; + + scheduler.Schedule(1s, [this](TaskContext /*context*/) + { + instance->SetData(DATA_EDNA_INTRO_STATE, DONE); + Talk(SAY_INTRO); + DoCastSelf(SPELL_ANCHOR_HERE, TRIGGERED_FULL_MASK); + me->SetImmuneToPC(false); + }); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + + Talk(SAY_AGGRO); + + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); + + if (IsMythic() || IsMythicPlus()) + { + events.ScheduleEvent(EVENT_CHECK_ENERGY, 500ms); + events.ScheduleEvent(EVENT_VOLATILE_SPIKE, 6s); + events.ScheduleEvent(EVENT_REFRACTING_BEAM, 14s); + events.ScheduleEvent(EVENT_SEISMIC_SMASH, 18s); + + DoCastSelf(SPELL_EDNA_ENERGIZE); + } + else + { + events.ScheduleEvent(EVENT_VOLATILE_SPIKE, 8100ms); + events.ScheduleEvent(EVENT_REFRACTING_BEAM, 11800ms); + events.ScheduleEvent(EVENT_SEISMIC_SMASH, 15400ms); + } + } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_VOLATILE_SPIKE: + { + Talk(SAY_VOLATILE_SPIKE); + DoCast(SPELL_VOLATILE_SPIKE_SELECTOR); + + _volatileSpikeCount++; + if (IsMythic() || IsMythicPlus()) + { + if (_volatileSpikeCount % 2 == 0) + events.Repeat(20s); + else + events.Repeat(28s); + } + else + events.Repeat(14600ms); + break; + } + case EVENT_REFRACTING_BEAM: + { + Talk(SAY_REFRACTING_BEAM); + DoCast(SPELL_REFRACTING_BEAM_SELECTOR); + + _refractingBeamCount++; + if (IsMythic() || IsMythicPlus()) + { + if (_refractingBeamCount % 2 == 0) + events.Repeat(20s); + else + events.Repeat(28s); + } + else + events.Repeat(10900ms); + + break; + } + case EVENT_SEISMIC_SMASH: + { + Talk(SAY_SEISMIC_SMASH); + Talk(SAY_SEISMIC_SMASH_ALERT); + DoCastVictim(SPELL_SEISMIC_SMASH); + + _seismicSmashCount++; + if (IsMythic() || IsMythicPlus()) + { + if (_seismicSmashCount % 2 == 0) + events.Repeat(20s); + else + events.Repeat(28s); + } + else + events.Repeat(23100ms); + break; + } + case EVENT_CHECK_ENERGY: + { + if (me->GetPower(POWER_ENERGY) >= 95) + { + Talk(SAY_EARTH_SHATTERER); + DoCast(SPELL_EARTH_SHATTERER); + events.Repeat(4s); + } + else + events.Repeat(500ms); + break; + } + default: + break; + } + } + +private: + uint16 _refractingBeamCount; + uint16 _seismicSmashCount; + uint16 _volatileSpikeCount; +}; + +// 224516 - Skardyn Invader +struct npc_skardyn_invader : public ScriptedAI +{ + npc_skardyn_invader(Creature* creature) : ScriptedAI(creature) { } + + void JustAppeared() override + { + DoCastSelf(SPELL_SHADOW_DISSOLVE_IN); + + _scheduler.Schedule(2400ms, [this](TaskContext /*context*/) + { + TempSummon* summon = me->ToTempSummon(); + if (!summon) + return; + + if (Unit* summoner = summon->GetSummonerUnit()) + { + me->GetMotionMaster()->MovePoint(POINT_EDNA, summoner->GetPosition()); + summoner->CastSpell(me, SPELL_REFRACTING_BEAM_INTRO, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR); + } + }); + } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } + +private: + TaskScheduler _scheduler; +}; + +// 451705 - E.D.N.A. Energize +class spell_edna_energize : public AuraScript +{ + static constexpr std::array<uint8, 6> EdnaEnergizeCycle = { 2, 2, 2, 2, 3, 2 }; + + void PeriodicTick(AuraEffect const* aurEff) const + { + uint8 cycleIdx = aurEff->GetTickNumber() % EdnaEnergizeCycle.size(); + GetTarget()->ModifyPower(POWER_ENERGY, EdnaEnergizeCycle[cycleIdx]); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_edna_energize::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +// 447230 - Skarden Spawn RP +class spell_edna_skarden_spawn_rp_periodic : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SKARDEN_SPAWN_RP }); + } + + void HandlePeriodic(AuraEffect const* aurEff) const + { + Unit* target = GetTarget(); + + target->CastSpell(target, SPELL_SKARDEN_SPAWN_RP, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringAura = aurEff + }); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_edna_skarden_spawn_rp_periodic::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +// 451728 - Skarden Spawn RP +class spell_edna_skarden_spawn_rp : public SpellScript +{ + static void SetDest(SpellDestination& dest) + { + dest.Relocate(Trinity::Containers::SelectRandomContainerElement(SkardenSpawnPositions)); + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_edna_skarden_spawn_rp::SetDest, EFFECT_0, TARGET_DEST_DEST); + } +}; + +// 424889 - Seismic Reverberation +class spell_edna_seismic_reverberation : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_STONE_SHIELD }); + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) const + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL) + return; + + Unit* target = GetTarget(); + + target->CastSpell(target, SPELL_STONE_SHIELD, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringAura = aurEff + }); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_edna_seismic_reverberation::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 424903 - Volatile Spike +class spell_edna_volatile_spike_selector : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_VOLATILE_SPIKE_MISSILE }); + } + + void HandleHitTarget(SpellEffIndex /*effIndex*/) const + { + GetCaster()->CastSpell(GetHitUnit()->GetPosition(), SPELL_VOLATILE_SPIKE_MISSILE, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = GetSpell(), + .OriginalCastId = GetSpell()->m_castId + }); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_edna_volatile_spike_selector::HandleHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// 452738 - Refracting Beam +class spell_edna_refracting_beam_selector : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_REFRACTING_BEAM }) + && ValidateSpellEffect({ { SPELL_REFRACTING_BEAM_DAMAGE, EFFECT_3 } }); + } + + void HandleHitTarget(SpellEffIndex /*effIndex*/) + { + GetCaster()->m_Events.AddEvent([this, hitUnitGUID = GetHitUnit()->GetGUID()]() + { + Unit* hitUnit = ObjectAccessor::GetUnit(*GetCaster(), hitUnitGUID); + if (!hitUnit) + return; + + GetCaster()->CastSpell(GetHitUnit(), SPELL_REFRACTING_BEAM, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = GetSpell(), + .OriginalCastId = GetSpell()->m_castId + }); + }, _timeMultiplier * 500ms); + + _timeMultiplier++; + } + + void FilterTargets(std::list<WorldObject*>& targets) const + { + SpellInfo const* spell = sSpellMgr->GetSpellInfo(SPELL_REFRACTING_BEAM_DAMAGE, GetCastDifficulty()); + uint32 maxTargets = spell->GetEffect(EFFECT_3).CalcValue(GetCaster()); + + if (targets.size() > maxTargets) + targets.resize(maxTargets); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_edna_refracting_beam_selector::HandleHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_edna_refracting_beam_selector::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); + } + +private: + uint8 _timeMultiplier = 0u; +}; + +// 424805 - Refracting Beam +class spell_edna_refracting_beam_instakill : public SpellScript +{ + void FilterTargets(std::list<WorldObject*>& targets) const + { + static constexpr uint8 MAX_TARGETS = 1; + + if (targets.size() <= MAX_TARGETS) + return; + + auto closestTargetItr = std::ranges::min_element(targets, [caster = GetCaster()](WorldObject const* left, WorldObject const* right) + { + return caster->GetDistance(left->GetPosition()) < caster->GetDistance(right->GetPosition()); + }); + + if (closestTargetItr == targets.end()) + return; + + WorldObject* closestTarget = *closestTargetItr; + targets.clear(); + targets.push_back(closestTarget); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_edna_refracting_beam_instakill::FilterTargets, EFFECT_2, TARGET_UNIT_LINE_CASTER_TO_DEST); + } +}; + +// 424879 - Earth Shatterer +class spell_edna_earth_shatterer : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_EARTH_SHATTERER_MISSILE }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) const + { + Unit* caster = GetCaster(); + std::list<Creature*> volatileSpikes; + GetCreatureListWithEntryInGrid(volatileSpikes, caster, NPC_VOLATILE_SPIKE, 200.0f); + + for (Creature* spike : volatileSpikes) + { + caster->CastSpell(spike, SPELL_EARTH_SHATTERER_MISSILE, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = GetSpell(), + .OriginalCastId = GetSpell()->m_castId + }); + } + } + + void HandleAfterCast() const + { + Unit* caster = GetCaster(); + caster->SetPower(caster->GetPowerType(), 0); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_edna_earth_shatterer::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + AfterCast += SpellCastFn(spell_edna_earth_shatterer::HandleAfterCast); + } +}; + +// 424909 - Volatile Spike +// Id - 33613 +struct at_edna_volatile_spike : AreaTriggerAI +{ + at_edna_volatile_spike(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) {} + + void OnUnitEnter(Unit* unit) override + { + if (!unit->IsPlayer()) + return; + + Unit* caster = at->GetCaster(); + if (!caster) + return; + + caster->CastSpell(unit, SPELL_VOLATILE_EXPLOSION, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR); + if (Creature* casterCreature = caster->ToCreature()) + casterCreature->DespawnOrUnsummon(); + } +}; + +void AddSC_boss_edna() +{ + RegisterTheStonevaultCreatureAI(boss_edna); + RegisterTheStonevaultCreatureAI(npc_skardyn_invader); + RegisterSpellScript(spell_edna_energize); + RegisterSpellScript(spell_edna_skarden_spawn_rp_periodic); + RegisterSpellScript(spell_edna_skarden_spawn_rp); + RegisterSpellScript(spell_edna_seismic_reverberation); + RegisterSpellScript(spell_edna_volatile_spike_selector); + RegisterSpellScript(spell_edna_refracting_beam_selector); + RegisterSpellScript(spell_edna_refracting_beam_instakill); + RegisterSpellScript(spell_edna_earth_shatterer); + RegisterAreaTriggerAI(at_edna_volatile_spike); +} diff --git a/src/server/scripts/KhazAlgar/TheStoneVault/instance_the_stonevault.cpp b/src/server/scripts/KhazAlgar/TheStoneVault/instance_the_stonevault.cpp new file mode 100644 index 00000000000..b573adb1137 --- /dev/null +++ b/src/server/scripts/KhazAlgar/TheStoneVault/instance_the_stonevault.cpp @@ -0,0 +1,141 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "AreaBoundary.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "InstanceScript.h" +#include "ScriptMgr.h" +#include "the_stonevault.h" + +BossBoundaryData const boundaries = +{ + { DATA_EDNA, new CircleBoundary({ 0.0f, 0.0f }, 58.0f)} +}; + +ObjectData const creatureData[] = +{ + { BOSS_EDNA, DATA_EDNA }, + { BOSS_SKARMORAX, DATA_SKARMORAK }, + { BOSS_SPEAKER_DORLITA, DATA_SPEAKER_DORLITA }, + { BOSS_SPEAKER_BROKK, DATA_SPEAKER_BROKK }, + { BOSS_VOID_SPEAKER_EIRICH, DATA_VOID_SPEAKER_EIRICH }, + { 0, 0 } // END +}; + +static DoorData const doorData[] = +{ + { GO_FOUNDRY_DOOR_ENTRANCE, DATA_EDNA, EncounterDoorBehavior::OpenWhenNotInProgress }, + { GO_FOUNDRY_DOOR_TOWARDS_SKARMORAK, DATA_EDNA, EncounterDoorBehavior::OpenWhenDone }, + { GO_FOUNDRY_DOOR_TOWARDS_MACHINISTS, DATA_EDNA, EncounterDoorBehavior::OpenWhenDone }, + { 0, 0, EncounterDoorBehavior::OpenWhenNotInProgress } +}; + +DungeonEncounterData const encounters[] = +{ + { DATA_EDNA, {{ 2854 }} }, + { DATA_SKARMORAK, {{ 2880 }} }, + { DATA_MASTER_MACHINISTS, {{ 2883 }} }, + { DATA_VOID_SPEAKER_EIRICH, {{ 2888 }} } +}; + +constexpr uint8 EDNA_INTRO_REQUIRED_KILLS = 5; + +class instance_the_stonevault : public InstanceMapScript +{ +public: + instance_the_stonevault() : InstanceMapScript(TSVScriptName, 2652) { } + + struct instance_the_stonevault_InstanceMapScript: public InstanceScript + { + instance_the_stonevault_InstanceMapScript(InstanceMap* map) : InstanceScript(map) + { + SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadObjectData(creatureData, nullptr); + LoadDoorData(doorData); + LoadBossBoundaries(boundaries); + LoadDungeonEncounterData(encounters); + + _ednaIntroState = NOT_STARTED; + _ednaIntroNPCsKillCount = 0; + } + + uint32 GetData(uint32 dataId) const override + { + switch (dataId) + { + case DATA_EDNA_INTRO_STATE: + return _ednaIntroState; + default: + break; + } + return 0; + } + + void SetData(uint32 dataId, uint32 value) override + { + switch (dataId) + { + case DATA_EDNA_INTRO_STATE: + _ednaIntroState = value; + break; + default: + break; + } + } + + void OnUnitDeath(Unit* unit) override + { + Creature* creature = unit->ToCreature(); + if (!creature) + return; + + if (creature->HasStringId("edna_intro_trash")) + { + if (_ednaIntroState != NOT_STARTED) + return; + + _ednaIntroNPCsKillCount++; + if (_ednaIntroNPCsKillCount < EDNA_INTRO_REQUIRED_KILLS) + return; + + _ednaIntroState = IN_PROGRESS; + + Creature* edna = GetCreature(DATA_EDNA); + if (!edna) + return; + + edna->AI()->DoAction(ACTION_START_EDNA_INTRO); + } + } + + private: + uint8 _ednaIntroState; + uint8 _ednaIntroNPCsKillCount; + }; + + InstanceScript* GetInstanceScript(InstanceMap* map) const override + { + return new instance_the_stonevault_InstanceMapScript(map); + } +}; + +void AddSC_instance_the_stonevault() +{ + new instance_the_stonevault(); +} diff --git a/src/server/scripts/KhazAlgar/TheStoneVault/the_stonevault.h b/src/server/scripts/KhazAlgar/TheStoneVault/the_stonevault.h new file mode 100644 index 00000000000..fdd509ef9c8 --- /dev/null +++ b/src/server/scripts/KhazAlgar/TheStoneVault/the_stonevault.h @@ -0,0 +1,72 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef DEF_THE_STONEVAULT_H_ +#define DEF_THE_STONEVAULT_H_ + +#include "CreatureAIImpl.h" + +#define TSVScriptName "instance_the_stonevault" +#define DataHeader "TheStonevault" + +constexpr uint32 EncounterCount = 4; + +enum TheStonevaultDataTypes +{ + // Encounters + DATA_EDNA = 0, + DATA_SKARMORAK = 1, + DATA_MASTER_MACHINISTS = 2, + DATA_VOID_SPEAKER_EIRICH = 3, + + // Additional Data + DATA_SPEAKER_DORLITA, + DATA_SPEAKER_BROKK, + DATA_EDNA_INTRO_STATE +}; + +enum TheStonevaultCreatureIds +{ + // Bosses + BOSS_EDNA = 210108, + BOSS_SKARMORAX = 210156, + BOSS_SPEAKER_DORLITA = 213216, + BOSS_SPEAKER_BROKK = 213217, + BOSS_VOID_SPEAKER_EIRICH = 213119 +}; + +enum TheStonevaultGameObjectIds +{ + GO_FOUNDRY_DOOR_ENTRANCE = 440236, + GO_FOUNDRY_DOOR_TOWARDS_SKARMORAK = 440235, + GO_FOUNDRY_DOOR_TOWARDS_MACHINISTS = 440239 +}; + +enum TheStonevaultSharedActions +{ + ACTION_START_EDNA_INTRO = 0 +}; + +template <class AI, class T> +inline AI* GetTheStonevaultAI(T* obj) +{ + return GetInstanceAI<AI>(obj, TSVScriptName); +} + +#define RegisterTheStonevaultCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetTheStonevaultAI) + +#endif diff --git a/src/server/scripts/KhazAlgar/khaz_algar_script_loader.cpp b/src/server/scripts/KhazAlgar/khaz_algar_script_loader.cpp index 13e6521560c..c2742c92dfd 100644 --- a/src/server/scripts/KhazAlgar/khaz_algar_script_loader.cpp +++ b/src/server/scripts/KhazAlgar/khaz_algar_script_loader.cpp @@ -23,6 +23,10 @@ void AddSC_zone_dornogal(); // Zone Isle Of Dorn void AddSC_zone_isle_of_dorn(); +// The Stonevault +void AddSC_instance_the_stonevault(); +void AddSC_boss_edna(); + // Nerub'ar Palace void AddSC_instance_nerubar_palace(); void AddSC_boss_ulgrax_the_devourer(); @@ -37,6 +41,10 @@ void AddKhazAlgarScripts() // Zone Isle of Dorn AddSC_zone_isle_of_dorn(); + // The Stonevault + AddSC_instance_the_stonevault(); + AddSC_boss_edna(); + // Nerub'ar Palace AddSC_instance_nerubar_palace(); AddSC_boss_ulgrax_the_devourer(); |