Decompiled source of CondensedProduction v1.4.0

CondensedProduction.dll

Decompiled 9 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
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: AssemblyVersion("0.0.0.0")]
namespace CondensedProduction;

public static class BeltStack
{
	public static int MaxStack
	{
		get
		{
			int num = ((Plugin.BeltStackMaxEntry == null) ? 255 : Plugin.BeltStackMaxEntry.Value);
			if (num < 1)
			{
				return 1;
			}
			if (num <= 255)
			{
				return num;
			}
			return 255;
		}
	}

	public static bool Enabled
	{
		get
		{
			if (Plugin.BeltStackEnabledEntry != null)
			{
				return Plugin.BeltStackEnabledEntry.Value;
			}
			return true;
		}
	}
}
public static class InserterStackPatch
{
	[HarmonyPrefix]
	[HarmonyPatch(typeof(InserterComponent), "InternalUpdate")]
	[HarmonyPatch(typeof(InserterComponent), "InternalUpdate_Bidirectional")]
	[HarmonyPatch(typeof(InserterComponent), "InternalUpdateNoAnim")]
	private static void Prefix(ref InserterComponent __instance)
	{
		if (BeltStack.Enabled && __instance.canStack)
		{
			if (__instance.stackInput != 1)
			{
				__instance.stackInput = 1;
			}
			int maxStack = BeltStack.MaxStack;
			if (__instance.stackOutput != (byte)maxStack)
			{
				__instance.stackOutput = (byte)maxStack;
			}
		}
	}

	[HarmonyTranspiler]
	[HarmonyPatch(typeof(InserterComponent), "InternalUpdate")]
	[HarmonyPatch(typeof(InserterComponent), "InternalUpdate_Bidirectional")]
	[HarmonyPatch(typeof(InserterComponent), "InternalUpdateNoAnim")]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		int max = BeltStack.MaxStack;
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		for (int i = 0; i < list.Count; i++)
		{
			if (list[i].opcode == OpCodes.Ldc_I4_4 && i + 1 < list.Count && list[i + 1].opcode == OpCodes.Div)
			{
				yield return new CodeInstruction(OpCodes.Ldc_I4, (object)max);
			}
			else
			{
				yield return list[i];
			}
		}
	}
}
public static class StationOutputPatch
{
	[HarmonyPatch(typeof(StationComponent), "UpdateOutputSlots")]
	[HarmonyPrefix]
	private static void Prefix(StationComponent __instance)
	{
		if (BeltStack.Enabled && __instance.planetId > 0)
		{
			int maxStack = BeltStack.MaxStack;
			if (maxStack > 4 && __instance.pilerCount != maxStack)
			{
				__instance.pilerCount = maxStack;
			}
		}
	}
}
public static class PilerStackPatch
{
	[HarmonyPatch(typeof(PilerComponent), "InternalUpdate")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		int max = BeltStack.MaxStack;
		foreach (CodeInstruction instruction in instructions)
		{
			if (instruction.opcode == OpCodes.Ldc_I4_4)
			{
				yield return new CodeInstruction(OpCodes.Ldc_I4, (object)max);
			}
			else if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num && num == 4f)
			{
				yield return new CodeInstruction(OpCodes.Ldc_R4, (object)(float)max);
			}
			else
			{
				yield return instruction;
			}
		}
	}
}
public static class CargoVisualClampPatch
{
	private static readonly List<int> stash = new List<int>(2048);

	[HarmonyPatch(typeof(CargoContainer), "Draw")]
	[HarmonyPrefix]
	private static void PreDraw(CargoContainer __instance)
	{
		if (Plugin.KeepVisualStackEntry == null || !Plugin.KeepVisualStackEntry.Value)
		{
			return;
		}
		Cargo[] cargoPool = __instance.cargoPool;
		if (cargoPool == null)
		{
			return;
		}
		int num = Math.Min(__instance.cursor, cargoPool.Length);
		stash.Clear();
		for (int i = 0; i < num; i++)
		{
			if (cargoPool[i].stack > 4)
			{
				stash.Add(i);
				stash.Add(cargoPool[i].stack);
				cargoPool[i].stack = 4;
			}
		}
	}

	[HarmonyPatch(typeof(CargoContainer), "Draw")]
	[HarmonyPostfix]
	private static void PostDraw(CargoContainer __instance)
	{
		if (stash.Count == 0)
		{
			return;
		}
		Cargo[] cargoPool = __instance.cargoPool;
		if (cargoPool != null)
		{
			int num = cargoPool.Length;
			for (int i = 0; i + 1 < stash.Count; i += 2)
			{
				int num2 = stash[i];
				if (num2 >= 0 && num2 < num)
				{
					cargoPool[num2].stack = (byte)stash[i + 1];
				}
			}
		}
		stash.Clear();
	}
}
[HarmonyPatch]
public static class BuildingGrabPatch
{
	public static void Grab(ref int slot, ref byte stackOut)
	{
		int maxStack = BeltStack.MaxStack;
		int num = ((slot < maxStack) ? slot : maxStack);
		slot -= num;
		stackOut = (byte)num;
	}

	private static IEnumerable<MethodBase> TargetMethods()
	{
		Type byrefByte = typeof(byte).MakeByRefType();
		yield return AccessTools.Method(typeof(PlanetFactory), "PickFrom", new Type[6]
		{
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(int[]),
			byrefByte,
			byrefByte
		}, (Type[])null);
		yield return AccessTools.Method(typeof(PlanetFactory), "PickFrom", new Type[6]
		{
			typeof(uint),
			typeof(int),
			typeof(int),
			typeof(int[]),
			byrefByte,
			byrefByte
		}, (Type[])null);
	}

	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Expected O, but got Unknown
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Expected O, but got Unknown
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Expected O, but got Unknown
		//IL_0376: Unknown result type (might be due to invalid IL or missing references)
		//IL_0380: Expected O, but got Unknown
		//IL_038b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0395: Expected O, but got Unknown
		//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03aa: Expected O, but got Unknown
		//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bf: Expected O, but got Unknown
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Expected O, but got Unknown
		//IL_0241: Unknown result type (might be due to invalid IL or missing references)
		//IL_024b: Expected O, but got Unknown
		//IL_0264: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		MethodInfo methodInfo = AccessTools.Method(typeof(BuildingGrabPatch), "Grab", (Type[])null, (Type[])null);
		MethodInfo cache = null;
		ParameterInfo parameterInfo = null;
		ParameterInfo[] parameters = original.GetParameters();
		foreach (ParameterInfo parameterInfo2 in parameters)
		{
			if (parameterInfo2.Name == "stack")
			{
				parameterInfo = parameterInfo2;
			}
		}
		int num = ((parameterInfo == null) ? (-1) : (parameterInfo.Position + ((!original.IsStatic) ? 1 : 0)));
		CodeInstruction val = ((num < 0) ? ((CodeInstruction)null) : new CodeInstruction(OpCodes.Ldarg_S, (object)num));
		for (int j = 0; j < list.Count; j++)
		{
			if (!IsTakeItemCall(list[j], ref cache))
			{
				continue;
			}
			LocalBuilder localBuilder = null;
			bool flag = false;
			int num2 = j - 1;
			while (num2 >= 0 && num2 >= j - 8)
			{
				LocalBuilder localBuilder2 = GetLocalBuilder(list[num2], OpCodes.Ldloca_S, OpCodes.Ldloca);
				if (localBuilder2 != null)
				{
					if (flag)
					{
						localBuilder = localBuilder2;
						break;
					}
					flag = true;
				}
				num2--;
			}
			if (localBuilder == null)
			{
				continue;
			}
			int num3 = j - 1;
			while (num3 >= 0 && num3 >= j - 30)
			{
				if (list[num3].opcode == OpCodes.Ldc_I4_1 && num3 + 1 < list.Count && IsStoreOf(list[num3 + 1], localBuilder))
				{
					list[num3] = new CodeInstruction(OpCodes.Ldc_I4, (object)255);
					break;
				}
				num3--;
			}
			List<CodeInstruction> list2 = new List<CodeInstruction>();
			if (val != null)
			{
				list2.Add(val.Clone());
				list2.Add(new CodeInstruction(OpCodes.Ldloc_S, (object)localBuilder));
				list2.Add(new CodeInstruction(OpCodes.Conv_U1, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Stind_I1, (object)null));
			}
			list.InsertRange(j + 1, list2);
			j += list2.Count;
			for (int k = j + 1; k < list.Count && k <= j + 10; k++)
			{
				if (IsLoadOf(list[k], localBuilder) && k + 2 < list.Count && list[k + 1].opcode == OpCodes.Ldc_I4_1 && list[k + 2].opcode == OpCodes.Bne_Un)
				{
					list[k + 1] = new CodeInstruction(OpCodes.Ldc_I4_0, (object)null);
					list[k + 2] = new CodeInstruction(OpCodes.Beq, list[k + 2].operand);
					break;
				}
			}
		}
		for (int l = 0; l + 5 < list.Count; l++)
		{
			if (list[l].opcode == OpCodes.Ldelema && list[l + 1].opcode == OpCodes.Dup && list[l + 2].opcode == OpCodes.Ldind_I4 && list[l + 3].opcode == OpCodes.Ldc_I4_1 && list[l + 4].opcode == OpCodes.Sub && list[l + 5].opcode == OpCodes.Stind_I4 && val != null)
			{
				list[l + 1] = val.Clone();
				list[l + 2] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
				list[l + 3] = new CodeInstruction(OpCodes.Nop, (object)null);
				list[l + 4] = new CodeInstruction(OpCodes.Nop, (object)null);
				list[l + 5] = new CodeInstruction(OpCodes.Nop, (object)null);
				l += 5;
			}
		}
		return list;
	}

	private static LocalBuilder GetLocalBuilder(CodeInstruction ci, OpCode shortForm, OpCode longForm)
	{
		if (ci.opcode != shortForm && ci.opcode != longForm)
		{
			return null;
		}
		return ci.operand as LocalBuilder;
	}

	private static bool IsStoreOf(CodeInstruction ci, LocalBuilder lb)
	{
		if ((ci.opcode == OpCodes.Stloc_S || ci.opcode == OpCodes.Stloc) && ci.operand is LocalBuilder localBuilder)
		{
			return localBuilder.LocalIndex == lb.LocalIndex;
		}
		return false;
	}

	private static bool IsLoadOf(CodeInstruction ci, LocalBuilder lb)
	{
		if ((ci.opcode == OpCodes.Ldloc_S || ci.opcode == OpCodes.Ldloc) && ci.operand is LocalBuilder localBuilder)
		{
			return localBuilder.LocalIndex == lb.LocalIndex;
		}
		return false;
	}

	private static bool IsTakeItemCall(CodeInstruction ci, ref MethodInfo cache)
	{
		if (ci.opcode != OpCodes.Callvirt)
		{
			return false;
		}
		MethodInfo methodInfo = ci.operand as MethodInfo;
		if (methodInfo == null || methodInfo.DeclaringType != typeof(StationComponent))
		{
			return false;
		}
		if (methodInfo.Name != "TakeItem")
		{
			return false;
		}
		cache = methodInfo;
		return true;
	}
}
public static class GlobalSupply
{
	private static long lastTick = long.MinValue;

