Add parameter handling.

This commit is contained in:
Ottermandias 2024-01-08 23:00:02 +01:00
parent 9361560350
commit 1a0a0f681f
27 changed files with 633 additions and 155 deletions

View file

@ -5,6 +5,7 @@ using Penumbra.GameData.Enums;
using System.Linq;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using Glamourer.GameData;
using Penumbra.GameData.Structs;
namespace Glamourer.State;
@ -79,7 +80,11 @@ public class ActorState
/// <summary> This contains whether a change to the base data was made by the game, the user via manual input or through automatic application. </summary>
private readonly StateChanged.Source[] _sources = Enumerable
.Repeat(StateChanged.Source.Game,
EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5 + CrestExtensions.AllRelevantSet.Count).ToArray();
EquipFlagExtensions.NumEquipFlags
+ CustomizationExtensions.NumIndices
+ 5
+ CrestExtensions.AllRelevantSet.Count
+ CustomizeParameterExtensions.AllFlags.Count).ToArray();
internal ActorState(ActorIdentifier identifier)
=> Identifier = identifier.CreatePermanent();
@ -96,6 +101,13 @@ public class ActorState
public ref StateChanged.Source this[MetaIndex index]
=> ref _sources[(int)index];
public ref StateChanged.Source this[CustomizeParameterFlag flag]
=> ref _sources[
EquipFlagExtensions.NumEquipFlags
+ CustomizationExtensions.NumIndices + 5
+ CrestExtensions.AllRelevantSet.Count
+ flag.ToInternalIndex()];
public void RemoveFixedDesignSources()
{
for (var i = 0; i < _sources.Length; ++i)
@ -105,6 +117,9 @@ public class ActorState
}
}
public CustomizeParameterFlag OnlyChangedParameters()
=> CustomizeParameterExtensions.AllFlags.Where(f => this[f] is not StateChanged.Source.Game).Aggregate((CustomizeParameterFlag) 0, (a, b) => a | b);
public bool UpdateTerritory(ushort territory)
{
if (territory == LastTerritory)

View file

@ -1,5 +1,7 @@
using System.Linq;
using FFXIVClientStructs.FFXIV.Shader;
using Glamourer.Events;
using Glamourer.GameData;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.Interop.Structs;
@ -14,8 +16,17 @@ namespace Glamourer.State;
/// This class applies changes made to state to actual objects in the game.
/// It handles applying those changes as well as redrawing the actor if necessary.
/// </summary>
public class StateApplier(UpdateSlotService _updateSlot, VisorService _visor, WeaponService _weapon, ChangeCustomizeService _changeCustomize,
ItemManager _items, PenumbraService _penumbra, MetaService _metaService, ObjectManager _objects, CrestService _crests)
public class StateApplier(
UpdateSlotService _updateSlot,
VisorService _visor,
WeaponService _weapon,
ChangeCustomizeService _changeCustomize,
ItemManager _items,
PenumbraService _penumbra,
MetaService _metaService,
ObjectManager _objects,
CrestService _crests,
Configuration _config)
{
/// <summary> Simply force a redraw regardless of conditions. </summary>
public void ForceRedraw(ActorData data)
@ -271,6 +282,35 @@ public class StateApplier(UpdateSlotService _updateSlot, VisorService _visor, We
return data;
}
/// <summary> Change the customize parameters on models. Can change multiple at once. </summary>
public unsafe void ChangeParameters(ActorData data, CustomizeParameterFlag flags, in CustomizeParameterData values)
{
if (!_config.UseAdvancedParameters)
return;
foreach (var actor in data.Objects.Where(a => a is { IsCharacter: true, Model.IsHuman: true }))
{
var buffer = actor.Model.AsHuman->CustomizeParameterCBuffer;
if (buffer == null)
continue;
var ptr = (CustomizeParameter*)buffer->UnsafeSourcePointer;
if (ptr == null)
continue;
values.Apply(ref *ptr, flags);
}
}
/// <inheritdoc cref="ChangeParameters(ActorData,CustomizeParameterFlag,in CustomizeParameterData)"/>
public ActorData ChangeParameters(ActorState state, CustomizeParameterFlag flags, bool apply)
{
var data = GetData(state);
if (apply)
ChangeParameters(data, flags, state.ModelData.Parameters);
return data;
}
private ActorData GetData(ActorState state)
{
_objects.Update();

View file

@ -1,7 +1,9 @@
using System;
using System.Linq;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Common.Math;
using Glamourer.Events;
using Glamourer.GameData;
using Glamourer.Services;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
@ -11,11 +13,11 @@ namespace Glamourer.State;
public class StateEditor
{
private readonly ItemManager _items;
private readonly ItemManager _items;
private readonly CustomizeService _customizations;
private readonly HumanModelList _humans;
private readonly GPoseService _gPose;
private readonly ICondition _condition;
private readonly HumanModelList _humans;
private readonly GPoseService _gPose;
private readonly ICondition _condition;
public StateEditor(CustomizeService customizations, HumanModelList humans, ItemManager items, GPoseService gPose, ICondition condition)
{
@ -208,6 +210,19 @@ public class StateEditor
return true;
}
/// <summary> Change the customize flags of a character. </summary>
public bool ChangeParameter(ActorState state, CustomizeParameterFlag flag, Vector3 value, StateChanged.Source source, out Vector3 oldValue,
uint key = 0)
{
oldValue = state.ModelData.Parameters[flag];
if (!state.CanUnlock(key))
return false;
state.ModelData.Parameters.Set(flag, value);
state[flag] = source;
return true;
}
public bool ChangeMetaState(ActorState state, ActorState.MetaIndex index, bool value, StateChanged.Source source, out bool oldValue,
uint key = 0)
{

View file

@ -4,13 +4,14 @@ using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using System;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Shader;
using Glamourer.GameData;
using Penumbra.GameData.DataContainers;
namespace Glamourer.State;
@ -46,6 +47,7 @@ public class StateListener : IDisposable
private ActorIdentifier _creatingIdentifier = ActorIdentifier.Invalid;
private ActorState? _creatingState;
private ActorState? _customizeState;
private CharacterWeapon _lastFistOffhand = CharacterWeapon.Empty;
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config,
@ -148,10 +150,10 @@ public class StateListener : IDisposable
return;
if (!actor.Identifier(_actors, out var identifier)
|| !_manager.TryGetValue(identifier, out var state))
|| !_manager.TryGetValue(identifier, out _customizeState))
return;
UpdateCustomize(actor, state, ref customize, false);
UpdateCustomize(actor, _customizeState, ref customize, false);
}
private void UpdateCustomize(Actor actor, ActorState state, ref CustomizeArray customize, bool checkTransform)
@ -671,6 +673,7 @@ public class StateListener : IDisposable
_changeCustomizeService.Subscribe(OnCustomizeChange, ChangeCustomizeService.Priority.StateListener);
_crestService.Subscribe(OnCrestChange, CrestService.Priority.StateListener);
_crestService.ModelCrestSetup += OnModelCrestSetup;
_changeCustomizeService.Subscribe(OnCustomizeChanged, ChangeCustomizeService.Post.Priority.StateListener);
}
private void Unsubscribe()
@ -686,6 +689,7 @@ public class StateListener : IDisposable
_changeCustomizeService.Unsubscribe(OnCustomizeChange);
_crestService.Unsubscribe(OnCrestChange);
_crestService.ModelCrestSetup -= OnModelCrestSetup;
_changeCustomizeService.Unsubscribe(OnCustomizeChanged);
}
private void OnCreatedCharacterBase(nint gameObject, string _, nint drawObject)
@ -700,5 +704,58 @@ public class StateListener : IDisposable
_applier.ChangeHatState(data, _creatingState.ModelData.IsHatVisible());
_applier.ChangeWeaponState(data, _creatingState.ModelData.IsWeaponVisible());
_applier.ChangeWetness(data, _creatingState.ModelData.IsWet());
ApplyParameters(_creatingState, drawObject);
}
private void OnCustomizeChanged(Model model)
{
if (_customizeState == null)
{
var actor = _penumbra.GameObjectFromDrawObject(model);
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
if (!actor.Identifier(_actors, out var identifier)
|| !_manager.TryGetValue(identifier, out _customizeState))
return;
}
ApplyParameters(_customizeState, model);
_customizeState = null;
}
private unsafe void ApplyParameters(ActorState state, Model model)
{
if (!model.IsHuman)
return;
var cBuffer = model.AsHuman->CustomizeParameterCBuffer;
if (cBuffer == null)
return;
var ptr = (CustomizeParameter*)cBuffer->UnsafeSourcePointer;
if (ptr == null)
return;
foreach (var flag in CustomizeParameterExtensions.AllFlags)
{
var newValue = CustomizeParameterData.FromParameter(*ptr, flag);
switch (state[flag])
{
case StateChanged.Source.Game:
case StateChanged.Source.Manual:
if (state.BaseData.Parameters.Set(flag, newValue))
_manager.ChangeCustomizeParameter(state, flag, newValue, StateChanged.Source.Game);
break;
case StateChanged.Source.Fixed:
case StateChanged.Source.Ipc:
state.BaseData.Parameters.Set(flag, newValue);
if (_config.UseAdvancedParameters)
state.ModelData.Parameters.ApplySingle(ref *ptr, flag);
break;
}
}
}
}

