Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/awgil/ffxiv_bossmod into wip
Browse files Browse the repository at this point in the history
  • Loading branch information
xanunderscore committed Dec 24, 2024
2 parents eba8cd9 + c1a229f commit 661f3eb
Show file tree
Hide file tree
Showing 18 changed files with 450 additions and 50 deletions.
4 changes: 2 additions & 2 deletions BossMod/Autorotation/RotationModuleManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ public void Update(float estimatedAnimLockDelay, bool isMoving)
public WPos ResolveTargetLocation(StrategyTarget strategy, int param, float off1, float off2) => strategy switch
{
StrategyTarget.PointAbsolute => new(off1, off2),
StrategyTarget.PointCenter or StrategyTarget.Automatic => (Bossmods.ActiveModule?.Center ?? Player?.Position ?? default) + off1 * off2.Degrees().ToDirection(),
_ => (ResolveTargetOverride(strategy, param)?.Position ?? Player?.Position ?? default) + off1 * off2.Degrees().ToDirection(),
StrategyTarget.PointCenter or StrategyTarget.Automatic => (Bossmods.ActiveModule?.Center + off1 * off2.Degrees().ToDirection()) ?? Player?.Position ?? default,
_ => (ResolveTargetOverride(strategy, param)?.Position + off1 * off2.Degrees().ToDirection()) ?? Player?.Position ?? default,
};

private Plan? CalculateExpectedPlan()
Expand Down
1 change: 1 addition & 0 deletions BossMod/Data/Actor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public sealed class Actor(ulong instanceID, uint oid, int spawnIndex, string nam
public ClassCategory ClassCategory => Class.GetClassCategory();
public WPos Position => new(PosRot.X, PosRot.Z);
public WPos PrevPosition => new(PrevPosRot.X, PrevPosRot.Z);
public WDir LastFrameMovement => Position - PrevPosition;
public Angle Rotation => PosRot.W.Radians();
// status "Directional Disregard" - applied temporarily on some DT raid bosses
public bool Omnidirectional => Statuses.Any(s => s.ID == 3808) || Utils.CharacterIsOmnidirectional(OID);
Expand Down
6 changes: 6 additions & 0 deletions BossMod/Debug/MainDebugWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ private unsafe void DrawStatuses()
var player = (Character*)GameObjectManager.Instance()->Objects.IndexSorted[0].Value;
player->GetStatusManager()->SetStatus(20, 3909, 20.0f, 100, 0xE0000000, true);
}
ImGui.SameLine();
if (ImGui.Button("Add thin ice"))
{
var player = (Character*)GameObjectManager.Instance()->Objects.IndexSorted[0].Value;
player->GetStatusManager()->SetStatus(20, 911, 20.0f, 320, 0xE0000000, true); // param = distance * 10
}

foreach (var elem in ws.Actors)
{
Expand Down
9 changes: 9 additions & 0 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRU.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@ class P2QuadrupleSlap(BossModule module) : Components.TankSwap(module, ActionID.
class P2CrystalOfLight(BossModule module) : Components.Adds(module, (uint)OID.CrystalOfLight);
class P3Junction(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.Junction));
class P3BlackHalo(BossModule module) : Components.CastSharedTankbuster(module, ActionID.MakeSpell(AID.BlackHalo), new AOEShapeCone(60, 45.Degrees())); // TODO: verify angle
class P4EdgeOfOblivion(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.EdgeOfOblivion));

