using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Mirage; using Mirage.Serialization; using NuclearOption.MissionEditorScripts; using NuclearOption.Networking; using NuclearOption.NodeGraph; using NuclearOption.SavedMission; using NuclearOption.SavedMission.Objectives; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] [CompilerGenerated] [EditorBrowsable(EditorBrowsableState.Never)] [GeneratedCode("Unity.MonoScriptGenerator.MonoScriptInfoGenerator", null)] internal class UnitySourceGeneratedAssemblyMonoScriptTypes_v1 { private struct MonoScriptData { public byte[] FilePathsData; public byte[] TypesData; public int TotalTypes; public int TotalFiles; public bool IsEditorOnly; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static MonoScriptData Get() { return new MonoScriptData { FilePathsData = new byte[1028] { 0, 0, 0, 1, 0, 0, 0, 53, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 66, 117, 105, 108, 100, 105, 110, 103, 79, 98, 106, 101, 99, 116, 105, 118, 101, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 58, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 66, 117, 105, 108, 100, 105, 110, 103, 83, 97, 118, 101, 100, 79, 98, 106, 101, 99, 116, 105, 118, 101, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 45, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 65, 115, 115, 101, 116, 115, 46, 99, 115, 0, 0, 0, 3, 0, 0, 0, 48, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 97, 116, 97, 108, 111, 103, 117, 101, 46, 99, 115, 0, 0, 0, 4, 0, 0, 0, 45, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 111, 110, 102, 105, 103, 46, 99, 115, 0, 0, 0, 2, 0, 0, 0, 48, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 76, 111, 103, 105, 115, 116, 105, 99, 115, 46, 99, 115, 0, 0, 0, 2, 0, 0, 0, 46, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 77, 97, 110, 97, 103, 101, 114, 46, 99, 115, 0, 0, 0, 4, 0, 0, 0, 42, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 78, 101, 116, 46, 99, 115, 0, 0, 0, 2, 0, 0, 0, 49, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 98, 106, 101, 99, 116, 105, 118, 101, 115, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 46, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 72, 111, 115, 116, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 44, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 80, 97, 110, 101, 108, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 47, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 82, 101, 103, 105, 115, 116, 114, 121, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 43, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 83, 105, 116, 101, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 53, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 84, 114, 97, 110, 115, 112, 111, 114, 116, 83, 116, 97, 116, 101, 46, 99, 115, 0, 0, 0, 2, 0, 0, 0, 41, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 85, 105, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 51, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 79, 98, 106, 101, 99, 116, 105, 118, 101, 69, 100, 105, 116, 111, 114, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 52, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 79, 98, 106, 101, 99, 116, 105, 118, 101, 80, 97, 116, 99, 104, 101, 115, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 33, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 80, 108, 117, 103, 105, 110, 46, 99, 115, 0, 0, 0, 1, 0, 0, 0, 32, 92, 65, 115, 115, 101, 116, 115, 92, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 92, 80, 114, 111, 98, 101, 46, 99, 115 }, TypesData = new byte[1383] { 0, 0, 0, 0, 42, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 66, 117, 105, 108, 100, 105, 110, 103, 79, 98, 106, 101, 99, 116, 105, 118, 101, 0, 0, 0, 0, 47, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 66, 117, 105, 108, 100, 105, 110, 103, 83, 97, 118, 101, 100, 79, 98, 106, 101, 99, 116, 105, 118, 101, 0, 0, 0, 0, 34, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 65, 115, 115, 101, 116, 115, 0, 0, 0, 0, 37, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 97, 116, 97, 108, 111, 103, 117, 101, 0, 0, 0, 0, 50, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 97, 116, 97, 108, 111, 103, 117, 101, 124, 77, 97, 116, 101, 114, 105, 97, 108, 76, 111, 97, 100, 0, 0, 0, 0, 49, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 97, 116, 97, 108, 111, 103, 117, 101, 124, 66, 117, 105, 108, 100, 79, 112, 116, 105, 111, 110, 0, 0, 0, 0, 34, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 111, 110, 102, 105, 103, 0, 0, 0, 0, 48, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 111, 110, 102, 105, 103, 124, 77, 97, 116, 101, 114, 105, 97, 108, 69, 110, 116, 114, 121, 0, 0, 0, 0, 45, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 111, 110, 102, 105, 103, 124, 66, 117, 105, 108, 100, 69, 110, 116, 114, 121, 0, 0, 0, 0, 58, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 67, 111, 110, 102, 105, 103, 124, 65, 105, 114, 99, 114, 97, 102, 116, 80, 114, 111, 100, 117, 99, 116, 105, 111, 110, 69, 110, 116, 114, 121, 0, 0, 0, 0, 37, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 76, 111, 103, 105, 115, 116, 105, 99, 115, 0, 0, 0, 0, 44, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 76, 111, 103, 105, 115, 116, 105, 99, 115, 124, 83, 111, 114, 116, 105, 101, 0, 0, 0, 0, 35, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 77, 97, 110, 97, 103, 101, 114, 0, 0, 0, 0, 50, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 77, 97, 110, 97, 103, 101, 114, 124, 67, 111, 109, 112, 108, 101, 116, 101, 100, 66, 117, 105, 108, 100, 0, 0, 0, 0, 31, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 78, 101, 116, 0, 0, 0, 0, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 78, 101, 116, 124, 67, 111, 109, 109, 97, 110, 100, 77, 101, 115, 115, 97, 103, 101, 0, 0, 0, 0, 41, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 78, 101, 116, 124, 83, 105, 116, 101, 83, 116, 97, 116, 101, 0, 0, 0, 0, 44, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 78, 101, 116, 124, 83, 116, 97, 116, 101, 77, 101, 115, 115, 97, 103, 101, 0, 0, 0, 0, 38, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 98, 106, 101, 99, 116, 105, 118, 101, 115, 0, 0, 0, 0, 48, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 98, 106, 101, 99, 116, 105, 118, 101, 115, 124, 68, 105, 114, 101, 99, 116, 105, 118, 101, 0, 0, 0, 0, 35, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 72, 111, 115, 116, 0, 0, 0, 0, 33, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 80, 97, 110, 101, 108, 0, 0, 0, 0, 36, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 82, 101, 103, 105, 115, 116, 114, 121, 0, 0, 0, 0, 32, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 83, 105, 116, 101, 0, 0, 0, 0, 42, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 84, 114, 97, 110, 115, 112, 111, 114, 116, 83, 116, 97, 116, 101, 0, 0, 0, 0, 30, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 85, 105, 0, 0, 0, 0, 38, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 46, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 85, 105, 124, 72, 111, 116, 90, 111, 110, 101, 0, 0, 0, 0, 40, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 79, 98, 106, 101, 99, 116, 105, 118, 101, 69, 100, 105, 116, 111, 114, 0, 0, 0, 0, 41, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 67, 111, 110, 115, 116, 114, 117, 99, 116, 79, 98, 106, 101, 99, 116, 105, 118, 101, 80, 97, 116, 99, 104, 101, 115, 0, 0, 0, 0, 22, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 80, 108, 117, 103, 105, 110, 0, 0, 0, 0, 21, 67, 111, 110, 115, 116, 114, 117, 99, 116, 105, 111, 110, 79, 112, 115, 124, 80, 114, 111, 98, 101 }, TotalFiles = 19, TotalTypes = 31, IsEditorOnly = false }; } } namespace ConstructionOps; public class ConstructBuildingObjective : Objective, IObjectiveWithPosition { public const string AnyBuild = "any"; public readonly Waypoint Spot = new Waypoint(); private readonly ValueWrapperInt count = new ValueWrapperInt(1); private readonly ValueWrapperBool aiBuild = new ValueWrapperBool(false); private string buildId = "any"; private readonly ObjectivePosition[] positions = (ObjectivePosition[])(object)new ObjectivePosition[1]; public ConstructBuildingSavedObjective Saved => (ConstructBuildingSavedObjective)(object)base.SavedObjective; public bool AiBuild => ((ValueWrapper)(object)aiBuild).Value; public GlobalPosition TargetPosition => ((ValueWrapper)(object)Spot.GlobalPosition).Value; public float TargetRange => Mathf.Max(1f, ((ValueWrapper)(object)Spot.Range).Value); public override bool NeedsFaction => false; public override float CompletePercent { get { int num = Mathf.Max(1, ((ValueWrapper)(object)count).Value); return Mathf.Clamp01((float)CountMatching() / (float)num); } } public IReadOnlyList Positions { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) positions[0] = Spot.ToObjectivePosition(); return positions; } } public string BuildIdForDiagnostics => buildId; public int RequiredCountForDiagnostics => Mathf.Max(1, ((ValueWrapper)(object)count).Value); public ConstructBuildingObjective(ConstructBuildingSavedObjective savedObjective) : base((SavedObjective)(object)savedObjective) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown ApplySaved(); } private void ApplySaved() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) ((ValueWrapper)(object)Spot.GlobalPosition).SetValue(new GlobalPosition(Saved.Position), (object)this, true); ((ValueWrapper)(object)Spot.Range).SetValue(Mathf.Max(1f, Saved.Range), (object)this, true); ((ValueWrapper)(object)count).SetValue(Mathf.Max(1, Saved.Count), (object)this, true); ((ValueWrapper)(object)aiBuild).SetValue(Saved.AiBuild, (object)this, true); buildId = (string.IsNullOrEmpty(Saved.BuildId) ? "any" : Saved.BuildId); } public override void Load(MissionLookups lookups) { ((Objective)this).Load(lookups); ApplySaved(); } public override void Save() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) ((Objective)this).Save(); ConstructBuildingSavedObjective saved = Saved; GlobalPosition value = ((ValueWrapper)(object)Spot.GlobalPosition).Value; saved.Position = ((GlobalPosition)(ref value)).AsVector3(); Saved.Range = ((ValueWrapper)(object)Spot.Range).Value; Saved.Count = ((ValueWrapper)(object)count).Value; Saved.AiBuild = ((ValueWrapper)(object)aiBuild).Value; Saved.BuildId = buildId; } public override void CopyFrom(Objective original) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) ((Objective)this).CopyFrom(original); if (original is ConstructBuildingObjective constructBuildingObjective) { ((ValueWrapper)(object)Spot.GlobalPosition).SetValue(((ValueWrapper)(object)constructBuildingObjective.Spot.GlobalPosition).Value, (object)this, true); ((ValueWrapper)(object)Spot.Range).SetValue(((ValueWrapper)(object)constructBuildingObjective.Spot.Range).Value, (object)this, true); ((ValueWrapper)(object)count).SetValue(((ValueWrapper)(object)constructBuildingObjective.count).Value, (object)this, true); ((ValueWrapper)(object)aiBuild).SetValue(((ValueWrapper)(object)constructBuildingObjective.aiBuild).Value, (object)this, true); buildId = constructBuildingObjective.buildId; } } public override void OnStart() { } public override void ClientOnlyUpdate() { } protected override void DataReferenceDestroyed(ISaveableReference reference) { } public override bool UpdateAndCheck() { return CountMatching() >= Mathf.Max(1, ((ValueWrapper)(object)count).Value); } public int CountMatching() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0080: Unknown result type (might be due to invalid IL or missing references) IList completedBuilds = ConstructionManager.CompletedBuilds; if (completedBuilds.Count == 0) { return 0; } GlobalPosition value = ((ValueWrapper)(object)Spot.GlobalPosition).Value; float num = Mathf.Max(1f, ((ValueWrapper)(object)Spot.Range).Value); float num2 = num * num; FactionHQ factionHQ = ((Objective)this).FactionHQ; int num3 = 0; for (int i = 0; i < completedBuilds.Count; i++) { ConstructionManager.CompletedBuild completedBuild = completedBuilds[i]; if ((!((Object)(object)factionHQ != (Object)null) || !((Object)(object)completedBuild.HQ != (Object)(object)factionHQ)) && MatchesBuild(completedBuild) && !(FastMath.SquareDistance(completedBuild.Position, value) > num2)) { num3++; } } return num3; } private bool MatchesBuild(ConstructionManager.CompletedBuild build) { if (string.IsNullOrEmpty(buildId) || buildId == "any") { return true; } if (!string.Equals(buildId, build.BuildId, StringComparison.OrdinalIgnoreCase)) { return string.Equals(buildId, build.BuildingJsonKey, StringComparison.OrdinalIgnoreCase); } return true; } public override void DrawData(DataDrawer drawer) { BuildChoices(out var ids, out var labels); int num = ids.IndexOf(buildId); if (num < 0) { num = 0; } drawer.DrawDropdown("Build", labels, num, (Action)delegate(int index) { if (index >= 0 && index < ids.Count) { buildId = ids[index]; } }); drawer.InstantiateWithParent(drawer.Prefabs.VectorFieldPrefab).Setup("Target Spot", (IValueWrapper)(object)Spot.GlobalPosition); drawer.InstantiateWithParent(drawer.Prefabs.FloatFieldPrefab).Setup("Range", (IValueWrapper)(object)Spot.Range, (FloatSettings?)null); drawer.InstantiateWithParent(drawer.Prefabs.FloatFieldPrefab).Setup("Count", count, (IntSettings?)null); drawer.Space(8); drawer.InstantiateWithParent(drawer.Prefabs.BoolFieldPrefab).Setup("AI will construct this", (IValueWrapper)(object)aiBuild); drawer.DrawLabel("Ticked, this objective's faction flies its own crates and material loads in. The haulers cost it airframes from its supply, exactly like any other AI deployment."); drawer.Space(8); drawer.DrawLabel("Drag the marker in the world to move the target spot."); } public override void AddPins(GraphNodeData data) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_0044: Expected O, but got Unknown ((Objective)this).AddPins(data); data.InputElements.Add((GraphElementData)new GraphReadOnlyFieldData { PinId = new PinId("Build"), DisplayName = "Build", GetText = () => LabelFor(buildId) + " x" + ((ValueWrapper)(object)count).Value + (((ValueWrapper)(object)aiBuild).Value ? " (AI builds)" : "") }); } public override IObjectiveEditorUpdate CreateEditorUpdate(Canvas canvas, UIPrefabs prefabs) { return (IObjectiveEditorUpdate)(object)new ConstructObjectiveEditor(canvas, prefabs, this); } private static void BuildChoices(out List ids, out List labels) { ids = new List { "any" }; labels = new List { "Any construction" }; foreach (string item in ConstructionCatalogue.AuthoredIds()) { ids.Add(item); labels.Add(LabelFor(item)); } } private static string LabelFor(string id) { if (string.IsNullOrEmpty(id) || id == "any") { return "Any construction"; } ConstructionCatalogue.BuildOption buildOption = ConstructionCatalogue.ById(id); if (buildOption != null && !string.IsNullOrEmpty(buildOption.DisplayName)) { return buildOption.DisplayName; } string text = ConstructionCatalogue.AuthoredDisplayName(id); if (!string.IsNullOrEmpty(text)) { return text; } return id; } } [Serializable] public class ConstructBuildingSavedObjective : SavedObjective { public const int TypeValue = 5; public Vector3 Position; public float Range = 500f; public string BuildId = "any"; public int Count = 1; public bool AiBuild; public override ObjectiveType ObjectiveTypeEnum => (ObjectiveType)5; public ConstructBuildingSavedObjective() { } public ConstructBuildingSavedObjective(string name) : base(name) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) Position = LookedAtPosition(); } private static Vector3 LookedAtPosition() { //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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) try { Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return Vector3.zero; } RaycastHit val = default(RaycastHit); if (Physics.Raycast(main.ScreenPointToRay(new Vector3((float)Screen.width / 2f, (float)Screen.height / 2f)), ref val, 100000f, LayerMask.op_Implicit(PhysicsLayers.StaticsMask))) { GlobalPosition val2 = GlobalPositionExtensions.ToGlobalPosition(((RaycastHit)(ref val)).point); return ((GlobalPosition)(ref val2)).AsVector3(); } } catch { } return Vector3.zero; } } public static class ConstructionAssets { private const string BundleName = "constructionops"; private const string CratePrefabName = "ConstructionCrate"; private const string CrateDefinitionName = "ConstructionCrateDefinition"; private const string MountName = "ConstructionMount"; private const string InfoName = "ConstructionInfo"; private const string ConfigName = "ConstructionCatalogue"; private static AssetBundle _bundle; public static bool Loaded => (Object)(object)_bundle != (Object)null; public static GameObject CratePrefab { get; private set; } public static UnitDefinition CrateDefinition { get; private set; } public static WeaponMount Mount { get; private set; } public static WeaponInfo Info { get; private set; } public static ConstructionConfig Config { get; private set; } public static bool Load(ManualLogSource log) { if ((Object)(object)_bundle != (Object)null) { return true; } string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrEmpty(directoryName)) { log.LogError((object)"[assets] could not resolve the plugin directory"); return false; } string text = Path.Combine(directoryName, "constructionops"); if (!File.Exists(text)) { log.LogError((object)("[assets] bundle missing at " + text + " — run the ThunderKit Deploy pipeline")); return false; } _bundle = AssetBundle.LoadFromFile(text); if ((Object)(object)_bundle == (Object)null) { log.LogError((object)("[assets] AssetBundle.LoadFromFile returned null for " + text)); return false; } CratePrefab = _bundle.LoadAsset("ConstructionCrate"); CrateDefinition = _bundle.LoadAsset("ConstructionCrateDefinition"); Mount = _bundle.LoadAsset("ConstructionMount"); Info = _bundle.LoadAsset("ConstructionInfo"); Config = _bundle.LoadAsset("ConstructionCatalogue"); if ((Object)(object)Config == (Object)null) { log.LogWarning((object)"[assets] no 'ConstructionCatalogue' in the bundle — using the built-in catalogue."); } if ((Object)(object)CratePrefab == (Object)null || (Object)(object)CrateDefinition == (Object)null || (Object)(object)Mount == (Object)null) { log.LogError((object)($"[assets] missing asset(s) — crate={(Object)(object)CratePrefab != (Object)null} " + $"definition={(Object)(object)CrateDefinition != (Object)null} mount={(Object)(object)Mount != (Object)null}. " + "Bundle contains: " + string.Join(", ", _bundle.GetAllAssetNames()))); return false; } log.LogInfo((object)$"[assets] bundle loaded, {_bundle.GetAllAssetNames().Length} assets"); return true; } } public static class ConstructionCatalogue { public sealed class MaterialLoad { public readonly string JsonKey; public readonly int Count; public string DisplayName { get { if (Encyclopedia.Lookup != null && Encyclopedia.Lookup.TryGetValue(JsonKey, out var value) && (Object)(object)value != (Object)null && !string.IsNullOrEmpty(value.unitName)) { return value.unitName; } return JsonKey; } } public MaterialLoad(string jsonKey, int count) { JsonKey = jsonKey; Count = count; } } public sealed class BuildOption { public string Id; public string BuildingJsonKey; public string DisplayName; public string Summary; public MaterialLoad[] BaseManifest; public bool RequiresFriendlyAirbase; public bool RequiresWarheadStorage; public string[] ProductionOptions = new string[0]; public float ProductionInterval = 900f; public BuildingDefinition Definition { get { if (Encyclopedia.Lookup != null && Encyclopedia.Lookup.TryGetValue(BuildingJsonKey, out var value)) { return (BuildingDefinition)(object)((value is BuildingDefinition) ? value : null); } return null; } } } private const string Munitions = "MunitionsContainer1"; private const string NavalSupply = "NavalSupplyContainer1"; private const string RadarCrate = "RadarContainer1"; private const string Fuel = "FuelContainer3"; private static List _resolved; private static ConstructionConfig _source; private static readonly List Empty = new List(); private static int MaxPerLine { get { if (_resolved == null) { Resolve(); } if (!((Object)(object)_source != (Object)null)) { return 20; } return Mathf.Max(1, _source.maxLoadsPerLine); } } public static IList Options { get { if (_resolved == null) { Resolve(); } return _resolved ?? Empty; } } public static ConstructionConfig BuiltIn() { ConstructionConfig constructionConfig = ScriptableObject.CreateInstance(); constructionConfig.builds = new ConstructionConfig.BuildEntry[6] { Entry("radar", "radarStation1", "Radar Station", "Faction-wide detection from this position.", Mat("RadarContainer1", 2)), Entry("bunker", "ammunitionBunker", "Munitions Bunker", "Forward rearming point. Adds warhead storage when built at an airbase.", Mat("MunitionsContainer1", 2), Mat("NavalSupplyContainer1", 1)), Airbased(Entry("shelter", "shelter1", "Hardened Aircraft Shelter", "Aircraft capacity and warhead storage. Must be built at a friendly airbase.", Mat("NavalSupplyContainer1", 2), Mat("MunitionsContainer1", 1))), Entry("depot", "VehicleDepot1", "Vehicle Depot", "Produces ground vehicles here, rather than into faction-wide supply.", Mat("NavalSupplyContainer1", 2), Mat("MunitionsContainer1", 1), Mat("FuelContainer3", 1)), Factory(Entry("factory", "factory_large", "Aircraft Factory", "Adds airframes to faction supply, usable from any friendly airbase. Each airframe has its own material cost.", Mat("NavalSupplyContainer1", 3), Mat("MunitionsContainer1", 2), Mat("FuelContainer3", 1))), Warheads(Entry("warheads", "enrichmentPlant1", "Nuclear Warhead Plant", "Produces warheads. Useless unless the faction owns an airbase with storage.", Mat("NavalSupplyContainer1", 4), Mat("MunitionsContainer1", 4), Mat("FuelContainer3", 2))) }; return constructionConfig; } private static ConstructionConfig.MaterialEntry Mat(string key, int count) { return new ConstructionConfig.MaterialEntry { jsonKey = key, count = count }; } private static ConstructionConfig.BuildEntry Entry(string id, string building, string name, string summary, params ConstructionConfig.MaterialEntry[] manifest) { return new ConstructionConfig.BuildEntry { id = id, buildingJsonKey = building, displayName = name, summary = summary, manifest = manifest }; } private static ConstructionConfig.BuildEntry Airbased(ConstructionConfig.BuildEntry e) { e.requiresFriendlyAirbase = true; return e; } private static ConstructionConfig.BuildEntry Factory(ConstructionConfig.BuildEntry e) { e.productionSource = ConstructionConfig.ProductionSource.AllAircraft; e.productionInterval = 900f; return e; } private static ConstructionConfig.BuildEntry Warheads(ConstructionConfig.BuildEntry e) { e.requiresWarheadStorage = true; e.productionSource = ConstructionConfig.ProductionSource.ExplicitList; e.productionOptions = new string[1] { "Nuclear Warhead" }; e.productionInterval = 1200f; return e; } public static BuildOption ById(string id) { if (string.IsNullOrEmpty(id)) { return null; } IList options = Options; for (int i = 0; i < options.Count; i++) { if (options[i].Id == id) { return options[i]; } } return null; } public static bool IsMaterial(string jsonKey) { if (string.IsNullOrEmpty(jsonKey)) { return false; } IList options = Options; for (int i = 0; i < options.Count; i++) { for (int j = 0; j < options[i].BaseManifest.Length; j++) { if (options[i].BaseManifest[j].JsonKey == jsonKey) { return true; } } } return false; } public static int ProductionRank(string productionKey) { if (string.IsNullOrEmpty(productionKey) || Encyclopedia.Lookup == null) { return 0; } if (!Encyclopedia.Lookup.TryGetValue(productionKey, out var value)) { return 0; } AircraftDefinition val = (AircraftDefinition)(object)((value is AircraftDefinition) ? value : null); if ((Object)(object)val == (Object)null || (Object)(object)val.aircraftParameters == (Object)null) { return 0; } return Mathf.Max(0, val.aircraftParameters.rankRequired); } public static List ResolveManifest(BuildOption option, string productionKey) { List list = new List(); if (option == null) { return list; } int maxPerLine = MaxPerLine; ConstructionConfig.AircraftProductionEntry aircraftProductionEntry = AircraftOverride(productionKey); if (aircraftProductionEntry != null && aircraftProductionEntry.manifestOverride != null && aircraftProductionEntry.manifestOverride.Length != 0) { ConstructionConfig.MaterialEntry[] manifestOverride = aircraftProductionEntry.manifestOverride; foreach (ConstructionConfig.MaterialEntry materialEntry in manifestOverride) { if (materialEntry != null && !string.IsNullOrEmpty(materialEntry.jsonKey)) { list.Add(new MaterialLoad(materialEntry.jsonKey, Mathf.Clamp(materialEntry.count, 1, maxPerLine))); } } if (list.Count > 0) { return list; } Plugin.Log.LogWarning((object)("[catalogue] the manifest override for '" + productionKey + "' has no usable lines — using the build's own manifest.")); } float num = ((aircraftProductionEntry != null && aircraftProductionEntry.loadsMultiplier > 0f) ? aircraftProductionEntry.loadsMultiplier : 1f); MaterialLoad[] baseManifest = option.BaseManifest; foreach (MaterialLoad materialLoad in baseManifest) { int num2 = Mathf.CeilToInt((float)materialLoad.Count * num); list.Add(new MaterialLoad(materialLoad.JsonKey, Mathf.Clamp(num2, 1, maxPerLine))); } return list; } public static ConstructionConfig.AircraftProductionEntry AircraftOverride(string productionKey) { if (_resolved == null) { Resolve(); } if (!((Object)(object)_source != (Object)null)) { return null; } return _source.FindAircraft(productionKey); } public static float ProductionInterval(BuildOption option, string productionKey) { ConstructionConfig.AircraftProductionEntry aircraftProductionEntry = AircraftOverride(productionKey); if (aircraftProductionEntry != null && aircraftProductionEntry.productionInterval > 0f) { return aircraftProductionEntry.productionInterval; } return option?.ProductionInterval ?? 900f; } public static int TotalLoads(IList manifest) { int num = 0; for (int i = 0; i < manifest.Count; i++) { num += manifest[i].Count; } return num; } private static void Resolve() { if (Encyclopedia.Lookup == null) { return; } _source = ConstructionAssets.Config; if ((Object)(object)_source == (Object)null || _source.builds == null || _source.builds.Length == 0) { if ((Object)(object)_source != (Object)null) { Plugin.Log.LogWarning((object)"[catalogue] the bundled catalogue asset has no builds — falling back to the built-in table."); } _source = BuiltIn(); } _resolved = new List(); List list = new List(); ConstructionConfig.BuildEntry[] builds = _source.builds; foreach (ConstructionConfig.BuildEntry buildEntry in builds) { if (buildEntry != null && !string.IsNullOrEmpty(buildEntry.buildingJsonKey)) { BuildOption buildOption = Convert(buildEntry); if (buildOption == null) { list.Add(buildEntry.buildingJsonKey); } else { _resolved.Add(buildOption); } } } if (_resolved.Count == 0) { Plugin.Log.LogError((object)"[catalogue] no buildable types resolved — the panel will be empty."); } else if (list.Count > 0) { Plugin.Log.LogWarning((object)("[catalogue] dropped, not present in this install: " + string.Join(", ", list.ToArray()))); } Plugin.Log.LogInfo((object)($"[catalogue] {_resolved.Count} build type(s) from " + (((Object)(object)ConstructionAssets.Config != (Object)null) ? "the bundled asset" : "the built-in table") + $"; {((_source.aircraftProduction != null) ? _source.aircraftProduction.Length : 0)} " + "per-airframe entr(ies).")); } private static BuildOption Convert(ConstructionConfig.BuildEntry entry) { if (!Encyclopedia.Lookup.TryGetValue(entry.buildingJsonKey, out var value)) { return null; } BuildingDefinition val = (BuildingDefinition)(object)((value is BuildingDefinition) ? value : null); if ((Object)(object)val == (Object)null || (Object)(object)((UnitDefinition)val).unitPrefab == (Object)null) { return null; } List list = new List(); if (entry.manifest != null) { ConstructionConfig.MaterialEntry[] manifest = entry.manifest; foreach (ConstructionConfig.MaterialEntry materialEntry in manifest) { if (materialEntry != null && !string.IsNullOrEmpty(materialEntry.jsonKey)) { list.Add(new MaterialLoad(materialEntry.jsonKey, Mathf.Max(1, materialEntry.count))); } } } if (list.Count == 0) { Plugin.Log.LogWarning((object)("[catalogue] '" + entry.id + "' has an empty manifest and was skipped — a build with no materials would complete immediately.")); return null; } return new BuildOption { Id = (string.IsNullOrEmpty(entry.id) ? entry.buildingJsonKey : entry.id), BuildingJsonKey = entry.buildingJsonKey, DisplayName = (string.IsNullOrEmpty(entry.displayName) ? ((UnitDefinition)val).unitName : entry.displayName), Summary = (entry.summary ?? ""), BaseManifest = list.ToArray(), RequiresFriendlyAirbase = entry.requiresFriendlyAirbase, RequiresWarheadStorage = entry.requiresWarheadStorage, ProductionOptions = ResolveProduction(entry), ProductionInterval = Mathf.Max(1f, entry.productionInterval) }; } private static string[] ResolveProduction(ConstructionConfig.BuildEntry entry) { switch (entry.productionSource) { case ConstructionConfig.ProductionSource.ExplicitList: return entry.productionOptions ?? new string[0]; case ConstructionConfig.ProductionSource.AllAircraft: { List list = new List(); if ((Object)(object)Encyclopedia.i != (Object)null && Encyclopedia.i.aircraft != null) { foreach (AircraftDefinition item in Encyclopedia.i.aircraft) { if (!((Object)(object)item == (Object)null) && !string.IsNullOrEmpty(((UnitDefinition)item).jsonKey)) { ConstructionConfig.AircraftProductionEntry aircraftProductionEntry = (((Object)(object)_source != (Object)null) ? _source.FindAircraft(((UnitDefinition)item).jsonKey) : null); if (aircraftProductionEntry == null || aircraftProductionEntry.canProduce) { list.Add(((UnitDefinition)item).jsonKey); } } } } list.Sort((string a, string b) => ProductionRank(a).CompareTo(ProductionRank(b))); return list.ToArray(); } default: return new string[0]; } } public static List AuthoredIds() { List list = new List(); foreach (ConstructionConfig.BuildEntry item in AuthoredEntries()) { if (!string.IsNullOrEmpty(item.id)) { list.Add(item.id); } } return list; } public static string AuthoredDisplayName(string id) { foreach (ConstructionConfig.BuildEntry item in AuthoredEntries()) { if (item.id == id) { return string.IsNullOrEmpty(item.displayName) ? item.buildingJsonKey : item.displayName; } } return null; } private static IEnumerable AuthoredEntries() { ConstructionConfig constructionConfig = ConstructionAssets.Config; if ((Object)(object)constructionConfig == (Object)null || constructionConfig.builds == null || constructionConfig.builds.Length == 0) { constructionConfig = BuiltIn(); } ConstructionConfig.BuildEntry[] builds = constructionConfig.builds; foreach (ConstructionConfig.BuildEntry buildEntry in builds) { if (buildEntry != null) { yield return buildEntry; } } } public static string ProductionName(string key) { if (string.IsNullOrEmpty(key)) { return "nothing"; } if (Encyclopedia.Lookup != null && Encyclopedia.Lookup.TryGetValue(key, out var value) && (Object)(object)value != (Object)null && !string.IsNullOrEmpty(value.unitName)) { return value.unitName; } return key; } public static void Reset() { _resolved = null; _source = null; } } [CreateAssetMenu(fileName = "ConstructionCatalogue", menuName = "ConstructionOps/Construction Catalogue", order = 0)] public class ConstructionConfig : ScriptableObject { public enum ProductionSource { None, ExplicitList, AllAircraft } [Serializable] public class MaterialEntry { [Tooltip("Encyclopedia jsonKey of the STOCK container that satisfies this line. e.g. MunitionsContainer1, NavalSupplyContainer1, RadarContainer1, FuelContainer3")] public string jsonKey = ""; [Tooltip("How many of it must be flown in.")] [Min(1f)] public int count = 1; } [Serializable] public class BuildEntry { [Tooltip("Stable id, written to the site state file. Changing it orphans sites that are already part-built in a running mission.")] public string id = ""; [Tooltip("Encyclopedia jsonKey of the STOCK BuildingDefinition to spawn.")] public string buildingJsonKey = ""; [Tooltip("Shown in the panel. Leave blank to use the building's own unitName.")] public string displayName = ""; [Tooltip("One line explaining what the finished building does.")] [TextArea(2, 4)] public string summary = ""; [Tooltip("The loads that must be flown in, in panel order. For the Aircraft Factory this is only the fallback — a per-airframe entry below normally decides the cost.")] public MaterialEntry[] manifest = new MaterialEntry[0]; [Tooltip("Refuse this build unless a friendly airbase is within AirbaseAttachRadius. Needed by anything carrying Hangar or WarheadStorage, which are inert unless the building is a member of an airbase.")] public bool requiresFriendlyAirbase; [Tooltip("Refuse this build unless the faction already owns an airbase with warhead storage. Without one FactionHQ.AddWarheadStockpile discards the output and the plant produces nothing, forever.")] public bool requiresWarheadStorage; public ProductionSource productionSource; [Tooltip("Used when productionSource is ExplicitList. Either an Encyclopedia jsonKey, or the literal string \"Nuclear Warhead\", which Factory.SetFactory treats as a special case rather than looking it up.")] public string[] productionOptions = new string[0]; [Tooltip("Seconds between production ticks. The game's own default is 900. For the Aircraft Factory a per-airframe entry below can override this.")] [Min(1f)] public float productionInterval = 900f; } [Serializable] public class AircraftProductionEntry { [Tooltip("Encyclopedia jsonKey of the aircraft, e.g. Fighter1 (FS-12 Revoker), QuadVTOL1 (VL-49 Tarantula), FastBomber1 (Alkyon AB-4). The full list for THIS install is in the AIRCRAFT ROSTER section of BepInEx/ConstructionOps.probe.txt.")] public string aircraftJsonKey = ""; [Tooltip("Human note for whoever edits this asset. Not used by the mod.")] public string aircraftName = ""; [Tooltip("Untick to remove this airframe from the factory's production dropdown.")] public bool canProduce = true; [Tooltip("Exact manifest for a factory producing this airframe. This is the normal way to tune an aircraft. Leave EMPTY to fall back to loadsMultiplier, and then to the Aircraft Factory's own base manifest.")] public MaterialEntry[] manifestOverride = new MaterialEntry[0]; [Tooltip("Shortcut: multiply the factory's base manifest instead of writing one out. 0 = unused. Ignored when manifestOverride has entries.")] [Min(0f)] public float loadsMultiplier; [Tooltip("Seconds between this airframe rolling off the line. 0 = use the Aircraft Factory's own productionInterval.")] [Min(0f)] public float productionInterval; } [Tooltip("The catalogue, in the order the panel offers it.")] public BuildEntry[] builds = new BuildEntry[0]; [Tooltip("Per-airframe cost and rate for the Aircraft Factory. An airframe with no entry costs the factory's own base manifest.")] public AircraftProductionEntry[] aircraftProduction = new AircraftProductionEntry[0]; [Tooltip("Safety clamp on any single manifest line, so a typo cannot demand 500 containers.")] [Min(1f)] public int maxLoadsPerLine = 20; public AircraftProductionEntry FindAircraft(string jsonKey) { if (string.IsNullOrEmpty(jsonKey) || aircraftProduction == null) { return null; } for (int i = 0; i < aircraftProduction.Length; i++) { AircraftProductionEntry aircraftProductionEntry = aircraftProduction[i]; if (aircraftProductionEntry != null && string.Equals(aircraftProductionEntry.aircraftJsonKey, jsonKey, StringComparison.OrdinalIgnoreCase)) { return aircraftProductionEntry; } } return null; } } public static class ConstructionLogistics { private sealed class Sortie { public Loadout Loadout; public AircraftDefinition Definition; public FactionHQ HQ; public int SiteId = -1; public string Objective; public string MaterialKey; public GlobalPosition Target; public float LaunchedAt; public Aircraft Aircraft; public ConstructionTransportState State; public bool Released; public float ReleasedAt; public bool IsCrate => string.IsNullOrEmpty(MaterialKey); } private static readonly List _sorties = new List(); private static readonly Dictionary _mountForMaterial = new Dictionary(); private static bool _mountsMapped; private static readonly HashSet _warned = new HashSet(); private static float _lastDispatch; private static FieldInfo _mountedCargoField; private static FieldInfo _activeAiField; public static int ActiveSorties => _sorties.Count; public static void Reset() { _sorties.Clear(); _mountForMaterial.Clear(); _mountsMapped = false; _warned.Clear(); _lastDispatch = 0f; } public static void Tick() { if (Plugin.AiEnabled.Value && ConstructionManager.CanBuild) { PruneSorties(); AdoptSpawnedHaulers(); TaskAirborneHaulers(); Plan(); } } private static void PruneSorties() { for (int num = _sorties.Count - 1; num >= 0; num--) { Sortie sortie = _sorties[num]; float num2 = Time.timeSinceLevelLoad - sortie.LaunchedAt; if ((Object)(object)sortie.Aircraft == (Object)null) { if (num2 > 60f) { Plugin.Log.LogInfo((object)("[ai] sortie for " + Describe(sortie) + " never appeared — giving up on it.")); _sorties.RemoveAt(num); } } else { bool flag = (Object)(object)sortie.Aircraft == (Object)null || ((Unit)sortie.Aircraft).disabled; bool flag2 = sortie.State != null && sortie.State.Finished; if ((!sortie.Released || !(Time.timeSinceLevelLoad - sortie.ReleasedAt < 60f)) && (flag || flag2 || num2 > Plugin.AiSortieTimeout.Value + 120f)) { if (flag && !sortie.Released) { Plugin.Log.LogInfo((object)("[ai] hauler for " + Describe(sortie) + " was lost before delivering.")); } _sorties.RemoveAt(num); } } } } private static void AdoptSpawnedHaulers() { List allAircraft = UnitRegistry.allAircraft; for (int i = 0; i < allAircraft.Count; i++) { Aircraft val = allAircraft[i]; if ((Object)(object)val == (Object)null || ((Unit)val).disabled || (Object)(object)val.Player != (Object)null) { continue; } for (int j = 0; j < _sorties.Count; j++) { Sortie sortie = _sorties[j]; if (!((Object)(object)sortie.Aircraft != (Object)null) && val.loadout == sortie.Loadout) { sortie.Aircraft = val; Plugin.Log.LogInfo((object)("[ai] hauler " + ((UnitDefinition)val.definition).jsonKey + " launched for " + Describe(sortie) + ".")); break; } } } } private static void TaskAirborneHaulers() { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < _sorties.Count; i++) { Sortie sortie = _sorties[i]; if ((Object)(object)sortie.Aircraft == (Object)null || ((Unit)sortie.Aircraft).disabled || sortie.State != null) { continue; } Pilot val = FirstPilot(sortie.Aircraft); if ((Object)(object)val == (Object)null || val.dead || val.ejected || val.playerControlled) { continue; } PilotBaseState currentState = val.currentState; if (currentState is AIPilotCombatModes || currentState is AIHeloCombatState || currentState is AIHeloTransportState) { Sortie captured = sortie; sortie.State = new ConstructionTransportState(sortie.Aircraft, sortie.Target, () => StillWanted(captured), delegate { OnReleased(captured); }); val.SwitchState((PilotBaseState)(object)sortie.State); Plugin.Log.LogInfo((object)("[ai] hauler " + ((UnitDefinition)sortie.Aircraft.definition).jsonKey + " tasked: " + Describe(sortie) + ".")); } } } private static bool StillWanted(Sortie sortie) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (sortie.SiteId < 0) { return true; } ConstructionSite constructionSite = FindSiteById(sortie.SiteId); if (constructionSite == null || constructionSite.Completed) { return false; } if (constructionSite.Build != null && !string.IsNullOrEmpty(sortie.MaterialKey) && !constructionSite.Wants(sortie.MaterialKey)) { return false; } sortie.Target = constructionSite.Position; sortie.State?.Retarget(constructionSite.Position); return true; } private static void OnReleased(Sortie sortie) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) sortie.Released = true; sortie.ReleasedAt = Time.timeSinceLevelLoad; float num = FastMath.Distance(sortie.State.ReleasePosition, sortie.Target); Plugin.Log.LogInfo((object)($"[ai] load released for {Describe(sortie)} — {num:F0} m from the aim point " + $"(delivery radius {Plugin.DeliveryRadius.Value:F0} m).")); } private static void Plan() { MissionRunner runner = MissionManager.Runner; if (runner == null || runner.ActiveObjectives == null || Time.timeSinceLevelLoad - _lastDispatch < Plugin.AiDispatchInterval.Value) { return; } for (int i = 0; i < runner.ActiveObjectives.Count; i++) { if (!(runner.ActiveObjectives[i] is ConstructBuildingObjective { AiBuild: not false } constructBuildingObjective)) { continue; } FactionHQ factionHQ = ((Objective)constructBuildingObjective).FactionHQ; if ((Object)(object)factionHQ == (Object)null || constructBuildingObjective.CountMatching() >= constructBuildingObjective.RequiredCountForDiagnostics) { continue; } ConstructionSite constructionSite = FindSiteFor(constructBuildingObjective, factionHQ); if (constructionSite == null) { if (TryLaunchCrate(constructBuildingObjective, factionHQ)) { break; } } else if ((!constructionSite.AwaitingChoice || AutoChoose(constructionSite, constructBuildingObjective)) && constructionSite.Build != null && !constructionSite.Completed && TryLaunchMaterial(constructionSite, constructBuildingObjective, factionHQ)) { break; } } } private static ConstructionSite FindSiteFor(ConstructBuildingObjective objective, FactionHQ hq) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) GlobalPosition targetPosition = objective.TargetPosition; float targetRange = objective.TargetRange; float num = targetRange * targetRange; IList sites = ConstructionManager.Sites; for (int i = 0; i < sites.Count; i++) { ConstructionSite constructionSite = sites[i]; if (!constructionSite.Completed && !((Object)(object)constructionSite.HQ != (Object)(object)hq) && !(FastMath.SquareDistance(constructionSite.Position, targetPosition) > num)) { return constructionSite; } } return null; } private static ConstructionSite FindSiteById(int id) { IList sites = ConstructionManager.Sites; for (int i = 0; i < sites.Count; i++) { if (sites[i].Id == id) { return sites[i]; } } return null; } private static bool AutoChoose(ConstructionSite site, ConstructBuildingObjective objective) { if (Time.timeSinceLevelLoad - site.CreatedAt < Plugin.AiClaimDelay.Value) { return false; } ConstructionCatalogue.BuildOption buildOption = ResolveBuild(site, objective); if (buildOption == null) { WarnOnce(objective, "cannot pick a build for '" + objective.BuildIdForDiagnostics + "' — no catalogue entry matches, or its prerequisites are unmet here."); return false; } string text = CheapestProduction(buildOption); if (!ConstructionManager.ChooseBuild(site, buildOption, text)) { return false; } Plugin.Log.LogInfo((object)($"[ai] site {site.Id} tasked by objective " + "'" + ((Objective)objective).SavedObjective.UniqueName + "' -> " + buildOption.DisplayName + ((text != null) ? (" producing " + ConstructionCatalogue.ProductionName(text)) : ""))); return true; } private static ConstructionCatalogue.BuildOption ResolveBuild(ConstructionSite site, ConstructBuildingObjective objective) { string buildIdForDiagnostics = objective.BuildIdForDiagnostics; if (!string.IsNullOrEmpty(buildIdForDiagnostics) && buildIdForDiagnostics != "any") { ConstructionCatalogue.BuildOption buildOption = ConstructionCatalogue.ById(buildIdForDiagnostics); if (buildOption == null || !Buildable(site, buildOption)) { return null; } return buildOption; } ConstructionCatalogue.BuildOption result = null; int num = int.MaxValue; IList options = ConstructionCatalogue.Options; for (int i = 0; i < options.Count; i++) { ConstructionCatalogue.BuildOption buildOption2 = options[i]; if (Buildable(site, buildOption2)) { int num2 = ConstructionCatalogue.TotalLoads(ConstructionCatalogue.ResolveManifest(buildOption2, CheapestProduction(buildOption2))); if (num2 > 0 && num2 < num) { result = buildOption2; num = num2; } } } return result; } private static bool Buildable(ConstructionSite site, ConstructionCatalogue.BuildOption option) { if (option == null || (Object)(object)option.Definition == (Object)null) { return false; } if (option.RequiresFriendlyAirbase && (Object)(object)ConstructionManager.NearestFriendlyAirbase(site, Plugin.AirbaseAttachRadius.Value) == (Object)null) { return false; } if (option.RequiresWarheadStorage && !ConstructionManager.FactionHasStorage(site.HQ)) { return false; } return true; } private static string CheapestProduction(ConstructionCatalogue.BuildOption option) { if (option == null || option.ProductionOptions == null || option.ProductionOptions.Length == 0) { return null; } string text = null; int num = int.MaxValue; string[] productionOptions = option.ProductionOptions; foreach (string text2 in productionOptions) { if (!string.IsNullOrEmpty(text2)) { int num2 = ConstructionCatalogue.ProductionRank(text2); if (num2 < num) { text = text2; num = num2; } } } return text ?? option.ProductionOptions[0]; } private static bool TryLaunchCrate(ConstructBuildingObjective objective, FactionHQ hq) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (!AllowedAnother(hq, -1, objective)) { return false; } if ((Object)(object)ConstructionAssets.Mount == (Object)null) { return false; } GlobalPosition? val = FindBuildableSpot(objective.TargetPosition, Mathf.Min(objective.TargetRange, 400f)); if (!val.HasValue) { WarnOnce(objective, "no ground flat enough to build on within range of the target " + $"spot (limit {Plugin.MaxSlopeDegrees.Value:F0}°) — the AI has " + "nowhere to put a crate."); return false; } return Launch(hq, ConstructionAssets.Mount, val.Value, -1, ((Objective)objective).SavedObjective.UniqueName, null); } private static bool TryLaunchMaterial(ConstructionSite site, ConstructBuildingObjective objective, FactionHQ hq) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (!AllowedAnother(hq, site.Id, objective)) { return false; } for (int i = 0; i < site.Required.Count; i++) { ConstructionCatalogue.MaterialLoad materialLoad = site.Required[i]; if (site.Outstanding(materialLoad) - InFlightFor(site.Id, materialLoad.JsonKey) > 0) { WeaponMount val = MountForMaterial(materialLoad.JsonKey); if ((Object)(object)val == (Object)null) { WarnOnce(objective, "no cargo mount in the Encyclopedia delivers '" + materialLoad.JsonKey + "' — the AI cannot fly this build's manifest."); return false; } return Launch(hq, val, site.Position, site.Id, ((Objective)objective).SavedObjective.UniqueName, materialLoad.JsonKey); } } return false; } private static bool Launch(FactionHQ hq, WeaponMount mount, GlobalPosition target, int siteId, string objectiveName, string materialKey) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (!ChooseHauler(hq, mount, target, out var definition, out var airbase, out var cargoSet)) { return false; } Loadout val = BuildLoadout(definition, mount, cargoSet); if (val == null) { return false; } AircraftParameters aircraftParameters = definition.aircraftParameters; LiveryKey val2 = default(LiveryKey); ((LiveryKey)(ref val2))..ctor(aircraftParameters.GetRandomLiveryForFaction(hq.faction)); TrySpawnResult val3; try { val3 = airbase.TrySpawnAircraft((Player)null, definition, val2, val, aircraftParameters.DefaultFuelLevel); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ai] spawning " + ((UnitDefinition)definition).jsonKey + " threw: " + ex.Message)); return false; } if (!val3.Allowed) { return false; } _sorties.Add(new Sortie { Loadout = val, Definition = definition, HQ = hq, SiteId = siteId, Objective = objectiveName, MaterialKey = materialKey, Target = target, LaunchedAt = Time.timeSinceLevelLoad }); _lastDispatch = Time.timeSinceLevelLoad; return true; } private static bool ChooseHauler(FactionHQ hq, WeaponMount mount, GlobalPosition target, out AircraftDefinition definition, out Airbase airbase, out int cargoSet) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) definition = null; airbase = null; cargoSet = -1; int num = int.MinValue; float num2 = float.MaxValue; foreach (AircraftDefinition item in Encyclopedia.i.aircraft) { if ((Object)(object)item == (Object)null || (Object)(object)((UnitDefinition)item).unitPrefab == (Object)null) { continue; } int num3 = CargoSetIndex(item, mount); if (num3 < 0 || hq.GetUnitSupply((UnitDefinition)(object)item) <= Plugin.AiSupplyReserve.Value) { continue; } float distance; Airbase val = NearestSpawnableAirbase(hq, item, target, out distance); if (!((Object)(object)val == (Object)null)) { int num4 = ((IsRotorcraft(item) && Plugin.AiPreferRotorcraft.Value) ? 1000 : 0) - item.aircraftParameters.rankRequired; if (num4 >= num && (num4 != num || !(distance >= num2))) { num = num4; num2 = distance; definition = item; airbase = val; cargoSet = num3; } } } return (Object)(object)definition != (Object)null; } private static int CargoSetIndex(AircraftDefinition definition, WeaponMount mount) { WeaponManager componentInChildren = ((UnitDefinition)definition).unitPrefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || componentInChildren.hardpointSets == null) { return -1; } for (int i = 0; i < componentInChildren.hardpointSets.Length; i++) { HardpointSet val = componentInChildren.hardpointSets[i]; if (val != null && val.weaponOptions != null && val.weaponOptions.Contains(mount)) { return i; } } return -1; } private static Loadout BuildLoadout(AircraftDefinition definition, WeaponMount mount, int cargoSet) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown WeaponManager componentInChildren = ((UnitDefinition)definition).unitPrefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || componentInChildren.hardpointSets == null) { return null; } Loadout val = new Loadout(); for (int i = 0; i < componentInChildren.hardpointSets.Length; i++) { val.weapons.Add((i == cargoSet) ? mount : null); } return val; } private static Airbase NearestSpawnableAirbase(FactionHQ hq, AircraftDefinition definition, GlobalPosition target, out float distance) { //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) Airbase result = null; distance = float.MaxValue; foreach (Airbase airbasis in hq.GetAirbases()) { if (!((Object)(object)airbasis == (Object)null) && airbasis.CanSpawnAircraft(definition)) { float num = FastMath.Distance(GlobalPositionExtensions.GlobalPosition(airbasis.center), target); if (!(num >= distance)) { distance = num; result = airbasis; } } } return result; } private static GlobalPosition? FindBuildableSpot(GlobalPosition centre, float searchRadius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (TryGround(centre, out var ground)) { return ground; } float num = Mathf.Max(20f, searchRadius); for (float num2 = 40f; num2 <= num; num2 += 40f) { for (int i = 0; i < 8; i++) { float num3 = (float)i * MathF.PI * 0.25f; if (TryGround(centre + new Vector3(Mathf.Cos(num3) * num2, 0f, Mathf.Sin(num3) * num2), out var ground2)) { return ground2; } } } return null; } private static bool TryGround(GlobalPosition position, out GlobalPosition ground) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0042: 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) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) ground = position; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(GlobalPositionExtensions.ToLocalPosition(position) + Vector3.up * 2000f, Vector3.down, ref val, 4000f, LayerMask.op_Implicit(PhysicsLayers.StaticsMask))) { return false; } if (((RaycastHit)(ref val)).point.y <= Datum.LocalSeaY) { return false; } if (Vector3.Angle(((RaycastHit)(ref val)).normal, Vector3.up) > Plugin.MaxSlopeDegrees.Value) { return false; } ground = GlobalPositionExtensions.ToGlobalPosition(((RaycastHit)(ref val)).point); return true; } private static bool AllowedAnother(FactionHQ hq, int siteId, ConstructBuildingObjective objective) { string uniqueName = ((Objective)objective).SavedObjective.UniqueName; int num = 0; int num2 = 0; for (int i = 0; i < _sorties.Count; i++) { Sortie sortie = _sorties[i]; if (!((Object)(object)sortie.HQ != (Object)(object)hq)) { num++; if (sortie.Objective == uniqueName || (siteId >= 0 && sortie.SiteId == siteId)) { num2++; } } } if (num2 >= Plugin.AiMaxSortiesPerSite.Value) { return false; } if (num >= Plugin.AiMaxSortiesPerFaction.Value) { return false; } int num3 = ActiveAiAircraft(hq); if (num3 >= 0 && num3 >= hq.AIAircraftLimit) { return false; } return true; } private static int InFlightFor(int siteId, string materialKey) { int num = 0; for (int i = 0; i < _sorties.Count; i++) { Sortie sortie = _sorties[i]; if (sortie.SiteId == siteId && sortie.MaterialKey == materialKey) { num++; } } return num; } private static int ActiveAiAircraft(FactionHQ hq) { try { if (_activeAiField == null) { _activeAiField = typeof(FactionHQ).GetField("activeAIAircraft", BindingFlags.Instance | BindingFlags.NonPublic); } if (_activeAiField == null) { return -1; } return (_activeAiField.GetValue(hq) is List list) ? list.Count : (-1); } catch { return -1; } } private static WeaponMount MountForMaterial(string jsonKey) { if (string.IsNullOrEmpty(jsonKey)) { return null; } EnsureMountMap(); if (!_mountForMaterial.TryGetValue(jsonKey, out var value)) { return null; } return value; } private static void EnsureMountMap() { if (_mountsMapped) { return; } _mountsMapped = true; if ((Object)(object)Encyclopedia.i == (Object)null || Encyclopedia.i.weaponMounts == null) { return; } foreach (WeaponMount weaponMount in Encyclopedia.i.weaponMounts) { if (!((Object)(object)weaponMount == (Object)null) && weaponMount.Cargo && !((Object)(object)weaponMount.prefab == (Object)null)) { UnitDefinition val = CargoDefinitionOf(weaponMount); if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.jsonKey) && !_mountForMaterial.ContainsKey(val.jsonKey)) { _mountForMaterial.Add(val.jsonKey, weaponMount); } } } Plugin.Log.LogInfo((object)$"[ai] {_mountForMaterial.Count} cargo mount(s) mapped to their containers."); } private static UnitDefinition CargoDefinitionOf(WeaponMount mount) { try { MountedCargo component = mount.prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return null; } if (_mountedCargoField == null) { _mountedCargoField = typeof(MountedCargo).GetField("cargo", BindingFlags.Instance | BindingFlags.NonPublic); } if (_mountedCargoField == null) { return null; } object? value = _mountedCargoField.GetValue(component); return (UnitDefinition)((value is UnitDefinition) ? value : null); } catch { return null; } } private static Pilot FirstPilot(Aircraft aircraft) { if (aircraft.pilots == null || aircraft.pilots.Length == 0) { return null; } return aircraft.pilots[0]; } private static bool IsRotorcraft(AircraftDefinition definition) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 Pilot componentInChildren = ((UnitDefinition)definition).unitPrefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { return (int)componentInChildren.pilotType > 0; } return false; } private static string Describe(Sortie sortie) { if (!sortie.IsCrate) { return $"'{sortie.MaterialKey}' to site {sortie.SiteId}"; } return "a construction crate for '" + sortie.Objective + "'"; } private static void WarnOnce(ConstructBuildingObjective objective, string message) { string item = ((Objective)objective).SavedObjective.UniqueName + "|" + message; if (_warned.Add(item)) { Plugin.Log.LogWarning((object)("[ai] objective '" + ((Objective)objective).SavedObjective.UniqueName + "': " + message)); } } public static void Describe(StringBuilder sb) { if (!Plugin.AiEnabled.Value) { sb.AppendLine("# AI construction: disabled"); return; } sb.AppendLine($"# AI construction: {_sorties.Count} sortie(s) in the air"); for (int i = 0; i < _sorties.Count; i++) { Sortie sortie = _sorties[i]; string text = (((Object)(object)sortie.Aircraft != (Object)null) ? ((UnitDefinition)sortie.Aircraft.definition).jsonKey : "(awaiting spawn)"); string text2 = (sortie.Released ? "released" : ((sortie.State != null) ? "inbound" : "departing")); sb.AppendLine("# " + text + ": " + Describe(sortie) + " — " + text2 + ", " + $"{Time.timeSinceLevelLoad - sortie.LaunchedAt:F0}s airborne"); } } } public static class ConstructionManager { public sealed class CompletedBuild { public string BuildId; public string BuildingJsonKey; public string Production; public string UniqueName; public GlobalPosition Position; public FactionHQ HQ; } private static readonly List _sites = new List(); private static readonly List _completed = new List(); private static readonly HashSet _consumed = new HashSet(); private static int _nextSiteId = 1; private static int _buildCounter; private static bool _dirty; public static IList Sites => _sites; public static IList CompletedBuilds => _completed; public static bool CanBuild { get { if (!InMission) { return false; } NetworkManagerNuclearOption i = NetworkManagerNuclearOption.i; if ((Object)(object)i != (Object)null && (Object)(object)((NetworkManager)i).Server != (Object)null) { return ((NetworkManager)i).Server.Active; } return false; } } public static bool CanCommand { get { if (!InMission) { return false; } if (CanBuild) { return true; } NetworkManagerNuclearOption i = NetworkManagerNuclearOption.i; if ((Object)(object)i != (Object)null && (Object)(object)((NetworkManager)i).Client != (Object)null) { return ((NetworkManager)i).Client.Active; } return false; } } private static bool InMission { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 GameState gameState = GameManager.gameState; if ((int)gameState != 1) { return (int)gameState == 2; } return true; } } public static void MarkDirty() { _dirty = true; } public static void Reset() { _sites.Clear(); _completed.Clear(); _consumed.Clear(); _nextSiteId = 1; _buildCounter = 0; _dirty = false; ConstructionCatalogue.Reset(); ConstructionObjectives.Reset(); ConstructionNet.Reset(); ConstructionLogistics.Reset(); } public static void Tick() { if (!InMission || (Object)(object)ConstructionAssets.CrateDefinition == (Object)null) { return; } ConstructionNet.EnsureRegistered(); if (!CanBuild) { SyncMapIcons(); return; } ScanForSites(); ScanForDeliveries(); ScanForCompletions(); PruneLostSites(); SyncMapIcons(); ConstructionObjectives.Tick(); ConstructionLogistics.Tick(); if (_dirty) { WriteStateFile(); ConstructionNet.BroadcastState(); _dirty = false; } } private static void ScanForSites() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) List allUnits = UnitRegistry.allUnits; for (int i = 0; i < allUnits.Count; i++) { Unit obj = allUnits[i]; Container val = (Container)(object)((obj is Container) ? obj : null); if (!((Object)(object)val == (Object)null) && !((Unit)val).disabled && !((Object)(object)((Unit)val).definition != (Object)(object)ConstructionAssets.CrateDefinition) && val.Networkkinematic && !_consumed.Contains(((Unit)val).persistentID) && FindSiteByCrate(val) == null) { ConstructionSite constructionSite = new ConstructionSite(_nextSiteId++) { Crate = val, Position = GlobalPositionExtensions.GlobalPosition((Unit)(object)val), Rotation = Quaternion.Euler(0f, ((Component)val).transform.eulerAngles.y, 0f), HQ = ((Unit)val).NetworkHQ, CreatedAt = Time.timeSinceLevelLoad }; _sites.Add(constructionSite); _dirty = true; Vector3 val2 = ((GlobalPosition)(ref constructionSite.Position)).AsVector3(); Plugin.Log.LogInfo((object)($"[site] {constructionSite.Id} established at ({val2.x:F0}, {val2.z:F0}) " + "for " + (((Object)(object)constructionSite.HQ != (Object)null) ? constructionSite.HQ.faction.factionName : "no faction") + " — open the map to choose what to build.")); } } } private static ConstructionSite FindSiteByCrate(Container crate) { for (int i = 0; i < _sites.Count; i++) { if (_sites[i].Crate == crate) { return _sites[i]; } } return null; } public static ConstructionSite SiteForCrateId(uint crateId) { if (crateId == 0) { return null; } for (int i = 0; i < _sites.Count; i++) { Container crate = _sites[i].Crate; if ((Object)(object)crate != (Object)null && ((Unit)crate).persistentID.Id == crateId) { return _sites[i]; } } return null; } public static ConstructionSite SiteForCrate(Unit unit) { if ((Object)(object)unit == (Object)null) { return null; } for (int i = 0; i < _sites.Count; i++) { if ((object)_sites[i].Crate == unit) { return _sites[i]; } } return null; } private static void PruneLostSites() { for (int num = _sites.Count - 1; num >= 0; num--) { ConstructionSite constructionSite = _sites[num]; if (!constructionSite.Completed && (!((Object)(object)constructionSite.Crate != (Object)null) || ((Unit)constructionSite.Crate).disabled)) { Plugin.Log.LogInfo((object)($"[site] {constructionSite.Id} lost — its crate was destroyed " + $"with {constructionSite.DeliveredTotal} load(s) delivered.")); _sites.RemoveAt(num); _dirty = true; } } } public static void ApplyNetworkState(ConstructionNet.SiteState[] states) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) if (CanBuild) { return; } _sites.Clear(); if (states == null) { _dirty = false; return; } Unit val2 = default(Unit); for (int i = 0; i < states.Length; i++) { ConstructionNet.SiteState siteState = states[i]; ConstructionSite constructionSite = new ConstructionSite(siteState.siteId) { Completed = siteState.completed, Blocked = (string.IsNullOrEmpty(siteState.blocked) ? null : siteState.blocked), Production = (string.IsNullOrEmpty(siteState.production) ? null : siteState.production) }; PersistentID val = new PersistentID { Id = siteState.crateId }; if (((PersistentID)(ref val)).TryGetUnit(ref val2)) { constructionSite.Crate = (Container)(object)((val2 is Container) ? val2 : null); if ((Object)(object)constructionSite.Crate != (Object)null) { constructionSite.Position = GlobalPositionExtensions.GlobalPosition((Unit)(object)constructionSite.Crate); constructionSite.HQ = ((Unit)constructionSite.Crate).NetworkHQ; } } if (!string.IsNullOrEmpty(siteState.buildId)) { constructionSite.Build = ConstructionCatalogue.ById(siteState.buildId); } int num = ((siteState.materialKeys != null) ? siteState.materialKeys.Length : 0); for (int j = 0; j < num; j++) { constructionSite.Required.Add(new ConstructionCatalogue.MaterialLoad(siteState.materialKeys[j], siteState.required[j])); constructionSite.Delivered[siteState.materialKeys[j]] = siteState.delivered[j]; } _sites.Add(constructionSite); } } private static void SyncMapIcons() { UnitMapIcon val = default(UnitMapIcon); for (int i = 0; i < _sites.Count; i++) { ConstructionSite constructionSite = _sites[i]; if (!((Object)(object)constructionSite.Crate == (Object)null) && !((Unit)constructionSite.Crate).disabled && DynamicMap.TryGetMapIcon((Unit)(object)constructionSite.Crate, ref val) && !((Object)(object)val == (Object)null) && !((Object)(object)((MapIcon)val).iconImage == (Object)null)) { Sprite val2 = ((constructionSite.Build != null && (Object)(object)constructionSite.Build.Definition != (Object)null) ? ((UnitDefinition)constructionSite.Build.Definition).mapIcon : ConstructionRegistry.DefaultSiteIcon); if ((Object)(object)val2 != (Object)null && (Object)(object)((MapIcon)val).iconImage.sprite != (Object)(object)val2) { ((MapIcon)val).iconImage.sprite = val2; } } } } private static void ScanForDeliveries() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) if (_sites.Count == 0) { return; } float value = Plugin.DeliveryRadius.Value; float radiusSq = value * value; List allUnits = UnitRegistry.allUnits; for (int i = 0; i < allUnits.Count; i++) { Unit obj = allUnits[i]; Container val = (Container)(object)((obj is Container) ? obj : null); if ((Object)(object)val == (Object)null || ((Unit)val).disabled || (Object)(object)((Unit)val).definition == (Object)null || (Object)(object)((Unit)val).definition == (Object)(object)ConstructionAssets.CrateDefinition || !val.Networkkinematic || _consumed.Contains(((Unit)val).persistentID)) { continue; } string jsonKey = ((Unit)val).definition.jsonKey; if (ConstructionCatalogue.IsMaterial(jsonKey)) { ConstructionSite constructionSite = NearestWantingSite(val, jsonKey, radiusSq); if (constructionSite != null) { constructionSite.Deliver(jsonKey); constructionSite.Credit(val.ownerID); RewardDelivery(val.ownerID, constructionSite); Consume((Unit)(object)val); _dirty = true; Plugin.Log.LogInfo((object)($"[site] {constructionSite.Id} received {jsonKey} " + $"({constructionSite.DeliveredTotal}/{constructionSite.TotalLoads} loads).")); } } } } private static ConstructionSite NearestWantingSite(Container container, string key, float radiusSq) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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) //IL_005a: Unknown result type (might be due to invalid IL or missing references) ConstructionSite result = null; float num = radiusSq; GlobalPosition val = GlobalPositionExtensions.GlobalPosition((Unit)(object)container); for (int i = 0; i < _sites.Count; i++) { ConstructionSite constructionSite = _sites[i]; if (constructionSite.Wants(key) && (!((Object)(object)constructionSite.HQ != (Object)null) || !((Object)(object)((Unit)container).NetworkHQ != (Object)null) || !((Object)(object)constructionSite.HQ != (Object)(object)((Unit)container).NetworkHQ))) { float num2 = FastMath.SquareDistance(val, constructionSite.Position); if (!(num2 > num)) { num = num2; result = constructionSite; } } } return result; } private static void Consume(Unit unit) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) _consumed.Add(unit.persistentID); unit.Networkdisabled = true; } private static void ScanForCompletions() { for (int i = 0; i < _sites.Count; i++) { ConstructionSite constructionSite = _sites[i]; if (!constructionSite.Completed && constructionSite.ManifestSatisfied) { TryComplete(constructionSite); } } } public static void TryComplete(ConstructionSite site) { //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_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown //IL_0138: 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_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) ConstructionCatalogue.BuildOption build = site.Build; if (build == null) { return; } BuildingDefinition definition = build.Definition; if ((Object)(object)definition == (Object)null || (Object)(object)((UnitDefinition)definition).unitPrefab == (Object)null) { Block(site, "this install has no prefab for " + build.BuildingJsonKey); return; } Spawner i = NetworkSceneSingleton.i; if ((Object)(object)i == (Object)null) { Block(site, "spawner not available yet"); return; } Airbase val = NearestFriendlyAirbase(site, Plugin.AirbaseAttachRadius.Value); if (build.RequiresFriendlyAirbase && (Object)(object)val == (Object)null) { Block(site, $"needs a friendly airbase within {Plugin.AirbaseAttachRadius.Value:F0} m"); return; } if (build.RequiresWarheadStorage && !FactionHasStorage(site.HQ)) { Block(site, "the faction owns no airbase with warhead storage — the plant would produce nothing"); return; } if (!ValidatePlacement(site, definition, out var position, out var reason)) { Block(site, reason); return; } int num = ++_buildCounter; string text = "hendev_build_" + num + "_" + build.Id; FactoryOptions val2 = null; if (!string.IsNullOrEmpty(site.Production)) { val2 = new FactoryOptions { productionType = site.Production, productionTime = site.ProductionInterval }; } Building val3; try { val3 = i.SpawnBuilding(((UnitDefinition)definition).unitPrefab, position, site.Rotation, site.HQ, val, text, false, val2); } catch (Exception ex) { Block(site, "spawn threw: " + ex.GetType().Name + ": " + ex.Message); Plugin.Log.LogError((object)$"[site] {site.Id} SpawnBuilding threw: {ex}"); return; } if ((Object)(object)val3 == (Object)null) { Block(site, "spawn returned nothing"); return; } site.Completed = true; site.Blocked = null; _completed.Add(new CompletedBuild { BuildId = build.Id, BuildingJsonKey = build.BuildingJsonKey, Production = site.Production, UniqueName = text, Position = position, HQ = site.HQ }); _dirty = true; if ((Object)(object)site.Crate != (Object)null && !((Unit)site.Crate).disabled) { Consume((Unit)(object)site.Crate); } RewardCompletion(site, build); Vector3 val4 = ((GlobalPosition)(ref position)).AsVector3(); Plugin.Log.LogInfo((object)($"[site] {site.Id} COMPLETE — {build.DisplayName} '{text}' built at " + $"({val4.x:F0}, {val4.y:F0}, {val4.z:F0})" + (((Object)(object)val != (Object)null) ? (", attached to " + ((Object)val).name) : "") + ((site.Production != null) ? (", producing " + site.Production) : "") + ".")); } private static void Block(ConstructionSite site, string reason) { if (!(site.Blocked == reason)) { site.Blocked = reason; _dirty = true; Plugin.Log.LogWarning((object)$"[site] {site.Id} cannot complete: {reason}"); } } private static bool ValidatePlacement(ConstructionSite site, BuildingDefinition def, out GlobalPosition position, out string reason) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) position = site.Position; reason = null; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(GlobalPositionExtensions.ToLocalPosition(site.Position) + Vector3.up * 200f, Vector3.down, ref val, 400f, LayerMask.op_Implicit(PhysicsLayers.StaticsMask))) { reason = "no solid ground under the crate"; return false; } float num = Vector3.Angle(((RaycastHit)(ref val)).normal, Vector3.up); if (num > Plugin.MaxSlopeDegrees.Value) { reason = $"ground is too steep ({num:F0}°, limit {Plugin.MaxSlopeDegrees.Value:F0}°)"; return false; } if (((RaycastHit)(ref val)).point.y < Datum.LocalSeaY) { reason = "the site is below sea level"; return false; } if ((Object)(object)site.HQ != (Object)null && !site.HQ.IsDropZoneClear(GlobalPositionExtensions.ToGlobalPosition(((RaycastHit)(ref val)).point))) { reason = "an active drop zone is in the way"; return false; } if (IsObstructed(site, def, out var blocker)) { reason = "blocked by " + blocker; return false; } position = GlobalPositionExtensions.ToGlobalPosition(((RaycastHit)(ref val)).point + ((UnitDefinition)def).spawnOffset); return true; } private static bool IsObstructed(ConstructionSite site, BuildingDefinition def, out string blocker) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) blocker = null; float num = Mathf.Max(((UnitDefinition)def).width, ((UnitDefinition)def).length) * 0.5f; if (num <= 0f) { num = 10f; } List allUnits = UnitRegistry.allUnits; for (int i = 0; i < allUnits.Count; i++) { Unit val = allUnits[i]; if (!((Object)(object)val == (Object)null) && !val.disabled && !((Object)(object)val.definition == (Object)null) && val.definition.IsObstacle && (object)val != site.Crate && !_consumed.Contains(val.persistentID)) { float num2 = num + Mathf.Max(val.definition.width, val.definition.length) * 0.5f; if (!(FastMath.SquareDistance(GlobalPositionExtensions.GlobalPosition(val), site.Position) > num2 * num2)) { blocker = (string.IsNullOrEmpty(val.definition.unitName) ? val.definition.jsonKey : val.definition.unitName); return true; } } } return false; } private static void RewardDelivery(PersistentID delivererId, ConstructionSite site) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Player val = PlayerFor(delivererId); if ((Object)(object)val == (Object)null || (Object)(object)site.HQ == (Object)null) { return; } float value = Plugin.DeliveryReward.Value; if (value <= 0f) { return; } try { site.HQ.RewardPlayer(val, (Unit)(object)site.Crate, value, value, (RewardType)4); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[reward] delivery reward failed: " + ex.Message)); } } private static void RewardCompletion(ConstructionSite site, ConstructionCatalogue.BuildOption option) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) float value = Plugin.CompletionReward.Value; if (value <= 0f || (Object)(object)site.HQ == (Object)null || site.Contributors.Count == 0) { return; } int num = 0; foreach (KeyValuePair contributor in site.Contributors) { num += contributor.Value; } if (num <= 0) { return; } foreach (KeyValuePair contributor2 in site.Contributors) { Player val = PlayerFor(contributor2.Key); if ((Object)(object)val == (Object)null) { continue; } float num2 = value * (float)contributor2.Value / (float)num; if (!(num2 <= 0f)) { try { site.HQ.RewardPlayer(val, (Unit)null, num2, num2, (RewardType)4); Plugin.Log.LogInfo((object)($"[reward] {val.GetPlayerName()} credited {num2:F1} for {contributor2.Value} " + "load(s) toward " + option.DisplayName + ".")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[reward] completion reward failed: " + ex.Message)); } } } } private static Player PlayerFor(PersistentID id) { if (((PersistentID)(ref id)).NotValid) { return null; } Unit val = default(Unit); if (!((PersistentID)(ref id)).TryGetUnit(ref val)) { return null; } Aircraft val2 = (Aircraft)(object)((val is Aircraft) ? val : null); if (!((Object)(object)val2 != (Object)null)) { return null; } return val2.Player; } public static Airbase NearestFriendlyAirbase(ConstructionSite site, float radius) { //IL_0052: 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) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)site.HQ == (Object)null) { return null; } Airbase result = null; float num = radius * radius; foreach (Airbase airbasis in site.HQ.GetAirbases()) { if (!((Object)(object)airbasis == (Object)null) && !((Object)(object)airbasis.CurrentHQ != (Object)(object)site.HQ)) { float num2 = FastMath.SquareDistance(GlobalPositionExtensions.ToGlobalPosition(((Component)airbasis).transform.position), site.Position); if (!(num2 > num)) { num = num2; result = airbasis; } } } return result; } public static bool FactionHasStorage(FactionHQ hq) { if ((Object)(object)hq == (Object)null) { return false; } foreach (Airbase airbasis in hq.GetAirbases()) { if ((Object)(object)airbasis != (Object)null && (Object)(object)airbasis.CurrentHQ == (Object)(object)hq && airbasis.HasStorage()) { return true; } } return false; } public static bool ChooseBuild(ConstructionSite site, ConstructionCatalogue.BuildOption option, string production) { if (site == null || option == null || site.Completed) { return false; } if (!CanCommand) { return false; } if (!CanBuild) { if ((Object)(object)site.Crate != (Object)null) { return ConstructionNet.SendCommand(((Unit)site.Crate).persistentID.Id, option.Id, production); } return false; } site.Build = option; site.Production = production; site.Blocked = null; site.Required.Clear(); site.Required.AddRange(ConstructionCatalogue.ResolveManifest(option, production)); site.ProductionInterval = ConstructionCatalogue.ProductionInterval(option, production); _dirty = true; Plugin.Log.LogInfo((object)($"[site] {site.Id} set to build {option.DisplayName}" + ((production != null) ? (" producing " + production) : "") + ((production != null) ? $" (rank {ConstructionCatalogue.ProductionRank(production)})" : "") + $" — {site.TotalLoads} load(s) required.")); return true; } public static bool ClearBuild(ConstructionSite site) { if (site == null || site.Completed || !CanCommand) { return false; } if (!CanBuild) { if ((Object)(object)site.Crate != (Object)null) { return ConstructionNet.SendCommand(((Unit)site.Crate).persistentID.Id, "", ""); } return false; } site.ClearChoice(); _dirty = true; return true; } private static void WriteStateFile() { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrEmpty(directoryName)) { return; } string path = Path.Combine(Directory.GetParent(directoryName)?.Parent?.FullName ?? directoryName, "ConstructionOps.sites.txt"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"# ConstructionOps sites {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); stringBuilder.AppendLine($"# server={CanBuild} sites={_sites.Count} consumed={_consumed.Count}"); ConstructionLogistics.Describe(stringBuilder); stringBuilder.AppendLine(); if (_sites.Count == 0) { stringBuilder.AppendLine("no construction sites — drop a Construction Crate to make one."); } for (int i = 0; i < _sites.Count; i++) { _sites[i].Describe(stringBuilder); stringBuilder.AppendLine(); } if (_completed.Count > 0) { stringBuilder.AppendLine("=== COMPLETED ==="); for (int j = 0; j < _completed.Count; j++) { CompletedBuild completedBuild = _completed[j]; Vector3 val = ((GlobalPosition)(ref completedBuild.Position)).AsVector3(); stringBuilder.AppendLine(" " + completedBuild.BuildId + " (" + completedBuild.BuildingJsonKey + ") '" + completedBuild.UniqueName + "' " + $"at ({val.x:F0}, {val.z:F0}) " + "faction=" + (((Object)(object)completedBuild.HQ != (Object)null) ? completedBuild.HQ.faction.factionName : "none") + ((completedBuild.Production != null) ? (" produces=" + completedBuild.Production) : "")); } stringBuilder.AppendLine(); } ConstructionObjectives.Describe(stringBuilder); File.WriteAllText(path, stringBuilder.ToString()); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[sites] could not write the state file: " + ex.Message)); } } } public static class ConstructionNet { public struct CommandMessage { public uint crateId; public string buildId; public string production; } public struct SiteState { public uint crateId; public int siteId; public string buildId; public string production; public string blocked; public bool completed; public string[] materialKeys; public int[] required; public int[] delivered; } public struct StateMessage { public SiteState[] sites; } private static bool _writersInstalled; private static bool _serverHandler; private static bool _clientHandler; public static bool Ready { get { if (!_serverHandler) { return _clientHandler; } return true; } } public static void EnsureRegistered() { InstallWriters(); NetworkManagerNuclearOption i = NetworkManagerNuclearOption.i; if (!((Object)(object)i == (Object)null)) { if ((Object)(object)((NetworkManager)i).Server != (Object)null && ((NetworkManager)i).Server.Active && !_serverHandler) { ((NetworkManager)i).Server.MessageHandler.RegisterHandler((MessageDelegateWithPlayer)OnCommandReceived, false); _serverHandler = true; Plugin.Log.LogInfo((object)"[net] host is listening for construction commands from clients."); } if ((Object)(object)((NetworkManager)i).Client != (Object)null && ((NetworkManager)i).Client.Active && ((Object)(object)((NetworkManager)i).Server == (Object)null || !((NetworkManager)i).Server.Active) && !_clientHandler) { ((NetworkManager)i).Client.MessageHandler.RegisterHandler((MessageDelegateWithPlayer)OnStateReceived, false); _clientHandler = true; Plugin.Log.LogInfo((object)"[net] client is listening for construction site updates."); } } } public static void Reset() { _serverHandler = false; _clientHandler = false; } private static void InstallWriters() { if (!_writersInstalled) { _writersInstalled = true; Writer.Write = WriteCommand; Reader.Read = ReadCommand; Writer.Write = WriteState; Reader.Read = ReadState; MessagePacker.RegisterMessage(); MessagePacker.RegisterMessage(); } } private static void WriteCommand(NetworkWriter writer, CommandMessage msg) { writer.WriteUInt32(msg.crateId); StringExtensions.WriteString(writer, msg.buildId ?? ""); StringExtensions.WriteString(writer, msg.production ?? ""); } private static CommandMessage ReadCommand(NetworkReader reader) { return new CommandMessage { crateId = reader.ReadUInt32(), buildId = StringExtensions.ReadString(reader), production = StringExtensions.ReadString(reader) }; } private static void WriteState(NetworkWriter writer, StateMessage msg) { SiteState[] array = msg.sites ?? new SiteState[0]; PackedExtensions.WritePackedInt32(writer, array.Length); for (int i = 0; i < array.Length; i++) { SiteState siteState = array[i]; writer.WriteUInt32(siteState.crateId); PackedExtensions.WritePackedInt32(writer, siteState.siteId); StringExtensions.WriteString(writer, siteState.buildId ?? ""); StringExtensions.WriteString(writer, siteState.production ?? ""); StringExtensions.WriteString(writer, siteState.blocked ?? ""); writer.WriteBoolean(siteState.completed); int num = ((siteState.materialKeys != null) ? siteState.materialKeys.Length : 0); PackedExtensions.WritePackedInt32(writer, num); for (int j = 0; j < num; j++) { StringExtensions.WriteString(writer, siteState.materialKeys[j] ?? ""); PackedExtensions.WritePackedInt32(writer, siteState.required[j]); PackedExtensions.WritePackedInt32(writer, siteState.delivered[j]); } } } private static StateMessage ReadState(NetworkReader reader) { int num = PackedExtensions.ReadPackedInt32(reader); SiteState[] array = new SiteState[num]; for (int i = 0; i < num; i++) { SiteState siteState = new SiteState { crateId = reader.ReadUInt32(), siteId = PackedExtensions.ReadPackedInt32(reader), buildId = StringExtensions.ReadString(reader), production = StringExtensions.ReadString(reader), blocked = StringExtensions.ReadString(reader), completed = reader.ReadBoolean() }; int num2 = PackedExtensions.ReadPackedInt32(reader); siteState.materialKeys = new string[num2]; siteState.required = new int[num2]; siteState.delivered = new int[num2]; for (int j = 0; j < num2; j++) { siteState.materialKeys[j] = StringExtensions.ReadString(reader); siteState.required[j] = PackedExtensions.ReadPackedInt32(reader); siteState.delivered[j] = PackedExtensions.ReadPackedInt32(reader); } array[i] = siteState; } return new StateMessage { sites = array }; } public static bool SendCommand(uint crateId, string buildId, string production) { NetworkManagerNuclearOption i = NetworkManagerNuclearOption.i; if ((Object)(object)i == (Object)null || (Object)(object)((NetworkManager)i).Client == (Object)null || !((NetworkManager)i).Client.Active) { return false; } try { ((NetworkManager)i).Client.Send(new CommandMessage { crateId = crateId, buildId = (buildId ?? ""), production = (production ?? "") }, (Channel)0); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[net] could not send the build command: " + ex.Message)); return false; } } public static void BroadcastState() { NetworkManagerNuclearOption i = NetworkManagerNuclearOption.i; if ((Object)(object)i == (Object)null || (Object)(object)((NetworkManager)i).Server == (Object)null || !((NetworkManager)i).Server.Active || !_serverHandler || ((NetworkManager)i).Server.Players == null || ((NetworkManager)i).Server.Players.Count == 0) { return; } try { IList sites = ConstructionManager.Sites; SiteState[] array = new SiteState[sites.Count]; for (int j = 0; j < sites.Count; j++) { ConstructionSite constructionSite = sites[j]; int count = constructionSite.Required.Count; string[] array2 = new string[count]; int[] array3 = new int[count]; int[] array4 = new int[count]; for (int k = 0; k < count; k++) { array2[k] = constructionSite.Required[k].JsonKey; array3[k] = constructionSite.Required[k].Count; array4[k] = constructionSite.DeliveredCount(constructionSite.Required[k].JsonKey); } array[j] = new SiteState { crateId = (((Object)(object)constructionSite.Crate != (Object)null) ? ((Unit)constructionSite.Crate).persistentID.Id : 0u), siteId = constructionSite.Id, buildId = ((constructionSite.Build != null) ? constructionSite.Build.Id : ""), production = (constructionSite.Production ?? ""), blocked = (constructionSite.Blocked ?? ""), completed = constructionSite.Completed, materialKeys = array2, required = array3, delivered = array4 }; } ((NetworkManager)i).Server.SendToAll(new StateMessage { sites = array }, true, (Channel)0); } catch (Exception ex) { Plugin.Log.LogError((object)("[net] could not broadcast site state: " + ex.Message)); } } private static void OnCommandReceived(INetworkPlayer player, CommandMessage msg) { try { ConstructionSite constructionSite = ConstructionManager.SiteForCrateId(msg.crateId); if (constructionSite == null) { Plugin.Log.LogWarning((object)($"[net] a client asked to task crate {msg.crateId}, " + "which is not a construction site here. Ignored.")); return; } if (string.IsNullOrEmpty(msg.buildId)) { ConstructionManager.ClearBuild(constructionSite); return; } ConstructionCatalogue.BuildOption buildOption = ConstructionCatalogue.ById(msg.buildId); if (buildOption == null) { Plugin.Log.LogWarning((object)("[net] a client asked for unknown build '" + msg.buildId + "'. Ignored.")); return; } string production = (string.IsNullOrEmpty(msg.production) ? null : msg.production); ConstructionManager.ChooseBuild(constructionSite, buildOption, production); } catch (Exception ex) { Plugin.Log.LogError((object)("[net] failed to apply a client's build command: " + ex)); } } private static void OnStateReceived(INetworkPlayer player, StateMessage msg) { try { ConstructionManager.ApplyNetworkState(msg.sites); } catch (Exception ex) { Plugin.Log.LogError((object)("[net] failed to apply site state from the host: " + ex)); } } } public static class ConstructionObjectives { private sealed class Directive { public string What; public string Near; public float Radius = 1500f; public int Count = 1; } private const float DefaultNearRadius = 1500f; private static readonly HashSet _warned = new HashSet(); private static readonly HashSet _fired = new HashSet(); private static readonly List _toComplete = new List(); public static void Reset() { _warned.Clear(); _fired.Clear(); _toComplete.Clear(); } public static void Tick() { if (!ConstructionManager.CanBuild) { return; } MissionRunner runner = MissionManager.Runner; if (runner == null || runner.ActiveObjectives == null) { return; } _toComplete.Clear(); for (int i = 0; i < runner.ActiveObjectives.Count; i++) { Objective val = runner.ActiveObjectives[i]; if (val == null || val.SavedObjective == null) { continue; } string text = val.SavedObjective.UniqueName; Directive directive = Parse(text); if (directive == null) { directive = Parse(val.SavedObjective.DisplayName); if (directive == null) { continue; } text = val.SavedObjective.DisplayName; } if (!_fired.Contains(text) && CountMatching(directive, val.FactionHQ) >= directive.Count) { _fired.Add(text); _toComplete.Add(val); ConstructionManager.MarkDirty(); } } for (int j = 0; j < _toComplete.Count; j++) { Objective val2 = _toComplete[j]; try { runner.CompleteObjective(val2); Plugin.Log.LogInfo((object)("[objective] completed '" + val2.SavedObjective.UniqueName + "' — its construction requirement was met.")); } catch (Exception arg) { Plugin.Log.LogError((object)("[objective] '" + val2.SavedObjective.UniqueName + "' threw while " + $"completing: {arg}")); } } _toComplete.Clear(); } private static int CountMatching(Directive directive, FactionHQ hq) { //IL_0002: 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) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) GlobalPosition val = default(GlobalPosition); bool flag = false; if (!string.IsNullOrEmpty(directive.Near)) { if (!UnitRegistry.customIDLookup.TryGetValue(directive.Near, out var value) || (Object)(object)value == (Object)null) { return 0; } val = GlobalPositionExtensions.GlobalPosition(value); flag = true; } int num = 0; IList completedBuilds = ConstructionManager.CompletedBuilds; for (int i = 0; i < completedBuilds.Count; i++) { ConstructionManager.CompletedBuild completedBuild = completedBuilds[i]; if ((!((Object)(object)hq != (Object)null) || !((Object)(object)completedBuild.HQ != (Object)(object)hq)) && Matches(directive.What, completedBuild) && (!flag || !(FastMath.SquareDistance(completedBuild.Position, val) > directive.Radius * directive.Radius))) { num++; } } return num; } private static bool Matches(string what, ConstructionManager.CompletedBuild build) { if (string.IsNullOrEmpty(what) || what == "any") { return true; } if (!string.Equals(what, build.BuildId, StringComparison.OrdinalIgnoreCase)) { return string.Equals(what, build.BuildingJsonKey, StringComparison.OrdinalIgnoreCase); } return true; } private static Directive Parse(string name) { if (string.IsNullOrEmpty(name)) { return null; } string text = name.Trim(); if (text.Length < 7) { return null; } if (!text.StartsWith("build", StringComparison.OrdinalIgnoreCase)) { return null; } char c = text[5]; if (c != ':' && c != '-' && c != '_') { return null; } string[] array = text.Substring(6).Split('/', ',', ';'); if (array.Length == 0 || string.IsNullOrEmpty(array[0].Trim())) { Warn(name, "nothing to build was named"); return null; } Directive directive = new Directive { What = array[0].Trim() }; for (int i = 1; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0) { continue; } int num = text2.IndexOf('='); if (num <= 0) { Warn(name, "option '" + text2 + "' is not key=value"); continue; } string text3 = text2.Substring(0, num).Trim().ToLowerInvariant(); string text4 = text2.Substring(num + 1).Trim(); switch (text3) { case "count": { if (int.TryParse(text4, out var result2) && result2 > 0) { directive.Count = result2; } else { Warn(name, "count '" + text4 + "' is not a positive whole number"); } break; } case "near": directive.Near = text4; break; case "radius": { if (float.TryParse(text4, out var result) && result > 0f) { directive.Radius = result; } else { Warn(name, "radius '" + text4 + "' is not a positive number"); } break; } default: Warn(name, "unknown option '" + text3 + "'"); break; } } if (directive.What != "any" && ConstructionCatalogue.ById(directive.What) == null && !NamesABuilding(directive.What)) { Warn(name, "'" + directive.What + "' is neither a catalogue build id nor a building jsonKey — this objective can never complete"); } return directive; } private static bool NamesABuilding(string key) { IList options = ConstructionCatalogue.Options; for (int i = 0; i < options.Count; i++) { if (string.Equals(options[i].BuildingJsonKey, key, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static void Warn(string name, string problem) { if (_warned.Add(name + "|" + problem)) { Plugin.Log.LogWarning((object)("[objective] '" + name + "': " + problem + ".")); } } public static void Describe(StringBuilder sb) { //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) MissionRunner runner = MissionManager.Runner; if (runner == null || runner.ActiveObjectives == null) { return; } List list = new List(); for (int i = 0; i < runner.ActiveObjectives.Count; i++) { Objective val = runner.ActiveObjectives[i]; if (val != null && val.SavedObjective != null) { string uniqueName = val.SavedObjective.UniqueName; Directive directive = Parse(uniqueName) ?? Parse(val.SavedObjective.DisplayName); if (directive != null) { int num = CountMatching(directive, val.FactionHQ); list.Add($" '{uniqueName}' -> build={directive.What} count={num}/{directive.Count}" + ((directive.Near != null) ? $" near={directive.Near} radius={directive.Radius:F0}" : "") + " faction=" + (((Object)(object)val.FactionHQ != (Object)null) ? val.FactionHQ.faction.factionName : "any")); } } } List list2 = new List(); for (int j = 0; j < runner.ActiveObjectives.Count; j++) { if (runner.ActiveObjectives[j] is ConstructBuildingObjective constructBuildingObjective) { GlobalPosition value = ((ValueWrapper)(object)constructBuildingObjective.Spot.GlobalPosition).Value; Vector3 val2 = ((GlobalPosition)(ref value)).AsVector3(); list2.Add($" '{((Objective)constructBuildingObjective).SavedObjective.UniqueName}' status={((Objective)constructBuildingObjective).Status}" + " build=" + constructBuildingObjective.BuildIdForDiagnostics + $" count={constructBuildingObjective.CountMatching()}/{constructBuildingObjective.RequiredCountForDiagnostics}" + $" spot=({val2.x:F0}, {val2.z:F0}) range={((ValueWrapper)(object)constructBuildingObjective.Spot.Range).Value:F0}" + " faction=" + (((Object)(object)((Objective)constructBuildingObjective).FactionHQ != (Object)null) ? ((Objective)constructBuildingObjective).FactionHQ.faction.factionName : "any")); } } sb.AppendLine("=== CONSTRUCT BUILDING OBJECTIVES (custom type) ==="); if (list2.Count == 0) { sb.AppendLine(" none ACTIVE. An objective the mission graph never starts is never evaluated — chain a 'Start Objective' outcome to it."); } else { for (int k = 0; k < list2.Count; k++) { sb.AppendLine(list2[k]); } } sb.AppendLine(); sb.AppendLine("=== 'build:' NAMED OBJECTIVES (vanilla-compatible fallback) ==="); if (list.Count == 0) { sb.AppendLine(" none — no active objective is named 'build:...'"); } else { for (int l = 0; l < list.Count; l++) { sb.AppendLine(list[l]); } } sb.AppendLine(); } } public class ConstructionOpsHost : MonoBehaviour { private bool _probed; private bool _inMission; public static ConstructionOpsHost Install() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ConstructionOps.Host"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; return val.AddComponent(); } private void Start() { if (ConstructionAssets.Loaded) { ((MonoBehaviour)this).StartCoroutine(ConstructionRegistry.RegisterWhenReady()); } ((MonoBehaviour)this).StartCoroutine(ProbeWhenReady()); ((MonoBehaviour)this).StartCoroutine(DumpAircraftWhenFlying()); ((MonoBehaviour)this).StartCoroutine(RunConstructionLoop()); } private void Update() { if (!Plugin.Enabled.Value) { return; } try { ConstructionPanel.Tick(); } catch (Exception ex) { Plugin.Log.LogError((object)("[panel] disabled after an exception: " + ex)); ConstructionPanel.Reset(); ((Behaviour)this).enabled = false; } } private IEnumerator RunConstructionLoop() { while (true) { yield return (object)new WaitForSeconds(1f); bool flag = FactionRegistry.airbaseLookup.Count > 0; if (_inMission && !flag) { ConstructionManager.Reset(); ConstructionPanel.Reset(); Plugin.Log.LogInfo((object)"[host] mission ended — construction state cleared."); } _inMission = flag; if (flag && ConstructionRegistry.Registered) { try { ConstructionManager.Tick(); } catch (Exception ex) { Plugin.Log.LogError((object)("[manager] tick threw: " + ex)); } } } } private IEnumerator ProbeWhenReady() { while (true) { yield return (object)new WaitForSeconds(1f); if (_probed && !Plugin.ProbeEveryMission.Value) { break; } if ((Object)(object)Encyclopedia.i == (Object)null || Encyclopedia.Lookup == null) { continue; } Probe.Run(FactionRegistry.airbaseLookup.Count > 0); _probed = true; if (Plugin.ProbeEveryMission.Value) { while (FactionRegistry.airbaseLookup.Count > 0) { yield return (object)new WaitForSeconds(5f); } } } } private IEnumerator DumpAircraftWhenFlying() { Aircraft last = null; Aircraft val = default(Aircraft); while (true) { yield return (object)new WaitForSeconds(3f); if (!GameManager.GetLocalAircraft(ref val) || (Object)(object)val == (Object)null) { last = null; } else if (val != last) { yield return (object)new WaitForSeconds(2f); if (GameManager.GetLocalAircraft(ref val) && !((Object)(object)val == (Object)null)) { Probe.DumpAircraft(val); last = val; } } } } } public static class ConstructionPanel { private enum Stage { Idle, Production, Confirm, Retask } private const float PanelWidth = 380f; private const float MarginX = 24f; private const float MarginY = 24f; private const int ProductionPageSize = 8; private static GameObject _root; private static RectTransform _content; private static Canvas _canvas; private static Image _background; private static readonly List _zones = new List(); private static int _selectedSiteId; private static Stage _stage; private static ConstructionCatalogue.BuildOption _pendingOption; private static string _pendingProduction; private static List _pendingManifest; private static int _productionPage; private static float _nextRefresh; private static bool _dirty = true; public static void Tick() { if (!DynamicMap.mapMaximized) { Hide(); return; } DynamicMap i = SceneSingleton.i; if ((Object)(object)i == (Object)null || (Object)(object)i.maximizedMapCanvas == (Object)null) { Hide(); return; } if ((Object)(object)_root == (Object)null || (Object)(object)_canvas != (Object)(object)i.maximizedMapCanvas) { Teardown(); _canvas = i.maximizedMapCanvas; Build(); _dirty = true; } HandleMouse(_root.activeSelf && Selected() != null); if (Selected() == null) { if (_root.activeSelf) { ClearContent(); _root.SetActive(false); } return; } if (!_root.activeSelf) { _root.SetActive(true); _dirty = true; } if (_dirty || Time.unscaledTime >= _nextRefresh) { _nextRefresh = Time.unscaledTime + 0.25f; _dirty = false; Refresh(); } } public static void Reset() { Teardown(); _selectedSiteId = 0; ClearPending(); _dirty = true; ConstructionUi.ResetFont(); } private static void ClearPending() { _stage = Stage.Idle; _pendingOption = null; _pendingProduction = null; _pendingManifest = null; _productionPage = 0; } private static void Hide() { if (!((Object)(object)_root == (Object)null) && _root.activeSelf) { ClearContent(); _root.SetActive(false); } } private static void ClearContent() { if ((Object)(object)_content == (Object)null) { _zones.Clear(); return; } for (int num = ((Transform)_content).childCount - 1; num >= 0; num--) { GameObject gameObject = ((Component)((Transform)_content).GetChild(num)).gameObject; gameObject.transform.SetParent((Transform)null, false); Object.Destroy((Object)(object)gameObject); } _zones.Clear(); } private static void Teardown() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = null; _content = null; _canvas = null; _background = null; _zones.Clear(); } private static void Build() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0053: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) _root = new GameObject("ConstructionOps.Panel", new Type[1] { typeof(RectTransform) }); RectTransform component = _root.GetComponent(); ((Transform)component).SetParent(((Component)_canvas).transform, false); ConstructionUi.Place(component, new Rect(24f, -24f, 380f, 200f)); GameObject val = new GameObject("Background", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform component2 = val.GetComponent(); ((Transform)component2).SetParent((Transform)(object)component, false); ConstructionUi.Place(component2, new Rect(0f, 0f, 380f, 200f)); _background = val.GetComponent(); ((Graphic)_background).color = ConstructionUi.PanelGround; ((Graphic)_background).raycastTarget = true; _content = ConstructionUi.Group(component, new Rect(0f, 0f, 380f, 200f), "Content"); } private static void HandleMouse(bool visible) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) Camera val = (((int)_canvas.renderMode == 0) ? null : _canvas.worldCamera); Vector3 mousePosition = Input.mousePosition; bool mouseButtonDown = Input.GetMouseButtonDown(0); if (visible) { for (int i = 0; i < _zones.Count; i++) { ConstructionUi.HotZone hotZone = _zones[i]; if (!((Object)(object)hotZone.Rect == (Object)null)) { bool flag = RectTransformUtility.RectangleContainsScreenPoint(hotZone.Rect, Vector2.op_Implicit(mousePosition), val); hotZone.Paint(flag); if (flag && mouseButtonDown && hotZone.Enabled && hotZone.OnClick != null) { hotZone.OnClick(); _dirty = true; return; } } } if (mouseButtonDown && RectTransformUtility.RectangleContainsScreenPoint(_root.GetComponent(), Vector2.op_Implicit(mousePosition), val)) { return; } } if (!mouseButtonDown) { return; } ConstructionSite constructionSite = SiteUnderCursor(); if (constructionSite != null) { if (constructionSite.Id != _selectedSiteId) { ClearPending(); } _selectedSiteId = constructionSite.Id; _dirty = true; } else if (visible) { _selectedSiteId = 0; ClearPending(); _dirty = true; } } private static ConstructionSite SiteUnderCursor() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) EventSystem current = EventSystem.current; if ((Object)(object)current == (Object)null) { return null; } PointerEventData val = new PointerEventData(current) { position = Vector2.op_Implicit(Input.mousePosition) }; List list = new List(); current.RaycastAll(val, list); for (int i = 0; i < list.Count; i++) { RaycastResult val2 = list[i]; GameObject gameObject = ((RaycastResult)(ref val2)).gameObject; if ((Object)(object)gameObject == (Object)null) { continue; } UnitMapIcon componentInParent = gameObject.GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && !((Object)(object)componentInParent.unit == (Object)null)) { ConstructionSite constructionSite = ConstructionManager.SiteForCrate(componentInParent.unit); if (constructionSite != null) { return constructionSite; } } } return null; } private static void Refresh() { ClearContent(); float y = -12f; float w = 356f; y = Header(12f, y, w); y = Detail(12f, y, w); y = Footer(12f, y, w); Resize(0f - y + 12f); } private static void Resize(float height) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) _root.GetComponent().sizeDelta = new Vector2(380f, height); if ((Object)(object)_background != (Object)null) { ((Graphic)_background).rectTransform.sizeDelta = new Vector2(380f, height); } _content.sizeDelta = new Vector2(380f, height); } private static float Header(float x, float y, float w) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) ConstructionSite constructionSite = Selected(); string text = ((constructionSite != null) ? ("SITE " + constructionSite.Id) : "CONSTRUCTION"); ConstructionUi.Label(_content, text, new Rect(x, y, w - 110f, 24f), ConstructionUi.Accent, 18f, (FontStyles)1, (TextAlignmentOptions)513); if (constructionSite != null) { ConstructionUi.Label(_content, SiteSummary(constructionSite), new Rect(x + w - 110f, y, 110f, 24f), SiteColor(constructionSite), 11f, (FontStyles)0, (TextAlignmentOptions)516); } y -= 24f; ConstructionUi.Label(_content, StatusLine(), new Rect(x, y, w, 16f), ConstructionUi.Dim, 11f, (FontStyles)0, (TextAlignmentOptions)513); y -= 24f; ConstructionUi.Rule(_content, new Rect(x, y, w, 1f), ConstructionUi.Frame); return y - 8f; } private static string StatusLine() { if (!ConstructionManager.CanCommand) { return "Not in a mission."; } ConstructionSite constructionSite = Selected(); if (constructionSite == null) { return ""; } if (constructionSite.Completed) { return "Built. Nothing more to do here."; } if (constructionSite.Build == null) { return "Choose what to build at this site."; } return "Fly the loads below to this crate."; } private static float Footer(float x, float y, float w) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) int num = 0; IList sites = ConstructionManager.Sites; for (int i = 0; i < sites.Count; i++) { if (sites[i].Id != _selectedSiteId && !sites[i].Completed) { num++; } } if (num == 0) { return y; } ConstructionUi.Rule(_content, new Rect(x, y, w, 1f), ConstructionUi.Frame); y -= 8f; ConstructionUi.Label(_content, num + ((num == 1) ? " other site" : " other sites") + " — click its crate on the map", new Rect(x, y, w, 14f), ConstructionUi.Disabled, 10f, (FontStyles)2, (TextAlignmentOptions)513); return y - 14f; } private static string SiteSummary(ConstructionSite site) { if (site.Completed) { return "complete"; } if (!string.IsNullOrEmpty(site.Blocked)) { return "blocked"; } if (site.Build == null) { return "awaiting orders"; } return site.DeliveredTotal + "/" + site.TotalLoads + " loads"; } private static Color SiteColor(ConstructionSite site) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0029: Unknown result type (might be due to invalid IL or missing references) if (site.Completed) { return ConstructionUi.Accent; } if (!string.IsNullOrEmpty(site.Blocked)) { return ConstructionUi.Alert; } if (site.Build == null) { return ConstructionUi.Warning; } return ConstructionUi.TextSecondary; } private static ConstructionSite Selected() { IList sites = ConstructionManager.Sites; for (int i = 0; i < sites.Count; i++) { if (sites[i].Id == _selectedSiteId) { return sites[i]; } } return null; } private static float Detail(float x, float y, float w) { ConstructionSite constructionSite = Selected(); if (constructionSite == null) { return y; } switch (_stage) { case Stage.Retask: return RetaskConfirm(constructionSite, x, y, w); case Stage.Confirm: return BuildConfirm(constructionSite, x, y, w); case Stage.Production: return ProductionPicker(constructionSite, x, y, w); default: if (constructionSite.Build != null) { return Manifest(constructionSite, x, y, w); } return BuildPicker(constructionSite, x, y, w); } } private static float BuildPicker(ConstructionSite site, float x, float y, float w) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_0121: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) ConstructionUi.Label(_content, "SELECT A BUILD", new Rect(x, y, w, 14f), ConstructionUi.TextSecondary, 10f, (FontStyles)1, (TextAlignmentOptions)513); y -= 18f; IList options = ConstructionCatalogue.Options; bool canCommand = ConstructionManager.CanCommand; for (int i = 0; i < options.Count; i++) { ConstructionCatalogue.BuildOption option = options[i]; string text = Prerequisite(site, option); bool flag = canCommand && text == null; RectTransform parent = ConstructionUi.Row(_content, new Rect(x, y, w, 30f), _zones, flag, selected: false, delegate { Choose(option); }); ConstructionUi.Label(parent, option.DisplayName, new Rect(8f, 0f, w - 90f, 30f), flag ? ConstructionUi.TextPrimary : ConstructionUi.Disabled, 12f, (FontStyles)0, (TextAlignmentOptions)513); ConstructionUi.Label(parent, text ?? CostHint(option), new Rect(w - 150f, 0f, 142f, 30f), (text != null) ? ConstructionUi.Alert : ConstructionUi.TextSecondary, 10f, (FontStyles)0, (TextAlignmentOptions)516); y -= 32f; } y -= 4f; ConstructionUi.Label(_content, "Materials are flown in as stock containers.", new Rect(x, y, w, 14f), ConstructionUi.Dim, 10f, (FontStyles)2, (TextAlignmentOptions)513); return y - 14f - 4f; } private static string CostHint(ConstructionCatalogue.BuildOption option) { if (option.ProductionOptions == null || option.ProductionOptions.Length <= 1) { int num = 0; for (int i = 0; i < option.BaseManifest.Length; i++) { num += option.BaseManifest[i].Count; } return num + " loads"; } int num2 = int.MaxValue; for (int j = 0; j < option.ProductionOptions.Length; j++) { int num3 = ConstructionCatalogue.TotalLoads(ConstructionCatalogue.ResolveManifest(option, option.ProductionOptions[j])); if (num3 < num2) { num2 = num3; } } if (num2 != int.MaxValue) { return "from " + num2 + " loads"; } return "varies"; } private static string Prerequisite(ConstructionSite site, ConstructionCatalogue.BuildOption option) { if (option.RequiresFriendlyAirbase && (Object)(object)ConstructionManager.NearestFriendlyAirbase(site, Plugin.AirbaseAttachRadius.Value) == (Object)null) { return "no airbase in range"; } if (option.RequiresWarheadStorage && !ConstructionManager.FactionHasStorage(site.HQ)) { return "no warhead storage"; } return null; } private static void Choose(ConstructionCatalogue.BuildOption option) { _pendingOption = option; _productionPage = 0; if (option.ProductionOptions == null || option.ProductionOptions.Length == 0) { StageConfirm(null); } else if (option.ProductionOptions.Length == 1) { StageConfirm(option.ProductionOptions[0]); } else { _stage = Stage.Production; } } private static void StageConfirm(string production) { _pendingProduction = production; _pendingManifest = ConstructionCatalogue.ResolveManifest(_pendingOption, production); _stage = Stage.Confirm; } private static float ProductionPicker(ConstructionSite site, float x, float y, float w) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) ConstructionCatalogue.BuildOption pendingOption = _pendingOption; if (pendingOption == null) { _stage = Stage.Idle; return y; } string[] productionOptions = pendingOption.ProductionOptions; ConstructionUi.Label(_content, pendingOption.DisplayName.ToUpperInvariant() + " — PRODUCES", new Rect(x, y, w, 14f), ConstructionUi.TextSecondary, 10f, (FontStyles)1, (TextAlignmentOptions)513); y -= 18f; int pages = Mathf.Max(1, Mathf.CeilToInt((float)productionOptions.Length / 8f)); _productionPage = Mathf.Clamp(_productionPage, 0, pages - 1); int num = _productionPage * 8; int num2 = Mathf.Min(8, productionOptions.Length - num); for (int i = 0; i < num2; i++) { string key = productionOptions[num + i]; RectTransform parent = ConstructionUi.Row(_content, new Rect(x, y, w, 30f), _zones, ConstructionManager.CanCommand, selected: false, delegate { StageConfirm(key); }); ConstructionUi.Label(parent, ConstructionCatalogue.ProductionName(key), new Rect(8f, 0f, w - 150f, 30f), ConstructionUi.TextPrimary, 12f, (FontStyles)0, (TextAlignmentOptions)513); int num3 = ConstructionCatalogue.TotalLoads(ConstructionCatalogue.ResolveManifest(pendingOption, key)); ConstructionUi.Label(parent, "rank " + ConstructionCatalogue.ProductionRank(key) + " · " + num3 + " loads", new Rect(w - 150f, 0f, 142f, 30f), ConstructionUi.TextSecondary, 10f, (FontStyles)0, (TextAlignmentOptions)516); y -= 32f; } y -= 4f; ConstructionUi.Button(_content, new Rect(x, y, 104f, 28f), "BACK", _zones, enabled: true, ClearPending); if (pages > 1) { ConstructionUi.Button(_content, new Rect(x + w - 44f, y, 44f, 28f), "MORE", _zones, enabled: true, delegate { _productionPage = (_productionPage + 1) % pages; }); } return y - 32f - 4f; } private static float BuildConfirm(ConstructionSite site, float x, float y, float w) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0160: 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_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) ConstructionCatalogue.BuildOption option = _pendingOption; if (option == null || _pendingManifest == null) { ClearPending(); return y; } ConstructionUi.Label(_content, "CONFIRM", new Rect(x, y, w, 14f), ConstructionUi.Warning, 10f, (FontStyles)1, (TextAlignmentOptions)513); y -= 18f; ConstructionUi.Label(_content, option.DisplayName, new Rect(x, y, w, 18f), ConstructionUi.TextPrimary, 14f, (FontStyles)1, (TextAlignmentOptions)513); y -= 22f; if (_pendingProduction != null) { string text = "produces " + ConstructionCatalogue.ProductionName(_pendingProduction); text = text + " (rank " + ConstructionCatalogue.ProductionRank(_pendingProduction) + ")"; ConstructionUi.Label(_content, text, new Rect(x, y, w, 14f), ConstructionUi.TextSecondary, 10f, (FontStyles)0, (TextAlignmentOptions)513); y -= 18f; } y = ManifestLines(_pendingManifest, null, x, y, w); int num = ConstructionCatalogue.TotalLoads(_pendingManifest); ConstructionUi.Label(_content, "You must fly " + num + ((num == 1) ? " load" : " loads") + " to this crate.", new Rect(x, y, w, 14f), ConstructionUi.Dim, 10f, (FontStyles)2, (TextAlignmentOptions)513); y -= 22f; bool canCommand = ConstructionManager.CanCommand; ConstructionUi.Button(_content, new Rect(x, y, 104f, 28f), "CONFIRM", _zones, canCommand, delegate { ConstructionManager.ChooseBuild(site, option, _pendingProduction); ClearPending(); }); ConstructionUi.Button(_content, new Rect(x + 104f + 4f, y, 104f, 28f), "BACK", _zones, enabled: true, delegate { if (option.ProductionOptions != null && option.ProductionOptions.Length > 1) { _stage = Stage.Production; _pendingProduction = null; _pendingManifest = null; } else { ClearPending(); } }); return y - 32f - 4f; } private static float RetaskConfirm(ConstructionSite site, float x, float y, float w) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) ConstructionUi.Label(_content, "RE-TASK THIS SITE?", new Rect(x, y, w, 14f), ConstructionUi.Alert, 10f, (FontStyles)1, (TextAlignmentOptions)513); y -= 18f; int deliveredTotal = site.DeliveredTotal; string text = ((deliveredTotal > 0) ? (deliveredTotal + ((deliveredTotal == 1) ? " delivered load is" : " delivered loads are") + " lost. Materials are not refunded.") : "Nothing has been delivered yet, so nothing is lost."); ConstructionUi.Label(_content, text, new Rect(x, y, w, 14f), (deliveredTotal > 0) ? ConstructionUi.Warning : ConstructionUi.Dim, 10f, (FontStyles)0, (TextAlignmentOptions)513); y -= 22f; ConstructionUi.Button(_content, new Rect(x, y, 104f, 28f), "RE-TASK", _zones, ConstructionManager.CanCommand, delegate { ConstructionManager.ClearBuild(site); ClearPending(); }); ConstructionUi.Button(_content, new Rect(x + 104f + 4f, y, 104f, 28f), "CANCEL", _zones, enabled: true, ClearPending); return y - 32f - 4f; } private static float ManifestLines(IList manifest, ConstructionSite site, float x, float y, float w) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < manifest.Count; i++) { ConstructionCatalogue.MaterialLoad materialLoad = manifest[i]; int num = ((site != null) ? Mathf.Min(materialLoad.Count, site.DeliveredCount(materialLoad.JsonKey)) : 0); bool flag = site != null && num >= materialLoad.Count; ConstructionUi.Label(_content, materialLoad.DisplayName, new Rect(x + 8f, y, w - 80f, 16f), flag ? ConstructionUi.Accent : ConstructionUi.Dim, 11f, (FontStyles)0, (TextAlignmentOptions)513); ConstructionUi.Label(_content, (site != null) ? (num + " / " + materialLoad.Count) : ("x" + materialLoad.Count), new Rect(x + w - 70f, y, 70f, 16f), flag ? ConstructionUi.Accent : ConstructionUi.TextSecondary, 11f, (FontStyles)0, (TextAlignmentOptions)516); y -= 18f; } return y - 4f; } private static float Manifest(ConstructionSite site, float x, float y, float w) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) ConstructionCatalogue.BuildOption build = site.Build; int totalLoads = site.TotalLoads; ConstructionUi.Label(_content, build.DisplayName.ToUpperInvariant(), new Rect(x, y, w - 90f, 18f), ConstructionUi.TextPrimary, 14f, (FontStyles)1, (TextAlignmentOptions)513); ConstructionUi.Label(_content, site.DeliveredTotal + " / " + totalLoads, new Rect(x + w - 90f, y, 90f, 18f), ConstructionUi.Accent, 12f, (FontStyles)1, (TextAlignmentOptions)516); y -= 22f; ConstructionUi.Bar(_content, new Rect(x, y, w, 3f), (totalLoads > 0) ? ((float)site.DeliveredTotal / (float)totalLoads) : 0f, ConstructionUi.Accent); y -= 11f; if (site.Production != null) { string text = "produces " + ConstructionCatalogue.ProductionName(site.Production); text = text + " (rank " + ConstructionCatalogue.ProductionRank(site.Production) + ")"; ConstructionUi.Label(_content, text, new Rect(x, y, w, 14f), ConstructionUi.TextSecondary, 10f, (FontStyles)0, (TextAlignmentOptions)513); y -= 18f; } y = ManifestLines(site.Required, site, x, y, w); if (!string.IsNullOrEmpty(site.Blocked)) { ConstructionUi.Label(_content, site.Blocked, new Rect(x, y, w, 16f), ConstructionUi.Alert, 10f, (FontStyles)0, (TextAlignmentOptions)513); y -= 20f; } else if (!site.Completed) { ConstructionUi.Label(_content, build.Summary, new Rect(x, y, w, 16f), ConstructionUi.Dim, 10f, (FontStyles)2, (TextAlignmentOptions)513); y -= 20f; } if (!site.Completed) { ConstructionUi.Button(_content, new Rect(x, y, 104f, 28f), "RE-TASK", _zones, ConstructionManager.CanCommand, delegate { _stage = Stage.Retask; }); y -= 32f; } return y - 4f; } } public static class ConstructionRegistry { private const int CratePrefabHash = 1178751813; public const string CrateJsonKey = "hendev_construction_crate"; public const string MountJsonKey = "hendev_construction_mount"; private static readonly string[] VisualFallbacks = new string[4] { "NavalSupplyContainer1", "MunitionsContainer1", "FuelContainer3", "Container1" }; private static readonly string[] SiteIconFallbacks = new string[5] { "factory_large", "VehicleDepot1", "factory_tall", "ammunitionBunker", "controlTower1" }; public static bool Registered { get; private set; } public static Sprite DefaultSiteIcon { get; private set; } public static IEnumerator RegisterWhenReady() { while (!Registered) { yield return (object)new WaitForSeconds(1f); if (!((Object)(object)Encyclopedia.i == (Object)null) && Encyclopedia.Lookup != null && Encyclopedia.WeaponLookup != null && Encyclopedia.i.IndexLookup != null) { TryRegister(); } } } private static void TryRegister() { WeaponMount mount = ConstructionAssets.Mount; if ((Object)(object)mount == (Object)null) { Abort("bundle has no WeaponMount — nothing to register"); return; } if (Encyclopedia.WeaponLookup.ContainsKey("hendev_construction_mount")) { Registered = true; return; } if ((Object)(object)mount.prefab == (Object)null) { Abort("mount has no pod prefab"); return; } if ((Object)(object)mount.info == (Object)null) { Abort("mount has no WeaponInfo"); return; } if (!mount.Cargo && !mount.Troops) { Abort("mount has neither Cargo nor Troops set — it would fit no hardpoint"); return; } MountedCargo componentInChildren = mount.prefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { Abort("pod prefab has no MountedCargo component"); return; } UnitDefinition crateDefinition = ConstructionAssets.CrateDefinition; if ((Object)(object)crateDefinition == (Object)null) { Abort("bundle has no crate UnitDefinition"); return; } if ((Object)(object)crateDefinition.unitPrefab == (Object)null) { Abort("crate definition has no unitPrefab"); return; } if ((Object)(object)crateDefinition.unitPrefab.GetComponent() == (Object)null) { Abort("crate prefab has no NetworkIdentity — refusing to inject"); return; } if ((Object)(object)crateDefinition.unitPrefab.GetComponent() == (Object)null) { Abort("crate prefab has no Unit component (expected Container) — refusing to inject"); return; } if (HashCollides(1178751813)) { Abort($"crate prefab hash 0x{1178751813:X} already used by a vanilla prefab"); return; } bool flag = false; try { crateDefinition.unitPrefab.GetComponent().PrefabHash = 1178751813; crateDefinition.jsonKey = "hendev_construction_crate"; if (crateDefinition.mass <= 0f) { crateDefinition.mass = 10000f; } UnitDefinition val = ResolveVisualDonor(); UnitDefinition preferred = ResolveSiteIconDonor() ?? val; BorrowIcons(crateDefinition, preferred); BorrowWeaponIcon(mount.info, val); Encyclopedia.i.otherUnits.Add(crateDefinition); Encyclopedia.Lookup.Add("hendev_construction_crate", crateDefinition); ((INetworkDefinition)crateDefinition).LookupIndex = Encyclopedia.i.IndexLookup.Count; Encyclopedia.i.IndexLookup.Add((INetworkDefinition)(object)crateDefinition); flag = true; componentInChildren.cargo = crateDefinition; mount.jsonKey = "hendev_construction_mount"; mount.Initialize(); if ((Object)(object)ConstructionAssets.Info != (Object)null) { mount.info = ConstructionAssets.Info; if (ConstructionAssets.Info.weaponName != null) { mount.mountName = ConstructionAssets.Info.weaponName; } } Encyclopedia.i.weaponMounts.Add(mount); Encyclopedia.WeaponLookup.Add("hendev_construction_mount", mount); ((INetworkDefinition)mount).LookupIndex = Encyclopedia.i.IndexLookup.Count; Encyclopedia.i.IndexLookup.Add((INetworkDefinition)(object)mount); AddToCargoHardpoints(); GraftParachute(crateDefinition.unitPrefab); if (Plugin.UseStockVisual.Value) { ApplyVisual(crateDefinition.unitPrefab, val, "crate"); ApplyVisual(mount.prefab, val, "pod"); } Registered = true; Plugin.Log.LogInfo((object)(string.Format("[registry] crate '{0}' (index {1}) ", "hendev_construction_crate", ((INetworkDefinition)crateDefinition).LookupIndex) + string.Format("and mount '{0}' (index {1}) registered.", "hendev_construction_mount", ((INetworkDefinition)mount).LookupIndex))); WeaponInfo info = ((Weapon)componentInChildren).info; Plugin.Log.LogInfo((object)("[registry] names: mount.mountName='" + mount.mountName + "' mount.info='" + (((Object)(object)mount.info != (Object)null) ? mount.info.weaponName : "NULL") + "' pod.info='" + (((Object)(object)info != (Object)null) ? info.weaponName : "NULL") + "' bundleInfo='" + (((Object)(object)ConstructionAssets.Info != (Object)null) ? ConstructionAssets.Info.weaponName : "NULL") + "' " + $"sameRef={(Object)(object)mount.info == (Object)(object)ConstructionAssets.Info}/{(Object)(object)info == (Object)(object)ConstructionAssets.Info}")); } catch (Exception ex) { if (flag) { Encyclopedia.i.otherUnits.Remove(crateDefinition); Encyclopedia.Lookup.Remove("hendev_construction_crate"); Encyclopedia.i.IndexLookup.Remove((INetworkDefinition)(object)crateDefinition); } Encyclopedia.i.weaponMounts.Remove(mount); Encyclopedia.WeaponLookup.Remove("hendev_construction_mount"); Encyclopedia.i.IndexLookup.Remove((INetworkDefinition)(object)mount); Abort("injection threw and was rolled back: " + ex.GetType().Name + ": " + ex.Message); } } private static void ApplyVisual(GameObject prefab, UnitDefinition donorDef, string label) { //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null) { return; } MeshFilter componentInChildren = prefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { Plugin.Log.LogWarning((object)("[visual] " + label + " prefab has no MeshFilter to replace")); return; } if ((Object)(object)donorDef == (Object)null || (Object)(object)donorDef.unitPrefab == (Object)null) { Plugin.Log.LogWarning((object)("[visual] no donor container found — " + label + " keeps the placeholder cube")); return; } MeshFilter val = null; int num = -1; MeshFilter[] componentsInChildren = donorDef.unitPrefab.GetComponentsInChildren(true); foreach (MeshFilter val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.sharedMesh == (Object)null)) { int vertexCount = val2.sharedMesh.vertexCount; if (vertexCount > num) { num = vertexCount; val = val2; } } } if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("[visual] '" + donorDef.jsonKey + "' has no mesh")); return; } MeshRenderer component = ((Component)val).GetComponent(); MeshRenderer component2 = ((Component)componentInChildren).GetComponent(); componentInChildren.sharedMesh = val.sharedMesh; if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null) { ((Renderer)component2).sharedMaterials = ((Renderer)component).sharedMaterials; } ((Component)componentInChildren).transform.localScale = ((Component)val).transform.lossyScale; ((Component)componentInChildren).transform.localRotation = Quaternion.identity; Plugin.Log.LogInfo((object)($"[visual] {label} now uses '{donorDef.jsonKey}' mesh ({num} verts, " + $"{(((Object)(object)component != (Object)null) ? ((Renderer)component).sharedMaterials.Length : 0)} material(s)) — visual only.")); } private static void GraftParachute(GameObject cratePrefab) { if ((Object)(object)cratePrefab == (Object)null) { return; } Container component = cratePrefab.GetComponent(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogWarning((object)"[parachute] the crate prefab has no Container"); return; } FieldInfo field = typeof(Container).GetField("parachuteSystem", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { Plugin.Log.LogWarning((object)"[parachute] Container.parachuteSystem not found — airdrops will not deploy a chute"); } else { if (field.GetValue(component) != null) { return; } string[] visualFallbacks = VisualFallbacks; foreach (string text in visualFallbacks) { if (!Encyclopedia.Lookup.TryGetValue(text, out var value) || (Object)(object)value == (Object)null || (Object)(object)value.unitPrefab == (Object)null) { continue; } Container component2 = value.unitPrefab.GetComponent(); if (!((Object)(object)component2 == (Object)null)) { object? value2 = field.GetValue(component2); GameObject val = (GameObject)((value2 is GameObject) ? value2 : null); if (!((Object)(object)val == (Object)null)) { field.SetValue(component, val); Plugin.Log.LogInfo((object)("[parachute] crate borrows '" + text + "' parachute system — airdropped crates now descend under canopy.")); return; } } } Plugin.Log.LogWarning((object)"[parachute] no stock container with a parachute system was found; an airdropped crate will fall unchecked. Helicopter delivery is unaffected."); } } private static UnitDefinition ResolveVisualDonor() { string value = Plugin.StockVisualKey.Value; if (!string.IsNullOrEmpty(value) && Encyclopedia.Lookup.TryGetValue(value, out var value2) && (Object)(object)value2 != (Object)null) { return value2; } if (!string.IsNullOrEmpty(value)) { Plugin.Log.LogWarning((object)("[visual] '" + value + "' not in the Encyclopedia — trying fallbacks")); } string[] visualFallbacks = VisualFallbacks; foreach (string key in visualFallbacks) { if (Encyclopedia.Lookup.TryGetValue(key, out var value3) && (Object)(object)value3 != (Object)null) { return value3; } } return null; } private static void BorrowIcons(UnitDefinition def, UnitDefinition preferred) { if ((Object)(object)def.mapIcon != (Object)null) { DefaultSiteIcon = def.mapIcon; return; } UnitDefinition val = (((Object)(object)preferred != (Object)null && (Object)(object)preferred.mapIcon != (Object)null) ? preferred : null); if ((Object)(object)val == (Object)null) { string[] visualFallbacks = VisualFallbacks; foreach (string key in visualFallbacks) { if (Encyclopedia.Lookup.TryGetValue(key, out var value) && (Object)(object)value != (Object)null && (Object)(object)value.mapIcon != (Object)null) { val = value; break; } } } if ((Object)(object)val == (Object)null && Encyclopedia.i.vehicles != null && Encyclopedia.i.vehicles.Count > 0) { val = (UnitDefinition)(object)Encyclopedia.i.vehicles[0]; } if (!((Object)(object)val == (Object)null)) { def.mapIcon = val.mapIcon; def.friendlyIcon = val.friendlyIcon; def.hostileIcon = val.hostileIcon; def.mapOrient = false; if (def.iconSize <= 0f) { def.iconSize = ((val.iconSize > 0f) ? val.iconSize : 1f); } float num = ((def.mapIconSize > 0f) ? def.mapIconSize : 1f); def.mapIconSize = num * Mathf.Max(0.1f, Plugin.SiteMapIconScale.Value); DefaultSiteIcon = val.mapIcon; Plugin.Log.LogInfo((object)("[registry] site icons borrowed from '" + val.jsonKey + "' (sprite '" + (((Object)(object)val.mapIcon != (Object)null) ? ((Object)val.mapIcon).name : "NULL") + "'), " + $"mapIconSize {num:F2} -> {def.mapIconSize:F2}.")); } } private static UnitDefinition ResolveSiteIconDonor() { string value = Plugin.SiteMapIconKey.Value; if (!string.IsNullOrEmpty(value) && Encyclopedia.Lookup.TryGetValue(value, out var value2) && (Object)(object)value2 != (Object)null && (Object)(object)value2.mapIcon != (Object)null) { return value2; } if (!string.IsNullOrEmpty(value)) { Plugin.Log.LogWarning((object)("[visual] site icon donor '" + value + "' is not in the Encyclopedia or has no map icon — trying fallbacks")); } string[] siteIconFallbacks = SiteIconFallbacks; foreach (string key in siteIconFallbacks) { if (Encyclopedia.Lookup.TryGetValue(key, out var value3) && (Object)(object)value3 != (Object)null && (Object)(object)value3.mapIcon != (Object)null) { return value3; } } if (Encyclopedia.i.buildings != null) { foreach (BuildingDefinition building in Encyclopedia.i.buildings) { if ((Object)(object)building != (Object)null && (Object)(object)((UnitDefinition)building).mapIcon != (Object)null) { return (UnitDefinition)(object)building; } } } return null; } private static void BorrowWeaponIcon(WeaponInfo info, UnitDefinition donorDef) { if ((Object)(object)info == (Object)null || (Object)(object)info.weaponIcon != (Object)null) { return; } if ((Object)(object)donorDef != (Object)null) { foreach (WeaponMount weaponMount in Encyclopedia.i.weaponMounts) { if (!((Object)(object)weaponMount == (Object)null) && weaponMount.Cargo && !((Object)(object)weaponMount.info == (Object)null) && !((Object)(object)weaponMount.info.weaponIcon == (Object)null)) { MountedCargo val = (((Object)(object)weaponMount.prefab != (Object)null) ? weaponMount.prefab.GetComponentInChildren(true) : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.cargo != (Object)(object)donorDef)) { info.weaponIcon = weaponMount.info.weaponIcon; Plugin.Log.LogInfo((object)("[visual] borrowed weapon icon from '" + weaponMount.jsonKey + "' (matches the mesh donor)")); return; } } } } foreach (WeaponMount weaponMount2 in Encyclopedia.i.weaponMounts) { if (!((Object)(object)weaponMount2 == (Object)null) && weaponMount2.Cargo && !((Object)(object)weaponMount2.info == (Object)null) && !((Object)(object)weaponMount2.info.weaponIcon == (Object)null) && !((Object)(object)weaponMount2.info == (Object)(object)info) && !((Object)(object)weaponMount2.prefab == (Object)null)) { MountedCargo componentInChildren = weaponMount2.prefab.GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)componentInChildren.cargo == (Object)null) && !((Object)(object)componentInChildren.cargo.unitPrefab == (Object)null) && !((Object)(object)componentInChildren.cargo.unitPrefab.GetComponent() == (Object)null)) { info.weaponIcon = weaponMount2.info.weaponIcon; Plugin.Log.LogInfo((object)("[visual] borrowed weapon icon from container mount '" + weaponMount2.jsonKey + "'")); return; } } } Plugin.Log.LogWarning((object)"[visual] no container cargo mount had a weapon icon — HUD tile stays blank"); } private static void AddToCargoHardpoints() { WeaponMount mount = ConstructionAssets.Mount; int num = 0; int num2 = 0; List list = new List(); foreach (AircraftDefinition item in Encyclopedia.i.aircraft) { if ((Object)(object)item == (Object)null || (Object)(object)((UnitDefinition)item).unitPrefab == (Object)null) { continue; } WeaponManager componentInChildren = ((UnitDefinition)item).unitPrefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || componentInChildren.hardpointSets == null) { continue; } bool flag = false; HardpointSet[] hardpointSets = componentInChildren.hardpointSets; foreach (HardpointSet val in hardpointSets) { if (val == null || val.weaponOptions == null || val.weaponOptions.Contains(mount)) { continue; } bool flag2 = false; foreach (WeaponMount weaponOption in val.weaponOptions) { if ((Object)(object)weaponOption != (Object)null && weaponOption.Cargo) { flag2 = true; break; } } if (flag2) { val.weaponOptions.Add(mount); num2++; flag = true; } } if (flag) { num++; list.Add(((UnitDefinition)item).unitName); } } Plugin.Log.LogInfo((object)($"[registry] crate offered on {num2} cargo hardpoint(s) across {num} aircraft: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "NONE — no aircraft has a cargo hardpoint"))); } private static void Abort(string reason) { Registered = true; Plugin.Log.LogError((object)("[registry] NOT registering the construction crate: " + reason + ". The mod will stay inert; the game is unaffected.")); } private static bool HashCollides(int hash) { foreach (INetworkDefinition item in Encyclopedia.i.IndexLookup) { UnitDefinition val = (UnitDefinition)(object)((item is UnitDefinition) ? item : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.unitPrefab == (Object)null)) { NetworkIdentity component = val.unitPrefab.GetComponent(); if ((Object)(object)component != (Object)null && component.PrefabHash == hash) { return true; } } } return false; } } public sealed class ConstructionSite { public readonly int Id; public Container Crate; public GlobalPosition Position; public Quaternion Rotation = Quaternion.identity; public FactionHQ HQ; public float CreatedAt; public ConstructionCatalogue.BuildOption Build; public string Production; public readonly List Required = new List(); public float ProductionInterval = 900f; public readonly Dictionary Delivered = new Dictionary(); public readonly Dictionary Contributors = new Dictionary(); public bool Completed; public string Blocked; public bool AwaitingChoice { get { if (Build == null) { return !Completed; } return false; } } public bool ManifestSatisfied { get { if (Build == null || Required.Count == 0) { return false; } foreach (ConstructionCatalogue.MaterialLoad item in Required) { if (Outstanding(item) > 0) { return false; } } return true; } } public int DeliveredTotal { get { int num = 0; foreach (ConstructionCatalogue.MaterialLoad item in Required) { num += Mathf.Min(item.Count, DeliveredCount(item.JsonKey)); } return num; } } public int TotalLoads => ConstructionCatalogue.TotalLoads(Required); public void Credit(PersistentID deliverer) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!((PersistentID)(ref deliverer)).NotValid) { Contributors[deliverer] = ((!Contributors.TryGetValue(deliverer, out var value)) ? 1 : (value + 1)); } } public ConstructionSite(int id) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Id = id; } public int DeliveredCount(string jsonKey) { if (!Delivered.TryGetValue(jsonKey, out var value)) { return 0; } return value; } public int Outstanding(ConstructionCatalogue.MaterialLoad load) { return Mathf.Max(0, load.Count - DeliveredCount(load.JsonKey)); } public bool Wants(string jsonKey) { if (Build == null || Completed) { return false; } foreach (ConstructionCatalogue.MaterialLoad item in Required) { if (item.JsonKey == jsonKey && Outstanding(item) > 0) { return true; } } return false; } public void Deliver(string jsonKey) { Delivered[jsonKey] = DeliveredCount(jsonKey) + 1; } public void ClearChoice() { Build = null; Production = null; Blocked = null; Required.Clear(); } public void Describe(StringBuilder sb) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((GlobalPosition)(ref Position)).AsVector3(); sb.AppendLine(string.Format("[site {0}] {1}", Id, Completed ? "COMPLETE" : ((Build == null) ? "awaiting choice" : "building")) + string.Format(" pos=({0:F0}, {1:F0}, {2:F0}) faction={3}", val.x, val.y, val.z, ((Object)(object)HQ != (Object)null) ? HQ.faction.factionName : "none") + " crate=" + (((Object)(object)Crate != (Object)null) ? "present" : "gone")); if (Build != null) { sb.AppendLine(" build=" + Build.Id + " (" + Build.BuildingJsonKey + ")" + ((Production != null) ? (" produces=" + Production) : "") + ((Production != null) ? $" rank={ConstructionCatalogue.ProductionRank(Production)}" : "") + $" progress={DeliveredTotal}/{TotalLoads}"); foreach (ConstructionCatalogue.MaterialLoad item in Required) { sb.AppendLine($" {item.JsonKey}: {Mathf.Min(item.Count, DeliveredCount(item.JsonKey))}/{item.Count}"); } } if (!string.IsNullOrEmpty(Blocked)) { sb.AppendLine(" blocked: " + Blocked); } } } public sealed class ConstructionTransportState : PilotBaseState { private enum Phase { Ingress, Approach, Egress } private GlobalPosition target; private readonly Func stillWanted; private readonly Action onDelivered; private Phase phase; private bool isRotorcraft; private bool released; private float enteredAt; private float touchedDownTime; private float lastWantedCheck; private float targetHeight; private AircraftParameters parameters; public GlobalPosition ReleasePosition { get; private set; } public bool Delivered => released; public bool Finished { get; private set; } public ConstructionTransportState(Aircraft aircraft, GlobalPosition target, Func stillWanted, Action onDelivered) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) base.aircraft = aircraft; this.target = target; this.stillWanted = stillWanted; this.onDelivered = onDelivered; } public void Retarget(GlobalPosition position) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (!released) { target = position; } } public override void EnterState(Pilot pilot) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 base.pilot = pilot; base.aircraft = pilot.aircraft; base.controlInputs = base.aircraft.GetInputs(); parameters = base.aircraft.GetAircraftParameters(); isRotorcraft = (int)pilot.pilotType > 0; base.stateDisplayName = "Delivering Construction Load"; enteredAt = Time.timeSinceLevelLoad; touchedDownTime = 0f; targetHeight = 20f; phase = Phase.Ingress; base.aircraft.SetFlightAssistToDefault(); SelectCargoStation(); } public override void UpdateState(Pilot pilot) { } public override void LeaveState() { } public override void FixedUpdateState(Pilot pilot) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (phase == Phase.Egress) { GoHome(); return; } if (Time.timeSinceLevelLoad - lastWantedCheck > 1f) { lastWantedCheck = Time.timeSinceLevelLoad; if (released || Time.timeSinceLevelLoad - enteredAt > Plugin.AiSortieTimeout.Value || !HasCargo() || (stillWanted != null && !stillWanted())) { phase = Phase.Egress; GoHome(); return; } } Vector3 offset = target - GlobalPositionExtensions.GlobalPosition((Unit)(object)base.aircraft); offset.y = 0f; float magnitude = ((Vector3)(ref offset)).magnitude; if (isRotorcraft) { FlyRotorcraft(magnitude); } else { FlyPlane(magnitude, offset); } } private void FlyRotorcraft(float range) { //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) float num = (((Object)(object)parameters != (Object)null) ? parameters.minimumRadarAlt : 100f); if (range < 1500f) { num *= 0.3f; if (range < 200f) { num = Mathf.Lerp(0f, num, (range - 10f) / 200f); } } bool flag = range < 1000f; if (base.aircraft.gearDeployed != flag) { base.aircraft.SetGear(flag); } if (range < 300f) { phase = Phase.Approach; if (!base.aircraft.IsAutoHoverEnabled()) { base.aircraft.GetControlsFilter().SetAutoHover(true); base.aircraft.SetFlightAssist(false); } Vector3 val = ((range > 50f) ? (target - GlobalPositionExtensions.GlobalPosition((Unit)(object)base.aircraft)) : base.aircraft.cockpit.xform.forward); targetHeight = ((range > 20f) ? 20f : (targetHeight - 2f * Time.fixedDeltaTime)); base.aircraft.autopilot.Hover(target, targetHeight, val); } else { base.aircraft.SetFlightAssist(true); base.aircraft.autopilot.AutoAim(target, num, Vector3.zero, Vector3.zero, range > 200f); } if (!(((Unit)base.aircraft).radarAlt < 2f)) { return; } base.controlInputs.brake = 1f; base.controlInputs.throttle = 0f; base.controlInputs.pitch = 0f; base.controlInputs.yaw = 0f; base.controlInputs.roll = 0f; if (((Unit)base.aircraft).speed < 10f) { touchedDownTime += Time.deltaTime; Release(); if (touchedDownTime > 7f) { phase = Phase.Egress; } } } private void FlyPlane(float range, Vector3 offset) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(50f, Plugin.PlaneDropAltitude.Value); if (base.aircraft.gearDeployed) { base.aircraft.SetGear(false); } base.aircraft.SetFlightAssist(true); if (range > 3000f) { float num2 = (((Object)(object)parameters != (Object)null) ? Mathf.Max(num, parameters.minimumRadarAlt) : num); base.aircraft.autopilot.AutoAim(target, num2, Vector3.zero, Vector3.zero, true); return; } phase = Phase.Approach; Vector3 val = ((((Vector3)(ref offset)).sqrMagnitude > 1f) ? ((Vector3)(ref offset)).normalized : ((Component)base.aircraft).transform.forward); GlobalPosition val2 = target + val * 4000f; base.aircraft.autopilot.AutoAim(val2, num, Vector3.zero, Vector3.zero, false); float num3 = Mathf.Max(0f, ((Unit)base.aircraft).speed * Plugin.PlaneDropLeadSeconds.Value); float num4 = Vector3.Angle(offset, ((Component)base.aircraft).transform.forward); bool flag = ((Unit)base.aircraft).radarAlt > 40f && ((Unit)base.aircraft).radarAlt < num * 2.5f; if (range <= num3 + 30f && num4 < 35f && flag) { Release(); phase = Phase.Egress; } } private void SelectCargoStation() { if (((Unit)base.aircraft).weaponStations == null) { return; } foreach (WeaponStation weaponStation in ((Unit)base.aircraft).weaponStations) { if (weaponStation != null && (Object)(object)weaponStation.WeaponInfo != (Object)null && weaponStation.WeaponInfo.cargo) { base.aircraft.weaponManager.currentWeaponStation = weaponStation; break; } } } private bool HasCargo() { if (((Unit)base.aircraft).weaponStations == null) { return false; } foreach (WeaponStation weaponStation in ((Unit)base.aircraft).weaponStations) { if (weaponStation != null && (Object)(object)weaponStation.WeaponInfo != (Object)null && weaponStation.WeaponInfo.cargo && weaponStation.Ammo > 0) { return true; } } return false; } private void Release() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (released) { return; } SelectCargoStation(); released = true; ReleasePosition = GlobalPositionExtensions.GlobalPosition((Unit)(object)base.aircraft); base.pilot.Fire(); base.pilot.flightInfo.LastCargoDelivery = Time.timeSinceLevelLoad; base.pilot.flightInfo.DeliveredCargo = true; if (onDelivered == null) { return; } try { onDelivered(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ai] delivery callback threw: " + ex.Message)); } } private void GoHome() { if (!Finished) { Finished = true; base.stateDisplayName = "Returning From Delivery"; PilotBaseState val = (PilotBaseState)(isRotorcraft ? ((object)base.pilot.AIHeloLandingState) : ((object)base.pilot.AILandingState)); if (val == null) { val = (PilotBaseState)(isRotorcraft ? ((object)base.pilot.AIHeloCombatState) : ((object)base.pilot.AICombatState)); } if (val != null) { base.pilot.SwitchState(val); } } } } internal static class ConstructionUi { internal sealed class HotZone { public readonly RectTransform Rect; public readonly Action OnClick; public readonly Image Fill; public readonly Color RestColor; public readonly Color HoverColor; public readonly bool Enabled; public HotZone(RectTransform rect, Image fill, Color rest, Color hover, bool enabled, Action onClick) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Rect = rect; Fill = fill; RestColor = rest; HoverColor = hover; Enabled = enabled; OnClick = onClick; } public void Paint(bool hovered) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Fill == (Object)null)) { ((Graphic)Fill).color = ((hovered && Enabled) ? HoverColor : RestColor); } } } public const float RowHeight = 30f; public const float RowPitch = 32f; public const float TabHeight = 32f; public const float Gap = 4f; public const float Pad = 12f; public const float Space2 = 8f; public const float Space3 = 12f; public const float ButtonCompact = 44f; public const float ButtonAction = 104f; public const float FontMicro = 10f; public const float FontSmall = 11f; public const float FontBody = 12f; public const float FontLead = 14f; public const float FontTitle = 18f; public static readonly Color PanelGround = new Color(0.028f, 0.045f, 0.062f, 0.945f); public static readonly Color CardFill = new Color(0f, 0f, 0f, 0.3f); public static readonly Color Frame = new Color(0.52f, 0.55f, 0.56f, 0.55f); public static readonly Color Dim = new Color(0.64f, 0.68f, 0.7f); public static readonly Color Disabled = new Color(0.42f, 0.45f, 0.46f, 0.75f); public static readonly Color TextPrimary = Color.white; public static readonly Color TextSecondary = new Color(0.58f, 0.639f, 0.722f); public static readonly Color Accent = new Color(0.3f, 1f, 0.35f); public static readonly Color Warning = new Color(0.961f, 0.62f, 0.043f); public static readonly Color Alert = new Color(0.937f, 0.267f, 0.267f); private static TMP_FontAsset _font; public static TMP_FontAsset Font { get { if ((Object)(object)_font != (Object)null) { return _font; } TextMeshProUGUI val = Object.FindObjectOfType(); _font = (((Object)(object)val != (Object)null) ? ((TMP_Text)val).font : null); return _font; } } public static void ResetFont() { _font = null; } public static void Place(RectTransform rt, Rect rect) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = new Vector2(0f, 1f); rt.anchorMax = new Vector2(0f, 1f); rt.pivot = new Vector2(0f, 1f); rt.sizeDelta = new Vector2(((Rect)(ref rect)).width, ((Rect)(ref rect)).height); rt.anchoredPosition = new Vector2(((Rect)(ref rect)).x, ((Rect)(ref rect)).y); ((Transform)rt).localScale = Vector3.one; } public static RectTransform Group(RectTransform parent, Rect rect, string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) RectTransform component = new GameObject(name, new Type[1] { typeof(RectTransform) }).GetComponent(); ((Transform)component).SetParent((Transform)(object)parent, false); Place(component, rect); return component; } public static Image Rule(RectTransform parent, Rect rect, Color color) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Rule", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent((Transform)(object)parent, false); Place(component, rect); Image component2 = val.GetComponent(); ((Graphic)component2).color = color; ((Graphic)component2).raycastTarget = false; return component2; } public static void Outline(RectTransform parent, Rect rect, Color color) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) Rule(parent, new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), color); Rule(parent, new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y - ((Rect)(ref rect)).height + 1f, ((Rect)(ref rect)).width, 1f), color); Rule(parent, new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), color); Rule(parent, new Rect(((Rect)(ref rect)).x + ((Rect)(ref rect)).width - 1f, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), color); } public static Image Surface(RectTransform parent, Rect rect, Color fill, Color frame) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) Image obj = Rule(parent, rect, fill); ((Transform)((Graphic)obj).rectTransform).SetAsFirstSibling(); Outline(parent, rect, frame); return obj; } public static TMP_Text Label(RectTransform parent, string text, Rect rect, Color color, float size, FontStyles style, TextAlignmentOptions align) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Label", new Type[1] { typeof(RectTransform) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent((Transform)(object)parent, false); Place(component, rect); TextMeshProUGUI val2 = val.AddComponent(); TMP_FontAsset font = Font; if ((Object)(object)font != (Object)null) { ((TMP_Text)val2).font = font; } ((TMP_Text)val2).text = text; ((Graphic)val2).color = color; ((TMP_Text)val2).fontSize = size; ((TMP_Text)val2).fontStyle = style; ((TMP_Text)val2).alignment = align; ((Graphic)val2).raycastTarget = false; ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)1; return (TMP_Text)(object)val2; } public static RectTransform Row(RectTransform parent, Rect rect, List zones, bool enabled, bool selected, Action onClick) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) RectTransform val = Group(parent, rect, "Row"); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(0f, 0f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height); Color val2 = (selected ? Wash(Accent, 0.36f, 0.82f) : CardFill); Color hover = (enabled ? Wash(Accent, 0.28f, 0.66f) : val2); Image fill = Surface(val, rect2, val2, selected ? Accent : Frame); zones.Add(new HotZone(val, fill, val2, hover, enabled, onClick)); return val; } public static void Button(RectTransform parent, Rect rect, string text, List zones, bool enabled, Action onClick) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0064: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) RectTransform val = Group(parent, rect, "Button"); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(0f, 0f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height); Color val2 = (Color)(enabled ? Wash(Accent, 0.19f, 0.66f) : new Color(0.18f, 0.18f, 0.18f, 0.8f)); Color hover = (enabled ? Wash(Accent, 0.34f, 0.8f) : val2); Image fill = Surface(val, rect2, val2, enabled ? Frame : Disabled); Label(val, text, rect2, enabled ? TextPrimary : Disabled, 11f, (FontStyles)0, (TextAlignmentOptions)514); zones.Add(new HotZone(val, fill, val2, hover, enabled, onClick)); } public static Color Wash(Color accent, float scale, float alpha) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) return new Color(accent.r * scale, accent.g * scale, accent.b * scale, alpha); } public static void Bar(RectTransform parent, Rect rect, float fraction, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) //IL_0063: Unknown result type (might be due to invalid IL or missing references) Rule(parent, rect, new Color(1f, 1f, 1f, 0.08f)); float num = Mathf.Max(0f, Mathf.Min(1f, fraction)) * ((Rect)(ref rect)).width; if (num > 0.5f) { Rule(parent, new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, num, ((Rect)(ref rect)).height), color); } } } public class ConstructObjectiveEditor : IObjectiveEditorUpdate { private readonly UIPrefabs prefabs; private readonly ConstructBuildingObjective objective; private readonly GameObject parent; private WaypointObjectiveHandle handle; private bool bound; public ConstructObjectiveEditor(Canvas canvas, UIPrefabs prefabs, ConstructBuildingObjective objective) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown this.prefabs = prefabs; this.objective = objective; parent = new GameObject("ConstructionOps_Objective_Editor", new Type[1] { typeof(RectTransform) }); parent.transform.SetParent(((Component)canvas).transform); parent.transform.SetAsFirstSibling(); } public void Update() { if (!((Object)(object)prefabs == (Object)null) && !((Object)(object)prefabs.WaypointEditor == (Object)null)) { if ((Object)(object)handle == (Object)null) { handle = Object.Instantiate(prefabs.WaypointEditor, parent.transform); bound = false; } if (!bound) { bound = true; handle.SetWaypoint((WaypointEditor)null, 0, objective.Spot, (Objective)(object)objective); } } } public void Destroy() { if ((Object)(object)parent != (Object)null) { Object.Destroy((Object)(object)parent); } handle = null; bound = false; } } public static class ConstructObjectivePatches { private const int WireTag = 6; public const string DropdownLabel = "Construct Building"; private static bool _applied; public static bool Ready { get; private set; } public static void Apply(Harmony harmony) { if (!_applied) { _applied = true; Ready = (byte)(1u & (Patch(harmony, typeof(SavedObjective), "CreateSavedObjective", "CreateSavedObjectivePrefix") ? 1u : 0u) & (Patch(harmony, typeof(SavedObjective), "Create", "CreatePrefix") ? 1u : 0u) & (Patch(harmony, typeof(MissionNetworkExtensions), "WriteSavedObjective", "WriteSavedObjectivePrefix") ? 1u : 0u) & (Patch(harmony, typeof(MissionNetworkExtensions), "ReadSavedObjective", "ReadSavedObjectivePrefix") ? 1u : 0u) & (PatchBinder(harmony) ? 1u : 0u) & (InjectDropdownEntry() ? 1u : 0u)) != 0; if (Ready) { Plugin.Log.LogInfo((object)"[objective] custom objective type registered — 'Construct Building' is available under New Objective."); } else { Plugin.Log.LogWarning((object)"[objective] the custom objective type is NOT available; the 'build:' naming convention still works."); } } } private static bool Patch(Harmony harmony, Type target, string method, string prefix) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(target, method, (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogError((object)("[objective] " + target.Name + "." + method + " not found — the game has changed; the custom objective type is disabled.")); return false; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(ConstructObjectivePatches), prefix, (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[objective] failed to patch " + target.Name + "." + method + ": " + ex.Message)); return false; } } private static bool PatchBinder(Harmony harmony) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown try { Type type = AccessTools.Inner(typeof(NewtonsoftHelper), "ShortNameSerializationBinder"); if (type == null) { Plugin.Log.LogError((object)"[objective] ShortNameSerializationBinder not found — a saved mission using the custom objective would fail to load."); return false; } MethodInfo methodInfo = AccessTools.Method(type, "BindToType", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogError((object)"[objective] BindToType not found."); return false; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(ConstructObjectivePatches), "BindToTypePrefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[objective] failed to patch the serialization binder: " + ex.Message)); return false; } } private static bool CreateSavedObjectivePrefix(ObjectiveType type, string uniqueName, ref SavedObjective __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)type != 5) { return true; } __result = (SavedObjective)(object)new ConstructBuildingSavedObjective(uniqueName); return false; } private static bool CreatePrefix(SavedObjective savedObjective, ref Objective __result) { if (!(savedObjective is ConstructBuildingSavedObjective savedObjective2)) { return true; } __result = (Objective)(object)new ConstructBuildingObjective(savedObjective2); return false; } private static bool WriteSavedObjectivePrefix(NetworkWriter writer, SavedObjective obj) { if (!(obj is ConstructBuildingSavedObjective constructBuildingSavedObjective)) { return true; } PackedExtensions.WritePackedInt32(writer, 6); StringExtensions.WriteString(writer, ((SavedObjective)constructBuildingSavedObjective).UniqueName ?? ""); StringExtensions.WriteString(writer, ((SavedObjective)constructBuildingSavedObjective).Faction ?? ""); StringExtensions.WriteString(writer, ((SavedObjective)constructBuildingSavedObjective).DisplayName ?? ""); writer.WriteBoolean(((SavedObjective)constructBuildingSavedObjective).Hidden); int num = ((((SavedObjective)constructBuildingSavedObjective).Outcomes != null) ? ((SavedObjective)constructBuildingSavedObjective).Outcomes.Count : 0); PackedExtensions.WritePackedInt32(writer, num); for (int i = 0; i < num; i++) { StringExtensions.WriteString(writer, ((SavedObjective)constructBuildingSavedObjective).Outcomes[i] ?? ""); } writer.WriteSingle(constructBuildingSavedObjective.Position.x); writer.WriteSingle(constructBuildingSavedObjective.Position.y); writer.WriteSingle(constructBuildingSavedObjective.Position.z); writer.WriteSingle(constructBuildingSavedObjective.Range); StringExtensions.WriteString(writer, constructBuildingSavedObjective.BuildId ?? "any"); PackedExtensions.WritePackedInt32(writer, constructBuildingSavedObjective.Count); writer.WriteBoolean(constructBuildingSavedObjective.AiBuild); return false; } private static bool ReadSavedObjectivePrefix(NetworkReader reader, ref SavedObjective __result) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) int bitPosition = reader.BitPosition; if (PackedExtensions.ReadPackedInt32(reader) != 6) { reader.MoveBitPosition(bitPosition); return true; } ConstructBuildingSavedObjective constructBuildingSavedObjective = new ConstructBuildingSavedObjective { UniqueName = StringExtensions.ReadString(reader), Faction = StringExtensions.ReadString(reader), DisplayName = StringExtensions.ReadString(reader), Hidden = reader.ReadBoolean() }; int num = PackedExtensions.ReadPackedInt32(reader); ((SavedObjective)constructBuildingSavedObjective).Outcomes = new List(num); for (int i = 0; i < num; i++) { ((SavedObjective)constructBuildingSavedObjective).Outcomes.Add(StringExtensions.ReadString(reader)); } float num2 = reader.ReadSingle(); float num3 = reader.ReadSingle(); float num4 = reader.ReadSingle(); constructBuildingSavedObjective.Position = new Vector3(num2, num3, num4); constructBuildingSavedObjective.Range = reader.ReadSingle(); constructBuildingSavedObjective.BuildId = StringExtensions.ReadString(reader); constructBuildingSavedObjective.Count = PackedExtensions.ReadPackedInt32(reader); constructBuildingSavedObjective.AiBuild = reader.ReadBoolean(); __result = (SavedObjective)(object)constructBuildingSavedObjective; return false; } private static bool BindToTypePrefix(string assemblyName, string typeName, ref Type __result) { if (string.IsNullOrEmpty(typeName)) { return true; } if (!typeName.StartsWith("ConstructionOps.", StringComparison.Ordinal)) { return true; } Type type = typeof(ConstructBuildingSavedObjective).Assembly.GetType(typeName); if (type == null || !typeof(SavedObjective).IsAssignableFrom(type)) { return true; } __result = type; return false; } private static bool InjectDropdownEntry() { try { EnumNames.GetNames(); Type? typeFromHandle = typeof(EnumNames); List list = Field(typeFromHandle, "realNames"); List list2 = Field(typeFromHandle, "names"); if (list == null || list2 == null) { Plugin.Log.LogError((object)"[objective] EnumNames private lists not found — 'Construct Building' will not appear in New Objective."); return false; } string item = 5.ToString(); if (list.Contains(item)) { return true; } list.Add(item); list2.Add("Construct Building"); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[objective] could not add the dropdown entry: " + ex.Message)); return false; } } private static List Field(Type type, string name) { FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.NonPublic); if (!(field != null)) { return null; } return field.GetValue(null) as List; } } [BepInPlugin("hendev.constructionops", "ConstructionOps", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "hendev.constructionops"; public const string Name = "ConstructionOps"; public const string Version = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry ProbeEveryMission; internal static ConfigEntry UseStockVisual; internal static ConfigEntry StockVisualKey; internal static ConfigEntry SiteMapIconKey; internal static ConfigEntry SiteMapIconScale; internal static ConfigEntry DeliveryReward; internal static ConfigEntry CompletionReward; internal static ConfigEntry DeliveryRadius; internal static ConfigEntry MaxSlopeDegrees; internal static ConfigEntry AirbaseAttachRadius; internal static ConfigEntry AiEnabled; internal static ConfigEntry AiMaxSortiesPerFaction; internal static ConfigEntry AiMaxSortiesPerSite; internal static ConfigEntry AiSupplyReserve; internal static ConfigEntry AiSortieTimeout; internal static ConfigEntry AiDispatchInterval; internal static ConfigEntry AiClaimDelay; internal static ConfigEntry AiPreferRotorcraft; internal static ConfigEntry PlaneDropAltitude; internal static ConfigEntry PlaneDropLeadSeconds; private void Awake() { //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master switch. When false the mod does nothing at all."); ProbeEveryMission = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "ProbeEveryMission", false, "Re-run the probe on every mission load instead of once per process. Only useful when comparing maps — the Encyclopedia does not change between missions, but the airbase list does."); UseStockVisual = ((BaseUnityPlugin)this).Config.Bind("Content", "UseStockVisual", true, "Borrow a stock container's MESH for the crate, instead of the grey placeholder cube. Visual only — the delivered unit is always our own crate, never the stock container, because stock containers carry gameplay components (a Refueler, for one) that would make a construction crate refuel aircraft. Set false once custom art exists."); StockVisualKey = ((BaseUnityPlugin)this).Config.Bind("Content", "StockVisualKey", "NavalSupplyContainer1", "Encyclopedia jsonKey of the container to borrow the mesh from. Mesh and materials only — none of its components are copied."); SiteMapIconKey = ((BaseUnityPlugin)this).Config.Bind("Content", "SiteMapIconKey", "factory_large", "Encyclopedia jsonKey of the BUILDING whose map icon a construction site borrows before a build has been chosen. A crate icon is tiny and reads as cargo; a structure icon reads as a site. Once a build is chosen the site switches to that building's own icon, so this only affects untasked sites. The borrowed sprite's name is logged at startup, so try another jsonKey if this one is unclear."); SiteMapIconScale = ((BaseUnityPlugin)this).Config.Bind("Content", "SiteMapIconScale", 2.5f, "Multiplier on the site's map icon size. The stock container icon is drawn at 1 and is hard to pick out, let alone click."); DeliveryReward = ((BaseUnityPlugin)this).Config.Bind("Rewards", "DeliveryReward", 3f, "Score and funds paid to the pilot who flies in one material load, through the game's own Supply reward — so it shows on the HUD like any other credit. 0 disables delivery rewards."); CompletionReward = ((BaseUnityPlugin)this).Config.Bind("Rewards", "CompletionReward", 30f, "Score and funds shared out when a building is finished, split between the pilots who delivered its loads in proportion to how many each flew. 0 disables it."); DeliveryRadius = ((BaseUnityPlugin)this).Config.Bind("Gameplay", "DeliveryRadius", 150f, "Metres. A material container that comes to rest within this distance of a construction site counts toward its manifest and is consumed. Generous enough that a rough drop still lands the load; tight enough that two nearby sites do not steal from each other."); MaxSlopeDegrees = ((BaseUnityPlugin)this).Config.Bind("Gameplay", "MaxSlopeDegrees", 10f, "Steepest ground, in degrees, a building may be built on. The game itself does no placement validation whatsoever, so this is the only thing stopping a factory half-buried in a hillside."); AirbaseAttachRadius = ((BaseUnityPlugin)this).Config.Bind("Gameplay", "AirbaseAttachRadius", 2000f, "Metres. A completed building within this distance of a FRIENDLY airbase is attached to it, which is what makes a Hangar contribute capacity and a WarheadStorage actually count. Builds that require an airbase refuse outside it."); AiEnabled = ((BaseUnityPlugin)this).Config.Bind("AI", "Enabled", true, "Let a faction fly its own construction. Only ever applies to a Construct Building objective with 'AI will construct this' ticked in the Mission Editor — with this on and no such objective in the mission, nothing changes. Server-side: on a client this setting does nothing, because the host runs all AI."); AiMaxSortiesPerFaction = ((BaseUnityPlugin)this).Config.Bind("AI", "MaxSortiesPerFaction", 2, "How many construction haulers one faction may have airborne at once. Every hauler is an airframe spent out of that faction's supply and a slot against its AI aircraft limit, so this is the main lever on how hard construction competes with the air war."); AiMaxSortiesPerSite = ((BaseUnityPlugin)this).Config.Bind("AI", "MaxSortiesPerSite", 1, "How many haulers may be inbound to the same site at once. 1 by default because two helicopters hovering onto the same spot collide."); AiSupplyReserve = ((BaseUnityPlugin)this).Config.Bind("AI", "SupplyReserve", 2, "Airframes of a given type the AI will never spend on construction. Stops a construction programme flying a faction out of transports."); AiSortieTimeout = ((BaseUnityPlugin)this).Config.Bind("AI", "SortieTimeout", 600f, "Seconds a hauler may spend on one delivery before it gives up and returns to base. Covers the case where the aim point cannot be reached at all — a spot ringed by SAMs, or terrain a helicopter cannot settle on."); AiDispatchInterval = ((BaseUnityPlugin)this).Config.Bind("AI", "DispatchInterval", 20f, "Seconds between launches. Paces the supply line so a six-load manifest reads as a steady stream rather than a swarm leaving at once."); AiClaimDelay = ((BaseUnityPlugin)this).Config.Bind("AI", "ClaimDelay", 15f, "Seconds a newly landed, untasked crate inside an AI objective's radius is left alone before the AI picks the build for it. The grace period is for the player who dropped that crate meaning to choose themselves."); AiPreferRotorcraft = ((BaseUnityPlugin)this).Config.Bind("AI", "PreferRotorcraft", true, "Prefer helicopters over planes when both can carry the load. A helicopter sets a load down at a stop, inside the delivery radius; a plane has to throw it and can miss. Turn off to watch transport planes do the work."); PlaneDropAltitude = ((BaseUnityPlugin)this).Config.Bind("AI", "PlaneDropAltitude", 150f, "Metres above ground a fixed-wing hauler flies its delivery pass. Low enough that the load lands near the aim point, high enough for the container's parachute to open."); PlaneDropLeadSeconds = ((BaseUnityPlugin)this).Config.Bind("AI", "PlaneDropLeadSeconds", 3f, "Seconds of travel ahead of the spot a fixed-wing hauler releases its load. The miss distance of every AI drop is written to ConstructionOps.sites.txt, so trim this against what actually happens rather than by feel."); if (!Enabled.Value) { Log.LogInfo((object)"ConstructionOps 1.0.0 loaded but disabled by config."); return; } if (!ConstructionAssets.Load(Log)) { Log.LogWarning((object)"Bundle not loaded — the construction crate will not be registered. Diagnostics still run."); } try { ConstructObjectivePatches.Apply(new Harmony("hendev.constructionops")); } catch (Exception ex) { Log.LogError((object)("Objective patches failed entirely; construction still works. " + ex)); } ConstructionOpsHost.Install(); Log.LogInfo((object)"ConstructionOps 1.0.0 loaded."); } } public static class Probe { public const int CandidateCrateHash = 1178751813; private static readonly HashSet InterestingComponents = new HashSet { "Factory", "WarheadStorage", "Hangar", "Rearmer", "Repairer", "Radar", "FireControl", "VehicleDepot", "Capture", "PowerSupply", "Transmission", "Building", "Unit", "NetworkIdentity", "Runway", "ArrestorGear" }; public static void DumpAircraft(Aircraft aircraft) { try { StringBuilder stringBuilder = new StringBuilder(8192); stringBuilder.AppendLine(); stringBuilder.AppendLine("=== LOCAL AIRCRAFT WEAPON STATIONS ==="); stringBuilder.AppendLine($"# {DateTime.Now:HH:mm:ss} aircraft={((UnitDefinition)aircraft.definition).unitName}"); stringBuilder.AppendLine(); WeaponManager weaponManager = aircraft.weaponManager; if ((Object)(object)weaponManager == (Object)null) { stringBuilder.AppendLine("no WeaponManager"); } else { stringBuilder.AppendLine("--- hardpoint sets (what is loaded) ---"); if (weaponManager.hardpointSets != null) { for (int i = 0; i < weaponManager.hardpointSets.Length; i++) { HardpointSet val = weaponManager.hardpointSets[i]; if (val == null) { continue; } string text = (((Object)(object)val.weaponMount != (Object)null) ? val.weaponMount.jsonKey : "(empty)"); stringBuilder.AppendLine($" [{i}] {val.name} loaded={text} hardpoints={((val.hardpoints != null) ? val.hardpoints.Count : 0)}"); if (val.weaponOptions == null) { continue; } List list = new List(); foreach (WeaponMount weaponOption in val.weaponOptions) { if ((Object)(object)weaponOption != (Object)null) { list.Add(weaponOption.jsonKey + (weaponOption.Cargo ? "*" : "")); } } stringBuilder.AppendLine(" options: " + string.Join(", ", list.ToArray())); } } stringBuilder.AppendLine(); stringBuilder.AppendLine("--- weapon stations (what you can cycle to in flight) ---"); List weaponStations = ((Unit)aircraft).weaponStations; stringBuilder.AppendLine($" count = {weaponStations?.Count ?? 0}"); if (weaponStations != null) { for (int j = 0; j < weaponStations.Count; j++) { WeaponStation val2 = weaponStations[j]; if (val2 == null) { stringBuilder.AppendLine($" [{j}] NULL"); continue; } string arg = (((Object)(object)val2.WeaponInfo != (Object)null) ? val2.WeaponInfo.weaponName : ">>> NULL WeaponInfo <<<"); stringBuilder.AppendLine($" [{j}] Number={val2.Number} Cargo={val2.Cargo} ammo={val2.Ammo} " + $"weapons={((val2.Weapons != null) ? val2.Weapons.Count : 0)} info={arg}" + (((Object)(object)val2.WeaponInfo != (Object)null) ? $" (cargo={val2.WeaponInfo.cargo} troops={val2.WeaponInfo.troops} hideInDisplay={val2.WeaponInfo.hideInDisplay})" : "")); if (val2.Weapons == null) { continue; } foreach (Weapon weapon in val2.Weapons) { if ((Object)(object)weapon != (Object)null) { stringBuilder.AppendLine(string.Format(" weapon: {0} info={1} ammoLoaded={2}", ((object)weapon).GetType().Name, ((Object)(object)weapon.info != (Object)null) ? weapon.info.weaponName : ">>> NULL <<<", weapon.GetAmmoLoaded())); } } } } } File.AppendAllText(Path.Combine(Paths.BepInExRootPath, "ConstructionOps.probe.txt"), stringBuilder.ToString()); Plugin.Log.LogInfo((object)$"[probe] appended aircraft dump ({((((Unit)aircraft).weaponStations != null) ? ((Unit)aircraft).weaponStations.Count : 0)} stations)"); } catch (Exception ex) { Plugin.Log.LogError((object)("[probe] aircraft dump failed: " + ex.GetType().Name + ": " + ex.Message)); } } public static void Run(bool inMission) { try { StringBuilder stringBuilder = new StringBuilder(65536); stringBuilder.AppendLine("# ConstructionOps probe v1.0.0"); stringBuilder.AppendLine($"# {DateTime.Now:yyyy-MM-dd HH:mm:ss} inMission={inMission}"); stringBuilder.AppendLine("# Read-only dump. Sections marked EMPTY were not live at probe time."); stringBuilder.AppendLine(); Encyclopedia i = Encyclopedia.i; DumpAircraftRoster(stringBuilder, i); DumpCargoMounts(stringBuilder, i); DumpBuildings(stringBuilder, i); DumpAirbases(stringBuilder, inMission); DumpPrefabHashes(stringBuilder, i); string text = Path.Combine(Paths.BepInExRootPath, "ConstructionOps.probe.txt"); File.WriteAllText(text, stringBuilder.ToString()); Plugin.Log.LogInfo((object)$"[probe] wrote {stringBuilder.Length} chars to {text}"); } catch (Exception ex) { Plugin.Log.LogError((object)("[probe] failed: " + ex.GetType().Name + ": " + ex.Message + "\n" + ex.StackTrace)); } } private static void DumpAircraftRoster(StringBuilder sb, Encyclopedia enc) { sb.AppendLine("=== AIRCRAFT ROSTER ==="); sb.AppendLine("# Paste a jsonKey into ConstructionCatalogue.asset -> aircraftProduction"); sb.AppendLine("# to give that airframe its own factory cost, build interval, or to"); sb.AppendLine("# switch it off. Anything not listed there uses the rank scaling."); sb.AppendLine("# 'rank' is AircraftDefinition.aircraftParameters.rankRequired — the same"); sb.AppendLine("# number the aircraft selection screen shows as 'Rank: n'."); sb.AppendLine(); if ((Object)(object)enc == (Object)null || enc.aircraft == null) { sb.AppendLine("EMPTY — Encyclopedia.aircraft was null"); sb.AppendLine(); return; } ConstructionCatalogue.BuildOption buildOption = ConstructionCatalogue.ById("factory"); sb.AppendLine(string.Format("{0,-18} {1,-24} {2,4} {3}", "jsonKey", "name", "rank", "factory cost")); foreach (AircraftDefinition item in enc.aircraft) { if ((Object)(object)item == (Object)null) { continue; } int num = ConstructionCatalogue.ProductionRank(((UnitDefinition)item).jsonKey); string text = "n/a"; if (buildOption != null) { List list = ConstructionCatalogue.ResolveManifest(buildOption, ((UnitDefinition)item).jsonKey); List list2 = new List(); foreach (ConstructionCatalogue.MaterialLoad item2 in list) { list2.Add(item2.JsonKey + " x" + item2.Count); } text = ConstructionCatalogue.TotalLoads(list) + " loads (" + string.Join(", ", list2.ToArray()) + ")"; } ConstructionConfig.AircraftProductionEntry aircraftProductionEntry = ConstructionCatalogue.AircraftOverride(((UnitDefinition)item).jsonKey); string text2 = ((aircraftProductionEntry == null) ? "" : (" <- tuned in the asset" + (aircraftProductionEntry.canProduce ? "" : " (DISABLED)"))); sb.AppendLine(string.Format("{0,-18} {1,-24} {2,4} {3}{4}", ((UnitDefinition)item).jsonKey ?? "?", ((UnitDefinition)item).unitName ?? "?", num, text, text2)); } sb.AppendLine(); sb.AppendLine($"# {enc.aircraft.Count} aircraft total"); sb.AppendLine(); } private static void DumpCargoMounts(StringBuilder sb, Encyclopedia enc) { //IL_0313: Unknown result type (might be due to invalid IL or missing references) sb.AppendLine("=== CARGO / TROOP WEAPON MOUNTS ==="); sb.AppendLine("# These are the mounts a cargo pylon accepts: Hardpoint.HardpointPylon.MatchesMount"); sb.AppendLine("# returns true for ANY mount with Cargo||Troops, with no per-aircraft allow-list."); sb.AppendLine("# Bake the MountedCargo values below into the authored bundle asset."); sb.AppendLine(); if (enc?.weaponMounts == null) { sb.AppendLine("EMPTY — Encyclopedia.weaponMounts was null"); sb.AppendLine(); return; } int num = 0; foreach (WeaponMount weaponMount in enc.weaponMounts) { if ((Object)(object)weaponMount == (Object)null || (!weaponMount.Cargo && !weaponMount.Troops)) { continue; } num++; sb.AppendLine("[mount] jsonKey=" + weaponMount.jsonKey + " name=" + weaponMount.mountName); sb.AppendLine($" Cargo={weaponMount.Cargo} Troops={weaponMount.Troops} ammo={weaponMount.ammo}"); sb.AppendLine($" emptyCost={weaponMount.emptyCost} emptyMass={weaponMount.emptyMass} mass={weaponMount.mass}"); sb.AppendLine($" drag={weaponMount.drag} emptyDrag={weaponMount.emptyDrag} RCS={weaponMount.RCS} emptyRCS={weaponMount.emptyRCS}"); WeaponInfo info = weaponMount.info; if ((Object)(object)info != (Object)null) { sb.AppendLine($" info: name={info.weaponName} costPerRound={info.costPerRound} " + $"massPerRound={info.massPerRound} cargo={info.cargo} troops={info.troops}"); } else { sb.AppendLine(" info: NULL"); } GameObject prefab = weaponMount.prefab; if ((Object)(object)prefab == (Object)null) { sb.AppendLine(" prefab: NULL"); sb.AppendLine(); continue; } sb.AppendLine(" prefab: " + ((Object)prefab).name); MountedCargo componentInChildren = prefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { sb.AppendLine(" MountedCargo: none (troop mounts may use a different component)"); sb.AppendLine(" components: " + ComponentList(prefab)); sb.AppendLine(); } else { sb.AppendLine(" MountedCargo.cargo = " + (((Object)(object)componentInChildren.cargo != (Object)null) ? componentInChildren.cargo.jsonKey : "NULL") + (((Object)(object)componentInChildren.cargo != (Object)null) ? ($" (unitName={componentInChildren.cargo.unitName}, mass={componentInChildren.cargo.mass}, " + $"{componentInChildren.cargo.width}x{componentInChildren.cargo.length}x{componentInChildren.cargo.height}, " + $"spawnOffset={componentInChildren.cargo.spawnOffset})") : "")); DumpPrivateFields(sb, componentInChildren, " MountedCargo."); sb.AppendLine(); } } if (num == 0) { sb.AppendLine("EMPTY — no mount had Cargo or Troops set"); } else { sb.AppendLine($"# {num} cargo/troop mounts total"); } sb.AppendLine(); } private static void DumpPrivateFields(StringBuilder sb, object instance, string prefix) { FieldInfo[] fields = instance.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic); Array.Sort(fields, (FieldInfo a, FieldInfo b) => string.CompareOrdinal(a.Name, b.Name)); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { object value; try { value = fieldInfo.GetValue(instance); } catch { continue; } Type fieldType = fieldInfo.FieldType; if (fieldType.IsPrimitive || fieldType.IsEnum || fieldType == typeof(string) || fieldType == typeof(decimal)) { sb.AppendLine($"{prefix}{fieldInfo.Name} = {value}"); continue; } if (fieldType == typeof(Vector3) || fieldType == typeof(Vector2) || fieldType == typeof(Quaternion)) { sb.AppendLine($"{prefix}{fieldInfo.Name} = {value}"); continue; } if (value != null) { Object val = (Object)((value is Object) ? value : null); if (val == null || !(val == (Object)null)) { if (value is ICollection collection) { sb.AppendLine($"{prefix}{fieldInfo.Name} : {fieldType.Name} = {collection.Count} item(s)"); } else { sb.AppendLine($"{prefix}{fieldInfo.Name} : {fieldType.Name} = present ({value})"); } continue; } } sb.AppendLine(prefix + fieldInfo.Name + " : " + fieldType.Name + " = null"); } } private static void DumpBuildings(StringBuilder sb, Encyclopedia enc) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) sb.AppendLine("=== BUILDING DEFINITIONS ==="); sb.AppendLine("# The construction catalogue is built from this. BuildingType is too coarse to"); sb.AppendLine("# tell what a prefab actually does, so the component column is the real answer."); sb.AppendLine("# WarheadStorage is the one to hunt for: without an airbase carrying it,"); sb.AppendLine("# FactionHQ.AddWarheadStockpile discards a nuke plant's entire output."); sb.AppendLine(); if (enc?.buildings == null) { sb.AppendLine("EMPTY — Encyclopedia.buildings was null"); sb.AppendLine(); return; } foreach (BuildingDefinition building in enc.buildings) { if (!((Object)(object)building == (Object)null)) { sb.AppendLine($"[building] jsonKey={((UnitDefinition)building).jsonKey} type={building.buildingType} name={((UnitDefinition)building).unitName}"); sb.AppendLine($" value={((UnitDefinition)building).value} mass={((UnitDefinition)building).mass} size={((UnitDefinition)building).width}x{((UnitDefinition)building).length}x{((UnitDefinition)building).height} " + $"spawnOffset={((UnitDefinition)building).spawnOffset} IsObstacle={((UnitDefinition)building).IsObstacle}"); sb.AppendLine($" capture: strength={((UnitDefinition)building).captureStrength} defense={((UnitDefinition)building).captureDefense} " + $"capacity={((UnitDefinition)building).captureCapacity} manpower={((UnitDefinition)building).manpower}"); sb.AppendLine(" components: " + (((Object)(object)((UnitDefinition)building).unitPrefab != (Object)null) ? ComponentList(((UnitDefinition)building).unitPrefab) : "NO PREFAB")); sb.AppendLine(); } } sb.AppendLine($"# {enc.buildings.Count} building definitions total"); sb.AppendLine(); } private static string ComponentList(GameObject prefab) { List list = new List(); Component[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { string name = ((object)val).GetType().Name; if (InterestingComponents.Contains(name) && !list.Contains(name)) { list.Add(name); } } } list.Sort(StringComparer.Ordinal); if (list.Count <= 0) { return "(none of interest)"; } return string.Join(", ", list.ToArray()); } private static void DumpAirbases(StringBuilder sb, bool inMission) { sb.AppendLine("=== AIRBASES (this mission) ==="); sb.AppendLine("# HasStorage() is literally stores.Count > 0, and stores is filled by"); sb.AppendLine("# Airbase.AddBuilding when a building carries WarheadStorage. A warhead"); sb.AppendLine("# plant is worthless to a faction that owns none of these."); sb.AppendLine(); if (!inMission || FactionRegistry.airbaseLookup == null || FactionRegistry.airbaseLookup.Count == 0) { sb.AppendLine("EMPTY — no mission loaded at probe time. Set Diagnostics/ProbeEveryMission=true"); sb.AppendLine(" and reload to capture this section."); sb.AppendLine(); return; } foreach (KeyValuePair item in FactionRegistry.airbaseLookup) { Airbase value = item.Value; if ((Object)(object)value == (Object)null) { continue; } string text = "neutral"; try { if ((Object)(object)value.CurrentHQ != (Object)null && (Object)(object)value.CurrentHQ.faction != (Object)null) { text = value.CurrentHQ.faction.factionName; } } catch { text = "?"; } bool flag = false; int num = 0; try { flag = value.HasStorage(); num = value.GetWarheads(); } catch { } sb.AppendLine($"[airbase] {item.Key} faction={text} hasStorage={flag} warheads={num}"); } sb.AppendLine(); sb.AppendLine($"# {FactionRegistry.airbaseLookup.Count} airbases total"); sb.AppendLine(); } private static void DumpPrefabHashes(StringBuilder sb, Encyclopedia enc) { sb.AppendLine("=== PREFAB HASHES ==="); sb.AppendLine("# IndexLookup order is the network wire format for definitions. A custom"); sb.AppendLine("# definition must be TAIL-APPENDED so every vanilla index keeps its value,"); sb.AppendLine("# and its prefab hash must be non-zero and unique or RegisterPrefabs throws"); sb.AppendLine("# and aborts ClientStarted."); sb.AppendLine(); if (enc?.IndexLookup == null) { sb.AppendLine("EMPTY — Encyclopedia.IndexLookup was null"); sb.AppendLine(); return; } sb.AppendLine($"IndexLookup.Count = {enc.IndexLookup.Count} (a tail-append lands at this index)"); HashSet hashSet = new HashSet(); int num = 0; int num2 = 0; bool flag = false; foreach (INetworkDefinition item in enc.IndexLookup) { UnitDefinition val = (UnitDefinition)(object)((item is UnitDefinition) ? item : null); if ((Object)(object)val == (Object)null || (Object)(object)val.unitPrefab == (Object)null) { continue; } NetworkIdentity val2 = null; try { val2 = val.unitPrefab.GetComponent(); } catch { } if (!((Object)(object)val2 == (Object)null)) { num++; if (!hashSet.Add(val2.PrefabHash)) { num2++; } if (!(val.jsonKey == "hendev_construction_crate") && val2.PrefabHash == 1178751813) { flag = true; sb.AppendLine(" !! CANDIDATE COLLIDES with " + val.jsonKey); } } } sb.AppendLine($"networked definitions = {num}, distinct hashes = {hashSet.Count}" + ((num2 > 0) ? $", VANILLA DUPLICATES = {num2}" : "")); sb.AppendLine($"candidate crate hash 0x{1178751813:X8} ('FBSE') is " + (flag ? "TAKEN — pick another" : "FREE — safe to use") + ((hashSet.Contains(1178751813) && !flag) ? " (held by our own registered crate)" : "")); sb.AppendLine(); } }