Decompiled source of HoldToDie v1.0.0

Mods/HoldToDie.dll

Decompiled 15 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using HarmonyLib;
using HoldToDie;
using Il2Cpp;
using Il2CppInterop.Common;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using SpellBrigade.ModMenu;
using SpellBrigade.Shared;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Localization;
using UnityEngine.Localization.Components;
using UnityEngine.Localization.Settings;
using UnityEngine.U2D;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(HoldToDieMod), "Hold to Die", "1.0.0", "Relsev", null)]
[assembly: MelonGame("BoltBlasterGames", "TheSpellBrigade")]
[assembly: MelonOptionalDependencies(new string[] { "ModMenu", "KeybindsUnlocked" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("HoldToDie")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1856b18f4bf94ea33a9b586b39e9ee6f647e5453")]
[assembly: AssemblyProduct("HoldToDie")]
[assembly: AssemblyTitle("HoldToDie")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SpellBrigade.Shared
{
	internal static class Hooks
	{
		public static bool Patch(Harmony harmony, Instance log, Type target, string method, Type patchClass, string prefix = null, string postfix = null)
		{
			return Patch(harmony, log, AccessTools.Method(target, method, (Type[])null, (Type[])null), patchClass, prefix, postfix, target.Name + "." + method);
		}

		public static bool Patch(Harmony harmony, Instance log, MethodBase original, Type patchClass, string prefix = null, string postfix = null, string name = null)
		{
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			if (name == null)
			{
				name = ((original != null) ? (original.DeclaringType?.Name + "." + original.Name) : ((prefix ?? postfix) + " target"));
			}
			try
			{
				if (original == null)
				{
					throw new MissingMethodException(name + " not found");
				}
				List<string> list = SharedCodeGuard.FindMethodsSharingCode(original);
				if (list.Count > 0)
				{
					log.Warning($"skipped {name}: its native code is shared with {list.Count} other method(s), patching would break them (e.g. {string.Join(", ", list.GetRange(0, Math.Min(5, list.Count)))})");
					return false;
				}
				harmony.Patch(original, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchClass, prefix, (Type[])null, (Type[])null)), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchClass, postfix, (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				return true;
			}
			catch (Exception ex)
			{
				log.Error($"failed to hook {name} — this part is disabled. {ex.GetType().Name}: {ex.Message}");
				return false;
			}
		}
	}
	internal sealed class Localizer
	{
		private readonly string[] _keys;

		private readonly Dictionary<string, string[]> _table;

		private readonly Func<Instance> _log;

		private readonly Dictionary<string, bool> _fontSupport = new Dictionary<string, bool>();

		private readonly List<(TMP_Text text, Func<string> value)> _bound = new List<(TMP_Text, Func<string>)>();

		private readonly List<Action> _listeners = new List<Action>();

		private string[] _current;

		private string _code;

		private TMP_Text _fontSample;

		private bool _subscribed;

		public Localizer(string[] keys, Dictionary<string, string[]> table, Func<Instance> log)
		{
			_keys = keys;
			_table = table;
			_log = log;
		}

		public string Get(string key)
		{
			if (_current == null || _code != LocaleCode())
			{
				_current = Resolve(_fontSample);
			}
			int num = Array.IndexOf(_keys, key);
			if (num < 0 || num >= _current.Length)
			{
				return key;
			}
			return _current[num];
		}

		public void Bind(TMP_Text text, Func<string> value, TMP_Text fontSample = null)
		{
			if (!((Object)(object)text == (Object)null))
			{
				EnsureSubscribed();
				if (_current == null)
				{
					_current = Resolve(fontSample ?? text);
				}
				text.text = value();
				_bound.Add((text, value));
			}
		}

		public void Bind(TMP_Text text, string key, TMP_Text fontSample = null)
		{
			Bind(text, () => Get(key), fontSample);
		}

		public void OnLanguageChanged(Action listener)
		{
			EnsureSubscribed();
			_listeners.Add(listener);
		}

		public void UseFont(TMP_Text sample)
		{
			_fontSample = sample;
			_current = null;
			EnsureSubscribed();
		}

		private void EnsureSubscribed()
		{
			if (_subscribed)
			{
				return;
			}
			_subscribed = true;
			try
			{
				LocalizationSettings.SelectedLocaleChanged += Action<Locale>.op_Implicit((Action<Locale>)delegate
				{
					Refresh();
				});
			}
			catch (Exception ex)
			{
				Instance obj = _log();
				if (obj != null)
				{
					obj.Warning("[lang] can't follow language changes: " + ex.Message);
				}
			}
		}

		private void Refresh()
		{
			_bound.RemoveAll(((TMP_Text text, Func<string> value) b) => (Object)(object)b.text == (Object)null);
			_current = Resolve((_bound.Count > 0) ? _bound[0].text : _fontSample);
			foreach (var (val, func) in _bound)
			{
				val.text = func();
			}
			foreach (Action listener in _listeners)
			{
				try
				{
					listener();
				}
				catch (Exception ex)
				{
					Instance obj = _log();
					if (obj != null)
					{
						obj.Warning("[lang] " + ex.Message);
					}
				}
			}
		}

		private static string LocaleCode()
		{
			try
			{
				Locale selectedLocale = LocalizationSettings.SelectedLocale;
				return ((selectedLocale != null) ? selectedLocale.Identifier.Code : null) ?? "en";
			}
			catch
			{
				return "en";
			}
		}

		private string[] Resolve(TMP_Text fontSample)
		{
			string text = (_code = LocaleCode());
			string text2 = TableKey(text);
			if (text2 != "en" && (Object)(object)fontSample != (Object)null && !FontHasAll(fontSample, text2))
			{
				Instance obj = _log();
				if (obj != null)
				{
					obj.Warning("[lang] font lacks glyphs for '" + text + "', using English");
				}
				text2 = "en";
			}
			return _table[text2];
		}

		private string TableKey(string code)
		{
			code = code.ToLowerInvariant().Replace('_', '-');
			string text = (code.StartsWith("zh") ? ((code.Contains("hant") || code.Contains("tw") || code.Contains("hk")) ? "zh-hant" : "zh-hans") : (code.StartsWith("pt") ? (code.Contains("br") ? "pt-br" : "pt") : ((!code.StartsWith("es")) ? code.Split('-')[0] : ((code == "es" || code.Contains("es-es")) ? "es" : "es-mx"))));
			if (_table.ContainsKey(text))
			{
				return text;
			}
			string text2 = text.Split('-')[0];
			if (!_table.ContainsKey(text2))
			{
				return "en";
			}
			return text2;
		}

		private bool FontHasAll(TMP_Text sample, string key)
		{
			if (_fontSupport.TryGetValue(key, out var value))
			{
				return value;
			}
			value = true;
			try
			{
				TMP_FontAsset font = sample.font;
				if ((Object)(object)font != (Object)null)
				{
					string[] array = _table[key];
					foreach (string text in array)
					{
						foreach (char c in text)
						{
							if (c > '\u007f' && !char.IsWhiteSpace(c) && !font.HasCharacter(c, true, true))
							{
								value = false;
								break;
							}
						}
						if (!value)
						{
							break;
						}
					}
				}
			}
			catch
			{
				value = true;
			}
			_fontSupport[key] = value;
			return value;
		}
	}
	internal static class MatchmakingGuard
	{
		private const string BlockersKey = "SpellBrigade.Matchmaking.Blockers";

		private const string OwnerKey = "SpellBrigade.Matchmaking.Owner";

		private const string PanelName = "SpellBrigade_MatchmakingLock";

		private static readonly Color Gold = new Color(0.96f, 0.78f, 0.35f);

		private static bool _owner;

		private static bool _wasBlocked;

		private static Instance _log;

		private static MatchmakingPreferencesModal _modal;

		private static TMP_Text _title;

		private static TMP_Text _body;

		private static float _pulse;

		private static float _nextCheck;

		private const string Title = "title";

		private const string Intro = "intro";

		private const string Footer = "footer";

		private static readonly Localizer Text = new Localizer(new string[3] { "title", "intro", "footer" }, new Dictionary<string, string[]>
		{
			["en"] = new string[3] { "Matchmaking is locked by mods", "These mods change the game for everyone in the lobby, so players without them can't be matched with you:", "Play with friends: invite them or share the lobby code. Matchmaking comes back as soon as nothing above applies." },
			["ru"] = new string[3] { "Подбор игроков заблокирован модами", "Эти моды меняют игру для всех в лобби, поэтому игроков без них к вам не подберёт:", "Играйте с друзьями: пригласите их или дайте код лобби. Подбор вернётся сам, как только ничего из списка не будет мешать." },
			["uk"] = new string[3] { "Підбір гравців заблоковано модами", "Ці моди змінюють гру для всіх у лобі, тому гравців без них до вас не підбере:", "Грайте з друзями: запросіть їх або дайте код лобі. Підбір повернеться сам, щойно нічого зі списку не заважатиме." },
			["de"] = new string[3] { "Matchmaking ist durch Mods gesperrt", "Diese Mods verändern das Spiel für alle in der Lobby, daher kannst du nicht mit Spielern ohne sie zusammengebracht werden:", "Spiele mit Freunden: Lade sie ein oder teile den Lobby-Code. Matchmaking kommt zurück, sobald nichts davon mehr zutrifft." },
			["fr"] = new string[3] { "Matchmaking bloqué par des mods", "Ces mods modifient la partie pour tout le salon, vous ne pouvez donc pas être associé à des joueurs qui ne les ont pas :", "Jouez avec des amis : invitez-les ou partagez le code du salon. Le matchmaking revient dès que plus rien ci-dessus ne s'applique." },
			["it"] = new string[3] { "Matchmaking bloccato dalle mod", "Queste mod cambiano la partita per tutta la lobby, quindi non puoi essere abbinato a chi non le ha:", "Gioca con gli amici: invitali o condividi il codice della lobby. Il matchmaking torna appena nulla di tutto ciò si applica." },
			["nl"] = new string[3] { "Matchmaking geblokkeerd door mods", "Deze mods veranderen het spel voor iedereen in de lobby, dus je wordt niet gekoppeld aan spelers zonder deze mods:", "Speel met vrienden: nodig ze uit of deel de lobbycode. Matchmaking komt terug zodra niets hiervan meer geldt." },
			["pl"] = new string[3] { "Dobieranie graczy zablokowane przez mody", "Te mody zmieniają grę wszystkim w lobby, więc gracze bez nich nie zostaną do ciebie dobrani:", "Graj ze znajomymi: zaproś ich lub podaj kod lobby. Dobieranie wróci samo, gdy nic z listy nie będzie przeszkadzać." },
			["pt-br"] = new string[3] { "Matchmaking bloqueado por mods", "Estes mods mudam o jogo para todos no lobby, então jogadores sem eles não serão pareados com você:", "Jogue com amigos: convide-os ou compartilhe o código do lobby. O matchmaking volta assim que nada acima se aplicar." },
			["pt"] = new string[3] { "Matchmaking bloqueado por mods", "Estes mods mudam o jogo para todos no lobby, por isso jogadores sem eles não serão emparelhados contigo:", "Joga com amigos: convida-os ou partilha o código do lobby. O matchmaking volta assim que nada acima se aplicar." },
			["es"] = new string[3] { "Emparejamiento bloqueado por mods", "Estos mods cambian la partida para toda la sala, así que no se te emparejará con jugadores que no los tengan:", "Juega con amigos: invítalos o comparte el código de sala. El emparejamiento vuelve en cuanto nada de lo anterior aplique." },
			["es-mx"] = new string[3] { "Emparejamiento bloqueado por mods", "Estos mods cambian la partida para toda la sala, así que no se te emparejará con jugadores que no los tengan:", "Juega con amigos: invítalos o comparte el código de sala. El emparejamiento regresa en cuanto nada de lo anterior aplique." },
			["ja"] = new string[3] { "MODによりマッチングがロックされています", "これらのMODはロビー全員のゲームを変えるため、MODのないプレイヤーとはマッチングしません:", "フレンドと遊びましょう:招待するかロビーコードを共有してください。上記が当てはまらなくなればマッチングは戻ります。" },
			["ko"] = new string[3] { "모드로 인해 매칭이 잠겼습니다", "이 모드들은 로비 전체의 게임을 바꾸므로, 모드가 없는 플레이어와는 매칭되지 않습니다:", "친구와 플레이하세요: 초대하거나 로비 코드를 공유하세요. 위 항목이 모두 해당되지 않으면 매칭이 다시 열립니다." },
			["zh-hans"] = new string[3] { "匹配已被模组锁定", "这些模组会改变大厅里所有人的游戏,因此不会为你匹配没有这些模组的玩家:", "和好友一起玩:邀请他们或分享大厅代码。只要上面各项都不再适用,匹配就会恢复。" },
			["zh-hant"] = new string[3] { "配對已被模組鎖定", "這些模組會改變大廳裡所有人的遊戲,因此不會為你配對沒有這些模組的玩家:", "和好友一起玩:邀請他們或分享大廳代碼。只要上面各項都不再適用,配對就會恢復。" },
			["th"] = new string[3] { "การจ\u0e31บค\u0e39\u0e48ถ\u0e39กล\u0e47อกโดยม\u0e47อด", "ม\u0e47อดเหล\u0e48าน\u0e35\u0e49เปล\u0e35\u0e48ยนเกมของท\u0e38กคนในล\u0e47อบบ\u0e35\u0e49 จ\u0e36งจะไม\u0e48จ\u0e31บค\u0e39\u0e48ค\u0e38ณก\u0e31บผ\u0e39\u0e49เล\u0e48นท\u0e35\u0e48ไม\u0e48ม\u0e35ม\u0e47อด:", "เล\u0e48นก\u0e31บเพ\u0e37\u0e48อน: เช\u0e34ญเพ\u0e37\u0e48อนหร\u0e37อแชร\u0e4cรห\u0e31สล\u0e47อบบ\u0e35\u0e49 การจ\u0e31บค\u0e39\u0e48จะกล\u0e31บมาเม\u0e37\u0e48อไม\u0e48ม\u0e35ข\u0e49อใดด\u0e49านบนเข\u0e49าเง\u0e37\u0e48อนไข" }
		}, () => _log);

		private static bool _prepareRestore;

		public static bool Blocked => Reasons().Count > 0;

		private static MatchmakingPreferencesManager Prefs => Singleton<MatchmakingPreferencesManager>.Instance;

		public static void Register(Harmony harmony, Instance log, string modName, Func<string> reason)
		{
			AppDomain currentDomain = AppDomain.CurrentDomain;
			List<Func<string>> list = currentDomain.GetData("SpellBrigade.Matchmaking.Blockers") as List<Func<string>>;
			if (list == null)
			{
				currentDomain.SetData("SpellBrigade.Matchmaking.Blockers", list = new List<Func<string>>());
			}
			list.Add(delegate
			{
				string text = reason();
				return (text == null) ? null : ("<b>" + modName + "</b> — " + text);
			});
			object data = currentDomain.GetData("SpellBrigade.Matchmaking.Owner");
			if (!(data is bool) || !(bool)data)
			{
				currentDomain.SetData("SpellBrigade.Matchmaking.Owner", true);
				_owner = true;
				_log = log;
				int num = 0;
				num += Hook(harmony, typeof(RunPreparationManager), "Prepare", "PreparePrefix", "PreparePostfix");
				num += Hook(harmony, typeof(RunPreparationManager), "CanUseMatchmaking", "CanUsePrefix");
				num += Hook(harmony, typeof(MatchmakingToggle), "Toggle", "TogglePrefix");
				num += Hook(harmony, typeof(MatchmakingPreferencesModal), "OnIsMatchmakingEnabledChanged", "EnabledChangedPrefix");
				num += Hook(harmony, typeof(MatchmakingPreferencesModal), "RefreshPanel", null, "RefreshPanelPostfix");
				log.Msg($"matchmaking guard: {num}/5 hooks");
			}
		}

		private static int Hook(Harmony harmony, Type type, string method, string prefix, string postfix = null)
		{
			return Hooks.Patch(harmony, _log, type, method, typeof(MatchmakingGuard), prefix, postfix) ? 1 : 0;
		}

		public static List<string> Reasons()
		{
			List<string> list = new List<string>();
			if (!(AppDomain.CurrentDomain.GetData("SpellBrigade.Matchmaking.Blockers") is List<Func<string>> list2))
			{
				return list;
			}
			foreach (Func<string> item in list2)
			{
				try
				{
					string text = item();
					if (text != null)
					{
						list.Add(text);
					}
				}
				catch
				{
				}
			}
			return list;
		}

		private static void PreparePrefix()
		{
			List<string> list = Reasons();
			MatchmakingPreferencesManager prefs = Prefs;
			if (list.Count != 0 && !((Object)(object)prefs == (Object)null) && prefs.IsMatchmakingEnabled)
			{
				prefs.IsMatchmakingEnabled = false;
				_prepareRestore = true;
				Instance log = _log;
				if (log != null)
				{
					log.Msg("run starts without matchmaking — " + StripTags(string.Join("; ", list)));
				}
			}
		}

		private static void PreparePostfix()
		{
			if (_prepareRestore)
			{
				_prepareRestore = false;
				MatchmakingPreferencesManager prefs = Prefs;
				if ((Object)(object)prefs != (Object)null)
				{
					prefs.IsMatchmakingEnabled = true;
				}
			}
		}

		private static bool CanUsePrefix(ref bool __result)
		{
			if (!Blocked)
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static bool TogglePrefix(MatchmakingToggle __instance)
		{
			if (!Blocked || !IsMatchmakingToggle(__instance))
			{
				return true;
			}
			_pulse = 1f;
			return false;
		}

		private static bool EnabledChangedPrefix(MatchmakingPreferencesModal __instance, bool isOn)
		{
			if (!isOn || !Blocked)
			{
				return true;
			}
			RefreshPanelPostfix(__instance);
			return false;
		}

		private static bool IsMatchmakingToggle(MatchmakingToggle toggle)
		{
			if ((Object)(object)_modal != (Object)null && (Object)(object)_modal.isMatchmakingEnabledToggle != (Object)null)
			{
				return ((Il2CppObjectBase)_modal.isMatchmakingEnabledToggle).Pointer == ((Il2CppObjectBase)toggle).Pointer;
			}
			return false;
		}

		private static void RefreshPanelPostfix(MatchmakingPreferencesModal __instance)
		{
			try
			{
				_modal = __instance;
				Apply(__instance, Reasons());
			}
			catch (Exception ex)
			{
				Instance log = _log;
				if (log != null)
				{
					log.Warning("matchmaking window: " + ex.Message);
				}
			}
		}

		private static void Apply(MatchmakingPreferencesModal modal, List<string> reasons)
		{
			bool flag = reasons.Count > 0;
			MatchmakingToggle isMatchmakingEnabledToggle = modal.isMatchmakingEnabledToggle;
			if ((Object)(object)isMatchmakingEnabledToggle != (Object)null)
			{
				if (flag)
				{
					isMatchmakingEnabledToggle.isOn = false;
					isMatchmakingEnabledToggle.RefreshUI();
				}
				(((Component)isMatchmakingEnabledToggle).GetComponent<CanvasGroup>() ?? ((Component)isMatchmakingEnabledToggle).gameObject.AddComponent<CanvasGroup>()).alpha = (flag ? 0.4f : 1f);
			}
			if (flag && (Object)(object)modal.matchmakingPreferencesObject != (Object)null)
			{
				modal.matchmakingPreferencesObject.SetActive(false);
			}
			Transform val = ((Component)modal).transform.Find("SpellBrigade_MatchmakingLock");
			if ((Object)(object)val == (Object)null && flag)
			{
				val = BuildPanel(modal);
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			((Component)val).gameObject.SetActive(flag);
			if (!flag)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(Text.Get("intro")).Append('\n');
			foreach (string reason in reasons)
			{
				stringBuilder.Append("\n•  ").Append(reason);
			}
			stringBuilder.Append("\n\n<alpha=#B3>").Append(Text.Get("footer"));
			_title.text = Text.Get("title");
			_body.text = stringBuilder.ToString();
		}

		private static Transform BuildPanel(MatchmakingPreferencesModal modal)
		{
			//IL_0038: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			Transform obj = ((Component)modal).transform.Find("Matchmaking_Description");
			TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null) ?? ((Component)modal).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			RectTransform val2 = new GameObject("SpellBrigade_MatchmakingLock").AddComponent<RectTransform>();
			((Transform)val2).SetParent(((Component)modal).transform, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 1f);
			val2.pivot = val3;
			Vector2 anchorMin = (val2.anchorMax = val3);
			val2.anchorMin = anchorMin;
			val2.anchoredPosition = new Vector2(250f, -1060f);
			val2.sizeDelta = new Vector2(2250f, 760f);
			float num = (val.enableAutoSizing ? val.fontSizeMax : val.fontSize);
			_title = CloneText(val, (Transform)(object)val2, "Title", num * 1.25f, Gold, (FontStyles)1);
			RectTransform rectTransform = _title.rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 1f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.pivot = new Vector2(0f, 1f);
			rectTransform.sizeDelta = new Vector2(0f, num * 1.8f);
			rectTransform.anchoredPosition = Vector2.zero;
			_body = CloneText(val, (Transform)(object)val2, "Body", num, Color.white, (FontStyles)0);
			RectTransform rectTransform2 = _body.rectTransform;
			rectTransform2.anchorMin = Vector2.zero;
			rectTransform2.anchorMax = Vector2.one;
			rectTransform2.pivot = new Vector2(0f, 1f);
			rectTransform2.offsetMin = Vector2.zero;
			rectTransform2.offsetMax = new Vector2(0f, (0f - num) * 2f);
			_body.textWrappingMode = (TextWrappingModes)1;
			_body.enableAutoSizing = true;
			_body.fontSizeMin = num * 0.6f;
			_body.fontSizeMax = num;
			return (Transform)(object)val2;
		}

		private static TMP_Text CloneText(TMP_Text template, Transform parent, string name, float size, Color color, FontStyles style)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text component = Object.Instantiate<GameObject>(((Component)template).gameObject, parent).GetComponent<TMP_Text>();
			foreach (LocalizeStringEvent component2 in ((Component)component).GetComponents<LocalizeStringEvent>())
			{
				Object.Destroy((Object)(object)component2);
			}
			foreach (MonoBehaviour component3 in ((Component)component).GetComponents<MonoBehaviour>())
			{
				if ((Object)(object)component3 != (Object)null && (Object)(object)((Il2CppObjectBase)component3).TryCast<TMP_Text>() == (Object)null)
				{
					Object.Destroy((Object)(object)component3);
				}
			}
			((Object)component).name = name;
			component.enableAutoSizing = false;
			component.fontSize = size;
			((Graphic)component).color = color;
			component.fontStyle = style;
			component.alignment = (TextAlignmentOptions)257;
			component.textWrappingMode = (TextWrappingModes)0;
			component.overflowMode = (TextOverflowModes)0;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private static string StripTags(string s)
		{
			return Regex.Replace(s, "<.*?>", "");
		}

		public static void Tick()
		{
			//IL_004e: 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_0073: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			if (!_owner)
			{
				return;
			}
			if (_pulse > 0f && (Object)(object)_title != (Object)null)
			{
				_pulse = Mathf.Max(0f, _pulse - Time.unscaledDeltaTime * 2.5f);
				_title.transform.localScale = Vector3.one * (1f + 0.08f * _pulse);
				((Graphic)_title).color = Color.Lerp(Gold, new Color(1f, 0.45f, 0.35f), _pulse);
			}
			if (Time.unscaledTime < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.unscaledTime + 0.25f;
			List<string> list = Reasons();
			bool flag = list.Count > 0;
			if (flag != _wasBlocked)
			{
				_wasBlocked = flag;
				Instance log = _log;
				if (log != null)
				{
					log.Msg(flag ? ("matchmaking locked — " + StripTags(string.Join("; ", list))) : "matchmaking unlocked");
				}
				if ((Object)(object)_modal != (Object)null && ((Component)_modal).gameObject.activeInHierarchy)
				{
					try
					{
						_modal.RefreshPanel();
					}
					catch
					{
					}
				}
			}
			else if (flag && (Object)(object)_modal != (Object)null && ((Component)_modal).gameObject.activeInHierarchy && (Object)(object)_body != (Object)null)
			{
				Apply(_modal, list);
			}
		}
	}
	internal static class SharedCodeGuard
	{
		private static Dictionary<IntPtr, List<IntPtr>> _methodsByCode;

		public static List<string> FindMethodsSharingCode(MethodBase generatedMethod)
		{
			List<string> list = new List<string>();
			if (!(Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(generatedMethod)?.GetValue(null) is IntPtr intPtr) || intPtr == IntPtr.Zero)
			{
				return list;
			}
			IntPtr intPtr2 = Marshal.ReadIntPtr(intPtr);
			if (intPtr2 == IntPtr.Zero)
			{
				return list;
			}
			if (_methodsByCode == null)
			{
				_methodsByCode = BuildIndex();
			}
			if (!_methodsByCode.TryGetValue(intPtr2, out var value))
			{
				return list;
			}
			foreach (IntPtr item in value)
			{
				if (item != intPtr)
				{
					list.Add(Describe(item));
				}
			}
			return list;
		}

		private static string Describe(IntPtr method)
		{
			IntPtr intPtr = IL2CPP.il2cpp_method_get_class(method);
			string text = IL2CPP.il2cpp_class_get_namespace_(intPtr);
			return (string.IsNullOrEmpty(text) ? IL2CPP.il2cpp_class_get_name_(intPtr) : (text + "." + IL2CPP.il2cpp_class_get_name_(intPtr))) + "::" + IL2CPP.il2cpp_method_get_name_(method);
		}

		private unsafe static Dictionary<IntPtr, List<IntPtr>> BuildIndex()
		{
			Dictionary<IntPtr, List<IntPtr>> dictionary = new Dictionary<IntPtr, List<IntPtr>>();
			uint num = 0u;
			IntPtr* ptr = IL2CPP.il2cpp_domain_get_assemblies(IL2CPP.il2cpp_domain_get(), ref num);
			for (uint num2 = 0u; num2 < num; num2++)
			{
				IntPtr intPtr = IL2CPP.il2cpp_assembly_get_image(ptr[num2]);
				uint num3 = IL2CPP.il2cpp_image_get_class_count(intPtr);
				for (uint num4 = 0u; num4 < num3; num4++)
				{
					IntPtr intPtr2 = IL2CPP.il2cpp_image_get_class(intPtr, num4);
					if (intPtr2 == IntPtr.Zero)
					{
						continue;
					}
					IntPtr zero = IntPtr.Zero;
					IntPtr intPtr3;
					while ((intPtr3 = IL2CPP.il2cpp_class_get_methods(intPtr2, ref zero)) != IntPtr.Zero)
					{
						IntPtr intPtr4 = Marshal.ReadIntPtr(intPtr3);
						if (!(intPtr4 == IntPtr.Zero))
						{
							if (!dictionary.TryGetValue(intPtr4, out var value))
							{
								value = (dictionary[intPtr4] = new List<IntPtr>(1));
							}
							value.Add(intPtr3);
						}
					}
				}
			}
			return dictionary;
		}
	}
	internal static class WheelSelect
	{
		private sealed class State
		{
			public ScrollRect Blocked;

			public float BlockedSensitivity;

			public Vector2 LastMouse = new Vector2(-1f, -1f);
		}

		private const string FrameKey = "SpellBrigade.WheelSelect.Frame";

		private const string CustomKey = "SpellBrigade.WheelSelect.Custom";

		private static readonly State Own = new State();

		private static List<(GameObject target, Action<int> step)> Custom
		{
			get
			{
				AppDomain currentDomain = AppDomain.CurrentDomain;
				if (currentDomain.GetData("SpellBrigade.WheelSelect.Custom") is List<(GameObject, Action<int>)> result)
				{
					return result;
				}
				List<(GameObject, Action<int>)> list = new List<(GameObject, Action<int>)>();
				currentDomain.SetData("SpellBrigade.WheelSelect.Custom", list);
				return list;
			}
		}

		public static void Register(GameObject target, Action<int> step)
		{
			if ((Object)(object)target != (Object)null && step != null)
			{
				Custom.Add((target, step));
			}
		}

		public static void Tick()
		{
			try
			{
				TickCore();
			}
			catch
			{
			}
		}

		private static void TickCore()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			int frameCount = Time.frameCount;
			AppDomain currentDomain = AppDomain.CurrentDomain;
			if (currentDomain.GetData("SpellBrigade.WheelSelect.Frame") is int num && num == frameCount)
			{
				return;
			}
			currentDomain.SetData("SpellBrigade.WheelSelect.Frame", frameCount);
			object data = currentDomain.GetData("SpellBrigade.Modal");
			if (data is bool && (bool)data)
			{
				return;
			}
			Mouse current = Mouse.current;
			if (current == null)
			{
				return;
			}
			State own = Own;
			Vector2 val = ((InputControl<Vector2>)(object)((Pointer)current).position).ReadValue();
			float y = ((InputControl<Vector2>)(object)current.scroll).ReadValue().y;
			Vector2 val2 = val - own.LastMouse;
			if (!(((Vector2)(ref val2)).sqrMagnitude > 0.25f) && Mathf.Abs(y) < 0.01f)
			{
				return;
			}
			own.LastMouse = val;
			var (val3, action, val4) = Hovered(val);
			BlockScroll(own, ((Object)(object)val4 != (Object)null) ? ScrollRectAbove(val4) : null);
			if (Mathf.Abs(y) < 0.01f || (Object)(object)val4 == (Object)null)
			{
				return;
			}
			int num2 = ((y > 0f) ? 1 : (-1));
			try
			{
				if ((Object)(object)val3 != (Object)null)
				{
					if (num2 > 0)
					{
						val3.SelectNext();
					}
					else
					{
						val3.SelectPrevious();
					}
				}
				else
				{
					action?.Invoke(num2);
				}
			}
			catch
			{
			}
		}

		private static (Selector selector, Action<int> custom, RectTransform rect) Hovered(Vector2 position)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			List<(GameObject target, Action<int> step)> custom = Custom;
			custom.RemoveAll(((GameObject target, Action<int> step) c) => (Object)(object)c.target == (Object)null);
			foreach (var (val, item) in custom)
			{
				if (val.activeInHierarchy && Contains(val.GetComponent<RectTransform>(), position))
				{
					return (selector: null, custom: item, rect: val.GetComponent<RectTransform>());
				}
			}
			foreach (Selector item2 in Object.FindObjectsOfType<Selector>())
			{
				if (!((Object)(object)item2 == (Object)null) && ((Selectable)item2).IsInteractable())
				{
					RectTransform component = ((Component)item2).GetComponent<RectTransform>();
					if (Contains(component, position))
					{
						return (selector: item2, custom: null, rect: component);
					}
				}
			}
			return (selector: null, custom: null, rect: null);
		}

		private static bool Contains(RectTransform rect, Vector2 position)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rect == (Object)null)
			{
				return false;
			}
			Canvas val = null;
			Transform val2 = ((Component)rect).transform;
			while ((Object)(object)val2 != (Object)null)
			{
				CanvasGroup component = ((Component)val2).GetComponent<CanvasGroup>();
				if ((Object)(object)component != (Object)null && (component.alpha < 0.01f || !component.blocksRaycasts))
				{
					return false;
				}
				Canvas component2 = ((Component)val2).GetComponent<Canvas>();
				if ((Object)(object)component2 != (Object)null)
				{
					val = component2;
				}
				val2 = val2.parent;
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Camera val3 = (((int)val.renderMode == 0) ? null : val.worldCamera);
			if (!RectTransformUtility.RectangleContainsScreenPoint(rect, position, val3))
			{
				return false;
			}
			Transform parent = ((Transform)rect).parent;
			while ((Object)(object)parent != (Object)null)
			{
				if ((Object)(object)((Component)parent).GetComponent<RectMask2D>() != (Object)null && !RectTransformUtility.RectangleContainsScreenPoint(((Component)parent).GetComponent<RectTransform>(), position, val3))
				{
					return false;
				}
				parent = parent.parent;
			}
			return true;
		}

		private static ScrollRect ScrollRectAbove(RectTransform rect)
		{
			Transform parent = ((Transform)rect).parent;
			while ((Object)(object)parent != (Object)null)
			{
				ScrollRect component = ((Component)parent).GetComponent<ScrollRect>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
				parent = parent.parent;
			}
			return null;
		}

		private static void BlockScroll(State state, ScrollRect scroll)
		{
			if (!((Object)(object)state.Blocked != (Object)null) || !((Object)(object)scroll != (Object)null) || !(((Il2CppObjectBase)state.Blocked).Pointer == ((Il2CppObjectBase)scroll).Pointer))
			{
				if ((Object)(object)state.Blocked != (Object)null)
				{
					state.Blocked.scrollSensitivity = state.BlockedSensitivity;
				}
				state.Blocked = scroll;
				if (!((Object)(object)scroll == (Object)null))
				{
					state.BlockedSensitivity = scroll.scrollSensitivity;
					scroll.scrollSensitivity = 0f;
				}
			}
		}
	}
}
namespace HoldToDie
{
	internal static class ControlsRow
	{
		private sealed class Cell
		{
			public Image Icon;

			public Image Cover;

			public Sprite Template;

			public TMP_Text Caption;

			public bool Gamepad;
		}

		private const string RowName = "HoldToDie_Row";

		private static readonly Color KeyFill = new Color(0.149f, 0.125f, 0.188f);

		private static readonly List<Cell> Cells = new List<Cell>();

		private static readonly Dictionary<string, Sprite> SpriteCache = new Dictionary<string, Sprite>();

		private static string _shownKey;

		private static string _shownPad;

		private static float _nextCheck;

		public static void InjectPostfix(SettingsPanel __instance)
		{
			try
			{
				if (Death.KeybindsInstalled)
				{
					return;
				}
				List<SubPanel> subPanels = __instance.subPanels;
				if (subPanels == null)
				{
					return;
				}
				for (int i = 0; i < subPanels.Count; i++)
				{
					Panel panel = subPanels[i].Panel;
					if ((Object)(object)panel == (Object)null || !((Object)panel).name.StartsWith("Controls"))
					{
						continue;
					}
					Transform transform = ((Component)panel).transform;
					for (int j = 0; j < transform.childCount; j++)
					{
						Transform child = transform.GetChild(j);
						if (((Object)child).name.Contains("Layout_Panel"))
						{
							AddRow(child, "Prompts_Panel", !((Object)child).name.Contains("Keyboard"));
						}
					}
				}
				_shownKey = (_shownPad = null);
			}
			catch (Exception ex)
			{
				HoldToDieMod.Log.Warning("can't add the key to Options → Controls: " + ex.Message);
			}
		}

		private static void AddRow(Transform controls, string path, bool gamepad)
		{
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			Transform val = controls.Find(path);
			if ((Object)(object)val == (Object)null || val.childCount == 0 || (Object)(object)val.Find("HoldToDie_Row") != (Object)null)
			{
				return;
			}
			GameObject gameObject = ((Component)val.GetChild(val.childCount - 1)).gameObject;
			GameObject val2 = Object.Instantiate<GameObject>(gameObject, val);
			((Object)val2).name = "HoldToDie_Row";
			TMP_Text val3 = null;
			Image val4 = null;
			foreach (TMP_Text componentsInChild in val2.GetComponentsInChildren<TMP_Text>(true))
			{
				if ((Object)(object)val3 == (Object)null)
				{
					val3 = componentsInChild;
				}
			}
			foreach (Image componentsInChild2 in val2.GetComponentsInChildren<Image>(true))
			{
				if (!((Object)(object)componentsInChild2.sprite == (Object)null) && ((Object)componentsInChild2.sprite).name.StartsWith("key_", StringComparison.OrdinalIgnoreCase))
				{
					if ((Object)(object)val4 == (Object)null)
					{
						val4 = componentsInChild2;
					}
					else
					{
						((Component)componentsInChild2).gameObject.SetActive(false);
					}
				}
			}
			if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				HoldToDieMod.Log.Warning($"Options → Controls: unexpected layout in {((Object)controls).name}/{path}, key row skipped");
				return;
			}
			foreach (LocalizeStringEvent componentsInChild3 in val2.GetComponentsInChildren<LocalizeStringEvent>(true))
			{
				Object.Destroy((Object)(object)componentsInChild3);
			}
			Strings.Bind(val3, "die_hold");
			Image val5 = new GameObject("Cover").AddComponent<Image>();
			RectTransform rectTransform = ((Graphic)val5).rectTransform;
			((Transform)rectTransform).SetParent(((Component)val4).transform, false);
			rectTransform.anchorMin = new Vector2(0.11f, 0.11f);
			rectTransform.anchorMax = new Vector2(0.89f, 0.89f);
			Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero);
			rectTransform.offsetMin = offsetMin;
			((Graphic)val5).color = KeyFill;
			((Graphic)val5).raycastTarget = false;
			TMP_Text component = Object.Instantiate<GameObject>(((Component)val3).gameObject, ((Component)val4).transform).GetComponent<TMP_Text>();
			foreach (LocalizeStringEvent component2 in ((Component)component).GetComponents<LocalizeStringEvent>())
			{
				Object.Destroy((Object)(object)component2);
			}
			RectTransform rectTransform2 = component.rectTransform;
			rectTransform2.anchorMin = rectTransform.anchorMin;
			rectTransform2.anchorMax = rectTransform.anchorMax;
			offsetMin = (rectTransform2.offsetMax = Vector2.zero);
			rectTransform2.offsetMin = offsetMin;
			component.alignment = (TextAlignmentOptions)514;
			component.fontStyle = (FontStyles)1;
			((Graphic)component).color = Color.white;
			component.enableAutoSizing = true;
			component.fontSizeMin = 6f;
			component.fontSizeMax = val3.fontSize * 0.7f;
			component.textWrappingMode = (TextWrappingModes)0;
			component.margin = Vector4.zero;
			Cells.Add(new Cell
			{
				Icon = val4,
				Cover = val5,
				Template = val4.sprite,
				Caption = component,
				Gamepad = gamepad
			});
			HoldToDieMod.Log.Msg($"Options → Controls: key row added ({(gamepad ? "controller" : "keyboard")}, template {((Object)gameObject).name} / {((Object)val4.sprite).name})");
		}

		public static void Tick()
		{
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			if (Cells.Count == 0 || Time.unscaledTime < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.unscaledTime + 0.5f;
			Cells.RemoveAll((Cell c) => (Object)(object)c.Icon == (Object)null);
			string value = HoldToDieMod.KeyEntry.Value;
			string value2 = HoldToDieMod.GamepadEntry.Value;
			if (value == _shownKey && value2 == _shownPad)
			{
				return;
			}
			_shownKey = value;
			_shownPad = value2;
			foreach (Cell cell in Cells)
			{
				string text = (cell.Gamepad ? Death.GamepadSprite(value2) : ("key_pc_" + value.ToLowerInvariant()));
				string text2 = (cell.Gamepad ? Death.GamepadName(value2) : value);
				Sprite val = ((text == null) ? null : FindSprite(text, cell.Template));
				((Component)cell.Icon).gameObject.SetActive(text2 != null);
				bool flag = (Object)(object)val != (Object)null;
				cell.Icon.sprite = (flag ? val : cell.Template);
				((Behaviour)cell.Icon).enabled = flag || !cell.Gamepad;
				((Component)cell.Cover).gameObject.SetActive(!flag && !cell.Gamepad);
				((Component)cell.Caption).gameObject.SetActive(!flag);
				cell.Caption.text = text2 ?? "";
				if (cell.Gamepad && !flag)
				{
					RectTransform rectTransform = cell.Caption.rectTransform;
					rectTransform.anchorMin = Vector2.zero;
					rectTransform.anchorMax = Vector2.one;
				}
			}
		}

		private static Sprite FindSprite(string name, Sprite reference)
		{
			if (SpriteCache.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Sprite val = null;
			foreach (Sprite item in Resources.FindObjectsOfTypeAll<Sprite>())
			{
				if (!((Object)(object)item == (Object)null) && ((Object)item).name.Equals(name, StringComparison.OrdinalIgnoreCase))
				{
					if (val == null)
					{
						val = item;
					}
					if ((Object)(object)reference != (Object)null && (Object)(object)item.texture == (Object)(object)reference.texture)
					{
						val = item;
						break;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				foreach (SpriteAtlas item2 in Resources.FindObjectsOfTypeAll<SpriteAtlas>())
				{
					try
					{
						val = item2.GetSprite(name);
					}
					catch
					{
					}
					if ((Object)(object)val != (Object)null)
					{
						((Object)val).hideFlags = (HideFlags)32;
						break;
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				SpriteCache[name] = val;
			}
			return val;
		}
	}
	internal static class Death
	{
		private const float RingSize = 170f;

		private static readonly Color Back = new Color(0f, 0f, 0f, 0.45f);

		private static readonly Color Fill = new Color(0.93f, 0.27f, 0.22f, 0.95f);

		private static float _held;

		private static bool _waitRelease;

		private static bool _noRevives;

		private static string _keyName;

		private static Key _key;

		private static GameObject _overlay;

		private static RectTransform _canvas;

		private static RectTransform _ring;

		private static Image _fill;

		public static readonly string[] GamepadButtons = new string[4] { "L3", "R3", "Select", "None" };

		private static bool? _keybinds;

		public static bool KeybindsInstalled
		{
			get
			{
				bool valueOrDefault = _keybinds == true;
				if (!_keybinds.HasValue)
				{
					valueOrDefault = MelonBase.FindMelon("Keybinds Unlocked", "Relsev") != null;
					_keybinds = valueOrDefault;
					return valueOrDefault;
				}
				return valueOrDefault;
			}
		}

		public static void Tick()
		{
			GameplayPlayerManager instance = Singleton<GameplayPlayerManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				_noRevives = false;
			}
			else if (_noRevives && instance.AlivePlayers.Count == 0)
			{
				ZeroRevives();
			}
			bool num = Pressed();
			if (!num)
			{
				_waitRelease = false;
			}
			GameplayPlayer val = ((num && !_waitRelease) ? LocalPlayer(instance) : null);
			if ((Object)(object)val == (Object)null)
			{
				Reset();
				return;
			}
			_held += Time.unscaledDeltaTime;
			float num2 = _held / HoldToDieMod.HoldTimeEntry.Value;
			if (num2 < 1f)
			{
				Show(val, num2);
				return;
			}
			Reset();
			_waitRelease = true;
			Kill(instance, val);
		}

		public static void Reset()
		{
			_held = 0f;
			if ((Object)(object)_overlay != (Object)null)
			{
				_overlay.SetActive(false);
			}
		}

		private static bool Pressed()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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)
			if (!HoldToDieMod.EnabledEntry.Value)
			{
				return false;
			}
			object data = AppDomain.CurrentDomain.GetData("SpellBrigade.Typing");
			if (data is bool && (bool)data)
			{
				return false;
			}
			object? data2 = AppDomain.CurrentDomain.GetData("SpellBrigade.Input.HoldToDie");
			InputAction val = (InputAction)((data2 is InputAction) ? data2 : null);
			if (val != null)
			{
				return val.IsPressed();
			}
			string value = HoldToDieMod.KeyEntry.Value;
			if (value != _keyName)
			{
				_keyName = value;
				if (!Enum.TryParse<Key>(value, ignoreCase: true, out _key) || (int)_key == 0)
				{
					_key = (Key)0;
					HoldToDieMod.Log.Warning("unknown key \"" + value + "\" — use names like K, Backspace, Delete, End, F9");
				}
			}
			Keyboard current = Keyboard.current;
			if (current != null && (int)_key != 0 && ((ButtonControl)current[_key]).isPressed)
			{
				return true;
			}
			Gamepad current2 = Gamepad.current;
			if (current2 == null)
			{
				return false;
			}
			return HoldToDieMod.GamepadEntry.Value switch
			{
				"L3" => current2.leftStickButton.isPressed, 
				"R3" => current2.rightStickButton.isPressed, 
				"Select" => current2.selectButton.isPressed, 
				_ => false, 
			};
		}

		public static string GamepadName(string value)
		{
			if (!(value == "None"))
			{
				return value;
			}
			return null;
		}

		public static string GamepadSprite(string value)
		{
			return value switch
			{
				"L3" => "key_xbox_L", 
				"R3" => "key_xbox_R", 
				"Select" => "key_xbox_select", 
				_ => null, 
			};
		}

		private static GameplayPlayer LocalPlayer(GameplayPlayerManager manager)
		{
			if ((Object)(object)manager == (Object)null || Time.timeScale <= 0f)
			{
				return null;
			}
			PauseMenuManager instance = Singleton<PauseMenuManager>.Instance;
			if ((Object)(object)instance != (Object)null && instance.IsOpen)
			{
				return null;
			}
			List<GameplayPlayer> alivePlayers = manager.AlivePlayers;
			for (int i = 0; i < alivePlayers.Count; i++)
			{
				GameplayPlayer val = alivePlayers[i];
				if ((Object)(object)val != (Object)null && manager.IsPlayerLocalPlayer(val))
				{
					return val;
				}
			}
			return null;
		}

		private static void Kill(GameplayPlayerManager manager, GameplayPlayer player)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			bool flag = (Object)(object)singleton != (Object)null && singleton.IsServer;
			if (HoldToDieMod.NoRevivesEntry.Value && flag && manager.AlivePlayers.Count == 1)
			{
				_noRevives = true;
				ZeroRevives();
			}
			((HealthContainer)player.HealthContainer).RemoveHealthWithoutModifiers(999999f, (DamageInfo)new HealthRerollDamageInfo(), false);
			HoldToDieMod.Log.Msg(_noRevives ? "died — no revives, the run ends" : "died");
		}

		private static void ZeroRevives()
		{
			ReviveManager instance = NetworkSingleton<ReviveManager>.Instance;
			if ((Object)(object)instance != (Object)null && instance.CurrentRevives.Value > 0)
			{
				instance.CurrentRevives.Value = 0;
			}
		}

		private static void Show(GameplayPlayer player, float progress)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Camera.main;
			if (!((Object)(object)main == (Object)null))
			{
				if ((Object)(object)_overlay == (Object)null)
				{
					Build();
				}
				_overlay.SetActive(true);
				Vector3 val = main.WorldToScreenPoint(player.GetPosition());
				Vector2 anchoredPosition = default(Vector2);
				RectTransformUtility.ScreenPointToLocalPointInRectangle(_canvas, new Vector2(val.x, val.y), (Camera)null, ref anchoredPosition);
				_ring.anchoredPosition = anchoredPosition;
				_fill.fillAmount = progress;
			}
		}

		private static void Build()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: 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_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			_overlay = new GameObject("HoldToDie_Ring");
			Object.DontDestroyOnLoad((Object)(object)_overlay);
			Canvas obj = _overlay.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 31000;
			CanvasScaler obj2 = _overlay.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			obj2.matchWidthOrHeight = 0.5f;
			_canvas = _overlay.GetComponent<RectTransform>();
			Sprite sprite = RingSprite();
			_ring = ((Graphic)NewImage("Ring", (Transform)(object)_canvas, sprite, Back)).rectTransform;
			_ring.sizeDelta = new Vector2(170f, 170f);
			_fill = NewImage("Fill", (Transform)(object)_ring, sprite, Fill);
			_fill.type = (Type)3;
			_fill.fillMethod = (FillMethod)4;
			_fill.fillOrigin = 2;
			_fill.fillClockwise = true;
			RectTransform rectTransform = ((Graphic)_fill).rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero);
			rectTransform.offsetMin = offsetMin;
		}

		private static Image NewImage(string name, Transform parent, Sprite sprite, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			((Transform)val.AddComponent<RectTransform>()).SetParent(parent, false);
			Image obj = val.AddComponent<Image>();
			obj.sprite = sprite;
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private static Sprite RingSprite()
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: 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_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			Color32[] array = (Color32[])(object)new Color32[16384];
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					float num = Mathf.Sqrt(((float)j - 63.5f) * ((float)j - 63.5f) + ((float)i - 63.5f) * ((float)i - 63.5f));
					float num2 = Mathf.Clamp01(63f - num + 0.5f) * Mathf.Clamp01(num - 52f + 0.5f);
					array[i * 128 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num2 * 255f));
				}
			}
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				hideFlags = (HideFlags)61,
				wrapMode = (TextureWrapMode)1
			};
			val.SetPixels32(Il2CppStructArray<Color32>.op_Implicit(array));
			val.Apply();
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f), 100f);
			((Object)obj).hideFlags = (HideFlags)61;
			return obj;
		}
	}
	internal static class MenuPage
	{
		private static readonly List<string> KeyNames = new List<string> { "K", "L", "Backspace", "Delete", "End", "Insert", "F8", "F9", "F10" };

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void Register()
		{
			Page val = Menu.AddPage("HoldToDie", "Hold to Die").Toggle((Func<string>)(() => Strings.Get("enabled")), (Func<bool>)(() => HoldToDieMod.EnabledEntry.Value), (Action<bool>)delegate(bool v)
			{
				Set(HoldToDieMod.EnabledEntry, v);
			}, (bool?)HoldToDieMod.EnabledEntry.DefaultValue);
			if (Death.KeybindsInstalled)
			{
				val.Note((Func<string>)(() => Strings.Get("key_in_controls")));
			}
			else
			{
				val.Choice((Func<string>)(() => Strings.Get("key")), (Func<IList<string>>)(() => KeyOptions()), (Func<int>)(() => KeyOptions().IndexOf(HoldToDieMod.KeyEntry.Value)), (Action<int>)delegate(int i)
				{
					Set(HoldToDieMod.KeyEntry, KeyOptions()[i]);
				}, (int?)KeyNames.IndexOf(HoldToDieMod.KeyEntry.DefaultValue)).Choice((Func<string>)(() => Strings.Get("gamepad")), (Func<IList<string>>)(() => PadOptions()), (Func<int>)(() => Array.IndexOf(Death.GamepadButtons, HoldToDieMod.GamepadEntry.Value)), (Action<int>)delegate(int i)
				{
					Set(HoldToDieMod.GamepadEntry, Death.GamepadButtons[i]);
				}, (int?)Array.IndexOf(Death.GamepadButtons, HoldToDieMod.GamepadEntry.DefaultValue));
			}
			val.Number((Func<string>)(() => Strings.Get("hold_time")), 0.5f, 5f, 0.5f, (Func<float>)(() => HoldToDieMod.HoldTimeEntry.Value), (Action<float>)delegate(float v)
			{
				Set(HoldToDieMod.HoldTimeEntry, v);
			}, (Func<float, string>)((float v) => string.Format(Strings.Get("seconds"), v.ToString("0.0", CultureInfo.InvariantCulture))), (float?)HoldToDieMod.HoldTimeEntry.DefaultValue).Choice((Func<string>)(() => Strings.Get("mode")), (Func<IList<string>>)(() => new string[2]
			{
				Strings.Get("revivable"),
				Strings.Get("no_revives")
			}), (Func<int>)(() => HoldToDieMod.NoRevivesEntry.Value ? 1 : 0), (Action<int>)delegate(int i)
			{
				Set(HoldToDieMod.NoRevivesEntry, i == 1);
			}, (int?)0).Note((Func<string>)(() => Strings.Get("hint")));
		}

		private static List<string> KeyOptions()
		{
			string value = HoldToDieMod.KeyEntry.Value;
			if (KeyNames.Contains(value))
			{
				return KeyNames;
			}
			return new List<string>(KeyNames) { value };
		}

		private static string[] PadOptions()
		{
			string[] array = new string[Death.GamepadButtons.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = Death.GamepadName(Death.GamepadButtons[i]) ?? Strings.Get("no_button");
			}
			return array;
		}

		private static void Set<T>(MelonPreferences_Entry<T> entry, T value)
		{
			if (!object.Equals(entry.Value, value))
			{
				entry.Value = value;
				HoldToDieMod.Save();
			}
		}
	}
	public class HoldToDieMod : MelonMod
	{
		public static Instance Log;

		private static MelonPreferences_Category _category;

		internal static MelonPreferences_Entry<bool> EnabledEntry;

		internal static MelonPreferences_Entry<bool> NoRevivesEntry;

		internal static MelonPreferences_Entry<string> KeyEntry;

		internal static MelonPreferences_Entry<string> GamepadEntry;

		internal static MelonPreferences_Entry<float> HoldTimeEntry;

		public override void OnInitializeMelon()
		{
			Log = ((MelonBase)this).LoggerInstance;
			_category = MelonPreferences.CreateCategory("HoldToDie", "Hold to Die");
			EnabledEntry = _category.CreateEntry<bool>("Enabled", true, "Enabled", "Удерживать клавишу в забеге — умереть", false, false, (ValueValidator)null, (string)null);
			KeyEntry = _category.CreateEntry<string>("Key", "K", "Key", "Клавиша, названия как в Unity Input System: K, Backspace, Delete, End, F9…", false, false, (ValueValidator)null, (string)null);
			GamepadEntry = _category.CreateEntry<string>("GamepadButton", "L3", "Controller Button", "Кнопка контроллера: L3, R3, Select или None. С Keybinds Unlocked клавиша и кнопка — в его списке управления", false, false, (ValueValidator)null, (string)null);
			HoldTimeEntry = _category.CreateEntry<float>("HoldTime", 2f, "Hold Time", "Сколько секунд держать клавишу", false, false, (ValueValidator)(object)new ValueRange<float>(0.5f, 5f), (string)null);
			NoRevivesEntry = _category.CreateEntry<bool>("NoRevives", false, "No Revives", "true — без воскрешений: если вы последний живой (соло или хост), забег сразу заканчивается", false, false, (ValueValidator)null, (string)null);
			_category.SaveToFile(false);
			Hooks.Patch(((MelonBase)this).HarmonyInstance, Log, typeof(SettingsPanel), "SetupSubPanels", typeof(ControlsRow), null, "InjectPostfix");
			Log.Msg("ready — hold the key in a run to die");
			if (MelonBase.FindMelon("Mod Menu", "Relsev") != null)
			{
				try
				{
					MenuPage.Register();
				}
				catch (Exception ex)
				{
					Log.Warning("Mod Menu page: " + ex.Message);
				}
			}
		}

		internal static void Save()
		{
			_category.SaveToFile(false);
		}

		public override void OnUpdate()
		{
			WheelSelect.Tick();
			try
			{
				Death.Tick();
			}
			catch (Exception ex)
			{
				Log.Error((object)ex);
				Death.Reset();
			}
			try
			{
				ControlsRow.Tick();
			}
			catch (Exception ex2)
			{
				Log.Warning("Options → Controls row: " + ex2.Message);
			}
		}
	}
	internal static class Strings
	{
		public const string Enabled = "enabled";

		public const string Key = "key";

		public const string HoldTime = "hold_time";

		public const string Seconds = "seconds";

		public const string Mode = "mode";

		public const string Revivable = "revivable";

		public const string NoRevives = "no_revives";

		public const string Hint = "hint";

		public const string KeyInControls = "key_in_controls";

		public const string Gamepad = "gamepad";

		public const string NoButton = "no_button";

		public const string DieHold = "die_hold";

		private static readonly string[] Keys = new string[12]
		{
			"enabled", "key", "hold_time", "seconds", "mode", "revivable", "no_revives", "hint", "key_in_controls", "gamepad",
			"no_button", "die_hold"
		};

		private static readonly Dictionary<string, string[]> Table = new Dictionary<string, string[]>
		{
			["en"] = new string[12]
			{
				"Enabled", "Key to hold", "Hold time", "{0} s", "Death", "Can be revived", "No revives", "No revives: if you are the last one standing (solo or host), the run ends right away. Otherwise it's a normal death.", "Key and controller button: Options → Controls → Die (hold). Keybinds Unlocked handles them.", "Controller button",
				"None", "Die (hold)"
			},
			["ru"] = new string[12]
			{
				"Включено", "Клавиша (удерживать)", "Сколько держать", "{0} с", "Смерть", "С воскрешением", "Без воскрешений", "Без воскрешений: если вы последний живой (соло или хост), забег сразу заканчивается. Иначе — обычная смерть.", "Клавиша и кнопка контроллера — в Настройки → Управление → «Умереть (удерживать)», их задаёт Keybinds Unlocked.", "Кнопка контроллера",
				"Нет", "Умереть (удерживать)"
			},
			["uk"] = new string[12]
			{
				"Увімкнено", "Клавіша (утримувати)", "Скільки тримати", "{0} с", "Смерть", "З воскресінням", "Без воскресінь", "Без воскресінь: якщо ви останній живий (соло або хост), забіг одразу закінчується. Інакше — звичайна смерть.", "Клавіша й кнопка контролера — у Налаштування → Керування → «Померти (утримувати)», їх задає Keybinds Unlocked.", "Кнопка контролера",
				"Немає", "Померти (утримувати)"
			},
			["de"] = new string[12]
			{
				"Aktiviert", "Taste (halten)", "Haltedauer", "{0} s", "Tod", "Wiederbelebbar", "Ohne Wiederbelebung", "Ohne Wiederbelebung: Lebst du als Letzter (solo oder als Host), endet der Lauf sofort. Sonst ist es ein normaler Tod.", "Taste und Controller-Knopf: Optionen → Steuerung → „Sterben (halten)“, festgelegt über Keybinds Unlocked.", "Controller-Taste",
				"Keine", "Sterben (halten)"
			},
			["fr"] = new string[12]
			{
				"Activé", "Touche (maintenir)", "Durée d'appui", "{0} s", "Mort", "Réanimable", "Sans réanimation", "Sans réanimation : si vous êtes le dernier en vie (solo ou hôte), la partie se termine aussitôt. Sinon, c'est une mort normale.", "Touche et bouton de manette : Options → Commandes → « Mourir (maintenir) », gérés par Keybinds Unlocked.", "Bouton de manette",
				"Aucun", "Mourir (maintenir)"
			},
			["it"] = new string[12]
			{
				"Attivo", "Tasto (tieni premuto)", "Durata pressione", "{0} s", "Morte", "Rianimabile", "Senza rianimazioni", "Senza rianimazioni: se sei l'ultimo in vita (in solitario o come host), la partita finisce subito. Altrimenti è una morte normale.", "Tasto e pulsante del controller: Opzioni → Comandi → «Morire (tieni premuto)», gestiti da Keybinds Unlocked.", "Pulsante del controller",
				"Nessuno", "Morire (tieni premuto)"
			},
			["nl"] = new string[12]
			{
				"Aan", "Toets (ingedrukt houden)", "Houdtijd", "{0} s", "Dood", "Reanimeerbaar", "Zonder reanimaties", "Zonder reanimaties: leef jij als laatste (solo of host), dan eindigt de run meteen. Anders is het een gewone dood.", "Toets en controllerknop: Opties → Besturing → ‘Sterven (ingedrukt houden)’, ingesteld via Keybinds Unlocked.", "Controllerknop",
				"Geen", "Sterven (ingedrukt houden)"
			},
			["pl"] = new string[12]
			{
				"Włączone", "Klawisz (przytrzymaj)", "Czas przytrzymania", "{0} s", "Śmierć", "Z wskrzeszeniem", "Bez wskrzeszeń", "Bez wskrzeszeń: jeśli żyjesz jako ostatni (solo lub host), wyprawa kończy się od razu. W innym wypadku to zwykła śmierć.", "Klawisz i przycisk kontrolera: Opcje → Sterowanie → „Zgiń (przytrzymaj)”, ustawia je Keybinds Unlocked.", "Przycisk kontrolera",
				"Brak", "Zgiń (przytrzymaj)"
			},
			["pt-br"] = new string[12]
			{
				"Ativado", "Tecla (segurar)", "Tempo segurando", "{0} s", "Morte", "Pode ser revivido", "Sem reviver", "Sem reviver: se você for o último vivo (solo ou anfitrião), a partida termina na hora. Caso contrário, é uma morte normal.", "Tecla e botão do controle: Opções → Controles → “Morrer (segurar)”, definidos pelo Keybinds Unlocked.", "Botão do controle",
				"Nenhum", "Morrer (segurar)"
			},
			["pt"] = new string[12]
			{
				"Ativado", "Tecla (manter premida)", "Tempo a premir", "{0} s", "Morte", "Pode ser reanimado", "Sem reanimações", "Sem reanimações: se fores o último vivo (a solo ou anfitrião), a partida termina de imediato. Caso contrário, é uma morte normal.", "Tecla e botão do comando: Opções → Controlos → «Morrer (manter premido)», definidos pelo Keybinds Unlocked.", "Botão do comando",
				"Nenhum", "Morrer (manter premido)"
			},
			["es"] = new string[12]
			{
				"Activado", "Tecla (mantener)", "Tiempo de pulsación", "{0} s", "Muerte", "Con reanimación", "Sin reanimaciones", "Sin reanimaciones: si eres el último con vida (en solitario o como anfitrión), la partida termina al instante. Si no, es una muerte normal.", "Tecla y botón del mando: Opciones → Controles → «Morir (mantener)», los gestiona Keybinds Unlocked.", "Botón del mando",
				"Ninguno", "Morir (mantener)"
			},
			["es-mx"] = new string[12]
			{
				"Activado", "Tecla (mantener)", "Tiempo presionado", "{0} s", "Muerte", "Con reanimación", "Sin reanimaciones", "Sin reanimaciones: si eres el último con vida (en solitario o como anfitrión), la partida termina al instante. Si no, es una muerte normal.", "Tecla y botón del control: Opciones → Controles → «Morir (mantener)», los gestiona Keybinds Unlocked.", "Botón del control",
				"Ninguno", "Morir (mantener)"
			},
			["ja"] = new string[12]
			{
				"有効", "長押しするキー", "長押し時間", "{0} 秒", "死亡", "復活あり", "復活なし", "復活なし:最後の生存者なら(ソロまたはホスト)ランはすぐに終了します。それ以外は通常の死亡です。", "キーとコントローラーのボタンは「オプション → 操作 → 死亡(長押し)」で設定します(Keybinds Unlocked)。", "コントローラーのボタン",
				"なし", "死亡(長押し)"
			},
			["ko"] = new string[12]
			{
				"사용", "누르고 있을 키", "누르는 시간", "{0}초", "사망", "부활 가능", "부활 없음", "부활 없음: 마지막 생존자라면(솔로 또는 호스트) 런이 즉시 끝납니다. 그 외에는 일반 사망입니다.", "키와 컨트롤러 버튼은 옵션 → 조작 → '사망 (길게 누르기)'에서 설정합니다 (Keybinds Unlocked).", "컨트롤러 버튼",
				"없음", "사망 (길게 누르기)"
			},
			["zh-hans"] = new string[12]
			{
				"启用", "长按按键", "长按时间", "{0} 秒", "死亡", "可复活", "不复活", "不复活:如果你是最后的幸存者(单人或主机),本局立即结束。否则为普通死亡。", "按键和手柄按钮在“选项 → 操作 → 死亡(长按)”中设置(Keybinds Unlocked)。", "手柄按钮",
				"无", "死亡(长按)"
			},
			["zh-hant"] = new string[12]
			{
				"啟用", "長按按鍵", "長按時間", "{0} 秒", "死亡", "可復活", "不復活", "不復活:如果你是最後的倖存者(單人或主機),本局立即結束。否則為普通死亡。", "按鍵和手把按鈕在「選項 → 操作 → 死亡(長按)」中設定(Keybinds Unlocked)。", "手把按鈕",
				"無", "死亡(長按)"
			},
			["th"] = new string[12]
			{
				"เป\u0e34ดใช\u0e49", "ป\u0e38\u0e48มท\u0e35\u0e48ต\u0e49องกดค\u0e49าง", "เวลากดค\u0e49าง", "{0} ว\u0e34", "ความตาย", "ช\u0e38บช\u0e35ว\u0e34ตได\u0e49", "ไม\u0e48ช\u0e38บช\u0e35ว\u0e34ต", "ไม\u0e48ช\u0e38บช\u0e35ว\u0e34ต: ถ\u0e49าค\u0e38ณเป\u0e47นคนส\u0e38ดท\u0e49ายท\u0e35\u0e48รอด (เล\u0e48นคนเด\u0e35ยวหร\u0e37อโฮสต\u0e4c) รอบจะจบท\u0e31นท\u0e35 ไม\u0e48เช\u0e48นน\u0e31\u0e49นจะเป\u0e47นการตายปกต\u0e34", "ป\u0e38\u0e48มและป\u0e38\u0e48มคอนโทรลเลอร\u0e4cต\u0e31\u0e49งได\u0e49ท\u0e35\u0e48 ต\u0e31วเล\u0e37อก → การควบค\u0e38ม → ตาย (กดค\u0e49าง) (Keybinds Unlocked)", "ป\u0e38\u0e48มคอนโทรลเลอร\u0e4c",
				"ไม\u0e48ม\u0e35", "ตาย (กดค\u0e49าง)"
			}
		};

		private static readonly Localizer Text = new Localizer(Keys, Table, () => HoldToDieMod.Log);

		public static string Get(string key)
		{
			return Text.Get(key);
		}

		public static void Bind(TMP_Text text, string key)
		{
			Text.Bind(text, key);
		}
	}
}