Decompiled source of DoubleBarrelPlus v1.1.1

DoubleBarrelPlus.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Bifrost.Cooked;
using DooDesch;
using DooDesch.Weapons;
using DoubleBarrelPlus;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Mimic;
using Mimic.Actors;
using Mimic.Animation;
using ReluProtocol;
using ReluProtocol.Enum;
using Steamworks;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "DoubleBarrelPlus", "1.1.1", "DooDesch", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+84e39077a211c16706ca561236a1f7d07ae8d9e3")]
[assembly: AssemblyProduct("DoubleBarrelPlus")]
[assembly: AssemblyTitle("DoubleBarrelPlus")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[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 DooDesch
{
	internal sealed class HostSettings
	{
		[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyCreated")]
		private static class CreatedPatch
		{
			private static void Postfix(LobbyCreated_t callback)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				if ((int)callback.m_eResult != 1)
				{
					return;
				}
				foreach (HostSettings instance in Instances)
				{
					instance.Begin(new CSteamID(callback.m_ulSteamIDLobby), host: true);
				}
			}
		}

		[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyEntered")]
		private static class EnteredPatch
		{
			private static void Prefix(LobbyEnter_t callback)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_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)
				if (callback.m_EChatRoomEnterResponse != 1)
				{
					return;
				}
				CSteamID val = default(CSteamID);
				((CSteamID)(ref val))..ctor(callback.m_ulSteamIDLobby);
				foreach (HostSettings instance in Instances)
				{
					if (!instance.isHost || !(instance.lobby == val))
					{
						instance.Begin(val, host: false);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SteamMatchmaking), "LeaveLobby")]
		private static class LeavePatch
		{
			private static void Prefix(CSteamID steamIDLobby)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				foreach (HostSettings instance in Instances)
				{
					if (instance.lobby == steamIDLobby)
					{
						instance.Reset();
					}
				}
			}
		}

		[HarmonyPatch(typeof(MainMenu), "Start")]
		private static class MenuPatch
		{
			private static void Prefix()
			{
				foreach (HostSettings instance in Instances)
				{
					instance.Reset();
				}
			}
		}

		private static readonly List<HostSettings> Instances = new List<HostSettings>();

		private static Callback<LobbyDataUpdate_t> dataCallback;

		private static float nextPoll;

		private readonly string modId;

		private readonly string lobbyKey;

		private readonly int schemaVersion;

		private readonly List<ISetting> settings = new List<ISetting>();

		private readonly string modernKey;

		private string modernPayload;

		private string lastModernPayload;

		private long modernRevision;

		private bool legacyHost;

		private Dictionary<string, object> snapshot;

		private CSteamID lobby;

		private CSteamID owner;

		private bool isHost;

		private bool hostCreated;

		private bool authorityValid;

		private bool ownerChanged;

		private bool published;

		private string hostPayload;

		private string warningReason;

		private int warningRepeats;

		internal bool IsInSession => lobby != CSteamID.Nil;

		internal bool IsHost
		{
			get
			{
				if (isHost)
				{
					return authorityValid;
				}
				return false;
			}
		}

		internal bool UsingLegacyHost
		{
			get
			{
				if (!isHost && authorityValid && legacyHost)
				{
					return modernRevision == 0;
				}
				return false;
			}
		}

		internal event Action EffectiveValuesChanged;

		internal event Action LiveValuesChanged;

		internal HostSettings(string modId, int schemaVersion = 1)
		{
			if (string.IsNullOrWhiteSpace(modId))
			{
				throw new ArgumentException("A mod ID is required.", "modId");
			}
			if (schemaVersion < 1)
			{
				throw new ArgumentOutOfRangeException("schemaVersion");
			}
			this.modId = modId;
			this.schemaVersion = schemaVersion;
			lobbyKey = "doodesch." + modId.ToLowerInvariant() + ".settings.v" + schemaVersion;
			modernKey = lobbyKey + ".extended";
			if (Encoding.UTF8.GetByteCount(modernKey) >= 255)
			{
				throw new ArgumentException("The mod ID exceeds the Steam lobby key limit.", "modId");
			}
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobbyKey == lobbyKey)
				{
					throw new ArgumentException("The mod already has a settings snapshot.", "modId");
				}
			}
			Instances.Add(this);
		}

		internal HostSetting<T> Register<T>(MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate = null, bool live = false, bool legacy = true, Func<T> localValue = null)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			if (string.IsNullOrWhiteSpace(((MelonPreferences_Entry)entry).Identifier))
			{
				throw new ArgumentException("A setting identifier is required.", "entry");
			}
			if (((MelonPreferences_Entry)entry).Identifier.IndexOfAny(new char[3] { '\n', '\r', '\t' }) >= 0)
			{
				throw new ArgumentException("Setting identifiers cannot contain wire delimiters.", "entry");
			}
			if (lobby != CSteamID.Nil)
			{
				throw new InvalidOperationException("Register settings before entering a lobby.");
			}
			if (typeof(T) != typeof(bool) && typeof(T) != typeof(int) && typeof(T) != typeof(float) && typeof(T) != typeof(string))
			{
				throw new NotSupportedException("Host settings support bool, int, float and string values.");
			}
			if (legacy && typeof(T) == typeof(string))
			{
				throw new ArgumentException("String settings must use the extended snapshot.", "legacy");
			}
			foreach (ISetting setting in settings)
			{
				if (setting.Key == ((MelonPreferences_Entry)entry).Identifier)
				{
					throw new ArgumentException("Duplicate setting: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
				}
			}
			HostSetting<T> hostSetting = new HostSetting<T>(this, entry, vanillaValue, validate, live, legacy, localValue);
			if (!hostSetting.IsValid(vanillaValue))
			{
				throw new ArgumentException("The vanilla value is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "vanillaValue");
			}
			if (!hostSetting.IsValid(entry.DefaultValue))
			{
				throw new ArgumentException("The configured default is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
			}
			settings.Add(hostSetting);
			return hostSetting;
		}

		internal T GetValue<T>(string key, T localValue, T vanillaValue, bool live)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (lobby == CSteamID.Nil)
			{
				return localValue;
			}
			if (live && !authorityValid)
			{
				return vanillaValue;
			}
			if (snapshot == null || !snapshot.TryGetValue(key, out var value))
			{
				return vanillaValue;
			}
			return (T)value;
		}

		internal static void Update()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (Time.realtimeSinceStartup < nextPoll)
			{
				return;
			}
			nextPoll = Time.realtimeSinceStartup + 1f;
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobby != CSteamID.Nil)
				{
					instance.Refresh();
				}
			}
		}

		private void Begin(CSteamID newLobby, bool host)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (settings.Count == 0 || (lobby == newLobby && (!host || hostCreated)))
			{
				return;
			}
			if (lobby != newLobby)
			{
				Reset();
			}
			lobby = newLobby;
			isHost = false;
			hostCreated = host;
			authorityValid = false;
			ownerChanged = false;
			snapshot = null;
			published = false;
			hostPayload = null;
			owner = CSteamID.Nil;
			try
			{
				owner = SteamMatchmaking.GetLobbyOwner(lobby);
				CSteamID steamID = SteamUser.GetSteamID();
				if (owner == CSteamID.Nil || (host ? (owner != steamID) : (owner == steamID)))
				{
					owner = CSteamID.Nil;
					Warn("Lobby owner does not identify the expected host; using vanilla settings.");
					Changed();
					return;
				}
				isHost = host;
				authorityValid = true;
				if (host)
				{
					CaptureHostSnapshot();
				}
				else
				{
					CaptureClientSnapshot();
				}
			}
			catch (Exception ex)
			{
				Warn("Initial lobby settings failed; vanilla settings are frozen for this session: " + ex.GetType().Name + ": " + ex.Message);
			}
			Refresh();
			Changed();
		}

		private static void EnsureCallback()
		{
			if (dataCallback == null)
			{
				dataCallback = Callback<LobbyDataUpdate_t>.Create((DispatchDelegate<LobbyDataUpdate_t>)OnLobbyDataUpdated);
			}
		}

		private static void OnLobbyDataUpdated(LobbyDataUpdate_t callback)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobby.m_SteamID == callback.m_ulSteamIDLobby && callback.m_ulSteamIDMember == callback.m_ulSteamIDLobby)
				{
					if (callback.m_bSuccess == 0)
					{
						instance.Warn("Steam lobby data update failed; frozen session settings are retained.");
					}
					else
					{
						instance.Refresh();
					}
				}
			}
		}

		private void CaptureHostSnapshot()
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			StringBuilder stringBuilder = new StringBuilder(schemaVersion.ToString(CultureInfo.InvariantCulture));
			for (int i = 0; i < settings.Count; i++)
			{
				ISetting setting = settings[i];
				object localValue = setting.LocalValue;
				dictionary.Add(setting.Key, localValue);
				if (setting.Legacy)
				{
					stringBuilder.Append('\n').Append(setting.Key).Append('\t')
						.Append(Format(localValue));
				}
			}
			string s = stringBuilder.ToString();
			if (Encoding.UTF8.GetByteCount(s) + 1 > 8192)
			{
				throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
			}
			hostPayload = s;
			snapshot = dictionary;
			modernRevision = 1L;
			modernPayload = BuildModern(dictionary, modernRevision);
		}

		private unsafe void CaptureClientSnapshot()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
			if (!string.IsNullOrEmpty(lobbyData))
			{
				snapshot = ParseModern(lobbyData, out modernRevision);
				lastModernPayload = lobbyData;
				MelonLogger.Msg("[" + modId + "] Host settings captured; live values follow the host, lobby values remain fixed.");
				return;
			}
			string lobbyData2 = SteamMatchmaking.GetLobbyData(lobby, lobbyKey);
			if (string.IsNullOrEmpty(lobbyData2))
			{
				Warn("Host snapshot is absent; vanilla settings are frozen for this session.");
				return;
			}
			snapshot = ParseSnapshot(lobbyData2);
			legacyHost = true;
			string[] obj = new string[5] { "[", modId, "] Host settings snapshot frozen for lobby ", null, null };
			CSteamID val = lobby;
			obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
			obj[4] = ".";
			MelonLogger.Msg(string.Concat(obj));
		}

		private void Refresh()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			try
			{
				EnsureCallback();
				CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(lobby);
				if (ownerChanged || owner == CSteamID.Nil || lobbyOwner != owner)
				{
					authorityValid = false;
					if (lobbyOwner != CSteamID.Nil && lobbyOwner != owner)
					{
						ownerChanged = true;
					}
					Warn("Lobby owner changed or is unavailable; authority revoked and frozen session settings retained.");
					return;
				}
				bool num = !authorityValid;
				authorityValid = true;
				flag = true;
				if (num)
				{
					MelonLogger.Msg("[" + modId + "] Pinned lobby authority recovered; frozen settings retained.");
				}
				if (isHost)
				{
					if (hostPayload != null)
					{
						if (published)
						{
							RefreshHostLive();
						}
						else
						{
							Publish();
						}
					}
				}
				else
				{
					RefreshClientLive();
				}
			}
			catch (Exception ex)
			{
				authorityValid = flag;
				Warn("Refresh lobby settings failed; frozen session settings retained: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private unsafe void Publish()
		{
			//IL_0001: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload && !SteamMatchmaking.SetLobbyData(lobby, lobbyKey, hostPayload))
			{
				CSteamID val = lobby;
				Warn("SetLobbyData rejected the settings snapshot for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
			}
			else if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload)
			{
				CSteamID val = lobby;
				Warn("Settings snapshot readback differs for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
			}
			else if (PublishModern(modernPayload))
			{
				published = true;
				Recover();
				string[] obj = new string[5] { "[", modId, "] Settings snapshot confirmed by local lobby readback for ", null, null };
				CSteamID val = lobby;
				obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
				obj[4] = ".";
				MelonLogger.Msg(string.Concat(obj));
			}
		}

		private Dictionary<string, object> ParseSnapshot(string payload)
		{
			if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
			{
				throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
			}
			string[] array = payload.Split('\n');
			if (array[0] != schemaVersion.ToString(CultureInfo.InvariantCulture))
			{
				throw new FormatException("Settings snapshot schema mismatch.");
			}
			if (array.Length != settings.FindAll((ISetting s) => s.Legacy).Count + 1)
			{
				throw new FormatException("Settings snapshot has missing or extra keys.");
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			for (int num = 1; num < array.Length; num++)
			{
				string text = array[num];
				int num2 = text.IndexOf('\t');
				if (num2 <= 0 || num2 != text.LastIndexOf('\t') || text.IndexOf('\r') >= 0)
				{
					throw new FormatException("Settings snapshot has an invalid entry delimiter.");
				}
				string key = text.Substring(0, num2);
				if (dictionary.ContainsKey(key))
				{
					throw new FormatException("Settings snapshot has a duplicate or missing key.");
				}
				ISetting setting = settings.Find((ISetting candidate) => candidate.Key == key && candidate.Legacy);
				if (setting == null)
				{
					throw new FormatException("Settings snapshot has an unknown key: " + key);
				}
				dictionary.Add(key, setting.Parse(text.Substring(num2 + 1)));
			}
			return dictionary;
		}

		private string BuildModern(Dictionary<string, object> values, long revision)
		{
			StringBuilder stringBuilder = new StringBuilder("2\t" + revision.ToString(CultureInfo.InvariantCulture));
			foreach (ISetting setting in settings)
			{
				stringBuilder.Append('\n').Append(setting.Key).Append('\t')
					.Append(Format(values[setting.Key]));
			}
			string text = stringBuilder.ToString();
			if (Encoding.UTF8.GetByteCount(text) + 1 > 8192)
			{
				throw new FormatException("Extended settings exceed the Steam metadata limit.");
			}
			return text;
		}

		private Dictionary<string, object> ParseModern(string payload, out long revision)
		{
			if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
			{
				throw new FormatException("Extended settings exceed the Steam metadata limit.");
			}
			string[] array = payload.Split('\n');
			string[] array2 = array[0].Split('\t');
			if (array2.Length != 2 || array2[0] != "2" || !long.TryParse(array2[1], NumberStyles.None, CultureInfo.InvariantCulture, out revision) || revision < 1)
			{
				throw new FormatException("Invalid extended settings schema or revision.");
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			for (int i = 1; i < array.Length; i++)
			{
				int num = array[i].IndexOf('\t');
				if (num <= 0 || num != array[i].LastIndexOf('\t') || array[i].IndexOf('\r') >= 0)
				{
					throw new FormatException("Invalid extended settings delimiter.");
				}
				string key = array[i].Substring(0, num);
				if (!hashSet.Add(key))
				{
					throw new FormatException("Duplicate extended setting: " + key);
				}
				ISetting setting = settings.Find((ISetting s) => s.Key == key);
				if (setting != null)
				{
					dictionary.Add(key, setting.Parse(array[i].Substring(num + 1)));
				}
			}
			return dictionary;
		}

		private bool PublishModern(string payload)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload && !SteamMatchmaking.SetLobbyData(lobby, modernKey, payload))
			{
				Warn("SetLobbyData rejected the extended settings; last confirmed values retained.");
				return false;
			}
			if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload)
			{
				Warn("Extended settings readback differs; last confirmed values retained.");
				return false;
			}
			return true;
		}

		private void RefreshHostLive()
		{
			if (snapshot == null)
			{
				return;
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(snapshot, StringComparer.Ordinal);
			bool flag = false;
			foreach (ISetting setting in settings)
			{
				if (setting.Live)
				{
					object localValue = setting.LocalValue;
					if (!dictionary.TryGetValue(setting.Key, out var value) || !object.Equals(value, localValue))
					{
						dictionary[setting.Key] = localValue;
						flag = true;
					}
				}
			}
			if (!flag)
			{
				if (PublishModern(modernPayload))
				{
					Recover();
				}
				return;
			}
			long revision = checked(modernRevision + 1);
			string payload = BuildModern(dictionary, revision);
			if (PublishModern(payload))
			{
				snapshot = dictionary;
				modernRevision = revision;
				modernPayload = payload;
				Recover();
				Changed(this.LiveValuesChanged);
			}
		}

		private void RefreshClientLive()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
			if (string.IsNullOrEmpty(lobbyData) || lobbyData == lastModernPayload)
			{
				return;
			}
			long revision;
			Dictionary<string, object> dictionary = ParseModern(lobbyData, out revision);
			if (revision <= modernRevision)
			{
				Warn("Extended settings revision did not advance; last confirmed values retained.");
				return;
			}
			Dictionary<string, object> dictionary2 = ((snapshot == null) ? new Dictionary<string, object>(StringComparer.Ordinal) : new Dictionary<string, object>(snapshot, StringComparer.Ordinal));
			bool flag = false;
			foreach (ISetting setting in settings)
			{
				if (setting.Live && dictionary.TryGetValue(setting.Key, out var value) && (!dictionary2.TryGetValue(setting.Key, out var value2) || !object.Equals(value2, value)))
				{
					dictionary2[setting.Key] = value;
					flag = true;
				}
			}
			snapshot = dictionary2;
			modernRevision = revision;
			lastModernPayload = lobbyData;
			Recover();
			if (flag)
			{
				Changed(this.LiveValuesChanged);
			}
		}

		private static string Format(object value)
		{
			if (value is string s)
			{
				return "s:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s));
			}
			if (value is float num)
			{
				return num.ToString("R", CultureInfo.InvariantCulture);
			}
			if (value is bool)
			{
				if (!(bool)value)
				{
					return "false";
				}
				return "true";
			}
			return ((int)value).ToString(CultureInfo.InvariantCulture);
		}

		private void Warn(string reason)
		{
			if (warningReason == reason)
			{
				warningRepeats++;
				return;
			}
			if (warningRepeats > 0)
			{
				MelonLogger.Warning("[" + modId + "] Previous settings warning repeated " + warningRepeats + " times.");
			}
			warningReason = reason;
			warningRepeats = 0;
			MelonLogger.Warning("[" + modId + "] " + reason);
		}

		private void Recover()
		{
			if (warningReason != null)
			{
				MelonLogger.Msg("[" + modId + "] Lobby settings recovered after " + (warningRepeats + 1) + " warning occurrences.");
				warningReason = null;
				warningRepeats = 0;
			}
		}

		private void Changed()
		{
			Changed(this.EffectiveValuesChanged);
		}

		private void Changed(Action handlers)
		{
			if (handlers == null)
			{
				return;
			}
			Delegate[] invocationList = handlers.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception ex)
				{
					MelonLogger.Error("[" + modId + "] Apply effective settings failed.", ex);
				}
			}
		}

		private void Reset()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			bool num = lobby != CSteamID.Nil;
			lobby = CSteamID.Nil;
			owner = CSteamID.Nil;
			isHost = false;
			hostCreated = false;
			authorityValid = false;
			ownerChanged = false;
			published = false;
			snapshot = null;
			hostPayload = null;
			modernPayload = null;
			lastModernPayload = null;
			modernRevision = 0L;
			legacyHost = false;
			warningReason = null;
			warningRepeats = 0;
			if (num)
			{
				MelonLogger.Msg("[" + modId + "] Session settings cleared; local settings restored.");
				Changed();
			}
		}
	}
	internal interface ISetting
	{
		string Key { get; }

		object LocalValue { get; }

		bool Live { get; }

		bool Legacy { get; }

		bool IsValid(object value);

		object Parse(string value);
	}
	internal sealed class HostSetting<T> : ISetting
	{
		private readonly HostSettings owner;

		private readonly MelonPreferences_Entry<T> entry;

		private readonly T vanillaValue;

		private readonly Func<T, bool> validate;

		private readonly Func<T> localValue;

		private bool invalidLocal;

		public bool Live { get; }

		public bool Legacy { get; }

		internal T Value => owner.GetValue(Key, (T)LocalValue, vanillaValue, Live);

		public string Key => ((MelonPreferences_Entry)entry).Identifier;

		public object LocalValue
		{
			get
			{
				T val = ((localValue == null) ? entry.Value : localValue());
				if (IsValid(val))
				{
					if (invalidLocal)
					{
						MelonLogger.Msg("Local setting recovered: " + Key);
					}
					invalidLocal = false;
					return val;
				}
				if (!invalidLocal)
				{
					MelonLogger.Warning("Invalid local setting " + Key + "; using its configured default without overwriting the file.");
				}
				invalidLocal = true;
				return entry.DefaultValue;
			}
		}

		internal HostSetting(HostSettings owner, MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate, bool live, bool legacy, Func<T> localValue)
		{
			this.owner = owner;
			this.entry = entry;
			this.vanillaValue = vanillaValue;
			this.validate = validate;
			this.localValue = localValue;
			Live = live;
			Legacy = legacy;
		}

		public bool IsValid(object value)
		{
			if (!(value is T arg))
			{
				return false;
			}
			if (value is float f && (float.IsNaN(f) || float.IsInfinity(f)))
			{
				return false;
			}
			if (value is string text && (Encoding.UTF8.GetByteCount(text) > 1024 || text.IndexOf('\0') >= 0))
			{
				return false;
			}
			if (validate != null)
			{
				return validate(arg);
			}
			return true;
		}

		public object Parse(string value)
		{
			object obj;
			int result2;
			float result3;
			if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result))
			{
				obj = result;
			}
			else if (typeof(T) == typeof(int) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
			{
				obj = result2;
			}
			else if (typeof(T) == typeof(float) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result3))
			{
				obj = result3;
			}
			else
			{
				if (!(typeof(T) == typeof(string)) || !value.StartsWith("s:", StringComparison.Ordinal))
				{
					throw new FormatException("Invalid settings value: " + Key);
				}
				obj = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(value.Substring(2)));
			}
			if (!IsValid(obj))
			{
				throw new FormatException("Settings value is out of range: " + Key);
			}
			return obj;
		}
	}
	internal static class ModVersion
	{
		internal const string Current = "1.1.1";
	}
}
namespace DooDesch.Weapons
{
	internal static class WeaponDefinition
	{
		internal const string Name = "DoubleBarrelPlus";

