Core/Scripts: Implement generic script loaders (and script registry macros) to greatly reduce code duplication (#19526)

This commit is contained in:
Seyden
2017-07-20 00:15:02 +02:00
committed by Treeston
parent a6e46c1c2e
commit a9174d5eb7
4 changed files with 369 additions and 327 deletions

View File

@@ -205,10 +205,10 @@ class TC_GAME_API SpellScriptLoader : public ScriptObject
public:
// Should return a fully valid SpellScript pointer.
virtual SpellScript* GetSpellScript() const { return NULL; }
virtual SpellScript* GetSpellScript() const { return nullptr; }
// Should return a fully valid AuraScript pointer.
virtual AuraScript* GetAuraScript() const { return NULL; }
virtual AuraScript* GetAuraScript() const { return nullptr; }
};
class TC_GAME_API ServerScript : public ScriptObject
@@ -1175,6 +1175,70 @@ class TC_GAME_API ScriptMgr
std::string _currentContext;
};
template <class S>
class GenericSpellScriptLoader : public SpellScriptLoader
{
public:
GenericSpellScriptLoader(char const* name) : SpellScriptLoader(name) { }
SpellScript* GetSpellScript() const override { return new S(); }
};
#define RegisterSpellScript(spell_script) new GenericSpellScriptLoader<spell_script>(#spell_script)
template <class A>
class GenericAuraScriptLoader : public SpellScriptLoader
{
public:
GenericAuraScriptLoader(char const* name) : SpellScriptLoader(name) { }
AuraScript* GetAuraScript() const override { return new A(); }
};
#define RegisterAuraScript(aura_script) new GenericAuraScriptLoader<aura_script>(#aura_script)
template <class S, class A>
class GenericSpellAndAuraScriptLoader : public SpellScriptLoader
{
public:
GenericSpellAndAuraScriptLoader(char const* name) : SpellScriptLoader(name) { }
SpellScript* GetSpellScript() const override { return new S(); }
AuraScript* GetAuraScript() const override { return new A(); }
};
#define RegisterSpellAndAuraScriptPair(spell_script, aura_script) new GenericSpellAndAuraScriptLoader<spell_script, aura_script>(#spell_script)
template <class AI>
class GenericCreatureScript : public CreatureScript
{
public:
GenericCreatureScript(char const* name) : CreatureScript(name) { }
CreatureAI* GetAI(Creature* me) const override { return new AI(me); }
};
#define RegisterCreatureAI(ai_name) new GenericCreatureScript<ai_name>(#ai_name)
template <class AI, AI*(*AIFactory)(Creature*)>
class FactoryCreatureScript : public CreatureScript
{
public:
FactoryCreatureScript(char const* name) : CreatureScript(name) { }
CreatureAI* GetAI(Creature* me) const override { return AIFactory(me); }
};
#define RegisterCreatureAIWithFactory(ai_name, factory_fn) new FactoryCreatureScript<ai_name, &factory_fn>(#ai_name)
template <class AI>
class GenericGameObjectScript : public GameObjectScript
{
public:
GenericGameObjectScript(char const* name) : GameObjectScript(name) { }
GameObjectAI* GetAI(GameObject* go) const override { return new AI(go); }
};
#define RegisterGameObjectAI(ai_name) new GenericGameObjectScript<ai_name>(#ai_name)
template <class AI>
class GenericAreaTriggerEntityScript : public AreaTriggerEntityScript
{
public:
GenericAreaTriggerEntityScript(char const* name) : AreaTriggerEntityScript(name) { }
AreaTriggerAI* GetAI(AreaTrigger* at) const override { return new AI(at); }
};
#define RegisterAreaTriggerAI(ai_name) new GenericAreaTriggerEntityScript<ai_name>(#ai_name)
#define sScriptMgr ScriptMgr::instance()
#endif

View File

