Decompiled source of StraftatReloadBalance v1.0.0

StraftatReloadBalance.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.InputSystem;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("StraftatReloadBalance")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StraftatReloadBalance")]
[assembly: AssemblyTitle("StraftatReloadBalance")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 StraftatReloadBalance
{
	internal static class HandPatches
	{
		private static MethodInfo _rightHandPickup;

		private static bool Active
		{
			get
			{
				if (ReloadBalancePlugin.Enabled.Value)
				{
					return ReloadBalancePlugin.NoDualWield.Value;
				}
				return false;
			}
		}

		public static void Apply(Harmony harmony)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			_rightHandPickup = AccessTools.Method(typeof(PlayerPickup), "RightHandPickup", (Type[])null, (Type[])null);
			MethodInfo methodInfo = AccessTools.Method(typeof(PlayerPickup), "LeftHandPickup", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(PlayerPickup), "SwitchWeapons", (Type[])null, (Type[])null);
			if (_rightHandPickup == null || methodInfo == null || methodInfo2 == null)
			{
				ReloadBalancePlugin.Log.LogError((object)"[ReloadBalance] PlayerPickup hand methods not found; the no-dual-wield rule is inactive.");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(HandPatches), "LeftHandPickup_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(HandPatches), "SwitchWeapons_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		public static bool LeftHandPickup_Prefix(PlayerPickup __instance)
		{
			if (!Active)
			{
				return true;
			}
			try
			{
				_rightHandPickup.Invoke(__instance, null);
			}
			catch (Exception ex)
			{
				ReloadBalancePlugin.WarnOnce("lefthand", "right-hand pickup redirect failed: " + ex.GetType().Name + " - " + ex.Message);
			}
			return false;
		}

		public static bool SwitchWeapons_Prefix()
		{
			return !Active;
		}
	}
	[BepInPlugin("com.clst.straftatreloadbalance", "Straftat Reload Balance", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ReloadBalancePlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.clst.straftatreloadbalance";

		public const string PluginName = "Straftat Reload Balance";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<bool> InfiniteReserve;

		public static ConfigEntry<bool> ManualReloadGunClass;

		public static ConfigEntry<bool> NoDualWield;

		public static ConfigEntry<bool> HideModelDuringReload;

		public static ConfigEntry<bool> HideModelVanillaWeapons;

		public static ConfigEntry<bool> HideHandsDuringReload;

		public static ConfigEntry<float> ReloadTimeMultiplier;

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

		private Harmony _harmony;

		public static ReloadBalancePlugin Instance { get; private set; }

		private void Awake()
		{
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle. Weapon data is applied when a weapon spawns, so turning this off mid-match only affects weapons spawned afterwards.");
			InfiniteReserve = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Infinite Reserve", true, "Reserve ammo never runs out for Gun, Shotgun and LargeRaycastGun weapons. Only the magazine has to be reloaded. The HUD shows the reserve as an infinity sign.");
			ManualReloadGunClass = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Manual Reload For Gun Class", true, "Lets Gun-class weapons (pistols, SMGs, rifles) reload with the reload key before the magazine is empty. Shotguns and LargeRaycastGun weapons already do this in vanilla.");
			NoDualWield = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "No Dual Wield", true, "Nothing can be picked up into the left hand. Interacting with a weapon while holding one swaps weapons. The vanilla hand-switch key does nothing.");
			HideModelDuringReload = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Hide Model During Reload", true, "Hides the held weapon model for the reload duration when the reload does not visibly animate the weapon, so the reload is visible even though vanilla only plays a sound. Which weapons hide is set per weapon in the Weapons section (hide / show / auto).");
			HideModelVanillaWeapons = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Hide Model Vanilla Weapons", true, "Also hide the model for weapons that could already reload in vanilla when their reload is invisible. In the current game files that is only the Tromblonj, whose reload clip is empty; the other 11 have real reload animations and keep their model.");
			HideHandsDuringReload = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Hide Hands During Reload", true, "Whenever the weapon model is hidden for a reload, hide the first-person arms and hands as well. Only you see your own arms; other players are unaffected.");
			ReloadTimeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Reload Time Multiplier", 1f, new ConfigDescription("Multiplies every reload time from the Weapons section. Applied when a weapon spawns.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			WeaponTable.BindConfig(((BaseUnityPlugin)this).Config);
			try
			{
				_harmony = new Harmony("com.clst.straftatreloadbalance");
				ReloadPatches.Apply(_harmony);
				HandPatches.Apply(_harmony);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[ReloadBalance] patching failed: " + ex));
				return;
			}
			Log.LogInfo((object)("[ReloadBalance] Straftat Reload Balance 1.0.0 loaded, " + WeaponTable.Count + " weapon entries."));
		}

		internal static void LogOnce(string key, string message)
		{
			if (Log != null && _loggedOnce.Add(key))
			{
				Log.LogInfo((object)("[ReloadBalance] " + message));
			}
		}

		internal static void WarnOnce(string key, string message)
		{
			if (Log != null && _loggedOnce.Add(key))
			{
				Log.LogWarning((object)("[ReloadBalance] " + message));
			}
		}
	}
	internal sealed class ReloadFields
	{
		public readonly Type Type;

		public readonly FieldInfo ReloadTime;

		public readonly FieldInfo ReloadClip;

		public readonly FieldInfo FireTimer;

		public readonly FieldInfo IsBursting;

		public readonly MethodInfo Reload;

		public readonly bool IsGun;

		private static readonly Dictionary<Type, ReloadFields> _cache = new Dictionary<Type, ReloadFields>();

		public static bool IsSupported(Type t)
		{
			if (!(t == typeof(Gun)) && !(t == typeof(Shotgun)))
			{
				return t == typeof(LargeRaycastGun);
			}
			return true;
		}

		public static ReloadFields For(Type t)
		{
			if (t == null)
			{
				return null;
			}
			if (_cache.TryGetValue(t, out var value))
			{
				return value;
			}
			value = (IsSupported(t) ? new ReloadFields(t) : null);
			_cache[t] = value;
			return value;
		}

		private ReloadFields(Type t)
		{
			Type = t;
			ReloadTime = AccessTools.DeclaredField(t, "reloadTime");
			ReloadClip = AccessTools.DeclaredField(t, "reloadClip");
			FireTimer = AccessTools.DeclaredField(t, "fireTimer");
			IsBursting = AccessTools.DeclaredField(t, "isBursting");
			Reload = AccessTools.DeclaredMethod(t, "Reload", (Type[])null, (Type[])null);
			IsGun = t == typeof(Gun);
			if (ReloadTime == null || ReloadClip == null || Reload == null)
			{
				ReloadBalancePlugin.Log.LogWarning((object)("[ReloadBalance] " + t.Name + " is missing expected reload members (reloadTime=" + (ReloadTime != null) + ", reloadClip=" + (ReloadClip != null) + ", Reload=" + (Reload != null) + ")."));
			}
		}
	}
	internal static class ReloadPatches
	{
		public static void Apply(Harmony harmony)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			Type[] array = new Type[3]
			{
				typeof(Gun),
				typeof(Shotgun),
				typeof(LargeRaycastGun)
			};
			foreach (Type type in array)
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "Awake", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					ReloadBalancePlugin.Log.LogError((object)("[ReloadBalance] " + type.Name + ".Awake not found; that class stays vanilla."));
				}
				else
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ReloadPatches), "Awake_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		public static void Awake_Postfix(object __instance)
		{
			Weapon val = (Weapon)((__instance is Weapon) ? __instance : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				Setup(val);
			}
			catch (Exception ex)
			{
				ReloadBalancePlugin.WarnOnce("setup/" + ((Object)((Component)val).gameObject).name, "setup failed for " + ((Object)((Component)val).gameObject).name + ": " + ex);
			}
		}

		private static void Setup(Weapon weapon)
		{
			if (!ReloadBalancePlugin.Enabled.Value)
			{
				return;
			}
			ReloadFields reloadFields = ReloadFields.For(((object)weapon).GetType());
			if (reloadFields == null)
			{
				return;
			}
			string text = WeaponTable.NormalizeName(((Object)((Component)weapon).gameObject).name);
			if (!WeaponTable.TryGet(text, out var entry))
			{
				entry = null;
				if (!weapon.reloadWeapon)
				{
					ReloadBalancePlugin.LogOnce("unknown/" + text, text + " is not in the weapon table and cannot reload in vanilla; left untouched.");
					return;
				}
			}
			else
			{
				weapon.reloadWeapon = true;
				weapon.ammoCharge = Mathf.Max(1, entry.Magazine);
				float num = Mathf.Max(0f, entry.ReloadTime * ReloadBalancePlugin.ReloadTimeMultiplier.Value);
				if (reloadFields.ReloadTime != null)
				{
					reloadFields.ReloadTime.SetValue(weapon, num);
				}
				ApplyClip(weapon, reloadFields, entry, text);
				if (entry.FireInterval > 0f)
				{
					weapon.timeBetweenFire = entry.FireInterval;
				}
			}
			if (ReloadBalancePlugin.InfiniteReserve.Value)
			{
				weapon.sync___set_value_currentAmmo(999, true);
			}
			ReloadState reloadState = ((Component)weapon).gameObject.GetComponent<ReloadState>();
			if ((Object)(object)reloadState == (Object)null)
			{
				reloadState = ((Component)weapon).gameObject.AddComponent<ReloadState>();
			}
			reloadState.Init(weapon, entry, reloadFields);
			if (entry != null)
			{
				AudioClip val = (AudioClip)((reloadFields.ReloadClip != null) ? /*isinst with value type is only supported in some contexts*/: null);
				float num2 = ((reloadFields.ReloadTime != null) ? ((float)reloadFields.ReloadTime.GetValue(weapon)) : 0f);
				string text2 = ((entry.FireInterval > 0f) ? string.Format(CultureInfo.InvariantCulture, ", fire interval {0:0.###} s", entry.FireInterval) : "");
				ReloadBalancePlugin.LogOnce("applied/" + text, string.Format(CultureInfo.InvariantCulture, "{0}: magazine {1}, reload {2:0.##} s, sound {3}{4}", text, weapon.ammoCharge, num2, ((Object)(object)val != (Object)null) ? ((Object)val).name : "(none)", text2));
			}
			else
			{
				ReloadBalancePlugin.LogOnce("applied/" + text, text + ": vanilla reload data kept, reserve pinned.");
			}
		}

		private static void ApplyClip(Weapon weapon, ReloadFields fields, WeaponEntry entry, string prefab)
		{
			if (fields.ReloadClip == null)
			{
				return;
			}
			object? value = fields.ReloadClip.GetValue(weapon);
			AudioClip val = (AudioClip)((value is AudioClip) ? value : null);
			AudioClip val2 = null;
			if (!string.IsNullOrEmpty(entry.Clip))
			{
				val2 = WeaponTable.FindClip(entry.Clip);
				if ((Object)(object)val2 == (Object)null)
				{
					ReloadBalancePlugin.WarnOnce("clip/" + prefab, prefab + ": reload sound " + entry.Clip + " not found; " + (((Object)(object)val != (Object)null) ? "keeping the vanilla sound." : "falling back to GlockReload."));
				}
			}
			if ((Object)(object)val2 == (Object)null && (Object)(object)val == (Object)null)
			{
				val2 = WeaponTable.FindClip("GlockReload");
			}
			if ((Object)(object)val2 != (Object)null)
			{
				fields.ReloadClip.SetValue(weapon, val2);
			}
		}
	}
	internal sealed class ReloadState : MonoBehaviour
	{
		public const int ReserveValue = 999;

		public const int ReserveFloor = 900;

		private const int GroundLayer = 7;

		private Weapon _weapon;

		private WeaponEntry _entry;

		private ReloadFields _fields;

		private bool _hidden;

		private readonly List<Renderer> _hiddenRenderers = new List<Renderer>();

		private int _hasReloadAnimation = -1;

		public void Init(Weapon weapon, WeaponEntry entry, ReloadFields fields)
		{
			_weapon = weapon;
			_entry = entry;
			_fields = fields;
			_hasReloadAnimation = -1;
		}

		private void LateUpdate()
		{
			if ((Object)(object)_weapon == (Object)null || _fields == null)
			{
				if (_hidden)
				{
					ShowModel();
				}
				((Behaviour)this).enabled = false;
				return;
			}
			if (!ReloadBalancePlugin.Enabled.Value)
			{
				if (_hidden)
				{
					ShowModel();
				}
				return;
			}
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				ReloadBalancePlugin.WarnOnce("tick/" + ((Object)((Component)this).gameObject).name, "per-frame update failed for " + ((Object)((Component)this).gameObject).name + ": " + ex);
			}
		}

		private void OnDisable()
		{
			if (_hidden)
			{
				ShowModel();
			}
		}

		private void Tick()
		{
			bool value = ReloadBalancePlugin.InfiniteReserve.Value;
			if (value && _weapon.sync___get_value_currentAmmo() < 900)
			{
				_weapon.sync___set_value_currentAmmo(999, true);
			}
			if ((!_weapon.inRightHand && !_weapon.inLeftHand) || ((Component)this).gameObject.layer == 7 || !((NetworkBehaviour)_weapon).IsOwner)
			{
				if (_hidden)
				{
					ShowModel();
				}
				return;
			}
			if (value && _weapon.reloadWeapon)
			{
				PauseManager instance = PauseManager.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.ChangeAmmoText("∞", FormatMagazine(_weapon.chargedBullets) + " / ", _weapon.inRightHand);
				}
			}
			if (_fields.IsGun && ReloadBalancePlugin.ManualReloadGunClass.Value)
			{
				TryManualReload();
			}
			bool flag = _entry == null || _entry.VanillaReloadable;
			bool flag2 = ((_entry != null && _entry.HideModel.HasValue) ? _entry.HideModel.Value : (!HasReloadAnimation()));
			bool flag3 = ReloadBalancePlugin.HideModelDuringReload.Value && _weapon.reloadWeapon && _weapon.isReloading && (!flag || ReloadBalancePlugin.HideModelVanillaWeapons.Value) && flag2;
			if (flag3 && !_hidden)
			{
				HideModel();
			}
			else if (!flag3 && _hidden)
			{
				ShowModel();
			}
		}

		private static string FormatMagazine(float bullets)
		{
			return Mathf.Max(0, Mathf.RoundToInt(bullets)).ToString(CultureInfo.InvariantCulture);
		}

		private void TryManualReload()
		{
			if (_weapon.isReloading || !_weapon.reloadWeapon || _weapon.chargedBullets >= (float)_weapon.ammoCharge)
			{
				return;
			}
			InputAction reload = _weapon.reload;
			if (reload != null && !(reload.ReadValue<float>() <= 0.1f))
			{
				PauseManager instance = PauseManager.Instance;
				if ((!((Object)(object)instance != (Object)null) || (!instance.pause && !instance.chatting)) && (!(_fields.FireTimer != null) || !((float)_fields.FireTimer.GetValue(_weapon) > 0f)) && (!(_fields.IsBursting != null) || !(bool)_fields.IsBursting.GetValue(_weapon)) && !(_fields.Reload == null) && _fields.Reload.Invoke(_weapon, null) is IEnumerator enumerator)
				{
					((MonoBehaviour)_weapon).StartCoroutine(enumerator);
				}
			}
		}

		private bool HasReloadAnimation()
		{
			if (_hasReloadAnimation >= 0)
			{
				return _hasReloadAnimation == 1;
			}
			bool flag = false;
			Animator animator = _weapon.animator;
			if ((Object)(object)animator != (Object)null && (Object)(object)animator.runtimeAnimatorController != (Object)null)
			{
				try
				{
					AnimatorControllerParameter[] parameters = animator.parameters;
					for (int i = 0; i < parameters.Length; i++)
					{
						if (parameters[i].name == "Reload")
						{
							flag = true;
							break;
						}
					}
				}
				catch
				{
				}
				if (!flag)
				{
					try
					{
						AnimationClip[] animationClips = animator.runtimeAnimatorController.animationClips;
						foreach (AnimationClip val in animationClips)
						{
							if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("reload", StringComparison.OrdinalIgnoreCase) >= 0)
							{
								flag = true;
								break;
							}
						}
					}
					catch
					{
					}
				}
			}
			_hasReloadAnimation = (flag ? 1 : 0);
			return flag;
		}

		private void HideModel()
		{
			_hiddenRenderers.Clear();
			DisableRenderers(((Component)_weapon).transform);
			if (ReloadBalancePlugin.HideHandsDuringReload.Value)
			{
				Transform fpArms = _weapon.fpArms;
				if ((Object)(object)fpArms != (Object)null)
				{
					DisableRenderers(fpArms);
				}
			}
			_hidden = true;
		}

		private void DisableRenderers(Transform root)
		{
			Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(false);
			foreach (Renderer val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && val.enabled)
				{
					val.enabled = false;
					_hiddenRenderers.Add(val);
				}
			}
		}

		private void ShowModel()
		{
			foreach (Renderer hiddenRenderer in _hiddenRenderers)
			{
				if ((Object)(object)hiddenRenderer != (Object)null)
				{
					hiddenRenderer.enabled = true;
				}
			}
			_hiddenRenderers.Clear();
			_hidden = false;
		}
	}
	internal sealed class WeaponEntry
	{
		public string Prefab;

		public string WeaponClass;

		public string DisplayName;

		public bool VanillaReloadable;

		public int Magazine;

		public float ReloadTime;

		public string Clip;

		public float FireInterval;

		public bool? HideModel;

		public string VanillaNote;

		public WeaponEntry(string prefab, string weaponClass, string displayName, bool vanillaReloadable, int magazine, float reloadTime, string clip, float fireInterval, bool hideModel, string vanillaNote)
		{
			Prefab = prefab;
			WeaponClass = weaponClass;
			DisplayName = displayName;
			VanillaReloadable = vanillaReloadable;
			Magazine = magazine;
			ReloadTime = reloadTime;
			Clip = clip;
			FireInterval = fireInterval;
			HideModel = hideModel;
			VanillaNote = vanillaNote;
		}

		public WeaponEntry Clone()
		{
			return (WeaponEntry)MemberwiseClone();
		}

		public string ToConfigString()
		{
			return string.Format(CultureInfo.InvariantCulture, "{0}, {1}, {2}, {3}, {4}", Magazine, ReloadTime, Clip, FireInterval, (!HideModel.HasValue) ? "auto" : (HideModel.Value ? "hide" : "show"));
		}
	}
	internal static class WeaponTable
	{
		internal const string Sounds = "GlockReload, smithcarbinereload, QCW05Reload, PhoenixReload, ReloadSnipeRifle";

		private static readonly WeaponEntry[] Defaults = new WeaponEntry[35]
		{
			new WeaponEntry("Revolver", "Gun", "revolver", vanillaReloadable: false, 6, 0.8f, "GlockReload", 0f, hideModel: true, "pool 6, no reload"),
			new WeaponEntry("Gun", "Gun", "pistol", vanillaReloadable: false, 12, 0.6f, "GlockReload", 0f, hideModel: true, "pool 12, no reload"),
			new WeaponEntry("Webley", "Gun", "Webley", vanillaReloadable: false, 6, 0.8f, "GlockReload", 0f, hideModel: true, "pool 12, no reload, 3-round aim burst"),
			new WeaponEntry("DF_Torrent", "Gun", "Torrent", vanillaReloadable: false, 15, 0.8f, "GlockReload", 0f, hideModel: true, "pool 15, no reload, 3-round burst"),
			new WeaponEntry("Glock", "Gun", "glock", vanillaReloadable: false, 17, 0.8f, "GlockReload", 0f, hideModel: true, "pool 17, no reload"),
			new WeaponEntry("Silenzzio", "Gun", "silenzzio", vanillaReloadable: false, 18, 0.8f, "GlockReload", 0f, hideModel: true, "pool 18, no reload"),
			new WeaponEntry("SMG", "Gun", "smg", vanillaReloadable: false, 24, 0.8f, "GlockReload", 0f, hideModel: true, "pool 24, no reload"),
			new WeaponEntry("Dispenser", "Gun", "dispenser", vanillaReloadable: false, 28, 1f, "smithcarbinereload", 0f, hideModel: true, "pool 28, no reload"),
			new WeaponEntry("AK-K", "Gun", "ak", vanillaReloadable: false, 30, 1f, "smithcarbinereload", 0f, hideModel: true, "pool 30, no reload"),
			new WeaponEntry("Crisis", "Gun", "crisis", vanillaReloadable: false, 30, 1f, "GlockReload", 0f, hideModel: true, "pool 30, no reload, fires the whole magazine per click"),
			new WeaponEntry("Warden", "Gun", "warden", vanillaReloadable: false, 30, 1f, "smithcarbinereload", 0f, hideModel: true, "pool 30, no reload"),
			new WeaponEntry("AR15", "Gun", "ar", vanillaReloadable: false, 32, 1f, "smithcarbinereload", 0f, hideModel: true, "pool 32, no reload"),
			new WeaponEntry("GlaiveGun", "Gun", "glaive gun", vanillaReloadable: false, 36, 1.2f, "GlockReload", 0f, hideModel: true, "pool 36, no reload, 6-round burst"),
			new WeaponEntry("Hill_H15", "Gun", "hill h15", vanillaReloadable: false, 25, 0.8f, "GlockReload", 0f, hideModel: true, "pool 50, no reload"),
			new WeaponEntry("HK_G11", "Gun", "HK_G11", vanillaReloadable: false, 24, 1f, "smithcarbinereload", 0f, hideModel: true, "pool 55, no reload, 3-round aim burst"),
			new WeaponEntry("Mac10", "Gun", "mac10", vanillaReloadable: false, 30, 0.8f, "GlockReload", 0f, hideModel: true, "pool 56, no reload"),
			new WeaponEntry("AAA12", "Shotgun", "AAA12", vanillaReloadable: false, 117, 0.5f, "GlockReload", 0.5f, hideModel: false, "pool 117, no reload, fire interval 0.34 s; plays the Havoc reload animation"),
			new WeaponEntry("BlankState", "LargeRaycastGun", "blank state", vanillaReloadable: false, 1, 5f, "GlockReload", 0f, hideModel: true, "pool 1, no reload, damage 100; Elephant controller animates nothing on this model"),
			new WeaponEntry("Bender", "LargeRaycastGun", "bender", vanillaReloadable: false, 7, 0.8f, "GlockReload", 0f, hideModel: true, "pool 12, no reload"),
			new WeaponEntry("Bayshore", "LargeRaycastGun", "Bayshore", vanillaReloadable: false, 3, 1f, "smithcarbinereload", 0f, hideModel: true, "pool 19, no reload, damage 4; Smith Carbine controller animates nothing on this model"),
			new WeaponEntry("FG42", "LargeRaycastGun", "FG42", vanillaReloadable: false, 5, 1f, "smithcarbinereload", 0f, hideModel: true, "pool 19, no reload, damage 3; Smith Carbine controller animates nothing on this model"),
			new WeaponEntry("Bukanee", "LargeRaycastGun", "Bukanee", vanillaReloadable: false, 35, 0.8f, "GlockReload", 0f, hideModel: true, "pool 35, no reload"),
			new WeaponEntry("Keso", "LargeRaycastGun", "Keso", vanillaReloadable: false, 35, 0.8f, "GlockReload", 0f, hideModel: true, "pool 35, no reload"),
			new WeaponEntry("Shotgun", "Shotgun", "shotgun", vanillaReloadable: true, 8, 0.21f, "GlockReload", 0f, hideModel: false, "pool 40, magazine 8, reload 0.21 s, animated reload"),
			new WeaponEntry("Havoc", "Shotgun", "havoc", vanillaReloadable: true, 24, 0.34f, "GlockReload", 0f, hideModel: false, "pool 72, magazine 24, reload 0.34 s, animated reload"),
			new WeaponEntry("SawedOff", "Shotgun", "sawed off", vanillaReloadable: true, 16, 0.7f, "GlockReload", 0f, hideModel: false, "pool 64, magazine 16, reload 0.7 s, animated reload"),
			new WeaponEntry("Tromblonj", "Shotgun", "tromblonj", vanillaReloadable: true, 32, 1f, "GlockReload", 0f, hideModel: true, "pool 128, magazine 32, reload 1 s; its reload clip is empty in the game files"),
			new WeaponEntry("Elephant", "LargeRaycastGun", "elephant", vanillaReloadable: true, 1, 1.2f, "GlockReload", 0f, hideModel: false, "pool 6, magazine 1, reload 0.18 s (mod default 1.2 s), animated reload"),
			new WeaponEntry("Yangtse", "LargeRaycastGun", "Yangtse", vanillaReloadable: true, 35, 0.7f, "QCW05Reload", 0f, hideModel: false, "pool 105, magazine 35, reload 0.7 s, animated reload"),
			new WeaponEntry("Phoenix", "LargeRaycastGun", "phoenix", vanillaReloadable: true, 8, 0.8f, "PhoenixReload", 0f, hideModel: false, "pool 100, magazine 8, reload 0.8 s, animated reload"),
			new WeaponEntry("Kusma", "LargeRaycastGun", "kusma", vanillaReloadable: true, 30, 1f, "QCW05Reload", 0f, hideModel: false, "pool 90, magazine 30, reload 1 s, animated reload"),
			new WeaponEntry("QCW05", "LargeRaycastGun", "QCW05", vanillaReloadable: true, 15, 1f, "QCW05Reload", 0f, hideModel: false, "pool 60, magazine 15, reload 1 s, animated reload"),
			new WeaponEntry("SmithCarbine", "LargeRaycastGun", "Smith Carbine", vanillaReloadable: true, 1, 1f, "smithcarbinereload", 0f, hideModel: false, "pool 24, magazine 1, reload 1 s, animated reload"),
			new WeaponEntry("M2000", "LargeRaycastGun", "M2000", vanillaReloadable: true, 1, 1.2f, "ReloadSnipeRifle", 0f, hideModel: false, "pool 6, magazine 1, reload 1.2 s, animated reload"),
			new WeaponEntry("Gust", "LargeRaycastGun", "Gust", vanillaReloadable: true, 1, 2f, "smithcarbinereload", 0f, hideModel: false, "pool 15, magazine 1, reload 2 s, animated reload")
		};

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

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

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

		private static Dictionary<string, AudioClip> _clips;

		public static int Count => _effective.Count;

		public static void BindConfig(ConfigFile cfg)
		{
			WeaponEntry[] defaults = Defaults;
			foreach (WeaponEntry weaponEntry in defaults)
			{
				_defaults[weaponEntry.Prefab] = weaponEntry;
				string text = weaponEntry.DisplayName + " [" + weaponEntry.WeaponClass + "]. Vanilla: " + weaponEntry.VanillaNote + ". Format: magazine, reload seconds, reload sound, fire interval seconds (0 = vanilla), model while reloading (hide, show or auto). Sounds in the game: GlockReload, smithcarbinereload, QCW05Reload, PhoenixReload, ReloadSnipeRifle.";
				ConfigEntry<string> val = cfg.Bind<string>("Weapons", weaponEntry.Prefab, weaponEntry.ToConfigString(), text);
				_config[weaponEntry.Prefab] = val;
				_effective[weaponEntry.Prefab] = Parse(weaponEntry, val.Value);
				string prefab = weaponEntry.Prefab;
				val.SettingChanged += delegate
				{
					_effective[prefab] = Parse(_defaults[prefab], _config[prefab].Value);
				};
			}
		}

		public static bool TryGet(string prefab, out WeaponEntry entry)
		{
			return _effective.TryGetValue(prefab, out entry);
		}

		public static string NormalizeName(string goName)
		{
			if (string.IsNullOrEmpty(goName))
			{
				return "";
			}
			string text = goName;
			int startIndex;
			while ((startIndex = text.IndexOf("(Clone)", StringComparison.Ordinal)) >= 0)
			{
				text = text.Remove(startIndex, 7);
			}
			return text.Trim();
		}

		private static WeaponEntry Parse(WeaponEntry d, string text)
		{
			WeaponEntry weaponEntry = d.Clone();
			if (string.IsNullOrEmpty(text))
			{
				return weaponEntry;
			}
			string[] array = text.Split(new char[1] { ',' });
			if (array.Length < 2 || !int.TryParse(array[0].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || result < 1 || result2 < 0f)
			{
				ReloadBalancePlugin.WarnOnce("cfg/" + d.Prefab, "config entry for " + d.Prefab + " is not valid (" + text + "); using the default " + d.ToConfigString());
				return weaponEntry;
			}
			weaponEntry.Magazine = result;
			weaponEntry.ReloadTime = result2;
			if (array.Length > 2)
			{
				string text2 = array[2].Trim();
				if (text2.Length > 0)
				{
					weaponEntry.Clip = text2;
				}
			}
			if (array.Length > 3 && float.TryParse(array[3].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && result3 >= 0f)
			{
				weaponEntry.FireInterval = result3;
			}
			if (array.Length > 4)
			{
				if (TryParseHide(array[4], out var value))
				{
					weaponEntry.HideModel = value;
				}
				else
				{
					ReloadBalancePlugin.WarnOnce("cfghide/" + d.Prefab, "config entry for " + d.Prefab + ": model flag '" + array[4].Trim() + "' not understood, use hide, show or auto; keeping " + ((!d.HideModel.HasValue) ? "auto" : (d.HideModel.Value ? "hide" : "show")));
				}
			}
			return weaponEntry;
		}

		private static bool TryParseHide(string token, out bool? value)
		{
			value = null;
			switch ((token ?? "").Trim().ToLowerInvariant())
			{
			case "true":
			case "hide":
			case "yes":
			case "1":
			case "on":
			case "hidden":
				value = true;
				return true;
			case "keep":
			case "show":
			case "off":
			case "0":
			case "no":
			case "false":
				value = false;
				return true;
			case "auto":
			case "":
				value = null;
				return true;
			default:
				return false;
			}
		}

		public static AudioClip FindClip(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			EnsureClips();
			if (_clips != null && _clips.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			try
			{
				AudioClip[] array = Resources.FindObjectsOfTypeAll<AudioClip>();
				foreach (AudioClip val in array)
				{
					if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, name, StringComparison.OrdinalIgnoreCase))
					{
						if (_clips != null)
						{
							_clips[name] = val;
						}
						return val;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static void EnsureClips()
		{
			if (_clips != null && _clips.Count > 0)
			{
				return;
			}
			Dictionary<string, AudioClip> dictionary = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase);
			GameObject[] array = null;
			try
			{
				array = Resources.LoadAll<GameObject>("RandomWeapons");
			}
			catch (Exception ex)
			{
				ReloadBalancePlugin.WarnOnce("clips", "could not load RandomWeapons prefabs: " + ex.Message);
			}
			if (array != null)
			{
				GameObject[] array2 = array;
				foreach (GameObject val in array2)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Weapon component = val.GetComponent<Weapon>();
					if ((Object)(object)component == (Object)null)
					{
						continue;
					}
					ReloadFields reloadFields = ReloadFields.For(((object)component).GetType());
					if (reloadFields != null && !(reloadFields.ReloadClip == null))
					{
						object? value = reloadFields.ReloadClip.GetValue(component);
						AudioClip val2 = (AudioClip)((value is AudioClip) ? value : null);
						if ((Object)(object)val2 != (Object)null && !dictionary.ContainsKey(((Object)val2).name))
						{
							dictionary[((Object)val2).name] = val2;
						}
					}
				}
			}
			if (dictionary.Count > 0)
			{
				_clips = dictionary;
				ReloadBalancePlugin.LogOnce("clips", "reload sounds found: " + string.Join(", ", new List<string>(dictionary.Keys).ToArray()));
			}
		}
	}
}