refactor(Scripts/Karazhan): Update Netherspite (#16756)

* init

* .

* .

* review

* .

* Remove whitespace at the end of the lines

* .

* .
This commit is contained in:
Eddy Vega
2023-07-27 06:03:19 -06:00
committed by GitHub
parent 642bd1936b
commit 750e0b4c23
2 changed files with 248 additions and 288 deletions

View File

@@ -121,8 +121,6 @@ struct boss_moroes : public BossAI
context.Repeat(5s);
}).Schedule(1min, 2min, GROUP_PRECOMBAT_TALK, [this](TaskContext context)
{
//this was not scheduled in the previous commit
//does this have to be removed?
Talk(SAY_OUT_OF_COMBAT);
context.Repeat(1min, 2min);
});

View File

@@ -15,13 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Boss_Netherspite
SD%Complete: 90
SDComment: Not sure about timing and portals placing
SDCategory: Karazhan
EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
@@ -29,19 +22,35 @@ EndScriptData */
#include "SpellAuraEffects.h"
#include "SpellScript.h"
enum Netherspite
enum Emotes
{
EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1,
EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1
};
SPELL_NETHERBURN_AURA = 30522,
SPELL_VOIDZONE = 37063,
SPELL_NETHER_INFUSION = 38688,
SPELL_NETHERBREATH = 38523,
SPELL_BANISH_VISUAL = 39833,
SPELL_BANISH_ROOT = 42716,
SPELL_EMPOWERMENT = 38549,
SPELL_NETHERSPITE_ROAR = 38684,
enum Spells
{
SPELL_NETHERBURN_AURA = 30522,
SPELL_VOIDZONE = 37063,
SPELL_NETHER_INFUSION = 38688,
SPELL_NETHERBREATH = 38523,
SPELL_BANISH_VISUAL = 39833,
SPELL_BANISH_ROOT = 42716,
SPELL_EMPOWERMENT = 38549,
SPELL_NETHERSPITE_ROAR = 38684
};
enum Portals
{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
enum Groups
{
PORTAL_PHASE = 0,
VANISH_PHASE = 1
};
const float PortalCoord[3][3] =
@@ -51,302 +60,255 @@ const float PortalCoord[3][3] =
{-11094.493164f, -1591.969238f, 279.949188f} // Back side
};
enum Netherspite_Portal
{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
const uint32 PortalID[3] = {17369, 17367, 17368};
const uint32 PortalID[3] = {17369, 17367, 17368};
const uint32 PortalVisual[3] = {30487, 30490, 30491};
const uint32 PortalBeam[3] = {30465, 30464, 30463};
const uint32 PlayerBuff[3] = {30421, 30422, 30423};
const uint32 NetherBuff[3] = {30466, 30467, 30468};
const uint32 PortalBeam[3] = {30465, 30464, 30463};
const uint32 PlayerBuff[3] = {30421, 30422, 30423};
const uint32 NetherBuff[3] = {30466, 30467, 30468};
const uint32 PlayerDebuff[3] = {38637, 38638, 38639};
class boss_netherspite : public CreatureScript
struct boss_netherspite : public BossAI
{
public:
boss_netherspite() : CreatureScript("boss_netherspite") { }
boss_netherspite(Creature* creature) : BossAI(creature, DATA_NETHERSPITE) {}
CreatureAI* GetAI(Creature* creature) const override
bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker
{
return GetKarazhanAI<boss_netherspiteAI>(creature);
if (!u1 || !u2 || !target)
return false;
float xn, yn, xp, yp, xh, yh;
xn = u1->GetPositionX();
yn = u1->GetPositionY();
xp = u2->GetPositionX();
yp = u2->GetPositionY();
xh = target->GetPositionX();
yh = target->GetPositionY();
// check if target is between (not checking distance from the beam yet)
if (dist(xn, yn, xh, yh) >= dist(xn, yn, xp, yp) || dist(xp, yp, xh, yh) >= dist(xn, yn, xp, yp))
return false;
// check distance from the beam
return (std::abs((xn - xp) * yh + (yp - yn) * xh - xn * yp + xp * yn) / dist(xn, yn, xp, yp) < 1.5f);
}
struct boss_netherspiteAI : public ScriptedAI
float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{
boss_netherspiteAI(Creature* creature) : ScriptedAI(creature)
return std::sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb));
}
void Reset() override
{
BossAI::Reset();
berserk = false;
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
{
uint8 r = rand() % 4;
uint8 pos[3];
pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0);
pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1));
pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i)
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
bool PortalPhase;
bool Berserk;
uint32 PhaseTimer; // timer for phase switching
uint32 VoidZoneTimer;
uint32 NetherInfusionTimer; // berserking timer
uint32 NetherbreathTimer;
uint32 EmpowermentTimer;
uint32 PortalTimer; // timer for beam checking
ObjectGuid PortalGUID[3]; // guid's of portals
ObjectGuid BeamerGUID[3]; // guid's of auxiliary beaming portals
ObjectGuid BeamTarget[3]; // guid's of portals' current targets
bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker
{
if (!u1 || !u2 || !target)
return false;
float xn, yn, xp, yp, xh, yh;
xn = u1->GetPositionX();
yn = u1->GetPositionY();
xp = u2->GetPositionX();
yp = u2->GetPositionY();
xh = target->GetPositionX();
yh = target->GetPositionY();
// check if target is between (not checking distance from the beam yet)
if (dist(xn, yn, xh, yh) >= dist(xn, yn, xp, yp) || dist(xp, yp, xh, yh) >= dist(xn, yn, xp, yp))
return false;
// check distance from the beam
return (std::abs((xn - xp) * yh + (yp - yn) * xh - xn * yp + xp * yn) / dist(xn, yn, xp, yp) < 1.5f);
}
float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{
return std::sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb));
}
void Reset() override
{
Berserk = false;
NetherInfusionTimer = 540000;
VoidZoneTimer = 15000;
NetherbreathTimer = 3000;
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
{
uint8 r = rand() % 4;
uint8 pos[3];
pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0);
pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1));
pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i)
if (Creature* portal = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
PortalGUID[i] = portal->GetGUID();
portal->AddAura(PortalVisual[i], portal);
}
}
void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
if (Creature* portal = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
portal->DisappearAndDie();
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
portal->DisappearAndDie();
PortalGUID[i].Clear();
BeamTarget[i].Clear();
PortalGUID[i] = portal->GetGUID();
portal->AddAura(PortalVisual[i], portal);
}
}
}
void UpdatePortals() // Here we handle the beams' behavior
void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j) // j = color
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j]))
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
{
portal->DisappearAndDie();
}
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
{
portal->DisappearAndDie();
}
PortalGUID[i].Clear();
BeamTarget[i].Clear();
}
}
void UpdatePortals() // Here we handle the beams' behavior
{
for (int j = 0; j < 3; ++j) // j = color
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j]))
{
// the one who's been cast upon before
Unit* current = ObjectAccessor::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit* target = me;
if (Map* map = me->GetMap())
{
// the one who's been cast upon before
Unit* current = ObjectAccessor::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit* target = me;
Map::PlayerList const& players = map->GetPlayers();
if (Map* map = me->GetMap())
// get the best suitable target
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
{
Map::PlayerList const& players = map->GetPlayers();
// get the best suitable target
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
{
Player* p = i->GetSource();
if (p && p->IsAlive() // alive
&& (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best
&& !p->HasAura(PlayerDebuff[j]) // not exhausted
&& !p->HasAura(PlayerBuff[(j + 1) % 3]) // not on another beam
&& !p->HasAura(PlayerBuff[(j + 2) % 3])
&& IsBetween(me, p, portal)) // on the beam
target = p;
}
Player* p = i->GetSource();
if (p && p->IsAlive() // alive
&& (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best
&& !p->HasAura(PlayerDebuff[j]) // not exhausted
&& !p->HasAura(PlayerBuff[(j + 1) % 3]) // not on another beam
&& !p->HasAura(PlayerBuff[(j + 2) % 3])
&& IsBetween(me, p, portal)) // on the beam
target = p;
}
// buff the target
if (target->GetTypeId() == TYPEID_PLAYER)
target->AddAura(PlayerBuff[j], target);
else
target->AddAura(NetherBuff[j], target);
// cast visual beam on the chosen target if switched
// simple target switching isn't working -> using BeamerGUID to cast (workaround)
if (!current || target != current)
{
BeamTarget[j] = target->GetGUID();
// remove currently beaming portal
if (Creature* beamer = ObjectAccessor::GetCreature(*portal, BeamerGUID[j]))
{
beamer->CastSpell(target, PortalBeam[j], false);
beamer->DisappearAndDie();
BeamerGUID[j].Clear();
}
// create new one and start beaming on the target
if (Creature* beamer = portal->SummonCreature(PortalID[j], portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), portal->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 60000))
{
beamer->CastSpell(target, PortalBeam[j], false);
BeamerGUID[j] = beamer->GetGUID();
}
}
// aggro target if Red Beam
if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER)
me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim()));
}
// buff the target
if (target->GetTypeId() == TYPEID_PLAYER)
{
target->AddAura(PlayerBuff[j], target);
}
else
{
target->AddAura(NetherBuff[j], target);
}
// cast visual beam on the chosen target if switched
// simple target switching isn't working -> using BeamerGUID to cast (workaround)
if (!current || target != current)
{
BeamTarget[j] = target->GetGUID();
// remove currently beaming portal
if (Creature* beamer = ObjectAccessor::GetCreature(*portal, BeamerGUID[j]))
{
beamer->CastSpell(target, PortalBeam[j], false);
beamer->DisappearAndDie();
BeamerGUID[j].Clear();
}
// create new one and start beaming on the target
if (Creature* beamer = portal->SummonCreature(PortalID[j], portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), portal->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 60000))
{
beamer->CastSpell(target, PortalBeam[j], false);
BeamerGUID[j] = beamer->GetGUID();
}
}
// aggro target if Red Beam
if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER)
{
me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim()));
}
}
}
}
void SwitchToPortalPhase()
void SwitchToPortalPhase()
{
scheduler.CancelGroup(VANISH_PHASE);
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
SummonPortals();
scheduler.Schedule(60s, [this](TaskContext /*context*/)
{
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
SummonPortals();
PhaseTimer = 60000;
PortalPhase = true;
PortalTimer = 10000;
EmpowermentTimer = 10000;
Talk(EMOTE_PHASE_PORTAL);
}
void SwitchToBanishPhase()
{
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCast(me, SPELL_BANISH_VISUAL, true);
DoCast(me, SPELL_BANISH_ROOT, true);
DestroyPortals();
PhaseTimer = 30000;
PortalPhase = false;
Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++i)
me->RemoveAurasDueToSpell(NetherBuff[i]);
}
void HandleDoors(bool open) // Massive Door switcher
{
if (GameObject* Door = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_GO_MASSIVE_DOOR) ))
Door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
void JustEngagedWith(Unit* /*who*/) override
{
HandleDoors(false);
SwitchToPortalPhase();
DoZoneInCombat();
}
void JustDied(Unit* /*killer*/) override
{
HandleDoors(true);
DestroyPortals();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToBanishPhase();
return;
// Void Zone
if (VoidZoneTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 1, 45, true), SPELL_VOIDZONE, true);
VoidZoneTimer = 15000;
}
else
VoidZoneTimer -= diff;
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
UpdatePortals();
context.Repeat(1s);
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
DoCastSelf(SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
context.Repeat(90s);
});
Talk(EMOTE_PHASE_PORTAL);
}
// NetherInfusion Berserk
if (!Berserk && NetherInfusionTimer <= diff)
void SwitchToBanishPhase()
{
scheduler.CancelGroup(PORTAL_PHASE);
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCastSelf(SPELL_BANISH_VISUAL, true);
DoCastSelf(SPELL_BANISH_ROOT, true);
DestroyPortals();
scheduler.Schedule(30s, [this](TaskContext /*context*/)
{
if (!me->IsNonMeleeSpellCast(false))
{
me->AddAura(SPELL_NETHER_INFUSION, me);
DoCast(me, SPELL_NETHERSPITE_ROAR);
Berserk = true;
SwitchToPortalPhase();
return;
}
else
NetherInfusionTimer -= diff;
if (PortalPhase) // PORTAL PHASE
{
// Distribute beams and buffs
if (PortalTimer <= diff)
{
UpdatePortals();
PortalTimer = 1000;
}
else
PortalTimer -= diff;
// Empowerment & Nether Burn
if (EmpowermentTimer <= diff)
{
DoCast(me, SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
EmpowermentTimer = 90000;
}
else
EmpowermentTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToBanishPhase();
return;
}
}
else
PhaseTimer -= diff;
}
else // BANISH PHASE
{
// Netherbreath
if (NetherbreathTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40, true))
DoCast(target, SPELL_NETHERBREATH);
NetherbreathTimer = urand(5000, 7000);
}
else
NetherbreathTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToPortalPhase();
return;
}
}
else
PhaseTimer -= diff;
}
DoMeleeAttackIfReady();
}).Schedule(10s, VANISH_PHASE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_NETHERBREATH, 0, 40.0f, true);
context.Repeat(5s, 7s);
});
Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++i)
{
me->RemoveAurasDueToSpell(NetherBuff[i]);
}
};
}
void HandleDoors(bool open)
{
if (GameObject* door = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_GO_MASSIVE_DOOR)))
{
door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
HandleDoors(false);
SwitchToPortalPhase();
DoZoneInCombat();
scheduler.Schedule(15s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_VOIDZONE, 1, 45.0f, true, true);
context.Repeat(15s);
}).Schedule(9min, [this](TaskContext /*context*/)
{
if (!berserk)
{
DoCastSelf(SPELL_NETHER_INFUSION);
DoCastAOE(SPELL_NETHERSPITE_ROAR);
berserk = true;
}
});
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
HandleDoors(true);
DestroyPortals();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
private:
bool berserk;
ObjectGuid PortalGUID[3]; // guid's of portals
ObjectGuid BeamerGUID[3]; // guid's of auxiliary beaming portals
ObjectGuid BeamTarget[3]; // guid's of portals' current targets
};
class spell_nether_portal_perseverence : public AuraScript
@@ -366,6 +328,6 @@ class spell_nether_portal_perseverence : public AuraScript
void AddSC_boss_netherspite()
{
new boss_netherspite();
RegisterKarazhanCreatureAI(boss_netherspite);
RegisterSpellScript(spell_nether_portal_perseverence);
}