Decompiled source of DSPGuideCheck v3.2.113

BepInEx/plugins/DSP-Guide-Check/DspGuideCheck.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("DspGuideCheck")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("An on-demand progression companion for Dyson Sphere Program.")]
[assembly: AssemblyFileVersion("3.2.113.0")]
[assembly: AssemblyInformationalVersion("3.2.113.d46a7bf")]
[assembly: AssemblyProduct("DSP Guide Check")]
[assembly: AssemblyTitle("DspGuideCheck")]
[assembly: AssemblyVersion("3.2.113.0")]
namespace DspProgressionStatusExporter;

internal static class BlueprintImporter
{
	private const string ResourceName = "DspGuideCheck.Blueprints.DSP-Guide-Blueprint-Collection-Ready.zip";

	internal static void Import(string blueprintFolder)
	{
		if (string.IsNullOrWhiteSpace(blueprintFolder) || !Path.IsPathRooted(blueprintFolder))
		{
			throw new InvalidOperationException("DSP's blueprint folder is unavailable.");
		}
		string fullPath = Path.GetFullPath(Path.Combine(blueprintFolder, "Guide Check"));
		char directorySeparatorChar = Path.DirectorySeparatorChar;
		string value = fullPath + directorySeparatorChar;
		using Stream stream = typeof(BlueprintImporter).Assembly.GetManifestResourceStream("DspGuideCheck.Blueprints.DSP-Guide-Blueprint-Collection-Ready.zip");
		if (stream == null)
		{
			throw new InvalidOperationException("The bundled blueprint pack is unavailable.");
		}
		using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read);
		foreach (ZipArchiveEntry entry in zipArchive.Entries)
		{
			string text = entry.FullName.Replace('/', Path.DirectorySeparatorChar);
			string fullPath2 = Path.GetFullPath(Path.Combine(fullPath, text));
			if (Path.IsPathRooted(text) || !fullPath2.StartsWith(value, StringComparison.OrdinalIgnoreCase))
			{
				throw new InvalidDataException("A blueprint pack path leaves Guide Check.");
			}
			RejectLinks(fullPath2, fullPath);
			if (string.IsNullOrEmpty(entry.Name))
			{
				Directory.CreateDirectory(fullPath2);
				continue;
			}
			Directory.CreateDirectory(Path.GetDirectoryName(fullPath2));
			using Stream stream2 = entry.Open();
			using FileStream destination = new FileStream(fullPath2, FileMode.Create, FileAccess.Write);
			stream2.CopyTo(destination);
		}
	}

	private static void RejectLinks(string destination, string root)
	{
		string text = destination;
		while (text != null && text.Length >= root.Length)
		{
			if ((File.Exists(text) || Directory.Exists(text)) && (File.GetAttributes(text) & FileAttributes.ReparsePoint) != FileAttributes.None)
			{
				throw new IOException("Guide Check contains a redirected file or folder.");
			}
			text = Path.GetDirectoryName(text);
		}
	}
}
public static class BuildFeatures
{
	public const string Variant = "public";

	public const bool SnapshotControlEnabled = false;
}
internal static class BuildVersion
{
	public const string BepInPluginVersion = "3.2.113";

	public const string PluginVersion = "3.2.113";

	public const string ReleaseLabel = "3.2.113.d46a7bf";
}
internal static class CompactSnapshotBuilder
{
	private const int ReceiverDetailLimit = 8;

	private const int BufferSourceDetailLimit = 12;

	private static readonly int[] CubeIds = new int[6] { 6001, 6002, 6003, 6004, 6005, 6006 };

	private static readonly int[] IlsPlayerItems = new int[10] { 1105, 1106, 2001, 2011, 2101, 2201, 2203, 2204, 2301, 2302 };

	private static readonly int[] IlsCargoItems = new int[4] { 1003, 1004, 1105, 1106 };

	private static readonly Dictionary<string, int[]> PhaseItems = new Dictionary<string, int[]>(StringComparer.OrdinalIgnoreCase)
	{
		{
			"blue",
			new int[14]
			{
				1101, 1104, 1202, 1301, 2001, 2011, 2301, 2302, 2303, 2101,
				2106, 2203, 2201, 6001
			}
		},
		{
			"red",
			new int[3] { 6002, 1114, 1120 }
		},
		{
			"ils",
			new int[14]
			{
				1003, 1004, 1105, 1106, 2001, 2011, 2101, 2104, 2201, 2203,
				2204, 2301, 2302, 5002
			}
		},
		{
			"yellow",
			new int[3] { 6003, 1112, 1118 }
		},
		{
			"purple",
			new int[3] { 6004, 1303, 1402 }
		},
		{
			"green",
			new int[3] { 6005, 1305, 1209 }
		},
		{
			"dyson",
			new int[3] { 1501, 1208, 1122 }
		},
		{
			"photon",
			new int[7] { 6001, 6002, 6003, 6004, 6005, 1208, 1122 }
		},
		{
			"white",
			new int[7] { 6001, 6002, 6003, 6004, 6005, 6006, 1122 }
		}
	};

	private static readonly Dictionary<string, int> PhaseRecipeIds = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
	{
		{ "blue", 9 },
		{ "red", 18 },
		{ "yellow", 27 },
		{ "purple", 55 },
		{ "green", 102 },
		{ "dyson", 74 },
		{ "white", 75 }
	};

	public static Dictionary<string, object> Build(string schemaVersion, string exporterVersion, Dictionary<string, object> provenance, Dictionary<string, object> game, Dictionary<string, object> location, Dictionary<string, object> research, ObservedGameState state, Dictionary<string, object> selection, Dictionary<string, object> analysis, Dictionary<string, object> presentation, Dictionary<string, object> samplingPerformance, bool includeCollectorDiagnostics)
	{
		string phaseId = Text(Value(Dictionary(analysis, "phase"), "id"));
		Dictionary<string, object> obj = new Dictionary<string, object>
		{
			{ "schemaVersion", schemaVersion },
			{ "exporterVersion", exporterVersion },
			{
				"exportedAtUtc",
				DateTime.UtcNow.ToString("o")
			},
			{ "provenance", provenance },
			{ "game", game },
			{ "location", location },
			{ "guideSelection", selection },
			{
				"guide",
				GuideSummary(analysis)
			},
			{ "presentation", presentation },
			{
				"summary",
				new Dictionary<string, object>
				{
					{
						"research",
						ResearchSummary(research, state)
					},
					{
						"cubes",
						CubeSummary(state)
					}
				}
			},
			{
				"evidence",
				Evidence(phaseId, state, samplingPerformance, includeCollectorDiagnostics)
			},
			{
				"omissions",
				new Dictionary<string, object>
				{
					{ "policy", "Only conclusions and evidence used by implemented guide functions are exported." },
					{
						"omitted",
						new List<object> { "raw factory and entity state", "all-technology rows", "player and mecha detail", "broad inventory detail", "all station slots", "all-item and per-factory telemetry", "broad reflection diagnostics" }
					},
					{
						"detailsTruncated",
						state.Dyson.Receivers.Count > 8
					}
				}
			}
		};
		Validate(obj);
		return obj;
	}

	private static Dictionary<string, object> GuideSummary(Dictionary<string, object> analysis)
	{
		return new Dictionary<string, object>
		{
			{
				"analysisVersion",
				Value(analysis, "analysisVersion")
			},
			{
				"selectionAuthority",
				Value(analysis, "phaseSelectionAuthority")
			},
			{
				"phase",
				Value(analysis, "phase")
			},
			{
				"objectives",
				Value(analysis, "progression")
			},
			{
				"ilsResearch",
				Value(analysis, "ilsResearch")
			},
			{
				"ilsActions",
				Value(analysis, "ilsActions")
			},
			{
				"researchQueueAvailable",
				Value(analysis, "researchQueueAvailable")
			},
			{
				"productionRisk",
				Value(analysis, "productionRisk")
			},
			{
				"currentStatus",
				Value(analysis, "findings")
			},
			{
				"limitations",
				Value(analysis, "limitations")
			}
		};
	}

	private static Dictionary<string, object> ResearchSummary(Dictionary<string, object> research, ObservedGameState state)
	{
		int num = 0;
		foreach (object item in Enumerate(Value(research, "technologies")))
		{
			_ = item;
			num++;
		}
		int num2 = 0;
		foreach (object item2 in Enumerate(Value(research, "techQueue")))
		{
			_ = item2;
			num2++;
		}
		Dictionary<string, object> dictionary = new Dictionary<string, object>
		{
			{
				"available",
				Bool(Value(research, "available"))
			},
			{ "totalTechnologies", num },
			{
				"unlockedTechnologies",
				state.UnlockedTechIds.Count
			},
			{ "queuedTechnologies", num2 },
			{
				"currentTechId",
				Value(research, "currentTech")
			},
			{
				"missionAccomplished",
				Value(research, "missionAccomplished")
			}
		};
		if (state.TechProgress.TryGetValue(1508, out var value))
		{
			dictionary["missionCompletedProgress"] = new Dictionary<string, object>
			{
				{ "available", true },
				{ "hashUploaded", value.HashUploaded },
				{ "hashNeeded", value.HashNeeded },
				{ "percent", value.Percent }
			};
		}
		else
		{
			dictionary["missionCompletedProgress"] = new Dictionary<string, object> { { "available", false } };
		}
		return dictionary;
	}

	private static List<object> CubeSummary(ObservedGameState state)
	{
		List<object> list = new List<object>();
		int[] cubeIds = CubeIds;
		foreach (int itemId in cubeIds)
		{
			list.Add(ItemEvidence(state, itemId, includeLifetime: true, includeBufferEvidence: false));
		}
		return list;
	}

	private static Dictionary<string, object> Evidence(string phaseId, ObservedGameState state, Dictionary<string, object> samplingPerformance, bool includeCollectorDiagnostics)
	{
		Dictionary<string, object> dictionary = new Dictionary<string, object>
		{
			{
				"collectors",
				CollectorHealth(state, samplingPerformance, includeCollectorDiagnostics)
			},
			{
				"phaseItems",
				PhaseItemEvidence(phaseId, state)
			},
			{
				"phaseRecipe",
				RecipeEvidence(phaseId, state)
			},
			{
				"logistics",
				LogisticsEvidence(phaseId, state)
			},
			{
				"power",
				PowerEvidence(state)
			}
		};
		if (phaseId == "photon")
		{
			dictionary["photonReadiness"] = state.ExportPhotonInputs();
		}
		if (IsDysonPhase(phaseId))
		{
			dictionary["dyson"] = DysonEvidence(phaseId, state);
		}
		return dictionary;
	}

	private static Dictionary<string, object> CollectorHealth(ObservedGameState state, Dictionary<string, object> samplingPerformance, bool includeCollectorDiagnostics)
	{
		Dictionary<string, object> dictionary = new Dictionary<string, object>
		{
			{ "available", state.ProductionWindowReady },
			{ "source", state.ProductionSource },
			{ "scope", state.ProductionScope },
			{ "period", state.ProductionPeriod },
			{ "windowGameSeconds", state.ProductionWindowSeconds },
			{ "sampleCount", state.ProductionSampleCount },
			{ "watchedItemCount", state.ProductionWatchedItemCount },
			{ "itemCoverage", state.ProductionItemCoverage },
			{ "factoryCount", state.ProductionFactoryCount },
			{
				"windows",
				new Dictionary<string, object>
				{
					{
						"oneMinute",
						new Dictionary<string, object>
						{
							{ "available", state.ProductionWindowReady },
							{ "ready", state.ProductionWindowReady },
							{
								"status",
								state.ProductionWindowReady ? "ready" : "unavailable"
							},
							{ "windowGameSeconds", state.ProductionWindowSeconds }
						}
					},
					{
						"tenMinute",
						new Dictionary<string, object>
						{
							{ "available", state.ProductionTenMinuteWindowAvailable },
							{ "ready", state.ProductionTenMinuteWindowReady },
							{ "status", state.ProductionTenMinuteWindowStatus },
							{ "windowGameSeconds", state.ProductionTenMinuteWindowSeconds },
							{ "readinessSource", state.ProductionTenMinuteReadinessSource },
							{ "availableItemCount", state.ProductionTenMinuteAvailableItemCount },
							{ "readyItemCount", state.ProductionTenMinuteReadyItemCount }
						}
					}
				}
			}
		};
		if (!string.IsNullOrEmpty(state.ProductionFailure))
		{
			dictionary["failure"] = state.ProductionFailure;
		}
		Dictionary<string, object> dictionary2 = new Dictionary<string, object>
		{
			{ "cadenceSeconds", 5.0 },
			{ "production", dictionary },
			{
				"traffic",
				new Dictionary<string, object>
				{
					{ "available", state.TrafficWindowReady },
					{ "windowGameSeconds", state.TrafficWindowSeconds }
				}
			},
			{
				"power",
				new Dictionary<string, object>
				{
					{
						"available",
						state.PowerPlanets.Count > 0
					},
					{ "windowGameSeconds", state.PowerWindowSeconds },
					{
						"planetCount",
						state.PowerPlanets.Count
					}
				}
			},
			{
				"recipes",
				new Dictionary<string, object> { { "available", state.RecipeTelemetryAvailable } }
			},
			{
				"receiverContinuity",
				new Dictionary<string, object>
				{
					{
						"available",
						state.Dyson.ReceiverTelemetryAvailable
					},
					{
						"windowSeconds",
						state.Dyson.ReceiverContinuityWindowSeconds
					}
				}
			}
		};
		if (includeCollectorDiagnostics)
		{
			dictionary2["performance"] = samplingPerformance;
		}
		return dictionary2;
	}

	private static List<object> PhaseItemEvidence(string phaseId, ObservedGameState state)
	{
		if (!PhaseItems.TryGetValue(phaseId ?? "", out var value))
		{
			value = new int[0];
		}
		List<object> list = new List<object>();
		int[] array = value;
		foreach (int itemId in array)
		{
			list.Add(ItemEvidence(state, itemId, includeLifetime: false, includeBufferEvidence: true));
		}
		return list;
	}

