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