1755 lines
54 KiB
C++
1755 lines
54 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*/
|
|
|
|
/* ScriptData
|
|
SDName: Shadowmoon_Valley
|
|
SD%Complete: 100
|
|
SDComment: Quest support: 10519, 10583, 10601, 10804, 10854, 10458, 10481, 10480, 10781, 10451. Vendor Drake Dealer Hurlunk.
|
|
SDCategory: Shadowmoon Valley
|
|
EndScriptData */
|
|
|
|
/* ContentData
|
|
npc_mature_netherwing_drake
|
|
npc_enslaved_netherwing_drake
|
|
npc_drake_dealer_hurlunk
|
|
npcs_flanis_swiftwing_and_kagrosh
|
|
npc_karynaku
|
|
npc_oronok_tornheart
|
|
npc_earthmender_wilda
|
|
npc_torloth_the_magnificent
|
|
npc_illidari_spawn
|
|
npc_lord_illidan_stormrage
|
|
go_crystal_prison
|
|
npc_enraged_spirit
|
|
EndContentData */
|
|
|
|
#include "ScriptMgr.h"
|
|
#include "ScriptedCreature.h"
|
|
#include "ScriptedGossip.h"
|
|
#include "ScriptedEscortAI.h"
|
|
#include "Group.h"
|
|
#include "SpellScript.h"
|
|
#include "Player.h"
|
|
#include "WorldSession.h"
|
|
|
|
// Ours
|
|
class spell_q10612_10613_the_fel_and_the_furious : public SpellScriptLoader
|
|
{
|
|
public:
|
|
spell_q10612_10613_the_fel_and_the_furious() : SpellScriptLoader("spell_q10612_10613_the_fel_and_the_furious") { }
|
|
|
|
class spell_q10612_10613_the_fel_and_the_furious_SpellScript : public SpellScript
|
|
{
|
|
PrepareSpellScript(spell_q10612_10613_the_fel_and_the_furious_SpellScript);
|
|
|
|
void HandleScriptEffect(SpellEffIndex effIndex)
|
|
{
|
|
Player* charmer = GetCaster()->GetCharmerOrOwnerPlayerOrPlayerItself();
|
|
if (!charmer)
|
|
return;
|
|
|
|
std::list<GameObject*> gList;
|
|
GetCaster()->GetGameObjectListWithEntryInGrid(gList, 184979, 30.0f);
|
|
uint8 counter = 0;
|
|
for (std::list<GameObject*>::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(14);
|
|
cr2->SetUInt32Value(UNIT_FIELD_FLAGS, 0);
|
|
GetCaster()->CastSpell(cr2, 38083, true);
|
|
}
|
|
|
|
go->SetLootState(GO_JUST_DEACTIVATED);
|
|
charmer->KilledMonsterCredit(21959, 0);
|
|
}
|
|
}
|
|
|
|
void Register()
|
|
{
|
|
OnEffectHitTarget += SpellEffectFn(spell_q10612_10613_the_fel_and_the_furious_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
|
}
|
|
};
|
|
|
|
SpellScript* GetSpellScript() const
|
|
{
|
|
return new spell_q10612_10613_the_fel_and_the_furious_SpellScript();
|
|
}
|
|
};
|
|
|
|
class spell_q10563_q10596_to_legion_hold : public SpellScriptLoader
|
|
{
|
|
public:
|
|
spell_q10563_q10596_to_legion_hold() : SpellScriptLoader("spell_q10563_q10596_to_legion_hold") { }
|
|
|
|
class spell_q10563_q10596_to_legion_hold_AuraScript : public AuraScript
|
|
{
|
|
PrepareAuraScript(spell_q10563_q10596_to_legion_hold_AuraScript)
|
|
|
|
void HandleEffectRemove(AuraEffect const * /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
|
{
|
|
if (Player* player = GetTarget()->ToPlayer())
|
|
{
|
|
player->KilledMonsterCredit(21502, 0);
|
|
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()
|
|
{
|
|
OnEffectApply += AuraEffectApplyFn(spell_q10563_q10596_to_legion_hold_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
|
|
OnEffectRemove += AuraEffectRemoveFn(spell_q10563_q10596_to_legion_hold_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
|
|
}
|
|
};
|
|
|
|
AuraScript* GetAuraScript() const
|
|
{
|
|
return new spell_q10563_q10596_to_legion_hold_AuraScript();
|
|
}
|
|
};
|
|
|
|
|
|
// Theirs
|
|
/*#####
|
|
# 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()
|
|
{
|
|
ground = me->GetMap()->GetHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
|
|
SummonInfernal();
|
|
events.ScheduleEvent(EVENT_CAST_SUMMON_INFERNAL, urand(1000, 3000));
|
|
}
|
|
|
|
void SetData(uint32 id, uint32 data)
|
|
{
|
|
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_TIMED_OR_DEAD_DESPAWN, 60000);
|
|
infernalGUID = infernal->GetGUID();
|
|
}
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
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;
|
|
uint64 infernalGUID;
|
|
float ground;
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
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()
|
|
{
|
|
me->SetDisplayId(MODEL_INVISIBLE);
|
|
me->GetMotionMaster()->MoveRandom(5.0f);
|
|
}
|
|
|
|
void IsSummonedBy(Unit* summoner)
|
|
{
|
|
if (!summoner)
|
|
return;
|
|
|
|
if (summoner->ToCreature())
|
|
casterGUID = summoner->ToCreature()->GetGUID();;
|
|
}
|
|
|
|
void JustDied(Unit* /*killer*/)
|
|
{
|
|
if (Creature* caster = ObjectAccessor::GetCreature(*me, casterGUID))
|
|
caster->AI()->SetData(TYPE_INFERNAL, DATA_DIED);
|
|
}
|
|
|
|
void SpellHit(Unit* /*caster*/, const SpellInfo* spell)
|
|
{
|
|
if (spell->Id == SPELL_SUMMON_INFERNAL)
|
|
{
|
|
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE);
|
|
me->SetDisplayId(MODEL_INFERNAL);
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
private:
|
|
uint64 casterGUID;
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
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
|
|
{
|
|
return new npc_mature_netherwing_drakeAI(creature);
|
|
}
|
|
|
|
struct npc_mature_netherwing_drakeAI : public ScriptedAI
|
|
{
|
|
npc_mature_netherwing_drakeAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
uint64 uiPlayerGUID;
|
|
|
|
bool bCanEat;
|
|
bool bIsEating;
|
|
|
|
uint32 EatTimer;
|
|
uint32 CastTimer;
|
|
|
|
void Reset()
|
|
{
|
|
uiPlayerGUID = 0;
|
|
|
|
bCanEat = false;
|
|
bIsEating = false;
|
|
|
|
EatTimer = 5000;
|
|
CastTimer = 5000;
|
|
}
|
|
|
|
void SpellHit(Unit* pCaster, SpellInfo const* spell)
|
|
{
|
|
if (bCanEat || bIsEating)
|
|
return;
|
|
|
|
if (pCaster->GetTypeId() == TYPEID_PLAYER && spell->Id == SPELL_PLACE_CARCASS && !me->HasAura(SPELL_JUST_EATEN))
|
|
{
|
|
uiPlayerGUID = pCaster->GetGUID();
|
|
bCanEat = true;
|
|
}
|
|
}
|
|
|
|
void MovementInform(uint32 type, uint32 id)
|
|
{
|
|
if (type != POINT_MOTION_TYPE)
|
|
return;
|
|
|
|
if (id == POINT_ID)
|
|
{
|
|
bIsEating = true;
|
|
EatTimer = 7000;
|
|
me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED);
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
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, 0);
|
|
|
|
if (GameObject* go = player->FindNearestGameObject(GO_CARCASS, 10))
|
|
go->Delete();
|
|
}
|
|
|
|
Reset();
|
|
me->GetMotionMaster()->Clear();
|
|
}
|
|
}
|
|
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
|
|
{
|
|
// Factions
|
|
FACTION_DEFAULT = 62,
|
|
FACTION_FRIENDLY = 1840, // Not sure if this is correct, it was taken off of Mordenai.
|
|
|
|
// Spells
|
|
SPELL_HIT_FORCE_OF_NELTHARAKU = 38762,
|
|
SPELL_FORCE_OF_NELTHARAKU = 38775,
|
|
|
|
// Creatures
|
|
NPC_DRAGONMAW_SUBJUGATOR = 21718,
|
|
NPC_ESCAPE_DUMMY = 22317
|
|
|
|
};
|
|
|
|
class npc_enslaved_netherwing_drake : public CreatureScript
|
|
{
|
|
public:
|
|
npc_enslaved_netherwing_drake() : CreatureScript("npc_enslaved_netherwing_drake") { }
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
return new npc_enslaved_netherwing_drakeAI(creature);
|
|
}
|
|
|
|
struct npc_enslaved_netherwing_drakeAI : public ScriptedAI
|
|
{
|
|
npc_enslaved_netherwing_drakeAI(Creature* creature) : ScriptedAI(creature)
|
|
{
|
|
PlayerGUID = 0;
|
|
Tapped = false;
|
|
Reset();
|
|
}
|
|
|
|
uint64 PlayerGUID;
|
|
uint32 FlyTimer;
|
|
bool Tapped;
|
|
|
|
void Reset()
|
|
{
|
|
if (!Tapped)
|
|
me->setFaction(FACTION_DEFAULT);
|
|
|
|
FlyTimer = 10000;
|
|
me->SetDisableGravity(false);
|
|
me->SetVisible(true);
|
|
}
|
|
|
|
void SpellHit(Unit* caster, const SpellInfo* spell)
|
|
{
|
|
if (!caster)
|
|
return;
|
|
|
|
if (caster->GetTypeId() == TYPEID_PLAYER && spell->Id == SPELL_HIT_FORCE_OF_NELTHARAKU && !Tapped)
|
|
{
|
|
Tapped = true;
|
|
PlayerGUID = caster->GetGUID();
|
|
|
|
me->setFaction(FACTION_FRIENDLY);
|
|
DoCast(caster, SPELL_FORCE_OF_NELTHARAKU, true);
|
|
|
|
Unit* Dragonmaw = me->FindNearestCreature(NPC_DRAGONMAW_SUBJUGATOR, 50);
|
|
if (Dragonmaw)
|
|
{
|
|
me->AddThreat(Dragonmaw, 100000.0f);
|
|
AttackStart(Dragonmaw);
|
|
}
|
|
|
|
HostileReference* ref = me->getThreatManager().getOnlineContainer().getReferenceByTarget(caster);
|
|
if (ref)
|
|
ref->removeReference();
|
|
}
|
|
}
|
|
|
|
void MovementInform(uint32 type, uint32 id)
|
|
{
|
|
if (type != POINT_MOTION_TYPE)
|
|
return;
|
|
|
|
if (id == 1)
|
|
{
|
|
if (PlayerGUID)
|
|
{
|
|
Unit* player = ObjectAccessor::GetUnit(*me, PlayerGUID);
|
|
if (player)
|
|
DoCast(player, SPELL_FORCE_OF_NELTHARAKU, true);
|
|
|
|
PlayerGUID = 0;
|
|
}
|
|
me->SetVisible(false);
|
|
me->SetDisableGravity(false);
|
|
Unit::DealDamage(me, me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
|
|
me->RemoveCorpse();
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
{
|
|
if (Tapped)
|
|
{
|
|
if (FlyTimer <= diff)
|
|
{
|
|
Tapped = false;
|
|
if (PlayerGUID)
|
|
{
|
|
Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID);
|
|
if (player && player->GetQuestStatus(10854) == QUEST_STATUS_INCOMPLETE)
|
|
{
|
|
DoCast(player, SPELL_FORCE_OF_NELTHARAKU, true);
|
|
/*
|
|
float x, y, z;
|
|
me->GetPosition(x, y, z);
|
|
|
|
float dx, dy, dz;
|
|
me->GetRandomPoint(x, y, z, 20, dx, dy, dz);
|
|
dz += 20; // so it's in the air, not ground*/
|
|
|
|
Position pos;
|
|
if (Unit* EscapeDummy = me->FindNearestCreature(NPC_ESCAPE_DUMMY, 30))
|
|
EscapeDummy->GetPosition(&pos);
|
|
else
|
|
{
|
|
me->GetRandomNearPosition(pos, 20);
|
|
pos.m_positionZ += 25;
|
|
}
|
|
|
|
me->SetDisableGravity(true);
|
|
me->GetMotionMaster()->MovePoint(1, pos);
|
|
}
|
|
}
|
|
} else FlyTimer -= diff;
|
|
}
|
|
return;
|
|
}
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
};
|
|
};
|
|
|
|
/*#####
|
|
# npc_dragonmaw_peon
|
|
#####*/
|
|
|
|
class npc_dragonmaw_peon : public CreatureScript
|
|
{
|
|
public:
|
|
npc_dragonmaw_peon() : CreatureScript("npc_dragonmaw_peon") { }
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
return new npc_dragonmaw_peonAI(creature);
|
|
}
|
|
|
|
struct npc_dragonmaw_peonAI : public ScriptedAI
|
|
{
|
|
npc_dragonmaw_peonAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
uint64 PlayerGUID;
|
|
bool Tapped;
|
|
uint32 PoisonTimer;
|
|
|
|
void Reset()
|
|
{
|
|
PlayerGUID = 0;
|
|
Tapped = false;
|
|
PoisonTimer = 0;
|
|
}
|
|
|
|
void SpellHit(Unit* caster, const SpellInfo* spell)
|
|
{
|
|
if (!caster)
|
|
return;
|
|
|
|
if (caster->GetTypeId() == TYPEID_PLAYER && spell->Id == 40468 && !Tapped)
|
|
{
|
|
PlayerGUID = caster->GetGUID();
|
|
|
|
Tapped = true;
|
|
float x, y, z;
|
|
caster->GetClosePoint(x, y, z, me->GetObjectSize());
|
|
|
|
me->SetWalk(false);
|
|
me->GetMotionMaster()->MovePoint(1, x, y, z);
|
|
}
|
|
}
|
|
|
|
void MovementInform(uint32 type, uint32 id)
|
|
{
|
|
if (type != POINT_MOTION_TYPE)
|
|
return;
|
|
|
|
if (id)
|
|
{
|
|
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_EAT);
|
|
PoisonTimer = 15000;
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
if (PoisonTimer)
|
|
{
|
|
if (PoisonTimer <= diff)
|
|
{
|
|
if (PlayerGUID)
|
|
{
|
|
Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID);
|
|
if (player && player->GetQuestStatus(11020) == QUEST_STATUS_INCOMPLETE)
|
|
player->KilledMonsterCredit(23209, 0);
|
|
}
|
|
PoisonTimer = 0;
|
|
Unit::DealDamage(me, me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
|
|
} else PoisonTimer -= diff;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
/*######
|
|
## 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)
|
|
{
|
|
player->PlayerTalkClass->ClearMenus();
|
|
if (action == GOSSIP_ACTION_TRADE)
|
|
player->GetSession()->SendListInventory(creature->GetGUID());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OnGossipHello(Player* player, Creature* creature)
|
|
{
|
|
if (creature->IsVendor() && player->GetReputationRank(1015) == REP_EXALTED)
|
|
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
|
|
|
|
player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/*######
|
|
## npc_flanis_swiftwing_and_kagrosh
|
|
######*/
|
|
|
|
#define GOSSIP_HSK1 "Take Flanis's Pack"
|
|
#define GOSSIP_HSK2 "Take Kagrosh's Pack"
|
|
|
|
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)
|
|
{
|
|
player->PlayerTalkClass->ClearMenus();
|
|
if (action == GOSSIP_ACTION_INFO_DEF+1)
|
|
{
|
|
ItemPosCountVec dest;
|
|
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 30658, 1, NULL);
|
|
if (msg == EQUIP_ERR_OK)
|
|
{
|
|
player->StoreNewItem(dest, 30658, 1, true);
|
|
player->PlayerTalkClass->ClearMenus();
|
|
}
|
|
}
|
|
if (action == GOSSIP_ACTION_INFO_DEF+2)
|
|
{
|
|
ItemPosCountVec dest;
|
|
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 30659, 1, NULL);
|
|
if (msg == EQUIP_ERR_OK)
|
|
{
|
|
player->StoreNewItem(dest, 30659, 1, true);
|
|
player->PlayerTalkClass->ClearMenus();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OnGossipHello(Player* player, Creature* creature)
|
|
{
|
|
if (player->GetQuestStatus(10583) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30658, 1, true))
|
|
player->ADD_GOSSIP_ITEM(0, GOSSIP_HSK1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
|
|
if (player->GetQuestStatus(10601) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30659, 1, true))
|
|
player->ADD_GOSSIP_ITEM(0, GOSSIP_HSK2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
|
|
|
|
player->SEND_GOSSIP_MENU(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)
|
|
{
|
|
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;
|
|
}
|
|
};
|
|
|
|
/*####
|
|
# npc_earthmender_wilda
|
|
####*/
|
|
|
|
enum Earthmender
|
|
{
|
|
SAY_WIL_START = 0,
|
|
SAY_WIL_AGGRO = 1,
|
|
SAY_WIL_PROGRESS1 = 2,
|
|
SAY_WIL_PROGRESS2 = 3,
|
|
SAY_WIL_FIND_EXIT = 4,
|
|
SAY_WIL_JUST_AHEAD = 5,
|
|
SAY_WIL_END = 6,
|
|
|
|
SPELL_CHAIN_LIGHTNING = 16006,
|
|
SPELL_EARTHBING_TOTEM = 15786,
|
|
SPELL_FROST_SHOCK = 12548,
|
|
SPELL_HEALING_WAVE = 12491,
|
|
|
|
QUEST_ESCAPE_COILSCAR = 10451,
|
|
NPC_COILSKAR_ASSASSIN = 21044,
|
|
FACTION_EARTHEN = 1726 //guessed
|
|
};
|
|
|
|
class npc_earthmender_wilda : public CreatureScript
|
|
{
|
|
public:
|
|
npc_earthmender_wilda() : CreatureScript("npc_earthmender_wilda") { }
|
|
|
|
bool OnQuestAccept(Player* player, Creature* creature, const Quest* quest)
|
|
{
|
|
if (quest->GetQuestId() == QUEST_ESCAPE_COILSCAR)
|
|
{
|
|
creature->AI()->Talk(SAY_WIL_START, player);
|
|
creature->setFaction(FACTION_EARTHEN);
|
|
|
|
if (npc_earthmender_wildaAI* pEscortAI = CAST_AI(npc_earthmender_wilda::npc_earthmender_wildaAI, creature->AI()))
|
|
pEscortAI->Start(false, false, player->GetGUID(), quest);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
return new npc_earthmender_wildaAI(creature);
|
|
}
|
|
|
|
struct npc_earthmender_wildaAI : public npc_escortAI
|
|
{
|
|
npc_earthmender_wildaAI(Creature* creature) : npc_escortAI(creature) { }
|
|
|
|
uint32 m_uiHealingTimer;
|
|
|
|
void Reset()
|
|
{
|
|
m_uiHealingTimer = 0;
|
|
}
|
|
|
|
void WaypointReached(uint32 waypointId)
|
|
{
|
|
Player* player = GetPlayerForEscort();
|
|
if (!player)
|
|
return;
|
|
|
|
switch (waypointId)
|
|
{
|
|
case 13:
|
|
Talk(SAY_WIL_PROGRESS1, player);
|
|
DoSpawnAssassin();
|
|
break;
|
|
case 14:
|
|
DoSpawnAssassin();
|
|
break;
|
|
case 15:
|
|
Talk(SAY_WIL_FIND_EXIT, player);
|
|
break;
|
|
case 19:
|
|
DoRandomSay();
|
|
break;
|
|
case 20:
|
|
DoSpawnAssassin();
|
|
break;
|
|
case 26:
|
|
DoRandomSay();
|
|
break;
|
|
case 27:
|
|
DoSpawnAssassin();
|
|
break;
|
|
case 33:
|
|
DoRandomSay();
|
|
break;
|
|
case 34:
|
|
DoSpawnAssassin();
|
|
break;
|
|
case 37:
|
|
DoRandomSay();
|
|
break;
|
|
case 38:
|
|
DoSpawnAssassin();
|
|
break;
|
|
case 39:
|
|
Talk(SAY_WIL_JUST_AHEAD, player);
|
|
break;
|
|
case 43:
|
|
DoRandomSay();
|
|
break;
|
|
case 44:
|
|
DoSpawnAssassin();
|
|
break;
|
|
case 50:
|
|
Talk(SAY_WIL_END, player);
|
|
player->GroupEventHappens(QUEST_ESCAPE_COILSCAR, me);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JustSummoned(Creature* summoned)
|
|
{
|
|
if (summoned->GetEntry() == NPC_COILSKAR_ASSASSIN)
|
|
summoned->AI()->AttackStart(me);
|
|
}
|
|
|
|
//this is very unclear, random say without no real relevance to script/event
|
|
void DoRandomSay()
|
|
{
|
|
Talk(SAY_WIL_PROGRESS2);
|
|
}
|
|
|
|
void DoSpawnAssassin()
|
|
{
|
|
//unknown where they actually appear
|
|
DoSummon(NPC_COILSKAR_ASSASSIN, me, 15.0f, 5000, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT);
|
|
}
|
|
|
|
void EnterCombat(Unit* who)
|
|
{
|
|
//don't always use
|
|
if (rand()%5)
|
|
return;
|
|
|
|
//only aggro text if not player
|
|
if (who->GetTypeId() != TYPEID_PLAYER)
|
|
{
|
|
//appears to be random
|
|
if (urand(0, 1))
|
|
Talk(SAY_WIL_AGGRO);
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 uiDiff)
|
|
{
|
|
npc_escortAI::UpdateAI(uiDiff);
|
|
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
/// @todo add more abilities
|
|
if (!HealthAbovePct(30))
|
|
{
|
|
if (m_uiHealingTimer <= uiDiff)
|
|
{
|
|
DoCast(me, SPELL_HEALING_WAVE);
|
|
m_uiHealingTimer = 15000;
|
|
}
|
|
else
|
|
m_uiHealingTimer -= uiDiff;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
/*#####
|
|
# Quest: Battle of the crimson watch
|
|
#####*/
|
|
|
|
/* ContentData
|
|
Battle of the crimson watch - creatures, gameobjects and defines
|
|
npc_illidari_spawn : Adds that are summoned in the Crimson Watch battle.
|
|
npc_torloth_the_magnificent : Final Creature that players have to face before quest is completed
|
|
npc_lord_illidan_stormrage : Creature that controls the event.
|
|
go_crystal_prison : GameObject that begins the event and hands out quest
|
|
EndContentData */
|
|
|
|
#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 WaveData
|
|
{
|
|
uint8 SpawnCount, UsedSpawnPoint;
|
|
uint32 CreatureId, SpawnTimer, YellTimer;
|
|
};
|
|
|
|
static WaveData 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
|
|
{
|
|
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;
|
|
|
|
uint64 LordIllidanGUID;
|
|
uint64 AggroTargetGUID;
|
|
|
|
bool Timers;
|
|
|
|
void Reset()
|
|
{
|
|
AnimationTimer = 4000;
|
|
AnimationCount = 0;
|
|
LordIllidanGUID = 0;
|
|
AggroTargetGUID = 0;
|
|
Timers = false;
|
|
|
|
me->AddUnitState(UNIT_STATE_ROOT);
|
|
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
|
|
me->SetTarget(0);
|
|
}
|
|
|
|
void EnterCombat(Unit* /*who*/) { }
|
|
|
|
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->RemoveFlag(UNIT_FIELD_FLAGS, 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)
|
|
{
|
|
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)
|
|
{
|
|
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
|
|
{
|
|
return new npc_lord_illidan_stormrageAI(c);
|
|
}
|
|
|
|
struct npc_lord_illidan_stormrageAI : public ScriptedAI
|
|
{
|
|
npc_lord_illidan_stormrageAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
uint64 PlayerGUID;
|
|
|
|
uint32 WaveTimer;
|
|
uint32 AnnounceTimer;
|
|
|
|
int8 LiveCount;
|
|
uint8 WaveCount;
|
|
|
|
bool EventStarted;
|
|
bool Announced;
|
|
bool Failed;
|
|
|
|
void Reset()
|
|
{
|
|
PlayerGUID = 0;
|
|
|
|
WaveTimer = 10000;
|
|
AnnounceTimer = 7000;
|
|
LiveCount = 0;
|
|
WaveCount = 0;
|
|
|
|
EventStarted = false;
|
|
Announced = false;
|
|
Failed = false;
|
|
|
|
me->SetVisible(false);
|
|
}
|
|
|
|
void EnterCombat(Unit* /*who*/) { }
|
|
void MoveInLineOfSight(Unit* /*who*/) { }
|
|
|
|
void AttackStart(Unit* /*who*/) { }
|
|
|
|
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)
|
|
{
|
|
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
|
|
{
|
|
return new npc_illidari_spawnAI(c);
|
|
}
|
|
|
|
struct npc_illidari_spawnAI : public ScriptedAI
|
|
{
|
|
npc_illidari_spawnAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
uint64 LordIllidanGUID;
|
|
uint32 SpellTimer1, SpellTimer2, SpellTimer3;
|
|
bool Timers;
|
|
|
|
void Reset()
|
|
{
|
|
LordIllidanGUID = 0;
|
|
Timers = false;
|
|
}
|
|
|
|
void EnterCombat(Unit* /*who*/) { }
|
|
|
|
void JustDied(Unit* /*killer*/)
|
|
{
|
|
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)
|
|
{
|
|
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(SELECT_TARGET_RANDOM, 0))
|
|
{
|
|
if (target->GetTypeId() == TYPEID_PLAYER)
|
|
{
|
|
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 = NULL;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
++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)
|
|
{
|
|
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,
|
|
|
|
// 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,
|
|
|
|
// Factions
|
|
FACTION_ENRAGED_SOUL_FRIENDLY = 35,
|
|
FACTION_ENRAGED_SOUL_HOSTILE = 14
|
|
};
|
|
|
|
class npc_enraged_spirit : public CreatureScript
|
|
{
|
|
public:
|
|
npc_enraged_spirit() : CreatureScript("npc_enraged_spirit") { }
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
return new npc_enraged_spiritAI(creature);
|
|
}
|
|
|
|
struct npc_enraged_spiritAI : public ScriptedAI
|
|
{
|
|
npc_enraged_spiritAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void Reset() { }
|
|
|
|
void EnterCombat(Unit* /*who*/) { }
|
|
|
|
void JustDied(Unit* /*killer*/)
|
|
{
|
|
// 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;
|
|
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 = NULL;
|
|
Unit* totemOspirits = NULL;
|
|
|
|
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_ENRAGED_SOUL_FRIENDLY);
|
|
Summoned->GetMotionMaster()->MovePoint(0, totemOspirits->GetPositionX(), totemOspirits->GetPositionY(), Summoned->GetPositionZ());
|
|
|
|
if (Unit* owner = totemOspirits->GetOwner())
|
|
if (Player* player = owner->ToPlayer())
|
|
player->KilledMonsterCredit(credit, 0);
|
|
DoCast(totemOspirits, SPELL_SOUL_CAPTURED);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
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()
|
|
{
|
|
tapped = false;
|
|
tuberGUID = 0;
|
|
resetTimer = 60000;
|
|
}
|
|
|
|
void SetData(uint32 id, uint32 data)
|
|
{
|
|
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*/, const SpellInfo* spell)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
uint64 tuberGUID;
|
|
uint32 resetTimer;
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
return new npc_shadowmoon_tuber_nodeAI(creature);
|
|
}
|
|
};
|
|
|
|
void AddSC_shadowmoon_valley()
|
|
{
|
|
// Ours
|
|
new spell_q10612_10613_the_fel_and_the_furious();
|
|
new spell_q10563_q10596_to_legion_hold();
|
|
|
|
// Theirs
|
|
new npc_invis_infernal_caster();
|
|
new npc_infernal_attacker();
|
|
new npc_mature_netherwing_drake();
|
|
new npc_enslaved_netherwing_drake();
|
|
new npc_dragonmaw_peon();
|
|
new npc_drake_dealer_hurlunk();
|
|
new npcs_flanis_swiftwing_and_kagrosh();
|
|
new npc_karynaku();
|
|
new npc_earthmender_wilda();
|
|
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();
|
|
}
|