View file

@ -3,9 +3,12 @@ using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Shader;
using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.GameData;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.Services;
@ -189,6 +192,14 @@ public class StateManager(
// Weapon visibility could technically be inferred from the weapon draw objects,
// but since we use hat visibility from the game object we can also use weapon visibility from it.
ret.SetWeaponVisible(!actor.AsCharacter->DrawData.IsWeaponHidden);
if (model.IsHuman && model.AsHuman->CustomizeParameterCBuffer != null)
{
var ptr = model.AsHuman->CustomizeParameterCBuffer->UnsafeSourcePointer;
if (ptr != null)
ret.Parameters = CustomizeParameterData.FromParameters(*(CustomizeParameter*)ptr);
}
return ret;
}
@ -309,6 +320,19 @@ public class StateManager(
_event.Invoke(StateChanged.Type.Crest, source, state, actors, (old, crest, slot));
}
/// <summary> Change the crest of an equipment piece. </summary>
public void ChangeCustomizeParameter(ActorState state, CustomizeParameterFlag flag, Vector3 value, StateChanged.Source source, uint key = 0)
{
if (!_editor.ChangeParameter(state, flag, value, source, out var old, key))
return;
var @new = state.ModelData.Parameters[flag];
var actors = _applier.ChangeParameters(state, flag, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
Glamourer.Log.Verbose(
$"Set {flag} crest in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(StateChanged.Type.Parameter, source, state, actors, (old, @new, flag));
}
/// <summary> Change hat visibility. </summary>
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
{
@ -390,6 +414,9 @@ public class StateManager(
foreach (var slot in CrestExtensions.AllRelevantSet.Where(design.DoApplyCrest))
_editor.ChangeCrest(state, slot, design.DesignData.Crest(slot), source, out _, key);
foreach (var flag in CustomizeParameterExtensions.AllFlags.Where(design.DoApplyParameter))
_editor.ChangeParameter(state, flag, design.DesignData.Parameters[flag], source, out _, key);
}
var actors = ApplyAll(state, redraw, false);
@ -441,6 +468,7 @@ public class StateManager(
_applier.ChangeWeaponState(actors, state.ModelData.IsWeaponVisible());
_applier.ChangeVisor(actors, state.ModelData.IsVisorToggled());
_applier.ChangeCrests(actors, state.ModelData.CrestVisibility);
_applier.ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters);
}
return actors;
@ -454,6 +482,7 @@ public class StateManager(
var redraw = state.ModelData.ModelId != state.BaseData.ModelId
|| !state.ModelData.IsHuman
|| CustomizeArray.Compare(state.ModelData.Customize, state.BaseData.Customize).RequiresRedraw();
state.ModelData = state.BaseData;
state.ModelData.SetIsWet(false);
foreach (var index in Enum.GetValues<CustomizeIndex>())
@ -471,9 +500,13 @@ public class StateManager(
foreach (var slot in CrestExtensions.AllRelevantSet)
state[slot] = StateChanged.Source.Game;
foreach (var flag in CustomizeParameterExtensions.AllFlags)
state[flag] = StateChanged.Source.Game;
var actors = ActorData.Invalid;
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
actors = ApplyAll(state, redraw, true);
Glamourer.Log.Verbose(
$"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(StateChanged.Type.Reset, StateChanged.Source.Manual, state, actors, null);
@ -514,6 +547,15 @@ public class StateManager(
}
}
foreach (var flag in CustomizeParameterExtensions.AllFlags)
{
if (state[flag] is StateChanged.Source.Fixed)
{
state[flag] = StateChanged.Source.Game;
state.ModelData.Parameters[flag] = state.BaseData.Parameters[flag];
}
}
if (state[ActorState.MetaIndex.HatState] is StateChanged.Source.Fixed)
{
state[ActorState.MetaIndex.HatState] = StateChanged.Source.Game;