Decompiled source of balrond DualMastery v0.3.14

plugins/BalrondDualMastery.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Balrond.DualMastery.Assets;
using Balrond.DualMastery.Compatibility;
using Balrond.DualMastery.Configuration;
using Balrond.DualMastery.Core;
using Balrond.DualMastery.Patches;
using Balrond.DualMastery.Progression;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using LitJson2;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("BalrondDualMastery")]
[assembly: AssemblyDescription("Independent paired-weapon combat implementation for Valheim")]
[assembly: AssemblyCompany("Balrond")]
[assembly: AssemblyProduct("BalrondDualMastery")]
[assembly: ComVisible(false)]
[assembly: Guid("A8C95E47-F3B3-4AF1-B4BD-544392311DA9")]
[assembly: AssemblyFileVersion("0.3.14.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.14.0")]
[module: UnverifiableCode]
namespace Balrond.DualMastery
{
	[BepInPlugin("balrond.astafaraios.BalrondDualMastery", "BalrondDualMastery", "0.3.14")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "balrond.astafaraios.BalrondDualMastery";

		public const string PluginName = "BalrondDualMastery";

		public const string PluginVersion = "0.3.14";

		public static readonly JsonLoader jsonLoader = new JsonLoader();

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			SkillsReworkedCompatibility.InitializePresence();
			ModSettings.Bind(((BaseUnityPlugin)this).Config);
			jsonLoader.loadJson();
			ContentPack.Load();
			MasterySkill.Initialize();
			_harmony = new Harmony("balrond.astafaraios.BalrondDualMastery");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			RuntimePatchInstaller.Install(_harmony);
			SkillsReworkedSkillNameResolverBridge.Install(_harmony);
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded.", "BalrondDualMastery", "0.3.14"));
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	public class BalrondTranslator
	{
		public static Dictionary<string, Dictionary<string, string>> translations = new Dictionary<string, Dictionary<string, string>>();

		public static Dictionary<string, string> getLanguage(string language)
		{
			if (string.IsNullOrEmpty(language))
			{
				return null;
			}
			if (translations.TryGetValue(language, out var value))
			{
				return value;
			}
			return null;
		}
	}
	public class JsonLoader
	{
		public string defaultPath = string.Empty;

		public void loadJson()
		{
			LoadTranslations();
			justDefaultPath();
		}

		public void justDefaultPath()
		{
			string configPath = Paths.ConfigPath;
			string text = Path.Combine(configPath, "BalrondDualMastery-translation/");
			defaultPath = text;
		}

		public void createDefaultPath()
		{
			string configPath = Paths.ConfigPath;
			string text = Path.Combine(configPath, "BalrondDualMastery-translation/");
			if (!Directory.Exists(text))
			{
				CreateFolder(text);
			}
			else
			{
				Debug.Log((object)("BalrondDualMastery: Folder already exists: " + text));
			}
			defaultPath = text;
		}

		private string[] jsonFilePath(string folderName, string extension)
		{
			string configPath = Paths.ConfigPath;
			string text = Path.Combine(configPath, "BalrondDualMastery-translation/");
			if (!Directory.Exists(text))
			{
				CreateFolder(text);
			}
			else
			{
				Debug.Log((object)("BalrondDualMastery: Folder already exists: " + text));
			}
			string[] files = Directory.GetFiles(text, extension);
			Debug.Log((object)("BalrondDualMastery:" + folderName + " Json Files Found: " + files.Length));
			return files;
		}

		private static void CreateFolder(string path)
		{
			try
			{
				Directory.CreateDirectory(path);
				Debug.Log((object)"BalrondDualMastery: Folder created successfully.");
			}
			catch (Exception ex)
			{
				Debug.Log((object)("BalrondDualMastery: Error creating folder: " + ex.Message));
			}
		}

		private void LoadTranslations()
		{
			int num = 0;
			string[] array = jsonFilePath("Translation", "*.json");
			foreach (string text in array)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
				string json = File.ReadAllText(text);
				JsonData jsonData = JsonMapper.ToObject(json);
				Dictionary<string, string> dictionary = new Dictionary<string, string>();
				foreach (string key in jsonData.Keys)
				{
					dictionary[key] = jsonData[key].ToString();
				}
				if (dictionary != null)
				{
					BalrondTranslator.translations.Add(fileNameWithoutExtension, dictionary);
					Debug.Log((object)("BalrondDualMastery: Json Files Language: " + fileNameWithoutExtension));
					num++;
				}
				else
				{
					Debug.LogError((object)("BalrondDualMastery: Loading FAILED file: " + text));
				}
			}
			Debug.Log((object)("BalrondDualMastery: Translation JsonFiles Loaded: " + num));
		}
	}
	[HarmonyPatch]
	internal static class TranslationPatches
	{
		[HarmonyPatch(typeof(FejdStartup), "SetupGui")]
		private class FejdStartup_SetupGUI
		{
			private static void Postfix()
			{
				string selectedLanguage = Localization.instance.GetSelectedLanguage();
				Dictionary<string, string> translations = GetTranslations(selectedLanguage);
				AddTranslations(translations);
			}
		}

		[HarmonyPriority(800)]
		[HarmonyPatch(typeof(Localization), "SetupLanguage")]
		private class Translation_SetupLanguage
		{
			private static void Prefix(Localization __instance, string language)
			{
				Dictionary<string, string> translations = GetTranslations(language);
				AddTranslations(translations, __instance);
			}
		}

		[HarmonyPriority(800)]
		[HarmonyPatch(typeof(Localization), "LoadCSV")]
		private class Translation_LoadCSV
		{
			private static void Prefix(Localization __instance, string language)
			{
				Dictionary<string, string> translations = GetTranslations(language);
				AddTranslations(translations, __instance);
			}
		}

		private static Dictionary<string, string> GetTranslations(string language)
		{
			Dictionary<string, string> result = BalrondTranslator.getLanguage("English");
			if (!string.Equals(language, "English", StringComparison.OrdinalIgnoreCase))
			{
				Dictionary<string, string> language2 = BalrondTranslator.getLanguage(language);
				if (language2 != null)
				{
					result = language2;
				}
				else
				{
					Debug.Log((object)("BalrondDualMastery: Did not find translation file for '" + language + "', loading English"));
				}
			}
			return result;
		}

		private static void AddTranslations(Dictionary<string, string> translations, Localization localizationInstance = null)
		{
			if (translations == null)
			{
				Debug.LogWarning((object)"BalrondDualMastery: No translation file found!");
				return;
			}
			if (localizationInstance != null)
			{
				foreach (KeyValuePair<string, string> translation in translations)
				{
					localizationInstance.AddWord(translation.Key, translation.Value);
				}
				return;
			}
			foreach (KeyValuePair<string, string> translation2 in translations)
			{
				Localization.instance.AddWord(translation2.Key, translation2.Value);
			}
		}
	}
}
namespace Balrond.DualMastery.Runtime
{
	internal sealed class HolsterBaseline : MonoBehaviour
	{
		private bool _captured;

		private Vector3 _localPosition;

		private Quaternion _localRotation;

		internal void Capture()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (!_captured)
			{
				_localPosition = ((Component)this).transform.localPosition;
				_localRotation = ((Component)this).transform.localRotation;
				_captured = true;
			}
		}

		internal void ApplySeparation()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			Capture();
			((Component)this).transform.localPosition = _localPosition + new Vector3(-0.022f, 0.01f, 0.016f);
			((Component)this).transform.localRotation = _localRotation * Quaternion.Euler(-5f, 56f, -7f);
		}

		internal void Restore()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (_captured)
			{
				((Component)this).transform.localPosition = _localPosition;
				((Component)this).transform.localRotation = _localRotation;
			}
		}
	}
}
namespace Balrond.DualMastery.Progression
{
	internal static class MasterySkill
	{
		internal const int NumericId = 874298;

		internal static readonly SkillType Type = (SkillType)874298;

		internal const string DisplayName = "Dual Mastery";

		internal const string DescriptionKey = "$balrond_dualmastery_tooltip";

		private static SkillDef _definition;

		internal static SkillDef Definition
		{
			get
			{
				if (_definition == null)
				{
					_definition = CreateDefinition();
				}
				return _definition;
			}
		}

		internal static void Initialize()
		{
			if (_definition == null)
			{
				_definition = CreateDefinition();
			}
		}

		private static SkillDef CreateDefinition()
		{
			//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_0007: 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_0011: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			return new SkillDef
			{
				m_skill = Type,
				m_icon = ContentPack.SkillIcon,
				m_description = "$balrond_dualmastery_tooltip",
				m_increseStep = 1f
			};
		}

		internal static bool MatchesCheatName(string value)
		{
			return MatchesExternalName(value);
		}

		internal static bool MatchesExternalName(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			string text = value.Trim().Replace("$", string.Empty).Replace("_", string.Empty)
				.Replace("-", string.Empty)
				.Replace(" ", string.Empty);
			return text.Equals("DualMastery", StringComparison.OrdinalIgnoreCase) || text.Equals("DualWield", StringComparison.OrdinalIgnoreCase) || text.Equals("BalrondDualMastery", StringComparison.OrdinalIgnoreCase) || text.Equals("skill874298", StringComparison.OrdinalIgnoreCase) || text.Equals(874298.ToString(), StringComparison.OrdinalIgnoreCase);
		}
	}
	[HarmonyPatch]
	internal static class MasterySkillPatches
	{
		private static bool _collisionLogged;

		private static bool _idCollision;

		internal static bool HasIdCollision => _idCollision;

		private static bool IsOurDefinition(SkillDef definition)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (definition == null || definition.m_skill != MasterySkill.Type)
			{
				return false;
			}
			if (definition == MasterySkill.Definition)
			{
				return true;
			}
			return string.Equals(definition.m_description, "$balrond_dualmastery_tooltip", StringComparison.Ordinal);
		}