		internal static readonly int ItemId = 1500;

		internal static bool IsGun => true;

		internal static bool IsBat => false;

		internal static bool IsReload(int skill)
		{
			return skill == 10902;
		}

		internal static bool HasSkill(int skill)
		{
			if (skill != 10900)
			{
				return skill == 10902;
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class CombatConsumptionScope
	{
		[ThreadStatic]
		internal static EquipmentItemElement Active;

		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.DeclaredMethod(typeof(InventoryController), "OnUseSkill", (Type[])null, (Type[])null);
			yield return AccessTools.DeclaredMethod(typeof(InventoryController), "OnHitSkill", (Type[])null, (Type[])null);
			yield return AccessTools.DeclaredMethod(typeof(InventoryController), "OnUseSkill_SpawnProjectile", (Type[])null, (Type[])null);
		}

		private static void Prefix(InventoryController __instance, VCreature ____self, out EquipmentItemElement __state)
		{
			__state = Active;
			Active = ((____self is VPlayer) ? WeaponRuntime.Current(__instance) : null);
		}

		private static Exception Finalizer(EquipmentItemElement __state, Exception __exception)
		{
			Active = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(EquipmentItemElement), "SetDurability")]
	internal static class CombatWearPatch
	{
		private static void Prefix(EquipmentItemElement __instance, ref int durability)
		{
			if (CombatConsumptionScope.Active == __instance && WeaponPreferences.Unbreakable && durability < __instance.RemainDurability)
			{
				durability = __instance.RemainDurability;
			}
		}
	}
	[HarmonyPatch(typeof(EquipmentItemElement), "SetAmount")]
	internal static class CombatEnergyPatch
	{
		private static void Prefix(EquipmentItemElement __instance, ref int amount)
		{
			if (CombatConsumptionScope.Active == __instance && WeaponPreferences.InfiniteGauge && amount < __instance.RemainAmount)
			{
				amount = __instance.RemainAmount;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryController), "CanUseSkill")]
	internal static class WeaponValidationPatch
	{
		private static void Prefix(InventoryController __instance, VCreature ____self, int skillMasterID)
		{
			if (!(____self is VPlayer) || !WeaponPreferences.InfiniteGauge || !WeaponDefinition.HasSkill(skillMasterID))
			{
				return;
			}
			EquipmentItemElement val = WeaponRuntime.Current(__instance);
			if (val != null)
			{
				ItemEquipmentInfo val2 = WeaponRuntime.Info(val);
				if (val2 != null && val.RemainAmount < val2.MaxGauge)
				{
					val.SetAmount(val2.MaxGauge);
				}
			}
		}

		private static void Postfix(InventoryController __instance, VCreature ____self, int skillMasterID, ref MsgErrorCode __result)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Invalid comparison between Unknown and I4
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected I4, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Invalid comparison between Unknown and I4
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected I4, but got Unknown
			if (!(____self is VPlayer) || !WeaponDefinition.IsGun)
			{
				return;
			}
			if (WeaponPreferences.FiniteMagazine && (int)__result == 20000016 && WeaponDefinition.HasSkill(skillMasterID) && !WeaponDefinition.IsReload(skillMasterID) && WeaponRuntime.Current(__instance) != null)
			{
				MsgErrorCode val = __instance.HandleReloadWeapon();
				if ((int)val == 0)
				{
					__result = (MsgErrorCode)(int)__instance.CanUseSkill(skillMasterID);
				}
				else if ((int)val != 20000013)
				{
					__result = (MsgErrorCode)(int)val;
				}
			}
			else if (WeaponPreferences.InfiniteReserve && (int)__result == 20000013)
			{
				EquipmentItemElement val2 = WeaponRuntime.Current(__instance);
				ItemEquipmentInfo val3 = ((val2 == null) ? null : WeaponRuntime.Info(val2));
				if (val3 != null && (int)val2.PartsType == 1 && (int)val3.WeaponType == 1 && skillMasterID == val3.ReloadSkillMasterID && WeaponRuntime.Data.ExcelDataManager.GetSkillInfo(skillMasterID) != null)
				{
					__result = (MsgErrorCode)0;
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryController), "HandleReloadWeapon")]
	internal static class InfiniteReserveReloadPatch
	{
		private static bool Prefix(InventoryController __instance, VCreature ____self, ref MsgErrorCode __result)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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)
			//IL_00ba: Expected O, but got Unknown
			if (!(____self is VPlayer) || !WeaponPreferences.InfiniteReserve)
			{
				return true;
			}
			EquipmentItemElement val = WeaponRuntime.Current(__instance);
			ItemEquipmentInfo val2 = ((val == null) ? null : WeaponRuntime.Info(val));
			if (val2 == null || (int)val.PartsType != 1 || (int)val2.WeaponType != 1 || val2.ReloadSkillMasterID == 0 || WeaponRuntime.Data.ExcelDataManager.GetSkillInfo(val2.ReloadSkillMasterID) == null)
			{
				return true;
			}
			if (val.RemainAmount >= val2.MaxGauge)
			{
				__result = (MsgErrorCode)20000013;
				return false;
			}
			val.SetAmount(val2.MaxGauge);
			((VActor)____self).SendInSight((IMsg)new ReloadWeaponSig
			{
				actorID = ((VActor)____self).ObjectID,
				currentHandEquipmentInfo = ((ItemElement)val).toItemInfo(),
				inventoryInfos = __instance.GetInventoryInfos(),
				currentInvenSlotIndex = __instance.CurrentInventorySlot
			}, true);
			__result = (MsgErrorCode)0;
			return false;
		}
	}
	[HarmonyPatch(typeof(ProtoActor), "TryUseEquipSkill")]
	internal static class InfiniteGaugeInputPatch
	{
		private static bool Prefix(ProtoActor __instance, SkillHelper ___skillHelper, ItemEquipmentInfo equipMasterInfo, InventoryItem selectedItem, int skillIndex)
		{
			if ((!WeaponPreferences.InfiniteGauge && !WeaponPreferences.FiniteMagazine) || !__instance.AmIAvatar() || selectedItem == null || selectedItem.IsFake || selectedItem.ItemMasterID != WeaponDefinition.ItemId || skillIndex < 0 || skillIndex >= equipMasterInfo.SkillList.Length)
			{
				return true;
			}
			if (WeaponPreferences.FiniteMagazine && WeaponRuntime.RemainingAmmo(selectedItem, __instance.GetInventoryItems()).GetValueOrDefault() == 0L)
			{
				return true;
			}
			int skillMasterIDWithGauge = equipMasterInfo.SkillList[skillIndex].SkillMasterIDWithGauge;
			if (skillMasterIDWithGauge <= 0 || !WeaponDefinition.HasSkill(skillMasterIDWithGauge))
			{
				return true;
			}
			___skillHelper.UseSkill(skillMasterIDWithGauge, (ProtoActor)null);
			return false;
		}
	}
	[HarmonyPatch(typeof(UIPrefab_Inventory), "UpdateSlot")]
	internal static class WeaponDisplay
	{
		private static readonly FieldInfo InventoryUi = AccessTools.Field(typeof(GameMainBase), "inventoryui") ?? throw new MissingFieldException("GameMainBase", "inventoryui");

