mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
Add viera ear flags
This commit is contained in:
parent
0f98fac157
commit
00d550f4fe
21 changed files with 245 additions and 21 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 1c517301c9fd0818e2c02e72304d6de121b9d703
|
||||
Subproject commit 54c1944dc7db704733b4788520e494761bb0b58e
|
||||
|
|
@ -38,7 +38,7 @@ public static class ApplicationTypeExtensions
|
|||
var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0;
|
||||
var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0;
|
||||
var crestFlags = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 0;
|
||||
var metaFlags = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState : 0)
|
||||
var metaFlags = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.EarState : 0)
|
||||
| (type.HasFlag(ApplicationType.Weapons) ? MetaFlag.WeaponState : 0)
|
||||
| (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0);
|
||||
var bonusFlags = type.HasFlag(ApplicationType.Armor) ? BonusExtensions.All : 0;
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ public record struct ApplicationCollection(
|
|||
public static readonly ApplicationCollection None = new(0, 0, CustomizeFlag.BodyType, 0, 0, 0);
|
||||
|
||||
public static readonly ApplicationCollection Equipment = new(EquipFlagExtensions.All, BonusExtensions.All,
|
||||
CustomizeFlag.BodyType, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState);
|
||||
CustomizeFlag.BodyType, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState | MetaFlag.EarState);
|
||||
|
||||
public static readonly ApplicationCollection Customizations = new(0, 0, CustomizeFlagExtensions.AllRelevant, 0,
|
||||
CustomizeParameterExtensions.All, MetaFlag.Wetness);
|
||||
|
||||
public static readonly ApplicationCollection Default = new(EquipFlagExtensions.All, BonusExtensions.All,
|
||||
CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState);
|
||||
CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState);
|
||||
|
||||
public static ApplicationCollection FromKeys()
|
||||
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
|
||||
|
|
@ -47,7 +47,7 @@ public record struct ApplicationCollection(
|
|||
Equip = 0;
|
||||
BonusItem = 0;
|
||||
Crest = 0;
|
||||
Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState);
|
||||
Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState);
|
||||
}
|
||||
|
||||
public void RemoveCustomize()
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ public class DesignBase
|
|||
}
|
||||
|
||||
/// <summary> Used when importing .cma or .chara files. </summary>
|
||||
internal DesignBase(CustomizeService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags, BonusItemFlag bonusFlags)
|
||||
internal DesignBase(CustomizeService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags,
|
||||
BonusItemFlag bonusFlags)
|
||||
{
|
||||
_designData = designData;
|
||||
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
|
||||
|
|
@ -254,9 +255,10 @@ public class DesignBase
|
|||
ret[slot.ToString()] = Serialize(item.Id, stains, crest, DoApplyEquip(slot), DoApplyStain(slot), DoApplyCrest(crestSlot));
|
||||
}
|
||||
|
||||
ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
|
||||
ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply");
|
||||
ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).ToJObject("Show", "Apply");
|
||||
ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
|
||||
ret["VieraEars"] = new QuadBool(_designData.AreEarsVisible(), DoApplyMeta(MetaIndex.EarState)).ToJObject("Show", "Apply");
|
||||
ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply");
|
||||
ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).ToJObject("Show", "Apply");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -603,6 +605,10 @@ public class DesignBase
|
|||
metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
|
||||
design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled);
|
||||
design._designData.SetVisor(metaValue.ForcedValue);
|
||||
|
||||
metaValue = QuadBool.FromJObject(equip["VieraEars"], "Show", "Apply", QuadBool.NullTrue);
|
||||
design.SetApplyMeta(MetaIndex.EarState, metaValue.Enabled);
|
||||
design._designData.SetEarsVisible(metaValue.ForcedValue);
|
||||
return;
|
||||
|
||||
void PrintWarning(string msg)
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ public unsafe struct DesignData
|
|||
MetaIndex.HatState => IsHatVisible(),
|
||||
MetaIndex.VisorState => IsVisorToggled(),
|
||||
MetaIndex.WeaponState => IsWeaponVisible(),
|
||||
MetaIndex.EarState => AreEarsVisible(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
|
@ -297,6 +298,7 @@ public unsafe struct DesignData
|
|||
MetaIndex.HatState => SetHatVisible(value),
|
||||
MetaIndex.VisorState => SetVisor(value),
|
||||
MetaIndex.WeaponState => SetWeaponVisible(value),
|
||||
MetaIndex.EarState => SetEarsVisible(value),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
|
@ -340,6 +342,9 @@ public unsafe struct DesignData
|
|||
public readonly bool IsWeaponVisible()
|
||||
=> (_states & 0x08) == 0x08;
|
||||
|
||||
public readonly bool AreEarsVisible()
|
||||
=> (_states & 0x10) == 0x00;
|
||||
|
||||
public bool SetWeaponVisible(bool value)
|
||||
{
|
||||
if (value == IsWeaponVisible())
|
||||
|
|
@ -349,6 +354,15 @@ public unsafe struct DesignData
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool SetEarsVisible(bool value)
|
||||
{
|
||||
if (value == AreEarsVisible())
|
||||
return false;
|
||||
|
||||
_states = (byte)(value ? _states & ~0x10 : _states | 0x10);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetDefaultEquipment(ItemManager items)
|
||||
{
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
|
|
@ -386,6 +400,7 @@ public unsafe struct DesignData
|
|||
|
||||
SetHatVisible(true);
|
||||
SetWeaponVisible(true);
|
||||
SetEarsVisible(true);
|
||||
SetVisor(false);
|
||||
fixed (uint* ptr = _itemIds)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,14 +10,15 @@ public enum MetaIndex
|
|||
VisorState = StateIndex.MetaVisorState,
|
||||
WeaponState = StateIndex.MetaWeaponState,
|
||||
ModelId = StateIndex.MetaModelId,
|
||||
EarState = StateIndex.MetaEarState,
|
||||
}
|
||||
|
||||
public static class MetaExtensions
|
||||
{
|
||||
public static readonly IReadOnlyList<MetaIndex> AllRelevant =
|
||||
[MetaIndex.Wetness, MetaIndex.HatState, MetaIndex.VisorState, MetaIndex.WeaponState];
|
||||
[MetaIndex.Wetness, MetaIndex.HatState, MetaIndex.VisorState, MetaIndex.WeaponState, MetaIndex.EarState];
|
||||
|
||||
public const MetaFlag All = MetaFlag.Wetness | MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState;
|
||||
public const MetaFlag All = MetaFlag.Wetness | MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState;
|
||||
|
||||
public static MetaFlag ToFlag(this MetaIndex index)
|
||||
=> index switch
|
||||
|
|
@ -26,6 +27,7 @@ public static class MetaExtensions
|
|||
MetaIndex.HatState => MetaFlag.HatState,
|
||||
MetaIndex.VisorState => MetaFlag.VisorState,
|
||||
MetaIndex.WeaponState => MetaFlag.WeaponState,
|
||||
MetaIndex.EarState => MetaFlag.EarState,
|
||||
_ => (MetaFlag)byte.MaxValue,
|
||||
};
|
||||
|
||||
|
|
@ -36,7 +38,8 @@ public static class MetaExtensions
|
|||
MetaFlag.HatState => MetaIndex.HatState,
|
||||
MetaFlag.VisorState => MetaIndex.VisorState,
|
||||
MetaFlag.WeaponState => MetaIndex.WeaponState,
|
||||
_ => (MetaIndex)byte.MaxValue,
|
||||
MetaFlag.EarState => MetaIndex.EarState,
|
||||
_ => (MetaIndex)byte.MaxValue,
|
||||
};
|
||||
|
||||
public static IEnumerable<MetaIndex> ToIndices(this MetaFlag index)
|
||||
|
|
@ -49,6 +52,8 @@ public static class MetaExtensions
|
|||
yield return MetaIndex.VisorState;
|
||||
if (index.HasFlag(MetaFlag.WeaponState))
|
||||
yield return MetaIndex.WeaponState;
|
||||
if (index.HasFlag(MetaFlag.EarState))
|
||||
yield return MetaIndex.EarState;
|
||||
}
|
||||
|
||||
public static string ToName(this MetaIndex index)
|
||||
|
|
@ -58,6 +63,7 @@ public static class MetaExtensions
|
|||
MetaIndex.VisorState => "Visor Toggled",
|
||||
MetaIndex.WeaponState => "Weapon Visible",
|
||||
MetaIndex.Wetness => "Force Wetness",
|
||||
MetaIndex.EarState => "Ears Visible",
|
||||
_ => "Unknown Meta",
|
||||
};
|
||||
|
||||
|
|
@ -68,6 +74,7 @@ public static class MetaExtensions
|
|||
MetaIndex.VisorState => "Toggle the visor state of the characters head gear.",
|
||||
MetaIndex.WeaponState => "Hide or show the characters weapons when not drawn.",
|
||||
MetaIndex.Wetness => "Force the character to be wet or not.",
|
||||
MetaIndex.EarState => "Hide or show the characters ears through the head gear. (Viera only)",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,8 @@ public sealed class PenumbraReloaded()
|
|||
|
||||
/// <seealso cref="Interop.VisorService.Restore"/>
|
||||
VisorService = 0,
|
||||
|
||||
/// <seealso cref="Interop.VieraEarService.Restore"/>
|
||||
VieraEarService = 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
Glamourer/Events/VieraEarStateChanged.cs
Normal file
22
Glamourer/Events/VieraEarStateChanged.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the state of viera ear visibility for any draw object is changed.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the model with a changed viera ear visibility state. </item>
|
||||
/// <item>Parameter is the new state. </item>
|
||||
/// <item>Parameter is whether to call the original function. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class VieraEarStateChanged()
|
||||
: EventWrapperRef2<Actor, bool, VieraEarStateChanged.Priority>(nameof(VieraEarStateChanged))
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnVieraEarChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
}
|
||||
|
|
@ -19,4 +19,4 @@ public sealed class VisorStateChanged()
|
|||
/// <seealso cref="State.StateListener.OnVisorChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -305,6 +305,12 @@ public class ActorPanel
|
|||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _stateManager, _state!));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
using (_ = ImRaii.Group())
|
||||
{
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.EarState, _stateManager, _state!));
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMonsterPanel()
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _ob
|
|||
PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(),
|
||||
state.Sources[MetaIndex.VisorState]);
|
||||
ImGui.TableNextRow();
|
||||
PrintRow("Viera Ears Visible", state.BaseData.AreEarsVisible(), state.ModelData.AreEarsVisible(),
|
||||
state.Sources[MetaIndex.EarState]);
|
||||
ImGui.TableNextRow();
|
||||
PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(),
|
||||
state.Sources[MetaIndex.WeaponState]);
|
||||
ImGui.TableNextRow();
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
|
|||
ImGuiUtil.DrawTableColumn(index.ToName());
|
||||
ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString());
|
||||
ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep");
|
||||
ImGui.TableNextRow();
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Model ID");
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace Glamourer.Gui.Tabs.DebugTab;
|
|||
public unsafe class ModelEvaluationPanel(
|
||||
ActorObjectManager _objectManager,
|
||||
VisorService _visorService,
|
||||
VieraEarService _vieraEarService,
|
||||
UpdateSlotService _updateSlotService,
|
||||
ChangeCustomizeService _changeCustomizeService,
|
||||
CrestService _crestService,
|
||||
|
|
@ -84,6 +85,7 @@ public unsafe class ModelEvaluationPanel(
|
|||
ImGuiUtil.CopyOnClickSelectable(offhand.ToString());
|
||||
|
||||
DrawVisor(actor, model);
|
||||
DrawVieraEars(actor, model);
|
||||
DrawHatState(actor, model);
|
||||
DrawWeaponState(actor, model);
|
||||
DrawWetness(actor, model);
|
||||
|
|
@ -135,6 +137,26 @@ public unsafe class ModelEvaluationPanel(
|
|||
_visorService.SetVisorState(model, !VisorService.GetVisorState(model));
|
||||
}
|
||||
|
||||
private void DrawVieraEars(Actor actor, Model model)
|
||||
{
|
||||
using var id = ImRaii.PushId("Viera Ears");
|
||||
ImGuiUtil.DrawTableColumn("Viera Ears");
|
||||
ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.ShowVieraEars.ToString() : "No Character");
|
||||
ImGuiUtil.DrawTableColumn(model.IsHuman ? model.VieraEarsVisible.ToString() : "No Human");
|
||||
ImGui.TableNextColumn();
|
||||
if (!model.IsHuman)
|
||||
return;
|
||||
|
||||
if (ImGui.SmallButton("Set True"))
|
||||
_vieraEarService.SetVieraEarState(model, true);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("Set False"))
|
||||
_vieraEarService.SetVieraEarState(model, false);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("Toggle"))
|
||||
_vieraEarService.SetVieraEarState(model, !model.VieraEarsVisible);
|
||||
}
|
||||
|
||||
private void DrawHatState(Actor actor, Model model)
|
||||
{
|
||||
using var id = ImRaii.PushId("HatState");
|
||||
|
|
|
|||
82
Glamourer/Interop/VieraEarService.cs
Normal file
82
Glamourer/Interop/VieraEarService.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using Glamourer.Events;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe class VieraEarService : IDisposable
|
||||
{
|
||||
private readonly PenumbraReloaded _penumbra;
|
||||
private readonly IGameInteropProvider _interop;
|
||||
public readonly VieraEarStateChanged Event;
|
||||
|
||||
public VieraEarService(VieraEarStateChanged visorStateChanged, IGameInteropProvider interop, PenumbraReloaded penumbra)
|
||||
{
|
||||
_interop = interop;
|
||||
_penumbra = penumbra;
|
||||
Event = visorStateChanged;
|
||||
_setupVieraEarHook = Create();
|
||||
_penumbra.Subscribe(Restore, PenumbraReloaded.Priority.VieraEarService);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_setupVieraEarHook.Dispose();
|
||||
_penumbra.Unsubscribe(Restore);
|
||||
}
|
||||
|
||||
/// <summary> Obtain the current state of viera ears for the given draw object (true: toggled). </summary>
|
||||
public static unsafe bool GetVieraEarState(Model characterBase)
|
||||
=> characterBase is { IsCharacterBase: true, VieraEarsVisible: true };
|
||||
|
||||
/// <summary> Manually set the state of the Visor for the given draw object. </summary>
|
||||
/// <param name="human"> The draw object. </param>
|
||||
/// <param name="on"> The desired state (true: toggled). </param>
|
||||
/// <returns> Whether the state was changed. </returns>
|
||||
public bool SetVieraEarState(Model human, bool on)
|
||||
{
|
||||
if (!human.IsHuman)
|
||||
return false;
|
||||
|
||||
var oldState = GetVieraEarState(human);
|
||||
Glamourer.Log.Verbose($"[SetVieraEarState] Invoked manually on 0x{human.Address:X} switching from {oldState} to {on}.");
|
||||
if (oldState == on)
|
||||
return false;
|
||||
|
||||
human.VieraEarsVisible = on;
|
||||
return true;
|
||||
}
|
||||
|
||||
private delegate void UpdateVieraEarDelegateInternal(DrawDataContainer* drawData, byte on);
|
||||
|
||||
private Hook<UpdateVieraEarDelegateInternal> _setupVieraEarHook;
|
||||
|
||||
private void SetupVieraEarDetour(DrawDataContainer* drawData, byte value)
|
||||
{
|
||||
Actor actor = drawData->OwnerObject;
|
||||
var originalOn = value != 0;
|
||||
var on = originalOn;
|
||||
// Invoke an event that can change the requested value
|
||||
Event.Invoke(actor, ref on);
|
||||
|
||||
Glamourer.Log.Verbose(
|
||||
$"[SetVieraEarState] Invoked from game on 0x{actor.Address:X} switching to {on} (original {originalOn} from {value}).");
|
||||
|
||||
_setupVieraEarHook.Original(drawData, on ? (byte)1 : (byte)0);
|
||||
}
|
||||
|
||||
private unsafe Hook<UpdateVieraEarDelegateInternal> Create()
|
||||
{
|
||||
var hook = _interop.HookFromSignature<UpdateVieraEarDelegateInternal>("E8 ?? ?? ?? ?? 48 8D 8F ?? ?? ?? ?? 4C 8D 4C 24", SetupVieraEarDetour);
|
||||
hook.Enable();
|
||||
return hook;
|
||||
}
|
||||
|
||||
private void Restore()
|
||||
{
|
||||
_setupVieraEarHook.Dispose();
|
||||
_setupVieraEarHook = Create();
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,9 @@ namespace Glamourer.Interop;
|
|||
|
||||
public class VisorService : IDisposable
|
||||
{
|
||||
private readonly PenumbraReloaded _penumbra;
|
||||
private readonly IGameInteropProvider _interop;
|
||||
public readonly VisorStateChanged Event;
|
||||
private readonly PenumbraReloaded _penumbra;
|
||||
private readonly IGameInteropProvider _interop;
|
||||
public readonly VisorStateChanged Event;
|
||||
|
||||
public VisorService(VisorStateChanged visorStateChanged, IGameInteropProvider interop, PenumbraReloaded penumbra)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ public static class StaticServiceManager
|
|||
|
||||
private static ServiceManager AddEvents(this ServiceManager services)
|
||||
=> services.AddSingleton<VisorStateChanged>()
|
||||
.AddSingleton<VieraEarStateChanged>()
|
||||
.AddSingleton<EquipSlotUpdating>()
|
||||
.AddSingleton<DesignChanged>()
|
||||
.AddSingleton<AutomationChanged>()
|
||||
|
|
@ -96,6 +97,7 @@ public static class StaticServiceManager
|
|||
|
||||
private static ServiceManager AddInterop(this ServiceManager services)
|
||||
=> services.AddSingleton<VisorService>()
|
||||
.AddSingleton<VieraEarService>()
|
||||
.AddSingleton<ChangeCustomizeService>()
|
||||
.AddSingleton<MetaService>()
|
||||
.AddSingleton<UpdateSlotService>()
|
||||
|
|
|
|||
|
|
@ -262,6 +262,14 @@ public class StateApplier(
|
|||
_visor.SetVisorState(actor.Model, value);
|
||||
return;
|
||||
}
|
||||
case MetaIndex.EarState:
|
||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||
{
|
||||
var model = actor.Model;
|
||||
model.VieraEarsVisible = value;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -402,6 +410,7 @@ public class StateApplier(
|
|||
ChangeMetaState(actors, MetaIndex.HatState, state.ModelData.IsHatVisible());
|
||||
ChangeMetaState(actors, MetaIndex.WeaponState, state.ModelData.IsWeaponVisible());
|
||||
ChangeMetaState(actors, MetaIndex.VisorState, state.ModelData.IsVisorToggled());
|
||||
ChangeMetaState(actors, MetaIndex.EarState, state.ModelData.AreEarsVisible());
|
||||
ChangeCrests(actors, state.ModelData.CrestVisibility);
|
||||
ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters);
|
||||
ChangeMaterialValues(actors, state.Materials);
|
||||
|
|
|
|||
|
|
@ -189,8 +189,9 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
public const int MetaVisorState = MetaHatState + 1;
|
||||
public const int MetaWeaponState = MetaVisorState + 1;
|
||||
public const int MetaModelId = MetaWeaponState + 1;
|
||||
public const int MetaEarState = MetaModelId + 1;
|
||||
|
||||
public const int CrestHead = MetaModelId + 1;
|
||||
public const int CrestHead = MetaEarState + 1;
|
||||
public const int CrestBody = CrestHead + 1;
|
||||
public const int CrestOffhand = CrestBody + 1;
|
||||
|
||||
|
|
@ -300,6 +301,7 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
MetaHatState => MetaFlag.HatState,
|
||||
MetaVisorState => MetaFlag.VisorState,
|
||||
MetaWeaponState => MetaFlag.WeaponState,
|
||||
MetaEarState => MetaFlag.EarState,
|
||||
MetaModelId => true,
|
||||
|
||||
CrestHead => CrestFlag.Head,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Penumbra.GameData.Enums;
|
|||
using Penumbra.GameData.Structs;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Glamourer.GameData;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
|
|
@ -39,6 +40,7 @@ public class StateListener : IDisposable
|
|||
private readonly WeaponLoading _weaponLoading;
|
||||
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
||||
private readonly VisorStateChanged _visorState;
|
||||
private readonly VieraEarStateChanged _vieraEarState;
|
||||
private readonly WeaponVisibilityChanged _weaponVisibility;
|
||||
private readonly StateFinalized _stateFinalized;
|
||||
private readonly AutoDesignApplier _autoDesignApplier;
|
||||
|
|
@ -62,7 +64,7 @@ public class StateListener : IDisposable
|
|||
WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier,
|
||||
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ActorObjectManager objects,
|
||||
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
|
||||
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateFinalized stateFinalized)
|
||||
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateFinalized stateFinalized, VieraEarStateChanged vieraEarState)
|
||||
{
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
|
|
@ -88,6 +90,7 @@ public class StateListener : IDisposable
|
|||
_crestService = crestService;
|
||||
_bonusSlotUpdating = bonusSlotUpdating;
|
||||
_stateFinalized = stateFinalized;
|
||||
_vieraEarState = vieraEarState;
|
||||
Subscribe();
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +269,7 @@ public class StateListener : IDisposable
|
|||
|
||||
private void OnGearsetDataLoaded(Actor actor, Model model)
|
||||
{
|
||||
if (!actor.Valid || (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart))
|
||||
if (!actor.Valid || _condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
|
||||
return;
|
||||
|
||||
// ensure actor and state are valid.
|
||||
|
|
@ -710,6 +713,44 @@ public class StateListener : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary> Handle visor state changes made by the game. </summary>
|
||||
private void OnVieraEarChange(Actor actor, ref bool value)
|
||||
{
|
||||
// Value is inverted compared to our own handling.
|
||||
|
||||
// Skip updates when in customize update.
|
||||
if (ChangeCustomizeService.InUpdate.InMethod)
|
||||
return;
|
||||
|
||||
if (!actor.IsCharacter)
|
||||
return;
|
||||
|
||||
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
|
||||
return;
|
||||
|
||||
if (!actor.Identifier(_actors, out var identifier))
|
||||
return;
|
||||
|
||||
if (!_manager.TryGetValue(identifier, out var state))
|
||||
return;
|
||||
|
||||
// Update visor base state.
|
||||
if (state.BaseData.SetEarsVisible(!value))
|
||||
{
|
||||
// if base state changed, either overwrite the actual value if we have fixed values,
|
||||
// or overwrite the stored model state with the new one.
|
||||
if (state.Sources[MetaIndex.EarState].IsFixed())
|
||||
value = !state.ModelData.AreEarsVisible();
|
||||
else
|
||||
_manager.ChangeMetaState(state, MetaIndex.EarState, !value, ApplySettings.Game);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if base state did not change, overwrite the value with the model state one.
|
||||
value = !state.ModelData.AreEarsVisible();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Handle Hat Visibility changes. These act on the game object. </summary>
|
||||
private void OnHeadGearVisibilityChange(Actor actor, ref bool value)
|
||||
{
|
||||
|
|
@ -802,6 +843,7 @@ public class StateListener : IDisposable
|
|||
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||
_vieraEarState.Subscribe(OnVieraEarChange, VieraEarStateChanged.Priority.StateListener);
|
||||
_headGearVisibility.Subscribe(OnHeadGearVisibilityChange, HeadGearVisibilityChanged.Priority.StateListener);
|
||||
_weaponVisibility.Subscribe(OnWeaponVisibilityChange, WeaponVisibilityChanged.Priority.StateListener);
|
||||
_changeCustomizeService.Subscribe(OnCustomizeChange, ChangeCustomizeService.Priority.StateListener);
|
||||
|
|
@ -820,6 +862,7 @@ public class StateListener : IDisposable
|
|||
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||
_visorState.Unsubscribe(OnVisorChange);
|
||||
_vieraEarState.Unsubscribe(OnVieraEarChange);
|
||||
_headGearVisibility.Unsubscribe(OnHeadGearVisibilityChange);
|
||||
_weaponVisibility.Unsubscribe(OnWeaponVisibilityChange);
|
||||
_changeCustomizeService.Unsubscribe(OnCustomizeChange);
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ public sealed class StateManager(
|
|||
|
||||
// Visor state is a flag on the game object, but we can see the actual state on the draw object.
|
||||
ret.SetVisor(VisorService.GetVisorState(model));
|
||||
ret.SetEarsVisible(model.VieraEarsVisible);
|
||||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
ret.SetCrest(slot, CrestService.GetModelCrest(actor, slot));
|
||||
|
|
@ -186,7 +187,7 @@ public sealed class StateManager(
|
|||
off = actor.GetOffhand();
|
||||
FistWeaponHack(ref ret, ref main, ref off);
|
||||
ret.SetVisor(actor.AsCharacter->DrawData.IsVisorToggled);
|
||||
|
||||
ret.SetEarsVisible(actor.ShowVieraEars);
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
ret.SetCrest(slot, actor.GetCrest(slot));
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 65c5bf3f46569a54b0057c9015ab839b4e2a4350
|
||||
Subproject commit 17f2f496664b0d69ebd7fcdabe7bc8e3e20b6463
|
||||
Loading…
Add table
Add a link
Reference in a new issue