Decompiled source of forgeweapon energylab experimental v0.0.5

plugins/ForgeEnergyLab/ForgeWeaponEnergyLabExperimental.dll

Decompiled 4 days ago
using System;
using System.Buffers.Binary;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using AIGraph;
using Agents;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Enemies;
using FX_EffectSystem;
using ForgeEnergyLab.Api;
using ForgeWeaponEnergyLabExperimental;
using ForgeWeaponEnergyLabExperimental.Native;
using ForgeWeaponEnergyLabExperimental.Presentation;
using GTFO.API;
using GameData;
using Gear;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using LevelGeneration;
using Localization;
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("ForgeWeaponEnergyLabExperimental")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.0.5.0")]
[assembly: AssemblyInformationalVersion("0.0.5+14c29500274e00f579a60fae7f71b79615346197")]
[assembly: AssemblyProduct("ForgeWeaponEnergyLabExperimental")]
[assembly: AssemblyTitle("ForgeWeaponEnergyLabExperimental")]
[assembly: AssemblyVersion("0.0.5.0")]
[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 ForgeWeaponEnergyLabExperimental
{
	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";

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

		internal static WeaponDefinition[] Load(string directory, WeaponDefinition[] defaults)
		{
			Directory.CreateDirectory(directory);
			JsonObject jsonObject = new JsonObject();
			foreach (WeaponDefinition weaponDefinition in defaults)
			{
				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));
			string path = Path.Combine(directory, "tuning-overrides.json");
			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));
			}
			try
			{
				using JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(path));
				JsonElement jsonElement = Object(Object(jsonDocument.RootElement, "$", new string[1] { "weapons" }, new string[1] { "weapons" }).GetProperty("weapons"), "$.weapons", defaults.Select((WeaponDefinition d) => d.Id).ToArray(), Array.Empty<string>());
				WeaponDefinition[] array = new WeaponDefinition[defaults.Length];
				for (int num = 0; num < defaults.Length; num++)
				{
					WeaponDefinition weaponDefinition2 = (array[num] = defaults[num]);
					if (!jsonElement.TryGetProperty(weaponDefinition2.Id, out var value))
					{
						continue;
					}
					string text = "$.weapons." + weaponDefinition2.Id;
					Type type = weaponDefinition2.Tuning.GetType();
					PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
					Object(value, text, properties.Select((PropertyInfo p) => JsonOptions.PropertyNamingPolicy.ConvertName(p.Name)).ToArray(), Array.Empty<string>());
					JsonObject jsonObject2 = TuningJson(weaponDefinition2.Tuning).AsObject();
					PropertyInfo[] array2 = properties;
					foreach (PropertyInfo propertyInfo in array2)
					{
						string text2 = JsonOptions.PropertyNamingPolicy.ConvertName(propertyInfo.Name);
						if (!value.TryGetProperty(text2, out var value2))
						{
							continue;
						}
						bool num2;
						if (value2.ValueKind == JsonValueKind.Number)
						{
							if (propertyInfo.PropertyType == typeof(int))
							{
								num2 = !value2.TryGetInt32(out var _);
								goto IL_0322;
							}
							float value5;
							if (!(propertyInfo.PropertyType == typeof(float)))
							{
								if (value2.TryGetDouble(out var value4))
								{
									num2 = !double.IsFinite(value4);
									goto IL_0322;
								}
							}
							else if (value2.TryGetSingle(out value5))
							{
								num2 = !float.IsFinite(value5);
								goto IL_0322;
							}
						}
						goto IL_0324;
						IL_0322:
						if (num2)
						{
							goto IL_0324;
						}
						goto IL_035b;
						IL_035b:
						jsonObject2[text2] = JsonNode.Parse(value2.GetRawText());
						continue;
						IL_0324:
						Fail(text + "." + text2, (propertyInfo.PropertyType == typeof(int)) ? "expected a 32-bit whole number" : "expected a finite number");
						goto IL_035b;
					}
					EnergyTuning energyTuning = (EnergyTuning)jsonObject2.Deserialize(type, JsonOptions);
					ValidateTuning(weaponDefinition2.Mode, energyTuning, text);
					array[num] = weaponDefinition2 with
					{
						Tuning = energyTuning
					};
				}
				return array;
			}
			catch (JsonException ex)
			{
				throw new InvalidDataException($"{ex.Path ?? "$"}: invalid JSON or numeric type at line {ex.LineNumber + 1}, byte {ex.BytePositionInLine + 1}", ex);
			}
		}

		private static JsonElement Object(JsonElement value, string path, string[] allowed, string[]? required = null)
		{
			if (value.ValueKind != JsonValueKind.Object)
			{
				Fail(path, "expected an object");
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (JsonProperty item in value.EnumerateObject())
			{
				if (!allowed.Contains<string>(item.Name, StringComparer.Ordinal))
				{
					Fail(path + "." + item.Name, "unknown field");
				}
				if (!hashSet.Add(item.Name))
				{
					Fail(path + "." + item.Name, "duplicate field");
				}
			}
			string[] array = required ?? allowed;
			foreach (string text in array)
			{
				if (!hashSet.Contains(text))
				{
					Fail(path + "." + text, "missing field");
				}
			}
			return value;
		}

		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("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");
			}
			string bundlePath = definition.Model.BundlePath;
			if (bundlePath != null)
			{
				if (!Path.IsPathFullyQualified(bundlePath))
				{
					Fail(definition.Id + ".model.bundlePath", "expected an absolute bundle path");
				}
				if (definition.Model.IconPath == null)
				{
					Fail(definition.Id + ".model.iconPath", "a provider bundle needs its own PNG icon");
				}
			}
			else
			{
				EnergyGears.BuiltInModel(definition.Model.Key);
			}
			string iconPath = definition.Model.IconPath;
			if (iconPath != null && !Path.IsPathFullyQualified(iconPath))
			{
				Fail(definition.Id + ".model.iconPath", "expected an absolute PNG 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("BlastRadius", blastTuning.BlastRadius, 0.01, 100.0);
						Check("ExplosionLifetime", blastTuning.ExplosionLifetime, 0.01, 120.0);
						Check("ActiveProjectiles", blastTuning.ActiveProjectiles, 1.0, 4.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 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
		{
			get
			{
				uint gearId = GearId;
				return "ForgeWeaponEnergyLabExperimental_" + ((gearId >= 794201 && gearId <= 794208) ? Mode.ToString() : Definition.Id);
			}
		}

		internal EnergyGear(WeaponDefinition definition)
		{
			Definition = definition;
			ModelPath = Path.GetFullPath(definition.Model.BundlePath ?? Path.Combine(Path.GetDirectoryName(typeof(EnergyGear).Assembly.Location), "forge-energy-model-" + definition.Model.Key + ".bundle"));
			(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];

		internal static readonly Dictionary<string, string> ModelHashes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static readonly string[] ModelKeys = new string[8] { "beam", "arc", "flame", "plasma-blast", "plasma-arc", "blast", "disc", "hole" };

		private static readonly string[] BuiltInIds = new string[8] { "forge.energylab.beam", "forge.energylab.arc", "forge.energylab.flame", "forge.energylab.plasma-blast", "forge.energylab.plasma-arc", "forge.energylab.blast", "forge.energylab.disc", "forge.energylab.hole" };

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

		internal static WeaponModel BuiltInModel(string key)
		{
			if (!Array.Exists(ModelKeys, (string model) => model == key))
			{
				throw new ArgumentException("Unknown built-in model key: " + key, "key");
			}
			return new WeaponModel(key);
		}

		internal static WeaponDefinition[] Defaults()
		{
			return new WeaponDefinition[8]
			{
				new WeaponDefinition(BuiltInIds[0], "Forge Beam", WeaponSlot.Main, EnergyMode.Beam, new BeamTuning(), BuiltInModel("beam")),
				new WeaponDefinition(BuiltInIds[1], "Forge Chain Arc", WeaponSlot.Main, EnergyMode.ArcChain, new ArcChainTuning(), BuiltInModel("arc")),
				new WeaponDefinition(BuiltInIds[2], "Forge Flamethrower", WeaponSlot.Main, EnergyMode.Flame, new FlameTuning(), BuiltInModel("flame")),
				new WeaponDefinition(BuiltInIds[3], "Forge Charge Orb", WeaponSlot.Special, EnergyMode.PlasmaBlast, new PlasmaBlastTuning(), BuiltInModel("plasma-blast")),
				new WeaponDefinition(BuiltInIds[4], "Forge Shock Orb", WeaponSlot.Special, EnergyMode.PlasmaArc, new PlasmaArcTuning(), BuiltInModel("plasma-arc")),
				new WeaponDefinition(BuiltInIds[5], "Forge Blast Gun", WeaponSlot.Special, EnergyMode.Blast, new BlastTuning(), BuiltInModel("blast")),
				new WeaponDefinition(BuiltInIds[6], "Forge Ricochet Disc", WeaponSlot.Special, EnergyMode.Disc, new DiscTuning(), BuiltInModel("disc")),
				new WeaponDefinition(BuiltInIds[7], "Forge Gravity Core", WeaponSlot.Special, EnergyMode.BlackHole, new BlackHoleTuning(), BuiltInModel("hole"))
			};
		}

		internal static (uint Gear, uint Category, uint Archetype) Ids(string id)
		{
			int num = Array.IndexOf(BuiltInIds, id);
			if (num >= 0)
			{
				return (Gear: (uint)(794201 + num), Category: (uint)(65001 + num), Archetype: (uint)(794101 + num));
			}
			return (Gear: 0x20000000 | (Hash(id + "/gear") & 0x1FFFFFFF), Category: 0x8000 | (Hash(id + "/category") & 0x7FFF), Archetype: 0x20000000 | (Hash(id + "/archetype") & 0x1FFFFFFF));
			static uint Hash(string text)
			{
				uint num2 = 2166136261u;
				byte[] bytes = Encoding.UTF8.GetBytes(text);
				foreach (byte b in bytes)
				{
					num2 = (num2 ^ b) * 16777619;
				}
				return num2;
			}
		}

		internal static void Freeze()
		{
			if (All.Length != 0)
			{
				return;
			}
			EnergyGear[] array = EnergyLab.Freeze();
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			EnergyGear[] array2 = array;
			foreach (EnergyGear energyGear in array2)
			{
				string iconPath = energyGear.Definition.Model.IconPath;
				if (iconPath != null)
				{
					FileInfo fileInfo = new FileInfo(iconPath);
					if (!fileInfo.Exists || fileInfo.Length < 32 || fileInfo.Length > 1048576)
					{
						throw new InvalidDataException(energyGear.Definition.Id + ".model.iconPath: missing PNG or size outside 32 B..1 MiB: " + iconPath);
					}
				}
				if (dictionary.ContainsKey(energyGear.ModelPath))
				{
					continue;
				}
				FileInfo fileInfo2 = new FileInfo(energyGear.ModelPath);
				if (!fileInfo2.Exists || fileInfo2.Length < 1024 || fileInfo2.Length > 134217728)
				{
					throw new InvalidDataException(energyGear.Definition.Id + ".model.bundlePath: missing bundle or size outside 1 KiB..128 MiB: " + energyGear.ModelPath);
				}
				using FileStream inputStream = File.OpenRead(energyGear.ModelPath);
				using SHA256 sHA = SHA256.Create();
				dictionary.Add(energyGear.ModelPath, Convert.ToHexString(sHA.ComputeHash(inputStream)));
			}
			array2 = array;
			foreach (EnergyGear energyGear2 in array2)
			{
				Categories[energyGear2.CategoryId] = energyGear2;
			}
			foreach (KeyValuePair<string, string> item in dictionary)
			{
				ModelHashes.Add(item.Key, item.Value);
			}
			All = array;
		}

		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
	{
		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.05f));
		}
	}
	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 InvalidOperationException("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 string Version = "0.0.5";

		internal static readonly Version Number = new Version("0.0.5");

		internal const int InboxLimit = 1024;

		internal const float HelloInterval = 1f;

		internal const float HelloLifetime = 3f;

		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
						{
							BlastRadius = blastTuning.BlastRadius,
							FullBlastRadius = blastTuning.BlastRadius,
							ExplosionLifetime = blastTuning.ExplosionLifetime,
							ActiveProjectiles = blastTuning.ActiveProjectiles,
							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;
		}
	}
	internal static class PcmSamples
	{
		public const int Rate = 48000;

		public const int MaxBytes = 1152000;

		public static float[] Decode(ReadOnlySpan<byte> bytes)
		{
			if (bytes.Length == 0 || (bytes.Length & 1) != 0 || bytes.Length > 1152000)
			{
				throw new ArgumentException("Invalid or oversized mono PCM resource.", "bytes");
			}
			float[] array = new float[bytes.Length / 2];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = (float)BinaryPrimitives.ReadInt16LittleEndian(bytes.Slice(i * 2, 2)) / 32768f;
			}
			return array;
		}
	}
}
namespace ForgeWeaponEnergyLabExperimental.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 AssetRetention
	{
		internal static T Keep<T>(T asset) where T : Object
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			T val = asset;
			? hideFlags = ((Object)val).hideFlags | 0x20;
			((Object)val).hideFlags = (HideFlags)hideFlags;
			return asset;
		}
	}
	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 int BeamLoop = 0;

		private const int BeamStart = 1;

		private const int BeamStop = 2;

		private const int BeamHit = 3;

		private const int Arc = 4;

		private const int ArcLoop = 5;

		private const int ArcHit = 6;

		private const int ChargeRise = 7;

		private const int ChargeHold = 8;

		private const int ChargeFull = 9;

		private const int PlasmaLaunch = 10;

		private const int ParcLaunch = 11;

		private const int ChargeCancel = 12;

		private static readonly string[] ClipNames = new string[13]
		{
			"beam-loop", "beam-start", "beam-stop", "beam-hit", "arc-fire", "arc-loop", "arc-hit", "plasma-charge-rise", "plasma-charge-loop", "plasma-charge-full",
			"orb-fire", "shock-fire", "plasma-charge-cancel"
		};

		private readonly GameObject _root;

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

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

		private AudioSource _loop;

		private AudioSource _beamEvent;

		private AudioSource _beamStop;

		private AudioSource _beamHit;

		private AudioSource _arcEvent;

		private AudioSource _arcLoop;

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

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

		private int _launchCursor;

		private AudioSource _chargeRise;

		private AudioSource _chargeHold;

		private AudioSource _chargeFull;

		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);
			try
			{
				_loop = EnergySound.Voice(_root.transform);
				_beamEvent = EnergySound.Voice(_root.transform);
				_beamStop = EnergySound.Voice(_root.transform);
				_beamHit = EnergySound.Voice(_root.transform, "beam-hit-voice");
				_arcEvent = EnergySound.Voice(_root.transform);
				_arcLoop = EnergySound.Voice(_root.transform);
				for (int i = 0; i < _launchVoices.Length; i++)
				{
					_launchVoices[i] = EnergySound.Voice(_root.transform);
				}
				_chargeRise = EnergySound.Voice(_root.transform);
				_chargeHold = EnergySound.Voice(_root.transform);
				_chargeFull = EnergySound.Voice(_root.transform);
				for (int j = 0; j < 4; j++)
				{
					_arcHits[j] = EnergySound.Voice(_root.transform, "arc-hit-voice-" + j);
				}
			}
			catch
			{
				Dispose();
				throw;
			}
		}

		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_00a6: Unknown result type (might be due to invalid IL or missing references)
			_segments = 0;
			_root.transform.position = start;
			if (!_beaming)
			{
				Play(_beamEvent, 1, 0.7f);
				_beamStarted = now;
				_beaming = true;
				_beamReleased = -1f;
				if (_loop.isPlaying)
				{
					EnergySound.Stop(_loop);
				}
			}
			bool num = now - _beamStarted >= 0.23f;
			if (num && hit && (!_beamWasHit || now >= _nextBeamHit) && !_beamHit.isPlaying)
			{
				((Component)_beamHit).transform.position = end;
				Play(_beamHit, 3, 0.33f);
				_nextBeamHit = now + 0.16f;
			}
			_beamWasHit = hit;
			if (num)
			{
				_beamLoopGain = (hit ? 0.63f : 0.55f) * Mathf.Clamp01((now - _beamStarted - 0.23f) / 0.2f);
				Hold(_loop, 0, _beamLoopGain);
			}
		}

		public void Charge(float power)
		{
			if (!_charging)
			{
				_charging = true;
				_chargedFull = false;
				EnergySound.Stop(_chargeFull);
				Play(_chargeRise, 7, 0.95f);
			}
			if (power >= 1f && !_chargedFull)
			{
				_chargedFull = true;
				EnergySound.Stop(_chargeRise);
				Play(_chargeFull, 9, 0.9f);
				Hold(_chargeHold, 8, 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);
		}

		private void Hold(AudioSource voice, int clip, float volume)
		{
			if (!EnergySound.PlayingCue(voice, ClipNames[clip]))
			{
				EnergySound.Stop(voice);
			}
			voice.pitch = 1f;
			if (EnergySound.PlayingCue(voice, ClipNames[clip]))
			{
				EnergySound.Gain(voice, volume);
			}
			else
			{
				EnergySound.PlayCue(voice, ClipNames[clip], volume);
			}
		}

		private void Play(AudioSource source, int index, float volume)
		{
			EnergySound.Stop(source);
			source.pitch = 1f;
			EnergySound.PlayCue(source, ClipNames[index], volume);
		}

		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;
			Play(_launchVoices[_launchCursor], arcOrb ? 11 : 10, 0.8f);
			_launchCursor = (_launchCursor + 1) % _launchVoices.Length;
		}

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

		public void CancelCharge()
		{
			if (_charging)
			{
				StopCharge();
				Play(_chargeFull, 12, 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];
			Play(_arcEvent, 4, 0.82f);
			Tick(now);
		}

		public void ArcHold(bool held)
		{
			if (held != _arcHeld)
			{
				_arcHeld = held;
				if (held)
				{
					Hold(_arcLoop, 5, 0.5f);
				}
				else if (_arcLoop.isPlaying)
				{
					EnergySound.Stop(_arcLoop);
				}
			}
		}

		public void Tick(float now)
		{
			//IL_00fa: 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.isPlaying)
				{
					EnergySound.Stop(_loop);
					_beamReleased = -1f;
				}
				else
				{
					EnergySound.Gain(_loop, _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;
					((Component)_arcHits[i]).transform.position = _ends[i];
					Play(_arcHits[i], 6, (i == 0) ? 0.34f : 0.22f);
				}
			}
		}

		public void Hide()
		{
			if (_segments == 0 && !_arcHeld && !_charging && !_beaming)
			{
				return;
			}
			_segments = 0;
			if (_arcHeld && (Object)(object)_arcLoop != (Object)null)
			{
				EnergySound.Stop(_arcLoop);
			}
			_arcHeld = false;
			if (_charging)
			{
				StopCharge();
			}
			if (_beaming && !_disposed)
			{
				Play(_beamStop, 2, 0.55f);
				if (_loop.isPlaying)
				{
					_beamReleased = float.PositiveInfinity;
					_beamReleaseGain = _beamLoopGain;
				}
			}
			_beaming = false;
			_beamWasHit = false;
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)_root);
				}
			}
		}
	}
	internal static class EnergyIcons
	{
		private sealed class Job
		{
			internal GearIDRange Gear;

			internal Action<IconRenderJobResult> Callback;

			internal string Icon = "";

			internal int Width;

			internal int Height;

			internal int Frame;
		}

		private readonly record struct Icon(Texture2D Texture, Sprite Sprite);

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

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

		private static readonly Dictionary<(string, int, int), RenderTexture> Targets = new Dictionary<(string, int, int), RenderTexture>();

		private static readonly Queue<Job> Jobs = new Queue<Job>();

		private static string Source(EnergyGear gear)
		{
			return gear.Definition.Model.IconPath ?? gear.Definition.Model.Key;
		}

		internal static IEnumerator Preload()
		{
			while (!EnergyGearRegistry.Ready)
			{
				yield return null;
			}
			EnergyGear[] all = EnergyGears.All;
			foreach (EnergyGear energyGear in all)
			{
				string text = Source(energyGear);
				if (Icons.ContainsKey(text))
				{
					continue;
				}
				string iconPath = energyGear.Definition.Model.IconPath;
				byte[] array;
				if (iconPath != null)
				{
					array = File.ReadAllBytes(iconPath);
				}
				else
				{
					string text2 = "ForgeWeaponEnergyLabExperimental.Icons." + text + ".png";
					using Stream stream = typeof(EnergyIcons).Assembly.GetManifestResourceStream(text2) ?? throw new InvalidDataException("Baked icon is missing: " + text2);
					using MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					array = memoryStream.ToArray();
				}
				if (array.Length < 32 || array.Length > 1048576)
				{
					throw new InvalidDataException("Icon size budget exceeded: " + text);
				}
				Il2CppStructArray<byte> val = new Il2CppStructArray<byte>(array);
				Texture2D val2 = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = "Energy icon " + energyGear.Definition.Id
				};
				if (!ImageConversion.LoadImage(val2, val, true))
				{
					throw new InvalidDataException("Cannot decode icon: " + text);
				}
				GC.KeepAlive(val);
				((Texture)val2).wrapMode = (TextureWrapMode)1;
				((Texture)val2).filterMode = (FilterMode)1;
				Icons.Add(text, new Icon(AssetRetention.Keep<Texture2D>(val2), AssetRetention.Keep<Sprite>(Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f), 100f))));
				yield return null;
			}
		}

		internal static void Register(GearIDRange gear, EnergyGear energy)
		{
			Checksums[gear.GetChecksum()] = Source(energy);
		}

		internal static bool OwnsChecksum(uint checksum)
		{
			return Checksums.ContainsKey(checksum);
		}

		internal static bool TryGetSprite(uint checksum, out Sprite sprite)
		{
			sprite = null;
			if (!Checksums.TryGetValue(checksum, out string value) || !Icons.TryGetValue(value, out var value2) || (Object)(object)value2.Sprite == (Object)null)
			{
				return false;
			}
			sprite = value2.Sprite;
			return true;
		}

		internal static bool Request(GearIDRange gear, Action<IconRenderJobResult> callback, IconRenderSettings settings)
		{
			if (gear != null)
			{
				EnergyGear energyGear = EnergyGears.ForCategory(gear.GetCompID((eGearComponent)2));
				if (energyGear != null)
				{
					if ((Delegate)(object)callback == (Delegate)null)
					{
						Plugin.Error("Energy icon job has no callback: " + new ArgumentNullException("callback"));
						return true;
					}
					if (PresentationPreload.Failed || settings == null || settings.resX < 32 || settings.resX > 2048 || settings.resY < 32 || settings.resY > 2048 || Jobs.Count >= 64)
					{
						string message = (PresentationPreload.Failed ? "preload-failed" : ((Jobs.Count >= 64) ? "queue-full" : "invalid-size"));
						Plugin.Error("Energy icon request refused: " + new InvalidOperationException(message));
						Complete(gear, callback, null);
						return true;
					}
					Jobs.Enqueue(new Job
					{
						Gear = gear,
						Callback = callback,
						Icon = Source(energyGear),
						Width = settings.resX,
						Height = settings.resY,
						Frame = Time.frameCount
					});
					return true;
				}
			}
			return false;
		}

		internal static void Tick()
		{
			for (int i = 0; i < 4; i++)
			{
				if (Jobs.Count == 0)
				{
					break;
				}
				Job job = Jobs.Peek();
				if (PresentationPreload.Failed)
				{
					Jobs.Dequeue();
					Complete(job.Gear, job.Callback, null);
					continue;
				}
				if (job.Frame >= Time.frameCount || (!PresentationPreload.Ready && !Icons.ContainsKey(job.Icon)))
				{
					break;
				}
				Jobs.Dequeue();
				RenderTexture target = null;
				try
				{
					if (!Icons.TryGetValue(job.Icon, out var value))
					{
						throw new InvalidOperationException("Energy icon is unavailable: " + job.Icon);
					}
					target = Target(job.Icon, value.Texture, job.Width, job.Height);
				}
				catch (Exception ex)
				{
					Plugin.Error("Energy icon rendering failed: " + ex);
				}
				Complete(job.Gear, job.Callback, target);
			}
		}

		private static void Complete(GearIDRange gear, Action<IconRenderJobResult> callback, RenderTexture? target)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			try
			{
				callback.Invoke(new IconRenderJobResult
				{
					gearID = gear,
					icon = target
				});
			}
			catch (Exception ex)
			{
				Plugin.Error("Energy icon callback failed: " + ex);
			}
		}

		private static RenderTexture Target(string icon, Texture2D texture, int width, int height)
		{
			//IL_005f: 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_007f: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: 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_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			(string, int, int) key = (icon, width, height);
			if (Targets.TryGetValue(key, out RenderTexture value) && (Object)(object)value != (Object)null && value.IsCreated())
			{
				return value;
			}
			if (Targets.Count >= 64 && !Targets.ContainsKey(key))
			{
				throw new InvalidOperationException("Energy preview target budget exceeded.");
			}
			RenderTexture val = (((Object)(object)value != (Object)null) ? value : AssetRetention.Keep<RenderTexture>(new RenderTexture(width, height, 0, (RenderTextureFormat)0)
			{
				name = "Energy preview " + ((Object)texture).name
			}));
			bool flag = (Object)(object)value == (Object)null;
			RenderTexture active = RenderTexture.active;
			try
			{
				try
				{
					if (!val.Create())
					{
						throw new InvalidOperationException("Cannot create energy preview target.");
					}
					float num = (float)((Texture)texture).width / (float)((Texture)texture).height;
					float num2 = (float)width / (float)height;
					Vector2 val2 = ((num2 > num) ? new Vector2(1f, num / num2) : new Vector2(num2 / num, 1f));
					Graphics.Blit((Texture)(object)texture, val, val2, (Vector2.one - val2) * 0.5f);
					Targets[key] = val;
					return val;
				}
				finally
				{
					RenderTexture.active = active;
				}
			}
			catch
			{
				try
				{
					val.Release();
				}
				catch (Exception ex)
				{
					Plugin.Error("Energy preview target release failed: " + ex);
				}
				if (flag)
				{
					try
					{
						Object.Destroy((Object)(object)val);
					}
					catch (Exception ex2)
					{
						Plugin.Error("Energy preview target destruction failed: " + ex2);
					}
				}
				throw;
			}
		}
	}
	internal readonly struct LightSpec
	{
		internal readonly Color Color;

		internal readonly float Range;

		internal readonly float Intensity;

		internal readonly float Life;

		internal readonly float Flicker;

		internal LightSpec(Color color, float range, float intensity, float life = 0f, float flicker = 0.1f)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Color = color;
			Range = range;
			Intensity = intensity;
			Life = life;
			Flicker = flicker;
		}
	}
	internal static class EnergyLights
	{
		internal const int Capacity = 16;

		private const float HoldFadeIn = 0.04f;

		private const float HoldGrace = 0.06f;

		private const float HoldFadeOut = 0.12f;

		private static readonly FX_PointLight?[] Lights = (FX_PointLight?[])(object)new FX_PointLight[16];

		private static readonly Vector3[] Positions = (Vector3[])(object)new Vector3[16];

		private static readonly float[] Born = new float[16];

		private static readonly float[] Seen = new float[16];

		private static readonly float[] Life = new float[16];

		private static readonly float[] Peak = new float[16];

		private static readonly float[] Strength = new float[16];

		private static readonly float[] Flicker = new float[16];

		private static readonly bool[] Held = new bool[16];

		private static readonly int[] Generation = new int[16];

		internal static void Flash(Vector3 position, in LightSpec spec, float now, float strength = 1f, float delay = 0f)
		{
			//IL_001b: 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)
			int num = Allocate(in spec, now);
			if (num >= 0)
			{
				Held[num] = false;
				Positions[num] = position;
				Born[num] = now + delay;
				Life[num] = Mathf.Max(0.02f, spec.Life);
				Strength[num] = strength;
			}
		}

		internal static void Hold(ref int handle, Vector3 position, in LightSpec spec, float now, float strength = 1f)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			int num = Resolve(handle);
			if (num < 0 || !Held[num])
			{
				num = Allocate(in spec, now);
				if (num < 0)
				{
					handle = 0;
					return;
				}
				Held[num] = true;
				Born[num] = now;
				handle = ((Generation[num] & 0x7FFFFF) << 8) | (num + 1);
			}
			Positions[num] = position;
			Seen[num] = now;
			Strength[num] = strength;
		}

		internal static void Release(ref int handle)
		{
			int num = Resolve(handle);
			if (num >= 0 && Held[num])
			{
				Seen[num] = float.MinValue;
			}
			handle = 0;
		}

		internal static void Tick(float now)
		{
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < 16; i++)
			{
				FX_PointLight val = Lights[i];
				if (val == null)
				{
					continue;
				}
				if ((Object)(object)val == (Object)null || !val.m_isInUse)
				{
					Lights[i] = null;
					Generation[i]++;
					continue;
				}
				float num2;
				if (Held[i])
				{
					float num = now - Seen[i];
					num2 = Mathf.Clamp01((now - Born[i]) / 0.04f) * ((num <= 0.06f) ? 1f : (1f - Mathf.Clamp01((num - 0.06f) / 0.12f)));
					if (num > 0.17999999f)
					{
						Free(i);
						continue;
					}
				}
				else
				{
					float num3 = now - Born[i];
					if (num3 >= Life[i])
					{
						Free(i);
						continue;
					}
					float num4 = ((num3 < 0f) ? 0f : (1f - num3 / Life[i]));
					num2 = ((num3 < 0f) ? 0f : (num4 * num4));
				}
				float num5 = 1f - Flicker[i] * Mathf.PerlinNoise(now * 14f, (float)i * 3.7f);
				val.m_intensity = Peak[i] * Strength[i] * num2 * num5;
				val.m_position = Positions[i];
				((CL_Light)val).UpdateData();
				((CL_Light)val).UpdateTransform();
			}
		}

		internal static void Clear()
		{
			for (int i = 0; i < 16; i++)
			{
				if (Lights[i] != null)
				{
					Free(i);
				}
			}
		}

		private static int Resolve(int handle)
		{
			if (handle == 0)
			{
				return -1;
			}
			int num = (handle & 0xFF) - 1;
			if (num < 0 || num >= 16 || Lights[num] == null || (Generation[num] & 0x7FFFFF) != handle >> 8)
			{
				return -1;
			}
			return num;
		}

		private static int Allocate(in LightSpec spec, float now)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			int num = -1;
			for (int i = 0; i < 16; i++)
			{
				if (Lights[i] == null)
				{
					num = i;
					break;
				}
			}
			if (num < 0)
			{
				float num2 = float.MaxValue;
				for (int j = 0; j < 16; j++)
				{
					if (!Held[j])
					{
						float num3 = Born[j] + Life[j] - now;
						if (num3 < num2)
						{
							num2 = num3;
							num = j;
						}
					}
				}
				if (num < 0)
				{
					return -1;
				}
			}
			FX_PointLight val = Lights[num];
			if (val == null || (Object)(object)val == (Object)null || !val.m_isInUse)
			{
				Lights[num] = null;
				FX_PointLight val2 = default(FX_PointLight);
				if (!FX_Manager.TryAllocateFXLight(ref val2, false) || (Object)(object)val2 == (Object)null)
				{
					return -1;
				}
				val = (Lights[num] = val2);
			}
			Generation[num]++;
			val.SetColor(spec.Color);
			val.SetRange(spec.Range);
			val.m_intensity = 0f;
			Peak[num] = spec.Intensity;
			Flicker[num] = spec.Flicker;
			return num;
		}

		private static void Free(int slot)
		{
			FX_PointLight val = Lights[slot];
			Lights[slot] = null;
			Generation[slot]++;
			if (val != null && !((Object)(object)val == (Object)null) && val.m_isInUse)
			{
				val.m_intensity = 0f;
				((CL_Light)val).UpdateData();
				FX_Manager.DeallocateFXLight(val);
			}
		}
	}
	internal static class EnergySound
	{
		internal readonly struct Cue
		{
			internal readonly string Name;

			internal readonly bool Loop;

			internal readonly bool Spatial;

			internal readonly float Near;

			internal readonly float Far;

			internal Cue(string name, bool loop, bool spatial, float near, float far)
			{
				Name = name;
				Loop = loop;
				Spatial = spatial;
				Near = near;
				Far = far;
			}
		}

		private const float Mix = 2f;

		private const float PanWidth = 0.75f;

		private const int VoiceCapacity = 96;

		internal static readonly Cue[] Cues = new Cue[38]
		{
			Clip("beam-start", 2f, 30f),
			Loop("beam-loop", 2f, 30f),
			Clip("beam-stop", 2f, 30f),
			Clip("beam-hit", 1.5f, 25f),
			Clip("arc-fire", 1f, 24f),
			Loop("arc-loop", 2f, 24f),
			Clip("arc-hit", 1f, 24f),
			Clip("plasma-charge-rise", 2f, 24f),
			Loop("plasma-charge-loop", 2f, 24f),
			Clip("plasma-charge-full", 2f, 24f),
			Clip("plasma-charge-cancel", 2f, 24f),
			Clip("orb-fire", 2f, 30f),
			Loop("orb-fly-loop", 2f, 30f),
			Loop("shock-fly-loop", 2f, 30f),
			Clip("orb-explode", 1.5f, 36f),
			Clip("shock-fire", 2f, 30f),
			Clip("shock-fade", 1.5f, 30f),
			Clip("shock-hit", 1f, 22f),
			Clip("shock-pulse", 1f, 24f),
			Clip("blast-fire", 2f, 30f),
			Clip("blast-cycle", 2f, 30f),
			Clip("blast-explode", 6f, 48f),
			Clip("disc-fire", 2f, 30f),
			Loop("disc-fly-loop", 4f, 36f),
			Clip("disc-bounce", 4f, 36f),
			Clip("disc-drop", 3f, 30f),
			Clip("flame-start", 2f, 24f),
			Loop("flame-loop", 2f, 24f),
			Clip("flame-stop", 2f, 24f),
			Clip("flame-hit", 3f, 18f),
			Clip("flame-crackle", 3f, 18f),
			Clip("gravity-fire", 2f, 30f),
			Loop("gravity-fly-loop", 3f, 30f),
			Clip("gravity-open", 5f, 40f),
			Clip("gravity-close", 5f, 40f),
			Loop("gravity-field-loop", 5f, 40f),
			Clip("energy-dryfire"),
			Clip("mech-dryfire")
		};

		private static readonly string[] SelectedFiles = ReadSelectedFiles();

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

		private static int _volumeFrame = -1;

		private static float _volume;

		private static readonly AudioSource?[] Voices = (AudioSource?[])(object)new AudioSource[96];

		private static readonly int[] VoiceCues = new int[96];

		private static readonly float[] VoiceGains = new float[96];

		private static int _voiceCount;

		private static bool _hasEar;

		private static Vector3 _ear;

		private static Vector3 _earRight;

		internal static float MasterVolume
		{
			get
			{
				int frameCount = Time.frameCount;
				if (frameCount == _volumeFrame)
				{
					return _volume;
				}
				_volumeFrame = frameCount;
				_volume = 2f * GameVolume();
				return _volume;
			}
		}

		private static Cue Clip(string name)
		{
			return new Cue(name, loop: false, spatial: false, 0f, 0f);
		}

		private static Cue Clip(string name, float near, float far)
		{
			return new Cue(name, loop: false, spatial: true, near, far);
		}

		private static Cue Loop(string name, float near, float far)
		{
			return new Cue(name, loop: true, spatial: true, near, far);
		}

		private static float GameVolume()
		{
			try
			{
				CellSettingsData settingsData = CellSettingsManager.SettingsData;
				CellSettings_Audio val = ((settingsData != null) ? settingsData.Audio : null);
				if (((val != null) ? val.MasterVolume : null) == null || val.SFXVolume == null)
				{
					return Unreadable(new InvalidOperationException("Audio settings are not loaded."));
				}
				CS_Value<bool> muteInBackground = val.MuteInBackground;
				if (muteInBackground != null && muteInBackground.Value && !Application.isFocused)
				{
					return 0f;
				}
				return Clamp01(val.MasterVolume.Value) * Clamp01(val.SFXVolume.Value);
			}
			catch (Exception error)
			{
				return Unreadable(error);
			}
		}

		private static float Unreadable(Exception error)
		{
			Plugin.Error("Game volume unreadable; energy sounds muted: " + error);
			return 0f;
		}

		private static float Clamp01(float value)
		{
			if (!float.IsNaN(value) && !(value < 0f))
			{
				if (!(value > 1f))
				{
					return value;
				}
				return 1f;
			}
			return 0f;
		}

		internal static int IndexOf(string cue)
		{
			for (int i = 0; i < Cues.Length; i++)
			{
				if (Cues[i].Name == cue)
				{
					return i;
				}
			}
			throw new ArgumentException("Unknown energy sound cue: " + cue, "cue");
		}

		internal static AudioSource Voice(Transform parent, string? name = null)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if (name != null)
			{
				Transform transform = new GameObject(name).transform;
				transform.SetParent(parent, false);
				parent = transform;
			}
			AudioSource obj = ((Component)parent).gameObject.AddComponent<AudioSource>();
			obj.playOnAwake = false;
			return obj;
		}

		internal static void PlaySpatial(AudioSource[] voices, ref int cursor, string cue, Vector3 position, float gain)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			AudioSource val = null;
			for (int i = 0; i < voices.Length; i++)
			{
				int num = (cursor + i) % voices.Length;
				AudioSource val2 = voices[num];
				if (!((Object)(object)val2 == (Object)null) && !val2.isPlaying)
				{
					val = val2;
					cursor = (num + 1) % voices.Length;
					break;
				}
			}
			if (val == null)
			{
				val = voices[cursor++ % voices.Length];
			}
			if (!((Object)(object)val == (Object)null))
			{
				((Component)val).transform.position = position;
				Stop(val);
				PlayCue(val, cue, gain);
			}
		}

		internal static void PlayCue(AudioSource source, string cue, float gain)
		{
			int num = IndexOf(cue);
			Cue cue2 = Cues[num];
			int num2 = Slot(source);
			if (num2 < 0)
			{
				Stop(source);
				return;
			}
			VoiceCues[num2] = num;
			VoiceGains[num2] = gain;
			source.loop = cue2.Loop;
			source.dopplerLevel = 0f;
			source.spatialBlend = 0f;
			AudioClip val = LoadSelected(num);
			if ((Object)(object)source.clip != (Object)(object)val)
			{
				source.clip = val;
			}
			Apply(num2);
			source.Play();
		}

		internal static bool PlayingCue(AudioSource source, string cue)
		{
			if (!source.isPlaying)
			{
				return false;
			}
			int num = IndexOf(cue);
			for (int i = 0; i < _voiceCount; i++)
			{
				if (Voices[i] == source)
				{
					return VoiceCues[i] == num;
				}
			}
			return false;
		}

		internal static void Gain(AudioSource source, float gain)
		{
			int num = Find(source);
			if (num >= 0 && source.isPlaying)
			{
				VoiceGains[num] = gain;
				Apply(num);
			}
		}

		internal static void Listen(Transform? ear)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			_hasEar = ear != null && (Object)(object)ear != (Object)null;
			if (_hasEar)
			{
				_ear = ear.position;
				_earRight = ear.right;
			}
			int num = 0;
			for (int i = 0; i < _voiceCount; i++)
			{
				AudioSource val = Voices[i];
				if (val != null && !((Object)(object)val == (Object)null) && val.isPlaying)
				{
					Voices[num] = val;
					VoiceCues[num] = VoiceCues[i];
					VoiceGains[num] = VoiceGains[i];
					Apply(num);
					num++;
				}
			}
			for (int j = num; j < _voiceCount; j++)
			{
				Voices[j] = null;
			}
			_voiceCount = num;
		}

		private static int Slot(AudioSource source)
		{
			int num = Find(source);
			if (num >= 0)
			{
				return num;
			}
			for (int num2 = _voiceCount - 1; num2 >= 0; num2--)
			{
				if ((Object)(object)Voices[num2] == (Object)null || !Voices[num2].isPlaying)
				{
					Release(num2);
				}
			}
			if (_voiceCount == 96)
			{
				return -1;
			}
			Voices[_voiceCount] = source;
			return _voiceCount++;
		}

		private static int Find(AudioSource source)
		{
			for (int i = 0; i < _voiceCount; i++)
			{
				if (Voices[i] == source)
				{
					return i;
				}
			}
			return -1;
		}

		private static void Release(int slot)
		{
			int num = --_voiceCount;
			Voices[slot] = Voices[num];
			VoiceCues[slot] = VoiceCues[num];
			VoiceGains[slot] = VoiceGains[num];
			Voices[num] = null;
		}

		internal static void Stop(AudioSource source)
		{
			if ((Object)(object)source != (Object)null)
			{
				source.Stop();
			}
			int num = Find(source);
			if (num >= 0)
			{
				Release(num);
			}
		}

		private static void Apply(int slot)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			AudioSource val = Voices[slot];
			Cue entry = Cues[VoiceCues[slot]];
			float num = VoiceGains[slot] * MasterVolume;
			if (!entry.Spatial)
			{
				val.volume = Math.Min(1f, num);
				val.panStereo = 0f;
			}
			else
			{
				Place(in entry, ((Component)val).transform.position, out var heard, out var pan);
				val.volume = Math.Min(1f, num * heard);
				val.panStereo = pan;
			}
		}

		internal static void Place(in Cue entry, Vector3 position, out float heard, out float pan)
		{
			//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)
			//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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			heard = 0f;
			pan = 0f;
			if (_hasEar)
			{
				Vector3 val = position - _ear;
				float magnitude = ((Vector3)(ref val)).magnitude;
				heard = ((magnitude <= entry.Near) ? 1f : ((magnitude >= entry.Far) ? 0f : (1f - (magnitude - entry.Near) / (entry.Far - entry.Near))));
				if (!(magnitude < 0.01f))
				{
					float value = (val.x * _earRight.x + val.y * _earRight.y + val.z * _earRight.z) / magnitude;
					pan = Math.Clamp(value, -1f, 1f) * 0.75f * Math.Min(1f, magnitude / entry.Near);
				}
			}
		}

		internal static AudioClip Load(string name)
		{
			return LoadSelected(IndexOf(name));
		}

		private static string[] ReadSelectedFiles()
		{
			using Stream stream = typeof(EnergySound).Assembly.GetManifestResourceStream("ForgeWeaponEnergyLabExperimental.Audio.variants.tsv") ?? throw new InvalidOperationException("Generated audio variant map is missing from this build.");
			using StreamReader streamReader = new StreamReader(stream);
			string[] array = new string[Cues.Length];
			string text;
			while ((text = streamReader.ReadLine()) != null)
			{
				string[] array2 = text.Split('\t');
				if (array2.Length != 2 || !array2[1].EndsWith(".bin", StringComparison.Ordinal))
				{
					throw new InvalidDataException("Malformed generated audio variant map.");
				}
				int num = IndexOf(array2[0]);
				if (array[num] != null)
				{
					throw new InvalidDataException("Duplicate recording for cue " + array2[0]);
				}
				array[num] = array2[1];
			}
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == null)
				{
					throw new InvalidDataException("Missing recording for cue " + Cues[i].Name);
				}
			}
			return array;
		}

		private static AudioClip LoadSelected(int index)
		{
			string text = SelectedFiles[index];
			if (Loaded.TryGetValue(text, out AudioClip value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			if (PresentationPreload.Ready)
			{
				throw new InvalidOperationException("Energy sound was not prepared: " + text);
			}
			using Stream stream = typeof(EnergySound).Assembly.GetManifestResourceStream("ForgeWeaponEnergyLabExperimental.Audio.Variants." + text + ".gz") ?? throw new InvalidOperationException("Missing selected energy sound: " + text);
			if (stream.Length > 1152000)
			{
				throw new InvalidDataException("Energy sound exceeds the resource budget.");
			}
			using MemoryStream memoryStream = new MemoryStream();
			using (GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true))
			{
				byte[] array = new byte[8192];
				int num;
				while ((num = gZipStream.Read(array, 0, array.Length)) > 0)
				{
					if (memoryStream.Length + num > 1152000)
					{
						throw new InvalidDataException("Decoded energy sound exceeds the resource budget.");
					}
					memoryStream.Write(array, 0, num);
				}
			}
			float[] array2 = PcmSamples.Decode(memoryStream.GetBuffer().AsSpan(0, checked((int)memoryStream.Length)));
			AudioClip val = AssetRetention.Keep<AudioClip>(AudioClip.Create("energy-" + Cues[index].Name + "-0", array2.Length, 1, 48000, false));
			try
			{
				if (val.channels != 1 || val.samples != array2.Length || val.frequency != 48000)
				{
					throw new InvalidOperationException("Unity created an unexpected audio buffer for " + text);
				}
				Il2CppStructArray<float> val2 = new Il2CppStructArray<float>(array2);
				try
				{
					if (!AudioClip.SetData(val, val2, array2.Length, 0))
					{
						throw new InvalidOperationException("Unity rejected the energy audio buffer: " + text);
					}
					int num2 = Math.Min(32, array2.Length);
					int num3 = (array2.Length - num2) / 2;
					Il2CppStructArray<float> val3 = new Il2CppStructArray<float>((long)num2);
					if (!AudioClip.GetData(val, val3, num2, num3))
					{
						throw new InvalidOperationException("Unity could not read back the energy audio buffer: " + text);
					}
					for (int i = 0; i < num2; i++)
					{
						if (!float.IsFinite(((Il2CppArrayBase<float>)(object)val3)[i]) || MathF.Abs(((Il2CppArrayBase<float>)(object)val3)[i] - array2[num3 + i]) > 0.0001f)
						{
							throw new InvalidOperationException("Unity energy audio readback differs: " + text);
						}
					}
					GC.KeepAlive(val3);
				}
				finally
				{
					GC.KeepAlive(val2);
				}
				Loaded[text] = val;
				return val;
			}
			catch
			{
				Object.Destroy((Object)(object)val);
				throw;
			}
		}
	}
	internal sealed class EnergyVisuals : IDisposable
	{
		private const string BeamMember = "forge_energy_beam";

		private const string ArcMember = "forge_lightning_arc";

		private const string IgniteState = "ignite";

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

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

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

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

		private const int Capacity = 4;

		private readonly VfxPool _pool = new VfxPool("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 VfxInstance? _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;

		public EnergyVisuals()
		{
			VfxLibrary.Prefab("forge_energy_beam");
			VfxLibrary.Prefab("forge_lightning_arc");
		}

		internal IEnumerator Prepare()
		{
			if (_prepared)
			{
				yield break;
			}
			foreach (object item in _pool.Prepare("forge_energy_beam", 2))
			{
				yield return item;
			}
			foreach (object item2 in _pool.Prepare("forge_lightning_arc", 8))
			{
				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("forge_energy_beam").Hold();
				}
				_beam.Stretch(start, end);
				_beam.Emit("impact", hit);
				_beam.Keep();
				if (num)
				{
					_beam.PlayState("ignite");
				}
				EnergyLights.Hold(ref _beamMuzzleLight, start, in BeamMuzzleLight, now);
				if (hit)
				{
					EnergyLights.Hold(ref _beamImpactLight, end, in BeamImpactLight, now);
				}
				else
				{
					EnergyLights.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();
				EnergyLights.Flash(start, in BlastMuzzleLight, now);
			}
		}

		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)
			{
				VfxInstance vfxInstance = _pool.Take("forge_lightning_arc");
				vfxInstance.Stretch(_starts[_started], _ends[_started]);
				vfxInstance.Emit("end", _hits[_started]);
				vfxInstance.Start();
				if (_hits[_started])
				{
					EnergyLights.Flash(_ends[_started], in ArcLight, now);
				}
				_started++;
			}
		}

		public void Hide()
		{
			if (_segments != 0 || _beam != null || _beamMuzzleLight != 0 || _beamImpactLight != 0)
			{
				_segments = (_started = 0);
				_beam?.Release();
				_beam = null;
				EnergyLights.Release(ref _beamMuzzleLight);
				EnergyLights.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 OrbMember = "forge_plasma_orb";

		private const string DischargeMember = "forge_plasma_discharge";

		private const string ExplosionMember = "forge_plasma_explosion";

		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 LightSpec OrbLight = new LightSpec(new Color(0.25f, 0.55f, 1f), 4f, 1.4f, 0f, 0.25f);

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

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

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

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

		private static readonly LightSpec ExplosionLight = new LightSpec(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 static readonly Color ElectricTint = new Color(1f, 0.7f, 1f);

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

		private readonly VfxInstance?[] _orbs;

		private readonly bool[] _exploded;

		private readonly Vector3[] _positions;

		private readonly bool[] _electric;

		private readonly bool[] _flying;

		private readonly int[] _lights;

		private readonly GameObject _voices;

		private readonly AudioSource[] _flightVoices;

		private readonly AudioSource[] _impactVoices = (AudioSource[])(object)new AudioSource[4];

		private readonly AudioSource[] _arcVoices = (AudioSource[])(object)new AudioSource[6];

		private int _impactCursor;

		private int _arcCursor;

		private bool _disposed;

		private bool _prepared;

		private bool Ready => !_disposed;

		public PlasmaEffects(int capacity)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			_orbs = new VfxInstance[capacity];
			_exploded = new bool[capacity];
			_positions = (Vector3[])(object)new Vector3[capacity];
			_electric = new bool[capacity];
			_flying = new bool[capacity];
			_lights = new int[capacity];
			_flightVoices = (AudioSource[])(object)new AudioSource[capacity];
			_voices = new GameObject("Plasma ionization voices");
			Object.DontDestroyOnLoad((Object)(object)_voices);
			try
			{
				VfxLibrary.Prefab("forge_plasma_orb");
				VfxLibrary.Prefab("forge_plasma_discharge");
				VfxLibrary.Prefab("forge_plasma_explosion");
				for (int i = 0; i < _flightVoices.Length; i++)
				{
					_flightVoices[i] = EnergySound.Voice(_voices.transform, "plasma-flight-voice-" + i);
				}
				for (int j = 0; j < _impactVoices.Length; j++)
				{
					_impactVoices[j] = EnergySound.Voice(_voices.transform, "plasma-impact-voice-" + j);
				}
				for (int k = 0; k < _arcVoices.Length; k++)
				{
					_arcVoices[k] = EnergySound.Voice(_voices.transform, "plasma-arc-voice-" + k);
				}
			}
			catch
			{
				Dispose();
				throw;
			}
		}

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

		internal IEnumerator Prepare()
		{
			if (_prepared)
			{
				yield break;
			}
			foreach (object item in _pool.Prepare("forge_plasma_orb", _orbs.Length * 2))
			{
				yield return item;
			}
			foreach (object item2 in _pool.Prepare("forge_plasma_orb", _orbs.Length * 2, ElectricTint))
			{
				yield return item2;
			}
			foreach (object item3 in _pool.Prepare("forge_plasma_discharge", _orbs.Length * 3 * 2))
			{
				yield return item3;
			}
			foreach (object item4 in _pool.Prepare("forge_plasma_explosion", _orbs.Length * 2))
			{
				yield return item4;
			}
			_prepared = true;
		}

		public void ChargingOrb(int index, Vector3 position, float radius, float now, bool electric)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Orb(index, position, radius * 0.55f, now, electric, flying: false);
		}

		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_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready)
			{
				return;
			}
			Draw(index, position, radius, electric, flying, now);
			if (!flying)
			{
				return;
			}
			AudioSource val = _flightVoices[index];
			if (Alive((Object?)(object)val))
			{
				((Component)val).transform.position = position;
				if (!val.isPlaying)
				{
					val.pitch = 1f;
					EnergySound.PlayCue(val, electric ? "shock-fly-loop" : "orb-fly-loop", 0.85f);
				}
			}
		}

		public void Fade(int index, Vector3 position)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				StopFlight(index);
				_orbs[index]?.PlayState("fizzle");
				EnergySound.PlaySpatial(_arcVoices, ref _arcCursor, "shock-fade", position, 0.5f);
			}
		}

		private void StopFlight(int index)
		{
			AudioSource val = _flightVoices[index];
			if (Alive((Object?)(object)val) && val.isPlaying)
			{
				EnergySound.Stop(val);
			}
		}

		public void Contact(Vector3 position)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				EnergySound.PlaySpatial(_arcVoices, ref _arcCursor, "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, 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;
				}
				VfxInstance vfxInstance = _pool.Take("forge_plasma_explosion");
				vfxInstance.Place(position, Quaternion.identity, Mathf.Max(0.1f, blastRadius / 3f));
				vfxInstance.Start();
				EnergyLights.Flash(position, in ExplosionLight, now, Mathf.Clamp(blastRadius / 3f, 0.6f, 1.4f));
			}
		}

		private void Draw(int index, Vector3 position, float radius, bool electric, bool flying, float now)
		{
			//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_00af: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			_exploded[index] = false;
			VfxInstance vfxInstance = _orbs[index];
			if (vfxInstance != null && _electric[index] != electric)
			{
				vfxInstance.Release();
				_orbs[index] = null;
			}
			bool num = _orbs[index] == null;
			ref VfxInstance reference = ref _orbs[index];
			VfxInstance vfxInstance2 = reference ?? (reference = _pool.Take("forge_plasma_orb", electric ? new Color?(ElectricTint) : ((Color?)null)).Hold());
			_electric[index] = electric;
			_positions[index] = position;
			float num2 = Mathf.Max(0.02f, radius / 0.3f);
			vfxInstance2.Place(position, Quaternion.identity, num2);
			vfxInstance2.Keep();
			if (num)
			{
				_flying[index] = false;
			}
			if (num && !flying)
			{
				vfxInstance2.PlayState("charging");
			}
			if (flying && !_flying[index])
			{
				_flying[index] = true;
				vfxInstance2.PlayState("launch");
				if (electric)
				{
					vfxInstance2.PlayState("storm");
				}
				EnergyLights.Flash(position, in LaunchLight, now, Mathf.Clamp(num2, 0.5f, 1.2f));
			}
			EnergyLights.Hold(ref _lights[index], position, electric ? ElectricOrbLight : OrbLight, now, Mathf.Clamp(num2, 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: 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)
			if (Ready)
			{
				if (targets.Length < 1 || targets.Length > 3)
				{
					throw new ArgumentException("Invalid orb pulse targets.");
				}
				Vector3 val = _positions[index];
				EnergySound.PlaySpatial(_arcVoices, ref _arcCursor, "shock-pulse", val, 0.22f);
				EnergyLights.Flash(val, in DischargeLight, now);
				ReadOnlySpan<Vector3> readOnlySpan = targets;
				for (int i = 0; i < readOnlySpan.Length; i++)
				{
					Vector3 end = readOnlySpan[i];
					VfxInstance vfxInstance = _pool.Take("forge_plasma_discharge");
					vfxInstance.Stretch(val, end);
					vfxInstance.Start();
				}
			}
		}

		public void Charged(int index)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				VfxInstance vfxInstance = _orbs[index];
				if (vfxInstance != null)
				{
					vfxInstance.PlayState("charged");
					EnergyLights.Flash(_positions[index], in ChargedLight, Time.time);
				}
			}
		}

		private void ReleaseOrb(int index)
		{
			_orbs[index]?.Release();
			_orbs[index] = null;
			_flying[index] = false;
			EnergyLights.Release(ref _lights[index]);
		}

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

		public void Boom(Vector3 position)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				EnergySound.PlaySpatial(_impactVoices, ref _impactCursor, "orb-explode", position, 0.48f);
			}
		}

		public void Dispose()
		{
			if (_disposed)
			{
				return;
			}
			_disposed = true;
			AudioSource[] impactVoices = _impactVoices;
			foreach (AudioSource val in impactVoices)
			{
				if (Alive((Object?)(object)val))
				{
					EnergySound.Stop(val);
				}
			}
			impactVoices = _arcVoices;
			foreach (AudioSource val2 in impactVoices)
			{
				if (Alive((Object?)(object)val2))
				{
					EnergySound.Stop(val2);
				}
			}
			impactVoices = _flightVoices;
			foreach (AudioSource val3 in impactVoices)
			{
				if (Alive((Object?)(object)val3))
				{
					EnergySound.Stop(val3);
				}
			}
			_pool.Dispose();
			if (Alive((Object?)(object)_voices))
			{
				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; } = Arra