		private static bool EnsureDefinitionInList(List<SkillDef> definitions)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (definitions == null)
			{
				return false;
			}
			SkillDef definition = MasterySkill.Definition;
			bool flag = false;
			bool flag2 = false;
			for (int i = 0; i < definitions.Count; i++)
			{
				SkillDef val = definitions[i];
				if (val == null || val.m_skill != MasterySkill.Type)
				{
					continue;
				}
				if (IsOurDefinition(val))
				{
					flag = true;
					if (val != definition)
					{
						definitions[i] = definition;
					}
				}
				else
				{
					flag2 = true;
				}
			}
			_idCollision = flag2;
			if (flag2)
			{
				if (!_collisionLogged && Plugin.Log != null)
				{
					_collisionLogged = true;
					Plugin.Log.LogError((object)("Dual Mastery skill ID collision: " + 874298 + " is already registered by another SkillDef. Dual Mastery will keep gameplay active at skill-0 scaling, but it will not overwrite, raise, reset, or resolve that foreign skill as Dual Mastery."));
				}
				return false;
			}
			if (!flag)
			{
				definitions.Add(definition);
			}
			return true;
		}

		internal static bool EnsureRuntimeDefinition(Skills skills)
		{
			return (Object)(object)skills != (Object)null && EnsureDefinitionInList(skills.m_skills);
		}

		private static void Seed(Skills skills)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)skills == (Object)null || !EnsureRuntimeDefinition(skills))
			{
				return;
			}
			try
			{
				skills.GetSkill(MasterySkill.Type);
			}
			catch (Exception ex)
			{
				if (Plugin.Log != null)
				{
					Plugin.Log.LogDebug((object)("Dual Mastery custom-skill seed failed safely: " + ex.GetBaseException().Message));
				}
			}
		}

		[HarmonyPatch(typeof(Skills), "Awake")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "M2Valheim.SkillsReworked" })]
		private static void SkillsAwakePrefix(Skills __instance)
		{
			EnsureRuntimeDefinition(__instance);
		}

		[HarmonyPatch(typeof(Skills), "Awake")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "M2Valheim.SkillsReworked" })]
		private static void SkillsAwakePostfix(Skills __instance)
		{
			if (!SkillsReworkedCompatibility.IsActive)
			{
				Seed(__instance);
			}
			Player val = (((Object)(object)__instance != (Object)null) ? __instance.m_player : null);
			if ((Object)(object)val != (Object)null)
			{
				SkillsReworkedCompatibility.RefreshNameMapping(val);
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "M2Valheim.SkillsReworked" })]
		private static void PlayerLoadPostfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				Skills skills = ((Character)__instance).GetSkills();
				EnsureRuntimeDefinition(skills);
				if (!SkillsReworkedCompatibility.IsActive)
				{
					Seed(skills);
				}
				MasterySkillPersistence.ReconcileAfterLoad(__instance);
				SkillsReworkedCompatibility.RefreshNameMapping(__instance);
			}
		}

		[HarmonyPatch(typeof(Skills), "GetSkillList")]
		[HarmonyPrefix]
		private static void SeedBeforeSkillList(Skills __instance)
		{
			EnsureRuntimeDefinition(__instance);
		}

		[HarmonyPatch(typeof(Skills), "IsSkillValid")]
		[HarmonyPostfix]
		private static void AcceptCustomSkill(Skills __instance, SkillType type, ref bool __result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (type == MasterySkill.Type && EnsureRuntimeDefinition(__instance))
			{
				__result = true;
			}
		}

		[HarmonyPatch(typeof(Skills), "GetSkillDef")]
		[HarmonyPostfix]
		private static void SupplyDefinition(Skills __instance, SkillType type, ref SkillDef __result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (type == MasterySkill.Type && EnsureRuntimeDefinition(__instance))
			{
				__result = MasterySkill.Definition;
			}
		}

		[HarmonyPatch(typeof(Skills), "CheatRaiseSkill")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "M2Valheim.SkillsReworked" })]
		private static bool RaiseCustomSkill(Skills __instance, string name, float value, bool showMessage)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (!MasterySkill.MatchesCheatName(name))
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)null)
			{
				EnsureRuntimeDefinition(__instance);
			}
			if (HasIdCollision)
			{
				if (showMessage && (Object)(object)Console.instance != (Object)null)
				{
					Console.instance.Print("Dual Mastery skill ID 874298 is occupied by another mod; command ignored.");
				}
				return false;
			}
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			__instance.RaiseSkill(MasterySkill.Type, value);
			MasterySkillPersistence.Capture(__instance);
			if (showMessage && (Object)(object)Console.instance != (Object)null)
			{
				Console.instance.Print("Dual Mastery raised by " + value);
			}
			return false;
		}

		[HarmonyPatch(typeof(Skills), "CheatResetSkill")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "M2Valheim.SkillsReworked" })]
		private static bool ResetCustomSkill(Skills __instance, string name)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if (!MasterySkill.MatchesCheatName(name))
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)null)
			{
				EnsureRuntimeDefinition(__instance);
			}
			if (HasIdCollision)
			{
				if ((Object)(object)Console.instance != (Object)null)
				{
					Console.instance.Print("Dual Mastery skill ID 874298 is occupied by another mod; reset ignored.");
				}
				return false;
			}
			if ((Object)(object)__instance != (Object)null)
			{
				__instance.ResetSkill(MasterySkill.Type);
				if (!SkillsReworkedCompatibility.IsActive)
				{
					__instance.GetSkill(MasterySkill.Type);
				}
				MasterySkillPersistence.Capture(__instance);
			}
			return false;
		}

		[HarmonyPatch(typeof(Skills), "RaiseSkill")]
		[HarmonyPrefix]
		private static void EnsureBeforeRaise(Skills __instance, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (skillType == MasterySkill.Type)
			{
				EnsureRuntimeDefinition(__instance);
			}
		}

		[HarmonyPatch(typeof(Skills), "RaiseSkill")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "M2Valheim.SkillsReworked" })]
		private static void PersistAfterRaise(Skills __instance, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (skillType == MasterySkill.Type)
			{
				MasterySkillPersistence.Capture(__instance);
			}
		}

		[HarmonyPatch(typeof(Skills), "ResetSkill")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "M2Valheim.SkillsReworked" })]
		private static void PersistAfterReset(Skills __instance, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (skillType == MasterySkill.Type && !((Object)(object)__instance == (Object)null) && !HasIdCollision)
			{
				if (!SkillsReworkedCompatibility.IsActive)
				{
					__instance.GetSkill(MasterySkill.Type);
				}
				MasterySkillPersistence.Capture(__instance);
			}
		}

		[HarmonyPatch(typeof(Skills), "OnDeath")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "M2Valheim.SkillsReworked" })]
		private static void PersistAfterDeath(Skills __instance)
		{
			MasterySkillPersistence.Capture(__instance);
		}
	}
	internal static class MasterySkillPersistence
	{
		internal const string CustomDataKey = "balrond.dualmastery.skill.v1";

		internal const string BackendMarkerKey = "balrond.dualmastery.skill.backend.v1";

		private const string NativeBackendMarker = "native";

		private const string SkillsReworkedBackendMarker = "skillsreworked";

		private const char Separator = '|';

		internal static void Capture(Player player)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || MasterySkillPatches.HasIdCollision)
			{
				return;
			}
			Skills skills = ((Character)player).GetSkills();
			if ((Object)(object)skills == (Object)null || skills.m_skillData == null)
			{
				return;
			}
			if (!skills.m_skillData.TryGetValue(MasterySkill.Type, out var value) || value == null)
			{
				if (SkillsReworkedCompatibility.IsActive && IsBackend(player, "skillsreworked"))
				{
					WriteValues(player, 0f, 0f);
				}
			}
			else
			{
				Write(player, value);
				MarkCurrentBackend(player);
			}
		}

		internal static void Capture(Skills skills)
		{
			Capture(((Object)(object)skills != (Object)null) ? skills.m_player : null);
		}

		internal static void ReconcileAfterLoad(Player player)
		{
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || MasterySkillPatches.HasIdCollision)
			{
				return;
			}
			Skills skills = ((Character)player).GetSkills();
			if ((Object)(object)skills == (Object)null)
			{
				return;
			}
			float level2;
			float accumulator2;
			Skill value2;
			if (SkillsReworkedCompatibility.IsActive)
			{
				if (skills.m_skillData != null && skills.m_skillData.TryGetValue(MasterySkill.Type, out var value) && value != null)
				{
					Write(player, value);
					MarkBackend(player, "skillsreworked");
					return;
				}
				if (!IsBackend(player, "skillsreworked"))
				{
					if (TryRead(player, out var level, out var accumulator))
					{
						Skill skill = skills.GetSkill(MasterySkill.Type);
						if (skill != null)
						{
							skill.m_level = level;
							skill.m_accumulator = accumulator;
							Write(player, skill);
							MarkBackend(player, "skillsreworked");
							if (Plugin.Log != null)
							{
								Plugin.Log.LogInfo((object)("Migrated fallback Dual Mastery state into SkillsReworked once: level " + level.ToString("0.###", CultureInfo.InvariantCulture) + "."));
							}
							return;
						}
					}
					MarkBackend(player, "skillsreworked");
				}
				WriteValues(player, 0f, 0f);
			}
			else if (TryRead(player, out level2, out accumulator2))
			{
				Skill skill2 = skills.GetSkill(MasterySkill.Type);
				if (skill2 != null)
				{
					float level3 = skill2.m_level;
					float accumulator3 = skill2.m_accumulator;
					skill2.m_level = level2;
					skill2.m_accumulator = accumulator2;
					MarkBackend(player, "native");
					if (Plugin.Log != null && (!Approximately(level3, level2) || !Approximately(accumulator3, accumulator2)))
					{
						Plugin.Log.LogInfo((object)("Restored native Dual Mastery state after character load: level " + level2.ToString("0.###", CultureInfo.InvariantCulture) + "."));
					}
				}
			}
			else if (skills.m_skillData != null && skills.m_skillData.TryGetValue(MasterySkill.Type, out value2) && value2 != null)
			{
				Write(player, value2);
				MarkBackend(player, "native");
			}
			else
			{
				Skill skill3 = skills.GetSkill(MasterySkill.Type);
				if (skill3 != null)
				{
					Write(player, skill3);
					MarkBackend(player, "native");
				}
			}
		}

		internal static float GetBackingLevel(Player player)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || MasterySkillPatches.HasIdCollision)
			{
				return 0f;
			}
			Skills skills = ((Character)player).GetSkills();
			if ((Object)(object)skills == (Object)null || skills.m_skillData == null)
			{
				return 0f;
			}
			if (!skills.m_skillData.TryGetValue(MasterySkill.Type, out var value) || value == null)
			{
				return 0f;
			}
			return SanitizeLevel(value.m_level);
		}

		private static bool TryRead(Player player, out float level, out float accumulator)
		{
			level = 0f;
			accumulator = 0f;
			if ((Object)(object)player == (Object)null || player.m_customData == null)
			{
				return false;
			}
			if (!player.m_customData.TryGetValue("balrond.dualmastery.skill.v1", out var value) || string.IsNullOrEmpty(value))
			{
				return false;
			}
			string[] array = value.Split(new char[1] { '|' });
			if (array.Length != 2)
			{
				return false;
			}
			if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.IsNaN(result) || float.IsInfinity(result) || float.IsNaN(result2) || float.IsInfinity(result2))
			{
				return false;
			}
			level = SanitizeLevel(result);
			accumulator = SanitizeAccumulator(result2);
			return true;
		}

		private static void Write(Player player, Skill skill)
		{
			if (!((Object)(object)player == (Object)null) && player.m_customData != null && skill != null)
			{
				WriteValues(player, skill.m_level, skill.m_accumulator);
			}
		}

		private static void WriteValues(Player player, float level, float accumulator)
		{
			if (!((Object)(object)player == (Object)null) && player.m_customData != null)
			{
				level = SanitizeLevel(level);
				accumulator = SanitizeAccumulator(accumulator);
				player.m_customData["balrond.dualmastery.skill.v1"] = level.ToString("R", CultureInfo.InvariantCulture) + "|" + accumulator.ToString("R", CultureInfo.InvariantCulture);
			}
		}

		private static void MarkCurrentBackend(Player player)
		{
			MarkBackend(player, SkillsReworkedCompatibility.IsActive ? "skillsreworked" : "native");
		}

		private static void MarkBackend(Player player, string marker)
		{
			if (!((Object)(object)player == (Object)null) && player.m_customData != null && !string.IsNullOrEmpty(marker))
			{
				player.m_customData["balrond.dualmastery.skill.backend.v1"] = marker;
			}
		}

		private static bool IsBackend(Player player, string marker)
		{
			if ((Object)(object)player == (Object)null || player.m_customData == null || string.IsNullOrEmpty(marker))
			{
				return false;
			}
			string value;
			return player.m_customData.TryGetValue("balrond.dualmastery.skill.backend.v1", out value) && string.Equals(value, marker, StringComparison.Ordinal);
		}

		private static float SanitizeLevel(float value)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return 0f;
			}
			return Math.Max(0f, value);
		}

		private static float SanitizeAccumulator(float value)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return 0f;
			}
			return Math.Max(0f, value);
		}

		private static bool Approximately(float a, float b)
		{
			return Math.Abs(a - b) <= 0.0001f;
		}
	}
}
namespace Balrond.DualMastery.Patches
{
	[HarmonyPatch]
	internal static class CombatPatches
	{
		private struct StartQuote
		{
			internal bool Active;

			internal ResourceQuote Extra;

			internal Player Player;

			internal bool Secondary;

			internal bool FacingChanged;

			internal Quaternion PreviousRotation;
		}

		private struct TriggerState
		{
			internal Player Player;

			internal bool Changed;

			internal Quaternion PreviousRotation;

			internal bool Paired;

			internal bool Secondary;

			internal bool PrimaryMainWindow;

			internal bool ExternalSingleWindowPrimary;
		}

		[HarmonyPatch(typeof(Humanoid), "StartAttack")]
		[HarmonyPrefix]
		private static bool CheckCombinedResources(Humanoid __instance, bool secondaryAttack, ref bool __result, out StartQuote __state)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			__state = default(StartQuote);
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null || !NetworkAuthority.IsOwner(val) || !PairedLoadout.TryGet(val, out var loadout))
			{
				return true;
			}
			__state.Player = val;
			__state.Secondary = secondaryAttack;
			__state.FacingChanged = PairedAttackRuntime.TryFaceCursor(val, out __state.PreviousRotation);
			Attack val2 = PairedLoadout.SelectAttack(loadout.MainHand, secondaryAttack);
			Attack val3 = PairedLoadout.SelectAttack(loadout.OffHand, secondaryAttack);
			if (val2 == null || val3 == null)
			{
				PairedAttackRuntime.RestoreFacing(val, __state.FacingChanged, __state.PreviousRotation);
				__state.FacingChanged = false;
				return true;
			}
			ResourceQuote resourceQuote = OffhandAttackEngine.Quote(val, loadout.MainHand, val2);
			ResourceQuote resourceQuote2 = OffhandAttackEngine.Quote(val, loadout.OffHand, val3).Scale(CombatPolicy.ExtraResourceFactor(val, secondaryAttack));
			ResourceQuote resourceQuote3 = resourceQuote.Add(resourceQuote2);
			if ((resourceQuote3.Stamina > 0f && !((Character)val).HaveStamina(resourceQuote3.Stamina)) || (resourceQuote3.Eitr > 0f && !((Character)val).HaveEitr(resourceQuote3.Eitr)) || (resourceQuote3.Health > 0f && !((Character)val).HaveHealth(resourceQuote3.Health)))
			{
				PairedAttackRuntime.RestoreFacing(val, __state.FacingChanged, __state.PreviousRotation);
				__state.FacingChanged = false;
				__result = false;
				return false;
			}
			__state.Active = true;
			__state.Extra = resourceQuote2;
			return true;
		}

		[HarmonyPatch(typeof(Humanoid), "StartAttack")]
		[HarmonyPostfix]
		private static void PayOffhandSurcharge(bool __result, StartQuote __state, Humanoid __instance)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!__result || !__state.Active || (Object)(object)val == (Object)null)
			{
				PairedAttackRuntime.RestoreFacing(__state.Player, __state.FacingChanged, __state.PreviousRotation);
				return;
			}
			if (__state.Extra.Stamina > 0f)
			{
				((Character)val).UseStamina(__state.Extra.Stamina);
			}
			if (__state.Extra.Eitr > 0f)
			{
				((Character)val).UseEitr(__state.Extra.Eitr);
			}
			if (__state.Extra.Health > 0f)
			{
				((Character)val).UseHealth(__state.Extra.Health);
			}
			Attack currentAttack = ((Humanoid)val).m_currentAttack;
			if (currentAttack != null)
			{
				PairedAttackRuntime.Begin(val, currentAttack, __state.Secondary);
			}
		}

		[HarmonyPatch(typeof(Humanoid), "OnAttackTrigger")]
		[HarmonyPrefix]
		private static bool RoutePairedAttackTrigger(Humanoid __instance, out TriggerState __state)
		{
			__state = default(TriggerState);
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null || !NetworkAuthority.IsOwner(val) || ((Humanoid)val).m_currentAttack == null)
			{
				return true;
			}
			if (!PairedLoadout.TryGet(val, out var loadout))
			{
				return true;
			}
			__state.Player = val;
			__state.Paired = true;
			__state.Secondary = ((Humanoid)val).m_currentAttackIsSecondary;
			__state.Changed = PairedAttackRuntime.TryFaceCursor(val, out __state.PreviousRotation);
			Attack currentAttack = ((Humanoid)val).m_currentAttack;
			PairedAttackRuntime.NormalizePrimaryAttackType(val, currentAttack, __state.Secondary);
			if (__state.Secondary)
			{
				return true;
			}
			if (ExternalAnimationCompatibility.PreserveExternalPrimaryAttackSemantics(val))
			{
				if (!PairedAttackRuntime.ConsumeExternalSingleWindowPrimary(val, currentAttack))
				{
					return false;
				}
				__state.ExternalSingleWindowPrimary = true;
				return true;
			}
			switch (PairedAttackRuntime.ConsumePrimaryTrigger(val, currentAttack))
			{
			case PrimaryTriggerRole.OffhandFirst:
				OffhandAttackEngine.Execute(loadout, currentAttack, secondary: false);
				return false;
			case PrimaryTriggerRole.MainSecond:
				__state.PrimaryMainWindow = true;
				return true;
			default:
				return false;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "OnAttackTrigger")]
		[HarmonyPostfix]
		private static void FinishPairedAttackTrigger(Humanoid __instance, TriggerState __state)
		{
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!__state.Paired)
				{
					return;
				}
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if ((Object)(object)val == (Object)null || !NetworkAuthority.IsOwner(val) || !PairedLoadout.TryGet(val, out var loadout))
				{
					return;
				}
				Attack currentAttack = ((Humanoid)val).m_currentAttack;
				if (currentAttack == null)
				{
					return;
				}
				bool flag = false;
				if (__state.Secondary)
				{
					PairedAttackRuntime.NormalizePrimaryAttackType(val, currentAttack, secondary: true);
					if (PairedAttackRuntime.TryConsumeSecondaryOffhand(val, currentAttack) && OffhandAttackEngine.Execute(loadout, currentAttack, secondary: true))
					{
						flag = true;
					}
				}
				else if (__state.ExternalSingleWindowPrimary)
				{
					if (PairedAttackRuntime.TryConsumeSameWindowOffhand(val, currentAttack) && OffhandAttackEngine.Execute(loadout, currentAttack, secondary: false))
					{
						flag = true;
					}
				}
				else if (__state.PrimaryMainWindow)
				{
					flag = true;
				}
				if (flag && PairedAttackRuntime.TryAwardMastery(val, currentAttack))
				{
					float num = CombatPolicy.MasteryXp(__state.Secondary);
					if (num > 0f)
					{
						((Character)val).RaiseSkill(MasterySkill.Type, num);
					}
				}
			}
			finally
			{
				PairedAttackRuntime.RestoreFacing(__state.Player, __state.Changed, __state.PreviousRotation);
			}
		}
	}
	[HarmonyPatch]
	internal static class EquipmentPatches
	{
		private struct EquipCapture
		{
			internal Player Player;

			internal ItemData RequestedItem;

			internal ItemData PreviousMain;

			internal ItemData PreviousOffhand;

			internal ItemData PreviousHiddenMain;

			internal ItemData PreviousHiddenOffhand;

			internal bool ConvertToOffhand;

			internal bool ForceOffhandReplacement;

			internal bool EnteredDepth;
		}

		private struct UnequipCapture
		{
			internal Player Player;

			internal ItemData Item;

			internal ItemData PreviousMain;

			internal ItemData PreviousOffhand;

			internal ItemData PreviousHiddenMain;

			internal ItemData PreviousHiddenOffhand;

			internal bool WasHandReference;
		}

		private struct HandVisibilityCapture
		{
			internal Player Player;

			internal ItemData Main;

			internal ItemData Offhand;

			internal bool Pair;

			internal bool EnteredDepth;
		}

		private struct DeathUnequipCapture
		{
			internal Player Player;

			internal bool EnteredDepth;
		}

		private static int _equipDepth;

		private static int _handVisibilityDepth;

		private static int _bulkUnequipDepth;

		private static int _deathUnequipDepth;

		private static bool _normalizing;

		private static readonly HashSet<ItemData> GridRepairLogged = new HashSet<ItemData>();

		private static readonly HashSet<ItemData> GridRepairFailureLogged = new HashSet<ItemData>();

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		[HarmonyPrefix]
		[HarmonyBefore(new string[] { "windstride.bowanimations", "ishid4.mods.betterarchery" })]
		[HarmonyPriority(800)]
		private static void CaptureEquip(Humanoid __instance, ItemData item, out EquipCapture __state)
		{
			__state = default(EquipCapture);
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!_normalizing && _handVisibilityDepth <= 0 && _bulkUnequipDepth <= 0 && _deathUnequipDepth <= 0 && !((Object)(object)val == (Object)null) && NetworkAuthority.IsOwner(val) && ModSettings.Enabled.Value)
			{
				RepairRelevantInventoryGrid(val, "equip-prefix");
				EnsureInventoryGridSlot(val, ((Humanoid)val).GetInventory(), item, "equip-request");
				if (!PairedLoadout.IsSupportedWeapon(item))
				{
					AnimationControllerService.PrepareForNonPairedHandTransition(val);
				}
				__state.Player = val;
				__state.RequestedItem = item;
				__state.PreviousMain = ((Humanoid)val).m_rightItem;
				__state.PreviousOffhand = ((Humanoid)val).m_leftItem;
				__state.PreviousHiddenMain = ((Humanoid)val).m_hiddenRightItem;
				__state.PreviousHiddenOffhand = ((Humanoid)val).m_hiddenLeftItem;
				__state.EnteredDepth = true;
				_equipDepth++;
				ItemData val2 = ((Humanoid)val).m_rightItem ?? ((Humanoid)val).m_hiddenRightItem;
				ItemData val3 = ((Humanoid)val).m_leftItem ?? ((Humanoid)val).m_hiddenLeftItem;
				if (val == Player.m_localPlayer && IsOffhandSwapModifierHeld() && val2 != null && val3 != null && IsReplaceableLeftItem(val3) && IsOffhandTarget(item) && CanPlaceInOffhand(val2, item))
				{
					__state.PreviousMain = val2;
					__state.PreviousOffhand = val3;
					__state.ForceOffhandReplacement = true;
				}
				else if (PairedLoadout.IsSupportedWeapon(item) && val2 != null && val2 != item && PairedLoadout.CanPair(val2, item))
				{
					__state.PreviousMain = val2;
					__state.ConvertToOffhand = true;
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		[HarmonyPostfix]
		private static void NormalizeAfterEquip(ItemData item, bool __result, EquipCapture __state)
		{
			Player player = __state.Player;
			if (!((Object)(object)player == (Object)null) && !_normalizing)
			{
				if (__result && __state.ForceOffhandReplacement && __state.PreviousMain != null)
				{
					ConvertSuccessfulOffhandReplacement(player, item, __state);
				}
				else if (__result && __state.ConvertToOffhand && __state.PreviousMain != null)
				{
					ConvertSuccessfulEquip(player, item, __state);
				}
				bool flag = __result && NormalizeHandInvariant(player);
				ReconcileCapturedFlags(player, __state);
				if (flag)
				{
					RunSetupEquipment(player);
					ReconcileCapturedFlags(player, __state);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		[HarmonyFinalizer]
		private static Exception EndEquipTransaction(Exception __exception, EquipCapture __state)
		{
			if (__state.EnteredDepth && _equipDepth > 0)
			{
				_equipDepth--;
			}
			return __exception;
		}

		private static bool ConvertSuccessfulEquip(Player player, ItemData item, EquipCapture state)
		{
			if (((Humanoid)player).m_rightItem != item)
			{
				return false;
			}
			ItemData previousMain = state.PreviousMain;
			if (!PairedLoadout.CanPair(previousMain, item))
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || !inventory.ContainsItem(previousMain) || !inventory.ContainsItem(item))
			{
				return false;
			}
			if (!EnsureInventoryGridSlot(player, inventory, previousMain, "pair-main") || !EnsureInventoryGridSlot(player, inventory, item, "pair-selected"))
			{
				return false;
			}
			_normalizing = true;
			try
			{
				((Humanoid)player).m_hiddenRightItem = null;
				((Humanoid)player).m_hiddenLeftItem = null;
				((Humanoid)player).m_rightItem = item;
				((Humanoid)player).m_leftItem = previousMain;
				item.m_equipped = true;
				previousMain.m_equipped = true;
				ItemData val = state.PreviousOffhand ?? state.PreviousHiddenOffhand;
				if (val != null && val != previousMain && val != item)
				{
					val.m_equipped = false;
				}
				AnimationControllerService.Refresh(player);
				((Humanoid)player).SetupEquipment();
			}
			finally
			{
				_normalizing = false;
			}
			return true;
		}

		private static void ConvertSuccessfulOffhandReplacement(Player player, ItemData item, EquipCapture state)
		{
			if ((Object)(object)player == (Object)null || item == null || state.PreviousMain == null || (((Humanoid)player).m_rightItem != item && ((Humanoid)player).m_leftItem != item && !item.m_equipped) || !CanPlaceInOffhand(state.PreviousMain, item))
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || !inventory.ContainsItem(state.PreviousMain) || !inventory.ContainsItem(item) || !EnsureInventoryGridSlot(player, inventory, state.PreviousMain, "offhand-replace-main") || !EnsureInventoryGridSlot(player, inventory, item, "offhand-replace-selected"))
			{
				return;
			}
			_normalizing = true;
			try
			{
				ItemData val = state.PreviousOffhand ?? state.PreviousHiddenOffhand;
				ItemData rightItem = ((Humanoid)player).m_rightItem;
				ItemData leftItem = ((Humanoid)player).m_leftItem;
				((Humanoid)player).m_hiddenRightItem = null;
				((Humanoid)player).m_hiddenLeftItem = null;
				((Humanoid)player).m_rightItem = state.PreviousMain;
				((Humanoid)player).m_leftItem = item;
				state.PreviousMain.m_equipped = true;
				item.m_equipped = true;
				if (val != null && val != state.PreviousMain && val != item)
				{
					val.m_equipped = false;
				}
				if (rightItem != null && rightItem != state.PreviousMain && rightItem != item)
				{
					rightItem.m_equipped = false;
				}
				if (leftItem != null && leftItem != state.PreviousMain && leftItem != item)
				{
					leftItem.m_equipped = false;
				}
				AnimationControllerService.Refresh(player);
				((Humanoid)player).SetupEquipment();
			}
			finally
			{
				_normalizing = false;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipItem")]
		[HarmonyPrefix]
		private static void CaptureUnequip(Humanoid __instance, ItemData item, out UnequipCapture __state)
		{
			__state = default(UnequipCapture);
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!_normalizing && _equipDepth <= 0 && _handVisibilityDepth <= 0 && _bulkUnequipDepth <= 0 && _deathUnequipDepth <= 0 && !((Object)(object)val == (Object)null) && NetworkAuthority.IsOwner(val))
			{
				__state.Player = val;
				__state.Item = item;
				__state.PreviousMain = ((Humanoid)val).m_rightItem;
				__state.PreviousOffhand = ((Humanoid)val).m_leftItem;
				__state.PreviousHiddenMain = ((Humanoid)val).m_hiddenRightItem;
				__state.PreviousHiddenOffhand = ((Humanoid)val).m_hiddenLeftItem;
				__state.WasHandReference = IsHandReference(val, item);
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipItem")]
		[HarmonyPostfix]
		private static void NormalizeAfterUnequip(UnequipCapture __state)
		{
			Player player = __state.Player;
			if (!((Object)(object)player == (Object)null) && !_normalizing && _equipDepth <= 0 && _bulkUnequipDepth <= 0 && _deathUnequipDepth <= 0)
			{
				bool flag = NormalizeHandInvariant(player);
				if (__state.WasHandReference)
				{
					ReconcileItemFlag(player, __state.Item);
				}
				ReconcileItemFlag(player, __state.PreviousMain);
				ReconcileItemFlag(player, __state.PreviousOffhand);
				ReconcileItemFlag(player, __state.PreviousHiddenMain);
				ReconcileItemFlag(player, __state.PreviousHiddenOffhand);
				if (flag)
				{
					RunSetupEquipment(player);
					ReconcileItemFlag(player, __state.Item);
					ReconcileItemFlag(player, __state.PreviousMain);
					ReconcileItemFlag(player, __state.PreviousOffhand);
					ReconcileItemFlag(player, __state.PreviousHiddenMain);
					ReconcileItemFlag(player, __state.PreviousHiddenOffhand);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipAllItems")]
		[HarmonyPrefix]
		private static void BeginBulkUnequip(Humanoid __instance, out bool __state)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			__state = (Object)(object)val != (Object)null && NetworkAuthority.IsOwner(val);
			if (__state)
			{
				_bulkUnequipDepth++;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipAllItems")]
		[HarmonyFinalizer]
		private static Exception EndBulkUnequip(Exception __exception, bool __state)
		{
			if (__state && _bulkUnequipDepth > 0)
			{
				_bulkUnequipDepth--;
			}
			return __exception;
		}

		[HarmonyPatch(typeof(Player), "UnequipDeathDropItems")]
		[HarmonyPrefix]
		private static void BeginDeathUnequip(Player __instance, out DeathUnequipCapture __state)
		{
			__state = default(DeathUnequipCapture);
			if (!((Object)(object)__instance == (Object)null) && NetworkAuthority.IsOwner(__instance))
			{
				if (ModSettings.Enabled.Value)
				{
					RepairRelevantInventoryGrid(__instance, "death-prefix");
				}
				__state.Player = __instance;
				__state.EnteredDepth = true;
				_deathUnequipDepth++;
			}
		}

		[HarmonyPatch(typeof(Player), "UnequipDeathDropItems")]
		[HarmonyFinalizer]
		private static Exception EndDeathUnequip(Exception __exception, DeathUnequipCapture __state)
		{
			try
			{
				if (__state.EnteredDepth && (Object)(object)__state.Player != (Object)null)
				{
					CleanupDeathHandReferences(__state.Player);
				}
			}
			finally
			{
				if (__state.EnteredDepth && _deathUnequipDepth > 0)
				{
					_deathUnequipDepth--;
				}
			}
			return __exception;
		}

		private static void CleanupDeathHandReferences(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory != null)
				{
					CleanupDeathHandReference(player, inventory, ((Humanoid)player).m_leftItem);
					CleanupDeathHandReference(player, inventory, ((Humanoid)player).m_hiddenLeftItem);
				}
			}
		}

		private static void CleanupDeathHandReference(Player player, Inventory inventory, ItemData item)
		{
			if (!((Object)(object)player == (Object)null) && inventory != null && item != null && PairedLoadout.IsSupportedWeapon(item) && (!inventory.ContainsItem(item) || !item.m_equipped))
			{
				RemoveHandReferences(player, item);
				if (!inventory.ContainsItem(item))
				{
					item.m_equipped = false;
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "HideHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static void CaptureHideHands(Humanoid __instance, bool __0, out HandVisibilityCapture __state)
		{
			__state = default(HandVisibilityCapture);
			if (!__0)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (!_normalizing && _bulkUnequipDepth <= 0 && _deathUnequipDepth <= 0 && !((Object)(object)val == (Object)null) && NetworkAuthority.IsOwner(val) && PairedLoadout.CanPair(((Humanoid)val).m_rightItem, ((Humanoid)val).m_leftItem))
				{
					__state.Player = val;
					__state.Main = ((Humanoid)val).m_rightItem;
					__state.Offhand = ((Humanoid)val).m_leftItem;
					__state.Pair = true;
					__state.EnteredDepth = true;
					_handVisibilityDepth++;
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "HideHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		[HarmonyPostfix]
		private static void RestoreHiddenPairAfterHide(HandVisibilityCapture __state)
		{
			Player player = __state.Player;
			if (__state.Pair && !((Object)(object)player == (Object)null) && !_normalizing)
			{
				ItemData main = __state.Main;
				ItemData offhand = __state.Offhand;
				if (PairedLoadout.CanPair(main, offhand) && (((Humanoid)player).m_rightItem != main || ((Humanoid)player).m_leftItem != offhand))
				{
					((Humanoid)player).m_rightItem = null;
					((Humanoid)player).m_leftItem = null;
					((Humanoid)player).m_hiddenRightItem = main;
					((Humanoid)player).m_hiddenLeftItem = offhand;
					main.m_equipped = true;
					offhand.m_equipped = true;
					RunSetupEquipment(player);
					ReconcileItemFlag(player, main);
					ReconcileItemFlag(player, offhand);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "HideHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		[HarmonyFinalizer]
		private static Exception EndHideHands(Exception __exception, HandVisibilityCapture __state)
		{
			EndHandVisibilityDepth(__state);
			return __exception;
		}

		[HarmonyPatch(typeof(Humanoid), "ShowHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static void CaptureShowHands(Humanoid __instance, bool __0, out HandVisibilityCapture __state)
		{
			__state = default(HandVisibilityCapture);
			if (!__0)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (!_normalizing && _bulkUnequipDepth <= 0 && _deathUnequipDepth <= 0 && !((Object)(object)val == (Object)null) && NetworkAuthority.IsOwner(val) && PairedLoadout.CanPair(((Humanoid)val).m_hiddenRightItem, ((Humanoid)val).m_hiddenLeftItem))
				{
					__state.Player = val;
					__state.Main = ((Humanoid)val).m_hiddenRightItem;
					__state.Offhand = ((Humanoid)val).m_hiddenLeftItem;
					__state.Pair = true;
					__state.EnteredDepth = true;
					_handVisibilityDepth++;
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "ShowHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		[HarmonyPostfix]
		private static void RestoreDrawnPairAfterShow(HandVisibilityCapture __state)
		{
			Player player = __state.Player;
			if (__state.Pair && !((Object)(object)player == (Object)null) && !_normalizing)
			{
				ItemData main = __state.Main;
				ItemData offhand = __state.Offhand;
				if (PairedLoadout.CanPair(main, offhand) && (((Humanoid)player).m_hiddenRightItem != main || ((Humanoid)player).m_hiddenLeftItem != offhand || ((Humanoid)player).m_rightItem != null || ((Humanoid)player).m_leftItem != null))
				{
					((Humanoid)player).m_hiddenRightItem = null;
					((Humanoid)player).m_hiddenLeftItem = null;
					((Humanoid)player).m_rightItem = main;
					((Humanoid)player).m_leftItem = offhand;
					main.m_equipped = true;
					offhand.m_equipped = true;
					RunSetupEquipment(player);
					ReconcileItemFlag(player, main);
					ReconcileItemFlag(player, offhand);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "ShowHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		[HarmonyFinalizer]
		private static Exception EndShowHands(Exception __exception, HandVisibilityCapture __state)
		{
			EndHandVisibilityDepth(__state);
			return __exception;
		}

		private static void EndHandVisibilityDepth(HandVisibilityCapture state)
		{
			if (state.EnteredDepth && _handVisibilityDepth > 0)
			{
				_handVisibilityDepth--;
			}
		}

		private static bool NormalizeHandInvariant(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			bool result = RemoveDuplicateHandReferences(player);
			ItemData val = ((Humanoid)player).m_rightItem ?? ((Humanoid)player).m_hiddenRightItem;
			ItemData val2 = ((Humanoid)player).m_leftItem ?? ((Humanoid)player).m_hiddenLeftItem;
			if (val == null && PairedLoadout.IsSupportedWeapon(val2))
			{
				if (((Humanoid)player).m_leftItem == val2)
				{
					((Humanoid)player).m_leftItem = null;
					((Humanoid)player).m_rightItem = val2;
				}
				else
				{
					((Humanoid)player).m_hiddenLeftItem = null;
					((Humanoid)player).m_hiddenRightItem = val2;
				}
				val2.m_equipped = IsHandReference(player, val2);
				return true;
			}
			if (PairedLoadout.IsSupportedWeapon(val2) && (val == null || !PairedLoadout.CanPair(val, val2)))
			{
				if (((Humanoid)player).m_leftItem == val2)
				{
					((Humanoid)player).m_leftItem = null;
				}
				if (((Humanoid)player).m_hiddenLeftItem == val2)
				{
					((Humanoid)player).m_hiddenLeftItem = null;
				}
				val2.m_equipped = false;
				result = true;
			}
			return result;
		}

		private static bool RemoveDuplicateHandReferences(Player player)
		{
			bool result = false;
			if (((Humanoid)player).m_rightItem != null && ((Humanoid)player).m_rightItem == ((Humanoid)player).m_hiddenRightItem)
			{
				((Humanoid)player).m_hiddenRightItem = null;
				result = true;
			}
			if (((Humanoid)player).m_leftItem != null && ((Humanoid)player).m_leftItem == ((Humanoid)player).m_hiddenLeftItem)
			{
				((Humanoid)player).m_hiddenLeftItem = null;
				result = true;
			}
			ItemData val = ((Humanoid)player).m_rightItem ?? ((Humanoid)player).m_hiddenRightItem;
			ItemData val2 = ((Humanoid)player).m_leftItem ?? ((Humanoid)player).m_hiddenLeftItem;
			if (val != null && val == val2)
			{
				if (((Humanoid)player).m_leftItem != null)
				{
					((Humanoid)player).m_leftItem = null;
				}
				if (((Humanoid)player).m_hiddenLeftItem != null)
				{
					((Humanoid)player).m_hiddenLeftItem = null;
				}
				result = true;
			}
			return result;
		}

		[HarmonyPatch(typeof(Player), "EquipInventoryItems")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void SanitizeAfterInventoryEquipmentRestore(Player __instance)
		{
			SanitizeRuntimeState(__instance);
		}

		internal static void SanitizeRuntimeState(Player player)
		{
			if ((Object)(object)player == (Object)null || _normalizing || _equipDepth > 0 || _handVisibilityDepth > 0 || _bulkUnequipDepth > 0 || _deathUnequipDepth > 0 || !NetworkAuthority.IsOwner(player))
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory != null)
			{
				if (ModSettings.Enabled.Value)
				{
					RepairRelevantInventoryGrid(player, "sanitize");
				}
				bool flag = false;
				flag |= ClearOrphanedHandReference(player, inventory, ((Humanoid)player).m_leftItem);
				flag |= ClearOrphanedHandReference(player, inventory, ((Humanoid)player).m_hiddenLeftItem);
				if (ModSettings.Enabled.Value)
				{
					flag |= NormalizeHandInvariant(player);
				}
				if (flag)
				{
					RunSetupEquipment(player);
				}
			}
		}

		private static void RepairRelevantInventoryGrid(Player player, string reason)
		{
			if ((Object)(object)player == (Object)null || !NetworkAuthority.IsOwner(player))
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return;
			}
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (val != null && PairedLoadout.IsSupportedWeapon(val) && (val.m_equipped || IsHandReference(player, val)))
				{
					EnsureInventoryGridSlot(player, inventory, val, reason);
				}
			}
		}

		private static bool EnsureInventoryGridSlot(Player player, Inventory inventory, ItemData item, string reason)
		{
			//IL_0137: 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_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || inventory == null || item == null)
			{
				return false;
			}
			if (!PairedLoadout.IsSupportedWeapon(item))
			{
				return true;
			}
			if (!inventory.ContainsItem(item))
			{
				return false;
			}
			int width = inventory.GetWidth();
			int height = inventory.GetHeight();
			int x = item.m_gridPos.x;
			int y = item.m_gridPos.y;
			bool flag = x >= 0 && y >= 0 && x < width && y < height;
			bool flag2 = false;
			if (flag)
			{
				List<ItemData> allItems = inventory.GetAllItems();
				if (allItems != null)
				{
					for (int i = 0; i < allItems.Count; i++)
					{
						ItemData val = allItems[i];
						if (val != null && val != item && val.m_gridPos.x == x && val.m_gridPos.y == y)
						{
							flag2 = true;
							break;
						}
					}
				}
			}
			if (flag && !flag2)
			{
				return true;
			}
			Vector2i val2 = inventory.FindEmptySlot(true);
			if (val2.x < 0 || val2.y < 0 || val2.x >= width || val2.y >= height)
			{
				if (GridRepairFailureLogged.Add(item) && Plugin.Log != null)
				{
					Plugin.Log.LogError((object)("Dual Mastery detected an invalid inventory position for " + DescribeItem(item) + " at (" + x + "," + y + ") during " + reason + ", but the normal inventory has no free cell to repair it. No item was added, duplicated or reserved; this diagnostic is emitted once for this ItemData."));
				}
				return false;
			}
			item.m_gridPos = val2;
			inventory.Changed(false, false);
			GridRepairFailureLogged.Remove(item);
			if (GridRepairLogged.Add(item) && Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)("Repaired Dual Mastery inventory grid position for " + DescribeItem(item) + ": (" + x + "," + y + ") -> (" + val2.x + "," + val2.y + ") during " + reason + ". No extra slot was created or reserved."));
			}
			return true;
		}

		private static string DescribeItem(ItemData item)
		{
			if (item == null)
			{
				return "<null>";
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null && !string.IsNullOrEmpty(((Object)item.m_dropPrefab).name))
			{
				return ((Object)item.m_dropPrefab).name;
			}
			if (item.m_shared != null && !string.IsNullOrEmpty(item.m_shared.m_name))
			{
				return item.m_shared.m_name;
			}
			return "<unnamed weapon>";
		}

		internal static void RepairSentinelCollision(Inventory inventory)
		{
			if (!ModSettings.Enabled.Value || inventory == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && NetworkAuthority.IsOwner(localPlayer) && ((Humanoid)localPlayer).GetInventory() == inventory)
			{
				ItemData itemAt = inventory.GetItemAt(-1, -1);
				if (itemAt != null && PairedLoadout.IsSupportedWeapon(itemAt) && (itemAt.m_equipped || IsHandReference(localPlayer, itemAt)))
				{
					EnsureInventoryGridSlot(localPlayer, inventory, itemAt, "Inventory.AddItem sentinel guard");
				}
			}
		}

		internal static void RepairBeforePlayerSave(Player player)
		{
			if (ModSettings.Enabled.Value)
			{
				RepairRelevantInventoryGrid(player, "player-save");
			}
		}

		private static bool ClearOrphanedHandReference(Player player, Inventory inventory, ItemData item)
		{
			if ((Object)(object)player == (Object)null || inventory == null || item == null || !PairedLoadout.IsSupportedWeapon(item))
			{
				return false;
			}
			if (inventory.ContainsItem(item))
			{
				return false;
			}
			bool flag = RemoveHandReferences(player, item);
			if (flag)
			{
				item.m_equipped = false;
			}
			return flag;
		}

		private static bool RemoveHandReferences(Player player, ItemData item)
		{
			if ((Object)(object)player == (Object)null || item == null)
			{
				return false;
			}
			bool result = false;
			if (((Humanoid)player).m_rightItem == item)
			{
				((Humanoid)player).m_rightItem = null;
				result = true;
			}
			if (((Humanoid)player).m_leftItem == item)
			{
				((Humanoid)player).m_leftItem = null;
				result = true;
			}
			if (((Humanoid)player).m_hiddenRightItem == item)
			{
				((Humanoid)player).m_hiddenRightItem = null;
				result = true;
			}
			if (((Humanoid)player).m_hiddenLeftItem == item)
			{
				((Humanoid)player).m_hiddenLeftItem = null;
				result = true;
			}
			return result;
		}

		private static void RunSetupEquipment(Player player)
		{
			if ((Object)(object)player == (Object)null || _normalizing)
			{
				return;
			}
			_normalizing = true;
			try
			{
				AnimationControllerService.Refresh(player);
				((Humanoid)player).SetupEquipment();
			}
			finally
			{
				_normalizing = false;
			}
		}

		private static bool IsHandReference(Player player, ItemData item)
		{
			if ((Object)(object)player == (Object)null || item == null)
			{
				return false;
			}
			return ((Humanoid)player).m_rightItem == item || ((Humanoid)player).m_leftItem == item || ((Humanoid)player).m_hiddenRightItem == item || ((Humanoid)player).m_hiddenLeftItem == item;
		}

		private static bool IsActiveHandReference(Player player, ItemData item)
		{
			if ((Object)(object)player == (Object)null || item == null)
			{
				return false;
			}
			return ((Humanoid)player).m_rightItem == item || ((Humanoid)player).m_leftItem == item;
		}

		private static bool IsOffhandSwapModifierHeld()
		{
			//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)
			ConfigEntry<KeyboardShortcut> offhandSwapShortcut = ModSettings.OffhandSwapShortcut;
			if (offhandSwapShortcut == null)
			{
				return false;
			}
			try
			{
				KeyboardShortcut value = offhandSwapShortcut.Value;
				return ((KeyboardShortcut)(ref value)).IsPressed();
			}
			catch
			{
				return false;
			}
		}

		private static bool IsOffhandTarget(ItemData item)
		{
			return PairedLoadout.IsSupportedWeapon(item) || IsShieldOrTorch(item);
		}

		private static bool IsReplaceableLeftItem(ItemData item)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			return PairedLoadout.IsSupportedWeapon(item) || (int)item.m_shared.m_itemType == 3 || IsShieldOrTorch(item);
		}

		private static bool CanPlaceInOffhand(ItemData main, ItemData target)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			if (main == null || main.m_shared == null || target == null || target.m_shared == null)
			{
				return false;
			}
			if (PairedLoadout.IsSupportedWeapon(target))
			{
				return PairedLoadout.CanPair(main, target);
			}
			if (!IsShieldOrTorch(target))
			{
				return false;
			}
			return (int)main.m_shared.m_itemType == 3 && (Object)(object)main.m_shared.m_buildPieces == (Object)null;
		}

		private static bool IsShieldOrTorch(ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			string a = ((object)Unsafe.As<ItemType, ItemType>(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString();
			return string.Equals(a, "Shield", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Torch", StringComparison.OrdinalIgnoreCase);
		}

		internal static void ForgetPlayer(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				GridRepairLogged.Clear();
				GridRepairFailureLogged.Clear();
			}
		}

		internal static void HandleEnabledChanged(Player player, bool enabled)
		{
			if ((Object)(object)player == (Object)null || !NetworkAuthority.IsOwner(player))
			{
				return;
			}
			if (enabled)
			{
				SanitizeRuntimeState(player);
				return;
			}
			RepairRelevantInventoryGrid(player, "feature-disable");
			ItemData val = ((Humanoid)player).m_leftItem ?? ((Humanoid)player).m_hiddenLeftItem;
			if (PairedLoadout.IsSupportedWeapon(val))
			{
				bool flag = false;
				if (((Humanoid)player).m_leftItem == val)
				{
					((Humanoid)player).m_leftItem = null;
					flag = true;
				}
				if (((Humanoid)player).m_hiddenLeftItem == val)
				{
					((Humanoid)player).m_hiddenLeftItem = null;
					flag = true;
				}
				if (flag)
				{
					val.m_equipped = false;
					RunSetupEquipment(player);
				}
			}
		}

		private static void ReconcileCapturedFlags(Player player, EquipCapture state)
		{
			ReconcileItemFlag(player, state.RequestedItem);
			ReconcileItemFlag(player, state.PreviousMain);
			ReconcileItemFlag(player, state.PreviousOffhand);
			ReconcileItemFlag(player, state.PreviousHiddenMain);
			ReconcileItemFlag(player, state.PreviousHiddenOffhand);
		}

		private static void ReconcileItemFlag(Player player, ItemData item)
		{
			if (!((Object)(object)player == (Object)null) && item != null && PairedLoadout.IsSupportedWeapon(item))
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory == null || !inventory.ContainsItem(item))
				{
					RemoveHandReferences(player, item);
					item.m_equipped = false;
				}
				else
				{
					item.m_equipped = IsHandReference(player, item);
				}
			}
		}
	}
	internal static class RuntimePatchInstaller
	{
		private static bool _installed;

		internal static void Install(Harmony harmony)
		{
			if (!_installed && harmony != null)
			{
				_installed = true;
				InstallInventorySentinelGuard(harmony);
				InstallPlayerSaveCheckpoint(harmony);
			}
		}

		private static void InstallInventorySentinelGuard(Harmony harmony)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			MethodInfo method = typeof(RuntimePatchInstaller).GetMethod("InventoryAddItemPrefix", BindingFlags.Static | BindingFlags.NonPublic);
			if (method == null)
			{
				return;
			}
			int num = 0;
			MethodInfo[] array;
			try
			{
				array = typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}
			catch
			{
				array = new MethodInfo[0];
			}
			foreach (MethodInfo methodInfo in array)
			{
				if (!IsSupportedInventoryAddItem(methodInfo))
				{
					continue;
				}
				try
				{
					HarmonyMethod val = new HarmonyMethod(method)
					{
						priority = 800
					};
					harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				catch (Exception ex)
				{
					if (Plugin.Log != null)
					{
						Plugin.Log.LogDebug((object)("Inventory sentinel guard skipped safely for " + methodInfo?.ToString() + ": " + ex.GetBaseException().Message));
					}
				}
			}
			if (Plugin.Log != null)
			{
				Plugin.Log.LogDebug((object)("Inventory sentinel guard installed on " + num + " Inventory.AddItem overload(s)."));
			}
		}

		private static bool IsSupportedInventoryAddItem(MethodInfo method)
		{
			if (method == null || !string.Equals(method.Name, "AddItem", StringComparison.Ordinal))
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			if (parameters.Length < 4 || parameters.Length > 5)
			{
				return false;
			}
			return parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(int) && parameters[3].ParameterType == typeof(int);
		}

		private static void InventoryAddItemPrefix(Inventory __instance, ItemData __0, int __1, int __2, int __3)
		{
			if (__instance != null && __2 == -1 && __3 == -1)
			{
				EquipmentPatches.RepairSentinelCollision(__instance);
			}
		}

		private static void InstallPlayerSaveCheckpoint(Harmony harmony)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			MethodInfo method = typeof(RuntimePatchInstaller).GetMethod("PlayerSavePrefix", BindingFlags.Static | BindingFlags.NonPublic);
			if (method == null)
			{
				return;
			}
			List<MethodInfo> list = new List<MethodInfo>();
			MethodInfo[] array;
			try
			{
				array = typeof(Player).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}
			catch
			{
				array = new MethodInfo[0];
			}
			foreach (MethodInfo methodInfo in array)
			{
				if (methodInfo != null && string.Equals(methodInfo.Name, "Save", StringComparison.Ordinal))
				{
					list.Add(methodInfo);
				}
			}
			int num = 0;
			for (int j = 0; j < list.Count; j++)
			{
				try
				{
					HarmonyMethod val = new HarmonyMethod(method);
					val.priority = 0;
					val.after = new string[1] { "M2Valheim.SkillsReworked" };
					HarmonyMethod val2 = val;
					harmony.Patch((MethodBase)list[j], val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				catch (Exception ex)
				{
					if (Plugin.Log != null)
					{
						Plugin.Log.LogDebug((object)("Player.Save checkpoint skipped safely for " + list[j]?.ToString() + ": " + ex.GetBaseException().Message));
					}
				}
			}
			if (Plugin.Log != null)
			{
				Plugin.Log.LogDebug((object)("Dual Mastery save checkpoint installed on " + num + " Player.Save overload(s)."));
			}
		}

		private static void PlayerSavePrefix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				EquipmentPatches.RepairBeforePlayerSave(__instance);
				MasterySkillPersistence.Capture(__instance);
			}
		}
	}
	[HarmonyPatch]
	internal static class VisualPatches
	{
		private sealed class TrailCacheEntry
		{
			internal GameObject Visual;

			internal MeleeWeaponTrail[] Trails;
		}

		private static readonly Dictionary<int, TrailCacheEntry> OffhandTrailCache = new Dictionary<int, TrailCacheEntry>();

		[HarmonyPatch(typeof(Player), "Awake")]
		[HarmonyPostfix]
		[HarmonyAfter(new string[] { "ishid4.mods.betterarchery", "Raelaziel.OdinHorse", "windstride.bowanimations" })]
		[HarmonyPriority(0)]
		private static void InitializeVisualState(Player __instance)
		{
			if ((Object)(object)__instance != (Object)null)
			{
				AnimationControllerService.Refresh(__instance);
				AnimationPlaybackSpeed.Refresh(__instance);
				MountedDualWieldPose.Ensure(__instance);
			}
		}

		[HarmonyPatch(typeof(Player), "Update")]
		[HarmonyPostfix]
		[HarmonyAfter(new string[] { "ishid4.mods.betterarchery", "Raelaziel.OdinHorse", "windstride.bowanimations" })]
		[HarmonyPriority(0)]
		private static void RefreshVisualState(Player __instance)
		{
			AnimationControllerService.Refresh(__instance);
			AnimationPlaybackSpeed.Refresh(__instance);
		}

		[HarmonyPatch(typeof(Humanoid), "StartAttack")]
		[HarmonyPrefix]
		[HarmonyAfter(new string[] { "ishid4.mods.betterarchery", "Raelaziel.OdinHorse", "windstride.bowanimations" })]
		[HarmonyPriority(0)]
		private static void RefreshBeforeLocalAttack(Humanoid __instance)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val != (Object)null)
			{
				AnimationControllerService.Refresh(val);
			}
		}

		[HarmonyPatch(typeof(ZSyncAnimation), "SetTrigger")]
		[HarmonyPrefix]
		[HarmonyAfter(new string[] { "ishid4.mods.betterarchery", "Raelaziel.OdinHorse", "windstride.bowanimations" })]
		[HarmonyPriority(0)]
		private static void RefreshBeforeLocalAnimationTrigger(ZSyncAnimation __instance, string name)
		{
			RefreshBeforeTrigger(__instance, name);
		}

		[HarmonyPatch(typeof(ZSyncAnimation), "RPC_SetTrigger")]
		[HarmonyPrefix]
		[HarmonyAfter(new string[] { "ishid4.mods.betterarchery", "Raelaziel.OdinHorse", "windstride.bowanimations" })]
		[HarmonyPriority(0)]
		private static void RefreshBeforeRemoteAnimationTrigger(ZSyncAnimation __instance, string name)
		{
			RefreshBeforeTrigger(__instance, name);
		}

		private static void RefreshBeforeTrigger(ZSyncAnimation syncAnimation, string name)
		{
			if (!string.Equals(name, "equip_hip", StringComparison.Ordinal))
			{
				Player val = (((Object)(object)syncAnimation != (Object)null) ? ((Component)syncAnimation).GetComponent<Player>() : null);
				if ((Object)(object)val != (Object)null)
				{
					AnimationControllerService.Refresh(val);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDestroy")]
		[HarmonyPostfix]
		private static void CleanupPlayerState(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				EquipmentPatches.ForgetPlayer(__instance);
				PairedAttackRuntime.ForgetPlayer(__instance);
				WindstrideCompatibility.ForgetPlayer(__instance);
				BetterArcheryCompatibility.ForgetTrackingForExternalOwner(__instance);
				ForgetTrailCache(((Humanoid)__instance).m_visEquipment);
			}
		}

		[HarmonyPatch(typeof(VisEquipment), "SetWeaponTrails")]
		[HarmonyPostfix]
		private static void MirrorTrailStateToOffhand(VisEquipment __instance, bool enabled)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)__instance).GetInstanceID();
			GameObject leftItemInstance = __instance.m_leftItemInstance;
			if ((Object)(object)leftItemInstance == (Object)null)
			{
				OffhandTrailCache.Remove(instanceID);
				return;
			}
			if (!OffhandTrailCache.TryGetValue(instanceID, out var value) || value == null || value.Visual != leftItemInstance)
			{
				value = new TrailCacheEntry
				{
					Visual = leftItemInstance,
					Trails = leftItemInstance.GetComponentsInChildren<MeleeWeaponTrail>(true)
				};
				OffhandTrailCache[instanceID] = value;
			}
			MeleeWeaponTrail[] trails = value.Trails;
			if (trails == null)
			{
				return;
			}
			foreach (MeleeWeaponTrail val in trails)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.Emit = enabled;
				}
			}
		}

		private static void ForgetTrailCache(VisEquipment visuals)
		{
			if ((Object)(object)visuals != (Object)null)
			{
				OffhandTrailCache.Remove(((Object)visuals).GetInstanceID());
			}
		}

		[HarmonyPatch(typeof(VisEquipment), "AttachItem")]
		[HarmonyPostfix]
		private static void OffsetSecondBackMelee(VisEquipment __instance, Transform joint, bool backAttach, GameObject __result)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if (ModSettings.SeparateBackWeapons.Value && !((Object)(object)__instance == (Object)null) && !((Object)(object)joint == (Object)null) && !((Object)(object)__result == (Object)null) && backAttach && !((Object)(object)joint != (Object)(object)__instance.m_backMelee))
			{
				Transform transform = __result.transform;
				if (!((Object)(object)transform.parent != (Object)(object)joint) && transform.GetSiblingIndex() > 0)
				{
					transform.localPosition = new Vector3(-0.003f, 0f, 0.003f);
					transform.localEulerAngles = new Vector3(0f, 80f, 0f);
				}
			}
		}
	}
}
namespace Balrond.DualMastery.Core
{
	internal struct ResourceQuote
	{
		internal float Stamina { get; private set; }

		internal float Eitr { get; private set; }

		internal float Health { get; private set; }

		internal ResourceQuote(float stamina, float eitr, float health)
		{
			Stamina = Mathf.Max(0f, stamina);
			Eitr = Mathf.Max(0f, eitr);
			Health = Mathf.Max(0f, health);
		}

		internal ResourceQuote Scale(float factor)
		{
			return new ResourceQuote(Stamina * factor, Eitr * factor, Health * factor);
		}

		internal ResourceQuote Add(ResourceQuote other)
		{
			return new ResourceQuote(Stamina + other.Stamina, Eitr + other.Eitr, Health + other.Health);
		}
	}
	internal static class CombatPolicy
	{
		internal static float Mastery(Player player)
		{
			if (MasterySkillPatches.HasIdCollision)
			{
				return 0f;
			}
			return Mathf.Clamp01(MasterySkillPersistence.GetBackingLevel(player) / 100f);
		}

		internal static float DamageFactor(Player player, bool secondary)
		{
			float num = Mathf.Lerp(ModSettings.OffhandDamageAtZero.Value, ModSettings.OffhandDamageAtMax.Value, Mastery(player));
			float num2 = (secondary ? Mathf.Max(0f, ModSettings.SecondaryDamageMultiplier.Value) : 1f);
			float num3 = Mathf.Max(0f, ModSettings.PairedDamageScale.Value);
			return Mathf.Max(0f, num * num2 * num3);
		}

		internal static float ForceFactor(Player player)
		{
			return Mathf.Max(0f, Mathf.Lerp(ModSettings.OffhandForceAtZero.Value, ModSettings.OffhandForceAtMax.Value, Mastery(player)));
		}

		internal static float ExtraResourceFactor(Player player, bool secondary)
		{
			float num = (secondary ? ModSettings.SecondaryExtraResourceRatio.Value : ModSettings.PrimaryExtraResourceRatio.Value);
			float num2 = Mathf.Clamp01(ModSettings.ResourceDiscountAtMaxMastery.Value) * Mastery(player);
			return Mathf.Max(0f, num * (1f - num2));
		}

		internal static float MasteryXp(bool secondary)
		{
			if (MasterySkillPatches.HasIdCollision || !ModSettings.MasteryProgressionEnabled.Value)
			{
				return 0f;
			}
			float num = (secondary ? ModSettings.SecondaryMasteryXp.Value : ModSettings.PrimaryMasteryXp.Value);
			return Mathf.Max(0f, num * Mathf.Max(0f, ModSettings.MasteryXpMultiplier.Value));
		}

		internal static float AnimationSpeed(bool secondary)
		{
			float num = (secondary ? ModSettings.SecondaryAnimationSpeed.Value : ModSettings.PrimaryAnimationSpeed.Value);
			return Mathf.Clamp(num, 0.05f, 5f);
		}
	}
	internal static class NetworkAuthority
	{
		internal static bool IsOwner(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			ZNetView nview = ((Character)player).m_nview;
			if ((Object)(object)nview != (Object)null && nview.IsValid())
			{
				return nview.IsOwner();
			}
			return player == Player.m_localPlayer;
		}
	}
	internal static class OffhandAttackEngine
	{
		private static bool _costProbeWarningLogged;

		private static bool _executeWarningLogged;

		private static bool TryCreateCostProbe(Player player, ItemData weapon, Attack template, out Attack probe)
		{
			probe = null;
			if ((Object)(object)player == (Object)null || weapon == null || template == null)
			{
				return false;
			}
			try
			{
				Attack val = template.Clone();
				val.m_character = (Humanoid)(object)player;
				val.m_weapon = weapon;
				val.m_attackDrawPercentage = 1f;
				probe = val;
				return true;
			}
			catch (Exception ex)
			{
				if (!_costProbeWarningLogged && Plugin.Log != null)
				{
					_costProbeWarningLogged = true;
					Plugin.Log.LogWarning((object)("Could not create off-hand resource probe; further identical warnings are suppressed: " + ex.Message));
				}
				return false;
			}
		}

		internal static ResourceQuote Quote(Player player, ItemData weapon, Attack template)
		{
			if (!TryCreateCostProbe(player, weapon, template, out var probe))
			{
				return default(ResourceQuote);
			}
			return new ResourceQuote(probe.GetAttackStamina(), probe.GetAttackEitr(), probe.GetAttackHealth());
		}

		internal static bool Execute(PairedLoadout loadout, Attack source, bool secondary)
		{
			if ((Object)(object)loadout.Owner == (Object)null || !NetworkAuthority.IsOwner(loadout.Owner) || loadout.OffHand == null || source == null)
			{
				return false;
			}
			Attack val = PairedLoadout.SelectAttack(loadout.OffHand, secondary);
			if (val == null)
			{
				return false;
			}
			float num = Mathf.Clamp01(source.m_attackDrawPercentage);
			float num2 = CombatPolicy.DamageFactor(loadout.Owner, secondary);
			float num3 = CombatPolicy.ForceFactor(loadout.Owner);
			try
			{
				Attack val2 = val.Clone();
				val2.m_damageMultiplier *= num2;
				val2.m_forceMultiplier *= num3;
				val2.m_staggerMultiplier *= num3;
				PairedAttackRuntime.NormalizeAttackTypeForPlayer(loadout.Owner, val2, secondary);
				val2.StartWithoutAnimation((Humanoid)(object)loadout.Owner, ((Character)loadout.Owner).m_body, ((Humanoid)loadout.Owner).m_visEquipment, loadout.OffHand, num);
				return true;
			}
			catch (Exception ex)
			{
				if (!_executeWarningLogged && Plugin.Log != null)
				{
					_executeWarningLogged = true;
					Plugin.Log.LogWarning((object)("Could not execute off-hand attack; further identical warnings are suppressed: " + ex.Message));
				}
				return false;
			}
		}
	}
	internal enum PrimaryTriggerRole
	{
		OffhandFirst,
		MainSecond,
		Ignore
	}
	internal static class PairedAttackRuntime
	{
		private sealed class State
		{
			internal Attack Attack;

			internal bool Secondary;

			internal int PrimaryTriggerIndex;

			internal bool OffhandConsumed;

			internal bool MasteryAwarded;
		}

		private static readonly Dictionary<int, State> States = new Dictionary<int, State>();

		private static readonly FieldInfo AttackTypeField = typeof(Attack).GetField("m_attackType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static void ForgetPlayer(Player player)
		{
			if ((Object)(object)player != (Object)null)
			{
				States.Remove(((Object)player).GetInstanceID());
			}
		}

		internal static void Begin(Player player, Attack attack, bool secondary)
		{
			if (!((Object)(object)player == (Object)null) && attack != null)
			{
				int instanceID = ((Object)player).GetInstanceID();
				if (!States.TryGetValue(instanceID, out var value))
				{
					value = new State();
					States[instanceID] = value;
				}
				value.Attack = attack;
				value.Secondary = secondary;
				value.PrimaryTriggerIndex = 0;
				value.OffhandConsumed = false;
				value.MasteryAwarded = false;
				NormalizePrimaryAttackType(player, attack, secondary);
			}
		}

		internal static PrimaryTriggerRole ConsumePrimaryTrigger(Player player, Attack attack)
		{
			State orCreateState = GetOrCreateState(player, attack, secondaryFallback: false);
			if (orCreateState == null || orCreateState.Secondary)
			{
				return PrimaryTriggerRole.Ignore;
			}
			switch (orCreateState.PrimaryTriggerIndex++)
			{
			case 0:
				orCreateState.OffhandConsumed = true;
				return PrimaryTriggerRole.OffhandFirst;
			case 1:
				return PrimaryTriggerRole.MainSecond;
			default:
				return PrimaryTriggerRole.Ignore;
			}
		}

		internal static bool ConsumeExternalSingleWindowPrimary(Player player, Attack attack)
		{
			State orCreateState = GetOrCreateState(player, attack, secondaryFallback: false);
			if (orCreateState == null || orCreateState.Secondary)
			{
				return false;
			}
			return orCreateState.PrimaryTriggerIndex++ == 0;
		}

		internal static bool TryConsumeSameWindowOffhand(Player player, Attack attack)
		{
			State orCreateState = GetOrCreateState(player, attack, secondaryFallback: false);
			if (orCreateState == null || orCreateState.OffhandConsumed)
			{
				return false;
			}
			orCreateState.OffhandConsumed = true;
			return true;
		}

		internal static bool TryConsumeSecondaryOffhand(Player player, Attack attack)
		{
			State orCreateState = GetOrCreateState(player, attack, secondaryFallback: true);
			if (orCreateState == null || !orCreateState.Secondary)
			{
				return false;
			}
			return TryConsumeSameWindowOffhand(player, attack);
		}

		internal static bool TryAwardMastery(Player player, Attack attack)
		{
			State orCreateState = GetOrCreateState(player, attack, secondaryFallback: false);
			if (orCreateState == null || orCreateState.MasteryAwarded)
			{
				return false;
			}
			orCreateState.MasteryAwarded = true;
			return true;
		}

		private static State GetOrCreateState(Player player, Attack attack, bool secondaryFallback)
		{
			if ((Object)(object)player == (Object)null || attack == null)
			{
				return null;
			}
			int instanceID = ((Object)player).GetInstanceID();
			if (!States.TryGetValue(instanceID, out var value) || value == null || value.Attack != attack)
			{
				value = new State
				{
					Attack = attack,
					Secondary = secondaryFallback,
					PrimaryTriggerIndex = 0,
					OffhandConsumed = false,
					MasteryAwarded = false
				};
				States[instanceID] = value;
			}
			return value;
		}

		internal static void NormalizePrimaryAttackType(Player player, Attack attack, bool secondary)
		{
			NormalizeAttackTypeForPlayer(player, attack, secondary);
		}

		internal static void NormalizeAttackTypeForPlayer(Player player, Attack attack, bool secondary)
		{
			if (!ExternalAnimationCompatibility.PreserveExternalPrimaryAttackSemantics(player))
			{
				NormalizeAttackType(attack, secondary);
			}
		}

		internal static void NormalizeAttackType(Attack attack, bool secondary)
		{
			if (attack == null || AttackTypeField == null || !AttackTypeField.FieldType.IsEnum)
			{
				return;
			}
			try
			{
				string value = (secondary ? "Vertical" : "Horizontal");
				object value2 = Enum.Parse(AttackTypeField.FieldType, value, ignoreCase: true);
				AttackTypeField.SetValue(attack, value2);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Could not normalize paired attack type: " + ex.Message));
			}
		}

		internal static bool TryFaceCursor(Player player, out Quaternion previousRotation)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			previousRotation = Quaternion.identity;
			if ((Object)(object)player == (Object)null || player != Player.m_localPlayer || ExternalAnimationCompatibility.PreserveExternalPrimaryAttackSemantics(player))
			{
				return false;
			}
			Vector3 val = Vector3.zero;
			if ((Object)(object)GameCamera.instance != (Object)null)
			{
				val = ((Component)GameCamera.instance).transform.forward;
			}
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				return false;
			}
			((Vector3)(ref val)).Normalize();
			previousRotation = ((Component)player).transform.rotation;
			((Component)player).transform.rotation = Quaternion.LookRotation(val, Vector3.up);
			return true;
		}

		internal static void RestoreFacing(Player player, bool changed, Quaternion previousRotation)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (changed && (Object)(object)player != (Object)null)
			{
				((Component)player).transform.rotation = previousRotation;
			}
		}
	}
	internal readonly struct PairedLoadout
	{
		internal Player Owner { get; }

		internal ItemData MainHand { get; }

		internal ItemData OffHand { get; }

		private PairedLoadout(Player owner, ItemData mainHand, ItemData offHand)
		{
			Owner = owner;
			MainHand = mainHand;
			OffHand = offHand;
		}

		internal static bool TryGet(Player player, out PairedLoadout loadout)
		{
			loadout = default(PairedLoadout);
			if ((Object)(object)player == (Object)null || !ModSettings.Enabled.Value)
			{
				return false;
			}
			ItemData rightItem = ((Humanoid)player).m_rightItem;
			ItemData leftItem = ((Humanoid)player).m_leftItem;
			if (!CanPair(rightItem, leftItem))
			{
				return false;
			}
			loadout = new PairedLoadout(player, rightItem, leftItem);
			return true;
		}

		internal static bool HasAnimationIntent(Player player)
		{
			if ((Object)(object)player == (Object)null || !ModSettings.Enabled.Value)
			{
				return false;
			}
			ItemData main = ((Humanoid)player).m_rightItem ?? ((Humanoid)player).m_hiddenRightItem;
			ItemData off = ((Humanoid)player).m_leftItem ?? ((Humanoid)player).m_hiddenLeftItem;
			return CanPair(main, off);
		}

		internal static bool CanPair(ItemData main, ItemData off)
		{
			//IL_0034: 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)
			if (main == off || !IsSupportedWeapon(main) || !IsSupportedWeapon(off))
			{
				return false;
			}
			return ModSettings.AllowMixedWeaponSkills.Value || main.m_shared.m_skillType == off.m_shared.m_skillType;
		}

		internal static bool IsSupportedWeapon(ItemData item)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			if (item?.m_shared == null)
			{
				return false;
			}
			if (OdinHorseCompatibility.IsReservedPairWeapon(item))
			{
				return false;
			}
			return (int)item.m_shared.m_itemType == 3 && (Object)(object)item.m_shared.m_buildPieces == (Object)null && item.m_shared.m_attack != null && (int)item.m_shared.m_skillType != 5;
		}

		internal static Attack SelectAttack(ItemData weapon, bool secondary)
		{
			if (weapon?.m_shared == null)
			{
				return null;
			}
			if (secondary && weapon.m_shared.m_secondaryAttack != null)
			{
				return weapon.m_shared.m_secondaryAttack;
			}
			return weapon.m_shared.m_attack;
		}
	}
}
namespace Balrond.DualMastery.Configuration
{
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order = null;

		public bool? Browsable = null;

		public string Category = null;

		public string DispName = null;

		public bool? IsAdvanced = null;

		public bool? ReadOnly = null;

		public bool? HideDefaultButton = null;

		public bool? ShowRangeAsPercent = null;

		public object DefaultValue = null;
	}
	internal static class ModSettings
	{
		private static readonly ConfigSync Sync = new ConfigSync("balrond.astafaraios.BalrondDualMastery")
		{
			DisplayName = "BalrondDualMastery",
			CurrentVersion = "0.3.14",
			MinimumRequiredVersion = "0.3.14",
			ModRequired = true
		};

		private static readonly MethodInfo VisEquipmentUpdateVisuals = typeof(VisEquipment).GetMethod("UpdateVisuals", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		internal static ConfigEntry<bool> LockConfiguration { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<bool> AllowMixedWeaponSkills { get; private set; }

		internal static ConfigEntry<bool> SeparateBackWeapons { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> OffhandSwapShortcut { get; private set; }

		internal static ConfigEntry<float> OffhandDamageAtZero { get; private set; }

		internal static ConfigEntry<float> PairedDamageScale { get; private set; }

		internal static ConfigEntry<float> OffhandDamageAtMax { get; private set; }

		internal static ConfigEntry<float> SecondaryDamageMultiplier { get; private set; }

		internal static ConfigEntry<float> OffhandForceAtZero { get; private set; }

		internal static ConfigEntry<float> OffhandForceAtMax { get; private set; }

		internal static ConfigEntry<float> PrimaryExtraResourceRatio { get; private set; }

		internal static ConfigEntry<float> SecondaryExtraResourceRatio { get; private set; }

		internal static ConfigEntry<float> ResourceDiscountAtMaxMastery { get; private set; }

		internal static ConfigEntry<bool> MasteryProgressionEnabled { get; private set; }

		internal static ConfigEntry<float> PrimaryMasteryXp { get; private set; }

		internal static ConfigEntry<float> SecondaryMasteryXp { get; private set; }

		internal static ConfigEntry<float> MasteryXpMultiplier { get; private set; }

		internal static ConfigEntry<float> PrimaryAnimationSpeed { get; private set; }

		internal static ConfigEntry<float> SecondaryAnimationSpeed { get; private set; }

		internal static ConfigEntry<bool> ExtraPrimarySwingAudio { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			LockConfiguration = BindRaw(config, "1 - General", "Lock Configuration", defaultValue: true, "When enabled on a server, synchronized gameplay settings are controlled by the server.", -100);
			Sync.AddLockingConfigEntry<bool>(LockConfiguration);
			Enabled = BindSynced(config, "1 - General", "Enabled", defaultValue: true, "Enable paired one-handed weapon behavior.", -90);
			Enabled.SettingChanged += OnEnabledChanged;
			AllowMixedWeaponSkills = BindSynced(config, "2 - Equipment", "Allow Mixed Weapon Skills", defaultValue: true, "Allow the two equipped weapons to use different weapon skill categories.", -100);
			AllowMixedWeaponSkills.SettingChanged += OnEquipmentPolicyChanged;
			SeparateBackWeapons = BindLocal(config, "2 - Equipment", "Separate Back Weapons", defaultValue: true, "Offset the second holstered weapon so paired weapons do not overlap. Client-side visual setting.", -90);
			SeparateBackWeapons.SettingChanged += OnSeparateBackWeaponsChanged;
			OffhandSwapShortcut = BindLocal<KeyboardShortcut>(config, "2 - Equipment", "Replace Offhand Modifier", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Local modifier for replacing only the left-hand item. Without the modifier, selecting a supported one-handed weapon immediately makes it the main hand and moves the previous main weapon to the off-hand. Selecting an incompatible/2H/tool item follows normal vanilla equip behavior.", -80);
			OffhandDamageAtZero = BindSyncedRange(config, "3 - Combat", "Offhand Damage At Skill 0", 0.55f, 0f, 10f, "Off-hand damage multiplier at 0 Dual Mastery.", -100);
			OffhandDamageAtMax = BindSyncedRange(config, "3 - Combat", "Offhand Damage At Skill 100", 0.8f, 0f, 10f, "Off-hand damage multiplier at 100 Dual Mastery.", -90);
			PairedDamageScale = BindSyncedRange(config, "3 - Combat", "Dual Wield Damage Scale", 0.75f, 0f, 10f, "Global balance scale applied to the off-hand strike after mastery and secondary multipliers. 1.0 restores legacy 0.2.7 damage.", -80);
			SecondaryDamageMultiplier = BindSyncedRange(config, "3 - Combat", "Secondary Damage Multiplier", 1.08f, 0f, 10f, "Additional multiplier applied to the off-hand strike during a secondary attack.", -70);
			OffhandForceAtZero = BindSyncedRange(config, "3 - Combat", "Offhand Force At Skill 0", 0.7f, 0f, 10f, "Off-hand force/stagger multiplier at 0 Dual Mastery.", -60);
			OffhandForceAtMax = BindSyncedRange(config, "3 - Combat", "Offhand Force At Skill 100", 0.95f, 0f, 10f, "Off-hand force/stagger multiplier at 100 Dual Mastery.", -50);
			PrimaryExtraResourceRatio = BindSyncedRange(config, "3 - Combat", "Primary Extra Resource Ratio", 0.6f, 0f, 10f, "Fraction of the off-hand attack resource cost added to a primary paired attack.", -40);
			SecondaryExtraResourceRatio = BindSyncedRange(config, "3 - Combat", "Secondary Extra Resource Ratio", 0.75f, 0f, 10f, "Fraction of the off-hand attack resource cost added to a secondary paired attack.", -30);
			ResourceDiscountAtMaxMastery = BindSyncedRange(config, "3 - Combat", "Resource Discount At Skill 100", 0.2f, 0f, 1f, "Maximum reduction of the off-hand resource surcharge at 100 Dual Mastery.", -20, showRangeAsPercent: true);
			MasteryProgressionEnabled = BindSynced(config, "4 - Skill", "Mastery Progression Enabled", defaultValue: true, "Grant Dual Mastery XP after successful paired attacks. If SkillsReworked is installed it owns the live progression/death/load rules; otherwise Dual Mastery uses its native persistent backend. A local mirror is maintained in both modes for fallback/migration.", -100);
			PrimaryMasteryXp = BindSyncedRange(config, "4 - Skill", "Primary Attack XP", 0.12f, 0f, 100f, "RaiseSkill factor granted to the persistent Dual Mastery skill after a successful primary paired attack.", -90);
			SecondaryMasteryXp = BindSyncedRange(config, "4 - Skill", "Secondary Attack XP", 0.18f, 0f, 100f, "RaiseSkill factor granted to the persistent Dual Mastery skill after a successful secondary paired attack.", -80);
			MasteryXpMultiplier = BindSyncedRange(config, "4 - Skill", "Mastery XP Multiplier", 1f, 0f, 100f, "Global multiplier applied to the Dual Mastery RaiseSkill factor.", -70);
			PrimaryAnimationSpeed = BindSyncedRange(config, "5 - Animation", "Primary Animation Speed", 2f, 0.05f, 5f, "Playback multiplier for primary paired attacks. Primary clips ignore their legacy authored Speed=0.7 marker, so 2.0 means an actual 2x gameplay cadence. Mounted OdinHorse combat keeps OdinHorse's own cadence.", -100);
			SecondaryAnimationSpeed = BindSyncedRange(config, "5 - Animation", "Secondary Animation Speed", 1f, 0.05f, 5f, "Additional playback multiplier for secondary paired attacks. The authored SPEED AnimationEvent inside the active Dual Mastery clip is applied first; this setting is a server-controlled tuning multiplier on top of it. Mounted OdinHorse combat keeps OdinHorse's own cadence.", -90);
			ExtraPrimarySwingAudio = BindLocal(config, "5 - Animation", "Extra Primary Swing Audio", defaultValue: true, "Play the extra non-damaging swing sounds authored for the paired primary animations. This is client-side only. Disable it as a diagnostic/fallback if a third-party weapon uses unusually heavy custom start-effect audio prefabs.", -80);
		}

		private static void OnEnabledChanged(object sender, EventArgs args)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && Enabled != null)
			{
				EquipmentPatches.HandleEnabledChanged(localPlayer, Enabled.Value);
			}
		}

		private static void OnEquipmentPolicyChanged(object sender, EventArgs args)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				EquipmentPatches.SanitizeRuntimeState(localPlayer);
			}
		}

		private static void OnSeparateBackWeaponsChanged(object sender, EventArgs args)
		{
			Player localPlayer = Player.m_localPlayer;
			VisEquipment val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).m_visEquipment : null);
			if ((Object)(object)val == (Object)null || VisEquipmentUpdateVisuals == null)
			{
				return;
			}
			try
			{
				VisEquipmentUpdateVisuals.Invoke(val, null);
			}
			catch (Exception ex)
			{
				if (Plugin.Log != null)
				{
					Plugin.Log.LogDebug((object)("Could not refresh back-weapon visuals after config change: " + ex.Message));
				}
			}
		}

		private static ConfigEntry<T> BindRaw<T>(ConfigFile config, string section, string key, T defaultValue, string description, int order)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			return config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			}));
		}

		private static ConfigEntry<T> BindSynced<T>(ConfigFile config, string section, string key, T defaultValue, string description, int order)
		{
			ConfigEntry<T> val = BindRaw(config, section, key, defaultValue, description, order);
			Sync.AddConfigEntry<T>(val).SynchronizedConfig = true;
			return val;
		}

		private static ConfigEntry<float> BindSyncedRange(ConfigFile config, string section, string key, float defaultValue, float min, float max, string description, int order, bool showRangeAsPercent = false)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			ConfigEntry<float> val = config.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order,
					ShowRangeAsPercent = (showRangeAsPercent ? new bool?(true) : ((bool?)null))
				}
			}));
			Sync.AddConfigEntry<float>(val).SynchronizedConfig = true;
			return val;
		}

		private static ConfigEntry<T> BindLocal<T>(ConfigFile config, string section, string key, T defaultValue, string description, int order)
		{
			ConfigEntry<T> val = BindRaw(config, section, key, defaultValue, description, order);
			Sync.AddConfigEntry<T>(val).SynchronizedConfig = false;
			return val;
		}

		private static ConfigEntry<float> BindLocalRange(ConfigFile config, string section, string key, float defaultValue, float min, float max, string description, int order)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			ConfigEntry<float> val = config.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			}));
			Sync.AddConfigEntry<float>(val).SynchronizedConfig = false;
			return val;
		}
	}
}
namespace Balrond.DualMastery.Compatibility
{
	internal static class BetterArcheryCompatibility
	{
		private const