	private static readonly HashSet<int> errLogged = new HashSet<int>();

	private static readonly HashSet<int> diagLogged = new HashSet<int>();

	public static void Tick()
	{
		if ((Plugin.GlobalSupplyEnabledEntry != null && !Plugin.GlobalSupplyEnabledEntry.Value) || GameMain.data == null)
		{
			return;
		}
		PlanetFactory[] factories = GameMain.data.factories;
		if (factories == null)
		{
			return;
		}
		long gameTick = GameMain.gameTick;
		int num = ((Plugin.GlobalSupplyIntervalEntry == null) ? 30 : Plugin.GlobalSupplyIntervalEntry.Value);
		if (num < 1)
		{
			num = 1;
		}
		if (gameTick < lastTick)
		{
			lastTick = long.MinValue;
			diagLogged.Clear();
		}
		if (lastTick != long.MinValue && gameTick - lastTick < num)
		{
			return;
		}
		lastTick = gameTick;
		bool collect = Plugin.GlobalSupplyCollectEntry == null || Plugin.GlobalSupplyCollectEntry.Value;
		for (int i = 0; i < factories.Length; i++)
		{
			PlanetFactory val = factories[i];
			if (val == null || val.factorySystem == null || val.transport == null || !MultStore.PlanetHasWireless(val.planetId))
			{
				continue;
			}
			try
			{
				ProcessFactory(val, collect);
			}
			catch (Exception arg)
			{
				if (MultStore.Log != null && errLogged.Add(i))
				{
					MultStore.Log.LogError((object)$"[CP] wireless supply error planet={val.planetId}: {arg}");
				}
			}
		}
	}

	private static void ProcessFactory(PlanetFactory factory, bool collect)
	{
		FactorySystem factorySystem = factory.factorySystem;
		PlanetTransport transport = factory.transport;
		StationComponent[] stationPool = transport.stationPool;
		int stationCount = 0;
		if (stationPool != null)
		{
			stationCount = Math.Min(Math.Max(transport.stationCount, transport.stationCursor), stationPool.Length);
		}
		AssemblerComponent[] assemblerPool = factorySystem.assemblerPool;
		if (assemblerPool != null)
		{
			int num = Math.Min(factorySystem.assemblerCursor, assemblerPool.Length);
			for (int i = 1; i < num; i++)
			{
				if (assemblerPool[i].entityId != 0)
				{
					ProcessAssembler(factory.planetId, ref assemblerPool[i], stationPool, stationCount, collect);
				}
			}
		}
		LabComponent[] labPool = factorySystem.labPool;
		if (labPool == null)
		{
			return;
		}
		int num2 = Math.Min(factorySystem.labCursor, labPool.Length);
		for (int j = 1; j < num2; j++)
		{
			if (labPool[j].entityId != 0)
			{
				if (labPool[j].researchMode)
				{
					ProcessResearchLab(factory, ref labPool[j], stationPool, stationCount);
				}
				else
				{
					ProcessLab(factory.planetId, ref labPool[j], stationPool, stationCount, collect);
				}
			}
		}
	}

	private static void ProcessResearchLab(PlanetFactory factory, ref LabComponent c, StationComponent[] stations, int stationCount)
	{
		int planetId = factory.planetId;
		if (!MultStore.GetWireless(planetId, c.entityId) || c.matrixServed == null)
		{
			return;
		}
		int num = MultStore.GetSet(planetId, c.entityId);
		if (num < 1)
		{
			num = 1;
		}
		long num2 = 36000L * (long)num;
		int[] matrixIds = LabComponent.matrixIds;
		int num3 = Math.Min(c.matrixServed.Length, (matrixIds == null) ? 6 : matrixIds.Length);
		if (num3 > 6)
		{
			num3 = 6;
		}
		byte b = default(byte);
		for (int i = 0; i < num3; i++)
		{
			int num4 = ((matrixIds != null) ? matrixIds[i] : (6001 + i));
			if (num4 <= 0)
			{
				continue;
			}
			long num5 = (num2 - c.matrixServed[i]) / 3600;
			if (num5 <= 0)
			{
				continue;
			}
			if (num5 > 200000)
			{
				num5 = 200000L;
			}
			int num6 = (int)num5;
			while (num6 > 0)
			{
				int want = ((num6 > 255) ? 255 : num6);
				TakeFromStations(stations, stationCount, num4, want, out var taken, out var incPts);
				if (taken <= 0)
				{
					break;
				}
				int num7 = incPts / taken;
				if (num7 < 0)
				{
					num7 = 0;
				}
				if (num7 > 4)
				{
					num7 = 4;
				}
				int num8 = factory.InsertInto(c.entityId, -1, num4, (byte)taken, (byte)num7, ref b);
				if (num8 <= 0)
				{
					AddToStations(stations, stationCount, num4, taken, incPts);
					break;
				}
				num6 -= num8;
				if (num8 < taken)
				{
					AddToStations(stations, stationCount, num4, taken - num8, incPts * (taken - num8) / taken);
				}
			}
		}
		if (MultStore.Log == null || !diagLogged.Add(c.entityId))
		{
			return;
		}
		int num9 = 0;
		if (stations != null)
		{
			for (int j = 0; j < stationCount; j++)
			{
				if (stations[j] != null && stations[j].storage != null)
				{
					num9++;
				}
			}
		}
		MultStore.Log.LogInfo((object)$"[CP] research-lab wireless planet={planetId} entity={c.entityId} kSet={num} matrix0={((c.matrixServed.Length != 0) ? c.matrixServed[0] : (-1))} stationCount={stationCount} withStorage={num9}");
	}

	private static void ProcessAssembler(int planetId, ref AssemblerComponent c, StationComponent[] stations, int stationCount, bool collect)
	{
		if (!MultStore.GetWireless(planetId, c.entityId))
		{
			return;
		}
		int set = MultStore.GetSet(planetId, c.entityId);
		RecipeExecuteData recipeExecuteData = c.recipeExecuteData;
		if (recipeExecuteData == null || recipeExecuteData.requires == null || recipeExecuteData.requireCounts == null || recipeExecuteData.timeSpend <= 0 || c.served == null || c.incServed == null)
		{
			return;
		}
		int num = c.speedOverride * 180 / recipeExecuteData.timeSpend + 1;
		if (num < 2)
		{
			num = 2;
		}
		long num2 = (long)set * (long)BufferFactor() * num;
		int num3 = Math.Min(recipeExecuteData.requires.Length, Math.Min(recipeExecuteData.requireCounts.Length, c.served.Length));
		for (int i = 0; i < num3; i++)
		{
			int num4 = recipeExecuteData.requires[i];
			int num5 = recipeExecuteData.requireCounts[i];
			if (num4 <= 0 || num5 <= 0)
			{
				continue;
			}
			long num6 = num5 * num2;
			int num7 = (int)((num6 > int.MaxValue) ? int.MaxValue : num6) - c.served[i];
			if (num7 > 0)
			{
				TakeFromStations(stations, stationCount, num4, num7, out var taken, out var incPts);
				if (taken > 0)
				{
					c.served[i] += taken;
					c.incServed[i] += incPts;
				}
			}
		}
		if (collect)
		{
			CollectProducts(stations, stationCount, ref c);
		}
	}

	private static void ProcessLab(int planetId, ref LabComponent c, StationComponent[] stations, int stationCount, bool collect)
	{
		if (!MultStore.GetWireless(planetId, c.entityId))
		{
			return;
		}
		int set = MultStore.GetSet(planetId, c.entityId);
		RecipeExecuteData recipeExecuteData = c.recipeExecuteData;
		if (recipeExecuteData == null || recipeExecuteData.requires == null || recipeExecuteData.requireCounts == null || recipeExecuteData.timeSpend <= 0 || c.served == null || c.incServed == null)
		{
			return;
		}
		long num = (long)set * (long)BufferFactor();
		int num2 = Math.Min(recipeExecuteData.requires.Length, Math.Min(recipeExecuteData.requireCounts.Length, c.served.Length));
		for (int i = 0; i < num2; i++)
		{
			int num3 = recipeExecuteData.requires[i];
			int num4 = recipeExecuteData.requireCounts[i];
			if (num3 <= 0 || num4 <= 0)
			{
				continue;
			}
			long num5 = num4 * num;
			int num6 = (int)((num5 > int.MaxValue) ? int.MaxValue : num5) - c.served[i];
			if (num6 > 0)
			{
				TakeFromStations(stations, stationCount, num3, num6, out var taken, out var incPts);
				if (taken > 0)
				{
					c.served[i] += taken;
					c.incServed[i] += incPts;
				}
			}
		}
		if (collect)
		{
			CollectLabProducts(stations, stationCount, ref c);
		}
	}

	private static void CollectProducts(StationComponent[] stations, int stationCount, ref AssemblerComponent c)
	{
		RecipeExecuteData recipeExecuteData = c.recipeExecuteData;
		if (recipeExecuteData == null || recipeExecuteData.products == null || recipeExecuteData.productCounts == null || c.produced == null)
		{
			return;
		}
		int num = Math.Min(recipeExecuteData.products.Length, c.produced.Length);
		if (num == 0)
		{
			return;
		}
		int num2 = AvgIncLevel(c.incUsed, c.served, c.incServed);
		for (int i = 0; i < num; i++)
		{
			int num3 = recipeExecuteData.products[i];
			int num4 = c.produced[i];
			if (num3 > 0 && num4 > 0)
			{
				int num5 = AddToStations(stations, stationCount, num3, num4, num2 * num4);
				if (num5 > 0)
				{
					c.produced[i] -= num5;
				}
			}
		}
	}

