mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-15 05:04:16 +01:00
.
This commit is contained in:
parent
803fd1b247
commit
1aba34f34a
11 changed files with 631 additions and 32 deletions
318
Glamourer/Api/GlamourerIpc.cs
Normal file
318
Glamourer/Api/GlamourerIpc.cs
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
using System;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.Api.Enums;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
public partial class Glamourer
|
||||
{
|
||||
public class GlamourerIpc : IDisposable
|
||||
{
|
||||
public const int CurrentApiVersion = 0;
|
||||
public const string LabelProviderApiVersion = "Glamourer.ApiVersion";
|
||||
public const string LabelProviderGetAllCustomization = "Glamourer.GetAllCustomization";
|
||||
public const string LabelProviderGetAllCustomizationFromCharacter = "Glamourer.GetAllCustomizationFromCharacter";
|
||||
public const string LabelProviderApplyAll = "Glamourer.ApplyAll";
|
||||
public const string LabelProviderApplyAllToCharacter = "Glamourer.ApplyAllToCharacter";
|
||||
public const string LabelProviderApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment";
|
||||
public const string LabelProviderApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter";
|
||||
public const string LabelProviderApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization";
|
||||
public const string LabelProviderApplyOnlyCustomizationToCharacter = "Glamourer.ApplyOnlyCustomizationToCharacter";
|
||||
public const string LabelProviderRevert = "Glamourer.Revert";
|
||||
public const string LabelProviderRevertCharacter = "Glamourer.RevertCharacter";
|
||||
|
||||
private readonly ObjectTable _objectTable;
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly ActiveDesign.Manager _stateManager;
|
||||
private readonly ItemManager _items;
|
||||
private readonly PenumbraAttach _penumbra;
|
||||
private readonly ActorService _actors;
|
||||
|
||||
internal ICallGateProvider<string, string?>? ProviderGetAllCustomization;
|
||||
internal ICallGateProvider<Character?, string?>? ProviderGetAllCustomizationFromCharacter;
|
||||
internal ICallGateProvider<string, string, object>? ProviderApplyAll;
|
||||
internal ICallGateProvider<string, Character?, object>? ProviderApplyAllToCharacter;
|
||||
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyCustomization;
|
||||
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyCustomizationToCharacter;
|
||||
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyEquipment;
|
||||
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyEquipmentToCharacter;
|
||||
internal ICallGateProvider<string, object>? ProviderRevert;
|
||||
internal ICallGateProvider<Character?, object>? ProviderRevertCharacter;
|
||||
internal ICallGateProvider<int>? ProviderGetApiVersion;
|
||||
|
||||
public GlamourerIpc(ObjectTable objectTable, DalamudPluginInterface pluginInterface, ActiveDesign.Manager stateManager,
|
||||
ItemManager items, PenumbraAttach penumbra, ActorService actors)
|
||||
{
|
||||
_objectTable = objectTable;
|
||||
_pluginInterface = pluginInterface;
|
||||
_stateManager = stateManager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
|
||||
InitializeProviders();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> DisposeProviders();
|
||||
|
||||
private void DisposeProviders()
|
||||
{
|
||||
ProviderGetAllCustomization?.UnregisterFunc();
|
||||
ProviderGetAllCustomizationFromCharacter?.UnregisterFunc();
|
||||
ProviderApplyAll?.UnregisterAction();
|
||||
ProviderApplyAllToCharacter?.UnregisterAction();
|
||||
ProviderApplyOnlyCustomization?.UnregisterAction();
|
||||
ProviderApplyOnlyCustomizationToCharacter?.UnregisterAction();
|
||||
ProviderApplyOnlyEquipment?.UnregisterAction();
|
||||
ProviderApplyOnlyEquipmentToCharacter?.UnregisterAction();
|
||||
ProviderRevert?.UnregisterAction();
|
||||
ProviderRevertCharacter?.UnregisterAction();
|
||||
ProviderGetApiVersion?.UnregisterFunc();
|
||||
}
|
||||
|
||||
private void InitializeProviders()
|
||||
{
|
||||
try
|
||||
{
|
||||
ProviderGetApiVersion = _pluginInterface.GetIpcProvider<int>(LabelProviderApiVersion);
|
||||
ProviderGetApiVersion.RegisterFunc(GetApiVersion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApiVersion}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderGetAllCustomization = _pluginInterface.GetIpcProvider<string, string?>(LabelProviderGetAllCustomization);
|
||||
ProviderGetAllCustomization.RegisterFunc(GetAllCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderGetAllCustomizationFromCharacter =
|
||||
_pluginInterface.GetIpcProvider<Character?, string?>(LabelProviderGetAllCustomizationFromCharacter);
|
||||
ProviderGetAllCustomizationFromCharacter.RegisterFunc(GetAllCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderGetAllCustomizationFromCharacter}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyAll =
|
||||
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyAll);
|
||||
ProviderApplyAll.RegisterAction(ApplyAll);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyAllToCharacter =
|
||||
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyAllToCharacter);
|
||||
ProviderApplyAllToCharacter.RegisterAction(ApplyAll);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyCustomization =
|
||||
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyCustomization);
|
||||
ProviderApplyOnlyCustomization.RegisterAction(ApplyOnlyCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyCustomizationToCharacter =
|
||||
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyCustomizationToCharacter);
|
||||
ProviderApplyOnlyCustomizationToCharacter.RegisterAction(ApplyOnlyCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyEquipment =
|
||||
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyEquipment);
|
||||
ProviderApplyOnlyEquipment.RegisterAction(ApplyOnlyEquipment);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyEquipmentToCharacter =
|
||||
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyEquipmentToCharacter);
|
||||
ProviderApplyOnlyEquipmentToCharacter.RegisterAction(ApplyOnlyEquipment);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderRevert =
|
||||
_pluginInterface.GetIpcProvider<string, object>(LabelProviderRevert);
|
||||
ProviderRevert.RegisterAction(Revert);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderRevertCharacter =
|
||||
_pluginInterface.GetIpcProvider<Character?, object>(LabelProviderRevertCharacter);
|
||||
ProviderRevertCharacter.RegisterAction(Revert);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetApiVersion()
|
||||
=> CurrentApiVersion;
|
||||
|
||||
private void ApplyAll(string customization, string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() == characterName)
|
||||
{
|
||||
ApplyAll(customization, gameObject as Character);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyAll(string customization, Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var design = Design.CreateTemporaryFromBase64(_items, customization, true, true);
|
||||
var active = _stateManager.GetOrCreateSave(character.Address);
|
||||
_stateManager.ApplyDesign(active, design, false);
|
||||
}
|
||||
|
||||
private void ApplyOnlyCustomization(string customization, string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() == characterName)
|
||||
{
|
||||
ApplyOnlyCustomization(customization, gameObject as Character);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyOnlyCustomization(string customization, Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var design = Design.CreateTemporaryFromBase64(_items, customization, true, false);
|
||||
var active = _stateManager.GetOrCreateSave(character.Address);
|
||||
_stateManager.ApplyDesign(active, design, false);
|
||||
}
|
||||
|
||||
private void ApplyOnlyEquipment(string customization, string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() != characterName)
|
||||
continue;
|
||||
|
||||
ApplyOnlyEquipment(customization, gameObject as Character);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyOnlyEquipment(string customization, Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var design = Design.CreateTemporaryFromBase64(_items, customization, false, true);
|
||||
var active = _stateManager.GetOrCreateSave(character.Address);
|
||||
_stateManager.ApplyDesign(active, design, false);
|
||||
}
|
||||
|
||||
private void Revert(string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() != characterName)
|
||||
continue;
|
||||
|
||||
Revert(gameObject as Character);
|
||||
}
|
||||
}
|
||||
|
||||
private void Revert(Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var ident = _actors.AwaitedService.FromObject(character, true, false, false);
|
||||
_stateManager.DeleteSave(ident);
|
||||
_penumbra.RedrawObject(character.Address, RedrawType.Redraw);
|
||||
}
|
||||
|
||||
private string? GetAllCustomization(Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return null;
|
||||
|
||||
var ident = _actors.AwaitedService.FromObject(character, true, false, false);
|
||||
if (!_stateManager.TryGetValue(ident, out var design))
|
||||
design = new ActiveDesign(_items, ident, character.Address);
|
||||
|
||||
return design.CreateOldBase64();
|
||||
}
|
||||
|
||||
private string? GetAllCustomization(string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() == characterName)
|
||||
return GetAllCustomization(gameObject as Character);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Glamourer/Events/HeadGearVisibilityChanged.cs
Normal file
32
Glamourer/Events/HeadGearVisibilityChanged.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using Glamourer.Interop.Structs;
|
||||
using OtterGui.Classes;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the visibility of head gear is changed.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the actor with changed head gear visibility. </item>
|
||||
/// <item>Parameter is the new state. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class HeadGearVisibilityChanged : EventWrapper<Action<Actor, Ref<bool>>, HeadGearVisibilityChanged.Priority>
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnHeadGearVisibilityChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public HeadGearVisibilityChanged()
|
||||
: base(nameof(HeadGearVisibilityChanged))
|
||||
{ }
|
||||
|
||||
public void Invoke(Actor actor, ref bool state)
|
||||
{
|
||||
var value = new Ref<bool>(state);
|
||||
Invoke(this, actor, value);
|
||||
state = value;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,21 +12,22 @@ namespace Glamourer.Events;
|
|||
/// <item>Parameter is whether to call the original function. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class VisorStateChanged : EventWrapper<Action<Model, Ref<bool>, Ref<bool>>, VisorStateChanged.Priority>
|
||||
public sealed class VisorStateChanged : EventWrapper<Action<Model, Ref<bool>>, VisorStateChanged.Priority>
|
||||
{
|
||||
public enum Priority
|
||||
{ }
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnVisorChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public VisorStateChanged()
|
||||
: base(nameof(VisorStateChanged))
|
||||
{ }
|
||||
|
||||
public void Invoke(Model model, ref bool state, ref bool callOriginal)
|
||||
public void Invoke(Model model, ref bool state)
|
||||
{
|
||||
var value = new Ref<bool>(state);
|
||||
var original = new Ref<bool>(callOriginal);
|
||||
Invoke(this, model, value, original);
|
||||
Invoke(this, model, value);
|
||||
state = value;
|
||||
callOriginal = original;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
Glamourer/Events/WeaponVisibilityChanged.cs
Normal file
32
Glamourer/Events/WeaponVisibilityChanged.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using Glamourer.Interop.Structs;
|
||||
using OtterGui.Classes;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the visibility of weapons is changed.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the actor with changed weapon visibility. </item>
|
||||
/// <item>Parameter is the new state. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class WeaponVisibilityChanged : EventWrapper<Action<Actor, Ref<bool>>, WeaponVisibilityChanged.Priority>
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnWeaponVisibilityChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public WeaponVisibilityChanged()
|
||||
: base(nameof(WeaponVisibilityChanged))
|
||||
{ }
|
||||
|
||||
public void Invoke(Actor actor, ref bool state)
|
||||
{
|
||||
var value = new Ref<bool>(state);
|
||||
Invoke(this, actor, value);
|
||||
state = value;
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ public unsafe class DebugTab : ITab
|
|||
private readonly ChangeCustomizeService _changeCustomizeService;
|
||||
private readonly UpdateSlotService _updateSlotService;
|
||||
private readonly WeaponService _weaponService;
|
||||
private readonly MetaService _metaService;
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly ObjectTable _objects;
|
||||
private readonly ObjectManager _objectManager;
|
||||
|
|
@ -56,7 +57,7 @@ public unsafe class DebugTab : ITab
|
|||
UpdateSlotService updateSlotService, WeaponService weaponService, PenumbraService penumbra,
|
||||
ActorService actors, ItemManager items, CustomizationService customization, ObjectManager objectManager,
|
||||
DesignFileSystem designFileSystem, DesignManager designManager, StateManager state, Configuration config,
|
||||
PenumbraChangedItemTooltip penumbraTooltip)
|
||||
PenumbraChangedItemTooltip penumbraTooltip, MetaService metaService)
|
||||
{
|
||||
_changeCustomizeService = changeCustomizeService;
|
||||
_visorService = visorService;
|
||||
|
|
@ -73,6 +74,7 @@ public unsafe class DebugTab : ITab
|
|||
_state = state;
|
||||
_config = config;
|
||||
_penumbraTooltip = penumbraTooltip;
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
|
|
|
|||
72
Glamourer/Interop/MetaService.cs
Normal file
72
Glamourer/Interop/MetaService.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop.Structs;
|
||||
using static OtterGui.Raii.ImRaii;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe class MetaService : IDisposable
|
||||
{
|
||||
private readonly HeadGearVisibilityChanged _headGearEvent;
|
||||
private readonly WeaponVisibilityChanged _weaponEvent;
|
||||
|
||||
private delegate void HideHatGearDelegate(DrawDataContainer* drawData, uint id, byte value);
|
||||
private delegate void HideWeaponsDelegate(DrawDataContainer* drawData, bool value);
|
||||
|
||||
private readonly Hook<HideHatGearDelegate> _hideHatGearHook;
|
||||
private readonly Hook<HideWeaponsDelegate> _hideWeaponsHook;
|
||||
|
||||
public MetaService(WeaponVisibilityChanged weaponEvent, HeadGearVisibilityChanged headGearEvent)
|
||||
{
|
||||
_weaponEvent = weaponEvent;
|
||||
_headGearEvent = headGearEvent;
|
||||
_hideHatGearHook = Hook<HideHatGearDelegate>.FromAddress((nint)DrawDataContainer.MemberFunctionPointers.HideHeadgear, HideHatDetour);
|
||||
_hideWeaponsHook = Hook<HideWeaponsDelegate>.FromAddress((nint)DrawDataContainer.MemberFunctionPointers.HideWeapons, HideWeaponsDetour);
|
||||
_hideHatGearHook.Enable();
|
||||
_hideWeaponsHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_hideHatGearHook.Dispose();
|
||||
_hideWeaponsHook.Dispose();
|
||||
}
|
||||
|
||||
public void SetHatState(Actor actor, bool value)
|
||||
{
|
||||
if (!actor.IsCharacter)
|
||||
return;
|
||||
|
||||
_hideHatGearHook.Original(&actor.AsCharacter->DrawData, 0, (byte) (value ? 1 : 0));
|
||||
}
|
||||
|
||||
public void SetWeaponState(Actor actor, bool value)
|
||||
{
|
||||
if (!actor.IsCharacter)
|
||||
return;
|
||||
|
||||
_hideWeaponsHook.Original(&actor.AsCharacter->DrawData, value);
|
||||
}
|
||||
|
||||
private void HideHatDetour(DrawDataContainer* drawData, uint id, byte value)
|
||||
{
|
||||
Actor actor = drawData->Parent;
|
||||
var v = value == 0;
|
||||
_headGearEvent.Invoke(actor, ref v);
|
||||
value = (byte) (v ? 0 : 1);
|
||||
Glamourer.Log.Information($"[MetaService] Hide Hat triggered with 0x{(nint)drawData:X} {id} {value} for {actor.Utf8Name}.");
|
||||
_hideHatGearHook.Original(drawData, id, value);
|
||||
}
|
||||
|
||||
private void HideWeaponsDetour(DrawDataContainer* drawData, bool value)
|
||||
{
|
||||
Actor actor = drawData->Parent;
|
||||
value = !value;
|
||||
_weaponEvent.Invoke(actor, ref value);
|
||||
value = !value;
|
||||
Glamourer.Log.Information($"[MetaService] Hide Weapon triggered with 0x{(nint)drawData:X} {value} for {actor.Utf8Name}.");
|
||||
_hideWeaponsHook.Original(drawData, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ using Dalamud.Hooking;
|
|||
using Dalamud.Utility.Signatures;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ public class VisorService : IDisposable
|
|||
if (oldState == on)
|
||||
return false;
|
||||
|
||||
SetupVisorHook(human, (ushort)human.AsHuman->HeadSetID, on);
|
||||
SetupVisorHook(human, human.GetArmor(EquipSlot.Head).Set.Value, on);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -50,17 +51,15 @@ public class VisorService : IDisposable
|
|||
|
||||
private void SetupVisorDetour(nint human, ushort modelId, bool on)
|
||||
{
|
||||
var callOriginal = true;
|
||||
var originalOn = on;
|
||||
// Invoke an event that can change the requested value
|
||||
// and also control whether the function should be called at all.
|
||||
Event.Invoke(human, ref on, ref callOriginal);
|
||||
Event.Invoke(human, ref on);
|
||||
|
||||
Glamourer.Log.Excessive(
|
||||
$"[SetVisorState] Invoked from game on 0x{human:X} switching to {on} (original {originalOn}, call original {callOriginal}).");
|
||||
$"[SetVisorState] Invoked from game on 0x{human:X} switching to {on} (original {originalOn}).");
|
||||
|
||||
if (callOriginal)
|
||||
SetupVisorHook(human, modelId, on);
|
||||
SetupVisorHook(human, modelId, on);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -92,4 +92,4 @@ public unsafe class WeaponService : IDisposable
|
|||
var weapon = new CharacterWeapon(value.Value) { Stain = stain.Value };
|
||||
LoadWeapon(character, slot, weapon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,9 @@ public static class ServiceManager
|
|||
.AddSingleton<SlotUpdating>()
|
||||
.AddSingleton<DesignChanged>()
|
||||
.AddSingleton<StateChanged>()
|
||||
.AddSingleton<WeaponLoading>();
|
||||
.AddSingleton<WeaponLoading>()
|
||||
.AddSingleton<HeadGearVisibilityChanged>()
|
||||
.AddSingleton<WeaponVisibilityChanged>();
|
||||
|
||||
private static IServiceCollection AddData(this IServiceCollection services)
|
||||
=> services.AddSingleton<IdentifierService>()
|
||||
|
|
@ -66,6 +68,7 @@ public static class ServiceManager
|
|||
private static IServiceCollection AddInterop(this IServiceCollection services)
|
||||
=> services.AddSingleton<VisorService>()
|
||||
.AddSingleton<ChangeCustomizeService>()
|
||||
.AddSingleton<MetaService>()
|
||||
.AddSingleton<UpdateSlotService>()
|
||||
.AddSingleton<WeaponService>()
|
||||
.AddSingleton<PenumbraService>()
|
||||
|
|
|
|||
|
|
@ -12,13 +12,16 @@ namespace Glamourer.State;
|
|||
|
||||
public class StateListener : IDisposable
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly ActorService _actors;
|
||||
private readonly StateManager _manager;
|
||||
private readonly ItemManager _items;
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly SlotUpdating _slotUpdating;
|
||||
private readonly WeaponLoading _weaponLoading;
|
||||
private readonly Configuration _config;
|
||||
private readonly ActorService _actors;
|
||||
private readonly StateManager _manager;
|
||||
private readonly ItemManager _items;
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly SlotUpdating _slotUpdating;
|
||||
private readonly WeaponLoading _weaponLoading;
|
||||
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
||||
private readonly VisorStateChanged _visorState;
|
||||
private readonly WeaponVisibilityChanged _weaponVisibility;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
|
|
@ -27,15 +30,19 @@ public class StateListener : IDisposable
|
|||
}
|
||||
|
||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
||||
SlotUpdating slotUpdating, WeaponLoading weaponLoading)
|
||||
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||
HeadGearVisibilityChanged headGearVisibility)
|
||||
{
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
_config = config;
|
||||
_slotUpdating = slotUpdating;
|
||||
_weaponLoading = weaponLoading;
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
_config = config;
|
||||
_slotUpdating = slotUpdating;
|
||||
_weaponLoading = weaponLoading;
|
||||
_visorState = visorState;
|
||||
_weaponVisibility = weaponVisibility;
|
||||
_headGearVisibility = headGearVisibility;
|
||||
|
||||
if (Enabled)
|
||||
Subscribe();
|
||||
|
|
@ -253,6 +260,9 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase += OnCreatingCharacterBase;
|
||||
_slotUpdating.Subscribe(OnSlotUpdating, SlotUpdating.Priority.StateListener);
|
||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||
_headGearVisibility.Subscribe(OnHeadGearVisibilityChange, HeadGearVisibilityChanged.Priority.StateListener);
|
||||
_weaponVisibility.Subscribe(OnWeaponVisibilityChange, WeaponVisibilityChanged.Priority.StateListener);
|
||||
}
|
||||
|
||||
private void Unsubscribe()
|
||||
|
|
@ -260,17 +270,19 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase -= OnCreatingCharacterBase;
|
||||
_slotUpdating.Unsubscribe(OnSlotUpdating);
|
||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||
_visorState.Unsubscribe(OnVisorChange);
|
||||
_headGearVisibility.Unsubscribe(OnHeadGearVisibilityChange);
|
||||
_weaponVisibility.Unsubscribe(OnWeaponVisibilityChange);
|
||||
}
|
||||
|
||||
private UpdateState UpdateBaseData(Actor actor, ActorState state, EquipSlot slot, CharacterArmor armor)
|
||||
{
|
||||
var actorArmor = actor.GetArmor(slot);
|
||||
// The actor armor does not correspond to the model armor, thus the actor is transformed.
|
||||
// This also prevents it from changing values due to hat state.
|
||||
if (actorArmor.Value != armor.Value)
|
||||
return UpdateState.Transformed;
|
||||
|
||||
// TODO: Hat State.
|
||||
|
||||
var baseData = state.BaseData.Armor(slot);
|
||||
var change = UpdateState.NoChange;
|
||||
if (baseData.Stain != armor.Stain)
|
||||
|
|
@ -338,4 +350,68 @@ public class StateListener : IDisposable
|
|||
state.BaseData.Customize.Load(customize);
|
||||
return UpdateState.Change;
|
||||
}
|
||||
|
||||
private void OnVisorChange(Model model, Ref<bool> value)
|
||||
{
|
||||
var actor = _penumbra.GameObjectFromDrawObject(model);
|
||||
if (!actor.Identifier(_actors.AwaitedService, out var identifier))
|
||||
return;
|
||||
|
||||
if (!_manager.TryGetValue(identifier, out var state))
|
||||
return;
|
||||
|
||||
if (state.BaseData.SetVisor(value))
|
||||
{
|
||||
if (state[ActorState.MetaFlag.VisorState] is StateChanged.Source.Fixed)
|
||||
value.Value = state.ModelData.IsVisorToggled();
|
||||
else
|
||||
_manager.ChangeVisorState(state, value, StateChanged.Source.Game);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.Value = state.ModelData.IsVisorToggled();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHeadGearVisibilityChange(Actor actor, Ref<bool> value)
|
||||
{
|
||||
if (!actor.Identifier(_actors.AwaitedService, out var identifier))
|
||||
return;
|
||||
|
||||
if (!_manager.TryGetValue(identifier, out var state))
|
||||
return;
|
||||
|
||||
if (state.BaseData.SetHatVisible(value))
|
||||
{
|
||||
if (state[ActorState.MetaFlag.HatState] is StateChanged.Source.Fixed)
|
||||
value.Value = state.ModelData.IsHatVisible();
|
||||
else
|
||||
_manager.ChangeHatState(state, value, StateChanged.Source.Game);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.Value = state.ModelData.IsHatVisible();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWeaponVisibilityChange(Actor actor, Ref<bool> value)
|
||||
{
|
||||
if (!actor.Identifier(_actors.AwaitedService, out var identifier))
|
||||
return;
|
||||
|
||||
if (!_manager.TryGetValue(identifier, out var state))
|
||||
return;
|
||||
|
||||
if (state.BaseData.SetWeaponVisible(value))
|
||||
{
|
||||
if (state[ActorState.MetaFlag.WeaponState] is StateChanged.Source.Fixed)
|
||||
value.Value = state.ModelData.IsWeaponVisible();
|
||||
else
|
||||
_manager.ChangeWeaponState(state, value, StateChanged.Source.Game);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.Value = state.ModelData.IsWeaponVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Glamourer.Interop.Penumbra;
|
|||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Structs;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using OtterGui.Log;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -266,6 +267,14 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (design.DoApplyHatVisible())
|
||||
ChangeHatState(state, design.DesignData.IsHatVisible(), StateChanged.Source.Manual);
|
||||
if (design.DoApplyWeaponVisible())
|
||||
ChangeWeaponState(state, design.DesignData.IsWeaponVisible(), StateChanged.Source.Manual);
|
||||
if (design.DoApplyVisorToggle())
|
||||
ChangeVisorState(state, design.DesignData.IsVisorToggled(), StateChanged.Source.Manual);
|
||||
if (design.DoApplyWetness())
|
||||
ChangeWetness(state, design.DesignData.IsWet());
|
||||
}
|
||||
|
||||
public void ResetState(ActorState state)
|
||||
|
|
@ -335,6 +344,61 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
_event.Invoke(StateChanged.Type.Stain, source, state, objects, (old, stain, slot));
|
||||
}
|
||||
|
||||
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source)
|
||||
{
|
||||
var old = state.ModelData.IsHatVisible();
|
||||
state.ModelData.SetHatVisible(value);
|
||||
state[ActorState.MetaFlag.HatState] = source;
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
if (source is StateChanged.Source.Manual)
|
||||
_editor.ChangeHatState(objects, value);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Head Gear Visibility in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaFlag.HatState));
|
||||
}
|
||||
|
||||
public void ChangeWeaponState(ActorState state, bool value, StateChanged.Source source)
|
||||
{
|
||||
var old = state.ModelData.IsWeaponVisible();
|
||||
state.ModelData.SetWeaponVisible(value);
|
||||
state[ActorState.MetaFlag.WeaponState] = source;
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
if (source is StateChanged.Source.Manual)
|
||||
_editor.ChangeWeaponState(objects, value);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Weapon Visibility in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaFlag.WeaponState));
|
||||
}
|
||||
|
||||
public void ChangeVisorState(ActorState state, bool value, StateChanged.Source source)
|
||||
{
|
||||
var old = state.ModelData.IsVisorToggled();
|
||||
state.ModelData.SetVisor(value);
|
||||
state[ActorState.MetaFlag.VisorState] = source;
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
if (source is StateChanged.Source.Manual)
|
||||
_editor.ChangeVisor(objects, value);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Visor State in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaFlag.VisorState));
|
||||
}
|
||||
|
||||
public void ChangeWetness(ActorState state, bool value)
|
||||
{
|
||||
var old = state.ModelData.IsWet();
|
||||
state.ModelData.SetIsWet(value);
|
||||
state[ActorState.MetaFlag.Wetness] = value ? StateChanged.Source.Manual : StateChanged.Source.Game;
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
_editor.ChangeWetness(objects, value);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Wetness in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaFlag.Wetness], state, objects, (old, value, ActorState.MetaFlag.Wetness));
|
||||
}
|
||||
|
||||
//
|
||||
///// <summary> Change whether to apply a specific customize value. </summary>
|
||||
//public void ChangeApplyCustomize(Design design, CustomizeIndex idx, bool value)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue