diff --git a/CheatEnabler/CheatEnabler.cs b/CheatEnabler/CheatEnabler.cs index 4334c97..7c28a5a 100644 --- a/CheatEnabler/CheatEnabler.cs +++ b/CheatEnabler/CheatEnabler.cs @@ -2,6 +2,7 @@ using System.Reflection; using BepInEx; using CheatEnabler.Patches; +using CheatEnabler.Patches.Factory; using HarmonyLib; using UXAssist.Common; using UXAssist.Common.ModFeatures; diff --git a/CheatEnabler/Patches/Factory/ArchitectModePatch.cs b/CheatEnabler/Patches/Factory/ArchitectModePatch.cs new file mode 100644 index 0000000..65aad12 --- /dev/null +++ b/CheatEnabler/Patches/Factory/ArchitectModePatch.cs @@ -0,0 +1,58 @@ +using HarmonyLib; +using UXAssist.Common; + +namespace CheatEnabler.Patches.Factory; + +internal class ArchitectMode : PatchImpl +{ + private static bool[] _canBuildItems; + + protected override void OnEnable() + { + var factory = GameMain.mainPlayer?.factory; + if (factory?.planet?.data != null) + { + FactoryPatch.ArrivePlanet(factory); + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), [typeof(int), typeof(int), typeof(int), typeof(bool)], + [ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Out, ArgumentType.Normal])] + [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), [typeof(int), typeof(int), typeof(int[]), typeof(int), typeof(bool)], + [ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal])] + public static bool TakeTailItemsPatch(StorageComponent __instance, int itemId) + { + if (__instance == null || GameMain.mainPlayer == null || __instance.id != GameMain.mainPlayer.package.id) return true; + if (itemId <= 0) return true; + if (_canBuildItems == null) + { + DoInit(); + } + + return itemId >= 12000 || !_canBuildItems[itemId]; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.GetItemCount), typeof(int))] + public static void GetItemCountPatch(StorageComponent __instance, int itemId, ref int __result) + { + if (__result > 99) return; + if (__instance == null || GameMain.mainPlayer == null || __instance.id != GameMain.mainPlayer.package.id) return; + if (itemId <= 0) return; + if (_canBuildItems == null) + { + DoInit(); + } + if (itemId < 12000 && _canBuildItems[itemId]) __result = 100; + } + + private static void DoInit() + { + _canBuildItems = new bool[12000]; + foreach (var ip in LDB.items.dataArray) + { + if ((ip.Type == EItemType.Logistics || ip.CanBuild) && ip.ID < 12000) _canBuildItems[ip.ID] = true; + } + } +} diff --git a/CheatEnabler/Patches/Factory/BeltSignalPatch.cs b/CheatEnabler/Patches/Factory/BeltSignalPatch.cs new file mode 100644 index 0000000..b3e453f --- /dev/null +++ b/CheatEnabler/Patches/Factory/BeltSignalPatch.cs @@ -0,0 +1,772 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using HarmonyLib; +using UnityEngine; +using UXAssist.Common; +using GameLogicProc = UXAssist.Common.GameLogic; + +namespace CheatEnabler.Patches.Factory; + +internal class BeltSignalGenerator : PatchImpl +{ + private static Dictionary[] _signalBelts; + private static Dictionary _portalFrom; + private static Dictionary> _portalTo; + private static int _signalBeltsCapacity; + private static bool _initialized; + + private class BeltSignal + { + public int SignalId; + public int SpeedLimit; + public byte Stack; + public byte Inc; + public int Progress; + public (int itemId, float itemCount, bool isExtra)[] Sources; + public float[] SourceProgress; + } + + protected override void OnEnable() + { + InitSignalBelts(); + GameLogicProc.OnGameBegin += OnGameBegin; + } + + protected override void OnDisable() + { + GameLogicProc.OnGameBegin -= OnGameBegin; + _initialized = false; + _signalBelts = null; + _signalBeltsCapacity = 0; + } + + internal static void OnAltFormatChanged() + { + if (_signalBelts == null) return; + var factories = GameMain.data?.factories; + if (factories == null) return; + var factoryCount = GameMain.data.factoryCount; + var altFormat = FactoryPatch.BeltSignalNumberAltFormat.Value; + for (var i = Math.Min(_signalBelts.Length, factoryCount) - 1; i >= 0; i--) + { + var factory = factories[i]; + var cargoTraffic = factory?.cargoTraffic; + if (cargoTraffic == null) continue; + var entitySignPool = factory.entitySignPool; + if (entitySignPool == null) continue; + var belts = _signalBelts[i]; + if (belts == null) continue; + foreach (var pair in belts) + { + var beltId = pair.Key; + ref var belt = ref cargoTraffic.beltPool[beltId]; + if (belt.id != beltId) continue; + ref var signal = ref entitySignPool[belt.entityId]; + if (signal.iconId0 < 1000) continue; + var signalBelt = pair.Value; + var inc = signalBelt.Inc / signalBelt.Stack; + if (altFormat) + signal.count0 = signalBelt.SpeedLimit + signalBelt.Stack * 10000 + inc * 100000; + else + signal.count0 = signalBelt.SpeedLimit * 100 + signalBelt.Stack + inc * 10; + } + } + } + + internal static void OnUseProliferatorChanged() + { + if (_signalBelts == null) return; + var factories = GameMain.data?.factories; + if (factories == null) return; + var factoryCount = GameMain.data.factoryCount; + var altFormat = FactoryPatch.BeltSignalNumberAltFormat.Value; + for (var i = Math.Min(_signalBelts.Length, factoryCount) - 1; i >= 0; i--) + { + var factory = factories[i]; + var cargoTraffic = factory?.cargoTraffic; + if (cargoTraffic == null) continue; + var entitySignPool = factory.entitySignPool; + if (entitySignPool == null) continue; + var belts = _signalBelts[i]; + if (belts == null) continue; + foreach (var pair in belts) + { + var beltId = pair.Key; + ref var belt = ref cargoTraffic.beltPool[beltId]; + if (belt.id != beltId) continue; + var signalBelt = pair.Value; + signalBelt.Progress = 0; + signalBelt.Sources = null; + signalBelt.SourceProgress = null; + AddSourcesToBeltSignal(signalBelt); + } + } + } + + private static void InitSignalBelts() + { + if (DSPGame.IsMenuDemo) return; + InitItemSources(); + _signalBelts = new Dictionary[64]; + _signalBeltsCapacity = 64; + _portalFrom = []; + _portalTo = []; + + var factories = GameMain.data?.factories; + if (factories == null) return; + foreach (var factory in factories) + { + var entitySignPool = factory?.entitySignPool; + if (entitySignPool == null) continue; + var cargoTraffic = factory.cargoTraffic; + var beltPool = cargoTraffic.beltPool; + for (var i = cargoTraffic.beltCursor - 1; i > 0; i--) + { + if (beltPool[i].id != i) continue; + ref var signal = ref entitySignPool[beltPool[i].entityId]; + var signalId = signal.iconId0; + if (signalId == 0U) continue; + var number = Mathf.RoundToInt(signal.count0); + switch (signalId) + { + case 404: + SetSignalBelt(factory.index, i, (int)signalId, 0); + continue; + case 600: + case >= 1000 and < 20000: + if (number > 0) + SetSignalBelt(factory.index, i, (int)signalId, number); + continue; + case >= 601 and <= 609: + if (number > 0) + SetSignalBeltPortalTo(factory.index, i, number); + continue; + } + } + } + + _initialized = true; + } + + private static Dictionary GetOrCreateSignalBelts(int index) + { + Dictionary obj; + if (index < 0) return null; + if (index >= _signalBeltsCapacity) + { + var newCapacity = _signalBeltsCapacity * 2; + var newSignalBelts = new Dictionary[newCapacity]; + Array.Copy(_signalBelts, newSignalBelts, _signalBeltsCapacity); + _signalBelts = newSignalBelts; + _signalBeltsCapacity = newCapacity; + } + else + { + obj = _signalBelts[index]; + if (obj != null) return obj; + } + + obj = []; + _signalBelts[index] = obj; + return obj; + } + + private static Dictionary GetSignalBelts(int index) + { + return index >= 0 && index < _signalBeltsCapacity ? _signalBelts[index] : null; + } + + private static void SetSignalBelt(int factory, int beltId, int signalId, int number) + { + int stack; + int inc; + int speedLimit; + if (signalId >= 1000) + { + if (!FactoryPatch.BeltSignalNumberAltFormat.Value) + { + stack = Mathf.Clamp(number % 10, 1, 4); + inc = number / 10 % 10 * stack; + speedLimit = number / 100; + } + else + { + stack = Mathf.Clamp(number / 10000 % 10, 1, 4); + inc = number / 100000 % 10 * stack; + speedLimit = number % 10000; + } + } + else + { + stack = 0; + inc = 0; + speedLimit = number; + } + + if (speedLimit > 3600) speedLimit = 3600; + + var signalBelts = GetOrCreateSignalBelts(factory); + if (signalBelts.TryGetValue(beltId, out var oldBeltSignal)) + { + if (oldBeltSignal.SignalId == signalId && oldBeltSignal.SpeedLimit == speedLimit && oldBeltSignal.Stack == stack && oldBeltSignal.Inc == inc) return; + oldBeltSignal.SpeedLimit = speedLimit; + oldBeltSignal.Stack = (byte)stack; + oldBeltSignal.Inc = (byte)inc; + oldBeltSignal.Progress = 0; + oldBeltSignal.SignalId = signalId; + oldBeltSignal.Sources = null; + oldBeltSignal.SourceProgress = null; + AddSourcesToBeltSignal(oldBeltSignal); + return; + } + + var beltSignal = new BeltSignal + { + SignalId = signalId, + SpeedLimit = speedLimit, + Stack = (byte)stack, + Inc = (byte)inc + }; + AddSourcesToBeltSignal(beltSignal); + signalBelts[beltId] = beltSignal; + } + + private static void AddSourcesToBeltSignal(BeltSignal beltSignal) + { + var itemId = beltSignal.SignalId; + if (itemId < 1000) return; + var result = new Dictionary(); + var extra = new Dictionary(); + var sprayedCount = 0f; + CalculateAllProductions(result, extra, ref sprayedCount, itemId); + + var proliferatorCount = 0f; + if (result.TryGetValue(1143, out var pv)) + { + proliferatorCount = pv; + result.Remove(1143); + } + if (FactoryPatch.BeltSignalUseProliferatorEnabled.Value) + { + if (beltSignal.Inc / beltSignal.Stack >= 4) + { + sprayedCount += 1f; + } + if (sprayedCount > 0) + { + proliferatorCount += sprayedCount / ProliferatorSpayCount; + } + } + if (proliferatorCount > 0f) + { + foreach (var p in ProliferatorSources) + { + result[p.Item1] = (result.TryGetValue(p.Item1, out var v) ? v : 0) + p.Item2 * proliferatorCount / ProliferatorDenom; + } + } + + result.Remove(itemId); + + var cnt = result.Count + extra.Count; + if (cnt == 0) + { + beltSignal.Sources = null; + beltSignal.SourceProgress = null; + return; + } + + var items = new (int itemId, float itemCount, bool isExtra)[cnt]; + var progress = new float[cnt]; + foreach (var p in extra) + { + items[--cnt] = (p.Key, p.Value, true); + } + foreach (var p in result) + { + items[--cnt] = (p.Key, p.Value, false); + } + + beltSignal.Sources = items; + beltSignal.SourceProgress = progress; + } + + private static void SetSignalBeltPortalTo(int factory, int beltId, int number) + { + var v = ((long)factory << 32) | (uint)beltId; + _portalFrom[v] = number; + if (!_portalTo.TryGetValue(number, out var set)) + { + set = []; + _portalTo[number] = set; + } + + set.Add(v); + } + + private static void RemoveSignalBelt(int factory, int beltId) + { + GetSignalBelts(factory)?.Remove(beltId); + } + + private static void RemovePlanetSignalBelts(int factory) + { + GetSignalBelts(factory)?.Clear(); + } + + private static void RemoveSignalBeltPortalEnd(int factory, int beltId) + { + var v = ((long)factory << 32) | (uint)beltId; + if (!_portalFrom.TryGetValue(v, out var number)) return; + _portalFrom.Remove(v); + if (!_portalTo.TryGetValue(number, out var set)) return; + set.Remove(v); + } + + private static void OnGameBegin() + { + if (DSPGame.IsMenuDemo) return; + if (FactoryPatch.BeltSignalGeneratorEnabled.Value) InitSignalBelts(); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(DigitalSystem), MethodType.Constructor, typeof(PlanetData))] + private static void DigitalSystem_Constructor_Postfix(PlanetData _planet) + { + if (!FactoryPatch.BeltSignalGeneratorEnabled.Value) return; + var player = GameMain.mainPlayer; + if (player == null) return; + var factory = _planet?.factory; + if (factory == null) return; + RemovePlanetSignalBelts(factory.index); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RemoveBeltComponent))] + public static void CargoTraffic_RemoveBeltComponent_Prefix(int id) + { + if (!_initialized) return; + var planet = GameMain.localPlanet; + if (planet == null) return; + RemoveSignalBeltPortalEnd(planet.factoryIndex, id); + RemoveSignalBelt(planet.factoryIndex, id); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.SetBeltSignalIcon))] + public static void CargoTraffic_SetBeltSignalIcon_Postfix(CargoTraffic __instance, int signalId, int entityId) + { + if (!_initialized) return; + var planet = GameMain.localPlanet; + if (planet == null) return; + var factory = __instance.factory; + int number; + var needAdd = false; + switch (signalId) + { + case 404: + number = 0; + needAdd = true; + break; + case 600: + case >= 1000 and < 20000: + number = Mathf.RoundToInt(factory.entitySignPool[entityId].count0); + if (number > 0) + needAdd = true; + break; + case >= 601 and <= 609: + number = Mathf.RoundToInt(factory.entitySignPool[entityId].count0); + var factoryIndex = planet.factoryIndex; + var beltId = factory.entityPool[entityId].beltId; + if (number > 0) + SetSignalBeltPortalTo(factoryIndex, beltId, number); + RemoveSignalBelt(factoryIndex, beltId); + return; + default: + number = 0; + break; + } + + { + var factoryIndex = planet.factoryIndex; + var beltId = factory.entityPool[entityId].beltId; + if (needAdd) + { + SetSignalBelt(factoryIndex, beltId, signalId, number); + } + else + { + RemoveSignalBelt(factoryIndex, beltId); + } + + RemoveSignalBeltPortalEnd(factoryIndex, beltId); + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.SetBeltSignalNumber))] + public static void CargoTraffic_SetBeltSignalNumber_Postfix(CargoTraffic __instance, float number, int entityId) + { + if (!_initialized) return; + var planet = GameMain.localPlanet; + if (planet == null) return; + var factory = __instance.factory; + var entitySignPool = factory.entitySignPool; + uint signalId; + if (entitySignPool[entityId].iconType == 0U || (signalId = entitySignPool[entityId].iconId0) == 0U) return; + switch (signalId) + { + case 404: + return; + case 600: + case >= 1000 and < 20000: + break; + case >= 601 and <= 609: + var factoryIndex = planet.factoryIndex; + var beltId = factory.entityPool[entityId].beltId; + RemoveSignalBeltPortalEnd(factoryIndex, beltId); + SetSignalBeltPortalTo(factoryIndex, beltId, Mathf.RoundToInt(number)); + return; + default: + return; + } + + { + var factoryIndex = planet.factoryIndex; + var beltId = factory.entityPool[entityId].beltId; + var n = Mathf.RoundToInt(number); + if (n == 0) + { + RemoveSignalBelt(factoryIndex, beltId); + } + else + { + SetSignalBelt(factoryIndex, beltId, (int)signalId, n); + } + } + } + + private static void ProcessBeltSignals() + { + if (!_initialized) return; + var data = GameMain.data; + var factories = data?.factories; + if (factories == null) return; + DeepProfiler.BeginSample(DPEntry.Belt); + for (var index = data.factoryCount - 1; index >= 0; index--) + { + var factory = factories[index]; + if (factory == null) continue; + var belts = GetSignalBelts(index); + if (belts == null || belts.Count == 0) continue; + var factoryProductionStat = GameMain.statistics.production.factoryStatPool[index]; + var productRegister = factoryProductionStat.productRegister; + var consumeRegister = factoryProductionStat.consumeRegister; + var countRecipe = FactoryPatch.BeltSignalCountRecipeEnabled.Value; + var cargoTraffic = factory.cargoTraffic; + var beltCount = cargoTraffic.beltCursor; + List beltsToRemove = null; + foreach (var pair in belts) + { + if (pair.Key >= beltCount) + { + if (beltsToRemove == null) + beltsToRemove = [pair.Key]; + else + beltsToRemove.Add(pair.Key); + continue; + } + var beltSignal = pair.Value; + var signalId = beltSignal.SignalId; + switch (signalId) + { + case 404: + { + var beltId = pair.Key; + ref var belt = ref cargoTraffic.beltPool[beltId]; + var cargoPath = cargoTraffic.GetCargoPath(belt.segPathId); + if (cargoPath == null) continue; + int itemId; + if ((itemId = cargoPath.TryPickItem(belt.segIndex + belt.segPivotOffset - 5, 12, out var stack, out _)) > 0) + { + if (FactoryPatch.BeltSignalCountRemEnabled.Value) consumeRegister[itemId] += stack; + } + + continue; + } + case 600: + { + if (!_portalTo.TryGetValue(beltSignal.SpeedLimit, out var set)) continue; + var beltId = pair.Key; + ref var belt = ref cargoTraffic.beltPool[beltId]; + var cargoPath = cargoTraffic.GetCargoPath(belt.segPathId); + if (cargoPath == null) continue; + var segIndex = belt.segIndex + belt.segPivotOffset; + if (!cargoPath.GetCargoAtIndex(segIndex, out var cargo, out var cargoId, out var _)) break; + var itemId = cargo.item; + var cargoPool = cargoPath.cargoContainer.cargoPool; + var inc = cargoPool[cargoId].inc; + var stack = cargoPool[cargoId].stack; + foreach (var n in set) + { + var cargoTraffic1 = factories[(int)(n >> 32)].cargoTraffic; + ref var belt1 = ref cargoTraffic1.beltPool[(int)(n & 0x7FFFFFFF)]; + cargoPath = cargoTraffic1.GetCargoPath(belt1.segPathId); + if (cargoPath == null) continue; + if (!cargoPath.TryInsertItem(belt1.segIndex + belt1.segPivotOffset, itemId, stack, inc)) continue; + cargoPath.TryPickItem(segIndex - 5, 12, out var stack1, out var inc1); + if (inc1 != inc || stack1 != stack) + cargoPath.TryPickItem(segIndex - 5, 12, out _, out _); + break; + } + + continue; + } + case >= 1000 and < 20000: + { + var hasSpeedLimit = beltSignal.SpeedLimit > 0; + if (hasSpeedLimit) + { + beltSignal.Progress += beltSignal.SpeedLimit; + switch (beltSignal.Progress) + { + case < 3600: + continue; + case > 18000: + beltSignal.Progress = 14400; + break; + } + } + + var beltId = pair.Key; + ref var belt = ref cargoTraffic.beltPool[beltId]; + var cargoPath = cargoTraffic.GetCargoPath(belt.segPathId); + if (cargoPath == null) continue; + var stack = beltSignal.Stack; + var inc = beltSignal.Inc; + if (!cargoPath.TryInsertItem(belt.segIndex + belt.segPivotOffset, signalId, stack, inc)) continue; + if (hasSpeedLimit) beltSignal.Progress -= 3600; + if (FactoryPatch.BeltSignalCountGenEnabled.Value) productRegister[signalId] += stack; + if (!countRecipe) continue; + var sources = beltSignal.Sources; + if (sources == null) continue; + var progress = beltSignal.SourceProgress; + var stackf = (float)stack; + for (var i = sources.Length - 1; i >= 0; i--) + { + var newCnt = progress[i] + sources[i].itemCount * stackf; + if (newCnt > 0) + { + var itemId = sources[i].itemId; + var cnt = Mathf.CeilToInt(newCnt); + productRegister[itemId] += cnt; + if (!sources[i].isExtra) consumeRegister[itemId] += cnt; + progress[i] = newCnt - cnt; + } + else + { + progress[i] = newCnt; + } + } + + continue; + } + } + } + if (beltsToRemove == null) continue; + foreach (var beltId in beltsToRemove) + { + belts.Remove(beltId); + } + } + + DeepProfiler.EndSample(DPEntry.Belt); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameLogic), nameof(GameLogic.OnFactoryFrameBegin))] + public static void GameLogic_OnFactoryFrameBegin_Postfix() + { + ProcessBeltSignals(); + } + + /* BEGIN: Item sources calculation */ + private static readonly int[] ExtraOreItemIds = [1000, 1116, 1120, 1121, 1208, 5201, 5202, 5203, 5204, 5205, 5206]; + private static readonly HashSet ExtraProliferationItemIds = [1107, 1111, 1125, 1142, 1143, 1202, 1203, 1204, 1205, 1209, 1210, 1301, 1305, 1401, 1402, 1403, 1405, 1406, 1502, 1503, 1802, 6001, 6003, 6004, 6005, 6006]; + private static readonly HashSet NoProliferationItemIds = [1126, 6002]; + // All source items used to create 25 proliferators mk.III (not self-sprayed) + private static readonly List<(int, float)> ProliferatorSources = [(1015, 60f), (1124, 20f), (1006, 64f), (1012, 16f), (1112, 32f), (1141, 64f), (1142, 40f), (1143, 25f)]; + private const float ProliferatorDenom = 21f; + // One sprayed proliferator mk.III can spray 75 items, but one is used for spray itself, so the actual count is 74 + private const float ProliferatorSpayCount = 74f; + private static readonly Dictionary ItemSources = []; + private static bool _itemSourcesInitialized; + + private class ItemSource + { + public float Count; + public Dictionary From; + public Dictionary Extra; + } + + private static void InitItemSources() + { + if (_itemSourcesInitialized) return; + foreach (var vein in LDB.veins.dataArray) + { + ItemSources[vein.MiningItem] = new ItemSource { Count = 1 }; + } + + foreach (var ip in LDB.items.dataArray) + { + if (!string.IsNullOrEmpty(ip.MiningFrom)) + { + ItemSources[ip.ID] = new ItemSource { Count = 1 }; + } + } + + // 水、硫酸、氢、重氢、光子 + foreach (var itemId in ExtraOreItemIds) + { + ItemSources[itemId] = new ItemSource { Count = 1 }; + } + + var recipes = LDB.recipes.dataArray; + foreach (var recipe in recipes) + { + if (!recipe.Explicit || recipe.ID == 58 || recipe.ID == 121) continue; + var res = recipe.Results; + var rescnt = recipe.ResultCounts; + var len = res.Length; + for (var i = 0; i < len; i++) + { + if (ItemSources.ContainsKey(res[i])) continue; + var rs = new ItemSource { Count = rescnt[i], From = [] }; + var it = recipe.Items; + var itcnt = recipe.ItemCounts; + var len2 = it.Length; + for (var j = 0; j < len2; j++) + { + rs.From[it[j]] = itcnt[j]; + } + + if (len > 1) + { + rs.Extra = []; + for (var k = 0; k < len; k++) + { + if (i != k) + { + rs.Extra[res[k]] = rescnt[k]; + } + } + } + + ItemSources[res[i]] = rs; + } + } + + foreach (var recipe in recipes) + { + if (recipe.Explicit) continue; + var res = recipe.Results; + var rescnt = recipe.ResultCounts; + var len = res.Length; + for (var i = 0; i < len; i++) + { + if (ItemSources.ContainsKey(res[i])) continue; + var rs = new ItemSource { Count = rescnt[i], From = [], Extra = null }; + var it = recipe.Items; + var itcnt = recipe.ItemCounts; + var len2 = it.Length; + for (var j = 0; j < len2; j++) + { + rs.From[it[j]] = itcnt[j]; + } + + if (len > 1) + { + rs.Extra = []; + for (var k = 0; k < len; k++) + { + if (i != k) + { + rs.Extra[res[k]] = rescnt[k]; + } + } + } + + ItemSources[res[i]] = rs; + } + } + + _itemSourcesInitialized = true; + } + + private static void CalculateAllProductions(IDictionary result, IDictionary extra, ref float sprayedCount, int itemId, float count = 1f) + { + if (!ItemSources.TryGetValue(itemId, out var itemSource)) + { + return; + } + + var times = 1f; + if (Math.Abs(count - itemSource.Count) > 0.000001f) + { + times = count / itemSource.Count; + } + + result[itemId] = (result.TryGetValue(itemId, out var oldCount) ? oldCount : 0) + count; + if (itemSource.Extra != null) + { + foreach (var p in itemSource.Extra) + { + extra[p.Key] = (extra.TryGetValue(p.Key, out oldCount) ? oldCount : 0) + times * p.Value; + } + } + + if (itemId == 1143 || itemSource.From == null) return; + var useProliferator = FactoryPatch.BeltSignalUseProliferatorEnabled.Value; + if (useProliferator && ExtraProliferationItemIds.Contains(itemId)) + { + times *= 0.8f; + } + foreach (var p in itemSource.From) + { + var value = p.Value * times; + if (useProliferator && !NoProliferationItemIds.Contains(p.Key)) sprayedCount += value; + if (extra.TryGetValue(p.Key, out var rcount)) + { + if (value <= rcount) + { + if (value == rcount) + { + extra.Remove(p.Key); + } + else + { + extra[p.Key] = rcount - value; + } + continue; + } + extra.Remove(p.Key); + value -= rcount; + } + if (result.TryGetValue(p.Key, out rcount)) + { + rcount -= value; + if (rcount <= 0) + { + result.Remove(p.Key); + } + else + { + result[p.Key] = rcount; + } + continue; + } + CalculateAllProductions(result, extra, ref sprayedCount, p.Key, value); + } + } + /* END: Item sources calculation */ +} diff --git a/CheatEnabler/Patches/Factory/FactoryPatch.cs b/CheatEnabler/Patches/Factory/FactoryPatch.cs new file mode 100644 index 0000000..a5cfdca --- /dev/null +++ b/CheatEnabler/Patches/Factory/FactoryPatch.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using BepInEx.Configuration; +using CommonAPI.Systems; +using HarmonyLib; +using UnityEngine; +using UXAssist.Common; +using UXAssist.Common.ModFeatures; +using GameLogicProc = UXAssist.Common.GameLogic; + +namespace CheatEnabler.Patches.Factory; + +[ModFeature("CheatFactory", Order = 10)] +public class FactoryPatch : PatchImpl +{ + public static ConfigEntry ImmediateEnabled; + public static ConfigEntry ArchitectModeEnabled; + public static ConfigEntry NoConditionEnabled; + public static ConfigEntry NoCollisionEnabled; + public static ConfigEntry BeltSignalGeneratorEnabled; + public static ConfigEntry BeltSignalNumberAltFormat; + public static ConfigEntry BeltSignalCountGenEnabled; + public static ConfigEntry BeltSignalCountRemEnabled; + public static ConfigEntry BeltSignalCountRecipeEnabled; + public static ConfigEntry BeltSignalUseProliferatorEnabled; + public static ConfigEntry RemovePowerSpaceLimitEnabled; + public static ConfigEntry BoostWindPowerEnabled; + public static ConfigEntry BoostSolarPowerEnabled; + public static ConfigEntry BoostFuelPowerEnabled; + public static ConfigEntry BoostGeothermalPowerEnabled; + public static ConfigEntry WindTurbinesPowerGlobalCoverageEnabled; + public static ConfigEntry ControlPanelRemoteLogisticsEnabled; + + private static PressKeyBind _noConditionKey; + private static PressKeyBind _noCollisionKey; + internal static HashSet BeltIds; + + public static void Init() + { + _noConditionKey = KeyBindings.RegisterKeyBinding(new BuiltinKey + { + key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true), + conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND, + name = "ToggleNoCondition", + canOverride = true + } + ); + _noCollisionKey = KeyBindings.RegisterKeyBinding(new BuiltinKey + { + key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true), + conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND, + name = "ToggleNoCollision", + canOverride = true + } + ); + I18N.Add("KEYToggleNoCondition", "[CE] Toggle No Condition Build", "[CE] 切换无条件建造"); + I18N.Add("KEYToggleNoCollision", "[CE] Toggle No Collision", "[CE] 切换无碰撞"); + I18N.Add("NoConditionOn", "No condition build is enabled!", "无条件建造已开启"); + I18N.Add("NoConditionOff", "No condition build is disabled!", "无条件建造已关闭"); + I18N.Add("NoCollisionOn", "No collision is enabled!", "无碰撞已开启"); + I18N.Add("NoCollisionOff", "No collision is disabled!", "无碰撞已关闭"); + I18N.Add("Build without condition is enabled!", "!!Build without condition is enabled!!", "!!无条件建造已开启!!"); + I18N.Add("No collision is enabled!", "!!No collision is enabled!!", "!!无碰撞已开启!!"); + + ImmediateEnabled.SettingChanged += (_, _) => ImmediateBuild.Enable(ImmediateEnabled.Value); + ArchitectModeEnabled.SettingChanged += (_, _) => ArchitectMode.Enable(ArchitectModeEnabled.Value); + NoConditionEnabled.SettingChanged += (_, _) => NoConditionBuild.Enable(NoConditionEnabled.Value); + NoCollisionEnabled.SettingChanged += (_, _) => NoCollisionValueChanged(); + BeltSignalGeneratorEnabled.SettingChanged += (_, _) => BeltSignalGenerator.Enable(BeltSignalGeneratorEnabled.Value); + BeltSignalNumberAltFormat.SettingChanged += (_, _) => BeltSignalGenerator.OnAltFormatChanged(); + BeltSignalUseProliferatorEnabled.SettingChanged += (_, _) => BeltSignalGenerator.OnUseProliferatorChanged(); + RemovePowerSpaceLimitEnabled.SettingChanged += (_, _) => RemovePowerSpaceLimit.Enable(RemovePowerSpaceLimitEnabled.Value); + BoostWindPowerEnabled.SettingChanged += (_, _) => BoostWindPower.Enable(BoostWindPowerEnabled.Value); + BoostSolarPowerEnabled.SettingChanged += (_, _) => BoostSolarPower.Enable(BoostSolarPowerEnabled.Value); + BoostFuelPowerEnabled.SettingChanged += (_, _) => BoostFuelPower.Enable(BoostFuelPowerEnabled.Value); + BoostGeothermalPowerEnabled.SettingChanged += (_, _) => BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value); + WindTurbinesPowerGlobalCoverageEnabled.SettingChanged += (_, _) => WindTurbinesPowerGlobalCoverage.Enable(WindTurbinesPowerGlobalCoverageEnabled.Value); + ControlPanelRemoteLogisticsEnabled.SettingChanged += (_, _) => ControlPanelRemoteLogistics.Enable(ControlPanelRemoteLogisticsEnabled.Value); + } + + public static void Start() + { + ImmediateBuild.Enable(ImmediateEnabled.Value); + ArchitectMode.Enable(ArchitectModeEnabled.Value); + NoConditionBuild.Enable(NoConditionEnabled.Value); + NoCollisionValueChanged(); + BeltSignalGenerator.Enable(BeltSignalGeneratorEnabled.Value); + RemovePowerSpaceLimit.Enable(RemovePowerSpaceLimitEnabled.Value); + BoostWindPower.Enable(BoostWindPowerEnabled.Value); + BoostSolarPower.Enable(BoostSolarPowerEnabled.Value); + BoostFuelPower.Enable(BoostFuelPowerEnabled.Value); + BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value); + WindTurbinesPowerGlobalCoverage.Enable(WindTurbinesPowerGlobalCoverageEnabled.Value); + ControlPanelRemoteLogistics.Enable(ControlPanelRemoteLogisticsEnabled.Value); + Enable(true); + CargoTrafficPatch.Enable(true); + GameLogicProc.OnGameBegin += OnGameBegin_For_ImmBuild; + GameLogicProc.OnDataLoaded += OnDataLoaded; + } + + public static void Uninit() + { + GameLogicProc.OnDataLoaded -= OnDataLoaded; + GameLogicProc.OnGameBegin -= OnGameBegin_For_ImmBuild; + CargoTrafficPatch.Enable(false); + Enable(false); + ImmediateBuild.Enable(false); + ArchitectMode.Enable(false); + NoConditionBuild.Enable(false); + BeltSignalGenerator.Enable(false); + RemovePowerSpaceLimit.Enable(false); + BoostWindPower.Enable(false); + BoostSolarPower.Enable(false); + BoostFuelPower.Enable(false); + BoostGeothermalPower.Enable(false); + WindTurbinesPowerGlobalCoverage.Enable(false); + ControlPanelRemoteLogistics.Enable(false); + } + + private static void OnDataLoaded() + { + WindTurbinesPowerGlobalCoverage.Enable(WindTurbinesPowerGlobalCoverageEnabled.Value); + BeltIds ??= [.. LDB.items.dataArray.Where(i => i.prefabDesc.isBelt).Select(i => i.ID)]; + } + + public static void OnInputUpdate() + { + if (_noConditionKey.keyValue) + { + NoConditionEnabled.Value = !NoConditionEnabled.Value; + if (!DSPGame.IsMenuDemo && GameMain.isRunning) + { + UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((NoConditionEnabled.Value ? "NoConditionOn" : "NoConditionOff").Translate()); + } + } + if (_noCollisionKey.keyValue) + { + NoCollisionEnabled.Value = !NoCollisionEnabled.Value; + if (!DSPGame.IsMenuDemo && GameMain.isRunning) + { + UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((NoCollisionEnabled.Value ? "NoCollisionOn" : "NoCollisionOff").Translate()); + } + } + } + + internal static void NoCollisionValueChanged() + { + var coll = ColliderPool.instance; + if (coll == null) return; + var obj = coll.gameObject; + if (obj == null) return; + obj.gameObject.SetActive(!NoCollisionEnabled.Value); + GameMain.data?.warningSystem?.UpdateCriticalWarningText(); + } + + public static void ArrivePlanet(PlanetFactory factory) + { + if (factory.prebuildCount <= 0) return; + var imm = ImmediateEnabled.Value; + var architect = ArchitectModeEnabled.Value; + if ((!imm && !architect) || GameMain.gameScenario == null) return; + var prebuilds = factory.prebuildPool; + if (imm) + { + var player = GameMain.mainPlayer; + for (var i = factory.prebuildCursor - 1; i > 0; i--) + { + ref var pb = ref prebuilds[i]; + if (pb.id != i || pb.isDestroyed) continue; + if (pb.itemRequired > 0) + { + if (!architect) continue; + pb.itemRequired = 0; + } + CargoTrafficPatch.InstantBuild(player, factory, i); + } + CargoTrafficPatch.TryEndBatchBuilding(factory); + } + else if (architect) + { + for (var i = factory.prebuildCursor - 1; i > 0; i--) + { + ref var pb = ref prebuilds[i]; + if (pb.id != i || pb.isDestroyed || pb.itemRequired == 0) continue; + pb.itemRequired = 0; + factory.AlterPrebuildModelState(i); + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(PlanetData), nameof(PlanetData.NotifyFactoryLoaded))] + private static void PlanetData_NotifyFactoryLoaded_Postfix(PlanetData __instance) + { + var main = GameMain.instance; + if (main != null && main._running && __instance.factory?.planet?.data != null) + { + ArrivePlanet(__instance.factory); + } + } + + private static void OnGameBegin_For_ImmBuild() + { + if (DSPGame.IsMenuDemo) return; + var factory = GameMain.mainPlayer?.factory; + if (factory?.planet?.data != null) + { + ArrivePlanet(factory); + } + GameMain.data?.warningSystem?.UpdateCriticalWarningText(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(WarningSystem), nameof(WarningSystem.hasCriticalWarning), MethodType.Getter)] + private static IEnumerable WarningSystem_hasCriticalWarning_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + var label1 = generator.DefineLabel(); + var label2 = generator.DefineLabel(); + matcher.End().MatchBack(false, + new CodeMatch(OpCodes.Ret) + ).RemoveInstructions(1); + matcher.InsertAndAdvance( + new CodeInstruction(OpCodes.Brfalse, label1), + new CodeInstruction(OpCodes.Ldc_I4_1), + new CodeInstruction(OpCodes.Ret), + new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoConditionEnabled))).WithLabels(label1), + new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry), nameof(ConfigEntry.Value))), + new CodeInstruction(OpCodes.Brfalse, label2), + new CodeInstruction(OpCodes.Ldc_I4_1), + new CodeInstruction(OpCodes.Ret), + new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoCollisionEnabled))).WithLabels(label2), + new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry), nameof(ConfigEntry.Value))), + new CodeInstruction(OpCodes.Ret) + ); + return matcher.InstructionEnumeration(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(WarningSystem), nameof(WarningSystem.UpdateCriticalWarningText))] + private static IEnumerable WarningSystem_UpdateCriticalWarningText_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldstr, ""), + new CodeMatch(OpCodes.Call, AccessTools.PropertySetter(typeof(WarningSystem), nameof(WarningSystem.criticalWarningTexts))) + ); + matcher.Repeat(m => + { + var label1 = generator.DefineLabel(); + m.Advance(3).InsertAndAdvance( + new CodeInstruction(OpCodes.Ldarg_0), + Transpilers.EmitDelegate((WarningSystem w) => + { + if (NoConditionEnabled.Value) + { + w.criticalWarningTexts = "Build without condition is enabled!".Translate() + "\r\n"; + } + else if (NoCollisionEnabled.Value) + { + w.criticalWarningTexts = "No collision is enabled!".Translate() + "\r\n"; + } + } + ) + ); + if (m.Opcode == OpCodes.Ret) + { + m.InsertAndAdvance( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))), + new CodeInstruction(OpCodes.Brfalse_S, label1), + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))), + new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Action), nameof(Action.Invoke))) + ); + m.Labels.Add(label1); + } + }); + return matcher.InstructionEnumeration(); + } +} diff --git a/CheatEnabler/Patches/Factory/ImmediateBuildPatch.cs b/CheatEnabler/Patches/Factory/ImmediateBuildPatch.cs new file mode 100644 index 0000000..de3e403 --- /dev/null +++ b/CheatEnabler/Patches/Factory/ImmediateBuildPatch.cs @@ -0,0 +1,259 @@ +using System.Collections.Generic; +using System.Reflection.Emit; +using HarmonyLib; +using UXAssist.Common; + +namespace CheatEnabler.Patches.Factory; + +internal class CargoTrafficPatch : PatchImpl +{ + private static bool _isBatchBuilding; + private static bool _disableRefreshBatchesBuffers; + private static bool _anyBelt; + private static readonly HashSet _alterBeltRendererIds = []; + private static readonly HashSet _alterPathRendererIds = []; + private static readonly HashSet _refreshPathUVIds = []; + + public static bool IsBatchBuilding => _isBatchBuilding; + + public static void StartBatchBuilding(PlanetFactory factory) + { + factory.BeginFlattenTerrain(); + factory.cargoTraffic._batch_buffer_no_refresh = true; + PlanetFactory.batchBuild = true; + _isBatchBuilding = true; + _disableRefreshBatchesBuffers = true; + _anyBelt = false; + } + + public static void EndBatchBuilding(PlanetFactory factory) + { + PlanetFactory.batchBuild = false; + factory.cargoTraffic._batch_buffer_no_refresh = false; + factory.EndFlattenTerrain(); + _isBatchBuilding = false; + var cargoTraffic = factory.cargoTraffic; + var entityPool = factory.entityPool; + var colChunks = factory.planet.physics?.colChunks; + foreach (var beltId in _alterBeltRendererIds) + { + cargoTraffic.AlterBeltRenderer(beltId, entityPool, colChunks, false); + } + foreach (var pathId in _alterPathRendererIds) + { + cargoTraffic.AlterPathRenderer(pathId, false); + } + foreach (var pathId in _refreshPathUVIds) + { + cargoTraffic.RefreshPathUV(pathId); + } + _alterBeltRendererIds.Clear(); + _alterPathRendererIds.Clear(); + _refreshPathUVIds.Clear(); + _disableRefreshBatchesBuffers = false; + if (_anyBelt) + { + factory.cargoTraffic.RefreshBeltBatchesBuffers(); + factory.cargoTraffic.RefreshPathBatchesBuffers(); + } + _anyBelt = false; + factory.planet.physics?.raycastLogic?.NotifyBatchObjectRemove(); + factory.planet.audio?.SetPlanetAudioDirty(); + } + + public static void TryEndBatchBuilding(PlanetFactory factory) + { + if (!_isBatchBuilding) return; + EndBatchBuilding(factory); + } + + public static void InstantBuild(Player player, PlanetFactory factory, int id) + { + if (!_isBatchBuilding) StartBatchBuilding(factory); + _anyBelt = _anyBelt || (FactoryPatch.BeltIds?.Contains(factory.prebuildPool[id].protoId) ?? false); + factory.BuildFinally(player, id, false); + } + + [HarmonyPrefix] + [HarmonyPriority(Priority.First)] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.AlterBeltRenderer))] + private static bool CargoTraffic_AlterBeltRenderer_Prefix(int beltId) + { + if (!_isBatchBuilding) return true; + _alterBeltRendererIds.Add(beltId); + return false; + } + + [HarmonyPrefix] + [HarmonyPriority(Priority.First)] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.AlterPathRenderer))] + private static bool CargoTraffic_AlterPathRenderer_Prefix(int pathId) + { + if (!_isBatchBuilding) return true; + _alterPathRendererIds.Add(pathId); + return false; + } + + [HarmonyPrefix] + [HarmonyPriority(Priority.First)] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RefreshPathUV))] + private static bool CargoTraffic_RefreshPathUV_Prefix(int pathId) + { + if (!_isBatchBuilding) return true; + _refreshPathUVIds.Add(pathId); + return false; + } + + [HarmonyPrefix] + [HarmonyPriority(Priority.First)] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RefreshBeltBatchesBuffers))] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RefreshPathBatchesBuffers))] + private static bool CargoTraffic_RefreshBeltBatchesBuffers_Prefix() + { + return !_disableRefreshBatchesBuffers; + } +} + +internal class ImmediateBuild : PatchImpl +{ + protected override void OnEnable() + { + var factory = GameMain.mainPlayer?.factory; + if (factory?.planet?.data != null) + { + FactoryPatch.ArrivePlanet(factory); + } + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.CreatePrebuilds))] + [HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CreatePrebuilds))] + [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CreatePrebuilds))] + [HarmonyPatch(typeof(BuildTool_Inserter), nameof(BuildTool_Inserter.CreatePrebuilds))] + [HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CreatePrebuilds))] + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.End().MatchBack(false, + new CodeMatch(OpCodes.Ret) + ); + if (matcher.IsInvalid) + { + CheatEnabler.Logger.LogWarning($"Failed to patch CreatePrebuilds"); + return matcher.InstructionEnumeration(); + } + + matcher.Advance(-1); + if (matcher.Opcode != OpCodes.Nop && (matcher.Opcode != OpCodes.Call || !matcher.Instruction.OperandIs(AccessTools.Method(typeof(System.GC), nameof(System.GC.Collect))))) + { + CheatEnabler.Logger.LogWarning($"Failed to patch CreatePrebuilds: last instruction is not `Nop` or `Call GC.Collect()`: {matcher.Instruction}"); + return matcher.InstructionEnumeration(); + } + + var labels = matcher.Labels; + matcher.Labels = []; + matcher.Insert( + new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels), + new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool), nameof(BuildTool.factory))), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FactoryPatch), nameof(FactoryPatch.ArrivePlanet))) + ); + return matcher.InstructionEnumeration(); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(GameLogic), nameof(GameLogic.FactoryConstructionSystemGameTick))] + private static void GameLogic_FactoryConstructionSystemGameTick_Prefix(GameLogic __instance) + { + var time = __instance.timei; + if (time % 6 != 0) return; + var planet = GameMain.localPlanet; + if (planet == null || !planet.factoryLoaded) return; + var factory = planet.factory; + if (factory == null || factory.prebuildCount <= 0) return; + var player = GameMain.mainPlayer; + if (player == null) return; + var total = factory.prebuildCursor - 1; + var stepCount = total switch + { + < 256 => 1, + < 2048 => 3, + < 16384 => 10, + _ => 20, + }; + var step = (int)(time / 6 % stepCount); + var start = 1 + total * step / stepCount; + var end = 1 + total * (step + 1) / stepCount; + for (var i = start; i < end; i++) + { + ref var prebuild = ref factory.prebuildPool[i]; + if (prebuild.id != i || prebuild.isDestroyed) continue; + if (prebuild.itemRequired > 0) + { + int itemId = prebuild.protoId; + int count = prebuild.itemRequired; + player.package.TakeTailItems(ref itemId, ref count, out var _, false); + if (count > 0) + { + prebuild.itemRequired -= count; + if (prebuild.itemRequired <= 0) + { + CargoTrafficPatch.InstantBuild(player, factory, i); + } + } + } + } + if (CargoTrafficPatch.IsBatchBuilding) + { + for (var i = start - 1; i > 0; i--) + { + ref var prebuild = ref factory.prebuildPool[i]; + if (prebuild.id != i || prebuild.isDestroyed) continue; + if (prebuild.itemRequired > 0) + { + int itemId = prebuild.protoId; + int count = prebuild.itemRequired; + player.package.TakeTailItems(ref itemId, ref count, out var _, false); + if (count > 0) + { + prebuild.itemRequired -= count; + if (prebuild.itemRequired <= 0) + { + CargoTrafficPatch.InstantBuild(player, factory, i); + } + } + } + } + for (var i = end; i <= total; i++) + { + ref var prebuild = ref factory.prebuildPool[i]; + if (prebuild.id != i || prebuild.isDestroyed) continue; + if (prebuild.itemRequired > 0) + { + int itemId = prebuild.protoId; + int count = prebuild.itemRequired; + player.package.TakeTailItems(ref itemId, ref count, out var _, false); + if (count > 0) + { + prebuild.itemRequired -= count; + if (prebuild.itemRequired <= 0) + { + CargoTrafficPatch.InstantBuild(player, factory, i); + } + } + } + } + CargoTrafficPatch.EndBatchBuilding(factory); + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UXAssist.Functions.PlanetFunctions), nameof(UXAssist.Functions.PlanetFunctions.BuildOrbitalCollectors))] + private static void UXAssist_PlanetFunctions_BuildOrbitalCollectors_Postfix() + { + var factory = GameMain.mainPlayer?.factory; + if (factory?.planet?.data != null) + { + FactoryPatch.ArrivePlanet(factory); + } + } +} diff --git a/CheatEnabler/Patches/Factory/LogisticsControlPatch.cs b/CheatEnabler/Patches/Factory/LogisticsControlPatch.cs new file mode 100644 index 0000000..9dad879 --- /dev/null +++ b/CheatEnabler/Patches/Factory/LogisticsControlPatch.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using System.Reflection.Emit; +using HarmonyLib; +using UXAssist.Common; + +namespace CheatEnabler.Patches.Factory; + +internal class ControlPanelRemoteLogistics : PatchImpl +{ + [HarmonyTranspiler] + [HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnItemIconMouseDown))] + [HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnHoldupItemClick))] + [HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnCourierIconClick))] + private static IEnumerable UIControlPanelDispenserInspector_OnItemIconMouseDown_Transpiler(IEnumerable instructions) + { + var matcher = new CodeMatcher(instructions); + Label? branch = null; + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.isLocal))), + new CodeMatch(ci => ci.Branches(out branch)) + ).Repeat( + m => + { + if (branch == null) + { + m.Advance(3); + return; + } + var labels = m.Labels; + m.RemoveInstructions(3).InsertAndAdvance( + new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) + ); + } + ); + return matcher.InstructionEnumeration(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnShipIconClick))] + [HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnWarperIconClick))] + [HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnDroneIconClick))] + private static IEnumerable UIControlPanelStationInspector_OnShipIconClick_Transpiler(IEnumerable instructions) + { + var matcher = new CodeMatcher(instructions); + Label? branch = null; + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.isLocal))), + new CodeMatch(ci => ci.Branches(out branch)) + ).Repeat( + m => + { + if (branch == null) + { + m.Advance(3); + return; + } + var labels = m.Labels; + m.RemoveInstructions(3).InsertAndAdvance( + new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) + ); + } + ); + return matcher.InstructionEnumeration(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnItemIconMouseDown))] + private static IEnumerable UIControlPanelStationStorage_OnItemIconMouseDown_Transpiler(IEnumerable instructions) + { + var matcher = new CodeMatcher(instructions); + Label? branch = null; + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.isLocal))), + new CodeMatch(ci => ci.Branches(out branch)) + ).Repeat( + m => + { + if (branch == null) + { + m.Advance(3); + return; + } + var labels = m.Labels; + m.RemoveInstructions(3).InsertAndAdvance( + new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) + ); + } + ); + return matcher.InstructionEnumeration(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnTakeBackButtonClick))] + private static IEnumerable UIControlPanelStationStorage_OnTakeBackButtonClick_Transpiler(IEnumerable instructions) + { + var matcher = new CodeMatcher(instructions); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.isLocal))), + new CodeMatch(ci => ci.Branches(out _)) + ).Repeat( + m => + { + var labels = m.Labels; + m.RemoveInstructions(3).Labels.AddRange(labels); + } + ); + return matcher.InstructionEnumeration(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UIControlPanelVeinCollectorPanel), nameof(UIControlPanelVeinCollectorPanel.OnProductIconClick))] + private static IEnumerable UIControlPanelVeinCollectorPanel_OnProductIconClick_Transpiler(IEnumerable instructions) + { + var matcher = new CodeMatcher(instructions); + Label? branch = null; + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelVeinCollectorPanel), nameof(UIControlPanelVeinCollectorPanel.isLocal))), + new CodeMatch(ci => ci.Branches(out branch)) + ).Repeat( + m => + { + if (branch == null) + { + m.Advance(3); + return; + } + var labels = m.Labels; + m.RemoveInstructions(3).InsertAndAdvance( + new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) + ); + } + ); + return matcher.InstructionEnumeration(); + } +} diff --git a/CheatEnabler/Patches/Factory/NoConditionBuildPatch.cs b/CheatEnabler/Patches/Factory/NoConditionBuildPatch.cs new file mode 100644 index 0000000..b3d02b8 --- /dev/null +++ b/CheatEnabler/Patches/Factory/NoConditionBuildPatch.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.Reflection.Emit; +using HarmonyLib; +using UXAssist.Common; + +namespace CheatEnabler.Patches.Factory; + +internal class NoConditionBuild : PatchImpl +{ + protected override void OnEnable() + { + GameMain.data?.warningSystem?.UpdateCriticalWarningText(); + } + + protected override void OnDisable() + { + GameMain.data?.warningSystem?.UpdateCriticalWarningText(); + } + + [HarmonyTranspiler, HarmonyPriority(Priority.Last)] + [HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.CheckBuildConditions))] + [HarmonyPatch(typeof(BuildTool_Inserter), nameof(BuildTool_Inserter.CheckBuildConditions))] + private static IEnumerable BuildTool_CheckBuildConditions_Transpiler(IEnumerable instructions) + { + yield return new CodeInstruction(OpCodes.Ldc_I4_1); + yield return new CodeInstruction(OpCodes.Ret); + } + + [HarmonyTranspiler, HarmonyPriority(Priority.First)] + [HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CheckBuildConditions))] + private static IEnumerable BuildTool_Path_CheckBuildConditions_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + var label1 = generator.DefineLabel(); + var label2 = generator.DefineLabel(); + matcher.Start().InsertAndAdvance( + new CodeInstruction(OpCodes.Br, label1) + ); + matcher.MatchForward(false, + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(BuildTool), nameof(BuildTool.buildPreviews))), + new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List), nameof(List.Count))), + new CodeMatch(ci => ci.IsStloc()) + ); + matcher.Labels.Add(label1); + matcher.Advance(4).InsertAndAdvance( + new CodeInstruction(OpCodes.Br, label2) + ); + matcher.MatchForward(false, + new CodeMatch(ci => ci.IsLdloc()), + new CodeMatch(ci => ci.Branches(out _)), + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool_Path), nameof(BuildTool_Path.waitForConfirm))), + new CodeMatch(ci => ci.Branches(out _)) + ); + var operand = matcher.Operand; + matcher.InsertAndAdvance( + new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(label2), + new CodeInstruction(OpCodes.Stloc_S, operand) + ); + return matcher.InstructionEnumeration(); + } + + [HarmonyTranspiler, HarmonyPriority(Priority.Last)] + [HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))] + [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))] + private static IEnumerable BuildTool_Click_CheckBuildConditions_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + var label1 = generator.DefineLabel(); + matcher.Start().InsertAndAdvance( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NoConditionBuild), nameof(CheckForMiner))), + new CodeInstruction(OpCodes.Brfalse_S, label1), + new CodeInstruction(OpCodes.Ldc_I4_1), + new CodeInstruction(OpCodes.Ret) + ); + matcher.Labels.Add(label1); + return matcher.InstructionEnumeration(); + } + + public static bool CheckForMiner(BuildTool tool) + { + var previews = tool.buildPreviews; + foreach (var preview in previews) + { + var desc = preview?.item?.prefabDesc; + if (desc == null) continue; + if (desc.veinMiner || desc.oilMiner) return false; + } + + return true; + } +} diff --git a/CheatEnabler/Patches/Factory/PowerBoostPatch.cs b/CheatEnabler/Patches/Factory/PowerBoostPatch.cs new file mode 100644 index 0000000..0679074 --- /dev/null +++ b/CheatEnabler/Patches/Factory/PowerBoostPatch.cs @@ -0,0 +1,211 @@ +using System.Collections.Generic; +using System.Reflection.Emit; +using HarmonyLib; +using UnityEngine; +using UXAssist.Common; + +namespace CheatEnabler.Patches.Factory; + +internal class RemovePowerSpaceLimit : PatchImpl +{ + [HarmonyTranspiler] + [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))] + [HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))] + private static IEnumerable BuildTool_CheckBuildConditions_Transpiler(IEnumerable instructions) + { + var matcher = new CodeMatcher(instructions); + matcher.Start().MatchForward(false, + new CodeMatch(OpCodes.Ldc_R4, 110.25f) + ); + if (matcher.IsValid) + { + matcher.Repeat(codeMatcher => codeMatcher.SetAndAdvance( + OpCodes.Ldc_R4, 1f + )); + } + matcher.Start().MatchForward(false, + new CodeMatch(OpCodes.Ldc_R4, 144f) + ); + if (matcher.IsValid) + { + matcher.Repeat(codeMatcher => codeMatcher.SetAndAdvance( + OpCodes.Ldc_R4, 1f + )); + } + return matcher.InstructionEnumeration(); + } +} + +internal class BoostWindPower : PatchImpl +{ + [HarmonyTranspiler] + [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Wind))] + private static IEnumerable PowerGeneratorComponent_EnergyCap_Wind_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.Start().RemoveInstructions(matcher.Length); + matcher.Insert( + // this.currentStrength = windStrength + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldarg_1), + new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.currentStrength))), + // this.capacityCurrentTick = 500000000L + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldc_I8, 500000000L), + new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))), + // return 500000000L + new CodeInstruction(OpCodes.Ldc_I8, 500000000L), + new CodeInstruction(OpCodes.Ret) + ); + return matcher.InstructionEnumeration(); + } +} + +internal class BoostSolarPower : PatchImpl +{ + [HarmonyTranspiler] + [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_PV))] + private static IEnumerable PowerGeneratorComponent_EnergyCap_PV_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.Start().RemoveInstructions(matcher.Length).Insert( + // this.currentStrength = lumino + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldarg_S, 4), + new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.currentStrength))), + // this.capacityCurrentTick = 600000000L + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldc_I8, 600000000L), + new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))), + // return 600000000L + new CodeInstruction(OpCodes.Ldc_I8, 600000000L), + new CodeInstruction(OpCodes.Ret) + ); + return matcher.InstructionEnumeration(); + } +} + +internal class BoostFuelPower : PatchImpl +{ + [HarmonyTranspiler] + [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Fuel))] + private static IEnumerable PowerGeneratorComponent_EnergyCap_Fuel_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + var label1 = generator.DefineLabel(); + var label2 = generator.DefineLabel(); + var label3 = generator.DefineLabel(); + matcher.Start().MatchForward(false, + new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))) + ); + var labels = matcher.Labels; + matcher.Labels = []; + matcher.Insert( + // if (this.fuelMask == 4) + new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels), + new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.fuelMask))), + new CodeInstruction(OpCodes.Ldc_I4_4), + new CodeInstruction(OpCodes.Bne_Un_S, label1), + // multiplier = 10000L + new CodeInstruction(OpCodes.Ldc_I8, 10000L), + new CodeInstruction(OpCodes.Br_S, label3), + // else if (this.fuelMask == 2) + new CodeInstruction(OpCodes.Ldarg_0).WithLabels(label1), + new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.fuelMask))), + new CodeInstruction(OpCodes.Ldc_I4_2), + new CodeInstruction(OpCodes.Bne_Un_S, label2), + // multiplier = 20000L + new CodeInstruction(OpCodes.Ldc_I8, 20000L), + new CodeInstruction(OpCodes.Br_S, label3), + // else multiplier = 50000L + new CodeInstruction(OpCodes.Ldc_I8, 50000L).WithLabels(label2), + // do multiplier before store to this.capacityCurrentTick + new CodeInstruction(OpCodes.Mul).WithLabels(label3) + ); + return matcher.InstructionEnumeration(); + } +} + +internal class BoostGeothermalPower : PatchImpl +{ + [HarmonyTranspiler] + [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_GTH))] + private static IEnumerable PowerGeneratorComponent_EnergyCap_GTH_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var matcher = new CodeMatcher(instructions, generator); + matcher.Start().RemoveInstructions(matcher.Length).Insert( + // this.currentStrength = this.gthStrength + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.gthStrength))), + new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.currentStrength))), + // this.capacityCurrentTick = 2000000000L + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldc_I8, 2000000000L), + new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))), + // return 2000000000L + new CodeInstruction(OpCodes.Ldc_I8, 2000000000L), + new CodeInstruction(OpCodes.Ret) + ); + return matcher.InstructionEnumeration(); + } +} + +internal static class WindTurbinesPowerGlobalCoverage +{ + private static bool _patched; + private static PrefabDesc _prefabdesc; + private static float _oldCoverRadius; + private static float _oldConnectDistance; + private const int WindTurbineId = 2203; + private const float WindTurbineNewCoverageDistance = 500f; + + public static void Enable(bool enable) + { + if (enable) + { + if (_patched) return; + _patched = true; + var itemProto = LDB.items.Select(WindTurbineId); + _oldCoverRadius = itemProto.prefabDesc.powerCoverRadius; + _oldConnectDistance = itemProto.prefabDesc.powerConnectDistance; + itemProto.prefabDesc.powerCoverRadius = WindTurbineNewCoverageDistance; + itemProto.prefabDesc.powerConnectDistance = WindTurbineNewCoverageDistance; + _prefabdesc = itemProto.prefabDesc; + } + else + { + if (!_patched) return; + _patched = false; + _prefabdesc.powerCoverRadius = _oldCoverRadius; + _prefabdesc.powerConnectDistance = _oldConnectDistance; + } + + // Iterate all factories and update wind turbines power nodes + if (GameMain.data == null) return; + foreach (var factory in GameMain.data.factories) + { + var powerSystem = factory?.powerSystem; + if (powerSystem == null) continue; + for (var i = powerSystem.nodeCursor - 1; i >= 0; i--) + { + ref var node = ref powerSystem.nodePool[i]; + if (node.id != i) continue; + ref var entity = ref factory.entityPool[node.entityId]; + if (entity.protoId != WindTurbineId) continue; + // Disconnect from power system + powerSystem.OnNodeRemoving(i); + // Set new properties + node.connectDistance = _prefabdesc.powerConnectDistance; + node.coverRadius = _prefabdesc.powerCoverRadius; + // Connect back to power system + powerSystem.OnNodeAdded(i); + } + // Refresh power nodes rendering if factory is loaded + if (factory.planet.factoryLoaded) + { + factory.planet.factoryModel.RefreshPowerNodes(); + } + } + } +} diff --git a/CheatEnabler/Patches/FactoryPatch.cs b/CheatEnabler/Patches/FactoryPatch.cs deleted file mode 100644 index 968c4bb..0000000 --- a/CheatEnabler/Patches/FactoryPatch.cs +++ /dev/null @@ -1,1815 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection.Emit; -using BepInEx.Configuration; -using CommonAPI.Systems; -using HarmonyLib; -using UnityEngine; -using UnityEngine.UI; -using UXAssist.Common; -using UXAssist.Common.ModFeatures; -using GameLogicProc = UXAssist.Common.GameLogic; - -namespace CheatEnabler.Patches; - -[ModFeature("Factory")] -public class FactoryPatch : PatchImpl -{ - public static ConfigEntry ImmediateEnabled; - public static ConfigEntry ArchitectModeEnabled; - public static ConfigEntry NoConditionEnabled; - public static ConfigEntry NoCollisionEnabled; - public static ConfigEntry BeltSignalGeneratorEnabled; - public static ConfigEntry BeltSignalNumberAltFormat; - public static ConfigEntry BeltSignalCountGenEnabled; - public static ConfigEntry BeltSignalCountRemEnabled; - public static ConfigEntry BeltSignalCountRecipeEnabled; - public static ConfigEntry BeltSignalUseProliferatorEnabled; - public static ConfigEntry RemovePowerSpaceLimitEnabled; - public static ConfigEntry BoostWindPowerEnabled; - public static ConfigEntry BoostSolarPowerEnabled; - public static ConfigEntry BoostFuelPowerEnabled; - public static ConfigEntry BoostGeothermalPowerEnabled; - public static ConfigEntry WindTurbinesPowerGlobalCoverageEnabled; - public static ConfigEntry ControlPanelRemoteLogisticsEnabled; - - private static PressKeyBind _noConditionKey; - private static PressKeyBind _noCollisionKey; - - public static void Init() - { - _noConditionKey = KeyBindings.RegisterKeyBinding(new BuiltinKey - { - key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true), - conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND, - name = "ToggleNoCondition", - canOverride = true - } - ); - _noCollisionKey = KeyBindings.RegisterKeyBinding(new BuiltinKey - { - key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true), - conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND, - name = "ToggleNoCollision", - canOverride = true - } - ); - I18N.Add("KEYToggleNoCondition", "[CE] Toggle No Condition Build", "[CE] 切换无条件建造"); - I18N.Add("KEYToggleNoCollision", "[CE] Toggle No Collision", "[CE] 切换无碰撞"); - I18N.Add("NoConditionOn", "No condition build is enabled!", "无条件建造已开启"); - I18N.Add("NoConditionOff", "No condition build is disabled!", "无条件建造已关闭"); - I18N.Add("NoCollisionOn", "No collision is enabled!", "无碰撞已开启"); - I18N.Add("NoCollisionOff", "No collision is disabled!", "无碰撞已关闭"); - I18N.Add("Build without condition is enabled!", "!!Build without condition is enabled!!", "!!无条件建造已开启!!"); - I18N.Add("No collision is enabled!", "!!No collision is enabled!!", "!!无碰撞已开启!!"); - - ImmediateEnabled.SettingChanged += (_, _) => ImmediateBuild.Enable(ImmediateEnabled.Value); - ArchitectModeEnabled.SettingChanged += (_, _) => ArchitectMode.Enable(ArchitectModeEnabled.Value); - NoConditionEnabled.SettingChanged += (_, _) => NoConditionBuild.Enable(NoConditionEnabled.Value); - NoCollisionEnabled.SettingChanged += (_, _) => NoCollisionValueChanged(); - BeltSignalGeneratorEnabled.SettingChanged += (_, _) => BeltSignalGenerator.Enable(BeltSignalGeneratorEnabled.Value); - BeltSignalNumberAltFormat.SettingChanged += (_, _) => BeltSignalGenerator.OnAltFormatChanged(); - BeltSignalUseProliferatorEnabled.SettingChanged += (_, _) => BeltSignalGenerator.OnUseProliferatorChanged(); - RemovePowerSpaceLimitEnabled.SettingChanged += (_, _) => RemovePowerSpaceLimit.Enable(RemovePowerSpaceLimitEnabled.Value); - BoostWindPowerEnabled.SettingChanged += (_, _) => BoostWindPower.Enable(BoostWindPowerEnabled.Value); - BoostSolarPowerEnabled.SettingChanged += (_, _) => BoostSolarPower.Enable(BoostSolarPowerEnabled.Value); - BoostFuelPowerEnabled.SettingChanged += (_, _) => BoostFuelPower.Enable(BoostFuelPowerEnabled.Value); - BoostGeothermalPowerEnabled.SettingChanged += (_, _) => BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value); - WindTurbinesPowerGlobalCoverageEnabled.SettingChanged += (_, _) => WindTurbinesPowerGlobalCoverage.Enable(WindTurbinesPowerGlobalCoverageEnabled.Value); - ControlPanelRemoteLogisticsEnabled.SettingChanged += (_, _) => ControlPanelRemoteLogistics.Enable(ControlPanelRemoteLogisticsEnabled.Value); - } - - public static void Start() - { - ImmediateBuild.Enable(ImmediateEnabled.Value); - ArchitectMode.Enable(ArchitectModeEnabled.Value); - NoConditionBuild.Enable(NoConditionEnabled.Value); - NoCollisionValueChanged(); - BeltSignalGenerator.Enable(BeltSignalGeneratorEnabled.Value); - RemovePowerSpaceLimit.Enable(RemovePowerSpaceLimitEnabled.Value); - BoostWindPower.Enable(BoostWindPowerEnabled.Value); - BoostSolarPower.Enable(BoostSolarPowerEnabled.Value); - BoostFuelPower.Enable(BoostFuelPowerEnabled.Value); - BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value); - ControlPanelRemoteLogistics.Enable(ControlPanelRemoteLogisticsEnabled.Value); - Enable(true); - CargoTrafficPatch.Enable(true); - GameLogicProc.OnGameBegin += OnGameBegin_For_ImmBuild; - GameLogicProc.OnDataLoaded += OnDataLoaded; - } - - public static void Uninit() - { - GameLogicProc.OnDataLoaded -= OnDataLoaded; - GameLogicProc.OnGameBegin -= OnGameBegin_For_ImmBuild; - CargoTrafficPatch.Enable(false); - Enable(false); - ImmediateBuild.Enable(false); - ArchitectMode.Enable(false); - NoConditionBuild.Enable(false); - BeltSignalGenerator.Enable(false); - RemovePowerSpaceLimit.Enable(false); - BoostWindPower.Enable(false); - BoostSolarPower.Enable(false); - BoostFuelPower.Enable(false); - BoostGeothermalPower.Enable(false); - WindTurbinesPowerGlobalCoverage.Enable(false); - ControlPanelRemoteLogistics.Enable(false); - } - - private static HashSet _beltIds = []; - - private static void OnDataLoaded() - { - WindTurbinesPowerGlobalCoverage.Enable(WindTurbinesPowerGlobalCoverageEnabled.Value); - _beltIds ??= [.. LDB.items.dataArray.Where(i => i.prefabDesc.isBelt).Select(i => i.ID)]; - } - - public static void OnInputUpdate() - { - if (_noConditionKey.keyValue) - { - NoConditionEnabled.Value = !NoConditionEnabled.Value; - if (!DSPGame.IsMenuDemo && GameMain.isRunning) - { - UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((NoConditionEnabled.Value ? "NoConditionOn" : "NoConditionOff").Translate()); - } - } - if (_noCollisionKey.keyValue) - { - NoCollisionEnabled.Value = !NoCollisionEnabled.Value; - if (!DSPGame.IsMenuDemo && GameMain.isRunning) - { - UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((NoCollisionEnabled.Value ? "NoCollisionOn" : "NoCollisionOff").Translate()); - } - } - } - - private static void NoCollisionValueChanged() - { - var coll = ColliderPool.instance; - if (coll == null) return; - var obj = coll.gameObject; - if (obj == null) return; - obj.gameObject.SetActive(!NoCollisionEnabled.Value); - GameMain.data?.warningSystem?.UpdateCriticalWarningText(); - } - - public static void ArrivePlanet(PlanetFactory factory) - { - if (factory.prebuildCount <= 0) return; - var imm = ImmediateEnabled.Value; - var architect = ArchitectModeEnabled.Value; - if ((!imm && !architect) || GameMain.gameScenario == null) return; - var prebuilds = factory.prebuildPool; - if (imm) - { - var player = GameMain.mainPlayer; - for (var i = factory.prebuildCursor - 1; i > 0; i--) - { - ref var pb = ref prebuilds[i]; - if (pb.id != i || pb.isDestroyed) continue; - if (pb.itemRequired > 0) - { - if (!architect) continue; - pb.itemRequired = 0; - } - CargoTrafficPatch.InstantBuild(player, factory, i); - } - CargoTrafficPatch.TryEndBatchBuilding(factory); - } - else if (architect) - { - for (var i = factory.prebuildCursor - 1; i > 0; i--) - { - ref var pb = ref prebuilds[i]; - if (pb.id != i || pb.isDestroyed || pb.itemRequired == 0) continue; - pb.itemRequired = 0; - factory.AlterPrebuildModelState(i); - } - } - } - - private class CargoTrafficPatch : PatchImpl - { - private static bool _isBatchBuilding; - private static bool _disableRefreshBatchesBuffers; - private static bool _anyBelt; - private static readonly HashSet _alterBeltRendererIds = []; - private static readonly HashSet _alterPathRendererIds = []; - private static readonly HashSet _refreshPathUVIds = []; - - public static bool IsBatchBuilding => _isBatchBuilding; - - public static void StartBatchBuilding(PlanetFactory factory) - { - factory.BeginFlattenTerrain(); - factory.cargoTraffic._batch_buffer_no_refresh = true; - PlanetFactory.batchBuild = true; - _isBatchBuilding = true; - _disableRefreshBatchesBuffers = true; - _anyBelt = false; - } - - public static void EndBatchBuilding(PlanetFactory factory) - { - PlanetFactory.batchBuild = false; - factory.cargoTraffic._batch_buffer_no_refresh = false; - factory.EndFlattenTerrain(); - _isBatchBuilding = false; - var cargoTraffic = factory.cargoTraffic; - var entityPool = factory.entityPool; - var colChunks = factory.planet.physics?.colChunks; - foreach (var beltId in _alterBeltRendererIds) - { - cargoTraffic.AlterBeltRenderer(beltId, entityPool, colChunks, false); - } - foreach (var pathId in _alterPathRendererIds) - { - cargoTraffic.AlterPathRenderer(pathId, false); - } - foreach (var pathId in _refreshPathUVIds) - { - cargoTraffic.RefreshPathUV(pathId); - } - _alterBeltRendererIds.Clear(); - _alterPathRendererIds.Clear(); - _refreshPathUVIds.Clear(); - _disableRefreshBatchesBuffers = false; - if (_anyBelt) - { - factory.cargoTraffic.RefreshBeltBatchesBuffers(); - factory.cargoTraffic.RefreshPathBatchesBuffers(); - } - _anyBelt = false; - factory.planet.physics?.raycastLogic?.NotifyBatchObjectRemove(); - factory.planet.audio?.SetPlanetAudioDirty(); - } - - public static void TryEndBatchBuilding(PlanetFactory factory) - { - if (!_isBatchBuilding) return; - EndBatchBuilding(factory); - } - - public static void InstantBuild(Player player, PlanetFactory factory, int id) - { - if (!_isBatchBuilding) StartBatchBuilding(factory); - _anyBelt = _anyBelt || _beltIds.Contains(factory.prebuildPool[id].protoId); - factory.BuildFinally(player, id, false); - } - - [HarmonyPrefix] - [HarmonyPriority(Priority.First)] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.AlterBeltRenderer))] - private static bool CargoTraffic_AlterBeltRenderer_Prefix(int beltId) - { - if (!_isBatchBuilding) return true; - _alterBeltRendererIds.Add(beltId); - return false; - } - - [HarmonyPrefix] - [HarmonyPriority(Priority.First)] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.AlterPathRenderer))] - private static bool CargoTraffic_AlterPathRenderer_Prefix(int pathId) - { - if (!_isBatchBuilding) return true; - _alterPathRendererIds.Add(pathId); - return false; - } - - [HarmonyPrefix] - [HarmonyPriority(Priority.First)] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RefreshPathUV))] - private static bool CargoTraffic_RefreshPathUV_Prefix(int pathId) - { - if (!_isBatchBuilding) return true; - _refreshPathUVIds.Add(pathId); - return false; - } - - [HarmonyPrefix] - [HarmonyPriority(Priority.First)] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RefreshBeltBatchesBuffers))] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RefreshPathBatchesBuffers))] - private static bool CargoTraffic_RefreshBeltBatchesBuffers_Prefix() - { - return !_disableRefreshBatchesBuffers; - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(PlanetData), nameof(PlanetData.NotifyFactoryLoaded))] - private static void PlanetData_NotifyFactoryLoaded_Postfix(PlanetData __instance) - { - var main = GameMain.instance; - if (main != null && main._running && __instance.factory?.planet?.data != null) - { - ArrivePlanet(__instance.factory); - } - } - - private static void OnGameBegin_For_ImmBuild() - { - if (DSPGame.IsMenuDemo) return; - var factory = GameMain.mainPlayer?.factory; - if (factory?.planet?.data != null) - { - ArrivePlanet(factory); - } - GameMain.data?.warningSystem?.UpdateCriticalWarningText(); - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(WarningSystem), nameof(WarningSystem.hasCriticalWarning), MethodType.Getter)] - private static IEnumerable WarningSystem_hasCriticalWarning_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - var label1 = generator.DefineLabel(); - var label2 = generator.DefineLabel(); - matcher.End().MatchBack(false, - new CodeMatch(OpCodes.Ret) - ).RemoveInstructions(1); - matcher.InsertAndAdvance( - new CodeInstruction(OpCodes.Brfalse, label1), - new CodeInstruction(OpCodes.Ldc_I4_1), - new CodeInstruction(OpCodes.Ret), - new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoConditionEnabled))).WithLabels(label1), - new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry), nameof(ConfigEntry.Value))), - new CodeInstruction(OpCodes.Brfalse, label2), - new CodeInstruction(OpCodes.Ldc_I4_1), - new CodeInstruction(OpCodes.Ret), - new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoCollisionEnabled))).WithLabels(label2), - new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry), nameof(ConfigEntry.Value))), - new CodeInstruction(OpCodes.Ret) - ); - return matcher.InstructionEnumeration(); - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(WarningSystem), nameof(WarningSystem.UpdateCriticalWarningText))] - private static IEnumerable WarningSystem_UpdateCriticalWarningText_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldstr, ""), - new CodeMatch(OpCodes.Call, AccessTools.PropertySetter(typeof(WarningSystem), nameof(WarningSystem.criticalWarningTexts))) - ); - matcher.Repeat(m => - { - var label1 = generator.DefineLabel(); - m.Advance(3).InsertAndAdvance( - new CodeInstruction(OpCodes.Ldarg_0), - Transpilers.EmitDelegate((WarningSystem w) => - { - if (NoConditionEnabled.Value) - { - w.criticalWarningTexts = "Build without condition is enabled!".Translate() + "\r\n"; - } - else if (NoCollisionEnabled.Value) - { - w.criticalWarningTexts = "No collision is enabled!".Translate() + "\r\n"; - } - } - ) - ); - if (m.Opcode == OpCodes.Ret) - { - m.InsertAndAdvance( - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))), - new CodeInstruction(OpCodes.Brfalse_S, label1), - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))), - new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Action), nameof(Action.Invoke))) - ); - m.Labels.Add(label1); - } - }); - return matcher.InstructionEnumeration(); - } - - private class ImmediateBuild : PatchImpl - { - protected override void OnEnable() - { - var factory = GameMain.mainPlayer?.factory; - if (factory?.planet?.data != null) - { - ArrivePlanet(factory); - } - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.CreatePrebuilds))] - [HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CreatePrebuilds))] - [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CreatePrebuilds))] - [HarmonyPatch(typeof(BuildTool_Inserter), nameof(BuildTool_Inserter.CreatePrebuilds))] - [HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CreatePrebuilds))] - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - matcher.End().MatchBack(false, - new CodeMatch(OpCodes.Ret) - ); - if (matcher.IsInvalid) - { - CheatEnabler.Logger.LogWarning($"Failed to patch CreatePrebuilds"); - return matcher.InstructionEnumeration(); - } - - matcher.Advance(-1); - if (matcher.Opcode != OpCodes.Nop && (matcher.Opcode != OpCodes.Call || !matcher.Instruction.OperandIs(AccessTools.Method(typeof(GC), nameof(GC.Collect))))) - { - CheatEnabler.Logger.LogWarning($"Failed to patch CreatePrebuilds: last instruction is not `Nop` or `Call GC.Collect()`: {matcher.Instruction}"); - return matcher.InstructionEnumeration(); - } - - var labels = matcher.Labels; - matcher.Labels = []; - matcher.Insert( - new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool), nameof(BuildTool.factory))), - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FactoryPatch), nameof(ArrivePlanet))) - ); - return matcher.InstructionEnumeration(); - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(GameLogic), nameof(GameLogic.FactoryConstructionSystemGameTick))] - private static void GameLogic_FactoryConstructionSystemGameTick_Prefix(GameLogic __instance) - { - var time = __instance.timei; - if (time % 6 != 0) return; - var planet = GameMain.localPlanet; - if (planet == null || !planet.factoryLoaded) return; - var factory = planet.factory; - if (factory == null || factory.prebuildCount <= 0) return; - var player = GameMain.mainPlayer; - if (player == null) return; - var total = factory.prebuildCursor - 1; - var stepCount = total switch - { - < 256 => 1, - < 2048 => 3, - < 16384 => 10, - _ => 20, - }; - var step = (int)(time / 6 % stepCount); - var start = 1 + total * step / stepCount; - var end = 1 + total * (step + 1) / stepCount; - for (var i = start; i < end; i++) - { - ref var prebuild = ref factory.prebuildPool[i]; - if (prebuild.id != i || prebuild.isDestroyed) continue; - if (prebuild.itemRequired > 0) - { - int itemId = prebuild.protoId; - int count = prebuild.itemRequired; - player.package.TakeTailItems(ref itemId, ref count, out var _, false); - if (count > 0) - { - prebuild.itemRequired -= count; - if (prebuild.itemRequired <= 0) - { - CargoTrafficPatch.InstantBuild(player, factory, i); - } - } - } - } - if (CargoTrafficPatch.IsBatchBuilding) - { - for (var i = start - 1; i > 0; i--) - { - ref var prebuild = ref factory.prebuildPool[i]; - if (prebuild.id != i || prebuild.isDestroyed) continue; - if (prebuild.itemRequired > 0) - { - int itemId = prebuild.protoId; - int count = prebuild.itemRequired; - player.package.TakeTailItems(ref itemId, ref count, out var _, false); - if (count > 0) - { - prebuild.itemRequired -= count; - if (prebuild.itemRequired <= 0) - { - CargoTrafficPatch.InstantBuild(player, factory, i); - } - } - } - } - for (var i = end; i <= total; i++) - { - ref var prebuild = ref factory.prebuildPool[i]; - if (prebuild.id != i || prebuild.isDestroyed) continue; - if (prebuild.itemRequired > 0) - { - int itemId = prebuild.protoId; - int count = prebuild.itemRequired; - player.package.TakeTailItems(ref itemId, ref count, out var _, false); - if (count > 0) - { - prebuild.itemRequired -= count; - if (prebuild.itemRequired <= 0) - { - CargoTrafficPatch.InstantBuild(player, factory, i); - } - } - } - } - CargoTrafficPatch.EndBatchBuilding(factory); - } - } - - /* - [HarmonyTranspiler] - [HarmonyPatch(typeof(ConstructionSystem), nameof(ConstructionSystem.AddBuildTargetToModules))] - private static IEnumerable ConstructionSystem_AddBuildTargetToModules_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - // 13 0035 ldarg.0 - // 14 0036 ldfld class Player ConstructionSystem::player - // 15 003B callvirt instance class Mecha Player::get_mecha() - // 16 0040 ldfld float32 Mecha::buildArea - // 17 0045 stloc.0 - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ConstructionSystem), nameof(ConstructionSystem.player))), - new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Player), nameof(Player.mecha))), - new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Mecha), nameof(Mecha.buildArea))), - new CodeMatch(ci => ci.IsStloc()) - ); - var labels = matcher.Labels; - matcher.Labels = []; - matcher.Insert( - new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels), - new CodeInstruction(OpCodes.Ldarg_1), - Transpilers.EmitDelegate((ConstructionSystem constructionSystem, int objId) => - { - var player = constructionSystem.player; - player.factory.BuildFinally(player, objId); - }), - new CodeInstruction(OpCodes.Ret) - ); - return matcher.InstructionEnumeration(); - } - */ - - [HarmonyPostfix] - [HarmonyPatch(typeof(UXAssist.Functions.PlanetFunctions), nameof(UXAssist.Functions.PlanetFunctions.BuildOrbitalCollectors))] - private static void UXAssist_PlanetFunctions_BuildOrbitalCollectors_Postfix() - { - var factory = GameMain.mainPlayer?.factory; - if (factory?.planet?.data != null) - { - ArrivePlanet(factory); - } - } - } - - private class ArchitectMode : PatchImpl - { - private static bool[] _canBuildItems; - - protected override void OnEnable() - { - var factory = GameMain.mainPlayer?.factory; - if (factory?.planet?.data != null) - { - ArrivePlanet(factory); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), [typeof(int), typeof(int), typeof(int), typeof(bool)], - [ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Out, ArgumentType.Normal])] - [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), [typeof(int), typeof(int), typeof(int[]), typeof(int), typeof(bool)], - [ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal])] - public static bool TakeTailItemsPatch(StorageComponent __instance, int itemId) - { - if (__instance == null || GameMain.mainPlayer == null || __instance.id != GameMain.mainPlayer.package.id) return true; - if (itemId <= 0) return true; - if (_canBuildItems == null) - { - DoInit(); - } - - return itemId >= 12000 || !_canBuildItems[itemId]; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.GetItemCount), typeof(int))] - public static void GetItemCountPatch(StorageComponent __instance, int itemId, ref int __result) - { - if (__result > 99) return; - if (__instance == null || GameMain.mainPlayer == null || __instance.id != GameMain.mainPlayer.package.id) return; - if (itemId <= 0) return; - if (_canBuildItems == null) - { - DoInit(); - } - if (itemId < 12000 && _canBuildItems[itemId]) __result = 100; - } - - private static void DoInit() - { - _canBuildItems = new bool[12000]; - foreach (var ip in LDB.items.dataArray) - { - if ((ip.Type == EItemType.Logistics || ip.CanBuild) && ip.ID < 12000) _canBuildItems[ip.ID] = true; - } - } - } - - private class NoConditionBuild : PatchImpl - { - protected override void OnEnable() - { - GameMain.data?.warningSystem?.UpdateCriticalWarningText(); - } - - protected override void OnDisable() - { - GameMain.data?.warningSystem?.UpdateCriticalWarningText(); - } - - [HarmonyTranspiler, HarmonyPriority(Priority.Last)] - [HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.CheckBuildConditions))] - [HarmonyPatch(typeof(BuildTool_Inserter), nameof(BuildTool_Inserter.CheckBuildConditions))] - private static IEnumerable BuildTool_CheckBuildConditions_Transpiler(IEnumerable instructions) - { - yield return new CodeInstruction(OpCodes.Ldc_I4_1); - yield return new CodeInstruction(OpCodes.Ret); - } - - [HarmonyTranspiler, HarmonyPriority(Priority.First)] - [HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CheckBuildConditions))] - private static IEnumerable BuildTool_Path_CheckBuildConditions_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - var label1 = generator.DefineLabel(); - var label2 = generator.DefineLabel(); - matcher.Start().InsertAndAdvance( - new CodeInstruction(OpCodes.Br, label1) - ); - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(BuildTool), nameof(BuildTool.buildPreviews))), - new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List), nameof(List.Count))), - new CodeMatch(ci => ci.IsStloc()) - ); - matcher.Labels.Add(label1); - matcher.Advance(4).InsertAndAdvance( - new CodeInstruction(OpCodes.Br, label2) - ); - matcher.MatchForward(false, - new CodeMatch(ci => ci.IsLdloc()), - new CodeMatch(ci => ci.Branches(out _)), - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool_Path), nameof(BuildTool_Path.waitForConfirm))), - new CodeMatch(ci => ci.Branches(out _)) - ); - var operand = matcher.Operand; - matcher.InsertAndAdvance( - new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(label2), - new CodeInstruction(OpCodes.Stloc_S, operand) - ); - return matcher.InstructionEnumeration(); - } - - [HarmonyTranspiler, HarmonyPriority(Priority.Last)] - [HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))] - [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))] - private static IEnumerable BuildTool_Click_CheckBuildConditions_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - var label1 = generator.DefineLabel(); - matcher.Start().InsertAndAdvance( - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NoConditionBuild), nameof(CheckForMiner))), - new CodeInstruction(OpCodes.Brfalse_S, label1), - new CodeInstruction(OpCodes.Ldc_I4_1), - new CodeInstruction(OpCodes.Ret) - ); - matcher.Labels.Add(label1); - return matcher.InstructionEnumeration(); - } - - public static bool CheckForMiner(BuildTool tool) - { - var previews = tool.buildPreviews; - foreach (var preview in previews) - { - var desc = preview?.item?.prefabDesc; - if (desc == null) continue; - if (desc.veinMiner || desc.oilMiner) return false; - } - - return true; - } - } - - public class BeltSignalGenerator : PatchImpl - { - private static Dictionary[] _signalBelts; - private static Dictionary _portalFrom; - private static Dictionary> _portalTo; - private static int _signalBeltsCapacity; - private static bool _initialized; - - private class BeltSignal - { - public int SignalId; - public int SpeedLimit; - public byte Stack; - public byte Inc; - public int Progress; - public (int itemId, float itemCount, bool isExtra)[] Sources; - public float[] SourceProgress; - } - - protected override void OnEnable() - { - InitSignalBelts(); - GameLogicProc.OnGameBegin += OnGameBegin; - } - - protected override void OnDisable() - { - GameLogicProc.OnGameBegin -= OnGameBegin; - _initialized = false; - _signalBelts = null; - _signalBeltsCapacity = 0; - } - - public static void OnAltFormatChanged() - { - if (_signalBelts == null) return; - var factories = GameMain.data?.factories; - if (factories == null) return; - var factoryCount = GameMain.data.factoryCount; - var altFormat = BeltSignalNumberAltFormat.Value; - for (var i = Math.Min(_signalBelts.Length, factoryCount) - 1; i >= 0; i--) - { - var factory = factories[i]; - var cargoTraffic = factory?.cargoTraffic; - if (cargoTraffic == null) continue; - var entitySignPool = factory.entitySignPool; - if (entitySignPool == null) continue; - var belts = _signalBelts[i]; - if (belts == null) continue; - foreach (var pair in belts) - { - var beltId = pair.Key; - ref var belt = ref cargoTraffic.beltPool[beltId]; - if (belt.id != beltId) continue; - ref var signal = ref entitySignPool[belt.entityId]; - if (signal.iconId0 < 1000) continue; - var signalBelt = pair.Value; - var inc = signalBelt.Inc / signalBelt.Stack; - if (altFormat) - signal.count0 = signalBelt.SpeedLimit + signalBelt.Stack * 10000 + inc * 100000; - else - signal.count0 = signalBelt.SpeedLimit * 100 + signalBelt.Stack + inc * 10; - } - } - } - - public static void OnUseProliferatorChanged() - { - if (_signalBelts == null) return; - var factories = GameMain.data?.factories; - if (factories == null) return; - var factoryCount = GameMain.data.factoryCount; - var altFormat = BeltSignalNumberAltFormat.Value; - for (var i = Math.Min(_signalBelts.Length, factoryCount) - 1; i >= 0; i--) - { - var factory = factories[i]; - var cargoTraffic = factory?.cargoTraffic; - if (cargoTraffic == null) continue; - var entitySignPool = factory.entitySignPool; - if (entitySignPool == null) continue; - var belts = _signalBelts[i]; - if (belts == null) continue; - foreach (var pair in belts) - { - var beltId = pair.Key; - ref var belt = ref cargoTraffic.beltPool[beltId]; - if (belt.id != beltId) continue; - var signalBelt = pair.Value; - signalBelt.Progress = 0; - signalBelt.Sources = null; - signalBelt.SourceProgress = null; - AddSourcesToBeltSignal(signalBelt); - } - } - } - - private static void InitSignalBelts() - { - if (DSPGame.IsMenuDemo) return; - InitItemSources(); - _signalBelts = new Dictionary[64]; - _signalBeltsCapacity = 64; - _portalFrom = []; - _portalTo = []; - - var factories = GameMain.data?.factories; - if (factories == null) return; - foreach (var factory in factories) - { - var entitySignPool = factory?.entitySignPool; - if (entitySignPool == null) continue; - var cargoTraffic = factory.cargoTraffic; - var beltPool = cargoTraffic.beltPool; - for (var i = cargoTraffic.beltCursor - 1; i > 0; i--) - { - if (beltPool[i].id != i) continue; - ref var signal = ref entitySignPool[beltPool[i].entityId]; - var signalId = signal.iconId0; - if (signalId == 0U) continue; - var number = Mathf.RoundToInt(signal.count0); - switch (signalId) - { - case 404: - SetSignalBelt(factory.index, i, (int)signalId, 0); - continue; - case 600: - case >= 1000 and < 20000: - if (number > 0) - SetSignalBelt(factory.index, i, (int)signalId, number); - continue; - case >= 601 and <= 609: - if (number > 0) - SetSignalBeltPortalTo(factory.index, i, number); - continue; - } - } - } - - _initialized = true; - } - - private static Dictionary GetOrCreateSignalBelts(int index) - { - Dictionary obj; - if (index < 0) return null; - if (index >= _signalBeltsCapacity) - { - var newCapacity = _signalBeltsCapacity * 2; - var newSignalBelts = new Dictionary[newCapacity]; - Array.Copy(_signalBelts, newSignalBelts, _signalBeltsCapacity); - _signalBelts = newSignalBelts; - _signalBeltsCapacity = newCapacity; - } - else - { - obj = _signalBelts[index]; - if (obj != null) return obj; - } - - obj = []; - _signalBelts[index] = obj; - return obj; - } - - private static Dictionary GetSignalBelts(int index) - { - return index >= 0 && index < _signalBeltsCapacity ? _signalBelts[index] : null; - } - - private static void SetSignalBelt(int factory, int beltId, int signalId, int number) - { - int stack; - int inc; - int speedLimit; - if (signalId >= 1000) - { - if (!BeltSignalNumberAltFormat.Value) - { - stack = Mathf.Clamp(number % 10, 1, 4); - inc = number / 10 % 10 * stack; - speedLimit = number / 100; - } - else - { - stack = Mathf.Clamp(number / 10000 % 10, 1, 4); - inc = number / 100000 % 10 * stack; - speedLimit = number % 10000; - } - } - else - { - stack = 0; - inc = 0; - speedLimit = number; - } - - if (speedLimit > 3600) speedLimit = 3600; - - var signalBelts = GetOrCreateSignalBelts(factory); - if (signalBelts.TryGetValue(beltId, out var oldBeltSignal)) - { - if (oldBeltSignal.SignalId == signalId && oldBeltSignal.SpeedLimit == speedLimit && oldBeltSignal.Stack == stack && oldBeltSignal.Inc == inc) return; - oldBeltSignal.SpeedLimit = speedLimit; - oldBeltSignal.Stack = (byte)stack; - oldBeltSignal.Inc = (byte)inc; - oldBeltSignal.Progress = 0; - oldBeltSignal.SignalId = signalId; - oldBeltSignal.Sources = null; - oldBeltSignal.SourceProgress = null; - AddSourcesToBeltSignal(oldBeltSignal); - return; - } - - var beltSignal = new BeltSignal - { - SignalId = signalId, - SpeedLimit = speedLimit, - Stack = (byte)stack, - Inc = (byte)inc - }; - AddSourcesToBeltSignal(beltSignal); - signalBelts[beltId] = beltSignal; - } - - private static void AddSourcesToBeltSignal(BeltSignal beltSignal) - { - var itemId = beltSignal.SignalId; - if (itemId < 1000) return; - var result = new Dictionary(); - var extra = new Dictionary(); - var sprayedCount = 0f; - CalculateAllProductions(result, extra, ref sprayedCount, itemId); - - var proliferatorCount = 0f; - if (result.TryGetValue(1143, out var pv)) - { - proliferatorCount = pv; - result.Remove(1143); - } - if (BeltSignalUseProliferatorEnabled.Value) - { - if (beltSignal.Inc / beltSignal.Stack >= 4) - { - sprayedCount += 1f; - } - if (sprayedCount > 0) - { - proliferatorCount += sprayedCount / ProliferatorSpayCount; - } - } - if (proliferatorCount > 0f) - { - foreach (var p in ProliferatorSources) - { - result[p.Item1] = (result.TryGetValue(p.Item1, out var v) ? v : 0) + p.Item2 * proliferatorCount / ProliferatorDenom; - } - } - - result.Remove(itemId); - - var cnt = result.Count + extra.Count; - if (cnt == 0) - { - beltSignal.Sources = null; - beltSignal.SourceProgress = null; - return; - } - - var items = new (int itemId, float itemCount, bool isExtra)[cnt]; - var progress = new float[cnt]; - foreach (var p in extra) - { - items[--cnt] = (p.Key, p.Value, true); - } - foreach (var p in result) - { - items[--cnt] = (p.Key, p.Value, false); - } - - beltSignal.Sources = items; - beltSignal.SourceProgress = progress; - } - - private static void SetSignalBeltPortalTo(int factory, int beltId, int number) - { - var v = ((long)factory << 32) | (uint)beltId; - _portalFrom[v] = number; - if (!_portalTo.TryGetValue(number, out var set)) - { - set = []; - _portalTo[number] = set; - } - - set.Add(v); - } - - private static void RemoveSignalBelt(int factory, int beltId) - { - GetSignalBelts(factory)?.Remove(beltId); - } - - private static void RemovePlanetSignalBelts(int factory) - { - GetSignalBelts(factory)?.Clear(); - } - - private static void RemoveSignalBeltPortalEnd(int factory, int beltId) - { - var v = ((long)factory << 32) | (uint)beltId; - if (!_portalFrom.TryGetValue(v, out var number)) return; - _portalFrom.Remove(v); - if (!_portalTo.TryGetValue(number, out var set)) return; - set.Remove(v); - } - - private static void OnGameBegin() - { - if (DSPGame.IsMenuDemo) return; - if (BeltSignalGeneratorEnabled.Value) InitSignalBelts(); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(DigitalSystem), MethodType.Constructor, typeof(PlanetData))] - private static void DigitalSystem_Constructor_Postfix(PlanetData _planet) - { - if (!BeltSignalGeneratorEnabled.Value) return; - var player = GameMain.mainPlayer; - if (player == null) return; - var factory = _planet?.factory; - if (factory == null) return; - RemovePlanetSignalBelts(factory.index); - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.RemoveBeltComponent))] - public static void CargoTraffic_RemoveBeltComponent_Prefix(int id) - { - if (!_initialized) return; - var planet = GameMain.localPlanet; - if (planet == null) return; - RemoveSignalBeltPortalEnd(planet.factoryIndex, id); - RemoveSignalBelt(planet.factoryIndex, id); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.SetBeltSignalIcon))] - public static void CargoTraffic_SetBeltSignalIcon_Postfix(CargoTraffic __instance, int signalId, int entityId) - { - if (!_initialized) return; - var planet = GameMain.localPlanet; - if (planet == null) return; - var factory = __instance.factory; - int number; - var needAdd = false; - switch (signalId) - { - case 404: - number = 0; - needAdd = true; - break; - case 600: - case >= 1000 and < 20000: - number = Mathf.RoundToInt(factory.entitySignPool[entityId].count0); - if (number > 0) - needAdd = true; - break; - case >= 601 and <= 609: - number = Mathf.RoundToInt(factory.entitySignPool[entityId].count0); - var factoryIndex = planet.factoryIndex; - var beltId = factory.entityPool[entityId].beltId; - if (number > 0) - SetSignalBeltPortalTo(factoryIndex, beltId, number); - RemoveSignalBelt(factoryIndex, beltId); - return; - default: - number = 0; - break; - } - - { - var factoryIndex = planet.factoryIndex; - var beltId = factory.entityPool[entityId].beltId; - if (needAdd) - { - SetSignalBelt(factoryIndex, beltId, signalId, number); - } - else - { - RemoveSignalBelt(factoryIndex, beltId); - } - - RemoveSignalBeltPortalEnd(factoryIndex, beltId); - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.SetBeltSignalNumber))] - public static void CargoTraffic_SetBeltSignalNumber_Postfix(CargoTraffic __instance, float number, int entityId) - { - if (!_initialized) return; - var planet = GameMain.localPlanet; - if (planet == null) return; - var factory = __instance.factory; - var entitySignPool = factory.entitySignPool; - uint signalId; - if (entitySignPool[entityId].iconType == 0U || (signalId = entitySignPool[entityId].iconId0) == 0U) return; - switch (signalId) - { - case 404: - return; - case 600: - case >= 1000 and < 20000: - break; - case >= 601 and <= 609: - var factoryIndex = planet.factoryIndex; - var beltId = factory.entityPool[entityId].beltId; - RemoveSignalBeltPortalEnd(factoryIndex, beltId); - SetSignalBeltPortalTo(factoryIndex, beltId, Mathf.RoundToInt(number)); - return; - default: - return; - } - - { - var factoryIndex = planet.factoryIndex; - var beltId = factory.entityPool[entityId].beltId; - var n = Mathf.RoundToInt(number); - if (n == 0) - { - RemoveSignalBelt(factoryIndex, beltId); - } - else - { - SetSignalBelt(factoryIndex, beltId, (int)signalId, n); - } - } - } - - public static void ProcessBeltSignals() - { - if (!_initialized) return; - var data = GameMain.data; - var factories = data?.factories; - if (factories == null) return; - DeepProfiler.BeginSample(DPEntry.Belt); - for (var index = data.factoryCount - 1; index >= 0; index--) - { - var factory = factories[index]; - if (factory == null) continue; - var belts = GetSignalBelts(index); - if (belts == null || belts.Count == 0) continue; - var factoryProductionStat = GameMain.statistics.production.factoryStatPool[index]; - var productRegister = factoryProductionStat.productRegister; - var consumeRegister = factoryProductionStat.consumeRegister; - var countRecipe = BeltSignalCountRecipeEnabled.Value; - var cargoTraffic = factory.cargoTraffic; - var beltCount = cargoTraffic.beltCursor; - List beltsToRemove = null; - foreach (var pair in belts) - { - if (pair.Key >= beltCount) - { - if (beltsToRemove == null) - beltsToRemove = [pair.Key]; - else - beltsToRemove.Add(pair.Key); - continue; - } - var beltSignal = pair.Value; - var signalId = beltSignal.SignalId; - switch (signalId) - { - case 404: - { - var beltId = pair.Key; - ref var belt = ref cargoTraffic.beltPool[beltId]; - var cargoPath = cargoTraffic.GetCargoPath(belt.segPathId); - if (cargoPath == null) continue; - int itemId; - if ((itemId = cargoPath.TryPickItem(belt.segIndex + belt.segPivotOffset - 5, 12, out var stack, out _)) > 0) - { - if (BeltSignalCountRemEnabled.Value) consumeRegister[itemId] += stack; - } - - continue; - } - case 600: - { - if (!_portalTo.TryGetValue(beltSignal.SpeedLimit, out var set)) continue; - var beltId = pair.Key; - ref var belt = ref cargoTraffic.beltPool[beltId]; - var cargoPath = cargoTraffic.GetCargoPath(belt.segPathId); - if (cargoPath == null) continue; - var segIndex = belt.segIndex + belt.segPivotOffset; - if (!cargoPath.GetCargoAtIndex(segIndex, out var cargo, out var cargoId, out var _)) break; - var itemId = cargo.item; - var cargoPool = cargoPath.cargoContainer.cargoPool; - var inc = cargoPool[cargoId].inc; - var stack = cargoPool[cargoId].stack; - foreach (var n in set) - { - var cargoTraffic1 = factories[(int)(n >> 32)].cargoTraffic; - ref var belt1 = ref cargoTraffic1.beltPool[(int)(n & 0x7FFFFFFF)]; - cargoPath = cargoTraffic1.GetCargoPath(belt1.segPathId); - if (cargoPath == null) continue; - if (!cargoPath.TryInsertItem(belt1.segIndex + belt1.segPivotOffset, itemId, stack, inc)) continue; - cargoPath.TryPickItem(segIndex - 5, 12, out var stack1, out var inc1); - if (inc1 != inc || stack1 != stack) - cargoPath.TryPickItem(segIndex - 5, 12, out _, out _); - break; - } - - continue; - } - case >= 1000 and < 20000: - { - var hasSpeedLimit = beltSignal.SpeedLimit > 0; - if (hasSpeedLimit) - { - beltSignal.Progress += beltSignal.SpeedLimit; - switch (beltSignal.Progress) - { - case < 3600: - continue; - case > 18000: - beltSignal.Progress = 14400; - break; - } - } - - var beltId = pair.Key; - ref var belt = ref cargoTraffic.beltPool[beltId]; - var cargoPath = cargoTraffic.GetCargoPath(belt.segPathId); - if (cargoPath == null) continue; - var stack = beltSignal.Stack; - var inc = beltSignal.Inc; - if (!cargoPath.TryInsertItem(belt.segIndex + belt.segPivotOffset, signalId, stack, inc)) continue; - if (hasSpeedLimit) beltSignal.Progress -= 3600; - if (BeltSignalCountGenEnabled.Value) productRegister[signalId] += stack; - if (!countRecipe) continue; - var sources = beltSignal.Sources; - if (sources == null) continue; - var progress = beltSignal.SourceProgress; - var stackf = (float)stack; - for (var i = sources.Length - 1; i >= 0; i--) - { - var newCnt = progress[i] + sources[i].itemCount * stackf; - if (newCnt > 0) - { - var itemId = sources[i].itemId; - var cnt = Mathf.CeilToInt(newCnt); - productRegister[itemId] += cnt; - if (!sources[i].isExtra) consumeRegister[itemId] += cnt; - progress[i] = newCnt - cnt; - } - else - { - progress[i] = newCnt; - } - } - - continue; - } - } - } - if (beltsToRemove == null) continue; - foreach (var beltId in beltsToRemove) - { - belts.Remove(beltId); - } - } - - DeepProfiler.EndSample(DPEntry.Belt); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameLogic), nameof(GameLogic.OnFactoryFrameBegin))] - public static void GameLogic_OnFactoryFrameBegin_Postfix() - { - ProcessBeltSignals(); - } - - /* BEGIN: Item sources calculation */ - private static readonly int[] ExtraOreItemIds = [1000, 1116, 1120, 1121, 1208, 5201, 5202, 5203, 5204, 5205, 5206]; - private static readonly HashSet ExtraProliferationItemIds = [1107, 1111, 1125, 1142, 1143, 1202, 1203, 1204, 1205, 1209, 1210, 1301, 1305, 1401, 1402, 1403, 1405, 1406, 1502, 1503, 1802, 6001, 6003, 6004, 6005, 6006]; - private static readonly HashSet NoProliferationItemIds = [1126, 6002]; - // All source items used to create 25 proliferators mk.III (not self-sprayed) - private static readonly List<(int, float)> ProliferatorSources = [(1015, 60f), (1124, 20f), (1006, 64f), (1012, 16f), (1112, 32f), (1141, 64f), (1142, 40f), (1143, 25f)]; - private const float ProliferatorDenom = 21f; - // One sprayed proliferator mk.III can spray 75 items, but one is used for spray itself, so the actual count is 74 - private const float ProliferatorSpayCount = 74f; - private static readonly Dictionary ItemSources = []; - private static bool _itemSourcesInitialized; - - private class ItemSource - { - public float Count; - public Dictionary From; - public Dictionary Extra; - } - - private static void InitItemSources() - { - if (_itemSourcesInitialized) return; - foreach (var vein in LDB.veins.dataArray) - { - ItemSources[vein.MiningItem] = new ItemSource { Count = 1 }; - } - - foreach (var ip in LDB.items.dataArray) - { - if (!string.IsNullOrEmpty(ip.MiningFrom)) - { - ItemSources[ip.ID] = new ItemSource { Count = 1 }; - } - } - - // 水、硫酸、氢、重氢、光子 - foreach (var itemId in ExtraOreItemIds) - { - ItemSources[itemId] = new ItemSource { Count = 1 }; - } - - var recipes = LDB.recipes.dataArray; - foreach (var recipe in recipes) - { - if (!recipe.Explicit || recipe.ID == 58 || recipe.ID == 121) continue; - var res = recipe.Results; - var rescnt = recipe.ResultCounts; - var len = res.Length; - for (var i = 0; i < len; i++) - { - if (ItemSources.ContainsKey(res[i])) continue; - var rs = new ItemSource { Count = rescnt[i], From = [] }; - var it = recipe.Items; - var itcnt = recipe.ItemCounts; - var len2 = it.Length; - for (var j = 0; j < len2; j++) - { - rs.From[it[j]] = itcnt[j]; - } - - if (len > 1) - { - rs.Extra = []; - for (var k = 0; k < len; k++) - { - if (i != k) - { - rs.Extra[res[k]] = rescnt[k]; - } - } - } - - ItemSources[res[i]] = rs; - } - } - - foreach (var recipe in recipes) - { - if (recipe.Explicit) continue; - var res = recipe.Results; - var rescnt = recipe.ResultCounts; - var len = res.Length; - for (var i = 0; i < len; i++) - { - if (ItemSources.ContainsKey(res[i])) continue; - var rs = new ItemSource { Count = rescnt[i], From = [], Extra = null }; - var it = recipe.Items; - var itcnt = recipe.ItemCounts; - var len2 = it.Length; - for (var j = 0; j < len2; j++) - { - rs.From[it[j]] = itcnt[j]; - } - - if (len > 1) - { - rs.Extra = []; - for (var k = 0; k < len; k++) - { - if (i != k) - { - rs.Extra[res[k]] = rescnt[k]; - } - } - } - - ItemSources[res[i]] = rs; - } - } - - _itemSourcesInitialized = true; - } - - private static void CalculateAllProductions(IDictionary result, IDictionary extra, ref float sprayedCount, int itemId, float count = 1f) - { - if (!ItemSources.TryGetValue(itemId, out var itemSource)) - { - return; - } - - var times = 1f; - if (Math.Abs(count - itemSource.Count) > 0.000001f) - { - times = count / itemSource.Count; - } - - result[itemId] = (result.TryGetValue(itemId, out var oldCount) ? oldCount : 0) + count; - if (itemSource.Extra != null) - { - foreach (var p in itemSource.Extra) - { - extra[p.Key] = (extra.TryGetValue(p.Key, out oldCount) ? oldCount : 0) + times * p.Value; - } - } - - if (itemId == 1143 || itemSource.From == null) return; - var useProliferator = BeltSignalUseProliferatorEnabled.Value; - if (useProliferator && ExtraProliferationItemIds.Contains(itemId)) - { - times *= 0.8f; - } - foreach (var p in itemSource.From) - { - var value = p.Value * times; - if (useProliferator && !NoProliferationItemIds.Contains(p.Key)) sprayedCount += value; - if (extra.TryGetValue(p.Key, out var rcount)) - { - if (value <= rcount) - { - if (value == rcount) - { - extra.Remove(p.Key); - } - else - { - extra[p.Key] = rcount - value; - } - continue; - } - extra.Remove(p.Key); - value -= rcount; - } - if (result.TryGetValue(p.Key, out rcount)) - { - rcount -= value; - if (rcount <= 0) - { - result.Remove(p.Key); - } - else - { - result[p.Key] = rcount; - } - continue; - } - CalculateAllProductions(result, extra, ref sprayedCount, p.Key, value); - } - } - /* END: Item sources calculation */ - } - - private class RemovePowerSpaceLimit : PatchImpl - { - [HarmonyTranspiler] - [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))] - [HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))] - private static IEnumerable BuildTool_CheckBuildConditions_Transpiler(IEnumerable instructions) - { - var matcher = new CodeMatcher(instructions); - matcher.Start().MatchForward(false, - new CodeMatch(OpCodes.Ldc_R4, 110.25f) - ); - if (matcher.IsValid) - { - matcher.Repeat(codeMatcher => codeMatcher.SetAndAdvance( - OpCodes.Ldc_R4, 1f - )); - } - matcher.Start().MatchForward(false, - new CodeMatch(OpCodes.Ldc_R4, 144f) - ); - if (matcher.IsValid) - { - matcher.Repeat(codeMatcher => codeMatcher.SetAndAdvance( - OpCodes.Ldc_R4, 1f - )); - } - return matcher.InstructionEnumeration(); - } - } - - private class BoostWindPower : PatchImpl - { - [HarmonyTranspiler] - [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Wind))] - private static IEnumerable PowerGeneratorComponent_EnergyCap_Wind_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - matcher.Start().RemoveInstructions(matcher.Length); - matcher.Insert( - // this.currentStrength = windStrength - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldarg_1), - new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.currentStrength))), - // this.capacityCurrentTick = 500000000L - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldc_I8, 500000000L), - new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))), - // return 500000000L - new CodeInstruction(OpCodes.Ldc_I8, 500000000L), - new CodeInstruction(OpCodes.Ret) - ); - return matcher.InstructionEnumeration(); - } - } - - private class BoostSolarPower : PatchImpl - { - [HarmonyTranspiler] - [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_PV))] - private static IEnumerable PowerGeneratorComponent_EnergyCap_PV_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - matcher.Start().RemoveInstructions(matcher.Length).Insert( - // this.currentStrength = lumino - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldarg_S, 4), - new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.currentStrength))), - // this.capacityCurrentTick = 600000000L - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldc_I8, 600000000L), - new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))), - // return 600000000L - new CodeInstruction(OpCodes.Ldc_I8, 600000000L), - new CodeInstruction(OpCodes.Ret) - ); - return matcher.InstructionEnumeration(); - } - } - - private class BoostFuelPower : PatchImpl - { - [HarmonyTranspiler] - [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Fuel))] - private static IEnumerable PowerGeneratorComponent_EnergyCap_Fuel_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - var label1 = generator.DefineLabel(); - var label2 = generator.DefineLabel(); - var label3 = generator.DefineLabel(); - matcher.Start().MatchForward(false, - new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))) - ); - var labels = matcher.Labels; - matcher.Labels = []; - matcher.Insert( - // if (this.fuelMask == 4) - new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.fuelMask))), - new CodeInstruction(OpCodes.Ldc_I4_4), - new CodeInstruction(OpCodes.Bne_Un_S, label1), - // multiplier = 10000L - new CodeInstruction(OpCodes.Ldc_I8, 10000L), - new CodeInstruction(OpCodes.Br_S, label3), - // else if (this.fuelMask == 2) - new CodeInstruction(OpCodes.Ldarg_0).WithLabels(label1), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.fuelMask))), - new CodeInstruction(OpCodes.Ldc_I4_2), - new CodeInstruction(OpCodes.Bne_Un_S, label2), - // multiplier = 20000L - new CodeInstruction(OpCodes.Ldc_I8, 20000L), - new CodeInstruction(OpCodes.Br_S, label3), - // else multiplier = 50000L - new CodeInstruction(OpCodes.Ldc_I8, 50000L).WithLabels(label2), - // do multiplier before store to this.capacityCurrentTick - new CodeInstruction(OpCodes.Mul).WithLabels(label3) - ); - return matcher.InstructionEnumeration(); - } - } - - private class BoostGeothermalPower : PatchImpl - { - [HarmonyTranspiler] - [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_GTH))] - private static IEnumerable PowerGeneratorComponent_EnergyCap_GTH_Transpiler(IEnumerable instructions, ILGenerator generator) - { - var matcher = new CodeMatcher(instructions, generator); - matcher.Start().RemoveInstructions(matcher.Length).Insert( - // this.currentStrength = this.gthStrength - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.gthStrength))), - new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.currentStrength))), - // this.capacityCurrentTick = 2000000000L - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldc_I8, 2000000000L), - new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.capacityCurrentTick))), - // return 2000000000L - new CodeInstruction(OpCodes.Ldc_I8, 2000000000L), - new CodeInstruction(OpCodes.Ret) - ); - return matcher.InstructionEnumeration(); - } - } - - private static class WindTurbinesPowerGlobalCoverage - { - private static bool _patched; - private static PrefabDesc _prefabdesc; - private static float _oldCoverRadius; - private static float _oldConnectDistance; - private const int WindTurbineId = 2203; - private const float WindTurbineNewCoverageDistance = 500f; - - public static void Enable(bool enable) - { - if (enable) - { - if (_patched) return; - _patched = true; - var itemProto = LDB.items.Select(WindTurbineId); - _oldCoverRadius = itemProto.prefabDesc.powerCoverRadius; - _oldConnectDistance = itemProto.prefabDesc.powerConnectDistance; - itemProto.prefabDesc.powerCoverRadius = WindTurbineNewCoverageDistance; - itemProto.prefabDesc.powerConnectDistance = WindTurbineNewCoverageDistance; - _prefabdesc = itemProto.prefabDesc; - } - else - { - if (!_patched) return; - _patched = false; - _prefabdesc.powerCoverRadius = _oldCoverRadius; - _prefabdesc.powerConnectDistance = _oldConnectDistance; - } - - // Iterate all factories and update wind turbines power nodes - if (GameMain.data == null) return; - foreach (var factory in GameMain.data.factories) - { - var powerSystem = factory?.powerSystem; - if (powerSystem == null) continue; - for (var i = powerSystem.nodeCursor - 1; i >= 0; i--) - { - ref var node = ref powerSystem.nodePool[i]; - if (node.id != i) continue; - ref var entity = ref factory.entityPool[node.entityId]; - if (entity.protoId != WindTurbineId) continue; - // Disconnect from power system - powerSystem.OnNodeRemoving(i); - // Set new properties - node.connectDistance = _prefabdesc.powerConnectDistance; - node.coverRadius = _prefabdesc.powerCoverRadius; - // Connect back to power system - powerSystem.OnNodeAdded(i); - } - // Refresh power nodes rendering if factory is loaded - if (factory.planet.factoryLoaded) - { - factory.planet.factoryModel.RefreshPowerNodes(); - } - } - } - } - - private class ControlPanelRemoteLogistics : PatchImpl - { - [HarmonyTranspiler] - [HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnItemIconMouseDown))] - [HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnHoldupItemClick))] - [HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnCourierIconClick))] - private static IEnumerable UIControlPanelDispenserInspector_OnItemIconMouseDown_Transpiler(IEnumerable instructions) - { - var matcher = new CodeMatcher(instructions); - Label? branch = null; - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.isLocal))), - new CodeMatch(ci => ci.Branches(out branch)) - ).Repeat( - m => - { - if (branch == null) - { - m.Advance(3); - return; - } - var labels = m.Labels; - m.RemoveInstructions(3).InsertAndAdvance( - new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) - ); - } - ); - return matcher.InstructionEnumeration(); - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnShipIconClick))] - [HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnWarperIconClick))] - [HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnDroneIconClick))] - private static IEnumerable UIControlPanelStationInspector_OnShipIconClick_Transpiler(IEnumerable instructions) - { - var matcher = new CodeMatcher(instructions); - Label? branch = null; - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.isLocal))), - new CodeMatch(ci => ci.Branches(out branch)) - ).Repeat( - m => - { - if (branch == null) - { - m.Advance(3); - return; - } - var labels = m.Labels; - m.RemoveInstructions(3).InsertAndAdvance( - new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) - ); - } - ); - return matcher.InstructionEnumeration(); - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnItemIconMouseDown))] - private static IEnumerable UIControlPanelStationStorage_OnItemIconMouseDown_Transpiler(IEnumerable instructions) - { - var matcher = new CodeMatcher(instructions); - Label? branch = null; - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.isLocal))), - new CodeMatch(ci => ci.Branches(out branch)) - ).Repeat( - m => - { - if (branch == null) - { - m.Advance(3); - return; - } - var labels = m.Labels; - m.RemoveInstructions(3).InsertAndAdvance( - new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) - ); - } - ); - return matcher.InstructionEnumeration(); - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnTakeBackButtonClick))] - private static IEnumerable UIControlPanelStationStorage_OnTakeBackButtonClick_Transpiler(IEnumerable instructions) - { - var matcher = new CodeMatcher(instructions); - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.isLocal))), - new CodeMatch(ci => ci.Branches(out _)) - ).Repeat( - m => - { - var labels = m.Labels; - m.RemoveInstructions(3).Labels.AddRange(labels); - } - ); - return matcher.InstructionEnumeration(); - } - - [HarmonyTranspiler] - [HarmonyPatch(typeof(UIControlPanelVeinCollectorPanel), nameof(UIControlPanelVeinCollectorPanel.OnProductIconClick))] - private static IEnumerable UIControlPanelVeinCollectorPanel_OnProductIconClick_Transpiler(IEnumerable instructions) - { - var matcher = new CodeMatcher(instructions); - Label? branch = null; - matcher.MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelVeinCollectorPanel), nameof(UIControlPanelVeinCollectorPanel.isLocal))), - new CodeMatch(ci => ci.Branches(out branch)) - ).Repeat( - m => - { - if (branch == null) - { - m.Advance(3); - return; - } - var labels = m.Labels; - m.RemoveInstructions(3).InsertAndAdvance( - new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels) - ); - } - ); - return matcher.InstructionEnumeration(); - } - } -} \ No newline at end of file diff --git a/CheatEnabler/UIConfigWindow.cs b/CheatEnabler/UIConfigWindow.cs index 3d39ad2..666b596 100644 --- a/CheatEnabler/UIConfigWindow.cs +++ b/CheatEnabler/UIConfigWindow.cs @@ -1,5 +1,6 @@ using CheatEnabler.Functions; using CheatEnabler.Patches; +using CheatEnabler.Patches.Factory; using UnityEngine; using UXAssist.UI; using UXAssist.Common;