diff --git a/UXAssist/FactoryPatch.cs b/UXAssist/FactoryPatch.cs index fcee32f..9899495 100644 --- a/UXAssist/FactoryPatch.cs +++ b/UXAssist/FactoryPatch.cs @@ -714,6 +714,7 @@ public static class FactoryPatch { _lastKey = KeyCode.None; } + if (!VFInput.noModifier) return; int delta; if (UpdateKeyPressed(KeyCode.LeftArrow)) { diff --git a/UXAssist/GamePatch.cs b/UXAssist/GamePatch.cs index c3f6107..adf79ee 100644 --- a/UXAssist/GamePatch.cs +++ b/UXAssist/GamePatch.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Reflection.Emit; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UXAssist.Common; +using Object = UnityEngine.Object; namespace UXAssist; @@ -15,6 +18,7 @@ public static class GamePatch public static ConfigEntry EnableWindowResizeEnabled; public static ConfigEntry LoadLastWindowRectEnabled; + public static ConfigEntry AutoSaveOptEnabled; public static ConfigEntry ConvertSavesFromPeaceEnabled; public static ConfigEntry LastWindowRect; private static Harmony _gamePatch; @@ -23,9 +27,11 @@ public static class GamePatch { EnableWindowResizeEnabled.SettingChanged += (_, _) => EnableWindowResize.Enable(EnableWindowResizeEnabled.Value); LoadLastWindowRectEnabled.SettingChanged += (_, _) => LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value); + AutoSaveOptEnabled.SettingChanged += (_, _) => AutoSaveOpt.Enable(AutoSaveOptEnabled.Value); ConvertSavesFromPeaceEnabled.SettingChanged += (_, _) => ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value); EnableWindowResize.Enable(EnableWindowResizeEnabled.Value); LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value); + AutoSaveOpt.Enable(AutoSaveOptEnabled.Value); ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value); _gamePatch ??= Harmony.CreateAndPatchAll(typeof(GamePatch)); } @@ -34,6 +40,7 @@ public static class GamePatch { LoadLastWindowRect.Enable(false); EnableWindowResize.Enable(false); + AutoSaveOpt.Enable(false); ConvertSavesFromPeace.Enable(false); _gamePatch?.UnpatchSelf(); _gamePatch = null; @@ -178,6 +185,148 @@ public static class GamePatch } } + private static class AutoSaveOpt + { + private static Harmony _patch; + + public static void Enable(bool on) + { + if (on) + { + Directory.CreateDirectory(GameConfig.gameSaveFolder + "AutoSaves/"); + _patch ??= Harmony.CreateAndPatchAll(typeof(AutoSaveOpt)); + return; + } + + _patch?.UnpatchSelf(); + _patch = null; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.AutoSave))] + private static bool GameSave_AutoSave_Prefix(ref bool __result) + { + if (!GameSave.SaveCurrentGame(GameSave.AutoSaveTmp)) + { + GlobalObject.SaveOpCounter(); + __result = false; + return false; + } + + var tmpFilename = GameConfig.gameSaveFolder + GameSave.AutoSaveTmp + GameSave.saveExt; + var targetFilename = $"{GameConfig.gameSaveFolder}AutoSaves/[{GameMain.data.gameDesc.clusterString}] {DateTime.Now:yyyy-MM-dd_hh-mm-ss}{GameSave.saveExt}"; + File.Move(tmpFilename, targetFilename); + __result = true; + return false; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.RefreshList))] + public static void UILoadGameWindow_RefreshList_Postfix(UILoadGameWindow __instance) + { + var baseDir = GameConfig.gameSaveFolder + "AutoSaves/"; + var files = Directory.GetFiles(baseDir, "*" + GameSave.saveExt, SearchOption.TopDirectoryOnly); + var entries = __instance.entries; + var entries2 = new List(); + var entryPrefab = __instance.entryPrefab; + var entryPrefabParent = entryPrefab.transform.parent; + foreach (var f in files) + { + var fileInfo = new FileInfo(f); + var entry = Object.Instantiate(entryPrefab, entryPrefabParent); + entry.fileInfo = fileInfo; + entries2.Add(entry); + } + entries2.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate)); + if (entries2.Count > 10) + entries2.RemoveRange(10, entries2.Count - 10); + var autoSaveText = ">> " + "自动存档条目".Translate(); + foreach (var entry in entries2) + { + entry.indexText.text = ""; + var saveName = entry.saveName; + entry._saveName = $"AutoSaves/{saveName}"; + var quoteIndex = saveName.IndexOf('['); + if (quoteIndex >= 0) + { + var quoteIndex2 = saveName.IndexOf(']', quoteIndex + 1); + if (quoteIndex2 > 0) saveName = saveName.Substring(quoteIndex, quoteIndex2 + 1 - quoteIndex); + } + entry.nameText.text = $"{autoSaveText} {saveName}"; + entry.nameText.fontStyle = FontStyle.Italic; + entry.nameText.color = new Color(1f, 1f, 1f, 0.7f); + entry.timeText.text = $"{entry.fileDate:yyyy-MM-dd HH:mm:ss}"; + GameSave.ReadModes(entry.fileInfo.FullName, out var isSandbox, out var isPeace); + if (entry.sandboxIcon != null) + { + entry.sandboxIcon.gameObject.SetActive(isSandbox); + } + if (entry.combatIcon != null) + { + entry.combatIcon.gameObject.SetActive(!isPeace); + } + entry.selected = false; + entry.gameObject.SetActive(true); + } + entries.AddRange(entries2); + entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate)); + var displayIndex = 1; + for (var i = 0; i < entries.Count; i++) + { + var entry = entries[i]; + entry.index = i + 1; + entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i); + if (string.IsNullOrEmpty(entry.indexText.text)) continue; + entry.indexText.text = displayIndex.ToString(); + displayIndex++; + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.RefreshList))] + public static void UISaveGameWindow_RefreshList_Postfix(UISaveGameWindow __instance) + { + var entries = __instance.entries; + entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate)); + for (var i = 0; i < entries.Count; i++) + { + var entry = entries[i]; + entry.index = i + 1; + entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i); + entry.indexText.text = (i + 1).ToString(); + } + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.DoLoadSelectedGame))] + [HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.OnSelectedChange))] + private static IEnumerable UILoadGameWindow_ReplaceSaveName_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.Start().MatchForward(false, + new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry.saveName))) + ); + matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldfld, AccessTools.Field(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry._saveName)))); + return matcher.InstructionEnumeration(); + } + [HarmonyTranspiler] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadCurrentGame))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadGameDesc))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeader))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeaderAndDescAndProperty))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.SaveExist))] + [HarmonyPatch(typeof(GameSave), nameof(GameSave.SavePath))] + private static IEnumerable GameSave_RemoveValidateOnLoad_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.Start().MatchForward(false, + new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(CommonUtils), nameof(CommonUtils.ValidFileName))) + ); + matcher.RemoveInstruction(); + return matcher.InstructionEnumeration(); + } + } + private static class ConvertSavesFromPeace { private static Harmony _patch; diff --git a/UXAssist/README.md b/UXAssist/README.md index 746023a..7f10285 100644 --- a/UXAssist/README.md +++ b/UXAssist/README.md @@ -4,6 +4,10 @@ #### 一些提升用户体验的功能和补丁 ## Changlog +* 1.0.9 + + New function: `Better auto-save mechanism` + - Auto saves are stored in 'Save\AutoSaves' folder, filenames are combined with cluster address and date-time + - Note: this will sort gamesaves by modified time on save/load window, so you don't have to use [DSP_Save_Game_Sorter] anymore * 1.0.8 + New function: `Enhanced control for logistic storage limits` * 1.0.7 @@ -47,6 +51,9 @@ + General - Enable game window resize - Remember window position and size on last exit + - Better auto-save mechanism + - Auto saves are stored in 'Save\AutoSaves' folder, filenames are combined with cluster address and date-time + - Note: this will sort gamesaves by modified time on save/load window, so you don't have to use [DSP_Save_Game_Sorter] anymore - Convert Peace-Mode saves to Combat-Mode on loading + Planet/Factory - Unlimited interactive range @@ -81,6 +88,10 @@ * [OffGridConstruction](https://github.com/Velociraptor115-DSPModding/OffGridConstruction): Off-grid building & stepped rotation implementations ## 更新日志 +* 1.0.9 + + 新功能:`更好的自动保存机制` + - 自动存档会以星区地址和日期时间组合为文件名存储在'Save\AutoSaves'文件夹中 + - 注意:此功能会在保存/读取菜单按最后修改时间对存档进行排序,因此你不再需要[DSP_Save_Game_Sorter]了 * 1.0.8 + 新功能:`物流塔存储数量限制控制改进` * 1.0.7 @@ -124,6 +135,9 @@ + 通用 - 可调整游戏窗口大小(可最大化和拖动边框) - 记住上次退出时的窗口位置和大小 + - 更好的自动保存机制 + - 自动存档会以星区地址和日期时间组合为文件名存储在'Save\AutoSaves'文件夹中 + - 注意:此功能会在保存/读取菜单按最后修改时间对存档进行排序,因此你不再需要[DSP_Save_Game_Sorter]了 - 在加载和平模式存档时将其转换为战斗模式 + 行星/工厂 - 无限交互距离 diff --git a/UXAssist/UIConfigWindow.cs b/UXAssist/UIConfigWindow.cs index 2d75a31..bad074b 100644 --- a/UXAssist/UIConfigWindow.cs +++ b/UXAssist/UIConfigWindow.cs @@ -18,6 +18,8 @@ public static class UIConfigWindow I18N.Add("Dyson Sphere", "Dyson Sphere", "戴森球"); I18N.Add("Enable game window resize", "Enable game window resize (maximum box and thick frame)", "可调整游戏窗口大小(可最大化和拖动边框)"); I18N.Add("Remeber window position and size on last exit", "Remeber window position and size on last exit", "记住上次退出时的窗口位置和大小"); + I18N.Add("Better auto-save mechanism", "Better auto-save mechanism", "更好的自动存档机制"); + I18N.Add("Better auto-save mechanism tips", "Auto saves are stored in 'Save\\AutoSaves' folder, filenames are combined with cluster address and date-time", "自动存档会以星区地址和日期时间组合为文件名存储在'Save\\AutoSaves'文件夹中"); I18N.Add("Convert old saves to Combat Mode on loading", "Convert old saves to Combat Mode on loading (Use settings in new game panel)", "读取旧档时转为战斗模式(使用新游戏面板的战斗难度设置)"); I18N.Add("Unlimited interactive range", "Unlimited interactive range", "无限交互距离"); I18N.Add("Night Light", "Sunlight at night", "夜间日光灯"); @@ -61,6 +63,12 @@ public static class UIConfigWindow y += 36f; MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.LoadLastWindowRectEnabled, "Remeber window position and size on last exit"); y += 36f; + MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.AutoSaveOptEnabled, "Better auto-save mechanism"); + x = 200f; + y += 6f; + MyWindow.AddTipsButton(x, y, tab1, "Better auto-save mechanism", "Better auto-save mechanism tips", "auto-save-opt-tips"); + x = 0f; + y += 30f; MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.ConvertSavesFromPeaceEnabled, "Convert old saves to Combat Mode on loading"); x += 10f; y = 278f; diff --git a/UXAssist/UXAssist.cs b/UXAssist/UXAssist.cs index e016c72..8c07c83 100644 --- a/UXAssist/UXAssist.cs +++ b/UXAssist/UXAssist.cs @@ -32,6 +32,8 @@ public class UXAssist : BaseUnityPlugin "Load last window position and size when game starts"); GamePatch.LastWindowRect = Config.Bind("Game", "LastWindowRect", new Vector4(0f, 0f, 0f, 0f), "Last window position and size"); + GamePatch.AutoSaveOptEnabled = Config.Bind("Game", "AutoSaveOpt", false, + "Better auto-save mechanism"); GamePatch.ConvertSavesFromPeaceEnabled = Config.Bind("Game", "ConvertSavesFromPeace", false, "Convert saves from Peace mode to Combat mode on save loading"); FactoryPatch.UnlimitInteractiveEnabled = Config.Bind("Factory", "UnlimitInteractive", false, diff --git a/UXAssist/UXAssist.csproj b/UXAssist/UXAssist.csproj index 0b8c1f7..0a59fe5 100644 --- a/UXAssist/UXAssist.csproj +++ b/UXAssist/UXAssist.csproj @@ -4,7 +4,7 @@ net472 org.soardev.uxassist DSP MOD - UXAssist - 1.0.8 + 1.0.9 true latest UXAssist diff --git a/UXAssist/package/manifest.json b/UXAssist/package/manifest.json index 31f1661..a83b9e2 100644 --- a/UXAssist/package/manifest.json +++ b/UXAssist/package/manifest.json @@ -1,6 +1,6 @@ { "name": "UXAssist", - "version_number": "1.0.8", + "version_number": "1.0.9", "website_url": "https://github.com/soarqin/DSP_Mods/tree/master/UXAssist", "description": "Some functions and patches for better user experience / 一些提升用户体验的功能和补丁", "dependencies": [