	private static Dictionary<string, object> ItemEvidence(ObservedGameState state, int itemId, bool includeLifetime, bool includeBufferEvidence)
	{
		state.ItemFlows.TryGetValue(itemId, out var value);
		state.LifetimeItemTotals.TryGetValue(itemId, out var value2);
		state.OwnedItemCounts.TryGetValue(itemId, out var value3);
		string value4 = ((value != null) ? value.Name : ((value2 != null) ? value2.Name : Plugin.ItemName(itemId)));
		Dictionary<string, object> dictionary = new Dictionary<string, object>
		{
			{ "itemId", itemId },
			{ "name", value4 },
			{ "owned", value3 },
			{
				"nativeWindowAvailable",
				state.ProductionWindowReady && value != null
			},
			{
				"producedPerMinute",
				(value != null) ? ((object)value.ProducedPerMinute) : null
			},
			{
				"consumedPerMinute",
				(value != null) ? ((object)value.ConsumedPerMinute) : null
			},
			{
				"netPerMinute",
				(value != null) ? ((object)value.NetPerMinute) : null
			},
			{
				"observedIntervals",
				(value != null) ? ((object)value.ObservedIntervals) : null
			},
			{
				"productionActiveFraction",
				(value != null) ? ((object)value.ProductionActiveFraction) : null
			},
			{
				"productionContinuity",
				value?.ProductionContinuity
			}
		};
		dictionary["nativeWindows"] = new Dictionary<string, object>
		{
			{
				"oneMinute",
				new Dictionary<string, object>
				{
					{
						"available",
						value?.OneMinuteAvailable ?? false
					},
					{
						"status",
						(value != null) ? value.OneMinuteStatus : "unavailable"
					},
					{
						"producedPerMinute",
						(value != null && value.OneMinuteAvailable) ? ((object)value.ProducedPerMinute) : null
					},
					{
						"consumedPerMinute",
						(value != null && value.OneMinuteAvailable) ? ((object)value.ConsumedPerMinute) : null
					},
					{
						"netPerMinute",
						(value != null && value.OneMinuteAvailable) ? ((object)value.NetPerMinute) : null
					}
				}
			},
			{
				"tenMinute",
				new Dictionary<string, object>
				{
					{
						"available",
						value?.TenMinuteAvailable ?? false
					},
					{
						"ready",
						value?.TenMinuteReady ?? false
					},
					{
						"status",
						(value != null) ? value.TenMinuteStatus : "unavailable"
					},
					{
						"observedGameSeconds",
						(value != null) ? ((object)value.TenMinuteObservedGameSeconds) : null
					},
					{
						"producedPerMinute",
						(value != null && value.TenMinuteAvailable) ? ((object)value.TenMinuteProducedPerMinute) : null
					},
					{
						"consumedPerMinute",
						(value != null && value.TenMinuteAvailable) ? ((object)value.TenMinuteConsumedPerMinute) : null
					},
					{
						"netPerMinute",
						(value != null && value.TenMinuteAvailable) ? ((object)value.TenMinuteNetPerMinute) : null
					}
				}
			}
		};
		if (itemId == 1122)
		{
			dictionary["stationaryStock"] = GuideGateEngine.StationaryStock(state, itemId, out var available);
			dictionary["stationaryStockAvailable"] = available;
			dictionary["stationaryScope"] = "Observed cluster storage and station stock; excludes Icarus.";
		}
		if (includeLifetime)
		{
			dictionary["lifetimeProduced"] = ((value2 != null) ? ((object)value2.Produced) : null);
			dictionary["lifetimeConsumed"] = ((value2 != null) ? ((object)value2.Consumed) : null);
		}
		if (includeBufferEvidence)
		{
			dictionary["buffer"] = BufferEvidence(state, itemId);
		}
		return dictionary;
	}

	private static Dictionary<string, object> BufferEvidence(ObservedGameState state, int itemId)
	{
		if (!state.ItemBuffers.TryGetValue(itemId, out var value))
		{
			return new Dictionary<string, object>
			{
				{ "sourcePolicy", "Item-configured logistics slots in local Supply mode only." },
				{ "scopePolicy", "Planet-local stock and planet-local native consumption." },
				{ "importsExportsIncluded", false },
				{ "eligibleSourceAvailable", false },
				{ "accessibleCount", 0L },
				{ "accessibleCapacity", 0L },
				{ "backpressureStatus", "unknown" },
				{
					"scopes",
					new List<object>()
				},
				{
					"excludedSources",
					new List<object>()
				}
			};
		}
		List<object> list = new List<object>();
		foreach (ObservedBufferScopeEvidence scope in value.Scopes)
		{
			int num = Math.Min(scope.Contributors.Count, 12);
			List<object> list2 = new List<object>();
			for (int i = 0; i < num; i++)
			{
				list2.Add(BufferSource(scope.Contributors[i]));
			}
			list.Add(new Dictionary<string, object>
			{
				{ "planetId", scope.PlanetId },
				{ "planetName", scope.PlanetName },
				{ "accessibleCount", scope.AccessibleCount },
				{ "accessibleCapacity", scope.AccessibleCapacity },
				{ "demandEvidenceAvailable", scope.DemandEvidenceAvailable },
				{
					"demandPerMinute",
					scope.DemandEvidenceAvailable ? ((object)scope.DemandPerMinute) : null
				},
				{ "runwayAvailable", scope.RunwayAvailable },
				{
					"runwayMinutes",
					scope.RunwayAvailable ? ((object)scope.RunwayMinutes) : null
				},
				{ "backpressureStatus", scope.BackpressureStatus },
				{
					"contributorCount",
					scope.Contributors.Count
				},
				{ "contributors", list2 },
				{
					"contributorsTruncated",
					scope.Contributors.Count > num
				}
			});
		}
		int num2 = Math.Min(value.ExcludedSources.Count, 12);
		List<object> list3 = new List<object>();
		for (int j = 0; j < num2; j++)
		{
			list3.Add(BufferSource(value.ExcludedSources[j]));
		}
		return new Dictionary<string, object>
		{
			{ "sourcePolicy", "Item-configured logistics slots in local Supply mode only." },
			{ "scopePolicy", "Planet-local stock and planet-local native consumption." },
			{ "importsExportsIncluded", false },
			{
				"eligibleSourceAvailable",
				value.Scopes.Count > 0
			},
			{ "accessibleCount", value.AccessibleCount },
			{ "accessibleCapacity", value.AccessibleCapacity },
			{ "backpressureStatus", value.BackpressureStatus },
			{ "scopes", list },
			{
				"excludedSourceCount",
				value.ExcludedSources.Count
			},
			{ "excludedSources", list3 },
			{
				"excludedSourcesTruncated",
				value.ExcludedSources.Count > num2
			}
		};
	}

	private static Dictionary<string, object> BufferSource(ObservedBufferSourceEvidence source)
	{
		Dictionary<string, object> dictionary = new Dictionary<string, object>
		{
			{ "sourceType", source.SourceType },
			{ "planetId", source.PlanetId },
			{ "planetName", source.PlanetName },
			{ "stationId", source.StationId },
			{ "count", source.Count },
			{ "capacity", source.Capacity },
			{ "localLogic", source.LocalLogic },
			{ "remoteLogic", source.RemoteLogic }
		};
		if (!string.IsNullOrEmpty(source.ExclusionReason))
		{
			dictionary["exclusionReason"] = source.ExclusionReason;
		}
		return dictionary;
	}

	private static Dictionary<string, object> LogisticsEvidence(string phaseId, ObservedGameState state)
	{
		if (!PhaseItems.TryGetValue(phaseId ?? "", out var value))
		{
			value = new int[0];
		}
		HashSet<int> hashSet = new HashSet<int>(value);
		Dictionary<int, Dictionary<string, object>> dictionary = new Dictionary<int, Dictionary<string, object>>();
		foreach (ObservedTrafficFlow trafficFlow in state.TrafficFlows)
		{
			if (hashSet.Contains(trafficFlow.ItemId))
			{
				if (!dictionary.TryGetValue(trafficFlow.ItemId, out var value2))
				{
					value2 = new Dictionary<string, object>
					{
						{ "itemId", trafficFlow.ItemId },
						{ "name", trafficFlow.Name },
						{ "inputPerMinute", 0.0 },
						{ "outputPerMinute", 0.0 },
						{ "internalPerMinute", 0.0 }
					};
					dictionary[trafficFlow.ItemId] = value2;
				}
				value2["inputPerMinute"] = Plugin.ToDouble(value2["inputPerMinute"]) + trafficFlow.InputPerMinute;
				value2["outputPerMinute"] = Plugin.ToDouble(value2["outputPerMinute"]) + trafficFlow.OutputPerMinute;
				value2["internalPerMinute"] = Plugin.ToDouble(value2["internalPerMinute"]) + trafficFlow.InternalPerMinute;
			}
		}
		List<object> list = new List<object>();
		List<int> list2 = new List<int>(dictionary.Keys);
		list2.Sort();
		foreach (int item in list2)
		{
			list.Add(dictionary[item]);
		}
		Dictionary<string, object> dictionary2 = new Dictionary<string, object>
		{
			{ "available", state.TrafficWindowReady },
			{ "windowGameSeconds", state.TrafficWindowSeconds },
			{ "selectedPhaseItems", list }
		};
		if (string.Equals(phaseId, "ils", StringComparison.OrdinalIgnoreCase))
		{
			dictionary2["stageEvidence"] = IlsStageEvidence(state);
		}
		return dictionary2;
	}

	private static Dictionary<string, object> IlsStageEvidence(ObservedGameState state)
	{
		return new Dictionary<string, object>
		{
			{ "playerPlanetId", state.PlayerPlanetId },
			{ "starterPlanetId", state.StarterPlanetId },
			{
				"selectedOutpostPlanetId",
				GuideGateEngine.FindExpeditionPlanet(state)
			},
			{ "playerLocationAvailable", state.PlayerLocationAvailable },
			{ "playerInventoryAvailable", state.PlayerInventoryAvailable },
			{
				"availablePlanetInventories",
				new List<int>(state.AvailablePlanetInventories)
			},
			{
				"departureResearchAvailable",
				state.AvailableTechIds.Contains(2902) && state.AvailableTechIds.Contains(1413)
			},
			{
				"playerInventory",
				CountEvidence(state.PlayerItemCounts, IlsPlayerItems)
			},
			{
				"planetCargo",
				IlsPlanetCargoEvidence(state)
			},
			{
				"transportPackage",
				IlsTransportEvidence.Build(state).Export()
			},
			{
				"homeDelivery",
				state.IlsReceipt.Export()
			}
		};
	}

