aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOvahlord <dreadkiller@gmx.de>2023-11-07 14:51:53 +0100
committerOvahlord <dreadkiller@gmx.de>2023-11-07 16:17:55 +0100
commit30759beac75ef5bd6024e51213804a56673e6680 (patch)
treea38eaf1d8183b8a0d8e68f51a3ae8d5eccadcca8 /src
parentb606af8c4ac501bfe30ac3eb31557932e905c100 (diff)
Scripts/LCT: implement Lockmaw encounter
Diffstat (limited to 'src')
-rw-r--r--src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp565
-rw-r--r--src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp147
-rw-r--r--src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h22
-rw-r--r--src/server/scripts/Kalimdor/kalimdor_script_loader.cpp2
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp29
5 files changed, 756 insertions, 9 deletions
diff --git a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp
new file mode 100644
index 00000000000..a863bc0f5a2
--- /dev/null
+++ b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp
@@ -0,0 +1,565 @@
+/*
+* 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 "ScriptMgr.h"
+#include "Containers.h"
+#include "InstanceScript.h"
+#include "lost_city_of_the_tolvir.h"
+#include "MotionMaster.h"
+#include "ObjectAccessor.h"
+#include "ScriptedCreature.h"
+#include "SpellScript.h"
+
+enum LockmawSpells
+{
+ // Lockmaw
+ SPELL_VISCOUS_POISON = 81630,
+ SPELL_SCENT_OF_BLOOD = 81690,
+ SPELL_VENOMOUS_RAGE = 81706,
+ SPELL_DUST_FLAIL = 81652,
+ SPELL_DUST_FLAIL_CHANNELED = 81642,
+
+ // Add Stalker
+ SPELL_SUMMON_CROCOLISK = 84242,
+ SPELL_AUGH_1 = 84809, // Casts Paralytic Blow Dart
+ SPELL_AUGH_2 = 84808, // Casts Whirlwind
+
+ // Adds
+ SPELL_STEALTHED = 84244,
+
+ // Dust Flail
+ SPELL_DUST_FLAIL_PERIODIC = 81646,
+
+ // Augh (Add)
+ SPELL_PARALYTIC_BLOW_DART = 84799,
+ SPELL_SMOKE_BOMB = 84768,
+ SPELL_RANDOM_AGGRO_TAUNT = 50230,
+ SPELL_WHIRLWIND = 84784,
+
+ // Augh (Boss)
+ SPELL_FRENZY = 91415,
+ SPELL_DRAGONS_BREATH = 83776,
+ SPELL_WHIRLWIND_BOSS = 91408
+};
+
+enum LockmawEvents
+{
+ // Lockmaw
+ EVENT_VISCOUS_POISON = 1,
+ EVENT_SCENT_OF_BLOOD,
+ EVENT_VENOMOUS_RAGE,
+ EVENT_DUST_FLAIL,
+ EVENT_CHANNEL_DUST_FLAIL,
+ EVENT_TURN_AGGRESSIVE,
+ EVENT_SUMMON_AUGH_ADD,
+
+ // Augh (Add)
+ EVENT_MOVE_TOWARDS_BOSS,
+ EVENT_HARASS_PLAYERS,
+ EVENT_PARALYTIC_BLOW_DART,
+ EVENT_SCREAM,
+ EVENT_SMOKE_BOMB,
+ EVENT_WHIRLWIND,
+ EVENT_STEALTHED,
+ EVENT_LEAVE_ARENA,
+
+ // Augh (Boss)
+ EVENT_SAY_INTRO_2,
+ EVENT_SET_EMOTE_STATE,
+ EVENT_TURN_ATTACKABLE,
+ EVENT_SAY_INTRO_3,
+ EVENT_SAY_INTRO_4,
+ EVENT_SAY_INTRO_5,
+ EVENT_DRAGONS_BREATH
+};
+
+enum LockmawPhases
+{
+ // Augh (Boss)
+ PHASE_INTRO = 1,
+ PHASE_COMBAT = 2
+};
+
+enum LockmawTexts
+{
+ // Augh (Adds)
+ SAY_HARASS_PLAYER = 0,
+ SAY_SCREAM = 1,
+
+ // Augh (Boss)
+ SAY_ANNOUNCE_APPEARANCE = 0,
+ SAY_INTRO_1 = 1,
+ SAY_INTRO_2 = 2,
+ SAY_INTRO_3 = 3,
+ SAY_INTRO_4 = 4,
+ SAY_INTRO_5 = 5
+};
+
+enum class LockmawAughAddType : uint8
+{
+ ParalyticBlowDart = 0,
+ Whirlwind = 1
+};
+
+enum LockmawMovemPoints
+{
+ POINT_PARALYTIC_BLOW_DART = 0,
+ POINT_AUGH_HOME_POSITION = 0
+};
+
+// Instead of using a fixate or taunt aura, the Crocolisks use a flat threat amount instead
+static constexpr float SCENT_OF_BLOOD_THREAT_AMOUNT = 500000.f;
+static Position const AughRespawnPosition = { -11062.5f, -1662.39f, 0.7606202f, 0.8028514f };
+
+struct boss_lockmaw : public BossAI
+{
+ boss_lockmaw(Creature* creature) : BossAI(creature, BOSS_LOCKMAW), _enraged(false), _aughAddType(LockmawAughAddType::ParalyticBlowDart) { }
+
+ void JustEngagedWith(Unit* who) override
+ {
+ BossAI::JustEngagedWith(who);
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
+
+ events.ScheduleEvent(EVENT_VISCOUS_POISON, 10s);
+ events.ScheduleEvent(EVENT_SCENT_OF_BLOOD, 5s);
+ events.ScheduleEvent(EVENT_DUST_FLAIL, 18s);
+ events.ScheduleEvent(EVENT_SUMMON_AUGH_ADD, 8s);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ summons.DespawnAll();
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ _EnterEvadeMode();
+ _DespawnAtEvade();
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ BossAI::JustDied(killer);
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SCENT_OF_BLOOD);
+ instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_VISCOUS_POISON);
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
+ {
+ if (damage >= me->GetHealth())
+ return;
+
+ if (!_enraged && me->HealthBelowPctDamaged(30, damage))
+ {
+ events.ScheduleEvent(EVENT_VENOMOUS_RAGE, 1ms);
+ _enraged = true;
+ }
+ }
+
+ bool CanAIAttack(Unit const* victim) const override
+ {
+ // Patch 4.0.6 (2011-02-08): Lockmaw no longer tolerates fighting in his treasure room.
+ return victim && victim->IsOutdoors();
+ }
+
+ void JustSummoned(Creature* summon) override
+ {
+ summons.Summon(summon);
+
+ switch (summon->GetEntry())
+ {
+ case NPC_DUST_FLAIL_CASTER:
+ summon->CastSpell(nullptr, SPELL_DUST_FLAIL_PERIODIC);
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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_VISCOUS_POISON:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.f, true, 0.f))
+ DoCast(target, SPELL_VISCOUS_POISON);
+ else
+ DoCastVictim(SPELL_VISCOUS_POISON);
+
+ events.Repeat(30s);
+ break;
+ case EVENT_SCENT_OF_BLOOD:
+ // just a occasional cleanup of despawned summons to keep the container slim
+ summons.RemoveNotExisting();
+ DoCastAOE(SPELL_SCENT_OF_BLOOD);
+ events.Repeat(30s);
+ break;
+ case EVENT_VENOMOUS_RAGE:
+ DoCastSelf(SPELL_VENOMOUS_RAGE);
+ break;
+ case EVENT_DUST_FLAIL:
+ me->AttackStop();
+ me->SetReactState(REACT_PASSIVE);
+ DoCastSelf(SPELL_DUST_FLAIL);
+ events.ScheduleEvent(EVENT_CHANNEL_DUST_FLAIL, 1s);
+ events.Repeat(30s);
+ break;
+ case EVENT_CHANNEL_DUST_FLAIL:
+ if (Creature const* dustFlail = instance->GetCreature(DATA_DUST_FLAIL))
+ {
+ // because splines need one update tick to update the position, we have to set the orientation manually because we need it NOW.
+ me->SetOrientation(me->GetAbsoluteAngle(dustFlail));
+ me->SetFacingToObject(dustFlail);
+ DoCastSelf(SPELL_DUST_FLAIL_CHANNELED);
+ events.ScheduleEvent(EVENT_TURN_AGGRESSIVE, 7s);
+ }
+ else
+ me->SetReactState(REACT_AGGRESSIVE);
+ break;
+ case EVENT_TURN_AGGRESSIVE:
+ me->SetReactState(REACT_AGGRESSIVE);
+ break;
+ case EVENT_SUMMON_AUGH_ADD:
+ instance->SetData(DATA_SHUFFLE_ADD_STALKERS, 0);
+ if (Creature* addStalker = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ADD_STALKER_1)))
+ {
+ addStalker->CastSpell(nullptr, _aughAddType == LockmawAughAddType::ParalyticBlowDart ? SPELL_AUGH_1 : SPELL_AUGH_2);
+ events.Repeat(_aughAddType == LockmawAughAddType::ParalyticBlowDart ? 20s : 40s);
+ _aughAddType = _aughAddType == LockmawAughAddType::ParalyticBlowDart ? LockmawAughAddType::Whirlwind : LockmawAughAddType::ParalyticBlowDart;
+ }
+ break;
+ default:
+ break;
+ }
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
+
+ DoMeleeAttackIfReady();
+ }
+private:
+ bool _enraged;
+ LockmawAughAddType _aughAddType;
+};
+
+struct npc_lockmaw_frenzied_crocolisk : public ScriptedAI
+{
+ npc_lockmaw_frenzied_crocolisk(Creature* creature) : ScriptedAI(creature) { }
+
+ void JustAppeared() override
+ {
+ DoZoneInCombat();
+ DoCastSelf(SPELL_STEALTHED);
+ me->SetCorpseDelay(5); // Frenzied Crocolisks despawn 5 seconds after death
+
+ if (Unit* victim = SelectTarget(SelectTargetMethod::Random, 0, 500.f, true, true, SPELL_SCENT_OF_BLOOD))
+ AddThreat(victim, SCENT_OF_BLOOD_THREAT_AMOUNT);
+ }
+};
+
+static constexpr float AUGH_DISTANCE_TO_BOSS = 20.f;
+
+struct npc_lockmaw_augh_add : public ScriptedAI
+{
+ npc_lockmaw_augh_add(Creature* creature) : ScriptedAI(creature), _instance(nullptr) { }
+
+ void InitializeAI() override
+ {
+ _instance = me->GetInstanceScript();
+
+ if (me->GetEntry() == NPC_AUGH_ADD_1)
+ me->SetReactState(REACT_PASSIVE);
+ }
+
+ void JustAppeared() override
+ {
+ if (me->GetEntry() == NPC_AUGH_ADD_1)
+ {
+ DoCastSelf(SPELL_STEALTHED);
+ _events.ScheduleEvent(EVENT_STEALTHED, 1ms);
+ _events.ScheduleEvent(EVENT_MOVE_TOWARDS_BOSS, 2s);
+ }
+ else
+ {
+ DoZoneInCombat();
+ _events.ScheduleEvent(EVENT_STEALTHED, 2s);
+ _events.ScheduleEvent(EVENT_WHIRLWIND, 3s);
+ }
+ }
+
+ void MovementInform(uint32 motionType, uint32 pointId) override
+ {
+ if (motionType != POINT_MOTION_TYPE && pointId != POINT_PARALYTIC_BLOW_DART)
+ return;
+
+ _events.ScheduleEvent(EVENT_HARASS_PLAYERS, 1ms);
+ _events.ScheduleEvent(EVENT_PARALYTIC_BLOW_DART, 1s);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ // Augh does perform events even if he is not engaged, so we do not return here
+ UpdateVictim();
+
+ _events.Update(diff);
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_MOVE_TOWARDS_BOSS:
+ {
+ DoZoneInCombat();
+ Creature* lockmaw = _instance->GetCreature(BOSS_LOCKMAW);
+ if (!lockmaw)
+ {
+ // This should never happen at this stage, but just to be sure....
+ me->DespawnOrUnsummon();
+ return;
+ }
+ me->GetMotionMaster()->MovePoint(POINT_PARALYTIC_BLOW_DART, lockmaw->GetPosition(), true, std::nullopt, std::nullopt, MovementWalkRunSpeedSelectionMode::ForceWalk, AUGH_DISTANCE_TO_BOSS);
+ break;
+ }
+ case EVENT_HARASS_PLAYERS:
+ Talk(SAY_HARASS_PLAYER);
+ break;
+ case EVENT_PARALYTIC_BLOW_DART:
+ DoCastAOE(SPELL_PARALYTIC_BLOW_DART, CastSpellExtraArgs().AddSpellMod(SPELLVALUE_MAX_TARGETS, 1));
+ _events.ScheduleEvent(EVENT_SCREAM, 3s);
+ break;
+ case EVENT_SCREAM:
+ Talk(SAY_SCREAM);
+ _events.ScheduleEvent(EVENT_SMOKE_BOMB, 1s);
+ break;
+ case EVENT_SMOKE_BOMB:
+ DoCastAOE(SPELL_SMOKE_BOMB);
+ me->DespawnOrUnsummon(3s + 500ms);
+ break;
+ case EVENT_WHIRLWIND:
+ me->SetReactState(REACT_AGGRESSIVE);
+ DoCastAOE(SPELL_RANDOM_AGGRO_TAUNT);
+ DoCastSelf(SPELL_WHIRLWIND);
+ _events.ScheduleEvent(EVENT_LEAVE_ARENA, 20s);
+ break;
+ case EVENT_STEALTHED:
+ DoCastSelf(SPELL_STEALTHED);
+ break;
+ case EVENT_LEAVE_ARENA:
+ me->AttackStop();
+ me->SetReactState(REACT_PASSIVE);
+ if (Creature* addStalker = me->FindNearestCreature(NPC_ADD_STALKER, 200.f))
+ me->GetMotionMaster()->MovePoint(0, addStalker->GetPosition());
+ me->DespawnOrUnsummon(4s);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+private:
+ EventMap _events;
+ InstanceScript* _instance;
+};
+
+struct npc_lockmaw_augh_boss : public ScriptedAI
+{
+ npc_lockmaw_augh_boss(Creature* creature) : ScriptedAI(creature), _instance(nullptr) { }
+
+ void InitializeAI() override
+ {
+ _instance = me->GetInstanceScript();
+ if (_instance->GetData(DATA_HEROIC_AUGH_DESPAWNED))
+ me->Relocate(AughRespawnPosition);
+ else
+ me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
+
+ me->SetReactState(REACT_PASSIVE);
+ }
+
+ void JustAppeared() override
+ {
+ if (!_instance->GetData(DATA_HEROIC_AUGH_DESPAWNED))
+ {
+ Talk(SAY_ANNOUNCE_APPEARANCE);
+ me->GetMotionMaster()->MovePoint(POINT_AUGH_HOME_POSITION, AughRespawnPosition, true, std::nullopt, std::nullopt, MovementWalkRunSpeedSelectionMode::ForceWalk);
+ }
+ }
+
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->SetEmoteState(EMOTE_ONESHOT_NONE);
+ DoCastSelf(SPELL_FRENZY);
+ _events.SetPhase(PHASE_COMBAT);
+ _events.ScheduleEvent(EVENT_PARALYTIC_BLOW_DART, 8s);
+ _events.ScheduleEvent(EVENT_WHIRLWIND, 9s);
+ _events.ScheduleEvent(EVENT_DRAGONS_BREATH, 6s);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ _instance->SetData(DATA_HEROIC_AUGH_DESPAWNED, 1);
+ me->DespawnOrUnsummon(0s, 30s);
+ }
+
+ void MovementInform(uint32 motionType, uint32 pointId) override
+ {
+ if (motionType != POINT_MOTION_TYPE && pointId != POINT_AUGH_HOME_POSITION)
+ return;
+
+ me->SetFacingTo(AughRespawnPosition.GetOrientation());
+ Talk(SAY_INTRO_1);
+
+ _events.SetPhase(PHASE_INTRO);
+ _events.ScheduleEvent(EVENT_SAY_INTRO_2, 3s, 0, PHASE_INTRO);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO))
+ return;
+
+ _events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SAY_INTRO_2:
+ Talk(SAY_INTRO_2);
+ _events.ScheduleEvent(EVENT_SET_EMOTE_STATE, 3s + 500ms, 0, PHASE_INTRO);
+ break;
+ case EVENT_SET_EMOTE_STATE:
+ me->SetEmoteState(EMOTE_STATE_SPELL_CHANNEL_OMNI);
+ _events.ScheduleEvent(EVENT_TURN_ATTACKABLE, 3s + 500ms, 0, PHASE_INTRO);
+ break;
+ case EVENT_TURN_ATTACKABLE:
+ me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
+ _events.ScheduleEvent(EVENT_SAY_INTRO_3, 11s, 0, PHASE_INTRO);
+ break;
+ case EVENT_SAY_INTRO_3:
+ Talk(SAY_INTRO_3);
+ _events.ScheduleEvent(EVENT_SAY_INTRO_4, 8s + 500ms, 0, PHASE_INTRO);
+ break;
+ case EVENT_SAY_INTRO_4:
+ Talk(SAY_INTRO_4);
+ _events.ScheduleEvent(EVENT_SAY_INTRO_5, 8s + 500ms, 0, PHASE_INTRO);
+ break;
+ case EVENT_SAY_INTRO_5:
+ Talk(SAY_INTRO_5);
+ _events.ScheduleEvent(EVENT_SAY_INTRO_3, 21s, 0, PHASE_INTRO);
+ break;
+ case EVENT_PARALYTIC_BLOW_DART:
+ DoCastAOE(SPELL_PARALYTIC_BLOW_DART, CastSpellExtraArgs().AddSpellMod(SPELLVALUE_MAX_TARGETS, 1));
+ _events.Repeat(10s, 15s);
+ break;
+ case EVENT_WHIRLWIND:
+ DoCastAOE(SPELL_RANDOM_AGGRO_TAUNT);
+ DoCastSelf(SPELL_WHIRLWIND_BOSS);
+ _events.Repeat(29s);
+ break;
+ case EVENT_DRAGONS_BREATH:
+ DoCastAOE(SPELL_DRAGONS_BREATH);
+ _events.Repeat(28s);
+ break;
+ default:
+ break;
+ }
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+private:
+ EventMap _events;
+ InstanceScript* _instance;
+};
+
+class spell_lockmaw_scent_of_blood : public SpellScript
+{
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_SUMMON_CROCOLISK });
+ }
+
+ void SelectTarget(std::list<WorldObject*>& targets)
+ {
+ if (targets.size() <= 1)
+ return;
+
+ std::list<WorldObject*> targetsCopy = targets;
+ targetsCopy.remove_if([caster = GetCaster()](WorldObject const* target)
+ {
+ return caster->GetVictim() != target;
+ });
+
+ if (!targetsCopy.empty())
+ {
+ Trinity::Containers::RandomResize(targetsCopy, 1);
+ targets = targetsCopy;
+ }
+ else
+ Trinity::Containers::RandomResize(targets, 1);
+ }
+
+ void HandleCrocoliskSummoning()
+ {
+ Unit* caster = GetCaster();
+ if (!caster)
+ return;
+
+ InstanceScript* instance = caster->GetInstanceScript();
+ if (!instance)
+ return;
+
+ // Shuffle the stored add stalkers so that we will get four random add stalkers in the next step below
+ instance->SetData(DATA_SHUFFLE_ADD_STALKERS, 0);
+
+ for (uint32 i = DATA_ADD_STALKER_1; i <= DATA_ADD_STALKER_4; ++i)
+ if (Creature* addStalker = ObjectAccessor::GetCreature(*caster, instance->GetGuidData(i)))
+ addStalker->CastSpell(nullptr, SPELL_SUMMON_CROCOLISK);
+ }
+
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_lockmaw_scent_of_blood::SelectTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ AfterCast += SpellCastFn(spell_lockmaw_scent_of_blood::HandleCrocoliskSummoning);
+ }
+};
+
+void AddSC_boss_lockmaw()
+{
+ RegisterLostCityOfTheTolvirAI(boss_lockmaw);
+ RegisterLostCityOfTheTolvirAI(npc_lockmaw_frenzied_crocolisk);
+ RegisterLostCityOfTheTolvirAI(npc_lockmaw_augh_add);
+ RegisterLostCityOfTheTolvirAI(npc_lockmaw_augh_boss);
+ RegisterSpellScript(spell_lockmaw_scent_of_blood);
+}
diff --git a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp
index 7a01516ad20..88aba2b8666 100644
--- a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp
+++ b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp
@@ -16,27 +16,43 @@
*/
#include "ScriptMgr.h"
+#include "Containers.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "EventMap.h"
#include "InstanceScript.h"
#include "lost_city_of_the_tolvir.h"
+#include "Map.h"
static constexpr ObjectData const creatureData[] =
{
{ NPC_GENERAL_HUSAM, BOSS_GENERAL_HUSAM },
- { NPC_LOCKMAW, DATA_LOCKMAW },
- { NPC_AUGH, DATA_AUGH },
+ { NPC_LOCKMAW, BOSS_LOCKMAW },
{ NPC_HIGH_PROPHET_BARIM, BOSS_HIGH_PROPHET_BARIM },
{ NPC_SIAMAT, BOSS_SIAMAT },
+ { NPC_DUST_FLAIL, DATA_DUST_FLAIL },
{ 0, 0 } // End
};
static constexpr DungeonEncounterData const encounters[] =
{
{ BOSS_GENERAL_HUSAM, {{ 1052 }} },
- { BOSS_LOCKMAW_AND_AUGH, {{ 1054 }} },
+ { BOSS_LOCKMAW, {{ 1054 }} },
{ BOSS_HIGH_PROPHET_BARIM, {{ 1053 }} },
{ BOSS_SIAMAT, {{ 1055 }} }
};
+enum LCTEvents
+{
+ EVENT_SPAWN_HEROIC_AUGH = 1
+};
+
+enum LCTSpawnGroups
+{
+ SPAWN_GROUP_ID_SIAMAT_WIND_TUNNEL = 1036,
+ SPAWN_GROUP_ID_AUGH_HEROIC = 1037
+};
+
class instance_lost_city_of_the_tolvir : public InstanceMapScript
{
public:
@@ -44,13 +60,136 @@ public:
struct instance_lost_city_of_the_tolvir_InstanceMapScript : public InstanceScript
{
- instance_lost_city_of_the_tolvir_InstanceMapScript(InstanceMap* map) : InstanceScript(map)
+ instance_lost_city_of_the_tolvir_InstanceMapScript(InstanceMap* map) : InstanceScript(map), _heroicAughDespawned(false)
{
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadDungeonEncounterData(encounters);
LoadObjectData(creatureData, nullptr);
}
+
+ void OnCreatureCreate(Creature* creature) override
+ {
+ InstanceScript::OnCreatureCreate(creature);
+
+ switch (creature->GetEntry())
+ {
+ case NPC_ADD_STALKER:
+ _lockmawAddStalkerGUIDs.push_back(creature->GetGUID());
+ break;
+ case NPC_FRENZIED_CROCOLISK:
+ case NPC_AUGH_ADD_1:
+ case NPC_AUGH_ADD_2:
+ // Some adds are spawned by Add Stalkers so we have to register them via instance script for Lockmaw to clean up
+ if (Creature* lockmaw = GetCreature(BOSS_LOCKMAW))
+ if (CreatureAI* ai = lockmaw->AI())
+ ai->JustSummoned(creature);
+ break;
+ default:
+ break;
+ }
+ }
+
+ bool SetBossState(uint32 id, EncounterState state) override
+ {
+ if (!InstanceScript::SetBossState(id, state))
+ return false;
+
+ switch (id)
+ {
+ case BOSS_LOCKMAW:
+ if (state == DONE && (instance->IsHeroic() || instance->IsTimewalking()))
+ _events.ScheduleEvent(EVENT_SPAWN_HEROIC_AUGH, 1ms);
+ break;
+ default:
+ break;
+ }
+
+ if (GetBossState(BOSS_GENERAL_HUSAM) == DONE && GetBossState(BOSS_LOCKMAW) == DONE && GetBossState(BOSS_HIGH_PROPHET_BARIM) == DONE)
+ EnableSiamat();
+
+ return true;
+ }
+
+ void SetData(uint32 type, uint32 value) override
+ {
+ switch (type)
+ {
+ case DATA_SHUFFLE_ADD_STALKERS:
+ Trinity::Containers::RandomShuffle(_lockmawAddStalkerGUIDs);
+ break;
+ case DATA_HEROIC_AUGH_DESPAWNED:
+ _heroicAughDespawned = value != 0 ? true : false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ uint32 GetData(uint32 type) const override
+ {
+ switch (type)
+ {
+ case DATA_HEROIC_AUGH_DESPAWNED:
+ return static_cast<uint8>(_heroicAughDespawned);
+ default:
+ return 0;
+ }
+
+ return 0;
+ }
+
+ void Update(uint32 diff) override
+ {
+ InstanceScript::Update(diff);
+
+ _events.Update(diff);
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SPAWN_HEROIC_AUGH:
+ instance->SpawnGroupSpawn(SPAWN_GROUP_ID_AUGH_HEROIC);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void AfterDataLoad() override
+ {
+ if (GetBossState(BOSS_GENERAL_HUSAM) == DONE && GetBossState(BOSS_LOCKMAW) == DONE && GetBossState(BOSS_HIGH_PROPHET_BARIM) == DONE)
+ EnableSiamat();
+ }
+
+ ObjectGuid GetGuidData(uint32 type) const override
+ {
+ switch (type)
+ {
+ case DATA_ADD_STALKER_1:
+ case DATA_ADD_STALKER_2:
+ case DATA_ADD_STALKER_3:
+ case DATA_ADD_STALKER_4:
+ if (_lockmawAddStalkerGUIDs.size() >= (type - DATA_ADD_STALKER_1 + 1))
+ return _lockmawAddStalkerGUIDs[(type - DATA_ADD_STALKER_1)];
+ else
+ return ObjectGuid::Empty;
+ default:
+ return InstanceScript::GetGuidData(type);
+ }
+
+ return InstanceScript::GetGuidData(type);
+ }
+ private:
+ EventMap _events;
+ std::vector<ObjectGuid> _lockmawAddStalkerGUIDs;
+ bool _heroicAughDespawned;
+
+ void EnableSiamat()
+ {
+ instance->SpawnGroupSpawn(SPAWN_GROUP_ID_SIAMAT_WIND_TUNNEL);
+ }
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
diff --git a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h
index d397617ac29..b700defb391 100644
--- a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h
+++ b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h
@@ -29,12 +29,17 @@ enum LCTData
{
// Encounters
BOSS_GENERAL_HUSAM = 0,
- BOSS_LOCKMAW_AND_AUGH = 1,
+ BOSS_LOCKMAW = 1,
BOSS_HIGH_PROPHET_BARIM = 2,
BOSS_SIAMAT = 3,
- DATA_LOCKMAW,
- DATA_AUGH
+ DATA_SHUFFLE_ADD_STALKERS,
+ DATA_ADD_STALKER_1,
+ DATA_ADD_STALKER_2,
+ DATA_ADD_STALKER_3,
+ DATA_ADD_STALKER_4,
+ DATA_DUST_FLAIL,
+ DATA_HEROIC_AUGH_DESPAWNED
};
enum LCTCreatureIds
@@ -42,7 +47,6 @@ enum LCTCreatureIds
// Bosses
NPC_GENERAL_HUSAM = 44577,
NPC_LOCKMAW = 43614,
- NPC_AUGH = 49045,
NPC_HIGH_PROPHET_BARIM = 43612,
NPC_SIAMAT = 44819,
@@ -52,7 +56,15 @@ enum LCTCreatureIds
NPC_SHOCKWAVE_STALKER = 44711,
NPC_TOLVIR_LAND_MINE_TARGET = 44840,
NPC_TOLVIR_LAND_MINE_VEHICLE = 44798,
- NPC_TOLVIR_LAND_MINE_CASTER = 44796
+ NPC_TOLVIR_LAND_MINE_CASTER = 44796,
+
+ /*Lockmaw*/
+ NPC_ADD_STALKER = 45124,
+ NPC_FRENZIED_CROCOLISK = 43658,
+ NPC_DUST_FLAIL = 43655,
+ NPC_DUST_FLAIL_CASTER = 43650,
+ NPC_AUGH_ADD_1 = 45379,
+ NPC_AUGH_ADD_2 = 45378,
};
template <class AI, class T>
diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
index eb5b043da2f..277348eefc8 100644
--- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
+++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
@@ -97,6 +97,7 @@ void AddSC_npc_anubisath_sentinel();
void AddSC_instance_temple_of_ahnqiraj();
// The Lost City of the Tol'vir
void AddSC_boss_general_husam();
+void AddSC_boss_lockmaw();
void AddSC_instance_lost_city_of_the_tolvir();
// Wailing caverns
void AddSC_wailing_caverns();
@@ -221,6 +222,7 @@ void AddKalimdorScripts()
AddSC_instance_temple_of_ahnqiraj();
// The Lost City of the Tol'vir
AddSC_boss_general_husam();
+ AddSC_boss_lockmaw();
AddSC_instance_lost_city_of_the_tolvir();
// Wailing caverns
AddSC_wailing_caverns();
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index fb16c4a9581..da6a028a78f 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -5330,6 +5330,34 @@ class spell_gen_major_healing_cooldown_modifier_aura : public AuraScript
}
};
+// 50230 - Random Aggro (Taunt)
+class spell_gen_random_aggro_taunt : public SpellScript
+{
+ bool Validate(SpellInfo const* spellInfo) override
+ {
+ return ValidateSpellEffect({ { spellInfo->Id, EFFECT_0 } }) && ValidateSpellInfo({ static_cast<uint32>(spellInfo->GetEffect(EFFECT_0).BasePoints) });
+ }
+
+ void SelectRandomTarget(std::list<WorldObject*>& targets)
+ {
+ if (targets.empty())
+ return;
+
+ Trinity::Containers::RandomResize(targets, 1);
+ }
+
+ void HandleTauntEffect(SpellEffIndex effIndex)
+ {
+ GetHitUnit()->CastSpell(GetCaster(), static_cast<uint32>(GetSpellInfo()->GetEffect(effIndex).BasePoints), CastSpellExtraArgs(TRIGGERED_FULL_MASK));
+ }
+
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gen_random_aggro_taunt::SelectRandomTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnEffectHitTarget += SpellEffectFn(spell_gen_random_aggro_taunt::HandleTauntEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
+
void AddSC_generic_spell_scripts()
{
RegisterSpellScript(spell_gen_absorb0_hitlimit1);
@@ -5505,4 +5533,5 @@ void AddSC_generic_spell_scripts()
RegisterSpellScript(spell_gen_waiting_to_resurrect);
RegisterSpellScript(spell_gen_major_healing_cooldown_modifier);
RegisterSpellScript(spell_gen_major_healing_cooldown_modifier_aura);
+ RegisterSpellScript(spell_gen_random_aggro_taunt);
}