Decompiled source of QuantumConduit v0.1.1

QuantumConduit.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using crecheng.DSPModSave;

[assembly: AssemblyTitle("Quantum Conduit")]
[assembly: AssemblyDescription("Planetary inventory transfers with proliferation preservation and budgeted scheduling.")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyCompany("Loki Moon Labs")]
[assembly: AssemblyProduct("Quantum Conduit")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: InternalsVisibleTo("QuantumConduit.Tests")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.1.1.0")]
namespace LokiMoonLabs.QuantumConduit;

internal sealed class Engine
{
	private sealed class MechaNotice
	{
		public Player Player;

		public int Item;

		public int Count;

		public int Points;

		public bool Pending;
	}

	private struct InventoryNotification
	{
		public Port Port;

		public StorageComponent Box;

		public MechaNotice Mecha;
	}

	public readonly Options Options;

	public readonly SaveState State = new SaveState();

	public TrashIndex Trash = new TrashIndex();

	private readonly Action<string> log;

	private readonly WorkBudget budget = new WorkBudget();

	private readonly Dictionary<int, MechaNotice> mechaNotices = new Dictionary<int, MechaNotice>();

	private readonly Queue<InventoryNotification> notifications = new Queue<InventoryNotification>();

	private readonly HashSet<StorageComponent> notifiedBoxes = new HashSet<StorageComponent>();

	private int factoryIndex;

	private int slice;

	private int cycle;

	private bool imported;

	private GameData bound;

	private Network[] networks = new Network[0];

	private Network interstellar = new Network(null, interstellar: true);

	private readonly MechaRefill mecha = new MechaRefill();

	private bool mechaFaulted;

	public long TotalMoved;

	public long TotalOperations;

	public long TickCount;

	public double LastMilliseconds;

	public double PeakMilliseconds;

	public double TotalMilliseconds;

	public int FaultCount;

	public int NotificationErrors;

	public int PendingNotifications => notifications.Count;

	public Engine(Options options, Action<string> logger)
	{
		Options = options;
		log = logger;
	}

	public void Reset(bool clearCredit)
	{
		networks = new Network[0];
		interstellar = new Network(null, interstellar: true);
		Trash = new TrashIndex();
		notifications.Clear();
		notifiedBoxes.Clear();
		factoryIndex = (slice = (cycle = 0));
		mechaNotices.Clear();
		bound = null;
		imported = false;
		if (clearCredit)
		{
			State.Clear();
		}
		mecha.Reset();
		mechaFaulted = false;
	}

	public void Import(BinaryReader reader)
	{
		Reset(clearCredit: true);
		State.Import(reader);
		imported = true;
	}

	public void Export(BinaryWriter writer)
	{
		State.Export(writer);
	}

	public void Tick(GameData data, long tick)
	{
		if (data == null || !Options.Enabled)
		{
			return;
		}
		if (!object.ReferenceEquals(bound, data))
		{
			bool flag = imported;
			Reset(!flag);
			bound = data;
		}
		int num = ((data.factories != null) ? Math.Min(Math.Max(0, data.factoryCount), data.factories.Length) : 0);
		if (num == 0)
		{
			return;
		}
		if (networks.Length < num)
		{
			int newSize = Math.Max(num, Math.Max(4, networks.Length * 2));
			Array.Resize(ref networks, newSize);
		}
		long timestamp = Stopwatch.GetTimestamp();
		budget.Begin(Options.OperationsPerTick, Options.MillisecondsPerTick);
		int num2 = 16;
		while (notifications.Count > 0 && num2-- > 0 && budget.Spend())
		{
			InventoryNotification inventoryNotification = notifications.Dequeue();
			if (inventoryNotification.Box != null)
			{
				notifiedBoxes.Remove(inventoryNotification.Box);
			}
			try
			{
				if (inventoryNotification.Mecha == null)
				{
					inventoryNotification.Port.NotifyAfterCommit();
					continue;
				}
				MechaNotice mechaNotice = inventoryNotification.Mecha;
				int count = mechaNotice.Count;
				int points = mechaNotice.Points;
				mechaNotice.Pending = false;
				mechaNotice.Count = (mechaNotice.Points = 0);
				if (object.ReferenceEquals(data.mainPlayer, mechaNotice.Player))
				{
					mechaNotice.Player.NotifyDeliveryPackageAddItem(mechaNotice.Item, count, points);
				}
			}
			catch (Exception ex)
			{
				NotificationErrors++;
				if (NotificationErrors == 1 && log != null)
				{
					log("An inventory notification failed after a completed transfer: " + ex);
				}
			}
		}
		while (budget.Spend())
		{
			cycle = (cycle + 1) & 0x3FF;
			if ((cycle & 0xF) == 0)
			{
				Trash.Step(data);
				continue;
			}
			if (Options.AutoFillMecha && !mechaFaulted && data.mainPlayer != null && (cycle & 0x1F) == 1)
			{
				Player mainPlayer = data.mainPlayer;
				PlanetFactory val = ((mainPlayer == null || mainPlayer.planetData == null) ? null : mainPlayer.planetData.factory);
				Network local = ((val != null && Port.Has(networks, val.index) && Port.Has(data.factories, val.index) && object.ReferenceEquals(data.factories[val.index], val)) ? networks[val.index] : null);
				try
				{
					mecha.Step(this, data, budget, local);
				}
				catch (Exception ex2)
				{
					mechaFaulted = true;
					FaultCount++;
					if (log != null)
					{
						log("Mecha refills paused; planetary transfers remain enabled. " + ex2);
					}
				}
				continue;
			}
			Network network;
			if (Options.Interstellar && (cycle & 7) == 0)
			{
				network = interstellar;
			}
			else
			{
				if (factoryIndex >= num)
				{
					factoryIndex = 0;
				}
				PlanetFactory val2 = data.factories[factoryIndex];
				if (val2 == null)
				{
					factoryIndex++;
					slice = 0;
					continue;
				}
				network = networks[factoryIndex];
				if (network == null || !object.ReferenceEquals(network.Factory, val2))
				{
					network = (networks[factoryIndex] = new Network(val2, interstellar: false));
				}
				if (++slice >= 32)
				{
					slice = 0;
					factoryIndex++;
				}
			}
			if (network.Faulted)
			{
				continue;
			}
			try
			{
				network.Step(this, data, tick, budget);
			}
			catch (Exception ex3)
			{
				network.Faulted = true;
				FaultCount++;
				if (log != null)
				{
					log("Transfers paused for " + (network.Interstellar ? "interstellar logistics" : "one factory") + ". No detached items are held. " + ex3);
				}
			}
		}
		LastMilliseconds = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency;
		PeakMilliseconds = Math.Max(PeakMilliseconds, LastMilliseconds);
		TotalMilliseconds += LastMilliseconds;
		TickCount++;
		TotalOperations += budget.Used;
	}

	public int Move(Port from, Port to, Network network, WorkBudget work)
	{
		if (from.Item != to.Item || from.SameAddress(to))
		{
			return 0;
		}
		if (!from.Read(out var value) || !to.Read(out var value2) || !value.Supply || !value2.Demand || value.Available <= 0 || value2.Count >= value2.Limit)
		{
			return 0;
		}
		int num = Math.Min(value.Available, value2.Limit - value2.Count);
		int num2 = (value2.DiscardPoints ? num : TransferMath.FitPoints(value.Count, value.Points, num, value2.PointLimit - value2.Points));
		if (num2 <= 0)
		{
			return 0;
		}
		int num3 = TransferMath.Split(value.Count, value.Points, num2);
		int num4 = 0;
		if (Options.AutoSpray && !network.Interstellar && from.Item != 1143 && !value2.DiscardPoints && network.Factory != null && network.Factory.planet != null)
		{
			ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(1143);
			if (val != null && val.Ability > 0)
			{
				int num5 = (int)Math.Max(0L, Math.Min((long)value2.PointLimit - (long)value2.Points - num3, (long)val.Ability * (long)num2 - num3));
				if (num5 > 0)
				{
					long val2 = network.TopUp(num5, this, work);
					if (!from.Read(out value) || !to.Read(out value2) || !value.Supply || !value2.Demand || value.Available < num2 || value2.Limit - value2.Count < num2)
					{
						return 0;
					}
					num3 = TransferMath.Split(value.Count, value.Points, num2);
					if ((long)value2.Points + (long)num3 > value2.PointLimit)
					{
						return 0;
					}
					num4 = (int)Math.Min(val2, Math.Max(0L, Math.Min((long)value2.PointLimit - (long)value2.Points - num3, (long)val.Ability * (long)num2 - num3)));
				}
			}
		}
		to.Write(value2, value2.Count + num2, value2.DiscardPoints ? value2.Points : (value2.Points + num3 + num4));
		from.Write(value, value.Count - num2, value.Points - num3);
		network.SpendSpray(this, num4);
		if (to.Kind == PortKind.Mecha)
		{
			QueueMechaNotification(to.Factory.gameData.mainPlayer, to.Item, num2, num3 + num4);
		}
		TotalMoved += num2;
		QueueNotification(from);
		QueueNotification(to);
		return num2;
	}

	public void QueueNotification(Port port)
	{
		if (port.Kind == PortKind.Box)
		{
			StorageComponent val = port.Factory.factoryStorage.storagePool[port.Index];
			if (val != null && notifiedBoxes.Add(val))
			{
				notifications.Enqueue(new InventoryNotification
				{
					Port = port,
					Box = val
				});
			}
		}
		else if (port.Kind == PortKind.Trash && port.Factory.gameData.trashSystem.container.trashObjPool[port.Index].count == 0)
		{
			notifications.Enqueue(new InventoryNotification
			{
				Port = port
			});
		}
	}

	private void QueueMechaNotification(Player player, int item, int count, int points)
	{
		if (!mechaNotices.TryGetValue(item, out var value) || !object.ReferenceEquals(value.Player, player))
		{
			MechaNotice mechaNotice = new MechaNotice();
			mechaNotice.Player = player;
			mechaNotice.Item = item;
			value = mechaNotice;
			mechaNotices[item] = value;
		}
		value.Count = (int)Math.Min(2147483647L, (long)value.Count + (long)count);
		value.Points = (int)Math.Min(2147483647L, (long)value.Points + (long)points);
		if (!value.Pending)
		{
			value.Pending = true;
			notifications.Enqueue(new InventoryNotification
			{
				Mecha = value
			});
		}
	}

	public void RecordSprayConsumption(PlanetFactory factory, int count)
	{
		GameData gameData = factory.gameData;
		if (gameData == null || gameData.statistics == null || gameData.statistics.production == null)
		{
			return;
		}
		FactoryProductionStat[] factoryStatPool = gameData.statistics.production.factoryStatPool;
		if (Port.Has(factoryStatPool, factory.index) && factoryStatPool[factory.index] != null)
		{
			int[] consumeRegister = factoryStatPool[factory.index].consumeRegister;
			if (Port.Has(consumeRegister, 1143))
			{
				consumeRegister[1143] = (int)Math.Min(2147483647L, (long)consumeRegister[1143] + (long)count);
			}
		}
	}
}
internal sealed class MechaRefill
{
	private Player player;

	private DeliveryPackage delivery;

	private Network network;

	private Plan plan;

	private int slot;

	private int provider;

	private int selectedItem;

	private bool stores;

	public void Reset()
	{
		player = null;
		delivery = null;
		network = null;
		plan = null;
		slot = (provider = (selectedItem = 0));
		stores = false;
	}

	private void NextSlot()
	{
		slot++;
		provider = (selectedItem = 0);
		stores = false;
	}

	public void Step(Engine engine, GameData data, WorkBudget budget, Network local)
	{
		Player mainPlayer = data.mainPlayer;
		DeliveryPackage val = ((mainPlayer == null) ? null : mainPlayer.deliveryPackage);
		if (local == null || local.Faulted || mainPlayer == null || val == null || val.grids == null || !val.unlockedAndEnabled || !object.ReferenceEquals(mainPlayer.planetData, local.Factory.planet))
		{
			Reset();
			return;
		}
		if (!object.ReferenceEquals(player, mainPlayer) || !object.ReferenceEquals(delivery, val) || !object.ReferenceEquals(network, local))
		{
			Reset();
			player = mainPlayer;
			delivery = val;
			network = local;
		}
		if (!object.ReferenceEquals(plan, local.Active))
		{
			plan = local.Active;
			provider = 0;
			stores = false;
		}
		if (val.grids.Length == 0)
		{
			return;
		}
		if (slot >= val.grids.Length)
		{
			slot = 0;
		}
		int itemId = val.grids[slot].itemId;
		if (selectedItem != itemId)
		{
			selectedItem = itemId;
			provider = 0;
			stores = false;
		}
		Port to = new Port
		{
			Factory = local.Factory,
			Kind = PortKind.Mecha,
			Index = slot + 1,
			Slot = slot,
			Item = itemId
		};
		if (!to.Read(out var value) || value.Count >= value.Limit || !plan.Map.TryGetValue(itemId, out var value2) || !value2.Active)
		{
			NextSlot();
			return;
		}
		List<Port> list = (stores ? value2.Stores : value2.Producers);
		if (provider >= list.Count)
		{
			if (!stores)
			{
				stores = true;
				provider = 0;
			}
			else
			{
				NextSlot();
			}
		}
		else
		{
			Port port = list[provider++];
			if (port.Kind != PortKind.Trash && object.ReferenceEquals(port.Factory, local.Factory))
			{
				engine.Move(port, to, local, budget);
			}
		}
	}
}
internal sealed class ItemRoute
{
	public readonly List<Port> Producers = new List<Port>();

	public readonly List<Port> Consumers = new List<Port>();

	public readonly List<Port> Stores = new List<Port>();

	public bool Active;

	public void Clear()
	{
		Producers.Clear();
		Consumers.Clear();
		Stores.Clear();
		Active = false;
	}
}
internal sealed class Plan
{
	public readonly Dictionary<int, ItemRoute> Map = new Dictionary<int, ItemRoute>();

	public readonly List<ItemRoute> Items = new List<ItemRoute>();

	public void Add(Port port, StackState state)
	{
		if (!Map.TryGetValue(port.Item, out var value))
		{
			value = new ItemRoute();
			Map.Add(port.Item, value);
		}
		if (!value.Active)
		{
			value.Active = true;
			Items.Add(value);
		}
		if (state.Supply && state.Demand)
		{
			value.Stores.Add(port);
		}
		else if (state.Supply)
		{
			value.Producers.Add(port);
		}
		else if (state.Demand)
		{
			value.Consumers.Add(port);
		}
	}
}
internal sealed class Network
{
	public readonly PlanetFactory Factory;

	public readonly bool Interstellar;

	public bool Faulted;

	public Plan Active = new Plan();

	private Plan building = new Plan();

	private bool scanning = true;

	private bool hasPlan;

	private int clearIndex;

	private int stage;

	private int index = 1;

	private int slot;

	private int scanFactory;

	private int group;

	private int phase;

	private int source;

	private int target;

	private int step;

	private int spraySource;

	private int sprayStore;

	private uint round;

	private long nextScan;

	public int ScanPasses;

	public Network(PlanetFactory factory, bool interstellar)
	{
		Factory = factory;
		Interstellar = interstellar;
	}

	public void Step(Engine engine, GameData data, long tick, WorkBudget budget)
	{
		if (Faulted)
		{
			return;
		}
		if (!scanning && tick >= nextScan)
		{
			scanning = true;
			clearIndex = 0;
			stage = 0;
			index = 1;
			slot = 0;
			scanFactory = 0;
		}
		if (scanning && (!hasPlan || (++step & 3) == 0))
		{
			if (clearIndex >= 0 && clearIndex < building.Items.Count)
			{
				building.Items[clearIndex++].Clear();
				return;
			}
			if (clearIndex >= 0)
			{
				building.Items.Clear();
				clearIndex = -1;
			}
			if (Scan(engine, data))
			{
				Plan active = Active;
				Active = building;
				building = active;
				scanning = false;
				hasPlan = true;
				nextScan = tick + 60;
				group = (phase = (source = (target = (spraySource = (sprayStore = 0)))));
				round++;
				ScanPasses++;
			}
		}
		else if (hasPlan)
		{
			RouteStep(engine, budget);
		}
	}

	private void RouteStep(Engine engine, WorkBudget budget)
	{
		if (Active.Items.Count == 0)
		{
			return;
		}
		if (group >= Active.Items.Count)
		{
			group = (phase = (source = (target = (spraySource = (sprayStore = 0)))));
			round++;
			return;
		}
		ItemRoute itemRoute = Active.Items[group];
		List<Port> list = ((phase == 1) ? itemRoute.Stores : itemRoute.Producers);
		List<Port> list2 = ((phase == 2) ? itemRoute.Stores : itemRoute.Consumers);
		if (source >= list.Count || target >= list2.Count)
		{
			source = (target = 0);
			if (++phase > 2)
			{
				phase = 0;
				group++;
			}
			return;
		}
		Port port = list[(int)((uint)(source + (int)round) % (uint)list.Count)];
		Port to = list2[(int)((uint)(target + (int)round) % (uint)list2.Count)];
		if (!port.Read(out var value) || !value.Supply || value.Available <= 0)
		{
			source++;
			return;
		}
		if (!to.Read(out var value2) || !value2.Demand || value2.Count >= value2.Limit)
		{
			target++;
			return;
		}
		if (Interstellar && port.Factory == to.Factory)
		{
			source++;
			return;
		}
		int num = engine.Move(port, to, this, budget);
		if (num <= 0)
		{
			target++;
			return;
		}
		if (num >= value.Available)
		{
			source++;
		}
		if (num >= value2.Limit - value2.Count || num < value.Available)
		{
			target++;
		}
	}

	public long TopUp(long wanted, Engine engine, WorkBudget budget)
	{
		if (Interstellar || Factory == null || Factory.planet == null)
		{
			return 0L;
		}
		SprayAccount sprayAccount = engine.State.ForPlanet(Factory.planet.id);
		ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(1143);
		if (sprayAccount.Points >= wanted || val == null || val.Ability <= 0 || val.HpMax <= 0 || !Active.Map.TryGetValue(1143, out var value) || !value.Active)
		{
			return sprayAccount.Points;
		}
		while (sprayAccount.Points < wanted && budget.Spend())
		{
			Port port;
			if (spraySource < value.Producers.Count)
			{
				port = value.Producers[spraySource];
			}
			else
			{
				if (sprayStore >= value.Stores.Count)
				{
					break;
				}
				port = value.Stores[sprayStore];
			}
			if (!port.Read(out var value2) || !value2.Supply || value2.Available <= 0)
			{
				if (spraySource < value.Producers.Count)
				{
					spraySource++;
				}
				else
				{
					sprayStore++;
				}
				continue;
			}
			long num = (long)val.HpMax * (long)val.Ability;
			if (num <= 0 || num > 1000000000000000L)
			{
				return sprayAccount.Points;
			}
			int num2 = (int)Math.Min(value2.Available, Math.Max(1L, (wanted - sprayAccount.Points + num - 1) / num));
			int num3;
			long num7;
			while (true)
			{
				num3 = TransferMath.Split(value2.Count, value2.Points, num2);
				int num4 = num3 / num2;
				int num5 = ((Cargo.incTable != null && num4 >= 0 && num4 < Cargo.incTable.Length) ? Math.Max(0, Cargo.incTable[num4]) : 0);
				long num6 = val.HpMax + ((long)val.HpMax * (long)num5 + 100) / 1000;
				if (num6 > 1000000000000000L / val.Ability)
				{
					return sprayAccount.Points;
				}
				num7 = num6 * val.Ability;
				int num8 = (int)Math.Min(num2, (1000000000000000L - sprayAccount.Points) / Math.Max(1L, num7));
				if (num8 <= 0)
				{
					return sprayAccount.Points;
				}
				if (num8 == num2)
				{
					break;
				}
				num2 = num8;
				if (!budget.Spend())
				{
					return sprayAccount.Points;
				}
			}
			port.Write(value2, value2.Count - num2, value2.Points - num3);
			sprayAccount.Points += num7 * num2;
			engine.RecordSprayConsumption(port.Factory, num2);
			engine.QueueNotification(port);
		}
		return sprayAccount.Points;
	}

	public void SpendSpray(Engine engine, int points)
	{
		if (points > 0)
		{
			engine.State.ForPlanet(Factory.planet.id).Points -= points;
		}
	}

	private bool Scan(Engine engine, GameData data)
	{
		//IL_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Invalid comparison between Unknown and I4
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Invalid comparison between Unknown and I4
		PlanetFactory val = Factory;
		PortKind kind;
		if (Interstellar)
		{
			if (data.factories == null || scanFactory >= Math.Min(data.factoryCount, data.factories.Length))
			{
				return true;
			}
			val = data.factories[scanFactory];
			kind = PortKind.Station;
			if (val == null)
			{
				scanFactory++;
				index = 1;
				slot = 0;
				return false;
			}
		}
		else
		{
			if (stage >= 23)
			{
				return true;
			}
			kind = (PortKind)stage;
		}
		int num = End(val, kind, engine);
		if (index >= num)
		{
			if (Interstellar)
			{
				scanFactory++;
			}
			else
			{
				stage++;
			}
			index = 1;
			slot = 0;
			return false;
		}
		int num2 = Slots(val, kind, index);
		if (slot >= num2)
		{
			index++;
			slot = 0;
			return false;
		}
		Port port = Make(val, kind, index, slot++, engine.Options.FractionatorStack, engine);
		if (Interstellar && val.transport != null && Port.Has(val.transport.stationPool, index))
		{
			StationComponent val2 = val.transport.stationPool[index];
			if (val2 != null && val2.isStellar && Port.Has(val2.storage, port.Slot))
			{
				ELogisticStorage remoteLogic = val2.storage[port.Slot].remoteLogic;
				port.RemoteRole = (((int)remoteLogic == 1) ? 1 : (((int)remoteLogic == 2) ? 2 : 0));
			}
			if (port.RemoteRole == 0)
			{
				return false;
			}
		}
		if (port.Read(out var value) && (value.Supply || value.Demand))
		{
			building.Add(port, value);
		}
		return false;
	}

	private static int Bound<T>(T[] pool, int cursor)
	{
		if (pool != null)
		{
			return Math.Min(pool.Length, Math.Max(1, cursor));
		}
		return 1;
	}

	private static int End(PlanetFactory f, PortKind kind, Engine engine)
	{
		if (f == null)
		{
			return 1;
		}
		FactorySystem factorySystem = f.factorySystem;
		PowerSystem powerSystem = f.powerSystem;
		FactoryStorage factoryStorage = f.factoryStorage;
		switch (kind)
		{
		case PortKind.AssemblerInput:
		case PortKind.AssemblerOutput:
			if (factorySystem != null)
			{
				return Bound(factorySystem.assemblerPool, factorySystem.assemblerCursor);
			}
			return 1;
		case PortKind.LabInput:
		case PortKind.LabOutput:
		case PortKind.ResearchInput:
			if (factorySystem != null)
			{
				return Bound(factorySystem.labPool, factorySystem.labCursor);
			}
			return 1;
		case PortKind.MinerOutput:
			if (factorySystem != null)
			{
				return Bound(factorySystem.minerPool, factorySystem.minerCursor);
			}
			return 1;
		case PortKind.Fuel:
		case PortKind.Catalyst:
		case PortKind.GammaOutput:
			if (powerSystem != null)
			{
				return Bound(powerSystem.genPool, powerSystem.genCursor);
			}
			return 1;
		case PortKind.ExchangerEmpty:
		case PortKind.ExchangerFull:
			if (powerSystem != null)
			{
				return Bound(powerSystem.excPool, powerSystem.excCursor);
			}
			return 1;
		case PortKind.FractionInput:
		case PortKind.FractionFluidOutput:
		case PortKind.FractionProductOutput:
			if (factorySystem != null)
			{
				return Bound(factorySystem.fractionatorPool, factorySystem.fractionatorCursor);
			}
			return 1;
		case PortKind.Ejector:
			if (factorySystem != null)
			{
				return Bound(factorySystem.ejectorPool, factorySystem.ejectorCursor);
			}
			return 1;
		case PortKind.Silo:
			if (factorySystem != null)
			{
				return Bound(factorySystem.siloPool, factorySystem.siloCursor);
			}
			return 1;
		case PortKind.Turret:
			if (f.defenseSystem != null && f.defenseSystem.turrets != null)
			{
				return Bound(f.defenseSystem.turrets.buffer, f.defenseSystem.turrets.cursor);
			}
			return 1;
		case PortKind.Box:
			if (factoryStorage != null)
			{
				return Bound(factoryStorage.storagePool, factoryStorage.storageCursor);
			}
			return 1;
		case PortKind.Tank:
			if (factoryStorage != null)
			{
				return Bound(factoryStorage.tankPool, factoryStorage.tankCursor);
			}
			return 1;
		case PortKind.Station:
		case PortKind.Warper:
			if (f.transport != null)
			{
				return Bound(f.transport.stationPool, f.transport.stationCursor);
			}
			return 1;
		case PortKind.Trash:
		{
			List<int> list = engine.Trash.ForPlanet((f.planet != null) ? f.planet.id : 0);
			if (list != null)
			{
				return list.Count + 1;
			}
			return 1;
		}
		default:
			return 1;
		}
	}

	private static int Slots(PlanetFactory f, PortKind kind, int n)
	{
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		FactorySystem factorySystem = f.factorySystem;
		switch (kind)
		{
		case PortKind.AssemblerInput:
		case PortKind.AssemblerOutput:
		{
			if (factorySystem.assemblerPool[n].id != n || factorySystem.assemblerPool[n].recipeId <= 0)
			{
				return 0;
			}
			int[] array2 = ((kind == PortKind.AssemblerInput) ? factorySystem.assemblerPool[n].recipeExecuteData.requires : factorySystem.assemblerPool[n].recipeExecuteData.products);
			if (array2 != null)
			{
				return array2.Length;
			}
			return 0;
		}
		case PortKind.LabInput:
		case PortKind.LabOutput:
		{
			if (factorySystem.labPool[n].id != n || factorySystem.labPool[n].researchMode || factorySystem.labPool[n].recipeId <= 0)
			{
				return 0;
			}
			int[] array = ((kind == PortKind.LabInput) ? factorySystem.labPool[n].recipeExecuteData.requires : factorySystem.labPool[n].recipeExecuteData.products);
			if (array != null)
			{
				return array.Length;
			}
			return 0;
		}
		case PortKind.ResearchInput:
			if (factorySystem.labPool[n].id == n && factorySystem.labPool[n].researchMode && factorySystem.labPool[n].matrixServed != null)
			{
				return factorySystem.labPool[n].matrixServed.Length;
			}
			return 0;
		case PortKind.Fuel:
		{
			PowerGeneratorComponent val2 = f.powerSystem.genPool[n];
			if (val2.id != n || val2.fuelMask <= 0)
			{
				return 0;
			}
			if (val2.fuelId > 0 || val2.curFuelId > 0)
			{
				return 1;
			}
			if (!Port.Has(ItemProto.fuelNeeds, val2.fuelMask) || ItemProto.fuelNeeds[val2.fuelMask] == null)
			{
				return 0;
			}
			return ItemProto.fuelNeeds[val2.fuelMask].Length;
		}
		case PortKind.Box:
		{
			StorageComponent val3 = f.factoryStorage.storagePool[n];
			if (val3 != null && val3.id == n && val3.grids != null)
			{
				return val3.grids.Length;
			}
			return 0;
		}
		case PortKind.Station:
		{
			StationComponent val = f.transport.stationPool[n];
			if (val != null && val.id == n && val.storage != null)
			{
				return val.storage.Length;
			}
			return 0;
		}
		default:
			return 1;
		}
	}

	private static Port Make(PlanetFactory f, PortKind kind, int n, int s, int stack, Engine engine)
	{
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0330: Unknown result type (might be due to invalid IL or missing references)
		//IL_0335: Unknown result type (might be due to invalid IL or missing references)
		Port result = new Port
		{
			Factory = f,
			Kind = kind,
			Index = n,
			Slot = s,
			StackSize = Math.Max(1, stack)
		};
		FactorySystem factorySystem = f.factorySystem;
		PowerSystem powerSystem = f.powerSystem;
		switch (kind)
		{
		case PortKind.AssemblerInput:
		case PortKind.AssemblerOutput:
			result.Item = ((kind == PortKind.AssemblerInput) ? factorySystem.assemblerPool[n].recipeExecuteData.requires : factorySystem.assemblerPool[n].recipeExecuteData.products)[s];
			result.Recipe = factorySystem.assemblerPool[n].recipeId;
			break;
		case PortKind.LabInput:
		case PortKind.LabOutput:
			result.Item = ((kind == PortKind.LabInput) ? factorySystem.labPool[n].recipeExecuteData.requires : factorySystem.labPool[n].recipeExecuteData.products)[s];
			result.Recipe = factorySystem.labPool[n].recipeId;
			break;
		case PortKind.ResearchInput:
			result.Item = (Port.Has(LabComponent.matrixIds, s) ? LabComponent.matrixIds[s] : 0);
			result.Recipe = factorySystem.labPool[n].techId;
			break;
		case PortKind.MinerOutput:
			result.Item = factorySystem.minerPool[n].productId;
			break;
		case PortKind.Fuel:
		{
			PowerGeneratorComponent val = powerSystem.genPool[n];
			result.Item = ((val.fuelId > 0) ? val.fuelId : ((val.curFuelId > 0) ? val.curFuelId : ItemProto.fuelNeeds[val.fuelMask][s]));
			break;
		}
		case PortKind.Catalyst:
			result.Item = powerSystem.genPool[n].catalystId;
			break;
		case PortKind.GammaOutput:
			result.Item = powerSystem.genPool[n].productId;
			break;
		case PortKind.ExchangerEmpty:
			result.Item = powerSystem.excPool[n].emptyId;
			break;
		case PortKind.ExchangerFull:
			result.Item = powerSystem.excPool[n].fullId;
			break;
		case PortKind.FractionInput:
		case PortKind.FractionFluidOutput:
			result.Item = factorySystem.fractionatorPool[n].fluidId;
			break;
		case PortKind.FractionProductOutput:
			result.Item = factorySystem.fractionatorPool[n].productId;
			break;
		case PortKind.Ejector:
			result.Item = factorySystem.ejectorPool[n].bulletId;
			break;
		case PortKind.Silo:
			result.Item = factorySystem.siloPool[n].bulletId;
			break;
		case PortKind.Turret:
			result.Item = f.defenseSystem.turrets.buffer[n].itemId;
			break;
		case PortKind.Box:
		{
			GRID val2 = f.factoryStorage.storagePool[n].grids[s];
			result.Item = ((val2.itemId > 0) ? val2.itemId : val2.filter);
			break;
		}
		case PortKind.Tank:
			result.Item = f.factoryStorage.tankPool[n].fluidId;
			break;
		case PortKind.Station:
			result.Item = f.transport.stationPool[n].storage[s].itemId;
			break;
		case PortKind.Warper:
			result.Item = 1210;
			break;
		case PortKind.Trash:
		{
			List<int> list = engine.Trash.ForPlanet((f.planet != null) ? f.planet.id : 0);
			if (list != null && n - 1 < list.Count)
			{
				result.Index = list[n - 1];
				GameData gameData = f.gameData;
				if (gameData != null && gameData.trashSystem != null && gameData.trashSystem.container != null && Port.Has(gameData.trashSystem.container.trashObjPool, result.Index))
				{
					result.Item = gameData.trashSystem.container.trashObjPool[result.Index].item;
				}
			}
			break;
		}
		}
		return result;
	}
}
[BepInIncompatibility("essium.DSP.PlanetWormhole")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("LokiMoonLabs.QuantumConduit", "Quantum Conduit", "0.1.1")]
public sealed class Plugin : BaseUnityPlugin, IModCanSave
{
	[HarmonyPatch(typeof(GameLogic), "OnFactoryFrameEnd")]
	private static class FactoryEnd
	{
		[HarmonyPriority(0)]
		[HarmonyPostfix]
		private static void Postfix(GameLogic __instance)
		{
			Plugin instance = Plugin.instance;
			if ((Object)(object)instance == (Object)null || instance.runtimeFailed || __instance == null || (Object)(object)__instance.main == (Object)null || __instance.main.isMenuDemo)
			{
				return;
			}
			Options options = instance.engine.Options;
			options.Enabled = instance.transfersEnabled.Value;
			options.AutoSpray = instance.autoSpray.Value;
			options.Interstellar = instance.interstellar.Value;
			options.OperationsPerTick = instance.operations.Value;
			options.AutoFillMecha = instance.autoFillMecha.Value;
			options.MillisecondsPerTick = instance.milliseconds.Value;
			options.FractionatorStack = instance.stack.Value;
			try
			{
				instance.engine.Tick(__instance.data, ++instance.factoryTicks);
			}
			catch (Exception ex)
			{
				options.Enabled = false;
				instance.runtimeFailed = true;
				((BaseUnityPlugin)instance).Logger.LogError((object)("Quantum Conduit stopped after an unexpected error. Committed items remain in game inventories. " + ex));
			}
		}
	}

	public const string Guid = "LokiMoonLabs.QuantumConduit";

	private static Plugin instance;

	private Engine engine;

	private long factoryTicks;

	private bool runtimeFailed;

	private Harmony harmony;

	private ConfigEntry<bool> transfersEnabled;

	private ConfigEntry<bool> autoSpray;

	private ConfigEntry<bool> interstellar;

	private ConfigEntry<bool> autoFillMecha;

	private ConfigEntry<int> operations;

	private ConfigEntry<int> stack;

	private ConfigEntry<double> milliseconds;

	private void Awake()
	{
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Expected O, but got Unknown
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Expected O, but got Unknown
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Expected O, but got Unknown
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Expected O, but got Unknown
		transfersEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable automatic planetary item transfers.");
		autoSpray = ((BaseUnityPlugin)this).Config.Bind<bool>("Logistics", "AutomaticSpraying", true, "Use available Proliferator Mk.III on this planet. Existing spray points always travel with items. Remaining spray credit is saved by DSPModSave.");
		interstellar = ((BaseUnityPlugin)this).Config.Bind<bool>("Logistics", "EnableInterstellar", false, "Also connect ILS remote Supply to remote Demand on different planets. Planetary transfers ignore local slot modes and preserve all slot settings.");
		autoFillMecha = ((BaseUnityPlugin)this).Config.Bind<bool>("Logistics", "AutoFillMechaLogistics", true, "Refill assigned, enabled Icarus logistics slots from production and storage on the current planet. Respects requested quantities, slot capacity, and incoming drone reservations. Does not recycle items or directly fill normal inventory.");
		operations = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "OperationsPerTick", 2048, new ConfigDescription("Maximum discovery/route probes per factory tick. Higher values improve transfer responsiveness at additional CPU cost.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 65536), new object[0]));
		milliseconds = ((BaseUnityPlugin)this).Config.Bind<double>("Performance", "TimeBudgetMilliseconds", 0.75, new ConfigDescription("Soft CPU time budget per factory tick. Checks run periodically; an individual operation or another mod's notification can overrun this budget.", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.1, 10.0), new object[0]));
		stack = ((BaseUnityPlugin)this).Config.Bind<int>("Compatibility", "FractionatorStackSize", 4, new ConfigDescription("Items per virtual fractionator cargo. Default 4 matches PlanetWormhole; adjust only if your fractionator/stacking setup requires it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), new object[0]));
		engine = new Engine(new Options(), delegate(string message)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)message);
		});
		instance = this;
		if (Chainloader.PluginInfos.ContainsKey("dsp.nebula-multiplayer"))
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Quantum Conduit 0.1.1 supports single-player only. Transfers are disabled while Nebula Multiplayer is loaded.");
			return;
		}
		harmony = new Harmony("LokiMoonLabs.QuantumConduit");
		harmony.PatchAll(typeof(Plugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Quantum Conduit 0.1.1 ready. Planetary transfers preserve physical inventories and proliferation.");
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if ((Object)(object)instance == (Object)(object)this)
		{
			instance = null;
		}
	}

	public void Export(BinaryWriter writer)
	{
		engine.Export(writer);
	}

	public void Import(BinaryReader reader)
	{
		try
		{
			engine.Import(reader);
			runtimeFailed = false;
		}
		catch (Exception ex)
		{
			engine.Reset(clearCredit: true);
			((BaseUnityPlugin)this).Logger.LogError((object)("Quantum Conduit could not read its spray credits. Physical game inventories were not changed. " + ex));
		}
	}

	public void IntoOtherSave()
	{
		engine.Reset(clearCredit: true);
		runtimeFailed = false;
	}
}
internal enum PortKind
{
	AssemblerInput,
	AssemblerOutput,
	LabInput,
	LabOutput,
	ResearchInput,
	MinerOutput,
	Fuel,
	Catalyst,
	GammaOutput,
	ExchangerEmpty,
	ExchangerFull,
	FractionInput,
	FractionFluidOutput,
	FractionProductOutput,
	Ejector,
	Silo,
	Turret,
	Box,
	Tank,
	Station,
	Warper,
	Trash,
	Mecha,
	Count
}
internal struct StackState
{
	public int Count;

	public int Points;

	public int Limit;

	public int Available;

	public int PointLimit;

	public bool Supply;

	public bool Demand;

	public bool DiscardPoints;
}
internal struct Port
{
	public PlanetFactory Factory;

	public PortKind Kind;

	public int Index;

	public int Slot;

	public int Item;

	public int Recipe;

	public int RemoteRole;

	public int StackSize;

	internal static bool Has<T>(T[] values, int index)
	{
		if (values != null && index >= 0)
		{
			return index < values.Length;
		}
		return false;
	}

	internal static bool IsOutput(PortKind kind)
	{
		if (kind != PortKind.AssemblerOutput && kind != PortKind.LabOutput && kind != PortKind.MinerOutput && kind != PortKind.GammaOutput && kind != PortKind.FractionFluidOutput && kind != PortKind.FractionProductOutput)
		{
			return kind == PortKind.Trash;
		}
		return true;
	}

	private static bool Match(int[] items, int slot, int item)
	{
		if (Has(items, slot))
		{
			return items[slot] == item;
		}
		return false;
	}

	private bool Values(int[] counts, int[] points, int limit, ref StackState value)
	{
		if (!Has(counts, Slot) || (points != null && !Has(points, Slot)))
		{
			return false;
		}
		value.Count = counts[Slot];
		value.Points = ((points != null) ? points[Slot] : 0);
		value.Limit = limit;
		return true;
	}

	public bool Read(out StackState value)
	{
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_022b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0230: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_050b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0510: Unknown result type (might be due to invalid IL or missing references)
		//IL_0707: Unknown result type (might be due to invalid IL or missing references)
		//IL_070c: Unknown result type (might be due to invalid IL or missing references)
		//IL_07f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_07fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0937: Unknown result type (might be due to invalid IL or missing references)
		//IL_093c: Unknown result type (might be due to invalid IL or missing references)
		//IL_09ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_09b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c22: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c27: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a5f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a64: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b22: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b27: Unknown result type (might be due to invalid IL or missing references)
		//IL_0f33: Unknown result type (might be due to invalid IL or missing references)
		//IL_0f38: Unknown result type (might be due to invalid IL or missing references)
		//IL_0db3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0db8: Unknown result type (might be due to invalid IL or missing references)
		//IL_1061: Unknown result type (might be due to invalid IL or missing references)
		//IL_1066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0de5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0deb: Invalid comparison between Unknown and I4
		//IL_0dd6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ddc: Invalid comparison between Unknown and I4
		value = new StackState
		{
			Limit = int.MaxValue,
			PointLimit = int.MaxValue
		};
		if (Factory == null || Item <= 0 || Index <= 0)
		{
			return false;
		}
		value.Supply = IsOutput(Kind);
		value.Demand = !value.Supply;
		FactorySystem factorySystem = Factory.factorySystem;
		PowerSystem powerSystem = Factory.powerSystem;
		FactoryStorage factoryStorage = Factory.factoryStorage;
		switch (Kind)
		{
		case PortKind.AssemblerInput:
		case PortKind.AssemblerOutput:
		{
			if (factorySystem == null || !Has(factorySystem.assemblerPool, Index))
			{
				return false;
			}
			AssemblerComponent val10 = factorySystem.assemblerPool[Index];
			if (val10.id != Index || val10.recipeId != Recipe || val10.recipeId <= 0)
			{
				return false;
			}
			if (Kind == PortKind.AssemblerInput)
			{
				if (!Match(val10.recipeExecuteData.requires, Slot, Item) || !Has(val10.recipeExecuteData.requireCounts, Slot))
				{
					return false;
				}
				if (!Values(val10.served, val10.incServed, TransferMath.Multiply(val10.recipeExecuteData.requireCounts[Slot], 3), ref value))
				{
					return false;
				}
			}
			else if (!Match(val10.recipeExecuteData.products, Slot, Item) || !Values(val10.produced, null, int.MaxValue, ref value))
			{
				return false;
			}
			break;
		}
		case PortKind.LabInput:
		case PortKind.LabOutput:
		case PortKind.ResearchInput:
		{
			if (factorySystem == null || !Has(factorySystem.labPool, Index))
			{
				return false;
			}
			LabComponent val14 = factorySystem.labPool[Index];
			if (val14.id != Index)
			{
				return false;
			}
			if (Kind == PortKind.ResearchInput)
			{
				if (!val14.researchMode || val14.techId != Recipe || val14.techId <= 0 || !Match(LabComponent.matrixIds, Slot, Item) || !Has(val14.matrixServed, Slot) || !Has(val14.matrixIncServed, Slot) || !Has(val14.needs, Slot) || (val14.needs[Slot] != Item && val14.matrixServed[Slot] < 36000) || val14.matrixServed[Slot] < 0 || val14.matrixIncServed[Slot] < 0)
				{
					return false;
				}
				value.Count = val14.matrixServed[Slot] / 3600;
				value.Points = val14.matrixIncServed[Slot] / 3600;
				value.Limit = value.Count + Math.Max(0, (36000 - val14.matrixServed[Slot]) / 3600);
				value.PointLimit = (int.MaxValue - val14.matrixIncServed[Slot] % 3600) / 3600;
				break;
			}
			if (val14.researchMode || val14.recipeId <= 0 || val14.recipeId != Recipe)
			{
				return false;
			}
			if (Kind == PortKind.LabInput)
			{
				if (!Match(val14.recipeExecuteData.requires, Slot, Item) || !Has(val14.recipeExecuteData.requireCounts, Slot) || !Values(val14.served, val14.incServed, TransferMath.Multiply(val14.recipeExecuteData.requireCounts[Slot], 3), ref value))
				{
					return false;
				}
			}
			else if (!Match(val14.recipeExecuteData.products, Slot, Item) || !Values(val14.produced, null, int.MaxValue, ref value))
			{
				return false;
			}
			break;
		}
		case PortKind.MinerOutput:
		{
			if (factorySystem == null || !Has(factorySystem.minerPool, Index))
			{
				return false;
			}
			MinerComponent val11 = factorySystem.minerPool[Index];
			if (val11.id != Index || val11.productId != Item)
			{
				return false;
			}
			value.Count = val11.productCount;
			break;
		}
		case PortKind.Fuel:
		case PortKind.Catalyst:
		case PortKind.GammaOutput:
		{
			if (powerSystem == null || !Has(powerSystem.genPool, Index))
			{
				return false;
			}
			PowerGeneratorComponent val12 = powerSystem.genPool[Index];
			if (val12.id != Index)
			{
				return false;
			}
			if (Kind == PortKind.Fuel)
			{
				if (Item > 32767 || val12.fuelMask <= 0 || (val12.fuelId > 0 && val12.fuelId != Item))
				{
					return false;
				}
				int[] array = (Has(ItemProto.fuelNeeds, val12.fuelMask) ? ItemProto.fuelNeeds[val12.fuelMask] : null);
				bool flag = false;
				if (array != null)
				{
					for (int i = 0; i < array.Length; i++)
					{
						if (array[i] == Item)
						{
							flag = true;
							break;
						}
					}
				}
				if (!flag || ((ProtoSet<ItemProto>)(object)LDB.items).Select(Item) == null)
				{
					return false;
				}
				value.Count = val12.fuelCount;
				value.Points = val12.fuelInc;
				value.Limit = 10;
				value.PointLimit = 32767;
			}
			else if (Kind == PortKind.Catalyst)
			{
				if (val12.catalystId != Item || Factory.gameData == null || Factory.gameData.history == null || !Factory.gameData.history.TechUnlocked(1505))
				{
					return false;
				}
				value.Count = val12.catalystCount;
				value.Points = val12.catalystInc;
				value.Limit = 2;
				value.PointLimit = 32767;
			}
			else
			{
				if (val12.productId != Item || float.IsNaN(val12.productCount) || float.IsInfinity(val12.productCount) || val12.productCount < 0f || val12.productCount >= 2.1474836E+09f)
				{
					return false;
				}
				value.Count = (int)val12.productCount;
			}
			break;
		}
		case PortKind.ExchangerEmpty:
		case PortKind.ExchangerFull:
		{
			if (powerSystem == null || !Has(powerSystem.excPool, Index))
			{
				return false;
			}
			PowerExchangerComponent val18 = powerSystem.excPool[Index];
			if (val18.id != Index || Math.Abs(val18.targetState) < 0.5f)
			{
				return false;
			}
			bool flag2 = Kind == PortKind.ExchangerEmpty;
			if ((flag2 ? val18.emptyId : val18.fullId) != Item)
			{
				return false;
			}
			value.Count = (flag2 ? val18.emptyCount : val18.fullCount);
			value.Points = (flag2 ? val18.emptyInc : val18.fullInc);
			value.Limit = 20;
			value.PointLimit = 32767;
			value.Demand = flag2 == val18.targetState > 0f;
			value.Supply = !value.Demand;
			break;
		}
		case PortKind.FractionInput:
		case PortKind.FractionFluidOutput:
		case PortKind.FractionProductOutput:
		{
			if (factorySystem == null || !Has(factorySystem.fractionatorPool, Index))
			{
				return false;
			}
			FractionatorComponent val17 = factorySystem.fractionatorPool[Index];
			if (val17.id != Index)
			{
				return false;
			}
			if (Kind == PortKind.FractionProductOutput)
			{
				if (val17.productId != Item)
				{
					return false;
				}
				value.Count = val17.productOutputCount;
				break;
			}
			if (val17.fluidId != Item)
			{
				return false;
			}
			if (Kind == PortKind.FractionFluidOutput)
			{
				value.Count = val17.fluidOutputCount;
				value.Points = val17.fluidOutputInc;
				break;
			}
			if (float.IsNaN(val17.fluidInputCargoCount) || float.IsInfinity(val17.fluidInputCargoCount) || val17.fluidInputCargoCount < 0f)
			{
				return false;
			}
			value.Count = val17.fluidInputCount;
			value.Points = val17.fluidInputInc;
			long num2 = (long)Math.Max(0.0, ((double)val17.fluidInputMax - (double)val17.fluidInputCargoCount) * (double)Math.Max(1, StackSize));
			value.Limit = (int)Math.Min(2147483647L, value.Count + num2);
			break;
		}
		case PortKind.Ejector:
		{
			if (factorySystem == null || !Has(factorySystem.ejectorPool, Index))
			{
				return false;
			}
			EjectorComponent val4 = factorySystem.ejectorPool[Index];
			if (val4.id != Index || val4.bulletId != Item)
			{
				return false;
			}
			value.Count = val4.bulletCount;
			value.Points = val4.bulletInc;
			value.Limit = 20;
			break;
		}
		case PortKind.Silo:
		{
			if (factorySystem == null || !Has(factorySystem.siloPool, Index))
			{
				return false;
			}
			SiloComponent val5 = factorySystem.siloPool[Index];
			if (val5.id != Index || val5.bulletId != Item)
			{
				return false;
			}
			value.Count = val5.bulletCount;
			value.Points = val5.bulletInc;
			value.Limit = 20;
			break;
		}
		case PortKind.Turret:
		{
			if (Factory.defenseSystem == null || Factory.defenseSystem.turrets == null || !Has(Factory.defenseSystem.turrets.buffer, Index))
			{
				return false;
			}
			TurretComponent val3 = Factory.defenseSystem.turrets.buffer[Index];
			if (val3.id != Index || val3.itemId != Item)
			{
				return false;
			}
			value.Count = val3.itemCount;
			value.Points = val3.itemInc;
			value.Limit = 5;
			value.PointLimit = 32767;
			break;
		}
		case PortKind.Box:
		{
			if (factoryStorage == null || !Has(factoryStorage.storagePool, Index))
			{
				return false;
			}
			StorageComponent val15 = factoryStorage.storagePool[Index];
			if (val15 == null || val15.id != Index || val15.isPlayerInventory || !Has(val15.grids, Slot))
			{
				return false;
			}
			GRID val16 = val15.grids[Slot];
			if (val16.itemId != Item && (val16.itemId != 0 || val16.filter != Item))
			{
				return false;
			}
			value.Count = val16.count;
			value.Points = val16.inc;
			value.Supply = true;
			value.Demand = (val16.filter == 0 || val16.filter == Item) && Slot < Math.Max(0, val15.size - val15.bans);
			value.Limit = ((val16.stackSize > 0) ? val16.stackSize : ((((ProtoSet<ItemProto>)(object)LDB.items).Select(Item) != null) ? ((ProtoSet<ItemProto>)(object)LDB.items).Select(Item).StackSize : 0));
			break;
		}
		case PortKind.Tank:
		{
			if (factoryStorage == null || !Has(factoryStorage.tankPool, Index))
			{
				return false;
			}
			TankComponent val13 = factoryStorage.tankPool[Index];
			if (val13.id != Index || val13.fluidId != Item)
			{
				return false;
			}
			value.Count = val13.fluidCount;
			value.Points = val13.fluidInc;
			value.Limit = val13.fluidCapacity;
			value.Supply = val13.outputSwitch;
			value.Demand = val13.inputSwitch;
			break;
		}
		case PortKind.Station:
		case PortKind.Warper:
		{
			if (Factory.transport == null || !Has(Factory.transport.stationPool, Index))
			{
				return false;
			}
			StationComponent val6 = Factory.transport.stationPool[Index];
			if (val6 == null || val6.id != Index)
			{
				return false;
			}
			if (Kind == PortKind.Warper)
			{
				if (!val6.isStellar || Item != 1210 || Factory.gameData == null || Factory.gameData.history == null || !Factory.gameData.history.TechUnlocked(3404))
				{
					return false;
				}
				value.Count = val6.warperCount;
				value.Limit = val6.warperMaxCount;
				value.DiscardPoints = true;
				break;
			}
			if (!Has(val6.storage, Slot) || val6.storage[Slot].itemId != Item)
			{
				return false;
			}
			StationStore val7 = val6.storage[Slot];
			if (RemoteRole != 0 && (!val6.isStellar || ((RemoteRole == 1) ? ((int)val7.remoteLogic != 1) : ((int)val7.remoteLogic != 2))))
			{
				return false;
			}
			value.Count = val7.count;
			value.Points = val7.inc;
			value.Available = (int)Math.Max(0L, (long)val7.count + (long)Math.Min(0, val7.localOrder) + Math.Min(0, val7.remoteOrder));
			value.Limit = (int)Math.Max(0L, (long)val7.max - (long)Math.Max(0, val7.localOrder) - Math.Max(0, val7.remoteOrder));
			value.Supply = RemoteRole != 2;
			value.Demand = RemoteRole != 1;
			if (value.Count >= 0 && value.Points >= 0)
			{
				return value.Limit >= 0;
			}
			return false;
		}
		case PortKind.Trash:
		{
			GameData gameData = Factory.gameData;
			TrashContainer val8 = ((gameData == null || gameData.trashSystem == null) ? null : gameData.trashSystem.container);
			if (val8 == null || !Has(val8.trashObjPool, Index) || !Has(val8.trashDataPool, Index) || Factory.planet == null)
			{
				return false;
			}
			TrashData val9 = val8.trashDataPool[Index];
			int num = ((val9.landPlanetId > 0) ? val9.landPlanetId : val9.nearPlanetId);
			if (num != Factory.planet.id || val8.trashObjPool[Index].item != Item)
			{
				return false;
			}
			value.Count = val8.trashObjPool[Index].count;
			value.Points = val8.trashObjPool[Index].inc;
			break;
		}
		case PortKind.Mecha:
		{
			Player val = ((Factory.gameData == null) ? null : Factory.gameData.mainPlayer);
			if (val == null || !object.ReferenceEquals(val.planetData, Factory.planet) || val.planetData == null)
			{
				return false;
			}
			DeliveryPackage deliveryPackage = val.deliveryPackage;
			if (deliveryPackage == null || !deliveryPackage.unlockedAndEnabled || !Has(deliveryPackage.grids, Slot) || !deliveryPackage.IsGridActive(Slot))
			{
				return false;
			}
			GRID val2 = deliveryPackage.grids[Slot];
			if (val2.itemId != Item || val2.requireCount <= 0)
			{
				return false;
			}
			int stackSizeModified = ((GRID)(ref val2)).stackSizeModified;
			int clampedRequireCount = ((GRID)(ref val2)).clampedRequireCount;
			if (stackSizeModified <= 0 || clampedRequireCount <= 0)
			{
				return false;
			}
			value.Count = val2.count;
			value.Points = val2.inc;
			value.Limit = (int)Math.Max(0L, (long)Math.Min(stackSizeModified, clampedRequireCount) - (long)Math.Max(0, val2.ordered));
			value.Supply = false;
			value.Demand = true;
			break;
		}
		default:
			return false;
		}
		value.Available = value.Count;
		if (value.Count >= 0 && value.Points >= 0 && value.Points <= value.PointLimit)
		{
			return value.Limit >= 0;
		}
		return false;
	}

	public void Write(StackState before, int count, int points)
	{
		FactorySystem factorySystem = Factory.factorySystem;
		PowerSystem powerSystem = Factory.powerSystem;
		switch (Kind)
		{
		case PortKind.AssemblerInput:
			factorySystem.assemblerPool[Index].served[Slot] = count;
			factorySystem.assemblerPool[Index].incServed[Slot] = points;
			break;
		case PortKind.AssemblerOutput:
			factorySystem.assemblerPool[Index].produced[Slot] = count;
			break;
		case PortKind.LabInput:
			factorySystem.labPool[Index].served[Slot] = count;
			factorySystem.labPool[Index].incServed[Slot] = points;
			break;
		case PortKind.LabOutput:
			factorySystem.labPool[Index].produced[Slot] = count;
			break;
		case PortKind.ResearchInput:
			factorySystem.labPool[Index].matrixServed[Slot] += (count - before.Count) * 3600;
			factorySystem.labPool[Index].matrixIncServed[Slot] += (points - before.Points) * 3600;
			break;
		case PortKind.MinerOutput:
			factorySystem.minerPool[Index].productCount = count;
			break;
		case PortKind.Fuel:
			if (powerSystem.genPool[Index].fuelId != Item)
			{
				((PowerGeneratorComponent)(ref powerSystem.genPool[Index])).SetNewFuel(Item, (short)count, (short)points);
				break;
			}
			powerSystem.genPool[Index].fuelCount = (short)count;
			powerSystem.genPool[Index].fuelInc = (short)points;
			break;
		case PortKind.Catalyst:
			powerSystem.genPool[Index].catalystCount = (short)count;
			powerSystem.genPool[Index].catalystInc = (short)points;
			break;
		case PortKind.GammaOutput:
		{
			ref PowerGeneratorComponent reference2 = ref powerSystem.genPool[Index];
			reference2.productCount += (float)(count - before.Count);
			break;
		}
		case PortKind.ExchangerEmpty:
			powerSystem.excPool[Index].emptyCount = (short)count;
			powerSystem.excPool[Index].emptyInc = (short)points;
			break;
		case PortKind.ExchangerFull:
			powerSystem.excPool[Index].fullCount = (short)count;
			powerSystem.excPool[Index].fullInc = (short)points;
			break;
		case PortKind.FractionInput:
		{
			factorySystem.fractionatorPool[Index].fluidInputCount = count;
			factorySystem.fractionatorPool[Index].fluidInputInc = points;
			ref FractionatorComponent reference = ref factorySystem.fractionatorPool[Index];
			reference.fluidInputCargoCount += (float)(count - before.Count) / (float)Math.Max(1, StackSize);
			break;
		}
		case PortKind.FractionFluidOutput:
			factorySystem.fractionatorPool[Index].fluidOutputCount = count;
			factorySystem.fractionatorPool[Index].fluidOutputInc = points;
			break;
		case PortKind.FractionProductOutput:
			factorySystem.fractionatorPool[Index].productOutputCount = count;
			break;
		case PortKind.Ejector:
			factorySystem.ejectorPool[Index].bulletCount = count;
			factorySystem.ejectorPool[Index].bulletInc = points;
			break;
		case PortKind.Silo:
			factorySystem.siloPool[Index].bulletCount = count;
			factorySystem.siloPool[Index].bulletInc = points;
			break;
		case PortKind.Turret:
			Factory.defenseSystem.turrets.buffer[Index].itemCount = (short)count;
			Factory.defenseSystem.turrets.buffer[Index].itemInc = (short)points;
			break;
		case PortKind.Box:
		{
			StorageComponent val = Factory.factoryStorage.storagePool[Index];
			val.grids[Slot].count = count;
			val.grids[Slot].inc = points;
			val.grids[Slot].itemId = ((count > 0) ? Item : 0);
			val.changed = true;
			val.lastFullItem = 0;
			val.lastEmptyItem = 0;
			val.searchStart = 0;
			break;
		}
		case PortKind.Tank:
			Factory.factoryStorage.tankPool[Index].fluidCount = count;
			Factory.factoryStorage.tankPool[Index].fluidInc = points;
			break;
		case PortKind.Station:
			Factory.transport.stationPool[Index].storage[Slot].count = count;
			Factory.transport.stationPool[Index].storage[Slot].inc = points;
			break;
		case PortKind.Warper:
			Factory.transport.stationPool[Index].warperCount = count;
			break;
		case PortKind.Trash:
			Factory.gameData.trashSystem.container.trashObjPool[Index].count = count;
			Factory.gameData.trashSystem.container.trashObjPool[Index].inc = points;
			break;
		case PortKind.Mecha:
			Factory.gameData.mainPlayer.deliveryPackage.grids[Slot].count = count;
			Factory.gameData.mainPlayer.deliveryPackage.grids[Slot].inc = points;
			break;
		}
	}

	public bool SameAddress(Port other)
	{
		if (Factory == other.Factory && Kind == other.Kind && Index == other.Index)
		{
			return Slot == other.Slot;
		}
		return false;
	}

	public void NotifyAfterCommit()
	{
		if (Kind == PortKind.Box)
		{
			if (Factory.factoryStorage != null && Has(Factory.factoryStorage.storagePool, Index))
			{
				StorageComponent val = Factory.factoryStorage.storagePool[Index];
				if (val != null && val.id == Index)
				{
					val.NotifyStorageChange();
				}
			}
		}
		else if (Kind == PortKind.Trash)
		{
			GameData gameData = Factory.gameData;
			if (gameData != null && gameData.trashSystem != null && gameData.trashSystem.container != null && Has(gameData.trashSystem.container.trashObjPool, Index) && gameData.trashSystem.container.trashObjPool[Index].item == Item && gameData.trashSystem.container.trashObjPool[Index].count == 0)
			{
				gameData.trashSystem.RemoveTrash(Index);
			}
		}
	}
}
internal sealed class SprayAccount
{
	public long Points;
}
internal sealed class SaveState
{
	private const int Magic = 1363365444;

	private const int Version = 1;

	private const int MaximumAccounts = 100000;

	public const long MaximumCredit = 1000000000000000L;

	internal readonly Dictionary<int, SprayAccount> Accounts = new Dictionary<int, SprayAccount>();

	public SprayAccount ForPlanet(int planet)
	{
		if (!Accounts.TryGetValue(planet, out var value))
		{
			value = new SprayAccount();
			Accounts.Add(planet, value);
		}
		return value;
	}

	public void Clear()
	{
		Accounts.Clear();
	}

	public void Export(BinaryWriter writer)
	{
		writer.Write(1363365444);
		writer.Write(1);
		writer.Write(Accounts.Count);
		foreach (KeyValuePair<int, SprayAccount> account in Accounts)
		{
			writer.Write(account.Key);
			writer.Write(account.Value.Points);
		}
	}

	public void Import(BinaryReader reader)
	{
		if (reader.ReadInt32() != 1363365444 || reader.ReadInt32() != 1)
		{
			throw new InvalidDataException("Unsupported Quantum Conduit save data.");
		}
		int num = reader.ReadInt32();
		if (num < 0 || num > 100000)
		{
			throw new InvalidDataException("Invalid planet account count.");
		}
		Dictionary<int, SprayAccount> dictionary = new Dictionary<int, SprayAccount>();
		for (int i = 0; i < num; i++)
		{
			int num2 = reader.ReadInt32();
			long num3 = reader.ReadInt64();
			if (num2 <= 0 || num3 < 0 || num3 > 1000000000000000L || dictionary.ContainsKey(num2))
			{
				throw new InvalidDataException("Invalid spray credit account.");
			}
			dictionary.Add(num2, new SprayAccount
			{
				Points = num3
			});
		}
		Accounts.Clear();
		foreach (KeyValuePair<int, SprayAccount> item in dictionary)
		{
			Accounts.Add(item.Key, item.Value);
		}
	}
}
internal sealed class Options
{
	public bool Enabled = true;

	public bool AutoSpray = true;

	public bool Interstellar;

	public bool AutoFillMecha = true;

	public int OperationsPerTick = 2048;

	public int FractionatorStack = 4;

	public double MillisecondsPerTick = 0.75;
}
internal static class TransferMath
{
	public static int Split(int count, int points, int take)
	{
		if (count <= 0 || points < 0 || take <= 0)
		{
			return 0;
		}
		int num = Math.Min(count, take);
		int num2 = points / count;
		return (int)((long)num2 * (long)num + Math.Max(0, points % count - (count - num)));
	}

	public static int FitPoints(int count, int points, int wanted, int room)
	{
		if (room < 0 || count <= 0 || points < 0)
		{
			return 0;
		}
		int num = 0;
		int num2 = Math.Min(count, Math.Max(0, wanted));
		if (Split(count, points, num2) <= room)
		{
			return num2;
		}
		while (num < num2)
		{
			int num3 = num + (int)(((long)num2 - (long)num + 1) / 2);
			if (Split(count, points, num3) <= room)
			{
				num = num3;
			}
			else
			{
				num2 = num3 - 1;
			}
		}
		return num;
	}

	public static int Multiply(int a, int b)
	{
		return (int)Math.Min(2147483647L, (long)Math.Max(0, a) * (long)Math.Max(0, b));
	}
}
internal sealed class WorkBudget
{
	public int Remaining;

	public int Used;

	private long deadline;

	public void Begin(int operations, double milliseconds)
	{
		Remaining = Math.Max(1, operations);
		Used = 0;
		deadline = Stopwatch.GetTimestamp() + (long)((double)Stopwatch.Frequency * Math.Max(0.01, milliseconds) / 1000.0);
	}

	public bool Spend()
	{
		if (Remaining <= 0)
		{
			return false;
		}
		if ((Used & 0xF) == 0 && Stopwatch.GetTimestamp() >= deadline)
		{
			Remaining = 0;
			return false;
		}
		Remaining--;
		Used++;
		return true;
	}
}
internal sealed class TrashIndex
{
	private sealed class Map
	{
		public readonly Dictionary<int, List<int>> Planets = new Dictionary<int, List<int>>();

		public readonly List<List<int>> Lists = new List<List<int>>();
	}

	private Map active = new Map();

	private Map building = new Map();

	private int clear;

	private int index = 1;

	public List<int> ForPlanet(int planet)
	{
		if (!active.Planets.TryGetValue(planet, out var value))
		{
			return null;
		}
		return value;
	}

	public void Step(GameData data)
	{
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		if (clear >= 0 && clear < building.Lists.Count)
		{
			building.Lists[clear++].Clear();
			return;
		}
		clear = -1;
		TrashContainer val = ((data.trashSystem == null) ? null : data.trashSystem.container);
		if (val == null || !Port.Has(val.trashObjPool, index) || !Port.Has(val.trashDataPool, index) || index >= val.trashCursor)
		{
			Map map = active;
			active = building;
			building = map;
			index = 1;
			clear = 0;
			return;
		}
		int num = index++;
		if (val.trashObjPool[num].item <= 0 || val.trashObjPool[num].count <= 0)
		{
			return;
		}
		TrashData val2 = val.trashDataPool[num];
		int num2 = ((val2.landPlanetId > 0) ? val2.landPlanetId : val2.nearPlanetId);
		if (num2 > 0)
		{
			if (!building.Planets.TryGetValue(num2, out var value))
			{
				value = new List<int>();
				building.Planets.Add(num2, value);
				building.Lists.Add(value);
			}
			value.Add(num);
		}
	}
}