		private static bool refresh;

		internal static void RequestRefresh()
		{
			refresh = true;
		}

		internal static void Update()
		{
			if (refresh)
			{
				GameMainBase main = Hub.Main;
				ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
				if (!((Object)(object)val == (Object)null) && InventoryUi.GetValue(main) != null)
				{
					refresh = false;
					main.UpdateInventoryUI(val);
				}
			}
		}

		private static void Postfix(List<InventoryItem> inventoryItems, List<int> visibleSlotIndices, List<Slot> ___inventorySlots)
		{
			if (!WeaponPreferences.ShowInfinity || !WeaponPreferences.Enabled || inventoryItems == null || visibleSlotIndices == null)
			{
				return;
			}
			foreach (int visibleSlotIndex in visibleSlotIndices)
			{
				if (visibleSlotIndex < 0 || visibleSlotIndex >= inventoryItems.Count || visibleSlotIndex >= ___inventorySlots.Count)
				{
					continue;
				}
				InventoryItem val = inventoryItems[visibleSlotIndex];
				TMP_Text val2 = ___inventorySlots[visibleSlotIndex]?.stackCount;
				if (val != null && !val.IsFake && val.ItemMasterID == WeaponDefinition.ItemId && !((Object)(object)val2 == (Object)null))
				{
					if (WeaponPreferences.FiniteMagazine)
					{
						long? num = WeaponRuntime.RemainingAmmo(val, inventoryItems);
						val2.text = (num.HasValue ? ("∞/" + num.Value) : "∞");
					}
					else if (WeaponDefinition.IsGun && WeaponPreferences.AmmoMode == 1)
					{
						val2.text = val.RemainGauge + "/∞";
					}
					else if (WeaponPreferences.InfiniteGauge || (WeaponPreferences.Unbreakable && WeaponDefinition.IsBat))
					{
						val2.text = "∞";
					}
				}
			}
		}
	}
	internal static class WeaponPreferences
	{
		private static HostSettings settings;

		private static HostSetting<bool> enabled;

		private static HostSetting<bool> unbreakable;

		private static HostSetting<bool> infiniteEnergy;

		private static HostSetting<int> ammoMode;

		private static MelonPreferences_Entry<bool> showInfinity;

		private static HostSetting<float> damage;

		private static HostSetting<float> range;

		private static HostSetting<float> cooldown;

		private static HostSetting<float> smashDamage;

		private static HostSetting<float> smashCooldown;

		private static HostSetting<float> shockDuration;

		private static HostSetting<float> knockback;

		private static HostSetting<float> throwSpeed;

		private static HostSetting<float> throwDamage;

		private static HostSetting<float> spread;

		private static HostSetting<float> reloadTime;

		internal static bool Enabled => enabled?.Value ?? false;

		internal static bool Unbreakable
		{
			get
			{
				if (Enabled)
				{
					return unbreakable?.Value ?? false;
				}
				return false;
			}
		}

		internal static int AmmoMode
		{
			get
			{
				if (!Enabled)
				{
					return 0;
				}
				return ammoMode?.Value ?? 0;
			}
		}

		internal static bool InfiniteReserve
		{
			get
			{
				if (WeaponDefinition.IsGun)
				{
					if (AmmoMode != 1)
					{
						return AmmoMode == 2;
					}
					return true;
				}
				return false;
			}
		}

