Scripts/HoO: ported master scripts. Need to rework and fix them.

This commit is contained in:
Ovahlord
2018-04-08 15:01:29 +02:00
parent 9c3f274491
commit 506ce5dc20
10 changed files with 3742 additions and 806 deletions

View File

@@ -0,0 +1,235 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
*
* 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 "halls_of_origination.h"
#include "ScriptMgr.h"
#include "SpellMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
enum Spells
{
// Ammunae
SPELL_ZERO_ENERGY = 72242,
SPELL_WITHER = 76043,
SPELL_CONSUME_LIFE_ENERGY = 75725,
SPELL_RAMPANT_GROWTH = 75790,
SPELL_SUMMON_SEEDLING_POD = 75621, // summons 40550 (normal) or 51329 (heroic)
SPELL_SUMMON_SPORE = 75695, // summons 40585
// Consume Life Energy spell
SPELL_CONSUME_LIFE_ENERGY_LEECH = 79768,
SPELL_CONSUME_LIFE_ENERGY_ENERGIZE = 75665,
// Seedling Pod npc
SPELL_SEEDLING_POD_ENERGIZE = 75708,
SPELL_FORCECAST_SUMMON_BLOSSOM = 75774, // forces Seedling Pods to cast 75771
SPELL_SUMMON_BLOODPETAL_BLOSSOM = 75771 // summons 40620
};
enum Texts
{
SAY_DEATH = 0,
SAY_AGGRO = 1,
SAY_SPECIAL = 2,
SAY_PLAYER_KILL = 3
};
enum Events
{
// Ammunae
EVENT_WITHER = 1,
EVENT_APPLY_IMMUNITY,
EVENT_CONSUME_LIFE_ENERGY,
EVENT_SEEDLING_POD,
EVENT_SUMMON_BLOODPETAL_BLOSSOM,
EVENT_SUMMON_SPORE,
// Blossom
EVENT_TRANSFORM,
EVENT_EMERGE,
EVENT_THORN_SLASH
};
// 39731 Ammunae <Construct of Life>
class boss_ammunae : public CreatureScript
{
public:
boss_ammunae() : CreatureScript("boss_ammunae") { }
struct boss_ammunaeAI : public BossAI
{
boss_ammunaeAI(Creature* creature) : BossAI(creature, DATA_AMMUNAE) { }
void Reset() override
{
_Reset();
me->MakeInterruptable(false);
me->AddAura(SPELL_ZERO_ENERGY, me);
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
Talk(SAY_AGGRO);
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);
events.ScheduleEvent(EVENT_WITHER, !IsHeroic() ? Seconds(7) : Seconds(5));
events.ScheduleEvent(EVENT_SEEDLING_POD, Seconds(7));
events.ScheduleEvent(EVENT_CONSUME_LIFE_ENERGY, Seconds(20));
events.ScheduleEvent(EVENT_SUMMON_SPORE, Seconds(47));
}
void JustReachedHome() override
{
me->AddAura(SPELL_ZERO_ENERGY, me);
me->SetPower(POWER_ENERGY, 0);
instance->SetBossState(DATA_AMMUNAE, NOT_STARTED);
_JustReachedHome();
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_PLAYER_KILL);
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
instance->SetBossState(DATA_AMMUNAE, FAIL);
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
me->MakeInterruptable(false);
events.Reset();
_EnterEvadeMode();
}
void JustDied(Unit* /*who*/) override
{
Talk(SAY_DEATH);
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || !CheckInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while(uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_WITHER:
me->MakeInterruptable(true);
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
DoCast(target, SPELL_WITHER);
events.ScheduleEvent(EVENT_APPLY_IMMUNITY, 1500);
events.Repeat(Seconds(20));
break;
case EVENT_APPLY_IMMUNITY:
me->MakeInterruptable(false);
break;
case EVENT_SEEDLING_POD:
// If 100 energy, cast Rampant Growth
if (me->GetPower(POWER_ENERGY) >= 100)
{
Talk(SAY_SPECIAL);
me->SetPower(POWER_ENERGY, 0);
DoCast(SPELL_RAMPANT_GROWTH);
}
DoCast(SPELL_SUMMON_SEEDLING_POD);
events.Repeat(Seconds(7));
break;
case EVENT_CONSUME_LIFE_ENERGY:
DoCast(SPELL_CONSUME_LIFE_ENERGY);
events.Repeat(Seconds(15));
break;
case EVENT_SUMMON_SPORE:
DoCast(SPELL_SUMMON_SPORE);
events.Repeat(Seconds(47));
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const
{
return new boss_ammunaeAI(creature);
}
};
// 75725 Consume Life Energy
class spell_ammunae_consume_life_energy : public SpellScriptLoader
{
public:
spell_ammunae_consume_life_energy() : SpellScriptLoader("spell_ammunae_consume_life_energy") { }
class spell_ammunae_consume_life_energy_SpellScript : public SpellScript
{
PrepareSpellScript(spell_ammunae_consume_life_energy_SpellScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CONSUME_LIFE_ENERGY_LEECH, SPELL_CONSUME_LIFE_ENERGY_ENERGIZE });
}
void FilterTargets(std::list<WorldObject*>& targets)
{
if (targets.empty())
return;
Trinity::Containers::RandomResize(targets, 1);
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
GetCaster()->CastSpell(GetHitUnit(), SPELL_CONSUME_LIFE_ENERGY_LEECH);
GetCaster()->CastSpell(GetCaster(), SPELL_CONSUME_LIFE_ENERGY_ENERGIZE);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_ammunae_consume_life_energy_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
OnEffectHitTarget += SpellEffectFn(spell_ammunae_consume_life_energy_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_ammunae_consume_life_energy_SpellScript();
}
};
void AddSC_boss_ammunae()
{
new boss_ammunae();
new spell_ammunae_consume_life_energy();
}

View File

@@ -15,80 +15,72 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
// To-do:
// - Script "Sand Vortex", heroic mode ability.
#include "halls_of_origination.h"
#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "Map.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "TemporarySummon.h"
#include "Weather.h"
enum Texts
{
SAY_AGGRO = 0,
SAY_DEATH = 1,
SAY_DEATH = 0,
SAY_AGGRO = 1,
SAY_PLAYER_KILL = 2,
SAY_SPECIAL = 3
};
enum Events
{
EVENT_RAGING_SMASH = 1,
EVENT_FLAME_BOLT = 2,
EVENT_EARTH_SPIKE = 3,
EVENT_PTAH_EXPLODE = 4,
EVENT_QUICKSAND = 5,
// Earthrager Ptah
EVENT_RAGING_SMASH = 1,
EVENT_FLAME_BOLT,
EVENT_EARTH_SPIKE,
EVENT_PTAH_EXPLODE,
EVENT_QUICKSAND,
// Beetle Stalker
EVENT_SUMMON_JEWELED_SCARAB
};
enum Spells
{
// Fight
SPELL_RAGING_SMASH = 83650,
SPELL_FLAME_BOLT = 77370,
SPELL_EARTH_SPIKE_WARN = 94974,
SPELL_PTAH_EXPLOSION = 75519,
// Disperse
SPELL_SANDSTORM = 75491,
// SPELL_TUMULTUOUS_EARTHSTORM = 75517, // Server-side spell
SPELL_PTAH_EXPLOSION = 75519,
SPELL_SUMMON_QUICKSAND = 75550, // Server-side spell + hidden client-side flag!
SPELL_SUMMON_QUICKSAND = 75550, // Spell not in DBC, no SMSG_SPELL_START/GO for it
SPELL_BEETLE_BURROW = 75463,
// Beetle Stalker
SPELL_BEETLE_BURROW = 75463, // Visual
SPELL_SUMMON_JEWELED_SCARAB = 75462,
SPELL_SUMMON_DUSTBONE_HORROR = 75521,
SPELL_SUMMON_DUSTBONE_HORROR = 75521
};
enum Phases
{
PHASE_NORMAL = 1,
PHASE_DISPERSE = 2,
PHASE_MASK_DISPERSE = (1 << PHASE_DISPERSE),
PHASE_MASK_NORMAL = (1 << PHASE_NORMAL),
PHASE_FIGHT = 1,
PHASE_EARTHSTORM = 2
};
enum PtahData
enum Sounds
{
DATA_SUMMON_DEATHS = 0
};
class SummonScarab : public BasicEvent
{
public:
SummonScarab(Unit* owner, InstanceScript* instance) : _owner(owner), _instance(instance) { }
bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override
{
if (!_instance || _instance->GetBossState(DATA_EARTHRAGER_PTAH) != IN_PROGRESS)
return true; // delete event
_owner->CastSpell(_owner, SPELL_SUMMON_JEWELED_SCARAB);
_owner->RemoveAurasDueToSpell(SPELL_BEETLE_BURROW);
return true;
}
protected:
Unit* _owner;
InstanceScript* _instance;
SOUND_PTAH_EARTHQUAKE = 18908
};
// 39428 Earthrager Ptah
class boss_earthrager_ptah : public CreatureScript
{
public:
@@ -96,82 +88,35 @@ public:
struct boss_earthrager_ptahAI : public BossAI
{
boss_earthrager_ptahAI(Creature* creature) : BossAI(creature, DATA_EARTHRAGER_PTAH), _summonDeaths(0), _hasDispersed(false) { }
boss_earthrager_ptahAI(Creature* creature) : BossAI(creature, DATA_EARTHRAGER_PTAH)
{
Initialize();
}
void Cleanup()
void Initialize()
{
std::list<Creature*> units;
GetCreatureListWithEntryInGrid(units, me, NPC_DUSTBONE_HORROR, 100.0f);
for (std::list<Creature*>::iterator itr = units.begin(); itr != units.end(); ++itr)
(*itr)->DespawnOrUnsummon();
GetCreatureListWithEntryInGrid(units, me, NPC_JEWELED_SCARAB, 100.0f);
for (std::list<Creature*>::iterator itr = units.begin(); itr != units.end(); ++itr)
(*itr)->DespawnOrUnsummon();
_hasDispersed = false;
_summonsMaxCount = 0;
_summonsDeadCount = 0;
}
void Reset() override
{
_summonDeaths = 0;
_hasDispersed = false;
Initialize();
Cleanup();
_Reset();
events.SetPhase(PHASE_NORMAL);
events.ScheduleEvent(EVENT_RAGING_SMASH, urand(7000, 12000), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_FLAME_BOLT, 15000, 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_EARTH_SPIKE, urand(16000, 21000), 0, PHASE_NORMAL);
events.SetPhase(PHASE_FIGHT);
ScheduleEvents();
}
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
void DoAction(int32 action) override
{
if (me->HealthBelowPctDamaged(50, damage) && (events.GetPhaseMask() & PHASE_MASK_NORMAL) && !_hasDispersed)
{
events.SetPhase(PHASE_DISPERSE);
_hasDispersed = true;
if (action != ACTION_PTAH_ADD_DIED)
return;
me->AttackStop();
DoCast(me, SPELL_SANDSTORM);
me->GetMap()->SetZoneWeather(AREA_TOMB_OF_THE_EARTHRAGER, WEATHER_STATE_LIGHT_SANDSTORM, 1.0f);
events.ScheduleEvent(EVENT_PTAH_EXPLODE, 6000, 0, PHASE_DISPERSE);
events.ScheduleEvent(EVENT_QUICKSAND, 10000, 0, PHASE_DISPERSE);
std::list<Creature*> stalkers;
GetCreatureListWithEntryInGrid(stalkers, me, NPC_BEETLE_STALKER, 100.0f);
std::list<Creature*> beetlers = stalkers;
Trinity::Containers::RandomResize(beetlers, 9); // Holds the summoners of Jeweled Scarab
for (std::list<Creature*>::iterator itr = beetlers.begin(); itr != beetlers.end(); ++itr)
{
stalkers.remove((*itr)); // Remove it to prevent a single trigger from spawning multiple npcs.
(*itr)->CastSpell((*itr), SPELL_BEETLE_BURROW); // Cast visual
// Summon after 5 seconds.
(*itr)->m_Events.AddEvent(new SummonScarab((*itr), instance), (*itr)->m_Events.CalculateTime(5000));
}
Trinity::Containers::RandomResize(stalkers, 2); // Holds the summoners of Dustbone Horror
for (std::list<Creature*>::iterator itr = stalkers.begin(); itr != stalkers.end(); ++itr)
(*itr)->CastSpell((*itr), SPELL_SUMMON_DUSTBONE_HORROR);
}
}
void SetData(uint32 index, uint32 /*value*/) override
{
if (index == DATA_SUMMON_DEATHS)
{
++_summonDeaths;
if (_summonDeaths == 11) // All summons died
{
me->GetMap()->SetZoneWeather(AREA_TOMB_OF_THE_EARTHRAGER, WEATHER_STATE_FOG, 0.0f);
me->RemoveAurasDueToSpell(SPELL_PTAH_EXPLOSION);
events.SetPhase(PHASE_NORMAL);
events.ScheduleEvent(EVENT_RAGING_SMASH, urand(7000, 12000), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_FLAME_BOLT, 15000, 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_EARTH_SPIKE, urand(16000, 21000), 0, PHASE_NORMAL);
}
}
// Increase _summonsDeadCount and check if max reached.
if (++_summonsDeadCount == _summonsMaxCount)
ExitDispersePhase();
}
void JustEngagedWith(Unit* /*who*/) override
@@ -181,6 +126,23 @@ public:
_JustEngagedWith();
}
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
// About to die? One-hit cases...
if (int64(me->GetHealth()) - int64(damage) <= 0)
return;
// Earthquake phase happens at 50% health remaining.
if (me->HealthBelowPctDamaged(50, damage) && !_hasDispersed)
EnterDispersePhase();
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_PLAYER_KILL);
}
void JustDied(Unit* /*killer*/) override
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
@@ -193,7 +155,6 @@ public:
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustReachedHome();
instance->SetBossState(DATA_EARTHRAGER_PTAH, FAIL);
}
void UpdateAI(uint32 diff) override
@@ -212,37 +173,109 @@ public:
{
case EVENT_RAGING_SMASH:
DoCastVictim(SPELL_RAGING_SMASH);
events.ScheduleEvent(EVENT_RAGING_SMASH, urand(7000, 12000), 0, PHASE_NORMAL);
events.Repeat(Seconds(19));
break;
case EVENT_FLAME_BOLT:
DoCast(me, SPELL_FLAME_BOLT);
events.ScheduleEvent(EVENT_FLAME_BOLT, 15000, 0, PHASE_NORMAL);
events.Repeat(Seconds(21));
break;
case EVENT_EARTH_SPIKE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
DoCast(target, SPELL_EARTH_SPIKE_WARN);
events.ScheduleEvent(EVENT_EARTH_SPIKE, urand(16000, 21000), 0, PHASE_NORMAL);
events.Repeat(Seconds(15));
break;
case EVENT_PTAH_EXPLODE:
instance->SendEncounterUnit(ENCOUNTER_FRAME_UPDATE_PRIORITY, me, 0);
Talk(SAY_SPECIAL);
DoCast(me, SPELL_PTAH_EXPLOSION);
break;
case EVENT_QUICKSAND:
// Spell not in DBC, it is not cast either, according to sniffs
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
if (Creature* quicksand = me->SummonCreature(NPC_QUICKSAND, *target))
quicksand->SetUInt32Value(UNIT_CREATED_BY_SPELL, SPELL_SUMMON_QUICKSAND);
events.ScheduleEvent(EVENT_QUICKSAND, 10000, 0, PHASE_DISPERSE);
DoCast(target, SPELL_SUMMON_QUICKSAND);
events.Repeat(Seconds(21)); //Seconds(10)
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
if (events.GetPhaseMask() & PHASE_MASK_NORMAL) // Do not melee in the disperse phase
if (!events.IsInPhase(PHASE_EARTHSTORM)) // Do not melee in the disperse phase
DoMeleeAttackIfReady();
}
protected:
uint8 _summonDeaths;
private:
void ScheduleEvents()
{
events.Reset();
events.ScheduleEvent(EVENT_RAGING_SMASH, Seconds(7), 0, PHASE_FIGHT); // Seconds(12)
events.ScheduleEvent(EVENT_FLAME_BOLT, Seconds(8), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_EARTH_SPIKE, Seconds(15), 0, PHASE_FIGHT); // Seconds(21)
}
void EnterDispersePhase()
{
events.SetPhase(PHASE_EARTHSTORM);
_hasDispersed = true;
me->SetReactState(REACT_PASSIVE);
me->InterruptNonMeleeSpells(true);
me->AttackStop();
me->GetMap()->SetZoneWeather(AREA_TOMB_OF_THE_EARTHRAGER, WEATHER_STATE_LIGHT_SANDSTORM, 1.0f);
DoCast(me, SPELL_SANDSTORM);
std::list<Creature*> stalkers;
GetCreatureListWithEntryInGrid(stalkers, me, NPC_BEETLE_STALKER, 100.0f);
for (std::list<Creature*>::iterator itr = stalkers.begin(); itr != stalkers.end(); ++itr)
{
++_summonsMaxCount;
if (-400.f < (*itr)->GetPositionY() && (*itr)->GetPositionY() < -390.f) // 2 stalkers in the middle
(*itr)->CastSpell((*itr), SPELL_SUMMON_DUSTBONE_HORROR);
else
(*itr)->CastSpell((*itr), SPELL_BEETLE_BURROW);
}
events.ScheduleEvent(EVENT_PTAH_EXPLODE, Seconds(6), 0, PHASE_EARTHSTORM);
events.ScheduleEvent(EVENT_QUICKSAND, Seconds(10), 0, PHASE_EARTHSTORM);
}
void ExitDispersePhase()
{
me->RemoveAurasDueToSpell(SPELL_PTAH_EXPLOSION);
me->GetMap()->SetZoneWeather(AREA_TOMB_OF_THE_EARTHRAGER, WEATHER_STATE_FOG, 0.0f);
instance->SendEncounterUnit(ENCOUNTER_FRAME_UPDATE_PRIORITY, me, 2);
events.SetPhase(PHASE_FIGHT);
ScheduleEvents();
}
void Cleanup()
{
std::list<Creature*> units;
GetCreatureListWithEntryInGrid(units, me, NPC_BEETLE_STALKER, 100.0f);
for (std::list<Creature*>::iterator itr = units.begin(); itr != units.end(); ++itr)
{
++_summonsMaxCount;
if (-400.f < (*itr)->GetPositionY() && (*itr)->GetPositionY() < -390.f) // 2 stalkers in the middle
(*itr)->RemoveAurasDueToSpell(SPELL_SUMMON_DUSTBONE_HORROR);
else
(*itr)->RemoveAurasDueToSpell(SPELL_BEETLE_BURROW);
}
units.clear();
GetCreatureListWithEntryInGrid(units, me, NPC_DUSTBONE_HORROR, 100.0f);
for (std::list<Creature*>::iterator itr = units.begin(); itr != units.end(); ++itr)
(*itr)->DespawnOrUnsummon();
units.clear();
GetCreatureListWithEntryInGrid(units, me, NPC_JEWELED_SCARAB, 100.0f);
for (std::list<Creature*>::iterator itr = units.begin(); itr != units.end(); ++itr)
(*itr)->DespawnOrUnsummon();
}
bool _hasDispersed;
uint8 _summonsMaxCount;
uint8 _summonsDeadCount;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -251,6 +284,62 @@ public:
}
};
// 15989 (criteria ID) Straw That Broke the Camel's Back
class achievement_straw_broke_camels_back : public AchievementCriteriaScript
{
public:
achievement_straw_broke_camels_back() : AchievementCriteriaScript("achievement_straw_broke_camels_back") { }
bool OnCheck(Player* player, Unit* /*target*/) override
{
if (Unit* vehicle = player->GetVehicleBase())
return vehicle->GetEntry() == NPC_HOO_CAMEL;
return false;
}
};
// 40459 Beetle Stalker
class npc_ptah_beetle_stalker : public CreatureScript
{
public:
npc_ptah_beetle_stalker() : CreatureScript("npc_ptah_beetle_stalker") { }
struct npc_ptah_beetle_stalkerAI : public ScriptedAI
{
npc_ptah_beetle_stalkerAI(Creature* creature) : ScriptedAI(creature) { }
void SpellHit(Unit* /*caster*/, const SpellInfo* spellInfo) override
{
if (spellInfo->Id == SPELL_BEETLE_BURROW)
events.ScheduleEvent(EVENT_SUMMON_JEWELED_SCARAB, Seconds(5), Seconds(6));
}
void UpdateAI(uint32 diff) override
{
if (events.Empty())
return;
events.Update(diff);
if (events.ExecuteEvent() == EVENT_SUMMON_JEWELED_SCARAB)
{
DoCastAOE(SPELL_SUMMON_JEWELED_SCARAB);
me->RemoveAurasDueToSpell(SPELL_BEETLE_BURROW);
}
}
private:
EventMap events;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_ptah_beetle_stalkerAI>(creature);
}
};
class spell_earthrager_ptah_flame_bolt : public SpellScriptLoader
{
public:
@@ -277,6 +366,34 @@ class spell_earthrager_ptah_flame_bolt : public SpellScriptLoader
}
};
// 75491 Sandstorm spell_earthrager_ptah_sandstorm
class spell_earthrager_ptah_sandstorm : public SpellScriptLoader
{
public:
spell_earthrager_ptah_sandstorm() : SpellScriptLoader("spell_earthrager_ptah_sandstorm") { }
class spell_earthrager_ptah_sandstorm_SpellScript : public SpellScript
{
PrepareSpellScript(spell_earthrager_ptah_sandstorm_SpellScript);
void PlaySound(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetHitUnit()->ToPlayer())
GetCaster()->PlayDirectSound(SOUND_PTAH_EARTHQUAKE, player);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_earthrager_ptah_sandstorm_SpellScript::PlaySound, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_earthrager_ptah_sandstorm_SpellScript();
}
};
class spell_earthrager_ptah_explosion : public SpellScriptLoader
{
public:
@@ -304,14 +421,14 @@ public:
}
}
void Register()
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_earthrager_ptah_explosion_AuraScript::SetFlags, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
OnEffectRemove += AuraEffectRemoveFn(spell_earthrager_ptah_explosion_AuraScript::RemoveFlags, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const
AuraScript* GetAuraScript() const override
{
return new spell_earthrager_ptah_explosion_AuraScript();
}
@@ -320,6 +437,9 @@ public:
void AddSC_boss_earthrager_ptah()
{
new boss_earthrager_ptah();
new achievement_straw_broke_camels_back();
new npc_ptah_beetle_stalker();
new spell_earthrager_ptah_flame_bolt();
new spell_earthrager_ptah_sandstorm();
new spell_earthrager_ptah_explosion();
}

View File

@@ -0,0 +1,859 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
*
* 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 "GridNotifiers.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
#include "halls_of_origination.h"
#include "TemporarySummon.h"
enum Spells
{
SPELL_SUPERNOVA = 74136,
SPELL_SUPERNOVA_EFFECT = 74137,
// Startfall / Astral Rain ability
SPELL_ASTRAL_RAIN_CONTROLLER = 74381,
SPELL_ASTRAL_RAIN_1 = 74134,
SPELL_ASTRAL_RAIN_2 = 74365,
SPELL_ASTRAL_RAIN_3 = 74371,
// Adds / Celestial Call ability
SPELL_ASTRAL_FAMILIAR_CONTROLLER = 74383,
SPELL_CELESTIAL_CALL_1 = 74362,
SPELL_CELESTIAL_CALL_2 = 74355,
SPELL_CELESTIAL_CALL_3 = 74364,
// Mana shield / Veil of Sky ability
SPELL_MANA_SHIELD_CONTROLLER = 74382,
SPELL_VEIL_OF_SKY_1 = 74133,
SPELL_VEIL_OF_SKY_2 = 74372,
SPELL_VEIL_OF_SKY_3 = 74373,
SPELL_VEIL_OF_SKY_DAMAGE = 79370,
// Mirror Images (at 66% and 33%)
SPELL_MIRROR_IMAGE_STARRY_SKY_SPAWNER = 69941,
SPELL_MIRROR_IMAGE_STARRY_SKY_N = 69936,
SPELL_MIRROR_IMAGE_STARRY_SKY_E = 69939,
SPELL_MIRROR_IMAGE_STARRY_SKY_W = 69940,
SPELL_ASTRAL_SHIFT = 74312,
SPELL_ASTRAL_SHIFT_EXPLOSION = 74333,
SPELL_MIRROR_IMAGE_SPAWNER = 74264,
SPELL_MIRROR_IMAGE_N = 74263,
SPELL_MIRROR_IMAGE_E = 74262,
SPELL_MIRROR_IMAGE_W = 74261,
SPELL_ADDS_STATE_VISUAL = 74289,
SPELL_STARFALL_STATE_VISUAL = 74265,
SPELL_MANA_SHIELD_STATE_VISUAL = 74266,
SPELL_IMAGE_EXPLOSION = 74301,
// Celestial Familiar npc
SPELL_ORB_VISUAL = 74356,
SPELL_ARCANE_BARRAGE = 74374,
// Starry Sky npc
SPELL_STARRY_SKY_VISUAL = 74149,
// Astral Shift Explosion Visual npc
SPELL_ASTRAL_SHIFT_EXPLOSION_VISUAL = 74331,
// Spatial Flux & Energy Flux (heroic only)
SPELL_CALL_OF_SKY = 90750 // Summons Spatial Flux (npc script in halls_of_oriignation.cpp)
};
enum NPCs
{
NPC_CELESTIAL_FAMILIAR = 39795,
NPC_ASTRAL_RAIN = 39720, // N
NPC_CELESTIAL_CALL = 39721, // E
NPC_VEIL_OF_SKY = 39722, // W
NPC_ISISET_SPATIAL_FLUX = 48707 // heroic only
};
enum Texts
{
SAY_AGGRO = 0,
SAY_SUPERNOVA = 1,
SAY_SUPERNOVA_WARNING = 2,
SAY_PLAYER_KILL = 3,
SAY_DEATH = 4
};
enum Events
{
EVENT_SUPERNOVA = 1,
EVENT_ASTRAL_RAIN,
EVENT_ASTRAL_FAMILIAR,
EVENT_VEIL_OF_SKY,
EVENT_IMAGES_ATTACK,
EVENT_DESPAWN_IMAGES,
EVENT_ORB_ADD_VISUAL,
EVENT_ORB_SET_AGGRESSIVE,
EVENT_ORB_ARCANE_BARRAGE,
EVENT_IMAGE_ASTRAL_RAIN_ABILITY,
EVENT_IMAGE_ARCANE_BARRAGE_ABILITY,
EVENT_IMAGE_VEIL_OF_SKY_ABILITY,
EVENT_STARRY_SKY_ADD_VISUAL,
};
enum Actions
{
ACTION_IMAGES_SET_AGGRESSIVE,
ACTION_MIRROR_IMAGE_DIED,
ACTION_IMAGES_SET_PASSIVE,
ACTION_IMAGES_DESPAWN
};
class boss_isiset : public CreatureScript
{
public:
boss_isiset() : CreatureScript("boss_isiset") { }
struct boss_isisetAI : public BossAI
{
boss_isisetAI(Creature* creature) : BossAI(creature, DATA_ISISET) { }
void Reset() override
{
_Reset();
me->SetReactState(REACT_AGGRESSIVE);
_transitionPhase = false;
instance->SetData(DATA_ISISET_PHASE, 1);
instance->SetData(DATA_ISISET_ASTRAL_RAIN_ALIVE, 1);
instance->SetData(DATA_ISISET_CELESTIAL_CALL_ALIVE, 1);
instance->SetData(DATA_ISISET_VEIL_OF_SKY_ALIVE, 1);
}
void JustEngagedWith(Unit* /*victim*/) override
{
Talk(SAY_AGGRO);
if (IsHeroic())
DoCastSelf(SPELL_CALL_OF_SKY);
RescheduleEvents();
_JustEngagedWith();
instance->SendEncounterUnit(ENCOUNTER_FRAME_SET_COMBAT_RES_LIMIT, 0);
}
void DamageTaken(Unit* /*attacker*/, uint32 &damage) override
{
if (_transitionPhase)
return;
// Second transition
if (instance->GetData(DATA_ISISET_PHASE) == 2 && me->HealthBelowPctDamaged(33, damage))
MirrorImage();
// First transition
if (instance->GetData(DATA_ISISET_PHASE) == 1 && me->HealthBelowPctDamaged(66, damage))
MirrorImage();
}
void DoAction(int32 action) override
{
// We only accept action from the first dying image
if (_mirrorImageDied || action != ACTION_MIRROR_IMAGE_DIED)
return;
_mirrorImageDied = true;
// Handle despawning
DummyEntryCheckPredicate pred;
summons.DoAction(ACTION_IMAGES_SET_PASSIVE, pred);
events.ScheduleEvent(EVENT_DESPAWN_IMAGES, Seconds(2));
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_PLAYER_KILL);
}
void JustDied(Unit* /*killer*/) override
{
//instance->SendEncounterUnit(ENCOUNTER_FRAME_SET_COMBAT_RES_LIMIT, ?me?, 1); //from sniff, not sure what it does
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me, 0);
_JustDied();
Talk(SAY_DEATH);
}
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_ASTRAL_RAIN:
DoCastSelf(SPELL_ASTRAL_RAIN_CONTROLLER);
events.Repeat(Seconds(15), Seconds(19));
break;
case EVENT_ASTRAL_FAMILIAR:
DoCastSelf(SPELL_ASTRAL_FAMILIAR_CONTROLLER);
events.Repeat(Seconds(26), Seconds(30));
break;
case EVENT_VEIL_OF_SKY:
DoCastSelf(SPELL_MANA_SHIELD_CONTROLLER);
events.Repeat(Seconds(20), Seconds(25));
break;
case EVENT_SUPERNOVA:
DoCastSelf(SPELL_SUPERNOVA);
Talk(SAY_SUPERNOVA);
Talk(SAY_SUPERNOVA_WARNING);
events.Repeat(Seconds(45), Seconds(50));
break;
case EVENT_IMAGES_ATTACK:
{
DummyEntryCheckPredicate pred;
summons.DoAction(ACTION_IMAGES_SET_AGGRESSIVE, pred);
break;
}
case EVENT_DESPAWN_IMAGES:
{
DummyEntryCheckPredicate pred;
summons.DoAction(ACTION_IMAGES_DESPAWN, pred);
instance->SetData(DATA_ISISET_PHASE, instance->GetData(DATA_ISISET_PHASE) + 1);
_transitionPhase = false;
me->RemoveAurasDueToSpell(SPELL_ASTRAL_SHIFT);
instance->SendEncounterUnit(ENCOUNTER_FRAME_UPDATE_PRIORITY, me, 1);
RescheduleEvents();
break;
}
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
DoMeleeAttackIfReady();
}
private:
void RescheduleEvents()
{
events.Reset();
if (instance->GetData(DATA_ISISET_ASTRAL_RAIN_ALIVE))
events.ScheduleEvent(EVENT_ASTRAL_RAIN, Seconds(4), Seconds(6));
if (instance->GetData(DATA_ISISET_CELESTIAL_CALL_ALIVE))
events.ScheduleEvent(EVENT_ASTRAL_FAMILIAR, Seconds(7));
if (instance->GetData(DATA_ISISET_VEIL_OF_SKY_ALIVE))
events.ScheduleEvent(EVENT_VEIL_OF_SKY, Seconds(8), Seconds(10));
events.ScheduleEvent(EVENT_SUPERNOVA, Seconds(12), Seconds(17));
}
// Mirror Image transition phase
void MirrorImage()
{
_transitionPhase = true;
events.Reset();
instance->SendEncounterUnit(ENCOUNTER_FRAME_UPDATE_PRIORITY, me, 0);
_mirrorImageDied = false;
DoCastSelf(SPELL_ASTRAL_SHIFT);
DoCastSelf(SPELL_ASTRAL_SHIFT_EXPLOSION);
DoCastSelf(SPELL_MIRROR_IMAGE_STARRY_SKY_SPAWNER);
DoCastSelf(SPELL_MIRROR_IMAGE_SPAWNER);
events.ScheduleEvent(EVENT_IMAGES_ATTACK, Seconds(2));
}
bool _transitionPhase;
bool _mirrorImageDied;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<boss_isisetAI>(creature);
}
};
// 39795 - Celestial Familiar
class npc_celestial_familiar : public CreatureScript
{
public:
npc_celestial_familiar() : CreatureScript("npc_celestial_familiar") { }
struct npc_celestial_familiarAI : public ScriptedAI
{
npc_celestial_familiarAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
me->SetReactState(REACT_PASSIVE);
me->SetInCombatWithZone();
events.Reset();
events.ScheduleEvent(EVENT_ORB_ADD_VISUAL, Seconds(1));
events.ScheduleEvent(EVENT_ORB_SET_AGGRESSIVE, Seconds(2));
events.ScheduleEvent(EVENT_ORB_ARCANE_BARRAGE, Seconds(4), Seconds(6));
}
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_ORB_ADD_VISUAL:
DoCastSelf(SPELL_ORB_VISUAL);
break;
case EVENT_ORB_SET_AGGRESSIVE:
me->SetReactState(REACT_AGGRESSIVE);
break;
case EVENT_ORB_ARCANE_BARRAGE:
DoCastSelf(SPELL_ARCANE_BARRAGE);
events.Repeat(Seconds(6), Seconds(7));
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
EventMap events;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_celestial_familiarAI>(creature);
}
};
// 39720 - Astral Rain
// 39721 - Celestial Call
// 39722 - Veil of Sky
class npc_isiset_mirror_image : public CreatureScript
{
public:
npc_isiset_mirror_image() : CreatureScript("npc_isiset_mirror_image") { }
struct npc_isiset_mirror_imageAI : public ScriptedAI
{
npc_isiset_mirror_imageAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }
void Reset() override
{
me->SetReactState(REACT_PASSIVE);
_instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
switch (me->GetEntry()) {
case NPC_ASTRAL_RAIN:
_events.ScheduleEvent(EVENT_IMAGE_ASTRAL_RAIN_ABILITY, Seconds(3), Seconds(5));
DoCastSelf(SPELL_STARFALL_STATE_VISUAL);
break;
case NPC_CELESTIAL_CALL:
_events.ScheduleEvent(EVENT_IMAGE_ARCANE_BARRAGE_ABILITY, Seconds(6));
DoCastSelf(SPELL_ADDS_STATE_VISUAL);
break;
case NPC_VEIL_OF_SKY:
_events.ScheduleEvent(EVENT_IMAGE_VEIL_OF_SKY_ABILITY, Seconds(8), Seconds(9));
//DoCastSelf(SPELL_MANA_SHIELD_STATE_VISUAL); // Disabled in sniffs/on retail, too shiny.
break;
}
}
void DamageTaken(Unit* /*attacker*/, uint32 &damage) override
{
// Because only one image explodes, others become passive
if (me->GetReactState() != REACT_AGGRESSIVE || me->GetHealth() > damage)
return;
me->SetReactState(REACT_PASSIVE);
damage = me->GetHealth() - 1;
DoCastSelf(SPELL_IMAGE_EXPLOSION);
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_IMAGES_SET_AGGRESSIVE:
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
AttackStart(target);
break;
case ACTION_IMAGES_SET_PASSIVE:
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
break;
case ACTION_IMAGES_DESPAWN:
_instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
me->DespawnOrUnsummon();
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_IMAGE_ASTRAL_RAIN_ABILITY:
DoCastSelf(SPELL_ASTRAL_RAIN_CONTROLLER);
_events.Repeat(Seconds(16), Seconds(19));
break;
case EVENT_IMAGE_ARCANE_BARRAGE_ABILITY:
DoCastSelf(SPELL_ARCANE_BARRAGE);
_events.Repeat(Seconds(8), Seconds(10));
break;
case EVENT_IMAGE_VEIL_OF_SKY_ABILITY:
DoCastSelf(SPELL_VEIL_OF_SKY_1); // Veil of Sky npc does not cast controller spell!
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
DoMeleeAttackIfReady();
}
private:
EventMap _events;
InstanceScript* _instance;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_isiset_mirror_imageAI>(creature);
}
};
// 74381 - Astral Rain Controller Spell
class spell_isiset_astral_rain_controller : public SpellScriptLoader
{
public:
spell_isiset_astral_rain_controller() : SpellScriptLoader("spell_isiset_astral_rain_controller") { }
class spell_isiset_astral_rain_controller_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_astral_rain_controller_SpellScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({
SPELL_ASTRAL_RAIN_1,
SPELL_ASTRAL_RAIN_2,
SPELL_ASTRAL_RAIN_3
});
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
InstanceScript* instance = GetCaster()->GetInstanceScript();
if (!instance || !instance->GetData(DATA_ISISET_ASTRAL_RAIN_ALIVE))
return;
uint32 phase = instance->GetData(DATA_ISISET_PHASE);
if (phase == 1)
GetCaster()->CastSpell(GetCaster(), SPELL_ASTRAL_RAIN_1, true);
else if (phase == 2)
GetCaster()->CastSpell(GetCaster(), SPELL_ASTRAL_RAIN_2, true);
else if (phase == 3)
GetCaster()->CastSpell(GetCaster(), SPELL_ASTRAL_RAIN_3, true);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_isiset_astral_rain_controller_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_astral_rain_controller_SpellScript();
}
};
// 74382 - Mana Shield Controller Spell
class spell_isiset_mana_shield_controller : public SpellScriptLoader
{
public:
spell_isiset_mana_shield_controller() : SpellScriptLoader("spell_isiset_mana_shield_controller") { }
class spell_isiset_mana_shield_controller_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_mana_shield_controller_SpellScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({
SPELL_VEIL_OF_SKY_1,
SPELL_VEIL_OF_SKY_2,
SPELL_VEIL_OF_SKY_3
});
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
InstanceScript* instance = GetCaster()->GetInstanceScript();
if (!instance || !instance->GetData(DATA_ISISET_VEIL_OF_SKY_ALIVE))
return;
uint32 phase = instance->GetData(DATA_ISISET_PHASE);
if (phase == 1)
GetCaster()->CastSpell(GetCaster(), SPELL_VEIL_OF_SKY_1);
else if (phase == 2)
GetCaster()->CastSpell(GetCaster(), SPELL_VEIL_OF_SKY_2);
else if (phase == 3)
GetCaster()->CastSpell(GetCaster(), SPELL_VEIL_OF_SKY_3);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_isiset_mana_shield_controller_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_mana_shield_controller_SpellScript();
}
};
// 74383 - Astral Familiar Controller Spell
class spell_isiset_astral_familiar_controller : public SpellScriptLoader
{
public:
spell_isiset_astral_familiar_controller() : SpellScriptLoader("spell_isiset_astral_familiar_controller") { }
class spell_isiset_astral_familiar_controller_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_astral_familiar_controller_SpellScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({
SPELL_CELESTIAL_CALL_1,
SPELL_CELESTIAL_CALL_2,
SPELL_CELESTIAL_CALL_3
});
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
InstanceScript* instance = GetCaster()->GetInstanceScript();
if (!instance || !instance->GetData(DATA_ISISET_CELESTIAL_CALL_ALIVE))
return;
uint32 phase = instance->GetData(DATA_ISISET_PHASE);
if (phase == 1)
GetCaster()->CastSpell(GetCaster(), SPELL_CELESTIAL_CALL_1, true);
else if (phase == 2)
GetCaster()->CastSpell(GetCaster(), SPELL_CELESTIAL_CALL_2, true);
else if (phase == 3)
GetCaster()->CastSpell(GetCaster(), SPELL_CELESTIAL_CALL_3, true);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_isiset_astral_familiar_controller_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_astral_familiar_controller_SpellScript();
}
};
// 74133, 74372, 74373 - Veil of Sky (mana shield)
class spell_isiset_veil_of_sky : public SpellScriptLoader
{
public:
spell_isiset_veil_of_sky() : SpellScriptLoader("spell_isiset_veil_of_sky") { }
class spell_isiset_veil_of_sky_AuraScript : public AuraScript
{
PrepareAuraScript(spell_isiset_veil_of_sky_AuraScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_VEIL_OF_SKY_DAMAGE });
}
bool Load() override
{
reflectPct = GetSpellInfo()->Effects[EFFECT_1].BasePoints;
return true;
}
void Trigger(AuraEffect* /*aurEff*/, DamageInfo& dmgInfo, uint32& absorbAmount)
{
if (dmgInfo.GetDamageType() != SPELL_DIRECT_DAMAGE)
return;
int32 damage = CalculatePct(absorbAmount, reflectPct);
GetCaster()->CastCustomSpell(SPELL_VEIL_OF_SKY_DAMAGE, SPELLVALUE_BASE_POINT0, damage, dmgInfo.GetAttacker(), true);
}
void Register() override
{
AfterEffectManaShield += AuraEffectManaShieldFn(spell_isiset_veil_of_sky_AuraScript::Trigger, EFFECT_0);
}
private:
int32 reflectPct;
};
AuraScript* GetAuraScript() const override
{
return new spell_isiset_veil_of_sky_AuraScript();
}
};
// 74137 - Supernova (disorient + triggers 76670) (are pets also affected?)
// 76670 - Supernova (damage)
class spell_isiset_supernova_filter : public SpellScriptLoader
{
public:
spell_isiset_supernova_filter() : SpellScriptLoader("spell_isiset_supernova_filter") { }
class spell_isiset_supernova_filter_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_supernova_filter_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
targets.remove_if([caster](WorldObject* object) { return !object->ToUnit() || !object->ToUnit()->IsCharmedOwnedByPlayerOrPlayer() || !object->isInFront(caster, 2.5f); });
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_isiset_supernova_filter_SpellScript::FilterTargets, m_scriptSpellId == SPELL_SUPERNOVA_EFFECT ? EFFECT_1 : EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_supernova_filter_SpellScript();
}
};
// 69941 - Mirror Image
class spell_isiset_mirror_image_starry_sky_spawner : public SpellScriptLoader
{
public:
spell_isiset_mirror_image_starry_sky_spawner() : SpellScriptLoader("spell_isiset_mirror_image_starry_sky_spawner") { }
class spell_isiset_mirror_image_starry_sky_spawner_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_mirror_image_starry_sky_spawner_SpellScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_MIRROR_IMAGE_STARRY_SKY_N, SPELL_MIRROR_IMAGE_STARRY_SKY_E, SPELL_MIRROR_IMAGE_STARRY_SKY_W });
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
InstanceScript* instance = GetCaster()->GetInstanceScript();
if (!instance)
return;
if (instance->GetData(DATA_ISISET_CELESTIAL_CALL_ALIVE))
GetCaster()->CastSpell(GetCaster(), SPELL_MIRROR_IMAGE_STARRY_SKY_N, true);
if (instance->GetData(DATA_ISISET_ASTRAL_RAIN_ALIVE))
GetCaster()->CastSpell(GetCaster(), SPELL_MIRROR_IMAGE_STARRY_SKY_E, true);
if (instance->GetData(DATA_ISISET_VEIL_OF_SKY_ALIVE))
GetCaster()->CastSpell(GetCaster(), SPELL_MIRROR_IMAGE_STARRY_SKY_W, true);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_isiset_mirror_image_starry_sky_spawner_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_mirror_image_starry_sky_spawner_SpellScript();
}
};
// 74264 - Mirror Image
class spell_isiset_mirror_image_spawner : public SpellScriptLoader
{
public:
spell_isiset_mirror_image_spawner() : SpellScriptLoader("spell_isiset_mirror_image_spawner") { }
class spell_isiset_mirror_image_spawner_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_mirror_image_spawner_SpellScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_MIRROR_IMAGE_N, SPELL_MIRROR_IMAGE_E, SPELL_MIRROR_IMAGE_W });
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
InstanceScript* const instance = GetCaster()->GetInstanceScript();
if (!instance)
return;
if (instance->GetData(DATA_ISISET_CELESTIAL_CALL_ALIVE))
GetCaster()->CastSpell(GetCaster(), SPELL_MIRROR_IMAGE_N, true);
if (instance->GetData(DATA_ISISET_ASTRAL_RAIN_ALIVE))
GetCaster()->CastSpell(GetCaster(), SPELL_MIRROR_IMAGE_E, true);
if (instance->GetData(DATA_ISISET_VEIL_OF_SKY_ALIVE))
GetCaster()->CastSpell(GetCaster(), SPELL_MIRROR_IMAGE_W, true);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_isiset_mirror_image_spawner_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_mirror_image_spawner_SpellScript();
}
};
// 74301 - Image Explosion
class spell_isiset_image_explosion : public SpellScriptLoader
{
public:
spell_isiset_image_explosion() : SpellScriptLoader("spell_isiset_image_explosion") { }
class spell_isiset_image_explosion_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_image_explosion_SpellScript);
void HandleDummy(SpellEffIndex /*effIndex*/)
{
InstanceScript* const instance = GetCaster()->GetInstanceScript();
if (!instance)
return;
switch (GetCaster()->GetEntry())
{
case NPC_ASTRAL_RAIN:
instance->SetData(DATA_ISISET_ASTRAL_RAIN_ALIVE, 0);
break;
case NPC_CELESTIAL_CALL:
instance->SetData(DATA_ISISET_CELESTIAL_CALL_ALIVE, 0);
break;
case NPC_VEIL_OF_SKY:
instance->SetData(DATA_ISISET_VEIL_OF_SKY_ALIVE, 0);
break;
}
if (Creature* Isiset = instance->GetCreature(DATA_ISISET))
Isiset->AI()->DoAction(ACTION_MIRROR_IMAGE_DIED);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_isiset_image_explosion_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_image_explosion_SpellScript();
}
};
// 90755 Call of Sky (heroic only)
class spell_isiset_call_of_sky : public SpellScriptLoader
{
public:
spell_isiset_call_of_sky() : SpellScriptLoader("spell_isiset_call_of_sky") { }
class spell_isiset_call_of_sky_SpellScript : public SpellScript
{
PrepareSpellScript(spell_isiset_call_of_sky_SpellScript);
void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (InstanceScript* instance = GetCaster()->GetInstanceScript())
if (Creature* Isiset = instance->GetCreature(DATA_ISISET))
Isiset->SummonCreature(NPC_ISISET_SPATIAL_FLUX, GetHitDest()->GetPositionX(), GetHitDest()->GetPositionY(), GetHitDest()->GetPositionZ(), 2.775074f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_isiset_call_of_sky_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_isiset_call_of_sky_SpellScript();
}
};
void AddSC_boss_isiset()
{
new boss_isiset();
new npc_celestial_familiar();
new npc_isiset_mirror_image();
new spell_isiset_astral_rain_controller();
new spell_isiset_mana_shield_controller();
new spell_isiset_astral_familiar_controller();
new spell_isiset_veil_of_sky();
new spell_isiset_supernova_filter();
new spell_isiset_mirror_image_starry_sky_spawner();
new spell_isiset_mirror_image_spawner();
new spell_isiset_image_explosion();
new spell_isiset_call_of_sky();
}

View File

@@ -0,0 +1,530 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
*
* 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 "halls_of_origination.h"
#include "Map.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
enum Spells
{
SPELL_CHAOS_BOLT = 77069,
SPELL_CHAOS_BLAST = 76674, // Forcecast player to summon npc that Setesh fires missle at.
SPELL_SEED_OF_CHAOS = 76888,
SPELL_REIGN_OF_CHAOS = 77023,
SPELL_CHANNEL_CHAOS_PORTAL = 76784,
// Chaos Blast
SPELL_CHAOS_BLAST_ME = 76737, // Tells Setesh to launch missle at it.
SPELL_CHAOS_BLAST_MISSLE = 76676,
SPELL_CHAOS_BLAST_AURA = 76681,
// Seed of Chaos
SPELL_SEED_OF_CHAOS_DUMMY = 76865,
SPELL_SEED_OF_CHAOS_EXPLOSION = 76870,
// Void Rift
SPELL_VOID_RIFT = 73699,
// Chaos Portal
SPELL_DUMMY_AURA = 94562, // Using random dummy aura without effect - for Setesh to target the correct portal.
SPELL_NIGHTMARE_PORTAL_VISUAL = 76714,
SPELL_SUMMON_VOID_SENTINEL = 77242, // summons 41208
SPELL_SUMMON_VOID_SEEKER = 77271, // summons 41148
SPELL_SUMMON_VOID_WYRM_1 = 77272, // summons 41212
SPELL_SUMMON_VOID_WYRM_2 = 79439, // summons 41212
// Add Stalker
SPELL_CHANNEL_SUMMON_ADDS = 77607
};
enum Events
{
// Ammunae
EVENT_CHAOS_PORTAL = 1,
EVENT_CONTINUE_FIGHT,
EVENT_CHAOS_BLAST,
EVENT_SEED_OF_CHAOS,
EVENT_REIGN_OF_CHAOS,
// Chaos Portal
EVENT_CAST_VISUAL,
EVENT_SUMMON_WAVE_1,
EVENT_SUMMON_WAVE_2,
EVENT_SUMMON_WAVE_3,
EVENT_SUMMON_WAVE_RANDOM
};
enum Actions
{
ACTION_SETESH_ATTACK
};
enum Faction
{
FACTION_ENEMY_14 = 14,
FACTION_ENEMY_16 = 16
};
enum Phases
{
PHASE_FIGHT = 1,
PHASE_CHAOS_PORTAL = 2
};
enum Points
{
POINT_CHANNEL_CHAOS_PORTAL,
POINT_GROUND
};
enum Texts
{
SAY_DEATH = 0,
SAY_AGGRO = 1,
SAY_SPECIAL = 2,
SAY_PLAYER_KILL = 3
};
class NPCEntryPred : public std::unary_function<Unit*, bool>
{
public:
NPCEntryPred(uint32 entry) : _entry(entry) { }
bool operator()(Unit* unit) const { return unit->GetEntry() == _entry; }
private:
uint32 _entry;
};
// 39732 Setesh <Construct of Destruction>
class boss_setesh : public CreatureScript
{
public:
boss_setesh() : CreatureScript("boss_setesh") { }
struct boss_seteshAI : public BossAI
{
boss_seteshAI(Creature* creature) : BossAI(creature, DATA_SETESH) { }
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
Talk(SAY_AGGRO);
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);
events.ScheduleEvent(EVENT_CHAOS_PORTAL, Seconds(5));
events.ScheduleEvent(EVENT_CHAOS_BLAST, Seconds(15));
events.ScheduleEvent(EVENT_SEED_OF_CHAOS, Seconds(20));
events.ScheduleEvent(EVENT_REIGN_OF_CHAOS, Seconds(30));
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_PLAYER_KILL);
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
_EnterEvadeMode();
me->SetReactState(REACT_AGGRESSIVE);
CleanUp();
}
void JustDied(Unit* /*who*/) override
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
Talk(SAY_DEATH);
}
void JustSummoned(Creature* summon) override
{
BossAI::JustSummoned(summon);
switch (summon->GetEntry())
{
case NPC_SETESH_VOID_RIFT:
summon->CastSpell((Unit*)nullptr, SPELL_VOID_RIFT, true);
break;
default:
break;
}
}
void MovementInform(uint32 type, uint32 pointId) override
{
if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE)
return;
switch (pointId)
{
case POINT_CHANNEL_CHAOS_PORTAL:
if (Unit* npcChaosPortal = SelectTarget(SELECT_TARGET_NEAREST, 0, 0.0f, false, SPELL_DUMMY_AURA))
DoCast(npcChaosPortal, SPELL_CHANNEL_CHAOS_PORTAL);
events.ScheduleEvent(EVENT_CONTINUE_FIGHT, Seconds(5));
break;
default:
break;
}
}
void DoAction(int32 action) override
{
if (action != ACTION_SETESH_ATTACK)
return;
// Start attacking
me->SetReactState(REACT_AGGRESSIVE);
events.SetPhase(PHASE_FIGHT);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || !CheckInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING) || events.IsInPhase(PHASE_CHAOS_PORTAL))
return;
while(uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CHAOS_PORTAL:
StartChaosPortalPhase();
events.Repeat(Seconds(45));
break;
case EVENT_CHAOS_BLAST:
DoCast(SPELL_CHAOS_BLAST);
events.Repeat(Seconds(20));
break;
case EVENT_SEED_OF_CHAOS:
DoCast(SPELL_SEED_OF_CHAOS);
events.Repeat(Seconds(40));
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
// Journal: Setesh casts Chaos Bolt at random players instead of performing a melee attack.
if (events.IsInPhase(PHASE_CHAOS_PORTAL) || !me->isAttackReady())
return;
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f, true))
{
me->SetTarget(target->GetGUID());
DoCast(target, SPELL_CHAOS_BOLT);
me->resetAttackTimer();
}
}
private:
void StartChaosPortalPhase()
{
Unit* npcChaosPortal = SelectTarget(SELECT_TARGET_FARTHEST, 0, NPCEntryPred(NPC_SETESH_CHAOS_PORTAL));
if (!npcChaosPortal)
return;
events.SetPhase(PHASE_CHAOS_PORTAL);
me->SetReactState(REACT_PASSIVE);
me->InterruptNonMeleeSpells(true);
me->AttackStop();
Talk(SAY_SPECIAL);
// Walk towards chaos portal.
npcChaosPortal->CastSpell((Unit*)nullptr, SPELL_DUMMY_AURA, true);
me->GetMotionMaster()->MoveCloserAndStop(POINT_CHANNEL_CHAOS_PORTAL, npcChaosPortal, 18.0f);
}
void CleanUp()
{
std::list<Creature*> units;
GetCreatureListWithEntryInGrid(units, me, NPC_SETESH_CHAOS_BLAST, 200.0f);
for (std::list<Creature*>::iterator itr = units.begin(); itr != units.end(); ++itr)
(*itr)->DespawnOrUnsummon();
units.clear();
GetCreatureListWithEntryInGrid(units, me, NPC_SETESH_CHAOS_SEED, 200.0f);
for (std::list<Creature*>::iterator itr = units.begin(); itr != units.end(); ++itr)
(*itr)->DespawnOrUnsummon();
}
};
CreatureAI* GetAI(Creature* creature) const
{
return new boss_seteshAI(creature);
}
};
// 41041 Chaos Blast
class npc_setesh_chaos_blast : public CreatureScript
{
public:
npc_setesh_chaos_blast() : CreatureScript("npc_setesh_chaos_blast") { }
struct npc_setesh_chaos_blastAI : public ScriptedAI
{
npc_setesh_chaos_blastAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* setesh = instance->GetCreature(DATA_SETESH))
setesh->CastSpell(me, SPELL_CHAOS_BLAST_MISSLE, true);
}
void SpellHit(Unit* /*caster*/, const SpellInfo* spellInfo) override
{
if (spellInfo->Id != SPELL_CHAOS_BLAST_MISSLE)
return;
DoCastSelf(SPELL_CHAOS_BLAST_AURA);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_setesh_chaos_blastAI>(creature);
}
};
// 41126 Chaos Seed
class npc_setesh_chaos_seed : public CreatureScript
{
public:
npc_setesh_chaos_seed() : CreatureScript("npc_setesh_chaos_seed") { }
struct npc_setesh_chaos_seedAI : public ScriptedAI
{
npc_setesh_chaos_seedAI(Creature* creature) : ScriptedAI(creature)
{
me->SetDisableGravity(true);
me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 7.0f);
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 7.0f);
me->SetFaction(FACTION_FRIENDLY);
DoCastSelf(SPELL_SEED_OF_CHAOS_DUMMY);
}
void Reset() override
{
// Simulate slow fall
float x = me->GetPositionX();
float y = me->GetPositionY();
float z = me->GetPositionZ();
Position dest = { x, y, me->GetMap()->GetWaterOrGroundLevel(me->GetPhaseShift(), x, y, z) };
const uint32 pathSize = 1;
const Position path[pathSize] = { dest };
me->GetMotionMaster()->MoveSmoothPath(POINT_GROUND, path, pathSize, true, true);
}
void MovementInform(uint32 type, uint32 pointId) override
{
if (type != FLIGHT_MOTION_TYPE || pointId != POINT_GROUND)
return;
me->SetDisableGravity(false);
me->SetFaction(FACTION_ENEMY_14);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoCast(SPELL_SEED_OF_CHAOS_EXPLOSION);
me->RemoveAurasDueToSpell(SPELL_SEED_OF_CHAOS_DUMMY);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->DespawnOrUnsummon(Seconds(15));
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_setesh_chaos_seedAI>(creature);
}
};
// 41055 Chaos Portal - add waves:
// - Normal: 1) 2x Void Wyrm 2) Void Seeker 3) Void Sentinel
// - Heroic: 1) Void Sentinel 2) 2x Void Wyrm 3) Void Seeker 4+) Void Seeker or 2x Void Wyrm
class npc_setesh_chaos_portal : public CreatureScript
{
public:
npc_setesh_chaos_portal() : CreatureScript("npc_setesh_chaos_portal") { }
struct npc_setesh_chaos_portalAI : public ScriptedAI
{
npc_setesh_chaos_portalAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
events.Reset();
me->SetFaction(FACTION_ENEMY_14);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
void SpellHit(Unit* caster, const SpellInfo* spellInfo) override
{
if (spellInfo->Id != SPELL_CHANNEL_CHAOS_PORTAL)
return;
caster->InterruptNonMeleeSpells(true, SPELL_CHANNEL_CHAOS_PORTAL);
me->RemoveAurasDueToSpell(SPELL_DUMMY_AURA);
events.ScheduleEvent(EVENT_CAST_VISUAL, Seconds(1));
}
void JustDied(Unit* /*who*/) override
{
events.Reset();
me->SetRespawnTime(0);
}
void UpdateAI(uint32 diff) override
{
if (events.Empty())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CAST_VISUAL:
DoCast(SPELL_NIGHTMARE_PORTAL_VISUAL);
if (IsHeroic())
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_SUMMON_WAVE_1, Seconds(4));
break;
case EVENT_SUMMON_WAVE_1: // Also make Setesh attack.
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* setesh = instance->GetCreature(DATA_SETESH))
setesh->GetAI()->DoAction(ACTION_SETESH_ATTACK);
if (!IsHeroic()) // Normal
SummonVoidWyrms();
else // Heroic
SummonVoidSentinel();
events.ScheduleEvent(EVENT_SUMMON_WAVE_2, !IsHeroic() ? Seconds(12) : Seconds(6));
break;
case EVENT_SUMMON_WAVE_2:
if (!IsHeroic()) // Normal
SummonVoidSeeker();
else // Heroic
SummonVoidWyrms();
events.ScheduleEvent(EVENT_SUMMON_WAVE_3, !IsHeroic() ? Seconds(15) : Seconds(10));
break;
case EVENT_SUMMON_WAVE_3:
if (!IsHeroic()) // Normal
SummonVoidSentinel();
else // Heroic
SummonVoidSeeker();
// Normal: automatically closes after 3 waves of adds.
// Heroic: must be destroyed, summons more adds every 15 seconds after 3 waves.
if (!IsHeroic())
me->DespawnOrUnsummon(Seconds(5), Seconds(1));
else
events.ScheduleEvent(EVENT_SUMMON_WAVE_RANDOM, Seconds(15));
break;
case EVENT_SUMMON_WAVE_RANDOM: // Heroic only.
if (urand(0,1)) // 50% chance
SummonVoidSeeker();
else
SummonVoidWyrms();
events.Repeat(Seconds(15));
break;
default:
break;
}
}
}
private:
void SummonAreaDamage()
{
if (Unit* npcSeteshAddStalker = SelectTarget(SELECT_TARGET_RANDOM, 200.0f, NPCEntryPred(NPC_SETESH_ADD_STALKER)))
npcSeteshAddStalker->CastSpell(me, SPELL_CHANNEL_SUMMON_ADDS);
}
// Adds
void SummonVoidWyrms() { SummonAreaDamage(); DoCast(SPELL_SUMMON_VOID_WYRM_1); DoCast(SPELL_SUMMON_VOID_WYRM_2); }
void SummonVoidSeeker() { SummonAreaDamage(); DoCast(SPELL_SUMMON_VOID_SEEKER); }
void SummonVoidSentinel() { SummonAreaDamage(); DoCast(SPELL_SUMMON_VOID_SENTINEL); }
EventMap events;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_setesh_chaos_portalAI>(creature);
}
};
// 76674 Chaos Blast
class spell_setesh_chaos_blast : public SpellScriptLoader
{
public:
spell_setesh_chaos_blast() : SpellScriptLoader("spell_setesh_chaos_blast") { }
class spell_setesh_chaos_blast_SpellScript : public SpellScript
{
PrepareSpellScript(spell_setesh_chaos_blast_SpellScript);
void FilterTargets(std::list<WorldObject*>& unitList)
{
if (unitList.empty())
return;
Unit* caster = GetCaster();
unitList.remove_if([caster](WorldObject* player) { return caster->IsWithinDist(player, 15.0f); });
Trinity::Containers::RandomResize(unitList, 1);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_setesh_chaos_blast_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_setesh_chaos_blast_SpellScript();
}
};
void AddSC_boss_setesh()
{
new boss_setesh();
new npc_setesh_chaos_blast();
new npc_setesh_chaos_seed();
new npc_setesh_chaos_portal();
new spell_setesh_chaos_blast();
}

View File

@@ -15,62 +15,75 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* To-do: */
// - AreaTrigger: unknown purpose (id: 1843; pos: front-right stairs) - dust animation, aggro snakes?
#include "halls_of_origination.h"
#include "ScriptMgr.h"
#include "GridNotifiers.h"
#include "halls_of_origination.h"
#include "InstanceScript.h"
#include "Map.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "World.h"
enum Texts
{
SAY_AGGRO = 0,
SAY_SHIELD = 1,
EMOTE_SHIELD = 2,
EMOTE_UNSHIELD = 3,
SAY_KILL = 4,
SAY_DEATH = 5
SAY_AGGRO = 0,
SAY_SHIELD = 1,
EMOTE_SHIELD = 2,
EMOTE_UNSHIELD = 3,
SAY_KILL = 4,
SAY_DEATH = 5
};
enum Events
{
EVENT_DIVINE_RECKONING = 1,
EVENT_BURNING_LIGHT,
EVENT_SEAR,
EVENT_ACHIEVEMENT_FAIL,
EVENT_BURNING_LIGHT = 1,
EVENT_SEARING_LIGHT = 2,
EVENT_DIVINE_RECKONING = 3,
EVENT_CAST_SHIELD = 4,
EVENT_ACTIVATE_BEACONS = 5,
EVENT_CAST_BEAMS = 6,
EVENT_ACHIEVEMENT_FAILED = 7
};
enum Spells
{
SPELL_DIVINE_RECKONING = 75592,
SPELL_BURNING_LIGHT = 75115,
SPELL_REVERBERATING_HYMN = 75322,
SPELL_SHIELD_OF_LIGHT = 74938,
// Fight phase
SPELL_DIVINE_RECKONING = 75592,
SPELL_BURNING_LIGHT = 75115, // Forces victim to summon Searing Light dummy at its location.
SPELL_ACTIVATE_BEACONS = 76599,
SPELL_TELEPORT = 74969,
// Shield phase
SPELL_TELEPORT = 74969,
SPELL_SHIELD_OF_LIGHT = 74938,
SPELL_REVERBERATING_HYMN = 75322,
SPELL_ACTIVATE_BEACONS = 76599,
SPELL_DEACTIVATE_BEACONS = 76600,
SPELL_SHIELD_VISUAL_RIGHT = 83698,
SPELL_BEAM_OF_LIGHT_RIGHT = 76573,
// Cave In Stalker (eyes)
SPELL_BURNING_LIGHT_SEAR = 75194,
SPELL_SHIELD_VISUAL_LEFT = 83697,
SPELL_BEAM_OF_LIGHT_LEFT = 74930,
// Cave In Stalker (beacons)
SPELL_SHIELD_VISUAL_LEFT = 83697,
SPELL_SHIELD_VISUAL_RIGHT = 83698,
SPELL_BEAM_OF_LIGHT_LEFT = 74930,
SPELL_BEAM_OF_LIGHT_RIGHT = 76573
};
SPELL_SEARING_LIGHT = 75194,
enum Phases
{
PHASE_FIGHT = 1,
PHASE_SHIELD = 2
};
enum Actions
{
ACTION_DISABLE_BEACON = 1,
};
enum AnhuurAchievementData
{
DATA_I_HATE_THAT_SONG = 1,
ACTION_DISABLE_BEACON_L,
ACTION_DISABLE_BEACON_R,
ACTION_HYMN_EXPIRED
};
class boss_temple_guardian_anhuur : public CreatureScript
@@ -82,27 +95,67 @@ public:
{
boss_temple_guardian_anhuurAI(Creature* creature) : BossAI(creature, DATA_TEMPLE_GUARDIAN_ANHUUR)
{
_shieldCount = 0;
_beacons = 0;
_achievement = true;
Initialize();
}
uint8 _shieldCount;
uint8 _beacons;
bool _achievement;
void Initialize()
{
_countShield = 0;
_leftBeaconDisabled = false;
_rightBeaconDisabled = false;
}
void Reset() override
{
Initialize();
_Reset();
CleanStalkers();
me->MakeInterruptable(false);
me->RemoveAurasDueToSpell(SPELL_SHIELD_OF_LIGHT);
DoCastAOE(SPELL_DEACTIVATE_BEACONS, true);
me->SetReactState(REACT_AGGRESSIVE);
events.SetPhase(PHASE_FIGHT);
ScheduleEvents();
}
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
// Already in shield phase? 2 shields are enough.
if (events.IsInPhase(PHASE_SHIELD) || _countShield == 2)
return;
// About to die? One-hit cases...
if (int64(me->GetHealth()) - int64(damage) <= 0)
return;
// Shield phase happens at 66% and 33% health remaining.
if ((me->HealthBelowPctDamaged(66, damage) && _countShield == 0) ||
(me->HealthBelowPctDamaged(33, damage) && _countShield == 1))
EnterShieldPhase();
}
void DoAction(int32 action) override
{
if (action == ACTION_DISABLE_BEACON_L)
_leftBeaconDisabled = true;
else if (action == ACTION_DISABLE_BEACON_R)
_rightBeaconDisabled = true;
else if (action == ACTION_HYMN_EXPIRED) // We manually deactivate beacons.
DoCastAOE(SPELL_DEACTIVATE_BEACONS, true);
// Exit shield phase if both beacons are disabled or channeling Reverberating Hymn finished.
if ((_leftBeaconDisabled && _rightBeaconDisabled) || action == ACTION_HYMN_EXPIRED)
ExitShieldPhase();
}
void JustEngagedWith(Unit* /*who*/) override
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);
Talk(SAY_AGGRO);
_JustEngagedWith();
events.ScheduleEvent(EVENT_DIVINE_RECKONING, urand(10000, 12000));
events.ScheduleEvent(EVENT_BURNING_LIGHT, 12000);
instance->DoUpdateWorldState(WS_I_HATE_THIS_SONG, 0);
sWorld->setWorldState(WS_I_HATE_THIS_SONG, 0); // To-do: make InstanceScript::DoUpdateWorldState do the World::setWorldState.
}
void JustDied(Unit* /*killer*/) override
@@ -118,115 +171,26 @@ public:
Talk(SAY_KILL);
}
void EnterEvadeMode(EvadeReason /*why*/) override
void EnterEvadeMode(EvadeReason why) override
{
_EnterEvadeMode();
CleanStalkers();
RespawnPit();
_shieldCount = 0;
_beacons = 0;
_achievement = true;
DoCastAOE(SPELL_DEACTIVATE_BEACONS, true);
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
me->GetMotionMaster()->MoveTargetedHome();
events.Reset();
_EnterEvadeMode();
_DespawnAtEvade();
}
void RespawnPit()
void OnSpellCastInterrupt(SpellInfo const* spell) override
{
std::list<Creature*> snakes;
GetCreatureListWithEntryInGrid(snakes, me, NPC_PIT_SNAKE, 500.0f);
for (std::list<Creature*>::iterator itr = snakes.begin(); itr != snakes.end(); ++itr)
me->MakeInterruptable(false);
if (spell->Id == SPELL_REVERBERATING_HYMN)
{
Position const pos = (*itr)->GetHomePosition();
(*itr)->Respawn();
(*itr)->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
events.CancelEvent(EVENT_ACHIEVEMENT_FAILED);
DoAction(ACTION_HYMN_EXPIRED);
ScheduleEvents();
}
}
void CleanStalkers()
{
std::list<Creature*> stalkers;
GetCreatureListWithEntryInGrid(stalkers, me, NPC_CAVE_IN_STALKER, 100.0f);
for (std::list<Creature*>::iterator itr = stalkers.begin(); itr != stalkers.end(); ++itr)
{
(*itr)->RemoveAurasDueToSpell(SPELL_BEAM_OF_LIGHT_RIGHT);
(*itr)->RemoveAurasDueToSpell(SPELL_BEAM_OF_LIGHT_LEFT);
}
}
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) override
{
if ((me->HealthBelowPct(66) && _shieldCount == 0) ||
(me->HealthBelowPct(33) && _shieldCount == 1))
{
_shieldCount++;
_beacons = 2;
me->InterruptNonMeleeSpells(true);
me->AttackStop();
DoCast(me, SPELL_TELEPORT);
DoCastAOE(SPELL_ACTIVATE_BEACONS);
DoCast(me, SPELL_REVERBERATING_HYMN);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_31);
RespawnPit();
std::list<Creature*> stalkers;
GameObject* door = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_ANHUUR_DOOR));
GetCreatureListWithEntryInGrid(stalkers, me, NPC_CAVE_IN_STALKER, 100.0f);
stalkers.remove_if(Trinity::HeightDifferenceCheck(door, 0.0f, false)); // Target only the bottom ones
for (std::list<Creature*>::iterator itr = stalkers.begin(); itr != stalkers.end(); ++itr)
{
if ((*itr)->GetPositionX() > door->GetPositionX())
{
(*itr)->CastSpell((*itr), SPELL_SHIELD_VISUAL_LEFT, true);
(*itr)->CastSpell((*itr), SPELL_BEAM_OF_LIGHT_LEFT, true);
}
else
{
(*itr)->CastSpell((*itr), SPELL_SHIELD_VISUAL_RIGHT, true);
(*itr)->CastSpell((*itr), SPELL_BEAM_OF_LIGHT_RIGHT, true);
}
}
Talk(EMOTE_SHIELD);
Talk(SAY_SHIELD);
events.CancelEvent(EVENT_DIVINE_RECKONING);
events.CancelEvent(EVENT_BURNING_LIGHT);
events.ScheduleEvent(EVENT_ACHIEVEMENT_FAIL, 15000);
}
else if (me->HasAura(SPELL_REVERBERATING_HYMN) && !me->HasAura(SPELL_SHIELD_OF_LIGHT))
{
me->RemoveAurasDueToSpell(SPELL_REVERBERATING_HYMN);
events.CancelEvent(EVENT_ACHIEVEMENT_FAIL);
events.ScheduleEvent(EVENT_DIVINE_RECKONING, urand(10000, 12000));
events.ScheduleEvent(EVENT_BURNING_LIGHT, 12000);
}
}
void DoAction(int32 action) override
{
if (action == ACTION_DISABLE_BEACON)
{
--_beacons;
if (!_beacons)
{
me->RemoveAura(SPELL_SHIELD_OF_LIGHT);
Talk(EMOTE_UNSHIELD);
}
}
}
uint32 GetData(uint32 type) const override
{
if (type == DATA_I_HATE_THAT_SONG)
return _achievement ? 1 : 0;
return 0;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || !CheckInRoom())
@@ -234,60 +198,157 @@ public:
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING) && !events.IsInPhase(PHASE_SHIELD))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_DIVINE_RECKONING:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0, true, 0))
DoCast(target, SPELL_DIVINE_RECKONING);
events.ScheduleEvent(EVENT_DIVINE_RECKONING, urand(10000, 12000));
break;
case EVENT_BURNING_LIGHT:
{
Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me));
if (!unit)
unit = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true);
DoCast(unit, SPELL_BURNING_LIGHT);
events.ScheduleEvent(EVENT_SEAR, 2000);
events.ScheduleEvent(EVENT_BURNING_LIGHT, 12000);
events.ScheduleEvent(EVENT_SEARING_LIGHT, Seconds(2)); // No phase.
break;
}
case EVENT_SEAR:
{
Unit* target = me->FindNearestCreature(NPC_SEARING_LIGHT, 100.0f);
if (!target)
break;
std::list<Creature*> stalkers;
GetCreatureListWithEntryInGrid(stalkers, me, NPC_CAVE_IN_STALKER, 100.0f);
stalkers.remove_if(Trinity::HeightDifferenceCheck(ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_ANHUUR_DOOR)), 5.0f, true));
if (stalkers.empty())
break;
stalkers.sort(Trinity::ObjectDistanceOrderPred(target));
// Get the closest statue face (any of its eyes)
Creature* eye1 = stalkers.front();
stalkers.remove(eye1); // Remove the eye.
stalkers.sort(Trinity::ObjectDistanceOrderPred(eye1)); // Find the second eye.
Creature* eye2 = stalkers.front();
eye1->CastSpell(eye1, SPELL_SEARING_LIGHT, true);
eye2->CastSpell(eye2, SPELL_SEARING_LIGHT, true);
case EVENT_SEARING_LIGHT:
HandleSearingLight();
events.ScheduleEvent(EVENT_BURNING_LIGHT, Seconds(10), 0, PHASE_FIGHT);
break;
}
case EVENT_ACHIEVEMENT_FAIL:
_achievement = false;
case EVENT_DIVINE_RECKONING:
DoCastVictim(SPELL_DIVINE_RECKONING);
events.ScheduleEvent(EVENT_DIVINE_RECKONING, Seconds(10), 0, PHASE_FIGHT);
break;
case EVENT_CAST_SHIELD:
me->AddUnitMovementFlag(MOVEMENTFLAG_ROOT);
me->SetFacingTo(1.5708f); // Sniffs set it again
DoCastSelf(SPELL_SHIELD_OF_LIGHT); // Note: stun!
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_31);
events.ScheduleEvent(EVENT_ACTIVATE_BEACONS, Seconds(2), 0, PHASE_SHIELD);
break;
case EVENT_ACTIVATE_BEACONS:
Talk(EMOTE_SHIELD);
DoCastAOE(SPELL_ACTIVATE_BEACONS, true);
DoCastSelf(SPELL_REVERBERATING_HYMN);
HandleVisuals(SPELL_SHIELD_VISUAL_LEFT, SPELL_SHIELD_VISUAL_RIGHT, true);
events.ScheduleEvent(EVENT_CAST_BEAMS, Seconds(1), 0, PHASE_SHIELD);
events.ScheduleEvent(EVENT_ACHIEVEMENT_FAILED, Seconds(15), 0, PHASE_SHIELD);
break;
case EVENT_CAST_BEAMS:
HandleVisuals(SPELL_BEAM_OF_LIGHT_LEFT, SPELL_BEAM_OF_LIGHT_RIGHT, true);
break;
case EVENT_ACHIEVEMENT_FAILED: // Happens on normal too, heroic check is in dbc.
instance->DoUpdateWorldState(WS_I_HATE_THIS_SONG, 1);
sWorld->setWorldState(WS_I_HATE_THIS_SONG, 1); // To-do: make InstanceScript::DoUpdateWorldState do the World::setWorldState.
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
DoMeleeAttackIfReady();
if (!events.IsInPhase(PHASE_SHIELD))
DoMeleeAttackIfReady();
}
private:
void ScheduleEvents()
{
events.Reset();
events.ScheduleEvent(EVENT_DIVINE_RECKONING, Seconds(10), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_BURNING_LIGHT, Seconds(12), 0, PHASE_FIGHT);
}
// To-do: Ideal for a custom spell.
void HandleSearingLight()
{
Unit* target = me->FindNearestCreature(NPC_SEARING_LIGHT, 100.0f);
GameObject* door = instance->GetGameObject(DATA_ANHUUR_DOOR);
if (!target || !door)
return;
std::list<Creature*> stalkers;
GetCreatureListWithEntryInGrid(stalkers, me, NPC_CAVE_IN_STALKER, 100.0f);
stalkers.remove_if(Trinity::HeightDifferenceCheck(door, 5.0f, true));
if (stalkers.empty())
return;
stalkers.sort(Trinity::ObjectDistanceOrderPred(target));
// Get the closest statue face (any of its eyes)
Creature* eye1 = stalkers.front();
stalkers.remove(eye1); // Remove the eye.
stalkers.sort(Trinity::ObjectDistanceOrderPred(eye1)); // Find the second eye.
Creature* eye2 = stalkers.front();
eye1->CastSpell(eye1, SPELL_BURNING_LIGHT_SEAR, true);
eye2->CastSpell(eye2, SPELL_BURNING_LIGHT_SEAR, true);
return;
}
void EnterShieldPhase()
{
events.SetPhase(PHASE_SHIELD);
++_countShield;
_leftBeaconDisabled = false;
_rightBeaconDisabled = false;
me->SetReactState(REACT_PASSIVE);
me->InterruptNonMeleeSpells(true);
me->AttackStop();
DoCastSelf(SPELL_TELEPORT);
me->SetFacingTo(1.5708f, true); // Note: Wrong orientation in old sniffs - 5.144157f.
Talk(SAY_SHIELD);
events.ScheduleEvent(EVENT_CAST_SHIELD, Seconds(1), 0, PHASE_SHIELD);
}
void ExitShieldPhase()
{
CleanStalkers();
me->MakeInterruptable(true);
me->RemoveAurasDueToSpell(SPELL_SHIELD_OF_LIGHT);
me->SetReactState(REACT_AGGRESSIVE);
Talk(EMOTE_UNSHIELD);
events.SetPhase(PHASE_FIGHT);
}
void CleanStalkers()
{
HandleVisuals(SPELL_SHIELD_VISUAL_LEFT, SPELL_SHIELD_VISUAL_RIGHT, false);
HandleVisuals(SPELL_BEAM_OF_LIGHT_LEFT, SPELL_BEAM_OF_LIGHT_RIGHT, false);
}
void HandleVisuals(uint32 leftSpellId, uint32 rightSpellId, bool apply)
{
std::list<Creature*> stalkers;
GetCreatureListWithEntryInGrid(stalkers, me, NPC_CAVE_IN_STALKER, 100.0f);
for (std::list<Creature*>::iterator itr = stalkers.begin(); itr != stalkers.end(); ++itr)
{
// Target only the bottom stalkers (Y: 65.392f and 64.9004f)
if ((*itr)->GetPositionZ() > 70.f)
return;
// Left stalker X: -603.465f; right stalker X: -678.132f
uint32 spellId = ((*itr)->GetPositionX() > -640.0f) ? leftSpellId : rightSpellId;
if (apply)
(*itr)->CastSpell((*itr), spellId, true);
else
{
(*itr)->InterruptNonMeleeSpells(true, spellId);
(*itr)->RemoveAurasDueToSpell(spellId);
}
}
}
uint8 _countShield;
bool _leftBeaconDisabled;
bool _rightBeaconDisabled;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -296,6 +357,44 @@ public:
}
};
// 75592 - Divine Reckoning
class spell_anhuur_divine_reckoning : public SpellScriptLoader
{
public:
spell_anhuur_divine_reckoning() : SpellScriptLoader("spell_anhuur_divine_reckoning") { }
class spell_anhuur_divine_reckoning_AuraScript : public AuraScript
{
PrepareAuraScript(spell_anhuur_divine_reckoning_AuraScript);
void OnPeriodic(AuraEffect const* aurEff)
{
if (Unit* caster = GetCaster())
{
if (!caster->isDead())
{
CustomSpellValues values;
values.AddSpellMod(SPELLVALUE_BASE_POINT0, aurEff->GetAmount());
caster->CastCustomSpell(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, values, GetTarget());
}
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_anhuur_divine_reckoning_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_anhuur_divine_reckoning_AuraScript();
}
};
// 74930 - Shield of Light (left)
// 76573 - Shield of Light (right)
class spell_anhuur_shield_of_light : public SpellScriptLoader
{
public:
@@ -307,17 +406,14 @@ class spell_anhuur_shield_of_light : public SpellScriptLoader
void FilterTargets(std::list<WorldObject*>& targets)
{
if (InstanceMap* instance = GetCaster()->GetMap()->ToInstanceMap())
if (InstanceScript* instance = GetCaster()->GetInstanceScript())
{
if (InstanceScript* const script = instance->GetInstanceScript())
if (GameObject* go = instance->GetGameObject(DATA_ANHUUR_DOOR))
{
if (GameObject* go = ObjectAccessor::GetGameObject(*GetCaster(), script->GetGuidData(DATA_ANHUUR_DOOR)))
{
targets.remove_if(Trinity::HeightDifferenceCheck(go, 5.0f, false));
targets.remove(GetCaster());
targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster()));
targets.resize(2);
}
targets.remove_if(Trinity::HeightDifferenceCheck(go, 5.0f, false));
targets.remove(GetCaster());
targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster()));
targets.resize(2);
}
}
}
@@ -334,6 +430,37 @@ class spell_anhuur_shield_of_light : public SpellScriptLoader
}
};
// 75322 - Reverberating Hymn
class spell_anhuur_reverberating_hymn : public SpellScriptLoader
{
public:
spell_anhuur_reverberating_hymn() : SpellScriptLoader("spell_anhuur_reverberating_hymn") { }
class spell_anhuur_reverberating_hymn_AuraScript : public AuraScript
{
PrepareAuraScript(spell_anhuur_reverberating_hymn_AuraScript);
void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
if (Creature* anhuur = GetCaster()->ToCreature())
anhuur->AI()->DoAction(ACTION_HYMN_EXPIRED);
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_anhuur_reverberating_hymn_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_anhuur_reverberating_hymn_AuraScript();
}
};
// 76606 - Disable Beacon Beams L
// 76608 - Disable Beacon Beams R
class spell_anhuur_disable_beacon_beams : public SpellScriptLoader
{
public:
@@ -345,15 +472,14 @@ class spell_anhuur_disable_beacon_beams : public SpellScriptLoader
void HandleScript(SpellEffIndex /*effIndex*/)
{
GetHitUnit()->RemoveAurasDueToSpell(GetEffectValue());
GetHitUnit()->InterruptNonMeleeSpells(true, GetEffectValue());
}
void Notify(SpellEffIndex /*index*/)
{
if (InstanceMap* instance = GetCaster()->GetMap()->ToInstanceMap())
if (InstanceScript* const script = instance->GetInstanceScript())
if (Creature* anhuur = instance->GetCreature(script->GetGuidData(DATA_ANHUUR_GUID)))
anhuur->AI()->DoAction(ACTION_DISABLE_BEACON);
if (InstanceScript* instance = GetCaster()->GetInstanceScript())
if (Creature* anhuur = instance->GetCreature(DATA_TEMPLE_GUARDIAN_ANHUUR))
anhuur->AI()->DoAction(GetEffectValue() == SPELL_BEAM_OF_LIGHT_LEFT ? ACTION_DISABLE_BEACON_L : ACTION_DISABLE_BEACON_R);
}
void Register() override
@@ -369,84 +495,52 @@ class spell_anhuur_disable_beacon_beams : public SpellScriptLoader
}
};
class spell_anhuur_activate_beacons : public SpellScriptLoader
// 76599 - Activate Beacons
// 76600 - Deactivate Beacons
// Temporary script until Spell::EffectActivateObject is scripted.
class spell_anhuur_handle_beacons : public SpellScriptLoader
{
public:
spell_anhuur_activate_beacons() : SpellScriptLoader("spell_anhuur_activate_beacons") { }
spell_anhuur_handle_beacons() : SpellScriptLoader("spell_anhuur_handle_beacons") { }
class spell_anhuur_activate_beacons_SpellScript : public SpellScript
class spell_anhuur_handle_beacons_SpellScript : public SpellScript
{
PrepareSpellScript(spell_anhuur_activate_beacons_SpellScript);
PrepareSpellScript(spell_anhuur_handle_beacons_SpellScript);
void Activate(SpellEffIndex index)
void HandleEffect(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(index);
GetHitGObj()->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
PreventHitDefaultEffect(effIndex);
int32 script = GetSpellInfo()->Effects[effIndex].MiscValue;
GameObject* beacon = GetHitGObj();
if (!beacon)
return;
beacon->SetLootState(GO_READY);
if (script == 16) // 16 => Disable
beacon->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
else if (script == 17) // 17 => Activate
beacon->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_anhuur_activate_beacons_SpellScript::Activate, EFFECT_0, SPELL_EFFECT_ACTIVATE_OBJECT);
OnEffectHitTarget += SpellEffectFn(spell_anhuur_handle_beacons_SpellScript::HandleEffect, EFFECT_0, SPELL_EFFECT_ACTIVATE_OBJECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_anhuur_activate_beacons_SpellScript();
}
};
class spell_anhuur_divine_reckoning : public SpellScriptLoader
{
public:
spell_anhuur_divine_reckoning() : SpellScriptLoader("spell_anhuur_divine_reckoning") { }
class spell_anhuur_divine_reckoning_AuraScript : public AuraScript
{
PrepareAuraScript(spell_anhuur_divine_reckoning_AuraScript);
void OnPeriodic(AuraEffect const* aurEff)
{
if (Unit* caster = GetCaster())
{
CustomSpellValues values;
values.AddSpellMod(SPELLVALUE_BASE_POINT0, aurEff->GetAmount());
caster->CastCustomSpell(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, values, GetTarget());
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_anhuur_divine_reckoning_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_anhuur_divine_reckoning_AuraScript();
}
};
class achievement_hate_that_song : public AchievementCriteriaScript
{
public:
achievement_hate_that_song() : AchievementCriteriaScript("achievement_hate_that_song") { }
bool OnCheck(Player* /*source*/, Unit* target) override
{
if (!target || !target->IsAIEnabled)
return false;
return target->GetMap()->IsHeroic() && target->GetAI()->GetData(DATA_I_HATE_THAT_SONG);
return new spell_anhuur_handle_beacons_SpellScript();
}
};
void AddSC_boss_temple_guardian_anhuur()
{
new boss_temple_guardian_anhuur();
new spell_anhuur_shield_of_light();
new spell_anhuur_disable_beacon_beams();
new spell_anhuur_activate_beacons();
new spell_anhuur_divine_reckoning();
new achievement_hate_that_song();
new spell_anhuur_shield_of_light();
new spell_anhuur_reverberating_hymn();
new spell_anhuur_disable_beacon_beams();
new spell_anhuur_handle_beacons();
}

View File

@@ -0,0 +1,429 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
*
* 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/>.
*/
/* To-do: */
// - Summoner Enabler dummy npc: unknown purpose (at the entrance)
// - AreaTrigger: unknown purpose (id: 2275; pos: hole in the left wall at the entrance)
// - Find out what Dummy Nuke (68991) spell does.
// - Spatial Flux won't enter combat on second aggro from creature group (but it should).
#include "ScriptMgr.h"
#include "CreatureGroups.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "GridNotifiers.h"
#include "InstanceScript.h"
#include "PassiveAI.h"
#include "ScriptedGossip.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "TemporarySummon.h"
#include "halls_of_origination.h"
enum Spells
{
SPELL_SUBMERGE = 76084,
// Isiset trash and adds
SPELL_ARCANE_ENERGY = 74881,
SPELL_ARCANE_BURST = 74888, // On retail not working! Should probably be cast at full energy.
SPELL_SPAWN_ENERGY_FLUX_TRASH = 82382, // Makes random player cast Summon Energy Flux (82385)
SPELL_SPAWN_ENERGY_FLUX_ISISET = 90735, // Makes random player cast Summon Energy Flux (90739)
SPELL_ENERGY_FLUX_PERIODIC = 74044,
SPELL_ENERGY_FLUX_BEAM_TRASH = 82377, // Makes nearby Spatial Flux cast visual beam
SPELL_ENERGY_FLUX_BEAM_ISISET = 90741, // Makes nearby Spatial Flux cast visual beam
SPELL_DUMMY_NUKE = 68991
};
enum Events
{
// Spatial Flux
EVENT_SPAWN_ENERGY_FLUX = 1,
EVENT_DUMMY_NUKE,
// Energy Flux
EVENT_FOLLOW_SUMMONER,
};
// The Maker's Lift
enum ElevatorMisc
{
GOSSIP_MENU_HOO_LIFT = 12646,
GOSSIP_NPC_TEXT_CHOOSE_A_DESTINATION = 17791,
GOSSIP_OPTION_FIRST_FLOOR = 0,
GOSSIP_OPTION_HOO_LIFT_SECOND_FLOOR = 1,
GOSSIP_OPTION_HOO_LIFT_THIRD_FLOOR = 2
};
// 207669 - The Maker's Lift Controller
class go_hoo_the_makers_lift_controller : public GameObjectScript
{
public:
go_hoo_the_makers_lift_controller() : GameObjectScript("go_hoo_the_makers_lift_controller") { }
struct go_hoo_the_makers_lift_controllerAI : public GameObjectAI
{
go_hoo_the_makers_lift_controllerAI(GameObject* go) : GameObjectAI(go) { }
bool GossipHello(Player* player) override
{
InstanceScript* instance = player->GetInstanceScript();
if (!instance)
return false;
// Build menu.
// First floor: Option available from start.
AddGossipItemFor(player, GOSSIP_MENU_HOO_LIFT, GOSSIP_OPTION_FIRST_FLOOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 0);
// Second floor: Anraphet must be defeated first.
if (instance->GetBossState(DATA_ANRAPHET) == DONE)
AddGossipItemFor(player, GOSSIP_MENU_HOO_LIFT, GOSSIP_OPTION_HOO_LIFT_SECOND_FLOOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
// Third floor: Constructs of The Four Seats must be defeated first.
if (instance->GetBossState(DATA_ISISET) == DONE && instance->GetBossState(DATA_AMMUNAE) == DONE &&
instance->GetBossState(DATA_SETESH) == DONE && instance->GetBossState(DATA_RAJH) == DONE)
AddGossipItemFor(player, GOSSIP_MENU_HOO_LIFT, GOSSIP_OPTION_HOO_LIFT_THIRD_FLOOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, GOSSIP_NPC_TEXT_CHOOSE_A_DESTINATION, me->GetGUID());
return true;
}
bool GossipSelect(Player* player, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
player->PlayerTalkClass->SendCloseGossip();
InstanceScript* instance = player->GetInstanceScript();
if (!instance)
return true;
// Handle elevator: gossip item index => stopFrame (floor index).
uint32 stopFrame = action - GOSSIP_ACTION_INFO_DEF;
GameObject* elevator = instance->GetGameObject(DATA_LIFT_OF_THE_MAKERS);
if (!elevator)
return true;
elevator->SetTransportState(GO_STATE_TRANSPORT_ACTIVE);
elevator->SetTransportState(GO_STATE_TRANSPORT_STOPPED, stopFrame);
return true;
}
};
GameObjectAI* GetAI(GameObject* go) const override
{
return GetHallsOfOriginationAI<go_hoo_the_makers_lift_controllerAI>(go);
}
};
// 40790 Aggro Stalker
class npc_hoo_aggro_stalker : public CreatureScript
{
public:
npc_hoo_aggro_stalker() : CreatureScript("npc_hoo_aggro_stalker") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_hoo_aggro_stalkerAI(creature);
}
struct npc_hoo_aggro_stalkerAI : public PassiveAI
{
npc_hoo_aggro_stalkerAI(Creature* creature) : PassiveAI(creature)
{
me->SearchFormation();
}
void MoveInLineOfSight(Unit* who) override
{
if (who && who->GetTypeId() == TYPEID_PLAYER && me->IsWithinDistInMap(who, 30.f))
if (CreatureGroup* formation = me->GetFormation())
formation->MemberAttackStart(me, who);
me->CombatStop();
}
};
};
// 39612 - Spatial Flux (trash)
// 48707 - Spatial Flux (Isiset)
class npc_hoo_spatial_flux : public CreatureScript
{
public:
npc_hoo_spatial_flux() : CreatureScript("npc_hoo_spatial_flux") { }
struct npc_hoo_spatial_fluxAI : public ScriptedAI
{
npc_hoo_spatial_fluxAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
events.Reset();
events.ScheduleEvent(EVENT_DUMMY_NUKE, Seconds(0));
events.ScheduleEvent(EVENT_SPAWN_ENERGY_FLUX, Seconds(3));
}
void IsSummonedBy(Unit* summoner) override
{
if (summoner->GetEntry() == BOSS_ISISET)
me->SetInCombatWithZone();
}
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_SPAWN_ENERGY_FLUX:
DoCastSelf(me->GetEntry() == NPC_SPATIAL_FLUX_TRASH ? SPELL_SPAWN_ENERGY_FLUX_TRASH : SPELL_SPAWN_ENERGY_FLUX_ISISET);
events.Repeat(Seconds(12));
break;
case EVENT_DUMMY_NUKE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true))
DoCast(target, SPELL_DUMMY_NUKE);
events.Repeat(Seconds(1));
break;
default:
break;
}
}
}
private:
EventMap events;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_hoo_spatial_fluxAI>(creature);
}
};
// 44015 - Energy flux (trash)
// 48709 - Energy flux (Isiset)
class npc_hoo_energy_flux : public CreatureScript
{
public:
npc_hoo_energy_flux() : CreatureScript("npc_hoo_energy_flux") { }
struct npc_hoo_energy_fluxAI : public ScriptedAI
{
npc_hoo_energy_fluxAI(Creature* creature) : ScriptedAI(creature)
{
me->SetReactState(REACT_PASSIVE);
DoCastSelf(SPELL_ENERGY_FLUX_PERIODIC);
DoCastSelf(me->GetEntry() == NPC_ENERGY_FLUX_TRASH ? SPELL_ENERGY_FLUX_BEAM_TRASH : SPELL_ENERGY_FLUX_BEAM_ISISET);
}
void IsSummonedBy(Unit* /*summoner*/) override
{
me->SetWalk(true);
events.ScheduleEvent(EVENT_FOLLOW_SUMMONER, Seconds(1));
me->DespawnOrUnsummon(Seconds(6));
}
void UpdateAI(uint32 diff) override
{
if (events.Empty())
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_FOLLOW_SUMMONER:
if (Unit* target = ObjectAccessor::GetUnit(*me, me->GetCreatorGUID()))
me->GetMotionMaster()->MovePoint(0, target->GetPosition(), true);
events.Repeat(Seconds(1));
break;
default:
break;
}
}
}
private:
EventMap events;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetHallsOfOriginationAI<npc_hoo_energy_fluxAI>(creature);
}
};
// 75764 - Emerge
class spell_hoo_emerge : public SpellScriptLoader
{
public:
spell_hoo_emerge() : SpellScriptLoader("spell_hoo_emerge") { }
class spell_hoo_emerge_SpellScript : public SpellScript
{
PrepareSpellScript(spell_hoo_emerge_SpellScript);
void RemoveSubmergeAura(SpellEffIndex /*effIndex*/)
{
GetHitUnit()->RemoveAurasDueToSpell(SPELL_SUBMERGE);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_hoo_emerge_SpellScript::RemoveSubmergeAura, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_hoo_emerge_SpellScript();
}
};
// 82382 - Energy Flux (cast by trash Spatial Flux)
// 90735 - Energy Flux (cast by Isiset's Spatial Flux)
class spell_hoo_energy_flux_target_selector : public SpellScriptLoader
{
public:
spell_hoo_energy_flux_target_selector() : SpellScriptLoader("spell_hoo_energy_flux_target_selector") { }
class spell_hoo_energy_flux_target_selector_SpellScript : public SpellScript
{
PrepareSpellScript(spell_hoo_energy_flux_target_selector_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
// Remove tank
if (InstanceScript* instance = GetCaster()->GetInstanceScript())
if (Creature* Isiset = instance->GetCreature(DATA_ISISET))
if (WorldObject* tank = Isiset->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO))
targets.remove(tank);
targets.remove_if(Trinity::ObjectTypeIdCheck(TYPEID_PLAYER, false));
if (targets.empty())
return;
WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets);
targets.clear();
targets.push_back(target);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hoo_energy_flux_target_selector_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_hoo_energy_flux_target_selector_SpellScript();
}
};
// 74880 - Arcane Energy
class spell_hoo_arcane_energy_check : public SpellScriptLoader
{
public:
spell_hoo_arcane_energy_check() : SpellScriptLoader("spell_hoo_arcane_energy_check") { }
class spell_hoo_arcane_energy_check_AuraScript : public AuraScript
{
PrepareAuraScript(spell_hoo_arcane_energy_check_AuraScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_ARCANE_BURST, SPELL_ARCANE_ENERGY });
}
void AfterProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/)
{
if (GetTarget()->GetPower(POWER_ENERGY) == 100)
{
GetTarget()->CastSpell((Unit*)nullptr, SPELL_ARCANE_BURST);
// Stacks should probably be consumed, right? (note: this ability doesn't work on retail)
GetTarget()->RemoveAurasDueToSpell(SPELL_ARCANE_ENERGY);
GetTarget()->SetPower(POWER_ENERGY, 0);
}
}
void Register() override
{
AfterEffectProc += AuraEffectProcFn(spell_hoo_arcane_energy_check_AuraScript::AfterProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_hoo_arcane_energy_check_AuraScript();
}
};
// 73686 Fixate
class spell_hoo_fixate : public SpellScriptLoader
{
public:
spell_hoo_fixate() : SpellScriptLoader("spell_hoo_fixate") { }
class spell_hoo_fixate_SpellScript : public SpellScript
{
PrepareSpellScript(spell_hoo_fixate_SpellScript);
bool Validate(SpellInfo const* spellInfo) override
{
return ValidateSpellInfo({ uint32(spellInfo->Effects[EFFECT_0].BasePoints) });
}
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
GetHitUnit()->CastSpell(GetCaster(), uint32(GetEffectValue()));
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_hoo_fixate_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_hoo_fixate_SpellScript();
}
};
void AddSC_halls_of_origination()
{
new go_hoo_the_makers_lift_controller();
new npc_hoo_aggro_stalker();
new npc_hoo_spatial_flux();
new npc_hoo_energy_flux();
new spell_hoo_emerge();
new spell_hoo_energy_flux_target_selector();
new spell_hoo_arcane_energy_check();
new spell_hoo_fixate();
}

View File

@@ -42,16 +42,37 @@ enum HOOData
DATA_RAJH,
// Temple Guardian Anhuur
DATA_ANHUUR_GUID,
DATA_ANHUUR_LEFT_BEACON,
DATA_ANHUUR_RIGHT_BEACON,
DATA_ANHUUR_BRIDGE,
DATA_ANHUUR_DOOR,
// Anraphet
DATA_BRANN_0_GUID,
DATA_BRANN_0,
DATA_VAULT_OF_LIGHTS_DOOR,
DATA_DEAD_ELEMENTALS,
DATA_ANRAPHET_GUID,
DATA_LIGHTMACHINE_EARTH,
DATA_LIGHTMACHINE_AIR,
DATA_LIGHTMACHINE_FIRE,
DATA_LIGHTMACHINE_WATER,
DATA_LASERBEAMS_EARTH,
DATA_LASERBEAMS_AIR,
DATA_LASERBEAMS_FIRE,
DATA_LASERBEAMS_WATER,
DATA_UPDATE_LASERBEAMS,
DATA_ANRAPHET_SUN_MIRROR,
DATA_ANRAPHET_DOOR,
// Isiset
DATA_ISISET_PHASE,
DATA_ISISET_ASTRAL_RAIN_ALIVE,
DATA_ISISET_CELESTIAL_CALL_ALIVE,
DATA_ISISET_VEIL_OF_SKY_ALIVE,
// Ammunae
// Setesh
DATA_SETESH_ADD_STALKER,
// Misc
DATA_LIFT_OF_THE_MAKERS
};
enum HOOCreatures
@@ -59,13 +80,13 @@ enum HOOCreatures
BOSS_TEMPLE_GUARDIAN_ANHUUR = 39425,
NPC_CAVE_IN_STALKER = 40183,
NPC_SEARING_LIGHT = 40283,
NPC_PIT_SNAKE = 39444,
BOSS_EARTHRAGER_PTAH = 39428,
NPC_BEETLE_STALKER = 40459, // Summons both Jeweled Scarab and Dustbone Horror
NPC_JEWELED_SCARAB = 40458,
NPC_DUSTBONE_HORROR = 40450,
NPC_QUICKSAND = 40503, // Summoned by a spell not in dbc (75550)
NPC_JEWELED_SCARAB = 40458, // Summoned by spell: 75462 Summon Jeweled Scarab
NPC_DUSTBONE_HORROR = 40450, // Summoned by spell: 75521 Summon Dustbone Horror
NPC_QUICKSAND = 40503, // Summoned by spell: 75550 (server-side, not in dbc)
NPC_HOO_CAMEL = 39443,
BOSS_ANRAPHET = 39788,
NPC_FIRE_WARDEN = 39800,
@@ -73,58 +94,134 @@ enum HOOCreatures
NPC_WATER_WARDEN = 39802,
NPC_AIR_WARDEN = 39803,
BOSS_RAJH = 39378,
NPC_INFERNO_LEAP = 47040,
NPC_BLAZING_INFERNO = 40927,
WARDEN_ENTRY_MAX_COUNT = 4,
WARDEN_ENTRY_DATA_DELTA = NPC_FIRE_WARDEN - DATA_FIRE_WARDEN,
NPC_BRANN_BRONZEBEARD_0 = 39908,
NPC_OMEGA_STANCE = 41194,
NPC_ALPHA_BEAM = 41144,
NPC_STONE_TROGG_PILLAGER = 39804,
NPC_STONE_TROGG_BRUTE = 40251,
NPC_STONE_TROGG_ROCK_FLINGER = 40252,
BOSS_ISISET = 39587,
NPC_SPATIAL_FLUX_TRASH = 39612, // Isiset trash
NPC_ENERGY_FLUX_TRASH = 44015,
NPC_SPATIAL_ANOMALY = 40170,
NPC_FLUX_ANIMATOR = 40033,
NPC_STAR_SHARD = 40106,
BOSS_AMMUNAE = 39731,
BOSS_SETESH = 39732,
NPC_SETESH_CHAOS_SEED = 41126,
NPC_SETESH_CHAOS_BLAST = 41041,
NPC_SETESH_CHAOS_PORTAL = 41055,
NPC_SETESH_ADD_STALKER = 41479,
NPC_SETESH_VOID_RIFT = 39266, // Setesh trash
BOSS_RAJH = 39378,
NPC_INFERNO_LEAP = 47040,
NPC_BLAZING_INFERNO = 40927,
};
enum HOOGameObjects
{
GO_ANHUURS_BRIDGE = 206506,
GO_DOODAD_ULDUM_ELEVATOR_COL01 = 207725,
GO_ANHUURS_DOOR = 202307,
GO_ANHUURS_RIGHT_BEACON = 203136,
GO_ANHUURS_LEFT_BEACON = 203133,
GO_DOODAD_ULDUM_DOOR_14 = 202306,
GO_DOODAD_ULDUM_DOOR_15 = 202307,
GO_VAULT_OF_LIGHTS_DOOR = 202313,
GO_SUN_MIRROR = 207726,
GO_ANRAPHET_DOOR = 202314,
GO_DOODAD_ULDUM_LIGHTMACHINE_01 = 207375,
GO_DOODAD_ULDUM_LIGHTMACHINE_02 = 207374,
GO_DOODAD_ULDUM_LIGHTMACHINE_03 = 207377,
GO_DOODAD_ULDUM_LIGHTMACHINE_04 = 207376,
GO_DOODAD_ULDUM_LIGHTMACHINE_02 = 207374, // South-West
GO_DOODAD_ULDUM_LIGHTMACHINE_01 = 207375, // South-East
GO_DOODAD_ULDUM_LIGHTMACHINE_04 = 207376, // North-West
GO_DOODAD_ULDUM_LIGHTMACHINE_03 = 207377, // North-East
GO_DOODAD_ULDUM_LASERBEAMS01 = 207662, // Matches GO_DOODAD_ULDUM_LIGHTMACHINE_02
GO_DOODAD_ULDUM_LASERBEAMS_01 = 207663, // Matches GO_DOODAD_ULDUM_LIGHTMACHINE_01
GO_DOODAD_ULDUM_LASERBEAMS_02 = 207664, // Matches GO_DOODAD_ULDUM_LIGHTMACHINE_04
GO_DOODAD_ULDUM_LASERBEAMS_03 = 207665, // Matches GO_DOODAD_ULDUM_LIGHTMACHINE_03
GO_DOODAD_ULDUM_LASERBEAMS01 = 207662, // South-West
GO_DOODAD_ULDUM_LASERBEAMS_01 = 207663, // South-East
GO_DOODAD_ULDUM_LASERBEAMS_02 = 207664, // North-West
GO_DOODAD_ULDUM_LASERBEAMS_03 = 207665, // North-East
GO_ULDUM_TEMPLE = 207802,
GO_REORIGINATION_MECHANISM_1 = 207445,
GO_REORIGINATION_MECHANISM_2 = 207449,
GO_REORIGINATION_MECHANISM_3 = 207452,
GO_REORIGINATION_MECHANISM_4 = 207454,
GO_REORIGINATION_MECHANISM_5 = 207456,
GO_HOO_TRANSIT_DEVICE = 204979, // Spell ID: 82916 - Teleports the caster to the lower floor of Halls of Origination.
GO_HOO_TRANSIT_DEVICE_2 = 204972, // Spell ID: 82900 - Teleports the caster to the upper floor of Halls of Origination. (dbc desc is wrong)
GO_LIFT_OF_THE_MAKERS = 207547,
GO_LIFT_GLASS_STAR = 207673,
GO_LIFT_GLASS_STAR_2 = 207674
};
enum Achievements
{
// I Hate That Song
// Achievement.db2 ID 5293: ParentTree 19724
// CriteriaTree.db2 ID 19725: ParentTree 19724 - Criteria 15988
// Criteria.db2 ID 15988: on Anhuur kill, world state 5638 = value 0
//CRITERIA_I_HATE_THAT_SONG = 15988,
WS_I_HATE_THIS_SONG = 5638,
// Straw That Broke the Camel's Back
// Achievement.db2 ID 5294: ParentTree 18221
// CriteriaTree.db2 ID 18222: ParentTree 18221 - Criteria 15989
// Criteria.db2 ID 15989: on Ptah kill, tree 2838?
CRITERIA_STRAW_BROKE_CAMELS_BACK = 15989,
// Faster Than The Speed Of Light
// Achievement.db2 ID 5296: ParentTree 17685
// CriteriaTree.db2 ID 17686: ParentTree 17685 - Criteria 16008
// Criteria.db2 ID 16008: spell 94067, startEvent 24212, tree 2615?, world state 5653 = value 0, type 69
ACHIEV_VAULT_OF_LIGHTS_START_EVENT = 24212,
SPELL_VAULT_OF_LIGHTS_CREDIT = 94067 // Achievement aura, not in DBC
};
enum HOOMisc
{
AREA_TOMB_OF_THE_EARTHRAGER = 4945,
ACHIEV_VAULT_OF_LIGHTS_EVENT = 24212, // Faster Than The Speed Of Light
SPELL_VAULT_OF_LIGHTS_CREDIT = 94067, // Not in DBC
AREA_TOMB_OF_THE_EARTHRAGER = 4945,
SPELL_ZERO_ENERGY_NO_REGEN_AURA = 72242, // Zero Energy + Zero Regen (used by some npcs in HoO)
// SPELL_SHRINK = 59632, // Used by static NPCs, summoned by bosses in The Four Seats (not in DBC!)
// Hmm... Do elementals use these spells to spawn on a random platform?
// Probably also 81796, 81798, 81799, 81800 (all hidden client-side).
SPELL_TELEPORT_EARTH = 82329, // South-West
SPELL_TELEPORT_AIR = 82330, // South-East
SPELL_TELEPORT_FIRE = 82331, // North-West
SPELL_TELEPORT_WATER = 82332 // North-East
};
enum HOOGlobalActions
{
ACTION_PTAH_ADD_DIED,
ACTION_ANRAPHET_INTRO,
ACTION_ELEMENTAL_DIED,
ACTION_ANRAPHET_DIED,
ACTION_OMEGA_TRIGGER,
ACTION_OMEGA_TRIGGER
};
template <class AI, class T>
inline AI* GetHallsOfOriginationAI(T* obj)
enum DataStates
{
return GetInstanceAI<AI>(obj, HoOScriptName);
ACTIVATE_SHIELD_OF_LIGHT = 0,
DISABLE_SHIELD_OF_LIGHT
};
template<typename AI>
inline AI* GetHallsOfOriginationAI(Creature* creature)
{
return GetInstanceAI<AI>(creature, HoOScriptName);
}
template<typename AI>
inline AI* GetHallsOfOriginationAI(GameObject* go)
{
return GetInstanceAI<AI>(go, HoOScriptName);
}
#endif // HALLS_OF_ORIGINATION_H

