fix(Scripts/BlackwingLair): Razorgore improvements (#10971)

- Rewrite reset events
- Use proper healing spell on phase transition
- Now uses abilities during phase 1
- Phase transition scripted - mobs now run away
This commit is contained in:
Skjalf
2022-03-23 15:42:34 -03:00
committed by GitHub
parent 6a6d0e5907
commit 7377c96cc8
12 changed files with 212 additions and 32 deletions

View File

@@ -39,13 +39,18 @@ enum BWLEncounter
// Additional Data
DATA_LORD_VICTOR_NEFARIUS = 8,
DATA_GRETHOK = 9,
DATA_NEFARIAN_TROOPS = 10,
// Doors
DATA_GO_CHROMAGGUS_DOOR = 9
DATA_GO_CHROMAGGUS_DOOR = 11
};
enum BWLCreatureIds
{
NPC_GRETHOK = 12557,
NPC_BLACKWING_GUARDSMAN = 14456,
NPC_NEFARIAN_TROOPS = 14459,
NPC_RAZORGORE = 12435,
NPC_BLACKWING_DRAGON = 12422,
NPC_BLACKWING_TASKMASTER = 12458,

View File

@@ -27,6 +27,8 @@ enum Say
SAY_EGGS_BROKEN2 = 1,
SAY_EGGS_BROKEN3 = 2,
SAY_DEATH = 3,
EMOTE_TROOPS_RETREAT = 0
};
enum Spells
@@ -38,7 +40,12 @@ enum Spells
SPELL_CLEAVE = 19632,
SPELL_WARSTOMP = 24375,
SPELL_FIREBALLVOLLEY = 22425,
SPELL_CONFLAGRATION = 23023
SPELL_CONFLAGRATION = 23023,
SPELL_EXPLODE_ORB = 20037,
SPELL_EXPLOSION = 20038, // Instakill everything.
SPELL_WARMING_FLAMES = 23040,
};
enum Summons
@@ -72,16 +79,19 @@ public:
void Reset() override
{
_Reset();
_died = false;
_charmerGUID.Clear();
secondPhase = false;
summons.DespawnAll();
instance->SetData(DATA_EGG_EVENT, NOT_STARTED);
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
if (secondPhase)
{
_JustDied();
}
}
bool CanAIAttack(Unit const* target) const override
@@ -93,6 +103,11 @@ public:
{
_EnterCombat();
events.ScheduleEvent(EVENT_CLEAVE, 15000);
events.ScheduleEvent(EVENT_STOMP, 35000);
events.ScheduleEvent(EVENT_FIREBALL, 7000);
events.ScheduleEvent(EVENT_CONFLAGRATION, 12000);
instance->SetData(DATA_EGG_EVENT, IN_PROGRESS);
}
@@ -101,12 +116,26 @@ public:
secondPhase = true;
_charmerGUID.Clear();
me->RemoveAllAuras();
me->SetHealth(me->GetMaxHealth());
events.ScheduleEvent(EVENT_CLEAVE, 15000);
events.ScheduleEvent(EVENT_STOMP, 35000);
events.ScheduleEvent(EVENT_FIREBALL, 7000);
events.ScheduleEvent(EVENT_CONFLAGRATION, 12000);
DoCastSelf(SPELL_WARMING_FLAMES, true);
if (Creature* troops = instance->GetCreature(DATA_NEFARIAN_TROOPS))
{
troops->AI()->Talk(EMOTE_TROOPS_RETREAT);
}
for (ObjectGuid const& guid : _summonGUIDS)
{
if (Creature* creature = ObjectAccessor::GetCreature(*me, guid))
{
if (creature->IsAlive())
{
creature->CombatStop(true);
creature->SetReactState(REACT_PASSIVE);
creature->GetMotionMaster()->MovePoint(0, Position(-7560.568848f, -1028.553345f, 408.491211f, 0.523858f));
}
}
}
}
void SetGUID(ObjectGuid const guid, int32 /*id*/) override
@@ -123,6 +152,13 @@ public:
charmer->CastSpell(charmer, SPELL_MIND_EXHAUSTION, true);
}
}
else
{
if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmerGUID))
{
me->TauntApply(charmer);
}
}
}
void DoAction(int32 action) override
@@ -138,12 +174,41 @@ public:
}
}
void JustSummoned(Creature* summon) override
{
_summonGUIDS.push_back(summon->GetGUID());
summon->SetOwnerGUID(me->GetGUID());
summons.Summon(summon);
}
void SummonMovementInform(Creature* summon, uint32 movementType, uint32 /*pathId*/) override
{
if (movementType == POINT_MOTION_TYPE)
{
summon->DespawnOrUnsummon();
}
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!secondPhase && damage >= me->GetHealth())
if (!secondPhase && damage >= me->GetHealth() && !_died)
{
damage = me->GetHealth() - 1;
EnterEvadeMode();
// This is required because he kills himself with the explosion spell, causing a loop.
_died = true;
Talk(SAY_DEATH);
DoCastAOE(SPELL_EXPLODE_ORB);
DoCastAOE(SPELL_EXPLOSION);
// Respawn shorty in case of failure during phase 1.
me->SetCorpseRemoveTime(25);
me->SetRespawnTime(30);
me->SaveRespawnTime();
// Might not be required, safe measure.
me->SetLootRecipient(nullptr);
instance->SetData(DATA_EGG_EVENT, FAIL);
}
}
@@ -152,7 +217,10 @@ public:
if (!UpdateVictim())
return;
events.Update(diff);
if (!me->IsCharmed())
{
events.Update(diff);
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
@@ -176,18 +244,21 @@ public:
case EVENT_CONFLAGRATION:
DoCastVictim(SPELL_CONFLAGRATION);
if (me->GetVictim() && me->GetVictim()->HasAura(SPELL_CONFLAGRATION))
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true))
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
me->TauntApply(target);
events.ScheduleEvent(EVENT_CONFLAGRATION, 30000);
break;
}
}
DoMeleeAttackIfReady();
}
private:
bool secondPhase;
bool _died;
ObjectGuid _charmerGUID;
GuidVector _summonGUIDS;
};
CreatureAI* GetAI(Creature* creature) const override

