From c6c04f70d1320c09f566a55b1789f73dd8662247 Mon Sep 17 00:00:00 2001 From: Andrew Gilewsky Date: Fri, 3 Jan 2025 22:47:50 +0000 Subject: [PATCH 1/3] FRU mirror-mirror AI --- .../Dawntrail/Ultimate/FRU/FRUConfig.cs | 12 +++- .../Dawntrail/Ultimate/FRU/P2MirrorMirror.cs | 63 +++++++++++++++++-- .../Ultimate/FRU/P4CrystallizeTime.cs | 3 + 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUConfig.cs b/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUConfig.cs index 40e6be78f2..7fee41ace8 100644 --- a/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUConfig.cs +++ b/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUConfig.cs @@ -97,8 +97,18 @@ public class FRUConfig() : ConfigNode() [GroupPreset("Default", [0, 1, 6, 2, 5, 3, 7, 4])] public GroupAssignmentUnique P1UtopianSkyInitialSpots = new() { Assignments = [0, 1, 6, 2, 5, 3, 7, 4] }; - [PropertyDisplay("P1 Utopian Sky: spread spots (G1 CCW from N, G2 CW from NE", tooltip: "Only used by AI")] + [PropertyDisplay("P1 Utopian Sky: spread spots (G1 CCW from N, G2 CW from NE)", tooltip: "Only used by AI")] [GroupDetails(["G1 Close", "G1 Far Center", "G1 Far Left", "G1 Far Right", "G2 Close", "G2 Far Center", "G2 Far Left", "G2 Far Right"])] [GroupPreset("Default", [1, 5, 0, 4, 2, 6, 3, 7])] public GroupAssignmentUnique P1UtopianSkySpreadSpots = new() { Assignments = [1, 5, 0, 4, 2, 6, 3, 7] }; + + [PropertyDisplay("P2 Mirror Mirror: spread spots for first proteans (looking from boss toward blue mirror)", tooltip: "Only used by AI")] + [GroupDetails(["Boss opposite right", "Boss opposite left", "Boss side right", "Boss side left", "Mirror diagonal right", "Mirror diagonal left", "Mirror wall right", "Mirror wall left"])] + [GroupPreset("Default", [0, 1, 4, 5, 2, 3, 6, 7])] + public GroupAssignmentUnique P2MirrorMirror1SpreadSpots = new() { Assignments = [0, 1, 4, 5, 2, 3, 6, 7] }; + + [PropertyDisplay("P2 Mirror Mirror: spread spots for second proteans (looking toward red mirror, if both red mirrors are symmetrical assume CW rotation)", tooltip: "Only used by AI")] + [GroupDetails(["Boss wall right", "Boss wall left", "Boss center", "Boss diagonal", "Mirror wall right", "Mirror wall left", "Mirror center", "Mirror diagonal"])] + [GroupPreset("Default", [1, 0, 6, 7, 2, 3, 4, 5])] + public GroupAssignmentUnique P2MirrorMirror2SpreadSpots = new() { Assignments = [1, 0, 6, 7, 2, 3, 4, 5] }; } diff --git a/BossMod/Modules/Dawntrail/Ultimate/FRU/P2MirrorMirror.cs b/BossMod/Modules/Dawntrail/Ultimate/FRU/P2MirrorMirror.cs index 87da66847c..7ae54c518f 100644 --- a/BossMod/Modules/Dawntrail/Ultimate/FRU/P2MirrorMirror.cs +++ b/BossMod/Modules/Dawntrail/Ultimate/FRU/P2MirrorMirror.cs @@ -57,7 +57,9 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) class P2MirrorMirrorHouseOfLight(BossModule module) : Components.GenericBaitAway(module, ActionID.MakeSpell(AID.HouseOfLight)) { - private WPos _mirror; + private readonly FRUConfig _config = Service.Config.Get(); + private Angle? _blueMirror; + private int _numRedMirrors; private readonly List<(Actor source, DateTime activation)> _sources = []; private static readonly AOEShapeCone _shape = new(60, 15.Degrees()); @@ -72,15 +74,53 @@ public override void Update() public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - // TODO: preassigned spots - //base.AddAIHints(slot, actor, assignment, hints); + var group = (NumCasts == 0 ? _config.P2MirrorMirror1SpreadSpots : _config.P2MirrorMirror2SpreadSpots)[assignment]; + if (_sources.Count < 2 || _blueMirror == null || group < 0) + return; // inactive or no valid assignments + + var origin = _sources[group < 4 ? 0 : 1]; + Angle dir; + if (NumCasts == 0) + { + dir = _blueMirror.Value + (group & 3) switch + { + 0 => -135.Degrees(), + 1 => 135.Degrees(), + 2 => -90.Degrees(), + 3 => 90.Degrees(), + _ => default + }; + } + else + { + var offset = origin.source.Position - Module.Center; + dir = Angle.FromDirection(offset) + (group & 3) switch + { + 0 => -90.Degrees(), + 1 => 90.Degrees(), + 2 => 180.Degrees(), + 3 => offset.OrthoL().Dot(_sources[group < 4 ? 1 : 0].source.Position - Module.Center) < 0 ? 135.Degrees() : -135.Degrees(), + _ => default + }; + + // special logic for current tank: if mechanic will take a while to resolve, and boss is far enough away from the destination, and normal destination is on the same side as the boss, drag towards other side first + // this guarantees uptime for OT + if (origin.activation > WorldState.FutureTime(3) && Module.Enemies(OID.BossP2).FirstOrDefault() is var boss && boss != null && boss.TargetID == actor.InstanceID) + { + var dirVec = dir.ToDirection(); + if (dirVec.Dot(boss.Position - origin.source.Position) > 2 && (origin.source.Position - 4 * dirVec - boss.Position).Length() > boss.HitboxRadius + 3) + dir += 180.Degrees(); + } + + } + hints.AddForbiddenZone(ShapeDistance.InvertedCone(origin.source.Position, 4, dir, 15.Degrees()), origin.activation); } public override void OnEventEnvControl(byte index, uint state) { if (index is >= 1 and <= 8 && state == 0x00020001) { - _mirror = Module.Center + 20 * (225 - index * 45).Degrees().ToDirection(); + _blueMirror = (225 - index * 45).Degrees(); } } @@ -91,12 +131,25 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) case AID.ScytheKick: var activation = Module.CastFinishAt(spell, 0.7f); _sources.Add((caster, activation)); - var mirror = Module.Enemies(OID.FrozenMirror).Closest(_mirror); + var mirror = _blueMirror != null ? Module.Enemies(OID.FrozenMirror).Closest(Module.Center + 20 * _blueMirror.Value.ToDirection()) : null; if (mirror != null) _sources.Add((mirror, activation)); break; case AID.ReflectedScytheKickRed: _sources.Add((caster, Module.CastFinishAt(spell, 0.6f))); + if (++_numRedMirrors == 2 && _blueMirror != null && _sources.Count >= 2) + { + // order two red mirrors so that first one is closer to boss and second one closer to blue mirror; if both are same distance, select CW ones (arbitrary) + var d1 = (Angle.FromDirection(_sources[^2].source.Position - Module.Center) - _blueMirror.Value).Normalized(); + var d2 = (Angle.FromDirection(_sources[^1].source.Position - Module.Center) - _blueMirror.Value).Normalized(); + var d1abs = d1.Abs(); + var d2abs = d2.Abs(); + var swap = d2abs.AlmostEqual(d1abs, 0.1f) + ? d2.Rad > 0 // swap if currently second one is CCW from blue mirror + : d2abs.Rad > d1abs.Rad; // swap if currently second one is further from the blue mirror + if (swap) + (_sources[^1], _sources[^2]) = (_sources[^2], _sources[^1]); + } break; } } diff --git a/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs b/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs index 702be5885e..12acfe6f97 100644 --- a/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs +++ b/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs @@ -165,7 +165,10 @@ class P4CrystallizeTimeMaelstrom(BossModule module) : Components.GenericAOEs(mod public override void OnActorCreated(Actor actor) { if ((OID)actor.OID == OID.SorrowsHourglass) + { _aoes.Add(new(_shape, actor.Position, actor.Rotation, WorldState.FutureTime(13.2f))); + _aoes.SortBy(aoe => aoe.Activation); + } } public override void OnTethered(Actor source, ActorTetherInfo tether) From 716bbf0ecae43044359071c0012a33ed47277860 Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Sat, 4 Jan 2025 03:52:40 +0100 Subject: [PATCH 2/3] some fixes and slash command additions --- BossMod/AI/AIBehaviour.cs | 2 +- BossMod/AI/AIManagementWindow.cs | 53 ++--- BossMod/AI/AIManager.cs | 185 ++++++++++++++---- BossMod/Autorotation/PresetDatabase.cs | 14 +- BossMod/Config/ConfigUI.cs | 8 +- .../TreasureHunt/CenoteJaJaGural/Room1.cs | 2 +- .../SecretDjinn.cs | 4 +- .../SecretPegasus.cs | 5 +- 8 files changed, 193 insertions(+), 80 deletions(-) diff --git a/BossMod/AI/AIBehaviour.cs b/BossMod/AI/AIBehaviour.cs index 7b86e2b939..a19626d231 100644 --- a/BossMod/AI/AIBehaviour.cs +++ b/BossMod/AI/AIBehaviour.cs @@ -44,7 +44,7 @@ public async Task Execute(Actor player, Actor master) var hadNavi = _naviDecision.Destination != null; Targeting target = new(); - if (!forbidActions && (AIPreset != null || autorot.Preset != null)) + if (!forbidActions && AIPreset != null && (!_config.ForbidAIMovementMounted || _config.ForbidAIMovementMounted && player.MountId == 0)) { target = SelectPrimaryTarget(player, master); if (_config.ManualTarget) diff --git a/BossMod/AI/AIManagementWindow.cs b/BossMod/AI/AIManagementWindow.cs index 208b63ab0e..8c1e805564 100644 --- a/BossMod/AI/AIManagementWindow.cs +++ b/BossMod/AI/AIManagementWindow.cs @@ -11,7 +11,6 @@ sealed class AIManagementWindow : UIWindow private readonly EventSubscriptions _subscriptions; private const string _title = $"AI: off{_windowID}"; private const string _windowID = "###AI debug window"; - DateTime? saveConfigIn; public AIManagementWindow(AIManager manager) : base(_windowID, false, new(100, 100)) { @@ -79,11 +78,9 @@ public override void Draw() { if (ImGui.Selectable(p.Name, _manager.MasterSlot == i)) { - var cfg = _config.FollowSlot; _manager.SwitchToFollow(i); _config.FollowSlot = i; - if (cfg != _config.FollowSlot) - configModified = true; + configModified = true; } } ImGui.EndCombo(); @@ -96,10 +93,8 @@ public override void Draw() var positionalIndex = (int)_config.DesiredPositional; if (ImGui.Combo("##DesiredPositional", ref positionalIndex, positionalOptions, positionalOptions.Length)) { - var cfg = _config.DesiredPositional; _config.DesiredPositional = (Positional)positionalIndex; - if (cfg != _config.DesiredPositional) - configModified = true; + configModified = true; } ImGui.SameLine(); ImGui.Text("Max distance - to targets"); @@ -111,10 +106,8 @@ public override void Draw() maxDistanceTargetStr = maxDistanceTargetStr.Replace(',', '.'); if (float.TryParse(maxDistanceTargetStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var maxDistance)) { - var cfg = _config.MaxDistanceToTarget; _config.MaxDistanceToTarget = maxDistance; - if (cfg != _config.MaxDistanceToTarget) - configModified = true; + configModified = true; } } ImGui.SameLine(); @@ -127,10 +120,8 @@ public override void Draw() maxDistanceSlotStr = maxDistanceSlotStr.Replace(',', '.'); if (float.TryParse(maxDistanceSlotStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var maxDistance)) { - var cfg = _config.MaxDistanceToSlot; _config.MaxDistanceToSlot = maxDistance; - if (cfg != _config.MaxDistanceToTarget) - configModified = true; + configModified = true; } } @@ -143,10 +134,8 @@ public override void Draw() movementDelayStr = movementDelayStr.Replace(',', '.'); if (float.TryParse(movementDelayStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var delay)) { - var cfg = _config.MoveDelay; _config.MoveDelay = delay; - if (cfg != _config.MoveDelay) - configModified = true; + configModified = true; } } ImGui.SameLine(); @@ -156,38 +145,38 @@ public override void Draw() ImGui.SetNextWindowSizeConstraints(default, new Vector2(float.MaxValue, ImGui.GetTextLineHeightWithSpacing() * 50)); var aipreset = _config.AIAutorotPresetName; var presets = _manager.Autorot.Database.Presets.VisiblePresets; - var presetNames = presets.Select(p => p.Name).ToList(); + + var count = presets.Count; + List presetNames = new(count + 1); + for (var i = 0; i < count; ++i) + { + presetNames.Add(presets[i].Name); + } + if (aipreset != null) presetNames.Add("Deactivate"); - + var countnames = presetNames.Count; var selectedIndex = presetNames.IndexOf(aipreset ?? ""); - if (selectedIndex == -1 && _manager.AiPreset == null) - selectedIndex = -1; - if (ImGui.Combo("##AI preset", ref selectedIndex, [.. presetNames], presetNames.Count)) + + if (ImGui.Combo("##AI preset", ref selectedIndex, [.. presetNames], countnames)) { - var cfg = _config.AIAutorotPresetName; - if (selectedIndex == presetNames.Count - 1 && aipreset != null) + if (selectedIndex == countnames - 1 && aipreset != null) { _manager.SetAIPreset(null); _config.AIAutorotPresetName = null; + configModified = true; selectedIndex = -1; } - else if (selectedIndex >= 0 && selectedIndex < presets.Count()) + else if (selectedIndex >= 0 && selectedIndex < count) { - var selectedPreset = presets.ElementAt(selectedIndex); + var selectedPreset = presets[selectedIndex]; _manager.SetAIPreset(selectedPreset); _config.AIAutorotPresetName = selectedPreset.Name; - } - if (cfg != _config.AIAutorotPresetName) configModified = true; + } } if (configModified) - saveConfigIn = DateTime.Now.AddSeconds(10); // delay config saving to potentially save multiple setting changes in a batch - if (saveConfigIn <= DateTime.Now) - { _config.Modified.Fire(); - saveConfigIn = null; - } } public override void OnClose() => SetVisible(false); diff --git a/BossMod/AI/AIManager.cs b/BossMod/AI/AIManager.cs index e534e42d74..fb16db92cd 100644 --- a/BossMod/AI/AIManager.cs +++ b/BossMod/AI/AIManager.cs @@ -118,62 +118,94 @@ private void OnCommand(string cmd, string message) configModified = ToggleFocusTargetLeader(); break; case "FOLLOW": - var cfg = _config.FollowSlot; + var cfgFollowSlot = _config.FollowSlot; HandleFollowCommand(messageData); - configModified = cfg != _config.FollowSlot; + configModified = cfgFollowSlot != _config.FollowSlot; break; case "UI": configModified = ToggleDebugMenu(); break; case "FORBIDACTIONS": - configModified = ToggleForbidActions(messageData); + var cfgForbidActions = _config.ForbidActions; + ToggleForbidActions(messageData); + configModified = cfgForbidActions != _config.ForbidActions; break; - case "FORDBIDMOVEMENT": - configModified = ToggleForbidMovement(messageData); + case "FORBIDMOVEMENT": + var cfgForbidMovement = _config.ForbidMovement; + ToggleForbidMovement(messageData); + configModified = cfgForbidMovement != _config.ForbidMovement; + break; + case "FORBIDMOUNTEDMOVEMENT": + var cfgForbidMMovement = _config.ForbidAIMovementMounted; + ToggleForbidMovement(messageData); + configModified = cfgForbidMMovement != _config.ForbidAIMovementMounted; break; case "FOLLOWOUTOFCOMBAT": - configModified = ToggleFollowOutOfCombat(messageData); + var cfgFollowOOC = _config.FollowOutOfCombat; + ToggleFollowOutOfCombat(messageData); + configModified = cfgFollowOOC != _config.FollowOutOfCombat; break; case "FOLLOWCOMBAT": - configModified = ToggleFollowCombat(messageData); + var cfgFollowIC = _config.FollowDuringCombat; + ToggleFollowCombat(messageData); + configModified = cfgFollowIC != _config.FollowDuringCombat; break; case "FOLLOWMODULE": - configModified = ToggleFollowModule(messageData); + var cfgFollowM = _config.FollowDuringActiveBossModule; + ToggleFollowModule(messageData); + configModified = cfgFollowM != _config.FollowDuringActiveBossModule; break; case "FOLLOWTARGET": - configModified = ToggleFollowTarget(messageData); + var cfgFollowT = _config.FollowTarget; + ToggleFollowTarget(messageData); + configModified = cfgFollowT != _config.FollowTarget; + break; + case "OUTOFBOUNDS": + var cfgOOB = _config.AllowAIToBeOutsideBounds; + ToggleOutOfBounds(messageData); + configModified = cfgOOB != _config.AllowAIToBeOutsideBounds; break; case "OVERRIDEAUTOROTATION": - configModified = ToggleAutorotationOverride(); + var cfgARO = _config.OverrideAutorotation; + ToggleAutorotationOverride(messageData); + configModified = cfgARO != _config.OverrideAutorotation; break; case "POSITIONAL": - var cfg2 = _config.DesiredPositional; + var cfgPositional = _config.DesiredPositional; HandlePositionalCommand(messageData); - configModified = cfg2 != _config.DesiredPositional; + configModified = cfgPositional != _config.DesiredPositional; break; case "MAXDISTANCETARGET": - var cfg3 = _config.MaxDistanceToTarget; + var cfgMDT = _config.MaxDistanceToTarget; HandleMaxDistanceTargetCommand(messageData); - configModified = cfg3 != _config.MaxDistanceToTarget; + configModified = cfgMDT != _config.MaxDistanceToTarget; break; case "MAXDISTANCESLOT": - var cfg4 = _config.MaxDistanceToSlot; + var cfgMDS = _config.MaxDistanceToSlot; HandleMaxDistanceSlotCommand(messageData); - configModified = cfg4 != _config.MaxDistanceToSlot; + configModified = cfgMDS != _config.MaxDistanceToSlot; + break; + case "MOVEDELAY": + var cfgMD = _config.MoveDelay; + HandleMoveDelayCommand(messageData); + configModified = cfgMD != _config.MoveDelay; break; case "SETPRESETNAME": if (cmd.Length <= 2) + { Service.Log("Specify an AI autorotation preset name."); + return; + } else { - var cfg5 = _config.AIAutorotPresetName; + var cfgARPreset = _config.AIAutorotPresetName; ParseAIAutorotationSetCommand(messageData); - configModified = cfg5 != _config.AIAutorotPresetName; + configModified = cfgARPreset != _config.AIAutorotPresetName; } break; default: Service.ChatGui.Print($"[AI] Unknown command: {messageData[0]}"); - break; + return; } if (configModified) @@ -202,10 +234,70 @@ private bool ToggleFocusTargetLeader() return true; } - private bool ToggleAutorotationOverride() + private void ToggleAutorotationOverride(string[] messageData) { - _config.OverrideAutorotation = !_config.OverrideAutorotation; - return true; + if (messageData.Length == 1) + _config.OverrideAutorotation = !_config.OverrideAutorotation; + else + { + switch (messageData[1].ToUpperInvariant()) + { + case "ON": + _config.OverrideAutorotation = true; + break; + case "OFF": + _config.OverrideAutorotation = false; + break; + default: + Service.ChatGui.Print($"[AI] Unknown follow target command: {messageData[1]}"); + return; + } + } + Service.Log($"[AI] Following targets is now {(_config.OverrideAutorotation ? "enabled" : "disabled")}"); + } + + private void ToggleOutOfBounds(string[] messageData) + { + if (messageData.Length == 1) + _config.AllowAIToBeOutsideBounds = !_config.AllowAIToBeOutsideBounds; + else + { + switch (messageData[1].ToUpperInvariant()) + { + case "ON": + _config.AllowAIToBeOutsideBounds = true; + break; + case "OFF": + _config.AllowAIToBeOutsideBounds = false; + break; + default: + Service.ChatGui.Print($"[AI] Unknown follow target command: {messageData[1]}"); + return; + } + } + Service.Log($"[AI] Following targets is now {(_config.AllowAIToBeOutsideBounds ? "enabled" : "disabled")}"); + } + + private void ToggleMountedMovement(string[] messageData) + { + if (messageData.Length == 1) + _config.ForbidAIMovementMounted = !_config.ForbidAIMovementMounted; + else + { + switch (messageData[1].ToUpperInvariant()) + { + case "ON": + _config.ForbidAIMovementMounted = true; + break; + case "OFF": + _config.ForbidAIMovementMounted = false; + break; + default: + Service.ChatGui.Print($"[AI] Unknown forbid mounted movement command: {messageData[1]}"); + return; + } + } + Service.Log($"[AI] Forbid mounted movement is now {(_config.ForbidAIMovementMounted ? "enabled" : "disabled")}"); } private void HandleFollowCommand(string[] messageData) @@ -239,7 +331,7 @@ private bool ToggleDebugMenu() return true; } - private bool ToggleForbidActions(string[] messageData) + private void ToggleForbidActions(string[] messageData) { if (messageData.Length == 1) _config.ForbidActions = !_config.ForbidActions; @@ -255,14 +347,13 @@ private bool ToggleForbidActions(string[] messageData) break; default: Service.ChatGui.Print($"[AI] Unknown forbid actions command: {messageData[1]}"); - return _config.ForbidActions; + return; } } Service.Log($"[AI] Forbid actions is now {(_config.ForbidActions ? "enabled" : "disabled")}"); - return _config.ForbidActions; } - private bool ToggleForbidMovement(string[] messageData) + private void ToggleForbidMovement(string[] messageData) { if (messageData.Length == 1) _config.ForbidMovement = !_config.ForbidMovement; @@ -278,14 +369,13 @@ private bool ToggleForbidMovement(string[] messageData) break; default: Service.ChatGui.Print($"[AI] Unknown forbid movement command: {messageData[1]}"); - return _config.ForbidMovement; + return; } } Service.Log($"[AI] Forbid movement is now {(_config.ForbidMovement ? "enabled" : "disabled")}"); - return _config.ForbidMovement; } - private bool ToggleFollowOutOfCombat(string[] messageData) + private void ToggleFollowOutOfCombat(string[] messageData) { if (messageData.Length == 1) _config.FollowOutOfCombat = !_config.FollowOutOfCombat; @@ -301,14 +391,13 @@ private bool ToggleFollowOutOfCombat(string[] messageData) break; default: Service.ChatGui.Print($"[AI] Unknown follow out of combat command: {messageData[1]}"); - return _config.FollowOutOfCombat; + return; } } Service.Log($"[AI] Follow out of combat is now {(_config.FollowOutOfCombat ? "enabled" : "disabled")}"); - return _config.FollowOutOfCombat; } - private bool ToggleFollowCombat(string[] messageData) + private void ToggleFollowCombat(string[] messageData) { if (messageData.Length == 1) { @@ -333,15 +422,14 @@ private bool ToggleFollowCombat(string[] messageData) break; default: Service.ChatGui.Print($"[AI] Unknown follow during combat command: {messageData[1]}"); - return _config.FollowDuringCombat; + return; } } Service.Log($"[AI] Follow during combat is now {(_config.FollowDuringCombat ? "enabled" : "disabled")}"); Service.Log($"[AI] Follow during active boss module is now {(_config.FollowDuringActiveBossModule ? "enabled" : "disabled")}"); - return _config.FollowDuringCombat; } - private bool ToggleFollowModule(string[] messageData) + private void ToggleFollowModule(string[] messageData) { if (messageData.Length == 1) { @@ -362,15 +450,14 @@ private bool ToggleFollowModule(string[] messageData) break; default: Service.ChatGui.Print($"[AI] Unknown follow during active boss module command: {messageData[1]}"); - return _config.FollowDuringActiveBossModule; + return; } } Service.Log($"[AI] Follow during active boss module is now {(_config.FollowDuringActiveBossModule ? "enabled" : "disabled")}"); Service.Log($"[AI] Follow during combat is now {(_config.FollowDuringCombat ? "enabled" : "disabled")}"); - return _config.FollowDuringActiveBossModule; } - private bool ToggleFollowTarget(string[] messageData) + private void ToggleFollowTarget(string[] messageData) { if (messageData.Length == 1) _config.FollowTarget = !_config.FollowTarget; @@ -386,11 +473,10 @@ private bool ToggleFollowTarget(string[] messageData) break; default: Service.ChatGui.Print($"[AI] Unknown follow target command: {messageData[1]}"); - return _config.FollowTarget; + return; } } Service.Log($"[AI] Following targets is now {(_config.FollowTarget ? "enabled" : "disabled")}"); - return _config.FollowTarget; } private void HandlePositionalCommand(string[] messageData) @@ -458,6 +544,25 @@ private void HandleMaxDistanceSlotCommand(string[] messageData) Service.Log($"[AI] Max distance to slot set to {distance}"); } + private void HandleMoveDelayCommand(string[] messageData) + { + if (messageData.Length < 2) + { + Service.ChatGui.Print("[AI] Missing delay value."); + return; + } + + var moveStr = messageData[1].Replace(',', '.'); + if (!float.TryParse(moveStr, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var delay)) + { + Service.ChatGui.Print("[AI] Invalid delay value."); + return; + } + + _config.MoveDelay = delay; + Service.Log($"[AI] Max distance to target set to {delay}"); + } + private int FindPartyMemberByName(string name) { for (var i = 0; i < 8; ++i) diff --git a/BossMod/Autorotation/PresetDatabase.cs b/BossMod/Autorotation/PresetDatabase.cs index 14b3b68274..30fc398cff 100644 --- a/BossMod/Autorotation/PresetDatabase.cs +++ b/BossMod/Autorotation/PresetDatabase.cs @@ -14,7 +14,19 @@ public sealed class PresetDatabase private readonly FileInfo _dbPath; - public IEnumerable VisiblePresets => _cfg.HideDefaultPreset ? UserPresets : DefaultPresets.Concat(UserPresets); + public List VisiblePresets + { + get + { + if (_cfg.HideDefaultPreset) + { + return UserPresets; + } + + List combinedPresets = [.. DefaultPresets, .. UserPresets]; + return combinedPresets; + } + } public PresetDatabase(string rootPath, FileInfo defaultPresets) { diff --git a/BossMod/Config/ConfigUI.cs b/BossMod/Config/ConfigUI.cs index 175298132e..465e5c26fe 100644 --- a/BossMod/Config/ConfigUI.cs +++ b/BossMod/Config/ConfigUI.cs @@ -88,6 +88,8 @@ public void Draw() { "forbidactions on/off", "Sets forbid actions to on or off. (only for autorotation)" }, { "forbidmovement", "Toggles the forbidding of movement." }, { "forbidmovement on/off", "Sets forbid movement to on or off." }, + { "forbidmountedmovement", "Toggles the forbidding of mounted movement." }, + { "forbidmountedmovement on/off", "Sets forbid mounted movement to on or off." }, { "followcombat", "Toggles following during combat." }, { "followcombat on/off", "Sets following following during combat to on or off." }, { "followmodule", "Toggles following during active boss module." }, @@ -99,7 +101,11 @@ public void Draw() { "positional X", "Switch to positional when following targets. (any, rear, flank, front)" }, { "maxdistancetarget X", "Sets max distance to target. (default = 2.6)" }, { "maxdistanceslot X", "Sets max distance to slot. (default = 1)" }, - { "overrideautorotation", "Overrides autorotation distance and positional values with custom settings." }, + { "movedelay X", "Sets AI movement decision delay. (default = 0)" }, + { "overrideautorotation", "Toggles the override for autorotation distance and positional values with custom settings." }, + { "overrideautorotation on/off", "Sets the override for autorotation distance and positional values with custom settings to on or off." }, + { "outofbounds", "Toggles the allow AI to be out of arena bounds setting." }, + { "outofbounds on/off", "Sets the allow AI to be out of arena bounds setting to on or off." }, { "setpresetname X", "Sets an autorotation preset for the AI, eg. setpresetname vbm default." } }; diff --git a/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/Room1.cs b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/Room1.cs index 371ba3154b..5e1967ddac 100644 --- a/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/Room1.cs +++ b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/Room1.cs @@ -58,7 +58,7 @@ public enum AID : uint class CreepingHush(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.CreepingHush), new AOEShapeCone(12, 60.Degrees())); class Ovation(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Ovation), new AOEShapeRect(14, 2)); class BestialFire(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.BestialFire), 5); -class HeadButt(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.CreepingHush), new AOEShapeCone(6, 60.Degrees())); +class HeadButt(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HeadButt), new AOEShapeCone(6, 60.Degrees())); class AetherialBlast(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.AetherialBlast), new AOEShapeRect(20, 2)); class Envenomate(BossModule module) : Components.BaitAwayChargeCast(module, ActionID.MakeSpell(AID.Envenomate), 1.5f); class SyrupSpout(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SyrupSpout), new AOEShapeCone(10, 60.Degrees())); diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs index 66f5ac833b..80b5929406 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs @@ -30,8 +30,8 @@ public enum AID : uint class Gust(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Gust), 6); class ChangelessWinds(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ChangelessWinds), new AOEShapeRect(40, 4)); class ChangelessWindsKB(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.ChangelessWinds), 10, shape: new AOEShapeRect(40, 4), kind: Kind.DirForward, stopAtWall: true); -class Whipwind(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Whipwind), new AOEShapeRect(54, 20, 1)); -class WhipwindKB(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.Whipwind), 25, shape: new AOEShapeRect(55, 20, 1), kind: Kind.DirForward, stopAtWall: true); +class Whipwind(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Whipwind), new AOEShapeRect(54, 20, 0.5f)); +class WhipwindKB(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.Whipwind), 25, shape: new AOEShapeRect(55, 20, 0.5f), kind: Kind.DirForward, stopAtWall: true); class GentleBreeze(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GentleBreeze), new AOEShapeRect(15, 2)); class WhirlingGaol(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.WhirlingGaol), "Raidwide + Knockback"); class WhirlingGaolKB(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.WhirlingGaol), 25, stopAtWall: true); diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs index 3c61f9c784..d71eb935c3 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs @@ -10,6 +10,7 @@ public enum OID : uint SecretTomato = 0x3020, // R0.84, icon 4, needs to be killed in order from 1 to 5 for maximum rewards SecretOnion = 0x301D, // R0.84, icon 1, needs to be killed in order from 1 to 5 for maximum rewards SecretEgg = 0x301E, // R0.84, icon 2, needs to be killed in order from 1 to 5 for maximum rewards + FuathTrickster = 0x3033, // R0.75 Helper = 0x233C } @@ -77,7 +78,7 @@ public SecretPegasusStates(BossModule module) : base(module) public class SecretPegasus(WorldState ws, Actor primary) : THTemplate(ws, primary) { private static readonly uint[] bonusAdds = [(uint)OID.SecretEgg, (uint)OID.SecretGarlic, (uint)OID.SecretOnion, (uint)OID.SecretTomato, - (uint)OID.SecretQueen, (uint)OID.KeeperOfKeys]; + (uint)OID.SecretQueen, (uint)OID.KeeperOfKeys, (uint)OID.FuathTrickster]; public static readonly uint[] All = [(uint)OID.Boss, .. bonusAdds]; protected override void DrawEnemies(int pcSlot, Actor pc) @@ -96,7 +97,7 @@ protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRoles OID.SecretOnion => 5, OID.SecretEgg => 4, OID.SecretGarlic => 3, - OID.SecretTomato => 2, + OID.SecretTomato or OID.FuathTrickster => 2, OID.SecretQueen or OID.KeeperOfKeys => 1, _ => 0 }; From 17e85b9cdac07c197b284235cba97145d0121683 Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Sat, 4 Jan 2025 04:11:32 +0100 Subject: [PATCH 3/3] renamed setting --- BossMod/AI/AIConfig.cs | 2 +- BossMod/AI/AIManagementWindow.cs | 2 +- BossMod/AI/AIManager.cs | 14 +++++++------- BossMod/Config/ConfigUI.cs | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/BossMod/AI/AIConfig.cs b/BossMod/AI/AIConfig.cs index 921a22d8be..e11374a8dd 100644 --- a/BossMod/AI/AIConfig.cs +++ b/BossMod/AI/AIConfig.cs @@ -64,7 +64,7 @@ sealed class AIConfig : ConfigNode [PropertyDisplay("Movement decision delay", tooltip: "Only change this at your own risk and keep this value low! Too high and it won't move in time for some mechanics. Make sure to readjust the value for different content.")] public float MoveDelay = 0; - [PropertyDisplay("Forbid AI movement while mounted")] + [PropertyDisplay("Idle while mounted")] public bool ForbidAIMovementMounted = false; public string? AIAutorotPresetName; diff --git a/BossMod/AI/AIManagementWindow.cs b/BossMod/AI/AIManagementWindow.cs index 8c1e805564..c86f72bf31 100644 --- a/BossMod/AI/AIManagementWindow.cs +++ b/BossMod/AI/AIManagementWindow.cs @@ -50,7 +50,7 @@ public override void Draw() ImGui.SameLine(); configModified |= ImGui.Checkbox("Forbid movement", ref _config.ForbidMovement); ImGui.SameLine(); - configModified |= ImGui.Checkbox("Forbid mounted movement", ref _config.ForbidAIMovementMounted); + configModified |= ImGui.Checkbox("Idle while mounted", ref _config.ForbidAIMovementMounted); ImGui.SameLine(); configModified |= ImGui.Checkbox("Follow during combat", ref _config.FollowDuringCombat); ImGui.Spacing(); diff --git a/BossMod/AI/AIManager.cs b/BossMod/AI/AIManager.cs index fb16db92cd..ad227dbdd0 100644 --- a/BossMod/AI/AIManager.cs +++ b/BossMod/AI/AIManager.cs @@ -135,10 +135,10 @@ private void OnCommand(string cmd, string message) ToggleForbidMovement(messageData); configModified = cfgForbidMovement != _config.ForbidMovement; break; - case "FORBIDMOUNTEDMOVEMENT": - var cfgForbidMMovement = _config.ForbidAIMovementMounted; - ToggleForbidMovement(messageData); - configModified = cfgForbidMMovement != _config.ForbidAIMovementMounted; + case "IDLEWHILEMOUNTED": + var cfgMountedIdle = _config.ForbidAIMovementMounted; + ToggleIdleWhileMounted(messageData); + configModified = cfgMountedIdle != _config.ForbidAIMovementMounted; break; case "FOLLOWOUTOFCOMBAT": var cfgFollowOOC = _config.FollowOutOfCombat; @@ -278,7 +278,7 @@ private void ToggleOutOfBounds(string[] messageData) Service.Log($"[AI] Following targets is now {(_config.AllowAIToBeOutsideBounds ? "enabled" : "disabled")}"); } - private void ToggleMountedMovement(string[] messageData) + private void ToggleIdleWhileMounted(string[] messageData) { if (messageData.Length == 1) _config.ForbidAIMovementMounted = !_config.ForbidAIMovementMounted; @@ -293,11 +293,11 @@ private void ToggleMountedMovement(string[] messageData) _config.ForbidAIMovementMounted = false; break; default: - Service.ChatGui.Print($"[AI] Unknown forbid mounted movement command: {messageData[1]}"); + Service.ChatGui.Print($"[AI] Unknown idle while mounted command: {messageData[1]}"); return; } } - Service.Log($"[AI] Forbid mounted movement is now {(_config.ForbidAIMovementMounted ? "enabled" : "disabled")}"); + Service.Log($"[AI] Idle while mounted is now {(_config.ForbidAIMovementMounted ? "enabled" : "disabled")}"); } private void HandleFollowCommand(string[] messageData) diff --git a/BossMod/Config/ConfigUI.cs b/BossMod/Config/ConfigUI.cs index 465e5c26fe..eacdd0405e 100644 --- a/BossMod/Config/ConfigUI.cs +++ b/BossMod/Config/ConfigUI.cs @@ -88,8 +88,8 @@ public void Draw() { "forbidactions on/off", "Sets forbid actions to on or off. (only for autorotation)" }, { "forbidmovement", "Toggles the forbidding of movement." }, { "forbidmovement on/off", "Sets forbid movement to on or off." }, - { "forbidmountedmovement", "Toggles the forbidding of mounted movement." }, - { "forbidmountedmovement on/off", "Sets forbid mounted movement to on or off." }, + { "idlewhilemounted", "Toggles the idling while mounted." }, + { "idlewhilemounted on/off", "Sets idle while mounted to on or off." }, { "followcombat", "Toggles following during combat." }, { "followcombat on/off", "Sets following following during combat to on or off." }, { "followmodule", "Toggles following during active boss module." },