Decompiled source of ForgeWeapon EnergyLab v1.0.0

ForgeWeaponEnergyLab.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using AIGraph;
using Agents;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using Enemies;
using FX_EffectSystem;
using ForgeAudio;
using ForgeCore;
using ForgeUI;
using ForgeVisual;
using ForgeWeaponEnergyLab.Api;
using ForgeWeaponEnergyLab.Native;
using ForgeWeaponEnergyLab.Presentation;
using GTFO.API;
using GameData;
using Gear;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppInterop.Runtime.Runtime;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Localization;
using MTFO.API;
using Microsoft.CodeAnalysis;
using Player;
using RootMotion.FinalIK;
using SNetwork;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ForgeWeaponEnergyLab")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+66ec6b43e1a1603bac84a4b9a226674300b2fc7d")]
[assembly: AssemblyProduct("ForgeWeaponEnergyLab")]
[assembly: AssemblyTitle("ForgeWeaponEnergyLab")]
[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]
	[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 ForgeWeaponEnergyLab
{
	internal sealed class BudgetExhausted : InvalidOperationException
	{
		public BudgetExhausted(string message)
			: base(message)
		{
		}
	}
	internal static class EnergyConfiguration
	{
		internal static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
			WriteIndented = true
		};

		private static readonly Dictionary<string, string> Descriptions = new Dictionary<string, string>
		{
			["magazine"] = "Whole rounds in the magazine (1..10000).",
			["ammoPerRound"] = "Native reserve units per round (>0).",
			["range"] = "Chain Arc first-hit distance or flame length in metres (>0); the other weapons have no range limit.",
			["interval"] = "Seconds between discharges (0.01..60).",
			["damage"] = "Base direct, explosion or field-pulse damage; minimum-charge damage for orbs.",
			["ammoCost"] = "Whole magazine rounds per discharge; must be 1 for every weapon, at every charge power.",
			["stagger"] = "Native stagger multiplier for direct/selected hits (0..100).",
			["precision"] = "Native precision multiplier for direct/selected hits (0..100).",
			["reloadTime"] = "Native reload duration in seconds (0.01..120).",
			["equipTime"] = "Native equip transition duration in seconds (0.01..120).",
			["aimTime"] = "Native aim transition duration in seconds (0.01..120).",
			["targets"] = "Chain recipients including the first hit (1..4 packet budget).",
			["jumpRange"] = "Maximum chain hop distance in metres (>0).",
			["damageRetention"] = "Damage fraction retained after each chain hop (0..1).",
			["arcLifetime"] = "Arc hit-feedback duration in seconds (>0).",
			["hopDelay"] = "Visual hop delay in seconds (0..10); damage is immediate.",
			["minimumCharge"] = "Minimum hold seconds to fire (>=0.1).",
			["fullCharge"] = "Hold seconds for full power (>minimumCharge); holding longer keeps full power until release.",
			["botChargePower"] = "Power of a native bot shot (0..1).",
			["speed"] = "Projectile speed in metres/second (>0).",
			["fullSpeed"] = "Full-charge projectile speed in metres/second (>0).",
			["bodyRadius"] = "Projectile collision radius in metres (>0).",
			["fullBodyRadius"] = "Full-charge collision radius in metres (>0).",
			["travelDistance"] = "Minimum-charge flight distance in metres (>0).",
			["fullTravelDistance"] = "Full-charge flight distance in metres (>0).",
			["fullDamage"] = "Full-charge damage (>=damage).",
			["activeProjectiles"] = "Per-definition active limit within shared slots (discs 1..3, others 1..4).",
			["blastRadius"] = "Explosion radius in metres with native linear falloff (>0).",
			["fullBlastRadius"] = "Full-charge explosion radius in metres (>0).",
			["explosionLifetime"] = "Explosion slot/feedback duration in seconds (>0).",
			["noiseRadius"] = "Native explosion noise radius in metres (0..1000).",
			["explosionForce"] = "Native explosion receiver force (0..1000).",
			["arcRadius"] = "Minimum-charge pulse radius in metres (>0).",
			["fullArcRadius"] = "Full-charge pulse radius in metres (>0).",
			["pulseInterval"] = "Seconds between damage pulses (0.01..60).",
			["pulseDamageFraction"] = "Fraction of orb direct damage applied by a pulse (0..1).",
			["pulseStagger"] = "Native pulse stagger multiplier (0..100).",
			["pulseTargets"] = "Recipients per flight pulse (1..3 effect budget).",
			["fadeLifetime"] = "Fade/slot duration after flight in seconds (>0).",
			["bounces"] = "Allowed wall bounces (0..128).",
			["lifetime"] = "Disc flight or gravity field duration in seconds (>0).",
			["startRadius"] = "Flame cone radius at the muzzle in metres (>0).",
			["radiusPerMetre"] = "Flame cone widening per metre (0..10).",
			["flightLifetime"] = "Gravity flight seconds before field activation (>0).",
			["gravity"] = "Downward projectile acceleration in metres/second squared (0..1000).",
			["radius"] = "Gravity outer damage/pull radius in metres (>coreRadius).",
			["coreRadius"] = "Full-damage inner radius in metres (>0, <radius).",
			["collapseDuration"] = "Final field seconds without damage/pull (>0, <lifetime).",
			["growthDuration"] = "Visible field growth duration in seconds (>0).",
			["edgeDamageFraction"] = "Damage fraction at the outer field edge (0..1).",
			["pullSpeed"] = "Attraction speed in metres/second (0..1000).",
			["queryInterval"] = "Maximum target re-query interval in seconds (0.01..60)."
		};

		internal const string OverridesFile = "tuning-overrides.json";

		internal const string DefaultsFile = "tuning-defaults.json";

		private static readonly string[] RootFields = new string[1] { "weapons" };

		internal static JsonNode TuningJson(EnergyTuning tuning)
		{
			return JsonSerializer.SerializeToNode(tuning, tuning.GetType(), JsonOptions);
		}

		internal static EnergyTuning DefaultTuning(EnergyMode mode)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			return mode switch
			{
				EnergyMode.Beam => new BeamTuning(), 
				EnergyMode.ArcChain => new ArcChainTuning(), 
				EnergyMode.PlasmaBlast => new PlasmaBlastTuning(), 
				EnergyMode.PlasmaArc => new PlasmaArcTuning(), 
				EnergyMode.Blast => new BlastTuning(), 
				EnergyMode.Disc => new DiscTuning(), 
				EnergyMode.Flame => new FlameTuning(), 
				EnergyMode.BlackHole => new BlackHoleTuning(), 
				_ => throw new StrictJsonException("Mode", "must be a firing behaviour, not Off."), 
			};
		}

		internal static EnergyTuning MergeTuning(EnergyTuning current, JsonElement item, string field)
		{
			Type type = current.GetType();
			PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
			StrictJson.OnlyKnown(item, field + ".", properties.Select((PropertyInfo p) => JsonOptions.PropertyNamingPolicy.ConvertName(p.Name)).ToArray(), (string[])null, (string)null);
			JsonObject jsonObject = TuningJson(current).AsObject();
			PropertyInfo[] array = properties;
			foreach (PropertyInfo propertyInfo in array)
			{
				string text = JsonOptions.PropertyNamingPolicy.ConvertName(propertyInfo.Name);
				if (item.TryGetProperty(text, out var value))
				{
					double num2 = ((propertyInfo.PropertyType == typeof(float)) ? 3.4028234663852886E+38 : double.MaxValue);
					if (propertyInfo.PropertyType == typeof(int))
					{
						StrictJson.Integer(item, field + ".", text, (double?)null, -2147483648.0, 2147483647.0);
					}
					else
					{
						StrictJson.Number(item, field + ".", text, (double?)null, 0.0 - num2, num2);
					}
					jsonObject[text] = JsonNode.Parse(value.GetRawText());
				}
			}
			return (EnergyTuning)jsonObject.Deserialize(type, JsonOptions);
		}

		internal static WeaponDefinition[] Load(string directory, WeaponDefinition[] defaults, List<string> errors)
		{
			//IL_02fa: Expected O, but got Unknown
			//IL_047c: Expected O, but got Unknown
			string path = Path.Combine(directory, "tuning-overrides.json");
			WeaponDefinition[] array;
			try
			{
				Directory.CreateDirectory(directory);
				JsonObject jsonObject = new JsonObject();
				array = defaults;
				foreach (WeaponDefinition weaponDefinition in array)
				{
					jsonObject.Add(weaponDefinition.Id, TuningJson(weaponDefinition.Tuning));
				}
				File.WriteAllText(Path.Combine(directory, "tuning-defaults.json"), new JsonObject
				{
					["_note"] = "Reference only, rewritten on every launch. Copy the values you want to change into tuning-overrides.json.",
					["_descriptions"] = JsonSerializer.SerializeToNode(Descriptions, JsonOptions),
					["weapons"] = jsonObject
				}.ToJsonString(JsonOptions), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				if (!File.Exists(path))
				{
					using FileStream stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.None);
					using StreamWriter streamWriter = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
					streamWriter.Write(new JsonObject { ["weapons"] = new JsonObject() }.ToJsonString(JsonOptions));
				}
			}
			catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException) ? 1 : 0) != 0)
			{
				errors.Add(directory + ": could not write the tuning files: " + ex);
			}
			JsonDocument jsonDocument;
			try
			{
				jsonDocument = JsonDocument.Parse(File.ReadAllText(path));
			}
			catch (JsonException ex2)
			{
				errors.Add($"{"tuning-overrides.json"}: invalid JSON at line {ex2.LineNumber + 1}, byte {ex2.BytePositionInLine + 1}; no override applied.");
				array = defaults;
				goto IL_05b5;
			}
			catch (IOException value)
			{
				errors.Add($"{"tuning-overrides.json"}: could not be read: {value}; no override applied.");
				array = defaults;
				goto IL_05b5;
			}
			using (jsonDocument)
			{
				JsonElement jsonElement;
				try
				{
					StrictJson.UniqueMembers(jsonDocument.RootElement, "");
					StrictJson.OnlyKnown(jsonDocument.RootElement, "", RootFields, (string[])null, (string)null);
					jsonElement = StrictJson.Object(jsonDocument.RootElement, "", "weapons");
				}
				catch (StrictJsonException ex3)
				{
					StrictJsonException ex4 = ex3;
					errors.Add($"{"tuning-overrides.json"}: {ex4.Field}: {((Exception)(object)ex4).Message} No override applied.");
					array = defaults;
					goto end_IL_02b8;
				}
				HashSet<string> hashSet = new HashSet<string>(defaults.Select((WeaponDefinition d) => d.Id), StringComparer.Ordinal);
				foreach (JsonProperty item in jsonElement.EnumerateObject())
				{
					if (!hashSet.Contains(item.Name))
					{
						errors.Add("tuning-overrides.json: weapons." + item.Name + ": no weapon with this id is loaded (is its package installed?); skipped.");
					}
				}
				WeaponDefinition[] array2 = new WeaponDefinition[defaults.Length];
				for (int num = 0; num < defaults.Length; num++)
				{
					WeaponDefinition weaponDefinition2 = (array2[num] = defaults[num]);
					if (jsonElement.TryGetProperty(weaponDefinition2.Id, out var value2))
					{
						string text = "weapons." + weaponDefinition2.Id;
						try
						{
							EnergyTuning energyTuning = MergeTuning(weaponDefinition2.Tuning, value2, text);
							ValidateTuning(weaponDefinition2.Mode, energyTuning, text);
							array2[num] = weaponDefinition2 with
							{
								Tuning = energyTuning
							};
						}
						catch (StrictJsonException ex5)
						{
							StrictJsonException ex6 = ex5;
							errors.Add($"{"tuning-overrides.json"}: {ex6.Field}: {((Exception)(object)ex6).Message} This weapon keeps its default tuning.");
						}
						catch (InvalidDataException ex7)
						{
							errors.Add("tuning-overrides.json: " + ex7.Message + ". This weapon keeps its default tuning.");
						}
						catch (JsonException ex8)
						{
							errors.Add($"{"tuning-overrides.json"}: {text}: invalid number type at line {ex8.LineNumber + 1}. This weapon keeps its default tuning.");
						}
					}
				}
				array = array2;
				end_IL_02b8:;
			}
			goto IL_05b5;
			IL_05b5:
			return array;
		}

		internal static void ValidateDefinition(WeaponDefinition definition)
		{
			if (definition == null)
			{
				throw new ArgumentNullException("definition");
			}
			if (definition.Id == null || definition.Id.Length > 128 || !Regex.IsMatch(definition.Id, "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*){2,}$"))
			{
				Fail(definition.Id + ".id", "expected a lowercase namespaced id such as author.mod.weapon (at most 128 characters)");
			}
			if (string.IsNullOrWhiteSpace(definition.Name) || definition.Name.Length > 128)
			{
				Fail(definition.Id + ".name", "expected a nonempty name of at most 128 characters");
			}
			string description = definition.Description;
			if (description != null && (string.IsNullOrWhiteSpace(description) || description.Length > 512))
			{
				Fail(definition.Id + ".description", "expected null or a nonempty text of at most 512 characters");
			}
			WeaponSlot slot = definition.Slot;
			if ((uint)slot > 1u)
			{
				Fail(definition.Id + ".slot", "expected Main or Special");
			}
			if (definition.Model == null || string.IsNullOrWhiteSpace(definition.Model.Key) || definition.Model.Key.Length > 128)
			{
				Fail(definition.Id + ".model.key", "expected a prefab key");
			}
			if (string.IsNullOrWhiteSpace(definition.Model.BundlePath) || !Path.IsPathFullyQualified(definition.Model.BundlePath))
			{
				Fail(definition.Id + ".model.bundlePath", "expected an absolute bundle path");
			}
			ValidateTuning(definition.Mode, definition.Tuning, definition.Id + ".tuning");
		}

		internal static void ValidateTuning(EnergyMode mode, EnergyTuning t, string path)
		{
			if (mode switch
			{
				EnergyMode.Beam => (t is BeamTuning) ? 1 : 0, 
				EnergyMode.ArcChain => (t is ArcChainTuning) ? 1 : 0, 
				EnergyMode.PlasmaBlast => (t is PlasmaBlastTuning) ? 1 : 0, 
				EnergyMode.PlasmaArc => (t is PlasmaArcTuning) ? 1 : 0, 
				EnergyMode.Blast => (t is BlastTuning) ? 1 : 0, 
				EnergyMode.Disc => (t is DiscTuning) ? 1 : 0, 
				EnergyMode.Flame => (t is FlameTuning) ? 1 : 0, 
				EnergyMode.BlackHole => (t is BlackHoleTuning) ? 1 : 0, 
				_ => 0, 
			} == 0)
			{
				Fail(path, "tuning record type must match a firing behaviour");
			}
			Check("Magazine", t.Magazine, 1.0, 10000.0);
			Check("AmmoCost", t.AmmoCost, 1.0, t.Magazine);
			if (t.AmmoCost != 1)
			{
				Fail(path + ".ammoCost", "native fire spends exactly one magazine round at every charge power");
			}
			Check("AmmoPerRound", t.AmmoPerRound, 0.001, 1000000.0);
			Check("Interval", t.Interval, 0.01, 60.0);
			Check("Damage", t.Damage, 0.0, 1000000.0);
			Check("Stagger", t.Stagger, 0.0, 100.0);
			Check("Precision", t.Precision, 0.0, 100.0);
			Check("ReloadTime", t.ReloadTime, 0.01, 120.0);
			Check("EquipTime", t.EquipTime, 0.01, 120.0);
			Check("AimTime", t.AimTime, 0.01, 120.0);
			if (!(t is ArcChainTuning arcChainTuning))
			{
				if (!(t is PlasmaTuning plasmaTuning))
				{
					if (!(t is BlastTuning blastTuning))
					{
						if (!(t is DiscTuning discTuning))
						{
							if (!(t is FlameTuning flameTuning))
							{
								if (t is BlackHoleTuning blackHoleTuning)
								{
									Check("Speed", blackHoleTuning.Speed, 0.01, 1000.0);
									Check("BodyRadius", blackHoleTuning.BodyRadius, 0.01, 100.0);
									Check("FlightLifetime", blackHoleTuning.FlightLifetime, 0.01, 120.0);
									Check("Gravity", blackHoleTuning.Gravity, 0.0, 1000.0);
									Check("Radius", blackHoleTuning.Radius, 0.02, 100.0);
									Check("CoreRadius", blackHoleTuning.CoreRadius, 0.01, (double)blackHoleTuning.Radius - 0.01);
									Check("Lifetime", blackHoleTuning.Lifetime, 0.02, 120.0);
									Check("CollapseDuration", blackHoleTuning.CollapseDuration, 0.01, (double)blackHoleTuning.Lifetime - 0.01);
									Check("GrowthDuration", blackHoleTuning.GrowthDuration, 0.01, 120.0);
									Check("PulseInterval", blackHoleTuning.PulseInterval, 0.01, 60.0);
									Check("EdgeDamageFraction", blackHoleTuning.EdgeDamageFraction, 0.0, 1.0);
									Check("PullSpeed", blackHoleTuning.PullSpeed, 0.0, 1000.0);
									Check("QueryInterval", blackHoleTuning.QueryInterval, 0.01, 60.0);
								}
							}
							else
							{
								Check("Range", flameTuning.Range, 0.01, 1000.0);
								Check("StartRadius", flameTuning.StartRadius, 0.01, 100.0);
								Check("RadiusPerMetre", flameTuning.RadiusPerMetre, 0.0, 10.0);
							}
						}
						else
						{
							Check("Speed", discTuning.Speed, 0.01, 1000.0);
							Check("BodyRadius", discTuning.BodyRadius, 0.01, 100.0);
							Check("Bounces", discTuning.Bounces, 0.0, 128.0);
							Check("Lifetime", discTuning.Lifetime, 0.01, 120.0);
							Check("ActiveProjectiles", discTuning.ActiveProjectiles, 1.0, 3.0);
						}
					}
					else
					{
						Check("Speed", blastTuning.Speed, 0.01, 1000.0);
						Check("BlastRadius", blastTuning.BlastRadius, 0.01, 100.0);
						Check("ExplosionLifetime", blastTuning.ExplosionLifetime, 0.01, 120.0);
						Check("NoiseRadius", blastTuning.NoiseRadius, 0.0, 1000.0);
						Check("ExplosionForce", blastTuning.ExplosionForce, 0.0, 1000.0);
					}
				}
				else
				{
					Check("MinimumCharge", plasmaTuning.MinimumCharge, 0.1, 120.0);
					Check("FullCharge", plasmaTuning.FullCharge, plasmaTuning.MinimumCharge + 0.01, 120.0);
					Check("BotChargePower", plasmaTuning.BotChargePower, 0.0, 1.0);
					Check("Speed", plasmaTuning.Speed, 0.01, 1000.0);
					Check("FullSpeed", plasmaTuning.FullSpeed, 0.01, 1000.0);
					Check("BodyRadius", plasmaTuning.BodyRadius, 0.01, 100.0);
					Check("FullBodyRadius", plasmaTuning.FullBodyRadius, 0.01, 100.0);
					Check("TravelDistance", plasmaTuning.TravelDistance, 0.01, 1000.0);
					Check("FullTravelDistance", plasmaTuning.FullTravelDistance, 0.01, 1000.0);
					Check("FullDamage", plasmaTuning.FullDamage, plasmaTuning.Damage, 1000000.0);
					Check("ActiveProjectiles", plasmaTuning.ActiveProjectiles, 1.0, 4.0);
					if (plasmaTuning is PlasmaBlastTuning plasmaBlastTuning)
					{
						Check("BlastRadius", plasmaBlastTuning.BlastRadius, 0.01, 100.0);
						Check("FullBlastRadius", plasmaBlastTuning.FullBlastRadius, 0.01, 100.0);
						Check("ExplosionLifetime", plasmaBlastTuning.ExplosionLifetime, 0.01, 120.0);
						Check("NoiseRadius", plasmaBlastTuning.NoiseRadius, 0.0, 1000.0);
						Check("ExplosionForce", plasmaBlastTuning.ExplosionForce, 0.0, 1000.0);
					}
					if (plasmaTuning is PlasmaArcTuning plasmaArcTuning)
					{
						Check("ArcRadius", plasmaArcTuning.ArcRadius, 0.01, 100.0);
						Check("FullArcRadius", plasmaArcTuning.FullArcRadius, 0.01, 100.0);
						Check("PulseInterval", plasmaArcTuning.PulseInterval, 0.01, 60.0);
						Check("PulseDamageFraction", plasmaArcTuning.PulseDamageFraction, 0.0, 1.0);
						Check("PulseStagger", plasmaArcTuning.PulseStagger, 0.0, 100.0);
						Check("PulseTargets", plasmaArcTuning.PulseTargets, 1.0, 3.0);
						Check("FadeLifetime", plasmaArcTuning.FadeLifetime, 0.01, 120.0);
					}
				}
			}
			else
			{
				Check("Range", arcChainTuning.Range, 0.01, 1000.0);
				Check("Targets", arcChainTuning.Targets, 1.0, 4.0);
				Check("JumpRange", arcChainTuning.JumpRange, 0.01, 1000.0);
				Check("DamageRetention", arcChainTuning.DamageRetention, 0.0, 1.0);
				Check("ArcLifetime", arcChainTuning.ArcLifetime, 0.01, 120.0);
				Check("HopDelay", arcChainTuning.HopDelay, 0.0, 10.0);
			}
			void Check(string name, double number, double min, double max)
			{
				if (!double.IsFinite(number) || number + 1E-07 < min || number - 1E-07 > max)
				{
					Fail(path + "." + JsonOptions.PropertyNamingPolicy.ConvertName(name), FormattableString.Invariant($"expected {min}..{max}"));
				}
			}
		}

		private static void Fail(string field, string reason)
		{
			throw new InvalidDataException(field + ": " + reason);
		}
	}
	internal static class EnergyContent
	{
		internal const string FolderName = "ForgeEnergyLab";

		private static readonly string[] Fields = new string[7] { "Id", "Name", "Description", "Slot", "Mode", "Model", "Tuning" };

		private static readonly string[] ModelFields = new string[2] { "Key", "Bundle" };

		internal static List<(WeaponDefinition Definition, string Folder)> Load(IEnumerable<string> folders, List<string> errors)
		{
			List<(WeaponDefinition, string)> list = new List<(WeaponDefinition, string)>();
			foreach (string folder in folders)
			{
				string[] files;
				try
				{
					files = Directory.GetFiles(folder, "*.json");
				}
				catch (Exception ex)
				{
					errors.Add(folder + ": could not list the definition files: " + ex);
					continue;
				}
				Array.Sort(files, (IComparer<string>?)StringComparer.Ordinal);
				string[] array = files;
				foreach (string text in array)
				{
					string json;
					try
					{
						json = File.ReadAllText(text);
					}
					catch (Exception ex2)
					{
						errors.Add(text + ": could not be read: " + ex2);
						continue;
					}
					foreach (WeaponDefinition item in Parse(text, json, errors))
					{
						if (EnergyLab.Register(item, out string error))
						{
							list.Add((item, folder));
						}
						else
						{
							errors.Add(text + ": " + error);
						}
					}
				}
			}
			return list;
		}

		internal static List<WeaponDefinition> Parse(string file, string json, List<string> errors)
		{
			//IL_0199: Expected O, but got Unknown
			List<WeaponDefinition> list = new List<WeaponDefinition>();
			JsonDocument jsonDocument;
			try
			{
				jsonDocument = JsonDocument.Parse(json);
			}
			catch (JsonException ex)
			{
				errors.Add($"{file}: invalid JSON at line {ex.LineNumber + 1}, byte {ex.BytePositionInLine + 1}.");
				return list;
			}
			using (jsonDocument)
			{
				if (jsonDocument.RootElement.ValueKind != JsonValueKind.Array)
				{
					errors.Add(file + ": expected an array of weapon definitions.");
					return list;
				}
				string directoryName = Path.GetDirectoryName(Path.GetFullPath(file));
				int num = 0;
				foreach (JsonElement item in jsonDocument.RootElement.EnumerateArray())
				{
					JsonElement value2;
					string value = ((item.ValueKind == JsonValueKind.Object && item.TryGetProperty("Id", out value2) && value2.ValueKind == JsonValueKind.String) ? value2.GetString() : ("[" + num + "]"));
					num++;
					try
					{
						list.Add(ParseOne(item, directoryName));
					}
					catch (StrictJsonException ex2)
					{
						StrictJsonException ex3 = ex2;
						errors.Add($"{file}: {value}{((ex3.Field.Length == 0) ? "" : ("." + ex3.Field))}: {((Exception)(object)ex3).Message}");
					}
				}
				return list;
			}
		}

		private static WeaponDefinition ParseOne(JsonElement item, string folder)
		{
			StrictJson.UniqueMembers(item, "");
			StrictJson.OnlyKnown(item, "", Fields, (string[])null, (string)null);
			string? id = Text(item, "", "Id", required: true);
			string name = Text(item, "", "Name", required: true);
			string description = Text(item, "", "Description", required: false);
			WeaponSlot slot = StrictJson.Enum<WeaponSlot>(item, "", "Slot");
			EnergyMode mode = StrictJson.Enum<EnergyMode>(item, "", "Mode");
			EnergyTuning energyTuning = EnergyConfiguration.DefaultTuning(mode);
			JsonElement jsonElement = StrictJson.Object(item, "", "Model");
			StrictJson.OnlyKnown(jsonElement, "Model.", ModelFields, (string[])null, (string)null);
			string key = Text(jsonElement, "Model.", "Key", required: true);
			string bundle = Text(jsonElement, "Model.", "Bundle", required: true);
			if (item.TryGetProperty("Tuning", out var value))
			{
				energyTuning = EnergyConfiguration.MergeTuning(energyTuning, value, "Tuning");
			}
			return new WeaponDefinition(id, name, slot, mode, energyTuning, new WeaponModel(key, Bundle(folder, bundle)), description);
		}

		private static string? Text(JsonElement owner, string prefix, string field, bool required)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (!owner.TryGetProperty(field, out var value))
			{
				if (!required)
				{
					return null;
				}
				throw new StrictJsonException(prefix + field, "is required.");
			}
			if (value.ValueKind != JsonValueKind.String)
			{
				throw new StrictJsonException(prefix + field, "must be a string.");
			}
			return value.GetString();
		}

		private static string Bundle(string folder, string bundle)
		{
			//IL_009e: 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)
			if (string.IsNullOrWhiteSpace(bundle) || Path.IsPathRooted(bundle) || ((ReadOnlySpan<string>)bundle.Split('/', '\\')).Contains(".."))
			{
				throw new StrictJsonException("Model.Bundle", "must be a file path inside the definition's folder (not absolute, no '..').");
			}
			try
			{
				return Path.GetFullPath(Path.Combine(folder, bundle));
			}
			catch (Exception ex) when (((ex is ArgumentException || ex is NotSupportedException || ex is PathTooLongException) ? 1 : 0) != 0)
			{
				throw new StrictJsonException("Model.Bundle", "is not a usable path: " + ex.Message);
			}
		}
	}
	internal sealed class EnergyGear
	{
		internal readonly WeaponDefinition Definition;

		internal readonly uint GearId;

		internal readonly uint CategoryId;

		internal readonly uint ArchetypeId;

		internal readonly WeaponNumbers Numbers;

		internal readonly string ModelPath;

		internal EnergyMode Mode => Definition.Mode;

		internal string Name => Definition.Name;

		internal bool Main => Definition.Slot == WeaponSlot.Main;

		internal int Clip => Definition.Tuning.Magazine;

		internal float AmmoPerRound => Definition.Tuning.AmmoPerRound;

		internal string NativeName => "ForgeWeaponEnergyLab_" + Definition.Id;

		internal string ModelDigest { get; set; } = "";

		internal EnergyGear(WeaponDefinition definition)
		{
			Definition = definition;
			ModelPath = Path.GetFullPath(definition.Model.BundlePath);
			(uint, uint, uint) tuple = EnergyGears.Ids(definition.Id);
			GearId = tuple.Item1;
			CategoryId = tuple.Item2;
			ArchetypeId = tuple.Item3;
			Numbers = WeaponNumbers.Resolve(definition.Tuning);
		}
	}
	internal static class EnergyGears
	{
		private static readonly EnergyGear?[] Categories = new EnergyGear[65536];

		private static bool _frozen;

		internal static EnergyGear[] All { get; private set; } = Array.Empty<EnergyGear>();

		internal static (uint Gear, uint Category, uint Archetype) Ids(string id)
		{
			return (Gear: 0x20000000 | (Fnv.Hash32(id + "/gear") & 0x1FFFFFFF), Category: 0x8000 | (Fnv.Hash32(id + "/category") & 0x7FFF), Archetype: 0x20000000 | (Fnv.Hash32(id + "/archetype") & 0x1FFFFFFF));
		}

		internal static void Freeze(List<string> errors)
		{
			if (_frozen)
			{
				return;
			}
			_frozen = true;
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			List<EnergyGear> list = new List<EnergyGear>();
			EnergyGear[] array = EnergyLab.Freeze(errors);
			foreach (EnergyGear energyGear in array)
			{
				if (!dictionary.ContainsKey(energyGear.ModelPath))
				{
					FileInfo fileInfo = new FileInfo(energyGear.ModelPath);
					if (!fileInfo.Exists || fileInfo.Length < 1024 || fileInfo.Length > 134217728)
					{
						errors.Add(energyGear.Definition.Id + ".model.bundle: missing bundle or size outside 1 KiB..128 MiB: " + energyGear.ModelPath + "; this weapon is skipped.");
						continue;
					}
					using FileStream inputStream = File.OpenRead(energyGear.ModelPath);
					using SHA256 sHA = SHA256.Create();
					dictionary.Add(energyGear.ModelPath, Convert.ToHexString(sHA.ComputeHash(inputStream)));
				}
				energyGear.ModelDigest = dictionary[energyGear.ModelPath];
				list.Add(energyGear);
			}
			foreach (EnergyGear item in list)
			{
				Categories[item.CategoryId] = item;
			}
			All = list.ToArray();
		}

		internal static EnergyGear? ForCategory(uint categoryId)
		{
			if (categoryId > 65535)
			{
				return null;
			}
			return Categories[categoryId];
		}

		internal static EnergyMode ModeForCategory(uint categoryId)
		{
			return ForCategory(categoryId)?.Mode ?? EnergyMode.Off;
		}

		internal static bool IsSelection(uint categoryId, int mode)
		{
			if (categoryId != 0 || mode != 0)
			{
				return Match(categoryId, mode) != null;
			}
			return true;
		}

		internal static EnergyGear? Match(uint categoryId, int mode)
		{
			EnergyGear energyGear = ForCategory(categoryId);
			if (energyGear == null || energyGear.Mode != (EnergyMode)mode)
			{
				return null;
			}
			return energyGear;
		}
	}
	internal static class EnergyGearJson
	{
		internal const int SightComponent = 21;

		internal const uint ZoomSightPart = 25u;

		internal static string Build(string original, EnergyGear gear)
		{
			if (gear.CategoryId > 65535)
			{
				throw new InvalidOperationException("Gear category ID exceeds the native 16-bit packet limit.");
			}
			JsonObject jsonObject = JsonNode.Parse(original)?.AsObject() ?? throw new InvalidOperationException("Invalid native gear JSON.");
			jsonObject["Name"] = gear.Name;
			JsonObject jsonObject2 = jsonObject["Packet"]?.AsObject() ?? throw new InvalidOperationException("Native gear packet missing.");
			JsonObject obj = jsonObject2["Comps"]?.AsObject() ?? throw new InvalidOperationException("Native gear components missing.");
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			bool flag4 = false;
			foreach (KeyValuePair<string, JsonNode> item in obj)
			{
				item.Deconstruct(out var _, out var value);
				if (value is JsonObject jsonObject3)
				{
					switch (jsonObject3["c"]?.GetValue<int>())
					{
					case 2:
						jsonObject3["v"] = gear.CategoryId;
						flag = true;
						break;
					case 5:
						jsonObject3["v"] = gear.CategoryId;
						flag3 = true;
						break;
					case 7:
						jsonObject3["v"] = gear.CategoryId;
						flag4 = true;
						break;
					case 21:
						jsonObject3["v"] = 25u;
						flag2 = true;
						break;
					}
				}
			}
			if (!flag)
			{
				throw new InvalidOperationException("Native gear has no category component.");
			}
			if (!flag2)
			{
				throw new InvalidOperationException("Native gear has no sight component.");
			}
			if (!flag3 || !flag4)
			{
				throw new InvalidOperationException("Native gear has no audio or casing component.");
			}
			(jsonObject2["publicName"]?.AsObject() ?? throw new InvalidOperationException("Native gear public name missing."))["data"] = gear.Name;
			return jsonObject.ToJsonString();
		}
	}
	internal static class SpecialWeaponMath
	{
		public const float MaxPullDelta = 0.1f;

		internal static float HoleDamageAt(float distance, in WeaponNumbers tuning)
		{
			if (!float.IsFinite(distance) || distance < 0f || distance > tuning.Radius)
			{
				return 0f;
			}
			return tuning.Pulse.Damage * (1f - (1f - tuning.EdgeDamageFraction) * Math.Clamp((distance - tuning.CoreRadius) / (tuning.Radius - tuning.CoreRadius), 0f, 1f));
		}

		public static Vector3 Reflect(Vector3 incoming, Vector3 normal)
		{
			if (!float.IsFinite(incoming.LengthSquared()) || !float.IsFinite(normal.LengthSquared()) || incoming.LengthSquared() < 1E-06f || normal.LengthSquared() < 1E-06f)
			{
				throw new ArgumentException("Ricochet needs a travel direction and surface normal.");
			}
			return Vector3.Normalize(Vector3.Reflect(Vector3.Normalize(incoming), Vector3.Normalize(normal)));
		}

		public static bool InFlame(Vector3 start, Vector3 direction, Vector3 point, float range, in WeaponNumbers tuning)
		{
			if (!float.IsFinite(range) || range <= 0f || !float.IsFinite(direction.LengthSquared()) || direction.LengthSquared() < 1E-06f)
			{
				return false;
			}
			direction = Vector3.Normalize(direction);
			Vector3 vector = point - start;
			float num = Vector3.Dot(vector, direction);
			if (num < 0f || num > range)
			{
				return false;
			}
			float num2 = tuning.StartRadius + num * tuning.RadiusPerMetre;
			return (vector - direction * num).LengthSquared() <= num2 * num2;
		}

		public static Vector3 PullStep(Vector3 position, Vector3 center, float delta, in WeaponNumbers tuning)
		{
			Vector3 vector = center - position;
			float num = vector.Length();
			if (!float.IsFinite(num) || !float.IsFinite(delta) || delta <= 0f || num <= tuning.CoreRadius || num > tuning.Radius)
			{
				return Vector3.Zero;
			}
			return vector / num * MathF.Min(num - tuning.CoreRadius, tuning.PullSpeed * MathF.Min(delta, 0.1f));
		}
	}
	internal readonly record struct PlasmaPayload(float Speed, float BodyRadius, float TravelDistance, float Damage, float BlastRadius, float ArcRadius, float ArcDamage)
	{
		internal float Lifetime => TravelDistance / Speed;

		internal static PlasmaPayload FromCharge(float charge, in WeaponNumbers tuning)
		{
			if (!float.IsFinite(charge))
			{
				throw new ArgumentOutOfRangeException("charge");
			}
			charge = Math.Clamp(charge, 0f, 1f);
			float num = Lerp(tuning.Shot.Damage, tuning.FullDamage, charge);
			return new PlasmaPayload(Lerp(tuning.Speed, tuning.FullSpeed, charge), Lerp(tuning.BodyRadius, tuning.FullBodyRadius, charge), Lerp(tuning.TravelDistance, tuning.FullTravelDistance, charge), num, Lerp(tuning.BlastRadius, tuning.FullBlastRadius, charge), Lerp(tuning.ArcRadius, tuning.FullArcRadius, charge), num * tuning.PulseDamageFraction);
			static float Lerp(float low, float high, float power)
			{
				return low + (high - low) * power;
			}
		}

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			builder.Append("Speed = ");
			builder.Append(Speed.ToString());
			builder.Append(", BodyRadius = ");
			builder.Append(BodyRadius.ToString());
			builder.Append(", TravelDistance = ");
			builder.Append(TravelDistance.ToString());
			builder.Append(", Damage = ");
			builder.Append(Damage.ToString());
			builder.Append(", BlastRadius = ");
			builder.Append(BlastRadius.ToString());
			builder.Append(", ArcRadius = ");
			builder.Append(ArcRadius.ToString());
			builder.Append(", ArcDamage = ");
			builder.Append(ArcDamage.ToString());
			return true;
		}
	}
	internal sealed class PlasmaContacts
	{
		public const int Capacity = 128;

		private readonly long[] _identities = new long[128];

		private int _count;

		public bool Remember(long identity)
		{
			if (identity == 0L)
			{
				throw new ArgumentOutOfRangeException("identity");
			}
			if (_identities.AsSpan(0, _count).Contains(identity))
			{
				return false;
			}
			if (_count == 128)
			{
				throw new BudgetExhausted("Plasma contact identity budget exhausted; no truncated result used.");
			}
			_identities[_count++] = identity;
			return true;
		}

		public void Reset()
		{
			_count = 0;
		}
	}
	internal sealed class PlasmaCharge
	{
		private double? _started;

		public bool Charging => _started.HasValue;

		public bool Ready(double now, in WeaponNumbers tuning)
		{
			double? started = _started;
			if (started.HasValue)
			{
				double valueOrDefault = started.GetValueOrDefault();
				if (double.IsFinite(now))
				{
					return now - valueOrDefault + 1E-07 >= tuning.MinimumCharge;
				}
			}
			return false;
		}

		public float Power(double now, in WeaponNumbers tuning)
		{
			double? started = _started;
			if (started.HasValue)
			{
				double valueOrDefault = started.GetValueOrDefault();
				return (float)Math.Clamp((now - valueOrDefault - tuning.MinimumCharge) / (tuning.FullCharge - tuning.MinimumCharge), 0.0, 1.0);
			}
			return 0f;
		}

		public bool Update(double now, bool held, bool pressed, bool allowed, in WeaponNumbers tuning, out float power)
		{
			power = 0f;
			if (!allowed || !double.IsFinite(now))
			{
				Cancel();
				return false;
			}
			if (held)
			{
				if (pressed)
				{
					double valueOrDefault = _started.GetValueOrDefault();
					if (!_started.HasValue)
					{
						valueOrDefault = now;
						_started = valueOrDefault;
					}
				}
				return false;
			}
			if (!_started.HasValue)
			{
				return false;
			}
			power = Power(now, in tuning);
			bool result = Ready(now, in tuning);
			Cancel();
			return result;
		}

		public void Cancel()
		{
			_started = null;
		}
	}
	internal readonly record struct ArcCandidate(long Identity, float DistanceSquared, bool Eligible);
	internal static class ArcSelection
	{
		public static int Nearest(ReadOnlySpan<ArcCandidate> candidates, ReadOnlySpan<long> visited, float range)
		{
			int num = -1;
			for (int i = 0; i < candidates.Length; i++)
			{
				ArcCandidate arcCandidate = candidates[i];
				if (arcCandidate.Eligible && arcCandidate.Identity != 0L && !visited.Contains(arcCandidate.Identity) && float.IsFinite(arcCandidate.DistanceSquared) && !(arcCandidate.DistanceSquared < 0f) && float.IsFinite(range) && !(range < 0f) && !(arcCandidate.DistanceSquared > range * range) && (num < 0 || arcCandidate.DistanceSquared < candidates[num].DistanceSquared || (arcCandidate.DistanceSquared == candidates[num].DistanceSquared && arcCandidate.Identity < candidates[num].Identity)))
				{
					num = i;
				}
			}
			return num;
		}
	}
	internal static class EnergyProtocol
	{
		internal const int InboxLimit = 1024;

		internal const float MaximumMuzzleDistance = 3.5f;
	}
	internal static class EnergyLimits
	{
		internal const int ArcTargets = 4;

		internal const int PulseTargets = 3;

		internal const float Reach = 1000f;
	}
	internal readonly record struct ShotTuning(float Range, double Interval, float Damage, float Stagger, float Precision);
	internal readonly record struct WeaponNumbers
	{
		internal ShotTuning Shot { get; init; }

		internal ShotTuning Pulse { get; init; }

		internal int Targets { get; init; }

		internal float JumpRange { get; init; }

		internal float DamageRetention { get; init; }

		internal float ArcLifetime { get; init; }

		internal float HopDelay { get; init; }

		internal double MinimumCharge { get; init; }

		internal double FullCharge { get; init; }

		internal float BotChargePower { get; init; }

		internal float Speed { get; init; }

		internal float FullSpeed { get; init; }

		internal float BodyRadius { get; init; }

		internal float FullBodyRadius { get; init; }

		internal float TravelDistance { get; init; }

		internal float FullTravelDistance { get; init; }

		internal float FullDamage { get; init; }

		internal int ActiveProjectiles { get; init; }

		internal float BlastRadius { get; init; }

		internal float FullBlastRadius { get; init; }

		internal float ExplosionLifetime { get; init; }

		internal float NoiseRadius { get; init; }

		internal float ExplosionForce { get; init; }

		internal float ArcRadius { get; init; }

		internal float FullArcRadius { get; init; }

		internal float PulseDamageFraction { get; init; }

		internal int PulseTargets { get; init; }

		internal float FadeLifetime { get; init; }

		internal int Bounces { get; init; }

		internal float Lifetime { get; init; }

		internal float StartRadius { get; init; }

		internal float RadiusPerMetre { get; init; }

		internal float FlightLifetime { get; init; }

		internal float Gravity { get; init; }

		internal float Radius { get; init; }

		internal float CoreRadius { get; init; }

		internal float CollapseDuration { get; init; }

		internal float GrowthDuration { get; init; }

		internal float EdgeDamageFraction { get; init; }

		internal float PullSpeed { get; init; }

		internal float QueryInterval { get; init; }

		internal static WeaponNumbers Resolve(EnergyTuning t)
		{
			float num = ((t is ArcChainTuning arcChainTuning) ? arcChainTuning.Range : ((!(t is FlameTuning flameTuning)) ? 1000f : flameTuning.Range));
			float range = num;
			WeaponNumbers weaponNumbers = new WeaponNumbers
			{
				Shot = new ShotTuning(range, t.Interval, t.Damage, t.Stagger, t.Precision)
			};
			if (t is PlasmaTuning plasmaTuning)
			{
				weaponNumbers = weaponNumbers with
				{
					MinimumCharge = plasmaTuning.MinimumCharge,
					FullCharge = plasmaTuning.FullCharge,
					BotChargePower = plasmaTuning.BotChargePower,
					Speed = plasmaTuning.Speed,
					FullSpeed = plasmaTuning.FullSpeed,
					BodyRadius = plasmaTuning.BodyRadius,
					FullBodyRadius = plasmaTuning.FullBodyRadius,
					TravelDistance = plasmaTuning.TravelDistance,
					FullTravelDistance = plasmaTuning.FullTravelDistance,
					FullDamage = plasmaTuning.FullDamage,
					ActiveProjectiles = plasmaTuning.ActiveProjectiles
				};
			}
			if (!(t is ArcChainTuning arcChainTuning2))
			{
				if (!(t is PlasmaBlastTuning plasmaBlastTuning))
				{
					if (!(t is PlasmaArcTuning plasmaArcTuning))
					{
						if (!(t is BlastTuning blastTuning))
						{
							if (!(t is DiscTuning discTuning))
							{
								if (!(t is FlameTuning flameTuning2))
								{
									if (t is BlackHoleTuning blackHoleTuning)
									{
										return weaponNumbers with
										{
											Speed = blackHoleTuning.Speed,
											BodyRadius = blackHoleTuning.BodyRadius,
											FlightLifetime = blackHoleTuning.FlightLifetime,
											Gravity = blackHoleTuning.Gravity,
											Radius = blackHoleTuning.Radius,
											CoreRadius = blackHoleTuning.CoreRadius,
											Lifetime = blackHoleTuning.Lifetime,
											CollapseDuration = blackHoleTuning.CollapseDuration,
											GrowthDuration = blackHoleTuning.GrowthDuration,
											Pulse = new ShotTuning(blackHoleTuning.Radius, blackHoleTuning.PulseInterval, blackHoleTuning.Damage, blackHoleTuning.Stagger, blackHoleTuning.Precision),
											EdgeDamageFraction = blackHoleTuning.EdgeDamageFraction,
											PullSpeed = blackHoleTuning.PullSpeed,
											QueryInterval = blackHoleTuning.QueryInterval
										};
									}
									return weaponNumbers;
								}
								return weaponNumbers with
								{
									StartRadius = flameTuning2.StartRadius,
									RadiusPerMetre = flameTuning2.RadiusPerMetre
								};
							}
							return weaponNumbers with
							{
								Speed = discTuning.Speed,
								BodyRadius = discTuning.BodyRadius,
								Bounces = discTuning.Bounces,
								Lifetime = discTuning.Lifetime,
								ActiveProjectiles = discTuning.ActiveProjectiles
							};
						}
						return weaponNumbers with
						{
							Speed = blastTuning.Speed,
							BlastRadius = blastTuning.BlastRadius,
							FullBlastRadius = blastTuning.BlastRadius,
							ExplosionLifetime = blastTuning.ExplosionLifetime,
							NoiseRadius = blastTuning.NoiseRadius,
							ExplosionForce = blastTuning.ExplosionForce
						};
					}
					return weaponNumbers with
					{
						ArcRadius = plasmaArcTuning.ArcRadius,
						FullArcRadius = plasmaArcTuning.FullArcRadius,
						Pulse = new ShotTuning(0f, plasmaArcTuning.PulseInterval, 0f, plasmaArcTuning.PulseStagger, plasmaArcTuning.Precision),
						PulseDamageFraction = plasmaArcTuning.PulseDamageFraction,
						PulseTargets = plasmaArcTuning.PulseTargets,
						FadeLifetime = plasmaArcTuning.FadeLifetime
					};
				}
				return weaponNumbers with
				{
					BlastRadius = plasmaBlastTuning.BlastRadius,
					FullBlastRadius = plasmaBlastTuning.FullBlastRadius,
					ExplosionLifetime = plasmaBlastTuning.ExplosionLifetime,
					NoiseRadius = plasmaBlastTuning.NoiseRadius,
					ExplosionForce = plasmaBlastTuning.ExplosionForce
				};
			}
			return weaponNumbers with
			{
				Targets = arcChainTuning2.Targets,
				JumpRange = arcChainTuning2.JumpRange,
				DamageRetention = arcChainTuning2.DamageRetention,
				ArcLifetime = arcChainTuning2.ArcLifetime,
				HopDelay = arcChainTuning2.HopDelay
			};
		}

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			return false;
		}
	}
}
namespace ForgeWeaponEnergyLab.Presentation
{
	internal readonly record struct AnimationFrame(Vector3 Position, Quaternion Rotation);
	internal static class AnimationPose
	{
		internal static AnimationFrame Follow(in AnimationFrame restParent, in AnimationFrame currentParent, in AnimationFrame restChild)
		{
			Validate(in restParent);
			Validate(in currentParent);
			Validate(in restChild);
			Quaternion quaternion = Quaternion.Inverse(restParent.Rotation);
			Vector3 value = Vector3.Transform(restChild.Position - restParent.Position, quaternion);
			Quaternion rotation = Quaternion.Normalize(currentParent.Rotation * quaternion * restChild.Rotation);
			return new AnimationFrame(currentParent.Position + Vector3.Transform(value, currentParent.Rotation), rotation);
		}

