mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Add parameter handling.
This commit is contained in:
parent
9361560350
commit
1a0a0f681f
27 changed files with 633 additions and 155 deletions
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.State;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
|
@ -80,20 +81,24 @@ public class AutoDesign
|
|||
return ret;
|
||||
}
|
||||
|
||||
public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, bool ApplyHat, bool ApplyVisor, bool ApplyWeapon, bool ApplyWet) ApplyWhat()
|
||||
public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, bool ApplyHat, bool ApplyVisor, bool
|
||||
ApplyWeapon, bool ApplyWet) ApplyWhat()
|
||||
{
|
||||
var equipFlags = (ApplicationType.HasFlag(Type.Weapons) ? WeaponFlags : 0)
|
||||
| (ApplicationType.HasFlag(Type.Armor) ? ArmorFlags : 0)
|
||||
| (ApplicationType.HasFlag(Type.Accessories) ? AccessoryFlags : 0)
|
||||
| (ApplicationType.HasFlag(Type.GearCustomization) ? StainFlags : 0);
|
||||
var customizeFlags = ApplicationType.HasFlag(Type.Customizations) ? CustomizeFlagExtensions.All : 0;
|
||||
var parameterFlags = ApplicationType.HasFlag(Type.Customizations) ? CustomizeParameterExtensions.All : 0;
|
||||
var crestFlag = ApplicationType.HasFlag(Type.GearCustomization) ? CrestExtensions.AllRelevant : 0;
|
||||
|
||||
if (Revert)
|
||||
return (equipFlags, customizeFlags, crestFlag, ApplicationType.HasFlag(Type.Armor), ApplicationType.HasFlag(Type.Armor),
|
||||
return (equipFlags, customizeFlags, crestFlag, parameterFlags, ApplicationType.HasFlag(Type.Armor),
|
||||
ApplicationType.HasFlag(Type.Armor),
|
||||
ApplicationType.HasFlag(Type.Weapons), ApplicationType.HasFlag(Type.Customizations));
|
||||
|
||||
return (equipFlags & Design!.ApplyEquip, customizeFlags & Design.ApplyCustomize, crestFlag & Design.ApplyCrest,
|
||||
parameterFlags & Design.ApplyParameters,
|
||||
ApplicationType.HasFlag(Type.Armor) && Design.DoApplyHatVisible(),
|
||||
ApplicationType.HasFlag(Type.Armor) && Design.DoApplyVisorToggle(),
|
||||
ApplicationType.HasFlag(Type.Weapons) && Design.DoApplyWeaponVisible(),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Dalamud.Plugin.Services;
|
|||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Services;
|
||||
|
|
@ -271,10 +272,11 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
private unsafe void Reduce(Actor actor, ActorState state, AutoDesignSet set, bool respectManual, bool fromJobChange)
|
||||
{
|
||||
EquipFlag totalEquipFlags = 0;
|
||||
CustomizeFlag totalCustomizeFlags = 0;
|
||||
CrestFlag totalCrestFlags = 0;
|
||||
byte totalMetaFlags = 0;
|
||||
EquipFlag totalEquipFlags = 0;
|
||||
CustomizeFlag totalCustomizeFlags = 0;
|
||||
CrestFlag totalCrestFlags = 0;
|
||||
CustomizeParameterFlag totalParameterFlags = 0;
|
||||
byte totalMetaFlags = 0;
|
||||
if (set.BaseState == AutoDesignSet.Base.Game)
|
||||
_state.ResetStateFixed(state);
|
||||
else if (!respectManual)
|
||||
|
|
@ -297,18 +299,19 @@ public class AutoDesignApplier : IDisposable
|
|||
if (!data.IsHuman)
|
||||
continue;
|
||||
|
||||
var (equipFlags, customizeFlags, crestFlags, applyHat, applyVisor, applyWeapon, applyWet) = design.ApplyWhat();
|
||||
var (equipFlags, customizeFlags, crestFlags, parameterFlags, applyHat, applyVisor, applyWeapon, applyWet) = design.ApplyWhat();
|
||||
ReduceMeta(state, data, applyHat, applyVisor, applyWeapon, applyWet, ref totalMetaFlags, respectManual, source);
|
||||
ReduceCustomize(state, data, customizeFlags, ref totalCustomizeFlags, respectManual, source);
|
||||
ReduceEquip(state, data, equipFlags, ref totalEquipFlags, respectManual, source, fromJobChange);
|
||||
ReduceCrests(state, data, crestFlags, ref totalCrestFlags, respectManual, source);
|
||||
ReduceParameters(state, data, parameterFlags, ref totalParameterFlags, respectManual, source);
|
||||
}
|
||||
|
||||
if (totalCustomizeFlags != 0)
|
||||
state.ModelData.ModelId = 0;
|
||||
}
|
||||
|
||||
/// <summary> Get world-specific first and all-world afterwards. </summary>
|
||||
/// <summary> Get world-specific first and all-world afterward. </summary>
|
||||
private bool GetPlayerSet(ActorIdentifier identifier, [NotNullWhen(true)] out AutoDesignSet? set)
|
||||
{
|
||||
switch (identifier.Type)
|
||||
|
|
@ -349,6 +352,24 @@ public class AutoDesignApplier : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private void ReduceParameters(ActorState state, in DesignData design, CustomizeParameterFlag parameterFlags,
|
||||
ref CustomizeParameterFlag totalParameterFlags, bool respectManual, StateChanged.Source source)
|
||||
{
|
||||
parameterFlags &= ~totalParameterFlags;
|
||||
if (parameterFlags == 0)
|
||||
return;
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
if (!parameterFlags.HasFlag(flag))
|
||||
continue;
|
||||
|
||||
if (!respectManual || state[flag] is not StateChanged.Source.Manual)
|
||||
_state.ChangeCustomizeParameter(state, flag, design.Parameters[flag], source);
|
||||
totalParameterFlags |= flag;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReduceEquip(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags, bool respectManual,
|
||||
StateChanged.Source source, bool fromJobChange)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
public bool RevertManualChangesOnZoneChange { get; set; } = false;
|
||||
public bool ShowQuickBarInTabs { get; set; } = true;
|
||||
public bool OpenWindowAtStart { get; set; } = false;
|
||||
public bool UseAdvancedParameters { get; set; } = false;
|
||||
public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY);
|
||||
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
|
||||
public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New;
|
||||
|
|
|
|||
|
|
@ -54,20 +54,21 @@ public sealed class Design : DesignBase, ISavable
|
|||
public new JObject JsonSerialize()
|
||||
{
|
||||
var ret = new JObject()
|
||||
{
|
||||
["FileVersion"] = FileVersion,
|
||||
["Identifier"] = Identifier,
|
||||
["CreationDate"] = CreationDate,
|
||||
["LastEdit"] = LastEdit,
|
||||
["Name"] = Name.Text,
|
||||
["Description"] = Description,
|
||||
["Color"] = Color,
|
||||
["Tags"] = JArray.FromObject(Tags),
|
||||
["WriteProtected"] = WriteProtected(),
|
||||
["Equipment"] = SerializeEquipment(),
|
||||
["Customize"] = SerializeCustomize(),
|
||||
["Mods"] = SerializeMods(),
|
||||
};
|
||||
{
|
||||
["FileVersion"] = FileVersion,
|
||||
["Identifier"] = Identifier,
|
||||
["CreationDate"] = CreationDate,
|
||||
["LastEdit"] = LastEdit,
|
||||
["Name"] = Name.Text,
|
||||
["Description"] = Description,
|
||||
["Color"] = Color,
|
||||
["Tags"] = JArray.FromObject(Tags),
|
||||
["WriteProtected"] = WriteProtected(),
|
||||
["Equipment"] = SerializeEquipment(),
|
||||
["Customize"] = SerializeCustomize(),
|
||||
["Parameters"] = SerializeParameters(),
|
||||
["Mods"] = SerializeMods(),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +134,8 @@ public sealed class Design : DesignBase, ISavable
|
|||
LoadCustomize(customizations, json["Customize"], design, design.Name, true, false);
|
||||
LoadEquip(items, json["Equipment"], design, design.Name, true);
|
||||
LoadMods(json["Mods"], design);
|
||||
design.Color = json["Color"]?.ToObject<string>() ?? string.Empty;
|
||||
LoadParameters(json["Parameters"], design, design.Name);
|
||||
design.Color = json["Color"]?.ToObject<string>() ?? string.Empty;
|
||||
return design;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ using OtterGui.Classes;
|
|||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
|
@ -71,6 +73,8 @@ public class DesignBase
|
|||
private CustomizeFlag _applyCustomize = CustomizeFlagExtensions.AllRelevant;
|
||||
public CustomizeSet CustomizeSet { get; private set; }
|
||||
|
||||
public CustomizeParameterFlag ApplyParameters { get; private set; }
|
||||
|
||||
internal CustomizeFlag ApplyCustomize
|
||||
{
|
||||
get => _applyCustomize.FixApplication(CustomizeSet);
|
||||
|
|
@ -174,6 +178,9 @@ public class DesignBase
|
|||
public bool DoApplyCrest(CrestFlag slot)
|
||||
=> ApplyCrest.HasFlag(slot);
|
||||
|
||||
public bool DoApplyParameter(CustomizeParameterFlag flag)
|
||||
=> ApplyParameters.HasFlag(flag);
|
||||
|
||||
internal bool SetApplyEquip(EquipSlot slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyEquip | slot.ToFlag() : ApplyEquip & ~slot.ToFlag();
|
||||
|
|
@ -214,32 +221,48 @@ public class DesignBase
|
|||
return true;
|
||||
}
|
||||
|
||||
internal FlagRestrictionResetter TemporarilyRestrictApplication(EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
=> new(this, equipFlags, customizeFlags, crestFlags);
|
||||
internal bool SetApplyParameter(CustomizeParameterFlag flag, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyParameters | flag : ApplyParameters & ~flag;
|
||||
if (newValue == ApplyParameters)
|
||||
return false;
|
||||
|
||||
ApplyParameters = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal FlagRestrictionResetter TemporarilyRestrictApplication(EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags,
|
||||
CustomizeParameterFlag parameterFlags)
|
||||
=> new(this, equipFlags, customizeFlags, crestFlags, parameterFlags);
|
||||
|
||||
internal readonly struct FlagRestrictionResetter : IDisposable
|
||||
{
|
||||
private readonly DesignBase _design;
|
||||
private readonly EquipFlag _oldEquipFlags;
|
||||
private readonly CustomizeFlag _oldCustomizeFlags;
|
||||
private readonly CrestFlag _oldCrestFlags;
|
||||
private readonly DesignBase _design;
|
||||
private readonly EquipFlag _oldEquipFlags;
|
||||
private readonly CustomizeFlag _oldCustomizeFlags;
|
||||
private readonly CrestFlag _oldCrestFlags;
|
||||
private readonly CustomizeParameterFlag _oldParameterFlags;
|
||||
|
||||
public FlagRestrictionResetter(DesignBase d, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
public FlagRestrictionResetter(DesignBase d, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags,
|
||||
CustomizeParameterFlag parameterFlags)
|
||||
{
|
||||
_design = d;
|
||||
_oldEquipFlags = d.ApplyEquip;
|
||||
_oldCustomizeFlags = d.ApplyCustomizeRaw;
|
||||
_oldCrestFlags = d.ApplyCrest;
|
||||
_oldParameterFlags = d.ApplyParameters;
|
||||
d.ApplyEquip &= equipFlags;
|
||||
d.ApplyCustomize &= customizeFlags;
|
||||
d.ApplyCrest &= crestFlags;
|
||||
d.ApplyParameters &= parameterFlags;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_design.ApplyEquip = _oldEquipFlags;
|
||||
_design.ApplyCustomize = _oldCustomizeFlags;
|
||||
_design.ApplyCrest = _oldCrestFlags;
|
||||
_design.ApplyEquip = _oldEquipFlags;
|
||||
_design.ApplyCustomize = _oldCustomizeFlags;
|
||||
_design.ApplyCrest = _oldCrestFlags;
|
||||
_design.ApplyParameters = _oldParameterFlags;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -256,26 +279,16 @@ public class DesignBase
|
|||
{
|
||||
var ret = new JObject
|
||||
{
|
||||
["FileVersion"] = FileVersion,
|
||||
["Equipment"] = SerializeEquipment(),
|
||||
["Customize"] = SerializeCustomize(),
|
||||
["FileVersion"] = FileVersion,
|
||||
["Equipment"] = SerializeEquipment(),
|
||||
["Customize"] = SerializeCustomize(),
|
||||
["Parameters"] = SerializeParameters(),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected JObject SerializeEquipment()
|
||||
{
|
||||
static JObject Serialize(CustomItemId id, StainId stain, bool crest, bool apply, bool applyStain, bool applyCrest)
|
||||
=> new()
|
||||
{
|
||||
["ItemId"] = id.Id,
|
||||
["Stain"] = stain.Id,
|
||||
["Crest"] = crest,
|
||||
["Apply"] = apply,
|
||||
["ApplyStain"] = applyStain,
|
||||
["ApplyCrest"] = applyCrest,
|
||||
};
|
||||
|
||||
var ret = new JObject();
|
||||
if (_designData.IsHuman)
|
||||
{
|
||||
|
|
@ -298,6 +311,17 @@ public class DesignBase
|
|||
}
|
||||
|
||||
return ret;
|
||||
|
||||
static JObject Serialize(CustomItemId id, StainId stain, bool crest, bool apply, bool applyStain, bool applyCrest)
|
||||
=> new()
|
||||
{
|
||||
["ItemId"] = id.Id,
|
||||
["Stain"] = stain.Id,
|
||||
["Crest"] = crest,
|
||||
["Apply"] = apply,
|
||||
["ApplyStain"] = applyStain,
|
||||
["ApplyCrest"] = applyCrest,
|
||||
};
|
||||
}
|
||||
|
||||
protected JObject SerializeCustomize()
|
||||
|
|
@ -329,6 +353,42 @@ public class DesignBase
|
|||
return ret;
|
||||
}
|
||||
|
||||
protected JObject SerializeParameters()
|
||||
{
|
||||
var ret = new JObject();
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.ValueFlags)
|
||||
{
|
||||
ret[flag.ToString()] = new JObject()
|
||||
{
|
||||
["Value"] = DesignData.Parameters[flag][0],
|
||||
["Apply"] = DoApplyParameter(flag),
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.PercentageFlags)
|
||||
{
|
||||
ret[flag.ToString()] = new JObject()
|
||||
{
|
||||
["Percentage"] = DesignData.Parameters[flag][0],
|
||||
["Apply"] = DoApplyParameter(flag),
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.TripleFlags)
|
||||
{
|
||||
ret[flag.ToString()] = new JObject()
|
||||
{
|
||||
["Red"] = DesignData.Parameters[flag][0],
|
||||
["Green"] = DesignData.Parameters[flag][1],
|
||||
["Blue"] = DesignData.Parameters[flag][2],
|
||||
["Apply"] = DoApplyParameter(flag),
|
||||
};
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Deserialization
|
||||
|
|
@ -348,9 +408,68 @@ public class DesignBase
|
|||
var ret = new DesignBase(customizations, items);
|
||||
LoadCustomize(customizations, json["Customize"], ret, "Temporary Design", false, true);
|
||||
LoadEquip(items, json["Equipment"], ret, "Temporary Design", true);
|
||||
LoadParameters(json["Parameters"], ret, "Temporary Design");
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected static void LoadParameters(JToken? parameters, DesignBase design, string name)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
design.ApplyParameters = 0;
|
||||
design.GetDesignDataRef().Parameters = default;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.ValueFlags)
|
||||
{
|
||||
if (!TryGetToken(flag, out var token))
|
||||
continue;
|
||||
|
||||
var value = token["Value"]?.ToObject<float>() ?? 0f;
|
||||
design.GetDesignDataRef().Parameters[flag] = new Vector3(value);
|
||||
}
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.PercentageFlags)
|
||||
{
|
||||
if (!TryGetToken(flag, out var token))
|
||||
continue;
|
||||
|
||||
var value = Math.Clamp(token["Percentage"]?.ToObject<float>() ?? 0f, 0f, 1f);
|
||||
design.GetDesignDataRef().Parameters[flag] = new Vector3(value);
|
||||
}
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.TripleFlags)
|
||||
{
|
||||
if (!TryGetToken(flag, out var token))
|
||||
continue;
|
||||
|
||||
var r = Math.Clamp(token["Red"]?.ToObject<float>() ?? 0f, 0, 1);
|
||||
var g = Math.Clamp(token["Green"]?.ToObject<float>() ?? 0f, 0, 1);
|
||||
var b = Math.Clamp(token["Blue"]?.ToObject<float>() ?? 0f, 0, 1);
|
||||
design.GetDesignDataRef().Parameters[flag] = new Vector3(r, g, b);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
// Load the token and set application.
|
||||
bool TryGetToken(CustomizeParameterFlag flag, [NotNullWhen(true)] out JToken? token)
|
||||
{
|
||||
token = parameters![flag.ToString()];
|
||||
if (token != null)
|
||||
{
|
||||
var apply = token["Apply"]?.ToObject<bool>() ?? false;
|
||||
design.SetApplyParameter(flag, apply);
|
||||
return true;
|
||||
}
|
||||
|
||||
design.ApplyParameters &= ~flag;
|
||||
design.GetDesignDataRef().Parameters[flag] = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void LoadEquip(ItemManager items, JToken? equip, DesignBase design, string name, bool allowUnknown)
|
||||
{
|
||||
if (equip == null)
|
||||
|
|
@ -514,8 +633,10 @@ public class DesignBase
|
|||
_designData = DesignBase64Migration.MigrateBase64(items, humans, base64, out var equipFlags, out var customizeFlags,
|
||||
out var writeProtected,
|
||||
out var applyHat, out var applyVisor, out var applyWeapon);
|
||||
ApplyEquip = equipFlags;
|
||||
ApplyCustomize = customizeFlags;
|
||||
ApplyEquip = equipFlags;
|
||||
ApplyCustomize = customizeFlags;
|
||||
ApplyParameters = 0;
|
||||
ApplyCrest = 0;
|
||||
SetWriteProtected(writeProtected);
|
||||
SetApplyHatVisible(applyHat);
|
||||
SetApplyVisorToggle(applyVisor);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Utility;
|
||||
|
|
@ -23,9 +24,9 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
public JObject ShareJObject(Design design)
|
||||
=> design.JsonSerialize();
|
||||
|
||||
public JObject ShareJObject(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
public JObject ShareJObject(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags, CustomizeParameterFlag parameterFlags)
|
||||
{
|
||||
var design = Convert(state, equipFlags, customizeFlags, crestFlags);
|
||||
var design = Convert(state, equipFlags, customizeFlags, crestFlags, parameterFlags);
|
||||
return ShareJObject(design);
|
||||
}
|
||||
|
||||
|
|
@ -36,21 +37,21 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
=> ShareBase64(ShareJObject(design));
|
||||
|
||||
public string ShareBase64(ActorState state)
|
||||
=> ShareBase64(state, EquipFlagExtensions.All, CustomizeFlagExtensions.All, CrestExtensions.All);
|
||||
=> ShareBase64(state, EquipFlagExtensions.All, CustomizeFlagExtensions.All, CrestExtensions.All, CustomizeParameterExtensions.All);
|
||||
|
||||
public string ShareBase64(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
=> ShareBase64(state.ModelData, equipFlags, customizeFlags, crestFlags);
|
||||
public string ShareBase64(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags, CustomizeParameterFlag parameterFlags)
|
||||
=> ShareBase64(state.ModelData, equipFlags, customizeFlags, crestFlags, parameterFlags);
|
||||
|
||||
public string ShareBase64(in DesignData data, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
public string ShareBase64(in DesignData data, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags, CustomizeParameterFlag parameterFlags)
|
||||
{
|
||||
var design = Convert(data, equipFlags, customizeFlags, crestFlags);
|
||||
var design = Convert(data, equipFlags, customizeFlags, crestFlags, parameterFlags);
|
||||
return ShareBase64(ShareJObject(design));
|
||||
}
|
||||
|
||||
public DesignBase Convert(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
=> Convert(state.ModelData, equipFlags, customizeFlags, crestFlags);
|
||||
public DesignBase Convert(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags, CustomizeParameterFlag parameterFlags)
|
||||
=> Convert(state.ModelData, equipFlags, customizeFlags, crestFlags, parameterFlags);
|
||||
|
||||
public DesignBase Convert(in DesignData data, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
public DesignBase Convert(in DesignData data, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags, CustomizeParameterFlag parameterFlags)
|
||||
{
|
||||
var design = _designs.CreateTemporary();
|
||||
design.ApplyEquip = equipFlags & EquipFlagExtensions.All;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -10,30 +11,31 @@ namespace Glamourer.Designs;
|
|||
|
||||
public unsafe struct DesignData
|
||||
{
|
||||
private string _nameHead = string.Empty;
|
||||
private string _nameBody = string.Empty;
|
||||
private string _nameHands = string.Empty;
|
||||
private string _nameLegs = string.Empty;
|
||||
private string _nameFeet = string.Empty;
|
||||
private string _nameEars = string.Empty;
|
||||
private string _nameNeck = string.Empty;
|
||||
private string _nameWrists = string.Empty;
|
||||
private string _nameRFinger = string.Empty;
|
||||
private string _nameLFinger = string.Empty;
|
||||
private string _nameMainhand = string.Empty;
|
||||
private string _nameOffhand = string.Empty;
|
||||
private fixed uint _itemIds[12];
|
||||
private fixed ushort _iconIds[12];
|
||||
private fixed byte _equipmentBytes[48];
|
||||
public CustomizeArray Customize = CustomizeArray.Default;
|
||||
public uint ModelId;
|
||||
public CrestFlag CrestVisibility;
|
||||
private SecondaryId _secondaryMainhand;
|
||||
private SecondaryId _secondaryOffhand;
|
||||
private FullEquipType _typeMainhand;
|
||||
private FullEquipType _typeOffhand;
|
||||
private byte _states;
|
||||
public bool IsHuman = true;
|
||||
private string _nameHead = string.Empty;
|
||||
private string _nameBody = string.Empty;
|
||||
private string _nameHands = string.Empty;
|
||||
private string _nameLegs = string.Empty;
|
||||
private string _nameFeet = string.Empty;
|
||||
private string _nameEars = string.Empty;
|
||||
private string _nameNeck = string.Empty;
|
||||
private string _nameWrists = string.Empty;
|
||||
private string _nameRFinger = string.Empty;
|
||||
private string _nameLFinger = string.Empty;
|
||||
private string _nameMainhand = string.Empty;
|
||||
private string _nameOffhand = string.Empty;
|
||||
private fixed uint _itemIds[12];
|
||||
private fixed ushort _iconIds[12];
|
||||
private fixed byte _equipmentBytes[48];
|
||||
public CustomizeParameterData Parameters;
|
||||
public CustomizeArray Customize = CustomizeArray.Default;
|
||||
public uint ModelId;
|
||||
public CrestFlag CrestVisibility;
|
||||
private SecondaryId _secondaryMainhand;
|
||||
private SecondaryId _secondaryOffhand;
|
||||
private FullEquipType _typeMainhand;
|
||||
private FullEquipType _typeOffhand;
|
||||
private byte _states;
|
||||
public bool IsHuman = true;
|
||||
|
||||
public DesignData()
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Utility;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
|
|
@ -17,6 +20,7 @@ using OtterGui;
|
|||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using static Glamourer.State.ActorState;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
|
|
@ -424,6 +428,20 @@ public class DesignManager
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary> Change a customize parameter. </summary>
|
||||
public void ChangeCustomizeParameter(Design design, CustomizeParameterFlag flag, Vector3 value)
|
||||
{
|
||||
var old = design.DesignData.Parameters[flag];
|
||||
if (!design.GetDesignDataRef().Parameters.Set(flag, value))
|
||||
return;
|
||||
|
||||
var @new = design.DesignData.Parameters[flag];
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
Glamourer.Log.Debug($"Set customize parameter {flag} in design {design.Identifier} from {old} to {@new}.");
|
||||
_saveService.QueueSave(design);
|
||||
_event.Invoke(DesignChanged.Type.Parameter, design, (old, @new, flag));
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific equipment piece. </summary>
|
||||
public void ChangeApplyEquip(Design design, EquipSlot slot, bool value)
|
||||
{
|
||||
|
|
@ -529,6 +547,18 @@ public class DesignManager
|
|||
_event.Invoke(DesignChanged.Type.Other, design, (metaIndex, true, value));
|
||||
}
|
||||
|
||||
/// <summary> Change the application value of a customize parameter. </summary>
|
||||
public void ChangeApplyParameter(Design design, CustomizeParameterFlag flag, bool value)
|
||||
{
|
||||
if (!design.SetApplyParameter(flag, value))
|
||||
return;
|
||||
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
_saveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of parameter {flag} to {value}.");
|
||||
_event.Invoke(DesignChanged.Type.ApplyParameter, design, flag);
|
||||
}
|
||||
|
||||
/// <summary> Apply an entire design based on its appliance rules piece by piece. </summary>
|
||||
public void ApplyDesign(Design design, DesignBase other)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ public sealed class DesignChanged()
|
|||
/// <summary> An existing design had a crest visibility changed. Data is the old crest visibility, the new crest visibility and the slot [(bool, bool, EquipSlot)]. </summary>
|
||||
Crest,
|
||||
|
||||
/// <summary> An existing design had a customize parameter changed. Data is the old value, the new value and the flag [(Vector3, Vector3, CustomizeParameterFlag)]. </summary>
|
||||
Parameter,
|
||||
|
||||
/// <summary> An existing design changed whether a specific customization is applied. Data is the type of customization [CustomizeIndex]. </summary>
|
||||
ApplyCustomize,
|
||||
|
||||
|
|
@ -77,6 +80,9 @@ public sealed class DesignChanged()
|
|||
/// <summary> An existing design changed whether a specific crest visibility is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||
ApplyCrest,
|
||||
|
||||
/// <summary> An existing design changed whether a specific customize parameter is applied. Data is the flag for the parameter [CustomizeParameterFlag]. </summary>
|
||||
ApplyParameter,
|
||||
|
||||
/// <summary> An existing design changed its write protection status. Data is the new value [bool]. </summary>
|
||||
WriteProtection,
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ public sealed class StateChanged()
|
|||
/// <summary> A characters saved state had a crest visibility changed. Data is the old crest visibility, the new crest visibility and the slot [(bool, bool, EquipSlot)]. </summary>
|
||||
Crest,
|
||||
|
||||
/// <summary> A characters saved state had its customize parameter changed. Data is the old value, the new value and the type [(Vector3, Vector3, CustomizeParameterFlag)]. </summary>
|
||||
Parameter,
|
||||
|
||||
/// <summary> A characters saved state had a design applied. This means everything may have changed. Data is the applied design. [DesignBase] </summary>
|
||||
Design,
|
||||
|
||||
|
|
|
|||
|
|
@ -159,8 +159,8 @@ public class DesignQuickBar : Window, IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest);
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ public class ActorPanel(
|
|||
DesignManager _designManager,
|
||||
ImportService _importService,
|
||||
ICondition _conditions,
|
||||
DictModelChara _modelChara)
|
||||
DictModelChara _modelChara,
|
||||
CustomizeParameterDrawer _parameterDrawer)
|
||||
{
|
||||
private ActorIdentifier _identifier;
|
||||
private string _actorName = string.Empty;
|
||||
|
|
@ -127,6 +128,7 @@ public class ActorPanel(
|
|||
{
|
||||
DrawCustomizationsHeader();
|
||||
DrawEquipmentHeader();
|
||||
DrawParameterHeader();
|
||||
}
|
||||
|
||||
private void DrawCustomizationsHeader()
|
||||
|
|
@ -169,6 +171,14 @@ public class ActorPanel(
|
|||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
}
|
||||
|
||||
private void DrawParameterHeader()
|
||||
{
|
||||
if (!_config.UseAdvancedParameters || !ImGui.CollapsingHeader("Advanced Customizations"))
|
||||
return;
|
||||
|
||||
_parameterDrawer.Draw(_stateManager, _state!);
|
||||
}
|
||||
|
||||
private void DrawEquipmentMetaToggles()
|
||||
{
|
||||
using (_ = ImRaii.Group())
|
||||
|
|
@ -302,9 +312,9 @@ public class ActorPanel(
|
|||
private void SaveDesignOpen()
|
||||
{
|
||||
ImGui.OpenPopup("Save as Design");
|
||||
_newName = _state!.Identifier.ToName();
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
_newDesign = _converter.Convert(_state, applyGear, applyCustomize, applyCrest);
|
||||
_newName = _state!.Identifier.ToName();
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
_newDesign = _converter.Convert(_state, applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
}
|
||||
|
||||
private void SaveDesignDrawPopup()
|
||||
|
|
@ -339,8 +349,8 @@ public class ActorPanel(
|
|||
{
|
||||
try
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var text = _converter.ShareBase64(_state!, applyGear, applyCustomize, applyCrest);
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
var text = _converter.ShareBase64(_state!, applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
ImGui.SetClipboardText(text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -379,9 +389,9 @@ public class ActorPanel(
|
|||
!data.Valid || id == _identifier || _state!.ModelData.ModelId != 0))
|
||||
return;
|
||||
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest), state,
|
||||
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state,
|
||||
StateChanged.Source.Manual);
|
||||
}
|
||||
|
||||
|
|
@ -397,9 +407,9 @@ public class ActorPanel(
|
|||
!data.Valid || id == _identifier || _state!.ModelData.ModelId != 0))
|
||||
return;
|
||||
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest), state,
|
||||
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state,
|
||||
StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ public class SetPanel(
|
|||
var size = new Vector2(ImGui.GetFrameHeight());
|
||||
size.X += ImGuiHelpers.GlobalScale;
|
||||
|
||||
var (equipFlags, customizeFlags, _, _, _, _, _) = design.ApplyWhat();
|
||||
var (equipFlags, customizeFlags, _, _, _, _, _, _) = design.ApplyWhat();
|
||||
var sb = new StringBuilder();
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -101,6 +101,18 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
|
|||
PrintRow(type.ToDefaultName(), state.BaseData.Customize[type].Value, state.ModelData.Customize[type].Value, state[type]);
|
||||
ImGui.TableNextRow();
|
||||
}
|
||||
|
||||
foreach (var crest in CrestExtensions.AllRelevantSet)
|
||||
{
|
||||
PrintRow(crest.ToLabel(), state.BaseData.Crest(crest), state.ModelData.Crest(crest), state[crest]);
|
||||
ImGui.TableNextRow();
|
||||
}
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
PrintRow(flag.ToString(), state.BaseData.Parameters[flag], state.ModelData.Parameters[flag], state[flag]);
|
||||
ImGui.TableNextRow();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using FFXIVClientStructs.FFXIV.Shader;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Structs;
|
||||
using ImGuiNET;
|
||||
|
|
@ -78,6 +80,28 @@ public unsafe class ModelEvaluationPanel(
|
|||
DrawEquip(actor, model);
|
||||
DrawCustomize(actor, model);
|
||||
DrawCrests(actor, model);
|
||||
DrawParameters(actor, model);
|
||||
}
|
||||
|
||||
private void DrawParameters(Actor actor, Model model)
|
||||
{
|
||||
if (!model.IsHuman)
|
||||
return;
|
||||
if (model.AsHuman->CustomizeParameterCBuffer == null)
|
||||
return;
|
||||
|
||||
var ptr = (CustomizeParameter*)model.AsHuman->CustomizeParameterCBuffer->UnsafeSourcePointer;
|
||||
if (ptr == null)
|
||||
return;
|
||||
|
||||
var convert = CustomizeParameterData.FromParameters(*ptr);
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn(flag.ToString());
|
||||
ImGuiUtil.DrawTableColumn(string.Empty);
|
||||
ImGuiUtil.DrawTableColumn(convert[flag].ToString());
|
||||
ImGui.TableNextColumn();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawVisor(Actor actor, Model model)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Interop;
|
||||
|
|
@ -21,9 +22,20 @@ using Penumbra.GameData.Enums;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer _customizationDrawer, DesignManager _manager,
|
||||
ObjectManager _objects, StateManager _state, EquipmentDrawer _equipmentDrawer, ModAssociationsTab _modAssociations,
|
||||
DesignDetailTab _designDetails, DesignConverter _converter, ImportService _importService, MultiDesignPanel _multiDesignPanel)
|
||||
public class DesignPanel(
|
||||
DesignFileSystemSelector _selector,
|
||||
CustomizationDrawer _customizationDrawer,
|
||||
DesignManager _manager,
|
||||
ObjectManager _objects,
|
||||
StateManager _state,
|
||||
EquipmentDrawer _equipmentDrawer,
|
||||
ModAssociationsTab _modAssociations,
|
||||
Configuration _config,
|
||||
DesignDetailTab _designDetails,
|
||||
DesignConverter _converter,
|
||||
ImportService _importService,
|
||||
MultiDesignPanel _multiDesignPanel,
|
||||
CustomizeParameterDrawer _parameterDrawer)
|
||||
{
|
||||
private readonly FileDialogManager _fileDialog = new();
|
||||
|
||||
|
|
@ -152,11 +164,20 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
}
|
||||
|
||||
private void DrawCustomizeParameters()
|
||||
{
|
||||
if (!_config.UseAdvancedParameters || !ImGui.CollapsingHeader("Advanced Customization"))
|
||||
return;
|
||||
|
||||
_parameterDrawer.Draw(_manager, _selector.Selected!);
|
||||
}
|
||||
|
||||
private void DrawCustomizeApplication()
|
||||
{
|
||||
var set = _selector.Selected!.CustomizeSet;
|
||||
var available = set.SettingAvailable | CustomizeFlag.Clan | CustomizeFlag.Gender | CustomizeFlag.BodyType;
|
||||
var flags = _selector.Selected!.ApplyCustomizeExcludingBodyType == 0 ? 0 : (_selector.Selected!.ApplyCustomize & available) == available ? 3 : 1;
|
||||
var set = _selector.Selected!.CustomizeSet;
|
||||
var available = set.SettingAvailable | CustomizeFlag.Clan | CustomizeFlag.Gender | CustomizeFlag.BodyType;
|
||||
var flags = _selector.Selected!.ApplyCustomizeExcludingBodyType == 0 ? 0 :
|
||||
(_selector.Selected!.ApplyCustomize & available) == available ? 3 : 1;
|
||||
if (ImGui.CheckboxFlags("Apply All Customizations", ref flags, 3))
|
||||
{
|
||||
var newFlags = flags == 3;
|
||||
|
|
@ -205,6 +226,9 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
DrawCustomizeApplication();
|
||||
ImGui.NewLine();
|
||||
DrawCrestApplication();
|
||||
ImGui.NewLine();
|
||||
if (_config.UseAdvancedParameters)
|
||||
DrawMetaApplication();
|
||||
}
|
||||
|
||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X / 2);
|
||||
|
|
@ -248,27 +272,47 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
EquipSlotExtensions.FullSlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
const uint all = 0x0Fu;
|
||||
var flags = (_selector.Selected!.DoApplyHatVisible() ? 0x01u : 0x00)
|
||||
| (_selector.Selected!.DoApplyVisorToggle() ? 0x02u : 0x00)
|
||||
| (_selector.Selected!.DoApplyWeaponVisible() ? 0x04u : 0x00)
|
||||
| (_selector.Selected!.DoApplyWetness() ? 0x08u : 0x00);
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
|
||||
var apply = bigChange ? (flags & 0x01) == 0x01 : _selector.Selected!.DoApplyHatVisible();
|
||||
if (ImGui.Checkbox("Apply Hat Visibility", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.HatState, apply);
|
||||
if (_config.UseAdvancedParameters)
|
||||
DrawParameterApplication();
|
||||
else
|
||||
DrawMetaApplication();
|
||||
}
|
||||
}
|
||||
|
||||
apply = bigChange ? (flags & 0x02) == 0x02 : _selector.Selected!.DoApplyVisorToggle();
|
||||
if (ImGui.Checkbox("Apply Visor State", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.VisorState, apply);
|
||||
private void DrawMetaApplication()
|
||||
{
|
||||
const uint all = 0x0Fu;
|
||||
var flags = (_selector.Selected!.DoApplyHatVisible() ? 0x01u : 0x00)
|
||||
| (_selector.Selected!.DoApplyVisorToggle() ? 0x02u : 0x00)
|
||||
| (_selector.Selected!.DoApplyWeaponVisible() ? 0x04u : 0x00)
|
||||
| (_selector.Selected!.DoApplyWetness() ? 0x08u : 0x00);
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
|
||||
var apply = bigChange ? (flags & 0x01) == 0x01 : _selector.Selected!.DoApplyHatVisible();
|
||||
if (ImGui.Checkbox("Apply Hat Visibility", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.HatState, apply);
|
||||
|
||||
apply = bigChange ? (flags & 0x04) == 0x04 : _selector.Selected!.DoApplyWeaponVisible();
|
||||
if (ImGui.Checkbox("Apply Weapon Visibility", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.WeaponState, apply);
|
||||
apply = bigChange ? (flags & 0x02) == 0x02 : _selector.Selected!.DoApplyVisorToggle();
|
||||
if (ImGui.Checkbox("Apply Visor State", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.VisorState, apply);
|
||||
|
||||
apply = bigChange ? (flags & 0x08) == 0x08 : _selector.Selected!.DoApplyWetness();
|
||||
if (ImGui.Checkbox("Apply Wetness", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.Wetness, apply);
|
||||
apply = bigChange ? (flags & 0x04) == 0x04 : _selector.Selected!.DoApplyWeaponVisible();
|
||||
if (ImGui.Checkbox("Apply Weapon Visibility", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.WeaponState, apply);
|
||||
|
||||
apply = bigChange ? (flags & 0x08) == 0x08 : _selector.Selected!.DoApplyWetness();
|
||||
if (ImGui.Checkbox("Apply Wetness", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.Wetness, apply);
|
||||
}
|
||||
|
||||
private void DrawParameterApplication()
|
||||
{
|
||||
var flags = (uint)_selector.Selected!.ApplyParameters;
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Customize Parameters", ref flags, (uint)CustomizeParameterExtensions.All);
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
var apply = bigChange ? ((CustomizeParameterFlag)flags).HasFlag(flag) : _selector.Selected!.DoApplyParameter(flag);
|
||||
if (ImGui.Checkbox($"Apply {flag}", ref apply) || bigChange)
|
||||
_manager.ChangeApplyParameter(_selector.Selected!, flag, apply);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,6 +360,7 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
DrawButtonRow();
|
||||
DrawCustomize();
|
||||
DrawEquipment();
|
||||
DrawCustomizeParameters();
|
||||
_designDetails.Draw();
|
||||
DrawApplicationRules();
|
||||
_modAssociations.Draw();
|
||||
|
|
@ -386,8 +431,8 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest);
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
|
@ -405,8 +450,8 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest);
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ public class NpcPanel(
|
|||
{
|
||||
try
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
var data = ToDesignData();
|
||||
var text = _converter.ShareBase64(data, applyGear, applyCustomize, applyCrest);
|
||||
var text = _converter.ShareBase64(data, applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
ImGui.SetClipboardText(text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -105,11 +105,11 @@ public class NpcPanel(
|
|||
private void SaveDesignOpen()
|
||||
{
|
||||
ImGui.OpenPopup("Save as Design");
|
||||
_newName = _selector.Selection.Name;
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
_newName = _selector.Selection.Name;
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
|
||||
var data = ToDesignData();
|
||||
_newDesign = _converter.Convert(data, applyGear, applyCustomize, applyCrest);
|
||||
_newDesign = _converter.Convert(data, applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
}
|
||||
|
||||
private void SaveDesignDrawPopup()
|
||||
|
|
@ -198,8 +198,8 @@ public class NpcPanel(
|
|||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, applyCrest);
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
_state.ApplyDesign(design, state, StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
|
@ -217,8 +217,8 @@ public class NpcPanel(
|
|||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, applyCrest);
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
_state.ApplyDesign(design, state, StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,9 @@ public class SettingsTab : ITab
|
|||
if (!child)
|
||||
return;
|
||||
|
||||
Checkbox("Enable Auto Designs", "Enable the application of designs associated to characters in the Automation tab to be applied automatically.",
|
||||
_config.EnableAutoDesigns, v => _config.EnableAutoDesigns = v);
|
||||
Checkbox("Enable Auto Designs",
|
||||
"Enable the application of designs associated to characters in the Automation tab to be applied automatically.",
|
||||
_config.EnableAutoDesigns, v => _config.EnableAutoDesigns = v);
|
||||
ImGui.NewLine();
|
||||
ImGui.NewLine();
|
||||
ImGui.NewLine();
|
||||
|
|
@ -97,6 +98,8 @@ public class SettingsTab : ITab
|
|||
Checkbox("Revert Manual Changes on Zone Change",
|
||||
"Restores the old behaviour of reverting your character to its game or automation base whenever you change the zone.",
|
||||
_config.RevertManualChangesOnZoneChange, v => _config.RevertManualChangesOnZoneChange = v);
|
||||
Checkbox("Enable Advanced Customization Options", "Enable the display and editing of advanced customization options like arbitrary colors.",
|
||||
_config.UseAdvancedParameters, v => _config.UseAdvancedParameters = v);
|
||||
ImGui.NewLine();
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +111,8 @@ public class SettingsTab : ITab
|
|||
EphemeralCheckbox("Show Quick Design Bar",
|
||||
"Show a bar separate from the main window that allows you to quickly apply designs or revert your character and target.",
|
||||
_config.Ephemeral.ShowDesignQuickBar, v => _config.Ephemeral.ShowDesignQuickBar = v);
|
||||
EphemeralCheckbox("Lock Quick Design Bar", "Prevent the quick design bar from being moved and lock it in place.", _config.Ephemeral.LockDesignQuickBar,
|
||||
EphemeralCheckbox("Lock Quick Design Bar", "Prevent the quick design bar from being moved and lock it in place.",
|
||||
_config.Ephemeral.LockDesignQuickBar,
|
||||
v => _config.Ephemeral.LockDesignQuickBar = v);
|
||||
if (Widget.ModifiableKeySelector("Hotkey to Toggle Quick Design Bar", "Set a hotkey that opens or closes the quick design bar.",
|
||||
100 * ImGuiHelpers.GlobalScale,
|
||||
|
|
@ -138,7 +142,8 @@ public class SettingsTab : ITab
|
|||
_config.HideWindowInCutscene = v;
|
||||
_uiBuilder.DisableCutsceneUiHide = !v;
|
||||
});
|
||||
EphemeralCheckbox("Lock Main Window", "Prevent the main window from being moved and lock it in place.", _config.Ephemeral.LockMainWindow,
|
||||
EphemeralCheckbox("Lock Main Window", "Prevent the main window from being moved and lock it in place.",
|
||||
_config.Ephemeral.LockMainWindow,
|
||||
v => _config.Ephemeral.LockMainWindow = v);
|
||||
Checkbox("Open Main Window at Game Start", "Whether the main Glamourer window should be open or closed after launching the game.",
|
||||
_config.OpenWindowAtStart, v => _config.OpenWindowAtStart = v);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
|
|
@ -98,13 +99,13 @@ public static class UiHelpers
|
|||
return (currentValue != newValue, currentApply != newApply);
|
||||
}
|
||||
|
||||
public static (EquipFlag, CustomizeFlag, CrestFlag) ConvertKeysToFlags()
|
||||
public static (EquipFlag, CustomizeFlag, CrestFlag, CustomizeParameterFlag) ConvertKeysToFlags()
|
||||
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
|
||||
{
|
||||
(false, false) => (EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All),
|
||||
(true, true) => (EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All),
|
||||
(true, false) => (EquipFlagExtensions.All, (CustomizeFlag)0, CrestExtensions.All),
|
||||
(false, true) => ((EquipFlag)0, CustomizeFlagExtensions.AllRelevant, 0),
|
||||
(false, false) => (EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All, CustomizeParameterExtensions.All),
|
||||
(true, true) => (EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All, CustomizeParameterExtensions.All),
|
||||
(true, false) => (EquipFlagExtensions.All, (CustomizeFlag)0, CrestExtensions.All, 0),
|
||||
(false, true) => ((EquipFlag)0, CustomizeFlagExtensions.AllRelevant, 0, CustomizeParameterExtensions.All),
|
||||
};
|
||||
|
||||
public static (bool, bool) ConvertKeysToBool()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
|
|||
private readonly PenumbraReloaded _penumbraReloaded;
|
||||
private readonly IGameInteropProvider _interop;
|
||||
private readonly delegate* unmanaged[Stdcall]<Human*, byte*, bool, bool> _original;
|
||||
private readonly Post _postEvent = new();
|
||||
|
||||
|
||||
/// <summary> Check whether we in a manual customize update, in which case we need to not toggle certain flags. </summary>
|
||||
public static readonly InMethodChecker InUpdate = new();
|
||||
|
|
@ -36,6 +38,7 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
|
|||
_interop = interop;
|
||||
_changeCustomizeHook = Create();
|
||||
_original = Human.MemberFunctionPointers.UpdateDrawData;
|
||||
interop.InitializeFromAttributes(this);
|
||||
_penumbraReloaded.Subscribe(Restore, PenumbraReloaded.Priority.ChangeCustomizeService);
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +84,23 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
|
|||
if (!InUpdate.InMethod)
|
||||
Invoke(human, ref *(CustomizeArray*)data);
|
||||
|
||||
return _changeCustomizeHook.Original(human, data, skipEquipment);
|
||||
var ret = _changeCustomizeHook.Original(human, data, skipEquipment);
|
||||
_postEvent.Invoke(human);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Subscribe(Action<Model> action, Post.Priority priority)
|
||||
=> _postEvent.Subscribe(action, priority);
|
||||
|
||||
public void Unsubscribe(Action<Model> action)
|
||||
=> _postEvent.Unsubscribe(action);
|
||||
|
||||
public sealed class Post() : EventWrapper<Model, Post.Priority>(nameof(ChangeCustomizeService) + '.' + nameof(Post))
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnCustomizeChanged"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OtterGui.Log;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Glamourer.Interop.Structs;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Dalamud.Plugin.Services;
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.State;
|
||||
|
|
@ -500,7 +501,7 @@ public class CommandService : IDisposable
|
|||
&& _stateManager.GetOrCreate(identifier, data.Objects[0], out state)))
|
||||
continue;
|
||||
|
||||
var design = _converter.Convert(state, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All);
|
||||
var design = _converter.Convert(state, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All, CustomizeParameterExtensions.All);
|
||||
_designManager.CreateClone(design, split[0], true);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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