	private static List<object> IlsPlanetCargoEvidence(ObservedGameState state)
	{
		HashSet<int> hashSet = new HashSet<int>();
		foreach (int key in state.PlanetItemCounts.Keys)
		{
			hashSet.Add(key);
		}
		foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows)
		{
			if (Array.IndexOf(IlsCargoItems, factoryItemFlow.ItemId) >= 0)
			{
				hashSet.Add(factoryItemFlow.PlanetId);
			}
		}
		List<int> list = new List<int>(hashSet);
		list.Sort();
		List<object> list2 = new List<object>();
		foreach (int item in list)
		{
			if (!state.PlanetItemCounts.TryGetValue(item, out var value))
			{
				value = new Dictionary<int, long>();
			}
			Dictionary<int, double> dictionary = new Dictionary<int, double>();
			foreach (ObservedFactoryItemFlow factoryItemFlow2 in state.FactoryItemFlows)
			{
				if (factoryItemFlow2.PlanetId == item && Array.IndexOf(IlsCargoItems, factoryItemFlow2.ItemId) >= 0)
				{
					dictionary.TryGetValue(factoryItemFlow2.ItemId, out var value2);
					dictionary[factoryItemFlow2.ItemId] = value2 + factoryItemFlow2.ProducedPerMinute;
				}
			}
			if (HasCount(value, IlsCargoItems) || dictionary.Count != 0)
			{
				state.PlanetNames.TryGetValue(item, out var value3);
				list2.Add(new Dictionary<string, object>
				{
					{ "planetId", item },
					{
						"planetName",
						value3 ?? ""
					},
					{
						"items",
						CountAndProductionEvidence(value, dictionary, IlsCargoItems)
					}
				});
			}
		}
		return list2;
	}

	private static List<object> CountEvidence(Dictionary<int, long> counts, int[] itemIds)
	{
		return CountAndProductionEvidence(counts, new Dictionary<int, double>(), itemIds);
	}

	private static List<object> CountAndProductionEvidence(Dictionary<int, long> counts, Dictionary<int, double> production, int[] itemIds)
	{
		List<object> list = new List<object>();
		foreach (int num in itemIds)
		{
			counts.TryGetValue(num, out var value);
			production.TryGetValue(num, out var value2);
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				{ "itemId", num },
				{
					"name",
					Plugin.ItemName(num)
				},
				{ "count", value }
			};
			if (production.Count > 0)
			{
				dictionary["producedPerMinute"] = value2;
			}
			list.Add(dictionary);
		}
		return list;
	}

	private static bool HasCount(Dictionary<int, long> counts, int[] itemIds)
	{
		foreach (int key in itemIds)
		{
			if (counts.TryGetValue(key, out var value) && value > 0)
			{
				return true;
			}
		}
		return false;
	}

	private static Dictionary<string, object> RecipeEvidence(string phaseId, ObservedGameState state)
	{
		if (!PhaseRecipeIds.TryGetValue(phaseId ?? "", out var value))
		{
			return new Dictionary<string, object> { { "available", state.RecipeTelemetryAvailable } };
		}
		int num = 0;
		foreach (ObservedRecipeConfiguration recipeConfiguration in state.RecipeConfigurations)
		{
			if (recipeConfiguration.RecipeId == value)
			{
				num += recipeConfiguration.ConfiguredMachineCount;
			}
		}
		return new Dictionary<string, object>
		{
			{ "available", state.RecipeTelemetryAvailable },
			{ "recipeId", value },
			{ "configuredMachineCount", num }
		};
	}

	private static Dictionary<string, object> PowerEvidence(ObservedGameState state)
	{
		ObservedPowerState observedPowerState = null;
		foreach (ObservedPowerState powerPlanet in state.PowerPlanets)
		{
			if (observedPowerState == null || powerPlanet.MinimumSatisfaction < observedPowerState.MinimumSatisfaction)
			{
				observedPowerState = powerPlanet;
			}
		}
		return new Dictionary<string, object>
		{
			{
				"available",
				observedPowerState != null
			},
			{ "windowGameSeconds", state.PowerWindowSeconds },
			{
				"worstPlanet",
				(observedPowerState == null) ? null : new Dictionary<string, object>
				{
					{ "planetId", observedPowerState.PlanetId },
					{ "planetName", observedPowerState.PlanetName },
					{ "observations", observedPowerState.Observations },
					{ "minimumSatisfaction", observedPowerState.MinimumSatisfaction },
					{ "undersuppliedFraction", observedPowerState.UndersuppliedFraction },
					{ "maximumDemandToCapacity", observedPowerState.MaximumDemandToCapacity }
				}
			}
		};
	}

	private static Dictionary<string, object> DysonEvidence(string phaseId, ObservedGameState state)
	{
		ObservedDysonState dyson = state.Dyson;
		Dictionary<string, object> dictionary = new Dictionary<string, object>
		{
			{ "available", dyson.Available },
			{ "aggregateSource", dyson.AggregateSource },
			{ "aggregateSystemCount", dyson.AggregateSystemCount },
			{ "constructionAggregateAvailable", dyson.ConstructionAggregateAvailable },
			{ "aggregateNodesRead", dyson.AggregateNodesRead },
			{ "aggregateNodesMissing", dyson.AggregateNodesMissing },
			{ "generationWatts", dyson.GenerationWatts },
			{ "permanentGenerationWatts", dyson.PermanentGenerationWatts },
			{ "swarmGenerationWatts", dyson.SwarmGenerationWatts },
			{ "requestedWatts", dyson.RequestedWatts }
		};
		if (phaseId == "dyson")
		{
			dictionary["swarm"] = new Dictionary<string, object>
			{
				{ "sailCount", dyson.SwarmSailCount },
				{ "netSailPopulationPerMinute", dyson.NetSwarmSailsPerMinute },
				{ "ejectorCount", dyson.EjectorCount },
				{ "ejectorsOnTarget", dyson.EjectorsOnTarget },
				{ "ejectorsSupplied", dyson.EjectorsSupplied },
				{ "ejectorsFiringNow", dyson.EjectorsFiringNow }
			};
		}
		switch (phaseId)
		{
		case "dyson":
		case "photon":
		case "white":
			dictionary["receivers"] = ReceiverEvidence(dyson);
			break;
		}
		return dictionary;
	}

	private static Dictionary<string, object> ReceiverEvidence(ObservedDysonState d)
	{
		List<object> list = new List<object>();
		foreach (ObservedReceiverState receiver in d.Receivers)
		{
			if (list.Count >= 8)
			{
				break;
			}
			list.Add(new Dictionary<string, object>
			{
				{ "planetId", receiver.PlanetId },
				{ "planetName", receiver.PlanetName },
				{ "entityId", receiver.EntityId },
				{ "sampleCount", receiver.SampleCount },
				{ "windowSeconds", receiver.WindowSeconds },
				{ "windowReady", receiver.WindowReady },
				{ "unhealthySampleCount", receiver.UnhealthySampleCount },
				{ "maximumUnhealthySamples", receiver.MaximumUnhealthySamples },
				{ "configuredForPhotonGeneration", receiver.ConfiguredForPhotonGeneration },
				{ "lensedNow", receiver.LensedNow },
				{ "currentCatalystId", receiver.CurrentCatalystId },
				{ "catalystPoints", receiver.CatalystPoints },
				{ "bufferedCatalystId", receiver.BufferedCatalystId },
				{ "bufferedCatalystCount", receiver.BufferedCatalystCount },
				{ "effectiveCatalystId", receiver.EffectiveCatalystId },
				{ "continuousReceivingNow", receiver.ContinuousReceivingNow },
				{ "minimumWarmup", receiver.MinimumWarmup },
				{ "minimumStrength", receiver.MinimumStrength },
				{ "sustainedHealthy", receiver.SustainedHealthy },
				{ "requestedDysonPowerWatts", receiver.RequestedDysonPowerWatts },
				{ "suppliedPowerWatts", receiver.SuppliedPowerWatts },
				{ "criticalPhotonOutputPerMinute", receiver.CriticalPhotonOutputPerMinute }
			});
		}
		return new Dictionary<string, object>
		{
			{ "deployed", d.ReceiverCount },
			{ "configuredForPhotonGeneration", d.ConfiguredPhotonReceiverCount },
			{ "lensed", d.LensedPhotonReceiverCount },
			{ "fullStrength", d.FullStrengthPhotonReceiverCount },
			{ "continuous", d.ContinuousPhotonReceiverCount },
			{ "sustainedHealthy", d.SustainedPhotonReceiverCount },
			{ "continuityWindowSeconds", d.ReceiverContinuityWindowSeconds },
			{ "maximumUnhealthySamples", d.ReceiverMaximumUnhealthySamples },
			{ "requestedDysonPowerWatts", d.ReceiverArrayRequestedDysonPowerWatts },
			{ "suppliedPowerWatts", d.ReceiverArraySuppliedPowerWatts },
			{ "criticalPhotonOutputPerMinute", d.ReceiverArrayCriticalPhotonOutputPerMinute },
			{ "details", list },
			{
				"detailsTruncated",
				d.Receivers.Count > 8
			},
			{
				"omittedDetailCount",
				Math.Max(0, d.Receivers.Count - 8)
			}
		};
	}

	private static bool IsDysonPhase(string phaseId)
	{
		switch (phaseId)
		{
		default:
			return phaseId == "white";
		case "green":
		case "dyson":
		case "photon":
			return true;
		}
	}

	private static void Validate(Dictionary<string, object> snapshot)
	{
		string[] array = new string[9] { "schemaVersion", "exporterVersion", "provenance", "game", "guideSelection", "guide", "summary", "evidence", "omissions" };
		foreach (string text in array)
		{
			if (!snapshot.ContainsKey(text))
			{
				throw new InvalidOperationException("Compact snapshot is missing " + text + ".");
			}
		}
		array = new string[11]
		{
			"factories", "player", "research", "productionTelemetry", "trafficTelemetry", "powerTelemetry", "recipeTelemetry", "observedState", "guideAnalysis", "guidePanel",
			"diagnostics"
		};
		foreach (string text2 in array)
		{
			if (snapshot.ContainsKey(text2))
			{
				throw new InvalidOperationException("Compact snapshot contains legacy section " + text2 + ".");
			}
		}
	}

	private static Dictionary<string, object> Dictionary(Dictionary<string, object> source, string key)
	{
		return (Value(source, key) as Dictionary<string, object>) ?? new Dictionary<string, object>();
	}

	private static object Value(Dictionary<string, object> source, string key)
	{
		if (source == null || !source.TryGetValue(key, out var value))
		{
			return null;
		}
		return value;
	}

	private static IEnumerable Enumerate(object value)
	{
		return (value as IEnumerable) ?? new object[0];
	}

	private static string Text(object value)
	{
		if (value != null)
		{
			return value.ToString();
		}
		return "";
	}

	private static bool Bool(object value)
	{
		if (value is bool)
		{
			return (bool)value;
		}
		bool result = default(bool);
		return value != null && bool.TryParse(value.ToString(), out result) && result;
	}
}
internal sealed class EmbeddedBasicFont : IDisposable
{
	private const string ResourceName = "DspGuideCheck.Fonts.Basic-Regular.ttf";

	private IntPtr fontMemory;

	private IntPtr fontResource;

	private bool ownsFont;

	public Font Font { get; private set; }

	public string Source { get; private set; }

	private EmbeddedBasicFont()
	{
	}

	public static EmbeddedBasicFont Load(Font fallback, int size)
	{
		Font val = FindLoadedFont();
		if ((Object)(object)val != (Object)null)
		{
			return new EmbeddedBasicFont
			{
				Font = val,
				Source = "loaded-basic-font"
			};
		}
		EmbeddedBasicFont embeddedBasicFont = new EmbeddedBasicFont
		{
			Font = fallback,
			Source = "native-fallback"
		};
		try
		{
			byte[] array = ReadResource();
			embeddedBasicFont.fontMemory = Marshal.AllocCoTaskMem(array.Length);
			Marshal.Copy(array, 0, embeddedBasicFont.fontMemory, array.Length);
			uint fontCount = 0u;
			embeddedBasicFont.fontResource = AddFontMemResourceEx(embeddedBasicFont.fontMemory, (uint)array.Length, IntPtr.Zero, ref fontCount);
			if (embeddedBasicFont.fontResource == IntPtr.Zero || fontCount == 0)
			{
				embeddedBasicFont.ReleaseResource();
				return embeddedBasicFont;
			}
			Font val2 = Font.CreateDynamicFontFromOSFont(new string[2] { "Basic", "Basic Regular" }, size);
			if ((Object)(object)val2 == (Object)null)
			{
				embeddedBasicFont.ReleaseResource();
				return embeddedBasicFont;
			}
			embeddedBasicFont.Font = val2;
			embeddedBasicFont.Source = "embedded-google-fonts-basic";
			embeddedBasicFont.ownsFont = true;
		}
		catch
		{
			embeddedBasicFont.ReleaseResource();
		}
		return embeddedBasicFont;
	}

	public void Dispose()
	{
		if (ownsFont && (Object)(object)Font != (Object)null)
		{
			Object.Destroy((Object)(object)Font);
		}
		Font = null;
		ownsFont = false;
		ReleaseResource();
	}

	private static Font FindLoadedFont()
	{
		Font[] array = Resources.FindObjectsOfTypeAll<Font>();
		if (array == null)
		{
			return null;
		}
		Font[] array2 = array;
		foreach (Font val in array2)
		{
			if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("basic", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return val;
			}
		}
		return null;
	}

	private static byte[] ReadResource()
	{
		using Stream stream = typeof(EmbeddedBasicFont).Assembly.GetManifestResourceStream("DspGuideCheck.Fonts.Basic-Regular.ttf");
		if (stream == null)
		{
			throw new InvalidOperationException("Embedded Basic font resource was not found.");
		}
		using MemoryStream memoryStream = new MemoryStream();
		stream.CopyTo(memoryStream);
		return memoryStream.ToArray();
	}

	private void ReleaseResource()
	{
		if (fontResource != IntPtr.Zero)
		{
			try
			{
				RemoveFontMemResourceEx(fontResource);
			}
			catch
			{
			}
			fontResource = IntPtr.Zero;
		}
		if (fontMemory != IntPtr.Zero)
		{
			Marshal.FreeCoTaskMem(fontMemory);
			fontMemory = IntPtr.Zero;
		}
	}

	[DllImport("gdi32.dll")]
	private static extern IntPtr AddFontMemResourceEx(IntPtr fontData, uint dataSize, IntPtr reserved, ref uint fontCount);

	[DllImport("gdi32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool RemoveFontMemResourceEx(IntPtr fontResourceHandle);
}
internal sealed class EmbeddedMatrixIcons : IDisposable
{
	private const string ResourcePrefix = "DspGuideCheck.MatrixIcons.";

	private static readonly Dictionary<string, string> ResourceNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
	{
		{ "blue", "t-matrix.png" },
		{ "red", "e-matrix.png" },
		{ "yellow", "c-matrix.png" },
		{ "purple", "i-matrix.png" },
		{ "green", "g-matrix.png" },
		{ "white", "u-matrix.png" },
		{ "ils", "1605.png" },
		{ "dyson", "solar-collector.png" },
		{ "photon", "photon-capacitor-full.png" },
		{ "risk-draining", "signal-402.png" },
		{ "risk-starved", "signal-404.png" }
	};

	private readonly Dictionary<string, Sprite> sprites = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase);

	private readonly List<Texture2D> textures = new List<Texture2D>();

	private EmbeddedMatrixIcons()
	{
	}

	public static EmbeddedMatrixIcons Load()
	{
		EmbeddedMatrixIcons embeddedMatrixIcons = new EmbeddedMatrixIcons();
		foreach (KeyValuePair<string, string> resourceName in ResourceNames)
		{
			embeddedMatrixIcons.TryLoad(resourceName.Key, "DspGuideCheck.MatrixIcons." + resourceName.Value);
		}
		return embeddedMatrixIcons;
	}

	public Sprite Get(string cubeId)
	{
		if (string.IsNullOrEmpty(cubeId) || !sprites.TryGetValue(cubeId, out var value))
		{
			return null;
		}
		return value;
	}

	public void Dispose()
	{
		foreach (Sprite value in sprites.Values)
		{
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value);
			}
		}
		foreach (Texture2D texture in textures)
		{
			if ((Object)(object)texture != (Object)null)
			{
				Object.Destroy((Object)(object)texture);
			}
		}
		sprites.Clear();
		textures.Clear();
	}

	private void TryLoad(string cubeId, string resourceName)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Expected O, but got Unknown
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		Texture2D val = null;
		Sprite val2 = null;
		try
		{
			byte[] array = ReadResource(resourceName);
			val = new Texture2D(2, 2, (TextureFormat)4, false);
			((Object)val).name = "DSPGuideCheck-" + cubeId + "-panel-icon";
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			if (!ImageConversion.LoadImage(val, array, true) || ((Texture)val).width <= 0 || ((Texture)val).height <= 0)
			{
				throw new InvalidOperationException("Embedded panel icon could not be decoded.");
			}
			val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			if ((Object)(object)val2 == (Object)null)
			{
				throw new InvalidOperationException("Embedded panel icon sprite could not be created.");
			}
			((Object)val2).name = ((Object)val).name;
			textures.Add(val);
			sprites[cubeId] = val2;
		}
		catch
		{
			if ((Object)(object)val2 != (Object)null)
			{
				Object.Destroy((Object)(object)val2);
			}
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)val);
			}
		}
	}

	private static byte[] ReadResource(string resourceName)
	{
		using Stream stream = typeof(EmbeddedMatrixIcons).Assembly.GetManifestResourceStream(resourceName);
		if (stream == null)
		{
			throw new InvalidOperationException("Embedded panel icon resource was not found.");
		}
		using MemoryStream memoryStream = new MemoryStream();
		stream.CopyTo(memoryStream);
		return memoryStream.ToArray();
	}
}
internal static class GuideAnalyzer
{
	private sealed class Phase
	{
		public string Id;

