Skip to content

Commit

Permalink
Autoduty fun: oriogenics
Browse files Browse the repository at this point in the history
- also added analyzer to catch empty first lines
  • Loading branch information
awgil committed Aug 24, 2024
1 parent 6d09bbf commit 7f48278
Show file tree
Hide file tree
Showing 7 changed files with 399 additions and 5 deletions.
104 changes: 104 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D051Herpekaris.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
namespace BossMod.Dawntrail.Dungeon.D05Origenics.D051Herpekaris;

public enum OID : uint
{
Boss = 0x4185, // R8.400, x1
Helper = 0x233C, // R0.500, x32, Helper type
VasoconstrictorVoidzone = 0x1E9E3C, // R0.500, x0 (spawn during fight), EventObj type
}

public enum AID : uint
{
AutoAttack = 872, // Boss->player, no cast, single-target
Teleport = 36520, // Boss->location, no cast, single-target
StridentShriek = 36519, // Boss->self, 5.0s cast, range 60 circle, raidwide
Vasoconstrictor = 36459, // Boss->self, 3.0+1.2s cast, single-target, visual (poison zones)
PoisonHeartVoidzone = 36460, // Helper->location, 4.2s cast, range 2 circle voidzones
VenomspillFirstL = 37451, // Boss->self, 5.0s cast, single-target, visual (hit left puddle)
VenomspillFirstR = 36452, // Boss->self, 5.0s cast, single-target, visual (hit right puddle)
VenomspillSecondR = 36453, // Boss->self, 4.0s cast, single-target, visual (hit right puddle)
VenomspillSecondL = 36454, // Boss->self, 4.0s cast, single-target, visual (hit left puddle)
PodBurstFirst = 38518, // Helper->location, 5.0s cast, range 6 circle
PodBurstRest = 38519, // Helper->location, 4.0s cast, range 6 circle
WrithingRiot = 36463, // Boss->self, 9.0s cast, single-target, visual (three sweeps)
WrithingRiotRight = 36465, // Helper->self, 2.0s cast, range 25 210-degree cone, visual (telegraph)
WrithingRiotLeft = 36466, // Helper->self, 2.0s cast, range 25 210-degree cone, visual (telegraph)
WrithingRiotRear = 36467, // Helper->self, 2.0s cast, range 25 90-degree cone, visual (telegraph)
RightSweep = 36469, // Boss->self, no cast, range 25 ?-degree cone
LeftSweep = 36470, // Boss->self, no cast, range 25 ?-degree cone
RearSweep = 36471, // Boss->self, no cast, range 25 ?-degree cone
PoisonHeartSpread = 37921, // Helper->player, 8.0s cast, range 5 circle spread
CollectiveAgony = 36473, // Boss->self/players, 5.5s cast, range 50 width 8 rect line stack
CollectiveAgonyTargetSelect = 36474, // Helper->player, no cast, single-target, visual (target select)
ConvulsiveCrush = 36518, // Boss->player, 5.0s cast, single-target, tankbuster
}

public enum IconID : uint
{
PoisonHeartSpread = 345, // player
ConvulsiveCrush = 218, // player
}

class StridentShriek(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.StridentShriek));
class PoisonHeartVoidzone(BossModule module) : Components.PersistentVoidzoneAtCastTarget(module, 2, ActionID.MakeSpell(AID.PoisonHeartVoidzone), m => m.Enemies(OID.VasoconstrictorVoidzone).Where(z => z.EventState != 7), 0.5f);
class PodBurstFirst(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.PodBurstFirst), 6);
class PodBurstRest(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.PodBurstRest), 6);

class WrithingRiot(BossModule module) : Components.GenericAOEs(module)
{
private readonly List<AOEInstance> _aoes = [];

private static readonly AOEShapeCone _shapeSide = new(25, 105.Degrees());
private static readonly AOEShapeCone _shapeRear = new(25, 45.Degrees());

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 1)
yield return _aoes[1];
if (_aoes.Count > 0)
yield return _aoes[0] with { Color = ArenaColor.Danger };
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
AOEShape? shape = (AID)spell.Action.ID switch
{
AID.WrithingRiotRight or AID.WrithingRiotLeft => _shapeSide,
AID.WrithingRiotRear => _shapeRear,
_ => null
};
if (shape != null)
{
_aoes.Add(new(shape, caster.Position, spell.Rotation, _aoes.Count > 0 ? _aoes[^1].Activation.AddSeconds(2) : Module.CastFinishAt(spell, 7.3f)));
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.RightSweep or AID.LeftSweep or AID.RearSweep && _aoes.Count > 0)
_aoes.RemoveAt(0);
}
}

