From 1a242af84e4c15942d9b692bb0c1841b5777373d Mon Sep 17 00:00:00 2001 From: Soar Qin Date: Fri, 14 Nov 2025 15:42:24 +0800 Subject: [PATCH] work in progress --- UXAssist/ModsCompat/BlueprintTweaks.cs | 86 ++++++++++++++++-- UXAssist/Patches/FactoryPatch.cs | 117 ++++++++++++++++--------- UXAssist/UIConfigWindow.cs | 13 ++- UXAssist/UXAssist.cs | 6 +- 4 files changed, 171 insertions(+), 51 deletions(-) diff --git a/UXAssist/ModsCompat/BlueprintTweaks.cs b/UXAssist/ModsCompat/BlueprintTweaks.cs index b23e933..1212ba2 100644 --- a/UXAssist/ModsCompat/BlueprintTweaks.cs +++ b/UXAssist/ModsCompat/BlueprintTweaks.cs @@ -1,6 +1,9 @@ using HarmonyLib; +using System; using System.Collections.Generic; using System.Reflection; +using System.Reflection.Emit; +using UnityEngine; using UXAssist.Functions; namespace UXAssist.ModsCompat; @@ -9,21 +12,92 @@ class BlueprintTweaks { public const string BlueprintTweaksGuid = "org.kremnev8.plugin.BlueprintTweaks"; private static FieldInfo selectObjIdsField; + private static Type classTypeBlueprintTweaksPlugin; + private static Type classTypeUIBuildingGridPatch2; public static bool Run(Harmony harmony) { if (!BepInEx.Bootstrap.Chainloader.PluginInfos.TryGetValue(BlueprintTweaksGuid, out var pluginInfo)) return false; var assembly = pluginInfo.Instance.GetType().Assembly; - var classType = assembly.GetType("BlueprintTweaks.DragRemoveBuildTool"); - if (classType == null) return false; - if (AccessTools.Method(classType, "DetermineMorePreviews") != null) return true; - selectObjIdsField = AccessTools.Field(classType, "selectObjIds"); - harmony.Patch(AccessTools.Method(classType, "DeterminePreviews"), + var classTypeDragRemoveBuildTool = assembly.GetType("BlueprintTweaks.DragRemoveBuildTool"); + if (classTypeDragRemoveBuildTool == null) return false; + if (AccessTools.Method(classTypeDragRemoveBuildTool, "DetermineMorePreviews") != null) return true; + classTypeBlueprintTweaksPlugin = assembly.GetType("BlueprintTweaks.BlueprintTweaksPlugin"); + classTypeUIBuildingGridPatch2 = assembly.GetType("BlueprintTweaks.UIBuildingGridPatch2"); + var UIBuildingGrid_Update = AccessTools.Method(typeof(UIBuildingGrid), nameof(UIBuildingGrid.Update)); + harmony.Patch(AccessTools.Method(classTypeUIBuildingGridPatch2, "UpdateGrid"), null, null, new HarmonyMethod(AccessTools.Method(typeof(BlueprintTweaks), nameof(PatchUpdateGrid)))); + selectObjIdsField = AccessTools.Field(classTypeDragRemoveBuildTool, "selectObjIds"); + harmony.Patch(AccessTools.Method(classTypeDragRemoveBuildTool, "DeterminePreviews"), new HarmonyMethod(AccessTools.Method(typeof(BlueprintTweaks), nameof(PatchDeterminePreviews)))); return true; } - public static void PatchDeterminePreviews(object __instance) + private static readonly int zMin = Shader.PropertyToID("_ZMin"); + private static readonly int reformMode = Shader.PropertyToID("_ReformMode"); + + private static IEnumerable PatchUpdateGrid(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(classTypeBlueprintTweaksPlugin, "tool")), + new CodeMatch(OpCodes.Ldfld), + new CodeMatch(ci => ci.opcode == OpCodes.Brfalse || ci.opcode == OpCodes.Brfalse_S) + ); + var label1 = generator.DefineLabel(); + matcher.Advance(2).Operand = label1; + matcher.Advance(1); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIBuildingGrid), nameof(UIBuildingGrid.blueprintMaterial))), + new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(classTypeUIBuildingGridPatch2, "tintColor")), + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Callvirt) + ).RemoveInstructions(5); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIBuildingGrid), nameof(UIBuildingGrid.blueprintMaterial))), + new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(classTypeUIBuildingGridPatch2, "cursorGratBox")), + new CodeMatch(OpCodes.Ldsfld), + new CodeMatch(OpCodes.Ldfld), + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Callvirt) + ).Advance(1).Operand = AccessTools.Field(typeof(UIBuildingGrid), nameof(UIBuildingGrid.material)); + matcher.Advance(6); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIBuildingGrid), nameof(UIBuildingGrid.blueprintMaterial))), + new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(classTypeUIBuildingGridPatch2, "selectColor")), + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld), + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Callvirt) + ).Advance(1).Operand = AccessTools.Field(typeof(UIBuildingGrid), nameof(UIBuildingGrid.material)); + matcher.Advance(1).Operand = AccessTools.Field(classTypeUIBuildingGridPatch2, "tintColor"); + matcher.Advance(5); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIBuildingGrid), nameof(UIBuildingGrid.blueprintMaterial))), + new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(classTypeUIBuildingGridPatch2, "showDivideLine")), + new CodeMatch(OpCodes.Ldc_R4, 0f), + new CodeMatch(OpCodes.Callvirt) + ).RemoveInstructions(5); + matcher.InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0), + Transpilers.EmitDelegate((UIBuildingGrid grid) => { + grid.material.SetFloat(reformMode, 0f); + grid.material.SetFloat(zMin, -0.5f); + }) + ); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIBuildingGrid), nameof(UIBuildingGrid.blueprintGridRnd))), + new CodeMatch(OpCodes.Ldc_I4_1), + new CodeMatch(OpCodes.Callvirt), + new CodeMatch(ci => ci.opcode == OpCodes.Br || ci.opcode == OpCodes.Br_S) + ).RemoveInstructions(4).Set(OpCodes.Ret, null).Labels.Add(label1); + return matcher.InstructionEnumeration(); + } + + private static void PatchDeterminePreviews(object __instance) { var selectObjIds = (HashSet)selectObjIdsField.GetValue(__instance); var buildTool = (BuildTool)__instance; diff --git a/UXAssist/Patches/FactoryPatch.cs b/UXAssist/Patches/FactoryPatch.cs index b701901..d0bc6e0 100644 --- a/UXAssist/Patches/FactoryPatch.cs +++ b/UXAssist/Patches/FactoryPatch.cs @@ -2337,42 +2337,22 @@ public class FactoryPatch : PatchImpl if (belt.id != entityData.beltId) return; HashSet pathIds = [belt.segPathId]; - if (PressShiftToTakeWholeBeltItemsIncludeBranches.Value) - { - List pendingPathIds = [belt.segPathId]; - while (pendingPathIds.Count > 0) - { - var lastIndex = pendingPathIds.Count - 1; - var thisPathId = pendingPathIds[lastIndex]; - pendingPathIds.RemoveAt(lastIndex); - var path = cargoTraffic.GetCargoPath(thisPathId); - if (path == null) continue; - foreach (var inputPathId in path.inputPaths) - { - if (pathIds.Contains(inputPathId)) continue; - pathIds.Add(inputPathId); - pendingPathIds.Add(inputPathId); - } - if (path.outputPath == null) continue; - var outputPathId = path.outputPath.id; - if (pathIds.Contains(outputPathId)) continue; - pathIds.Add(outputPathId); - pendingPathIds.Add(outputPathId); - } - } - - var mainPlayer = factory.gameData.mainPlayer; - var factorySystem = factory.factorySystem; + HashSet inserterIds = []; + var includeBranches = PressShiftToTakeWholeBeltItemsIncludeBranches.Value; + var includeInserters = PressShiftToTakeWholeBeltItemsIncludeInserters.Value; + List pendingPathIds = [belt.segPathId]; Dictionary takeOutItems = []; - foreach (var pathId in pathIds) + var factorySystem = factory.factorySystem; + while (pendingPathIds.Count > 0) { - var cargoPath = cargoTraffic.GetCargoPath(pathId); - if (cargoPath == null) continue; - var end = cargoPath.bufferLength - 5; - var buffer = cargoPath.buffer; - if (PressShiftToTakeWholeBeltItemsIncludeInserters.Value) + var lastIndex = pendingPathIds.Count - 1; + var thisPathId = pendingPathIds[lastIndex]; + pendingPathIds.RemoveAt(lastIndex); + var path = cargoTraffic.GetCargoPath(thisPathId); + if (path == null) continue; + if (includeInserters) { - foreach (var beltId in cargoPath.belts) + foreach (var beltId in path.belts) { ref var b = ref cargoTraffic.beltPool[beltId]; if (b.id != beltId) return; @@ -2385,18 +2365,62 @@ public class FactoryPatch : PatchImpl if (inserterId <= 0) continue; ref var inserter = ref factorySystem.inserterPool[inserterId]; if (inserter.id != inserterId) continue; - if (inserter.itemId > 0 && inserter.stackCount > 0) + inserterIds.Add(inserterId); + if (includeBranches) { - takeOutItems[inserter.itemId] = (takeOutItems.TryGetValue(inserter.itemId, out var value) ? value : 0) - + ((long)inserter.itemCount | ((long)inserter.itemInc << 32)); - inserter.itemId = 0; - inserter.stackCount = 0; - inserter.itemCount = 0; - inserter.itemInc = 0; + var pickTargetId = inserter.pickTarget; + if (pickTargetId > 0) + { + ref var pickTarget = ref factory.entityPool[pickTargetId]; + if (pickTarget.id == pickTargetId && pickTarget.beltId > 0) + { + ref var pickTargetBelt = ref cargoTraffic.beltPool[pickTarget.beltId]; + if (pickTargetBelt.id == pickTarget.beltId && !pathIds.Contains(pickTargetBelt.segPathId)) + { + pathIds.Add(pickTargetBelt.segPathId); + pendingPathIds.Add(pickTargetBelt.segPathId); + } + } + } + var insertTargetId = inserter.insertTarget; + if (insertTargetId > 0) + { + ref var insertTarget = ref factory.entityPool[insertTargetId]; + if (insertTarget.id == insertTargetId && insertTarget.beltId > 0) + { + ref var insertTargetBelt = ref cargoTraffic.beltPool[insertTarget.beltId]; + if (insertTargetBelt.id == insertTarget.beltId && !pathIds.Contains(insertTargetBelt.segPathId)) + { + pathIds.Add(insertTargetBelt.segPathId); + pendingPathIds.Add(insertTargetBelt.segPathId); + } + } + } } } } } + if (!includeBranches) continue; + foreach (var inputPathId in path.inputPaths) + { + if (pathIds.Contains(inputPathId)) continue; + pathIds.Add(inputPathId); + pendingPathIds.Add(inputPathId); + } + if (path.outputPath == null) continue; + var outputPathId = path.outputPath.id; + if (pathIds.Contains(outputPathId)) continue; + pathIds.Add(outputPathId); + pendingPathIds.Add(outputPathId); + } + + var mainPlayer = factory.gameData.mainPlayer; + foreach (var pathId in pathIds) + { + var cargoPath = cargoTraffic.GetCargoPath(pathId); + if (cargoPath == null) continue; + var end = cargoPath.bufferLength - 5; + var buffer = cargoPath.buffer; for (var i = 0; i <= end;) { if (buffer[i] >= 246) @@ -2425,6 +2449,19 @@ public class FactoryPatch : PatchImpl } } } + foreach (var inserterId in inserterIds) + { + ref var inserter = ref factorySystem.inserterPool[inserterId]; + if (inserter.itemId > 0 && inserter.stackCount > 0) + { + takeOutItems[inserter.itemId] = (takeOutItems.TryGetValue(inserter.itemId, out var value) ? value : 0) + + ((long)inserter.itemCount | ((long)inserter.itemInc << 32)); + inserter.itemId = 0; + inserter.stackCount = 0; + inserter.itemCount = 0; + inserter.itemInc = 0; + } + } foreach (var kvp in takeOutItems) { var added = mainPlayer.TryAddItemToPackage(kvp.Key, (int)(kvp.Value & 0xFFFFFFFF), (int)(kvp.Value >> 32), true, entityId); diff --git a/UXAssist/UIConfigWindow.cs b/UXAssist/UIConfigWindow.cs index 6f9c84a..72a2c96 100644 --- a/UXAssist/UIConfigWindow.cs +++ b/UXAssist/UIConfigWindow.cs @@ -71,6 +71,9 @@ public static class UIConfigWindow I18N.Add("Drag building power poles in maximum connection range", "Drag building power poles in maximum connection range", "拖动建造电线杆时自动使用最大连接距离间隔"); I18N.Add("Build Tesla Tower and Wireless Power Tower alternately", "Build Tesla Tower and Wireless Power Tower alternately", "交替建造电力感应塔和无线输电塔"); I18N.Add("Belt signals for buy out dark fog items automatically", "Belt signals for buy out dark fog items automatically", "用于自动购买黑雾物品的传送带信号"); + I18N.Add("Ctrl+Shift+Click to pick items from whole belts", "Press Ctrl+Shift to pick items from whole belts", "按住Ctrl+Shift键从整条传送抓取物品"); + I18N.Add("Include branches of belts", "Include branches of belts", "包含传送带分支"); + I18N.Add("Include connected inserters", "Include connected inserters (and their connected belts if above is checked)", "包含连接的分拣器(若勾选上面的选项则包含分拣器连接的传送带)"); I18N.Add("Auto-config logistic stations", "Auto-config logistic stations", "自动配置物流设施"); I18N.Add("Limit auto-replenish count to values below", "Limit auto-replenish count to values below", "限制自动补充数量为下面配置的值"); I18N.Add("Dispenser", "Logistics Distributor", "物流配送器"); @@ -404,6 +407,13 @@ public static class UIConfigWindow } } + y += 36f; + wnd.AddCheckBox(x, y, tab2, FactoryPatch.PressShiftToTakeWholeBeltItemsEnabled, "Ctrl+Shift+Click to pick items from whole belts"); + y += 27f; + wnd.AddCheckBox(x + 10, y, tab2, FactoryPatch.PressShiftToTakeWholeBeltItemsIncludeBranches, "Include branches of belts", 13); + y += 27f; + wnd.AddCheckBox(x + 10, y, tab2, FactoryPatch.PressShiftToTakeWholeBeltItemsIncludeInserters, "Include connected inserters", 13); + x = 400f; y = 10f; wnd.AddButton(x, y, tab2, "Initialize This Planet", 16, "button-init-planet", () => @@ -470,7 +480,6 @@ public static class UIConfigWindow FactoryPatch.TweakBuildingBufferEnabled.SettingChanged += TweakBuildingBufferChanged; wnd.OnFree += () => { FactoryPatch.TweakBuildingBufferEnabled.SettingChanged -= TweakBuildingBufferChanged; }; TweakBuildingBufferChanged(null, null); - void TweakBuildingBufferChanged(object o, EventArgs e) { assemblerBufferTimeMultiplierSlider.SetEnable(FactoryPatch.TweakBuildingBufferEnabled.Value); @@ -606,7 +615,7 @@ public static class UIConfigWindow textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Min. Piler Value", 13, "text-amm-min-piler-value"); maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth); y = oy + 1; - var nx = x + maxWidth + 5f; + var nx = x + maxWidth + 5f + 10f; wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigDispenserChargePower, new AutoConfigDispenserChargePowerMapper(), "G", 150f, -100f).WithFontSize(13); y += 18f; wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigDispenserCourierCount, new MyWindow.RangeValueMapper(0, 10), "G", 150f, -100f).WithFontSize(13); diff --git a/UXAssist/UXAssist.cs b/UXAssist/UXAssist.cs index d4a10f4..ea391e1 100644 --- a/UXAssist/UXAssist.cs +++ b/UXAssist/UXAssist.cs @@ -141,11 +141,11 @@ public class UXAssist : BaseUnityPlugin, IModCanSave FactoryPatch.ShortcutKeysForBlueprintCopyEnabled = Config.Bind("Factory", "DismantleBlueprintSelection", false, "Dismantle blueprint selected buildings"); FactoryPatch.PressShiftToTakeWholeBeltItemsEnabled = Config.Bind("Factory", "PressShiftToTakeWholeBeltItems", false, - "Press Ctrl+Shift to take items from whole belts"); + "Ctrl+Shift+Click to pick items from whole belts"); FactoryPatch.PressShiftToTakeWholeBeltItemsIncludeBranches = Config.Bind("Factory", "PressShiftToTakeWholeBeltItemsIncludeBranches", true, - "Press Ctrl+Shift to take items from whole belts: Include branches"); + "Include branches of belts"); FactoryPatch.PressShiftToTakeWholeBeltItemsIncludeInserters = Config.Bind("Factory", "PressShiftToTakeWholeBeltItemsIncludeInserters", true, - "Press Ctrl+Shift to take items from whole belts: Include inserters"); + "Include connected inserters"); LogisticsPatch.AutoConfigLogisticsEnabled = Config.Bind("Factory", "AutoConfigLogistics", false, "Auto-config logistic stations"); LogisticsPatch.AutoConfigLimitAutoReplenishCount = Config.Bind("Factory", "AutoConfigLimitAutoReplenishCount", false,