Decompiled source of TowerFactory v1.1.0

TowerFactory.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
internal static class PluginInfo
{
	public const string PLUGIN_GUID = "com.yummi.dsp.towerfactory";

	public const string PLUGIN_NAME = "TowerFactory";

	public const string PLUGIN_VERSION = "1.0.0";
}
namespace TowerFactory
{
	internal class ItemAmounts
	{
		public readonly int[] ids;

		public readonly int[] counts;

		public ItemAmounts(IDictionary<int, int> amounts)
		{
			ids = amounts.Keys.ToArray();
			counts = ids.Select((int id) => amounts[id]).ToArray();
		}

		public static ItemAmounts Of(int[] ids, int[] counts)
		{
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			for (int i = 0; i < ids.Length; i++)
			{
				dictionary.TryGetValue(ids[i], out var value);
				dictionary[ids[i]] = value + counts[i];
			}
			return new ItemAmounts(dictionary);
		}

		public void Register(int[] register, int times)
		{
			lock (register)
			{
				for (int i = 0; i < ids.Length; i++)
				{
					register[ids[i]] += counts[i] * times;
				}
			}
		}
	}
	internal class PseudoRecipe
	{
		public string name;

		public ERecipeType buildingType;

		public int[] ingredients;

		public int[] ingredientCounts;

		public Dictionary<int, int> outputs;

		public ItemAmounts produced;

		public ItemAmounts consumed;

		public int ticks;
	}
	internal static class PseudoRecipes
	{
		private const int Coal = 1006;

		private const int CrudeOil = 1007;

		private const int FireIce = 1011;

		private const int OpticalGrating = 1014;

		private const int Graphite = 1109;

		private const int RefinedOil = 1114;

		private const int TitaniumCrystal = 1118;

		private const int Hydrogen = 1120;

		private const int Graphene = 1123;

		private const int Casimir = 1126;

		private const int EnergyMatrix = 6002;

		private static readonly Lazy<List<PseudoRecipe>> all = new Lazy<List<PseudoRecipe>>(Build);

		public static List<PseudoRecipe> All => all.Value;

		private static List<PseudoRecipe> Build()
		{
			List<PseudoRecipe> list = new List<PseudoRecipe>();
			RecipeProto item = Find((ERecipeType)3, new int[1] { 1007 }, 1114);
			RecipeProto item2 = Find((ERecipeType)3, new int[2] { 1114, 1120 }, 1109);
			RecipeProto item3 = Find((ERecipeType)3, new int[3] { 1114, 1120, 1006 }, 1114);
			RecipeProto item4 = Find((ERecipeType)15, new int[2] { 1109, 1120 }, 6002);
			RecipeProto grapheneRecipe = Find((ERecipeType)2, new int[1] { 1011 }, 1123);
			RecipeProto target = Find((ERecipeType)4, new int[3] { 1118, 1123, 1120 }, 1126);
			RecipeProto target2 = Find((ERecipeType)4, new int[3] { 1014, 1123, 1120 }, 1126);
			Add(list, "能量矩阵(原油+石墨)", (ERecipeType)3, new int[2] { 1007, 1109 }, (item, 2), (item2, 4), (item4, 3));
			Add(list, "X 射线裂解(净)", (ERecipeType)3, new int[1] { 1114 }, (item2, 1));
			Add(list, "精炼油(原油+煤)", (ERecipeType)3, new int[2] { 1007, 1006 }, (item, 1), (item3, 1));
			AddGrapheneSwap(list, "卡西米尔晶体(可燃冰)", target, grapheneRecipe);
			AddGrapheneSwap(list, "卡西米尔晶体(光栅石+可燃冰)", target2, grapheneRecipe);
			return list;
		}

		private static void AddGrapheneSwap(List<PseudoRecipe> list, string name, RecipeProto target, RecipeProto grapheneRecipe)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			if (target == null || grapheneRecipe == null)
			{
				TowerFactory.Log.LogWarning((object)("伪配方 " + name + " 未启用:找不到对应的真实配方"));
				return;
			}
			int num = target.ItemCounts[Array.IndexOf(target.Items, 1123)];
			int num2 = grapheneRecipe.ResultCounts[Array.IndexOf(grapheneRecipe.Results, 1123)];
			int num3 = num / Gcd(num, num2) * num2;
			int[] ingredientOrder = target.Items.Select((int id) => (id != 1123) ? id : 1011).ToArray();
			Add(list, name, target.Type, ingredientOrder, (grapheneRecipe, num3 / num2), (target, num3 / num));
		}

