Skip to content

Commit

Permalink
Merge pull request #548 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
BA Owain module, COD arena improvement, merge vbm
  • Loading branch information
CarnifexOptimus authored Jan 8, 2025
2 parents 8c9f276 + 1876010 commit b2f135c
Show file tree
Hide file tree
Showing 20 changed files with 670 additions and 201 deletions.
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

0 comments on commit b2f135c

Please sign in to comment.