		public string Title;

		public int GateTechId;

		public int NextTechId;

		public string NextResearch;
	}

	private sealed class RiskItemSpec
	{
		public int ItemId;

		public string Name;

		public double ExactTargetPerMinute;

		public double DemandCeilingPerMinute;
	}

	private sealed class OrderedRisk
	{
		public ProductionRiskResult Result;

		public int PhaseOrder;
	}

	private static readonly Phase[] Phases = new Phase[9]
	{
		new Phase
		{
			Id = "blue",
			Title = "Sustain the Blue Cube science loop",
			GateTechId = 1002,
			NextTechId = 1111,
			NextResearch = "Energy Matrix"
		},
		new Phase
		{
			Id = "red",
			Title = "Sustain Red Cubes without refinery deadlock",
			GateTechId = 1111,
			NextTechId = 2902,
			NextResearch = "Drive Engine Lv2"
		},
		new Phase
		{
			Id = "ils",
			Title = "Complete the first interplanetary logistics expedition",
			GateTechId = 2902,
			NextTechId = 1124,
			NextResearch = "Structure Matrix"
		},
		new Phase
		{
			Id = "yellow",
			Title = "Run three continuous Yellow Cube labs",
			GateTechId = 1124,
			NextTechId = 1312,
			NextResearch = "Information Matrix"
		},
		new Phase
		{
			Id = "purple",
			Title = "Run three continuous Purple Cube labs",
			GateTechId = 1312,
			NextTechId = 1705,
			NextResearch = "Gravity Matrix"
		},
		new Phase
		{
			Id = "green",
			Title = "Run two continuous Green Cube labs",
			GateTechId = 1705,
			NextTechId = 1505,
			NextResearch = "Planetary Ionosphere Utilization"
		},
		new Phase
		{
			Id = "dyson",
			Title = "Build the Photon swarm",
			GateTechId = 1505,
			NextTechId = 1506,
			NextResearch = "Dirac Inversion Mechanism"
		},
		new Phase
		{
			Id = "photon",
			Title = "Prepare the inputs for White Cubes",
			GateTechId = 1506,
			NextTechId = 1507,
			NextResearch = "Universe Matrix"
		},
		new Phase
		{
			Id = "white",
			Title = "Complete the main progression route",
			GateTechId = 1507,
			NextTechId = 1508,
			NextResearch = "Mission Completed"
		}
	};

	private static readonly Dictionary<string, RiskItemSpec[]> RiskItems = new Dictionary<string, RiskItemSpec[]>(StringComparer.OrdinalIgnoreCase)
	{
		{
			"blue",
			new RiskItemSpec[1] { RiskItem(6001, "Blue Cubes", 20.0) }
		},
		{
			"red",
			new RiskItemSpec[1] { RiskItem(6002, "Red Cubes", 20.0) }
		},
		{
			"ils",
			new RiskItemSpec[2]
			{
				RiskItem(1106, "Titanium Ingots", 0.0),
				RiskItem(1105, "High-Purity Silicon", 0.0)
			}
		},
		{
			"yellow",
			new RiskItemSpec[3]
			{
				RiskItem(1112, "Diamonds", 0.0),
				RiskItem(1118, "Titanium Crystals", 0.0),
				RiskItem(6003, "Yellow Cubes", 0.0)
			}
		},
		{
			"purple",
			new RiskItemSpec[3]
			{
				RiskItem(1303, "Processors", 0.0),
				RiskItem(1402, "Particle Broadband", 0.0),
				RiskItem(6004, "Purple Cubes", 0.0)
			}
		},
		{
			"green",
			new RiskItemSpec[3]
			{
				RiskItem(1305, "Quantum Chips", 0.0),
				RiskItem(1209, "Graviton Lenses", 0.0),
				RiskItem(6005, "Green Cubes", 0.0)
			}
		},
		{
			"dyson",
			new RiskItemSpec[1] { RiskItem(1501, "Solar Sails", 0.0) }
		},
		{
			"photon",
			new RiskItemSpec[2]
			{
				RiskItem(1208, "Critical Photons", 0.0),
				RiskItem(1122, "Antimatter", 0.0)
			}
		},
		{
			"white",
			new RiskItemSpec[7]
			{
				RiskItem(6001, "Blue Cubes", 0.0),
				RiskItem(6002, "Red Cubes", 0.0),
				RiskItem(6003, "Yellow Cubes", 0.0),
				RiskItem(6004, "Purple Cubes", 0.0),
				RiskItem(6005, "Green Cubes", 0.0),
				RiskItem(1122, "Antimatter", 0.0),
				RiskItem(6006, "White Cubes", 40.0)
			}
		}
	};

	private static List<object> IlsActions(GuideGateResult gate, ObservedGameState state, int stage)
	{
		List<object> list = new List<object>();
		bool flag = false;
		foreach (GuideGateCondition condition in gate.Conditions)
		{
			if (condition.Id == "ils-expedition-production")
			{
				flag = condition.Status == "ready";
			}
		}
		int num = 0;
		foreach (GuideGateCondition condition2 in gate.Conditions)
		{
			bool flag2 = condition2.Status != "ready" && condition2.Status != "unknown" && !string.IsNullOrEmpty(condition2.Action);
			if (stage == 2 && condition2.Id == "ils-expedition-cargo")
			{
				int num2 = GuideGateEngine.FindExpeditionPlanet(state);
				Dictionary<int, long> value;
				bool flag3 = state.AvailablePlanetInventories.Contains(num2) && state.PlanetItemCounts.TryGetValue(num2, out value) && ((value.ContainsKey(1106) && value[1106] > 0) || (value.ContainsKey(1105) && value[1105] > 0));
				flag2 &= flag || flag3 || state.PlayerPlanetId == state.StarterPlanetId;
			}
			string value2 = ((condition2.Id.Contains("research") || condition2.Id == "ils-rush-tech") ? "research" : ((condition2.Id == "ils-rush-deployment") ? "deployment" : ((condition2.Id == "ils-home-delivery") ? "supply" : "physical")));
			list.Add(new Dictionary<string, object>
			{
				{ "id", condition2.Id },
				{ "kind", value2 },
				{
					"priority",
					num++
				},
				{ "eligible", flag2 },
				{ "label", condition2.Action },
				{ "status", condition2.Status },
				{ "required", condition2.Required }
			});
		}
		return list;
	}

	public static Dictionary<string, object> AnalyzeSelected(ObservedGameState state, string selectedPhaseId, int ilsStage = 1)
	{
		selectedPhaseId = ManualPhaseNavigator.NormalizePhase(selectedPhaseId);
		GuideProgressionEvaluation guideProgressionEvaluation = GuideGateEngine.EvaluatePhase(selectedPhaseId, state, ilsStage);
		Phase phase = FindPhase(selectedPhaseId) ?? FindPhase("blue");
		List<object> list = new List<object>();
		ProductionRiskResult selected;
		Dictionary<string, object> value = AnalyzeProductionRisk(state, phase.Id, out selected);
		if (selected != null && selected.Actionable)
		{
			list.Add(RiskFinding(selected));
		}
		if (phase.Id == "red")
		{
			AddRefineryCongestionFinding(list, state);
		}
		if (phase.Id == "photon")
		{
			AddPhotonPowerFinding(list, state);
		}
		KeepStrongestFinding(list);
		Dictionary<string, object> value2 = new Dictionary<string, object>
		{
			{ "id", phase.Id },
			{
				"ilsStage",
				(phase.Id == "ils") ? ilsStage : 0
			},
			{ "title", phase.Title },
			{ "gateTechId", phase.GateTechId },
			{ "nextTechId", phase.NextTechId },
			{ "nextResearch", phase.NextResearch },
			{ "basis", "Player-selected phase; runtime evidence evaluates this phase but cannot change it." }
		};
		return new Dictionary<string, object>
		{
			{ "analysisVersion", "3.13" },
			{ "phaseSelectionAuthority", "player" },
			{ "phase", value2 },
			{
				"progression",
				guideProgressionEvaluation.Export()
			},
			{ "researchQueueAvailable", state.ResearchQueueAvailable },
			{
				"ilsActions",
				(phase.Id == "ils") ? IlsActions(guideProgressionEvaluation.Gates[0], state, ilsStage) : null
			},
			{
				"ilsResearch",
				(phase.Id == "ils") ? IlsResearchPolicy.Export(state, ilsStage) : null
			},
			{ "productionRisk", value },
			{ "findings", list },
			{
				"limitations",
				new List<object> { "A technology unlock proves availability, not that the corresponding factory objective is complete.", "Production claims require a valid native Statistics Panel observation window.", "Only positively observed runtime evidence is reported as ready; player checks remain explicit." }
			}
		};
	}

	private static Phase FindPhase(string id)
	{
		Phase[] phases = Phases;
		foreach (Phase phase in phases)
		{
			if (string.Equals(phase.Id, id, StringComparison.OrdinalIgnoreCase))
			{
				return phase;
			}
		}
		return null;
	}

	private static RiskItemSpec RiskItem(int itemId, string name, double exactTargetPerMinute)
	{
		return new RiskItemSpec
		{
			ItemId = itemId,
			Name = name,
			ExactTargetPerMinute = exactTargetPerMinute,
			DemandCeilingPerMinute = (IsCubeItem(itemId) ? 40.0 : 0.0)
		};
	}

	private static bool IsCubeItem(int itemId)
	{
		if (itemId >= 6001)
		{
			return itemId <= 6006;
		}
		return false;
	}

	private static Dictionary<string, object> AnalyzeProductionRisk(ObservedGameState state, string phaseId, out ProductionRiskResult selected)
	{
		selected = null;
		int num = 0;
		List<OrderedRisk> list = new List<OrderedRisk>();
		List<object> list2 = new List<object>();
		if (!RiskItems.TryGetValue(phaseId ?? "", out var value))
		{
			value = new RiskItemSpec[0];
		}
		for (int i = 0; i < value.Length; i++)
		{
			RiskItemSpec riskItemSpec = value[i];
			ProductionRiskResult productionRiskResult = null;
			bool num2 = ExactTargetSatisfied(state, riskItemSpec);
			bool flag = DemandReferenceSatisfied(state, riskItemSpec);
			ObservedItemBufferEvidence value2;
			bool flag2 = state.ItemBuffers.TryGetValue(riskItemSpec.ItemId, out value2) && value2.Scopes.Count > 0;
			if (num2 || flag)
			{
				productionRiskResult = ProductionRiskAnalyzer.Evaluate(ClusterRiskInput(state, riskItemSpec));
				num++;
			}
			else if (flag2)
			{
				foreach (ObservedBufferScopeEvidence scope in value2.Scopes)
				{
					ProductionRiskResult candidate = ProductionRiskAnalyzer.Evaluate(PlanetRiskInput(state, riskItemSpec, scope));
					num++;
					productionRiskResult = WorseRisk(productionRiskResult, candidate);
				}
				if (riskItemSpec.ExactTargetPerMinute > 0.0)
				{
					ProductionRiskInput productionRiskInput = ClusterRiskInput(state, riskItemSpec);
					productionRiskInput.BackpressureStatus = value2.BackpressureStatus;
					ProductionRiskResult candidate2 = ProductionRiskAnalyzer.Evaluate(productionRiskInput);
					num++;
					productionRiskResult = WorseRisk(productionRiskResult, candidate2);
				}
			}
			else
			{
				ProductionRiskResult candidate3 = ProductionRiskAnalyzer.Evaluate(ClusterRiskInput(state, riskItemSpec));
				num++;
				productionRiskResult = WorseRisk(productionRiskResult, candidate3);
			}
			selected = WorseRisk(selected, productionRiskResult);
			if (productionRiskResult != null && productionRiskResult.TargetSatisfied)
			{
				list2.Add(productionRiskResult.Export());
			}
			if (productionRiskResult != null && productionRiskResult.Actionable)
			{
				list.Add(new OrderedRisk
				{
					Result = productionRiskResult,
					PhaseOrder = i
				});
			}
		}
		list.Sort(CompareOrderedRisks);
		if (list.Count > 0)
		{
			selected = list[0].Result;
		}
		List<object> list3 = new List<object>();
		foreach (OrderedRisk item in list)
		{
			list3.Add(item.Result.Export());
		}
		return new Dictionary<string, object>
		{
			{ "contractVersion", "1.2" },
			{ "basis", "Deterministic selected-phase evaluation from scope-matched native rates and conservative accessible-buffer evidence." },
			{ "evaluatedItemScopes", num },
			{ "actionable", list3 },
			{ "satisfiedExactTargets", list2 },
			{
				"selected",
				(selected != null) ? selected.Export() : null
			}
		};
	}

