diff --git a/OtterGui b/OtterGui index 98064e79..c84ff1b1 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 98064e790042c90c82a58fbfa79201bd69800758 +Subproject commit c84ff1b1c313c5f4ae28645a2e3fcb4d214f240a diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index bd4231b9..0da4ca5e 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -39,6 +39,8 @@ public partial class Configuration : IPluginConfiguration public bool PreferNamedCollectionsOverOwners { get; set; } = true; public bool UseDefaultCollectionForRetainers { get; set; } = false; + public bool HideRedrawBar { get; set; } = false; + #if DEBUG public bool DebugMode { get; set; } = true; #else diff --git a/Penumbra/Import/TexToolsImporter.ModPack.cs b/Penumbra/Import/TexToolsImporter.ModPack.cs index 5227b454..0edef44e 100644 --- a/Penumbra/Import/TexToolsImporter.ModPack.cs +++ b/Penumbra/Import/TexToolsImporter.ModPack.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text; using Newtonsoft.Json; +using OtterGui; using Penumbra.Mods; using Penumbra.Util; using SharpCompress.Archives.Zip; @@ -166,16 +167,17 @@ public partial class TexToolsImporter : ( 1 + allOptions.Count / IModGroup.MaxMultiOptions, IModGroup.MaxMultiOptions ); _currentGroupName = GetGroupName( group.GroupName, groupNames ); - var optionIdx = 0; + var optionIdx = 0; for( var groupId = 0; groupId < numGroups; ++groupId ) { - var name = numGroups == 1 ? _currentGroupName : $"{_currentGroupName}, Part {groupId + 1}"; + var name = numGroups == 1 ? _currentGroupName : $"{_currentGroupName}, Part {groupId + 1}"; options.Clear(); var description = new StringBuilder(); var groupFolder = Mod.NewSubFolderName( _currentModDirectory, name ) ?? new DirectoryInfo( Path.Combine( _currentModDirectory.FullName, numGroups == 1 ? $"Group {groupPriority + 1}" : $"Group {groupPriority + 1}, Part {groupId + 1}" ) ); + uint? defaultSettings = group.SelectionType == SelectType.Multi ? 0u : null; for( var i = 0; i + optionIdx < allOptions.Count && i < maxOptions; ++i ) { var option = allOptions[ i + optionIdx ]; @@ -191,6 +193,13 @@ public partial class TexToolsImporter description.Append( '\n' ); } + if( option.IsChecked ) + { + defaultSettings = group.SelectionType == SelectType.Multi + ? ( defaultSettings!.Value | ( 1u << i ) ) + : ( uint )i; + } + ++_currentOptionIdx; } @@ -205,11 +214,12 @@ public partial class TexToolsImporter { _currentOptionName = empty.Name; options.Insert( 0, Mod.CreateEmptySubMod( empty.Name ) ); + defaultSettings = defaultSettings == null ? 0 : defaultSettings.Value + 1; } } Mod.CreateOptionGroup( _currentModDirectory, group.SelectionType, name, groupPriority, groupPriority, - description.ToString(), options ); + defaultSettings ?? 0, description.ToString(), options ); ++groupPriority; } } diff --git a/Penumbra/Mods/Editor/Mod.Editor.Edit.cs b/Penumbra/Mods/Editor/Mod.Editor.Edit.cs index 3b27f573..29f5389b 100644 --- a/Penumbra/Mods/Editor/Mod.Editor.Edit.cs +++ b/Penumbra/Mods/Editor/Mod.Editor.Edit.cs @@ -37,14 +37,7 @@ public partial class Mod } Penumbra.ModManager.OptionSetFiles( _mod, _subMod.GroupIdx, _subMod.OptionIdx, dict ); - if( num > 0 ) - { - RevertFiles(); - } - else - { - FileChanges = false; - } + UpdateFiles(); return num; } diff --git a/Penumbra/Mods/Editor/Mod.Editor.Files.cs b/Penumbra/Mods/Editor/Mod.Editor.Files.cs index 00f246a4..114359db 100644 --- a/Penumbra/Mods/Editor/Mod.Editor.Files.cs +++ b/Penumbra/Mods/Editor/Mod.Editor.Files.cs @@ -98,7 +98,7 @@ public partial class Mod { void HandleSubMod( ISubMod mod, int groupIdx, int optionIdx ) { - var newDict = mod.Files.Where( kvp => CheckAgainstMissing( kvp.Value, kvp.Key ) ) + var newDict = mod.Files.Where( kvp => CheckAgainstMissing( kvp.Value, kvp.Key, mod == _subMod ) ) .ToDictionary( kvp => kvp.Key, kvp => kvp.Value ); if( newDict.Count != mod.Files.Count ) { @@ -110,13 +110,18 @@ public partial class Mod _missingFiles.Clear(); } - private bool CheckAgainstMissing( FullPath file, Utf8GamePath key ) + private bool CheckAgainstMissing( FullPath file, Utf8GamePath key, bool removeUsed ) { if( !_missingFiles.Contains( file ) ) { return true; } + if( removeUsed ) + { + _usedPaths.Remove( key ); + } + Penumbra.Log.Debug( $"[RemoveMissingPaths] Removing {key} -> {file} from {_mod.Name}." ); return false; } diff --git a/Penumbra/Mods/Manager/Mod.Manager.Options.cs b/Penumbra/Mods/Manager/Mod.Manager.Options.cs index b2ecdc54..3e248c0e 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Options.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Options.cs @@ -28,6 +28,18 @@ public sealed partial class Mod ModOptionChanged.Invoke( ModOptionChangeType.GroupTypeChanged, mod, groupIdx, -1, -1 ); } + public void ChangeModGroupDefaultOption( Mod mod, int groupIdx, uint defaultOption ) + { + var group = mod._groups[groupIdx]; + if( group.DefaultSettings == defaultOption ) + { + return; + } + + group.DefaultSettings = defaultOption; + ModOptionChanged.Invoke( ModOptionChangeType.DefaultOptionChanged, mod, groupIdx, -1, -1 ); + } + public void RenameModGroup( Mod mod, int groupIdx, string newName ) { var group = mod._groups[ groupIdx ]; diff --git a/Penumbra/Mods/Manager/ModOptionChangeType.cs b/Penumbra/Mods/Manager/ModOptionChangeType.cs index 080c2983..b4c4947e 100644 --- a/Penumbra/Mods/Manager/ModOptionChangeType.cs +++ b/Penumbra/Mods/Manager/ModOptionChangeType.cs @@ -17,6 +17,7 @@ public enum ModOptionChangeType OptionMetaChanged, DisplayChange, PrepareChange, + DefaultOptionChanged, } public static class ModOptionChangeTypeExtension @@ -30,21 +31,22 @@ public static class ModOptionChangeTypeExtension { ( requiresSaving, requiresReloading, wasPrepared ) = type switch { - ModOptionChangeType.GroupRenamed => ( true, false, false ), - ModOptionChangeType.GroupAdded => ( true, false, false ), - ModOptionChangeType.GroupDeleted => ( true, true, false ), - ModOptionChangeType.GroupMoved => ( true, false, false ), - ModOptionChangeType.GroupTypeChanged => ( true, true, true ), - ModOptionChangeType.PriorityChanged => ( true, true, true ), - ModOptionChangeType.OptionAdded => ( true, true, true ), - ModOptionChangeType.OptionDeleted => ( true, true, false ), - ModOptionChangeType.OptionMoved => ( true, false, false ), - ModOptionChangeType.OptionFilesChanged => ( false, true, false ), - ModOptionChangeType.OptionFilesAdded => ( false, true, true ), - ModOptionChangeType.OptionSwapsChanged => ( false, true, false ), - ModOptionChangeType.OptionMetaChanged => ( false, true, false ), - ModOptionChangeType.DisplayChange => ( false, false, false ), - _ => ( false, false, false ), + ModOptionChangeType.GroupRenamed => ( true, false, false ), + ModOptionChangeType.GroupAdded => ( true, false, false ), + ModOptionChangeType.GroupDeleted => ( true, true, false ), + ModOptionChangeType.GroupMoved => ( true, false, false ), + ModOptionChangeType.GroupTypeChanged => ( true, true, true ), + ModOptionChangeType.PriorityChanged => ( true, true, true ), + ModOptionChangeType.OptionAdded => ( true, true, true ), + ModOptionChangeType.OptionDeleted => ( true, true, false ), + ModOptionChangeType.OptionMoved => ( true, false, false ), + ModOptionChangeType.OptionFilesChanged => ( false, true, false ), + ModOptionChangeType.OptionFilesAdded => ( false, true, true ), + ModOptionChangeType.OptionSwapsChanged => ( false, true, false ), + ModOptionChangeType.OptionMetaChanged => ( false, true, false ), + ModOptionChangeType.DisplayChange => ( false, false, false ), + ModOptionChangeType.DefaultOptionChanged => ( true, false, false ), + _ => ( false, false, false ), }; } } \ No newline at end of file diff --git a/Penumbra/Mods/Mod.Creation.cs b/Penumbra/Mods/Mod.Creation.cs index de120796..184dc8cb 100644 --- a/Penumbra/Mods/Mod.Creation.cs +++ b/Penumbra/Mods/Mod.Creation.cs @@ -63,7 +63,7 @@ public partial class Mod // Create a file for an option group from given data. internal static void CreateOptionGroup( DirectoryInfo baseFolder, SelectType type, string name, - int priority, int index, string desc, IEnumerable< ISubMod > subMods ) + int priority, int index, uint defaultSettings, string desc, IEnumerable< ISubMod > subMods ) { switch( type ) { @@ -71,9 +71,10 @@ public partial class Mod { var group = new MultiModGroup() { - Name = name, - Description = desc, - Priority = priority, + Name = name, + Description = desc, + Priority = priority, + DefaultSettings = defaultSettings, }; group.PrioritizedOptions.AddRange( subMods.OfType< SubMod >().Select( ( s, idx ) => ( s, idx ) ) ); IModGroup.Save( group, baseFolder, index ); @@ -83,9 +84,10 @@ public partial class Mod { var group = new SingleModGroup() { - Name = name, - Description = desc, - Priority = priority, + Name = name, + Description = desc, + Priority = priority, + DefaultSettings = defaultSettings, }; group.OptionData.AddRange( subMods.OfType< SubMod >() ); IModGroup.Save( group, baseFolder, index ); diff --git a/Penumbra/Mods/Subclasses/IModGroup.cs b/Penumbra/Mods/Subclasses/IModGroup.cs index 9507b0f3..fb2d305a 100644 --- a/Penumbra/Mods/Subclasses/IModGroup.cs +++ b/Penumbra/Mods/Subclasses/IModGroup.cs @@ -20,6 +20,7 @@ public interface IModGroup : IEnumerable< ISubMod > public string Description { get; } public SelectType Type { get; } public int Priority { get; } + public uint DefaultSettings { get; set; } public int OptionPriority( Index optionIdx ); @@ -60,7 +61,8 @@ public interface IModGroup : IEnumerable< ISubMod > public static void SaveDelayed( IModGroup group, DirectoryInfo basePath, int groupIdx ) { - Penumbra.Framework.RegisterDelayed( $"{nameof( SaveModGroup )}_{basePath.Name}_{group.Name}", () => SaveModGroup( group, basePath, groupIdx ) ); + Penumbra.Framework.RegisterDelayed( $"{nameof( SaveModGroup )}_{basePath.Name}_{group.Name}", + () => SaveModGroup( group, basePath, groupIdx ) ); } public static void Save( IModGroup group, DirectoryInfo basePath, int groupIdx ) @@ -82,6 +84,8 @@ public interface IModGroup : IEnumerable< ISubMod > j.WriteValue( group.Priority ); j.WritePropertyName( nameof( Type ) ); j.WriteValue( group.Type.ToString() ); + j.WritePropertyName( nameof( group.DefaultSettings ) ); + j.WriteValue( group.DefaultSettings ); j.WritePropertyName( "Options" ); j.WriteStartArray(); for( var idx = 0; idx < group.Count; ++idx ) @@ -96,5 +100,5 @@ public interface IModGroup : IEnumerable< ISubMod > public IModGroup Convert( SelectType type ); public bool MoveOption( int optionIdxFrom, int optionIdxTo ); - public void UpdatePositions(int from = 0); + public void UpdatePositions( int from = 0 ); } \ No newline at end of file diff --git a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs index 3413cc6f..91bc304b 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Numerics; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; @@ -20,6 +21,7 @@ public partial class Mod public string Name { get; set; } = "Group"; public string Description { get; set; } = "A non-exclusive group of settings."; public int Priority { get; set; } + public uint DefaultSettings { get; set; } public int OptionPriority( Index idx ) => PrioritizedOptions[ idx ].Priority; @@ -44,9 +46,10 @@ public partial class Mod var options = json[ "Options" ]; var ret = new MultiModGroup() { - Name = json[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty, - Description = json[ nameof( Description ) ]?.ToObject< string >() ?? string.Empty, - Priority = json[ nameof( Priority ) ]?.ToObject< int >() ?? 0, + Name = json[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty, + Description = json[ nameof( Description ) ]?.ToObject< string >() ?? string.Empty, + Priority = json[ nameof( Priority ) ]?.ToObject< int >() ?? 0, + DefaultSettings = json[ nameof( DefaultSettings ) ]?.ToObject< uint >() ?? 0, }; if( ret.Name.Length == 0 ) { @@ -71,6 +74,8 @@ public partial class Mod } } + ret.DefaultSettings = (uint) (ret.DefaultSettings & ( ( 1ul << ret.Count ) - 1 )); + return ret; } @@ -82,9 +87,10 @@ public partial class Mod case SelectType.Single: var multi = new SingleModGroup() { - Name = Name, - Description = Description, - Priority = Priority, + Name = Name, + Description = Description, + Priority = Priority, + DefaultSettings = ( uint )Math.Max( Math.Min( Count - 1, BitOperations.TrailingZeroCount( DefaultSettings) ), 0 ), }; multi.OptionData.AddRange( PrioritizedOptions.Select( p => p.Mod ) ); return multi; @@ -99,6 +105,7 @@ public partial class Mod return false; } + DefaultSettings = Functions.MoveBit( DefaultSettings, optionIdxFrom, optionIdxTo ); UpdatePositions( Math.Min( optionIdxFrom, optionIdxTo ) ); return true; } diff --git a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs index e37b1988..3de02d5c 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs @@ -20,6 +20,7 @@ public partial class Mod public string Name { get; set; } = "Option"; public string Description { get; set; } = "A mutually exclusive group of settings."; public int Priority { get; set; } + public uint DefaultSettings { get; set; } public readonly List< SubMod > OptionData = new(); @@ -44,9 +45,10 @@ public partial class Mod var options = json[ "Options" ]; var ret = new SingleModGroup { - Name = json[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty, - Description = json[ nameof( Description ) ]?.ToObject< string >() ?? string.Empty, - Priority = json[ nameof( Priority ) ]?.ToObject< int >() ?? 0, + Name = json[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty, + Description = json[ nameof( Description ) ]?.ToObject< string >() ?? string.Empty, + Priority = json[ nameof( Priority ) ]?.ToObject< int >() ?? 0, + DefaultSettings = json[ nameof( DefaultSettings ) ]?.ToObject< uint >() ?? 0u, }; if( ret.Name.Length == 0 ) { @@ -64,6 +66,9 @@ public partial class Mod } } + if( ( int )ret.DefaultSettings >= ret.Count ) + ret.DefaultSettings = 0; + return ret; } @@ -75,9 +80,10 @@ public partial class Mod case SelectType.Multi: var multi = new MultiModGroup() { - Name = Name, - Description = Description, - Priority = Priority, + Name = Name, + Description = Description, + Priority = Priority, + DefaultSettings = 1u << ( int )DefaultSettings, }; multi.PrioritizedOptions.AddRange( OptionData.Select( ( o, i ) => ( o, i ) ) ); return multi; @@ -92,6 +98,23 @@ public partial class Mod return false; } + // Update default settings with the move. + if( DefaultSettings == optionIdxFrom ) + { + DefaultSettings = ( uint )optionIdxTo; + } + else if( optionIdxFrom < optionIdxTo ) + { + if( DefaultSettings > optionIdxFrom && DefaultSettings <= optionIdxTo ) + { + --DefaultSettings; + } + } + else if( DefaultSettings < optionIdxFrom && DefaultSettings >= optionIdxTo ) + { + ++DefaultSettings; + } + UpdatePositions( Math.Min( optionIdxFrom, optionIdxTo ) ); return true; } diff --git a/Penumbra/Mods/Subclasses/ModSettings.cs b/Penumbra/Mods/Subclasses/ModSettings.cs index b79f1c9e..6e8c7343 100644 --- a/Penumbra/Mods/Subclasses/ModSettings.cs +++ b/Penumbra/Mods/Subclasses/ModSettings.cs @@ -30,7 +30,7 @@ public class ModSettings { Enabled = false, Priority = 0, - Settings = Enumerable.Repeat( 0u, mod.Groups.Count ).ToList(), + Settings = mod.Groups.Select( g => g.DefaultSettings ).ToList(), }; // Automatically react to changes in a mods available options. @@ -41,7 +41,7 @@ public class ModSettings case ModOptionChangeType.GroupRenamed: return true; case ModOptionChangeType.GroupAdded: // Add new empty setting for new mod. - Settings.Insert( groupIdx, 0 ); + Settings.Insert( groupIdx, mod.Groups[groupIdx].DefaultSettings ); return true; case ModOptionChangeType.GroupDeleted: // Remove setting for deleted mod. @@ -70,8 +70,8 @@ public class ModSettings var config = Settings[ groupIdx ]; Settings[ groupIdx ] = group.Type switch { - SelectType.Single => config >= optionIdx ? (config > 1 ? config - 1 : 0) : config, - SelectType.Multi => RemoveBit( config, optionIdx ), + SelectType.Single => config >= optionIdx ? config > 1 ? config - 1 : 0 : config, + SelectType.Multi => Functions.RemoveBit( config, optionIdx ), _ => config, }; return config != Settings[ groupIdx ]; @@ -88,7 +88,7 @@ public class ModSettings Settings[ groupIdx ] = group.Type switch { SelectType.Single => config == optionIdx ? ( uint )movedToIdx : config, - SelectType.Multi => MoveBit( config, optionIdx, movedToIdx ), + SelectType.Multi => Functions.MoveBit( config, optionIdx, movedToIdx ), _ => config, }; return config != Settings[ groupIdx ]; @@ -114,27 +114,6 @@ public class ModSettings Settings[ groupIdx ] = FixSetting( group, newValue ); } - // Remove a single bit, moving all further bits one down. - private static uint RemoveBit( uint config, int bit ) - { - var lowMask = ( 1u << bit ) - 1u; - var highMask = ~( ( 1u << ( bit + 1 ) ) - 1u ); - var low = config & lowMask; - var high = ( config & highMask ) >> 1; - return low | high; - } - - // Move a bit in an uint from its position to another, shifting other bits accordingly. - private static uint MoveBit( uint config, int bit1, int bit2 ) - { - var enabled = ( config & ( 1 << bit1 ) ) != 0 ? 1u << bit2 : 0u; - config = RemoveBit( config, bit1 ); - var lowMask = ( 1u << bit2 ) - 1u; - var low = config & lowMask; - var high = ( config & ~lowMask ) << 1; - return low | enabled | high; - } - // Add defaulted settings up to the required count. private bool AddMissingSettings( int totalCount ) { diff --git a/Penumbra/UI/Classes/ModEditWindow.Files.cs b/Penumbra/UI/Classes/ModEditWindow.Files.cs index 74a5b6e9..dff8eaa6 100644 --- a/Penumbra/UI/Classes/ModEditWindow.Files.cs +++ b/Penumbra/UI/Classes/ModEditWindow.Files.cs @@ -8,6 +8,7 @@ using OtterGui; using OtterGui.Classes; using OtterGui.Raii; using Penumbra.GameData.ByteString; +using Penumbra.GameData.Util; using Penumbra.Mods; namespace Penumbra.UI.Classes; @@ -230,7 +231,7 @@ public partial class ModEditWindow using var id = ImRaii.PushId( j ); ImGui.TableNextColumn(); var tmp = _fileIdx == i && _pathIdx == j ? _gamePathEdit : gamePath.ToString(); - + var pos = ImGui.GetCursorPosX() - ImGui.GetFrameHeight(); ImGui.SetNextItemWidth( -1 ); if( ImGui.InputText( string.Empty, ref tmp, Utf8GamePath.MaxGamePathLength ) ) { @@ -251,11 +252,20 @@ public partial class ModEditWindow _fileIdx = -1; _pathIdx = -1; } + else if( _fileIdx == i && _pathIdx == j && ( !Utf8GamePath.FromString( _gamePathEdit, out var path, false ) + || !path.IsEmpty && !path.Equals( gamePath ) && !_editor!.CanAddGamePath( path )) ) + { + ImGui.SameLine(); + ImGui.SetCursorPosX( pos ); + using var font = ImRaii.PushFont( UiBuilder.IconFont ); + ImGuiUtil.TextColored( 0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString() ); + } } private void PrintNewGamePath( int i, Mod.Editor.FileRegistry registry, ISubMod subMod ) { var tmp = _fileIdx == i && _pathIdx == -1 ? _gamePathEdit : string.Empty; + var pos = ImGui.GetCursorPosX() - ImGui.GetFrameHeight(); ImGui.SetNextItemWidth( -1 ); if( ImGui.InputTextWithHint( "##new", "Add New Path...", ref tmp, Utf8GamePath.MaxGamePathLength ) ) { @@ -274,6 +284,14 @@ public partial class ModEditWindow _fileIdx = -1; _pathIdx = -1; } + else if( _fileIdx == i && _pathIdx == -1 && (!Utf8GamePath.FromString( _gamePathEdit, out var path, false ) + || !path.IsEmpty && !_editor!.CanAddGamePath( path )) ) + { + ImGui.SameLine(); + ImGui.SetCursorPosX( pos ); + using var font = ImRaii.PushFont( UiBuilder.IconFont ); + ImGuiUtil.TextColored( 0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString() ); + } } private void DrawButtonHeader() diff --git a/Penumbra/UI/ConfigWindow.Changelog.cs b/Penumbra/UI/ConfigWindow.Changelog.cs index 013e7357..5805cb30 100644 --- a/Penumbra/UI/ConfigWindow.Changelog.cs +++ b/Penumbra/UI/ConfigWindow.Changelog.cs @@ -33,11 +33,15 @@ public partial class ConfigWindow "Migration should have set your currently assigned Base Collection to the Interface Collection, please verify that.", 1 ) .RegisterEntry( "New API / IPC for the Interface Collection added.", 1 ) .RegisterHighlight( "API / IPC consumers should verify whether they need to change resolving to the new collection.", 1 ) + .RegisterEntry( "Files that the game loads super early should now be replaceable correctly via base or interface collection." ) + .RegisterEntry( "The 1.0 neck tattoo file should now be replaceable, even in character collections. You can also replace the transparent texture used instead. (This was ugly.)" ) .RegisterEntry( "Added buttons for redrawing self or all as well as a tooltip to describe redraw options and a tutorial step for it." ) .RegisterEntry( "Collection Selectors now display None at the top if available." ) + .RegisterEntry( "Adding mods via API/IPC will now cause them to incorporate and then delete TexTools .meta and .rgsp files automatically." ) .RegisterEntry( "Fixed an issue with Actor 201 using Your Character collections in cutscenes." ) .RegisterEntry( "Fixed issues with and improved mod option editing." ) + .RegisterEntry( "Fixed some issues with and improved file redirection editing - you are now informed if you can not add a game path (because it is invalid or already in use)." ) .RegisterEntry( "Backend optimizations." ) .RegisterEntry( "Changed metadata change system again.", 1 ) .RegisterEntry( "Improved logging efficiency.", 1 ); diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs index 4a3fec24..dcd7ef82 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs @@ -97,7 +97,7 @@ public partial class ConfigWindow ImGui.Dummy( _window._defaultSpace ); } - private void DrawUpdateBibo( Vector2 buttonSize) + private void DrawUpdateBibo( Vector2 buttonSize ) { if( ImGui.Button( "Update Bibo Material", buttonSize ) ) { @@ -108,7 +108,8 @@ public partial class ConfigWindow _window.ModEditPopup.UpdateModels(); } - ImGuiUtil.HoverTooltip( "For every model in this mod, change all material names that end in a _b or _c suffix to a _bibo or _bibopube suffix respectively.\n" + ImGuiUtil.HoverTooltip( + "For every model in this mod, change all material names that end in a _b or _c suffix to a _bibo or _bibopube suffix respectively.\n" + "Does nothing if the mod does not contain any such models or no model contains such materials.\n" + "Use this for outdated mods made for old Bibo bodies.\n" + "Go to Advanced Editing for more fine-tuned control over material assignment." ); @@ -459,15 +460,16 @@ public partial class ConfigWindow public static void Draw( ModPanel panel, int groupIdx ) { - using var table = ImRaii.Table( string.Empty, 4, ImGuiTableFlags.SizingFixedFit ); + using var table = ImRaii.Table( string.Empty, 5, ImGuiTableFlags.SizingFixedFit ); if( !table ) { return; } ImGui.TableSetupColumn( "idx", ImGuiTableColumnFlags.WidthFixed, 60 * ImGuiHelpers.GlobalScale ); + ImGui.TableSetupColumn( "default", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight() ); ImGui.TableSetupColumn( "name", ImGuiTableColumnFlags.WidthFixed, - panel._window._inputTextWidth.X - 62 * ImGuiHelpers.GlobalScale ); + panel._window._inputTextWidth.X - 68 * ImGuiHelpers.GlobalScale - ImGui.GetFrameHeight() ); ImGui.TableSetupColumn( "delete", ImGuiTableColumnFlags.WidthFixed, panel._window._iconButtonSize.X ); ImGui.TableSetupColumn( "priority", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale ); @@ -491,6 +493,31 @@ public partial class ConfigWindow Source( group, groupIdx, optionIdx ); Target( panel, group, groupIdx, optionIdx ); + ImGui.TableNextColumn(); + + + if( group.Type == SelectType.Single ) + { + if( ImGui.RadioButton( "##default", group.DefaultSettings == optionIdx ) ) + { + Penumbra.ModManager.ChangeModGroupDefaultOption( panel._mod, groupIdx, ( uint )optionIdx ); + } + + ImGuiUtil.HoverTooltip( $"Set {option.Name} as the default choice for this group." ); + } + else + { + var isDefaultOption = ( ( group.DefaultSettings >> optionIdx ) & 1 ) != 0; + if( ImGui.Checkbox( "##default", ref isDefaultOption ) ) + { + Penumbra.ModManager.ChangeModGroupDefaultOption( panel._mod, groupIdx, isDefaultOption + ? group.DefaultSettings | ( 1u << optionIdx ) + : group.DefaultSettings & ~( 1u << optionIdx ) ); + } + + ImGuiUtil.HoverTooltip( $"{( isDefaultOption ? "Disable" : "Enable" )} {option.Name} per default in this group." ); + } + ImGui.TableNextColumn(); if( Input.Text( "##Name", groupIdx, optionIdx, option.Name, out var newOptionName, 256, -1 ) ) { @@ -527,6 +554,7 @@ public partial class ConfigWindow ImGui.Selectable( $"Option #{group.Count + 1}" ); Target( panel, group, groupIdx, group.Count ); ImGui.TableNextColumn(); + ImGui.TableNextColumn(); ImGui.SetNextItemWidth( -1 ); var tmp = _newOptionNameIdx == groupIdx ? _newOptionName : string.Empty; if( ImGui.InputTextWithHint( "##newOption", "Add new option...", ref tmp, 256 ) ) @@ -575,7 +603,7 @@ public partial class ConfigWindow return; } - if( _dragDropGroupIdx >= 0 && _dragDropOptionIdx >= 0 ) + if( _dragDropGroupIdx >= 0 && _dragDropOptionIdx >= 0 ) { if( _dragDropGroupIdx == groupIdx ) { @@ -589,7 +617,7 @@ public partial class ConfigWindow var sourceOption = _dragDropOptionIdx; var sourceGroup = panel._mod.Groups[ sourceGroupIdx ]; var currentCount = group.Count; - var option = sourceGroup[sourceOption]; + var option = sourceGroup[ sourceOption ]; var priority = sourceGroup.OptionPriority( _dragDropGroupIdx ); panel._delayedActions.Enqueue( () => { diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs index bb012be3..6c5582b8 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs @@ -160,7 +160,7 @@ public partial class ConfigWindow } using var id = ImRaii.PushId( groupIdx ); - var selectedOption = _emptySetting ? 0 : ( int )_settings.Settings[ groupIdx ]; + var selectedOption = _emptySetting ? (int) group.DefaultSettings : ( int )_settings.Settings[ groupIdx ]; ImGui.SetNextItemWidth( _window._inputTextWidth.X * 3 / 4 ); using var combo = ImRaii.Combo( string.Empty, group[ selectedOption ].Name ); if( combo ) @@ -199,7 +199,7 @@ public partial class ConfigWindow } using var id = ImRaii.PushId( groupIdx ); - var flags = _emptySetting ? 0u : _settings.Settings[ groupIdx ]; + var flags = _emptySetting ? group.DefaultSettings : _settings.Settings[ groupIdx ]; Widget.BeginFramedGroup( group.Name, group.Description ); for( var idx2 = 0; idx2 < group.Count; ++idx2 ) { diff --git a/Penumbra/UI/ConfigWindow.ModsTab.cs b/Penumbra/UI/ConfigWindow.ModsTab.cs index 2a0e193e..35b01d19 100644 --- a/Penumbra/UI/ConfigWindow.ModsTab.cs +++ b/Penumbra/UI/ConfigWindow.ModsTab.cs @@ -37,8 +37,8 @@ public partial class ConfigWindow using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, Vector2.Zero ); - using( var child = ImRaii.Child( "##ModsTabMod", new Vector2( -1, -ImGui.GetFrameHeight() ), true, - ImGuiWindowFlags.HorizontalScrollbar ) ) + using( var child = ImRaii.Child( "##ModsTabMod", new Vector2( -1, Penumbra.Config.HideRedrawBar ? 0 : -ImGui.GetFrameHeight() ), + true, ImGuiWindowFlags.HorizontalScrollbar ) ) { style.Pop(); if( child ) @@ -68,6 +68,11 @@ public partial class ConfigWindow private void DrawRedrawLine() { + if( Penumbra.Config.HideRedrawBar ) + { + return; + } + var frameHeight = new Vector2( 0, ImGui.GetFrameHeight() ); var frameColor = ImGui.GetColorU32( ImGuiCol.FrameBg ); using( var _ = ImRaii.Group() ) diff --git a/Penumbra/UI/ConfigWindow.SettingsTab.General.cs b/Penumbra/UI/ConfigWindow.SettingsTab.General.cs index fe9983a6..1e0e0c50 100644 --- a/Penumbra/UI/ConfigWindow.SettingsTab.General.cs +++ b/Penumbra/UI/ConfigWindow.SettingsTab.General.cs @@ -59,6 +59,9 @@ public partial class ConfigWindow Dalamud.PluginInterface.UiBuilder.DisableGposeUiHide = !v; } ); ImGui.Dummy( _window._defaultSpace ); + Checkbox( "Hide Redraw Bar in Mod Panel", "Hides the lower redraw buttons in the mod panel in your Mods tab.", + Penumbra.Config.HideRedrawBar, v => Penumbra.Config.HideRedrawBar = v ); + ImGui.Dummy( _window._defaultSpace ); Checkbox( $"Use {AssignedCollections} in Character Window", "Use the character collection for your characters name or the Your Character collection in your main character window, if it is set.", Penumbra.Config.UseCharacterCollectionInMainWindow, v => Penumbra.Config.UseCharacterCollectionInMainWindow = v );