Merge branch 'master' into Playerbot

# Conflicts:
#	src/server/game/World/IWorld.h
#	src/server/game/World/World.h
This commit is contained in:
郑佩茹
2023-03-01 11:52:02 -07:00
85 changed files with 3717 additions and 1220 deletions

View File

@@ -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...");

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
};
};

View File

@@ -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:
{

View File

@@ -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;
};
};

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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];

View 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);
}

View File

@@ -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);
}

View File

@@ -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;
}
};
};

View File

@@ -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

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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();

View File

@@ -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();