Continue renames, some cleanup.

This commit is contained in:
Ottermandias 2025-01-24 17:50:58 +01:00
parent 7be283ca30
commit d9f9937d41
18 changed files with 175 additions and 134 deletions

View file

@ -33,7 +33,7 @@ public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager
{ {
var once = (flags & ApplyFlag.Once) != 0; var once = (flags & ApplyFlag.Once) != 0;
var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true, var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true,
ResetMaterials: !once && key != 0, SendStateUpdate: true); ResetMaterials: !once && key != 0, IsFinal: true);
using var restrict = ApiHelpers.Restrict(design, flags); using var restrict = ApiHelpers.Restrict(design, flags);
stateManager.ApplyDesign(state, design, settings); stateManager.ApplyDesign(state, design, settings);

View file

@ -2,7 +2,6 @@ using Dalamud.Plugin;
using Glamourer.Api.Api; using Glamourer.Api.Api;
using Glamourer.Api.Helpers; using Glamourer.Api.Helpers;
using OtterGui.Services; using OtterGui.Services;
using System.Reflection.Emit;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
namespace Glamourer.Api; namespace Glamourer.Api;

View file

@ -11,9 +11,9 @@ using OtterGui.Services;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using ObjectManager = Glamourer.Interop.ObjectManager; using ObjectManager = Glamourer.Interop.ObjectManager;
using StateChanged = Glamourer.Events.StateChanged; using StateChanged = Glamourer.Events.StateChanged;
using StateUpdated = Glamourer.Events.StateUpdated;
namespace Glamourer.Api; namespace Glamourer.Api;
public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
{ {
private readonly ApiHelpers _helpers; private readonly ApiHelpers _helpers;
@ -23,7 +23,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private readonly AutoDesignApplier _autoDesigns; private readonly AutoDesignApplier _autoDesigns;
private readonly ObjectManager _objects; private readonly ObjectManager _objects;
private readonly StateChanged _stateChanged; private readonly StateChanged _stateChanged;
private readonly StateUpdated _stateUpdated; private readonly StateFinalized _stateFinalized;
private readonly GPoseService _gPose; private readonly GPoseService _gPose;
public StateApi(ApiHelpers helpers, public StateApi(ApiHelpers helpers,
@ -33,27 +33,27 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
AutoDesignApplier autoDesigns, AutoDesignApplier autoDesigns,
ObjectManager objects, ObjectManager objects,
StateChanged stateChanged, StateChanged stateChanged,
StateUpdated stateUpdated, StateFinalized stateFinalized,
GPoseService gPose) GPoseService gPose)
{ {
_helpers = helpers; _helpers = helpers;
_stateManager = stateManager; _stateManager = stateManager;
_converter = converter; _converter = converter;
_config = config; _config = config;
_autoDesigns = autoDesigns; _autoDesigns = autoDesigns;
_objects = objects; _objects = objects;
_stateChanged = stateChanged; _stateChanged = stateChanged;
_stateUpdated = stateUpdated; _stateFinalized = stateFinalized;
_gPose = gPose; _gPose = gPose;
_stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc); _stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc);
_stateUpdated.Subscribe(OnStateUpdated, Events.StateUpdated.Priority.GlamourerIpc); _stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi);
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.GlamourerIpc); _gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi);
} }
public void Dispose() public void Dispose()
{ {
_stateChanged.Unsubscribe(OnStateChanged); _stateChanged.Unsubscribe(OnStateChanged);
_stateUpdated.Unsubscribe(OnStateUpdated); _stateFinalized.Unsubscribe(OnStateFinalized);
_gPose.Unsubscribe(OnGPoseChange); _gPose.Unsubscribe(OnGPoseChange);
} }
@ -253,16 +253,16 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public event Action<nint>? StateChanged; public event Action<nint>? StateChanged;
public event Action<IntPtr, StateChangeType>? StateChangedWithType; public event Action<IntPtr, StateChangeType>? StateChangedWithType;
public event Action<IntPtr, StateFinalizationType>? StateFinalized; public event Action<IntPtr, StateFinalizationType>? StateFinalized;
public event Action<bool>? GPoseChanged; public event Action<bool>? GPoseChanged;
private void ApplyDesign(ActorState state, DesignBase design, uint key, ApplyFlag flags) private void ApplyDesign(ActorState state, DesignBase design, uint key, ApplyFlag flags)
{ {
var once = (flags & ApplyFlag.Once) != 0; var once = (flags & ApplyFlag.Once) != 0;
var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true, var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true,
ResetMaterials: !once && key != 0, SendStateUpdate: true); ResetMaterials: !once && key != 0, IsFinal: true);
_stateManager.ApplyDesign(state, design, settings); _stateManager.ApplyDesign(state, design, settings);
ApiHelpers.Lock(state, key, flags); ApiHelpers.Lock(state, key, flags);
} }
@ -349,7 +349,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
StateChangedWithType.Invoke(actor.Address, type); StateChangedWithType.Invoke(actor.Address, type);
} }
private void OnStateUpdated(StateFinalizationType type, ActorData actors) private void OnStateFinalized(StateFinalizationType type, ActorData actors)
{ {
Glamourer.Log.Verbose($"[OnStateUpdated] State Updated with Type {type}. [Affecting {actors.ToLazyString("nothing")}.]"); Glamourer.Log.Verbose($"[OnStateUpdated] State Updated with Type {type}. [Affecting {actors.ToLazyString("nothing")}.]");
if (StateFinalized != null) if (StateFinalized != null)

View file

@ -14,7 +14,7 @@ public readonly record struct ApplySettings(
bool UseSingleSource = false, bool UseSingleSource = false,
bool MergeLinks = false, bool MergeLinks = false,
bool ResetMaterials = false, bool ResetMaterials = false,
bool SendStateUpdate = false) bool IsFinal = false)
{ {
public static readonly ApplySettings Manual = new() public static readonly ApplySettings Manual = new()
{ {
@ -25,7 +25,7 @@ public readonly record struct ApplySettings(
UseSingleSource = false, UseSingleSource = false,
MergeLinks = false, MergeLinks = false,
ResetMaterials = false, ResetMaterials = false,
SendStateUpdate = false, IsFinal = false,
}; };
public static readonly ApplySettings ManualWithLinks = new() public static readonly ApplySettings ManualWithLinks = new()
@ -37,7 +37,7 @@ public readonly record struct ApplySettings(
UseSingleSource = false, UseSingleSource = false,
MergeLinks = true, MergeLinks = true,
ResetMaterials = false, ResetMaterials = false,
SendStateUpdate = false, IsFinal = false,
}; };
public static readonly ApplySettings Game = new() public static readonly ApplySettings Game = new()
@ -49,7 +49,7 @@ public readonly record struct ApplySettings(
UseSingleSource = false, UseSingleSource = false,
MergeLinks = false, MergeLinks = false,
ResetMaterials = true, ResetMaterials = true,
SendStateUpdate = false, IsFinal = false,
}; };
} }

View file

@ -13,8 +13,8 @@ public sealed class GPoseService : EventWrapper<bool, GPoseService.Priority>
public enum Priority public enum Priority
{ {
/// <seealso cref="Api.GlamourerIpc.OnGPoseChanged"/> /// <seealso cref="Api.StateApi.OnGPoseChange"/>
GlamourerIpc = int.MinValue, StateApi = int.MinValue,
} }
public bool InGPose { get; private set; } public bool InGPose { get; private set; }

View file

@ -1,24 +1,23 @@
using Glamourer.Api;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Designs.History;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using Glamourer.State;
using OtterGui.Classes; using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary>
/// Triggered when a Design is edited in any way. /// Triggered when a set of grouped changes finishes being applied to a Glamourer state.
/// <list type="number"> /// <list type="number">
/// <item>Parameter is the operation that finished updating the saved state. </item> /// <item>Parameter is the operation that finished updating the saved state. </item>
/// <item>Parameter is the existing actors using this saved state. </item> /// <item>Parameter is the existing actors using this saved state. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class StateUpdated() public sealed class StateFinalized()
: EventWrapper<StateFinalizationType, ActorData, StateUpdated.Priority>(nameof(StateUpdated)) : EventWrapper<StateFinalizationType, ActorData, StateFinalized.Priority>(nameof(StateFinalized))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="Api.GlamourerIpc.OnStateUpdated"/> /// <seealso cref="StateApi.OnStateFinalized"/>
GlamourerIpc = int.MinValue, StateApi = int.MinValue,
} }
} }

View file

@ -183,7 +183,7 @@ public sealed class DesignQuickBar : Window, IDisposable
} }
using var _ = design!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); using var _ = design!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true }); _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true });
} }
private void DrawRevertButton(Vector2 buttonSize) private void DrawRevertButton(Vector2 buttonSize)
@ -213,7 +213,7 @@ public sealed class DesignQuickBar : Window, IDisposable
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, tooltip, available); var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, tooltip, available);
ImGui.SameLine(); ImGui.SameLine();
if (clicked) if (clicked)
_stateManager.ResetState(state!, StateSource.Manual, stateUpdate: true); _stateManager.ResetState(state!, StateSource.Manual, isFinal: true);
} }
private void DrawRevertAutomationButton(Vector2 buttonSize) private void DrawRevertAutomationButton(Vector2 buttonSize)

View file

@ -385,7 +385,7 @@ public class ActorPanel
{ {
if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.", if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.",
_state!.IsLocked)) _state!.IsLocked))
_stateManager.ResetState(_state!, StateSource.Manual, stateUpdate: true); _stateManager.ResetState(_state!, StateSource.Manual, isFinal: true);
ImGui.SameLine(); ImGui.SameLine();
@ -423,7 +423,7 @@ public class ActorPanel
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state)) if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)), _stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)),
ApplySettings.Manual with { SendStateUpdate = true }); ApplySettings.Manual with { IsFinal = true });
} }
private void DrawApplyToTarget() private void DrawApplyToTarget()
@ -440,7 +440,7 @@ public class ActorPanel
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state)) if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)), _stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)),
ApplySettings.Manual with { SendStateUpdate = true }); ApplySettings.Manual with { IsFinal = true });
} }
@ -467,7 +467,7 @@ public class ActorPanel
var text = ImGui.GetClipboardText(); var text = ImGui.GetClipboardText();
var design = panel._converter.FromBase64(text, applyCustomize, applyGear, out _) var design = panel._converter.FromBase64(text, applyCustomize, applyGear, out _)
?? throw new Exception("The clipboard did not contain valid data."); ?? throw new Exception("The clipboard did not contain valid data.");
panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true }); panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks with { IsFinal = true });
} }
catch (Exception ex) catch (Exception ex)
{ {

View file

@ -79,7 +79,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
if (ImGuiUtil.DrawDisabledButton("Apply to Player", Vector2.Zero, string.Empty, !enabled)) if (ImGuiUtil.DrawDisabledButton("Apply to Player", Vector2.Zero, string.Empty, !enabled))
{ {
var design = CreateDesign(plate); var design = CreateDesign(plate);
_state.ApplyDesign(state!, design, ApplySettings.Manual with { SendStateUpdate = true }); _state.ApplyDesign(state!, design, ApplySettings.Manual with { IsFinal = true });
} }
using (ImRaii.Group()) using (ImRaii.Group())

View file

@ -51,6 +51,7 @@ public class IpcTesterPanel(
Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester."); Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester.");
state.GPoseChanged.Enable(); state.GPoseChanged.Enable();
state.StateChanged.Enable(); state.StateChanged.Enable();
state.StateFinalized.Enable();
framework.Update += CheckUnsubscribe; framework.Update += CheckUnsubscribe;
_subscribed = true; _subscribed = true;
} }
@ -73,5 +74,6 @@ public class IpcTesterPanel(
_subscribed = false; _subscribed = false;
state.GPoseChanged.Disable(); state.GPoseChanged.Disable();
state.StateChanged.Disable(); state.StateChanged.Disable();
state.StateFinalized.Disable();
} }
} }