	private static void CollectLabProducts(StationComponent[] stations, int stationCount, ref LabComponent c)
	{
		RecipeExecuteData recipeExecuteData = c.recipeExecuteData;
		if (recipeExecuteData == null || recipeExecuteData.products == null || recipeExecuteData.productCounts == null || c.produced == null)
		{
			return;
		}
		int num = Math.Min(recipeExecuteData.products.Length, c.produced.Length);
		if (num == 0)
		{
			return;
		}
		int num2 = AvgIncLevel(c.incUsed, c.served, c.incServed);
		for (int i = 0; i < num; i++)
		{
			int num3 = recipeExecuteData.products[i];
			int num4 = c.produced[i];
			if (num3 > 0 && num4 > 0)
			{
				int num5 = AddToStations(stations, stationCount, num3, num4, num2 * num4);
				if (num5 > 0)
				{
					c.produced[i] -= num5;
				}
			}
		}
	}

	private static int AvgIncLevel(bool incUsed, int[] served, int[] incServed)
	{
		if (!incUsed || served == null || incServed == null)
		{
			return 0;
		}
		int num = 0;
		int num2 = 0;
		int num3 = Math.Min(served.Length, incServed.Length);
		for (int i = 0; i < num3; i++)
		{
			if (served[i] > 0 && incServed[i] > 0)
			{
				num += served[i];
				num2 += incServed[i];
			}
		}
		if (num <= 0)
		{
			return 0;
		}
		int num4 = num2 / num;
		if (num4 >= 0)
		{
			if (num4 <= 4)
			{
				return num4;
			}
			return 4;
		}
		return 0;
	}

	private static void TakeFromStations(StationComponent[] stations, int stationCount, int itemId, int want, out int taken, out int incPts)
	{
		taken = 0;
		incPts = 0;
		if (stations == null)
		{
			return;
		}
		int num3 = default(int);
		for (int i = 0; i < stationCount; i++)
		{
			if (taken >= want)
			{
				break;
			}
			StationComponent val = stations[i];
			if (val == null || val.storage == null)
			{
				continue;
			}
			while (taken < want)
			{
				int num = itemId;
				int num2 = want - taken;
				val.TakeItem(ref num, ref num2, ref num3);
				if (num2 <= 0 || num != itemId)
				{
					break;
				}
				taken += num2;
				incPts += num3 * num2;
			}
		}
		if (taken > want)
		{
			taken = want;
		}
	}

	private static int AddToStations(StationComponent[] stations, int stationCount, int itemId, int count, int incPts)
	{
		if (stations == null || count <= 0)
		{
			return 0;
		}
		int num = count;
		int num2 = 0;
		for (int i = 0; i < stationCount; i++)
		{
			if (num <= 0)
			{
				break;
			}
			StationComponent val = stations[i];
			if (val == null || val.storage == null)
			{
				continue;
			}
			StationStore[] storage = val.storage;
			for (int j = 0; j < storage.Length; j++)
			{
				if (num <= 0)
				{
					break;
				}
				if (storage[j].itemId == itemId && storage[j].max > 0)
				{
					int num3 = storage[j].max - storage[j].count;
					if (num3 > 0)
					{
						int num4 = ((num < num3) ? num : num3);
						int num5 = (int)((long)incPts * (long)num4 / count);
						storage[j].count += num4;
						storage[j].inc += num5;
						num -= num4;
						num2 += num4;
					}
				}
			}
		}
		return num2;
	}

	private static int BufferFactor()
	{
		int num = ((Plugin.InputBufferMultiplierEntry == null) ? 3 : Plugin.InputBufferMultiplierEntry.Value);
		if (num >= 1)
		{
			return num;
		}
		return 1;
	}
}
public static class GlobalSupplyPatch
{
	[HarmonyPatch(typeof(GameLogic), "LogicFrame")]
	[HarmonyPostfix]
	private static void PostLogicFrame()
	{
		GlobalSupply.Tick();
		StorageCapPatch.Maintain();
	}
}
public static class MultiplierUI
{
	private const int WinIdAssembler = 93101;

	private const int WinIdLab = 93102;

	private const float PanelW = 230f;

	private const float PanelH = 178f;

	private static Texture2D whiteTex;

	private static string inputText = "";

	private static int editingKey = -1;

	private static FieldInfo labFactoryField;

	private static FieldInfo labFactorySystemField;

	private static readonly Dictionary<int, Vector2> userOffset = new Dictionary<int, Vector2>();

	private static readonly Vector3[] cornersBuf = (Vector3[])(object)new Vector3[4];

	private static Rect lastPanelRect;

	public static bool PanelShowing { get; private set; }

	public static void Init()
	{
		labFactoryField = typeof(UILabWindow).GetField("factory", BindingFlags.Instance | BindingFlags.NonPublic);
		labFactorySystemField = typeof(UILabWindow).GetField("factorySystem", BindingFlags.Instance | BindingFlags.NonPublic);
	}

	public static bool IsMouseOverPanel()
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		if (!PanelShowing)
		{
			return false;
		}
		Vector3 mousePosition = Input.mousePosition;
		if (mousePosition.x >= ((Rect)(ref lastPanelRect)).x && mousePosition.x <= ((Rect)(ref lastPanelRect)).x + ((Rect)(ref lastPanelRect)).width && mousePosition.y >= (float)Screen.height - ((Rect)(ref lastPanelRect)).y - ((Rect)(ref lastPanelRect)).height)
		{
			return mousePosition.y <= (float)Screen.height - ((Rect)(ref lastPanelRect)).y;
		}
		return false;
	}

	public static void Draw()
	{
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		PanelShowing = false;
		if ((Object)(object)GameMain.instance == (Object)null || GameMain.isPaused)
		{
			return;
		}
		UIGame val = (((Object)(object)UIRoot.instance != (Object)null) ? UIRoot.instance.uiGame : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		UIAssemblerWindow assemblerWindow = val.assemblerWindow;
		if ((Object)(object)assemblerWindow != (Object)null && ((ManualBehaviour)assemblerWindow).active && assemblerWindow.assemblerId != 0 && assemblerWindow.factorySystem != null)
		{
			AssemblerComponent val2 = assemblerWindow.factorySystem.assemblerPool[assemblerWindow.assemblerId];
			DrawPanel(93101, assemblerWindow.windowTrans, assemblerWindow.factorySystem.factory, val2.entityId);
			return;
		}
		UILabWindow labWindow = val.labWindow;
		if ((Object)(object)labWindow != (Object)null && ((ManualBehaviour)labWindow).active && labWindow.labId != 0)
		{
			PlanetFactory val3 = (PlanetFactory)((labFactoryField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			FactorySystem val4 = (FactorySystem)((labFactorySystemField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if (val3 != null && val4 != null)
			{
				LabComponent val5 = val4.labPool[labWindow.labId];
				DrawPanel(93102, labWindow.windowTrans, val3, val5.entityId);
			}
		}
	}

	private static Vector2 ComputeDock(RectTransform anchor)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		Vector2 result = default(Vector2);
		((Vector2)(ref result))..ctor((float)Screen.width - 230f - 20f, (float)Screen.height * 0.3f);
		if ((Object)(object)anchor == (Object)null)
		{
			return result;
		}
		anchor.GetWorldCorners(cornersBuf);
		Vector3 val = cornersBuf[2] - cornersBuf[0];
		if (((Vector3)(ref val)).sqrMagnitude < 1f)
		{
			return result;
		}
		Camera val2 = null;
		Canvas componentInParent = ((Component)anchor).GetComponentInParent<Canvas>();
		if ((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0)
		{
			val2 = componentInParent.worldCamera;
		}
		Vector2 val3 = RectTransformUtility.WorldToScreenPoint(val2, cornersBuf[2]);
		float num = Mathf.Clamp(val3.x + 8f, 0f, (float)Screen.width - 230f);
		float num2 = Mathf.Clamp((float)Screen.height - val3.y, 0f, (float)Screen.height - 178f);
		return new Vector2(num, num2);
	}

	private static void DrawPanel(int winId, RectTransform anchor, PlanetFactory factory, int entityId)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Expected O, but got Unknown
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		if (factory == null || entityId == 0)
		{
			return;
		}
		int planetId = factory.planetId;
		int num = planetId * 1000000 + entityId;
		if (editingKey != num)
		{
			editingKey = num;
			inputText = MultStore.GetSet(planetId, entityId).ToString();
		}
		Vector2 val = ComputeDock(anchor);
		if (!userOffset.TryGetValue(winId, out var value))
		{
			value = Vector2.zero;
		}
		Rect val2 = default(Rect);
		((Rect)(ref val2))..ctor(val.x + value.x, val.y + value.y, 230f, 178f);
		Rect val3 = GUI.Window(winId, val2, (WindowFunction)delegate
		{
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label("设定倍率上限:", Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("-10", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }))
			{
				Apply(factory, entityId, MultStore.GetSet(planetId, entityId) - 10);
			}
			if (GUILayout.Button("-1", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(35f) }))
			{
				Apply(factory, entityId, MultStore.GetSet(planetId, entityId) - 1);
			}
			inputText = GUILayout.TextField(inputText, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			if (GUILayout.Button("+1", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(35f) }))
			{
				Apply(factory, entityId, MultStore.GetSet(planetId, entityId) + 1);
			}
			if (GUILayout.Button("+10", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }))
			{
				Apply(factory, entityId, MultStore.GetSet(planetId, entityId) + 10);
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("1", Array.Empty<GUILayoutOption>()))
			{
				Apply(factory, entityId, 1);
			}
			if (GUILayout.Button("5", Array.Empty<GUILayoutOption>()))
			{
				Apply(factory, entityId, 5);
			}
			if (GUILayout.Button("10", Array.Empty<GUILayoutOption>()))
			{
				Apply(factory, entityId, 10);
			}
			if (GUILayout.Button("100", Array.Empty<GUILayoutOption>()))
			{
				Apply(factory, entityId, 100);
			}
			if (GUILayout.Button("1000", Array.Empty<GUILayoutOption>()))
			{
				Apply(factory, entityId, 1000);
			}
			if (GUILayout.Button("应用输入", Array.Empty<GUILayoutOption>()) && int.TryParse(inputText, out var result))
			{
				Apply(factory, entityId, result);
			}
			GUILayout.EndHorizontal();
			bool wireless = MultStore.GetWireless(planetId, entityId);
			if (GUILayout.Button(wireless ? "无线:开" : "无线:关", Array.Empty<GUILayoutOption>()))
			{
				MultStore.SetWireless(planetId, entityId, !wireless);
			}
			int set = MultStore.GetSet(planetId, entityId);
			int eff = MultStore.GetEff(planetId, entityId);
			GUILayout.Label($"设定 x{set} / 实际 x{eff}", Array.Empty<GUILayoutOption>());
			DrawEnergyBar(planetId, entityId);
			GUI.DragWindow(new Rect(0f, 0f, 230f, 20f));
		}, "浓缩生产");
		userOffset[winId] = new Vector2(((Rect)(ref val3)).x - val.x, ((Rect)(ref val3)).y - val.y);
		lastPanelRect = val3;
		PanelShowing = true;
	}

	private static void DrawEnergyBar(int planetId, int entityId)
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Expected O, but got Unknown
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Expected O, but got Unknown
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		if (!MultStore.GetBufferInfo(planetId, entityId, out var cur, out var cap))
		{
			int set = MultStore.GetSet(planetId, entityId);
			int num = ((Plugin.EnergyBufferSecondsEntry == null) ? 10 : Plugin.EnergyBufferSecondsEntry.Value);
			if (set <= 1 || num <= 0)
			{
				return;
			}
			cap = (long)set * 40000000L * num * 60;
			if (cap <= 0)
			{
				return;
			}
			MultStore.SetBufferCap(planetId, entityId, cap);
			MultStore.ChargeBuffer(planetId, entityId, cap, cap);
			cur = cap;
		}
		if (cap > 0)
		{
			if ((Object)(object)whiteTex == (Object)null)
			{
				whiteTex = new Texture2D(1, 1);
				whiteTex.SetPixel(0, 0, Color.white);
				whiteTex.Apply();
			}
			float num2 = (float)((double)cur / (double)cap);
			if (num2 < 0f)
			{
				num2 = 0f;
			}
			if (num2 > 1f)
			{
				num2 = 1f;
			}
			Rect rect = GUILayoutUtility.GetRect(206f, 16f);
			GUI.color = new Color(0.15f, 0.15f, 0.15f);
			GUI.DrawTexture(rect, (Texture)(object)whiteTex);
			GUI.color = ((num2 > 0.3f) ? new Color(0.25f, 0.75f, 1f) : new Color(1f, 0.55f, 0.15f));
			GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, (((Rect)(ref rect)).width - 2f) * num2, ((Rect)(ref rect)).height - 2f), (Texture)(object)whiteTex);
			GUI.color = Color.white;
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				alignment = (TextAnchor)4,
				fontSize = 11
			};
			GUI.Label(rect, $"储能 {num2 * 100f:0}%", val);
		}
	}

	private static void Apply(PlanetFactory factory, int entityId, int k)
	{
		if (k < 1)
		{
			k = 1;
		}
		MultStore.SetMult(factory.planetId, entityId, k);
		PropagateToStack(factory, entityId, k);
		inputText = MultStore.GetSet(factory.planetId, entityId).ToString();
	}

	private static void PropagateToStack(PlanetFactory factory, int entityId, int k)
	{
		FactorySystem val = factory?.factorySystem;
		if (val == null || val.labPool == null)
		{
			return;
		}
		LabComponent[] labPool = val.labPool;
		int num = Math.Min(val.labCursor, labPool.Length);
		if (num <= 1)
		{
			return;
		}
		int num2 = -1;
		for (int i = 1; i < num; i++)
		{
			if (labPool[i].entityId == entityId && labPool[i].id != 0)
			{
				num2 = labPool[i].id;
				break;
			}
		}
		if (num2 <= 0)
		{
			return;
		}
		HashSet<int> hashSet = new HashSet<int>();
		int num3 = num2;
		int num4 = 0;
		while (num3 > 0 && num3 < num && num4++ < 64 && hashSet.Add(num3))
		{
			num3 = labPool[num3].nextLabId;
		}
		for (int j = 1; j < num; j++)
		{
			if (labPool[j].id == 0 || hashSet.Contains(labPool[j].id))
			{
				continue;
			}
			int nextLabId = labPool[j].nextLabId;
			int num5 = 0;
			while (nextLabId > 0 && nextLabId < num && num5++ < 64)
			{
				if (hashSet.Contains(nextLabId))
				{
					hashSet.Add(labPool[j].id);
					break;
				}
				nextLabId = labPool[nextLabId].nextLabId;
			}
		}
		foreach (int item in hashSet)
		{
			if (item > 0 && item < num && labPool[item].entityId != 0)
			{
				MultStore.SetMult(factory.planetId, labPool[item].entityId, k);
			}
		}
	}
}
public static class VFInputGuardPatch
{
	[HarmonyPatch(typeof(VFInput), "UpdateGameStates")]
	[HarmonyPostfix]
	private static void Postfix()
	{
		if (MultiplierUI.IsMouseOverPanel())
		{
			VFInput.onGUI = true;
			VFInput.onGUIOperate = true;
		}
	}
}
public static class MultStore
{
	public const int SaveVersion = 3;

