Decompiled source of DSPGuideCheck v2.1.83
BepInEx/plugins/DSP-Guide-Check/DspGuideCheck.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; 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("2.1.83.0")] [assembly: AssemblyInformationalVersion("2.1.83.22a5998")] [assembly: AssemblyProduct("DSP Guide Check")] [assembly: AssemblyTitle("DspGuideCheck")] [assembly: AssemblyVersion("2.1.83.0")] namespace DspProgressionStatusExporter; public static class BuildFeatures { public const string Variant = "public"; public const bool SnapshotControlEnabled = false; } internal static class BuildVersion { public const string BepInPluginVersion = "2.1.83"; public const string PluginVersion = "2.1.83"; public const string ReleaseLabel = "2.1.83.22a5998"; } 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[8] { 2001, 2011, 2101, 2201, 2203, 2204, 2301, 2302 }; private static readonly int[] IlsCargoItems = new int[4] { 1003, 1004, 1105, 1106 }; private static readonly int[] IlsReserveItems = new int[8] { 1103, 1107, 1203, 1206, 1303, 2104, 5002, 6003 }; 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[20] { 1003, 1004, 1103, 1105, 1106, 1107, 1203, 1206, 1303, 2001, 2011, 2101, 2104, 2201, 2203, 2204, 2301, 2302, 5002, 6003 } }, { "yellow", new int[3] { 6003, 1112, 1118 } }, { "purple", new int[3] { 6004, 1303, 1402 } }, { "green", new int[3] { 6005, 1305, 1209 } }, { "dyson", new int[1] { 1501 } }, { "photon", new int[2] { 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 }, { "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") }, { "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 (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 (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)) { int num = 0; int num2 = 0; foreach (ObservedStationState station in state.Stations) { if (station.IsStellar) { num++; num2 += station.IdleShipCount + station.WorkShipCount; } } dictionary2["stellarStationCount"] = num; dictionary2["deployedVesselCount"] = num2; 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 }, { "playerInventory", CountEvidence(state.PlayerItemCounts, IlsPlayerItems) }, { "planetCargo", IlsPlanetCargoEvidence(state) }, { "protectedReserve", CountEvidence(BestIlsReserve(state), IlsReserveItems) } }; } 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 Dictionary<int, long> BestIlsReserve(ObservedGameState state) { if (state.PlayerPlanetId > 0 && state.PlanetItemCounts.TryGetValue(state.PlayerPlanetId, out var value)) { return value; } Dictionary<int, long> result = new Dictionary<int, long>(); long num = -1L; foreach (Dictionary<int, long> value3 in state.PlanetItemCounts.Values) { long num2 = 0L; int[] ilsReserveItems = IlsReserveItems; foreach (int key in ilsReserveItems) { if (value3.TryGetValue(key, out var value2)) { num2 += value2; } } if (num2 > num) { result = value3; num = num2; } } return result; } 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 } }; } if (phaseId == "photon" || phaseId == "white") { dictionary["receivers"] = ReceiverEvidence(dyson); } 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 }, { "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 = "Run the critical-photon receiver array", 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) } } }; public static Dictionary<string, object> AnalyzeSelected(ObservedGameState state, string selectedPhaseId) { selectedPhaseId = ManualPhaseNavigator.NormalizePhase(selectedPhaseId); GuideProgressionEvaluation guideProgressionEvaluation = GuideGateEngine.EvaluatePhase(selectedPhaseId, state); 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 }, { "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.3" }, { "phaseSelectionAuthority", "player" }, { "phase", value2 }, { "progression", guideProgressionEvaluation.Export() }, { "normalizedState", state.Export() }, { "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.1" }, { "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 = "Run the Critical Photon receiver array" }, new GateDefinition { Id = "white", Title = "Complete the main progression route" } }; public static GuideProgressionEvaluation EvaluatePhase(string selectedPhaseId, ObservedGameState state) { string text = ManualPhaseNavigator.NormalizePhase(selectedPhaseId); GuideGateResult item = EvaluateCurrentGate(FindGate(text) ?? Gates[0], state); 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) { 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); } 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."); AddVisibleCubeInputs(gate, state, "yellow-inputs", "Diamonds and Titanium Crystals are visible in storage", 1112, "Diamonds", 1118, "Titanium Crystals", "Buffer both Yellow Cube inputs in visible storage."); } private static void EvaluateIls(GuideGateResult gate, ObservedGameState state) { int num = FindExpeditionPlanet(state); double num2 = PlanetProduction(state, num, 1004, 1106); double num3 = PlanetProduction(state, num, 1003, 1105); long num4 = PlanetOwned(state, num, 1106); long num5 = PlanetOwned(state, num, 1105); bool num6 = num > 0 && (num2 > 0.0 || num3 > 0.0 || num4 > 0 || num5 > 0); bool flag = (num4 >= 860 && num5 >= 520) || TechStarted(state, 1414) || TechStarted(state, 1604) || TechStarted(state, 2903) || TechStarted(state, 1605) || CountStellarStations(state) > 0; if (!num6 && !flag) { EvaluateIlsPreparation(gate, state); } else if (!flag) { EvaluateIlsExpedition(gate, state, num, num2, num3, num4, num5); } else { EvaluateIlsRush(gate, state); } } private static void EvaluateIlsPreparation(GuideGateResult gate, ObservedGameState state) { List<string> list = new List<string>(); if (!state.UnlockedTechIds.Contains(2902)) { list.Add("Drive Engine Lv2"); } if (!state.UnlockedTechIds.Contains(1413)) { list.Add("Titanium Smelting"); } 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 flag = list.Count == 0; gate.Conditions.Add(Condition("ils-preparation", "The interplanetary expedition is ready to launch", flag ? "ready" : "blocked", required: true, flag ? "Required flight technology and outpost essentials are in Icarus." : ("Still needed: " + string.Join(", ", list.ToArray()) + "."), "observed", flag ? null : "Research the missing technology and load the listed outpost essentials.")); } private static void EvaluateIlsExpedition(GuideGateResult gate, ObservedGameState state, int planetId, double titaniumRate, double siliconRate, long titaniumCargo, long siliconCargo) { bool flag = titaniumRate > 0.0 && siliconRate > 0.0; string text = PlanetName(state, planetId); gate.Conditions.Add(Condition("ils-expedition-production", "Titanium and Silicon production is active on " + text, flag ? "ready" : "blocked", required: true, "Found Titanium at " + Math.Round(titaniumRate, 1) + "/min and Silicon at " + Math.Round(siliconRate, 1) + "/min.", "observed", flag ? null : "Start both Titanium and Silicon production on the expedition planet.")); bool flag2 = titaniumCargo >= 860 && siliconCargo >= 520; gate.Conditions.Add(Condition("ils-expedition-cargo", "The return cargo is buffered in local storage", flag2 ? "ready" : "blocked", required: true, titaniumCargo + "/860 Titanium Ingots and " + siliconCargo + "/520 High-Purity Silicon stored on " + text + ".", "observed", flag2 ? null : "Buffer the full return cargo in local storage.")); } private static void EvaluateIlsRush(GuideGateResult gate, ObservedGameState state) { int[] obj = new int[4] { 1414, 1604, 2903, 1605 }; int num = 0; int[] array = obj; foreach (int num2 in array) { if (!state.UnlockedTechIds.Contains(num2)) { num = num2; break; } } bool flag = num == 0; gate.Conditions.Add(Condition("ils-rush-tech", flag ? "The ILS research chain is complete" : ("Current research target: " + TechName(state, num)), flag ? "ready" : (state.QueuedTechIds.Contains(num) ? "watch" : "blocked"), required: true, flag ? "Interstellar Logistics System is researched." : (state.QueuedTechIds.Contains(num) ? "This technology is queued." : "This technology is not queued."), "observed", flag ? null : ("Research " + TechName(state, num) + "."))); int num3 = CountStellarStations(state) + (int)Owned(state, 2104); int num4 = CountLogisticsVessels(state) + (int)Owned(state, 5002); bool flag2 = num3 >= 2 && num4 >= 5; List<string> list = MissingIlsReserve(state); bool flag3 = flag2 || list.Count == 0; gate.Conditions.Add(Condition("ils-rush-reserve", "The protected ILS build reserve is complete", flag3 ? "ready" : "blocked", required: true, flag2 ? "Found two ILS stations and five Logistics Vessels." : (flag3 ? "All protected components are stored together." : ("Still needed: " + string.Join(", ", list.ToArray()) + ".")), "observed", flag3 ? null : "Store the missing components without spending the protected reserve.")); bool num5 = HasSustainableRoute(state, 1106); bool flag4 = HasSustainableRoute(state, 1105) || HasSustainableRoute(state, 1003); bool flag5 = num5 && flag4; List<string> list2 = new List<string>(); if (!num5) { list2.Add("Titanium"); } if (!flag4) { list2.Add("Silicon"); } gate.Conditions.Add(Condition("ils-rush-routes", "Titanium and Silicon arrive home automatically", flag5 ? "ready" : "blocked", required: true, flag5 ? "Both activated ILS routes were found." : ("Missing activated route: " + string.Join(" and ", list2.ToArray()) + "."), flag5 ? "derived" : "observed", flag5 ? null : "Activate the missing ILS route.")); } 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."); AddVisibleCubeInputs(gate, state, "purple-inputs", "Processors and Particle Broadband are visible in storage", 1303, "Processors", 1402, "Particle Broadband", "Buffer both Purple Cube inputs in visible storage."); } 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.")); } private static void EvaluatePhoton(GuideGateResult gate, ObservedGameState state) { bool receiverTelemetryAvailable = state.Dyson.ReceiverTelemetryAvailable; bool flag = receiverTelemetryAvailable && state.Dyson.ConfiguredPhotonReceiverCount >= 4 && state.Dyson.LensedPhotonReceiverCount >= 4 && state.Dyson.SustainedPhotonReceiverCount >= 4; gate.Conditions.Add(Condition("photon-receivers", "Four lensed Ray Receivers remain continuously supplied", 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 ? null : "Keep four Photon Generation receivers lensed and continuously supplied.")); double num = ItemRate(state, 1208); double num2 = ItemRate(state, 1122); bool flag2 = state.ProductionWindowReady && num > 0.0 && num2 > 0.0; gate.Conditions.Add(Condition("photon-production", "Critical Photon and Antimatter production is running", flag2 ? "ready" : (state.ProductionWindowReady ? "blocked" : "unknown"), required: true, "Found " + Math.Round(num, 1) + " Critical Photons/min and " + Math.Round(num2, 1) + " Antimatter/min; 48/min is the receiver-array reference.", state.ProductionWindowReady ? "observed" : "unknown", flag2 ? null : "Establish continuous Critical Photon and Antimatter production.")); long num3 = Owned(state, 1122); gate.Conditions.Add(Condition("antimatter-stock", "The Antimatter bank reaches the 2,000 midpoint", (num3 >= 2000) ? "ready" : "blocked", required: true, num3 + "/2,000 stored" + ((num3 >= 2000) ? " - halfway to the final research cost." : "."), "observed", (num3 >= 2000) ? null : "Bank 2,000 Antimatter to reach the midway checkpoint.")); } 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 (!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 int CountStellarStations(ObservedGameState state) { int num = 0; foreach (ObservedStationState station in state.Stations) { if (station.IsStellar) { num++; } } return num; } private static int CountLogisticsVessels(ObservedGameState state) { int num = 0; foreach (ObservedStationState station in state.Stations) { if (station.IsStellar) { num += station.IdleShipCount + station.WorkShipCount; } } return num; } 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; } private static int FindExpeditionPlanet(ObservedGameState state) { HashSet<int> hashSet = new HashSet<int>(); foreach (ObservedFactoryItemFlow factoryItemFlow in state.FactoryItemFlows) { if (factoryItemFlow.PlanetId > 0) { hashSet.Add(factoryItemFlow.PlanetId); } } foreach (int key in state.PlanetItemCounts.Keys) { if (key > 0) { hashSet.Add(key); } } int result = 0; double num = 0.0; foreach (int item in hashSet) { if (state.StarterPlanetId <= 0 || item != state.StarterPlanetId) { double num2 = PlanetProduction(state, item, 1004, 1106); double num3 = PlanetProduction(state, item, 1003, 1105); long val = PlanetOwned(state, item, 1106); long val2 = PlanetOwned(state, item, 1105); double num4 = ((num2 > 0.0) ? 1000.0 : 0.0) + ((num3 > 0.0) ? 1000.0 : 0.0) + (double)Math.Min(val, 860L) / 860.0 + (double)Math.Min(val2, 520L) / 520.0; if (num4 > num) { num = num4; result = item; } } } 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 List<string> MissingIlsReserve(ObservedGameState state) { int planetId = BestIlsReservePlanet(state); int[] array = new int[6] { 1103, 1106, 1303, 1206, 1107, 1203 }; long[] array2 = new long[6] { 80L, 80L, 130L, 80L, 180L, 50L }; string[] array3 = new string[6] { "Steel", "Titanium Ingots", "Processors", "Particle Containers", "Titanium Alloy", "Electromagnetic Turbines" }; List<string> list = new List<string>(); for (int i = 0; i < array.Length; i++) { long num = PlanetOwned(state, planetId, array[i]) + PlayerOwned(state, array[i]); if (num < array2[i]) { list.Add(array2[i] - num + " " + array3[i]); } } long num2 = ((!state.UnlockedTechIds.Contains(1605)) ? (state.UnlockedTechIds.Contains(1414) ? 120 : 200) : 0); long num3 = PlanetOwned(state, planetId, 6003) + PlayerOwned(state, 6003); if (num3 < num2) { list.Add(num2 - num3 + " Yellow Cubes"); } return list; } private static int BestIlsReservePlanet(ObservedGameState state) { if (state.PlayerPlanetId > 0 && state.PlanetItemCounts.ContainsKey(state.PlayerPlanetId)) { return state.PlayerPlanetId; } int result = 0; long num = -1L; foreach (int key in state.PlanetItemCounts.Keys) { long num2 = PlanetOwned(state, key, 1103) + PlanetOwned(state, key, 1106) + PlanetOwned(state, key, 1303) + PlanetOwned(state, key, 1206) + PlanetOwned(state, key, 1107) + PlanetOwned(state, key, 1203) + PlanetOwned(state, key, 6003); if (num2 > num) { num = num2; result = key; } } return result; } 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.Add(new Dictionary<string, object> { { "type", effect.IsOutline ? "outline" : "shadow" }, { "color", ColorValue(effect.Color) }, { "distance", Vector(effect.Distance) }, { "usesGraphicAlpha", effect.UsesGraphicAlpha } }); } return list; } public void Apply(NativeGoalStyle goalStyle) { Apply(goalStyle.HeaderText); Apply(goalStyle.GroupText); Apply(goalStyle.InfoText); } private void Apply(TextStyle textStyle) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) textStyle.Font = Font; textStyle.FontStyle = FontStyle; textStyle.LineSpacing = LineSpacing; textStyle.Material = Material; } public void ApplyEffects(GameObject target) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) foreach (EffectStyle effect in Effects) { Component obj = target.AddComponent(effect.IsOutline ? typeof(Outline) : typeof(Shadow)); Shadow val = (Shadow)(object)((obj is Shadow) ? obj : null); if (!((Object)(object)val == (Object)null)) { val.effectColor = effect.Color; val.effectDistance = effect.Distance; val.useGraphicAlpha = effect.UsesGraphicAlpha; } } } public static NativeTextPresentation Capture() { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) try { object member = GetMember(GetMember(GetMember(GetMember(GetStatic(FindType("UIRoot"), "instance", "_instance"), "uiGame"), "veinDetail"), "nodePrefab"), "infoText"); Text val = (Text)((member is Text) ? member : null); string source = "UIRoot.instance.uiGame.veinDetail.nodePrefab.infoText"; if ((Object)(object)val == (Object)null || (Object)(object)val.font == (Object)null) { val = FindLoadedVeinText(); source = "loaded UIVeinDetailNode.infoText"; } if ((Object)(object)val == (Object)null || (Object)(object)val.font == (Object)null) { return null; } NativeTextPresentation nativeTextPresentation = new NativeTextPresentation { Source = source, Font = val.font, FontStyle = val.fontStyle, LineSpacing = val.lineSpacing, Material = ((Graphic)val).material }; Shadow[] components = ((Component)val).GetComponents<Shadow>(); if (components != null) { Shadow[] array = components; foreach (Shadow val2 in array) { if ((Object)(object)val2 != (Object)null) { nativeTextPresentation.Effects.Add(new EffectStyle { IsOutline = (val2 is Outline), Color = val2.effectColor, Distance = val2.effectDistance, UsesGraphicAlpha = val2.useGraphicAlpha }); } } } return nativeTextPresentation; } catch { return null; } } private static Text FindLoadedVeinText() { Type type = FindType("UIVeinDetailNode"); if (type == null) { return null; } Object[] array = Resources.FindObjectsOfTypeAll(type); if (array == null) { return null; } Object[] array2 = array; for (int i = 0; i < array2.Length; i++) { object member = GetMember(array2[i], "infoText"); Text val = (Text)((member is Text) ? member : null); if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null) { return val; } } return null; } private static Type FindType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(name, throwOnError: false); if (type != null) { return type; } } return null; } private static object GetStatic(Type type, params string[] names) { if (type == null) { return null; } foreach (string name in names) { FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(null); } PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { return property.GetValue(null, null); } } return null; } private static object GetMember(object target, params string[] names) { if (target == null) { return null; } Type type = target.GetType(); foreach (string name in names) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(target); } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { return property.GetValue(target, null); } } return null; } } private sealed class NativeGoalStyle { public Transform Parent; public RectTransform NativeRect; public TextStyle HeaderText; public TextStyle GroupText; public TextStyle InfoText; public Color CompletedTextColor; public ImageStyle Background; public ImageStyle Edge; public ImageStyle Dot; public ImageStyle Check; public ImageStyle Strike; public ImageStyle Collapse; public Vector2 CollapseSize = new Vector2(30f, 30f); public static NativeGoalStyle Capture(Font fallbackFont) { //IL_0016: 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) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04aa: Unknown result type (might be due to invalid IL or missing references) //IL_04ae: Unknown result type (might be due to invalid IL or missing references) //IL_04b3: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_0536: Unknown result type (might be due to invalid IL or missing references) //IL_053b: Unknown result type (might be due to invalid IL or missing references) //IL_04c4: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04d9: Unknown result typ