/*
* Copyright (C) 2008-2016 TrinityCore
* Copyright (C) 2006-2009 ScriptDev2
*
* 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_Thekal
SD%Complete: 95
SDComment: Almost finished.
SDCategory: Zul'Gurub
EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "zulgurub.h"
enum Says
{
SAY_AGGRO = 0,
SAY_DEATH = 1
};
enum Spells
{
SPELL_MORTAL_CLEAVE = 22859,
SPELL_SILENCE = 23207,
SPELL_FRENZY = 23128,
SPELL_FORCE_PUNCH = 24189,
SPELL_CHARGE = 24408,
SPELL_ENRAGE = 23537,
SPELL_SUMMON_TIGERS = 24183,
SPELL_TIGER_FORM = 24169,
SPELL_RESURRECT = 24173,
// Zealot Lor'Khan Spells
SPELL_SHIELD = 25020,
SPELL_BLOODLUST = 24185,
SPELL_GREATER_HEAL = 24208,
SPELL_DISARM = 22691,
// Zealot Zath Spells
SPELL_SWEEPING_STRIKES = 18765,
SPELL_SINISTER_STRIKE = 15667,
SPELL_GOUGE = 24698,
SPELL_KICK = 15614,
SPELL_BLIND = 21060
};
enum Actions
{
ACTION_PREVENT_REVIVE = 1
};
enum Phases
{
PHASE_NORMAL = 1,
PHASE_FAKE_DEATH = 2,
PHASE_WAITING = 3,
PHASE_TIGER = 4
};
// abstract base class for faking death
struct boss_thekalBaseAI : public ScriptedAI
{
boss_thekalBaseAI(Creature* creature) : ScriptedAI(creature)
{
Phase = PHASE_NORMAL;
}
uint8 Phase;
virtual void OnFakeingDeath() {}
virtual void OnRevive() {}
void DamageTaken(Unit* /*killer*/, uint32& damage) override
{
if (damage < me->GetHealth())
return;
// Prevent glitch if in fake death
if (Phase == PHASE_FAKE_DEATH || Phase == PHASE_WAITING)
{
damage = 0;
return;
}
// Only init fake in normal phase
if (Phase != PHASE_NORMAL)
return;
damage = 0;
me->InterruptNonMeleeSpells(true);
me->SetHealth(0);
me->StopMoving();
me->ClearComboPointHolders();
me->RemoveAllAurasOnDeath();
me->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
me->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
me->ClearAllReactives();
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
me->SetStandState(UNIT_STAND_STATE_DEAD);
Phase = PHASE_FAKE_DEATH;
OnFakeingDeath();
}
void Revive(bool OnlyFlags = false)
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetStandState(UNIT_STAND_STATE_STAND);
if (OnlyFlags)
return;
me->SetHealth(me->GetMaxHealth());
Phase = PHASE_NORMAL;
DoResetThreat();
Reset();
// Assume Attack
if (me->GetVictim())
me->GetMotionMaster()->MoveChase(me->GetVictim());
OnRevive();
}
void PreventRevive()
{
if (me->IsNonMeleeSpellCast(true))
me->InterruptNonMeleeSpells(true);
Phase = PHASE_WAITING;
}
};
class boss_thekal : public CreatureScript
{
public:
boss_thekal() : CreatureScript("boss_thekal") { }
struct boss_thekalAI : public boss_thekalBaseAI
{
boss_thekalAI(Creature* creature) : boss_thekalBaseAI(creature)
{
instance = creature->GetInstanceScript();
}
void Reset() override
{
MortalCleaveTimer = 4 * IN_MILLISECONDS;
SilenceTimer = 9 * IN_MILLISECONDS;
FrenzyTimer = 30 * IN_MILLISECONDS;
ForcePunchTimer = 4 * IN_MILLISECONDS;
ChargeTimer = 12 * IN_MILLISECONDS;
EnrageTimer = 32 * IN_MILLISECONDS;
SummonTigersTimer = 25 * IN_MILLISECONDS;
ResurrectTimer = 10 * IN_MILLISECONDS;
Phase = PHASE_NORMAL;
Enraged = false;
// remove fake death
Revive(true);
}
void EnterCombat(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
instance->SetBossState(DATA_THEKAL, DONE);
// remove the two adds
Unit* Lorkhan = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_LORKHAN));
Unit* pZath = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_ZATH));
if (!Lorkhan || !pZath)
return;
Lorkhan->RemoveFromWorld();
pZath->RemoveFromWorld();
}
void JustReachedHome() override
{
instance->SetBossState(DATA_THEKAL, FAIL);
}
void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_RESURRECT)
Revive();
}
// Only call in context where m_pInstance is valid
bool CanPreventAddsResurrect()
{
// If any add is alive, return false
if (instance->GetBossState(DATA_ZATH) != SPECIAL || instance->GetBossState(DATA_LORKHAN) != SPECIAL)
return false;
// Else Prevent them Resurrecting
if (Creature* pLorkhan = me->FindNearestCreature(NPC_ZEALOT_LORKHAN, 500.0f))
pLorkhan->AI()->DoAction(ACTION_PREVENT_REVIVE);
if (Creature* pzath = me->FindNearestCreature(NPC_ZEALOT_ZATH, 500.0f))
pzath->AI()->DoAction(ACTION_PREVENT_REVIVE);
return true;
}
void OnFakeingDeath()
{
ResurrectTimer = 10 * IN_MILLISECONDS;
if (instance)
{
instance->SetBossState(DATA_THEKAL, SPECIAL);
// If both Adds are already dead, don't wait 10 seconds
if (CanPreventAddsResurrect())
ResurrectTimer = 1 * IN_MILLISECONDS;
}
}
void OnRevive()
{
if (!instance)
return;
// Both Adds are 'dead' enter tiger phase
if (CanPreventAddsResurrect())
{
DoCast(me, SPELL_TIGER_FORM, true);
Phase = PHASE_TIGER;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
switch (Phase)
{
case PHASE_FAKE_DEATH:
if (ResurrectTimer < diff)
{
// resurrect him in any case
DoCast(me, SPELL_RESURRECT);
Phase = PHASE_WAITING;
if (instance)
{
CanPreventAddsResurrect();
instance->SetBossState(DATA_THEKAL, IN_PROGRESS);
}
}
else
ResurrectTimer -= diff;
// No break needed here
case PHASE_WAITING:
return;
case PHASE_NORMAL:
if (MortalCleaveTimer < diff)
{
DoCastVictim(SPELL_MORTAL_CLEAVE);
MortalCleaveTimer = urand(15, 20) * IN_MILLISECONDS;
}
else
MortalCleaveTimer -= diff;
if (SilenceTimer < diff)
{
DoCastVictim(SPELL_SILENCE, true);
SilenceTimer = urand(20, 25) * IN_MILLISECONDS;
}
else
SilenceTimer -= diff;
break;
case PHASE_TIGER:
if (ChargeTimer < diff)
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
{
DoCast(target, SPELL_CHARGE);
DoResetThreat();
AttackStart(target);
}
ChargeTimer = urand(15, 22) * IN_MILLISECONDS;
}
else
ChargeTimer -= diff;
if (FrenzyTimer < diff)
{
DoCast(me, SPELL_FRENZY, true);
FrenzyTimer = 30 * IN_MILLISECONDS;
}
else
FrenzyTimer -= diff;
if (ForcePunchTimer < diff)
{
DoCast(me, SPELL_FORCE_PUNCH);
ForcePunchTimer = urand(16, 21) * IN_MILLISECONDS;
}
else
ForcePunchTimer -= diff;
if (SummonTigersTimer < diff)
{
DoCast(me, SPELL_SUMMON_TIGERS);
SummonTigersTimer = 50 * IN_MILLISECONDS;
}
else
SummonTigersTimer -= diff;
if (!Enraged && !HealthAbovePct(11))
{
DoCast(me, SPELL_ENRAGE);
Enraged = true;
}
break;
}
DoMeleeAttackIfReady();
}
private:
InstanceScript* instance;
uint32 MortalCleaveTimer;
uint32 SilenceTimer;
uint32 FrenzyTimer;
uint32 ForcePunchTimer;
uint32 ChargeTimer;
uint32 EnrageTimer;
uint32 SummonTigersTimer;
uint32 ResurrectTimer;
bool Enraged;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetInstanceAI(creature);
}
};
/*######
## npc_zealot_lorkhan
######*/
class npc_zealot_lorkhan : public CreatureScript
{
public:
npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { }
struct npc_zealot_lorkhanAI : public boss_thekalBaseAI
{
npc_zealot_lorkhanAI(Creature* creature) : boss_thekalBaseAI(creature)
{
instance = creature->GetInstanceScript();
}
void Reset() override
{
ShieldTimer = 1 * IN_MILLISECONDS;
BloodLustTimer = 16 * IN_MILLISECONDS;
GreaterHealTimer = 32 * IN_MILLISECONDS;
DisarmTimer = 6 * IN_MILLISECONDS;
Phase = PHASE_NORMAL;
instance->SetBossState(DATA_LORKHAN, NOT_STARTED);
Revive(true);
}
void EnterCombat(Unit* /*who*/) override
{
instance->SetBossState(DATA_LORKHAN, IN_PROGRESS);
}
void OnFakeingDeath()
{
ResurrectTimer = 10 * IN_MILLISECONDS;
instance->SetBossState(DATA_LORKHAN, SPECIAL);
}
void DoAction(int32 actionId) override
{
if (actionId == ACTION_PREVENT_REVIVE)
{
if (me->IsNonMeleeSpellCast(true))
me->InterruptNonMeleeSpells(true);
Phase = PHASE_WAITING;
}
}
void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_RESURRECT)
Revive();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
switch (Phase)
{
case PHASE_FAKE_DEATH:
if (ResurrectTimer < diff)
{
if (instance->GetBossState(DATA_THEKAL) != SPECIAL || instance->GetBossState(DATA_ZATH) != SPECIAL)
{
DoCast(me, SPELL_RESURRECT);
instance->SetBossState(DATA_LORKHAN, IN_PROGRESS);
}
Phase = PHASE_WAITING;
}
else
ResurrectTimer -= diff;
// no break needed here
case PHASE_WAITING:
return;
case PHASE_NORMAL:
// Shield_Timer
if (ShieldTimer < diff)
{
DoCast(me, SPELL_SHIELD);
ShieldTimer = 61 * IN_MILLISECONDS;
}
else
ShieldTimer -= diff;
// BloodLust_Timer
if (BloodLustTimer < diff)
{
// ToDo: research if this should be cast on Thekal or Zath
DoCast(me, SPELL_BLOODLUST);
BloodLustTimer = urand(20, 28) * IN_MILLISECONDS;
}
else
BloodLustTimer -= diff;
// Casting Greaterheal to Thekal or Zath if they are in meele range.
// TODO - why this range check?
if (GreaterHealTimer < diff)
{
if (instance)
{
Unit* pThekal = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THEKAL));
Unit* pZath = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_ZATH));
if (!pThekal || !pZath)
return;
switch (urand(0, 1))
{
case 0:
if (me->IsWithinMeleeRange(pThekal))
DoCast(pThekal, SPELL_GREATER_HEAL);
break;
case 1:
if (me->IsWithinMeleeRange(pZath))
DoCast(pZath, SPELL_GREATER_HEAL);
break;
}
}
GreaterHealTimer = urand(15, 20) * IN_MILLISECONDS;
}
else
GreaterHealTimer -= diff;
// Disarm_Timer
if (DisarmTimer < diff)
{
DoCastVictim(SPELL_DISARM);
DisarmTimer = urand(15, 25) * IN_MILLISECONDS;
}
else
DisarmTimer -= diff;
break;
}
DoMeleeAttackIfReady();
}
private:
InstanceScript* instance;
uint32 ShieldTimer;
uint32 BloodLustTimer;
uint32 GreaterHealTimer;
uint32 DisarmTimer;
uint32 ResurrectTimer;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetInstanceAI(creature);
}
};
/*######
## npc_zealot_zath
######*/
class npc_zealot_zath : public CreatureScript
{
public:
npc_zealot_zath() : CreatureScript("npc_zealot_zath") { }
struct npc_zealot_zathAI : public boss_thekalBaseAI
{
npc_zealot_zathAI(Creature* creature) : boss_thekalBaseAI(creature)
{
instance = creature->GetInstanceScript();
}
void Reset() override
{
SweepingStrikesTimer = 13 * IN_MILLISECONDS;
SinisterStrikeTimer = 8 * IN_MILLISECONDS;
GougeTimer = 25 * IN_MILLISECONDS;
KickTimer = 18 * IN_MILLISECONDS;
BlindTimer = 5 * IN_MILLISECONDS;
Phase = PHASE_NORMAL;
instance->SetBossState(DATA_ZATH, NOT_STARTED);
Revive(true);
}
void EnterCombat(Unit* /*who*/) override
{
instance->SetBossState(DATA_ZATH, IN_PROGRESS);
}
void OnFakeingDeath()
{
ResurrectTimer = 10 * IN_MILLISECONDS;
instance->SetBossState(DATA_ZATH, SPECIAL);
}
void DoAction(int32 actionId) override
{
if (actionId == ACTION_PREVENT_REVIVE)
{
if (me->IsNonMeleeSpellCast(true))
me->InterruptNonMeleeSpells(true);
Phase = PHASE_WAITING;
}
}
void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_RESURRECT)
Revive();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
switch (Phase)
{
case PHASE_FAKE_DEATH:
if (ResurrectTimer < diff)
{
if (instance->GetBossState(DATA_THEKAL) != SPECIAL || instance->GetBossState(DATA_LORKHAN) != SPECIAL)
{
DoCast(me, SPELL_RESURRECT);
instance->SetBossState(DATA_ZATH, IN_PROGRESS);
}
Phase = PHASE_WAITING;
}
else
ResurrectTimer -= diff;
// no break needed here
case PHASE_WAITING:
return;
case PHASE_NORMAL:
// SweepingStrikes_Timer
if (SweepingStrikesTimer < diff)
{
DoCastVictim(SPELL_SWEEPING_STRIKES);
SweepingStrikesTimer = urand(22, 26) * IN_MILLISECONDS;
}
else
SweepingStrikesTimer -= diff;
// SinisterStrike_Timer
if (SinisterStrikeTimer < diff)
{
DoCastVictim(SPELL_SINISTER_STRIKE);
SinisterStrikeTimer = urand(8, 16) * IN_MILLISECONDS;
}
else
SinisterStrikeTimer -= diff;
// Gouge_Timer
if (GougeTimer < diff)
{
DoCastVictim(SPELL_GOUGE);
if (DoGetThreat(me->GetVictim()))
DoModifyThreatPercent(me->GetVictim(), -100);
GougeTimer = urand(17, 27) * IN_MILLISECONDS;
}
else
GougeTimer -= diff;
// Kick_Timer
if (KickTimer < diff)
{
DoCastVictim(SPELL_KICK);
KickTimer = urand(15, 25) * IN_MILLISECONDS;
}
else
KickTimer -= diff;
// Blind_Timer
if (BlindTimer < diff)
{
DoCastVictim(SPELL_BLIND);
BlindTimer = urand(10, 20) * IN_MILLISECONDS;
}
else
BlindTimer -= diff;
break;
}
DoMeleeAttackIfReady();
}
private:
uint32 SweepingStrikesTimer;
uint32 SinisterStrikeTimer;
uint32 GougeTimer;
uint32 KickTimer;
uint32 BlindTimer;
uint32 ResurrectTimer;
InstanceScript* instance;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetInstanceAI(creature);
}
};
void AddSC_boss_thekal()
{
new boss_thekal();
new npc_zealot_lorkhan();
new npc_zealot_zath();
}