Decompiled source of DSPPowerPriority v1.0.0

PowerPriority.dll

Decompiled 10 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PowerPriority")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PowerPriority")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7dbe0ca4-66d4-43a8-ae90-762a7c6b0d22")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DSPPowerPriority;

[BepInPlugin("lee.dsp.powerpriority", "DSP Power Priority", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "lee.dsp.powerpriority";

	public const string PluginName = "DSP Power Priority";

	public const string PluginVersion = "1.0.0";

	internal static ManualLogSource Log;

	private ConfigEntry<int> nonFuelPriority;

	private ConfigEntry<int> exchangerPriority;

	private ConfigEntry<int> highGradeFuelPriority;

	private ConfigEntry<int> lowGradeThermalPriority;

	private ConfigEntry<string> highGradeThermalFuelIds;

	private ConfigEntry<int> exchangerChargingMaxSourcePriority;

	private void Awake()
	{
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		nonFuelPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "NonFuelGeneration", 1, "Priority bucket for wind, solar, ray receivers and geothermal. Lower numbers are used first. Equal numbers share load proportionally.");
		exchangerPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "EnergyExchangers", 2, "Priority bucket for energy exchangers while discharging.");
		highGradeFuelPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "HighGradeFuelGeneration", 3, "Priority bucket for thermal plants burning configured high-grade fuels, mini fusion plants and artificial stars.");
		lowGradeThermalPriority = ((BaseUnityPlugin)this).Config.Bind<int>("Priorities", "LowGradeThermalGeneration", 4, "Priority bucket for ordinary thermal generation such as coal, energetic graphite, oil and hydrogen.");
		highGradeThermalFuelIds = ((BaseUnityPlugin)this).Config.Bind<string>("Classification", "HighGradeThermalFuelIds", "1801", "Comma-separated item IDs that make a thermal power plant use the HighGradeFuelGeneration priority. 1801 is the vanilla Hydrogen Fuel Rod.");
		exchangerChargingMaxSourcePriority = ((BaseUnityPlugin)this).Config.Bind<int>("EnergyExchanger", "ChargingMaxSourcePriority", 0, "Highest-numbered priority bucket that may be brought online solely to charge energy exchangers. 0 = unrestricted vanilla charging behaviour. Example: 3 allows charging from priorities 1-3 but will not start priority 4 generation just to charge exchangers.");
		RebuildConfigSnapshot();
		nonFuelPriority.SettingChanged += OnConfigChanged;
		exchangerPriority.SettingChanged += OnConfigChanged;
		highGradeFuelPriority.SettingChanged += OnConfigChanged;
		lowGradeThermalPriority.SettingChanged += OnConfigChanged;
		highGradeThermalFuelIds.SettingChanged += OnConfigChanged;
		exchangerChargingMaxSourcePriority.SettingChanged += OnConfigChanged;
		new Harmony("lee.dsp.powerpriority").PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"DSP Power Priority 1.0.0 loaded.");
	}

	private void OnConfigChanged(object sender, EventArgs e)
	{
		RebuildConfigSnapshot();
	}

	private void RebuildConfigSnapshot()
	{
		int num = NormalizePriority(nonFuelPriority.Value);
		int num2 = NormalizePriority(exchangerPriority.Value);
		int num3 = NormalizePriority(highGradeFuelPriority.Value);
		int num4 = NormalizePriority(lowGradeThermalPriority.Value);
		int[] array = new int[4] { num, num2, num3, num4 };
		int[] array2 = new int[4];
		int[] array3 = new int[4];
		int num5 = 0;
		for (int i = 0; i < 4; i++)
		{
			int num6 = array[i];
			int num7 = -1;
			for (int j = 0; j < num5; j++)
			{
				if (array2[j] == num6)
				{
					num7 = j;
					break;
				}
			}
			if (num7 < 0)
			{
				num7 = num5++;
				array2[num7] = num6;
			}
			array3[num7] |= 1 << i;
		}
		for (int k = 1; k < num5; k++)
		{
			int num8 = array2[k];
			int num9 = array3[k];
			int num10 = k - 1;
			while (num10 >= 0 && array2[num10] > num8)
			{
				array2[num10 + 1] = array2[num10];
				array3[num10 + 1] = array3[num10];
				num10--;
			}
			array2[num10 + 1] = num8;
			array3[num10 + 1] = num9;
		}
		int invalidCount;
		int[] array4 = ParseItemIds(highGradeThermalFuelIds.Value, out invalidCount);
		int num11 = exchangerChargingMaxSourcePriority.Value;
		if (num11 < 0)
		{
			num11 = 0;
		}
		PriorityDispatch.SetConfig(new DispatchConfig(array3, num5, array, array4, num11));
		if (nonFuelPriority.Value < 1 || exchangerPriority.Value < 1 || highGradeFuelPriority.Value < 1 || lowGradeThermalPriority.Value < 1)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Priority values below 1 are treated as priority 1.");
		}
		if (invalidCount > 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)string.Format("Ignored {0} invalid HighGradeThermalFuelIds entr{1}.", invalidCount, (invalidCount == 1) ? "y" : "ies"));
		}
		if (exchangerChargingMaxSourcePriority.Value < 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"ChargingMaxSourcePriority values below 0 are treated as 0 (unrestricted vanilla charging).");
		}
	}

	private static int NormalizePriority(int value)
	{
		if (value >= 1)
		{
			return value;
		}
		return 1;
	}

	private static int[] ParseItemIds(string text, out int invalidCount)
	{
		invalidCount = 0;
		if (string.IsNullOrWhiteSpace(text))
		{
			return new int[0];
		}
		string[] array = text.Split(new char[1] { ',' });
		List<int> list = new List<int>(array.Length);
		for (int i = 0; i < array.Length; i++)
		{
			string text2 = array[i].Trim();
			if (!int.TryParse(text2, out var result) || result <= 0)
			{
				if (text2.Length > 0)
				{
					invalidCount++;
				}
			}
			else if (!list.Contains(result))
			{
				list.Add(result);
			}
		}
		list.Sort();
		return list.ToArray();
	}
}
internal enum GeneratorCategory
{
	NonFuel,
	Exchanger,
	HighGradeFuel,
	LowGradeThermal
}
internal sealed class DispatchConfig
{
	internal readonly int[] GroupMasks;