[ModuleInfo(BossModuleInfo.Maturity.WIP, PrimaryActorOID = (uint)OID.BossP1, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1006, NameID = 9707, PlanLevel = 100)]
public class FRU(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(20))
{
private Actor? _bossP2;
private Actor? _iceVeil;
private Actor? _bossP3;
private Actor? _bossP4Usurper;
private Actor? _bossP4Oracle;

public Actor? BossP1() => PrimaryActor;
public Actor? BossP2() => _bossP2;
public Actor? IceVeil() => _iceVeil;
public Actor? BossP3() => _bossP3;
public Actor? BossP4Usurper() => _bossP4Usurper;
public Actor? BossP4Oracle() => _bossP4Oracle;

protected override void UpdateModule()
{
Expand All @@ -24,6 +29,8 @@ protected override void UpdateModule()
_bossP2 ??= StateMachine.ActivePhaseIndex == 1 ? Enemies(OID.BossP2).FirstOrDefault() : null;
_iceVeil ??= StateMachine.ActivePhaseIndex == 1 ? Enemies(OID.IceVeil).FirstOrDefault() : null;
_bossP3 ??= StateMachine.ActivePhaseIndex == 2 ? Enemies(OID.BossP3).FirstOrDefault() : null;
_bossP4Usurper ??= StateMachine.ActivePhaseIndex == 2 ? Enemies(OID.UsurperOfFrostP4).FirstOrDefault() : null;
_bossP4Oracle ??= StateMachine.ActivePhaseIndex == 2 ? Enemies(OID.OracleOfDarknessP4).FirstOrDefault() : null;
}

protected override void DrawEnemies(int pcSlot, Actor pc)
Expand All @@ -32,5 +39,7 @@ protected override void DrawEnemies(int pcSlot, Actor pc)
Arena.Actor(_bossP2, ArenaColor.Enemy);
Arena.Actor(_iceVeil, ArenaColor.Enemy);
Arena.Actor(_bossP3, ArenaColor.Enemy);
Arena.Actor(_bossP4Usurper, ArenaColor.Enemy);
Arena.Actor(_bossP4Oracle, ArenaColor.Enemy);
}
}
7 changes: 6 additions & 1 deletion BossMod/Modules/Dawntrail/Ultimate/FRU/FRUConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,19 @@ public class FRUConfig() : ConfigNode()

[PropertyDisplay("P2 Diamond Dust: cardinal assignments")]
[GroupDetails(["Support N", "Support E", "Support S", "Support W", "DD N", "DD E", "DD S", "DD W"])]
public GroupAssignmentUnique P2DiamondDustCardinals = GroupAssignmentUnique.DefaultRoles();
[GroupPreset("Default", [0, 2, 3, 1, 7, 6, 4, 5])]
public GroupAssignmentUnique P2DiamondDustCardinals = new() { Assignments = [0, 2, 3, 1, 7, 6, 4, 5] };

[PropertyDisplay("P2 Diamond Dust: supports go to CCW intercardinal")]
public bool P2DiamondDustSupportsCCW;

[PropertyDisplay("P2 Diamond Dust: DD go to CCW intercardinal")]
public bool P2DiamondDustDDCCW;

[PropertyDisplay("P2 Diamond Dust: knockback groups")]
[GroupDetails(["G1 (CCW from N)", "G2 (CW from NE)"])]
public GroupAssignmentLightParties P2DiamondDustKnockbacks = GroupAssignmentLightParties.DefaultLightParties();

[PropertyDisplay("P2 Light Rampant: conga spots (lower numbers to the west, assume CW rotation)")]
[GroupDetails(["N1", "N2", "N3", "N4", "S1", "S2", "S3", "S4"])]
[GroupPreset("HHTT/RRMM", [2, 3, 0, 1, 6, 7, 4, 5])]
Expand Down
30 changes: 26 additions & 4 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@ public enum OID : uint
Gaia = 0x45A6, // R1.000, x0 (spawn during fight)
//_Gen_CrystalOfLight = 0x464F, // R1.000, x0 (spawn during fight)
HiemalRayVoidzone = 0x1EA1CB, // R0.500, x0 (spawn during fight), EventObj type
//_Gen_EternalIceFragment = 0x1EBBF8, // R0.500, x0 (spawn during fight), EventObj type
EternalIceFragment = 0x1EBBF8, // R0.500, x0 (spawn during fight), EventObj type, spawns if dark crystals are killed

BossP3 = 0x45A7, // R7.040, x0 (spawn during fight)
DelightsHourglass = 0x45A8, // R1.000, x0 (spawn during fight)
ApocalypseLight = 0x1EB0FF, // R0.500, x0 (spawn during fight), EventObj type

UsurperOfFrostP4 = 0x45A9, // R6.125, x0 (spawn during fight)
OracleOfDarknessP4 = 0x45AB, // R7.040, x0 (spawn during fight)
VisionOfRyne = 0x45B4, // R0.750, x0 (spawn during fight)
VisionOfGaia = 0x45B5, // R1.500, x0 (spawn during fight)
FragmentOfFate = 0x45B1, // R3.500, x0 (spawn during fight)
GreatWyrm = 0x45AA, // R3.500, x0 (spawn during fight), Part type
}