class PoisonHeartSpread(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.PoisonHeartSpread), 5);
class CollectiveAgony(BossModule module) : Components.SimpleLineStack(module, 4, 50, ActionID.MakeSpell(AID.CollectiveAgonyTargetSelect), ActionID.MakeSpell(AID.CollectiveAgony), 5.6f);
class ConvulsiveCrush(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.ConvulsiveCrush));

class D051HerpekarisStates : StateMachineBuilder
{
public D051HerpekarisStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<StridentShriek>()
.ActivateOnEnter<PoisonHeartVoidzone>()
.ActivateOnEnter<PodBurstFirst>()
.ActivateOnEnter<PodBurstRest>()
.ActivateOnEnter<WrithingRiot>()
.ActivateOnEnter<PoisonHeartSpread>()
.ActivateOnEnter<CollectiveAgony>()
.ActivateOnEnter<ConvulsiveCrush>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 825, NameID = 12741)]
public class D051Herpekaris(WorldState ws, Actor primary) : BossModule(ws, primary, new(-88, -180), new ArenaBoundsSquare(18));
119 changes: 119 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D052Deceiver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
namespace BossMod.Dawntrail.Dungeon.D05Origenics.D052Deceiver;

public enum OID : uint
{
Boss = 0x4170, // R5.000, x1
Helper = 0x233C, // R0.500, x10, Helper type
SentryReal = 0x4171, // R0.900, x0 (spawn during fight)
SentryFake = 0x4172, // R0.900, x0 (spawn during fight)
}

public enum AID : uint
{
AutoAttackBoss = 870, // Boss->player, no cast, single-target
AutoAttackAdd = 873, // SentryReal->player, no cast, single-target
Teleport = 36362, // Boss->location, no cast, single-target
Electrowave = 36371, // Boss->self, 5.0s cast, range 72 circle, raidwide
BionicThrash = 36369, // Boss->self, 7.0s cast, single-target, visual (two quadrant cleave)
BionicThrashAOE = 36370, // Helper->self, 8.0s cast, range 30 90-degree cone
InitializeAndroids = 36363, // Boss->self, 4.0s cast, single-target, visual (sentries)
SynchroshotFake = 36373, // SentryFake->self, 5.0s cast, range 40 width 4 rect
SynchroshotReal = 36372, // SentryReal->self, 5.0s cast, range 40 width 4 rect
InitializeTurrets = 36364, // Boss->self, 4.0s cast, single-target, visual (turrets)
InitializeTurretsReal = 36365, // Helper->self, 4.7s cast, range 4 width 10 rect (? kill player if he'll be standing inside turret?)
InitializeTurretsFake = 36426, // Helper->self, 4.7s cast, range 4 width 10 rect (? kill player if he'll be standing inside turret?)
LaserLashReal = 36366, // Helper->self, 5.0s cast, range 40 width 10 rect
LaserLashFake = 38807, // Helper->self, 5.0s cast, range 40 width 10 rect
Surge = 36367, // Boss->location, 8.0s cast, range 40 width 40 rect, knock players left/right 30
SurgeNPC = 39736, // Helper->self, 8.5s cast, range 40 width 40 rect, knock duty support left/right 15
Electray = 38320, // Helper->player, 8.0s cast, range 5 circle spread
}

public enum IconID : uint
{
Electray = 345, // player
}

class Electrowave(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Electrowave));
class BionicThrash(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BionicThrashAOE), new AOEShapeCone(30, 45.Degrees()));
class Synchroshot(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SynchroshotReal), new AOEShapeRect(40, 2));
class Sentry(BossModule module) : Components.Adds(module, (uint)OID.SentryReal);
class LaserLash(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LaserLashReal), new AOEShapeRect(40, 5));

class Surge(BossModule module) : Components.Knockback(module, ActionID.MakeSpell(AID.Surge))
{
private readonly List<(WPos origin, WDir dir)> _realTurrets = [];
private readonly List<(WPos origin, WDir dir)> _fakeTurrets = [];
private DateTime _activation;

public override IEnumerable<Source> Sources(int slot, Actor actor)
{
if (_activation != default)
{
float distance = 30;
if (_realTurrets.Any(t => (t.origin.X < Module.Center.X) == (actor.Position.X < Module.Center.X) && Math.Abs(t.origin.Z - actor.Position.Z) <= 5))
distance = Math.Max(0, 15.5f - MathF.Abs(actor.Position.X - Module.Center.X));
yield return new(new(Module.Center.X, actor.Position.Z), distance, _activation);
}
}

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (_activation != default)
foreach (var t in _fakeTurrets)
hints.AddForbiddenZone(ShapeDistance.Rect(t.origin, t.dir, 20, 0, 5), _activation);
}