		internal static bool FiniteMagazine
		{
			get
			{
				if (WeaponDefinition.IsGun)
				{
					return AmmoMode == 3;
				}
				return false;
			}
		}

		internal static bool InfiniteGauge
		{
			get
			{
				if (Enabled)
				{
					if (!WeaponDefinition.IsGun)
					{
						return infiniteEnergy?.Value ?? false;
					}
					return AmmoMode == 2;
				}
				return false;
			}
		}

		internal static bool ShowInfinity => showInfinity?.Value ?? true;

		internal static float Range
		{
			get
			{
				if (!Enabled)
				{
					return 1f;
				}
				return range?.Value ?? 1f;
			}
		}

		internal static float ShockDuration
		{
			get
			{
				if (!Enabled)
				{
					return 1f;
				}
				return shockDuration?.Value ?? 1f;
			}
		}

		internal static float Knockback
		{
			get
			{
				if (!Enabled)
				{
					return 1f;
				}
				return knockback?.Value ?? 1f;
			}
		}

		internal static float ThrowSpeed
		{
			get
			{
				if (!Enabled)
				{
					return 1f;
				}
				return throwSpeed?.Value ?? 1f;
			}
		}

		internal static float ThrowDamage
		{
			get
			{
				if (!Enabled)
				{
					return 1f;
				}
				return throwDamage?.Value ?? 1f;
			}
		}

		internal static float Spread
		{
			get
			{
				if (!Enabled)
				{
					return 1f;
				}
				return spread?.Value ?? 1f;
			}
		}

		internal static float ReloadTime
		{
			get
			{
				if (!Enabled)
				{
					return 1f;
				}
				return reloadTime?.Value ?? 1f;
			}
		}

		internal static float Damage(int skill)
		{
			if (!Enabled)
			{
				return 1f;
			}
			return ((skill == 10870) ? smashDamage : damage)?.Value ?? 1f;
		}

		internal static float Cooldown(int skill)
		{
			if (!WeaponDefinition.IsReload(skill))
			{
				if (!Enabled)
				{
					return 1f;
				}
				return ((skill == 10870) ? smashCooldown : cooldown)?.Value ?? 1f;
			}
			return ReloadTime;
		}

