Skip to content

Commit

Permalink
Merge pull request #539 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
merge vbm + some other random changes
  • Loading branch information
CarnifexOptimus authored Jan 2, 2025
2 parents 908d15a + 565adc3 commit 1e8c330
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 72 deletions.
12 changes: 1 addition & 11 deletions BossMod/AI/AIManagementWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,7 @@ public void SetVisible(bool vis)
}
}

public void DrawDebug()
{

}
private Task UIAsync()
public override void Draw()
{
var configModified = false;

Expand Down Expand Up @@ -192,12 +188,6 @@ private Task UIAsync()
_config.Modified.Fire();
saveConfigIn = null;
}
return Task.CompletedTask;
}

public override async void Draw()
{
await UIAsync().ConfigureAwait(true);
}

public override void OnClose() => SetVisible(false);
Expand Down
12 changes: 6 additions & 6 deletions BossMod/BossModule/ArenaBounds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ public record class ArenaBoundsCustom : ArenaBounds
private readonly float offset;
public float HalfWidth, HalfHeight;

public ArenaBoundsCustom(float Radius, RelSimplifiedComplexPolygon Poly, float MapResolution = Half, float Offset = 0)
: base(Radius, MapResolution)
public ArenaBoundsCustom(float Radius, RelSimplifiedComplexPolygon Poly, float MapResolution = Half, float Offset = 0, float ScaleFactor = 1)
: base(Radius, MapResolution, ScaleFactor)
{
offset = Offset;
poly = Poly;
Expand Down Expand Up @@ -337,21 +337,21 @@ private Pathfinding.Map BuildMap()
public sealed record class ArenaBoundsComplex : ArenaBoundsCustom
{
public readonly WPos Center;
public ArenaBoundsComplex(Shape[] UnionShapes, Shape[]? DifferenceShapes = null, Shape[]? AdditionalShapes = null, float MapResolution = Half, float Offset = 0)
: base(BuildBounds(UnionShapes, DifferenceShapes, AdditionalShapes, MapResolution, Offset, out var center, out var halfWidth, out var halfHeight))
public ArenaBoundsComplex(Shape[] UnionShapes, Shape[]? DifferenceShapes = null, Shape[]? AdditionalShapes = null, float MapResolution = Half, float Offset = 0, float ScaleFactor = 1)
: base(BuildBounds(UnionShapes, DifferenceShapes, AdditionalShapes, MapResolution, Offset, ScaleFactor, out var center, out var halfWidth, out var halfHeight))
{
Center = center;
HalfWidth = halfWidth + Offset;
HalfHeight = halfHeight + Offset;
}

private static ArenaBoundsCustom BuildBounds(Shape[] unionShapes, Shape[]? differenceShapes, Shape[]? additionalShapes, float mapResolution, float offset, out WPos center, out float halfWidth, out float halfHeight)
private static ArenaBoundsCustom BuildBounds(Shape[] unionShapes, Shape[]? differenceShapes, Shape[]? additionalShapes, float mapResolution, float offset, float scalefactor, out WPos center, out float halfWidth, out float halfHeight)
{
var properties = CalculatePolygonProperties(unionShapes, differenceShapes ?? [], additionalShapes ?? []);
center = properties.Center;
halfWidth = properties.HalfWidth;
halfHeight = properties.HalfHeight;
return new(properties.Radius, properties.Poly, mapResolution, offset);
return new(scalefactor == 1 ? properties.Radius : properties.Radius / scalefactor, properties.Poly, mapResolution, offset, scalefactor);
}

private static (WPos Center, float HalfWidth, float HalfHeight, float Radius, RelSimplifiedComplexPolygon Poly) CalculatePolygonProperties(Shape[] unionShapes, Shape[] differenceShapes, Shape[] additionalShapes)
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Config/ConfigNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public abstract class ConfigNode
// draw custom contents; override this for complex config nodes
public virtual void DrawCustom(UITree tree, WorldState ws) { }

private static readonly Dictionary<Type, FieldInfo[]> _fieldsCache = [];
private static readonly ConcurrentDictionary<Type, FieldInfo[]> _fieldsCache = [];

private static FieldInfo[] GetSerializableFields(Type t)
{
Expand Down
42 changes: 28 additions & 14 deletions BossMod/Config/ConfigRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ public class ConfigRoot
private const int _version = 10;

public Event Modified = new();
public Version AssemblyVersion = new(); // we use this to show newly added config options
private readonly Dictionary<Type, ConfigNode> _nodes = [];

public IEnumerable<ConfigNode> Nodes => _nodes.Values;
public readonly Dictionary<Type, ConfigNode> _nodes = [];
public List<ConfigNode> Nodes => [.. _nodes.Values];

public void Initialize()
{
Expand Down Expand Up @@ -46,7 +45,6 @@ public void LoadFromFile(FileInfo file)
var node = type != null ? _nodes.GetValueOrDefault(type) : null;
node?.Deserialize(jconfig.Value, ser);
}
AssemblyVersion = json.RootElement.TryGetProperty(nameof(AssemblyVersion), out var jver) ? new(jver.GetString() ?? "") : new();
}
catch (Exception e)
{
Expand All @@ -58,18 +56,34 @@ public void SaveToFile(FileInfo file)
{
try
{
WriteFile(file, jwriter =>
var ser = Serialization.BuildSerializationOptions();
var serializedNodes = new ConcurrentDictionary<Type, string>();

Parallel.ForEach(_nodes, entry =>
{
jwriter.WriteStartObject();
var ser = Serialization.BuildSerializationOptions();
foreach (var (t, n) in _nodes)
{
jwriter.WritePropertyName(t.FullName!);
n.Serialize(jwriter, ser);
}
jwriter.WriteEndObject();
jwriter.WriteString(nameof(AssemblyVersion), AssemblyVersion.ToString());
using var ms = new MemoryStream();
using var tempWriter = new Utf8JsonWriter(ms);
entry.Value.Serialize(tempWriter, ser);
tempWriter.Flush();
serializedNodes[entry.Key] = Encoding.UTF8.GetString(ms.ToArray());
});

using var stream = new FileStream(file.FullName, FileMode.Create, FileAccess.Write, FileShare.None);
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });

writer.WriteStartObject();
writer.WriteNumber("Version", _version);
writer.WritePropertyName("Payload");
writer.WriteStartObject();

foreach (var (t, jsonStr) in serializedNodes)
{
writer.WritePropertyName(t.FullName!);
writer.WriteRawValue(jsonStr);
}

writer.WriteEndObject();
writer.WriteEndObject();
}
catch (Exception e)
{
Expand Down
4 changes: 3 additions & 1 deletion BossMod/Config/ConfigUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ public ConfigUI(ConfigRoot config, WorldState ws, DirectoryInfo? replayDir, Rota
_tabs.Add("About", _about.Draw);

Dictionary<Type, UINode> nodes = [];
foreach (var n in config.Nodes)
var nodes2 = _root.Nodes;
for (var i = 0; i < nodes2.Count; ++i)
{
var n = nodes2[i];
nodes[n.GetType()] = new(n);
}

Expand Down
2 changes: 1 addition & 1 deletion BossMod/Config/GroupAssignment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class GroupAssignment

public int this[PartyRolesConfig.Assignment r]
{
get => Assignments[(int)r];
get => (int)r is var index && index >= 0 && index < Assignments.Length ? Assignments[index] : -1;
set => Assignments[(int)r] = value;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness;

class Flare(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeCircle(25), (uint)IconID.Flare, ActionID.MakeSpell(AID.FlareAOE), 8.1f, true);
class Flare(BossModule module) : Components.SpreadFromIcon(module, (uint)IconID.Flare, ActionID.MakeSpell(AID.FlareAOE), 25, 8.1f);
class StygianShadow(BossModule module) : Components.Adds(module, (uint)OID.StygianShadow);
class Atomos(BossModule module) : Components.Adds(module, (uint)OID.Atomos);
class GhastlyGloomCross(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GhastlyGloomCrossAOE), new AOEShapeCross(40, 15));
Expand All @@ -10,7 +10,7 @@ class Atomos(BossModule module) : Components.Adds(module, (uint)OID.Atomos);
class LoomingChaos(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.LoomingChaosAOE));

// TODO: tankswap hints component for phase1
// TODO: phase 2 squares, break timer, teleport zones, outer ring safety
// TODO: phase 2 teleport zones?
// TODO: grim embrace / curse of darkness prevent turning

[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1010, NameID = 13624, PlanLevel = 100)]
Expand All @@ -24,7 +24,7 @@ public class Ch01CloudOfDarkness(WorldState ws, Actor primary) : BossModule(ws,
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);
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ public enum OID : uint
StygianTendrils = 0x4622, // R1.200, x0 (spawn during fight), evil seed
CloudletOfDarkness = 0x4623, // R3.000, x0 (spawn during fight), criss-cross source
BallOfNaught = 0x4624, // R1.500, x1, (en)death sphere
//_Gen_DreadGale = 0x4625, // R1.200, x1
//DreadGale = 0x4625, // R1.200, x1, ???
SinisterEye = 0x4626, // R2.800, x2, break gaze source
AtomosSpawnPoint = 0x1EBD7B, // R0.500, x0 (spawn during fight), EventObj type
EvilSeed = 0x1E9B3B, // R0.500, x0 (spawn during fight), EventObj type
//_Gen_Actor1e8536 = 0x1E8536, // R2.000, x1, EventObj type
//_Gen_Exit = 0x1E850B, // R0.500, x1, EventObj type
}

public enum AID : uint
Expand Down Expand Up @@ -156,11 +154,11 @@ public enum SID : uint
//_Gen_StabWound = 3062, // none->player, extra=0x0
//_Gen_ThornyVine = 445, // none->player, extra=0x0
//_Gen_ForwardWithThee = 2240, // none->player, extra=0x33F
//_Gen_Stun = 149, // none->player, extra=0x0
//_Gen_BackWithThee = 2241, // none->player, extra=0x340
//_Gen_LeftWithThee = 2242, // none->player, extra=0x341
//_Gen_Stun = 2656, // none->player, extra=0x0
//_Gen_RightWithThee = 2243, // none->player, extra=0x342
//_Gen_Stun = 149, // none->player, extra=0x0
//_Gen_Stun = 2656, // none->player, extra=0x0
}

public enum IconID : uint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ private void Subphase1Variant2End(uint id, float delay)
private void Subphase2(uint id, float delay)
{
DelugeOfDarkness2(id, delay);
DarkDominion(id + 0x10000, 9.3f);
ThirdArtOfDarknessParticleConcentration(id + 0x20000, 4);
DarkDominion(id + 0x10000, 9.3f); // note: 1s after cast ends, outer ring becomes dangerous
ThirdArtOfDarknessParticleConcentration(id + 0x20000, 4); // note: 3s after towers resolve, outer ring becomes normal
GhastlyGloom(id + 0x30000, 12.3f);
CurseOfDarkness(id + 0x40000, 8.3f);
EvilSeedChaosCondensedDiffusiveForceParticleBeam(id + 0x50000, 9.9f);
Expand All @@ -83,8 +83,8 @@ private void Subphase2(uint id, float delay)

CurseOfDarkness(id + 0x100000, 11.9f);
ParticleConcentrationPhaser(id + 0x110000, 4.2f);
DarkDominion(id + 0x120000, 1);
FeintParticleBeamThirdActOfDarkness(id + 0x130000, 3.1f);
DarkDominion(id + 0x120000, 1); // note: 1s after cast ends, outer ring becomes dangerous
FeintParticleBeamThirdActOfDarkness(id + 0x130000, 3.1f); // note: 2.5s after act of darkness resolves, outer ring becomes normal
GhastlyGloom(id + 0x140000, 11.4f);
PhaserChaosCondensedDiffusiveForceParticleBeam(id + 0x150000, 3.4f);
FloodOfDarknessAdds(id + 0x160000, 3);
Expand Down Expand Up @@ -165,9 +165,9 @@ private void RapidSequenceParticleBeam(uint id, float delay)
private void Flare(uint id, float delay)
{
Cast(id, AID.Flare, delay, 4);
ComponentCondition<Flare>(id + 0x10, 1, comp => comp.CurrentBaits.Count > 0)
ComponentCondition<Flare>(id + 0x10, 1, comp => comp.Spreads.Count != 0)
.ActivateOnEnter<Flare>();
ComponentCondition<Flare>(id + 0x20, 8.1f, comp => comp.NumCasts > 0, "Flares")
ComponentCondition<Flare>(id + 0x20, 8.1f, comp => comp.NumFinishedSpreads > 0, "Flares")
.DeactivateOnExit<Flare>();
}

Expand All @@ -177,15 +177,15 @@ private void FlareUnholyDarknessBladeOfDarkness(uint id, float delay)
ComponentCondition<RazingVolleyParticleBeam>(id + 1, 1.9f, comp => comp.NumCasts > 0, "Criss-cross start");
ComponentCondition<RazingVolleyParticleBeam>(id + 2, 2, comp => comp.NumCasts > 1);
CastEnd(id + 3, 0.1f);
ComponentCondition<Flare>(id + 0x10, 1, comp => comp.CurrentBaits.Count > 0)
ComponentCondition<Flare>(id + 0x10, 1, comp => comp.Spreads.Count != 0)
.ActivateOnEnter<Flare>();
ComponentCondition<RazingVolleyParticleBeam>(id + 0x11, 0.9f, comp => comp.NumCasts > 2);
CastStart(id + 0x20, AID.UnholyDarkness, 1.2f);
ComponentCondition<RazingVolleyParticleBeam>(id + 0x21, 0.8f, comp => comp.NumCasts > 3);
CastEnd(id + 0x22, 4.2f);
ComponentCondition<UnholyDarkness>(id + 0x30, 0.7f, comp => comp.Stacks.Count > 0)
.ActivateOnEnter<UnholyDarkness>();
ComponentCondition<Flare>(id + 0x40, 0.3f, comp => comp.NumCasts > 0, "Flares")
ComponentCondition<Flare>(id + 0x40, 0.3f, comp => comp.NumFinishedSpreads != 0, "Flares")
.DeactivateOnExit<Flare>();
CastStartMulti(id + 0x50, [AID.BladeOfDarknessL, AID.BladeOfDarknessR, AID.BladeOfDarknessC], 7.1f);
ComponentCondition<UnholyDarkness>(id + 0x51, 0.7f, comp => comp.NumFinishedStacks > 0, "Stacks")
Expand Down Expand Up @@ -216,6 +216,7 @@ private void DelugeOfDarkness2(uint id, float delay)
ComponentCondition<StygianShadow>(id + 0x10, 4.2f, comp => comp.ActiveActors.Any(), "Platform adds")
.ActivateOnEnter<StygianShadow>()
.ActivateOnEnter<Atomos>()
.ActivateOnEnter<Phase2InnerCells>()
.ActivateOnEnter<DarkEnergyParticleBeam>(); // overlaps with multiple mechanics
}

Expand Down Expand Up @@ -381,6 +382,7 @@ private void FloodOfDarkness2(uint id, float delay)
CastStart(id, AID.FloodOfDarkness2, delay, "Adds disappear")
.DeactivateOnExit<StygianShadow>()
.DeactivateOnExit<Atomos>()
.DeactivateOnExit<Phase2InnerCells>()
.DeactivateOnExit<DarkEnergyParticleBeam>();
CastEnd(id + 1, 7, "Raidwide + arena transition")
.SetHint(StateMachine.StateHint.Raidwide);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness;

// envcontrols:
// 00 = main bounds telegraph
// - 00200010 - phase 1
// - 00020001 - phase 2
// - 00040004 - remove telegraph (note that actual bounds are controlled by something else!)
class Phase2InnerCells(BossModule module) : BossComponent(module)
{
private readonly DateTime[] _breakTime = new DateTime[28];

public override void AddHints(int slot, Actor actor, TextHints hints)
{
var cell = CellIndex(actor.Position - Module.Center) - 3;
var breakTime = cell >= 0 && cell < _breakTime.Length ? _breakTime[cell] : default;
if (breakTime != default)
{
var remaining = Math.Max(0, (breakTime - WorldState.CurrentTime).TotalSeconds);
hints.Add($"Cell breaks in {remaining:f1}s", remaining < 10);
}
}

public override void OnEventEnvControl(byte index, uint state)
{
// 03-1E = mid squares
// - 08000001 - init
// - 00200010 - become occupied
// - 02000001 - become free
// - 00800040 - player is standing for too long (38s), will break soon (in 6s)
// - 00080004 - break
// - 00020001 - repair
// - arrangement:
// 04 0B
// 03 05 06 07 0E 0D 0C 0A
// 08 0F
// 09 10
// 17 1E
// 16 1D
// 11 13 14 15 1C 1B 1A 18
// 12 19
if (index is < 3 or > 30)
return;
_breakTime[index - 3] = state switch
{
0x00200010 => WorldState.FutureTime(44),
0x00800040 => WorldState.FutureTime(6),
_ => default,
};
}

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
{
(-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
};
}
3 changes: 0 additions & 3 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUAI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa
// assumption: pull range is 12; hitbox is 5, so maxmelee is 8, meaning we have approx 4m to move during pull - with sprint, speed is 7.8, accel is 30 => over 0.26s accel period we move 1.014m, then need another 0.38s to reach boss (but it also moves)
private WPos PrepullPosition(FRU module, PartyRolesConfig.Assignment assignment)
{
if (assignment == PartyRolesConfig.Assignment.Unassigned)
return default;

var safeRange = 12.5f;
var desiredRange = assignment is PartyRolesConfig.Assignment.MT or PartyRolesConfig.Assignment.MT or PartyRolesConfig.Assignment.M1 or PartyRolesConfig.Assignment.M2 ? 5 : 10;
var dir = _config.P1CyclonicBreakSpots[assignment];
Expand Down
5 changes: 4 additions & 1 deletion BossMod/Modules/Dawntrail/Ultimate/FRU/FRUConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class FRUConfig() : ConfigNode()
[GroupPreset("G1 N, G2 S, TMRH", [0, 4, 3, 7, 1, 5, 2, 6])]
public GroupAssignmentUnique P1BoundOfFaithAssignment = GroupAssignmentUnique.DefaultRoles();

[PropertyDisplay("P1 Fall of Faith (cone tethers) : conga priority (two people without tethers with lower priorities join odd group)")]
[PropertyDisplay("P1 Fall of Faith (cone tethers): conga priority (two people without tethers with lower priorities join odd group)")]
[GroupDetails(["1", "2", "3", "4", "5", "6", "7", "8"])]
[GroupPreset("TTHHMMRR", [0, 1, 2, 3, 4, 5, 6, 7])]
[GroupPreset("RHMTTMHR", [3, 4, 1, 6, 2, 5, 0, 7])]
Expand All @@ -24,6 +24,9 @@ public class FRUConfig() : ConfigNode()
[GroupPreset("H1-R2-H2 fixed, M1-M2-R1 flex", [0, 1, 2, 4, 5, 6, 7, 3])]
public GroupAssignmentUnique P1ExplosionsAssignment = new() { Assignments = [0, 1, 2, 4, 5, 6, 7, 3] };

[PropertyDisplay("P1 Explosions: flex roles only fill 3/4 if their natural tower is 1 (instead of doing conga)")]
public bool P1ExplosionsPriorityFill;

[PropertyDisplay("P1 Explosions: have tanks stack on tankbuster (survivable with saves, simplifies uptime)")]
public bool P1ExplosionsTankbusterCheese;

Expand Down
Loading

0 comments on commit 1e8c330

Please sign in to comment.