	private static readonly Dictionary<int, Dictionary<int, int>> setMap = new Dictionary<int, Dictionary<int, int>>();

	private static readonly HashSet<int> wirelessMap = new HashSet<int>();

	private static readonly ConcurrentDictionary<long, int> effMap = new ConcurrentDictionary<long, int>();

	private static readonly ConcurrentDictionary<long, int> energyMap = new ConcurrentDictionary<long, int>();

	private static readonly ConcurrentDictionary<long, float> satMap = new ConcurrentDictionary<long, float>();

	private static readonly ConcurrentDictionary<long, long> bufferMap = new ConcurrentDictionary<long, long>();

	private static readonly ConcurrentDictionary<long, int> energyCapMap = new ConcurrentDictionary<long, int>();

	private static readonly ConcurrentDictionary<long, long> bufCapMap = new ConcurrentDictionary<long, long>();

	private static readonly ConcurrentDictionary<long, int> healthyCountMap = new ConcurrentDictionary<long, int>();

	[ThreadStatic]
	public static int CurrentPlanetId = -1;

	public static ManualLogSource Log;

	public static int ClipboardK = 1;

	public static bool ClipboardWireless;

	public static int AmbientPlanetId = -1;

	private static readonly Dictionary<long, long> energyFrame = new Dictionary<long, long>();

	private static readonly Dictionary<int, int> capMap = new Dictionary<int, int>();

	public static int EffectivePlanetId
	{
		get
		{
			if (CurrentPlanetId < 0)
			{
				return AmbientPlanetId;
			}
			return CurrentPlanetId;
		}
	}

	public static int MaxMultiplier
	{
		get
		{
			int num = ((Plugin.MaxMultiplierEntry == null) ? 100 : Plugin.MaxMultiplierEntry.Value);
			if (num > 0)
			{
				return num;
			}
			return int.MaxValue;
		}
	}

	public static void SamplePower(int planetId, int entityId, float power)
	{
		if (planetId >= 0)
		{
			if (power < 0f)
			{
				power = 0f;
			}
			if (power > 1f)
			{
				power = 1f;
			}
			satMap[EffKey(planetId, entityId)] = power;
		}
	}

	public static float GetSat(int planetId, int entityId)
	{
		if (!satMap.TryGetValue(EffKey(planetId, entityId), out var value))
		{
			return 1f;
		}
		return value;
	}

	public static long ChargeBuffer(int planetId, int entityId, long delta, long cap)
	{
		long key = EffKey(planetId, entityId);
		bufferMap.TryGetValue(key, out var value);
		value += delta;
		if (value < 0)
		{
			value = 0L;
		}
		if (value > cap)
		{
			value = cap;
		}
		bufferMap[key] = value;
		return value;
	}

	public static void TickHealthyCount(int planetId, int entityId)
	{
		if (planetId >= 0)
		{
			long key = EffKey(planetId, entityId);
			healthyCountMap.AddOrUpdate(key, 1, (long k, int v) => (v >= 9999) ? v : (v + 1));
		}
	}

	public static void ResetHealthyCount(int planetId, int entityId)
	{
		if (planetId >= 0)
		{
			healthyCountMap[EffKey(planetId, entityId)] = 0;
		}
	}

	public static int GetHealthyCount(int planetId, int entityId)
	{
		if (!healthyCountMap.TryGetValue(EffKey(planetId, entityId), out var value))
		{
			return 0;
		}
		return value;
	}

	public static void SetBufferCap(int planetId, int entityId, long cap)
	{
		if (planetId >= 0)
		{
			bufCapMap[EffKey(planetId, entityId)] = cap;
		}
	}

	public static bool GetBufferInfo(int planetId, int entityId, out long cur, out long cap)
	{
		long key = EffKey(planetId, entityId);
		bufferMap.TryGetValue(key, out cur);
		return bufCapMap.TryGetValue(key, out cap);
	}

	public static int GetEnergyCapK(int planetId, int entityId)
	{
		if (!energyCapMap.TryGetValue(EffKey(planetId, entityId), out var value))
		{
			return int.MaxValue;
		}
		return value;
	}

	public static void SetEnergyCapK(int planetId, int entityId, int k)
	{
		if (planetId >= 0)
		{
			energyCapMap[EffKey(planetId, entityId)] = k;
		}
	}

	private static long EffKey(int planetId, int entityId)
	{
		return (long)(planetId + 2) * 1000000L + entityId;
	}

	public static int GetSet(int planetId, int entityId)
	{
		if (setMap.TryGetValue(planetId, out var value) && value.TryGetValue(entityId, out var value2))
		{
			return value2;
		}
		return 1;
	}

	public static void SetMult(int planetId, int entityId, int k)
	{
		int maxMultiplier = MaxMultiplier;
		if (k > maxMultiplier)
		{
			k = maxMultiplier;
		}
		Dictionary<int, int> value;
		if (k <= 1)
		{
			if (setMap.TryGetValue(planetId, out value))
			{
				value.Remove(entityId);
				if (value.Count == 0)
				{
					setMap.Remove(planetId);
				}
			}
			SetEff(planetId, entityId, 1);
			capMap.Remove(entityId);
		}
		else
		{
			if (!setMap.TryGetValue(planetId, out value))
			{
				value = new Dictionary<int, int>();
				setMap[planetId] = value;
			}
			value[entityId] = k;
			capMap[entityId] = k;
		}
	}

	public static bool PlanetHasSets(int planetId)
	{
		return setMap.ContainsKey(planetId);
	}

	public static bool GetWireless(int planetId, int entityId)
	{
		return wirelessMap.Contains(planetId);
	}

	public static void SetWireless(int planetId, int entityId, bool on)
	{
		if (planetId >= 0)
		{
			if (on)
			{
				wirelessMap.Add(planetId);
			}
			else
			{
				wirelessMap.Remove(planetId);
			}
		}
	}

	public static bool PlanetHasWireless(int planetId)
	{
		return wirelessMap.Contains(planetId);
	}

	public static bool TryMarkEnergy(int planetId, int entityId, long frame)
	{
		long key = (long)planetId * 1000000L + entityId;
		if (energyFrame.TryGetValue(key, out var value) && value == frame)
		{
			return false;
		}
		energyFrame[key] = frame;
		return true;
	}

	public static int GetCapK(int entityId)
	{
		if (!capMap.TryGetValue(entityId, out var value))
		{
			return 1;
		}
		return value;
	}

	public static int GetEff(int planetId, int entityId)
	{
		if (!effMap.TryGetValue(EffKey(planetId, entityId), out var value))
		{
			return 1;
		}
		return value;
	}

	public static void SetEff(int planetId, int entityId, int k)
	{
		if (planetId < 0)
		{
			return;
		}
		long key = EffKey(planetId, entityId);
		effMap[key] = k;
		if (!energyMap.TryGetValue(key, out var value))
		{
			energyMap[key] = k;
			return;
		}
		int num = k - value;
		if (num != 0)
		{
			int num2 = num / 4;
			if (num2 == 0)
			{
				num2 = ((num > 0) ? 1 : (-1));
			}
			energyMap[key] = value + num2;
		}
	}

	public static int GetEnergyK(int planetId, int entityId)
	{
		if (!energyMap.TryGetValue(EffKey(planetId, entityId), out var value))
		{
			return 1;
		}
		return value;
	}

	public static void ClearAll()
	{
		setMap.Clear();
		effMap.Clear();
		energyMap.Clear();
		satMap.Clear();
		bufferMap.Clear();
		bufCapMap.Clear();
		energyCapMap.Clear();
		healthyCountMap.Clear();
		capMap.Clear();
		energyFrame.Clear();
		wirelessMap.Clear();
	}

	public static void Export(BinaryWriter w)
	{
		w.Write(3);
		w.Write(setMap.Count);
		foreach (KeyValuePair<int, Dictionary<int, int>> item in setMap)
		{
			w.Write(item.Key);
			w.Write(item.Value.Count);
			foreach (KeyValuePair<int, int> item2 in item.Value)
			{
				w.Write(item2.Key);
				w.Write(item2.Value);
			}
		}
		w.Write(wirelessMap.Count);
		foreach (int item3 in wirelessMap)
		{
			w.Write(item3);
		}
	}

	public static void Import(BinaryReader r)
	{
		ClearAll();
		int num = r.ReadInt32();
		int num2 = r.ReadInt32();
		for (int i = 0; i < num2; i++)
		{
			int key = r.ReadInt32();
			int num3 = r.ReadInt32();
			Dictionary<int, int> dictionary = new Dictionary<int, int>(num3);
			for (int j = 0; j < num3; j++)
			{
				int key2 = r.ReadInt32();
				int num4 = r.ReadInt32();
				if (num4 > 1)
				{
					dictionary[key2] = num4;
					capMap[key2] = num4;
				}
			}
			if (dictionary.Count > 0)
			{
				setMap[key] = dictionary;
			}
		}
		if (num >= 3)
		{
			int num5 = r.ReadInt32();
			for (int k = 0; k < num5; k++)
			{
				wirelessMap.Add(r.ReadInt32());
			}
		}
		else
		{
			if (num < 2)
			{
				return;
			}
			int num6 = r.ReadInt32();
			for (int l = 0; l < num6; l++)
			{
				int item = r.ReadInt32();
				int num7 = r.ReadInt32();
				for (int m = 0; m < num7; m++)
				{
					r.ReadInt32();
				}
				wirelessMap.Add(item);
			}
		}
	}
}
[BepInPlugin("com.mandao.CondensedProduction", "CondensedProduction", "1.4.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin, IModCanSave
{
	public const string GUID = "com.mandao.CondensedProduction";

	public const string NAME = "CondensedProduction";

	public const string VERSION = "1.4.0";

	public static ConfigEntry<int> MaxMultiplierEntry;

	public static ConfigEntry<bool> ScaleOutputStorageEntry;

	public static ConfigEntry<int> InputBufferMultiplierEntry;

	public static ConfigEntry<int> EnergyBufferSecondsEntry;

	public static ConfigEntry<int> ContainerMultiplierEntry;

	public static ConfigEntry<bool> GlobalSupplyEnabledEntry;

	public static ConfigEntry<int> GlobalSupplyIntervalEntry;

	public static ConfigEntry<bool> GlobalSupplyCollectEntry;

	public static ConfigEntry<bool> BeltStackEnabledEntry;

	public static ConfigEntry<int> BeltStackMaxEntry;

	public static ConfigEntry<bool> KeepVisualStackEntry;

	private void Awake()
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Expected O, but got Unknown
		//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: Unknown result type (might be due to invalid IL or missing references)
		//IL_0217: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_0287: Unknown result type (might be due to invalid IL or missing references)
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
		MaxMultiplierEntry = ((BaseUnityPlugin)this).Config.Bind<int>("通用", "MaxMultiplier", 1000, "单个建筑可设定的倍率上限(1~100000,0 表示不限制)。实际运行倍率始终受输入物品量与引擎速度上限自适应。");
		ScaleOutputStorageEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("通用", "ScaleOutputStorage", true, "是否按设定倍率放大熔炉/装配机的产物缓存上限(熔炉100→100×N等)。");
		EnergyBufferSecondsEntry = ((BaseUnityPlugin)this).Config.Bind<int>("电力", "EnergyBufferSeconds", 10, new ConfigDescription("每栋倍率建筑的内部储能时长(秒,按满倍率功耗计)。电网按平均功率平稳充电,瞬时消耗从储能扣除;电网不足时靠储能维持运行并逐步降速,而不是功率瞬间跳变。0 = 关闭储能", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 120), Array.Empty<object>()));
		InputBufferMultiplierEntry = ((BaseUnityPlugin)this).Config.Bind<int>("通用", "InputBufferMultiplier", 3, "原料缓存系数:缓存上限 = 原版上限 × 设定倍率 × 该值。越大越能囤料,供料波动时不易停机。");
		ContainerMultiplierEntry = ((BaseUnityPlugin)this).Config.Bind<int>("通用", "ContainerMultiplier", 100, "容器每格上限倍数:物流站、储物箱、罐体、玩家背包的每格容量 = 原版 × 该值。");
		GlobalSupplyEnabledEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("无线供料", "Enabled", true, "启用无线供料:开启了「无线」的生产建筑,由本行星的原版行星物流站/星际物流站直接供料,无需分拣器。");
		GlobalSupplyIntervalEntry = ((BaseUnityPlugin)this).Config.Bind<int>("无线供料", "IntervalTicks", 30, "无线供料执行间隔(游戏刻)。越小补料越及时,CPU 开销越大。");
		GlobalSupplyCollectEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("无线供料", "Collect", true, "是否把开启了「无线」的建筑的产出直接回收到本行星物流站库存。");
		BeltStackEnabledEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("传送带堆叠", "Enabled", true, "启用传送带堆叠扩容(堆叠器、白爪、物流站出口)。");
		BeltStackMaxEntry = ((BaseUnityPlugin)this).Config.Bind<int>("传送带堆叠", "MaxStack", 255, "堆叠上限。游戏内堆叠数存为单字节,最大 255(原版 4 层)。");
		KeepVisualStackEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("传送带堆叠", "KeepVisualStack", true, "保持原版渲染:绘制时把堆叠临时压回 4 层,画面与原版一致(仅影响显示)。");
		ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("兼容", "MultiplierMigrated", false, "内部使用:记录已将旧的 100 倍上限迁移为 1000。");
		if (!val.Value)
		{
			if (MaxMultiplierEntry.Value > 0 && MaxMultiplierEntry.Value <= 100)
			{
				MaxMultiplierEntry.Value = 1000;
			}
			val.Value = true;
			((BaseUnityPlugin)this).Config.Save();
		}
		Harmony val2 = new Harmony("com.mandao.CondensedProduction");
		val2.PatchAll(typeof(ParallelTickPatch));
		val2.PatchAll(typeof(FactoryContextPatch));
		val2.PatchAll(typeof(AssemblerBatchPatch));
		val2.PatchAll(typeof(LabBatchPatch));
		val2.PatchAll(typeof(LabResearchPatch));
		val2.PatchAll(typeof(ResearchNeedsPatch));
		val2.PatchAll(typeof(NeedsBufferPatch));
		val2.PatchAll(typeof(PowerSamplePatch));
		val2.PatchAll(typeof(CopySettingPatch));
		val2.PatchAll(typeof(VFInputGuardPatch));
		val2.PatchAll(typeof(StorageCapPatch));
		val2.PatchAll(typeof(StationLimitPatch));
		val2.PatchAll(typeof(BuildingGrabPatch));
		val2.PatchAll(typeof(GlobalSupplyPatch));
		val2.PatchAll(typeof(PilerStackPatch));
		val2.PatchAll(typeof(InserterStackPatch));
		val2.PatchAll(typeof(StationOutputPatch));
		val2.PatchAll(typeof(CargoVisualClampPatch));
		MultiplierUI.Init();
		MultStore.Log = ((BaseUnityPlugin)this).Logger;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"CondensedProduction 1.4.0 loaded.");
	}

	private void OnGUI()
	{
		MultiplierUI.Draw();
	}

	public void Export(BinaryWriter w)
	{
		MultStore.Export(w);
	}

	public void Import(BinaryReader r)
	{
		MultStore.Import(r);
	}

	public void IntoOtherSave()
	{
		MultStore.ClearAll();
	}
}
public static class MultEngine
{
	private static readonly ConcurrentDictionary<int, int> fallbackEff = new ConcurrentDictionary<int, int>();

	[ThreadStatic]
	private static long tlsFrame = long.MinValue;

	[ThreadStatic]
	private static Dictionary<long, int> tlsK;

