diff --git a/BossMod/BossModule/BossModuleManager.cs b/BossMod/BossModule/BossModuleManager.cs index cb21427bd2..97876b8868 100644 --- a/BossMod/BossModule/BossModuleManager.cs +++ b/BossMod/BossModule/BossModuleManager.cs @@ -12,6 +12,8 @@ public sealed class BossModuleManager : IDisposable public IReadOnlyList LoadedModules => _loadedModules; public Event ModuleLoaded = new(); public Event ModuleUnloaded = new(); + public Event ModuleActivated = new(); + public Event ModuleDeactivated = new(); // drawn module among loaded modules; this can be changed explicitly if needed // usually we don't have multiple concurrently active modules, since this prevents meaningful cd planning, raid cooldown tracking, etc. @@ -79,6 +81,10 @@ public void Update() isActive = false; } + // if module was activated or deactivated, notify listeners + if (isActive != wasActive) + (isActive ? ModuleActivated : ModuleDeactivated).Fire(m); + // unload module either if it became deactivated or its primary actor disappeared without ever activating if (!isActive && (wasActive || m.PrimaryActor.IsDestroyed)) { @@ -89,6 +95,7 @@ public void Update() // if module is active and wants to be reset, oblige if (isActive && m.CheckReset()) { + ModuleDeactivated.Fire(m); var actor = m.PrimaryActor; UnloadModule(i--); if (!actor.IsDestroyed) diff --git a/BossMod/Framework/Plugin.cs b/BossMod/Framework/Plugin.cs index 3a5a9118ff..3492bbee64 100644 --- a/BossMod/Framework/Plugin.cs +++ b/BossMod/Framework/Plugin.cs @@ -89,7 +89,7 @@ public unsafe Plugin(IDalamudPluginInterface dalamud, ICommandManager commandMan _configUI = new(Service.Config, _ws, replayDir, _rotationDB); _wndBossmod = new(_bossmod, _zonemod); _wndBossmodHints = new(_bossmod, _zonemod); - _wndReplay = new(_ws, _rotationDB, replayDir); + _wndReplay = new(_ws, _bossmod, _rotationDB, replayDir); _wndRotation = new(_rotation, _amex, () => OpenConfigUI("Autorotation Presets")); _wndAI = new(_ai); _wndDebug = new(_ws, _rotation, _amex, _hintsBuilder, dalamud); diff --git a/BossMod/Replay/ReplayManagementConfig.cs b/BossMod/Replay/ReplayManagementConfig.cs index 6dbab3b53a..843e66dc99 100644 --- a/BossMod/Replay/ReplayManagementConfig.cs +++ b/BossMod/Replay/ReplayManagementConfig.cs @@ -6,7 +6,7 @@ public class ReplayManagementConfig : ConfigNode [PropertyDisplay("Show replay management UI")] public bool ShowUI = false; - [PropertyDisplay("Auto record replays on duty start/end")] + [PropertyDisplay("Auto record replays on duty start/end or outdoor module start/end")] public bool AutoRecord = true; [PropertyDisplay("Max replays to keep before removal")] diff --git a/BossMod/Replay/ReplayManagementWindow.cs b/BossMod/Replay/ReplayManagementWindow.cs index 3b768422d5..2181d6c5a7 100644 --- a/BossMod/Replay/ReplayManagementWindow.cs +++ b/BossMod/Replay/ReplayManagementWindow.cs @@ -1,4 +1,5 @@ using BossMod.Autorotation; +using Dalamud.Interface; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using Lumina.Excel.GeneratedSheets; @@ -16,12 +17,14 @@ public class ReplayManagementWindow : UIWindow private readonly EventSubscriptions _subscriptions; private ReplayRecorder? _recorder; private string _message = ""; - private bool _autoRecording; + private bool _recordingManual; // recording was started manually, and so should not be stopped automatically + private bool _recordingDuty; // recording was started automatically because we've entered duty + private int _recordingActiveModules; // recording was started automatically, because we've activated N modules private string _lastErrorMessage = ""; private const string _windowID = "###Replay recorder"; - public ReplayManagementWindow(WorldState ws, RotationDatabase rotationDB, DirectoryInfo logDir) : base(_windowID, false, new(300, 200)) + public ReplayManagementWindow(WorldState ws, BossModuleManager bmm, RotationDatabase rotationDB, DirectoryInfo logDir) : base(_windowID, false, new(300, 200)) { _ws = ws; _logDir = logDir; @@ -30,10 +33,12 @@ public class ReplayManagementWindow : UIWindow _subscriptions = new ( _config.Modified.ExecuteAndSubscribe(() => IsOpen = _config.ShowUI), - _ws.CurrentZoneChanged.Subscribe(op => UpdateAutoRecord(op.CFCID)) + _ws.CurrentZoneChanged.Subscribe(op => OnZoneChange(op.CFCID)), + bmm.ModuleActivated.Subscribe(OnModuleActivation), + bmm.ModuleDeactivated.Subscribe(OnModuleDeactivation) ); - if (!UpdateAutoRecord(_ws.CurrentCFCID)) + if (!OnZoneChange(_ws.CurrentCFCID)) UpdateTitle(); RespectCloseHotkey = false; @@ -66,9 +71,14 @@ public override void Draw() if (ImGui.Button(!IsRecording() ? "Start recording" : "Stop recording")) { if (!IsRecording()) - StartRecording(); + { + _recordingManual = true; + StartRecording(""); + } else + { StopRecording(); + } } if (_recorder != null) @@ -97,7 +107,61 @@ public override void Draw() _manager.Draw(); } - public void StartRecording() + public bool IsRecording() => _recorder != null; + + public override void OnClose() + { + SetVisible(false); + } + + private void UpdateTitle() => WindowName = $"Replay recording: {(_recorder != null ? "in progress..." : "idle")}{_windowID}"; + + private bool OnZoneChange(uint cfcId) + { + if (!_config.AutoRecord || _recordingManual) + return false; // don't care + + var isDuty = cfcId != 0; + if (_recordingDuty == isDuty) + return false; // don't care + _recordingDuty = isDuty; + + if (isDuty && !IsRecording()) + { + StartRecording(""); + return true; + } + + if (!isDuty && _recordingActiveModules <= 0 && IsRecording()) + { + StopRecording(); + return true; + } + + return false; + } + + private void OnModuleActivation(BossModule m) + { + if (!_config.AutoRecord || _recordingManual) + return; // don't care + + ++_recordingActiveModules; + if (!IsRecording()) + StartRecording($"{m.GetType().Name}-"); + } + + private void OnModuleDeactivation(BossModule m) + { + if (!_config.AutoRecord || _recordingManual || _recordingActiveModules <= 0) + return; // don't care + + --_recordingActiveModules; + if (_recordingActiveModules <= 0 && !_recordingDuty && IsRecording()) + StopRecording(); + } + + private void StartRecording(string prefix) { if (IsRecording()) return; // already recording @@ -120,7 +184,7 @@ public void StartRecording() try { - _recorder = new(_ws, _config.WorldLogFormat, true, _logDir, GetPrefix()); + _recorder = new(_ws, _config.WorldLogFormat, true, _logDir, prefix + GetPrefix()); } catch (Exception ex) { @@ -130,44 +194,16 @@ public void StartRecording() UpdateTitle(); } - public void StopRecording() + private void StopRecording() { + _recordingManual = false; + _recordingDuty = false; + _recordingActiveModules = 0; _recorder?.Dispose(); _recorder = null; UpdateTitle(); } - public bool IsRecording() => _recorder != null; - - public override void OnClose() - { - SetVisible(false); - } - - private void UpdateTitle() => WindowName = $"Replay recording: {(_recorder != null ? "in progress..." : "idle")}{_windowID}"; - - private bool UpdateAutoRecord(uint cfcId) - { - if (!_config.AutoRecord) - return false; // don't care - - if (!IsRecording() && _config.AutoRecord && cfcId != 0) - { - StartRecording(); - _autoRecording = true; - return true; - } - - if (IsRecording() && _autoRecording && cfcId == 0) - { - StopRecording(); - _autoRecording = false; - return true; - } - - return false; - } - private unsafe string GetPrefix() { string? prefix = null;