EverWrath/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp
天鹭 9c49349e1e
refactor(Core/EventMap): Refactor EventMap and related scripts (#23121)
Co-authored-by: Kelno <3866946+kelno@users.noreply.github.com>
Co-authored-by: Peter Keresztes Schmidt <carbenium@outlook.com>
2025-10-12 22:19:24 -03:00

389 lines
14 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 "CreatureScript.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "oculus.h"
enum Spells
{
SPELL_EVOCATION = 51602,
SPELL_SUMMON_MENAGERIE_1 = 50476,
SPELL_SUMMON_MENAGERIE_2 = 50495,
SPELL_SUMMON_MENAGERIE_3 = 50496,
SPELL_TELEPORT = 51112,
SPELL_FROSTBOMB = 51103,
SPELL_TIME_BOMB_N = 51121,
SPELL_TIME_BOMB_H = 59376,
SPELL_EMPOWERED_ARCANE_EXPLOSION_N = 51110,
SPELL_EMPOWERED_ARCANE_EXPLOSION_H = 59377,
};
#define SPELL_EMPOWERED_ARCANE_EXPLOSION DUNGEON_MODE(SPELL_EMPOWERED_ARCANE_EXPLOSION_N, SPELL_EMPOWERED_ARCANE_EXPLOSION_H)
//#define SPELL_TIME_BOMB DUNGEON_MODE(SPELL_TIME_BOMB_N, SPELL_TIME_BOMB_H)
enum UromNPCs
{
NPC_PHANTASMAL_CLOUDSCRAPER = 27645,
NPC_PHANTASMAL_MAMMOTH = 27642,
NPC_PHANTASMAL_WOLF = 27644,
NPC_PHANTASMAL_AIR = 27650,
NPC_PHANTASMAL_FIRE = 27651,
NPC_PHANTASMAL_WATER = 27653,
NPC_PHANTASMAL_MURLOC = 27649,
NPC_PHANTASMAL_NAGAL = 27648,
NPC_PHANTASMAL_OGRE = 27647,
};
enum Events
{
EVENT_FROSTBOMB = 1,
EVENT_TELEPORT_TO_CENTER = 2,
EVENT_TELE_BACK = 3,
EVENT_TIME_BOMB = 4,
};
enum Yells
{
SAY_SUMMON_1 = 0,
SAY_SUMMON_2 = 1,
SAY_SUMMON_3 = 2,
SAY_AGGRO = 3,
EMOTE_ARCANE_EXPLOSION = 4,
SAY_ARCANE_EXPLOSION = 5,
SAY_DEATH = 6,
SAY_PLAYER_KILL = 7
};
float summons[3][4] =
{
{NPC_PHANTASMAL_AIR, NPC_PHANTASMAL_AIR, NPC_PHANTASMAL_WATER, NPC_PHANTASMAL_FIRE},
{NPC_PHANTASMAL_OGRE, NPC_PHANTASMAL_OGRE, NPC_PHANTASMAL_NAGAL, NPC_PHANTASMAL_MURLOC},
{NPC_PHANTASMAL_CLOUDSCRAPER, NPC_PHANTASMAL_CLOUDSCRAPER, NPC_PHANTASMAL_MAMMOTH, NPC_PHANTASMAL_WOLF}
};
float cords[5][4] =
{
{1177.47f, 937.722f, 527.405f, 2.21657f},
{968.66f, 1042.53f, 527.32f, 0.077f},
{1164.02f, 1170.85f, 527.321f, 3.66f},
{1118.31f, 1080.377f, 508.361f, 4.25f},
// There are 4 phases but with the code like this:
// me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]);
// lets provide fallback position.
{1177.47f, 937.722f, 527.405f, 2.21657f}
};
class boss_urom : public CreatureScript
{
public:
boss_urom() : CreatureScript("boss_urom") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetOculusAI<boss_uromAI>(pCreature);
}
struct boss_uromAI : public ScriptedAI
{
boss_uromAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
bool lock, inCenter;
float x, y, z;
int32 releaseLockTimer;
uint8 GetPhaseByCurrentPosition()
{
for (uint8 i = 0; i < 4; ++i)
if (me->GetDistance(cords[i][0], cords[i][1], cords[i][2]) < 20.0f)
return i;
return 0;
}
void Reset() override
{
if (pInstance)
{
pInstance->SetData(DATA_UROM, NOT_STARTED);
if (pInstance->GetData(DATA_VAROS) != DONE )
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
else
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
}
me->CastSpell(me, SPELL_EVOCATION, true);
events.Reset();
lock = false;
inCenter = false;
x = 0.0f;
y = 0.0f;
z = 0.0f;
releaseLockTimer = 0;
me->ApplySpellImmune(0, IMMUNITY_ID, 49838, true);
}
void JustEngagedWith(Unit* /*who*/) override
{
if (lock)
return;
uint8 phase = GetPhaseByCurrentPosition();
if (phase == 3)
{
Talk(SAY_AGGRO);
if (pInstance)
pInstance->SetData(DATA_UROM, IN_PROGRESS);
me->SetInCombatWithZone();
me->SetHomePosition(cords[0][0], cords[0][1], cords[0][2], cords[0][3]);
if (me->FindCurrentSpellBySpellId(SPELL_EVOCATION))
me->InterruptNonMeleeSpells(false);
events.RescheduleEvent(EVENT_FROSTBOMB, 7s, 11s);
events.RescheduleEvent(EVENT_TELEPORT_TO_CENTER, 30s, 35s);
events.RescheduleEvent(EVENT_TIME_BOMB, 20s, 25s);
}
else
{
lock = true;
switch (phase)
{
case 0:
Talk(SAY_SUMMON_1);
me->InterruptNonMeleeSpells(false);
me->CastSpell(me, SPELL_SUMMON_MENAGERIE_1, false);
break;
case 1:
Talk(SAY_SUMMON_2);
me->InterruptNonMeleeSpells(false);
me->CastSpell(me, SPELL_SUMMON_MENAGERIE_2, false);
break;
case 2:
Talk(SAY_SUMMON_3);
me->InterruptNonMeleeSpells(false);
me->CastSpell(me, SPELL_SUMMON_MENAGERIE_3, false);
break;
}
}
}
void AttackStart(Unit* who) override
{
if (lock)
return;
if (me->GetDistance(1103.0f, 1049.0f, 510.0f) < 55.0f)
ScriptedAI::AttackStart(who);
}
void JustSummoned(Creature* pSummon) override
{
pSummon->SetInCombatWithZone();
if (Unit* v = pSummon->SelectVictim())
if (pSummon->AI())
pSummon->AI()->AttackStart(v);
}
void LeaveCombat()
{
me->RemoveAllAuras();
me->GetThreatMgr().ClearAllThreat();
me->CombatStop(true);
me->LoadCreaturesAddon(true);
me->SetLootRecipient(nullptr);
me->ResetPlayerDamageReq();
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (pInstance)
{
pInstance->SetData(DATA_UROM, DONE);
}
// Body teleportation required only when boss is flying in the center
if (inCenter)
{
me->SetCanFly(false);
me->SetDisableGravity(false);
me->NearTeleportTo(x, y, z, 0.0f);
}
}
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_PLAYER_KILL);
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
switch (spell->Id)
{
case SPELL_SUMMON_MENAGERIE_1:
{
for( uint8 i = 0; i < 4; ++i )
me->SummonCreature(summons[0][i], cords[0][0] + ((i % 2) ? 4.0f : -4.0f), cords[0][1] + (i < 2 ? 4.0f : -4.0f), cords[0][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000);
uint8 phase = GetPhaseByCurrentPosition();
me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]);
me->DestroyForVisiblePlayers();
LeaveCombat();
me->CastSpell(me, SPELL_EVOCATION, true);
releaseLockTimer = 1;
}
break;
case SPELL_SUMMON_MENAGERIE_2:
{
for( uint8 i = 0; i < 4; ++i )
me->SummonCreature(summons[1][i], cords[1][0] + ((i % 2) ? 4.0f : -4.0f), cords[1][1] + (i < 2 ? 4.0f : -4.0f), cords[1][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000);
uint8 phase = GetPhaseByCurrentPosition();
me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]);
me->DestroyForVisiblePlayers();
LeaveCombat();
me->CastSpell(me, SPELL_EVOCATION, true);
releaseLockTimer = 1;
}
break;
case SPELL_SUMMON_MENAGERIE_3:
{
for( uint8 i = 0; i < 4; ++i )
me->SummonCreature(summons[2][i], cords[2][0] + ((i % 2) ? 4.0f : -4.0f), cords[2][1] + (i < 2 ? 4.0f : -4.0f), cords[2][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000);
uint8 phase = GetPhaseByCurrentPosition();
me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]);
me->DestroyForVisiblePlayers();
LeaveCombat();
me->CastSpell(me, SPELL_EVOCATION, true);
releaseLockTimer = 1;
}
break;
case SPELL_TELEPORT:
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
me->StopMoving();
me->SetControlled(true, UNIT_STATE_ROOT);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetCanFly(true);
me->SetDisableGravity(true);
me->NearTeleportTo(1103.69f, 1048.76f, 512.279f, 1.16f);
inCenter = true;
Talk(SAY_ARCANE_EXPLOSION);
Talk(EMOTE_ARCANE_EXPLOSION);
//At this point we are still in casting state so we need to clear it for DoCastAOE not to fail
me->ClearUnitState(UNIT_STATE_CASTING);
DoCastAOE(DUNGEON_MODE(SPELL_EMPOWERED_ARCANE_EXPLOSION_N, SPELL_EMPOWERED_ARCANE_EXPLOSION_H));
me->AddUnitState(UNIT_STATE_CASTING);
events.RescheduleEvent(EVENT_TELE_BACK, DUNGEON_MODE(9s, 7s));
default:
break;
}
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void UpdateAI(uint32 diff) override
{
if (releaseLockTimer)
{
if (releaseLockTimer >= 5000)
{
lock = false;
if (me->IsInCombat())
{
LeaveCombat();
me->CastSpell(me, SPELL_EVOCATION, true);
}
releaseLockTimer = 0;
}
else
releaseLockTimer += diff;
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_FROSTBOMB:
if (Unit* v = me->GetVictim())
me->CastSpell(v, SPELL_FROSTBOMB, false);
events.Repeat(7s, 11s);
break;
case EVENT_TIME_BOMB:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
DoCast(target, DUNGEON_MODE(SPELL_TIME_BOMB_N, SPELL_TIME_BOMB_H));
events.Repeat(20s, 25s);
break;
case EVENT_TELEPORT_TO_CENTER:
x = me->GetPositionX();
y = me->GetPositionY();
z = me->GetPositionZ();
me->CastSpell(me, SPELL_TELEPORT, false);
events.Repeat(25s, 30s);
events.DelayEvents(10s);
break;
case EVENT_TELE_BACK:
me->GetMotionMaster()->MoveIdle();
me->DisableSpline();
me->SetCanFly(false);
me->SetDisableGravity(false);
me->NearTeleportTo(x, y, z, 0.0f);
me->SetControlled(false, UNIT_STATE_ROOT);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->GetMotionMaster()->MoveChase(me->GetVictim());
inCenter = false;
break;
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetCanFly(false);
me->SetDisableGravity(false);
me->SetControlled(false, UNIT_STATE_ROOT);
inCenter = false;
ScriptedAI::EnterEvadeMode(why);
}
};
};
void AddSC_boss_urom()
{
new boss_urom();
}