From 3b9e35aa5f7757b691358afcf6b7da5c7a9c9ccd Mon Sep 17 00:00:00 2001 From: Soar Qin Date: Sun, 27 Nov 2022 17:52:52 +0800 Subject: [PATCH] Dustbin v1.1.0 release --- Dustbin/Dustbin.cs | 104 ++++++++++++++++-------------- Dustbin/Dustbin.csproj | 4 +- Dustbin/MyCheckbox.cs | 84 ++++++++++++++++++++++++ Dustbin/README.md | 30 ++++----- Dustbin/StoragePatch.cs | 118 ++++++++++++++++++++++++++++++++++ Dustbin/TankPatch.cs | 106 ++++++++++++++++++++++++++++++ Dustbin/package/manifest.json | 4 +- README.md | 4 +- 8 files changed, 382 insertions(+), 72 deletions(-) create mode 100644 Dustbin/MyCheckbox.cs create mode 100644 Dustbin/StoragePatch.cs create mode 100644 Dustbin/TankPatch.cs diff --git a/Dustbin/Dustbin.cs b/Dustbin/Dustbin.cs index 10e8c9e..d571d60 100644 --- a/Dustbin/Dustbin.cs +++ b/Dustbin/Dustbin.cs @@ -1,8 +1,44 @@ -using BepInEx; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using BepInEx; using HarmonyLib; +using UnityEngine; namespace Dustbin; +class IsDusbinIndexer +{ + private bool[] store = new bool[256]; + + public bool this[int index] + { + get + { + if (index < 0 || index >= store.Length) return false; + return store[index]; + } + set + { + if (index >= store.Length) + { + var oldLen = store.Length; + var newLen = oldLen * 2; + var oldArr = store; + store = new bool[newLen]; + Array.Copy(oldArr, store, oldLen); + } + store[index] = value; + } + } + + public void Reset() + { + store = new bool[256]; + } +} + [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] public class Dustbin : BaseUnityPlugin { @@ -10,8 +46,8 @@ public class Dustbin : BaseUnityPlugin BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME); private bool _cfgEnabled = true; - private static readonly int[] SandsFactors = { 0, 1, 5, 10, 100 }; - private static readonly bool[] IsFluid = new bool[2000]; + public static readonly int[] SandsFactors = { 0, 1, 5, 10, 100 }; + public static bool[] IsFluid; private void Awake() { @@ -22,55 +58,25 @@ public class Dustbin : BaseUnityPlugin SandsFactors[3] = Config.Bind("General", "SandsPerSilicon", SandsFactors[3], "Sands gathered from silicon ores").Value; SandsFactors[4] = Config.Bind("General", "SandsPerFractal", SandsFactors[4], "Sands gathered from fractal silicon ores").Value; Harmony.CreateAndPatchAll(typeof(Dustbin)); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(DSPGame), "StartGame", typeof(GameDesc))] - [HarmonyPatch(typeof(DSPGame), "StartGame", typeof(string))] - private static void OnGameStart() - { - foreach (var data in LDB.items.dataArray) - { - if (data.ID < 2000 && data.IsFluid) - { - IsFluid[data.ID] = true; - } - } + Harmony.CreateAndPatchAll(typeof(StoragePatch)); + Harmony.CreateAndPatchAll(typeof(TankPatch)); } [HarmonyPrefix] - [HarmonyPatch(typeof(StorageComponent), "AddItem", - new[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(bool) }, - new[] - { - ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal - })] - public static bool AbandonItems(ref int __result, StorageComponent __instance, int itemId, int count, int inc, - out int remainInc, bool useBan = false) + [HarmonyPatch(typeof(GameMain), "Start")] + private static void GameMain_Start_Prefix() { - remainInc = inc; - if (!useBan || count == 0 || __instance.id != __instance.top) return true; - var size = __instance.size; - if (size == 0 || size != __instance.bans || __instance.grids[0].count > 0) return true; - __result = count; - var isFluid = itemId < 2000 && IsFluid[itemId]; - var sandsPerItem = SandsFactors[isFluid - ? 0 - : itemId switch - { - 1005 => 2, - 1003 => 3, - 1013 => 4, - _ => 1, - }]; - if (sandsPerItem <= 0) return false; - var player = GameMain.mainPlayer; - var addCount = count * sandsPerItem; - player.sandCount += addCount; - GameMain.history.OnSandCountChange(player.sandCount, addCount); - /* Following line crashes game, seems that it should not be called in this working thread: - * UIRoot.instance.uiGame.OnSandCountChanged(player.sandCount, addCount); - */ - return false; + StoragePatch.Reset(); + TankPatch.Reset(); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(VFPreload), "InvokeOnLoadWorkEnded")] + private static void VFPreload_InvokeOnLoadWorkEnded_Postfix() + { + var maxId = ItemProto.fluids.Max(); + IsFluid = new bool[maxId + 1]; + foreach (var id in ItemProto.fluids) + IsFluid[id] = true; } } diff --git a/Dustbin/Dustbin.csproj b/Dustbin/Dustbin.csproj index f133024..c0ac1e2 100644 --- a/Dustbin/Dustbin.csproj +++ b/Dustbin/Dustbin.csproj @@ -1,10 +1,10 @@ - netstandard2.0 + net472 org.soardev.dustbin DSP MOD - Dustbin - 1.0.1 + 1.1.0 true latest Dustbin diff --git a/Dustbin/MyCheckbox.cs b/Dustbin/MyCheckbox.cs new file mode 100644 index 0000000..56c2d7b --- /dev/null +++ b/Dustbin/MyCheckbox.cs @@ -0,0 +1,84 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace Dustbin; + +// MyCheckbox modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/LSTM.cs +public class MyCheckBox : MonoBehaviour +{ + public UIButton uiButton; + public Image checkImage; + public RectTransform rectTrans; + public Text labelText; + + public event Action OnChecked; + public bool Checked + { + get => _checked; + set + { + _checked = value; + checkImage.enabled = value; + } + } + + private bool _checked; + + public static MyCheckBox CreateCheckBox(bool check, Transform parent = null, float x = 0f, float y = 0f, string label = "", int fontSize = 15) + { + UIBuildMenu buildMenu = UIRoot.instance.uiGame.buildMenu; + UIButton src = buildMenu.uxFacilityCheck; + + GameObject go = GameObject.Instantiate(src.gameObject); + go.name = "my-checkbox"; + MyCheckBox cb = go.AddComponent(); + cb._checked = check; + RectTransform rect = go.transform as RectTransform; + if (parent != null) + { + rect.SetParent(parent); + } + rect.anchorMax = new Vector2(0f, 1f); + rect.anchorMin = new Vector2(0f, 1f); + rect.pivot = new Vector2(0f, 1f); + rect.anchoredPosition3D = new Vector3(x, -y, 0f); + + cb.rectTrans = rect; + cb.uiButton = go.GetComponent(); + cb.checkImage = go.transform.Find("checked")?.GetComponent(); + //ResetAnchor(cb.checkImage.rectTransform); + + //text + Transform child = go.transform.Find("text"); + if (child != null) + { + //ResetAnchor(child as RectTransform); + GameObject.DestroyImmediate(child.GetComponent()); + cb.labelText = child.GetComponent(); + cb.labelText.fontSize = fontSize; + cb.SetLabelText(label); + } + + //value + cb.uiButton.onClick += cb.OnClick; + cb.checkImage.enabled = check; + + return cb; + } + + public void SetLabelText(string val) + { + if (labelText != null) + { + labelText.text = val; + } + } + + public void OnClick(int obj) + { + _checked = !_checked; + checkImage.enabled = _checked; + OnChecked?.Invoke(); + } +} diff --git a/Dustbin/README.md b/Dustbin/README.md index ff5ee36..c777c72 100644 --- a/Dustbin/README.md +++ b/Dustbin/README.md @@ -1,33 +1,29 @@ # Dustbin -#### Storages can destroy incoming items while capacity limited to zero -#### 空间限制为0的储物仓可以销毁送进来的物品 +#### Can turn Storages and Tanks into Dustbin(Destroy incoming items) +#### 储物仓和储液罐可以转变为垃圾桶(销毁送进的物品) ## Updates +* 1.1.0 + * Rewrite whole plugin, make a checkbox on UI so that you can turn storages into dustbin by just ticking it. + * Can turn tank into dustbin now. + * 1.0.1 - * Remove a debug log + * Remove a debug log ## Usage -* Conditions to be dustbin: Storages with capacity limited to zero at top of stacks(or only one level), and empty in 1st cell. +* A checkbox is added to Storages and Tanks UI, which turns them into dustbins. * Items sent into dustbins are removed immediately. * Can get sands from destroyed items (with factors configurable): - * Get 10/100 sands from each silicon/fractal silicon ore - * Get 1 sand from any other normal item but fluid -* Known bugs - * Stack 1 more storage up on a zero limited one and remove it will cause dustbin stop working. Just put somethings - in and take them out to make the dustbin working again. - This is caused by a logic bug in original code where faulty set `lastFullItem` field of `StorageComponent` for - empty storages. + * Get 10/100 sands from each silicon/fractal silicon ore + * Get 1 sand from any other normal item but fluid ## 使用说明 -* 垃圾桶条件:空间限制为0第一格为空并且放在堆叠顶部(或者只有一层)的储物仓。 +* 在储物仓和储液罐上增加一个垃圾桶的勾选框。 * 送进垃圾桶的物品会立即被移除。 * 可以从移除的物品中获得沙子(可配置,下为默认值): - * 从硅石和分形硅石中获得10/100个沙子。 - * 从普通物品中获得1个沙子,但液体不会给沙子。 -* 已知Bug - * 在空间限制为0的储物仓上面再叠一个储物仓后再移除,会导致垃圾箱功能失效,放一个物品进去再拿出来即可恢复正常。 - 这是原游戏的逻辑Bug错误设置了`StorageComponent`的`lastFullItem`字段导致。 + * 从硅石和分形硅石中获得10/100个沙子。 + * 从普通物品中获得1个沙子,但液体不会给沙子。 diff --git a/Dustbin/StoragePatch.cs b/Dustbin/StoragePatch.cs new file mode 100644 index 0000000..3ba57b0 --- /dev/null +++ b/Dustbin/StoragePatch.cs @@ -0,0 +1,118 @@ +using HarmonyLib; +using UnityEngine; + +namespace Dustbin; + +[HarmonyPatch] +public static class StoragePatch +{ + private static MyCheckBox _storageDustbinCheckBox; + private static int lastStorageId; + private static IsDusbinIndexer storageIsDustbin = new(); + + public static void Reset() + { + storageIsDustbin.Reset(); + lastStorageId = 0; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UIStorageWindow), "_OnCreate")] + private static void UIStorageWindow__OnCreate_Postfix(UIStorageWindow __instance) + { + _storageDustbinCheckBox = MyCheckBox.CreateCheckBox(false, __instance.transform, 50f, 50f, Localization.language == Language.zhCN ? "垃圾桶" : "Dustbin"); + var window = __instance; + _storageDustbinCheckBox.OnChecked += () => + { + var storageId = window.storageId; + if (storageId <= 0 || window.factoryStorage.storagePool[storageId].id != storageId) return; + storageIsDustbin[storageId] = _storageDustbinCheckBox.Checked; + }; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UIStorageWindow), "_OnUpdate")] + private static void UIStorageWindow__OnUpdate_Postfix(UIStorageWindow __instance) + { + var storageId = __instance.storageId; + if (lastStorageId == storageId) return; + lastStorageId = storageId; + if (storageId > 0 && __instance.factoryStorage.storagePool[storageId].id == storageId) + { + _storageDustbinCheckBox.Checked = storageIsDustbin[storageId]; + } + if (__instance.transform is RectTransform rectTrans) { + _storageDustbinCheckBox.rectTrans.anchoredPosition3D = new Vector3(50, 58 - rectTrans.sizeDelta.y, 0); + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(StorageComponent), "AddItem", + new[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(bool) }, + new[] + { + ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal + })] + private static bool StorageComponent_AddItem_Prefix(ref int __result, StorageComponent __instance, int itemId, int count, int inc, + ref int remainInc, bool useBan = false) + { + if (!storageIsDustbin[__instance.id]) return true; + remainInc = inc; + __result = count; + var fluidArr = Dustbin.IsFluid; + var sandsPerItem = Dustbin.SandsFactors[itemId < fluidArr.Length && fluidArr[itemId] + ? 0 + : itemId switch + { + 1005 => 2, + 1003 => 3, + 1013 => 4, + _ => 1, + }]; + if (sandsPerItem <= 0) return false; + var player = GameMain.mainPlayer; + var addCount = count * sandsPerItem; + player.sandCount += addCount; + GameMain.history.OnSandCountChange(player.sandCount, addCount); + /* Following line crashes game, seems that it should not be called in this working thread: + * UIRoot.instance.uiGame.OnSandCountChanged(player.sandCount, addCount); + */ + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(StorageComponent), "Export")] + private static void StorageComponent_Export_Prefix(StorageComponent __instance, out int __state) + { + if (storageIsDustbin[__instance.id]) + { + __state = __instance.bans; + __instance.bans = -__instance.bans - 1; + } + else + { + __state = -1; + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(StorageComponent), "Export")] + private static void StorageComponent_Export_Postfix(StorageComponent __instance, int __state) + { + if (__state < 0) return; + __instance.bans = __state; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(StorageComponent), "Import")] + private static void StorageComponent_Import_Postfix(StorageComponent __instance) + { + if ((__instance.bans & 0x8000) == 0) + storageIsDustbin[__instance.id] = false; + else + { + storageIsDustbin[__instance.id] = true; + __instance.bans ^= 0x8000; + } + } +} diff --git a/Dustbin/TankPatch.cs b/Dustbin/TankPatch.cs new file mode 100644 index 0000000..705e938 --- /dev/null +++ b/Dustbin/TankPatch.cs @@ -0,0 +1,106 @@ +using HarmonyLib; +using UnityEngine; + +namespace Dustbin; + +[HarmonyPatch] +public static class TankPatch +{ + private static MyCheckBox _tankDustbinCheckBox; + private static int lastTankId; + private static IsDusbinIndexer tankIsDustbin = new(); + + public static void Reset() + { + tankIsDustbin.Reset(); + lastTankId = 0; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UITankWindow), "_OnCreate")] + private static void UITankWindow__OnCreate_Postfix(UITankWindow __instance) + { + _tankDustbinCheckBox = MyCheckBox.CreateCheckBox(false, __instance.transform, 120f, 20f, Localization.language == Language.zhCN ? "垃圾桶" : "Dustbin"); + var window = __instance; + _tankDustbinCheckBox.OnChecked += () => + { + var tankId = window.tankId; + if (tankId <= 0 || window.storage.tankPool[tankId].id != tankId) return; + tankIsDustbin[tankId] = _tankDustbinCheckBox.Checked; + }; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UITankWindow), "_OnUpdate")] + private static void UITankWindow__OnUpdate_Postfix(UITankWindow __instance) + { + var tankId = __instance.tankId; + if (lastTankId == tankId) return; + lastTankId = tankId; + if (tankId > 0 && __instance.storage.tankPool[tankId].id == tankId) + { + _tankDustbinCheckBox.Checked = tankIsDustbin[tankId]; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(TankComponent), "GameTick")] + private static void TankComponent_GameTick_Prefix(ref TankComponent __instance, out long __state) + { + if (tankIsDustbin[__instance.id]) + { + __state = ((long)__instance.fluidInc << 36) | ((long)__instance.fluidCount << 16) | (uint)__instance.fluidId; + __instance.fluidId = __instance.fluidCount = __instance.fluidInc = 0; + } + else + { + __state = -1; + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(TankComponent), "GameTick")] + private static void TankComponent_GameTick_Postfix(ref TankComponent __instance, long __state) + { + if (__state < 0) return; + __instance.fluidId = (int)(__state & 0xFFFFL); + __instance.fluidCount = (int)((__state >> 16) & 0xFFFFFL); + __instance.fluidInc = (int)(__state >> 36); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(TankComponent), "Export")] + private static void TankComponent_Export_Prefix(ref TankComponent __instance, out int __state) + { + if (tankIsDustbin[__instance.id]) + { + __state = __instance.fluidInc; + __instance.fluidInc = -__instance.fluidInc - 1; + } + else + { + __state = -1; + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(TankComponent), "Export")] + private static void TankComponent_Export_Postfix(ref TankComponent __instance, int __state) + { + if (__state < 0) return; + __instance.fluidInc = __state; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(TankComponent), "Import")] + private static void TankComponent_Import_Postfix(TankComponent __instance) + { + if (__instance.fluidInc >= 0) + tankIsDustbin[__instance.id] = false; + else + { + tankIsDustbin[__instance.id] = true; + __instance.fluidInc = -__instance.fluidInc - 1; + } + } +} diff --git a/Dustbin/package/manifest.json b/Dustbin/package/manifest.json index 2396eee..6e5d448 100644 --- a/Dustbin/package/manifest.json +++ b/Dustbin/package/manifest.json @@ -1,8 +1,8 @@ { "name": "Dustbin", - "version_number": "1.0.1", + "version_number": "1.1.0", "website_url": "https://github.com/soarqin/DSP_Mods/tree/master/Dustbin", - "description": "Storages can destroy incoming items while capacity limited to zero / 空间限制为0的储物仓可以销毁送进来的物品", + "description": "Can turn Storages and Tanks into Dustbin(Destroy incoming items) / 储物仓和储液罐可以转变为垃圾桶(销毁送进的物品)", "dependencies": [ "xiaoye97-BepInEx-5.4.17" ] diff --git a/README.md b/README.md index 2e8bb09..f715aff 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ Hide/Disable various tutorial tips/messages # [Dustbin](Dustbin) -Storages can destroy incoming items while capacity limited to zero -空间限制为0的储物仓可以销毁送进来的物品 +Can turn Storages and Tanks into Dustbin(Destroy incoming items) +储物仓和储液罐可以转变为垃圾桶(销毁送进的物品) # [OrbitalCollectorBatchBuild](OCBatchBuild)