View file

@ -11,6 +11,7 @@ using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Services; using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.String; using Penumbra.String;
@ -31,9 +32,16 @@ public class StateIpcTester : IUiService, IDisposable
private string? _getStateString; private string? _getStateString;
public readonly EventSubscriber<nint, StateChangeType> StateChanged; public readonly EventSubscriber<nint, StateChangeType> StateChanged;
private nint _lastStateChangeActor; private nint _lastStateChangeActor;
private ByteString _lastStateChangeName = ByteString.Empty; private ByteString _lastStateChangeName = ByteString.Empty;
private DateTime _lastStateChangeTime; private DateTime _lastStateChangeTime;
private StateChangeType _lastStateChangeType;
public readonly EventSubscriber<nint, StateFinalizationType> StateFinalized;
private nint _lastStateFinalizeActor;
private ByteString _lastStateFinalizeName = ByteString.Empty;
private DateTime _lastStateFinalizeTime;
private StateFinalizationType _lastStateFinalizeType;
public readonly EventSubscriber<bool> GPoseChanged; public readonly EventSubscriber<bool> GPoseChanged;
private bool _lastGPoseChangeValue; private bool _lastGPoseChangeValue;
@ -44,15 +52,18 @@ public class StateIpcTester : IUiService, IDisposable
public StateIpcTester(IDalamudPluginInterface pluginInterface) public StateIpcTester(IDalamudPluginInterface pluginInterface)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
StateChanged = Api.IpcSubscribers.StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged); StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged);
StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized);
GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange); GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange);
StateChanged.Disable(); StateChanged.Disable();
StateFinalized.Disable();
GPoseChanged.Disable(); GPoseChanged.Disable();
} }
public void Dispose() public void Dispose()
{ {
StateChanged.Dispose(); StateChanged.Dispose();
StateFinalized.Dispose();
GPoseChanged.Dispose(); GPoseChanged.Dispose();
} }
@ -73,86 +84,88 @@ public class StateIpcTester : IUiService, IDisposable
IpcTesterHelpers.DrawIntro("Last Error"); IpcTesterHelpers.DrawIntro("Last Error");
ImGui.TextUnformatted(_lastError.ToString()); ImGui.TextUnformatted(_lastError.ToString());
IpcTesterHelpers.DrawIntro("Last State Change"); IpcTesterHelpers.DrawIntro("Last State Change");
PrintName(); PrintChangeName();
IpcTesterHelpers.DrawIntro("Last State Finalization");
PrintFinalizeName();
IpcTesterHelpers.DrawIntro("Last GPose Change"); IpcTesterHelpers.DrawIntro("Last GPose Change");
ImGui.TextUnformatted($"{_lastGPoseChangeValue} at {_lastGPoseChangeTime.ToLocalTime().TimeOfDay}"); ImGui.TextUnformatted($"{_lastGPoseChangeValue} at {_lastGPoseChangeTime.ToLocalTime().TimeOfDay}");
IpcTesterHelpers.DrawIntro(GetState.Label); IpcTesterHelpers.DrawIntro(GetState.Label);
DrawStatePopup(); DrawStatePopup();
if (ImGui.Button("Get##Idx")) if (ImUtf8.Button("Get##Idx"u8))
{ {
(_lastError, _state) = new GetState(_pluginInterface).Invoke(_gameObjectIndex, _key); (_lastError, _state) = new GetState(_pluginInterface).Invoke(_gameObjectIndex, _key);
_stateString = _state?.ToString(Formatting.Indented) ?? "No State Available"; _stateString = _state?.ToString(Formatting.Indented) ?? "No State Available";
ImGui.OpenPopup("State"); ImUtf8.OpenPopup("State"u8);
} }
IpcTesterHelpers.DrawIntro(GetStateName.Label); IpcTesterHelpers.DrawIntro(GetStateName.Label);
if (ImGui.Button("Get##Name")) if (ImUtf8.Button("Get##Name"u8))
{ {
(_lastError, _state) = new GetStateName(_pluginInterface).Invoke(_gameObjectName, _key); (_lastError, _state) = new GetStateName(_pluginInterface).Invoke(_gameObjectName, _key);
_stateString = _state?.ToString(Formatting.Indented) ?? "No State Available"; _stateString = _state?.ToString(Formatting.Indented) ?? "No State Available";
ImGui.OpenPopup("State"); ImUtf8.OpenPopup("State"u8);
} }
IpcTesterHelpers.DrawIntro(GetStateBase64.Label); IpcTesterHelpers.DrawIntro(GetStateBase64.Label);
if (ImGui.Button("Get##Base64Idx")) if (ImUtf8.Button("Get##Base64Idx"u8))
{ {
(_lastError, _getStateString) = new GetStateBase64(_pluginInterface).Invoke(_gameObjectIndex, _key); (_lastError, _getStateString) = new GetStateBase64(_pluginInterface).Invoke(_gameObjectIndex, _key);
_stateString = _getStateString ?? "No State Available"; _stateString = _getStateString ?? "No State Available";
ImGui.OpenPopup("State"); ImUtf8.OpenPopup("State"u8);
} }
IpcTesterHelpers.DrawIntro(GetStateBase64Name.Label); IpcTesterHelpers.DrawIntro(GetStateBase64Name.Label);
if (ImGui.Button("Get##Base64Idx")) if (ImUtf8.Button("Get##Base64Idx"u8))
{ {
(_lastError, _getStateString) = new GetStateBase64Name(_pluginInterface).Invoke(_gameObjectName, _key); (_lastError, _getStateString) = new GetStateBase64Name(_pluginInterface).Invoke(_gameObjectName, _key);
_stateString = _getStateString ?? "No State Available"; _stateString = _getStateString ?? "No State Available";
ImGui.OpenPopup("State"); ImUtf8.OpenPopup("State"u8);
} }
IpcTesterHelpers.DrawIntro(ApplyState.Label); IpcTesterHelpers.DrawIntro(ApplyState.Label);
if (ImGuiUtil.DrawDisabledButton("Apply Last##Idx", Vector2.Zero, string.Empty, _state == null)) if (ImGuiUtil.DrawDisabledButton("Apply Last##Idx", Vector2.Zero, string.Empty, _state == null))
_lastError = new ApplyState(_pluginInterface).Invoke(_state!, _gameObjectIndex, _key, _flags); _lastError = new ApplyState(_pluginInterface).Invoke(_state!, _gameObjectIndex, _key, _flags);
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Apply Base64##Idx")) if (ImUtf8.Button("Apply Base64##Idx"u8))
_lastError = new ApplyState(_pluginInterface).Invoke(_base64State, _gameObjectIndex, _key, _flags); _lastError = new ApplyState(_pluginInterface).Invoke(_base64State, _gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(ApplyStateName.Label); IpcTesterHelpers.DrawIntro(ApplyStateName.Label);
if (ImGuiUtil.DrawDisabledButton("Apply Last##Name", Vector2.Zero, string.Empty, _state == null)) if (ImGuiUtil.DrawDisabledButton("Apply Last##Name", Vector2.Zero, string.Empty, _state == null))
_lastError = new ApplyStateName(_pluginInterface).Invoke(_state!, _gameObjectName, _key, _flags); _lastError = new ApplyStateName(_pluginInterface).Invoke(_state!, _gameObjectName, _key, _flags);
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Apply Base64##Name")) if (ImUtf8.Button("Apply Base64##Name"u8))
_lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags); _lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertState.Label); IpcTesterHelpers.DrawIntro(RevertState.Label);
if (ImGui.Button("Revert##Idx")) if (ImUtf8.Button("Revert##Idx"u8))
_lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags); _lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertStateName.Label); IpcTesterHelpers.DrawIntro(RevertStateName.Label);
if (ImGui.Button("Revert##Name")) if (ImUtf8.Button("Revert##Name"u8))
_lastError = new RevertStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags); _lastError = new RevertStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(UnlockState.Label); IpcTesterHelpers.DrawIntro(UnlockState.Label);
if (ImGui.Button("Unlock##Idx")) if (ImUtf8.Button("Unlock##Idx"u8))
_lastError = new UnlockState(_pluginInterface).Invoke(_gameObjectIndex, _key); _lastError = new UnlockState(_pluginInterface).Invoke(_gameObjectIndex, _key);
IpcTesterHelpers.DrawIntro(UnlockStateName.Label); IpcTesterHelpers.DrawIntro(UnlockStateName.Label);
if (ImGui.Button("Unlock##Name")) if (ImUtf8.Button("Unlock##Name"u8))
_lastError = new UnlockStateName(_pluginInterface).Invoke(_gameObjectName, _key); _lastError = new UnlockStateName(_pluginInterface).Invoke(_gameObjectName, _key);
IpcTesterHelpers.DrawIntro(UnlockAll.Label); IpcTesterHelpers.DrawIntro(UnlockAll.Label);
if (ImGui.Button("Unlock##All")) if (ImUtf8.Button("Unlock##All"u8))
_numUnlocked = new UnlockAll(_pluginInterface).Invoke(_key); _numUnlocked = new UnlockAll(_pluginInterface).Invoke(_key);
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted($"Unlocked {_numUnlocked}"); ImGui.TextUnformatted($"Unlocked {_numUnlocked}");
IpcTesterHelpers.DrawIntro(RevertToAutomation.Label); IpcTesterHelpers.DrawIntro(RevertToAutomation.Label);
if (ImGui.Button("Revert##AutomationIdx")) if (ImUtf8.Button("Revert##AutomationIdx"u8))
_lastError = new RevertToAutomation(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags); _lastError = new RevertToAutomation(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertToAutomationName.Label); IpcTesterHelpers.DrawIntro(RevertToAutomationName.Label);
if (ImGui.Button("Revert##AutomationName")) if (ImUtf8.Button("Revert##AutomationName"u8))
_lastError = new RevertToAutomationName(_pluginInterface).Invoke(_gameObjectName, _key, _flags); _lastError = new RevertToAutomationName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
} }
@ -162,44 +175,70 @@ public class StateIpcTester : IUiService, IDisposable
if (_stateString == null) if (_stateString == null)
return; return;
using var p = ImRaii.Popup("State"); using var p = ImUtf8.Popup("State"u8);
if (!p) if (!p)
return; return;
if (ImGui.Button("Copy to Clipboard")) if (ImUtf8.Button("Copy to Clipboard"u8))
ImGui.SetClipboardText(_stateString); ImUtf8.SetClipboardText(_stateString);
if (_stateString[0] is '{') if (_stateString[0] is '{')
{ {
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Copy as Base64") && _state != null) if (ImUtf8.Button("Copy as Base64"u8) && _state != null)
ImGui.SetClipboardText(DesignConverter.ToBase64(_state)); ImUtf8.SetClipboardText(DesignConverter.ToBase64(_state));
} }
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGuiUtil.TextWrapped(_stateString ?? string.Empty); ImUtf8.TextWrapped(_stateString ?? string.Empty);
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) if (ImUtf8.Button("Close"u8, -Vector2.UnitX) || !ImGui.IsWindowFocused())
ImGui.CloseCurrentPopup(); ImGui.CloseCurrentPopup();
} }
private unsafe void PrintName() private unsafe void PrintChangeName()
{ {
ImGuiNative.igTextUnformatted(_lastStateChangeName.Path, _lastStateChangeName.Path + _lastStateChangeName.Length); ImUtf8.Text(_lastStateChangeName.Span);
ImGui.SameLine(0, 0);
ImUtf8.Text($" ({_lastStateChangeType})");
ImGui.SameLine(); ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.MonoFont)) using (ImRaii.PushFont(UiBuilder.MonoFont))
{ {
ImGuiUtil.CopyOnClickSelectable($"0x{_lastStateChangeActor:X}"); ImUtf8.CopyOnClickSelectable($"0x{_lastStateChangeActor:X}");
} }
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted($"at {_lastStateChangeTime.ToLocalTime().TimeOfDay}"); ImUtf8.Text($"at {_lastStateChangeTime.ToLocalTime().TimeOfDay}");
} }
private void OnStateChanged(nint actor, StateChangeType _) private unsafe void PrintFinalizeName()
{
ImUtf8.Text(_lastStateFinalizeName.Span);
ImGui.SameLine(0, 0);
ImUtf8.Text($" ({_lastStateFinalizeType})");
ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.MonoFont))
{
ImUtf8.CopyOnClickSelectable($"0x{_lastStateFinalizeActor:X}");
}
ImGui.SameLine();
ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}");
}
private void OnStateChanged(nint actor, StateChangeType type)
{ {
_lastStateChangeActor = actor; _lastStateChangeActor = actor;
_lastStateChangeTime = DateTime.UtcNow; _lastStateChangeTime = DateTime.UtcNow;
_lastStateChangeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty; _lastStateChangeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty;
_lastStateChangeType = type;
}
private void OnStateFinalized(nint actor, StateFinalizationType type)
{
_lastStateFinalizeActor = actor;
_lastStateFinalizeTime = DateTime.UtcNow;
_lastStateFinalizeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty;
_lastStateFinalizeType = type;
} }
private void OnGPoseChange(bool value) private void OnGPoseChange(bool value)