	internal readonly int GroupCount;

	internal readonly int[] CategoryPriorities;

	internal readonly int[] HighGradeThermalFuelIds;

	internal readonly int ChargingMaxSourcePriority;

	internal DispatchConfig(int[] groupMasks, int groupCount, int[] categoryPriorities, int[] highGradeThermalFuelIds, int chargingMaxSourcePriority)
	{
		GroupMasks = groupMasks;
		GroupCount = groupCount;
		CategoryPriorities = categoryPriorities;
		HighGradeThermalFuelIds = highGradeThermalFuelIds;
		ChargingMaxSourcePriority = chargingMaxSourcePriority;
	}
}
internal static class PriorityDispatch
{
	private sealed class DispatchState
	{
		internal PowerSystem PowerSystem;

		internal long PendingDemand;

		internal int PlanNetworkId = -1;

		internal bool PlanValid;

		internal int ScanNetworkId = -1;

		internal bool ScanValid;

		internal long ChargingCapacity;

		internal DispatchConfig ConfigSnapshot;

		internal readonly long[] Capacities = new long[4];

		internal readonly double[] Ratios = new double[4];
	}

	private static DispatchConfig config = new DispatchConfig(new int[4] { 1, 2, 4, 8 }, 4, new int[4] { 1, 2, 3, 4 }, new int[1] { 1801 }, 0);

	[ThreadStatic]
	private static DispatchState threadState;

	internal static void SetConfig(DispatchConfig newConfig)
	{
		config = newConfig;
	}

	internal static void BeginTick(PowerSystem powerSystem)
	{
		DispatchState dispatchState = threadState;
		if (dispatchState == null)
		{
			dispatchState = (threadState = new DispatchState());
		}
		dispatchState.PowerSystem = powerSystem;
		dispatchState.PendingDemand = 0L;
		dispatchState.PlanNetworkId = -1;
		dispatchState.PlanValid = false;
		dispatchState.ScanNetworkId = -1;
		dispatchState.ScanValid = false;
		dispatchState.ConfigSnapshot = null;
	}

