Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BA Owain module, COD arena improvement, merge vbm #548

Merged
merged 3 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions BossMod/Components/Cleave.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class Cleave(BossModule module, ActionID aid, AOEShape shape, uint enemyO
public readonly bool ActiveWhileCasting = activeWhileCasting;
public readonly bool OriginAtTarget = originAtTarget;
public DateTime NextExpected;
private readonly List<Actor> _enemies = module.Enemies(enemyOID != 0 ? enemyOID : module.PrimaryActor.OID);
public readonly List<Actor> Enemies = module.Enemies(enemyOID != 0 ? enemyOID : module.PrimaryActor.OID);

public override void AddHints(int slot, Actor actor, TextHints hints)
{
Expand All @@ -21,7 +21,7 @@ public override void AddHints(int slot, Actor actor, TextHints hints)

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (!OriginsAndTargets().Any())
if (OriginsAndTargets().Count == 0)
return;

foreach (var (origin, target, angle) in OriginsAndTargets())
Expand Down Expand Up @@ -56,10 +56,13 @@ public override void DrawArenaForeground(int pcSlot, Actor pc)
}
}

private IEnumerable<(Actor origin, Actor target, Angle angle)> OriginsAndTargets()
public virtual List<(Actor origin, Actor target, Angle angle)> OriginsAndTargets()
{
foreach (var enemy in _enemies)
var count = Enemies.Count;
List<(Actor, Actor, Angle)> origins = new(count);
for (var i = 0; i < count; ++i)
{
var enemy = Enemies[i];
if (enemy.IsDead)
continue;

Expand All @@ -72,8 +75,9 @@ public override void DrawArenaForeground(int pcSlot, Actor pc)
var target = WorldState.Actors.Find(enemy.TargetID);
if (target != null)
{
yield return (OriginAtTarget ? target : enemy, target, Angle.FromDirection(target.Position - enemy.Position));
origins.Add(new(OriginAtTarget ? target : enemy, target, Angle.FromDirection(target.Position - enemy.Position)));
}
}
return origins;
}
}
2 changes: 1 addition & 1 deletion BossMod/Components/Gaze.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static void DrawEye(Vector2 eyeCenter, bool danger)
dl.AddCircleFilled(eyeCenter, _eyeInnerR, Colors.Border);
}

private static bool HitByEye(Actor actor, Eye eye) => (actor.Rotation + eye.Forward).ToDirection().Dot((eye.Position - actor.Position).Normalized()) >= 0.707107f; // 45-degree
public static bool HitByEye(Actor actor, Eye eye) => (actor.Rotation + eye.Forward).ToDirection().Dot((eye.Position - actor.Position).Normalized()) >= 0.707107f; // 45-degree

private Vector2 IndicatorScreenPos(WPos eye)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,28 @@ public class Ch01CloudOfDarkness(WorldState ws, Actor primary) : BossModule(ws,
public static readonly WPos Phase1BoundsCenter = new(100, 76.28427f);
public static readonly PolygonCustom[] Diamond = [new([new(115, 63), new(128.28427f, 76.28427f), new(100, 104.56854f), new(71.71573f, 76.28427f), new(85, 63)])];
private static readonly DonutV[] donut = [new(DefaultCenter, 34, 40, 80)];
public static readonly Square[] IntersectionBlockers = [.. GenerateIntersectionBlockers()];
public static readonly Shape[] Phase2ShapesND = [new Rectangle(new(100, 115), 24, 3), new Rectangle(new(100, 85), 24, 3), new Rectangle(new(115, 100), 3, 24),
new Rectangle(new(85, 100), 3, 24), new Square(new(126.5f, 100), 7.5f), new Square(new(73.5f, 100), 7.5f)];
public static readonly Shape[] Phase2ShapesWD = [.. donut, .. Phase2ShapesND];
public static readonly ArenaBoundsCircle DefaultArena = new(40);
public static readonly ArenaBoundsComplex Phase1Bounds = new(Diamond, ScaleFactor: 1.414f);
public static readonly ArenaBoundsComplex Phase2BoundsWD = new(Phase2ShapesWD);
public static readonly ArenaBoundsComplex Phase2BoundsND = new(Phase2ShapesND, donut);
public static readonly ArenaBoundsComplex Phase2BoundsWD = new(Phase2ShapesWD, IntersectionBlockers);
public static readonly ArenaBoundsComplex Phase2BoundsND = new(Phase2ShapesND, [.. IntersectionBlockers, .. donut]);

private static List<Square> GenerateIntersectionBlockers() // at intersections there are small blockers to prevent players from skipping tiles
{
var a45 = 45.Degrees();
var a135 = 135.Degrees();
WDir[] dirs = [a45.ToDirection(), a135.ToDirection(), (-a45).ToDirection(), (-a135).ToDirection()];
WPos[] pos = [new(85, 85), new(115, 85), new(115, 115), new(85, 115)];
var distance = 3 * MathF.Sqrt(2);

List<Square> squares = new(16);

for (var i = 0; i < 4; ++i)
for (var j = 0; j < 4; ++j)
squares.Add(new(pos[i] + distance * dirs[j], 1, a45));
return squares;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,31 @@
// - 00200010 - phase 1
// - 00020001 - phase 2
// - 00040004 - remove telegraph (note that actual bounds are controlled by something else!)
class Phase2InnerCells(BossModule module) : BossComponent(module)
class Phase2InnerCells(BossModule module) : Components.GenericAOEs(module)
{
private readonly DateTime[] _breakTime = new DateTime[28];
private static readonly AOEShapeRect square = new(3, 3, 3);
private static readonly Dictionary<int, (int x, int y)> _cellIndexToCoordinates = GenerateCellIndexToCoordinates();

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
var cell = CellIndex(actor.Position - Arena.Center) - 3;
for (var i = 0; i < 28; ++i)
{
if (i == cell)
{
if (Math.Max(0, (_breakTime[i] - WorldState.CurrentTime).TotalSeconds) < 10)
yield return new(square, CellCenter(i));
continue;
}
else if (_breakTime[i] != default)
yield return new(square, CellCenter(i), Color: Colors.FutureVulnerable);
}
}

public override void AddHints(int slot, Actor actor, TextHints hints)
{
var cell = CellIndex(actor.Position - Module.Center) - 3;
var cell = CellIndex(actor.Position - Arena.Center) - 3;
var breakTime = cell >= 0 && cell < _breakTime.Length ? _breakTime[cell] : default;
if (breakTime != default)
{
Expand All @@ -38,7 +56,7 @@ public override void OnEventEnvControl(byte index, uint state)
// 16 1D
// 11 13 14 15 1C 1B 1A 18
// 12 19
if (index is < 3 or > 30)
if (index is < 0x03 or > 0x1E)
return;
_breakTime[index - 3] = state switch
{
Expand All @@ -48,38 +66,66 @@ public override void OnEventEnvControl(byte index, uint state)
};
}

private int CoordinateToCell(float c) => (int)Math.Floor(c / 6);
private int CellIndex(WDir offset) => CellIndex(CoordinateToCell(offset.X), CoordinateToCell(offset.Z));
private int CellIndex(int x, int y) => (x, y) switch
private static int CoordinateToCell(float c) => (int)Math.Floor(c / 6);
private static int CellIndex(WDir offset) => CellIndex(CoordinateToCell(offset.X), CoordinateToCell(offset.Z));
private static int CellIndex(int x, int y) => (x, y) switch
{
(-4, -3) => 3,
(-3, -4) => 4,
(-3, -3) => 5,
(-2, -3) => 6,
(-1, -3) => 7,
(-3, -2) => 8,
(-3, -1) => 9,
(+3, -3) => 10,
(+2, -4) => 11,
(+2, -3) => 12,
(+1, -3) => 13,
(+0, -3) => 14,
(+2, -2) => 15,
(+2, -1) => 16,
(-4, +2) => 17,
(-3, +3) => 18,
(-3, +2) => 19,
(-2, +2) => 20,
(-1, +2) => 21,
(-3, +1) => 22,
(-3, +0) => 23,
(+3, +2) => 24,
(+2, +3) => 25,
(+2, +2) => 26,
(+1, +2) => 27,
(+0, +2) => 28,
(+2, +1) => 29,
(+2, +0) => 30,
_ => -1
(-4, -3) => 0x03,
(-3, -4) => 0x04,
(-3, -3) => 0x05,
(-2, -3) => 0x06,
(-1, -3) => 0x07,
(-3, -2) => 0x08,
(-3, -1) => 0x09,
(+3, -3) => 0x0A,
(+2, -4) => 0x0B,
(+2, -3) => 0x0C,
(+1, -3) => 0x0D,
(+0, -3) => 0x0E,
(+2, -2) => 0x0F,
(+2, -1) => 0x10,
(-4, +2) => 0x11,
(-3, +3) => 0x12,
(-3, +2) => 0x13,
(-2, +2) => 0x14,
(-1, +2) => 0x15,
(-3, +1) => 0x16,
(-3, +0) => 0x17,
(+3, +2) => 0x18,
(+2, +3) => 0x19,
(+2, +2) => 0x1A,
(+1, +2) => 0x1B,
(+0, +2) => 0x1C,
(+2, +1) => 0x1D,
(+2, +0) => 0x1E,
_ => 0
};

private static Dictionary<int, (int x, int y)> GenerateCellIndexToCoordinates()
{
var map = new Dictionary<int, (int x, int y)>();
for (var x = -4; x <= 3; ++x)
{
for (var y = -4; y <= 3; ++y)
{
var index = CellIndex(x, y);
if (index >= 0)
map[index] = (x, y);
}
}
return map;
}

public static WPos CellCenter(int breakTimeIndex)
{
var cellIndex = breakTimeIndex + 3;
if (_cellIndexToCoordinates.TryGetValue(cellIndex, out var coordinates))
{
var worldX = (coordinates.x + 0.5f) * 6;
var worldZ = (coordinates.y + 0.5f) * 6;
return Ch01CloudOfDarkness.DefaultCenter + new WDir(worldX, worldZ);
}
else
return default;
}
}
5 changes: 5 additions & 0 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public enum OID : uint
VisionOfRyne = 0x45B4, // R0.750, x0 (spawn during fight)
VisionOfGaia = 0x45B5, // R1.500, x0 (spawn during fight)
DragonPuddle = 0x1EBD41, // R0.500, x0 (spawn during fight), EventObj type, puddle appears when head is touched
GuardianOfEden = 0x45AE, // R115.380, x0 (spawn during fight), p5 failure state tree
}

public enum AID : uint
Expand Down Expand Up @@ -243,6 +244,10 @@ public enum AID : uint
CrystallizeTimeHallowedWings1 = 40229, // UsurperOfFrostP4->self, 4.7+1.3s cast, single-target, visual (first knockback)
CrystallizeTimeHallowedWings2 = 40230, // UsurperOfFrostP4->self, 0.5+1.3s cast, single-target, visual (second knockback)
CrystallizeTimeHallowedWingsAOE = 40332, // UsurperOfFrostP4->self, 0.5s cast, range 40 width 50 rect, knockback 20, heavy damage on first target, vuln on first 4 targets

MemorysEndP4 = 40305, // OracleOfDarknessP4->self, 10.0s cast, range 100 circle, enrage
AbsoluteZeroP4 = 40245, // UsurperOfFrostP4->self, 10.0s cast, range 100 circle, enrage
ParadiseLost = 40263, // Helper->self, no cast, range 100 circle, wipe on p5 failure state
}