	private static int CompareOrderedRisks(OrderedRisk left, OrderedRisk right)
	{
		int num = RiskRank(right.Result.State).CompareTo(RiskRank(left.Result.State));
		if (num != 0)
		{
			return num;
		}
		if (left.Result.DepletionMinutesAvailable != right.Result.DepletionMinutesAvailable)
		{
			if (!left.Result.DepletionMinutesAvailable)
			{
				return 1;
			}
			return -1;
		}
		if (left.Result.DepletionMinutesAvailable)
		{
			int num2 = left.Result.DepletionMinutes.CompareTo(right.Result.DepletionMinutes);
			if (num2 != 0)
			{
				return num2;
			}
		}
		return left.PhaseOrder.CompareTo(right.PhaseOrder);
	}

	private static bool ExactTargetSatisfied(ObservedGameState state, RiskItemSpec spec)
	{
		if (spec.ExactTargetPerMinute <= 0.0)
		{
			return false;
		}
		if (state.ItemFlows.TryGetValue(spec.ItemId, out var value) && value != null && value.OneMinuteAvailable)
		{
			return value.ProducedPerMinute >= spec.ExactTargetPerMinute;
		}
		return false;
	}

	private static bool DemandReferenceSatisfied(ObservedGameState state, RiskItemSpec spec)
	{
		if (spec.DemandCeilingPerMinute <= 0.0)
		{
			return false;
		}
		if (state.ItemFlows.TryGetValue(spec.ItemId, out var value) && value != null && value.OneMinuteAvailable)
		{
			return ProductionRiskAnalyzer.DemandSatisfied(value.ProducedPerMinute, value.ConsumedPerMinute, spec.DemandCeilingPerMinute);
		}
		return false;
	}

	private static ProductionRiskInput ClusterRiskInput(ObservedGameState state, RiskItemSpec spec)
	{
		state.ItemFlows.TryGetValue(spec.ItemId, out var value);
		return new ProductionRiskInput
		{
			ItemId = spec.ItemId,
			Name = spec.Name,
			Scope = "entire-star-cluster",
			OneMinuteAvailable = (value?.OneMinuteAvailable ?? false),
			ProducedPerMinute = (value?.ProducedPerMinute ?? 0.0),
			ConsumedPerMinute = (value?.ConsumedPerMinute ?? 0.0),
			TenMinuteAvailable = (value?.TenMinuteAvailable ?? false),
			TenMinuteReady = (value?.TenMinuteReady ?? false),
			TenMinuteProducedPerMinute = (value?.TenMinuteProducedPerMinute ?? 0.0),
			TenMinuteConsumedPerMinute = (value?.TenMinuteConsumedPerMinute ?? 0.0),
			BackpressureStatus = "unknown",
			ExactTargetPerMinute = spec.ExactTargetPerMinute,
			DemandCeilingPerMinute = spec.DemandCeilingPerMinute
		};
	}

	private static ProductionRiskInput PlanetRiskInput(ObservedGameState state, RiskItemSpec spec, ObservedBufferScopeEvidence scope)
	{
		bool flag = false;
		bool flag2 = false;
		bool flag3 = false;
		bool flag4 = true;
		double num = 0.0;
		double num2 = 0.0;
		double num3 = 0.0;
		double num4 = 0.0;
		foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows)
		{
			if (factoryItemFlow.ItemId == spec.ItemId && factoryItemFlow.PlanetId == scope.PlanetId)
			{
				flag = true;
				flag2 |= factoryItemFlow.OneMinuteAvailable;
				flag3 |= factoryItemFlow.TenMinuteAvailable;
				flag4 &= factoryItemFlow.TenMinuteReady;
				num += factoryItemFlow.ProducedPerMinute;
				num2 += factoryItemFlow.ConsumedPerMinute;
				num3 += factoryItemFlow.TenMinuteProducedPerMinute;
				num4 += factoryItemFlow.TenMinuteConsumedPerMinute;
			}
		}
		return new ProductionRiskInput
		{
			ItemId = spec.ItemId,
			Name = spec.Name,
			Scope = "planet-local",
			PlanetId = scope.PlanetId,
			PlanetName = scope.PlanetName,
			OneMinuteAvailable = (flag && flag2),
			ProducedPerMinute = num,
			ConsumedPerMinute = num2,
			TenMinuteAvailable = (flag && flag3),
			TenMinuteReady = (flag && flag4),
			TenMinuteProducedPerMinute = num3,
			TenMinuteConsumedPerMinute = num4,
			RunwayAvailable = scope.RunwayAvailable,
			RunwayMinutes = scope.RunwayMinutes,
			AccessibleCount = scope.AccessibleCount,
			BackpressureStatus = scope.BackpressureStatus,
			ExactTargetPerMinute = 0.0,
			DemandCeilingPerMinute = spec.DemandCeilingPerMinute
		};
	}

	private static ProductionRiskResult WorseRisk(ProductionRiskResult current, ProductionRiskResult candidate)
	{
		if (current == null)
		{
			return candidate;
		}
		int num = RiskRank(current.State);
		int num2 = RiskRank(candidate.State);
		if (num2 > num)
		{
			return candidate;
		}
		if (num2 < num)
		{
			return current;
		}
		if (candidate.DepletionMinutesAvailable != current.DepletionMinutesAvailable)
		{
			if (!candidate.DepletionMinutesAvailable)
			{
				return current;
			}
			return candidate;
		}
		if (candidate.DepletionMinutesAvailable && candidate.DepletionMinutes != current.DepletionMinutes)
		{
			if (!(candidate.DepletionMinutes < current.DepletionMinutes))
			{
				return current;
			}
			return candidate;
		}
		if (!(candidate.Score > current.Score))
		{
			return current;
		}
		return candidate;
	}

	private static int RiskRank(string state)
	{
		return state switch
		{
			"starved" => 6, 
			"draining" => 5, 
			"warming" => 4, 
			"unknown" => 3, 
			"backpressured" => 2, 
			_ => 1, 
		};
	}

	private static Dictionary<string, object> RiskFinding(ProductionRiskResult result)
	{
		string value = ((result.State == "starved") ? (result.Name + " production is starved.") : ((!result.DemandDeficit) ? (result.Name + " production is below the phase target.") : (result.Name + " are draining faster than they are replenished.")));
		string text = "Found " + Rate(result.ProducedPerMinute) + "/min produced";
		if (result.ConsumedPerMinute > 0.0)
		{
			text = text + " and " + Rate(result.ConsumedPerMinute) + "/min consumed";
		}
		if (result.ExactTargetPerMinute > 0.0)
		{
			text = text + " against a " + Rate(result.ExactTargetPerMinute) + "/min target";
		}
		if (result.RunwayAvailable)
		{
			text = text + "; accessible runway is " + Rate(result.RunwayMinutes) + " min";
		}
		text = text + "; the ten-minute production baseline is " + Rate(result.BaselinePerMinute) + "/min. " + ((result.State == "starved") ? "Restore the upstream supply or stopped production line." : "Increase production before the deficit consumes its buffer.");
		return new Dictionary<string, object>
		{
			{
				"id",
				"production-risk-" + result.ItemId
			},
			{
				"status",
				(result.State == "starved") ? "blocked" : "watch"
			},
			{ "claim", value },
			{ "evidence", text },
			{ "confidence", "high" },
			{
				"priority",
				(result.State == "starved") ? 20 : 30
			},
			{
				"risk",
				result.Export()
			}
		};
	}

	private static string Rate(double value)
	{
		return Math.Round(value, 1).ToString("0.0", CultureInfo.InvariantCulture);
	}

	private static void KeepStrongestFinding(List<object> findings)
	{
		if (findings.Count <= 1)
		{
			return;
		}
		object obj = findings[0];
		int num = FindingPriority(obj);
		for (int i = 1; i < findings.Count; i++)
		{
			int num2 = FindingPriority(findings[i]);
			if (num2 < num)
			{
				obj = findings[i];
				num = num2;
			}
		}
		findings.Clear();
		findings.Add(obj);
	}

	private static int FindingPriority(object value)
	{
		if (!(value is Dictionary<string, object> dictionary) || !dictionary.TryGetValue("priority", out var value2))
		{
			return 100;
		}
		return Convert.ToInt32(value2, CultureInfo.InvariantCulture);
	}

	private static void AddPhotonPowerFinding(List<object> findings, ObservedGameState state)
	{
		if (state.Dyson.Available && state.Dyson.ConfiguredPhotonReceiverCount > 0)
		{
			double receiverArrayRequestedDysonPowerWatts = state.Dyson.ReceiverArrayRequestedDysonPowerWatts;
			double receiverArraySuppliedPowerWatts = state.Dyson.ReceiverArraySuppliedPowerWatts;
			double generationWatts = state.Dyson.GenerationWatts;
			string text = ((receiverArrayRequestedDysonPowerWatts > generationWatts || generationWatts <= 0.0) ? "watch" : "ready");
			findings.Add(Finding("photon-receiver-power", text, "Photon receivers request " + FormatPower(receiverArrayRequestedDysonPowerWatts) + " from " + FormatPower(generationWatts) + " of Dyson generation.", "The receiver array is currently supplied with " + FormatPower(receiverArraySuppliedPowerWatts) + ".", "high", (text == "watch") ? 10 : 80));
		}
	}

	private static string FormatPower(double watts)
	{
		if (watts >= 1000000000.0)
		{
			return Math.Round(watts / 1000000000.0, 3) + " GW";
		}
		if (watts >= 1000000.0)
		{
			return Math.Round(watts / 1000000.0, 1) + " MW";
		}
		if (watts >= 1000.0)
		{
			return Math.Round(watts / 1000.0, 1) + " kW";
		}
		return Math.Round(watts, 0) + " W";
	}

	private static void AddRefineryCongestionFinding(List<object> findings, ObservedGameState state)
	{
		if (state.ProductionWindowReady && state.ItemFlows.TryGetValue(1114, out var value) && value != null && state.TankStorage.TryGetValue(1114, out var value2) && value2.Capacity > 0)
		{
			double num = value.ProducedPerMinute - value.ConsumedPerMinute;
			double num2 = (double)value2.Count * 1.0 / (double)value2.Capacity;
			double num3 = ((num > 0.0) ? ((double)(value2.Capacity - value2.Count) / num) : double.PositiveInfinity);
			if (!(num2 < 0.8) || !(num3 > 20.0))
			{
				string status = ((num2 >= 0.98 || num3 <= 2.0) ? "blocked" : "watch");
				string text = (double.IsInfinity(num3) ? "not filling at the current net rate" : ("about " + Math.Max(0.0, Math.Round(num3, 1)) + " production minutes remain"));
				findings.Add(Finding("refined-oil-congestion", status, "Refined Oil tanks are " + Math.Round(num2 * 100.0, 1) + "% full; " + text + ".", "Use Refined Oil or expand storage to avoid refinery bottlenecks.", "high", 10));
			}
		}
	}

	private static Dictionary<string, object> Finding(string id, string status, string claim, string evidence, string confidence, int priority)
	{
		return new Dictionary<string, object>
		{
			{ "id", id },
			{ "status", status },
			{ "claim", claim },
			{ "evidence", evidence },
			{ "confidence", confidence },
			{ "priority", priority }
		};
	}
}
internal sealed class GuideGateCondition
{
	public string Id;

	public string Label;

	public string Status;

	public bool Required;

	public string Evidence;

	public string EvidenceKind;

	public string Action;

	public Dictionary<string, object> Export()
	{
		return new Dictionary<string, object>
		{
			{ "id", Id },
			{ "label", Label },
			{ "status", Status },
			{ "required", Required },
			{ "evidence", Evidence },
			{ "evidenceKind", EvidenceKind },
			{ "action", Action }
		};
	}
}
internal sealed class GuideGateResult
{
	public string Id;

	public string Title;

	public string Status;

	public string Basis;

	public readonly List<GuideGateCondition> Conditions = new List<GuideGateCondition>();

	public Dictionary<string, object> Export()
	{
		List<object> list = new List<object>();
		foreach (GuideGateCondition condition in Conditions)
		{
			list.Add(condition.Export());
		}
		return new Dictionary<string, object>
		{
			{ "id", Id },
			{ "title", Title },
			{ "status", Status },
			{ "basis", Basis },
			{ "conditions", list }
		};
	}
}
internal sealed class GuideProgressionEvaluation
{
	public string SelectedPhase;

	public readonly List<GuideGateResult> Gates = new List<GuideGateResult>();

	public Dictionary<string, object> Export()
	{
		List<object> list = new List<object>();
		foreach (GuideGateResult gate in Gates)
		{
			list.Add(gate.Export());
		}
		return new Dictionary<string, object>
		{
			{ "contractVersion", "3.10" },
			{ "selectionAuthority", "player" },
			{ "selectedPhase", SelectedPhase },
			{ "gateEvaluations", list }
		};
	}
}
internal static class GuideGateEngine
{
	private sealed class GateDefinition
	{
		public string Id;

		public string Title;
	}

	private static readonly GateDefinition[] Gates = new GateDefinition[9]
	{
		new GateDefinition
		{
			Id = "blue",
			Title = "Sustain the Blue Cube science loop"
		},
		new GateDefinition
		{
			Id = "red",
			Title = "Sustain Red Cubes without refinery deadlock"
		},
		new GateDefinition
		{
			Id = "ils",
			Title = "Complete the first interplanetary logistics expedition"
		},
		new GateDefinition
		{
			Id = "yellow",
			Title = "Run three continuous Yellow Cube labs"
		},
		new GateDefinition
		{
			Id = "purple",
			Title = "Run three continuous Purple Cube labs"
		},
		new GateDefinition
		{
			Id = "green",
			Title = "Run two continuous Green Cube labs"
		},
		new GateDefinition
		{
			Id = "dyson",
			Title = "Build the Photon swarm"
		},
		new GateDefinition
		{
			Id = "photon",
			Title = "Prepare the inputs for White Cubes"
		},
		new GateDefinition
		{
			Id = "white",
			Title = "Complete the main progression route"
		}
	};

