diff --git a/Glamourer/Interop/ChangeCustomizeService.cs b/Glamourer/Interop/ChangeCustomizeService.cs index c12f608..73c9089 100644 --- a/Glamourer/Interop/ChangeCustomizeService.cs +++ b/Glamourer/Interop/ChangeCustomizeService.cs @@ -6,6 +6,7 @@ using Glamourer.Customization; using Glamourer.Events; using Glamourer.Interop.Structs; using OtterGui.Classes; +using Penumbra.String.Functions; using CustomizeData = Penumbra.GameData.Structs.CustomizeData; namespace Glamourer.Interop; @@ -74,9 +75,7 @@ public unsafe class ChangeCustomizeService : EventWrapper(new Customize(*(CustomizeData*)data)); Invoke(this, (Model)human, customize); - fixed (byte* ptr = customize.Value.Data.Data) - { - return _changeCustomizeHook.Original(human, ptr, skipEquipment); - } + ((Customize*)data)->Load(customize.Value); + return _changeCustomizeHook.Original(human, data, skipEquipment); } } diff --git a/Glamourer/Interop/Structs/Actor.cs b/Glamourer/Interop/Structs/Actor.cs index 58668f4..54d0a36 100644 --- a/Glamourer/Interop/Structs/Actor.cs +++ b/Glamourer/Interop/Structs/Actor.cs @@ -43,7 +43,7 @@ public readonly unsafe struct Actor : IEquatable => actor.Address; public bool IsGPoseOrCutscene - => Index is >= (int)ScreenActor.CutsceneStart and < (int)ScreenActor.CutsceneEnd; + => Index.Index is >= (int)ScreenActor.CutsceneStart and < (int)ScreenActor.CutsceneEnd; public ActorIdentifier GetIdentifier(ActorManager actors) => actors.FromObject(AsObject, out _, true, true, false); @@ -63,8 +63,8 @@ public readonly unsafe struct Actor : IEquatable return false; } - public int Index - => Valid ? AsObject->ObjectIndex : -1; + public ObjectIndex Index + => Valid ? AsObject->ObjectIndex : ObjectIndex.AnyIndex; public Model Model => Valid ? AsObject->DrawObject : null; diff --git a/Glamourer/Interop/UpdateSlotService.cs b/Glamourer/Interop/UpdateSlotService.cs index 916b6fe..8d0c0eb 100644 --- a/Glamourer/Interop/UpdateSlotService.cs +++ b/Glamourer/Interop/UpdateSlotService.cs @@ -51,7 +51,7 @@ public unsafe class UpdateSlotService : IDisposable var slot = slotIdx.ToEquipSlot(); var returnValue = ulong.MaxValue; SlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue); - Glamourer.Log.Excessive($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue})."); + Glamourer.Log.Excessive($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X})."); return returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue; } diff --git a/Glamourer/Services/DalamudServices.cs b/Glamourer/Services/DalamudServices.cs index d8e5914..7872db1 100644 --- a/Glamourer/Services/DalamudServices.cs +++ b/Glamourer/Services/DalamudServices.cs @@ -1,4 +1,5 @@ using Dalamud.Game; +using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Keys; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Gui; @@ -23,6 +24,7 @@ public class DalamudServices services.AddSingleton(Commands); services.AddSingleton(GameData); services.AddSingleton(ClientState); + services.AddSingleton(Condition); services.AddSingleton(GameGui); services.AddSingleton(Chat); services.AddSingleton(Framework); @@ -40,6 +42,7 @@ public class DalamudServices [PluginService][RequiredVersion("1.0")] public ICommandManager Commands { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IDataManager GameData { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IClientState ClientState { get; private set; } = null!; + [PluginService][RequiredVersion("1.0")] public Condition Condition { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IGameGui GameGui { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public ChatGui Chat { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public Framework Framework { get; private set; } = null!; diff --git a/Glamourer/State/FunModule.cs b/Glamourer/State/FunModule.cs index 4da7792..fef1ca4 100644 --- a/Glamourer/State/FunModule.cs +++ b/Glamourer/State/FunModule.cs @@ -35,7 +35,7 @@ public unsafe class FunModule if (actor.AsCharacter->CharacterData.ModelCharaId != 0) return; - ApplyEmperor(new Span(ref armor)); + ApplyEmperor(new Span(ref armor), slot); ApplyClown(new Span(ref armor)); } diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 01d68c9..fec1a34 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -11,6 +11,7 @@ using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using System; +using Dalamud.Game.ClientState.Conditions; namespace Glamourer.State; @@ -40,6 +41,7 @@ public class StateListener : IDisposable private readonly MovedEquipment _movedEquipment; private readonly GPoseService _gPose; private readonly ChangeCustomizeService _changeCustomizeService; + private readonly Condition _condition; private ActorIdentifier _creatingIdentifier = ActorIdentifier.Invalid; private ActorState? _creatingState; @@ -55,7 +57,7 @@ public class StateListener : IDisposable SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose, - ChangeCustomizeService changeCustomizeService, CustomizationService customizations) + ChangeCustomizeService changeCustomizeService, CustomizationService customizations, Condition condition) { _manager = manager; _items = items; @@ -76,6 +78,7 @@ public class StateListener : IDisposable _gPose = gPose; _changeCustomizeService = changeCustomizeService; _customizations = customizations; + _condition = condition; if (Enabled) Subscribe(); @@ -123,6 +126,9 @@ public class StateListener : IDisposable private unsafe void OnCreatingCharacterBase(nint actorPtr, string _, nint modelPtr, nint customizePtr, nint equipDataPtr) { var actor = (Actor)actorPtr; + if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) + return; + _creatingIdentifier = actor.GetIdentifier(_actors.AwaitedService); ref var modelId = ref *(uint*)modelPtr; @@ -154,7 +160,7 @@ public class StateListener : IDisposable private unsafe void OnCustomizeChange(Model model, Ref customize) { - if (!model.IsHuman) + if (_condition[ConditionFlag.CreatingCharacter] || !model.IsHuman) return; var actor = _penumbra.GameObjectFromDrawObject(model); @@ -206,6 +212,9 @@ public class StateListener : IDisposable /// private void OnSlotUpdating(Model model, EquipSlot slot, Ref armor, Ref returnValue) { + if (_condition[ConditionFlag.CreatingCharacter]) + return; + var actor = _penumbra.GameObjectFromDrawObject(model); if (actor.Identifier(_actors.AwaitedService, out var identifier) && _manager.TryGetValue(identifier, out var state)) @@ -259,6 +268,9 @@ public class StateListener : IDisposable /// private void OnWeaponLoading(Actor actor, EquipSlot slot, Ref weapon) { + if (_condition[ConditionFlag.CreatingCharacter]) + return; + // Fist weapon gauntlet hack. if (slot is EquipSlot.OffHand && weapon.Value.Variant == 0 && weapon.Value.Set.Id != 0 && _lastFistOffhand.Set.Id != 0) weapon.Value = _lastFistOffhand; @@ -448,6 +460,9 @@ public class StateListener : IDisposable /// Handle visor state changes made by the game. private void OnVisorChange(Model model, Ref value) { + if (_condition[ConditionFlag.CreatingCharacter]) + return; + // Find appropriate actor and state. // We do not need to handle fixed designs, // since a fixed design would already have established state-tracking. @@ -478,6 +493,9 @@ public class StateListener : IDisposable /// Handle Hat Visibility changes. These act on the game object. private void OnHeadGearVisibilityChange(Actor actor, Ref value) { + if (_condition[ConditionFlag.CreatingCharacter]) + return; + // Find appropriate state. // We do not need to handle fixed designs, // if there is no model that caused a fixed design to exist yet, @@ -508,6 +526,9 @@ public class StateListener : IDisposable /// Handle Weapon Visibility changes. These act on the game object. private void OnWeaponVisibilityChange(Actor actor, Ref value) { + if (_condition[ConditionFlag.CreatingCharacter]) + return; + // Find appropriate state. // We do not need to handle fixed designs, // if there is no model that caused a fixed design to exist yet, @@ -579,6 +600,9 @@ public class StateListener : IDisposable private void OnCreatedCharacterBase(nint gameObject, string _, nint drawObject) { + if (_condition[ConditionFlag.CreatingCharacter]) + return; + if (_creatingState == null) return;