diff --git a/Penumbra/Mod/ModCleanup.cs b/Penumbra/Mod/ModCleanup.cs index fca6acfd..0026fd99 100644 --- a/Penumbra/Mod/ModCleanup.cs +++ b/Penumbra/Mod/ModCleanup.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -478,5 +479,46 @@ namespace Penumbra.Mod RemoveUselessGroups( meta ); ClearEmptySubDirectories( baseDir ); } + + public static void AutoGenerateGroups( DirectoryInfo baseDir, ModMeta meta ) + { + meta.Groups.Clear(); + ClearEmptySubDirectories( baseDir ); + foreach( var groupDir in baseDir.EnumerateDirectories() ) + { + var group = new OptionGroup + { + GroupName = groupDir.Name, + SelectionType = SelectType.Single, + Options = new List< Option >(), + }; + + foreach( var optionDir in groupDir.EnumerateDirectories() ) + { + var option = new Option + { + OptionDesc = string.Empty, + OptionName = optionDir.Name, + OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >(), + }; + foreach( var file in optionDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) ) + { + var relPath = new RelPath( file, baseDir ); + var gamePath = new GamePath( file, optionDir ); + option.OptionFiles[ relPath ] = new HashSet< GamePath > { gamePath }; + } + + if( option.OptionFiles.Any() ) + { + group.Options.Add( option ); + } + } + + if( group.Options.Any() ) + { + meta.Groups.Add( groupDir.Name, @group ); + } + } + } } } \ No newline at end of file diff --git a/Penumbra/Mods/ModCollectionCache.cs b/Penumbra/Mods/ModCollectionCache.cs index 9914553a..1e4582c6 100644 --- a/Penumbra/Mods/ModCollectionCache.cs +++ b/Penumbra/Mods/ModCollectionCache.cs @@ -97,6 +97,10 @@ namespace Penumbra.Mods else { mod.Cache.AddConflict( oldMod, gamePath ); + if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority) + { + oldMod.Cache.AddConflict( mod, gamePath ); + } } } @@ -223,6 +227,10 @@ namespace Penumbra.Mods else { mod.Cache.AddConflict( oldMod, swap.Key ); + if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority ) + { + oldMod.Cache.AddConflict( mod, swap.Key ); + } } } } @@ -231,13 +239,17 @@ namespace Penumbra.Mods { foreach( var manip in mod.Data.Resources.MetaManipulations.GetManipulationsForConfig( mod.Settings, mod.Data.Meta ) ) { - if( MetaManipulations.TryGetValue( manip, out var precedingMod ) ) + if( !MetaManipulations.TryGetValue( manip, out var oldMod ) ) { - mod.Cache.AddConflict( precedingMod, manip ); + MetaManipulations.ApplyMod( manip, mod ); } else { - MetaManipulations.ApplyMod( manip, mod ); + mod.Cache.AddConflict( oldMod, manip ); + if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority ) + { + oldMod.Cache.AddConflict( mod, manip ); + } } } } diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs index 4a42d7b6..846d06fb 100644 --- a/Penumbra/Mods/ModManager.cs +++ b/Penumbra/Mods/ModManager.cs @@ -298,10 +298,10 @@ namespace Penumbra.Mods return true; } - public bool UpdateMod( ModData mod, bool reloadMeta = false, bool recomputeMeta = false ) + public bool UpdateMod( ModData mod, bool reloadMeta = false, bool recomputeMeta = false, bool force = false ) { var oldName = mod.Meta.Name; - var metaChanges = mod.Meta.RefreshFromFile( mod.MetaFile ); + var metaChanges = mod.Meta.RefreshFromFile( mod.MetaFile ) || force; var fileChanges = mod.Resources.RefreshModFiles( mod.BasePath ); if( !recomputeMeta && !reloadMeta && !metaChanges && fileChanges == 0 ) diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs index fd386d50..22aea5c7 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs @@ -1,7 +1,10 @@ using System.IO; using System.Linq; +using System.Numerics; using Dalamud.Interface; using ImGuiNET; +using Lumina.Data.Parsing; +using Lumina.Excel.GeneratedSheets; using Penumbra.GameData.Enums; using Penumbra.GameData.Util; using Penumbra.Meta; @@ -198,6 +201,14 @@ namespace Penumbra.UI _base._penumbra.Api.InvokeTooltip( data ); raii.Pop(); } + + if( data is Item it ) + { + var modelId = $"({( ( Quad )it.ModelMain ).A})"; + var offset = ImGui.CalcTextSize( modelId ).X - ImGui.GetStyle().ItemInnerSpacing.X; + ImGui.SameLine(ImGui.GetWindowContentRegionWidth() - offset); + ImGui.TextColored( new Vector4(0.5f, 0.5f, 0.5f, 1 ), modelId ); + } } } @@ -401,6 +412,7 @@ namespace Penumbra.UI continue; } + _fullFilenameList![ i ].selected = false; var relName = _fullFilenameList[ i ].relName; if( defaultIndex >= 0 ) { @@ -428,6 +440,7 @@ namespace Penumbra.UI if( changed ) { + _fullFilenameList = null; _selector.SaveCurrentMod(); // Since files may have changed, we need to recompute effective files. foreach( var collection in _modManager.Collections.Collections.Values diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsManipulations.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsManipulations.cs index 1766286f..5239b00f 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsManipulations.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsManipulations.cs @@ -701,7 +701,7 @@ namespace Penumbra.UI { MetaType.Est => new MetaManipulation( newManip.Value.Identifier, ( ulong )def ), MetaType.Eqp => new MetaManipulation( newManip.Value.Identifier, ( ulong )def ), - MetaType.Eqdp => new MetaManipulation( newManip.Value.Identifier, ( ulong )def ), + MetaType.Eqdp => new MetaManipulation( newManip.Value.Identifier, (ushort) def ), MetaType.Gmp => new MetaManipulation( newManip.Value.Identifier, ( ulong )def ), MetaType.Imc => new MetaManipulation( newManip.Value.Identifier, ( ( ImcFile.ImageChangeData )def ).ToInteger() ), diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs index 2250b80e..bbc2c42a 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledModPanel.cs @@ -216,6 +216,11 @@ namespace Penumbra.UI if( ImGui.Checkbox( LabelModEnabled, ref enabled ) ) { Mod.Settings.Enabled = enabled; + if( !enabled ) + { + Mod.Cache.ClearConflicts(); + } + _base.SaveCurrentCollection( Mod.Data.Resources.MetaManipulations.Count > 0 ); _selector.Cache.TriggerFilterReset(); } @@ -425,7 +430,7 @@ namespace Penumbra.UI { if( ImGui.Button( "Recompute Metadata" ) ) { - _selector.ReloadCurrentMod( true, true ); + _selector.ReloadCurrentMod( true, true, true ); } ImGuiCustom.HoverTooltip( @@ -440,7 +445,7 @@ namespace Penumbra.UI { ModCleanup.Deduplicate( Mod!.Data.BasePath, Meta! ); _selector.SaveCurrentMod(); - _selector.ReloadCurrentMod(); + _selector.ReloadCurrentMod( true, true, true ); } ImGuiCustom.HoverTooltip( TooltipDeduplicate ); @@ -452,12 +457,28 @@ namespace Penumbra.UI { ModCleanup.Normalize( Mod!.Data.BasePath, Meta! ); _selector.SaveCurrentMod(); - _selector.ReloadCurrentMod(); + _selector.ReloadCurrentMod( true, true, true ); } ImGuiCustom.HoverTooltip( TooltipNormalize ); } + private void DrawAutoGenerateGroupsButton() + { + if( ImGui.Button( "Auto-Generate Groups" ) ) + { + ModCleanup.AutoGenerateGroups( Mod!.Data.BasePath, Meta! ); + _selector.SaveCurrentMod(); + _selector.ReloadCurrentMod( true, true ); + } + + ImGuiCustom.HoverTooltip( "Automatically generate single-select groups from all folders (clears existing groups):\n" + + "First subdirectory: Option Group\n" + + "Second subdirectory: Option Name\n" + + "Afterwards: Relative file paths.\n" + + "Experimental - Use at own risk!" ); + } + private void DrawSplitButton() { if( ImGui.Button( "Split Mod" ) ) @@ -487,6 +508,8 @@ namespace Penumbra.UI ImGui.SameLine(); DrawNormalizeButton(); ImGui.SameLine(); + DrawAutoGenerateGroupsButton(); + ImGui.SameLine(); DrawSplitButton(); DrawSortOrder( Mod!.Data, _modManager, _selector ); @@ -498,7 +521,7 @@ namespace Penumbra.UI { using var raii = ImGuiRaii.DeferredEnd( ImGui.EndChild ); var ret = ImGui.BeginChild( LabelModPanel, AutoFillSize, true ); - + if( !ret || Mod == null ) { return; diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs index 5f73a2e9..4e430298 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs @@ -452,14 +452,14 @@ namespace Penumbra.UI SetSelection( idx, mod ); } - public void ReloadCurrentMod( bool reloadMeta = false, bool recomputeMeta = false ) + public void ReloadCurrentMod( bool reloadMeta = false, bool recomputeMeta = false, bool force = false ) { if( Mod == null ) { return; } - if( _index >= 0 && _modManager.UpdateMod( Mod.Data, reloadMeta, recomputeMeta ) ) + if( _index >= 0 && _modManager.UpdateMod( Mod.Data, reloadMeta, recomputeMeta, force ) ) { SelectModOnUpdate( Mod.Data.BasePath.Name ); _base._menu.InstalledTab.ModPanel.Details.ResetState();