Skip to content

Commit

Permalink
Window stuff.
Browse files Browse the repository at this point in the history
  • Loading branch information
awgil committed Oct 14, 2024
1 parent fc282ed commit a4b34ec
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 156 deletions.
1 change: 1 addition & 0 deletions BossMod/AI/AIConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public enum Slot { One, Two, Three, Four }
public bool ShowDTR = true;

// ai settings
// TODO: this is really bad, it should not be here! it's a transient thing, doesn't make sense to preserve in config
[PropertyDisplay($"Follow slot")]
public Slot FollowSlot = 0;

Expand Down
160 changes: 21 additions & 139 deletions BossMod/AI/AIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,32 @@
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Client.Game.Group;
using ImGuiNET;

namespace BossMod.AI;

sealed class AIManager : IDisposable
{
private readonly RotationModuleManager _autorot;
private readonly AIController _controller;
private readonly AIConfig _config;
private int MasterSlot => (int)_config.FollowSlot; // non-zero means corresponding player is master
private readonly UISimpleWindow _ui;
private WorldState WorldState => _autorot.Bossmods.WorldState;
private string _aiStatus = "";
private string _naviStatus = "";

public float ForceMovementIn => Behaviour?.ForceMovementIn ?? float.MaxValue;
public readonly AIConfig Config = Service.Config.Get<AIConfig>();
public AIBehaviour? Behaviour { get; private set; }
public string AIStatus { get; private set; } = "";
public string NaviStatus { get; private set; } = "";

public int MasterSlot => (int)Config.FollowSlot; // non-zero means corresponding player is master
public WorldState WorldState => _autorot.Bossmods.WorldState;
public float ForceMovementIn => Behaviour?.ForceMovementIn ?? float.MaxValue;

private bool Enabled
// TODO: this is not good, callers should use SwitchToXXX directly
public bool Enabled
{
get => _config.Enabled;
get => Config.Enabled;
set
{
if (_config.Enabled != value)
_config.Enabled = value;
if (Config.Enabled != value)
Config.Enabled = value;

if (!value && Behaviour != null)
SwitchToIdle();
Expand All @@ -42,34 +41,23 @@ public AIManager(RotationModuleManager autorot, ActionManagerEx amex, MovementOv
{
_autorot = autorot;
_controller = new(amex, movement);
_config = Service.Config.Get<AIConfig>();
_ui = new("###AI", DrawOverlay, false, new(100, 100), ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoFocusOnAppearing)
{
RespectCloseHotkey = false,
ShowCloseButton = false,
WindowName = $"AI: off###AI",
TitleBarButtons = [new() { Icon = FontAwesomeIcon.WindowClose, IconOffset = new(1, 1), Click = _ => _config.DrawUI = false }]
};
Service.ChatGui.ChatMessage += OnChatMessage;
Service.CommandManager.AddHandler("/vbmai", new Dalamud.Game.Command.CommandInfo(OnCommand) { HelpMessage = "Toggle AI mode" });
}

public void Dispose()
{
SwitchToIdle();
_ui.Dispose();
Service.ChatGui.ChatMessage -= OnChatMessage;
Service.CommandManager.RemoveHandler("/vbmai");
}

public void Update()
{
Enabled = _config.Enabled;
Enabled = Config.Enabled;

if (!WorldState.Party.Members[MasterSlot].IsValid())
SwitchToIdle();

if (!_config.Enabled && Behaviour != null)
if (!Config.Enabled && Behaviour != null)
SwitchToIdle();

var player = WorldState.Party.Player();
Expand All @@ -85,58 +73,26 @@ public void Update()
}

_controller.Update(player, _autorot.Hints, WorldState.CurrentTime);
_aiStatus = $"AI: {(Behaviour != null ? $"on, {$"master={master?.Name}[{(int)_config.FollowSlot + 1}]"}" : "off")}";
AIStatus = $"AI: {(Behaviour != null ? $"on, {$"master={master?.Name}[{(int)Config.FollowSlot + 1}]"}" : "off")}";
var dist = _controller.NaviTargetPos != null && player != null ? (_controller.NaviTargetPos.Value - player.Position).Length() : 0;
_naviStatus = $"Navi={_controller.NaviTargetPos?.ToString() ?? "<none>"} (d={dist:f3}, max-cast={MathF.Min(Behaviour?.ForceMovementIn ?? float.MaxValue, 1000):f3})";
_ui.IsOpen = player != null && _config.DrawUI;
_ui.WindowName = _config.ShowStatusOnTitlebar ? $"{_aiStatus}, {_naviStatus}###AI" : $"AI###AI";
}

private void DrawOverlay()
{
if (!_config.ShowStatusOnTitlebar)
{
ImGui.TextUnformatted(_aiStatus);
ImGui.TextUnformatted(_naviStatus);
}
Behaviour?.DrawDebug();

using (var leaderCombo = ImRaii.Combo("Follow", Behaviour == null ? "<idle>" : WorldState.Party[MasterSlot]?.Name ?? "<unknown>"))
{
if (leaderCombo)
{
if (ImGui.Selectable("<idle>", Behaviour == null))
{
Enabled = false;
}
foreach (var (i, p) in WorldState.Party.WithSlot(true))
{
if (ImGui.Selectable(p.Name, MasterSlot == i))
{
_config.FollowSlot = (AIConfig.Slot)i;
_config.Modified.Fire();
Enabled = true;
}
}
}
}
NaviStatus = $"Navi={_controller.NaviTargetPos?.ToString() ?? "<none>"} (d={dist:f3}, max-cast={MathF.Min(Behaviour?.ForceMovementIn ?? float.MaxValue, 1000):f3})";
}

public void SwitchToIdle()
{
Behaviour?.Dispose();
Behaviour = null;

_config.FollowSlot = PartyState.PlayerSlot;
_config.Modified.Fire();
Config.FollowSlot = PartyState.PlayerSlot;
Config.Modified.Fire();
_controller.Clear();
}

public void SwitchToFollow(int masterSlot)
{
SwitchToIdle();
_config.FollowSlot = (AIConfig.Slot)masterSlot;
_config.Modified.Fire();
Config.FollowSlot = (AIConfig.Slot)masterSlot;
Config.Modified.Fire();
Behaviour = new AIBehaviour(_controller, _autorot);
}

Expand All @@ -159,7 +115,7 @@ private unsafe int FindPartyMemberSlotFromSender(SeString sender)

private void OnChatMessage(XivChatType type, int timestamp, ref SeString sender, ref SeString message, ref bool isHandled)
{
if (!_config.Enabled || type != XivChatType.Party)
if (!Config.Enabled || type != XivChatType.Party)
return;

var messagePrefix = message.Payloads.FirstOrDefault() as TextPayload;
Expand All @@ -185,78 +141,4 @@ private void OnChatMessage(XivChatType type, int timestamp, ref SeString sender,
break;
}
}

private void OnCommand(string cmd, string message)
{
var messageData = message.Split(' ');
switch (messageData[0])
{
case "on":
Enabled = true;
break;
case "off":
Enabled = false;
break;
case "toggle":
Enabled ^= true;
break;
case "follow":
if (messageData.Length < 2)
{
Service.Log($"[AI] [Follow] Usage: /vbmai follow name");
return;
}

var masterString = messageData.Length > 2 ? $"{messageData[1]} {messageData[2]}" : messageData[1];
var masterStringIsSlot = masterString[..4].Equals("slot", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt32(masterString.Substring(4, 1)) : 0;

var master = masterStringIsSlot > 0 ? (masterStringIsSlot - 1, WorldState.Party[masterStringIsSlot - 1]) : WorldState.Party.WithSlot().FirstOrDefault(x => x.Item2.Name.Equals(masterString, StringComparison.OrdinalIgnoreCase));

if (master.Item2 is null)
{
Service.Log($"[AI] [Follow] Error: can't find {masterString} in our party");
return;
}

_config.FollowSlot = (AIConfig.Slot)master.Item1;
_config.Modified.Fire();
Enabled = true;

break;
case "ui":
_config.DrawUI ^= true;
_config.Modified.Fire();
break;
default:
List<string> list = [];
list.Add("AIConfig");
list.AddRange(messageData);

if (list.Count == 2)
{
//toggle
var result = Service.Config.ConsoleCommand(list);
if (bool.TryParse(result[0], out var resultBool))
{
list.Add((!resultBool).ToString());
Service.Config.ConsoleCommand(list);
}
else
Service.Log($"[AI] Unknown command: {messageData[0]}");
}
else if (list.Count == 3)
{
//set
var onOffReplace = list[2].Replace("on", "true", StringComparison.InvariantCultureIgnoreCase).Replace("off", "false", StringComparison.InvariantCultureIgnoreCase);
list[2] = onOffReplace;

if (Service.Config.ConsoleCommand(list).Count > 0)
Service.Log($"[AI] Unknown command: {messageData[0]}");
}
else
Service.Log($"[AI] Unknown command: {messageData[0]}");

break;
}
}
}
76 changes: 76 additions & 0 deletions BossMod/AI/AIWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;

namespace BossMod.AI;

internal sealed class AIWindow : UIWindow
{
private const string _windowID = "###AI";

private readonly AIManager _manager;
private readonly EventSubscriptions _subscriptions;

public AIWindow(AIManager mgr) : base(_windowID, false, new(100, 100), ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoFocusOnAppearing)
{
_manager = mgr;
_subscriptions = new
(
_manager.Config.Modified.ExecuteAndSubscribe(() => IsOpen = _manager.Config.DrawUI)
);
RespectCloseHotkey = false;
}

protected override void Dispose(bool disposing)
{
_subscriptions.Dispose();
base.Dispose(disposing);
}

public void SetVisible(bool vis)
{
if (_manager.Config.DrawUI != vis)
{
_manager.Config.DrawUI = vis;
_manager.Config.Modified.Fire();
}
}

public override bool DrawConditions() => _manager.WorldState.Party.Player() != null;

public override void PreDraw()
{
var windowName = _manager.Config.ShowStatusOnTitlebar ? _manager.AIStatus : "AI";
WindowName = windowName + _windowID;
base.PreDraw();
}

public override void Draw()
{
if (!_manager.Config.ShowStatusOnTitlebar)
ImGui.TextUnformatted(_manager.AIStatus);
ImGui.TextUnformatted(_manager.NaviStatus);
_manager.Behaviour?.DrawDebug();

using (var leaderCombo = ImRaii.Combo("Follow", _manager.Behaviour == null ? "<idle>" : _manager.WorldState.Party[_manager.MasterSlot]?.Name ?? "<unknown>"))
{
if (leaderCombo)
{
if (ImGui.Selectable("<idle>", _manager.Behaviour == null))
{
_manager.Enabled = false;
}
foreach (var (i, p) in _manager.WorldState.Party.WithSlot(true))
{
if (ImGui.Selectable(p.Name, _manager.MasterSlot == i))
{
_manager.Config.FollowSlot = (AIConfig.Slot)i;
_manager.Config.Modified.Fire();
_manager.Enabled = true;
}
}
}
}
}

public override void OnClose() => SetVisible(false);
}
26 changes: 24 additions & 2 deletions BossMod/Autorotation/UIRotationWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,42 @@ public sealed class UIRotationWindow : UIWindow
private readonly RotationModuleManager _mgr;
private readonly ActionManagerEx _amex;
private readonly AutorotationConfig _config = Service.Config.Get<AutorotationConfig>();
private readonly EventSubscriptions _subscriptions;

public UIRotationWindow(RotationModuleManager mgr, ActionManagerEx amex, Action openConfig) : base("Autorotation", false, new(400, 400), ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoFocusOnAppearing)
{
_mgr = mgr;
_amex = amex;
ShowCloseButton = false;
_subscriptions = new
(
_config.Modified.ExecuteAndSubscribe(() => IsOpen = _config.ShowUI)
);
RespectCloseHotkey = false;
TitleBarButtons.Add(new() { Icon = FontAwesomeIcon.Cog, IconOffset = new(1), Click = _ => openConfig() });
}

protected override void Dispose(bool disposing)
{
_subscriptions.Dispose();
base.Dispose(disposing);
}

public void SetVisible(bool vis)
{
if (_config.ShowUI != vis)
{
_config.ShowUI = vis;
_config.Modified.Fire();
}
}

public override void PreOpenCheck()
{
IsOpen = _config.ShowUI && _mgr.WorldState.Party.Player() != null;
DrawPositional();
}

public override bool DrawConditions() => _mgr.WorldState.Party.Player() != null;

public override void Draw()
{
var player = _mgr.Player;
Expand Down Expand Up @@ -79,6 +99,8 @@ public override void Draw()
}
}

public override void OnClose() => SetVisible(false);

public static bool DrawRotationSelector(RotationModuleManager mgr)
{
var modified = false;
Expand Down
5 changes: 3 additions & 2 deletions BossMod/BossModule/BossModuleMainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public override void PreOpenCheck()
if (_mgr.Config.Lock)
Flags |= ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoInputs;
ForceMainWindow = _mgr.Config.TrishaMode; // NoBackground flag without ForceMainWindow works incorrectly for whatever reason

if (_mgr.Config.ShowWorldArrows && _mgr.ActiveModule != null && _mgr.WorldState.Party[PartyState.PlayerSlot] is var pc && pc != null)
DrawMovementHints(_mgr.ActiveModule.CalculateMovementHintsForRaidMember(PartyState.PlayerSlot, pc), pc.PosRot.Y);
}

public override void OnOpen()
Expand Down Expand Up @@ -65,8 +68,6 @@ public override void Draw()
try
{
_mgr.ActiveModule.Draw(_mgr.Config.RotateArena ? _mgr.WorldState.Client.CameraAzimuth : default, PartyState.PlayerSlot, !_mgr.Config.HintsInSeparateWindow, true);
if (_mgr.Config.ShowWorldArrows && _mgr.WorldState.Party[PartyState.PlayerSlot] is var pc && pc != null)
DrawMovementHints(_mgr.ActiveModule.CalculateMovementHintsForRaidMember(PartyState.PlayerSlot, pc), pc.PosRot.Y);
}
catch (Exception ex)
{
Expand Down
Loading

0 comments on commit a4b34ec

Please sign in to comment.