Decompiled source of Odins Second Hand v0.1.1

plugins/OdinsSecondHand.dll

Decompiled a day 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 BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[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("OdinsSecondHand")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+cce88f4f5e8d204139e621ed2600df67090ba08d")]
[assembly: AssemblyProduct("OdinsSecondHand")]
[assembly: AssemblyTitle("OdinsSecondHand")]
[assembly: AssemblyVersion("0.1.1.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 OdinsSecondHand
{
	internal static class HudText
	{
		private sealed class Scale : MonoBehaviour
		{
			private const float ReferenceWidth = 1920f;

			private const float ReferenceHeight = 1080f;

			private static readonly FieldRef<float> GameGuiScale = TryFindGuiScale();

			internal CanvasScaler Scaler;

			private float _logged = -1f;

			private void Update()
			{
				if (!((Object)(object)Scaler == (Object)null))
				{
					float num = ((GameGuiScale != null) ? GameGuiScale.Invoke() : 1f);
					if (num <= 0f)
					{
						num = 1f;
					}
					float num2 = Mathf.Min((float)Screen.width / 1920f, (float)Screen.height / 1080f) * num * Plugin.TextScale.Value;
					if (Mathf.Abs(Scaler.scaleFactor - num2) > 0.0001f)
					{
						Scaler.scaleFactor = num2;
					}
					if (Plugin.DebugLog.Value && Mathf.Abs(_logged - num2) > 0.0001f)
					{
						_logged = num2;
						Plugin.Log.LogInfo((object)$"Text scale {num2:0.###}: screen {Screen.width}x{Screen.height}, game GUI scale {num:0.##}, TextScale {Plugin.TextScale.Value:0.##}.");
					}
				}
			}

			private static FieldRef<float> TryFindGuiScale()
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(GuiScaler), "m_largeGuiScale");
				if (fieldInfo == null || fieldInfo.FieldType != typeof(float) || !fieldInfo.IsStatic)
				{
					Plugin.Log.LogWarning((object)"Couldn't find the game's GUI scale; text follows the screen size only.");
					return null;
				}
				return AccessTools.StaticFieldRefAccess<float>(fieldInfo);
			}
		}

		private static Canvas _canvas;

		public static TMP_Text Create(string name, Transform owner)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text val = (((Object)(object)MessageHud.instance != (Object)null) ? MessageHud.instance.m_messageCenterText : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.font == (Object)null || !EnsureCanvas(owner))
			{
				return null;
			}
			GameObject val2 = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(((Component)_canvas).transform, false);
			TextMeshProUGUI obj = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = val.font;
			((TMP_Text)obj).fontSharedMaterial = val.fontSharedMaterial;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)1;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).outlineWidth = 0.15f;
			((TMP_Text)obj).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(0.5f, 0.5f);
			val3.anchoredPosition = Vector2.zero;
			return (TMP_Text)(object)obj;
		}

		private static bool EnsureCanvas(Transform owner)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_canvas != (Object)null)
			{
				return true;
			}
			GameObject val = new GameObject("OdinsSecondHand_Canvas");
			val.transform.SetParent(owner, false);
			_canvas = val.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 5000;
			CanvasScaler val2 = val.AddComponent<CanvasScaler>();
			val2.uiScaleMode = (ScaleMode)0;
			val.AddComponent<Scale>().Scaler = val2;
			Plugin.Log.LogInfo((object)"On-screen text layer created.");
			return true;
		}
	}
	internal class OdinLine : MonoBehaviour
	{
		private const float ScreenY = 0.72f;

		private const float FadeIn = 0.2f;

		private const float Hold = 2.5f;

		private const float FadeOut = 0.8f;

		private const float TextSize = 21f;

		private static readonly Color Tint = new Color(1f, 0.86f, 0.62f);

		private TMP_Text _text;

		private float _elapsed = -1f;

		public void Show(string line)
		{
			if (EnsureText())
			{
				_text.text = line;
				_elapsed = 0f;
				SetAlpha(0f);
				((Component)_text).gameObject.SetActive(true);
			}
		}

		private void Update()
		{
			if (_elapsed < 0f || (Object)(object)_text == (Object)null)
			{
				return;
			}
			_elapsed += Time.deltaTime;
			float num = 3.5f;
			if (_elapsed >= num)
			{
				_elapsed = -1f;
				((Component)_text).gameObject.SetActive(false);
				return;
			}
			float num2 = Mathf.Min(Mathf.Clamp01(_elapsed / 0.2f), Mathf.Clamp01((num - _elapsed) / 0.8f));
			if (!HudVisible())
			{
				num2 = 0f;
			}
			SetAlpha(Mathf.SmoothStep(0f, 1f, num2));
		}

		private void SetAlpha(float alpha)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Color tint = Tint;
			tint.a = alpha;
			((Graphic)_text).color = tint;
		}

		private bool EnsureText()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_text != (Object)null)
			{
				return true;
			}
			_text = HudText.Create("OdinsSecondHand_Line", ((Component)this).transform);
			if ((Object)(object)_text == (Object)null)
			{
				return false;
			}
			RectTransform val = (RectTransform)_text.transform;
			val.anchorMin = new Vector2(0.5f, 0.72f);
			val.anchorMax = new Vector2(0.5f, 0.72f);
			val.sizeDelta = new Vector2(1400f, 160f);
			_text.fontSize = 21f;
			return true;
		}

		private static bool HudVisible()
		{
			if ((Object)(object)Hud.instance != (Object)null && !Hud.IsUserHidden())
			{
				return Hud.instance.IsVisible();
			}
			return false;
		}

		private void OnDestroy()
		{
			if ((Object)(object)_text != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_text).gameObject);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class ControlsPatch
	{
		private static void Prefix(Player __instance, ref bool attack, ref bool attackHold)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && (Object)(object)SecondHand.Instance != (Object)null && SecondHand.Instance.HoldBackAttack)
			{
				attack = false;
				attackHold = false;
			}
		}
	}
	[BepInPlugin("hevyh2o.valheim.odinssecondhand", "Odin's Second Hand", "0.1.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "hevyh2o.valheim.odinssecondhand";

		public const string Name = "Odin's Second Hand";

		public const string Version = "0.1.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> InstantSwap;

		internal static ConfigEntry<bool> AutoSwap;

		internal static ConfigEntry<float> SwapDistance;

		internal static ConfigEntry<float> ReachMargin;

		internal static ConfigEntry<bool> SwapBack;

		internal static ConfigEntry<float> SwapBackDelay;

		internal static ConfigEntry<bool> IncludeStaffs;

		internal static ConfigEntry<string> PreferredWeapon;

		internal static ConfigEntry<string> Exclude;

		internal static ConfigEntry<KeyboardShortcut> SwapKey;

		internal static ConfigEntry<bool> ShowRemarks;

		internal static ConfigEntry<float> RemarkCooldown;

		internal static ConfigEntry<float> TextScale;

		internal static ConfigEntry<bool> DebugLog;

		private Harmony _harmony;

		private SecondHand _hand;

		private void Awake()
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Expected O, but got Unknown
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch.");
			InstantSwap = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "InstantSwap", true, "On: swaps happen at once, the way the game equips items that have no equip time. Off: swaps use the game's own equip timer and animation, so a slow weapon takes as long to draw as it would from the hotbar.");
			AutoSwap = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto", "AutoSwap", true, "While a bow or crossbow is in your hands, switch to your best melee weapon for the enemy the moment it comes close (see SwapDistance).");
			SwapDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Auto", "SwapDistance", 6f, new ConfigDescription("How close, in metres, an enemy gets before Odin swaps, 0 to 15, measured to the creature's edge. Higher draws steel sooner: 6 gives you a step or two before it reaches you. 0 = auto: the melee weapon's own reach plus ReachMargin, so a spear comes out further away than a knife. The bow comes back once nothing is this close.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 15f), Array.Empty<object>()));
			ReachMargin = ((BaseUnityPlugin)this).Config.Bind<float>("Auto", "ReachMargin", 1f, new ConfigDescription("Only used when SwapDistance is 0 (auto). Metres added to the melee weapon's own reach before Odin swaps, 0 to 5. Larger swaps sooner.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			SwapBack = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto", "SwapBack", true, "After an automatic swap, hand the bow back once no enemy is close. If you're still holding the attack button, the bow carries on drawing.");
			SwapBackDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Auto", "SwapBackDelay", 1.5f, new ConfigDescription("Seconds with no enemy close before the bow comes back, 0 to 10. 0 = straight away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			IncludeStaffs = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto", "IncludeStaffs", false, "Also swap away from magic staffs (elemental and blood magic) when an enemy gets close. Bows and crossbows always swap.");
			PreferredWeapon = ((BaseUnityPlugin)this).Config.Bind<string>("Weapons", "PreferredWeapon", "", "Always take this weapon when you carry it, whatever the enemy. Its prefab name (MaceIron) or its name as the game shows it (Iron mace); several, separated by commas, are tried in order. Empty = Odin picks the best for each enemy.");
			Exclude = ((BaseUnityPlugin)this).Config.Bind<string>("Weapons", "Exclude", "", "Weapons Odin never picks, separated by commas, by prefab name or shown name. Handy for the knife you keep for skinning.");
			SwapKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "SwapKey", KeyboardShortcut.Empty, "Optional key: with a bow out, swap to the best melee weapon for the nearest enemy now; press again to get the bow back. A swap you make with this key isn't undone by itself. Unbound by default.");
			ShowRemarks = ((BaseUnityPlugin)this).Config.Bind<bool>("Messages", "ShowRemarks", true, "Odin says a word when he swaps for you, often about why he picked that weapon.");
			RemarkCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "RemarkCooldown", 20f, new ConfigDescription("Seconds between remarks, 0 to 300. Swaps in between happen quietly.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			TextScale = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "TextScale", 1f, new ConfigDescription("Size of Odin's remarks, 0.5 to 2. 1 = sized to match the game's own HUD, which already follows your screen height and the game's GUI scale setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Write each swap to the BepInEx log: the enemy, every weapon Odin weighed and its damage against it, the reach and the equip times.");
			_harmony = new Harmony("hevyh2o.valheim.odinssecondhand");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			_hand = ((Component)this).gameObject.AddComponent<SecondHand>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Second Hand 0.1.1 loaded. Odin is minding your back.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)_hand != (Object)null)
			{
				Object.Destroy((Object)(object)_hand);
			}
		}
	}
	internal sealed class SecondHand : MonoBehaviour
	{
		private const float ScanInterval = 0.1f;

		private const float LookRadius = 16f;

		private const float StayMargin = 0.5f;

		private const float AutoCooldown = 1f;

		private const float DrawResumeWindow = 3f;

		private const float HoldBackLimit = 3f;

		private const float TieMargin = 0.05f;

		private static readonly MethodInfo ToggleEquipped = AccessTools.Method(typeof(Player), "ToggleEquipped", (Type[])null, (Type[])null);

		private static readonly FieldInfo AttackDrawTime = AccessTools.Field(typeof(Humanoid), "m_attackDrawTime");

		private static readonly DamageType[] Types;

		private static readonly string[] Openers;

		private static readonly Dictionary<DamageType, string[]> Because;

		private static readonly string[] Plain;

		private OdinLine _line;

		private ItemData _origin;

		private ItemData _melee;

		private bool _auto;

		private float _lastEnemyNear;

		private float _holdBackSince = -1f;

		private float _nextScan;

		private float _nextAutoAt;

		private float _resumeUntil = -1f;

		private float _nextRemarkAt;

		private string _lastRemark;

		internal static SecondHand Instance { get; private set; }

		internal bool HoldBackAttack => _holdBackSince >= 0f;

		private void Awake()
		{
			Instance = this;
			_line = ((Component)this).gameObject.AddComponent<OdinLine>();
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !Plugin.Enabled.Value)
			{
				Clear();
				_resumeUntil = -1f;
				return;
			}
			ResumeDraw(localPlayer);
			if (_melee != null && !Holding(localPlayer, _melee))
			{
				Clear();
			}
			if (HoldBackAttack && Time.time - _holdBackSince > 3f)
			{
				_holdBackSince = -1f;
			}
			if (Pressed(Plugin.SwapKey.Value) && CanSwap(localPlayer))
			{
				SwapNow(localPlayer);
			}
			else
			{
				if (Time.time < _nextScan)
				{
					return;
				}
				_nextScan = Time.time + 0.1f;
				if (CanSwap(localPlayer))
				{
					if (_melee == null)
					{
						TryAutoSwap(localPlayer);
					}
					else if (_auto)
					{
						TrySwapBack(localPlayer);
					}
				}
			}
		}

		private void TryAutoSwap(Player player)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.AutoSwap.Value || Time.time < _nextAutoAt)
			{
				return;
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			if (currentWeapon == null || !IsRanged(currentWeapon))
			{
				return;
			}
			float distance;
			Character val = NearestThreat(player, 16f, out distance);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			DamageType reason;
			ItemData val2 = BestMelee(player, val, out reason);
			if (val2 != null)
			{
				float num = Reach(val2);
				if (!(distance > num) && Equip(player, val2))
				{
					Begin(currentWeapon, val2, auto: true);
					_lastEnemyNear = Time.time;
					Remark(val2, val, reason);
					Note($"Auto-swap: {Describe(currentWeapon)} -> {Describe(val2)}, reach {num:0.0} m, {val.m_name} at {distance:0.0} m.");
				}
			}
		}

		private void TrySwapBack(Player player)
		{
			if (Plugin.SwapBack.Value)
			{
				if (ThreatInReach(player))
				{
					_lastEnemyNear = Time.time;
					_holdBackSince = -1f;
				}
				else if (!(Time.time - _lastEnemyNear < Plugin.SwapBackDelay.Value))
				{
					BackToRanged(player);
				}
			}
		}

		private void BackToRanged(Player player)
		{
			if (_origin == null || !((Humanoid)player).GetInventory().ContainsItem(_origin))
			{
				Clear();
				return;
			}
			ItemData origin = _origin;
			ItemData melee = _melee;
			if (Equip(player, origin))
			{
				Note("Swap back: " + Describe(melee) + " -> " + Describe(origin) + ".");
				if (origin.m_shared.m_attack.m_bowDraw)
				{
					_resumeUntil = Time.time + 3f;
				}
				Clear();
				_nextAutoAt = Time.time + 1f;
			}
		}

		private void SwapNow(Player player)
		{
			if (_melee != null)
			{
				BackToRanged(player);
				return;
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			if (currentWeapon != null && IsRanged(currentWeapon))
			{
				float distance;
				Character val = NearestThreat(player, 16f, out distance);
				DamageType reason;
				ItemData val2 = BestMelee(player, val, out reason);
				if (val2 != null && Equip(player, val2))
				{
					Begin(currentWeapon, val2, auto: false);
					Note("Swap key: " + Describe(currentWeapon) + " -> " + Describe(val2) + (((Object)(object)val != (Object)null) ? (", for " + val.m_name + ".") : "."));
				}
			}
		}

		private bool ThreatInReach(Player player)
		{
			if ((Object)(object)NearestThreat(player, 16f, out var distance) != (Object)null)
			{
				return distance <= Reach(_melee) + 0.5f;
			}
			return false;
		}

		private void WantHoldBack(Player player)
		{
			if (Plugin.SwapBack.Value && AttackHeld())
			{
				if (ThreatInReach(player))
				{
					_lastEnemyNear = Time.time;
					_holdBackSince = -1f;
				}
				else if (_holdBackSince < 0f && Time.time - _lastEnemyNear >= Plugin.SwapBackDelay.Value)
				{
					_holdBackSince = Time.time;
				}
			}
		}

		private void ResumeDraw(Player player)
		{
			if (_resumeUntil < 0f)
			{
				return;
			}
			if (Time.time > _resumeUntil || !AttackHeld() || AttackDrawTime == null)
			{
				_resumeUntil = -1f;
				return;
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			if (currentWeapon != null && currentWeapon.m_shared.m_attack.m_bowDraw && !((Character)player).InMinorAction())
			{
				float num = (float)AttackDrawTime.GetValue(player);
				if (num < 0f)
				{
					AttackDrawTime.SetValue(player, 0f);
				}
				else if (num > 0f)
				{
					_resumeUntil = -1f;
				}
			}
		}

		private static ItemData BestMelee(Player player, Character enemy, out DamageType reason)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Expected I4, but got Unknown
			reason = (DamageType)1024;
			List<ItemData> allItems = ((Humanoid)player).GetInventory().GetAllItems();
			foreach (string item2 in Names(Plugin.PreferredWeapon.Value))
			{
				foreach (ItemData item3 in allItems)
				{
					if (IsCandidate(item3) && Matches(item3, item2))
					{
						return item3;
					}
				}
			}
			DamageModifiers mods = (DamageModifiers)(((Object)(object)enemy != (Object)null) ? enemy.GetDamageModifiers((WeakSpot)null) : default(DamageModifiers));
			List<string> list = Names(Plugin.Exclude.Value);
			ItemData val = null;
			float num = 0f;
			float num2 = 0f;
			List<string> list2 = (Plugin.DebugLog.Value ? new List<string>() : null);
			foreach (ItemData item in allItems)
			{
				if (IsCandidate(item) && !list.Exists((string n) => Matches(item, n)))
				{
					DamageType why;
					float num3 = Effective(item.GetDamage(), mods, out why);
					float skillLevel = ((Character)player).GetSkillLevel(item.m_shared.m_skillType);
					list2?.Add($"{Name(item)} q{item.m_quality} {num3:0.#}");
					if (!(num3 <= 0f) && (val == null || num3 > num * 1.05f || (num3 >= num * 0.95f && skillLevel > num2)))
					{
						val = item;
						num = num3;
						num2 = skillLevel;
						reason = (DamageType)(int)why;
					}
				}
			}
			if (list2 != null && list2.Count > 0)
			{
				Plugin.Log.LogInfo((object)("Weighed against " + (((Object)(object)enemy != (Object)null) ? enemy.m_name : "nothing in particular") + ": " + string.Join(", ", list2) + "."));
			}
			return val;
		}

		private static float Effective(DamageTypes damage, DamageModifiers mods, out DamageType why)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected I4, but got Unknown
			why = (DamageType)1024;
			float num = damage.m_damage;
			float num2 = 1f;
			DamageType[] types = Types;
			foreach (DamageType val in types)
			{
				float num3 = Amount(damage, val);
				if (!(num3 <= 0f))
				{
					float num4 = Factor(((DamageModifiers)(ref mods)).GetModifier(val));
					num += num3 * num4;
					if (num4 > num2)
					{
						num2 = num4;
						why = (DamageType)(int)val;
					}
				}
			}
			return num;
		}

		private static float Amount(DamageTypes d, DamageType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0005: 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_001d: Expected I4, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0074: 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)
			if ((int)type <= 64)
			{
				switch (type - 1)
				{
				default:
					if ((int)type != 32)
					{
						if ((int)type != 64)
						{
							break;
						}
						return d.m_frost;
					}
					return d.m_fire;
				case 0:
					return d.m_blunt;
				case 1:
					return d.m_slash;
				case 3:
					return d.m_pierce;
				case 2:
					break;
				}
			}
			else
			{
				if ((int)type == 128)
				{
					return d.m_lightning;
				}
				if ((int)type == 256)
				{
					return d.m_poison;
				}
				if ((int)type == 512)
				{
					return d.m_spirit;
				}
			}
			return 0f;
		}

		private static float Factor(DamageModifier mod)
		{
			//IL_0000: 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_0028: Expected I4, but got Unknown
			switch (mod - 1)
			{
			case 6:
				return 0.75f;
			case 0:
				return 0.5f;
			case 4:
				return 0.25f;
			case 7:
				return 1.25f;
			case 1:
				return 1.5f;
			case 5:
				return 2f;
			case 2:
			case 3:
				return 0f;
			default:
				return 1f;
			}
		}

		private static bool IsCandidate(ItemData item)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_0054: Invalid comparison between Unknown and I4
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Invalid comparison between Unknown and I4
			if (item == null || !item.IsWeapon() || IsRanged(item) || (int)item.m_shared.m_attack.m_attackType == 2)
			{
				return false;
			}
			if (item.m_shared.m_useDurability && item.m_durability <= 0f)
			{
				return false;
			}
			SkillType skillType = item.m_shared.m_skillType;
			if (skillType - 1 <= 4 || (int)skillType == 7)
			{
				return true;
			}
			return false;
		}

		private static bool IsRanged(ItemData item)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_000e: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			SkillType skillType = item.m_shared.m_skillType;
			if ((int)skillType == 8 || (int)skillType == 14 || (int)item.m_shared.m_itemType == 4)
			{
				return true;
			}
			if (Plugin.IncludeStaffs.Value)
			{
				if ((int)skillType != 9)
				{
					return (int)skillType == 10;
				}
				return true;
			}
			return false;
		}

		private static float Reach(ItemData melee)
		{
			if (!(Plugin.SwapDistance.Value > 0f))
			{
				return melee.m_shared.m_attack.m_attackRange + Plugin.ReachMargin.Value;
			}
			return Plugin.SwapDistance.Value;
		}

		private static List<string> Names(string list)
		{
			List<string> list2 = new List<string>();
			string[] array = (list ?? "").Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (text.Trim().Length > 0)
				{
					list2.Add(text.Trim());
				}
			}
			return list2;
		}

		private static bool Matches(ItemData item, string wanted)
		{
			if ((Object)(object)item.m_dropPrefab != (Object)null && string.Equals(((Object)item.m_dropPrefab).name, wanted, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			string text = item.m_shared.m_name ?? "";
			if (string.Equals(text.TrimStart(new char[1] { '$' }), wanted.TrimStart(new char[1] { '$' }), StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (Localization.instance != null)
			{
				return string.Equals(Localization.instance.Localize(text), wanted, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static Character NearestThreat(Player player, float radius, out float distance)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			distance = float.MaxValue;
			Character result = null;
			Vector3 position = ((Component)player).transform.position;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)player) && !allCharacter.IsDead() && !allCharacter.IsTamed())
				{
					float num = Mathf.Max(0f, Vector3.Distance(((Component)allCharacter).transform.position, position) - allCharacter.GetRadius());
					if (!(num > radius) && !(num >= distance) && IsThreat(player, allCharacter))
					{
						result = allCharacter;
						distance = num;
					}
				}
			}
			return result;
		}

		private static bool IsThreat(Player player, Character other)
		{
			//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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			if (!BaseAI.IsEnemy((Character)(object)player, other))
			{
				return false;
			}
			Faction faction = other.GetFaction();
			if ((int)faction != 1)
			{
				if ((int)faction == 12)
				{
					return false;
				}
				return true;
			}
			BaseAI baseAI = other.GetBaseAI();
			MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
			if (val != null)
			{
				return (Object)(object)((BaseAI)val).GetTargetCreature() == (Object)(object)player;
			}
			return false;
		}

		private static bool Equip(Player player, ItemData item)
		{
			if (Holding(player, item))
			{
				return true;
			}
			if (Plugin.InstantSwap.Value || ToggleEquipped == null)
			{
				return ((Humanoid)player).EquipItem(item, true);
			}
			ToggleEquipped.Invoke(player, new object[1] { item });
			return Holding(player, item);
		}

		private static bool Holding(Player player, ItemData item)
		{
			if (!((Humanoid)player).IsItemEquiped(item))
			{
				return player.IsEquipActionQueued(item);
			}
			return true;
		}

		private void Begin(ItemData origin, ItemData melee, bool auto)
		{
			_origin = origin;
			_melee = melee;
			_auto = auto;
			_holdBackSince = -1f;
		}

		private void Clear()
		{
			_origin = null;
			_melee = null;
			_auto = false;
			_holdBackSince = -1f;
		}

		private bool CanSwap(Player player)
		{
			if (((Character)player).IsDead() || ((Character)player).InCutscene() || ((Character)player).IsTeleporting() || player.IsSleeping() || ((Character)player).InDodge() || ((Character)player).IsSwimming() || ((Character)player).InPlaceMode())
			{
				return false;
			}
			if (Menu.IsVisible() || InventoryGui.IsVisible() || Minimap.IsOpen() || StoreGui.IsVisible() || Typing())
			{
				return false;
			}
			if (((Character)player).InAttack())
			{
				if (_auto && _melee != null)
				{
					WantHoldBack(player);
				}
				return false;
			}
			return true;
		}

		private static bool Typing()
		{
			if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible())
			{
				return TextInput.IsVisible();
			}
			return true;
		}

		private static bool AttackHeld()
		{
			if (!ZInput.GetButton("Attack"))
			{
				return ZInput.GetButton("JoyAttack");
			}
			return true;
		}

		private static bool Pressed(KeyboardShortcut key)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref key)).MainKey == 0 || Typing() || !ZInput.GetKeyDown(((KeyboardShortcut)(ref key)).MainKey, false))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref key)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}

		private void Remark(ItemData melee, Character enemy, DamageType reason)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ShowRemarks.Value || Time.time < _nextRemarkAt)
			{
				return;
			}
			string[] value;
			string[] lines = (Because.TryGetValue(reason, out value) ? value : Plain);
			string text = null;
			for (int i = 0; i < 6; i++)
			{
				if (text != null && !(text == _lastRemark))
				{
					break;
				}
				text = Pick(Openers) + " " + string.Format(Pick(lines), ShownName(melee), ShownName(enemy));
			}
			_lastRemark = text;
			_nextRemarkAt = Time.time + Plugin.RemarkCooldown.Value;
			_line.Show(text);
		}

		private static string Pick(string[] lines)
		{
			return lines[Random.Range(0, lines.Length)];
		}

		private static string ShownName(ItemData item)
		{
			return Localize(item.m_shared.m_name).ToLowerInvariant();
		}

		private static string ShownName(Character c)
		{
			return Localize(c.m_name);
		}

		private static string Localize(string token)
		{
			if (Localization.instance == null)
			{
				return token.TrimStart(new char[1] { '$' });
			}
			return Localization.instance.Localize(token);
		}

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

		private static string Describe(ItemData item)
		{
			return $"{Name(item)} (equip time {item.m_shared.m_equipDuration:0.##} s)";
		}

		private static void Note(string message)
		{
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)(message + (Plugin.InstantSwap.Value ? " Instant." : " Game timer.")));
			}
		}

		static SecondHand()
		{
			DamageType[] array = new DamageType[8];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			Types = (DamageType[])(object)array;
			Openers = new string[4] { "Too close for arrows.", "Bow down.", "No time to nock.", "Close enough to smell." };
			Because = new Dictionary<DamageType, string[]>
			{
				[(DamageType)1] = new string[3] { "Odin chose the {0}. {1}s hate something heavy.", "The {0}. Some things only listen to blunt arguments.", "Odin chose the {0}. Bones break; that's what they're for." },
				[(DamageType)2] = new string[3] { "Odin chose the {0}. {1}s don't care for edges.", "The {0}. Long cuts for soft things.", "Odin chose the {0}. The sharp end goes in front." },
				[(DamageType)4] = new string[3] { "Odin chose the {0}. Find the gap and lean on it.", "The {0}. {1}s have holes in their plans; make more.", "Odin chose the {0}. Poke, then poke again." },
				[(DamageType)32] = new string[3] { "Odin chose the {0}. {1}s and fire don't get on.", "The {0}. Warm it up.", "Odin chose the {0}. He likes a bit of smoke." },
				[(DamageType)64] = new string[3] { "Odin chose the {0}. Cool it down.", "The {0}. {1}s hate the cold almost as much as you do.", "Odin chose the {0}. Frost it." },
				[(DamageType)128] = new string[3] { "Odin chose the {0}. A little thunder, up close.", "The {0}. {1}s don't like a spark.", "Odin chose the {0}. Don't tell Thor." },
				[(DamageType)256] = new string[3] { "Odin chose the {0}. Slow, but it gets there.", "The {0}. {1}s can't keep poison down.", "Odin chose the {0}. Let the venom do the walking." },
				[(DamageType)512] = new string[3] { "Odin chose the {0}. The dead hate spirit.", "The {0}. {1}s should have stayed in the ground.", "Odin chose the {0}. Send it back where it came from." }
			};
			Plain = new string[3] { "Odin chose the {0}. It hits hardest.", "The {0}. Nothing clever, just the biggest stick you've got.", "Odin chose the {0}. You're welcome." };
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.1.1";
	}
}