public enum AID : uint
Expand Down Expand Up @@ -151,6 +158,7 @@ public enum AID : uint
HiemalStorm = 40255, // CrystalOfLight->self, no cast, single-target, visual (baited puddle)
HiemalStormAOE = 40256, // Helper->location, 3.3s cast, range 7 circle
HiemalRay = 40257, // Helper->player, no cast, range 4 circle
Icecrusher = 40121, // Gaia->IceVeil, no cast, single-target, 50% hp hit on large crystal

// P3
Junction = 40226, // Helper->self, no cast, range 40 circle, raidwide
Expand Down Expand Up @@ -189,7 +197,21 @@ public enum AID : uint
DarkestDanceKnockback = 40183, // BossP3->self, no cast, range 40 circle, knockback 21

MemorysEnd = 40300, // BossP3->self, 10.0s cast, single-target, visual (enrage)
MemorysEndAOE = 40336, // Helper->self, no cast, range 100 circle, enrage
MemorysEndRaidwide = 40335, // Helper->self, no cast, range 100 circle, raidwide if boss is <20%
MemorysEndEnrage = 40336, // Helper->self, no cast, range 100 circle, enrage if boss is >20%

// P4
Materialization = 40246, // UsurperOfFrostP4->self, 3.0s cast, single-target, visual (create visions)
DrachenArmor = 40186, // Helper->self, no cast, single-target, visual (wings appear)
AutoAttackP4Wyrm = 40178, // GreatWyrm->player, no cast, single-target
AutoAttackP4Usurper = 40177, // UsurperOfFrostP4->player, no cast, single-target
EdgeOfOblivion = 40174, // FragmentOfFate->self, 5.0s cast, range 100 circle, raidwide
AkhRhai = 40237, // Helper->location, 2.5s cast, range 4 circle, visual (puddle)
AkhRhaiAOE = 40238, // Helper->location, no cast, range 4 circle, repeated puddle x10

DarklitDragonsongUsurper = 40239, // UsurperOfFrostP4->self, 5.0s cast, range 100 circle, raidwide
DarklitDragonsongOracle = 40301, // OracleOfDarknessP4->self, 5.0s cast, single-target, visual
//_Weaponskill_ThePathOfLight = 40187, // UsurperOfFrostP4->self, 8.0s cast, single-target
}

public enum SID : uint
Expand All @@ -200,6 +222,7 @@ public enum SID : uint
FatedBurnMark = 4165, // none->player, extra=0x0
FloatingFetters = 2304, // FatebreakersImage/BossP1->player, extra=0xC8
MarkOfMortality = 4372, // Helper->player, extra=0x1
ThinIce = 911, // none->player, extra=0x140
ChainsOfEverlastingLight = 4157, // none->player, extra=0x0, light rampant first tether
CurseOfEverlastingLight = 4158, // none->player, extra=0x0, light rampant second tether
WeightOfLight = 4159, // none->player, extra=0x0, light rampant stack
Expand All @@ -214,8 +237,6 @@ public enum SID : uint
DelightsHourglassRotation = 2970, // none->DelightsHourglass, extra=0x10D (ccw)/0x15C (cw)
Return = 2452, // none->player, extra=0x0
Stun = 4163, // none->player, extra=0x0
//SpellInWaitingRefrain = 4373, // BossP3->BossP3, extra=0x0
//_Gen_ = 2458, // none->player, extra=0x0
}

public enum IconID : uint
Expand All @@ -238,6 +259,7 @@ public enum TetherID : uint
LightRampantCurse = 111, // player->player
IntermissionGaia = 112, // Gaia->IceVeil
IntermissionCrystal = 8, // CrystalOfLight/CrystalOfDarkness->IceVeil
IntermissionCrystalGaia = 37, // CrystalOfDarkness->Gaia/CrystalOfDarkness
HiemalRay = 84, // CrystalOfLight->player
UltimateRelativitySlow = 133, // DelightsHourglass->BossP3
UltimateRelativityQuicken = 134, // DelightsHourglass->BossP3
Expand Down
54 changes: 48 additions & 6 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public FRUStates(FRU module) : base(module)
SimplePhase(1, Phase2, "P2: Usurper of Frost")
.SetHint(StateMachine.PhaseHint.StartWithDowntime)
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP2()?.IsDeadOrDestroyed ?? false) || (_module.IceVeil()?.IsDeadOrDestroyed ?? false);
SimplePhase(2, Phase3, "P3: Oracle of Darkness")
SimplePhase(2, Phase34, "P3/4: Oracle of Darkness & Both")
.SetHint(StateMachine.PhaseHint.StartWithDowntime)
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP2()?.IsDeadOrDestroyed ?? false) && (_module.BossP3()?.IsDeadOrDestroyed ?? true);
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP2()?.IsDeadOrDestroyed ?? false) && (_module.BossP3()?.IsDeadOrDestroyed ?? true) && (_module.BossP4Oracle()?.IsDeadOrDestroyed ?? true);
}