View file

@ -460,7 +460,7 @@ public class DesignPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state)) if (_state.GetOrCreate(id, data.Objects[0], out var state))
{ {
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { SendStateUpdate = true }); _state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true });
} }
} }
@ -478,7 +478,7 @@ public class DesignPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state)) if (_state.GetOrCreate(id, data.Objects[0], out var state))
{ {
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { SendStateUpdate = true }); _state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true });
} }
} }

View file

@ -196,7 +196,7 @@ public class NpcPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state)) if (_state.GetOrCreate(id, data.Objects[0], out var state))
{ {
var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers()); var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
_state.ApplyDesign(state, design, ApplySettings.Manual with { SendStateUpdate = true }); _state.ApplyDesign(state, design, ApplySettings.Manual with { IsFinal = true });
} }
} }
@ -214,7 +214,7 @@ public class NpcPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state)) if (_state.GetOrCreate(id, data.Objects[0], out var state))
{ {
var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers()); var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
_state.ApplyDesign(state, design, ApplySettings.Manual with { SendStateUpdate = true }); _state.ApplyDesign(state, design, ApplySettings.Manual with { IsFinal = true });
} }
} }

View file

@ -18,6 +18,7 @@ public unsafe class UpdateSlotService : IDisposable
public readonly BonusSlotUpdating BonusSlotUpdatingEvent; public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
public readonly GearsetDataLoaded GearsetDataLoadedEvent; public readonly GearsetDataLoaded GearsetDataLoadedEvent;
private readonly DictBonusItems _bonusItems; private readonly DictBonusItems _bonusItems;
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded, public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded,
IGameInteropProvider interop, DictBonusItems bonusItems) IGameInteropProvider interop, DictBonusItems bonusItems)
{ {
@ -26,8 +27,8 @@ public unsafe class UpdateSlotService : IDisposable
GearsetDataLoadedEvent = gearsetDataLoaded; GearsetDataLoadedEvent = gearsetDataLoaded;
_bonusItems = bonusItems; _bonusItems = bonusItems;
_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
interop.InitializeFromAttributes(this); interop.InitializeFromAttributes(this);
_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
_flagSlotForUpdateHook.Enable(); _flagSlotForUpdateHook.Enable();
_flagBonusSlotForUpdateHook.Enable(); _flagBonusSlotForUpdateHook.Enable();
_loadGearsetDataHook.Enable(); _loadGearsetDataHook.Enable();
@ -89,8 +90,8 @@ public unsafe class UpdateSlotService : IDisposable
/// <summary> Detours the func that makes all FlagSlotForUpdate calls on a gearset change or initial render of a given actor (Only Cases this is Called). /// <summary> Detours the func that makes all FlagSlotForUpdate calls on a gearset change or initial render of a given actor (Only Cases this is Called).
/// <para> Logic done after returning the original hook executes <b>After</b> all equipment/weapon/crest data is loaded into the Actors BaseData. </para> /// <para> Logic done after returning the original hook executes <b>After</b> all equipment/weapon/crest data is loaded into the Actors BaseData. </para>
/// </summary> /// </summary>
private delegate Int64 LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData); private delegate ulong LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData);
private readonly Hook<LoadGearsetDataDelegate> _loadGearsetDataHook = null!; private readonly Hook<LoadGearsetDataDelegate> _loadGearsetDataHook;
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data) private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
{ {
@ -115,30 +116,30 @@ public unsafe class UpdateSlotService : IDisposable
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Glamourer-Invoked on 0x{drawObject.Address:X} on {slot} with item data {armor}."); Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Glamourer-Invoked on 0x{drawObject.Address:X} on {slot} with item data {armor}.");
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor); return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
} }
private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData) private ulong LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData)
{ {
var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData); var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
Model drawObject = drawDataContainer->OwnerObject->DrawObject; var drawObject = drawDataContainer->OwnerObject->DrawObject;
GearsetDataLoadedEvent.Invoke(drawObject); GearsetDataLoadedEvent.Invoke(drawObject);
// Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}"); Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
return ret; return ret;
} }
// If you ever care to debug this, here is a formatted string output of this new gearsetData struct.
private string FormatGearsetItemDataStruct(PacketPlayerGearsetData gearsetData) private static string FormatGearsetItemDataStruct(PacketPlayerGearsetData gearsetData)
{ {
string ret = var ret =
$"\nMainhandWeaponData: Id: {gearsetData.MainhandWeaponData.Id}, Type: {gearsetData.MainhandWeaponData.Type}, " + $"\nMainhandWeaponData: Id: {gearsetData.MainhandWeaponData.Id}, Type: {gearsetData.MainhandWeaponData.Type}, " +
$"Variant: {gearsetData.MainhandWeaponData.Variant}, Stain0: {gearsetData.MainhandWeaponData.Stain0}, Stain1: {gearsetData.MainhandWeaponData.Stain1}" + $"Variant: {gearsetData.MainhandWeaponData.Variant}, Stain0: {gearsetData.MainhandWeaponData.Stain0}, Stain1: {gearsetData.MainhandWeaponData.Stain1}" +
$"\nOffhandWeaponData: Id: {gearsetData.OffhandWeaponData.Id}, Type: {gearsetData.OffhandWeaponData.Type}, " + $"\nOffhandWeaponData: Id: {gearsetData.OffhandWeaponData.Id}, Type: {gearsetData.OffhandWeaponData.Type}, " +
$"Variant: {gearsetData.OffhandWeaponData.Variant}, Stain0: {gearsetData.OffhandWeaponData.Stain0}, Stain1: {gearsetData.OffhandWeaponData.Stain1}" + $"Variant: {gearsetData.OffhandWeaponData.Variant}, Stain0: {gearsetData.OffhandWeaponData.Stain0}, Stain1: {gearsetData.OffhandWeaponData.Stain1}" +
$"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId}"; $"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId}";
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor)) for (var offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
{ {
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset); var equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
int dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset var dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset
byte* dyePtr = (byte*)&gearsetData + dyeOffset; var dyePtr = (byte*)&gearsetData + dyeOffset;
ret += $"\nEquipSlot {((EquipSlot)(dyeOffset - 60)).ToString()}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}"; ret += $"\nEquipSlot {(EquipSlot)(dyeOffset - 60)}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}";
} }
return ret; return ret;
} }

