Decompiled source of ForgeOrdnance Arsenal v1.0.0

ForgeOrdnanceArsenal.dll

Decompiled 2 days ago
using System;
using System.Buffers.Binary;
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 Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ForgeOrdnanceArsenal")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+66ec6b43e1a1603bac84a4b9a226674300b2fc7d")]
[assembly: AssemblyProduct("ForgeOrdnanceArsenal")]
[assembly: AssemblyTitle("ForgeOrdnanceArsenal")]
[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 ForgeOrdnanceArsenal
{
	public interface IReach
	{
		bool Reaches(int index);
	}
	public interface IBlocker
	{
		bool Blocked(float ax, float ay, float az, float bx, float by, float bz);
	}
	public enum RouteStep
	{
		Moving,
		Arrived,
		Blocked
	}
	public sealed class BeamRoute
	{
		public const int Capacity = 48;

		private readonly float[] _x = new float[48];

		private readonly float[] _y = new float[48];

		private readonly float[] _z = new float[48];

		private int _count;

		private int _next;

		public float X;

		public float Y;

		public float Z;

		public float Moved;

		public int Count => _count;

		public BeamRoute(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public void Begin()
		{
			_count = 0;
			_next = 0;
		}

		public bool Add(float x, float y, float z)
		{
			if (_count == 48)
			{
				Begin();
				return false;
			}
			_x[_count] = x;
			_y[_count] = y;
			_z[_count] = z;
			_count++;
			return true;
		}

		public RouteStep Step<T>(float distance, ref T blocker) where T : IBlocker
		{
			Moved = 0f;
			while (distance > 1E-06f)
			{
				if (_next >= _count)
				{
					return RouteStep.Arrived;
				}
				float num = _x[_next] - X;
				float num2 = _z[_next] - Z;
				float num3 = MathF.Sqrt(num * num + num2 * num2);
				if (num3 <= 0.0001f)
				{
					Y = _y[_next];
					_next++;
					continue;
				}
				float num4 = MathF.Min(distance, num3);
				float num5 = num4 / num3;
				float num6 = Y + (_y[_next] - Y) * num5;
				float x = X;
				float y = Y;
				float z = Z;
				float bx = X + num * num5;
				float bz = Z + num2 * num5;
				if (blocker.Blocked(x, y, z, bx, num6, bz))
				{
					return RouteStep.Blocked;
				}
				X += num * num5;
				Y = num6;
				Z += num2 * num5;
				Moved += num4;
				distance -= num4;
				if (num4 >= num3 - 0.0001f)
				{
					_next++;
				}
			}
			if (_next < _count)
			{
				return RouteStep.Moving;
			}
			return RouteStep.Arrived;
		}
	}
	public static class ClusterScatter
	{
		public const int Sparklets = 3;

		public static uint Seed(ulong summoner, uint throwId, float x, float z)
		{
			return (((StrikePlan.Seed(summoner, throwId) ^ (uint)(int)MathF.Round(x * 100f)) * 16777619) ^ (uint)(int)MathF.Round(z * 100f)) * 16777619;
		}

		public static void Offsets(uint seed, int count, float radius, float[] offsets)
		{
			if (count < 0 || offsets.Length < count * 2)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			StrikePlan.Lcg lcg = new StrikePlan.Lcg(seed);
			float num = lcg.Next() * (float)Math.PI * 2f;
			for (int i = 0; i < count; i++)
			{
				float x = num + (float)i * (float)Math.PI * 2f / (float)count + (lcg.Next() - 0.5f) * 0.7f;
				float num2 = radius * (0.5f + 0.5f * lcg.Next());
				offsets[2 * i] = MathF.Cos(x) * num2;
				offsets[2 * i + 1] = MathF.Sin(x) * num2;
			}
		}
	}
	public static class HeavyModels
	{
		public const string MarkerPrefix = "Forge Heavy ";

		private static readonly Dictionary<uint, string> Known = new Dictionary<uint, string>();

		private static readonly Dictionary<string, (uint Hash, string Id)> ByMarker = new Dictionary<string, (uint, string)>(StringComparer.Ordinal);

		public static uint Hash(string id)
		{
			return Fnv.Hash32(id);
		}

		public static string Marker(uint hash)
		{
			return "Forge Heavy " + hash.ToString("x8", CultureInfo.InvariantCulture);
		}

		public static bool TryMarker(string? name, out uint hash)
		{
			hash = 0u;
			if (name == null || !ByMarker.TryGetValue(name, out (uint, string) value))
			{
				return false;
			}
			(hash, _) = value;
			return true;
		}

		public static bool TryMarkerId(string? name, out string id)
		{
			id = "";
			if (name == null || !ByMarker.TryGetValue(name, out (uint, string) value))
			{
				return false;
			}
			id = value.Item2;
			return true;
		}

		public static bool IsId(string? id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return false;
			}
			int num = id.IndexOf(':');
			if (num > 0 && num < id.Length - 1 && id.IndexOf(':', num + 1) < 0)
			{
				return id.IndexOf(' ') < 0;
			}
			return false;
		}

		public static bool TryId(uint hash, out string id)
		{
			return Known.TryGetValue(hash, out id);
		}

		public static string? Register(string id)
		{
			uint num = Hash(id);
			if (Known.TryAdd(num, id))
			{
				ByMarker[Marker(num)] = (num, id);
				return null;
			}
			if (Known[num] == id)
			{
				return null;
			}
			return "its hash " + num.ToString("x8", CultureInfo.InvariantCulture) + " is the same as the hash of the model " + Known[num] + " (" + id + " is refused; rename one of them).";
		}

		public static void Clear()
		{
			Known.Clear();
			ByMarker.Clear();
		}
	}
	public sealed record GrenadeSpec(float Damage, float Radius, float Speed)
	{
		internal bool Valid
		{
			get
			{
				if (Damage >= 0f && (double)Damage <= 20000.0 && (double)Radius >= 0.5 && (double)Radius <= 20.0 && (double)Speed >= 5.0)
				{
					return (double)Speed <= 150.0;
				}
				return false;
			}
		}

		internal const double DamageMax = 20000.0;

		internal const double RadiusMin = 0.5;

		internal const double RadiusMax = 20.0;

		internal const double SpeedMin = 5.0;

		internal const double SpeedMax = 150.0;

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			RuntimeHelpers.EnsureSufficientExecutionStack();
			builder.Append("Damage = ");
			builder.Append(Damage.ToString());
			builder.Append(", Radius = ");
			builder.Append(Radius.ToString());
			builder.Append(", Speed = ");
			builder.Append(Speed.ToString());
			return true;
		}

		[CompilerGenerated]
		private GrenadeSpec(GrenadeSpec original)
		{
			Damage = original.Damage;
			Radius = original.Radius;
			Speed = original.Speed;
		}
	}
	public sealed class HeavyWeaponSpec
	{
		public const string TypeName = "arsenal.HeavyWeapon";

		private static readonly string[] Known = Concat(new string[4] { "Model", "Gear", "AmmoRatio", "Grenade" }, ArchetypeTuning.Names);

		private static readonly string[] GrenadeFields = new string[3] { "Damage", "Radius", "Speed" };

		private static readonly string[] TypeField = new string[1] { "Type" };

		public string Model { get; private set; } = "";

		public uint ModelHash { get; private set; }

		public string Marker { get; private set; } = "";

		public uint? GearId { get; private set; }

		public string? GearName { get; private set; }

		public float AmmoRatio { get; private set; }

		public GrenadeSpec? Grenade { get; private set; }

		public ArchetypeTuning Tuning { get; private set; } = ArchetypeTuning.None;

		public static HeavyWeaponSpec Parse(JsonElement payload)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (payload.ValueKind != JsonValueKind.Object)
			{
				throw new StrictJsonException("Payload", "must be an object.");
			}
			StrictJson.OnlyKnown(payload, "", Known, TypeField, "a heavy weapon");
			if (!payload.TryGetProperty("Model", out var value) || value.ValueKind != JsonValueKind.String || !HeavyModels.IsId(value.GetString()))
			{
				throw new StrictJsonException("Model", "is required: a ForgeVisual model id written Pack:item.");
			}
			HeavyWeaponSpec heavyWeaponSpec = WithModel(value.GetString());
			HeavyWeaponSpec heavyWeaponSpec2 = heavyWeaponSpec;
			(uint?, string) tuple = StrictJson.Reference(payload, "", "Gear");
			heavyWeaponSpec2.GearId = tuple.Item1;
			heavyWeaponSpec.GearName = tuple.Item2;
			heavyWeaponSpec.AmmoRatio = (float)StrictJson.Number(payload, "", "AmmoRatio", (double?)1.0, 0.05, 1.0);
			heavyWeaponSpec.Tuning = ArchetypeTuning.Parse(payload);
			if (payload.TryGetProperty("Grenade", out var value2))
			{
				StrictJson.OnlyKnown(value2, "Grenade.", GrenadeFields, (string[])null, (string)null);
				heavyWeaponSpec.Grenade = new GrenadeSpec((float)StrictJson.Number(value2, "Grenade.", "Damage", (double?)140.0, 0.0, 20000.0), (float)StrictJson.Number(value2, "Grenade.", "Radius", (double?)4.0, 0.5, 20.0), (float)StrictJson.Number(value2, "Grenade.", "Speed", (double?)32.0, 5.0, 150.0));
			}
			return heavyWeaponSpec;
		}

		public static HeavyWeaponSpec? Restore(string model, uint gearId, float ammoRatio, GrenadeSpec? grenade, ArchetypeTuning? tuning)
		{
			if (!HeavyModels.IsId(model) || gearId == 0 || !(ammoRatio >= 0.05f) || !(ammoRatio <= 1f) || tuning == null || ((object)grenade != null && !grenade.Valid))
			{
				return null;
			}
			HeavyWeaponSpec heavyWeaponSpec = WithModel(model);
			heavyWeaponSpec.GearId = gearId;
			heavyWeaponSpec.AmmoRatio = ammoRatio;
			heavyWeaponSpec.Grenade = grenade;
			heavyWeaponSpec.Tuning = tuning;
			return heavyWeaponSpec;
		}

		private static HeavyWeaponSpec WithModel(string model)
		{
			uint num = HeavyModels.Hash(model);
			return new HeavyWeaponSpec
			{
				Model = model,
				ModelHash = num,
				Marker = HeavyModels.Marker(num)
			};
		}

		private static string[] Concat(string[] a, string[] b)
		{
			string[] array = new string[a.Length + b.Length];
			a.CopyTo(array, 0);
			b.CopyTo(array, a.Length);
			return array;
		}
	}
	public sealed class ArchetypeTuning
	{
		public const int Count = 12;

		private static readonly (string Name, double Min, double Max, bool Whole)[] Table = new(string, double, double, bool)[12]
		{
			("Damage", 0.01, 20000.0, false),
			("ShotDelay", 0.02, 10.0, false),
			("Cooldown", 0.0, 30.0, false),
			("Chargeup", 0.0, 10.0, false),
			("Magazine", 1.0, 1000.0, true),
			("Reload", 0.1, 30.0, false),
			("PrecisionMultiplier", 0.0, 10.0, false),
			("StaggerMultiplier", 0.0, 20.0, false),
			("Pierce", 0.0, 20.0, true),
			("AmmoCost", 0.01, 1000.0, false),
			("FalloffStart", 0.0, 1000.0, false),
			("FalloffEnd", 0.01, 1000.0, false)
		};

		public static readonly string[] Names = Array.ConvertAll(Table, ((string Name, double Min, double Max, bool Whole) row) => row.Name);

		public static readonly ArchetypeTuning None = new ArchetypeTuning(Filled(float.NaN));

		private readonly float[] _v;

		public float this[int index] => _v[index];

		public float Damage => _v[0];

		public float ShotDelay => _v[1];

		public float Cooldown => _v[2];

		public float Chargeup => _v[3];

		public float Magazine => _v[4];

		public float Reload => _v[5];

		public float Precision => _v[6];

		public float Stagger => _v[7];

		public float Pierce => _v[8];

		public float AmmoCost => _v[9];

		public float FalloffStart => _v[10];

		public float FalloffEnd => _v[11];

		public bool IsEmpty
		{
			get
			{
				float[] v = _v;
				for (int i = 0; i < v.Length; i++)
				{
					if (!float.IsNaN(v[i]))
					{
						return false;
					}
				}
				return true;
			}
		}

		public ArchetypeTuning(float[] values)
		{
			if (values.Length != 12)
			{
				throw new ArgumentException("An archetype tuning has " + 12 + " values.");
			}
			_v = (float[])values.Clone();
		}

		private static float[] Filled(float value)
		{
			float[] array = new float[12];
			Array.Fill(array, value);
			return array;
		}

		public static ArchetypeTuning Parse(JsonElement payload)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			float[] array = new float[12];
			for (int i = 0; i < 12; i++)
			{
				(string, double, double, bool) tuple = Table[i];
				array[i] = ((!payload.TryGetProperty(tuple.Item1, out var _)) ? float.NaN : ((float)(tuple.Item4 ? StrictJson.Integer(payload, "", tuple.Item1, (double?)null, tuple.Item2, tuple.Item3) : StrictJson.Number(payload, "", tuple.Item1, (double?)null, tuple.Item2, tuple.Item3))));
			}
			if (float.IsNaN(array[10]) != float.IsNaN(array[11]))
			{
				throw new StrictJsonException("FalloffStart", "is set together with FalloffEnd.");
			}
			if (!float.IsNaN(array[10]) && array[11] <= array[10])
			{
				throw new StrictJsonException("FalloffEnd", "must be greater than FalloffStart.");
			}
			return new ArchetypeTuning(array);
		}

		public static ArchetypeTuning? FromWire(float[] values)
		{
			if (values.Length != 12)
			{
				return null;
			}
			for (int i = 0; i < 12; i++)
			{
				float num = values[i];
				if (!float.IsNaN(num))
				{
					(string, double, double, bool) tuple = Table[i];
					if (!((double)num >= tuple.Item2) || !((double)num <= tuple.Item3) || (tuple.Item4 && Math.Floor(num) != (double)num))
					{
						return null;
					}
				}
			}
			if (float.IsNaN(values[10]) != float.IsNaN(values[11]) || (!float.IsNaN(values[10]) && values[11] <= values[10]))
			{
				return null;
			}
			return new ArchetypeTuning(values);
		}

		public ArchetypeTuning Over(ArchetypeTuning native)
		{
			float[] array = new float[12];
			for (int i = 0; i < 12; i++)
			{
				array[i] = (float.IsNaN(_v[i]) ? native._v[i] : _v[i]);
			}
			return new ArchetypeTuning(array);
		}

		public double Rounds(float ammoRatio, float poolMax)
		{
			return Math.Floor((double)ammoRatio * (double)poolMax / (double)AmmoCost) + (double)Magazine;
		}

		public bool Valid()
		{
			float[] v = _v;
			for (int i = 0; i < v.Length; i++)
			{
				if (!float.IsFinite(v[i]))
				{
					return false;
				}
			}
			if (ShotDelay > 0f && AmmoCost > 0f)
			{
				return Magazine >= 1f;
			}
			return false;
		}

		public bool SameAs(ArchetypeTuning other)
		{
			for (int i = 0; i < 12; i++)
			{
				if (_v[i] != other._v[i] && (!float.IsNaN(_v[i]) || !float.IsNaN(other._v[i])))
				{
					return false;
				}
			}
			return true;
		}
	}
	public enum LoanEnd : byte
	{
		None,
		Empty,
		Dropped,
		Down
	}
	public sealed class HeavyLoan
	{
		public const float SettleSeconds = 1.5f;

		public float StartedAt { get; }

		public LoanEnd Ended { get; private set; }

		public HeavyLoan(float startedAt)
		{
			StartedAt = startedAt;
		}

		public LoanEnd Update(float now, int clip, int reserve, bool dropPressed, bool down)
		{
			if (Ended != LoanEnd.None)
			{
				return LoanEnd.None;
			}
			if (down)
			{
				Ended = LoanEnd.Down;
			}
			else if (dropPressed)
			{
				Ended = LoanEnd.Dropped;
			}
			else if (now - StartedAt >= 1.5f && clip <= 0 && reserve <= 0)
			{
				Ended = LoanEnd.Empty;
			}
			return Ended;
		}
	}
	internal sealed class LauncherLoan
	{
		internal const float OriginTolerance = 2.4f;

		internal readonly Guid Identity = Guid.NewGuid();

		internal readonly HeavyWeaponSpec Spec;

		internal readonly uint GearChecksum;

		private readonly float _interval;

		private float _nextShot;

		private uint _lastShot;

		internal int Remaining { get; private set; }

		internal LauncherLoan(HeavyWeaponSpec spec, uint gearChecksum, int ammunition, float interval, float createdAt)
		{
			if (spec.Grenade == null || ammunition <= 0 || !float.IsFinite(interval) || interval <= 0f || !float.IsFinite(createdAt))
			{
				throw new ArgumentException("Invalid host launcher budget or cadence.");
			}
			Spec = spec;
			GearChecksum = gearChecksum;
			Remaining = ammunition;
			_interval = interval;
			_nextShot = createdAt;
		}

		internal bool TrySchedule(Guid identity, uint sequence, float now, float originDistanceSquared, out float delay, out string reason)
		{
			delay = 0f;
			reason = ((identity != Identity) ? "wrong loan identity" : ((sequence == 0 || sequence <= _lastShot) ? "stale shot identity" : ((!(originDistanceSquared <= 5.76f) || !float.IsFinite(originDistanceSquared)) ? "origin outside host-observed reach" : ((!float.IsFinite(now) || now < 0f) ? "invalid host time" : ((Remaining <= 0) ? "loan ammunition exhausted" : "")))));
			if (reason.Length != 0)
			{
				return false;
			}
			float num = Math.Max(now, _nextShot);
			float num2 = num + _interval;
			if (!float.IsFinite(num) || !float.IsFinite(num2))
			{
				reason = "fire schedule overflowed";
				return false;
			}
			Remaining--;
			_lastShot = sequence;
			_nextShot = num2;
			delay = num - now;
			return true;
		}
	}
	public sealed class LoanBook
	{
		private readonly Dictionary<ulong, (ulong Summoner, uint ThrowId, uint Attempt)> _loans = new Dictionary<ulong, (ulong, uint, uint)>();

		private readonly Dictionary<ulong, bool> _stashed = new Dictionary<ulong, bool>();

		public bool Any => _loans.Count != 0;

		public void Commit(ulong summoner, uint throwId, ulong player, uint attempt)
		{
			_loans[player] = (summoner, throwId, attempt);
		}

		public bool Release(ulong summoner, uint throwId, ulong player, uint attempt)
		{
			if (_loans.TryGetValue(player, out (ulong, uint, uint) value))
			{
				(ulong, uint, uint) tuple = value;
				if (tuple.Item1 == summoner && tuple.Item2 == throwId && tuple.Item3 == attempt)
				{
					_stashed.Remove(player);
					return _loans.Remove(player);
				}
			}
			return false;
		}

		public bool Stash(ulong summoner, uint throwId, ulong player, uint attempt)
		{
			if (_loans.TryGetValue(player, out (ulong, uint, uint) value))
			{
				(ulong, uint, uint) tuple = value;
				if (tuple.Item1 == summoner && tuple.Item2 == throwId && tuple.Item3 == attempt)
				{
					_stashed.TryAdd(player, value: false);
					return true;
				}
			}
			return false;
		}

		public bool IsStashed(ulong player)
		{
			return _stashed.ContainsKey(player);
		}

		public bool Observe(ulong player, bool marked)
		{
			if (!_stashed.TryGetValue(player, out var value))
			{
				return marked;
			}
			if (!marked)
			{
				_stashed[player] = true;
			}
			else if (value)
			{
				_stashed.Remove(player);
			}
			return true;
		}

		public bool HasLoan(ulong player)
		{
			return _loans.ContainsKey(player);
		}

		internal void ReconcileLoans(Func<ulong, bool> isStillBorrowing, Action<ulong> released)
		{
			List<ulong> list = null;
			foreach (KeyValuePair<ulong, (ulong, uint, uint)> loan in _loans)
			{
				if (!isStillBorrowing(loan.Key))
				{
					(list ?? (list = new List<ulong>())).Add(loan.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (ulong item in list)
			{
				_loans.Remove(item);
				_stashed.Remove(item);
				released(item);
			}
		}

		public void Clear()
		{
			_loans.Clear();
			_stashed.Clear();
		}
	}
	public sealed record AmmoStash(float Pool, int Clip);
	public enum ShellLook : byte
	{
		Shell = 1,
		Bomblet,
		Canister,
		Emitter
	}
	public readonly struct PlannedShell
	{
		public readonly float Right;

		public readonly float Forward;

		public readonly float Time;

		public PlannedShell(float right, float forward, float time)
		{
			Right = right;
			Forward = forward;
			Time = time;
		}
	}
	public static class StrikePlan
	{
		internal struct Lcg
		{
			private uint _state;

			internal Lcg(uint seed)
			{
				_state = ((seed == 0) ? 2654435769u : seed);
			}

			internal float Next()
			{
				_state = _state * 1664525 + 1013904223;
				return (float)(_state >> 8) / 16777216f;
			}
		}

		public static ShellLook Look(StrikeKind kind)
		{
			return kind switch
			{
				StrikeKind.Cluster => ShellLook.Bomblet, 
				StrikeKind.Gas => ShellLook.Canister, 
				StrikeKind.Stasis => ShellLook.Emitter, 
				_ => ShellLook.Shell, 
			};
		}

		public static PlannedShell[] Build(StrikeSpec spec, uint seed)
		{
			Lcg lcg = new Lcg(seed);
			switch (spec.Kind)
			{
			case StrikeKind.Precision:
			case StrikeKind.Incendiary:
			case StrikeKind.Gas:
			case StrikeKind.Stasis:
				return new PlannedShell[1]
				{
					new PlannedShell(0f, 0f, 0f)
				};
			case StrikeKind.Area:
			{
				PlannedShell[] array2 = new PlannedShell[spec.Shells];
				for (int j = 0; j < array2.Length; j++)
				{
					float x = lcg.Next() * (float)Math.PI * 2f;
					float num = spec.Spread * MathF.Sqrt(lcg.Next());
					float num2 = ((array2.Length == 1) ? 0f : (spec.Duration * (float)j / (float)(array2.Length - 1)));
					float num3 = ((array2.Length == 1 || j == 0) ? 0f : ((lcg.Next() - 0.5f) * spec.Duration / (float)array2.Length * 0.6f));
					array2[j] = new PlannedShell(MathF.Cos(x) * num, MathF.Sin(x) * num, Math.Clamp(num2 + num3, 0f, spec.Duration));
				}
				return array2;
			}
			case StrikeKind.Creeping:
			{
				PlannedShell[] array3 = new PlannedShell[spec.Rows * spec.Shells];
				for (int k = 0; k < spec.Rows; k++)
				{
					for (int l = 0; l < spec.Shells; l++)
					{
						float num4 = ((spec.Shells == 1) ? 0f : (spec.Width * ((float)l / (float)(spec.Shells - 1) - 0.5f)));
						float num5 = (lcg.Next() - 0.5f) * 1.2f;
						array3[k * spec.Shells + l] = new PlannedShell(num4 + num5, (float)k * spec.Length + (lcg.Next() - 0.5f), (float)k * spec.RowInterval + lcg.Next() * 0.25f);
					}
				}
				return array3;
			}
			case StrikeKind.Cluster:
			{
				PlannedShell[] array = new PlannedShell[spec.Shells];
				for (int i = 0; i < array.Length; i++)
				{
					float right = ((array.Length == 1) ? 0f : (spec.Length * ((float)i / (float)(array.Length - 1) - 0.5f)));
					array[i] = new PlannedShell(right, (lcg.Next() - 0.5f) * 1.5f, (array.Length == 1) ? 0f : (spec.Duration * (float)i / (float)(array.Length - 1)));
				}
				return array;
			}
			default:
				return Array.Empty<PlannedShell>();
			}
		}

		public static uint Seed(ulong summoner, uint throwId)
		{
			uint num = 2166136261u;
			for (int i = 0; i < 8; i++)
			{
				num ^= (byte)(summoner >> i * 8);
				num *= 16777619;
			}
			for (int j = 0; j < 4; j++)
			{
				num ^= (byte)(throwId >> j * 8);
				num *= 16777619;
			}
			return num;
		}
	}
	public static class Targeting
	{
		public static int Largest(ReadOnlySpan<float> healthMax, ReadOnlySpan<float> distance)
		{
			int num = -1;
			for (int i = 0; i < healthMax.Length; i++)
			{
				if (num < 0 || healthMax[i] > healthMax[num] || (healthMax[i] == healthMax[num] && distance[i] < distance[num]))
				{
					num = i;
				}
			}
			return num;
		}

		public static int Nearest(ReadOnlySpan<float> distance)
		{
			int num = -1;
			for (int i = 0; i < distance.Length; i++)
			{
				if (num < 0 || distance[i] < distance[num])
				{
					num = i;
				}
			}
			return num;
		}

		public static int NearestReachable<T>(ReadOnlySpan<float> distance, Span<float> scratch, int budget, ref T probe) where T : IReach
		{
			distance.CopyTo(scratch);
			Span<float> span = scratch.Slice(0, distance.Length);
			for (int i = 0; i < budget; i++)
			{
				int num = Nearest(span);
				if (num < 0 || float.IsPositiveInfinity(span[num]))
				{
					return -1;
				}
				if (probe.Reaches(num))
				{
					return num;
				}
				span[num] = float.PositiveInfinity;
			}
			return -1;
		}
	}
	public enum StrikeKind : byte
	{
		Precision = 1,
		Area,
		Creeping,
		Cluster,
		Incendiary,
		Gas,
		Stasis,
		Beam,
		Hunter
	}
	public sealed class StrikeSpec
	{
		private readonly struct Field
		{
			internal readonly string Name;

			internal readonly double Fallback;

			internal readonly double Min;

			internal readonly double Max;

			internal readonly Action<StrikeSpec, double> Set;

			internal readonly bool Integer;

			internal Field(string name, double fallback, double min, double max, Action<StrikeSpec, double> set, bool integer = false)
			{
				Name = name;
				Fallback = fallback;
				Min = min;
				Max = max;
				Set = set;
				Integer = integer;
			}
		}

		public const string TypeName = "arsenal.Strike";

		private static readonly Dictionary<StrikeKind, Field[]> Fields = new Dictionary<StrikeKind, Field[]>
		{
			[StrikeKind.Precision] = new Field[3]
			{
				Damage_(600.0),
				Blast(5.0),
				Force_(800.0)
			},
			[StrikeKind.Area] = new Field[6]
			{
				Damage_(180.0),
				Blast(4.5),
				Force_(500.0),
				Shells_(7.0),
				Spread_(15.0),
				Duration_(5.0)
			},
			[StrikeKind.Creeping] = new Field[8]
			{
				Damage_(160.0),
				Blast(4.5),
				Force_(500.0),
				Shells_(3.0),
				new Field("Rows", 5.0, 1.0, 12.0, delegate(StrikeSpec s, double v)
				{
					s.Rows = (int)v;
				}, integer: true),
				new Field("Length", 5.0, 1.0, 20.0, delegate(StrikeSpec s, double v)
				{
					s.Length = (float)v;
				}),
				new Field("Width", 10.0, 0.0, 30.0, delegate(StrikeSpec s, double v)
				{
					s.Width = (float)v;
				}),
				new Field("RowInterval", 1.2, 0.2, 10.0, delegate(StrikeSpec s, double v)
				{
					s.RowInterval = (float)v;
				})
			},
			[StrikeKind.Cluster] = new Field[6]
			{
				Damage_(90.0),
				Blast(3.5),
				Force_(300.0),
				Shells_(10.0),
				Duration_(1.5),
				new Field("Length", 20.0, 1.0, 60.0, delegate(StrikeSpec s, double v)
				{
					s.Length = (float)v;
				})
			},
			[StrikeKind.Incendiary] = new Field[7]
			{
				Damage_(80.0),
				Blast(3.5),
				Force_(300.0),
				Duration_(10.0, 1.0),
				Tick_(0.5),
				TickDamage_(3.0),
				Zone(6.0)
			},
			[StrikeKind.Gas] = new Field[4]
			{
				Duration_(12.0, 1.0),
				Tick_(0.5),
				TickDamage_(2.5),
				Zone(7.0)
			},
			[StrikeKind.Stasis] = new Field[3]
			{
				Duration_(20.0, 1.0),
				Zone(8.0),
				new Field("Slow", 0.35, 0.05, 0.95, delegate(StrikeSpec s, double v)
				{
					s.Slow = (float)v;
				})
			},
			[StrikeKind.Beam] = new Field[5]
			{
				Duration_(10.0, 1.0),
				Tick_(0.2),
				TickDamage_(6.0),
				Spread_(18.0),
				Speed_(4.0)
			},
			[StrikeKind.Hunter] = new Field[5]
			{
				Damage_(2000.0, 50000.0),
				Blast(3.0),
				Force_(800.0),
				Spread_(25.0),
				TickDamage_(60.0)
			}
		};

		private static readonly string[] KindFields = new string[2] { "Type", "Kind" };

		public StrikeKind Kind { get; internal set; }

		public float Damage { get; internal set; }

		public float BlastRadius { get; internal set; }

		public float Force { get; internal set; }

		public int Shells { get; internal set; }

		public float Spread { get; internal set; }

		public float Duration { get; internal set; }

		public int Rows { get; internal set; }

		public float Length { get; internal set; }

		public float Width { get; internal set; }

		public float RowInterval { get; internal set; }

		public float Tick { get; internal set; }

		public float TickDamage { get; internal set; }

		public float ZoneRadius { get; internal set; }

		public float Speed { get; internal set; }

		public float Slow { get; internal set; }

		private static Field Damage_(double d, double max = 20000.0)
		{
			return new Field("Damage", d, 0.0, max, delegate(StrikeSpec s, double v)
			{
				s.Damage = (float)v;
			});
		}

		private static Field Blast(double d)
		{
			return new Field("BlastRadius", d, 0.5, 30.0, delegate(StrikeSpec s, double v)
			{
				s.BlastRadius = (float)v;
			});
		}

		private static Field Force_(double d)
		{
			return new Field("Force", d, 0.0, 5000.0, delegate(StrikeSpec s, double v)
			{
				s.Force = (float)v;
			});
		}

		private static Field Shells_(double d)
		{
			return new Field("Shells", d, 1.0, 32.0, delegate(StrikeSpec s, double v)
			{
				s.Shells = (int)v;
			}, integer: true);
		}

		private static Field Spread_(double d)
		{
			return new Field("Spread", d, 0.0, 60.0, delegate(StrikeSpec s, double v)
			{
				s.Spread = (float)v;
			});
		}

		private static Field Duration_(double d, double min = 0.0)
		{
			return new Field("Duration", d, min, 120.0, delegate(StrikeSpec s, double v)
			{
				s.Duration = (float)v;
			});
		}

		private static Field Tick_(double d)
		{
			return new Field("Tick", d, 0.1, 5.0, delegate(StrikeSpec s, double v)
			{
				s.Tick = (float)v;
			});
		}

		private static Field TickDamage_(double d)
		{
			return new Field("TickDamage", d, 0.0, 2000.0, delegate(StrikeSpec s, double v)
			{
				s.TickDamage = (float)v;
			});
		}

		private static Field Speed_(double d)
		{
			return new Field("Speed", d, 0.5, 20.0, delegate(StrikeSpec s, double v)
			{
				s.Speed = (float)v;
			});
		}

		private static Field Zone(double d)
		{
			return new Field("ZoneRadius", d, 0.5, 30.0, delegate(StrikeSpec s, double v)
			{
				s.ZoneRadius = (float)v;
			});
		}

		public static StrikeSpec Parse(JsonElement payload)
		{
			//IL_0014: 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.");
			}
			StrikeKind strikeKind = StrictJson.Enum<StrikeKind>(payload, "", "Kind");
			Field[] array = Fields[strikeKind];
			StrictJson.OnlyKnown(payload, "", Array.ConvertAll(array, (Field f) => f.Name), KindFields, strikeKind.ToString());
			StrikeSpec strikeSpec = new StrikeSpec
			{
				Kind = strikeKind
			};
			Field[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				Field field = array2[num];
				field.Set(strikeSpec, field.Integer ? StrictJson.Integer(payload, "", field.Name, (double?)field.Fallback, field.Min, field.Max) : StrictJson.Number(payload, "", field.Name, (double?)field.Fallback, field.Min, field.Max));
			}
			return strikeSpec;
		}
	}
	public static class StrikeTiming
	{
		public const float FallSeconds = 1.8f;

		public const float DebrisDelay = 0.5f;

		public const float IgniteDelay = 0.2f;

		public const float BeamChargeSeconds = 1.5f;

		public static float ZoneStartDelay(StrikeKind kind)
		{
			switch (kind)
			{
			case StrikeKind.Incendiary:
			case StrikeKind.Gas:
				return 0.8f;
			case StrikeKind.Stasis:
				return 1f;
			default:
				return 0f;
			}
		}
	}
	public enum DataTag : byte
	{
		Shell = 1,
		Zone = 2,
		Beam = 3,
		BeamEnd = 4,
		ZoneEnd = 6,
		Rack = 7,
		RackUnavailable = 10
	}
	public readonly struct RackRecord
	{
		public readonly HeavyWeaponSpec Spec;

		public readonly uint GearId;

		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public readonly float Yaw;

		public readonly string Id;

		public RackRecord(HeavyWeaponSpec spec, uint gearId, float x, float y, float z, float yaw, string id)
		{
			Spec = spec;
			GearId = gearId;
			X = x;
			Y = y;
			Z = z;
			Yaw = yaw;
			Id = id;
		}
	}
	public readonly struct ShellRecord
	{
		public readonly ShellLook Look;

		public readonly StrikeKind Kind;

		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public readonly float Top;

		public readonly float Fall;

		public readonly float Blast;

		public ShellRecord(ShellLook look, StrikeKind kind, float x, float y, float z, float top, float fall, float blast)
		{
			Look = look;
			Kind = kind;
			X = x;
			Y = y;
			Z = z;
			Top = top;
			Fall = fall;
			Blast = blast;
		}
	}
	public readonly struct ZoneRecord
	{
		public readonly StrikeKind Kind;

		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public readonly float Radius;

		public readonly float Seconds;

		public readonly float Slow;

		public ZoneRecord(StrikeKind kind, float x, float y, float z, float radius, float seconds, float slow = 1f)
		{
			Kind = kind;
			X = x;
			Y = y;
			Z = z;
			Radius = radius;
			Seconds = seconds;
			Slow = slow;
		}
	}
	public readonly struct BeamRecord
	{
		public readonly Guid Generation;

		public readonly float OX;

		public readonly float OY;

		public readonly float OZ;

		public readonly float TX;

		public readonly float TY;

		public readonly float TZ;

		public readonly bool HasTarget;

		public BeamRecord(Guid generation, float ox, float oy, float oz, float tx, float ty, float tz, bool hasTarget)
		{
			Generation = generation;
			OX = ox;
			OY = oy;
			OZ = oz;
			TX = tx;
			TY = ty;
			TZ = tz;
			HasTarget = hasTarget;
		}
	}
	public static class Wire
	{
		public const int ShellBytes = 26;

		public const int ZoneBytes = 25;

		public const int BeamBytes = 41;

		public const int RackBytes = 88;

		public const int MaxDataBytes = 192;

		public static byte[] Shell(in ShellRecord r)
		{
			byte[] array = new byte[26];
			array[0] = (byte)r.Look;
			array[1] = (byte)r.Kind;
			Put(array.AsSpan(2), 0, r.X);
			Put(array.AsSpan(2), 1, r.Y);
			Put(array.AsSpan(2), 2, r.Z);
			Put(array.AsSpan(2), 3, r.Top);
			Put(array.AsSpan(2), 4, r.Fall);
			Put(array.AsSpan(2), 5, r.Blast);
			return array;
		}

		public static bool TryShell(byte[] b, out ShellRecord r)
		{
			r = default(ShellRecord);
			if (b.Length != 26 || !Enum.IsDefined(typeof(ShellLook), b[0]) || !Enum.IsDefined(typeof(StrikeKind), b[1]))
			{
				return false;
			}
			Span<byte> span = b.AsSpan(2);
			r = new ShellRecord((ShellLook)b[0], (StrikeKind)b[1], F(span, 0), F(span, 1), F(span, 2), F(span, 3), F(span, 4), F(span, 5));
			if (float.IsFinite(r.X) && float.IsFinite(r.Y) && float.IsFinite(r.Z) && float.IsFinite(r.Top) && float.IsFinite(r.Fall) && float.IsFinite(r.Blast) && r.Fall >= 0f)
			{
				return r.Fall < 30f;
			}
			return false;
		}

		public static byte[] Zone(in ZoneRecord r)
		{
			byte[] array = new byte[25];
			array[0] = (byte)r.Kind;
			Put(array.AsSpan(1), 0, r.X);
			Put(array.AsSpan(1), 1, r.Y);
			Put(array.AsSpan(1), 2, r.Z);
			Put(array.AsSpan(1), 3, r.Radius);
			Put(array.AsSpan(1), 4, r.Seconds);
			Put(array.AsSpan(1), 5, r.Slow);
			return array;
		}

		public static bool TryZone(byte[] b, out ZoneRecord r)
		{
			r = default(ZoneRecord);
			if (b.Length != 25 || !Enum.IsDefined(typeof(StrikeKind), b[0]))
			{
				return false;
			}
			Span<byte> span = b.AsSpan(1);
			r = new ZoneRecord((StrikeKind)b[0], F(span, 0), F(span, 1), F(span, 2), F(span, 3), F(span, 4), F(span, 5));
			if (float.IsFinite(r.X) && float.IsFinite(r.Y) && float.IsFinite(r.Z) && float.IsFinite(r.Radius) && float.IsFinite(r.Seconds) && r.Radius > 0f && r.Seconds > 0f && r.Seconds <= 600f && r.Slow > 0f)
			{
				return r.Slow <= 1f;
			}
			return false;
		}

		public static byte[] Beam(in BeamRecord r)
		{
			byte[] array = new byte[41];
			array[0] = (r.HasTarget ? ((byte)1) : ((byte)0));
			Put(array.AsSpan(1), 0, r.OX);
			Put(array.AsSpan(1), 1, r.OY);
			Put(array.AsSpan(1), 2, r.OZ);
			Put(array.AsSpan(1), 3, r.TX);
			Put(array.AsSpan(1), 4, r.TY);
			Put(array.AsSpan(1), 5, r.TZ);
			r.Generation.TryWriteBytes(array.AsSpan(25));
			return array;
		}

		public static bool TryBeam(byte[] b, out BeamRecord r)
		{
			r = default(BeamRecord);
			if (b.Length != 41 || b[0] > 1)
			{
				return false;
			}
			Span<byte> span = b.AsSpan(1);
			r = new BeamRecord(new Guid(b.AsSpan(25)), F(span, 0), F(span, 1), F(span, 2), F(span, 3), F(span, 4), F(span, 5), b[0] == 1);
			if (r.Generation != Guid.Empty)
			{
				if (float.IsFinite(r.OX) && float.IsFinite(r.OY) && float.IsFinite(r.OZ) && float.IsFinite(r.TX) && float.IsFinite(r.TY))
				{
					return float.IsFinite(r.TZ);
				}
				return false;
			}
			return false;
		}

		public static byte[] Rack(in RackRecord r)
		{
			byte[] array = Utf8Prefix(r.Id, 104);
			byte[] array2 = new byte[88 + array.Length];
			BinaryPrimitives.WriteUInt32LittleEndian(array2.AsSpan(0, 4), r.Spec.ModelHash);
			BinaryPrimitives.WriteUInt32LittleEndian(array2.AsSpan(4, 4), r.GearId);
			Put(array2.AsSpan(8), 0, r.Spec.AmmoRatio);
			Put(array2.AsSpan(8), 1, r.Spec.Grenade?.Damage ?? 0f);
			Put(array2.AsSpan(8), 2, r.Spec.Grenade?.Radius ?? 0f);
			Put(array2.AsSpan(8), 3, r.Spec.Grenade?.Speed ?? 0f);
			Put(array2.AsSpan(8), 4, r.X);
			Put(array2.AsSpan(8), 5, r.Y);
			Put(array2.AsSpan(8), 6, r.Z);
			Put(array2.AsSpan(8), 7, r.Yaw);
			for (int i = 0; i < 12; i++)
			{
				Put(array2.AsSpan(8), 8 + i, r.Spec.Tuning[i]);
			}
			array.CopyTo(array2, 88);
			return array2;
		}

		public static bool TryRack(byte[] b, out RackRecord r)
		{
			r = default(RackRecord);
			if (b.Length < 88 || b.Length > 192)
			{
				return false;
			}
			Span<byte> span = b.AsSpan(8);
			float num = F(span, 4);
			float num2 = F(span, 5);
			float num3 = F(span, 6);
			float num4 = F(span, 7);
			if (!float.IsFinite(num) || !float.IsFinite(num2) || !float.IsFinite(num3) || !float.IsFinite(num4))
			{
				return false;
			}
			if (!HeavyModels.TryId(BinaryPrimitives.ReadUInt32LittleEndian(b.AsSpan(0, 4)), out string id))
			{
				return false;
			}
			uint gearId = BinaryPrimitives.ReadUInt32LittleEndian(b.AsSpan(4, 4));
			float num5 = F(span, 1);
			float num6 = F(span, 2);
			float num7 = F(span, 3);
			GrenadeSpec grenade = ((num5 == 0f && num6 == 0f && num7 == 0f) ? null : new GrenadeSpec(num5, num6, num7));
			HeavyWeaponSpec heavyWeaponSpec = HeavyWeaponSpec.Restore(id, gearId, F(span, 0), grenade, ArchetypeTuning.FromWire(Tuned(span)));
			if (heavyWeaponSpec == null)
			{
				return false;
			}
			string id2;
			try
			{
				id2 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(b, 88, b.Length - 88);
			}
			catch (ArgumentException)
			{
				return false;
			}
			r = new RackRecord(heavyWeaponSpec, gearId, num, num2, num3, num4, id2);
			return true;
		}

		public static bool UnknownRackModel(byte[] b, out uint hash)
		{
			hash = 0u;
			if (b.Length < 88 || b.Length > 192)
			{
				return false;
			}
			hash = BinaryPrimitives.ReadUInt32LittleEndian(b.AsSpan(0, 4));
			string id;
			return !HeavyModels.TryId(hash, out id);
		}

		private static byte[] Utf8Prefix(string text, int limit)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(text);
			if (bytes.Length <= limit)
			{
				return bytes;
			}
			int num = limit;
			while (num > 0 && (bytes[num] & 0xC0) == 128)
			{
				num--;
			}
			return bytes.AsSpan(0, num).ToArray();
		}

		private static float[] Tuned(ReadOnlySpan<byte> s)
		{
			float[] array = new float[12];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = F(s, 8 + i);
			}
			return array;
		}

		private static void Put(Span<byte> into, int index, float value)
		{
			BinaryPrimitives.WriteSingleLittleEndian(into.Slice(index * 4, 4), value);
		}

		private static float F(ReadOnlySpan<byte> s, int index)
		{
			return BinaryPrimitives.ReadSingleLittleEndian(s.Slice(index * 4, 4));
		}
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct LoanNotice
	{
		public const string Name = "Loan";

		public ulong Summoner;

		public uint ThrowId;

		public uint Attempt;

		public LoanAction Action;
	}
	public enum LoanAction : byte
	{
		Returned,
		Stashed
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct GrenadeShot
	{
		public const string Name = "Grenade";

		public ulong Shooter;

		public Guid Loan;

		public uint Sequence;

		public float OX;

		public float OY;

		public float OZ;

		public float VX;

		public float VY;

		public float VZ;

		public float Damage;

		public float Radius;
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct GrenadeBurst
	{
		public const string Name = "GrenadeBurst";

		public ulong Shooter;

		public Guid Loan;

		public uint Sequence;

		public float X;

		public float Y;

		public float Z;
	}
	internal static class ArsenalArt
	{
		internal const string BundleFile = "forge-ordnance-arsenal.bundle";

		internal const string Pack = "Arsenal";

		internal static readonly Quaternion FrontToForward = Quaternion.Euler(0f, 180f, 0f);

		internal static readonly Quaternion NoseDown = Quaternion.Euler(-90f, 0f, 0f);

		private static bool _tried;

		private static BundleHandle? _pack;

		internal static ModelHandle? Shell;

		internal static ModelHandle? Bomblet;

		internal static ModelHandle? Canister;

		internal static ModelHandle? Emitter;

		private static readonly Dictionary<string, ModelHandle> Weapons = new Dictionary<string, ModelHandle>(StringComparer.Ordinal);

		internal static BundleHandle? Bundle { get; private set; }

		internal static ArsenalSheets? Sheets { get; private set; }

		internal static string Failure { get; private set; } = "the level has not been built yet";

		internal static void Register()
		{
			try
			{
				string text = Path.Combine(Path.GetDirectoryName(typeof(ArsenalArt).Assembly.Location) ?? throw new InvalidOperationException("The plugin folder is unknown."), "forge-ordnance-arsenal.bundle");
				if (!File.Exists(text))
				{
					throw new FileNotFoundException("Install forge-ordnance-arsenal.bundle next to ForgeOrdnanceArsenal.dll.", text);
				}
				BundleHandle pack = Bundles.Register("NAinfini.ForgeOrdnanceArsenal", "Arsenal", text) ?? throw new InvalidDataException("ForgeVisual refused forge-ordnance-arsenal.bundle (see its error above).");
				Shell = Model("shell");
				Bomblet = Model("bomblet");
				Canister = Model("gas-canister");
				Emitter = Model("stasis-emitter");
				_pack = pack;
				ArsenalTurrets.Register(Model);
				ModelHandle Model(string name)
				{
					ModelHandle result = default(ModelHandle);
					if (!pack.TryModel(name, ref result))
					{
						throw new InvalidDataException("forge-ordnance-arsenal.bundle has no model " + name + ".");
					}
					return result;
				}
			}
			catch (Exception ex)
			{
				Failure = ex.ToString();
				Plugin.Error("Arsenal art could not be registered; strikes, heavy weapons and turrets work without their models and effect looks: " + ex);
			}
		}

		internal static void Load()
		{
			if (_tried)
			{
				return;
			}
			_tried = true;
			if (_pack == null)
			{
				return;
			}
			try
			{
				BundleHandle bundle = _pack;
				Sheets = new ArsenalSheets
				{
					Smoke = Sheet("smoke", "vfx-smoke", 5, 3, (FxBlend)0, sheddable: true),
					Blast = Sheet("blast", "vfx-blast", 5, 5, (FxBlend)0),
					Fireball = Sheet("fireball", "vfx-fireball", 8, 8, (FxBlend)1),
					Flash = Sheet("flash", "vfx-flash", 8, 8, (FxBlend)1),
					Flame = Sheet("flame", "vfx-flame", 16, 4, (FxBlend)1),
					Flare = Sheet("flare", "vfx-flare", 1, 1, (FxBlend)1),
					Ring = Sheet("ring", "vfx-ring", 1, 1, (FxBlend)1),
					Scorch = Sheet("scorch", "vfx-scorch", 1, 1, (FxBlend)0),
					Streak = Sheet("streak", "vfx-streak", 1, 1, (FxBlend)1),
					Glow = Sheet("glow", "vfx-glow", 1, 1, (FxBlend)1),
					Mist = Sheet("mist", "vfx-mist", 2, 2, (FxBlend)0),
					Fire = Sheet("fire", "vfx-fire", 2, 2, (FxBlend)0),
					Spark = Sheet("spark", "vfx-spark", 1, 1, (FxBlend)0)
				};
				Bundle = bundle;
				Failure = "";
				SpriteSheet Sheet(string item, string texture, int columns, int rows, FxBlend blend, bool sheddable = false)
				{
					//IL_003a: Unknown result type (might be due to invalid IL or missing references)
					Texture2D val = default(Texture2D);
					if (!bundle.TryTexture(texture, ref val))
					{
						throw new InvalidDataException("forge-ordnance-arsenal.bundle has no effect texture " + texture + ".");
					}
					return SpriteSheet.Register("NAinfini.ForgeOrdnanceArsenal", "Arsenal:" + item, val, columns, rows, blend, sheddable, 0) ?? throw new InvalidDataException("ForgeVisual refused the sprite sheet Arsenal:" + item + " (see its error above).");
				}
			}
			catch (Exception ex)
			{
				Sheets = null;
				Failure = ex.ToString();
				Plugin.Error("Arsenal art could not be loaded; strikes and heavy weapons work without their models and effect looks: " + ex);
				return;
			}
			try
			{
				StrikeBursts.Prepare();
			}
			catch (Exception ex2)
			{
				Plugin.Error("Building the strike effects at level load failed; they are built at the first strike instead: " + ex2);
			}
		}

		internal static ModelHandle? Look(ShellLook look)
		{
			return (ModelHandle?)(look switch
			{
				ShellLook.Bomblet => Bomblet, 
				ShellLook.Canister => Canister, 
				ShellLook.Emitter => Emitter, 
				_ => Shell, 
			});
		}

		internal static string? CheckWeaponModel(string id)
		{
			if (Weapons.ContainsKey(id))
			{
				return null;
			}
			ModelHandle val = Models.Get(id);
			if (val == null)
			{
				return "no ForgeVisual model " + id + " is registered (its pack must be registered in its plugin's Load).";
			}
			string[] array = new string[3] { "RightHand", "LeftHand", "SightLook" };
			foreach (string text in array)
			{
				if ((Object)(object)Models.Find(val.Prefab.transform, text) == (Object)null)
				{
					return "the model " + id + " has no " + text + " child (a heavy weapon model needs RightHand, LeftHand and SightLook).";
				}
			}
			Weapons.Add(id, val);
			return null;
		}

		internal static ModelHandle? Weapon(string id)
		{
			if (!Weapons.TryGetValue(id, out ModelHandle value))
			{
				return null;
			}
			return value;
		}
	}
	internal sealed class ArsenalSheets
	{
		internal SpriteSheet Smoke;

		internal SpriteSheet Blast;

		internal SpriteSheet Fireball;

		internal SpriteSheet Flash;

		internal SpriteSheet Flame;

		internal SpriteSheet Flare;

		internal SpriteSheet Ring;

		internal SpriteSheet Scorch;

		internal SpriteSheet Streak;

		internal SpriteSheet Glow;

		internal SpriteSheet Mist;

		internal SpriteSheet Fire;

		internal SpriteSheet Spark;

		internal SpriteSheet[] All => (SpriteSheet[])(object)new SpriteSheet[13]
		{
			Smoke, Blast, Fireball, Flash, Flame, Flare, Ring, Scorch, Streak, Glow,
			Mist, Fire, Spark
		};
	}
	internal static class ArsenalAudio
	{
		internal static class Cue
		{
			internal const string Incoming = "strike.incoming_shell";

			internal const string SmallBoom = "strike.small_boom";

			internal const string BigBoom = "strike.big_boom";

			internal const string Debris = "strike.debris";

			internal const string ClusterSplit = "strike.cluster_split";

			internal const string FireIgnite = "strike.fire_ignite";

			internal const string FireLoop = "strike.fire_loop";

			internal const string GasRelease = "strike.gas_release";

			internal const string GasLoop = "strike.gas_loop";

			internal const string StasisPulse = "strike.stasis_pulse";

			internal const string StasisLoop = "strike.stasis_loop";

			internal const string BeamStart = "strike.beam_start";

			internal const string BeamLoop = "strike.beam_loop";

			internal const string BeamCutting = "strike.beam_cutting";

			internal const string BeamEnd = "strike.beam_end";

			internal const string Handover = "weapon.handover";

			internal const string GrenadeFire = "weapon.grenade_fire";

			internal const string GrenadeReload = "weapon.grenade_reload";

			internal const string GrenadeBurst = "weapon.grenade_burst";

			internal const string WeaponReturn = "weapon.return";

			internal const string TurretDeploy = "turret.deploy";

			internal const string TurretIdle = "turret.idle";

			internal const string TurretExpire = "turret.expire";
		}

		private static AudioLibrary? _sounds;

		internal static AudioLibrary? Sounds => _sounds;

		internal static void Register()
		{
			if (_sounds == null)
			{
				_sounds = AudioKit.Load("NAinfini.ForgeOrdnanceArsenal", (string)null);
			}
		}

		internal static void Dispose()
		{
			AudioLibrary? sounds = _sounds;
			if (sounds != null)
			{
				sounds.Dispose();
			}
			_sounds = null;
		}

		internal static void PlayReturn(GameObject owner, int dimension)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			AudioLibrary? sounds = _sounds;
			if (sounds != null)
			{
				PlayOptions val = new PlayOptions();
				val.set_IsSelf(true);
				val.set_Dimension((int?)dimension);
				sounds.Play("weapon.return", owner, val);
			}
		}

		internal static void PlayAt(string cue, Vector3 position)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			AudioLibrary? sounds = _sounds;
			if (sounds != null)
			{
				sounds.Play(cue, position, (PlayOptions)null);
			}
		}

		internal static bool Prewarm()
		{
			AudioLibrary? sounds = _sounds;
			if (sounds == null)
			{
				return true;
			}
			return sounds.Prewarm();
		}

		internal static void PlayAttached(string cue, GameObject owner, int dimension, bool isSelf = false)
		{
			//IL_000c: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			AudioLibrary? sounds = _sounds;
			if (sounds != null)
			{
				PlayOptions val = new PlayOptions();
				val.set_IsSelf(isSelf);
				val.set_Dimension((int?)dimension);
				sounds.Play(cue, owner, val);
			}
		}

		internal static void PlayOn(string cue, GameObject owner)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			AudioLibrary? sounds = _sounds;
			if (sounds != null)
			{
				sounds.Play(cue, owner, (PlayOptions)null);
			}
		}

		internal static void PlayLoop(string cue, GameObject owner)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			AudioLibrary? sounds = _sounds;
			if (sounds != null)
			{
				sounds.Play(cue, owner, (PlayOptions)null);
			}
		}
	}
	internal static class ArsenalKeys
	{
		internal static KeyAction Drop;

		internal static void Register()
		{
			Drop = UIKit.Mod("NAinfini.ForgeOrdnanceArsenal").AddKeyAction("DropHeavyWeapon", "keys.drop", "keys.group", "G", (KeyMode)0, "on-foot") ?? throw new InvalidOperationException("ForgeUI refused the key action DropHeavyWeapon; see its error.");
		}
	}
	internal static class ArsenalNetwork
	{
		private const int InboxLimit = 256;

		private static NetChannel? _channel;

		private static NetChannel Channel => _channel ?? throw new InvalidOperationException("Arsenal's network channel is not open.");

		internal static void Register()
		{
			_channel = Net.Open("NAinfini.ForgeOrdnanceArsenal", 256);
			_channel.On<LoanNotice>("Loan", (Action<ulong, LoanNotice>)HeavyWeapons.OnLoanNotice);
			_channel.On<GrenadeShot>("Grenade", (Action<ulong, GrenadeShot>)Grenades.OnShot);
			_channel.On<GrenadeBurst>("GrenadeBurst", (Action<ulong, GrenadeBurst>)Grenades.OnBurst);
		}

		internal static void Clear()
		{
			NetChannel? channel = _channel;
			if (channel != null)
			{
				channel.Clear();
			}
		}

		internal static void Pump()
		{
			NetChannel? channel = _channel;
			if (channel != null)
			{
				channel.Pump();
			}
		}

		internal static void ToHost<T>(string name, T message) where T : unmanaged
		{
			Channel.TryToHost<T>(name, message);
		}

		internal static void Broadcast<T>(string name, T message) where T : unmanaged
		{
			Channel.Broadcast<T>(name, message);
		}

		internal static bool IsPlayer(ulong lookup)
		{
			SNet_Player val = default(SNet_Player);
			if (SNet.TryGetPlayer(lookup, ref val) && (Object)(object)val != (Object)null)
			{
				return !val.IsBot;
			}
			return false;
		}
	}
	internal static class ArsenalText
	{
		private static readonly UIKitMod Ui = UIKit.Mod("NAinfini.ForgeOrdnanceArsenal");

		private static UITexts? _definitions;

		private static readonly HashSet<string> NamedMarkers = new HashSet<string>(StringComparer.Ordinal);

		private static UITexts Definitions => _definitions ?? (_definitions = UIKit.Texts(Path.Combine(Path.GetDirectoryName(typeof(ArsenalText).Assembly.Location) ?? ".", "ForgeOrdnance", "lang")));

		internal static int Version => UITexts.Version;

		internal static string T(string key)
		{
			return Ui.Text(key);
		}

		internal static string T(string key, params object?[] args)
		{
			return Ui.Text(key, args);
		}

		internal static string CallIn(string id)
		{
			string result = default(string);
			if (!Definitions.TryText(id + ".Name", ref result))
			{
				return id;
			}
			return result;
		}

		internal static void NameHeavyWeapons()
		{
			string text3 = default(string);
			foreach (OrdnanceDefinition definition in OrdnanceApi.Definitions)
			{
				if (!(definition.Parameters is HeavyParameters heavyParameters))
				{
					continue;
				}
				string text = HeavyModels.Marker(heavyParameters.Spec.ModelHash);
				if (!NamedMarkers.Add(text))
				{
					continue;
				}
				string text2 = definition.Id + ".Name";
				if (Definitions.TryText(text2, ref text3))
				{
					UIKit.GearName(Definitions, text, text2);
					continue;
				}
				UIKit.GearName(text, (Func<string>)(() => OrdnanceApi.DisplayName(definition)));
			}
		}
	}
	internal static class ArsenalTurrets
	{
		internal const string Burst = "Arsenal:turret-burst";

		internal const string Sniper = "Arsenal:turret-sniper";

		internal const string Shotgun = "Arsenal:turret-shotgun";

		private const string Prefix = "Arsenal:";

		private const string Pack = "Arsenal";

		private static readonly (string Id, string Model, float Scale)[] Looks = new(string, string, float)[3]
		{
			("Arsenal:turret-burst", "turret-burst", 1.24f),
			("Arsenal:turret-sniper", "turret-sniper", 1.14f),
			("Arsenal:turret-shotgun", "turret-shotgun", 1.05f)
		};

		private static readonly Dictionary<IntPtr, AudioSustain> Humming = new Dictionary<IntPtr, AudioSustain>();

		internal static void Register(Func<string, ModelHandle> model)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			(string, string, float)[] looks = Looks;
			for (int i = 0; i < looks.Length; i++)
			{
				var (text, arg, worldScale) = looks[i];
				OrdnanceTurrets.RegisterLook(text, model(arg), new TurretLookSpec
				{
					WorldScale = worldScale
				});
			}
			OrdnanceTurrets.Deployed += OnDeployed;
			OrdnanceTurrets.Online += OnOnline;
			OrdnanceTurrets.Expiring += OnExpiring;
		}

		private static bool Ours(string lookId)
		{
			return lookId.StartsWith("Arsenal:", StringComparison.Ordinal);
		}

		private static void OnDeployed(string lookId, Transform shell)
		{
			if (Ours(lookId))
			{
				ArsenalAudio.PlayOn("turret.deploy", ((Component)shell).gameObject);
			}
		}

		private static void OnOnline(string lookId, SentryGunInstance sentry, Transform at)
		{
			if (!Ours(lookId) || (Humming.TryGetValue(((Il2CppObjectBase)sentry).Pointer, out AudioSustain value) && value.IsActive))
			{
				return;
			}
			AudioLibrary? sounds = ArsenalAudio.Sounds;
			string text = default(string);
			AudioSustain val = ((sounds != null) ? sounds.Sustain("turret.idle", ((Component)sentry).gameObject, (PlayOptions)null, (Func<bool>)(() => OrdnanceTurrets.TryLook(sentry, ref text))) : null);
			if (val == null || !val.IsActive)
			{
				return;
			}
			foreach (IntPtr item in new List<IntPtr>(Humming.Keys))
			{
				if (!Humming[item].IsActive)
				{
					Humming.Remove(item);
				}
			}
			Humming[((Il2CppObjectBase)sentry).Pointer] = val;
		}

		private static void OnExpiring(string lookId, SentryGunInstance sentry, Vector3 at)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (Ours(lookId))
			{
				ArsenalAudio.PlayAt("turret.expire", at);
			}
		}

		internal static void Clear()
		{
			Humming.Clear();
		}
	}
	internal static class Grenades
	{
		private sealed class Flight
		{
			internal ulong Shooter;

			internal Guid Loan;

			internal uint Sequence;

			internal Vector3 Position;

			internal Vector3 Velocity;

			internal float Damage;

			internal float Radius;

			internal float Ends;

			internal GameObject? Look;
		}

		private const float Gravity = 9.81f;

		private const float Lifetime = 6f;

		private const float Spawn = 0.7f;

		private static bool _suppress;

		private static uint _nextShot;

		private static FxHandle? _explosion;

		private static readonly List<Flight> Flights = new List<Flight>();

		private static readonly List<(GrenadeShot Shot, float At)> Pending = new List<(GrenadeShot, float)>();

		private static readonly Dictionary<(ulong, Guid, uint), float> Settled = new Dictionary<(ulong, Guid, uint), float>();

		private static readonly List<(ulong, Guid, uint)> Expired = new List<(ulong, Guid, uint)>();

		internal static bool Suppressing => _suppress;

		internal static void BeforeFire(BulletWeapon weapon)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			if (!(HeavyWeapons.ActiveLauncher == null) && !((Object)(object)((Item)weapon).Owner == (Object)null) && ((Agent)((Item)weapon).Owner).IsLocallyOwned)
			{
				ItemDataBlock itemDataBlock = ((Item)weapon).ItemDataBlock;
				if (itemDataBlock != null && (int)itemDataBlock.inventorySlot == 2)
				{
					_suppress = true;
				}
			}
		}

		internal static void AfterFire(BulletWeapon weapon)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			if (!_suppress)
			{
				return;
			}
			_suppress = false;
			HeavyParameters activeLauncher = HeavyWeapons.ActiveLauncher;
			PlayerAgent owner = ((Item)weapon).Owner;
			if (!(activeLauncher == null) && !((Object)(object)owner == (Object)null))
			{
				Transform transform = ((Component)owner.FPSCamera).transform;
				GrenadeShot grenadeShot = new GrenadeShot
				{
					Shooter = SNet.LocalPlayer.Lookup,
					Loan = HeavyWeapons.ActiveLauncherIdentity,
					Sequence = ++_nextShot,
					OX = transform.position.x,
					OY = transform.position.y,
					OZ = transform.position.z,
					VX = transform.forward.x,
					VY = transform.forward.y,
					VZ = transform.forward.z
				};
				if (SNet.IsMaster)
				{
					OnShot(SNet.LocalPlayer.Lookup, grenadeShot);
					return;
				}
				GrenadeShot s = grenadeShot;
				float speed = activeLauncher.Spec.Grenade.Speed;
				s.VX *= speed;
				s.VY = s.VY * speed + 1.5f;
				s.VZ *= speed;
				Launch(s);
				ArsenalNetwork.ToHost("Grenade", grenadeShot);
			}
		}

		internal static void OnShot(ulong sender, GrenadeShot shot)
		{
			if (SNet.IsMaster)
			{
				if (HeavyWeapons.AuthorizeGrenade(sender, ref shot, out float delay, out string _))
				{
					if (delay > 0f)
					{
						Pending.Add((shot, Time.time + delay));
					}
					else
					{
						LaunchHost(shot);
					}
				}
			}
			else if (!((Object)(object)SNet.Master == (Object)null) && sender == SNet.Master.Lookup)
			{
				RemoveFlight(shot.Shooter, shot.Loan, shot.Sequence);
				if (!Settled.ContainsKey((shot.Shooter, shot.Loan, shot.Sequence)))
				{
					Launch(shot);
				}
			}
		}

		private static void LaunchHost(GrenadeShot shot)
		{
			ArsenalNetwork.Broadcast("Grenade", shot);
			Launch(shot);
		}

		private static void Launch(GrenadeShot s)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0159: 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_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			Flight flight = new Flight
			{
				Shooter = s.Shooter,
				Loan = s.Loan,
				Sequence = s.Sequence,
				Position = new Vector3(s.OX, s.OY, s.OZ),
				Velocity = new Vector3(s.VX, s.VY, s.VZ),
				Damage = s.Damage,
				Radius = s.Radius,
				Ends = Time.time + 6f
			};
			if (!float.IsFinite(flight.Position.x + flight.Position.y + flight.Position.z + flight.Velocity.x + flight.Velocity.y + flight.Velocity.z))
			{
				return;
			}
			Vector3 val = flight.Velocity - Vector3.up * 1.5f;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			RaycastHit val2 = default(RaycastHit);
			if (SNet.IsMaster && Physics.Raycast(flight.Position, normalized, ref val2, 0.7f, LayerManager.MASK_WORLD | LayerManager.MASK_ENEMY_DAMAGABLE, (QueryTriggerInteraction)1))
			{
				Burst(flight, ((RaycastHit)(ref val2)).point + ((RaycastHit)(ref val2)).normal * 0.1f);
				return;
			}
			flight.Position += normalized * 0.7f;
			ModelHandle bomblet = ArsenalArt.Bomblet;
			if (bomblet != null)
			{
				flight.Look = bomblet.Spawn(flight.Position, Quaternion.LookRotation(flight.Velocity) * ArsenalArt.FrontToForward, (Transform)null);
			}
			Flights.Add(flight);
			ArsenalAudio.PlayAt("weapon.grenade_fire", flight.Position);
		}

		internal static void Tick(float now)
		{
			//IL_011d: 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_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: 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_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			while (num < Pending.Count)
			{
				(GrenadeShot, float) tuple = Pending[num];
				if (now < tuple.Item2)
				{
					num++;
					continue;
				}
				Pending.RemoveAt(num);
				if (SNet.IsMaster)
				{
					LaunchHost(tuple.Item1);
				}
			}
			Expired.Clear();
			foreach (KeyValuePair<(ulong, Guid, uint), float> item in Settled)
			{
				if (now >= item.Value)
				{
					Expired.Add(item.Key);
				}
			}
			foreach (var item2 in Expired)
			{
				Settled.Remove(item2);
			}
			if (Flights.Count == 0)
			{
				return;
			}
			float deltaTime = Time.deltaTime;
			int num2 = LayerManager.MASK_WORLD | LayerManager.MASK_ENEMY_DAMAGABLE;
			RaycastHit val4 = default(RaycastHit);
			for (int num3 = Flights.Count - 1; num3 >= 0; num3--)
			{
				Flight flight = Flights[num3];
				try
				{
					Vector3 val = flight.Position + flight.Velocity * deltaTime;
					flight.Velocity += Vector3.down * (9.81f * deltaTime);
					Vector3 val2 = val - flight.Position;
					if (!SNet.IsMaster && now >= flight.Ends + 2f)
					{
						Flights.RemoveAt(num3);
						if ((Object)(object)flight.Look != (Object)null && !((Il2CppObjectBase)flight.Look).WasCollected)
						{
							Object.Destroy((Object)(object)flight.Look);
						}
						Plugin.Error("Grenade settlement timed out for " + flight.Shooter + "/" + flight.Sequence + "; projectile removed without a predicted explosion.");
					}
					else
					{
						bool flag = SNet.IsMaster && now >= flight.Ends;
						Vector3 val3 = val;
						if (SNet.IsMaster && !flag && Physics.Raycast(flight.Position, ((Vector3)(ref val2)).normalized, ref val4, ((Vector3)(ref val2)).magnitude, num2, (QueryTriggerInteraction)1))
						{
							flag = true;
							val3 = ((RaycastHit)(ref val4)).point + ((RaycastHit)(ref val4)).normal * 0.1f;
						}
						flight.Position = val3;
						if ((Object)(object)flight.Look != (Object)null && !((Il2CppObjectBase)flight.Look).WasCollected)
						{
							flight.Look.transform.position = val3;
							if (((Vector3)(ref flight.Velocity)).sqrMagnitude > 0.01f)
							{
								flight.Look.transform.rotation = Quaternion.LookRotation(flight.Velocity) * ArsenalArt.FrontToForward;
							}
						}
						if (flag)
						{
							Burst(flight, val3);
							Flights.RemoveAt(num3);
						}
					}
				}
				catch (Exception ex)
				{
					Flights.RemoveAt(num3);
					if ((Object)(object)flight.Look != (Object)null && !((Il2CppObjectBase)flight.Look).WasCollected)
					{
						Object.Destroy((Object)(object)flight.Look);
					}
					Plugin.Error("A grenade failed: " + ex);
				}
			}
		}

		private static void Burst(Flight f, Vector3 at)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)f.Look != (Object)null && !((Il2CppObjectBase)f.Look).WasCollected)
			{
				Object.Destroy((Object)(object)f.Look);
			}
			Combat.Explode(at, f.Radius, f.Damage, 600f);
			Settled[(f.Shooter, f.Loan, f.Sequence)] = Time.time + 6f + 2f;
			ArsenalNetwork.Broadcast("GrenadeBurst", new GrenadeBurst
			{
				Shooter = f.Shooter,
				Loan = f.Loan,
				Sequence = f.Sequence,
				X = at.x,
				Y = at.y,
				Z = at.z
			});
			ShowBurst(at);
		}

		internal static void OnBurst(ulong sender, GrenadeBurst burst)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if (!SNet.IsMaster && !((Object)(object)SNet.Master == (Object)null) && sender == SNet.Master.Lookup && burst.Shooter != 0L && !(burst.Loan == Guid.Empty) && burst.Sequence != 0 && float.IsFinite(burst.X) && float.IsFinite(burst.Y) && float.IsFinite(burst.Z))
			{
				(ulong, Guid, uint) key = (burst.Shooter, burst.Loan, burst.Sequence);
				if (Settled.TryAdd(key, Time.time + 6f + 2f))
				{
					RemoveFlight(burst.Shooter, burst.Loan, burst.Sequence);
					ShowBurst(new Vector3(burst.X, burst.Y, burst.Z));
				}
			}
		}

		private static void RemoveFlight(ulong shooter, Guid loan, uint sequence)
		{
			for (int num = Flights.Count - 1; num >= 0; num--)
			{
				Flight flight = Flights[num];
				if (flight.Shooter == shooter && !(flight.Loan != loan) && flight.Sequence == sequence)
				{
					if ((Object)(object)flight.Look != (Object)null && !((Il2CppObjectBase)flight.Look).WasCollected)
					{
						Object.Destroy((Object)(object)flight.Look);
					}
					Flights.RemoveAt(num);
				}
			}
		}

		private static void ShowBurst(Vector3 at)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (_explosion == null)
			{
				_explosion = Fx.Find("Game:FX_Tripmine");
			}
			if (_explosion != null)
			{
				Fx.Play(_explosion, at, Vector3.up);
			}
			ArsenalAudio.PlayAt("weapon.grenade_burst", at);
		}

		internal static void Clear()
		{
			foreach (Flight flight in Flights)
			{
				if ((Object)(object)flight.Look != (Object)null && !((Il2CppObjectBase)flight.Look).WasCollected)
				{
					Object.Destroy((Object)(object)flight.Look);
				}
			}
			Flights.Clear();
			Pending.Clear();
			Settled.Clear();
			Expired.Clear();
			_suppress = false;
		}
	}
	internal static class LauncherReload
	{
		internal static void Subscribe()
		{
			GameHooks.ReloadTriggered.Around<bool>((Func<ItemEquippable, bool>)((ItemEquippable item) => item.IsReloading), (Action<ItemEquippable, bool, bool>)delegate(ItemEquippable item, bool wasReloading, bool ran)
			{
				Postfix(item, ran, wasReloading);
			}, (Action<ItemEquippable, bool, Exception>)null);
		}

		private static void Postfix(ItemEquippable __instance, bool __runOriginal, bool __state)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected I4, but got Unknown
			PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack;
			if (!(!__runOriginal || __state) && __instance.IsReloading && !((Object)(object)((Item)__instance).Owner == (Object)null) && ((Agent)((Item)__instance).Owner).IsLocallyOwned)
			{
				ItemDataBlock itemDataBlock = ((Item)__instance).ItemDataBlock;
				BackpackItem val = default(BackpackItem);
				if (itemDataBlock != null && (int)itemDataBlock.inventorySlot == 2 && !(HeavyWeapons.ActiveLauncher == null) && localBackpack != null && localBackpack.TryGetBackpackItem((InventorySlot)2, ref val) && !((Object)(object)((val != null) ? val.Instance : null) != (Object)(object)__instance))
				{
					ArsenalAudio.PlayAttached("weapon.grenade_reload", ((Component)__instance).gameObject, (int)((Agent)((Item)__instance).Owner).DimensionIndex, isSelf: true);
				}
			}
		}
	}
	internal static class LauncherFire
	{
		internal static void Subscribe()
		{
			Fire.BulletWeapon.Before((Action<BulletWeapon>)delegate(BulletWeapon weapon)
			{
				try
				{
					Grenades.BeforeFire(weapon);
				}
				catch (Exception ex)
				{
					Plugin.Error("Grenade launcher fire hook failed: " + ex);
				}
			});
			Fire.BulletWeapon.After((Action<BulletWeapon, bool>)delegate(BulletWeapon weapon, bool _)
			{
				try
				{
					Grenades.AfterFire(weapon);
				}
				catch (Exception ex)
				{
					Plugin.Error("Grenade launcher fire hook failed: " + ex);
				}
			});
		}
	}
	internal static class LauncherBullet
	{
		internal static void Subscribe()
		{
			GameHooks.BulletHit.Before((Action<BulletHitCall>)delegate(BulletHitCall call)
			{
				if (Grenades.Suppressing)
				{
					call.Skip(false);
				}
			});
		}
	}
	internal sealed record HeavyParameters(HeavyWeaponSpec Spec, uint GearId);
	internal sealed class HeavyWeaponPayload : IOrdnancePayload, IOrdnancePayloadParser
	{
		public bool HasOwnArrival => true;

		public bool SupportsDirect => true;

		public object Parse(JsonElement payload)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Invalid comparison between Unknown and I4
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			HeavyWeaponSpec heavyWeaponSpec = HeavyWeaponSpec.Parse(payload);
			string text = ArsenalArt.CheckWeaponModel(heavyWeaponSpec.Model);
			if (text != null)
			{
				throw new PayloadParameterException("Model", text);
			}
			PlayerOfflineGearDataBlock val = (heavyWeaponSpec.GearId.HasValue ? GameDataBlockBase<PlayerOfflineGearDataBlock>.GetBlock(heavyWeaponSpec.GearId.Value) : GameDataBlockBase<PlayerOfflineGearDataBlock>.GetBlock(heavyWeaponSpec.GearName));
			if (val == null || ((Il2CppObjectBase)val).WasCollected)
			{
				throw new PayloadParameterException("Gear", "no offline gear block " + (heavyWeaponSpec.GearId.HasValue ? heavyWeaponSpec.GearId.Value.ToString() : ("'" + heavyWeaponSpec.GearName + "'")) + " in this install.");
			}
			if (string.IsNullOrEmpty(val.GearJSON))
			{
				throw new PayloadParameterException("Gear", "the gear block has no GearJSON.");
			}
			uint compID = new GearIDRange(val.GearJSON).GetCompID((eGearComponent)3);
			ItemDataBlock block = GameDataBlockBase<ItemDataBlock>.GetBlock(compID);
			if (block == null || (int)block.inventorySlot != 2)
			{
				throw new PayloadParameterException("Gear", "gear block " + ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)val).persistentID + " is not a special slot weapon (base item " + compID + ").");
			}
			string text2 = HeavyModels.Register(heavyWeaponSpec.Model);
			if (text2 != null)
			{
				throw new PayloadParameterException("Model", text2);
			}
			return new HeavyParameters(heavyWeaponSpec, ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)val).persistentID);
		}

		public void Execute(OrdnanceExecution execution)
		{
			HeavyWeapons.HostArrived(execution);
		}

		public void OnArrived(OrdnancePresentation presentation)
		{
			HeavyWeapons.ShowRack(presentation);
		}

		public void OnData(OrdnancePayloadData data)
		{
			HeavyWeapons.OnData(data);
		}
	}
	internal static class HeavyWeapons
	{
		private sealed class WornLook
		{
			internal Dressed Dressed;

			internal ItemEquippable? Gear;

			internal Transform? Sight;

			internal Transform? NativeHolderSight;

			internal Transform? NativeWeaponSight;
		}

		private sealed class Rack
		{
			internal ulong Summoner;

			internal uint ThrowId;

			internal HeavyParameters P;

			internal string Id = "";

			internal OrdnanceDefinition? Definition;

			internal OrdnancePod? Pod;

			internal OrdnanceClaimable? Claim;

			internal int Bay;

			internal GameObject? Look;

			internal Vector3 Fallback;

			internal bool Presented;

			internal LauncherLoan? PendingLauncher;

			internal bool Taken
			{
				get
				{
					OrdnanceClaimable? claim = Claim;
					if (claim == null)
					{
						return false;
					}
					return claim.IsTaken;
				}
			}
		}

		private enum Phase
		{
			None,
			Equipping,
			AwaitingCommit,
			Active,
			Returning,
			Reconciling,
			Stashed,
			Suspended
		}

		private const float SwapTimeout = 6f;

		private const float SkinScanSeconds = 0.5f;

		private static readonly Dictionary<IntPtr, WornLook> Skins = new Dictionary<IntPtr, WornLook>();

		private static readonly List<Rack> Racks = new List<Rack>();

		private static readonly HashSet<(ulong Summoner, uint ThrowId)> UnavailableRacks = new HashSet<(ulong, uint)>();

		private static readonly LoanBook Loans = new LoanBook();

		private static readonly Dictionary<ulong, LauncherLoan> Launchers = new Dictionary<ulong, LauncherLoan>();

		private static Guid _launcherIdentity;

		private static float _nextSkinScan;

		private static Rack? _promptRack;

		private static Phase _promptPhase;

		private static SpecialSlotLease? _promptRefusal;

		private static int _promptLanguage = -1;

		private static string _promptText = "";

		private static string _promptKey = "";

		private static Phase _phase;

		private static HeavyParameters? _lent;

		private static SpecialSlotLease? _lease;

		private static bool _suspendedInHand;

		private static bool _resumeInHand;

		private static GearIDRange? _expected;

		private static ClaimInstall? _claim;

		private static bool _returnRequested;

		private static HeavyLoan? _loan;

		private static float _phaseStarted;

		private static AmmoStash? _heavyStash;

		private static float _switchReadyAt;

		private const float SwitchCooldown = 0.5f;

		private static readonly PropertyInfo[] BlockProperties = (from p in typeof(ArchetypeDataBlock).GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
			where p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0
			select p).ToArray();

		private const string LookName = "ForgeOrdnanceArsenal.WeaponLook";

		internal const string SightSocket = "SightLook";

		private static SpecialSlot Slot => Gear.SpecialSlot;

		internal static HeavyParameters? ActiveLauncher
		{
			get
			{
				if (_phase != Phase.Active || !(_lent?.Spec.Grenade != null))
				{
					return null;
				}
				return _lent;
			}
		}

		internal static Guid ActiveLauncherIdentity => _launcherIdentity;

		internal static string Marker(HeavyWeaponSpec spec)
		{
			return spec.Marker;
		}

		private static bool TryMarker(string? name, out string model)
		{
			return HeavyModels.TryMarkerId(name, out model);
		}

		internal static bool AuthorizeGrenade(ulong sender, ref GrenadeShot shot, out float delay, out string reason)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: 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_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: 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_0261: Unknown result type (might be due to invalid IL or missing references)
			delay = 0f;
			reason = "sender is not the shooter or not a human player";
			if (sender != shot.Shooter || !ArsenalNetwork.IsPlayer(sender))
			{
				return false;
			}
			reason = "no active host launcher loan";
			if (!Launchers.TryGetValue(sender, out LauncherLoan value))
			{
				return false;
			}
			PlayerAgent val = Level.PlayerByLookup(sender);
			reason = "launcher is not held by a live borrower";
			if (!((Object)(object)val == (Object)null) && ((Agent)val).Alive)
			{
				PlayerInventoryBase inventory = val.Inventory;
				if (inventory != null)
				{
					ItemEquippable wieldedItem = inventory.WieldedItem;
					InventorySlot? obj;
					if (wieldedItem == null)
					{
						obj = null;
					}
					else
					{
						ItemDataBlock itemDataBlock = ((Item)wieldedItem).ItemDataBlock;
						obj = ((itemDataBlock != null) ? new InventorySlot?(itemDataBlock.inventorySlot) : ((InventorySlot?)null));
					}
					PlayerBackpack val2 = default(PlayerBackpack);
					BackpackItem val3 = default(BackpackItem);
					if (obj == (InventorySlot?)2 && PlayerBackpackManager.TryGetBackpack(val.Owner, ref val2) && val2 != null && val2.TryGetBackpackItem((InventorySlot)2, ref val3) && val3 != null && val3.IsLoaded)
					{
						GearIDRange gearIDRange = val3.GearIDRange;
						if (((gearIDRange != null) ? new uint?(gearIDRange.GetChecksum()) : ((uint?)null)) == value.GearChecksum && !(val3.GearIDRange.PublicGearName != Marker(value.Spec)))
						{
							Vector3 val4 = default(Vector3);
							((Vector3)(ref val4))..ctor(shot.VX, shot.VY, shot.VZ);
							reason = "invalid aim direction";
							if (!float.IsFinite(((Vector3)(ref val4)).sqrMagnitude) || ((Vector3)(ref val4)).sqrMagnitude < 0.9f || ((Vector3)(ref val4)).sqrMagnitude > 1.1f)
							{
								return false;
							}
							Vector3 val5 = default(Vector3);
							((Vector3)(ref val5))..ctor(shot.OX, shot.OY, shot.OZ);
							LauncherLoan launcherLoan = value;
							Guid loan = shot.Loan;
							uint sequence = shot.Sequence;
							float time = Time.time;
							Vector3 val6 = val5 - ((Agent)val).EyePosition;
							if (!launcherLoan.TrySchedule(loan, sequence, time, ((Vector3)(ref val6)).sqrMagnitude, out delay, out reason))
							{
								return false;
							}
							shot.OX = ((Agent)val).EyePosition.x;
							shot.OY = ((Agent)val).EyePosition.y;
							shot.OZ = ((Agent)val).EyePosition.z;
							GrenadeSpec grenade = value.Spec.Grenade;
							Vector3 val7 = ((Vector3)(ref val4)).normalized * grenade.Speed + Vector3.up * 1.5f;
							shot.VX = val7.x;
							shot.VY = val7.y;
							shot.VZ = val7.z;
							shot.Damage = grenade.Damage;
							shot.Radius = grenade.Radius;
							return true;
						}
					}
				}
			}
			return false;
		}

		internal static void ShowRack(OrdnancePresentation p)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (!UnavailableRacks.Remove((p.SummonerLookup, p.ThrowId)))
			{
				Rack rack = new Rack
				{
					Summoner = p.SummonerLookup,
					ThrowId = p.ThrowId,
					P = (HeavyParameters)p.Parameters,
					Id = p.Definition.Id,
					Definition = p.Definition,
					Fallback = p.Position,
					Presented = true
				};
				Racks.Add(rack);
				rack.Claim = AddClaimable(rack, rack.Pod = OrdnanceDelivery.StartPod(p), p.Position + Vector3.up * 0.4f, OrdnanceDelivery.PodContentsDelay(p.Arrival));
			}
		}

		private static OrdnanceClaimable AddClaimable(Rack rack, OrdnancePod? pod, Vector3 position, float openAfter)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			ClaimableSpec val = new ClaimableSpec
			{
				Prompt = () => RackPrompt(rack),
				HostCheck = (PlayerAgent agent) => HostCheck(rack, agent),
				Install = delegate(ClaimInstall claim)
				{
					Install(rack, claim);
				},
				Cancelled = delegate
				{
					Phase phase = _phase;
					if ((uint)(phase - 1) <= 1u)
					{
						Abort();
					}
				},
				HostVerify = (PlayerAgent agent) => Verify(rack, agent),
				Committed = delegate(ClaimResult result)
				{
					//IL_0006: Unknown result type (might be due to invalid IL or missing references)
					Committed(rack, result);
				},
				Opened = delegate
				{
					OpenRack(rack);
				},
				Removed = delegate
				{
					RemoveRack(rack);
				}
			};
			if (pod == null)
			{
				return OrdnanceDelivery.AddClaimable(rack.Summoner, rack.ThrowId, position, openAfter, val);
			}
			rack.Bay = (int)(rack.ThrowId % (uint)pod.WeaponBayCount);
			return pod.AddClaimable(rack.Bay, val);
		}

		private static void OpenRack(Rack rack)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			if (rack.Taken || (Object)(object)rack.Look != (Object)null)
			{
				return;
			}
			ModelHandle val = ArsenalArt.Weapon(rack.P.Spec.Model);
			OrdnancePod? pod = rack.Pod;
			OrdnancePodBay? val2 = ((pod != null) ? pod.WeaponBayAt(rack.Bay) : ((OrdnancePodBay?)null));
			if (val == null)
			{
				return;
			}
			if (val2.HasValue)
			{
				OrdnancePodBay valueOrDefault = val2.GetValueOrDefault();
				GameObject val3 = val.Spawn(((OrdnancePodBay)(ref valueOrDefault)).Position, ((OrdnancePodBay)(ref valueOrDefault)).Rotation, (Transform)null);
				if (!((Object)(object)val3 == (Object)null))
				{
					float num = default(float);
					float num2 = default(float);
					rack.Look = ((OrdnancePodBay)(ref valueOrDefault)).Fit(val3, ArsenalArt.FrontToForward, ref num, ref num2);
				}
			}
			else
			{
				GameObject val4 = val.Spawn(rack.Fallback + Vector3.up * 0.15f, ArsenalArt.FrontToForward, (Transform)null);
				if (!((Object)(object)val4 == (Object)null))
				{
					((Object)val4).name = "ForgeOrdnanceArsenal.Rack";
					rack.Look = val4;
				}
			}
		}

		internal static void HostArrived(OrdnanceExecution e)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			ulong summoner = e.SummonerLookup;
			uint throwId = e.ThrowId;
			HeavyParameters heavyParameters = (HeavyParameters)e.Parameters;
			HeavyWeaponSpec spec = heavyParameters.Spec;
			uint gearId = heavyParameters.GearId;
			float x = e.Position.x;
			float y = e.Position.y;
			float z = e.Position.z;
			Quaternion rotation = e.Rotation;
			byte[] record = Wire.Rack(new RackRecord(spec, gearId, x, y, z, ((Quaternion)(ref rotation)).eulerAngles.y, e.Definition.Id));
			OrdnanceDelivery.HostAfter(OrdnanceDelivery.PodContentsDelay(e.Arrival), (Action)delegate
			{
				if (!OrdnanceApi.SendState("arsenal.HeavyWeapon", summoner, throwId, (byte)7, record))
				{
					OrdnanceApi.SendData("arsenal.HeavyWeapon", summoner, throwId, (byte)10, Array.Empty<byte>());
					OrdnanceDelivery.HostReportPodEmpty(summoner, throwId);
				}
				else
				{
					OrdnanceDelivery.HostOpenClaim(summoner, throwId);
				}
			});
		}

		private static string? HostCheck(Rack rack, PlayerAgent agent)
		{
			if (HasBorrowedGear(agent) || ((Object)(object)agent.Owner != (Object)null && Loans.HasLoan(agent.Owner.Lookup)))
			{
				return "the requester is already borrowing a heavy weapon";
			}
			try
			{
				LoanedGun.Require(BorrowedRange(rack.P));
			}
			catch (Exception ex)
			{
				Plugin.Error("Host refused unloaded heavy weapon effects: " + ex);
				return "the host has not loaded the heavy weapon's effects";
			}
			return null;
		}

		private static bool HasBorrowedGear(PlayerAgent agent)
		{
			PlayerBackpack val = default(PlayerBackpack);
			BackpackItem val2 = default(BackpackItem);
			if ((Object)(object)agent.Owner != (Object)null && PlayerBackpackManager.TryGetBackpack(agent.Owner, ref val) && val != null && val.TryGetBackpackItem((InventorySlot)2, ref val2) && val2 != null)
			{
				GearIDRange gearIDRange = val2.GearIDRange;
				string model;
				return TryMarker((gearIDRange != null) ? gearIDRange.PublicGearName : null, out model);
			}
			return false;
		}

		private static void Install(Rack rack, ClaimInstall claim)
		{
			if (_phase != Phase.None)
			{
				claim.Failed();
				return;
			}
			_claim = claim;
			try
			{
				BeginLoan(rack.P);
			}
			catch (Exception ex)
			{
				Plugin.Error("Heavy weapon installation refused: " + ex);
				Abort();
			}
		}

		private static ClaimVerdict Verify(Rack rack, PlayerAgent agent)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected I4, but got Unknown
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			PlayerBackpack val = default(PlayerBackpack);
			BackpackItem val2 = default(BackpackItem);
			if (PlayerBackpackManager.TryGetBackpack(agent.Owner, ref val) && val != null && val.TryGetBackpackItem((InventorySlot)2, ref val2) && val2 != null && val2.IsLoaded && !((Object)(object)val2.Instance == (Object)null) && !((Il2CppObjectBase)val2.Instance).WasCollected)
			{
				GearIDRange gearIDRange = val2.GearIDRange;
				if (!(((gearIDRange != null) ? gearIDRange.PublicGearName : null) != Marker(rack.P.Spec)))
				{
					GearIDRange val3 = BorrowedRange(rack.P);
					if (val2.GearIDRange.GetChecksum() != val3.GetChecksum())
					{
						return ClaimVerdict.Wait;
					}
					LauncherLoan launcherLoan = null;
					if (rack.P.Spec.Grenade != null)
					{
						GearCategoryDataBlock block = GameDataBlockBase<GearCategoryDataBlock>.GetBlock(val3.GetCompID((eGearComponent)2));
						uint num;
						if (block == null)
						{
							num = 0u;
						}
						else
						{
							eWeaponFireMode val4 = (eWeaponFireMode)val3.GetCompID((eGearComponent)1);
							num = (int)val4 switch
							{
								0 => block.SemiArchetype, 
								1 => block.BurstArchetype, 
								2 => block.AutoArchetype, 
								3 => block.SemiBurstArchetype, 
								_ => 0u, 
							};
						}
						ArchetypeDataBlock block2 = GameDataBlockBase<ArchetypeDataBlock>.GetBlock(num);
						InventorySlotAmmo specialAmmo = val.AmmoStorage.SpecialAmmo;
						ArchetypeTuning archetypeTuning = ((block2 == null) ? null : rack.P.Spec.Tuning.Over(Readings(block2)));
						if (archetypeTuning == null || !archetypeTuning.Valid())
						{
							return ClaimVerdict.Refuse("host gear has invalid ammunition or cadence");
						}
						double num2 = archetypeTuning.Rounds(rack.P.Spec.AmmoRatio, specialAmmo.AmmoMaxCap);
						if (!(num2 > 0.0) || !(num2 <= 2147483647.0))
						{
							return ClaimVerdict.Refuse("host gear has invalid ammunition or cadence");
						}
						launcherLoan = new LauncherLoan(rack.P.Spec, val3.GetChecksum(), (int)num2, archetypeTuning.ShotDelay, Time.time);
					}
					rack.PendingLauncher = launcherLoan;
					return ClaimVerdict.Accept(launcherLoan?.Identity ?? Guid.Empty);
				}
			}
			return ClaimVerdict.Wait;
		}

		private static void Committed(Rack rack, ClaimResult result)
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected I4, but got Unknown
			if (SNet.IsMaster)
			{
				Loans.Commit(rack.Summoner, rack.ThrowId, ((ClaimResult)(ref result)).Player, ((ClaimResult)(ref result)).Attempt);
				if (rack.PendingLauncher != null)
				{
					Launchers[((ClaimResult)(ref result)).Player] = rack.PendingLauncher;
				}
				rack.PendingLauncher = null;
				OrdnanceApi.ClearState("arsenal.HeavyWeapon", rack.Summoner, rack.ThrowId);
				OrdnanceDelivery.HostReportPodEmpty(rack.Summoner, rack.ThrowId);
			}
			Destroy(rack.Look);
			rack.Look = null;
			if (((ClaimResult)(ref result)).Ours && _phase == Phase.AwaitingCommit)
			{
				_launcherIdentity = ((ClaimResult)(ref result)).Token;
				_phase = Phase.Active;
				_loan = new HeavyLoan(Time.time);
				PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent();
				if ((Object)(object)localPlayerAgent != (Object)null)
				{
					ArsenalAudio.PlayAttached("weapon.handover", ((Component)localPlayerAgent).gameObject, (int)((Agent)localPlayerAgent).DimensionIndex, ((Agent)localPlayerAgent).IsLocallyOwned);
				}
			}
		}

		internal static void OnLoanNotice(ulong sender, LoanNotice m)
		{
			if (SNet.IsMaster && ArsenalNetwork.IsPlayer(sender))
			{
				if (m.Action == LoanAction.Stashed)
				{
					Loans.Stash(m.Summoner, m.ThrowId, sender, m.Attempt);
				}
				else if (Loans.Release(m.Summoner, m.ThrowId, sender, m.Attempt))
				{
					Launchers.Remove(sender);
				}
			}
		}

		internal static void OnData(OrdnancePayloadData d)
		{
			if (d.Tag == 10)
			{
				if (d.Data.Length != 0)
				{
					return;
				}
				bool flag = true;
				for (int num = Racks.Count - 1; num >= 0; num--)
				{
					if (Racks[num].Summoner == d.SummonerLookup && Racks[num].ThrowId == d.ThrowId)
					{
						Rack rack = Racks[num];
						flag &= !rack.Presented;
						OrdnanceClaimable? claim = rack.Claim;
						if (claim != null)
						{
							claim.Withdraw();
						}
						RemoveRack(rack);
					}
				}
				if (flag)
				{
					UnavailableRacks.Add((d.SummonerLookup, d.ThrowId));
				}
			}
			else
			{
				if (d.Tag != 7)
				{
					return;
				}
				if (!Wire.TryRack(d.Data, out var r))
				{
					if (Wire.UnknownRackModel(d.Data, out var hash))
					{
						Plugin.Report("A heavy weapon rack was dropped: its model hash " + hash.ToString("x8") + " is not one of this install's heavy weapon models (the host's heavy weapon packs must be installed here too).");
					}
				}
				else if (!Racks.Exists((Rack rack2) => rack2.Summoner == d.SummonerLookup && rack2.ThrowId == d.ThrowId))
				{
					RackFromState(d, in r);
				}
			}
		}

		private static void RemoveRack(Rack rack)
		{
			Destroy(rack.Look);
			rack.Look = null;
			Racks.Remove(rack);
		}

		private static void RackFromState(OrdnancePayloadData d, in RackRecord record)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(record.X, record.Y, record.Z);
			Rack rack = new Rack
			{
				Summoner = d.SummonerLookup,
				ThrowId = d.ThrowId,
				P = new HeavyParameters(record.Spec, record.GearId),
				Id = record.Id,
				Fallback = val
			};
			Racks.Add(rack);
			OrdnancePod val2 = (rack.Pod = OrdnanceDelivery.FindPod(d.SummonerLookup, d.ThrowId));
			rack.Claim = AddClaimable(rack, val2, val + Vector3.up * 0.4f, 0f);
			if (val2 == null)
			{
				OpenRack(rack);
			}
		}

		internal static void Tick(float now)
		{
			if (SNet.IsMaster && Loans.Any)
			{
				Loans.ReconcileLoans(HostLoanStillPresent, ReleaseHostLoan);
			}
			try
			{
				LocalLoan(now);
			}
			catch (Exception ex)
			{
				Plugin.Error("Heavy weapon loan failed: " + ex);
				Abort();
			}
			if (now >= _nextSkinScan)
			{
				_nextSkinScan = now + 0.5f;
				try
				{
					ScanSkins();
				}
				catch (E