EverWrath/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp
Andrew 272da9fbad
refactor(Scripts/VioletHold): Modernize Violet Hold dungeon scripts (#25187)
Co-authored-by: joschiwald <joschiwald@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:02:28 -03:00

335 lines
10 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 General Public License as published by
* the Free Software Foundation; either version 2 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 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 "Player.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "violet_hold.h"
#define ACTION_WATER_ELEMENT_HIT 1
#define ACTION_WATER_ELEMENT_KILLED 2
#define MAX_SPAWN_LOC 5
static Position SpawnLoc[MAX_SPAWN_LOC] =
{
{1840.64f, 795.407f, 44.079f, 1.676f},
{1886.24f, 757.733f, 47.750f, 5.201f},
{1877.91f, 845.915f, 43.417f, 3.560f},
{1918.97f, 850.645f, 47.225f, 4.136f},
{1935.50f, 796.224f, 52.492f, 4.224f},
};
enum Yells
{
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_DEATH = 2,
SAY_SPAWN = 3,
SAY_ENRAGE = 4,
SAY_SHATTER = 5,
SAY_BUBBLE = 6
};
enum eCreatures
{
NPC_ICHOR_GLOBULE = 29321,
};
enum eSpells
{
SPELL_DRAINED = 59820,
SPELL_FRENZY = 54312,
SPELL_PROTECTIVE_BUBBLE = 54306,
SPELL_WATER_BLAST = 54237,
SPELL_WATER_BOLT_VOLLEY = 54241,
SPELL_SPLASH = 59516,
SPELL_WATER_GLOBULE = 54268,
SPELL_CREATE_GLOBULE_VISUAL = 54260,
};
enum eEvents
{
EVENT_WATER_BOLT_VOLLEY = 1,
EVENT_DRAINED_CHECK,
};
struct boss_ichoron : public BossAI
{
boss_ichoron(Creature* c) : BossAI(c, BOSS_ICHORON) { }
void Reset() override
{
BossAI::Reset();
_isExploded = false;
_isFrenzy = false;
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
}
void DoAction(int32 param) override
{
if (!me->IsAlive())
return;
switch (param)
{
case ACTION_WATER_ELEMENT_HIT:
instance->SetData(DATA_ACHIEV, 0);
me->ModifyHealth(int32(me->CountPctFromMaxHealth(1)));
if (_isExploded)
DoExplodeCompleted();
break;
case ACTION_WATER_ELEMENT_KILLED:
uint32 damage = me->CountPctFromMaxHealth(3);
damage = std::min(damage, me->GetHealth() - 1);
me->ModifyHealth(-int32(damage));
me->LowerPlayerDamageReq(damage);
break;
}
}
void DoExplodeCompleted()
{
_isExploded = false;
me->RemoveAura(SPELL_DRAINED);
if (!HealthBelowPct(25))
{
Talk(SAY_BUBBLE);
DoCastSelf(SPELL_PROTECTIVE_BUBBLE, true);
}
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
}
void IchoronDoCastToAllHostilePlayers(uint32 spellId, bool triggered)
{
Map::PlayerList const& playerList = me->GetMap()->GetPlayers();
if (playerList.IsEmpty())
return;
for (Map::PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i)
if (Player* plr = i->GetSource())
DoCast(plr, spellId, triggered);
}
void JustEngagedWith(Unit* who) override
{
_isExploded = false;
_isFrenzy = false;
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
DoCastSelf(SPELL_PROTECTIVE_BUBBLE, true);
events.RescheduleEvent(EVENT_WATER_BOLT_VOLLEY, 7s, 12s);
instance->SetData(DATA_ACHIEV, 1);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (!_isFrenzy && !_isExploded && HealthBelowPct(25))
{
Talk(SAY_ENRAGE);
DoCastSelf(SPELL_FRENZY, true);
_isFrenzy = true;
}
if (!_isFrenzy)
{
if (!_isExploded)
{
if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE))
{
me->InterruptNonMeleeSpells(false);
Talk(SAY_SHATTER);
DoZoneInCombat();
IchoronDoCastToAllHostilePlayers(SPELL_WATER_BLAST, true);
DoCastSelf(SPELL_DRAINED, true);
_isExploded = true;
events.CancelEvent(EVENT_WATER_BOLT_VOLLEY);
events.RescheduleEvent(EVENT_DRAINED_CHECK, 15s);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(11686);
for (uint8 i = 0; i < MAX_SPAWN_LOC; ++i)
{
float angle = rand_norm() * 2 * M_PI;
Position p1(SpawnLoc[i]), p2(SpawnLoc[i]);
p1.m_positionX += 2.5f * cos(angle);
p1.m_positionY += 2.5f * std::sin(angle);
p2.m_positionX -= 2.5f * cos(angle);
p2.m_positionY -= 2.5f * std::sin(angle);
DoSummon(NPC_ICHOR_GLOBULE, p1, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN);
DoSummon(NPC_ICHOR_GLOBULE, p2, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN);
}
}
}
else
{
if (events.ExecuteEvent() == EVENT_DRAINED_CHECK)
DoExplodeCompleted();
else
{
bool isWaterElementsAlive = false;
if (!summons.empty())
{
for (ObjectGuid const& guid : summons)
if (Creature* temp = ObjectAccessor::GetCreature(*me, guid))
if (temp->IsAlive())
{
isWaterElementsAlive = true;
break;
}
}
if (!isWaterElementsAlive)
DoExplodeCompleted();
}
}
}
if (!_isExploded)
{
if (events.ExecuteEvent() == EVENT_WATER_BOLT_VOLLEY)
{
DoCastAOE(SPELL_WATER_BOLT_VOLLEY);
events.Repeat(10s, 15s);
}
}
DoMeleeAttackIfReady();
}
void JustSummoned(Creature* summoned) override
{
if (summoned)
{
summoned->SetSpeed(MOVE_RUN, 0.3f);
summoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
summoned->GetMotionMaster()->MoveFollow(me, 0, 0, MOTION_SLOT_ACTIVE, false, false);
DoCast(summoned, SPELL_CREATE_GLOBULE_VISUAL, true);
BossAI::JustSummoned(summoned);
instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID());
}
}
void SummonedCreatureDespawn(Creature* summoned) override
{
if (summoned)
{
BossAI::SummonedCreatureDespawn(summoned);
instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID());
}
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
_isExploded = false;
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
BossAI::JustDied(killer);
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
private:
bool _isExploded;
bool _isFrenzy;
};
enum eGlobuleEvents
{
EVENT_RANGE_CHECK = 1,
};
struct npc_ichor_globule : public ScriptedAI
{
npc_ichor_globule(Creature* c) : ScriptedAI(c)
{
_instance = c->GetInstanceScript();
}
void Reset() override
{
_events.RescheduleEvent(EVENT_RANGE_CHECK, 1s);
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_CREATE_GLOBULE_VISUAL)
DoCastSelf(SPELL_WATER_GLOBULE, true);
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
if (_events.ExecuteEvent() == EVENT_RANGE_CHECK)
{
if (Creature* ichoron = _instance->GetCreature(BOSS_ICHORON))
if (me->IsWithinDist(ichoron, 2.0f, false))
{
if (ichoron->AI())
ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_HIT);
me->DespawnOrUnsummon();
return;
}
_events.Repeat(1s);
}
}
void JustDied(Unit* /*killer*/) override
{
DoCastSelf(SPELL_SPLASH, true);
if (Creature* ichoron = _instance->GetCreature(BOSS_ICHORON))
if (ichoron->AI())
ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_KILLED);
me->DespawnOrUnsummon(2500ms);
}
void AttackStart(Unit* /*who*/) override {}
void MoveInLineOfSight(Unit* /*who*/) override {}
private:
InstanceScript* _instance;
EventMap _events;
};
void AddSC_boss_ichoron()
{
RegisterVioletHoldCreatureAI(boss_ichoron);
RegisterVioletHoldCreatureAI(npc_ichor_globule);
}