Decompiled source of CaptainValheim v1.1.0

CaptainValheim.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CaptainValheim")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("CaptainValheim")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[CompilerGenerated]
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	int ICollection.Count => _items.Length;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => _items.Length;

	T IReadOnlyList<T>.this[int index] => _items[index];

	int ICollection<T>.Count => _items.Length;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlyArray(T[] items)
	{
		_items = items;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable)_items).GetEnumerator();
	}

	void ICollection.CopyTo(Array array, int index)
	{
		((ICollection)_items).CopyTo(array, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return ((IList)_items).Contains(value);
	}

	int IList.IndexOf(object value)
	{
		return ((IList)_items).IndexOf(value);
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return ((IEnumerable<T>)_items).GetEnumerator();
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return ((ICollection<T>)_items).Contains(item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		((ICollection<T>)_items).CopyTo(array, arrayIndex);
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		return ((IList<T>)_items).IndexOf(item);
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
[CompilerGenerated]
internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T>
	{
		object IEnumerator.Current => _item;

		T IEnumerator<T>.Current => _item;

		public Enumerator(T item)
		{
			_item = item;
		}

		bool IEnumerator.MoveNext()
		{
			if (!_moveNextCalled)
			{
				return _moveNextCalled = true;
			}
			return false;
		}

		void IEnumerator.Reset()
		{
			_moveNextCalled = false;
		}

		void IDisposable.Dispose()
		{
		}
	}

	int ICollection.Count => 1;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => 1;

	T IReadOnlyList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
	}

	int ICollection<T>.Count => 1;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlySingleElementList(T item)
	{
		_item = item;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection.CopyTo(Array array, int index)
	{
		array.SetValue(_item, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return EqualityComparer<T>.Default.Equals(_item, (T)value);
	}

	int IList.IndexOf(object value)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, (T)value))
		{
			return -1;
		}
		return 0;
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return EqualityComparer<T>.Default.Equals(_item, item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		array[arrayIndex] = _item;
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, item))
		{
			return -1;
		}
		return 0;
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 CaptainValheim
{
	internal static class CaptainValheimLocalization
	{
		private const string EnglishLanguage = "English";

		private const string ResourcePrefix = "CaptainValheim.translations.";

		private const string ExternalFilePrefix = "CaptainValheim.";

		private const string TranslationFileExtension = ".yml";

		private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreFields().Build();

		private static readonly Dictionary<string, Dictionary<string, string>?> TranslationCache = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		private static bool _reportedMissingEnglish;

		internal static void Load()
		{
			if (ReadEmbeddedTranslations("English") == null)
			{
				ReportMissingEnglish();
			}
			if (Localization.instance != null)
			{
				ApplyLanguage(Localization.instance);
			}
		}

		internal static string Text(string key)
		{
			return Localize("$" + key);
		}

		internal static string Localize(string token)
		{
			if (string.IsNullOrWhiteSpace(token))
			{
				return token ?? string.Empty;
			}
			Localization instance = Localization.instance;
			string text = ((instance != null) ? instance.Localize(token) : null) ?? token;
			if (!IsMissingTranslation(text, token))
			{
				return text;
			}
			string translationKey = GetTranslationKey(token);
			Dictionary<string, string> dictionary = ReadEmbeddedTranslations("English");
			if (dictionary != null && dictionary.TryGetValue(translationKey, out var value))
			{
				return value;
			}
			return text;
		}

		internal static void ApplyLanguage(Localization localization)
		{
			if (localization == null)
			{
				return;
			}
			string selectedLanguage = localization.GetSelectedLanguage();
			string text = (string.IsNullOrWhiteSpace(selectedLanguage) ? "English" : selectedLanguage);
			try
			{
				Dictionary<string, string> dictionary = ReadEmbeddedTranslations("English");
				if (dictionary == null)
				{
					ReportMissingEnglish();
					return;
				}
				Dictionary<string, string> dictionary2 = new Dictionary<string, string>(dictionary, StringComparer.Ordinal);
				if (!string.Equals(text, "English", StringComparison.OrdinalIgnoreCase))
				{
					Dictionary<string, string> dictionary3 = ReadEmbeddedTranslations(text);
					if (dictionary3 != null)
					{
						OverlayTranslations(dictionary2, dictionary3);
					}
				}
				Dictionary<string, string> dictionary4 = ReadExternalTranslations(text);
				if (dictionary4 != null)
				{
					OverlayTranslations(dictionary2, dictionary4);
				}
				foreach (KeyValuePair<string, string> item in dictionary2)
				{
					GameAccess.AddWord(localization, item.Key, item.Value);
				}
			}
			catch (Exception ex)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to load localization for '" + text + "': " + ex.Message));
			}
		}

		private static Dictionary<string, string>? ReadExternalTranslations(string language)
		{
			if (!IsSafeLanguageFileComponent(language))
			{
				CaptainValheimPlugin.ModLogger.LogWarning((object)("Skipped external localization for unsafe language name '" + language + "'."));
				return null;
			}
			string logicalFileName = "CaptainValheim." + language + ".yml";
			string directoryName;
			try
			{
				directoryName = Path.GetDirectoryName(typeof(CaptainValheimPlugin).Assembly.Location);
			}
			catch (Exception ex)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to resolve the plugin directory for external localization: " + ex.Message));
				return null;
			}
			if (string.IsNullOrWhiteSpace(directoryName) || !Directory.Exists(directoryName))
			{
				return null;
			}
			string text2;
			try
			{
				string text = Path.Combine(directoryName, logicalFileName);
				text2 = (File.Exists(text) ? text : (from path in Directory.EnumerateFiles(directoryName, "*", SearchOption.TopDirectoryOnly)
					where string.Equals(Path.GetFileName(path), logicalFileName, StringComparison.OrdinalIgnoreCase)
					select path).OrderBy<string, string>(Path.GetFileName, StringComparer.Ordinal).FirstOrDefault());
			}
			catch (Exception ex2)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to search for external localization '" + logicalFileName + "': " + ex2.Message));
				return null;
			}
			if (text2 == null)
			{
				return null;
			}
			try
			{
				using StreamReader streamReader = new StreamReader(text2, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
				Dictionary<string, string> dictionary = Deserializer.Deserialize<Dictionary<string, string>>(streamReader.ReadToEnd());
				if (dictionary == null)
				{
					throw new InvalidDataException("the document root must be a translation mapping.");
				}
				foreach (KeyValuePair<string, string> item in dictionary)
				{
					if (string.IsNullOrWhiteSpace(item.Key) || item.Value == null)
					{
						throw new InvalidDataException("translation keys must be non-empty and values cannot be null.");
					}
				}
				return dictionary;
			}
			catch (Exception ex3)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to read external localization '" + logicalFileName + "': " + ex3.Message));
				return null;
			}
		}

		private static bool IsSafeLanguageFileComponent(string language)
		{
			if (!string.IsNullOrWhiteSpace(language) && language.IndexOf("..", StringComparison.Ordinal) < 0 && language.IndexOf('/') < 0 && language.IndexOf('\\') < 0)
			{
				return language.IndexOfAny(Path.GetInvalidFileNameChars()) < 0;
			}
			return false;
		}

		private static void OverlayTranslations(Dictionary<string, string> target, Dictionary<string, string> overlay)
		{
			foreach (KeyValuePair<string, string> item in overlay)
			{
				if (target.ContainsKey(item.Key))
				{
					target[item.Key] = item.Value;
				}
				else
				{
					CaptainValheimPlugin.ModLogger.LogWarning((object)("Ignored unknown localization key '" + item.Key + "'."));
				}
			}
		}

		private static Dictionary<string, string>? ReadEmbeddedTranslations(string language)
		{
			if (TranslationCache.TryGetValue(language, out Dictionary<string, string> value))
			{
				return value;
			}
			Assembly assembly = typeof(CaptainValheimPlugin).Assembly;
			string resourceSuffix = "CaptainValheim.translations." + language + ".yml";
			string text = assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(resourceSuffix, StringComparison.OrdinalIgnoreCase));
			if (text == null)
			{
				TranslationCache[language] = null;
				return null;
			}
			using Stream stream = assembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				TranslationCache[language] = null;
				return null;
			}
			using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
			Dictionary<string, string> dictionary = Deserializer.Deserialize<Dictionary<string, string>>(streamReader.ReadToEnd());
			TranslationCache[language] = dictionary;
			return dictionary;
		}

		private static bool IsMissingTranslation(string localized, string token)
		{
			if (string.IsNullOrWhiteSpace(localized) || string.Equals(localized, token, StringComparison.Ordinal))
			{
				return true;
			}
			if (token[0] == '$')
			{
				return string.Equals(localized, "[" + GetTranslationKey(token) + "]", StringComparison.Ordinal);
			}
			return false;
		}

		private static string GetTranslationKey(string token)
		{
			if (token.Length <= 0 || token[0] != '$')
			{
				return token;
			}
			return token.Substring(1);
		}

		private static void ReportMissingEnglish()
		{
			if (!_reportedMissingEnglish)
			{
				_reportedMissingEnglish = true;
				CaptainValheimPlugin.ModLogger.LogError((object)"Embedded English localization resource was not found.");
			}
		}
	}
	[HarmonyPatch(typeof(Localization), "SetupLanguage")]
	internal static class LocalizationSetupLanguageCaptainValheimPatch
	{
		private static void Postfix(Localization __instance)
		{
			CaptainValheimLocalization.ApplyLanguage(__instance);
		}
	}
	internal sealed class KeyHintCell
	{
		private readonly List<TMP_Text> _keys = new List<TMP_Text>();

		private readonly List<GameObject> _keyParents = new List<GameObject>();

		private readonly List<TMP_Text> _extraTexts = new List<TMP_Text>();

		private readonly List<TMP_Text> _generatedSeparatorTexts = new List<TMP_Text>();

		private TMP_Text? _label;

		internal GameObject Root { get; }

		private KeyHintCell(GameObject root)
		{
			Root = root;
			RefreshChildren();
		}

		internal static KeyHintCell? CloneFrom(GameObject? template, string name)
		{
			if (!IsUsableTemplate(template) || (Object)(object)template.transform.parent == (Object)null)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(template, template.transform.parent, false);
			((Object)obj).name = name;
			obj.SetActive(false);
			return new KeyHintCell(obj);
		}

		internal static bool IsUsableTemplate(GameObject? template)
		{
			if ((Object)(object)template != (Object)null && (Object)(object)template.transform.parent != (Object)null && !((Object)template).name.StartsWith("CaptainValheim_"))
			{
				return template.GetComponentsInChildren<TMP_Text>(true).Length != 0;
			}
			return false;
		}

		internal static Transform? FindParentWithTemplates(GameObject root, string name)
		{
			Transform val = root.transform.Find(name);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (!((IEnumerable)val).Cast<Transform>().Any((Transform child) => IsUsableTemplate(((Component)child).gameObject)))
			{
				return null;
			}
			return val;
		}

		internal void Set(string label, IReadOnlyList<string> keys, bool hideExtraTexts = false)
		{
			EnsureKeyCount(keys.Count);
			Root.SetActive(true);
			if ((Object)(object)_label != (Object)null)
			{
				SetText(_label, label);
			}
			for (int i = 0; i < _keys.Count; i++)
			{
				bool flag = i < keys.Count;
				if (i < _keyParents.Count && (Object)(object)_keyParents[i] != (Object)null)
				{
					_keyParents[i].SetActive(flag);
				}
				if (flag)
				{
					SetText(_keys[i], keys[i]);
				}
			}
			if (hideExtraTexts)
			{
				foreach (TMP_Text extraText in _extraTexts)
				{
					if ((Object)(object)extraText != (Object)null)
					{
						((Component)extraText).gameObject.SetActive(false);
					}
				}
				return;
			}
			EnsureSeparatorCount(Mathf.Max(0, keys.Count - 1));
			for (int j = 0; j < _generatedSeparatorTexts.Count; j++)
			{
				TMP_Text val = _generatedSeparatorTexts[j];
				if (!((Object)(object)val == (Object)null))
				{
					bool flag2 = j < keys.Count - 1;
					((Component)val).gameObject.SetActive(flag2);
					if (flag2)
					{
						SetText(val, "+");
					}
				}
			}
			foreach (TMP_Text extraText2 in _extraTexts)
			{
				if ((Object)(object)extraText2 != (Object)null)
				{
					((Component)extraText2).gameObject.SetActive(keys.Count > 1);
				}
			}
		}

		internal void SetActive(bool active)
		{
			if ((Object)(object)Root != (Object)null)
			{
				Root.SetActive(active);
			}
		}

		internal void MoveBefore(GameObject? template)
		{
			if (!((Object)(object)template == (Object)null) && !((Object)(object)Root == (Object)null) && !((Object)(object)Root == (Object)(object)template) && !((Object)(object)Root.transform.parent != (Object)(object)template.transform.parent))
			{
				int siblingIndex = Root.transform.GetSiblingIndex();
				int siblingIndex2 = template.transform.GetSiblingIndex();
				int num = ((siblingIndex < siblingIndex2) ? (siblingIndex2 - 1) : siblingIndex2);
				if (siblingIndex != num)
				{
					Root.transform.SetSiblingIndex(Mathf.Max(0, num));
				}
			}
		}

		internal void RebuildParentLayout()
		{
			if ((Object)(object)Root != (Object)null)
			{
				Transform parent = Root.transform.parent;
				RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				if (val != null)
				{
					LayoutRebuilder.ForceRebuildLayoutImmediate(val);
				}
			}
		}

		private void EnsureKeyCount(int count)
		{
			RefreshChildren();
			if (count <= _keys.Count || _keyParents.Count == 0)
			{
				return;
			}
			GameObject val = _keyParents[0];
			Transform parent = val.transform.parent;
			while (_keys.Count < count)
			{
				int count2 = _keys.Count;
				GameObject val2 = Object.Instantiate<GameObject>(val, parent, false);
				((Object)val2).name = ((_keys.Count == 1) ? "key_bkg (1)" : $"key_bkg ({_keys.Count})");
				RefreshChildren();
				if (_keys.Count <= count2)
				{
					val2.SetActive(false);
					val2.transform.SetParent((Transform)null, false);
					Object.Destroy((Object)(object)val2);
					RefreshChildren();
					break;
				}
			}
		}

		private void EnsureSeparatorCount(int count)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			if (count <= 0 || _keyParents.Count == 0 || _extraTexts.Count > 0)
			{
				return;
			}
			Transform parent = _keyParents[0].transform.parent;
			TMP_Text val = ResolveSeparatorTemplateText();
			if ((Object)(object)parent == (Object)null || (Object)(object)val == (Object)null)
			{
				return;
			}
			while (_generatedSeparatorTexts.Count < count)
			{
				GameObject val2 = new GameObject($"CaptainValheim_KeyHintSeparator_{_generatedSeparatorTexts.Count}");
				val2.SetActive(false);
				val2.transform.SetParent(parent, false);
				val2.AddComponent<RectTransform>().sizeDelta = new Vector2(18f, 24f);
				TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>();
				CopyTextStyle(val, (TMP_Text)(object)val3);
				((TMP_Text)val3).alignment = (TextAlignmentOptions)514;
				((Graphic)val3).raycastTarget = false;
				((TMP_Text)val3).text = "+";
				LayoutElement obj = val2.AddComponent<LayoutElement>();
				obj.preferredWidth = 18f;
				obj.minWidth = 12f;
				_generatedSeparatorTexts.Add((TMP_Text)(object)val3);
			}
			for (int i = 0; i < _generatedSeparatorTexts.Count; i++)
			{
				TMP_Text val4 = _generatedSeparatorTexts[i];
				if (!((Object)(object)val4 == (Object)null))
				{
					int num = Mathf.Min(i + 1, _keyParents.Count - 1);
					if (num >= 0 && num < _keyParents.Count && (Object)(object)_keyParents[num] != (Object)null)
					{
						val4.transform.SetSiblingIndex(_keyParents[num].transform.GetSiblingIndex());
					}
				}
			}
		}

		private TMP_Text? ResolveSeparatorTemplateText()
		{
			return GetStyleTemplate(_label) ?? _keys.Select(GetStyleTemplate).FirstOrDefault<TMP_Text>((Func<TMP_Text, bool>)((TMP_Text text) => (Object)(object)text != (Object)null)) ?? GetStyleTemplate(((IEnumerable<TMP_Text>)Root.GetComponentsInChildren<TMP_Text>(true)).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => (Object)(object)text != (Object)null && (Object)(object)text.font != (Object)null))) ?? _label ?? _keys.FirstOrDefault();
		}

		private static TMP_Text? GetStyleTemplate(TMP_Text? text)
		{
			if (!((Object)(object)text != (Object)null) || !((Object)(object)text.font != (Object)null))
			{
				return null;
			}
			return text;
		}

		private static void CopyTextStyle(TMP_Text source, TMP_Text target)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			target.font = source.font;
			target.fontSharedMaterial = source.fontSharedMaterial;
			target.fontSize = source.fontSize;
			target.fontSizeMin = source.fontSizeMin;
			target.fontSizeMax = source.fontSizeMax;
			target.enableAutoSizing = source.enableAutoSizing;
			target.fontStyle = source.fontStyle;
			target.fontWeight = source.fontWeight;
			((Graphic)target).color = ((Graphic)source).color;
			target.characterSpacing = source.characterSpacing;
			target.wordSpacing = source.wordSpacing;
			target.lineSpacing = source.lineSpacing;
			target.paragraphSpacing = source.paragraphSpacing;
			target.textWrappingMode = source.textWrappingMode;
			target.overflowMode = source.overflowMode;
		}

		private void RefreshChildren()
		{
			_keys.Clear();
			_keyParents.Clear();
			_extraTexts.Clear();
			_label = null;
			TMP_Text[] array = (from text in Root.GetComponentsInChildren<TMP_Text>(true)
				where (Object)(object)text != (Object)null
				select text).ToArray();
			TMP_Text[] array2 = array;
			foreach (TMP_Text val in array2)
			{
				Localization instance = Localization.instance;
				if (instance != null)
				{
					instance.RemoveTextFromCache(val);
				}
				TextMeshProUGUI val2 = (TextMeshProUGUI)(object)((val is TextMeshProUGUI) ? val : null);
				if (val2 != null)
				{
					((Graphic)val2).raycastTarget = false;
				}
			}
			_keys.AddRange(array.Where((TMP_Text text) => string.Equals(((Object)text).name, "Key", StringComparison.OrdinalIgnoreCase)));
			if (_keys.Count == 0)
			{
				TMP_Text val3 = ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => LooksLikeKeyBindingText(text.text))) ?? array.OrderBy((TMP_Text text) => text.transform.position.x).LastOrDefault();
				if ((Object)(object)val3 != (Object)null && array.Length > 1)
				{
					_keys.Add(val3);
				}
			}
			_label = ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => string.Equals(((Object)text).name, "Text", StringComparison.OrdinalIgnoreCase) && !_keys.Contains(text))) ?? ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => !_keys.Contains(text) && !LooksLikeKeyBindingText(text.text))) ?? ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => !_keys.Contains(text)));
			foreach (TMP_Text key in _keys)
			{
				_keyParents.Add(((Object)(object)key.transform.parent != (Object)null) ? ((Component)key.transform.parent).gameObject : ((Component)key).gameObject);
			}
			_extraTexts.AddRange(array.Where((TMP_Text text) => (Object)(object)text != (Object)(object)_label && !_keys.Contains(text)));
			SortKeysBySiblingIndex();
		}

		private void SortKeysBySiblingIndex()
		{
			List<int> list = (from i in Enumerable.Range(0, _keys.Count)
				orderby (!((Object)(object)_keyParents[i] != (Object)null)) ? i : _keyParents[i].transform.GetSiblingIndex()
				select i).ToList();
			if (list.Count <= 1)
			{
				return;
			}
			List<TMP_Text> list2 = new List<TMP_Text>();
			List<GameObject> list3 = new List<GameObject>();
			foreach (int item in list)
			{
				list2.Add(_keys[item]);
				list3.Add(_keyParents[item]);
			}
			_keys.Clear();
			_keys.AddRange(list2);
			_keyParents.Clear();
			_keyParents.AddRange(list3);
		}

		private static void SetText(TMP_Text? text, string value)
		{
			if (!((Object)(object)text == (Object)null))
			{
				Localization instance = Localization.instance;
				if (instance != null)
				{
					instance.RemoveTextFromCache(text);
				}
				((Component)text).gameObject.SetActive(true);
				text.text = value;
			}
		}

		private static bool LooksLikeKeyBindingText(string? text)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = new string(text.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray());
			if (!text2.Contains("mouse") && !text2.Contains("ctrl") && !text2.Contains("shift") && !text2.Contains("alt") && !text2.Contains("button") && !text2.Contains("key") && !text2.Contains("sprite"))
			{
				return text2.Length <= 2;
			}
			return true;
		}
	}
	internal static class ShieldChargeCooldownStatusSystem
	{
		private const string StatusEffectName = "CaptainValheim_Cooldown_shieldCharge";

		private const string DisplayName = "$captainvalheim_shield_charge_cooldown_name";

		private const string Tooltip = "$captainvalheim_shield_charge_cooldown_tooltip";

		private const string FallbackIconPrefabName = "ShieldWood";

		private static readonly ConditionalWeakTable<StatusEffect, object> OwnedStatusEffects = new ConditionalWeakTable<StatusEffect, object>();

		internal static void RegisterStatusEffect(ObjectDB objectDb)
		{
			if (!((Object)(object)objectDb == (Object)null) && !objectDb.m_StatusEffects.Exists((StatusEffect statusEffect) => (Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name == "CaptainValheim_Cooldown_shieldCharge"))
			{
				StatusEffect val = ScriptableObject.CreateInstance<StatusEffect>();
				((Object)val).name = "CaptainValheim_Cooldown_shieldCharge";
				val.m_name = "$captainvalheim_shield_charge_cooldown_name";
				val.m_tooltip = "$captainvalheim_shield_charge_cooldown_tooltip";
				val.m_icon = ResolveIcon(objectDb, "ShieldWood");
				objectDb.m_StatusEffects.Add(val);
				OwnedStatusEffects.Add(val, new object());
			}
		}

		internal static void UnregisterStatusEffect(ObjectDB objectDb)
		{
			if ((Object)(object)objectDb == (Object)null)
			{
				return;
			}
			for (int num = objectDb.m_StatusEffects.Count - 1; num >= 0; num--)
			{
				StatusEffect val = objectDb.m_StatusEffects[num];
				if (val != null && OwnedStatusEffects.TryGetValue(val, out object _))
				{
					objectDb.m_StatusEffects.RemoveAt(num);
					OwnedStatusEffects.Remove(val);
					Object.Destroy((Object)(object)val);
				}
			}
		}

		internal static void Apply(Character character, ItemData? shield, float cooldown)
		{
			if ((Object)(object)character == (Object)null || cooldown <= 0f)
			{
				return;
			}
			SEMan sEMan = character.GetSEMan();
			if (sEMan != null)
			{
				int stableHashCode = StringExtensionMethods.GetStableHashCode("CaptainValheim_Cooldown_shieldCharge");
				sEMan.AddStatusEffect(stableHashCode, true, 0, 0f, (short)(-1));
				StatusEffect statusEffect = sEMan.GetStatusEffect(stableHashCode);
				if (statusEffect != null)
				{
					statusEffect.m_ttl = cooldown;
					statusEffect.m_icon = ResolveShieldIcon(shield) ?? statusEffect.m_icon;
				}
			}
		}

		private static Sprite? ResolveShieldIcon(ItemData? shield)
		{
			Sprite[] array = shield?.m_shared?.m_icons;
			if (array == null || array.Length <= 0)
			{
				return null;
			}
			return array[0];
		}

		private static Sprite? ResolveIcon(ObjectDB objectDb, string itemPrefabName)
		{
			GameObject itemPrefab = objectDb.GetItemPrefab(itemPrefabName);
			Sprite[] array = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_icons;
			if (array == null || array.Length <= 0)
			{
				return null;
			}
			return array[0];
		}
	}
	internal static class ShieldOnlyKeyHintSystem
	{
		private readonly struct ShieldHintState
		{
			internal static readonly ShieldHintState Hidden = new ShieldHintState("", gamepad: false, preserveWeaponHints: false, hasCharge: false, hasPrimaryAttack: false, hasThrow: false, "", "", "");

			private readonly string _language;

			private readonly bool _gamepad;

			internal readonly bool PreserveWeaponHints;

			internal readonly bool HasCharge;

			internal readonly bool HasPrimaryAttack;

			internal readonly bool HasThrow;

			internal readonly string AttackKey;

			internal readonly string BlockKey;

			internal readonly string SecondaryAttackKey;

			internal bool HasRows
			{
				get
				{
					if (!HasCharge && !HasPrimaryAttack)
					{
						return HasThrow;
					}
					return true;
				}
			}

			public ShieldHintState(string language, bool gamepad, bool preserveWeaponHints, bool hasCharge, bool hasPrimaryAttack, bool hasThrow, string attackKey, string blockKey, string secondaryAttackKey)
			{
				_language = language;
				_gamepad = gamepad;
				PreserveWeaponHints = preserveWeaponHints;
				HasCharge = hasCharge;
				HasPrimaryAttack = hasPrimaryAttack;
				HasThrow = hasThrow;
				AttackKey = attackKey;
				BlockKey = blockKey;
				SecondaryAttackKey = secondaryAttackKey;
			}

			internal bool Equals(ShieldHintState other)
			{
				if (_gamepad == other._gamepad && PreserveWeaponHints == other.PreserveWeaponHints && HasCharge == other.HasCharge && HasPrimaryAttack == other.HasPrimaryAttack && HasThrow == other.HasThrow && string.Equals(AttackKey, other.AttackKey, StringComparison.Ordinal) && string.Equals(BlockKey, other.BlockKey, StringComparison.Ordinal) && string.Equals(SecondaryAttackKey, other.SecondaryAttackKey, StringComparison.Ordinal))
				{
					return string.Equals(_language, other._language, StringComparison.Ordinal);
				}
				return false;
			}
		}

		private readonly struct ShieldHintRow
		{
			internal readonly string Label;

			internal readonly IReadOnlyList<string> Keys;

			public ShieldHintRow(string label, IReadOnlyList<string> keys)
			{
				Label = label;
				Keys = keys;
			}
		}

		private static readonly List<KeyHintCell> HintCells = new List<KeyHintCell>();

		private static readonly List<ShieldHintRow> ReusableRows = new List<ShieldHintRow>();

		private static ShieldHintState _lastHintState = ShieldHintState.Hidden;

		private static bool _hasLastHintState;

		private static bool _showingHints;

		private static KeyHints? _preparedOwner;

		private static ShieldHintState _preparedState = ShieldHintState.Hidden;

		private static bool _preparedVisible;

		internal static void InitializeKeyHints(KeyHints hints)
		{
			DestroyHints();
			_hasLastHintState = false;
			_lastHintState = ShieldHintState.Hidden;
			_showingHints = false;
			_preparedOwner = hints;
			_preparedVisible = false;
		}

		internal static void DestroyKeyHints(KeyHints hints)
		{
			if (_preparedOwner == hints)
			{
				Dispose();
			}
		}

		internal static void PrepareKeyHintUpdate(KeyHints hints)
		{
			_preparedOwner = hints;
			_preparedState = ShieldHintState.Hidden;
			Player localPlayer = Player.m_localPlayer;
			_preparedVisible = ShouldShowCustomCombatHints(hints, localPlayer) && TryBuildHintState(localPlayer, out _preparedState) && _preparedState.HasRows;
		}

		internal static bool FilterCombatHintVisibility(bool vanillaVisible, KeyHints hints, bool combatGroup)
		{
			if (_preparedOwner != hints || !_preparedVisible)
			{
				return vanillaVisible;
			}
			if (!combatGroup)
			{
				return _preparedState.PreserveWeaponHints && vanillaVisible;
			}
			return true;
		}

		internal static void UpdateKeyHint(KeyHints hints, bool originalRan)
		{
			if ((Object)(object)hints == (Object)null)
			{
				return;
			}
			if (!originalRan || _preparedOwner != hints || !_preparedVisible)
			{
				HideHints();
				RememberHintState(ShieldHintState.Hidden);
				return;
			}
			ShieldHintState preparedState = _preparedState;
			if (_showingHints && _hasLastHintState && _lastHintState.Equals(preparedState))
			{
				HideVanillaWeaponHints(hints, preparedState.PreserveWeaponHints);
				return;
			}
			BuildHintRows(preparedState, ReusableRows);
			EnsureHints(hints, ReusableRows.Count);
			if (HintCells.Count == 0)
			{
				return;
			}
			HideVanillaWeaponHints(hints, preparedState.PreserveWeaponHints);
			for (int i = 0; i < HintCells.Count; i++)
			{
				KeyHintCell keyHintCell = HintCells[i];
				if (i >= ReusableRows.Count)
				{
					keyHintCell.SetActive(active: false);
					continue;
				}
				ShieldHintRow shieldHintRow = ReusableRows[i];
				keyHintCell.Set(shieldHintRow.Label, shieldHintRow.Keys, shieldHintRow.Keys.Count <= 1);
			}
			HintCells[0].RebuildParentLayout();
			RememberHintState(preparedState);
			_showingHints = true;
		}

		private static bool TryBuildHintState(Player player, out ShieldHintState state)
		{
			//IL_0035: 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_0049: Invalid comparison between Unknown and I4
			state = ShieldHintState.Hidden;
			ItemData leftItem = ((Humanoid)player).LeftItem;
			ItemData rightItem = ((Humanoid)player).RightItem;
			if (leftItem != null && (int)(leftItem.m_shared?.m_itemType).GetValueOrDefault() == 5 && ShieldRuntimeSystem.TryGetDefinition(leftItem, out SecondaryAttackDefinition definition))
			{
				ShieldSpecialSecondaryBehavior shieldSpecial = definition.ShieldSpecial;
				if (shieldSpecial != null)
				{
					bool flag = rightItem == null;
					bool hasCharge = shieldSpecial.HasShieldCharge && shieldSpecial.ShieldChargeDistance > 0f && ShieldRuntimeSystem.IsShieldFeatureAllowed((Humanoid)(object)player, leftItem, CaptainValheimPlugin.Settings.WeaponCompatibility.AllowCharge.Value);
					Localization instance = Localization.instance;
					state = new ShieldHintState(((instance != null) ? instance.GetSelectedLanguage() : null) ?? "", ZInput.IsGamepadActive(), !flag, hasCharge, flag && shieldSpecial.HasShieldPrimaryAttack, flag && shieldSpecial.HasShieldThrow, ResolveButtonLabel("Attack"), ResolveButtonLabel("Block"), ResolveButtonLabel("SecondaryAttack"));
					return state.HasRows;
				}
			}
			return false;
		}

		private static void BuildHintRows(ShieldHintState state, List<ShieldHintRow> rows)
		{
			rows.Clear();
			if (state.HasCharge)
			{
				rows.Add(new ShieldHintRow(CaptainValheimLocalization.Text("captainvalheim_hint_charge"), new <>z__ReadOnlyArray<string>(new string[2] { state.BlockKey, state.SecondaryAttackKey })));
			}
			if (state.HasPrimaryAttack)
			{
				rows.Add(new ShieldHintRow(CaptainValheimLocalization.Text("captainvalheim_hint_attack"), new <>z__ReadOnlySingleElementList<string>(state.AttackKey)));
			}
			if (state.HasThrow)
			{
				rows.Add(new ShieldHintRow(CaptainValheimLocalization.Text("captainvalheim_hint_throw"), new <>z__ReadOnlySingleElementList<string>(state.SecondaryAttackKey)));
			}
		}

		private static bool ShouldShowCustomCombatHints(KeyHints hints, Player? player)
		{
			if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && GameAccess.KeyHintsEnabled.Invoke(hints) && !((Character)player).InPlaceMode() && !Hud.IsPieceSelectionVisible() && !Hud.InRadial() && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !Game.IsPaused() && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.IsChatDialogWindowVisible()) && ((Object)(object)InventoryGui.instance == (Object)null || (!InventoryGui.instance.IsSkillsPanelOpen && !InventoryGui.instance.IsTrophisPanelOpen && !InventoryGui.instance.IsAchievementsPanelOpen && !InventoryGui.instance.IsTextPanelOpen)) && !PlayerCustomizaton.IsBarberGuiVisible())
			{
				return player.GetDoodadController() == null;
			}
			return false;
		}

		private static void HideVanillaWeaponHints(KeyHints hints, bool preserveWeaponHints)
		{
			if (!preserveWeaponHints)
			{
				SetVanillaCombatHintActive(hints.m_bowDrawGP, active: false);
				SetVanillaCombatHintActive(hints.m_bowDrawKB, active: false);
				SetVanillaCombatHintActive(hints.m_primaryAttackGP, active: false);
				SetVanillaCombatHintActive(hints.m_primaryAttackKB, active: false);
				SetVanillaCombatHintActive(hints.m_secondaryAttackGP, active: false);
				SetVanillaCombatHintActive(hints.m_secondaryAttackKB, active: false);
			}
		}

		private static void SetVanillaCombatHintActive(GameObject? hint, bool active)
		{
			if ((Object)(object)hint != (Object)null)
			{
				hint.SetActive(active);
			}
		}

		private static void EnsureHints(KeyHints hints, int count)
		{
			GameObject val = ResolveCombatHintTemplate(hints);
			if ((Object)(object)val == (Object)null || (Object)(object)val.transform.parent == (Object)null)
			{
				return;
			}
			if (HintCells.Count > 0 && (Object)(object)HintCells[0].Root.transform.parent != (Object)(object)val.transform.parent)
			{
				DestroyHints();
			}
			while (HintCells.Count < count)
			{
				KeyHintCell keyHintCell = KeyHintCell.CloneFrom(val, $"CaptainValheim_ShieldOnlyHint_{HintCells.Count}");
				if (keyHintCell == null)
				{
					break;
				}
				keyHintCell.MoveBefore(val);
				HintCells.Add(keyHintCell);
			}
			foreach (KeyHintCell hintCell in HintCells)
			{
				hintCell.MoveBefore(val);
			}
		}

		private static GameObject? ResolveCombatHintTemplate(KeyHints hints)
		{
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			GameObject val = (ZInput.IsGamepadActive() ? hints.m_primaryAttackGP : hints.m_primaryAttackKB);
			if (KeyHintCell.IsUsableTemplate(val))
			{
				return val;
			}
			GameObject val2 = (ZInput.IsGamepadActive() ? hints.m_primaryAttackKB : hints.m_primaryAttackGP);
			if (KeyHintCell.IsUsableTemplate(val2))
			{
				return val2;
			}
			GameObject val3 = (ZInput.IsGamepadActive() ? hints.m_secondaryAttackGP : hints.m_secondaryAttackKB);
			if (KeyHintCell.IsUsableTemplate(val3))
			{
				return val3;
			}
			GameObject val4 = (ZInput.IsGamepadActive() ? hints.m_secondaryAttackKB : hints.m_secondaryAttackGP);
			if (KeyHintCell.IsUsableTemplate(val4))
			{
				return val4;
			}
			if ((Object)(object)hints.m_combatHints == (Object)null)
			{
				return null;
			}
			foreach (Transform item in KeyHintCell.FindParentWithTemplates(hints.m_combatHints, ZInput.IsGamepadActive() ? "Gamepad" : "Keyboard") ?? KeyHintCell.FindParentWithTemplates(hints.m_combatHints, "Keyboard") ?? KeyHintCell.FindParentWithTemplates(hints.m_combatHints, "Gamepad") ?? hints.m_combatHints.transform)
			{
				Transform val5 = item;
				if (KeyHintCell.IsUsableTemplate(((Component)val5).gameObject))
				{
					return ((Component)val5).gameObject;
				}
			}
			return null;
		}

		private static string ResolveButtonLabel(string button)
		{
			bool flag = ZInput.IsGamepadActive();
			string text = button;
			if (flag && button.Equals("Attack", StringComparison.OrdinalIgnoreCase))
			{
				text = "JoyAttack";
			}
			else if (flag && button.Equals("Block", StringComparison.OrdinalIgnoreCase))
			{
				text = "JoyBlock";
			}
			else if (flag && button.Equals("SecondaryAttack", StringComparison.OrdinalIgnoreCase))
			{
				text = "JoySecondaryAttack";
			}
			ZInput instance = ZInput.instance;
			string text2 = ((instance != null) ? instance.GetBoundKeyString(text, true) : null) ?? "";
			if (!string.IsNullOrWhiteSpace(text2))
			{
				if (Localization.instance == null)
				{
					return text2;
				}
				return Localization.instance.Localize(text2);
			}
			if (!(button == "Attack"))
			{
				if (button == "Block")
				{
					return flag ? "LB" : "RMB";
				}
				return flag ? "RB" : "MMB";
			}
			return flag ? "RT" : "LMB";
		}

		private static void HideHints()
		{
			if (!_showingHints)
			{
				return;
			}
			foreach (KeyHintCell hintCell in HintCells)
			{
				hintCell.SetActive(active: false);
			}
			if (HintCells.Count > 0)
			{
				HintCells[0].RebuildParentLayout();
			}
			_showingHints = false;
		}

		internal static void Dispose()
		{
			DestroyHints();
			ReusableRows.Clear();
			_lastHintState = ShieldHintState.Hidden;
			_hasLastHintState = false;
			_showingHints = false;
			_preparedOwner = null;
			_preparedVisible = false;
			_preparedState = ShieldHintState.Hidden;
		}

		private static void DestroyHints()
		{
			foreach (KeyHintCell hintCell in HintCells)
			{
				if ((Object)(object)hintCell.Root != (Object)null)
				{
					hintCell.Root.SetActive(false);
					hintCell.Root.transform.SetParent((Transform)null, false);
					Object.Destroy((Object)(object)hintCell.Root);
				}
			}
			HintCells.Clear();
		}

		private static void RememberHintState(ShieldHintState state)
		{
			_lastHintState = state;
			_hasLastHintState = true;
		}
	}
	[HarmonyPatch(typeof(KeyHints), "Awake")]
	internal static class KeyHintsAwakeShieldOnlyPatch
	{
		private static void Postfix(KeyHints __instance)
		{
			ShieldOnlyKeyHintSystem.InitializeKeyHints(__instance);
		}
	}
	[HarmonyPatch(typeof(KeyHints), "UpdateHints")]
	internal static class KeyHintsUpdateShieldOnlyPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(KeyHints __instance)
		{
			ShieldOnlyKeyHintSystem.PrepareKeyHintUpdate(__instance);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Expected O, but got Unknown
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			MethodInfo method = typeof(GameObject).GetMethod("SetActive", new Type[1] { typeof(bool) });
			MethodInfo method2 = typeof(ShieldOnlyKeyHintSystem).GetMethod("FilterCombatHintVisibility", BindingFlags.Static | BindingFlags.NonPublic);
			Dictionary<string, int> dictionary = new Dictionary<string, int>
			{
				["m_combatHints"] = 0,
				["m_bowDrawGP"] = 0,
				["m_bowDrawKB"] = 0,
				["m_primaryAttackGP"] = 0,
				["m_primaryAttackKB"] = 0,
				["m_secondaryAttackGP"] = 0,
				["m_secondaryAttackKB"] = 0
			};
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			List<CodeInstruction> list2 = new List<CodeInstruction>(list.Count + 45);
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (CodeInstructionExtensions.Calls(val, method) && i >= 3 && list[i - 3].opcode == OpCodes.Ldarg_0 && list[i - 2].opcode == OpCodes.Ldfld && list[i - 2].operand is FieldInfo fieldInfo && fieldInfo.DeclaringType == typeof(KeyHints) && dictionary.ContainsKey(fieldInfo.Name) && IsVisibilityLoad(list[i - 1]))
				{
					dictionary[fieldInfo.Name]++;
					CodeInstruction val2 = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					CodeInstructionExtensions.MoveLabelsFrom(val2, val);
					list2.Add(val2);
					list2.Add(new CodeInstruction((fieldInfo.Name == "m_combatHints") ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0, (object)null));
					list2.Add(new CodeInstruction(OpCodes.Call, (object)method2));
				}
				list2.Add(val);
			}
			foreach (KeyValuePair<string, int> item in dictionary)
			{
				int num = ((!(item.Key == "m_combatHints")) ? 1 : 9);
				if (item.Value != num)
				{
					throw new InvalidOperationException($"Expected {num} visibility writes for KeyHints.{item.Key}, found {item.Value}.");
				}
			}
			return list2;
		}

		private static bool IsVisibilityLoad(CodeInstruction instruction)
		{
			OpCode opcode = instruction.opcode;
			if (!(opcode == OpCodes.Ldc_I4_0) && !(opcode == OpCodes.Ldc_I4_1) && !(opcode == OpCodes.Ldloc) && !(opcode == OpCodes.Ldloc_S) && !(opcode == OpCodes.Ldloc_0) && !(opcode == OpCodes.Ldloc_1) && !(opcode == OpCodes.Ldloc_2))
			{
				return opcode == OpCodes.Ldloc_3;
			}
			return true;
		}

		private static void Postfix(KeyHints __instance, bool __runOriginal)
		{
			ShieldOnlyKeyHintSystem.UpdateKeyHint(__instance, __runOriginal);
		}
	}
	[HarmonyPatch(typeof(KeyHints), "OnDestroy")]
	internal static class KeyHintsDestroyShieldOnlyPatch
	{
		private static void Postfix(KeyHints __instance)
		{
			ShieldOnlyKeyHintSystem.DestroyKeyHints(__instance);
		}
	}
	internal static class ShieldTechniqueCompendiumManager
	{
		private enum ShieldTechniqueGroup
		{
			ActiveAttacks,
			DefensiveTechniques
		}

		private sealed class ShieldTechniqueInfo(ShieldTechniqueGroup group, string iconPrefabName, string nameToken, string descriptionToken)
		{
			internal ShieldTechniqueGroup Group { get; } = group;

			internal string IconPrefabName { get; } = iconPrefabName;

			internal string NameToken { get; } = nameToken;

			internal string DescriptionToken { get; } = descriptionToken;
		}

		private const string PageTopic = "$captainvalheim_compendium_title";

		private const string BodyIconPrefix = "CaptainValheim_CompendiumTechniqueIcon_";

		private const char IconMarker = '*';

		private const float IconSize = 18f;

		private const float IconTextGap = 5f;

		private static readonly IReadOnlyList<ShieldTechniqueInfo> Entries = new <>z__ReadOnlyArray<ShieldTechniqueInfo>(new ShieldTechniqueInfo[5]
		{
			new ShieldTechniqueInfo(ShieldTechniqueGroup.ActiveAttacks, "ShieldWood", "$captainvalheim_compendium_primary_attack_name", "$captainvalheim_compendium_primary_attack_description"),
			new ShieldTechniqueInfo(ShieldTechniqueGroup.ActiveAttacks, "ShieldBanded", "$captainvalheim_compendium_throw_name", "$captainvalheim_compendium_throw_description"),
			new ShieldTechniqueInfo(ShieldTechniqueGroup.ActiveAttacks, "ShieldIronTower", "$captainvalheim_compendium_charge_name", "$captainvalheim_compendium_charge_description"),
			new ShieldTechniqueInfo(ShieldTechniqueGroup.DefensiveTechniques, "ShieldSerpentscale", "$captainvalheim_compendium_reflect_name", "$captainvalheim_compendium_reflect_description"),
			new ShieldTechniqueInfo(ShieldTechniqueGroup.DefensiveTechniques, "ShieldCarapaceBuckler", "$captainvalheim_compendium_block_charge_name", "$captainvalheim_compendium_block_charge_description")
		});

		private static readonly List<GameObject> BodyIcons = new List<GameObject>();

		private static readonly List<TextsDialog> PageDialogs = new List<TextsDialog>();

		internal static void AddTechniquePage(TextsDialog dialog)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			if (!((Object)(object)dialog == (Object)null) && GameAccess.Texts.Invoke(dialog) != null)
			{
				TrackDialog(dialog);
				GameAccess.Texts.Invoke(dialog).RemoveAll((TextInfo text) => IsTechniquePage(text?.m_topic));
				GameAccess.Texts.Invoke(dialog).Add(new TextInfo("$captainvalheim_compendium_title", BuildPageText()));
			}
		}

		internal static void RefreshPageContentIcons(TextsDialog dialog, TextInfo info)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)dialog?.m_textArea == (Object)null || info == null)
			{
				return;
			}
			ClearPageContentIcons(dialog);
			if (!IsTechniquePage(info.m_topic))
			{
				return;
			}
			TMP_Text textArea = dialog.m_textArea;
			Transform parent = textArea.transform.parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			textArea.ForceMeshUpdate(false, false);
			LayoutRebuilder.ForceRebuildLayoutImmediate(val);
			textArea.ForceMeshUpdate(false, false);
			TMP_TextInfo textInfo = textArea.textInfo;
			int num = 0;
			for (int i = 0; i < textInfo.characterCount; i++)
			{
				if (num >= Entries.Count)
				{
					break;
				}
				TMP_CharacterInfo val2 = textInfo.characterInfo[i];
				if (val2.character == '*')
				{
					ShieldTechniqueInfo entry = Entries[num];
					AttachBodyIcon(val, textArea.rectTransform, val2, ResolveIcon(entry), num);
					num++;
				}
			}
		}

		internal static void ClearPageContentIcons(TextsDialog dialog)
		{
			DestroyTrackedIcons();
			if (!((Object)(object)dialog?.m_textArea == (Object)null))
			{
				ClearIconChildren(dialog.m_textArea.transform);
				if ((Object)(object)dialog.m_textArea.transform.parent != (Object)null)
				{
					ClearIconChildren(dialog.m_textArea.transform.parent);
				}
			}
		}

		internal static void Dispose()
		{
			DestroyTrackedIcons();
			foreach (TextsDialog pageDialog in PageDialogs)
			{
				if ((Object)(object)pageDialog == (Object)null)
				{
					continue;
				}
				GameAccess.Texts.Invoke(pageDialog)?.RemoveAll((TextInfo text) => IsTechniquePage(text?.m_topic));
				if (!((Object)(object)pageDialog.m_textArea == (Object)null))
				{
					ClearIconChildren(pageDialog.m_textArea.transform);
					if ((Object)(object)pageDialog.m_textArea.transform.parent != (Object)null)
					{
						ClearIconChildren(pageDialog.m_textArea.transform.parent);
					}
				}
			}
			PageDialogs.Clear();
		}

		private static string BuildPageText()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<color=#D6D6D6>").Append(CaptainValheimLocalization.Localize("$captainvalheim_compendium_intro")).Append("</color>\n\n");
			ShieldTechniqueGroup? shieldTechniqueGroup = null;
			foreach (ShieldTechniqueInfo entry in Entries)
			{
				if (shieldTechniqueGroup != entry.Group)
				{
					if (shieldTechniqueGroup.HasValue)
					{
						stringBuilder.Append('\n');
					}
					stringBuilder.Append("<color=#FFD27A><b>").Append(LocalizeGroupHeading(entry.Group)).Append("</b></color>\n\n");
					shieldTechniqueGroup = entry.Group;
				}
				stringBuilder.Append("<color=#00000000>").Append('*').Append("</color>    ")
					.Append("<color=orange><b>")
					.Append(CaptainValheimLocalization.Localize(entry.NameToken))
					.Append("</b></color>\n     ")
					.Append(CaptainValheimLocalization.Localize(entry.DescriptionToken))
					.Append("\n\n");
			}
			return stringBuilder.ToString().TrimEnd();
		}

		private static string LocalizeGroupHeading(ShieldTechniqueGroup group)
		{
			return group switch
			{
				ShieldTechniqueGroup.ActiveAttacks => CaptainValheimLocalization.Localize("$captainvalheim_compendium_group_active"), 
				ShieldTechniqueGroup.DefensiveTechniques => CaptainValheimLocalization.Localize("$captainvalheim_compendium_group_defensive"), 
				_ => "", 
			};
		}

		private static void AttachBodyIcon(RectTransform content, RectTransform textArea, TMP_CharacterInfo marker, Sprite? sprite, int index)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: 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)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)content == (Object)null) && !((Object)(object)textArea == (Object)null) && !((Object)(object)sprite == (Object)null))
			{
				GameObject val = new GameObject(string.Format("{0}{1}", "CaptainValheim_CompendiumTechniqueIcon_", index), new Type[3]
				{
					typeof(RectTransform),
					typeof(Image),
					typeof(LayoutElement)
				})
				{
					layer = ((Component)textArea).gameObject.layer
				};
				RectTransform val2 = (RectTransform)val.transform;
				((Transform)val2).SetParent((Transform)(object)content, false);
				val2.anchorMin = content.pivot;
				val2.anchorMax = content.pivot;
				val2.pivot = new Vector2(0.5f, 0.5f);
				val2.sizeDelta = new Vector2(18f, 18f);
				Vector3 val3 = (marker.bottomLeft + marker.topLeft) * 0.5f;
				val3.x += 14f;
				Vector3 val4 = ((Transform)textArea).TransformPoint(val3);
				Vector3 val5 = ((Transform)content).InverseTransformPoint(val4);
				val2.anchoredPosition = new Vector2(val5.x, val5.y);
				Image component = val.GetComponent<Image>();
				component.sprite = sprite;
				component.preserveAspect = true;
				((Graphic)component).raycastTarget = false;
				val.GetComponent<LayoutElement>().ignoreLayout = true;
				BodyIcons.Add(val);
			}
		}

		private static void DestroyTrackedIcons()
		{
			foreach (GameObject bodyIcon in BodyIcons)
			{
				if (!((Object)(object)bodyIcon == (Object)null))
				{
					bodyIcon.SetActive(false);
					bodyIcon.transform.SetParent((Transform)null, false);
					Object.Destroy((Object)(object)bodyIcon);
				}
			}
			BodyIcons.Clear();
		}

		private static void ClearIconChildren(Transform parent)
		{
			for (int num = parent.childCount - 1; num >= 0; num--)
			{
				Transform child = parent.GetChild(num);
				if (((Object)child).name.StartsWith("CaptainValheim_CompendiumTechniqueIcon_", StringComparison.Ordinal))
				{
					((Component)child).gameObject.SetActive(false);
					child.SetParent((Transform)null, false);
					Object.Destroy((Object)(object)((Component)child).gameObject);
				}
			}
		}

		private static void TrackDialog(TextsDialog dialog)
		{
			PageDialogs.RemoveAll((TextsDialog trackedDialog) => (Object)(object)trackedDialog == (Object)null);
			if (!PageDialogs.Contains(dialog))
			{
				PageDialogs.Add(dialog);
			}
		}

		private static Sprite? ResolveIcon(ShieldTechniqueInfo entry)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject obj = ObjectDB.instance.GetItemPrefab(entry.IconPrefabName) ?? ObjectDB.instance.GetItemPrefab("ShieldWood");
			Sprite[] array = ((obj != null) ? obj.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_icons;
			if (array == null || array.Length <= 0)
			{
				return null;
			}
			return array[0];
		}

		private static bool IsTechniquePage(string? topic)
		{
			return string.Equals(topic, "$captainvalheim_compendium_title", StringComparison.Ordinal);
		}
	}
	[HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")]
	internal static class TextsDialogUpdateTextsListShieldTechniquePatch
	{
		private static void Postfix(TextsDialog __instance)
		{
			ShieldTechniqueCompendiumManager.AddTechniquePage(__instance);
		}
	}
	[HarmonyPatch(typeof(TextsDialog), "ShowText", new Type[] { typeof(TextInfo) })]
	internal static class TextsDialogShowTextShieldTechniquePatch
	{
		private static void Postfix(TextsDialog __instance, TextInfo text)
		{
			ShieldTechniqueCompendiumManager.RefreshPageContentIcons(__instance, text);
		}
	}
	[HarmonyPatch(typeof(TextsDialog), "OnClose")]
	internal static class TextsDialogOnCloseShieldTechniquePatch
	{
		private static void Postfix(TextsDialog __instance)
		{
			ShieldTechniqueCompendiumManager.ClearPageContentIcons(__instance);
		}
	}
	internal static class ShieldTooltipSystem
	{
		internal static void AppendShieldGuidance(ItemData? item, ref string tooltip)
		{
			if (CaptainValheimPlugin.Settings.General.ShowShieldTooltip.Value == CaptainValheimPlugin.Toggle.On && HasEnabledShieldFeature(item))
			{
				string text = CaptainValheimLocalization.Text("captainvalheim_shield_tooltip");
				string text2 = CaptainValheimLocalization.Text("captainvalheim_shield_tooltip_note");
				string text3 = text + "\n" + text2;
				tooltip = (string.IsNullOrWhiteSpace(tooltip) ? text3 : (tooltip + "\n\n" + text3));
			}
		}

		private static bool HasEnabledShieldFeature(ItemData? item)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			if (item == null || (int)(item.m_shared?.m_itemType).GetValueOrDefault() != 5 || !ShieldRuntimeSystem.TryGetDefinition(item, out SecondaryAttackDefinition definition))
			{
				return false;
			}
			ShieldSpecialSecondaryBehavior shieldSpecial = definition.ShieldSpecial;
			if (!definition.ShieldProjectileReflect && !definition.ShieldBlockCharge && (shieldSpecial == null || !shieldSpecial.HasShieldPrimaryAttack) && (shieldSpecial == null || !shieldSpecial.HasShieldThrow))
			{
				if (shieldSpecial != null && shieldSpecial.HasShieldCharge)
				{
					return shieldSpecial.ShieldChargeDistance > 0f;
				}
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class ItemDataGetTooltipShieldGuidancePatch
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			ShieldTooltipSystem.AppendShieldGuidance(item, ref __result);
		}
	}
	[BepInPlugin("sighsorry.CaptainValheim", "CaptainValheim", "1.1.0")]
	public class CaptainValheimPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		internal sealed class PluginSettings
		{
			internal GeneralSettings General { get; } = new GeneralSettings();

			internal WeaponCompatibilitySettings WeaponCompatibility { get; } = new WeaponCompatibilitySettings();

			internal void Bind(CaptainValheimPlugin plugin)
			{
				General.Bind(plugin);
				WeaponCompatibility.Bind(plugin);
			}
		}

		internal sealed class WeaponCompatibilitySettings
		{
			internal ConfigEntry<Toggle> AllowCharge;

			internal ConfigEntry<Toggle> AllowBlockCharge;

			internal ConfigEntry<Toggle> AllowReflection;

			internal void Bind(CaptainValheimPlugin plugin)
			{
				AllowCharge = plugin.config("2 - Shield Actions With One-Handed Weapon", "Allow Shield Charge With One-Handed Weapon", Toggle.Off, "Allows Block + Secondary Attack to use shield charge while holding a one-handed melee weapon. Failed charges do not fall back to a weapon attack. The shield's YAML charge settings still apply.");
				AllowBlockCharge = plugin.config("2 - Shield Actions With One-Handed Weapon", "Allow Block Charge With One-Handed Weapon", Toggle.On, "Allows shield block-charge accumulation and counterattacks while holding a one-handed melee weapon. If off, these require an empty right hand, and entering disallowed equipment clears stored charges. The shield's YAML blockCharge settings still apply.");
				AllowReflection = plugin.config("2 - Shield Actions With One-Handed Weapon", "Allow Projectile Reflection With One-Handed Weapon", Toggle.On, "Allows shield projectile reflection while holding a one-handed melee weapon. If off, reflection requires an empty right hand. The shield's YAML reflect settings still apply.");
			}
		}

		internal sealed class GeneralSettings
		{
			internal ConfigEntry<Toggle> LockConfiguration;

			internal ConfigEntry<Toggle> ShowShieldTooltip;

			internal ConfigEntry<Toggle> PreventWeaponAutoEquip;

			internal void Bind(CaptainValheimPlugin plugin)
			{
				LockConfiguration = plugin.config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.");
				ShowShieldTooltip = plugin.config("1 - General", "Show Shield Tooltip", Toggle.On, "Shows CaptainValheim guidance on shield item tooltips.", synchronizedSetting: false);
				PreventWeaponAutoEquip = plugin.config("1 - General", "Prevent Weapon Auto Equip While Shield Only", Toggle.On, "Keeps automatically picked up weapons in the inventory while using only a shield, including while waiting for a thrown shield to return. Identified weapons thrown by you retain the game's normal re-equip checks. Manual pickup and equipment choices are unchanged.", synchronizedSetting: false);
			}
		}

		internal const string ModName = "CaptainValheim";

		internal const string ModVersion = "1.1.0";

		internal const string Author = "sighsorry";

		private const string ModGUID = "sighsorry.CaptainValheim";

		private static string ConfigFileName = "sighsorry.CaptainValheim.cfg";

		private static string ConfigFileFullPath = Paths.ConfigPath + Path.DirectorySeparatorChar + ConfigFileName;

		private readonly Harmony _harmony = new Harmony("sighsorry.CaptainValheim");

		public static readonly ManualLogSource ModLogger = Logger.CreateLogSource("CaptainValheim");

		internal static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.CaptainValheim")
		{
			DisplayName = "CaptainValheim",
			CurrentVersion = "1.1.0",
			MinimumRequiredVersion = "1.1.0"
		};

		private FileSystemWatcher? _watcher;

		private readonly object _reloadLock = new object();

		private DateTime _configReloadDueUtc;

		private bool _hasPendingConfigReload;

		private string? _lastConfigFileText;

		private static readonly TimeSpan ConfigReloadDelay = TimeSpan.FromSeconds(1.0);

		private static ConfigEntry<Toggle> _serverConfigLocked = null;

		internal static PluginSettings Settings { get; } = new PluginSettings();

		public void Awake()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			Settings.Bind(this);
			_serverConfigLocked = Settings.General.LockConfiguration;
			ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked);
			PatchCaptainValheimHooks();
			CaptainValheimLocalization.Load();
			SecondaryAttackFacade.Initialize();
			SetupWatcher();
			((BaseUnityPlugin)this).Config.Save();
			_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void Update()
		{
			TryReloadConfigValues();
			SecondaryAttackFacade.TryApplyPendingConfig();
		}

		private void OnDestroy()
		{
			RunCleanup("secondary attack state", SecondaryAttackFacade.Dispose);
			RunCleanup("configuration save", delegate
			{
				SaveWithRespectToConfigSet();
			});
			RunCleanup("configuration watcher", DisposeWatcher);
			RunCleanup("shield key hints", ShieldOnlyKeyHintSystem.Dispose);
			RunCleanup("shield compendium", ShieldTechniqueCompendiumManager.Dispose);
			RunCleanup("pickup scopes", ShieldPickupPolicy.Reset);
			RunCleanup("Harmony patches", (Action)_harmony.UnpatchSelf);
		}

		private static void RunCleanup(string operation, Action cleanup)
		{
			try
			{
				cleanup();
			}
			catch (Exception ex)
			{
				ModLogger.LogError((object)("Failed to clean up " + operation + ": " + ex.Message));
			}
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void DisposeWatcher()
		{
			FileSystemWatcher watcher = _watcher;
			_watcher = null;
			if (watcher == null)
			{
				return;
			}
			watcher.EnableRaisingEvents = false;
			watcher.Changed -= ReadConfigValues;
			watcher.Created -= ReadConfigValues;
			watcher.Renamed -= ReadConfigValues;
			watcher.Dispose();
			lock (_reloadLock)
			{
				_hasPendingConfigReload = false;
				_configReloadDueUtc = default(DateTime);
			}
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			lock (_reloadLock)
			{
				_hasPendingConfigReload = true;
				_configReloadDueUtc = DateTime.UtcNow + ConfigReloadDelay;
			}
		}

		private void TryReloadConfigValues()
		{
			lock (_reloadLock)
			{
				if (!_hasPendingConfigReload || DateTime.UtcNow < _configReloadDueUtc)
				{
					return;
				}
				_hasPendingConfigReload = false;
				if (!File.Exists(ConfigFileFullPath))
				{
					ModLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					string b = File.ReadAllText(ConfigFileFullPath);
					if (!string.Equals(_lastConfigFileText, b, StringComparison.Ordinal))
					{
						SaveWithRespectToConfigSet(reload: true);
						_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
						ModLogger.LogInfo((object)"Configuration reload complete.");
					}
				}
				catch (Exception ex)
				{
					ModLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
		}

		private static string? ReadFileTextIfExists(string path)
		{
			if (!File.Exists(path))
			{
				return null;
			}
			return File.ReadAllText(path);
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				if (reload)
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
				((BaseUnityPlugin)this).Config.Save();
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void PatchCaptainValheimHooks()
		{
			_harmony.PatchAll(typeof(CaptainValheimPlugin).Assembly);
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}
	}
	internal static class ProjectileAccess
	{
		private static readonly FieldInfo? AmmoField = AccessTools.Field(typeof(Projectile), "m_ammo");

		private static readonly FieldInfo? OriginalHitDataField = AccessTools.Field(typeof(Projectile), "m_originalHitData");

		private static readonly FieldInfo? StatusEffectHashField = AccessTools.Field(typeof(Projectile), "m_statusEffectHash");

		private static readonly FieldInfo? VelocityField = AccessTools.Field(typeof(Projectile), "m_vel");

		internal static ItemData? GetAmmo(Projectile projectile)
		{
			object? obj = AmmoField?.GetValue(projectile);
			return (ItemData?)((obj is ItemData) ? obj : null);
		}

		internal static HitData? GetOriginalHitData(Projectile projectile)
		{
			object? obj = OriginalHitDataField?.GetValue(projectile);
			return (HitData?)((obj is HitData) ? obj : null);
		}

		internal static int GetStatusEffectHash(Projectile projectile)
		{
			object obj = StatusEffectHashField?.GetValue(projectile);
			if (obj is int)
			{
				return (int)obj;
			}
			return 0;
		}

		internal static Vector3 GetVelocity(Projectile projectile)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			object obj = VelocityField?.GetValue(projectile);
			if (obj is Vector3)
			{
				return (Vector3)obj;
			}
			return Vector3.zero;
		}

		internal static Character? GetHitCharacter(Collider collider)
		{
			if (!((Object)(object)collider != (Object)null))
			{
				return null;
			}
			GameObject obj = Projectile.FindHitObject(collider);
			if (obj == null)
			{
				return null;
			}
			return obj.GetComponent<Character>();
		}
	}
	internal static class GameAccess
	{
		private delegate void ProjectileSpawnPoint(Attack attack, out Vector3 point, out Vector3 direction);

		private static readonly FieldRef<Attack, Humanoid> AttackCharacter = AccessTools.FieldRefAccess<Attack, Humanoid>("m_character");

		private static readonly FieldRef<Attack, ItemData> AttackWeapon = AccessTools.FieldRefAccess<Attack, ItemData>("m_weapon");

		private static readonly FieldRef<Attack, BaseAI> AttackBaseAI = AccessTools.FieldRefAccess<Attack, BaseAI>("m_baseAI");

		internal static readonly FieldRef<Humanoid, Attack> CurrentAttack = AccessTools.FieldRefAccess<Humanoid, Attack>("m_currentAttack");

		internal static readonly FieldRef<Humanoid, ItemData> HiddenLeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_hiddenLeftItem");

		internal static readonly FieldRef<Humanoid, ItemData> HiddenRightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_hiddenRightItem");

		internal static readonly FieldRef<Humanoid, int> BlockCharges = AccessTools.FieldRefAccess<Humanoid, int>("m_blockCharges");

		internal static readonly FieldRef<Humanoid, float> BlockChargeRemoveTimer = AccessTools.FieldRefAccess<Humanoid, float>("m_blockChargeRemoveTimer");

		internal static readonly FieldRef<Character, bool> SecondaryAttackPressed = AccessTools.FieldRefAccess<Character, bool>("m_secondaryAttack");

		internal static readonly FieldRef<Character, bool> SecondaryAttackHeld = AccessTools.FieldRefAccess<Character, bool>("m_secondaryAttackHold");

		internal static readonly FieldRef<Character, bool> BlockingInput = AccessTools.FieldRefAccess<Character, bool>("m_blocking");

		internal static readonly FieldRef<Player, float> QueuedSecondaryAttack = AccessTools.FieldRefAccess<Player, float>("m_queuedSecondAttackTimer");

		internal static readonly FieldRef<KeyHints, bool> KeyHintsEnabled = AccessTools.FieldRefAccess<KeyHints, bool>("m_keyHintsEnabled");

		internal static readonly FieldRef<TextsDialog, List<TextInfo>> Texts = AccessTools.FieldRefAccess<TextsDialog, List<TextInfo>>("m_texts");

		internal static readonly FieldRef<Projectile, bool> ChangedVisual = AccessTools.FieldRefAccess<Projectile, bool>("m_changedVisual");

		private static readonly ProjectileSpawnPoint SpawnPoint = AccessTools.MethodDelegate<ProjectileSpawnPoint>(AccessTools.DeclaredMethod(typeof(Attack), "GetProjectileSpawnPoint", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Attack, float> AttackStamina = AccessTools.MethodDelegate<Func<Attack, float>>(AccessTools.DeclaredMethod(typeof(Attack), "GetAttackStamina", (Type[])null, (Type[])null), (object)null, true);

		internal static readonly Func<Character, Collider, short> FindWeakSpotIndex = AccessTools.MethodDelegate<Func<Character, Collider, short>>(AccessTools.DeclaredMethod(typeof(Character), "FindWeakSpotIndex", (Type[])null, (Type[])null), (object)null, true);

		internal static readonly Action<Localization, string, string> AddWord = AccessTools.MethodDelegate<Action<Localization, string, string>>(AccessTools.DeclaredMethod(typeof(Localization), "AddWord", (Type[])null, (Type[])null), (object)null, true);

		internal static readonly Action<Projectile> UpdateVisual = AccessTools.MethodDelegate<Action<Projectile>>(AccessTools.DeclaredMethod(typeof(Projectile), "UpdateVisual", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Type EquipmentVisualType = typeof(ItemDrop).Assembly.GetType("IEquipmentVisual", throwOnError: true);

		private static readonly MethodInfo EquipmentVisualSetup = EquipmentVisualType.GetMethod("Setup");

		internal static Humanoid GetCharacter(this Attack attack)
		{
			return AttackCharacter.Invoke(attack);
		}

		internal static BaseAI GetAttackBaseAI(this Attack attack)
		{
			return AttackBaseAI.Invoke(attack);
		}

		internal static float GetAttackStamina(this Attack attack)
		{
			return AttackStamina(attack);
		}

		internal static void GetProjectileSpawnPoint(this Attack attack, out Vector3 point, out Vector3 direction)
		{
			SpawnPoint(attack, out point, out direction);
		}

		internal static void SetChargeContext(Attack attack, Humanoid character, ItemData weapon)
		{
			AttackCharacter.Invoke(attack) = character;
			AttackWeapon.Invoke(attack) = weapon;
		}

		internal static void SetupEquipmentVisual(GameObject visual, int variant)
		{
			Component componentInChildren = visual.GetComponentInChildren(EquipmentVisualType);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				EquipmentVisualSetup.Invoke(componentInChildren, new object[1] { variant });
			}
		}
	}
	internal sealed class SecondaryAttackCompiledSnapshot
	{
		public static readonly SecondaryAttackCompiledSnapshot Empty = new SecondaryAttackCompiledSnapshot(new Dictionary<string, NormalizedShieldModeConfig>(StringComparer.OrdinalIgnoreCase), null);

		public IReadOnlyDictionary<string, NormalizedShieldModeConfig> Shields { get; }

		public NormalizedShieldModeConfig? GlobalShieldFallback { get; }

		public SecondaryAttackCompiledSnapshot(IReadOnlyDictionary<string, NormalizedShieldModeConfig> shields, NormalizedShieldModeConfig? globalShieldFallback)
		{
			Shields = shields ?? throw new ArgumentNullException("shields");
			GlobalShieldFallback = globalShieldFallback;
		}
	}
	internal sealed class SecondaryAttackAppliedWorldSnapshot
	{
		public static readonly SecondaryAttackAppliedWorldSnapshot Empty = new SecondaryAttackAppliedWorldSnapshot(new Dictionary<string, SecondaryAttackDefinition>(StringComparer.OrdinalIgnoreCase));

		public IReadOnlyDictionary<string, SecondaryAttackDefinition> DefinitionsByPrefabName { get; }

		public SecondaryAttackAppliedWorldSnapshot(IReadOnlyDictionary<string, SecondaryAttackDefinition> definitionsByPrefabName)
		{
			DefinitionsByPrefabName = definitionsByPrefabName ?? throw new ArgumentNullException("definitionsByPrefabName");
		}
	}
	internal static class SecondaryAttackConfigLoader
	{
		private const string DefaultResourcePrefix = "CaptainValheim.Resources.Defaults.";

		private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();

		public static void EnsureLocalFileExists()
		{
			Directory.CreateDirectory(SecondaryAttackYamlConfig.DirectoryPath);
			if (!File.Exists(SecondaryAttackYamlConfig.FilePath))
			{
				File.WriteAllText(SecondaryAttackYamlConfig.FilePath, LoadEmbeddedDefault("CaptainValheim.yml"));
			}
		}

		public static string ReadLocalYamlText()
		{
			return File.ReadAllText(SecondaryAttackYamlConfig.FilePath);
		}

		public static bool TryCompileSnapshot(string yamlText, out SecondaryAttackCompiledSnapshot? snapshot)
		{
			snapshot = null;
			if (!TryParseDictionary(yamlText, out Dictionary<string, ShieldWeaponConfig> parsed))
			{
				return false;
			}
			if (!TryValidateFiniteValues(parsed, out string invalidValuePath))
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml: '" + invalidValuePath + "' must be a finite number."));
				return false;
			}
			try
			{
				snapshot = SecondaryAttackWeaponConfigNormalizer.Normalize(parsed);
				return true;
			}
			catch (Exception ex)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to compile CaptainValheim.yml: " + ex.Message));
				return false;
			}
		}

		private static bool TryParseDictionary(string yamlText, out Dictionary<string, ShieldWeaponConfig>? parsed)
		{
			parsed = null;
			if (string.IsNullOrWhiteSpace(yamlText))
			{
				CaptainValheimPlugin.ModLogger.LogError((object)"Failed to parse CaptainValheim.yml: the document cannot be empty.");
				return false;
			}
			try
			{
				parsed = new Dictionary<string, ShieldWeaponConfig>(StringComparer.OrdinalIgnoreCase);
				YamlStream yamlStream = new YamlStream();
				yamlStream.Load(new StringReader(yamlText));
				if (yamlStream.Documents.Count != 1 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode))
				{
					CaptainValheimPlugin.ModLogger.LogError((object)"Failed to parse CaptainValheim.yml: the document root must be a single mapping.");
					return false;
				}
				foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children)
				{
					string text = (child.Key as YamlScalarNode)?.Value?.Trim() ?? "";
					if (string.IsNullOrWhiteSpace(text))
					{
						CaptainValheimPlugin.ModLogger.LogError((object)"Failed to parse CaptainValheim.yml: every root block must have a non-empty scalar key.");
						return false;
					}
					if (!(child.Value is YamlMappingNode))
					{
						CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml block '" + text + "': the block must be a mapping."));
						return false;
					}
					if (parsed.ContainsKey(text))
					{
						CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml: duplicate root block '" + text + "'."));
						return false;
					}
					try
					{
						ShieldWeaponConfig shieldWeaponConfig = DeserializeYamlNode(child.Value);
						if (shieldWeaponConfig == null)
						{
							CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml block '" + text + "': the block is empty or invalid."));
							return false;
						}
						parsed.Add(text, shieldWeaponConfig);
					}
					catch (Exception ex)
					{
						CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml block '" + text + "': " + ex.Message));
						return false;
					}
				}
				return true;
			}
			catch (Exception ex2)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml: " + ex2.Message));
				return false;
			}
		}

		private static ShieldWeaponConfig? DeserializeYamlNode(YamlNode node)
		{
			using StringWriter stringWriter = new StringWriter();
			new YamlStream(new YamlDocument(node)).Save(stringWriter, assignAnchors: false);
			return Deserializer.Deserialize<ShieldWeaponConfig>(stringWriter.ToString());
		}

		private static bool TryValidateFiniteValues(IReadOnlyDictionary<string, ShieldWeaponConfig> shields, out string invalidValuePath)
		{
			foreach (var (text2, shieldWeaponConfig2) in shields)
			{
				if (TryFindNonFiniteValue(out invalidValuePath, (text2 + ".primaryAttack.damageFactor", shieldWeaponConfig2.PrimaryAttack?.DamageFactor), (text2 + ".primaryAttack.pushFactor", shieldWeaponConfig2.PrimaryAttack?.PushFactor), (text2 + ".primaryAttack.staminaFactor", shieldWeaponConfig2.PrimaryAttack?.StaminaFactor), (text2 + ".primaryAttack.durabilityFactor", shieldWeaponConfig2.PrimaryAttack?.DurabilityFactor), (text2 + ".throw.damageFactor", shieldWeaponConfig2.Throw?.DamageFactor), (text2 + ".throw.pushFactor", shieldWeaponConfig2.Throw?.PushFactor), (text2 + ".throw.staminaFactor", shieldWeaponConfig2.Throw?.StaminaFactor), (text2 + ".throw.durabilityFactor", shieldWeaponConfig2.Throw?.DurabilityFactor), (text2 + ".throw.damageDecay", shieldWeaponConfig2.Throw?.DamageDecay), (text2 + ".throw.radiusFactor", shieldWeaponConfig2.Throw?.RadiusFactor), (text2 + ".throw.ttlFactor", shieldWeaponConfig2.Throw?.TtlFactor), (text2 + ".charge.damageFactor", shieldWeaponConfig2.Charge?.DamageFactor), (text2 + ".charge.pushFactor", shieldWeaponConfig2.Charge?.PushFactor), (text2 + ".charge.staminaFactor", shieldWeaponConfig2.Charge?.StaminaFactor), (text2 + ".charge.distance", shieldWeaponConfig2.Charge?.Distance), (text2 + ".charge.speed", shieldWeaponConfig2.Charge?.Speed), (text2 + ".charge.cooldown", shieldWeaponConfig2.Charge?.Cooldown), (text2 + ".charge.cooldownReductionFactor", shieldWeaponConfig2.Charge?.CooldownReductionFactor), (text2 + ".charge.durabilityFactor", shieldWeaponConfig2.Charge?.DurabilityFactor), (text2 + ".charge.hitRadiusFactor", shieldWeaponConfig2.Charge?.HitRadiusFactor), (text2 + ".reflect.staminaFactor", shieldWeaponConfig2.Reflect?.StaminaFactor), (text2 + ".reflect.reflectionFactor", shieldWeaponConfig2.Reflect?.ReflectionFactor), (text2 + ".blockCharge.decayTime", shieldWeaponConfig2.BlockCharge?.DecayTime), (text2 + ".blockCharge.blockingDecayFactor", shieldWeaponConfig2.BlockCharge?.BlockingDecayFactor)))
				{
					return false;
				}
			}
			invalidValuePath = string.Empty;
			return true;
		}

		private static bool TryFindNonFiniteValue(out string invalidValuePath, params (string Path, float? Value)[] values)
		{
			for (int i = 0; i < values.Length; i++)
			{
				var (text, num) = values[i];
				if (num.HasValue && (float.IsNaN(num.Value) || float.IsInfinity(num.Value)))
				{
					invalidValuePath = text;
					return true;
				}
			}
			invalidValuePath = string.Empty;
			return false;
		}

		private static string LoadEmbeddedDefault(string fileName)
		{
			string text = "CaptainValheim.Resources.Defaults." + fileName;
			using Stream stream = typeof(SecondaryAttackConfigLoader).Assembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				throw new InvalidOperationException("Embedded default YAML resource '" + text + "' was not found.");
			}
			using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
			return streamReader.ReadToEnd();
		}
	}
	internal static class SecondaryAttackYamlConfig
	{
		internal const string FileName = "CaptainValheim.yml";

		internal const string SyncedIdentifier = "captain_valheim_yaml";

		internal const long ReloadDelayTicks = 10000000L;

		internal static readonly string DirectoryPath = Paths.ConfigPath;

		internal static readonly string FilePath = Path.Combine(DirectoryPath, "CaptainValheim.yml");
	}
	internal static class SecondaryAttackWeaponConfigNormalizer
	{
		private const string GlobalFallbackKey = "Global";

		internal static SecondaryAttackCompiledSnapshot Normalize(IReadOnlyDictionary<string, ShieldWeaponConfig> shields)
		{
			Dictionary<string, NormalizedShieldModeConfig> dictionary = new Dictionary<string, NormalizedShieldModeConfig>(StringComparer.OrdinalIgnoreCase);
			NormalizedShieldModeConfig normalizedShieldModeConfig = CreateGlobalDefaultShieldFallback();
			ShieldWeaponConfig shieldWeaponConfig = null;
			string key;
			ShieldWeaponConfig value;
			foreach (KeyValuePair<string, ShieldWeaponConfig> shield in shields)
			{
				shield.Deconstruct(out key, out value);
				string text = key;
				ShieldWeaponConfig shieldWeaponConfig2 = value;
				if (!string.IsNullOrWhiteSpace(text) && shieldWeaponConfig2 != null && text.Trim().Equals("Global", StringComparison.OrdinalIgnoreCase))
				{
					shieldWeaponConfig = shieldWeaponConfig2;
					break;
				}
			}
			NormalizedShieldModeConfig normalizedShieldModeConfig2 = ((shieldWeaponConfig != null) ? FromShieldRaw(shieldWeaponConfig, normalizedShieldModeConfig) : normalizedShieldModeConfig);
			foreach (KeyValuePair<string, ShieldWeaponConfig> shield2 in shields)
			{
				shield2.Deconstruct(out key, out value);
				string text2 = key;
				ShieldWeaponConfig shieldWeaponConfig3 = value;
				if (!string.IsNullOrWhiteSpace(text2) && shieldWeaponConfig3 != null)
				{
					string text3 = text2.Trim();
					if (!text3.Equals("Global", StringComparison.OrdinalIgnoreCase))
					{
						dictionary[text3] = FromShieldRaw(shieldWeaponConfig3, normalizedShieldModeConfig2);
					}
				}
			}
			return new SecondaryAttackCompiledSnapshot(dictionary, normalizedShieldModeConfig2);
		}

		internal static NormalizedShieldModeConfig FromShieldRaw(ShieldWeaponConfig raw, NormalizedShieldModeConfig? fallback = null)
		{
			if (fallback == null)
			{
				fallback = CreateGlobalDefaultShieldFallback();
			}
			return NormalizeShield(raw, fallback);
		}

		internal static NormalizedShieldModeConfig CreateGlobalDefaultShieldFallback()
		{
			return new NormalizedShieldModeConfig
			{
				PrimaryAttack = new NormalizedShieldPrimaryAttackConfig(),
				Throw = new NormalizedShieldThrowConfig(),
				Charge = new NormalizedShieldChargeConfig(),
				Reflect = new NormalizedShieldReflectConfig(),
				BlockCharge = new NormalizedShieldBlockChargeConfig()
			};
		}

		private static NormalizedShieldModeConfig NormalizeShield(ShieldWeaponConfig rawShield, NormalizedShieldModeConfig fallback)
		{
			return new NormalizedShieldModeConfig
			{
				PrimaryAttack = NormalizeShieldPrimaryAttack(rawShield.PrimaryAttack, fallback.PrimaryAttack),
				Throw = NormalizeShieldThrow(rawShield.Throw, fallback.Throw),
				Charge = NormalizeShieldCharge(rawShield.Charge, fallback.Charge),
				Reflect = NormalizeShieldReflect(rawShield.Reflect, fallback.Reflect),
				BlockCharge = NormalizeShieldBlockCharge(rawShield.BlockCharge, fallback.BlockCharge)
			};
		}

		private static NormalizedShieldPrimaryAttackConfig? NormalizeShieldPrimaryAttack(ShieldPrimaryAttackConfig? rawPrimaryAttack, NormalizedShieldPrimaryAttackConfig? fallback)
		{
			if (rawPrimaryAttack == null)
			{
				return fallback;
			}
			if (rawPrimaryAttack.Enabled == false)
			{
				return null;
			}
			NormalizedShieldPrimaryAttackConfig normalizedShieldPrimaryAttackConfig = fallback ?? new NormalizedShieldPrimaryAttackConfig();
			return new NormalizedShieldPrimaryAttackConfig
			{
				DamageFactor = (rawPrimaryAttack.DamageFactor ?? normalizedShieldPrimaryAttackConfig.DamageFactor),
				PushFactor = (rawPrimaryAttack.PushFactor ?? normalizedShieldPrimaryAttackConfig.PushFactor),
				StaminaFactor = (rawPrimaryAttack.StaminaFactor ?? normalizedShieldPrimaryAttackConfig.StaminaFactor),
				DurabilityFactor = (rawPrimaryAttack.DurabilityFactor ?? normalizedShieldPrimaryAttackConfig.DurabilityFactor)
			};
		}

		private static NormalizedShieldThrowConfig? NormalizeShieldThrow(ShieldThrowConfig? rawThrow, NormalizedShieldThrowConfig? fallback)
		{
			if (rawThrow == null)
			{
				return fallback;
			}
			if (rawThrow.Enabled == false)
			{
				return null;
			}
			NormalizedShieldThrowConfig normalizedShieldThrowConfig = fallback ?? new NormalizedShieldThrowConfig();
			return new NormalizedShieldThrowConfig
			{
				Animation = ((!string.IsNullOrWhiteSpace(rawThrow.Animation)) ? rawThrow.Animation.Trim() : normalizedShieldThrowConfig.Animation),
				DamageFactor = (rawThrow.DamageFactor ?? normalizedShieldThrowConfig.DamageFactor),
				PushFactor = (rawThrow.PushFactor ?? normalizedShieldThrowConfig.PushFactor),
				StaminaFactor = (rawThrow.StaminaFactor ?? normalizedShieldThrowConfig.StaminaFactor),
				DurabilityFactor = (rawThrow.DurabilityFactor ?? normalizedShieldThrowConfig.DurabilityFactor),
				Targets = (rawThrow.Targets ?? normalizedShieldThrowConfig.Targets),
				DamageDecay = (rawThrow.DamageDecay ?? normalizedShieldThrowConfig.DamageDecay),
				RadiusFactor = (rawThrow.RadiusFactor ?? normalizedShieldThrowConfig.RadiusFactor),
				TtlFactor = (rawThrow.TtlFactor ?? normalizedShieldThrowConfig.TtlFactor)
			};
		}

		private static NormalizedShieldChargeConfig? NormalizeShieldCharge(ShieldChargeConfig? rawCharge, NormalizedShieldChargeConfig? fallback)
		{
			if (rawCharge == null)
			{
				return fallback;
			}
			if (rawCharge.Enabled == false)
			{
				return null;
			}
			NormalizedShieldChargeConfig normalizedShieldChargeConfig = fallback ?? new NormalizedShieldChargeConfig();
			return new NormalizedShieldChargeConfig
			{
				DamageFactor = (rawCharge.DamageFactor ?? normalizedShieldChargeConfig.DamageFactor),
				PushFactor = (rawCharge.PushFactor ?? normalizedShieldChargeConfig.PushFactor),
				StaminaFactor = (rawCharge.StaminaFactor ?? normalizedShieldChargeConfig.StaminaFactor),
				Distance = (rawCharge.Distance ?? normalizedShieldChargeConfig.Distance),
				Speed = (rawCharge.Speed ?? normalizedShieldChargeConfig.Speed),
				Cooldown = (rawCharge.Cooldown ?? normalizedShieldChargeConfig.Cooldown),
				CooldownReductionFactor = (rawCharge.CooldownReductionFactor ?? normalizedShieldChargeConfig.CooldownReductionFactor),
				DurabilityFactor = (rawCharge.DurabilityFactor ?? normalizedShieldChargeConfig.DurabilityFactor),
				HitRadiusFactor = (rawCharge.HitRadiusFactor ?? normalizedShieldChargeConfig.HitRadiusFactor)
			};
		}

		private static NormalizedShieldReflectConfig? NormalizeShieldReflect(ShieldReflectConfig? rawReflect, NormalizedShieldReflectConfig? fallback)
		{
			if (rawReflect == null)
			{
				return fallback;
			}
			if (rawReflect.Enabled == false)
			{
				return null;
			}
			NormalizedShieldReflectConfig normalizedShieldReflectConfig = fallback ?? new NormalizedShieldReflectConfig();
			return new NormalizedShieldReflectConfig
			{
				StaminaFactor = (rawReflect.StaminaFactor ?? normalizedShieldReflectConfig.StaminaFactor),
				ReflectionFactor = (rawReflect.ReflectionFactor ?? normalizedShieldReflectConfig.ReflectionFactor)
			};
		}

		private static NormalizedShieldBlockChargeConfig? NormalizeShieldBlockCharge(ShieldBlockChargeConfig? rawBlockCharge, NormalizedShieldBlockChargeConfig? fallback)
		{
			if (rawBlockCharge == null)
			{
				return fallback;
			}
			if (rawBlockCharge.Enabled == false)
			{
				return null;
			}
			return new NormalizedShieldBlockChargeConfig
			{
				ChargeCount = (rawBlockCharge.ChargeCount ?? fallback?.ChargeCount),
				DecayTime = (rawBlockCharge.DecayTime ?? fallback?.DecayTime),
				BlockingDecayFactor = (rawBlockCharge.BlockingDecayFactor ?? fallback?.BlockingDecayFactor)
			};
		}
	}
	internal static class SecondaryAttackFacade
	{
		private enum YamlAuthorityMode
		{
			LocalFiles,
			SyncedOnly
		}

		private enum PendingCommitResult
		{
			None,
			Deferred,
			Committed,
			Failed
		}

		private static readonly object ReloadLock = new object();

		private static FileSystemWatcher? _watcher;

		private static CustomSyncedValue<string>? _syncedYamlValue;

		private static SecondaryAttackCompiledSnapshot _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty;

		private static SecondaryAttackCompiledSnapshot? _pendingCompiledSnapshot;

		private static SecondaryAttackAppliedWorldSnapshot _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty;

		private static DateTime _localYamlReloadDueUtc;

		private static bool _hasPendingLocalYamlReload;

		private static bool _suppressSyncedYamlChanged;

		private static YamlAuthorityMode _yamlAuthorityMode;

		private static string _currentYamlFingerprint = string.Empty;

		private static string? _pendingYamlFingerprint;

		private static string? _rejectedYamlFingerprint;

		private static string _lastObservedSyncedYamlFingerprint = string.Empty;

		private static bool _hasObservedSyncedYaml;

		internal static SecondaryAttackAppliedWorldSnapshot CurrentAppliedWorldSnapshot => _currentAppliedWorldSnapshot;

		public static void Initialize()
		{
			SecondaryAttackConfigLoader.EnsureLocalFileExists();
			InitializeSyncedYamlValue();
			RefreshYamlAuthorityMode(force: true);
		}

		public static void Dispose()
		{
			DisposeSyncedYamlValue();
			DisposeWatcher();
			ClearConfigState();
		}

		internal static void TryApplyPendingConfig()
		{
			RefreshYamlAuthorityMode();
			StageSyncedYamlIfReady();
			TryReloadDebouncedLocalYaml();
			CommitPendingConfig(force: false, ObjectDB.instance, emitMissingWarnings: true);
		}

		internal static void ApplyPendingConfigToObjectDb(ObjectDB objectDb, bool emitMissingWarnings)
		{
			try
			{
				RefreshYamlAuthorityMode();
				StageSyncedYamlIfReady();
				TryReloadDebouncedLocalYaml();
				PendingCommitResult pendingCommitResult = CommitPendingConfig(force: true, objectDb, emitMissingWarnings);
				if ((uint)pendingCommitResult <= 1u)
				{
					ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings);
				}
			}
			catch (Exception ex)
			{
				_currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty;
				StageCurrentConfigForRetry();
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to apply CaptainValheim config during ObjectDB initialization: " + ex.Message));
			}
		}

		private static void SetupWatcher()
		{
			if (_watcher == null)
			{
				Directory.CreateDirectory(SecondaryAttackYamlConfig.DirectoryPath);
				_watcher = new FileSystemWatcher(SecondaryAttackYamlConfig.DirectoryPath, "CaptainValheim.yml");
				_watcher.Changed += OnYamlFileChanged;
				_watcher.Created += OnYamlFileChanged;
				_watcher.Renamed += OnYamlFileChanged;
				_watcher.IncludeSubdirectories = false;
				_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
				_watcher.EnableRaisingEvents = true;
			}
		}

		private static void OnYamlFileChanged(object sender, FileSystemEventArgs e)
		{
			if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles)
			{
				return;
			}
			lock (ReloadLock)
			{
				_hasPendingLocalYamlReload = true;
				_localYamlReloadDueUtc = DateTime.UtcNow.AddTicks(10000000L);
			}
		}

		private static void TryReloadDebouncedLocalYaml()
		{
			if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles)
			{
				return;
			}
			lock (ReloadLock)
			{
				if (_hasPendingLocalYamlReload && !(DateTime.UtcNow < _localYamlReloadDueUtc))
				{
					_hasPendingLocalYamlReload = false;
					if (!ReloadLocalYaml())
					{
						_hasPendingLocalYamlReload = true;
						_localYamlReloadDueUtc = DateTime.UtcNow.AddTicks(10000000L);
					}
				}
			}
		}

		private static bool ReloadLocalYaml()
		{
			if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles)
			{
				return true;
			}
			string yamlText;
			try
			{
				SecondaryAttackConfigLoader.EnsureLocalFileExists();
				yamlText = SecondaryAttackConfigLoader.ReadLocalYamlText();
			}
			catch (Exception ex)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to read CaptainValheim.yml: " + ex.Message));
				return false;
			}
			ApplyYamlText(yamlText);
			return true;
		}

		private static void OnSyncedYamlChanged()
		{
			if (!_suppressSyncedYamlChanged)
			{
				StageSyncedYamlIfReady();
			}
		}

		private static void RefreshYamlAuthorityMode(bool force = false)
		{
			YamlAuthorityMode yamlAuthorityMode = DetermineYamlAuthorityMode();
			if (force || yamlAuthorityMode != _yamlAuthorityMode)
			{
				_yamlAuthorityMode = yamlAuthorityMode;
				switch (yamlAuthorityMode)
				{
				case YamlAuthorityMode.LocalFiles:
					_hasObservedSyncedYaml = false;
					_lastObservedSyncedYamlFingerprint = string.Empty;
					SetupWatcher();
					ReloadLocalYaml();
					CaptainValheimPlugin.ModLogger.LogInfo((object)"CaptainValheim YAML authority mode: LocalFiles.");
					break;
				case YamlAuthorityMode.SyncedOnly:
					_hasObservedSyncedYaml = false;
					_lastObservedSyncedYamlFingerprint = string.Empty;
					DisposeWatcher();
					ClearConfigState();
					StageSyncedYamlIfReady();
					CaptainValheimPlugin.ModLogger.LogInfo((object)"CaptainValheim YAML authority mode: SyncedOnly.");
					break;
				}
			}
		}

		private static void StageSyncedYamlIfReady()
		{
			if (_yamlAuthorityMode != YamlAuthorityMode.SyncedOnly || !CaptainValheimPlugin.ConfigSync.InitialSyncDone)
			{
				return;
			}
			string text = _syncedYamlValue?.Value ?? string.Empty;
			if (_hasObservedSyncedYaml && string.Equals(_lastObservedSyncedYamlFingerprint, text, StringComparison.Ordinal))
			{
				return;
			}
			_hasObservedSyncedYaml = true;
			_lastObservedSyncedYamlFingerprint = text;
			if (string.IsNullOrWhiteSpace(text))
			{
				if (_currentCompiledSnapshot != SecondaryAttackCompiledSnapshot.Empty || _pendingCompiledSnapshot != null)
				{
					Clear