refactor(Scripts/AI) ZG20: Venoxis (Snake Boss) modernization (#17699)

* npc_razzashi_cobra_venoxis: modernize

* ZG/Venoxis : npc_razzashi_cobra_venoxis

* ZG20/Venoxis - modernize AI

* Reimplement cobras with SAI and DB edits
This commit is contained in:
KJack
2023-11-16 15:42:11 -05:00
committed by GitHub
parent d1e26997ca
commit 05e530317a
2 changed files with 133 additions and 280 deletions

View File

@@ -33,43 +33,25 @@ enum Says
enum Spells
{
// troll form
// High Priest Venoxis (14507)
// All Phases
SPELL_THRASH = 3391,
// Phase 1 - Troll Form
SPELL_DISPEL_MAGIC = 23859,
SPELL_RENEW = 23895,
SPELL_HOLY_NOVA = 23858,
SPELL_HOLY_FIRE = 23860,
SPELL_HOLY_WRATH = 23979,
// snake form
// Transform
SPELL_VENOXIS_TRANSFORM = 23849,
// Phase 2 - Snake Form
SPELL_POISON_CLOUD = 23861,
SPELL_VENOM_SPIT = 23862,
SPELL_PARASITIC_SERPENT = 23865,
SPELL_SUMMON_PARASITIC_SERPENT = 23866,
SPELL_PARASITIC_SERPENT_TRIGGER = 23867,
// used when swapping event-stages
SPELL_VENOXIS_TRANSFORM = 23849, // 50% health - shapechange to cobra
SPELL_FRENZY = 8269, // 20% health - frenzy
SPELL_POISON = 24097
};
enum Events
{
// troll form
EVENT_THRASH = 1,
EVENT_DISPEL_MAGIC = 2,
EVENT_RENEW = 3,
EVENT_HOLY_NOVA = 4,
EVENT_HOLY_FIRE = 5,
EVENT_HOLY_WRATH = 6,
// phase-changing
EVENT_TRANSFORM = 7,
// snake form events
EVENT_POISON_CLOUD = 8,
EVENT_VENOM_SPIT = 9,
EVENT_PARASITIC_SERPENT = 10,
EVENT_FRENZY = 11,
EVENT_POISON
SPELL_FRENZY = 8269,
};
enum Phases
@@ -80,275 +62,114 @@ enum Phases
enum NPCs
{
NPC_PARASITIC_SERPENT = 14884,
NPC_RAZZASHI_COBRA = 11373,
BOSS_VENOXIS = 14507
BOSS_VENOXIS = 14507,
NPC_RAZZASHI_COBRA = 11373,
NPC_PARASITIC_SERPENT = 14884
};
class boss_venoxis : public CreatureScript
// High Priest Venoxis (14507)
struct boss_venoxis : public BossAI
{
public:
boss_venoxis() : CreatureScript("boss_venoxis") { }
boss_venoxis(Creature* creature) : BossAI(creature, DATA_VENOXIS) { }
struct boss_venoxisAI : public BossAI
void InitializeAI() override
{
boss_venoxisAI(Creature* creature) : BossAI(creature, DATA_VENOXIS) { }
void Reset() override
scheduler.SetValidator([this]
{
_Reset();
// remove all spells and auras from previous attempts
me->RemoveAllAuras();
me->SetReactState(REACT_PASSIVE);
// set some internally used variables to their defaults
_inMeleeRange = 0;
_transformed = false;
_frenzied = false;
events.SetPhase(PHASE_ONE);
return !me->HasUnitState(UNIT_STATE_CASTING);
});
SpawnCobras();
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_VENOXIS_DEATH);
me->RemoveAllAuras();
}
void SpawnCobras()
{
me->SummonCreature(NPC_RAZZASHI_COBRA, -12021.20f, -1719.73f, 39.34f, 0.85f, TEMPSUMMON_CORPSE_DESPAWN);
me->SummonCreature(NPC_RAZZASHI_COBRA, -12029.40f, -1714.54f, 39.36f, 0.68f, TEMPSUMMON_CORPSE_DESPAWN);
me->SummonCreature(NPC_RAZZASHI_COBRA, -12036.79f, -1704.27f, 40.06f, 0.45f, TEMPSUMMON_CORPSE_DESPAWN);
me->SummonCreature(NPC_RAZZASHI_COBRA, -12037.70f, -1694.20f, 39.35f, 0.27f, TEMPSUMMON_CORPSE_DESPAWN);
}
void SetCombatCombras()
{
std::list<Creature*> cobraList;
me->GetCreatureListWithEntryInGrid(cobraList, NPC_RAZZASHI_COBRA, 50.0f);
if (!cobraList.empty())
{
for (auto& cobras : cobraList)
{
cobras->SetInCombatWithZone();
}
}
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
me->SetReactState(REACT_AGGRESSIVE);
// Always running events
events.ScheduleEvent(EVENT_THRASH, 5s);
// Phase one events (regular form)
events.ScheduleEvent(EVENT_HOLY_NOVA, 5s, 15s, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_DISPEL_MAGIC, 35s, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_HOLY_FIRE, 10s, 20s, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_RENEW, 30s, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_HOLY_WRATH, 15s, 25s, 0, PHASE_ONE);
events.SetPhase(PHASE_ONE);
// Set zone in combat
DoZoneInCombat();
SetCombatCombras();
}
void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
{
// check if venoxis is ready to transform
if (!_transformed && !HealthAbovePct(50))
{
_transformed = true;
// schedule the event that changes our phase
events.ScheduleEvent(EVENT_TRANSFORM, 100ms);
}
// we're losing health, bad, go frenzy
else if (!_frenzied && !HealthAbovePct(20))
{
_frenzied = true;
events.ScheduleEvent(EVENT_FRENZY, 100ms);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
// return back to main code if we're still casting
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
// thrash is available in all phases
case EVENT_THRASH:
DoCast(me, SPELL_THRASH, true);
events.ScheduleEvent(EVENT_THRASH, 10s, 20s);
break;
// troll form spells and Actions (first part)
case EVENT_DISPEL_MAGIC:
DoCast(me, SPELL_DISPEL_MAGIC);
events.ScheduleEvent(EVENT_DISPEL_MAGIC, 15s, 20s, 0, PHASE_ONE);
break;
case EVENT_RENEW:
DoCast(me, SPELL_RENEW);
events.ScheduleEvent(EVENT_RENEW, 25s, 30s, 0, PHASE_ONE);
break;
case EVENT_HOLY_WRATH:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat))
DoCast(target, SPELL_HOLY_WRATH);
events.ScheduleEvent(EVENT_HOLY_WRATH, 12s, 22s, 0, PHASE_ONE);
break;
case EVENT_HOLY_FIRE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
DoCast(target, SPELL_HOLY_FIRE);
events.ScheduleEvent(EVENT_HOLY_FIRE, 10s, 24s, 0, PHASE_ONE);
break;
case EVENT_HOLY_NOVA:
DoCastSelf(SPELL_HOLY_NOVA);
events.ScheduleEvent(EVENT_HOLY_NOVA, 10s, 24s, 0, PHASE_ONE);
break;
//
// snake form spells and Actions
//
case EVENT_VENOM_SPIT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
DoCast(target, SPELL_VENOM_SPIT);
events.ScheduleEvent(EVENT_VENOM_SPIT, 5s, 15s, 0, PHASE_TWO);
break;
case EVENT_POISON_CLOUD:
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
DoCast(target, SPELL_POISON_CLOUD);
events.ScheduleEvent(EVENT_POISON_CLOUD, 15s, 20s, 0, PHASE_TWO);
break;
case EVENT_PARASITIC_SERPENT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
DoCast(target, SPELL_SUMMON_PARASITIC_SERPENT);
events.ScheduleEvent(EVENT_PARASITIC_SERPENT, 15s, 0, PHASE_TWO);
break;
case EVENT_FRENZY:
// frenzy at 20% health
DoCast(me, SPELL_FRENZY, true);
break;
//
// shape and phase-changing
//
case EVENT_TRANSFORM:
// shapeshift at 50% health
DoCast(me, SPELL_VENOXIS_TRANSFORM);
Talk(SAY_VENOXIS_TRANSFORM);
DoResetThreatList();
// phase two events (snakeform)
events.ScheduleEvent(EVENT_VENOM_SPIT, 5s, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_POISON_CLOUD, 10s, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_PARASITIC_SERPENT, 30s, 0, PHASE_TWO);
// transformed, start phase two
events.SetPhase(PHASE_TWO);
break;
default:
break;
}
}
DoMeleeAttackIfReady();
}
private:
uint8 _inMeleeRange;
bool _transformed;
bool _frenzied;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<boss_venoxisAI>(creature);
Reset();
}
};
class npc_razzashi_cobra_venoxis : public CreatureScript
{
public:
npc_razzashi_cobra_venoxis() : CreatureScript("npc_razzashi_cobra_venoxis") {}
struct npc_razzashi_cobra_venoxis_AI : public ScriptedAI
void Reset() override
{
npc_razzashi_cobra_venoxis_AI(Creature* creature) : ScriptedAI(creature) {}
BossAI::Reset();
EventMap events;
me->RemoveAllAuras();
me->SetReactState(REACT_PASSIVE);
}
void Reset()
{
events.Reset();
}
void JustEngagedWith(Unit*)
{
events.ScheduleEvent(EVENT_POISON, 8s);
if (Creature* Venoxis = GetVenoxis())
{
Venoxis->SetInCombatWithZone();
}
}
Creature* GetVenoxis()
{
return me->FindNearestCreature(BOSS_VENOXIS, 200.0f, true);
}
void UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_POISON:
{
me->CastSpell(me->GetVictim(), SPELL_POISON);
events.ScheduleEvent(EVENT_POISON, 15s);
break;
}
}
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
void JustEngagedWith(Unit* /*who*/) override
{
return new npc_razzashi_cobra_venoxis_AI(creature);
_JustEngagedWith();
me->SetReactState(REACT_AGGRESSIVE);
// Both phases
scheduler.Schedule(8s, [this](TaskContext context)
{
DoCastSelf(SPELL_THRASH, true);
context.Repeat(10s, 20s);
});
//
// Phase 1 - Troll Form
//
scheduler.Schedule(5s, 15s, PHASE_ONE, [this](TaskContext context)
{
DoCastSelf(SPELL_HOLY_NOVA);
context.Repeat(10s, 24s);
}).Schedule(35s, PHASE_ONE, [this](TaskContext context)
{
DoCastSelf(SPELL_DISPEL_MAGIC);
context.Repeat(15s, 20s);
}).Schedule(10s, 20s, PHASE_ONE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_HOLY_FIRE);
context.Repeat(10s, 24s);
}).Schedule(30s, PHASE_ONE, [this](TaskContext context)
{
DoCastSelf(SPELL_RENEW);
context.Repeat(25s, 30s);
}).Schedule(15s, 25s, PHASE_ONE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_HOLY_WRATH);
context.Repeat(12s, 22s);
});
//
// Phase 2 - Snake Form (50% health)
//
ScheduleHealthCheckEvent(50, [&]
{
scheduler.CancelGroup(PHASE_ONE);
DoCastSelf(SPELL_VENOXIS_TRANSFORM);
Talk(SAY_VENOXIS_TRANSFORM);
DoResetThreatList();
scheduler.Schedule(5s, PHASE_TWO, [this](TaskContext context)
{
DoCastSelf(SPELL_VENOM_SPIT);
context.Repeat(5s, 15s);
}).Schedule(10s, PHASE_TWO, [this](TaskContext context)
{
DoCastSelf(SPELL_POISON_CLOUD);
context.Repeat(15s, 20s);
}).Schedule(30s, PHASE_TWO, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_PARASITIC_SERPENT);
context.Repeat(15s);
});
// frenzy at 20% health
ScheduleHealthCheckEvent(20, [&]
{
DoCastSelf(SPELL_FRENZY, true);
});
});
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_VENOXIS_DEATH);
me->RemoveAllAuras(); // removes transform
}
};
void AddSC_boss_venoxis()
{
new boss_venoxis();
new npc_razzashi_cobra_venoxis();
RegisterCreatureAI(boss_venoxis);
}