diff --git a/Glamourer/Designs/DesignData.cs b/Glamourer/Designs/DesignData.cs index 935f80b..7570b11 100644 --- a/Glamourer/Designs/DesignData.cs +++ b/Glamourer/Designs/DesignData.cs @@ -205,15 +205,14 @@ public unsafe struct DesignData } - public bool LoadNonHuman(uint modelId, Customize customize, byte* equipData) + public bool LoadNonHuman(uint modelId, Customize customize, nint equipData) { ModelId = modelId; IsHuman = false; Customize.Load(customize); fixed (byte* ptr = _equipmentBytes) { - MemoryUtility.MemCpyUnchecked(ptr, equipData, 40); - MemoryUtility.MemSet(ptr + 40, 0, 8); + MemoryUtility.MemCpyUnchecked(ptr, (byte*) equipData, 40); } SetHatVisible(true); @@ -221,19 +220,14 @@ public unsafe struct DesignData SetVisor(false); fixed (uint* ptr = _itemIds) { - MemoryUtility.MemSet(ptr, 0, 12 * 4); + MemoryUtility.MemSet(ptr, 0, 10 * 4); } fixed (ushort* ptr = _iconIds) { - MemoryUtility.MemSet(ptr, 0, 12 * 2); + MemoryUtility.MemSet(ptr, 0, 10 * 2); } - _secondaryMainhand = 0; - _secondaryOffhand = 0; - _typeMainhand = FullEquipType.Unknown; - _typeOffhand = FullEquipType.Unknown; - _nameHead = string.Empty; _nameBody = string.Empty; _nameHands = string.Empty; @@ -244,8 +238,6 @@ public unsafe struct DesignData _nameWrists = string.Empty; _nameRFinger = string.Empty; _nameLFinger = string.Empty; - _nameMainhand = string.Empty; - _nameOffhand = string.Empty; return true; } diff --git a/Glamourer/Designs/DesignManager.cs b/Glamourer/Designs/DesignManager.cs index 5560357..5c58671 100644 --- a/Glamourer/Designs/DesignManager.cs +++ b/Glamourer/Designs/DesignManager.cs @@ -13,6 +13,7 @@ using Newtonsoft.Json.Linq; using OtterGui; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; +using static OtterGui.Raii.ImRaii; namespace Glamourer.Designs; @@ -259,6 +260,28 @@ public class DesignManager _event.Invoke(DesignChanged.Type.WriteProtection, design, value); } + public void ChangeModelId(Design design, uint modelId, Customize customize, nint equipData, bool isHuman) + { + var oldValue = design.DesignData.ModelId; + + if (!isHuman) + { + design.DesignData.LoadNonHuman(modelId, customize, equipData); + } + else if (!design.DesignData.IsHuman) + { + design.DesignData.IsHuman = true; + design.DesignData.ModelId = modelId; + design.DesignData.SetDefaultEquipment(_items); + design.DesignData.Customize = Customize.Default; + } + + design.LastEdit = DateTimeOffset.UtcNow; + Glamourer.Log.Debug($"Changed model id in design {design.Identifier} from {oldValue} to {modelId}."); + _saveService.QueueSave(design); + _event.Invoke(DesignChanged.Type.ModelId, design, (oldValue, modelId)); + } + /// Change a customization value. public void ChangeCustomize(Design design, CustomizeIndex idx, CustomizeValue value) { @@ -454,6 +477,36 @@ public class DesignManager /// Apply an entire design based on its appliance rules piece by piece. public void ApplyDesign(Design design, DesignBase other) { + ChangeModelId(design, other.DesignData.ModelId, other.DesignData.Customize, other.DesignData.GetEquipmentPtr(), + other.DesignData.IsHuman); + + if (other.DoApplyWetness()) + design.DesignData.SetIsWet(other.DesignData.IsWet()); + if (other.DoApplyHatVisible()) + design.DesignData.SetHatVisible(other.DesignData.IsHatVisible()); + if (other.DoApplyVisorToggle()) + design.DesignData.SetVisor(other.DesignData.IsVisorToggled()); + if (other.DoApplyWeaponVisible()) + design.DesignData.SetWeaponVisible(other.DesignData.IsWeaponVisible()); + + if (design.DesignData.IsHuman) + { + foreach (var index in Enum.GetValues()) + { + if (other.DoApplyCustomize(index)) + ChangeCustomize(design, index, other.DesignData.Customize[index]); + } + + foreach (var slot in EquipSlotExtensions.EqdpSlots) + { + if (other.DoApplyEquip(slot)) + ChangeEquip(design, slot, other.DesignData.Item(slot)); + + if (other.DoApplyStain(slot)) + ChangeStain(design, slot, other.DesignData.Stain(slot)); + } + } + if (other.DoApplyEquip(EquipSlot.MainHand)) ChangeWeapon(design, EquipSlot.MainHand, other.DesignData.Item(EquipSlot.MainHand)); @@ -465,31 +518,6 @@ public class DesignManager if (other.DoApplyStain(EquipSlot.OffHand)) ChangeStain(design, EquipSlot.OffHand, other.DesignData.Stain(EquipSlot.OffHand)); - - - foreach (var slot in EquipSlotExtensions.EqdpSlots) - { - if (other.DoApplyEquip(slot)) - ChangeEquip(design, slot, other.DesignData.Item(slot)); - - if (other.DoApplyStain(slot)) - ChangeStain(design, slot, other.DesignData.Stain(slot)); - } - - foreach (var index in Enum.GetValues()) - { - if (other.DoApplyCustomize(index)) - ChangeCustomize(design, index, other.DesignData.Customize[index]); - } - - if (other.DoApplyHatVisible()) - design.DesignData.SetHatVisible(other.DesignData.IsHatVisible()); - if (other.DoApplyVisorToggle()) - design.DesignData.SetVisor(other.DesignData.IsVisorToggled()); - if (other.DoApplyWeaponVisible()) - design.DesignData.SetWeaponVisible(other.DesignData.IsWeaponVisible()); - if (other.DoApplyWetness()) - design.DesignData.SetIsWet(other.DesignData.IsWet()); } private void MigrateOldDesigns() diff --git a/Glamourer/Events/DesignChanged.cs b/Glamourer/Events/DesignChanged.cs index 154880a..b36af52 100644 --- a/Glamourer/Events/DesignChanged.cs +++ b/Glamourer/Events/DesignChanged.cs @@ -46,6 +46,9 @@ public sealed class DesignChanged : EventWrapper An existing design had an existing associated mod removed. Data is the Mod and its Settings [(Mod, ModSettings)]. RemovedMod, + /// An existing design had its model id changed. This means everything else might also have changed. Data is the old value and the new value. [(uint, uint)]. + ModelId, + /// An existing design had a customization changed. Data is the old value, the new value and the type [(CustomizeValue, CustomizeValue, CustomizeIndex)]. Customize, diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index 6f8cba0..789e027 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -65,11 +65,8 @@ public class StateEditor } else { - unsafe - { - state.ModelData.LoadNonHuman(modelId, customize, (byte*)equipData); - state[ActorState.MetaIndex.ModelId] = source; - } + state.ModelData.LoadNonHuman(modelId, customize, equipData); + state[ActorState.MetaIndex.ModelId] = source; } return true; diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 5c4208c..9353849 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -327,7 +327,7 @@ public class StateListener : IDisposable if (isHuman) state.BaseData = _manager.FromActor(actor, false); else - state.BaseData.LoadNonHuman(modelId, *(Customize*)customizeData, (byte*)equipData); + state.BaseData.LoadNonHuman(modelId, *(Customize*)customizeData, equipData); return UpdateState.Change; } diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index 4f67764..37becae 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -116,7 +116,7 @@ public class StateManager : IReadOnlyDictionary if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId)) { ret.LoadNonHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId, *(Customize*)&actor.AsCharacter->DrawData.CustomizeData, - (byte*)&actor.AsCharacter->DrawData.Head); + (nint) (&actor.AsCharacter->DrawData.Head)); return ret; }