Core/Scripts Corrections on Halion's Encounter (#16725)

Core/Scripts: Updates to Halion

* Fixed Combustion and Consumption.
* Fixed Living Embers and Blazing Auras.
* Timer changes.
This commit is contained in:
Gustavo
2016-07-05 05:54:31 -03:00
committed by Aokromes
parent b67e304c88
commit 58aaccb8af
5 changed files with 364 additions and 265 deletions

View File

@@ -137,7 +137,8 @@ enum Events
EVENT_CHECK_CORPOREALITY = 13,
EVENT_SHADOW_PULSARS_SHOOT = 14,
EVENT_TRIGGER_BERSERK = 15,
EVENT_TWILIGHT_MENDING = 16
EVENT_TWILIGHT_MENDING = 16,
EVENT_ACTIVATE_EMBERS = 17
};
enum Actions
@@ -150,7 +151,9 @@ enum Actions
ACTION_MONITOR_CORPOREALITY = 3,
// Orb Carrier
ACTION_SHOOT = 4
ACTION_WARNING_SHOOT = 4,
ACTION_SHOOT = 5,
ACTION_ACTIVATE_EMBERS = 6
};
enum Phases
@@ -168,8 +171,7 @@ enum Misc
DATA_MATERIAL_DAMAGE_TAKEN = 2,
DATA_STACKS_DISPELLED = 3,
DATA_FIGHT_PHASE = 4,
DATA_EVADE_METHOD = 5,
DATA_SPAWNED_FLAMES = 6,
DATA_SPAWNED_FLAMES = 5,
};
enum OrbCarrierSeats
@@ -189,6 +191,7 @@ enum CorporealityEvent
};
Position const HalionSpawnPos = {3156.67f, 533.8108f, 72.98822f, 3.159046f};
Position const HalionRespawnPos = {3156.625f, 533.2674f, 72.97205f, 0.0f};
uint8 const MAX_CORPOREALITY_STATE = 11;
@@ -212,132 +215,39 @@ CorporealityEntry const _corporealityReference[MAX_CORPOREALITY_STATE] = {
{74831, 74836}
};
struct generic_halionAI : public BossAI
{
generic_halionAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), _canEvade(false) { }
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
me->AddAura(SPELL_TWILIGHT_PRECISION, me);
_canEvade = false;
events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000));
events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
events.ScheduleEvent(EVENT_BREATH, urand(10000, 15000));
}
void Reset() override
{
_canEvade = false;
_Reset();
}
void JustReachedHome() override
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustReachedHome();
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_CLEAVE:
DoCastVictim(SPELL_CLEAVE);
events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000));
break;
case EVENT_TAIL_LASH:
DoCastAOE(SPELL_TAIL_LASH);
events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
break;
case EVENT_BREATH:
DoCast(me, me->GetEntry() == NPC_HALION ? SPELL_FLAME_BREATH : SPELL_DARK_BREATH);
events.ScheduleEvent(EVENT_BREATH, urand(10000, 12000));
break;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING))
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
ExecuteEvent(eventId);
DoMeleeAttackIfReady();
}
void SetData(uint32 index, uint32 dataValue) override
{
switch (index)
{
case DATA_EVADE_METHOD:
_canEvade = (dataValue == 1);
break;
default:
break;
}
}
void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_TWILIGHT_MENDING)
Talk(SAY_REGENERATE);
}
protected:
bool _canEvade;
};
class boss_halion : public CreatureScript
{
public:
boss_halion() : CreatureScript("boss_halion") { }
struct boss_halionAI : public generic_halionAI
struct boss_halionAI : public BossAI
{
boss_halionAI(Creature* creature) : generic_halionAI(creature, DATA_HALION)
{
me->SetHomePosition(HalionSpawnPos);
}
void Reset() override
{
generic_halionAI::Reset();
me->SetReactState(REACT_DEFENSIVE);
me->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
boss_halionAI(Creature* creature) : BossAI(creature, DATA_HALION) { }
void EnterEvadeMode(EvadeReason why) override
{
if (why == EVADE_REASON_BOUNDARY)
if (why == EVADE_REASON_BOUNDARY || events.IsInPhase(PHASE_ONE))
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
controller->AI()->EnterEvadeMode();
// Phase 1: We always can evade. Phase 2 & 3: We can evade if and only if the controller tells us to.
if (events.IsInPhase(PHASE_ONE) || _canEvade)
generic_halionAI::EnterEvadeMode(why);
controller->AI()->EnterEvadeMode(why);
}
void EnterCombat(Unit* who) override
void EnterCombat(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
events.Reset();
events.SetPhase(PHASE_ONE);
generic_halionAI::EnterCombat(who);
_EnterCombat();
me->AddAura(SPELL_TWILIGHT_PRECISION, me);
events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, Seconds(5));
events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(5), Seconds(15)));
events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(6), Seconds(10)));
events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(7), Seconds(12)));
events.ScheduleEvent(EVENT_FIERY_COMBUSTION, randtime(Seconds(15), Seconds(18)));
events.ScheduleEvent(EVENT_METEOR_STRIKE, Seconds(18));
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
instance->SetBossState(DATA_HALION, IN_PROGRESS);
events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, 5000);
events.ScheduleEvent(EVENT_METEOR_STRIKE, urand(20000, 25000));
events.ScheduleEvent(EVENT_FIERY_COMBUSTION, urand(15000, 18000));
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_ONE);
@@ -388,58 +298,76 @@ class boss_halion : public CreatureScript
}
}
void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_TWILIGHT_MENDING)
Talk(SAY_REGENERATE);
}
void UpdateAI(uint32 diff) override
{
if (events.IsInPhase(PHASE_TWO))
return;
generic_halionAI::UpdateAI(diff);
}
if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING))
return;
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
case EVENT_ACTIVATE_FIREWALL:
// Flame ring is activated 5 seconds after starting encounter, DOOR_TYPE_ROOM is only instant.
for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i)
if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(i)))
instance->HandleGameObject(instance->GetGuidData(DATA_FLAME_RING), false, flameRing);
break;
case EVENT_METEOR_STRIKE:
switch (eventId)
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM))
case EVENT_CLEAVE:
DoCastVictim(SPELL_CLEAVE);
events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(8), Seconds(10)));
break;
case EVENT_TAIL_LASH:
DoCastAOE(SPELL_TAIL_LASH);
events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(11), Seconds(16)));
break;
case EVENT_BREATH:
DoCast(me, SPELL_FLAME_BREATH);
events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(16), Seconds(25)));
break;
case EVENT_ACTIVATE_FIREWALL:
// Flame ring is activated 5 seconds after starting encounter, DOOR_TYPE_ROOM is only instant.
for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i)
if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(i)))
instance->HandleGameObject(instance->GetGuidData(DATA_FLAME_RING), false, flameRing);
break;
case EVENT_METEOR_STRIKE:
{
_meteorStrikePos = target->GetPosition();
me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, NULL, NULL, me->GetGUID());
Talk(SAY_METEOR_STRIKE);
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM))
{
_meteorStrikePos = target->GetPosition();
me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, nullptr, nullptr, me->GetGUID());
Talk(SAY_METEOR_STRIKE);
}
events.ScheduleEvent(EVENT_METEOR_STRIKE, Seconds(38));
break;
}
events.ScheduleEvent(EVENT_METEOR_STRIKE, 40000);
break;
case EVENT_FIERY_COMBUSTION:
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM))
me->CastSpell(target, SPELL_FIERY_COMBUSTION, TRIGGERED_IGNORE_SET_FACING);
events.ScheduleEvent(EVENT_FIERY_COMBUSTION, Seconds(25));
break;
}
default:
break;
}
case EVENT_FIERY_COMBUSTION:
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM))
DoCast(target, SPELL_FIERY_COMBUSTION);
events.ScheduleEvent(EVENT_FIERY_COMBUSTION, 25000);
break;
}
default:
generic_halionAI::ExecuteEvent(eventId);
break;
}
DoMeleeAttackIfReady();
}
void SetData(uint32 index, uint32 value) override
{
switch (index)
{
case DATA_FIGHT_PHASE:
events.SetPhase(value);
break;
default:
generic_halionAI::SetData(index, value);
}
if (index != DATA_FIGHT_PHASE)
return;
events.SetPhase(value);
}
private:
@@ -459,9 +387,9 @@ class boss_twilight_halion : public CreatureScript
public:
boss_twilight_halion() : CreatureScript("boss_twilight_halion") { }
struct boss_twilight_halionAI : public generic_halionAI
struct boss_twilight_halionAI : public BossAI
{
boss_twilight_halionAI(Creature* creature) : generic_halionAI(creature, DATA_TWILIGHT_HALION)
boss_twilight_halionAI(Creature* creature) : BossAI(creature, DATA_TWILIGHT_HALION)
{
Creature* halion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION));
if (!halion)
@@ -470,26 +398,29 @@ class boss_twilight_halion : public CreatureScript
// Using AddAura because no spell cast packet in sniffs.
halion->AddAura(SPELL_COPY_DAMAGE, me); // We use explicit targeting here to avoid conditions + SPELL_ATTR6_CANT_TARGET_SELF.
me->AddAura(SPELL_COPY_DAMAGE, halion);
me->AddAura(SPELL_DUSK_SHROUD, me);
DoCast(me, SPELL_DUSK_SHROUD, true);
me->SetHealth(halion->GetHealth());
me->SetPhaseMask(0x20, true);
me->SetReactState(REACT_AGGRESSIVE);
me->SetReactState(REACT_DEFENSIVE);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
events.ScheduleEvent(EVENT_TAIL_LASH, Seconds(12));
events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, Seconds(15));
}
void EnterCombat(Unit* who) override
void EnterCombat(Unit* /*who*/) override
{
events.Reset();
events.SetPhase(PHASE_TWO);
generic_halionAI::EnterCombat(who);
events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000);
_EnterCombat();
me->AddAura(SPELL_TWILIGHT_PRECISION, me);
events.ScheduleEvent(EVENT_CLEAVE, Seconds(3));
events.ScheduleEvent(EVENT_BREATH, Seconds(12));
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 2);
}
// Never evade
void Reset() override { }
void EnterEvadeMode(EvadeReason /*why*/) override { }
void KilledUnit(Unit* victim) override
@@ -522,6 +453,10 @@ class boss_twilight_halion : public CreatureScript
void DamageTaken(Unit* attacker, uint32& damage) override
{
//Needed because we already have UNIT_FLAG_IN_COMBAT, otherwise EnterCombat won't ever be called
if (!events.IsInPhase(PHASE_TWO) && !events.IsInPhase(PHASE_THREE))
EnterCombat(attacker);
if (me->HealthBelowPctDamaged(50, damage) && events.IsInPhase(PHASE_TWO))
{
events.SetPhase(PHASE_THREE);
@@ -542,7 +477,7 @@ class boss_twilight_halion : public CreatureScript
}
}
void SpellHit(Unit* who, SpellInfo const* spell) override
void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
{
switch (spell->Id)
{
@@ -550,25 +485,51 @@ class boss_twilight_halion : public CreatureScript
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
controller->AI()->DoAction(ACTION_MONITOR_CORPOREALITY);
break;
case SPELL_TWILIGHT_MENDING:
Talk(SAY_REGENERATE);
break;
default:
generic_halionAI::SpellHit(who, spell);
break;
}
}
void ExecuteEvent(uint32 eventId) override
void UpdateAI(uint32 diff) override
{
switch (eventId)
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (!UpdateVictim())
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
case EVENT_SOUL_CONSUMPTION:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM))
DoCast(target, SPELL_SOUL_CONSUMPTION);
events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000);
break;
default:
generic_halionAI::ExecuteEvent(eventId);
break;
switch (eventId)
{
case EVENT_CLEAVE:
DoCastVictim(SPELL_CLEAVE);
events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(7), Seconds(10)));
break;
case EVENT_TAIL_LASH:
DoCastAOE(SPELL_TAIL_LASH);
events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(12), Seconds(16)));
break;
case EVENT_BREATH:
DoCast(me, SPELL_DARK_BREATH);
events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(10), Seconds(14)));
break;
case EVENT_SOUL_CONSUMPTION:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM))
me->CastSpell(target, SPELL_SOUL_CONSUMPTION, TRIGGERED_IGNORE_SET_FACING);
events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, Seconds(20));
break;
default:
break;
}
}
DoMeleeAttackIfReady();
}
};
@@ -589,7 +550,6 @@ class npc_halion_controller : public CreatureScript
_instance(creature->GetInstanceScript()), _summons(me)
{
Initialize();
me->SetPhaseMask(me->GetPhaseMask() | 0x20, true);
}
void Initialize()
@@ -599,6 +559,15 @@ class npc_halion_controller : public CreatureScript
_twilightDamageTaken = 0;
}
void JustRespawned() override
{
if (_instance->GetGuidData(DATA_HALION))
return;
Reset();
me->GetMap()->SummonCreature(NPC_HALION, HalionRespawnPos);
}
void Reset() override
{
_summons.DespawnAll();
@@ -626,21 +595,36 @@ class npc_halion_controller : public CreatureScript
_twilightDamageTaken = 0;
_materialDamageTaken = 0;
_events.ScheduleEvent(EVENT_TRIGGER_BERSERK, 8 * MINUTE * IN_MILLISECONDS);
_events.ScheduleEvent(EVENT_TRIGGER_BERSERK, Minutes(8));
}
void JustReachedHome() override
void EnterEvadeMode(EvadeReason /*why*/) override
{
if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
{
twilightHalion->DespawnOrUnsummon();
_instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, twilightHalion);
}
if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION)))
{
halion->AI()->SetData(DATA_EVADE_METHOD, 1);
halion->AI()->EnterEvadeMode();
halion->DespawnOrUnsummon();
_instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, halion);
}
_instance->SetBossState(DATA_HALION, FAIL);
_summons.DespawnAll();
uint32 corpseDelay = me->GetCorpseDelay();
uint32 respawnDelay = me->GetRespawnDelay();
me->SetCorpseDelay(1);
me->SetRespawnDelay(30);
me->DespawnOrUnsummon();
me->SetCorpseDelay(corpseDelay);
me->SetRespawnDelay(respawnDelay);
}
void DoAction(int32 action) override
@@ -650,7 +634,16 @@ class npc_halion_controller : public CreatureScript
case ACTION_INTRO_HALION:
_events.Reset();
_events.SetPhase(PHASE_INTRO);
_events.ScheduleEvent(EVENT_START_INTRO, 2000);
_events.ScheduleEvent(EVENT_START_INTRO, Seconds(2));
break;
case ACTION_INTRO_HALION_2:
if (_instance->GetGuidData(DATA_HALION))
return;
for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_4; ++i)
if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i)))
_instance->HandleGameObject(_instance->GetGuidData(i), true, tree);
me->GetMap()->SummonCreature(NPC_HALION, HalionRespawnPos);
break;
case ACTION_MONITOR_CORPOREALITY:
{
@@ -678,8 +671,11 @@ class npc_halion_controller : public CreatureScript
_instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, 50);
_instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 50);
_events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 7500);
_events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, Seconds(7));
}
case ACTION_ACTIVATE_EMBERS:
_events.ScheduleEvent(EVENT_ACTIVATE_EMBERS, Seconds(6));
break;
default:
break;
}
@@ -692,7 +688,7 @@ class npc_halion_controller : public CreatureScript
// combat state.
if (!_events.IsInPhase(PHASE_INTRO) && me->IsInCombat() && !UpdateVictim())
{
EnterEvadeMode();
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
return;
}
@@ -704,29 +700,31 @@ class npc_halion_controller : public CreatureScript
{
case EVENT_START_INTRO:
DoCast(me, SPELL_COSMETIC_FIRE_PILLAR, true);
_events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, 4000);
_events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, Seconds(4));
break;
case EVENT_INTRO_PROGRESS_1:
for (uint8 i = DATA_BURNING_TREE_3; i <= DATA_BURNING_TREE_4; ++i)
if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i)))
_instance->HandleGameObject(_instance->GetGuidData(i), true, tree);
_events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, 4000);
_events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, Seconds(4));
break;
case EVENT_INTRO_PROGRESS_2:
for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_2; ++i)
if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i)))
_instance->HandleGameObject(_instance->GetGuidData(i), true, tree);
_events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, 4000);
_events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, Seconds(4));
break;
case EVENT_INTRO_PROGRESS_3:
DoCast(me, SPELL_FIERY_EXPLOSION);
if (_instance->GetGuidData(DATA_HALION))
return;
if (Creature* halion = me->GetMap()->SummonCreature(NPC_HALION, HalionSpawnPos))
halion->AI()->Talk(SAY_INTRO);
break;
case EVENT_TWILIGHT_MENDING:
if (ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION))) // Just check if physical Halion is spawned
if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
twilightHalion->CastSpell((Unit*)NULL, SPELL_TWILIGHT_MENDING, true);
twilightHalion->CastSpell((Unit*)nullptr, SPELL_TWILIGHT_MENDING, true);
break;
case EVENT_TRIGGER_BERSERK:
for (uint8 i = DATA_HALION; i <= DATA_TWILIGHT_HALION; i++)
@@ -734,17 +732,16 @@ class npc_halion_controller : public CreatureScript
halion->CastSpell(halion, SPELL_BERSERK, true);
break;
case EVENT_SHADOW_PULSARS_SHOOT:
if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
twilightHalion->AI()->Talk(SAY_SPHERE_PULSE);
if (Creature* orbCarrier = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ORB_CARRIER)))
orbCarrier->AI()->DoAction(ACTION_SHOOT);
_events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000);
orbCarrier->AI()->DoAction(ACTION_WARNING_SHOOT);
_events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, Seconds(30));
break;
case EVENT_CHECK_CORPOREALITY:
UpdateCorporeality();
_events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 5000);
_events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, Seconds(5));
break;
case EVENT_ACTIVATE_EMBERS:
_summons.DoZoneInCombat(NPC_LIVING_EMBER);
break;
default:
break;
@@ -770,7 +767,7 @@ class npc_halion_controller : public CreatureScript
DoZoneInCombat();
break;
case PHASE_TWO:
_events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000);
_events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, Seconds(35));
break;
default:
break;
@@ -793,7 +790,7 @@ class npc_halion_controller : public CreatureScript
uint8 oldValue = _materialCorporealityValue;
if (_twilightDamageTaken == 0 || _materialDamageTaken == 0)
{
_events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100);
_events.ScheduleEvent(EVENT_TWILIGHT_MENDING, Milliseconds(100));
_twilightDamageTaken = 0;
_materialDamageTaken = 0;
return;
@@ -833,7 +830,7 @@ class npc_halion_controller : public CreatureScript
}
case CORPOREALITY_TWILIGHT_MENDING:
{
_events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100);
_events.ScheduleEvent(EVENT_TWILIGHT_MENDING, Milliseconds(100));
_materialDamageTaken = 0;
_twilightDamageTaken = 0;
return;
@@ -889,52 +886,73 @@ class npc_orb_carrier : public CreatureScript
struct npc_orb_carrierAI : public ScriptedAI
{
npc_orb_carrierAI(Creature* creature) : ScriptedAI(creature),
instance(creature->GetInstanceScript())
_instance(creature->GetInstanceScript())
{
ASSERT(creature->GetVehicleKit());
}
void UpdateAI(uint32 /*diff*/) override
void UpdateAI(uint32 diff) override
{
/// According to sniffs this spell is cast every 1 or 2 seconds.
/// However, refreshing it looks bad, so just cast the spell if
/// we are not channeling it.
if (!me->HasUnitState(UNIT_STATE_CASTING))
me->CastSpell((Unit*)NULL, SPELL_TRACK_ROTATION, false);
me->CastSpell((Unit*)nullptr, SPELL_TRACK_ROTATION, false);
scheduler.Update(diff);
/// Workaround: This is here because even though the above spell has SPELL_ATTR1_CHANNEL_TRACK_TARGET,
/// we are having two creatures involded here. This attribute is handled clientside, meaning the client
/// sends orientation update itself. Here, no packet is sent, and the creature does not rotate. By
/// forcing the carrier to always be facing the rotation focus, we ensure everything works as it should.
if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ORB_ROTATION_FOCUS)))
if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ORB_ROTATION_FOCUS)))
me->SetFacingToObject(rotationFocus); // setInFront
}
void DoAction(int32 action) override
{
if (action == ACTION_SHOOT)
switch (action)
{
Vehicle* vehicle = me->GetVehicleKit();
Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH);
Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH);
if (southOrb && northOrb)
case ACTION_WARNING_SHOOT:
{
if (northOrb->GetTypeId() == TYPEID_UNIT)
Vehicle* vehicle = me->GetVehicleKit();
Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH);
if (northOrb && northOrb->GetTypeId() == TYPEID_UNIT)
northOrb->ToCreature()->AI()->Talk(EMOTE_WARN_LASER);
TriggerCutter(northOrb, southOrb);
scheduler.Schedule(Seconds(5), [this](TaskContext /*context*/)
{
DoAction(ACTION_SHOOT);
});
break;
}
case ACTION_SHOOT:
{
Vehicle* vehicle = me->GetVehicleKit();
Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH);
Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH);
if (southOrb && northOrb)
TriggerCutter(northOrb, southOrb);
if (!IsHeroic())
return;
if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
twilightHalion->AI()->Talk(SAY_SPHERE_PULSE);
Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST);
Unit* westOrb = vehicle->GetPassenger(SEAT_WEST);
if (eastOrb && westOrb)
TriggerCutter(eastOrb, westOrb);
if (!IsHeroic())
return;
Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST);
Unit* westOrb = vehicle->GetPassenger(SEAT_WEST);
if (eastOrb && westOrb)
TriggerCutter(eastOrb, westOrb);
break;
}
default:
break;
}
}
private:
InstanceScript* instance;
InstanceScript* _instance;
TaskScheduler scheduler;
void TriggerCutter(Unit* caster, Unit* target)
{
@@ -987,12 +1005,12 @@ class npc_meteor_strike_initial : public CreatureScript
controller->AI()->JustSummoned(me);
DoCast(me, SPELL_METEOR_STRIKE_COUNTDOWN);
DoCast(me, SPELL_BIRTH_NO_VISUAL); // Unknown purpose
DoCast(me, SPELL_BIRTH_NO_VISUAL);
if (HalionAI* halionAI = CAST_AI(HalionAI, owner->AI()))
{
Position const* ownerPos = halionAI->GetMeteorStrikePosition();
float randomAdjustment = frand(0.0f, static_cast<float>(M_PI / 5.0f));
float randomAdjustment = frand(static_cast<float>(M_PI / 5.0f), static_cast<float>(M_PI / 2.0f));
float angle[4];
angle[0] = me->GetAngle(ownerPos);
angle[1] = angle[0] + randomAdjustment;
@@ -1006,10 +1024,7 @@ class npc_meteor_strike_initial : public CreatureScript
me->SetOrientation(angle[i]);
Position newPos = me->GetNearPosition(10.0f, 0.0f); // Exact distance
if (Creature* meteor = me->SummonCreature(NPC_METEOR_STRIKE_NORTH + i, newPos, TEMPSUMMON_TIMED_DESPAWN, 30000))
{
meteor->SetOrientation(angle[i]);
_meteorList.push_back(meteor);
}
}
}
}
@@ -1046,7 +1061,7 @@ class npc_meteor_strike : public CreatureScript
{
DoCast(me, SPELL_METEOR_STRIKE_FIRE_AURA_2, true);
me->setActive(true);
_events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 500);
_events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, Milliseconds(500));
}
}
@@ -1110,7 +1125,7 @@ class npc_meteor_strike_flame : public CreatureScript
void SetGUID(ObjectGuid guid, int32 /*id = 0 */) override
{
_rootOwnerGuid = guid;
_events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 800);
_events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, Milliseconds(800));
}
void IsSummonedBy(Unit* /*summoner*/) override
@@ -1202,7 +1217,7 @@ class npc_combustion_consumption : public CreatureScript
if (type != DATA_STACKS_DISPELLED || !_damageSpell || !_explosionSpell || !summoner)
return;
me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount, me);
me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount + 1, me);
DoCast(me, _damageSpell);
int32 damage = 1200 + (stackAmount * 1290); // Needs more research.
@@ -1240,17 +1255,32 @@ class npc_living_inferno : public CreatureScript
// SMSG_SPELL_GO for the living ember stuff isn't even sent to the client - Blizzard on drugs.
if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC)
me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true);
scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/)
{
me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true);
});
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
{
controller->AI()->DoAction(ACTION_ACTIVATE_EMBERS);
controller->AI()->JustSummoned(me);
}
}
void JustDied(Unit* /*killer*/) override
{
me->DespawnOrUnsummon(1);
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
ScriptedAI::UpdateAI(diff);
}
private:
TaskScheduler scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1266,27 +1296,7 @@ class npc_living_ember : public CreatureScript
struct npc_living_emberAI : public ScriptedAI
{
npc_living_emberAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
_enrageTimer = 0;
}
void Initialize()
{
_hasEnraged = false;
}
void Reset() override
{
Initialize();
}
void EnterCombat(Unit* /*who*/) override
{
_enrageTimer = 20000;
_hasEnraged = false;
}
npc_living_emberAI(Creature* creature) : ScriptedAI(creature) { }
void IsSummonedBy(Unit* /*summoner*/) override
{
@@ -1299,25 +1309,6 @@ class npc_living_ember : public CreatureScript
{
me->DespawnOrUnsummon(1);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING))
return;
if (!_hasEnraged && _enrageTimer <= diff)
{
_hasEnraged = true;
DoCast(me, SPELL_BERSERK);
}
else _enrageTimer -= diff;
DoMeleeAttackIfReady();
}
private:
uint32 _enrageTimer;
bool _hasEnraged;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1473,6 +1464,47 @@ class spell_halion_combustion_consumption : public SpellScriptLoader
uint32 _spellID;
};
class spell_halion_combustion_consumption_periodic : public SpellScriptLoader
{
public:
spell_halion_combustion_consumption_periodic() : SpellScriptLoader("spell_halion_combustion_consumption_periodic") { }
class spell_halion_combustion_consumption_periodic_AuraScript : public AuraScript
{
PrepareAuraScript(spell_halion_combustion_consumption_periodic_AuraScript);
bool Validate(SpellInfo const* spellInfo) override
{
if (!sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell))
return false;
return true;
}
void HandleTick(AuraEffect const* aurEff)
{
PreventDefaultAction();
Unit* caster = GetCaster();
if (!caster)
return;
uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell;
int32 radius = caster->GetObjectScale() * M_PI * 10000 / 3;
caster->CastCustomSpell(triggerSpell, SPELLVALUE_RADIUS_MOD, radius, (Unit*)nullptr, TRIGGERED_FULL_MASK, nullptr, aurEff, caster->GetGUID());
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_halion_combustion_consumption_periodic_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_halion_combustion_consumption_periodic_AuraScript();
}
};
class spell_halion_marks : public SpellScriptLoader
{
public:
@@ -1511,7 +1543,7 @@ class spell_halion_marks : public SpellScriptLoader
return;
// Stacks marker
GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, NULL, NULL, GetCasterGUID());
GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, nullptr, nullptr, GetCasterGUID());
}
void Register() override
@@ -1819,6 +1851,35 @@ class spell_halion_spawn_living_embers : public SpellScriptLoader
}
};
class spell_halion_blazing_aura : public SpellScriptLoader
{
public:
spell_halion_blazing_aura() : SpellScriptLoader("spell_halion_blazing_aura") { }
class spell_halion_blazing_aura_SpellScript : public SpellScript
{
PrepareSpellScript(spell_halion_blazing_aura_SpellScript);
void HandleScript(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
GetHitUnit()->CastSpell(GetHitUnit(), GetSpellInfo()->Effects[EFFECT_1].TriggerSpell);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_halion_blazing_aura_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_FORCE_CAST);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_halion_blazing_aura_SpellScript();
}
};
void AddSC_boss_halion()
{
new boss_halion();
@@ -1840,6 +1901,7 @@ void AddSC_boss_halion()
new spell_halion_combustion_consumption("spell_halion_fiery_combustion", SPELL_MARK_OF_COMBUSTION);
new spell_halion_marks("spell_halion_mark_of_combustion", SPELL_FIERY_COMBUSTION_SUMMON, SPELL_FIERY_COMBUSTION);
new spell_halion_marks("spell_halion_mark_of_consumption", SPELL_SOUL_CONSUMPTION_SUMMON, SPELL_SOUL_CONSUMPTION);
new spell_halion_combustion_consumption_periodic();
new spell_halion_damage_aoe_summon();
new spell_halion_twilight_realm_handlers("spell_halion_leave_twilight_realm", SPELL_SOUL_CONSUMPTION, false);
new spell_halion_twilight_realm_handlers("spell_halion_enter_twilight_realm", SPELL_FIERY_COMBUSTION, true);
@@ -1848,4 +1910,5 @@ void AddSC_boss_halion()
new spell_halion_twilight_cutter();
new spell_halion_clear_debuffs();
new spell_halion_spawn_living_embers();
new spell_halion_blazing_aura();
}