		private static void Add(List<PseudoRecipe> list, string name, ERecipeType buildingType, int[] ingredientOrder, params (RecipeProto recipe, int times)[] steps)
		{
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			if (steps.Any(((RecipeProto recipe, int times) step) => step.recipe == null))
			{
				TowerFactory.Log.LogWarning((object)("伪配方 " + name + " 未启用:找不到对应的真实配方"));
				return;
			}
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			Dictionary<int, int> dictionary2 = new Dictionary<int, int>();
			int num = 0;
			for (int num2 = 0; num2 < steps.Length; num2++)
			{
				var (val, num3) = steps[num2];
				Accumulate(dictionary, val.Results, val.ResultCounts, num3);
				Accumulate(dictionary2, val.Items, val.ItemCounts, num3);
				num += val.TimeSpend * num3;
			}
			Dictionary<int, int> net = new Dictionary<int, int>(dictionary);
			foreach (KeyValuePair<int, int> item in dictionary2)
			{
				net.TryGetValue(item.Key, out var value);
				net[item.Key] = value - item.Value;
			}
			List<int> list2 = (from pair in net
				where pair.Value < 0
				select pair.Key).ToList();
			if (list2.Count != ingredientOrder.Length || !ingredientOrder.All(list2.Contains))
			{
				string text = string.Join("、", list2.Select((int id) => $"{id}×{-net[id]}"));
				TowerFactory.Log.LogWarning((object)("伪配方 " + name + " 未启用:按真实配方算出的净原料为 " + text + ",与设计不符"));
				return;
			}
			PseudoRecipe pseudo = new PseudoRecipe
			{
				name = name,
				buildingType = buildingType,
				ingredients = ingredientOrder,
				ingredientCounts = ingredientOrder.Select((int id) => -net[id]).ToArray(),
				outputs = net.Where((KeyValuePair<int, int> pair) => pair.Value > 0).ToDictionary((KeyValuePair<int, int> pair) => pair.Key, (KeyValuePair<int, int> pair) => pair.Value),
				produced = new ItemAmounts(dictionary),
				consumed = new ItemAmounts(dictionary2),
				ticks = Math.Max(num, 1)
			};
			list.Add(pseudo);
			string text2 = string.Join(" + ", pseudo.ingredients.Select((int id, int i) => $"{pseudo.ingredientCounts[i]} {ItemName(id)}"));
			string text3 = string.Join(" + ", pseudo.outputs.Select((KeyValuePair<int, int> pair) => $"{pair.Value} {ItemName(pair.Key)}"));
			TowerFactory.Log.LogInfo((object)$"伪配方 {name}:{text2} → {text3},{(double)num / 60.0:0.##} 秒");
		}

		private static void Accumulate(Dictionary<int, int> target, int[] ids, int[] counts, int times)
		{
			for (int i = 0; i < ids.Length; i++)
			{
				target.TryGetValue(ids[i], out var value);
				target[ids[i]] = value + counts[i] * times;
			}
		}

		private static RecipeProto Find(ERecipeType type, int[] items, int result)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto recipe in dataArray)
			{
				if (recipe != null && recipe.Type == type && Array.IndexOf(recipe.Results, result) >= 0 && recipe.Items.Length == items.Length && items.All((int id) => Array.IndexOf(recipe.Items, id) >= 0))
				{
					return recipe;
				}
			}
			return null;
		}

		private static int Gcd(int a, int b)
		{
			while (b != 0)
			{
				int num = b;
				b = a % b;
				a = num;
			}
			return a;
		}

		private static string ItemName(int itemId)
		{
			ItemProto obj = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
			return ((obj != null) ? ((Proto)obj).name : null) ?? itemId.ToString();
		}
	}
	[HarmonyPatch]
	public class StationProductionPatch
	{
		private enum PlanKind
		{
			Recipe,
			Miner,
			Gamma
		}

		private class Plan
		{
			public PlanKind kind;

			public string description;

			public int[] items;

			public int[] itemCounts;

			public int resultId;

			public int resultCount;

			public double recipeTicks;

			public ItemAmounts statProduced;

			public ItemAmounts statConsumed;

			public int[] outputSlots;

			public int outputMultiplier;

			public double minerTicksPerItem;

			public double photonTicks;

			public long photonHeat;
		}

		private class State
		{
			public int[] signature = Array.Empty<int>();

			public Plan plan;

			public int stableTicks;

			public bool active;

			public double progress;

			public long energyBuffer;
		}

		private const int ActivateDelayTicks = 60;

		private const int CycleDivisor = 10;

		private const int FirstIngredientSlot = 2;

		private const string SlotsNotEnoughMessage = "原料格子不够,请加装运输塔扩容mod";

		private const double VeinMinerTicksPerItem = 120.0;

		private const double OilExtractorTicksPerItem = 60.0;

		private const int AdvancedMinerMultiplier = 3;

		private const double FractionatorTicksPerItem = 300.0;

		private const float GammaPhotonModeMultiplier = 8f;

		private const float GammaFullWarmupMultiplier = 2.5f;

		private const float GammaFullWarmupLossFactor = 0.6f;

		private static readonly ConditionalWeakTable<StationComponent, State> states = new ConditionalWeakTable<StationComponent, State>();

		internal static string Tr(string zh, string en)
		{
			if (!Localization.isZHCN)
			{
				return en;
			}
			return zh;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlanetTransport), "GameTick")]
		public static void PlanetTransport_GameTick_Postfix(PlanetTransport __instance)
		{
			PlanetFactory factory = __instance.factory;
			FactoryProductionStat val = GameMain.statistics.production.factoryStatPool[factory.index];
			StationComponent[] stationPool = __instance.stationPool;
			for (int i = 1; i < __instance.stationCursor; i++)
			{
				StationComponent val2 = stationPool[i];
				if (val2 != null && val2.id == i && !val2.isCollector && !val2.isVeinCollector)
				{
					Tick(factory, val2, val.productRegister, val.consumeRegister);
				}
			}
		}

		private static void Tick(PlanetFactory factory, StationComponent station, int[] productRegister, int[] consumeRegister)
		{
			StationStore[] storage = station.storage;
			if (storage == null)
			{
				return;
			}
			State orCreateValue = states.GetOrCreateValue(station);
			if (!SignatureEquals(orCreateValue.signature, storage))
			{
				Plan plan = orCreateValue.plan;
				orCreateValue.signature = BuildSignature(storage);
				orCreateValue.plan = MatchPlan(factory, station, out var reason);
				orCreateValue.stableTicks = 0;
				orCreateValue.active = false;
				orCreateValue.progress = 0.0;
				orCreateValue.energyBuffer = 0L;
				LogMatch(factory, station, plan, orCreateValue.plan, reason);
			}
			Plan plan2 = orCreateValue.plan;
			if (plan2 == null)
			{
				return;
			}
			if (!orCreateValue.active)
			{
				if (++orCreateValue.stableTicks < 60)
				{
					return;
				}
				orCreateValue.active = true;
				TowerFactory.Log.LogInfo((object)(StationLabel(factory, station) + " 塔厂开始生产:" + plan2.description));
			}
			int count = storage[0].count;
			if (count <= 0)
			{
				return;
			}
			int batch = (count + 10 - 1) / 10;
			double period = GetPeriod(plan2);
			if (period <= 0.0)
			{
				return;
			}
			if (plan2.kind == PlanKind.Gamma)
			{
				DrawGammaEnergy(factory, orCreateValue, plan2, batch);
			}
			orCreateValue.progress += 10.0;
			while (orCreateValue.progress >= period)
			{
				orCreateValue.progress -= period;
				switch (plan2.kind)
				{
				case PlanKind.Recipe:
					CraftRecipe(station, plan2, batch, productRegister, consumeRegister);
					break;
				case PlanKind.Miner:
					CraftMiner(station, plan2, batch, productRegister);
					break;
				case PlanKind.Gamma:
					CraftGamma(station, orCreateValue, plan2, batch, productRegister);
					break;
				}
			}
		}

		private static double GetPeriod(Plan plan)
		{
			switch (plan.kind)
			{
			case PlanKind.Recipe:
				return plan.recipeTicks;
			case PlanKind.Miner:
			{
				float miningSpeedScale = GameMain.history.miningSpeedScale;
				if (!(miningSpeedScale > 0f))
				{
					return 0.0;
				}
				return plan.minerTicksPerItem / (double)miningSpeedScale;
			}
			case PlanKind.Gamma:
				return plan.photonTicks;
			default:
				return 0.0;
			}
		}

		private static void CraftRecipe(StationComponent station, Plan plan, int batch, int[] productRegister, int[] consumeRegister)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			StationStore[] storage = station.storage;
			int[] items = plan.items;
			int[] itemCounts = plan.itemCounts;
			int resultCount = plan.resultCount;
			lock (storage)
			{
				int val = batch;
				for (int i = 0; i < items.Length; i++)
				{
					val = Math.Min(val, storage[2 + i].count / itemCounts[i]);
				}
				val = Math.Min(val, FreeSpace(storage[1]) / resultCount);
				if (val <= 0)
				{
					return;
				}
				for (int j = 0; j < items.Length; j++)
				{
					ref StationStore reference = ref storage[2 + j];
					reference.count -= val * itemCounts[j];
					if (reference.inc > reference.count)
					{
						reference.inc = reference.count;
					}
				}
				storage[1].count += val * resultCount;
				plan.statConsumed.Register(consumeRegister, val);
				plan.statProduced.Register(productRegister, val);
			}
		}

		private static void CraftMiner(StationComponent station, Plan plan, int batch, int[] productRegister)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			StationStore[] storage = station.storage;
			lock (storage)
			{
				int[] outputSlots = plan.outputSlots;
				foreach (int num in outputSlots)
				{
					int num2 = Math.Min(batch * plan.outputMultiplier, FreeSpace(storage[num]));
					if (num2 > 0)
					{
						storage[num].count += num2;
						lock (productRegister)
						{
							productRegister[storage[num].itemId] += num2;
						}
					}
				}
			}
		}

		private static long EnergyPerPhoton(Plan plan)
		{
			float num = 1f - GameMain.history.solarEnergyLossRate * 0.6f;
			if (!(num > 0f))
			{
				return 0L;
			}
			return (long)((float)plan.photonHeat / num);
		}

		private static void DrawGammaEnergy(PlanetFactory factory, State state, Plan plan, int batch)
		{
			DysonSphere dysonSphere = factory.dysonSphere;
			long num = EnergyPerPhoton(plan);
			if (dysonSphere == null || num <= 0)
			{
				return;
			}
			long num2 = batch * num;
			long num3 = Math.Min((long)((double)(num2 * 10) / plan.photonTicks) + 1, num2 - state.energyBuffer);
			if (num3 <= 0)
			{
				return;
			}
			long num4;
			long num5;
			do
			{
				num4 = Interlocked.Read(in dysonSphere.energyReqCurrentTick);
				long val = dysonSphere.energyGenCurrentTick - num4;
				num5 = Math.Min(num3, val);
				if (num5 <= 0)
				{
					return;
				}
			}
			while (Interlocked.CompareExchange(ref dysonSphere.energyReqCurrentTick, num4 + num5, num4) != num4);
			state.energyBuffer += num5;
		}

		private static void CraftGamma(StationComponent station, State state, Plan plan, int batch, int[] productRegister)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			long num = EnergyPerPhoton(plan);
			if (num <= 0)
			{
				return;
			}
			StationStore[] storage = station.storage;
			lock (storage)
			{
				int num2 = (int)Math.Min(Math.Min(batch, FreeSpace(storage[1])), state.energyBuffer / num);
				if (num2 <= 0)
				{
					return;
				}
				state.energyBuffer -= num2 * num;
				storage[1].count += num2;
				lock (productRegister)
				{
					productRegister[storage[1].itemId] += num2;
				}
			}
		}

		private static int FreeSpace(StationStore store)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			int num = store.max - store.count;
			if (num <= 0)
			{
				return 0;
			}
			return num;
		}

		private static Plan MatchPlan(PlanetFactory factory, StationComponent station, out string reason)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Invalid comparison between Unknown and I4
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			reason = null;
			StationStore[] storage = station.storage;
			if (storage.Length < 2)
			{
				return null;
			}
			StationStore val = storage[0];
			PrefabDesc val2 = ((val.itemId <= 0) ? null : ((ProtoSet<ItemProto>)(object)LDB.items).Select(val.itemId)?.prefabDesc);
			if (val2 == null)
			{
				return null;
			}
			bool flag = (int)val2.minerType == 2 || val2.isVeinCollector;
			bool flag2 = (int)val2.minerType == 1;
			bool flag3 = (int)val2.minerType == 3;
			if (!val2.isAssembler && !val2.isLab && !val2.isFractionator && !flag && !flag2 && !flag3 && !val2.gammaRayReceiver && !val2.isPowerExchanger)
			{
				return null;
			}
			if ((int)val.localLogic != 0 || (station.isStellar && (int)val.remoteLogic != 0))
			{
				reason = "第一格设置了需求或供应";
				return null;
			}
			if (storage[1].itemId <= 0)
			{
				reason = "第二格为空";
				return null;
			}
			if (val2.isAssembler)
			{
				return MatchRecipePlan(storage, val2.assemblerRecipeType, out reason);
			}
			if (val2.isLab)
			{
				return MatchRecipePlan(storage, (ERecipeType)15, out reason);
			}
			if (val2.isFractionator)
			{
				return MatchFractionatorPlan(storage, out reason);
			}
			if (flag)
			{
				return MatchMinerPlan(factory, storage, val2.isVeinCollector, out reason);
			}
			if (flag2)
			{
				return MatchPumpPlan(factory, storage, val2, out reason);
			}
			if (flag3)
			{
				return MatchOilExtractorPlan(factory, storage, out reason);
			}
			if (val2.isPowerExchanger)
			{
				return MatchExchangerPlan(storage, val2, out reason);
			}
			return MatchGammaPlan(factory, storage, val2, out reason);
		}

		private static Plan MatchRecipePlan(StationStore[] storage, ERecipeType recipeType, out string reason)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			reason = null;
			int itemId = storage[1].itemId;
			bool flag = false;
			bool flag2 = false;
			foreach (PseudoRecipe item in PseudoRecipes.All)
			{
				if (item.buildingType == recipeType && item.outputs.TryGetValue(itemId, out var value))
				{
					flag = true;
					flag2 |= FitsSlotCount(storage, item.ingredients);
					if (SlotsMatch(storage, item.ingredients))
					{
						return new Plan
						{
							kind = PlanKind.Recipe,
							description = "伪配方 " + item.name,
							items = item.ingredients,
							itemCounts = item.ingredientCounts,
							resultId = itemId,
							resultCount = value,
							recipeTicks = item.ticks,
							statProduced = item.produced,
							statConsumed = item.consumed
						};
					}
				}
			}
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val == null || val.Type != recipeType)
				{
					continue;
				}
				int num = Array.IndexOf(val.Results, itemId);
				if (num >= 0)
				{
					flag = true;
					flag2 |= FitsSlotCount(storage, val.Items);
					if (SlotsMatch(storage, val.Items))
					{
						return new Plan
						{
							kind = PlanKind.Recipe,
							description = "配方 " + ((Proto)val).name,
							items = val.Items,
							itemCounts = val.ItemCounts,
							resultId = itemId,
							resultCount = val.ResultCounts[num],
							recipeTicks = Math.Max(val.TimeSpend, 1),
							statProduced = ItemAmounts.Of(val.Results, val.ResultCounts),
							statConsumed = ItemAmounts.Of(val.Items, val.ItemCounts)
						};
					}
				}
			}
			if (!flag)
			{
				reason = "第二格 " + ItemName(itemId) + " 不是该建筑能生产的产品";
			}
			else if (!flag2)
			{
				reason = "原料格子不够,请加装运输塔扩容mod";
			}
			else
			{
				reason = "第三格起的原料与配方顺序不符";
			}
			return null;
		}

		private static bool FitsSlotCount(StationStore[] storage, int[] ingredients)
		{
			return 2 + ingredients.Length <= storage.Length;
		}

		private static Plan MatchFractionatorPlan(StationStore[] storage, out string reason)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Invalid comparison between Unknown and I4
			reason = null;
			if (storage.Length <= 2)
			{
				reason = "格子不够";
				return null;
			}
			int itemId = storage[1].itemId;
			int itemId2 = storage[2].itemId;
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val != null && (int)val.Type == 8 && val.Results.Length != 0 && val.Results[0] == itemId && val.Items.Length != 0 && val.Items[0] == itemId2)
				{
					Plan plan = new Plan();
					plan.kind = PlanKind.Recipe;
					plan.description = "分馏 " + ItemName(itemId2) + " → " + ItemName(itemId);
					plan.items = new int[1] { itemId2 };
					plan.itemCounts = new int[1] { 1 };
					plan.resultId = itemId;
					plan.resultCount = 1;
					plan.recipeTicks = 300.0;
					plan.statProduced = ItemAmounts.Of(new int[1] { itemId }, new int[1] { 1 });
					plan.statConsumed = ItemAmounts.Of(new int[1] { itemId2 }, new int[1] { 1 });
					return plan;
				}
			}
			reason = "第二格/第三格不是分馏配方的产品/原料";
			return null;
		}

		public static bool TryAutoFill(PlanetTransport transport, StationComponent station, out string message)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			int[] array = SuggestIngredients(station, out message);
			if (array == null)
			{
				return false;
			}
			ELogisticStorage val = (ELogisticStorage)(station.isStellar ? 2 : 0);
			for (int i = 0; i < array.Length; i++)
			{
				transport.SetStationStorage(station.id, 2 + i, array[i], int.MaxValue, (ELogisticStorage)2, val, (Player)null);
			}
			string text = string.Join(Tr("、", ", "), array.Select(ItemName));
			message = Tr("已填入原料:" + text, "Ingredients filled: " + text);
			TowerFactory.Log.LogInfo((object)(StationLabel(transport.factory, station) + " 已填入原料:" + text));
			return true;
		}

		private static int[] SuggestIngredients(StationComponent station, out string reason)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Invalid comparison between Unknown and I4
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			StationStore[] storage = station?.storage;
			if (storage == null || station.isCollector || station.isVeinCollector || storage.Length <= 2)
			{
				reason = Tr("这座塔不能做塔厂", "This station can't be a tower factory");
				return null;
			}
			StationStore val = storage[0];
			PrefabDesc val2 = ((val.itemId <= 0) ? null : ((ProtoSet<ItemProto>)(object)LDB.items).Select(val.itemId)?.prefabDesc);
			if (val2 == null || (!val2.isAssembler && !val2.isLab && !val2.isFractionator && !val2.isPowerExchanger))
			{
				reason = Tr("第一格需要放有配方的生产建筑", "Slot 1 must hold a production building that uses recipes");
				return null;
			}
			if ((int)val.localLogic != 0 || (station.isStellar && (int)val.remoteLogic != 0))
			{
				reason = Tr("第一格需要设为仓储", "Slot 1 must be set to Storage");
				return null;
			}
			int productId = storage[1].itemId;
			if (productId <= 0)
			{
				reason = Tr("第二格需要放产品", "Slot 2 must hold the product");
				return null;
			}
			for (int i = 2; i < storage.Length; i++)
			{
				if (storage[i].itemId != 0)
				{
					reason = Tr("第三格起需要全部为空", "Slot 3 and later must all be empty");
					return null;
				}
			}
			List<int[]> list = new List<int[]>();
			if (val2.isPowerExchanger)
			{
				if (productId == val2.fullId && val2.emptyId > 0)
				{
					list.Add(new int[1] { val2.emptyId });
				}
			}
			else if (val2.isFractionator)
			{
				RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
				foreach (RecipeProto val3 in dataArray)
				{
					if (val3 != null && (int)val3.Type == 8 && val3.Results.Length != 0 && val3.Results[0] == productId && val3.Items.Length != 0)
					{
						list.Add(new int[1] { val3.Items[0] });
					}
				}
			}
			else
			{
				ERecipeType recipeType = (ERecipeType)((!val2.isAssembler) ? 15 : ((int)val2.assemblerRecipeType));
				list.AddRange(from pseudo in PseudoRecipes.All
					where pseudo.buildingType == recipeType && pseudo.outputs.ContainsKey(productId)
					select pseudo.ingredients);
				list.AddRange(from recipe in ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray
					where recipe != null && recipe.Type == recipeType && Array.IndexOf(recipe.Results, productId) >= 0
					select recipe.Items);
			}
			if (list.Count == 0)
			{
				reason = Tr(ItemName(productId) + " 不是该建筑能生产的产品", ItemName(productId) + " can't be produced by this building");
				return null;
			}
			if (!list.Any((int[] ingredients) => FitsSlotCount(storage, ingredients)))
			{
				reason = Tr("原料格子不够,请加装运输塔扩容mod", "Not enough slots for the ingredients. Please install a station slot expansion mod");
				return null;
			}
			foreach (int[] item in list)
			{
				if (FitsSlotCount(storage, item) && item.Distinct().Count() == item.Length && !item.Contains(val.itemId) && !item.Contains(productId))
				{
					reason = null;
					return item;
				}
			}
			reason = Tr("放得下的配方里,原料与塔里已有物品重复", "Every recipe that fits has an ingredient that duplicates an item already in the station");
			return null;
		}

		private static Plan MatchExchangerPlan(StationStore[] storage, PrefabDesc prefabDesc, out string reason)
		{
			reason = null;
			if (storage.Length <= 2)
			{
				reason = "原料格子不够,请加装运输塔扩容mod";
				return null;
			}
			if (storage[1].itemId != prefabDesc.fullId || storage[2].itemId != prefabDesc.emptyId)
			{
				reason = "第二格应为 " + ItemName(prefabDesc.fullId) + ",第三格应为 " + ItemName(prefabDesc.emptyId);
				return null;
			}
			if (prefabDesc.exchangeEnergyPerTick <= 0 || prefabDesc.maxExcEnergy <= 0)
			{
				reason = "能量枢纽功率数据异常";
				return null;
			}
			double num = (double)prefabDesc.maxExcEnergy / (double)prefabDesc.exchangeEnergyPerTick;
			Plan plan = new Plan();
			plan.kind = PlanKind.Recipe;
			plan.description = $"充电 {ItemName(prefabDesc.emptyId)} → {ItemName(prefabDesc.fullId)},单座每 {num / 60.0:0.##} 秒 1 个";
			plan.items = new int[1] { prefabDesc.emptyId };
			plan.itemCounts = new int[1] { 1 };
			plan.resultId = prefabDesc.fullId;
			plan.resultCount = 1;
			plan.recipeTicks = num;
			plan.statProduced = ItemAmounts.Of(new int[1] { prefabDesc.fullId }, new int[1] { 1 });
			plan.statConsumed = ItemAmounts.Of(new int[1] { prefabDesc.emptyId }, new int[1] { 1 });
			return plan;
		}

		private static bool SlotsMatch(StationStore[] storage, int[] ingredients)
		{
			if (2 + ingredients.Length > storage.Length)
			{
				return false;
			}
			for (int i = 0; i < ingredients.Length; i++)
			{
				if (storage[2 + i].itemId != ingredients[i])
				{
					return false;
				}
			}
			return true;
		}

		private static Plan MatchMinerPlan(PlanetFactory factory, StationStore[] storage, bool isAdvanced, out string reason)
		{
			reason = null;
			int num = ((!isAdvanced) ? 1 : (storage.Length - 1));
			List<int> list = new List<int>();
			List<string> list2 = new List<string>();
			List<string> list3 = new List<string>();
			for (int i = 1; i <= num; i++)
			{
				int itemId = storage[i].itemId;
				if (itemId > 0)
				{
					string text = null;
					if (!IsMineral(itemId))
					{
						text = $"第 {i + 1} 格 {ItemName(itemId)} 不是采矿机能采的矿物";
					}
					else if (!PlanetHasMineral(factory.planet, itemId))
					{
						text = $"本行星没有第 {i + 1} 格 {ItemName(itemId)} 的矿簇";
					}
					if (text != null)
					{
						list3.Add(text);
						continue;
					}
					list.Add(i);
					list2.Add(ItemName(itemId));
				}
			}
			if (list.Count == 0)
			{
				reason = ((list3.Count > 0) ? string.Join(";", list3) : "第二格为空");
				return null;
			}
			string text2 = ((list3.Count > 0) ? (";跳过:" + string.Join(";", list3)) : "");
			return new Plan
			{
				kind = PlanKind.Miner,
				description = (isAdvanced ? "大型采矿" : "采矿") + " " + string.Join("、", list2) + text2,
				outputSlots = list.ToArray(),
				outputMultiplier = ((!isAdvanced) ? 1 : 3),
				minerTicksPerItem = 120.0
			};
		}

		private static Plan MatchPumpPlan(PlanetFactory factory, StationStore[] storage, PrefabDesc prefabDesc, out string reason)
		{
			reason = null;
			int num = factory.planet?.waterItemId ?? 0;
			if (num <= 0)
			{
				reason = "本行星没有可抽取的海洋";
				return null;
			}
			if (storage[1].itemId != num)
			{
				reason = "第二格应为本行星的海洋物品 " + ItemName(num);
				return null;
			}
			if (prefabDesc.minerPeriod <= 0)
			{
				reason = "抽水机速度数据异常";
				return null;
			}
			Plan plan = new Plan();
			plan.kind = PlanKind.Miner;
			plan.description = "抽取 " + ItemName(num);
			plan.outputSlots = new int[1] { 1 };
			plan.outputMultiplier = 1;
			plan.minerTicksPerItem = (double)prefabDesc.minerPeriod / 10000.0;
			return plan;
		}

		private static Plan MatchOilExtractorPlan(PlanetFactory factory, StationStore[] storage, out string reason)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			reason = null;
			int itemId = storage[1].itemId;
			if ((int)LDB.veins.GetVeinTypeByItemId(itemId) != 7)
			{
				reason = "第二格 " + ItemName(itemId) + " 不是原油";
				return null;
			}
			if (!PlanetHasMineral(factory.planet, itemId))
			{
				reason = "本行星没有油井";
				return null;
			}
			Plan plan = new Plan();
			plan.kind = PlanKind.Miner;
			plan.description = "萃取 " + ItemName(itemId);
			plan.outputSlots = new int[1] { 1 };
			plan.outputMultiplier = 1;
			plan.minerTicksPerItem = 60.0;
			return plan;
		}

		private static bool IsMineral(int itemId)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_0011: Invalid comparison between Unknown and I4
			EVeinType veinTypeByItemId = LDB.veins.GetVeinTypeByItemId(itemId);
			if ((int)veinTypeByItemId != 0)
			{
				return (int)veinTypeByItemId != 7;
			}
			return false;
		}

		private static bool PlanetHasMineral(PlanetData planet, int itemId)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			EVeinType veinTypeByItemId = LDB.veins.GetVeinTypeByItemId(itemId);
			VeinGroup[] array = ((planet != null) ? planet.runtimeVeinGroups : null);
			if (array == null)
			{
				return false;
			}
			VeinGroup[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				VeinGroup val = array2[i];
				if (val.type == veinTypeByItemId && !((VeinGroup)(ref val)).isEmpty)
				{
					return true;
				}
			}
			return false;
		}

		private static Plan MatchGammaPlan(PlanetFactory factory, StationStore[] storage, PrefabDesc prefabDesc, out string reason)
		{
			reason = null;
			if (storage[1].itemId != prefabDesc.powerProductId || prefabDesc.powerProductHeat <= 0)
			{
				reason = "第二格应为 " + ItemName(prefabDesc.powerProductId);
				return null;
			}
			float num = (float)prefabDesc.genEnergyPerTick * 8f * 2.5f;
			if (num <= 0f)
			{
				reason = "射线接收站功率数据异常";
				return null;
			}
			if (factory.dysonSphere == null)
			{
				reason = "本星系没有戴森球";
				return null;
			}
			Plan plan = new Plan
			{
				kind = PlanKind.Gamma,
				photonTicks = (double)prefabDesc.powerProductHeat / (double)num,
				photonHeat = prefabDesc.powerProductHeat
			};
			plan.description = $"临界光子,单座每 {plan.photonTicks / 60.0:0.##} 秒 1 个,每个耗能 {(double)prefabDesc.powerProductHeat / 1000000.0:0.##} MJ";
			return plan;
		}

		private static void LogMatch(PlanetFactory factory, StationComponent station, Plan previous, Plan plan, string reason)
		{
			if (plan != null)
			{
				TowerFactory.Log.LogInfo((object)$"{StationLabel(factory, station)} 识别为塔厂({plan.description}),{1} 秒后生效");
			}
			else if (reason != null)
			{
				TowerFactory.Log.LogInfo((object)(StationLabel(factory, station) + " 未成为塔厂:" + reason));
			}
			else if (previous != null)
			{
				TowerFactory.Log.LogInfo((object)(StationLabel(factory, station) + " 不再是塔厂"));
			}
		}

		private static string StationLabel(PlanetFactory factory, StationComponent station)
		{
			PlanetData planet = factory.planet;
			return $"[{((planet != null) ? planet.displayName : null)} 站点 #{station.id}]";
		}

		private static string ItemName(int itemId)
		{
			ItemProto obj = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
			return ((obj != null) ? ((Proto)obj).name : null) ?? itemId.ToString();
		}

		private static int[] BuildSignature(StationStore[] storage)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected I4, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected I4, but got Unknown
			int[] array = new int[storage.Length * 3];
			for (int i = 0; i < storage.Length; i++)
			{
				array[i * 3] = storage[i].itemId;
				array[i * 3 + 1] = (int)storage[i].localLogic;
				array[i * 3 + 2] = (int)storage[i].remoteLogic;
			}
			return array;
		}

		private static bool SignatureEquals(int[] signature, StationStore[] storage)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between I4 and Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between I4 and Unknown
			if (signature.Length != storage.Length * 3)
			{
				return false;
			}
			for (int i = 0; i < storage.Length; i++)
			{
				if (signature[i * 3] != storage[i].itemId || signature[i * 3 + 1] != (int)storage[i].localLogic || signature[i * 3 + 2] != (int)storage[i].remoteLogic)
				{
					return false;
				}
			}
			return true;
		}
	}
	[BepInPlugin("com.yummi.dsp.towerfactory", "塔厂 (Tower Factory)", "1.1.0")]
	public class TowerFactory : BaseUnityPlugin
	{
		public const string PLUGIN_GUID = "com.yummi.dsp.towerfactory";

		public const string PLUGIN_NAME = "塔厂 (Tower Factory)";

		public const string PLUGIN_VERSION = "1.1.0";

		private static readonly Harmony harmony = new Harmony("com.yummi.dsp.towerfactory");

		internal static ManualLogSource Log;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			harmony.PatchAll(typeof(StationProductionPatch));
			harmony.PatchAll(typeof(UIStationWindowPatch));
			Log.LogInfo((object)"塔厂 (Tower Factory) v1.1.0 已加载");
		}

		private void OnDestroy()
		{
			harmony.UnpatchSelf();
		}
	}
	[HarmonyPatch(typeof(UIStationWindow))]
	public static class UIStationWindowPatch
	{
		private static Button _autoFillButton;

		private static Text _autoFillText;

		[HarmonyPostfix]
		[HarmonyPatch("OnStationIdChange")]
		public static void OnStationIdChange_Postfix(UIStationWindow __instance)
		{
			EnsureButton(__instance);
			if (!((Object)(object)_autoFillButton == (Object)null))
			{
				StationComponent station = GetStation(__instance);
				bool flag = ((ManualBehaviour)__instance).active && station != null && !station.isCollector && !station.isVeinCollector;
				((Component)_autoFillButton).gameObject.SetActive(flag);
				if (flag)
				{
					UpdateButtonText();
				}
			}
		}

		private static void UpdateButtonText()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			string text = StationProductionPatch.Tr("塔厂:填原料", "Fill Ingredients");
			if (!(_autoFillText.text == text))
			{
				_autoFillText.text = text;
				RectTransform val = (RectTransform)((Component)_autoFillButton).transform;
				val.sizeDelta = new Vector2(Mathf.Max(100f, _autoFillText.preferredWidth + 16f), val.sizeDelta.y);
			}
		}

		private static StationComponent GetStation(UIStationWindow window)
		{
			PlanetTransport transport = window.transport;
			int stationId = window.stationId;
			if (transport == null || stationId <= 0 || stationId >= transport.stationCursor)
			{
				return null;
			}
			StationComponent val = transport.stationPool[stationId];
			if (val == null || val.id != stationId)
			{
				return null;
			}
			return val;
		}

		private static void EnsureButton(UIStationWindow window)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_autoFillButton != (Object)null) && !((Object)(object)window.windowTrans == (Object)null))
			{
				GameObject val = new GameObject("tower-factory-autofill");
				val.transform.SetParent((Transform)(object)window.windowTrans, false);
				RectTransform obj = val.AddComponent<RectTransform>();
				obj.anchorMin = new Vector2(1f, 1f);
				obj.anchorMax = new Vector2(1f, 1f);
				obj.pivot = new Vector2(1f, 1f);
				obj.anchoredPosition = new Vector2(-20f, -36f);
				obj.sizeDelta = new Vector2(100f, 22f);
				((Graphic)val.AddComponent<Image>()).color = new Color(0.24f, 0.55f, 0.65f, 0.9f);
				_autoFillButton = val.AddComponent<Button>();
				ColorBlock colors = ((Selectable)_autoFillButton).colors;
				((ColorBlock)(ref colors)).highlightedColor = new Color(1.2f, 1.2f, 1.2f, 1f);
				((ColorBlock)(ref colors)).pressedColor = new Color(0.7f, 0.7f, 0.7f, 1f);
				((Selectable)_autoFillButton).colors = colors;
				((UnityEvent)_autoFillButton.onClick).AddListener((UnityAction)delegate
				{
					OnAutoFillClick(window);
				});
				GameObject val2 = new GameObject("Text");
				val2.transform.SetParent(val.transform, false);
				Text obj2 = (_autoFillText = val2.AddComponent<Text>());
				obj2.font = (((Object)(object)window.titleText != (Object)null) ? window.titleText.font : Resources.GetBuiltinResource<Font>("Arial.ttf"));
				obj2.fontSize = 14;
				obj2.alignment = (TextAnchor)4;
				((Graphic)obj2).color = Color.white;
				RectTransform component = val2.GetComponent<RectTransform>();
				component.anchorMin = Vector2.zero;
				component.anchorMax = Vector2.one;
				component.offsetMin = Vector2.zero;
				component.offsetMax = Vector2.zero;
			}
		}

		private static void OnAutoFillClick(UIStationWindow window)
		{
			StationComponent station = GetStation(window);
			if (station != null)
			{
				string message;
				bool flag = StationProductionPatch.TryAutoFill(window.transport, station, out message);
				UIRealtimeTip.Popup(message, !flag, 0);
				if (flag)
				{
					window.stationId = window.stationId;
				}
			}
		}
	}
}