Decompiled source of CaptainValheim v1.0.8

CaptainValheim.dll

Decompiled a month 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.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.0.8")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.8.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)
				{
					localization.AddWord(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);
				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, hasCharge: false, hasPrimaryAttack: false, hasThrow: false, "", "", "");

			private readonly string _language;

			private readonly bool _gamepad;

			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 hasCharge, bool hasPrimaryAttack, bool hasThrow, string attackKey, string blockKey, string secondaryAttackKey)
			{
				_language = language;
				_gamepad = gamepad;
				HasCharge = hasCharge;
				HasPrimaryAttack = hasPrimaryAttack;
				HasThrow = hasThrow;
				AttackKey = attackKey;
				BlockKey = blockKey;
				SecondaryAttackKey = secondaryAttackKey;
			}

			internal bool Equals(ShieldHintState other)
			{
				if (_gamepad == other._gamepad && 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;

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

		internal static void UpdateKeyHint(KeyHints hints)
		{
			if ((Object)(object)hints == (Object)null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!ShouldShowCustomCombatHints(hints, localPlayer))
			{
				HideHints();
				if ((Object)(object)hints.m_combatHints != (Object)null)
				{
					hints.m_combatHints.SetActive(false);
				}
				return;
			}
			if (!TryBuildHintState(localPlayer, out var state) || !state.HasRows)
			{
				HideHints();
				RememberHintState(ShieldHintState.Hidden);
				return;
			}
			if (_showingHints && _hasLastHintState && _lastHintState.Equals(state))
			{
				PrepareCombatHintGroup(hints);
				return;
			}
			BuildHintRows(state, ReusableRows);
			EnsureHints(hints, ReusableRows.Count);
			if (HintCells.Count == 0)
			{
				return;
			}
			PrepareCombatHintGroup(hints);
			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(state);
			_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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			state = ShieldHintState.Hidden;
			ItemData leftItem = ((Humanoid)player).GetLeftItem();
			if (((Humanoid)player).GetRightItem() == null && leftItem != null && (int)(leftItem.m_shared?.m_itemType).GetValueOrDefault() == 5 && ShieldRuntimeSystem.TryGetDefinition(leftItem, out SecondaryAttackDefinition definition))
			{
				ShieldSpecialSecondaryBehavior shieldSpecial = definition.ShieldSpecial;
				if (shieldSpecial != null)
				{
					Localization instance = Localization.instance;
					state = new ShieldHintState(((instance != null) ? instance.GetSelectedLanguage() : null) ?? "", ZInput.IsGamepadActive(), shieldSpecial.HasShieldCharge && shieldSpecial.ShieldChargeDistance > 0f, shieldSpecial.HasShieldPrimaryAttack, 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() && hints.m_keyHintsEnabled && !((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.IsTextPanelOpen)) && !PlayerCustomizaton.IsBarberGuiVisible())
			{
				return player.GetDoodadController() == null;
			}
			return false;
		}

		private static void PrepareCombatHintGroup(KeyHints hints)
		{
			if ((Object)(object)hints.m_combatHints != (Object)null)
			{
				hints.m_combatHints.SetActive(true);
			}
			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;
		}

		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
	{
		private static void Postfix(KeyHints __instance)
		{
			ShieldOnlyKeyHintSystem.UpdateKeyHint(__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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			if (dialog?.m_texts != null)
			{
				TrackDialog(dialog);
				dialog.m_texts.RemoveAll((TextInfo text) => IsTechniquePage(text?.m_topic));
				dialog.m_texts.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;
				}
				pageDialog.m_texts?.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 && HasEnabledShieldAttack(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 HasEnabledShieldAttack(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 ((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)
	})]
	internal static class ItemDataGetTooltipShieldGuidancePatch
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			ShieldTooltipSystem.AppendShieldGuidance(item, ref __result);
		}
	}
	[BepInPlugin("sighsorry.CaptainValheim", "CaptainValheim", "1.0.8")]
	public class CaptainValheimPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

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

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

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

			internal ConfigEntry<Toggle> ShowShieldTooltip;

			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);
			}
		}

		internal const string ModName = "CaptainValheim";

		internal const string ModVersion = "1.0.8";

		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.0.8",
			MinimumRequiredVersion = "1.0.8"
		};

		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("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 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)
				{
					ClearConfigState();
				}
			}
			else
			{
				ApplyYamlText(text);
			}
		}

		private static void ClearConfigState()
		{
			_pendingCompiledSnapshot = null;
			_pendingYamlFingerprint = null;
			_currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty;
			_currentYamlFingerprint = string.Empty;
			_rejectedYamlFingerprint = null;
			if (_yamlAuthorityMode != YamlAuthorityMode.SyncedOnly)
			{
				_hasObservedSyncedYaml = false;
				_lastObservedSyncedYamlFingerprint = string.Empty;
			}
			_currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty;
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				SecondaryAttackObjectDbStateStore.Restore(ObjectDB.instance);
				ShieldChargeCooldownStatusSystem.UnregisterStatusEffect(ObjectDB.instance);
			}
			ShieldRuntimeSystem.ResetTransientState();
		}

		private static YamlAuthorityMode DetermineYamlAuthorityMode()
		{
			if (!((Object)(object)ZNet.instance != (Object)null) || ZNet.instance.IsServer())
			{
				return YamlAuthorityMode.LocalFiles;
			}
			return YamlAuthorityMode.SyncedOnly;
		}

		private static void InitializeSyncedYamlValue()
		{
			DisposeSyncedYamlValue();
			_syncedYamlValue = new CustomSyncedValue<string>(CaptainValheimPlugin.ConfigSync, "captain_valheim_yaml", string.Empty);
			_syncedYamlValue.ValueChanged += OnSyncedYamlChanged;
		}

		private static void DisposeSyncedYamlValue()
		{
			if (_syncedYamlValue != null)
			{
				_syncedYamlValue.ValueChanged -= OnSyncedYamlChanged;
				_syncedYamlValue = null;
			}
		}

		private static void DisposeWatcher()
		{
			_watcher?.Dispose();
			_watcher = null;
			lock (ReloadLock)
			{
				_hasPendingLocalYamlReload = false;
				_localYamlReloadDueUtc = default(DateTime);
			}
		}

		private static bool ApplyYamlText(string yamlText)
		{
			string text = yamlText ?? string.Empty;
			if (string.Equals(_rejectedYamlFingerprint, text, StringComparison.Ordinal))
			{
				DiscardPendingConfig();
				return false;
			}
			if (_currentCompiledSnapshot != SecondaryAttackCompiledSnapshot.Empty && _currentAppliedWorldSnapshot != SecondaryAttackAppliedWorldSnapshot.Empty && string.Equals(_currentYamlFingerprint, text, StringComparison.Ordinal))
			{
				_rejectedYamlFingerprint = null;
				DiscardPendingConfig();
				return true;
			}
			if (_pendingCompiledSnapshot != null && string.Equals(_pendingYamlFingerprint, text, StringComparison.Ordinal))
			{
				return true;
			}
			if (!SecondaryAttackConfigLoader.TryCompileSnapshot(text, out SecondaryAttackCompiledSnapshot snapshot))
			{
				_rejectedYamlFingerprint = text;
				DiscardPendingConfig();
				return false;
			}
			_rejectedYamlFingerprint = null;
			StageConfig(snapshot, text);
			return true;
		}

		private static void StageConfig(SecondaryAttackCompiledSnapshot snapshot, string fingerprint)
		{
			_pendingCompiledSnapshot = snapshot;
			_pendingYamlFingerprint = fingerprint;
		}

		private static void StageCurrentConfigForRetry()
		{
			if (_pendingCompiledSnapshot == null)
			{
				SecondaryAttackCompiledSnapshot currentCompiledSnapshot = _currentCompiledSnapshot;
				string currentYamlFingerprint = _currentYamlFingerprint;
				_currentYamlFingerprint = string.Empty;
				if (currentCompiledSnapshot != SecondaryAttackCompiledSnapshot.Empty && !string.IsNullOrWhiteSpace(currentYamlFingerprint))
				{
					StageConfig(currentCompiledSnapshot, currentYamlFingerprint);
				}
			}
		}

		private static PendingCommitResult CommitPendingConfig(bool force, ObjectDB? objectDb, bool emitMissingWarnings)
		{
			if (_pendingCompiledSnapshot == null)
			{
				return PendingCommitResult.None;
			}
			if (HasPendingLocalYamlReload() || (Object)(object)objectDb == (Object)null || (!force && !CanApplyPendingConfigNow()))
			{
				return PendingCommitResult.Deferred;
			}
			SecondaryAttackCompiledSnapshot pendingCompiledSnapshot = _pendingCompiledSnapshot;
			string text = _pendingYamlFingerprint ?? string.Empty;
			SecondaryAttackAppliedWorldSnapshot currentAppliedWorldSnapshot;
			try
			{
				currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, pendingCompiledSnapshot, emitMissingWarnings);
			}
			catch (Exception ex)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to apply staged YAML config; keeping the last working config: " + ex.Message));
				DiscardPendingConfig();
				try
				{
					ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings: false);
				}
				catch (Exception ex2)
				{
					_currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty;
					StageCurrentConfigForRetry();
					CaptainValheimPlugin.ModLogger.LogError((object)("Failed to restore the last working YAML config: " + ex2.Message));
				}
				return PendingCommitResult.Failed;
			}
			_currentCompiledSnapshot = pendingCompiledSnapshot;
			_currentYamlFingerprint = text;
			_currentAppliedWorldSnapshot = currentAppliedWorldSnapshot;
			DiscardPendingConfig();
			PublishCommittedLocalYaml(text);
			CaptainValheimPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes.");
			return PendingCommitResult.Committed;
		}

		private static bool HasPendingLocalYamlReload()
		{
			if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles)
			{
				return false;
			}
			lock (ReloadLock)
			{
				return _hasPendingLocalYamlReload;
			}
		}

		private static void PublishCommittedLocalYaml(string yamlText)
		{
			if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles || _syncedYamlValue == null)
			{
				return;
			}
			_suppressSyncedYamlChanged = true;
			try
			{
				_syncedYamlValue.AssignLocalValue(yamlText);
			}
			catch (Exception ex)
			{
				CaptainValheimPlugin.ModLogger.LogError((object)("Failed to synchronize the committed YAML config: " + ex.Message));
			}
			finally
			{
				_suppressSyncedYamlChanged = false;
			}
		}

		private static void DiscardPendingConfig()
		{
			_pendingCompiledSnapshot = null;
			_pendingYamlFingerprint = null;
		}

		private static void ApplyCompiledSnapshotToObjectDb(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings)
		{
			_currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, compiledSnapshot, emitMissingWarnings);
		}

		private static bool CanApplyPendingConfigNow()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return true;
			}
			if (((Humanoid)localPlayer).m_currentAttack != null)
			{
				return false;
			}
			if (!ShieldRuntimeSystem.IsShieldChargeActive((Humanoid)(object)localPlayer))
			{
				return !SecondaryAttackManager.HasActiveAsyncSecondaryWorkForFacade((Character?)(object)localPlayer);
			}
			return false;
		}
	}
	internal static class SecondaryAttackDefinitionCompiler
	{
		internal static bool TryCreateDefinition(string prefabName, ItemDrop itemDrop, NormalizedShieldModeConfig shieldConfig, bool emitWarnings, out SecondaryAttackDefinition? definition)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			definition = null;
			SharedData val = itemDrop.m_itemData?.m_shared;
			if (val == null)
			{
				return false;
			}
			if ((int)val.m_itemType != 5)
			{
				if (emitWarnings && SecondaryAttackManager.TryMarkCompatibilityWarningReported("non_shield_prefab:" + prefabName))
				{
					CaptainValheimPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": CaptainValheim shield features can only be used on shield prefabs."));
				}
				return false;
			}
			int num;
			if (shieldConfig.PrimaryAttack == null && shieldConfig.Throw == null)
			{
				NormalizedShieldChargeConfig charge = shieldConfig.Charge;
				num = ((charge != null && charge.Distance > 0f) ? 1 : 0);
			}
			else
			{
				num = 1;
			}
			bool flag = (byte)num != 0;
			if (!flag && shieldConfig.Reflect == null && shieldConfig.BlockCharge == null)
			{
				return false;
			}
			definition = CreateDefinition(shieldConfig);
			if (flag)
			{
				definition.ShieldSpecial = CreateShieldSpecialBehavior(shieldConfig);
			}
			return true;
		}

		private static SecondaryAttackDefinition CreateDefinition(NormalizedShieldModeConfig shieldConfig)
		{
			return new SecondaryAttackDefinition
			{
				ShieldProjectileReflect = (shieldConfig.Reflect != null),
				ShieldProjectileReflectStaminaFactor = Mathf.Max(0f, shieldConfig.Reflect?.StaminaFactor ?? 1f),
				ShieldProjectileReflectionFactor = (shieldConfig.Reflect?.ReflectionFactor ?? 0f),
				ShieldBlockCharge = (shieldConfig.BlockCharge != null),
				ShieldBlockChargeCount = shieldConfig.BlockCharge?.ChargeCount,
				ShieldBlockChargeDecayTime = shieldConfig.BlockCharge?.DecayTime,
				ShieldBlockChargeBlockingDecayFactor = shieldConfig.BlockCharge?.BlockingDecayFactor
			};
		}

		private static ShieldSpecialSecondaryBehavior CreateShieldSpecialBehavior(NormalizedShieldModeConfig shieldConfig)
		{
			NormalizedShieldPrimaryAttackConfig primaryAttack = shieldConfig.PrimaryAttack;
			NormalizedShieldThrowConfig normalizedShieldThrowConfig = shieldConfig.Throw;
			NormalizedShieldChargeConfig charge = shieldConfig.Charge;
			bool flag = primaryAttack != null;
			bool flag2 = normalizedShieldThrowConfig != null;
			bool flag3 = charge != null && charge.Distance > 0f;
			return new ShieldSpecialSecondaryBehavior
			{
				HasShieldPrimaryAttack = flag,
				ShieldPrimaryAttackDamageFactor = (flag ? Mathf.Max(0f, primaryAttack.DamageFactor) : 0f),
				ShieldPrimaryAttackPushFactor = (flag ? Mathf.Max(0f, primaryAttack.PushFactor) : 0f),
				ShieldPrimaryAttackStaminaFactor = (flag ? Mathf.Max(0f, primaryAttack.StaminaFactor) : 0f),
				ShieldPrimaryAttackDurabilityFactor = (flag ? Mathf.Max(0f, primaryAttack.DurabilityFactor) : 1f),
				HasShieldThrow = flag2,
				ShieldThrowAnimation = (flag2 ? normalizedShieldThrowConfig.Animation : string.Empty),
				ShieldThrowTargets = (flag2 ? Mathf.Max(0, normalizedShieldThrowConfig.Targets) : 0),
				ShieldThrowDamageFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.DamageFactor) : 0f),
				ShieldThrowPushFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.PushFactor) : 0f),
				ShieldThrowStaminaFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.StaminaFactor) : 0f),
				ShieldThrowDurabilityFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.DurabilityFactor) : 1f),
				ShieldThrowDamageDecay = (flag2 ? Mathf.Clamp01(normalizedShieldThrowConfig.DamageDecay) : 0f),
				ShieldThrowRadiusFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.RadiusFactor) : 0f),
				ShieldThrowTtlFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.TtlFactor) : 0f),
				HasShieldCharge = flag3,
				ShieldChargeDamageFactor = (flag3 ? Mathf.Max(0f, charge.DamageFactor) : 0f),
				ShieldChargePushFactor = (flag3 ? Mathf.Max(0f, charge.PushFactor) : 0f),
				ShieldChargeStaminaFactor = (flag3 ? Mathf.Max(0f, charge.StaminaFactor) : 0f),
				ShieldChargeDistance = (flag3 ? Mathf.Max(0f, charge.Distance) : 0f),
				ShieldChargeSpeed = (flag3 ? Mathf.Max(0f, charge.Speed) : 0f),
				ShieldChargeCooldown = (flag3 ? Mathf.Max(0f, charge.Cooldown) : 0f),
				ShieldChargeCooldownReductionFactor = (flag3 ? Mathf.Clamp01(charge.CooldownReductionFactor) : 0f),
				ShieldChargeDurabilityFactor = (flag3