View File

@@ -53,11 +53,11 @@ class instance_ruby_sanctum : public InstanceMapScript
void OnPlayerEnter(Player* /*player*/) override
{
if (!GetGuidData(DATA_HALION_CONTROLLER) && GetBossState(DATA_HALION) != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE)
if (!GetGuidData(DATA_HALION) && GetBossState(DATA_HALION) != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE)
{
instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY());
if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
halionController->AI()->DoAction(ACTION_INTRO_HALION);
if (Creature* halionController = instance->GetCreature(GetGuidData(DATA_HALION_CONTROLLER)))
halionController->AI()->DoAction(ACTION_INTRO_HALION_2);
}
}
@@ -161,6 +161,12 @@ class instance_ruby_sanctum : public InstanceMapScript
}
}
void OnCreatureRemove(Creature* creature) override
{
if (creature->GetEntry() == NPC_HALION)
HalionGUID = ObjectGuid::Empty;
}
void OnUnitDeath(Unit* unit) override
{
Creature* creature = unit->ToCreature();
@@ -170,7 +176,7 @@ class instance_ruby_sanctum : public InstanceMapScript
if (creature->GetEntry() == NPC_GENERAL_ZARITHRIAN && GetBossState(DATA_HALION) != DONE)
{
instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY());
if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
if (Creature* halionController = instance->GetCreature(GetGuidData(DATA_HALION_CONTROLLER)))
halionController->AI()->DoAction(ACTION_INTRO_HALION);
}
}

View File

@@ -56,6 +56,7 @@ enum SharedActions
ACTION_INTRO_BALTHARUS = -3975101,
ACTION_BALTHARUS_DEATH = -3975102,
ACTION_INTRO_HALION = -4014601,
ACTION_INTRO_HALION_2 = -4014602,
};
enum CreaturesIds