/*
* 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 .
*/
#include "zulgurub.h"
#include "Containers.h"
#include "GridNotifiers.h"
#include "InstanceScript.h"
#include "ObjectAccessor.h"
#include "MotionMaster.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
enum Yells
{
SAY_AGGRO = 0,
SAY_PLAYER_KILL = 1,
SAY_DISMOUNT_OHGAN = 2,
EMOTE_DEVASTATING_SLAM = 3,
SAY_REANIMATE_OHGAN = 4,
EMOTE_FRENZY = 5,
SAY_FRENZY = 6,
SAY_DEATH = 7
};
enum Spells
{
// Bloodlord Mandokir
SPELL_BLOODLORD_AURA = 96480,
SPELL_SUMMON_OHGAN = 96717,
SPELL_REANIMATE_OHGAN = 96724,
SPELL_DECAPITATE = 96682,
SPELL_BLOODLETTING = 96776,
SPELL_BLOODLETTING_DAMAGE = 96777,
SPELL_BLOODLETTING_HEAL = 96778,
SPELL_FRENZY = 96800,
SPELL_LEVEL_UP = 96662,
SPELL_DEVASTATING_SLAM = 96740,
SPELL_DEVASTATING_SLAM_TRIGGER = 96761,
SPELL_DEVASTATING_SLAM_DAMAGE = 97385,
SPELL_SPIRIT_VENGEANCE_CANCEL = 96821,
// Chained Spirit
SPELL_REVIVE = 96484,
// Ohgan
SPELL_OHGAN_HEART_VISUAL = 96727,
SPELL_PERMANENT_FEIGN_DEATH = 96733,
SPELL_CLEAR_ALL = 28471,
SPELL_OHGAN_ORDERS = 96721,
SPELL_OHGAN_ORDERS_TRIGGER = 96722
};
enum Events
{
// Bloodlord Mandokir
EVENT_SUMMON_OHGAN = 1,
EVENT_DECAPITATE = 2,
EVENT_BLOODLETTING = 3,
EVENT_REANIMATE_OHGAN = 4,
EVENT_REANIMATE_OHGAN_COOLDOWN = 5,
EVENT_DEVASTATING_SLAM = 6
};
enum Action
{
// Bloodlord Mandokir
ACTION_OHGAN_IS_DEATH = 1,
ACTION_START_REVIVE = 2,
// Chained Spirit
ACTION_REVIVE = 1
};
enum MandokirMisc
{
POINT_START_REVIVE = 1,
DATA_OHGANOT_SO_FAST = 5762,
DATA_REVIVE_GUID = 0,
};
enum SummonGroups
{
SUMMON_GROUP_CHAINED_SPIRIT = 0
};
struct boss_mandokir : public BossAI
{
boss_mandokir(Creature* creature) : BossAI(creature, DATA_MANDOKIR)
{
Initialize();
}
void Initialize()
{
_ohganotSoFast = true;
_reanimateOhganCooldown = false;
}
void Reset() override
{
DoCastAOE(SPELL_SPIRIT_VENGEANCE_CANCEL);
_Reset();
me->SummonCreatureGroup(SUMMON_GROUP_CHAINED_SPIRIT);
Initialize();
_reviveGUID.Clear();
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
DoCastAOE(SPELL_BLOODLORD_AURA);
if (!summons.empty())
{
for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (Creature* chainedSpirit = ObjectAccessor::GetCreature(*me, *itr))
if (chainedSpirit->GetEntry() == NPC_CHAINED_SPIRIT && chainedSpirit->AI())
chainedSpirit->SetFaction(FACTION_NONE);
}
}
events.ScheduleEvent(EVENT_DECAPITATE, 10s);
events.ScheduleEvent(EVENT_BLOODLETTING, 15s);
events.ScheduleEvent(EVENT_SUMMON_OHGAN, 20s);
events.ScheduleEvent(EVENT_DEVASTATING_SLAM, 25s);
}
void JustDied(Unit* /*killer*/) override
{
DoCastAOE(SPELL_SPIRIT_VENGEANCE_CANCEL);
_JustDied();
Talk(SAY_DEATH);
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
{
Talk(SAY_PLAYER_KILL);
DoCast(SPELL_LEVEL_UP);
_reviveGUID = victim->GetGUID();
DoAction(ACTION_START_REVIVE);
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (me->HealthBelowPctDamaged(20, damage) && !me->HasAura(SPELL_FRENZY))
{
DoCast(me, SPELL_FRENZY, true);
Talk(SAY_FRENZY);
Talk(EMOTE_FRENZY);
}
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_OHGAN_IS_DEATH:
events.ScheduleEvent(EVENT_REANIMATE_OHGAN, 4s);
_ohganotSoFast = false;
break;
case ACTION_START_REVIVE:
{
std::list creatures;
GetCreatureListWithEntryInGrid(creatures, me, NPC_CHAINED_SPIRIT, 200.0f);
creatures.remove_if(Trinity::AnyDeadUnitCheck());
creatures.remove_if(Trinity::UnitAuraCheck(true, SPELL_OHGAN_ORDERS_TRIGGER));
Trinity::Containers::RandomResize(creatures, 1);
if (creatures.empty())
return;
for (std::list::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
{
if (Creature* chainedSpirit = ObjectAccessor::GetCreature(*me, (*itr)->GetGUID()))
{
chainedSpirit->AI()->SetGUID(_reviveGUID, DATA_REVIVE_GUID);
chainedSpirit->AI()->DoAction(ACTION_REVIVE);
_reviveGUID.Clear();
}
}
break;
}
default:
break;
}
}
uint32 GetData(uint32 type) const override
{
if (type == DATA_OHGANOT_SO_FAST)
return _ohganotSoFast;
return 0;
}
void SetGUID(ObjectGuid const& guid, int32 /*type*/) override
{
_reviveGUID = guid;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_SUMMON_OHGAN:
me->SetMountDisplayId(0);
DoCast(me, SPELL_SUMMON_OHGAN, true);
break;
case EVENT_DECAPITATE:
DoCastAOE(SPELL_DECAPITATE);
events.ScheduleEvent(EVENT_DECAPITATE, me->HasAura(SPELL_FRENZY) ? (17s + 500ms) : 35s);
break;
case EVENT_BLOODLETTING:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0.0f, true))
{
DoCast(target, SPELL_BLOODLETTING, true);
me->ClearUnitState(UNIT_STATE_CASTING);
}
events.ScheduleEvent(EVENT_BLOODLETTING, 25s);
break;
case EVENT_REANIMATE_OHGAN:
if (_reanimateOhganCooldown)
events.ScheduleEvent(EVENT_REANIMATE_OHGAN, 1s);
else
{
DoCastAOE(SPELL_REANIMATE_OHGAN);
Talk(SAY_REANIMATE_OHGAN);
// Cooldown
_reanimateOhganCooldown = true;
events.ScheduleEvent(EVENT_REANIMATE_OHGAN_COOLDOWN, 20s);
}
break;
case EVENT_REANIMATE_OHGAN_COOLDOWN:
_reanimateOhganCooldown = false;
break;
case EVENT_DEVASTATING_SLAM:
DoCastAOE(SPELL_DEVASTATING_SLAM_TRIGGER);
events.ScheduleEvent(EVENT_DEVASTATING_SLAM, 30s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
bool _ohganotSoFast;
bool _reanimateOhganCooldown;
ObjectGuid _reviveGUID;
};
struct npc_ohgan : public ScriptedAI
{
npc_ohgan(Creature* creature) : ScriptedAI(creature)
{
_instance = me->GetInstanceScript();
}
void JustEngagedWith(Unit* /*who*/) override
{
DoCastAOE(SPELL_OHGAN_ORDERS, true);
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (damage >= me->GetHealth())
{
damage = 0;
me->AttackStop();
me->SetHealth(0);
me->SetTarget(ObjectGuid::Empty);
DoCast(me, SPELL_CLEAR_ALL, true);
DoCast(me, SPELL_PERMANENT_FEIGN_DEATH);
if (Creature* mandokir = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MANDOKIR)))
mandokir->AI()->DoAction(ACTION_OHGAN_IS_DEATH);
}
}
void KilledUnit(Unit* victim) override
{
if (Creature* creature = victim->ToCreature())
{
if (creature->GetEntry() == NPC_CHAINED_SPIRIT)
DoCastAOE(SPELL_OHGAN_ORDERS, true);
}
}
private:
InstanceScript* _instance;
};
struct npc_chained_spirit : public ScriptedAI
{
npc_chained_spirit(Creature* creature) : ScriptedAI(creature)
{
_instance = me->GetInstanceScript();
me->AddUnitMovementFlag(MOVEMENTFLAG_HOVER);
me->SetReactState(REACT_PASSIVE); // correct?
}
void Reset() override
{
_revivePlayerGUID.Clear();
}
void SetGUID(ObjectGuid const& guid, int32 /*type*/) override
{
_revivePlayerGUID = guid;
}
void DoAction(int32 action) override
{
if (action == ACTION_REVIVE)
{
Position pos;
if (Player* target = ObjectAccessor::GetPlayer(*me, _revivePlayerGUID))
{
target->GetNearPoint(me, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 5.0f, target->GetAbsoluteAngle(me));
me->GetMotionMaster()->MovePoint(POINT_START_REVIVE, pos);
}
}
}
void MovementInform(uint32 type, uint32 pointId) override
{
if (type != POINT_MOTION_TYPE || !_revivePlayerGUID)
return;
if (pointId == POINT_START_REVIVE)
{
if (Player* target = ObjectAccessor::GetPlayer(*me, _revivePlayerGUID))
DoCast(target, SPELL_REVIVE);
me->DespawnOrUnsummon(2s);
}
}
void JustDied(Unit* /*killer*/) override
{
Player* target = ObjectAccessor::GetPlayer(*me, _revivePlayerGUID);
if (!target || target->IsAlive())
return;
if (Creature* mandokir = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MANDOKIR)))
{
mandokir->GetAI()->SetGUID(target->GetGUID(), DATA_REVIVE_GUID);
mandokir->GetAI()->DoAction(ACTION_START_REVIVE);
}
me->DespawnOrUnsummon();
}
void UpdateAI(uint32 /*diff*/) override { }
private:
InstanceScript* _instance;
ObjectGuid _revivePlayerGUID;
};
// 96682 - Decapitate
class spell_mandokir_decapitate : public SpellScript
{
void FilterTargets(std::list& targets)
{
if (targets.empty())
return;
WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets);
targets.clear();
targets.push_back(target);
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
Unit* caster = GetCaster();
if (Player* target = GetHitPlayer())
caster->CastSpell(target, uint32(GetEffectValue()), true);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mandokir_decapitate::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
OnEffectHitTarget += SpellEffectFn(spell_mandokir_decapitate::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
// 96776 - Bloodletting
class spell_mandokir_bloodletting : public AuraScript
{
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_BLOODLETTING_DAMAGE, SPELL_BLOODLETTING_HEAL });
}
void HandleEffectPeriodic(AuraEffect const* aurEff)
{
Unit* target = GetTarget();
Unit* caster = GetCaster();
if (!caster)
return;
CastSpellExtraArgs args;
args.TriggerFlags = TRIGGERED_FULL_MASK;
args.AddSpellMod(SPELLVALUE_BASE_POINT0, std::max(7500, target->CountPctFromCurHealth(aurEff->GetAmount())));
caster->CastSpell(target, SPELL_BLOODLETTING_DAMAGE, args);
target->CastSpell(caster, SPELL_BLOODLETTING_HEAL, args);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_mandokir_bloodletting::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
}
};
// 96821 - Spirit's Vengeance Cancel
class spell_mandokir_spirit_vengeance_cancel : public SpellScript
{
void HandleScript(SpellEffIndex /*effIndex*/)
{
if (Player* target = GetHitPlayer())
target->RemoveAura(uint32(GetEffectValue()));
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_mandokir_spirit_vengeance_cancel::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
OnEffectHitTarget += SpellEffectFn(spell_mandokir_spirit_vengeance_cancel::HandleScript, EFFECT_1, SPELL_EFFECT_DUMMY);
}
};
class DevastatingSlamTargetSelector
{
public:
DevastatingSlamTargetSelector(Creature* me, const Unit* victim) : _me(me), _victim(victim) {}
bool operator() (WorldObject* target)
{
if (target == _victim && _me->GetThreatManager().GetThreatListSize() > 1)
return true;
if (target->GetTypeId() != TYPEID_PLAYER)
return true;
return false;
}
Creature* _me;
Unit const* _victim;
};
// 96761 - Devastating Slam
class spell_mandokir_devastating_slam : public SpellScript
{
void FilterTargets(std::list& targets)
{
targets.remove_if(DevastatingSlamTargetSelector(GetCaster()->ToCreature(), GetCaster()->GetVictim()));
if (targets.empty())
return;
WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets);
targets.clear();
targets.push_back(target);
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
Unit* caster = GetCaster();
float angle = 0.0f;
float x, y, z;
if (Player* target = GetHitPlayer())
{
caster->AttackStop();
caster->SetOrientation(caster->GetAbsoluteAngle(target));
caster->SetFacingTo(caster->GetAbsoluteAngle(target));
caster->CastSpell(caster, SPELL_DEVASTATING_SLAM, false);
// HACK: Need better way for pos calculation
for (uint8 i = 0; i <= 50; ++i)
{
angle = rand_norm() * static_cast(M_PI * 35.0f / 180.0f) - static_cast(M_PI * 17.5f / 180.0f);
caster->GetClosePoint(x, y, z, 4.0f, frand(-2.5f, 50.0f), angle);
caster->CastSpell(Position{ x, y, z }, SPELL_DEVASTATING_SLAM_DAMAGE, true);
}
}
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mandokir_devastating_slam::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
OnEffectHitTarget += SpellEffectFn(spell_mandokir_devastating_slam::HandleScript, EFFECT_0, SPELL_EFFECT_FORCE_CAST);
}
};
// 96721 - Ohgan's Orders
class spell_mandokir_ohgan_orders : public SpellScript
{
void FilterTargets(std::list& targets)
{
if (targets.empty())
return;
WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets);
targets.clear();
targets.push_back(target);
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
Unit* caster = GetCaster();
if (Unit* target = GetHitUnit())
caster->CastSpell(target, uint32(GetEffectValue()), true);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mandokir_ohgan_orders::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
OnEffectHitTarget += SpellEffectFn(spell_mandokir_ohgan_orders::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
// 96722 - Ohgan's Orders
class spell_mandokir_ohgan_orders_trigger : public AuraScript
{
void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
Unit* target = GetTarget();
if (Unit* caster = GetCaster())
{
// HACK: research better way
caster->ClearUnitState(UNIT_STATE_CASTING);
caster->GetMotionMaster()->Clear();
caster->GetThreatManager().ResetAllThreat();
caster->GetThreatManager().AddThreat(target, 50000000.0f);
// TODO: Fixate mechanic
}
}
void Register() override
{
AfterEffectApply += AuraEffectApplyFn(spell_mandokir_ohgan_orders_trigger::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
// 96724 - Reanimate Ohgan
class spell_mandokir_reanimate_ohgan : public SpellScript
{
void HandleScript(SpellEffIndex /*effIndex*/)
{
if (Unit* target = GetHitUnit())
{
target->RemoveAura(SPELL_PERMANENT_FEIGN_DEATH);
target->CastSpell(target, SPELL_OHGAN_HEART_VISUAL, true);
target->CastSpell(nullptr, SPELL_OHGAN_ORDERS, true);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_mandokir_reanimate_ohgan::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class achievement_ohganot_so_fast : public AchievementCriteriaScript
{
public:
achievement_ohganot_so_fast() : AchievementCriteriaScript("achievement_ohganot_so_fast") { }
bool OnCheck(Player* /*player*/, Unit* target) override
{
return target && target->GetAI()->GetData(DATA_OHGANOT_SO_FAST);
}
};
void AddSC_boss_mandokir()
{
RegisterZulGurubCreatureAI(boss_mandokir);
RegisterZulGurubCreatureAI(npc_ohgan);
RegisterZulGurubCreatureAI(npc_chained_spirit);
RegisterSpellScript(spell_mandokir_decapitate);
RegisterSpellScript(spell_mandokir_bloodletting);
RegisterSpellScript(spell_mandokir_spirit_vengeance_cancel);
RegisterSpellScript(spell_mandokir_devastating_slam);
RegisterSpellScript(spell_mandokir_ohgan_orders);
RegisterSpellScript(spell_mandokir_ohgan_orders_trigger);
RegisterSpellScript(spell_mandokir_reanimate_ohgan);
new achievement_ohganot_so_fast();
}