mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-13 12:14:18 +01:00
Implements true endpoints for all glamourer operations, also correctly marks reverts and gearsets. Replaced back excessive logging to maintain with logging formats expected by glamourer.
This commit is contained in:
parent
c605d19510
commit
e1a41b5f3c
21 changed files with 225 additions and 99 deletions
|
|
@ -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);
|
ResetMaterials: !once && key != 0, SendStateUpdate: 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);
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ public sealed class IpcProviders : IDisposable, IApiService
|
||||||
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),
|
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),
|
||||||
IpcSubscribers.StateChanged.Provider(pi, api.State),
|
IpcSubscribers.StateChanged.Provider(pi, api.State),
|
||||||
IpcSubscribers.StateChangedWithType.Provider(pi, api.State),
|
IpcSubscribers.StateChangedWithType.Provider(pi, api.State),
|
||||||
|
IpcSubscribers.StateUpdated.Provider(pi, api.State),
|
||||||
IpcSubscribers.GPoseChanged.Provider(pi, api.State),
|
IpcSubscribers.GPoseChanged.Provider(pi, api.State),
|
||||||
];
|
];
|
||||||
_initializedProvider.Invoke();
|
_initializedProvider.Invoke();
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ 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;
|
||||||
|
|
||||||
|
|
@ -23,6 +24,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 GPoseService _gPose;
|
private readonly GPoseService _gPose;
|
||||||
|
|
||||||
public StateApi(ApiHelpers helpers,
|
public StateApi(ApiHelpers helpers,
|
||||||
|
|
@ -32,6 +34,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
AutoDesignApplier autoDesigns,
|
AutoDesignApplier autoDesigns,
|
||||||
ObjectManager objects,
|
ObjectManager objects,
|
||||||
StateChanged stateChanged,
|
StateChanged stateChanged,
|
||||||
|
StateUpdated stateUpdated,
|
||||||
GPoseService gPose)
|
GPoseService gPose)
|
||||||
{
|
{
|
||||||
_helpers = helpers;
|
_helpers = helpers;
|
||||||
|
|
@ -41,8 +44,10 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
_autoDesigns = autoDesigns;
|
_autoDesigns = autoDesigns;
|
||||||
_objects = objects;
|
_objects = objects;
|
||||||
_stateChanged = stateChanged;
|
_stateChanged = stateChanged;
|
||||||
|
_stateUpdated = stateUpdated;
|
||||||
_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);
|
||||||
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.GlamourerIpc);
|
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.GlamourerIpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,13 +255,14 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
|
|
||||||
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, StateUpdateType>? StateUpdated;
|
||||||
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);
|
ResetMaterials: !once && key != 0, SendStateUpdate: true);
|
||||||
_stateManager.ApplyDesign(state, design, settings);
|
_stateManager.ApplyDesign(state, design, settings);
|
||||||
ApiHelpers.Lock(state, key, flags);
|
ApiHelpers.Lock(state, key, flags);
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +302,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
{
|
{
|
||||||
var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
|
var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
|
||||||
_autoDesigns.ReapplyAutomation(actor, state.Identifier, state, true, out var forcedRedraw);
|
_autoDesigns.ReapplyAutomation(actor, state.Identifier, state, true, out var forcedRedraw);
|
||||||
_stateManager.ReapplyState(actor, state, forcedRedraw, source);
|
_stateManager.ReapplyAutomationState(actor, state, forcedRedraw, true, source);
|
||||||
ApiHelpers.Lock(state, key, flags);
|
ApiHelpers.Lock(state, key, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,7 +339,8 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
|
|
||||||
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
|
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
|
||||||
{
|
{
|
||||||
Glamourer.Log.Error($"[OnStateChanged API CALL] Sending out OnStateChanged with type {type}.");
|
// Remove this comment before creating PR.
|
||||||
|
Glamourer.Log.Verbose($"[OnStateChanged] Sending out OnStateChanged with type {type}.");
|
||||||
|
|
||||||
if (StateChanged != null)
|
if (StateChanged != null)
|
||||||
foreach (var actor in actors.Objects)
|
foreach (var actor in actors.Objects)
|
||||||
|
|
@ -343,4 +350,16 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
foreach (var actor in actors.Objects)
|
foreach (var actor in actors.Objects)
|
||||||
StateChangedWithType.Invoke(actor.Address, type);
|
StateChangedWithType.Invoke(actor.Address, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnStateUpdated(StateUpdateType type, ActorData actors)
|
||||||
|
{
|
||||||
|
if (StateUpdated != null)
|
||||||
|
foreach (var actor in actors.Objects)
|
||||||
|
{
|
||||||
|
// Remove these before creating PR
|
||||||
|
Glamourer.Log.Information($"[ENDPOINT DEBUGGING] 0x{actor.Address:X} had update of type {type}.");
|
||||||
|
Glamourer.Log.Information("--------------------------------------------------------------");
|
||||||
|
StateUpdated.Invoke(actor.Address, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -216,8 +216,6 @@ public sealed class AutoDesignApplier : IDisposable
|
||||||
if (!_config.EnableAutoDesigns || !actor.Identifier(_actors, out var id))
|
if (!_config.EnableAutoDesigns || !actor.Identifier(_actors, out var id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Glamourer.Log.Information($"[AutoDesignApplier][OnJobChange] We had EnableAutoDesigns active, and are a valid actor!");
|
|
||||||
|
|
||||||
if (!GetPlayerSet(id, out var set))
|
if (!GetPlayerSet(id, out var set))
|
||||||
{
|
{
|
||||||
if (_state.TryGetValue(id, out var s))
|
if (_state.TryGetValue(id, out var s))
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ public readonly record struct ApplySettings(
|
||||||
bool FromJobChange = false,
|
bool FromJobChange = false,
|
||||||
bool UseSingleSource = false,
|
bool UseSingleSource = false,
|
||||||
bool MergeLinks = false,
|
bool MergeLinks = false,
|
||||||
bool ResetMaterials = false)
|
bool ResetMaterials = false,
|
||||||
|
bool SendStateUpdate = false)
|
||||||
{
|
{
|
||||||
public static readonly ApplySettings Manual = new()
|
public static readonly ApplySettings Manual = new()
|
||||||
{
|
{
|
||||||
|
|
@ -24,6 +25,7 @@ public readonly record struct ApplySettings(
|
||||||
UseSingleSource = false,
|
UseSingleSource = false,
|
||||||
MergeLinks = false,
|
MergeLinks = false,
|
||||||
ResetMaterials = false,
|
ResetMaterials = false,
|
||||||
|
SendStateUpdate = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly ApplySettings ManualWithLinks = new()
|
public static readonly ApplySettings ManualWithLinks = new()
|
||||||
|
|
@ -35,6 +37,7 @@ public readonly record struct ApplySettings(
|
||||||
UseSingleSource = false,
|
UseSingleSource = false,
|
||||||
MergeLinks = true,
|
MergeLinks = true,
|
||||||
ResetMaterials = false,
|
ResetMaterials = false,
|
||||||
|
SendStateUpdate = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly ApplySettings Game = new()
|
public static readonly ApplySettings Game = new()
|
||||||
|
|
@ -46,6 +49,7 @@ public readonly record struct ApplySettings(
|
||||||
UseSingleSource = false,
|
UseSingleSource = false,
|
||||||
MergeLinks = false,
|
MergeLinks = false,
|
||||||
ResetMaterials = true,
|
ResetMaterials = true,
|
||||||
|
SendStateUpdate = false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
23
Glamourer/Events/GearsetDataLoaded.cs
Normal file
23
Glamourer/Events/GearsetDataLoaded.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
using OtterGui.Classes;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
namespace Glamourer.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers when the equipped gearset finished running all of its LoadEquipment, LoadWeapon, and crest calls.
|
||||||
|
/// This defines a universal endpoint of base game state application to monitor.
|
||||||
|
/// <list type="number">
|
||||||
|
/// <item>The model drawobject associated with the finished load (should always be ClientPlayer) </item>
|
||||||
|
/// </list>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class GearsetDataLoaded()
|
||||||
|
: EventWrapper<Model, GearsetDataLoaded.Priority>(nameof(GearsetDataLoaded))
|
||||||
|
{
|
||||||
|
public enum Priority
|
||||||
|
{
|
||||||
|
/// <seealso cref="State.StateListener.OnEquippedGearsetLoaded"/>
|
||||||
|
StateListener = 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Glamourer/Events/StateUpdated.cs
Normal file
26
Glamourer/Events/StateUpdated.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
using Glamourer.Api.Enums;
|
||||||
|
using Glamourer.Designs.History;
|
||||||
|
using Glamourer.Interop.Structs;
|
||||||
|
using Glamourer.State;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
|
||||||
|
namespace Glamourer.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggered when a Design is edited in any way.
|
||||||
|
/// <list type="number">
|
||||||
|
/// <item>Parameter is the type of the change </item>
|
||||||
|
/// <item>Parameter is the changed saved state. </item>
|
||||||
|
/// <item>Parameter is the existing actors using this saved state. </item>
|
||||||
|
/// <item>Parameter is any additional data depending on the type of change. </item>
|
||||||
|
/// </list>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StateUpdated()
|
||||||
|
: EventWrapper<StateUpdateType, ActorData, StateUpdated.Priority>(nameof(StateUpdated))
|
||||||
|
{
|
||||||
|
public enum Priority
|
||||||
|
{
|
||||||
|
/// <seealso cref="Api.GlamourerIpc.OnStateUpdated"/>
|
||||||
|
GlamourerIpc = int.MinValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = 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);
|
_stateManager.ResetState(state!, StateSource.Manual, stateUpdate: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawRevertAutomationButton(Vector2 buttonSize)
|
private void DrawRevertAutomationButton(Vector2 buttonSize)
|
||||||
|
|
@ -252,7 +252,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
||||||
foreach (var actor in data.Objects)
|
foreach (var actor in data.Objects)
|
||||||
{
|
{
|
||||||
_autoDesignApplier.ReapplyAutomation(actor, id, state!, true, out var forcedRedraw);
|
_autoDesignApplier.ReapplyAutomation(actor, id, state!, true, out var forcedRedraw);
|
||||||
_stateManager.ReapplyState(actor, forcedRedraw, StateSource.Manual);
|
_stateManager.ReapplyAutomationState(actor, forcedRedraw, true, StateSource.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,7 +292,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
||||||
foreach (var actor in data.Objects)
|
foreach (var actor in data.Objects)
|
||||||
{
|
{
|
||||||
_autoDesignApplier.ReapplyAutomation(actor, id, state!, false, out var forcedRedraw);
|
_autoDesignApplier.ReapplyAutomation(actor, id, state!, false, out var forcedRedraw);
|
||||||
_stateManager.ReapplyState(actor, forcedRedraw, StateSource.Manual);
|
_stateManager.ReapplyAutomationState(actor, forcedRedraw, false, StateSource.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
_stateManager.ResetState(_state!, StateSource.Manual, stateUpdate: true);
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
|
@ -394,7 +394,7 @@ public class ActorPanel
|
||||||
!_config.EnableAutoDesigns || _state!.IsLocked))
|
!_config.EnableAutoDesigns || _state!.IsLocked))
|
||||||
{
|
{
|
||||||
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, false, out var forcedRedraw);
|
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, false, out var forcedRedraw);
|
||||||
_stateManager.ReapplyState(_actor, forcedRedraw, StateSource.Manual);
|
_stateManager.ReapplyAutomationState(_actor, forcedRedraw, false, StateSource.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -403,14 +403,14 @@ public class ActorPanel
|
||||||
!_config.EnableAutoDesigns || _state!.IsLocked))
|
!_config.EnableAutoDesigns || _state!.IsLocked))
|
||||||
{
|
{
|
||||||
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, true, out var forcedRedraw);
|
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, true, out var forcedRedraw);
|
||||||
_stateManager.ReapplyState(_actor, forcedRedraw, StateSource.Manual);
|
_stateManager.ReapplyAutomationState(_actor, forcedRedraw, true, StateSource.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiUtil.DrawDisabledButton("Reapply", Vector2.Zero,
|
if (ImGuiUtil.DrawDisabledButton("Reapply", Vector2.Zero,
|
||||||
"Try to reapply the configured state if something went wrong. Should generally not be necessary.",
|
"Try to reapply the configured state if something went wrong. Should generally not be necessary.",
|
||||||
_state!.IsLocked))
|
_state!.IsLocked))
|
||||||
_stateManager.ReapplyState(_actor, false, StateSource.Manual);
|
_stateManager.ReapplyState(_actor, false, StateSource.Manual, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawApplyToSelf()
|
private void DrawApplyToSelf()
|
||||||
|
|
@ -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);
|
ApplySettings.Manual with { SendStateUpdate = 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);
|
ApplySettings.Manual with { SendStateUpdate = 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);
|
panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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);
|
_state.ApplyDesign(state!, design, ApplySettings.Manual with { SendStateUpdate = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
using (ImRaii.Group())
|
using (ImRaii.Group())
|
||||||
|
|
|
||||||
|
|
@ -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);
|
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { SendStateUpdate = 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);
|
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
_state.ApplyDesign(state, design, ApplySettings.Manual with { SendStateUpdate = 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);
|
_state.ApplyDesign(state, design, ApplySettings.Manual with { SendStateUpdate = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
|
||||||
if (!model.IsHuman)
|
if (!model.IsHuman)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Glamourer.Log.Information($"[ChangeCustomize] Glamour-Invoked on 0x{model.Address:X} with {customize}.");
|
Glamourer.Log.Verbose($"[ChangeCustomize] Invoked on 0x{model.Address:X} with {customize}.");
|
||||||
using var _ = InUpdate.EnterMethod();
|
using var _ = InUpdate.EnterMethod();
|
||||||
var ret = _original(model.AsHuman, customize.Data, true);
|
var ret = _original(model.AsHuman, customize.Data, true);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -86,8 +86,6 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
|
||||||
Invoke(human, ref *(CustomizeArray*)data);
|
Invoke(human, ref *(CustomizeArray*)data);
|
||||||
|
|
||||||
var ret = _changeCustomizeHook.Original(human, data, skipEquipment);
|
var ret = _changeCustomizeHook.Original(human, data, skipEquipment);
|
||||||
|
|
||||||
Glamourer.Log.Information($"[ChangeCustomize] Called on with {*(CustomizeArray*)data} ({ret}).");
|
|
||||||
_postEvent.Invoke(human);
|
_postEvent.Invoke(human);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
||||||
private readonly EquippedGearset _gearsetEvent;
|
private readonly EquippedGearset _gearsetEvent;
|
||||||
private readonly List<(EquipSlot, uint, StainIds)> _itemList = new(12);
|
private readonly List<(EquipSlot, uint, StainIds)> _itemList = new(12);
|
||||||
|
|
||||||
// This can be moved into client structs or penumbra.gamedata when needed.
|
// Called by EquipGearset, but returns a pointer instead of an int.
|
||||||
|
// This is the internal function processed by all sources of Equipping a gearset,
|
||||||
|
// such as hotbar gearset application and command gearset application
|
||||||
public const string EquipGearsetInternal = "40 55 53 56 57 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 4C 63 FA";
|
public const string EquipGearsetInternal = "40 55 53 56 57 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 4C 63 FA";
|
||||||
private delegate nint ChangeGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId);
|
private delegate nint ChangeGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId);
|
||||||
|
|
||||||
|
|
@ -56,7 +58,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
||||||
var ret = _equipGearsetInternalHook.Original(module, gearsetId, glamourPlateId);
|
var ret = _equipGearsetInternalHook.Original(module, gearsetId, glamourPlateId);
|
||||||
var set = module->GetGearset((int)gearsetId);
|
var set = module->GetGearset((int)gearsetId);
|
||||||
_gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob);
|
_gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob);
|
||||||
Glamourer.Log.Warning($"[InventoryService] [EquipInternal] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
|
Glamourer.Log.Verbose($"[InventoryService] [EquipInternal] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
var entry = module->GetGearset((int)gearsetId);
|
var entry = module->GetGearset((int)gearsetId);
|
||||||
|
|
@ -132,7 +134,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
||||||
private int EquipGearSetDetour(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId)
|
private int EquipGearSetDetour(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId)
|
||||||
{
|
{
|
||||||
var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
|
var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
|
||||||
Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
|
Glamourer.Log.Excessive($"[InventoryService] (old) Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +150,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
||||||
InventoryType targetContainer, ushort targetSlot, byte unk)
|
InventoryType targetContainer, ushort targetSlot, byte unk)
|
||||||
{
|
{
|
||||||
var ret = _moveItemHook.Original(manager, sourceContainer, sourceSlot, targetContainer, targetSlot, unk);
|
var ret = _moveItemHook.Original(manager, sourceContainer, sourceSlot, targetContainer, targetSlot, unk);
|
||||||
Glamourer.Log.Verbose($"[InventoryService] Moved {sourceContainer} {sourceSlot} {targetContainer} {targetSlot} (Returned {ret})");
|
Glamourer.Log.Excessive($"[InventoryService] Moved {sourceContainer} {sourceSlot} {targetContainer} {targetSlot} (Returned {ret})");
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
if (InvokeSource(sourceContainer, sourceSlot, out var source))
|
if (InvokeSource(sourceContainer, sourceSlot, out var source))
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public class JobService : IDisposable
|
||||||
var newJob = Jobs.TryGetValue(newJobIndex, out var j) ? j : Jobs[0];
|
var newJob = Jobs.TryGetValue(newJobIndex, out var j) ? j : Jobs[0];
|
||||||
var oldJob = Jobs.TryGetValue(oldJobIndex, out var o) ? o : Jobs[0];
|
var oldJob = Jobs.TryGetValue(oldJobIndex, out var o) ? o : Jobs[0];
|
||||||
|
|
||||||
Glamourer.Log.Error($"{actor} changed job from {oldJob} to {newJob}.");
|
Glamourer.Log.Excessive($"{actor} changed job from {oldJob} to {newJob}.");
|
||||||
JobChanged?.Invoke(actor, oldJob, newJob);
|
JobChanged?.Invoke(actor, oldJob, newJob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService
|
||||||
_actions.Enqueue((state, () =>
|
_actions.Enqueue((state, () =>
|
||||||
{
|
{
|
||||||
foreach (var actor in actors.Objects)
|
foreach (var actor in actors.Objects)
|
||||||
_state.ReapplyState(actor, state, false, StateSource.IpcManual);
|
_state.ReapplyState(actor, state, false, StateSource.IpcManual, true);
|
||||||
Glamourer.Log.Debug($"Automatically applied mod settings of type {type} to {id.Incognito(null)}.");
|
Glamourer.Log.Debug($"Automatically applied mod settings of type {type} to {id.Incognito(null)}.");
|
||||||
}, WaitFrames));
|
}, WaitFrames));
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +108,7 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService
|
||||||
_frame = currentFrame;
|
_frame = currentFrame;
|
||||||
_framework.RunOnFrameworkThread(() =>
|
_framework.RunOnFrameworkThread(() =>
|
||||||
{
|
{
|
||||||
_state.ReapplyState(_objects.Player, false, StateSource.IpcManual);
|
_state.ReapplyState(_objects.Player, false, StateSource.IpcManual, true);
|
||||||
Glamourer.Log.Debug(
|
Glamourer.Log.Debug(
|
||||||
$"Automatically applied mod settings of type {type} to {_objects.PlayerData.Identifier.Incognito(null)} (Local Player).");
|
$"Automatically applied mod settings of type {type} to {_objects.PlayerData.Identifier.Incognito(null)} (Local Player).");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public readonly struct GearsetItemDataStruct
|
||||||
[FieldOffset(16)] public readonly byte CrestBitField; // A Bitfield:: ShieldCrest == 1, HeadCrest == 2, Chest Crest == 4
|
[FieldOffset(16)] public readonly byte CrestBitField; // A Bitfield:: ShieldCrest == 1, HeadCrest == 2, Chest Crest == 4
|
||||||
[FieldOffset(17)] public readonly byte JobId; // Job ID associated with the gearset change.
|
[FieldOffset(17)] public readonly byte JobId; // Job ID associated with the gearset change.
|
||||||
|
|
||||||
// Flicks from 0 to 128 (anywhere inbetween), have yet to associate what it is linked to. Remains the same when flicking between gearsets of the same job.
|
// Flicks from 0 to 127 (anywhere inbetween), have yet to associate what it is linked to. Remains the same when flicking between gearsets of the same job.
|
||||||
[FieldOffset(18)] public readonly byte UNK_18;
|
[FieldOffset(18)] public readonly byte UNK_18;
|
||||||
[FieldOffset(19)] public readonly byte UNK_19; // I have never seen this be anything other than 0.
|
[FieldOffset(19)] public readonly byte UNK_19; // I have never seen this be anything other than 0.
|
||||||
|
|
||||||
|
|
@ -56,69 +56,47 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
{
|
{
|
||||||
public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
|
public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
|
||||||
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
|
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
|
||||||
|
public readonly GearsetDataLoaded GearsetDataLoadedEvent;
|
||||||
private readonly DictBonusItems _bonusItems;
|
private readonly DictBonusItems _bonusItems;
|
||||||
|
|
||||||
#region LoadAllEquipData
|
// This function is what calls the weapon/equipment/crest loads, which call FlagSlotForUpdate if different. (MetaData not included)
|
||||||
///////////////////////////////////////////////////
|
public const string LoadGearsetDataSig = "48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 44 0F B6 B9";
|
||||||
// This is a currently undocumented signature that loads all equipment after changing a gearset.
|
private delegate Int64 LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData);
|
||||||
// :: Signature Maintainers Note:
|
private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData)
|
||||||
// To obtain this signature, get the stacktrace from FlagSlotForUpdate for human, and find func `sub_140842F50`.
|
|
||||||
// This function is what calls the weapon/equipment/crest loads, which call FlagSlotForUpdate if different.
|
|
||||||
//
|
|
||||||
// By detouring this function, and executing the original, then logic after, we have a consistant point in time where we know all
|
|
||||||
// slots have been flagged, meaning a consistant point in time that glamourer has processed all of its updates.
|
|
||||||
public const string LoadAllEquipmentSig = "48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 44 0F B6 B9";
|
|
||||||
private delegate Int64 LoadAllEquipmentDelegate(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData);
|
|
||||||
private Int64 LoadAllEquipmentDetour(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData)
|
|
||||||
{
|
{
|
||||||
// return original first so we can log the changes after
|
// Let the gearset data process all of its loads and slot flag update calls first.
|
||||||
var ret = _loadAllEquipmentHook.Original(drawDataContainer, gearsetData);
|
var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
|
||||||
|
// Ensure that the owner of the drawdata container is a character base.
|
||||||
|
Model ownerDrawObject = drawDataContainer->OwnerObject->DrawObject;
|
||||||
|
if (!ownerDrawObject.IsCharacterBase)
|
||||||
|
return ret;
|
||||||
|
|
||||||
// perform logic stuff.
|
// invoke the changed event for the state listener and return.
|
||||||
var owner = drawDataContainer->OwnerObject;
|
Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] Owner: 0x{ownerDrawObject.Address:X} Finished Applying its GameState!");
|
||||||
Glamourer.Log.Warning($"[LoadAllEquipmentDetour] Owner: 0x{(nint)owner->DrawObject:X} Finished Applying its GameState!");
|
// Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
|
||||||
Glamourer.Log.Warning($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
|
GearsetDataLoadedEvent.Invoke(drawDataContainer->OwnerObject->DrawObject);
|
||||||
|
|
||||||
// return original.
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatWeaponModelId(WeaponModelId weaponModelId) => $"Id: {weaponModelId.Id}, Type: {weaponModelId.Type}, Variant: {weaponModelId.Variant}, Stain0: {weaponModelId.Stain0}, Stain1: {weaponModelId.Stain1}";
|
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded,
|
||||||
|
IGameInteropProvider interop, DictBonusItems bonusItems)
|
||||||
private string FormatGearsetItemDataStruct(GearsetItemDataStruct gearsetItemData)
|
|
||||||
{
|
|
||||||
string ret = $"\nMainhandWeaponData: {FormatWeaponModelId(gearsetItemData.MainhandWeaponData)}," +
|
|
||||||
$"\nOffhandWeaponData: {FormatWeaponModelId(gearsetItemData.OffhandWeaponData)}," +
|
|
||||||
$"\nCrestBitField: {gearsetItemData.CrestBitField} | JobId: {gearsetItemData.JobId} | UNK_18: {gearsetItemData.UNK_18} | UNK_19: {gearsetItemData.UNK_19}";
|
|
||||||
// Iterate through offsets from 20 to 60 and format the CharacterArmor data
|
|
||||||
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
|
|
||||||
{
|
|
||||||
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetItemData + offset);
|
|
||||||
int dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset
|
|
||||||
byte* dyePtr = (byte*)&gearsetItemData + dyeOffset;
|
|
||||||
ret += $"\nEquipSlot {((EquipSlot)(dyeOffset-60)).ToString()}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}";
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endregion LoadAllEquipData
|
|
||||||
|
|
||||||
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, IGameInteropProvider interop,
|
|
||||||
DictBonusItems bonusItems)
|
|
||||||
{
|
{
|
||||||
EquipSlotUpdatingEvent = equipSlotUpdating;
|
EquipSlotUpdatingEvent = equipSlotUpdating;
|
||||||
BonusSlotUpdatingEvent = bonusSlotUpdating;
|
BonusSlotUpdatingEvent = bonusSlotUpdating;
|
||||||
|
GearsetDataLoadedEvent = gearsetDataLoaded;
|
||||||
|
|
||||||
_bonusItems = bonusItems;
|
_bonusItems = bonusItems;
|
||||||
interop.InitializeFromAttributes(this);
|
interop.InitializeFromAttributes(this);
|
||||||
_flagSlotForUpdateHook.Enable();
|
_flagSlotForUpdateHook.Enable();
|
||||||
_flagBonusSlotForUpdateHook.Enable();
|
_flagBonusSlotForUpdateHook.Enable();
|
||||||
_loadAllEquipmentHook.Enable();
|
_loadGearsetDataHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_flagSlotForUpdateHook.Dispose();
|
_flagSlotForUpdateHook.Dispose();
|
||||||
_flagBonusSlotForUpdateHook.Dispose();
|
_flagBonusSlotForUpdateHook.Dispose();
|
||||||
_loadAllEquipmentHook.Dispose();
|
_loadGearsetDataHook.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateEquipSlot(Model drawObject, EquipSlot slot, CharacterArmor data)
|
public void UpdateEquipSlot(Model drawObject, EquipSlot slot, CharacterArmor data)
|
||||||
|
|
@ -167,18 +145,16 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
[Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))]
|
[Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))]
|
||||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagBonusSlotForUpdateHook = null!;
|
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagBonusSlotForUpdateHook = null!;
|
||||||
|
|
||||||
[Signature(LoadAllEquipmentSig, DetourName = nameof(LoadAllEquipmentDetour))]
|
[Signature(LoadGearsetDataSig, DetourName = nameof(LoadGearsetDataDetour))]
|
||||||
private readonly Hook<LoadAllEquipmentDelegate> _loadAllEquipmentHook = null!;
|
private readonly Hook<LoadGearsetDataDelegate> _loadGearsetDataHook = null!;
|
||||||
|
|
||||||
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||||
{
|
{
|
||||||
var slot = slotIdx.ToEquipSlot();
|
var slot = slotIdx.ToEquipSlot();
|
||||||
var returnValue = ulong.MaxValue;
|
var returnValue = ulong.MaxValue;
|
||||||
|
|
||||||
EquipSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
EquipSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
||||||
Glamourer.Log.Information($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
Glamourer.Log.Excessive($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
||||||
returnValue = returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
returnValue = returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
||||||
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,17 +162,35 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
{
|
{
|
||||||
var slot = slotIdx.ToBonusSlot();
|
var slot = slotIdx.ToBonusSlot();
|
||||||
var returnValue = ulong.MaxValue;
|
var returnValue = ulong.MaxValue;
|
||||||
|
|
||||||
BonusSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
BonusSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
||||||
Glamourer.Log.Information($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
||||||
returnValue = returnValue == ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
returnValue = returnValue == ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
||||||
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
||||||
{
|
{
|
||||||
Glamourer.Log.Warning($"Glamour-Invoked Equip Slot update for 0x{drawObject.Address:X} with {slot} and {armor}.");
|
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Invoked by Glamourer on 0x{drawObject.Address:X} on {slot} with itemdata {armor}.");
|
||||||
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you ever care to debug this, here is a formatted string output of this new gearsetDataPacket struct.
|
||||||
|
private string FormatGearsetItemDataStruct(GearsetItemDataStruct gearsetData)
|
||||||
|
{
|
||||||
|
string ret =
|
||||||
|
$"\nMainhandWeaponData: Id: {gearsetData.MainhandWeaponData.Id}, Type: {gearsetData.MainhandWeaponData.Type}, " +
|
||||||
|
$"Variant: {gearsetData.MainhandWeaponData.Variant}, Stain0: {gearsetData.MainhandWeaponData.Stain0}, Stain1: {gearsetData.MainhandWeaponData.Stain1}" +
|
||||||
|
$"\nOffhandWeaponData: Id: {gearsetData.OffhandWeaponData.Id}, Type: {gearsetData.OffhandWeaponData.Type}, " +
|
||||||
|
$"Variant: {gearsetData.OffhandWeaponData.Variant}, Stain0: {gearsetData.OffhandWeaponData.Stain0}, Stain1: {gearsetData.OffhandWeaponData.Stain1}" +
|
||||||
|
$"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId} | UNK_18: {gearsetData.UNK_18} | UNK_19: {gearsetData.UNK_19}";
|
||||||
|
// Iterate through offsets from 20 to 60 and format the CharacterArmor data
|
||||||
|
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
|
||||||
|
{
|
||||||
|
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
|
||||||
|
int dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset
|
||||||
|
byte* dyePtr = (byte*)&gearsetData + dyeOffset;
|
||||||
|
ret += $"\nEquipSlot {((EquipSlot)(dyeOffset - 60)).ToString()}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}";
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
if (_stateManager.GetOrCreate(identifier, actor, out var state))
|
if (_stateManager.GetOrCreate(identifier, actor, out var state))
|
||||||
{
|
{
|
||||||
_autoDesignApplier.ReapplyAutomation(actor, identifier, state, revert, out var forcedRedraw);
|
_autoDesignApplier.ReapplyAutomation(actor, identifier, state, revert, out var forcedRedraw);
|
||||||
_stateManager.ReapplyState(actor, forcedRedraw, StateSource.Manual);
|
_stateManager.ReapplyAutomationState(actor, forcedRedraw, revert, StateSource.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -378,7 +378,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
foreach (var actor in data.Objects)
|
foreach (var actor in data.Objects)
|
||||||
_stateManager.ReapplyState(actor, false, StateSource.Manual);
|
_stateManager.ReapplyState(actor, false, StateSource.Manual, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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);
|
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = 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);
|
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ public class StateEditor(
|
||||||
InternalStateEditor editor,
|
InternalStateEditor editor,
|
||||||
StateApplier applier,
|
StateApplier applier,
|
||||||
StateChanged stateChanged,
|
StateChanged stateChanged,
|
||||||
|
StateUpdated stateUpdated,
|
||||||
JobChangeState jobChange,
|
JobChangeState jobChange,
|
||||||
Configuration config,
|
Configuration config,
|
||||||
ItemManager items,
|
ItemManager items,
|
||||||
|
|
@ -27,6 +28,7 @@ public class StateEditor(
|
||||||
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 Configuration Config = config;
|
protected readonly Configuration Config = config;
|
||||||
protected readonly ItemManager Items = items;
|
protected readonly ItemManager Items = items;
|
||||||
|
|
||||||
|
|
@ -41,6 +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(StateUpdateType.ModelChange, actors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -419,6 +422,8 @@ public class StateEditor(
|
||||||
|
|
||||||
Glamourer.Log.Debug($"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
|
Glamourer.Log.Debug($"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)
|
||||||
|
StateUpdated.Invoke(StateUpdateType.DesignApplied, actors);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ using Penumbra.GameData.DataContainers;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Penumbra.GameData.Interop;
|
using Penumbra.GameData.Interop;
|
||||||
using ObjectManager = Glamourer.Interop.ObjectManager;
|
using ObjectManager = Glamourer.Interop.ObjectManager;
|
||||||
|
using Glamourer.Api.Enums;
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
|
|
@ -34,10 +35,12 @@ public class StateListener : IDisposable
|
||||||
private readonly PenumbraService _penumbra;
|
private readonly PenumbraService _penumbra;
|
||||||
private readonly EquipSlotUpdating _equipSlotUpdating;
|
private readonly EquipSlotUpdating _equipSlotUpdating;
|
||||||
private readonly BonusSlotUpdating _bonusSlotUpdating;
|
private readonly BonusSlotUpdating _bonusSlotUpdating;
|
||||||
|
private readonly GearsetDataLoaded _gearsetDataLoaded;
|
||||||
private readonly WeaponLoading _weaponLoading;
|
private readonly WeaponLoading _weaponLoading;
|
||||||
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 AutoDesignApplier _autoDesignApplier;
|
private readonly AutoDesignApplier _autoDesignApplier;
|
||||||
private readonly FunModule _funModule;
|
private readonly FunModule _funModule;
|
||||||
private readonly HumanModelList _humans;
|
private readonly HumanModelList _humans;
|
||||||
|
|
@ -54,11 +57,11 @@ public class StateListener : IDisposable
|
||||||
private ActorState? _customizeState;
|
private ActorState? _customizeState;
|
||||||
|
|
||||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config,
|
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config,
|
||||||
EquipSlotUpdating equipSlotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState,
|
EquipSlotUpdating equipSlotUpdating, GearsetDataLoaded gearsetDataLoaded, WeaponLoading weaponLoading, VisorStateChanged visorState,
|
||||||
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)
|
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateUpdated stateUpdated)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_items = items;
|
_items = items;
|
||||||
|
|
@ -66,6 +69,7 @@ public class StateListener : IDisposable
|
||||||
_actors = actors;
|
_actors = actors;
|
||||||
_config = config;
|
_config = config;
|
||||||
_equipSlotUpdating = equipSlotUpdating;
|
_equipSlotUpdating = equipSlotUpdating;
|
||||||
|
_gearsetDataLoaded = gearsetDataLoaded;
|
||||||
_weaponLoading = weaponLoading;
|
_weaponLoading = weaponLoading;
|
||||||
_visorState = visorState;
|
_visorState = visorState;
|
||||||
_weaponVisibility = weaponVisibility;
|
_weaponVisibility = weaponVisibility;
|
||||||
|
|
@ -82,6 +86,7 @@ public class StateListener : IDisposable
|
||||||
_condition = condition;
|
_condition = condition;
|
||||||
_crestService = crestService;
|
_crestService = crestService;
|
||||||
_bonusSlotUpdating = bonusSlotUpdating;
|
_bonusSlotUpdating = bonusSlotUpdating;
|
||||||
|
_stateUpdated = stateUpdated;
|
||||||
Subscribe();
|
Subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,6 +264,22 @@ public class StateListener : IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnGearsetDataLoaded(Model model)
|
||||||
|
{
|
||||||
|
var actor = _penumbra.GameObjectFromDrawObject(model);
|
||||||
|
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// ensure actor and state are valid.
|
||||||
|
if (!actor.Identifier(_actors, out var identifier))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_objects.Update();
|
||||||
|
if (_objects.TryGetValue(identifier, out var actors) && actors.Valid)
|
||||||
|
_stateUpdated.Invoke(StateUpdateType.Gearset, actors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void OnMovedEquipment((EquipSlot, uint, StainIds)[] items)
|
private void OnMovedEquipment((EquipSlot, uint, StainIds)[] items)
|
||||||
{
|
{
|
||||||
_objects.Update();
|
_objects.Update();
|
||||||
|
|
@ -382,7 +403,7 @@ public class StateListener : IDisposable
|
||||||
lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant,
|
lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant,
|
||||||
weapon.Stains);
|
weapon.Stains);
|
||||||
_fistOffhands[actor] = lastFistOffhand;
|
_fistOffhands[actor] = lastFistOffhand;
|
||||||
Glamourer.Log.Verbose($"Storing fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}.");
|
Glamourer.Log.Excessive($"Storing fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_funModule.ApplyFunToWeapon(actor, ref weapon, slot);
|
_funModule.ApplyFunToWeapon(actor, ref weapon, slot);
|
||||||
|
|
@ -765,6 +786,7 @@ public class StateListener : IDisposable
|
||||||
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
|
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
|
||||||
_equipSlotUpdating.Subscribe(OnEquipSlotUpdating, EquipSlotUpdating.Priority.StateListener);
|
_equipSlotUpdating.Subscribe(OnEquipSlotUpdating, EquipSlotUpdating.Priority.StateListener);
|
||||||
_bonusSlotUpdating.Subscribe(OnBonusSlotUpdating, BonusSlotUpdating.Priority.StateListener);
|
_bonusSlotUpdating.Subscribe(OnBonusSlotUpdating, BonusSlotUpdating.Priority.StateListener);
|
||||||
|
_gearsetDataLoaded.Subscribe(OnGearsetDataLoaded, GearsetDataLoaded.Priority.StateListener);
|
||||||
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
||||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||||
|
|
@ -782,6 +804,7 @@ public class StateListener : IDisposable
|
||||||
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
|
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
|
||||||
_equipSlotUpdating.Unsubscribe(OnEquipSlotUpdating);
|
_equipSlotUpdating.Unsubscribe(OnEquipSlotUpdating);
|
||||||
_bonusSlotUpdating.Unsubscribe(OnBonusSlotUpdating);
|
_bonusSlotUpdating.Unsubscribe(OnBonusSlotUpdating);
|
||||||
|
_gearsetDataLoaded.Unsubscribe(OnGearsetDataLoaded);
|
||||||
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
||||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||||
_visorState.Unsubscribe(OnVisorChange);
|
_visorState.Unsubscribe(OnVisorChange);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ public sealed class StateManager(
|
||||||
ActorManager _actors,
|
ActorManager _actors,
|
||||||
ItemManager items,
|
ItemManager items,
|
||||||
StateChanged @event,
|
StateChanged @event,
|
||||||
|
StateUpdated @event2,
|
||||||
StateApplier applier,
|
StateApplier applier,
|
||||||
InternalStateEditor editor,
|
InternalStateEditor editor,
|
||||||
HumanModelList _humans,
|
HumanModelList _humans,
|
||||||
|
|
@ -30,7 +31,7 @@ public sealed class StateManager(
|
||||||
DesignMerger merger,
|
DesignMerger merger,
|
||||||
ModSettingApplier modApplier,
|
ModSettingApplier modApplier,
|
||||||
GPoseService gPose)
|
GPoseService gPose)
|
||||||
: StateEditor(editor, applier, @event, jobChange, config, items, merger, modApplier, gPose),
|
: StateEditor(editor, applier, @event, @event2, jobChange, config, items, merger, modApplier, gPose),
|
||||||
IReadOnlyDictionary<ActorIdentifier, ActorState>
|
IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
{
|
{
|
||||||
private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
|
private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
|
||||||
|
|
@ -235,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)
|
public void ResetState(ActorState state, StateSource source, uint key = 0, bool stateUpdate = false)
|
||||||
{
|
{
|
||||||
if (!state.Unlock(key))
|
if (!state.Unlock(key))
|
||||||
return;
|
return;
|
||||||
|
|
@ -276,6 +277,9 @@ public sealed class StateManager(
|
||||||
Glamourer.Log.Debug(
|
Glamourer.Log.Debug(
|
||||||
$"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.
|
||||||
|
if(stateUpdate)
|
||||||
|
StateUpdated.Invoke(StateUpdateType.Revert, actors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0)
|
public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0)
|
||||||
|
|
@ -301,6 +305,8 @@ public sealed class StateManager(
|
||||||
Glamourer.Log.Debug(
|
Glamourer.Log.Debug(
|
||||||
$"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)
|
||||||
|
StateUpdated.Invoke(StateUpdateType.RevertAdvanced, actors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetCustomize(ActorState state, StateSource source, uint key = 0)
|
public void ResetCustomize(ActorState state, StateSource source, uint key = 0)
|
||||||
|
|
@ -318,6 +324,8 @@ public sealed class StateManager(
|
||||||
actors = Applier.ChangeCustomize(state, true);
|
actors = Applier.ChangeCustomize(state, true);
|
||||||
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)
|
||||||
|
StateUpdated.Invoke(StateUpdateType.RevertCustomize, actors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetEquip(ActorState state, StateSource source, uint key = 0)
|
public void ResetEquip(ActorState state, StateSource source, uint key = 0)
|
||||||
|
|
@ -367,6 +375,8 @@ 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)
|
||||||
|
StateUpdated.Invoke(StateUpdateType.RevertEquipment, actors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0)
|
public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0)
|
||||||
|
|
@ -443,21 +453,44 @@ public sealed class StateManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReapplyState(Actor actor, bool forceRedraw, StateSource source)
|
public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool isUpdate = false)
|
||||||
{
|
{
|
||||||
if (!GetOrCreate(actor, out var state))
|
if (!GetOrCreate(actor, out var state))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ReapplyState(actor, state, forceRedraw, source);
|
ReapplyState(actor, state, forceRedraw, source, isUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source)
|
public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool isUpdate)
|
||||||
{
|
{
|
||||||
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(isUpdate)
|
||||||
|
StateUpdated.Invoke(StateUpdateType.Reapply, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Automation variant for reapply, to fire the correct StateUpdateType once reapplied. </summary>
|
||||||
|
public void ReapplyAutomationState(Actor actor, bool forceRedraw, bool wasReset, StateSource source)
|
||||||
|
{
|
||||||
|
if (!GetOrCreate(actor, out var state))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ReapplyAutomationState(actor, state, forceRedraw, wasReset, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Automation variant for reapply, to fire the correct StateUpdateType once reapplied. </summary>
|
||||||
|
public void ReapplyAutomationState(Actor actor, ActorState state, bool forceRedraw, bool wasReset, StateSource source)
|
||||||
|
{
|
||||||
|
var data = Applier.ApplyAll(state,
|
||||||
|
forceRedraw
|
||||||
|
|| !actor.Model.IsHuman
|
||||||
|
|| CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
|
||||||
|
StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null);
|
||||||
|
// invoke the automation update based on what reset is.
|
||||||
|
StateUpdated.Invoke(wasReset ? StateUpdateType.RevertAutomation : StateUpdateType.ReapplyAutomation, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteState(ActorIdentifier identifier)
|
public void DeleteState(ActorIdentifier identifier)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue