diff options
author | Aqua Deus <95978183+aquadeus@users.noreply.github.com> | 2025-09-20 16:55:17 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-09-20 16:55:17 +0200 |
commit | 440cdae48c06ef772892fe680a4a6ccf269774ff (patch) | |
tree | 0608732b7308841aeaf43d6877eefd14a2ae7657 | |
parent | a7f15e7f20db250e57ae260324e126be01665e48 (diff) |
Scripts/DarkmaulCitadel: Implement Tunk encounter (#31195)
5 files changed, 366 insertions, 0 deletions
diff --git a/sql/updates/world/master/2025_09_20_02_world.sql b/sql/updates/world/master/2025_09_20_02_world.sql new file mode 100644 index 00000000000..d8d748f5117 --- /dev/null +++ b/sql/updates/world/master/2025_09_20_02_world.sql @@ -0,0 +1,23 @@ +DELETE FROM `instance_template` WHERE `map`=2236; +INSERT INTO `instance_template` (`map`, `parent`, `script`) VALUES +(2236, 0, 'instance_darkmaul_citadel'); + +DELETE FROM `areatrigger_template` WHERE (`Id`=22171 AND `IsCustom`=0); +INSERT INTO `areatrigger_template` (`Id`, `IsCustom`, `Flags`, `VerifiedBuild`) VALUES +(22171, 0, 0, 49570); + +DELETE FROM `areatrigger_create_properties` WHERE (`Id`=18034 AND `IsCustom`=0); +INSERT INTO `areatrigger_create_properties` (`Id`, `IsCustom`, `AreaTriggerId`, `IsAreatriggerCustom`, `Flags`, `MoveCurveId`, `ScaleCurveId`, `MorphCurveId`, `FacingCurveId`, `AnimId`, `AnimKitId`, `DecalPropertiesId`, `SpellForVisuals`, `TimeToTargetScale`, `Speed`, `SpeedIsTime`, `Shape`, `ShapeData0`, `ShapeData1`, `ShapeData2`, `ShapeData3`, `ShapeData4`, `ShapeData5`, `ShapeData6`, `ShapeData7`, `ScriptName`, `VerifiedBuild`) VALUES +(18034, 0, 22171, 0, 0, 0, 0, 13613, 0, -1, 0, 0, NULL, 6000, 6, 1, 4, 3, 3, 6, 6, 0.300000011920928955, 0.300000011920928955, 0, 0, 'at_tunk_seismic_slam', 49570); -- Spell: 308455 (Seismic Slam) + +UPDATE `creature_template` SET `ScriptName`='boss_tunk' WHERE `entry`=157300; + +DELETE FROM `spell_script_names` WHERE `ScriptName` IN('spell_tunk_seismic_slam_selector', 'spell_tunk_interrupting_shout'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(308454, 'spell_tunk_seismic_slam_selector'), +(321240, 'spell_tunk_interrupting_shout'); + +DELETE FROM `creature_text` WHERE `CreatureID`=157300; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(157300, 0, 0, '|TINTERFACE\\ICONS\\Ability_Earthen_Pillar.blp:20|t Avoid Tunk\'s |cFFFF0404|Hspell:308463|h[Seismic Slam]|h|r by moving to the left or right!', 42, 0, 100, 0, 0, 0, 0, 0, 'Tunk to Player'), -- Missing BroadcastTextId +(157300, 1, 0, '|TINTERFACE\\ICONS\\WARRIOR_DISRUPTINGSHOUT.BLP:20|t Some spells can interrupt your casting. Stop casting to avoid being interrupted by |cFFFF0404|Hspell:321240|h[Interrupting Shout]|h|r!', 42, 0, 100, 0, 0, 0, 0, 0, 'Tunk to Player'); -- Missing BroadcastTextId diff --git a/src/server/scripts/ExilesReach/DarkmaulCitadel/boss_tunk.cpp b/src/server/scripts/ExilesReach/DarkmaulCitadel/boss_tunk.cpp new file mode 100644 index 00000000000..1a0b989b2ef --- /dev/null +++ b/src/server/scripts/ExilesReach/DarkmaulCitadel/boss_tunk.cpp @@ -0,0 +1,206 @@ +/* + * 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 "InstanceScript.h" +#include "PathGenerator.h" +#include "Player.h" +#include "ScriptedCreature.h" +#include "ScriptMgr.h" +#include "SpellInfo.h" +#include "SpellMgr.h" +#include "SpellScript.h" +#include "darkmaul_citadel.h" + +enum TunkSpells +{ + SPELL_INTERRUPTING_SHOUT = 321240, + SPELL_SEISMIC_SLAM_SELECTOR = 308454, + SPELL_SEISMIC_SLAM_AT = 308455, + SPELL_SEISMIC_SLAM_DAMAGE = 308463 +}; + +enum TunkEvents +{ + EVENT_INTERRUPTING_SHOUT = 1, + EVENT_SEISMIC_SLAM +}; + +enum TunkTexts +{ + SAY_WARNING_SEISMIC_SLAM = 0, + SAY_WARNING_INTERRUPT = 1 +}; + +// 157300 - Tunk +struct boss_tunk : public BossAI +{ + boss_tunk(Creature* creature) : BossAI(creature, DATA_TUNK) { } + + void JustDied(Unit* /*killer*/) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + _JustDied(); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + _EnterEvadeMode(); + _DespawnAtEvade(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); + + events.ScheduleEvent(EVENT_INTERRUPTING_SHOUT, 7s); + events.ScheduleEvent(EVENT_SEISMIC_SLAM, 14s); + } + + void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override + { + if (!target->IsPlayer()) + return; + + if (spellInfo->Id == SPELL_SEISMIC_SLAM_DAMAGE) + Talk(SAY_WARNING_SEISMIC_SLAM, target); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_INTERRUPTING_SHOUT: + { + DoCastSelf(SPELL_INTERRUPTING_SHOUT); + events.Repeat(20s); + break; + } + case EVENT_SEISMIC_SLAM: + { + DoCast(SPELL_SEISMIC_SLAM_SELECTOR); + events.Repeat(30s); + break; + } + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + } +}; + +// 308454 - Seismic Slam +class spell_tunk_seismic_slam_selector : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SEISMIC_SLAM_AT }); + } + + void HandleHitTarget(SpellEffIndex /*effIndex*/) const + { + GetCaster()->CastSpell(GetHitUnit()->GetPosition(), SPELL_SEISMIC_SLAM_AT, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = GetSpell() + }); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_tunk_seismic_slam_selector::HandleHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// 308455 - Seismic Slam +struct at_tunk_seismic_slam : AreaTriggerAI +{ + using AreaTriggerAI::AreaTriggerAI; + + void OnInitialize() override + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(at->GetSpellId(), DIFFICULTY_NONE); + if (!spellInfo) + return; + + Unit* caster = at->GetCaster(); + if (!caster) + return; + + Position destPos = at->GetFirstCollisionPosition(spellInfo->GetMaxRange(false, caster), 0.0f); + PathGenerator path(at); + + path.CalculatePath(destPos.GetPositionX(), destPos.GetPositionY(), destPos.GetPositionZ(), false); + + at->InitSplines(path.GetPath()); + } + + void OnUnitEnter(Unit* unit) override + { + if (Unit* caster = at->GetCaster()) + if (caster->IsValidAttackTarget(unit)) + caster->CastSpell(unit, SPELL_SEISMIC_SLAM_DAMAGE, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR); + } +}; + +// 321240 - Interrupting Shout +class spell_tunk_interrupting_shout : public SpellScript +{ + void HandleLaunchTarget(SpellEffIndex /*effIndex*/) const + { + Creature* caster = GetCaster()->ToCreature(); + if (!caster) + return; + + Player* hitPlayer = GetHitUnit()->ToPlayer(); + if (!hitPlayer) + return; + + if (hitPlayer->GetCurrentSpell(CurrentSpellTypes::CURRENT_GENERIC_SPELL) != nullptr) + caster->AI()->Talk(SAY_WARNING_INTERRUPT, hitPlayer); + } + + void Register() override + { + OnEffectLaunchTarget += SpellEffectFn(spell_tunk_interrupting_shout::HandleLaunchTarget, EFFECT_1, SPELL_EFFECT_INTERRUPT_CAST); + } +}; + +void AddSC_boss_tunk() +{ + RegisterDarkmaulCitadelCreatureAI(boss_tunk); + + RegisterSpellScript(spell_tunk_seismic_slam_selector); + RegisterSpellScript(spell_tunk_interrupting_shout); + + RegisterAreaTriggerAI(at_tunk_seismic_slam); +} diff --git a/src/server/scripts/ExilesReach/DarkmaulCitadel/darkmaul_citadel.h b/src/server/scripts/ExilesReach/DarkmaulCitadel/darkmaul_citadel.h new file mode 100644 index 00000000000..9aed3b6c53a --- /dev/null +++ b/src/server/scripts/ExilesReach/DarkmaulCitadel/darkmaul_citadel.h @@ -0,0 +1,59 @@ +/* + * 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_DARKMAUL_CITADEL_H_ +#define DEF_DARKMAUL_CITADEL_H_ + +#include "CreatureAIImpl.h" + +#define DCScriptName "instance_darkmaul_citadel" +#define DataHeader "DarkmaulCitadel" + +constexpr uint32 EncounterCount = 2; + +enum DarkmaulCitadelDataTypes +{ + // Encounters + DATA_TUNK = 0, + DATA_RAVNYR = 1, + + DATA_GORGROTH +}; + +enum DarkmaulCitadelCreatureIds +{ + // Bosses + BOSS_TUNK = 157300, + BOSS_GORGROTH = 156814, + BOSS_RAVNYR = 156501 +}; + +enum DarkmaulCitadelGameObjectIds +{ + GO_SHADOWY_DOOR = 334578, + GO_TEMP_DOOR = 334502 +}; + +template <class AI, class T> +inline AI* GetDarkmaulCitadelAI(T* obj) +{ + return GetInstanceAI<AI>(obj, DCScriptName); +} + +#define RegisterDarkmaulCitadelCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetDarkmaulCitadelAI) + +#endif diff --git a/src/server/scripts/ExilesReach/DarkmaulCitadel/instance_darkmaul_citadel.cpp b/src/server/scripts/ExilesReach/DarkmaulCitadel/instance_darkmaul_citadel.cpp new file mode 100644 index 00000000000..a4c3a3bb134 --- /dev/null +++ b/src/server/scripts/ExilesReach/DarkmaulCitadel/instance_darkmaul_citadel.cpp @@ -0,0 +1,70 @@ +/* + * 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 "Creature.h" +#include "CreatureAI.h" +#include "InstanceScript.h" +#include "ScriptMgr.h" +#include "darkmaul_citadel.h" + +ObjectData const creatureData[] = +{ + { BOSS_TUNK, DATA_TUNK }, + { BOSS_GORGROTH, DATA_GORGROTH }, + { BOSS_RAVNYR, DATA_RAVNYR }, + { 0, 0 } // END +}; + +static DoorData const doorData[] = +{ + { GO_TEMP_DOOR, DATA_TUNK, EncounterDoorBehavior::OpenWhenDone }, + { 0, 0, EncounterDoorBehavior::OpenWhenNotInProgress } +}; + +DungeonEncounterData const encounters[] = +{ + { DATA_TUNK, {{ 2325 }} }, + { DATA_RAVNYR, {{ 2326 }} } +}; + +class instance_darkmaul_citadel : public InstanceMapScript +{ +public: + instance_darkmaul_citadel() : InstanceMapScript(DCScriptName, 2236) { } + + struct instance_darkmaul_citadel_InstanceMapScript: public InstanceScript + { + instance_darkmaul_citadel_InstanceMapScript(InstanceMap* map) : InstanceScript(map) + { + SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadObjectData(creatureData, nullptr); + LoadDoorData(doorData); + LoadDungeonEncounterData(encounters); + } + }; + + InstanceScript* GetInstanceScript(InstanceMap* map) const override + { + return new instance_darkmaul_citadel_InstanceMapScript(map); + } +}; + +void AddSC_instance_darkmaul_citadel() +{ + new instance_darkmaul_citadel(); +} diff --git a/src/server/scripts/ExilesReach/exiles_reach_script_loader.cpp b/src/server/scripts/ExilesReach/exiles_reach_script_loader.cpp index 9aba87f489e..56ec40677b8 100644 --- a/src/server/scripts/ExilesReach/exiles_reach_script_loader.cpp +++ b/src/server/scripts/ExilesReach/exiles_reach_script_loader.cpp @@ -18,9 +18,17 @@ // This is where scripts' loading functions should be declared: void AddSC_zone_exiles_reach(); +// Darkmaul Citacel +void AddSC_instance_darkmaul_citadel(); +void AddSC_boss_tunk(); + // The name of this function should match: // void Add${NameOfDirectory}Scripts() void AddExilesReachScripts() { AddSC_zone_exiles_reach(); + + // Darkmaul Citacel + AddSC_instance_darkmaul_citadel(); + AddSC_boss_tunk(); } |