diff --git a/Glamourer/Designs/DesignData.cs b/Glamourer/Designs/DesignData.cs index 9bd9447..d870105 100644 --- a/Glamourer/Designs/DesignData.cs +++ b/Glamourer/Designs/DesignData.cs @@ -77,7 +77,7 @@ public unsafe struct DesignData fixed (byte* ptr = _equipmentBytes) { var armorPtr = (CharacterArmor*)ptr; - return armorPtr[slot is EquipSlot.MainHand ? 10 : 11].ToWeapon(_secondaryMainhand); + return slot is EquipSlot.MainHand ? armorPtr[10].ToWeapon(_secondaryMainhand) : armorPtr[11].ToWeapon(_secondaryOffhand); } } diff --git a/Glamourer/Gui/Tabs/DebugTab.cs b/Glamourer/Gui/Tabs/DebugTab.cs index 03af378..ada210e 100644 --- a/Glamourer/Gui/Tabs/DebugTab.cs +++ b/Glamourer/Gui/Tabs/DebugTab.cs @@ -289,7 +289,7 @@ public unsafe class DebugTab : ITab ? actor.AsCharacter->DrawData.IsWeaponHidden ? "Hidden" : "Visible" : "No Character"); var text = string.Empty; - // TODO + if (!model.IsHuman) { text = "No Model"; @@ -1142,6 +1142,11 @@ public unsafe class DebugTab : ITab _objectManager.Update(); foreach (var (identifier, actors) in _objectManager) { + if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.Trash.ToIconString()}##{actors.Label}", new Vector2(ImGui.GetFrameHeight()), + string.Empty, !_state.ContainsKey(identifier), true)) + _state.DeleteState(identifier); + + ImGui.SameLine(); using var t = ImRaii.TreeNode(actors.Label); if (!t) continue; diff --git a/Glamourer/Interop/WeaponService.cs b/Glamourer/Interop/WeaponService.cs index 70c386e..f52e565 100644 --- a/Glamourer/Interop/WeaponService.cs +++ b/Glamourer/Interop/WeaponService.cs @@ -4,8 +4,10 @@ using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Glamourer.Events; using Glamourer.Interop.Structs; +using ImGuiNET; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; +using static FFXIVClientStructs.FFXIV.Client.UI.UIModule; namespace Glamourer.Interop; @@ -69,26 +71,19 @@ public unsafe class WeaponService : IDisposable switch (slot) { case EquipSlot.MainHand: - LoadWeaponDetour(&character.AsCharacter->DrawData, 0, weapon.Value, 0, 0, 1, 0); + _loadWeaponHook.Original(&character.AsCharacter->DrawData, 0, weapon.Value, 0, 0, 1, 0); return; case EquipSlot.OffHand: - LoadWeaponDetour(&character.AsCharacter->DrawData, 1, weapon.Value, 0, 0, 1, 0); + _loadWeaponHook.Original(&character.AsCharacter->DrawData, 1, weapon.Value, 0, 0, 1, 0); return; case EquipSlot.BothHand: - LoadWeaponDetour(&character.AsCharacter->DrawData, 0, weapon.Value, 0, 0, 1, 0); - LoadWeaponDetour(&character.AsCharacter->DrawData, 1, CharacterWeapon.Empty.Value, 0, 0, 1, 0); + _loadWeaponHook.Original(&character.AsCharacter->DrawData, 0, weapon.Value, 0, 0, 1, 0); + _loadWeaponHook.Original(&character.AsCharacter->DrawData, 1, CharacterWeapon.Empty.Value, 0, 0, 1, 0); return; // function can also be called with '2', but does not seem to ever be. } } - // Load specific Main- and Offhand weapons. - public void LoadWeapon(Actor character, CharacterWeapon main, CharacterWeapon off) - { - LoadWeaponDetour(&character.AsCharacter->DrawData, 0, main.Value, 1, 0, 1, 0); - LoadWeaponDetour(&character.AsCharacter->DrawData, 1, off.Value, 1, 0, 1, 0); - } - public void LoadStain(Actor character, EquipSlot slot, StainId stain) { var mdl = character.Model; diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index bb7fa1b..08ad5d2 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -312,10 +312,11 @@ public class StateListener : IDisposable return UpdateState.NoChange; // Model ID did change, reload entire state accordingly. + // Always use the actor for the base data. if (modelId == 0) state.BaseData.LoadNonHuman(modelId, *(Customize*)customizeData, (byte*)equipData); else - state.BaseData = _manager.FromActor(actor); + state.BaseData = _manager.FromActor(actor, false); return UpdateState.Change; } @@ -334,7 +335,7 @@ public class StateListener : IDisposable // Customize array did not change to stored state. if (state.BaseData.Customize.Equals(customize)) - return UpdateState.NoChange; + return UpdateState.NoChange; // TODO: handle wrong base data. // Update customize base state. state.BaseData.Customize.Load(customize); diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index a87a21b..4093f05 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -75,12 +75,12 @@ public class StateManager : IReadOnlyDictionary try { - var designData = FromActor(actor); - // Initial Creation has identical base and model data. + // Initial Creation, use the actors data for the base data, + // and the draw objects data for the model data (where possible). state = new ActorState(identifier) { - ModelData = designData, - BaseData = designData, + ModelData = FromActor(actor, true), + BaseData = FromActor(actor, false), }; // state.Identifier is owned. _states.Add(state.Identifier, state); @@ -98,7 +98,7 @@ public class StateManager : IReadOnlyDictionary /// This uses the draw object if available and where possible, /// and the game object where necessary. /// - public unsafe DesignData FromActor(Actor actor) + public unsafe DesignData FromActor(Actor actor, bool useModel) { var ret = new DesignData(); // If the given actor is not a character, just return a default character. @@ -110,7 +110,7 @@ public class StateManager : IReadOnlyDictionary // Model ID is only unambiguously contained in the game object. // The draw object only has the object type. - // TODO do this right. + // TODO reverse search model data to get model id from model. if (actor.AsCharacter->CharacterData.ModelCharaId != 0) { ret.LoadNonHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId, *(Customize*)&actor.AsCharacter->DrawData.CustomizeData, @@ -127,7 +127,7 @@ public class StateManager : IReadOnlyDictionary ret.SetHatVisible(!actor.AsCharacter->DrawData.IsHatHidden); // Use the draw object if it is a human. - if (model.IsHuman) + if (useModel && model.IsHuman) { // Customize can be obtained from the draw object. ret.Customize = model.GetCustomize(); @@ -484,4 +484,7 @@ public class StateManager : IReadOnlyDictionary _editor.ChangeHatState(data, state.ModelData.IsHatVisible()); _editor.ChangeVisor(data, state.ModelData.IsVisorToggled()); } + + public void DeleteState(ActorIdentifier identifier) + => _states.Remove(identifier); }