public override void DrawArenaBackground(int pcSlot, Actor pc)
{
if (_activation != default)
foreach (var t in _realTurrets)
Arena.ZoneRect(t.origin, t.dir, 20, 0, 5, ArenaColor.SafeFromAOE);
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
switch ((AID)spell.Action.ID)
{
case AID.InitializeTurretsReal:
_realTurrets.Add((caster.Position, spell.Rotation.ToDirection()));
break;
case AID.InitializeTurretsFake:
_fakeTurrets.Add((caster.Position, spell.Rotation.ToDirection()));
break;
case AID.Surge:
_activation = Module.CastFinishAt(spell);
break;
}
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if (spell.Action == WatchedAction)
{
_realTurrets.Clear();
_fakeTurrets.Clear();
_activation = default;
}
}
}

class Electray(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.Electray), 5);

class D052DeceiverStates : StateMachineBuilder
{
public D052DeceiverStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<Electrowave>()
.ActivateOnEnter<BionicThrash>()
.ActivateOnEnter<Synchroshot>()
.ActivateOnEnter<Sentry>()
.ActivateOnEnter<LaserLash>()
.ActivateOnEnter<Surge>()
.ActivateOnEnter<Electray>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 825, NameID = 12693)]
public class D052Deceiver(WorldState ws, Actor primary) : BossModule(ws, primary, new(-172, -142), new ArenaBoundsRect(16, 20));
155 changes: 155 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D053Ambrose.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
namespace BossMod.Dawntrail.Dungeon.D05Origenics.D053Ambrose;

public enum OID : uint
{
Boss = 0x417D, // R4.998, x1
OrigenicsEyeborg = 0x417E, // R4.000, x3
Superfluity = 0x417F, // R1.800, x6
Electrolance = 0x4180, // R1.380, x1
Helper = 0x233C, // R0.500, x53, Helper type
}

public enum AID : uint
{
AutoAttackBoss = 870, // Boss->player, no cast, single-target
AutoAttackAdd = 872, // Superfluity/OrigenicsEyeborg->player, no cast, single-target
Teleport = 36439, // Boss->location, no cast, single-target
PsychicWave = 36436, // Boss->self, 5.0s cast, range 80 circle, raidwide
VoltaicSlash = 36437, // Boss->player, 5.0s cast, single-target, tankbuster
OverwhelmingChargeSolo = 39233, // Boss->self, 5.0s cast, range 26 180-degree cone
PsychokinesisDoors = 36427, // Boss->self, 10.0s cast, single-target, visual (door aoes)
PsychokinesisDoorsAOE = 36428, // Helper->self, 10.0s cast, range 70 width 13 rect
ExtrasensoryField = 36432, // Boss->self, 7.0s cast, single-target, visual (square knockbacks)
ExtrasensoryExpulsionAOEV = 36433, // Helper->self, 7.0s cast, range 20 width 15 rect, knock forward 20
ExtrasensoryExpulsionAOEH = 36434, // Helper->self, 7.0s cast, range 15 width 20 rect, knock forward 20
PsychokineticCharge = 39055, // Boss->self, 7.0s cast, single-target, visual (knockbacks + cleave)
OverwhelmingChargeKnockback = 36435, // Boss->self, no cast, single-target, visual (cleave after knockback)
OverwhelmingChargeKnockbackAOE = 39072, // Helper->self, 9.8s cast, range 26 180-degree cone
Electrolance = 36429, // Boss->location, 6.0s cast, range 22 circle
PsychokinesisLance = 38929, // Boss->self, 8.0s cast, single-target, visual (line aoes)
PsychokinesisLanceVisual = 38953, // Helper->location, 2.5s cast, width 10 rect charge
PsychokinesisLanceRush = 38954, // Electrolance->location, no cast, width 10 rect charge
ElectrolanceAssimilation = 36430, // Boss->self, 0.5s cast, single-target, visual (final lance charge)
ElectrolanceAssimilationAOE = 36431, // Helper->self, 1.0s cast, range 33 width 10 rect
WhorlOfTheMind = 36438, // Helper->player, 5.0s cast, range 5 circle spread
}

public enum IconID : uint
{
VoltaicSlash = 218, // player
WhorlOfTheMind = 376, // player
}

public enum TetherID : uint
{
Psychokinesis = 266, // Boss->Electrolance
}

class PsychicWave(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.PsychicWave));
class VoltaicSlash(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.VoltaicSlash));
class OverwhelmingChargeSolo(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.OverwhelmingChargeSolo), new AOEShapeCone(26, 90.Degrees()));
class PsychokinesisDoors(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.PsychokinesisDoorsAOE), new AOEShapeRect(70, 6.5f));
class Adds(BossModule module) : Components.AddsMulti(module, [(uint)OID.OrigenicsEyeborg, (uint)OID.Superfluity]);

class ExtrasensoryExpulsion(BossModule module) : Components.Knockback(module)
{
public readonly List<Source> AOEs = [];
private WDir _cleaveDir;

private static readonly AOEShapeRect _shape = new(10, 10, 10);

public override IEnumerable<Source> Sources(int slot, Actor actor) => AOEs;
public override bool DestinationUnsafe(int slot, Actor actor, WPos pos) => !Module.InBounds(pos) || _cleaveDir.Dot(pos - Module.Center) > 0;

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
foreach (var s in AOEs)
if (DestinationUnsafe(slot, actor, s.Origin + s.Distance * s.Direction.ToDirection()))
hints.AddForbiddenZone(ShapeDistance.Rect(s.Origin, s.Direction, 15, 15, 15), s.Activation); // note: bigger than actual aoe, to prevent ai from standing too close to the border and being knocked across safe tile
}

public override void DrawArenaBackground(int pcSlot, Actor pc)
{
foreach (var s in AOEs)
if (!DestinationUnsafe(pcSlot, pc, s.Origin + s.Distance * s.Direction.ToDirection()))
_shape.Draw(Arena, s.Origin, s.Direction, ArenaColor.SafeFromAOE);
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
switch ((AID)spell.Action.ID)
{
case AID.ExtrasensoryExpulsionAOEV:
case AID.ExtrasensoryExpulsionAOEH:
var origin = Module.Center + new WDir(caster.Position.X < Module.Center.X ? -10 : +10, caster.Position.Z < Module.Center.Z ? -10 : +10);
AOEs.Add(new(origin, 20, Module.CastFinishAt(spell), _shape, spell.Rotation, Kind.DirForward));
break;
case AID.OverwhelmingChargeKnockbackAOE:
_cleaveDir = spell.Rotation.ToDirection();
break;
}
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID is AID.ExtrasensoryExpulsionAOEV or AID.ExtrasensoryExpulsionAOEH)
{
AOEs.Clear();
_cleaveDir = default;
}
}
}

class OverwhelmingChargeKnockback(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.OverwhelmingChargeKnockbackAOE), new AOEShapeCone(26, 90.Degrees()))
{
private readonly ExtrasensoryExpulsion? _knockback = module.FindComponent<ExtrasensoryExpulsion>();

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _knockback?.AOEs.Count > 0 ? [] : base.ActiveAOEs(slot, actor);
}

class Electrolance(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Electrolance), 22);

class PsychokinesisLance(BossModule module) : Components.GenericAOEs(module)
{
private readonly List<AOEInstance> _aoes = [];

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _aoes;

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.PsychokinesisLanceVisual)
{
var dir = spell.LocXZ - caster.Position;
_aoes.Add(new(new AOEShapeRect(dir.Length(), 5), caster.Position, Angle.FromDirection(dir), _aoes.Count == 0 ? Module.CastFinishAt(spell, 5.1f) : _aoes[^1].Activation.AddSeconds(0.8f)));
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.PsychokinesisLanceRush or AID.ElectrolanceAssimilationAOE && _aoes.Count > 0)
_aoes.RemoveAt(0);
}
}

class WhorlOfTheMind(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.WhorlOfTheMind), 5);

class D053AmbroseStates : StateMachineBuilder
{
public D053AmbroseStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<PsychicWave>()
.ActivateOnEnter<VoltaicSlash>()
.ActivateOnEnter<OverwhelmingChargeSolo>()
.ActivateOnEnter<PsychokinesisDoors>()
.ActivateOnEnter<Adds>()
.ActivateOnEnter<ExtrasensoryExpulsion>()
.ActivateOnEnter<OverwhelmingChargeKnockback>()
.ActivateOnEnter<Electrolance>()
.ActivateOnEnter<PsychokinesisLance>()
.ActivateOnEnter<WhorlOfTheMind>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 825, NameID = 12695)]
public class D053Ambrose(WorldState ws, Actor primary) : BossModule(ws, primary, new(190, 0), new ArenaBoundsRect(15, 19.5f));
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@

namespace BossMod.Dawntrail.Extreme.Ex1Valigarmanda;
namespace BossMod.Dawntrail.Extreme.Ex1Valigarmanda;

class Spikesicle(BossModule module) : Components.GenericAOEs(module)
{
Expand Down
3 changes: 1 addition & 2 deletions BossMod/Modules/Dawntrail/Extreme/Ex1Valigarmanda/Stance.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@

namespace BossMod.Dawntrail.Extreme.Ex1Valigarmanda;
namespace BossMod.Dawntrail.Extreme.Ex1Valigarmanda;

class Stance(BossModule module) : Components.GenericAOEs(module)
{
Expand Down
Loading

0 comments on commit 7f48278

Please sign in to comment.