fix(Scripts/Karazhan): Rework Prince Malchezaar (#14877)

This commit is contained in:
Skjalf
2023-02-27 01:38:02 -03:00
committed by GitHub
parent 7538a59435
commit e5c0c7227a
2 changed files with 239 additions and 397 deletions
@@ -0,0 +1,4 @@
--
UPDATE `creature_template` SET `ScriptName` = 'npc_malchezaar_axe', `unit_flags` = `unit_flags`|33554432 WHERE `entry` = 17650;
UPDATE `creature_template` SET `ScriptName` = 'npc_netherspite_infernal' WHERE `entry` = 17646;
UPDATE `creature_template` SET `flags_extra` = `flags_extra` |2147483648 WHERE `entry` = 15690;
@@ -32,28 +32,42 @@ enum PrinceSay
enum Spells enum Spells
{ {
SPELL_ENFEEBLE = 30843, //Enfeeble during phase 1 and 2 SPELL_ENFEEBLE = 30843,
SPELL_ENFEEBLE_EFFECT = 41624, SPELL_ENFEEBLE_EFFECT = 41624,
SPELL_SHADOWNOVA = 30852, //Shadownova used during all phases SPELL_SHADOW_NOVA = 30852,
SPELL_SW_PAIN = 30854, //Shadow word pain during phase 1 and 3 (different targeting rules though) SPELL_SHADOW_WORD_PAIN = 30854,
SPELL_THRASH_PASSIVE = 12787, //Extra attack chance during phase 2 SPELL_THRASH_PASSIVE = 12787,
SPELL_SUNDER_ARMOR = 30901, //Sunder armor during phase 2 SPELL_SUNDER_ARMOR = 30901,
SPELL_THRASH_AURA = 12787, //Passive proc chance for thrash SPELL_THRASH_AURA = 12787,
SPELL_EQUIP_AXES = 30857, //Visual for axe equiping SPELL_EQUIP_AXES = 30857,
SPELL_AMPLIFY_DAMAGE = 39095, //Amplifiy during phase 3 SPELL_AMPLIFY_DAMAGE = 39095,
SPELL_CLEAVE = 30131, //Same as Nightbane. SPELL_CLEAVE = 30131,
SPELL_HELLFIRE = 30859, //Infenals' hellfire aura SPELL_HELLFIRE = 30859,
}; };
enum creatures enum creatures
{ {
NETHERSPITE_INFERNAL = 17646, NPC_NETHERSPITE_INFERNAL = 17646,
MALCHEZARS_AXE = 17650, NPC_MALCHEZARS_AXE = 17650,
INFERNAL_MODEL_INVISIBLE = 11686, INFERNAL_MODEL_INVISIBLE = 11686,
SPELL_INFERNAL_RELAY = 33814, // 30835, SPELL_INFERNAL_RELAY = 33814, // 30835,
EQUIP_ID_AXE = 33542 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 struct InfernalPoint
{ {
float x, y; float x, y;
@@ -83,125 +97,13 @@ struct InfernalPoint
{ -10935.7f, -1996.0f } { -10935.7f, -1996.0f }
};*/ };*/
//---------Infernal code first struct boss_malchezaar : public BossAI
class netherspite_infernal : public CreatureScript
{ {
public: boss_malchezaar(Creature* creature) : BossAI(creature, DATA_MALCHEZZAR) { }
netherspite_infernal() : CreatureScript("netherspite_infernal") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new netherspite_infernalAI(creature);
}
struct netherspite_infernalAI : public ScriptedAI
{
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);
}
struct boss_malchezaarAI : public ScriptedAI
{
boss_malchezaarAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
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() void Initialize()
{ {
EnfeebleTimer = 30000; _phase = 1;
EnfeebleResetTimer = 38000;
ShadowNovaTimer = 35500;
SWPainTimer = 20000;
InfernalCleanupTimer = 47000;
AmplifyDamageTimer = 5000;
SunderArmorTimer = urand(5000, 10000);
InfernalTimer = 40000;
phase = 1;
clearweapons(); clearweapons();
positions.clear(); positions.clear();
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true); instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
@@ -216,6 +118,7 @@ public:
void Reset() override void Reset() override
{ {
Initialize(); Initialize();
_Reset();
} }
void KilledUnit(Unit* /*victim*/) override void KilledUnit(Unit* /*victim*/) override
@@ -225,274 +128,211 @@ public:
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
_JustDied();
Talk(SAY_DEATH); Talk(SAY_DEATH);
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true); instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
if (Creature* Axe = me->FindNearestCreature(MALCHEZARS_AXE, 100.0f))
{
Axe->DespawnOrUnsummon();
}
} }
void JustEngagedWith(Unit* /*who*/) override void JustEngagedWith(Unit* /*who*/) override
{ {
Talk(SAY_AGGRO); Talk(SAY_AGGRO);
DoZoneInCombat(); _JustEngagedWith();
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), false); instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), false);
scheduler.Schedule(30s, [this](TaskContext context)
{
EnfeebleHealthEffect();
scheduler.Schedule(9s, [this](TaskContext)
{
EnfeebleResetHealth();
});
context.SetGroup(GROUP_ENFEEBLE);
scheduler.DelayGroup(GROUP_SHADOW_NOVA, 5s);
context.Repeat();
}).Schedule(35500ms, [this](TaskContext context)
{
DoCastAOE(SPELL_SHADOW_NOVA);
context.SetGroup(GROUP_SHADOW_NOVA);
context.Repeat();
}).Schedule(40s, [this](TaskContext context)
{
Position pos = me->GetRandomNearPosition(40.0);;
if (Creature* RELAY = me->FindNearestCreature(NPC_RELAY, 100.0f))
{
if (Creature* infernal = RELAY->SummonCreature(NPC_NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000))
{
infernal->SetDisplayId(INFERNAL_MODEL_INVISIBLE);
infernal->SetFaction(me->GetFaction());
infernal->SetControlled(true, UNIT_STATE_ROOT);
RELAY->AI()->DoCast(infernal, SPELL_INFERNAL_RELAY);
summons.Summon(infernal);
}
} }
void SummonAxes() context.Repeat(_phase == PHASE_THREE ? 15s : 45s);
Talk(SAY_SUMMON);
}).Schedule(20s, [this](TaskContext context)
{ {
me->SummonCreature(MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000); 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;
DoCastSelf( SPELL_EQUIP_AXES);
Talk(SAY_AXE_TOSS1);
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);
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 = PHASE_THREE;
clearweapons();
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 EnfeebleHealthEffect() void EnfeebleHealthEffect()
{ {
SpellInfo const* info = sSpellMgr->GetSpellInfo(SPELL_ENFEEBLE_EFFECT); std::list<Unit*> targetList;
if (!info) SelectTargetList(targetList, 5, SelectTargetMethod::Random, 1, [&](Unit* u) { return u->IsAlive() && u->IsPlayer(); });
if (targetList.empty())
return; return;
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); for (auto const& target : targetList)
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(); if (target)
enfeeble_health[i] = target->GetHealth(); {
_enfeebleTargets[target->GetGUID()] = target->GetHealth();
me->CastSpell(target, SPELL_ENFEEBLE, true, 0, 0, me->GetGUID()); me->CastSpell(target, SPELL_ENFEEBLE, true);
target->SetHealth(1); target->SetHealth(1);
} }
} }
}
void EnfeebleResetHealth() void EnfeebleResetHealth()
{ {
for (uint8 i = 0; i < 5; ++i) for (auto targets : _enfeebleTargets)
{ {
Unit* target = ObjectAccessor::GetUnit(*me, enfeeble_targets[i]); if (Unit* target = ObjectAccessor::GetUnit(*me, targets.first))
if (target && target->IsAlive()) {
target->SetHealth(enfeeble_health[i]); if (target->IsAlive())
enfeeble_targets[i].Clear(); {
enfeeble_health[i] = 0; target->SetHealth(targets.second);
}
}
} }
} }
void SummonInfernal() private:
{ uint32 _phase;
Position pos; std::map<ObjectGuid, uint32> _enfeebleTargets;
std::vector<InfernalPoint*> positions;
};
if ((me->GetMapId() == 532)) struct npc_netherspite_infernal : public ScriptedAI
{ {
pos = me->GetRandomNearPosition(40.0); npc_netherspite_infernal(Creature* creature) : ScriptedAI(creature) { }
}
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)) void JustEngagedWith(Unit* /*who*/) override { }
{ void MoveInLineOfSight(Unit* /*who*/) override { }
Creature* infernal = RELAY->SummonCreature(NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000);
if (infernal)
{
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);
}
}
Talk(SAY_SUMMON);
}
void Phase2()
{
me->InterruptNonMeleeSpells(false);
phase = 2;
DoCast(me, SPELL_EQUIP_AXES);
Talk(SAY_AXE_TOSS1);
DoCast(me, 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()
{
me->RemoveAurasDueToSpell(SPELL_THRASH_AURA);
Talk(SAY_AXE_TOSS2);
phase = 3;
clearweapons();
SummonAxes();
AmplifyDamageTimer = urand(20000, 30000);
}
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (!UpdateVictim()) _scheduler.Update(diff);
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; void KilledUnit(Unit* who) override
}
else
SWPainTimer -= diff;
}
if (ShadowNovaTimer <= diff)
{ {
DoCast(SPELL_SHADOWNOVA); if (me->ToTempSummon())
ShadowNovaTimer = 35500; {
if (WorldObject* summoner = me->ToTempSummon()->GetSummoner())
{
if (Creature* creature = summoner->ToCreature())
{
creature->AI()->KilledUnit(who);
}
} }
else
ShadowNovaTimer -= diff;
if (phase == 1)
{
if (HealthBelowPct(60))
{
Phase2();
} }
} }
if (phase == 2) void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
{ {
if (SunderArmorTimer <= diff) if (spell->Id == SPELL_INFERNAL_RELAY)
{ {
DoCast(SPELL_SUNDER_ARMOR); me->SetDisplayId(me->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID));
SunderArmorTimer = urand(5000, 10000); me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
else
SunderArmorTimer -= diff;
if (HealthBelowPct(30)) _scheduler.Schedule(4s, [this](TaskContext /*context*/)
{ {
Phase3(); DoCastSelf(SPELL_HELLFIRE);
});
} }
} }
if (phase == 3) void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
{ {
if (AmplifyDamageTimer <= diff) damage = 0;
{
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) private:
{ TaskScheduler _scheduler;
if (EnfeebleTimer <= diff)
{
EnfeebleHealthEffect();
EnfeebleTimer = 30000;
ShadowNovaTimer = 5000;
EnfeebleResetTimer = 9000;
}
else EnfeebleTimer -= diff;
}
DoMeleeAttackIfReady();
}
};
}; };
class prince_axes : public CreatureScript struct npc_malchezaar_axe : public ScriptedAI
{ {
public: npc_malchezaar_axe(Creature* creature) : ScriptedAI(creature)
prince_axes() : CreatureScript("prince_axes") { }
CreatureAI* GetAI(Creature* creature) const override
{ {
return GetKarazhanAI<prince_axesAI>(creature); creature->SetCanDualWield(true);
} }
struct prince_axesAI : public ScriptedAI
{
prince_axesAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
instance = creature->GetInstanceScript();
}
uint32 AxesTargetSwitchTimer;
InstanceScript* instance;
void Initialize() void Initialize()
{ {
AxesTargetSwitchTimer = 7500;
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetCanDualWield(true);
}
void Reset() override
{
} }
void JustEngagedWith(Unit* /*who*/) override void JustEngagedWith(Unit* /*who*/) override
{ {
DoZoneInCombat(); DoZoneInCombat();
} _scheduler.Schedule(7500ms, [this](TaskContext context)
void changetarget()
{ {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{ {
if (me->GetVictim()) if (me->GetVictim())
{ {
@@ -501,6 +341,9 @@ public:
me->AddThreat(target, 1000000.0f); me->AddThreat(target, 1000000.0f);
} }
context.Repeat(7500ms, 20s);
});
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
@@ -508,22 +351,17 @@ public:
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
if (AxesTargetSwitchTimer <= diff) _scheduler.Update(diff,
{ std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
AxesTargetSwitchTimer = urand(7500, 20000);
changetarget();
} }
else
AxesTargetSwitchTimer -= diff;
DoMeleeAttackIfReady(); private:
} TaskScheduler _scheduler;
};
}; };
void AddSC_boss_malchezaar() void AddSC_boss_malchezaar()
{ {
new boss_malchezaar(); RegisterKarazhanCreatureAI(boss_malchezaar);
new prince_axes(); RegisterKarazhanCreatureAI(npc_malchezaar_axe);
new netherspite_infernal(); RegisterKarazhanCreatureAI(npc_netherspite_infernal);
} }