mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-25 06:36:24 +00:00
Merge branch 'master' into Playerbot
# Conflicts: # src/server/game/World/IWorld.h # src/server/game/World/World.h
This commit is contained in:
@@ -24,6 +24,7 @@ EndScriptData */
|
||||
|
||||
#include "AchievementMgr.h"
|
||||
#include "AuctionHouseMgr.h"
|
||||
#include "AutobroadcastMgr.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "Chat.h"
|
||||
#include "CreatureTextMgr.h"
|
||||
@@ -32,6 +33,7 @@ EndScriptData */
|
||||
#include "LFGMgr.h"
|
||||
#include "Language.h"
|
||||
#include "MapMgr.h"
|
||||
#include "ServerMotd.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SkillDiscovery.h"
|
||||
@@ -80,6 +82,7 @@ public:
|
||||
{ "areatrigger_tavern", HandleReloadAreaTriggerTavernCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "areatrigger_teleport", HandleReloadAreaTriggerTeleportCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "autobroadcast", HandleReloadAutobroadcastCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "motd", HandleReloadMotdCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "broadcast_text", HandleReloadBroadcastTextCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "battleground_template", HandleReloadBattlegroundTemplate, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "command", HandleReloadCommandCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
@@ -209,6 +212,7 @@ public:
|
||||
HandleReloadVehicleTemplateAccessoryCommand(handler);
|
||||
|
||||
HandleReloadAutobroadcastCommand(handler);
|
||||
HandleReloadMotdCommand(handler);
|
||||
HandleReloadBroadcastTextCommand(handler);
|
||||
HandleReloadBattlegroundTemplate(handler);
|
||||
return true;
|
||||
@@ -398,11 +402,20 @@ public:
|
||||
static bool HandleReloadAutobroadcastCommand(ChatHandler* handler)
|
||||
{
|
||||
LOG_INFO("server.loading", "Re-Loading Autobroadcasts...");
|
||||
sWorld->LoadAutobroadcasts();
|
||||
sAutobroadcastMgr->LoadAutobroadcasts();
|
||||
handler->SendGlobalGMSysMessage("DB table `autobroadcast` reloaded.");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleReloadMotdCommand(ChatHandler* handler)
|
||||
{
|
||||
LOG_INFO("server.loading", "Re-Loading Motd...");
|
||||
sWorld->LoadMotd();
|
||||
handler->SendGlobalGMSysMessage("DB table `motd` reloaded.");
|
||||
handler->SendGlobalSysMessage(Motd::GetMotd());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleReloadBroadcastTextCommand(ChatHandler* handler)
|
||||
{
|
||||
LOG_INFO("server.loading", "Re-Loading Broadcast texts...");
|
||||
|
||||
@@ -509,10 +509,40 @@ public:
|
||||
}
|
||||
|
||||
// Define the 'Message of the day' for the realm
|
||||
static bool HandleServerSetMotdCommand(ChatHandler* handler, std::string motd)
|
||||
static bool HandleServerSetMotdCommand(ChatHandler* handler, std::string realmId, Tail motd)
|
||||
{
|
||||
Motd::SetMotd(motd);
|
||||
handler->PSendSysMessage(LANG_MOTD_NEW, motd);
|
||||
std::wstring wMotd = std::wstring();
|
||||
std::string strMotd = std::string();
|
||||
|
||||
if (realmId.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (motd.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Utf8toWStr(motd, wMotd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WStrToUtf8(wMotd, strMotd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_MOTD);
|
||||
stmt->SetData(0, Acore::StringTo<int32>(realmId).value());
|
||||
stmt->SetData(1, strMotd);
|
||||
trans->Append(stmt);
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
|
||||
sWorld->LoadMotd();
|
||||
handler->PSendSysMessage(LANG_MOTD_NEW, Acore::StringTo<int32>(realmId).value(), strMotd);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,36 +22,50 @@
|
||||
|
||||
enum PrinceSay
|
||||
{
|
||||
SAY_AGGRO = 0,
|
||||
SAY_AGGRO = 0,
|
||||
SAY_AXE_TOSS1 = 1,
|
||||
SAY_AXE_TOSS2 = 2,
|
||||
SAY_SLAY = 6,
|
||||
SAY_SUMMON = 7,
|
||||
SAY_DEATH = 8,
|
||||
SAY_SLAY = 6,
|
||||
SAY_SUMMON = 7,
|
||||
SAY_DEATH = 8,
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_ENFEEBLE = 30843, //Enfeeble during phase 1 and 2
|
||||
SPELL_ENFEEBLE_EFFECT = 41624,
|
||||
SPELL_SHADOWNOVA = 30852, //Shadownova used during all phases
|
||||
SPELL_SW_PAIN = 30854, //Shadow word pain during phase 1 and 3 (different targeting rules though)
|
||||
SPELL_THRASH_PASSIVE = 12787, //Extra attack chance during phase 2
|
||||
SPELL_SUNDER_ARMOR = 30901, //Sunder armor during phase 2
|
||||
SPELL_THRASH_AURA = 12787, //Passive proc chance for thrash
|
||||
SPELL_EQUIP_AXES = 30857, //Visual for axe equiping
|
||||
SPELL_AMPLIFY_DAMAGE = 39095, //Amplifiy during phase 3
|
||||
SPELL_CLEAVE = 30131, //Same as Nightbane.
|
||||
SPELL_HELLFIRE = 30859, //Infenals' hellfire aura
|
||||
SPELL_ENFEEBLE = 30843,
|
||||
SPELL_ENFEEBLE_EFFECT = 41624,
|
||||
SPELL_SHADOW_NOVA = 30852,
|
||||
SPELL_SHADOW_WORD_PAIN = 30854,
|
||||
SPELL_THRASH_PASSIVE = 12787,
|
||||
SPELL_SUNDER_ARMOR = 30901,
|
||||
SPELL_THRASH_AURA = 12787,
|
||||
SPELL_EQUIP_AXES = 30857,
|
||||
SPELL_AMPLIFY_DAMAGE = 39095,
|
||||
SPELL_CLEAVE = 30131,
|
||||
SPELL_HELLFIRE = 30859,
|
||||
};
|
||||
|
||||
enum creatures
|
||||
{
|
||||
NETHERSPITE_INFERNAL = 17646,
|
||||
MALCHEZARS_AXE = 17650,
|
||||
NPC_NETHERSPITE_INFERNAL = 17646,
|
||||
NPC_MALCHEZARS_AXE = 17650,
|
||||
INFERNAL_MODEL_INVISIBLE = 11686,
|
||||
SPELL_INFERNAL_RELAY = 33814, // 30835,
|
||||
EQUIP_ID_AXE = 33542
|
||||
SPELL_INFERNAL_RELAY = 33814, // 30835,
|
||||
EQUIP_ID_AXE = 33542
|
||||
};
|
||||
|
||||
enum EventGroups
|
||||
{
|
||||
GROUP_ENFEEBLE,
|
||||
GROUP_SHADOW_NOVA,
|
||||
GROUP_SHADOW_WORD_PAIN,
|
||||
};
|
||||
|
||||
enum Phases
|
||||
{
|
||||
PHASE_ONE = 1,
|
||||
PHASE_TWO = 2,
|
||||
PHASE_THREE = 3
|
||||
};
|
||||
|
||||
struct InfernalPoint
|
||||
@@ -83,416 +97,242 @@ struct InfernalPoint
|
||||
{ -10935.7f, -1996.0f }
|
||||
};*/
|
||||
|
||||
//---------Infernal code first
|
||||
class netherspite_infernal : public CreatureScript
|
||||
struct boss_malchezaar : public BossAI
|
||||
{
|
||||
public:
|
||||
netherspite_infernal() : CreatureScript("netherspite_infernal") { }
|
||||
boss_malchezaar(Creature* creature) : BossAI(creature, DATA_MALCHEZZAR) { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
void Initialize()
|
||||
{
|
||||
return new netherspite_infernalAI(creature);
|
||||
_phase = 1;
|
||||
clearweapons();
|
||||
positions.clear();
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
|
||||
}
|
||||
|
||||
struct netherspite_infernalAI : public ScriptedAI
|
||||
void clearweapons()
|
||||
{
|
||||
netherspite_infernalAI(Creature* creature) : ScriptedAI(creature),
|
||||
HellfireTimer(0), CleanupTimer(0), point(nullptr) { }
|
||||
|
||||
uint32 HellfireTimer;
|
||||
uint32 CleanupTimer;
|
||||
ObjectGuid malchezaar;
|
||||
InfernalPoint* point;
|
||||
|
||||
void Reset() override { }
|
||||
void JustEngagedWith(Unit* /*who*/) override { }
|
||||
void MoveInLineOfSight(Unit* /*who*/) override { }
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (HellfireTimer)
|
||||
{
|
||||
if (HellfireTimer <= diff)
|
||||
{
|
||||
DoCast(me, SPELL_HELLFIRE);
|
||||
HellfireTimer = 0;
|
||||
}
|
||||
else
|
||||
HellfireTimer -= diff;
|
||||
}
|
||||
|
||||
if (CleanupTimer)
|
||||
{
|
||||
if (CleanupTimer <= diff)
|
||||
{
|
||||
CleanupTimer = 0;
|
||||
}
|
||||
else
|
||||
CleanupTimer -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (Unit* unit = ObjectAccessor::GetUnit(*me, malchezaar))
|
||||
if (Creature* creature = unit->ToCreature())
|
||||
creature->AI()->KilledUnit(who);
|
||||
}
|
||||
|
||||
void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
|
||||
{
|
||||
if (spell->Id == SPELL_INFERNAL_RELAY)
|
||||
{
|
||||
me->SetDisplayId(me->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID));
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
HellfireTimer = 4000;
|
||||
CleanupTimer = 170000;
|
||||
}
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* done_by, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (!done_by || done_by->GetGUID() != malchezaar)
|
||||
damage = 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class boss_malchezaar : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_malchezaar() : CreatureScript("boss_malchezaar") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetKarazhanAI<boss_malchezaarAI>(creature);
|
||||
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
|
||||
me->SetCanDualWield(false);
|
||||
}
|
||||
|
||||
struct boss_malchezaarAI : public ScriptedAI
|
||||
void Reset() override
|
||||
{
|
||||
boss_malchezaarAI(Creature* creature) : ScriptedAI(creature)
|
||||
Initialize();
|
||||
_Reset();
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
Talk(SAY_SLAY);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
_JustDied();
|
||||
Talk(SAY_DEATH);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
Talk(SAY_AGGRO);
|
||||
_JustEngagedWith();
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), false);
|
||||
|
||||
scheduler.Schedule(30s, [this](TaskContext context)
|
||||
{
|
||||
instance = creature->GetInstanceScript();
|
||||
}
|
||||
EnfeebleHealthEffect();
|
||||
|
||||
InstanceScript* instance;
|
||||
uint32 EnfeebleTimer;
|
||||
uint32 EnfeebleResetTimer;
|
||||
uint32 ShadowNovaTimer;
|
||||
uint32 SWPainTimer;
|
||||
uint32 SunderArmorTimer;
|
||||
uint32 AmplifyDamageTimer;
|
||||
uint32 InfernalTimer;
|
||||
uint32 InfernalCleanupTimer;
|
||||
uint32 phase;
|
||||
uint32 enfeeble_health[5];
|
||||
ObjectGuid enfeeble_targets[5];
|
||||
|
||||
GuidVector infernals;
|
||||
std::vector<InfernalPoint*> positions;
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
EnfeebleTimer = 30000;
|
||||
EnfeebleResetTimer = 38000;
|
||||
ShadowNovaTimer = 35500;
|
||||
SWPainTimer = 20000;
|
||||
InfernalCleanupTimer = 47000;
|
||||
AmplifyDamageTimer = 5000;
|
||||
SunderArmorTimer = urand(5000, 10000);
|
||||
InfernalTimer = 40000;
|
||||
phase = 1;
|
||||
clearweapons();
|
||||
positions.clear();
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
|
||||
}
|
||||
|
||||
void clearweapons()
|
||||
{
|
||||
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
|
||||
me->SetCanDualWield(false);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
Talk(SAY_SLAY);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
Talk(SAY_DEATH);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
|
||||
if (Creature* Axe = me->FindNearestCreature(MALCHEZARS_AXE, 100.0f))
|
||||
scheduler.Schedule(9s, [this](TaskContext)
|
||||
{
|
||||
Axe->DespawnOrUnsummon();
|
||||
}
|
||||
}
|
||||
EnfeebleResetHealth();
|
||||
});
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
context.SetGroup(GROUP_ENFEEBLE);
|
||||
scheduler.DelayGroup(GROUP_SHADOW_NOVA, 5s);
|
||||
context.Repeat();
|
||||
}).Schedule(35500ms, [this](TaskContext context)
|
||||
{
|
||||
Talk(SAY_AGGRO);
|
||||
DoZoneInCombat();
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), false);
|
||||
}
|
||||
|
||||
void SummonAxes()
|
||||
DoCastAOE(SPELL_SHADOW_NOVA);
|
||||
context.SetGroup(GROUP_SHADOW_NOVA);
|
||||
context.Repeat();
|
||||
}).Schedule(40s, [this](TaskContext context)
|
||||
{
|
||||
me->SummonCreature(MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
|
||||
}
|
||||
Position pos = me->GetRandomNearPosition(40.0);;
|
||||
|
||||
void EnfeebleHealthEffect()
|
||||
{
|
||||
SpellInfo const* info = sSpellMgr->GetSpellInfo(SPELL_ENFEEBLE_EFFECT);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList();
|
||||
std::vector<Unit*> targets;
|
||||
|
||||
if (t_list.empty())
|
||||
return;
|
||||
|
||||
//begin + 1, so we don't target the one with the highest threat
|
||||
ThreatContainer::StorageType::const_iterator itr = t_list.begin();
|
||||
std::advance(itr, 1);
|
||||
for (; itr != t_list.end(); ++itr) //store the threat list in a different container
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
|
||||
if (target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER)
|
||||
targets.push_back(target);
|
||||
|
||||
//cut down to size if we have more than 5 targets
|
||||
while (targets.size() > 5)
|
||||
targets.erase(targets.begin() + rand() % targets.size());
|
||||
|
||||
uint32 i = 0;
|
||||
for (std::vector<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter, ++i)
|
||||
if (Unit* target = *iter)
|
||||
{
|
||||
enfeeble_targets[i] = target->GetGUID();
|
||||
enfeeble_health[i] = target->GetHealth();
|
||||
|
||||
me->CastSpell(target, SPELL_ENFEEBLE, true, 0, 0, me->GetGUID());
|
||||
target->SetHealth(1);
|
||||
}
|
||||
}
|
||||
|
||||
void EnfeebleResetHealth()
|
||||
{
|
||||
for (uint8 i = 0; i < 5; ++i)
|
||||
if (Creature* RELAY = me->FindNearestCreature(NPC_RELAY, 100.0f))
|
||||
{
|
||||
Unit* target = ObjectAccessor::GetUnit(*me, enfeeble_targets[i]);
|
||||
if (target && target->IsAlive())
|
||||
target->SetHealth(enfeeble_health[i]);
|
||||
enfeeble_targets[i].Clear();
|
||||
enfeeble_health[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SummonInfernal()
|
||||
{
|
||||
Position pos;
|
||||
|
||||
if ((me->GetMapId() == 532))
|
||||
{
|
||||
pos = me->GetRandomNearPosition(40.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
InfernalPoint* point = Acore::Containers::SelectRandomContainerElement(positions);
|
||||
pos.Relocate(point->x, point->y, INFERNAL_Z, frand(0.0f, float(M_PI * 2)));
|
||||
}
|
||||
|
||||
if (Creature* RELAY = me->FindNearestCreature(NPC_RELAY, 100.0f))
|
||||
{
|
||||
Creature* infernal = RELAY->SummonCreature(NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000);
|
||||
|
||||
if (infernal)
|
||||
if (Creature* infernal = RELAY->SummonCreature(NPC_NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000))
|
||||
{
|
||||
infernal->SetDisplayId(INFERNAL_MODEL_INVISIBLE);
|
||||
infernal->SetFaction(me->GetFaction());
|
||||
infernals.push_back(infernal->GetGUID());
|
||||
infernal->SetControlled(true, UNIT_STATE_ROOT);
|
||||
RELAY->AI()->DoCast(infernal, SPELL_INFERNAL_RELAY);
|
||||
summons.Summon(infernal);
|
||||
}
|
||||
}
|
||||
Talk(SAY_SUMMON);
|
||||
}
|
||||
|
||||
void Phase2()
|
||||
context.Repeat(_phase == PHASE_THREE ? 15s : 45s);
|
||||
|
||||
Talk(SAY_SUMMON);
|
||||
}).Schedule(20s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_SHADOW_WORD_PAIN);
|
||||
context.SetGroup(GROUP_SHADOW_WORD_PAIN);
|
||||
context.Repeat();
|
||||
});
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (me->HealthBelowPctDamaged(60, damage) && _phase == PHASE_ONE)
|
||||
{
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
phase = 2;
|
||||
DoCast(me, SPELL_EQUIP_AXES);
|
||||
_phase = 2;
|
||||
DoCastSelf( SPELL_EQUIP_AXES);
|
||||
Talk(SAY_AXE_TOSS1);
|
||||
DoCast(me, SPELL_THRASH_AURA, true);
|
||||
DoCastSelf( SPELL_THRASH_AURA, true);
|
||||
SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE);
|
||||
me->SetCanDualWield(true);
|
||||
me->SetAttackTime(OFF_ATTACK, (me->GetAttackTime(BASE_ATTACK) * 150) / 100);
|
||||
SunderArmorTimer = urand(5000, 10000);
|
||||
}
|
||||
|
||||
void Phase3()
|
||||
scheduler.Schedule(5s, 10s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_SUNDER_ARMOR);
|
||||
context.Repeat();
|
||||
});
|
||||
|
||||
scheduler.CancelGroup(GROUP_SHADOW_WORD_PAIN);
|
||||
}
|
||||
else if (me->HealthBelowPctDamaged(30, damage) && _phase == PHASE_TWO)
|
||||
{
|
||||
me->RemoveAurasDueToSpell(SPELL_THRASH_AURA);
|
||||
Talk(SAY_AXE_TOSS2);
|
||||
phase = 3;
|
||||
_phase = PHASE_THREE;
|
||||
clearweapons();
|
||||
SummonAxes();
|
||||
AmplifyDamageTimer = urand(20000, 30000);
|
||||
|
||||
me->SummonCreature(NPC_MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
|
||||
|
||||
scheduler.Schedule(20s, 30s, [this](TaskContext context)
|
||||
{
|
||||
DoCastRandomTarget(SPELL_AMPLIFY_DAMAGE, 1);
|
||||
context.Repeat();
|
||||
}).Schedule(20s, [this](TaskContext context)
|
||||
{
|
||||
DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN);
|
||||
context.SetGroup(GROUP_SHADOW_WORD_PAIN);
|
||||
context.Repeat();
|
||||
});;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
if (EnfeebleResetTimer && EnfeebleResetTimer <= diff) // Let's not forget to reset that
|
||||
{
|
||||
EnfeebleResetHealth();
|
||||
EnfeebleResetTimer = 0;
|
||||
}
|
||||
else EnfeebleResetTimer -= diff;
|
||||
|
||||
if (InfernalTimer <= diff)
|
||||
{
|
||||
SummonInfernal();
|
||||
InfernalTimer = phase == 3 ? 14500 : 44500; // 15 secs in phase 3, 45 otherwise
|
||||
}
|
||||
else InfernalTimer -= diff;
|
||||
|
||||
if (phase != 2)
|
||||
{
|
||||
if (SWPainTimer <= diff)
|
||||
{
|
||||
|
||||
// if phase == 1 target the tank, otherwise anyone but the tank
|
||||
Unit* target = phase == 1
|
||||
? me->GetVictim()
|
||||
: SelectTarget(SelectTargetMethod::Random, 1, 100, true);
|
||||
|
||||
if (target)
|
||||
{
|
||||
DoCast(target, SPELL_SW_PAIN);
|
||||
}
|
||||
|
||||
SWPainTimer = 20000;
|
||||
}
|
||||
else
|
||||
SWPainTimer -= diff;
|
||||
}
|
||||
|
||||
if (ShadowNovaTimer <= diff)
|
||||
{
|
||||
DoCast(SPELL_SHADOWNOVA);
|
||||
ShadowNovaTimer = 35500;
|
||||
}
|
||||
else
|
||||
ShadowNovaTimer -= diff;
|
||||
|
||||
if (phase == 1)
|
||||
{
|
||||
if (HealthBelowPct(60))
|
||||
{
|
||||
Phase2();
|
||||
}
|
||||
}
|
||||
|
||||
if (phase == 2)
|
||||
{
|
||||
if (SunderArmorTimer <= diff)
|
||||
{
|
||||
DoCast(SPELL_SUNDER_ARMOR);
|
||||
SunderArmorTimer = urand(5000, 10000);
|
||||
}
|
||||
else
|
||||
SunderArmorTimer -= diff;
|
||||
|
||||
if (HealthBelowPct(30))
|
||||
{
|
||||
Phase3();
|
||||
}
|
||||
}
|
||||
|
||||
if (phase == 3)
|
||||
{
|
||||
if (AmplifyDamageTimer <= diff)
|
||||
{
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true);
|
||||
|
||||
if (target)
|
||||
{
|
||||
DoCast(target, SPELL_AMPLIFY_DAMAGE);
|
||||
AmplifyDamageTimer = urand(20000, 30000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AmplifyDamageTimer -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (phase != 3)
|
||||
{
|
||||
if (EnfeebleTimer <= diff)
|
||||
{
|
||||
EnfeebleHealthEffect();
|
||||
EnfeebleTimer = 30000;
|
||||
ShadowNovaTimer = 5000;
|
||||
EnfeebleResetTimer = 9000;
|
||||
}
|
||||
else EnfeebleTimer -= diff;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class prince_axes : public CreatureScript
|
||||
{
|
||||
public:
|
||||
prince_axes() : CreatureScript("prince_axes") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetKarazhanAI<prince_axesAI>(creature);
|
||||
}
|
||||
|
||||
struct prince_axesAI : public ScriptedAI
|
||||
void EnfeebleHealthEffect()
|
||||
{
|
||||
prince_axesAI(Creature* creature) : ScriptedAI(creature)
|
||||
std::list<Unit*> targetList;
|
||||
SelectTargetList(targetList, 5, SelectTargetMethod::Random, 1, [&](Unit* u) { return u->IsAlive() && u->IsPlayer(); });
|
||||
|
||||
if (targetList.empty())
|
||||
return;
|
||||
|
||||
for (auto const& target : targetList)
|
||||
{
|
||||
Initialize();
|
||||
instance = creature->GetInstanceScript();
|
||||
if (target)
|
||||
{
|
||||
_enfeebleTargets[target->GetGUID()] = target->GetHealth();
|
||||
|
||||
me->CastSpell(target, SPELL_ENFEEBLE, true);
|
||||
target->SetHealth(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 AxesTargetSwitchTimer;
|
||||
InstanceScript* instance;
|
||||
|
||||
void Initialize()
|
||||
void EnfeebleResetHealth()
|
||||
{
|
||||
for (auto targets : _enfeebleTargets)
|
||||
{
|
||||
AxesTargetSwitchTimer = 7500;
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*me, targets.first))
|
||||
{
|
||||
if (target->IsAlive())
|
||||
{
|
||||
target->SetHealth(targets.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 _phase;
|
||||
std::map<ObjectGuid, uint32> _enfeebleTargets;
|
||||
std::vector<InfernalPoint*> positions;
|
||||
};
|
||||
|
||||
struct npc_netherspite_infernal : public ScriptedAI
|
||||
{
|
||||
npc_netherspite_infernal(Creature* creature) : ScriptedAI(creature) { }
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override { }
|
||||
void MoveInLineOfSight(Unit* /*who*/) override { }
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
_scheduler.Update(diff);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (me->ToTempSummon())
|
||||
{
|
||||
if (WorldObject* summoner = me->ToTempSummon()->GetSummoner())
|
||||
{
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
{
|
||||
creature->AI()->KilledUnit(who);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
|
||||
{
|
||||
if (spell->Id == SPELL_INFERNAL_RELAY)
|
||||
{
|
||||
me->SetDisplayId(me->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID));
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
me->SetCanDualWield(true);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_scheduler.Schedule(4s, [this](TaskContext /*context*/)
|
||||
{
|
||||
DoCastSelf(SPELL_HELLFIRE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
DoZoneInCombat();
|
||||
}
|
||||
void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
void changetarget()
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
struct npc_malchezaar_axe : public ScriptedAI
|
||||
{
|
||||
npc_malchezaar_axe(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
creature->SetCanDualWield(true);
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
DoZoneInCombat();
|
||||
_scheduler.Schedule(7500ms, [this](TaskContext context)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
|
||||
{
|
||||
if (me->GetVictim())
|
||||
{
|
||||
@@ -501,29 +341,27 @@ public:
|
||||
|
||||
me->AddThreat(target, 1000000.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
context.Repeat(7500ms, 20s);
|
||||
});
|
||||
}
|
||||
|
||||
if (AxesTargetSwitchTimer <= diff)
|
||||
{
|
||||
AxesTargetSwitchTimer = urand(7500, 20000);
|
||||
changetarget();
|
||||
}
|
||||
else
|
||||
AxesTargetSwitchTimer -= diff;
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
_scheduler.Update(diff,
|
||||
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
void AddSC_boss_malchezaar()
|
||||
{
|
||||
new boss_malchezaar();
|
||||
new prince_axes();
|
||||
new netherspite_infernal();
|
||||
RegisterKarazhanCreatureAI(boss_malchezaar);
|
||||
RegisterKarazhanCreatureAI(npc_malchezaar_axe);
|
||||
RegisterKarazhanCreatureAI(npc_netherspite_infernal);
|
||||
}
|
||||
|
||||
@@ -106,16 +106,6 @@ public:
|
||||
me->DespawnOrUnsummon(1);
|
||||
}
|
||||
|
||||
void IsSummonedBy(WorldObject* /*summoner*/) override
|
||||
{
|
||||
events2.Reset();
|
||||
events2.ScheduleEvent(INTRO_1, 1s);
|
||||
me->SetDisableGravity(true);
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
|
||||
me->SetImmuneToAll(true); // for some reason he aggroes if we don't have this.
|
||||
}
|
||||
|
||||
void MovementInform(uint32 type, uint32 id) override
|
||||
{
|
||||
if (type == WAYPOINT_MOTION_TYPE && id == POINT_KIRTONOS_LAND)
|
||||
@@ -128,6 +118,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
{
|
||||
if (action == IN_PROGRESS)
|
||||
{
|
||||
events2.Reset();
|
||||
events2.ScheduleEvent(INTRO_1, 1s);
|
||||
me->SetCanFly(true);
|
||||
me->SetDisableGravity(true);
|
||||
me->SendMovementFlagUpdate();
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
|
||||
me->SetImmuneToAll(true); // for some reason he aggroes if we don't have this.
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
events2.Update(diff);
|
||||
@@ -148,6 +153,7 @@ public:
|
||||
me->SetCanFly(false);
|
||||
me->SetDisableGravity(false);
|
||||
me->CastSpell(me, SPELL_KIRTONOS_TRANSFORM, true);
|
||||
me->SendMovementFlagUpdate();
|
||||
break;
|
||||
case INTRO_5:
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
|
||||
|
||||
@@ -131,7 +131,10 @@ public:
|
||||
// summon kirtonos and close door
|
||||
if (_kirtonosState == NOT_STARTED)
|
||||
{
|
||||
instance->SummonCreature(NPC_KIRTONOS, KirtonosSpawn);
|
||||
if (Creature* kirtonos = instance->SummonCreature(NPC_KIRTONOS, KirtonosSpawn))
|
||||
{
|
||||
kirtonos->AI()->DoAction(IN_PROGRESS);
|
||||
}
|
||||
if (GameObject* gate = instance->GetGameObject(GetGuidData(GO_GATE_KIRTONOS)))
|
||||
{
|
||||
gate->SetGoState(GO_STATE_READY);
|
||||
|
||||
@@ -57,36 +57,13 @@ public:
|
||||
{
|
||||
boss_lieutenant_drakeAI(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
pathPoints.clear();
|
||||
WPPath* path = sSmartWaypointMgr->GetPath(me->GetEntry());
|
||||
if (!path || path->empty())
|
||||
return;
|
||||
|
||||
pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
|
||||
|
||||
uint32 wpCounter = 1;
|
||||
WPPath::const_iterator itr;
|
||||
while ((itr = path->find(wpCounter++)) != path->end())
|
||||
{
|
||||
WayPoint* wp = itr->second;
|
||||
pathPoints.push_back(G3D::Vector3(wp->x, wp->y, wp->z));
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeAI() override
|
||||
{
|
||||
ScriptedAI::InitializeAI();
|
||||
//Talk(SAY_ENTER);
|
||||
JustReachedHome();
|
||||
}
|
||||
|
||||
void JustReachedHome() override
|
||||
{
|
||||
me->SetWalk(true);
|
||||
Movement::MoveSplineInit init(me);
|
||||
init.MovebyPath(pathPoints);
|
||||
init.SetCyclic();
|
||||
init.Launch();
|
||||
runSecondPath = false;
|
||||
pathId = me->GetEntry() * 10;
|
||||
me->GetMotionMaster()->MovePath(pathId, false);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
@@ -118,8 +95,38 @@ public:
|
||||
instance->SetData(DATA_ESCORT_PROGRESS, ENCOUNTER_PROGRESS_DRAKE_KILLED);
|
||||
}
|
||||
|
||||
void MovementInform(uint32 type, uint32 point) override
|
||||
{
|
||||
if (type != WAYPOINT_MOTION_TYPE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathId == me->GetEntry() * 10)
|
||||
{
|
||||
switch (point)
|
||||
{
|
||||
case 7:
|
||||
Talk(SAY_ENTER);
|
||||
break;
|
||||
case 10:
|
||||
pathId = (me->GetEntry() * 10) + 1;
|
||||
runSecondPath = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (runSecondPath)
|
||||
{
|
||||
runSecondPath = false;
|
||||
me->GetMotionMaster()->MovePath(pathId, true);
|
||||
}
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
@@ -161,7 +168,8 @@ public:
|
||||
|
||||
private:
|
||||
EventMap events;
|
||||
Movement::PointsArray pathPoints;
|
||||
uint32 pathId;
|
||||
bool runSecondPath;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "old_hillsbrad.h"
|
||||
|
||||
const Position instancePositions[INSTANCE_POSITIONS_COUNT] =
|
||||
static Position const instancePositions[INSTANCE_POSITIONS_COUNT] =
|
||||
{
|
||||
{2188.18f, 228.90f, 53.025f, 1.77f}, // Orcs Gather Point 1
|
||||
{2103.23f, 93.55f, 53.096f, 3.78f}, // Orcs Gather Point 2
|
||||
{2128.43f, 71.01f, 64.42f, 1.74f} // Lieutenant Drake Summon Position
|
||||
{ 2188.18f, 228.90f, 53.025f, 1.77f }, // Orcs Gather Point 1
|
||||
{ 2103.23f, 93.550f, 53.096f, 3.78f }, // Orcs Gather Point 2
|
||||
{ 2172.76f, 149.54f, 87.981f, 4.19f } // Lieutenant Drake Summon Position
|
||||
};
|
||||
|
||||
const Position thrallPositions[THRALL_POSITIONS_COUNT] =
|
||||
@@ -248,11 +248,8 @@ public:
|
||||
case EVENT_SUMMON_LIEUTENANT:
|
||||
{
|
||||
instance->LoadGrid(instancePositions[2].GetPositionX(), instancePositions[2].GetPositionY());
|
||||
if (Creature* drake = instance->SummonCreature(NPC_LIEUTENANT_DRAKE, instancePositions[2]))
|
||||
{
|
||||
drake->AI()->Talk(0);
|
||||
}
|
||||
[[fallthrough]]; /// @todo: Not sure whether the fallthrough was a mistake (forgetting a break) or intended. This should be double-checked.
|
||||
instance->SummonCreature(NPC_LIEUTENANT_DRAKE, instancePositions[2]);
|
||||
break;
|
||||
}
|
||||
case EVENT_THRALL_REPOSITION:
|
||||
{
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
#include "TemporarySummon.h"
|
||||
#include "the_black_morass.h"
|
||||
|
||||
const Position PortalLocation[4] =
|
||||
#define MAX_PORTAL_LOCATIONS 4
|
||||
const Position PortalLocation[MAX_PORTAL_LOCATIONS] =
|
||||
{
|
||||
{ -2030.8318f, 7024.9443f, 23.071817f, 3.14159f },
|
||||
{ -1961.7335f, 7029.5280f, 21.811401f, 2.12931f },
|
||||
@@ -58,6 +59,7 @@ public:
|
||||
_currentRift = 0;
|
||||
_shieldPercent = 100;
|
||||
encounterNPCs.clear();
|
||||
_timerToNextBoss = 0;
|
||||
}
|
||||
|
||||
void CleanupInstance()
|
||||
@@ -66,6 +68,8 @@ public:
|
||||
_currentRift = 0;
|
||||
_shieldPercent = 100;
|
||||
|
||||
_usedRiftPostions.fill(ObjectGuid::Empty);
|
||||
|
||||
instance->LoadGrid(-2023.0f, 7121.0f);
|
||||
if (Creature* medivh = instance->GetCreature(_medivhGUID))
|
||||
{
|
||||
@@ -116,6 +120,7 @@ public:
|
||||
case NPC_INFINITE_CRONOMANCER:
|
||||
case NPC_INFINITE_EXECUTIONER:
|
||||
case NPC_INFINITE_VANQUISHER:
|
||||
case NPC_DP_BEAM_STALKER:
|
||||
encounterNPCs.insert(creature->GetGUID());
|
||||
break;
|
||||
}
|
||||
@@ -150,45 +155,105 @@ public:
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_AEONUS:
|
||||
{
|
||||
encounters[type] = DONE;
|
||||
SaveToDB();
|
||||
|
||||
if (Creature* medivh = instance->GetCreature(_medivhGUID))
|
||||
{
|
||||
encounters[type] = DONE;
|
||||
SaveToDB();
|
||||
|
||||
if (Creature* medivh = instance->GetCreature(_medivhGUID))
|
||||
medivh->AI()->DoAction(ACTION_OUTRO);
|
||||
|
||||
Map::PlayerList const& players = instance->GetPlayers();
|
||||
if (!players.IsEmpty())
|
||||
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
||||
if (Player* player = itr->GetSource())
|
||||
{
|
||||
if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
|
||||
player->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);
|
||||
|
||||
if (player->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
|
||||
player->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
|
||||
}
|
||||
break;
|
||||
medivh->AI()->DoAction(ACTION_OUTRO);
|
||||
}
|
||||
|
||||
Map::PlayerList const& players = instance->GetPlayers();
|
||||
if (!players.IsEmpty())
|
||||
{
|
||||
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
||||
{
|
||||
if (Player* player = itr->GetSource())
|
||||
{
|
||||
if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
|
||||
{
|
||||
player->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);
|
||||
}
|
||||
|
||||
if (player->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
|
||||
{
|
||||
player->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_CHRONO_LORD_DEJA:
|
||||
case TYPE_TEMPORUS:
|
||||
{
|
||||
GuidSet eCopy = encounterNPCs;
|
||||
for (ObjectGuid const& guid : eCopy)
|
||||
{
|
||||
if (Creature* creature = instance->GetCreature(guid))
|
||||
{
|
||||
switch (creature->GetEntry())
|
||||
{
|
||||
case NPC_RIFT_KEEPER_WARLOCK:
|
||||
case NPC_RIFT_KEEPER_MAGE:
|
||||
case NPC_RIFT_LORD:
|
||||
case NPC_RIFT_LORD_2:
|
||||
case NPC_TIME_RIFT:
|
||||
creature->DespawnOrUnsummon();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
encounters[type] = DONE;
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 60000);
|
||||
|
||||
if (!_timerToNextBoss || _timerToNextBoss > 30 * IN_MILLISECONDS)
|
||||
{
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 30 * IN_MILLISECONDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, _timerToNextBoss);
|
||||
}
|
||||
Events.SetPhase(1);
|
||||
SaveToDB();
|
||||
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
|
||||
break;
|
||||
case DATA_RIFT_KILLED:
|
||||
if (!Events.IsInPhase(1))
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 4000);
|
||||
break;
|
||||
}
|
||||
case DATA_MEDIVH:
|
||||
{
|
||||
DoUpdateWorldState(WORLD_STATE_BM, 1);
|
||||
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
|
||||
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 3000);
|
||||
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
|
||||
|
||||
for (ObjectGuid const& guid : encounterNPCs)
|
||||
{
|
||||
if (guid.GetEntry() == NPC_DP_BEAM_STALKER)
|
||||
{
|
||||
if (Creature* creature = instance->GetCreature(guid))
|
||||
{
|
||||
if (!creature->IsAlive())
|
||||
{
|
||||
creature->Respawn(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DATA_DAMAGE_SHIELD:
|
||||
{
|
||||
if (_shieldPercent <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_shieldPercent -= data;
|
||||
if (_shieldPercent < 0)
|
||||
{
|
||||
@@ -203,15 +268,20 @@ public:
|
||||
{
|
||||
if (medivh->IsAlive())
|
||||
{
|
||||
Unit::Kill(medivh, medivh);
|
||||
medivh->SetImmuneToNPC(true);
|
||||
|
||||
// Xinef: delete all spawns
|
||||
GuidSet eCopy = encounterNPCs;
|
||||
for (ObjectGuid const& guid : eCopy)
|
||||
if (medivh->IsAIEnabled)
|
||||
{
|
||||
medivh->AI()->Talk(SAY_MEDIVH_DEATH);
|
||||
}
|
||||
|
||||
Events.ScheduleEvent(EVENT_WIPE_1, 4s);
|
||||
|
||||
for (ObjectGuid const& guid : encounterNPCs)
|
||||
{
|
||||
if (Creature* creature = instance->GetCreature(guid))
|
||||
{
|
||||
creature->DespawnOrUnsummon();
|
||||
creature->InterruptNonMeleeSpells(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,6 +289,8 @@ public:
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +316,34 @@ public:
|
||||
encounterNPCs.insert(data);
|
||||
else if (type == DATA_DELETED_NPC)
|
||||
encounterNPCs.erase(data);
|
||||
else if (type == DATA_RIFT_KILLED)
|
||||
{
|
||||
if (!Events.IsInPhase(1))
|
||||
{
|
||||
uint8 emptySpots = 0;
|
||||
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
|
||||
{
|
||||
if (!_usedRiftPostions[i])
|
||||
{
|
||||
++emptySpots;
|
||||
}
|
||||
|
||||
if (_usedRiftPostions[i] == data)
|
||||
{
|
||||
_usedRiftPostions[i].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (emptySpots >= MAX_PORTAL_LOCATIONS - 1)
|
||||
{
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 4000);
|
||||
}
|
||||
else if (!emptySpots)
|
||||
{
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, (_currentRift >= 13 ? 120 : 90) * IN_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectGuid GetGuidData(uint32 data) const override
|
||||
@@ -254,17 +354,11 @@ public:
|
||||
return ObjectGuid::Empty;
|
||||
}
|
||||
|
||||
void SummonPortalKeeper()
|
||||
void SummonPortalKeeper(uint32 eventId)
|
||||
{
|
||||
Creature* rift = nullptr;
|
||||
for (ObjectGuid const& guid : encounterNPCs)
|
||||
if (Creature* summon = instance->GetCreature(guid))
|
||||
if (summon->GetEntry() == NPC_TIME_RIFT)
|
||||
{
|
||||
rift = summon;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8 riftPosition = eventId - EVENT_SUMMON_KEEPER_1;
|
||||
ObjectGuid const& riftGUID = _usedRiftPostions[riftPosition];
|
||||
Creature* rift = instance->GetCreature(riftGUID);
|
||||
if (!rift)
|
||||
return;
|
||||
|
||||
@@ -307,23 +401,120 @@ public:
|
||||
|
||||
void Update(uint32 diff) override
|
||||
{
|
||||
if (_timerToNextBoss)
|
||||
{
|
||||
if (_timerToNextBoss <= diff)
|
||||
{
|
||||
_timerToNextBoss = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_timerToNextBoss -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
Events.Update(diff);
|
||||
switch (Events.ExecuteEvent())
|
||||
|
||||
uint32 eventId = Events.ExecuteEvent();
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_NEXT_PORTAL:
|
||||
++_currentRift;
|
||||
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
|
||||
Events.ScheduleEvent(EVENT_SUMMON_KEEPER, 6000);
|
||||
Events.SetPhase(0);
|
||||
|
||||
{
|
||||
if (instance->GetCreature(_medivhGUID))
|
||||
{
|
||||
uint8 position = (_currentRift - 1) % 4;
|
||||
instance->SummonCreature(NPC_TIME_RIFT, PortalLocation[position]);
|
||||
uint8 position = MAX_PORTAL_LOCATIONS;
|
||||
|
||||
std::vector<uint8> possibleSpots;
|
||||
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
|
||||
{
|
||||
if (!_usedRiftPostions[i])
|
||||
{
|
||||
possibleSpots.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!possibleSpots.empty())
|
||||
{
|
||||
position = Acore::Containers::SelectRandomContainerElement(possibleSpots);
|
||||
}
|
||||
|
||||
if (position < MAX_PORTAL_LOCATIONS)
|
||||
{
|
||||
++_currentRift;
|
||||
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
|
||||
Events.ScheduleEvent(EVENT_SUMMON_KEEPER_1 + position, 6000);
|
||||
Events.SetPhase(0);
|
||||
|
||||
if (Creature* rift = instance->SummonCreature(NPC_TIME_RIFT, PortalLocation[position]))
|
||||
{
|
||||
_usedRiftPostions[position] = rift->GetGUID();
|
||||
|
||||
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
|
||||
{
|
||||
if (!_usedRiftPostions[i])
|
||||
{
|
||||
Events.RescheduleEvent(EVENT_NEXT_PORTAL, (_currentRift >= 13 ? 120 : 90) * IN_MILLISECONDS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_SUMMON_KEEPER:
|
||||
SummonPortalKeeper();
|
||||
}
|
||||
case EVENT_SUMMON_KEEPER_1:
|
||||
case EVENT_SUMMON_KEEPER_2:
|
||||
case EVENT_SUMMON_KEEPER_3:
|
||||
case EVENT_SUMMON_KEEPER_4:
|
||||
SummonPortalKeeper(eventId);
|
||||
break;
|
||||
case EVENT_WIPE_1:
|
||||
if (Creature* medivh = instance->GetCreature(_medivhGUID))
|
||||
{
|
||||
medivh->RemoveAllAuras();
|
||||
}
|
||||
Events.ScheduleEvent(EVENT_WIPE_2, 500ms);
|
||||
break;
|
||||
case EVENT_WIPE_2:
|
||||
if (Creature* medivh = instance->GetCreature(_medivhGUID))
|
||||
{
|
||||
medivh->KillSelf(false);
|
||||
|
||||
GuidSet encounterNPCSCopy = encounterNPCs;
|
||||
for (ObjectGuid const& guid : encounterNPCSCopy)
|
||||
{
|
||||
switch (guid.GetEntry())
|
||||
{
|
||||
case NPC_TIME_RIFT:
|
||||
case NPC_DP_EMITTER_STALKER:
|
||||
case NPC_DP_CRYSTAL_STALKER:
|
||||
case NPC_DP_BEAM_STALKER:
|
||||
if (Creature* creature = instance->GetCreature(guid))
|
||||
{
|
||||
creature->DespawnOrUnsummon();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Events.ScheduleEvent(EVENT_WIPE_3, 2s);
|
||||
break;
|
||||
case EVENT_WIPE_3:
|
||||
{
|
||||
GuidSet encounterNPCSCopy = encounterNPCs;
|
||||
for (ObjectGuid const& guid : encounterNPCSCopy)
|
||||
{
|
||||
if (Creature* creature = instance->GetCreature(guid))
|
||||
{
|
||||
creature->CastSpell(creature, SPELL_TELEPORT_VISUAL, true);
|
||||
creature->DespawnOrUnsummon(1200ms, 0s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -344,6 +535,8 @@ public:
|
||||
|
||||
protected:
|
||||
EventMap Events;
|
||||
std::array<ObjectGuid, MAX_PORTAL_LOCATIONS> _usedRiftPostions;
|
||||
uint32 _timerToNextBoss;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -22,33 +22,8 @@
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ScriptedGossip.h"
|
||||
|
||||
enum medivhSays
|
||||
{
|
||||
SAY_ENTER = 0,
|
||||
SAY_DEATH = 5,
|
||||
SAY_WIN = 6,
|
||||
SAY_ORCS_ENTER = 7,
|
||||
|
||||
SAY_ORCS_ANSWER = 0
|
||||
};
|
||||
|
||||
enum medivhSpells
|
||||
{
|
||||
SPELL_MANA_SHIELD = 31635,
|
||||
SPELL_MEDIVH_CHANNEL = 31556,
|
||||
SPELL_BLACK_CRYSTAL = 32563,
|
||||
SPELL_PORTAL_CRYSTALS = 32564,
|
||||
SPELL_BANISH_PURPLE = 32566,
|
||||
SPELL_BANISH_GREEN = 32567,
|
||||
|
||||
SPELL_CORRUPT = 31326,
|
||||
SPELL_CORRUPT_AEONUS = 37853,
|
||||
};
|
||||
|
||||
enum medivhMisc
|
||||
{
|
||||
NPC_DP_EMITTER_STALKER = 18582,
|
||||
NPC_DP_CRYSTAL_STALKER = 18553,
|
||||
NPC_SHADOW_COUNCIL_ENFORCER = 17023,
|
||||
GO_DARK_PORTAL = 185103,
|
||||
|
||||
@@ -120,6 +95,8 @@ struct npc_medivh_bm : public ScriptedAI
|
||||
{
|
||||
me->CastSpell(me, SPELL_MEDIVH_CHANNEL, false);
|
||||
}
|
||||
|
||||
me->SetImmuneToNPC(false);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
@@ -156,7 +133,7 @@ struct npc_medivh_bm : public ScriptedAI
|
||||
|
||||
if (who->GetTypeId() == TYPEID_PLAYER && me->IsWithinDistInMap(who, 20.0f))
|
||||
{
|
||||
Talk(SAY_ENTER);
|
||||
Talk(SAY_MEDIVH_ENTER);
|
||||
_instance->SetData(DATA_MEDIVH, 1);
|
||||
|
||||
me->CastSpell(me, SPELL_MEDIVH_CHANNEL, false);
|
||||
@@ -189,7 +166,6 @@ struct npc_medivh_bm : public ScriptedAI
|
||||
{
|
||||
me->SetRespawnTime(DAY);
|
||||
events.Reset();
|
||||
Talk(SAY_DEATH);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
@@ -216,7 +192,7 @@ struct npc_medivh_bm : public ScriptedAI
|
||||
break;
|
||||
case EVENT_OUTRO_1:
|
||||
me->SetFacingTo(6.21f);
|
||||
Talk(SAY_WIN);
|
||||
Talk(SAY_MEDIVH_WIN);
|
||||
events.ScheduleEvent(EVENT_OUTRO_2, 17000);
|
||||
break;
|
||||
case EVENT_OUTRO_2:
|
||||
@@ -240,14 +216,14 @@ struct npc_medivh_bm : public ScriptedAI
|
||||
events.ScheduleEvent(EVENT_OUTRO_7, 7000);
|
||||
break;
|
||||
case EVENT_OUTRO_7:
|
||||
Talk(SAY_ORCS_ENTER);
|
||||
Talk(SAY_MEDIVH_ORCS_ENTER);
|
||||
events.ScheduleEvent(EVENT_OUTRO_8, 7000);
|
||||
break;
|
||||
case EVENT_OUTRO_8:
|
||||
if (Creature* cr = me->FindNearestCreature(NPC_SHADOW_COUNCIL_ENFORCER, 20.0f))
|
||||
{
|
||||
cr->SetFacingTo(3.07f);
|
||||
cr->AI()->Talk(SAY_ORCS_ANSWER);
|
||||
cr->AI()->Talk(SAY_MEDIVH_ORCS_ANSWER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -350,7 +326,7 @@ struct npc_time_rift : public NullCreatureAI
|
||||
Creature* riftKeeper = ObjectAccessor::GetCreature(*me, _riftKeeperGUID);
|
||||
if (!riftKeeper || !riftKeeper->IsAlive())
|
||||
{
|
||||
_instance->SetData(DATA_RIFT_KILLED, 1);
|
||||
_instance->SetGuidData(DATA_RIFT_KILLED, me->GetGUID());
|
||||
|
||||
me->DespawnOrUnsummon(0);
|
||||
break;
|
||||
|
||||
@@ -77,19 +77,53 @@ enum CreatureIds
|
||||
NPC_INFINITE_WHELP = 21818,
|
||||
NPC_INFINITE_CRONOMANCER = 17892,
|
||||
NPC_INFINITE_EXECUTIONER = 18994,
|
||||
NPC_INFINITE_VANQUISHER = 18995
|
||||
NPC_INFINITE_VANQUISHER = 18995,
|
||||
|
||||
NPC_DP_EMITTER_STALKER = 18582,
|
||||
NPC_DP_CRYSTAL_STALKER = 18553,
|
||||
NPC_DP_BEAM_STALKER = 18555
|
||||
};
|
||||
|
||||
enum Misc
|
||||
{
|
||||
SPELL_RIFT_CHANNEL = 31387,
|
||||
SPELL_TELEPORT_VISUAL = 7791,
|
||||
|
||||
EVENT_NEXT_PORTAL = 1,
|
||||
EVENT_SUMMON_KEEPER = 2,
|
||||
EVENT_SUMMON_KEEPER_1 = 2,
|
||||
EVENT_SUMMON_KEEPER_2 = 3,
|
||||
EVENT_SUMMON_KEEPER_3 = 4,
|
||||
EVENT_SUMMON_KEEPER_4 = 5,
|
||||
EVENT_WIPE_1 = 6,
|
||||
EVENT_WIPE_2 = 7,
|
||||
EVENT_WIPE_3 = 8,
|
||||
|
||||
ACTION_OUTRO = 1
|
||||
};
|
||||
|
||||
enum medivhSays
|
||||
{
|
||||
SAY_MEDIVH_ENTER = 0,
|
||||
SAY_MEDIVH_DEATH = 5,
|
||||
SAY_MEDIVH_WIN = 6,
|
||||
SAY_MEDIVH_ORCS_ENTER = 7,
|
||||
|
||||
SAY_MEDIVH_ORCS_ANSWER = 0
|
||||
};
|
||||
|
||||
enum medivhSpells
|
||||
{
|
||||
SPELL_MANA_SHIELD = 31635,
|
||||
SPELL_MEDIVH_CHANNEL = 31556,
|
||||
SPELL_BLACK_CRYSTAL = 32563,
|
||||
SPELL_PORTAL_CRYSTALS = 32564,
|
||||
SPELL_BANISH_PURPLE = 32566,
|
||||
SPELL_BANISH_GREEN = 32567,
|
||||
|
||||
SPELL_CORRUPT = 31326,
|
||||
SPELL_CORRUPT_AEONUS = 37853,
|
||||
};
|
||||
|
||||
template <class AI, class T>
|
||||
inline AI* GetTheBlackMorassAI(T* obj)
|
||||
{
|
||||
|
||||
@@ -1061,6 +1061,7 @@ public:
|
||||
{
|
||||
data >> m_auiEncounter[0];
|
||||
data >> m_auiEncounter[1];
|
||||
data >> m_auiEncounter[2];
|
||||
data >> m_auiEncounter[3];
|
||||
data >> m_auiEncounter[4];
|
||||
data >> m_auiEncounter[5];
|
||||
|
||||
152
src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp
Normal file
152
src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "sethekk_halls.h"
|
||||
|
||||
enum Anzu
|
||||
{
|
||||
SAY_ANZU_INTRO1 = 0,
|
||||
SAY_ANZU_INTRO2 = 1,
|
||||
SAY_SUMMON = 2,
|
||||
|
||||
SPELL_PARALYZING_SCREECH = 40184,
|
||||
SPELL_SPELL_BOMB = 40303,
|
||||
SPELL_CYCLONE = 40321,
|
||||
SPELL_BANISH_SELF = 42354,
|
||||
SPELL_SHADOWFORM = 40973,
|
||||
|
||||
EVENT_SPELL_SCREECH = 1,
|
||||
EVENT_SPELL_BOMB = 2,
|
||||
EVENT_SPELL_CYCLONE = 3,
|
||||
EVENT_ANZU_HEALTH1 = 4,
|
||||
EVENT_ANZU_HEALTH2 = 5
|
||||
};
|
||||
|
||||
struct boss_anzu : public BossAI
|
||||
{
|
||||
boss_anzu(Creature* creature) : BossAI(creature, DATA_ANZU)
|
||||
{
|
||||
talkTimer = 1;
|
||||
me->ReplaceAllUnitFlags(UNIT_FLAG_NON_ATTACKABLE);
|
||||
me->AddAura(SPELL_SHADOWFORM, me);
|
||||
}
|
||||
|
||||
uint32 talkTimer;
|
||||
|
||||
void SummonedCreatureDies(Creature* summon, Unit*) override
|
||||
{
|
||||
summons.Despawn(summon);
|
||||
summons.RemoveNotExisting();
|
||||
if (summons.empty())
|
||||
me->RemoveAurasDueToSpell(SPELL_BANISH_SELF);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
_JustEngagedWith();
|
||||
events.Reset();
|
||||
events.ScheduleEvent(EVENT_SPELL_SCREECH, 14000);
|
||||
events.ScheduleEvent(EVENT_SPELL_BOMB, 5000);
|
||||
events.ScheduleEvent(EVENT_SPELL_CYCLONE, 8000);
|
||||
events.ScheduleEvent(EVENT_ANZU_HEALTH1, 2000);
|
||||
events.ScheduleEvent(EVENT_ANZU_HEALTH2, 2001);
|
||||
}
|
||||
|
||||
void SummonBroods()
|
||||
{
|
||||
Talk(SAY_SUMMON);
|
||||
me->CastSpell(me, SPELL_BANISH_SELF, true);
|
||||
for (uint8 i = 0; i < 5; ++i)
|
||||
me->SummonCreature(23132 /*NPC_BROOD_OF_ANZU*/, me->GetPositionX() + 20 * cos((float)i), me->GetPositionY() + 20 * std::sin((float)i), me->GetPositionZ() + 25.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (talkTimer)
|
||||
{
|
||||
talkTimer += diff;
|
||||
if (talkTimer >= 1000 && talkTimer < 10000)
|
||||
{
|
||||
Talk(SAY_ANZU_INTRO1);
|
||||
talkTimer = 10000;
|
||||
}
|
||||
else if (talkTimer >= 16000)
|
||||
{
|
||||
me->ReplaceAllUnitFlags(UNIT_FLAG_NONE);
|
||||
me->RemoveAurasDueToSpell(SPELL_SHADOWFORM);
|
||||
Talk(SAY_ANZU_INTRO2);
|
||||
talkTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED))
|
||||
return;
|
||||
|
||||
switch (events.ExecuteEvent())
|
||||
{
|
||||
case EVENT_SPELL_SCREECH:
|
||||
me->CastSpell(me, SPELL_PARALYZING_SCREECH, false);
|
||||
events.RepeatEvent(23000);
|
||||
events.DelayEvents(3000);
|
||||
break;
|
||||
case EVENT_SPELL_BOMB:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
|
||||
me->CastSpell(target, SPELL_SPELL_BOMB, false);
|
||||
events.RepeatEvent(urand(16000, 24500));
|
||||
events.DelayEvents(3000);
|
||||
break;
|
||||
case EVENT_SPELL_CYCLONE:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 45.0f, true))
|
||||
me->CastSpell(target, SPELL_CYCLONE, false);
|
||||
events.RepeatEvent(urand(22000, 27000));
|
||||
events.DelayEvents(3000);
|
||||
break;
|
||||
case EVENT_ANZU_HEALTH1:
|
||||
if (me->HealthBelowPct(66))
|
||||
{
|
||||
SummonBroods();
|
||||
events.DelayEvents(10000);
|
||||
return;
|
||||
}
|
||||
events.RepeatEvent(1000);
|
||||
break;
|
||||
case EVENT_ANZU_HEALTH2:
|
||||
if (me->HealthBelowPct(33))
|
||||
{
|
||||
SummonBroods();
|
||||
events.DelayEvents(10000);
|
||||
return;
|
||||
}
|
||||
events.RepeatEvent(1000);
|
||||
break;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_anzu()
|
||||
{
|
||||
RegisterSethekkHallsCreatureAI(boss_anzu);
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "sethekk_halls.h"
|
||||
|
||||
enum TailonkingIkiss
|
||||
@@ -26,308 +27,150 @@ enum TailonkingIkiss
|
||||
SAY_SLAY = 2,
|
||||
SAY_DEATH = 3,
|
||||
EMOTE_ARCANE_EXP = 4,
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_BLINK = 38194,
|
||||
SPELL_BLINK_TELEPORT = 38203,
|
||||
SPELL_MANA_SHIELD = 38151,
|
||||
SPELL_ARCANE_BUBBLE = 9438,
|
||||
SPELL_SLOW = 35032,
|
||||
SPELL_POLYMORPH_N = 38245, // Difficulty data
|
||||
SPELL_POLYMORPH_H = 43309,
|
||||
SPELL_ARCANE_VOLLEY_N = 35059, // Difficulty data
|
||||
SPELL_ARCANE_VOLLEY_H = 40424,
|
||||
SPELL_ARCANE_EXPLOSION_N = 38197, // Difficulty data
|
||||
SPELL_ARCANE_EXPLOSION_H = 40425,
|
||||
|
||||
EVENT_SPELL_BLINK = 1,
|
||||
EVENT_SPELL_POLYMORPH = 2,
|
||||
EVENT_SPELL_SLOW = 3,
|
||||
EVENT_SPELL_ARCANE_VOLLEY = 4,
|
||||
EVENT_SPELL_ARCANE_EXPLO = 5,
|
||||
EVENT_HEALTH_CHECK = 6,
|
||||
EVENT_SPELL_BLINK_2 = 7
|
||||
SPELL_POLYMORPH = 38245,
|
||||
SPELL_ARCANE_VOLLEY = 35059,
|
||||
SPELL_ARCANE_EXPLOSION = 38197,
|
||||
};
|
||||
|
||||
class boss_talon_king_ikiss : public CreatureScript
|
||||
struct boss_talon_king_ikiss : public BossAI
|
||||
{
|
||||
public:
|
||||
boss_talon_king_ikiss() : CreatureScript("boss_talon_king_ikiss") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
boss_talon_king_ikiss(Creature* creature) : BossAI(creature, DATA_IKISS), _spoken(false), _manaShield(false)
|
||||
{
|
||||
return GetSethekkHallsAI<boss_talon_king_ikissAI>(creature);
|
||||
scheduler.SetValidator([this]
|
||||
{
|
||||
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||
});
|
||||
}
|
||||
|
||||
struct boss_talon_king_ikissAI : public ScriptedAI
|
||||
void Reset() override
|
||||
{
|
||||
boss_talon_king_ikissAI(Creature* creature) : ScriptedAI(creature)
|
||||
_Reset();
|
||||
_spoken = false;
|
||||
_manaShield = false;
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (!_spoken && who->IsPlayer())
|
||||
{
|
||||
Talk(SAY_INTRO);
|
||||
_spoken = true;
|
||||
}
|
||||
|
||||
EventMap events;
|
||||
ScriptedAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
_JustEngagedWith();
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
scheduler.Schedule(35s, [this](TaskContext context)
|
||||
{
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
DoCastAOE(SPELL_BLINK);
|
||||
Talk(EMOTE_ARCANE_EXP);
|
||||
context.Repeat(35s, 40s);
|
||||
|
||||
scheduler.Schedule(1s, [this](TaskContext)
|
||||
{
|
||||
DoCastAOE(SPELL_ARCANE_EXPLOSION);
|
||||
DoCastSelf(SPELL_ARCANE_BUBBLE, true);
|
||||
});
|
||||
}).Schedule(5s, [this](TaskContext context)
|
||||
{
|
||||
DoCastAOE(SPELL_ARCANE_VOLLEY);
|
||||
context.Repeat(7s, 12s);
|
||||
}).Schedule(8s, [this](TaskContext context)
|
||||
{
|
||||
IsHeroic() ? DoCastRandomTarget(SPELL_POLYMORPH) : DoCastMaxThreat(SPELL_POLYMORPH);
|
||||
context.Repeat(15s, 17500ms);
|
||||
});
|
||||
|
||||
if (IsHeroic())
|
||||
{
|
||||
scheduler.Schedule(15s, 25s, [this](TaskContext context)
|
||||
{
|
||||
DoCastAOE(SPELL_SLOW);
|
||||
context.Repeat(15s, 30s);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
_JustDied();
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
if (GameObject* coffer = instance->GetGameObject(DATA_GO_TALON_KING_COFFER))
|
||||
{
|
||||
coffer->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE | GO_FLAG_INTERACT_COND);
|
||||
}
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
|
||||
{
|
||||
if (!_manaShield && me->HealthBelowPctDamaged(20, damage))
|
||||
{
|
||||
DoCast(me, SPELL_MANA_SHIELD);
|
||||
_manaShield = true;
|
||||
}
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
if (urand(0, 1))
|
||||
Talk(SAY_SLAY);
|
||||
}
|
||||
|
||||
private:
|
||||
bool _spoken;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_spoken = false;
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (!_spoken && who->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Talk(SAY_INTRO);
|
||||
_spoken = true;
|
||||
}
|
||||
|
||||
ScriptedAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
events.ScheduleEvent(EVENT_SPELL_BLINK, 35000);
|
||||
events.ScheduleEvent(EVENT_SPELL_ARCANE_VOLLEY, 5000);
|
||||
events.ScheduleEvent(EVENT_SPELL_POLYMORPH, 8000);
|
||||
events.ScheduleEvent(EVENT_HEALTH_CHECK, 2000);
|
||||
if (IsHeroic())
|
||||
events.ScheduleEvent(EVENT_SPELL_SLOW, urand(15000, 25000));
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
instance->SetData(DATA_IKISSDOOREVENT, DONE);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
if (urand(0, 1))
|
||||
Talk(SAY_SLAY);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
switch (events.ExecuteEvent())
|
||||
{
|
||||
case EVENT_SPELL_ARCANE_VOLLEY:
|
||||
me->CastSpell(me, SPELL_ARCANE_VOLLEY_N, false);
|
||||
events.RepeatEvent(urand(7000, 12000));
|
||||
break;
|
||||
case EVENT_SPELL_POLYMORPH:
|
||||
if (Unit* target = (IsHeroic() ? SelectTarget(SelectTargetMethod::Random, 0) : SelectTarget(SelectTargetMethod::MaxThreat, 1)))
|
||||
me->CastSpell(target, SPELL_POLYMORPH_N, false);
|
||||
events.RepeatEvent(urand(15000, 17500));
|
||||
break;
|
||||
case EVENT_SPELL_SLOW:
|
||||
me->CastSpell(me, SPELL_SLOW, false);
|
||||
events.RepeatEvent(urand(15000, 30000));
|
||||
break;
|
||||
case EVENT_HEALTH_CHECK:
|
||||
if (me->HealthBelowPct(20))
|
||||
{
|
||||
me->CastSpell(me, SPELL_MANA_SHIELD, false);
|
||||
return;
|
||||
}
|
||||
events.RepeatEvent(1000);
|
||||
break;
|
||||
case EVENT_SPELL_BLINK:
|
||||
Talk(EMOTE_ARCANE_EXP);
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
me->CastSpell(target, SPELL_BLINK, false);
|
||||
me->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation());
|
||||
|
||||
DoCast(target, SPELL_BLINK_TELEPORT);
|
||||
}
|
||||
events.RepeatEvent(urand(35000, 40000));
|
||||
events.DelayEvents(500);
|
||||
events.ScheduleEvent(EVENT_SPELL_BLINK_2, 0);
|
||||
return;
|
||||
case EVENT_SPELL_BLINK_2:
|
||||
me->CastSpell(me, SPELL_ARCANE_EXPLOSION_N, false);
|
||||
me->CastSpell(me, SPELL_ARCANE_BUBBLE, true);
|
||||
break;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
bool _manaShield;
|
||||
};
|
||||
|
||||
enum Anzu
|
||||
// 38194 - Blink
|
||||
class spell_talon_king_ikiss_blink : public SpellScript
|
||||
{
|
||||
SAY_ANZU_INTRO1 = 0,
|
||||
SAY_ANZU_INTRO2 = 1,
|
||||
SAY_SUMMON = 2,
|
||||
PrepareSpellScript(spell_talon_king_ikiss_blink);
|
||||
|
||||
SPELL_PARALYZING_SCREECH = 40184,
|
||||
SPELL_SPELL_BOMB = 40303,
|
||||
SPELL_CYCLONE = 40321,
|
||||
SPELL_BANISH_SELF = 42354,
|
||||
SPELL_SHADOWFORM = 40973,
|
||||
|
||||
EVENT_SPELL_SCREECH = 1,
|
||||
EVENT_SPELL_BOMB = 2,
|
||||
EVENT_SPELL_CYCLONE = 3,
|
||||
EVENT_ANZU_HEALTH1 = 4,
|
||||
EVENT_ANZU_HEALTH2 = 5
|
||||
};
|
||||
|
||||
class boss_anzu : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_anzu() : CreatureScript("boss_anzu") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return GetSethekkHallsAI<boss_anzuAI>(creature);
|
||||
return sSpellMgr->GetSpellInfo(SPELL_BLINK);
|
||||
}
|
||||
|
||||
struct boss_anzuAI : public ScriptedAI
|
||||
void FilterTargets(std::list<WorldObject*>& targets)
|
||||
{
|
||||
boss_anzuAI(Creature* creature) : ScriptedAI(creature), summons(me)
|
||||
uint8 maxSize = 1;
|
||||
if (targets.size() > maxSize)
|
||||
{
|
||||
talkTimer = 1;
|
||||
me->ReplaceAllUnitFlags(UNIT_FLAG_NON_ATTACKABLE);
|
||||
me->AddAura(SPELL_SHADOWFORM, me);
|
||||
Acore::Containers::RandomResize(targets, maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
uint32 talkTimer;
|
||||
void HandleDummyHitTarget(SpellEffIndex effIndex)
|
||||
{
|
||||
PreventHitDefaultEffect(effIndex);
|
||||
GetHitUnit()->CastSpell(GetCaster(), SPELL_BLINK_TELEPORT, true);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
summons.DespawnAll();
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
if (instance->GetData(TYPE_ANZU_ENCOUNTER) != DONE)
|
||||
instance->SetData(TYPE_ANZU_ENCOUNTER, NOT_STARTED);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
summons.Summon(summon);
|
||||
summon->AI()->AttackStart(me->GetVictim());
|
||||
}
|
||||
|
||||
void SummonedCreatureDies(Creature* summon, Unit*) override
|
||||
{
|
||||
summons.Despawn(summon);
|
||||
summons.RemoveNotExisting();
|
||||
if (summons.empty())
|
||||
me->RemoveAurasDueToSpell(SPELL_BANISH_SELF);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
events.Reset();
|
||||
events.ScheduleEvent(EVENT_SPELL_SCREECH, 14000);
|
||||
events.ScheduleEvent(EVENT_SPELL_BOMB, 5000);
|
||||
events.ScheduleEvent(EVENT_SPELL_CYCLONE, 8000);
|
||||
events.ScheduleEvent(EVENT_ANZU_HEALTH1, 2000);
|
||||
events.ScheduleEvent(EVENT_ANZU_HEALTH2, 2001);
|
||||
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
instance->SetData(TYPE_ANZU_ENCOUNTER, IN_PROGRESS);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
instance->SetData(TYPE_ANZU_ENCOUNTER, DONE);
|
||||
}
|
||||
|
||||
void SummonBroods()
|
||||
{
|
||||
Talk(SAY_SUMMON);
|
||||
me->CastSpell(me, SPELL_BANISH_SELF, true);
|
||||
for (uint8 i = 0; i < 5; ++i)
|
||||
me->SummonCreature(23132 /*NPC_BROOD_OF_ANZU*/, me->GetPositionX() + 20 * cos((float)i), me->GetPositionY() + 20 * std::sin((float)i), me->GetPositionZ() + 25.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (talkTimer)
|
||||
{
|
||||
talkTimer += diff;
|
||||
if (talkTimer >= 1000 && talkTimer < 10000)
|
||||
{
|
||||
Talk(SAY_ANZU_INTRO1);
|
||||
talkTimer = 10000;
|
||||
}
|
||||
else if (talkTimer >= 16000)
|
||||
{
|
||||
me->ReplaceAllUnitFlags(UNIT_FLAG_NONE);
|
||||
me->RemoveAurasDueToSpell(SPELL_SHADOWFORM);
|
||||
Talk(SAY_ANZU_INTRO2);
|
||||
talkTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED))
|
||||
return;
|
||||
|
||||
switch (events.ExecuteEvent())
|
||||
{
|
||||
case EVENT_SPELL_SCREECH:
|
||||
me->CastSpell(me, SPELL_PARALYZING_SCREECH, false);
|
||||
events.RepeatEvent(23000);
|
||||
events.DelayEvents(3000);
|
||||
break;
|
||||
case EVENT_SPELL_BOMB:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
|
||||
me->CastSpell(target, SPELL_SPELL_BOMB, false);
|
||||
events.RepeatEvent(urand(16000, 24500));
|
||||
events.DelayEvents(3000);
|
||||
break;
|
||||
case EVENT_SPELL_CYCLONE:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 45.0f, true))
|
||||
me->CastSpell(target, SPELL_CYCLONE, false);
|
||||
events.RepeatEvent(urand(22000, 27000));
|
||||
events.DelayEvents(3000);
|
||||
break;
|
||||
case EVENT_ANZU_HEALTH1:
|
||||
if (me->HealthBelowPct(66))
|
||||
{
|
||||
SummonBroods();
|
||||
events.DelayEvents(10000);
|
||||
return;
|
||||
}
|
||||
events.RepeatEvent(1000);
|
||||
break;
|
||||
case EVENT_ANZU_HEALTH2:
|
||||
if (me->HealthBelowPct(33))
|
||||
{
|
||||
SummonBroods();
|
||||
events.DelayEvents(10000);
|
||||
return;
|
||||
}
|
||||
events.RepeatEvent(1000);
|
||||
break;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
void Register() override
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_talon_king_ikiss_blink::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
|
||||
OnEffectHitTarget += SpellEffectFn(spell_talon_king_ikiss_blink::HandleDummyHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_talon_king_ikiss()
|
||||
{
|
||||
new boss_talon_king_ikiss();
|
||||
new boss_anzu();
|
||||
RegisterSethekkHallsCreatureAI(boss_talon_king_ikiss);
|
||||
RegisterSpellScript(spell_talon_king_ikiss_blink);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,17 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "sethekk_halls.h"
|
||||
|
||||
DoorData const doorData[] =
|
||||
{
|
||||
{ GO_IKISS_DOOR, DATA_IKISS, DOOR_TYPE_PASSAGE },
|
||||
{ 0, 0, DOOR_TYPE_ROOM } // END
|
||||
};
|
||||
|
||||
ObjectData const gameObjectData[] =
|
||||
{
|
||||
{ GO_THE_TALON_KINGS_COFFER, DATA_GO_TALON_KING_COFFER }
|
||||
};
|
||||
|
||||
class instance_sethekk_halls : public InstanceMapScript
|
||||
{
|
||||
public:
|
||||
@@ -31,91 +42,20 @@ public:
|
||||
|
||||
struct instance_sethekk_halls_InstanceMapScript : public InstanceScript
|
||||
{
|
||||
instance_sethekk_halls_InstanceMapScript(Map* map) : InstanceScript(map) {}
|
||||
|
||||
uint32 AnzuEncounter;
|
||||
ObjectGuid m_uiIkissDoorGUID;
|
||||
ObjectGuid _talonKingsCofferGUID;
|
||||
|
||||
void Initialize() override
|
||||
instance_sethekk_halls_InstanceMapScript(Map* map) : InstanceScript(map)
|
||||
{
|
||||
AnzuEncounter = NOT_STARTED;
|
||||
SetHeaders(DataHeaders);
|
||||
SetBossNumber(EncounterCount);
|
||||
LoadDoorData(doorData);
|
||||
LoadObjectData(nullptr, gameObjectData);
|
||||
}
|
||||
|
||||
void OnCreatureCreate(Creature* creature) override
|
||||
{
|
||||
if (creature->GetEntry() == NPC_ANZU || creature->GetEntry() == NPC_VOICE_OF_THE_RAVEN_GOD)
|
||||
if (AnzuEncounter >= IN_PROGRESS)
|
||||
if (GetBossState(DATA_ANZU) == DONE)
|
||||
creature->DespawnOrUnsummon(1);
|
||||
}
|
||||
|
||||
void OnGameObjectCreate(GameObject* go) override
|
||||
{
|
||||
switch (go->GetEntry())
|
||||
{
|
||||
case GO_IKISS_DOOR:
|
||||
m_uiIkissDoorGUID = go->GetGUID();
|
||||
break;
|
||||
case GO_THE_TALON_KINGS_COFFER:
|
||||
_talonKingsCofferGUID = go->GetGUID();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetData(uint32 type, uint32 data) override
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DATA_IKISSDOOREVENT:
|
||||
if (data == DONE)
|
||||
{
|
||||
DoUseDoorOrButton(m_uiIkissDoorGUID, DAY * IN_MILLISECONDS);
|
||||
if (GameObject* coffer = instance->GetGameObject(_talonKingsCofferGUID))
|
||||
coffer->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE | GO_FLAG_INTERACT_COND);
|
||||
}
|
||||
break;
|
||||
case TYPE_ANZU_ENCOUNTER:
|
||||
AnzuEncounter = data;
|
||||
SaveToDB();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetSaveData() override
|
||||
{
|
||||
OUT_SAVE_INST_DATA;
|
||||
|
||||
std::ostringstream saveStream;
|
||||
saveStream << "S H " << AnzuEncounter;
|
||||
|
||||
OUT_SAVE_INST_DATA_COMPLETE;
|
||||
return saveStream.str();
|
||||
}
|
||||
|
||||
void Load(const char* strIn) override
|
||||
{
|
||||
if (!strIn)
|
||||
{
|
||||
OUT_LOAD_INST_DATA_FAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
OUT_LOAD_INST_DATA(strIn);
|
||||
|
||||
char dataHead1, dataHead2;
|
||||
|
||||
std::istringstream loadStream(strIn);
|
||||
loadStream >> dataHead1 >> dataHead2;
|
||||
|
||||
if (dataHead1 == 'S' && dataHead2 == 'H')
|
||||
{
|
||||
loadStream >> AnzuEncounter;
|
||||
if (AnzuEncounter == IN_PROGRESS)
|
||||
AnzuEncounter = NOT_STARTED;
|
||||
}
|
||||
|
||||
OUT_LOAD_INST_DATA_COMPLETE;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -20,14 +20,30 @@
|
||||
|
||||
#include "CreatureAIImpl.h"
|
||||
|
||||
#define DataHeaders "SH"
|
||||
|
||||
uint32 constexpr EncounterCount = 3;
|
||||
|
||||
#define SethekkHallsScriptName "instance_sethekk_halls"
|
||||
|
||||
enum Encounters
|
||||
{
|
||||
DATA_DARKWEAVER_SYTH,
|
||||
DATA_ANZU,
|
||||
DATA_IKISS,
|
||||
};
|
||||
|
||||
enum eTypes
|
||||
{
|
||||
DATA_IKISSDOOREVENT = 1,
|
||||
TYPE_ANZU_ENCOUNTER = 2,
|
||||
};
|
||||
|
||||
enum InstanceData
|
||||
{
|
||||
DATA_GO_TALON_KING_COFFER
|
||||
};
|
||||
|
||||
enum eIds
|
||||
{
|
||||
NPC_VOICE_OF_THE_RAVEN_GOD = 21851,
|
||||
@@ -43,4 +59,6 @@ inline AI* GetSethekkHallsAI(T* obj)
|
||||
return GetInstanceAI<AI>(obj, SethekkHallsScriptName);
|
||||
}
|
||||
|
||||
#define RegisterSethekkHallsCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetSethekkHallsAI)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
{
|
||||
SetHeaders(DataHeader);
|
||||
SetBossNumber(MAX_ENCOUNTER);
|
||||
SetPersistentDataCount(MAX_DATA_INDEXES);
|
||||
LoadDoorData(doorData);
|
||||
|
||||
_passageEncounter = 0;
|
||||
@@ -149,7 +150,8 @@ public:
|
||||
DoSummonAction(creature, player);
|
||||
}
|
||||
}
|
||||
_passageEncounter++;
|
||||
|
||||
StorePersistentData(DATA_INDEX_PASSAGE_ENCOUNTER, _passageEncounter++);
|
||||
SaveToDB();
|
||||
}
|
||||
}
|
||||
@@ -186,16 +188,17 @@ public:
|
||||
if (Creature* creature = instance->GetCreature(_pathaleonGUID))
|
||||
creature->AI()->DoAction(1);
|
||||
}
|
||||
_passageEncounter++;
|
||||
|
||||
StorePersistentData(DATA_INDEX_PASSAGE_ENCOUNTER, _passageEncounter++);
|
||||
SaveToDB();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadSaveDataMore(std::istringstream& data) override
|
||||
void ReadSaveDataMore(std::istringstream& /*data*/) override
|
||||
{
|
||||
data >> _passageEncounter;
|
||||
_passageEncounter = GetPersistentData(DATA_INDEX_PASSAGE_ENCOUNTER);
|
||||
|
||||
if (_passageEncounter == ENCOUNTER_PASSAGE_DONE)
|
||||
{
|
||||
@@ -203,11 +206,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void WriteSaveDataMore(std::ostringstream& data) override
|
||||
{
|
||||
data << _passageEncounter;
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectGuid _pathaleonGUID;
|
||||
uint32 _passageTimer;
|
||||
|
||||
@@ -70,6 +70,12 @@ enum SpellIds
|
||||
SPELL_TELEPORT_VISUAL = 35517
|
||||
};
|
||||
|
||||
enum DataIndex
|
||||
{
|
||||
DATA_INDEX_PASSAGE_ENCOUNTER = 0,
|
||||
MAX_DATA_INDEXES
|
||||
};
|
||||
|
||||
template <class AI, class T>
|
||||
inline AI* GetMechanarAI(T* obj)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ void AddSC_boss_nexusprince_shaffar(); //Auchindoun Mana Tombs
|
||||
void AddSC_boss_pandemonius();
|
||||
void AddSC_instance_mana_tombs();
|
||||
void AddSC_boss_talon_king_ikiss(); //Auchindoun Sekketh Halls
|
||||
void AddSC_boss_anzu();
|
||||
void AddSC_instance_sethekk_halls();
|
||||
void AddSC_instance_shadow_labyrinth(); //Auchindoun Shadow Labyrinth
|
||||
void AddSC_boss_ambassador_hellmaw();
|
||||
@@ -119,6 +120,7 @@ void AddOutlandScripts()
|
||||
AddSC_boss_pandemonius();
|
||||
AddSC_instance_mana_tombs();
|
||||
AddSC_boss_talon_king_ikiss(); //Auchindoun Sekketh Halls
|
||||
AddSC_boss_anzu();
|
||||
AddSC_instance_sethekk_halls();
|
||||
AddSC_instance_shadow_labyrinth(); //Auchindoun Shadow Labyrinth
|
||||
AddSC_boss_ambassador_hellmaw();
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
/* ScriptData
|
||||
SDName: Shadowmoon_Valley
|
||||
SD%Complete: 100
|
||||
SDComment: Quest support: 10519, 10583, 10601, 10804, 10854, 10458, 10481, 10480, 10781, 10451. Vendor Drake Dealer Hurlunk.
|
||||
SDComment: Quest support: 10519, 10583, 10601, 10804, 10854, 10458, 10481, 10480, 10781. Vendor Drake Dealer Hurlunk.
|
||||
SDCategory: Shadowmoon Valley
|
||||
EndScriptData */
|
||||
|
||||
@@ -29,7 +29,6 @@ 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
|
||||
@@ -811,176 +810,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/*####
|
||||
# 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
|
||||
};
|
||||
|
||||
class npc_earthmender_wilda : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_earthmender_wilda() : CreatureScript("npc_earthmender_wilda") { }
|
||||
|
||||
bool OnQuestAccept(Player* player, Creature* creature, const Quest* quest) override
|
||||
{
|
||||
if (quest->GetQuestId() == QUEST_ESCAPE_COILSCAR)
|
||||
{
|
||||
creature->AI()->Talk(SAY_WIL_START, player);
|
||||
creature->SetFaction(FACTION_EARTHEN_RING); //guessed
|
||||
|
||||
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 override
|
||||
{
|
||||
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() override
|
||||
{
|
||||
m_uiHealingTimer = 0;
|
||||
}
|
||||
|
||||
void WaypointReached(uint32 waypointId) override
|
||||
{
|
||||
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) override
|
||||
{
|
||||
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 JustEngagedWith(Unit* who) override
|
||||
{
|
||||
//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) override
|
||||
{
|
||||
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
|
||||
#####*/
|
||||
@@ -1821,7 +1650,6 @@ void AddSC_shadowmoon_valley()
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user