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:
Cordelia Mist 2025-01-17 17:39:26 -08:00
parent c605d19510
commit e1a41b5f3c
21 changed files with 225 additions and 99 deletions

View file

@ -17,6 +17,7 @@ public class StateEditor(
InternalStateEditor editor,
StateApplier applier,
StateChanged stateChanged,
StateUpdated stateUpdated,
JobChangeState jobChange,
Configuration config,
ItemManager items,
@ -27,6 +28,7 @@ public class StateEditor(
protected readonly InternalStateEditor Editor = editor;
protected readonly StateApplier Applier = applier;
protected readonly StateChanged StateChanged = stateChanged;
protected readonly StateUpdated StateUpdated = stateUpdated;
protected readonly Configuration Config = config;
protected readonly ItemManager Items = items;
@ -41,6 +43,7 @@ public class StateEditor(
Glamourer.Log.Verbose(
$"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);
StateUpdated.Invoke(StateUpdateType.ModelChange, actors);
}
/// <inheritdoc/>
@ -419,6 +422,8 @@ public class StateEditor(
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
if(settings.SendStateUpdate)
StateUpdated.Invoke(StateUpdateType.DesignApplied, actors);
return;

View file

@ -14,6 +14,7 @@ using Penumbra.GameData.DataContainers;
using Glamourer.Designs;
using Penumbra.GameData.Interop;
using ObjectManager = Glamourer.Interop.ObjectManager;
using Glamourer.Api.Enums;
namespace Glamourer.State;
@ -34,10 +35,12 @@ public class StateListener : IDisposable
private readonly PenumbraService _penumbra;
private readonly EquipSlotUpdating _equipSlotUpdating;
private readonly BonusSlotUpdating _bonusSlotUpdating;
private readonly GearsetDataLoaded _gearsetDataLoaded;
private readonly WeaponLoading _weaponLoading;
private readonly HeadGearVisibilityChanged _headGearVisibility;
private readonly VisorStateChanged _visorState;
private readonly WeaponVisibilityChanged _weaponVisibility;
private readonly StateUpdated _stateUpdated;
private readonly AutoDesignApplier _autoDesignApplier;
private readonly FunModule _funModule;
private readonly HumanModelList _humans;
@ -54,11 +57,11 @@ public class StateListener : IDisposable
private ActorState? _customizeState;
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,
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects,
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
CrestService crestService, BonusSlotUpdating bonusSlotUpdating)
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateUpdated stateUpdated)
{
_manager = manager;
_items = items;
@ -66,6 +69,7 @@ public class StateListener : IDisposable
_actors = actors;
_config = config;
_equipSlotUpdating = equipSlotUpdating;
_gearsetDataLoaded = gearsetDataLoaded;
_weaponLoading = weaponLoading;
_visorState = visorState;
_weaponVisibility = weaponVisibility;
@ -82,6 +86,7 @@ public class StateListener : IDisposable
_condition = condition;
_crestService = crestService;
_bonusSlotUpdating = bonusSlotUpdating;
_stateUpdated = stateUpdated;
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)
{
_objects.Update();
@ -382,7 +403,7 @@ public class StateListener : IDisposable
lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant,
weapon.Stains);
_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);
@ -765,6 +786,7 @@ public class StateListener : IDisposable
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
_equipSlotUpdating.Subscribe(OnEquipSlotUpdating, EquipSlotUpdating.Priority.StateListener);
_bonusSlotUpdating.Subscribe(OnBonusSlotUpdating, BonusSlotUpdating.Priority.StateListener);
_gearsetDataLoaded.Subscribe(OnGearsetDataLoaded, GearsetDataLoaded.Priority.StateListener);
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
@ -782,6 +804,7 @@ public class StateListener : IDisposable
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
_equipSlotUpdating.Unsubscribe(OnEquipSlotUpdating);
_bonusSlotUpdating.Unsubscribe(OnBonusSlotUpdating);
_gearsetDataLoaded.Unsubscribe(OnGearsetDataLoaded);
_movedEquipment.Unsubscribe(OnMovedEquipment);
_weaponLoading.Unsubscribe(OnWeaponLoading);
_visorState.Unsubscribe(OnVisorChange);

View file

@ -21,6 +21,7 @@ public sealed class StateManager(
ActorManager _actors,
ItemManager items,
StateChanged @event,
StateUpdated @event2,
StateApplier applier,
InternalStateEditor editor,
HumanModelList _humans,
@ -30,7 +31,7 @@ public sealed class StateManager(
DesignMerger merger,
ModSettingApplier modApplier,
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>
{
private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
@ -235,7 +236,7 @@ public sealed class StateManager(
public void TurnHuman(ActorState state, StateSource source, uint key = 0)
=> 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))
return;
@ -276,6 +277,9 @@ public sealed class StateManager(
Glamourer.Log.Debug(
$"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
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)
@ -301,6 +305,8 @@ public sealed class StateManager(
Glamourer.Log.Debug(
$"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);
// 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)
@ -318,6 +324,8 @@ public sealed class StateManager(
actors = Applier.ChangeCustomize(state, true);
Glamourer.Log.Verbose(
$"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)
@ -367,6 +375,8 @@ public sealed class StateManager(
Glamourer.Log.Verbose(
$"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)
@ -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))
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,
forceRedraw
|| !actor.Model.IsHuman
|| CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
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)