/*
* 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 Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "CreatureScript.h"
#include "GameObjectScript.h"
#include "Group.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
enum TheFelAndTheFurious
{
SPELL_ROCKET_LAUNCHER = 38083
};
class spell_q10612_10613_the_fel_and_the_furious : public SpellScript
{
PrepareSpellScript(spell_q10612_10613_the_fel_and_the_furious);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_ROCKET_LAUNCHER });
}
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
Player* charmer = GetCaster()->GetCharmerOrOwnerPlayerOrPlayerItself();
if (!charmer)
return;
std::list gList;
GetCaster()->GetGameObjectListWithEntryInGrid(gList, 184979, 30.0f);
uint8 counter = 0;
for (std::list::const_iterator itr = gList.begin(); itr != gList.end(); ++itr, ++counter)
{
if (counter >= 10)
break;
GameObject* go = *itr;
if (!go->isSpawned())
continue;
Creature* cr2 = go->SummonTrigger(go->GetPositionX(), go->GetPositionY(), go->GetPositionZ() + 2.0f, 0.0f, 100);
if (cr2)
{
cr2->SetFaction(FACTION_MONSTER);
cr2->ReplaceAllUnitFlags(UNIT_FLAG_NONE);
GetCaster()->CastSpell(cr2, SPELL_ROCKET_LAUNCHER, true);
}
go->SetLootState(GO_JUST_DEACTIVATED);
charmer->KilledMonsterCredit(21959);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_q10612_10613_the_fel_and_the_furious::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_q10563_q10596_to_legion_hold_aura : public AuraScript
{
PrepareAuraScript(spell_q10563_q10596_to_legion_hold_aura);
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Player* player = GetTarget()->ToPlayer())
{
player->KilledMonsterCredit(21502);
player->SetControlled(false, UNIT_STATE_STUNNED);
}
}
void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Player* player = GetTarget()->ToPlayer())
{
player->SetControlled(true, UNIT_STATE_STUNNED);
player->SummonCreature(21633, -3311.13f, 2946.15f, 171.1f, 4.86f, TEMPSUMMON_TIMED_DESPAWN, 64000);
}
}
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_q10563_q10596_to_legion_hold_aura::HandleEffectApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
OnEffectRemove += AuraEffectRemoveFn(spell_q10563_q10596_to_legion_hold_aura::HandleEffectRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
}
};
/*#####
# npc_invis_infernal_caster
#####*/
enum InvisInfernalCaster
{
EVENT_CAST_SUMMON_INFERNAL = 1,
NPC_INFERNAL_ATTACKER = 21419,
MODEL_INVISIBLE = 20577,
MODEL_INFERNAL = 17312,
SPELL_SUMMON_INFERNAL = 37277,
TYPE_INFERNAL = 1,
DATA_DIED = 1
};
class npc_invis_infernal_caster : public CreatureScript
{
public:
npc_invis_infernal_caster() : CreatureScript("npc_invis_infernal_caster") { }
struct npc_invis_infernal_casterAI : public ScriptedAI
{
npc_invis_infernal_casterAI(Creature* creature) : ScriptedAI(creature)
{
ground = 0.f;
}
void Reset() override
{
ground = me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
SummonInfernal();
events.ScheduleEvent(EVENT_CAST_SUMMON_INFERNAL, urand(1000, 3000));
}
void SetData(uint32 id, uint32 data) override
{
if (id == TYPE_INFERNAL && data == DATA_DIED)
SummonInfernal();
}
void SummonInfernal()
{
Creature* infernal = me->SummonCreature(NPC_INFERNAL_ATTACKER, me->GetPositionX(), me->GetPositionY(), ground + 0.05f, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0);
infernalGUID = infernal->GetGUID();
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CAST_SUMMON_INFERNAL:
{
if (Unit* infernal = ObjectAccessor::GetUnit(*me, infernalGUID))
if (infernal->GetDisplayId() == MODEL_INVISIBLE)
me->CastSpell(infernal, SPELL_SUMMON_INFERNAL, true);
events.ScheduleEvent(EVENT_CAST_SUMMON_INFERNAL, 12000);
break;
}
default:
break;
}
}
}
private:
EventMap events;
ObjectGuid infernalGUID;
float ground;
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_invis_infernal_casterAI(creature);
}
};
/*#####
# npc_infernal_attacker
#####*/
class npc_infernal_attacker : public CreatureScript
{
public:
npc_infernal_attacker() : CreatureScript("npc_infernal_attacker") { }
struct npc_infernal_attackerAI : public ScriptedAI
{
npc_infernal_attackerAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
me->SetDisplayId(MODEL_INVISIBLE);
me->GetMotionMaster()->MoveRandom(5.0f);
}
void IsSummonedBy(WorldObject* summoner) override
{
if (!summoner)
return;
if (summoner->ToCreature())
casterGUID = summoner->ToCreature()->GetGUID();
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* caster = ObjectAccessor::GetCreature(*me, casterGUID))
caster->AI()->SetData(TYPE_INFERNAL, DATA_DIED);
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_SUMMON_INFERNAL)
{
me->RemoveUnitFlag(UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE);
me->SetImmuneToPC(false);
me->SetDisplayId(MODEL_INFERNAL);
}
}
void UpdateAI(uint32 /*diff*/) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
private:
ObjectGuid casterGUID;
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_infernal_attackerAI(creature);
}
};
/*#####
# npc_mature_netherwing_drake
#####*/
enum MatureNetherwing
{
SAY_JUST_EATEN = 0,
SPELL_PLACE_CARCASS = 38439,
SPELL_JUST_EATEN = 38502,
SPELL_NETHER_BREATH = 38467,
POINT_ID = 1,
GO_CARCASS = 185155,
QUEST_KINDNESS = 10804,
NPC_EVENT_PINGER = 22131
};
class npc_mature_netherwing_drake : public CreatureScript
{
public:
npc_mature_netherwing_drake() : CreatureScript("npc_mature_netherwing_drake") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_mature_netherwing_drakeAI(creature);
}
struct npc_mature_netherwing_drakeAI : public ScriptedAI
{
npc_mature_netherwing_drakeAI(Creature* creature) : ScriptedAI(creature) { }
ObjectGuid uiPlayerGUID;
bool bCanEat;
bool bIsEating;
uint32 EatTimer;
uint32 CastTimer;
void Reset() override
{
uiPlayerGUID.Clear();
bCanEat = false;
bIsEating = false;
EatTimer = 5000;
CastTimer = 5000;
}
void SpellHit(Unit* pCaster, SpellInfo const* spell) override
{
if (bCanEat || bIsEating)
return;
if (pCaster->IsPlayer() && spell->Id == SPELL_PLACE_CARCASS && !me->HasAura(SPELL_JUST_EATEN))
{
uiPlayerGUID = pCaster->GetGUID();
bCanEat = true;
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (id == POINT_ID)
{
bIsEating = true;
EatTimer = 7000;
me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED);
}
}
void JustReachedHome() override
{
me->GetMotionMaster()->InitDefault();
}
void UpdateAI(uint32 diff) override
{
if (bCanEat || bIsEating)
{
if (EatTimer <= diff)
{
if (bCanEat && !bIsEating)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, uiPlayerGUID))
{
if (GameObject* go = unit->FindNearestGameObject(GO_CARCASS, 10))
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MoveIdle();
me->StopMoving();
me->GetMotionMaster()->MovePoint(POINT_ID, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
}
}
bCanEat = false;
}
else if (bIsEating)
{
DoCast(me, SPELL_JUST_EATEN);
Talk(SAY_JUST_EATEN);
if (Player* player = ObjectAccessor::GetPlayer(*me, uiPlayerGUID))
{
player->KilledMonsterCredit(NPC_EVENT_PINGER);
if (GameObject* go = player->FindNearestGameObject(GO_CARCASS, 10))
go->Delete();
}
Reset();
me->GetMotionMaster()->MoveTargetedHome();
}
}
else
EatTimer -= diff;
return;
}
if (!UpdateVictim())
return;
if (CastTimer <= diff)
{
DoCastVictim(SPELL_NETHER_BREATH);
CastTimer = 5000;
}
else CastTimer -= diff;
DoMeleeAttackIfReady();
}
};
};
/*###
# npc_enslaved_netherwing_drake
####*/
enum EnshlavedNetherwingDrake
{
// Quest
QUEST_THE_FORCE_OF_NELTHARAKU = 10854,
// Spells
SPELL_HIT_FORCE_OF_NELTHARAKU = 38762,
SPELL_FORCE_OF_NELTHARAKU = 38775,
// Creatures
NPC_DRAGONMAW_SUBJUGATOR = 21718,
NPC_DRAGONMAW_WRANGLER = 21717,
NPC_ESCAPE_DUMMY = 22317,
// Point
POINT_DESPAWN = 1
};
struct npc_enslaved_netherwing_drake : public ScriptedAI
{
public:
npc_enslaved_netherwing_drake(Creature* creature) : ScriptedAI(creature)
{
_tapped = false;
Reset();
}
void Reset() override
{
scheduler.CancelAll();
if (!_tapped)
{
me->RestoreFaction();
me->SetReactState(REACT_AGGRESSIVE);
}
me->SetDisableGravity(false);
me->SetVisible(true);
}
void JustDied(Unit* /*killer*/) override
{
_tapped = false;
me->RestoreFaction();
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
Player* playerCaster = caster->ToPlayer();
if (!playerCaster)
return;
if (spell->Id == SPELL_HIT_FORCE_OF_NELTHARAKU && !_tapped &&
playerCaster->GetQuestStatus(QUEST_THE_FORCE_OF_NELTHARAKU) == QUEST_STATUS_INCOMPLETE)
{
_tapped = true;
_playerGUID = caster->GetGUID();
scheduler.Schedule(2s, [this](TaskContext)
{
me->SetFaction(FACTION_FLAYER_HUNTER); // Not sure if this is correct, it was taken off of Mordenai.
if (Unit* dragonmaw = me->FindNearestCreature(NPC_DRAGONMAW_SUBJUGATOR, 25.0f))
AttackStart(dragonmaw);
else if (Unit* dragonmaw = me->FindNearestCreature(NPC_DRAGONMAW_WRANGLER, 25.0f))
AttackStart(dragonmaw);
scheduler.Schedule(2s, [this](TaskContext)
{
_tapped = false;
Position pos;
if (Unit* escapeDummy = me->FindNearestCreature(NPC_ESCAPE_DUMMY, 30.0f))
pos = escapeDummy->GetPosition();
else
{
pos = me->GetRandomNearPosition(20.0f);
pos.m_positionZ += 25.0f;
}
me->SetDisableGravity(true);
me->GetMotionMaster()->MovePoint(POINT_DESPAWN, pos);
me->SetReactState(REACT_PASSIVE);
scheduler.Schedule(100ms, [this](TaskContext)
{
if (Player* player = _GetPlayer())
{
DoCast(player, SPELL_FORCE_OF_NELTHARAKU, true);
}
me->DespawnOrUnsummon(3s, 0s);
});
});
});
}
}
void MovementInform(uint32 type, uint32 data) override
{
if (type == POINT_MOTION_TYPE && data == POINT_DESPAWN)
{
me->SetVisible(false);
me->SetDisableGravity(false);
me->DespawnOrUnsummon();
}
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
private:
bool _tapped;
ObjectGuid _playerGUID;
Player* _GetPlayer() { return ObjectAccessor::GetPlayer(*me, _playerGUID); }
};
/*#####
# npc_dragonmaw_peon
#####*/
enum DragonmawPeon
{
SAY_1 = 0,
SAY_POISONED_1 = 1,
SPELL_POISON = 40468,
SPELL_KICK = 15610,
SPELL_SUNDER = 15572,
SPELL_VOMIT = 43327,
EVENT_KICK = 1,
EVENT_SUNDER = 2,
EVENT_CHECK_POISON = 3,
EVENT_WALK_TO_MUTTON = 4,
EVENT_POISONED = 5,
EVENT_KILL = 6,
DELICIOUS_MUTTON = 185893,
QUEST_A_SLOW_DEATH = 11020,
DRAGONMAW_PEON_KILL_CREDIT = 23209
};
class npc_dragonmaw_peon : public CreatureScript
{
public:
npc_dragonmaw_peon() : CreatureScript("npc_dragonmaw_peon") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_dragonmaw_peonAI(creature);
}
struct npc_dragonmaw_peonAI : public ScriptedAI
{
npc_dragonmaw_peonAI(Creature* creature) : ScriptedAI(creature) { }
EventMap events;
ObjectGuid PlayerGUID;
bool Tapped;
void Reset() override
{
events.Reset();
PlayerGUID.Clear();
Tapped = false;
}
void JustEngagedWith(Unit* /*who*/) override
{
events.ScheduleEvent(EVENT_KICK, urand(5000, 10000));
events.ScheduleEvent(EVENT_SUNDER, urand(5000, 10000));
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (!caster)
return;
PlayerGUID = caster->GetGUID();
if (caster->IsPlayer() && spell->Id == SPELL_POISON && !Tapped)
{
Tapped = true;
caster->GetClosePoint(x, y, z, me->GetObjectSize());
Talk(SAY_1);
events.ScheduleEvent(EVENT_WALK_TO_MUTTON, 0);
}
}
void MovementInform(uint32 /*type*/, uint32 id) override
{
if (id == 1)
{
if (GameObject* food = me->FindNearestGameObject(DELICIOUS_MUTTON, 5.0f))
me->SetFacingToObject(food);
me->HandleEmoteCommand(EMOTE_ONESHOT_EAT);
events.ScheduleEvent(EVENT_POISONED, 5000);
}
}
void CreditPlayer()
{
if (PlayerGUID)
{
Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID);
if (player && player->GetQuestStatus(QUEST_A_SLOW_DEATH) == QUEST_STATUS_INCOMPLETE)
player->KilledMonsterCredit(DRAGONMAW_PEON_KILL_CREDIT);
}
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
if (!UpdateVictim())
{
switch (events.ExecuteEvent())
{
case EVENT_WALK_TO_MUTTON:
me->SetWalk(true);
me->GetMotionMaster()->MovePoint(1, x, y, z, true);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
me->HandleEmoteCommand(EMOTE_ONESHOT_CHEER);
break;
case EVENT_POISONED:
if (GameObject* food = me->FindNearestGameObject(DELICIOUS_MUTTON, 5.0f))
food->RemoveFromWorld();
if (roll_chance_i(20))
Talk(SAY_POISONED_1);
CreditPlayer();
me->CastSpell(me, SPELL_VOMIT);
events.ScheduleEvent(EVENT_KILL, 5000);
break;
case EVENT_KILL:
Unit::DealDamage(me, me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
break;
}
return;
}
switch (events.ExecuteEvent())
{
case EVENT_KICK:
if (me->GetVictim()->HasUnitState(SPELL_STATE_CASTING))
DoCastVictim(SPELL_KICK);
events.RepeatEvent(urand(5000, 10000));
break;
case EVENT_SUNDER:
DoCastVictim(SPELL_SUNDER);
events.RepeatEvent(urand(5000, 10000));
break;
}
DoMeleeAttackIfReady();
}
private:
float x, y, z;
};
};
/*######
## npc_drake_dealer_hurlunk
######*/
class npc_drake_dealer_hurlunk : public CreatureScript
{
public:
npc_drake_dealer_hurlunk() : CreatureScript("npc_drake_dealer_hurlunk") { }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_TRADE)
player->GetSession()->SendListInventory(creature->GetGUID());
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsVendor() && player->GetReputationRank(1015) == REP_EXALTED)
AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
};
/*######
## npc_flanis_swiftwing_and_kagrosh
######*/
enum Flanis : uint32
{
QUEST_THE_FATE_OF_FLANIS = 10583,
ITEM_FLAUNISS_PACK = 30658,
GOSSIP_MENU_FLANIS = 8356,
};
enum Kagrosh : uint32
{
QUEST_THE_FATE_OF_KAGROSH = 10601,
ITEM_KAGROSHS_PACK = 30659,
GOSSIP_MENU_KAGROSH = 8371,
};
class npcs_flanis_swiftwing_and_kagrosh : public CreatureScript
{
public:
npcs_flanis_swiftwing_and_kagrosh() : CreatureScript("npcs_flanis_swiftwing_and_kagrosh") { }
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_INFO_DEF + 1)
{
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_FLAUNISS_PACK, 1, nullptr);
if (msg == EQUIP_ERR_OK)
{
player->StoreNewItem(dest, ITEM_FLAUNISS_PACK, true);
}
}
if (action == GOSSIP_ACTION_INFO_DEF + 2)
{
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_KAGROSHS_PACK, 1, nullptr);
if (msg == EQUIP_ERR_OK)
{
player->StoreNewItem(dest, ITEM_KAGROSHS_PACK, true);
}
}
CloseGossipMenuFor(player);
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (player->GetQuestStatus(QUEST_THE_FATE_OF_FLANIS) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(ITEM_FLAUNISS_PACK, 1, true))
AddGossipItemFor(player, GOSSIP_MENU_FLANIS, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
if (player->GetQuestStatus(QUEST_THE_FATE_OF_KAGROSH) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(ITEM_KAGROSHS_PACK, 1, true))
AddGossipItemFor(player, GOSSIP_MENU_KAGROSH, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
};
/*####
# npc_karynaku
####*/
enum Karynaku
{
QUEST_ALLY_OF_NETHER = 10870,
QUEST_ZUHULED_THE_WACK = 10866,
NPC_ZUHULED_THE_WACKED = 11980,
TAXI_PATH_ID = 649,
};
class npc_karynaku : public CreatureScript
{
public:
npc_karynaku() : CreatureScript("npc_karynaku") { }
bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) override
{
if (quest->GetQuestId() == QUEST_ALLY_OF_NETHER)
player->ActivateTaxiPathTo(TAXI_PATH_ID);
if (quest->GetQuestId() == QUEST_ZUHULED_THE_WACK)
creature->SummonCreature(NPC_ZUHULED_THE_WACKED, -4204.94f, 316.397f, 122.508f, 1.309f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300000);
return true;
}
};
/*#####
# Quest: Battle of the crimson watch
#####*/
#define QUEST_BATTLE_OF_THE_CRIMSON_WATCH 10781
#define EVENT_AREA_RADIUS 65 //65yds
#define EVENT_COOLDOWN 30000 //in ms. appear after event completed or failed (should be = Adds despawn time)
struct TorlothCinematic
{
uint32 creature, Timer;
};
// Creature 0 - Torloth, 1 - Illidan
static TorlothCinematic TorlothAnim[] =
{
{0, 2000},
{1, 7000},
{0, 3000},
{0, 2000}, // Torloth stand
{0, 1000},
{0, 3000},
{0, 0}
};
struct Location
{
float x, y, z, o;
};
//Cordinates for Spawns
static Location SpawnLocation[] =
{
//Cords used for:
{-4615.8556f, 1342.2532f, 139.9f, 1.612f}, //Illidari Soldier
{-4598.9365f, 1377.3182f, 139.9f, 3.917f}, //Illidari Soldier
{-4598.4697f, 1360.8999f, 139.9f, 2.427f}, //Illidari Soldier
{-4589.3599f, 1369.1061f, 139.9f, 3.165f}, //Illidari Soldier
{-4608.3477f, 1386.0076f, 139.9f, 4.108f}, //Illidari Soldier
{-4633.1889f, 1359.8033f, 139.9f, 0.949f}, //Illidari Soldier
{-4623.5791f, 1351.4574f, 139.9f, 0.971f}, //Illidari Soldier
{-4607.2988f, 1351.6099f, 139.9f, 2.416f}, //Illidari Soldier
{-4633.7764f, 1376.0417f, 139.9f, 5.608f}, //Illidari Soldier
{-4600.2461f, 1369.1240f, 139.9f, 3.056f}, //Illidari Mind Breaker
{-4631.7808f, 1367.9459f, 139.9f, 0.020f}, //Illidari Mind Breaker
{-4600.2461f, 1369.1240f, 139.9f, 3.056f}, //Illidari Highlord
{-4631.7808f, 1367.9459f, 139.9f, 0.020f}, //Illidari Highlord
{-4615.5586f, 1353.0031f, 139.9f, 1.540f}, //Illidari Highlord
{-4616.4736f, 1384.2170f, 139.9f, 4.971f}, //Illidari Highlord
{-4627.1240f, 1378.8752f, 139.9f, 2.544f} //Torloth The Magnificent
};
struct WaveDataCreature
{
uint8 SpawnCount, UsedSpawnPoint;
uint32 CreatureId, SpawnTimer, YellTimer;
};
static WaveDataCreature WavesInfo[] =
{
{9, 0, 22075, 10000, 7000}, //Illidari Soldier
{2, 9, 22074, 10000, 7000}, //Illidari Mind Breaker
{4, 11, 19797, 10000, 7000}, //Illidari Highlord
{1, 15, 22076, 10000, 7000} //Torloth The Magnificent
};
struct SpawnSpells
{
uint32 Timer1, Timer2, SpellId;
};
static SpawnSpells SpawnCast[] =
{
{10000, 15000, 35871}, // Illidari Soldier Cast - Spellbreaker
{10000, 10000, 38985}, // Illidari Mind Breake Cast - Focused Bursts
{35000, 35000, 22884}, // Illidari Mind Breake Cast - Psychic Scream
{20000, 20000, 17194}, // Illidari Mind Breake Cast - Mind Blast
{8000, 15000, 38010}, // Illidari Highlord Cast - Curse of Flames
{12000, 20000, 16102}, // Illidari Highlord Cast - Flamestrike
{10000, 15000, 15284}, // Torloth the Magnificent Cast - Cleave
{18000, 20000, 39082}, // Torloth the Magnificent Cast - Shadowfury
{25000, 28000, 33961} // Torloth the Magnificent Cast - Spell Reflection
};
/*######
# npc_torloth_the_magnificent
#####*/
class npc_torloth_the_magnificent : public CreatureScript
{
public:
npc_torloth_the_magnificent() : CreatureScript("npc_torloth_the_magnificent") { }
CreatureAI* GetAI(Creature* c) const override
{
return new npc_torloth_the_magnificentAI(c);
}
struct npc_torloth_the_magnificentAI : public ScriptedAI
{
npc_torloth_the_magnificentAI(Creature* creature) : ScriptedAI(creature) { }
uint32 AnimationTimer, SpellTimer1, SpellTimer2, SpellTimer3;
uint8 AnimationCount;
ObjectGuid LordIllidanGUID;
ObjectGuid AggroTargetGUID;
bool Timers;
void Reset() override
{
AnimationTimer = 4000;
AnimationCount = 0;
LordIllidanGUID.Clear();
AggroTargetGUID.Clear();
Timers = false;
me->AddUnitState(UNIT_STATE_ROOT);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetTarget();
}
void JustEngagedWith(Unit* /*who*/) override { }
void HandleAnimation()
{
Creature* creature = me;
if (TorlothAnim[AnimationCount].creature == 1)
{
creature = (ObjectAccessor::GetCreature(*me, LordIllidanGUID));
if (!creature)
return;
}
AnimationTimer = TorlothAnim[AnimationCount].Timer;
switch (AnimationCount)
{
case 0:
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 8);
break;
case 3:
me->RemoveFlag(UNIT_FIELD_BYTES_1, 8);
break;
case 5:
if (Player* AggroTarget = ObjectAccessor::GetPlayer(*me, AggroTargetGUID))
{
me->SetTarget(AggroTarget->GetGUID());
me->AddThreat(AggroTarget, 1);
me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
}
break;
case 6:
if (Player* AggroTarget = ObjectAccessor::GetPlayer(*me, AggroTargetGUID))
{
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->ClearUnitState(UNIT_STATE_ROOT);
float x, y, z;
AggroTarget->GetPosition(x, y, z);
me->GetMotionMaster()->MovePoint(0, x, y, z);
}
break;
}
++AnimationCount;
}
void UpdateAI(uint32 diff) override
{
if (AnimationTimer)
{
if (AnimationTimer <= diff)
{
HandleAnimation();
}
else AnimationTimer -= diff;
}
if (AnimationCount < 6)
{
me->CombatStop();
}
else if (!Timers)
{
SpellTimer1 = SpawnCast[6].Timer1;
SpellTimer2 = SpawnCast[7].Timer1;
SpellTimer3 = SpawnCast[8].Timer1;
Timers = true;
}
if (Timers)
{
if (SpellTimer1 <= diff)
{
DoCastVictim(SpawnCast[6].SpellId);//Cleave
SpellTimer1 = SpawnCast[6].Timer2 + (rand() % 10 * 1000);
}
else SpellTimer1 -= diff;
if (SpellTimer2 <= diff)
{
DoCastVictim(SpawnCast[7].SpellId);//Shadowfury
SpellTimer2 = SpawnCast[7].Timer2 + (rand() % 5 * 1000);
}
else SpellTimer2 -= diff;
if (SpellTimer3 <= diff)
{
DoCast(me, SpawnCast[8].SpellId);
SpellTimer3 = SpawnCast[8].Timer2 + (rand() % 7 * 1000); //Spell Reflection
}
else SpellTimer3 -= diff;
}
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer) override
{
switch (killer->GetTypeId())
{
case TYPEID_UNIT:
if (Unit* owner = killer->GetOwner())
if (Player* player = owner->ToPlayer())
player->GroupEventHappens(QUEST_BATTLE_OF_THE_CRIMSON_WATCH, me);
break;
case TYPEID_PLAYER:
if (Player* player = killer->ToPlayer())
player->GroupEventHappens(QUEST_BATTLE_OF_THE_CRIMSON_WATCH, me);
break;
default:
break;
}
if (Creature* LordIllidan = (ObjectAccessor::GetCreature(*me, LordIllidanGUID)))
LordIllidan->AI()->EnterEvadeMode();
}
};
};
/*#####
# npc_lord_illidan_stormrage
#####*/
class npc_lord_illidan_stormrage : public CreatureScript
{
public:
npc_lord_illidan_stormrage() : CreatureScript("npc_lord_illidan_stormrage") { }
CreatureAI* GetAI(Creature* c) const override
{
return new npc_lord_illidan_stormrageAI(c);
}
struct npc_lord_illidan_stormrageAI : public ScriptedAI
{
npc_lord_illidan_stormrageAI(Creature* creature) : ScriptedAI(creature) { }
ObjectGuid PlayerGUID;
uint32 WaveTimer;
uint32 AnnounceTimer;
int8 LiveCount;
uint8 WaveCount;
bool EventStarted;
bool Announced;
bool Failed;
void Reset() override
{
PlayerGUID.Clear();
WaveTimer = 10000;
AnnounceTimer = 7000;
LiveCount = 0;
WaveCount = 0;
EventStarted = false;
Announced = false;
Failed = false;
me->SetVisible(false);
}
void JustEngagedWith(Unit* /*who*/) override { }
void MoveInLineOfSight(Unit* /*who*/) override { }
void AttackStart(Unit* /*who*/) override { }
void SummonNextWave();
void CheckEventFail()
{
Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID);
if (!player)
{
Failed = true;
return;
}
if (Group* EventGroup = player->GetGroup())
{
uint8 GroupMemberCount = 0;
uint8 DeadMemberCount = 0;
uint8 FailedMemberCount = 0;
Group::MemberSlotList const& members = EventGroup->GetMemberSlots();
for (Group::member_citerator itr = members.begin(); itr != members.end(); ++itr)
{
Player* GroupMember = ObjectAccessor::GetPlayer(*me, itr->guid);
if (!GroupMember)
continue;
if (!GroupMember->IsWithinDistInMap(me, EVENT_AREA_RADIUS) && GroupMember->GetQuestStatus(QUEST_BATTLE_OF_THE_CRIMSON_WATCH) == QUEST_STATUS_INCOMPLETE)
{
GroupMember->FailQuest(QUEST_BATTLE_OF_THE_CRIMSON_WATCH);
++FailedMemberCount;
}
++GroupMemberCount;
if (GroupMember->isDead())
++DeadMemberCount;
}
if (GroupMemberCount == FailedMemberCount)
{
Failed = true;
}
if (GroupMemberCount == DeadMemberCount)
{
for (Group::member_citerator itr = members.begin(); itr != members.end(); ++itr)
{
if (Player* groupMember = ObjectAccessor::GetPlayer(*me, itr->guid))
if (groupMember->GetQuestStatus(QUEST_BATTLE_OF_THE_CRIMSON_WATCH) == QUEST_STATUS_INCOMPLETE)
groupMember->FailQuest(QUEST_BATTLE_OF_THE_CRIMSON_WATCH);
}
Failed = true;
}
}
else if (player->isDead() || !player->IsWithinDistInMap(me, EVENT_AREA_RADIUS))
{
player->FailQuest(QUEST_BATTLE_OF_THE_CRIMSON_WATCH);
Failed = true;
}
}
void LiveCounter()
{
--LiveCount;
if (!LiveCount)
Announced = false;
}
void UpdateAI(uint32 diff) override
{
if (!PlayerGUID || !EventStarted)
return;
if (!LiveCount && WaveCount < 4)
{
if (!Announced && AnnounceTimer <= diff)
{
Announced = true;
}
else
AnnounceTimer -= diff;
if (WaveTimer <= diff)
{
SummonNextWave();
}
else
WaveTimer -= diff;
}
CheckEventFail();
if (Failed)
EnterEvadeMode();
}
};
};
/*######
# npc_illidari_spawn
######*/
class npc_illidari_spawn : public CreatureScript
{
public:
npc_illidari_spawn() : CreatureScript("npc_illidari_spawn") { }
CreatureAI* GetAI(Creature* c) const override
{
return new npc_illidari_spawnAI(c);
}
struct npc_illidari_spawnAI : public ScriptedAI
{
npc_illidari_spawnAI(Creature* creature) : ScriptedAI(creature) { }
ObjectGuid LordIllidanGUID;
uint32 SpellTimer1, SpellTimer2, SpellTimer3;
bool Timers;
void Reset() override
{
LordIllidanGUID.Clear();
Timers = false;
}
void JustEngagedWith(Unit* /*who*/) override { }
void JustDied(Unit* /*killer*/) override
{
me->RemoveCorpse();
if (Creature* LordIllidan = (ObjectAccessor::GetCreature(*me, LordIllidanGUID)))
if (LordIllidan)
CAST_AI(npc_lord_illidan_stormrage::npc_lord_illidan_stormrageAI, LordIllidan->AI())->LiveCounter();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (!Timers)
{
if (me->GetEntry() == 22075)//Illidari Soldier
{
SpellTimer1 = SpawnCast[0].Timer1 + (rand() % 4 * 1000);
}
if (me->GetEntry() == 22074)//Illidari Mind Breaker
{
SpellTimer1 = SpawnCast[1].Timer1 + (rand() % 10 * 1000);
SpellTimer2 = SpawnCast[2].Timer1 + (rand() % 4 * 1000);
SpellTimer3 = SpawnCast[3].Timer1 + (rand() % 4 * 1000);
}
if (me->GetEntry() == 19797)// Illidari Highlord
{
SpellTimer1 = SpawnCast[4].Timer1 + (rand() % 4 * 1000);
SpellTimer2 = SpawnCast[5].Timer1 + (rand() % 4 * 1000);
}
Timers = true;
}
//Illidari Soldier
if (me->GetEntry() == 22075)
{
if (SpellTimer1 <= diff)
{
DoCastVictim(SpawnCast[0].SpellId);//Spellbreaker
SpellTimer1 = SpawnCast[0].Timer2 + (rand() % 5 * 1000);
}
else SpellTimer1 -= diff;
}
//Illidari Mind Breaker
if (me->GetEntry() == 22074)
{
if (SpellTimer1 <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
if (target->IsPlayer())
{
DoCast(target, SpawnCast[1].SpellId); //Focused Bursts
SpellTimer1 = SpawnCast[1].Timer2 + (rand() % 5 * 1000);
}
else SpellTimer1 = 2000;
}
}
else SpellTimer1 -= diff;
if (SpellTimer2 <= diff)
{
DoCastVictim(SpawnCast[2].SpellId);//Psychic Scream
SpellTimer2 = SpawnCast[2].Timer2 + (rand() % 13 * 1000);
}
else SpellTimer2 -= diff;
if (SpellTimer3 <= diff)
{
DoCastVictim(SpawnCast[3].SpellId);//Mind Blast
SpellTimer3 = SpawnCast[3].Timer2 + (rand() % 8 * 1000);
}
else SpellTimer3 -= diff;
}
//Illidari Highlord
if (me->GetEntry() == 19797)
{
if (SpellTimer1 <= diff)
{
DoCastVictim(SpawnCast[4].SpellId);//Curse Of Flames
SpellTimer1 = SpawnCast[4].Timer2 + (rand() % 10 * 1000);
}
else SpellTimer1 -= diff;
if (SpellTimer2 <= diff)
{
DoCastVictim(SpawnCast[5].SpellId);//Flamestrike
SpellTimer2 = SpawnCast[5].Timer2 + (rand() % 7 * 13000);
}
else SpellTimer2 -= diff;
}
DoMeleeAttackIfReady();
}
};
};
void npc_lord_illidan_stormrage::npc_lord_illidan_stormrageAI::SummonNextWave()
{
uint8 count = WavesInfo[WaveCount].SpawnCount;
uint8 locIndex = WavesInfo[WaveCount].UsedSpawnPoint;
uint8 FelguardCount = 0;
uint8 DreadlordCount = 0;
for (uint8 i = 0; i < count; ++i)
{
Creature* Spawn = nullptr;
float X = SpawnLocation[locIndex + i].x;
float Y = SpawnLocation[locIndex + i].y;
float Z = SpawnLocation[locIndex + i].z;
float O = SpawnLocation[locIndex + i].o;
Spawn = me->SummonCreature(WavesInfo[WaveCount].CreatureId, X, Y, Z, O, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000);
++LiveCount;
if (Spawn)
{
if (WaveCount == 0)//1 Wave
{
if (rand() % 3 == 1 && FelguardCount < 2)
{
Spawn->SetDisplayId(18654);
++FelguardCount;
}
else if (DreadlordCount < 3)
{
Spawn->SetDisplayId(19991);
++DreadlordCount;
}
else if (FelguardCount < 2)
{
Spawn->SetDisplayId(18654);
++FelguardCount;
}
}
if (WaveCount < 3)//1-3 Wave
{
if (PlayerGUID)
{
if (Player* target = ObjectAccessor::GetPlayer(*me, PlayerGUID))
{
float x, y, z;
target->GetPosition(x, y, z);
Spawn->GetMotionMaster()->MovePoint(0, x, y, z);
}
}
CAST_AI(npc_illidari_spawn::npc_illidari_spawnAI, Spawn->AI())->LordIllidanGUID = me->GetGUID();
}
if (WavesInfo[WaveCount].CreatureId == 22076) // Torloth
{
CAST_AI(npc_torloth_the_magnificent::npc_torloth_the_magnificentAI, Spawn->AI())->LordIllidanGUID = me->GetGUID();
if (PlayerGUID)
CAST_AI(npc_torloth_the_magnificent::npc_torloth_the_magnificentAI, Spawn->AI())->AggroTargetGUID = PlayerGUID;
}
}
}
if (WaveCount < 3)
{
++WaveCount;
WaveTimer = WavesInfo[WaveCount].SpawnTimer;
AnnounceTimer = WavesInfo[WaveCount].YellTimer;
}
}
/*#####
# go_crystal_prison
######*/
class go_crystal_prison : public GameObjectScript
{
public:
go_crystal_prison() : GameObjectScript("go_crystal_prison") { }
bool OnQuestAccept(Player* player, GameObject* /*go*/, Quest const* quest) override
{
if (quest->GetQuestId() == QUEST_BATTLE_OF_THE_CRIMSON_WATCH)
{
Creature* Illidan = player->FindNearestCreature(22083, 50);
if (Illidan && !CAST_AI(npc_lord_illidan_stormrage::npc_lord_illidan_stormrageAI, Illidan->AI())->EventStarted)
{
CAST_AI(npc_lord_illidan_stormrage::npc_lord_illidan_stormrageAI, Illidan->AI())->PlayerGUID = player->GetGUID();
CAST_AI(npc_lord_illidan_stormrage::npc_lord_illidan_stormrageAI, Illidan->AI())->LiveCount = 0;
CAST_AI(npc_lord_illidan_stormrage::npc_lord_illidan_stormrageAI, Illidan->AI())->EventStarted = true;
}
}
return true;
}
};
/*####
# npc_enraged_spirits
####*/
enum Enraged_Dpirits
{
// QUESTS
QUEST_ENRAGED_SPIRITS_FIRE_EARTH = 10458,
QUEST_ENRAGED_SPIRITS_AIR = 10481,
QUEST_ENRAGED_SPIRITS_WATER = 10480,
// Totem
ENTRY_TOTEM_OF_SPIRITS = 21071,
RADIUS_TOTEM_OF_SPIRITS = 15,
// SPIRITS
NPC_ENRAGED_EARTH_SPIRIT = 21050,
NPC_ENRAGED_FIRE_SPIRIT = 21061,
NPC_ENRAGED_AIR_SPIRIT = 21060,
NPC_ENRAGED_WATER_SPIRIT = 21059,
// ENRAGED WATER SPIRIT SPELLS
SPELL_STORMBOLT = 38032,
// ENRAGED AIR SPIRIT SPELLS
SPELL_AIR_SPIRIT_CHAIN_LIGHTNING = 12058,
SPELL_HURRICANE = 32717,
SPELL_ENRAGE = 8599,
// ENRAGED FIRE SPIRIT SPELLS - Will be using the enrage spell from Air Spirit
SPELL_FEL_FIREBALL = 36247,
SPELL_FEL_FIRE_AURA = 36006, // Earth spirit uses this one
// ENRAGED EARTH SPIRIT SPELLS
SPELL_FIERY_BOULDER = 38498,
SPELL_SUMMON_ENRAGED_EARTH_SHARD = 38365,
// SOULS
NPC_EARTHEN_SOUL = 21073,
NPC_FIERY_SOUL = 21097,
NPC_ENRAGED_AIRY_SOUL = 21116,
NPC_ENRAGED_WATERY_SOUL = 21109, // wrong model
// SPELL KILLCREDIT - not working!?! - using KilledMonsterCredit
SPELL_EARTHEN_SOUL_CAPTURED_CREDIT = 36108,
SPELL_FIERY_SOUL_CAPTURED_CREDIT = 36117,
SPELL_AIRY_SOUL_CAPTURED_CREDIT = 36182,
SPELL_WATERY_SOUL_CAPTURED_CREDIT = 36171,
// KilledMonsterCredit Workaround
NPC_CREDIT_FIRE = 21094,
NPC_CREDIT_WATER = 21095,
NPC_CREDIT_AIR = 21096,
NPC_CREDIT_EARTH = 21092,
// Captured Spell / Buff
SPELL_SOUL_CAPTURED = 36115
};
class npc_enraged_spirit : public CreatureScript
{
public:
npc_enraged_spirit() : CreatureScript("npc_enraged_spirit") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_enraged_spiritAI(creature);
}
struct npc_enraged_spiritAI : public ScriptedAI
{
npc_enraged_spiritAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override { }
void JustEngagedWith(Unit* /*who*/) override
{
switch (me->GetEntry())
{
case NPC_ENRAGED_WATER_SPIRIT:
_scheduler.Schedule(0s, 1s, [this](TaskContext context)
{
if (UpdateVictim())
{
DoCastVictim(SPELL_STORMBOLT);
}
context.Repeat(17s, 23s);
});
break;
case NPC_ENRAGED_FIRE_SPIRIT:
if (!me->GetAura(SPELL_FEL_FIRE_AURA))
{
DoCastSelf(SPELL_FEL_FIRE_AURA);
}
_scheduler.Schedule(2s, 10s, [this](TaskContext context)
{
if (UpdateVictim())
{
DoCastVictim(SPELL_FEL_FIREBALL);
}
context.Repeat(6s, 12s);
});
break;
case NPC_ENRAGED_EARTH_SPIRIT:
if (!me->GetAura(SPELL_FEL_FIRE_AURA))
{
DoCastSelf(SPELL_FEL_FIRE_AURA);
}
_scheduler.Schedule(3s, 4s, [this](TaskContext context)
{
if (UpdateVictim())
{
DoCastVictim(SPELL_FIERY_BOULDER);
}
context.Repeat(6s, 9s);
});
break;
case NPC_ENRAGED_AIR_SPIRIT:
_scheduler.Schedule(10s, [this](TaskContext context)
{
if (UpdateVictim())
{
DoCastVictim(SPELL_AIR_SPIRIT_CHAIN_LIGHTNING);
}
_scheduler.Schedule(3s, 5s, [this](TaskContext /*context*/)
{
if (UpdateVictim())
{
DoCastVictim(SPELL_HURRICANE);
}
});
context.Repeat(12s, 15s);
});
break;
default:
break;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (me->GetEntry() == NPC_ENRAGED_FIRE_SPIRIT || me->GetEntry() == NPC_ENRAGED_AIR_SPIRIT)
{
if (HealthBelowPct(35) && !me->GetAura(SPELL_ENRAGE))
{
DoCastSelf(SPELL_ENRAGE);
}
}
DoMeleeAttackIfReady();
}
void JustDied(Unit* /*killer*/) override
{
// always spawn spirit on death
// if totem around
// move spirit to totem and cast kill count
uint32 entry = 0;
uint32 credit = 0;
switch (me->GetEntry())
{
case NPC_ENRAGED_FIRE_SPIRIT:
entry = NPC_FIERY_SOUL;
//credit = SPELL_FIERY_SOUL_CAPTURED_CREDIT;
credit = NPC_CREDIT_FIRE;
break;
case NPC_ENRAGED_EARTH_SPIRIT:
entry = NPC_EARTHEN_SOUL;
//credit = SPELL_EARTHEN_SOUL_CAPTURED_CREDIT;
credit = NPC_CREDIT_EARTH;
DoCastSelf(SPELL_SUMMON_ENRAGED_EARTH_SHARD);
break;
case NPC_ENRAGED_AIR_SPIRIT:
entry = NPC_ENRAGED_AIRY_SOUL;
//credit = SPELL_AIRY_SOUL_CAPTURED_CREDIT;
credit = NPC_CREDIT_AIR;
break;
case NPC_ENRAGED_WATER_SPIRIT:
entry = NPC_ENRAGED_WATERY_SOUL;
//credit = SPELL_WATERY_SOUL_CAPTURED_CREDIT;
credit = NPC_CREDIT_WATER;
break;
default:
break;
}
// Spawn Soul on Kill ALWAYS!
Creature* Summoned = nullptr;
Unit* totemOspirits = nullptr;
if (entry != 0)
Summoned = DoSpawnCreature(entry, 0, 0, 1, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 5000);
// FIND TOTEM, PROCESS QUEST
if (Summoned)
{
totemOspirits = me->FindNearestCreature(ENTRY_TOTEM_OF_SPIRITS, RADIUS_TOTEM_OF_SPIRITS);
if (totemOspirits)
{
Summoned->SetFaction(FACTION_FRIENDLY);
Summoned->GetMotionMaster()->MovePoint(0, totemOspirits->GetPositionX(), totemOspirits->GetPositionY(), Summoned->GetPositionZ());
if (Unit* owner = totemOspirits->GetOwner())
if (Player* player = owner->ToPlayer())
player->KilledMonsterCredit(credit);
DoCast(totemOspirits, SPELL_SOUL_CAPTURED);
}
}
}
private:
TaskScheduler _scheduler;
};
};
enum ShadowMoonTuberEnum
{
SPELL_WHISTLE = 36652,
SPELL_SHADOWMOON_TUBER = 36462,
NPC_BOAR_ENTRY = 21195,
GO_SHADOWMOON_TUBER_MOUND = 184701,
POINT_TUBER = 1,
TYPE_BOAR = 1,
DATA_BOAR = 1
};
class npc_shadowmoon_tuber_node : public CreatureScript
{
public:
npc_shadowmoon_tuber_node() : CreatureScript("npc_shadowmoon_tuber_node") { }
struct npc_shadowmoon_tuber_nodeAI : public ScriptedAI
{
npc_shadowmoon_tuber_nodeAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
tapped = false;
tuberGUID.Clear();
resetTimer = 60000;
}
void SetData(uint32 id, uint32 data) override
{
if (id == TYPE_BOAR && data == DATA_BOAR)
{
// Spawn chest GO
DoCast(SPELL_SHADOWMOON_TUBER);
// Despawn the tuber
if (GameObject* tuber = me->FindNearestGameObject(GO_SHADOWMOON_TUBER_MOUND, 5.0f))
{
tuberGUID = tuber->GetGUID();
// @Workaround: find how to properly despawn the GO
tuber->SetPhaseMask(2, true);
}
}
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (!tapped && spell->Id == SPELL_WHISTLE)
{
if (Creature* boar = me->FindNearestCreature(NPC_BOAR_ENTRY, 30.0f))
{
// Disable trigger and force nearest boar to walk to him
tapped = true;
boar->SetWalk(false);
boar->GetMotionMaster()->MovePoint(POINT_TUBER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
}
}
}
void UpdateAI(uint32 diff) override
{
if (tapped)
{
if (resetTimer <= diff)
{
// Respawn the tuber
if (tuberGUID)
if (GameObject* tuber = ObjectAccessor::GetGameObject(*me, tuberGUID))
// @Workaround: find how to properly respawn the GO
tuber->SetPhaseMask(1, true);
Reset();
}
else
resetTimer -= diff;
}
}
private:
bool tapped;
ObjectGuid tuberGUID;
uint32 resetTimer;
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_shadowmoon_tuber_nodeAI(creature);
}
};
enum KorWild
{
SAY_LAND = 0,
POINT_LAND = 1
};
class npc_korkron_or_wildhammer : public ScriptedAI
{
public:
npc_korkron_or_wildhammer(Creature* creature) : ScriptedAI(creature)
{
creature->SetDisableGravity(true);
creature->SetHover(true);
}
void Reset() override
{
me->SetReactState(REACT_PASSIVE);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
}
void JustDied(Unit* /*killer*/) override
{
me->DespawnOrUnsummon(3s, 0s);
}
void IsSummonedBy(WorldObject* summoner) override
{
_playerGUID = summoner->GetGUID();
me->SetFacingToObject(summoner);
Position pos = summoner->GetPosition();
me->GetMotionMaster()->MovePoint(POINT_LAND, pos);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE && id == POINT_LAND)
{
if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
Talk(SAY_LAND, player);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
}
private:
ObjectGuid _playerGUID;
};
class spell_calling_korkron_or_wildhammer : public SpellScript
{
PrepareSpellScript(spell_calling_korkron_or_wildhammer);
void SetDest(SpellDestination& dest)
{
// Adjust effect summon position
Position const offset = { -14.0f, -14.0f, 16.0f, 0.0f };
dest.RelocateOffset(offset);
}
void Register() override
{
OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_calling_korkron_or_wildhammer::SetDest, EFFECT_0, TARGET_DEST_CASTER);
}
};
enum InfernalOversoul
{
NPC_INFERNAL_OVERSOUL = 21735,
SPELL_DISRUPT_SUMMONING_RITUAL = 37285
};
class spell_disrupt_summoning_ritual : public SpellScript
{
public:
PrepareSpellScript(spell_disrupt_summoning_ritual);
SpellCastResult CheckRequirement()
{
if (Unit* caster = GetCaster())
if (Creature* infernal = caster->FindNearestCreature(NPC_INFERNAL_OVERSOUL, 100.0f))
if (!infernal->HasAura(SPELL_DISRUPT_SUMMONING_RITUAL))
return SPELL_FAILED_CASTER_AURASTATE;
return SPELL_CAST_OK;
}
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_disrupt_summoning_ritual::CheckRequirement);
}
};
/*
######
# Dragonmaw Races
######
*/
enum DragonmawRaces
{
QUEST_MUCKJAW = 11064,
QUEST_TROPE = 11067,
QUEST_CORLOK = 11068,
QUEST_ICHMAN = 11069,
QUEST_MULVERICK = 11070,
QUEST_SKYSHATTER = 11071,
NPC_MUCKJAW = 23340,
NPC_TROPE = 23342,
NPC_CORLOK = 23344,
NPC_ICHMAN = 23345,
NPC_MULVERICK = 23346,
NPC_SKYSHATTER = 23348,
PATH_MUCKJAW = 233401,
PATH_TROPE = 233421,
PATH_CORLOK = 233441,
PATH_ICHMAN = 233451,
PATH_MULVERICK = 233461,
PATH_SKYSHATTER = 233481,
NPC_TARGET_MUCKJAW = 23356,
NPC_TARGET_TROPE = 23357,
NPC_TARGET_CORLOK = 23358,
NPC_TARGET_ICHMAN = 23359,
NPC_TARGET_MULVERICK = 23360,
NPC_TARGET_SKYSHATTER = 23361,
SAY_START = 0,
SAY_COMPLETE = 1,
SAY_SKYSHATTER_SPECIAL = 2,
};
struct dragonmaw_race_npc : public ScriptedAI
{
dragonmaw_race_npc(Creature* creature) : ScriptedAI(creature)
{
}
void Reset() override
{
scheduler.CancelAll();
me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
me->SetWalk(true);
me->SetDisableGravity(false);
me->GetMotionMaster()->MoveIdle();
_playerGUID.Clear();
}
void sQuestAccept(Player* player, Quest const* /*quest*/) override
{
me->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
if (player)
{
_playerGUID = player->GetGUID();
Talk(SAY_START, player);
}
switch (me->GetEntry())
{
case NPC_MUCKJAW:
me->GetMotionMaster()->MovePath(PATH_MUCKJAW, false);
break;
case NPC_TROPE:
me->GetMotionMaster()->MovePath(PATH_TROPE, false);
break;
case NPC_CORLOK:
me->GetMotionMaster()->MovePath(PATH_CORLOK, false);
break;
case NPC_ICHMAN:
me->GetMotionMaster()->MovePath(PATH_ICHMAN, false);
break;
case NPC_MULVERICK:
me->GetMotionMaster()->MovePath(PATH_MULVERICK, false);
break;
case NPC_SKYSHATTER:
me->GetMotionMaster()->MovePath(PATH_SKYSHATTER, false);
break;
default:
break;
}
}
void TakeOff()
{
me->SetDisableGravity(true);
}
void StartRace()
{
me->SetWalk(false);
ScheduleTimedEvent(5s, [&]
{
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player || !me->IsWithinDist(player, 100.f))
FailQuest(player);
}, 5s);
}
void FailQuest(Player *player)
{
if (player)
{
switch (me->GetEntry())
{
case NPC_MUCKJAW:
player->FailQuest(QUEST_MUCKJAW);
break;
case NPC_TROPE:
player->FailQuest(QUEST_TROPE);
break;
case NPC_CORLOK:
player->FailQuest(QUEST_CORLOK);
break;
case NPC_ICHMAN:
player->FailQuest(QUEST_ICHMAN);
break;
case NPC_MULVERICK:
player->FailQuest(QUEST_MULVERICK);
break;
case NPC_SKYSHATTER:
player->FailQuest(QUEST_SKYSHATTER);
break;
default:
break;
}
}
scheduler.CancelAll();
me->DespawnOnEvade();
}
void StartRaceAttacks()
{
/*
* Timers are placeholders
* After spawned, the rest is done via SmartAI
*/
if (_playerGUID.IsEmpty())
return;
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
switch (me->GetEntry())
{
case NPC_MUCKJAW:
ScheduleTimedEvent(4s, [&]
{
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
Position summonPos;
summonPos = me->GetRandomPoint(player->GetPosition(), 15.f);
summonPos.m_positionZ = player->GetPositionZ(); // So they don't spawn at ground height
me->SummonCreature(NPC_TARGET_MUCKJAW, summonPos, TEMPSUMMON_TIMED_DESPAWN, 10000);
}, 4s, 8s);
break;
case NPC_TROPE:
ScheduleTimedEvent(4s, [&]
{
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
Position summonPos;
summonPos = me->GetRandomPoint(player->GetPosition(), 10.f);
summonPos.m_positionZ = player->GetPositionZ();
me->SummonCreature(NPC_TARGET_TROPE, summonPos, TEMPSUMMON_TIMED_DESPAWN, 10000);
}, 1s, 3s);
break;
case NPC_CORLOK:
ScheduleTimedEvent(4s, [&]
{
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
Position summonPos;
summonPos = me->GetRandomPoint(player->GetPosition(), 10.f);
summonPos.m_positionZ = player->GetPositionZ();
me->SummonCreature(NPC_TARGET_CORLOK, summonPos, TEMPSUMMON_TIMED_DESPAWN, 10000);
}, 1s, 3s);
break;
case NPC_ICHMAN:
ScheduleTimedEvent(4s, [&]
{
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
Position summonPos;
summonPos = me->GetRandomPoint(player->GetPosition(), 10.f);
summonPos.m_positionZ = player->GetPositionZ();
me->SummonCreature(NPC_TARGET_ICHMAN, summonPos, TEMPSUMMON_TIMED_DESPAWN, 10000);
}, 1s, 3s);
break;
case NPC_MULVERICK:
ScheduleTimedEvent(4s, [&]
{
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
Position summonPos;
summonPos = me->GetRandomPoint(player->GetPosition(), 10.f);
summonPos.m_positionZ = player->GetPositionZ();
me->SummonCreature(NPC_TARGET_MULVERICK, summonPos, TEMPSUMMON_TIMED_DESPAWN, 10000);
}, 1s, 3s);
break;
case NPC_SKYSHATTER:
ScheduleTimedEvent(4s, [&]
{
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
Position summonPos;
summonPos = me->GetRandomPoint(player->GetPosition(), 7.f);
summonPos.m_positionZ = player->GetPositionZ(); // So they don't spawn at ground height
me->SummonCreature(NPC_TARGET_SKYSHATTER, summonPos, TEMPSUMMON_TIMED_DESPAWN, 10000);
}, 1s, 3s);
break;
default:
break;
}
}
void FinishRace()
{
scheduler.CancelAll();
me->SetHover(false);
me->SetDisableGravity(false);
me->SetWalk(true);
Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID);
if (!player)
return;
Talk(SAY_COMPLETE, player);
switch (me->GetEntry())
{
case NPC_MUCKJAW:
player->AreaExploredOrEventHappens(QUEST_MUCKJAW);
break;
case NPC_TROPE:
player->AreaExploredOrEventHappens(QUEST_TROPE);
break;
case NPC_CORLOK:
player->AreaExploredOrEventHappens(QUEST_CORLOK);
break;
case NPC_ICHMAN:
player->AreaExploredOrEventHappens(QUEST_ICHMAN);
break;
case NPC_MULVERICK:
player->AreaExploredOrEventHappens(QUEST_MULVERICK);
break;
case NPC_SKYSHATTER:
player->AreaExploredOrEventHappens(QUEST_SKYSHATTER);
break;
default:
break;
}
}
void MovementInform(uint32 /*type*/, uint32 id) override
{
switch (me->GetEntry())
{
case NPC_MUCKJAW:
switch (id)
{
case 4:
TakeOff();
break;
case 7:
StartRace();
break;
case 9:
StartRaceAttacks();
break;
case 35:
FinishRace();
break;
case 37:
Reset();
break;
}
break;
case NPC_TROPE:
switch (id)
{
case 5:
TakeOff();
break;
case 7:
StartRace();
break;
case 10:
StartRaceAttacks();
break;
case 53:
FinishRace();
break;
case 60:
Reset();
break;
}
break;
case NPC_CORLOK:
switch (id)
{
case 6:
TakeOff();
break;
case 9:
StartRace();
break;
case 12:
StartRaceAttacks();
break;
case 79:
FinishRace();
break;
case 89:
Reset();
break;
}
break;
case NPC_ICHMAN:
switch (id)
{
case 4:
TakeOff();
StartRace();
break;
case 12:
StartRaceAttacks();
break;
case 107:
FinishRace();
break;
case 111:
Reset();
break;
}
break;
case NPC_MULVERICK:
switch (id)
{
case 5:
TakeOff();
break;
case 9:
StartRace();
break;
case 12:
StartRaceAttacks();
break;
case 166:
FinishRace();
break;
case 172:
Reset();
break;
}
break;
case NPC_SKYSHATTER:
switch (id)
{
case 3:
TakeOff();
break;
case 7:
StartRace();
if (Player *player = ObjectAccessor::GetPlayer(me->GetMap(), _playerGUID))
Talk(SAY_SKYSHATTER_SPECIAL, player);
break;
case 10:
StartRaceAttacks();
break;
case 140:
FinishRace();
break;
case 145:
Reset();
break;
}
break;
default:
break;
}
}
void PathEndReached(uint32 /*pathId*/) override
{
Reset();
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
}
private:
ObjectGuid _playerGUID;
};
void AddSC_shadowmoon_valley()
{
RegisterSpellScript(spell_q10612_10613_the_fel_and_the_furious);
RegisterSpellScript(spell_q10563_q10596_to_legion_hold_aura);
RegisterCreatureAI(dragonmaw_race_npc);
new npc_invis_infernal_caster();
new npc_infernal_attacker();
new npc_mature_netherwing_drake();
RegisterCreatureAI(npc_enslaved_netherwing_drake);
new npc_dragonmaw_peon();
new npc_drake_dealer_hurlunk();
new npcs_flanis_swiftwing_and_kagrosh();
new npc_karynaku();
new npc_lord_illidan_stormrage();
new go_crystal_prison();
new npc_illidari_spawn();
new npc_torloth_the_magnificent();
new npc_enraged_spirit();
new npc_shadowmoon_tuber_node();
RegisterCreatureAI(npc_korkron_or_wildhammer);
RegisterSpellScript(spell_calling_korkron_or_wildhammer);
RegisterSpellScript(spell_disrupt_summoning_ritual);
}