Scripts/LCT: implement Lockmaw encounter

This commit is contained in:
Ovahlord
2023-11-07 14:51:53 +01:00
parent b606af8c4a
commit 30759beac7
6 changed files with 794 additions and 9 deletions

View 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);

View 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);
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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();

View File

@@ -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);
}