using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using crecheng.DSPModSave; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("AssemblerVerticalConstruction")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AssemblerVerticalConstruction")] [assembly: AssemblyCopyright("Copyright lltcggie 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("5678B572-A4A6-488A-8229-BE42EB9AEFE3")] [assembly: AssemblyFileVersion("1.1.13.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.1.13.0")] namespace AssemblerVerticalConstruction; public class AssemblerComponentEx { public const int DefaultProductTransferStack = 9; public const int DefaultProductBufferStack = 9; private const int InputSlotCount = 6; public int[][] assemblerNextIds = new int[384][]; private long[][] assemblerInputReceivedTicks = new long[384][]; private int[][] assemblerInputReceivedCounts = new int[384][]; public int assemblerCapacity = 384; public int transfarCount = 6; private int productTransferStack = 9; private int productBufferStack = 9; public int ProductTransferStack { get { return productTransferStack; } set { productTransferStack = Math.Max(1, value); } } public int ProductBufferStack { get { return productBufferStack; } set { productBufferStack = Math.Max(1, value); } } public void SetCargoStackLimit(int stack) { int val = (ProductTransferStack = Math.Max(1, stack)); ProductBufferStack = Math.Max(9, val); } public void SetAssemblerCapacity(int newCapacity) { if (newCapacity > assemblerNextIds.Length) { int[][] array = assemblerNextIds; long[][] array2 = assemblerInputReceivedTicks; int[][] array3 = assemblerInputReceivedCounts; assemblerNextIds = new int[newCapacity][]; assemblerInputReceivedTicks = new long[newCapacity][]; assemblerInputReceivedCounts = new int[newCapacity][]; if (array != null) { Array.Copy(array, assemblerNextIds, array.Length); Array.Copy(array2, assemblerInputReceivedTicks, array2.Length); Array.Copy(array3, assemblerInputReceivedCounts, array3.Length); } assemblerCapacity = newCapacity; } } public void EnsureAssemblerArray(int factoryIndex, int requiredCapacity) { EnsureFactoryIndex(factoryIndex); int[] array = assemblerNextIds[factoryIndex]; long[] array2 = assemblerInputReceivedTicks[factoryIndex]; int[] array3 = assemblerInputReceivedCounts[factoryIndex]; if (array == null || array.Length < requiredCapacity || array2 == null || array2.Length < array.Length || array3 == null || array3.Length < array.Length * 6) { int val = ((array != null) ? array.Length : 0); int num = Math.Max(Math.Max(requiredCapacity, val), 256); int[] array4 = new int[num]; if (array != null) { Array.Copy(array, array4, array.Length); } assemblerNextIds[factoryIndex] = array4; long[] array5 = new long[num]; if (array2 != null) { Array.Copy(array2, array5, array2.Length); } assemblerInputReceivedTicks[factoryIndex] = array5; int[] array6 = new int[num * 6]; if (array3 != null) { Array.Copy(array3, array6, array3.Length); } assemblerInputReceivedCounts[factoryIndex] = array6; } } public void ClearAssemblerArray(int factoryIndex, int requiredCapacity) { EnsureAssemblerArray(factoryIndex, requiredCapacity); Array.Clear(assemblerNextIds[factoryIndex], 0, assemblerNextIds[factoryIndex].Length); Array.Clear(assemblerInputReceivedTicks[factoryIndex], 0, assemblerInputReceivedTicks[factoryIndex].Length); Array.Clear(assemblerInputReceivedCounts[factoryIndex], 0, assemblerInputReceivedCounts[factoryIndex].Length); } public int GetNextId(int index, int assemblerId) { if (index >= assemblerNextIds.Length) { return 0; } if (assemblerNextIds[index] == null || assemblerId >= assemblerNextIds[index].Length) { return 0; } return assemblerNextIds[index][assemblerId]; } public int[] GetNextIds(int factoryIndex) { if (factoryIndex < 0 || factoryIndex >= assemblerNextIds.Length) { return null; } return assemblerNextIds[factoryIndex]; } public static void FindRecipeIdForBuild(FactorySystem factorySystem, int assemblerId) { int entityId = factorySystem.assemblerPool[assemblerId].entityId; if (entityId == 0) { return; } int num = entityId; bool flag = default(bool); int num2 = default(int); int num3 = default(int); do { factorySystem.factory.ReadObjectConn(num, 14, ref flag, ref num2, ref num3); num = num2; if (num > 0) { int assemblerId2 = factorySystem.factory.entityPool[num].assemblerId; if (assemblerId2 > 0 && factorySystem.assemblerPool[assemblerId2].id == assemblerId2 && factorySystem.assemblerPool[assemblerId2].recipeId > 0) { ((AssemblerComponent)(ref factorySystem.assemblerPool[assemblerId])).SetRecipe(factorySystem.assemblerPool[assemblerId2].recipeId, factorySystem.factory.entitySignPool); SyncForceAccModeFromSource(factorySystem, assemblerId, assemblerId2); return; } } } while (num != 0); num = entityId; do { factorySystem.factory.ReadObjectConn(num, 15, ref flag, ref num2, ref num3); num = num2; if (num > 0) { int assemblerId3 = factorySystem.factory.entityPool[num].assemblerId; if (assemblerId3 > 0 && factorySystem.assemblerPool[assemblerId3].id == assemblerId3 && factorySystem.assemblerPool[assemblerId3].recipeId > 0) { ((AssemblerComponent)(ref factorySystem.assemblerPool[assemblerId])).SetRecipe(factorySystem.assemblerPool[assemblerId3].recipeId, factorySystem.factory.entitySignPool); SyncForceAccModeFromSource(factorySystem, assemblerId, assemblerId3); break; } } } while (num != 0); } private static void SyncForceAccModeFromSource(FactorySystem factorySystem, int targetAssemblerId, int sourceAssemblerId) { ref AssemblerComponent reference = ref factorySystem.assemblerPool[targetAssemblerId]; ref AssemblerComponent reference2 = ref factorySystem.assemblerPool[sourceAssemblerId]; if (reference.recipeExecuteData != null && reference.recipeExecuteData.productive) { reference.forceAccMode = reference2.forceAccMode; } } public void SetAssemblerInsertTarget(PlanetFactory __instance, int assemblerId, int nextEntityId) { int index = __instance.factorySystem.factory.index; EnsureAssemblerArray(index, assemblerId + 1); if (assemblerId != 0 && __instance.factorySystem.assemblerPool[assemblerId].id == assemblerId) { if (nextEntityId == 0) { assemblerNextIds[index][assemblerId] = 0; return; } int assemblerId2 = __instance.entityPool[nextEntityId].assemblerId; assemblerNextIds[index][assemblerId] = assemblerId2; FindRecipeIdForBuild(__instance.factorySystem, assemblerId); } } public void UnsetAssemblerInsertTarget(PlanetFactory __instance, int assemblerId, int assemblerRemoveId) { int index = __instance.factorySystem.factory.index; EnsureAssemblerArray(index, Math.Max(assemblerId, assemblerRemoveId) + 1); int[] array = assemblerNextIds[index]; if (assemblerRemoveId > 0 && assemblerRemoveId < array.Length) { Interlocked.Exchange(ref array[assemblerRemoveId], 0); } if (assemblerId > 0 && assemblerId < array.Length && array[assemblerId] == assemblerRemoveId) { Interlocked.CompareExchange(ref array[assemblerId], 0, assemblerRemoveId); } } public void SetAssemblerNext(int index, int assemblerId, int nextId) { EnsureAssemblerArray(index, assemblerId + 1); assemblerNextIds[index][assemblerId] = nextId; } public void RebuildAssemblerRelation(PlanetFactory factory, int assemblerId) { RebuildAssemblerRelation(factory, assemblerId, synchronizeRecipe: true); } internal void RebuildAssemblerRelation(PlanetFactory factory, int assemblerId, bool synchronizeRecipe) { if (factory == null || factory.factorySystem == null || assemblerId <= 0) { return; } FactorySystem factorySystem = factory.factorySystem; AssemblerComponent[] assemblerPool = factorySystem.assemblerPool; if (assemblerId >= assemblerPool.Length || assemblerPool[assemblerId].id != assemblerId) { return; } int entityId = assemblerPool[assemblerId].entityId; if (entityId <= 0) { return; } int index = factory.index; EnsureAssemblerArray(index, assemblerId + 1); bool flag = default(bool); int num = default(int); int num2 = default(int); factory.ReadObjectConn(entityId, 15, ref flag, ref num, ref num2); int nextId = 0; if (flag && num > 0 && num < factory.entityPool.Length) { int assemblerId2 = factory.entityPool[num].assemblerId; if (assemblerId2 > 0 && assemblerId2 < assemblerPool.Length && assemblerPool[assemblerId2].id == assemblerId2) { nextId = assemblerId2; } } SetAssemblerNext(index, assemblerId, nextId); factory.ReadObjectConn(entityId, 14, ref flag, ref num, ref num2); if (num > 0 && num < factory.entityPool.Length) { int assemblerId3 = factory.entityPool[num].assemblerId; if (assemblerId3 > 0 && assemblerId3 < assemblerPool.Length && assemblerPool[assemblerId3].id == assemblerId3) { SetAssemblerNext(index, assemblerId3, assemblerId); } } if (synchronizeRecipe) { FindRecipeIdForBuild(factorySystem, assemblerId); } } public void UpdateOutputToNext(PlanetFactory factory, int planeIndex, int assemblerId, AssemblerComponent[] assemblerPool, int assemblerNextId, long transferTick, bool useMutex) { if (assemblerId <= 0 || assemblerNextId <= 0 || assemblerId >= assemblerPool.Length || assemblerNextId >= assemblerPool.Length) { ClearAssemblerNextIfMatches(planeIndex, assemblerId, assemblerNextId); } else if (useMutex) { int entityId = assemblerPool[assemblerId].entityId; int entityId2 = assemblerPool[assemblerNextId].entityId; if (entityId > 0 && entityId2 > 0) { lock (factory.entityMutexs[entityId]) { lock (factory.entityMutexs[entityId2]) { UpdateOutputToNextInner(factory, planeIndex, assemblerId, assemblerNextId, assemblerPool, transferTick); return; } } } ClearAssemblerNextIfMatches(planeIndex, assemblerId, assemblerNextId); } else { UpdateOutputToNextInner(factory, planeIndex, assemblerId, assemblerNextId, assemblerPool, transferTick); } } private void UpdateOutputToNextInner(PlanetFactory factory, int planeIndex, int assemblerId, int assemblerNextId, AssemblerComponent[] assemblerPool, long transferTick) { ref AssemblerComponent reference = ref assemblerPool[assemblerId]; ref AssemblerComponent reference2 = ref assemblerPool[assemblerNextId]; if (reference.id != assemblerId || reference2.id != assemblerNextId) { ClearAssemblerNextIfMatches(planeIndex, assemblerId, assemblerNextId); return; } bool flag = default(bool); int num = default(int); int num2 = default(int); factory.ReadObjectConn(reference.entityId, 15, ref flag, ref num, ref num2); if (!flag || num != reference2.entityId) { int newNextId = 0; if (flag && num > 0 && num < factory.entityPool.Length) { int assemblerId2 = factory.entityPool[num].assemblerId; if (assemblerId2 > 0 && assemblerId2 < assemblerPool.Length && assemblerPool[assemblerId2].id == assemblerId2) { newNextId = assemblerId2; } } ReplaceAssemblerNextIfMatches(planeIndex, assemblerId, assemblerNextId, newNextId); } else { if (reference.recipeId <= 0 || reference.recipeId != reference2.recipeId || reference.served == null || reference2.served == null || reference.recipeExecuteData == null || reference2.recipeExecuteData == null) { return; } RecipeExecuteData recipeExecuteData = reference.recipeExecuteData; RecipeExecuteData recipeExecuteData2 = reference2.recipeExecuteData; int timeSpend = recipeExecuteData2.timeSpend; if (timeSpend <= 0) { return; } int num3 = Math.Max(2, reference2.speedOverride * 180 / timeSpend + 1); int[] served = reference.served; int[] incServed = reference.incServed; int[] served2 = reference2.served; int[] incServed2 = reference2.incServed; int[] requireCounts = recipeExecuteData2.requireCounts; long[] array = assemblerInputReceivedTicks[planeIndex]; int[] array2 = assemblerInputReceivedCounts[planeIndex]; int num4 = assemblerId * 6; int num5 = assemblerNextId * 6; bool flag2 = array[assemblerId] == transferTick; bool flag3 = array[assemblerNextId] == transferTick; bool flag4 = false; int num6 = served.Length; for (int i = 0; i < num6; i++) { int num7 = served[i]; int num8 = requireCounts[i] * num3 - served2[i]; int num9 = (flag2 ? array2[num4 + i] : 0); int num10 = num7 - num9; if (num8 > 0 && num10 > 0) { ref int reference3 = ref incServed[i]; int num11 = Math.Min(num10, num8); if (reference3 <= 0) { reference3 = 0; } int num12 = split_inc_level(ref served[i], ref reference3, num11); if (served[i] == 0) { reference3 = 0; } served2[i] += num11; incServed2[i] += num11 * num12; if (!flag3) { array[assemblerNextId] = transferTick; array2[num5] = 0; array2[num5 + 1] = 0; array2[num5 + 2] = 0; array2[num5 + 3] = 0; array2[num5 + 4] = 0; array2[num5 + 5] = 0; flag3 = true; } array2[num5 + i] += num11; flag4 = true; } } if (flag4) { ((AssemblerComponent)(ref reference)).UpdateNeeds(); ((AssemblerComponent)(ref reference2)).UpdateNeeds(); } int num13 = productTransferStack; int num14 = productBufferStack; int[] productCounts = recipeExecuteData.productCounts; int[] produced = reference.produced; int[] produced2 = reference2.produced; int num15 = productCounts.Length; for (int j = 0; j < num15; j++) { int num16 = productCounts[j]; int num17 = num16 * num14 - produced[j]; int num18 = produced2[j]; if (num17 > 0 && num18 > 0) { int val = num16 * num13; int num19 = Math.Min(num17, Math.Min(val, num18)); produced[j] += num19; produced2[j] -= num19; } } } } private void EnsureFactoryIndex(int factoryIndex) { if (factoryIndex >= assemblerNextIds.Length) { int num = assemblerNextIds.Length; while (factoryIndex >= num) { num *= 2; } SetAssemblerCapacity(num); } } private void ClearAssemblerNextIfMatches(int index, int assemblerId, int expectedNextId) { ReplaceAssemblerNextIfMatches(index, assemblerId, expectedNextId, 0); } private void ReplaceAssemblerNextIfMatches(int index, int assemblerId, int expectedNextId, int newNextId) { if (index >= 0 && index < assemblerNextIds.Length) { int[] array = assemblerNextIds[index]; if (array != null && assemblerId > 0 && assemblerId < array.Length && array[assemblerId] == expectedNextId) { Interlocked.CompareExchange(ref array[assemblerId], newNextId, expectedNextId); } } } private int split_inc_level(ref int n, ref int m, int p) { if (n <= 0) { return 0; } int num = m / n; int num2 = m - num * n; n -= p; int num3 = num2 - n; m -= ((num3 > 0) ? (num * p + num3) : (num * p)); return num; } } [BepInPlugin("lltcggie.DSP.plugin.AssemblerVerticalConstruction", "AssemblerVerticalConstruction", "1.1.13")] [BepInDependency(/*Could not decode attribute arguments.*/)] [ModSaveSettings(/*Could not decode attribute arguments.*/)] public class AssemblerVerticalConstruction : BaseUnityPlugin, IModCanSave { public static readonly int CurrentSavedataVersion = 4; private const string FractionateEverythingGuid = "com.menglei.dsp.fe"; private static readonly Version FractionateEverythingStackingVersion = new Version(2, 3, 0); private const int FractionateEverythingStackLimit = 20; private const string StackLimitNotice = " This is only an upper limit; the actual height is also restricted by the game's construction range, so tall buildings may stop below this value. Requires restart. / 此值仅为上限,实际高度还受游戏建造范围限制,高层建筑可能达不到该层数;需要重启。"; private static ManualLogSource _logger; public static ManualLogSource Logger => _logger; private AssemblerVerticalConstruction() { _logger = ((BaseUnityPlugin)this).Logger; } public void Awake() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown AcceptableValueRange val = new AcceptableValueRange(2, 32); ConfigEntry obj = ((BaseUnityPlugin)this).Config.Bind("Stack Limits", "AssemblerMaxStackCount", 12, new ConfigDescription("Maximum assembler stack count at the highest vertical construction technology level. This is only an upper limit; the actual height is also restricted by the game's construction range, so tall buildings may stop below this value. Requires restart. / 此值仅为上限,实际高度还受游戏建造范围限制,高层建筑可能达不到该层数;需要重启。", (AcceptableValueBase)(object)val, Array.Empty())); ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind("Stack Limits", "SmelterMaxStackCount", 14, new ConfigDescription("Maximum smelter stack count at the highest vertical construction technology level. This is only an upper limit; the actual height is also restricted by the game's construction range, so tall buildings may stop below this value. Requires restart. / 此值仅为上限,实际高度还受游戏建造范围限制,高层建筑可能达不到该层数;需要重启。", (AcceptableValueBase)(object)val, Array.Empty())); ConfigEntry val3 = ((BaseUnityPlugin)this).Config.Bind("Stack Limits", "ChemicalPlantMaxStackCount", 9, new ConfigDescription("Maximum chemical plant stack count at the highest vertical construction technology level. This is only an upper limit; the actual height is also restricted by the game's construction range, so tall buildings may stop below this value. Requires restart. / 此值仅为上限,实际高度还受游戏建造范围限制,高层建筑可能达不到该层数;需要重启。", (AcceptableValueBase)(object)val, Array.Empty())); ConfigEntry val4 = ((BaseUnityPlugin)this).Config.Bind("Stack Limits", "OilRefineryMaxStackCount", 5, new ConfigDescription("Maximum oil refinery stack count at the highest vertical construction technology level. This is only an upper limit; the actual height is also restricted by the game's construction range, so tall buildings may stop below this value. Requires restart. / 此值仅为上限,实际高度还受游戏建造范围限制,高层建筑可能达不到该层数;需要重启。", (AcceptableValueBase)(object)val, Array.Empty())); ConfigEntry val5 = ((BaseUnityPlugin)this).Config.Bind("Stack Limits", "ParticleColliderMaxStackCount", 4, new ConfigDescription("Maximum miniature particle collider stack count at the highest vertical construction technology level. This is only an upper limit; the actual height is also restricted by the game's construction range, so tall buildings may stop below this value. Requires restart. / 此值仅为上限,实际高度还受游戏建造范围限制,高层建筑可能达不到该层数;需要重启。", (AcceptableValueBase)(object)val, Array.Empty())); RemoveLegacyResetConfig(); AssemblerPatches.ApplyMaxStackCounts(obj.Value, val2.Value, val3.Value, val4.Value, val5.Value); ApplyKnownModCompatibility(); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); } private void RemoveLegacyResetConfig() { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; if (!File.Exists(configFilePath) || File.ReadAllText(configFilePath).IndexOf("IsResetNextIds", StringComparison.Ordinal) < 0) { return; } bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; try { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; ConfigEntry val = ((BaseUnityPlugin)this).Config.Bind("config", "IsResetNextIds", false, "Obsolete internal repair option."); ((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val).Definition); ((BaseUnityPlugin)this).Config.Save(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } public void Import(BinaryReader binaryReader) { if ((Object)(object)DSPGame.Game == (Object)null) { return; } int num = binaryReader.ReadInt32() * -1; if (num < CurrentSavedataVersion) { Logger.LogInfo((object)$"Old save data version: read {num} current {CurrentSavedataVersion}"); Logger.LogInfo((object)"ResetNextIds"); AssemblerPatches.ResetNextIds(); return; } if (num != CurrentSavedataVersion) { Logger.LogWarning((object)$"Invalid save data version: read {num} current {CurrentSavedataVersion}"); Logger.LogInfo((object)"ResetNextIds"); AssemblerPatches.ResetNextIds(); return; } int num2 = binaryReader.ReadInt32(); if (num2 > AssemblerPatches.assemblerComponentEx.assemblerCapacity) { AssemblerPatches.assemblerComponentEx.SetAssemblerCapacity(num2); } for (int i = 0; i < num2; i++) { int num3 = binaryReader.ReadInt32(); for (int j = 0; j < num3; j++) { int nextId = binaryReader.ReadInt32(); AssemblerPatches.assemblerComponentEx.SetAssemblerNext(i, j, nextId); } } } public void Export(BinaryWriter binaryWriter) { if ((Object)(object)DSPGame.Game == (Object)null) { return; } binaryWriter.Write(CurrentSavedataVersion * -1); binaryWriter.Write(AssemblerPatches.assemblerComponentEx.assemblerCapacity); for (int i = 0; i < AssemblerPatches.assemblerComponentEx.assemblerCapacity; i++) { if (AssemblerPatches.assemblerComponentEx.assemblerNextIds[i] != null) { binaryWriter.Write(AssemblerPatches.assemblerComponentEx.assemblerNextIds[i].Length); for (int j = 0; j < AssemblerPatches.assemblerComponentEx.assemblerNextIds[i].Length; j++) { binaryWriter.Write(AssemblerPatches.assemblerComponentEx.assemblerNextIds[i][j]); } } else { binaryWriter.Write(0); } } } public void IntoOtherSave() { if (!((Object)(object)DSPGame.Game == (Object)null) && !DSPGame.IsMenuDemo) { Logger.LogInfo((object)"ResetNextIds"); AssemblerPatches.ResetNextIds(); } } private static void ApplyKnownModCompatibility() { if (Chainloader.PluginInfos.TryGetValue("com.menglei.dsp.fe", out var value)) { Version version = value.Metadata.Version; if (!(version == null) && !(version < FractionateEverythingStackingVersion)) { AssemblerVerticalConstructionCompat.SetCargoStackLimit(20); Logger.LogInfo((object)$"Detected FractionateEverything {version}; set stacked product transfer/buffer stack limit to {20}."); } } } } public static class AssemblerVerticalConstructionCompat { public static int ProductTransferStack { get { return AssemblerPatches.assemblerComponentEx.ProductTransferStack; } set { AssemblerPatches.assemblerComponentEx.ProductTransferStack = value; } } public static int ProductBufferStack { get { return AssemblerPatches.assemblerComponentEx.ProductBufferStack; } set { AssemblerPatches.assemblerComponentEx.ProductBufferStack = value; } } public static void SetCargoStackLimit(int stack) { AssemblerPatches.assemblerComponentEx.SetCargoStackLimit(stack); } public static void RebuildAssemblerRelation(PlanetFactory factory, int assemblerId) { AssemblerPatches.RebuildAssemblerRelation(factory, assemblerId); } public static void RebuildFactoryRelations(PlanetFactory factory) { AssemblerPatches.RebuildFactoryRelations(factory); } public static void RebuildAllRelations() { AssemblerPatches.ResetNextIds(); } } [HarmonyPatch] internal class AssemblerPatches { private class ModelSetting { public bool multiLevelAllowPortsOrSlots; public List multiLevelAlternativeIds; public List multiLevelAlternativeYawTransposes; public Vector3 lapJoint; public int[] multiLevelMaxBuildCount; public ModelSetting(bool multiLevelAllowPortsOrSlots, List multiLevelAlternativeIds, List multiLevelAlternativeYawTransposes, Vector3 lapJoint, int[] multiLevelMaxBuildCount) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) this.multiLevelAllowPortsOrSlots = multiLevelAllowPortsOrSlots; this.multiLevelAlternativeIds = multiLevelAlternativeIds; this.multiLevelAlternativeYawTransposes = multiLevelAlternativeYawTransposes; this.lapJoint = lapJoint; this.multiLevelMaxBuildCount = multiLevelMaxBuildCount; } } private static readonly ModelSetting AssemblerSetting = new ModelSetting(multiLevelAllowPortsOrSlots: false, new List { 2303, 2304, 2305, 2318 }, new List { false, false, false, false }, new Vector3(0f, 5.05f, 0f), new int[7] { 2, 4, 6, 8, 10, 11, 12 }); private static readonly ModelSetting SmelterSetting = new ModelSetting(multiLevelAllowPortsOrSlots: false, new List { 2302, 2315, 2319, 6230 }, new List { false, false, false, false }, new Vector3(0f, 4.3f, 0f), new int[7] { 2, 4, 6, 8, 10, 12, 14 }); private static readonly ModelSetting ChemicalPlantSetting = new ModelSetting(multiLevelAllowPortsOrSlots: false, new List { 2309, 2317 }, new List { false, false }, new Vector3(0f, 6.85f, 0f), new int[7] { 2, 4, 5, 6, 7, 8, 9 }); private static readonly ModelSetting OilRefinerySetting = new ModelSetting(multiLevelAllowPortsOrSlots: false, new List { 2308 }, new List { false }, new Vector3(0f, 11.8f, 0f), new int[7] { 1, 2, 2, 3, 3, 4, 5 }); private static readonly ModelSetting MiniatureParticleColliderSetting = new ModelSetting(multiLevelAllowPortsOrSlots: false, new List { 2310 }, new List { false }, new Vector3(0f, 15.2f, 0f), new int[7] { 1, 2, 2, 3, 3, 3, 4 }); private static readonly Dictionary ModelSettingDict = new Dictionary { { 2303, AssemblerSetting }, { 2304, AssemblerSetting }, { 2305, AssemblerSetting }, { 2318, AssemblerSetting }, { 2302, SmelterSetting }, { 2315, SmelterSetting }, { 2319, SmelterSetting }, { 6230, SmelterSetting }, { 2309, ChemicalPlantSetting }, { 2317, ChemicalPlantSetting }, { 2308, OilRefinerySetting }, { 2310, MiniatureParticleColliderSetting } }; public static AssemblerComponentEx assemblerComponentEx = new AssemblerComponentEx(); public static void ApplyMaxStackCounts(int assembler, int smelter, int chemicalPlant, int oilRefinery, int particleCollider) { ApplyMaxStackCount(AssemblerSetting, assembler); ApplyMaxStackCount(SmelterSetting, smelter); ApplyMaxStackCount(ChemicalPlantSetting, chemicalPlant); ApplyMaxStackCount(OilRefinerySetting, oilRefinery); ApplyMaxStackCount(MiniatureParticleColliderSetting, particleCollider); } private static void ApplyMaxStackCount(ModelSetting setting, int maxStackCount) { int[] multiLevelMaxBuildCount = setting.multiLevelMaxBuildCount; int num = multiLevelMaxBuildCount.Length - 1; for (int i = 0; i < num; i++) { if (multiLevelMaxBuildCount[i] > maxStackCount) { multiLevelMaxBuildCount[i] = maxStackCount; } } multiLevelMaxBuildCount[num] = maxStackCount; } public static void RebuildAssemblerRelation(PlanetFactory factory, int assemblerId) { assemblerComponentEx.RebuildAssemblerRelation(factory, assemblerId); } public static void RebuildFactoryRelations(PlanetFactory factory) { if (factory == null || factory.factorySystem == null) { return; } FactorySystem factorySystem = factory.factorySystem; AssemblerComponent[] assemblerPool = factorySystem.assemblerPool; int index = factory.index; int requiredCapacity = assemblerPool.Length; assemblerComponentEx.ClearAssemblerArray(index, requiredCapacity); int[] array = assemblerComponentEx.assemblerNextIds[index]; int[] array2 = new int[array.Length]; int assemblerCursor = factorySystem.assemblerCursor; bool flag = default(bool); int num = default(int); int num2 = default(int); for (int i = 1; i < assemblerCursor; i++) { if (assemblerPool[i].id != i) { continue; } int entityId = assemblerPool[i].entityId; if (entityId <= 0) { continue; } factory.ReadObjectConn(entityId, 15, ref flag, ref num, ref num2); if (flag && num > 0 && num < factory.entityPool.Length) { int assemblerId = factory.entityPool[num].assemblerId; if (assemblerId > 0 && assemblerId < assemblerPool.Length && assemblerPool[assemblerId].id == assemblerId) { array[i] = assemblerId; array2[assemblerId] = i; } } } for (int j = 1; j < assemblerCursor; j++) { if (array2[j] == 0 && assemblerPool[j].id == j) { for (int num3 = array[j]; num3 != 0; num3 = array[num3]) { AssemblerComponentEx.FindRecipeIdForBuild(factorySystem, num3); } } } } public static void ResetNextIds() { for (int i = 0; i < GameMain.data.factories.Length; i++) { if (GameMain.data.factories[i] != null) { RebuildFactoryRelations(GameMain.data.factories[i]); } } } [HarmonyPrefix] [HarmonyPatch(typeof(ItemProto), "Preload")] private static void PreloadPatch(ItemProto __instance, int _index) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) ModelProto val = LDB.models.modelArray[__instance.ModelIndex]; if (val == null || val.prefabDesc == null || !val.prefabDesc.isAssembler || !ModelSettingDict.TryGetValue(((Proto)__instance).ID, out var value)) { return; } LDB.models.modelArray[__instance.ModelIndex].prefabDesc.multiLevel = true; LDB.models.modelArray[__instance.ModelIndex].prefabDesc.multiLevelAllowPortsOrSlots = value.multiLevelAllowPortsOrSlots; LDB.models.modelArray[__instance.ModelIndex].prefabDesc.lapJoint = value.lapJoint; int num = value.multiLevelAlternativeIds.FindIndex((int item) => item == ((Proto)__instance).ID); if (num >= 0) { int[] array = new int[value.multiLevelAlternativeIds.Count - 1]; bool[] array2 = new bool[value.multiLevelAlternativeIds.Count - 1]; int num2 = 0; for (int num3 = 0; num3 < value.multiLevelAlternativeIds.Count; num3++) { if (num3 != num) { array[num2] = value.multiLevelAlternativeIds[num3]; array2[num2] = value.multiLevelAlternativeYawTransposes[num3]; num2++; } } LDB.models.modelArray[__instance.ModelIndex].prefabDesc.multiLevelAlternativeIds = array; LDB.models.modelArray[__instance.ModelIndex].prefabDesc.multiLevelAlternativeYawTransposes = array2; } else { LDB.models.modelArray[__instance.ModelIndex].prefabDesc.multiLevelAlternativeIds = value.multiLevelAlternativeIds.ToArray(); LDB.models.modelArray[__instance.ModelIndex].prefabDesc.multiLevelAlternativeYawTransposes = value.multiLevelAlternativeYawTransposes.ToArray(); } } [HarmonyPrefix] [HarmonyPatch(typeof(FactorySystem), "SetAssemblerCapacity")] private static bool SetAssemblerCapacityPatch(FactorySystem __instance, int newCapacity) { int index = __instance.factory.index; assemblerComponentEx.EnsureAssemblerArray(index, newCapacity); return true; } [HarmonyPrefix] [HarmonyPatch(typeof(PlanetFactory), "ApplyInsertTarget")] public static bool ApplyInsertTargetPatch(PlanetFactory __instance, int entityId, int insertTarget, int slotId, int offset) { if (entityId != 0) { if (insertTarget < 0) { Assert.CannotBeReached(); insertTarget = 0; } else { bool flag = default(bool); int num = default(int); int num2 = default(int); __instance.ReadObjectConn(entityId, 15, ref flag, ref num, ref num2); if (!flag || num != insertTarget) { int num3 = insertTarget; insertTarget = entityId; entityId = num3; } int assemblerId = __instance.entityPool[entityId].assemblerId; if (assemblerId > 0 && __instance.entityPool[insertTarget].assemblerId > 0) { assemblerComponentEx.SetAssemblerInsertTarget(__instance, assemblerId, insertTarget); } } } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(PlanetFactory), "ApplyEntityDisconnection")] public static bool ApplyEntityDisconnectionPatch(PlanetFactory __instance, int otherEntityId, int removingEntityId, int otherSlotId, int removingSlotId) { if (otherEntityId == 0) { return true; } int assemblerId = __instance.entityPool[otherEntityId].assemblerId; if (assemblerId > 0) { int assemblerId2 = __instance.entityPool[removingEntityId].assemblerId; if (assemblerId2 > 0) { assemblerComponentEx.UnsetAssemblerInsertTarget(__instance, assemblerId, assemblerId2); } } return true; } [HarmonyPostfix] [HarmonyPatch(typeof(PlanetFactory), "CreateEntityLogicComponents")] public static void CreateEntityLogicComponentsPatch(PlanetFactory __instance, int entityId, PrefabDesc desc, int prebuildId) { if (entityId != 0 && desc.isAssembler) { int assemblerId = __instance.entityPool[entityId].assemblerId; assemblerComponentEx.RebuildAssemblerRelation(__instance, assemblerId); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameLogic), "FactorySystemLabOutputToNext")] public static void AssemblerOutputToNextPatch(PlanetFactory[] ___factories, int ___factoryCount, long ___timei) { UpdateAssemblerOutputToNext(___factories, ___factoryCount, 0, 1, ___timei + 1, useMutex: false); } [HarmonyPostfix] [HarmonyPatch(typeof(GameLogic), "FactorySystemLabOutputToNext_Parallel")] public static void AssemblerOutputToNextParallelPatch(int threadOrdinal, int threadCount, PlanetFactory[] ___factories, int ___factoryCount, long ___timei) { UpdateAssemblerOutputToNext(___factories, ___factoryCount, threadOrdinal, threadCount, ___timei + 1, useMutex: true); } private static void UpdateAssemblerOutputToNext(PlanetFactory[] factories, int factoryCount, int threadOrdinal, int threadCount, long transferTick, bool useMutex) { if (factoryCount <= 0 || threadCount <= 0) { return; } for (int i = 0; i < factoryCount; i++) { PlanetFactory val = factories[i]; FactorySystem factorySystem = val.factorySystem; AssemblerComponent[] assemblerPool = factorySystem.assemblerPool; int assemblerCursor = factorySystem.assemblerCursor; int index = val.index; int[] nextIds = assemblerComponentEx.GetNextIds(index); if (nextIds == null) { continue; } int num = assemblerCursor - 1; int num2 = 1 + num * threadOrdinal / threadCount; int num3 = 1 + num * (threadOrdinal + 1) / threadCount; if (num3 > nextIds.Length) { num3 = nextIds.Length; } for (int j = num2; j < num3; j++) { int num4 = nextIds[j]; if (num4 > 0) { assemblerComponentEx.UpdateOutputToNext(val, index, j, assemblerPool, num4, transferTick, useMutex); } } } } public static void SyncAssemblerFunctions(FactorySystem factorySystem, Player player, int assemblerId) { int entityId = factorySystem.assemblerPool[assemblerId].entityId; if (entityId == 0) { return; } int num = entityId; bool flag = default(bool); int num2 = default(int); int num3 = default(int); do { factorySystem.factory.ReadObjectConn(num, 14, ref flag, ref num2, ref num3); num = num2; if (num <= 0) { continue; } int assemblerId2 = factorySystem.factory.entityPool[num].assemblerId; if (assemblerId2 <= 0 || factorySystem.assemblerPool[assemblerId2].id != assemblerId2) { continue; } if (factorySystem.assemblerPool[assemblerId].recipeId > 0) { if (factorySystem.assemblerPool[assemblerId2].recipeId != factorySystem.assemblerPool[assemblerId].recipeId) { factorySystem.TakeBackItems_Assembler(player, assemblerId2); ((AssemblerComponent)(ref factorySystem.assemblerPool[assemblerId2])).SetRecipe(factorySystem.assemblerPool[assemblerId].recipeId, factorySystem.factory.entitySignPool); } } else if (factorySystem.assemblerPool[assemblerId2].recipeId != 0) { factorySystem.TakeBackItems_Assembler(player, assemblerId2); ((AssemblerComponent)(ref factorySystem.assemblerPool[assemblerId2])).SetRecipe(0, factorySystem.factory.entitySignPool); } } while (num != 0); num = entityId; bool flag2 = default(bool); int num4 = default(int); int num5 = default(int); do { factorySystem.factory.ReadObjectConn(num, 15, ref flag2, ref num4, ref num5); num = num4; if (num <= 0) { continue; } int assemblerId3 = factorySystem.factory.entityPool[num].assemblerId; if (assemblerId3 <= 0 || factorySystem.assemblerPool[assemblerId3].id != assemblerId3) { continue; } if (factorySystem.assemblerPool[assemblerId].recipeId > 0) { if (factorySystem.assemblerPool[assemblerId3].recipeId != factorySystem.assemblerPool[assemblerId].recipeId) { factorySystem.TakeBackItems_Assembler(factorySystem.factory.gameData.mainPlayer, assemblerId3); ((AssemblerComponent)(ref factorySystem.assemblerPool[assemblerId3])).SetRecipe(factorySystem.assemblerPool[assemblerId].recipeId, factorySystem.factory.entitySignPool); } } else if (factorySystem.assemblerPool[assemblerId3].recipeId != 0) { factorySystem.TakeBackItems_Assembler(factorySystem.factory.gameData.mainPlayer, assemblerId3); ((AssemblerComponent)(ref factorySystem.assemblerPool[assemblerId3])).SetRecipe(0, factorySystem.factory.entitySignPool); } } while (num != 0); } [HarmonyPostfix] [HarmonyPatch(typeof(UIAssemblerWindow), "OnRecipeResetClick")] public static void OnRecipeResetClickPatch(UIAssemblerWindow __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (__instance.assemblerId != 0 && __instance.factory != null && __instance.factorySystem.assemblerPool[__instance.assemblerId].id == __instance.assemblerId) { SyncAssemblerFunctions(__instance.factorySystem, __instance.player, __instance.assemblerId); } } [HarmonyPostfix] [HarmonyPatch(typeof(UIAssemblerWindow), "OnRecipePickerReturn")] public static void OnRecipePickerReturnPatch(UIAssemblerWindow __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (__instance.assemblerId != 0 && __instance.factory != null && __instance.factorySystem.assemblerPool[__instance.assemblerId].id == __instance.assemblerId) { SyncAssemblerFunctions(__instance.factorySystem, __instance.player, __instance.assemblerId); } } [HarmonyPostfix] [HarmonyPatch(typeof(UIAssemblerWindow), "OnIncSwitchClick")] public static void OnIncSwitchClickPatch(UIAssemblerWindow __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (__instance.assemblerId != 0 && __instance.factory != null && __instance.factorySystem.assemblerPool[__instance.assemblerId].id == __instance.assemblerId) { SyncForceAccMode(__instance.factorySystem, __instance.assemblerId); } } public static void SyncForceAccMode(FactorySystem factorySystem, int assemblerId) { int entityId = factorySystem.assemblerPool[assemblerId].entityId; if (entityId == 0) { return; } bool forceAccMode = factorySystem.assemblerPool[assemblerId].forceAccMode; int num = entityId; bool flag = default(bool); int num2 = default(int); int num3 = default(int); do { factorySystem.factory.ReadObjectConn(num, 14, ref flag, ref num2, ref num3); num = num2; if (num <= 0) { continue; } int assemblerId2 = factorySystem.factory.entityPool[num].assemblerId; if (assemblerId2 > 0 && factorySystem.assemblerPool[assemblerId2].id == assemblerId2) { RecipeExecuteData recipeExecuteData = factorySystem.assemblerPool[assemblerId2].recipeExecuteData; if (recipeExecuteData != null && recipeExecuteData.productive) { factorySystem.assemblerPool[assemblerId2].forceAccMode = forceAccMode; } } } while (num != 0); num = entityId; bool flag2 = default(bool); int num4 = default(int); int num5 = default(int); do { factorySystem.factory.ReadObjectConn(num, 15, ref flag2, ref num4, ref num5); num = num4; if (num <= 0) { continue; } int assemblerId3 = factorySystem.factory.entityPool[num].assemblerId; if (assemblerId3 > 0 && factorySystem.assemblerPool[assemblerId3].id == assemblerId3) { RecipeExecuteData recipeExecuteData2 = factorySystem.assemblerPool[assemblerId3].recipeExecuteData; if (recipeExecuteData2 != null && recipeExecuteData2.productive) { factorySystem.assemblerPool[assemblerId3].forceAccMode = forceAccMode; } } } while (num != 0); } [HarmonyPostfix] [HarmonyPatch(typeof(BuildingParameters), "PasteToFactoryObject")] public static void PasteToFactoryObjectPatch(BuildingParameters __instance, int objectId, PlanetFactory factory, bool __result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (__result && objectId > 0 && (int)__instance.type == 5) { int assemblerId = factory.entityPool[objectId].assemblerId; if (assemblerId > 0 && factory.factorySystem.assemblerPool[assemblerId].id == assemblerId && factory.factorySystem.assemblerPool[assemblerId].recipeId == __instance.recipeId) { SyncAssemblerFunctions(factory.factorySystem, factory.gameData.mainPlayer, assemblerId); SyncForceAccMode(factory.factorySystem, assemblerId); assemblerComponentEx.RebuildAssemblerRelation(factory, assemblerId, synchronizeRecipe: false); } } } [HarmonyPostfix] [HarmonyPatch(typeof(BuildTool_Click), "CheckBuildConditions")] public static void CheckBuildConditionsPatch(BuildTool_Click __instance, ref bool __result) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Invalid comparison between Unknown and I4 //IL_0108: Unknown result type (might be due to invalid IL or missing references) if (((BuildTool)__instance).buildPreviews.Count == 0) { return; } GameHistoryData history = ((BuildTool)__instance).actionBuild.history; bool flag = true; bool flag2 = default(bool); int num5 = default(int); int num6 = default(int); for (int i = 0; i < ((BuildTool)__instance).buildPreviews.Count; i++) { BuildPreview val = ((BuildTool)__instance).buildPreviews[i]; if ((int)val.condition != 0 || !val.desc.isAssembler || !val.desc.multiLevel) { continue; } int iD = ((Proto)val.item).ID; if (!ModelSettingDict.TryGetValue(iD, out var value)) { continue; } int num = history.storageLevel - 2; if (num >= value.multiLevelMaxBuildCount.Length) { continue; } int num2 = value.multiLevelMaxBuildCount[num]; int num3 = value.multiLevelMaxBuildCount[6]; int num4 = 0; if (val.inputObjId != 0) { ((BuildTool)__instance).factory.ReadObjectConn(val.inputObjId, 14, ref flag2, ref num5, ref num6); while (num5 != 0) { num4++; ((BuildTool)__instance).factory.ReadObjectConn(num5, 14, ref flag2, ref num5, ref num6); } } if (num2 >= 2 && num4 >= num2 - 1) { flag = num2 >= num3; val.condition = (EBuildCondition)40; } } for (int j = 0; j < ((BuildTool)__instance).buildPreviews.Count; j++) { BuildPreview val2 = ((BuildTool)__instance).buildPreviews[j]; if ((int)val2.condition == 40) { ((BuildTool)__instance).actionBuild.model.cursorState = -1; ((BuildTool)__instance).actionBuild.model.cursorText = val2.conditionText; if (!flag) { BuildModel model = ((BuildTool)__instance).actionBuild.model; model.cursorText += Localization.Translate("垂直建造可升级"); } if (!VFInput.onGUI) { UICursor.SetCursor((ECursor)6); } __result = false; break; } } } }