330 lines
11 KiB
C++
330 lines
11 KiB
C++
/*
|
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Affero General Public License as published by the
|
|
* Free Software Foundation; either version 3 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 Affero 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 "Player.h"
|
|
#include "ScriptMgr.h"
|
|
#include "ScriptedCreature.h"
|
|
#include "karazhan.h"
|
|
#include "SpellAuraEffects.h"
|
|
#include "SpellScript.h"
|
|
|
|
enum Emotes
|
|
{
|
|
EMOTE_PHASE_PORTAL = 0,
|
|
EMOTE_PHASE_BANISH = 1
|
|
};
|
|
|
|
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] =
|
|
{
|
|
{-11195.353516f, -1613.237183f, 278.237258f}, // Left side
|
|
{-11137.846680f, -1685.607422f, 278.239258f}, // Right side
|
|
{-11094.493164f, -1591.969238f, 279.949188f} // Back side
|
|
};
|
|
|
|
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 PlayerDebuff[3] = {38637, 38638, 38639};
|
|
|
|
struct boss_netherspite : public BossAI
|
|
{
|
|
boss_netherspite(Creature* creature) : BossAI(creature, DATA_NETHERSPITE) {}
|
|
|
|
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
|
|
{
|
|
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)
|
|
{
|
|
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 = 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())
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
// 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()
|
|
{
|
|
scheduler.CancelGroup(VANISH_PHASE);
|
|
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
|
|
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
|
|
SummonPortals();
|
|
scheduler.Schedule(60s, [this](TaskContext /*context*/)
|
|
{
|
|
if (!me->IsNonMeleeSpellCast(false))
|
|
{
|
|
SwitchToBanishPhase();
|
|
return;
|
|
}
|
|
}).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);
|
|
}).Schedule(15s, PORTAL_PHASE, [this](TaskContext context)
|
|
{
|
|
DoCastRandomTarget(SPELL_VOIDZONE, 1, 45.0f, true, true);
|
|
context.Repeat(15s);
|
|
});
|
|
Talk(EMOTE_PHASE_PORTAL);
|
|
}
|
|
|
|
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)
|
|
{
|
|
SwitchToPortalPhase();
|
|
return;
|
|
}).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(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
|
|
{
|
|
PrepareAuraScript(spell_nether_portal_perseverence);
|
|
|
|
void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
|
{
|
|
const_cast<AuraEffect*>(aurEff)->SetAmount(aurEff->GetAmount() + 30000);
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectApply += AuraEffectApplyFn(spell_nether_portal_perseverence::HandleApply, EFFECT_2, SPELL_AURA_MOD_INCREASE_HEALTH, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
|
|
}
|
|
};
|
|
|
|
void AddSC_boss_netherspite()
|
|
{
|
|
RegisterKarazhanCreatureAI(boss_netherspite);
|
|
RegisterSpellScript(spell_nether_portal_perseverence);
|
|
}
|