diff --git a/.gitignore b/.gitignore index ca91ac7b..f9e5f3df 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ obj/ desktop.ini Build/ *DotSettings -*DotSettings.user \ No newline at end of file +*DotSettings.user +.vs diff --git a/YAFC/Windows/PreferencesScreen.cs b/YAFC/Windows/PreferencesScreen.cs index 9d8fe7e3..d7eaa4cc 100644 --- a/YAFC/Windows/PreferencesScreen.cs +++ b/YAFC/Windows/PreferencesScreen.cs @@ -75,6 +75,8 @@ public override void Build(ImGui gui) Close(); if (prefs.justChanged) MainScreen.Instance.RebuildProjectView(); + if (settings.justChanged) + Project.current.RecalculateDisplayPages(); } private void ChoiceObject(ImGui gui, string text, T[] list, T current, Action select) where T:FactorioObject diff --git a/YAFC/Workspace/ProductionTable/ModuleFillerParametersScreen.cs b/YAFC/Workspace/ProductionTable/ModuleFillerParametersScreen.cs index 351bdba5..18d5fe90 100644 --- a/YAFC/Workspace/ProductionTable/ModuleFillerParametersScreen.cs +++ b/YAFC/Workspace/ProductionTable/ModuleFillerParametersScreen.cs @@ -86,6 +86,8 @@ public override void Build(ImGui gui) if (gui.BuildButton("Done")) Close(); + if (Project.current.settings.justChanged) + Project.current.RecalculateDisplayPages(); } } } \ No newline at end of file diff --git a/YAFC/Workspace/ProductionTable/ProductionTableView.cs b/YAFC/Workspace/ProductionTable/ProductionTableView.cs index 16134a45..cede1c96 100644 --- a/YAFC/Workspace/ProductionTable/ProductionTableView.cs +++ b/YAFC/Workspace/ProductionTable/ProductionTableView.cs @@ -159,6 +159,20 @@ public override void BuildMenu(ImGui gui) SelectObjectPanel.Select(Database.recipes.all, "Select raw recipe", r => view.AddRecipe(view.model, r)); } + if (gui.BuildButton("Remove unused recipes") && gui.CloseDropdown()) + { + view.model.RecordUndo().RemoveUnusedRecipes(false); + view.Rebuild(); + } + + var padding = ImGuiUtils.DefaultButtonPadding; + padding = new Padding(padding.left + 1, padding.right, padding.top, padding.bottom); + if (gui.BuildButton("... and unpack empty tables", indentLevel: 1) && gui.CloseDropdown()) + { + view.model.RecordUndo().RemoveUnusedRecipes(true); + view.Rebuild(); + } + gui.BuildText("Export inputs and outputs to blueprint with constant combinators:", wrap: true); using (gui.EnterRow()) { diff --git a/YAFCmodel/Model/ProductionTable.cs b/YAFCmodel/Model/ProductionTable.cs index 6228d8bd..09c39472 100644 --- a/YAFCmodel/Model/ProductionTable.cs +++ b/YAFCmodel/Model/ProductionTable.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Google.OrTools.LinearSolver; @@ -228,6 +229,40 @@ private void CalculateFlow(RecipeRow include) flow = flowArr; } + public void RemoveUnusedRecipes(bool unpackToo) + { + for (int i = recipes.Count - 1; i >= 0; i--) + if (recipes[i].subgroup != null) + { + recipes[i].subgroup.RemoveUnusedRecipes(unpackToo); + if (recipes[i].recipesPerSecond == 0 && recipes[i].subgroup.recipes.Count == 0) + recipes.Remove(recipes[i]); + else if (recipes[i].subgroup.recipes.Count == 0 && unpackToo) + recipes[i].subgroup = null; + else if (recipes[i].recipesPerSecond == 0) + // This table header needs to be deleted, without deleting its children. + if (recipes[i].subgroup.recipes.FirstOrDefault(r => r.subgroup == null || r.subgroup.recipes.Count == 0) is RecipeRow newParent) + { + // Promote the first child with no children + recipes[i].subgroup.recipes.Remove(newParent); + newParent.subgroup = recipes[i].subgroup; + recipes[i] = newParent; + } + else + { + // or just the first child, if necessary + newParent = recipes[i].subgroup.recipes[0]; + recipes[i].subgroup.recipes.Remove(newParent); + newParent.subgroup.recipes.AddRange(recipes[i].subgroup.recipes); + newParent.subgroup.links.AddRange(recipes[i].subgroup.links); + newParent.subgroup.RebuildLinkMap(); + recipes[i] = newParent; + } + } + else if (recipes[i].recipesPerSecond == 0) + recipes.Remove(recipes[i]); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddLinkCoef(Constraint cst, Variable var, ProductionLink link, RecipeRow recipe, float amount) { diff --git a/YAFCmodel/Model/Project.cs b/YAFCmodel/Model/Project.cs index da15cad7..d5ccd539 100644 --- a/YAFCmodel/Model/Project.cs +++ b/YAFCmodel/Model/Project.cs @@ -111,6 +111,12 @@ public void Save(string fileName) lastSavedVersion = projectVersion; } + public void RecalculateDisplayPages() + { + foreach (var page in displayPages) + FindPage(page)?.SetToRecalculate(); + } + public (float multiplier, string suffix) ResolveUnitOfMeasure(UnitOfMeasure unit) { switch (unit) diff --git a/YAFCparser/FactorioDataSource.cs b/YAFCparser/FactorioDataSource.cs index a0e7aafb..bf135928 100644 --- a/YAFCparser/FactorioDataSource.cs +++ b/YAFCparser/FactorioDataSource.cs @@ -137,10 +137,12 @@ public static Project Parse(string factorioPath, string modPath, string projectP var modSettingsPath = Path.Combine(modPath, "mod-settings.dat"); progress.Report(("Initializing", "Loading mod list")); var modListPath = Path.Combine(modPath, "mod-list.json"); + Dictionary versionSpecifiers = new Dictionary(); if (File.Exists(modListPath)) { var mods = JsonSerializer.Deserialize(File.ReadAllText(modListPath)); allMods = mods.mods.Where(x => x.enabled).Select(x => x.name).ToDictionary(x => x, x => (ModInfo) null); + versionSpecifiers = mods.mods.Where(x => x.enabled && !string.IsNullOrEmpty(x.version)).ToDictionary(x => x.name, x => Version.Parse(x.version)); } else allMods = new Dictionary {{"base", null}}; @@ -168,7 +170,7 @@ public static Project Parse(string factorioPath, string modPath, string projectP foreach (var mod in allFoundMods) { currentLoadingMod = mod.name; - if (mod.ValidForFactorioVersion(factorioVersion) && (allMods.TryGetValue(mod.name, out var existing) && (existing == null || mod.parsedVersion > existing.parsedVersion || (mod.parsedVersion == existing.parsedVersion && existing.zipArchive != null && mod.zipArchive == null)))) + if (mod.ValidForFactorioVersion(factorioVersion) && allMods.TryGetValue(mod.name, out var existing) && (existing == null || mod.parsedVersion > existing.parsedVersion || (mod.parsedVersion == existing.parsedVersion && existing.zipArchive != null && mod.zipArchive == null)) && (!versionSpecifiers.TryGetValue(mod.name, out var version) || mod.parsedVersion == version)) { existing?.Dispose(); allMods[mod.name] = mod; @@ -182,8 +184,8 @@ public static Project Parse(string factorioPath, string modPath, string projectP throw new NotSupportedException("Mod not found: "+name+". Try loading this pack in Factorio first."); mod.ParseDependencies(); } - - + + var modsToDisable = new List(); do { @@ -304,6 +306,7 @@ internal class ModEntry { public string name { get; set; } public bool enabled { get; set; } + public string version { get; set; } } internal class ModList diff --git a/YAFCui/ImGui/ImGuiUtils.cs b/YAFCui/ImGui/ImGuiUtils.cs index 17999b97..33b2bd3d 100644 --- a/YAFCui/ImGui/ImGuiUtils.cs +++ b/YAFCui/ImGui/ImGuiUtils.cs @@ -93,14 +93,18 @@ public static bool OnClick(this ImGui gui, Rect rect) return false; } - public static bool BuildButton(this ImGui gui, string text, SchemeColor color = SchemeColor.Primary, Padding? padding = null, bool active = true) + public static bool BuildButton(this ImGui gui, string text, SchemeColor color = SchemeColor.Primary, Padding? padding = null, bool active = true, int indentLevel = 0) { if (!active) color = SchemeColor.Grey; - using (gui.EnterGroup(padding ?? DefaultButtonPadding, active ? color+2 : color+3)) - gui.BuildText(text, Font.text, align:RectAlignment.Middle); - return gui.BuildButton(gui.lastRect, color, color + 1) && active; + using (gui.EnterGroup(new Padding(indentLevel * 1.5f, 0, 0, 0))) + { + using (gui.EnterGroup(padding ?? DefaultButtonPadding, active ? color + 2 : color + 3)) + gui.BuildText(text, Font.text, align: RectAlignment.Middle); + + return gui.BuildButton(gui.lastRect, color, color + 1) && active; + } } public static ButtonEvent BuildContextMenuButton(this ImGui gui, string text, string rightText = null, Icon icon = default, bool disabled = false)