mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-17 04:57:43 +01:00
Add parameter handling.
This commit is contained in:
parent
9361560350
commit
1a0a0f681f
27 changed files with 633 additions and 155 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue