Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BetterArmory v1.0.2
BetterArmory.dll
Decompiled 3 hours 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.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BetterArmory; using BetterArmory.Effects; using BetterArmory.Interactive; using BetterArmory.Ui; using Dawn; using GameNetcodeStuff; using HarmonyLib; using LethalCompanyInputUtils.Api; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Utilities; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using UnityEngine.SceneManagement; using UnityEngine.UI; using Y4NGZInteractions.InteractionAnimationApi; using Y4NGZUpgrades.HUD; using Y4NGZUpgrades.Interactive.Feedback; using Y4NGZUpgrades.Weapons; using Y4NGZUpgrades.Weapons.Patches; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BetterArmory")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Better Armory weapon plugin split out of Y4NGZUpgrades")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+dfef71c89c7844cd9ab349fbd0fbf0af3289f0ac")] [assembly: AssemblyProduct("BetterArmory")] [assembly: AssemblyTitle("BetterArmory")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.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 Y4NGZUpgrades.Weapons { internal sealed class AmmoPickupConfig { public string Id; public string DisplayName; public WeaponAmmoType AmmoType; public string FilePrefix; public float TargetWorldSize; public int ScrapMinValue; public int ScrapMaxValue; internal int DefaultRoundsGranted; internal int DefaultScrapRarity = 18; internal Func<int> ReadRoundsGranted; internal Func<int> ReadScrapRarity; internal string ShopDescription; internal int RoundsGranted { get { try { return Math.Max(0, (ReadRoundsGranted != null) ? ReadRoundsGranted() : DefaultRoundsGranted); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Could not read rounds config for " + DisplayName + ": " + ex.Message)); } return Math.Max(0, DefaultRoundsGranted); } } } internal int ScrapRarity { get { try { return Math.Max(0, (ReadScrapRarity != null) ? ReadScrapRarity() : DefaultScrapRarity); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Could not read scrap rarity for " + DisplayName + ": " + ex.Message)); } return Math.Max(0, DefaultScrapRarity); } } } } internal static class AmmoPickupCatalog { internal const string ShotgunShellsBoxId = "shotgun_shells_box"; internal const string PistolRoundsBoxId = "pistol_rounds_box"; internal const string HeavyRoundsBoxId = "heavy_rounds_box"; internal const string FuelTankId = "fuel_tank"; internal const string GrenadeId = "grenade"; internal static readonly AmmoPickupConfig ShotgunShellsBox = new AmmoPickupConfig { Id = "shotgun_shells_box", DisplayName = "Shotgun Shells", AmmoType = WeaponAmmoType.ShotgunShells, FilePrefix = "shotgun_shells", TargetWorldSize = 0.28f, ScrapMinValue = 8, ScrapMaxValue = 16, DefaultRoundsGranted = 4, DefaultScrapRarity = 18, ReadRoundsGranted = () => ArmoryContentConfig.RoundsPerBox(WeaponAmmoType.ShotgunShells, 4), ReadScrapRarity = () => ArmoryContentConfig.AmmoInteriorWeight(WeaponAmmoType.ShotgunShells, 18), ShopDescription = "Buckshot in a company-issue carton. Feeds the shell reserve every shotgun draws from." }; internal static readonly AmmoPickupConfig PistolRoundsBox = new AmmoPickupConfig { Id = "pistol_rounds_box", DisplayName = "Small Caliber Rounds", AmmoType = WeaponAmmoType.SmallCaliberRounds, FilePrefix = "pistol_rounds", TargetWorldSize = 0.22f, ScrapMinValue = 8, ScrapMaxValue = 16, DefaultRoundsGranted = 6, DefaultScrapRarity = 18, ReadRoundsGranted = () => ArmoryContentConfig.RoundsPerBox(WeaponAmmoType.SmallCaliberRounds, 6), ReadScrapRarity = () => ArmoryContentConfig.AmmoInteriorWeight(WeaponAmmoType.SmallCaliberRounds, 18), ShopDescription = "Mixed small-calibre cartridges. Serves the revolver and the pistol from one reserve." }; internal static readonly AmmoPickupConfig HeavyRoundsBox = new AmmoPickupConfig { Id = "heavy_rounds_box", DisplayName = "Heavy Caliber Rounds", AmmoType = WeaponAmmoType.HeavyCaliberRounds, FilePrefix = "heavy_rounds", TargetWorldSize = 0.26f, ScrapMinValue = 8, ScrapMaxValue = 16, DefaultRoundsGranted = 10, DefaultScrapRarity = 18, ReadRoundsGranted = () => ArmoryContentConfig.RoundsPerBox(WeaponAmmoType.HeavyCaliberRounds, 10), ReadScrapRarity = () => ArmoryContentConfig.AmmoInteriorWeight(WeaponAmmoType.HeavyCaliberRounds, 18), ShopDescription = "Belted rifle brass. The only thing the assault rifle and the bolt-action will eat." }; internal static readonly AmmoPickupConfig FuelTank = new AmmoPickupConfig { Id = "fuel_tank", DisplayName = "Fuel Tank", AmmoType = WeaponAmmoType.Fuel, FilePrefix = "fuel_tank", TargetWorldSize = 0.55f, ScrapMinValue = 8, ScrapMaxValue = 16, DefaultRoundsGranted = 100, DefaultScrapRarity = 18, ReadRoundsGranted = () => ArmoryContentConfig.RoundsPerBox(WeaponAmmoType.Fuel, 100), ReadScrapRarity = () => ArmoryContentConfig.AmmoInteriorWeight(WeaponAmmoType.Fuel, 18), ShopDescription = "Pressurised accelerant. Tops the flame thrower's tank back up out in the field." }; internal static readonly AmmoPickupConfig Grenade = new AmmoPickupConfig { Id = "grenade", DisplayName = "Grenade", AmmoType = WeaponAmmoType.Grenades, FilePrefix = "grenade", TargetWorldSize = 0.2f, ScrapMinValue = 18, ScrapMaxValue = 32, DefaultRoundsGranted = 1, DefaultScrapRarity = 12, ReadRoundsGranted = () => ArmoryContentConfig.RoundsPerBox(WeaponAmmoType.Grenades, 1), ReadScrapRarity = () => ArmoryContentConfig.AmmoInteriorWeight(WeaponAmmoType.Grenades, 12), ShopDescription = "One fragmentation grenade. Throw it by hand, or load it into the launcher." }; internal static IEnumerable<AmmoPickupConfig> All() { yield return ShotgunShellsBox; yield return PistolRoundsBox; yield return HeavyRoundsBox; yield return FuelTank; } internal static AmmoPickupConfig ById(string id) { foreach (AmmoPickupConfig item in All()) { if (string.Equals(item.Id, id, StringComparison.Ordinal)) { return item; } } return ShotgunShellsBox; } } internal sealed class AmmoPickupDevSpawnHost : MonoBehaviour { internal static bool Enabled = true; internal static Key SpawnKey = (Key)70; private int _lastSpawnFrame = -1; internal static void Ensure() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) try { GameObject host = ArmoryPersistentRunner.Host; if (!((Object)(object)host == (Object)null) && !((Object)(object)host.GetComponent<AmmoPickupDevSpawnHost>() != (Object)null)) { host.AddComponent<AmmoPickupDevSpawnHost>(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Y4NGZAmmo] Dev spawn host attached (enabled={Enabled}, key={SpawnKey})."); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Y4NGZAmmo] Could not attach the dev spawn host: " + ex.Message)); } } } private void Update() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { Keyboard current = Keyboard.current; if (Enabled && current != null && ((ButtonControl)current[SpawnKey]).wasPressedThisFrame && _lastSpawnFrame != Time.frameCount) { _lastSpawnFrame = Time.frameCount; AmmoPickupRegistry.DebugSpawnAllAtLocalPlayer(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Dev spawn input failed safely: " + ex.Message)); } } } } internal sealed class AmmoPickupItem : MonoBehaviour { public string ConfigId = "shotgun_shells_box"; [NonSerialized] internal bool ConsumptionStarted; internal AmmoPickupConfig Config { get { try { return AmmoPickupCatalog.ById(ConfigId); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Could not resolve pickup config '" + ConfigId + "': " + ex.Message)); } return AmmoPickupCatalog.ShotgunShellsBox; } } } } internal static class AmmoPickupRegistry { private readonly struct VisualBuildResult { internal readonly MeshRenderer Renderer; internal readonly Bounds LocalBounds; internal VisualBuildResult(MeshRenderer renderer, Bounds localBounds) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Renderer = renderer; LocalBounds = localBounds; } } private const string PickupSoundFileName = "ammo_pickup.mp3"; private const int PropsLayer = 6; private const int ScanNodeFallbackLayer = 22; private const float PickupRootGroundOffset = 0.12f; private const float VisualFloorClearance = 0.015f; private const float MinimumPickupWidth = 0.52f; private const float MinimumPickupHeight = 0.24f; private const float MinimumPickupDepth = 0.38f; private static readonly Dictionary<string, GameObject> Prefabs = new Dictionary<string, GameObject>(); private static readonly Dictionary<string, Item> Items = new Dictionary<string, Item>(); private static readonly List<GameObject> PrefabHolders = new List<GameObject>(); private static bool _registered; private static bool _pickupSoundLoadAttempted; private static bool _missingPickupSoundWarned; private static AudioClip _pickupSound; private static AudioClip _fallbackGrabSound; private static AudioClip _fallbackDropSound; private static Sprite _simpleIcon; internal static int RegisteredCount => Prefabs.Count; internal static GameObject PrefabFor(string configId) { try { GameObject value; return Prefabs.TryGetValue(configId ?? string.Empty, out value) ? value : null; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Prefab lookup failed for '" + configId + "': " + ex.Message)); } return null; } } internal static Item ItemFor(string configId) { try { Item value; return Items.TryGetValue(configId ?? string.Empty, out value) ? value : null; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Item lookup failed for '" + configId + "': " + ex.Message)); } return null; } } internal static bool TryRegisterAll() { try { RefreshFallbackSounds(); if (_registered) { ApplySoundsToRegisteredItems(); TryBeginPickupSoundLoad(); return true; } bool flag = true; foreach (AmmoPickupConfig item in AmmoPickupCatalog.All()) { if ((Object)(object)PrefabFor(item.Id) != (Object)null && (Object)(object)ItemFor(item.Id) != (Object)null) { continue; } try { BuildAndRegister(item); } catch (Exception arg) { flag = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[Y4NGZAmmo] Registration failed for {item.DisplayName}; other gameplay will continue: {arg}"); } } } foreach (AmmoPickupConfig item2 in AmmoPickupCatalog.All()) { flag &= (Object)(object)PrefabFor(item2.Id) != (Object)null && (Object)(object)ItemFor(item2.Id) != (Object)null; } bool num = flag && !_registered; _registered = flag; if (num) { if (Prefabs.Count > 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[Y4NGZAmmo] Registered {Prefabs.Count} ammo pickups."); } } else { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[Y4NGZAmmo] Registered 0 ammo pickups: the ammo pickup catalog yielded no entries. No ammunition will spawn or appear in the shop."); } } } ApplySoundsToRegisteredItems(); TryBeginPickupSoundLoad(); return flag; } catch (Exception arg2) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)$"[Y4NGZAmmo] Ammo pickup registration failed safely: {arg2}"); } return false; } } private static void BuildAndRegister(AmmoPickupConfig config) { GameObject holder = null; GameObject val = null; Item val2 = null; try { val = BuildItemPrefab(config, out holder); val2 = BuildItemProperties(config, val); PhysicsProp component = val.GetComponent<PhysicsProp>(); if ((Object)(object)component == (Object)null) { throw new InvalidOperationException("The generated prefab has no PhysicsProp component."); } ((GrabbableObject)component).itemProperties = val2; val2.spawnPrefab = val; DawnLibCompat.FixMixerGroups(val); DawnLibCompat.RegisterNetworkPrefab(val); WeaponAmmoType family = config.AmmoType; DawnLibCompat.RegisterItem(config.Id, val2, () => ArmoryContentConfig.IsAmmoPurchasable(family), () => ArmoryContentConfig.AmmoShopPrice(family), config.ShopDescription, () => config.ScrapRarity); Prefabs[config.Id] = val; Items[config.Id] = val2; if ((Object)(object)holder != (Object)null) { PrefabHolders.Add(holder); } NetworkObject component2 = val.GetComponent<NetworkObject>(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)($"[Y4NGZAmmo] Registered {config.DisplayName} (hash={component2?.GlobalObjectIdHash ?? 0}, " + $"rounds={config.RoundsGranted}, scrapRarity={config.ScrapRarity}).")); } } catch { if ((Object)(object)holder != (Object)null) { Object.DestroyImmediate((Object)(object)holder); } else if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)val); } if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)val2); } throw; } } private static GameObject BuildItemPrefab(AmmoPickupConfig config, out GameObject holder) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) holder = new GameObject("Y4NGZ_" + config.Id + "_InactiveHolder"); ((Object)holder).hideFlags = (HideFlags)61; holder.SetActive(false); Object.DontDestroyOnLoad((Object)(object)holder); GameObject val = new GameObject("Y4NGZ_" + config.Id + "_Prefab"); val.transform.SetParent(holder.transform, false); ((Object)val).hideFlags = (HideFlags)0; val.layer = 6; SetTagSafe(val, "PhysicsProp"); NetworkObject obj = val.AddComponent<NetworkObject>(); obj.SynchronizeTransform = true; obj.SceneMigrationSynchronization = true; obj.SpawnWithObservers = true; obj.DontDestroyWithOwner = true; obj.AutoObjectParentSync = true; AssignStableNetworkHash(obj, config.Id); Rigidbody val2 = val.AddComponent<Rigidbody>(); val2.isKinematic = true; val2.useGravity = false; val2.mass = 0.8f; val2.drag = 0.15f; val2.angularDrag = 4f; AudioSource obj2 = val.AddComponent<AudioSource>(); obj2.playOnAwake = false; obj2.spatialBlend = 1f; obj2.dopplerLevel = 0f; obj2.rolloffMode = (AudioRolloffMode)0; obj2.minDistance = 1f; obj2.maxDistance = 16f; PhysicsProp val3 = val.AddComponent<PhysicsProp>(); ((GrabbableObject)val3).grabbable = true; ((GrabbableObject)val3).grabbableToEnemies = false; ((GrabbableObject)val3).deactivated = false; ((GrabbableObject)val3).isHeld = false; ((GrabbableObject)val3).isPocketed = false; ((GrabbableObject)val3).isInFactory = false; ((GrabbableObject)val3).scrapValue = (config.ScrapMinValue + config.ScrapMaxValue) / 2; ((GrabbableObject)val3).rotateObject = false; ((GrabbableObject)val3).propBody = val2; ((GrabbableObject)val3).originalScale = Vector3.one; ((GrabbableObject)val3).customGrabTooltip = "Collect ammunition"; VisualBuildResult visualBuildResult = BuildVisual(val, config); BoxCollider val4 = val.AddComponent<BoxCollider>(); ((Collider)val4).isTrigger = false; Bounds localBounds = visualBuildResult.LocalBounds; val4.center = ((Bounds)(ref localBounds)).center; localBounds = visualBuildResult.LocalBounds; float num = Mathf.Max(0.52f, ((Bounds)(ref localBounds)).size.x + 0.05f); localBounds = visualBuildResult.LocalBounds; float num2 = Mathf.Max(0.24f, ((Bounds)(ref localBounds)).size.y + 0.04f); localBounds = visualBuildResult.LocalBounds; val4.size = new Vector3(num, num2, Mathf.Max(0.38f, ((Bounds)(ref localBounds)).size.z + 0.05f)); Vector3 center = val4.center; center.y = Mathf.Max(center.y, val4.size.y * 0.5f + 0.015f - 0.12f); val4.center = center; ((Collider)val4).enabled = true; ((Component)val4).gameObject.layer = 6; SetTagSafe(((Component)val4).gameObject, "PhysicsProp"); ((GrabbableObject)val3).propColliders = (Collider[])(object)new Collider[1] { (Collider)val4 }; ((GrabbableObject)val3).mainObjectRenderer = visualBuildResult.Renderer; BuildScanNode(val, val4, config); val.AddComponent<AmmoPickupItem>().ConfigId = config.Id; return val; } private static VisualBuildResult BuildVisual(GameObject root, AmmoPickupConfig config) { //IL_046f: Unknown result type (might be due to invalid IL or missing references) //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_047f: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_0403: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) try { if (config.Id == "grenade" && TryBuildBundledGrenadeVisual(root, config, out var result)) { return result; } string text = WeaponAssetLoader.AssetDirectory(); Mesh val = WeaponAssetLoader.LoadMesh(string.IsNullOrWhiteSpace(text) ? null : Path.Combine(text, config.FilePrefix + ".mesh"), config.DisplayName); Material val2 = WeaponAssetLoader.BuildMaterial(text, config.FilePrefix, config.DisplayName); GameObject val3; MeshRenderer val4; if (!((Object)(object)val == (Object)null)) { Bounds bounds = val.bounds; if (!(WeaponAssetLoader.LongestAxis(((Bounds)(ref bounds)).size) <= 1E-05f)) { val3 = new GameObject("Y4NGZ_" + config.Id + "_Visual"); val3.transform.SetParent(root.transform, false); val3.AddComponent<MeshFilter>().sharedMesh = val; val4 = val3.AddComponent<MeshRenderer>(); goto IL_017c; } } val3 = GameObject.CreatePrimitive((PrimitiveType)3); val3.SetActive(false); ((Object)val3).name = "Y4NGZ_" + config.Id + "_PlaceholderVisual"; val3.transform.SetParent(root.transform, false); Collider component = val3.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } MeshFilter component2 = val3.GetComponent<MeshFilter>(); val = ((component2 != null) ? component2.sharedMesh : null); val4 = val3.GetComponent<MeshRenderer>(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] " + config.DisplayName + " is using a primitive-cube placeholder visual.")); } val3.SetActive(true); goto IL_017c; IL_017c: val3.layer = root.layer; if ((Object)(object)val4 != (Object)null && (Object)(object)val2 != (Object)null) { ((Renderer)val4).sharedMaterial = val2; } Bounds val5 = (Bounds)(((Object)(object)val != (Object)null) ? val.bounds : new Bounds(Vector3.zero, Vector3.one)); float num = Mathf.Max(1E-05f, WeaponAssetLoader.LongestAxis(((Bounds)(ref val5)).size)); float num2 = Mathf.Max(0.01f, config.TargetWorldSize) / num; val3.transform.localRotation = Quaternion.identity; val3.transform.localScale = Vector3.one * num2; val3.transform.localPosition = new Vector3((0f - ((Bounds)(ref val5)).center.x) * num2, (0f - ((Bounds)(ref val5)).min.y) * num2 + 0.015f - 0.12f, (0f - ((Bounds)(ref val5)).center.z) * num2); Vector3 val6 = default(Vector3); ((Vector3)(ref val6))..ctor(Mathf.Abs(((Bounds)(ref val5)).size.x * num2), Mathf.Abs(((Bounds)(ref val5)).size.y * num2), Mathf.Abs(((Bounds)(ref val5)).size.z * num2)); Bounds localBounds = default(Bounds); ((Bounds)(ref localBounds))..ctor(new Vector3(0f, val6.y * 0.5f + 0.015f - 0.12f, 0f), val6); return new VisualBuildResult(val4, localBounds); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Y4NGZAmmo] Visual build failed for " + config.DisplayName + "; using a minimal pickup box: " + ex.Message)); } try { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); obj.SetActive(false); ((Object)obj).name = "Y4NGZ_" + config.Id + "_EmergencyPlaceholderVisual"; obj.transform.SetParent(root.transform, false); Collider component3 = obj.GetComponent<Collider>(); if ((Object)(object)component3 != (Object)null) { Object.DestroyImmediate((Object)(object)component3); } obj.layer = root.layer; obj.transform.localPosition = Vector3.up * (config.TargetWorldSize * 0.5f + 0.015f - 0.12f); obj.transform.localRotation = Quaternion.identity; obj.transform.localScale = Vector3.one * config.TargetWorldSize; obj.SetActive(true); Bounds localBounds2 = default(Bounds); ((Bounds)(ref localBounds2))..ctor(new Vector3(0f, config.TargetWorldSize * 0.5f + 0.015f - 0.12f, 0f), Vector3.one * config.TargetWorldSize); return new VisualBuildResult(obj.GetComponent<MeshRenderer>(), localBounds2); } catch (Exception ex2) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[Y4NGZAmmo] Emergency placeholder also failed: " + ex2.Message)); } Bounds localBounds3 = default(Bounds); ((Bounds)(ref localBounds3))..ctor(new Vector3(0f, config.TargetWorldSize * 0.5f + 0.015f - 0.12f, 0f), Vector3.one * config.TargetWorldSize); return new VisualBuildResult(null, localBounds3); } } } private static bool TryBuildBundledGrenadeVisual(GameObject root, AmmoPickupConfig config, out VisualBuildResult result) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: 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_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) result = default(VisualBuildResult); GameObject val = GrenadeContentAssets.CreateVisual(root.transform); if ((Object)(object)val == (Object)null) { return false; } ((Object)val).name = "Y4NGZ_" + config.Id + "_Visual"; Renderer[] componentsInChildren = val.GetComponentsInChildren<Renderer>(true); if (componentsInChildren.Length == 0) { Object.DestroyImmediate((Object)(object)val); return false; } Bounds bounds = componentsInChildren[0].bounds; for (int i = 1; i < componentsInChildren.Length; i++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds); } float num = Mathf.Max(1E-05f, WeaponAssetLoader.LongestAxis(((Bounds)(ref bounds)).size)); Transform transform = val.transform; transform.localScale *= Mathf.Max(0.01f, config.TargetWorldSize) / num; bounds = componentsInChildren[0].bounds; for (int j = 1; j < componentsInChildren.Length; j++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[j].bounds); } Vector3 val2 = root.transform.InverseTransformPoint(((Bounds)(ref bounds)).center); Vector3 val3 = root.transform.InverseTransformPoint(new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y, ((Bounds)(ref bounds)).center.z)); Transform transform2 = val.transform; transform2.localPosition += new Vector3(0f - val2.x, -0.105f - val3.y, 0f - val2.z); bounds = componentsInChildren[0].bounds; for (int k = 1; k < componentsInChildren.Length; k++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[k].bounds); } Vector3 val4 = root.transform.InverseTransformPoint(((Bounds)(ref bounds)).center); Vector3 lossyScale = root.transform.lossyScale; Vector3 val5 = default(Vector3); ((Vector3)(ref val5))..ctor(((Bounds)(ref bounds)).size.x / Mathf.Max(1E-05f, Mathf.Abs(lossyScale.x)), ((Bounds)(ref bounds)).size.y / Mathf.Max(1E-05f, Mathf.Abs(lossyScale.y)), ((Bounds)(ref bounds)).size.z / Mathf.Max(1E-05f, Mathf.Abs(lossyScale.z))); Renderer obj = componentsInChildren[0]; result = new VisualBuildResult((MeshRenderer)(object)((obj is MeshRenderer) ? obj : null), new Bounds(val4, val5)); return true; } private static void BuildScanNode(GameObject root, BoxCollider pickupCollider, AmmoPickupConfig config) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = new GameObject("ScanNode"); val.transform.SetParent(root.transform, false); val.layer = ResolveLayer("ScanNode", 22); val.transform.localPosition = pickupCollider.center + Vector3.up * 0.08f; BoxCollider obj = val.AddComponent<BoxCollider>(); ((Collider)obj).isTrigger = true; obj.size = pickupCollider.size + Vector3.one * 0.08f; ScanNodeProperties obj2 = val.AddComponent<ScanNodeProperties>(); obj2.headerText = config.DisplayName; obj2.subText = "Ammunition"; obj2.maxRange = 24; obj2.minRange = 1; obj2.requiresLineOfSight = false; obj2.nodeType = 0; obj2.creatureScanID = -1; obj2.scrapValue = (config.ScrapMinValue + config.ScrapMaxValue) / 2; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Scan node setup failed for " + config.DisplayName + ": " + ex.Message)); } } } private static Item BuildItemProperties(AmmoPickupConfig config, GameObject prefab) { //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) try { Item obj = ScriptableObject.CreateInstance<Item>(); ((Object)obj).name = "Y4NGZ_" + config.Id + "_Item"; obj.itemName = config.DisplayName; obj.spawnPositionTypes = new List<ItemGroup>(); obj.twoHanded = false; obj.twoHandedAnimation = false; obj.canBeGrabbedBeforeGameStart = true; obj.disallowUtilitySlot = true; obj.weight = 1f; obj.itemSpawnsOnGround = true; obj.isConductiveMetal = true; obj.isScrap = true; obj.creditsWorth = (config.ScrapMinValue + config.ScrapMaxValue) / 2; obj.minValue = config.ScrapMinValue; obj.maxValue = config.ScrapMaxValue; obj.spawnPrefab = prefab; obj.requiresBattery = false; obj.itemIcon = WeaponAssetLoader.LoadIconSprite(config.Id) ?? GetSimpleIcon(); obj.grabAnimationTime = 0.25f; obj.grabSFX = (((Object)(object)_pickupSound != (Object)null) ? _pickupSound : _fallbackGrabSound); obj.dropSFX = _fallbackDropSound; obj.syncGrabFunction = true; obj.syncUseFunction = false; obj.syncDiscardFunction = true; obj.syncInteractLRFunction = false; obj.saveItemVariable = false; obj.toolTips = Array.Empty<string>(); obj.verticalOffset = 0.12f; obj.floorYOffset = 0; obj.allowDroppingAheadOfPlayer = true; obj.restingRotation = Vector3.zero; obj.rotationOffset = Vector3.zero; obj.positionOffset = Vector3.zero; obj.meshOffset = false; obj.canBeInspected = true; ((Object)obj).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)obj); return obj; } catch (Exception innerException) { throw new InvalidOperationException("Could not build Item data for " + config.DisplayName + ".", innerException); } } private static void RefreshFallbackSounds() { try { if ((Object)(object)_fallbackGrabSound != (Object)null && (Object)(object)_fallbackDropSound != (Object)null) { return; } Item[] array = Resources.FindObjectsOfTypeAll<Item>(); foreach (Item val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.spawnPrefab == (Object)null) && !((Object)(object)val.spawnPrefab.GetComponent<AmmoPickupItem>() != (Object)null)) { if ((Object)(object)_fallbackGrabSound == (Object)null && (Object)(object)val.grabSFX != (Object)null) { _fallbackGrabSound = val.grabSFX; } if ((Object)(object)_fallbackDropSound == (Object)null && (Object)(object)val.dropSFX != (Object)null) { _fallbackDropSound = val.dropSFX; } if ((Object)(object)_fallbackGrabSound != (Object)null && (Object)(object)_fallbackDropSound != (Object)null) { break; } } } AudioClip[] array2 = StartOfRound.Instance?.playerGrabSFX; if ((Object)(object)_fallbackGrabSound == (Object)null && array2 != null && array2.Length != 0) { _fallbackGrabSound = array2[0]; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Could not resolve vanilla pickup sounds: " + ex.Message)); } } } private static void ApplySoundsToRegisteredItems() { try { foreach (Item value in Items.Values) { if (!((Object)(object)value == (Object)null)) { if ((Object)(object)_pickupSound != (Object)null) { value.grabSFX = _pickupSound; } else if ((Object)(object)value.grabSFX == (Object)null) { value.grabSFX = _fallbackGrabSound; } if ((Object)(object)value.dropSFX == (Object)null) { value.dropSFX = _fallbackDropSound; } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Could not apply pickup sounds: " + ex.Message)); } } } private static void TryBeginPickupSoundLoad() { try { if (_pickupSoundLoadAttempted || (Object)(object)_pickupSound != (Object)null) { return; } string text = WeaponAssetLoader.AssetDirectory(); string text2 = (string.IsNullOrWhiteSpace(text) ? null : Path.Combine(text, "ammo_pickup.mp3")); if (string.IsNullOrWhiteSpace(text2) || !File.Exists(text2)) { _pickupSoundLoadAttempted = true; if (!_missingPickupSoundWarned) { _missingPickupSoundWarned = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Y4NGZAmmo] ammo_pickup.mp3 is missing; ammo pickups will keep a vanilla/default grab sound."); } } return; } _pickupSoundLoadAttempted = true; if (ArmoryPersistentRunner.Run(LoadPickupSound(text2)) == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[Y4NGZAmmo] Could not start the pickup sound load; keeping defaults."); } } } catch (Exception ex) { _pickupSoundLoadAttempted = true; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[Y4NGZAmmo] Pickup sound load could not start; keeping defaults: " + ex.Message)); } } } private static IEnumerator LoadPickupSound(string path) { UnityWebRequest request = null; UnityWebRequestAsyncOperation val; try { request = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, (AudioType)13); val = request.SendWebRequest(); } catch (Exception ex) { UnityWebRequest obj = request; if (obj != null) { obj.Dispose(); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Pickup sound request failed to start; keeping defaults: " + ex.Message)); } yield break; } yield return val; try { if ((int)request.result != 1) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Y4NGZAmmo] Pickup sound failed to load; keeping defaults: " + request.error)); } yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content == (Object)null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[Y4NGZAmmo] Pickup sound decoded as null; keeping defaults."); } yield break; } ((Object)content).name = "Y4NGZ_AmmoPickup"; _pickupSound = content; ApplySoundsToRegisteredItems(); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[Y4NGZAmmo] Loaded shared ammo pickup grab sound."); } } catch (Exception ex2) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)("[Y4NGZAmmo] Pickup sound processing failed; keeping defaults: " + ex2.Message)); } } finally { UnityWebRequest obj2 = request; if (obj2 != null) { obj2.Dispose(); } } } private static Sprite GetSimpleIcon() { //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_simpleIcon != (Object)null) { return _simpleIcon; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { name = "Y4NGZ_AmmoPickupIconTexture", hideFlags = (HideFlags)61 }; Color val2 = default(Color); ((Color)(ref val2))..ctor(0.86f, 0.53f, 0.16f, 1f); val.SetPixels((Color[])(object)new Color[4] { val2, val2, val2, val2 }); val.Apply(false, true); _simpleIcon = Sprite.Create(val, new Rect(0f, 0f, 2f, 2f), new Vector2(0.5f, 0.5f), 100f); ((Object)_simpleIcon).name = "Y4NGZ_AmmoPickupIcon"; ((Object)_simpleIcon).hideFlags = (HideFlags)61; return _simpleIcon; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Could not create the simple ammo icon: " + ex.Message)); } return null; } } private static void AssignStableNetworkHash(NetworkObject networkObject, string id) { if ((Object)(object)networkObject == (Object)null) { return; } try { uint num; using (MD5 mD = MD5.Create()) { num = BitConverter.ToUInt32(mD.ComputeHash(Encoding.UTF8.GetBytes("com.y4ngz.betterarmory.AmmoPickup." + id)), 0); } FieldInfo? obj = typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(NetworkObject).GetField("<GlobalObjectIdHash>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (obj == null) { throw new MissingFieldException(typeof(NetworkObject).FullName, "GlobalObjectIdHash"); } obj.SetValue(networkObject, num); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Failed to assign the stable network hash for " + id + ": " + ex.Message)); } } } private static int ResolveLayer(string name, int fallback) { try { int num = LayerMask.NameToLayer(name); return (num >= 0) ? num : fallback; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Layer '" + name + "' lookup failed: " + ex.Message)); } return fallback; } } private static void SetTagSafe(GameObject target, string tag) { try { if ((Object)(object)target != (Object)null) { target.tag = tag; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZAmmo] Tag '" + tag + "' was not available for " + ((target != null) ? ((Object)target).name : null) + ": " + ex.Message)); } } } internal static void DebugSpawnAllAtLocalPlayer() { //IL_00d4: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) try { if (!TryRegisterAll()) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Y4NGZAmmo] Dev spawn skipped because ammo pickup registration is incomplete."); } return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[Y4NGZAmmo] Dev spawn requires being the host."); } return; } PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[Y4NGZAmmo] Dev spawn skipped because the local player is unavailable."); } return; } int num = 0; foreach (AmmoPickupConfig item in AmmoPickupCatalog.All()) { try { GameObject val2 = PrefabFor(item.Id); if (!((Object)(object)val2 == (Object)null)) { float num2 = ((float)num - 1.5f) * 0.42f; Vector3 val3 = ((Component)val).transform.position + ((Component)val).transform.forward * 1.35f + ((Component)val).transform.right * num2 + Vector3.up * 0.55f; GameObject val4 = Object.Instantiate<GameObject>(val2, val3, Quaternion.identity); val4.SetActive(true); NetworkObject component = val4.GetComponent<NetworkObject>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val4); throw new InvalidOperationException("Spawned prefab has no NetworkObject."); } component.Spawn(false); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[Y4NGZAmmo] Dev-spawned {item.DisplayName} at {val3}."); } } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)("[Y4NGZAmmo] Dev spawn failed for " + item.DisplayName + ": " + ex.Message)); } } finally { num++; } } } catch (Exception arg) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogWarning((object)$"[Y4NGZAmmo] Dev spawn failed safely: {arg}"); } } } } [Serializable] internal class AmmoReserveData { public int shotgunShells; public int pistolRounds; public int bolts; public int fuel; public int grenades; public bool grantedStartingReserve; public int smallCaliberRounds; public int heavyRounds; public int Get(WeaponAmmoType t) { return t switch { WeaponAmmoType.ShotgunShells => shotgunShells, WeaponAmmoType.Fuel => fuel, WeaponAmmoType.Grenades => grenades, WeaponAmmoType.SmallCaliberRounds => smallCaliberRounds, WeaponAmmoType.HeavyCaliberRounds => heavyRounds, _ => 0, }; } public void Set(WeaponAmmoType t, int v) { v = Mathf.Max(0, v); int num = AmmoReserve.MaximumFor(t); if (num != int.MaxValue) { v = Mathf.Min(num, v); } switch (t) { case WeaponAmmoType.ShotgunShells: shotgunShells = v; break; case WeaponAmmoType.Fuel: fuel = v; break; case WeaponAmmoType.Grenades: grenades = v; break; case WeaponAmmoType.SmallCaliberRounds: smallCaliberRounds = v; break; case WeaponAmmoType.HeavyCaliberRounds: heavyRounds = v; break; } } } internal static class AmmoReserve { internal const int MaxGrenades = 2; private static AmmoReserveData _data; private static string _key; private static readonly HashSet<WeaponAmmoType> _devGranted = new HashSet<WeaponAmmoType>(); private static string SaveDir => Path.Combine(Paths.ConfigPath, "Y4NGZAmmoReserve"); internal static AmmoReserveData Local => Load(); internal static int Get(WeaponAmmoType t) { return Load().Get(t); } internal static int MaximumFor(WeaponAmmoType t) { return ArmoryContentConfig.MaxReserve(t); } internal static void Add(WeaponAmmoType t, int amount) { TryAdd(t, amount, out var _); } internal static bool TryAdd(WeaponAmmoType t, int amount, out int added) { added = 0; if (amount <= 0) { return false; } AmmoReserveData ammoReserveData = Load(); int num = ammoReserveData.Get(t); int num2 = MaximumFor(t); added = ((num2 == int.MaxValue) ? amount : Mathf.Min(amount, Mathf.Max(0, num2 - num))); if (added <= 0) { return false; } ammoReserveData.Set(t, num + added); Save(); return true; } internal static bool TryConsume(WeaponAmmoType t, int amount) { if (amount <= 0) { return true; } AmmoReserveData ammoReserveData = Load(); if (ammoReserveData.Get(t) < amount) { return false; } ammoReserveData.Set(t, ammoReserveData.Get(t) - amount); Save(); return true; } internal static void Set(WeaponAmmoType t, int value) { Load().Set(t, value); Save(); } internal static void Reload() { _data = null; _key = null; _devGranted.Clear(); } internal static bool TryDevGrantOnce(WeaponAmmoType t, int amount) { if (amount <= 0 || _devGranted.Contains(t) || Get(t) > 0) { return false; } _devGranted.Add(t); Set(t, amount); return true; } internal static void ResetCurrentSave() { if (!ArmorySaveBridge.TryGetCurrentExistingOrSlotKey(out var key)) { Reload(); } else { DeleteForKey(key); } } internal static bool DeleteForKey(string key) { bool result = false; try { string path = GetPath(key); if (File.Exists(path)) { File.Delete(path); result = true; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZWeapons] ammo reserve reset failed: " + ex.Message)); } } if (string.Equals(_key, key, StringComparison.OrdinalIgnoreCase)) { Reload(); } return result; } private static AmmoReserveData Load() { if (!ArmorySaveBridge.TryGetCurrentKey(out var key)) { if (_data == null || _key != null) { _data = new AmmoReserveData(); _key = null; } return _data; } if (_data != null && _key == key) { return _data; } _key = key; string path = GetPath(key); if (File.Exists(path)) { try { _data = JsonUtility.FromJson<AmmoReserveData>(File.ReadAllText(path)) ?? new AmmoReserveData(); } catch { _data = new AmmoReserveData(); } } else { _data = new AmmoReserveData(); } _data.Set(WeaponAmmoType.Grenades, _data.Get(WeaponAmmoType.Grenades)); if (MigrateRetiredFamilies(_data) | GrantStartingReserveOnce(_data)) { Save(); } return _data; } private static bool MigrateRetiredFamilies(AmmoReserveData d) { bool result = false; if (d.pistolRounds > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)($"[Y4NGZWeapons] Ammo migration (#148): folding {d.pistolRounds} pistol round(s) " + "into the small-caliber reserve; the revolver now shares that family.")); } d.smallCaliberRounds += d.pistolRounds; result = true; } if (d.pistolRounds != 0) { d.pistolRounds = 0; result = true; } if (d.bolts != 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)($"[Y4NGZWeapons] Ammo migration (#150): dropping {d.bolts} crossbow bolt(s); " + "the crossbow was removed and no weapon fires them.")); } d.bolts = 0; result = true; } return result; } private static bool GrantStartingReserveOnce(AmmoReserveData d) { if (d == null || d.grantedStartingReserve) { return false; } d.grantedStartingReserve = true; List<string> list = new List<string>(); foreach (AmmoPickupConfig item in AmmoPickupCatalog.All()) { if (item != null) { int num = ArmoryContentConfig.StartingReserve(item.AmmoType); if (num > 0) { d.Set(item.AmmoType, Math.Max(d.Get(item.AmmoType), num)); list.Add($"{d.Get(item.AmmoType)} {ArmoryContentConfig.FamilyName(item.AmmoType)}"); } } } if (list.Count > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[Y4NGZWeapons] Starting ammo reserve granted for this save: " + string.Join(", ", list.ToArray()) + ".")); } } return true; } private static void Save() { if (_data == null || string.IsNullOrWhiteSpace(_key)) { return; } try { if (!Directory.Exists(SaveDir)) { Directory.CreateDirectory(SaveDir); } File.WriteAllText(GetPath(_key), JsonUtility.ToJson((object)_data, true)); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZWeapons] ammo reserve save failed: " + ex.Message)); } } } private static string GetPath(string key) { return Path.Combine(SaveDir, Sanitize(key) + ".json"); } private static string Sanitize(string raw) { char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); char[] array = raw.ToCharArray(); for (int i = 0; i < array.Length; i++) { if (Array.IndexOf(invalidFileNameChars, array[i]) >= 0) { array[i] = '_'; } } return new string(array); } } public static class ArmoryAmmoReserveApi { public const int ProtocolVersion = 1; private static readonly (WeaponAmmoType Type, string Label)[] Families = new(WeaponAmmoType, string)[4] { (WeaponAmmoType.ShotgunShells, "SHOTGUN SHELLS"), (WeaponAmmoType.Fuel, "FLAMETHROWER PROPELLANT"), (WeaponAmmoType.SmallCaliberRounds, "SMALL CALIBER ROUNDS"), (WeaponAmmoType.HeavyCaliberRounds, "HEAVY CALIBER ROUNDS") }; private static readonly string[] NoIds = new string[0]; public static string[] GetAmmoFamilyIds() { try { List<string> list = new List<string>(Families.Length); for (int i = 0; i < Families.Length; i++) { list.Add(Families[i].Type.ToString()); } return list.ToArray(); } catch (Exception ex) { Warn("family enumeration", ex); return NoIds; } } public static string GetAmmoFamilyLabel(string familyId) { if (!TryResolve(familyId, out var index)) { return familyId; } return Families[index].Label; } public static int GetAmmoFamilyReserve(string familyId) { if (!TryResolve(familyId, out var index)) { return 0; } try { return AmmoReserve.Get(Families[index].Type); } catch (Exception ex) { Warn("reserve read", ex); return 0; } } public static int GetAmmoFamilyCapacity(string familyId) { if (!TryResolve(familyId, out var index)) { return 0; } try { return AmmoReserve.MaximumFor(Families[index].Type); } catch (Exception ex) { Warn("capacity read", ex); return 0; } } private static bool TryResolve(string familyId, out int index) { index = -1; if (string.IsNullOrWhiteSpace(familyId)) { return false; } for (int i = 0; i < Families.Length; i++) { if (string.Equals(Families[i].Type.ToString(), familyId, StringComparison.Ordinal)) { index = i; return true; } } return false; } private static void Warn(string what, Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[ArmoryAmmoReserveApi] " + what + " failed safely: " + ex.Message)); } } } internal sealed class WeaponContentSettings { internal ConfigEntry<bool> Enabled; internal ConfigEntry<bool> PurchasableInShop; internal ConfigEntry<int> ShopPrice; internal ConfigEntry<bool> SpawnsInInteriors; internal ConfigEntry<int> InteriorSpawnRarity; internal ConfigEntry<int> ScrapValue; internal ConfigEntry<int> EnemyDamageNear; internal ConfigEntry<int> EnemyDamageFar; internal ConfigEntry<int> PlayerDamageNear; internal ConfigEntry<int> PlayerDamageFar; internal ConfigEntry<int> MagazineSize; internal ConfigEntry<float> FireCadenceMultiplier; internal ConfigEntry<float> SpreadMultiplier; internal ConfigEntry<float> Weight; internal ConfigEntry<bool> TwoHanded; internal ConfigEntry<bool> TwoHandedAnimation; internal ConfigEntry<int> FlameEnemyDamagePerTick; internal ConfigEntry<float> FlameDamageTickSeconds; } internal sealed class AmmoFamilySettings { internal ConfigEntry<bool> PurchasableInShop; internal ConfigEntry<int> ShopPrice; internal ConfigEntry<int> RoundsPerBox; internal ConfigEntry<bool> SpawnsInInteriors; internal ConfigEntry<int> InteriorSpawnRarity; internal ConfigEntry<int> MaxReservePerPlayer; internal ConfigEntry<int> StartingReserve; internal ConfigEntry<bool> VanillaShotgunSharesReserve; } internal static class ArmoryContentConfig { internal const string ShopSection = "Shop"; internal const int DefaultWeaponInteriorRarity = 8; private static readonly Dictionary<string, WeaponContentSettings> Weapons = new Dictionary<string, WeaponContentSettings>(StringComparer.Ordinal); private static readonly Dictionary<WeaponAmmoType, AmmoFamilySettings> AmmoFamilies = new Dictionary<WeaponAmmoType, AmmoFamilySettings>(); private static bool _applied; internal static ConfigEntry<float> ShopPriceMultiplier { get; private set; } internal static bool VanillaShotgunSharesShellReserve { get { AmmoFamilySettings ammoFamilySettings = ForFamily(WeaponAmmoType.ShotgunShells); if (ammoFamilySettings?.VanillaShotgunSharesReserve != null) { return ammoFamilySettings.VanillaShotgunSharesReserve.Value; } return true; } } internal static string FamilyName(WeaponAmmoType type) { return type switch { WeaponAmmoType.ShotgunShells => "Shotgun Shells", WeaponAmmoType.SmallCaliberRounds => "Small Caliber Rounds", WeaponAmmoType.HeavyCaliberRounds => "Heavy Caliber Rounds", WeaponAmmoType.Fuel => "Fuel", WeaponAmmoType.Grenades => "Grenades", _ => type.ToString(), }; } private static int DefaultShopPrice(WeaponAmmoType type) { return type switch { WeaponAmmoType.ShotgunShells => 20, WeaponAmmoType.SmallCaliberRounds => 20, WeaponAmmoType.HeavyCaliberRounds => 30, WeaponAmmoType.Fuel => 25, WeaponAmmoType.Grenades => 40, _ => 20, }; } private static int DefaultMaxReserve(WeaponAmmoType type) { if (type != WeaponAmmoType.Grenades) { return 0; } return 2; } internal static string WeaponSection(WeaponConfig config) { return "Weapon: " + config.DisplayName; } internal static string AmmoSection(WeaponAmmoType type) { return "Ammo: " + FamilyName(type); } internal static void Bind(ConfigFile config) { if (config == null) { return; } ShopPriceMultiplier = config.Bind<float>("Shop", "Shop Price Multiplier", 1f, "Multiplier applied to every Y4NGZ weapon and ammunition shop price before it is shown in the terminal. 1.0 leaves each 'Shop Price' exactly as configured. Prices are re-read on each terminal redraw, so this takes effect without a restart. Terminal purchases are host-authoritative, but the printed price comes from the reading player's config - keep this identical across a lobby."); Weapons.Clear(); foreach (WeaponConfig item in WeaponCatalog.AllRegistered()) { BindWeapon(config, item); } AmmoFamilies.Clear(); foreach (AmmoPickupConfig item2 in AmmoPickupCatalog.All()) { BindAmmoFamily(config, item2); } } private static void BindWeapon(ConfigFile config, WeaponConfig weapon) { if (weapon != null && !Weapons.ContainsKey(weapon.Id)) { string text = WeaponSection(weapon); WeaponContentSettings weaponContentSettings = new WeaponContentSettings { Enabled = config.Bind<bool>(text, "Enabled", true, "Register this weapon at all. When false it is never built, never registered as an item, never appears in the shop or in interiors, and is dropped from the LUCKY-8 prize pool. Existing saves holding one will find the item missing, so prefer the shop and interior toggles below for balance changes. Registration is per machine: every player in a lobby must agree."), PurchasableInShop = config.Bind<bool>(text, "Purchasable In Shop", true, "List this weapon in the terminal store. ON by default: a buyable armoury is what this mod is for. Turn it off to keep this weapon out of the store without disabling it. Purchases are host-authoritative, but the printed listing comes from the reading player's config."), ShopPrice = config.Bind<int>(text, "Shop Price", weapon.CreditsWorth, "Terminal store price in credits, before 'Shop / Shop Price Multiplier'. Re-read on each terminal redraw."), SpawnsInInteriors = config.Bind<bool>(text, "Spawns In Interiors", false, "Let this weapon spawn as interior loot. Off by default: weapons have never spawned in interiors and turning one on is a real balance change."), InteriorSpawnRarity = config.Bind<int>(text, "Interior Spawn Rarity", 8, "Spawn weight in every moon's scrap pool, used only while 'Spawns In Interiors' is on. Higher is more common; compare against the ammunition families' 18 and vanilla scrap, which runs from about 5 to 100. Zero also removes the weapon from the pool. Weights are recomputed every round, so this needs no restart."), ScrapValue = config.Bind<int>(text, "Scrap Value", weapon.CreditsWorth, "Credits this weapon is worth when sold as scrap. Applied when the item is built, so it takes a restart, and it is read on the host when a spawned instance rolls its value."), Weight = config.Bind<float>(text, "Weight", weapon.Weight, "Carried weight in Lethal Company's units, where 1.0 is weightless and each 0.01 above it is one pound in the HUD. Applied when the item is built, so it takes a restart."), TwoHanded = config.Bind<bool>(text, "Two Handed", weapon.TwoHanded, "Whether carrying this weapon occupies both hands, which blocks the sprint, ladder and second-item interactions a one-handed item allows. Applied when the item is built, so it takes a restart. Evaluated per machine: every player in a lobby must use the same value."), TwoHandedAnimation = config.Bind<bool>(text, "Two Handed Animation", weapon.TwoHandedAnimation, "Whether the player's third-person carry pose uses the two-handed hold. Applied when the item is built, so it takes a restart, and every player in a lobby must use the same value. CAUTION: this is a pose, not a rule - setting it against what the weapon's authored hold was built for makes the model sit wrong in other players' hands even though it looks correct in your own.") }; if (weapon.Native) { weaponContentSettings.EnemyDamageNear = config.Bind<int>(text, "Enemy Damage Near", weapon.EnemyDamageClose, "Damage dealt to CREATURES at point-blank range, falling off linearly to 'Enemy Damage Far' at the weapon's maximum range. This is Lethal Company's own enemy damage scale, NOT player health: most creatures have between 1 and 10 hit points (a Bracken has 5, a Hoarding Bug 3, a Thumper 6) and a shovel hits for 1, so 1 to 3 is the normal range for a weapon here and small changes are large balance changes. On a weapon that fires a pattern this is PER PELLET - the Heavy Shotgun's ten pellets each apply this value at their own distance. Zero means the weapon does not damage creatures at all. HOST-AUTHORITATIVE: enemy damage is resolved on the server, so the host's value is the one that lands."); weaponContentSettings.EnemyDamageFar = config.Bind<int>(text, "Enemy Damage Far", weapon.EnemyDamageFar, "Damage dealt to CREATURES at maximum range, on the same 1-to-10 enemy scale as 'Enemy Damage Near' and per pellet in the same way. HOST-AUTHORITATIVE."); weaponContentSettings.PlayerDamageNear = config.Bind<int>(text, "Player Damage Near", weapon.PlayerDamageClose, "Friendly-fire damage dealt to PLAYERS at point-blank range, falling off linearly to 'Player Damage Far' at the weapon's maximum range. This is player health, where a player has 100 - so it is a completely different scale from the 'Enemy Damage' pair above and must not be tuned against it. Applied once per shot rather than per pellet. Resolved on the victim's own machine."); weaponContentSettings.PlayerDamageFar = config.Bind<int>(text, "Player Damage Far", weapon.PlayerDamageFar, "Friendly-fire damage dealt to PLAYERS at maximum range, out of 100 player health. Same scale as 'Player Damage Near'."); weaponContentSettings.MagazineSize = config.Bind<int>(text, "Magazine Size", weapon.MagazineSize, "Rounds the weapon holds when full. Read locally by every player's reload, HUD and save/load path, so this MUST MATCH across a lobby - a mismatch makes two players disagree about when the gun is empty."); weaponContentSettings.FireCadenceMultiplier = config.Bind<float>(text, "Fire Cadence Multiplier", 1f, "Multiplier on the weapon's rate of fire. 2.0 fires twice as fast, 0.5 half as fast. Applied to the authored rounds-per-minute (or, on a weapon that has none, to its shot cooldown). Evaluated locally: keep it identical across a lobby."); weaponContentSettings.SpreadMultiplier = config.Bind<float>(text, "Spread Multiplier", 1f, "Multiplier on the weapon's base spread cone, before the movement, stance, shot and recoil bloom terms. Below 1 tightens the pattern. The crosshair is sized from the same number, so it follows automatically. Pellets are resolved on the shooter's machine."); } else if (weapon.Id == "grenade_launcher") { weaponContentSettings.MagazineSize = config.Bind<int>(text, "Magazine Size", weapon.MagazineSize, "Grenades the launcher holds when full. Read locally by the reload path, so this MUST MATCH across a lobby."); } if (weapon.Id == "flame_thrower") { weaponContentSettings.FlameEnemyDamagePerTick = config.Bind<int>(text, "Enemy Damage Per Tick", 1, "Direct damage dealt to CREATURES caught in the stream on each damage tick. On Lethal Company's enemy scale, where most creatures have between 1 and 10 hit points and a shovel hits for 1. This is on TOP of the burning effect the stream applies, which ticks for 1 on its own - so the shipped 1 per second here is about 2 health per second in total. HOST-AUTHORITATIVE: the stream is resolved on the server."); weaponContentSettings.FlameDamageTickSeconds = config.Bind<float>(text, "Damage Tick Interval Seconds", 1f, "Seconds between damage ticks while a creature is held in the stream. Lower is faster and therefore more damage per second; 1.0 is the shipped value. Clamped to between 0.1 and 10. HOST-AUTHORITATIVE, same as 'Enemy Damage Per Tick'."); } Weapons[weapon.Id] = weaponContentSettings; } } private static void BindAmmoFamily(ConfigFile config, AmmoPickupConfig pickup) { if (pickup != null && !AmmoFamilies.ContainsKey(pickup.AmmoType)) { string text = AmmoSection(pickup.AmmoType); AmmoFamilySettings ammoFamilySettings = new AmmoFamilySettings { PurchasableInShop = config.Bind<bool>(text, "Purchasable In Shop", true, "List " + pickup.DisplayName + " in the terminal store. ON by default: buyable weapons are only usable alongside buyable ammunition. Interior spawning stays on as well, so ammunition is both found and bought."), ShopPrice = config.Bind<int>(text, "Shop Price", DefaultShopPrice(pickup.AmmoType), "Terminal store price in credits for one " + pickup.DisplayName + " pickup, before 'Shop / Shop Price Multiplier'. One purchase delivers one pickup, which grants 'Rounds Per Box' rounds when collected."), RoundsPerBox = config.Bind<int>(text, "Rounds Per Box", pickup.DefaultRoundsGranted, "Reserve rounds granted when one " + pickup.DisplayName + " pickup is collected. Each player consumes their own pickup locally."), SpawnsInInteriors = config.Bind<bool>(text, "Spawns In Interiors", true, "Let " + pickup.DisplayName + " spawn as interior loot. On by default - this is how ammunition has always been distributed."), InteriorSpawnRarity = config.Bind<int>(text, "Interior Spawn Rarity", pickup.DefaultScrapRarity, "Spawn weight in every moon's scrap pool, used only while 'Spawns In Interiors' is on. Higher is more common. Zero also removes the pickup from the pool. Weights are recomputed every round, so this needs no restart."), MaxReservePerPlayer = config.Bind<int>(text, "Max Reserve Per Player", DefaultMaxReserve(pickup.AmmoType), "Hard cap on how much of this family one player may carry in reserve. 0 means unlimited. Enforced on pickup, on purchase delivery and on the reserve file itself, so lowering it clamps an over-full reserve on the next save. The reserve is per player and per save, so this is a local rule, not a host one."), StartingReserve = config.Bind<int>(text, "Starting Reserve", 0, "Reserve rounds of this family granted ONCE per save file, the first time that save's reserve is loaded. Raising it later does not re-grant; a save that has already been granted is marked as such in its reserve file. Clamped by 'Max Reserve Per Player'.") }; if (pickup.AmmoType == WeaponAmmoType.ShotgunShells) { ammoFamilySettings.VanillaShotgunSharesReserve = config.Bind<bool>(text, "Vanilla Shotgun Shares Shell Reserve", true, "Let Lethal Company's own shotgun reload from this reserve once it has run out of hotbar ammo. Hotbar shells always take priority; this is only the fallback. Turn it off to keep the shell reserve exclusively for Y4NGZ weapons."); } AmmoFamilies[pickup.AmmoType] = ammoFamilySettings; } } internal static void ApplyToCatalogs() { if (_applied) { return; } _applied = true; foreach (WeaponConfig item in WeaponCatalog.AllRegistered()) { if (item == null || !Weapons.TryGetValue(item.Id, out var value)) { continue; } try { if (value.ScrapValue != null) { item.CreditsWorth = Mathf.Max(0, value.ScrapValue.Value); } if (value.EnemyDamageNear != null) { item.EnemyDamageClose = Mathf.Max(0, value.EnemyDamageNear.Value); } if (value.EnemyDamageFar != null) { item.EnemyDamageFar = Mathf.Max(0, value.EnemyDamageFar.Value); } if (value.PlayerDamageNear != null) { item.PlayerDamageClose = Mathf.Max(0, value.PlayerDamageNear.Value); } if (value.PlayerDamageFar != null) { item.PlayerDamageFar = Mathf.Max(0, value.PlayerDamageFar.Value); } if (value.FlameEnemyDamagePerTick != null) { FlamethrowerStreamDriver.DirectDamage = Mathf.Max(0, value.FlameEnemyDamagePerTick.Value); } if (value.FlameDamageTickSeconds != null) { FlamethrowerStreamDriver.DamageInterval = Mathf.Clamp(value.FlameDamageTickSeconds.Value, 0.1f, 10f); } if (value.MagazineSize != null) { item.MagazineSize = Mathf.Max(1, value.MagazineSize.Value); } if (value.Weight != null) { item.Weight = Mathf.Max(1f, value.Weight.Value); } if (value.TwoHanded != null) { item.TwoHanded = value.TwoHanded.Value; } if (value.TwoHandedAnimation != null) { item.TwoHandedAnimation = value.TwoHandedAnimation.Value; } if (value.SpreadMultiplier != null) { item.SpreadDegrees = Mathf.Max(0f, item.SpreadDegrees * Mathf.Max(0f, value.SpreadMultiplier.Value)); } if (value.FireCadenceMultiplier != null) { float num = Mathf.Max(0.01f, value.FireCadenceMultiplier.Value); if (item.RoundsPerMinute > 0f) { item.RoundsPerMinute *= num; } else if (item.FireCooldownSeconds > 0f) { item.FireCooldownSeconds /= num; } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZArmoryConfig] Could not apply config to " + item.DisplayName + "; its authored values stand: " + ex.Message)); } } } } private static WeaponContentSettings ForWeapon(string weaponId) { if (weaponId == null || !Weapons.TryGetValue(weaponId, out var value)) { return null; } return value; } private static AmmoFamilySettings ForFamily(WeaponAmmoType type) { if (!AmmoFamilies.TryGetValue(type, out var value)) { return null; } return value; } internal static bool IsWeaponEnabled(string weaponId) { WeaponContentSettings weaponContentSettings = ForWeapon(weaponId); if (weaponContentSettings?.Enabled != null) { return weaponContentSettings.Enabled.Value; } return true; } internal static bool IsWeaponPurchasable(string weaponId) { WeaponContentSettings weaponContentSettings = ForWeapon(weaponId); if (weaponContentSettings?.PurchasableInShop != null) { return weaponContentSettings.PurchasableInShop.Value; } return false; } internal static int WeaponShopPrice(string weaponId, int fallback) { WeaponContentSettings weaponContentSettings = ForWeapon(weaponId); return ScalePrice((weaponContentSettings?.ShopPrice != null) ? weaponContentSettings.ShopPrice.Value : fallback); } internal static int WeaponInteriorWeight(string weaponId) { WeaponContentSettings weaponContentSettings = ForWeapon(weaponId); if (weaponContentSettings?.SpawnsInInteriors == null || !weaponContentSettings.SpawnsInInteriors.Value) { return 0; } if (weaponContentSettings.InteriorSpawnRarity == null) { return 0; } return Math.Max(0, weaponContentSettings.InteriorSpawnRarity.Value); } internal static bool IsAmmoPurchasable(WeaponAmmoType type) { AmmoFamilySettings ammoFamilySettings = ForFamily(type); if (ammoFamilySettings?.PurchasableInShop != null) { return ammoFamilySettings.PurchasableInShop.Value; } return false; } internal static int AmmoShopPrice(WeaponAmmoType type) { AmmoFamilySettings ammoFamilySettings = ForFamily(type); return ScalePrice((ammoFamilySettings?.ShopPrice != null) ? ammoFamilySettings.ShopPrice.Value : DefaultShopPrice(type)); } internal static int AmmoInteriorWeight(WeaponAmmoType type, int fallbackRarity) { AmmoFamilySettings ammoFamilySettings = ForFamily(type); if (ammoFamilySettings == null) { return Math.Max(0, fallbackRarity); } if (ammoFamilySettings.SpawnsInInteriors != null && !ammoFamilySettings.SpawnsInInteriors.Value) { return 0; } if (ammoFamilySettings.InteriorSpawnRarity == null) { return Math.Max(0, fallbackRarity); } return Math.Max(0, ammoFamilySettings.InteriorSpawnRarity.Value); } internal static int RoundsPerBox(WeaponAmmoType type, int fallback) { AmmoFamilySettings ammoFamilySettings = ForFamily(type); if (ammoFamilySettings?.RoundsPerBox == null) { return Math.Max(0, fallback); } return Math.Max(0, ammoFamilySettings.RoundsPerBox.Value); } internal static int MaxReserve(WeaponAmmoType type) { AmmoFamilySettings ammoFamilySettings = ForFamily(type); int num = ((ammoFamilySettings?.MaxReservePerPlayer != null) ? ammoFamilySettings.MaxReservePerPlayer.Value : DefaultMaxReserve(type)); if (num > 0) { return num; } return int.MaxValue; } internal static int StartingReserve(WeaponAmmoType type) { AmmoFamilySettings ammoFamilySettings = ForFamily(type); if (ammoFamilySettings?.StartingReserve == null) { return 0; } return Math.Max(0, ammoFamilySettings.StartingReserve.Value); } private static int ScalePrice(int raw) { float num = ((ShopPriceMultiplier != null) ? ShopPriceMultiplier.Value : 1f); return Math.Max(0, Mathf.RoundToInt((float)Math.Max(0, raw) * Mathf.Max(0f, num))); } } public static class ArmoryPresentationApi { public const int ProtocolVersion = 1; public static void StopPresentationForHeldItemStow(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return; } try { WeaponFirstPersonAnimation.StopForHeldItemStow(player); WeaponThirdPersonAnimation.StopForHeldItemStow(player); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[ArmoryPresentationApi] stow presentation stop failed safely: " + ex.Message)); } } } } public static class ArmoryPrizeApi { public const int ProtocolVersion = 1; private static readonly string[] NoIds = new string[0]; public static string[] GetWeaponPrizeIds() { try { WeaponRuntimeRegistry.TryRegisterAll(); List<string> list = new List<string>(); foreach (WeaponConfig item in WeaponCatalog.AllRegistered()) { if (item != null && !((Object)(object)WeaponRuntimeRegistry.ItemFor(item.Id) == (Object)null) && !((Object)(object)WeaponRuntimeRegistry.PrefabFor(item.Id) == (Object)null)) { list.Add(item.Id); } } return list.ToArray(); } catch (Exception ex) { Warn("weapon prize enumeration", ex); return NoIds; } } public static string GetWeaponDisplayName(string weaponId) { try { WeaponConfig weaponConfig = WeaponCatalog.ById(weaponId); return (weaponConfig != null && !string.IsNullOrWhiteSpace(weaponConfig.DisplayName)) ? weaponConfig.DisplayName : weaponId; } catch (Exception ex) { Warn("weapon display name", ex); return weaponId; } } public static GameObject GetWeaponPrizePrefab(string weaponId) { try { WeaponRuntimeRegistry.TryRegisterAll(); return WeaponRuntimeRegistry.PrefabFor(weaponId); } catch (Exception ex) { Warn("weapon prize prefab", ex); return null; } } public static Item GetWeaponItem(string weaponId) { try { WeaponRuntimeRegistry.TryRegisterAll(); return WeaponRuntimeRegistry.ItemFor(weaponId); } catch (Exception ex) { Warn("weapon item", ex); return null; } } public static void InitializeWeaponPrize(GameObject instance, string weaponId) { if ((Object)(object)instance == (Object)null) { return; } try { WeaponConfig weaponConfig = WeaponCatalog.ById(weaponId); if (weaponConfig != null) { ShotgunItem component = instance.GetComponent<ShotgunItem>(); if ((Object)(object)component != (Object)null) { component.shellsLoaded = weaponConfig.MagazineSize; } Y4NGZWeaponBase component2 = instance.GetComponent<Y4NGZWeaponBase>(); if ((Object)(object)component2 != (Object)null) { component2.Magazine = weaponConfig.MagazineSize; } } } catch (Exception ex) { Warn("weapon prize initialisation", ex); } } public static string[] GetWeaponFingerprintTokens() { try { List<string> list = new List<string>(); foreach (WeaponConfig item in WeaponCatalog.AllRegistered()) { if (item != null) { list.Add(item.Id); list.Add(((Object)(object)WeaponRuntimeRegistry.ItemFor(item.Id) != (Object)null) ? "item" : "no-item"); list.Add(((Object)(object)WeaponRuntimeRegistry.PrefabFor(item.Id) != (Object)null) ? "prefab" : "no-prefab"); } } return list.ToArray(); } catch (Exception ex) { Warn("weapon fingerprint", ex); return NoIds; } } public static string[] GetAmmoPrizeIds() { try { AmmoPickupRegistry.TryRegisterAll(); List<string> list = new List<string>(); foreach (AmmoPickupConfig item in AmmoPickupCatalog.All()) { if (item != null && !((Object)(object)AmmoPickupRegistry.ItemFor(item.Id) == (Object)null) && !((Object)(object)AmmoPickupRegistry.PrefabFor(item.Id) == (Object)null)) { list.Add(item.Id); } } return list.ToArray(); } catch (Exception ex) { Warn("ammo prize enumeration", ex); return NoIds; } } public static string GetAmmoDisplayName(string pickupId) { try { AmmoPickupConfig ammoPickupConfig = AmmoPickupCatalog.ById(pickupId); return (ammoPickupConfig != null && !string.IsNullOrWhiteSpace(ammoPickupConfig.DisplayName)) ? ammoPickupConfig.DisplayName : pickupId; } catch (Exception ex) { Warn("ammo display name", ex); return pickupId; } } public static GameObject GetAmmoPrizePrefab(string pickupId) { try { AmmoPickupRegistry.TryRegisterAll(); return AmmoPickupRegistry.PrefabFor(pickupId); } catch (Exception ex) { Warn("ammo prize prefab", ex); return null; } } public static Item GetAmmoItem(string pickupId) { try { AmmoPickupRegistry.TryRegisterAll(); return AmmoPickupRegistry.ItemFor(pickupId); } catch (Exception ex) { Warn("ammo item", ex); return null; } } private static void Warn(string what, Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[ArmoryPrizeApi] " + what + " failed safely: " + ex.Message)); } } } internal static class BulletHoleDecalModel { internal const float StackedMinOffset = 0.002f; internal const float StackedMaxOffset = 0.02f; internal const float StackedStepDistance = 0.001f; internal const int StackedSlotCount = 19; internal static float OffsetForStackSlot(int slot) { int num = slot % 19; if (num < 0) { num += 19; } float num2 = 0.002f + (float)num * 0.001f; if (!(num2 <= 0.02f)) { return 0.02f; } return num2; } internal static float OpacityForRemaining(float remainingSeconds, float fadeSeconds) { if (remainingSeconds <= 0f) { return 0f; } if (fadeSeconds <= 0f || remainingSeconds >= fadeSeconds) { return 1f; } return remainingSeconds / fadeSeconds; } } internal static class DawnLibCompat { private sealed class FuncWeighted : IWeighted { private readonly Func<int> _weight; internal FuncWeighted(Func<int> weight) { _weight = weight; } public int GetWeight() { try { return Math.Max(0, (_weight != null) ? _weight() : 0); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZWeapons] Spawn weight read failed; treating as 0: " + ex.Message)); } return 0; } } } private sealed class ConfigGatedPurchasePredicate : ITerminalPurchasePredicate { private readonly Func<bool> _enabled; internal ConfigGatedPurchasePredicate(Func<bool> enabled) { _enabled = enabled; } public TerminalPurchaseResult CanPurchase() { try { if (_enabled == null || _enabled()) { return (TerminalPurchaseResult)(object)TerminalPurchaseResult.Success(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZWeapons] Shop predicate read failed; hiding the entry: " + ex.Message)); } } return (TerminalPurchaseResult)(object)TerminalPurchaseResult.Hidden(); } } internal const string ItemNamespace = "y4ngz_upgrades"; [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static void RegisterNetworkPrefab(GameObject prefab) { DawnLib.RegisterNetworkPrefab(prefab); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static void FixMixerGroups(GameObject prefab) { DawnLib.FixMixerGroups(prefab); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static void RegisterItem(string itemKey, Item item, Func<bool> shopEnabled = null, Func<int> shopPrice = null, string shopDescription = null, Func<int> interiorWeight = null) { if ((Object)(object)item == (Object)null || string.IsNullOrWhiteSpace(itemKey)) { return; } DawnLib.DefineItem(NamespacedKey<DawnItemInfo>.From("y4ngz_upgrades", itemKey), item, (Action<ItemInfoBuilder>)delegate(ItemInfoBuilder builder) { if (shopPrice != null) { string itemName = item.itemName; builder.DefineShop((Action<ShopBuilder>)delegate(ShopBuilder shop) { shop.OverrideCost((IProvider<int>)(object)new FuncProvider<int>(shopPrice)); shop.SetPurchasePredicate((ITerminalPurchasePredicate)(object)new ConfigGatedPurchasePredicate(shopEnabled)); TerminalNode val = BuildInfoNode(itemName, shopDescription); if ((Object)(object)val != (Object)null) { shop.OverrideInfoNode(val); } }); } if (interiorWeight != null) { builder.DefineScrap((Action<ScrapBuilder>)delegate(ScrapBuilder scrap) { scrap.SetWeights((Action<WeightTableBuilder<DawnMoonInfo, SpawnWeightContext>>)delegate(WeightTableBuilder<DawnMoonInfo, SpawnWeightContext> w) { w.SetGlobalWeight((IWeighted)(object)new FuncWeighted(interiorWeight)); }); }); } }); } private static TerminalNode BuildInfoNode(string itemName, string description) { if (string.IsNullOrWhiteSpace(description)) { return null; } try { TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>(); ((Object)obj).name = "Y4NGZ_" + (itemName ?? "item") + "_InfoNode"; obj.displayText = description.TrimEnd() + "\n\n"; obj.clearPreviousText = true; obj.maxCharactersToType = 35; ((Object)obj).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)obj); return obj; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Y4NGZWeapons] Could not build the store info node for '" + itemName + "'; DawnLib's placeholder text stands: " + ex.Message)); } return null; } } } internal static class FlamethrowerContentAssets { internal const string BundleFileName = "y4ngz-interactions-flamethrower-iktargets.animationbundle"; internal const string BundleInternalName = "y4ngz-interactions-flamethrower-iktargets.animationbundle"; internal const string PropPrefabName = "Y4NGZ_FPSFlamethrowerProp"; internal const string MuzzleName = "Y4NGZ_FlamethrowerMuzzle"; private static AssetBundle _bundle; private static GameObject _propPrefab; private static Material _runtimeWeaponMaterial; private static GameObject _vanillaStreamPrefab; private static AudioClip _vanillaLoopClip; private static float _nextVanillaAssetSearchTime; private static bool _missingParticleLogged; private static bool _missingAudioLogged; internal static GameObject LoadPropPrefab() { if ((Object)(object)_propPrefab != (Object)null) { return _propPrefab; } AssetBundle orLoadBundle = GetOrLoadBundle(); if ((Object)(object)orLoadBundle == (Object)null) { return null; } _propPrefab = orLoadBundle.LoadAsset<GameObject>("Y4NGZ_FPSFlamethrowerProp"); if ((Object)(object)_propPrefab == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"[Flamethrower] consumer prop missing from y4ngz-interactions-flamethrower-iktargets.animationbundle."); } } return _propPrefab; } internal static Material LoadRuntimeWeaponMaterial() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_00a5: Expected O, but got Unknown //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_runtimeWeaponMaterial != (Object)null) { return _runtimeWeaponMaterial; } GameObject val = LoadPropPrefab(); Renderer val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren<Renderer>(true) : null); Material material = (((Object)(object)val2 != (Object)null) ? val2.sharedMaterial : null); Shader val3 = Shader.Find("HDRP/Lit") ?? Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard"); if ((Object)(object)val3 == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"[Flamethrower] no supported runtime shader was found for the weapon model."); } return null; } _runtimeWeaponMaterial = new Material(val3) { name = "Y4NGZ_Flamethrower_RuntimeMaterial", hideFlags = (HideFlags)61 }; Texture texture = GetTexture(material, "_BaseColorMap", "_MainTex"); Texture texture2 = GetTexture(material, "_NormalMap", "_BumpMap"); Texture texture3 = GetTexture(material, "_MaskMap", "_MetallicGlossMap"); SetTexture(_runtimeWeaponMaterial, texture, "_BaseColorMap", "_MainTex"); SetTexture(_runtimeWeaponMaterial, texture2, "_NormalMap", "_BumpMap"); SetTexture(_runtimeWeaponMaterial, texture3, "_MaskMap", "_MetallicGlossMap"); if ((Object)(object)texture2 != (Object)null) { _runtimeWeaponMaterial.EnableKeyword("_NORMALMAP"); } if (_runtimeWeaponMaterial.HasProperty("_BaseColor")) { _runtimeWeaponMaterial.SetColor("_BaseColor", Color.white); } if (_runtimeWeaponMaterial.HasProperty("_Color")) { _runtimeWeaponMaterial.SetColor("_Color", Color.white); } if (_runtimeWeaponMaterial.HasProperty("_Metallic")) { _runtimeWeaponMaterial.SetFloat("_Metallic", 0.2f); } if (_runtimeWeaponMaterial.HasProperty("_Smoothness")) { _runtimeWeaponMaterial.SetFloat("_Smoothness", 0.35f); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[Flamethrower] built runtime weapon material with shader '" + ((Object)val3).name + "' (baseColor=" + ((Object)(object)texture != (Object)null) + ", normal=" + ((Object)(object)texture2 != (Object)null) + ", mask=" + ((Object)(object)texture3 != (Object)null) + ").")); } return _runtimeWeaponMaterial; } internal static v