	internal static int LiveInventoryItem(string phaseId)
	{
		switch (phaseId)
		{
		default:
			return 0;
		case "dyson":
		case "photon":
			return 1122;
		case "white":
			return 6006;
		}
	}

	internal static bool LiveRecipesNeedAssemblers(string phaseId)
	{
		return phaseId == "dyson";
	}

	public static GuideProgressionEvaluation EvaluatePhase(string selectedPhaseId, ObservedGameState state, int ilsStage = 1)
	{
		string text = ManualPhaseNavigator.NormalizePhase(selectedPhaseId);
		GuideGateResult item = EvaluateCurrentGate(FindGate(text) ?? Gates[0], state, ilsStage);
		return new GuideProgressionEvaluation
		{
			SelectedPhase = text,
			Gates = { item }
		};
	}

	private static GateDefinition FindGate(string id)
	{
		GateDefinition[] gates = Gates;
		foreach (GateDefinition gateDefinition in gates)
		{
			if (string.Equals(gateDefinition.Id, id, StringComparison.OrdinalIgnoreCase))
			{
				return gateDefinition;
			}
		}
		return null;
	}

	private static GuideGateResult EvaluateCurrentGate(GateDefinition definition, ObservedGameState state, int ilsStage)
	{
		GuideGateResult guideGateResult = new GuideGateResult
		{
			Id = definition.Id,
			Title = definition.Title,
			Basis = "Current practical conditions evaluated from normalized live evidence."
		};
		if (definition.Id == "blue")
		{
			EvaluateBlue(guideGateResult, state);
		}
		else if (definition.Id == "red")
		{
			EvaluateRed(guideGateResult, state);
		}
		else if (definition.Id == "ils")
		{
			EvaluateIls(guideGateResult, state, ilsStage);
		}
		else if (definition.Id == "yellow")
		{
			EvaluateYellow(guideGateResult, state);
		}
		else if (definition.Id == "purple")
		{
			EvaluatePurple(guideGateResult, state);
		}
		else if (definition.Id == "green")
		{
			EvaluateGreen(guideGateResult, state);
		}
		else if (definition.Id == "dyson")
		{
			EvaluateDyson(guideGateResult, state);
		}
		else if (definition.Id == "photon")
		{
			EvaluatePhoton(guideGateResult, state);
		}
		else if (definition.Id == "white")
		{
			EvaluateWhite(guideGateResult, state);
		}
		bool flag = false;
		bool flag2 = false;
		bool flag3 = false;
		foreach (GuideGateCondition condition in guideGateResult.Conditions)
		{
			if (condition.Required)
			{
				if (condition.Status == "blocked")
				{
					flag = true;
				}
				else if (condition.Status == "watch")
				{
					flag2 = true;
				}
				else if (condition.Status == "unknown")
				{
					flag3 = true;
				}
			}
		}
		guideGateResult.Status = ((flag || flag2) ? "in-progress" : (flag3 ? "evidence-incomplete" : "complete"));
		return guideGateResult;
	}

	private static void EvaluateBlue(GuideGateResult gate, ObservedGameState state)
	{
		AddNamedFlowSet(gate, state, "starter-inputs", "Starter inputs arrive continuously", new int[4] { 1101, 1104, 1202, 1301 }, new string[4] { "Iron Ingots", "Copper Ingots", "Magnetic Coils", "Circuit Boards" }, "Connect the missing starter inputs to continuous production.");
		AddNamedAvailabilitySet(gate, state, "starter-mall", "Routine factory hardware replenishes automatically", new int[9] { 2001, 2011, 2301, 2302, 2303, 2101, 2106, 2203, 2201 }, new string[9] { "Conveyor Belts", "Sorters", "Mining Machines", "Arc Smelters", "Assembling Machines", "Storage", "Storage Tanks", "Wind Turbines", "Tesla Towers" }, "Automate the missing routine factory hardware.");
		AddFlow(gate, state, "blue-continuous", "Blue Cubes (Electromagnetic Matrices) run continuously at 20/min", 6001, 20.0, required: true);
		AddManualCondition(gate, "blue-labs-fed", "Research labs run without hand feeding", "Confirm that the Blue science loop reaches research automatically.", "Connect Blue Cube production to the research labs.");
	}

	private static void EvaluateRed(GuideGateResult gate, ObservedGameState state)
	{
		if (!state.ProductionWindowReady)
		{
			gate.Conditions.Add(Condition("red-loop", "Two labs sustain 20 Red Cubes (Energy Matrices) per minute", "unknown", required: true, "Production statistics are still warming up.", "unknown", "Let the factory run long enough to measure the Red science loop."));
			return;
		}
		double num = ItemRate(state, 6002);
		int num2 = ConfiguredRecipeMachines(state, 18);
		bool flag = num2 >= 2 && num >= 20.0;
		gate.Conditions.Add(Condition("red-loop", "Two labs sustain 20 Red Cubes (Energy Matrices) per minute", flag ? "ready" : "blocked", required: true, "Found " + num2 + " configured lab(s), " + Math.Round(num, 1) + " Red Cubes/min.", "derived", flag ? null : "Configure and supply two Red Cube labs at 20/min."));
	}

	private static void EvaluateYellow(GuideGateResult gate, ObservedGameState state)
	{
		AddConfiguredLabFlow(gate, state, "yellow-labs", "Three Yellow Cube (Structure Matrix) labs run continuously", 27, 6003, 3, "Configure and supply three Yellow Cube labs.");
	}

	private static void EvaluateIls(GuideGateResult gate, ObservedGameState state, int stage)
	{
		switch (stage)
		{
		case 3:
			EvaluateIlsRush(gate, state);
			break;
		case 2:
			EvaluateIlsExpedition(gate, state);
			break;
		default:
			EvaluateIlsPreparation(gate, state);
			break;
		}
		if (stage != 3)
		{
			gate.Conditions.Insert(0, IlsResearchPolicy.Evaluate(state, stage));
		}
		if (stage != 2 && stage != 3)
		{
			gate.Conditions.Add(IlsResearchPolicy.Survey(state));
		}
	}

	private static void EvaluateIlsPreparation(GuideGateResult gate, ObservedGameState state)
	{
		List<string> list = new List<string>();
		if (state.PlayerInventoryAvailable)
		{
			AddMissingPlayerItem(state, list, 2301, "Mining Machine");
			AddMissingPlayerItem(state, list, 2302, "Arc Smelter");
			AddMissingPlayerItem(state, list, 2101, "Storage Mk.I");
			AddMissingPlayerItem(state, list, 2001, "Conveyor Belt");
			AddMissingPlayerItem(state, list, 2011, "Sorter");
			AddMissingPlayerItem(state, list, 2201, "Tesla Tower");
			if (PlayerOwned(state, 2203) <= 0 && PlayerOwned(state, 2204) <= 0)
			{
				list.Add("independent power");
			}
		}
		bool playerInventoryAvailable = state.PlayerInventoryAvailable;
		bool flag = playerInventoryAvailable && list.Count == 0;
		gate.Conditions.Add(Condition("ils-preparation", "Departure equipment", flag ? "ready" : (playerInventoryAvailable ? "blocked" : "unknown"), required: true, (!playerInventoryAvailable) ? "Icarus inventory is unavailable." : (flag ? "Outpost equipment observed. Check fuel and building space before departure." : ("Still needed: " + string.Join(", ", list.ToArray()) + ".")), "observed", (playerInventoryAvailable && !flag) ? "Load the listed outpost equipment." : null));
	}

	private static void EvaluateIlsExpedition(GuideGateResult gate, ObservedGameState state)
	{
		int num = FindExpeditionPlanet(state);
		double rate;
		bool num2 = TryPlanetProduction(state, num, 1106, out rate);
		double rate2;
		bool flag = TryPlanetProduction(state, num, 1105, out rate2);
		bool flag2 = num2 && flag;
		bool flag3 = flag2 && rate > 0.0 && rate2 > 0.0;
		gate.Conditions.Add(Condition("ils-expedition-production", "Outpost smelting", flag3 ? "ready" : (flag2 ? "blocked" : "unknown"), required: true, flag2 ? (PlanetName(state, num) + ": " + Math.Round(rate, 1) + "/min Titanium Ingots; " + Math.Round(rate2, 1) + "/min High-Purity Silicon.") : "Outpost production is unavailable.", "observed", (flag2 && !flag3) ? "Smelt both Titanium Ingots and High-Purity Silicon at the outpost." : null));
		bool flag4 = state.PlayerLocationAvailable && state.StarterPlanetId > 0;
		bool flag5 = flag4 && state.PlayerPlanetId == state.StarterPlanetId;
		bool flag6 = flag4 && state.PlayerInventoryAvailable && (!flag5 || state.AvailablePlanetInventories.Contains(state.StarterPlanetId));
		long num3 = PlayerOwned(state, 1106) + (flag5 ? PlanetOwned(state, state.StarterPlanetId, 1106) : 0);
		long num4 = PlayerOwned(state, 1105) + (flag5 ? PlanetOwned(state, state.StarterPlanetId, 1105) : 0);
		bool flag7 = flag6 && num3 >= 860 && num4 >= 520;
		string text = (flag6 ? (num3 + "/860 Titanium Ingots; " + num4 + "/520 High-Purity Silicon " + (flag5 ? "aboard or stored at home." : "aboard Icarus.")) : "Cargo inventory is unavailable.");
		if (!flag5 && state.AvailablePlanetInventories.Contains(num))
		{
			text = text + " Outpost stock: " + PlanetOwned(state, num, 1106) + " Titanium; " + PlanetOwned(state, num, 1105) + " Silicon.";
		}
		List<string> list = new List<string>();
		if (num3 < 860)
		{
			list.Add(860 - num3 + " Titanium Ingots");
		}
		if (num4 < 520)
		{
			list.Add(520 - num4 + " High-Purity Silicon");
		}
		gate.Conditions.Add(Condition("ils-expedition-cargo", "Return cargo", flag7 ? "ready" : (flag6 ? "blocked" : "unknown"), required: true, text, "observed", (flag6 && !flag7) ? ((flag5 ? "Gather " : "Load ") + string.Join(" and ", list.ToArray()) + (flag5 ? " at home." : " into Icarus.")) : null));
		bool flag8 = flag5 && flag7;
		gate.Conditions.Add(Condition("ils-expedition-home", "Cargo at home", flag8 ? "ready" : ((flag4 && flag6) ? "blocked" : "unknown"), required: true, flag8 ? "Cargo available at home. Continue with III Automation." : ((!flag4) ? "Home location is unavailable." : (flag5 ? "Home reached; the full cargo is not available." : "Return to the home planet with the cargo.")), "observed", (flag4 && !flag5 && flag7) ? "Bring the cargo home." : null));
	}

	private static void EvaluateIlsRush(GuideGateResult gate, ObservedGameState state)
	{
		GuideGateCondition guideGateCondition = IlsResearchPolicy.Evaluate(state, 3);
		gate.Conditions.Add(guideGateCondition);
		bool flag = guideGateCondition.Status == "ready";
		IlsTransportEvidence ilsTransportEvidence = IlsTransportEvidence.Build(state);
		gate.Conditions.Add(Condition("ils-rush-hardware", "Two ILS towers and five Vessels", ilsTransportEvidence.HardwareReady ? "ready" : (ilsTransportEvidence.Available ? "blocked" : "unknown"), required: true, "Observed: " + (ilsTransportEvidence.InventoryTowers + ilsTransportEvidence.DeployedTowers) + "/2 towers; " + (ilsTransportEvidence.InventoryVessels + ilsTransportEvidence.AssignedVessels) + "/5 Vessels at home or assigned to the selected endpoints.", "observed", (flag && ilsTransportEvidence.Available && !ilsTransportEvidence.HardwareReady) ? "Finish the package: two ILS towers and five Logistics Vessels." : null));
		gate.Conditions.Add(Condition("ils-rush-deployment", "Configure the home and outpost stations", ilsTransportEvidence.DeploymentReady ? "ready" : (ilsTransportEvidence.Available ? "blocked" : "unknown"), required: true, ilsTransportEvidence.DeploymentReady ? "Home demands both finished materials with five assigned Vessels; the outpost supplies both." : (ilsTransportEvidence.Available ? "Both endpoint policies and the home fleet are needed." : "Station evidence is unavailable."), "observed", (flag && ilsTransportEvidence.HardwareReady && ilsTransportEvidence.Available && !ilsTransportEvidence.DeploymentReady) ? "Set home to Remote Demand, outpost to Remote Supply, and assign five Vessels at home." : null));
		IlsReceiptEvidence ilsReceipt = state.IlsReceipt;
		gate.Conditions.Add(Condition("ils-home-delivery", "Finished materials reach home", (ilsReceipt.Status == "confirmed") ? "ready" : ((ilsReceipt.Status == "unavailable") ? "unknown" : "watch"), required: true, (ilsReceipt.Status == "confirmed") ? "Home imports observed for both finished materials." : ((ilsReceipt.Status == "source-empty") ? "The outpost needs finished material stock or production." : ((ilsReceipt.Status == "unavailable") ? "Delivery evidence is unavailable." : "Awaiting delivery evidence at home.")), "planet-level corroboration", (flag && ilsTransportEvidence.DeploymentReady && ilsReceipt.Status == "source-empty") ? "Supply Titanium Ingots and High-Purity Silicon at the outpost." : null));
	}

	private static void EvaluatePurple(GuideGateResult gate, ObservedGameState state)
	{
		AddConfiguredLabFlow(gate, state, "purple-labs", "Three Purple Cube (Information Matrix) labs run continuously", 55, 6004, 3, "Configure and supply three Purple Cube labs.");
	}

