diff --git a/Glamourer/CharacterSave.cs b/Glamourer/CharacterSave.cs index 86163f5..83dec78 100644 --- a/Glamourer/CharacterSave.cs +++ b/Glamourer/CharacterSave.cs @@ -298,6 +298,7 @@ namespace Glamourer Alpha = a.Alpha(); } + public void Apply(Character a) { Glamourer.RevertableDesigns.Add(a); @@ -323,6 +324,30 @@ namespace Glamourer } } + public void ApplyOnlyEquipment(Character a) + { + var oldState = _bytes[1]; + WriteCustomizations = false; + SetHatState = false; + SetVisorState = false; + SetWeaponState = false; + Apply(a); + _bytes[1] = oldState; + } + + public void ApplyOnlyCustomizations(Character a) + { + var oldState = _bytes[1]; + SetHatState = false; + SetVisorState = false; + SetWeaponState = false; + var oldEquip = WriteEquipment; + WriteEquipment = CharacterEquipMask.None; + Apply(a); + _bytes[1] = oldState; + WriteEquipment = oldEquip; + } + public void Load(string base64) { var bytes = Convert.FromBase64String(base64); diff --git a/Glamourer/Gui/InterfaceActorPanel.cs b/Glamourer/Gui/InterfaceActorPanel.cs index 48aebb4..c41995c 100644 --- a/Glamourer/Gui/InterfaceActorPanel.cs +++ b/Glamourer/Gui/InterfaceActorPanel.cs @@ -11,6 +11,7 @@ using Glamourer.Customization; using Glamourer.Designs; using Glamourer.FileSystem; using ImGuiNET; +using Penumbra.GameData.Structs; using Penumbra.PlayerWatch; namespace Glamourer.Gui @@ -47,12 +48,45 @@ namespace Glamourer.Gui ImGuiCustom.HoverTooltip("Copy customization code to clipboard."); } + private static void ConditionalApply(CharacterSave save, Character player) + { + if (ImGui.GetIO().KeyShift) + save.ApplyOnlyCustomizations(player); + else if (ImGui.GetIO().KeyCtrl) + save.ApplyOnlyEquipment(player); + else + save.Apply(player); + } + + private CharacterSave ConditionalCopy(CharacterSave save) + { + var copy = save.Copy(); + if (ImGui.GetIO().KeyShift) + { + copy.Load(new CharacterEquipment()); + copy.SetHatState = false; + copy.SetVisorState = false; + copy.SetWeaponState = false; + copy.WriteEquipment = CharacterEquipMask.None; + } + else if (ImGui.GetIO().KeyCtrl) + { + copy.Load(CharacterCustomization.Default); + copy.SetHatState = false; + copy.SetVisorState = false; + copy.SetWeaponState = false; + copy.WriteCustomizations = false; + } + + return save.Copy(); + } + private bool DrawApplyClipboardButton() { ImGui.PushFont(UiBuilder.IconFont); var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString()) && _player != null; ImGui.PopFont(); - ImGuiCustom.HoverTooltip("Apply customization code from clipboard."); + ImGuiCustom.HoverTooltip("Apply customization code from clipboard.\nHold Shift to apply only customizations.\nHold Control to apply only equipment."); if (!applyButton) return false; @@ -64,7 +98,7 @@ namespace Glamourer.Gui try { var save = CharacterSave.FromString(text); - save.Apply(_player!); + ConditionalApply(save, _player!); } catch (Exception e) { @@ -82,7 +116,7 @@ namespace Glamourer.Gui OpenDesignNamePopup(DesignNameUse.SaveCurrent); ImGui.PopFont(); - ImGuiCustom.HoverTooltip("Save the current design."); + ImGuiCustom.HoverTooltip("Save the current design.\nHold Shift to save only customizations.\nHold Control to save only equipment."); DrawDesignNamePopup(DesignNameUse.SaveCurrent); } @@ -105,9 +139,9 @@ namespace Glamourer.Gui if (player == null) return; - save.Apply(player); + ConditionalApply(save, player); if (_inGPose) - save.Apply(fallback!); + ConditionalApply(save, fallback!); Glamourer.Penumbra.UpdateCharacters(player, fallback); } @@ -135,9 +169,9 @@ namespace Glamourer.Gui return; var fallBackCharacter = _gPoseActors.TryGetValue(player.Name.ToString(), out var f) ? f : null; - save.Apply(player); + ConditionalApply(save, player); if (fallBackCharacter != null) - save.Apply(fallBackCharacter); + ConditionalApply(save, fallBackCharacter!); Glamourer.Penumbra.UpdateCharacters(player, fallBackCharacter); } @@ -210,7 +244,7 @@ namespace Glamourer.Gui { DrawCopyClipboardButton(_currentSave); ImGui.SameLine(); - var changes = DrawApplyClipboardButton(); + var changes = !_currentSave.WriteProtected && DrawApplyClipboardButton(); ImGui.SameLine(); DrawSaveDesignButton(); ImGui.SameLine(); @@ -219,28 +253,39 @@ namespace Glamourer.Gui { ImGui.SameLine(); DrawApplyToTargetButton(_currentSave); - if (_player != null) + if (_player != null && !_currentSave.WriteProtected) { ImGui.SameLine(); DrawTargetPlayerButton(); } } - ImGui.SameLine(); - DrawRevertButton(); + var data = _currentSave; + if (!_currentSave.WriteProtected) + { + ImGui.SameLine(); + DrawRevertButton(); + } + else + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.8f); + data = data.Copy(); + } - if (DrawCustomization(ref _currentSave.Customizations) && _player != null) + if (DrawCustomization(ref data.Customizations) && _player != null) { Glamourer.RevertableDesigns.Add(_player); _currentSave.Customizations.Write(_player.Address); changes = true; } - changes |= DrawEquip(_currentSave.Equipment); - changes |= DrawMiscellaneous(_currentSave, _player); + changes |= DrawEquip(data.Equipment); + changes |= DrawMiscellaneous(data, _player); if (_player != null && changes) Glamourer.Penumbra.UpdateCharacters(_player); + if (_currentSave.WriteProtected) + ImGui.PopStyleVar(); } private void DrawActorPanel() diff --git a/Glamourer/Gui/InterfaceActorSelector.cs b/Glamourer/Gui/InterfaceActorSelector.cs index 4f26236..e4e3511 100644 --- a/Glamourer/Gui/InterfaceActorSelector.cs +++ b/Glamourer/Gui/InterfaceActorSelector.cs @@ -11,12 +11,17 @@ namespace Glamourer.Gui { internal partial class Interface { + public const int CharacterScreenIndex = 240; + public const int ExamineScreenIndex = 241; + public const int FittingRoomIndex = 242; + public const int DyePreviewIndex = 243; + private Character? _player; private string _currentLabel = string.Empty; private string _playerFilter = string.Empty; private string _playerFilterLower = string.Empty; private readonly Dictionary _playerNames = new(100); - private readonly Dictionary _gPoseActors = new(48); + private readonly Dictionary _gPoseActors = new(CharacterScreenIndex - GPoseObjectId); private void DrawPlayerFilter() { @@ -36,7 +41,7 @@ namespace Glamourer.Gui _gPoseActors[playerName] = null; - DrawSelectable(player, $"{playerName} (GPose)"); + DrawSelectable(player, $"{playerName} (GPose)", true); } private static string GetLabel(Character player, string playerName, int num) @@ -50,9 +55,16 @@ namespace Glamourer.Gui return num == 1 ? $"{playerName} (Monster)" : $"{playerName} #{num} (Monster)"; } - private void DrawPlayerSelectable(Character player) + private void DrawPlayerSelectable(Character player, int idx = 0) { - var playerName = player.Name.ToString(); + var (playerName, modifiable) = idx switch + { + CharacterScreenIndex => ("Character Screen Actor", false), + ExamineScreenIndex => ("Examine Screen Actor", false), + FittingRoomIndex => ("Fitting Room Actor", false), + DyePreviewIndex => ("Dye Preview Actor", false), + _ => (player.Name.ToString(), true), + }; if (!playerName.Any()) return; @@ -68,18 +80,19 @@ namespace Glamourer.Gui } var label = GetLabel(player, playerName, num); - DrawSelectable(player, label); + DrawSelectable(player, label, modifiable); } - private void DrawSelectable(Character player, string label) + private void DrawSelectable(Character player, string label, bool modifiable) { if (!_playerFilterLower.Any() || label.ToLowerInvariant().Contains(_playerFilterLower)) if (ImGui.Selectable(label, _currentLabel == label)) { _currentLabel = label; _currentSave.LoadCharacter(player); - _player = player; + _player = player; + _currentSave.WriteProtected = !modifiable; return; } @@ -87,7 +100,8 @@ namespace Glamourer.Gui return; _currentSave.LoadCharacter(player); - _player = player; + _player = player; + _currentSave.WriteProtected = !modifiable; } private void DrawSelectionButtons() @@ -125,6 +139,7 @@ namespace Glamourer.Gui _player = select; _currentLabel = _player.Name.ToString(); _currentSave.LoadCharacter(_player); + _currentSave.WriteProtected = false; } private void DrawPlayerSelector() @@ -152,16 +167,16 @@ namespace Glamourer.Gui for (var i = 0; i < GPoseObjectId; ++i) { - var player = CharacterFactory.Convert(Dalamud.Objects[i])!; + var player = CharacterFactory.Convert(Dalamud.Objects[i]); if (player != null) DrawPlayerSelectable(player); } - for (var i = GPoseObjectId + 48; i < Dalamud.Objects.Length; ++i) + for (var i = CharacterScreenIndex; i < Dalamud.Objects.Length; ++i) { - var player = CharacterFactory.Convert(Dalamud.Objects[i])!; + var player = CharacterFactory.Convert(Dalamud.Objects[i]); if (player != null) - DrawPlayerSelectable(player); + DrawPlayerSelectable(player, i); } diff --git a/Glamourer/Gui/InterfaceDesigns.cs b/Glamourer/Gui/InterfaceDesigns.cs index 98eb35c..371fd4b 100644 --- a/Glamourer/Gui/InterfaceDesigns.cs +++ b/Glamourer/Gui/InterfaceDesigns.cs @@ -125,7 +125,7 @@ namespace Glamourer.Gui ImGui.PopFont(); if (_selection == null) ImGui.PopStyleVar(); - ImGuiCustom.HoverTooltip("Clone the currently selected Design."); + ImGuiCustom.HoverTooltip("Clone the currently selected Design.\nHold Shift to only clone the customizations.\nHold Control to only clone the equipment."); DrawDesignNamePopup(DesignNameUse.DuplicateDesign); } diff --git a/Glamourer/Gui/InterfaceHelpers.cs b/Glamourer/Gui/InterfaceHelpers.cs index 21251fa..0db19a1 100644 --- a/Glamourer/Gui/InterfaceHelpers.cs +++ b/Glamourer/Gui/InterfaceHelpers.cs @@ -152,7 +152,7 @@ namespace Glamourer.Gui switch (use) { case DesignNameUse.SaveCurrent: - SaveNewDesign(_currentSave.Copy()); + SaveNewDesign(ConditionalCopy(_currentSave)); break; case DesignNameUse.NewDesign: var empty = new CharacterSave(); @@ -161,7 +161,7 @@ namespace Glamourer.Gui SaveNewDesign(empty); break; case DesignNameUse.DuplicateDesign: - SaveNewDesign(_selection!.Data.Copy()); + SaveNewDesign(ConditionalCopy(_selection!.Data)); break; case DesignNameUse.NewFolder: _designs.FileSystem