Scripts/BWD: more work on Nefarian's End encounter

* Nefarian will now enter phase three when the first Chromatic Prototype has died
* Onyxia can no longer die before Nefarian has landed in phase one
* inital work on the Shadowblaze Flame spreading
* implement achievement 'Keeping it in the Family
This commit is contained in:
Ovahlord committed 2019-11-12 01:58:12 +01:00
1 parent eff2527604
commit acd4639709
4 files changed
+253 -20

No files matched your search

+8 -1
View File
@@ -9694,7 +9694,14 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
if (instance && mapid == 657)
instance->FillInitialWorldStates(data);
else
data << uint32(5649) << uint32 (0);
data << uint32(5649) << uint32(0);
break;
// Blackwing Descent
if (instance && mapid == 669)
instance->FillInitialWorldStates(data);
else
data << uint32(5652) << uint32(1);
case 5094:
break;
// Twin Peaks
case 5031:
@@ -41,6 +41,7 @@ enum BWDDataTypes
DATA_COLUMN_OF_LIGHT,
DATA_ATRAMEDES_INTRO,
DATA_ENTRANCE_INTRO,
DATA_NEFARIAN_ACHIEVEMENT_STATE,
// Encounter Data
/*Magmaw*/
@@ -144,6 +145,7 @@ enum BWDCreatureIds
NPC_ONYXIA = 41270,
NPC_CHROMATIC_PROTOTYPE = 41948,
NPC_CONTROLLER_STALKER = 42856,
NPC_SHADOWBLAZE_FLASHPOINT = 42595,
/*Events*/
NPC_SPIRIT_OF_MOLTENFIST = 43125,
@@ -186,6 +188,11 @@ enum BWDSpells
SPELL_COLUMN_OF_LIGHT = 95660
};
enum BWDWorldStates
{
WS_KEEPING_IT_IN_THE_FAMILY = 0
};
template <class AI, class T>
inline AI* GetBlackwingDescentAI(T* obj)
{
@@ -103,6 +103,7 @@ enum Spells
SPELL_BRUSHFIRE_GROWTH = 79393,
SPELL_BRUSHFIRE_BURN_AURA = 79396,
SPELL_BRUSHFIRE_SUMMON = 79405,
SPELL_BRUSHFIRE_CHECK_VALID_LOCATION = 79401
};
enum Texts
@@ -191,9 +192,14 @@ enum Actions
// Onyxia
ACTION_REANIMATED = 1,
ACTION_UPDATE_ELECTRICAL_CHARGE = 2,
ACTION_NEFARIAN_LANDED = 3,
// Chromatic Prototype
ACTION_DISENGAGE_PLAYERS = 1
ACTION_DISENGAGE_PLAYERS = 1,
// Shadowblaze Flashpoint / Shadowblaze
ACTION_DESPAWN_FLAMES = 1,
ACTION_SPREAD_FLAMES = 2
};
enum TransportStopFrames
@@ -244,7 +250,8 @@ std::vector<OnyxiaChainData> OnyxiaChainInfo =
enum Misc
{
SOUND_ID_ROAR = 7274
SOUND_ID_ROAR = 7274,
WS_ACHIEVEMENT_CRITERIA = 5652
};
Position const NefarianSummonPosition = { -166.655f, -224.602f, 40.48163f, 0.0f };
@@ -303,7 +310,7 @@ Position const ChromaticPrototypeJumpPositions[MaxChromaticPrototypes]
struct boss_nefarians_end : public BossAI
{
boss_nefarians_end(Creature* creature) : BossAI(creature, DATA_NEFARIANS_END),
_landed(false), _elevatorLowered(false), _killedChromaticPrototypes(0), _nextElectrocuteHealthPercentage(90)
_elevatorLowered(false), _nextElectrocuteHealthPercentage(90)
{
me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); // Remove this little workarround when mmaps for transports have arrived.
me->SetReactState(REACT_PASSIVE);
@@ -353,6 +360,7 @@ struct boss_nefarians_end : public BossAI
}
summons.DespawnAll();
instance->SetData(DATA_NEFARIAN_ACHIEVEMENT_STATE, 1);
instance->SetBossState(DATA_NEFARIANS_END, FAIL);
me->DespawnOrUnsummon();
}
@@ -370,6 +378,13 @@ struct boss_nefarians_end : public BossAI
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
// keeping the summon list clean since we have to deal with many tempoary summons
summons.RemoveNotExisting();
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE)
@@ -429,7 +444,7 @@ struct boss_nefarians_end : public BossAI
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
// Do not allow Nefarian to die before he raised the platform again
if (damage >= me->GetHealth() && events.IsInPhase(PHASE_TWO))
if (damage >= me->GetHealth() && !events.IsInPhase(PHASE_THREE))
damage = 0;
if (damage >= me->GetHealth())
@@ -466,9 +481,8 @@ struct boss_nefarians_end : public BossAI
switch (summon->GetEntry())
{
case NPC_CHROMATIC_PROTOTYPE:
_killedChromaticPrototypes++;
if (_killedChromaticPrototypes == MaxChromaticPrototypes)
events.ScheduleEvent(EVENT_ENTER_PHASE_THREE, 1ms);
// Nefarian enters phase three when the first Chromatic Prototype has died
events.ScheduleEvent(EVENT_ENTER_PHASE_THREE, 1ms, 0, PHASE_TWO);
break;
default:
break;
@@ -541,7 +555,9 @@ struct boss_nefarians_end : public BossAI
DoZoneInCombat();
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, FRAME_INDEX_NEFARIAN);
events.ScheduleEvent(EVENT_ENGAGE_PLAYERS, 2s, 0, PHASE_ONE);
_landed = true;
if (Creature* onyxia = instance->GetCreature(DATA_ONYXIA))
if (onyxia->IsAIEnabled)
onyxia->AI()->DoAction(ACTION_NEFARIAN_LANDED);
}
else if (events.IsInPhase(PHASE_THREE))
events.ScheduleEvent(EVENT_ENGAGE_PLAYERS, 3s, 0, PHASE_THREE);
@@ -582,11 +598,6 @@ struct boss_nefarians_end : public BossAI
events.Repeat(17s, 22s);
break;
case EVENT_SAY_ONYXIA_DEAD:
if (!_landed) // Do not transition to phase two if Nefarian did not land first
{
events.Repeat(1s);
break;
}
me->AttackStop();
me->SetReactState(REACT_PASSIVE);
me->RemoveAurasDueToSpell(SPELL_CHILDREN_OF_DEATHWING_NEFARIAN);
@@ -652,6 +663,9 @@ struct boss_nefarians_end : public BossAI
Talk(SAY_LAND_PHASE_THREE);
break;
case EVENT_LAND_PHASE_THREE:
if (me->GetHealthPct() > 50.f)
instance->SetData(DATA_NEFARIAN_ACHIEVEMENT_STATE, 0);
me->RemoveAurasDueToSpell(SPELL_NEFARIAN_PHASE_2_HEALTH_AURA);
me->SendSetPlayHoverAnim(true);
me->GetMotionMaster()->MoveLand(POINT_LAND, NefarianElevatorLandPhaseThreePosition);
@@ -752,15 +766,13 @@ private:
return elevator->ToTransport();
}
bool _landed;
bool _elevatorLowered;
uint8 _killedChromaticPrototypes;
uint8 _nextElectrocuteHealthPercentage;
};
struct npc_nefarians_end_onyxia : public ScriptedAI
{
npc_nefarians_end_onyxia(Creature* creature) : ScriptedAI(creature), _instance(me->GetInstanceScript())
npc_nefarians_end_onyxia(Creature* creature) : ScriptedAI(creature), _instance(me->GetInstanceScript()), _allowDeath(false)
{
me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); // Remove this little workarround when mmaps for transports have arrived.
}
@@ -831,11 +843,21 @@ struct npc_nefarians_end_onyxia : public ScriptedAI
me->SetPower(POWER_ALTERNATE_POWER, stacks - 1);
}
break;
case ACTION_NEFARIAN_LANDED:
_allowDeath = true;
break;
default:
break;
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
// Onyxia may not die before Nefarian has landed
if (damage >= me->GetHealth() && !_allowDeath)
damage = me->GetHealth() - 1;
}
void OnSuccessfulSpellCast(SpellInfo const* spell) override
{
switch (spell->Id)
@@ -885,6 +907,7 @@ struct npc_nefarians_end_onyxia : public ScriptedAI
private:
InstanceScript* _instance;
EventMap _events;
bool _allowDeath;
};
struct npc_nefarians_end_lord_victor_nefarius : public PassiveAI
@@ -951,16 +974,23 @@ private:
struct npc_nefarians_end_animated_bone_warrior : public ScriptedAI
{
npc_nefarians_end_animated_bone_warrior(Creature* creature) : ScriptedAI(creature)
npc_nefarians_end_animated_bone_warrior(Creature* creature) : ScriptedAI(creature), _instance(me->GetInstanceScript())
{
me->SetReactState(REACT_PASSIVE);
me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); // tempfix until mmaps for transports have arrived
}
void IsSummonedBy(Unit* /*summoner*/) override
void JustAppeared() override
{
DoCastSelf(SPELL_FULL_POWER_NO_REGEN);
DoCastSelf(SPELL_ANIMATE_BONES);
if (GameObject* elevator = _instance->GetGameObject(DATA_BLACKWING_ELEVATOR_ONYXIA))
if (Transport* transport = elevator->ToTransport())
transport->AddPassenger(me);
me->UpdatePositionData();
me->m_Events.AddEventAtOffset([this]()
{
me->SetReactState(REACT_AGGRESSIVE);
@@ -985,6 +1015,8 @@ struct npc_nefarians_end_animated_bone_warrior : public ScriptedAI
DoMeleeAttackIfReady();
}
private:
InstanceScript* _instance;
};
struct npc_nefarians_end_chromatic_prototype : public PassiveAI
@@ -1083,6 +1115,104 @@ private:
EventMap _events;
};
struct npc_nefarians_end_shadowblaze : public NullCreatureAI
{
npc_nefarians_end_shadowblaze(Creature* creature) : NullCreatureAI(creature), _summonedByController(false), _instance(me->GetInstanceScript()) { }
void JustAppeared() override
{
if (me->GetEntry() == NPC_SHADOWBLAZE_FLASHPOINT)
DoCastSelf(SPELL_BRUSHFIRE_FLASHPOINT_CONTROL);
else
DoCastSelf(SPELL_BRUSHFIRE_CHECK_VALID_LOCATION);
DoCastSelf(SPELL_BRUSHFIRE_BURN_AURA);
DoCastSelf(SPELL_BRUSHFIRE_GROWTH);
}
void IsSummonedBy(Unit* summoner) override
{
if (summoner->GetEntry() == NPC_SHADOWBLAZE_FLASHPOINT)
_summonedByController = true;
if (Creature* nefarian = _instance->GetCreature(DATA_NEFARIANS_END))
if (nefarian->IsAIEnabled)
nefarian->AI()->JustSummoned(me);
}
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
{
switch (spell->Id)
{
case SPELL_BRUSHFIRE_CHECK_VALID_LOCATION:
_controllerStalkerPosition = target->GetPosition();
break;
default:
break;
}
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_SPREAD_FLAMES:
{
if (!_summonedByController && me->GetEntry() != NPC_SHADOWBLAZE_FLASHPOINT)
{
// Select a destination that will serve as our offset selection helper
float angle = me->GetAngle(_controllerStalkerPosition) - float(M_PI / 2.5f);
float compareX = me->GetPositionX() + std::cos(angle) * 5.f;
float compareY = me->GetPositionY() + std::sin(angle) * 5.f;
Position comparePos = Position(compareX, compareY);
Position summonPos = me->GetPosition();
// Select the closest nearby location for summoning
float offsets[3] = { -5.f, 0.f, 5.f };
for (uint8 i = 0; i < 3; i++)
{
float x = me->GetPositionX() + offsets[i];
for (uint8 u = 0; u < 3; u++)
{
float y = me->GetPositionY() + offsets[u];
if (Position(x, y).GetExactDist2d(comparePos) < summonPos.GetExactDist2d(comparePos))
summonPos = Position(x, y);
}
}
me->CastSpell(summonPos.GetPositionX(), summonPos.GetPositionY(), me->GetPositionZ(), 0.f, SPELL_BRUSHFIRE_SUMMON, true);
}
else
{
if (me->GetEntry() == NPC_SHADOWBLAZE_FLASHPOINT)
{
me->CastSpell(me->GetPositionX() - 5.f, me->GetPositionY(), me->GetPositionZ(), 0.f, SPELL_BRUSHFIRE_SUMMON, true);
me->CastSpell(me->GetPositionX(), me->GetPositionY() + 5.f, me->GetPositionZ(), 0.f, SPELL_BRUSHFIRE_SUMMON, true);
}
else if (_summonedByController)
{
for (uint8 i = 0; i < 2; i++)
{
float x = me->GetPositionX() + 5.f;
float y = me->GetPositionY() + 5.f;
me->CastSpell(x, y, me->GetPositionZ(), 0.f, SPELL_BRUSHFIRE_SUMMON, true);
}
}
}
break;
}
default:
break;
}
}
private:
Position _controllerStalkerPosition;
InstanceScript* _instance;
bool _summonedByController;
};
class spell_nefarians_end_electrical_charge : public AuraScript
{
PrepareAuraScript(spell_nefarians_end_electrical_charge);
@@ -1507,6 +1637,23 @@ class spell_nefarians_end_brushfire_start : public SpellScript
}
};
class spell_nefarians_end_brushfire_growth : public AuraScript
{
PrepareAuraScript(spell_nefarians_end_brushfire_growth);
void HandlePeriodicTick(AuraEffect const* /*aurEff*/)
{
if (Creature* creature = GetTarget()->ToCreature())
if (creature->IsAIEnabled)
creature->AI()->DoAction(ACTION_SPREAD_FLAMES);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_nefarians_end_brushfire_growth::HandlePeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
}
};
struct go_nefarians_end_orb_of_culmination : public GameObjectAI
{
go_nefarians_end_orb_of_culmination(GameObject* go) : GameObjectAI(go), _instance(me->GetInstanceScript()) { }
@@ -1530,6 +1677,62 @@ private:
InstanceScript* _instance;
};
class spell_nefarians_end_shadowblaze : public SpellScript
{
PrepareSpellScript(spell_nefarians_end_shadowblaze);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo(
{
SPELL_PERMANENT_FEIGN_DEATH_2,
SPELL_FULL_POWER_NO_REGEN,
SPELL_CLEAR_ALL_DEBUFFS
});
}
void HandleHit(SpellEffIndex effIndex)
{
Creature* target = GetHitCreature();
if (!target)
return;
if (target->HasAura(SPELL_PERMANENT_FEIGN_DEATH_2))
{
target->SetReactState(REACT_AGGRESSIVE);
if (target->IsAIEnabled)
target->AI()->DoZoneInCombat();
target->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH_2);
target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
target->CastSpell(target, SPELL_CLEAR_ALL_DEBUFFS, true);
target->CastSpell(target, SPELL_FULL_POWER_NO_REGEN);
target->CastSpell(target, GetSpellInfo()->Effects[effIndex].BasePoints);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_nefarians_end_shadowblaze::HandleHit, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class achievement_keeping_it_in_the_family : public AchievementCriteriaScript
{
public:
achievement_keeping_it_in_the_family() : AchievementCriteriaScript("achievement_keeping_it_in_the_family") { }
bool OnCheck(Player* /*source*/, Unit* target) override
{
InstanceScript* instance = target->GetInstanceScript();
if (!instance)
return false;
return instance->GetData(DATA_NEFARIAN_ACHIEVEMENT_STATE);
}
};
void AddSC_boss_nefarians_end()
{
RegisterBlackwingDescentCreatureAI(boss_nefarians_end);
@@ -1537,6 +1740,7 @@ void AddSC_boss_nefarians_end()
RegisterBlackwingDescentCreatureAI(npc_nefarians_end_lord_victor_nefarius);
RegisterBlackwingDescentCreatureAI(npc_nefarians_end_animated_bone_warrior);
RegisterBlackwingDescentCreatureAI(npc_nefarians_end_chromatic_prototype);
RegisterBlackwingDescentCreatureAI(npc_nefarians_end_shadowblaze);
RegisterAuraScript(spell_nefarians_end_electrical_charge);
RegisterAuraScript(spell_nefarians_end_lightning_discharge_triggered_periodic_aura);
RegisterSpellScript(spell_nefarians_end_lightning_discharge_cone);
@@ -1551,6 +1755,8 @@ void AddSC_boss_nefarians_end()
RegisterAuraScript(spell_nefarians_end_empowering_strikes);
RegisterAuraScript(spell_nefarians_end_brushfire_pre_start_periodic);
RegisterSpellScript(spell_nefarians_end_brushfire_start);
RegisterAuraScript(spell_nefarians_end_brushfire_growth);
RegisterSpellScript(spell_nefarians_end_shadowblaze);
RegisterGameObjectAI(go_nefarians_end_orb_of_culmination);
new achievement_keeping_it_in_the_family();
}
@@ -114,6 +114,7 @@ class instance_blackwing_descent : public InstanceMapScript
_deadDwarfSpirits = 0;
_atramedesIntroState = NOT_STARTED;
_entranceSequenceDone = false;
_nefarianAchievementEligible = true;
}
void Create() override
@@ -322,17 +323,28 @@ class instance_blackwing_descent : public InstanceMapScript
}
}
break;
case DATA_NEFARIAN_ACHIEVEMENT_STATE:
_nefarianAchievementEligible = uint8(data);
DoUpdateWorldState(WS_KEEPING_IT_IN_THE_FAMILY, uint8(_nefarianAchievementEligible));
break;
default:
break;
}
}
void FillInitialWorldStates(WorldPacket& data) override
{
data << uint32(WS_KEEPING_IT_IN_THE_FAMILY) << uint32(_nefarianAchievementEligible);
}
uint32 GetData(uint32 type) const override
{
switch (type)
{
case DATA_ATRAMEDES_INTRO:
return _atramedesIntroState;
case DATA_NEFARIAN_ACHIEVEMENT_STATE:
return uint8(_nefarianAchievementEligible);
}
return 0;
}
@@ -454,6 +466,7 @@ class instance_blackwing_descent : public InstanceMapScript
uint8 _deadDwarfSpirits;
uint8 _atramedesIntroState;
bool _entranceSequenceDone;
bool _nefarianAchievementEligible;
bool IsNefarianAvailable() const
{