public enum SID : uint
Expand Down
6 changes: 6 additions & 0 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ private void Phase34(uint id)
P4AkhMornMornAfah(id + 0x120000, 5.8f);
P4CrystallizeTime(id + 0x130000, 4.6f);
P4AkhMornMornAfah(id + 0x140000, 0.1f);
P4Enrage(id + 0x150000, 2.3f);

SimpleState(id + 0xFF0000, 100, "???");
}
Expand Down Expand Up @@ -615,4 +616,9 @@ private void P4CrystallizeTime(uint id, float delay)
ActorTargetable(id + 0xD0, _module.BossP4Usurper, true, 5.3f, "Bosses reappear")
.SetHint(StateMachine.StateHint.DowntimeEnd);
}

private void P4Enrage(uint id, float delay)
{
ActorCast(id, _module.BossP4Usurper, AID.AbsoluteZeroP4, delay, 10, true, "Enrage");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ class P3UltimateRelativityDarkBlizzard(BossModule module) : Components.GenericAO
private readonly List<Actor> _sources = [];
private DateTime _activation;

private static readonly AOEShapeDonut _shape = new(2, 12); // TODO: verify inner radius
private static readonly AOEShapeDonut _shape = new(4, 12); // TODO: verify inner radius

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _sources.Select(s => new AOEInstance(_shape, s.Position, default, _activation));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
switch ((AID)spell.Action.ID)
{
case AID.Hellpounce:
var offset = spell.LocXZ - Module.Center;
Activate(spell.LocXZ, Module.Center - offset, WorldState.FutureTime(3.7f));
var offset = spell.LocXZ - Arena.Center;
Activate(spell.LocXZ, Arena.Center - offset, WorldState.FutureTime(3.7f));
break;
case AID.HellpounceSecond:
_charge = null;
Expand Down
Loading