	internal static void SetPendingDemand(long demand)
	{
		DispatchState dispatchState = threadState;
		if (dispatchState != null)
		{
			dispatchState.PendingDemand = ((demand > 0) ? demand : 0);
			dispatchState.PlanNetworkId = -1;
			dispatchState.PlanValid = false;
		}
	}

	internal static double GetChargingRatio(double vanillaRatio, PowerSystem powerSystem, ref PowerExchangerComponent exchanger, long vanillaSurplus)
	{
		if (powerSystem == null || exchanger.networkId <= 0 || vanillaSurplus <= 0)
		{
			return vanillaRatio;
		}
		DispatchConfig dispatchConfig = config;
		int chargingMaxSourcePriority = dispatchConfig.ChargingMaxSourcePriority;
		if (chargingMaxSourcePriority <= 0)
		{
			return vanillaRatio;
		}
		DispatchState dispatchState = EnsureNetworkScan(powerSystem, exchanger.networkId, dispatchConfig);
		if (dispatchState == null || dispatchState.ChargingCapacity <= 0)
		{
			return vanillaRatio;
		}
		long num = 0L;
		long num2 = 0L;
		for (int i = 0; i < 4; i++)
		{
			long num3 = dispatchState.Capacities[i];
			num += num3;
			if (dispatchConfig.CategoryPriorities[i] <= chargingMaxSourcePriority)
			{
				num2 += num3;
			}
		}
		long num4 = num - vanillaSurplus;
		if (num4 < 0)
		{
			num4 = 0L;
		}
		long num5 = num2 - num4;
		if (num5 <= 0)
		{
			return 0.0;
		}
		long num6 = ((num5 < vanillaSurplus) ? num5 : vanillaSurplus);
		if (num6 >= dispatchState.ChargingCapacity)
		{
			return 1.0;
		}
		return (double)num6 / (double)dispatchState.ChargingCapacity;
	}

	internal static double GetExchangerRatio(double vanillaRatio, PowerSystem powerSystem, ref PowerExchangerComponent exchanger)
	{
		if (powerSystem == null || exchanger.networkId <= 0)
		{
			return vanillaRatio;
		}
		DispatchState dispatchState = EnsurePlan(powerSystem, exchanger.networkId);
		if (dispatchState != null)
		{
			return dispatchState.Ratios[1];
		}
		return vanillaRatio;
	}

	internal static double GetGeneratorRatio(double vanillaRatio, PowerSystem powerSystem, ref PowerGeneratorComponent generator)
	{
		if (powerSystem == null || generator.networkId <= 0)
		{
			return vanillaRatio;
		}
		DispatchState dispatchState = EnsurePlan(powerSystem, generator.networkId);
		if (dispatchState == null || dispatchState.ConfigSnapshot == null)
		{
			return vanillaRatio;
		}
		GeneratorCategory generatorCategory = ClassifyGenerator(ref generator, dispatchState.ConfigSnapshot);
		return dispatchState.Ratios[(int)generatorCategory];
	}