View file

@ -668,7 +668,7 @@ public class CommandService : IDisposable, IApiService
if (!_objects.TryGetValue(identifier, out var actors)) if (!_objects.TryGetValue(identifier, out var actors))
{ {
if (_stateManager.TryGetValue(identifier, out var state)) if (_stateManager.TryGetValue(identifier, out var state))
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true }); _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true });
} }
else else
{ {
@ -677,7 +677,7 @@ public class CommandService : IDisposable, IApiService
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state)) if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
{ {
ApplyModSettings(design, actor, applyMods); ApplyModSettings(design, actor, applyMods);
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true }); _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true });
} }
} }
} }

View file

@ -17,7 +17,7 @@ public class StateEditor(
InternalStateEditor editor, InternalStateEditor editor,
StateApplier applier, StateApplier applier,
StateChanged stateChanged, StateChanged stateChanged,
StateUpdated stateUpdated, StateFinalized stateFinalized,
JobChangeState jobChange, JobChangeState jobChange,
Configuration config, Configuration config,
ItemManager items, ItemManager items,
@ -25,12 +25,12 @@ public class StateEditor(
ModSettingApplier modApplier, ModSettingApplier modApplier,
GPoseService gPose) : IDesignEditor GPoseService gPose) : IDesignEditor
{ {
protected readonly InternalStateEditor Editor = editor; protected readonly InternalStateEditor Editor = editor;
protected readonly StateApplier Applier = applier; protected readonly StateApplier Applier = applier;
protected readonly StateChanged StateChanged = stateChanged; protected readonly StateChanged StateChanged = stateChanged;
protected readonly StateUpdated StateUpdated = stateUpdated; protected readonly StateFinalized StateFinalized = stateFinalized;
protected readonly Configuration Config = config; protected readonly Configuration Config = config;
protected readonly ItemManager Items = items; protected readonly ItemManager Items = items;
/// <summary> Turn an actor to. </summary> /// <summary> Turn an actor to. </summary>
public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source, public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source,
@ -43,7 +43,7 @@ public class StateEditor(
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Model, source, state, actors, null); StateChanged.Invoke(StateChangeType.Model, source, state, actors, null);
StateUpdated.Invoke(StateFinalizationType.ModelChange, actors); StateFinalized.Invoke(StateFinalizationType.ModelChange, actors);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -383,7 +383,7 @@ public class StateEditor(
Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key); Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key);
} }
if (settings.ResetMaterials || (!settings.RespectManual && mergedDesign.ResetAdvancedDyes)) if (settings.ResetMaterials || !settings.RespectManual && mergedDesign.ResetAdvancedDyes)
state.Materials.Clear(); state.Materials.Clear();
foreach (var (key, value) in mergedDesign.Design.Materials) foreach (var (key, value) in mergedDesign.Design.Materials)
@ -420,8 +420,8 @@ public class StateEditor(
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Design, state.Sources[MetaIndex.Wetness], state, actors, null); // FIXME: maybe later StateChanged.Invoke(StateChangeType.Design, state.Sources[MetaIndex.Wetness], state, actors, null); // FIXME: maybe later
if(settings.SendStateUpdate) if (settings.IsFinal)
StateUpdated.Invoke(StateFinalizationType.DesignApplied, actors); StateFinalized.Invoke(StateFinalizationType.DesignApplied, actors);
return; return;
@ -442,7 +442,8 @@ public class StateEditor(
if (!settings.MergeLinks || design is not Design d) if (!settings.MergeLinks || design is not Design d)
merged = new MergedDesign(design); merged = new MergedDesign(design);
else else
merged = merger.Merge(d.AllLinks(true), state.ModelData.IsHuman ? state.ModelData.Customize : CustomizeArray.Default, state.BaseData, merged = merger.Merge(d.AllLinks(true), state.ModelData.IsHuman ? state.ModelData.Customize : CustomizeArray.Default,
state.BaseData,
false, Config.AlwaysApplyAssociatedMods); false, Config.AlwaysApplyAssociatedMods);
ApplyDesign(data, merged, settings with ApplyDesign(data, merged, settings with
@ -460,7 +461,7 @@ public class StateEditor(
if (!Config.ChangeEntireItem || !settings.Source.IsManual()) if (!Config.ChangeEntireItem || !settings.Source.IsManual())
return; return;
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand); var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);
// Do not change Shields to nothing. // Do not change Shields to nothing.
if (mh.Type is FullEquipType.Sword) if (mh.Type is FullEquipType.Sword)
return; return;

View file

@ -41,7 +41,7 @@ public class StateListener : IDisposable
private readonly HeadGearVisibilityChanged _headGearVisibility; private readonly HeadGearVisibilityChanged _headGearVisibility;
private readonly VisorStateChanged _visorState; private readonly VisorStateChanged _visorState;
private readonly WeaponVisibilityChanged _weaponVisibility; private readonly WeaponVisibilityChanged _weaponVisibility;
private readonly StateUpdated _stateUpdated; private readonly StateFinalized _stateFinalized;
private readonly AutoDesignApplier _autoDesignApplier; private readonly AutoDesignApplier _autoDesignApplier;
private readonly FunModule _funModule; private readonly FunModule _funModule;
private readonly HumanModelList _humans; private readonly HumanModelList _humans;
@ -63,7 +63,7 @@ public class StateListener : IDisposable
WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier,
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects,
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition, GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateUpdated stateUpdated) CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateFinalized stateFinalized)
{ {
_manager = manager; _manager = manager;
_items = items; _items = items;
@ -88,7 +88,7 @@ public class StateListener : IDisposable
_condition = condition; _condition = condition;
_crestService = crestService; _crestService = crestService;
_bonusSlotUpdating = bonusSlotUpdating; _bonusSlotUpdating = bonusSlotUpdating;
_stateUpdated = stateUpdated; _stateFinalized = stateFinalized;
Subscribe(); Subscribe();
} }
@ -281,7 +281,7 @@ public class StateListener : IDisposable
_objects.Update(); _objects.Update();
if (_objects.TryGetValue(identifier, out var actors) && actors.Valid) if (_objects.TryGetValue(identifier, out var actors) && actors.Valid)
_stateUpdated.Invoke(StateFinalizationType.Gearset, actors); _stateFinalized.Invoke(StateFinalizationType.Gearset, actors);
} }

