Scripts/Naxxramas: Update Noth to new model (#26387)

Co-authored-by: offl <offl@users.noreply.github.com>
This commit is contained in:
offl
2021-04-13 10:30:56 +03:00
committed by GitHub
parent cb82cded17
commit 7dc1d5c5a1

View File

@@ -73,268 +73,257 @@ const uint32 SummonGuardianSpells[N_GUARDIAN_SPELLS] = { 29239, 29256, 29268 };
#define SPELL_BLINK RAND(29208, 29209, 29210, 29211)
class boss_noth : public CreatureScript
struct boss_noth : public BossAI
{
public:
boss_noth() : CreatureScript("boss_noth") { }
struct boss_nothAI : public BossAI
boss_noth(Creature* creature) : BossAI(creature, BOSS_NOTH), balconyCount(0), justBlinked(false)
{
boss_nothAI(Creature* creature) : BossAI(creature, BOSS_NOTH), balconyCount(0), justBlinked(false)
std::copy(SummonWarriorSpells, SummonWarriorSpells + N_WARRIOR_SPELLS, _SummonWarriorSpells);
std::copy(SummonChampionSpells, SummonChampionSpells + N_CHAMPION_SPELLS, _SummonChampionSpells);
std::copy(SummonGuardianSpells, SummonGuardianSpells + N_GUARDIAN_SPELLS, _SummonGuardianSpells);
events.SetPhase(PHASE_NONE);
}
void EnterEvadeMode(EvadeReason why) override
{
// in case we reset during balcony phase
if (events.IsInPhase(PHASE_BALCONY))
DoCastAOE(SPELL_TELEPORT_BACK);
BossAI::EnterEvadeMode(why);
}
void Reset() override
{
_Reset();
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
balconyCount = 0;
events.SetPhase(PHASE_NONE);
justBlinked = false;
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
EnterPhaseGround();
}
void EnterPhaseGround()
{
events.SetPhase(PHASE_GROUND);
DoZoneInCombat();
if (!me->IsThreatened())
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
else
{
std::copy(SummonWarriorSpells, SummonWarriorSpells + N_WARRIOR_SPELLS, _SummonWarriorSpells);
std::copy(SummonChampionSpells, SummonChampionSpells + N_CHAMPION_SPELLS, _SummonChampionSpells);
std::copy(SummonGuardianSpells, SummonGuardianSpells + N_GUARDIAN_SPELLS, _SummonGuardianSpells);
events.SetPhase(PHASE_NONE);
}
void EnterEvadeMode(EvadeReason why) override
{
// in case we reset during balcony phase
if (events.IsInPhase(PHASE_BALCONY))
DoCastAOE(SPELL_TELEPORT_BACK);
BossAI::EnterEvadeMode(why);
}
void Reset() override
{
_Reset();
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
balconyCount = 0;
events.SetPhase(PHASE_NONE);
justBlinked = false;
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
EnterPhaseGround();
}
void EnterPhaseGround()
{
events.SetPhase(PHASE_GROUND);
DoZoneInCombat();
if (!me->IsThreatened())
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
else
uint8 timeGround;
switch (balconyCount)
{
uint8 timeGround;
switch (balconyCount)
{
case 0:
timeGround = 90;
break;
case 1:
timeGround = 110;
break;
case 2:
default:
timeGround = 180;
}
events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, Seconds(2), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_BALCONY, Seconds(timeGround), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_CURSE, randtime(Seconds(10), Seconds(25)), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_WARRIOR, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
events.ScheduleEvent(EVENT_BLINK, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
case 0:
timeGround = 90;
break;
case 1:
timeGround = 110;
break;
case 2:
default:
timeGround = 180;
}
events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, Seconds(2), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_BALCONY, Seconds(timeGround), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_CURSE, randtime(Seconds(10), Seconds(25)), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_WARRIOR, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
events.ScheduleEvent(EVENT_BLINK, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
}
}
void KilledUnit(Unit* victim) override
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->setActive(true);
summon->SetFarVisible(true);
summon->AI()->DoZoneInCombat();
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
}
void DamageTaken(Unit* /*who*/, uint32& damage) override // prevent noth from somehow dying in the balcony phase
{
if (!events.IsInPhase(PHASE_BALCONY))
return;
if (damage < me->GetHealth())
return;
me->SetHealth(1u);
damage = 0u;
}
void HandleSummon(uint32* spellsList, const uint8 nSpells, uint8 num)
{ // this ensures we do not spawn two mobs using the same spell (<=> in the same position) if we can help it
while (num)
for (uint8 it = 0; it < nSpells && num; ++it)
{
num--;
uint8 selected = urand(it, nSpells - 1);
DoCastAOE(spellsList[selected]);
if (selected != it) // shuffle the selected into the part of the array that is no longer being searched
std::swap(spellsList[selected], spellsList[it]);
}
}
void CastSummon(uint8 nWarrior, uint8 nChampion, uint8 nGuardian)
{
HandleSummon(_SummonWarriorSpells, N_WARRIOR_SPELLS, nWarrior);
HandleSummon(_SummonChampionSpells, N_CHAMPION_SPELLS, nChampion);
HandleSummon(_SummonGuardianSpells, N_GUARDIAN_SPELLS, nGuardian);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->setActive(true);
summon->SetFarVisible(true);
summon->AI()->DoZoneInCombat();
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
}
void DamageTaken(Unit* /*who*/, uint32& damage) override // prevent noth from somehow dying in the balcony phase
{
if (!events.IsInPhase(PHASE_BALCONY))
return;
if (damage < me->GetHealth())
return;
me->SetHealth(1u);
damage = 0u;
}
void HandleSummon(uint32* spellsList, const uint8 nSpells, uint8 num)
{ // this ensures we do not spawn two mobs using the same spell (<=> in the same position) if we can help it
while (num)
for (uint8 it = 0; it < nSpells && num; ++it)
switch (eventId)
{
case EVENT_CURSE:
{
num--;
uint8 selected = urand(it, nSpells - 1);
DoCastAOE(spellsList[selected]);
if (selected != it) // shuffle the selected into the part of the array that is no longer being searched
std::swap(spellsList[selected], spellsList[it]);
DoCastAOE(SPELL_CURSE);
events.Repeat(randtime(Seconds(50), Seconds(70)));
break;
}
}
case EVENT_WARRIOR:
Talk(SAY_SUMMON);
Talk(EMOTE_SUMMON);
void CastSummon(uint8 nWarrior, uint8 nChampion, uint8 nGuardian)
{
HandleSummon(_SummonWarriorSpells, N_WARRIOR_SPELLS, nWarrior);
HandleSummon(_SummonChampionSpells, N_CHAMPION_SPELLS, nChampion);
HandleSummon(_SummonGuardianSpells, N_GUARDIAN_SPELLS, nGuardian);
}
CastSummon(RAID_MODE(2, 3), 0, 0);
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Repeat(Seconds(40));
break;
case EVENT_BLINK:
DoCastAOE(SPELL_CRIPPLE, true);
DoCastAOE(SPELL_BLINK);
ResetThreatList();
justBlinked = true;
events.Update(diff);
events.Repeat(Seconds(40));
break;
case EVENT_BALCONY:
events.SetPhase(PHASE_BALCONY);
me->SetReactState(REACT_PASSIVE);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->AttackStop();
me->StopMoving();
me->RemoveAllAuras();
events.ScheduleEvent(EVENT_BALCONY_TELEPORT, Seconds(3), 0, PHASE_BALCONY);
events.ScheduleEvent(EVENT_WAVE, randtime(Seconds(5), Seconds(8)), 0, PHASE_BALCONY);
uint8 timeBalcony;
switch (balconyCount)
{
case 0:
timeBalcony = 70;
break;
case 1:
timeBalcony = 97;
break;
case 2:
default:
timeBalcony = 120;
break;
}
events.ScheduleEvent(EVENT_GROUND, Seconds(timeBalcony), 0, PHASE_BALCONY);
break;
case EVENT_BALCONY_TELEPORT:
Talk(EMOTE_TELEPORT_1);
DoCastAOE(SPELL_TELEPORT);
break;
case EVENT_WAVE:
Talk(EMOTE_SUMMON_WAVE);
switch (balconyCount)
{
case 0:
CastSummon(0, RAID_MODE(2, 4), 0);
break;
case 1:
CastSummon(0, RAID_MODE(1, 2), RAID_MODE(1, 2));
break;
case 2:
CastSummon(0, 0, RAID_MODE(2, 4));
break;
default:
CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10));
break;
}
events.Repeat(randtime(Seconds(30), Seconds(45)));
break;
case EVENT_GROUND:
++balconyCount;
DoCastAOE(SPELL_TELEPORT_BACK);
Talk(EMOTE_TELEPORT_2);
EnterPhaseGround();
break;
case EVENT_GROUND_ATTACKABLE:
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CURSE:
{
DoCastAOE(SPELL_CURSE);
events.Repeat(randtime(Seconds(50), Seconds(70)));
break;
}
case EVENT_WARRIOR:
Talk(SAY_SUMMON);
Talk(EMOTE_SUMMON);
CastSummon(RAID_MODE(2, 3), 0, 0);
events.Repeat(Seconds(40));
break;
case EVENT_BLINK:
DoCastAOE(SPELL_CRIPPLE, true);
DoCastAOE(SPELL_BLINK);
ResetThreatList();
justBlinked = true;
events.Repeat(Seconds(40));
break;
case EVENT_BALCONY:
events.SetPhase(PHASE_BALCONY);
me->SetReactState(REACT_PASSIVE);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->AttackStop();
me->StopMoving();
me->RemoveAllAuras();
events.ScheduleEvent(EVENT_BALCONY_TELEPORT, Seconds(3), 0, PHASE_BALCONY);
events.ScheduleEvent(EVENT_WAVE, randtime(Seconds(5), Seconds(8)), 0, PHASE_BALCONY);
uint8 timeBalcony;
switch (balconyCount)
{
case 0:
timeBalcony = 70;
break;
case 1:
timeBalcony = 97;
break;
case 2:
default:
timeBalcony = 120;
break;
}
events.ScheduleEvent(EVENT_GROUND, Seconds(timeBalcony), 0, PHASE_BALCONY);
break;
case EVENT_BALCONY_TELEPORT:
Talk(EMOTE_TELEPORT_1);
DoCastAOE(SPELL_TELEPORT);
break;
case EVENT_WAVE:
Talk(EMOTE_SUMMON_WAVE);
switch (balconyCount)
{
case 0:
CastSummon(0, RAID_MODE(2, 4), 0);
break;
case 1:
CastSummon(0, RAID_MODE(1, 2), RAID_MODE(1, 2));
break;
case 2:
CastSummon(0, 0, RAID_MODE(2, 4));
break;
default:
CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10));
break;
}
events.Repeat(randtime(Seconds(30), Seconds(45)));
break;
case EVENT_GROUND:
++balconyCount;
DoCastAOE(SPELL_TELEPORT_BACK);
Talk(EMOTE_TELEPORT_2);
EnterPhaseGround();
break;
case EVENT_GROUND_ATTACKABLE:
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
if (events.IsInPhase(PHASE_GROUND))
{
/* workaround for movechase breaking after blinking
without this noth would just stand there unless his current target moves */
if (justBlinked && me->GetVictim() && !me->IsWithinMeleeRange(me->EnsureVictim()))
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveChase(me->EnsureVictim());
justBlinked = false;
}
else
DoMeleeAttackIfReady();
}
}
private:
uint32 balconyCount;
bool justBlinked;
uint32 _SummonWarriorSpells[N_WARRIOR_SPELLS];
uint32 _SummonChampionSpells[N_CHAMPION_SPELLS];
uint32 _SummonGuardianSpells[N_GUARDIAN_SPELLS];
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetNaxxramasAI<boss_nothAI>(creature);
if (events.IsInPhase(PHASE_GROUND))
{
/* workaround for movechase breaking after blinking
without this noth would just stand there unless his current target moves */
if (justBlinked && me->GetVictim() && !me->IsWithinMeleeRange(me->EnsureVictim()))
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveChase(me->EnsureVictim());
justBlinked = false;
}
else
DoMeleeAttackIfReady();
}
}
private:
uint32 balconyCount;
bool justBlinked;
uint32 _SummonWarriorSpells[N_WARRIOR_SPELLS];
uint32 _SummonChampionSpells[N_CHAMPION_SPELLS];
uint32 _SummonGuardianSpells[N_GUARDIAN_SPELLS];
};
void AddSC_boss_noth()
{
new boss_noth();
RegisterNaxxramasCreatureAI(boss_noth);
}