	private static void EvaluateGreen(GuideGateResult gate, ObservedGameState state)
	{
		AddConfiguredLabFlow(gate, state, "green-labs", "Two Green Cube (Gravity Matrix) labs run continuously", 102, 6005, 2, "Configure and supply two Green Cube labs.");
		AddVisibleCubeInputs(gate, state, "green-inputs", "Quantum Chips and Graviton Lenses are visible in storage", 1305, "Quantum Chips", 1209, "Graviton Lenses", "Buffer both Green Cube inputs in visible storage.");
	}

	private static void EvaluateDyson(GuideGateResult gate, ObservedGameState state)
	{
		double num = ItemRate(state, 1501);
		double num2 = ItemConsumption(state, 1501);
		bool flag = state.ProductionWindowReady && num > 0.0 && num2 > 0.0;
		gate.Conditions.Add(Condition("dyson-sails", "Solar Sails are being produced and launched", flag ? "ready" : (state.ProductionWindowReady ? "blocked" : "unknown"), required: true, "Found " + Math.Round(num, 1) + "/min produced and " + Math.Round(num2, 1) + "/min launched.", state.ProductionWindowReady ? "observed" : "unknown", flag ? null : "Supply an active EM-Rail Ejector line with Solar Sails."));
		bool flag2 = state.Dyson.SwarmSailCount > 0 && state.Dyson.SwarmGenerationWatts > 0.0;
		gate.Conditions.Add(Condition("dyson-swarm", "The Dyson swarm is generating power", flag2 ? "ready" : "blocked", required: true, "Found " + state.Dyson.SwarmSailCount + " active sails generating " + FormatPower(state.Dyson.SwarmGenerationWatts) + ".", "observed", flag2 ? null : "Keep sails in orbit and confirm the swarm is generating power."));
		AddDysonBridge(gate, state);
	}

	private static void AddReceiverCondition(GuideGateResult gate, ObservedGameState state, string id, bool allowAction)
	{
		bool receiverTelemetryAvailable = state.Dyson.ReceiverTelemetryAvailable;
		bool flag = receiverTelemetryAvailable && state.Dyson.ConfiguredPhotonReceiverCount >= 4 && state.Dyson.LensedPhotonReceiverCount >= 4 && state.Dyson.SustainedPhotonReceiverCount >= 4;
		gate.Conditions.Add(Condition(id, "Four lensed Ray Receivers in Photon Generation", flag ? "ready" : (receiverTelemetryAvailable ? "blocked" : "unknown"), required: true, receiverTelemetryAvailable ? (state.Dyson.SustainedPhotonReceiverCount + "/4 sustained; " + state.Dyson.LensedPhotonReceiverCount + "/4 currently lensed.") : "Receiver continuity telemetry is not ready.", receiverTelemetryAvailable ? "observed" : "unknown", (flag || !allowAction || !receiverTelemetryAvailable) ? null : "Keep four Photon Generation receivers lensed and continuously supplied."));
	}

	internal static long StationaryStock(ObservedGameState state, int itemId, out bool available)
	{
		long num = 0L;
		available = state.PlanetItemCounts.Count > 0;
		foreach (KeyValuePair<int, Dictionary<int, long>> planetItemCount in state.PlanetItemCounts)
		{
			bool flag = state.AvailablePlanetInventories.Contains(planetItemCount.Key);
			available &= flag;
			if (flag && planetItemCount.Value.TryGetValue(itemId, out var value))
			{
				num += Math.Max(0L, value);
			}
		}
		return num;
	}

	private static bool AddBridgeResearch(GuideGateResult gate, ObservedGameState state)
	{
		int[] array = new int[3] { 1504, 1505, 1506 };
		string[] array2 = new string[3] { "Ray Receiver", "Planetary Ionosphere Utilization", "Dirac Inversion Mechanism" };
		for (int i = 0; i < array.Length; i++)
		{
			if (!state.UnlockedTechIds.Contains(array[i]))
			{
				bool flag = state.QueuedTechIds.Contains(array[i]);
				bool flag2 = state.AvailableTechIds.Contains(array[i]) && state.ResearchQueueAvailable;
				gate.Conditions.Add(Condition("dyson-bridge-research", "Receiver research", flag ? "watch" : (flag2 ? "blocked" : "unknown"), required: true, flag ? (array2[i] + " queued.") : (flag2 ? (array2[i] + " is not yet researched.") : "Research evidence is unavailable."), "observed", (flag2 && !flag) ? ("Research " + array2[i] + ".") : null));
				return false;
			}
		}
		gate.Conditions.Add(Condition("dyson-bridge-research", "Receiver research", "ready", required: true, "Receiver and conversion research complete.", "observed", null));
		return true;
	}

	private static void AddDysonBridge(GuideGateResult gate, ObservedGameState state)
	{
		bool flag = AddBridgeResearch(gate, state);
		AddReceiverCondition(gate, state, "dyson-receivers", flag);
		bool flag2 = state.ItemFlows.TryGetValue(1208, out var value) && value.OneMinuteAvailable;
		flag2 &= state.ItemFlows.TryGetValue(1122, out var value2) && value2.OneMinuteAvailable;
		bool available;
		long num = StationaryStock(state, 1122, out available);
		bool flag3 = state.RecipeTelemetryAvailable && flag2 && (available || num > 0);
		bool flag4 = flag3 && ConfiguredRecipeMachines(state, 74) > 0 && value.ProducedPerMinute > 0.0 && value.ConsumedPerMinute > 0.0 && value2.ProducedPerMinute > 0.0 && num > 0;
		gate.Conditions.Add(Condition("dyson-conversion", "Convert Critical Photons to Antimatter", flag4 ? "ready" : (flag3 ? "blocked" : "unknown"), required: true, flag4 ? ("Cluster production and consumption observed; " + num + " Antimatter in stationary storage.") : (flag3 ? "Needs Photon Materialization, Photon production and consumption, and stored Antimatter." : "Conversion or stationary inventory evidence is unavailable."), "cluster-level observation", (!flag4 && flag3 && flag) ? "Run Photon Materialization in a Collider and store its Antimatter." : null));
	}

	private static void EvaluatePhoton(GuideGateResult gate, ObservedGameState state)
	{
		string[] array = new string[6] { "Blue Cubes", "Red Cubes", "Yellow Cubes", "Purple Cubes", "Green Cubes", "Antimatter" };
		bool flag = true;
		bool flag2 = false;
		bool flag3 = false;
		string text = null;
		List<string> list = new List<string>();
		for (int i = 0; i < PhotonReadiness.ItemIds.Length; i++)
		{
			int key = PhotonReadiness.ItemIds[i];
			ObservedItemFlow value;
			bool flag4 = state.ItemFlows.TryGetValue(key, out value) && value.OneMinuteAvailable;
			SustainedInputReadiness value2;
			string text2 = ((state.PhotonInputs.TryGetValue(key, out value2) && flag4) ? value2.Reason : "unavailable");
			bool flag5 = text2 == "ready" && value.ProducedPerMinute >= 40.0;
			flag = flag && flag5;
			flag2 |= text2 == "unavailable";
			flag3 |= text2 == "below-target";
			if (!flag5)
			{
				list.Add(array[i] + ": " + text2.Replace('-', ' ') + ".");
			}
			if (text == null && text2 == "below-target" && flag4 && value.ProducedPerMinute < 40.0)
			{
				text = "Bring " + array[i] + " production to at least 40/min.";
			}
		}
		gate.Conditions.Add(Condition("photon-inputs", "Five colored Cubes and Antimatter sustain 40/min", flag ? "ready" : (flag2 ? "unknown" : (flag3 ? "blocked" : "watch")), required: true, flag ? "All six inputs sustained 40/min over the two-minute observation window." : string.Join(" ", list.ToArray()), "sampled native aggregates", text));
		bool available;
		long num = StationaryStock(state, 1122, out available);
		gate.Conditions.Add(Condition("antimatter-stock", "Store 2,000 Antimatter", (num >= 2000) ? "ready" : (available ? "blocked" : "unknown"), required: true, (available || num >= 2000) ? (num + "/2,000 in observed cluster storage and stations; excludes Icarus.") : "Stationary Antimatter inventory is unavailable.", "observed stationary stock", (available && num < 2000) ? "Store 2,000 Antimatter for White science." : null));
	}

	private static void EvaluateWhite(GuideGateResult gate, ObservedGameState state)
	{
		bool flag = state.UnlockedTechIds.Contains(1507);
		gate.Conditions.Add(Condition("tech-1507", "White Cubes researched", flag ? "ready" : "blocked", required: true, null, "observed", null));
		bool flag2 = state.UnlockedTechIds.Contains(1508);
		int num = ConfiguredRecipeMachines(state, 75);
		double num2 = ItemRate(state, 6006);
		long num3 = Owned(state, 6006);
		bool flag3 = flag2 || (state.ProductionWindowReady && num >= 10 && num2 >= 40.0);
		gate.Conditions.Add(Condition("white-production", "Ten labs sustain 40 White Cubes/min", flag3 ? "ready" : (state.ProductionWindowReady ? "blocked" : "unknown"), required: true, num + "/10 labs configured; " + num3.ToString("N0", CultureInfo.InvariantCulture) + " White Cubes stored", state.ProductionWindowReady ? "observed" : "unknown", null));
		ObservedTechProgress value;
		bool flag4 = state.TechProgress.TryGetValue(1508, out value) && value.HashUploaded > 0;
		string evidence = (flag2 ? "Mission Completed complete" : (flag4 ? ("Mission Completed " + Math.Min(99, value.Percent) + "% done") : (state.QueuedTechIds.Contains(1508) ? "Mission Completed queued" : "Mission Completed not queued")));
		gate.Conditions.Add(Condition("mission-completed", "Mission Completed", flag2 ? "ready" : (state.QueuedTechIds.Contains(1508) ? "watch" : "blocked"), required: true, evidence, "observed", flag2 ? null : "Complete Mission Completed research."));
	}

	private static void AddConfiguredLabFlow(GuideGateResult gate, ObservedGameState state, string id, string label, int recipeId, int itemId, int desiredLabs, string action)
	{
		int num = ConfiguredRecipeMachines(state, recipeId);
		if ((itemId == 6003 || itemId == 6004) && (!state.RecipeTelemetryAvailable || !state.ItemFlows.TryGetValue(itemId, out var value) || !value.OneMinuteAvailable))
		{
			gate.Conditions.Add(Condition(id, label, "unknown", required: true, "Lab configuration or production evidence is unavailable.", "unknown", null));
			return;
		}
		if (!state.ProductionWindowReady)
		{
			gate.Conditions.Add(Condition(id, label, "unknown", required: true, "Found " + num + " configured lab(s); production statistics are still warming up.", "unknown", action));
			return;
		}
		double num2 = ItemRate(state, itemId);
		bool flag = num >= desiredLabs && num2 > 0.0;
		gate.Conditions.Add(Condition(id, label, flag ? "ready" : "blocked", required: true, "Found " + num + " configured lab(s) producing " + Math.Round(num2, 1) + "/min.", "observed", flag ? null : action));
	}

	private static void AddVisibleCubeInputs(GuideGateResult gate, ObservedGameState state, string id, string label, int firstItemId, string firstItemName, int secondItemId, string secondItemName, string action)
	{
		long num = Owned(state, firstItemId);
		long num2 = Owned(state, secondItemId);
		bool flag = num > 0 && num2 > 0;
		gate.Conditions.Add(Condition(id, label, flag ? "ready" : "blocked", required: true, "Owned: " + num + " " + firstItemName + " and " + num2 + " " + secondItemName + ".", "observed", flag ? null : action));
	}

	private static void AddFlow(GuideGateResult gate, ObservedGameState state, string id, string label, int itemId, double minimum, bool required)
	{
		if (!state.ProductionWindowReady)
		{
			gate.Conditions.Add(Condition(id, label, "unknown", required, "Production observation window is not ready.", "unknown", "Let the factory run long enough to establish a rate."));
			return;
		}
		state.ItemFlows.TryGetValue(itemId, out var value);
		double num = value?.ProducedPerMinute ?? 0.0;
		bool flag = num >= minimum;
		string status = (flag ? "ready" : "blocked");
		string text = ((value != null && !string.IsNullOrEmpty(value.Name)) ? value.Name : ItemNameForAction(itemId, label));
		gate.Conditions.Add(Condition(id, label, status, required, "Found " + Math.Round(num, 1) + "/min; desired " + minimum + "/min.", "observed", flag ? null : ("Build or stabilize " + text + " at or above " + minimum + "/min.")));
	}

	private static void AddNamedFlowSet(GuideGateResult gate, ObservedGameState state, string id, string label, int[] itemIds, string[] itemNames, string action)
	{
		if (!state.ProductionWindowReady)
		{
			gate.Conditions.Add(Condition(id, label, "unknown", required: true, "The production statistics window is not ready.", "unknown", "Let the factory run long enough to check this objective."));
			return;
		}
		List<string> list = new List<string>();
		for (int i = 0; i < itemIds.Length; i++)
		{
			if (ItemRate(state, itemIds[i]) <= 0.0)
			{
				list.Add(itemNames[i]);
			}
		}
		bool flag = list.Count == 0;
		gate.Conditions.Add(Condition(id, label, flag ? "ready" : "blocked", required: true, flag ? "All four starter inputs are producing." : ("Not producing: " + string.Join(", ", list.ToArray()) + "."), "observed", flag ? null : action));
	}

	private static void AddNamedAvailabilitySet(GuideGateResult gate, ObservedGameState state, string id, string label, int[] itemIds, string[] itemNames, string action)
	{
		List<string> list = new List<string>();
		for (int i = 0; i < itemIds.Length; i++)
		{
			long num = Owned(state, itemIds[i]);
			double num2 = ItemRate(state, itemIds[i]);
			if (num < 1 && num2 <= 0.0)
			{
				list.Add(itemNames[i]);
			}
		}
		bool flag = list.Count == 0;
		gate.Conditions.Add(Condition(id, label, flag ? "ready" : "blocked", required: true, flag ? "Routine factory hardware is stocked or replenishing." : ("Missing: " + string.Join(", ", list.ToArray()) + "."), "derived", flag ? null : action));
	}

