diff --git a/BossMod/Config/ConfigNode.cs b/BossMod/Config/ConfigNode.cs index bdc1a5077e..334379a52f 100644 --- a/BossMod/Config/ConfigNode.cs +++ b/BossMod/Config/ConfigNode.cs @@ -60,6 +60,22 @@ public PropertySliderAttribute(float min, float max) } } + // attribute that specifies slider should be used for displaying float property + [AttributeUsage(AttributeTargets.Field)] + public class PropertyIntSliderAttribute : Attribute + { + public float Speed = 1; + public int Min; + public int Max; + public bool Logarithmic; + + public PropertyIntSliderAttribute(int min, int max) + { + Min = min; + Max = max; + } + } + // base class for configuration nodes public abstract class ConfigNode { diff --git a/BossMod/Config/ConfigUI.cs b/BossMod/Config/ConfigUI.cs index 7ed906665f..ab4b137018 100644 --- a/BossMod/Config/ConfigUI.cs +++ b/BossMod/Config/ConfigUI.cs @@ -95,6 +95,7 @@ private void DrawNodes(List nodes) bool v => DrawProperty(props, n.Node, field, v), Enum v => DrawProperty(props, n.Node, field, v), float v => DrawProperty(props, n.Node, field, v), + int v => DrawProperty(props, n.Node, field, v), GroupAssignment v => DrawProperty(props, n.Node, field, v), _ => false }; @@ -165,6 +166,31 @@ private bool DrawProperty(PropertyDisplayAttribute props, ConfigNode node, Field return true; } + private bool DrawProperty(PropertyDisplayAttribute props, ConfigNode node, FieldInfo member, int v) + { + var slider = member.GetCustomAttribute(); + if (slider != null) + { + var flags = ImGuiSliderFlags.None; + if (slider.Logarithmic) + flags |= ImGuiSliderFlags.Logarithmic; + if (ImGui.DragInt(props.Label, ref v, slider.Speed, slider.Min, slider.Max, "%d", flags)) + { + member.SetValue(node, v); + node.NotifyModified(); + } + } + else + { + if (ImGui.InputInt(props.Label, ref v)) + { + member.SetValue(node, v); + node.NotifyModified(); + } + } + return true; + } + private bool DrawProperty(PropertyDisplayAttribute props, ConfigNode node, FieldInfo member, GroupAssignment v) { var group = member.GetCustomAttribute(); diff --git a/BossMod/Framework/Plugin.cs b/BossMod/Framework/Plugin.cs index cea1885293..0af3007d11 100644 --- a/BossMod/Framework/Plugin.cs +++ b/BossMod/Framework/Plugin.cs @@ -4,6 +4,7 @@ using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game; using System; using System.Reflection; @@ -46,6 +47,7 @@ public Plugin( Service.WindowSystem = new("vbm"); //Service.Device = pluginInterface.UiBuilder.Device; Service.Condition.ConditionChange += OnConditionChanged; + Service.ClientState.TerritoryChanged += OnZoneChange; MultiboxUnlock.Exec(); Network.IDScramble.Initialize(); Camera.Instance = new(); @@ -82,6 +84,7 @@ public Plugin( public void Dispose() { Service.Condition.ConditionChange -= OnConditionChanged; + Service.ClientState.TerritoryChanged -= OnZoneChange; _wndDebug.Dispose(); _wndReplay.Dispose(); _wndBossmodHints.Dispose(); @@ -160,5 +163,20 @@ private void OnConditionChanged(ConditionFlag flag, bool value) { Service.Log($"Condition chage: {flag}={value}"); } + + private unsafe void OnZoneChange(ushort obj) + { + if (GameMain.Instance()->CurrentContentFinderConditionId != 0 && !_wndReplay.IsRecording() && Service.Config.Get().AutoRecord) + { + _wndReplay.StartRecording(); + return; + } + + if (_wndReplay.IsRecording() && Service.Config.Get().AutoStop) + { + _wndReplay.StopRecording(); + return; + } + } } } diff --git a/BossMod/Framework/Service.cs b/BossMod/Framework/Service.cs index 0bb4ee55d3..4870420632 100644 --- a/BossMod/Framework/Service.cs +++ b/BossMod/Framework/Service.cs @@ -27,6 +27,7 @@ public class Service [PluginService] public static IFramework Framework { get; private set; } [PluginService] public static ITextureProvider Texture { get; private set; } [PluginService] public static ICommandManager CommandManager { get; private set; } + [PluginService] public static IDutyState DutyState { get; private set; } [PluginService] public static DalamudPluginInterface PluginInterface { get; private set; } #pragma warning restore CS8618 diff --git a/BossMod/Replay/ReplayManagementConfig.cs b/BossMod/Replay/ReplayManagementConfig.cs index 5183a99d64..2b1a4fde71 100644 --- a/BossMod/Replay/ReplayManagementConfig.cs +++ b/BossMod/Replay/ReplayManagementConfig.cs @@ -6,6 +6,16 @@ public class ReplayManagementConfig : ConfigNode [PropertyDisplay("Show replay management UI")] public bool ShowUI = false; + [PropertyDisplay("Auto record replays on duty start")] + public bool AutoRecord = false; + + [PropertyDisplay("Auto stop replays on duty end")] + public bool AutoStop = false; + + [PropertyDisplay("Max replays to keep before removal")] + [PropertyIntSlider(0, 1000, Speed = 1f, Logarithmic = false)] + public int MaxReplays = 0; + [PropertyDisplay("Store server packets in the replay")] public bool DumpServerPackets = false; diff --git a/BossMod/Replay/ReplayManagementWindow.cs b/BossMod/Replay/ReplayManagementWindow.cs index 2c96bd5cfb..bbb755fed9 100644 --- a/BossMod/Replay/ReplayManagementWindow.cs +++ b/BossMod/Replay/ReplayManagementWindow.cs @@ -1,6 +1,9 @@ -using ImGuiNET; +using FFXIVClientStructs.FFXIV.Client.Game.UI; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets; using System; using System.IO; +using System.Linq; namespace BossMod { @@ -56,7 +59,7 @@ public override void Draw() { try { - _recorder = new(_ws, _config.WorldLogFormat, true, _logDir, "World"); + _recorder = new(_ws, _config.WorldLogFormat, true, _logDir, $"{GetPrefix()}"); } catch (Exception ex) { @@ -86,6 +89,45 @@ public override void Draw() _manager.Draw(); } + public void StartRecording() + { + if (_recorder == null) + { + try + { + if (_config.MaxReplays > 0) + { + var files = _logDir.GetFiles().OrderBy(f => f.LastWriteTime).ToList(); + files.Take(files.Count - _config.MaxReplays).ToList().ForEach(f => f.Delete()); + } + } + catch (Exception ex) + { + Service.Log($"Failed to delete old replays: {ex}"); + } + + try + { + _recorder = new(_ws, _config.WorldLogFormat, true, _logDir, $"{GetPrefix()}"); + } + catch (Exception ex) + { + Service.Log($"Failed to start recording: {ex}"); + } + } + } + + public void StopRecording() + { + if (_recorder != null) + { + _recorder.Dispose(); + _recorder = null; + } + } + + public bool IsRecording() => _recorder != null; + public override void OnClose() { SetVisible(false); @@ -93,5 +135,28 @@ public override void OnClose() private void ApplyConfig(object? sender, EventArgs args) => IsOpen = _config.ShowUI; private void UpdateTitle() => WindowName = $"Replay recording: {(_recorder != null ? "in progress..." : "idle")}{_windowID}"; + private static unsafe string GetPrefix() + { + var prefix = "World"; + var row = Service.LuminaGameData!.GetExcelSheet()!.GetRow(Service.ClientState.TerritoryType); + if (row != null) + { + if (row.ContentFinderCondition.Value!.RowId != 0) + prefix = row.ContentFinderCondition.Value.Name.RawString; + else + prefix = row.PlaceName.Value?.NameNoArticle.RawString ?? prefix; + } + + var cf = ContentsFinder.Instance(); + var unrestricted = cf->IsUnrestrictedParty ? "U" : ""; + var levelsync = cf->IsLevelSync ? "LS" : ""; + var minilvl = cf->IsMinimalIL ? "MI" : ""; + var noecho = cf->IsSilenceEcho ? "NE" : ""; + var dfsettings = string.Join(", ", new[] { unrestricted, levelsync, minilvl, noecho }.Where(s => !string.IsNullOrEmpty(s))); + if (dfsettings != null) + prefix += $"_{dfsettings}"; + + return prefix; + } } }