Decompiled source of ForgeOrdnance Frames v1.0.0
ForgeOrdnanceFrames.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using Agents; using BepInEx; using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using Enemies; using ForgeAudio; using ForgeCore; using ForgeOrdnance; using ForgeUI; using ForgeVisual; using GTFO.API; using GameData; using Gear; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using LevelGeneration; using Microsoft.CodeAnalysis; using Player; using SNetwork; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ForgeOrdnanceFrames")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+66ec6b43e1a1603bac84a4b9a226674300b2fc7d")] [assembly: AssemblyProduct("ForgeOrdnanceFrames")] [assembly: AssemblyTitle("ForgeOrdnanceFrames")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsUnmanagedAttribute : Attribute { } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ForgeOrdnanceFrames { public static class CarrySocket { public const float Gap = 0.05f; public static (float X, float Y, float Z) Centre(float backZ, float chestY, float depth) { return (X: 0f, Y: chestY, Z: backZ - 0.05f - depth * 0.5f); } } public enum CockpitPhase : byte { Off, Boarding, Worn, Leaving, Out, Suspended } public sealed class CockpitScope { public CockpitPhase Phase { get; private set; } public bool HideGameHud { get { CockpitPhase phase = Phase; if (phase - 2 <= CockpitPhase.Boarding) { return true; } return false; } } public bool MoveChat => HideGameHud; public bool Cockpit { get { CockpitPhase phase = Phase; if (phase - 2 <= CockpitPhase.Boarding) { return true; } return false; } } public bool Visor { get { CockpitPhase phase = Phase; if (phase - 1 <= CockpitPhase.Worn) { return true; } return false; } } public bool Set(CockpitPhase phase) { if (phase == Phase) { return false; } (bool, bool, bool) valueTuple = (HideGameHud, Cockpit, Visor); Phase = phase; (bool, bool, bool) tuple = valueTuple; bool hideGameHud = HideGameHud; bool cockpit = Cockpit; bool visor = Visor; if (tuple.Item1 == hideGameHud && tuple.Item2 == cockpit) { return tuple.Item3 != visor; } return true; } public static CockpitPhase For(bool piloting, FrameState state, bool inside, bool dark, bool holds) { if (!piloting) { return CockpitPhase.Off; } CockpitPhase cockpitPhase = state switch { FrameState.Entering => (!inside) ? CockpitPhase.Boarding : CockpitPhase.Worn, FrameState.Piloted => CockpitPhase.Worn, FrameState.Exiting => (!inside || dark) ? CockpitPhase.Out : CockpitPhase.Leaving, _ => CockpitPhase.Off, }; bool flag = !holds; if (flag) { bool flag2 = cockpitPhase - 2 <= CockpitPhase.Boarding; flag = flag2; } if (!flag) { return cockpitPhase; } return CockpitPhase.Suspended; } } public sealed class FireClock { public const int MaxPerFrame = 4; public const float PauseSeconds = 0.25f; private float _due; private bool _running; public float Due => _due; public int AfterShot(float now, float interval, out float nextTimer) { if (!(interval > 0f)) { _running = false; nextTimer = now; return 0; } if (!_running || now - _due > 0.25f) { _due = now; } _running = true; int num = 0; _due += interval; while (_due <= now && num < 3) { num++; _due += interval; } if (_due <= now) { _due = now + interval; } nextTimer = _due; return num; } public void Stop() { _running = false; } public static int Simulate(float frameSeconds, float interval, float seconds, out int mostInOneFrame) { FireClock fireClock = new FireClock(); float nextTimer = 0f; int num = 0; mostInOneFrame = 0; int num2 = (int)Math.Round(seconds / frameSeconds); for (int i = 0; i <= num2; i++) { float num3 = (float)i * frameSeconds; if (!(num3 < nextTimer)) { int num4 = fireClock.AfterShot(num3, interval, out nextTimer); num += 1 + num4; mostInOneFrame = Math.Max(mostInOneFrame, 1 + num4); } } return num; } } public enum FrameAlert : byte { None, Failing } public sealed class FrameAlerts { public const float FailingArmor = 0.1f; private bool _seen; private bool _failing; public void Reset() { _seen = false; } public static FrameAlertTone Tone(FrameAlert alert) { return FrameAlertTone.Alert; } public static string Text(FrameAlert alert, FrameWords words) { if (alert != FrameAlert.Failing) { return ""; } return words.Failing; } public static float Seconds(FrameAlert alert) { return 2f; } public FrameAlert Next(FrameRecord r) { FrameSpec spec = r.Spec; bool flag = spec.Armor > 0f && r.Armor / spec.Armor < 0.1f; bool failing = _failing; _failing = flag; if (!_seen) { _seen = true; return FrameAlert.None; } if (!flag || failing) { return FrameAlert.None; } return FrameAlert.Failing; } } public enum FrameAlertTone : byte { Info, Good, Warn, Alert } internal static class FrameBodyRig { internal static readonly PoseLink[] PoseLinks = (PoseLink[])(object)new PoseLink[16] { new PoseLink("pelvis", (BodyBone)0, 1, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("spine", (BodyBone)7, 2, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("chest", (BodyBone)54, 3, -1, (BodyBone?)(BodyBone)8, (BodyBone?)(BodyBone)9, (BodyBone?)(BodyBone)10), new PoseLink("head", (BodyBone)10, -1, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("upperarm.L", (BodyBone)13, 5, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("forearm.L", (BodyBone)15, 6, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("hand.L", (BodyBone)17, -1, 5, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("upperarm.R", (BodyBone)14, 8, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("forearm.R", (BodyBone)16, 9, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("hand.R", (BodyBone)18, -1, 8, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("thigh.L", (BodyBone)1, 11, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("shin.L", (BodyBone)3, 12, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("foot.L", (BodyBone)5, -1, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("thigh.R", (BodyBone)2, 14, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("shin.R", (BodyBone)4, 15, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null), new PoseLink("foot.R", (BodyBone)6, -1, -1, (BodyBone?)null, (BodyBone?)null, (BodyBone?)null) }; internal const int LeftUpperArmLink = 4; internal const int RightUpperArmLink = 7; internal const int LeftHandLink = 6; internal const int RightHandLink = 9; private const int ChestLink = 2; internal static readonly PoseReach[] ArmReach = (PoseReach[])(object)new PoseReach[2] { new PoseReach(4, 5, 6, 2), new PoseReach(7, 8, 9, 2) }; internal static IEnumerable<(string Field, string Node)> MissingNodes(FrameSpec spec, Func<string, bool> has) { PoseLink[] poseLinks = PoseLinks; for (int i = 0; i < poseLinks.Length; i++) { PoseLink val = poseLinks[i]; if (!has(((PoseLink)(ref val)).Model)) { yield return (Field: "Model", Node: ((PoseLink)(ref val)).Model); } } if (spec.Opening != null) { for (int i = 0; i < spec.Opening.Parts.Length; i++) { if (!has(spec.Opening.Parts[i].Node)) { yield return (Field: "Opening.Parts[" + i + "].Node", Node: spec.Opening.Parts[i].Node); } } } for (int i = 0; i < spec.Shell.Length; i++) { if (!has(spec.Shell[i].Bone)) { yield return (Field: "Shell[" + i + "].Bone", Node: spec.Shell[i].Bone); } } if (spec.Rockets != null && !has(spec.Rockets.Launcher.Bone)) { yield return (Field: "Rockets.Launcher.Bone", Node: spec.Rockets.Launcher.Bone); } } internal static WearBone[] ShellBones(IReadOnlyList<ShellBone> shell) { //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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) WearBone[] array = (WearBone[])(object)new WearBone[shell.Count]; for (int i = 0; i < shell.Count; i++) { BodyBone val = Body(shell[i].Body); array[i] = new WearBone(shell[i].Bone, val, !IsArm(val), Measure(val)); } return array; } internal static string[] HingedParts(OpeningSpec? opening) { if (opening == null) { return Array.Empty<string>(); } string[] array = new string[opening.Parts.Length]; for (int i = 0; i < array.Length; i++) { array[i] = opening.Parts[i].Node; } return array; } internal static BodyBone Body(FrameBodyBone bone) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) return Enum.Parse<BodyBone>(bone.ToString()); } private static bool IsArm(BodyBone bone) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 if (bone - 13 <= 5) { return true; } return false; } private static BodyMeasure Measure(BodyBone bone) { //IL_0000: 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_0020: Expected I4, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) switch (bone - 1) { default: if ((int)bone == 19) { goto case 0; } if ((int)bone != 20) { return (BodyMeasure)0; } goto case 1; case 0: case 2: case 4: return (BodyMeasure)1; case 1: case 3: case 5: return (BodyMeasure)2; } } } public static class FrameBootScreen { public const int PanelCount = 10; public const int SeamCount = 16; public const int Groups = 5; public const float ShiftSeconds = 0.4f; public const float SeamHold = 0.12f; public const float SeamFade = 0.16f; public const float DownStep = 0.12f; public const float DownShiftSeconds = 0.3f; public const float DownClear = 0.15f; public const float DownDark = 0.78f; public const float DownClearStart = 1.5799999f; public const float DownSeconds = 1.7299999f; private static readonly float[][] Poly = new float[10][] { new float[12] { 0.25f, 0.76f, 0.75f, 0.76f, 0.757f, 0.62f, 0.645f, 0.38f, 0.355f, 0.38f, 0.243f, 0.62f }, new float[8] { 0.355f, 0.38f, 0.645f, 0.38f, 0.645f, 0.14f, 0.355f, 0.14f }, new float[8] { 0f, 1f, 1f, 1f, 0.75f, 0.76f, 0.25f, 0.76f }, new float[8] { 0.355f, 0.14f, 0.645f, 0.14f, 0.66f, 0f, 0.34f, 0f }, new float[8] { 0f, 0.5f, 0.243f, 0.62f, 0.355f, 0.38f, 0f, 0.12f }, new float[8] { 1f, 0.12f, 0.645f, 0.38f, 0.757f, 0.62f, 1f, 0.5f }, new float[8] { 0f, 1f, 0.25f, 0.76f, 0.243f, 0.62f, 0f, 0.5f }, new float[8] { 1f, 0.5f, 0.757f, 0.62f, 0.75f, 0.76f, 1f, 1f }, new float[10] { 0f, 0.12f, 0.355f, 0.38f, 0.355f, 0.14f, 0.34f, 0f, 0f, 0f }, new float[10] { 1f, 0f, 0.66f, 0f, 0.645f, 0.14f, 0.645f, 0.38f, 1f, 0.12f } }; private static readonly float[][] Seams = new float[16][] { new float[4] { 0.25f, 0.76f, 0.75f, 0.76f }, new float[4] { 0.75f, 0.76f, 0.757f, 0.62f }, new float[4] { 0.757f, 0.62f, 0.645f, 0.38f }, new float[4] { 0.645f, 0.38f, 0.645f, 0.14f }, new float[4] { 0.645f, 0.14f, 0.355f, 0.14f }, new float[4] { 0.355f, 0.14f, 0.355f, 0.38f }, new float[4] { 0.355f, 0.38f, 0.243f, 0.62f }, new float[4] { 0.243f, 0.62f, 0.25f, 0.76f }, new float[4] { 0.25f, 0.76f, 0f, 1f }, new float[4] { 0.75f, 0.76f, 1f, 1f }, new float[4] { 0.243f, 0.62f, 0f, 0.5f }, new float[4] { 0.757f, 0.62f, 1f, 0.5f }, new float[4] { 0.355f, 0.38f, 0f, 0.12f }, new float[4] { 0.645f, 0.38f, 1f, 0.12f }, new float[4] { 0.355f, 0.14f, 0.34f, 0f }, new float[4] { 0.645f, 0.14f, 0.66f, 0f } }; private static readonly int[] Group = new int[10] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 }; public static int VertexCount(int p) { return Poly[p].Length / 2; } public static float X(int p, int k) { return Poly[p][k * 2]; } public static float Y(int p, int k) { return Poly[p][k * 2 + 1]; } public static int GroupOf(int p) { return Group[p]; } public static void Seam(int i, out float x0, out float y0, out float x1, out float y1) { float[] array = Seams[i]; x0 = array[0]; y0 = array[1]; x1 = array[2]; y1 = array[3]; } public static PowerOnTimeline Timeline(float[] starts) { //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_0011: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown int[] array = Group; PowerOnRhythm val = new PowerOnRhythm(); val.set_Starts(starts); val.set_ShiftSeconds(0.4f); val.set_SeamHold(0.12f); val.set_SeamFade(0.16f); val.set_DownStep(0.12f); val.set_DownShiftSeconds(0.3f); val.set_DownClearStart(1.5799999f); val.set_DownClear(0.15f); return new PowerOnTimeline(array, val); } } public sealed class BootPlan { public static readonly float[] DefaultStarts = new float[5] { 0.2f, 0.36f, 0.56f, 0.74f, 0.95f }; public static readonly float[] DefaultReports = new float[7] { 0.48f, 0.86f, 1.17f, 1.26f, 1.08f, 0.94f, 2.01f }; public const float MessageStart = 0.85f; public const float LinkLock = 2.35f; public const float Duration = 3.0500002f; public static readonly BootPlan Default = new BootPlan(DefaultStarts, DefaultReports); private readonly float[] _reports; public PowerOnTimeline Timeline { get; } public float ControlsAt { get; } public float Assembled => Timeline.Assembled; public float LinkSteady => 2.35f; public float Settled => 3.0500002f; private BootPlan(float[] starts, float[] reports) { _reports = reports; Timeline = FrameBootScreen.Timeline(starts); float val = 0f; foreach (float val2 in starts) { val = Math.Max(val, val2); } ControlsAt = val; } public static float MessageAt(float boot) { return boot - 0.85f; } public float ToneAt(int group) { return Timeline.GroupStart(group); } public float ReportAt(CockpitPart part) { return _reports[(uint)part]; } public bool Shown(CockpitPart part, float s) { return s >= ReportAt(part); } } public static class FrameBootMessage { public static readonly BootMessageBeats Beats; public static readonly HudBox Keepout; public static readonly HudColour StatusGreen; public static readonly BootMessageLook Look; private static HudBox ArcKeepout() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) HudBox val = GaugeMath.ArcExtent((ArcSide)0, 96f, 72f, 3.2f, 44f, 0f); HudBox val2 = GaugeMath.ArcExtent((ArcSide)1, 96f, 72f, 3.2f, 44f, 90f); return new HudBox(Math.Min(((HudBox)(ref val)).Left, ((HudBox)(ref val2)).Left), Math.Min(((HudBox)(ref val)).Bottom, ((HudBox)(ref val2)).Bottom), Math.Max(((HudBox)(ref val)).Right, ((HudBox)(ref val2)).Right), Math.Max(((HudBox)(ref val)).Top, ((HudBox)(ref val2)).Top)); } public static BootMessageWords Words(FrameWords words, int armorPercent) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown BootMessageWords val = new BootMessageWords(); val.set_Header(words.LinkHeader); val.set_Stall(words.BootSync); val.set_Ok(words.BootOk); val.set_Message(words.Link); val.set_Down(words.Shutdown); val.set_Checks(new string[5] { words.BootSeal, FrameWords.Format(words.BootArmor, armorPercent), words.BootServos, words.BootWeapon, words.BootSensors }); return val; } static FrameBootMessage() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: 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) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Expected O, but got Unknown BootMessageBeats val = new BootMessageBeats(); val.set_IgniteEnd(0.12f); val.set_BracketsStart(0.04f); val.set_BracketsEnd(0.2f); val.set_HeaderStart(0.08f); val.set_HeaderEnd(0.32f); val.set_FillTimes(new float[5] { 0.15f, 0.5f, 0.75f, 1.15f, 1.3f }); val.set_FillValues(new float[5] { 0f, 0.35f, 0.48f, 0.92f, 1f }); val.set_StallStart(0.5f); val.set_StallEnd(0.78f); val.set_BarFlash(0.08f); val.set_CheckStarts(new float[5] { 0.42f, 0.58f, 0.74f, 0.9f, 1.06f }); val.set_CheckType(0.1f); val.set_CheckOk(0.12f); val.set_CheckStamp(0.08f); val.set_ChecksFade(1.6f); val.set_ChecksGone(1.8f); val.set_MessageStart(1.32f); val.set_MessageLock(1.4999999f); val.set_LockFlash(0.07f); val.set_LockBurst(0.25f); val.set_LockShake(0.12f); val.set_FoldStart(1.9f); val.set_Duration(2.2000003f); val.set_DownStart(0.06f); val.set_DownDecoded(0.28f); val.set_DownEnd(0.78f); Beats = val; Keepout = ArcKeepout(); StatusGreen = new HudColour(0.3f, 0.95f, 0.48f, 1f); BootMessageLook val2 = new BootMessageLook(); val2.set_Accent(new HudColour(0.8f, 0.94f, 1f, 1f)); val2.set_Bright(new HudColour(0.957f, 0.965f, 0.957f, 1f)); val2.set_White(new HudColour(1f, 1f, 1f, 1f)); val2.set_SplitLeft(new HudColour(1f, 0.29f, 0.23f, 1f)); val2.set_SplitRight(new HudColour(0.25f, 0.85f, 1f, 1f)); val2.set_Stall(new HudColour(1f, 0.56f, 0.17f, 1f)); val2.set_Backing(new HudColour(0.016f, 0.03f, 0.047f, 0.62f)); val2.set_Down(new HudColour(1f, 0.25f, 0.2f, 1f)); val2.set_MessagePx(50f); val2.set_HeaderPx(16f); val2.set_CheckPx(16f); val2.set_CheckRow(20f); val2.set_BarWidth(420f); val2.set_Cells(24); val2.set_CellHeight(6f); val2.set_CellGap(3f); val2.set_HeaderY(18f); val2.set_CheckBase(44f); val2.set_LineY(80f); val2.set_MessageLift(86f); val2.set_Status((HudColour?)StatusGreen); val2.set_Keepout((HudBox?)Keepout); val2.set_DownPx(64f); val2.set_Width(500f); val2.set_Height(320f); Look = val2; } } public enum CockpitPart : byte { Bars, Objectives, Skills, Teammates, Weapon, Radar, Arcs } public static class CockpitParts { public const int Count = 7; public static int Order(CockpitPart part) { return (int)part; } public static int DownGroup(CockpitPart part) { switch (part) { case CockpitPart.Bars: return 1; case CockpitPart.Objectives: return 3; case CockpitPart.Skills: case CockpitPart.Teammates: case CockpitPart.Weapon: return 4; default: return 0; } } public static bool ShownLeaving(CockpitPart part, float d) { float num = BootPlan.Default.Timeline.DownGroup(DownGroup(part), d); if (!(num < 0.15f)) { if (num >= 0.3f) { return num < 0.55f; } return false; } return true; } } internal sealed class FrameCheckpoint { private List<FrameCheckpointMessage> _entries = new List<FrameCheckpointMessage>(); private List<FrameCheckpointMessage>? _incoming; private int _expected; private uint _incomingRevision; internal int Count => _entries.Count; internal uint Revision { get; private set; } internal bool IsComplete { get; private set; } internal void Capture(IEnumerable<FrameRecord> records, float now) { Revision++; IsComplete = true; CancelIncoming(); _entries.Clear(); foreach (FrameRecord record in records) { if (record.State != FrameState.Destroyed) { FrameStateMessage state = FrameStateMessage.From(record, now, FrameEjection.None); state.State = 1; state.Pilot = 0uL; state.Flags &= 2; state.BoostRemaining = (state.StateRemaining = 0f); state.SiegeDeployRemaining = 0f; _entries.Add(new FrameCheckpointMessage { Kind = 2, Revision = Revision, Thrower = record.Thrower, ThrowId = record.ThrowId, State = state }); } } } internal IEnumerable<FrameCheckpointMessage> Messages() { if (!IsComplete) { yield break; } yield return new FrameCheckpointMessage { Kind = 1, Revision = Revision, Count = Count }; foreach (FrameCheckpointMessage entry in _entries) { yield return entry; } yield return new FrameCheckpointMessage { Kind = 3, Revision = Revision }; } internal void Receive(FrameCheckpointMessage message) { switch (message.Kind) { case 1: if (message.Revision >= Revision) { IsComplete = false; Revision = message.Revision; _incomingRevision = message.Revision; _expected = message.Count; _incoming = ((message.Count >= 0) ? new List<FrameCheckpointMessage>() : null); } break; case 2: if (_incoming != null && message.Revision == _incomingRevision) { if (_incoming.Count >= _expected || _incoming.Exists((FrameCheckpointMessage e) => e.Thrower == message.Thrower && e.ThrowId == message.ThrowId)) { _incoming = null; } else { _incoming.Add(message); } } break; case 3: if (message.Revision == _incomingRevision) { if (_incoming != null && _incoming.Count == _expected) { _entries = _incoming; Revision = _incomingRevision; IsComplete = true; } _incoming = null; } break; } } internal List<FrameRecord> Restore(IReadOnlyDictionary<ulong, FrameSpec> specs, float now, uint sequence) { if (!IsComplete) { throw new InvalidOperationException("Cannot restore exosuits: the checkpoint snapshot is incomplete; awaiting resynchronization."); } List<FrameRecord> list = new List<FrameRecord>(); foreach (FrameCheckpointMessage entry in _entries) { sequence = Math.Max(sequence, entry.State.Sequence); } foreach (FrameCheckpointMessage entry2 in _entries) { if (!specs.TryGetValue(entry2.State.Spec, out FrameSpec value)) { throw new InvalidOperationException("Cannot restore exosuit " + entry2.ThrowId + ": checkpoint model " + entry2.State.Spec + " is unavailable."); } FrameRecord frameRecord = new FrameRecord(entry2.Thrower, entry2.ThrowId, value); FrameStateMessage state = entry2.State; if (!state.ApplyTo(frameRecord, now)) { throw new InvalidOperationException("Invalid checkpoint state for exosuit " + entry2.ThrowId + "."); } frameRecord.Sequence = ++sequence; list.Add(frameRecord); } return list; } internal void CancelIncoming() { _incoming = null; } internal void Invalidate() { IsComplete = false; CancelIncoming(); } internal void Clear() { _entries.Clear(); Revision = 0u; Invalidate(); } } public static class FrameEffectsPlan { public const float ThrustFrom = 0.12f; public const float KickFrom = 0.92f; public const float ChargeJet = 0.6f; public const float ChargeInterval = 0.06f; public const int ChargeDustEvery = 2; public const float WreckFadeSeconds = 3f; public const float WreckSinkSeconds = 2f; public const float WreckFlameInterval = 0.16f; public const float WreckSmokeInterval = 0.14f; public const float WreckSmokeLead = 1.5f; public const float ExplosionLightSeconds = 0.45f; public const float HeatFrom = 0.6f; public const float RocketTailFlamesPerSecond = 22.222221f; public const float RocketTailSmokePerSecond = 11.111111f; public const float PulseGap = 0.75f; public static float ArrivalThrust(float fall) { float num = (fall - 0.12f) / 0.88f; if (!(num <= 0f)) { if (!(num >= 1f)) { return num * num; } return 1f; } return 0f; } public static float JetSpeed(float strength) { return 10f + 10f * strength; } public static float JetSize(float strength) { return 0.45f + 0.5f * strength; } public static float JetLife(float strength) { return 0.11f + 0.06f * strength; } public static float WreckFire(float age, float life) { if (age < 0f || age >= life) { return 0f; } float num = (age - (life - 3f)) / 3f; if (!(num <= 0f)) { return 1f - ((num >= 1f) ? 1f : num); } return 1f; } public static float WreckSink(float age, float life) { float num = (age - (life - 2f)) / 2f; if (!(num <= 0f)) { if (!(num >= 1f)) { return num; } return 1f; } return 0f; } public static bool WreckSmoking(float age, float life) { if (age >= 0f) { return age < life - 1.5f; } return false; } public static float ExplosionFlash(float age) { if (!(age <= 0f)) { if (!(age >= 0.45f)) { return 1f - age / 0.45f; } return 0f; } return 1f; } public static float HeatLevel(float heat, bool overheated) { if (overheated) { return 1f; } float num = (heat - 0.6f) / 0.39999998f; if (!(num <= 0f)) { if (!(num >= 1f)) { return num; } return 1f; } return 0f; } public static float HeatInterval(float level) { return 0.34f - 0.22f * level; } } public enum GlowLook : byte { Off, Standby, StandbyDamaged, Normal, Damaged, Overheat, Opening } public static class FrameGlow { public const float DamagedShare = 0.25f; public const float Strength = 1.35f; public const float StripLevel = 0.8f; public const float StandbyLow = 0.12f; public const float StandbyHigh = 0.22f; public const float BreathSeconds = 3.3333333f; public const float PulseLow = 0.3f; public const float PulseHz = 2f; public const float BootSeconds = 0.9f; public const float WreckFlickerSeconds = 0.5f; public const float DamageFlashSeconds = 0.15f; public static readonly (float R, float G, float B) DamagedColour = (R: 1f, G: 0.06f, B: 0.08f); public static readonly (float R, float G, float B) OverheatColour = (R: 1f, G: 0.3f, B: 0.02f); public const float OpeningWhite = 0.25f; public static GlowLook Look(FrameRecord r, bool overheated) { bool flag = r.Armor <= r.Spec.Armor * 0.25f; switch (r.State) { case FrameState.Destroyed: return GlowLook.Off; case FrameState.Entering: case FrameState.Exiting: return GlowLook.Opening; case FrameState.Piloted: return overheated ? GlowLook.Overheat : (flag ? GlowLook.Damaged : GlowLook.Normal); default: return (!flag) ? GlowLook.Standby : GlowLook.StandbyDamaged; } } public static bool Moving(GlowLook look, GlowLook from, float age) { switch (look) { case GlowLook.Standby: case GlowLook.StandbyDamaged: case GlowLook.Overheat: return true; case GlowLook.Normal: return from == GlowLook.Opening && age < 0.9f; case GlowLook.Damaged: return Flashes(from) && age < 0.15f; case GlowLook.Off: return from != GlowLook.Off && age < 0.5f; default: return false; } } public static (float R, float G, float B) Colour(LightSpec light, GlowLook look, GlowLook from, float age, float now) { (float, float, float) tuple = (light.GlowR, light.GlowG, light.GlowB); switch (look) { case GlowLook.Off: { if (from == GlowLook.Off || age >= 0.5f) { return (R: 0f, G: 0f, B: 0f); } bool flag = (int)(age * 24f) % 3 != 1; return Scale(Hue(light, from), flag ? (1f - age / 0.5f * 0.5f) : 0.05f); } case GlowLook.Standby: return Scale(tuple, Breath(now)); case GlowLook.StandbyDamaged: return Scale(DamagedColour, Breath(now)); case GlowLook.Overheat: return Scale(OverheatColour, 0.3f + 0.7f * (0.5f + 0.5f * MathF.Cos((float)Math.PI * 4f * age))); case GlowLook.Opening: return Scale(Toward(tuple, (R: 1f, G: 1f, B: 1f), 0.25f), 1f); case GlowLook.Damaged: return Scale((Flashes(from) && age < 0.15f) ? Toward(DamagedColour, (R: 1f, G: 1f, B: 1f), 0.45f) : DamagedColour, 1f); default: return Scale(tuple, (from == GlowLook.Opening) ? Boot(age) : 1f); } } public static float Boot(float age) { if (age >= 0.9f) { return 1f; } if ((age >= 0.25f && age < 0.33f) || (age >= 0.45f && age < 0.53f)) { return 1f; } if (age < 0.6f) { return 0.04f; } return 0.04f + 0.96f * (age - 0.6f) / 0.29999995f; } public static float Breath(float now) { return 0.12f + 0.1f * (0.5f + 0.5f * MathF.Sin((float)Math.PI * 2f * now / 3.3333333f)); } private static bool Flashes(GlowLook from) { return from == GlowLook.Normal; } private static (float R, float G, float B) Hue(LightSpec light, GlowLook look) { switch (look) { case GlowLook.StandbyDamaged: case GlowLook.Damaged: return DamagedColour; case GlowLook.Overheat: return OverheatColour; default: return (R: light.GlowR, G: light.GlowG, B: light.GlowB); } } private static (float R, float G, float B) Toward((float R, float G, float B) a, (float R, float G, float B) b, float t) { return (R: a.R + (b.R - a.R) * t, G: a.G + (b.G - a.G) * t, B: a.B + (b.B - a.B) * t); } private static (float R, float G, float B) Scale((float R, float G, float B) c, float k) { float num = Math.Max(c.R, Math.Max(c.G, c.B)); float num2 = ((num > 0f) ? (1.35f * k / num) : 0f); return (R: c.R * num2, G: c.G * num2, B: c.B * num2); } } public readonly record struct FrameGunReading(bool Lent, int Clip, int Reserve, bool Reloading, float ReloadProgress = -1f); public enum FrameSpecialKind : byte { None, Charge, Siege, Scan } public enum BarrierState : byte { None, Up, Standby, Broken } public sealed class FrameHudModel { public const int Segments = 10; public const float LowArmor = 0.25f; public const float WarnArmor = 0.4f; public const float ArmorEase = 7f; public const float ScanLit = 0.4f; public FrameWords Words { get; set; } public float ArmorFraction { get; private set; } public BarrierState Barrier { get; private set; } public float BarrierFraction { get; private set; } public bool BarrierShown { get { BarrierState barrier = Barrier; if (barrier == BarrierState.Up || barrier == BarrierState.Broken) { return true; } return false; } } public int BarrierPoints { get; private set; } public float BarrierRecovery { get; private set; } = -1f; public float Heat { get; private set; } public bool Overheated { get; private set; } public int Clip { get; private set; } public int Reserve { get; private set; } public int StockCapacity { get; private set; } public float ReloadProgress { get; private set; } = -1f; public FrameSpecialKind Special { get; private set; } public string SpecialName { get; private set; } = ""; public float SpecialRemaining { get; private set; } public float SpecialCooldown { get; private set; } public bool SpecialActive { get; private set; } public float SpecialProgress { get; private set; } = -1f; public bool HasRockets { get; private set; } public float RocketRemaining { get; private set; } public float RocketCooldown { get; private set; } public FrameHudModel(FrameWords words) { Words = words; } public void Update(FrameRecord r, FrameGunReading gun, float heat, bool overheated, float now, float lastScanAt, float rocketsReadyAt) { FrameSpec spec = r.Spec; FrameWords words = Words; ArmorFraction = ((spec.Armor > 0f) ? Math.Clamp(r.Armor / spec.Armor, 0f, 1f) : 0f); ShieldSpec shield = spec.Shield; BarrierState barrierState = (Barrier = ((!(shield == null)) ? ((shield.WhileSieged && !r.Sieged) ? BarrierState.Standby : (r.ShieldUp ? BarrierState.Up : BarrierState.Broken)) : BarrierState.None)); BarrierFraction = ((shield != null && r.ShieldUp) ? Math.Clamp(r.Shield / Math.Max(0.001f, shield.Capacity), 0f, 1f) : 0f); BarrierPoints = (int)MathF.Ceiling(BarrierFraction * (shield?.Capacity ?? 0f) - 0.001f); BarrierRecovery = -1f; if (barrierState == BarrierState.Broken) { float num = now - r.ShieldLastHit; BarrierRecovery = ((num < shield.RegenDelay) ? (0.5f * ((shield.RegenDelay > 0f) ? Math.Clamp(num / shield.RegenDelay, 0f, 1f) : 1f)) : (0.5f + 0.5f * Math.Clamp(r.Shield / Math.Max(0.001f, shield.Capacity * shield.RaiseFraction), 0f, 1f))); } Heat = Math.Clamp(heat, 0f, 1f); Overheated = overheated; Clip = (gun.Lent ? gun.Clip : r.Magazine); Reserve = (gun.Lent ? gun.Reserve : r.Reserve); StockCapacity = spec.Weapon.Magazine + spec.Weapon.Reserve; ReloadProgress = ((gun.Lent && gun.Reloading) ? gun.ReloadProgress : (-1f)); SpecialProgress = -1f; SpecialRemaining = 0f; SpecialCooldown = 0f; if (spec.Boost != null) { Special = FrameSpecialKind.Charge; SpecialName = words.Charge; SpecialActive = r.Boosting(now); SpecialCooldown = spec.Boost.Cooldown; SpecialRemaining = (SpecialActive ? 0f : Math.Max(0f, r.BoostReadyAt - now)); } else if (spec.Siege != null) { Special = FrameSpecialKind.Siege; SpecialName = words.Siege; SpecialActive = r.Sieged; if (r.SiegeDeployEndsAt > 0f) { SpecialProgress = ((spec.Siege.DeploySeconds > 0f) ? Math.Clamp(1f - (r.SiegeDeployEndsAt - now) / spec.Siege.DeploySeconds, 0f, 1f) : 1f); } } else if (spec.Scan != null) { Special = FrameSpecialKind.Scan; SpecialName = words.Scan; float num2 = now - lastScanAt; SpecialActive = num2 >= 0f && num2 < 0.4f; } else { Special = FrameSpecialKind.None; SpecialName = ""; SpecialActive = false; } HasRockets = spec.Rockets != null; RocketCooldown = spec.Rockets?.Cooldown ?? 0f; RocketRemaining = (HasRockets ? Math.Max(0f, rocketsReadyAt - now) : 0f); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FrameStateMessage { public uint Sequence; public byte State; public byte Flags; public ulong Pilot; public float Armor; public float Shield; public int Magazine; public int Reserve; public float X; public float Y; public float Z; public float Yaw; public float StateRemaining; public float BoostRemaining; public byte Ejection; public ulong Spec; public uint Definition; public ulong Authority; public float BoostCooldownRemaining; public float RocketsCooldownRemaining; public float ShieldRegenRemaining; public float SiegeDeployRemaining; public const byte FlagSieged = 1; public const byte FlagShieldUp = 2; public static FrameStateMessage From(FrameRecord f, float now, FrameEjection ejection) { return new FrameStateMessage { Sequence = f.Sequence, State = (byte)f.State, Flags = (byte)((f.Sieged ? 1u : 0u) | (uint)(f.ShieldUp ? 2 : 0)), Pilot = f.Pilot, Armor = f.Armor, Shield = f.Shield, Magazine = f.Magazine, Reserve = f.Reserve, X = f.X, Y = f.Y, Z = f.Z, Yaw = f.Yaw, StateRemaining = Math.Max(0f, f.StateEndsAt - now), BoostRemaining = Math.Max(0f, f.BoostEndsAt - now), Ejection = (byte)ejection, Spec = f.SpecKey, Definition = f.DefinitionKey, Authority = f.Authority, BoostCooldownRemaining = Math.Max(0f, f.BoostReadyAt - now), RocketsCooldownRemaining = Math.Max(0f, f.RocketsReadyAt - now), ShieldRegenRemaining = Math.Max(0f, (f.Spec.Shield?.RegenDelay ?? 0f) - (now - f.ShieldLastHit)), SiegeDeployRemaining = Math.Max(0f, f.SiegeDeployEndsAt - now) }; } public bool ApplyTo(FrameRecord f, float now) { if (Authority == f.Authority && (int)(Sequence - f.Sequence) <= 0 && f.Sequence != 0) { return false; } if (!Enum.IsDefined(typeof(FrameState), State)) { return false; } f.Sequence = Sequence; f.State = (FrameState)State; f.Sieged = (Flags & 1) != 0; f.ShieldUp = (Flags & 2) != 0; f.Pilot = Pilot; f.Armor = Armor; f.Shield = Shield; f.Magazine = Magazine; f.Reserve = Reserve; f.X = X; f.Y = Y; f.Z = Z; f.Yaw = Yaw; f.StateEndsAt = now + StateRemaining; f.BoostEndsAt = ((BoostRemaining > 0f) ? (now + BoostRemaining) : 0f); f.Authority = Authority; f.DefinitionKey = Definition; f.BoostReadyAt = now + BoostCooldownRemaining; f.RocketsReadyAt = now + RocketsCooldownRemaining; f.ShieldLastHit = now + ShieldRegenRemaining - (f.Spec.Shield?.RegenDelay ?? 0f); f.SiegeDeployEndsAt = ((SiegeDeployRemaining > 0f) ? (now + SiegeDeployRemaining) : 0f); return true; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FrameRequestMessage { public ulong Thrower; public uint ThrowId; public byte Kind; public int Magazine; public int Reserve; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FrameRejectMessage { public ulong Thrower; public uint ThrowId; public byte Kind; public byte Reason; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FrameFireMessage { public ulong Thrower; public uint ThrowId; public float OriginX; public float OriginY; public float OriginZ; public float TargetX; public float TargetY; public float TargetZ; public byte Shots; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FrameRocketMessage { public ulong Thrower; public uint ThrowId; public float TargetX; public float TargetY; public float TargetZ; public ushort Seed; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FrameHitMessage { public ulong Thrower; public uint ThrowId; public float SourceX; public float SourceZ; public float Damage; public byte Shielded; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FrameCheckpointMessage { public const byte Request = 0; public const byte Begin = 1; public const byte Entry = 2; public const byte Commit = 3; public byte Kind; public int Count; public uint Revision; public ulong Thrower; public uint ThrowId; public FrameStateMessage State; } public static class FrameMessages { public const string RequestEvent = "Request"; public const string RejectEvent = "Reject"; public const string FireEvent = "Fire"; public const string RocketRequestEvent = "RocketRequest"; public const string RocketLaunchEvent = "RocketLaunch"; public const string CheckpointEvent = "Checkpoint"; public const string HitEvent = "Hit"; public const byte StateTag = 1; public unsafe static byte[] ToBytes<T>(T message) where T : unmanaged { byte[] array = new byte[sizeof(T)]; MemoryMarshal.Write(array, in message); return array; } public unsafe static bool TryRead<T>(byte[] bytes, out T message) where T : unmanaged { message = default(T); if (bytes == null || bytes.Length != sizeof(T)) { return false; } message = MemoryMarshal.Read<T>(bytes); return true; } } public static class FrameNoise { public const float PilotMultiplier = 2f; public static float LandingRadius(float defaultMovementDetectionDistance) { return defaultMovementDetectionDistance * 2f; } } internal sealed class FrameRocketFlights<T> where T : struct { private struct Slot { internal bool Live; internal T Value; } private const int InitialCapacity = 32; private Slot[] _slots = new Slot[32]; internal int Capacity => _slots.Length; internal int Count { get; private set; } internal int Add(T value) { for (int i = 0; i < _slots.Length; i++) { if (!_slots[i].Live) { return Activate(i, value); } } int num = _slots.Length; Array.Resize(ref _slots, checked(num * 2)); return Activate(num, value); } internal bool IsLive(int index) { return _slots[index].Live; } internal ref T At(int index) { return ref _slots[index].Value; } internal bool Settle(int index, bool currentHost) { if (!_slots[index].Live) { return false; } _slots[index].Live = false; Count--; return currentHost; } internal void Reset() { if (_slots.Length == 32) { Array.Clear(_slots, 0, _slots.Length); } else { _slots = new Slot[32]; } Count = 0; } private int Activate(int index, T value) { _slots[index].Value = value; _slots[index].Live = true; Count++; return index; } } public static class FrameRocketRack { public const string MuzzlePrefix = "muzzle_"; public const string FirstMuzzle = "muzzle_1"; public static int Tube(int rocket, int tubes) { return rocket % tubes; } public static bool Loaded(int tube, int tubes, int salvo, float cooldown, float salvoAt, float now) { if (tubes < 1 || salvo < 1) { throw new ArgumentOutOfRangeException("salvo", "A launcher has at least one tube and a salvo at least one rocket."); } if (float.IsNegativeInfinity(salvoAt)) { return true; } float num = float.PositiveInfinity; float num2 = float.NegativeInfinity; for (int i = 0; i < salvo; i++) { if (Tube(i, tubes) == tube) { num = Math.Min(num, salvoAt + (float)i * 0.12f); num2 = Math.Max(num2, salvoAt + cooldown * (float)(i + 1) / (float)salvo); } } if (!float.IsPositiveInfinity(num) && !(now < num)) { return now >= num2; } return true; } } public enum FrameState : byte { Arriving, Idle, Entering, Piloted, Exiting, Destroyed } public enum FrameRequestKind : byte { Enter = 1, Exit, Ammo, Boost, Siege, Rockets } public enum FrameRejection : byte { None, NotIdle, TooFar, AlreadyPiloting, PilotDown, NotPilot, NoSpecial, Cooling, Planted, Charging, Seized } public enum FrameEjection : byte { None, Destroyed, PilotDown, PilotLeft, PilotTaken } public sealed class FrameRecord { public uint DefinitionKey; public FrameState State; public ulong Pilot; public float Armor; public float Shield; public bool ShieldUp; public float ShieldLastHit = float.NegativeInfinity; public int Magazine; public int Reserve; public float X; public float Y; public float Z; public float Yaw; public float StateEndsAt; public float BoostEndsAt; public float BoostReadyAt; public float RocketsReadyAt; public bool Sieged; public float SiegeDeployEndsAt; public ulong Authority; public uint Sequence; public ulong Thrower { get; } public ulong SpecKey { get; } public uint ThrowId { get; } public FrameSpec Spec { get; } public bool HasPilot { get { FrameState state = State; if (state - 2 <= FrameState.Entering) { return true; } return false; } } public FrameRecord(ulong thrower, uint throwId, FrameSpec spec) { Thrower = thrower; ThrowId = throwId; Spec = spec; SpecKey = FrameSpecKey.Of(spec); Armor = spec.Armor; Shield = spec.Shield?.Capacity ?? 0f; ShieldUp = spec.Shield != null; Magazine = spec.Weapon.Magazine; Reserve = spec.Weapon.Reserve; } public bool Boosting(float now) { return now < BoostEndsAt; } } public static class FrameRules { public const float BoardDistance = 0.9f; public const float BoardSpot = 0.85f; public const float WreckSeconds = 12f; public const float TetherReach = 3f; public const float RocketInterval = 0.12f; public static FrameRejection CanEnter(FrameRecord frame, float distance, bool alreadyPiloting, bool pilotDown) { if (frame.State != FrameState.Idle) { return FrameRejection.NotIdle; } if (pilotDown) { return FrameRejection.PilotDown; } if (alreadyPiloting) { return FrameRejection.AlreadyPiloting; } if (!(distance <= 0.9f)) { return FrameRejection.TooFar; } return FrameRejection.None; } public static void Enter(FrameRecord frame, ulong pilot, float now) { frame.State = FrameState.Entering; frame.Pilot = pilot; frame.StateEndsAt = now + FrameSequence.EnterSeconds; frame.Sieged = false; frame.SiegeDeployEndsAt = 0f; frame.Sequence++; } public static FrameRejection CanExit(FrameRecord frame, ulong sender) { if (frame.State == FrameState.Piloted) { if (frame.Pilot == sender) { return FrameRejection.None; } return FrameRejection.NotPilot; } return FrameRejection.NotIdle; } public static void Exit(FrameRecord frame, float now, float x, float y, float z, float yaw) { frame.State = FrameState.Exiting; frame.StateEndsAt = now + 1.6466666f; frame.BoostEndsAt = 0f; frame.Sieged = false; frame.SiegeDeployEndsAt = 0f; Place(frame, x, y, z, yaw); } public static void Eject(FrameRecord frame, float x, float y, float z, float yaw) { frame.State = FrameState.Idle; frame.Pilot = 0uL; frame.BoostEndsAt = 0f; frame.Sieged = false; frame.SiegeDeployEndsAt = 0f; Place(frame, x, y, z, yaw); } public static bool Interrupted(FrameRecord frame, bool seized, float x, float y, float z) { FrameState state = frame.State; if ((state != FrameState.Entering && state != FrameState.Exiting) || 1 == 0) { return false; } float num = x - frame.X; float num2 = y - frame.Y; float num3 = z - frame.Z; if (!seized) { return !(num * num + num2 * num2 + num3 * num3 <= 9f); } return true; } public static void Place(FrameRecord frame, float x, float y, float z, float yaw) { frame.X = x; frame.Y = y; frame.Z = z; frame.Yaw = yaw; frame.Sequence++; } public static float Absorbed(FrameRecord frame, float damage) { float num = 1f - frame.Spec.DamageReduction; if (frame.Sieged && frame.Spec.Siege != null) { num *= 1f - frame.Spec.Siege.DamageReduction; } return damage * num; } public static bool Damage(FrameRecord frame, float damage, float now) { FrameState state = frame.State; if (state - 2 > FrameState.Entering) { return false; } if (!(damage > 0f)) { return false; } frame.Armor = Math.Max(0f, frame.Armor - Absorbed(frame, damage)); frame.Sequence++; if (frame.Armor > 0f) { return false; } frame.State = FrameState.Destroyed; frame.Pilot = 0uL; frame.StateEndsAt = now + 12f; frame.BoostEndsAt = 0f; frame.Sieged = false; frame.SiegeDeployEndsAt = 0f; return true; } public static FrameRejection CanBoost(FrameRecord frame, ulong sender, float now) { if (frame.State != FrameState.Piloted || frame.Pilot != sender) { return FrameRejection.NotPilot; } if (frame.Spec.Boost == null) { return FrameRejection.NoSpecial; } if (frame.Sieged || frame.SiegeDeployEndsAt > 0f) { return FrameRejection.Planted; } if (frame.Boosting(now)) { return FrameRejection.Charging; } if (now < frame.BoostReadyAt) { return FrameRejection.Cooling; } return FrameRejection.None; } public static void Boost(FrameRecord frame, float now) { BoostSpec boost = frame.Spec.Boost; frame.BoostEndsAt = now + boost.Seconds; frame.BoostReadyAt = now + boost.Seconds + boost.Cooldown; frame.Sequence++; } public static FrameRejection Rockets(FrameRecord frame, ulong sender, float now) { if (frame.State != FrameState.Piloted || frame.Pilot != sender) { return FrameRejection.NotPilot; } if (frame.Spec.Rockets == null) { return FrameRejection.NoSpecial; } if (now < frame.RocketsReadyAt) { return FrameRejection.Cooling; } frame.RocketsReadyAt = now + frame.Spec.Rockets.Cooldown; frame.Sequence++; return FrameRejection.None; } public static void RocketShot(ushort seed, int index, float spread, out float dx, out float dz, out float delay) { uint num = (uint)((seed * -1640531535) ^ ((index + 1) * -2048144777)); num ^= num >> 15; num *= 2654435761u; num ^= num >> 13; float x = (float)(num & 0xFFFF) / 65536f * 2f * (float)Math.PI; float num2 = spread * MathF.Sqrt((float)(num >> 16) / 65536f); dx = num2 * MathF.Cos(x); dz = num2 * MathF.Sin(x); delay = (float)index * 0.12f; } public static FrameRejection CanSiege(FrameRecord frame, ulong sender) { if (frame.State == FrameState.Piloted && frame.Pilot == sender) { if (!(frame.Spec.Siege == null)) { return FrameRejection.None; } return FrameRejection.NoSpecial; } return FrameRejection.NotPilot; } public static bool ScanTags(EnemyAlert alert) { //IL_0000: 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_0004: Invalid comparison between Unknown and I4 if (alert - 2 <= 1) { return true; } return false; } public static bool BarrierUp(FrameRecord frame) { ShieldSpec shield = frame.Spec.Shield; if (shield != null && frame.ShieldUp && frame.State == FrameState.Piloted) { if (shield.WhileSieged) { return frame.Sieged; } return true; } return false; } public static bool ShieldHit(FrameRecord frame, float damage, float now) { if (!BarrierUp(frame) || !(damage > 0f)) { return false; } frame.ShieldLastHit = now; frame.Shield = Math.Max(0f, frame.Shield - damage); if (frame.Shield <= 0f) { frame.ShieldUp = false; } frame.Sequence++; return !frame.ShieldUp; } public static bool Shelters(FrameRecord frame, float px, float py, float pz, float tx, float ty, float tz, float sx, float sy, float sz) { if (!BarrierUp(frame)) { return false; } float num = frame.Spec.Shield.Radius * frame.Spec.Shield.Radius; float num2 = tx - px; float num3 = ty - (py + 1.2f); float num4 = tz - pz; float num5 = sx - px; float num6 = sy - (py + 1.2f); float num7 = sz - pz; if (num2 * num2 + num3 * num3 + num4 * num4 <= num) { return num5 * num5 + num6 * num6 + num7 * num7 > num; } return false; } public static bool Repels(FrameRecord frame, float x, float y, float z) { if (BarrierUp(frame)) { return Distance2(frame, x, y, z) <= frame.Spec.Shield.Radius * frame.Spec.Shield.Radius; } return false; } private static float Distance2(FrameRecord frame, float x, float y, float z) { float num = x - frame.X; float num2 = y - (frame.Y + 1.2f); float num3 = z - frame.Z; return num * num + num2 * num2 + num3 * num3; } public static bool RegenShield(FrameRecord frame, float now, float dt) { ShieldSpec shield = frame.Spec.Shield; if (shield == null || frame.Shield >= shield.Capacity || now - frame.ShieldLastHit < shield.RegenDelay || !(dt > 0f)) { return false; } frame.Shield = Math.Min(shield.Capacity, frame.Shield + shield.RegenRate * dt); if (!frame.ShieldUp && frame.Shield >= shield.Capacity * shield.RaiseFraction) { frame.ShieldUp = true; } frame.Sequence++; return true; } public static void ToggleSiege(FrameRecord frame, float now) { if (frame.Sieged || frame.SiegeDeployEndsAt > 0f) { frame.Sieged = false; frame.SiegeDeployEndsAt = 0f; } else { float deploySeconds = frame.Spec.Siege.DeploySeconds; if (deploySeconds == 0f) { frame.Sieged = true; } else { frame.SiegeDeployEndsAt = now + deploySeconds; } frame.BoostEndsAt = 0f; } frame.Sequence++; } public static bool Tick(FrameRecord frame, float now, out bool remove) { remove = false; if (frame.State == FrameState.Piloted && frame.SiegeDeployEndsAt > 0f && now >= frame.SiegeDeployEndsAt) { frame.SiegeDeployEndsAt = 0f; frame.Sieged = true; frame.Sequence++; return true; } switch (frame.State) { case FrameState.Arriving: if (now >= frame.StateEndsAt) { frame.State = FrameState.Idle; break; } goto default; case FrameState.Entering: if (now >= frame.StateEndsAt) { frame.State = FrameState.Piloted; break; } goto default; case FrameState.Exiting: if (now >= frame.StateEndsAt) { frame.State = FrameState.Idle; frame.Pilot = 0uL; break; } goto default; case FrameState.Destroyed: if (now >= frame.StateEndsAt) { remove = true; return false; } goto default; default: return false; } frame.Sequence++; return true; } public static bool ReportAmmo(FrameRecord frame, ulong sender, int magazine, int reserve) { if (!frame.HasPilot || frame.Pilot != sender) { return false; } magazine = Math.Clamp(magazine, 0, frame.Spec.Weapon.Magazine); reserve = Math.Clamp(reserve, 0, frame.Spec.Weapon.Reserve); if (magazine + reserve > frame.Magazine + frame.Reserve) { return false; } if (magazine == frame.Magazine && reserve == frame.Reserve) { return false; } frame.Magazine = magazine; frame.Reserve = reserve; frame.Sequence++; return true; } public static bool CarryBlockReleased(bool localPlayersInventory, bool piloting, bool carriesHeavy) { return localPlayersInventory && piloting && carriesHeavy; } public static float SpeedMultiplier(FrameRecord frame, float now) { if (!frame.Sieged && !(frame.SiegeDeployEndsAt > 0f)) { return frame.Spec.Movement.Walk * ((frame.Boosting(now) && frame.Spec.Boost != null) ? frame.Spec.Boost.SpeedMultiplier : 1f); } return 0f; } } public sealed class SprintHeat { public const float Rearm = 0.35f; public float Value { get; private set; } public bool Overheated { get; private set; } public void Reset() { Value = 0f; Overheated = false; } public bool Step(MovementSpec spec, bool sprinting, float dt) { if (!(dt > 0f)) { if (sprinting) { return !Overheated; } return false; } if (sprinting && !Overheated) { Value = Math.Min(1f, Value + dt / spec.SprintSeconds); } else { Value = Math.Max(0f, Value - dt / spec.SprintRecovery); } if (Value >= 1f) { Overheated = true; } else if (Overheated && Value <= 0.35f) { Overheated = false; } if (sprinting) { return !Overheated; } return false; } } public static class FrameSequence { public const float Black = 1.9f; public const float FadeSeconds = 0.3f; public const float CloseStart = 1.9f; public const float CockpitBack = 0.3f; public const float ExitClearance = 1f; public const float OpenBy = 1.9f; public const float LandSettleSeconds = 1.5f; public const float NearClip = 0.02f; public const float HandBackSeconds = 0.25f; public const float ExitOpenStart = 0f; public const float ExitClipStart = 0.78f; public const float ExitSeconds = 1.6466666f; public const float CloseAfterLand = 0.2f; public static readonly float EnterSeconds = 1.9f + BootPlan.Default.ControlsAt; private const float EjectOpenStart = 0.05f; private const float EjectOpenBy = 0.31f; private const float EjectCloseStart = 0.6f; private const float EjectCloseBy = 0.98f; public static float Smooth(float t) { t = Math.Clamp(t, 0f, 1f); return t * t * (3f - 2f * t); } public static float Play(float elapsed, float start, float by, float length) { if (length <= 0f) { return 0f; } float num = Math.Max(1f, length / Math.Max(0.001f, by - start)); return Math.Clamp((elapsed - start) * num, 0f, length); } public static float Ends(float start, float by, float length) { return start + Math.Min(Math.Max(0f, length), Math.Max(0.001f, by - start)); } private static PlatePose Plates(float elapsed, float openStart, float openBy, float closeStart, float closeBy, float openLength, float closeLength) { if (!(elapsed < closeStart)) { return new PlatePose(Closing: true, Play(elapsed, closeStart, closeBy, closeLength)); } return new PlatePose(Closing: false, Play(elapsed, openStart, openBy, openLength)); } public static PlatePose EnterPlates(float elapsed, float openLength, float closeLength) { return Plates(elapsed, 0f, 1.9f, 1.9f, EnterSeconds, openLength, closeLength); } public static bool ControlsOpen(float elapsed) { return elapsed >= EnterSeconds; } public static float EnterShut(float closeLength) { return Ends(1.9f, EnterSeconds, closeLength); } public static PlatePose ExitPlates(float elapsed, float openLength) { return new PlatePose(Closing: false, Play(elapsed, 0f, 0.78f, openLength)); } public static PlatePose? ClosePlates(float sinceLanding, float openLength, float closeLength) { if (sinceLanding < 0.2f) { return new PlatePose(Closing: false, openLength); } float num = sinceLanding - 0.2f; if (!(num >= closeLength)) { return new PlatePose(Closing: true, num); } return null; } public static PlatePose EjectPlates(float t, float seconds, float openLength, float closeLength) { return Plates(t * seconds, 0.05f * seconds, 0.31f * seconds, 0.6f * seconds, 0.98f * seconds, openLength, closeLength); } public static bool PilotShownEntering(float elapsed) { return elapsed < 1.9f; } public static bool PilotShownExiting(float elapsed) { return elapsed >= 0.78f; } public static float EnterFade(float elapsed) { return Math.Clamp((elapsed - 1.5999999f) / 0.3f, 0f, 1f); } public static void Ahead(float x, float z, float yawDegrees, float ahead, out float ax, out float az) { float x2 = yawDegrees * (float)Math.PI / 180f; ax = x + MathF.Sin(x2) * ahead; az = z + MathF.Cos(x2) * ahead; } } public readonly record struct PlatePose(bool Closing, float Time); public enum FrameSound { Footstep, Boot, VoiceReady, Servo, Alarm, Destroyed, Boost, ShieldUp, ShieldHit, ShieldDown, Rocket, PlateSlide, HatchClose, HudSection, Unseal, PowerDown, Jump, ReloadStart, ReloadDone, DryFire, BoostEnd, SiegeOn, SiegeOff, BreachHit, ShoveHit, ArmorHit, HoldComplete, HoldCancel, Land, RocketBlast, SiegeBolt, Landing, SiegeDeploy } public static class FrameSounds { public readonly record struct Entry(FrameSound Sound, string Cue); public static readonly Entry[] Table = new Entry[33] { new Entry(FrameSound.Footstep, "exo.footstep"), new Entry(FrameSound.Boot, "exo.visor_boot"), new Entry(FrameSound.VoiceReady, "exo.voice_ready"), new Entry(FrameSound.Servo, "exo.servo_loop"), new Entry(FrameSound.Alarm, "exo.damage_alarm"), new Entry(FrameSound.Destroyed, "exo.destroyed"), new Entry(FrameSound.Boost, "exo.boost"), new Entry(FrameSound.ShieldUp, "exo.shield_up"), new Entry(FrameSound.ShieldHit, "exo.shield_hit"), new Entry(FrameSound.ShieldDown, "exo.shield_down"), new Entry(FrameSound.Rocket, "exo.rocket"), new Entry(FrameSound.PlateSlide, "exo.plate_slide"), new Entry(FrameSound.HatchClose, "exo.hatch_close"), new Entry(FrameSound.HudSection, "exo.hud_section"), new Entry(FrameSound.Unseal, "exo.unseal"), new Entry(FrameSound.PowerDown, "exo.power_down"), new Entry(FrameSound.Jump, "exo.jump"), new Entry(FrameSound.ReloadStart, "exo.reload_start"), new Entry(FrameSound.ReloadDone, "exo.reload_done"), new Entry(FrameSound.DryFire, "exo.dry_fire"), new Entry(FrameSound.BoostEnd, "exo.boost_end"), new Entry(FrameSound.SiegeOn, "exo.siege_on"), new Entry(FrameSound.SiegeOff, "exo.siege_off"), new Entry(FrameSound.BreachHit, "exo.breach_hit"), new Entry(FrameSound.ShoveHit, "exo.shove_hit"), new Entry(FrameSound.ArmorHit, "exo.armor_hit"), new Entry(FrameSound.HoldComplete, "ui.hold_complete"), new Entry(FrameSound.HoldCancel, "ui.hold_cancel"), new Entry(FrameSound.Land, "exo.land"), new Entry(FrameSound.RocketBlast, "exo.rocket_blast"), new Entry(FrameSound.SiegeBolt, "exo.siege_bolt"), new Entry(FrameSound.Landing, "exo.landing"), new Entry(FrameSound.SiegeDeploy, "exo.siege_deploy") }; public static FrameSound Siege(bool sieged) { if (!sieged) { return FrameSound.SiegeOff; } return FrameSound.SiegeOn; } } public sealed record WeaponSpec(uint Gear, float DamageMultiplier, int Magazine, int Reserve, float RecoilMultiplier, float ReloadMultiplier); public sealed record MovementSpec(float Walk, float Sprint, float Jump, float SprintSeconds, float SprintRecovery); public sealed record OpeningPart(string Node, bool Seam); public sealed record OpeningSpec(OpeningPart[] Parts); public enum FrameBodyBone { Hips, Spine, Chest, UpperChest, Neck, Head, LeftShoulder, LeftUpperArm, LeftLowerArm, LeftHand, RightShoulder, RightUpperArm, RightLowerArm, RightHand, LeftUpperLeg, LeftLowerLeg, LeftFoot, LeftToes, RightUpperLeg, RightLowerLeg, RightFoot, RightToes } public sealed record ShellBone(string Bone, FrameBodyBone Body); public sealed record LightSpec(float RangeMultiplier, float AngleMultiplier, float IntensityMultiplier, float R, float G, float B, float GlowR, float GlowG, float GlowB); public sealed record BoostSpec(float Seconds, float SpeedMultiplier, float Cooldown, bool BreachDoors, float Shove); public sealed record SiegeSpec(float DeploySeconds, float DamageMultiplier, float DamageReduction, float FireRateMultiplier, float ReloadMultiplier, float RecoilMultiplier); public sealed record ShieldSpec(float Capacity, float RegenDelay, float RegenRate, float Radius, float RaiseFraction, bool WhileSieged); public sealed record ScanSpec(float Radius, float Interval); public sealed record RocketsSpec(int Salvo, float Damage, float Radius, float Spread, float Cooldown, float Range, float Speed, RocketLauncherSpec Launcher); public sealed record RocketLauncherSpec(string Pod, string Round, string Bone, float X, float Y, float Z, float Pitch, float Yaw, float Roll); public sealed record FrameSpec(string Model, float Armor, float DamageReduction, float EyeHeight, MovementSpec Movement, WeaponSpec Weapon, LightSpec Light, BoostSpec? Boost, SiegeSpec? Siege, ScanSpec? Scan, ShieldSpec? Shield, bool CarryHeavy, RocketsSpec? Rockets, OpeningSpec? Opening, ShellBone[] Shell); public static class FrameSpecParser { public const string TypeName = "frames.Exosuit"; private static readonly string[] TypeField = new string[1] { "Type" }; private static readonly string[] Fields = new string[15] { "Model", "Armor", "DamageReduction", "EyeHeight", "Movement", "Weapon", "Light", "Boost", "Siege", "Scan", "Shield", "CarryHeavy", "Rockets", "Opening", "Shell" }; private static readonly string[] WeaponFields = new string[6] { "Gear", "DamageMultiplier", "Magazine", "Reserve", "RecoilMultiplier", "ReloadMultiplier" }; private static readonly string[] MovementFields = new string[5] { "Walk", "Sprint", "Jump", "SprintSeconds", "SprintRecovery" }; private static readonly string[] OpeningFields = new string[1] { "Parts" }; private static readonly string[] PartFields = new string[2] { "Node", "Seam" }; private static readonly string[] ShellFields = new string[2] { "Bone", "Body" }; public const int MaxOpeningParts = 64; public const int MaxShellBones = 24; private static readonly string[] LightFields = new string[5] { "RangeMultiplier", "AngleMultiplier", "IntensityMultiplier", "Colour", "Glow" }; private static readonly string[] BoostFields = new string[5] { "Seconds", "SpeedMultiplier", "Cooldown", "BreachDoors", "Shove" }; private static readonly string[] SiegeFields = new string[6] { "DeploySeconds", "DamageMultiplier", "DamageReduction", "FireRateMultiplier", "ReloadMultiplier", "RecoilMultiplier" }; private static readonly string[] ScanFields = new string[2] { "Radius", "Interval" }; private static readonly string[] RocketsFields = new string[8] { "Salvo", "Damage", "Radius", "Spread", "Cooldown", "Range", "Speed", "Launcher" }; private static readonly string[] LauncherFields = new string[5] { "Pod", "Round", "Bone", "Position", "Rotation" }; private static readonly string[] ShieldFields = new string[6] { "Capacity", "RegenDelay", "RegenRate", "Radius", "RaiseFraction", "WhileSieged" }; public static FrameSpec Parse(JsonElement payload) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (payload.ValueKind != JsonValueKind.Object) { throw new StrictJsonException("Payload", "must be an object."); } StrictJson.OnlyKnown(payload, "", Fields, TypeField, (string)null); string model = Asset(payload, "", "Model"); if (payload.TryGetProperty("Boost", out var value) && payload.TryGetProperty("Siege", out value)) { throw new StrictJsonException("Siege", "cannot be combined with Boost: both use the special key."); } if (payload.TryGetProperty("Shield", out var value2) && value2.ValueKind == JsonValueKind.Object && StrictJson.Bool(value2, "Shield.", "WhileSieged", (bool?)false) && !payload.TryGetProperty("Siege", out value)) { throw new StrictJsonException("Shield.WhileSieged", "needs a Siege: the barrier stands while the suit is planted."); } JsonElement value3; JsonElement value4; JsonElement value5; JsonElement value6; JsonElement value7; JsonElement value8; JsonElement value9; JsonElement value10; JsonElement value11; return new FrameSpec(model, (float)StrictJson.Number(payload, "", "Armor", (double?)null, 1.0, 100000.0), (float)StrictJson.Number(payload, "", "DamageReduction", (double?)0.0, 0.0, 0.9), (float)StrictJson.Number(payload, "", "EyeHeight", (double?)2.0, 1.0, 4.0), Movement(payload.TryGetProperty("Movement", out value3) ? value3 : default(JsonElement)), Weapon(StrictJson.Object(payload, "", "Weapon")), Light(payload.TryGetProperty("Light", out value4) ? value4 : default(JsonElement)), payload.TryGetProperty("Boost", out value5) ? Boost(value5) : null, payload.TryGetProperty("Siege", out value6) ? Siege(value6) : null, payload.TryGetProperty("Scan", out value7) ? Scan(value7) : null, payload.TryGetProperty("Shield", out value8) ? Shield(value8) : null, StrictJson.Bool(payload, "", "CarryHeavy", (bool?)false), payload.TryGetProperty("Rockets", out value9) ? Rockets(value9) : null, payload.TryGetProperty("Opening", out value10) ? Opening(value10) : null, payload.TryGetProperty("Shell", out value11) ? Shell(value11) : Array.Empty<ShellBone>()); } private static WeaponSpec Weapon(JsonElement w) { StrictJson.OnlyKnown(w, "Weapon.", WeaponFields, (string[])null, (string)null); return new WeaponSpec((uint)StrictJson.Integer(w, "Weapon.", "Gear", (double?)null, 1.0, 4294967295.0), (float)StrictJson.Number(w, "Weapon.", "DamageMultiplier", (double?)1.0, 0.1, 20.0), (int)StrictJson.Integer(w, "Weapon.", "Magazine", (double?)null, 1.0, 10000.0), (int)StrictJson.Integer(w, "Weapon.", "Reserve", (double?)0.0, 0.0, 100000.0), (float)StrictJson.Number(w, "Weapon.", "RecoilMultiplier", (double?)1.0, 0.05, 1.0), (float)StrictJson.Number(w, "Weapon.", "ReloadMultiplier", (double?)1.0, 0.2, 1.0)); } private static MovementSpec Movement(JsonElement m) { if (m.ValueKind == JsonValueKind.Undefined) { return new MovementSpec(1.1f, 1.2f, 1.3f, 5f, 8f); } StrictJson.OnlyKnown(m, "Movement.", MovementFields, (string[])null, (string)null); return new MovementSpec((float)StrictJson.Number(m, "Movement.", "Walk", (double?)1.1, 1.0, 3.0), (float)StrictJson.Number(m, "Movement.", "Sprint", (double?)1.2, 1.0, 3.0), (float)StrictJson.Number(m, "Movement.", "Jump", (double?)1.3, 1.0, 4.0), (float)StrictJson.Number(m, "Movement.", "SprintSeconds", (double?)5.0, 0.5, 120.0), (float)StrictJson.Number(m, "Movement.", "SprintRecovery", (double?)8.0, 0.5, 120.0)); } private static OpeningSpec Opening(JsonElement o) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) StrictJson.OnlyKnown(o, "Opening.", OpeningFields, (string[])null, (string)null); if (!o.TryGetProperty("Parts", out var value) || value.ValueKind != JsonValueKind.Array) { throw new StrictJsonException("Opening.Parts", "is required and must be an array."); } int arrayLength = value.GetArrayLength(); if (arrayLength < 1 || arrayLength > 64) { throw new StrictJsonException("Opening.Parts", "must list 1.." + 64 + " parts."); } OpeningPart[] array = new OpeningPart[arrayLength]; int num = 0; foreach (JsonElement item in value.EnumerateArray()) { string text = "Opening.Parts[" + num + "]."; StrictJson.OnlyKnown(item, text, PartFields, (string[])null, (string)null); string text2 = Name(item, text, "Node"); for (int i = 0; i < num; i++) { if (array[i].Node == text2) { throw new StrictJsonException(text + "Node", "'" + text2 + "' is listed twice."); } } int num2; if (item.TryGetProperty("Seam", out var value2)) { if (value2.ValueKind != JsonValueKind.True) { if (value2.ValueKind != JsonValueKind.False) { throw new StrictJsonException(text + "Seam", "must be true or false."); } num2 = 0; } else { num2 = 1; } } else { num2 = 0; } bool seam = (byte)num2 != 0; array[num++] = new OpeningPart(text2, seam); } return new OpeningSpec(array); } private static ShellBone[] Shell(JsonElement s) { //IL_0014: 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_0124: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) if (s.ValueKind != JsonValueKind.Array) { throw new StrictJsonException("Shell", "must be an array."); } int arrayLength = s.GetArrayLength(); if (arrayLength < 1 || arrayLength > 24) { throw new StrictJsonException("Shell", "must list 1.." + 24 + " bones."); } ShellBone[] array = new ShellBone[arrayLength]; int num = 0; foreach (JsonElement item in s.EnumerateArray()) { string text = "Shell[" + num + "]."; StrictJson.OnlyKnown(item, text, ShellFields, (string[])null, (string)null); string text2 = Name(item, text, "Bone"); string text3 = Name(item, text, "Body"); if (!Enum.TryParse<FrameBodyBone>(text3, ignoreCase: false, out var result) || !Enum.IsDefined(result) || char.IsDigit(text3[0])) { throw new StrictJsonException(text + "Body", "'" + text3 + "' is not a body bone (known: " + string.Join(", ", Enum.GetNames<FrameBodyBone>()) + ")."); } for (int i = 0; i < num; i++) { if (array[i].Bone == text2) { throw new StrictJsonException(text + "Bone", "'" + text2 + "' is listed twice."); } if (array[i].Body == result) { throw new StrictJsonException(text + "Body", "'" + text3 + "' is listed twice."); } } array[num++] = new ShellBone(text2, result); } return array; } private static string Name(JsonElement o, string prefix, string field) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) JsonElement value; string text = ((o.TryGetProperty(field, out value) && value.ValueKind == JsonValueKind.String) ? (value.GetString() ?? "") : ""); if (text.Length == 0 || text.Length > 64) { throw new StrictJsonException(prefix + field, "is required: a name of 1-64 characters."); } return text; } private static string Asset(JsonElement o, string prefix, string field) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) string text = Name(o, prefix, field); if (!FrameModelName.TrySplit(text, out string _, out string _)) { throw new StrictJsonException(prefix + field, "'" + text + "' must be Pack:item, a prefab of a ForgeVisual pack."); } return text; } private static LightSpec Light(JsonElement l) { if (l.ValueKind == JsonValueKind.Undefined) { l = JsonDocument.Parse("{}").RootElement; } StrictJson.OnlyKnown(l, "Light.", LightFields, (string[])null, (string)null); var (r, g, b) = Colour(l, "Light.", "Colour", "#FFEBD1"); var (glowR, glowG, glowB) = Colour(l, "Light.", "Glow", "#22D3FF"); return new LightSpec((float)StrictJson.Number(l, "Light.", "RangeMultiplier", (double?)2.0, 0.5, 5.0), (float)StrictJson.Number(l, "Light.", "AngleMultiplier", (double?)1.4, 0.5, 3.0), (float)StrictJson.Number(l, "Light.", "IntensityMultiplier", (double?)1.2, 0.2, 5.0), r, g, b, glowR, glowG, glowB); } private static (float, float, float) Colour(JsonElement o, string prefix, string field, string fallback) { //IL_0075: 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) string text = fallback; if (o.TryGetProperty(field, out var value)) { if (value.ValueKind != JsonValueKind.String) { throw new StrictJsonException(prefix + field, "must be a colour \"#RRGGBB\"."); } text = value.GetString() ?? ""; } if (text.Length != 7 || text[0] != '#' || !int.TryParse(text.AsSpan(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { throw new StrictJsonException(prefix + field, "must be a colour \"#RRGGBB\"."); } return ((float)((result >> 16) & 0xFF) / 255f, (float)((result >> 8) & 0xFF) / 255f, (float)(result & 0xFF) / 255f); } private static BoostSpec Boost(JsonElement b) { StrictJson.OnlyKnown(b, "Boost.", BoostFields, (string[])null, (string)null); return new BoostSpec((float)StrictJson.Number(b, "Boost.", "Seconds", (double?)null, 0.2, 30.0), (float)StrictJson.Number(b, "Boost.", "SpeedMultiplier", (double?)null, 1.0, 4.0), (float)StrictJson.Number(b, "Boost.", "Cooldown", (double?)null, 0.0, 600.0), StrictJson.Bool(b, "Boost.", "BreachDoors", (bool?)false), (float)StrictJson.Number(b, "Boost.", "Shove", (double?)0.0, 0.0, 100.0)); } private static SiegeSpec Siege(JsonElement s) { StrictJson.OnlyKnown(s, "Siege.", SiegeFields, (string[])null, (string)null); return new SiegeSpec((float)StrictJson.Number(s, "Siege.", "DeploySeconds", (double?)1.0, 0.0, 10.0), (float)StrictJson.Number(s, "Siege.", "DamageMultiplier", (double?)1.0, 1.0, 5.0), (float)StrictJson.Number(s, "Siege.", "DamageReduction", (double?)0.0, 0.0, 0.9), (float)StrictJson.Number(s, "Siege.", "FireRateMultiplier", (double?)1.0, 1.0, 3.0), (float)StrictJson.Number(s, "Siege.", "ReloadMultiplier", (double?)1.0, 0.2, 1.0), (float)StrictJson.Number(s, "Siege.", "RecoilMultiplier", (double?)1.0, 0.1, 1.0)); } private static ScanSpec Scan(JsonElement s) { StrictJson.OnlyKnown(s, "Scan.", ScanFields, (string[])null, (string)null); return new ScanSpec((float)StrictJson.Number(s, "Scan.", "Radius", (double?)null, 1.0, 100.0), (float)StrictJson.Number(s, "Scan.", "Interval", (double?)5.0, 1.0, 120.0)); } private static ShieldSpec Shield(JsonElement s) { StrictJson.OnlyKnown(s, "Shield.", ShieldFields, (string[])null, (string)null); return new ShieldSpec((float)StrictJson.Number(s, "Shield.", "Capacity", (double?)null, 1.0, 100000.0), (float)StrictJson.Number(s, "Shield.", "RegenDelay", (double?)6.0, 0.0, 120.0), (float)StrictJson.Number(s, "Shield.", "RegenRate", (double?)null, 0.01, 10000.0), (float)StrictJson.Number(s, "Shield.", "Radius", (double?)2.75, 1.0, 6.0), (float)StrictJson.Number(s, "Shield.", "RaiseFraction", (double?)0.5, 0.05, 1.0), StrictJson.Bool(s, "Shield.", "WhileSieged", (bool?)false)); } private static RocketsSpec Rockets(JsonElement s) { //IL_0168: Unknown result type (might be due to invalid IL or missing references) StrictJson.OnlyKnown(s, "Rockets.", RocketsFields, (string[])null, (string)null); int salvo = (int)StrictJson.Integer(s, "Rockets.", "Salvo", (double?)4.0, 1.0, 8.0); float damage = (float)StrictJson.Number(s, "Rockets.", "Damage", (double?)null, 0.1, 1000.0); float radius = (float)StrictJson.Number(s, "Rockets.", "Radius", (double?)4.0, 0.5, 15.0); float spread = (float)StrictJson.Number(s, "Rockets.", "Spread", (double?)1.5, 0.0, 10.0); float cooldown = (float)StrictJson.Number(s, "Rockets.", "Cooldown", (double?)12.0, 1.0, 300.0); float range = (float)StrictJson.Number(s, "Rockets.", "Range", (double?)120.0, 5.0, 300.0); float speed = (float)StrictJson.Number(s, "Rockets.", "Speed", (double?)45.0, 5.0, 200.0); if (!s.TryGetProperty("Launcher", out var value) || value.ValueKind != JsonValueKind.Object) { throw new StrictJsonException("Rockets.Launcher", "is required and must be an object."); } return new RocketsSpec(salvo, damage, radius, spread, cooldown, range, speed, Launcher(value)); } private static RocketLauncherSpec Launcher(JsonElement l) { StrictJson.OnlyKnown(l, "Rockets.Launcher.", LauncherFields, (string[])null, (string)null); float[] array = Vector(l, "Rockets.Launcher.", "Position", 4.0); float[] array2 = Vector(l, "Rockets.Launcher.", "Rotation", 180.0); return new RocketLauncherSpec(Asset(l, "Rockets.Launcher.", "Pod"), Asset(l, "Rockets.Launcher.", "Round"), Name(l, "Rockets.Launcher.", "Bone"), array[0], array[1], array[2], array2[0], array2[1], array2[2]); } private static float[] Vector(JsonElement o, string prefix, string field, double limit) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if (!o.TryGetProperty(field, out var value) || value.ValueKind != JsonValueKind.Array || value.GetArrayLength() != 3) { throw new StrictJsonException(prefix + field, "is required: three numbers."); } float[] array = new float[3]; int num = 0; double num2 = default(double); foreach (JsonElement item in value.EnumerateArray()) { if (!StrictJson.Finite(item, ref num2) || Math.Abs(num2) > limit) { throw new StrictJsonException(prefix + field, "must be three finite numbers within -" + limit.ToString(CultureInfo.InvariantCulture) + ".." + limit.ToString(CultureInfo.InvariantCulture) + "."); } array[num++] = (float)num2; } return array; } } public static class FrameModelName { public static bool TrySplit(string? name, out string pack, out string item) { pack = (item = ""); if (string.IsNullOrEmpty(name)) { return false; } int num = name.IndexOf(':'); if (num <= 0 || num == name.Length - 1 || name.IndexOf(':', num + 1) >= 0) { return false; } pack = name.Substring(0, num); item = name.Substring(num + 1); return true; } public static string MarkIcon(string model) { if (!TrySplit(model, out string pack, out string item)) { throw new ArgumentException("'" + model + "' is not Pack:item.", "model"); } return pack + ":mark-" + item; } } public static class FrameSpecKey { public static ulong Of(FrameSpec s) { ulong h = 14695981039346656037uL; I(s.Model.Length); string model = s.Model; foreach (char num in model) { B((byte)num); B((byte)((int)num >> 8)); } F(s.Armor); F(s.DamageReduction); F(s.EyeHeight); MovementSpec movement = s.Movement; F(movement.Walk); F(movement.Sprint); F(movement.Jump); F(movement.SprintSeconds); F(movement.SprintRecovery); WeaponSpec weapon = s.Weapon; I((int)weapon.Gear); F(weapon.DamageMultiplier); I(weapon.Magazine); I(weapon.Reserve); F(weapon.RecoilMultiplier); F(weapon.ReloadMultiplier); LightSpec light = s.Light; F(light.RangeMultiplier); F(light.AngleMultiplier); F(light.IntensityMultiplier); F(light.R); F(light.G); F(light.B); F(light.GlowR); F(light.GlowG); F(light.GlowB); Z(s.Boost != null); if (s.Boost != null) { F(s.Boost.Seconds); F(s.Boost.SpeedMultiplier); F(s.Boost.Cooldown); Z(s.Boost.BreachDoors); F(s.Boost.Shove); } Z(s.Siege != null); if (s.Siege != null) { F(s.Siege.DeploySeconds); F(s.Siege.DamageMultiplier); F(s.Siege.DamageReduction); F(s.Siege.FireRateMultiplier); F(s.Siege.ReloadMultiplier); F(s.Siege.RecoilMultiplier); } Z(s.Scan != null); if (s.Scan != null) { F(s.Scan.Radius); F(s.Scan.Interval); } Z(s.Shield != null); if (s.Shield != null) { F(s.Shield.Capacity); F(s.Shield.RegenDelay); F(s.Shield.RegenRate); F(s.Shield.Radius); F(s.Shield.RaiseFraction); Z(s.Shield.WhileSieged); } Z(s.CarryHeavy); Z(s.Rockets != null); if (s.Rockets != null) { I(s.Rockets.Salvo); F(s.Rockets.Damage); F(s.Rockets.Radius); F(s.Rockets.Spread); F(s.Rockets.Cooldown); F(s.Rockets.Range); F(s.Rockets.Speed); } if (s.Rockets != null) { RocketLauncherSpec launcher = s.Rockets.Launcher; S(launcher.Pod); S(launcher.Round); S(launcher.Bone); F(launcher.X); F(launcher.Y); F(launcher.Z); F(launcher.Pitch); F(launcher.Yaw); F(launcher.Roll); } Z(s.Opening != null); if (s.Opening != null) { I(s.Opening.Parts.Length); OpeningPart[] parts = s.Opening.Parts; foreach (OpeningPart obj in parts) { S(obj.Node); Z(obj.Seam); } } I(s.Shell.Length); ShellBone[] shell = s.Shell; foreach (ShellBone obj2 in shell) { S(obj2.Bone); I((int)obj2.Body); } return h; void B(byte b) { h = Fnv.Mix64(h, b); } void F(float v) { I(BitConverter.SingleToInt32Bits(v)); } void I(int v) { for (int j = 0; j < 4; j++) { B((byte)(v >> 8 * j)); } } void S(string v) { I(v.Length); foreach (char num2 in v) { B((byte)num2); B((byte)((int)num2 >> 8)); } } void Z(bool v) { B(v ? ((byte)1) : ((byte)0)); } } } public sealed class FrameWords { private readonly Func<string, string> _text; public string Piloting { get; } public string Grabbed { get; } public string Stunned { get; } public string Barrier { get; } public string Pilot { get; } public string Heat { get; } public string Overheat { get; } public string Ready { get; } public string Active { get; } public string On { get; } public string Off { get; } public string Deploying { get; } public string Rockets { get; } public string Lamp { get; } public string Exit { get; } public string CarryTake { get; } public string CarryStow { get; } public string Charge { get; } public string Siege { get; } public string Scan { get; } public string BoardHeader { get; } public string BoardHold { get; } public string BoardBehind { get; } public string BoardArmor { get; } public string Failing { get; } public string Destroyed { get; } public string ForcedOut { get; } public string NotFree { get; } public string TooFar { get; } public string AlreadyIn { get; } public string PilotDown { get; } public string NotReady { get; } public string Planted { get; } public string Charging { get; } public string RocketRange { get; } public string NotNow { get; } public string Link { get; } public string LinkHeader { get; } public string Shutdown { get; } public string BootOk { get; } public string BootSync { get; } public string BootSeal { get; } public string BootArmor { get; } public string BootServos { get; } public string BootWeapon { get; } public string BootSensors { get; } public string Hull { get; } public string LoadoutTitle { get; } public string Squad { get; } public string Behind { get; } public FrameWords(Func<string, string> text) { _text = text ?? throw new ArgumentNullException("text"); Barrier = text("hud.barrier"); Pilot = text("hud.pilot"); Heat = text("hud.heat"); Overheat = text("hud.overheat"); Ready = text("hud.ready"); Active = text("hud.active"); On = text("hud.on"); Off = text("hud.off"); Deploying = text("hud.deploying"); Rockets = text("hud.rockets"); Lamp = text("hud.lamp"); Exit = text("hud.exit"); CarryTake = text("hud.carryTake"); CarryStow = text("hud.carryStow"); Charge = text("special.charge"); Siege = text("special.siege"); Scan = text("special.scan"); BoardHeader = text("board.header"); BoardHold = text("board.hold"); BoardBehind = text("board.behind"); BoardArmor = text("board.armor"); Failing = text("banner.failing"); Destroyed = text("banner.destroyed"); ForcedOut = text("banner.forcedOut"); NotFree = text("refuse.notFree"); TooFar = text("refuse.tooFar"); AlreadyIn = text("refuse.alreadyIn"); PilotDown = text("refuse.pilotDown"); NotReady = text("refuse.notReady"); Planted = text("refuse.planted"); Charging = text("refuse.charging"); RocketRange = text("refuse.range"); NotNow = text("refuse.notNow"); Link = text("boot.link"); BootSeal = text("boot.seal"); BootArmor = text("boot.armor"); BootServos = text("boot.servos"); BootWeapon = text("boot.weapon"); BootSensors = text("boot.sensors"); LinkHeader = text("boot.header"); BootOk = text("boot.ok"); BootSync = text("boot.sync"); Shutdown = text("boot.shutdown"); Hull = text("hud.hull"); LoadoutTitle = text("hud.loadout"); Squad = text("hud.squad"); Behind = text("hud.behind"); Grabbed = text("hud.grabbed"); Stunned = text("hud.stunned"); Piloting = text("slot.piloting"); } public static string Format(string text, object value) { return string.Format(CultureInfo.InvariantCulture, text, value); } } public static class Gait { public const float MinStepGap = 0.2f; public const float StepSeconds = 0.94f; public const int MaxStepsSounding = 5; } public struct StepGate { private float _last; private bool _any; public bool Admit(float now) { if (_any && now - _last < 0.2f && now >= _last) { return false; } _any = true; _last = now; return true; } } public static class GunHandling { public static float Recoil(WeaponSpec weapon, SiegeSpec? siege, bool sieged) { return weapon.RecoilMultiplier * ((sieged && siege != null) ? siege.RecoilMultiplier : 1f); } public static float Reload(WeaponSpec weapon, SiegeSpec? siege, bool sieged) { return weapon.ReloadMultiplier * ((sieged && siege != null) ? siege.ReloadMultiplier : 1f); } public static float Damage(bool suitGun, WeaponSpec weapon, SiegeSpec? siege, bool sieged) { if (!suitGun) { return 1f; } return weapon.DamageMultiplier * ((sieged && siege != null) ? siege.DamageMultiplier : 1f); } public static float FireRate(bool suitGun, SiegeSpec? siege, bool sieged) { if (!(suitGun && sieged) || !(siege != null)) { return 1f; } return siege.FireRateMultiplier; } } public static class PilotMotion { public const float HeadOut = 0.8f; public const float ExitSeconds = 1.6f; public const float Land = 13f / 15f; } public readonly record struct PlateEvent(float At, FrameSound? Sound, bool Steam = false); public static class PlateTimeline { public const float StillMetres = 0.0005f; public const float StillDegrees = 0.2f; public static PlateEvent[] Opening(float[][] metres, float[][] degrees, bool[] seam, float step) { int num = ((metres.Length != 0) ? metres[0].Length : 0); float num2 = float.MaxValue; float num3 = float.MaxValue; List<float> list = new List<float>(); for (int i = 0; i < num; i++) { int num4 = Unlock(metres, degrees, i); if (num4 >= 0) { num2 = Math.Min(num2, (float)num4 * step); if (seam[i]) { num3 = Math.Min(num3, (float)num4 * step); } list.Add((float)num4 * step); } } list.Sort(); List<PlateEvent> list2 = new List<PlateEvent>(); if (num2 < float.MaxValue) { list2.Add(new PlateEvent(num2, FrameSound.Unseal)); } if (num3 < float.MaxValue) { list2.Add(new PlateEvent(num3, null, Steam: true)); } if (list.Count > 0) { list2.Add(new PlateEvent(list[list.Count / 2], FrameSound.PlateSlide)); } return list2.OrderBy((PlateEvent e) => e.At).ToArray(); } public static PlateEvent[] Closing(float[][] metres, float[][] degrees, float step) { int num = ((metres.Length != 0) ? metres[0].Length : 0); float num2 = -1f; for (int i = 0; i < num; i++) { int num3 = -1; for (int j = 1; j < metres.Length; j++) { if (num3 >= 0) { break; } if (!Same(metres, degrees, i, j, 0)) { num3 = j - 1; } } if (num3 < 0) { continue; } for (int k = num3 + 1; k < metres.Length; k++) { if (metres[k][i] <= 0.0005f && degrees[k][i] <= 0.2f) { num2 = Math.Max(num2, (float)k * step); break; } } } if (!(num2 < 0f)) { return new PlateEvent[1] { new PlateEvent(num2, FrameSound.HatchClose) }; } return Array.Empty<PlateEvent>(); } public static int Passed(PlateEvent[] events, float seconds) { int i; for (i = 0; i < events.Length && events[i].At <= seconds; i++) { } return i; } private static int Unlock(float[][] metres, float[][] degrees, int p) { for (int i = 0; i < metres.Length; i++) { if (metres[i][p] > 0.0005f || degrees[i][p] > 0.2f) { return Math.Max(0, i - 1); } } return -1; } private static bool Same(float[][] metres, float[][] degrees, int p, int a, int b) { if (Math.Abs(metres[a][p] - metres[b][p]) <= 0.0005f) { return Math.Abs(degrees[a][p] - degrees[b][p]) <= 0.2f; } return false; } } public enum LoanGun : byte { Own, Suit } public sealed class SuitGunLoan { public const string Owner = "NAinfini.ForgeOrdnanceFrames"; private readonly SpecialSlot _slot; private readonly Func<string> _refusal; private SpecialSlotLease? _lease; public bool Piloting { get; private set; } public LoanGun InSlot { get; private set; } public LoanGun? Pending { get; private set; } public bool SuitSpent { get; private set; } public int SuitClip { get; private set; } public int SuitReserve { get; private set; } public bool Leased { get { SpecialSlotLease lease = _lease; if (lease != null) { return lease.Held; } return false; } } public bool SlotReady { get { SpecialSlotLease lease = _lease; if (lease != null && lease.Held) { return lease.Ready; } return false; } } public OwnSpecialWeapon? Own => _slot.Own; public SuitGunLoan(SpecialSlot slot, Func<string> refusal) { _slot = slot; _refusal = refusal; } public LoanGun? Board(int suitClip, int suitReserve, string? slotGearJson, uint slotChecksum) { if (!Leased) { _lease = _slot.Take("NAinfini.ForgeOrdnanceFrames", _refusal); } if (slotGearJson != null) { _slot.Remember(slotGearJson, slotChecksum); } Piloting = true; SuitClip = suitClip; SuitReserve = suitReserve; SuitSpent = suitClip + suitReserve <= 0; LoanGun? result = Ask(LoanGun.Suit); if (result.HasValue || !_slot.OwnAway || _slot.Own == (OwnSpecialWeapon)null) { return result; } Pending = LoanGun.Own; return LoanGun.Own; } public LoanGun? Toggle() { if (Piloting && !Pending.HasValue) { SpecialSlotLease lease = _lease; if (lease != null && lease.Held && !lease.Suspended) { return Ask((InSlot != LoanGun.Suit) ? LoanGun.Suit : LoanGun.Own); } } return null; } public LoanGun? SuitEmpty() { int suitClip = (SuitReserve = 0); SuitClip = suitClip; SuitSpent = true; if (InSlot != LoanGun.Suit || Pending.HasValue) { return null; } return Ask(LoanGun.Own); } public LoanGun? Leave() { Piloting = false; if (InSlot == LoanGun.Own && !Pending.HasValue) { Release(); return null; } return ReturnOwn(); } public void Withdraw() { Piloting = false; Pending = null; InSlot = LoanGun.Own; Release(); } public LoanGun ReturnOwn() { if (!Leased) { _lease = _slot.Take("NAinfini.ForgeOrdnanceFrames", _refusal); } Piloting = false; Pending = LoanGun.Own; return LoanGun.Own; } public LoanGun? TimedOut() { if (!Pending.HasValue) { return null; } Pending = LoanGun.Own; return LoanGun.Own; } public void Arrived(LoanGun gun) { if (Pending != gun) { return; } InSlot = gun; Pending = null; if (gun == LoanGun.Own) { _slot.OwnReturned(); if (!Piloting) { Release(); } } } public void Lost(bool ownInSlot) { InSlot = LoanGun.Own; Pending = null; if (ownInSlot) { _slot.OwnReturned(); } if (!Piloting) { Release(); } } public void KeepSuit(int clip, int reserve) { SuitClip = clip; SuitReserve = reserve; } public void Clear() { bool piloting = (SuitSpent = false); Piloting = piloting; InSlot = LoanGun.Own; Pending = nul