		internal static AnimationFrame FollowHand(in AnimationFrame restGrip, in AnimationFrame currentGrip, in AnimationFrame restHand, in AnimationFrame currentHand, in AnimationFrame authoredHand)
		{
			Validate(in currentHand);
			AnimationFrame animationFrame = Follow(in restGrip, in currentGrip, in restHand);
			AnimationFrame animationFrame2 = Follow(in restGrip, in currentGrip, in authoredHand);
			Quaternion rotation = Quaternion.Normalize(currentHand.Rotation * Quaternion.Inverse(animationFrame.Rotation) * animationFrame2.Rotation);
			return new AnimationFrame(animationFrame2.Position + currentHand.Position - animationFrame.Position, rotation);
		}

		internal static AnimationFrame LimitHandReach(in AnimationFrame modelRest, in AnimationFrame donorHand, in AnimationFrame authoredHand, float holdForward)
		{
			Validate(in modelRest);
			Validate(in donorHand);
			Validate(in authoredHand);
			Vector3 vector = Vector3.Transform(Vector3.UnitZ, modelRest.Rotation);
			float num = Vector3.Dot(authoredHand.Position - donorHand.Position, vector);
			float num2 = num + holdForward;
			if (!float.IsFinite(num2))
			{
				throw new ArgumentException("Support-hand forward reach must be finite.");
			}
			return donorHand with
			{
				Position = donorHand.Position + vector * (num - MathF.Max(0f, num2))
			};
		}

