diff options
author | Ovahlord <dreadkiller@gmx.de> | 2023-11-07 14:51:53 +0100 |
---|---|---|
committer | Ovahlord <dreadkiller@gmx.de> | 2023-11-07 16:17:55 +0100 |
commit | 30759beac75ef5bd6024e51213804a56673e6680 (patch) | |
tree | a38eaf1d8183b8a0d8e68f51a3ae8d5eccadcca8 /src | |
parent | b606af8c4ac501bfe30ac3eb31557932e905c100 (diff) |
Scripts/LCT: implement Lockmaw encounter
Diffstat (limited to 'src')
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); } |