Decompiled source of WiresWeaponMaster v0.2.1

WiresWeaponMaster.dll

Decompiled 15 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("WiresWeaponMaster")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("WiresWeaponMaster")]
[assembly: AssemblyTitle("WiresWeaponMaster")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WiresWeaponMaster
{
	internal static class HoldRController
	{
		private const float HoldSeconds = 0.6f;

		private static readonly MethodInfo ShowHandItemsMethod = AccessTools.Method(typeof(Humanoid), "ShowHandItems", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null);

		private static bool _tracking;

		private static bool _fired;

		private static bool _gamepadHold;

		private static float _downTime;

		private static bool _hadHandsBeforePress;

		private static bool _pressHolsteredItems;

		private static bool _pressUnholsteredItems;

		internal static void BeforePlayerUpdate(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!WeaponMasterConfig.IsEnabled)
			{
				ResetTracking();
			}
			else if (!InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && !StoreGui.IsVisible() && !Minimap.IsOpen() && !Hud.InRadial())
			{
				bool buttonDown = ZInput.GetButtonDown("Hide");
				bool flag = ZInput.IsGamepadActive() && ZInput.GetButtonDown("JoyHide") && !ZInput.GetButton("JoyAltKeys") && !((Character)player).InPlaceMode();
				if (buttonDown || flag)
				{
					_tracking = true;
					_fired = false;
					_gamepadHold = flag && !buttonDown;
					_downTime = Time.realtimeSinceStartup;
					_hadHandsBeforePress = WeaponMasterController.HasHandItems((Humanoid)(object)player);
					_pressHolsteredItems = false;
					_pressUnholsteredItems = false;
				}
			}
		}

		internal static void AfterPlayerUpdate(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!WeaponMasterConfig.IsEnabled)
			{
				ResetTracking();
			}
			else
			{
				if (!_tracking)
				{
					return;
				}
				if (!_gamepadHold && ZInput.GetButtonDown("Hide"))
				{
					CaptureVanillaToggleResult(player);
				}
				string text = (_gamepadHold ? "JoyHide" : "Hide");
				if (!_fired && ZInput.GetButton(text) && Time.realtimeSinceStartup - _downTime >= 0.6f)
				{
					if (_gamepadHold)
					{
						PerformGamepadHoldToggle(player);
					}
					Fire(player);
				}
				if (ZInput.GetButtonUp(text) || (!ZInput.GetButton(text) && !ZInput.GetButtonDown(text)))
				{
					ResetTracking();
				}
			}
		}

		private static void CaptureVanillaToggleResult(Player player)
		{
			bool flag = WeaponMasterController.HasHandItems((Humanoid)(object)player);
			bool flag2 = WeaponMasterController.HasHiddenHandItems((Humanoid)(object)player);
			_pressHolsteredItems = _hadHandsBeforePress && !flag && flag2;
			_pressUnholsteredItems = !_hadHandsBeforePress && flag;
		}

		private static void PerformGamepadHoldToggle(Player player)
		{
			if (_hadHandsBeforePress)
			{
				if (!((Character)player).InAttack() && !((Character)player).InDodge())
				{
					((Humanoid)player).HideHandItems(false, true);
				}
			}
			else if ((!((Character)player).IsSwimming() || ((Character)player).IsOnGround()) && !((Character)player).InDodge() && ShowHandItemsMethod != null)
			{
				ShowHandItemsMethod.Invoke(player, new object[2] { false, true });
			}
			CaptureVanillaToggleResult(player);
		}

		private static void Fire(Player player)
		{
			_fired = true;
			WeaponMasterController.ClearHistoryPreserveCurrentHands(player);
			if (_pressHolsteredItems && !_pressUnholsteredItems)
			{
				WeaponMasterController.ClearHiddenHandItems(player);
			}
			WeaponMasterController.PlayClearFeedback(player);
			WeaponVisuals.RefreshLocal();
		}

		internal static void ResetTracking()
		{
			_tracking = false;
			_fired = false;
			_gamepadHold = false;
			_downTime = 0f;
			_hadHandsBeforePress = false;
			_pressHolsteredItems = false;
			_pressUnholsteredItems = false;
		}
	}
	internal enum HolsterLane
	{
		None,
		Shield,
		Tool,
		Melee,
		TwoHandedMelee,
		Bow,
		Atgeir
	}
	internal static class ItemClassifier
	{
		internal static HolsterLane ResolveLane(ItemData item, bool rightHand)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected I4, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected I4, but got Unknown
			if (item == null || item.m_shared == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return HolsterLane.None;
			}
			ItemType val = (((int)item.m_shared.m_attachOverride != 0) ? item.m_shared.m_attachOverride : item.m_shared.m_itemType);
			switch (val - 3)
			{
			default:
				switch (val - 14)
				{
				case 5:
					return HolsterLane.Tool;
				case 0:
				case 8:
					return HolsterLane.TwoHandedMelee;
				case 6:
					return HolsterLane.Atgeir;
				case 1:
					if (!rightHand)
					{
						return HolsterLane.Tool;
					}
					return HolsterLane.Melee;
				default:
					return HolsterLane.None;
				}
			case 2:
				return HolsterLane.Shield;
			case 0:
				return HolsterLane.Melee;
			case 1:
				return HolsterLane.Bow;
			}
		}

		internal static bool HasHolsterVisual(ItemData item)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return false;
			}
			Transform transform = item.m_dropPrefab.transform;
			for (int i = 0; i < transform.childCount; i++)
			{
				string name = ((Object)transform.GetChild(i)).name;
				if (name == "attach_back" || name == "attach")
				{
					return true;
				}
			}
			return false;
		}

		internal static bool UsesRightHandForBackAttach(HolsterLane lane)
		{
			return lane == HolsterLane.Melee;
		}
	}
	internal readonly struct HolsterSelection
	{
		internal readonly int Hash;

		internal readonly int Variant;

		internal readonly int Quality;

		internal int PackedMeta => (Quality << 16) | (Variant & 0xFFFF);

		internal HolsterSelection(int hash, int variant, int quality)
		{
			Hash = hash;
			Variant = variant;
			Quality = quality;
		}

		internal static HolsterSelection FromPacked(int hash, int meta)
		{
			if (hash == 0)
			{
				return default(HolsterSelection);
			}
			int variant = meta & 0xFFFF;
			int quality = (meta >> 16) & 0xFFFF;
			return new HolsterSelection(hash, variant, quality);
		}
	}
	internal static class MultiplayerSync
	{
		private const int LaneCount = 7;

		private static readonly FieldInfo NViewField = AccessTools.Field(typeof(VisEquipment), "m_nview");

		private static readonly int[] HashKeys = BuildKeys("hash");

		private static readonly int[] MetaKeys = BuildKeys("meta");

		private static int[] BuildKeys(string suffix)
		{
			int[] array = new int[7];
			for (int i = 1; i < 7; i++)
			{
				array[i] = StringExtensionMethods.GetStableHashCode("wire.weaponmaster.v2.lane" + i + "." + suffix);
			}
			return array;
		}

		internal static ZNetView GetNetView(VisEquipment vis)
		{
			if (!((Object)(object)vis == (Object)null) && !(NViewField == null))
			{
				object? value = NViewField.GetValue(vis);
				return (ZNetView)((value is ZNetView) ? value : null);
			}
			return null;
		}

		internal static ZDO GetZdo(VisEquipment vis)
		{
			ZNetView netView = GetNetView(vis);
			if (!((Object)(object)netView != (Object)null) || !netView.IsValid())
			{
				return null;
			}
			return netView.GetZDO();
		}

		internal static void Publish(VisEquipment vis, HolsterSelection[] selections)
		{
			if ((Object)(object)vis == (Object)null || selections == null)
			{
				return;
			}
			ZNetView netView = GetNetView(vis);
			if ((Object)(object)netView == (Object)null || !netView.IsValid() || !netView.IsOwner())
			{
				return;
			}
			ZDO zDO = netView.GetZDO();
			if (zDO == null)
			{
				return;
			}
			for (int i = 1; i < 7; i++)
			{
				HolsterSelection holsterSelection = selections[i];
				int hash = holsterSelection.Hash;
				int num = ((hash != 0) ? holsterSelection.PackedMeta : 0);
				if (zDO.GetInt(HashKeys[i], 0) != hash)
				{
					zDO.Set(HashKeys[i], hash, false);
				}
				if (zDO.GetInt(MetaKeys[i], 0) != num)
				{
					zDO.Set(MetaKeys[i], num, false);
				}
			}
		}

		internal static HolsterSelection Read(VisEquipment vis, HolsterLane lane)
		{
			ZDO zdo = GetZdo(vis);
			if (zdo == null || lane == HolsterLane.None)
			{
				return default(HolsterSelection);
			}
			int hash = zdo.GetInt(HashKeys[(int)lane], 0);
			int meta = zdo.GetInt(MetaKeys[(int)lane], 0);
			return HolsterSelection.FromPacked(hash, meta);
		}

		internal static void ClearLocal()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				VisEquipment visEquipment = WeaponVisuals.GetVisEquipment((Humanoid)(object)localPlayer);
				if (!((Object)(object)visEquipment == (Object)null))
				{
					Publish(visEquipment, new HolsterSelection[7]);
				}
			}
		}
	}
	internal static class Patches
	{
		[HarmonyPatch(typeof(Player), "Start")]
		private static class PlayerStartPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Player __instance)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					WeaponMasterController.Reset();
					WeaponMasterController.SeedCurrentState(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDestroy")]
		private static class PlayerDestroyPatch
		{
			[HarmonyPrefix]
			private static void Prefix(Player __instance)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					MultiplayerSync.ClearLocal();
					HoldRController.ResetTracking();
					WeaponVisuals.Cleanup();
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Update")]
		private static class PlayerUpdatePatch
		{
			[HarmonyPrefix]
			private static void Prefix(Player __instance)
			{
				HoldRController.BeforePlayerUpdate(__instance);
			}

			[HarmonyPostfix]
			private static void Postfix(Player __instance)
			{
				HoldRController.AfterPlayerUpdate(__instance);
			}
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem", new Type[]
		{
			typeof(ItemData),
			typeof(bool)
		})]
		private static class EquipItemPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Humanoid __instance, ItemData item, bool __result)
			{
				if (__result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					WeaponMasterController.NoteUsed(__instance, item);
					WeaponVisuals.RefreshLocal();
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "HideHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		private static class HideHandItemsPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Humanoid __instance)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					WeaponVisuals.RefreshLocal();
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "ShowHandItems", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		private static class ShowHandItemsPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Humanoid __instance)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					WeaponVisuals.RefreshLocal();
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "Changed", new Type[]
		{
			typeof(bool),
			typeof(bool)
		})]
		private static class InventoryChangedPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Inventory __instance)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && ((Humanoid)localPlayer).GetInventory() == __instance)
				{
					WeaponVisuals.RefreshLocal();
				}
			}
		}

		[HarmonyPatch(typeof(VisEquipment), "UpdateEquipmentVisuals")]
		private static class VisEquipmentUpdatePatch
		{
			[HarmonyPostfix]
			private static void Postfix(VisEquipment __instance)
			{
				WeaponVisuals.SyncNetwork(__instance);
			}
		}

		[HarmonyPatch(typeof(VisEquipment), "OnDisable")]
		private static class VisEquipmentDisablePatch
		{
			[HarmonyPrefix]
			private static void Prefix(VisEquipment __instance)
			{
				WeaponVisuals.Unregister(__instance);
			}
		}
	}
	[BepInPlugin("wire.valheim.weaponmaster", "Wire's Weapon Master", "0.2.1")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "wire.valheim.weaponmaster";

		public const string Name = "Wire's Weapon Master";

		public const string Version = "0.2.1";

		internal static Plugin Instance;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Instance = this;
			WeaponMasterConfig.Bind(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("wire.valheim.weaponmaster");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Wire's Weapon Master 0.2.1 loaded.");
		}

		internal static void LogInfo(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)message);
			}
		}

		internal static void LogWarning(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)message);
			}
		}

		private void OnDestroy()
		{
			MultiplayerSync.ClearLocal();
			WeaponMasterConfigSync.Reset();
			WeaponVisuals.Cleanup();
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
			Instance = null;
		}
	}
	internal static class VanillaAttachment
	{
		private static readonly MethodInfo AttachBackItemMethod = AccessTools.Method(typeof(VisEquipment), "AttachBackItem", (Type[])null, (Type[])null);

		internal static GameObject AttachBackItem(VisEquipment vis, int hash, int variant, bool rightHand, int quality)
		{
			if ((Object)(object)vis == (Object)null || hash == 0 || AttachBackItemMethod == null)
			{
				return null;
			}
			try
			{
				object? obj = AttachBackItemMethod.Invoke(vis, new object[4] { hash, variant, rightHand, quality });
				return (GameObject)((obj is GameObject) ? obj : null);
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("Vanilla holster attach failed for item " + hash + ": " + ex.GetType().Name);
				return null;
			}
		}
	}
	internal static class WeaponMasterConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> BackShield;

		internal static ConfigEntry<bool> BackTool;

		internal static ConfigEntry<bool> BackMelee;

		internal static ConfigEntry<bool> BackTwohandedMelee;

		internal static ConfigEntry<bool> BackAtgeir;

		internal static ConfigEntry<bool> BackBow;

		internal static bool IsEnabled => WeaponMasterConfigSync.GetEnabled(Enabled == null || Enabled.Value);

		internal static void Bind(ConfigFile file)
		{
			Enabled = file.Bind<bool>("General", "Enabled", true, "Enable Wire's Weapon Master.");
			BackShield = BindCategory(file, "backShield");
			BackTool = BindCategory(file, "backTool");
			BackMelee = BindCategory(file, "backMelee");
			BackTwohandedMelee = BindCategory(file, "backTwohandedMelee");
			BackAtgeir = BindCategory(file, "backAtgeir");
			BackBow = BindCategory(file, "backBow");
			Enabled.SettingChanged += delegate
			{
				OnChanged();
			};
		}

		private static ConfigEntry<bool> BindCategory(ConfigFile file, string name)
		{
			ConfigEntry<bool> obj = file.Bind<bool>("Permanent display categories", name, true, "Keep this vanilla holster category permanently visible when Weapon Master has a recent item for it. Disabling this does not affect normal Valheim stowing; actually stowed items can still appear.");
			obj.SettingChanged += delegate
			{
				OnChanged();
			};
			return obj;
		}

		private static void OnChanged()
		{
			WeaponMasterConfigSync.NotifyLocalChanged();
			if (!IsEnabled)
			{
				HoldRController.ResetTracking();
			}
			WeaponVisuals.RefreshConfig();
		}

		internal static bool IsLaneEnabled(HolsterLane lane)
		{
			if (!IsEnabled)
			{
				return false;
			}
			bool local;
			switch (lane)
			{
			case HolsterLane.Shield:
				local = BackShield == null || BackShield.Value;
				break;
			case HolsterLane.Tool:
				local = BackTool == null || BackTool.Value;
				break;
			case HolsterLane.Melee:
				local = BackMelee == null || BackMelee.Value;
				break;
			case HolsterLane.TwoHandedMelee:
				local = BackTwohandedMelee == null || BackTwohandedMelee.Value;
				break;
			case HolsterLane.Atgeir:
				local = BackAtgeir == null || BackAtgeir.Value;
				break;
			case HolsterLane.Bow:
				local = BackBow == null || BackBow.Value;
				break;
			default:
				return false;
			}
			return WeaponMasterConfigSync.GetLaneEnabled(lane, local);
		}

		internal static bool GetLocalLane(HolsterLane lane)
		{
			switch (lane)
			{
			case HolsterLane.Shield:
				if (BackShield != null)
				{
					return BackShield.Value;
				}
				return true;
			case HolsterLane.Tool:
				if (BackTool != null)
				{
					return BackTool.Value;
				}
				return true;
			case HolsterLane.Melee:
				if (BackMelee != null)
				{
					return BackMelee.Value;
				}
				return true;
			case HolsterLane.TwoHandedMelee:
				if (BackTwohandedMelee != null)
				{
					return BackTwohandedMelee.Value;
				}
				return true;
			case HolsterLane.Atgeir:
				if (BackAtgeir != null)
				{
					return BackAtgeir.Value;
				}
				return true;
			case HolsterLane.Bow:
				if (BackBow != null)
				{
					return BackBow.Value;
				}
				return true;
			default:
				return false;
			}
		}
	}
	internal static class WeaponMasterConfigSync
	{
		private const string RequestRpc = "WWM_ConfigRequest";

		private const string ConfigRpc = "WWM_Config";

		private static bool _registered;

		private static bool _serverEnforced;

		private static bool _enabled = true;

		private static readonly bool[] Lanes = new bool[7] { false, true, true, true, true, true, true };

		internal static bool GetEnabled(bool local)
		{
			if (!_serverEnforced)
			{
				return local;
			}
			return _enabled;
		}

		internal static bool GetLaneEnabled(HolsterLane lane, bool local)
		{
			if (!_serverEnforced || lane <= HolsterLane.None || (int)lane >= Lanes.Length)
			{
				return local;
			}
			return Lanes[(int)lane];
		}

		internal static void Register()
		{
			if (!_registered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register("WWM_ConfigRequest", (Action<long>)OnRequested);
				ZRoutedRpc.instance.Register<ZPackage>("WWM_Config", (Action<long, ZPackage>)OnReceived);
				_registered = true;
			}
		}

		internal static void RequestFromServer()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC("WWM_ConfigRequest", Array.Empty<object>());
			}
		}

		internal static void NotifyLocalChanged()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "WWM_Config", new object[1] { BuildPackage() });
			}
		}

		private static void OnRequested(long sender)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "WWM_Config", new object[1] { BuildPackage() });
			}
		}

		private static ZPackage BuildPackage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(WeaponMasterConfig.Enabled == null || WeaponMasterConfig.Enabled.Value);
			for (int i = 1; i < 7; i++)
			{
				val.Write(WeaponMasterConfig.GetLocalLane((HolsterLane)i));
			}
			return val;
		}

		private static void OnReceived(long sender, ZPackage package)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && package != null)
			{
				_enabled = package.ReadBool();
				for (int i = 1; i < 7; i++)
				{
					Lanes[i] = package.ReadBool();
				}
				_serverEnforced = true;
				if (!_enabled)
				{
					HoldRController.ResetTracking();
				}
				WeaponVisuals.RefreshConfig();
				Plugin.LogInfo("Using Weapon Master settings from the server.");
			}
		}

		internal static void Reset()
		{
			_registered = false;
			_serverEnforced = false;
			_enabled = true;
			for (int i = 1; i < 7; i++)
			{
				Lanes[i] = true;
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class WeaponMasterConfigSyncAwakePatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			WeaponMasterConfigSync.Reset();
			WeaponMasterConfigSync.Register();
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	internal static class WeaponMasterConfigSyncPeerPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			WeaponMasterConfigSync.Register();
			WeaponMasterConfigSync.RequestFromServer();
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class WeaponMasterConfigSyncShutdownPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			WeaponMasterConfigSync.Reset();
		}
	}
	internal static class WeaponMasterController
	{
		private const int LaneCount = 7;

		private static readonly List<ItemData>[] History = new List<ItemData>[7]
		{
			null,
			new List<ItemData>(8),
			new List<ItemData>(8),
			new List<ItemData>(8),
			new List<ItemData>(8),
			new List<ItemData>(8),
			new List<ItemData>(8)
		};

		private static readonly FieldInfo LeftItemField = AccessTools.Field(typeof(Humanoid), "m_leftItem");

		private static readonly FieldInfo RightItemField = AccessTools.Field(typeof(Humanoid), "m_rightItem");

		private static readonly FieldInfo HiddenLeftItemField = AccessTools.Field(typeof(Humanoid), "m_hiddenLeftItem");

		private static readonly FieldInfo HiddenRightItemField = AccessTools.Field(typeof(Humanoid), "m_hiddenRightItem");

		private static readonly MethodInfo TriggerEquipEffectMethod = AccessTools.Method(typeof(Humanoid), "TriggerEquipEffect", (Type[])null, (Type[])null);

		internal static void Reset()
		{
			for (int i = 1; i < 7; i++)
			{
				History[i].Clear();
			}
			WeaponVisuals.RefreshLocal();
		}

		internal static void SeedCurrentState(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				NoteUsed((Humanoid)(object)player, GetLeft((Humanoid)(object)player));
				NoteUsed((Humanoid)(object)player, GetRight((Humanoid)(object)player));
				ItemData hiddenLeft = GetHiddenLeft((Humanoid)(object)player);
				ItemData hiddenRight = GetHiddenRight((Humanoid)(object)player);
				Promote(hiddenLeft, ItemClassifier.ResolveLane(hiddenLeft, rightHand: false));
				Promote(hiddenRight, ItemClassifier.ResolveLane(hiddenRight, rightHand: true));
				WeaponVisuals.RefreshLocal();
			}
		}

		internal static void NoteUsed(Humanoid humanoid, ItemData item)
		{
			if (!((Object)(object)humanoid == (Object)null) && !((Object)(object)humanoid != (Object)(object)Player.m_localPlayer) && item != null)
			{
				bool rightHand = GetRight(humanoid) == item;
				HolsterLane lane = ItemClassifier.ResolveLane(item, rightHand);
				Promote(item, lane);
			}
		}

		private static void Promote(ItemData item, HolsterLane lane)
		{
			if (item == null || lane == HolsterLane.None)
			{
				return;
			}
			for (int i = 1; i < 7; i++)
			{
				List<ItemData> list = History[i];
				for (int num = list.Count - 1; num >= 0; num--)
				{
					if (list[num] == item)
					{
						list.RemoveAt(num);
					}
				}
			}
			History[(int)lane].Insert(0, item);
		}

		internal static ItemData GetVisibleForLane(HolsterLane lane)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || lane == HolsterLane.None)
			{
				return null;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (inventory == null)
			{
				return null;
			}
			PruneMissing(inventory);
			ItemData left = GetLeft((Humanoid)(object)localPlayer);
			ItemData right = GetRight((Humanoid)(object)localPlayer);
			ItemData hiddenLeft = GetHiddenLeft((Humanoid)(object)localPlayer);
			ItemData hiddenRight = GetHiddenRight((Humanoid)(object)localPlayer);
			if (OccupiesLane(hiddenLeft, rightHand: false, lane) || OccupiesLane(hiddenRight, rightHand: true, lane))
			{
				return null;
			}
			List<ItemData> list = History[(int)lane];
			for (int i = 0; i < list.Count; i++)
			{
				ItemData val = list[i];
				if (val != left && val != right && val != hiddenLeft && val != hiddenRight && ItemClassifier.HasHolsterVisual(val))
				{
					return val;
				}
			}
			return null;
		}

		private static bool OccupiesLane(ItemData item, bool rightHand, HolsterLane lane)
		{
			if (item != null)
			{
				return ItemClassifier.ResolveLane(item, rightHand) == lane;
			}
			return false;
		}

		private static void PruneMissing(Inventory inventory)
		{
			for (int i = 1; i < 7; i++)
			{
				List<ItemData> list = History[i];
				for (int num = list.Count - 1; num >= 0; num--)
				{
					ItemData val = list[num];
					if (val == null || !inventory.ContainsItem(val))
					{
						list.RemoveAt(num);
					}
				}
			}
		}

		internal static void ClearHistoryPreserveCurrentHands(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				ItemData left = GetLeft((Humanoid)(object)player);
				ItemData right = GetRight((Humanoid)(object)player);
				for (int i = 1; i < 7; i++)
				{
					History[i].Clear();
				}
				NoteUsed((Humanoid)(object)player, left);
				NoteUsed((Humanoid)(object)player, right);
				WeaponVisuals.RefreshLocal();
			}
		}

		internal static void ClearHiddenHandItems(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				ItemData hiddenLeft = GetHiddenLeft((Humanoid)(object)player);
				ItemData hiddenRight = GetHiddenRight((Humanoid)(object)player);
				if (hiddenLeft != null)
				{
					((Humanoid)player).UnequipItem(hiddenLeft, false);
				}
				if (hiddenRight != null && hiddenRight != hiddenLeft)
				{
					((Humanoid)player).UnequipItem(hiddenRight, false);
				}
			}
		}

		internal static bool HasHandItems(Humanoid humanoid)
		{
			if ((Object)(object)humanoid != (Object)null)
			{
				if (GetLeft(humanoid) == null)
				{
					return GetRight(humanoid) != null;
				}
				return true;
			}
			return false;
		}

		internal static bool HasHiddenHandItems(Humanoid humanoid)
		{
			if ((Object)(object)humanoid != (Object)null)
			{
				if (GetHiddenLeft(humanoid) == null)
				{
					return GetHiddenRight(humanoid) != null;
				}
				return true;
			}
			return false;
		}

		internal static void PlayClearFeedback(Player player)
		{
			if ((Object)(object)player == (Object)null || TriggerEquipEffectMethod == null)
			{
				return;
			}
			try
			{
				TriggerEquipEffectMethod.Invoke(player, new object[1]);
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("Clear feedback failed: " + ex.GetType().Name);
			}
		}

		internal static ItemData GetLeft(Humanoid humanoid)
		{
			if (!((Object)(object)humanoid == (Object)null))
			{
				object? value = LeftItemField.GetValue(humanoid);
				return (ItemData)((value is ItemData) ? value : null);
			}
			return null;
		}

		internal static ItemData GetRight(Humanoid humanoid)
		{
			if (!((Object)(object)humanoid == (Object)null))
			{
				object? value = RightItemField.GetValue(humanoid);
				return (ItemData)((value is ItemData) ? value : null);
			}
			return null;
		}

		internal static ItemData GetHiddenLeft(Humanoid humanoid)
		{
			if (!((Object)(object)humanoid == (Object)null))
			{
				object? value = HiddenLeftItemField.GetValue(humanoid);
				return (ItemData)((value is ItemData) ? value : null);
			}
			return null;
		}

		internal static ItemData GetHiddenRight(Humanoid humanoid)
		{
			if (!((Object)(object)humanoid == (Object)null))
			{
				object? value = HiddenRightItemField.GetValue(humanoid);
				return (ItemData)((value is ItemData) ? value : null);
			}
			return null;
		}
	}
	internal static class WeaponVisuals
	{
		private sealed class VisualState
		{
			internal readonly GameObject[] Instances = (GameObject[])(object)new GameObject[7];

			internal readonly int[] Hashes = new int[7];

			internal readonly int[] Metas = new int[7];
		}

		private const int LaneCount = 7;

		private static readonly FieldInfo VisEquipmentField = AccessTools.Field(typeof(Humanoid), "m_visEquipment");

		private static readonly Dictionary<VisEquipment, VisualState> States = new Dictionary<VisEquipment, VisualState>();

		internal static VisEquipment GetVisEquipment(Humanoid humanoid)
		{
			if (!((Object)(object)humanoid == (Object)null) && !(VisEquipmentField == null))
			{
				object? value = VisEquipmentField.GetValue(humanoid);
				return (VisEquipment)((value is VisEquipment) ? value : null);
			}
			return null;
		}

		internal static void RefreshLocal()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				VisEquipment visEquipment = GetVisEquipment((Humanoid)(object)localPlayer);
				if (!((Object)(object)visEquipment == (Object)null))
				{
					HolsterSelection[] selections = BuildLocalSelections();
					MultiplayerSync.Publish(visEquipment, selections);
					ApplySelections(visEquipment, selections);
				}
			}
		}

		internal static void RefreshConfig()
		{
			RefreshLocal();
			List<VisEquipment> list = new List<VisEquipment>(States.Keys);
			for (int i = 0; i < list.Count; i++)
			{
				VisEquipment val = list[i];
				if ((Object)(object)val != (Object)null)
				{
					SyncNetwork(val);
				}
			}
		}

		private static HolsterSelection[] BuildLocalSelections()
		{
			HolsterSelection[] array = new HolsterSelection[7];
			for (int i = 1; i < 7; i++)
			{
				HolsterLane lane = (HolsterLane)i;
				if (WeaponMasterConfig.IsLaneEnabled(lane))
				{
					ItemData visibleForLane = WeaponMasterController.GetVisibleForLane(lane);
					if (visibleForLane != null && !((Object)(object)visibleForLane.m_dropPrefab == (Object)null))
					{
						array[i] = new HolsterSelection(StringExtensionMethods.GetStableHashCode(((Object)visibleForLane.m_dropPrefab).name), visibleForLane.m_variant, visibleForLane.m_quality);
					}
				}
			}
			return array;
		}

		internal static void SyncNetwork(VisEquipment vis)
		{
			if (!((Object)(object)vis == (Object)null) && vis.m_isPlayer && !vis.m_isArmorStand)
			{
				VisualState orCreateState = GetOrCreateState(vis);
				ZDO zdo = MultiplayerSync.GetZdo(vis);
				int vanillaLeftBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_leftBackItem, 0) : 0);
				int vanillaRightBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_rightBackItem, 0) : 0);
				for (int i = 1; i < 7; i++)
				{
					HolsterLane lane = (HolsterLane)i;
					ApplySelection(vis, orCreateState, lane, MultiplayerSync.Read(vis, lane), vanillaLeftBack, vanillaRightBack);
				}
			}
		}

		private static void ApplySelections(VisEquipment vis, HolsterSelection[] selections)
		{
			if (!((Object)(object)vis == (Object)null) && selections != null)
			{
				VisualState orCreateState = GetOrCreateState(vis);
				ZDO zdo = MultiplayerSync.GetZdo(vis);
				int vanillaLeftBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_leftBackItem, 0) : 0);
				int vanillaRightBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_rightBackItem, 0) : 0);
				for (int i = 1; i < 7; i++)
				{
					ApplySelection(vis, orCreateState, (HolsterLane)i, selections[i], vanillaLeftBack, vanillaRightBack);
				}
			}
		}

		private static VisualState GetOrCreateState(VisEquipment vis)
		{
			if (!States.TryGetValue(vis, out var value))
			{
				value = new VisualState();
				States.Add(vis, value);
			}
			return value;
		}

		private static void ApplySelection(VisEquipment vis, VisualState state, HolsterLane lane, HolsterSelection selection, int vanillaLeftBack, int vanillaRightBack)
		{
			if (!WeaponMasterConfig.IsLaneEnabled(lane))
			{
				selection = default(HolsterSelection);
			}
			int num = selection.Hash;
			int num2 = ((num != 0) ? selection.PackedMeta : 0);
			if (num != 0 && (num == vanillaLeftBack || num == vanillaRightBack))
			{
				num = 0;
				num2 = 0;
				selection = default(HolsterSelection);
			}
			if (state.Hashes[(int)lane] != num || state.Metas[(int)lane] != num2)
			{
				Replace(vis, state, lane, selection, num, num2);
			}
		}

		private static void Replace(VisEquipment vis, VisualState state, HolsterLane lane, HolsterSelection selection, int hash, int meta)
		{
			DestroyInstance(state, (int)lane);
			state.Hashes[(int)lane] = hash;
			state.Metas[(int)lane] = meta;
			if (hash != 0)
			{
				bool rightHand = ItemClassifier.UsesRightHandForBackAttach(lane);
				state.Instances[(int)lane] = VanillaAttachment.AttachBackItem(vis, hash, selection.Variant, rightHand, selection.Quality);
			}
		}

		private static void DestroyInstance(VisualState state, int index)
		{
			if ((Object)(object)state.Instances[index] != (Object)null)
			{
				Object.Destroy((Object)(object)state.Instances[index]);
			}
			state.Instances[index] = null;
			state.Hashes[index] = 0;
			state.Metas[index] = 0;
		}

		internal static void Unregister(VisEquipment vis)
		{
			if (!((Object)(object)vis == (Object)null) && States.TryGetValue(vis, out var value))
			{
				for (int i = 1; i < 7; i++)
				{
					DestroyInstance(value, i);
				}
				States.Remove(vis);
			}
		}

		internal static void Cleanup()
		{
			foreach (KeyValuePair<VisEquipment, VisualState> state in States)
			{
				VisualState value = state.Value;
				for (int i = 1; i < 7; i++)
				{
					DestroyInstance(value, i);
				}
			}
			States.Clear();
		}
	}
}