		private static void Validate(in AnimationFrame frame)
		{
			if (!PresentationContract.Finite(frame.Position) || !float.IsFinite(frame.Rotation.LengthSquared()) || MathF.Abs(frame.Rotation.LengthSquared() - 1f) > 0.001f)
			{
				throw new ArgumentException("Animation socket pose must be finite with a unit rotation.");
			}
		}
	}
	internal static class EnergyAudio
	{
		internal static readonly string[] Cues = new string[36]
		{
			"beam-start", "beam-loop", "beam-stop", "beam-hit", "arc-fire", "arc-loop", "arc-hit", "plasma-charge-rise", "plasma-charge-loop", "plasma-charge-full",
			"plasma-charge-cancel", "orb-fire", "orb-fly-loop", "shock-fly-loop", "orb-explode", "shock-fire", "shock-fade", "shock-hit", "shock-pulse", "blast-fire",
			"blast-cycle", "blast-explode", "disc-fire", "disc-fly-loop", "disc-bounce", "disc-drop", "flame-start", "flame-loop", "flame-stop", "flame-hit",
			"flame-crackle", "gravity-fire", "gravity-fly-loop", "gravity-open", "gravity-close", "gravity-field-loop"
		};

		private static AudioLibrary? _library;

		internal static AudioLibrary Library => _library ?? throw new InvalidOperationException("Energy sounds were not loaded.");