@@ -160,320 +160,308 @@ enum ThaddiusSpells
SPELL_NEGATIVE_CHARGE_AMP = 29660,
};
class boss_thaddius : public CreatureScript
struct boss_thaddius : public BossAI
{
public:
boss_thaddius() : CreatureScript("boss_thaddius") { }
public:
boss_thaddius(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningUnlocked(false), ballLightningEnabled(false), shockingEligibility(true) {}
CreatureAI* GetAI(Creature* creature) const override
{
return GetNaxxramasAI<boss_thaddiusAI>(creature);
}
struct boss_thaddiusAI : public BossAI
{
public:
boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningUnlocked(false), ballLightningEnabled(false), shockingEligibility(true) {}
void InitializeAI() override
void InitializeAI() override
{
if (instance->GetBossState(BOSS_THADDIUS) != DONE)
{
if (instance->GetBossState(BOSS_THADDIUS) != DONE)
events.SetPhase(PHASE_NOT_ENGAGED);
SetCombatMovement(false);
// initialize everything properly, and ensure that the coils are loaded by the time we initialize
BeginResetEncounter(true);
}
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
void Reset() override { }
void EnterEvadeMode(EvadeReason why) override
{
if (!ballLightningEnabled && why == EVADE_REASON_NO_HOSTILES)
{
ballLightningEnabled = true;
return; // try again
}
if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive()))
BeginResetEncounter();
}
bool CanAIAttack(Unit const* who) const override
{
if (ballLightningEnabled || me->IsWithinMeleeRange(who))
return BossAI::CanAIAttack(who);
else
return false;
}
void JustRespawned() override
{
if (events.IsInPhase(PHASE_RESETTING))
ResetEncounter();
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
me->setActive(false);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->setActive(false);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->setActive(false);
Talk(SAY_DEATH);
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_RESET_ENCOUNTER_TIMER:
if (events.IsInPhase(PHASE_RESETTING))
ResetEncounter();
break;
case ACTION_FEUGEN_RESET:
case ACTION_STALAGG_RESET:
if (!events.IsInPhase(PHASE_NOT_ENGAGED) && !events.IsInPhase(PHASE_RESETTING))
BeginResetEncounter();
break;
case ACTION_FEUGEN_AGGRO:
case ACTION_STALAGG_AGGRO:
if (events.IsInPhase(PHASE_RESETTING))
{
BeginResetEncounter();
return;
}
if (!events.IsInPhase(PHASE_NOT_ENGAGED))
return;
events.SetPhase(PHASE_PETS);
shockingEligibility = true;
if (!instance->CheckRequiredBosses(BOSS_THADDIUS))
{
BeginResetEncounter();
return;
}
instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS);
me->setActive(true);
DoZoneInCombat();
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->setActive(true);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->setActive(true);
break;
case ACTION_FEUGEN_DIED:
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX);
feugenAlive = false;
if (stalaggAlive)
events.ScheduleEvent(EVENT_REVIVE_FEUGEN, Seconds(5), 0, PHASE_PETS);
else
Transition();
break;
case ACTION_STALAGG_DIED:
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX);
stalaggAlive = false;
if (feugenAlive)
events.ScheduleEvent(EVENT_REVIVE_STALAGG, Seconds(5), 0, PHASE_PETS);
else
Transition();
break;
case ACTION_POLARITY_CROSSED:
shockingEligibility = false;
break;
default:
break;
}
}
uint32 GetData(uint32 id) const override
{
return (id == DATA_POLARITY_CROSSED && shockingEligibility) ? 1u : 0u;
}
void Transition() // initiate transition between pet phase and thaddius phase
{
events.SetPhase(PHASE_TRANSITION);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_TRANSITION_1, Seconds(10), 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_2, Seconds(12), 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION);
}
void BeginResetEncounter(bool initial = false)
{
if (instance->GetBossState(BOSS_THADDIUS) == DONE)
return;
if (events.IsInPhase(PHASE_RESETTING))
return;
// remove polarity shift debuffs on reset
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY);
me->DespawnOrUnsummon();
me->SetRespawnTime(initial ? 5 : 30);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
events.SetPhase(PHASE_RESETTING);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
me->setActive(false);
}
void ResetEncounter()
{
feugenAlive = true;
stalaggAlive = true;
_Reset();
events.SetPhase(PHASE_NOT_ENGAGED);
me->SetReactState(REACT_PASSIVE);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_RESET_ENCOUNTER);
}
void UpdateAI(uint32 diff) override
{
if (events.IsInPhase(PHASE_NOT_ENGAGED))
return;
if (events.IsInPhase(PHASE_THADDIUS) && !UpdateVictim())
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
events.SetPhase(PHASE_NOT_ENGAGED);
SetCombatMovement(false);
// initialize everything properly, and ensure that the coils are loaded by the time we initialize
BeginResetEncounter(true);
}
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
void Reset() override { }
void EnterEvadeMode(EvadeReason why) override
{
if (!ballLightningEnabled && why == EVADE_REASON_NO_HOSTILES)
{
ballLightningEnabled = true;
return; // try again
}
if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive()))
BeginResetEncounter();
}
bool CanAIAttack(Unit const* who) const override
{
if (ballLightningEnabled || me->IsWithinMeleeRange(who))
return BossAI::CanAIAttack(who);
else
return false;
}
void JustRespawned() override
{
if (events.IsInPhase(PHASE_RESETTING))
ResetEncounter();
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
me->setActive(false);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->setActive(false);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->setActive(false);
Talk(SAY_DEATH);
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_RESET_ENCOUNTER_TIMER:
if (events.IsInPhase(PHASE_RESETTING))
ResetEncounter();
case EVENT_REVIVE_FEUGEN:
feugenAlive = true;
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_FEUGEN_REVIVED);
break;
case ACTION_FEUGEN_RESET:
case ACTION_STALAGG_RESET:
if (!events.IsInPhase(PHASE_NOT_ENGAGED) && !events.IsInPhase(PHASE_RESETTING))
BeginResetEncounter();
case EVENT_REVIVE_STALAGG:
stalaggAlive = true;
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_STALAGG_REVIVED);
break;
case ACTION_FEUGEN_AGGRO:
case ACTION_STALAGG_AGGRO:
if (events.IsInPhase(PHASE_RESETTING))
{
BeginResetEncounter();
return;
}
if (!events.IsInPhase(PHASE_NOT_ENGAGED))
return;
events.SetPhase(PHASE_PETS);
case EVENT_TRANSITION_1: // tesla coils overload
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_TRANSITION);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_TRANSITION);
break;
case EVENT_TRANSITION_2: // tesla coils shock thaddius
me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_TRANSITION_2);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_TRANSITION_2);
break;
case EVENT_TRANSITION_3: // thaddius becomes active
me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
ballLightningUnlocked = false;
me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_AGGRESSIVE);
shockingEligibility = true;
if (!instance->CheckRequiredBosses(BOSS_THADDIUS))
{
BeginResetEncounter();
return;
}
instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS);
me->setActive(true);
DoZoneInCombat();
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->setActive(true);
if (Unit* closest = SelectTarget(SELECT_TARGET_NEAREST, 0, 500.0f))
AttackStart(closest);
else // if there is no nearest target, then there is no target, meaning we should reset
{
BeginResetEncounter();
return;
}
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->setActive(true);
break;
case ACTION_FEUGEN_DIED:
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX);
feugenAlive = false;
if (stalaggAlive)
events.ScheduleEvent(EVENT_REVIVE_FEUGEN, Seconds(5), 0, PHASE_PETS);
else
Transition();
break;
case ACTION_STALAGG_DIED:
feugen->AI()->DoAction(ACTION_TRANSITION_3);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX);
stalaggAlive = false;
if (feugenAlive)
events.ScheduleEvent(EVENT_REVIVE_STALAGG, Seconds(5), 0, PHASE_PETS);
else
Transition();
stalagg->AI()->DoAction(ACTION_TRANSITION_3);
events.SetPhase(PHASE_THADDIUS);
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, Seconds(5), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, Seconds(10), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_CHAIN, randtime(Seconds(10), Seconds(20)), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_BERSERK, Minutes(6), 0, PHASE_THADDIUS);
break;
case ACTION_POLARITY_CROSSED:
shockingEligibility = false;
case EVENT_ENABLE_BALL_LIGHTNING:
ballLightningUnlocked = true;
break;
case EVENT_SHIFT:
me->CastStop(); // shift overrides all other spells
DoCastAOE(SPELL_POLARITY_SHIFT);
events.ScheduleEvent(EVENT_SHIFT_TALK, Seconds(3), PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, Seconds(30), PHASE_THADDIUS);
break;
case EVENT_SHIFT_TALK:
Talk(SAY_ELECT);
Talk(EMOTE_POLARITY_SHIFTED);
break;
case EVENT_CHAIN:
if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over
events.Repeat(Seconds(3));
else
{
me->CastStop();
DoCastVictim(SPELL_CHAIN_LIGHTNING);
events.Repeat(randtime(Seconds(10), Seconds(20)));
}
break;
case EVENT_BERSERK:
me->CastStop();
DoCast(me, SPELL_BERSERK);
break;
default:
break;
}
}
uint32 GetData(uint32 id) const override
if (events.IsInPhase(PHASE_THADDIUS) && !me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady())
{
return (id == DATA_POLARITY_CROSSED && shockingEligibility) ? 1u : 0u;
}
void Transition() // initiate transition between pet phase and thaddius phase
{
events.SetPhase(PHASE_TRANSITION);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_TRANSITION_1, Seconds(10), 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_2, Seconds(12), 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION);
}
void BeginResetEncounter(bool initial = false)
{
if (instance->GetBossState(BOSS_THADDIUS) == DONE)
return;
if (events.IsInPhase(PHASE_RESETTING))
return;
// remove polarity shift debuffs on reset
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY);
me->DespawnOrUnsummon();
me->SetRespawnTime(initial ? 5 : 30);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
events.SetPhase(PHASE_RESETTING);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
me->setActive(false);
}
void ResetEncounter()
{
feugenAlive = true;
stalaggAlive = true;
_Reset();
events.SetPhase(PHASE_NOT_ENGAGED);
me->SetReactState(REACT_PASSIVE);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_RESET_ENCOUNTER);
}
void UpdateAI(uint32 diff) override
{
if (events.IsInPhase(PHASE_NOT_ENGAGED))
return;
if (events.IsInPhase(PHASE_THADDIUS) && !UpdateVictim())
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
if (me->IsWithinMeleeRange(me->GetVictim()))
{
switch (eventId)
{
case EVENT_REVIVE_FEUGEN:
feugenAlive = true;
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_FEUGEN_REVIVED);
break;
case EVENT_REVIVE_STALAGG:
stalaggAlive = true;
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_STALAGG_REVIVED);
break;
case EVENT_TRANSITION_1: // tesla coils overload
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_TRANSITION);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_TRANSITION);
break;
case EVENT_TRANSITION_2: // tesla coils shock thaddius
me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_TRANSITION_2);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_TRANSITION_2);
break;
case EVENT_TRANSITION_3: // thaddius becomes active
me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
ballLightningUnlocked = false;
me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
if (Unit* closest = SelectTarget(SELECT_TARGET_NEAREST, 0, 500.0f))
AttackStart(closest);
else // if there is no nearest target, then there is no target, meaning we should reset
{
BeginResetEncounter();
return;
}
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_TRANSITION_3);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_TRANSITION_3);
events.SetPhase(PHASE_THADDIUS);
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, Seconds(5), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, Seconds(10), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_CHAIN, randtime(Seconds(10), Seconds(20)), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_BERSERK, Minutes(6), 0, PHASE_THADDIUS);
break;
case EVENT_ENABLE_BALL_LIGHTNING:
ballLightningUnlocked = true;
break;
case EVENT_SHIFT:
me->CastStop(); // shift overrides all other spells
DoCastAOE(SPELL_POLARITY_SHIFT);
events.ScheduleEvent(EVENT_SHIFT_TALK, Seconds(3), PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, Seconds(30), PHASE_THADDIUS);
break;
case EVENT_SHIFT_TALK:
Talk(SAY_ELECT);
Talk(EMOTE_POLARITY_SHIFTED);
break;
case EVENT_CHAIN:
if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over
events.Repeat(Seconds(3));
else
{
me->CastStop();
DoCastVictim(SPELL_CHAIN_LIGHTNING);
events.Repeat(randtime(Seconds(10), Seconds(20)));
}
break;
case EVENT_BERSERK:
me->CastStop();
DoCast(me, SPELL_BERSERK);
break;
default:
break;
}
}
if (events.IsInPhase(PHASE_THADDIUS) && !me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady())
{
if (me->IsWithinMeleeRange(me->GetVictim()))
{
ballLightningEnabled = false;
DoMeleeAttackIfReady();
}
else if (ballLightningUnlocked)
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
DoCast(target, SPELL_BALL_LIGHTNING);
ballLightningEnabled = false;
DoMeleeAttackIfReady();
}
else if (ballLightningUnlocked)
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
DoCast(target, SPELL_BALL_LIGHTNING);
}
}
private:
bool stalaggAlive;
bool feugenAlive;
bool ballLightningUnlocked; // whether the initial ball lightning grace period has expired and we should proceed to exterminate with extreme prejudice
bool ballLightningEnabled; // switch that is flipped to true if we try to evade due to no eligible targets in melee range
bool shockingEligibility;
};
private:
bool stalaggAlive;
bool feugenAlive;
bool ballLightningUnlocked; // whether the initial ball lightning grace period has expired and we should proceed to exterminate with extreme prejudice
bool ballLightningEnabled; // switch that is flipped to true if we try to evade due to no eligible targets in melee range
bool shockingEligibility;
};
class npc_stalagg : public CreatureScript
@@ -1307,7 +1295,7 @@ class achievement_thaddius_shocking : public AchievementCriteriaScript
void AddSC_boss_thaddius()
{
new boss_thaddius();
RegisterNaxxramasCreatureAI(boss_thaddius);
new npc_stalagg();
new npc_feugen();
new npc_tesla();

View File

@@ -223,5 +223,6 @@ inline AI* GetNaxxramasAI(T* obj)
{
return GetInstanceAI<AI>(obj, NaxxramasScriptName);
}
#define RegisterNaxxramasCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetNaxxramasAI)
#endif

View File

@@ -3305,43 +3305,32 @@ enum TeachLanguage
SPELL_LEARN_GOBLIN_BINARY = 50246,
};
class spell_item_teach_language : public SpellScriptLoader
class spell_item_teach_language : public SpellScript
{
public:
spell_item_teach_language() : SpellScriptLoader("spell_item_teach_language") { }
PrepareSpellScript(spell_item_teach_language);
class spell_item_teach_language_SpellScript : public SpellScript
{
PrepareSpellScript(spell_item_teach_language_SpellScript);
bool Load() override
{
return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
bool Load() override
{
return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_LEARN_GNOMISH_BINARY, SPELL_LEARN_GOBLIN_BINARY });
}
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_LEARN_GNOMISH_BINARY, SPELL_LEARN_GOBLIN_BINARY });
}
void HandleDummy(SpellEffIndex /* effIndex */)
{
Player* caster = GetCaster()->ToPlayer();
void HandleDummy(SpellEffIndex /* effIndex */)
{
Player* caster = GetCaster()->ToPlayer();
if (roll_chance_i(34))
caster->CastSpell(caster, caster->GetTeam() == ALLIANCE ? SPELL_LEARN_GNOMISH_BINARY : SPELL_LEARN_GOBLIN_BINARY, true);
}
if (roll_chance_i(34))
caster->CastSpell(caster, caster->GetTeam() == ALLIANCE ? SPELL_LEARN_GNOMISH_BINARY : SPELL_LEARN_GOBLIN_BINARY, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_item_teach_language_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_item_teach_language_SpellScript();
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_item_teach_language::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
enum RocketBoots
@@ -4608,7 +4597,7 @@ void AddSC_item_spell_scripts()
new spell_item_impale_leviroth();
new spell_item_brewfest_mount_transformation();
new spell_item_nitro_boots();
new spell_item_teach_language();
RegisterSpellScript(spell_item_teach_language);
new spell_item_rocket_boots();
new spell_item_pygmy_oil();
new spell_item_unusual_compass();