Decompiled source of ConstructionOps v1.0.0
BepInEx/plugins/ConstructionOps/ConstructionOps.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
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<bool>)(object)aiBuild).Value; public GlobalPosition TargetPosition => ((ValueWrapper<GlobalPosition>)(object)Spot.GlobalPosition).Value; public float TargetRange => Mathf.Max(1f, ((ValueWrapper<float>)(object)Spot.Range).Value); public override bool NeedsFaction => false; public override float CompletePercent { get { int num = Mathf.Max(1, ((ValueWrapper<int>)(object)count).Value); return Mathf.Clamp01((float)CountMatching() / (float)num); } } public IReadOnlyList<ObjectivePosition> 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<int>)(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<GlobalPosition>)(object)Spot.GlobalPosition).SetValue(new GlobalPosition(Saved.Position), (object)this, true); ((ValueWrapper<float>)(object)Spot.Range).SetValue(Mathf.Max(1f, Saved.Range), (object)this, true); ((ValueWrapper<int>)(object)count).SetValue(Mathf.Max(1, Saved.Count), (object)this, true); ((ValueWrapper<bool>)(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<GlobalPosition>)(object)Spot.GlobalPosition).Value; saved.Position = ((GlobalPosition)(ref value)).AsVector3(); Saved.Range = ((ValueWrapper<float>)(object)Spot.Range).Value; Saved.Count = ((ValueWrapper<int>)(object)count).Value; Saved.AiBuild = ((ValueWrapper<bool>)(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<GlobalPosition>)(object)Spot.GlobalPosition).SetValue(((ValueWrapper<GlobalPosition>)(object)constructBuildingObjective.Spot.GlobalPosition).Value, (object)this, true); ((ValueWrapper<float>)(object)Spot.Range).SetValue(((ValueWrapper<float>)(object)constructBuildingObjective.Spot.Range).Value, (object)this, true); ((ValueWrapper<int>)(object)count).SetValue(((ValueWrapper<int>)(object)constructBuildingObjective.count).Value, (object)this, true); ((ValueWrapper<bool>)(object)aiBuild).SetValue(((ValueWrapper<bool>)(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<int>)(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<ConstructionManager.CompletedBuild> completedBuilds = ConstructionManager.CompletedBuilds; if (completedBuilds.Count == 0) { return 0; } GlobalPosition value = ((ValueWrapper<GlobalPosition>)(object)Spot.GlobalPosition).Value; float num = Mathf.Max(1f, ((ValueWrapper<float>)(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<int>)delegate(int index) { if (index >= 0 && index < ids.Count) { buildId = ids[index]; } }); drawer.InstantiateWithParent<Vector3DataField>(drawer.Prefabs.VectorFieldPrefab).Setup("Target Spot", (IValueWrapper<Vector3>)(object)Spot.GlobalPosition); drawer.InstantiateWithParent<FloatDataField>(drawer.Prefabs.FloatFieldPrefab).Setup("Range", (IValueWrapper<float>)(object)Spot.Range, (FloatSettings?)null); drawer.InstantiateWithParent<FloatDataField>(drawer.Prefabs.FloatFieldPrefab).Setup("Count", count, (IntSettings?)null); drawer.Space(8); drawer.InstantiateWithParent<BoolDataField>(drawer.Prefabs.BoolFieldPrefab).Setup("AI will construct this", (IValueWrapper<bool>)(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<int>)(object)count).Value + (((ValueWrapper<bool>)(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<string> ids, out List<string> labels) { ids = new List<string> { "any" }; labels = new List<string> { "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<GameObject>("ConstructionCrate"); CrateDefinition = _bundle.LoadAsset<UnitDefinition>("ConstructionCrateDefinition"); Mount = _bundle.LoadAsset<WeaponMount>("ConstructionMount"); Info = _bundle.LoadAsset<WeaponInfo>("ConstructionInfo"); Config = _bundle.LoadAsset<ConstructionConfig>("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<BuildOption> _resolved; private static ConstructionConfig _source; private static readonly List<BuildOption> Empty = new List<BuildOption>(); 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<BuildOption> Options { get { if (_resolved == null) { Resolve(); } return _resolved ?? Empty; } } public static ConstructionConfig BuiltIn() { ConstructionConfig constructionConfig = ScriptableObject.CreateInstance<ConstructionConfig>(); 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<BuildOption> 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<BuildOption> 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<MaterialLoad> ResolveManifest(BuildOption option, string productionKey) { List<MaterialLoad> list = new List<MaterialLoad>(); 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<MaterialLoad> 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<BuildOption>(); List<string> list = new List<string>(); 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<MaterialLoad> list = new List<MaterialLoad>(); 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<string> list = new List<string>(); 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<string> AuthoredIds() { List<string> list = new List<string>(); 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<ConstructionConfig.BuildEntry> 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<Sortie> _sorties = new List<Sortie>(); private static readonly Dictionary<string, WeaponMount> _mountForMaterial = new Dictionary<string, WeaponMount>(); private static bool _mountsMapped; private static readonly HashSet<string> _warned = new HashSet<string>(); 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<Aircraft> 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<ConstructionSite> 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<ConstructionSite> 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<ConstructionCatalogue.BuildOption> 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<WeaponManager>(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<WeaponManager>(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<Aircraft> 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<MountedCargo>(); 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<Pilot>(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<ConstructionSite> _sites = new List<ConstructionSite>(); private static readonly List<CompletedBuild> _completed = new List<CompletedBuild>(); private static readonly HashSet<PersistentID> _consumed = new HashSet<PersistentID>(); private static int _nextSiteId = 1; private static int _buildCounter; private static bool _dirty; public static IList<ConstructionSite> Sites => _sites; public static IList<CompletedBuild> 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<Unit> 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<Unit> 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<Spawner>.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<Unit> 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<PersistentID, int> contributor in site.Contributors) { num += contributor.Value; } if (num <= 0) { return; } foreach (KeyValuePair<PersistentID, int> 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<CommandMessage>((MessageDelegateWithPlayer<CommandMessage>)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<StateMessage>((MessageDelegateWithPlayer<StateMessage>)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<CommandMessage>.Write = WriteCommand; Reader<CommandMessage>.Read = ReadCommand; Writer<StateMessage>.Write = WriteState; Reader<StateMessage>.Read = ReadState; MessagePacker.RegisterMessage<CommandMessage>(); MessagePacker.RegisterMessage<StateMessage>(); } } 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 = StringE