	private static void AddManualCondition(GuideGateResult gate, string id, string label, string evidence, string action)
	{
		gate.Conditions.Add(Condition(id, label, "unknown", required: true, evidence, "player-check", action));
	}

	private static void AddTech(GuideGateResult gate, ObservedGameState state, int techId, string label, bool required)
	{
		bool flag = state.UnlockedTechIds.Contains(techId);
		gate.Conditions.Add(Condition("tech-" + techId, label, flag ? "ready" : "blocked", required, "Technology " + techId + (flag ? " is complete." : " is incomplete."), "observed", flag ? null : ("Research " + TechName(state, techId) + ".")));
	}

	private static void AddAutomatedRoute(GuideGateResult gate, ObservedGameState state, int itemId, string id, string label, bool required)
	{
		bool flag = HasImportAndExport(state, itemId);
		bool flag2 = flag || HasSustainableRoute(state, itemId);
		gate.Conditions.Add(Condition(id, label, flag2 ? "ready" : "blocked", required, flag ? "Matching import and export traffic was observed during the rolling window." : (flag2 ? "A one-sided traffic observation is corroborated by matching remote Supply/Demand station policy and source production or stock." : "No traffic or corroborated supplied/demanded route was proven."), flag ? "observed" : (flag2 ? "derived" : "observed"), flag2 ? null : "Activate the automated interplanetary route."));
	}

	private static bool HasImportAndExport(ObservedGameState state, int itemId)
	{
		bool flag = false;
		bool flag2 = false;
		foreach (ObservedTrafficFlow trafficFlow in state.TrafficFlows)
		{
			if (trafficFlow.ItemId == itemId)
			{
				if (trafficFlow.InputPerMinute > 0.0)
				{
					flag = true;
				}
				if (trafficFlow.OutputPerMinute > 0.0)
				{
					flag2 = true;
				}
			}
		}
		return flag && flag2;
	}

	private static bool HasSustainableRoute(ObservedGameState state, int itemId)
	{
		if (HasImportAndExport(state, itemId))
		{
			return true;
		}
		bool flag = false;
		bool flag2 = false;
		foreach (ObservedTrafficFlow trafficFlow in state.TrafficFlows)
		{
			if (trafficFlow.ItemId == itemId)
			{
				if (trafficFlow.InputPerMinute > 0.0)
				{
					flag = true;
				}
				if (trafficFlow.OutputPerMinute > 0.0)
				{
					flag2 = true;
				}
			}
		}
		HashSet<int> hashSet = new HashSet<int>();
		HashSet<int> hashSet2 = new HashSet<int>();
		foreach (ObservedStationSlot stationSlot in state.StationSlots)
		{
			if (stationSlot.IsStellar && stationSlot.ItemId == itemId)
			{
				string obj = stationSlot.RemoteLogic ?? "";
				if (obj.IndexOf("Supply", StringComparison.OrdinalIgnoreCase) >= 0 && (stationSlot.Count > 0 || RemotePlanetProduction(state, stationSlot.PlanetId, itemId) > 0.0))
				{
					hashSet.Add(stationSlot.PlanetId);
				}
				if (obj.IndexOf("Demand", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					hashSet2.Add(stationSlot.PlanetId);
				}
			}
		}
		bool flag3 = false;
		foreach (int item in hashSet)
		{
			foreach (int item2 in hashSet2)
			{
				if (item != item2)
				{
					flag3 = true;
				}
			}
		}
		if (flag3)
		{
			return flag || flag2;
		}
		return false;
	}

	private static double RemotePlanetProduction(ObservedGameState state, int planetId, int itemId)
	{
		double num = 0.0;
		foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows)
		{
			if (factoryItemFlow.PlanetId == planetId && factoryItemFlow.ItemId == itemId)
			{
				num += factoryItemFlow.ProducedPerMinute;
			}
		}
		return num;
	}

	internal static int FindExpeditionPlanet(ObservedGameState state)
	{
		if (state.StarterPlanetId <= 0)
		{
			return 0;
		}
		if (state.PlayerLocationAvailable && state.PlayerPlanetId > 0 && state.PlayerPlanetId != state.StarterPlanetId)
		{
			return state.PlayerPlanetId;
		}
		SortedSet<int> sortedSet = new SortedSet<int>();
		foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows)
		{
			if (factoryItemFlow.PlanetId > 0 && factoryItemFlow.OneMinuteAvailable && factoryItemFlow.ProducedPerMinute > 0.0 && (factoryItemFlow.ItemId == 1105 || factoryItemFlow.ItemId == 1106))
			{
				sortedSet.Add(factoryItemFlow.PlanetId);
			}
		}
		foreach (int availablePlanetInventory in state.AvailablePlanetInventories)
		{
			if (PlanetOwned(state, availablePlanetInventory, 1105) > 0 || PlanetOwned(state, availablePlanetInventory, 1106) > 0)
			{
				sortedSet.Add(availablePlanetInventory);
			}
		}
		foreach (int item in sortedSet)
		{
			if (item != state.StarterPlanetId)
			{
				return item;
			}
		}
		return 0;
	}

	private static bool TryPlanetProduction(ObservedGameState state, int planetId, int itemId, out double rate)
	{
		rate = 0.0;
		bool result = false;
		if (planetId <= 0)
		{
			return false;
		}
		foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows)
		{
			if (factoryItemFlow.PlanetId == planetId && factoryItemFlow.ItemId == itemId)
			{
				if (!factoryItemFlow.OneMinuteAvailable)
				{
					return false;
				}
				result = true;
				rate += factoryItemFlow.ProducedPerMinute;
			}
		}
		return result;
	}

	private static double PlanetProduction(ObservedGameState state, int planetId, params int[] itemIds)
	{
		if (planetId <= 0)
		{
			return 0.0;
		}
		double num = 0.0;
		foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows)
		{
			if (factoryItemFlow.PlanetId != planetId)
			{
				continue;
			}
			foreach (int num2 in itemIds)
			{
				if (factoryItemFlow.ItemId == num2)
				{
					num += factoryItemFlow.ProducedPerMinute;
				}
			}
		}
		return num;
	}

	private static long PlanetOwned(ObservedGameState state, int planetId, int itemId)
	{
		if (planetId <= 0 || !state.PlanetItemCounts.TryGetValue(planetId, out var value) || !value.TryGetValue(itemId, out var value2))
		{
			return 0L;
		}
		return value2;
	}

	private static long PlayerOwned(ObservedGameState state, int itemId)
	{
		if (!state.PlayerItemCounts.TryGetValue(itemId, out var value))
		{
			return 0L;
		}
		return value;
	}

	private static string PlanetName(ObservedGameState state, int planetId)
	{
		if (!state.PlanetNames.TryGetValue(planetId, out var value) || string.IsNullOrEmpty(value))
		{
			return "the expedition planet";
		}
		return value;
	}

	private static bool TechStarted(ObservedGameState state, int techId)
	{
		if (!state.UnlockedTechIds.Contains(techId))
		{
			return state.QueuedTechIds.Contains(techId);
		}
		return true;
	}

	private static void AddMissingPlayerItem(ObservedGameState state, List<string> missing, int itemId, string name)
	{
		if (PlayerOwned(state, itemId) <= 0)
		{
			missing.Add(name);
		}
	}

	private static string FormatPower(double watts)
	{
		if (watts >= 1000000000.0)
		{
			return Math.Round(watts / 1000000000.0, 3) + " GW";
		}
		if (watts >= 1000000.0)
		{
			return Math.Round(watts / 1000000.0, 1) + " MW";
		}
		if (watts >= 1000.0)
		{
			return Math.Round(watts / 1000.0, 1) + " kW";
		}
		return Math.Round(watts, 0) + " W";
	}

	private static bool HasStellarStation(ObservedGameState state)
	{
		foreach (ObservedStationSlot stationSlot in state.StationSlots)
		{
			if (stationSlot.IsStellar)
			{
				return true;
			}
		}
		return false;
	}

	private static int DistinctFactoryPlanets(ObservedGameState state)
	{
		HashSet<int> hashSet = new HashSet<int>();
		foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows)
		{
			if (factoryItemFlow.PlanetId > 0)
			{
				hashSet.Add(factoryItemFlow.PlanetId);
			}
		}
		return hashSet.Count;
	}

	private static long Owned(ObservedGameState state, int itemId)
	{
		if (!state.OwnedItemCounts.TryGetValue(itemId, out var value))
		{
			return 0L;
		}
		return value;
	}

	private static int ConfiguredRecipeMachines(ObservedGameState state, int recipeId)
	{
		int num = 0;
		foreach (ObservedRecipeConfiguration recipeConfiguration in state.RecipeConfigurations)
		{
			if (recipeConfiguration.RecipeId == recipeId)
			{
				num += recipeConfiguration.ConfiguredMachineCount;
			}
		}
		return num;
	}

	private static double ItemRate(ObservedGameState state, int itemId)
	{
		if (!state.ItemFlows.TryGetValue(itemId, out var value))
		{
			return 0.0;
		}
		return value.ProducedPerMinute;
	}

	private static double ItemConsumption(ObservedGameState state, int itemId)
	{
		if (!state.ItemFlows.TryGetValue(itemId, out var value))
		{
			return 0.0;
		}
		return value.ConsumedPerMinute;
	}

	private static string TechName(ObservedGameState state, int techId)
	{
		if (!state.TechNames.TryGetValue(techId, out var value))
		{
			return "Technology " + techId;
		}
		return value;
	}

	private static string ItemNameForAction(int itemId, string fallback)
	{
		return itemId switch
		{
			6001 => "Electromagnetic Matrix production", 
			6002 => "Energy Matrix production", 
			6003 => "Structure Matrix production", 
			6004 => "Information Matrix production", 
			6005 => "Gravity Matrix production", 
			1101 => "Iron Ingot production", 
			1104 => "Copper Ingot production", 
			1121 => "Deuterium supply", 
			1123 => "Graphene production", 
			1124 => "Carbon Nanotube production", 
			1127 => "Strange Matter production", 
			1209 => "Graviton Lens production", 
			1303 => "Processor production", 
			1305 => "Quantum Chip production", 
			1402 => "Particle Broadband production", 
			_ => fallback, 
		};
	}

	private static GuideGateCondition Condition(string id, string label, string status, bool required, string evidence, string evidenceKind, string action)
	{
		return new GuideGateCondition
		{
			Id = id,
			Label = label,
			Status = status,
			Required = required,
			Evidence = evidence,
			EvidenceKind = evidenceKind,
			Action = action
		};
	}
}
internal sealed class GuidePanelController
{
	private sealed class RowView
	{
		public GameObject Root;

		public Image Dot;

		public Image Check;

		public Text Label;

		public Text Detail;

		public readonly List<Image> Strikes = new List<Image>();

		public bool Completed;

		public float CompletionProgress;

		public float StrikeWidth;
	}

	private sealed class CubeRateView
	{
		public GameObject Root;

		public Image Icon;

		public Text Rate;

		public string CubeId;
	}

	private sealed class ImageStyle
	{
		public Sprite Sprite;

		public Material Material;

		public Color Color = Color.white;

		public Type Type;

		public bool PreserveAspect;

		public void Apply(Image image)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)image == (Object)null))
			{
				image.sprite = Sprite;
				((Graphic)image).material = Material;
				((Graphic)image).color = Color;
				image.type = Type;
				image.preserveAspect = PreserveAspect;
			}
		}

		public static ImageStyle Capture(Image image)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)image == (Object)null)
			{
				return null;
			}
			return new ImageStyle
			{
				Sprite = image.sprite,
				Material = ((Graphic)image).material,
				Color = ((Graphic)image).color,
				Type = image.type,
				PreserveAspect = image.preserveAspect
			};
		}
	}

	private sealed class TextStyle
	{
		public Font Font;

		public int FontSize;

		public FontStyle FontStyle;

		public Color Color;

		public float LineSpacing;

		public Material Material;

		public void Apply(Text text)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)text == (Object)null))
			{
				text.font = Font;
				text.fontSize = FontSize;
				text.fontStyle = FontStyle;
				((Graphic)text).color = Color;
				text.lineSpacing = LineSpacing;
				((Graphic)text).material = Material;
				text.supportRichText = true;
			}
		}

		public static TextStyle Capture(Text text, TextStyle fallback)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)text == (Object)null)
			{
				return new TextStyle
				{
					Font = fallback.Font,
					FontSize = fallback.FontSize,
					FontStyle = fallback.FontStyle,
					Color = fallback.Color,
					LineSpacing = fallback.LineSpacing,
					Material = fallback.Material
				};
			}
			return new TextStyle
			{
				Font = text.font,
				FontSize = text.fontSize,
				FontStyle = text.fontStyle,
				Color = ((Graphic)text).color,
				LineSpacing = text.lineSpacing,
				Material = ((Graphic)text).material
			};
		}
	}

	private sealed class NativeTextPresentation
	{
		public sealed class EffectStyle
		{
			public bool IsOutline;

			public Color Color;

			public Vector2 Distance;

			public bool UsesGraphicAlpha;
		}

		public const string RuntimeSource = "UIRoot.instance.uiGame.veinDetail.nodePrefab.infoText";

		public const string LoadedNodeSource = "loaded UIVeinDetailNode.infoText";

		public string Source;

		public Font Font;

		public FontStyle FontStyle;

		public float LineSpacing;

		public Material Material;

		public readonly List<EffectStyle> Effects = new List<EffectStyle>();

		public EffectStyle PrimaryEffect
		{
			get
			{
				if (Effects.Count <= 0)
				{
					return null;
				}
				return Effects[0];
			}
		}

		public List<object> ExportEffects()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			List<object> list = new List<object>();
			foreach (EffectStyle effect in Effects)
			{
				list.Ad