View File

@@ -41,6 +41,12 @@ DoorData const doorData[] =
{ 0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE} // END
};
ObjectData const creatureData[] =
{
{ NPC_GRETHOK, DATA_GRETHOK },
{ NPC_NEFARIAN_TROOPS, DATA_NEFARIAN_TROOPS }
};
Position const SummonPosition[8] =
{
{-7661.207520f, -1043.268188f, 407.199554f, 6.280452f},
@@ -67,7 +73,7 @@ public:
//SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
//LoadObjectData(creatureData, gameObjectData);
LoadObjectData(creatureData, nullptr);
}
void Initialize() override
@@ -98,6 +104,9 @@ public:
if (CreatureAI* razorAI = razor->AI())
razorAI->JustSummoned(creature);
break;
case NPC_BLACKWING_GUARDSMAN:
guardList.push_back(creature->GetGUID());
break;
case NPC_NEFARIAN:
nefarianGUID = creature->GetGUID();
break;
@@ -135,11 +144,14 @@ public:
{
case GO_BLACK_DRAGON_EGG:
if (GetBossState(DATA_FIREMAW) == DONE)
{
go->SetPhaseMask(2, true);
}
else
{
EggList.push_back(go->GetGUID());
}
break;
case GO_PORTCULLIS_RAZORGORE:
case GO_PORTCULLIS_VAELASTRASZ:
case GO_PORTCULLIS_BROODLORD:
@@ -233,10 +245,15 @@ public:
if (state == DONE)
{
for (ObjectGuid const& guid : EggList)
{
// Eggs should be destroyed instead
// @todo: after dynamic spawns
if (GameObject* egg = instance->GetGameObject(guid))
{
egg->SetPhaseMask(2, true);
}
}
}
SetData(DATA_EGG_EVENT, NOT_STARTED);
break;
case DATA_CHROMAGGUS:
if (state == DONE)
@@ -270,7 +287,7 @@ public:
switch (data)
{
case IN_PROGRESS:
_events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45000);
_events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45 * IN_MILLISECONDS);
EggEvent = data;
EggCount = 0;
break;
@@ -278,13 +295,19 @@ public:
_events.CancelEvent(EVENT_RAZOR_SPAWN);
EggEvent = data;
EggCount = 0;
for (ObjectGuid const& guid : EggList)
{
if (GameObject* egg = instance->GetGameObject(guid))
{
egg->Respawn();
}
DoRespawnGameObject(guid, 0);
}
DoRespawnCreature(DATA_GRETHOK);
for (ObjectGuid const& guid : guardList)
{
DoRespawnCreature(guid);
}
break;
case SPECIAL:
if (++EggCount >= EggList.size())
@@ -336,12 +359,22 @@ public:
void OnUnitDeath(Unit* unit) override
{
//! HACK, needed because of buggy CreatureAI after charm
if (unit->GetEntry() == NPC_RAZORGORE && GetBossState(DATA_RAZORGORE_THE_UNTAMED) != DONE)
SetBossState(DATA_RAZORGORE_THE_UNTAMED, DONE);
switch (unit->GetEntry())
{
case NPC_RAZORGORE:
//! HACK, needed because of buggy CreatureAI after charm
if (EggEvent == DONE)
{
if (unit->GetEntry() == NPC_RAZORGORE && GetBossState(DATA_RAZORGORE_THE_UNTAMED) != DONE)
{
SetBossState(DATA_RAZORGORE_THE_UNTAMED, DONE);
}
}
else
{
_events.CancelEvent(EVENT_RAZOR_SPAWN);
}
break;
case NPC_BLACK_DRAKONID:
case NPC_BLUE_DRAKONID:
case NPC_BRONZE_DRAKONID:
@@ -379,10 +412,18 @@ public:
switch (eventId)
{
case EVENT_RAZOR_SPAWN:
for (uint8 i = urand(2, 5); i > 0; --i)
if (Creature* summon = instance->SummonCreature(Entry[urand(0, 2)], SummonPosition[urand(0, 7)]))
summon->AI()->DoZoneInCombat();
_events.ScheduleEvent(EVENT_RAZOR_SPAWN, 12000, 17000);
if (EggEvent == IN_PROGRESS)
{
for (uint8 i = urand(2, 5); i > 0; --i)
{
if (Creature* summon = instance->SummonCreature(Entry[urand(0, 2)], SummonPosition[urand(0, 7)]))
{
summon->AI()->DoZoneInCombat();
}
}
_events.ScheduleEvent(EVENT_RAZOR_SPAWN, 12000, 17000);
}
break;
case EVENT_RAZOR_PHASE_TWO:
_events.CancelEvent(EVENT_RAZOR_SPAWN);
@@ -462,6 +503,7 @@ public:
uint8 EggCount;
uint32 EggEvent;
GuidList EggList;
GuidList guardList;
// Nefarian
uint32 NefarianLeftTunnel;