		internal static bool Loaded => Library.IsLoaded;

		internal static void Load()
		{
			_library = AudioKit.Load("NAinfini.ForgeWeaponEnergyLab", (string)null);
		}

		internal static void PlayAt(string cue, Vector3 position, float gain)
		{
			//IL_0006: 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)
			Library.Play(cue, position, PlayOptions.OfGain(gain));
		}
	}
	internal sealed class EnergyEffects : IDisposable
	{
		private const int Capacity = 4;

		private const float ChargeHoldGain = 0.6f;

		private const float BeamLoopEntry = 0.23f;

		private const float BeamLoopFadeIn = 0.2f;

		private const float BeamLoopFadeOut = 0.18f;

		private const string BeamLoop = "beam-loop";

		private const string BeamStart = "beam-start";

		private const string BeamStop = "beam-stop";

		private const string BeamHit = "beam-hit";

		private const string Arc = "arc-fire";

		private const string ArcLoop = "arc-loop";

		private const string ArcHit = "arc-hit";

		private const string ChargeRise = "plasma-charge-rise";

		private const string ChargeHold = "plasma-charge-loop";

		private const string ChargeFull = "plasma-charge-full";

		private const string PlasmaLaunch = "orb-fire";

		private const string ParcLaunch = "shock-fire";