	private static int GetAsmKConsume(ref AssemblerComponent c)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		int num = ((currentPlanetId >= 0) ? MultStore.GetSet(currentPlanetId, c.entityId) : MultStore.GetCapK(c.entityId));
		if (num <= 1)
		{
			return 1;
		}
		return ResolveDyn(currentPlanetId, c.entityId, num, c.recipeExecuteData.requireCounts, c.served);
	}

	private static int GetLabKConsume(ref LabComponent c)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		int num = ((currentPlanetId >= 0) ? MultStore.GetSet(currentPlanetId, c.entityId) : MultStore.GetCapK(c.entityId));
		if (num <= 1)
		{
			return 1;
		}
		return ResolveDyn(currentPlanetId, c.entityId, num, c.recipeExecuteData.requireCounts, c.served);
	}

	private static int GetAsmKProduce(ref AssemblerComponent c)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		int num = ((currentPlanetId >= 0) ? MultStore.GetSet(currentPlanetId, c.entityId) : MultStore.GetCapK(c.entityId));
		if (num <= 1)
		{
			return 1;
		}
		return ProduceK(currentPlanetId, c.entityId, num);
	}

	private static int GetLabKProduce(ref LabComponent c)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		int num = ((currentPlanetId >= 0) ? MultStore.GetSet(currentPlanetId, c.entityId) : MultStore.GetCapK(c.entityId));
		if (num <= 1)
		{
			return 1;
		}
		return ProduceK(currentPlanetId, c.entityId, num);
	}

	private static int ProduceK(int planet, int entityId, int kSet)
	{
		int value = 0;
		if (planet >= 0)
		{
			value = MultStore.GetEff(planet, entityId);
		}
		else
		{
			fallbackEff.TryGetValue(entityId, out value);
		}
		if (value < 1)
		{
			value = kSet;
		}
		if (value < 1)
		{
			value = 1;
		}
		return value;
	}

	private static int ResolveDyn(int planet, int entityId, int kSet, int[] require, int[] served)
	{
		long gameTick = GameMain.gameTick;
		if (gameTick != tlsFrame || tlsK == null)
		{
			tlsFrame = gameTick;
			if (tlsK == null)
			{
				tlsK = new Dictionary<long, int>();
			}
			else
			{
				tlsK.Clear();
			}
		}
		long key = (long)(planet + 2) * 1000000L + entityId;
		if (tlsK.TryGetValue(key, out var value))
		{
			return value;
		}
		int num = kSet;
		if (require != null && served != null)
		{
			int num2 = ((require.Length < served.Length) ? require.Length : served.Length);
			for (int i = 0; i < num2; i++)
			{
				int num3 = require[i];
				if (num3 > 0)
				{
					int num4 = served[i] / num3;
					if (num4 < num)
					{
						num = num4;
					}
				}
			}
		}
		int energyCapK = MultStore.GetEnergyCapK(planet, entityId);
		if (num > energyCapK)
		{
			num = energyCapK;
		}
		if (num < 1)
		{
			num = 1;
		}
		if (num > kSet)
		{
			num = kSet;
		}
		int num5 = ((planet >= 0) ? MultStore.GetEff(planet, entityId) : 0);
		if (num5 > 0)
		{
			int num6 = kSet / 20;
			if (num6 < 1)
			{
				num6 = 1;
			}
			int num7 = num - num5;
			if (num7 > num6)
			{
				num = num5 + num6;
			}
			else if (num7 < -num6)
			{
				num = num5 - num6;
			}
			if (num < 1)
			{
				num = 1;
			}
			if (num > kSet)
			{
				num = kSet;
			}
		}
		tlsK[key] = num;
		MultStore.SetEff(planet, entityId, num);
		fallbackEff[entityId] = num;
		return num;
	}

	private static int GetAsmKNeeds(ref AssemblerComponent c)
	{
		int num = KFor(c.entityId);
		if (num <= 1)
		{
			return 1;
		}
		return (int)Math.Min(2147483647L, (long)num * (long)BufferFactor());
	}

	private static int GetLabKNeeds(ref LabComponent c)
	{
		int num = KFor(c.entityId);
		if (num <= 1)
		{
			return 1;
		}
		return (int)Math.Min(2147483647L, (long)num * (long)BufferFactor());
	}

	private static int GetLabKSet(ref LabComponent c)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		int num = ((currentPlanetId < 0) ? 1 : MultStore.GetSet(currentPlanetId, c.entityId));
		if (num >= 1)
		{
			return num;
		}
		return 1;
	}

	private static int BufferFactor()
	{
		int num = ((Plugin.InputBufferMultiplierEntry == null) ? 3 : Plugin.InputBufferMultiplierEntry.Value);
		if (num >= 1)
		{
			return num;
		}
		return 1;
	}

	internal static int KFor(int entityId)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		if (currentPlanetId < 0)
		{
			return MultStore.GetCapK(entityId);
		}
		return MultStore.GetSet(currentPlanetId, entityId);
	}

	internal static void EnergyPost(FactorySystem fs)
	{
		if (fs == null || fs.factory == null)
		{
			return;
		}
		int planetId = fs.factory.planetId;
		if (!MultStore.PlanetHasSets(planetId))
		{
			return;
		}
		PowerConsumerComponent[] array = ((fs.factory.powerSystem != null) ? fs.factory.powerSystem.consumerPool : null);
		if (array == null)
		{
			return;
		}
		long gameTick = GameMain.gameTick;
		AssemblerComponent[] assemblerPool = fs.assemblerPool;
		if (assemblerPool != null)
		{
			int num = Math.Min(fs.assemblerCursor, assemblerPool.Length);
			for (int i = 1; i < num; i++)
			{
				if (assemblerPool[i].entityId == 0 || !assemblerPool[i].replicating)
				{
					continue;
				}
				int energyK = MultStore.GetEnergyK(planetId, assemblerPool[i].entityId);
				if (energyK > 1)
				{
					int pcId = assemblerPool[i].pcId;
					if (pcId > 0 && pcId < array.Length && MultStore.TryMarkEnergy(planetId, assemblerPool[i].entityId, gameTick))
					{
						AccountEnergy(planetId, assemblerPool[i].entityId, array, pcId, energyK);
						array[pcId].requiredEnergy *= energyK;
					}
				}
			}
		}
		LabComponent[] labPool = fs.labPool;
		if (labPool == null)
		{
			return;
		}
		int num2 = Math.Min(fs.labCursor, labPool.Length);
		for (int j = 1; j < num2; j++)
		{
			if (labPool[j].entityId == 0 || !labPool[j].replicating)
			{
				continue;
			}
			int num3 = (labPool[j].researchMode ? MultStore.GetSet(planetId, labPool[j].entityId) : MultStore.GetEnergyK(planetId, labPool[j].entityId));
			if (num3 <= 1)
			{
				continue;
			}
			int pcId2 = labPool[j].pcId;
			if (pcId2 > 0 && pcId2 < array.Length && MultStore.TryMarkEnergy(planetId, labPool[j].entityId, gameTick))
			{
				if (!labPool[j].researchMode)
				{
					AccountEnergy(planetId, labPool[j].entityId, array, pcId2, num3);
				}
				array[pcId2].requiredEnergy *= num3;
			}
		}
	}

	internal static void EnergyPostAll()
	{
		if (GameMain.data == null)
		{
			return;
		}
		PlanetFactory[] factories = GameMain.data.factories;
		if (factories == null)
		{
			return;
		}
		foreach (PlanetFactory val in factories)
		{
			if (val == null || val.factorySystem == null)
			{
				continue;
			}
			try
			{
				EnergyPost(val.factorySystem);
			}
			catch (Exception ex)
			{
				if (MultStore.Log != null)
				{
					MultStore.Log.LogError((object)("[CP] energy sweep error: " + ex));
				}
			}
		}
	}

	private static int BufferSeconds()
	{
		int num = ((Plugin.EnergyBufferSecondsEntry == null) ? 10 : Plugin.EnergyBufferSecondsEntry.Value);
		if (num >= 0)
		{
			return num;
		}
		return 0;
	}

	private static void AccountEnergy(int planet, int entityId, PowerConsumerComponent[] consumers, int pcId, int kSmooth)
	{
		long num = consumers[pcId].requiredEnergy;
		if (num <= 0)
		{
			num = consumers[pcId].workEnergyPerTick;
		}
		if (num <= 0)
		{
			return;
		}
		int num2 = BufferSeconds() * 60;
		float num3 = MultStore.GetSat(planet, entityId);
		if (num3 > 1f)
		{
			num3 = 1f;
		}
		long num4 = (long)((double)num * (double)kSmooth * (double)num3);
		long num5 = MultStore.GetEff(planet, entityId) * num;
		int set = MultStore.GetSet(planet, entityId);
		long num6 = set * num * num2;
		long num7 = MultStore.ChargeBuffer(planet, entityId, num4 - num5, num6);
		MultStore.SetBufferCap(planet, entityId, num6);
		int num9;
		if (num3 < 0.95f && num2 > 0)
		{
			long num8 = num7 / (num2 * num);
			num9 = kSmooth + (int)num8;
			if (num9 < 1)
			{
				num9 = 1;
			}
			if (num9 > set)
			{
				num9 = set;
			}
			MultStore.ResetHealthyCount(planet, entityId);
		}
		else
		{
			MultStore.TickHealthyCount(planet, entityId);
			int energyCapK = MultStore.GetEnergyCapK(planet, entityId);
			if (energyCapK == int.MaxValue)
			{
				num9 = int.MaxValue;
			}
			else if (MultStore.GetHealthyCount(planet, entityId) < 30 || (num2 > 0 && num6 > 0 && (double)num7 / (double)num6 < 0.2))
			{
				num9 = energyCapK;
			}
			else
			{
				int num10 = set / 360 + 1;
				num9 = energyCapK + num10;
				if (num9 >= set)
				{
					num9 = int.MaxValue;
				}
			}
		}
		MultStore.SetEnergyCapK(planet, entityId, num9);
	}
}
public static class PowerSamplePatch
{
	[HarmonyPatch(typeof(AssemblerComponent), "InternalUpdate")]
	[HarmonyPrefix]
	private static void PreAsm(ref AssemblerComponent __instance, float power)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		if (currentPlanetId >= 0 && MultStore.GetSet(currentPlanetId, __instance.entityId) > 1)
		{
			MultStore.SamplePower(currentPlanetId, __instance.entityId, power);
		}
	}

	[HarmonyPatch(typeof(LabComponent), "InternalUpdateAssemble")]
	[HarmonyPrefix]
	private static void PreLab(ref LabComponent __instance, float power)
	{
		int currentPlanetId = MultStore.CurrentPlanetId;
		if (currentPlanetId >= 0 && MultStore.GetSet(currentPlanetId, __instance.entityId) > 1)
		{
			MultStore.SamplePower(currentPlanetId, __instance.entityId, power);
		}
	}
}
public static class CountMultiplyTranspiler
{
	internal static IEnumerable<CodeInstruction> Apply(IEnumerable<CodeInstruction> instructions, MethodInfo getKConsume, MethodInfo getKProduce)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		Dictionary<int, MethodInfo> tracked = new Dictionary<int, MethodInfo>();
		for (int i = 0; i + 2 < list.Count; i++)
		{
			if (!IsLdfld(list[i], "recipeExecuteData"))
			{
				continue;
			}
			MethodInfo methodInfo = CountFieldGetter(list[i + 1], getKConsume, getKProduce);
			if (methodInfo != null)
			{
				int storeLocalIndex = GetStoreLocalIndex(list[i + 2]);
				if (storeLocalIndex >= 0)
				{
					tracked[storeLocalIndex] = methodInfo;
				}
			}
		}
		for (int j = 0; j < list.Count; j++)
		{
			int loadLocalIndex = GetLoadLocalIndex(list[j]);
			if (loadLocalIndex >= 0 && tracked.TryGetValue(loadLocalIndex, out var getK))
			{
				int k = j + 1;
				bool flag = false;
				for (; k < list.Count && k <= j + 4; k++)
				{
					if (list[k].opcode == OpCodes.Ldelem_I4)
					{
						flag = true;
						break;
					}
					if (!IsIndexPush(list[k]))
					{
						break;
					}
				}
				if (flag)
				{
					for (int t = j; t <= k; t++)
					{
						yield return list[t];
					}
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return new CodeInstruction(OpCodes.Call, (object)getK);
					yield return new CodeInstruction(OpCodes.Mul, (object)null);
					j = k;
					continue;
				}
			}
			yield return list[j];
			getK = null;
		}
	}

	private static bool IsLdfld(CodeInstruction ci, string name)
	{
		if (ci.opcode == OpCodes.Ldfld && ci.operand is FieldInfo fieldInfo)
		{
			return fieldInfo.Name == name;
		}
		return false;
	}

	private static MethodInfo CountFieldGetter(CodeInstruction ci, MethodInfo consume, MethodInfo produce)
	{
		if (ci.opcode == OpCodes.Ldfld && ci.operand is FieldInfo fieldInfo && fieldInfo.DeclaringType != null && fieldInfo.DeclaringType.Name == "RecipeExecuteData")
		{
			if (fieldInfo.Name == "requireCounts")
			{
				return consume;
			}
			if (fieldInfo.Name == "productCounts")
			{
				return produce;
			}
		}
		return null;
	}

	private static bool IsIndexPush(CodeInstruction ci)
	{
		if (!(ci.opcode == OpCodes.Ldc_I4) && !(ci.opcode == OpCodes.Ldc_I4_S) && !(ci.opcode == OpCodes.Ldc_I4_0) && !(ci.opcode == OpCodes.Ldc_I4_1) && !(ci.opcode == OpCodes.Ldc_I4_2) && !(ci.opcode == OpCodes.Ldc_I4_3))
		{
			return GetLoadLocalIndex(ci) >= 0;
		}
		return true;
	}

	internal static int GetStoreLocalIndex(CodeInstruction ci)
	{
		if (ci.opcode == OpCodes.Stloc_0)
		{
			return 0;
		}
		if (ci.opcode == OpCodes.Stloc_1)
		{
			return 1;
		}
		if (ci.opcode == OpCodes.Stloc_2)
		{
			return 2;
		}
		if (ci.opcode == OpCodes.Stloc_3)
		{
			return 3;
		}
		if (ci.opcode == OpCodes.Stloc_S || ci.opcode == OpCodes.Stloc)
		{
			return LocalIndex(ci.operand);
		}
		return -1;
	}

	internal static int GetLoadLocalIndex(CodeInstruction ci)
	{
		if (ci.opcode == OpCodes.Ldloc_0)
		{
			return 0;
		}
		if (ci.opcode == OpCodes.Ldloc_1)
		{
			return 1;
		}
		if (ci.opcode == OpCodes.Ldloc_2)
		{
			return 2;
		}
		if (ci.opcode == OpCodes.Ldloc_3)
		{
			return 3;
		}
		if (ci.opcode == OpCodes.Ldloc_S || ci.opcode == OpCodes.Ldloc)
		{
			return LocalIndex(ci.operand);
		}
		return -1;
	}

	private static int LocalIndex(object operand)
	{
		if (operand is LocalBuilder localBuilder)
		{
			return localBuilder.LocalIndex;
		}
		if (operand is int)
		{
			return (int)operand;
		}
		if (operand is short)
		{
			return (short)operand;
		}
		if (operand is byte)
		{
			return (byte)operand;
		}
		return -1;
	}

	internal static CodeInstruction LoadLocal(int index)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected O, but got Unknown
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		return (CodeInstruction)(index switch
		{
			0 => (object)new CodeInstruction(OpCodes.Ldloc_0, (object)null), 
			1 => (object)new CodeInstruction(OpCodes.Ldloc_1, (object)null), 
			2 => (object)new CodeInstruction(OpCodes.Ldloc_2, (object)null), 
			3 => (object)new CodeInstruction(OpCodes.Ldloc_3, (object)null), 
			_ => (object)new CodeInstruction(OpCodes.Ldloc_S, (object)index), 
		});
	}
}
public static class AssemblerBatchPatch
{
	private static readonly MethodInfo GetKConsume = typeof(MultEngine).GetMethod("GetAsmKConsume", BindingFlags.Static | BindingFlags.NonPublic);

