/*
* This file is part of the TrinityCore 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 .
*/
/* ScriptData
SDName: Boss_Felmyst
SD%Complete: 0
SDComment:
EndScriptData */
#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "sunwell_plateau.h"
#include "TemporarySummon.h"
enum Yells
{
YELL_BIRTH = 0,
YELL_KILL = 1,
YELL_BREATH = 2,
YELL_TAKEOFF = 3,
YELL_BERSERK = 4,
YELL_DEATH = 5,
//YELL_KALECGOS = 6, Not used. After felmyst's death spawned and say this
};
enum Spells
{
//Aura
AURA_SUNWELL_RADIANCE = 45769,
AURA_NOXIOUS_FUMES = 47002,
//Land phase
SPELL_CLEAVE = 19983,
SPELL_CORROSION = 45866,
SPELL_GAS_NOVA = 45855,
SPELL_ENCAPSULATE_CHANNEL = 45661,
// SPELL_ENCAPSULATE_EFFECT = 45665,
// SPELL_ENCAPSULATE_AOE = 45662,
//Flight phase
SPELL_VAPOR_SELECT = 45391, // fel to player, force cast 45392, 50000y selete target
SPELL_VAPOR_SUMMON = 45392, // player summon vapor, radius around caster, 5y,
SPELL_VAPOR_FORCE = 45388, // vapor to fel, force cast 45389
SPELL_VAPOR_CHANNEL = 45389, // fel to vapor, green beam channel
SPELL_VAPOR_TRIGGER = 45411, // linked to 45389, vapor to self, trigger 45410 and 46931
SPELL_VAPOR_DAMAGE = 46931, // vapor damage, 4000
SPELL_TRAIL_SUMMON = 45410, // vapor summon trail
SPELL_TRAIL_TRIGGER = 45399, // trail to self, trigger 45402
SPELL_TRAIL_DAMAGE = 45402, // trail damage, 2000 + 2000 dot
SPELL_DEAD_SUMMON = 45400, // summon blazing dead, 5min
SPELL_DEAD_PASSIVE = 45415,
SPELL_FOG_BREATH = 45495, // fel to self, speed burst
SPELL_FOG_TRIGGER = 45582, // fog to self, trigger 45782
SPELL_FOG_FORCE = 45782, // fog to player, force cast 45714
SPELL_FOG_INFORM = 45714, // player let fel cast 45717, script effect
SPELL_FOG_CHARM = 45717, // fel to player
SPELL_FOG_CHARM2 = 45726, // link to 45717
SPELL_TRANSFORM_TRIGGER = 44885, // madrigosa to self, trigger 46350
SPELL_TRANSFORM_VISUAL = 46350, // 46411stun?
SPELL_TRANSFORM_FELMYST = 45068, // become fel
SPELL_FELMYST_SUMMON = 45069,
//Other
SPELL_BERSERK = 45078,
SPELL_CLOUD_VISUAL = 45212,
SPELL_CLOUD_SUMMON = 45884
};
enum PhaseFelmyst
{
PHASE_NONE,
PHASE_GROUND,
PHASE_FLIGHT
};
enum EventFelmyst
{
EVENT_NONE,
EVENT_BERSERK,
EVENT_CLEAVE,
EVENT_CORROSION,
EVENT_GAS_NOVA,
EVENT_ENCAPSULATE,
EVENT_FLIGHT,
EVENT_FLIGHT_SEQUENCE,
EVENT_SUMMON_DEAD,
EVENT_SUMMON_FOG
};
struct boss_felmyst : public BossAI
{
boss_felmyst(Creature* creature) : BossAI(creature, DATA_FELMYST)
{
Initialize();
uiBreathCount = 0;
breathX = 0.f;
breathY = 0.f;
}
void Initialize()
{
phase = PHASE_NONE;
uiFlightCount = 0;
}
PhaseFelmyst phase;
uint32 uiFlightCount;
uint32 uiBreathCount;
float breathX, breathY;
void InitializeAI() override
{
// for intro sequence
if (instance->GetBossState(DATA_FELMYST) == SPECIAL)
if (Creature* madrigosa = instance->GetCreature(DATA_MADRIGOSA))
me->Relocate(madrigosa);
me->SetDisplayFromModel(0);
me->SetDisplayId(me->GetDisplayId(), true);
}
void Reset() override
{
Initialize();
BossAI::Reset();
me->SetDisableGravity(true);
me->SetBoundingRadius(10);
me->SetCombatReach(10);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_BERSERK, 10min);
DoCast(me, AURA_SUNWELL_RADIANCE, true);
DoCast(me, AURA_NOXIOUS_FUMES, true);
EnterPhase(PHASE_GROUND);
}
void AttackStart(Unit* who) override
{
if (phase != PHASE_FLIGHT)
BossAI::AttackStart(who);
}
void MoveInLineOfSight(Unit* who) override
{
if (phase != PHASE_FLIGHT)
BossAI::MoveInLineOfSight(who);
}
void KilledUnit(Unit* /*victim*/) override
{
Talk(YELL_KILL);
}
void JustAppeared() override
{
Talk(YELL_BIRTH);
}
void JustDied(Unit* killer) override
{
Talk(YELL_DEATH);
BossAI::JustDied(killer);
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
Reset();
_DespawnAtEvade();
}
void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override
{
Unit* unitCaster = caster->ToUnit();
if (!unitCaster)
return;
// workaround for linked aura
/*if (spell->Id == SPELL_VAPOR_FORCE)
{
caster->CastSpell(caster, SPELL_VAPOR_TRIGGER, true);
}*/
// workaround for mind control
if (spellInfo->Id == SPELL_FOG_INFORM)
{
float x, y, z;
unitCaster->GetPosition(x, y, z);
if (Unit* summon = me->SummonCreature(NPC_DEAD, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5s))
{
summon->SetMaxHealth(unitCaster->GetMaxHealth());
summon->SetHealth(unitCaster->GetMaxHealth());
summon->CastSpell(summon, SPELL_FOG_CHARM, true);
summon->CastSpell(summon, SPELL_FOG_CHARM2, true);
}
Unit::DealDamage(me, unitCaster, unitCaster->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
}
}
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() == NPC_DEAD)
{
summon->AI()->AttackStart(SelectTarget(SelectTargetMethod::Random));
DoZoneInCombat(summon);
summon->CastSpell(summon, SPELL_DEAD_PASSIVE, true);
}
BossAI::JustSummoned(summon);
}
void MovementInform(uint32, uint32) override
{
if (phase == PHASE_FLIGHT)
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 1ms);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (phase != PHASE_GROUND && damage >= me->GetHealth())
damage = 0;
}
void EnterPhase(PhaseFelmyst NextPhase)
{
switch (NextPhase)
{
case PHASE_GROUND:
me->CastStop(SPELL_FOG_BREATH);
me->RemoveAurasDueToSpell(SPELL_FOG_BREATH);
me->StopMoving();
me->SetSpeedRate(MOVE_RUN, 2.0f);
events.ScheduleEvent(EVENT_CLEAVE, 5s, 10s);
events.ScheduleEvent(EVENT_CORROSION, 10s, 20s);
events.ScheduleEvent(EVENT_GAS_NOVA, 15s, 20s);
events.ScheduleEvent(EVENT_ENCAPSULATE, 20s, 25s);
events.ScheduleEvent(EVENT_FLIGHT, 1min);
break;
case PHASE_FLIGHT:
me->SetDisableGravity(true);
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 1s);
uiFlightCount = 0;
uiBreathCount = 0;
break;
default:
break;
}
phase = NextPhase;
me->SetCanMelee(phase == PHASE_GROUND);
}
void HandleFlightSequence()
{
switch (uiFlightCount)
{
case 0:
//me->AttackStop();
me->GetMotionMaster()->Clear();
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->StopMoving();
Talk(YELL_TAKEOFF);
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 2s);
break;
case 1:
me->GetMotionMaster()->MovePoint(0, me->GetPositionX()+1, me->GetPositionY(), me->GetPositionZ()+10);
break;
case 2:
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 150, true);
if (!target)
target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PLAYER_GUID));
if (!target)
{
EnterEvadeMode(EvadeReason::NoHostiles);
return;
}
if (Creature* Vapor = me->SummonCreature(NPC_VAPOR, target->GetPositionX() - 5 + rand32() % 10, target->GetPositionY() - 5 + rand32() % 10, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 9s))
{
Vapor->AI()->AttackStart(target);
me->InterruptNonMeleeSpells(false);
DoCast(Vapor, SPELL_VAPOR_CHANNEL, false); // core bug
Vapor->CastSpell(Vapor, SPELL_VAPOR_TRIGGER, true);
}
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 10s);
break;
}
case 3:
{
DespawnSummons(NPC_VAPOR_TRAIL);
//DoCast(me, SPELL_VAPOR_SELECT); need core support
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 150, true);
if (!target)
target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PLAYER_GUID));
if (!target)
{
EnterEvadeMode(EvadeReason::NoHostiles);
return;
}
//target->CastSpell(target, SPELL_VAPOR_SUMMON, true); need core support
if (Creature* pVapor = me->SummonCreature(NPC_VAPOR, target->GetPositionX() - 5 + rand32() % 10, target->GetPositionY() - 5 + rand32() % 10, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 9s))
{
if (pVapor->AI())
pVapor->AI()->AttackStart(target);
me->InterruptNonMeleeSpells(false);
DoCast(pVapor, SPELL_VAPOR_CHANNEL, false); // core bug
pVapor->CastSpell(pVapor, SPELL_VAPOR_TRIGGER, true);
}
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 10s);
break;
}
case 4:
DespawnSummons(NPC_VAPOR_TRAIL);
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 1ms);
break;
case 5:
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 150, true);
if (!target)
target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PLAYER_GUID));
if (!target)
{
EnterEvadeMode(EvadeReason::NoHostiles);
return;
}
breathX = target->GetPositionX();
breathY = target->GetPositionY();
float x, y, z;
target->GetContactPoint(me, x, y, z, 70);
me->GetMotionMaster()->MovePoint(0, x, y, z+10);
break;
}
case 6:
me->SetFacingTo(me->GetAbsoluteAngle(breathX, breathY));
//DoTextEmote("takes a deep breath.", nullptr);
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 10s);
break;
case 7:
{
DoCast(me, SPELL_FOG_BREATH, true);
float x, y, z;
me->GetPosition(x, y, z);
x = 2 * breathX - x;
y = 2 * breathY - y;
me->GetMotionMaster()->MovePoint(0, x, y, z);
events.ScheduleEvent(EVENT_SUMMON_FOG, 1ms);
break;
}
case 8:
me->CastStop(SPELL_FOG_BREATH);
me->RemoveAurasDueToSpell(SPELL_FOG_BREATH);
++uiBreathCount;
events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 1ms);
if (uiBreathCount < 3)
uiFlightCount = 4;
break;
case 9:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat))
DoStartMovement(target);
else
{
EnterEvadeMode(EvadeReason::NoHostiles);
return;
}
break;
case 10:
me->SetDisableGravity(false);
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
EnterPhase(PHASE_GROUND);
AttackStart(SelectTarget(SelectTargetMethod::MaxThreat));
break;
}
++uiFlightCount;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
{
if (phase == PHASE_FLIGHT && !me->IsInEvadeMode())
EnterEvadeMode(EvadeReason::NoHostiles);
return;
}
events.Update(diff);
if (me->IsNonMeleeSpellCast(false))
return;
if (phase == PHASE_GROUND)
{
switch (events.ExecuteEvent())
{
case EVENT_BERSERK:
Talk(YELL_BERSERK);
DoCast(me, SPELL_BERSERK, true);
events.ScheduleEvent(EVENT_BERSERK, 10s);
break;
case EVENT_CLEAVE:
DoCastVictim(SPELL_CLEAVE, false);
events.ScheduleEvent(EVENT_CLEAVE, 5s, 10s);
break;
case EVENT_CORROSION:
DoCastVictim(SPELL_CORROSION, false);
events.ScheduleEvent(EVENT_CORROSION, 20s, 30s);
break;
case EVENT_GAS_NOVA:
DoCast(me, SPELL_GAS_NOVA, false);
events.ScheduleEvent(EVENT_GAS_NOVA, 20s, 25s);
break;
case EVENT_ENCAPSULATE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 150, true))
DoCast(target, SPELL_ENCAPSULATE_CHANNEL, false);
events.ScheduleEvent(EVENT_ENCAPSULATE, 25s, 30s);
break;
case EVENT_FLIGHT:
EnterPhase(PHASE_FLIGHT);
break;
default:
break;
}
}
if (phase == PHASE_FLIGHT)
{
switch (events.ExecuteEvent())
{
case EVENT_BERSERK:
Talk(YELL_BERSERK);
DoCast(me, SPELL_BERSERK, true);
break;
case EVENT_FLIGHT_SEQUENCE:
HandleFlightSequence();
break;
case EVENT_SUMMON_FOG:
{
float x, y, z;
me->GetPosition(x, y, z);
me->UpdateGroundPositionZ(x, y, z);
if (Creature* Fog = me->SummonCreature(NPC_VAPOR_TRAIL, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN, 10s))
{
Fog->RemoveAurasDueToSpell(SPELL_TRAIL_TRIGGER);
Fog->CastSpell(Fog, SPELL_FOG_TRIGGER, true);
me->CastSpell(Fog, SPELL_FOG_FORCE, true);
}
}
events.ScheduleEvent(EVENT_SUMMON_FOG, 1s);
break;
}
}
}
void DespawnSummons(uint32 entry)
{
std::vector unyieldingDeadPositions;
summons.DespawnIf([&](ObjectGuid guid)
{
if (guid.GetEntry() != entry)
return false;
if (guid.GetEntry() == NPC_VAPOR_TRAIL && phase == PHASE_FLIGHT)
if (Creature const* vapor = ObjectAccessor::GetCreature(*me, guid))
unyieldingDeadPositions.push_back(vapor->GetPosition());
return true;
});
for (Position const& unyieldingDeadPosition : unyieldingDeadPositions)
me->SummonCreature(NPC_DEAD, unyieldingDeadPosition, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5s);
}
};
struct npc_felmyst_vapor : public ScriptedAI
{
npc_felmyst_vapor(Creature* creature) : ScriptedAI(creature) { }
void Reset() override { }
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
//DoCast(me, SPELL_VAPOR_FORCE, true); core bug
}
void UpdateAI(uint32 /*diff*/) override
{
if (!me->GetVictim())
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
AttackStart(target);
}
};
struct npc_felmyst_trail : public ScriptedAI
{
npc_felmyst_trail(Creature* creature) : ScriptedAI(creature)
{
DoCast(me, SPELL_TRAIL_TRIGGER, true);
me->SetTarget(me->GetGUID());
me->SetBoundingRadius(0.01f); // core bug
}
void Reset() override { }
void JustEngagedWith(Unit* /*who*/) override { }
void AttackStart(Unit* /*who*/) override { }
void MoveInLineOfSight(Unit* /*who*/) override { }
void UpdateAI(uint32 /*diff*/) override { }
};
void AddSC_boss_felmyst()
{
RegisterSunwellPlateauCreatureAI(boss_felmyst);
RegisterSunwellPlateauCreatureAI(npc_felmyst_vapor);
RegisterSunwellPlateauCreatureAI(npc_felmyst_trail);
}