		private const string ChargeCancel = "plasma-charge-cancel";

		private readonly GameObject _root;

		private readonly Vector3[] _ends = (Vector3[])(object)new Vector3[4];

		private readonly bool[] _hits = new bool[4];

		private readonly AudioVoice _loop;

		private readonly AudioVoice _beamEvent;

		private readonly AudioVoice _beamStop;

		private readonly AudioVoice _beamHit;

		private readonly AudioVoice _arcEvent;

		private readonly AudioVoice _arcLoop;

		private readonly AudioVoice _chargeRise;

		private readonly AudioVoice _chargeHold;

		private readonly AudioVoice _chargeFull;

		private readonly AudioVoice[] _arcHits = (AudioVoice[])(object)new AudioVoice[4];

		private readonly AudioVoice[] _launchVoices = (AudioVoice[])(object)new AudioVoice[4];

		private int _launchCursor;

		private float _arcStarted;

		private float _arcLifetime;

		private float _arcHopDelay;

		private float _beamStarted;

		private float _beamLoopGain;

		private float _beamReleaseGain;

		private float _beamReleased = -1f;

		private int _segments;

		private bool _beaming;

		private bool _beamWasHit;

		private bool _charging;

		private bool _chargedFull;

		private float _nextBeamHit;

		private int _arcHitMask;

		private bool _disposed;

		private bool _arcHeld;

		public EnergyEffects()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			_root = new GameObject("Energy weapon effects");
			Object.DontDestroyOnLoad((Object)(object)_root);
			Transform transform = _root.transform;
			_loop = EnergyAudio.Library.Voice(transform);
			_beamEvent = EnergyAudio.Library.Voice(transform);
			_beamStop = EnergyAudio.Library.Voice(transform);
			_beamHit = EnergyAudio.Library.Voice(Child("beam-hit-voice"));
			_arcEvent = EnergyAudio.Library.Voice(transform);
			_arcLoop = EnergyAudio.Library.Voice(transform);
			for (int i = 0; i < _launchVoices.Length; i++)
			{
				_launchVoices[i] = EnergyAudio.Library.Voice(transform);
			}
			_chargeRise = EnergyAudio.Library.Voice(transform);
			_chargeHold = EnergyAudio.Library.Voice(transform);
			_chargeFull = EnergyAudio.Library.Voice(transform);
			for (int j = 0; j < 4; j++)
			{
				_arcHits[j] = EnergyAudio.Library.Voice(Child("arc-hit-voice-" + j));
			}
		}

