aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAqua Deus <95978183+aquadeus@users.noreply.github.com>2025-02-04 19:14:20 +0100
committerGitHub <noreply@github.com>2025-02-04 19:14:20 +0100
commit17df38e02506c11a330346b5a2c4b6d88d3a57ab (patch)
tree70dce189679727123a969bd63fcd2c8dbfd4d53e /src
parent2b1ad96be1c102b1d681b7205ec38d2c5de28d9d (diff)
Scripts/AtalDazar: Implement Volkaal encounter (#30663)
Diffstat (limited to 'src')
-rw-r--r--src/server/scripts/Zandalar/AtalDazar/atal_dazar.h10
-rw-r--r--src/server/scripts/Zandalar/AtalDazar/boss_volkaal.cpp457
-rw-r--r--src/server/scripts/Zandalar/AtalDazar/instance_atal_dazar.cpp15
-rw-r--r--src/server/scripts/Zandalar/zandalar_script_loader.cpp2
4 files changed, 481 insertions, 3 deletions
diff --git a/src/server/scripts/Zandalar/AtalDazar/atal_dazar.h b/src/server/scripts/Zandalar/AtalDazar/atal_dazar.h
index 2da36868e0b..afd5015ad09 100644
--- a/src/server/scripts/Zandalar/AtalDazar/atal_dazar.h
+++ b/src/server/scripts/Zandalar/AtalDazar/atal_dazar.h
@@ -38,11 +38,19 @@ enum AtalDazarCreatureIds
{
// Bosses
BOSS_PRIESTESS_ALUNZA = 129614,
- BOSS_VOLKAAL = 129399,
+ BOSS_VOLKAAL = 122965,
BOSS_REZAN = 122963,
BOSS_YAZMA = 129412
};
+enum AtalDazarGameObjectIds
+{
+ GO_VOLKAAL_DOOR_1 = 292399,
+ GO_VOLKAAL_DOOR_2 = 292400,
+ GO_VOLKAAL_DOOR_3 = 292401,
+ GO_VOLKAAL_DOOR_4 = 292402
+};
+
template <class AI, class T>
inline AI* GetAtalDazarAI(T* obj)
{
diff --git a/src/server/scripts/Zandalar/AtalDazar/boss_volkaal.cpp b/src/server/scripts/Zandalar/AtalDazar/boss_volkaal.cpp
new file mode 100644
index 00000000000..358e3ff5171
--- /dev/null
+++ b/src/server/scripts/Zandalar/AtalDazar/boss_volkaal.cpp
@@ -0,0 +1,457 @@
+/*
+ * 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 "AreaTriggerAI.h"
+#include "CellImpl.h"
+#include "Creature.h"
+#include "GridNotifiersImpl.h"
+#include "InstanceScript.h"
+#include "MotionMaster.h"
+#include "MoveSpline.h"
+#include "ObjectAccessor.h"
+#include "ScriptMgr.h"
+#include "ScriptedCreature.h"
+#include "SpellAuraEffects.h"
+#include "SpellScript.h"
+#include "atal_dazar.h"
+
+enum VolkaalSpells
+{
+ SPELL_SOUL_ANCHOR = 259537,
+ SPELL_BAD_VOODOO = 250192,
+ SPELL_RAPID_DECAY = 250241,
+ SPELL_RAPID_DECAY_TARGET = 250694,
+ SPELL_RAPID_DECAY_RANDOM = 250697,
+ SPELL_TOXIC_POOL = 250585,
+ SPELL_REANIMATE = 259531,
+ SPELL_TOXIC_LEAP_SELECTOR = 250708,
+ SPELL_TOXIC_LEAP = 250258,
+ SPELL_TOXIC_LEAP_DAMAGE = 250259,
+ SPELL_NOXIOUS_STENCH = 259572,
+ SPELL_LINGERING_NAUSEA = 250372
+};
+
+enum VolkaalEvents
+{
+ EVENT_TOXIC_LEAP = 1,
+ EVENT_NOXIOUS_STENCH,
+};
+
+enum VolkaalActions
+{
+ ACTION_TOTEMS_DIED = 1
+};
+
+enum VolkaalSummonGroups
+{
+ SUMMON_GROUP_VOLKAAL_TOTEMS = 0
+};
+
+enum VolkaalTexts
+{
+ SAY_AGGRO = 0,
+ SAY_DECAY = 1,
+ SAY_DIED = 2,
+ SAY_SLAY = 3,
+ SAY_WIPE = 4
+};
+
+// 122965 - Vol'kaal
+struct boss_volkaal : public BossAI
+{
+ boss_volkaal(Creature* creature) : BossAI(creature, DATA_VOLKAAL), _toxicLeapCount(1), _leaping(false) { }
+
+ void JustAppeared() override
+ {
+ me->SummonCreatureGroup(SUMMON_GROUP_VOLKAAL_TOTEMS);
+ }
+
+ void Reset() override
+ {
+ _Reset();
+
+ _toxicLeapCount = 1;
+ _leaping = false;
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ Talk(SAY_WIPE);
+
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ summons.DespawnAll();
+
+ _EnterEvadeMode();
+ _DespawnAtEvade();
+ }
+
+ void KilledUnit(Unit* victim) override
+ {
+ if (!victim->IsPlayer())
+ return;
+
+ Talk(SAY_SLAY);
+ }
+
+ void JustEngagedWith(Unit* who) override
+ {
+ BossAI::JustEngagedWith(who);
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
+
+ for (ObjectGuid summonGuid : summons)
+ {
+ if (Creature * totem = ObjectAccessor::GetCreature(*me, summonGuid))
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, totem, 2);
+ }
+
+ Talk(SAY_AGGRO);
+
+ events.ScheduleEvent(EVENT_TOXIC_LEAP, 2200ms);
+ events.ScheduleEvent(EVENT_NOXIOUS_STENCH, 5800ms);
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _JustDied();
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ summons.DespawnAll();
+
+ Talk(SAY_DIED);
+ }
+
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ if (spellInfo->Id == SPELL_RAPID_DECAY)
+ {
+ Talk(SAY_DECAY);
+ events.CancelEvent(EVENT_TOXIC_LEAP);
+ }
+ }
+
+ void MovementInform(uint32 /*type*/, uint32 id) override
+ {
+ if (id == EVENT_JUMP)
+ {
+ DoCastSelf(SPELL_TOXIC_LEAP_DAMAGE);
+ _leaping = false;
+ }
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_TOTEMS_DIED)
+ {
+ for (ObjectGuid summonGuid : summons)
+ {
+ if (Creature* totem = ObjectAccessor::GetCreature(*me, summonGuid))
+ {
+ totem->SetUnkillable(false);
+ totem->KillSelf();
+ }
+ }
+ }
+ }
+
+ 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_TOXIC_LEAP:
+ {
+ _leaping = true;
+ DoCast(SPELL_TOXIC_LEAP_SELECTOR);
+ _toxicLeapCount++;
+ if (_toxicLeapCount % 3 == 2)
+ events.Repeat(8500ms);
+ else
+ events.Repeat(6s);
+ break;
+ }
+ case EVENT_NOXIOUS_STENCH:
+ {
+ if (!_leaping)
+ {
+ DoCast(SPELL_NOXIOUS_STENCH);
+ events.Repeat(19400ms);
+ }
+ else
+ events.Repeat(100ms);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
+ }
+
+private:
+ uint32 _toxicLeapCount;
+ bool _leaping;
+};
+
+// 125977 - Reanimation Totem
+struct npc_volkaal_reanimation_totem : public ScriptedAI
+{
+ npc_volkaal_reanimation_totem(Creature* creature) : ScriptedAI(creature) { }
+
+ void JustAppeared() override
+ {
+ if (IsHeroicOrHigher())
+ me->SetUnkillable(true);
+ }
+
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ InstanceScript* instance = me->GetInstanceScript();
+ if (!instance)
+ return;
+
+ if (Creature* volkaal = instance->GetCreature(DATA_VOLKAAL))
+ volkaal->AI()->DoZoneInCombat();
+ }
+
+ void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
+ {
+ if (me->GetHealth() <= damage)
+ {
+ if (IsHeroicOrHigher())
+ {
+ DoCastSelf(SPELL_SOUL_ANCHOR);
+ DoCastSelf(SPELL_REANIMATE);
+ }
+ }
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ InstanceScript* instance = me->GetInstanceScript();
+ if (!instance)
+ return;
+
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ }
+};
+
+// 250372 - Lingering Nausea
+class spell_volkaal_lingering_nausea : public AuraScript
+{
+ void HandlePeriodic(AuraEffect const* aurEff) const
+ {
+ Unit* target = GetTarget();
+
+ if (Unit* caster = GetCaster())
+ caster->CastSpell(target, aurEff->GetAmount(), CastSpellExtraArgsInit{
+ .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR,
+ .TriggeringAura = aurEff
+ });
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_volkaal_lingering_nausea::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+};
+
+// 250368 - Noxious Stench
+class spell_volkaal_noxious_stench : public SpellScript
+{
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo ({ SPELL_LINGERING_NAUSEA });
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/) const
+ {
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_LINGERING_NAUSEA, CastSpellExtraArgsInit{
+ .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR,
+ .TriggeringSpell = GetSpell()
+ });
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_volkaal_noxious_stench::HandleDummy, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
+ }
+};
+
+// 250708 - Toxic Leap
+class spell_volkaal_toxic_leap_selector : public SpellScript
+{
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_TOXIC_LEAP });
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/) const
+ {
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_TOXIC_LEAP);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_volkaal_toxic_leap_selector::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+};
+
+// 250258 - Toxic Leap
+class spell_volkaal_toxic_leap : public SpellScript
+{
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_TOXIC_LEAP });
+ }
+
+ void HandleHit(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ float dist = GetCaster()->GetExactDist(GetHitDest());
+ float jumpGravity = 159500.0f / (dist * dist); // constant based on calculating avg of inverse from multiple leaps
+ GetCaster()->GetMotionMaster()->MoveJumpWithGravity(*GetHitDest(), 50, jumpGravity, EVENT_JUMP);
+ }
+
+ void Register() override
+ {
+ OnEffectHit += SpellEffectFn(spell_volkaal_toxic_leap::HandleHit, EFFECT_1, SPELL_EFFECT_JUMP_CHARGE);
+ }
+};
+
+// 250229 - Soul Anchor
+class spell_volkaal_soul_anchor : public SpellScript
+{
+ void SelectTotem(std::list<WorldObject*>& targets) const
+ {
+ if (targets.empty())
+ {
+ InstanceScript* instance = GetCaster()->GetInstanceScript();
+ if (!instance)
+ return;
+
+ if (Creature* volkaal = instance->GetCreature(DATA_VOLKAAL))
+ volkaal->AI()->DoAction(ACTION_TOTEMS_DIED);
+
+ Unit* caster = GetCaster();
+ caster->RemoveAurasDueToSpell(SPELL_BAD_VOODOO);
+ caster->CastSpell(caster, SPELL_RAPID_DECAY, CastSpellExtraArgsInit{
+ .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR,
+ .TriggeringSpell = GetSpell()
+ });
+ }
+ }
+
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_volkaal_soul_anchor::SelectTotem, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
+ }
+};
+
+// 259531 - Reanimate
+class spell_volkaal_reanimate : public SpellScript
+{
+ void HandleScript(SpellEffIndex /*effIndex*/) const
+ {
+ GetCaster()->RemoveAurasDueToSpell(SPELL_SOUL_ANCHOR);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_volkaal_reanimate::HandleScript, EFFECT_0, SPELL_EFFECT_HEAL_PCT);
+ }
+};
+
+// 250241 - Rapid Decay
+class spell_volkaal_rapid_decay : public AuraScript
+{
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo ({ SPELL_RAPID_DECAY_TARGET, SPELL_RAPID_DECAY_RANDOM });
+ }
+
+ void HandleToxicPool(AuraEffect const* aurEff) const
+ {
+ Unit* caster = GetCaster();
+ if (!caster)
+ return;
+
+ float range = 100.0f;
+ Player* player = nullptr;
+ Trinity::AnyPlayerInObjectRangeCheck check(caster, range);
+ Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(caster, player, check);
+ Cell::VisitWorldObjects(caster, searcher, range);
+
+ CastSpellExtraArgs args;
+ args.TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR;
+ args.TriggeringAura = aurEff;
+ caster->CastSpell(player->GetPosition(), SPELL_RAPID_DECAY_TARGET, args);
+ // not present in 11.0.7
+ //caster->CastSpell(caster, SPELL_RAPID_DECAY_RANDOM, args);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_volkaal_rapid_decay::HandleToxicPool, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+};
+
+// 250696 - Rapid Decay
+// ID - 11243
+struct at_volkaal_rapid_decay : AreaTriggerAI
+{
+ at_volkaal_rapid_decay(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { }
+
+ void OnUnitEnter(Unit* unit) override
+ {
+ if (!unit->IsPlayer())
+ return;
+
+ unit->CastSpell(unit, SPELL_TOXIC_POOL, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR);
+ }
+
+ void OnUnitExit(Unit* unit) override
+ {
+ unit->RemoveAurasDueToSpell(SPELL_TOXIC_POOL);
+ }
+};
+
+void AddSC_boss_volkaal()
+{
+ RegisterAtalDazarCreatureAI(boss_volkaal);
+ RegisterAtalDazarCreatureAI(npc_volkaal_reanimation_totem);
+
+ RegisterSpellScript(spell_volkaal_lingering_nausea);
+ RegisterSpellScript(spell_volkaal_noxious_stench);
+ RegisterSpellScript(spell_volkaal_toxic_leap_selector);
+ RegisterSpellScript(spell_volkaal_toxic_leap);
+ RegisterSpellScript(spell_volkaal_soul_anchor);
+ RegisterSpellScript(spell_volkaal_reanimate);
+ RegisterSpellScript(spell_volkaal_rapid_decay);
+
+ RegisterAreaTriggerAI(at_volkaal_rapid_decay);
+}
diff --git a/src/server/scripts/Zandalar/AtalDazar/instance_atal_dazar.cpp b/src/server/scripts/Zandalar/AtalDazar/instance_atal_dazar.cpp
index 6bf3af91f54..fd84c32d220 100644
--- a/src/server/scripts/Zandalar/AtalDazar/instance_atal_dazar.cpp
+++ b/src/server/scripts/Zandalar/AtalDazar/instance_atal_dazar.cpp
@@ -23,9 +23,10 @@
BossBoundaryData const boundaries =
{
{ DATA_REZAN, new ZRangeBoundary(639.0f, 650.0f) }, // 647.65f
+ { DATA_VOLKAAL, new ZRangeBoundary(709.0f, 730.0f) },
};
-ObjectData const creatureData[] =
+constexpr ObjectData creatureData[] =
{
{ BOSS_PRIESTESS_ALUNZA, DATA_PRIESTESS_ALUNZA },
{ BOSS_VOLKAAL, DATA_VOLKAAL },
@@ -34,7 +35,16 @@ ObjectData const creatureData[] =
{ 0, 0 } // END
};
-DungeonEncounterData const encounters[] =
+constexpr DoorData doorData[] =
+{
+ { GO_VOLKAAL_DOOR_1, DATA_VOLKAAL, EncounterDoorBehavior::OpenWhenNotInProgress },
+ { GO_VOLKAAL_DOOR_2, DATA_VOLKAAL, EncounterDoorBehavior::OpenWhenNotInProgress },
+ { GO_VOLKAAL_DOOR_3, DATA_VOLKAAL, EncounterDoorBehavior::OpenWhenNotInProgress },
+ { GO_VOLKAAL_DOOR_4, DATA_VOLKAAL, EncounterDoorBehavior::OpenWhenNotInProgress },
+ { 0, 0, EncounterDoorBehavior::OpenWhenNotInProgress } // END
+};
+
+constexpr DungeonEncounterData encounters[] =
{
{ DATA_PRIESTESS_ALUNZA, {{ 2084 }} },
{ DATA_VOLKAAL, {{ 2085 }} },
@@ -54,6 +64,7 @@ public:
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadObjectData(creatureData, nullptr);
+ LoadDoorData(doorData);
LoadBossBoundaries(boundaries);
LoadDungeonEncounterData(encounters);
}
diff --git a/src/server/scripts/Zandalar/zandalar_script_loader.cpp b/src/server/scripts/Zandalar/zandalar_script_loader.cpp
index d907c8036c3..44d821e6c3d 100644
--- a/src/server/scripts/Zandalar/zandalar_script_loader.cpp
+++ b/src/server/scripts/Zandalar/zandalar_script_loader.cpp
@@ -20,6 +20,7 @@
// Atal'Dazar
void AddSC_instance_atal_dazar();
void AddSC_boss_rezan();
+void AddSC_boss_volkaal();
// Underrot
void AddSC_instance_underrot();
@@ -38,6 +39,7 @@ void AddZandalarScripts()
// Atal'Dazar
AddSC_instance_atal_dazar();
AddSC_boss_rezan();
+ AddSC_boss_volkaal();
// Underrot
AddSC_instance_underrot();