private void Phase1(uint id)
Expand Down Expand Up @@ -42,13 +42,18 @@ private void Phase2(uint id)
P2AbsoluteZero(id + 0x60000, 8.4f);
}

private void Phase3(uint id)
private void Phase34(uint id)
{
P3JunctionHellsJudgment(id, 13.3f);
P3UltimateRelativity(id + 0x10000, 4.3f);
P3BlackHalo(id + 0x20000, 3.2f);
P3Apocalypse(id + 0x30000, 7.2f);
ActorCast(id + 0x40000, _module.BossP3, AID.MemorysEnd, 3.7f, 10, true, "Enrage");
P3Enrage(id + 0x40000, 3.7f);

P4AkhRhai(id + 0x100000, 5.5f);
P4DarklitDragonsong(id + 0x110000, 1.9f);

SimpleState(id + 0xFF0000, 100, "???");
}

private void P1CyclonicBreakPowderMarkTrail(uint id, float delay)
Expand Down Expand Up @@ -221,19 +226,24 @@ private void P2DiamondDust(uint id, float delay)
.DeactivateOnExit<P2AxeKick>()
.DeactivateOnExit<P2ScytheKick>();
ComponentCondition<P2DiamondDustHouseOfLight>(id + 0x31, 0.8f, comp => comp.NumCasts > 0, "Proteans")
.ExecOnEnter<P2FrigidStone>(comp => comp.EnableHints = true)
.DeactivateOnExit<P2DiamondDustHouseOfLight>();
ComponentCondition<P2FrigidStone>(id + 0x32, 1.6f, comp => comp.NumCasts > 0, "Ice baits")
.DeactivateOnExit<P2FrigidStone>()
.DeactivateOnExit<P2DiamondDustSafespots>();
ComponentCondition<P2IcicleImpact>(id + 0x33, 0.4f, comp => comp.NumCasts > 0, "Ice circle 1");
ComponentCondition<P2HeavenlyStrike>(id + 0x40, 3.9f, comp => comp.NumCasts > 0, "Knockback")
.ActivateOnEnter<P2HeavenlyStrike>()
.ActivateOnEnter<P2FrigidNeedleCircle>()
.ActivateOnEnter<P2FrigidNeedleCross>()
.ActivateOnEnter<P2SinboundHoly>()
.ActivateOnEnter<P2HeavenlyStrike>()
.ActivateOnEnter<P2TwinStillnessSilence>() // show the cone caster early, to simplify finding movement direction...
.ExecOnEnter<P2FrigidNeedleCircle>(comp => comp.Risky = false)
.ExecOnEnter<P2FrigidNeedleCross>(comp => comp.Risky = false)
.DeactivateOnExit<P2HeavenlyStrike>();
ComponentCondition<P2FrigidNeedleCross>(id + 0x50, 2.8f, comp => comp.NumCasts > 0, "Stars")
.ActivateOnEnter<P2SinboundHoly>()
.ExecOnEnter<P2FrigidNeedleCircle>(comp => comp.Risky = true)
.ExecOnEnter<P2FrigidNeedleCross>(comp => comp.Risky = true)
.DeactivateOnExit<P2FrigidNeedleCircle>()
.DeactivateOnExit<P2FrigidNeedleCross>();
ComponentCondition<P2SinboundHoly>(id + 0x60, 1.3f, comp => comp.NumCasts > 0);
Expand All @@ -243,6 +253,7 @@ private void P2DiamondDust(uint id, float delay)
.DeactivateOnExit<P2IcicleImpact>() // last icicle explodes together with first stack
.DeactivateOnExit<P2SinboundHoly>();
ComponentCondition<P2ShiningArmor>(id + 0x80, 3.7f, comp => comp.NumCasts > 0, "Gaze")
.ExecOnEnter<P2TwinStillnessSilence>(comp => comp.EnableAIHints())
.DeactivateOnExit<P2ShiningArmor>();
ComponentCondition<P2TwinStillnessSilence>(id + 0x90, 3.0f, comp => comp.AOEs.Count > 0);
ComponentCondition<P2TwinStillnessSilence>(id + 0x91, 3.5f, comp => comp.NumCasts > 0, "Front/back");
Expand Down Expand Up @@ -462,4 +473,35 @@ private void P3Apocalypse(uint id, float delay)

