mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-06 12:27:48 +00:00
chore(Scripts/Karazhan): rework Shade of Aran using TaskScheduler and fixes (#16689)
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
#include "ScriptedCreature.h"
|
#include "ScriptedCreature.h"
|
||||||
#include "SpellInfo.h"
|
#include "SpellInfo.h"
|
||||||
#include "karazhan.h"
|
#include "karazhan.h"
|
||||||
|
#include "TaskScheduler.h"
|
||||||
|
|
||||||
enum ShadeOfAran
|
enum ShadeOfAran
|
||||||
{
|
{
|
||||||
@@ -58,9 +59,9 @@ enum ShadeOfAran
|
|||||||
SPELL_SHADOW_PYRO = 29978,
|
SPELL_SHADOW_PYRO = 29978,
|
||||||
|
|
||||||
//Creatures
|
//Creatures
|
||||||
CREATURE_WATER_ELEMENTAL = 17167,
|
NPC_WATER_ELEMENTAL = 17167,
|
||||||
CREATURE_SHADOW_OF_ARAN = 18254,
|
NPC_SHADOW_OF_ARAN = 18254,
|
||||||
CREATURE_ARAN_BLIZZARD = 17161,
|
NPC_ARAN_BLIZZARD = 17161,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SuperSpell
|
enum SuperSpell
|
||||||
@@ -70,560 +71,458 @@ enum SuperSpell
|
|||||||
SUPER_AE,
|
SUPER_AE,
|
||||||
};
|
};
|
||||||
|
|
||||||
class boss_shade_of_aran : public CreatureScript
|
enum Groups
|
||||||
{
|
{
|
||||||
public:
|
GROUP_FLAMEWREATH = 0,
|
||||||
boss_shade_of_aran() : CreatureScript("boss_shade_of_aran") { }
|
GROUP_DRINKING = 1
|
||||||
|
};
|
||||||
|
|
||||||
struct boss_aranAI : public BossAI
|
Position const elementalPos[4] =
|
||||||
|
{
|
||||||
|
{-11168.1f, -1939.29f, 232.092f, 1.46f},
|
||||||
|
{-11138.2f, -1915.38f, 232.092f, 3.00f},
|
||||||
|
{-11161.7f, -1885.36f, 232.092f, 4.59f},
|
||||||
|
{-11192.4f, -1909.36f, 232.092f, 6.19f}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct boss_shade_of_aran : public BossAI
|
||||||
|
{
|
||||||
|
boss_shade_of_aran(Creature* creature) : BossAI(creature, DATA_ARAN)
|
||||||
{
|
{
|
||||||
boss_aranAI(Creature* creature) : BossAI(creature, DATA_ARAN)
|
scheduler.SetValidator([this]
|
||||||
{
|
{
|
||||||
|
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 LastSuperSpell;
|
||||||
|
|
||||||
|
ObjectGuid FlameWreathTarget[3];
|
||||||
|
float FWTargPosX[3];
|
||||||
|
float FWTargPosY[3];
|
||||||
|
|
||||||
|
uint32 CurrentNormalSpell;
|
||||||
|
|
||||||
|
bool Drinking;
|
||||||
|
|
||||||
|
void Reset() override
|
||||||
|
{
|
||||||
|
BossAI::Reset();
|
||||||
|
drinkScheduler.CancelAll();
|
||||||
|
LastSuperSpell = rand() % 3;
|
||||||
|
|
||||||
|
for (uint8 i = 0; i < 3; ++i)
|
||||||
|
FlameWreathTarget[i].Clear();
|
||||||
|
|
||||||
|
CurrentNormalSpell = 0;
|
||||||
|
|
||||||
|
_arcaneCooledDown = true;
|
||||||
|
_fireCooledDown = true;
|
||||||
|
_frostCooledDown = true;
|
||||||
|
|
||||||
|
Drinking = false;
|
||||||
|
|
||||||
|
// Not in progress
|
||||||
|
instance->SetData(DATA_ARAN, NOT_STARTED);
|
||||||
|
|
||||||
|
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
||||||
|
{
|
||||||
|
libraryDoor->SetGoState(GO_STATE_ACTIVE);
|
||||||
|
libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 SecondarySpellTimer;
|
ScheduleHealthCheckEvent(40, [&]{
|
||||||
uint32 NormalCastTimer;
|
Talk(SAY_ELEMENTALS);
|
||||||
uint32 SuperCastTimer;
|
|
||||||
uint32 BerserkTimer;
|
|
||||||
uint32 CloseDoorTimer; // Don't close the door right on aggro in case some people are still entering.
|
|
||||||
|
|
||||||
uint8 LastSuperSpell;
|
for(Position pos : elementalPos)
|
||||||
|
|
||||||
uint32 FlameWreathTimer;
|
|
||||||
uint32 FlameWreathCheckTime;
|
|
||||||
ObjectGuid FlameWreathTarget[3];
|
|
||||||
float FWTargPosX[3];
|
|
||||||
float FWTargPosY[3];
|
|
||||||
|
|
||||||
uint32 CurrentNormalSpell;
|
|
||||||
uint32 ArcaneCooldown;
|
|
||||||
uint32 FireCooldown;
|
|
||||||
uint32 FrostCooldown;
|
|
||||||
|
|
||||||
uint32 DrinkInterruptTimer;
|
|
||||||
|
|
||||||
bool ElementalsSpawned;
|
|
||||||
bool Drinking;
|
|
||||||
bool DrinkInturrupted;
|
|
||||||
void Reset() override
|
|
||||||
{
|
|
||||||
SecondarySpellTimer = 5000;
|
|
||||||
NormalCastTimer = 0;
|
|
||||||
SuperCastTimer = 35000;
|
|
||||||
BerserkTimer = 720000;
|
|
||||||
CloseDoorTimer = 15000;
|
|
||||||
|
|
||||||
LastSuperSpell = rand() % 3;
|
|
||||||
|
|
||||||
FlameWreathTimer = 0;
|
|
||||||
FlameWreathCheckTime = 0;
|
|
||||||
|
|
||||||
for (uint8 i = 0; i < 3; ++i)
|
|
||||||
FlameWreathTarget[i].Clear();
|
|
||||||
|
|
||||||
CurrentNormalSpell = 0;
|
|
||||||
ArcaneCooldown = 0;
|
|
||||||
FireCooldown = 0;
|
|
||||||
FrostCooldown = 0;
|
|
||||||
|
|
||||||
DrinkInterruptTimer = 10000;
|
|
||||||
|
|
||||||
ElementalsSpawned = false;
|
|
||||||
Drinking = false;
|
|
||||||
DrinkInturrupted = false;
|
|
||||||
|
|
||||||
// Not in progress
|
|
||||||
instance->SetData(DATA_ARAN, NOT_STARTED);
|
|
||||||
|
|
||||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
|
||||||
{
|
{
|
||||||
libraryDoor->SetGoState(GO_STATE_ACTIVE);
|
if(Creature* elemental = me->SummonCreature(NPC_WATER_ELEMENTAL, pos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000))
|
||||||
libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
{
|
||||||
|
if(Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true))
|
||||||
|
{
|
||||||
|
DoStartNoMovement(target);
|
||||||
|
elemental->SetInCombatWithZone();
|
||||||
|
elemental->CombatStart(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void KilledUnit(Unit* /*victim*/) override
|
void KilledUnit(Unit* /*victim*/) override
|
||||||
|
{
|
||||||
|
Talk(SAY_KILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerArcaneCooldown()
|
||||||
|
{
|
||||||
|
scheduler.Schedule(5s, [this](TaskContext)
|
||||||
{
|
{
|
||||||
Talk(SAY_KILL);
|
_arcaneCooledDown = true;
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void JustDied(Unit* /*killer*/) override
|
void TriggerFireCooldown()
|
||||||
|
{
|
||||||
|
scheduler.Schedule(5s, [this](TaskContext)
|
||||||
{
|
{
|
||||||
Talk(SAY_DEATH);
|
_fireCooledDown = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
instance->SetData(DATA_ARAN, DONE);
|
void TriggerFrostCooldown()
|
||||||
|
{
|
||||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
scheduler.Schedule(5s, [this](TaskContext)
|
||||||
{
|
|
||||||
libraryDoor->SetGoState(GO_STATE_ACTIVE);
|
|
||||||
libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JustEngagedWith(Unit* /*who*/) override
|
|
||||||
{
|
{
|
||||||
Talk(SAY_AGGRO);
|
_frostCooledDown = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
instance->SetData(DATA_ARAN, IN_PROGRESS);
|
void JustDied(Unit* /*killer*/) override
|
||||||
|
{
|
||||||
|
Talk(SAY_DEATH);
|
||||||
|
|
||||||
|
instance->SetData(DATA_ARAN, DONE);
|
||||||
|
|
||||||
|
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
||||||
|
{
|
||||||
|
libraryDoor->SetGoState(GO_STATE_ACTIVE);
|
||||||
|
libraryDoor->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JustEngagedWith(Unit* /*who*/) override
|
||||||
|
{
|
||||||
|
Talk(SAY_AGGRO);
|
||||||
|
|
||||||
|
instance->SetData(DATA_ARAN, IN_PROGRESS);
|
||||||
|
|
||||||
|
DoZoneInCombat();
|
||||||
|
|
||||||
|
//handle timed closing door
|
||||||
|
scheduler.Schedule(15s, [this](TaskContext)
|
||||||
|
{
|
||||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
||||||
{
|
{
|
||||||
libraryDoor->SetGoState(GO_STATE_READY);
|
libraryDoor->SetGoState(GO_STATE_READY);
|
||||||
libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||||
}
|
}
|
||||||
|
}).Schedule(1ms, [this](TaskContext context)
|
||||||
DoZoneInCombat();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameWreathEffect()
|
|
||||||
{
|
{
|
||||||
std::vector<Unit*> targets;
|
if (!me->IsNonMeleeSpellCast(false))
|
||||||
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList();
|
|
||||||
|
|
||||||
if (t_list.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
//store the threat list in a different container
|
|
||||||
for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr)
|
|
||||||
{
|
{
|
||||||
Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
|
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true);
|
||||||
//only on alive players
|
if (!target)
|
||||||
if (target && target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER)
|
return;
|
||||||
targets.push_back(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
//cut down to size if we have more than 3 targets
|
uint32 Spells[3];
|
||||||
while (targets.size() > 3)
|
uint8 AvailableSpells = 0;
|
||||||
targets.erase(targets.begin() + rand() % targets.size());
|
|
||||||
|
|
||||||
uint32 i = 0;
|
//Check for what spells are not on cooldown
|
||||||
for (std::vector<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
|
if (_arcaneCooledDown)
|
||||||
{
|
|
||||||
if (*itr)
|
|
||||||
{
|
{
|
||||||
FlameWreathTarget[i] = (*itr)->GetGUID();
|
Spells[AvailableSpells] = SPELL_ARCMISSLE;
|
||||||
FWTargPosX[i] = (*itr)->GetPositionX();
|
++AvailableSpells;
|
||||||
FWTargPosY[i] = (*itr)->GetPositionY();
|
}
|
||||||
DoCast((*itr), SPELL_FLAME_WREATH, true);
|
if (_fireCooledDown)
|
||||||
++i;
|
{
|
||||||
|
Spells[AvailableSpells] = SPELL_FIREBALL;
|
||||||
|
++AvailableSpells;
|
||||||
|
}
|
||||||
|
if (_frostCooledDown)
|
||||||
|
{
|
||||||
|
Spells[AvailableSpells] = SPELL_FROSTBOLT;
|
||||||
|
++AvailableSpells;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If no available spells wait 1 second and try again
|
||||||
|
if (AvailableSpells)
|
||||||
|
{
|
||||||
|
CurrentNormalSpell = Spells[rand() % AvailableSpells];
|
||||||
|
DoCast(target, CurrentNormalSpell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
context.Repeat(10s);
|
||||||
|
}).Schedule(5s, [this](TaskContext context)
|
||||||
void UpdateAI(uint32 diff) override
|
|
||||||
{
|
{
|
||||||
if (!UpdateVictim())
|
switch (urand(0, 1))
|
||||||
return;
|
|
||||||
|
|
||||||
if (CloseDoorTimer)
|
|
||||||
{
|
{
|
||||||
if (CloseDoorTimer <= diff)
|
case 0:
|
||||||
{
|
DoCastSelf(SPELL_AOE_CS);
|
||||||
if (GameObject* libraryDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR)))
|
break;
|
||||||
|
case 1:
|
||||||
|
DoCastRandomTarget(SPELL_CHAINSOFICE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
context.Repeat(5s, 20s);
|
||||||
|
}).Schedule(35s, [this](TaskContext context)
|
||||||
|
{
|
||||||
|
uint8 Available[2];
|
||||||
|
|
||||||
|
switch (LastSuperSpell)
|
||||||
|
{
|
||||||
|
case SUPER_AE:
|
||||||
|
Available[0] = SUPER_FLAME;
|
||||||
|
Available[1] = SUPER_BLIZZARD;
|
||||||
|
break;
|
||||||
|
case SUPER_FLAME:
|
||||||
|
Available[0] = SUPER_AE;
|
||||||
|
Available[1] = SUPER_BLIZZARD;
|
||||||
|
break;
|
||||||
|
case SUPER_BLIZZARD:
|
||||||
|
Available[0] = SUPER_FLAME;
|
||||||
|
Available[1] = SUPER_AE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LastSuperSpell = Available[urand(0, 1)];
|
||||||
|
|
||||||
|
switch (LastSuperSpell)
|
||||||
|
{
|
||||||
|
case SUPER_AE:
|
||||||
|
Talk(SAY_EXPLOSION);
|
||||||
|
|
||||||
|
DoCastSelf(SPELL_BLINK_CENTER, true);
|
||||||
|
DoCastSelf(SPELL_PLAYERPULL, true);
|
||||||
|
DoCastSelf(SPELL_MASSSLOW, true);
|
||||||
|
DoCastSelf(SPELL_AEXPLOSION, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SUPER_FLAME:
|
||||||
|
Talk(SAY_FLAMEWREATH);
|
||||||
|
|
||||||
|
scheduler.Schedule(20s, GROUP_FLAMEWREATH, [this](TaskContext)
|
||||||
{
|
{
|
||||||
libraryDoor->SetGoState(GO_STATE_READY);
|
scheduler.CancelGroup(GROUP_FLAMEWREATH);
|
||||||
libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
}).Schedule(500ms, GROUP_FLAMEWREATH, [this](TaskContext context)
|
||||||
}
|
|
||||||
CloseDoorTimer = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CloseDoorTimer -= diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Cooldowns for casts
|
|
||||||
if (ArcaneCooldown)
|
|
||||||
{
|
|
||||||
if (ArcaneCooldown >= diff)
|
|
||||||
ArcaneCooldown -= diff;
|
|
||||||
else
|
|
||||||
ArcaneCooldown = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FireCooldown)
|
|
||||||
{
|
|
||||||
if (FireCooldown >= diff)
|
|
||||||
FireCooldown -= diff;
|
|
||||||
else
|
|
||||||
FireCooldown = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FrostCooldown)
|
|
||||||
{
|
|
||||||
if (FrostCooldown >= diff)
|
|
||||||
FrostCooldown -= diff;
|
|
||||||
else
|
|
||||||
FrostCooldown = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Drinking && me->GetMaxPower(POWER_MANA) && (me->GetPower(POWER_MANA) * 100 / me->GetMaxPower(POWER_MANA)) < 20)
|
|
||||||
{
|
|
||||||
Drinking = true;
|
|
||||||
me->InterruptNonMeleeSpells(false);
|
|
||||||
|
|
||||||
Talk(SAY_DRINK);
|
|
||||||
|
|
||||||
if (!DrinkInturrupted)
|
|
||||||
{
|
|
||||||
DoCast(me, SPELL_MASS_POLY, true);
|
|
||||||
DoCast(me, SPELL_CONJURE, false);
|
|
||||||
DoCast(me, SPELL_DRINK, false);
|
|
||||||
me->SetStandState(UNIT_STAND_STATE_SIT);
|
|
||||||
DrinkInterruptTimer = 10000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Drink Interrupt
|
|
||||||
if (Drinking && DrinkInturrupted)
|
|
||||||
{
|
|
||||||
Drinking = false;
|
|
||||||
me->RemoveAurasDueToSpell(SPELL_DRINK);
|
|
||||||
me->SetStandState(UNIT_STAND_STATE_STAND);
|
|
||||||
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000);
|
|
||||||
DoCast(me, SPELL_POTION, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Drink Interrupt Timer
|
|
||||||
if (Drinking && !DrinkInturrupted)
|
|
||||||
{
|
|
||||||
if (DrinkInterruptTimer >= diff)
|
|
||||||
DrinkInterruptTimer -= diff;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
me->SetStandState(UNIT_STAND_STATE_STAND);
|
|
||||||
DoCast(me, SPELL_POTION, true);
|
|
||||||
DoCast(me, SPELL_AOE_PYROBLAST, false);
|
|
||||||
DrinkInturrupted = true;
|
|
||||||
Drinking = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Don't execute any more code if we are drinking
|
|
||||||
if (Drinking)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//Normal casts
|
|
||||||
if (NormalCastTimer <= diff)
|
|
||||||
{
|
|
||||||
if (!me->IsNonMeleeSpellCast(false))
|
|
||||||
{
|
|
||||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true);
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32 Spells[3];
|
|
||||||
uint8 AvailableSpells = 0;
|
|
||||||
|
|
||||||
//Check for what spells are not on cooldown
|
|
||||||
if (!ArcaneCooldown)
|
|
||||||
{
|
{
|
||||||
Spells[AvailableSpells] = SPELL_ARCMISSLE;
|
for (uint8 i = 0; i < 3; ++i)
|
||||||
++AvailableSpells;
|
|
||||||
}
|
|
||||||
if (!FireCooldown)
|
|
||||||
{
|
|
||||||
Spells[AvailableSpells] = SPELL_FIREBALL;
|
|
||||||
++AvailableSpells;
|
|
||||||
}
|
|
||||||
if (!FrostCooldown)
|
|
||||||
{
|
|
||||||
Spells[AvailableSpells] = SPELL_FROSTBOLT;
|
|
||||||
++AvailableSpells;
|
|
||||||
}
|
|
||||||
|
|
||||||
//If no available spells wait 1 second and try again
|
|
||||||
if (AvailableSpells)
|
|
||||||
{
|
|
||||||
CurrentNormalSpell = Spells[rand() % AvailableSpells];
|
|
||||||
DoCast(target, CurrentNormalSpell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NormalCastTimer = 1000;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
NormalCastTimer -= diff;
|
|
||||||
|
|
||||||
if (SecondarySpellTimer <= diff)
|
|
||||||
{
|
|
||||||
switch (urand(0, 1))
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
DoCast(me, SPELL_AOE_CS);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
|
|
||||||
DoCast(target, SPELL_CHAINSOFICE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SecondarySpellTimer = urand(5000, 20000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SecondarySpellTimer -= diff;
|
|
||||||
|
|
||||||
if (SuperCastTimer <= diff)
|
|
||||||
{
|
|
||||||
uint8 Available[2];
|
|
||||||
|
|
||||||
switch (LastSuperSpell)
|
|
||||||
{
|
|
||||||
case SUPER_AE:
|
|
||||||
Available[0] = SUPER_FLAME;
|
|
||||||
Available[1] = SUPER_BLIZZARD;
|
|
||||||
break;
|
|
||||||
case SUPER_FLAME:
|
|
||||||
Available[0] = SUPER_AE;
|
|
||||||
Available[1] = SUPER_BLIZZARD;
|
|
||||||
break;
|
|
||||||
case SUPER_BLIZZARD:
|
|
||||||
Available[0] = SUPER_FLAME;
|
|
||||||
Available[1] = SUPER_AE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LastSuperSpell = Available[urand(0, 1)];
|
|
||||||
|
|
||||||
switch (LastSuperSpell)
|
|
||||||
{
|
|
||||||
case SUPER_AE:
|
|
||||||
Talk(SAY_EXPLOSION);
|
|
||||||
|
|
||||||
DoCast(me, SPELL_BLINK_CENTER, true);
|
|
||||||
DoCast(me, SPELL_PLAYERPULL, true);
|
|
||||||
DoCast(me, SPELL_MASSSLOW, true);
|
|
||||||
DoCast(me, SPELL_AEXPLOSION, false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SUPER_FLAME:
|
|
||||||
Talk(SAY_FLAMEWREATH);
|
|
||||||
|
|
||||||
FlameWreathTimer = 20000;
|
|
||||||
FlameWreathCheckTime = 500;
|
|
||||||
|
|
||||||
FlameWreathTarget[0].Clear();
|
|
||||||
FlameWreathTarget[1].Clear();
|
|
||||||
FlameWreathTarget[2].Clear();
|
|
||||||
|
|
||||||
FlameWreathEffect();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SUPER_BLIZZARD:
|
|
||||||
Talk(SAY_BLIZZARD);
|
|
||||||
|
|
||||||
if (Creature* pSpawn = me->SummonCreature(CREATURE_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000))
|
|
||||||
{
|
{
|
||||||
pSpawn->SetFaction(me->GetFaction());
|
if (!FlameWreathTarget[i])
|
||||||
pSpawn->CastSpell(pSpawn, SPELL_CIRCULAR_BLIZZARD, false);
|
continue;
|
||||||
|
|
||||||
|
Unit* unit = ObjectAccessor::GetUnit(*me, FlameWreathTarget[i]);
|
||||||
|
if (unit && !unit->IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3))
|
||||||
|
{
|
||||||
|
unit->CastSpell(unit, 20476, true, 0, 0, me->GetGUID());
|
||||||
|
unit->CastSpell(unit, 11027, true);
|
||||||
|
FlameWreathTarget[i].Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
context.Repeat(500ms);
|
||||||
}
|
});
|
||||||
|
|
||||||
SuperCastTimer = urand(35000, 40000);
|
FlameWreathTarget[0].Clear();
|
||||||
}
|
FlameWreathTarget[1].Clear();
|
||||||
else
|
FlameWreathTarget[2].Clear();
|
||||||
SuperCastTimer -= diff;
|
|
||||||
|
|
||||||
if (!ElementalsSpawned && HealthBelowPct(40))
|
FlameWreathEffect();
|
||||||
{
|
break;
|
||||||
ElementalsSpawned = true;
|
|
||||||
|
|
||||||
Creature* ElementalOne = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11168.1f, -1939.29f, 232.092f, 1.46f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000);
|
case SUPER_BLIZZARD:
|
||||||
Creature* ElementalTwo = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11138.2f, -1915.38f, 232.092f, 3.00f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000);
|
Talk(SAY_BLIZZARD);
|
||||||
Creature* ElementalThree = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11161.7f, -1885.36f, 232.092f, 4.59f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000);
|
|
||||||
Creature* ElementalFour = me->SummonCreature(CREATURE_WATER_ELEMENTAL, -11192.4f, -1909.36f, 232.092f, 6.19f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000);
|
|
||||||
|
|
||||||
if (ElementalOne)
|
if (Creature* pSpawn = me->SummonCreature(NPC_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000))
|
||||||
{
|
|
||||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true);
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DoStartNoMovement(target);
|
|
||||||
ElementalOne->SetInCombatWithZone();
|
|
||||||
ElementalOne->CombatStart(target);
|
|
||||||
ElementalOne->SetFaction(me->GetFaction());
|
|
||||||
ElementalOne->SetUnitMovementFlags(MOVEMENTFLAG_ROOT);
|
|
||||||
ElementalOne->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ElementalTwo)
|
|
||||||
{
|
|
||||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true);
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DoStartNoMovement(target);
|
|
||||||
ElementalTwo->SetInCombatWithZone();
|
|
||||||
ElementalTwo->CombatStart(target);
|
|
||||||
ElementalTwo->SetFaction(me->GetFaction());
|
|
||||||
ElementalTwo->SetUnitMovementFlags(MOVEMENTFLAG_ROOT);
|
|
||||||
ElementalTwo->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ElementalThree)
|
|
||||||
{
|
|
||||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true);
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DoStartNoMovement(target);
|
|
||||||
ElementalThree->SetInCombatWithZone();
|
|
||||||
ElementalThree->CombatStart(target);
|
|
||||||
ElementalThree->SetFaction(me->GetFaction());
|
|
||||||
ElementalThree->SetUnitMovementFlags(MOVEMENTFLAG_ROOT);
|
|
||||||
ElementalThree->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ElementalFour)
|
|
||||||
{
|
|
||||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true);
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DoStartNoMovement(target);
|
|
||||||
ElementalFour->SetInCombatWithZone();
|
|
||||||
ElementalFour->CombatStart(target);
|
|
||||||
ElementalFour->SetFaction(me->GetFaction());
|
|
||||||
ElementalFour->SetUnitMovementFlags(MOVEMENTFLAG_ROOT);
|
|
||||||
ElementalFour->SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Talk(SAY_ELEMENTALS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BerserkTimer <= diff)
|
|
||||||
{
|
|
||||||
for (uint32 i = 0; i < 5; ++i)
|
|
||||||
{
|
|
||||||
if (Creature* unit = me->SummonCreature(CREATURE_SHADOW_OF_ARAN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000))
|
|
||||||
{
|
{
|
||||||
unit->Attack(me->GetVictim(), true);
|
pSpawn->SetFaction(me->GetFaction());
|
||||||
unit->SetFaction(me->GetFaction());
|
pSpawn->CastSpell(me, SPELL_CIRCULAR_BLIZZARD, false);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
Talk(SAY_TIMEOVER);
|
|
||||||
|
|
||||||
BerserkTimer = 60000;
|
|
||||||
}
|
}
|
||||||
else
|
context.Repeat(35s, 40s);
|
||||||
BerserkTimer -= diff;
|
}).Schedule(12min, [this](TaskContext context)
|
||||||
|
{
|
||||||
//Flame Wreath check
|
for (uint32 i = 0; i < 5; ++i)
|
||||||
if (FlameWreathTimer)
|
|
||||||
{
|
{
|
||||||
if (FlameWreathTimer >= diff)
|
if (Creature* unit = me->SummonCreature(NPC_SHADOW_OF_ARAN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000))
|
||||||
FlameWreathTimer -= diff;
|
|
||||||
else
|
|
||||||
FlameWreathTimer = 0;
|
|
||||||
|
|
||||||
if (FlameWreathCheckTime <= diff)
|
|
||||||
{
|
{
|
||||||
for (uint8 i = 0; i < 3; ++i)
|
unit->Attack(me->GetVictim(), true);
|
||||||
{
|
unit->SetFaction(me->GetFaction());
|
||||||
if (!FlameWreathTarget[i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Unit* unit = ObjectAccessor::GetUnit(*me, FlameWreathTarget[i]);
|
|
||||||
if (unit && !unit->IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3))
|
|
||||||
{
|
|
||||||
unit->CastSpell(unit, 20476, true, 0, 0, me->GetGUID());
|
|
||||||
unit->CastSpell(unit, 11027, true);
|
|
||||||
FlameWreathTarget[i].Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FlameWreathCheckTime = 500;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
FlameWreathCheckTime -= diff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ArcaneCooldown && FireCooldown && FrostCooldown)
|
Talk(SAY_TIMEOVER);
|
||||||
DoMeleeAttackIfReady();
|
|
||||||
|
context.Repeat(1min);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlameWreathEffect()
|
||||||
|
{
|
||||||
|
std::vector<Unit*> targets;
|
||||||
|
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList();
|
||||||
|
|
||||||
|
if (t_list.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//store the threat list in a different container
|
||||||
|
for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr)
|
||||||
|
{
|
||||||
|
Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
|
||||||
|
//only on alive players
|
||||||
|
if (target && target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER)
|
||||||
|
targets.push_back(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
//cut down to size if we have more than 3 targets
|
||||||
|
while (targets.size() > 3)
|
||||||
|
targets.erase(targets.begin() + rand() % targets.size());
|
||||||
|
|
||||||
|
uint32 i = 0;
|
||||||
|
for (std::vector<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
|
||||||
{
|
{
|
||||||
if (!DrinkInturrupted && Drinking && damage)
|
if (*itr)
|
||||||
DrinkInturrupted = true;
|
{
|
||||||
|
FlameWreathTarget[i] = (*itr)->GetGUID();
|
||||||
|
FWTargPosX[i] = (*itr)->GetPositionX();
|
||||||
|
FWTargPosY[i] = (*itr)->GetPositionY();
|
||||||
|
DoCast((*itr), SPELL_FLAME_WREATH, true);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SpellHit(Unit* /*pAttacker*/, SpellInfo const* Spell) override
|
void UpdateAI(uint32 diff) override
|
||||||
|
{
|
||||||
|
scheduler.Update(diff);
|
||||||
|
drinkScheduler.Update(diff);
|
||||||
|
|
||||||
|
if (!UpdateVictim())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Drinking && me->GetMaxPower(POWER_MANA) && (me->GetPower(POWER_MANA) * 100 / me->GetMaxPower(POWER_MANA)) < 20)
|
||||||
{
|
{
|
||||||
//We only care about interrupt effects and only if they are durring a spell currently being cast
|
Drinking = true;
|
||||||
if ((Spell->Effects[0].Effect != SPELL_EFFECT_INTERRUPT_CAST &&
|
|
||||||
Spell->Effects[1].Effect != SPELL_EFFECT_INTERRUPT_CAST &&
|
|
||||||
Spell->Effects[2].Effect != SPELL_EFFECT_INTERRUPT_CAST) || !me->IsNonMeleeSpellCast(false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
//Interrupt effect
|
|
||||||
me->InterruptNonMeleeSpells(false);
|
me->InterruptNonMeleeSpells(false);
|
||||||
|
|
||||||
//Normally we would set the cooldown equal to the spell duration
|
Talk(SAY_DRINK);
|
||||||
//but we do not have access to the DurationStore
|
|
||||||
|
|
||||||
switch (CurrentNormalSpell)
|
scheduler.DelayAll(10s);
|
||||||
|
DoCastSelf(SPELL_MASS_POLY, true);
|
||||||
|
DoCastSelf(SPELL_CONJURE, false);
|
||||||
|
me->SetReactState(REACT_PASSIVE);
|
||||||
|
me->SetStandState(UNIT_STAND_STATE_SIT);
|
||||||
|
DoCastSelf(SPELL_DRINK, true);
|
||||||
|
_currentHealth = me->GetHealth();
|
||||||
|
drinkScheduler.Schedule(500ms, GROUP_DRINKING, [this](TaskContext context)
|
||||||
{
|
{
|
||||||
case SPELL_ARCMISSLE:
|
//check for damage to interrupt
|
||||||
ArcaneCooldown = 5000;
|
if(CheckDamageDuringDrinking(_currentHealth))
|
||||||
break;
|
{
|
||||||
case SPELL_FIREBALL:
|
Drinking = false;
|
||||||
FireCooldown = 5000;
|
me->RemoveAurasDueToSpell(SPELL_DRINK);
|
||||||
break;
|
me->SetStandState(UNIT_STAND_STATE_STAND);
|
||||||
case SPELL_FROSTBOLT:
|
me->SetReactState(REACT_AGGRESSIVE);
|
||||||
FrostCooldown = 5000;
|
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000);
|
||||||
break;
|
DoCastSelf(SPELL_POTION, false);
|
||||||
|
DoCastSelf(SPELL_AOE_PYROBLAST, false);
|
||||||
|
drinkScheduler.CancelGroup(GROUP_DRINKING);
|
||||||
|
} else {
|
||||||
|
context.Repeat(500ms);
|
||||||
|
}
|
||||||
|
}).Schedule(10s, GROUP_DRINKING, [this](TaskContext)
|
||||||
|
{
|
||||||
|
me->SetStandState(UNIT_STAND_STATE_STAND);
|
||||||
|
me->SetReactState(REACT_AGGRESSIVE);
|
||||||
|
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000);
|
||||||
|
DoCastSelf(SPELL_POTION, true);
|
||||||
|
DoCastSelf(SPELL_AOE_PYROBLAST, false);
|
||||||
|
|
||||||
|
Drinking = false;
|
||||||
|
drinkScheduler.CancelGroup(GROUP_DRINKING);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_arcaneCooledDown && _fireCooledDown && _frostCooledDown && !Drinking)
|
||||||
|
DoMeleeAttackIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckDamageDuringDrinking(uint32 oldHealth)
|
||||||
|
{
|
||||||
|
if (Drinking)
|
||||||
|
{
|
||||||
|
if (me->GetHealth() < oldHealth)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
return false;
|
||||||
|
|
||||||
CreatureAI* GetAI(Creature* creature) const override
|
|
||||||
{
|
|
||||||
return GetKarazhanAI<boss_aranAI>(creature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpellHit(Unit* /*pAttacker*/, SpellInfo const* Spell) override
|
||||||
|
{
|
||||||
|
//We only care about interrupt effects and only if they are durring a spell currently being cast
|
||||||
|
if ((Spell->Effects[0].Effect != SPELL_EFFECT_INTERRUPT_CAST &&
|
||||||
|
Spell->Effects[1].Effect != SPELL_EFFECT_INTERRUPT_CAST &&
|
||||||
|
Spell->Effects[2].Effect != SPELL_EFFECT_INTERRUPT_CAST) || !me->IsNonMeleeSpellCast(false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Interrupt effect
|
||||||
|
me->InterruptNonMeleeSpells(false);
|
||||||
|
|
||||||
|
//Normally we would set the cooldown equal to the spell duration
|
||||||
|
//but we do not have access to the DurationStore
|
||||||
|
|
||||||
|
switch (CurrentNormalSpell)
|
||||||
|
{
|
||||||
|
case SPELL_ARCMISSLE:
|
||||||
|
TriggerArcaneCooldown();
|
||||||
|
break;
|
||||||
|
case SPELL_FIREBALL:
|
||||||
|
TriggerFireCooldown();
|
||||||
|
break;
|
||||||
|
case SPELL_FROSTBOLT:
|
||||||
|
TriggerFrostCooldown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
TaskScheduler drinkScheduler;
|
||||||
|
|
||||||
|
bool _arcaneCooledDown;
|
||||||
|
bool _fireCooledDown;
|
||||||
|
bool _frostCooledDown;
|
||||||
|
uint32 _currentHealth;
|
||||||
};
|
};
|
||||||
|
|
||||||
class npc_aran_elemental : public CreatureScript
|
struct npc_aran_elemental : public ScriptedAI
|
||||||
{
|
{
|
||||||
public:
|
npc_aran_elemental(Creature* creature) : ScriptedAI(creature)
|
||||||
npc_aran_elemental() : CreatureScript("npc_aran_elemental") { }
|
|
||||||
|
|
||||||
CreatureAI* GetAI(Creature* creature) const override
|
|
||||||
{
|
{
|
||||||
return new water_elementalAI(creature);
|
SetCombatMovement(false);
|
||||||
|
_scheduler.SetValidator([this]
|
||||||
|
{
|
||||||
|
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct water_elementalAI : public ScriptedAI
|
void Reset() override
|
||||||
{
|
{
|
||||||
water_elementalAI(Creature* creature) : ScriptedAI(creature)
|
_scheduler.CancelAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JustEngagedWith(Unit* /*who*/) override
|
||||||
|
{
|
||||||
|
_scheduler.Schedule(2s, [this](TaskContext context)
|
||||||
{
|
{
|
||||||
}
|
DoCastVictim(SPELL_WATERBOLT);
|
||||||
|
context.Repeat(2s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
uint32 CastTimer;
|
void UpdateAI(uint32 diff) override
|
||||||
|
{
|
||||||
|
if (!UpdateVictim())
|
||||||
|
return;
|
||||||
|
|
||||||
void Reset() override
|
_scheduler.Update(diff);
|
||||||
{
|
}
|
||||||
CastTimer = 2000 + (rand() % 3000);
|
private:
|
||||||
}
|
TaskScheduler _scheduler;
|
||||||
|
|
||||||
void JustEngagedWith(Unit* /*who*/) override { }
|
|
||||||
|
|
||||||
void UpdateAI(uint32 diff) override
|
|
||||||
{
|
|
||||||
if (!UpdateVictim())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (CastTimer <= diff)
|
|
||||||
{
|
|
||||||
DoCastVictim(SPELL_WATERBOLT);
|
|
||||||
CastTimer = urand(2000, 5000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CastTimer -= diff;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void AddSC_boss_shade_of_aran()
|
void AddSC_boss_shade_of_aran()
|
||||||
{
|
{
|
||||||
new boss_shade_of_aran();
|
RegisterKarazhanCreatureAI(boss_shade_of_aran);
|
||||||
new npc_aran_elemental();
|
RegisterKarazhanCreatureAI(npc_aran_elemental);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user