From 41f2103c112c5e3e0096cb2b94eb8e4dc20f32bd Mon Sep 17 00:00:00 2001 From: Soar Qin Date: Mon, 9 Oct 2023 21:23:23 +0800 Subject: [PATCH] UXAssist: Work in progress --- CheatEnabler/CheatEnabler.cs | 143 +--------- CheatEnabler/CheatEnabler.csproj | 4 + CheatEnabler/DevShortcuts.cs | 1 - CheatEnabler/README.md | 20 +- CheatEnabler/UIConfigWindow.cs | 201 ++++++------- DSP_Mods.sln | 6 + {CheatEnabler => UXAssist/Common}/I18N.cs | 2 +- UXAssist/README.md | 200 +++++++++++++ {CheatEnabler => UXAssist}/UI/MyCheckbox.cs | 2 +- UXAssist/UI/MyConfigWindow.cs | 64 +++++ {CheatEnabler => UXAssist}/UI/MyKeyBinder.cs | 2 +- {CheatEnabler => UXAssist}/UI/MySlider.cs | 2 +- {CheatEnabler => UXAssist}/UI/MyWindow.cs | 30 +- {CheatEnabler => UXAssist}/UI/Util.cs | 2 +- UXAssist/UIConfigWindow.cs | 26 ++ UXAssist/UXAssist.cs | 285 +++++++++++++++++++ UXAssist/UXAssist.csproj | 28 ++ UXAssist/package/icon.png | Bin 0 -> 26471 bytes UXAssist/package/manifest.json | 9 + 19 files changed, 740 insertions(+), 287 deletions(-) rename {CheatEnabler => UXAssist/Common}/I18N.cs (98%) create mode 100644 UXAssist/README.md rename {CheatEnabler => UXAssist}/UI/MyCheckbox.cs (98%) create mode 100644 UXAssist/UI/MyConfigWindow.cs rename {CheatEnabler => UXAssist}/UI/MyKeyBinder.cs (99%) rename {CheatEnabler => UXAssist}/UI/MySlider.cs (99%) rename {CheatEnabler => UXAssist}/UI/MyWindow.cs (91%) rename {CheatEnabler => UXAssist}/UI/Util.cs (98%) create mode 100644 UXAssist/UIConfigWindow.cs create mode 100644 UXAssist/UXAssist.cs create mode 100644 UXAssist/UXAssist.csproj create mode 100644 UXAssist/package/icon.png create mode 100644 UXAssist/package/manifest.json diff --git a/CheatEnabler/CheatEnabler.cs b/CheatEnabler/CheatEnabler.cs index f69c8d0..e2698d5 100644 --- a/CheatEnabler/CheatEnabler.cs +++ b/CheatEnabler/CheatEnabler.cs @@ -5,9 +5,11 @@ using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UnityEngine.UI; +using UXAssist.Common; namespace CheatEnabler; +[BepInDependency("org.soardev.uxassist")] [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] public class CheatEnabler : BaseUnityPlugin { @@ -15,13 +17,6 @@ public class CheatEnabler : BaseUnityPlugin BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME); public static ConfigEntry Hotkey; - private static bool _configWinInitialized = false; - private static UIConfigWindow _configWin; - - private static Harmony _windowPatch; - private static Harmony _patch; - - private static bool _initialized; private void Awake() { @@ -111,10 +106,8 @@ public class CheatEnabler : BaseUnityPlugin I18N.Init(); I18N.Add("CheatEnabler Config", "CheatEnabler Config", "CheatEnabler设置"); I18N.Apply(); - - // UI Patch - _windowPatch ??= Harmony.CreateAndPatchAll(typeof(UI.MyWindowManager.Patch)); - _patch ??= Harmony.CreateAndPatchAll(typeof(CheatEnabler)); + + UIConfigWindow.Init(); DevShortcuts.Init(); AbnormalDisabler.Init(); @@ -140,138 +133,10 @@ public class CheatEnabler : BaseUnityPlugin TechPatch.Uninit(); AbnormalDisabler.Uninit(); DevShortcuts.Uninit(); - _patch?.UnpatchSelf(); - _patch = null; - _windowPatch?.UnpatchSelf(); - _windowPatch = null; - } - - private void Update() - { - if (VFInput.inputing) return; - if (Hotkey.Value.IsDown()) ToggleConfigWindow(); } private void LateUpdate() { FactoryPatch.NightLight.LateUpdate(); } - - [HarmonyPostfix, HarmonyPatch(typeof(UIRoot), nameof(UIRoot.OpenMainMenuUI))] - public static void UIRoot_OpenMainMenuUI_Postfix() - { - if (_initialized) return; - { - var mainMenu = UIRoot.instance.uiMainMenu; - var src = mainMenu.newGameButton; - var parent = src.transform.parent; - var btn = Instantiate(src, parent); - btn.name = "button-cheatenabler-config"; - var l = btn.text.GetComponent(); - if (l != null) - { - l.stringKey = "CheatEnabler Config"; - l.translation = "CheatEnabler Config".Translate(); - } - btn.text.text = "CheatEnabler Config".Translate(); - btn.text.fontSize = btn.text.fontSize * 7 / 8; - I18N.OnInitialized += () => { btn.text.text = "CheatEnabler Config".Translate(); }; - var vec = ((RectTransform)mainMenu.exitButton.transform).anchoredPosition3D; - var vec2 = ((RectTransform)mainMenu.creditsButton.transform).anchoredPosition3D; - var transform1 = (RectTransform)btn.transform; - transform1.anchoredPosition3D = new Vector3(vec.x, vec.y + (vec.y - vec2.y) * 2, vec.z); - btn.button.onClick.RemoveAllListeners(); - btn.button.onClick.AddListener(ToggleConfigWindow); - } - { - var panel = UIRoot.instance.uiGame.planetGlobe; - var src = panel.button2; - var sandboxMenu = UIRoot.instance.uiGame.sandboxMenu; - var icon = sandboxMenu.categoryButtons[6].transform.Find("icon")?.GetComponent()?.sprite; - var b = Instantiate(src, src.transform.parent); - var panelButtonGo = b.gameObject; - var rect = (RectTransform)panelButtonGo.transform; - var btn = panelButtonGo.GetComponent(); - var img = panelButtonGo.transform.Find("button-2/icon")?.GetComponent(); - if (img != null) - { - img.sprite = icon; - } - if (panelButtonGo != null && btn != null) - { - panelButtonGo.name = "open-cheatenabler-config"; - rect.localScale = new Vector3(0.5f, 0.5f, 0.5f); - rect.anchoredPosition3D = new Vector3(128f, -105f, 0f); - b.onClick.RemoveAllListeners(); - btn.onClick += _ => { ToggleConfigWindow(); }; - btn.tips.tipTitle = "CheatEnabler Config"; - I18N.OnInitialized += () => { btn.tips.tipTitle = "CheatEnabler Config".Translate(); }; - btn.tips.tipText = null; - btn.tips.corner = 9; - btn.tips.offset = new Vector2(-20f, -20f); - panelButtonGo.SetActive(true); - } - } - _initialized = true; - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(UIBuildMenu), nameof(UIBuildMenu._OnUpdate))] - private static IEnumerable UIBuildMenu__OnUpdate_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.inScreen))) - ); - matcher.Repeat(codeMatcher => - { - var jumpPos = codeMatcher.Advance(1).Operand; - codeMatcher.Advance(-1).InsertAndAdvance( - new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.noModifier))), - new CodeInstruction(OpCodes.Brfalse_S, jumpPos) - ).Advance(2); - }); - return matcher.InstructionEnumeration(); - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(UIButton), nameof(UIButton.LateUpdate))] - private static IEnumerable UIButton_LateUpdate_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldloc_2), - new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.gameObject))), - new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(GameObject), nameof(GameObject.activeSelf))) - ); - var labels = matcher.Labels; - matcher.Labels = null; - matcher.Insert( - new CodeInstruction(OpCodes.Ldloc_2).WithLabels(labels), - new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.transform))), - new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))), - new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))), - new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Transform), nameof(Transform.SetAsLastSibling))) - ); - return matcher.InstructionEnumeration(); - } - - private static void ToggleConfigWindow() - { - if (!_configWinInitialized) - { - if (!I18N.Initialized()) return; - _configWinInitialized = true; - _configWin = UIConfigWindow.CreateInstance(); - } - - if (_configWin.active) - { - _configWin._Close(); - } - else - { - _configWin.Open(); - } - } } diff --git a/CheatEnabler/CheatEnabler.csproj b/CheatEnabler/CheatEnabler.csproj index e2b1493..a230340 100644 --- a/CheatEnabler/CheatEnabler.csproj +++ b/CheatEnabler/CheatEnabler.csproj @@ -23,6 +23,10 @@ + + + + diff --git a/CheatEnabler/DevShortcuts.cs b/CheatEnabler/DevShortcuts.cs index 4177067..2f7ff0d 100644 --- a/CheatEnabler/DevShortcuts.cs +++ b/CheatEnabler/DevShortcuts.cs @@ -80,7 +80,6 @@ public static class DevShortcuts matcher.RemoveInstructions(2); matcher.Opcode = OpCodes.Br; matcher.Labels = labels; - CheatEnabler.Logger.LogDebug($"{matcher.Pos}"); return matcher.InstructionEnumeration(); } } diff --git a/CheatEnabler/README.md b/CheatEnabler/README.md index a1844f0..7dc8957 100644 --- a/CheatEnabler/README.md +++ b/CheatEnabler/README.md @@ -156,33 +156,33 @@ + 工厂: + 建造秒完成 + 建筑师模式(无限建筑) - + 无限交互距离 + + ** 无限交互距离 + 无条件建造 - + 移除部分不影响游戏逻辑的建造条件 + + ** 移除部分不影响游戏逻辑的建造条件 + 无碰撞 - + 夜间日光灯 + + ** 夜间日光灯 + 传送带信号物品生成 + 统计面板中计算所有原材料和中间产物 + 传送带信号替换格式 + 风力发电机和太阳能板无间距限制 + 提升各种发电设备发电量 + 行星: - + 在行星视图中允许玩家操作 + + ** 在行星视图中允许玩家操作 + 自然资源采集不消耗 + 高速采集 + 平地抽水 + 沙土不够时依然可以整改地形 - + 初始化本行星(不重置矿脉) - + 快速拆除所有建筑(不掉落) + + ** 初始化本行星(不重置矿脉) + + ** 快速拆除所有建筑(不掉落) + 戴森球: - + 可用节点全部造完时停止弹射 - + 只建造节点不建造框架 + + ** 可用节点全部造完时停止弹射 + + ** 只建造节点不建造框架 + 跳过子弹阶段 + 跳过吸收阶段 + 快速吸收 + 全球弹射 - + 初始化戴森球 - + 快速拆除戴森壳 + + ** 初始化戴森球 + + ** 快速拆除戴森壳 + 母星系: + 母星有稀有资源 + 母星是纯平的 diff --git a/CheatEnabler/UIConfigWindow.cs b/CheatEnabler/UIConfigWindow.cs index 4b441bc..dea18a7 100644 --- a/CheatEnabler/UIConfigWindow.cs +++ b/CheatEnabler/UIConfigWindow.cs @@ -1,15 +1,19 @@ using UnityEngine; +using UXAssist.UI; +using UXAssist.Common; namespace CheatEnabler; -public class UIConfigWindow : UI.MyWindowWithTabs +public static class UIConfigWindow { - private RectTransform _windowTrans; + private static RectTransform _windowTrans; + private static MyConfigWindow _configWindow; - private UIButton _resignGameBtn; - private readonly UIButton[] _dysonLayerBtn = new UIButton[10]; + private static RectTransform _tab4; + private static UIButton _resignGameBtn; + private static readonly UIButton[] _dysonLayerBtn = new UIButton[10]; - static UIConfigWindow() + public static void Init() { I18N.Add("General", "General", "常规"); I18N.Add("Enable Dev Shortcuts", "Enable Dev Shortcuts", "开发模式快捷键"); @@ -75,70 +79,61 @@ public class UIConfigWindow : UI.MyWindowWithTabs I18N.Add("Birth planet is solid flat (no water at all)", "Birth planet is solid flat (no water at all)", "母星是纯平的(没有水)"); I18N.Add("Birth star has high luminosity", "Birth star has high luminosity", "母星系恒星高亮"); I18N.Apply(); + MyConfigWindow.OnUICreated += CreateUI; + MyConfigWindow.OnUpdateUI += UpdateUI; } - public static UIConfigWindow CreateInstance() - { - return UI.MyWindowManager.CreateWindow("CEConfigWindow", "CheatEnabler Config"); - } - - public override void _OnCreate() - { - _windowTrans = GetComponent(); - _windowTrans.sizeDelta = new Vector2(580f, 420f); - - CreateUI(); - } - - private void CreateUI() + private static void CreateUI(MyConfigWindow wnd, RectTransform trans) { + _configWindow = wnd; + _windowTrans = trans; // General tab var x = 0f; var y = 10f; - var tab1 = AddTab(36f, 0, _windowTrans, "General"); - UI.MyCheckBox.CreateCheckBox(x, y, tab1, DevShortcuts.Enabled, "Enable Dev Shortcuts"); + var tab1 = wnd.AddTab(_windowTrans, "General"); + MyCheckBox.CreateCheckBox(x, y, tab1, DevShortcuts.Enabled, "Enable Dev Shortcuts"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab1, AbnormalDisabler.Enabled, "Disable Abnormal Checks"); + MyCheckBox.CreateCheckBox(x, y, tab1, AbnormalDisabler.Enabled, "Disable Abnormal Checks"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab1, TechPatch.Enabled, "Unlock Tech with Key-Modifiers"); + MyCheckBox.CreateCheckBox(x, y, tab1, TechPatch.Enabled, "Unlock Tech with Key-Modifiers"); y += 118f; - UI.MyKeyBinder.CreateKeyBinder(x, y, tab1, CheatEnabler.Hotkey, "Hotkey"); + MyKeyBinder.CreateKeyBinder(x, y, tab1, CheatEnabler.Hotkey, "Hotkey"); x = 156f; y = 16f; - AddTipsButton(x, y, tab1, "Dev Shortcuts", "Dev Shortcuts Tips", "dev-shortcuts-tips"); + MyWindow.AddTipsButton(x, y, tab1, "Dev Shortcuts", "Dev Shortcuts Tips", "dev-shortcuts-tips"); x += 52f; y += 72f; - AddTipsButton(x, y, tab1, "Unlock Tech with Key-Modifiers", "Unlock Tech with Key-Modifiers Tips", "unlock-tech-tips"); + MyWindow.AddTipsButton(x, y, tab1, "Unlock Tech with Key-Modifiers", "Unlock Tech with Key-Modifiers Tips", "unlock-tech-tips"); x = 300f; y = 10f; - _resignGameBtn = AddButton(x, y, tab1, "Assign game to current account", 16, "resign-game-btn", () => { GameMain.data.account = AccountData.me; }); + _resignGameBtn = wnd.AddButton(x, y, tab1, "Assign game to current account", 16, "resign-game-btn", () => { GameMain.data.account = AccountData.me; }); var rect = (RectTransform)_resignGameBtn.transform; rect.sizeDelta = new Vector2(200f, rect.sizeDelta.y); - var tab2 = AddTab(136f, 1, _windowTrans, "Factory"); + var tab2 = wnd.AddTab(_windowTrans, "Factory"); x = 0f; y = 10f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.ImmediateEnabled, "Finish build immediately"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.ImmediateEnabled, "Finish build immediately"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.ArchitectModeEnabled, "Architect mode"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.ArchitectModeEnabled, "Architect mode"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.UnlimitInteractiveEnabled, "Unlimited interactive range"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.UnlimitInteractiveEnabled, "Unlimited interactive range"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NoConditionEnabled, "Build without condition"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NoConditionEnabled, "Build without condition"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NoCollisionEnabled, "No collision"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NoCollisionEnabled, "No collision"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NightLightEnabled, "Night Light"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NightLightEnabled, "Night Light"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalGeneratorEnabled, "Belt signal generator"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalGeneratorEnabled, "Belt signal generator"); y += 26f; x += 26f; - var cb = UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalCountRecipeEnabled, "Count all raws and intermediates in statistics", 13); + var cb = MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalCountRecipeEnabled, "Count all raws and intermediates in statistics", 13); y += 26f; - var cb2 = UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalNumberAltFormat, "Belt signal alt format", 13); + var cb2 = MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalNumberAltFormat, "Belt signal alt format", 13); x += 180f; y += 6f; - var tip1 = AddTipsButton(x, y, tab2, "Belt signal alt format", "Belt signal alt format tips", "belt-signal-alt-format-tips"); + var tip1 = MyWindow.AddTipsButton(x, y, tab2, "Belt signal alt format", "Belt signal alt format tips", "belt-signal-alt-format-tips"); FactoryPatch.BeltSignalGeneratorEnabled.SettingChanged += (_, _) => { @@ -147,41 +142,41 @@ public class UIConfigWindow : UI.MyWindowWithTabs OnBeltSignalChanged(); x = 240f; y = 10f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.RemoveSomeConditionEnabled, "Remove some build conditions"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.RemoveSomeConditionEnabled, "Remove some build conditions"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.RemovePowerSpaceLimitEnabled, "Remove power space limit"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.RemovePowerSpaceLimitEnabled, "Remove power space limit"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostWindPowerEnabled, "Boost wind power"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostWindPowerEnabled, "Boost wind power"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostSolarPowerEnabled, "Boost solar power"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostSolarPowerEnabled, "Boost solar power"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostGeothermalPowerEnabled, "Boost geothermal power"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostGeothermalPowerEnabled, "Boost geothermal power"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostFuelPowerEnabled, "Boost fuel power"); + MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostFuelPowerEnabled, "Boost fuel power"); x += 32f; y += 26f; - AddText(x, y, tab2, "Boost fuel power 2", 13); + MyWindow.AddText(x, y, tab2, "Boost fuel power 2", 13); // Planet Tab - var tab3 = AddTab(236f, 2, _windowTrans, "Planet"); + var tab3 = wnd.AddTab(_windowTrans, "Planet"); x = 0f; y = 10f; - UI.MyCheckBox.CreateCheckBox(x, y, tab3, PlanetFunctions.PlayerActionsInGlobeViewEnabled, "Enable player actions in globe view"); + MyCheckBox.CreateCheckBox(x, y, tab3, PlanetFunctions.PlayerActionsInGlobeViewEnabled, "Enable player actions in globe view"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab3, ResourcePatch.InfiniteResourceEnabled, "Infinite Natural Resources"); + MyCheckBox.CreateCheckBox(x, y, tab3, ResourcePatch.InfiniteResourceEnabled, "Infinite Natural Resources"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab3, ResourcePatch.FastMiningEnabled, "Fast Mining"); + MyCheckBox.CreateCheckBox(x, y, tab3, ResourcePatch.FastMiningEnabled, "Fast Mining"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab3, WaterPumperPatch.Enabled, "Pump Anywhere"); + MyCheckBox.CreateCheckBox(x, y, tab3, WaterPumperPatch.Enabled, "Pump Anywhere"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab3, TerraformPatch.Enabled, "Terraform without enough sands"); + MyCheckBox.CreateCheckBox(x, y, tab3, TerraformPatch.Enabled, "Terraform without enough sands"); x = 300f; y = 10f; - AddButton(x, y, tab3, "矿物掩埋标题", 16, "button-bury-all", () => { PlanetFunctions.BuryAllVeins(true); }); + wnd.AddButton(x, y, tab3, "矿物掩埋标题", 16, "button-bury-all", () => { PlanetFunctions.BuryAllVeins(true); }); y += 36f; - AddButton(x, y, tab3, "矿物还原标题", 16, "button-bury-restore-all", () => { PlanetFunctions.BuryAllVeins(false); }); + wnd.AddButton(x, y, tab3, "矿物还原标题", 16, "button-bury-restore-all", () => { PlanetFunctions.BuryAllVeins(false); }); y += 36f; - AddButton(x, y, tab3, "铺满地基提示", 16, "button-reform-all", () => + wnd.AddButton(x, y, tab3, "铺满地基提示", 16, "button-reform-all", () => { var player = GameMain.mainPlayer; if (player == null) return; @@ -191,45 +186,45 @@ public class UIConfigWindow : UI.MyWindowWithTabs GameMain.localPlanet.factory.PlanetReformAll(reformTool.brushType, reformTool.brushColor, reformTool.buryVeins); }); y += 36f; - AddButton(x, y, tab3, "还原地形提示", 16, "button-reform-revert-all", () => + wnd.AddButton(x, y, tab3, "还原地形提示", 16, "button-reform-revert-all", () => { var factory = GameMain.localPlanet?.factory; if (factory == null) return; GameMain.localPlanet.factory.PlanetReformRevert(); }); y += 36f; - AddButton(x, y, tab3, "Initialize This Planet", 16, "button-init-planet", () => { PlanetFunctions.RecreatePlanet(true); }); + wnd.AddButton(x, y, tab3, "Initialize This Planet", 16, "button-init-planet", () => { PlanetFunctions.RecreatePlanet(true); }); y += 36f; - AddButton(x, y, tab3, "Dismantle All Buildings", 16, "button-dismantle-all", () => { PlanetFunctions.DismantleAll(false); }); + wnd.AddButton(x, y, tab3, "Dismantle All Buildings", 16, "button-dismantle-all", () => { PlanetFunctions.DismantleAll(false); }); - var tab4 = AddTab(336f, 3, _windowTrans, "Dyson Sphere"); + var tab4 = wnd.AddTab(_windowTrans, "Dyson Sphere"); x = 0f; y = 10f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.StopEjectOnNodeCompleteEnabled, "Stop ejectors when available nodes are all filled up"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.StopEjectOnNodeCompleteEnabled, "Stop ejectors when available nodes are all filled up"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OnlyConstructNodesEnabled, "Construct only nodes but frames"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OnlyConstructNodesEnabled, "Construct only nodes but frames"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.SkipBulletEnabled, "Skip bullet period"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.SkipBulletEnabled, "Skip bullet period"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.SkipAbsorbEnabled, "Skip absorption period"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.SkipAbsorbEnabled, "Skip absorption period"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.QuickAbsorbEnabled, "Quick absorb"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.QuickAbsorbEnabled, "Quick absorb"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.EjectAnywayEnabled, "Eject anyway"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.EjectAnywayEnabled, "Eject anyway"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OverclockEjectorEnabled, "Overclock Ejectors"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OverclockEjectorEnabled, "Overclock Ejectors"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OverclockSiloEnabled, "Overclock Silos"); + MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OverclockSiloEnabled, "Overclock Silos"); x = 300f; y = 10f; - AddButton(x, y, tab4, "Initialize Dyson Sphere", 16, "init-dyson-sphere", () => { DysonSpherePatch.InitCurrentDysonSphere(-1); }); + wnd.AddButton(x, y, tab4, "Initialize Dyson Sphere", 16, "init-dyson-sphere", () => { DysonSpherePatch.InitCurrentDysonSphere(-1); }); y += 36f; - AddText(x, y, tab4, "Click to dismantle selected layer", 16, "text-dismantle-layer"); + MyWindow.AddText(x, y, tab4, "Click to dismantle selected layer", 16, "text-dismantle-layer"); y += 26f; for (var i = 0; i < 10; i++) { var id = i + 1; - var btn = AddFlatButton(x, y, tab4, id.ToString(), 12, "dismantle-layer-" + id, () => { DysonSpherePatch.InitCurrentDysonSphere(id); }); + var btn = wnd.AddFlatButton(x, y, tab4, id.ToString(), 12, "dismantle-layer-" + id, () => { DysonSpherePatch.InitCurrentDysonSphere(id); }); ((RectTransform)btn.transform).sizeDelta = new Vector2(40f, 20f); _dysonLayerBtn[i] = btn; if (i == 4) @@ -243,32 +238,31 @@ public class UIConfigWindow : UI.MyWindowWithTabs } } - var tab5 = AddTab(436f, 4, _windowTrans, "Birth"); + var tab5 = wnd.AddTab(_windowTrans, "Birth"); x = 0f; y = 10f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.SitiVeinsOnBirthPlanet, "Silicon/Titanium on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.SitiVeinsOnBirthPlanet, "Silicon/Titanium on birth planet"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.FireIceOnBirthPlanet, "Fire ice on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.FireIceOnBirthPlanet, "Fire ice on birth planet"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.KimberliteOnBirthPlanet, "Kimberlite on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.KimberliteOnBirthPlanet, "Kimberlite on birth planet"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.FractalOnBirthPlanet, "Fractal silicon on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.FractalOnBirthPlanet, "Fractal silicon on birth planet"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.OrganicOnBirthPlanet, "Organic crystal on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.OrganicOnBirthPlanet, "Organic crystal on birth planet"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.OpticalOnBirthPlanet, "Optical grating crystal on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.OpticalOnBirthPlanet, "Optical grating crystal on birth planet"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.SpiniformOnBirthPlanet, "Spiniform stalagmite crystal on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.SpiniformOnBirthPlanet, "Spiniform stalagmite crystal on birth planet"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.UnipolarOnBirthPlanet, "Unipolar magnet on birth planet"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.UnipolarOnBirthPlanet, "Unipolar magnet on birth planet"); x = 200f; y = 10f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.FlatBirthPlanet, "Birth planet is solid flat (no water at all)"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.FlatBirthPlanet, "Birth planet is solid flat (no water at all)"); y += 36f; - UI.MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.HighLuminosityBirthStar, "Birth star has high luminosity"); + MyCheckBox.CreateCheckBox(x, y, tab5, BirthPlanetPatch.HighLuminosityBirthStar, "Birth star has high luminosity"); - SetCurrentTab(0); - UpdateUI(); + _tab4 = tab4; return; void OnBeltSignalChanged() @@ -280,22 +274,22 @@ public class UIConfigWindow : UI.MyWindowWithTabs } } - public void UpdateUI() + private static void UpdateUI() { UpdateResignButton(); UpdateDysonShells(); } - private void UpdateResignButton() + private static void UpdateResignButton() { var resignEnabled = GameMain.data.account != AccountData.me; if (_resignGameBtn.gameObject.activeSelf == resignEnabled) return; _resignGameBtn.gameObject.SetActive(resignEnabled); } - private void UpdateDysonShells() + private static void UpdateDysonShells() { - if (!Tabs[3].Item1.gameObject.activeSelf) return; + if (!_tab4.gameObject.activeSelf) return; var star = GameMain.localStar; if (star != null) { @@ -318,39 +312,4 @@ public class UIConfigWindow : UI.MyWindowWithTabs _dysonLayerBtn[i].button.interactable = false; } } - - public override void _OnDestroy() - { - } - - public override bool _OnInit() - { - _windowTrans.anchoredPosition = new Vector2(0, 0); - return true; - } - - public override void _OnFree() - { - } - - public override void _OnOpen() - { - } - - public override void _OnClose() - { - } - - public override void _OnUpdate() - { - base._OnUpdate(); - if (VFInput.escape && !VFInput.inputing) - { - VFInput.UseEscape(); - _Close(); - return; - } - - UpdateUI(); - } } \ No newline at end of file diff --git a/DSP_Mods.sln b/DSP_Mods.sln index f33bb42..7379bb3 100644 --- a/DSP_Mods.sln +++ b/DSP_Mods.sln @@ -29,6 +29,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DustbinPreloader", "Dustbin EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoolOpt", "PoolOpt\PoolOpt.csproj", "{8BE61246-2C9D-4088-AA33-5AFF22C5046E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UXAssist", "UXAssist\UXAssist.csproj", "{9C048229-6A50-4642-BC5E-02CD39D3869A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,5 +89,9 @@ Global {8BE61246-2C9D-4088-AA33-5AFF22C5046E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BE61246-2C9D-4088-AA33-5AFF22C5046E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BE61246-2C9D-4088-AA33-5AFF22C5046E}.Release|Any CPU.Build.0 = Release|Any CPU + {9C048229-6A50-4642-BC5E-02CD39D3869A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C048229-6A50-4642-BC5E-02CD39D3869A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C048229-6A50-4642-BC5E-02CD39D3869A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C048229-6A50-4642-BC5E-02CD39D3869A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/CheatEnabler/I18N.cs b/UXAssist/Common/I18N.cs similarity index 98% rename from CheatEnabler/I18N.cs rename to UXAssist/Common/I18N.cs index bcab0b1..2a7cbb8 100644 --- a/CheatEnabler/I18N.cs +++ b/UXAssist/Common/I18N.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using HarmonyLib; -namespace CheatEnabler; +namespace UXAssist.Common; public static class I18N { diff --git a/UXAssist/README.md b/UXAssist/README.md new file mode 100644 index 0000000..7dc8957 --- /dev/null +++ b/UXAssist/README.md @@ -0,0 +1,200 @@ +# CheatEnabler + +#### Add various cheat functions while disabling abnormal determinants +#### 添加一些作弊功能,同时屏蔽异常检测 + +## Changlog +* 2.2.7 + + New function: `Construct only nodes but frames` + + Opening config panel does not close inventory panel now + + Remove `Input direction conflict` check while using `Remove some build conditions` + + Fix a bug that prevents `Belt signal alt format` from switching number formats for current belt signals +* 2.2.6 + + New function: `Stop ejectors when available nodes are all filled up` + + Fix a bug that absorb solar sails on unfinised nodes +* 2.2.5 + + Skip all intermediate states and absorb solar sails instantly while enable `Quick absorb`, `Skip bullet period` and `Skip absorption period` at the same time. + + Fix a problem that `Quick absorb` does not absorb all solar sails instantly when most nodes are full. + + Fix crash while using with some mods +* 2.2.4 + + New function: `Enable player actions in globe view` + + Fix UI bug +* 2.2.3 + + New function: `Remove some build conditions` + + Fix compatibility with some mods +* 2.2.2 + + New function: `Assign game to currrnet account` + + New subfunction: `Belt signal alt format` + + Fix a crash on using `Initialize this Planet` + + Fix belt build in `Finish build immediately` +* 2.2.1 + + Check condition for miners even when `Build without condition` is enabled. + + Fix a patch issue that may cause `Build without condition` not working. +* 2.2.0 + + Add some power related functions + + Add a subfunction to belt signal item generation, which simulates production process of raws and intermediates on statistics + + Split some functions from Architect mode +* 2.1.0 + + Belt signal item generation + + Fix window display priority which may cause tips to be covered by main window +* 2.0.0 + + Refactorying codes + + UI implementation + + Add a lot of functions +* 1.0.0 + + Initial release + +## Usage + +* Press `` LAlt+`(BackQuote) `` to call up the config panel. You can change the shortcut on the panel. +* There are also buttons on title screen and planet minimap area to call up the config panel. +* Features: + + Strict hotkey dectection for build menu, thus building hotkeys(0~9, F1~F10, X, U) are not triggered while holding Ctrl/Alt/Shift. + + General: + + Enable Dev Shortcuts (check config panel for usage) + + Disable Abnormal Checks + + Unlock techs with key-modifiers (Ctrl/Alt/Shift) + + Assign game to currrnet account + + Factory: + + Finish build immediately + + Architect mode (Infinite buildings) + + Unlimited interactive range + + Build without condition + + Remove some build conditions + + No collision + + Sunlight at night + + Belt signal item generation + + Count all raws and intermediates in statistics + + Belt signal alt format + + Remove space limit between wind turbines and solar panels + + Boost power generations for kinds of power generators + + Planet: + + Enable player actions in globe view + + Infinite Natural Resources + + Fast Mining + + Pump Anywhere + + Terraform without enought sands + + Re-intialize planet (without reseting veins) + + Quick dismantle all buildings (without drops) + + Dyson Sphere: + + Stop ejectors when available nodes are all filled up + + Construct only nodes but frames + + Skip bullet period + + Skip absorption period + + Quick absorb + + Eject anyway + + Re-initialize Dyson Spheres + + Quick dismantle Dyson Shells + + Birth star: + + Rare resources on birth planet + + Solid flat on birth planet + + High luminosity for birth star + +## Notes +* Please upgrade `BepInEx` 5.4.21 or later if using with [BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/) to avoid possible conflicts. + + You can download `BepInEx` [here](https://github.com/bepinex/bepinex/releases/latest)(choose x64 edition). + + If using with r2modman, you can upgrade `BepInEx` by clicking `Settings` -> `Browse profile folder`, then extract downloaded zip to the folder and overwrite existing files. + +## CREDITS +* [Dyson Sphere Program](https://store.steampowered.com/app/1366540): The great game +* [BepInEx](https://bepinex.dev/): Base modding framework +* [Multifunction_mod](https://github.com/blacksnipebiu/Multifunction_mod): Some cheat functions +* [LSTM](https://github.com/hetima/DSP_LSTM) & [PlanetFinder](https://github.com/hetima/DSP_PlanetFinder): UI implementations + +## 更新日志 +* 2.2.7 + + 新功能:`只建造节点不建造框架` + + 打开设置面板时不再关闭背包面板 + + 在`移除部分不影响游戏逻辑的建造条件`启用时移除`输入方向冲突`的检查条件 + + 修复导致`传送带信号替换格式`不切换传送带信号数字格式的问题 +* 2.2.6 + + 新功能:`可用节点全部造完时停止弹射` + + 修复了在未完成的节点上吸收太阳帆的问题 +* 2.2.5 + + 在同时启用`快速吸收`、`跳过子弹阶段`和`跳过吸收阶段`时,所有弹射的太阳帆会跳过所有中间环节立即吸收 + + 修复了`快速吸收`在大部分节点已满时无法立即吸收所有太阳帆的问题 + + 修复了与一些mod的兼容性问题 +* 2.2.4 + + 新功能:`在行星视图中允许玩家操作` + + 修复了UI显示问题 +* 2.2.3 + + 新功能:`移除部分不影响游戏逻辑的建造条件` + + 修复了与一些mod的兼容性问题 +* 2.2.2 + + 新功能:`将游戏绑定给当前账号` + + 新子功能:`传送带信号替换格式` + + 修复了`初始化本行星`可能导致崩溃的问题 + + 修复了`建造秒完成`中传送带建造的问题 +* 2.2.1 + + 即使在启用`无条件建造`时依然检查矿机的建造条件 + + 修复一个可能导致`无条件建造`不生效的问题 +* 2.2.0 + + 添加了一些发电相关功能 + + 为传送带信号物品生成添加了一个子功能,在统计面板模拟了原材料和中间产物的生产过程 + + 从建筑师模式中分离了一些功能 +* 2.1.0 + + 传送带信号物品生成 + + 修复窗口显示优先级可能导致提示信息被主窗口遮挡的问题 +* 2.0.0 + + 重构代码 + + UI实现 + + 添加了很多功能 +* 1.0.0 + + 初始版本 + +## 使用说明 + +* 按 `` 左Alt+`(反引号) `` 键呼出主面板,可以在面板上修改快捷键。 +* 标题界面和行星小地图旁也有按钮呼出主面板。 +* 功能: + + 更严格的建造菜单热键检测,因此在按住Ctrl/Alt/Shift时不再会触发建造热键(0~9, F1~F10, X, U) + + 常规: + + 启用开发模式快捷键(使用说明见设置面板) + + 屏蔽异常检测 + + 使用组合键解锁科技(Ctrl/Alt/Shift) + + 将游戏绑定给当前账号 + + 工厂: + + 建造秒完成 + + 建筑师模式(无限建筑) + + ** 无限交互距离 + + 无条件建造 + + ** 移除部分不影响游戏逻辑的建造条件 + + 无碰撞 + + ** 夜间日光灯 + + 传送带信号物品生成 + + 统计面板中计算所有原材料和中间产物 + + 传送带信号替换格式 + + 风力发电机和太阳能板无间距限制 + + 提升各种发电设备发电量 + + 行星: + + ** 在行星视图中允许玩家操作 + + 自然资源采集不消耗 + + 高速采集 + + 平地抽水 + + 沙土不够时依然可以整改地形 + + ** 初始化本行星(不重置矿脉) + + ** 快速拆除所有建筑(不掉落) + + 戴森球: + + ** 可用节点全部造完时停止弹射 + + ** 只建造节点不建造框架 + + 跳过子弹阶段 + + 跳过吸收阶段 + + 快速吸收 + + 全球弹射 + + ** 初始化戴森球 + + ** 快速拆除戴森壳 + + 母星系: + + 母星有稀有资源 + + 母星是纯平的 + + 母星系恒星高亮 + +## 注意事项 +* 如果和[BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/)一起使用,请升级`BepInEx`到5.4.21或更高版本,以避免可能的冲突。 + + 你可以在[这里](https://github.com/bepinex/bepinex/releases/latest)(选择x64版本)下载`BepInEx`。 + + 如果使用r2modman,你可以点击`Settings` -> `Browse profile folder`,然后将下载的zip解压到该文件夹并覆盖现有文件。 + +## 鸣谢 +* [戴森球计划](https://store.steampowered.com/app/1366540): 伟大的游戏 +* [BepInEx](https://bepinex.dev/): 基础模组框架 +* [Multifunction_mod](https://github.com/blacksnipebiu/Multifunction_mod): 一些作弊功能 +* [LSTM](https://github.com/hetima/DSP_LSTM) & [PlanetFinder](https://github.com/hetima/DSP_PlanetFinder): UI实现 diff --git a/CheatEnabler/UI/MyCheckbox.cs b/UXAssist/UI/MyCheckbox.cs similarity index 98% rename from CheatEnabler/UI/MyCheckbox.cs rename to UXAssist/UI/MyCheckbox.cs index 708aba1..041f599 100644 --- a/CheatEnabler/UI/MyCheckbox.cs +++ b/UXAssist/UI/MyCheckbox.cs @@ -3,7 +3,7 @@ using BepInEx.Configuration; using UnityEngine; using UnityEngine.UI; -namespace CheatEnabler.UI; +namespace UXAssist.UI; // MyCheckBox modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyCheckBox.cs public class MyCheckBox : MonoBehaviour diff --git a/UXAssist/UI/MyConfigWindow.cs b/UXAssist/UI/MyConfigWindow.cs new file mode 100644 index 0000000..1f00d79 --- /dev/null +++ b/UXAssist/UI/MyConfigWindow.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using UnityEngine; +using UXAssist.Common; + +namespace UXAssist.UI; + +public class MyConfigWindow : MyWindowWithTabs +{ + public static Action OnUICreated; + public static Action OnUpdateUI; + + private RectTransform _windowTrans; + + public static MyConfigWindow CreateInstance() + { + return MyWindowManager.CreateWindow("UXAConfigWindow", "UXAssist Config"); + } + + public override void _OnCreate() + { + _windowTrans = GetComponent(); + _windowTrans.sizeDelta = new Vector2(680f, 420f); + + OnUICreated?.Invoke(this, _windowTrans); + SetCurrentTab(0); + OnUpdateUI?.Invoke(); + } + + public override void _OnDestroy() + { + } + + public override bool _OnInit() + { + _windowTrans.anchoredPosition = new Vector2(0, 0); + return true; + } + + public override void _OnFree() + { + } + + public override void _OnOpen() + { + } + + public override void _OnClose() + { + } + + public override void _OnUpdate() + { + base._OnUpdate(); + if (VFInput.escape && !VFInput.inputing) + { + VFInput.UseEscape(); + _Close(); + return; + } + + OnUpdateUI?.Invoke(); + } +} diff --git a/CheatEnabler/UI/MyKeyBinder.cs b/UXAssist/UI/MyKeyBinder.cs similarity index 99% rename from CheatEnabler/UI/MyKeyBinder.cs rename to UXAssist/UI/MyKeyBinder.cs index 3333580..cfddd1b 100644 --- a/CheatEnabler/UI/MyKeyBinder.cs +++ b/UXAssist/UI/MyKeyBinder.cs @@ -4,7 +4,7 @@ using BepInEx.Configuration; using UnityEngine; using UnityEngine.UI; -namespace CheatEnabler.UI; +namespace UXAssist.UI; // MyKeyBinder modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyKeyBinder.cs public class MyKeyBinder : MonoBehaviour diff --git a/CheatEnabler/UI/MySlider.cs b/UXAssist/UI/MySlider.cs similarity index 99% rename from CheatEnabler/UI/MySlider.cs rename to UXAssist/UI/MySlider.cs index 7978f44..0434506 100644 --- a/CheatEnabler/UI/MySlider.cs +++ b/UXAssist/UI/MySlider.cs @@ -2,7 +2,7 @@ using UnityEngine; using UnityEngine.UI; -namespace CheatEnabler.UI; +namespace UXAssist.UI; // MySlider modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MySlider.cs diff --git a/CheatEnabler/UI/MyWindow.cs b/UXAssist/UI/MyWindow.cs similarity index 91% rename from CheatEnabler/UI/MyWindow.cs rename to UXAssist/UI/MyWindow.cs index 3780a86..a78af1d 100644 --- a/CheatEnabler/UI/MyWindow.cs +++ b/UXAssist/UI/MyWindow.cs @@ -6,7 +6,7 @@ using UnityEngine.Events; using UnityEngine.UI; using Object = UnityEngine.Object; -namespace CheatEnabler.UI; +namespace UXAssist.UI; // MyWindow modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyWindowCtl.cs @@ -51,7 +51,7 @@ public class MyWindow: ManualBehaviour } } - protected static Text AddText(float x, float y, RectTransform parent, string label, int fontSize = 14, string objName = "label") + public static Text AddText(float x, float y, RectTransform parent, string label, int fontSize = 14, string objName = "label") { var src = UIRoot.instance.uiGame.assemblerWindow.stateText; var txt = Instantiate(src); @@ -68,7 +68,7 @@ public class MyWindow: ManualBehaviour return txt; } - protected static UIButton AddTipsButton(float x, float y, RectTransform parent, string label, string tip, string content, string objName = "tips-button") + public static UIButton AddTipsButton(float x, float y, RectTransform parent, string label, string tip, string content, string objName = "tips-button") { var src = UIRoot.instance.galaxySelect.sandboxToggle.gameObject.transform.parent.Find("tip-button"); var dst = Instantiate(src); @@ -82,7 +82,7 @@ public class MyWindow: ManualBehaviour return btn; } - protected UIButton AddButton(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "button", UnityAction onClick = null) + public UIButton AddButton(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "button", UnityAction onClick = null) { var panel = UIRoot.instance.uiGame.statWindow.performancePanelUI; var btn = Instantiate(panel.cpuActiveButton); @@ -113,7 +113,7 @@ public class MyWindow: ManualBehaviour return btn; } - protected UIButton AddFlatButton(float x, float y, RectTransform parent, string text = "", int fontSize = 12, string objName = "button", UnityAction onClick = null) + public UIButton AddFlatButton(float x, float y, RectTransform parent, string text = "", int fontSize = 12, string objName = "button", UnityAction onClick = null) { var panel = UIRoot.instance.uiGame.dysonEditor.controlPanel.hierarchy.layerPanel; var btn = Instantiate(panel.layerButtons[0]); @@ -206,7 +206,8 @@ public class MyWindow: ManualBehaviour public class MyWindowWithTabs : MyWindow { - protected readonly List> Tabs = new(); + private readonly List> _tabs = new(); + private float _tabX = 36f; public override void TryClose() { _Close(); @@ -217,7 +218,7 @@ public class MyWindowWithTabs : MyWindow return true; } - protected RectTransform AddTab(float x, int index, RectTransform parent, string label) + public RectTransform AddTab(float x, int index, RectTransform parent, string label) { var tab = new GameObject(); var tabRect = tab.AddComponent(); @@ -243,7 +244,7 @@ public class MyWindowWithTabs : MyWindow btnText.fontSize = 16; btn.data = index; - Tabs.Add(Tuple.Create(tabRect, btn)); + _tabs.Add(Tuple.Create(tabRect, btn)); if (EventRegistered) { btn.onClick += OnTabButtonClick; @@ -251,11 +252,18 @@ public class MyWindowWithTabs : MyWindow return tabRect; } + public RectTransform AddTab(RectTransform parent, string label) + { + var result = AddTab(_tabX, _tabs.Count, parent, label); + _tabX += 100f; + return result; + } + public override void _OnRegEvent() { if (!EventRegistered) { - foreach (var t in Tabs) + foreach (var t in _tabs) { t.Item2.onClick += OnTabButtonClick; } @@ -267,7 +275,7 @@ public class MyWindowWithTabs : MyWindow { if (EventRegistered) { - foreach (var t in Tabs) + foreach (var t in _tabs) { t.Item2.onClick -= OnTabButtonClick; } @@ -279,7 +287,7 @@ public class MyWindowWithTabs : MyWindow private void OnTabButtonClick(int index) { - foreach (var (rectTransform, btn) in Tabs) + foreach (var (rectTransform, btn) in _tabs) { if (btn.data != index) { diff --git a/CheatEnabler/UI/Util.cs b/UXAssist/UI/Util.cs similarity index 98% rename from CheatEnabler/UI/Util.cs rename to UXAssist/UI/Util.cs index 34d5e29..f53eeed 100644 --- a/CheatEnabler/UI/Util.cs +++ b/UXAssist/UI/Util.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace CheatEnabler.UI; +namespace UXAssist.UI; public static class Util { diff --git a/UXAssist/UIConfigWindow.cs b/UXAssist/UIConfigWindow.cs new file mode 100644 index 0000000..81fd6b7 --- /dev/null +++ b/UXAssist/UIConfigWindow.cs @@ -0,0 +1,26 @@ +using System; +using UnityEngine; +using UXAssist.UI; +using UXAssist.Common; + +namespace UXAssist; + +public static class UIConfigWindow +{ + private static RectTransform _windowTrans; + private static MyConfigWindow _configWindow; + + public static void Init() + { + I18N.Add("UXAssist", "UXAssist", "UX助手"); + I18N.Apply(); + MyConfigWindow.OnUICreated += CreateUI; + } + + private static void CreateUI(MyConfigWindow wnd, RectTransform trans) + { + _configWindow = wnd; + _windowTrans = trans; + var tab1 = wnd.AddTab(_windowTrans, "UXAssist"); + } +} diff --git a/UXAssist/UXAssist.cs b/UXAssist/UXAssist.cs new file mode 100644 index 0000000..4daa13a --- /dev/null +++ b/UXAssist/UXAssist.cs @@ -0,0 +1,285 @@ +using System.Collections.Generic; +using System.Reflection.Emit; +using BepInEx; +using BepInEx.Configuration; +using HarmonyLib; +using UnityEngine; +using UnityEngine.UI; +using UXAssist.Common; +using UXAssist.UI; + +namespace UXAssist; + +[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] +public class UXAssist : BaseUnityPlugin +{ + public new static readonly BepInEx.Logging.ManualLogSource Logger = + BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME); + + public static ConfigEntry Hotkey; + private static bool _configWinInitialized = false; + private static MyConfigWindow _configWin; + + private static Harmony _windowPatch; + private static Harmony _patch; + + private static bool _initialized; + + private void Awake() + { + Hotkey = Config.Bind("General", "Shortcut", KeyboardShortcut.Deserialize("BackQuote + LeftAlt"), "Shortcut to open config window"); + /* + DevShortcuts.Enabled = Config.Bind("General", "DevShortcuts", false, "Enable DevMode shortcuts"); + AbnormalDisabler.Enabled = Config.Bind("General", "DisableAbnormalChecks", false, + "disable all abnormal checks"); + TechPatch.Enabled = Config.Bind("General", "UnlockTech", false, + "Unlock clicked tech by holding key-modifilers(Shift/Alt/Ctrl)"); + FactoryPatch.ImmediateEnabled = Config.Bind("Build", "ImmediateBuild", false, + "Build immediately"); + FactoryPatch.ArchitectModeEnabled = Config.Bind("Build", "Architect", false, + "Architect Mode"); + FactoryPatch.UnlimitInteractiveEnabled = Config.Bind("Build", "UnlimitInteractive", false, + "Unlimit interactive range"); + FactoryPatch.RemoveSomeConditionEnabled = Config.Bind("Build", "RemoveSomeBuildConditionCheck", false, + "Remove part of build condition checks that does not affect game logic"); + FactoryPatch.NoConditionEnabled = Config.Bind("Build", "BuildWithoutCondition", false, + "Build without condition"); + FactoryPatch.NoCollisionEnabled = Config.Bind("Build", "NoCollision", false, + "No collision"); + FactoryPatch.BeltSignalGeneratorEnabled = Config.Bind("Build", "BeltSignalGenerator", false, + "Belt signal generator"); + FactoryPatch.BeltSignalNumberAltFormat = Config.Bind("Build", "BeltSignalNumberFormat", false, + "Belt signal number format alternative format (AAAA=generation speed in minutes, B=proliferate points, C=stack count):\n AAAABC by default\n BCAAAA as alternative"); + FactoryPatch.BeltSignalCountRecipeEnabled = Config.Bind("Build", "BeltSignalCountRecipe", false, + "Belt signal count all raws and intermediates in statistics"); + FactoryPatch.NightLightEnabled = Config.Bind("Build", "NightLight", false, + "Night light"); + FactoryPatch.RemovePowerSpaceLimitEnabled = Config.Bind("Build", "RemovePowerDistanceLimit", false, + "Remove distance limit for wind turbines and geothermals"); + FactoryPatch.BoostWindPowerEnabled = Config.Bind("Build", "BoostWindPower", false, + "Boost wind power"); + FactoryPatch.BoostSolarPowerEnabled = Config.Bind("Build", "BoostSolarPower", false, + "Boost solar power"); + FactoryPatch.BoostFuelPowerEnabled = Config.Bind("Build", "BoostFuelPower", false, + "Boost fuel power"); + FactoryPatch.BoostGeothermalPowerEnabled = Config.Bind("Build", "BoostGeothermalPower", false, + "Boost geothermal power"); + PlanetFunctions.PlayerActionsInGlobeViewEnabled = Config.Bind("Planet", "PlayerActionsInGlobeView", false, + "Enable player actions in globe view"); + ResourcePatch.InfiniteResourceEnabled = Config.Bind("Planet", "AlwaysInfiniteResource", false, + "always infinite natural resource"); + ResourcePatch.FastMiningEnabled = Config.Bind("Planet", "FastMining", false, + "super-fast mining speed"); + WaterPumperPatch.Enabled = Config.Bind("Planet", "WaterPumpAnywhere", false, + "Can pump water anywhere (while water type is not None)"); + TerraformPatch.Enabled = Config.Bind("Planet", "TerraformAnyway", false, + "Can do terraform without enough sands"); + DysonSpherePatch.StopEjectOnNodeCompleteEnabled = Config.Bind("DysonSphere", "StopEjectOnNodeComplete", false, + "Stop ejectors when available nodes are all filled up"); + DysonSpherePatch.OnlyConstructNodesEnabled = Config.Bind("DysonSphere", "OnlyConstructNodes", false, + "Construct only nodes but frames"); + DysonSpherePatch.SkipBulletEnabled = Config.Bind("DysonSphere", "SkipBullet", false, + "Skip bullet"); + DysonSpherePatch.SkipAbsorbEnabled = Config.Bind("DysonSphere", "SkipAbsorb", false, + "Skip absorption"); + DysonSpherePatch.QuickAbsorbEnabled = Config.Bind("DysonSphere", "QuickAbsorb", false, + "Quick absorb"); + DysonSpherePatch.EjectAnywayEnabled = Config.Bind("DysonSphere", "EjectAnyway", false, + "Eject anyway"); + DysonSpherePatch.OverclockEjectorEnabled = Config.Bind("DysonSphere", "OverclockEjector", false, + "Overclock ejector"); + DysonSpherePatch.OverclockSiloEnabled = Config.Bind("DysonSphere", "OverclockSilo", false, + "Overclock silo"); + BirthPlanetPatch.SitiVeinsOnBirthPlanet = Config.Bind("Birth", "SiTiVeinsOnBirthPlanet", false, + "Silicon/Titanium on birth planet"); + BirthPlanetPatch.FireIceOnBirthPlanet = Config.Bind("Birth", "FireIceOnBirthPlanet", false, + "Fire ice on birth planet"); + BirthPlanetPatch.KimberliteOnBirthPlanet = Config.Bind("Birth", "KimberliteOnBirthPlanet", false, + "Kimberlite on birth planet"); + BirthPlanetPatch.FractalOnBirthPlanet = Config.Bind("Birth", "FractalOnBirthPlanet", false, + "Fractal silicon on birth planet"); + BirthPlanetPatch.OrganicOnBirthPlanet = Config.Bind("Birth", "OrganicOnBirthPlanet", false, + "Organic crystal on birth planet"); + BirthPlanetPatch.OpticalOnBirthPlanet = Config.Bind("Birth", "OpticalOnBirthPlanet", false, + "Optical grating crystal on birth planet"); + BirthPlanetPatch.SpiniformOnBirthPlanet = Config.Bind("Birth", "SpiniformOnBirthPlanet", false, + "Spiniform stalagmite crystal on birth planet"); + BirthPlanetPatch.UnipolarOnBirthPlanet = Config.Bind("Birth", "UnipolarOnBirthPlanet", false, + "Unipolar magnet on birth planet"); + BirthPlanetPatch.FlatBirthPlanet = Config.Bind("Birth", "FlatBirthPlanet", false, + "Birth planet is solid flat (no water at all)"); + BirthPlanetPatch.HighLuminosityBirthStar = Config.Bind("Birth", "HighLuminosityBirthStar", false, + "Birth star has high luminosity"); + */ + I18N.Init(); + I18N.Add("UXAssist Config", "UXAssist Config", "UX助手设置"); + I18N.Apply(); + + // UI Patch + _windowPatch ??= Harmony.CreateAndPatchAll(typeof(UI.MyWindowManager.Patch)); + _patch ??= Harmony.CreateAndPatchAll(typeof(UXAssist)); + + UIConfigWindow.Init(); + /* + DevShortcuts.Init(); + AbnormalDisabler.Init(); + TechPatch.Init(); + FactoryPatch.Init(); + PlanetFunctions.Init(); + ResourcePatch.Init(); + WaterPumperPatch.Init(); + TerraformPatch.Init(); + DysonSpherePatch.Init(); + BirthPlanetPatch.Init(); + */ + } + + private void OnDestroy() + { + /* + BirthPlanetPatch.Uninit(); + DysonSpherePatch.Uninit(); + TerraformPatch.Uninit(); + WaterPumperPatch.Uninit(); + ResourcePatch.Uninit(); + PlanetFunctions.Uninit(); + FactoryPatch.Uninit(); + TechPatch.Uninit(); + AbnormalDisabler.Uninit(); + DevShortcuts.Uninit(); + */ + _patch?.UnpatchSelf(); + _patch = null; + _windowPatch?.UnpatchSelf(); + _windowPatch = null; + } + + private void Update() + { + if (VFInput.inputing) return; + if (Hotkey.Value.IsDown()) ToggleConfigWindow(); + } +/* + private void LateUpdate() + { + FactoryPatch.NightLight.LateUpdate(); + } +*/ + [HarmonyPostfix, HarmonyPatch(typeof(UIRoot), nameof(UIRoot.OpenMainMenuUI))] + public static void UIRoot_OpenMainMenuUI_Postfix() + { + if (_initialized) return; + { + var mainMenu = UIRoot.instance.uiMainMenu; + var src = mainMenu.newGameButton; + var parent = src.transform.parent; + var btn = Instantiate(src, parent); + btn.name = "button-cheatenabler-config"; + var l = btn.text.GetComponent(); + if (l != null) + { + l.stringKey = "CheatEnabler Config"; + l.translation = "CheatEnabler Config".Translate(); + } + btn.text.text = "CheatEnabler Config".Translate(); + btn.text.fontSize = btn.text.fontSize * 7 / 8; + I18N.OnInitialized += () => { btn.text.text = "UXAssist Config".Translate(); }; + var vec = ((RectTransform)mainMenu.exitButton.transform).anchoredPosition3D; + var vec2 = ((RectTransform)mainMenu.creditsButton.transform).anchoredPosition3D; + var transform1 = (RectTransform)btn.transform; + transform1.anchoredPosition3D = new Vector3(vec.x, vec.y + (vec.y - vec2.y) * 2, vec.z); + btn.button.onClick.RemoveAllListeners(); + btn.button.onClick.AddListener(ToggleConfigWindow); + } + { + var panel = UIRoot.instance.uiGame.planetGlobe; + var src = panel.button2; + var sandboxMenu = UIRoot.instance.uiGame.sandboxMenu; + var icon = sandboxMenu.categoryButtons[6].transform.Find("icon")?.GetComponent()?.sprite; + var b = Instantiate(src, src.transform.parent); + var panelButtonGo = b.gameObject; + var rect = (RectTransform)panelButtonGo.transform; + var btn = panelButtonGo.GetComponent(); + var img = panelButtonGo.transform.Find("button-2/icon")?.GetComponent(); + if (img != null) + { + img.sprite = icon; + } + if (panelButtonGo != null && btn != null) + { + panelButtonGo.name = "open-uxassist-config"; + rect.localScale = new Vector3(0.5f, 0.5f, 0.5f); + rect.anchoredPosition3D = new Vector3(128f, -105f, 0f); + b.onClick.RemoveAllListeners(); + btn.onClick += _ => { ToggleConfigWindow(); }; + btn.tips.tipTitle = "CheatEnabler Config"; + I18N.OnInitialized += () => { btn.tips.tipTitle = "UXAssist Config".Translate(); }; + btn.tips.tipText = null; + btn.tips.corner = 9; + btn.tips.offset = new Vector2(-20f, -20f); + panelButtonGo.SetActive(true); + } + } + _initialized = true; + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UIBuildMenu), nameof(UIBuildMenu._OnUpdate))] + private static IEnumerable UIBuildMenu__OnUpdate_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.inScreen))) + ); + matcher.Repeat(codeMatcher => + { + var jumpPos = codeMatcher.Advance(1).Operand; + codeMatcher.Advance(-1).InsertAndAdvance( + new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.noModifier))), + new CodeInstruction(OpCodes.Brfalse_S, jumpPos) + ).Advance(2); + }); + return matcher.InstructionEnumeration(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UIButton), nameof(UIButton.LateUpdate))] + private static IEnumerable UIButton_LateUpdate_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldloc_2), + new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.gameObject))), + new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(GameObject), nameof(GameObject.activeSelf))) + ); + var labels = matcher.Labels; + matcher.Labels = null; + matcher.Insert( + new CodeInstruction(OpCodes.Ldloc_2).WithLabels(labels), + new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.transform))), + new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))), + new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))), + new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Transform), nameof(Transform.SetAsLastSibling))) + ); + return matcher.InstructionEnumeration(); + } + + private static void ToggleConfigWindow() + { + if (!_configWinInitialized) + { + if (!I18N.Initialized()) return; + _configWinInitialized = true; + _configWin = MyConfigWindow.CreateInstance(); + } + + if (_configWin.active) + { + _configWin._Close(); + } + else + { + _configWin.Open(); + } + } +} diff --git a/UXAssist/UXAssist.csproj b/UXAssist/UXAssist.csproj new file mode 100644 index 0000000..b6cbe38 --- /dev/null +++ b/UXAssist/UXAssist.csproj @@ -0,0 +1,28 @@ + + + + net472 + org.soardev.uxassist + DSP MOD - UXAssist + 1.0.0 + true + latest + UXAssist + UXAssist + + + + + + + + + + + + + + + + + diff --git a/UXAssist/package/icon.png b/UXAssist/package/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1e293d04104195f92486a9f78c0c2f8e830a2401 GIT binary patch literal 26471 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