Decompiled source of GK2 Faster Doors v0.3.3
GK2FasterDoors.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Cysharp.Threading.Tasks; using GK2.Framework; using HarmonyLib; using LazyBearTechnology; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Experimental.Rendering; using UnityEngine.Profiling; using UnityEngine.SceneManagement; using UnityEngine.Scripting; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal sealed class ConfigurationManagerAttributes { public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput); public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public CustomHotkeyDrawerFunc CustomHotkeyDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func<object, string> ObjToStr; public Func<string, object> StrToObj; } namespace GK2FasterDoors { internal static class Sec { public const string Transitions = "1 Transitions"; public const string Cleanup = "2 Memory clean-up"; public const string Pause = "3 Pause menu"; public const string Doors = "4 Doors"; public const string Debug = "Debug"; } internal static class CfgMigrate { public static void Run(ConfigFile cfg) { Carry(cfg, "Transitions", "Enabled", "1 Transitions", "FasterTransitions"); Carry(cfg, "Transitions", "FadeSeconds", "1 Transitions", "FadeSeconds"); Carry(cfg, "Transitions", "BlackPauseSeconds", "1 Transitions", "BlackPauseSeconds"); Carry(cfg, "General", "Enabled", "2 Memory clean-up", "SkipCleanup"); Carry(cfg, "General", "FullCleanupEveryDoors", "2 Memory clean-up", "FullCleanupEveryDoors"); Carry(cfg, "General", "FullCleanupAfterGrowthMB", "2 Memory clean-up", "FullCleanupAfterGrowthMB"); Carry(cfg, "General", "GcOnSkippedDoors", "2 Memory clean-up", "GcOnSkippedDoors"); Carry(cfg, "Hotkeys", "Toggle", "Debug", "Toggle"); } private static void Carry(ConfigFile cfg, string oldSection, string oldKey, string newSection, string newKey) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown try { ConfigDefinition key = new ConfigDefinition(oldSection, oldKey); ConfigDefinition key2 = new ConfigDefinition(newSection, newKey); Dictionary<ConfigDefinition, string> dictionary = Orphans(cfg); if (dictionary != null && dictionary.TryGetValue(key, out var value)) { if (!dictionary.ContainsKey(key2)) { dictionary[key2] = value; } dictionary.Remove(key); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Settings: could not carry " + oldSection + "." + oldKey + " over: " + ex.Message)); } } private static Dictionary<ConfigDefinition, string> Orphans(ConfigFile cfg) { PropertyInfo property = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property != null)) { return null; } return property.GetValue(cfg, null) as Dictionary<ConfigDefinition, string>; } private static void Drop(ConfigFile cfg, string section, string key) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown try { Orphans(cfg)?.Remove(new ConfigDefinition(section, key)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Settings: could not drop " + section + "." + key + ": " + ex.Message)); } } } internal static class Cm { public static ConfigDescription D(string description, string name, int order, bool advanced = false, AcceptableValueBase range = null, bool browsable = true) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { DispName = name, Order = 1000 - order }; if (advanced) { configurationManagerAttributes.IsAdvanced = true; } if (!browsable) { configurationManagerAttributes.Browsable = false; } return new ConfigDescription(description, range, new object[1] { configurationManagerAttributes }); } } internal static class Framework { private sealed class Entry : Gk2ModBase { private readonly Gk2ModMetadata _meta = new Gk2ModMetadata("gk2.fasterdoors", "GK2 Faster Doors", "SaintArchI", "0.3.3", FwText.L("Faster doors: no black screen inside an area, quicker fades elsewhere, the memory clean-up where the screen is black anyway, and an instant pause menu."), false, false); private readonly IReadOnlyList<Gk2ModDependency> _deps = (IReadOnlyList<Gk2ModDependency>)(object)new Gk2ModDependency[1] { new Gk2ModDependency("ru.superman4eg.gk2.framework", "0.1.0", "0.2.0", false) }; public override Gk2ModMetadata Metadata => _meta; public override IReadOnlyList<Gk2ModDependency> Dependencies => _deps; public override void OnRegister(Gk2ModContext context) { Gk2Settings settings = context.Settings; Toggle(settings, Plugin.QuickEnabled); Toggle(settings, Plugin.HardCut); Float(settings, Plugin.FadeSeconds, 0.01f); Float(settings, Plugin.BlackPauseSeconds, 0.01f); Toggle(settings, Plugin.Enabled); Toggle(settings, Plugin.WhileSleeping); Toggle(settings, Plugin.WhenRamLow); Int(settings, Plugin.EveryDoors, 1); Int(settings, Plugin.GrowthMb, 50); Toggle(settings, Plugin.InstantPause); Toggle(settings, Plugin.WalkIn); Key(settings, Plugin.ToggleKey); Toggle(settings, Plugin.Verbose); } private static string Name(ConfigEntryBase e) { ConfigurationManagerAttributes configurationManagerAttributes = Attrs(e); return FwText.L((configurationManagerAttributes != null && configurationManagerAttributes.DispName != null) ? configurationManagerAttributes.DispName : e.Definition.Key); } private static string Desc(ConfigEntryBase e) { return FwText.L((e.Description != null) ? e.Description.Description : ""); } private static int Order(ConfigEntryBase e) { ConfigurationManagerAttributes configurationManagerAttributes = Attrs(e); if (configurationManagerAttributes == null || !configurationManagerAttributes.Order.HasValue) { return 0; } return 1000 - configurationManagerAttributes.Order.Value; } private static ConfigurationManagerAttributes Attrs(ConfigEntryBase e) { if (e.Description == null || e.Description.Tags == null) { return null; } object[] tags = e.Description.Tags; for (int i = 0; i < tags.Length; i++) { if (tags[i] is ConfigurationManagerAttributes result) { return result; } } return null; } private static void Toggle(Gk2Settings s, ConfigEntry<bool> e) { s.AddToggle(((ConfigEntryBase)e).Definition.Section, ((ConfigEntryBase)e).Definition.Key, (bool)((ConfigEntryBase)e).DefaultValue, Name((ConfigEntryBase)(object)e), Desc((ConfigEntryBase)(object)e), Order((ConfigEntryBase)(object)e)); } private static void Float(Gk2Settings s, ConfigEntry<float> e, float step) { AcceptableValueRange<float> val = ((ConfigEntryBase)e).Description.AcceptableValues as AcceptableValueRange<float>; s.AddFloatSlider(((ConfigEntryBase)e).Definition.Section, ((ConfigEntryBase)e).Definition.Key, (float)((ConfigEntryBase)e).DefaultValue, val?.MinValue ?? 0f, val?.MaxValue ?? 1f, Name((ConfigEntryBase)(object)e), Desc((ConfigEntryBase)(object)e), step, Order((ConfigEntryBase)(object)e)); } private static void Int(Gk2Settings s, ConfigEntry<int> e, int step) { AcceptableValueRange<int> val = ((ConfigEntryBase)e).Description.AcceptableValues as AcceptableValueRange<int>; s.AddIntSlider(((ConfigEntryBase)e).Definition.Section, ((ConfigEntryBase)e).Definition.Key, (int)((ConfigEntryBase)e).DefaultValue, val?.MinValue ?? 0, val?.MaxValue ?? 100, Name((ConfigEntryBase)(object)e), Desc((ConfigEntryBase)(object)e), step, Order((ConfigEntryBase)(object)e)); } private static void Key(Gk2Settings s, ConfigEntry<KeyboardShortcut> e) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) s.AddKeybind(((ConfigEntryBase)e).Definition.Section, ((ConfigEntryBase)e).Definition.Key, (KeyboardShortcut)((ConfigEntryBase)e).DefaultValue, Name((ConfigEntryBase)(object)e), Desc((ConfigEntryBase)(object)e), Order((ConfigEntryBase)(object)e)); } } public const string FrameworkGuid = "ru.superman4eg.gk2.framework"; public static void TryRegister(BaseUnityPlugin plugin) { try { if (AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "GK2.Framework")) { Register(plugin); } } catch (Exception ex) { Plugin.Log.LogInfo((object)("Mod Framework not used: " + ex.Message)); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void Register(BaseUnityPlugin plugin) { foreach (RegisteredMod mod in FrameworkApi.Mods) { if (mod != null && mod.Metadata != null && string.Equals(mod.Metadata.Id, "gk2.fasterdoors", StringComparison.OrdinalIgnoreCase)) { return; } } SectionNames.Install(); FrameworkApi.RegisterMod((IGk2Mod)(object)new Entry(), plugin.Config); Plugin.Log.LogInfo((object)"Registered in GK2 Mod Framework"); } } internal static class FwText { private static readonly string[] Langs = new string[10] { "ru", "de", "fr", "es", "pt", "pl", "tr", "ja", "zh", "ko" }; private static int _idx = -2; private static readonly Dictionary<string, string[]> Table = new Dictionary<string, string[]> { { "Skip the game's memory clean-up on most doors and teleports. It still runs in full when due (see below).", new string[10] { "Пропускать очистку памяти игры на большинстве дверей и телепортов. Полная очистка всё равно выполняется, когда пора (см. ниже).", "Das Speicher-Aufräumen des Spiels bei den meisten Türen und Teleports überspringen. Wenn es fällig ist, läuft es trotzdem vollständig (siehe unten).", "Sauter le nettoyage mémoire du jeu à la plupart des portes et téléportations. Il s'exécute quand même en entier quand c'est dû (voir plus bas).", "Saltar la limpieza de memoria del juego en la mayoría de puertas y teletransportes. Se hace completa igualmente cuando toca (ver abajo).", "Pular a limpeza de memória do jogo na maioria das portas e teleportes. Ela ainda roda completa quando for a hora (veja abaixo).", "Pomijaj czyszczenie pamięci gry przy większości drzwi i teleportów. Pełne czyszczenie i tak się wykona, gdy przyjdzie pora (patrz niżej).", "Çoğu kapı ve ışınlanmada oyunun bellek temizliğini atlayın. Zamanı gelince yine tam olarak çalışır (aşağıya bakın).", "ほとんどのドアやテレポートでゲームのメモリ掃除を省きます。必要なときは完全に行われます(下記参照)。", "大多数门和传送时跳过游戏的内存清理。到时候仍会完整执行(见下文)。", "대부분의 문과 순간이동에서 게임의 메모리 정리를 건너뜁니다. 때가 되면 여전히 전부 실행됩니다(아래 참고)." } }, { "Full clean-up every N doors", new string[10] { "Полная очистка каждые N дверей", "Volles Aufräumen alle N Türen", "Nettoyage complet toutes les N portes", "Limpieza completa cada N puertas", "Limpeza completa a cada N portas", "Pełne czyszczenie co N drzwi", "Her N kapıda tam temizlik", "Nドアごとに完全な掃除", "每 N 扇门完整清理一次", "N개 문마다 전체 정리" } }, { "Run the full clean-up at least on every Nth door. 1 = every door, as in the unmodded game.", new string[10] { "Полная очистка хотя бы на каждой N-й двери. 1 — на каждой, как в игре без мода.", "Das volle Aufräumen mindestens bei jeder N-ten Tür ausführen. 1 = bei jeder Tür, wie im Spiel ohne Mod.", "Faire le nettoyage complet au moins à chaque Nième porte. 1 = à chaque porte, comme dans le jeu sans mod.", "Hacer la limpieza completa al menos cada N puertas. 1 = en cada puerta, como en el juego sin mods.", "Fazer a limpeza completa pelo menos a cada N portas. 1 = em toda porta, como no jogo sem mod.", "Pełne czyszczenie przynajmniej przy co N-tych drzwiach. 1 = przy każdych, jak w grze bez moda.", "Tam temizliği en az her N. kapıda yapın. 1 = her kapıda, modsuz oyundaki gibi.", "少なくともNドアに1回、完全な掃除を行います。1なら毎回(MODなしと同じ)。", "至少每第 N 扇门完整清理一次。1 = 每扇门都清理,与原版相同。", "적어도 N번째 문마다 전체 정리를 합니다. 1 = 매번, 모드 없는 게임과 같음." } }, { "Also run the full clean-up on the next door once the game's memory has grown by this many MB since the last one.", new string[10] { "Также полная очистка на следующей двери, если память игры выросла на столько МБ с прошлой очистки.", "Das volle Aufräumen auch bei der nächsten Tür ausführen, sobald der Speicher des Spiels seit dem letzten um so viele MB gewachsen ist.", "Faire aussi le nettoyage complet à la porte suivante dès que la mémoire du jeu a augmenté de ce nombre de Mo depuis le dernier.", "Hacer también la limpieza completa en la siguiente puerta cuando la memoria del juego haya crecido estos MB desde la última.", "Fazer também a limpeza completa na próxima porta quando a memória do jogo tiver crescido estes MB desde a última.", "Pełne czyszczenie także przy następnych drzwiach, gdy pamięć gry urośnie o tyle MB od ostatniego.", "Oyunun belleği son temizlikten beri bu kadar MB büyüdüğünde bir sonraki kapıda da tam temizlik yapın.", "前回からゲームのメモリがこのMB分増えたら、次のドアでも完全な掃除を行います。", "自上次清理后游戏内存增长这么多 MB 时,下一扇门也完整清理。", "지난 정리 이후 게임 메모리가 이만큼(MB) 늘면 다음 문에서도 전체 정리를 합니다." } }, { "Faster transitions", new string[10] { "Быстрые переходы", "Schnellere Übergänge", "Transitions plus rapides", "Transiciones más rápidas", "Transições mais rápidas", "Szybsze przejścia", "Daha hızlı geçişler", "速い暗転", "更快的过渡", "빠른 전환" } }, { "Shorter fades and pause for doors you walk through and for map travel. Cutscenes, fights and quest scripts keep the game's timing.", new string[10] { "Короче затемнение и пауза для дверей, в которые вы входите, и для перемещения по карте. Катсцены, бои и сценарии заданий идут с таймингом игры.", "Kürzere Überblendungen und Pause für Türen, durch die du gehst, und für Kartenreisen. Zwischensequenzen, Kämpfe und Quest-Skripte behalten das Timing des Spiels.", "Fondus et pause plus courts pour les portes que vous franchissez et les voyages sur la carte. Cinématiques, combats et scripts de quête gardent le rythme du jeu.", "Fundidos y pausa más cortos en las puertas que cruzas y en los viajes por el mapa. Cinemáticas, combates y guiones de misiones mantienen el ritmo del juego.", "Escurecimentos e pausa mais curtos para as portas que você atravessa e para viagens pelo mapa. Cenas, lutas e roteiros de missões mantêm o tempo do jogo.", "Krótsze ściemnianie i pauza dla drzwi, przez które przechodzisz, i podróży po mapie. Przerywniki, walki i skrypty misji zachowują tempo gry.", "İçinden geçtiğiniz kapılar ve harita yolculuğu için daha kısa kararma ve bekleme. Ara sahneler, dövüşler ve görev betikleri oyunun zamanlamasını korur.", "通るドアとマップ移動の暗転と待ちを短くします。カットシーン・戦闘・クエストの演出はゲームのままです。", "缩短你走过的门和地图旅行的淡入淡出与停顿。过场、战斗和任务脚本保持游戏原有节奏。", "지나가는 문과 지도 이동의 페이드와 멈춤을 줄입니다. 컷신, 전투, 퀘스트 스크립트는 게임의 타이밍을 그대로 따릅니다." } }, { "Fade length", new string[10] { "Длина затемнения", "Überblendungsdauer", "Durée du fondu", "Duración del fundido", "Duração do escurecimento", "Długość ściemniania", "Kararma süresi", "暗転の長さ", "淡入淡出时长", "페이드 길이" } }, { "Black pause", new string[10] { "Пауза в темноте", "Schwarzpause", "Pause au noir", "Pausa en negro", "Pausa no escuro", "Pauza w ciemności", "Karanlıkta bekleme", "暗転中の待ち", "黑屏停顿", "검은 화면 대기" } }, { "Seconds the screen stays black before the move (the game: 0.3).", new string[10] { "Сколько секунд экран остаётся чёрным перед перемещением (в игре: 0,3).", "Sekunden, die der Bildschirm vor dem Wechsel schwarz bleibt (das Spiel: 0,3).", "Secondes pendant lesquelles l'écran reste noir avant le déplacement (le jeu : 0,3).", "Segundos que la pantalla queda en negro antes del cambio (el juego: 0,3).", "Segundos em que a tela fica preta antes da mudança (o jogo: 0,3).", "Ile sekund ekran pozostaje czarny przed przejściem (gra: 0,3).", "Geçişten önce ekranın siyah kaldığı saniye (oyun: 0,3).", "移動前に画面が暗いままの秒数(ゲーム:0.3)。", "移动前保持黑屏的秒数(游戏:0.3)。", "이동 전에 화면이 검게 유지되는 초(게임: 0.3)." } }, { "Detailed log", new string[10] { "Подробный лог", "Ausführliches Protokoll", "Journal détaillé", "Registro detallado", "Log detalhado", "Szczegółowy log", "Ayrıntılı günlük", "詳細ログ", "详细日志", "자세한 로그" } }, { "Log every door's decision and what the full clean-up removed.", new string[10] { "Писать в лог решение по каждой двери и что убрала полная очистка.", "Die Entscheidung jeder Tür und was das volle Aufräumen entfernt hat protokollieren.", "Consigner la décision de chaque porte et ce que le nettoyage complet a retiré.", "Registrar la decisión de cada puerta y lo que quitó la limpieza completa.", "Registrar a decisão de cada porta e o que a limpeza completa removeu.", "Zapisuj w logu decyzję przy każdych drzwiach i co usunęło pełne czyszczenie.", "Her kapının kararını ve tam temizliğin neyi kaldırdığını günlüğe yazın.", "各ドアの判断と、完全な掃除で消えたものをログに書きます。", "记录每扇门的决定以及完整清理移除了什么。", "문마다의 결정과 전체 정리가 지운 것을 로그에 남깁니다." } }, { "Skip the game's memory clean-up at doors", new string[10] { "Пропускать очистку памяти игры на дверях", "Speicherbereinigung des Spiels an Türen auslassen", "Sauter le nettoyage de la mémoire du jeu aux portes", "Omitir la limpieza de memoria del juego en las puertas", "Pular a limpeza de memória do jogo nas portas", "Pomijaj czyszczenie pamięci gry przy drzwiach", "Kapılarda oyunun bellek temizliğini atlayın", "ドアでのゲームのメモリ掃除を省く", "过门时跳过游戏的内存清理", "문에서 게임의 메모리 정리 건너뛰기" } }, { "Full clean-up after memory grows (MB)", new string[10] { "Полная очистка, когда память выросла (МБ)", "Volle Bereinigung nach Speicherzuwachs (MB)", "Nettoyage complet après hausse de la mémoire (Mo)", "Limpieza completa tras crecer la memoria (MB)", "Limpeza completa quando a memória crescer (MB)", "Pełne czyszczenie po wzroście pamięci (MB)", "Bellek büyüyünce tam temizlik (MB)", "メモリ増加後に完全な掃除(MB)", "内存增长后完整清理(MB)", "메모리 증가 후 전체 정리(MB)" } }, { "Switch the mod off / on (to compare)", new string[10] { "Выключить / включить мод (для сравнения)", "Mod aus / an (zum Vergleich)", "Couper / remettre le mod (pour comparer)", "Apagar / encender el mod (para comparar)", "Desligar / ligar o mod (para comparar)", "Wyłącz / włącz mod (do porównania)", "Modu kapatın / açın (karşılaştırmak için)", "Modのオフ / オン(比較用)", "关闭 / 开启模组(用于对比)", "모드 끄기 / 켜기(비교용)" } }, { "For comparing doors with the unmodded game: switches the whole mod off and on while playing. Not set by default; not saved, on again after a restart.", new string[10] { "Чтобы сравнить двери с игрой без мода: выключает и включает весь мод прямо в игре. По умолчанию не задана; не сохраняется, после перезапуска мод снова включён.", "Zum Vergleich der Türen mit dem Spiel ohne Mod: schaltet die ganze Mod im Spiel aus und an. Standardmäßig nicht belegt; nicht gespeichert, nach einem Neustart wieder an.", "Pour comparer les portes avec le jeu sans mod : coupe et remet tout le mod en jeu. Aucune touche par défaut ; non enregistré, actif à nouveau après un redémarrage.", "Para comparar las puertas con el juego sin mod: apaga y enciende todo el mod mientras juegas. Sin tecla por defecto; no se guarda, vuelve a estar activo al reiniciar.", "Para comparar as portas com o jogo sem mod: desliga e liga o mod inteiro durante o jogo. Sem tecla padrão; não é salvo, volta a ligar ao reiniciar.", "Do porównania drzwi z grą bez moda: wyłącza i włącza cały mod podczas gry. Domyślnie bez klawisza; nie zapisuje się, po restarcie mod znów działa.", "Kapıları modsuz oyunla karşılaştırmak için: oynarken tüm modu kapatıp açar. Varsayılan tuş yok; kaydedilmez, yeniden başlatınca yine açık.", "ドアを素のゲームと比べるため、プレイ中にmod全体をオフ・オンする。初期設定ではキーなし。保存されず、再起動すると再びオン。", "用于与无模组的游戏对比门:游戏中开关整个模组。默认不绑定;不会保存,重启后重新开启。", "문을 모드 없는 게임과 비교하기 위해: 플레이 중 모드 전체를 끄고 켬. 기본 키 없음, 저장되지 않으며 재시작하면 다시 켜짐." } }, { "Faster doors: no black screen inside an area, quicker fades elsewhere, the memory clean-up where the screen is black anyway, and an instant pause menu.", new string[10] { "Быстрые двери: без чёрного экрана внутри локации, короче затемнения в остальных местах, очистка памяти там, где экран и так чёрный, и мгновенное меню паузы.", "Schnellere Türen: kein schwarzer Bildschirm innerhalb eines Gebiets, kürzere Blenden anderswo, Speicherbereinigung dort, wo der Bildschirm ohnehin schwarz ist, und ein sofortiges Pausenmenü.", "Portes plus rapides : pas d'écran noir dans une même zone, fondus plus courts ailleurs, nettoyage de la mémoire là où l'écran est noir de toute façon, et un menu pause instantané.", "Puertas más rápidas: sin pantalla negra dentro de una zona, fundidos más cortos en otros sitios, limpieza de memoria donde la pantalla ya está negra y un menú de pausa instantáneo.", "Portas mais rápidas: sem tela preta dentro de uma área, transições mais curtas em outros lugares, limpeza de memória onde a tela já está preta e um menu de pausa instantâneo.", "Szybsze drzwi: bez czarnego ekranu w obrębie obszaru, krótsze ściemnienia gdzie indziej, czyszczenie pamięci tam, gdzie ekran i tak jest czarny, oraz natychmiastowe menu pauzy.", "Daha hızlı kapılar: bir alan içinde siyah ekran yok, başka yerlerde daha kısa kararmalar, bellek temizliği zaten siyah olan ekranda ve anında açılan duraklatma menüsü.", "ドアが速くなる:同じエリア内では暗転なし、それ以外は短い暗転、メモリの掃除はもともと暗い画面で行い、ポーズメニューも即座に開く。", "更快的门:同一区域内没有黑屏,其他地方黑屏更短,内存清理放在本就黑屏的时候,暂停菜单立即打开。", "더 빠른 문: 같은 지역 안에서는 검은 화면 없음, 다른 곳은 짧은 암전, 메모리 정리는 어차피 검은 화면일 때, 일시 정지 메뉴는 즉시 열림." } }, { "No black screen inside an area", new string[10] { "Без чёрного экрана внутри локации", "Kein schwarzer Bildschirm innerhalb eines Gebiets", "Pas d'écran noir dans une même zone", "Sin pantalla negra dentro de una zona", "Sem tela preta dentro de uma área", "Bez czarnego ekranu w obrębie obszaru", "Bir alan içinde siyah ekran yok", "同じエリア内では暗転なし", "同一区域内没有黑屏", "같은 지역 안에서는 검은 화면 없음" } }, { "Doors you walk through and map travel inside the same area skip the black screen: you are on the other side in the next frame.", new string[10] { "Двери, через которые вы проходите, и перемещение по карте внутри одной локации обходятся без чёрного экрана: в следующем кадре вы уже на той стороне.", "Türen, durch die du gehst, und Kartenreisen innerhalb desselben Gebiets überspringen den schwarzen Bildschirm: im nächsten Bild bist du auf der anderen Seite.", "Les portes que vous franchissez et les voyages sur la carte dans une même zone sautent l'écran noir : vous êtes de l'autre côté à l'image suivante.", "Las puertas que cruzas y los viajes por el mapa dentro de la misma zona se saltan la pantalla negra: en el siguiente fotograma ya estás al otro lado.", "Portas que você atravessa e viagens pelo mapa dentro da mesma área pulam a tela preta: no quadro seguinte você já está do outro lado.", "Drzwi, przez które przechodzisz, i podróże po mapie w tym samym obszarze pomijają czarny ekran: w następnej klatce jesteś po drugiej stronie.", "Aynı alan içinde geçtiğiniz kapılar ve harita yolculukları siyah ekranı atlar: bir sonraki karede öbür taraftasınız.", "同じエリア内で通るドアと地図での移動は暗転を飛ばし、次のフレームにはもう向こう側にいる。", "同一区域内你穿过的门和地图旅行跳过黑屏:下一帧你就在另一边。", "같은 지역 안에서 지나가는 문과 지도 이동은 검은 화면을 건너뛰어 다음 프레임에 바로 건너편에 있음." } }, { "Seconds of the fade to black and of the fade back, each, where a door still fades (the game: 0.3).", new string[10] { "Секунды затемнения и обратного появления, каждое, там, где дверь ещё затемняет экран (в игре: 0.3).", "Sekunden der Abblende und der Aufblende, jeweils, wo eine Tür noch abblendet (das Spiel: 0.3).", "Secondes du fondu au noir et du retour, chacun, là où une porte fond encore au noir (le jeu : 0.3).", "Segundos del fundido a negro y de la vuelta, cada uno, donde una puerta aún funde a negro (el juego: 0.3).", "Segundos do escurecimento e do retorno, cada um, onde uma porta ainda escurece (o jogo: 0.3).", "Sekundy ściemnienia i rozjaśnienia, każde, tam gdzie drzwi wciąż ściemniają ekran (gra: 0.3).", "Bir kapının hâlâ kararttığı yerlerde, kararma ve geri açılma saniyesi, her biri (oyun: 0.3).", "ドアがまだ暗転する場所での暗転と明転の秒数、それぞれ(ゲーム: 0.3)。", "仍会黑屏的门,淡出和淡入各自的秒数(游戏:0.3)。", "문이 여전히 암전되는 곳에서 암전과 복귀 각각의 초(게임: 0.3)." } }, { "Clean up while you sleep", new string[10] { "Очищать память во сне", "Beim Schlafen aufräumen", "Nettoyer pendant le sommeil", "Limpiar mientras duermes", "Limpar enquanto você dorme", "Czyść pamięć podczas snu", "Uyurken temizleyin", "眠っている間に掃除", "睡觉时清理", "자는 동안 정리" } }, { "Run the game's memory clean-up while you sleep, behind the black screen, so memory doesn't keep growing over a long session.", new string[10] { "Запускать очистку памяти игры, пока вы спите, за чёрным экраном, чтобы память не росла за долгую игру.", "Die Speicherbereinigung des Spiels läuft, während du schläfst, hinter dem schwarzen Bildschirm, damit der Speicher in langen Sitzungen nicht immer weiter wächst.", "Lance le nettoyage de la mémoire du jeu pendant votre sommeil, derrière l'écran noir, pour que la mémoire ne grossisse pas au fil d'une longue session.", "Ejecuta la limpieza de memoria del juego mientras duermes, tras la pantalla negra, para que la memoria no siga creciendo en una sesión larga.", "Executa a limpeza de memória do jogo enquanto você dorme, atrás da tela preta, para a memória não crescer numa sessão longa.", "Uruchamia czyszczenie pamięci gry podczas snu, za czarnym ekranem, żeby pamięć nie rosła w długiej sesji.", "Oyunun bellek temizliğini siz uyurken siyah ekranın arkasında çalıştırır; böylece uzun oturumda bellek büyümeye devam etmez.", "眠っている間、暗い画面の裏でゲームのメモリ掃除を行い、長時間のプレイでメモリが増え続けないようにする。", "在你睡觉时于黑屏后运行游戏的内存清理,避免长时间游玩时内存不断增长。", "자는 동안 검은 화면 뒤에서 게임의 메모리 정리를 실행해 오래 플레이해도 메모리가 계속 늘지 않게 함." } }, { "Clean up at a door when RAM runs low", new string[10] { "Очистка на двери, когда мало памяти", "Bei knappem RAM an einer Tür aufräumen", "Nettoyer à une porte quand la RAM manque", "Limpiar en una puerta si falta RAM", "Limpar numa porta quando faltar RAM", "Czyść przy drzwiach, gdy brakuje RAM", "RAM azalınca bir kapıda temizleyin", "RAMが足りないときはドアで掃除", "内存不足时在门口清理", "RAM이 부족하면 문에서 정리" } }, { "If memory has grown a lot and your PC is running short of RAM, the next door goes through black once and cleans up there.", new string[10] { "Если память сильно выросла и компьютеру не хватает RAM, следующая дверь один раз проходит через чёрный экран и очищает память там.", "Ist der Speicher stark gewachsen und wird der RAM knapp, geht die nächste Tür einmal durch Schwarz und räumt dort auf.", "Si la mémoire a beaucoup grossi et que votre PC manque de RAM, la porte suivante passe une fois par le noir et nettoie à ce moment-là.", "Si la memoria ha crecido mucho y a tu PC le falta RAM, la siguiente puerta pasa una vez por negro y limpia allí.", "Se a memória cresceu muito e falta RAM no PC, a próxima porta passa uma vez pelo preto e limpa ali.", "Jeśli pamięć mocno urosła, a komputerowi brakuje RAM, następne drzwi raz przejdą przez czarny ekran i tam wyczyszczą pamięć.", "Bellek çok büyüdüyse ve bilgisayarınızda RAM azaldıysa, sonraki kapı bir kez siyah ekrandan geçer ve orada temizler.", "メモリが大きく増え、PCのRAMが足りなくなったら、次のドアで一度だけ暗転して掃除する。", "如果内存增长很多且电脑内存不足,下一道门会黑屏一次并在那时清理。", "메모리가 많이 늘고 PC의 RAM이 부족하면 다음 문에서 한 번 검은 화면을 거쳐 정리함." } }, { "Instant pause menu", new string[10] { "Мгновенное меню паузы", "Sofortiges Pausenmenü", "Menu pause instantané", "Menú de pausa instantáneo", "Menu de pausa instantâneo", "Natychmiastowe menu pauzy", "Anında duraklatma menüsü", "ポーズメニューを即座に開く", "即时暂停菜单", "즉시 일시 정지 메뉴" } }, { "Esc opens the pause menu at once. The game first encodes a screenshot for its bug-report button; here that happens in the background.", new string[10] { "Esc сразу открывает меню паузы. Игра сначала кодирует скриншот для кнопки отчёта об ошибке; здесь это делается в фоне.", "Esc öffnet das Pausenmenü sofort. Das Spiel kodiert vorher einen Screenshot für seinen Fehlerbericht-Knopf; hier geschieht das im Hintergrund.", "Échap ouvre le menu pause tout de suite. Le jeu encode d'abord une capture pour son bouton de rapport de bug ; ici cela se fait en arrière-plan.", "Esc abre el menú de pausa al instante. El juego primero codifica una captura para su botón de informe de errores; aquí se hace en segundo plano.", "Esc abre o menu de pausa na hora. O jogo primeiro codifica uma captura para o botão de relatar bug; aqui isso é feito em segundo plano.", "Esc od razu otwiera menu pauzy. Gra najpierw koduje zrzut ekranu dla przycisku zgłaszania błędów; tutaj dzieje się to w tle.", "Esc duraklatma menüsünü hemen açar. Oyun önce hata bildirme düğmesi için bir ekran görüntüsü kodlar; burada bu arka planda olur.", "Escでポーズメニューがすぐ開く。ゲームは先に不具合報告ボタン用のスクリーンショットを変換するが、ここではそれを裏で行う。", "Esc 立即打开暂停菜单。游戏会先为错误报告按钮编码截图;这里改为在后台进行。", "Esc로 일시 정지 메뉴가 바로 열림. 게임은 먼저 버그 신고 버튼용 스크린샷을 인코딩하는데, 여기서는 이를 백그라운드에서 함." } }, { "Walk into a door to use it", new string[10] { "Входить в дверь без E", "Türen durch Hineinlaufen benutzen", "Entrer dans une porte sans E", "Entrar por una puerta sin E", "Entrar numa porta sem E", "Wchodzić w drzwi bez E", "Kapıya yürüyerek girin", "Eなしでドアに歩いて入る", "走进门即可使用", "E 없이 문으로 걸어 들어가기" } }, { "Walking into a door uses it, without pressing E. Only doors you could use with E right now; never a door with a cutscene or a quest waiting on it, and never while you carry a body or a zombie. After you arrive you can't walk straight back for a moment.", new string[10] { "Если войти в дверь, она сработает без нажатия E. Только двери, которыми можно воспользоваться по E прямо сейчас; никогда двери, где ждёт сцена или квест, и никогда с телом или зомби на плечах. После прихода на миг нельзя сразу пройти обратно.", "Wer in eine Tür hineinläuft, benutzt sie, ohne E zu drücken. Nur Türen, die du gerade mit E benutzen könntest; nie eine Tür mit wartender Zwischensequenz oder Quest und nie mit einem Körper oder Zombie auf den Schultern. Nach der Ankunft geht es einen Moment nicht gleich zurück.", "Entrer dans une porte l'utilise, sans appuyer sur E. Seulement les portes que vous pourriez utiliser avec E à l'instant ; jamais une porte avec une cinématique ou une quête en attente, ni en portant un corps ou un zombie. Après l'arrivée, impossible de repasser aussitôt.", "Entrar en una puerta la usa, sin pulsar E. Solo puertas que podrías usar con E ahora mismo; nunca una puerta con una cinemática o misión pendiente, ni cargando un cuerpo o un zombi. Tras llegar no puedes volver de inmediato.", "Entrar numa porta a usa, sem apertar E. Só portas que você poderia usar com E agora; nunca uma porta com cutscene ou missão pendente, nem carregando um corpo ou zumbi. Ao chegar, não dá para voltar logo em seguida.", "Wejście w drzwi używa ich bez naciskania E. Tylko drzwi, których mógłbyś teraz użyć klawiszem E; nigdy drzwi z oczekującą sceną lub misją ani z ciałem lub zombie na barkach. Po przybyciu przez chwilę nie wrócisz od razu.", "Kapıya yürümek, E'ye basmadan onu kullanır. Yalnızca şu an E ile kullanabileceğiniz kapılar; bekleyen ara sahnesi veya görevi olan kapılar ve sırtınızda ceset ya da zombi varken asla. Vardıktan sonra bir an hemen geri dönemezsiniz.", "ドアに歩いて入ると、Eを押さずに使われます。今Eで使えるドアだけで、イベントやクエストが待つドアや、死体やゾンビを担いでいる間は対象外です。到着後しばらくはすぐ戻れません。", "走进门就会使用它,无需按 E。只对此刻能用 E 开的门生效;有过场或任务等待的门、以及背着尸体或僵尸时绝不触发。到达后短时间内不会立刻原路返回。", "문으로 걸어 들어가면 E를 누르지 않아도 문이 사용됩니다. 지금 E로 쓸 수 있는 문만 해당하며, 컷신이나 퀘스트가 걸린 문과 시체나 좀비를 짊어진 동안에는 쓰이지 않습니다. 도착 직후에는 잠시 바로 돌아갈 수 없습니다." } } }; private static int Index { get { if (_idx != -2) { return _idx; } string text = null; try { text = FrameworkLanguage(); } catch { } text = (text ?? "en").ToLowerInvariant(); _idx = -1; for (int i = 0; i < Langs.Length; i++) { if (text.StartsWith(Langs[i])) { _idx = i; break; } } return _idx; } } [MethodImpl(MethodImplOptions.NoInlining)] private static string FrameworkLanguage() { return FrameworkLocalization.CurrentLanguage; } public static string At(string en, string lang) { int num = IndexOf(lang); if (num < 0 || en == null || !Table.TryGetValue(en, out var value) || num >= value.Length || string.IsNullOrEmpty(value[num])) { return en; } return value[num]; } private static int IndexOf(string lang) { lang = (lang ?? "en").ToLowerInvariant(); for (int i = 0; i < Langs.Length; i++) { if (lang.StartsWith(Langs[i])) { return i; } } return -1; } public static string L(string en) { int index = Index; if (index < 0 || en == null || !Table.TryGetValue(en, out var value) || index >= value.Length || string.IsNullOrEmpty(value[index])) { return en; } return value[index]; } } internal static class Hotkey { internal static bool Down(ConfigEntry<KeyboardShortcut> entry, params ConfigEntry<KeyboardShortcut>[] rivals) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) try { if (entry == null) { return false; } if (SaSettings.Capturing()) { return false; } KeyboardShortcut value = entry.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !Input.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey) || !Held(value)) { return false; } int num = ((KeyboardShortcut)(ref value)).Modifiers.Count(); if (rivals != null) { foreach (ConfigEntry<KeyboardShortcut> val in rivals) { if (val != null && val != entry) { KeyboardShortcut value2 = val.Value; if (((KeyboardShortcut)(ref value2)).MainKey == ((KeyboardShortcut)(ref value)).MainKey && ((KeyboardShortcut)(ref value2)).Modifiers.Count() > num && Held(value2)) { return false; } } } } return true; } catch { return false; } } private static bool Held(KeyboardShortcut s) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) foreach (KeyCode modifier in ((KeyboardShortcut)(ref s)).Modifiers) { if (!Input.GetKey(modifier)) { return false; } } return true; } internal static bool TypingText() { try { if (GUIUtility.keyboardControl != 0) { return true; } EventSystem current = EventSystem.current; GameObject val = (((Object)(object)current != (Object)null) ? current.currentSelectedGameObject : null); if ((Object)(object)val == (Object)null) { return false; } TMP_InputField component = val.GetComponent<TMP_InputField>(); if ((Object)(object)component != (Object)null && component.isFocused) { return true; } InputField component2 = val.GetComponent<InputField>(); return (Object)(object)component2 != (Object)null && component2.isFocused; } catch { return false; } } } internal static class PauseMenu { private static FieldInfo _bytes; private static PropertyInfo _capturedAt; private static MethodInfo _run; private static bool _capturing; public static void Patch(Harmony h) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("BugReportScreenshot"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "Capture", new Type[1] { typeof(Action) }, (Type[])null) : null); _bytes = ((type != null) ? AccessTools.Field(type, "bytes") : null); _capturedAt = ((type != null) ? AccessTools.Property(type, "CapturedAtUtc") : null); Type type2 = AccessTools.TypeByName("BugReporter"); _run = ((type2 != null) ? AccessTools.Method(type2, "Run", new Type[1] { typeof(IEnumerator) }, (Type[])null) : null); if (methodInfo == null || _bytes == null || _run == null) { Plugin.Log.LogInfo((object)"Pause menu: the game's screenshot code has changed, the pause menu keeps its own timing"); } else { h.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(PauseMenu), "CapturePre", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Pause menu: " + ex.Message)); } } private static bool CapturePre(Action onDone, bool __runOriginal) { if (!__runOriginal) { return false; } if (!Plugin.InstantPause.Value || !Plugin.IsOn) { return true; } if (_capturing) { return false; } try { PropertyInfo propertyInfo = AccessTools.Property(AccessTools.TypeByName("BugReporter"), "Instance"); if (propertyInfo == null || propertyInfo.GetValue(null, null) == null) { return true; } _capturing = true; _run.Invoke(null, new object[1] { Capture(onDone) }); return false; } catch (Exception ex) { _capturing = false; Plugin.Log.LogWarning((object)("Pause menu: " + ex.Message + "; the game's own screenshot is used")); return true; } } private static IEnumerator Capture(Action onDone) { yield return (object)new WaitForEndOfFrame(); Color32[] pixels = null; int w = 0; int h = 0; Texture2D val = null; try { val = ScreenCapture.CaptureScreenshotAsTexture(); if ((Object)(object)val != (Object)null) { w = ((Texture)val).width; h = ((Texture)val).height; pixels = val.GetPixels32(); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Pause menu: screenshot failed: " + ex.Message)); } finally { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } _capturing = false; } try { onDone?.Invoke(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Pause menu: " + ex2.Message)); } if (pixels != null) { ThreadPool.QueueUserWorkItem(delegate { Encode(pixels, w, h); }); } } private static void Encode(Color32[] px, int w, int h) { try { byte[] array = new byte[w * h * 3]; int num = 0; int num2 = 0; while (num < px.Length) { array[num2] = px[num].r; array[num2 + 1] = px[num].g; array[num2 + 2] = px[num].b; num++; num2 += 3; } byte[] value = ImageConversion.EncodeArrayToPNG((Array)array, (GraphicsFormat)7, (uint)w, (uint)h, 0u); _bytes.SetValue(null, value); if (_capturedAt != null && _capturedAt.CanWrite) { _capturedAt.SetValue(null, DateTime.UtcNow, null); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Pause menu: the bug report's screenshot wasn't encoded: " + ex.Message)); } } } [BepInPlugin("gk2.fasterdoors", "GK2 Faster Doors", "0.3.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "gk2.fasterdoors"; public const string Version = "0.3.3"; private static readonly Dictionary<string, string> Rivals = new Dictionary<string, string> { { "verto13.gk2.instanttransitions", "Instant Transitions" }, { "gk2.performance", "GK2 Performance" }, { "johnc0de.gk2.performance", "Jc0de FPS Boost" } }; internal static ManualLogSource Log; internal static ConfigEntry<bool> Diagnostics; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> Verbose; internal static ConfigEntry<bool> GcOnSkipped; internal static ConfigEntry<bool> QuickEnabled; internal static ConfigEntry<bool> HardCut; internal static ConfigEntry<bool> WhileSleeping; internal static ConfigEntry<bool> WhenRamLow; internal static ConfigEntry<bool> InstantPause; internal static ConfigEntry<bool> WalkIn; internal static ConfigEntry<int> EveryDoors; internal static ConfigEntry<int> GrowthMb; internal static ConfigEntry<float> FadeSeconds; internal static ConfigEntry<float> BlackPauseSeconds; internal static ConfigEntry<KeyboardShortcut> ToggleKey; private static bool _switchedOff; private static int _doorsSkipped; private static long _baseManaged; private static long _baseNative; private static bool _haveBase; private static float _measureAt = -1f; private static int _gcAtDoor; private static readonly Dictionary<string, int> Stripped = new Dictionary<string, int>(); private static bool _counting; private static readonly Stopwatch Clock = Stopwatch.StartNew(); private static long _doorAt = -1L; private static long _cleanAt = -1L; private static double _cleanMs = -1.0; private static double _gcMs = -1.0; private static string _decision; private static bool _ownersLogged; private static MethodBase _optMethod; private static MethodBase _teleportMethod; private static bool _quick; private static float _quickUntil; private static string _fadeWhy = ""; private static int _fadesShortened; private static bool _quitting; private bool _cfgBatch; private static TeleportDataBase _cutData; private static TeleportDataBase _delayData; private static float _delayWas; private static bool _cleanUnderBlack; private static string _ownRun; private static float _lastFullAt; private static bool _wasSleeping; private const float SleepCleanupGapSeconds = 300f; private const long RamGrowthBytes = 1048576000L; private const long FreeRamFloorMb = 1536L; private const float GameShareOfRam = 0.7f; private static float _noWalkInUntil; private static bool _walkInCall; private static string _walkInSkip; private static readonly Dictionary<CustomInteraction, bool> TeleportSlots = new Dictionary<CustomInteraction, bool>(); private static FieldInfo _expressionString; private const float WalkInCooldownSeconds = 1.2f; private static PropertyInfo _almanacWalking; private static bool _almanacLooked; private static bool On => !_switchedOff; internal static bool IsOn => On; private static void OnQuitting() { _quitting = true; } private void BeginCfgBatch() { _cfgBatch = true; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; } private void EndCfgBatch() { if (!_cfgBatch) { return; } _cfgBatch = false; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; try { ((BaseUnityPlugin)this).Config.Save(); } catch (Exception ex) { Log.LogWarning((object)("Config save: " + ex.Message)); } } private unsafe void Awake() { //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_04cc: Unknown result type (might be due to invalid IL or missing references) //IL_04e1: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Expected O, but got Unknown //IL_04ee: Expected O, but got Unknown //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Expected O, but got Unknown //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_0541: Expected O, but got Unknown //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Expected O, but got Unknown //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_0431: Expected O, but got Unknown //IL_0431: Expected O, but got Unknown //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_060c: Expected O, but got Unknown //IL_0620: Unknown result type (might be due to invalid IL or missing references) //IL_062e: Expected O, but got Unknown //IL_07f5: Unknown result type (might be due to invalid IL or missing references) //IL_07fa: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Unknown result type (might be due to invalid IL or missing references) //IL_080f: Unknown result type (might be due to invalid IL or missing references) //IL_0814: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Application.quitting -= OnQuitting; Application.quitting += OnQuitting; BeginCfgBatch(); CfgMigrate.Run(((BaseUnityPlugin)this).Config); QuickEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 Transitions", "FasterTransitions", true, Cm.D("Shorter fades and pause for doors you walk through and for map travel. Cutscenes, fights and quest scripts keep the game's timing.", "Faster transitions", 0)); HardCut = ((BaseUnityPlugin)this).Config.Bind<bool>("1 Transitions", "HardCut", true, Cm.D("Doors you walk through and map travel inside the same area skip the black screen: you are on the other side in the next frame.", "No black screen inside an area", 1)); FadeSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("1 Transitions", "FadeSeconds", 0.15f, Cm.D("Seconds of the fade to black and of the fade back, each, where a door still fades (the game: 0.3).", "Fade length", 2, advanced: false, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 0.3f))); BlackPauseSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("1 Transitions", "BlackPauseSeconds", 0.1f, Cm.D("Seconds the screen stays black before the move (the game: 0.3).", "Black pause", 3, advanced: false, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.3f))); Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2 Memory clean-up", "SkipCleanup", true, Cm.D("Skip the game's memory clean-up on most doors and teleports. It still runs in full when due (see below).", "Skip the game's memory clean-up at doors", 0)); WhileSleeping = ((BaseUnityPlugin)this).Config.Bind<bool>("2 Memory clean-up", "WhileSleeping", true, Cm.D("Run the game's memory clean-up while you sleep, behind the black screen, so memory doesn't keep growing over a long session.", "Clean up while you sleep", 1)); WhenRamLow = ((BaseUnityPlugin)this).Config.Bind<bool>("2 Memory clean-up", "WhenRamLow", true, Cm.D("If memory has grown a lot and your PC is running short of RAM, the next door goes through black once and cleans up there.", "Clean up at a door when RAM runs low", 2)); EveryDoors = ((BaseUnityPlugin)this).Config.Bind<int>("2 Memory clean-up", "FullCleanupEveryDoors", 10, Cm.D("Run the full clean-up at least on every Nth door. 1 = every door, as in the unmodded game.", "Full clean-up every N doors", 3, advanced: true, (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100))); GrowthMb = ((BaseUnityPlugin)this).Config.Bind<int>("2 Memory clean-up", "FullCleanupAfterGrowthMB", 300, Cm.D("Also run the full clean-up on the next door once the game's memory has grown by this many MB since the last one.", "Full clean-up after memory grows (MB)", 4, advanced: true, (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 4000))); GcOnSkipped = ((BaseUnityPlugin)this).Config.Bind<bool>("2 Memory clean-up", "GcOnSkippedDoors", true, Cm.D("Fallback only: if the game runs without incremental garbage collection, a door that skips the clean-up still gets a plain managed collection (GC.Collect, no asset unloading). GK2 uses incremental GC, so this normally does nothing.", "Light clean-up on skipped doors", 5, advanced: true)); InstantPause = ((BaseUnityPlugin)this).Config.Bind<bool>("3 Pause menu", "Instant", true, Cm.D("Esc opens the pause menu at once. The game first encodes a screenshot for its bug-report button; here that happens in the background.", "Instant pause menu", 0)); WalkIn = ((BaseUnityPlugin)this).Config.Bind<bool>("4 Doors", "WalkIn", false, Cm.D("Walking into a door uses it, without pressing E. Only doors you could use with E right now; never a door with a cutscene or a quest waiting on it, and never while you carry a body or a zombie. After you arrive you can't walk straight back for a moment.", "Walk into a door to use it", 0)); ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "Toggle", KeyboardShortcut.Empty, Cm.D("For comparing doors with the unmodded game: switches the whole mod off and on while playing. Not set by default; not saved, on again after a restart.", "Switch the mod off / on (to compare)", 0)); Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Verbose", false, Cm.D("Log every door's decision and what the full clean-up removed.", "Detailed log", 1)); Diagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Diagnostics", false, Cm.D("Writes one \"[Diag]\" line at start: whether Instant Transitions, GK2 Performance or Jc0de FPS Boost is loaded. For a compatibility test.", "Diagnostic log", 2, advanced: true)); Framework.TryRegister((BaseUnityPlugin)(object)this); SaLink.Join((BaseUnityPlugin)(object)this, null); try { Harmony val = new Harmony("gk2.fasterdoors"); MethodInfo methodInfo = AccessTools.Method(typeof(MainGame), "HiddenOptimization", (Type[])null, (Type[])null); if (methodInfo == null) { Log.LogError((object)"MainGame.HiddenOptimization not found - the mod does nothing"); return; } val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "OptPre", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _optMethod = methodInfo; MethodInfo methodInfo2 = AccessTools.Method(typeof(PlayerController), "Teleport", new Type[1] { typeof(TeleportDataBase) }, (Type[])null); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Plugin), "TeleportPre", (Type[])null), new HarmonyMethod(typeof(Plugin), "TeleportPost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _teleportMethod = methodInfo2; } else { Log.LogWarning((object)"PlayerController.Teleport not found: no faster transitions, no door timing"); } try { PlayerController.OnPlayerTeleported += Teleported; } catch (Exception ex) { Log.LogWarning((object)("No door timing: " + ex.Message)); } MethodInfo methodInfo3 = AccessTools.Method(typeof(MainGame).Assembly.GetType("EditorOnlyComponentStripper"), "StripAll", (Type[])null, (Type[])null); if (methodInfo3 != null) { val.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(Plugin), "StripPre", (Type[])null), new HarmonyMethod(typeof(Plugin), "StripPost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo4 = AccessTools.Method(typeof(MainGame).Assembly.GetType("EditorOnlyComponentStripper"), "DestroyEditorOnlyComponent", (Type[])null, (Type[])null); if (methodInfo4 != null) { val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(Plugin), "DestroyPre", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo5 = AccessTools.Method(typeof(UIBasicFade), "FadeIn", new Type[4] { typeof(float), typeof(Action), typeof(FadeFlag), typeof(bool) }, (Type[])null); MethodInfo methodInfo6 = AccessTools.Method(typeof(UIBasicFade), "FadeOut", new Type[3] { typeof(float), typeof(Action), typeof(FadeFlag) }, (Type[])null); if (methodInfo5 != null && methodInfo6 != null) { val.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(Plugin), "FadeInPre", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)methodInfo6, new HarmonyMethod(typeof(Plugin), "FadeOutPre", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"The game's fade methods were not found (game update?): fades keep the game's length"); } PauseMenu.Patch(val); } catch (Exception ex2) { Log.LogError((object)("Patching failed, the mod does nothing: " + ex2)); return; } ManualLogSource log = Log; string[] obj = new string[16] { "GK2 Faster Doors 0.3.3 loaded (full clean-up every ", EveryDoors.Value.ToString(), " doors or after +", GrowthMb.Value.ToString(), " MB; incremental GC ", IncrementalGc() ? "on" : "off", "; transitions ", QuickEnabled.Value ? ("fade " + FadeSeconds.Value.ToString("0.00") + " s, black " + BlackPauseSeconds.Value.ToString("0.00") + " s") : "as in the game", (HardCut.Value && QuickEnabled.Value) ? "; no black inside an area" : "", WhileSleeping.Value ? "; clean-up while sleeping" : "", InstantPause.Value ? "; instant pause menu" : "", WalkIn.Value ? "; walk-in doors" : "", "; RAM ", SystemInfo.systemMemorySize.ToString(), " MB", null }; KeyboardShortcut value = ToggleKey.Value; object obj2; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { value = ToggleKey.Value; obj2 = "; " + ((object)(*(KeyboardShortcut*)(&value))/*cast due to .constrained prefix*/).ToString() + " switches it off and on)"; } else { obj2 = "; no on/off key)"; } obj[15] = (string)obj2; log.LogInfo((object)string.Concat(obj)); EndCfgBatch(); } private void Start() { try { if (Diagnostics != null && Diagnostics.Value) { List<string> list = new List<string>(); foreach (KeyValuePair<string, string> rival in Rivals) { PluginInfo value; bool flag = Chainloader.PluginInfos.TryGetValue(rival.Key, out value); list.Add(rival.Value + " (" + rival.Key + ") " + (flag ? ("loaded " + ((value.Metadata != null) ? value.Metadata.Version.ToString() : "?")) : "not loaded")); } Log.LogInfo((object)("[Diag] Faster Doors 0.3.3 other door mods: " + string.Join("; ", list.ToArray()))); } foreach (KeyValuePair<string, string> rival2 in Rivals) { if (Chainloader.PluginInfos.ContainsKey(rival2.Key)) { Log.LogWarning((object)(rival2.Value + " is installed too: it changes the same door code as Faster Doors. Use one of the two, not both.")); } } } catch { } } private static bool IncrementalGc() { try { return GarbageCollector.isIncremental; } catch { return false; } } private void OnDestroy() { try { PlayerController.OnPlayerTeleported -= Teleported; } catch { } if (_quitting) { return; } try { Harmony.UnpatchID("gk2.fasterdoors"); } catch (Exception ex) { Log.LogWarning((object)("Unpatch: " + ex.Message)); } } private void Update() { EndCfgBatch(); if (Hotkey.Down(ToggleKey) && !Hotkey.TypingText()) { Toggle(); } TickSleep(); TickWalkIn(); if (_measureAt >= 0f && Time.unscaledTime >= _measureAt) { _measureAt = -1f; _baseManaged = GC.GetTotalMemory(forceFullCollection: false); _baseNative = Native(); _haveBase = true; Log.LogInfo((object)("memory after the full clean-up: " + Mb(_baseManaged) + " + " + Mb(_baseNative) + " MB")); } } private static void Toggle() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) _switchedOff = !_switchedOff; _quick = false; string text = (_switchedOff ? "Faster Doors: off - doors as in the unmodded game" : "Faster Doors: on"); Log.LogInfo((object)(text + " (" + ((object)ToggleKey.Value/*cast due to .constrained prefix*/).ToString() + ")")); try { LazySingleton<UINotificator>.Instance.ShowSimpleTextNotification(text); } catch (Exception ex) { Log.LogWarning((object)("Notice: " + ex.Message)); } } private static long Native() { try { return Profiler.GetTotalAllocatedMemoryLong(); } catch { return 0L; } } private static long Mb(long bytes) { return bytes / 1048576; } private static bool FromTeleport() { try { StackFrame[] frames = new StackTrace(fNeedFileInfo: false).GetFrames(); if (frames == null) { return false; } StackFrame[] array = frames; for (int i = 0; i < array.Length; i++) { Type type = array[i].GetMethod()?.DeclaringType; while (type != null) { if (type == typeof(PlayerController)) { return true; } type = type.DeclaringType; } } } catch { } return false; } private static bool OptPre(ref UniTask __result, bool __runOriginal) { //IL_02f6: Unknown result type (might be due to invalid IL or missing references) if (!__runOriginal) { return false; } try { _gcMs = -1.0; if (_ownRun != null) { FullRun(_ownRun); _ownRun = null; return true; } if (!Enabled.Value || !On) { FullRun(On ? "off" : "switched off"); return true; } if (!FromTeleport()) { FullRun("scene load"); return true; } string text = null; long totalMemory = GC.GetTotalMemory(forceFullCollection: false); long num = Native(); long num2 = (_haveBase ? (Math.Max(0L, totalMemory - _baseManaged) + Math.Max(0L, num - _baseNative)) : 0); if (!_haveBase) { text = "first door"; } else if (_doorsSkipped + 1 >= EveryDoors.Value) { text = "door " + (_doorsSkipped + 1) + " of " + EveryDoors.Value; } else if (num2 >= (long)GrowthMb.Value * 1024L * 1024) { text = "memory +" + Mb(num2) + " MB"; } if (text != null) { FullRun(text); return true; } _doorsSkipped++; if (GcOnSkipped.Value && !IncrementalGc()) { long elapsedTicks = Clock.ElapsedTicks; GC.Collect(); _gcMs = (double)(Clock.ElapsedTicks - elapsedTicks) * 1000.0 / (double)Stopwatch.Frequency; } _decision = "clean-up skipped (" + _doorsSkipped + " since the last full one, memory +" + Mb(num2) + " MB" + ((_gcMs >= 0.0) ? (", GC only " + _gcMs.ToString("0") + " ms") : "") + ")"; if (Verbose.Value) { Log.LogInfo((object)("door: clean-up skipped (" + _doorsSkipped + " since the last one, memory +" + Mb(num2) + " MB, managed " + Mb(totalMemory) + " MB, native " + Mb(num) + " MB, GCs since last door " + (GC.CollectionCount(0) - _gcAtDoor) + ")")); } _gcAtDoor = GC.CollectionCount(0); __result = default(UniTask); return false; } catch (Exception ex) { Log.LogWarning((object)("Deciding failed, running the game's clean-up: " + ex.Message)); return true; } } private static void FullRun(string why) { _decision = "full clean-up (" + why + ")"; _lastFullAt = Time.unscaledTime; _cleanAt = Clock.ElapsedTicks; _cleanMs = -1.0; _doorsSkipped = 0; _measureAt = Time.unscaledTime + 3f; _gcAtDoor = GC.CollectionCount(0); if (Verbose.Value) { Log.LogInfo((object)("full clean-up (" + why + "): managed " + Mb(GC.GetTotalMemory(forceFullCollection: false)) + " MB, native " + Mb(Native()) + " MB")); } } private static void TeleportPre(TeleportDataBase __0) { RestoreDelay(); _noWalkInUntil = Time.unscaledTime + 3f; _doorAt = Clock.ElapsedTicks; _decision = null; _cleanMs = -1.0; _gcMs = -1.0; _quick = false; _fadesShortened = 0; _fadeWhy = ""; try { bool flag = false; bool flag2 = false; string why; if (__0 == null || __0.donNotFade) { _fadeWhy = "no fade"; } else if (!On || !QuickEnabled.Value) { _fadeWhy = "game's fades (" + (On ? "transitions off" : "switched off") + ")"; } else if (!PlayerStarted(out why)) { _fadeWhy = "game's fades (" + why + ")"; } else if ((flag = SameScene(__0)) && HardCut.Value && !(flag2 = WhenRamLow.Value && RamCleanupDue())) { __0.donNotFade = true; _cutData = __0; _fadeWhy = "cut (" + why + ")"; } else { _quick = true; _quickUntil = Time.realtimeSinceStartup + 60f; _delayData = __0; _delayWas = __0.delayInFade; __0.delayInFade = Mathf.Min(__0.delayInFade, Mathf.Max(0f, BlackPauseSeconds.Value)); _fadeWhy = "quick fades (" + why + ")"; if (flag && !HardCut.Value) { flag2 = WhenRamLow.Value && RamCleanupDue(); } if (flag2) { _cleanUnderBlack = true; _fadeWhy = "through black to clean up (RAM low)"; } } } catch (Exception ex) { _quick = false; Log.LogWarning((object)("Transitions: " + ex.Message)); } LogOwnersOnce(); } private static bool PlayerStarted(out string why) { why = "not started by the player"; if (_walkInCall) { why = "walk-in"; return true; } StackFrame[] frames = new StackTrace(2, fNeedFileInfo: false).GetFrames(); if (frames == null) { return false; } bool flag = false; bool flag2 = false; StackFrame[] array = frames; for (int i = 0; i < array.Length; i++) { Type type = array[i].GetMethod()?.DeclaringType; while (type != null) { if (type == typeof(FightingGameController)) { why = "fight"; return false; } if (type == typeof(MapPageWidget)) { flag2 = true; } else if (type == typeof(PlayerInputHandler)) { flag = true; } type = type.DeclaringType; } } if (flag2) { why = "map"; return true; } if (!flag && AlmanacWalking()) { why = "Almanac's AutoWalk"; return true; } if (!flag) { return false; } try { if ((Object)(object)LazyWindowsStackController.ActiveWindow != (Object)null) { why = "a window is open"; return false; } PlayerController val = (((Object)(object)MainGame.Instance != (Object)null) ? MainGame.PlayerController : null); if ((Object)(object)val != (Object)null && !val.IsControlEnabledByType((TakenControlType)11)) { why = "cutscene"; return false; } } catch { } why = "door"; return true; } private static void FadeInPre(UIBasicFade __instance, ref float fadeTime, ref Action onComplete) { Shorten(__instance, ref fadeTime, last: false); if (_cleanUnderBlack && __instance is UIFade && FromTeleport()) { _cleanUnderBlack = false; Action next = onComplete; onComplete = delegate { CleanThen("RAM low", next); }; } } private static void FadeOutPre(UIBasicFade __instance, ref float fadeTime) { Shorten(__instance, ref fadeTime, last: true); } private static void Shorten(UIBasicFade fade, ref float fadeTime, bool last) { if (!_quick) { return; } try { if (Time.realtimeSinceStartup > _quickUntil || !On || !QuickEnabled.Value) { _quick = false; } else if (fade is UIFade && FromTeleport()) { float num = Mathf.Clamp(FadeSeconds.Value, 0.05f, 0.3f); if (fadeTime > num) { fadeTime = num; _fadesShortened++; } if (last) { _quick = false; } } } catch { _quick = false; } } private static void TeleportPost() { if (_cutData != null) { try { _cutData.donNotFade = false; } catch { } _cutData = null; } } private static void RestoreDelay() { if (_delayData != null) { try { _delayData.delayInFade = _delayWas; } catch { } _delayData = null; } } private static bool SameScene(TeleportDataBase data) { try { GameSceneData destinationSceneData = data.GetDestinationSceneData(); return destinationSceneData == null || destinationSceneData.id == MainGame.PlayerData.currentGameSceneId; } catch { return false; } } private static void CleanThen(string why, Action next) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) UniTask val; try { _ownRun = why; val = MainGame.HiddenOptimization(); } catch (Exception ex) { _ownRun = null; Log.LogWarning((object)("Clean-up (" + why + ") failed: " + ex.Message)); if (next != null) { next(); } return; } Awaiter awaiter = ((UniTask)(ref val)).GetAwaiter(); if (((Awaiter)(ref awaiter)).IsCompleted) { if (next != null) { next(); } return; } ((Awaiter)(ref awaiter)).OnCompleted((Action)delegate { try { if (next != null) { next(); } } catch (Exception ex2) { Log.LogWarning((object)("After the clean-up: " + ex2.Message)); } }); } private static void TickSleep() { bool flag = false; try { PlayerData val = (((Object)(object)MainGame.Instance != (Object)null) ? MainGame.PlayerData : null); flag = val != null && val.energySystem != null && val.energySystem.IsSleeping; } catch { } if (flag && !_wasSleeping && On && WhileSleeping.Value && (_lastFullAt <= 0f || Time.unscaledTime - _lastFullAt >= 300f)) { CleanThen("while sleeping", null); } _wasSleeping = flag; } private static bool RamCleanupDue() { try { if (!_haveBase) { return false; } long num = Math.Max(0L, GC.GetTotalMemory(forceFullCollection: false) - _baseManaged) + Math.Max(0L, Native() - _baseNative); if (num < 1048576000) { return false; } long num2 = SystemInfo.systemMemorySize; long num3 = Ram.FreeMb(); long num4 = Process.GetCurrentProcess().WorkingSet64 / 1048576; bool flag = (num3 >= 0 && num3 < 1536) || (num2 > 0 && (float)num4 > (float)num2 * 0.7f); if (flag) { Log.LogInfo((object)("RAM low (free " + num3 + " MB, game " + num4 + " of " + num2 + " MB, grown " + Mb(num) + " MB): this door cleans up under black")); } return flag; } catch { return false; } } private void TickWalkIn() { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Invalid comparison between Unknown and I4 if (WalkIn == null || !WalkIn.Value || !On || Time.unscaledTime < _noWalkInUntil) { return; } try { MainGame instance = MainGame.Instance; PlayerController val = (((Object)(object)instance != (Object)null) ? MainGame.PlayerController : null); if ((Object)(object)val == (Object)null || MainGame.PlayerData == null || MainGame.IsGamePaused) { return; } if (!val.IsControlsEnabled) { _noWalkInUntil = Math.Max(_noWalkInUntil, Time.unscaledTime + 1.2f); return; } PlayerInteractionComponent playerInteractionComponent = val.PlayerInteractionComponent; Wgo val2 = (((Object)(object)playerInteractionComponent != (Object)null) ? playerInteractionComponent.WgoUnderInteraction : null); if ((Object)(object)val2 == (Object)null || (Object)(object)playerInteractionComponent.BigDropUnderInteraction != (Object)null) { _walkInSkip = null; return; } PlayerAnimation val3 = (((Object)(object)val.PhysicalBody != (Object)null && (Object)(object)val.PhysicalBody.PlayerView != (Object)null) ? val.PhysicalBody.PlayerView.PlayerAnimation : null); if ((Object)(object)val3 == (Object)null || (int)((AnimationComponentBase)val3).AnimationState != 1) { return; } WgoData data = val2.Data; if (DoorSlot(val2) == null) { return; } string text = null; if ((Object)(object)LazyWindowsStackController.ActiveWindow != (Object)null) { text = "a window is open"; } else if (AlmanacWalking()) { text = "Almanac's AutoWalk is walking"; } else if (data.Events.Count > 0) { text = "an interaction event is waiting on it"; } else if (instance.GameSave.questSystemData.WgoHasReadyToFinishQuest(val2.Id)) { text = "a quest is ready to hand in there"; } else if (Carrying(MainGame.PlayerData)) { text = "carrying a body or a zombie"; } else if (!((IInteractionHandler)val2.InteractionHandler).HasInteraction()) { text = "the door is not usable now"; } if (text != null) { if (Verbose.Value && _walkInSkip != val2.Id + text) { Log.LogInfo((object)("walk-in: " + val2.Id + " left alone (" + text + ")")); } _walkInSkip = val2.Id + text; return; } _walkInSkip = null; _noWalkInUntil = Time.unscaledTime + 2f; Log.LogInfo((object)("walk-in: " + val2.Id + " used without E")); _walkInCall = true; try { val2.InteractionHandler.Interact(val); } finally { _walkInCall = false; } } catch (Exception ex) { _noWalkInUntil = Time.unscaledTime + 5f; Log.LogWarning((object)("Walk-in: " + ex.Message)); } } private static CustomInteraction DoorSlot(Wgo wgo) { if (!(wgo.InteractionHandler is CustomInteractionHandler)) { return null; } WGODef definition = ((ObjectLinkedToDefinition<WGODef>)(object)wgo.Data).Definition; if (definition == null || definition.customInteraction == null) { return null; } CustomInteraction val = (definition.customInteraction.IsInteractable(wgo.Data) ? definition.customInteraction : definition.customInteraction2); if (val == null || !Teleports(val)) { return null; } return val; } private static bool Teleports(CustomInteraction slot) { if (TeleportSlots.TryGetValue(slot, out var value)) { return value; } bool flag = false; try { if (_expressionString == null) { _expressionString = AccessTools.Field(typeof(LazyExpressionBase), "expressionString"); } if (_expressionString != null && slot.execution != null) { foreach (LazyExpression item in slot.execution) { string text = ((item != null) ? (_expressionString.GetValue(item) as string) : null); if (text != null && text.IndexOf("Teleport", StringComparison.Ordinal) >= 0) { flag = true; break; } } } } catch { flag = false; } TeleportSlots[slot] = flag; return flag; } private static bool Carrying(PlayerData pd) { IReadOnlyList<Item> overheadItems = pd.OverheadItems; if (overheadItems == null) { return false; } for (int i = 0; i < overheadItems.Count; i++) { Item val = overheadItems[i]; List<string> list = ((val != null && !val.IsEmpty && ((ObjectLinkedToDefinition<ItemDef>)(object)val).Definition != null) ? ((ObjectLinkedToDefinition<ItemDef>)(object)val).Definition.itemGroupIds : null); if (list != null && (list.Contains("body") || list.Contains("zombie") || list.Contains("corpse"))) { return true; } } return false; } private static bool AlmanacWalking() { try { if (!_almanacLooked) { _almanacLooked = true; Type type = AccessTools.TypeByName("Almanac.Hud.AutoWalk"); _almanacWalking = ((type != null) ? AccessTools.Property(type, "Walking") : null); } return _almanacWalking != null && (bool)_almanacWalking.GetValue(null, null); } catch { return false; } } private static void LogOwnersOnce() { if (_ownersLogged) { return; } _ownersLogged = true; try { List<string> list = new List<string>(); MethodBase[] array = new MethodBase[2] { _optMethod, _teleportMethod }; foreach (MethodBase methodBase in array) { Patches val = ((methodBase != null) ? Harmony.GetPatchInfo(methodBase) : null); if (val == null) { continue; } foreach (string owner in val.Owners) { if (owner != "gk2.fasterdoors" && !list.Contains(owner)) { list.Add(owner); } } } Log.LogInfo((object)((list.Count == 0) ? "no other mod patches the door code" : ("other mods patching the door code: " + string.Join(", ", list.ToArray())))); } catch { } } private static void Teleported() { RestoreDelay(); _noWalkInUntil = Time.unscaledTime + 1.2f; if (_doorAt < 0) { return; } try { string text = "door: " + ((double)(Clock.ElapsedTicks - _doorAt) / (double)Stopwatch.Frequency).ToString("0.00") + " s to the new place, " + (_decision ?? "the game's clean-up wasn't called"); if (_cleanMs >= 0.0 && _decision != null && _decision.StartsWith("full")) { text = text + " took " + _cleanMs.ToString("0") + " ms"; } text = text + ", " + _fadeWhy + ", memory " + Mb(GC.GetTotalMemory(forceFullCollection: false)) + " + " + Mb(Native()) + " MB"; Log.LogInfo((object)text); } catch { } _doorAt = -1L; } private static void StripPre() { _counting = Verbose.Value; Stripped.Clear(); } private static void StripPost() { if (_cleanAt >= 0) { _cleanMs = (double)(Clock.ElapsedTicks - _cleanAt) * 1000.0 / (double)Stopwatch.Frequency; _cleanAt = -1L; if (_doorAt < 0) { Log.LogInfo((object)(_decision + " took " + _cleanMs.ToString("0") + " ms")); } } if (_counting) { _counting = false; Log.LogInfo((object)((Stripped.Count == 0) ? "editor-only components removed: none" : ("editor-only components removed: " + string.Join(", ", from p in Stripped orderby p.Value descending select p.Key + " " + p.Value)))); } } private static void DestroyPre(Component __0) { if (_counting && !((Object)(object)__0 == (Object)null)) { string name = ((object)__0).GetType().Name; Stripped.TryGetValue(name, out var value); Stripped[name] = value + 1; } } } internal static class Ram { private struct MemoryStatusEx { public uint dwLength; public uint dwMemoryLoad; public ulong ullTotalPhys; public ulong ullAvailPhys; public ulong ullTotalPageFile; public ulong ullAvailPageFile; public ulong ullTotalVirtual; public ulong ullAvailVirtual; public ulong ullAvailExtendedVirtual; } private static bool _broken; [DllImport("kernel32.dll", SetLastError = true)] private static extern bool GlobalMemoryStatusEx(ref MemoryStatusEx buffer); public static long FreeMb() { if (_broken) { return -1L; } try { MemoryStatusEx buffer = new MemoryStatusEx { dwLength = (uint)Marshal.SizeOf(typeof(MemoryStatusEx)) }; if (!GlobalMemoryStatusEx(ref buffer)) { return -1L; } return (long)(buffer.ullAvailPhys / 1048576); } catch { _broken = true; return -1L; } } } internal static class SaHelp { private static readonly Dictionary<string, Dictionary<string, string>> Cache = new Dictionary<string, Dictionary<string, string>>(); private static readonly Regex Head = new Regex("\\[([^\\]]+)\\]\\s*$", RegexOptions.Compiled); private static readonly Dictionary<string, string> Found = new Dictionary<string, string>(); public static string ModDir(string pluginFolder) { try { string location = typeof(SaHelp).Assembly.Location; if (!string.IsNullOrEmpty(location)) { return Path.GetDirectoryName(location); } } catch { } try { if (Chainloader.PluginInfos.TryGetValue("gk2.fasterdoors", out var value) && value != null && !string.IsNullOrEmpty(value.Location)) { return Path.GetDirectoryName(value.Location); } } catch { } return Path.Combine(Paths.PluginPath, pluginFolder); } public static string Long(string dir, string lang, string sectionKey) { try { Dictionary<string, string> dictionary = Lines(dir, lang); if (dictionary != null && dictionary.TryGetValue(sectionKey, out var value)) { return value; } Dictionary<string, string> dictionary2 = Lines(dir, "en"); if (dictionary2 != null && dictionary2.TryGetValue(sectionKey, out value)) { return value; } } catch { } return ""; } public static string FileFor(string dir, string lang) { string key = dir + "|" + lang; string value; lock (Found) { if (Found.TryGetValue(key, out value)) { return value; } } value = Find(dir, lang); lock (Found) { Found[key] = value; return value; } } private static string Find(string dir, string lang) { lang = (lang ?? "en").Trim().ToLowerInvariant(); if (lang.Length > 0 && !lang.StartsWith("en", StringComparison.Ordinal)) { string[] array = new string[4] { lang, lang.Replace('_', '-'), lang.Replace('-', '_'), (lang.Length > 2) ? lang.Substring(0, 2) : lang }; foreach (string text in array) { string text2 = Path.Combine(dir, "Settings." + text + ".txt"); if (File.Exists(text2)) { return text2; } } } string text3 = Path.Combine(dir, "Settings.txt"); if (!File.Exists(text3)) { return null; } return text3; } private static Dictionary<string, string> Lines(string dir, string lang) { string text = FileFor(dir, lang); if (text == null) { return null; } lock (Cache) { if (Cache.TryGetValue(text, out var value)) { return value; } value = Parse(File.ReadAllText(text, Encoding.UTF8)); Cache[text] = value; return value; } } private static Dictionary<string, string> Parse(string text) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); string[] array = text.Replace("\r\n", "\n").Split('\n'); for (int i = 0; i < array.Length; i++) { string text2 = array[i]; if (text2.Length == 0 || char.IsWhiteSpace(text2[0]) || text2.StartsWith("==", StringComparison.Ordinal)) { continue; } Match match = Head.Match(text2); if (!match.Success) { continue; } string value = match.Groups[1].Value; int num = value.IndexOf(','); string text3 = ((num >= 0) ? value.Substring(0, num) : value).Trim(); if (text3.IndexOf('.') < 0) { continue; } StringBuilder stringBuilder = new StringBuilder(); int j; for (j = i + 1; j < array.Length && array[j].Length > 0 && char.IsWhiteSpace(array[j][0]); j++) { if (stringBuilder.Length > 0) { stringBuilder.Append(' '); } stringBuilder.Append(array[j].Trim()); } if (!dictionary.ContainsKey(text3)) { dictionary[text3] = stringBuilder.ToString(); } i = j - 1; } return dictionary; } public static bool OpenFile(string dir, string lang) { try { string text = FileFor(dir, lang); if (text == null) { return false; } Process.Start(new ProcessStartInfo(text) { UseShellExecute = true }); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Settings.txt did not open: " + ex.Message)); return false; } } } internal static class SaHost { private const string WindowGuid = "gk2.saintarchi.settings"; private const string PauseButtonName = "GK2SaintArchIPauseButton"; private const string WheelKey = "gk2kit.wheel.v1"; private static ConfigFile _cfg; private static ConfigEntry<KeyboardShortcut> _key; private static ConfigEntry<bool> _wheelOn; private static ConfigEntry<bool> _pauseOn; private static ConfigEntry<float> _scale; private static ConfigEntry<string> _lastMod; private static SaMod _self; private static SaWindowUi _win; private static bool _stackHooked; private static Dictionary<string, object> _wheelEntry; private static Sprite _wheelIcon; private static bool _wasInGame; private static int _lastToggleFrame = -1; private static string _hostGuid = ""; private static bool _pauseBroken; private static float _saveDue = -1f; private static List<Action> _warm; private static int _warmNext; private static float _warmAt; private static bool _warmedOnce; private static float _nextClashCheck; private static readonly HashSet<string> _clashLogged = new HashSet<string>(); internal static ConfigEntry<float> Scale => _scale; internal static ConfigEntry<string> LastMod => _lastMod; internal static SaMod SelfMod => _self; internal static string SelfGuid => "gk2.saintarchi.settings"; internal static string HostGuid => _hostGuid; internal static Sprite ScrollIcon() { return WheelIcon(); } internal static void Activate(SaMod host) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown _hostGuid = host.Guid; _nextClashCheck = 0f; _cfg = new ConfigFile(Path.Combine(Paths.ConfigPath, "gk2.saintarchi.settings.cfg"), false); _cfg.SaveOnConfigSet = false; _key = _cfg.Bind<KeyboardShortcut>("Window", "Key", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Opens and closes the settings window of the SaintArchI mods."); _wheelOn = _cfg.Bind<bool>("Window", "Wheel", true, "Adds an entry for the window to the Keeper's Lantern controller wheel."); _pauseOn = _cfg.Bind<bool>("Window", "PauseMenu", true, "Shows a button for the window in the game's pause menu. Off hides it; the window's key (F7) still opens it."); _scale = _cfg.Bind<float>("Window", "Scale", 1f, new ConfigDescription("Size of the window: 1 is the largest. Smaller makes the window smaller, not its text.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 1f), Array.Empty<object>())); _lastMod = _cfg.Bind<string>("Window", "LastMod", "", "The mod the window was last open on."); SaveCfg(); _cfg.SettingChanged += OnCfgChanged; Application.quitting -= SaveCfg; Application.quitting += SaveCfg; _self = BuildSelf(); _wheelOn.SettingChanged += OnWheelSetting; _pauseOn.SettingChanged += OnPauseSetting; try { LazyWindowsStackController.OnWindowOpened += OnWindowOpened; _stackHooked = true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Settings window: the pause menu button is off: " + ex.Message)); } if (_wheelOn.Value) { WheelAdd(); } _warmedOnce = false; _warm = null; if (_wasInGame) { WarmPlan(); } } internal static void Deactivate() { try { if (_win != null) { _win.Destroy(); } } catch { } _win = null; _warm = null; try { SaToast.Destroy(); } catch { } try { SaOverlay.Destroy(); } catch { } try { WheelRemove(); } catch { } try { if (_wheelOn != null) { _wheelOn.SettingChanged -= OnWheelSetting; } if (_pauseOn != null) { _pauseOn.SettingChanged -= OnPauseSetting; } } catch { } try { if (_stackHooked) { LazyWindowsStackController.OnWindowOpened -= OnWindowOpened; } } catch { } _stackHooked = false; try { RemovePauseButtons(); } catch { } try { Application.quitting -= SaveCfg; if (_cfg != null) { _cfg.SettingChanged -= OnCfgChanged; } if (_saveDue >= 0f) { SaveCfg(); } } catch { } _cfg = null; _key = null; _wheelOn = null; _pauseOn = null; _scale = null; _lastMod = null; _self = null; } private static void OnCfgChanged(object s, SettingChangedEventArgs e) { if (_saveDue < 0f) { _saveDue = Time.unscaledTime + 3f; } } private static void SaveCfg() { _saveDue = -1f; try { if (_cfg != null) { _cfg.Save(); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Settings window: gk2.saintarchi.settings.cfg not written: " + ex.Message)); } } private static SaMod BuildSelf() { string[] keys = new string[4] { "Window.Key", "Window.Wheel", "Window.PauseMenu", "Window.Scale" }; Dictionary<string, string> labels = new Dictionary<string, string> { { "Window.Key", "WinKeyLabel" }, { "Window.Wheel", "WinWheelLabel" }, { "Window.PauseMenu", "WinPauseLabel" }, { "Window.Scale", "WinScaleLabel" } }; Dictionary<string, string> helps = new Dictionary<string, string> { { "Window.Key", "WinKeyHelp" }, { "Window.Wheel", "WinWheelHelp" }, { "Window.PauseMenu", "WinPauseHelp" }, { "Window.Scale", "WinScaleHelp" } }; string value; return new SaMod { Guid = "gk2.saintarchi.settings", Version = "", Order = 1000, Nexus = "", FaqPage = "settings", Name = () => SaText.T("ThisWindow"), Icon = () => SaIcons.Get("sa"), Description = () => SaText.T("WinNote"), Config = _cfg, Keys = () => keys, Label = (string k) => (!labels.TryGetValue(k, out value)) ? k : SaText.T(value), Help = (string k) => (!helps.TryGetValue(k, out value)) ? "" : SaText.T(value), Long = (string k) => (!helps.TryGetValue(k, out value)) ? "" : SaText.T(value), Block = (string s) => SaText.T("ThisWindow"), Option = (string k, string v) => v, Control = (string k) => (!(k == "Window.Scale")) ? null : new Dictionary<string, object> { { "step", 0.05f } } }; } internal static bool InGame() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 try { MainGame instance = MainGame.Instance; return (Object)(object)instance != (Object)null && (int)instance.gameState == 1 && MainGame.PlayerData != null; } catch { return false; } } private static void WarmPlan() { _warmAt = Time.unscaledTime + 3f; _warmNext = 0; List<Action> list = new List<Action> { delegate { SaOverlay.Ensure(); } }; if (!_warmedOnce) { list.Add(delegate { SaUi.Spr("comm-frame_1-border"); }); list.Add(delegate { SaUi.Font("small_font"); }); list.Add(delegate { SaUi.Font("small_font_bold"); }); list.Add(delegate { string[] array2 = new string[5] { "left", "right", "up", "down", "diamond" }; for (int i = 0; i < array2.Length; i++) { SaUi.Shape(array2[i]); } }); list.Add(delegate { WheelIcon(); }); list.Add(delegate { SaToast.Prepare(); }); string[] array = new string[7] { "sa", "codex", "sl", "tw", "mb", "zpp", "fd" }; foreach (string text in array) { string x = text; list.Add(delegate { SaIcons.Get(x); }); } foreach (SaModView item in SaKeys.Mods()) { SaModView v = item; list.Add(delegate { _ = v.Icon; string[] keys = v.Keys; if (keys.Length != 0) { v.Long(keys[0]); } }); } list.Add(delegate { if (_win == null && SaOverlay.Ensure()) { _win = new SaWindowUi((Transform)(object)SaOverlay.WindowLayer); } }); list.Add(delegate { _warmedOnce = true; }); } _warm = list; } private static void Warm() { if (_warm == null || Time.unscaledTime < _warmAt || (_win != null && _win.IsOpen)) { return; } if (_warmNext >= _warm.Count) { _warm = null; return; } try { _warm[_warmNext++](); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Settings window: getting ready: " + ex.Message)); } } private static void LogClashes() { //IL_011f: Unknown result type (might be due to invalid IL or missing references) try { foreach (IGrouping<string, SaKeys.Use> item in from u in SaKeys.All(SaKeys.Mods()) group u by SaKeys.Id(u.Ks)) { List<SaKeys.Use> all = (from u in item group u by u.E into x select x.First()).ToList(); List<SaKeys.Use> list = all.Where((SaKeys.Use u) => all.Any((SaKeys.Use o) => o.E != u.E && SaKeys.CtxClash(u.Context, o.Context))).ToList(); if (list.Count >= 2) { string[] value = list.Select((SaKeys.Use u) => u.Where).OrderBy<string, string>((string s) => s, StringComparer.Ordinal).ToArray(); string text = SaWindowUi.KeyText(list[0].Ks); if (_clashLogged.Add(text + ": " + string.Join(", ", value))) { Plugin.Log.LogWarning((object)("Settings window: the key " + text + " is set for " + string.Join(" and ", value) + "; one press does all of them. Change one in the settings window (it marks the key in red) or in BepInEx\\config.")); } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Settings window: key check: " + ex.Message)); } } internal static void Tick() { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if (_saveDue >= 0f && Time.unscaledTime >= _saveDue) { SaveCfg(); } if (Time.unscaledTime >= _nextClashCheck) { bool flag = _nextClashCheck == 0f; _nextClashCheck = Time.unscaledTime + (flag ? 3f : 5f); if (!flag) { LogClashes(); } } if (!InGame()) { if (!_wasInGame) { return; } _wasInGame = false; try { if (_win != null && _win.IsOpen) { _win.Close(fromBack: false); } } catch { } SaOverlay.SetActive(on: false); return; } if (!_wasInGame) { _wasInGame = true; SaOverlay.SetActive(on: true); WarmPlan(); } if (_win != null) { _win.Tick(); } SaToast.Tick(); Warm(); if (_key == null || Time.frameCount == _lastToggleFrame || SaSettings.Capturing() || !KeyDown(_key.Value) || Typing()) { return; } if (_win != null && _win.IsOpen) { if (_win.IsTop) { _lastToggleFrame = Time.frameCount; _win.Close(fromBack: false); } } else if (CanOpenFromKey()) { _lastToggleFrame = Time.frameCount; Show("", fromPause: false); } } private static bool KeyDown(KeyboardShortcut s) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) try { if ((int)((KeyboardShortcut)(ref s)).MainKey == 0 || !Input.GetKeyDown(((KeyboardShortcut)(ref s)).MainKey)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref s)).Modifiers) { if (!Input.GetKey(modifier)) { return false; } } return true; } catch { return false; } } internal static bool Typing() { try { EventSystem current = EventSystem.current; GameObject val = (((Object)(object)current != (Object)null) ? current.currentSelectedGameObject : null); if ((Object)(object)val != (Object)null && val.activeInHierarchy && ((Object)(object)val.GetComponent<TMP_InputField>() != (Object)null || (Object)(object)val.GetComponent<InputField>() != (Object)null)) { return true; } return !LazyInput.IsInputActive(); } catch { return false; } } private static bool CanOpenFromKey() { try { if ((Object)(object)LazyWindowsStackController.ActiveWindow != (Object)null || MainGame.IsGamePaused) { return false; } PlayerController playerController = MainGame.PlayerController; return (Object)(object)playerController != (Object)null && playerController.IsControlsEnabled; } catch { return false; } } internal static bool Capturing() { if (_win != null) { return _win.Capturing; } return false; } internal static bool IsOpen() { if (_win != null) { return _win.IsOpen; } return false; } internal static bool Toggle() { if (_win != null && _win.IsOpen) { _win.Close(fromBack: false); return true; } return Show("", fromPause: false); } internal static bool Open(string guid) { if ((_win != null && _win.IsOpen) || CanOpenFromKey()) { return Show(guid, fromPause: false); } return false; } private static bool Show(string guid, bool fromPause) { if (!InGame()) { return false; } if (_win == null) { if (!SaOverlay.Ensure()) { return false; } SaOverlay.SetActive(on: true); _win = new SaWindowUi((Transform)(object)SaOverlay.WindowLayer); } if (string.IsNullOrEmpty(guid)) { guid = ((_lastMod != null) ? _lastMod.Value : ""); } _win.Open(guid, fromPause); return _win.IsOpen; } internal static void ReopenPause() { try { UIGamePauseWindow window = LazyUI.GetWindow<UIGamePauseWindow>(); if ((Object)(object)window != (Object)null) { ((LazyWindow<LazyWidgetDataBase>)(object)window).Open((LazyWidgetDataBase)null); } } catch (Exception ex) { Plugin.Log.LogWarning((ob