Decompiled source of SmartShield v1.0.11

plugins\cjayride-SmartShield\Cjayride.SmartShield.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Cjayride.SmartShield")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.11.0")]
[assembly: AssemblyInformationalVersion("1.0.11+ca95b60e6d1e8c9e43abf5b918a43edd8c88eb57")]
[assembly: AssemblyProduct("Cjayride.SmartShield")]
[assembly: AssemblyTitle("Cjayride.SmartShield")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.11.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Cjayride.SmartShield
{
	internal static class ModConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> AutoEquipOnOneHanded;

		internal static ConfigEntry<bool> EquipAfterRanged;

		internal static ConfigEntry<bool> UnequipOnTwoHanded;

		internal static ConfigEntry<bool> UnequipOnSheath;

		internal static ConfigEntry<bool> UnequipOnHotbarDeselect;

		internal static ConfigEntry<string> SelectionMode;

		internal static ConfigEntry<string> PreferredShield;

		internal static ConfigEntry<string> AllowedWeaponTypes;

		internal static ConfigEntry<string> BlacklistShields;

		internal static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("General", "Enabled", true, "Master switch. No messages are ever shown.");
			AutoEquipOnOneHanded = config.Bind<bool>("Behavior", "AutoEquipOnOneHanded", true, "Equip a shield whenever a one-handed weapon is equipped.");
			EquipAfterRanged = config.Bind<bool>("Behavior", "EquipAfterRanged", true, "Also equip a shield after switching from a bow/crossbow (or other ranged) to a one-handed weapon.");
			UnequipOnTwoHanded = config.Bind<bool>("Behavior", "UnequipOnTwoHanded", true, "Unequip the shield when a two-handed or ranged weapon is equipped.");
			UnequipOnSheath = config.Bind<bool>("Behavior", "UnequipOnSheath", false, "When true, stow the shield in inventory on R-sheath (weapon-only on your back). When false, vanilla dual back display (weapon + shield).");
			UnequipOnHotbarDeselect = config.Bind<bool>("Behavior", "UnequipOnHotbarDeselect", true, "Unequip the shield when you deselect a one-handed weapon on the hotbar (press the same slot again). Does not unequip if you manually equip a shield alone.");
			SelectionMode = config.Bind<string>("ShieldSelection", "SelectionMode", "Best", "Best = highest block power. First = first shield in inventory. Preferred = use PreferredShield.");
			PreferredShield = config.Bind<string>("ShieldSelection", "PreferredShield", "", "Prefab name used when SelectionMode is Preferred (example: ShieldWood).");
			AllowedWeaponTypes = config.Bind<string>("Filters", "AllowedWeaponTypes", "", "Comma-separated ItemType, skill, or prefab names. Empty = all one-handed weapons.");
			BlacklistShields = config.Bind<string>("Filters", "BlacklistShields", "", "Comma-separated shield prefab names that must never be auto-equipped.");
		}
	}
	[BepInPlugin("cjayride.smartshield", "SmartShield", "1.0.11")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "cjayride.smartshield";

		public const string PluginName = "SmartShield";

		public const string PluginVersion = "1.0.11";

		internal static Plugin Instance;

		internal static Harmony Harmony;

		private float _seenPlayerAt = -1f;

		private float _nextPoll;

		private bool _baselined;

		private int _catchUpPolls;

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			Instance = this;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			Harmony = new Harmony("cjayride.smartshield");
			Harmony.PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"SmartShield 1.0.11 loaded.");
		}

		private void Update()
		{
			if (!ModConfig.Enabled.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				_seenPlayerAt = -1f;
				_baselined = false;
				_catchUpPolls = 0;
			}
			else if (_seenPlayerAt < 0f)
			{
				_seenPlayerAt = Time.unscaledTime;
			}
			else
			{
				if (Time.unscaledTime < _seenPlayerAt + 1.5f || Time.unscaledTime < _nextPoll)
				{
					return;
				}
				_nextPoll = Time.unscaledTime + 0.12f;
				if (!_baselined)
				{
					ShieldService.Baseline(localPlayer);
					_baselined = true;
					ShieldService.Apply();
					_catchUpPolls = 10;
					return;
				}
				ShieldService.Tick(localPlayer);
				if (_catchUpPolls > 0)
				{
					_catchUpPolls--;
					ShieldService.Apply();
				}
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "HideHandItems")]
	internal static class PatchHideHandItems
	{
		private static void Postfix(Humanoid __instance, bool onlyRightHand)
		{
			if (!onlyRightHand && IsLocal(__instance) && SheathUnequipEnabled())
			{
				ShieldService.RetractShieldWhileSheathed(__instance);
			}
		}

		private static bool IsLocal(Humanoid humanoid)
		{
			if ((Object)(object)humanoid != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				return (Object)(object)humanoid == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		private static bool SheathUnequipEnabled()
		{
			if (ModConfig.Enabled != null && ModConfig.Enabled.Value && ModConfig.UnequipOnSheath != null)
			{
				return ModConfig.UnequipOnSheath.Value;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "ShowHandItems")]
	internal static class PatchShowHandItems
	{
		private static void Prefix(Humanoid __instance, bool onlyRightHand)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			if (!onlyRightHand && IsLocal(__instance) && SheathUnequipEnabled())
			{
				ItemData hiddenLeftItem = __instance.m_hiddenLeftItem;
				if (hiddenLeftItem?.m_shared != null && (int)hiddenLeftItem.m_shared.m_itemType == 5)
				{
					__instance.m_hiddenLeftItem = null;
				}
			}
		}

		private static void Postfix(Humanoid __instance, bool onlyRightHand)
		{
			if (!onlyRightHand && IsLocal(__instance) && ModConfig.Enabled != null && ModConfig.Enabled.Value)
			{
				ShieldService.NotifyHandsShown();
				ShieldService.Apply();
			}
		}

		private static bool IsLocal(Humanoid humanoid)
		{
			if ((Object)(object)humanoid != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				return (Object)(object)humanoid == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		private static bool SheathUnequipEnabled()
		{
			if (ModConfig.Enabled != null && ModConfig.Enabled.Value && ModConfig.UnequipOnSheath != null)
			{
				return ModConfig.UnequipOnSheath.Value;
			}
			return false;
		}
	}
	internal static class ShieldService
	{
		internal static bool InternalChange;

		private static bool _previousWasRangedOrTwoHanded;

		private static ItemData _lastRight;

		private static bool _wasSheathed;

		private static float _ignoreHotbarUnequipUntil;

		internal static void Baseline(Player player)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = (_lastRight = ((Humanoid)player).GetRightItem());
			_wasSheathed = IsSheathed((Humanoid)(object)player);
			_previousWasRangedOrTwoHanded = val?.m_shared != null && IsRangedOrTwoHanded(val.m_shared.m_itemType);
		}

		internal static void NotifyHandsShown()
		{
			_ignoreHotbarUnequipUntil = Time.unscaledTime + 0.35f;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				_lastRight = ((Humanoid)localPlayer).GetRightItem();
				_wasSheathed = IsSheathed((Humanoid)(object)localPlayer);
			}
		}

		internal static void Tick(Player player)
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Invalid comparison between Unknown and I4
			ItemData lastRight = _lastRight;
			ItemData rightItem = ((Humanoid)player).GetRightItem();
			ItemData leftItem = ((Humanoid)player).GetLeftItem();
			bool flag = IsSheathed((Humanoid)(object)player);
			if (ModConfig.UnequipOnSheath.Value && flag && ShieldStillAttachedWhileSheathed((Humanoid)(object)player))
			{
				_lastRight = rightItem;
				_wasSheathed = true;
				Apply();
				return;
			}
			bool num = rightItem != lastRight;
			bool flag2 = flag != _wasSheathed;
			if (!num && !flag2)
			{
				return;
			}
			if (ModConfig.UnequipOnHotbarDeselect.Value && Time.unscaledTime >= _ignoreHotbarUnequipUntil && rightItem == null && !flag && lastRight != null && IsOneHandedWeapon(lastRight) && leftItem?.m_shared != null && (int)leftItem.m_shared.m_itemType == 5)
			{
				_lastRight = rightItem;
				_wasSheathed = flag;
				UnequipSilent((Humanoid)(object)player, leftItem);
				return;
			}
			if (rightItem?.m_shared != null && IsRangedOrTwoHanded(rightItem.m_shared.m_itemType))
			{
				_previousWasRangedOrTwoHanded = true;
			}
			_lastRight = rightItem;
			_wasSheathed = flag;
			Apply();
		}

		internal static void Apply()
		{
			if (!ModConfig.Enabled.Value || InternalChange)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				return;
			}
			try
			{
				ApplyInner(localPlayer);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("SmartShield apply failed: " + ex.Message));
			}
		}

		private static void ApplyInner(Player player)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Invalid comparison between Unknown and I4
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Invalid comparison between Unknown and I4
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Invalid comparison between Unknown and I4
			ItemData rightItem = ((Humanoid)player).GetRightItem();
			ItemData leftItem = ((Humanoid)player).GetLeftItem();
			bool previousWasRangedOrTwoHanded = _previousWasRangedOrTwoHanded;
			_previousWasRangedOrTwoHanded = rightItem?.m_shared != null && IsRangedOrTwoHanded(rightItem.m_shared.m_itemType);
			if (IsSheathed((Humanoid)(object)player))
			{
				if (ModConfig.UnequipOnSheath.Value)
				{
					RetractShieldWhileSheathed((Humanoid)(object)player);
				}
			}
			else
			{
				if (rightItem == null)
				{
					return;
				}
				if (IsRangedOrTwoHanded(rightItem.m_shared.m_itemType))
				{
					if (ModConfig.UnequipOnTwoHanded.Value && leftItem != null && (int)leftItem.m_shared.m_itemType == 5)
					{
						UnequipSilent((Humanoid)(object)player, leftItem);
					}
				}
				else
				{
					if (rightItem == null || !IsOneHandedWeapon(rightItem) || !WeaponAllowed(rightItem))
					{
						return;
					}
					bool flag = ModConfig.AutoEquipOnOneHanded.Value;
					if (!flag && ModConfig.EquipAfterRanged.Value && previousWasRangedOrTwoHanded)
					{
						flag = true;
					}
					if (flag && (leftItem == null || (int)leftItem.m_shared.m_itemType == 5) && (leftItem == null || (int)leftItem.m_shared.m_itemType != 5))
					{
						ItemData val = SelectShield(player);
						if (val != null)
						{
							EquipSilent((Humanoid)(object)player, val);
						}
					}
				}
			}
		}

		internal static bool IsSheathed(Humanoid humanoid)
		{
			if ((Object)(object)humanoid != (Object)null && humanoid.GetRightItem() == null)
			{
				return humanoid.m_hiddenRightItem != null;
			}
			return false;
		}

		internal static bool ShieldStillAttachedWhileSheathed(Humanoid humanoid)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Invalid comparison between Unknown and I4
			if (!IsSheathed(humanoid))
			{
				return false;
			}
			ItemData leftItem = humanoid.GetLeftItem();
			if (leftItem?.m_shared != null && (int)leftItem.m_shared.m_itemType == 5)
			{
				return true;
			}
			ItemData hiddenLeftItem = humanoid.m_hiddenLeftItem;
			if (hiddenLeftItem?.m_shared != null)
			{
				return (int)hiddenLeftItem.m_shared.m_itemType == 5;
			}
			return false;
		}

		internal static void RetractShieldWhileSheathed(Humanoid humanoid)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			if ((Object)(object)humanoid == (Object)null)
			{
				return;
			}
			ItemData leftItem = humanoid.GetLeftItem();
			if (leftItem?.m_shared != null && (int)leftItem.m_shared.m_itemType == 5)
			{
				UnequipSilent(humanoid, leftItem);
			}
			ItemData hiddenLeftItem = humanoid.m_hiddenLeftItem;
			if (hiddenLeftItem?.m_shared != null && (int)hiddenLeftItem.m_shared.m_itemType == 5)
			{
				humanoid.m_hiddenLeftItem = null;
			}
			VisEquipment visEquipment = humanoid.m_visEquipment;
			if (!((Object)(object)visEquipment == (Object)null))
			{
				visEquipment.SetLeftBackItem(0, 0, 0);
				if ((Object)(object)visEquipment.m_leftBackItemInstance != (Object)null)
				{
					Object.Destroy((Object)(object)visEquipment.m_leftBackItemInstance);
					visEquipment.m_leftBackItemInstance = null;
					visEquipment.m_currentLeftBackItemHash = 0;
					visEquipment.m_currentLeftBackItemVariant = 0;
					visEquipment.m_currentLeftBackItemQuality = 0;
				}
				humanoid.SetupVisEquipment(visEquipment, false);
				visEquipment.UpdateEquipmentVisuals();
			}
		}

		internal static void UnequipSilentPublic(Humanoid player, ItemData item)
		{
			UnequipSilent(player, item);
		}

		private static bool IsOneHandedWeapon(ItemData item)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			return (int)item.m_shared.m_itemType == 3;
		}

		private static bool IsRangedOrTwoHanded(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			if ((int)type != 4 && (int)type != 14 && (int)type != 22)
			{
				return (int)type == 20;
			}
			return true;
		}

		private static bool WeaponAllowed(ItemData weapon)
		{
			string value = ModConfig.AllowedWeaponTypes.Value;
			if (string.IsNullOrWhiteSpace(value))
			{
				return true;
			}
			string value2 = PrefabName(weapon);
			string value3 = ((object)Unsafe.As<ItemType, ItemType>(ref weapon.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString();
			string value4 = ((object)Unsafe.As<SkillType, SkillType>(ref weapon.m_shared.m_skillType)/*cast due to .constrained prefix*/).ToString();
			foreach (string item in SplitList(value))
			{
				if (item.Equals(value3, StringComparison.OrdinalIgnoreCase) || item.Equals(value4, StringComparison.OrdinalIgnoreCase) || item.Equals(value2, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private static ItemData SelectShield(Player player)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Invalid comparison between Unknown and I4
			List<ItemData> allItems = ((Humanoid)player).GetInventory().GetAllItems();
			HashSet<string> hashSet = new HashSet<string>(SplitList(ModConfig.BlacklistShields.Value), StringComparer.OrdinalIgnoreCase);
			string text = (ModConfig.SelectionMode.Value ?? "Best").Trim();
			string text2 = (ModConfig.PreferredShield.Value ?? "").Trim();
			ItemData val = null;
			float num = float.MinValue;
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val2 = allItems[i];
				if (val2?.m_shared == null || (int)val2.m_shared.m_itemType != 5)
				{
					continue;
				}
				string text3 = PrefabName(val2);
				if (hashSet.Contains(text3))
				{
					continue;
				}
				if (text.Equals("Preferred", StringComparison.OrdinalIgnoreCase) && text2.Length > 0)
				{
					if (text3.Equals(text2, StringComparison.OrdinalIgnoreCase))
					{
						return val2;
					}
					continue;
				}
				if (text.Equals("First", StringComparison.OrdinalIgnoreCase))
				{
					return val2;
				}
				float num2 = val2.m_shared.m_blockPower + val2.m_shared.m_timedBlockBonus;
				if (val == null || num2 > num)
				{
					val = val2;
					num = num2;
				}
			}
			if (val == null && text.Equals("Preferred", StringComparison.OrdinalIgnoreCase))
			{
				return SelectFallbackBest(allItems, hashSet);
			}
			return val;
		}

		private static ItemData SelectFallbackBest(List<ItemData> items, HashSet<string> banned)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			ItemData val = null;
			float num = float.MinValue;
			for (int i = 0; i < items.Count; i++)
			{
				ItemData val2 = items[i];
				if (val2?.m_shared != null && (int)val2.m_shared.m_itemType == 5 && !banned.Contains(PrefabName(val2)))
				{
					float num2 = val2.m_shared.m_blockPower + val2.m_shared.m_timedBlockBonus;
					if (val == null || num2 > num)
					{
						val = val2;
						num = num2;
					}
				}
			}
			return val;
		}

		private static void EquipSilent(Humanoid player, ItemData item)
		{
			InternalChange = true;
			try
			{
				player.EquipItem(item, false);
			}
			finally
			{
				InternalChange = false;
			}
		}

		private static void UnequipSilent(Humanoid player, ItemData item)
		{
			InternalChange = true;
			try
			{
				player.UnequipItem(item, false);
			}
			finally
			{
				InternalChange = false;
			}
		}

		private static string PrefabName(ItemData item)
		{
			if ((Object)(object)item.m_dropPrefab != (Object)null)
			{
				return ((Object)item.m_dropPrefab).name;
			}
			return item.m_shared.m_name ?? "";
		}

		private static List<string> SplitList(string raw)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrWhiteSpace(raw))
			{
				return list;
			}
			string[] array = raw.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					list.Add(text);
				}
			}
			return list;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}