		internal static void Initialize()
		{
			if (settings != null)
			{
				return;
			}
			MelonPreferences_Category category = MelonPreferences.CreateCategory("DoubleBarrelPlus");
			settings = new HostSettings("DoubleBarrelPlus");
			enabled = Flag("Enabled", initial: true, "Enable this weapon's changes. Clients use native behavior when the host does not have this mod.");
			unbreakable = Flag("Unbreakable", WeaponDefinition.IsBat, "Prevent combat durability loss for this weapon. Repairs and save values remain native.");
			if (WeaponDefinition.IsGun)
			{
				ammoMode = settings.Register(category.CreateEntry<int>("AmmoMode", 1, (string)null, "Host setting, live. 0 = native ammo, 1 = infinite reserve with normal reload, 2 = infinite magazine without reload, 3 = no reload with finite loaded and inventory ammo.", false, false, (ValueValidator)null, (string)null), 0, (int value) => value >= 0 && value <= 3, live: true, legacy: false);
				spread = Factor("SpreadMultiplier", "Multiply the native shotgun hit-cone angle. Range 0.1-4.", 0.1f, 4f);
				reloadTime = Factor("ReloadTimeMultiplier", "Multiply reload animation, ammo insertion timing and reload cooldown together. Smaller = faster. Range 0.25-4.", 0.25f, 4f);
			}
			if (WeaponDefinition.ItemId == 2040)
			{
				infiniteEnergy = Flag("InfiniteEnergy", initial: true, "Keep charged attacks available without consuming energy. Durability is controlled separately.");
			}
			damage = Factor("DamageMultiplier", "Multiply this weapon's attack damage. Range 0-10.");
			range = Factor("RangeMultiplier", "Multiply attack reach. Range 0.25-4.", 0.25f, 4f);
			cooldown = Factor("CooldownMultiplier", "Multiply the native skill cooldown, independently of animation length. Range 0.1-5.", 0.1f, 5f);
			if (WeaponDefinition.ItemId == 1600)
			{
				smashDamage = Factor("SmashDamageMultiplier", "Multiply Smash damage independently of the normal swing. Range 0-10.");
				smashCooldown = Factor("SmashCooldownMultiplier", "Multiply the Smash cooldown. Range 0.1-5.", 0.1f, 5f);
			}
			if (WeaponDefinition.ItemId == 2040)
			{
				shockDuration = Factor("ShockDurationMultiplier", "Multiply charged electric-shock duration. Range 0.1-5.", 0.1f, 5f);
			}
			if (WeaponDefinition.ItemId == 2900)
			{
				knockback = Factor("KnockbackMultiplier", "Multiply native swing knockback distance. Range 0-4.", 0f, 4f);
				throwSpeed = Factor("ThrowSpeedMultiplier", "Multiply initial throw speed. Native gravity and item recovery remain. Range 0.25-4.", 0.25f, 4f);
				throwDamage = Factor("ThrowDamageMultiplier", "Multiply thrown crowbar damage independently of the swing. Range 0-10.");
			}
			showInfinity = category.CreateEntry<bool>("ShowInfinity", true, (string)null, "Local display only. Show native weapon counts with an infinity symbol when the host enables infinite ammo or energy.", false, false, (ValueValidator)null, (string)null);
			settings.EffectiveValuesChanged += WeaponDisplay.RequestRefresh;
			settings.LiveValuesChanged += WeaponDisplay.RequestRefresh;
			((MelonEventBase<LemonAction<bool, bool>>)(object)showInfinity.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)delegate
			{
				WeaponDisplay.RequestRefresh();
			}, 0, false);
			HostSetting<float> Factor(string key, string description, float minimum = 0f, float maximum = 10f)
			{
				return settings.Register(category.CreateEntry<float>(key, 1f, (string)null, "Host setting, live for the next attack. 1 = native. " + description, false, false, (ValueValidator)null, (string)null), 1f, (float value) => !float.IsNaN(value) && !float.IsInfinity(value) && value >= minimum && value <= maximum, live: true, legacy: false);
			}
			HostSetting<bool> Flag(string key, bool initial, string description)
			{
				return settings.Register(category.CreateEntry<bool>(key, initial, (string)null, "Host setting, live. " + description, false, false, (ValueValidator)null, (string)null), vanillaValue: false, null, live: true, legacy: false);
			}
		}
	}
	internal static class WeaponReloadAnimation
	{
		private sealed class Track
		{
			internal Animator Animator;

			internal ProtoActor Actor;

			internal SkillHelper Helper;

			internal int Skill;

			internal float Before;

			internal float Applied;
		}

		private static readonly List<Track> Tracks = new List<Track>();

		private static readonly FieldInfo HelperField = AccessTools.Field(typeof(ProtoActor), "skillHelper") ?? throw new MissingFieldException("ProtoActor", "skillHelper");

		internal static void Release(Animator animator)
		{
			for (int num = Tracks.Count - 1; num >= 0; num--)
			{
				if ((Object)(object)Tracks[num].Animator == (Object)(object)animator)
				{
					Restore(Tracks[num]);
					Tracks.RemoveAt(num);
				}
			}
		}

		internal static void Begin(Animator animator, ProtoActor actor, int skill)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			if (!((Object)(object)animator == (Object)null) && !((Object)(object)actor == (Object)null) && (int)actor.ActorType == 1 && WeaponDefinition.IsReload(skill) && WeaponPreferences.ReloadTime != 1f)
			{
				SkillHelper val = (SkillHelper)HelperField.GetValue(actor);
				if (val == null)
				{
					throw new InvalidOperationException("Reload animation: player SkillHelper is missing.");
				}
				Release(animator);
				Track track = new Track
				{
					Animator = animator,
					Actor = actor,
					Helper = val,
					Skill = skill,
					Before = animator.speed,
					Applied = animator.speed / WeaponPreferences.ReloadTime
				};
				animator.speed = track.Applied;
				Tracks.Add(track);
			}
		}

		internal static void Update()
		{
			for (int num = Tracks.Count - 1; num >= 0; num--)
			{
				Track track = Tracks[num];
				InventoryItem val = (((Object)(object)track.Actor == (Object)null) ? null : track.Actor.GetSelectedInventoryItem());
				if (!((Object)(object)track.Animator != (Object)null) || !((Object)(object)track.Actor != (Object)null) || ((val != null) ? new int?(val.ItemMasterID) : ((int?)null)) != WeaponDefinition.ItemId || track.Helper.currentCastingSkillMasterIdCached != track.Skill || !track.Helper.IsUsingSkill())
				{
					Restore(track);
					Tracks.RemoveAt(num);
				}
			}
		}

		private static void Restore(Track track)
		{
			if (!((Object)(object)track.Animator == (Object)null))
			{
				if (Mathf.Approximately(track.Animator.speed, track.Applied))
				{
					track.Animator.speed = track.Before;
				}
				else
				{
					MelonLogger.Warning(string.Format("[{0}] Reload animation speed changed externally; preserving current speed {1}.", "DoubleBarrelPlus", track.Animator.speed));
				}
			}
		}

		private static ImmutableArray<T> Events<T>(ImmutableArray<T> original, float factor) where T : AnimNotifyInfo
		{
			ImmutableArray<T>.Builder builder = ImmutableArray.CreateBuilder<T>(original.Length);
			ImmutableArray<T>.Enumerator enumerator = original.GetEnumerator();
			while (enumerator.MoveNext())
			{
				T current = enumerator.Current;
				T val = WeaponCopies.Copy<T>(current);
				WeaponCopies.Set<AnimNotifyInfo>((AnimNotifyInfo)(object)val, "Start", ((AnimNotifyInfo)current).Start * (double)factor);
				object obj = val;
				AnimNotifyHitCheck val2 = (AnimNotifyHitCheck)((obj is AnimNotifyHitCheck) ? obj : null);
				if (val2 != null)
				{
					WeaponCopies.Set<AnimNotifyHitCheck>(val2, "Duration", val2.Duration * (double)factor);
				}
				else
				{
					object obj2 = val;
					AnimNotifyPickedHit val3 = (AnimNotifyPickedHit)((obj2 is AnimNotifyPickedHit) ? obj2 : null);
					if (val3 != null)
					{
						WeaponCopies.Set<AnimNotifyPickedHit>(val3, "Duration", val3.Duration * (double)factor);
					}
					else
					{
						object obj3 = val;
						AnimNotifyNextSkill val4 = (AnimNotifyNextSkill)((obj3 is AnimNotifyNextSkill) ? obj3 : null);
						if (val4 != null)
						{
							WeaponCopies.Set<AnimNotifyNextSkill>(val4, "Duration", val4.Duration * (double)factor);
						}
					}
				}
				builder.Add(val);
			}
			return builder.ToImmutable();
		}

		internal static SkillAnimInfo Scale(SkillAnimInfo original, float factor)
		{
			SkillAnimInfo obj = WeaponCopies.Copy<SkillAnimInfo>(original);
			WeaponCopies.Set<SkillAnimInfo>(obj, "Length", original.Length * (double)factor);
			WeaponCopies.Set<SkillAnimInfo>(obj, "ReloadWeapons", Events<AnimNotifyReloadWeapon>(original.ReloadWeapons, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "AllowMoves", Events<AnimNotifyAllowMove>(original.AllowMoves, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "NextSkills", Events<AnimNotifyNextSkill>(original.NextSkills, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "HitChecks", Events<AnimNotifyHitCheck>(original.HitChecks, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "PickedHits", Events<AnimNotifyPickedHit>(original.PickedHits, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "Projectiles", Events<AnimNotifyProjectile>(original.Projectiles, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "FieldSkills", Events<AnimNotifyFieldSkill>(original.FieldSkills, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "WeaponDestroyes", Events<AnimNotifyDestroyWeapon>(original.WeaponDestroyes, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "ImmuneAppliers", Events<AnimNotifyImmuneApplier>(original.ImmuneAppliers, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "ActivateAuras", Events<AnimNotifyHandleAura>(original.ActivateAuras, factor));
			WeaponCopies.Set<SkillAnimInfo>(obj, "DestroyLootingObjects", Events<AnimNotifyDestroyLootingObject>(original.DestroyLootingObjects, factor));
			return obj;
		}
	}
	[HarmonyPatch(typeof(SkillHelper), "OnNetUseSkillSig")]
	internal static class ReloadClientDurationPatch
	{
		private static void Postfix(ProtoActor ___owner, UseSkillSig sig, ref double ____usingSkillEndTime)
		{
			InventoryItem selectedInventoryItem = ___owner.GetSelectedInventoryItem();
			if (((selectedInventoryItem != null) ? new int?(selectedInventoryItem.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !selectedInventoryItem.IsFake && WeaponDefinition.IsReload(sig.skillMasterID))
			{
				____usingSkillEndTime = Time.timeAsDouble + (____usingSkillEndTime - Time.timeAsDouble) * (double)WeaponPreferences.ReloadTime;
			}
		}
	}
	[HarmonyPatch(typeof(PuppetScript), "UseSkill")]
	internal static class ReloadPuppetAnimationPatch
	{
		private static void Prefix(Animator ___anim)
		{
			WeaponReloadAnimation.Release(___anim);
		}

		private static void Postfix(PuppetScript __instance, Animator ___anim, int skillMasterID)
		{
			ProtoActor owner = __instance.Owner;
			InventoryItem val = ((owner != null) ? owner.GetSelectedInventoryItem() : null);
			if (((val != null) ? new int?(val.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !val.IsFake)
			{
				WeaponReloadAnimation.Begin(___anim, __instance.Owner, skillMasterID);
			}
		}
	}
	[HarmonyPatch(typeof(SocketAttachable), "OnReload")]
	internal static class ReloadWeaponAnimationPatch
	{
		private static readonly PropertyInfo Owner = AccessTools.Property(typeof(SocketAttachable), "owner") ?? throw new MissingMemberException("SocketAttachable", "owner");

		private static void Postfix(SocketAttachable __instance, Animator ___animator)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (WeaponDefinition.IsGun)
			{
				ProtoActor val = (ProtoActor)Owner.GetValue(__instance);
				InventoryItem val2 = ((val != null) ? val.GetSelectedInventoryItem() : null);
				if (((val2 != null) ? new int?(val2.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !val2.IsFake)
				{
					ItemMasterInfo masterInfo = val2.MasterInfo;
					WeaponReloadAnimation.Begin(___animator, val, ((ItemEquipmentInfo)(((masterInfo is ItemEquipmentInfo) ? masterInfo : null)?)).ReloadSkillMasterID ?? 0);
				}
			}
		}
	}
	internal static class WeaponRuntime
	{
		private static readonly PropertyInfo DataManagerProperty = AccessTools.Property(typeof(Hub), "dataman") ?? throw new MissingMemberException("Hub", "dataman");

		internal static DataManager Data
		{
			get
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Expected O, but got Unknown
				if (!((Object)(object)Hub.s == (Object)null))
				{
					return (DataManager)DataManagerProperty.GetValue(Hub.s);
				}
				return null;
			}
		}

		internal static EquipmentItemElement Current(InventoryController inventory)
		{
			if (!WeaponPreferences.Enabled || inventory == null)
			{
				return null;
			}
			if (inventory.GetAllItemElements().TryGetValue(inventory.CurrentInventorySlot, out var value))
			{
				EquipmentItemElement val = (EquipmentItemElement)(object)((value is EquipmentItemElement) ? value : null);
				if (val != null && !((ItemElement)val).IsFake && ((ItemElement)val).ItemMasterID == WeaponDefinition.ItemId)
				{
					return val;
				}
			}
			return null;
		}

		internal static bool Matches(VCreature actor, int skill)
		{
			if (actor is VPlayer && Current(((VActor)actor).InventoryControlUnit) != null)
			{
				return WeaponDefinition.HasSkill(skill);
			}
			return false;
		}

		internal static ItemEquipmentInfo Info(EquipmentItemElement item)
		{
			DataManager data = Data;
			ItemMasterInfo obj = ((data != null) ? data.ExcelDataManager.GetItemInfo(((ItemElement)item).ItemMasterID) : null);
			return (ItemEquipmentInfo)(object)((obj is ItemEquipmentInfo) ? obj : null);
		}

		internal static long? RemainingAmmo(InventoryItem weapon, List<InventoryItem> inventory)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			//IL_0092: 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)
			ItemMasterInfo obj = ((weapon != null) ? weapon.MasterInfo : null);
			ItemEquipmentInfo val = (ItemEquipmentInfo)(object)((obj is ItemEquipmentInfo) ? obj : null);
			if (val == null || inventory == null)
			{
				return null;
			}
			DataManager data = Data;
			SkillInfo val2 = ((data != null) ? data.ExcelDataManager.GetSkillInfo(val.ReloadSkillMasterID) : null);
			if (val2 == null)
			{
				return null;
			}
			long num = Math.Max(0, weapon.RemainGauge);
			foreach (InventoryItem item in inventory)
			{
				ItemMasterInfo obj2 = ((item != null) ? item.MasterInfo : null);
				ItemConsumableInfo val3 = (ItemConsumableInfo)(object)((obj2 is ItemConsumableInfo) ? obj2 : null);
				if (val3 != null && (int)val3.ConsumeType == 1 && val3.BulletType == val2.BulletType)
				{
					num += Math.Max(0, item.StackCount);
				}
			}
			return num;
		}
	}
	internal static class WeaponCopies
	{
		private static readonly MethodInfo MemberwiseCopy = AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null);

		private static readonly Dictionary<(Type, string), FieldInfo> Fields = new Dictionary<(Type, string), FieldInfo>();

		internal static T Copy<T>(T source) where T : class
		{
			return (T)MemberwiseCopy.Invoke(source, null);
		}

		internal static void Set<T>(T target, string name, object value) where T : class
		{
			(Type, string) key = (typeof(T), name);
			if (!Fields.TryGetValue(key, out var value2))
			{
				value2 = AccessTools.Field(typeof(T), name) ?? throw new MissingFieldException(typeof(T).FullName, name);
				Fields.Add(key, value2);
			}
			value2.SetValue(target, value);
		}

		internal static long Scale(long value, float factor)
		{
			return checked((long)Math.Round((double)value * (double)factor, MidpointRounding.AwayFromZero));
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class WeaponAttackContextPatch
	{
		internal sealed class AttackValues
		{
			internal float Range;

			internal float Spread;

			internal float Damage;
		}

		private static readonly ConditionalWeakTable<SkillContextBaseInfo, AttackValues> Attacks = new ConditionalWeakTable<SkillContextBaseInfo, AttackValues>();

		internal static AttackValues For(SkillContextBaseInfo context)
		{
			if (!Attacks.TryGetValue(context, out var value))
			{
				return null;
			}
			return value;
		}

		internal static void CopyValues(SkillContextBaseInfo source, SkillContextBaseInfo copy)
		{
			AttackValues attackValues = For(source);
			if (attackValues != null)
			{
				Attacks.Add(copy, attackValues);
			}
		}

		private static void Prefix(VCreature actor, ref SkillInfo skillInfo, ref SkillAnimInfo animInfo)
		{
			if (skillInfo == null || !WeaponRuntime.Matches(actor, skillInfo.MasterID))
			{
				return;
			}
			if (WeaponDefinition.IsReload(skillInfo.MasterID))
			{
				if (animInfo != null && WeaponPreferences.ReloadTime != 1f)
				{
					animInfo = WeaponReloadAnimation.Scale(animInfo, WeaponPreferences.ReloadTime);
				}
			}
			else if (WeaponPreferences.Range != 1f)
			{
				SkillInfo val = WeaponCopies.Copy<SkillInfo>(skillInfo);
				WeaponCopies.Set<SkillInfo>(val, "MaxRange", skillInfo.MaxRange * WeaponPreferences.Range);
				WeaponCopies.Set<SkillInfo>(val, "AllowRange", skillInfo.AllowRange * WeaponPreferences.Range);
				skillInfo = val;
			}
		}

		private static void Postfix(SkillContextBaseInfo __instance)
		{
			if (WeaponRuntime.Matches(__instance.Creature, __instance.SkillInfo.MasterID))
			{
				Attacks.Add(__instance, new AttackValues
				{
					Range = WeaponPreferences.Range,
					Spread = (WeaponDefinition.IsGun ? WeaponPreferences.Spread : 1f),
					Damage = WeaponPreferences.Damage(__instance.SkillInfo.MasterID)
				});
			}
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class WeaponAttackContextCopyPatch
	{
		private static void Postfix(SkillContextBaseInfo __instance, SkillContextBaseInfo other)
		{
			WeaponAttackContextPatch.CopyValues(other, __instance);
		}
	}
	[HarmonyPatch(typeof(SkillContext<UseSkillSig>), "OnHit")]
	internal static class WeaponDamageScope
	{
		[ThreadStatic]
		internal static VActor Actor;

		[ThreadStatic]
		internal static float Factor;

		[ThreadStatic]
		internal static int Sequence;

		private static void Prefix(ISkillContext __instance, HitInputData targetPack, out (VActor, float, int) __state)
		{
			__state = (Actor, Factor, Sequence);
			WeaponAttackContextPatch.AttackValues attackValues = WeaponAttackContextPatch.For(__instance.ContextInfo);
			Actor = (VActor)(object)((attackValues == null) ? null : __instance.ContextInfo.Creature);
			Factor = attackValues?.Damage ?? 1f;
			Sequence = targetPack.SkillSequenceInfo.MasterID;
		}

		private static Exception Finalizer((VActor, float, int) __state, Exception __exception)
		{
			(Actor, Factor, Sequence) = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(VWorldCombatUtil), "CalculateDamage")]
	internal static class WeaponDamagePatch
	{
		private static void Postfix(VActor attacker, int skillSeqMasterID, ref DamageAttribute __result)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			if (attacker != null && attacker == WeaponDamageScope.Actor && skillSeqMasterID == WeaponDamageScope.Sequence && WeaponDamageScope.Factor != 1f && __result != null)
			{
				__result = new DamageAttribute(WeaponCopies.Scale(__result.Damage, WeaponDamageScope.Factor), __result.GroggyValue, __result.Critical);
			}
		}
	}
	[HarmonyPatch(typeof(CooltimeController), "AddCooltime")]
	internal static class WeaponCooldownPatch
	{
		private static void Prefix(VActor ____self, CooltimeType type, int masterID, ref long idCooltime)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if ((int)type == 0)
			{
				VCreature val = (VCreature)(object)((____self is VCreature) ? ____self : null);
				if (val != null && WeaponRuntime.Matches(val, masterID))
				{
					idCooltime = WeaponCopies.Scale(idCooltime, WeaponPreferences.Cooldown(masterID));
				}
			}
		}
	}
	[HarmonyPatch(typeof(SkillHelper), "ReserveEndSkillCoolTime")]
	internal static class WeaponClientCooldownPatch
	{
		private static void Prefix(ProtoActor ___owner, int skillMasterID, ref float durationSec)
		{
			InventoryItem selectedInventoryItem = ___owner.GetSelectedInventoryItem();
			if (((selectedInventoryItem != null) ? new int?(selectedInventoryItem.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !selectedInventoryItem.IsFake && WeaponDefinition.HasSkill(skillMasterID))
			{
				durationSec *= WeaponPreferences.Cooldown(skillMasterID);
			}
		}
	}
	[HarmonyPatch]
	internal static class WeaponHitboxScope
	{
		[ThreadStatic]
		internal static float Range;

		[ThreadStatic]
		internal static float Spread;

		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = typeof(SkillContext<UseSkillSig>);
			yield return AccessTools.DeclaredMethod(type, "CollectSkillTargetInfoByHit", (Type[])null, (Type[])null);
			yield return AccessTools.DeclaredMethod(type, "DestroyHitDestructibleProjectiles", (Type[])null, (Type[])null);
		}

		private static void Prefix(ISkillContext __instance, out (float, float) __state)
		{
			__state = (Range, Spread);
			WeaponAttackContextPatch.AttackValues attackValues = WeaponAttackContextPatch.For(__instance.ContextInfo);
			Range = attackValues?.Range ?? 1f;
			Spread = attackValues?.Spread ?? 1f;
		}

		private static Exception Finalizer((float, float) __state, Exception __exception)
		{
			(Range, Spread) = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(AnimNotifyDataManager), "GetHitBox")]
	internal static class WeaponHitboxPatch
	{
		private static void Postfix(ref IHitCheck __result)
		{
			//IL_003e: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: 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_0184: Expected O, but got Unknown
			float range = WeaponHitboxScope.Range;
			float spread = WeaponHitboxScope.Spread;
			if (range == 0f || (range == 1f && (spread == 0f || spread == 1f)) || __result == null)
			{
				return;
			}
			IHitCheck obj = __result;
			FanHitCheck val = (FanHitCheck)(object)((obj is FanHitCheck) ? obj : null);
			if (val != null)
			{
				__result = (IHitCheck)new FanHitCheck(val.Center * range, val.Rotation, val.Height, val.InnerRad * range, val.OuterRad * range, Mathf.Clamp(val.Angle * ((spread == 0f) ? 1f : spread), 1f, 360f), val.Key);
				return;
			}
			IHitCheck obj2 = __result;
			CubeHitCheck val2 = (CubeHitCheck)(object)((obj2 is CubeHitCheck) ? obj2 : null);
			if (val2 != null)
			{
				__result = (IHitCheck)new CubeHitCheck(val2.Center * range, val2.Extent * range, val2.Rotation, val2.Key);
				return;
			}
			IHitCheck obj3 = __result;
			SphereHitCheck val3 = (SphereHitCheck)(object)((obj3 is SphereHitCheck) ? obj3 : null);
			if (val3 != null)
			{
				__result = (IHitCheck)new SphereHitCheck(val3.Center * range, val3.Rad * range, val3.Key);
				return;
			}
			IHitCheck obj4 = __result;
			CapsuleHitCheck val4 = (CapsuleHitCheck)(object)((obj4 is CapsuleHitCheck) ? obj4 : null);
			if (val4 != null)
			{
				__result = (IHitCheck)new CapsuleHitCheck(val4.Rad * range, val4.Length * range, val4.Center * range, val4.Rotation, val4.Key);
				return;
			}
			IHitCheck obj5 = __result;
			TorusHitCheck val5 = (TorusHitCheck)(object)((obj5 is TorusHitCheck) ? obj5 : null);
			if (val5 != null)
			{
				__result = (IHitCheck)new TorusHitCheck(val5.Center * range, val5.Height, val5.OuterRad * range, val5.InnerRad * range, val5.Key);
				return;
			}
			throw new NotSupportedException("Weapon reach: unsupported hitbox " + ((object)__result).GetType().FullName);
		}
	}
	[HarmonyPatch(typeof(AbnormalController), "ApplyAbnormalInternal")]
	internal static class WeaponAbnormalPatch
	{
		private static void Prefix(AbnormalController __instance, int casterObjectID, int skillMasterID, ref AbnormalInfo info, ref int duration)
		{
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Invalid comparison between Unknown and I4
			VActor obj = ((VActor)__instance.Self).VRoom.FindActorByObjectID(casterObjectID);
			if (!WeaponRuntime.Matches((VCreature)(object)((obj is VCreature) ? obj : null), skillMasterID))
			{
				return;
			}
			if (WeaponDefinition.ItemId == 2040 && skillMasterID == 10850 && info.MasterID == 24001 && WeaponPreferences.ShockDuration != 1f)
			{
				duration = checked((int)WeaponCopies.Scale((duration > 0) ? duration : info.MaxDuration, WeaponPreferences.ShockDuration));
			}
			if (WeaponDefinition.ItemId != 2900 || skillMasterID != 10806 || info.MasterID != 18006 || (WeaponPreferences.Knockback == 1f && WeaponPreferences.Damage(skillMasterID) == 1f))
			{
				return;
			}
			ImmutableDictionary<int, IAbnormalElementInfo>.Builder builder = info.ElementList.ToBuilder();
			foreach (KeyValuePair<int, IAbnormalElementInfo> element in info.ElementList)
			{
				IAbnormalElementInfo value = element.Value;
				CCElementInfo val = (CCElementInfo)(object)((value is CCElementInfo) ? value : null);
				if (val != null && val.BattleActionInfo != null && WeaponPreferences.Knockback != 1f)
				{
					BattleActionInfo val2 = WeaponCopies.Copy<BattleActionInfo>(val.BattleActionInfo);
					WeaponCopies.Set<BattleActionInfo>(val2, "Distance", val2.Distance * WeaponPreferences.Knockback);
					CCElementInfo val3 = WeaponCopies.Copy<CCElementInfo>(val);
					WeaponCopies.Set<CCElementInfo>(val3, "BattleActionInfo", val2);
					builder[element.Key] = (IAbnormalElementInfo)(object)val3;
					continue;
				}
				IAbnormalElementInfo value2 = element.Value;
				AbnormalStatsElementInfo val4 = (AbnormalStatsElementInfo)(object)((value2 is AbnormalStatsElementInfo) ? value2 : null);
				if (val4 != null && (int)val4.MutableStatType == 1 && val4.Value < 0)
				{
					AbnormalStatsElementInfo val5 = WeaponCopies.Copy<AbnormalStatsElementInfo>(val4);
					WeaponCopies.Set<AbnormalStatsElementInfo>(val5, "Value", WeaponCopies.Scale(val4.Value, WeaponPreferences.Damage(skillMasterID)));
					builder[element.Key] = (IAbnormalElementInfo)(object)val5;
				}
			}
			info = WeaponCopies.Copy<AbnormalInfo>(info);
			WeaponCopies.Set<AbnormalInfo>(info, "ElementList", builder.ToImmutable());
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class CrowbarProjectilePatch
	{
		private static void Postfix(int masterID, ISkillContext skillContext, ref ProjectileInfo ____projectileInfo, ref float ____speed, ref Vector3 ____initialVector)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (WeaponDefinition.ItemId == 2900 && masterID == 52900 && skillContext != null && WeaponRuntime.Matches(skillContext.ContextInfo.Creature, skillContext.SkillMasterID))
			{
				if (WeaponPreferences.ThrowSpeed != 1f)
				{
					____speed *= WeaponPreferences.ThrowSpeed;
					____initialVector *= WeaponPreferences.ThrowSpeed;
				}
				if (WeaponPreferences.ThrowDamage != 1f)
				{
					ProjectileInfo val = WeaponCopies.Copy<ProjectileInfo>(____projectileInfo);
					WeaponCopies.Set<ProjectileInfo>(val, "MutableValue", WeaponCopies.Scale(val.MutableValue, WeaponPreferences.ThrowDamage));
					____projectileInfo = val;
				}
			}
		}
	}
}
namespace DoubleBarrelPlus
{
	public sealed class Core : MelonMod
	{
		public override void OnInitializeMelon()
		{
			WeaponPreferences.Initialize();
			MelonLogger.Msg("DoubleBarrelPlus initialized for item 1500. Gameplay follows the host.");
		}

		public override void OnUpdate()
		{
			HostSettings.Update();
			WeaponDisplay.Update();
			WeaponReloadAnimation.Update();
		}
	}
}