	private static DispatchState EnsurePlan(PowerSystem powerSystem, int networkId)
	{
		DispatchState dispatchState = threadState;
		if (dispatchState == null || dispatchState.PowerSystem != powerSystem)
		{
			return null;
		}
		if (dispatchState.PlanValid && dispatchState.PlanNetworkId == networkId)
		{
			return dispatchState;
		}
		if (networkId <= 0 || networkId >= powerSystem.netPool.Length)
		{
			return null;
		}
		PowerNetwork val = powerSystem.netPool[networkId];
		if (val == null || val.id != networkId)
		{
			return null;
		}
		DispatchConfig dispatchConfig = config;
		dispatchState = EnsureNetworkScan(powerSystem, networkId, dispatchConfig);
		if (dispatchState == null)
		{
			return null;
		}
		Array.Clear(dispatchState.Ratios, 0, dispatchState.Ratios.Length);
		long num = dispatchState.PendingDemand;
		for (int i = 0; i < dispatchConfig.GroupCount; i++)
		{
			int num2 = dispatchConfig.GroupMasks[i];
			long num3 = 0L;
			for (int j = 0; j < 4; j++)
			{
				if ((num2 & (1 << j)) != 0)
				{
					num3 += dispatchState.Capacities[j];
				}
			}
			double num4;
			if (num3 <= 0 || num <= 0)
			{
				num4 = 0.0;
			}
			else if (num >= num3)
			{
				num4 = 1.0;
				num -= num3;
			}
			else
			{
				num4 = (double)num / (double)num3;
				num = 0L;
			}
			for (int k = 0; k < 4; k++)
			{
				if ((num2 & (1 << k)) != 0)
				{
					dispatchState.Ratios[k] = num4;
				}
			}
		}
		dispatchState.PlanNetworkId = networkId;
		dispatchState.PlanValid = true;
		return dispatchState;
	}

	private static DispatchState EnsureNetworkScan(PowerSystem powerSystem, int networkId, DispatchConfig snapshot)
	{
		DispatchState dispatchState = threadState;
		if (dispatchState == null || dispatchState.PowerSystem != powerSystem)
		{
			return null;
		}
		if (dispatchState.ScanValid && dispatchState.ScanNetworkId == networkId && dispatchState.ConfigSnapshot == snapshot)
		{
			return dispatchState;
		}
		if (networkId <= 0 || networkId >= powerSystem.netPool.Length)
		{
			return null;
		}
		PowerNetwork val = powerSystem.netPool[networkId];
		if (val == null || val.id != networkId)
		{
			return null;
		}
		Array.Clear(dispatchState.Capacities, 0, dispatchState.Capacities.Length);
		dispatchState.ChargingCapacity = 0L;
		dispatchState.ConfigSnapshot = snapshot;
		List<int> generators = val.generators;
		PowerGeneratorComponent[] genPool = powerSystem.genPool;
		for (int i = 0; i < generators.Count; i++)
		{
			int num = generators[i];
			if (num <= 0 || num >= genPool.Length)
			{
				continue;
			}
			ref PowerGeneratorComponent reference = ref genPool[num];
			if (reference.id == num && reference.productId == 0)
			{
				long capacityCurrentTick = reference.capacityCurrentTick;
				if (capacityCurrentTick > 0)
				{
					GeneratorCategory generatorCategory = ClassifyGenerator(ref reference, snapshot);
					dispatchState.Capacities[(int)generatorCategory] += capacityCurrentTick;
				}
			}
		}
		List<int> exchangers = val.exchangers;
		PowerExchangerComponent[] excPool = powerSystem.excPool;
		for (int j = 0; j < exchangers.Count; j++)
		{
			int num2 = exchangers[j];
			if (num2 <= 0 || num2 >= excPool.Length)
			{
				continue;
			}
			ref PowerExchangerComponent reference2 = ref excPool[num2];
			if (reference2.id != num2)
			{
				continue;
			}
			long capsCurrentTick = reference2.capsCurrentTick;
			if (capsCurrentTick > 0)
			{
				if (reference2.state <= -1f)
				{
					dispatchState.Capacities[1] += capsCurrentTick;
				}
				else if (reference2.state >= 1f)
				{
					dispatchState.ChargingCapacity += capsCurrentTick;
				}
			}
		}
		dispatchState.ScanNetworkId = networkId;
		dispatchState.ScanValid = true;
		return dispatchState;
	}

	private static GeneratorCategory ClassifyGenerator(ref PowerGeneratorComponent generator, DispatchConfig snapshot)
	{
		if (generator.wind || generator.photovoltaic || generator.gamma || generator.geothermal || generator.fuelMask <= 0)
		{
			return GeneratorCategory.NonFuel;
		}
		if (generator.fuelMask == 2 || generator.fuelMask == 4)
		{
			return GeneratorCategory.HighGradeFuel;
		}
		int num = 0;
		if (generator.fuelEnergy > 0 && generator.curFuelId > 0)
		{
			num = generator.curFuelId;
		}
		else if (generator.fuelId > 0)
		{
			num = generator.fuelId;
		}
		else if (generator.curFuelId > 0)
		{
			num = generator.curFuelId;
		}
		if (num > 0 && IsHighGradeThermalFuel(num, snapshot.HighGradeThermalFuelIds))
		{
			return GeneratorCategory.HighGradeFuel;
		}
		return GeneratorCategory.LowGradeThermal;
	}

	private static bool IsHighGradeThermalFuel(int fuelId, int[] ids)
	{
		foreach (int num in ids)
		{
			if (num == fuelId)
			{
				return true;
			}
			if (num > fuelId)
			{
				break;
			}
		}
		return false;
	}
}
[HarmonyPatch(typeof(PowerSystem), "GameTick")]
internal static class PowerSystem_GameTick_Patch
{
	private struct PatchSite
	{
		internal int RatioLoadIndex;

		internal int RatioStoreIndex;

		internal CodeInstruction ComponentLocalLoad;

		internal CodeInstruction DemandLocalLoad;
	}

	private static readonly FieldInfo ExcPoolField = AccessTools.Field(typeof(PowerSystem), "excPool");

	private static readonly FieldInfo ExchangerCapsField = AccessTools.Field(typeof(PowerExchangerComponent), "capsCurrentTick");

	private static readonly FieldInfo GeneratorCapacityField = AccessTools.Field(typeof(PowerGeneratorComponent), "capacityCurrentTick");

	private static readonly MethodInfo ExchangerInputUpdateMethod = AccessTools.Method(typeof(PowerExchangerComponent), "InputUpdate", (Type[])null, (Type[])null);

	private static readonly MethodInfo ExchangerOutputUpdateMethod = AccessTools.Method(typeof(PowerExchangerComponent), "OutputUpdate", (Type[])null, (Type[])null);

	private static readonly MethodInfo GeneratorFuelMethod = AccessTools.Method(typeof(PowerGeneratorComponent), "GenEnergyByFuel", (Type[])null, (Type[])null);

	private static readonly MethodInfo SetPendingDemandMethod = AccessTools.Method(typeof(PriorityDispatch), "SetPendingDemand", (Type[])null, (Type[])null);

	private static readonly MethodInfo GetChargingRatioMethod = AccessTools.Method(typeof(PriorityDispatch), "GetChargingRatio", (Type[])null, (Type[])null);

	private static readonly MethodInfo GetExchangerRatioMethod = AccessTools.Method(typeof(PriorityDispatch), "GetExchangerRatio", (Type[])null, (Type[])null);

	private static readonly MethodInfo GetGeneratorRatioMethod = AccessTools.Method(typeof(PriorityDispatch), "GetGeneratorRatio", (Type[])null, (Type[])null);

