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 customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0;
|
||||||
var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0;
|
var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0;
|
||||||
var crestFlags = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 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.Weapons) ? MetaFlag.WeaponState : 0)
|
||||||
| (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0);
|
| (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0);
|
||||||
var bonusFlags = type.HasFlag(ApplicationType.Armor) ? BonusExtensions.All : 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 None = new(0, 0, CustomizeFlag.BodyType, 0, 0, 0);
|
||||||
|
|
||||||
public static readonly ApplicationCollection Equipment = new(EquipFlagExtensions.All, BonusExtensions.All,
|
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,
|
public static readonly ApplicationCollection Customizations = new(0, 0, CustomizeFlagExtensions.AllRelevant, 0,
|
||||||
CustomizeParameterExtensions.All, MetaFlag.Wetness);
|
CustomizeParameterExtensions.All, MetaFlag.Wetness);
|
||||||
|
|
||||||
public static readonly ApplicationCollection Default = new(EquipFlagExtensions.All, BonusExtensions.All,
|
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()
|
public static ApplicationCollection FromKeys()
|
||||||
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
|
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
|
||||||
|
|
@ -47,7 +47,7 @@ public record struct ApplicationCollection(
|
||||||
Equip = 0;
|
Equip = 0;
|
||||||
BonusItem = 0;
|
BonusItem = 0;
|
||||||
Crest = 0;
|
Crest = 0;
|
||||||
Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState);
|
Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveCustomize()
|
public void RemoveCustomize()
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@ public class DesignBase
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Used when importing .cma or .chara files. </summary>
|
/// <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;
|
_designData = designData;
|
||||||
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
|
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[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["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
|
||||||
ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply");
|
ret["VieraEars"] = new QuadBool(_designData.AreEarsVisible(), DoApplyMeta(MetaIndex.EarState)).ToJObject("Show", "Apply");
|
||||||
ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).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
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -603,6 +605,10 @@ public class DesignBase
|
||||||
metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
|
metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
|
||||||
design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled);
|
design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled);
|
||||||
design._designData.SetVisor(metaValue.ForcedValue);
|
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;
|
return;
|
||||||
|
|
||||||
void PrintWarning(string msg)
|
void PrintWarning(string msg)
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,7 @@ public unsafe struct DesignData
|
||||||
MetaIndex.HatState => IsHatVisible(),
|
MetaIndex.HatState => IsHatVisible(),
|
||||||
MetaIndex.VisorState => IsVisorToggled(),
|
MetaIndex.VisorState => IsVisorToggled(),
|
||||||
MetaIndex.WeaponState => IsWeaponVisible(),
|
MetaIndex.WeaponState => IsWeaponVisible(),
|
||||||
|
MetaIndex.EarState => AreEarsVisible(),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -297,6 +298,7 @@ public unsafe struct DesignData
|
||||||
MetaIndex.HatState => SetHatVisible(value),
|
MetaIndex.HatState => SetHatVisible(value),
|
||||||
MetaIndex.VisorState => SetVisor(value),
|
MetaIndex.VisorState => SetVisor(value),
|
||||||
MetaIndex.WeaponState => SetWeaponVisible(value),
|
MetaIndex.WeaponState => SetWeaponVisible(value),
|
||||||
|
MetaIndex.EarState => SetEarsVisible(value),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -340,6 +342,9 @@ public unsafe struct DesignData
|
||||||
public readonly bool IsWeaponVisible()
|
public readonly bool IsWeaponVisible()
|
||||||
=> (_states & 0x08) == 0x08;
|
=> (_states & 0x08) == 0x08;
|
||||||
|
|
||||||
|
public readonly bool AreEarsVisible()
|
||||||
|
=> (_states & 0x10) == 0x00;
|
||||||
|
|
||||||
public bool SetWeaponVisible(bool value)
|
public bool SetWeaponVisible(bool value)
|
||||||
{
|
{
|
||||||
if (value == IsWeaponVisible())
|
if (value == IsWeaponVisible())
|
||||||
|
|
@ -349,6 +354,15 @@ public unsafe struct DesignData
|
||||||
return true;
|
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)
|
public void SetDefaultEquipment(ItemManager items)
|
||||||
{
|
{
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
|
|
@ -386,6 +400,7 @@ public unsafe struct DesignData
|
||||||
|
|
||||||
SetHatVisible(true);
|
SetHatVisible(true);
|
||||||
SetWeaponVisible(true);
|
SetWeaponVisible(true);
|
||||||
|
SetEarsVisible(true);
|
||||||
SetVisor(false);
|
SetVisor(false);
|
||||||
fixed (uint* ptr = _itemIds)
|
fixed (uint* ptr = _itemIds)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,15 @@ public enum MetaIndex
|
||||||
VisorState = StateIndex.MetaVisorState,
|
VisorState = StateIndex.MetaVisorState,
|
||||||
WeaponState = StateIndex.MetaWeaponState,
|
WeaponState = StateIndex.MetaWeaponState,
|
||||||
ModelId = StateIndex.MetaModelId,
|
ModelId = StateIndex.MetaModelId,
|
||||||
|
EarState = StateIndex.MetaEarState,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MetaExtensions
|
public static class MetaExtensions
|
||||||
{
|
{
|
||||||
public static readonly IReadOnlyList<MetaIndex> AllRelevant =
|
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)
|
public static MetaFlag ToFlag(this MetaIndex index)
|
||||||
=> index switch
|
=> index switch
|
||||||
|
|
@ -26,6 +27,7 @@ public static class MetaExtensions
|
||||||
MetaIndex.HatState => MetaFlag.HatState,
|
MetaIndex.HatState => MetaFlag.HatState,
|
||||||
MetaIndex.VisorState => MetaFlag.VisorState,
|
MetaIndex.VisorState => MetaFlag.VisorState,
|
||||||
MetaIndex.WeaponState => MetaFlag.WeaponState,
|
MetaIndex.WeaponState => MetaFlag.WeaponState,
|
||||||
|
MetaIndex.EarState => MetaFlag.EarState,
|
||||||
_ => (MetaFlag)byte.MaxValue,
|
_ => (MetaFlag)byte.MaxValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,7 +38,8 @@ public static class MetaExtensions
|
||||||
MetaFlag.HatState => MetaIndex.HatState,
|
MetaFlag.HatState => MetaIndex.HatState,
|
||||||
MetaFlag.VisorState => MetaIndex.VisorState,
|
MetaFlag.VisorState => MetaIndex.VisorState,
|
||||||
MetaFlag.WeaponState => MetaIndex.WeaponState,
|
MetaFlag.WeaponState => MetaIndex.WeaponState,
|
||||||
_ => (MetaIndex)byte.MaxValue,
|
MetaFlag.EarState => MetaIndex.EarState,
|
||||||
|
_ => (MetaIndex)byte.MaxValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static IEnumerable<MetaIndex> ToIndices(this MetaFlag index)
|
public static IEnumerable<MetaIndex> ToIndices(this MetaFlag index)
|
||||||
|
|
@ -49,6 +52,8 @@ public static class MetaExtensions
|
||||||
yield return MetaIndex.VisorState;
|
yield return MetaIndex.VisorState;
|
||||||
if (index.HasFlag(MetaFlag.WeaponState))
|
if (index.HasFlag(MetaFlag.WeaponState))
|
||||||
yield return MetaIndex.WeaponState;
|
yield return MetaIndex.WeaponState;
|
||||||
|
if (index.HasFlag(MetaFlag.EarState))
|
||||||
|
yield return MetaIndex.EarState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToName(this MetaIndex index)
|
public static string ToName(this MetaIndex index)
|
||||||
|
|
@ -58,6 +63,7 @@ public static class MetaExtensions
|
||||||
MetaIndex.VisorState => "Visor Toggled",
|
MetaIndex.VisorState => "Visor Toggled",
|
||||||
MetaIndex.WeaponState => "Weapon Visible",
|
MetaIndex.WeaponState => "Weapon Visible",
|
||||||
MetaIndex.Wetness => "Force Wetness",
|
MetaIndex.Wetness => "Force Wetness",
|
||||||
|
MetaIndex.EarState => "Ears Visible",
|
||||||
_ => "Unknown Meta",
|
_ => "Unknown Meta",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -68,6 +74,7 @@ public static class MetaExtensions
|
||||||
MetaIndex.VisorState => "Toggle the visor state of the characters head gear.",
|
MetaIndex.VisorState => "Toggle the visor state of the characters head gear.",
|
||||||
MetaIndex.WeaponState => "Hide or show the characters weapons when not drawn.",
|
MetaIndex.WeaponState => "Hide or show the characters weapons when not drawn.",
|
||||||
MetaIndex.Wetness => "Force the character to be wet or not.",
|
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,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,8 @@ public sealed class PenumbraReloaded()
|
||||||
|
|
||||||
/// <seealso cref="Interop.VisorService.Restore"/>
|
/// <seealso cref="Interop.VisorService.Restore"/>
|
||||||
VisorService = 0,
|
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"/>
|
/// <seealso cref="State.StateListener.OnVisorChange"/>
|
||||||
StateListener = 0,
|
StateListener = 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -305,6 +305,12 @@ public class ActorPanel
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _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()
|
private void DrawMonsterPanel()
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,9 @@ public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _ob
|
||||||
PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(),
|
PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(),
|
||||||
state.Sources[MetaIndex.VisorState]);
|
state.Sources[MetaIndex.VisorState]);
|
||||||
ImGui.TableNextRow();
|
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(),
|
PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(),
|
||||||
state.Sources[MetaIndex.WeaponState]);
|
state.Sources[MetaIndex.WeaponState]);
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
|
||||||
ImGuiUtil.DrawTableColumn(index.ToName());
|
ImGuiUtil.DrawTableColumn(index.ToName());
|
||||||
ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString());
|
ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString());
|
||||||
ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep");
|
ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep");
|
||||||
|
ImGui.TableNextRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.DrawTableColumn("Model ID");
|
ImGuiUtil.DrawTableColumn("Model ID");
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ namespace Glamourer.Gui.Tabs.DebugTab;
|
||||||
public unsafe class ModelEvaluationPanel(
|
public unsafe class ModelEvaluationPanel(
|
||||||
ActorObjectManager _objectManager,
|
ActorObjectManager _objectManager,
|
||||||
VisorService _visorService,
|
VisorService _visorService,
|
||||||
|
VieraEarService _vieraEarService,
|
||||||
UpdateSlotService _updateSlotService,
|
UpdateSlotService _updateSlotService,
|
||||||
ChangeCustomizeService _changeCustomizeService,
|
ChangeCustomizeService _changeCustomizeService,
|
||||||
CrestService _crestService,
|
CrestService _crestService,
|
||||||
|
|
@ -84,6 +85,7 @@ public unsafe class ModelEvaluationPanel(
|
||||||
ImGuiUtil.CopyOnClickSelectable(offhand.ToString());
|
ImGuiUtil.CopyOnClickSelectable(offhand.ToString());
|
||||||
|
|
||||||
DrawVisor(actor, model);
|
DrawVisor(actor, model);
|
||||||
|
DrawVieraEars(actor, model);
|
||||||
DrawHatState(actor, model);
|
DrawHatState(actor, model);
|
||||||
DrawWeaponState(actor, model);
|
DrawWeaponState(actor, model);
|
||||||
DrawWetness(actor, model);
|
DrawWetness(actor, model);
|
||||||
|
|
@ -135,6 +137,26 @@ public unsafe class ModelEvaluationPanel(
|
||||||
_visorService.SetVisorState(model, !VisorService.GetVisorState(model));
|
_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)
|
private void DrawHatState(Actor actor, Model model)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId("HatState");
|
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
|
public class VisorService : IDisposable
|
||||||
{
|
{
|
||||||
private readonly PenumbraReloaded _penumbra;
|
private readonly PenumbraReloaded _penumbra;
|
||||||
private readonly IGameInteropProvider _interop;
|
private readonly IGameInteropProvider _interop;
|
||||||
public readonly VisorStateChanged Event;
|
public readonly VisorStateChanged Event;
|
||||||
|
|
||||||
public VisorService(VisorStateChanged visorStateChanged, IGameInteropProvider interop, PenumbraReloaded penumbra)
|
public VisorService(VisorStateChanged visorStateChanged, IGameInteropProvider interop, PenumbraReloaded penumbra)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ public static class StaticServiceManager
|
||||||
|
|
||||||
private static ServiceManager AddEvents(this ServiceManager services)
|
private static ServiceManager AddEvents(this ServiceManager services)
|
||||||
=> services.AddSingleton<VisorStateChanged>()
|
=> services.AddSingleton<VisorStateChanged>()
|
||||||
|
.AddSingleton<VieraEarStateChanged>()
|
||||||
.AddSingleton<EquipSlotUpdating>()
|
.AddSingleton<EquipSlotUpdating>()
|
||||||
.AddSingleton<DesignChanged>()
|
.AddSingleton<DesignChanged>()
|
||||||
.AddSingleton<AutomationChanged>()
|
.AddSingleton<AutomationChanged>()
|
||||||
|
|
@ -96,6 +97,7 @@ public static class StaticServiceManager
|
||||||
|
|
||||||
private static ServiceManager AddInterop(this ServiceManager services)
|
private static ServiceManager AddInterop(this ServiceManager services)
|
||||||
=> services.AddSingleton<VisorService>()
|
=> services.AddSingleton<VisorService>()
|
||||||
|
.AddSingleton<VieraEarService>()
|
||||||
.AddSingleton<ChangeCustomizeService>()
|
.AddSingleton<ChangeCustomizeService>()
|
||||||
.AddSingleton<MetaService>()
|
.AddSingleton<MetaService>()
|
||||||
.AddSingleton<UpdateSlotService>()
|
.AddSingleton<UpdateSlotService>()
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,14 @@ public class StateApplier(
|
||||||
_visor.SetVisorState(actor.Model, value);
|
_visor.SetVisorState(actor.Model, value);
|
||||||
return;
|
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.HatState, state.ModelData.IsHatVisible());
|
||||||
ChangeMetaState(actors, MetaIndex.WeaponState, state.ModelData.IsWeaponVisible());
|
ChangeMetaState(actors, MetaIndex.WeaponState, state.ModelData.IsWeaponVisible());
|
||||||
ChangeMetaState(actors, MetaIndex.VisorState, state.ModelData.IsVisorToggled());
|
ChangeMetaState(actors, MetaIndex.VisorState, state.ModelData.IsVisorToggled());
|
||||||
|
ChangeMetaState(actors, MetaIndex.EarState, state.ModelData.AreEarsVisible());
|
||||||
ChangeCrests(actors, state.ModelData.CrestVisibility);
|
ChangeCrests(actors, state.ModelData.CrestVisibility);
|
||||||
ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters);
|
ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters);
|
||||||
ChangeMaterialValues(actors, state.Materials);
|
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 MetaVisorState = MetaHatState + 1;
|
||||||
public const int MetaWeaponState = MetaVisorState + 1;
|
public const int MetaWeaponState = MetaVisorState + 1;
|
||||||
public const int MetaModelId = MetaWeaponState + 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 CrestBody = CrestHead + 1;
|
||||||
public const int CrestOffhand = CrestBody + 1;
|
public const int CrestOffhand = CrestBody + 1;
|
||||||
|
|
||||||
|
|
@ -300,6 +301,7 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
||||||
MetaHatState => MetaFlag.HatState,
|
MetaHatState => MetaFlag.HatState,
|
||||||
MetaVisorState => MetaFlag.VisorState,
|
MetaVisorState => MetaFlag.VisorState,
|
||||||
MetaWeaponState => MetaFlag.WeaponState,
|
MetaWeaponState => MetaFlag.WeaponState,
|
||||||
|
MetaEarState => MetaFlag.EarState,
|
||||||
MetaModelId => true,
|
MetaModelId => true,
|
||||||
|
|
||||||
CrestHead => CrestFlag.Head,
|
CrestHead => CrestFlag.Head,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Penumbra.GameData.DataContainers;
|
using Penumbra.GameData.DataContainers;
|
||||||
|
|
@ -39,6 +40,7 @@ public class StateListener : IDisposable
|
||||||
private readonly WeaponLoading _weaponLoading;
|
private readonly WeaponLoading _weaponLoading;
|
||||||
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
||||||
private readonly VisorStateChanged _visorState;
|
private readonly VisorStateChanged _visorState;
|
||||||
|
private readonly VieraEarStateChanged _vieraEarState;
|
||||||
private readonly WeaponVisibilityChanged _weaponVisibility;
|
private readonly WeaponVisibilityChanged _weaponVisibility;
|
||||||
private readonly StateFinalized _stateFinalized;
|
private readonly StateFinalized _stateFinalized;
|
||||||
private readonly AutoDesignApplier _autoDesignApplier;
|
private readonly AutoDesignApplier _autoDesignApplier;
|
||||||
|
|
@ -62,7 +64,7 @@ public class StateListener : IDisposable
|
||||||
WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier,
|
WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier,
|
||||||
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ActorObjectManager objects,
|
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ActorObjectManager objects,
|
||||||
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
|
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
|
||||||
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateFinalized stateFinalized)
|
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateFinalized stateFinalized, VieraEarStateChanged vieraEarState)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_items = items;
|
_items = items;
|
||||||
|
|
@ -88,6 +90,7 @@ public class StateListener : IDisposable
|
||||||
_crestService = crestService;
|
_crestService = crestService;
|
||||||
_bonusSlotUpdating = bonusSlotUpdating;
|
_bonusSlotUpdating = bonusSlotUpdating;
|
||||||
_stateFinalized = stateFinalized;
|
_stateFinalized = stateFinalized;
|
||||||
|
_vieraEarState = vieraEarState;
|
||||||
Subscribe();
|
Subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,7 +269,7 @@ public class StateListener : IDisposable
|
||||||
|
|
||||||
private void OnGearsetDataLoaded(Actor actor, Model model)
|
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;
|
return;
|
||||||
|
|
||||||
// ensure actor and state are valid.
|
// 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>
|
/// <summary> Handle Hat Visibility changes. These act on the game object. </summary>
|
||||||
private void OnHeadGearVisibilityChange(Actor actor, ref bool value)
|
private void OnHeadGearVisibilityChange(Actor actor, ref bool value)
|
||||||
{
|
{
|
||||||
|
|
@ -802,6 +843,7 @@ public class StateListener : IDisposable
|
||||||
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
||||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||||
|
_vieraEarState.Subscribe(OnVieraEarChange, VieraEarStateChanged.Priority.StateListener);
|
||||||
_headGearVisibility.Subscribe(OnHeadGearVisibilityChange, HeadGearVisibilityChanged.Priority.StateListener);
|
_headGearVisibility.Subscribe(OnHeadGearVisibilityChange, HeadGearVisibilityChanged.Priority.StateListener);
|
||||||
_weaponVisibility.Subscribe(OnWeaponVisibilityChange, WeaponVisibilityChanged.Priority.StateListener);
|
_weaponVisibility.Subscribe(OnWeaponVisibilityChange, WeaponVisibilityChanged.Priority.StateListener);
|
||||||
_changeCustomizeService.Subscribe(OnCustomizeChange, ChangeCustomizeService.Priority.StateListener);
|
_changeCustomizeService.Subscribe(OnCustomizeChange, ChangeCustomizeService.Priority.StateListener);
|
||||||
|
|
@ -820,6 +862,7 @@ public class StateListener : IDisposable
|
||||||
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
||||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||||
_visorState.Unsubscribe(OnVisorChange);
|
_visorState.Unsubscribe(OnVisorChange);
|
||||||
|
_vieraEarState.Unsubscribe(OnVieraEarChange);
|
||||||
_headGearVisibility.Unsubscribe(OnHeadGearVisibilityChange);
|
_headGearVisibility.Unsubscribe(OnHeadGearVisibilityChange);
|
||||||
_weaponVisibility.Unsubscribe(OnWeaponVisibilityChange);
|
_weaponVisibility.Unsubscribe(OnWeaponVisibilityChange);
|
||||||
_changeCustomizeService.Unsubscribe(OnCustomizeChange);
|
_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.
|
// 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.SetVisor(VisorService.GetVisorState(model));
|
||||||
|
ret.SetEarsVisible(model.VieraEarsVisible);
|
||||||
|
|
||||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||||
ret.SetCrest(slot, CrestService.GetModelCrest(actor, slot));
|
ret.SetCrest(slot, CrestService.GetModelCrest(actor, slot));
|
||||||
|
|
@ -186,7 +187,7 @@ public sealed class StateManager(
|
||||||
off = actor.GetOffhand();
|
off = actor.GetOffhand();
|
||||||
FistWeaponHack(ref ret, ref main, ref off);
|
FistWeaponHack(ref ret, ref main, ref off);
|
||||||
ret.SetVisor(actor.AsCharacter->DrawData.IsVisorToggled);
|
ret.SetVisor(actor.AsCharacter->DrawData.IsVisorToggled);
|
||||||
|
ret.SetEarsVisible(actor.ShowVieraEars);
|
||||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||||
ret.SetCrest(slot, actor.GetCrest(slot));
|
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