refactor(Scripts/HoR): modernize Halls of Reflection scripts (#25176)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andrew
2026-03-22 12:51:35 -03:00
committed by GitHub
parent 11d4d01e7f
commit 94faf55a86
7 changed files with 2363 additions and 2632 deletions
@@ -134,6 +134,11 @@ void InstanceScript::HandleGameObject(ObjectGuid GUID, bool open, GameObject* go
} }
} }
void InstanceScript::HandleGameObject(uint32 type, bool open)
{
HandleGameObject(ObjectGuid::Empty, open, GetGameObject(type));
}
bool InstanceScript::IsEncounterInProgress() const bool InstanceScript::IsEncounterInProgress() const
{ {
for (std::vector<BossInfo>::const_iterator itr = bosses.begin(); itr != bosses.end(); ++itr) for (std::vector<BossInfo>::const_iterator itr = bosses.begin(); itr != bosses.end(); ++itr)
+13 -3
View File
@@ -193,11 +193,21 @@ public:
//Called when a player enters/leaves water bodies. //Called when a player enters/leaves water bodies.
virtual void OnPlayerInWaterStateUpdate(Player* /*player*/, bool /*inWater*/) {} virtual void OnPlayerInWaterStateUpdate(Player* /*player*/, bool /*inWater*/) {}
//Handle open / close objects /**
//use HandleGameObject(ObjectGuid::Empty, boolen, GO); in OnObjectCreate in instance scripts * @brief Open or close a GameObject by GUID.
//use HandleGameObject(GUID, boolen, nullptr); in any other script * @param guid The GUID of the GameObject. Pass ObjectGuid::Empty when providing the go pointer directly.
* @param open true to open (GO_STATE_ACTIVE), false to close (GO_STATE_READY).
* @param go Optional pointer to the GameObject. If nullptr, the object is looked up by GUID.
*/
void HandleGameObject(ObjectGuid guid, bool open, GameObject* go = nullptr); void HandleGameObject(ObjectGuid guid, bool open, GameObject* go = nullptr);
/**
* @brief Open or close a GameObject registered via LoadObjectData.
* @param type The ObjectData type constant (e.g. GO_FRONT_DOOR) used in the gameObjectData array.
* @param open true to open (GO_STATE_ACTIVE), false to close (GO_STATE_READY).
*/
void HandleGameObject(uint32 type, bool open);
//change active state of doors or buttons //change active state of doors or buttons
void DoUseDoorOrButton(ObjectGuid guid, uint32 withRestoreTime = 0, bool useAlternativeState = false); void DoUseDoorOrButton(ObjectGuid guid, uint32 withRestoreTime = 0, bool useAlternativeState = false);
@@ -16,9 +16,9 @@
*/ */
#include "CreatureScript.h" #include "CreatureScript.h"
#include "halls_of_reflection.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "halls_of_reflection.h"
enum Yells enum Yells
{ {
@@ -38,45 +38,30 @@ enum Spells
enum Events enum Events
{ {
EVENT_NONE, EVENT_QUIVERING_STRIKE = 1,
EVENT_QUIVERING_STRIKE,
EVENT_IMPENDING_DESPAIR, EVENT_IMPENDING_DESPAIR,
EVENT_DEFILING_HORROR, EVENT_DEFILING_HORROR,
EVENT_UNROOT, EVENT_UNROOT,
}; };
const uint32 hopelessnessId[3] = { 72395, 72396, 72397 }; constexpr uint32 hopelessnessId[3] = { 72395, 72396, 72397 };
class boss_falric : public CreatureScript struct boss_falric : public BossAI
{ {
public: boss_falric(Creature* creature) : BossAI(creature, DATA_FALRIC) { }
boss_falric() : CreatureScript("boss_falric") { }
struct boss_falricAI : public ScriptedAI
{
boss_falricAI(Creature* creature) : ScriptedAI(creature)
{
pInstance = creature->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
uint8 uiHopelessnessCount;
uint16 startFightTimer;
void Reset() override void Reset() override
{ {
startFightTimer = 0; BossAI::Reset();
uiHopelessnessCount = 0; _hopelessnessCount = 0;
_startingFight = false;
me->SetImmuneToAll(true); me->SetImmuneToAll(true);
me->SetControlled(false, UNIT_STATE_ROOT); me->SetControlled(false, UNIT_STATE_ROOT);
events.Reset();
if (pInstance)
pInstance->SetData(DATA_FALRIC, NOT_STARTED);
} }
void JustEngagedWith(Unit* /*who*/) override void JustEngagedWith(Unit* who) override
{ {
BossAI::JustEngagedWith(who);
me->SetImmuneToAll(false); me->SetImmuneToAll(false);
events.ScheduleEvent(EVENT_QUIVERING_STRIKE, 5s); events.ScheduleEvent(EVENT_QUIVERING_STRIKE, 5s);
@@ -84,28 +69,22 @@ public:
events.ScheduleEvent(EVENT_DEFILING_HORROR, 20s); events.ScheduleEvent(EVENT_DEFILING_HORROR, 20s);
} }
void DoAction(int32 a) override void DoAction(int32 action) override
{ {
if (a == 1) if (action == 1)
{ {
Talk(SAY_AGGRO); Talk(SAY_AGGRO);
startFightTimer = 8000; _startingFight = true;
me->m_Events.AddEventAtOffset([this]()
{
_startingFight = false;
me->SetInCombatWithZone();
}, 8s);
} }
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (startFightTimer)
{
if (startFightTimer <= diff)
{
startFightTimer = 0;
me->SetInCombatWithZone();
}
else
startFightTimer -= diff;
}
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
@@ -117,48 +96,49 @@ public:
switch (events.ExecuteEvent()) switch (events.ExecuteEvent())
{ {
case EVENT_QUIVERING_STRIKE: case EVENT_QUIVERING_STRIKE:
me->CastSpell(me->GetVictim(), SPELL_QUIVERING_STRIKE, false); DoCastVictim(SPELL_QUIVERING_STRIKE);
events.ScheduleEvent(EVENT_QUIVERING_STRIKE, 5s); events.Repeat(5s);
break; break;
case EVENT_IMPENDING_DESPAIR: case EVENT_IMPENDING_DESPAIR:
if (Unit* target = SelectTargetFromPlayerList(45.0f, 0, true)) if (Unit* target = SelectTargetFromPlayerList(45.0f, 0, true))
{ {
Talk(SAY_IMPENDING_DESPAIR); Talk(SAY_IMPENDING_DESPAIR);
me->CastSpell(target, SPELL_IMPENDING_DESPAIR, false); DoCast(target, SPELL_IMPENDING_DESPAIR);
} }
events.ScheduleEvent(EVENT_IMPENDING_DESPAIR, 12s); events.Repeat(12s);
break; break;
case EVENT_DEFILING_HORROR: case EVENT_DEFILING_HORROR:
Talk(SAY_DEFILING_HORROR); Talk(SAY_DEFILING_HORROR);
me->CastSpell((Unit*)nullptr, SPELL_DEFILING_HORROR, false); DoCastAOE(SPELL_DEFILING_HORROR);
me->SetControlled(true, UNIT_STATE_ROOT); me->SetControlled(true, UNIT_STATE_ROOT);
events.DelayEventsToMax(5s, 0); events.DelayEventsToMax(5s, 0);
events.ScheduleEvent(EVENT_UNROOT, 4s); events.ScheduleEvent(EVENT_UNROOT, 4s);
events.ScheduleEvent(EVENT_DEFILING_HORROR, 20s); events.Repeat(20s);
break; break;
case EVENT_UNROOT: case EVENT_UNROOT:
me->SetControlled(false, UNIT_STATE_ROOT); me->SetControlled(false, UNIT_STATE_ROOT);
break; break;
} }
if ((uiHopelessnessCount == 0 && HealthBelowPct(67)) || (uiHopelessnessCount == 1 && HealthBelowPct(34)) || (uiHopelessnessCount == 2 && HealthBelowPct(11))) if ((_hopelessnessCount == 0 && HealthBelowPct(67))
|| (_hopelessnessCount == 1 && HealthBelowPct(34))
|| (_hopelessnessCount == 2 && HealthBelowPct(11)))
{ {
if (uiHopelessnessCount) if (_hopelessnessCount)
me->RemoveOwnedAura(sSpellMgr->GetSpellIdForDifficulty(hopelessnessId[uiHopelessnessCount - 1], me)); me->RemoveOwnedAura(sSpellMgr->GetSpellIdForDifficulty(hopelessnessId[_hopelessnessCount - 1], me));
me->CastSpell((Unit*)nullptr, hopelessnessId[uiHopelessnessCount], true); DoCastAOE(hopelessnessId[_hopelessnessCount], true);
++uiHopelessnessCount; ++_hopelessnessCount;
} }
if (!me->HasUnitState(UNIT_STATE_ROOT)) if (!me->HasUnitState(UNIT_STATE_ROOT))
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* killer) override
{ {
BossAI::JustDied(killer);
Talk(SAY_DEATH); Talk(SAY_DEATH);
if (pInstance)
pInstance->SetData(DATA_FALRIC, DONE);
} }
void KilledUnit(Unit* who) override void KilledUnit(Unit* who) override
@@ -170,19 +150,17 @@ public:
void EnterEvadeMode(EvadeReason why) override void EnterEvadeMode(EvadeReason why) override
{ {
me->SetControlled(false, UNIT_STATE_ROOT); me->SetControlled(false, UNIT_STATE_ROOT);
ScriptedAI::EnterEvadeMode(why); BossAI::EnterEvadeMode(why);
if (startFightTimer) if (_startingFight)
Reset(); Reset();
} }
};
CreatureAI* GetAI(Creature* creature) const override private:
{ uint8 _hopelessnessCount{};
return GetHallsOfReflectionAI<boss_falricAI>(creature); bool _startingFight{};
}
}; };
void AddSC_boss_falric() void AddSC_boss_falric()
{ {
new boss_falric(); RegisterHallsOfReflectionCreatureAI(boss_falric);
} }
@@ -16,11 +16,11 @@
*/ */
#include "CreatureScript.h" #include "CreatureScript.h"
#include "SpellScriptLoader.h"
#include "halls_of_reflection.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
#include "SpellScript.h" #include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "halls_of_reflection.h"
enum Yells enum Yells
{ {
@@ -41,40 +41,26 @@ enum Spells
enum Events enum Events
{ {
EVENT_NONE, EVENT_OBLITERATE = 1,
EVENT_OBLITERATE,
EVENT_WELL_OF_CORRUPTION, EVENT_WELL_OF_CORRUPTION,
EVENT_CORRUPTED_FLESH, EVENT_CORRUPTED_FLESH,
EVENT_SHARED_SUFFERING, EVENT_SHARED_SUFFERING,
}; };
class boss_marwyn : public CreatureScript struct boss_marwyn : public BossAI
{ {
public: boss_marwyn(Creature* creature) : BossAI(creature, DATA_MARWYN) { }
boss_marwyn() : CreatureScript("boss_marwyn") { }
struct boss_marwynAI : public ScriptedAI
{
boss_marwynAI(Creature* creature) : ScriptedAI(creature)
{
pInstance = creature->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
uint16 startFightTimer;
void Reset() override void Reset() override
{ {
startFightTimer = 0; BossAI::Reset();
_startingFight = false;
me->SetImmuneToAll(true); me->SetImmuneToAll(true);
events.Reset();
if (pInstance)
pInstance->SetData(DATA_MARWYN, NOT_STARTED);
} }
void JustEngagedWith(Unit* /*who*/) override void JustEngagedWith(Unit* who) override
{ {
BossAI::JustEngagedWith(who);
me->SetImmuneToAll(false); me->SetImmuneToAll(false);
events.ScheduleEvent(EVENT_OBLITERATE, 15s); events.ScheduleEvent(EVENT_OBLITERATE, 15s);
@@ -83,28 +69,22 @@ public:
events.ScheduleEvent(EVENT_SHARED_SUFFERING, 5s); events.ScheduleEvent(EVENT_SHARED_SUFFERING, 5s);
} }
void DoAction(int32 a) override void DoAction(int32 action) override
{ {
if (a == 1) if (action == 1)
{ {
Talk(SAY_AGGRO); Talk(SAY_AGGRO);
startFightTimer = 8000; _startingFight = true;
me->m_Events.AddEventAtOffset([this]()
{
_startingFight = false;
me->SetInCombatWithZone();
}, 8s);
} }
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (startFightTimer)
{
if (startFightTimer <= diff)
{
startFightTimer = 0;
me->SetInCombatWithZone();
}
else
startFightTimer -= diff;
}
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
@@ -118,38 +98,37 @@ public:
case EVENT_OBLITERATE: case EVENT_OBLITERATE:
if (me->IsWithinMeleeRange(me->GetVictim())) if (me->IsWithinMeleeRange(me->GetVictim()))
{ {
me->CastSpell(me->GetVictim(), SPELL_OBLITERATE, false); DoCastVictim(SPELL_OBLITERATE);
events.ScheduleEvent(EVENT_OBLITERATE, 15s); events.Repeat(15s);
} }
else else
events.ScheduleEvent(EVENT_OBLITERATE, 3s); events.Repeat(3s);
break; break;
case EVENT_WELL_OF_CORRUPTION: case EVENT_WELL_OF_CORRUPTION:
Talk(SAY_CORRUPTED_WELL); Talk(SAY_CORRUPTED_WELL);
if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true))
me->CastSpell(target, SPELL_WELL_OF_CORRUPTION, false); DoCast(target, SPELL_WELL_OF_CORRUPTION);
events.ScheduleEvent(EVENT_WELL_OF_CORRUPTION, 13s); events.Repeat(13s);
break; break;
case EVENT_CORRUPTED_FLESH: case EVENT_CORRUPTED_FLESH:
Talk(SAY_CORRUPTED_FLESH); Talk(SAY_CORRUPTED_FLESH);
me->CastSpell((Unit*)nullptr, SPELL_CORRUPTED_FLESH, false); DoCastAOE(SPELL_CORRUPTED_FLESH);
events.ScheduleEvent(EVENT_CORRUPTED_FLESH, 20s); events.Repeat(20s);
break; break;
case EVENT_SHARED_SUFFERING: case EVENT_SHARED_SUFFERING:
if (Unit* target = SelectTargetFromPlayerList(200.0f, 0, true)) if (Unit* target = SelectTargetFromPlayerList(200.0f, 0, true))
me->CastSpell(target, SPELL_SHARED_SUFFERING, true); DoCast(target, SPELL_SHARED_SUFFERING, true);
events.ScheduleEvent(EVENT_SHARED_SUFFERING, 15s); events.Repeat(15s);
break; break;
} }
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* killer) override
{ {
BossAI::JustDied(killer);
Talk(SAY_DEATH); Talk(SAY_DEATH);
if (pInstance)
pInstance->SetData(DATA_MARWYN, DONE);
} }
void KilledUnit(Unit* who) override void KilledUnit(Unit* who) override
@@ -160,16 +139,13 @@ public:
void EnterEvadeMode(EvadeReason why) override void EnterEvadeMode(EvadeReason why) override
{ {
ScriptedAI::EnterEvadeMode(why); BossAI::EnterEvadeMode(why);
if (startFightTimer) if (_startingFight)
Reset(); Reset();
} }
};
CreatureAI* GetAI(Creature* creature) const override private:
{ bool _startingFight{};
return GetHallsOfReflectionAI<boss_marwynAI>(creature);
}
}; };
enum SharedSufferingAura enum SharedSufferingAura
@@ -188,24 +164,36 @@ class spell_hor_shared_suffering_aura : public AuraScript
void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{ {
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_ENEMY_SPELL) // dispelled if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL)
if (Unit* caster = GetCaster()) return;
if (Map* map = caster->FindMap())
if (Aura* a = aurEff->GetBase()) Unit* caster = GetCaster();
{ if (!caster)
return;
Map* map = caster->FindMap();
if (!map)
return;
Aura* aura = aurEff->GetBase();
if (!aura)
return;
uint32 count = 0; uint32 count = 0;
uint32 ticks = 0; uint32 dmgPerTick = aura->GetSpellInfo()->Effects[0].BasePoints;
uint32 dmgPerTick = a->GetSpellInfo()->Effects[0].BasePoints;
Map::PlayerList const& pl = map->GetPlayers(); Map::PlayerList const& pl = map->GetPlayers();
for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) for (auto itr = pl.begin(); itr != pl.end(); ++itr)
if (Player* p = itr->GetSource()) if (Player* p = itr->GetSource())
if (p->IsAlive()) if (p->IsAlive())
++count; ++count;
ticks = (a->GetDuration() / int32(a->GetSpellInfo()->Effects[0].Amplitude)) + 1;
if (!count)
return;
uint32 ticks = (aura->GetDuration() / int32(aura->GetSpellInfo()->Effects[0].Amplitude)) + 1;
int32 dmg = (ticks * dmgPerTick) / count; int32 dmg = (ticks * dmgPerTick) / count;
caster->CastCustomSpell(GetTarget(), SPELL_SHARED_SUFFERING_DAMAGE, nullptr, &dmg, nullptr, true); caster->CastCustomSpell(GetTarget(), SPELL_SHARED_SUFFERING_DAMAGE, nullptr, &dmg, nullptr, true);
} }
}
void Register() override void Register() override
{ {
@@ -215,6 +203,6 @@ class spell_hor_shared_suffering_aura : public AuraScript
void AddSC_boss_marwyn() void AddSC_boss_marwyn()
{ {
new boss_marwyn(); RegisterHallsOfReflectionCreatureAI(boss_marwyn);
RegisterSpellScript(spell_hor_shared_suffering_aura); RegisterSpellScript(spell_hor_shared_suffering_aura);
} }
File diff suppressed because it is too large Load Diff
@@ -44,6 +44,7 @@ enum Data
ACTION_DELETE_ICE_WALL, ACTION_DELETE_ICE_WALL,
DATA_WAVE_NUMBER, DATA_WAVE_NUMBER,
DATA_LK_BATTLE,// in progress DATA_LK_BATTLE,// in progress
DATA_SHIP_CAPTAIN,
}; };
enum Creatures enum Creatures
@@ -108,6 +109,17 @@ enum BatteredHiltStatusFlags
BHSF_FINISHED = 4, BHSF_FINISHED = 4,
}; };
enum HoRPersistentData
{
PERSISTENT_DATA_INTRO,
PERSISTENT_DATA_FROSTSWORN_GENERAL,
PERSISTENT_DATA_LK_INTRO,
PERSISTENT_DATA_BATTERED_HILT,
PERSISTENT_DATA_COUNT
};
constexpr uint8 MAX_SPIRITUAL_REFLECTIONS = 5;
constexpr uint8 MAX_ICE_WALL_TARGETS = 4;
#define NUM_OF_TRASH 34 #define NUM_OF_TRASH 34
#define MAX_DIST_FROM_CENTER_IN_COMBAT 70.5f #define MAX_DIST_FROM_CENTER_IN_COMBAT 70.5f
#define MAX_DIST_FROM_CENTER_TO_START 40.0f #define MAX_DIST_FROM_CENTER_TO_START 40.0f
@@ -291,6 +303,11 @@ enum hMisc
// Battered Hilt - Summon Quel'Delar // Battered Hilt - Summon Quel'Delar
SPELL_SUMMON_EVIL_QUEL = 69966, SPELL_SUMMON_EVIL_QUEL = 69966,
SPELL_QUEL_DELAR_HATRED = 70300,
SPELL_FROSTMOURNE_ALTAR_GLOW = 70720,
SPELL_UTHER_HOLY_LIGHT_VISUAL = 73036,
SPELL_QUELDELAR_COMPULSION = 70013,
SPELL_QUELDELAR_WILL = 70698,
}; };
const uint32 allowedCompositions[8][5] = const uint32 allowedCompositions[8][5] =
@@ -390,4 +407,7 @@ inline AI* GetHallsOfReflectionAI(T* obj)
return GetInstanceAI<AI>(obj, HallsOfReflectionScriptName); return GetInstanceAI<AI>(obj, HallsOfReflectionScriptName);
} }
#define RegisterHallsOfReflectionCreatureAI(ai_name) \
RegisterCreatureAIWithFactory(ai_name, GetHallsOfReflectionAI)
#endif #endif