P3ShockwavePulsar(id + 0x1000, 0.3f);
}

private void P3Enrage(uint id, float delay)
{
ActorCast(id, _module.BossP3, AID.MemorysEnd, delay, 10, true, "Enrage");
ActorTargetable(id + 0x10, _module.BossP3, false, 3.5f, "Boss disappears")
.SetHint(StateMachine.StateHint.DowntimeStart);
}

private void P4AkhRhai(uint id, float delay)
{
ActorTargetable(id, _module.BossP4Usurper, true, delay, "Usurper appears")
.SetHint(StateMachine.StateHint.DowntimeEnd);
ActorCast(id + 0x10, _module.BossP4Usurper, AID.Materialization, 5.1f, 3, true);
ComponentCondition<P4AhkRhai>(id + 0x20, 11.2f, comp => comp.AOEs.Count > 0, "Puddle baits")
.ActivateOnEnter<P4AhkRhai>();
ComponentCondition<P4AhkRhai>(id + 0x30, 2.6f, comp => comp.NumCasts > 0);
ComponentCondition<P4EdgeOfOblivion>(id + 0x40, 2.4f, comp => comp.NumCasts > 0, "Raidwide")
.ActivateOnEnter<P4EdgeOfOblivion>()
.DeactivateOnExit<P4EdgeOfOblivion>()
.SetHint(StateMachine.StateHint.Raidwide);
ActorTargetable(id + 0x50, _module.BossP4Oracle, true, 1.2f, "Oracle appears");
ComponentCondition<P4AhkRhai>(id + 0x60, 1.6f, comp => comp.NumCasts >= 10 * comp.AOEs.Count, "Puddle resolve")
.DeactivateOnExit<P4AhkRhai>();
}

private void P4DarklitDragonsong(uint id, float delay)
{
ActorCast(id, _module.BossP4Usurper, AID.DarklitDragonsongUsurper, delay, 5, true, "Raidwide (darklit)")
.SetHint(StateMachine.StateHint.Raidwide);
// TODO: more...
}
}
5 changes: 1 addition & 4 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/P1FallOfFaith.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
var dest = TetherSpot(baitOrder);
if (_playerOrder[slot] != baitOrder)
dest += BaitOffset(_playerOrder[slot], _fireTethers[baitOrder - 1]);
// note: the baits need to be very precise
var destCellSize = 0.5f * Module.Bounds.MapResolution;
var destCellCenter = Module.Center + ((dest - Module.Center) / Module.Bounds.MapResolution).Floor() * Module.Bounds.MapResolution + new WDir(destCellSize, destCellSize);
hints.AddForbiddenZone(p => p.AlmostEqual(destCellCenter, destCellSize) ? 1 : -1);
hints.AddForbiddenZone(ShapeDistance.PrecisePosition(dest, new(0, 1), Module.Bounds.MapResolution, actor.Position, 0.1f));
}

public override PlayerPriority CalcPriority(int pcSlot, Actor pc, int playerSlot, Actor player, ref uint customColor)
Expand Down
11 changes: 0 additions & 11 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/P1UtopianSky.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,6 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
var assignedDirection = (180 - 45 * clockspot).Degrees();
hints.AddForbiddenZone(ShapeDistance.InvertedCone(Module.Center, 50, assignedDirection, 5.Degrees()), DateTime.MaxValue);
}

//if (assignment is PartyRolesConfig.Assignment.MT or PartyRolesConfig.Assignment.OT)
//{
// // adjust slightly to neighbouring tank slot
// var coTankSpot = _config.P1UtopianSkyInitialSpots[assignment == PartyRolesConfig.Assignment.MT ? PartyRolesConfig.Assignment.OT : PartyRolesConfig.Assignment.MT];
// if (coTankSpot == ((clockspot + 1) & 7))
// assignedDirection -= 4.Degrees();
// else if (coTankSpot == ((clockspot + 7) & 7))
// assignedDirection += 4.Degrees();
//}
//hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center + 18 * assignedDirection.ToDirection(), 1));
}
}

Expand Down
Loading

0 comments on commit 661f3eb

Please sign in to comment.