	private static readonly MethodInfo GetKProduce = typeof(MultEngine).GetMethod("GetAsmKProduce", BindingFlags.Static | BindingFlags.NonPublic);

	[HarmonyPatch(typeof(AssemblerComponent), "InternalUpdate")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		foreach (CodeInstruction item in CountMultiplyTranspiler.Apply(instructions, GetKConsume, GetKProduce))
		{
			if (item.opcode == OpCodes.Ldc_I4_S && item.operand is sbyte b && b == 100)
			{
				yield return item;
				yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
				yield return new CodeInstruction(OpCodes.Call, (object)GetKProduce);
				yield return new CodeInstruction(OpCodes.Mul, (object)null);
			}
			else
			{
				yield return item;
			}
		}
	}
}
public static class NeedsBufferPatch
{
	private static readonly MethodInfo GetAsmK = typeof(MultEngine).GetMethod("GetAsmKNeeds", BindingFlags.Static | BindingFlags.NonPublic);

	private static readonly MethodInfo GetLabK = typeof(MultEngine).GetMethod("GetLabKNeeds", BindingFlags.Static | BindingFlags.NonPublic);

	[HarmonyPatch(typeof(AssemblerComponent), "UpdateNeeds")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> AsmNeeds(IEnumerable<CodeInstruction> instructions)
	{
		return CountMultiplyTranspiler.Apply(instructions, GetAsmK, GetAsmK);
	}

	[HarmonyPatch(typeof(LabComponent), "UpdateNeedsAssemble")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> LabNeeds(IEnumerable<CodeInstruction> instructions)
	{
		return CountMultiplyTranspiler.Apply(instructions, GetLabK, GetLabK);
	}
}
public static class LabBatchPatch
{
	private static readonly MethodInfo GetKConsume = typeof(MultEngine).GetMethod("GetLabKConsume", BindingFlags.Static | BindingFlags.NonPublic);

	private static readonly MethodInfo GetKProduce = typeof(MultEngine).GetMethod("GetLabKProduce", BindingFlags.Static | BindingFlags.NonPublic);

	[HarmonyPatch(typeof(LabComponent), "InternalUpdateAssemble")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(CountMultiplyTranspiler.Apply(instructions, GetKConsume, GetKProduce));
		for (int i = 0; i < list.Count; i++)
		{
			yield return list[i];
			if (list[i].opcode == OpCodes.Ldc_I4_S && list[i].operand is sbyte b && b == 10 && i + 2 < list.Count && list[i + 1].opcode == OpCodes.Ldarg_0 && list[i + 2].opcode == OpCodes.Ldfld && list[i + 2].operand is FieldInfo { Name: "speedOverride" })
			{
				yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
				yield return new CodeInstruction(OpCodes.Call, (object)GetKProduce);
				yield return new CodeInstruction(OpCodes.Mul, (object)null);
			}
		}
	}
}
public static class LabResearchPatch
{
	[ThreadStatic]
	private static bool inLoop;

	[HarmonyPatch(typeof(LabComponent), "InternalUpdateResearch")]
	[HarmonyPrefix]
	private static bool Prefix(ref LabComponent __instance, float power, float research_speed, int[] consumeRegister, ref TechState ts, ref int techHashedThisFrame, ref long uMatrixPoint, ref long hashRegister, ref uint __result)
	{
		if (inLoop)
		{
			return true;
		}
		int currentPlanetId = MultStore.CurrentPlanetId;
		int num = ((currentPlanetId < 0) ? 1 : MultStore.GetSet(currentPlanetId, __instance.entityId));
		if (num <= 1)
		{
			return true;
		}
		inLoop = true;
		try
		{
			__result = ((LabComponent)(ref __instance)).InternalUpdateResearch(power, research_speed, consumeRegister, ref ts, ref techHashedThisFrame, ref uMatrixPoint, ref hashRegister);
			for (int i = 1; i < num; i++)
			{
				if (!__instance.replicating)
				{
					break;
				}
				((LabComponent)(ref __instance)).InternalUpdateResearch(power, research_speed, consumeRegister, ref ts, ref techHashedThisFrame, ref uMatrixPoint, ref hashRegister);
			}
		}
		finally
		{
			inLoop = false;
		}
		return false;
	}
}
public static class ResearchNeedsPatch
{
	private static readonly MethodInfo GetK = typeof(MultEngine).GetMethod("GetLabKSet", BindingFlags.Static | BindingFlags.NonPublic);

	[HarmonyPatch(typeof(LabComponent), "UpdateNeedsResearch")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		foreach (CodeInstruction ci in instructions)
		{
			yield return ci;
			if (ci.opcode == OpCodes.Ldc_I4 && ci.operand is int v && v == 36000)
			{
				yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
				yield return new CodeInstruction(OpCodes.Call, (object)GetK);
				yield return new CodeInstruction(OpCodes.Mul, (object)null);
			}
		}
	}
}
public static class ParallelContextTranspiler
{
	private static readonly MethodInfo SetCtx = typeof(ParallelContextTranspiler).GetMethod("SetContext", BindingFlags.Static | BindingFlags.NonPublic);

	private static void SetContext(PlanetFactory factory)
	{
		MultStore.CurrentPlanetId = ((factory != null) ? factory.planetId : (-1));
	}

	internal static IEnumerable<CodeInstruction> Apply(IEnumerable<CodeInstruction> instructions)
	{
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Expected O, but got Unknown
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		for (int i = 0; i + 2 < list.Count; i++)
		{
			if (!(list[i].opcode == OpCodes.Ldfld) || !(list[i].operand is FieldInfo { Name: "factories" } fieldInfo) || !(fieldInfo.DeclaringType.Name == "GameLogic"))
			{
				continue;
			}
			for (int j = i + 1; j < list.Count && j <= i + 12; j++)
			{
				if (list[j].opcode == OpCodes.Ldelem_Ref && CountMultiplyTranspiler.GetStoreLocalIndex(list[j + 1]) >= 0)
				{
					List<CodeInstruction> collection = new List<CodeInstruction>
					{
						new CodeInstruction(OpCodes.Dup, (object)null),
						new CodeInstruction(OpCodes.Call, (object)SetCtx)
					};
					list.InsertRange(j + 1, collection);
					return list;
				}
			}
		}
		return list;
	}
}
public static class ParallelTickPatch
{
	[HarmonyPatch(typeof(GameLogic), "_assembler_parallel")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> AsmTranspiler(IEnumerable<CodeInstruction> instructions)
	{
		return ParallelContextTranspiler.Apply(instructions);
	}

	[HarmonyPatch(typeof(GameLogic), "_lab_produce_parallel")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> LabTranspiler(IEnumerable<CodeInstruction> instructions)
	{
		return ParallelContextTranspiler.Apply(instructions);
	}

	[HarmonyPatch(typeof(GameLogic), "_assembler_parallel")]
	[HarmonyPostfix]
	private static void PostAsm(int threadOrdinal)
	{
		MultStore.CurrentPlanetId = -1;
		if (threadOrdinal == 0)
		{
			MultEngine.EnergyPostAll();
		}
	}

	[HarmonyPatch(typeof(GameLogic), "_lab_produce_parallel")]
	[HarmonyPostfix]
	private static void PostLab(int threadOrdinal)
	{
		MultStore.CurrentPlanetId = -1;
	}
}
public static class FactoryContextPatch
{
	[HarmonyPrefix]
	[HarmonyPatch(typeof(FactorySystem), "GameTick")]
	private static void PreGameTick(FactorySystem __instance)
	{
		MultStore.CurrentPlanetId = ((__instance.factory != null) ? __instance.factory.planetId : (-1));
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(FactorySystem), "GameTick")]
	private static void PostGameTick(FactorySystem __instance)
	{
		MultEngine.EnergyPost(__instance);
		MultStore.CurrentPlanetId = -1;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(FactorySystem), "GameTickLabProduceMode")]
	private static void PreLabProduce(FactorySystem __instance)
	{
		MultStore.CurrentPlanetId = ((__instance.factory != null) ? __instance.factory.planetId : (-1));
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(FactorySystem), "GameTickLabProduceMode")]
	private static void PostLabProduce()
	{
		MultStore.CurrentPlanetId = -1;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(FactorySystem), "GameTickLabResearchMode")]
	private static void PreLabResearch(FactorySystem __instance)
	{
		MultStore.CurrentPlanetId = ((__instance.factory != null) ? __instance.factory.planetId : (-1));
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(FactorySystem), "GameTickLabResearchMode")]
	private static void PostLabResearch()
	{
		MultStore.CurrentPlanetId = -1;
	}
}
public static class CopySettingPatch
{
	[HarmonyPatch(typeof(BuildingParameters), "CopyFromFactoryObject")]
	[HarmonyPostfix]
	private static void BpCopy(int objectId, PlanetFactory factory)
	{
		if (factory != null && objectId > 0)
		{
			MultStore.ClipboardK = MultStore.GetSet(factory.planetId, objectId);
			MultStore.ClipboardWireless = MultStore.GetWireless(factory.planetId, objectId);
		}
	}

