mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Scripts/LCT: implement Lockmaw encounter
This commit is contained in:
38
sql/updates/world/master/2023_11_07_00_world.sql
Normal file
38
sql/updates/world/master/2023_11_07_00_world.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- Correct spawn group boss state values for LCT spawn groups
|
||||
UPDATE `instance_spawn_groups` SET `bossStates`= 0x17 WHERE `instanceMapId`= 755;
|
||||
|
||||
UPDATE `creature_template` SET `mechanic_immune_mask`= 667893759, `ScriptName`= 'boss_lockmaw' WHERE `entry`= 43614;
|
||||
UPDATE `creature_template` SET `flags_extra`= 0x80 WHERE `entry` IN (45124, 43655, 43650);
|
||||
UPDATE `creature_template` SET `flags_extra`= 0x100, `ScriptName`= 'npc_lockmaw_frenzied_crocolisk' WHERE `entry`= 43658;
|
||||
UPDATE `creature_template` SET `mechanic_immune_mask`= 667893759, `speed_walk`= 2.4, `speed_run`= 0.85714, `ScriptName`= 'npc_lockmaw_augh_add' WHERE `entry` IN (45379, 45378);
|
||||
UPDATE `creature_template` SET `mechanic_immune_mask`= 667893759, `ScriptName`= 'npc_lockmaw_augh_boss' WHERE `entry`= 49045;
|
||||
|
||||
DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_lockmaw_scent_of_blood', 'spell_gen_random_aggro_taunt', 'spell_husam_hurl_vehicle');
|
||||
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
|
||||
(81690, 'spell_lockmaw_scent_of_blood'),
|
||||
(81690, 'spell_gen_random_aggro_taunt');
|
||||
|
||||
DELETE FROM `creature_text` WHERE `CreatureID` IN (45379, 49045);
|
||||
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
|
||||
(45379, 0, 0, 'Bwagauugh!!! Augh feed you to da crocs!!!', 12, 0, 100, 53, 0, 0, 45544, 0, 'Augh'),
|
||||
(45379, 1, 0, 'Gwaaaaaaaaaaaahhh!!!', 12, 0, 100, 0, 0, 0, 45530, 0, 'Augh'),
|
||||
(49045, 0, 0, 'Augh appears from the distance!', 41, 0, 100, 0, 0, 0, 50638, 0, 'Augh'),
|
||||
(49045, 1, 0, 'GAAAH! How you kill croc?!', 12, 0, 100, 53, 0, 0, 49169, 0, 'Augh'),
|
||||
(49045, 2, 0, 'Augh smart! Augh already steal treasure while you no looking! ', 12, 0, 100, 1, 0, 0, 49170, 0, 'Augh'),
|
||||
(49045, 3, 0, 'Augh da boss! Oh yeah!', 12, 0, 100, 0, 0, 0, 49171, 0, 'Augh'),
|
||||
(49045, 4, 0, 'Augh steal your treasure. Uhn uhn uhnnn!', 12, 0, 100, 0, 0, 0, 49173, 0, 'Augh'),
|
||||
(49045, 5, 0, 'Who bad?! Augh bad!! Ugn!', 12, 0, 100, 0, 0, 0, 49172, 0, 'Augh');
|
||||
|
||||
SET @CGUID := 339734;
|
||||
DELETE FROM `creature` WHERE `guid`= @CGUID;
|
||||
DELETE FROM `creature_addon` WHERE `guid`= @CGUID;
|
||||
INSERT INTO `creature` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnDifficulties`, `PhaseId`, `PhaseGroup`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `unit_flags2`, `unit_flags3`, `VerifiedBuild`) VALUES
|
||||
(@CGUID, 49045, 755, 5396, 5631, '1,2', 169, 0, 0, 1, -11058.9111328125, -1625.342041015625, -0.13049933314323425, 4.78220224380493164, 7200, 0, 0, 702775, 0, 0, NULL, NULL, NULL, NULL, 26654); -- Augh (Area: Oasis of the Fallen Prophet - Difficulty: 2) CreateObject2
|
||||
|
||||
DELETE FROM `spawn_group_template` WHERE `groupId`= 1037;
|
||||
INSERT INTO `spawn_group_template` (`groupId`, `groupName`, `groupFlags`) VALUES
|
||||
(1037, 'The Lost City of the Tol''vir - Augh (Heroic)', 4);
|
||||
|
||||
DELETE FROM `spawn_group` WHERE `groupId`= 1037;
|
||||
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`) VALUES
|
||||
(1037, 0, @CGUID);
|
||||
565
src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp
Normal file
565
src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user