-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
477 additions
and
12 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D041VanguardCommander.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
namespace BossMod.Dawntrail.Dungeon.D04Vanguard.D041VanguardCommander; | ||
|
||
public enum OID : uint | ||
{ | ||
Boss = 0x411D, // R3.240, x1 | ||
Helper = 0x233C, // R0.500, x7, Helper type | ||
VanguardSentryR8 = 0x41BC, // R3.240, x0 (spawn during fight) | ||
} | ||
|
||
public enum AID : uint | ||
{ | ||
AutoAttack = 36403, // Boss->player, no cast, single-target | ||
Electrowave = 36571, // Boss->self, 5.0s cast, range 60 circle, raidwide | ||
EnhancedMobilityROut = 36559, // Boss->location, 10.0s cast, range 14 width 6 rect, teleport (right hand + move so that out is safe) | ||
EnhancedMobilityRIn = 36560, // Boss->location, 10.0s cast, range 14 width 6 rect, teleport (right hand + move so that in is safe) | ||
EnhancedMobilityLIn = 39140, // Boss->location, 10.0s cast, range 14 width 6 rect (right hand + move so that in is safe) | ||
EnhancedMobilityLOut = 39141, // Boss->location, 10.0s cast, range 14 width 6 rect (left hand + move so that in is safe) | ||
EnhancedMobilityAOEROut = 36563, // Helper->self, 10.5s cast, range 10 width 14 rect | ||
EnhancedMobilityAOELOut = 36564, // Helper->self, 10.5s cast, range 10 width 14 rect | ||
EnhancedMobilityAOERIn = 37184, // Helper->self, 10.5s cast, range 20 width 14 rect | ||
EnhancedMobilityAOELIn = 37191, // Helper->self, 10.5s cast, range 20 width 14 rect | ||
RapidRotaryROut = 36561, // Boss->self, no cast, single-target, visual | ||
RapidRotaryRIn = 36562, // Boss->self, no cast, single-target, visual | ||
RapidRotaryLIn = 39142, // Boss->self, no cast, single-target, visual | ||
RapidRotaryLOut = 39143, // Boss->self, no cast, single-target, visual | ||
RapidRotaryAOE = 36565, // Helper->self, no cast, range 11-17 donut 120-degree cone (common to in & out) | ||
RapidRotaryAOEOut = 36566, // Helper->self, no cast, range 14 120-degree cone | ||
RapidRotaryAOEIn = 36567, // Helper->self, no cast, range 14-28 donut 120-degree cone | ||
Dispatch = 36568, // Boss->self, 4.0s cast, single-target, visual (spawn sentries) | ||
Rush = 36569, // VanguardSentryR8->location, 6.0s cast, width 5 rect charge | ||
AerialOffensive = 36570, // VanguardSentryR8->location, 9.0s cast, range 4+10 circle puddle | ||
Electrosurge = 36572, // Boss->self, 4.0+1.0s cast, single-target, visual (spread) | ||
ElectrosurgeAOE = 36573, // Helper->player, 5.0s cast, range 5 circle spread | ||
} | ||
|
||
public enum IconID : uint | ||
{ | ||
Electrosurge = 315, // player | ||
} | ||
|
||
class Electrowave(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Electrowave)); | ||
|
||
class EnhancedMobility(BossModule module) : Components.GenericAOEs(module) | ||
{ | ||
private readonly List<AOEInstance> _aoes = []; | ||
|
||
private static readonly AOEShapeRect _shapeSideOut = new(10, 7); | ||
private static readonly AOEShapeRect _shapeSideIn = new(20, 7); | ||
private static readonly AOEShape _shapeOut = new AOEShapeCone(17, 60.Degrees()); | ||
private static readonly AOEShape _shapeIn = new AOEShapeDonutSector(11, 28, 60.Degrees()); | ||
|
||
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _aoes; | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
(AOEShape? side, AOEShape? main, float offset, Angle rotation) = (AID)spell.Action.ID switch | ||
{ | ||
AID.EnhancedMobilityAOEROut => (_shapeSideOut, _shapeOut, +7, -60.Degrees()), | ||
AID.EnhancedMobilityAOELOut => (_shapeSideOut, _shapeOut, -7, 60.Degrees()), | ||
AID.EnhancedMobilityAOERIn => (_shapeSideIn, _shapeIn, +7, 60.Degrees()), | ||
AID.EnhancedMobilityAOELIn => (_shapeSideIn, _shapeIn, -7, -60.Degrees()), | ||
_ => (null, null, 0, default) | ||
}; | ||
if (side != null && main != null) | ||
{ | ||
var activation = Module.CastFinishAt(spell); | ||
_aoes.Add(new(side, caster.Position + offset * spell.Rotation.ToDirection().OrthoR(), spell.Rotation, activation)); | ||
_aoes.Add(new(main, caster.Position, spell.Rotation + rotation, activation.AddSeconds(1.3f))); | ||
_aoes.Add(new(main, caster.Position, spell.Rotation + rotation * 3, activation.AddSeconds(1.6f))); | ||
_aoes.Add(new(main, caster.Position, spell.Rotation + rotation * 5, activation.AddSeconds(1.9f))); | ||
} | ||
} | ||
|
||
public override void OnEventCast(Actor caster, ActorCastEvent spell) | ||
{ | ||
if ((AID)spell.Action.ID is AID.EnhancedMobilityAOEROut or AID.EnhancedMobilityAOELOut or AID.EnhancedMobilityAOERIn or AID.EnhancedMobilityAOELIn or AID.RapidRotaryAOEOut or AID.RapidRotaryAOEIn && _aoes.Count > 0) | ||
_aoes.RemoveAt(0); | ||
} | ||
} | ||
|
||
class Rush(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.Rush), 2.5f); | ||
class AerialOffensive(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.AerialOffensive), 14, maxCasts: 4); | ||
class Electrosurge(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.ElectrosurgeAOE), 5); | ||
|
||
class D041VanguardCommanderStates : StateMachineBuilder | ||
{ | ||
public D041VanguardCommanderStates(BossModule module) : base(module) | ||
{ | ||
TrivialPhase() | ||
.ActivateOnEnter<Electrowave>() | ||
.ActivateOnEnter<EnhancedMobility>() | ||
.ActivateOnEnter<Rush>() | ||
.ActivateOnEnter<AerialOffensive>() | ||
.ActivateOnEnter<Electrosurge>(); | ||
} | ||
} | ||
|
||
[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 831, NameID = 12750)] | ||
public class D041VanguardCommander(WorldState ws, Actor primary) : BossModule(ws, primary, new(-100, 207), new ArenaBoundsSquare(17)); |
202 changes: 202 additions & 0 deletions
202
BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D042Protector.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
namespace BossMod.Dawntrail.Dungeon.D04Vanguard.D042Protector; | ||
|
||
public enum OID : uint | ||
{ | ||
Boss = 0x4237, // R5.830, x1 | ||
LaserTurret = 0x4238, // R0.960, x16 | ||
ExplosiveTurret = 0x4239, // R0.960, x8 | ||
FulminousFence = 0x4255, // R1.000, x4 | ||
Helper = 0x233C, // R0.500, x7, Helper type | ||
} | ||
|
||
public enum AID : uint | ||
{ | ||
AutoAttack = 878, // Boss->player, no cast, single-target | ||
Electrowave = 37161, // Boss->self, 5.0s cast, range 50 circle, raidwide | ||
SearchAndDestroy = 37154, // Boss->self, 3.0s cast, single-target, visual (turrets) | ||
HomingCannon = 37155, // LaserTurret->self, 2.5s cast, range 50 width 2 rect | ||
Shock = 37156, // ExplosiveTurret->location, 2.5s cast, range 3 circle | ||
SearchAndDestroyEnd = 37153, // Boss->self, no cast, single-target, visual (mechanic end) | ||
FulminousFence = 37149, // Boss->self, 3.0s cast, single-target, visual (barriers) | ||
ElectrostaticContact = 37158, // FulminousFence->player, no cast, single-target, damage + paralyze if hit by fence | ||
BatteryCircuit = 37159, // Boss->self, 5.0s cast, single-target, visual (rotating aoe) | ||
BatteryCircuitAOEFirst = 37351, // Helper->self, 5.0s cast, range 30 30-degree cone | ||
BatteryCircuitAOERest = 37344, // Helper->self, no cast, range 30 30-degree cone | ||
ElectrowhirlFirst = 37350, // Helper->self, 5.0s cast, range 6 circle | ||
ElectrowhirlRest = 37160, // Helper->self, 3.0s cast, range 6 circle | ||
Bombardment = 39016, // Helper->location, 3.0s cast, range 5 circle | ||
RapidThunder = 37162, // Boss->player, 5.0s cast, single-target, tankbuster | ||
MotionSensor = 37150, // Boss->self, 3.0s cast, single-target, visual (acceleration bombs) | ||
MotionSensorApply = 37343, // Helper->player, no cast, single-target, visual (apply bomb debuff) | ||
BlastCannon = 37151, // LaserTurret->self, 3.0s cast, range 26 width 4 rect | ||
HeavyBlastCannon = 37345, // Boss->self/players, 8.0s cast, range 36 width 8 rect, line stack | ||
HeavyBlastCannonTargetSelect = 37347, // Helper->player, no cast, single-target, target select | ||
TrackingBolt = 37348, // Boss->self, 8.0s cast, single-target, visual (spread) | ||
TrackingBoltAOE = 37349, // Helper->player, 8.0s cast, range 8 circle spread | ||
} | ||
|
||
public enum SID : uint | ||
{ | ||
AccelerationBomb1 = 3802, // Helper->player, extra=0x0 | ||
AccelerationBomb2 = 4144, // Helper->player, extra=0x0 | ||
} | ||
|
||
public enum IconID : uint | ||
{ | ||
RotateCW = 167, // Boss | ||
RapidThunder = 218, // player | ||
MotionSensor = 267, // player | ||
TrackingBolt = 196, // player | ||
} | ||
|
||
class Electrowave(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Electrowave)); | ||
class HomingCannon(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HomingCannon), new AOEShapeRect(50, 1)); | ||
class Shock(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Shock), 3); | ||
|
||
class FulminousFence(BossModule module) : BossComponent(module) | ||
{ | ||
public record struct Line(WDir A, WDir B); | ||
|
||
private Line[] _curPattern = []; | ||
private float _curPatternZMult; // each pattern can be mirrored | ||
|
||
private static readonly Line[] _pattern1 = [ | ||
new(new(-4, -12), new(+4, -12)), new(new(+4, -12), new(+4, -4)), new(new(+4, -12), new(+12, -12)), new(new(+12, -12), new(+12, -4)), new(new(+12, -4), new(+12, +4)), new(new(+12, +4), new(+4, +4)), | ||
new(new(+4, +12), new(-4, +12)), new(new(-4, +12), new(-4, +4)), new(new(-4, +12), new(-12, +12)), new(new(-12, +12), new(-12, +4)), new(new(-12, +4), new(-12, -4)), new(new(-12, -4), new(-4, -4)) | ||
]; | ||
private static readonly Line[] _pattern2 = [ | ||
new(new(0, -12), new(0, -8)), new(new(-8, -8), new(-4, -4)), new(new(+8, -8), new(+4, -4)), new(new(0, +4), new(0, +8)), new(new(-8, +8), new(-12, +12)), new(new(+8, +8), new(+12, +12)) | ||
]; | ||
|
||
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) | ||
{ | ||
foreach (var (a, b) in ActiveLines()) | ||
{ | ||
var raw = ShapeDistance.Rect(a, b, 0); | ||
hints.AddForbiddenZone(p => raw(p) - 1); | ||
} | ||
} | ||
|
||
public override void DrawArenaForeground(int pcSlot, Actor pc) | ||
{ | ||
foreach (var (a, b) in ActiveLines()) | ||
Arena.AddLine(a, b, ArenaColor.Danger, 2); | ||
} | ||
|
||
public override void OnEventEnvControl(byte index, uint state) | ||
{ | ||
if (index != 13) | ||
return; | ||
switch (state) | ||
{ | ||
case 0x00020001: | ||
_curPattern = _pattern1; | ||
_curPatternZMult = 1; | ||
break; | ||
case 0x00200010: | ||
_curPattern = _pattern1; | ||
_curPatternZMult = -1; | ||
break; | ||
case 0x01000080: | ||
_curPattern = _pattern2; | ||
_curPatternZMult = 1; | ||
break; | ||
case 0x08000400: | ||
_curPattern = _pattern2; | ||
_curPatternZMult = -1; | ||
break; | ||
case 0x00080004: | ||
case 0x00400004: | ||
case 0x02000004: | ||
case 0x10000004: | ||
_curPattern = []; | ||
_curPatternZMult = 0; | ||
break; | ||
} | ||
} | ||
|
||
private IEnumerable<(WPos a, WPos b)> ActiveLines() => _curPattern.Select(l => (ConvertEndpoint(l.A), ConvertEndpoint(l.B))); | ||
private WPos ConvertEndpoint(WDir p) => new(Module.Center.X + p.X, Module.Center.Z + p.Z * _curPatternZMult); | ||
} | ||
|
||
// note: never seen ccw rotation, assume it's not possible | ||
class BatteryCircuit(BossModule module) : Components.GenericRotatingAOE(module) | ||
{ | ||
private static readonly AOEShapeCone _shape = new(30, 15.Degrees()); | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.BatteryCircuitAOEFirst) | ||
Sequences.Add(new(_shape, caster.Position, spell.Rotation, -11.Degrees(), Module.CastFinishAt(spell), 0.5f, 33, 10)); | ||
} | ||
|
||
public override void OnEventCast(Actor caster, ActorCastEvent spell) | ||
{ | ||
if ((AID)spell.Action.ID is AID.BatteryCircuitAOEFirst or AID.BatteryCircuitAOERest) | ||
AdvanceSequence(caster.Position, caster.Rotation, WorldState.CurrentTime); | ||
} | ||
} | ||
|
||
class ElectrowhirlFirst(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ElectrowhirlFirst), new AOEShapeCircle(6)); | ||
class ElectrowhirlRest(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ElectrowhirlRest), new AOEShapeCircle(6)); | ||
class Bombardment(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Bombardment), 5); | ||
class RapidThunder(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.RapidThunder)); | ||
|
||
class MotionSensor(BossModule module) : Components.StayMove(module) | ||
{ | ||
private readonly DateTime[] _expire = new DateTime[4]; | ||
|
||
public override void Update() | ||
{ | ||
base.Update(); | ||
var deadline = WorldState.FutureTime(3); | ||
for (int i = 0; i < _expire.Length; ++i) | ||
Requirements[i] = _expire[i] != default && _expire[i] < deadline ? Requirement.Stay : Requirement.None; | ||
} | ||
|
||
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) | ||
{ | ||
if (Requirements[slot] == Requirement.Stay) | ||
hints.ForcedMovement = new(); | ||
} | ||
|
||
public override void OnStatusGain(Actor actor, ActorStatus status) | ||
{ | ||
if ((SID)status.ID is SID.AccelerationBomb1 or SID.AccelerationBomb2 && Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0) | ||
_expire[slot] = status.ExpireAt; | ||
} | ||
|
||
public override void OnStatusLose(Actor actor, ActorStatus status) | ||
{ | ||
if ((SID)status.ID is SID.AccelerationBomb1 or SID.AccelerationBomb2 && Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0) | ||
_expire[slot] = default; | ||
} | ||
} | ||
|
||
class BlastCannon(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BlastCannon), new AOEShapeRect(26, 2)); | ||
class HeavyBlastCannon(BossModule module) : Components.SimpleLineStack(module, 4, 36, ActionID.MakeSpell(AID.HeavyBlastCannonTargetSelect), ActionID.MakeSpell(AID.HeavyBlastCannon), 8); | ||
class TrackingBolt(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.TrackingBoltAOE), 8); | ||
|
||
class D042ProtectorStates : StateMachineBuilder | ||
{ | ||
public D042ProtectorStates(BossModule module) : base(module) | ||
{ | ||
TrivialPhase() | ||
.ActivateOnEnter<Electrowave>() | ||
.ActivateOnEnter<HomingCannon>() | ||
.ActivateOnEnter<Shock>() | ||
.ActivateOnEnter<FulminousFence>() | ||
.ActivateOnEnter<BatteryCircuit>() | ||
.ActivateOnEnter<ElectrowhirlFirst>() | ||
.ActivateOnEnter<ElectrowhirlRest>() | ||
.ActivateOnEnter<Bombardment>() | ||
.ActivateOnEnter<RapidThunder>() | ||
.ActivateOnEnter<MotionSensor>() | ||
.ActivateOnEnter<BlastCannon>() | ||
.ActivateOnEnter<HeavyBlastCannon>() | ||
.ActivateOnEnter<TrackingBolt>(); | ||
} | ||
} | ||
|
||
[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 831, NameID = 12757)] | ||
public class D042Protector(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -100), new ArenaBoundsRect(12, 20)); |
Oops, something went wrong.