		private Transform Child(string name)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = new GameObject(name).transform;
			transform.SetParent(_root.transform, false);
			return transform;
		}

		public void Beam(Vector3 start, Vector3 end, bool hit, float now)
		{
			//IL_0012: 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)
			_segments = 0;
			_root.transform.position = start;
			if (!_beaming)
			{
				_beamEvent.Play("beam-start", 0.7f);
				_beamStarted = now;
				_beaming = true;
				_beamReleased = -1f;
				if (_loop.Playing)
				{
					_loop.Stop(0f);
				}
			}
			bool num = now - _beamStarted >= 0.23f;
			if (num && hit && (!_beamWasHit || now >= _nextBeamHit) && !_beamHit.Playing)
			{
				_beamHit.Anchor.position = end;
				_beamHit.Play("beam-hit", 0.33f);
				_nextBeamHit = now + 0.16f;
			}
			_beamWasHit = hit;
			if (num)
			{
				_beamLoopGain = (hit ? 0.63f : 0.55f) * Mathf.Clamp01((now - _beamStarted - 0.23f) / 0.2f);
				_loop.Hold("beam-loop", _beamLoopGain);
			}
		}

		public void Charge(float power)
		{
			if (!_charging)
			{
				_charging = true;
				_chargedFull = false;
				_chargeFull.Stop(0f);
				_chargeRise.Play("plasma-charge-rise", 0.95f);
			}
			if (power >= 1f && !_chargedFull)
			{
				_chargedFull = true;
				_chargeRise.Stop(0f);
				_chargeFull.Play("plasma-charge-full", 0.9f);
				_chargeHold.Hold("plasma-charge-loop", 0.6f);
			}
		}

		public void Charge(float power, Vector3 position)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_root.transform.position = position;
			Charge(power);
		}

		public void Discharge(bool arcOrb, Vector3 position)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			StopCharge();
			_root.transform.position = position;
			_launchVoices[_launchCursor].Play(arcOrb ? "shock-fire" : "orb-fire", 0.8f);
			_launchCursor = (_launchCursor + 1) % _launchVoices.Length;
		}

		public void StopCharge()
		{
			if (_charging)
			{
				_charging = (_chargedFull = false);
				_chargeRise.Stop(0f);
				_chargeHold.Stop(0f);
				_chargeFull.Stop(0f);
			}
		}

		public void CancelCharge()
		{
			if (_charging)
			{
				StopCharge();
				_chargeFull.Play("plasma-charge-cancel", 0.45f);
			}
		}

		public void Chain(ReadOnlySpan<Vector3> starts, ReadOnlySpan<Vector3> ends, ReadOnlySpan<bool> hits, float now, float lifetime, float hopDelay)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (starts.Length < 1 || starts.Length > 4 || ends.Length != starts.Length || hits.Length != starts.Length)
			{
				throw new ArgumentException("Invalid chain presentation segments.");
			}
			ends.CopyTo(_ends);
			hits.CopyTo(_hits);
			_segments = starts.Length;
			_arcStarted = now;
			_arcLifetime = lifetime;
			_arcHopDelay = hopDelay;
			_arcHitMask = 0;
			_root.transform.position = starts[0];
			_arcEvent.Play("arc-fire", 0.82f);
			Tick(now);
		}

		public void ArcHold(bool held)
		{
			if (held != _arcHeld)
			{
				_arcHeld = held;
				if (held)
				{
					_arcLoop.Hold("arc-loop", 0.5f);
				}
				else
				{
					_arcLoop.Stop(0f);
				}
			}
		}

		public void Tick(float now)
		{
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			if (_beamReleased >= 0f)
			{
				if (float.IsPositiveInfinity(_beamReleased))
				{
					_beamReleased = now;
				}
				float num = (now - _beamReleased) / 0.18f;
				if (num >= 1f || !_loop.Playing)
				{
					_loop.Stop(0f);
					_beamReleased = -1f;
				}
				else
				{
					_loop.SetGain(_beamReleaseGain * (1f - num));
				}
			}
			if (_segments == 0)
			{
				return;
			}
			float num2 = now - _arcStarted;
			if (num2 >= _arcLifetime + (float)(_segments - 1) * _arcHopDelay)
			{
				_segments = 0;
				return;
			}
			for (int i = 0; i < _segments; i++)
			{
				if (!(num2 < (float)i * _arcHopDelay) && _hits[i] && (_arcHitMask & (1 << i)) == 0)
				{
					_arcHitMask |= 1 << i;
					_arcHits[i].Anchor.position = _ends[i];
					_arcHits[i].Play("arc-hit", (i == 0) ? 0.34f : 0.22f);
				}
			}
		}

		public void Hide()
		{
			if (_segments == 0 && !_arcHeld && !_charging && !_beaming)
			{
				return;
			}
			_segments = 0;
			if (_arcHeld)
			{
				_arcLoop.Stop(0f);
			}
			_arcHeld = false;
			if (_charging)
			{
				StopCharge();
			}
			if (_beaming && !_disposed)
			{
				_beamStop.Play("beam-stop", 0.55f);
				if (_loop.Playing)
				{
					_beamReleased = float.PositiveInfinity;
					_beamReleaseGain = _beamLoopGain;
				}
			}
			_beaming = false;
			_beamWasHit = false;
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				_loop.Stop(0f);
				_beamEvent.Stop(0f);
				_beamStop.Stop(0f);
				_beamHit.Stop(0f);
				_arcEvent.Stop(0f);
				_arcLoop.Stop(0f);
				_chargeRise.Stop(0f);
				_chargeHold.Stop(0f);
				_chargeFull.Stop(0f);
				AudioVoice[] launchVoices = _launchVoices;
				for (int i = 0; i < launchVoices.Length; i++)
				{
					launchVoices[i].Stop(0f);
				}
				launchVoices = _arcHits;
				for (int i = 0; i < launchVoices.Length; i++)
				{
					launchVoices[i].Stop(0f);
				}
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)_root);
				}
			}
		}
	}
	internal static class EnergyIcons
	{
		private static readonly GearIconModel Model = new GearIconModel(IconModel);

		internal static void Supply()
		{
			EnergyGear[] all = EnergyGears.All;
			for (int i = 0; i < all.Length; i++)
			{
				GearIcons.Supply(all[i].CategoryId, Model);
			}
		}

		private static GameObject? IconModel(GearIDRange range, out string? failure)
		{
			failure = null;
			EnergyGear energyGear = EnergyGears.ForCategory(range.GetCompID((eGearComponent)2));
			if (energyGear == null)
			{
				failure = "the gear is not an energy gun";
				return null;
			}
			if (PresentationPreload.Failed)
			{
				failure = "the energy presentation of " + energyGear.Definition.Id + " failed to preload";
				return null;
			}
			bool failed;
			GameObject? result = WeaponModels.IconModel(energyGear, out failed);
			if (failed)
			{
				failure = "the model of " + energyGear.Definition.Id + " failed to load";
			}
			return result;
		}
	}
	internal static class EnergyVfx
	{
		private const string Pack = "Forge";

		private const string DiscPack = "EnergyLabDisc";

		internal static ModelHandle Beam;

		internal static ModelHandle Arc;

		internal static ModelHandle Explosion;

		internal static ModelHandle Flame;

		internal static ModelHandle FlameImpact;

		internal static ModelHandle Discharge;

		internal static ModelHandle PlasmaExplosion;

		internal static ModelHandle Orb;

		internal static ModelHandle Sparks;

		internal static ModelHandle BlackHole;

		internal static ModelHandle Disc;

		internal static IEnumerator Preload()
		{
			Register("EnergyLabDisc", "forge-energy-disc.bundle");
			Beam = Resolve("Forge", "forge_energy_beam");
			Arc = Resolve("Forge", "forge_lightning_arc");
			Explosion = Resolve("Forge", "forge_explosion");
			Flame = Resolve("Forge", "forge_flame");
			FlameImpact = Resolve("Forge", "forge_flame_impact");
			Discharge = Resolve("Forge", "forge_plasma_discharge");
			PlasmaExplosion = Resolve("Forge", "forge_plasma_explosion");
			Orb = Resolve("Forge", "forge_plasma_orb");
			Sparks = Resolve("Forge", "forge_sparks");
			BlackHole = Resolve("Forge", "forge_black_hole");
			Disc = Resolve("EnergyLabDisc", "forge_ricochet_disc");
			UseNativeDiscShaders(Disc.Prefab);
			yield break;
		}

		private static void Register(string pack, string file)
		{
			string text = "ForgeWeaponEnergyLab.Vfx." + file;
			using Stream stream = typeof(EnergyVfx).Assembly.GetManifestResourceStream(text) ?? throw new InvalidDataException("Missing " + text);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			if (Bundles.RegisterBytes("NAinfini.ForgeWeaponEnergyLab", pack, file, memoryStream.ToArray()) == null)
			{
				throw new InvalidDataException("ForgeVisual refused the effect bundle " + file + " (see its error in the log).");
			}
		}

		private static ModelHandle Resolve(string pack, string member)
		{
			return Models.Get(pack + ":" + member) ?? throw new InvalidDataException("Missing VFX " + member);
		}

		private static void UseNativeDiscShaders(GameObject prefab)
		{
			MeshRenderer componentInChildren = prefab.GetComponentInChildren<MeshRenderer>();
			MeshFilter componentInChildren2 = prefab.GetComponentInChildren<MeshFilter>();
			Mesh val = ((componentInChildren2 != null) ? componentInChildren2.sharedMesh : null);
			if ((Object)(object)componentInChildren == (Object)null || (Object)(object)val == (Object)null || val.subMeshCount != 4 || ((Il2CppArrayBase<Material>)(object)((Renderer)componentInChildren).sharedMaterials).Length != 4)
			{
				throw new InvalidDataException("Disc prefab requires four surfaces.");
			}
			for (int i = 0; i < 4; i++)
			{
				Material val2 = ((Il2CppArrayBase<Material>)(object)((Renderer)componentInChildren).sharedMaterials)[i] ?? throw new InvalidDataException("Missing disc material.");
				val2.shader = Shader.Find((i == 3) ? "Sprites/Default" : "Standard") ?? throw new InvalidOperationException("Native disc shader is unavailable.");
				if (i < 3)
				{
					val2.EnableKeyword("_EMISSION");
				}
			}
		}
	}
	internal sealed class EnergyVisuals : IDisposable
	{
		private const string IgniteState = "ignite";

		private static readonly PointLightSpec BeamMuzzleLight = new PointLightSpec(new Color(0.4f, 0.7f, 1f), 3f, 1.5f, 0f, 0.2f);

		private static readonly PointLightSpec BeamImpactLight = new PointLightSpec(new Color(0.45f, 0.75f, 1f), 4.5f, 2.5f, 0f, 0.3f);

		private static readonly PointLightSpec ArcLight = new PointLightSpec(new Color(0.5f, 0.75f, 1f), 5f, 3f, 0.2f, 0.4f);

		private static readonly PointLightSpec BlastMuzzleLight = new PointLightSpec(new Color(1f, 0.6f, 0.25f), 4f, 3f, 0.12f, 0.1f);

		private const int Capacity = 4;

		private readonly FxInstancePool _pool = new FxInstancePool("Energy weapon visuals");

		private readonly Vector3[] _starts = (Vector3[])(object)new Vector3[4];

		private readonly Vector3[] _ends = (Vector3[])(object)new Vector3[4];

		private readonly bool[] _hits = new bool[4];

		private FxInstance? _beam;

		private int _beamMuzzleLight;

		private int _beamImpactLight;

		private float _arcStarted;

		private float _arcHopDelay;

		private int _segments;

		private int _started;

		private bool _disposed;

		private bool _prepared;

		internal IEnumerator Prepare()
		{
			if (_prepared)
			{
				yield break;
			}
			foreach (object item in _pool.Prepare(EnergyVfx.Beam, 2, (Color?)null))
			{
				yield return item;
			}
			foreach (object item2 in _pool.Prepare(EnergyVfx.Arc, 8, (Color?)null))
			{
				yield return item2;
			}
			_prepared = true;
		}

		public void Beam(Vector3 start, Vector3 end, bool hit, float now)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed)
			{
				_segments = 0;
				bool num = _beam == null;
				if (_beam == null)
				{
					_beam = _pool.Take(EnergyVfx.Beam, (Color?)null).Hold();
				}
				_beam.Stretch(start, end);
				_beam.Emit("impact", hit);
				_beam.Keep();
				if (num)
				{
					_beam.PlayState("ignite");
				}
				PointLights.Hold(ref _beamMuzzleLight, start, ref BeamMuzzleLight, now, 1f);
				if (hit)
				{
					PointLights.Hold(ref _beamImpactLight, end, ref BeamImpactLight, now, 1f);
				}
				else
				{
					PointLights.Release(ref _beamImpactLight);
				}
			}
		}

		public void Chain(ReadOnlySpan<Vector3> starts, ReadOnlySpan<Vector3> ends, ReadOnlySpan<bool> hits, float now, float hopDelay)
		{
			if (starts.Length < 1 || starts.Length > 4 || ends.Length != starts.Length || hits.Length != starts.Length)
			{
				throw new ArgumentException("Invalid chain presentation segments.");
			}
			if (!_disposed)
			{
				Hide();
				starts.CopyTo(_starts);
				ends.CopyTo(_ends);
				hits.CopyTo(_hits);
				_segments = starts.Length;
				_started = 0;
				_arcStarted = now;
				_arcHopDelay = hopDelay;
				Tick(now);
			}
		}

		public void Blast(Vector3 start, float now)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed)
			{
				Hide();
				PointLights.Flash(start, ref BlastMuzzleLight, now, 1f, 0f);
			}
		}

		public void Tick(float now)
		{
			//IL_0032: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			if (_disposed)
			{
				return;
			}
			while (_started < _segments && now - _arcStarted >= (float)_started * _arcHopDelay)
			{
				FxInstance obj = _pool.Take(EnergyVfx.Arc, (Color?)null);
				obj.Stretch(_starts[_started], _ends[_started]);
				obj.Emit("end", _hits[_started]);
				obj.Start();
				if (_hits[_started])
				{
					PointLights.Flash(_ends[_started], ref ArcLight, now, 1f, 0f);
				}
				_started++;
			}
		}

		public void Hide()
		{
			if (_segments != 0 || _beam != null || _beamMuzzleLight != 0 || _beamImpactLight != 0)
			{
				_segments = (_started = 0);
				FxInstance? beam = _beam;
				if (beam != null)
				{
					beam.Release();
				}
				_beam = null;
				PointLights.Release(ref _beamMuzzleLight);
				PointLights.Release(ref _beamImpactLight);
			}
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				_pool.Dispose();
			}
		}
	}
	internal static class ModelSockets
	{
		internal static Transform Require(Transform model, string name)
		{
			return Find(model, name) ?? throw new InvalidDataException("Energy model socket missing: " + name);
		}

		internal static Transform? Find(Transform model, string name)
		{
			if ((Object)(object)model == (Object)null)
			{
				throw new ArgumentNullException("model");
			}
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentException("Socket name is required.", "name");
			}
			Transform val = null;
			for (int i = 0; i < model.childCount; i++)
			{
				Transform child = model.GetChild(i);
				if (!(((Object)child).name != name) && !((Object)(object)((Component)child).GetComponent<MeshFilter>() != (Object)null) && !((Object)(object)((Component)child).GetComponent<Renderer>() != (Object)null))
				{
					if ((Object)(object)val != (Object)null)
					{
						throw new InvalidDataException("Energy model socket is ambiguous: " + name);
					}
					val = child;
				}
			}
			return val;
		}
	}
	internal sealed class PlasmaEffects : IDisposable
	{
		private const string ChargingState = "charging";

		private const string ChargedState = "charged";

		private const string LaunchState = "launch";

		private const string StormState = "storm";

		private const string FizzleState = "fizzle";

		private const float ExplosionStandOff = 0.3f;

		private static readonly PointLightSpec OrbLight = new PointLightSpec(new Color(0.25f, 0.55f, 1f), 4f, 1.4f, 0f, 0.25f);

		private static readonly PointLightSpec ElectricOrbLight = new PointLightSpec(new Color(0.5f, 0.35f, 1f), 4f, 1.4f, 0f, 0.45f);

		private static readonly PointLightSpec ChargedLight = new PointLightSpec(new Color(0.3f, 0.6f, 1f), 3f, 2.5f, 0.2f, 0.1f);

		private static readonly PointLightSpec LaunchLight = new PointLightSpec(new Color(0.3f, 0.6f, 1f), 5f, 3f, 0.15f, 0.1f);

		private static readonly PointLightSpec DischargeLight = new PointLightSpec(new Color(0.55f, 0.4f, 1f), 3f, 2f, 0.12f, 0.5f);

		private static readonly PointLightSpec ExplosionLight = new PointLightSpec(new Color(0.35f, 0.6f, 1f), 7f, 3f, 0.55f, 0.2f);

		private const float OrbSize = 0.3f;

		private const float ExplosionSize = 3f;

		private const float SightedReach = 1.1f;

		private const float SightMargin = 0.01f;

		private static readonly string[] BeyondCore = new string[2] { "shell", "motes" };

		private static readonly Color ElectricTint = new Color(1f, 0.7f, 1f);

		private readonly FxInstancePool _pool = new FxInstancePool("Plasma ionization effects");

		private readonly FxInstance?[] _orbs;

		private readonly bool[] _exploded;

		private readonly Vector3[] _positions;

		private readonly bool[] _electric;

		private readonly bool[] _flying;

		private readonly bool[] _sighted;

		private readonly int[] _lights;

		private readonly GameObject _voices;

		private readonly AudioVoice[] _flightVoices;

		private bool _disposed;

		private bool _prepared;

		private bool Ready => !_disposed;

		public PlasmaEffects(int capacity)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			_orbs = (FxInstance?[])(object)new FxInstance[capacity];
			_exploded = new bool[capacity];
			_positions = (Vector3[])(object)new Vector3[capacity];
			_electric = new bool[capacity];
			_flying = new bool[capacity];
			_sighted = new bool[capacity];
			_lights = new int[capacity];
			_flightVoices = (AudioVoice[])(object)new AudioVoice[capacity];
			_voices = new GameObject("Plasma ionization voices");
			Object.DontDestroyOnLoad((Object)(object)_voices);
			for (int i = 0; i < _flightVoices.Length; i++)
			{
				Transform transform = new GameObject("plasma-flight-voice-" + i).transform;
				transform.SetParent(_voices.transform, false);
				_flightVoices[i] = EnergyAudio.Library.Voice(transform);
			}
		}

		internal IEnumerator Prepare()
		{
			if (_prepared)
			{
				yield break;
			}
			foreach (object item in _pool.Prepare(EnergyVfx.Orb, _orbs.Length * 2, (Color?)null))
			{
				yield return item;
			}
			foreach (object item2 in _pool.Prepare(EnergyVfx.Orb, _orbs.Length * 2, (Color?)ElectricTint))
			{
				yield return item2;
			}
			foreach (object item3 in _pool.Prepare(EnergyVfx.Discharge, _orbs.Length * 3 * 2, (Color?)null))
			{
				yield return item3;
			}
			foreach (object item4 in _pool.Prepare(EnergyVfx.PlasmaExplosion, _orbs.Length * 2, (Color?)null))
			{
				yield return item4;
			}
			_prepared = true;
		}

		public void ChargingOrb(int index, Vector3 position, float radius, float now, bool electric, Ray? sight)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				float num = radius * 0.55f;
				if (sight.HasValue)
				{
					Ray valueOrDefault = sight.GetValueOrDefault();
					Vector3 val = position - ((Ray)(ref valueOrDefault)).origin;
					Vector3 val2 = val - ((Ray)(ref valueOrDefault)).direction * Vector3.Dot(val, ((Ray)(ref valueOrDefault)).direction);
					float magnitude = ((Vector3)(ref val2)).magnitude;
					num = Mathf.Min(num, Mathf.Max(0f, magnitude - 0.01f) / 1.1f);
				}
				Draw(index, position, num, electric, flying: false, sight.HasValue, now);
			}
		}

		public void Orb(int index, Vector3 position, float radius, float now, bool electric, bool flying)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready)
			{
				return;
			}
			Draw(index, position, radius, electric, flying, sighted: false, now);
			if (flying)
			{
				AudioVoice val = _flightVoices[index];
				val.Anchor.position = position;
				if (!val.Playing)
				{
					val.Play(electric ? "shock-fly-loop" : "orb-fly-loop", 0.85f);
				}
			}
		}

		public void Fade(int index, Vector3 position)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				StopFlight(index);
				FxInstance? obj = _orbs[index];
				if (obj != null)
				{
					obj.PlayState("fizzle");
				}
				EnergyAudio.PlayAt("shock-fade", position, 0.5f);
			}
		}

		private void StopFlight(int index)
		{
			_flightVoices[index].Stop(0f);
		}

		public void Contact(Vector3 position)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				EnergyAudio.PlayAt("shock-hit", position, 0.22f);
			}
		}

		public void Dissipate(int index, Vector3 position, float radius, float progress, float now)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				Draw(index, position, radius * (1f - Mathf.Clamp01(progress) * 0.9f), electric: true, flying: true, sighted: false, now);
			}
		}

		public void Explosion(int index, Vector3 position, float blastRadius, float now)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (Ready && !_exploded[index])
			{
				_exploded[index] = true;
				StopFlight(index);
				Vector3 val = ((_orbs[index] != null) ? (_positions[index] - position) : Vector3.zero);
				ReleaseOrb(index);
				if (((Vector3)(ref val)).sqrMagnitude > 1E-06f)
				{
					position += ((Vector3)(ref val)).normalized * 0.3f;
				}
				FxInstance obj = _pool.Take(EnergyVfx.PlasmaExplosion, (Color?)null);
				obj.Place(position, Quaternion.identity, Mathf.Max(0.1f, blastRadius / 3f));
				obj.Start();
				PointLights.Flash(position, ref ExplosionLight, now, Mathf.Clamp(blastRadius / 3f, 0.6f, 1.4f), 0f);
			}
		}

		private void Draw(int index, Vector3 position, float radius, bool electric, bool flying, bool sighted, float now)
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: 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)
			_exploded[index] = false;
			FxInstance val = _orbs[index];
			if (val != null && _electric[index] != electric)
			{
				val.Release();
				_orbs[index] = null;
			}
			bool flag = _orbs[index] == null;
			ref FxInstance reference = ref _orbs[index];
			FxInstance val2 = reference ?? (reference = _pool.Take(EnergyVfx.Orb, electric ? new Color?(ElectricTint) : ((Color?)null)).Hold());
			_electric[index] = electric;
			if (flag)
			{
				_sighted[index] = sighted;
				string[] beyondCore = BeyondCore;
				foreach (string text in beyondCore)
				{
					val2.Emit(text, !sighted);
				}
			}
			_positions[index] = position;
			float num = Mathf.Max(0.02f, radius / 0.3f);
			val2.Place(position, Quaternion.identity, num);
			val2.Keep();
			if (flag)
			{
				_flying[index] = false;
			}
			if (flag && !flying && !sighted)
			{
				val2.PlayState("charging");
			}
			if (flying && !_flying[index])
			{
				_flying[index] = true;
				val2.PlayState("launch");
				if (electric)
				{
					val2.PlayState("storm");
				}
				PointLights.Flash(position, ref LaunchLight, now, Mathf.Clamp(num, 0.5f, 1.2f), 0f);
			}
			ref int reference2 = ref _lights[index];
			PointLightSpec val3 = (electric ? ElectricOrbLight : OrbLight);
			PointLights.Hold(ref reference2, position, ref val3, now, Mathf.Clamp(num, 0.3f, 1.2f));
		}

		public void Pulse(int index, ReadOnlySpan<Vector3> targets, float now)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				if (targets.Length < 1 || targets.Length > 3)
				{
					throw new ArgumentException("Invalid orb pulse targets.");
				}
				Vector3 val = _positions[index];
				EnergyAudio.PlayAt("shock-pulse", val, 0.22f);
				PointLights.Flash(val, ref DischargeLight, now, 1f, 0f);
				ReadOnlySpan<Vector3> readOnlySpan = targets;
				for (int i = 0; i < readOnlySpan.Length; i++)
				{
					Vector3 val2 = readOnlySpan[i];
					FxInstance obj = _pool.Take(EnergyVfx.Discharge, (Color?)null);
					obj.Stretch(val, val2);
					obj.Start();
				}
			}
		}

		public void Charged(int index)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready)
			{
				return;
			}
			FxInstance val = _orbs[index];
			if (val != null)
			{
				if (!_sighted[index])
				{
					val.PlayState("charged");
				}
				PointLights.Flash(_positions[index], ref ChargedLight, Time.time, 1f, 0f);
			}
		}

		private void ReleaseOrb(int index)
		{
			FxInstance? obj = _orbs[index];
			if (obj != null)
			{
				obj.Release();
			}
			_orbs[index] = null;
			_flying[index] = false;
			PointLights.Release(ref _lights[index]);
		}

		public void Hide(int index)
		{
			if (Ready)
			{
				StopFlight(index);
				ReleaseOrb(index);
				_exploded[index] = false;
			}
		}

		public void Boom(Vector3 position)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				EnergyAudio.PlayAt("orb-explode", position, 0.48f);
			}
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				AudioVoice[] flightVoices = _flightVoices;
				for (int i = 0; i < flightVoices.Length; i++)
				{
					flightVoices[i].Stop(0f);
				}
				_pool.Dispose();
				if ((Object)(object)_voices != (Object)null)
				{
					Object.Destroy((Object)(object)_voices);
				}
			}
		}
	}
	internal sealed class PresentationColor
	{
		public float R { get; set; }

		public float G { get; set; }

		public float B { get; set; }

		public float A { get; set; }
	}
	internal sealed class PartPresentation
	{
		public string Name { get; set; } = "";

		public string Surface { get; set; } = "";

		public string TextureSet { get; set; } = "";

		public Vector3 Pivot { get; set; }

		public PresentationColor Color { get; set; } = new PresentationColor();
	}
	internal sealed class TextureSetPresentation
	{
		public string Name { get; set; } = "";

		public bool Emission { get; set; }

		public bool Normal { get; set; }
	}
	internal sealed class WeaponPresentation
	{
		public string Mode { get; set; } = "";

		public Vector3 RightHand { get; set; }

		public Vector3 LeftHand { get; set; }

		public Vector3 Muzzle { get; set; }

		public Vector3 SightLook { get; set; }

		public Vector3 BoundsMin { get; set; }

		public Vector3 BoundsMax { get; set; }

		public PartPresentation[] Parts { get; set; } = Array.Empty<PartPresentation>();
	}
	internal sealed class PresentationContract
	{
		internal const int CurrentFormat = 4;

		private static readonly string[] RequiredParts = new string[6] { "Receiver", "Rail", "Front", "Stock", "Magazine", "Sight" };

		public int Format { get; set; }

		public WeaponPresentation[] Weapons { get; set; } = Array.Empty<WeaponPresentation>();

		public TextureSetPresentation[] TextureSets { get; set; } = Array.Empty<TextureSetPresentation>();

		internal TextureSetPresentation TextureSet(string name)
		{
			return Array.Find(TextureSets, (TextureSetPresentation set) => set.Name == name) ?? throw new ArgumentException("Unknown texture set: " + name);
		}

		internal static bool Finite(Vector3 v)
		{
			if (float.IsFinite(v.X) && float.IsFinite(v.Y))
			{
				return float.IsFinite(v.Z);
			}
			return false;
		}

		internal static PresentationContract Parse(string json)
		{
			PresentationContract presentationContract = JsonSerializer.Deserialize<PresentationContract>(json, new JsonSerializerOptions
			{
				PropertyNameCaseInsensitive = true,
				IncludeFields = true
			}) ?? throw new ArgumentException("Missing presentation contract.");
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (presentationContract.Format != 4 || presentationContract.Weapons.Length == 0 || presentationContract.Weapons.Length > 128)
			{
				throw new ArgumentException($"Expected one to 128 format-{4} model presentations.");
			}
			HashSet<string> hashSet2 = new HashSet<string>();
			TextureSetPresentation[] textureSets = presentationContract.TextureSets;
			foreach (TextureSetPresentation textureSetPresentation in textureSets)
			{
				if (string.IsNullOrEmpty(textureSetPresentation.Name) || !hashSet2.Add(textureSetPresentation.Name))
				{
					throw new ArgumentException("Invalid or duplicate texture set.");
				}
			}
			WeaponPresentation[] weapons = presentationContract.Weapons;
			foreach (WeaponPresentation weaponPresentation in weapons)
			{
				if (string.IsNullOrWhiteSpace(weaponPresentation.Mode) || !hashSet.Add(weaponPresentation.Mode))
				{
					throw new ArgumentException("Invalid or duplicate weapon: " + weaponPresentation.Mode);
				}
				Vector3[] array = new Vector3[6] { weaponPresentation.RightHand, weaponPresentation.LeftHand, weaponPresentation.Muzzle, weaponPresentation.SightLook, weaponPresentation.BoundsMin, weaponPresentation.BoundsMax };
				int j;
				for (j = 0; j < array.Length; j++)
				{
					if (!Finite(array[j]))
					{
						throw new ArgumentException("Non-finite socket or bounds: " + weaponPresentation.Mode);
					}
				}
				float num = weaponPresentation.BoundsMax.Z - weaponPresentation.BoundsMin.Z;
				if (weaponPresentation.BoundsMin.X >= weaponPresentation.BoundsMax.X || weaponPresentation.BoundsMin.Y >= weaponPresentation.BoundsMax.Y || num < 0.5f || num > 1.3f)
				{
					throw new ArgumentException("Invalid metric weapon bounds: " + weaponPresentation.Mode);
				}
				HashSet<string> hashSet3 = new HashSet<string>();
				PartPresentation[] parts = weaponPresentation.Parts;
				j = 0;
				while (j < parts.Length)
				{
					PartPresentation partPresentation = parts[j];
					if (string.IsNullOrEmpty(partPresentation.Name) || !hashSet3.Add(partPresentation.Name) || !Finite(partPresentation.Pivot))
					{
						throw new ArgumentException("Invalid or duplicate part: " + weaponPresentation.Mode + "/" + partPresentation.Name);
					}
					string surface = partPresentation.Surface;
					bool flag = ((surface == "glass" || surface == "reticle") ? true : false);
					bool flag2 = flag;
					bool num2;
					if (!(partPresentation.Surface == "body"))
					{
						if (!flag2 || partPresentation.TextureSet != "")
						{
							goto IL_032b;
						}
						num2 = partPresentation.Color.A <= 0f;
					}
					else
					{
						num2 = !hashSet2.Contains(partPresentation.TextureSet);
					}
					if (!num2)
					{
						j++;
						continue;
					}
					goto IL_032b;
					IL_032b:
					throw new ArgumentException("Invalid part surface: " + weaponPresentation.Mode + "/" + partPresentation.Name);
				}
				string[] requiredParts = RequiredParts;
				foreach (string text in requiredParts)
				{
					if (!hashSet3.Contains(text))
					{
						throw new ArgumentException("Missing part: " + weaponPresentation.Mode + "/" + text);
					}
				}
			}
			return presentationContract;
		}
	}
	internal static class PresentationPreload
	{
		private static IEnumerator? _work;

		private const float AudioLoadSeconds = 120f;

		private static string _phase = "icons";

		internal static bool Ready { get; private set; }

		internal static bool Failed { get; private set; }

		internal static void Tick()
		{
			if (Ready || Failed)
			{
				return;
			}
			try
			{
				if (_work == null)
				{
					_work = Prepare();
				}
				if (!_work.MoveNext())
				{
					(_work as IDisposable)?.Dispose();
					_work = null;
					Ready = true;
				}
			}
			catch (Exception ex)
			{
				Failed = true;
				(_work as IDisposable)?.Dispose();
				_work = null;
				Plugin.Error("EnergyPreload failed at " + _phase + "; energy weapons stay disabled: " + ex);
			}
		}

		private static IEnumerator Prepare()
		{
			IEnumerator[] phases = new IEnumerator[3]
			{
				WeaponModels.Preload(),
				EnergyVfx.Preload(),
				PrepareAudio()
			};
			string[] names = new string[3] { "models", "vfx", "audio" };
			for (int i = 0; i < phases.Length; i++)
			{
				_phase = names[i];
				using (phases[i] as IDisposable)
				{
					while (phases[i].MoveNext())
					{
						yield return null;
					}
					yield return null;
				}
			}
		}

		private static IEnumerator PrepareAudio()
		{
			float give = Time.realtimeSinceStartup + 120f;
			while (!EnergyAudio.Loaded)
			{
				if (Time.realtimeSinceStartup > give)
				{
					throw new TimeoutException("ForgeAudio did not finish decoding the energy sounds in " + 120f + " s.");
				}
				yield return null;
			}
			EnergyAudio.Library.Require(EnergyAudio.Cues);
		}
	}
	internal static class SightProjection
	{
		internal const float VirtualDistance = 100f;

		internal static bool TryOffset(Vector3 eye, float planeDepth, out Vector3 offset)
		{
			offset = default(Vector3);
			if (!float.IsFinite(eye.X) || !float.IsFinite(eye.Y) || !float.IsFinite(eye.Z) || !float.IsFinite(planeDepth) || eye.Z >= planeDepth)
			{
				return false;
			}
			float num = 100f / (100f + planeDepth - eye.Z);
			offset = new Vector3(eye.X * num, eye.Y * num, 0f);
			return true;
		}
	}
	internal sealed class SpecialEffects : IDisposable
	{
		private sealed class DiscView
		{
			internal Transform Root;

			internal Transform Blade;

			internal TrailRenderer Trail;

			internal AudioVoice Audio;

			internal Vector3 Lift;
		}

		private const float FlameSize = 8f;

		private const float ExplosionSize = 2.2f;

		private const float ProjectileScale = 0.15f;

		private const float CollapseFlashDelay = 0.28f;

		private const string FlameIgnite = "ignite";

		private const string HoleOpenState = "open";

		private const string HoleCollapseState = "collapse";

		private static readonly PointLightSpec FlameLight = new PointLightSpec(new Color(1f, 0.45f, 0.12f), 6f, 4f, 0f, 0.35f);

		private static readonly PointLightSpec FlameImpactLight = new PointLightSpec(new Color(1f, 0.45f, 0.12f), 2.5f, 2f, 0f, 0.4f);

		private static readonly PointLightSpec ExplosionLight = new PointLightSpec(new Color(1f, 0.62f, 0.28f), 8f, 3f, 0.5f, 0.25f);

		private static readonly PointLightSpec SparksLight = new PointLightSpec(new Color(1f, 0.7f, 0.4f), 2.5f, 2f, 0.12f, 0.3f);

		private static readonly PointLightSpec HoleLight = new PointLightSpec(new Color(0.55f, 0.15f, 1f), 6f, 2f, 0f, 0.2f);

		private static readonly PointLightSpec HoleOpenLight = new PointLightSpec(new Color(0.6f, 0.3f, 1f), 9f, 5f, 0.4f, 0.1f);

		private static readonly PointLightSpec HoleCollapseLight = new PointLightSpec(new Color(0.75f, 0.5f, 1f), 8f, 4f, 0.35f, 0.1f);

		private static readonly Color ShellTint = new Color(1f, 0.6f, 0.25f);

		private static readonly PointLightSpec ShellLight = new PointLightSpec(new Color(1f, 0.62f, 0.28f), 3f, 1.5f, 0f, 0.2f);

		private const float ShellSize = 0.09f;

		private const float OrbSize = 0.3f;

		private const int ShellCapacity = 64;

		private const float FlameImpactInterval = 0.08f;

		private const int FlameStart = 0;

		private const int FlameCrackle = 1;

		private const int FlameImpactSound = 2;

		private const int DiscLaunch = 3;

		private const int DiscFlight = 4;

		private const int DiscBounce = 5;

		private const int HoleLaunchSound = 6;

		private const int HoleFlight = 7;

		private const int HoleOpen = 8;

		private const int HoleClose = 9;

		private const int BlastLaunchSound = 10;

		private const int BlastCycle = 11;

		private const int BlastImpact = 12;

		private const int FlameLoop = 13;

		private const int FlameStop = 14;

		private const int DiscStop = 15;

		private const int HoleField = 16;

		private const uint DiscHitSound = 3633778169u;

		private readonly GameObject _root;

		private readonly FxInstancePool _pool = new FxInstancePool("Energy disc flame and gravity effects");

		private readonly DiscView[] _discs = new DiscView[3];

		private readonly AudioVoice _flameAudio;

		private readonly AudioVoice _holeAudio;

		private readonly AudioVoice _crackleAudio;

		private readonly Transform _holeVoice;

		private readonly Transform _crackleVoice;

		private readonly AudioVoice[] _launchVoices = (AudioVoice[])(object)new AudioVoice[3];

		private readonly ImpactLook _sparks;

		private readonly ImpactLook _discHit;

		private readonly ImpactLook _explosion;

		private readonly FxInstance?[] _shells = (FxInstance?[])(object)new FxInstance[64];

		private readonly int[] _shellLights = new int[64];

		private FxInstance? _flame;

		private FxInstance? _hole;

		private int _flameLight;

		private int _flameImpactLight;

		private int _holeLight;

		private int _launchCursor;

		private float _nextFlameImpact;

		private float _nextFlameImpactSound;

		private bool _flaming;

		private bool _disposed;

		private bool _prepared;

		private static readonly string[] Sounds = new string[17]
		{
			"flame-start", "flame-crackle", "flame-hit", "disc-fire", "disc-fly-loop", "disc-bounce", "gravity-fire", "gravity-fly-loop", "gravity-open", "gravity-close",
			"blast-fire", "blast-cycle", "blast-explode", "flame-loop", "flame-stop", "disc-drop", "gravity-field-loop"
		};

		internal Quaternion ViewRotation { get; set; } = Quaternion.identity;

		private bool Ready
		{
			get
			{
				if (!_disposed)
				{
					return Alive((Object?)(object)_root);
				}
				return false;
			}
		}

		internal SpecialEffects()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0043: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			_sparks = new ImpactLook
			{
				Model = EnergyVfx.Sparks,
				Light = SparksLight,
				LightLift = 0.1f
			};
			_discHit = new ImpactLook
			{
				Model = EnergyVfx.Sparks,
				ModelScale = 0.7f,
				Light = SparksLight
			};
			_explosion = new ImpactLook
			{
				Model = EnergyVfx.Explosion,
				Light = ExplosionLight
			};
			_root = new GameObject("Energy disc flame and gravity voices");
			Object.DontDestroyOnLoad((Object)(object)_root);
			try
			{
				Material sharedMaterial = TrailMaterial();
				GameObject prefab = EnergyVfx.Disc.Prefab;
				for (int i = 0; i < _discs.Length; i++)
				{
					GameObject val = Object.Instantiate<GameObject>(prefab, _root.transform, false);
					((Object)val).name = "ricochet-disc-" + i;
					TrailRenderer component = val.GetComponent<TrailRenderer>();
					Transform val2 = val.transform.Find("cast-metal-rotor");
					if ((Object)(object)component == (Object)null || (Object)(object)val2 == (Object)null)
					{
						throw new InvalidOperationException("Disc prefab is missing its blade or trail.");
					}
					((Renderer)component).sharedMaterial = sharedMaterial;
					_discs[i] = new DiscView
					{
						Root = val.transform,
						Blade = val2,
						Audio = EnergyAudio.Library.Voice(val.transform),
						Trail = component
					};
					val.SetActive(false);
				}
			}
			catch
			{
				Object.Destroy((Object)(object)_root);
				_pool.Dispose();
				throw;
			}
			_holeVoice = Child("singularity-voice", _root.transform);
			_crackleVoice = Child("flame-crackle-voice", _root.transform);
			_holeAudio = EnergyAudio.Library.Voice(_holeVoice);
			_crackleAudio = EnergyAudio.Library.Voice(_crackleVoice);
			_flameAudio = EnergyAudio.Library.Voice(_root.transform);
			for (int j = 0; j < _launchVoices.Length; j++)
			{
				_launchVoices[j] = EnergyAudio.Library.Voice(Child("launch-voice-" + j, _root.transform));
			}
		}

		internal IEnumerator Prepare()
		{
			if (_prepared)
			{
				yield break;
			}
			foreach (object item in _pool.Prepare(EnergyVfx.Flame, 2, (Color?)null))
			{
				yield return item;
			}
			foreach (object item2 in _pool.Prepare(EnergyVfx.FlameImpact, 8, (Color?)null))
			{
				yield return item2;
			}
			foreach (object item3 in Impacts.Prepare(_explosion, 4))
			{
				yield return item3;
			}
			foreach (object item4 in Impacts.Prepare(_sparks, 8))
			{
				yield return item4;
			}
			foreach (object item5 in _pool.Prepare(EnergyVfx.BlackHole, 2, (Color?)null))
			{
				yield return item5;
			}
			foreach (object item6 in _pool.Prepare(EnergyVfx.Orb, 4, (Color?)ShellTint))
			{
				yield return item6;
			}
			_prepared = true;
		}

		private static Transform Child(string name, Transform parent)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = new GameObject(name).transform;
			transform.SetParent(parent, false);
			return transform;
		}

		private static Material TrailMaterial()
		{
			ParticleSystemRenderer component = ((Component)(EnergyVfx.Orb.Prefab.transform.Find("trail") ?? throw new InvalidOperationException(EnergyVfx.Orb.Name + " has no trail layer for the disc streak."))).GetComponent<ParticleSystemRenderer>();
			Material val = (Alive((Object?)(object)component) ? component.trailMaterial : null);
			if (!Alive((Object?)(object)val))
			{
				throw new InvalidOperationException(EnergyVfx.Orb.Name + "'s trail layer has no trail material.");
			}
			return val;
		}

		private static bool Alive(Object? value)
		{
			if (value != null)
			{
				return value != (Object)null;
			}
			return false;
		}

		internal void Origin(Vector3 position)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				_root.transform.position = position;
			}
		}

		private void BurstSound(int clip, float volume = 0.8f)
		{
			if (Ready)
			{
				_launchVoices[_launchCursor++ % _launchVoices.Length].Play(Sounds[clip], volume);
			}
		}

		private void LoopSound(AudioVoice voice, int clip, float volume)
		{
			if (Ready && !voice.IsPlaying(Sounds[clip]))
			{
				voice.Play(Sounds[clip], volume);
			}
		}

		private void OneShot(ModelHandle model, Vector3 position, Quaternion rotation, float scale = 1f)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			FxInstance obj = _pool.Take(model, (Color?)null);
			obj.Place(position, rotation, scale);
			obj.Start();
		}

		private static Quaternion Facing(Vector3 direction)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!(((Vector3)(ref direction)).sqrMagnitude > 0.0001f))
			{
				return Quaternion.LookRotation(Vector3.up);
			}
			return Quaternion.LookRotation(direction);
		}

		internal void Flame(Vector3 start, Vector3 direction, float distance, float now)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready)
			{
				return;
			}
			Origin(start);
			if (((Vector3)(ref direction)).sqrMagnitude < 0.0001f || distance <= 0.12f)
			{
				StopFlame();
				return;
			}
			if (!_flaming)
			{
				_flaming = true;
				BurstSound(0);
			}
			LoopSound(_flameAudio, 13, 0.5f);
			bool num = _flame == null;
			if (_flame == null)
			{
				_flame = _pool.Take(EnergyVfx.Flame, (Color?)null).Hold();
			}
			_flame.Place(start, Facing(direction), distance / 8f);
			_flame.Keep();
			if (num)
			{
				_flame.PlayState("ignite");
			}
			PointLights.Hold(ref _flameLight, start + ((Vector3)(ref direction)).normalized * Mathf.Min(1.2f, distance), ref FlameLight, now, 1f);
		}

		internal void StopFlame()
		{
			if (_flaming || _flame != null || _flameLight != 0 || _flameImpactLight != 0)
			{
				if (_flaming && !_disposed)
				{
					_flameAudio.Stop(0f);