	[HarmonyPatch(typeof(BuildingParameters), "ApplyPrebuildParametersToEntity")]
	[HarmonyPostfix]
	private static void BpPaste(int entityId, PlanetFactory factory)
	{
		if (factory != null && entityId > 0)
		{
			MultStore.SetMult(factory.planetId, entityId, MultStore.ClipboardK);
			MultStore.SetWireless(factory.planetId, entityId, MultStore.ClipboardWireless);
		}
	}

	[HarmonyPatch(typeof(PlanetFactory), "OnCopyBuildingSetting")]
	[HarmonyPostfix]
	private static void OnCopy(PlanetFactory __instance, int entityId)
	{
		MultStore.ClipboardK = MultStore.GetSet(__instance.planetId, entityId);
		MultStore.ClipboardWireless = MultStore.GetWireless(__instance.planetId, entityId);
	}

	[HarmonyPatch(typeof(PlanetFactory), "OnPasteBuildingSetting")]
	[HarmonyPostfix]
	private static void OnPaste(PlanetFactory __instance, int entityId)
	{
		MultStore.SetMult(__instance.planetId, entityId, MultStore.ClipboardK);
		MultStore.SetWireless(__instance.planetId, entityId, MultStore.ClipboardWireless);
	}
}
public static class StorageCapPatch
{
	private static bool tableScaled;

	private static bool prefabScaled;

	private static long lastMaintain = long.MinValue;

	private static int Factor
	{
		get
		{
			int num = ((Plugin.ContainerMultiplierEntry == null) ? 100 : Plugin.ContainerMultiplierEntry.Value);
			if (num >= 1)
			{
				return num;
			}
			return 1;
		}
	}

	public static void Maintain()
	{
		if (Factor > 1)
		{
			long gameTick = GameMain.gameTick;
			if (gameTick < lastMaintain)
			{
				lastMaintain = long.MinValue;
			}
			if (lastMaintain == long.MinValue || gameTick - lastMaintain >= 30)
			{
				lastMaintain = gameTick;
				EnsureStackTable();
				EnsureStationPrefabs();
			}
		}
	}

	private static void EnsureStationPrefabs()
	{
		if (prefabScaled || (Object)(object)LDB.items == (Object)null || ((ProtoSet<ItemProto>)(object)LDB.items).dataArray == null)
		{
			return;
		}
		int factor = Factor;
		HashSet<PrefabDesc> seen = new HashSet<PrefabDesc>();
		ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
		for (int i = 0; i < dataArray.Length; i++)
		{
			ScaleStationMax(dataArray[i].prefabDesc, factor, seen);
		}
		if ((Object)(object)LDB.models != (Object)null && ((ProtoSet<ModelProto>)(object)LDB.models).dataArray != null)
		{
			ModelProto[] dataArray2 = ((ProtoSet<ModelProto>)(object)LDB.models).dataArray;
			for (int j = 0; j < dataArray2.Length; j++)
			{
				ScaleStationMax(dataArray2[j].prefabDesc, factor, seen);
			}
		}
		prefabScaled = true;
	}

	private static void ScaleStationMax(PrefabDesc pd, int f, HashSet<PrefabDesc> seen)
	{
		if (pd != null && pd.stationMaxItemCount > 0 && seen.Add(pd))
		{
			long val = (long)pd.stationMaxItemCount * (long)f;
			pd.stationMaxItemCount = (int)Math.Min(2147483647L, val);
		}
	}

	private static void EnsureStackTable()
	{
		if (tableScaled || !StorageComponent.staticLoaded)
		{
			return;
		}
		int[] itemStackCount = StorageComponent.itemStackCount;
		if (itemStackCount != null)
		{
			int factor = Factor;
			for (int i = 0; i < itemStackCount.Length; i++)
			{
				long val = (long)itemStackCount[i] * (long)factor;
				itemStackCount[i] = (int)Math.Min(2147483647L, val);
			}
			tableScaled = true;
		}
	}

	[HarmonyPatch(typeof(StationComponent), "Init")]
	[HarmonyPrefix]
	private static void StationInitPrefix()
	{
		EnsureStationPrefabs();
	}

	[HarmonyPatch(typeof(StorageComponent), "LoadStatic")]
	[HarmonyPostfix]
	private static void LoadStaticPostfix()
	{
		EnsureStackTable();
	}

	[HarmonyPatch(typeof(StorageComponent), "Import")]
	[HarmonyPostfix]
	private static void ImportPostfix(StorageComponent __instance)
	{
		if (Factor <= 1)
		{
			return;
		}
		EnsureStackTable();
		GRID[] grids = __instance.grids;
		int[] itemStackCount = StorageComponent.itemStackCount;
		if (grids == null || itemStackCount == null)
		{
			return;
		}
		for (int i = 0; i < grids.Length; i++)
		{
			int filter = grids[i].filter;
			if (filter > 0 && filter < itemStackCount.Length)
			{
				grids[i].stackSize = itemStackCount[filter];
			}
		}
	}
}
[HarmonyPatch]
public static class StationLimitPatch
{
	private static int requested;

	private static int requestedItem;

	[HarmonyPatch(typeof(PlanetTransport), "SetStationStorage")]
	[HarmonyPrefix]
	private static void PreSet(int itemId, int itemCountMax)
	{
		requested = itemCountMax;
		requestedItem = itemId;
	}

	[HarmonyPatch(typeof(PlanetTransport), "SetStationStorage")]
	[HarmonyPostfix]
	private static void PostSet(PlanetTransport __instance, int stationId, int storageIdx, int itemId)
	{
		if (itemId <= 0 || itemId != requestedItem)
		{
			return;
		}
		StationComponent[] stationPool = __instance.stationPool;
		if (stationPool == null)
		{
			return;
		}
		foreach (StationComponent val in stationPool)
		{
			if (val != null && val.id == stationId && val.storage != null)
			{
				if (storageIdx >= 0 && storageIdx < val.storage.Length && val.storage[storageIdx].itemId == itemId && requested > val.storage[storageIdx].max)
				{
					val.storage[storageIdx].max = requested;
				}
				break;
			}
		}
	}
}