Decompiled source of ForgeWeapon EnergyLab v1.0.0
ForgeWeaponEnergyLab.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using AIGraph; using Agents; using BepInEx; using BepInEx.Unity.IL2CPP; using Enemies; using FX_EffectSystem; using ForgeAudio; using ForgeCore; using ForgeUI; using ForgeVisual; using ForgeWeaponEnergyLab.Api; using ForgeWeaponEnergyLab.Native; using ForgeWeaponEnergyLab.Presentation; using GTFO.API; using GameData; using Gear; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Localization; using MTFO.API; using Microsoft.CodeAnalysis; using Player; using RootMotion.FinalIK; using SNetwork; using UnityEngine; using UnityEngine.AI; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ForgeWeaponEnergyLab")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+66ec6b43e1a1603bac84a4b9a226674300b2fc7d")] [assembly: AssemblyProduct("ForgeWeaponEnergyLab")] [assembly: AssemblyTitle("ForgeWeaponEnergyLab")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ForgeWeaponEnergyLab { internal sealed class BudgetExhausted : InvalidOperationException { public BudgetExhausted(string message) : base(message) { } } internal static class EnergyConfiguration { internal static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }; private static readonly Dictionary<string, string> Descriptions = new Dictionary<string, string> { ["magazine"] = "Whole rounds in the magazine (1..10000).", ["ammoPerRound"] = "Native reserve units per round (>0).", ["range"] = "Chain Arc first-hit distance or flame length in metres (>0); the other weapons have no range limit.", ["interval"] = "Seconds between discharges (0.01..60).", ["damage"] = "Base direct, explosion or field-pulse damage; minimum-charge damage for orbs.", ["ammoCost"] = "Whole magazine rounds per discharge; must be 1 for every weapon, at every charge power.", ["stagger"] = "Native stagger multiplier for direct/selected hits (0..100).", ["precision"] = "Native precision multiplier for direct/selected hits (0..100).", ["reloadTime"] = "Native reload duration in seconds (0.01..120).", ["equipTime"] = "Native equip transition duration in seconds (0.01..120).", ["aimTime"] = "Native aim transition duration in seconds (0.01..120).", ["targets"] = "Chain recipients including the first hit (1..4 packet budget).", ["jumpRange"] = "Maximum chain hop distance in metres (>0).", ["damageRetention"] = "Damage fraction retained after each chain hop (0..1).", ["arcLifetime"] = "Arc hit-feedback duration in seconds (>0).", ["hopDelay"] = "Visual hop delay in seconds (0..10); damage is immediate.", ["minimumCharge"] = "Minimum hold seconds to fire (>=0.1).", ["fullCharge"] = "Hold seconds for full power (>minimumCharge); holding longer keeps full power until release.", ["botChargePower"] = "Power of a native bot shot (0..1).", ["speed"] = "Projectile speed in metres/second (>0).", ["fullSpeed"] = "Full-charge projectile speed in metres/second (>0).", ["bodyRadius"] = "Projectile collision radius in metres (>0).", ["fullBodyRadius"] = "Full-charge collision radius in metres (>0).", ["travelDistance"] = "Minimum-charge flight distance in metres (>0).", ["fullTravelDistance"] = "Full-charge flight distance in metres (>0).", ["fullDamage"] = "Full-charge damage (>=damage).", ["activeProjectiles"] = "Per-definition active limit within shared slots (discs 1..3, others 1..4).", ["blastRadius"] = "Explosion radius in metres with native linear falloff (>0).", ["fullBlastRadius"] = "Full-charge explosion radius in metres (>0).", ["explosionLifetime"] = "Explosion slot/feedback duration in seconds (>0).", ["noiseRadius"] = "Native explosion noise radius in metres (0..1000).", ["explosionForce"] = "Native explosion receiver force (0..1000).", ["arcRadius"] = "Minimum-charge pulse radius in metres (>0).", ["fullArcRadius"] = "Full-charge pulse radius in metres (>0).", ["pulseInterval"] = "Seconds between damage pulses (0.01..60).", ["pulseDamageFraction"] = "Fraction of orb direct damage applied by a pulse (0..1).", ["pulseStagger"] = "Native pulse stagger multiplier (0..100).", ["pulseTargets"] = "Recipients per flight pulse (1..3 effect budget).", ["fadeLifetime"] = "Fade/slot duration after flight in seconds (>0).", ["bounces"] = "Allowed wall bounces (0..128).", ["lifetime"] = "Disc flight or gravity field duration in seconds (>0).", ["startRadius"] = "Flame cone radius at the muzzle in metres (>0).", ["radiusPerMetre"] = "Flame cone widening per metre (0..10).", ["flightLifetime"] = "Gravity flight seconds before field activation (>0).", ["gravity"] = "Downward projectile acceleration in metres/second squared (0..1000).", ["radius"] = "Gravity outer damage/pull radius in metres (>coreRadius).", ["coreRadius"] = "Full-damage inner radius in metres (>0, <radius).", ["collapseDuration"] = "Final field seconds without damage/pull (>0, <lifetime).", ["growthDuration"] = "Visible field growth duration in seconds (>0).", ["edgeDamageFraction"] = "Damage fraction at the outer field edge (0..1).", ["pullSpeed"] = "Attraction speed in metres/second (0..1000).", ["queryInterval"] = "Maximum target re-query interval in seconds (0.01..60)." }; internal const string OverridesFile = "tuning-overrides.json"; internal const string DefaultsFile = "tuning-defaults.json"; private static readonly string[] RootFields = new string[1] { "weapons" }; internal static JsonNode TuningJson(EnergyTuning tuning) { return JsonSerializer.SerializeToNode(tuning, tuning.GetType(), JsonOptions); } internal static EnergyTuning DefaultTuning(EnergyMode mode) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) return mode switch { EnergyMode.Beam => new BeamTuning(), EnergyMode.ArcChain => new ArcChainTuning(), EnergyMode.PlasmaBlast => new PlasmaBlastTuning(), EnergyMode.PlasmaArc => new PlasmaArcTuning(), EnergyMode.Blast => new BlastTuning(), EnergyMode.Disc => new DiscTuning(), EnergyMode.Flame => new FlameTuning(), EnergyMode.BlackHole => new BlackHoleTuning(), _ => throw new StrictJsonException("Mode", "must be a firing behaviour, not Off."), }; } internal static EnergyTuning MergeTuning(EnergyTuning current, JsonElement item, string field) { Type type = current.GetType(); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); StrictJson.OnlyKnown(item, field + ".", properties.Select((PropertyInfo p) => JsonOptions.PropertyNamingPolicy.ConvertName(p.Name)).ToArray(), (string[])null, (string)null); JsonObject jsonObject = TuningJson(current).AsObject(); PropertyInfo[] array = properties; foreach (PropertyInfo propertyInfo in array) { string text = JsonOptions.PropertyNamingPolicy.ConvertName(propertyInfo.Name); if (item.TryGetProperty(text, out var value)) { double num2 = ((propertyInfo.PropertyType == typeof(float)) ? 3.4028234663852886E+38 : double.MaxValue); if (propertyInfo.PropertyType == typeof(int)) { StrictJson.Integer(item, field + ".", text, (double?)null, -2147483648.0, 2147483647.0); } else { StrictJson.Number(item, field + ".", text, (double?)null, 0.0 - num2, num2); } jsonObject[text] = JsonNode.Parse(value.GetRawText()); } } return (EnergyTuning)jsonObject.Deserialize(type, JsonOptions); } internal static WeaponDefinition[] Load(string directory, WeaponDefinition[] defaults, List<string> errors) { //IL_02fa: Expected O, but got Unknown //IL_047c: Expected O, but got Unknown string path = Path.Combine(directory, "tuning-overrides.json"); WeaponDefinition[] array; try { Directory.CreateDirectory(directory); JsonObject jsonObject = new JsonObject(); array = defaults; foreach (WeaponDefinition weaponDefinition in array) { jsonObject.Add(weaponDefinition.Id, TuningJson(weaponDefinition.Tuning)); } File.WriteAllText(Path.Combine(directory, "tuning-defaults.json"), new JsonObject { ["_note"] = "Reference only, rewritten on every launch. Copy the values you want to change into tuning-overrides.json.", ["_descriptions"] = JsonSerializer.SerializeToNode(Descriptions, JsonOptions), ["weapons"] = jsonObject }.ToJsonString(JsonOptions), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); if (!File.Exists(path)) { using FileStream stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.None); using StreamWriter streamWriter = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); streamWriter.Write(new JsonObject { ["weapons"] = new JsonObject() }.ToJsonString(JsonOptions)); } } catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException) ? 1 : 0) != 0) { errors.Add(directory + ": could not write the tuning files: " + ex); } JsonDocument jsonDocument; try { jsonDocument = JsonDocument.Parse(File.ReadAllText(path)); } catch (JsonException ex2) { errors.Add($"{"tuning-overrides.json"}: invalid JSON at line {ex2.LineNumber + 1}, byte {ex2.BytePositionInLine + 1}; no override applied."); array = defaults; goto IL_05b5; } catch (IOException value) { errors.Add($"{"tuning-overrides.json"}: could not be read: {value}; no override applied."); array = defaults; goto IL_05b5; } using (jsonDocument) { JsonElement jsonElement; try { StrictJson.UniqueMembers(jsonDocument.RootElement, ""); StrictJson.OnlyKnown(jsonDocument.RootElement, "", RootFields, (string[])null, (string)null); jsonElement = StrictJson.Object(jsonDocument.RootElement, "", "weapons"); } catch (StrictJsonException ex3) { StrictJsonException ex4 = ex3; errors.Add($"{"tuning-overrides.json"}: {ex4.Field}: {((Exception)(object)ex4).Message} No override applied."); array = defaults; goto end_IL_02b8; } HashSet<string> hashSet = new HashSet<string>(defaults.Select((WeaponDefinition d) => d.Id), StringComparer.Ordinal); foreach (JsonProperty item in jsonElement.EnumerateObject()) { if (!hashSet.Contains(item.Name)) { errors.Add("tuning-overrides.json: weapons." + item.Name + ": no weapon with this id is loaded (is its package installed?); skipped."); } } WeaponDefinition[] array2 = new WeaponDefinition[defaults.Length]; for (int num = 0; num < defaults.Length; num++) { WeaponDefinition weaponDefinition2 = (array2[num] = defaults[num]); if (jsonElement.TryGetProperty(weaponDefinition2.Id, out var value2)) { string text = "weapons." + weaponDefinition2.Id; try { EnergyTuning energyTuning = MergeTuning(weaponDefinition2.Tuning, value2, text); ValidateTuning(weaponDefinition2.Mode, energyTuning, text); array2[num] = weaponDefinition2 with { Tuning = energyTuning }; } catch (StrictJsonException ex5) { StrictJsonException ex6 = ex5; errors.Add($"{"tuning-overrides.json"}: {ex6.Field}: {((Exception)(object)ex6).Message} This weapon keeps its default tuning."); } catch (InvalidDataException ex7) { errors.Add("tuning-overrides.json: " + ex7.Message + ". This weapon keeps its default tuning."); } catch (JsonException ex8) { errors.Add($"{"tuning-overrides.json"}: {text}: invalid number type at line {ex8.LineNumber + 1}. This weapon keeps its default tuning."); } } } array = array2; end_IL_02b8:; } goto IL_05b5; IL_05b5: return array; } internal static void ValidateDefinition(WeaponDefinition definition) { if (definition == null) { throw new ArgumentNullException("definition"); } if (definition.Id == null || definition.Id.Length > 128 || !Regex.IsMatch(definition.Id, "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*){2,}$")) { Fail(definition.Id + ".id", "expected a lowercase namespaced id such as author.mod.weapon (at most 128 characters)"); } if (string.IsNullOrWhiteSpace(definition.Name) || definition.Name.Length > 128) { Fail(definition.Id + ".name", "expected a nonempty name of at most 128 characters"); } string description = definition.Description; if (description != null && (string.IsNullOrWhiteSpace(description) || description.Length > 512)) { Fail(definition.Id + ".description", "expected null or a nonempty text of at most 512 characters"); } WeaponSlot slot = definition.Slot; if ((uint)slot > 1u) { Fail(definition.Id + ".slot", "expected Main or Special"); } if (definition.Model == null || string.IsNullOrWhiteSpace(definition.Model.Key) || definition.Model.Key.Length > 128) { Fail(definition.Id + ".model.key", "expected a prefab key"); } if (string.IsNullOrWhiteSpace(definition.Model.BundlePath) || !Path.IsPathFullyQualified(definition.Model.BundlePath)) { Fail(definition.Id + ".model.bundlePath", "expected an absolute bundle path"); } ValidateTuning(definition.Mode, definition.Tuning, definition.Id + ".tuning"); } internal static void ValidateTuning(EnergyMode mode, EnergyTuning t, string path) { if (mode switch { EnergyMode.Beam => (t is BeamTuning) ? 1 : 0, EnergyMode.ArcChain => (t is ArcChainTuning) ? 1 : 0, EnergyMode.PlasmaBlast => (t is PlasmaBlastTuning) ? 1 : 0, EnergyMode.PlasmaArc => (t is PlasmaArcTuning) ? 1 : 0, EnergyMode.Blast => (t is BlastTuning) ? 1 : 0, EnergyMode.Disc => (t is DiscTuning) ? 1 : 0, EnergyMode.Flame => (t is FlameTuning) ? 1 : 0, EnergyMode.BlackHole => (t is BlackHoleTuning) ? 1 : 0, _ => 0, } == 0) { Fail(path, "tuning record type must match a firing behaviour"); } Check("Magazine", t.Magazine, 1.0, 10000.0); Check("AmmoCost", t.AmmoCost, 1.0, t.Magazine); if (t.AmmoCost != 1) { Fail(path + ".ammoCost", "native fire spends exactly one magazine round at every charge power"); } Check("AmmoPerRound", t.AmmoPerRound, 0.001, 1000000.0); Check("Interval", t.Interval, 0.01, 60.0); Check("Damage", t.Damage, 0.0, 1000000.0); Check("Stagger", t.Stagger, 0.0, 100.0); Check("Precision", t.Precision, 0.0, 100.0); Check("ReloadTime", t.ReloadTime, 0.01, 120.0); Check("EquipTime", t.EquipTime, 0.01, 120.0); Check("AimTime", t.AimTime, 0.01, 120.0); if (!(t is ArcChainTuning arcChainTuning)) { if (!(t is PlasmaTuning plasmaTuning)) { if (!(t is BlastTuning blastTuning)) { if (!(t is DiscTuning discTuning)) { if (!(t is FlameTuning flameTuning)) { if (t is BlackHoleTuning blackHoleTuning) { Check("Speed", blackHoleTuning.Speed, 0.01, 1000.0); Check("BodyRadius", blackHoleTuning.BodyRadius, 0.01, 100.0); Check("FlightLifetime", blackHoleTuning.FlightLifetime, 0.01, 120.0); Check("Gravity", blackHoleTuning.Gravity, 0.0, 1000.0); Check("Radius", blackHoleTuning.Radius, 0.02, 100.0); Check("CoreRadius", blackHoleTuning.CoreRadius, 0.01, (double)blackHoleTuning.Radius - 0.01); Check("Lifetime", blackHoleTuning.Lifetime, 0.02, 120.0); Check("CollapseDuration", blackHoleTuning.CollapseDuration, 0.01, (double)blackHoleTuning.Lifetime - 0.01); Check("GrowthDuration", blackHoleTuning.GrowthDuration, 0.01, 120.0); Check("PulseInterval", blackHoleTuning.PulseInterval, 0.01, 60.0); Check("EdgeDamageFraction", blackHoleTuning.EdgeDamageFraction, 0.0, 1.0); Check("PullSpeed", blackHoleTuning.PullSpeed, 0.0, 1000.0); Check("QueryInterval", blackHoleTuning.QueryInterval, 0.01, 60.0); } } else { Check("Range", flameTuning.Range, 0.01, 1000.0); Check("StartRadius", flameTuning.StartRadius, 0.01, 100.0); Check("RadiusPerMetre", flameTuning.RadiusPerMetre, 0.0, 10.0); } } else { Check("Speed", discTuning.Speed, 0.01, 1000.0); Check("BodyRadius", discTuning.BodyRadius, 0.01, 100.0); Check("Bounces", discTuning.Bounces, 0.0, 128.0); Check("Lifetime", discTuning.Lifetime, 0.01, 120.0); Check("ActiveProjectiles", discTuning.ActiveProjectiles, 1.0, 3.0); } } else { Check("Speed", blastTuning.Speed, 0.01, 1000.0); Check("BlastRadius", blastTuning.BlastRadius, 0.01, 100.0); Check("ExplosionLifetime", blastTuning.ExplosionLifetime, 0.01, 120.0); Check("NoiseRadius", blastTuning.NoiseRadius, 0.0, 1000.0); Check("ExplosionForce", blastTuning.ExplosionForce, 0.0, 1000.0); } } else { Check("MinimumCharge", plasmaTuning.MinimumCharge, 0.1, 120.0); Check("FullCharge", plasmaTuning.FullCharge, plasmaTuning.MinimumCharge + 0.01, 120.0); Check("BotChargePower", plasmaTuning.BotChargePower, 0.0, 1.0); Check("Speed", plasmaTuning.Speed, 0.01, 1000.0); Check("FullSpeed", plasmaTuning.FullSpeed, 0.01, 1000.0); Check("BodyRadius", plasmaTuning.BodyRadius, 0.01, 100.0); Check("FullBodyRadius", plasmaTuning.FullBodyRadius, 0.01, 100.0); Check("TravelDistance", plasmaTuning.TravelDistance, 0.01, 1000.0); Check("FullTravelDistance", plasmaTuning.FullTravelDistance, 0.01, 1000.0); Check("FullDamage", plasmaTuning.FullDamage, plasmaTuning.Damage, 1000000.0); Check("ActiveProjectiles", plasmaTuning.ActiveProjectiles, 1.0, 4.0); if (plasmaTuning is PlasmaBlastTuning plasmaBlastTuning) { Check("BlastRadius", plasmaBlastTuning.BlastRadius, 0.01, 100.0); Check("FullBlastRadius", plasmaBlastTuning.FullBlastRadius, 0.01, 100.0); Check("ExplosionLifetime", plasmaBlastTuning.ExplosionLifetime, 0.01, 120.0); Check("NoiseRadius", plasmaBlastTuning.NoiseRadius, 0.0, 1000.0); Check("ExplosionForce", plasmaBlastTuning.ExplosionForce, 0.0, 1000.0); } if (plasmaTuning is PlasmaArcTuning plasmaArcTuning) { Check("ArcRadius", plasmaArcTuning.ArcRadius, 0.01, 100.0); Check("FullArcRadius", plasmaArcTuning.FullArcRadius, 0.01, 100.0); Check("PulseInterval", plasmaArcTuning.PulseInterval, 0.01, 60.0); Check("PulseDamageFraction", plasmaArcTuning.PulseDamageFraction, 0.0, 1.0); Check("PulseStagger", plasmaArcTuning.PulseStagger, 0.0, 100.0); Check("PulseTargets", plasmaArcTuning.PulseTargets, 1.0, 3.0); Check("FadeLifetime", plasmaArcTuning.FadeLifetime, 0.01, 120.0); } } } else { Check("Range", arcChainTuning.Range, 0.01, 1000.0); Check("Targets", arcChainTuning.Targets, 1.0, 4.0); Check("JumpRange", arcChainTuning.JumpRange, 0.01, 1000.0); Check("DamageRetention", arcChainTuning.DamageRetention, 0.0, 1.0); Check("ArcLifetime", arcChainTuning.ArcLifetime, 0.01, 120.0); Check("HopDelay", arcChainTuning.HopDelay, 0.0, 10.0); } void Check(string name, double number, double min, double max) { if (!double.IsFinite(number) || number + 1E-07 < min || number - 1E-07 > max) { Fail(path + "." + JsonOptions.PropertyNamingPolicy.ConvertName(name), FormattableString.Invariant($"expected {min}..{max}")); } } } private static void Fail(string field, string reason) { throw new InvalidDataException(field + ": " + reason); } } internal static class EnergyContent { internal const string FolderName = "ForgeEnergyLab"; private static readonly string[] Fields = new string[7] { "Id", "Name", "Description", "Slot", "Mode", "Model", "Tuning" }; private static readonly string[] ModelFields = new string[2] { "Key", "Bundle" }; internal static List<(WeaponDefinition Definition, string Folder)> Load(IEnumerable<string> folders, List<string> errors) { List<(WeaponDefinition, string)> list = new List<(WeaponDefinition, string)>(); foreach (string folder in folders) { string[] files; try { files = Directory.GetFiles(folder, "*.json"); } catch (Exception ex) { errors.Add(folder + ": could not list the definition files: " + ex); continue; } Array.Sort(files, (IComparer<string>?)StringComparer.Ordinal); string[] array = files; foreach (string text in array) { string json; try { json = File.ReadAllText(text); } catch (Exception ex2) { errors.Add(text + ": could not be read: " + ex2); continue; } foreach (WeaponDefinition item in Parse(text, json, errors)) { if (EnergyLab.Register(item, out string error)) { list.Add((item, folder)); } else { errors.Add(text + ": " + error); } } } } return list; } internal static List<WeaponDefinition> Parse(string file, string json, List<string> errors) { //IL_0199: Expected O, but got Unknown List<WeaponDefinition> list = new List<WeaponDefinition>(); JsonDocument jsonDocument; try { jsonDocument = JsonDocument.Parse(json); } catch (JsonException ex) { errors.Add($"{file}: invalid JSON at line {ex.LineNumber + 1}, byte {ex.BytePositionInLine + 1}."); return list; } using (jsonDocument) { if (jsonDocument.RootElement.ValueKind != JsonValueKind.Array) { errors.Add(file + ": expected an array of weapon definitions."); return list; } string directoryName = Path.GetDirectoryName(Path.GetFullPath(file)); int num = 0; foreach (JsonElement item in jsonDocument.RootElement.EnumerateArray()) { JsonElement value2; string value = ((item.ValueKind == JsonValueKind.Object && item.TryGetProperty("Id", out value2) && value2.ValueKind == JsonValueKind.String) ? value2.GetString() : ("[" + num + "]")); num++; try { list.Add(ParseOne(item, directoryName)); } catch (StrictJsonException ex2) { StrictJsonException ex3 = ex2; errors.Add($"{file}: {value}{((ex3.Field.Length == 0) ? "" : ("." + ex3.Field))}: {((Exception)(object)ex3).Message}"); } } return list; } } private static WeaponDefinition ParseOne(JsonElement item, string folder) { StrictJson.UniqueMembers(item, ""); StrictJson.OnlyKnown(item, "", Fields, (string[])null, (string)null); string? id = Text(item, "", "Id", required: true); string name = Text(item, "", "Name", required: true); string description = Text(item, "", "Description", required: false); WeaponSlot slot = StrictJson.Enum<WeaponSlot>(item, "", "Slot"); EnergyMode mode = StrictJson.Enum<EnergyMode>(item, "", "Mode"); EnergyTuning energyTuning = EnergyConfiguration.DefaultTuning(mode); JsonElement jsonElement = StrictJson.Object(item, "", "Model"); StrictJson.OnlyKnown(jsonElement, "Model.", ModelFields, (string[])null, (string)null); string key = Text(jsonElement, "Model.", "Key", required: true); string bundle = Text(jsonElement, "Model.", "Bundle", required: true); if (item.TryGetProperty("Tuning", out var value)) { energyTuning = EnergyConfiguration.MergeTuning(energyTuning, value, "Tuning"); } return new WeaponDefinition(id, name, slot, mode, energyTuning, new WeaponModel(key, Bundle(folder, bundle)), description); } private static string? Text(JsonElement owner, string prefix, string field, bool required) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!owner.TryGetProperty(field, out var value)) { if (!required) { return null; } throw new StrictJsonException(prefix + field, "is required."); } if (value.ValueKind != JsonValueKind.String) { throw new StrictJsonException(prefix + field, "must be a string."); } return value.GetString(); } private static string Bundle(string folder, string bundle) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(bundle) || Path.IsPathRooted(bundle) || ((ReadOnlySpan<string>)bundle.Split('/', '\\')).Contains("..")) { throw new StrictJsonException("Model.Bundle", "must be a file path inside the definition's folder (not absolute, no '..')."); } try { return Path.GetFullPath(Path.Combine(folder, bundle)); } catch (Exception ex) when (((ex is ArgumentException || ex is NotSupportedException || ex is PathTooLongException) ? 1 : 0) != 0) { throw new StrictJsonException("Model.Bundle", "is not a usable path: " + ex.Message); } } } internal sealed class EnergyGear { internal readonly WeaponDefinition Definition; internal readonly uint GearId; internal readonly uint CategoryId; internal readonly uint ArchetypeId; internal readonly WeaponNumbers Numbers; internal readonly string ModelPath; internal EnergyMode Mode => Definition.Mode; internal string Name => Definition.Name; internal bool Main => Definition.Slot == WeaponSlot.Main; internal int Clip => Definition.Tuning.Magazine; internal float AmmoPerRound => Definition.Tuning.AmmoPerRound; internal string NativeName => "ForgeWeaponEnergyLab_" + Definition.Id; internal string ModelDigest { get; set; } = ""; internal EnergyGear(WeaponDefinition definition) { Definition = definition; ModelPath = Path.GetFullPath(definition.Model.BundlePath); (uint, uint, uint) tuple = EnergyGears.Ids(definition.Id); GearId = tuple.Item1; CategoryId = tuple.Item2; ArchetypeId = tuple.Item3; Numbers = WeaponNumbers.Resolve(definition.Tuning); } } internal static class EnergyGears { private static readonly EnergyGear?[] Categories = new EnergyGear[65536]; private static bool _frozen; internal static EnergyGear[] All { get; private set; } = Array.Empty<EnergyGear>(); internal static (uint Gear, uint Category, uint Archetype) Ids(string id) { return (Gear: 0x20000000 | (Fnv.Hash32(id + "/gear") & 0x1FFFFFFF), Category: 0x8000 | (Fnv.Hash32(id + "/category") & 0x7FFF), Archetype: 0x20000000 | (Fnv.Hash32(id + "/archetype") & 0x1FFFFFFF)); } internal static void Freeze(List<string> errors) { if (_frozen) { return; } _frozen = true; Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); List<EnergyGear> list = new List<EnergyGear>(); EnergyGear[] array = EnergyLab.Freeze(errors); foreach (EnergyGear energyGear in array) { if (!dictionary.ContainsKey(energyGear.ModelPath)) { FileInfo fileInfo = new FileInfo(energyGear.ModelPath); if (!fileInfo.Exists || fileInfo.Length < 1024 || fileInfo.Length > 134217728) { errors.Add(energyGear.Definition.Id + ".model.bundle: missing bundle or size outside 1 KiB..128 MiB: " + energyGear.ModelPath + "; this weapon is skipped."); continue; } using FileStream inputStream = File.OpenRead(energyGear.ModelPath); using SHA256 sHA = SHA256.Create(); dictionary.Add(energyGear.ModelPath, Convert.ToHexString(sHA.ComputeHash(inputStream))); } energyGear.ModelDigest = dictionary[energyGear.ModelPath]; list.Add(energyGear); } foreach (EnergyGear item in list) { Categories[item.CategoryId] = item; } All = list.ToArray(); } internal static EnergyGear? ForCategory(uint categoryId) { if (categoryId > 65535) { return null; } return Categories[categoryId]; } internal static EnergyMode ModeForCategory(uint categoryId) { return ForCategory(categoryId)?.Mode ?? EnergyMode.Off; } internal static bool IsSelection(uint categoryId, int mode) { if (categoryId != 0 || mode != 0) { return Match(categoryId, mode) != null; } return true; } internal static EnergyGear? Match(uint categoryId, int mode) { EnergyGear energyGear = ForCategory(categoryId); if (energyGear == null || energyGear.Mode != (EnergyMode)mode) { return null; } return energyGear; } } internal static class EnergyGearJson { internal const int SightComponent = 21; internal const uint ZoomSightPart = 25u; internal static string Build(string original, EnergyGear gear) { if (gear.CategoryId > 65535) { throw new InvalidOperationException("Gear category ID exceeds the native 16-bit packet limit."); } JsonObject jsonObject = JsonNode.Parse(original)?.AsObject() ?? throw new InvalidOperationException("Invalid native gear JSON."); jsonObject["Name"] = gear.Name; JsonObject jsonObject2 = jsonObject["Packet"]?.AsObject() ?? throw new InvalidOperationException("Native gear packet missing."); JsonObject obj = jsonObject2["Comps"]?.AsObject() ?? throw new InvalidOperationException("Native gear components missing."); bool flag = false; bool flag2 = false; bool flag3 = false; bool flag4 = false; foreach (KeyValuePair<string, JsonNode> item in obj) { item.Deconstruct(out var _, out var value); if (value is JsonObject jsonObject3) { switch (jsonObject3["c"]?.GetValue<int>()) { case 2: jsonObject3["v"] = gear.CategoryId; flag = true; break; case 5: jsonObject3["v"] = gear.CategoryId; flag3 = true; break; case 7: jsonObject3["v"] = gear.CategoryId; flag4 = true; break; case 21: jsonObject3["v"] = 25u; flag2 = true; break; } } } if (!flag) { throw new InvalidOperationException("Native gear has no category component."); } if (!flag2) { throw new InvalidOperationException("Native gear has no sight component."); } if (!flag3 || !flag4) { throw new InvalidOperationException("Native gear has no audio or casing component."); } (jsonObject2["publicName"]?.AsObject() ?? throw new InvalidOperationException("Native gear public name missing."))["data"] = gear.Name; return jsonObject.ToJsonString(); } } internal static class SpecialWeaponMath { public const float MaxPullDelta = 0.1f; internal static float HoleDamageAt(float distance, in WeaponNumbers tuning) { if (!float.IsFinite(distance) || distance < 0f || distance > tuning.Radius) { return 0f; } return tuning.Pulse.Damage * (1f - (1f - tuning.EdgeDamageFraction) * Math.Clamp((distance - tuning.CoreRadius) / (tuning.Radius - tuning.CoreRadius), 0f, 1f)); } public static Vector3 Reflect(Vector3 incoming, Vector3 normal) { if (!float.IsFinite(incoming.LengthSquared()) || !float.IsFinite(normal.LengthSquared()) || incoming.LengthSquared() < 1E-06f || normal.LengthSquared() < 1E-06f) { throw new ArgumentException("Ricochet needs a travel direction and surface normal."); } return Vector3.Normalize(Vector3.Reflect(Vector3.Normalize(incoming), Vector3.Normalize(normal))); } public static bool InFlame(Vector3 start, Vector3 direction, Vector3 point, float range, in WeaponNumbers tuning) { if (!float.IsFinite(range) || range <= 0f || !float.IsFinite(direction.LengthSquared()) || direction.LengthSquared() < 1E-06f) { return false; } direction = Vector3.Normalize(direction); Vector3 vector = point - start; float num = Vector3.Dot(vector, direction); if (num < 0f || num > range) { return false; } float num2 = tuning.StartRadius + num * tuning.RadiusPerMetre; return (vector - direction * num).LengthSquared() <= num2 * num2; } public static Vector3 PullStep(Vector3 position, Vector3 center, float delta, in WeaponNumbers tuning) { Vector3 vector = center - position; float num = vector.Length(); if (!float.IsFinite(num) || !float.IsFinite(delta) || delta <= 0f || num <= tuning.CoreRadius || num > tuning.Radius) { return Vector3.Zero; } return vector / num * MathF.Min(num - tuning.CoreRadius, tuning.PullSpeed * MathF.Min(delta, 0.1f)); } } internal readonly record struct PlasmaPayload(float Speed, float BodyRadius, float TravelDistance, float Damage, float BlastRadius, float ArcRadius, float ArcDamage) { internal float Lifetime => TravelDistance / Speed; internal static PlasmaPayload FromCharge(float charge, in WeaponNumbers tuning) { if (!float.IsFinite(charge)) { throw new ArgumentOutOfRangeException("charge"); } charge = Math.Clamp(charge, 0f, 1f); float num = Lerp(tuning.Shot.Damage, tuning.FullDamage, charge); return new PlasmaPayload(Lerp(tuning.Speed, tuning.FullSpeed, charge), Lerp(tuning.BodyRadius, tuning.FullBodyRadius, charge), Lerp(tuning.TravelDistance, tuning.FullTravelDistance, charge), num, Lerp(tuning.BlastRadius, tuning.FullBlastRadius, charge), Lerp(tuning.ArcRadius, tuning.FullArcRadius, charge), num * tuning.PulseDamageFraction); static float Lerp(float low, float high, float power) { return low + (high - low) * power; } } [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { builder.Append("Speed = "); builder.Append(Speed.ToString()); builder.Append(", BodyRadius = "); builder.Append(BodyRadius.ToString()); builder.Append(", TravelDistance = "); builder.Append(TravelDistance.ToString()); builder.Append(", Damage = "); builder.Append(Damage.ToString()); builder.Append(", BlastRadius = "); builder.Append(BlastRadius.ToString()); builder.Append(", ArcRadius = "); builder.Append(ArcRadius.ToString()); builder.Append(", ArcDamage = "); builder.Append(ArcDamage.ToString()); return true; } } internal sealed class PlasmaContacts { public const int Capacity = 128; private readonly long[] _identities = new long[128]; private int _count; public bool Remember(long identity) { if (identity == 0L) { throw new ArgumentOutOfRangeException("identity"); } if (_identities.AsSpan(0, _count).Contains(identity)) { return false; } if (_count == 128) { throw new BudgetExhausted("Plasma contact identity budget exhausted; no truncated result used."); } _identities[_count++] = identity; return true; } public void Reset() { _count = 0; } } internal sealed class PlasmaCharge { private double? _started; public bool Charging => _started.HasValue; public bool Ready(double now, in WeaponNumbers tuning) { double? started = _started; if (started.HasValue) { double valueOrDefault = started.GetValueOrDefault(); if (double.IsFinite(now)) { return now - valueOrDefault + 1E-07 >= tuning.MinimumCharge; } } return false; } public float Power(double now, in WeaponNumbers tuning) { double? started = _started; if (started.HasValue) { double valueOrDefault = started.GetValueOrDefault(); return (float)Math.Clamp((now - valueOrDefault - tuning.MinimumCharge) / (tuning.FullCharge - tuning.MinimumCharge), 0.0, 1.0); } return 0f; } public bool Update(double now, bool held, bool pressed, bool allowed, in WeaponNumbers tuning, out float power) { power = 0f; if (!allowed || !double.IsFinite(now)) { Cancel(); return false; } if (held) { if (pressed) { double valueOrDefault = _started.GetValueOrDefault(); if (!_started.HasValue) { valueOrDefault = now; _started = valueOrDefault; } } return false; } if (!_started.HasValue) { return false; } power = Power(now, in tuning); bool result = Ready(now, in tuning); Cancel(); return result; } public void Cancel() { _started = null; } } internal readonly record struct ArcCandidate(long Identity, float DistanceSquared, bool Eligible); internal static class ArcSelection { public static int Nearest(ReadOnlySpan<ArcCandidate> candidates, ReadOnlySpan<long> visited, float range) { int num = -1; for (int i = 0; i < candidates.Length; i++) { ArcCandidate arcCandidate = candidates[i]; if (arcCandidate.Eligible && arcCandidate.Identity != 0L && !visited.Contains(arcCandidate.Identity) && float.IsFinite(arcCandidate.DistanceSquared) && !(arcCandidate.DistanceSquared < 0f) && float.IsFinite(range) && !(range < 0f) && !(arcCandidate.DistanceSquared > range * range) && (num < 0 || arcCandidate.DistanceSquared < candidates[num].DistanceSquared || (arcCandidate.DistanceSquared == candidates[num].DistanceSquared && arcCandidate.Identity < candidates[num].Identity))) { num = i; } } return num; } } internal static class EnergyProtocol { internal const int InboxLimit = 1024; internal const float MaximumMuzzleDistance = 3.5f; } internal static class EnergyLimits { internal const int ArcTargets = 4; internal const int PulseTargets = 3; internal const float Reach = 1000f; } internal readonly record struct ShotTuning(float Range, double Interval, float Damage, float Stagger, float Precision); internal readonly record struct WeaponNumbers { internal ShotTuning Shot { get; init; } internal ShotTuning Pulse { get; init; } internal int Targets { get; init; } internal float JumpRange { get; init; } internal float DamageRetention { get; init; } internal float ArcLifetime { get; init; } internal float HopDelay { get; init; } internal double MinimumCharge { get; init; } internal double FullCharge { get; init; } internal float BotChargePower { get; init; } internal float Speed { get; init; } internal float FullSpeed { get; init; } internal float BodyRadius { get; init; } internal float FullBodyRadius { get; init; } internal float TravelDistance { get; init; } internal float FullTravelDistance { get; init; } internal float FullDamage { get; init; } internal int ActiveProjectiles { get; init; } internal float BlastRadius { get; init; } internal float FullBlastRadius { get; init; } internal float ExplosionLifetime { get; init; } internal float NoiseRadius { get; init; } internal float ExplosionForce { get; init; } internal float ArcRadius { get; init; } internal float FullArcRadius { get; init; } internal float PulseDamageFraction { get; init; } internal int PulseTargets { get; init; } internal float FadeLifetime { get; init; } internal int Bounces { get; init; } internal float Lifetime { get; init; } internal float StartRadius { get; init; } internal float RadiusPerMetre { get; init; } internal float FlightLifetime { get; init; } internal float Gravity { get; init; } internal float Radius { get; init; } internal float CoreRadius { get; init; } internal float CollapseDuration { get; init; } internal float GrowthDuration { get; init; } internal float EdgeDamageFraction { get; init; } internal float PullSpeed { get; init; } internal float QueryInterval { get; init; } internal static WeaponNumbers Resolve(EnergyTuning t) { float num = ((t is ArcChainTuning arcChainTuning) ? arcChainTuning.Range : ((!(t is FlameTuning flameTuning)) ? 1000f : flameTuning.Range)); float range = num; WeaponNumbers weaponNumbers = new WeaponNumbers { Shot = new ShotTuning(range, t.Interval, t.Damage, t.Stagger, t.Precision) }; if (t is PlasmaTuning plasmaTuning) { weaponNumbers = weaponNumbers with { MinimumCharge = plasmaTuning.MinimumCharge, FullCharge = plasmaTuning.FullCharge, BotChargePower = plasmaTuning.BotChargePower, Speed = plasmaTuning.Speed, FullSpeed = plasmaTuning.FullSpeed, BodyRadius = plasmaTuning.BodyRadius, FullBodyRadius = plasmaTuning.FullBodyRadius, TravelDistance = plasmaTuning.TravelDistance, FullTravelDistance = plasmaTuning.FullTravelDistance, FullDamage = plasmaTuning.FullDamage, ActiveProjectiles = plasmaTuning.ActiveProjectiles }; } if (!(t is ArcChainTuning arcChainTuning2)) { if (!(t is PlasmaBlastTuning plasmaBlastTuning)) { if (!(t is PlasmaArcTuning plasmaArcTuning)) { if (!(t is BlastTuning blastTuning)) { if (!(t is DiscTuning discTuning)) { if (!(t is FlameTuning flameTuning2)) { if (t is BlackHoleTuning blackHoleTuning) { return weaponNumbers with { Speed = blackHoleTuning.Speed, BodyRadius = blackHoleTuning.BodyRadius, FlightLifetime = blackHoleTuning.FlightLifetime, Gravity = blackHoleTuning.Gravity, Radius = blackHoleTuning.Radius, CoreRadius = blackHoleTuning.CoreRadius, Lifetime = blackHoleTuning.Lifetime, CollapseDuration = blackHoleTuning.CollapseDuration, GrowthDuration = blackHoleTuning.GrowthDuration, Pulse = new ShotTuning(blackHoleTuning.Radius, blackHoleTuning.PulseInterval, blackHoleTuning.Damage, blackHoleTuning.Stagger, blackHoleTuning.Precision), EdgeDamageFraction = blackHoleTuning.EdgeDamageFraction, PullSpeed = blackHoleTuning.PullSpeed, QueryInterval = blackHoleTuning.QueryInterval }; } return weaponNumbers; } return weaponNumbers with { StartRadius = flameTuning2.StartRadius, RadiusPerMetre = flameTuning2.RadiusPerMetre }; } return weaponNumbers with { Speed = discTuning.Speed, BodyRadius = discTuning.BodyRadius, Bounces = discTuning.Bounces, Lifetime = discTuning.Lifetime, ActiveProjectiles = discTuning.ActiveProjectiles }; } return weaponNumbers with { Speed = blastTuning.Speed, BlastRadius = blastTuning.BlastRadius, FullBlastRadius = blastTuning.BlastRadius, ExplosionLifetime = blastTuning.ExplosionLifetime, NoiseRadius = blastTuning.NoiseRadius, ExplosionForce = blastTuning.ExplosionForce }; } return weaponNumbers with { ArcRadius = plasmaArcTuning.ArcRadius, FullArcRadius = plasmaArcTuning.FullArcRadius, Pulse = new ShotTuning(0f, plasmaArcTuning.PulseInterval, 0f, plasmaArcTuning.PulseStagger, plasmaArcTuning.Precision), PulseDamageFraction = plasmaArcTuning.PulseDamageFraction, PulseTargets = plasmaArcTuning.PulseTargets, FadeLifetime = plasmaArcTuning.FadeLifetime }; } return weaponNumbers with { BlastRadius = plasmaBlastTuning.BlastRadius, FullBlastRadius = plasmaBlastTuning.FullBlastRadius, ExplosionLifetime = plasmaBlastTuning.ExplosionLifetime, NoiseRadius = plasmaBlastTuning.NoiseRadius, ExplosionForce = plasmaBlastTuning.ExplosionForce }; } return weaponNumbers with { Targets = arcChainTuning2.Targets, JumpRange = arcChainTuning2.JumpRange, DamageRetention = arcChainTuning2.DamageRetention, ArcLifetime = arcChainTuning2.ArcLifetime, HopDelay = arcChainTuning2.HopDelay }; } [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { return false; } } } namespace ForgeWeaponEnergyLab.Presentation { internal readonly record struct AnimationFrame(Vector3 Position, Quaternion Rotation); internal static class AnimationPose { internal static AnimationFrame Follow(in AnimationFrame restParent, in AnimationFrame currentParent, in AnimationFrame restChild) { Validate(in restParent); Validate(in currentParent); Validate(in restChild); Quaternion quaternion = Quaternion.Inverse(restParent.Rotation); Vector3 value = Vector3.Transform(restChild.Position - restParent.Position, quaternion); Quaternion rotation = Quaternion.Normalize(currentParent.Rotation * quaternion * restChild.Rotation); return new AnimationFrame(currentParent.Position + Vector3.Transform(value, currentParent.Rotation), rotation); } internal static AnimationFrame FollowHand(in AnimationFrame restGrip, in AnimationFrame currentGrip, in AnimationFrame restHand, in AnimationFrame currentHand, in AnimationFrame authoredHand) { Validate(in currentHand); AnimationFrame animationFrame = Follow(in restGrip, in currentGrip, in restHand); AnimationFrame animationFrame2 = Follow(in restGrip, in currentGrip, in authoredHand); Quaternion rotation = Quaternion.Normalize(currentHand.Rotation * Quaternion.Inverse(animationFrame.Rotation) * animationFrame2.Rotation); return new AnimationFrame(animationFrame2.Position + currentHand.Position - animationFrame.Position, rotation); } internal static AnimationFrame LimitHandReach(in AnimationFrame modelRest, in AnimationFrame donorHand, in AnimationFrame authoredHand, float holdForward) { Validate(in modelRest); Validate(in donorHand); Validate(in authoredHand); Vector3 vector = Vector3.Transform(Vector3.UnitZ, modelRest.Rotation); float num = Vector3.Dot(authoredHand.Position - donorHand.Position, vector); float num2 = num + holdForward; if (!float.IsFinite(num2)) { throw new ArgumentException("Support-hand forward reach must be finite."); } return donorHand with { Position = donorHand.Position + vector * (num - MathF.Max(0f, num2)) }; } private static void Validate(in AnimationFrame frame) { if (!PresentationContract.Finite(frame.Position) || !float.IsFinite(frame.Rotation.LengthSquared()) || MathF.Abs(frame.Rotation.LengthSquared() - 1f) > 0.001f) { throw new ArgumentException("Animation socket pose must be finite with a unit rotation."); } } } internal static class EnergyAudio { internal static readonly string[] Cues = new string[36] { "beam-start", "beam-loop", "beam-stop", "beam-hit", "arc-fire", "arc-loop", "arc-hit", "plasma-charge-rise", "plasma-charge-loop", "plasma-charge-full", "plasma-charge-cancel", "orb-fire", "orb-fly-loop", "shock-fly-loop", "orb-explode", "shock-fire", "shock-fade", "shock-hit", "shock-pulse", "blast-fire", "blast-cycle", "blast-explode", "disc-fire", "disc-fly-loop", "disc-bounce", "disc-drop", "flame-start", "flame-loop", "flame-stop", "flame-hit", "flame-crackle", "gravity-fire", "gravity-fly-loop", "gravity-open", "gravity-close", "gravity-field-loop" }; private static AudioLibrary? _library; internal static AudioLibrary Library => _library ?? throw new InvalidOperationException("Energy sounds were not loaded."); internal static bool Loaded => Library.IsLoaded; internal static void Load() { _library = AudioKit.Load("NAinfini.ForgeWeaponEnergyLab", (string)null); } internal static void PlayAt(string cue, Vector3 position, float gain) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) Library.Play(cue, position, PlayOptions.OfGain(gain)); } } internal sealed class EnergyEffects : IDisposable { private const int Capacity = 4; private const float ChargeHoldGain = 0.6f; private const float BeamLoopEntry = 0.23f; private const float BeamLoopFadeIn = 0.2f; private const float BeamLoopFadeOut = 0.18f; private const string BeamLoop = "beam-loop"; private const string BeamStart = "beam-start"; private const string BeamStop = "beam-stop"; private const string BeamHit = "beam-hit"; private const string Arc = "arc-fire"; private const string ArcLoop = "arc-loop"; private const string ArcHit = "arc-hit"; private const string ChargeRise = "plasma-charge-rise"; private const string ChargeHold = "plasma-charge-loop"; private const string ChargeFull = "plasma-charge-full"; private const string PlasmaLaunch = "orb-fire"; private const string ParcLaunch = "shock-fire"; private const string ChargeCancel = "plasma-charge-cancel"; private readonly GameObject _root; private readonly Vector3[] _ends = (Vector3[])(object)new Vector3[4]; private readonly bool[] _hits = new bool[4]; private readonly AudioVoice _loop; private readonly AudioVoice _beamEvent; private readonly AudioVoice _beamStop; private readonly AudioVoice _beamHit; private readonly AudioVoice _arcEvent; private readonly AudioVoice _arcLoop; private readonly AudioVoice _chargeRise; private readonly AudioVoice _chargeHold; private readonly AudioVoice _chargeFull; private readonly AudioVoice[] _arcHits = (AudioVoice[])(object)new AudioVoice[4]; private readonly AudioVoice[] _launchVoices = (AudioVoice[])(object)new AudioVoice[4]; private int _launchCursor; private float _arcStarted; private float _arcLifetime; private float _arcHopDelay; private float _beamStarted; private float _beamLoopGain; private float _beamReleaseGain; private float _beamReleased = -1f; private int _segments; private bool _beaming; private bool _beamWasHit; private bool _charging; private bool _chargedFull; private float _nextBeamHit; private int _arcHitMask; private bool _disposed; private bool _arcHeld; public EnergyEffects() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown _root = new GameObject("Energy weapon effects"); Object.DontDestroyOnLoad((Object)(object)_root); Transform transform = _root.transform; _loop = EnergyAudio.Library.Voice(transform); _beamEvent = EnergyAudio.Library.Voice(transform); _beamStop = EnergyAudio.Library.Voice(transform); _beamHit = EnergyAudio.Library.Voice(Child("beam-hit-voice")); _arcEvent = EnergyAudio.Library.Voice(transform); _arcLoop = EnergyAudio.Library.Voice(transform); for (int i = 0; i < _launchVoices.Length; i++) { _launchVoices[i] = EnergyAudio.Library.Voice(transform); } _chargeRise = EnergyAudio.Library.Voice(transform); _chargeHold = EnergyAudio.Library.Voice(transform); _chargeFull = EnergyAudio.Library.Voice(transform); for (int j = 0; j < 4; j++) { _arcHits[j] = EnergyAudio.Library.Voice(Child("arc-hit-voice-" + j)); } } private Transform Child(string name) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Transform transform = new GameObject(name).transform; transform.SetParent(_root.transform, false); return transform; } public void Beam(Vector3 start, Vector3 end, bool hit, float now) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) _segments = 0; _root.transform.position = start; if (!_beaming) { _beamEvent.Play("beam-start", 0.7f); _beamStarted = now; _beaming = true; _beamReleased = -1f; if (_loop.Playing) { _loop.Stop(0f); } } bool num = now - _beamStarted >= 0.23f; if (num && hit && (!_beamWasHit || now >= _nextBeamHit) && !_beamHit.Playing) { _beamHit.Anchor.position = end; _beamHit.Play("beam-hit", 0.33f); _nextBeamHit = now + 0.16f; } _beamWasHit = hit; if (num) { _beamLoopGain = (hit ? 0.63f : 0.55f) * Mathf.Clamp01((now - _beamStarted - 0.23f) / 0.2f); _loop.Hold("beam-loop", _beamLoopGain); } } public void Charge(float power) { if (!_charging) { _charging = true; _chargedFull = false; _chargeFull.Stop(0f); _chargeRise.Play("plasma-charge-rise", 0.95f); } if (power >= 1f && !_chargedFull) { _chargedFull = true; _chargeRise.Stop(0f); _chargeFull.Play("plasma-charge-full", 0.9f); _chargeHold.Hold("plasma-charge-loop", 0.6f); } } public void Charge(float power, Vector3 position) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) _root.transform.position = position; Charge(power); } public void Discharge(bool arcOrb, Vector3 position) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) StopCharge(); _root.transform.position = position; _launchVoices[_launchCursor].Play(arcOrb ? "shock-fire" : "orb-fire", 0.8f); _launchCursor = (_launchCursor + 1) % _launchVoices.Length; } public void StopCharge() { if (_charging) { _charging = (_chargedFull = false); _chargeRise.Stop(0f); _chargeHold.Stop(0f); _chargeFull.Stop(0f); } } public void CancelCharge() { if (_charging) { StopCharge(); _chargeFull.Play("plasma-charge-cancel", 0.45f); } } public void Chain(ReadOnlySpan<Vector3> starts, ReadOnlySpan<Vector3> ends, ReadOnlySpan<bool> hits, float now, float lifetime, float hopDelay) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (starts.Length < 1 || starts.Length > 4 || ends.Length != starts.Length || hits.Length != starts.Length) { throw new ArgumentException("Invalid chain presentation segments."); } ends.CopyTo(_ends); hits.CopyTo(_hits); _segments = starts.Length; _arcStarted = now; _arcLifetime = lifetime; _arcHopDelay = hopDelay; _arcHitMask = 0; _root.transform.position = starts[0]; _arcEvent.Play("arc-fire", 0.82f); Tick(now); } public void ArcHold(bool held) { if (held != _arcHeld) { _arcHeld = held; if (held) { _arcLoop.Hold("arc-loop", 0.5f); } else { _arcLoop.Stop(0f); } } } public void Tick(float now) { //IL_00ff: Unknown result type (might be due to invalid IL or missing references) if (_beamReleased >= 0f) { if (float.IsPositiveInfinity(_beamReleased)) { _beamReleased = now; } float num = (now - _beamReleased) / 0.18f; if (num >= 1f || !_loop.Playing) { _loop.Stop(0f); _beamReleased = -1f; } else { _loop.SetGain(_beamReleaseGain * (1f - num)); } } if (_segments == 0) { return; } float num2 = now - _arcStarted; if (num2 >= _arcLifetime + (float)(_segments - 1) * _arcHopDelay) { _segments = 0; return; } for (int i = 0; i < _segments; i++) { if (!(num2 < (float)i * _arcHopDelay) && _hits[i] && (_arcHitMask & (1 << i)) == 0) { _arcHitMask |= 1 << i; _arcHits[i].Anchor.position = _ends[i]; _arcHits[i].Play("arc-hit", (i == 0) ? 0.34f : 0.22f); } } } public void Hide() { if (_segments == 0 && !_arcHeld && !_charging && !_beaming) { return; } _segments = 0; if (_arcHeld) { _arcLoop.Stop(0f); } _arcHeld = false; if (_charging) { StopCharge(); } if (_beaming && !_disposed) { _beamStop.Play("beam-stop", 0.55f); if (_loop.Playing) { _beamReleased = float.PositiveInfinity; _beamReleaseGain = _beamLoopGain; } } _beaming = false; _beamWasHit = false; } public void Dispose() { if (!_disposed) { _disposed = true; _loop.Stop(0f); _beamEvent.Stop(0f); _beamStop.Stop(0f); _beamHit.Stop(0f); _arcEvent.Stop(0f); _arcLoop.Stop(0f); _chargeRise.Stop(0f); _chargeHold.Stop(0f); _chargeFull.Stop(0f); AudioVoice[] launchVoices = _launchVoices; for (int i = 0; i < launchVoices.Length; i++) { launchVoices[i].Stop(0f); } launchVoices = _arcHits; for (int i = 0; i < launchVoices.Length; i++) { launchVoices[i].Stop(0f); } if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } } } } internal static class EnergyIcons { private static readonly GearIconModel Model = new GearIconModel(IconModel); internal static void Supply() { EnergyGear[] all = EnergyGears.All; for (int i = 0; i < all.Length; i++) { GearIcons.Supply(all[i].CategoryId, Model); } } private static GameObject? IconModel(GearIDRange range, out string? failure) { failure = null; EnergyGear energyGear = EnergyGears.ForCategory(range.GetCompID((eGearComponent)2)); if (energyGear == null) { failure = "the gear is not an energy gun"; return null; } if (PresentationPreload.Failed) { failure = "the energy presentation of " + energyGear.Definition.Id + " failed to preload"; return null; } bool failed; GameObject? result = WeaponModels.IconModel(energyGear, out failed); if (failed) { failure = "the model of " + energyGear.Definition.Id + " failed to load"; } return result; } } internal static class EnergyVfx { private const string Pack = "Forge"; private const string DiscPack = "EnergyLabDisc"; internal static ModelHandle Beam; internal static ModelHandle Arc; internal static ModelHandle Explosion; internal static ModelHandle Flame; internal static ModelHandle FlameImpact; internal static ModelHandle Discharge; internal static ModelHandle PlasmaExplosion; internal static ModelHandle Orb; internal static ModelHandle Sparks; internal static ModelHandle BlackHole; internal static ModelHandle Disc; internal static IEnumerator Preload() { Register("EnergyLabDisc", "forge-energy-disc.bundle"); Beam = Resolve("Forge", "forge_energy_beam"); Arc = Resolve("Forge", "forge_lightning_arc"); Explosion = Resolve("Forge", "forge_explosion"); Flame = Resolve("Forge", "forge_flame"); FlameImpact = Resolve("Forge", "forge_flame_impact"); Discharge = Resolve("Forge", "forge_plasma_discharge"); PlasmaExplosion = Resolve("Forge", "forge_plasma_explosion"); Orb = Resolve("Forge", "forge_plasma_orb"); Sparks = Resolve("Forge", "forge_sparks"); BlackHole = Resolve("Forge", "forge_black_hole"); Disc = Resolve("EnergyLabDisc", "forge_ricochet_disc"); UseNativeDiscShaders(Disc.Prefab); yield break; } private static void Register(string pack, string file) { string text = "ForgeWeaponEnergyLab.Vfx." + file; using Stream stream = typeof(EnergyVfx).Assembly.GetManifestResourceStream(text) ?? throw new InvalidDataException("Missing " + text); using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); if (Bundles.RegisterBytes("NAinfini.ForgeWeaponEnergyLab", pack, file, memoryStream.ToArray()) == null) { throw new InvalidDataException("ForgeVisual refused the effect bundle " + file + " (see its error in the log)."); } } private static ModelHandle Resolve(string pack, string member) { return Models.Get(pack + ":" + member) ?? throw new InvalidDataException("Missing VFX " + member); } private static void UseNativeDiscShaders(GameObject prefab) { MeshRenderer componentInChildren = prefab.GetComponentInChildren<MeshRenderer>(); MeshFilter componentInChildren2 = prefab.GetComponentInChildren<MeshFilter>(); Mesh val = ((componentInChildren2 != null) ? componentInChildren2.sharedMesh : null); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)val == (Object)null || val.subMeshCount != 4 || ((Il2CppArrayBase<Material>)(object)((Renderer)componentInChildren).sharedMaterials).Length != 4) { throw new InvalidDataException("Disc prefab requires four surfaces."); } for (int i = 0; i < 4; i++) { Material val2 = ((Il2CppArrayBase<Material>)(object)((Renderer)componentInChildren).sharedMaterials)[i] ?? throw new InvalidDataException("Missing disc material."); val2.shader = Shader.Find((i == 3) ? "Sprites/Default" : "Standard") ?? throw new InvalidOperationException("Native disc shader is unavailable."); if (i < 3) { val2.EnableKeyword("_EMISSION"); } } } } internal sealed class EnergyVisuals : IDisposable { private const string IgniteState = "ignite"; private static readonly PointLightSpec BeamMuzzleLight = new PointLightSpec(new Color(0.4f, 0.7f, 1f), 3f, 1.5f, 0f, 0.2f); private static readonly PointLightSpec BeamImpactLight = new PointLightSpec(new Color(0.45f, 0.75f, 1f), 4.5f, 2.5f, 0f, 0.3f); private static readonly PointLightSpec ArcLight = new PointLightSpec(new Color(0.5f, 0.75f, 1f), 5f, 3f, 0.2f, 0.4f); private static readonly PointLightSpec BlastMuzzleLight = new PointLightSpec(new Color(1f, 0.6f, 0.25f), 4f, 3f, 0.12f, 0.1f); private const int Capacity = 4; private readonly FxInstancePool _pool = new FxInstancePool("Energy weapon visuals"); private readonly Vector3[] _starts = (Vector3[])(object)new Vector3[4]; private readonly Vector3[] _ends = (Vector3[])(object)new Vector3[4]; private readonly bool[] _hits = new bool[4]; private FxInstance? _beam; private int _beamMuzzleLight; private int _beamImpactLight; private float _arcStarted; private float _arcHopDelay; private int _segments; private int _started; private bool _disposed; private bool _prepared; internal IEnumerator Prepare() { if (_prepared) { yield break; } foreach (object item in _pool.Prepare(EnergyVfx.Beam, 2, (Color?)null)) { yield return item; } foreach (object item2 in _pool.Prepare(EnergyVfx.Arc, 8, (Color?)null)) { yield return item2; } _prepared = true; } public void Beam(Vector3 start, Vector3 end, bool hit, float now) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (!_disposed) { _segments = 0; bool num = _beam == null; if (_beam == null) { _beam = _pool.Take(EnergyVfx.Beam, (Color?)null).Hold(); } _beam.Stretch(start, end); _beam.Emit("impact", hit); _beam.Keep(); if (num) { _beam.PlayState("ignite"); } PointLights.Hold(ref _beamMuzzleLight, start, ref BeamMuzzleLight, now, 1f); if (hit) { PointLights.Hold(ref _beamImpactLight, end, ref BeamImpactLight, now, 1f); } else { PointLights.Release(ref _beamImpactLight); } } } public void Chain(ReadOnlySpan<Vector3> starts, ReadOnlySpan<Vector3> ends, ReadOnlySpan<bool> hits, float now, float hopDelay) { if (starts.Length < 1 || starts.Length > 4 || ends.Length != starts.Length || hits.Length != starts.Length) { throw new ArgumentException("Invalid chain presentation segments."); } if (!_disposed) { Hide(); starts.CopyTo(_starts); ends.CopyTo(_ends); hits.CopyTo(_hits); _segments = starts.Length; _started = 0; _arcStarted = now; _arcHopDelay = hopDelay; Tick(now); } } public void Blast(Vector3 start, float now) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!_disposed) { Hide(); PointLights.Flash(start, ref BlastMuzzleLight, now, 1f, 0f); } } public void Tick(float now) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (_disposed) { return; } while (_started < _segments && now - _arcStarted >= (float)_started * _arcHopDelay) { FxInstance obj = _pool.Take(EnergyVfx.Arc, (Color?)null); obj.Stretch(_starts[_started], _ends[_started]); obj.Emit("end", _hits[_started]); obj.Start(); if (_hits[_started]) { PointLights.Flash(_ends[_started], ref ArcLight, now, 1f, 0f); } _started++; } } public void Hide() { if (_segments != 0 || _beam != null || _beamMuzzleLight != 0 || _beamImpactLight != 0) { _segments = (_started = 0); FxInstance? beam = _beam; if (beam != null) { beam.Release(); } _beam = null; PointLights.Release(ref _beamMuzzleLight); PointLights.Release(ref _beamImpactLight); } } public void Dispose() { if (!_disposed) { _disposed = true; _pool.Dispose(); } } } internal static class ModelSockets { internal static Transform Require(Transform model, string name) { return Find(model, name) ?? throw new InvalidDataException("Energy model socket missing: " + name); } internal static Transform? Find(Transform model, string name) { if ((Object)(object)model == (Object)null) { throw new ArgumentNullException("model"); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException("Socket name is required.", "name"); } Transform val = null; for (int i = 0; i < model.childCount; i++) { Transform child = model.GetChild(i); if (!(((Object)child).name != name) && !((Object)(object)((Component)child).GetComponent<MeshFilter>() != (Object)null) && !((Object)(object)((Component)child).GetComponent<Renderer>() != (Object)null)) { if ((Object)(object)val != (Object)null) { throw new InvalidDataException("Energy model socket is ambiguous: " + name); } val = child; } } return val; } } internal sealed class PlasmaEffects : IDisposable { private const string ChargingState = "charging"; private const string ChargedState = "charged"; private const string LaunchState = "launch"; private const string StormState = "storm"; private const string FizzleState = "fizzle"; private const float ExplosionStandOff = 0.3f; private static readonly PointLightSpec OrbLight = new PointLightSpec(new Color(0.25f, 0.55f, 1f), 4f, 1.4f, 0f, 0.25f); private static readonly PointLightSpec ElectricOrbLight = new PointLightSpec(new Color(0.5f, 0.35f, 1f), 4f, 1.4f, 0f, 0.45f); private static readonly PointLightSpec ChargedLight = new PointLightSpec(new Color(0.3f, 0.6f, 1f), 3f, 2.5f, 0.2f, 0.1f); private static readonly PointLightSpec LaunchLight = new PointLightSpec(new Color(0.3f, 0.6f, 1f), 5f, 3f, 0.15f, 0.1f); private static readonly PointLightSpec DischargeLight = new PointLightSpec(new Color(0.55f, 0.4f, 1f), 3f, 2f, 0.12f, 0.5f); private static readonly PointLightSpec ExplosionLight = new PointLightSpec(new Color(0.35f, 0.6f, 1f), 7f, 3f, 0.55f, 0.2f); private const float OrbSize = 0.3f; private const float ExplosionSize = 3f; private const float SightedReach = 1.1f; private const float SightMargin = 0.01f; private static readonly string[] BeyondCore = new string[2] { "shell", "motes" }; private static readonly Color ElectricTint = new Color(1f, 0.7f, 1f); private readonly FxInstancePool _pool = new FxInstancePool("Plasma ionization effects"); private readonly FxInstance?[] _orbs; private readonly bool[] _exploded; private readonly Vector3[] _positions; private readonly bool[] _electric; private readonly bool[] _flying; private readonly bool[] _sighted; private readonly int[] _lights; private readonly GameObject _voices; private readonly AudioVoice[] _flightVoices; private bool _disposed; private bool _prepared; private bool Ready => !_disposed; public PlasmaEffects(int capacity) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) _orbs = (FxInstance?[])(object)new FxInstance[capacity]; _exploded = new bool[capacity]; _positions = (Vector3[])(object)new Vector3[capacity]; _electric = new bool[capacity]; _flying = new bool[capacity]; _sighted = new bool[capacity]; _lights = new int[capacity]; _flightVoices = (AudioVoice[])(object)new AudioVoice[capacity]; _voices = new GameObject("Plasma ionization voices"); Object.DontDestroyOnLoad((Object)(object)_voices); for (int i = 0; i < _flightVoices.Length; i++) { Transform transform = new GameObject("plasma-flight-voice-" + i).transform; transform.SetParent(_voices.transform, false); _flightVoices[i] = EnergyAudio.Library.Voice(transform); } } internal IEnumerator Prepare() { if (_prepared) { yield break; } foreach (object item in _pool.Prepare(EnergyVfx.Orb, _orbs.Length * 2, (Color?)null)) { yield return item; } foreach (object item2 in _pool.Prepare(EnergyVfx.Orb, _orbs.Length * 2, (Color?)ElectricTint)) { yield return item2; } foreach (object item3 in _pool.Prepare(EnergyVfx.Discharge, _orbs.Length * 3 * 2, (Color?)null)) { yield return item3; } foreach (object item4 in _pool.Prepare(EnergyVfx.PlasmaExplosion, _orbs.Length * 2, (Color?)null)) { yield return item4; } _prepared = true; } public void ChargingOrb(int index, Vector3 position, float radius, float now, bool electric, Ray? sight) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (Ready) { float num = radius * 0.55f; if (sight.HasValue) { Ray valueOrDefault = sight.GetValueOrDefault(); Vector3 val = position - ((Ray)(ref valueOrDefault)).origin; Vector3 val2 = val - ((Ray)(ref valueOrDefault)).direction * Vector3.Dot(val, ((Ray)(ref valueOrDefault)).direction); float magnitude = ((Vector3)(ref val2)).magnitude; num = Mathf.Min(num, Mathf.Max(0f, magnitude - 0.01f) / 1.1f); } Draw(index, position, num, electric, flying: false, sight.HasValue, now); } } public void Orb(int index, Vector3 position, float radius, float now, bool electric, bool flying) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!Ready) { return; } Draw(index, position, radius, electric, flying, sighted: false, now); if (flying) { AudioVoice val = _flightVoices[index]; val.Anchor.position = position; if (!val.Playing) { val.Play(electric ? "shock-fly-loop" : "orb-fly-loop", 0.85f); } } } public void Fade(int index, Vector3 position) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (Ready) { StopFlight(index); FxInstance? obj = _orbs[index]; if (obj != null) { obj.PlayState("fizzle"); } EnergyAudio.PlayAt("shock-fade", position, 0.5f); } } private void StopFlight(int index) { _flightVoices[index].Stop(0f); } public void Contact(Vector3 position) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (Ready) { EnergyAudio.PlayAt("shock-hit", position, 0.22f); } } public void Dissipate(int index, Vector3 position, float radius, float progress, float now) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (Ready) { Draw(index, position, radius * (1f - Mathf.Clamp01(progress) * 0.9f), electric: true, flying: true, sighted: false, now); } } public void Explosion(int index, Vector3 position, float blastRadius, float now) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (Ready && !_exploded[index]) { _exploded[index] = true; StopFlight(index); Vector3 val = ((_orbs[index] != null) ? (_positions[index] - position) : Vector3.zero); ReleaseOrb(index); if (((Vector3)(ref val)).sqrMagnitude > 1E-06f) { position += ((Vector3)(ref val)).normalized * 0.3f; } FxInstance obj = _pool.Take(EnergyVfx.PlasmaExplosion, (Color?)null); obj.Place(position, Quaternion.identity, Mathf.Max(0.1f, blastRadius / 3f)); obj.Start(); PointLights.Flash(position, ref ExplosionLight, now, Mathf.Clamp(blastRadius / 3f, 0.6f, 1.4f), 0f); } } private void Draw(int index, Vector3 position, float radius, bool electric, bool flying, bool sighted, float now) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) _exploded[index] = false; FxInstance val = _orbs[index]; if (val != null && _electric[index] != electric) { val.Release(); _orbs[index] = null; } bool flag = _orbs[index] == null; ref FxInstance reference = ref _orbs[index]; FxInstance val2 = reference ?? (reference = _pool.Take(EnergyVfx.Orb, electric ? new Color?(ElectricTint) : ((Color?)null)).Hold()); _electric[index] = electric; if (flag) { _sighted[index] = sighted; string[] beyondCore = BeyondCore; foreach (string text in beyondCore) { val2.Emit(text, !sighted); } } _positions[index] = position; float num = Mathf.Max(0.02f, radius / 0.3f); val2.Place(position, Quaternion.identity, num); val2.Keep(); if (flag) { _flying[index] = false; } if (flag && !flying && !sighted) { val2.PlayState("charging"); } if (flying && !_flying[index]) { _flying[index] = true; val2.PlayState("launch"); if (electric) { val2.PlayState("storm"); } PointLights.Flash(position, ref LaunchLight, now, Mathf.Clamp(num, 0.5f, 1.2f), 0f); } ref int reference2 = ref _lights[index]; PointLightSpec val3 = (electric ? ElectricOrbLight : OrbLight); PointLights.Hold(ref reference2, position, ref val3, now, Mathf.Clamp(num, 0.3f, 1.2f)); } public void Pulse(int index, ReadOnlySpan<Vector3> targets, float now) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (Ready) { if (targets.Length < 1 || targets.Length > 3) { throw new ArgumentException("Invalid orb pulse targets."); } Vector3 val = _positions[index]; EnergyAudio.PlayAt("shock-pulse", val, 0.22f); PointLights.Flash(val, ref DischargeLight, now, 1f, 0f); ReadOnlySpan<Vector3> readOnlySpan = targets; for (int i = 0; i < readOnlySpan.Length; i++) { Vector3 val2 = readOnlySpan[i]; FxInstance obj = _pool.Take(EnergyVfx.Discharge, (Color?)null); obj.Stretch(val, val2); obj.Start(); } } } public void Charged(int index) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!Ready) { return; } FxInstance val = _orbs[index]; if (val != null) { if (!_sighted[index]) { val.PlayState("charged"); } PointLights.Flash(_positions[index], ref ChargedLight, Time.time, 1f, 0f); } } private void ReleaseOrb(int index) { FxInstance? obj = _orbs[index]; if (obj != null) { obj.Release(); } _orbs[index] = null; _flying[index] = false; PointLights.Release(ref _lights[index]); } public void Hide(int index) { if (Ready) { StopFlight(index); ReleaseOrb(index); _exploded[index] = false; } } public void Boom(Vector3 position) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (Ready) { EnergyAudio.PlayAt("orb-explode", position, 0.48f); } } public void Dispose() { if (!_disposed) { _disposed = true; AudioVoice[] flightVoices = _flightVoices; for (int i = 0; i < flightVoices.Length; i++) { flightVoices[i].Stop(0f); } _pool.Dispose(); if ((Object)(object)_voices != (Object)null) { Object.Destroy((Object)(object)_voices); } } } } internal sealed class PresentationColor { public float R { get; set; } public float G { get; set; } public float B { get; set; } public float A { get; set; } } internal sealed class PartPresentation { public string Name { get; set; } = ""; public string Surface { get; set; } = ""; public string TextureSet { get; set; } = ""; public Vector3 Pivot { get; set; } public PresentationColor Color { get; set; } = new PresentationColor(); } internal sealed class TextureSetPresentation { public string Name { get; set; } = ""; public bool Emission { get; set; } public bool Normal { get; set; } } internal sealed class WeaponPresentation { public string Mode { get; set; } = ""; public Vector3 RightHand { get; set; } public Vector3 LeftHand { get; set; } public Vector3 Muzzle { get; set; } public Vector3 SightLook { get; set; } public Vector3 BoundsMin { get; set; } public Vector3 BoundsMax { get; set; } public PartPresentation[] Parts { get; set; } = Array.Empty<PartPresentation>(); } internal sealed class PresentationContract { internal const int CurrentFormat = 4; private static readonly string[] RequiredParts = new string[6] { "Receiver", "Rail", "Front", "Stock", "Magazine", "Sight" }; public int Format { get; set; } public WeaponPresentation[] Weapons { get; set; } = Array.Empty<WeaponPresentation>(); public TextureSetPresentation[] TextureSets { get; set; } = Array.Empty<TextureSetPresentation>(); internal TextureSetPresentation TextureSet(string name) { return Array.Find(TextureSets, (TextureSetPresentation set) => set.Name == name) ?? throw new ArgumentException("Unknown texture set: " + name); } internal static bool Finite(Vector3 v) { if (float.IsFinite(v.X) && float.IsFinite(v.Y)) { return float.IsFinite(v.Z); } return false; } internal static PresentationContract Parse(string json) { PresentationContract presentationContract = JsonSerializer.Deserialize<PresentationContract>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, IncludeFields = true }) ?? throw new ArgumentException("Missing presentation contract."); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if (presentationContract.Format != 4 || presentationContract.Weapons.Length == 0 || presentationContract.Weapons.Length > 128) { throw new ArgumentException($"Expected one to 128 format-{4} model presentations."); } HashSet<string> hashSet2 = new HashSet<string>(); TextureSetPresentation[] textureSets = presentationContract.TextureSets; foreach (TextureSetPresentation textureSetPresentation in textureSets) { if (string.IsNullOrEmpty(textureSetPresentation.Name) || !hashSet2.Add(textureSetPresentation.Name)) { throw new ArgumentException("Invalid or duplicate texture set."); } } WeaponPresentation[] weapons = presentationContract.Weapons; foreach (WeaponPresentation weaponPresentation in weapons) { if (string.IsNullOrWhiteSpace(weaponPresentation.Mode) || !hashSet.Add(weaponPresentation.Mode)) { throw new ArgumentException("Invalid or duplicate weapon: " + weaponPresentation.Mode); } Vector3[] array = new Vector3[6] { weaponPresentation.RightHand, weaponPresentation.LeftHand, weaponPresentation.Muzzle, weaponPresentation.SightLook, weaponPresentation.BoundsMin, weaponPresentation.BoundsMax }; int j; for (j = 0; j < array.Length; j++) { if (!Finite(array[j])) { throw new ArgumentException("Non-finite socket or bounds: " + weaponPresentation.Mode); } } float num = weaponPresentation.BoundsMax.Z - weaponPresentation.BoundsMin.Z; if (weaponPresentation.BoundsMin.X >= weaponPresentation.BoundsMax.X || weaponPresentation.BoundsMin.Y >= weaponPresentation.BoundsMax.Y || num < 0.5f || num > 1.3f) { throw new ArgumentException("Invalid metric weapon bounds: " + weaponPresentation.Mode); } HashSet<string> hashSet3 = new HashSet<string>(); PartPresentation[] parts = weaponPresentation.Parts; j = 0; while (j < parts.Length) { PartPresentation partPresentation = parts[j]; if (string.IsNullOrEmpty(partPresentation.Name) || !hashSet3.Add(partPresentation.Name) || !Finite(partPresentation.Pivot)) { throw new ArgumentException("Invalid or duplicate part: " + weaponPresentation.Mode + "/" + partPresentation.Name); } string surface = partPresentation.Surface; bool flag = ((surface == "glass" || surface == "reticle") ? true : false); bool flag2 = flag; bool num2; if (!(partPresentation.Surface == "body")) { if (!flag2 || partPresentation.TextureSet != "") { goto IL_032b; } num2 = partPresentation.Color.A <= 0f; } else { num2 = !hashSet2.Contains(partPresentation.TextureSet); } if (!num2) { j++; continue; } goto IL_032b; IL_032b: throw new ArgumentException("Invalid part surface: " + weaponPresentation.Mode + "/" + partPresentation.Name); } string[] requiredParts = RequiredParts; foreach (string text in requiredParts) { if (!hashSet3.Contains(text)) { throw new ArgumentException("Missing part: " + weaponPresentation.Mode + "/" + text); } } } return presentationContract; } } internal static class PresentationPreload { private static IEnumerator? _work; private const float AudioLoadSeconds = 120f; private static string _phase = "icons"; internal static bool Ready { get; private set; } internal static bool Failed { get; private set; } internal static void Tick() { if (Ready || Failed) { return; } try { if (_work == null) { _work = Prepare(); } if (!_work.MoveNext()) { (_work as IDisposable)?.Dispose(); _work = null; Ready = true; } } catch (Exception ex) { Failed = true; (_work as IDisposable)?.Dispose(); _work = null; Plugin.Error("EnergyPreload failed at " + _phase + "; energy weapons stay disabled: " + ex); } } private static IEnumerator Prepare() { IEnumerator[] phases = new IEnumerator[3] { WeaponModels.Preload(), EnergyVfx.Preload(), PrepareAudio() }; string[] names = new string[3] { "models", "vfx", "audio" }; for (int i = 0; i < phases.Length; i++) { _phase = names[i]; using (phases[i] as IDisposable) { while (phases[i].MoveNext()) { yield return null; } yield return null; } } } private static IEnumerator PrepareAudio() { float give = Time.realtimeSinceStartup + 120f; while (!EnergyAudio.Loaded) { if (Time.realtimeSinceStartup > give) { throw new TimeoutException("ForgeAudio did not finish decoding the energy sounds in " + 120f + " s."); } yield return null; } EnergyAudio.Library.Require(EnergyAudio.Cues); } } internal static class SightProjection { internal const float VirtualDistance = 100f; internal static bool TryOffset(Vector3 eye, float planeDepth, out Vector3 offset) { offset = default(Vector3); if (!float.IsFinite(eye.X) || !float.IsFinite(eye.Y) || !float.IsFinite(eye.Z) || !float.IsFinite(planeDepth) || eye.Z >= planeDepth) { return false; } float num = 100f / (100f + planeDepth - eye.Z); offset = new Vector3(eye.X * num, eye.Y * num, 0f); return true; } } internal sealed class SpecialEffects : IDisposable { private sealed class DiscView { internal Transform Root; internal Transform Blade; internal TrailRenderer Trail; internal AudioVoice Audio; internal Vector3 Lift; } private const float FlameSize = 8f; private const float ExplosionSize = 2.2f; private const float ProjectileScale = 0.15f; private const float CollapseFlashDelay = 0.28f; private const string FlameIgnite = "ignite"; private const string HoleOpenState = "open"; private const string HoleCollapseState = "collapse"; private static readonly PointLightSpec FlameLight = new PointLightSpec(new Color(1f, 0.45f, 0.12f), 6f, 4f, 0f, 0.35f); private static readonly PointLightSpec FlameImpactLight = new PointLightSpec(new Color(1f, 0.45f, 0.12f), 2.5f, 2f, 0f, 0.4f); private static readonly PointLightSpec ExplosionLight = new PointLightSpec(new Color(1f, 0.62f, 0.28f), 8f, 3f, 0.5f, 0.25f); private static readonly PointLightSpec SparksLight = new PointLightSpec(new Color(1f, 0.7f, 0.4f), 2.5f, 2f, 0.12f, 0.3f); private static readonly PointLightSpec HoleLight = new PointLightSpec(new Color(0.55f, 0.15f, 1f), 6f, 2f, 0f, 0.2f); private static readonly PointLightSpec HoleOpenLight = new PointLightSpec(new Color(0.6f, 0.3f, 1f), 9f, 5f, 0.4f, 0.1f); private static readonly PointLightSpec HoleCollapseLight = new PointLightSpec(new Color(0.75f, 0.5f, 1f), 8f, 4f, 0.35f, 0.1f); private static readonly Color ShellTint = new Color(1f, 0.6f, 0.25f); private static readonly PointLightSpec ShellLight = new PointLightSpec(new Color(1f, 0.62f, 0.28f), 3f, 1.5f, 0f, 0.2f); private const float ShellSize = 0.09f; private const float OrbSize = 0.3f; private const int ShellCapacity = 64; private const float FlameImpactInterval = 0.08f; private const int FlameStart = 0; private const int FlameCrackle = 1; private const int FlameImpactSound = 2; private const int DiscLaunch = 3; private const int DiscFlight = 4; private const int DiscBounce = 5; private const int HoleLaunchSound = 6; private const int HoleFlight = 7; private const int HoleOpen = 8; private const int HoleClose = 9; private const int BlastLaunchSound = 10; private const int BlastCycle = 11; private const int BlastImpact = 12; private const int FlameLoop = 13; private const int FlameStop = 14; private const int DiscStop = 15; private const int HoleField = 16; private const uint DiscHitSound = 3633778169u; private readonly GameObject _root; private readonly FxInstancePool _pool = new FxInstancePool("Energy disc flame and gravity effects"); private readonly DiscView[] _discs = new DiscView[3]; private readonly AudioVoice _flameAudio; private readonly AudioVoice _holeAudio; private readonly AudioVoice _crackleAudio; private readonly Transform _holeVoice; private readonly Transform _crackleVoice; private readonly AudioVoice[] _launchVoices = (AudioVoice[])(object)new AudioVoice[3]; private readonly ImpactLook _sparks; private readonly ImpactLook _discHit; private readonly ImpactLook _explosion; private readonly FxInstance?[] _shells = (FxInstance?[])(object)new FxInstance[64]; private readonly int[] _shellLights = new int[64]; private FxInstance? _flame; private FxInstance? _hole; private int _flameLight; private int _flameImpactLight; private int _holeLight; private int _launchCursor; private float _nextFlameImpact; private float _nextFlameImpactSound; private bool _flaming; private bool _disposed; private bool _prepared; private static readonly string[] Sounds = new string[17] { "flame-start", "flame-crackle", "flame-hit", "disc-fire", "disc-fly-loop", "disc-bounce", "gravity-fire", "gravity-fly-loop", "gravity-open", "gravity-close", "blast-fire", "blast-cycle", "blast-explode", "flame-loop", "flame-stop", "disc-drop", "gravity-field-loop" }; internal Quaternion ViewRotation { get; set; } = Quaternion.identity; private bool Ready { get { if (!_disposed) { return Alive((Object?)(object)_root); } return false; } } internal SpecialEffects() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown _sparks = new ImpactLook { Model = EnergyVfx.Sparks, Light = SparksLight, LightLift = 0.1f }; _discHit = new ImpactLook { Model = EnergyVfx.Sparks, ModelScale = 0.7f, Light = SparksLight }; _explosion = new ImpactLook { Model = EnergyVfx.Explosion, Light = ExplosionLight }; _root = new GameObject("Energy disc flame and gravity voices"); Object.DontDestroyOnLoad((Object)(object)_root); try { Material sharedMaterial = TrailMaterial(); GameObject prefab = EnergyVfx.Disc.Prefab; for (int i = 0; i < _discs.Length; i++) { GameObject val = Object.Instantiate<GameObject>(prefab, _root.transform, false); ((Object)val).name = "ricochet-disc-" + i; TrailRenderer component = val.GetComponent<TrailRenderer>(); Transform val2 = val.transform.Find("cast-metal-rotor"); if ((Object)(object)component == (Object)null || (Object)(object)val2 == (Object)null) { throw new InvalidOperationException("Disc prefab is missing its blade or trail."); } ((Renderer)component).sharedMaterial = sharedMaterial; _discs[i] = new DiscView { Root = val.transform, Blade = val2, Audio = EnergyAudio.Library.Voice(val.transform), Trail = component }; val.SetActive(false); } } catch { Object.Destroy((Object)(object)_root); _pool.Dispose(); throw; } _holeVoice = Child("singularity-voice", _root.transform); _crackleVoice = Child("flame-crackle-voice", _root.transform); _holeAudio = EnergyAudio.Library.Voice(_holeVoice); _crackleAudio = EnergyAudio.Library.Voice(_crackleVoice); _flameAudio = EnergyAudio.Library.Voice(_root.transform); for (int j = 0; j < _launchVoices.Length; j++) { _launchVoices[j] = EnergyAudio.Library.Voice(Child("launch-voice-" + j, _root.transform)); } } internal IEnumerator Prepare() { if (_prepared) { yield break; } foreach (object item in _pool.Prepare(EnergyVfx.Flame, 2, (Color?)null)) { yield return item; } foreach (object item2 in _pool.Prepare(EnergyVfx.FlameImpact, 8, (Color?)null)) { yield return item2; } foreach (object item3 in Impacts.Prepare(_explosion, 4)) { yield return item3; } foreach (object item4 in Impacts.Prepare(_sparks, 8)) { yield return item4; } foreach (object item5 in _pool.Prepare(EnergyVfx.BlackHole, 2, (Color?)null)) { yield return item5; } foreach (object item6 in _pool.Prepare(EnergyVfx.Orb, 4, (Color?)ShellTint)) { yield return item6; } _prepared = true; } private static Transform Child(string name, Transform parent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Transform transform = new GameObject(name).transform; transform.SetParent(parent, false); return transform; } private static Material TrailMaterial() { ParticleSystemRenderer component = ((Component)(EnergyVfx.Orb.Prefab.transform.Find("trail") ?? throw new InvalidOperationException(EnergyVfx.Orb.Name + " has no trail layer for the disc streak."))).GetComponent<ParticleSystemRenderer>(); Material val = (Alive((Object?)(object)component) ? component.trailMaterial : null); if (!Alive((Object?)(object)val)) { throw new InvalidOperationException(EnergyVfx.Orb.Name + "'s trail layer has no trail material."); } return val; } private static bool Alive(Object? value) { if (value != null) { return value != (Object)null; } return false; } internal void Origin(Vector3 position) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (Ready) { _root.transform.position = position; } } private void BurstSound(int clip, float volume = 0.8f) { if (Ready) { _launchVoices[_launchCursor++ % _launchVoices.Length].Play(Sounds[clip], volume); } } private void LoopSound(AudioVoice voice, int clip, float volume) { if (Ready && !voice.IsPlaying(Sounds[clip])) { voice.Play(Sounds[clip], volume); } } private void OneShot(ModelHandle model, Vector3 position, Quaternion rotation, float scale = 1f) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) FxInstance obj = _pool.Take(model, (Color?)null); obj.Place(position, rotation, scale); obj.Start(); } private static Quaternion Facing(Vector3 direction) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!(((Vector3)(ref direction)).sqrMagnitude > 0.0001f)) { return Quaternion.LookRotation(Vector3.up); } return Quaternion.LookRotation(direction); } internal void Flame(Vector3 start, Vector3 direction, float distance, float now) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) if (!Ready) { return; } Origin(start); if (((Vector3)(ref direction)).sqrMagnitude < 0.0001f || distance <= 0.12f) { StopFlame(); return; } if (!_flaming) { _flaming = true; BurstSound(0); } LoopSound(_flameAudio, 13, 0.5f); bool num = _flame == null; if (_flame == null) { _flame = _pool.Take(EnergyVfx.Flame, (Color?)null).Hold(); } _flame.Place(start, Facing(direction), distance / 8f); _flame.Keep(); if (num) { _flame.PlayState("ignite"); } PointLights.Hold(ref _flameLight, start + ((Vector3)(ref direction)).normalized * Mathf.Min(1.2f, distance), ref FlameLight, now, 1f); } internal void StopFlame() { if (_flaming || _flame != null || _flameLight != 0 || _flameImpactLight != 0) { if (_flaming && !_disposed) { _flameAudio.Stop(0f);