	[HarmonyPrefix]
	private static void Prefix(PowerSystem __instance)
	{
		PriorityDispatch.BeginTick(__instance);
	}

	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		MethodBody methodBody = original.GetMethodBody();
		if (methodBody == null)
		{
			Plugin.Log.LogError((object)"Power priority patch disabled: PowerSystem.GameTick has no method body.");
			return list;
		}
		if (!TryFindChargingSite(list, methodBody, out var site) || !TryFindExchangerSite(list, methodBody, out var site2) || !TryFindGeneratorSite(list, methodBody, out var site3))
		{
			Plugin.Log.LogError((object)"Power priority patch disabled: expected 0.10.34 PowerSystem.GameTick dispatch pattern was not found. Vanilla power dispatch will be used.");
			return list;
		}
		ReplaceGeneratorRatio(list, site3);
		ReplaceExchangerRatio(list, site2);
		InsertDemandCapture(list, site2);
		ReplaceChargingRatio(list, site);
		Plugin.Log.LogInfo((object)"PowerSystem.GameTick priority patch applied: exchanger charging limit, demand capture, exchanger dispatch ratio and generator dispatch ratio patched.");
		return list;
	}

	private static bool TryFindChargingSite(List<CodeInstruction> code, MethodBody body, out PatchSite site)
	{
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Expected O, but got Unknown
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Expected O, but got Unknown
		site = default(PatchSite);
		int num = FindUniqueCall(code, ExchangerInputUpdateMethod);
		if (num < 0)
		{
			return false;
		}
		int num2 = FindPreviousFieldLoad(code, num, ExchangerCapsField);
		if (num2 < 0)
		{
			return false;
		}
		int num3 = FindPreviousLocalLoadOfType(code, body, num2, typeof(double));
		if (num3 < 0)
		{
			return false;
		}
		int num4 = FindPreviousOpcode(code, num2, OpCodes.Ldelema);
		if (num4 < 1 || code[num4].operand as Type != typeof(PowerExchangerComponent))
		{
			return false;
		}
		int index = num4 - 1;
		if (!IsLoadLocal(code[index]))
		{
			return false;
		}
		int num5 = -1;
		for (int i = num2 + 1; i < num; i++)
		{
			if (IsLoadLocal(code[i]))
			{
				int localIndex = GetLocalIndex(code[i]);
				if (localIndex >= 0 && localIndex < body.LocalVariables.Count && body.LocalVariables[localIndex].LocalType == typeof(long))
				{
					num5 = i;
					break;
				}
			}
		}
		if (num5 < 0)
		{
			return false;
		}
		site = new PatchSite
		{
			RatioLoadIndex = num3,
			ComponentLocalLoad = new CodeInstruction(code[index]),
			DemandLocalLoad = new CodeInstruction(code[num5])
		};
		return true;
	}

	private static bool TryFindExchangerSite(List<CodeInstruction> code, MethodBody body, out PatchSite site)
	{
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Expected O, but got Unknown
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Expected O, but got Unknown
		site = default(PatchSite);
		int num = FindUniqueCall(code, ExchangerOutputUpdateMethod);
		if (num < 0)
		{
			return false;
		}
		int num2 = FindPreviousFieldLoad(code, num, ExchangerCapsField);
		if (num2 < 0)
		{
			return false;
		}
		int num3 = FindPreviousLocalLoadOfType(code, body, num2, typeof(double));
		if (num3 < 0)
		{
			return false;
		}
		int localIndex = GetLocalIndex(code[num3]);
		if (localIndex < 0)
		{
			return false;
		}
		int num4 = FindPreviousStoreToLocal(code, num3, localIndex);
		if (num4 < 0)
		{
			return false;
		}
		int num5 = FindPreviousOpcode(code, num2, OpCodes.Ldelema);
		if (num5 < 1 || code[num5].operand as Type != typeof(PowerExchangerComponent))
		{
			return false;
		}
		int index = num5 - 1;
		if (!IsLoadLocal(code[index]))
		{
			return false;
		}
		int num6 = -1;
		for (int i = num2 + 1; i < num; i++)
		{
			if (IsLoadLocal(code[i]))
			{
				int localIndex2 = GetLocalIndex(code[i]);
				if (localIndex2 >= 0 && body.LocalVariables[localIndex2].LocalType == typeof(long))
				{
					num6 = i;
					break;
				}
			}
		}
		if (num6 < 0)
		{
			return false;
		}
		site = new PatchSite
		{
			RatioLoadIndex = num3,
			RatioStoreIndex = num4,
			ComponentLocalLoad = new CodeInstruction(code[index]),
			DemandLocalLoad = new CodeInstruction(code[num6])
		};
		return true;
	}

	private static bool TryFindGeneratorSite(List<CodeInstruction> code, MethodBody body, out PatchSite site)
	{
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Expected O, but got Unknown
		site = default(PatchSite);
		int num = FindUniqueCall(code, GeneratorFuelMethod);
		if (num < 0)
		{
			return false;
		}
		int num2 = FindPreviousFieldLoad(code, num, GeneratorCapacityField);
		if (num2 < 0)
		{
			return false;
		}
		int num3 = FindPreviousLocalLoadOfType(code, body, num2, typeof(double));
		if (num3 < 0)
		{
			return false;
		}
		int num4 = num2 - 1;
		if (num4 < 0 || !IsLoadLocal(code[num4]))
		{
			return false;
		}
		int localIndex = GetLocalIndex(code[num4]);
		if (localIndex < 0)
		{
			return false;
		}
		Type localType = body.LocalVariables[localIndex].LocalType;
		if (!localType.IsByRef || localType.GetElementType() != typeof(PowerGeneratorComponent))
		{
			return false;
		}
		site = new PatchSite
		{
			RatioLoadIndex = num3,
			ComponentLocalLoad = new CodeInstruction(code[num4])
		};
		return true;
	}

	private static void InsertDemandCapture(List<CodeInstruction> code, PatchSite site)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		CodeInstruction val = new CodeInstruction(site.DemandLocalLoad);
		CodeInstruction val2 = new CodeInstruction(OpCodes.Call, (object)SetPendingDemandMethod);
		code.InsertRange(site.RatioStoreIndex + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2] { val, val2 });
	}

	private static void ReplaceChargingRatio(List<CodeInstruction> code, PatchSite site)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Expected O, but got Unknown
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Expected O, but got Unknown
		CodeInstruction val = code[site.RatioLoadIndex];
		List<CodeInstruction> list = new List<CodeInstruction>
		{
			new CodeInstruction(val.opcode, val.operand),
			new CodeInstruction(OpCodes.Ldarg_0, (object)null),
			new CodeInstruction(OpCodes.Ldarg_0, (object)null),
			new CodeInstruction(OpCodes.Ldfld, (object)ExcPoolField),
			new CodeInstruction(site.ComponentLocalLoad),
			new CodeInstruction(OpCodes.Ldelema, (object)typeof(PowerExchangerComponent)),
			new CodeInstruction(site.DemandLocalLoad),
			new CodeInstruction(OpCodes.Call, (object)GetChargingRatioMethod)
		};
		MoveLabelsAndBlocks(val, list[0]);
		code.RemoveAt(site.RatioLoadIndex);
		code.InsertRange(site.RatioLoadIndex, list);
	}

	private static void ReplaceExchangerRatio(List<CodeInstruction> code, PatchSite site)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Expected O, but got Unknown
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Expected O, but got Unknown
		CodeInstruction val = code[site.RatioLoadIndex];
		List<CodeInstruction> list = new List<CodeInstruction>
		{
			new CodeInstruction(val.opcode, val.operand),
			new CodeInstruction(OpCodes.Ldarg_0, (object)null),
			new CodeInstruction(OpCodes.Ldarg_0, (object)null),
			new CodeInstruction(OpCodes.Ldfld, (object)ExcPoolField),
			new CodeInstruction(site.ComponentLocalLoad),
			new CodeInstruction(OpCodes.Ldelema, (object)typeof(PowerExchangerComponent)),
			new CodeInstruction(OpCodes.Call, (object)GetExchangerRatioMethod)
		};
		MoveLabelsAndBlocks(val, list[0]);
		code.RemoveAt(site.RatioLoadIndex);
		code.InsertRange(site.RatioLoadIndex, list);
	}

	private static void ReplaceGeneratorRatio(List<CodeInstruction> code, PatchSite site)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Expected O, but got Unknown
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		CodeInstruction val = code[site.RatioLoadIndex];
		List<CodeInstruction> list = new List<CodeInstruction>
		{
			new CodeInstruction(val.opcode, val.operand),
			new CodeInstruction(OpCodes.Ldarg_0, (object)null),
			new CodeInstruction(site.ComponentLocalLoad),
			new CodeInstruction(OpCodes.Call, (object)GetGeneratorRatioMethod)
		};
		MoveLabelsAndBlocks(val, list[0]);
		code.RemoveAt(site.RatioLoadIndex);
		code.InsertRange(site.RatioLoadIndex, list);
	}

	private static int FindUniqueCall(List<CodeInstruction> code, MethodInfo method)
	{
		int num = -1;
		for (int i = 0; i < code.Count; i++)
		{
			if ((code[i].opcode == OpCodes.Call || code[i].opcode == OpCodes.Callvirt) && object.Equals(code[i].operand, method))
			{
				if (num >= 0)
				{
					return -1;
				}
				num = i;
			}
		}
		return num;
	}

	private static int FindPreviousFieldLoad(List<CodeInstruction> code, int startExclusive, FieldInfo field)
	{
		for (int num = startExclusive - 1; num >= 0; num--)
		{
			if (code[num].opcode == OpCodes.Ldfld && object.Equals(code[num].operand, field))
			{
				return num;
			}
		}
		return -1;
	}

	private static int FindPreviousLocalLoadOfType(List<CodeInstruction> code, MethodBody body, int startExclusive, Type type)
	{
		int num = startExclusive - 1;
		while (num >= 0 && num >= startExclusive - 20)
		{
			if (IsLoadLocal(code[num]))
			{
				int localIndex = GetLocalIndex(code[num]);
				if (localIndex >= 0 && localIndex < body.LocalVariables.Count && body.LocalVariables[localIndex].LocalType == type)
				{
					return num;
				}
			}
			num--;
		}
		return -1;
	}

	private static int FindPreviousStoreToLocal(List<CodeInstruction> code, int startExclusive, int localIndex)
	{
		for (int num = startExclusive - 1; num >= 0; num--)
		{
			if (IsStoreLocal(code[num]) && GetLocalIndex(code[num]) == localIndex)
			{
				return num;
			}
		}
		return -1;
	}

	private static int FindPreviousOpcode(List<CodeInstruction> code, int startExclusive, OpCode opcode)
	{
		int num = startExclusive - 1;
		while (num >= 0 && num >= startExclusive - 10)
		{
			if (code[num].opcode == opcode)
			{
				return num;
			}
			num--;
		}
		return -1;
	}

	private static bool IsLoadLocal(CodeInstruction instruction)
	{
		OpCode opcode = instruction.opcode;
		if (!(opcode == OpCodes.Ldloc) && !(opcode == OpCodes.Ldloc_S) && !(opcode == OpCodes.Ldloc_0) && !(opcode == OpCodes.Ldloc_1) && !(opcode == OpCodes.Ldloc_2))
		{
			return opcode == OpCodes.Ldloc_3;
		}
		return true;
	}

	private static bool IsStoreLocal(CodeInstruction instruction)
	{
		OpCode opcode = instruction.opcode;
		if (!(opcode == OpCodes.Stloc) && !(opcode == OpCodes.Stloc_S) && !(opcode == OpCodes.Stloc_0) && !(opcode == OpCodes.Stloc_1) && !(opcode == OpCodes.Stloc_2))
		{
			return opcode == OpCodes.Stloc_3;
		}
		return true;
	}

	private static int GetLocalIndex(CodeInstruction instruction)
	{
		if (instruction.opcode == OpCodes.Ldloc_0 || instruction.opcode == OpCodes.Stloc_0)
		{
			return 0;
		}
		if (instruction.opcode == OpCodes.Ldloc_1 || instruction.opcode == OpCodes.Stloc_1)
		{
			return 1;
		}
		if (instruction.opcode == OpCodes.Ldloc_2 || instruction.opcode == OpCodes.Stloc_2)
		{
			return 2;
		}
		if (instruction.opcode == OpCodes.Ldloc_3 || instruction.opcode == OpCodes.Stloc_3)
		{
			return 3;
		}
		if (instruction.operand is LocalBuilder localBuilder)
		{
			return localBuilder.LocalIndex;
		}
		object operand = instruction.operand;
		if (operand is byte)
		{
			return (byte)operand;
		}
		operand = instruction.operand;
		if (operand is int)
		{
			return (int)operand;
		}
		return -1;
	}

	private static void MoveLabelsAndBlocks(CodeInstruction from, CodeInstruction to)
	{
		if (from.labels != null && from.labels.Count > 0)
		{
			to.labels.AddRange(from.labels);
			from.labels.Clear();
		}
		if (from.blocks != null && from.blocks.Count > 0)
		{
			to.blocks.AddRange(from.blocks);
			from.blocks.Clear();
		}
	}
}