View File

@@ -15,33 +15,65 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "halls_of_origination.h"
#include "ObjectMgr.h"
#include "ScriptMgr.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "CreatureGroups.h"
#include "GameObject.h"
#include "InstanceScript.h"
#include "ScriptedCreature.h"
#include "Map.h"
#include "PoolMgr.h"
#include "AccountMgr.h"
#include "halls_of_origination.h"
#include "Player.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
DoorData const doorData[] =
{
{GO_ANHUURS_DOOR, DATA_TEMPLE_GUARDIAN_ANHUUR, DOOR_TYPE_PASSAGE },
{GO_ANHUURS_BRIDGE, DATA_TEMPLE_GUARDIAN_ANHUUR, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_ELEVATOR_COL01, DATA_TEMPLE_GUARDIAN_ANHUUR, DOOR_TYPE_PASSAGE },
{GO_VAULT_OF_LIGHTS_DOOR, DATA_VAULT_OF_LIGHTS, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LIGHTMACHINE_02, DATA_EARTH_WARDEN, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LASERBEAMS01, DATA_EARTH_WARDEN, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LIGHTMACHINE_01, DATA_FIRE_WARDEN, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LASERBEAMS_01, DATA_FIRE_WARDEN, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LIGHTMACHINE_03, DATA_WATER_WARDEN, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LASERBEAMS_03, DATA_WATER_WARDEN, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LIGHTMACHINE_04, DATA_AIR_WARDEN, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ULDUM_LASERBEAMS_02, DATA_AIR_WARDEN, DOOR_TYPE_PASSAGE },
{0, 0, DOOR_TYPE_ROOM, }
{ GO_DOODAD_ULDUM_DOOR_14, DATA_TEMPLE_GUARDIAN_ANHUUR, DOOR_TYPE_ROOM },
{ GO_DOODAD_ULDUM_DOOR_15, DATA_TEMPLE_GUARDIAN_ANHUUR, DOOR_TYPE_PASSAGE },
{ GO_ANHUURS_BRIDGE, DATA_TEMPLE_GUARDIAN_ANHUUR, DOOR_TYPE_PASSAGE },
{ GO_DOODAD_ULDUM_ELEVATOR_COL01, DATA_TEMPLE_GUARDIAN_ANHUUR, DOOR_TYPE_PASSAGE },
{ GO_LIFT_GLASS_STAR, DATA_ANRAPHET, DOOR_TYPE_PASSAGE },
{ GO_LIFT_GLASS_STAR_2, DATA_ANRAPHET, DOOR_TYPE_PASSAGE },
{ 0, 0, DOOR_TYPE_ROOM }
};
/*BossBoundaryData const boundaries =
{
};*/
ObjectData const creatureData[] =
{
{ BOSS_TEMPLE_GUARDIAN_ANHUUR, DATA_TEMPLE_GUARDIAN_ANHUUR },
{ BOSS_EARTHRAGER_PTAH, DATA_EARTHRAGER_PTAH },
{ NPC_BRANN_BRONZEBEARD_0, DATA_BRANN_0 },
{ NPC_FIRE_WARDEN, DATA_FIRE_WARDEN },
{ NPC_EARTH_WARDEN, DATA_EARTH_WARDEN },
{ NPC_WATER_WARDEN, DATA_WATER_WARDEN },
{ NPC_AIR_WARDEN, DATA_AIR_WARDEN },
{ BOSS_ANRAPHET, DATA_ANRAPHET },
{ BOSS_ISISET, DATA_ISISET },
{ BOSS_AMMUNAE, DATA_AMMUNAE },
{ BOSS_SETESH, DATA_SETESH },
{ BOSS_RAJH, DATA_RAJH },
{ 0, 0 } // END
};
ObjectData const gameObjectData[] =
{
{ GO_DOODAD_ULDUM_DOOR_15, DATA_ANHUUR_DOOR },
{ GO_VAULT_OF_LIGHTS_DOOR, DATA_VAULT_OF_LIGHTS_DOOR },
{ GO_DOODAD_ULDUM_LIGHTMACHINE_02, DATA_LIGHTMACHINE_EARTH },
{ GO_DOODAD_ULDUM_LIGHTMACHINE_01, DATA_LIGHTMACHINE_AIR },
{ GO_DOODAD_ULDUM_LIGHTMACHINE_04, DATA_LIGHTMACHINE_FIRE },
{ GO_DOODAD_ULDUM_LIGHTMACHINE_03, DATA_LIGHTMACHINE_WATER },
{ GO_DOODAD_ULDUM_LASERBEAMS01, DATA_LASERBEAMS_EARTH },
{ GO_DOODAD_ULDUM_LASERBEAMS_01, DATA_LASERBEAMS_AIR },
{ GO_DOODAD_ULDUM_LASERBEAMS_03, DATA_LASERBEAMS_FIRE },
{ GO_DOODAD_ULDUM_LASERBEAMS_02, DATA_LASERBEAMS_WATER },
{ GO_SUN_MIRROR, DATA_ANRAPHET_SUN_MIRROR },
{ GO_ANRAPHET_DOOR, DATA_ANRAPHET_DOOR },
{ GO_LIFT_OF_THE_MAKERS, DATA_LIFT_OF_THE_MAKERS },
{ 0, 0 } //END
};
class instance_halls_of_origination : public InstanceMapScript
@@ -56,82 +88,144 @@ class instance_halls_of_origination : public InstanceMapScript
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
LoadObjectData(creatureData, gameObjectData);
_brannIntroStarted = 0;
_deadElementals = 0;
RotateWardenPositions();
}
void OnGameObjectCreate(GameObject* go) override
{
InstanceScript::OnGameObjectCreate(go);
switch (go->GetEntry())
{
case GO_ANHUURS_BRIDGE:
AnhuursBridgeGUID = go->GetGUID();
case GO_DOODAD_ULDUM_ELEVATOR_COL01:
case GO_HOO_TRANSIT_DEVICE: // 5 spawns
case GO_HOO_TRANSIT_DEVICE_2: // 1 spawn: elevator
_transitDeviceGUIDs.insert(go->GetGUID());
UpdateTransitDevice(go);
break;
case GO_LIFT_OF_THE_MAKERS:
go->SetTransportState(GO_STATE_TRANSPORT_ACTIVE);
go->SetTransportState(GO_STATE_TRANSPORT_STOPPED, 0);
break;
case GO_VAULT_OF_LIGHTS_DOOR:
case GO_DOODAD_ULDUM_LIGHTMACHINE_01:
case GO_DOODAD_ULDUM_LIGHTMACHINE_02:
case GO_DOODAD_ULDUM_LIGHTMACHINE_03:
case GO_DOODAD_ULDUM_LIGHTMACHINE_04:
if (_brannIntroStarted || GetBossState(DATA_VAULT_OF_LIGHTS) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_DOODAD_ULDUM_LASERBEAMS01:
if (GetBossState(DATA_EARTH_WARDEN) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_DOODAD_ULDUM_LASERBEAMS_01:
if (GetBossState(DATA_FIRE_WARDEN) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_DOODAD_ULDUM_LASERBEAMS_02:
if (GetBossState(DATA_AIR_WARDEN) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_DOODAD_ULDUM_LASERBEAMS_03:
AddDoor(go, true);
break;
case GO_ANHUURS_DOOR:
AnhuursDoorGUID = go->GetGUID();
AddDoor(go, true);
break;
case GO_ANHUURS_RIGHT_BEACON:
AnhuurRightBeaconGUID = go->GetGUID();
break;
case GO_ANHUURS_LEFT_BEACON:
AnhuurLeftBeaconGUID = go->GetGUID();
if (GetBossState(DATA_WATER_WARDEN) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SUN_MIRROR:
SunMirrorGUID = go->GetGUID();
break;
case GO_ANRAPHET_DOOR:
AnraphetDoorGUID = go->GetGUID();
if (GetBossState(DATA_VAULT_OF_LIGHTS) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ULDUM_TEMPLE: // Research about these objects.
case GO_REORIGINATION_MECHANISM_1:
case GO_REORIGINATION_MECHANISM_2:
case GO_REORIGINATION_MECHANISM_3:
case GO_REORIGINATION_MECHANISM_4:
case GO_REORIGINATION_MECHANISM_5:
go->SetGoState(GO_STATE_ACTIVE);
break;
}
}
void OnGameObjectRemove(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_ANHUURS_BRIDGE:
case GO_DOODAD_ULDUM_ELEVATOR_COL01:
case GO_ANHUURS_DOOR:
case GO_VAULT_OF_LIGHTS_DOOR:
case GO_DOODAD_ULDUM_LIGHTMACHINE_01:
case GO_DOODAD_ULDUM_LIGHTMACHINE_02:
case GO_DOODAD_ULDUM_LIGHTMACHINE_03:
case GO_DOODAD_ULDUM_LIGHTMACHINE_04:
case GO_DOODAD_ULDUM_LASERBEAMS01:
case GO_DOODAD_ULDUM_LASERBEAMS_01:
case GO_DOODAD_ULDUM_LASERBEAMS_02:
case GO_DOODAD_ULDUM_LASERBEAMS_03:
AddDoor(go, false);
if (GetBossState(DATA_VAULT_OF_LIGHTS) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
default:
break;
}
go->SetFarVisible(true);
}
void OnCreatureCreate(Creature* creature) override
{
InstanceScript::OnCreatureCreate(creature);
switch (creature->GetEntry())
{
case BOSS_TEMPLE_GUARDIAN_ANHUUR:
TempleGuardianAnhuurGUID = creature->GetGUID();
case NPC_HOO_CAMEL:
_hooCamelGUIDs.insert(creature->GetGUID());
break;
case NPC_BEETLE_STALKER: // Must be active (sometimes they fail to summon adds, hopefully not anymore).
creature->setActive(true);
break;
case NPC_DUSTBONE_HORROR:
case NPC_JEWELED_SCARAB:
creature->SetInCombatWithZone();
break;
case BOSS_ANRAPHET: // Must be active (their AI runs the event at the Vault of Lights).
case NPC_BRANN_BRONZEBEARD_0:
BrannBronzebeardGUID = creature->GetGUID();
creature->setActive(true);
break;
case BOSS_ANRAPHET:
AnraphetGUID = creature->GetGUID();
case NPC_FIRE_WARDEN: // random teleport spell + far-visibility
case NPC_EARTH_WARDEN:
case NPC_WATER_WARDEN:
case NPC_AIR_WARDEN:
creature->CastSpell(nullptr, _wardenPositionSpells[creature->GetEntry() - NPC_FIRE_WARDEN]);
creature->SetFarVisible(true);
break;
case BOSS_RAJH:
RajhGUID = creature->GetGUID();
case NPC_STONE_TROGG_PILLAGER: // far-visibility
case NPC_STONE_TROGG_BRUTE:
case NPC_STONE_TROGG_ROCK_FLINGER:
if (GetBossState(DATA_VAULT_OF_LIGHTS) == DONE)
creature->DespawnOrUnsummon(0, Seconds(DAY));
creature->SetFarVisible(true);
break;
case NPC_SPATIAL_FLUX_TRASH:
case NPC_SPATIAL_ANOMALY:
case NPC_FLUX_ANIMATOR:
case NPC_STAR_SHARD:
creature->SearchFormation();
if (creature->GetFormation())
_isisetTrashGUIDs.insert(creature->GetGUID());
break;
case NPC_ALPHA_BEAM:
if (Creature* anraphet = GetCreature(DATA_ANRAPHET))
anraphet->AI()->JustSummoned(creature);
default:
break;
}
}
void SetData(uint32 data, uint32 value) override
{
switch (data)
{
case DATA_UPDATE_LASERBEAMS:
UpdateAllLaserBeams();
break;
case DATA_ISISET_PHASE:
_isisetPhase = value;
break;
case DATA_ISISET_ASTRAL_RAIN_ALIVE:
_isisetAstralRainAlive = value;
break;
case DATA_ISISET_CELESTIAL_CALL_ALIVE:
_isisetCelestialCallAlive = value;
break;
case DATA_ISISET_VEIL_OF_SKY_ALIVE:
_isisetVeilOfSkyAlive = value;
break;
}
}
@@ -142,6 +236,14 @@ class instance_halls_of_origination : public InstanceMapScript
{
case DATA_DEAD_ELEMENTALS:
return _deadElementals;
case DATA_ISISET_PHASE:
return _isisetPhase;
case DATA_ISISET_ASTRAL_RAIN_ALIVE:
return _isisetAstralRainAlive;
case DATA_ISISET_CELESTIAL_CALL_ALIVE:
return _isisetCelestialCallAlive;
case DATA_ISISET_VEIL_OF_SKY_ALIVE:
return _isisetVeilOfSkyAlive;
default:
break;
}
@@ -149,41 +251,62 @@ class instance_halls_of_origination : public InstanceMapScript
return 0;
}
ObjectGuid GetGuidData(uint32 index) const override
bool SetBossState(uint32 type, EncounterState state) override
{
switch (index)
if (!InstanceScript::SetBossState(type, state))
return false;
if (state == DONE) // Respawn transit devices if needed.
for (ObjectGuid guid : _transitDeviceGUIDs)
if (GameObject* transit = instance->GetGameObject(guid))
UpdateTransitDevice(transit);
switch (type)
{
case DATA_ANHUUR_BRIDGE:
return AnhuursBridgeGUID;
case DATA_ANHUUR_DOOR:
return AnhuursDoorGUID;
case DATA_ANHUUR_LEFT_BEACON:
return AnhuurLeftBeaconGUID;
case DATA_ANHUUR_RIGHT_BEACON:
return AnhuurRightBeaconGUID;
case DATA_ANHUUR_GUID:
return TempleGuardianAnhuurGUID;
case DATA_BRANN_0_GUID:
return BrannBronzebeardGUID;
case DATA_ANRAPHET_GUID:
return AnraphetGUID;
case DATA_RAJH:
return RajhGUID;
case DATA_EARTHRAGER_PTAH:
if (state == IN_PROGRESS)
{
// Camels cannot be mounted during the boss fight.
for (ObjectGuid guid : _hooCamelGUIDs)
if (Creature* camel = instance->GetCreature(guid))
camel->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
}
else
{
// Make camels mountable again.
for (ObjectGuid guid : _hooCamelGUIDs)
if (Creature* camel = instance->GetCreature(guid))
camel->SetFlag(UNIT_FIELD_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
}
break;
case DATA_VAULT_OF_LIGHTS:
if (state == IN_PROGRESS)
{
HandleGameObject(ObjectGuid::Empty, true, GetGameObject(DATA_VAULT_OF_LIGHTS_DOOR));
DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_VAULT_OF_LIGHTS_START_EVENT);
}
else if (state == DONE)
{
DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, SPELL_VAULT_OF_LIGHTS_CREDIT);
}
break;
case DATA_FIRE_WARDEN:
case DATA_EARTH_WARDEN:
case DATA_WATER_WARDEN:
case DATA_AIR_WARDEN:
if (state == DONE)
{
UpdateAllLightMachines();
++_deadElementals;
if (Creature* brann = GetCreature(DATA_BRANN_0))
brann->AI()->DoAction(ACTION_ELEMENTAL_DIED);
}
break;
default:
break;
}
return ObjectGuid::Empty;
}
void IncreaseDeadElementals(uint32 inc)
{
_deadElementals += inc;
if (_deadElementals == 4)
{
if (GameObject* mirror = instance->GetGameObject(SunMirrorGUID))
mirror->SetGoState(GO_STATE_ACTIVE);
if (GameObject* door = instance->GetGameObject(AnraphetDoorGUID))
door->SetGoState(GO_STATE_ACTIVE);
}
return true;
}
void OnUnitDeath(Unit* unit) override
@@ -194,44 +317,111 @@ class instance_halls_of_origination : public InstanceMapScript
switch (creature->GetEntry())
{
case NPC_FIRE_WARDEN:
case NPC_EARTH_WARDEN:
case NPC_DUSTBONE_HORROR:
case NPC_JEWELED_SCARAB:
if (Creature* ptah = GetCreature(DATA_EARTHRAGER_PTAH))
ptah->GetAI()->DoAction(ACTION_PTAH_ADD_DIED);
break;
case NPC_FIRE_WARDEN: // We have to set boss states manually (wardens use SmartAI).
case NPC_EARTH_WARDEN: // To-do: check if smart scripts can handle this
case NPC_WATER_WARDEN:
case NPC_AIR_WARDEN:
{
uint32 data = creature->GetEntry() - WARDEN_ENTRY_DATA_DELTA;
SetBossState(data, IN_PROGRESS); // Needs to be set to IN_PROGRESS or else the gameobjects state won't be updated
SetBossState(data, DONE);
IncreaseDeadElementals(1);
if (Creature* brann = instance->GetCreature(BrannBronzebeardGUID))
brann->AI()->DoAction(ACTION_ELEMENTAL_DIED);
break;
}
case NPC_SPATIAL_ANOMALY:
case NPC_FLUX_ANIMATOR:
case NPC_STAR_SHARD:
{
CreatureGroup* group = creature->GetFormation();
if (!group)
return;
// Check if whole formation is dead, then despawn its leader
for (ObjectGuid guid : _isisetTrashGUIDs)
if (Creature* member = instance->GetCreature(guid))
if (member->GetEntry() != NPC_SPATIAL_FLUX_TRASH && member->GetFormation()->GetId() == group->GetId() && member->IsAlive())
return;
// Despawn leader (Spatial Flux)
if (Creature* leader = group->getLeader())
leader->DespawnOrUnsummon();
break;
}
default:
break;
}
}
void WriteSaveDataMore(std::ostringstream& data) override
{
data << _deadElementals;
data << _brannIntroStarted << ' ' << _deadElementals << ' ';
for (uint8 i = 0; i < WARDEN_ENTRY_MAX_COUNT; i++)
data << _wardenPositionSpells[i] << ' ';
}
void ReadSaveDataMore(std::istringstream& data) override
{
uint32 deadElementals;
data >> deadElementals;
IncreaseDeadElementals(deadElementals);
data >> _brannIntroStarted >> _deadElementals;
for (uint8 i = 0; i < WARDEN_ENTRY_MAX_COUNT; i++)
data >> _wardenPositionSpells[i];
}
protected:
ObjectGuid TempleGuardianAnhuurGUID;
ObjectGuid AnhuursBridgeGUID;
ObjectGuid AnhuursDoorGUID;
ObjectGuid AnhuurRightBeaconGUID;
ObjectGuid AnhuurLeftBeaconGUID;
ObjectGuid BrannBronzebeardGUID;
ObjectGuid AnraphetGUID;
ObjectGuid AnraphetDoorGUID;
ObjectGuid SunMirrorGUID;
ObjectGuid RajhGUID;
private:
void RotateWardenPositions()
{
uint32 startAt = urand(0, WARDEN_ENTRY_MAX_COUNT - 1);
for (uint32 i = 0; i < WARDEN_ENTRY_MAX_COUNT; i++)
_wardenPositionSpells[(i + startAt) % WARDEN_ENTRY_MAX_COUNT] = SPELL_TELEPORT_EARTH + i;
}
// Activated when warden dies (on SetBossState DONE).
void UpdateAllLightMachines()
{
for (uint8 i = 0; i < WARDEN_ENTRY_MAX_COUNT; i++)
{
uint32 positionIndex = _wardenPositionSpells[i] - SPELL_TELEPORT_EARTH;
HandleGameObject(ObjectGuid::Empty, GetBossState(DATA_FIRE_WARDEN + i) == DONE, GetGameObject(DATA_LIGHTMACHINE_EARTH + positionIndex));
}
}
// Activated 9 seconds after warden dies (on SetData DATA_UPDATE_LASERBEAMS, called by Brann).
void UpdateAllLaserBeams()
{
for (uint8 i = 0; i < WARDEN_ENTRY_MAX_COUNT; i++)
{
uint32 positionIndex = _wardenPositionSpells[i] - SPELL_TELEPORT_EARTH;
HandleGameObject(ObjectGuid::Empty, GetBossState(DATA_FIRE_WARDEN + i) == DONE, GetGameObject(DATA_LIGHTMACHINE_EARTH + positionIndex));
}
}
void UpdateTransitDevice(GameObject* transit)
{
if (transit->GetPositionX() < -900.f) // The southernmost transit: x = -934.576
transit->SetRespawnTime(GetBossState(DATA_TEMPLE_GUARDIAN_ANHUUR) == DONE ? -1 : DAY);
else if (transit->GetPositionY() < -300.f) // The two easternmost transits: y = -337.028, y = -686.826
transit->SetRespawnTime(GetBossState(DATA_EARTHRAGER_PTAH) == DONE ? -1 : DAY);
else // All other transits.
transit->SetRespawnTime(GetBossState(DATA_ANRAPHET) == DONE ? -1 : DAY);
}
GuidSet _transitDeviceGUIDs;
GuidSet _hooCamelGUIDs;
GuidSet _isisetTrashGUIDs;
GuidSet _caveInStalkerGUIDs;
uint32 _brannIntroStarted;
uint32 _wardenPositionSpells[WARDEN_ENTRY_MAX_COUNT];
uint32 _deadElementals;
uint32 _isisetPhase;
uint32 _isisetAstralRainAlive;
uint32 _isisetCelestialCallAlive;
uint32 _isisetVeilOfSkyAlive;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override

View File

@@ -99,9 +99,13 @@ void AddSC_boss_high_prophet_barim();
void AddSC_boss_lockmaw_and_augh();
void AddSC_boss_siamat();
void AddSC_instance_halls_of_origination(); //Halls of Origination
void AddSC_halls_of_origination();
void AddSC_boss_temple_guardian_anhuur();
void AddSC_boss_earthrager_ptah();
void AddSC_boss_anraphet();
void AddSC_boss_ammunae();
void AddSC_boss_isiset();
void AddSC_boss_setesh();
void AddSC_boss_rajh();
void AddSC_vortex_pinnacle(); //Vortex Pinnacle
void AddSC_instance_vortex_pinnacle();
@@ -214,9 +218,13 @@ void AddKalimdorScripts()
AddSC_boss_lockmaw_and_augh();
AddSC_boss_siamat();
AddSC_instance_halls_of_origination(); //Halls of Origination
AddSC_halls_of_origination();
AddSC_boss_temple_guardian_anhuur();
AddSC_boss_earthrager_ptah();
AddSC_boss_anraphet();
AddSC_boss_ammunae();
AddSC_boss_isiset();
AddSC_boss_setesh();
AddSC_boss_rajh();
AddSC_vortex_pinnacle();
AddSC_instance_vortex_pinnacle();