View file

@ -18,20 +18,20 @@ using Penumbra.GameData.Interop;
namespace Glamourer.State; namespace Glamourer.State;
public sealed class StateManager( public sealed class StateManager(
ActorManager _actors, ActorManager actors,
ItemManager items, ItemManager items,
StateChanged @event, StateChanged changeEvent,
StateUpdated @event2, StateFinalized finalizeEvent,
StateApplier applier, StateApplier applier,
InternalStateEditor editor, InternalStateEditor editor,
HumanModelList _humans, HumanModelList humans,
IClientState _clientState, IClientState clientState,
Configuration config, Configuration config,
JobChangeState jobChange, JobChangeState jobChange,
DesignMerger merger, DesignMerger merger,
ModSettingApplier modApplier, ModSettingApplier modApplier,
GPoseService gPose) GPoseService gPose)
: StateEditor(editor, applier, @event, @event2, jobChange, config, items, merger, modApplier, gPose), : StateEditor(editor, applier, changeEvent, finalizeEvent, jobChange, config, items, merger, modApplier, gPose),
IReadOnlyDictionary<ActorIdentifier, ActorState> IReadOnlyDictionary<ActorIdentifier, ActorState>
{ {
private readonly Dictionary<ActorIdentifier, ActorState> _states = []; private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
@ -62,7 +62,7 @@ public sealed class StateManager(
/// <inheritdoc cref="GetOrCreate(ActorIdentifier, Actor, out ActorState?)"/> /// <inheritdoc cref="GetOrCreate(ActorIdentifier, Actor, out ActorState?)"/>
public bool GetOrCreate(Actor actor, [NotNullWhen(true)] out ActorState? state) public bool GetOrCreate(Actor actor, [NotNullWhen(true)] out ActorState? state)
=> GetOrCreate(actor.GetIdentifier(_actors), actor, out state); => GetOrCreate(actor.GetIdentifier(actors), actor, out state);
/// <summary> Try to obtain or create a new state for an existing actor. Returns false if no state could be created. </summary> /// <summary> Try to obtain or create a new state for an existing actor. Returns false if no state could be created. </summary>
public unsafe bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state) public unsafe bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state)
@ -82,7 +82,7 @@ public sealed class StateManager(
ModelData = FromActor(actor, true, false), ModelData = FromActor(actor, true, false),
BaseData = FromActor(actor, false, false), BaseData = FromActor(actor, false, false),
LastJob = (byte)(actor.IsCharacter ? actor.AsCharacter->CharacterData.ClassJob : 0), LastJob = (byte)(actor.IsCharacter ? actor.AsCharacter->CharacterData.ClassJob : 0),
LastTerritory = _clientState.TerritoryType, LastTerritory = clientState.TerritoryType,
}; };
// state.Identifier is owned. // state.Identifier is owned.
_states.Add(state.Identifier, state); _states.Add(state.Identifier, state);
@ -115,7 +115,7 @@ public sealed class StateManager(
// Model ID is only unambiguously contained in the game object. // Model ID is only unambiguously contained in the game object.
// The draw object only has the object type. // The draw object only has the object type.
// TODO reverse search model data to get model id from model. // TODO reverse search model data to get model id from model.
if (!_humans.IsHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId)) if (!humans.IsHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId))
{ {
ret.LoadNonHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId, *(CustomizeArray*)&actor.AsCharacter->DrawData.CustomizeData, ret.LoadNonHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId, *(CustomizeArray*)&actor.AsCharacter->DrawData.CustomizeData,
(nint)Unsafe.AsPointer(ref actor.AsCharacter->DrawData.EquipmentModelIds[0])); (nint)Unsafe.AsPointer(ref actor.AsCharacter->DrawData.EquipmentModelIds[0]));
@ -236,7 +236,7 @@ public sealed class StateManager(
public void TurnHuman(ActorState state, StateSource source, uint key = 0) public void TurnHuman(ActorState state, StateSource source, uint key = 0)
=> ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key); => ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key);
public void ResetState(ActorState state, StateSource source, uint key = 0, bool stateUpdate = false) public void ResetState(ActorState state, StateSource source, uint key = 0, bool isFinal = false)
{ {
if (!state.Unlock(key)) if (!state.Unlock(key))
return; return;
@ -278,8 +278,8 @@ public sealed class StateManager(
$"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); $"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null); StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null);
// only invoke if we define this reset call as the final call in our state update. // only invoke if we define this reset call as the final call in our state update.
if(stateUpdate) if(isFinal)
StateUpdated.Invoke(StateFinalizationType.Revert, actors); StateFinalized.Invoke(StateFinalizationType.Revert, actors);
} }
public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0) public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0)
@ -306,7 +306,7 @@ public sealed class StateManager(
$"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); $"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null); StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null);
// Update that we have completed a full operation. (We can do this directly as nothing else is linked) // Update that we have completed a full operation. (We can do this directly as nothing else is linked)
StateUpdated.Invoke(StateFinalizationType.RevertAdvanced, actors); StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, actors);
} }
public void ResetCustomize(ActorState state, StateSource source, uint key = 0) public void ResetCustomize(ActorState state, StateSource source, uint key = 0)
@ -325,7 +325,7 @@ public sealed class StateManager(
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Reset customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); $"Reset customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
// Update that we have completed a full operation. (We can do this directly as nothing else is linked) // Update that we have completed a full operation. (We can do this directly as nothing else is linked)
StateUpdated.Invoke(StateFinalizationType.RevertCustomize, actors); StateFinalized.Invoke(StateFinalizationType.RevertCustomize, actors);
} }
public void ResetEquip(ActorState state, StateSource source, uint key = 0) public void ResetEquip(ActorState state, StateSource source, uint key = 0)
@ -376,7 +376,7 @@ public sealed class StateManager(
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Reset equipment state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); $"Reset equipment state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
// Update that we have completed a full operation. (We can do this directly as nothing else is linked) // Update that we have completed a full operation. (We can do this directly as nothing else is linked)
StateUpdated.Invoke(StateFinalizationType.RevertEquipment, actors); StateFinalized.Invoke(StateFinalizationType.RevertEquipment, actors);
} }
public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0) public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0)
@ -453,23 +453,23 @@ public sealed class StateManager(
} }
} }
public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool stateUpdate = false) public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool isFinal = false)
{ {
if (!GetOrCreate(actor, out var state)) if (!GetOrCreate(actor, out var state))
return; return;
ReapplyState(actor, state, forceRedraw, source, stateUpdate); ReapplyState(actor, state, forceRedraw, source, isFinal);
} }
public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool stateUpdate) public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool isFinal)
{ {
var data = Applier.ApplyAll(state, var data = Applier.ApplyAll(state,
forceRedraw forceRedraw
|| !actor.Model.IsHuman || !actor.Model.IsHuman
|| CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false); || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null); StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null);
if(stateUpdate) if(isFinal)
StateUpdated.Invoke(StateFinalizationType.Reapply, data); StateFinalized.Invoke(StateFinalizationType.Reapply, data);
} }
/// <summary> Automation variant for reapply, to fire the correct StateUpdateType once reapplied. </summary> /// <summary> Automation variant for reapply, to fire the correct StateUpdateType once reapplied. </summary>
@ -490,7 +490,7 @@ public sealed class StateManager(
|| CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false); || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null); StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null);
// invoke the automation update based on what reset is. // invoke the automation update based on what reset is.
StateUpdated.Invoke(wasReset ? StateFinalizationType.RevertAutomation : StateFinalizationType.ReapplyAutomation, data); StateFinalized.Invoke(wasReset ? StateFinalizationType.RevertAutomation : StateFinalizationType.ReapplyAutomation, data);
} }
public void DeleteState(ActorIdentifier identifier) public void DeleteState(ActorIdentifier identifier)