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.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Peak; using Peak.Network; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zorro.Core; using Zorro.Core.Serizalization; using Zorro.UI; using Zorro.UI.Modal; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PEAKQuickResume")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.3.0.0")] [assembly: AssemblyInformationalVersion("2.3.0+a5afea9b4fa2d0354c7375d3ee999de07feb3231")] [assembly: AssemblyProduct("PEAKQuickResume")] [assembly: AssemblyTitle("PEAKQuickResume")] [assembly: AssemblyVersion("2.3.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; } } } namespace PEAKQuickResume { public static class AchievementDebugLogging { private static ManualLogSource _log; public static void Apply(Harmony harmony, ManualLogSource log) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0051: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Expected O, but got Unknown //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Expected O, but got Unknown //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Expected O, but got Unknown _log = log; try { Type typeFromHandle = typeof(AchievementManager); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "RecordMaxHeight", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AchievementDebugLogging), "RecordMaxHeight_Prefix", (Type[])null), new HarmonyMethod(typeof(AchievementDebugLogging), "RecordMaxHeight_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "IncrementSteamStat", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "IncrementSteamStat_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "SetRunBasedInt", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "SetRunBasedInt_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "SetRunBasedFloat", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "SetRunBasedFloat_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "AddToRunBasedFruitsEaten", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "FruitsEaten_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "AddToShroomBerriesEaten", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "ShroomBerries_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "AddToNonToxicMushroomsEaten", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "NonToxicMushrooms_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "AddToGourmandRequirementsEaten", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "GourmandEaten_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "ThrowAchievement", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AchievementDebugLogging), "ThrowAchievement_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "TestWonRun", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AchievementDebugLogging), "TestWonRun_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "TestRespawnChestOpened", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AchievementDebugLogging), "TestRespawnChestOpened_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"AchievementDebugLogging: patched AchievementManager for [achievement-debug] logging."); } catch (Exception arg) { log.LogError((object)$"AchievementDebugLogging.Apply failed (non-fatal): {arg}"); } } private static void RecordMaxHeight_Prefix(out int __state) { __state = 0; try { AchievementManager instance = Singleton.Instance; if (instance != null) { instance.GetSteamStatInt((STEAMSTATTYPE)5, ref __state); } } catch { } } private static void RecordMaxHeight_Postfix(int meters, int __state) { try { AchievementManager instance = Singleton.Instance; if ((Object)(object)instance == (Object)null) { return; } int num = default(int); instance.GetSteamStatInt((STEAMSTATTYPE)5, ref num); int runBasedInt = instance.GetRunBasedInt((RUNBASEDVALUETYPE)9); int num2 = num - __state; if (num2 != 0) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)($"[achievement-debug] HighAltitudeBadge: altitude check at {meters}m (this run's MaxHeightReached was {runBasedInt}m) " + $"-> CREDITED {num2}m to the permanent HeightClimbed stat (now {num}m total). If this happened right after a teleport/load, the fix did not work.")); } } else { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)($"[achievement-debug] HighAltitudeBadge: altitude check at {meters}m (this run's MaxHeightReached was {runBasedInt}m) " + $"-> no credit (HeightClimbed stat stays at {num}m total). Correct behavior right after a teleport/load.")); } } } catch (Exception ex) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)("[achievement-debug] RecordMaxHeight_Postfix failed: " + ex.Message)); } } } private static void IncrementSteamStat_Postfix(STEAMSTATTYPE steamStatType, int value, int __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_003c: Unknown result type (might be due to invalid IL or missing references) try { if ((int)steamStatType == 5) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)$"[achievement-debug] HighAltitudeBadge: IncrementSteamStat(HeightClimbed, +{value}m) -> new total {__result}m."); } } else { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)$"[achievement-debug] Steam stat '{steamStatType}' incremented by {value} -> new total {__result}."); } } } catch { } } private static void SetRunBasedInt_Postfix(RUNBASEDVALUETYPE type, int value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((int)type == 6) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[achievement-debug] PlundererBadge: {value}/15 luggages opened this run."); } } else if ((int)type == 15) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)$"[achievement-debug] JesterBadge: {value}/3 clown luggages opened this run."); } } else if ((int)type == 2) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)$"[achievement-debug] ClutchBadge: {value}/3 scouts resurrected this run."); } } else { ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)$"[achievement-debug] run-based int '{type}' set to {value}."); } } } private static void SetRunBasedFloat_Postfix(RUNBASEDVALUETYPE type, float value) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[achievement-debug] run-based float '{type}' set to {value}."); } } private static void FruitsEaten_Postfix() { (int, int, int, int) eatenCounts = AchievementProgressIO.GetEatenCounts(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[achievement-debug] ForagingBadge: {eatenCounts.Item1}/5 different berries eaten this run."); } } private static void ShroomBerries_Postfix() { (int, int, int, int) eatenCounts = AchievementProgressIO.GetEatenCounts(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[achievement-debug] AdvancedMycologyBadge: {eatenCounts.Item2}/5 different Shroomberries eaten this run."); } } private static void NonToxicMushrooms_Postfix() { (int, int, int, int) eatenCounts = AchievementProgressIO.GetEatenCounts(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[achievement-debug] MycologyBadge: {eatenCounts.Item3}/4 different non-toxic mushrooms eaten this run."); } } private static void GourmandEaten_Postfix() { (int, int, int, int) eatenCounts = AchievementProgressIO.GetEatenCounts(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[achievement-debug] GourmandBadge: {eatenCounts.Item4}/4 required items cooked+eaten this run (coconut half, honeycomb, yellow winterberry, egg)."); } } private static void TestWonRun_Postfix() { AchievementProgressIO.LogSnapshot("run-won", _log); } private static void TestRespawnChestOpened_Prefix(RespawnChest chest, Character opener) { try { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)("[achievement-debug] TestRespawnChestOpened: chest='" + ((chest != null) ? ((Object)chest).name : null) + "' opener='" + ((opener != null) ? opener.characterName : null) + "' " + $"(opener.IsLocal={((opener != null) ? new bool?(opener.IsLocal) : ((bool?)null))}). Character.AllCharacters snapshot:")); } foreach (Character allCharacter in Character.AllCharacters) { string arg = "?"; try { arg = (((Object)(object)((allCharacter != null) ? allCharacter.player : null) != (Object)null) ? NetworkingUtilities.GetUserId(allCharacter.player) : "(no player)"); } catch { } ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)($" - '{((allCharacter != null) ? allCharacter.characterName : null)}' instanceId={((allCharacter != null) ? new int?(((Object)allCharacter).GetInstanceID()) : ((int?)null))} userId={arg} " + $"dead={((allCharacter != null) ? new bool?(allCharacter.data.dead) : ((bool?)null))} fullyPassedOut={allCharacter?.data.fullyPassedOut} passedOut={allCharacter?.data.passedOut}")); } } } catch (Exception ex) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)("[achievement-debug] TestRespawnChestOpened_Prefix failed: " + ex.Message)); } } } private static void ThrowAchievement_Prefix(ACHIEVEMENTTYPE type) { //IL_0010: 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) try { AchievementManager instance = Singleton.Instance; bool flag = (Object)(object)instance != (Object)null && instance.IsAchievementUnlocked(type); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[achievement-debug] ThrowAchievement({type}) called - already unlocked: {flag}."); } } catch { } } } public static class AchievementProgressIO { private static readonly Type NativeType = typeof(SerializableRunBasedValues); private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic; private static readonly FieldInfo FRunBasedInts = NativeType.GetField("runBasedInts", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo FRunBasedFloats = NativeType.GetField("runBasedFloats", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo FFruits = NativeType.GetField("runBasedFruitsEaten", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo FShroomBerries = NativeType.GetField("shroomBerriesEaten", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo FNonToxicMushrooms = NativeType.GetField("nonToxicMushroomsEaten", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo FGourmand = NativeType.GetField("gourmandRequirementsEaten", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo FEarnedThisRun = NativeType.GetField("achievementsEarnedThisRun", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo FCompletedAscents = NativeType.GetField("completedAscentsThisRun", BindingFlags.Instance | BindingFlags.NonPublic); private static bool AnyFieldMissing() { if (!(FRunBasedInts == null) && !(FRunBasedFloats == null) && !(FFruits == null) && !(FShroomBerries == null) && !(FNonToxicMushrooms == null) && !(FGourmand == null) && !(FEarnedThisRun == null)) { return FCompletedAscents == null; } return true; } public static (int fruits, int shroomBerries, int nonToxicMushrooms, int gourmand) GetEatenCounts() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Singleton.Instance == (Object)null || AnyFieldMissing()) { return (fruits: 0, shroomBerries: 0, nonToxicMushrooms: 0, gourmand: 0); } object obj = Singleton.Instance.runBasedValueData; int item = (FFruits.GetValue(obj) as List)?.Count ?? 0; int item2 = (FShroomBerries.GetValue(obj) as List)?.Count ?? 0; int item3 = (FNonToxicMushrooms.GetValue(obj) as List)?.Count ?? 0; int item4 = (FGourmand.GetValue(obj) as List)?.Count ?? 0; return (fruits: item, shroomBerries: item2, nonToxicMushrooms: item3, gourmand: item4); } catch { return (fruits: 0, shroomBerries: 0, nonToxicMushrooms: 0, gourmand: 0); } } public static OwnSavedAchievementProgress CaptureLocal(ManualLogSource log) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Singleton.Instance == (Object)null) { return null; } return ToSaved(Singleton.Instance.runBasedValueData, log); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("AchievementProgressIO.CaptureLocal failed (non-fatal): " + ex.Message)); } return null; } } public static OwnSavedAchievementProgress ToSaved(object boxedNative, ManualLogSource log) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected I4, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected I4, but got Unknown try { if (boxedNative == null || AnyFieldMissing()) { return null; } OwnSavedAchievementProgress ownSavedAchievementProgress = new OwnSavedAchievementProgress(); if (FRunBasedInts.GetValue(boxedNative) is Dictionary dictionary) { foreach (KeyValuePair item in dictionary) { ownSavedAchievementProgress.runBasedInts[(int)item.Key] = item.Value; } } if (FRunBasedFloats.GetValue(boxedNative) is Dictionary dictionary2) { foreach (KeyValuePair item2 in dictionary2) { ownSavedAchievementProgress.runBasedFloats[(int)item2.Key] = item2.Value; } } if (FFruits.GetValue(boxedNative) is List collection) { ownSavedAchievementProgress.runBasedFruitsEaten = new List(collection); } if (FShroomBerries.GetValue(boxedNative) is List collection2) { ownSavedAchievementProgress.shroomBerriesEaten = new List(collection2); } if (FNonToxicMushrooms.GetValue(boxedNative) is List collection3) { ownSavedAchievementProgress.nonToxicMushroomsEaten = new List(collection3); } if (FGourmand.GetValue(boxedNative) is List collection4) { ownSavedAchievementProgress.gourmandRequirementsEaten = new List(collection4); } if (FEarnedThisRun.GetValue(boxedNative) is List source) { ownSavedAchievementProgress.achievementsEarnedThisRun = source.Select((ACHIEVEMENTTYPE a) => (int)a).ToList(); } if (FCompletedAscents.GetValue(boxedNative) is List collection5) { ownSavedAchievementProgress.completedAscentsThisRun = new List(collection5); } return ownSavedAchievementProgress; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("AchievementProgressIO.ToSaved failed (non-fatal): " + ex.Message)); } return null; } } public static void ApplyLocal(OwnSavedAchievementProgress saved, ManualLogSource log) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Singleton.Instance == (Object)null) { return; } object obj = SerializableRunBasedValues.ConstructNew(); if (saved != null && !AnyFieldMissing()) { Dictionary dictionary = new Dictionary(); if (saved.runBasedInts != null) { foreach (KeyValuePair runBasedInt in saved.runBasedInts) { if (runBasedInt.Key != 9) { dictionary[(RUNBASEDVALUETYPE)runBasedInt.Key] = runBasedInt.Value; } } } FRunBasedInts.SetValue(obj, dictionary); Dictionary dictionary2 = new Dictionary(); if (saved.runBasedFloats != null) { foreach (KeyValuePair runBasedFloat in saved.runBasedFloats) { dictionary2[(RUNBASEDVALUETYPE)runBasedFloat.Key] = runBasedFloat.Value; } } FRunBasedFloats.SetValue(obj, dictionary2); FFruits.SetValue(obj, new List(saved.runBasedFruitsEaten ?? new List())); FShroomBerries.SetValue(obj, new List(saved.shroomBerriesEaten ?? new List())); FNonToxicMushrooms.SetValue(obj, new List(saved.nonToxicMushroomsEaten ?? new List())); FGourmand.SetValue(obj, new List(saved.gourmandRequirementsEaten ?? new List())); List value = (saved.achievementsEarnedThisRun ?? new List()).Select((int i) => (ACHIEVEMENTTYPE)i).ToList(); FEarnedThisRun.SetValue(obj, value); FCompletedAscents.SetValue(obj, new List(saved.completedAscentsThisRun ?? new List())); } Singleton.Instance.InitRunBasedValues((SerializableRunBasedValues)obj); Player localPlayer = Player.localPlayer; if (localPlayer != null) { localPlayer.OnAchievementProgressChanged(); } if (saved != null) { log.Trace("[achievement-debug] Restored this run's achievement progress from save:\n" + FormatDump(saved)); } else { log.Trace("[achievement-debug] No saved achievement progress found - primed a fresh baseline (matches vanilla's own fresh-run behavior)."); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("AchievementProgressIO.ApplyLocal failed (non-fatal): " + ex.Message)); } } } private static string FormatDump(OwnSavedAchievementProgress saved) { string text = ((saved.runBasedInts != null && saved.runBasedInts.Count > 0) ? string.Join(", ", saved.runBasedInts.Select((KeyValuePair kv) => $"{(object)(RUNBASEDVALUETYPE)kv.Key}={kv.Value}")) : "(none)"); string text2 = ((saved.runBasedFloats != null && saved.runBasedFloats.Count > 0) ? string.Join(", ", saved.runBasedFloats.Select((KeyValuePair kv) => $"{(object)(RUNBASEDVALUETYPE)kv.Key}={kv.Value}")) : "(none)"); return " ints: " + text + "\n floats: " + text2 + "\n" + $" fruitsEaten={saved.runBasedFruitsEaten?.Count ?? 0}, shroomBerriesEaten={saved.shroomBerriesEaten?.Count ?? 0}, " + $"nonToxicMushroomsEaten={saved.nonToxicMushroomsEaten?.Count ?? 0}, gourmandRequirementsEaten={saved.gourmandRequirementsEaten?.Count ?? 0}\n" + " completedAscentsThisRun=[" + string.Join(",", saved.completedAscentsThisRun ?? new List()) + "]"; } public static void LogSnapshot(string tag, ManualLogSource log) { try { OwnSavedAchievementProgress ownSavedAchievementProgress = CaptureLocal(log); if (ownSavedAchievementProgress == null) { log.Trace("[achievement-debug] SNAPSHOT[" + tag + "]: AchievementManager not available, nothing to snapshot."); return; } string text = (((Object)(object)Character.localCharacter != (Object)null) ? Character.localCharacter.characterName : "(unknown)"); log.Trace("[achievement-debug] SNAPSHOT[" + tag + "] for " + text + ":\n" + FormatDump(ownSavedAchievementProgress)); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("AchievementProgressIO.LogSnapshot(" + tag + ") failed (non-fatal): " + ex.Message)); } } } public static string ToJson(OwnSavedAchievementProgress saved) { try { return (saved == null) ? null : JsonConvert.SerializeObject((object)saved); } catch { return null; } } public static OwnSavedAchievementProgress FromJson(string json, ManualLogSource log) { try { return string.IsNullOrEmpty(json) ? null : JsonConvert.DeserializeObject(json); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("AchievementProgressIO.FromJson failed (non-fatal): " + ex.Message)); } return null; } } public static void RestoreAllPlayers(SaveSelection selection, OwnLoadEntryPoints entryPoints, ManualLogSource log, HashSet alreadyRestored = null) { try { bool offline = selection.Offline; Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); log.Trace($"[achievement-debug] RestoreAllPlayers: {array.Length} player object(s) found."); Player[] array2 = array; foreach (Player val in array2) { if ((Object)(object)val == (Object)null) { continue; } string text = (offline ? "" : NetworkingUtilities.GetUserId(val)); if (alreadyRestored != null && alreadyRestored.Contains(text)) { continue; } PhotonView component = ((Component)val).GetComponent(); OwnSavedAchievementProgress saved = null; if (selection.TryGetPlayerFile(text, out var path) && File.Exists(path)) { try { saved = JsonConvert.DeserializeObject(File.ReadAllText(path))?.achievementProgress; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("AchievementProgressIO.RestoreAllPlayers: could not read save for userId '" + text + "': " + ex.Message)); } } } else { log.Trace("AchievementProgressIO: skipping restore for '" + text + "' - no save file in this checkpoint's save event; priming a fresh baseline instead."); } if (offline || ((Object)(object)component != (Object)null && component.IsMine)) { log.Trace("[achievement-debug] RestoreAllPlayers: '" + text + "' is this machine - applying locally."); ApplyLocal(saved, log); alreadyRestored?.Add(text); } else if (PhotonNetwork.IsMasterClient && (Object)(object)component != (Object)null) { entryPoints?.Network?.RestoreAchievementProgressFor(component, text, ToJson(saved)); alreadyRestored?.Add(text); } else { log.Trace("[achievement-debug] RestoreAllPlayers: '" + text + "' NOT restored - " + string.Format("isMasterClient={0}, playerView={1}.", PhotonNetwork.IsMasterClient, ((Object)(object)component == (Object)null) ? "null" : "present")); } } } catch (Exception ex2) { if (log != null) { log.LogWarning((object)("AchievementProgressIO.RestoreAllPlayers failed (non-fatal): " + ex2.Message)); } } } } public static class AchievementSubscriptionFix { private static ManualLogSource _log; private static MethodInfo _unsubscribe; public static void Apply(Harmony harmony, ManualLogSource log) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown _log = log; try { _unsubscribe = AccessTools.Method(typeof(AchievementManager), "UnsubscribeFromEvents", (Type[])null, (Type[])null); harmony.Patch((MethodBase)AccessTools.Method(typeof(AchievementManager), "SubscribeToEvents", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AchievementSubscriptionFix), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"AchievementSubscriptionFix: patched AchievementManager.SubscribeToEvents (one achievement-event handler per manager, no double credit after a resume)."); } catch (Exception arg) { log.LogError((object)$"AchievementSubscriptionFix.Apply failed (non-fatal): {arg}"); } } private static void Prefix(AchievementManager __instance) { try { PurgeDestroyedSubscribers(); if ((Object)(object)__instance != (Object)null) { _unsubscribe?.Invoke(__instance, null); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("AchievementSubscriptionFix.Prefix failed (non-fatal, subscribing anyway): " + ex.Message)); } } } private static void PurgeDestroyedSubscribers() { int num = 0; FieldInfo[] fields = typeof(GlobalEvents).GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (!typeof(Delegate).IsAssignableFrom(fieldInfo.FieldType) || !(fieldInfo.GetValue(null) is Delegate obj)) { continue; } List list = new List(); Delegate[] invocationList = obj.GetInvocationList(); foreach (Delegate obj2 in invocationList) { object? target = obj2.Target; Object val = (Object)((target is Object) ? target : null); if (val != null && val == (Object)null) { num++; } else { list.Add(obj2); } } if (list.Count != obj.GetInvocationList().Length) { fieldInfo.SetValue(null, (list.Count == 0) ? null : Delegate.Combine(list.ToArray())); } } if (num > 0) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)($"AchievementSubscriptionFix: dropped {num} GlobalEvents handler(s) " + "belonging to destroyed objects (leftovers from a previous run).")); } } } } internal static class AfflictionArrayCompat { public static int CopyOverlap(float[] saved, float[] live) { if (saved == null || live == null) { return 0; } int num = Math.Min(saved.Length, live.Length); if (num <= 0) { return 0; } Array.Copy(saved, live, num); return num; } } public static class AncientStatueRestore { private const float StatueSearchRadius = 100f; private const float ItemSearchRadius = 8f; public static void Capture(Vector3 fallbackPos, HashSet claimed, ManualLogSource log, out OwnSavedStatueState state) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) state = null; try { Vector3 val = CampfireAreaHelpers.ResolveNearestCampfirePos(fallbackPos); RespawnChest val2 = FindNearestStatue(val); if ((Object)(object)val2 == (Object)null) { log.Trace($"AncientStatueRestore.Capture: no Ancient Statue found within {100f}m of {val}."); return; } state = new OwnSavedStatueState { broken = ((Luggage)val2).IsOpen }; log.Trace($"AncientStatueRestore.Capture: found statue '{((Object)val2).name}' at {((Component)val2).transform.position} " + $"({Vector3.Distance(((Component)val2).transform.position, val):F1}m from search center), broken={state.broken}."); if (!state.broken) { return; } Item val3 = CampfireAreaHelpers.FindNearestFreeItem(((Component)val2).transform.position, 8f, claimed); if ((Object)(object)val3 == (Object)null) { log.Trace("AncientStatueRestore.Capture: statue is broken but no unclaimed ground item found nearby."); return; } OwnSavedPositionedItem ownSavedPositionedItem = new OwnSavedPositionedItem { itemId = val3.itemID, posX = ((Component)val3).transform.position.x, posY = ((Component)val3).transform.position.y, posZ = ((Component)val3).transform.position.z, rotX = ((Component)val3).transform.rotation.x, rotY = ((Component)val3).transform.rotation.y, rotZ = ((Component)val3).transform.rotation.z, rotW = ((Component)val3).transform.rotation.w }; foreach (KeyValuePair item in OwnItemStateIO.ReadItemStateValues(val3.data)) { ownSavedPositionedItem.values[item.Key] = new OwnSavedEntry { type = item.Value.TypeName, value = item.Value.Value }; } state.item = ownSavedPositionedItem; claimed?.Add(val3); log.Trace($"AncientStatueRestore.Capture: statue holds item '{((Object)val3).name}' (id={ownSavedPositionedItem.itemId}) at {((Component)val3).transform.position}."); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("AncientStatueRestore.Capture failed (non-fatal): " + ex.Message)); } } } public static void Restore(OwnSaveData data, Vector3 fallbackPos, ManualLogSource log) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0149: 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_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) if (data?.ancientStatue == null || !data.ancientStatue.broken) { log.Trace("AncientStatueRestore.Restore: nothing to restore for this load (statue was unbroken when saved, or no save data)."); return; } try { Vector3 val = CampfireAreaHelpers.ResolveNearestCampfirePos(fallbackPos); RespawnChest val2 = FindNearestStatue(val); if ((Object)(object)val2 == (Object)null) { if (log != null) { log.LogWarning((object)$"AncientStatueRestore: no Ancient Statue found within {100f}m of {val}, nothing to restore."); } return; } log.Trace($"AncientStatueRestore: found statue '{((Object)val2).name}' at {((Component)val2).transform.position} " + $"({Vector3.Distance(((Component)val2).transform.position, val):F1}m from search center), currently open={((Luggage)val2).IsOpen}."); if (((Luggage)val2).IsOpen) { return; } val2.Break(); log.Trace("AncientStatueRestore: restored the Ancient Statue to its broken state."); OwnSavedPositionedItem item = data.ancientStatue.item; Item val3 = default(Item); if (item == null || !ItemDatabase.TryGetItem(item.itemId, ref val3) || !((Object)(object)val3 != (Object)null)) { return; } Vector3 val4 = default(Vector3); ((Vector3)(ref val4))..ctor(item.posX, item.posY, item.posZ); Quaternion val5 = default(Quaternion); ((Quaternion)(ref val5))..ctor(item.rotX, item.rotY, item.rotZ, item.rotW); GameObject val6 = PhotonNetwork.InstantiateItemRoom(((Object)val3).name, val4, val5); if ((Object)(object)val6 != (Object)null) { PhotonView val7 = default(PhotonView); if (((Spawner)val2).isKinematic && val6.TryGetComponent(ref val7)) { val7.RPC("SetKinematicRPC", (RpcTarget)3, new object[3] { true, val4, val5 }); } CampfireAreaHelpers.ApplySavedItemValues(val6, item.values, log); log.Trace($"AncientStatueRestore: respawned saved item {item.itemId} at {val4} on the Ancient Statue."); } else if (log != null) { log.LogWarning((object)$"AncientStatueRestore: InstantiateItemRoom returned null for item {item.itemId}."); } } catch (Exception arg) { if (log != null) { log.LogError((object)$"AncientStatueRestore.Restore failed (non-fatal): {arg}"); } } } private static RespawnChest FindNearestStatue(Vector3 nearPos) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) RespawnChest result = null; float num = float.MaxValue; RespawnChest[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (RespawnChest val in array) { if (!((Object)(object)val == (Object)null)) { float num2 = Vector3.Distance(((Component)val).transform.position, nearPos); if (num2 <= 100f && num2 < num) { num = num2; result = val; } } } return result; } } internal static class AreaNameCompat { public static string ResolveAreaName(Segment segment, string legacyName, ManualLogSource log) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected I4, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) if ((int)segment == 6) { return "NADIR"; } try { MountainProgressHandler instance = Singleton.Instance; ProgressPoint[] array = (((Object)(object)instance != (Object)null) ? instance.progressPoints : null); if (array == null) { return legacyName; } int num = (int)segment; if (num < 0 || num >= array.Length) { return legacyName; } string text = array[num]?.title; if (string.IsNullOrEmpty(text)) { return legacyName; } if (text != legacyName) { log.Trace($"AreaNameCompat: segment {segment} resolved to area '{text}' (legacy naming would have said '{legacyName}')."); } return text; } catch (Exception ex) { if (log != null) { log.LogWarning((object)$"AreaNameCompat: could not resolve the area name for {segment} ({ex.Message}); using '{legacyName}'."); } return legacyName; } } } public static class BackpackSaveMitigation { private class TrackedDrop { public string UserId; public Backpack Backpack; } private class PendingRestore { public string UserId; public SaveTarget Target; public JArray BackpackItemStates; public int BackpackViewId; public int BackpackType; public JObject BackpackOwnValues; } private const byte BackpackSlotId = 3; private const float WatchRadius = 50f; private static ManualLogSource _log; private static Campfire _watchedCampfire; private static readonly Dictionary _tracked = new Dictionary(); private static readonly List _pending = new List(); public static void Apply(Harmony harmony, ManualLogSource log) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown _log = log; try { MethodInfo methodInfo = AccessTools.Method(typeof(CharacterItems), "DropItemRpc", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(BackpackSaveMitigation), "OnDropItemRpc", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Campfire), "Light_Rpc", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(BackpackSaveMitigation), "OnLightRpc", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"BackpackSaveMitigation: patched DropItemRpc/Light_Rpc (dropped-backpack save mitigation)."); } catch (Exception arg) { log.LogError((object)$"BackpackSaveMitigation.Apply failed (non-fatal): {arg}"); } } private static void OnDropItemRpc(byte slotID, Vector3 spawnPos, Character ___character) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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) try { if (slotID != 3 || !IsHostMachine() || (Object)(object)___character == (Object)null) { return; } Campfire val = FindNearestUnlitCampfire(); if ((Object)(object)val == (Object)null || Vector3.Distance(((Component)val).transform.position, spawnPos) > 50f) { return; } Backpack val2 = FindNearestGroundBackpack(spawnPos); if (!((Object)(object)val2 == (Object)null)) { if ((Object)(object)_watchedCampfire != (Object)(object)val) { _tracked.Clear(); _watchedCampfire = val; } string text = SafeUserId(___character); if (_tracked.ContainsKey(text)) { _log.Trace("[backpack-mitigation] Replacing an earlier tracked drop for userId '" + text + "' with this newer one (the earlier one now falls through to WorldItemRestore's normal ground-item save)."); } _tracked[text] = new TrackedDrop { UserId = text, Backpack = val2 }; _log.Trace("[backpack-mitigation] Tracking a dropped backpack near the unlit campfire (owner userId '" + text + "')."); } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"[backpack-mitigation] OnDropItemRpc failed (non-fatal): {arg}"); } } } private static void OnLightRpc(Campfire __instance, bool updateSegment) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected I4, but got Unknown try { if (!updateSegment || !IsHostMachine()) { return; } if ((Object)(object)_watchedCampfire != (Object)(object)__instance) { _tracked.Clear(); _watchedCampfire = null; return; } foreach (TrackedDrop value in _tracked.Values) { if (!((Object)(object)value.Backpack == (Object)null) && (int)((Item)value.Backpack).itemState == 0 && !(Vector3.Distance(((Component)__instance).transform.position, ((Component)value.Backpack).transform.position) > 50f) && !PlayerAlreadyHasBackpack(value.UserId) && TryBuildBackpackItemStates(value.Backpack, out var states) && ((JContainer)states).Count != 0) { SaveTarget saveTarget = (RunLauncher.IsCustomRun ? SaveTarget.Custom() : SaveTarget.Normal(Ascents.currentAscent)); _pending.Add(new PendingRestore { UserId = value.UserId, Target = saveTarget, BackpackItemStates = states, BackpackViewId = ((MonoBehaviourPun)value.Backpack).photonView.ViewID, BackpackType = (int)value.Backpack.backpackType, BackpackOwnValues = BuildBackpackOwnValues(value.Backpack) }); _log.Trace($"[backpack-mitigation] Queued a backpack restore for userId '{value.UserId}' ({saveTarget}, {((JContainer)states).Count} item(s))."); } } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"[backpack-mitigation] OnLightRpc failed (non-fatal): {arg}"); } } finally { _tracked.Clear(); _watchedCampfire = null; } } public static void ApplyPendingRestores(bool offline, string stamp, ManualLogSource log) { if (_pending.Count == 0) { return; } foreach (PendingRestore restore in _pending) { if (SaveArchive.PatchSaveFile(OwnSavePaths.For(restore.Target, offline, restore.UserId, stamp), delegate(JObject json) { json["hasBackpack"] = JToken.op_Implicit(true); json["backpackType"] = JToken.op_Implicit(restore.BackpackType); json["backpackItemStates"] = (JToken)(object)restore.BackpackItemStates; if (restore.BackpackOwnValues != null) { json["backpackOwnValues"] = (JToken)(object)restore.BackpackOwnValues; } }, log)) { log.Trace("[backpack-mitigation] Restored a dropped backpack into the save for userId '" + restore.UserId + "'."); } else if (log != null) { log.LogWarning((object)("[backpack-mitigation] Could not find a save file for userId '" + restore.UserId + "' to restore the dropped backpack into.")); } } _pending.Clear(); } public static HashSet GetPendingBackpackViewIds() { HashSet hashSet = new HashSet(); foreach (PendingRestore item in _pending) { hashSet.Add(item.BackpackViewId); } return hashSet; } private static bool TryBuildBackpackItemStates(Backpack backpack, out JArray states) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_009e: 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) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown states = new JArray(); try { BackpackData val = default(BackpackData); if (((Item)backpack).data == null || !((Item)backpack).data.TryGetDataEntry((DataEntryKey)7, ref val) || val?.itemSlots == null) { return false; } for (byte b = 0; b < val.itemSlots.Length; b++) { ItemSlot val2 = val.itemSlots[b]; if (val2 != null && !val2.IsEmpty() && !((Object)(object)val2.prefab == (Object)null) && val2.data != null) { JObject val3 = new JObject(); foreach (KeyValuePair item in OwnItemStateIO.ReadItemStateValues(val2.data)) { val3[item.Key] = (JToken)new JObject { ["type"] = JToken.op_Implicit(item.Value.TypeName), ["value"] = JToken.op_Implicit(item.Value.Value) }; } states.Add((JToken)new JObject { ["slotIndex"] = JToken.op_Implicit(b), ["itemId"] = JToken.op_Implicit(val2.prefab.itemID), ["values"] = (JToken)(object)val3 }); } } return true; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("[backpack-mitigation] Could not read dropped backpack contents: " + ex.Message)); } return false; } } private static JObject BuildBackpackOwnValues(Backpack backpack) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown try { if (((Item)(backpack?)).data == null) { return null; } JObject val = new JObject(); foreach (KeyValuePair item in OwnItemStateIO.ReadItemStateValues(((Item)backpack).data)) { val[item.Key] = (JToken)new JObject { ["type"] = JToken.op_Implicit(item.Value.TypeName), ["value"] = JToken.op_Implicit(item.Value.Value) }; } return (((JContainer)val).Count > 0) ? val : null; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("[backpack-mitigation] Could not read the dropped backpack's own stats: " + ex.Message)); } return null; } } private static bool PlayerAlreadyHasBackpack(string userId) { try { foreach (Character allPlayerCharacter in PlayerHandler.GetAllPlayerCharacters()) { if (!(SafeUserId(allPlayerCharacter) != userId)) { return BackpackTypeCompat.HasAny(allPlayerCharacter.player); } } } catch { } return false; } private static Campfire FindNearestUnlitCampfire() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) try { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return null; } Vector3 center = localCharacter.Center; Campfire result = null; float num = float.MaxValue; Campfire[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Campfire val in array) { if (!val.Lit) { float num2 = Vector3.Distance(((Component)val).transform.position, center); if (num2 < num) { num = num2; result = val; } } } return result; } catch { return null; } } private static Backpack FindNearestGroundBackpack(Vector3 near) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) Backpack result = null; float num = float.MaxValue; Backpack[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Backpack val in array) { if ((int)((Item)val).itemState == 0) { float num2 = Vector3.Distance(((Component)val).transform.position, near); if (num2 < num) { num = num2; result = val; } } } return result; } private static string SafeUserId(Character c) { try { Player owner = ((MonoBehaviourPun)c).photonView.Owner; return ((owner != null) ? owner.UserId : null) ?? ""; } catch { return ""; } } private static bool IsHostMachine() { if (!PhotonNetwork.OfflineMode) { return PhotonNetwork.IsMasterClient; } return true; } } internal static class BackpackTypeCompat { public const int NoBackpack = 0; public const int PlainBackpack = 1; public static bool HasAny(Player p) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if (p?.backpackSlot != null) { return (int)p.backpackSlot.backpackType > 0; } return false; } public static bool HasAny(BackpackSlot slot) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (slot != null) { return (int)slot.backpackType > 0; } return false; } public static int Capture(Player p) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected I4, but got Unknown if (p?.backpackSlot == null) { return 0; } return (int)p.backpackSlot.backpackType; } public static int FromSave(bool legacyHasBackpack, int savedBackpackType) { if (savedBackpackType != 0) { return savedBackpackType; } return legacyHasBackpack ? 1 : 0; } public static bool TryResolveItemId(int backpackType, out ushort itemId, ManualLogSource log) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) itemId = 0; if (backpackType == 0) { return false; } BackpackType val = (BackpackType)backpackType; string text = PrefabNameFor(backpackType); try { Dictionary dictionary = SingletonAsset.Instance?.itemLookup; if (dictionary != null) { foreach (KeyValuePair item in dictionary) { Item value = item.Value; Backpack val2 = (Backpack)(object)((value is Backpack) ? value : null); if (val2 != null && val2.backpackType == val) { itemId = item.Key; return true; } } } Item val3 = default(Item); if (ItemDatabase.TryGetItem(text, ref val3) && (Object)(object)val3 != (Object)null) { itemId = val3.itemID; return true; } } catch (Exception ex) { if (log != null) { log.LogWarning((object)$"BackpackTypeCompat: backpack lookup for type {val} failed ({ex.Message}) - skipping the backpack restore."); } return false; } if (log != null) { log.LogWarning((object)$"BackpackTypeCompat: no prefab for backpack type {val} ('{text}') in the item database - skipping the backpack restore."); } return false; } public static void EnsureFuelCaptured(Item fuelSource, ItemInstanceData data, Dictionary values, ManualLogSource log) { if ((Object)(object)fuelSource == (Object)null || values == null || values.ContainsKey("Fuel")) { return; } try { JetpackItem component = ((Component)fuelSource).GetComponent(); if (!((Object)(object)component == (Object)null)) { FloatItemData val = default(FloatItemData); float fuel; string arg; if (data != null && data.TryGetDataEntry((DataEntryKey)10, ref val) && val != null) { fuel = val.Value; arg = "stored entry"; } else if (fuelSource.data == data && TryReadLiveFuel(component, out fuel)) { arg = "live JetpackItem.fuel"; } else { fuel = component.startingFuel; arg = "startingFuel default"; } values["Fuel"] = new OwnSavedEntry { type = typeof(FloatItemData).AssemblyQualifiedName, value = fuel }; if (log != null) { log.LogInfo((object)("OwnSaveCapture: '" + ((Object)fuelSource).name + "' had no saved Fuel value; stamped " + $"Fuel={fuel:0.###} from its {arg} (without this it would restore as a full tank).")); } } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("BackpackTypeCompat: could not capture jetpack fuel (" + ex.Message + "); it may come back full.")); } } } private static bool TryReadLiveFuel(JetpackItem jetpack, out float fuel) { fuel = 0f; FieldInfo field = typeof(JetpackItem).GetField("fuel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return false; } if (!(field.GetValue(jetpack) is float num)) { return false; } fuel = num; return true; } private static string PrefabNameFor(int backpackType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected I4, but got Unknown BackpackType val = (BackpackType)backpackType; return (val - 2) switch { 0 => "Fannypack", 1 => "Jetpack", 2 => "Rocketpack", _ => "Backpack", }; } } internal static class CampfireAreaHelpers { private const float CampfireSearchRadius = 30f; public static Vector3 ResolveNearestCampfirePos(Vector3 fallbackPos) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_0087: Unknown result type (might be due to invalid IL or missing references) try { Campfire val = null; float num = float.MaxValue; Campfire[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Campfire val2 in array) { if (!((Object)(object)val2 == (Object)null)) { float num2 = Vector3.Distance(((Component)val2).transform.position, fallbackPos); if (num2 <= 30f && num2 < num) { num = num2; val = val2; } } } if ((Object)(object)val != (Object)null) { return ((Component)val).transform.position; } if (NadirSearchRadius.CurrentlyInNadir()) { Vector3 val3 = ResolveNearestSoulPillarPos(fallbackPos); if (val3 != fallbackPos) { return val3; } } } catch { } return fallbackPos; } private static Vector3 ResolveNearestSoulPillarPos(Vector3 fallbackPos) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) ScoutmasterSoulPillar val = null; float num = float.MaxValue; ScoutmasterSoulPillar[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (ScoutmasterSoulPillar val2 in array) { if (!((Object)(object)val2 == (Object)null)) { float num2 = Vector3.Distance(((Component)val2).transform.position, fallbackPos); if (num2 <= 80f && num2 < num) { num = num2; val = val2; } } } if (!((Object)(object)val != (Object)null)) { return fallbackPos; } return ((Component)val).transform.position; } public static bool IsFreeWorldItem(Item item, bool includeBackpacks = false) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null || (int)item.itemState != 0) { return false; } if (!includeBackpacks && item is Backpack) { return false; } if ((Object)(object)((Component)item).GetComponentInParent(true) != (Object)null) { return false; } if ((Object)(object)((Component)item).GetComponentInParent(true) != (Object)null) { return false; } return true; } public static Item FindNearestFreeItem(Vector3 pos, float radius, HashSet exclude = null) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) Item result = null; float num = float.MaxValue; Item[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Item val in array) { if (IsFreeWorldItem(val) && (exclude == null || !exclude.Contains(val))) { float num2 = Vector3.Distance(((Component)val).transform.position, pos); if (num2 <= radius && num2 < num) { num = num2; result = val; } } } return result; } public static List FindFreeItemsWithin(Vector3 pos, float radius, bool includeBackpacks = false, HashSet exclude = null) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) return (from item in Object.FindObjectsByType((FindObjectsSortMode)0) where IsFreeWorldItem(item, includeBackpacks) && (exclude == null || !exclude.Contains(item)) && Vector3.Distance(((Component)item).transform.position, pos) <= radius orderby Vector3.Distance(((Component)item).transform.position, pos) select item).ToList(); } public static ItemInstanceData BuildItemInstanceData(Dictionary values, ManualLogSource log) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) ItemInstanceData val = new ItemInstanceData(Guid.NewGuid()); ItemInstanceDataHandler.AddInstanceData(val); if (values == null) { return val; } foreach (KeyValuePair value2 in values) { if (OwnItemStateIO.TryGetKey(value2.Key, out var key)) { OwnSavedEntry value = value2.Value; if (value != null && !OwnItemStateIO.TrySetOrCreateEntry(val, key, value.type, value.value, log) && log != null) { log.LogWarning((object)("CampfireAreaHelpers.BuildItemInstanceData: could not apply '" + value2.Key + "'.")); } } } return val; } public static void PushItemInstanceData(GameObject spawned, ItemInstanceData instanceData, ManualLogSource log) { if ((Object)(object)spawned == (Object)null || instanceData == null) { return; } PhotonView val = default(PhotonView); if (!spawned.TryGetComponent(ref val)) { if (log != null) { log.LogWarning((object)"CampfireAreaHelpers.PushItemInstanceData: spawned item has no PhotonView."); } } else { val.RPC("SetItemInstanceDataRPC", (RpcTarget)0, new object[1] { instanceData }); } } public static void ApplySavedItemValues(GameObject spawned, Dictionary values, ManualLogSource log) { if (values != null && values.Count != 0) { PushItemInstanceData(spawned, BuildItemInstanceData(values, log), log); } } } public static class CampfireAutoSavePatch { private static PluginConfig _cfg; private static OwnLoadEntryPoints _entryPoints; private static OwnNetwork _network; private static ManualLogSource _log; public static void Apply(Harmony harmony, PluginConfig cfg, OwnLoadEntryPoints entryPoints, OwnNetwork network, ManualLogSource log) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(typeof(Campfire), "Light_Rpc", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(CampfireAutoSavePatch), "Postfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"CampfireAutoSavePatch: patched Campfire.Light_Rpc (canonical autosave, reached on the host however the fire was lit)."); _cfg = cfg; _entryPoints = entryPoints; _network = network; _log = log; } catch (Exception arg) { log.LogError((object)$"CampfireAutoSavePatch.Apply failed (non-fatal): {arg}"); } } private static void Postfix(Campfire __instance, bool updateSegment) { try { if (!updateSegment || ((Object)__instance).name.Contains("PortableStovetop_Placed") || ((Object)(object)_entryPoints != (Object)null && _entryPoints.RecentlyLitCampfireUntil > Time.time)) { return; } _entryPoints?.ArmRecentlyLitCampfireCooldown(32f); _entryPoints?.ArmRecentlyLoadedCooldown(30f); if (PhotonNetwork.OfflineMode || PhotonNetwork.IsMasterClient) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"CampfireAutoSavePatch: campfire lit -> autosave triggered."); } AchievementProgressIO.LogSnapshot("campfire-lit", _log); if (PhotonNetwork.OfflineMode) { OwnSaveCapture.SavePlayerOffline(_cfg, _log, _network?.MessageOverlay, __instance); return; } _network?.RecentlyLitCampfireOthers(); OwnSaveCapture.SavePlayerCoop(_cfg, _log, _network, __instance); } } catch (Exception arg) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)$"CampfireAutoSavePatch.Postfix failed (non-fatal): {arg}"); } } } } internal static class CampfireIgniteBuffCompat { public static (float extraStamina, int petrifyAmount) Apply(Campfire campfire, Character character, float rawExtraStamina, int rawPetrifyAmount, float[] currentStatuses) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)campfire == (Object)null || (Object)(object)character == (Object)null) { return (extraStamina: rawExtraStamina, petrifyAmount: rawPetrifyAmount); } if (Vector3.Distance(((Component)campfire).transform.position, character.Center) > campfire.moraleBoostRadius) { return (extraStamina: rawExtraStamina, petrifyAmount: rawPetrifyAmount); } float num = 1f - (float)rawPetrifyAmount * 0.01f; float item = Mathf.Clamp(rawExtraStamina + campfire.moraleBoostBaseline, 0f, num); int item2 = Mathf.Clamp(rawPetrifyAmount - 20, 0, 100); int num2 = 0; if (currentStatuses != null && num2 < currentStatuses.Length) { currentStatuses[num2] = Mathf.Clamp(currentStatuses[num2] - 0.2f, 0f, 1f); } return (extraStamina: item, petrifyAmount: item2); } } public static class DeathStateRestore { private const string ReviveRpcName = "ReviveCharacter"; public static HashSet ResolveSavedDeadUserIds(SaveSelection selection, ManualLogSource log) { HashSet hashSet = new HashSet(StringComparer.Ordinal); if (selection == null || selection.Offline || !PhotonNetwork.IsMasterClient) { return hashSet; } try { int num = 0; Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { Character val2 = (((Object)(object)val != (Object)null) ? val.character : null); if (!((Object)(object)val2 == (Object)null)) { string userId = NetworkingUtilities.GetUserId(val2.player); if (WasSavedDead(selection, userId, log)) { hashSet.Add(userId); } else { num++; } } } if (hashSet.Count > 0 && num == 0) { if (log != null) { log.LogWarning((object)($"DeathStateRestore: the loaded checkpoint has all {hashSet.Count} player(s) " + "recorded as dead; skipping the death restore entirely rather than ending the run on load.")); } hashSet.Clear(); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("DeathStateRestore.ResolveSavedDeadUserIds failed (non-fatal, everyone stays alive): " + ex.Message)); } hashSet.Clear(); } return hashSet; } public static void ApplySavedDeaths(HashSet deadUserIds, OwnLoadEntryPoints entryPoints, ManualLogSource log) { if (deadUserIds == null || deadUserIds.Count == 0 || !PhotonNetwork.IsMasterClient) { return; } try { Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { Character val2 = (((Object)(object)val != (Object)null) ? val.character : null); if ((Object)(object)val2 == (Object)null || !deadUserIds.Contains(NetworkingUtilities.GetUserId(val2.player)) || val2.data.dead) { continue; } try { SuppressWatchdogFor(val, val2, entryPoints, log); ((MonoBehaviourPun)val2).photonView.RPC("RPCA_SetDead", (RpcTarget)0, Array.Empty()); log.Trace("DeathStateRestore: restored " + val2.characterName + " as dead (they were dead when this checkpoint was saved)."); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("DeathStateRestore: could not restore " + val2.characterName + " as dead (non-fatal): " + ex.Message)); } } } } catch (Exception ex2) { if (log != null) { log.LogWarning((object)("DeathStateRestore.ApplySavedDeaths failed (non-fatal, everyone stays alive): " + ex2.Message)); } } } public static void EnsureUnsavedPlayersAlive(HashSet deadUserIds, ManualLogSource log) { if (!PhotonNetwork.IsMasterClient || PhotonNetwork.OfflineMode) { return; } try { Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { Character val2 = (((Object)(object)val != (Object)null) ? val.character : null); if ((Object)(object)val2 == (Object)null || (Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null || (deadUserIds != null && deadUserIds.Contains(NetworkingUtilities.GetUserId(val2.player))) || !val2.data.dead) { continue; } try { ((MonoBehaviourPun)val2).photonView.RPC("ReviveCharacter", (RpcTarget)0, new object[1] { false }); if (log != null) { log.LogInfo((object)("DeathStateRestore: " + val2.characterName + " died during the load and this checkpoint records no death for them - revived so they come back alive.")); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("DeathStateRestore: could not revive " + val2.characterName + " (non-fatal): " + ex.Message)); } } } } catch (Exception ex2) { if (log != null) { log.LogWarning((object)("DeathStateRestore.EnsureUnsavedPlayersAlive failed (non-fatal): " + ex2.Message)); } } } private static void SuppressWatchdogFor(Player player, Character ch, OwnLoadEntryPoints entryPoints, ManualLogSource log) { try { PhotonView component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null && component.IsMine) { entryPoints?.Network?.Watchdog?.SuppressForRestoredDeath(); } else if ((Object)(object)component != (Object)null) { entryPoints?.Network?.SuppressWatchdogForRestoredDeath(component, NetworkingUtilities.GetUserId(ch.player)); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("DeathStateRestore: could not suppress the teleport watchdog for " + ch.characterName + " (non-fatal, they may see a spurious teleport-bug hint): " + ex.Message)); } } } private static bool WasSavedDead(SaveSelection selection, string userId, ManualLogSource log) { if (!selection.TryGetPlayerFile(userId, out var path)) { log.Trace("DeathStateRestore: '" + userId + "' has no file in this checkpoint's save event (joined after it was written, most likely) - restoring them alive."); return false; } try { return JsonConvert.DeserializeObject(File.ReadAllText(path))?.isDead ?? false; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("DeathStateRestore: could not read the save for '" + userId + "' (" + ex.Message + ") - restoring them alive.")); } return false; } } } internal static class DebugLog { public static void Trace(this ManualLogSource log, string message) { if (log != null && Plugin.DebugLoggingEnabled) { log.LogInfo((object)message); } } } public static class DeployableRestore { private const float SearchRadius = 30f; private const int MaxPerType = 20; public static void CaptureStoves(Vector3 fallbackPos, ManualLogSource log, out List states) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) Capture("PortableStovetop_Placed", "Portable Stove", fallbackPos, log, out states); } public static void CaptureCannons(Vector3 fallbackPos, ManualLogSource log, out List states) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) Capture("ScoutCannon_Placed", "Scout Cannon", fallbackPos, log, out states); } public static void RestoreStoves(OwnSaveData data, Vector3 fallbackPos, ManualLogSource log) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) Restore("PortableStovetop_Placed", "Portable Stove", data?.portableStoves, fallbackPos, log); } public static void RestoreCannons(OwnSaveData data, Vector3 fallbackPos, ManualLogSource log) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) Restore("ScoutCannon_Placed", "Scout Cannon", data?.scoutCannons, fallbackPos, log); } private static void Capture(string prefabNameNeedle, string label, Vector3 fallbackPos, ManualLogSource log, out List states) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_015e: 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) states = new List(); try { Vector3 val = CampfireAreaHelpers.ResolveNearestCampfirePos(fallbackPos); float num = NadirSearchRadius.ForCurrentSegment(30f); List list = FindPlayerPlaced(prefabNameNeedle, val, num); foreach (PhotonView item in list) { if (states.Count >= 20) { if (log != null) { log.LogWarning((object)$"DeployableRestore.Capture({label}): hit the {20}-item cap within {num}m of {val}, stopping early."); } break; } Transform transform = ((Component)item).transform; states.Add(new OwnSavedDeployableState { posX = transform.position.x, posY = transform.position.y, posZ = transform.position.z, rotX = transform.rotation.x, rotY = transform.rotation.y, rotZ = transform.rotation.z, rotW = transform.rotation.w }); } log.Trace($"DeployableRestore.Capture({label}): found {list.Count} player-placed within {num}m of {val}, saved {states.Count}."); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("DeployableRestore.Capture(" + label + ") failed (non-fatal): " + ex.Message)); } } } private static void Restore(string prefabName, string label, List saved, Vector3 fallbackPos, ManualLogSource log) { //IL_0079: 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) if (saved == null || saved.Count == 0) { log.Trace("DeployableRestore.Restore(" + label + "): nothing to restore for this load."); return; } try { int num = 0; Vector3 val = default(Vector3); Quaternion val2 = default(Quaternion); foreach (OwnSavedDeployableState item in saved) { if (item == null) { continue; } ((Vector3)(ref val))..ctor(item.posX, item.posY, item.posZ); ((Quaternion)(ref val2))..ctor(item.rotX, item.rotY, item.rotZ, item.rotW); if ((Object)(object)PhotonNetwork.Instantiate(prefabName, val, val2, (byte)0, (object[])null) == (Object)null) { if (log != null) { log.LogWarning((object)("DeployableRestore.Restore(" + label + "): PhotonNetwork.Instantiate('" + prefabName + "') returned null.")); } } else { num++; } } log.Trace($"DeployableRestore.Restore({label}): restored {num}/{saved.Count} within range of the loaded campfire."); } catch (Exception arg) { if (log != null) { log.LogError((object)$"DeployableRestore.Restore({label}) failed (non-fatal): {arg}"); } } } internal static List FindPlayerPlaced(string nameNeedle, Vector3 center, float radius) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) List list = new List(); PhotonView[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (PhotonView val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Object)((Component)val).gameObject).name.Contains(nameNeedle)) { bool isRoomView; try { isRoomView = val.IsRoomView; } catch { continue; } if (!isRoomView && val.CreatorActorNr > 0 && Vector3.Distance(((Component)val).transform.position, center) <= radius) { list.Add(val); } } } return list; } } public static class FairootsCompat { private const float MaxWaitSeconds = 25f; private static MethodInfo _shouldHold; private static bool _resolved; private static bool _loggedUnavailable; public static IEnumerator WaitUntilReady(ManualLogSource log, Action onFrame = null) { if (!Resolve(log)) { yield break; } float waited = 0f; bool everWaited = false; while (waited < 25f && ShouldHold(log)) { if (!everWaited) { everWaited = true; log.Trace("FairootsCompat: Fairoots is preparing the Roots biome - holding the loading screen."); } onFrame?.Invoke(); waited += Time.unscaledDeltaTime; yield return null; } if (waited >= 25f) { if (log != null) { log.LogWarning((object)$"FairootsCompat: Fairoots still busy after {25f:F0}s - taking the loading screen down anyway."); } } else if (everWaited) { log.Trace($"FairootsCompat: Fairoots finished after {waited:F1}s - continuing."); } } private static bool ShouldHold(ManualLogSource log) { try { object obj = _shouldHold.Invoke(null, null); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("FairootsCompat: Fairoots' hold check threw (" + ex.GetType().Name + "); not waiting on it.")); } _shouldHold = null; return false; } } private static bool Resolve(ManualLogSource log) { if (_resolved) { return _shouldHold != null; } _resolved = true; try { Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Fairoots", StringComparison.OrdinalIgnoreCase))?.GetType("Fairoots.FairootsInterop"); if (type == null) { return false; } _shouldHold = type.GetMethod("ShouldHoldLoadingScreen", BindingFlags.Static | BindingFlags.Public); if (_shouldHold == null || _shouldHold.ReturnType != typeof(bool)) { _shouldHold = null; if (!_loggedUnavailable) { _loggedUnavailable = true; if (log != null) { log.LogWarning((object)"FairootsCompat: Fairoots is installed but Fairoots.FairootsInterop.ShouldHoldLoadingScreen() was not found - the loading screen will not wait for it. Mod update?"); } } return false; } log.Trace("FairootsCompat: Fairoots detected - the loading screen will wait for its Roots setup."); return true; } catch (Exception ex) { _shouldHold = null; if (log != null) { log.LogWarning((object)("FairootsCompat: could not resolve Fairoots (non-fatal): " + ex.GetType().Name + ": " + ex.Message)); } return false; } } } public static class GameUpdateModalSuppressPatch { private static ManualLogSource _log; private static OwnMessageOverlay _messageOverlay; private static bool _noticeShownThisSession; public static void Apply(Harmony harmony, ManualLogSource log, OwnMessageOverlay messageOverlay) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown _log = log; _messageOverlay = messageOverlay; try { MethodInfo methodInfo = AccessTools.Method(typeof(Modal), "OpenModal", (Type[])null, (Type[])null); if (methodInfo == null) { log.LogWarning((object)"GameUpdateModalSuppressPatch: Modal.OpenModal not found; the vanilla 'game updated, close to continue' popup can still force-quit the game at the Airport."); return; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(GameUpdateModalSuppressPatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"GameUpdateModalSuppressPatch: patched Modal.OpenModal."); } catch (Exception arg) { log.LogError((object)$"GameUpdateModalSuppressPatch.Apply failed (non-fatal): {arg}"); } } private static bool Prefix(HeaderModalOption headerContent) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) try { DefaultHeaderModalOption val = (DefaultHeaderModalOption)(object)((headerContent is DefaultHeaderModalOption) ? headerContent : null); if (val == null) { return true; } if (!IsOutOfDateTitle(val.Title)) { return true; } _log.LogWarning((object)"Suppressed the vanilla game-update modal (would have force-quit on the only button). The game server reports a newer build is available. DO NOT update or restart PEAK until everyone is done with this save - the currently running game keeps working fine, but an update mid-save (especially a map-pool rotation) can make this save load the wrong island for good."); if ((Object)(object)_messageOverlay != (Object)null && !_noticeShownThisSession) { _noticeShownThisSession = true; _messageOverlay.Show(MessagesLocalization.Get(MsgKey.GameUpdateDialogSuppressed), new Color(1f, 0.55f, 0.3f, 1f), 12f); } return false; } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"GameUpdateModalSuppressPatch.Prefix failed (non-fatal, letting the vanilla modal through): {arg}"); } return true; } } private static bool IsOutOfDateTitle(string title) { if (string.IsNullOrEmpty(title)) { return false; } if (!(title == LocalizedText.GetText("VERSIONOUTOFDATE", true))) { return title == LocalizedText.GetText("MODAL_OUTOFDATE_TITLE", true); } return true; } } internal static class GameVersionCompat { public const string NoVersionDisplay = "v?.??.?"; public const string LegacySettingsVersion6GameVersion = "1.64.a"; public static string Current => Application.version; public static string Display(string version) { return "v" + version; } public static bool IsOlderThan(string version, string current) { if (string.IsNullOrEmpty(version)) { return true; } if (version == current) { return false; } if (TryParse(version, out var major, out var minor, out var letter) && TryParse(current, out var major2, out var minor2, out letter)) { if (major != major2) { return major < major2; } return minor < minor2; } return true; } private static bool TryParse(string version, out int major, out int minor, out char letter) { major = 0; minor = 0; letter = '\0'; if (string.IsNullOrEmpty(version)) { return false; } string[] array = version.Split('.'); if (array.Length != 3) { return false; } if (!int.TryParse(array[0], out major)) { return false; } if (!int.TryParse(array[1], out minor)) { return false; } if (array[2].Length != 1) { return false; } letter = array[2][0]; return true; } } internal static class HeightAchievementGuard { private static ManualLogSource _log; private static bool _patched; private static int _suppressDepth; private static float _suppressedUntil = -1f; private const float MaxSuppressionSeconds = 180f; private static bool Suppressed { get { if (_suppressDepth > 0 && Time.realtimeSinceStartup >= _suppressedUntil) { _suppressDepth = 0; ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"HeightAchievementGuard: the no-credit window outlasted its safety timeout (a load that never finished?); height credit resumes normally from here."); } } return _suppressDepth > 0; } } public static void Apply(Harmony harmony, ManualLogSource log) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown _log = log; try { MethodInfo methodInfo = AccessTools.Method(typeof(AchievementManager), "RecordMaxHeight", (Type[])null, (Type[])null); if (methodInfo == null) { log.LogWarning((object)"HeightAchievementGuard: AchievementManager.RecordMaxHeight not found - loading a save may inflate the HeightClimbed stat."); return; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(HeightAchievementGuard), "RecordMaxHeightPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _patched = true; log.LogInfo((object)"HeightAchievementGuard: patched AchievementManager.RecordMaxHeight (no height credit while loading)."); } catch (Exception arg) { log.LogError((object)$"HeightAchievementGuard.Apply failed (non-fatal): {arg}"); } } private static bool RecordMaxHeightPrefix(AchievementManager __instance, int meters) { if (!Suppressed) { return true; } try { if ((Object)(object)__instance != (Object)null && meters >= 25) { int runBasedInt = __instance.GetRunBasedInt((RUNBASEDVALUETYPE)9); if (meters >= runBasedInt + 5) { __instance.SetRunBasedInt((RUNBASEDVALUETYPE)9, meters); } } } catch { } return false; } public static void Suppress(string reason) { if (_patched) { _suppressDepth++; _suppressedUntil = Time.realtimeSinceStartup + 180f; if (_suppressDepth == 1) { _log.Trace("HeightAchievementGuard: height credit paused (" + reason + ")."); } } } public static void Release(string reason) { if (_patched && Suppressed) { _suppressDepth--; if (_suppressDepth <= 0) { SeedMarkFromCurrentAltitude(); _log.Trace("HeightAchievementGuard: height credit resumed (" + reason + ")."); } } } private static void SeedMarkFromCurrentAltitude() { try { Character localCharacter = Character.localCharacter; AchievementManager instance = Singleton.Instance; CharacterStats val = (((Object)(object)localCharacter != (Object)null && localCharacter.refs != null) ? localCharacter.refs.stats : null); if ((Object)(object)localCharacter == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)val == (Object)null) { return; } int num = Mathf.RoundToInt(val.heightInMeters); if (num < 25) { return; } int runBasedInt = instance.GetRunBasedInt((RUNBASEDVALUETYPE)9); if (runBasedInt < num) { instance.SetRunBasedInt((RUNBASEDVALUETYPE)9, num); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)($"HeightAchievementGuard: set this run's height mark to {num}m (was {runBasedInt}m) " + "so the altitude you were restored at isn't credited as a fresh climb.")); } } } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("HeightAchievementGuard: could not seed the height mark (" + ex.Message + "); the HeightClimbed stat may gain your current altitude once.")); } } } } public class HelpScreen : MonoBehaviour { private ManualLogSource _log; private PluginConfig _cfg; private GameObject _root; private Image _dimImage; private float _dimFadeElapsed; private const float DimFadeDuration = 0.25f; private Image _panelFillImage; private Image _grainImage; private RectTransform _panelRect; private RectTransform _bodyRect; private TextMeshProUGUI _titleText; private TextMeshProUGUI _bodyText; private TMP_FontAsset _font; private GameObject _loadingRoot; private bool _uiWarmedUp; private bool _warmingUp; private int _jagFrame; private float _jagFrameTimer; private int _lastWidth; private int _lastHeight; private const float BodyFontSize = 20f; private const float TitleFontSize = 27f; private const float FooterGap = 14f; private const float TitleBodyGap = 10f; private TextMeshProUGUI _footerKeyText; private TextMeshProUGUI _footerLabelText; private RectTransform _footerRow; public bool IsOpen { get; private set; } private bool MinimalUi { get { if (_cfg != null) { return _cfg.MinimalPickerUi.Value; } return false; } } public void Init(ManualLogSource log, PluginConfig cfg) { _log = log; _cfg = cfg; } public void Open() { IsOpen = true; if (!_uiWarmedUp) { EnsureLoadingUi(); GameObject loadingRoot = _loadingRoot; if (loadingRoot != null) { loadingRoot.SetActive(true); } if (!_warmingUp) { _warmingUp = true; ((MonoBehaviour)this).StartCoroutine(WarmUpThenShow()); } } else { ShowReal(skipDimFade: false); } } public void Close() { GameObject root = _root; if (root != null) { root.SetActive(false); } GameObject loadingRoot = _loadingRoot; if (loadingRoot != null) { loadingRoot.SetActive(false); } IsOpen = false; } private IEnumerator WarmUpThenShow() { yield return null; ShowReal(skipDimFade: true); GameObject loadingRoot = _loadingRoot; if (loadingRoot != null) { loadingRoot.SetActive(false); } _uiWarmedUp = true; _warmingUp = false; } private void ShowReal(bool skipDimFade) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) EnsureUi(); RebuildContent(); GameObject root = _root; if (root != null) { root.SetActive(true); } if (skipDimFade) { if ((Object)(object)_dimImage != (Object)null) { ((Graphic)_dimImage).color = SavePicker.DimColor; } _dimFadeElapsed = 0.25f; } else { if ((Object)(object)_dimImage != (Object)null) { ((Graphic)_dimImage).color = new Color(SavePicker.DimColor.r, SavePicker.DimColor.g, SavePicker.DimColor.b, 0f); } _dimFadeElapsed = 0f; } } private void Update() { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen || (Object)(object)_root == (Object)null || !_root.activeSelf) { return; } if (Input.GetKeyDown((KeyCode)27)) { Close(); PauseSuppressPatch.SuppressNextOpen(); return; } if ((Object)(object)_dimImage != (Object)null && _dimFadeElapsed < 0.25f) { _dimFadeElapsed += Time.unscaledDeltaTime; float num = Mathf.Clamp01(_dimFadeElapsed / 0.25f); ((Graphic)_dimImage).color = new Color(SavePicker.DimColor.r, SavePicker.DimColor.g, SavePicker.DimColor.b, SavePicker.DimColor.a * num); } if (MinimalUi) { return; } _jagFrameTimer += Time.unscaledDeltaTime; if (_jagFrameTimer >= 0.5f) { _jagFrameTimer -= 0.5f; _jagFrame = (_jagFrame + 1) % 3; if ((Object)(object)_panelFillImage != (Object)null) { _panelFillImage.sprite = SavePicker.PanelSprite(_lastWidth, _lastHeight, _jagFrame, minimal: false); } } } private void EnsureLoadingUi() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown if ((Object)(object)_loadingRoot != (Object)null) { return; } try { if ((Object)(object)_font == (Object)null) { _font = SavePicker.FindGameFont(); } _loadingRoot = new GameObject("PEAKQuickResume_HelpScreen_Loading", new Type[1] { typeof(RectTransform) }); _loadingRoot.transform.SetParent(((Component)this).transform, false); Canvas obj = _loadingRoot.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 30001; SavePicker.ApplyWidescreenScaler(obj); GameObject val = new GameObject("Dim", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_loadingRoot.transform, false); ((Graphic)val.AddComponent()).color = SavePicker.DimColor; SavePicker.StretchFull((RectTransform)val.transform); TextMeshProUGUI obj2 = MakeText("LoadingText", 27f, (TextAlignmentOptions)514, SavePicker.TitleColor); ((TMP_Text)obj2).transform.SetParent(_loadingRoot.transform, false); SavePicker.ApplyChromeTextStyle(obj2); ((TMP_Text)obj2).text = SavePickerLocalization.Get(PickerText.Loading); SavePicker.StretchFull((RectTransform)((TMP_Text)obj2).transform); _loadingRoot.SetActive(false); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"HelpScreen.EnsureLoadingUi failed (non-fatal): {arg}"); } } } private void EnsureUi() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Expected O, but got Unknown //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0284: 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_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Expected O, but got Unknown //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Expected O, but got Unknown //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Expected O, but got Unknown //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_045e: Unknown result type (might be due to invalid IL or missing references) //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_0493: Unknown result type (might be due to invalid IL or missing references) //IL_049d: Unknown result type (might be due to invalid IL or missing references) //IL_04b8: Unknown result type (might be due to invalid IL or missing references) //IL_04c2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root != (Object)null) { return; } try { _font = SavePicker.FindGameFont(); _root = new GameObject("PEAKQuickResume_HelpScreen", new Type[1] { typeof(RectTransform) }); _root.transform.SetParent(((Component)this).transform, false); Canvas obj = _root.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 30001; SavePicker.ApplyWidescreenScaler(obj); GameObject val = new GameObject("Dim", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_root.transform, false); _dimImage = val.AddComponent(); ((Graphic)_dimImage).color = new Color(SavePicker.DimColor.r, SavePicker.DimColor.g, SavePicker.DimColor.b, 0f); SavePicker.StretchFull((RectTransform)val.transform); GameObject val2 = new GameObject("Panel", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(_root.transform, false); _panelFillImage = val2.AddComponent(); _panelFillImage.type = (Type)0; ((Graphic)_panelFillImage).color = Color.white; _panelRect = (RectTransform)val2.transform; RectTransform panelRect = _panelRect; Vector2 anchorMin = (_panelRect.anchorMax = new Vector2(0.5f, 0.5f)); panelRect.anchorMin = anchorMin; _panelRect.pivot = new Vector2(0.5f, 0.5f); GameObject val4 = new GameObject("GrainMask", new Type[1] { typeof(RectTransform) }); val4.transform.SetParent(val2.transform, false); Image obj2 = val4.AddComponent(); obj2.sprite = SavePicker.PanelInnerMaskSprite(); obj2.type = (Type)1; ((Graphic)obj2).color = Color.white; val4.AddComponent().showMaskGraphic = false; RectTransform val5 = (RectTransform)val4.transform; val5.anchorMin = Vector2.zero; val5.anchorMax = Vector2.one; val5.offsetMin = new Vector2(11f, 11f); val5.offsetMax = new Vector2(-11f, -11f); GameObject val6 = new GameObject("Grain", new Type[1] { typeof(RectTransform) }); val6.transform.SetParent(val4.transform, false); Image val7 = val6.AddComponent(); val7.sprite = Sprite.Create(SavePicker.PanelGrainTexture(), new Rect(0f, 0f, 368f, 368f), new Vector2(0.5f, 0.5f), 100f); val7.type = (Type)0; ((Graphic)val7).color = Color.white; ((Graphic)val7).raycastTarget = false; SavePicker.StretchFull((RectTransform)val6.transform); _grainImage = val7; _titleText = MakeText("Title", 27f, (TextAlignmentOptions)258, SavePicker.TitleColor); SavePicker.ApplyChromeTextStyle(_titleText); ((TMP_Text)_titleText).textWrappingMode = (TextWrappingModes)1; RectTransform val8 = (RectTransform)((TMP_Text)_titleText).transform; ((Transform)val8).SetParent(val2.transform, false); val8.anchorMin = new Vector2(0f, 1f); val8.anchorMax = new Vector2(1f, 1f); val8.pivot = new Vector2(0.5f, 1f); val8.offsetMin = new Vector2(30f, val8.offsetMin.y); val8.offsetMax = new Vector2(-30f, val8.offsetMax.y); val8.anchoredPosition = new Vector2(0f, -20f); _bodyText = MakeText("Body", 20f, (TextAlignmentOptions)257, SavePicker.FooterColor); ((TMP_Text)_bodyText).textWrappingMode = (TextWrappingModes)1; _bodyRect = (RectTransform)((TMP_Text)_bodyText).transform; ((Transform)_bodyRect).SetParent(val2.transform, false); _bodyRect.anchorMin = new Vector2(0f, 1f); _bodyRect.anchorMax = new Vector2(1f, 1f); _bodyRect.pivot = new Vector2(0.5f, 1f); _bodyRect.offsetMin = new Vector2(30f, _bodyRect.offsetMin.y); _bodyRect.offsetMax = new Vector2(-30f, _bodyRect.offsetMax.y); ContentSizeFitter obj3 = ((Component)_bodyText).gameObject.AddComponent(); obj3.horizontalFit = (FitMode)0; obj3.verticalFit = (FitMode)2; BuildFooter(val2.transform); _root.SetActive(false); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"HelpScreen.EnsureUi failed (non-fatal, F1 screen will not render): {arg}"); } } } private void BuildFooter(Transform parent) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Footer", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); _footerRow = (RectTransform)val.transform; _footerRow.anchorMin = new Vector2(0.5f, 0f); _footerRow.anchorMax = new Vector2(0.5f, 0f); _footerRow.pivot = new Vector2(0.5f, 0f); _footerRow.sizeDelta = new Vector2(200f, 34f); _footerRow.anchoredPosition = new Vector2(0f, 20f); HorizontalLayoutGroup obj = val.AddComponent(); ((LayoutGroup)obj).childAlignment = (TextAnchor)4; ((HorizontalOrVerticalLayoutGroup)obj).spacing = 8f; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ContentSizeFitter obj2 = val.AddComponent(); obj2.horizontalFit = (FitMode)2; obj2.verticalFit = (FitMode)2; GameObject val2 = new GameObject("Badge", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(val.transform, false); Image obj3 = val2.AddComponent(); obj3.sprite = SavePicker.BadgeSprite(); obj3.type = (Type)1; ((Graphic)obj3).color = Color.white; HorizontalLayoutGroup obj4 = val2.AddComponent(); ((LayoutGroup)obj4).childAlignment = (TextAnchor)4; ((LayoutGroup)obj4).padding = new RectOffset(10, 10, 4, 4); ((HorizontalOrVerticalLayoutGroup)obj4).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj4).childControlHeight = true; ContentSizeFitter obj5 = val2.AddComponent(); obj5.horizontalFit = (FitMode)2; obj5.verticalFit = (FitMode)2; _footerKeyText = MakeText("Key", 16f, (TextAlignmentOptions)4098, SavePicker.KeyTextColor); ((TMP_Text)_footerKeyText).transform.SetParent(val2.transform, false); _footerLabelText = MakeText("Label", 17f, (TextAlignmentOptions)4098, SavePicker.FooterColor); ((TMP_Text)_footerLabelText).transform.SetParent(val.transform, false); SavePicker.ApplyChromeTextStyle(_footerLabelText); ContentSizeFitter obj6 = ((Component)_footerLabelText).gameObject.AddComponent(); obj6.horizontalFit = (FitMode)2; obj6.verticalFit = (FitMode)2; } private TextMeshProUGUI MakeText(string name, float fontSize, TextAlignmentOptions alignment, Color color) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI val = new GameObject(name, new Type[1] { typeof(RectTransform) }).AddComponent(); if ((Object)(object)_font != (Object)null) { ((TMP_Text)val).font = _font; } ((TMP_Text)val).fontSize = fontSize; ((TMP_Text)val).fontStyle = (FontStyles)0; ((Graphic)val).color = color; ((TMP_Text)val).richText = true; ((TMP_Text)val).alignment = alignment; ((TMP_Text)val).textWrappingMode = (TextWrappingModes)0; return val; } private void RebuildContent() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root == (Object)null) { return; } try { PluginConfig cfg = _cfg; string text = ((cfg != null) ? ((object)cfg.HelpKey.Value/*cast due to .constrained prefix*/).ToString() : null) ?? "F4"; ((TMP_Text)_titleText).text = "Quick Resume " + HelpScreenLocalization.Get(HelpText.HelpTitleWord); ((TMP_Text)_bodyText).text = HelpScreenContent.Build(_cfg); ((TMP_Text)_footerKeyText).text = text + " / Esc"; ((TMP_Text)_footerLabelText).text = HelpScreenLocalization.Get(HelpText.Close); float num = Mathf.Min(900f, SavePicker.CanvasWidthUnits - 80f) + 8f; _panelRect.sizeDelta = new Vector2(num, 200f); _root.SetActive(true); Canvas.ForceUpdateCanvases(); LayoutRebuilder.ForceRebuildLayoutImmediate(_bodyRect); float preferredHeight = ((TMP_Text)_bodyText).preferredHeight; float num2 = Mathf.Min(140f + preferredHeight, 1000f) + 8f; bool minimalUi = MinimalUi; _panelRect.sizeDelta = new Vector2(num, num2); _lastWidth = Mathf.RoundToInt(num); _lastHeight = Mathf.RoundToInt(num2); _panelFillImage.sprite = SavePicker.PanelSprite(_lastWidth, _lastHeight, _jagFrame, minimalUi); float num3 = ((_cfg != null) ? Mathf.Clamp01(_cfg.PanelOpacity.Value) : 1f); ((Graphic)_panelFillImage).color = new Color(1f, 1f, 1f, num3); if ((Object)(object)_grainImage != (Object)null) { ((Component)_grainImage).gameObject.SetActive(!minimalUi); ((Graphic)_grainImage).color = new Color(1f, 1f, 1f, num3); } RectTransform val = (RectTransform)((TMP_Text)_titleText).transform; val.offsetMin = new Vector2(30f, val.offsetMin.y); val.sizeDelta = new Vector2(val.sizeDelta.x, 42f); _bodyRect.anchoredPosition = new Vector2(0f, -72f); Canvas.ForceUpdateCanvases(); LayoutRebuilder.ForceRebuildLayoutImmediate(_panelRect); LayoutRebuilder.ForceRebuildLayoutImmediate(_footerRow); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("HelpScreen.RebuildContent failed (non-fatal): " + ex.Message)); } } } } internal static class HelpScreenContent { private const string Accent = "#FFF2B8"; private static string Key(string k) { return "(" + k + ")"; } public static string Build(PluginConfig cfg) { string k = Plugin.Instance?.ResumeKeyText ?? "F7"; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(HelpScreenLocalization.Get(HelpText.Intro1)).Append('\n'); bool flag = cfg?.ResumeKeyLoadsInsteadOfClosing.Value ?? false; stringBuilder.Append(HelpScreenLocalization.Get(flag ? HelpText.QuickResumeKeyLoadsFormat : HelpText.QuickResumeFormat, Key(k))).Append("\n\n"); string text = HelpScreenLocalization.Get(HelpText.RestartTip); int num = text.IndexOfAny(new char[2] { '(', '(' }); if (num > 0) { string text2 = text.Substring(0, num).TrimEnd(); text = "" + text2 + "" + text.Substring(text2.Length); } stringBuilder.Append(text).Append("\n\n"); stringBuilder.Append(HelpScreenLocalization.Get(HelpText.AchievementsNote)); Plugin instance = Plugin.Instance; if (instance != null && instance.CheckpointModInstalled) { stringBuilder.Append("\n\n").Append("" + MessagesLocalization.Get(MsgKey.CheckpointModStillInstalled) + ""); } return stringBuilder.ToString(); } } internal enum HelpText { HelpTitleWord, Close, Intro1, QuickResumeFormat, QuickResumeKeyLoadsFormat, RestartTip, AchievementsNote } internal static class HelpScreenLocalization { private static readonly Dictionary _table = new Dictionary { [HelpText.HelpTitleWord] = new string[15] { "Help", "Aide", "Aiuto", "Hilfe", "Ayuda", "Ayuda", "Ajuda", "Справка", "Довідка", "帮助", "幫助", "ヘルプ", "도움말", "Pomoc", "Yardım" }, [HelpText.Close] = new string[15] { "Close", "Fermer", "Chiudi", "Schließen", "Cerrar", "Cerrar", "Fechar", "Закрыть", "Закрити", "关闭", "關閉", "閉じる", "닫기", "Zamknij", "Kapat" }, [HelpText.Intro1] = new string[15] { "Progress saves at every campfire you light (host-only, the host must reload each session).", "La progression est sauvegardée à chaque feu de camp que vous allumez (hôte uniquement, l'hôte doit recharger à chaque session).", "I progressi vengono salvati a ogni falò che accendi (solo host, l'host deve ricaricare a ogni sessione).", "Der Fortschritt wird an jedem Lagerfeuer gespeichert, das du entzündest (nur Host, der Host muss die Sitzung jedes Mal neu laden).", "El progreso se guarda en cada hoguera que enciendes (solo el host, que debe recargar cada sesión).", "El progreso se guarda en cada fogata que enciendes (solo el host, que debe recargar cada sesión).", "O progresso é salvo em cada fogueira que você acende (somente o host, que precisa recarregar a cada sessão).", "Прогресс сохраняется у каждого зажжённого костра (только у хоста, хосту нужно перезагружать каждую сессию).", "Прогрес зберігається біля кожного запаленого вогнища (лише в хоста, хосту потрібно перезавантажувати щосесії).", "在你点燃的每个篝火处保存进度(仅限房主,房主每次都需要重新加载)。", "在你點燃的每個營火處儲存進度(僅限房主,房主每次遊戲都需要重新載入)。", "焚き火を灯すたびに進行状況が保存されます(ホストのみ。ホストはセッションごとに再読み込みが必要です)。", "모닥불을 피울 때마다 진행 상황이 저장됩니다 (호스트 전용, 호스트는 매 세션마다 다시 불러와야 합니다).", "Postęp zapisuje się przy każdym rozpalonym ognisku (tylko host, host musi wczytywać ponownie co sesję).", "İlerleme yaktığın her kamp ateşinde kaydedilir (yalnızca sunucu; sunucu her oturumda yeniden yüklemelidir)." }, [HelpText.QuickResumeFormat] = new string[15] { "Press {0} ANYWHERE to open the save picker, arrow keys to choose, Enter to load (the newest save is preselected), {0} or Escape to close.", "Appuyez sur {0} N'IMPORTE OÙ pour ouvrir le sélecteur de sauvegardes, les flèches pour choisir, Entrée pour charger (la sauvegarde la plus récente est présélectionnée), {0} ou Échap pour fermer.", "Premi {0} OVUNQUE per aprire il selettore dei salvataggi, le frecce per scegliere, Invio per caricare (il salvataggio più recente è preselezionato), {0} o Esc per chiudere.", "Drücke {0} ÜBERALL, um die Speicherstandauswahl zu öffnen, Pfeiltasten zum Auswählen, Enter zum Laden (der neueste Speicherstand ist vorausgewählt), {0} oder Escape zum Schließen.", "Pulsa {0} EN CUALQUIER LUGAR para abrir el selector de partidas, flechas para elegir, Intro para cargar (la partida más reciente está preseleccionada), {0} o Escape para cerrar.", "Presiona {0} EN CUALQUIER LUGAR para abrir el selector de partidas, flechas para elegir, Enter para cargar (la partida más reciente está preseleccionada), {0} o Escape para cerrar.", "Pressione {0} EM QUALQUER LUGAR para abrir o seletor de saves, setas para escolher, Enter para carregar (o save mais recente já vem selecionado), {0} ou Esc para fechar.", "Нажмите {0} В ЛЮБОМ МЕСТЕ, чтобы открыть выбор сохранений, стрелки для выбора, Enter для загрузки (последнее сохранение уже выбрано), {0} или Escape для закрытия.", "Натисніть {0} БУДЬ-ДЕ, щоб відкрити вибір збережень, стрілки для вибору, Enter для завантаження (останнє збереження вже вибрано), {0} або Escape, щоб закрити.", "在任何地方按下 {0} 打开存档选择器,方向键选择,回车加载(已默认选中最新存档),{0} 或 Esc 关闭。", "在任何地方按下 {0} 開啟存檔選擇器,方向鍵選擇,Enter 載入(已預設選取最新存檔),{0} 或 Esc 關閉。", "どこでも {0} を押すとセーブ選択画面が開きます。矢印キーで選択し、Enterでロード(最新のセーブが初期選択)、{0} または Esc で閉じます。", "어디서든 {0}을(를) 눌러 저장 파일 선택 화면을 열고, 방향키로 선택한 뒤 Enter로 불러오세요 (최신 저장 파일이 미리 선택됩니다). {0} 또는 Esc로 닫습니다.", "Naciśnij {0} GDZIEKOLWIEK, aby otworzyć wybór zapisów, strzałki do wyboru, Enter do wczytania (najnowszy zapis jest już wybrany), {0} lub Esc, aby zamknąć.", "Kayıt seçiciyi açmak için HER YERDE {0} tuşuna basın, seçim için ok tuşları, yüklemek için Enter (en yeni kayıt önceden seçilidir), kapatmak için {0} veya Esc." }, [HelpText.QuickResumeKeyLoadsFormat] = new string[15] { "Press {0} ANYWHERE to open the save picker, arrow keys to choose, {0}/Enter to load (press it twice for your latest).", "Appuyez sur {0} N'IMPORTE OÙ pour ouvrir le sélecteur de sauvegardes, les flèches pour choisir, {0}/Entrée pour charger (appuyez-y deux fois pour la plus récente).", "Premi {0} OVUNQUE per aprire il selettore dei salvataggi, le frecce per scegliere, {0}/Invio per caricare (premilo due volte per il più recente).", "Drücke {0} ÜBERALL, um die Speicherstandauswahl zu öffnen, Pfeiltasten zum Auswählen, {0}/Enter zum Laden (zweimal drücken für den neuesten).", "Pulsa {0} EN CUALQUIER LUGAR para abrir el selector de partidas, flechas para elegir, {0}/Intro para cargar (púlsalo dos veces para la más reciente).", "Presiona {0} EN CUALQUIER LUGAR para abrir el selector de partidas, flechas para elegir, {0}/Enter para cargar (presiónalo dos veces para la más reciente).", "Pressione {0} EM QUALQUER LUGAR para abrir o seletor de saves, setas para escolher, {0}/Enter para carregar (pressione duas vezes para o mais recente).", "Нажмите {0} В ЛЮБОМ МЕСТЕ, чтобы открыть выбор сохранений, стрелки для выбора, {0}/Enter для загрузки (нажмите дважды для последнего).", "Натисніть {0} БУДЬ-ДЕ, щоб відкрити вибір збережень, стрілки для вибору, {0}/Enter для завантаження (натисніть двічі для останнього).", "在任何地方按下 {0} 打开存档选择器,方向键选择,{0}/回车加载(连按两次加载最新存档)。", "在任何地方按下 {0} 開啟存檔選擇器,方向鍵選擇,{0}/Enter 載入(連按兩次載入最新存檔)。", "どこでも {0} を押すとセーブ選択画面が開きます。矢印キーで選択し、{0}/Enterでロード(2回押すと最新のセーブをロード)。", "어디서든 {0}을(를) 눌러 저장 파일 선택 화면을 열고, 방향키로 선택한 뒤 {0}/Enter로 불러오세요 (두 번 누르면 최신 저장 파일을 불러옵니다).", "Naciśnij {0} GDZIEKOLWIEK, aby otworzyć wybór zapisów, strzałki do wyboru, {0}/Enter do wczytania (naciśnij dwukrotnie, aby wczytać najnowszy).", "Kayıt seçiciyi açmak için HER YERDE {0} tuşuna basın, seçim için ok tuşları, yüklemek için {0}/Enter (en sonuncusu için iki kez basın)." }, [HelpText.RestartTip] = new string[15] { "Did something go wrong after loading? (blank map, bouncing up and down, falling through the floor, or you never actually reached your campfire)\nHave everyone quit and rejoin (or fully restart) the game, then load the same save again. This alone fixes most issues.", "Un problème est survenu après le chargement ? (carte vide, rebonds de haut en bas, chute à travers le sol, ou vous n'avez en fait jamais atteint votre feu de camp)\nQue tout le monde quitte et revienne (ou redémarre complètement) le jeu, puis rechargez la même sauvegarde. Cela suffit à résoudre la plupart des problèmes.", "Qualcosa è andato storto dopo il caricamento? (mappa vuota, rimbalzi su e giù, caduta attraverso il pavimento, oppure non hai mai effettivamente raggiunto il tuo falò)\nFai in modo che tutti escano e rientrino (o riavviino completamente) il gioco, poi ricarica lo stesso salvataggio. Questo da solo risolve la maggior parte dei problemi.", "Ist nach dem Laden etwas schiefgelaufen? (leere Karte, Auf-und-ab-Springen, Durchfallen durch den Boden, oder du hast dein Lagerfeuer eigentlich nie erreicht)\nLasst alle das Spiel verlassen und wieder beitreten (oder komplett neu starten) und ladet dann denselben Speicherstand erneut. Das allein behebt die meisten Probleme.", "¿Algo salió mal después de cargar? (mapa vacío, rebotando arriba y abajo, cayendo a través del suelo, o en realidad nunca llegaste a tu hoguera)\nQue todos salgan y vuelvan a entrar (o reinicien completamente) el juego, y luego recarguen la misma partida. Esto por sí solo soluciona la mayoría de los problemas.", "¿Algo salió mal después de cargar? (mapa vacío, rebotando arriba y abajo, cayendo a través del suelo, o en realidad nunca llegaste a tu fogata)\nQue todos salgan y vuelvan a entrar (o reinicien completamente) el juego, y luego recarguen la misma partida. Esto por sí solo soluciona la mayoría de los problemas.", "Algo deu errado depois de carregar? (mapa vazio, quicando para cima e para baixo, caindo através do chão, ou você nunca chegou realmente à sua fogueira)\nFaça todos saírem e entrarem novamente (ou reiniciarem completamente) o jogo, depois recarreguem o mesmo save. Isso sozinho resolve a maioria dos problemas.", "Что-то пошло не так после загрузки? (пустая карта, подпрыгивание вверх-вниз, падение сквозь пол, или вы так и не добрались до костра)\nПусть все выйдут и снова зайдут в игру (или полностью перезапустят её), а затем загрузите то же сохранение. Одно это решает большинство проблем.", "Щось пішло не так після завантаження? (порожня карта, підстрибування вгору-вниз, падіння крізь підлогу, або ви так і не дісталися до вогнища)\nНехай усі вийдуть і знову зайдуть у гру (або повністю перезапустять її), а потім завантажте те саме збереження. Одне це вирішує більшість проблем.", "加载后出问题了吗?(地图空白、上下弹跳、穿过地板掉落,或者其实根本没有到达篝火旁)\n让所有人退出并重新加入游戏(或彻底重启游戏),然后再加载同一个存档。仅此一步就能解决大多数问题。", "載入後出問題了嗎?(地圖空白、上下彈跳、穿過地板掉落,或是根本沒有到達營火旁)\n讓所有人退出並重新加入遊戲(或完全重啟遊戲),然後再次載入同一個存檔。光是這一步就能解決大多數問題。", "ロード後に問題が起きましたか?(マップが空、上下に跳ね続ける、床をすり抜けて落下する、または実際には焚き火にたどり着いていない)\n全員が一度ゲームを退出して再参加する(またはゲームを完全に再起動する)、その後同じセーブをロードし直してください。これだけでほとんどの問題が解決します。", "불러온 뒤 문제가 발생했나요? (빈 맵, 위아래로 튕김, 바닥을 뚫고 떨어짐, 또는 실제로 모닥불에 도착하지 않음)\n모두가 게임을 나갔다가 다시 참가하거나(또는 게임을 완전히 재시작) 한 뒤, 같은 저장 파일을 다시 불러오세요. 이것만으로 대부분의 문제가 해결됩니다.", "Czy po wczytaniu coś poszło nie tak? (pusta mapa, odbijanie się góra-dół, przepadanie przez podłogę, albo w ogóle nie dotarłeś do ogniska)\nNiech wszyscy opuszczą grę i dołączą ponownie (albo całkowicie ją zrestartują), a następnie wczytajcie ten sam zapis. Samo to rozwiązuje większość problemów.", "Yükledikten sonra bir şeyler ters mi gitti? (boş harita, yukarı aşağı zıplama, zeminin içinden düşme, veya aslında hiç kamp ateşine ulaşmama)\nHerkesin oyundan çıkıp yeniden katılmasını (veya oyunu tamamen yeniden başlatmasını) sağlayın, ardından aynı kaydı tekrar yükleyin. Bu tek başına çoğu sorunu çözer." }, [HelpText.AchievementsNote] = new string[15] { "Achievement progress is saved and restored correctly when you load a checkpoint, but only for players who have PEAK Quick Resume installed themselves.", "La progression des succès est sauvegardée et restaurée correctement lorsque vous chargez un point de contrôle, mais uniquement pour les joueurs ayant eux-mêmes PEAK Quick Resume installé.", "I progressi degli obiettivi vengono salvati e ripristinati correttamente quando carichi un checkpoint, ma solo per i giocatori che hanno PEAK Quick Resume installato personalmente.", "Der Erfolgsfortschritt wird beim Laden eines Checkpoints korrekt gespeichert und wiederhergestellt, aber nur für Spieler, die PEAK Quick Resume selbst installiert haben.", "El progreso de los logros se guarda y se restaura correctamente al cargar un punto de control, pero solo para los jugadores que tengan PEAK Quick Resume instalado ellos mismos.", "El progreso de los logros se guarda y se restaura correctamente al cargar un punto de control, pero solo para los jugadores que tengan PEAK Quick Resume instalado ellos mismos.", "O progresso das conquistas é salvo e restaurado corretamente ao carregar um checkpoint, mas apenas para jogadores que tiverem o PEAK Quick Resume instalado.", "Прогресс достижений корректно сохраняется и восстанавливается при загрузке чекпоинта, но только для игроков, у которых сам установлен PEAK Quick Resume.", "Прогрес досягнень коректно зберігається та відновлюється під час завантаження чекпоінта, але лише для гравців, які самі встановили PEAK Quick Resume.", "加载存档点时,成就进度会被正确保存和恢复,但仅限自己安装了 PEAK Quick Resume 的玩家。", "載入存檔點時,成就進度會被正確儲存與復原,但僅限自己安裝了 PEAK Quick Resume 的玩家。", "チェックポイントをロードすると実績の進行状況が正しく保存・復元されますが、これはPEAK Quick Resumeを自分自身でインストールしているプレイヤーにのみ適用されます。", "체크포인트를 불러오면 업적 진행 상황이 올바르게 저장 및 복원되지만, 본인이 직접 PEAK Quick Resume를 설치한 플레이어에게만 해당됩니다.", "Postęp osiągnięć jest poprawnie zapisywany i przywracany podczas wczytywania punktu kontrolnego, ale tylko dla graczy, którzy sami mają zainstalowany PEAK Quick Resume.", "Bir kontrol noktası yüklediğinizde başarım ilerlemesi doğru şekilde kaydedilir ve geri yüklenir, ancak bu yalnızca PEAK Quick Resume'u kendisi yüklemiş oyuncular için geçerlidir." } }; public static string Get(HelpText key) { return LocalizationHelper.Resolve(_table[key]); } public static string Get(HelpText key, params object[] args) { return string.Format(Get(key), args); } } internal static class LocalizationHelper { public static string Resolve(string[] arr) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected I4, but got Unknown int num = (int)LocalizedText.CURRENT_LANGUAGE; if (num >= 0 && num < arr.Length && !string.IsNullOrEmpty(arr[num])) { return arr[num]; } return arr[0]; } } public static class LuggageRestore { private const float LuggageSearchRadius = 30f; private const float ItemSearchRadius = 10f; public static void Capture(Vector3 fallbackPos, HashSet claimed, ManualLogSource log, out List states) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0227: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: 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_0150: Unknown result type (might be due to invalid IL or missing references) states = new List(); try { Vector3 val = CampfireAreaHelpers.ResolveNearestCampfirePos(fallbackPos); float num = NadirSearchRadius.ForCurrentSegment(30f); List list = FindLuggageNear(val, num); if (list.Count == 0) { log.Trace($"LuggageRestore.Capture: no luggage found within {num}m of {val}."); return; } foreach (Luggage item in list) { OwnSavedLuggageState ownSavedLuggageState = new OwnSavedLuggageState { opened = item.IsOpen }; if (item.IsOpen) { foreach (Item item2 in CampfireAreaHelpers.FindFreeItemsWithin(((Component)item).transform.position, 10f, includeBackpacks: false, claimed)) { OwnSavedPositionedItem ownSavedPositionedItem = new OwnSavedPositionedItem { itemId = item2.itemID, posX = ((Component)item2).transform.position.x, posY = ((Component)item2).transform.position.y, posZ = ((Component)item2).transform.position.z, rotX = ((Component)item2).transform.rotation.x, rotY = ((Component)item2).transform.rotation.y, rotZ = ((Component)item2).transform.rotation.z, rotW = ((Component)item2).transform.rotation.w }; foreach (KeyValuePair item3 in OwnItemStateIO.ReadItemStateValues(item2.data)) { ownSavedPositionedItem.values[item3.Key] = new OwnSavedEntry { type = item3.Value.TypeName, value = item3.Value.Value }; } ownSavedLuggageState.items.Add(ownSavedPositionedItem); claimed?.Add(item2); } } states.Add(ownSavedLuggageState); log.Trace($"LuggageRestore.Capture: luggage '{((Object)item).name}' at {((Component)item).transform.position}, " + $"opened={ownSavedLuggageState.opened}, items={ownSavedLuggageState.items.Count}."); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("LuggageRestore.Capture failed (non-fatal): " + ex.Message)); } } } public static void Restore(OwnSaveData data, Vector3 fallbackPos, ManualLogSource log) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (data?.luggageStates == null || data.luggageStates.Count == 0) { log.Trace("LuggageRestore.Restore: nothing to restore for this load."); return; } try { Vector3 val = CampfireAreaHelpers.ResolveNearestCampfirePos(fallbackPos); float num = NadirSearchRadius.ForSavedSegment(30f, data); List list = FindLuggageNear(val, num); if (list.Count == 0) { if (log != null) { log.LogWarning((object)$"LuggageRestore: no luggage found within {num}m of {val}, nothing to restore."); } return; } int num2 = Math.Min(list.Count, data.luggageStates.Count); if (list.Count != data.luggageStates.Count && log != null) { log.LogWarning((object)($"LuggageRestore: found {list.Count} luggage box(es) but saved {data.luggageStates.Count} - " + $"restoring the first {num2}, matched by ascending distance from the campfire.")); } for (int i = 0; i < num2; i++) { RestoreOne(list[i], data.luggageStates[i], log); } } catch (Exception arg) { if (log != null) { log.LogError((object)$"LuggageRestore.Restore failed (non-fatal): {arg}"); } } } private static void RestoreOne(Luggage box, OwnSavedLuggageState state, ManualLogSource log) { //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) if (state == null || !state.opened || box.IsOpen) { return; } PhotonView component = ((Component)box).GetComponent(); if ((Object)(object)component == (Object)null) { if (log != null) { log.LogWarning((object)("LuggageRestore: luggage '" + ((Object)box).name + "' has no PhotonView, cannot restore.")); } return; } component.RPC("OpenLuggageRPC", (RpcTarget)3, new object[1] { false }); log.Trace("LuggageRestore: restored luggage '" + ((Object)box).name + "' to its open state."); if (state.items.Count == 0) { return; } Item val = default(Item); Vector3 val2 = default(Vector3); Quaternion val3 = default(Quaternion); PhotonView val5 = default(PhotonView); foreach (OwnSavedPositionedItem item in state.items) { if (!ItemDatabase.TryGetItem(item.itemId, ref val) || (Object)(object)val == (Object)null) { if (log != null) { log.LogWarning((object)$"LuggageRestore: could not find item prefab for id {item.itemId}, skipping."); } continue; } ((Vector3)(ref val2))..ctor(item.posX, item.posY, item.posZ); ((Quaternion)(ref val3))..ctor(item.rotX, item.rotY, item.rotZ, item.rotW); GameObject val4 = PhotonNetwork.InstantiateItemRoom(((Object)val).name, val2, val3); if ((Object)(object)val4 == (Object)null) { if (log != null) { log.LogWarning((object)$"LuggageRestore: InstantiateItemRoom returned null for item {item.itemId}."); } continue; } if (((Spawner)box).isKinematic && val4.TryGetComponent(ref val5)) { val5.RPC("SetKinematicRPC", (RpcTarget)3, new object[3] { true, val2, val3 }); } CampfireAreaHelpers.ApplySavedItemValues(val4, item.values, log); log.Trace($"LuggageRestore: respawned saved item {item.itemId} at {val2} for luggage '{((Object)box).name}'."); } } private static List FindLuggageNear(Vector3 center, float radius) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return (from box in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)box != (Object)null && !(box is RespawnChest) && Vector3.Distance(((Component)box).transform.position, center) <= radius orderby Vector3.Distance(((Component)box).transform.position, center) select box).ToList(); } } public static class MapBakerLevelOverridePatch { public static void Apply(Harmony harmony, ManualLogSource log) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(typeof(MapBaker), "GetLevel", (Type[])null, (Type[])null); HarmonyMethod val = new HarmonyMethod(typeof(MapBakerLevelOverridePatch), "Prefix", (Type[])null) { priority = 0 }; harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"MapBakerLevelOverridePatch: patched MapBaker.GetLevel."); } catch (Exception arg) { log.LogError((object)$"MapBakerLevelOverridePatch.Apply failed (non-fatal): {arg}"); } } private static bool Prefix(int levelIndex, ref string __result) { string selectedLevel = OwnLoadEntryPoints.SelectedLevel; if (string.IsNullOrEmpty(selectedLevel) || selectedLevel == "null") { return true; } __result = selectedLevel; OwnLoadEntryPoints.ClearSelectedLevel(); return false; } } internal enum MsgKey { OnlyHostResume, LoadIntoGameFirst, QuickResumeStarting, NoSaveCustom, NoSaveDifficulty, StartingFreshRun, WaitingForPlayers, PlayersTimedOut, SaveLoadedWelcomeBack, SavedGameProgress, OnlyHostRestart, RestartingRun, RunRestarted, RestartFailed, NoSavesSolo, NoSavesCoop, TeleportBugHint, LoadingSaveScreen, CheckpointModStillInstalled, CheckpointModStillInstalledShort, GameUpdatedSavesMayBeWrong, GameUpdateDialogSuppressed } internal static class MessagesLocalization { private static readonly Dictionary _table = new Dictionary { [MsgKey.OnlyHostResume] = new string[15] { "Only the host can resume the save!", "Seul l'hôte peut reprendre la sauvegarde !", "Solo l'host può riprendere la partita salvata!", "Nur der Host kann den Spielstand fortsetzen!", "¡Solo el host puede reanudar la partida guardada!", "¡Solo el host puede reanudar la partida guardada!", "Somente o host pode retomar o save!", "Только хост может продолжить сохранённую игру!", "Лише хост може відновити збережену гру!", "只有房主才能恢复存档!", "只有房主才能恢復存檔!", "セーブデータの再開はホストのみ可能です!", "호스트만 저장된 게임을 재개할 수 있습니다!", "Tylko host może wznowić zapisaną grę!", "Kayıtlı oyunu yalnızca host devam ettirebilir!" }, [MsgKey.LoadIntoGameFirst] = new string[15] { "Load into the game first.", "Chargez d'abord une partie.", "Prima entra in una partita.", "Lade zuerst ein Spiel.", "Primero carga una partida.", "Primero carga una partida.", "Primeiro entre em uma partida.", "Сначала загрузитесь в игру.", "Спочатку завантажтеся в гру.", "请先进入游戏。", "請先進入遊戲。", "まずゲームに入ってください。", "먼저 게임에 접속하세요.", "Najpierw wejdź do gry.", "Önce oyuna girin." }, [MsgKey.QuickResumeStarting] = new string[15] { "Quick Resume: starting...", "Reprise rapide : démarrage...", "Ripresa rapida: avvio in corso...", "Quick Resume: wird gestartet...", "Reanudación rápida: iniciando...", "Reanudación rápida: iniciando...", "Retomada rápida: iniciando...", "Быстрое возобновление: запуск...", "Швидке відновлення: запуск...", "快速续玩:启动中...", "快速續玩:啟動中...", "クイックレジューム: 開始中...", "퀵 레주메: 시작 중...", "Szybkie wznowienie: uruchamianie...", "Hızlı Devam: başlatılıyor..." }, [MsgKey.NoSaveCustom] = new string[15] { "No save found for this custom run.", "Aucune sauvegarde trouvée pour cette partie personnalisée.", "Nessun salvataggio trovato per questa partita personalizzata.", "Kein Speicherstand für diesen benutzerdefinierten Lauf gefunden.", "No se encontró ninguna partida guardada para esta partida personalizada.", "No se encontró ninguna partida guardada para esta partida personalizada.", "Nenhum save encontrado para esta corrida personalizada.", "Сохранение для этого пользовательского забега не найдено.", "Збереження для цього користувацького забігу не знайдено.", "未找到该自定义局的存档。", "找不到此自訂遊戲的存檔。", "このカスタムランのセーブデータが見つかりません。", "이 커스텀 런에 대한 저장 파일을 찾을 수 없습니다.", "Nie znaleziono zapisu dla tego własnego przebiegu.", "Bu özel koşu için kayıt bulunamadı." }, [MsgKey.NoSaveDifficulty] = new string[15] { "No save found for this difficulty (ascent {0}).", "Aucune sauvegarde trouvée pour cette difficulté (ascension {0}).", "Nessun salvataggio trovato per questa difficoltà (ascensione {0}).", "Kein Speicherstand für diesen Schwierigkeitsgrad gefunden (Aufstieg {0}).", "No se encontró ninguna partida guardada para esta dificultad (ascenso {0}).", "No se encontró ninguna partida guardada para esta dificultad (ascenso {0}).", "Nenhum save encontrado para esta dificuldade (ascensão {0}).", "Сохранение для этой сложности не найдено (восхождение {0}).", "Збереження для цієї складності не знайдено (сходження {0}).", "未找到该难度的存档(攀登 {0})。", "找不到此難度的存檔(攀登 {0})。", "この難易度のセーブデータが見つかりません (アセント {0})。", "이 난이도에 대한 저장 파일을 찾을 수 없습니다 (어센트 {0}).", "Nie znaleziono zapisu dla tego poziomu trudności (wspinaczka {0}).", "Bu zorluk için kayıt bulunamadı (çıkış {0})." }, [MsgKey.StartingFreshRun] = new string[15] { "Starting a fresh run of your saved difficulty...", "Démarrage d'une nouvelle partie de votre difficulté sauvegardée...", "Avvio di una nuova partita alla tua difficoltà salvata...", "Ein neuer Lauf deines gespeicherten Schwierigkeitsgrads wird gestartet...", "Iniciando una nueva partida de tu dificultad guardada...", "Iniciando una nueva partida de tu dificultad guardada...", "Iniciando uma nova corrida na sua dificuldade salva...", "Запускается новый забег вашей сохранённой сложности...", "Запускається новий забіг вашої збереженої складності...", "正在以你保存的难度开始新的一局...", "正在以你儲存的難度開始新的一局...", "保存された難易度で新しいランを開始しています...", "저장된 난이도로 새로운 런을 시작합니다...", "Rozpoczynanie nowego przebiegu na zapisanym poziomie trudności...", "Kayıtlı zorluğunuzla yeni bir koşu başlatılıyor..." }, [MsgKey.WaitingForPlayers] = new string[15] { "Waiting for other players to load...", "En attente du chargement des autres joueurs...", "In attesa che gli altri giocatori carichino...", "Warte, bis andere Spieler geladen haben...", "Esperando a que otros jugadores carguen...", "Esperando a que otros jugadores carguen...", "Aguardando outros jogadores carregarem...", "Ожидание загрузки других игроков...", "Очікування завантаження інших гравців...", "正在等待其他玩家加载...", "正在等待其他玩家載入...", "他のプレイヤーの読み込みを待っています...", "다른 플레이어의 로딩을 기다리는 중...", "Oczekiwanie na wczytanie innych graczy...", "Diğer oyuncuların yüklenmesi bekleniyor..." }, [MsgKey.PlayersTimedOut] = new string[15] { "Some players didn't finish loading in time. Try again.", "Certains joueurs n'ont pas fini de charger à temps. Réessayez.", "Alcuni giocatori non hanno terminato il caricamento in tempo. Riprova.", "Einige Spieler haben das Laden nicht rechtzeitig abgeschlossen. Versuche es erneut.", "Algunos jugadores no terminaron de cargar a tiempo. Inténtalo de nuevo.", "Algunos jugadores no terminaron de cargar a tiempo. Inténtalo de nuevo.", "Alguns jogadores não terminaram de carregar a tempo. Tente novamente.", "Некоторые игроки не успели загрузиться. Попробуйте снова.", "Деякі гравці не встигли завантажитися. Спробуйте ще раз.", "部分玩家未能及时加载完成,请重试。", "部分玩家未能及時載入完成,請重試。", "一部のプレイヤーの読み込みが間に合いませんでした。もう一度お試しください。", "일부 플레이어가 제시간에 로딩을 마치지 못했습니다. 다시 시도하세요.", "Niektórzy gracze nie zdążyli się wczytać. Spróbuj ponownie.", "Bazı oyuncular zamanında yüklenemedi. Tekrar deneyin." }, [MsgKey.SaveLoadedWelcomeBack] = new string[15] { "Save loaded. Welcome back!", "Sauvegarde chargée. Bon retour !", "Salvataggio caricato. Bentornato!", "Spielstand geladen. Willkommen zurück!", "Partida cargada. ¡Bienvenido de nuevo!", "Partida cargada. ¡Bienvenido de nuevo!", "Save carregado. Bem-vindo de volta!", "Сохранение загружено. С возвращением!", "Збереження завантажено. З поверненням!", "存档加载完成,欢迎回来!", "存檔載入完成,歡迎回來!", "セーブデータを読み込みました。おかえりなさい!", "저장 파일을 불러왔습니다. 다시 오신 것을 환영합니다!", "Zapis wczytany. Witaj z powrotem!", "Kayıt yüklendi. Tekrar hoş geldin!" }, [MsgKey.SavedGameProgress] = new string[15] { "Saved game progress", "Progression sauvegardée", "Progressi salvati", "Spielfortschritt gespeichert", "Progreso guardado", "Progreso guardado", "Progresso salvo", "Прогресс игры сохранён", "Прогрес гри збережено", "游戏进度已保存", "遊戲進度已儲存", "ゲームの進行状況を保存しました", "게임 진행 상황이 저장되었습니다", "Zapisano postęp gry", "Oyun ilerlemesi kaydedildi" }, [MsgKey.OnlyHostRestart] = new string[15] { "Only the host can restart the run!", "Seul l'hôte peut redémarrer la partie !", "Solo l'host può riavviare la partita!", "Nur der Host kann den Lauf neu starten!", "¡Solo el host puede reiniciar la partida!", "¡Solo el host puede reiniciar la partida!", "Somente o host pode reiniciar a corrida!", "Только хост может перезапустить забег!", "Лише хост може перезапустити забіг!", "只有房主才能重新开始本局!", "只有房主才能重新開始這一局!", "ランの再開始はホストのみ可能です!", "호스트만 런을 재시작할 수 있습니다!", "Tylko host może zrestartować przebieg!", "Koşuyu yalnızca host yeniden başlatabilir!" }, [MsgKey.RestartingRun] = new string[15] { "Restarting run...", "Redémarrage de la partie...", "Riavvio della partita...", "Lauf wird neu gestartet...", "Reiniciando partida...", "Reiniciando partida...", "Reiniciando corrida...", "Перезапуск забега...", "Перезапуск забігу...", "正在重新开始...", "正在重新開始...", "ランを再開始しています...", "런을 재시작하는 중...", "Restartowanie przebiegu...", "Koşu yeniden başlatılıyor..." }, [MsgKey.RunRestarted] = new string[15] { "Run restarted!", "Partie redémarrée !", "Partita riavviata!", "Lauf neu gestartet!", "¡Partida reiniciada!", "¡Partida reiniciada!", "Corrida reiniciada!", "Забег перезапущен!", "Забіг перезапущено!", "已重新开始!", "已重新開始!", "ランを再開始しました!", "런이 재시작되었습니다!", "Przebieg zrestartowany!", "Koşu yeniden başlatıldı!" }, [MsgKey.RestartFailed] = new string[15] { "Restart failed, see the log for details.", "Échec du redémarrage, consultez le journal pour plus de détails.", "Riavvio non riuscito, controlla il log per i dettagli.", "Neustart fehlgeschlagen, Details siehe Log.", "El reinicio falló, consulta el registro para más detalles.", "El reinicio falló, consulta el registro para más detalles.", "Falha ao reiniciar, veja o log para mais detalhes.", "Не удалось перезапустить, подробности в логе.", "Не вдалося перезапустити, деталі в лозі.", "重新开始失败,详情请查看日志。", "重新開始失敗,詳情請查看日誌。", "再開始に失敗しました。詳細はログを確認してください。", "재시작에 실패했습니다. 자세한 내용은 로그를 확인하세요.", "Restart nie powiódł się, szczegóły w logu.", "Yeniden başlatma başarısız oldu, ayrıntılar için günlüğe bakın." }, [MsgKey.NoSavesSolo] = new string[15] { "No solo saves found yet.", "Aucune sauvegarde solo trouvée pour l'instant.", "Nessun salvataggio solo trovato ancora.", "Bisher keine Solo-Speicherstände gefunden.", "Aún no se encontraron partidas guardadas en solitario.", "Aún no se encontraron partidas guardadas en solitario.", "Ainda não há saves solo encontrados.", "Соло-сохранения пока не найдены.", "Соло-збережень поки не знайдено.", "暂未找到单人存档。", "尚未找到單人存檔。", "ソロのセーブデータはまだ見つかりません。", "아직 솔로 저장 파일이 없습니다.", "Nie znaleziono jeszcze żadnych zapisów solo.", "Henüz solo kayıt bulunamadı." }, [MsgKey.NoSavesCoop] = new string[15] { "No co-op saves found yet.", "Aucune sauvegarde coop trouvée pour l'instant.", "Nessun salvataggio co-op trovato ancora.", "Bisher keine Koop-Speicherstände gefunden.", "Aún no se encontraron partidas guardadas cooperativas.", "Aún no se encontraron partidas guardadas cooperativas.", "Ainda não há saves cooperativos encontrados.", "Кооп-сохранения пока не найдены.", "Кооп-збережень поки не знайдено.", "暂未找到合作存档。", "尚未找到合作存檔。", "協力プレイのセーブデータはまだ見つかりません。", "아직 협동 저장 파일이 없습니다.", "Nie znaleziono jeszcze żadnych zapisów kooperacji.", "Henüz co-op kayıt bulunamadı." }, [MsgKey.TeleportBugHint] = new string[15] { "Did something go wrong? If yes, press {0} to open the help screen.", "Un problème est survenu ? Si oui, appuyez sur {0} pour ouvrir l'écran d'aide.", "Qualcosa è andato storto? Se sì, premi {0} per aprire la schermata di aiuto.", "Ist etwas schiefgelaufen? Falls ja, drücke {0}, um den Hilfebildschirm zu öffnen.", "¿Ha ido algo mal? Si es así, pulsa {0} para abrir la pantalla de ayuda.", "¿Salió algo mal? Si es así, presiona {0} para abrir la pantalla de ayuda.", "Algo deu errado? Se sim, pressione {0} para abrir a tela de ajuda.", "Что-то пошло не так? Если да, нажмите {0}, чтобы открыть экран помощи.", "Щось пішло не так? Якщо так, натисніть {0}, щоб відкрити екран довідки.", "出了什么问题吗?如果是的话,按 {0} 打开帮助界面。", "出了什麼問題嗎?如果是的話,按 {0} 開啟說明畫面。", "何か問題が起きましたか?もしそうなら、{0}を押してヘルプ画面を開いてください。", "무언가 잘못되었나요? 그렇다면 {0}을 눌러 도움말 화면을 열어보세요.", "Czy coś poszło nie tak? Jeśli tak, naciśnij {0}, aby otworzyć ekran pomocy.", "Bir şeyler mi ters gitti? Öyleyse, yardım ekranını açmak için {0} tuşuna basın." }, [MsgKey.LoadingSaveScreen] = new string[15] { "LOADING SAVE", "CHARGEMENT SAUVEGARDE", "CARICAMENTO SALVATAGGIO", "SPEICHERSTAND LADEN", "CARGANDO PARTIDA", "CARGANDO PARTIDA", "CARREGANDO SAVE", "ЗАГРУЗКА СОХРАНЕНИЯ", "ЗАВАНТАЖЕННЯ ЗБЕРЕЖЕННЯ", "正在加载存档", "正在載入存檔", "セーブデータ読み込み中", "저장 파일 로딩 중", "WCZYTYWANIE ZAPISU", "KAYIT YÜKLENİYOR" }, [MsgKey.CheckpointModStillInstalled] = new string[15] { "PEAK Checkpoint Save is still installed. Quick Resume no longer needs it and runs fully on its own; both mods will save/log independently, so expect duplicate log messages and saves appearing. This is harmless (no logic conflict), but uninstall PEAK Checkpoint Save to remove the duplicates.", "PEAK Checkpoint Save est toujours installé. Quick Resume n'en a plus besoin et fonctionne entièrement seul ; les deux mods sauvegardent et écrivent leurs logs indépendamment, attendez-vous donc à des messages de log et des sauvegardes en double. C'est sans danger (aucun conflit de logique), mais désinstallez PEAK Checkpoint Save pour supprimer les doublons.", "PEAK Checkpoint Save è ancora installato. Quick Resume non ne ha più bisogno e funziona in modo del tutto autonomo; entrambe le mod salvano e registrano i log in modo indipendente, quindi aspettati messaggi di log e salvataggi duplicati. È innocuo (nessun conflitto logico), ma disinstalla PEAK Checkpoint Save per eliminare i duplicati.", "PEAK Checkpoint Save ist noch installiert. Quick Resume benötigt es nicht mehr und funktioniert völlig eigenständig; beide Mods speichern und loggen unabhängig voneinander, erwarte also doppelte Log-Meldungen und doppelte Speicherungen. Das ist harmlos (kein Logikkonflikt), aber deinstalliere PEAK Checkpoint Save, um die Duplikate zu entfernen.", "PEAK Checkpoint Save sigue instalado. Quick Resume ya no lo necesita y funciona completamente por su cuenta; ambos mods guardan y registran de forma independiente, así que espera mensajes de registro y guardados duplicados. Es inofensivo (sin conflicto de lógica), pero desinstala PEAK Checkpoint Save para eliminar los duplicados.", "PEAK Checkpoint Save sigue instalado. Quick Resume ya no lo necesita y funciona completamente por su cuenta; ambos mods guardan y registran de forma independiente, así que espera mensajes de registro y guardados duplicados. Es inofensivo (sin conflicto de lógica), pero desinstala PEAK Checkpoint Save para eliminar los duplicados.", "PEAK Checkpoint Save ainda está instalado. O Quick Resume não precisa mais dele e funciona totalmente sozinho; os dois mods salvam e registram logs de forma independente, então espere mensagens de log e saves duplicados. Isso é inofensivo (nenhum conflito de lógica), mas desinstale o PEAK Checkpoint Save para remover os duplicados.", "PEAK Checkpoint Save всё ещё установлен. Quick Resume больше в нём не нуждается и работает полностью самостоятельно; оба мода сохраняют и пишут логи независимо, поэтому ожидайте дублирующиеся сообщения в логе и сохранения. Это безвредно (нет конфликта логики), но удалите PEAK Checkpoint Save, чтобы убрать дубликаты.", "PEAK Checkpoint Save усе ще встановлений. Quick Resume більше його не потребує й працює повністю самостійно; обидва моди зберігають і пишуть логи незалежно, тож очікуйте дубльовані повідомлення в лозі та збереження. Це безпечно (немає конфлікту логіки), але видаліть PEAK Checkpoint Save, щоб прибрати дублікати.", "PEAK Checkpoint Save 仍已安装。Quick Resume 不再需要它,可以完全独立运行;两个模组会各自独立保存和记录日志,因此你会看到重复的日志消息和存档。这没有危害(不会产生逻辑冲突),但卸载 PEAK Checkpoint Save 即可消除这些重复。", "PEAK Checkpoint Save 仍已安裝。Quick Resume 不再需要它,可以完全獨立運作;兩個模組會各自獨立儲存與記錄日誌,因此你會看到重複的日誌訊息和存檔。這沒有危害(不會產生邏輯衝突),但解除安裝 PEAK Checkpoint Save 即可消除這些重複。", "PEAK Checkpoint Save がまだインストールされています。Quick Resume はもう必要とせず、完全に単独で動作します。両方のModがそれぞれ独立してセーブとログ記録を行うため、ログメッセージとセーブが重複して表示されます。これは無害です(ロジックの競合はありません)が、重複をなくすには PEAK Checkpoint Save をアンインストールしてください。", "PEAK Checkpoint Save가 아직 설치되어 있습니다. Quick Resume는 더 이상 이것이 필요 없으며 완전히 독립적으로 작동합니다. 두 모드가 각각 독립적으로 저장하고 로그를 기록하므로 로그 메시지와 저장이 중복되어 나타납니다. 이는 무해하지만(로직 충돌 없음), 중복을 없애려면 PEAK Checkpoint Save를 제거하세요.", "PEAK Checkpoint Save jest nadal zainstalowany. Quick Resume już go nie potrzebuje i działa całkowicie samodzielnie; oba mody zapisują i logują niezależnie, więc spodziewaj się zduplikowanych komunikatów w logu i zapisów. Jest to nieszkodliwe (brak konfliktu logiki), ale odinstaluj PEAK Checkpoint Save, aby usunąć duplikaty.", "PEAK Checkpoint Save hâlâ yüklü. Quick Resume artık ona ihtiyaç duymuyor ve tamamen kendi başına çalışıyor; her iki mod da bağımsız olarak kaydediyor ve log tutuyor, bu yüzden yinelenen log mesajları ve kayıtlar görmeyi bekleyin. Bu zararsızdır (mantık çakışması yok), ancak yinelenenleri kaldırmak için PEAK Checkpoint Save'i kaldırın." }, [MsgKey.CheckpointModStillInstalledShort] = new string[15] { "PEAK Checkpoint Save is still installed and no longer needed. Expect harmless duplicate saves and logs. Press {0} for details.", "PEAK Checkpoint Save est toujours installé et n'est plus nécessaire. Attendez-vous à des sauvegardes et des logs en double, sans danger. Appuyez sur {0} pour plus de détails.", "PEAK Checkpoint Save è ancora installato e non più necessario. Aspettati salvataggi e log duplicati, del tutto innocui. Premi {0} per i dettagli.", "PEAK Checkpoint Save ist noch installiert und wird nicht mehr benötigt. Rechne mit harmlosen doppelten Speicherungen und Logs. Drücke {0} für Details.", "PEAK Checkpoint Save sigue instalado y ya no es necesario. Espera guardados y registros duplicados, sin ningún problema. Pulsa {0} para más detalles.", "PEAK Checkpoint Save sigue instalado y ya no es necesario. Espera guardados y registros duplicados, sin ningún problema. Presiona {0} para más detalles.", "PEAK Checkpoint Save ainda está instalado e não é mais necessário. Espere saves e logs duplicados, sem problema algum. Pressione {0} para mais detalhes.", "PEAK Checkpoint Save всё ещё установлен и больше не нужен. Ожидайте безвредные дублирующиеся сохранения и логи. Нажмите {0} для подробностей.", "PEAK Checkpoint Save усе ще встановлений і більше не потрібен. Очікуйте безпечні дубльовані збереження та логи. Натисніть {0} для деталей.", "PEAK Checkpoint Save 仍已安装,但已不再需要。你会看到重复的存档和日志,这没有危害。按 {0} 查看详情。", "PEAK Checkpoint Save 仍已安裝,但已不再需要。你會看到重複的存檔和日誌,這沒有危害。按 {0} 查看詳情。", "PEAK Checkpoint Save がまだインストールされていますが、もう必要ありません。無害ですが、セーブとログが重複します。詳細は {0} を押してください。", "PEAK Checkpoint Save가 아직 설치되어 있지만 더 이상 필요하지 않습니다. 무해하지만 저장과 로그가 중복됩니다. 자세한 내용은 {0}을(를) 누르세요.", "PEAK Checkpoint Save jest nadal zainstalowany i już niepotrzebny. Spodziewaj się nieszkodliwych zduplikowanych zapisów i logów. Naciśnij {0}, aby zobaczyć szczegóły.", "PEAK Checkpoint Save hâlâ yüklü ve artık gerekli değil. Zararsız, yinelenen kayıtlar ve loglar görebilirsiniz. Ayrıntılar için {0} tuşuna basın." }, [MsgKey.GameUpdatedSavesMayBeWrong] = new string[15] { "Game updated to {0}: your existing save(s) may now LOAD THE WRONG ISLAND as the map pool was likely rotated by the update.", "Le jeu a été mis à jour vers la version {0} : vos sauvegardes existantes risquent maintenant de CHARGER LA MAUVAISE ÎLE, la rotation des cartes ayant probablement changé avec la mise à jour.", "Il gioco è stato aggiornato alla versione {0}: i tuoi salvataggi esistenti potrebbero ora CARICARE L'ISOLA SBAGLIATA, poiché il pool di mappe è stato probabilmente modificato dall'aggiornamento.", "Spiel wurde auf {0} aktualisiert: Deine vorhandenen Speicherstände laden jetzt möglicherweise DIE FALSCHE INSEL, da der Karten-Pool durch das Update wahrscheinlich rotiert wurde.", "El juego se actualizó a la versión {0}: tus partidas guardadas existentes ahora pueden CARGAR LA ISLA INCORRECTA, ya que es probable que el conjunto de mapas haya cambiado con la actualización.", "El juego se actualizó a la versión {0}: tus partidas guardadas existentes ahora pueden CARGAR LA ISLA INCORRECTA, ya que es probable que el conjunto de mapas haya cambiado con la actualización.", "O jogo foi atualizado para {0}: seus saves existentes agora podem CARREGAR A ILHA ERRADA, já que a pool de mapas provavelmente foi alterada pela atualização.", "Игра обновлена до версии {0}: ваши существующие сохранения теперь могут ЗАГРУЖАТЬ НЕПРАВИЛЬНЫЙ ОСТРОВ, так как пул карт, вероятно, был изменён обновлением.", "Гру оновлено до версії {0}: ваші наявні збереження тепер можуть ЗАВАНТАЖУВАТИ НЕПРАВИЛЬНИЙ ОСТРІВ, оскільки пул карт, ймовірно, було змінено оновленням.", "游戏已更新至 {0}:你现有的存档现在可能会加载错误的岛屿,因为地图池很可能已随更新轮换。", "遊戲已更新至 {0}:你現有的存檔現在可能會載入錯誤的島嶼,因為地圖池很可能已隨更新輪換。", "ゲームが{0}に更新されました:更新によってマッププールが変更された可能性があるため、既存のセーブデータが間違った島を読み込むことがあります。", "게임이 {0} 버전으로 업데이트되었습니다: 업데이트로 맵 풀이 변경되었을 가능성이 있어 기존 저장 파일이 잘못된 섬을 불러올 수 있습니다.", "Gra została zaktualizowana do wersji {0}: Twoje istniejące zapisy mogą teraz WCZYTAĆ NIEWŁAŚCIWĄ WYSPĘ, ponieważ pula map najprawdopodobniej została zmieniona przez aktualizację.", "Oyun {0} sürümüne güncellendi: mevcut kayıtlarınız artık YANLIŞ ADAYI yükleyebilir, çünkü harita havuzu güncelleme ile muhtemelen değişti." }, [MsgKey.GameUpdateDialogSuppressed] = new string[15] { "PEAK has an update available. DO NOT close or update the game until everyone is done with this save. Updating now may make this save load the wrong island or break it entirely.", "Une mise à jour de PEAK est disponible. NE fermez PAS et NE mettez PAS à jour le jeu tant que tout le monde n'a pas terminé avec cette sauvegarde. Mettre à jour maintenant pourrait faire charger la mauvaise île à cette sauvegarde, voire la corrompre complètement.", "È disponibile un aggiornamento per PEAK. NON chiudere né aggiornare il gioco finché tutti non hanno finito con questo salvataggio. Aggiornare ora potrebbe far caricare a questo salvataggio l'isola sbagliata o comprometterlo del tutto.", "Für PEAK ist ein Update verfügbar. Schließe das Spiel NICHT und aktualisiere es NICHT, bis alle mit diesem Speicherstand fertig sind. Ein Update jetzt kann dazu führen, dass dieser Speicherstand die falsche Insel lädt oder komplett unbrauchbar wird.", "Hay una actualización disponible para PEAK. NO cierres ni actualices el juego hasta que todos hayan terminado con esta partida guardada. Actualizar ahora puede hacer que esta partida cargue la isla incorrecta o que se rompa por completo.", "Hay una actualización disponible para PEAK. NO cierres ni actualices el juego hasta que todos hayan terminado con esta partida guardada. Actualizar ahora puede hacer que esta partida cargue la isla incorrecta o que se rompa por completo.", "Uma atualização do PEAK está disponível. NÃO feche nem atualize o jogo até que todos tenham terminado com este save. Atualizar agora pode fazer este save carregar a ilha errada ou quebrá-lo completamente.", "Доступно обновление PEAK. НЕ закрывайте и НЕ обновляйте игру, пока все не закончат с этим сохранением. Обновление сейчас может привести к тому, что это сохранение загрузит неправильный остров или будет полностью повреждено.", "Доступне оновлення PEAK. НЕ закривайте і НЕ оновлюйте гру, поки всі не завершать роботу з цим збереженням. Оновлення зараз може призвести до того, що це збереження завантажить неправильний острів або буде повністю пошкоджене.", "PEAK 有可用更新。在所有人使用完这个存档之前,请不要关闭或更新游戏。现在更新可能会导致此存档加载错误的岛屿,甚至彻底损坏。", "PEAK 有可用更新。在所有人使用完這個存檔之前,請不要關閉或更新遊戲。現在更新可能會導致此存檔載入錯誤的島嶼,甚至完全損壞。", "PEAKのアップデートが利用可能です。このセーブデータを全員が使い終わるまで、ゲームを閉じたりアップデートしたりしないでください。今アップデートすると、このセーブデータが間違った島を読み込んだり、完全に壊れてしまう可能性があります。", "PEAK 업데이트가 있습니다. 모두가 이 저장 파일 사용을 마칠 때까지 게임을 종료하거나 업데이트하지 마세요. 지금 업데이트하면 이 저장 파일이 잘못된 섬을 불러오거나 완전히 손상될 수 있습니다.", "Dostępna jest aktualizacja PEAK. NIE zamykaj ani NIE aktualizuj gry, dopóki wszyscy nie skończą korzystać z tego zapisu. Aktualizacja teraz może sprawić, że ten zapis wczyta niewłaściwą wyspę albo zostanie całkowicie uszkodzony.", "PEAK için bir güncelleme mevcut. Bu kayıtla herkes işini bitirene kadar oyunu KAPATMAYIN veya GÜNCELLEMEYİN. Şimdi güncellemek, bu kaydın yanlış adayı yüklemesine veya tamamen bozulmasına neden olabilir." } }; public static string Get(MsgKey key) { return LocalizationHelper.Resolve(_table[key]); } public static string Get(MsgKey key, object arg0) { return string.Format(LocalizationHelper.Resolve(_table[key]), arg0); } } internal static class NadirCommuner { private const float PendingWindowSeconds = 5f; private static float _pendingUntil = -1f; private static Vector3 _pendingAnchor; public static string PendingUserId { get; private set; } public static string PendingName { get; private set; } private static bool Pending => _pendingUntil > Time.time; public static void ArmPending(Character interactor, ManualLogSource log) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) ClearPending(); try { if ((Object)(object)interactor == (Object)null) { if (log != null) { log.LogWarning((object)"NadirCommuner: the commune RPC carried no usable character, so this save falls back to anchoring on the host and the ghost will orbit the host on load."); } return; } if ((Object)(object)interactor.data != (Object)null && interactor.data.dead) { if (log != null) { log.LogWarning((object)("NadirCommuner: " + interactor.characterName + " reads as dead at the moment they communed, so their position is the off-map death zone - anchoring this save the normal way instead.")); } return; } _pendingAnchor = interactor.Head; PendingName = interactor.characterName; PendingUserId = ReadUserId(interactor); _pendingUntil = Time.time + 5f; if (log != null) { log.LogInfo((object)("NadirCommuner: " + PendingName + " (" + ((PendingUserId.Length > 0) ? PendingUserId : "offline") + ") " + $"communed at {_pendingAnchor} - this save is anchored on them, not on the host.")); } } catch (Exception ex) { ClearPending(); if (log != null) { log.LogWarning((object)("NadirCommuner.ArmPending failed (non-fatal, the save falls back to the host's own position): " + ex.Message)); } } } public static void ClearPending() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) _pendingUntil = -1f; _pendingAnchor = Vector3.zero; PendingUserId = null; PendingName = null; } public static bool TryGetPendingAnchor(out Vector3 anchor) { //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) anchor = _pendingAnchor; return Pending; } public static Character ResolveSavedCommuner(OwnSaveData data, ManualLogSource log) { Character localCharacter = Character.localCharacter; string text = data?.nadirCommunerUserId; string text2 = data?.nadirCommunerName; if (PhotonNetwork.OfflineMode) { return localCharacter; } if (string.IsNullOrEmpty(text)) { if (log != null) { log.LogInfo((object)"NadirCommuner: this Nadir checkpoint predates recording who communed, so the host stands in for the interactor (the scoutmaster's ghost will orbit the host)."); } return localCharacter; } try { Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { Character val2 = (((Object)(object)val != (Object)null) ? val.character : null); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null) && !(ReadUserId(val2) != text)) { if (log != null) { log.LogInfo((object)("NadirCommuner: replaying the commune as " + val2.characterName + " (" + text + "), the player who originally communed.")); } return val2; } } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("NadirCommuner.ResolveSavedCommuner failed (non-fatal, falling back to the host): " + ex.Message)); } return localCharacter; } if (log != null) { log.LogWarning((object)("NadirCommuner: the player who communed at this checkpoint (" + (string.IsNullOrEmpty(text2) ? text : (text2 + ", " + text)) + ") isn't in this session, so the host stands in for them and the scoutmaster's ghost orbits the host instead. Nothing else about the restore changes.")); } return localCharacter; } private static string ReadUserId(Character character) { try { if (PhotonNetwork.OfflineMode || (Object)(object)character == (Object)null || (Object)(object)character.player == (Object)null) { return ""; } return NetworkingUtilities.GetUserId(character.player) ?? ""; } catch { return ""; } } } internal static class NadirRisingField { public static LavaRising Find(ManualLogSource log) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 try { if (LavaRising.ALL_LAVA == null) { return null; } foreach (LavaRising item in LavaRising.ALL_LAVA) { if ((Object)(object)item != (Object)null && (int)item.risingFieldType == 2) { return item; } } return null; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("NadirRisingField.Find failed (non-fatal): " + ex.Message)); } return null; } } public static void Describe(LavaRising field, ManualLogSource log) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)field == (Object)null || log == null) { return; } try { log.LogInfo((object)($"NadirRisingField: found Nadir's rising field - requiredSegment={field.requiredSegment}, " + $"waitForEvent={field.waitForEvent}, initialWaitTime={field.initialWaitTime:F2}s, " + $"travelTime={field.travelTime:F1}s, fogEnabled={Ascents.fogEnabled}.")); } catch (Exception ex) { log.LogWarning((object)("NadirRisingField.Describe failed (non-fatal): " + ex.Message)); } } public static void Park(LavaRising field) { if (!((Object)(object)field == (Object)null)) { field.started = false; field.ended = false; field.timeTraveled = 0f; field.secondsWaitedToStart = 0f; } } public static void BroadcastParked(LavaRising field, ManualLogSource log) { if ((Object)(object)field == (Object)null || PhotonNetwork.OfflineMode) { return; } try { PhotonView photonView = ((MonoBehaviourPun)field).photonView; if ((Object)(object)photonView != (Object)null) { photonView.RPC("RPC_SyncLava", (RpcTarget)1, new object[4] { false, false, 0f, 0f }); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("NadirRisingField.BroadcastParked failed (non-fatal, clients will pick the parked state up on the host's next 15s sync instead): " + ex.Message)); } } } public static bool IsArmed(LavaRising field) { if ((Object)(object)field != (Object)null) { if (!field.started) { return field.secondsWaitedToStart > 0f; } return true; } return false; } public static void Release(LavaRising field, ManualLogSource log) { if ((Object)(object)field == (Object)null) { return; } try { field.StartWaiting(); } catch (Exception arg) { if (log != null) { log.LogError((object)("NadirRisingField.Release failed - Nadir's rising field may stay down for the " + $"rest of this run: {arg}")); } } } } internal static class NadirSearchRadius { public const float Radius = 80f; public static float ForCurrentSegment(float defaultRadius) { return Widen(defaultRadius, CurrentlyInNadir()); } public static bool CurrentlyInNadir() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 try { MapHandler instance = Singleton.Instance; return (Object)(object)instance != (Object)null && (int)instance.GetCurrentSegment() == 6; } catch { return false; } } public static float ForSavedSegment(float defaultRadius, OwnSaveData data) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 return Widen(defaultRadius, data != null && (int)data.segment == 6); } private static float Widen(float defaultRadius, bool isNadir) { if (!isNadir) { return defaultRadius; } return Mathf.Max(defaultRadius, 80f); } } internal static class OrchestrationLock { private static string _owner; private static MonoBehaviour _coroutineHost; private static float _cooldownUntil = -1f; private static Coroutine _queuedCoroutine; private static Action _queuedAction; private static string _queuedDescription; public static bool IsBusy => _owner != null; public static float RemainingCooldown { get { if (!(_cooldownUntil < 0f)) { return Mathf.Max(0f, _cooldownUntil - Time.unscaledTime); } return 0f; } } public static bool TryAcquire(string owner) { if (_owner != null) { return false; } _owner = owner; return true; } public static void Release(string owner) { if (_owner == owner) { _owner = null; } } public static void Init(MonoBehaviour coroutineHost) { _coroutineHost = coroutineHost; } public static void ArmCooldown(float seconds) { _cooldownUntil = ((seconds > 0f) ? (Time.unscaledTime + seconds) : (-1f)); } public static void RunOrQueue(string description, Action action, ManualLogSource log) { if (!IsBusy && RemainingCooldown <= 0f) { action(); return; } _queuedAction = action; _queuedDescription = description; log.Trace($"[cooldown] '{description}' queued (busy={IsBusy}, {RemainingCooldown:F1}s cooldown " + "remaining); will auto-run once clear (replaces any previously queued request)."); if (_queuedCoroutine == null && (Object)(object)_coroutineHost != (Object)null) { _queuedCoroutine = _coroutineHost.StartCoroutine(RunQueuedWhenClear(log)); } } private static IEnumerator RunQueuedWhenClear(ManualLogSource log) { while (IsBusy || RemainingCooldown > 0f) { yield return null; } _queuedCoroutine = null; Action queuedAction = _queuedAction; string queuedDescription = _queuedDescription; _queuedAction = null; _queuedDescription = null; if (queuedAction != null) { log.Trace("[cooldown] running queued '" + queuedDescription + "'."); queuedAction(); } } } public static class OwnEnvironmentReset { public static IEnumerator ResetFogAfterLoad(int index, Segment segment, ManualLogSource log, bool extendedTime = false) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) OrbFogHandler fog = Singleton.Instance; if ((Object)(object)fog == (Object)null) { yield break; } if (Ascents.currentAscent > -1) { fog.SetFogOrigin(index); yield return (object)new WaitForSeconds(extendedTime ? 8f : 1.5f); fog.isMoving = false; fog.currentWaitTime = 0f; } else { GameObject val = GameObject.Find("FogSphereSystem"); if ((Object)(object)val != (Object)null && val.activeSelf) { val.SetActive(false); } } if ((int)segment == 3 || (int)segment == 4) { fog.currentSize = 10000f; fog.speed = 0f; if (!extendedTime) { yield return (object)new WaitForSeconds(0.5f); } } } public static void ResetLavaAfterLoad(ManualLogSource log) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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) LavaRising val = Object.FindFirstObjectByType(); if ((Object)(object)val == (Object)null) { if (log != null) { log.LogWarning((object)"OwnEnvironmentReset: LavaRising not found."); } return; } val.started = false; val.ended = false; val.secondsWaitedToStart = 0f; val.timeTraveled = 0f; if ((Object)(object)val.lava != (Object)null) { Vector3 position = val.lava.position; position.y = 847.8f; val.lava.position = position; } typeof(LavaRising).GetMethod("EndRising", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(val, null); typeof(LavaRising).GetMethod("Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(val, null); val.ended = false; typeof(LavaRising).GetField("shownLavaRisingMessage", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(val, false); log.Trace("OwnEnvironmentReset: lava fully reset."); } public static IEnumerator SpawnFlaresAtPeak() { Vector3 basePos = new Vector3(19f, 1228.1f, 2240f); for (int i = 0; i < 10; i++) { Vector3 val = Random.insideUnitSphere * 0.1f; val.y = 0f; GameObject val2 = PhotonNetwork.InstantiateItem("flare", basePos + val, Quaternion.identity); if ((Object)(object)val2 != (Object)null) { Rigidbody component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { component.isKinematic = true; component.useGravity = true; } } yield return (object)new WaitForSeconds(0.1f); } } } public static class OwnFallDamageProtection { private static float _until; public static void Apply(Harmony harmony, ManualLogSource log) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown try { harmony.Patch((MethodBase)AccessTools.Method(typeof(CharacterMovement), "CheckFallDamage", (Type[])null, (Type[])null), new HarmonyMethod(typeof(OwnFallDamageProtection), "PrefixSkipIfProtected", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Lava), "HitPlayer", (Type[])null, (Type[])null), new HarmonyMethod(typeof(OwnFallDamageProtection), "PrefixSkipIfProtected", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Lava), "Heat", (Type[])null, (Type[])null), new HarmonyMethod(typeof(OwnFallDamageProtection), "PrefixSkipIfProtected", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"OwnFallDamageProtection: patched CharacterMovement.CheckFallDamage / Lava.HitPlayer / Lava.Heat."); } catch (Exception arg) { log.LogError((object)$"OwnFallDamageProtection.Apply failed (non-fatal): {arg}"); } } public static void Activate(float seconds) { _until = Time.time + seconds; } private static bool PrefixSkipIfProtected() { return !(Time.time < _until); } } public static class OwnInventoryRestore { private static MethodInfo _playerAddItemMethod; public static IEnumerator RestoreAll(SaveSelection selection, PluginConfig cfg, OwnLoadEntryPoints entryPoints, ManualLogSource log, HashSet restoringAsDead = null) { for (int i = 0; i < 60; i++) { yield return null; } bool offline = selection.Offline; Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); InventorySyncData val3 = default(InventorySyncData); foreach (Player val in array) { Character ch = val.character; if ((Object)(object)ch == (Object)null) { continue; } string userId = (offline ? "" : NetworkingUtilities.GetUserId(ch.player)); PhotonView playerView = ((Component)val).GetComponent(); if (restoringAsDead != null && restoringAsDead.Contains(userId)) { log.Trace("OwnInventoryRestore: skipping the per-player restore for '" + userId + "' - they were dead when this checkpoint was saved and are being restored as dead."); continue; } OwnSaveData data = null; if (selection.TryGetPlayerFile(userId, out var path)) { try { data = JsonConvert.DeserializeObject(File.ReadAllText(path)); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: could not read the save for '" + userId + "': " + ex.Message)); } } } else { log.Trace("OwnInventoryRestore: skipping restore for '" + userId + "' - they have no save file in this checkpoint's save event; leaving their current state untouched."); } ThornsAndTicksRestore.ClearThornsSilently(ch, log); if (cfg.RestoreInventory.Value && data != null) { if (ch.player.itemSlots != null) { ItemSlot[] itemSlots = ch.player.itemSlots; foreach (ItemSlot val2 in itemSlots) { if (val2 != null) { try { val2.EmptyOut(); } catch { } } } } if (BackpackTypeCompat.HasAny(ch.player.backpackSlot)) { try { ((ItemSlot)ch.player.backpackSlot).EmptyOut(); } catch { } } for (int k = 0; k < 30; k++) { yield return null; } LoadPlayerInventory(data, ch.player, ch, playerView, cfg, log); if ((Object)(object)playerView != (Object)null && playerView.Owner != null && data.backpackItemStates.Count > 0) { LoadBackpackFromSave(ch.player, data, cfg, log); } } if (cfg.RestorePlayerTempSlot.Value && data != null) { try { ItemSlot tempFullSlot = ch.player.tempFullSlot; if (tempFullSlot != null) { tempFullSlot.EmptyOut(); } } catch { } bool flag = offline || ((Object)(object)playerView != (Object)null && playerView.IsMine) || entryPoints?.Network?.PlayerReportedMod(userId) == true; if (data.heldItemState != null && flag) { try { LoadHeldItem(data.heldItemState, ch.player, cfg, log); } catch (Exception ex2) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: held-item restore failed: " + ex2.Message)); } } } else if (data.heldItemState != null) { log.Trace("OwnInventoryRestore: skipping held-item restore for '" + userId + "' - not confirmed to be running Quick Resume, would leave the slot reserved but empty."); } } if (cfg.RestoreAfflictions.Value && data != null && offline) { try { try { ch.data.SetSkeleton(data.isSkeleton); } catch { } try { ch.SetExtraStamina((data.extraStamina > 0f && data.extraStamina <= 1f) ? data.extraStamina : 0f); } catch { } CharacterAfflictions afflictions = ch.refs.afflictions; AfflictionArrayCompat.CopyOverlap(data.afflictions_current, afflictions.currentStatuses); if (cfg.RestorePetrify.Value) { try { ch.data.SetPetrify(data.petrifyAmount); } catch { } } } catch { } } else if (cfg.RestoreAfflictions.Value && data != null && !offline && PhotonNetwork.IsMasterClient && data.afflictions_current != null) { try { ch.data.SetSkeleton(data.isSkeleton); } catch { } try { bool valueOrDefault = entryPoints?.Network?.ApplyStatusesViaVanilla(ch, data.afflictions_current) == true; int petrifyAmount = (cfg.RestorePetrify.Value ? data.petrifyAmount : 0); if ((Object)(object)playerView != (Object)null) { entryPoints?.Network?.ApplyAfflictionsTo(playerView, userId, data.afflictions_current, data.extraStamina, petrifyAmount); } else if (!valueOrDefault) { if (log != null) { log.LogWarning((object)"OwnInventoryRestore: Player has no PhotonView, cannot send afflictions RPC."); } } } catch (Exception ex3) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: failed to send afflictions RPC: " + ex3.Message)); } } } if (cfg.RestorePlayerEntities.Value && data != null && data.stuckThornIndices != null && data.stuckThornIndices.Count > 0) { try { if (offline || ((Object)(object)playerView != (Object)null && playerView.IsMine)) { ThornsAndTicksRestore.ApplyThorns(ch, data.stuckThornIndices, log); } else if (PhotonNetwork.IsMasterClient && (Object)(object)playerView != (Object)null) { entryPoints?.Network?.RestoreThornsFor(playerView, userId, ((IEnumerable)data.stuckThornIndices).Select((Func)((ushort result) => result)).ToArray()); } } catch (Exception ex4) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: thorn restore failed: " + ex4.Message)); } } } if (cfg.RestorePlayerEntities.Value && data != null) { try { ThornsAndTicksRestore.RemoveExistingTick(ch, log); } catch (Exception ex5) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: tick cleanup failed: " + ex5.Message)); } } if (data.hasTick) { ThornsAndTicksRestore.ApplyTick(ch, log); } } for (int k = 0; k < 20; k++) { yield return null; } if (!offline) { try { if ((Object)(object)playerView != (Object)null) { ((InventorySyncData)(ref val3))..ctor(ch.player.itemSlots, ch.player.backpackSlot, ch.player.tempFullSlot); playerView.RPC("SyncInventoryRPC", (RpcTarget)1, new object[2] { IBinarySerializable.ToManagedArray(val3), true }); } } catch (Exception ex6) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: SendSyncInventory failed: " + ex6.Message)); } } } for (int k = 0; k < 20; k++) { yield return null; } if (!cfg.RestorePlayerTempSlot.Value || data == null || data.heldItemState == null || ch.player?.tempFullSlot == null || ch.player.tempFullSlot.IsEmpty()) { continue; } try { if (offline || ((Object)(object)playerView != (Object)null && playerView.IsMine)) { ch.refs.items.EquipSlot(Optionable.Some((byte)250)); } else if (PhotonNetwork.IsMasterClient && (Object)(object)playerView != (Object)null) { entryPoints?.Network?.EquipHeldItemFor(playerView, userId); } } catch (Exception ex7) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: held-item equip failed: " + ex7.Message)); } } } RestoreTimePlayed(selection, log); for (int i = 0; i < 30; i++) { yield return null; } entryPoints?.MarkNotCurrentlyLoading(); entryPoints?.ArmRecentlyLoadedCooldown(10f); entryPoints?.ArmRecentlyLitCampfireCooldown(32f); TeleportWatchdog teleportWatchdog = entryPoints?.Network?.Watchdog; teleportWatchdog?.ArmPendingWatch(); entryPoints?.Network?.LoadingScreenOthers(enable: false, teleportWatchdog?.KnownTarget); if (log != null) { log.LogInfo((object)"OwnInventoryRestore: restore sequence complete."); } } private static void RestoreTimePlayed(SaveSelection selection, ManualLogSource log) { try { if (string.IsNullOrEmpty(selection.HostFilePath) || !File.Exists(selection.HostFilePath)) { return; } OwnSaveData ownSaveData = JsonConvert.DeserializeObject(File.ReadAllText(selection.HostFilePath)); if (ownSaveData != null && !(ownSaveData.timePlayed <= 0f)) { RunManager val = Object.FindFirstObjectByType(); if (!((Object)(object)val == (Object)null)) { RunTimerCompat.TryWrite(val, ownSaveData.timePlayed, log); } } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("OwnInventoryRestore: time-played sync failed (non-fatal): " + ex.Message)); } } } public static void LoadPlayerInventory(OwnSaveData data, Player player, Character ch, PhotonView playerView, PluginConfig cfg, ManualLogSource log) { //IL_0149: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { if (log != null) { log.LogWarning((object)"OwnInventoryRestore.LoadPlayerInventory: no player."); } return; } if ((Object)(object)ch == (Object)null || (Object)(object)((MonoBehaviourPun)ch).photonView == (Object)null) { if (log != null) { log.LogWarning((object)"OwnInventoryRestore.LoadPlayerInventory: missing Character or photonView."); } return; } int num = BackpackTypeCompat.FromSave(data.hasBackpack, data.backpackType); try { if (BackpackTypeCompat.TryResolveItemId(num, out var itemId, log)) { AddItemToInventory(player, itemId, log); RestoreBackpackOwnValues(data, player, cfg, log); } } catch (Exception arg) { if (log != null) { log.LogWarning((object)$"OwnInventoryRestore.LoadPlayerInventory: backpack restore failed for type {num}: {arg}"); } } if (data.inventoryItemStates == null || data.inventoryItemStates.Count <= 0) { return; } foreach (OwnSavedItemState inventoryItemState in data.inventoryItemStates) { if (inventoryItemState == null || !AddItemToInventory_GetSlot(player, inventoryItemState.itemId, out var createdSlot, log) || createdSlot == null) { continue; } ItemInstanceData data2 = createdSlot.data; if (data2 == null || !cfg.RestoreItemStats.Value) { continue; } foreach (KeyValuePair value2 in inventoryItemState.values) { if (OwnItemStateIO.TryGetKey(value2.Key, out var key)) { OwnSavedEntry value = value2.Value; if (value != null && !OwnItemStateIO.TrySetOrCreateEntry(data2, key, value.type, value.value, log) && log != null) { log.LogWarning((object)$"OwnInventoryRestore: could not apply '{value2.Key}' for item {inventoryItemState.itemId}."); } } } } } private static void RestoreBackpackOwnValues(OwnSaveData data, Player player, PluginConfig cfg, ManualLogSource log) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (data.backpackOwnValues == null || data.backpackOwnValues.Count == 0 || cfg == null || !cfg.RestoreItemStats.Value) { return; } object obj; if (player == null) { obj = null; } else { BackpackSlot backpackSlot = player.backpackSlot; obj = ((backpackSlot != null) ? ((ItemSlot)backpackSlot).data : null); } ItemInstanceData val = (ItemInstanceData)obj; if (val == null) { if (log != null) { log.LogWarning((object)"OwnInventoryRestore: the restored backpack has no instance data; its own stats (e.g. fuel) were not applied."); } return; } foreach (KeyValuePair backpackOwnValue in data.backpackOwnValues) { if (OwnItemStateIO.TryGetKey(backpackOwnValue.Key, out var key)) { OwnSavedEntry value = backpackOwnValue.Value; if (value != null && !OwnItemStateIO.TrySetOrCreateEntry(val, key, value.type, value.value, log) && log != null) { log.LogWarning((object)("OwnInventoryRestore: could not apply worn-backpack stat '" + backpackOwnValue.Key + "'.")); } } } } private static void LoadHeldItem(OwnSavedItemState itemState, Player player, PluginConfig cfg, ManualLogSource log) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if (player?.tempFullSlot == null) { return; } Item val = default(Item); if (!ItemDatabase.TryGetItem(itemState.itemId, ref val) || (Object)(object)val == (Object)null) { if (log != null) { log.LogWarning((object)$"OwnInventoryRestore: held item {itemState.itemId} not found in ItemDatabase, skipping."); } return; } ItemInstanceData val2 = new ItemInstanceData(Guid.NewGuid()); ItemInstanceDataHandler.AddInstanceData(val2); player.tempFullSlot.SetItem(val, val2); if (!cfg.RestoreItemStats.Value) { return; } foreach (KeyValuePair value2 in itemState.values) { if (OwnItemStateIO.TryGetKey(value2.Key, out var key)) { OwnSavedEntry value = value2.Value; if (value != null && !OwnItemStateIO.TrySetOrCreateEntry(val2, key, value.type, value.value, log) && log != null) { log.LogWarning((object)$"OwnInventoryRestore: could not apply held-item '{value2.Key}' for item {itemState.itemId}."); } } } } public static void LoadBackpackFromSave(Player player, OwnSaveData data, PluginConfig cfg, ManualLogSource log) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || data == null || data.backpackItemStates == null || data.backpackItemStates.Count == 0) { return; } BackpackData backpackData = GetBackpackData(player); if (backpackData == null || backpackData.itemSlots == null) { return; } Item val = default(Item); foreach (OwnSavedBackpackItemState backpackItemState in data.backpackItemStates) { if (backpackItemState == null || backpackItemState.slotIndex >= backpackData.itemSlots.Length || !ItemDatabase.TryGetItem(backpackItemState.itemId, ref val) || (Object)(object)val == (Object)null) { continue; } ItemInstanceData val2 = new ItemInstanceData(Guid.NewGuid()); ItemInstanceDataHandler.AddInstanceData(val2); backpackData.AddItem(val, val2, backpackItemState.slotIndex); ItemSlot obj = backpackData.itemSlots[backpackItemState.slotIndex]; ItemInstanceData val3 = ((obj != null) ? obj.data : null); if (val3 == null || !cfg.RestoreItemStats.Value) { continue; } foreach (KeyValuePair value2 in backpackItemState.values) { if (OwnItemStateIO.TryGetKey(value2.Key, out var key)) { OwnSavedEntry value = value2.Value; if (value != null) { OwnItemStateIO.TrySetOrCreateEntry(val3, key, value.type, value.value, log); } } } } log.Trace($"OwnInventoryRestore: backpack states loaded for {NetworkingUtilities.GetUserId(player)} (items={data.backpackItemStates.Count})."); } public static BackpackData GetBackpackData(Player p) { if ((Object)(object)p == (Object)null || !BackpackTypeCompat.HasAny(p)) { return null; } ItemSlot backpackSlot = (ItemSlot)(object)p.backpackSlot; if (backpackSlot.data == null) { return null; } BackpackData val = default(BackpackData); if (!backpackSlot.data.TryGetDataEntry((DataEntryKey)7, ref val) || val == null) { backpackSlot.data.RegisterNewEntry((DataEntryKey)7); backpackSlot.data.TryGetDataEntry((DataEntryKey)7, ref val); } return val; } public static bool AddItemToInventory(Player player, ushort itemId, ManualLogSource log) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)player == (Object)null) { return false; } try { if (!EnsurePlayerAddItemMethod(log)) { return false; } ItemInstanceData val = new ItemInstanceData(Guid.NewGuid()); object[] parameters = new object[3] { itemId, val, null }; _playerAddItemMethod.Invoke(player, parameters); return true; } catch (Exception arg) { if (log != null) { log.LogError((object)$"OwnInventoryRestore.AddItemToInventory error: {arg}"); } return false; } } public static bool AddItemToInventory_GetSlot(Player player, ushort itemId, out ItemSlot createdSlot, ManualLogSource log) { createdSlot = null; if ((Object)(object)player == (Object)null) { return false; } try { if (!EnsurePlayerAddItemMethod(log)) { return false; } object[] array = new object[3] { itemId, null, null }; _playerAddItemMethod.Invoke(player, array); object obj = array[2]; createdSlot = (ItemSlot)((obj is ItemSlot) ? obj : null); return createdSlot != null; } catch (Exception arg) { if (log != null) { log.LogError((object)$"OwnInventoryRestore.AddItemToInventory_GetSlot error: {arg}"); } return false; } } private static bool EnsurePlayerAddItemMethod(ManualLogSource log) { if (_playerAddItemMethod != null) { return true; } _playerAddItemMethod = typeof(Player).GetMethod("AddItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(ushort), typeof(ItemInstanceData), typeof(ItemSlot).MakeByRefType() }, null); if (_playerAddItemMethod == null) { if (log != null) { log.LogError((object)"OwnInventoryRestore: Player.AddItem(...) method not found."); } return false; } return true; } } public readonly struct OwnItemStateEntry { public readonly string TypeName; public readonly float Value; public OwnItemStateEntry(string typeName, float value) { TypeName = typeName; Value = value; } } public static class OwnItemStateIO { public static readonly string[] ItemStateKeyNames = new string[13] { "ItemUses", "PetterItemUses", "UseRemainingPercentage", "CookedAmount", "Fuel", "Color", "Scale", "value__", "Used", "SpawnedBees", "ScreamTime", "FlareActive", "InstanceID" }; private static readonly FieldInfo IidDataField = typeof(ItemInstanceData).GetField("data", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); public static Dictionary ReadItemStateValues(ItemInstanceData data) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); if (data == null) { return dictionary; } string[] itemStateKeyNames = ItemStateKeyNames; foreach (string text in itemStateKeyNames) { if (TryGetKey(text, out var key) && TryGetEntryObject(data, key, out var entryObj) && TryReadEntryNumeric(entryObj, out var value)) { dictionary[text] = new OwnItemStateEntry(entryObj.GetType().AssemblyQualifiedName, value); } } return dictionary; } public static bool TryGetEntryObject(ItemInstanceData inst, DataEntryKey key, out object entryObj) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) entryObj = null; if (inst == null) { return false; } if (!(IidDataField?.GetValue(inst) is IDictionary dictionary)) { return false; } if (!dictionary.Contains(key)) { return false; } entryObj = dictionary[key]; return entryObj != null; } public static bool TryReadEntryNumeric(object entryObj, out float value) { value = 0f; if (entryObj == null) { return false; } Type type = entryObj.GetType(); PropertyInfo property = type.GetProperty("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { object value2; try { value2 = property.GetValue(entryObj); } catch { return false; } return TryConvertToFloat(value2, out value); } FieldInfo field = type.GetField("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object value3; try { value3 = field.GetValue(entryObj); } catch { return false; } return TryConvertToFloat(value3, out value); } return false; } public static bool TrySetOrCreateEntry(ItemInstanceData inst, DataEntryKey key, string entryTypeName, float value, ManualLogSource log = null) { //IL_0023: 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_0082: 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) if (inst == null) { return false; } if (!(IidDataField?.GetValue(inst) is IDictionary dictionary)) { return false; } object obj = (dictionary.Contains(key) ? dictionary[key] : null); if (obj == null) { Type type = ((!string.IsNullOrEmpty(entryTypeName)) ? Type.GetType(entryTypeName, throwOnError: false) : null); if (type == null) { if (log != null) { log.LogWarning((object)$"OwnItemStateIO: cannot resolve entry type '{entryTypeName}' for key {key}."); } return false; } try { object obj2 = (dictionary[key] = Activator.CreateInstance(type)); obj = obj2; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("OwnItemStateIO: failed to create entry type '" + type.FullName + "': " + ex.Message)); } return false; } } bool num = TryWriteEntryNumeric(obj, value); if (num) { MarkHasData(obj); } return num; } private static void MarkHasData(object entryObj) { if (entryObj == null) { return; } Type type = entryObj.GetType(); PropertyInfo property = type.GetProperty("HasData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite && property.PropertyType == typeof(bool)) { try { property.SetValue(entryObj, true); return; } catch { } } FieldInfo field = type.GetField("HasData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null) || !(field.FieldType == typeof(bool))) { return; } try { field.SetValue(entryObj, true); } catch { } } public static bool TryConvertToFloat(object v, out float value) { value = 0f; if (v == null) { return false; } try { if (!(v is float num)) { if (!(v is double num2)) { if (!(v is int num3)) { if (!(v is long num4)) { if (!(v is short num5)) { if (v is byte b) { value = (int)b; return true; } value = Convert.ToSingle(v); return true; } value = num5; return true; } value = num4; return true; } value = num3; return true; } value = (float)num2; return true; } value = num; return true; } catch { return false; } } public static bool TryGetKey(string name, out DataEntryKey key) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected I4, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected I4, but got Unknown try { key = (DataEntryKey)(int)(DataEntryKey)Enum.Parse(typeof(DataEntryKey), name, ignoreCase: true); return true; } catch { string[] names = Enum.GetNames(typeof(DataEntryKey)); foreach (string text in names) { if (text.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0) { key = (DataEntryKey)(int)(DataEntryKey)Enum.Parse(typeof(DataEntryKey), text); return true; } } key = (DataEntryKey)0; return false; } } public static bool TryWriteEntryNumeric(object entryObj, float value) { if (entryObj == null) { return false; } Type type = entryObj.GetType(); PropertyInfo property = type.GetProperty("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { try { property.SetValue(entryObj, Convert.ChangeType(value, property.PropertyType)); return true; } catch { } } FieldInfo field = type.GetField("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { field.SetValue(entryObj, Convert.ChangeType(value, field.FieldType)); return true; } catch { } } return false; } } public class OwnLoadEntryPoints : MonoBehaviour { private ManualLogSource _log; private PluginConfig _cfg; private OwnNetwork _network; private OwnTeleportSequence _teleportSequence; private static bool _suppressExternalTerrainRandomizer; private float _recentlyLoadedUntil = -1f; public static string SelectedLevel { get; private set; } = "null"; public bool CurrentlyLoading { get; private set; } public bool TeleportInProgress { get { if ((Object)(object)_teleportSequence != (Object)null) { return _teleportSequence.IsRunning; } return false; } } public bool RestoreComplete { get { if (!((Object)(object)_teleportSequence == (Object)null)) { return _teleportSequence.RestoreComplete; } return true; } } internal OwnNetwork Network => _network; public bool LoadedSaveFileThisRound { get; private set; } public float RecentlyLitCampfireUntil { get; private set; } = -1f; internal static void ClearSelectedLevel() { SelectedLevel = "null"; } internal static void ForceSelectedLevel(string sceneName) { SelectedLevel = (string.IsNullOrEmpty(sceneName) ? "null" : sceneName); } internal static void ArmSuppressExternalTerrainRandomizerOnce() { _suppressExternalTerrainRandomizer = true; } internal static bool ConsumeSuppressExternalTerrainRandomizerOnce() { bool suppressExternalTerrainRandomizer = _suppressExternalTerrainRandomizer; _suppressExternalTerrainRandomizer = false; return suppressExternalTerrainRandomizer; } public void Init(ManualLogSource log, PluginConfig cfg, OwnNetwork network, OwnTeleportSequence teleportSequence) { _log = log; _cfg = cfg; _network = network; _teleportSequence = teleportSequence; } internal void MarkNotCurrentlyLoading() { CurrentlyLoading = false; } internal void ArmRecentlyLoadedCooldown(float seconds) { _recentlyLoadedUntil = Time.time + seconds; } internal void ArmRecentlyLitCampfireCooldown(float seconds) { RecentlyLitCampfireUntil = Time.time + seconds; } internal void MarkLoadedThisRound() { LoadedSaveFileThisRound = true; } private void Update() { if (RunLauncher.InAirport) { LoadedSaveFileThisRound = false; _recentlyLoadedUntil = -1f; } } public bool TryPreStartSetSegment(SaveSelection selection) { try { OwnSaveData ownSaveData = ReadHostSave(selection); if (ownSaveData == null || string.IsNullOrEmpty(ownSaveData.sceneName)) { if (ownSaveData != null) { ManualLogSource log = _log; if (log != null) { log.LogError((object)"OwnLoadEntryPoints: the chosen save has no scene recorded (is it a co-op client's file rather than the host's?)."); } } SelectedLevel = "null"; return false; } SelectedLevel = ownSaveData.sceneName; return true; } catch (Exception arg) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)$"OwnLoadEntryPoints.TryPreStartSetSegment failed: {arg}"); } SelectedLevel = "null"; return false; } } private OwnSaveData ReadHostSave(SaveSelection selection) { if (selection == null || string.IsNullOrEmpty(selection.HostFilePath)) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"OwnLoadEntryPoints: no save selection to load."); } return null; } if (!File.Exists(selection.HostFilePath)) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("OwnLoadEntryPoints: save file no longer exists: " + selection.HostFilePath)); } return null; } OwnSaveData ownSaveData = JsonConvert.DeserializeObject(File.ReadAllText(selection.HostFilePath)); if (ownSaveData == null) { ManualLogSource log3 = _log; if (log3 == null) { return ownSaveData; } log3.LogError((object)("OwnLoadEntryPoints: save file failed to deserialize: " + selection.HostFilePath)); } return ownSaveData; } public bool TryLoadPlayer(SaveSelection selection) { try { bool flag = selection?.Offline ?? true; if (RunLauncher.InAirport) { ManualLogSource log = _log; if (log != null) { log.LogError((object)"OwnLoadEntryPoints: tried to load save at the Airport!"); } return false; } if (!RunLauncher.IsHost) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)"OwnLoadEntryPoints: tried to load as a non-host client!"); } return false; } if (LoadedSaveFileThisRound && OneTimeLoadEnabled()) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogError((object)"OwnLoadEntryPoints: tried to load again in Hardmode (one-time-load)!"); } return false; } if (_recentlyLoadedUntil > Time.time) { _log.Trace($"OwnLoadEntryPoints: please wait {_recentlyLoadedUntil - Time.time:F0}s before loading again."); return false; } if (!flag && (Object)(object)_network != (Object)null && !_network.CheckReadyStatusForPlayers() && !LoadedSaveFileThisRound) { _log.Trace("OwnLoadEntryPoints: please wait until everybody is ready!"); return false; } CurrentlyLoading = true; OwnSaveData ownSaveData = ReadHostSave(selection); if (ownSaveData == null) { CurrentlyLoading = false; return false; } _teleportSequence.Begin(ownSaveData, selection); return true; } catch (Exception arg) { _recentlyLoadedUntil = Time.time - 1f; ManualLogSource log4 = _log; if (log4 != null) { log4.LogError((object)$"OwnLoadEntryPoints.TryLoadPlayer failed: {arg}"); } return false; } } private bool OneTimeLoadEnabled() { return false; } } public class OwnLoadingScreen : MonoBehaviour { private const int SortingOrder = 32000; private ManualLogSource _log; private LoadingScreen _screen; private Coroutine _textCoroutine; public void Init(ManualLogSource log) { _log = log; } public IEnumerator FadeIn(float duration) { if ((Object)(object)_screen != (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"OwnLoadingScreen.FadeIn: a loading screen is already active; skipping duplicate instantiate."); } } else { try { LoadingScreenHandler instance = RetrievableResourceSingleton.Instance; LoadingScreen val = ((instance != null) ? instance.GetLoadingScreenPrefab((LoadingScreenType)0) : null); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)"OwnLoadingScreen.FadeIn: could not resolve the native Basic loading-screen prefab; skipping."); } } else { _screen = Object.Instantiate(val); ((Behaviour)_screen.canvas).enabled = true; _screen.canvas.sortingOrder = 32000; Animator component = ((Component)_screen).GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } if ((Object)(object)_screen.group != (Object)null) { _screen.group.alpha = 0f; } TakeOverLoadingText(); ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)"OwnLoadingScreen: native Basic loading screen instantiated, fading in."); } } } catch (Exception ex) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogWarning((object)("OwnLoadingScreen.FadeIn: failed to instantiate the native loading screen (non-fatal): " + ex.Message)); } _screen = null; } } yield return Crossfade(0f, 1f, duration); } public IEnumerator FadeOut(float duration) { yield return Crossfade(1f, 0f, duration); if (_textCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_textCoroutine); _textCoroutine = null; } if (!((Object)(object)_screen != (Object)null)) { yield break; } try { Object.Destroy((Object)(object)((Component)_screen).gameObject); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnLoadingScreen.FadeOut: failed to destroy the loading screen (non-fatal): " + ex.Message)); } } _screen = null; ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"OwnLoadingScreen: faded out and destroyed."); } } private void TakeOverLoadingText() { try { LoadingScreenAnimationSimple component = ((Component)_screen).GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.loading == (Object)null)) { ((MonoBehaviour)component).StopAllCoroutines(); ((Behaviour)component).enabled = false; float yieldTime = ((component.yieldTime > 0f) ? component.yieldTime : 1f); _textCoroutine = ((MonoBehaviour)this).StartCoroutine(DriveLoadingText(component.loading, yieldTime)); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnLoadingScreen.TakeOverLoadingText failed (non-fatal, native \"LOADING\" text stays): " + ex.Message)); } } } private IEnumerator DriveLoadingText(TMP_Text text, float yieldTime) { string text2 = MessagesLocalization.Get(MsgKey.LoadingSaveScreen); string[] frames = new string[4] { text2, text2 + ".", text2 + "..", text2 + "..." }; int i = 0; while (true) { text.text = frames[i % frames.Length]; i++; yield return (object)new WaitForSeconds(yieldTime); } } private IEnumerator Crossfade(float from, float to, float duration) { if ((Object)(object)_screen == (Object)null || (Object)(object)_screen.group == (Object)null) { yield break; } CanvasGroup group = _screen.group; duration = Mathf.Max(0f, duration); if (duration <= 0f) { group.alpha = to; yield break; } float elapsed = 0f; while (elapsed < duration) { elapsed += Time.unscaledDeltaTime; group.alpha = Mathf.Lerp(from, to, Mathf.Clamp01(elapsed / duration)); yield return null; } group.alpha = to; } } public class OwnMessageOverlay : MonoBehaviour { private ManualLogSource _log; private TMP_FontAsset _font; private GameObject _root; private TextMeshProUGUI _text; private CanvasGroup _group; private float _hideAt = -1f; private float _fadeElapsed; private const float FadeDuration = 0.25f; private bool _fadingOut; public void Init(ManualLogSource log) { _log = log; } public void Show(string text, Color color, float duration = 4f) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) try { EnsureUi(); if (!((Object)(object)_text == (Object)null)) { ((TMP_Text)_text).text = text; ((Graphic)_text).color = color; ((TMP_Text)_text).outlineColor = Color32.op_Implicit(Darken(color)); _hideAt = Time.unscaledTime + Mathf.Max(0.1f, duration); _fadingOut = false; _fadeElapsed = 0.25f; if ((Object)(object)_group != (Object)null) { _group.alpha = 1f; } _root.SetActive(true); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnMessageOverlay.Show failed (non-fatal): " + ex.Message)); } } } private void Update() { if ((Object)(object)_root == (Object)null || !_root.activeSelf || _hideAt < 0f) { return; } if (Time.unscaledTime >= _hideAt) { _fadingOut = true; _fadeElapsed -= Time.unscaledDeltaTime; if ((Object)(object)_group != (Object)null) { _group.alpha = Mathf.Clamp01(_fadeElapsed / 0.25f); } if (_fadeElapsed <= 0f) { _root.SetActive(false); _hideAt = -1f; _fadingOut = false; } } else if (!_fadingOut && _fadeElapsed < 0.25f) { _fadeElapsed += Time.unscaledDeltaTime; if ((Object)(object)_group != (Object)null) { _group.alpha = Mathf.Clamp01(_fadeElapsed / 0.25f); } } } private void EnsureUi() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { _font = SavePicker.FindGameFont(); _root = new GameObject("PEAKQuickResume_MessageOverlay", new Type[1] { typeof(RectTransform) }); Object.DontDestroyOnLoad((Object)(object)_root); Canvas obj = _root.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 31000; CanvasScaler obj2 = _root.AddComponent(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); obj2.matchWidthOrHeight = 0.5f; _group = _root.AddComponent(); _group.alpha = 0f; _group.blocksRaycasts = false; _group.interactable = false; GameObject val = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_root.transform, false); _text = val.AddComponent(); if ((Object)(object)_font != (Object)null) { ((TMP_Text)_text).font = _font; } ((TMP_Text)_text).fontSize = 40f; ((TMP_Text)_text).alignment = (TextAlignmentOptions)258; ((TMP_Text)_text).textWrappingMode = (TextWrappingModes)1; ((Graphic)_text).color = Color.white; ((TMP_Text)_text).fontMaterial.EnableKeyword("OUTLINE_ON"); ((TMP_Text)_text).outlineWidth = 0.06f; ((TMP_Text)_text).outlineColor = Color32.op_Implicit(Darken(((Graphic)_text).color)); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(0.1f, 1f); val2.anchorMax = new Vector2(0.9f, 1f); val2.pivot = new Vector2(0.5f, 1f); val2.anchoredPosition = new Vector2(0f, -60f); val2.sizeDelta = new Vector2(0f, 200f); _root.SetActive(false); } } private static Color Darken(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) float num = default(float); float num2 = default(float); float num3 = default(float); Color.RGBToHSV(c, ref num, ref num2, ref num3); Color result = Color.HSVToRGB(num, num2, num3 * 0.35f); result.a = c.a; return result; } } public class OwnNetwork : MonoBehaviour { private const int ViewId = 69420; private ManualLogSource _log; private PluginConfig _cfg; private GameObject _networkGo; private PhotonView _pv; private readonly Dictionary _playerReceivedReadyStatus = new Dictionary(); private bool _clientSentReadyStatus; private readonly Dictionary _playerModVersions = new Dictionary(); private bool _clientSentVersionReport; private readonly Dictionary _playerPresentationDone = new Dictionary(); private Coroutine _readyStatusCoroutine; private float _levelEnteredAt = -1f; private const float ModDetectionGraceSeconds = 10f; internal OwnMessageOverlay MessageOverlay { get; private set; } internal TeleportWatchdog Watchdog { get; private set; } internal OwnLoadEntryPoints EntryPoints { get; private set; } internal OwnWakeUpEffect WakeUpEffect { get; private set; } internal OwnLoadingScreen LoadingScreen { get; private set; } internal ManualLogSource Log => _log; public void Init(ManualLogSource log, PluginConfig cfg) { _log = log; _cfg = cfg; CreatePhotonView(); } internal void AttachDependencies(OwnMessageOverlay messageOverlay, TeleportWatchdog watchdog, OwnLoadEntryPoints entryPoints, OwnWakeUpEffect wakeUpEffect = null, OwnLoadingScreen loadingScreen = null) { MessageOverlay = messageOverlay; Watchdog = watchdog; EntryPoints = entryPoints; WakeUpEffect = wakeUpEffect; LoadingScreen = loadingScreen; } private void CreatePhotonView() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown try { if (!((Object)(object)_networkGo != (Object)null)) { _networkGo = new GameObject("PEAKQuickResume.OwnNetwork"); Object.DontDestroyOnLoad((Object)(object)_networkGo); _pv = _networkGo.AddComponent(); _networkGo.AddComponent().Owner = this; _pv.ViewID = 69420; _log.Trace($"OwnNetwork: PhotonView created (ViewID={69420})."); } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"OwnNetwork.CreatePhotonView failed: {arg}"); } } } private void Update() { if (_cfg == null) { return; } if (RunLauncher.InAirport) { StopReadyStatusRetry(); _clientSentReadyStatus = false; _clientSentVersionReport = false; _playerReceivedReadyStatus.Clear(); _playerModVersions.Clear(); _playerPresentationDone.Clear(); _levelEnteredAt = -1f; } else if (RunLauncher.InLevel) { if (_levelEnteredAt < 0f) { _levelEnteredAt = Time.unscaledTime; } if (!_clientSentReadyStatus && !RunLauncher.IsHost) { _readyStatusCoroutine = ((MonoBehaviour)this).StartCoroutine(SendReadyStatusToMaster()); _clientSentReadyStatus = true; } if (!_clientSentVersionReport && !RunLauncher.IsHost) { ((MonoBehaviour)this).StartCoroutine(ReportVersionToMaster()); _clientSentVersionReport = true; } } else if (RunLauncher.InTitle) { StopReadyStatusRetry(); _clientSentReadyStatus = false; _clientSentVersionReport = false; _playerReceivedReadyStatus.Clear(); _playerModVersions.Clear(); _playerPresentationDone.Clear(); _levelEnteredAt = -1f; } } private void StopReadyStatusRetry() { if (_readyStatusCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_readyStatusCoroutine); _readyStatusCoroutine = null; } } private IEnumerator SendReadyStatusToMaster() { while ((Object)(object)Character.localCharacter == (Object)null) { yield return null; } yield return (object)new WaitForSeconds(5f); for (float elapsed = 0f; elapsed < 60f; elapsed += 3f) { if (!RunLauncher.InLevel) { break; } try { _pv.RPC("RPC_SendReadyStatusToMaster", (RpcTarget)2, new object[2] { PhotonNetwork.LocalPlayer.UserId, PhotonNetwork.LocalPlayer.NickName }); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"OwnNetwork.SendReadyStatusToMaster RPC failed: {arg}"); } } yield return (object)new WaitForSeconds(3f); } } private IEnumerator ReportVersionToMaster() { while ((Object)(object)Character.localCharacter == (Object)null) { yield return null; } try { _pv.RPC("RPC_ReportModVersion", (RpcTarget)2, new object[2] { PhotonNetwork.LocalPlayer.UserId, "2.3.0" }); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.ReportVersionToMaster failed: " + ex.Message)); } } } internal void OnClientReportedVersion(string userId, string version) { try { _playerModVersions[userId] = version; _log.Trace("OwnNetwork: client " + userId + " reports Quick Resume v" + version + "."); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.OnClientReportedVersion failed: " + ex.Message)); } } } public bool PlayerReportedMod(string userId) { return _playerModVersions.ContainsKey(userId); } internal void HandleClientPresentation(bool show) { ((MonoBehaviour)this).StartCoroutine(show ? RunClientPresentationEnter() : RunClientPresentationExit()); } private IEnumerator RunClientPresentationEnter() { float waited = 0f; while ((Object)(object)Character.localCharacter == (Object)null && waited < 15f) { waited += Time.unscaledDeltaTime; yield return null; } if ((Object)(object)Character.localCharacter == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.RunClientPresentationEnter: local character still null after " + $"{15f:F0}s; proceeding anyway (wake-up beat will no-op).")); } } if (_cfg != null && !_cfg.DebugDisableLoadingScreen.Value && (Object)(object)LoadingScreen != (Object)null) { yield return LoadingScreen.FadeIn(_cfg.OwnLoadingScreenFadeTime.Value); } } private IEnumerator RunClientPresentationExit() { bool showLoadingScreen = _cfg != null && !_cfg.DebugDisableLoadingScreen.Value; if ((Object)(object)WakeUpEffect != (Object)null) { WakeUpEffect.Collapse(); } yield return FairootsCompat.WaitUntilReady(_log, delegate { WakeUpEffect?.RefreshHold(); }); if (_cfg != null) { float delayElapsed = 0f; for (float fadeOutDelay = Mathf.Max(0f, _cfg.OwnLoadingScreenFadeOutDelay.Value); delayElapsed < fadeOutDelay; delayElapsed += Time.unscaledDeltaTime) { WakeUpEffect?.RefreshHold(); yield return null; } } if (showLoadingScreen && (Object)(object)LoadingScreen != (Object)null) { yield return LoadingScreen.FadeOut(_cfg.OwnLoadingScreenFadeTime.Value); } if ((Object)(object)WakeUpEffect != (Object)null && _cfg != null) { yield return WakeUpEffect.Wake(_cfg.OwnWakeUpStandTime.Value); } try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_ClientPresentationDone", (RpcTarget)2, new object[1] { PhotonNetwork.LocalPlayer.UserId }); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.RunClientPresentationExit: RPC_ClientPresentationDone failed: " + ex.Message)); } } } internal void OnClientReportedReady(string userId, string userName) { try { if (!PhotonNetwork.OfflineMode && PhotonNetwork.IsMasterClient && _cfg != null && _cfg.OwnEnableClientReadyStatusCheck.Value) { if (!_playerReceivedReadyStatus.ContainsKey(userId)) { _playerReceivedReadyStatus.Add(userId, userName); } _log.Trace("OwnNetwork: RPC_SendReadyStatusToMaster userId=" + userId + ", userName=" + userName + "."); } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"OwnNetwork.OnClientReportedReady failed: {arg}"); } } } public bool CheckReadyStatusForPlayers() { if (_cfg == null || !_cfg.OwnEnableClientReadyStatusCheck.Value) { return true; } try { bool flag = _levelEnteredAt >= 0f && Time.unscaledTime - _levelEnteredAt >= 10f; Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { if ((Object)(object)val == (Object)null) { continue; } Character character = val.character; if (!((Object)(object)character == (Object)null)) { string userId = NetworkingUtilities.GetUserId(character.player); if (!((MonoBehaviourPun)character).photonView.Owner.IsMasterClient && !_playerReceivedReadyStatus.ContainsKey(userId) && (!flag || _playerModVersions.ContainsKey(userId))) { return false; } } } return true; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.CheckReadyStatusForPlayers failed (assuming ready): " + ex.Message)); } return true; } } public void RequestFalldamageProtectionAll(int seconds) { try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_RequestFalldamageProtection", (RpcTarget)0, new object[1] { seconds }); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.RequestFalldamageProtectionAll failed: " + ex.Message)); } } } public void CloseEndscreenOthers() { try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_CloseEndscreen", (RpcTarget)1, Array.Empty()); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.CloseEndscreenOthers failed: " + ex.Message)); } } } public void SendMessageOthers(string message, string colorKey, float seconds) { try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_SendMessage", (RpcTarget)1, new object[3] { message, colorKey, seconds.ToString(CultureInfo.InvariantCulture) }); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.SendMessageOthers failed: " + ex.Message)); } } } public void LoadingScreenOthers(bool enable, Vector3? target = null) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) try { string text = (target.HasValue ? FormatVector(target.Value) : ""); PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_Loadingscreen", (RpcTarget)1, new object[2] { enable ? "true" : "false", text }); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.LoadingScreenOthers failed: " + ex.Message)); } } } private static string FormatVector(Vector3 v) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; return v.x.ToString(invariantCulture) + "|" + v.y.ToString(invariantCulture) + "|" + v.z.ToString(invariantCulture); } internal static Vector3? ParseVector(string s) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(s)) { return null; } string[] array = s.Split('|'); if (array.Length != 3) { return null; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; if (float.TryParse(array[0], NumberStyles.Float, invariantCulture, out var result) && float.TryParse(array[1], NumberStyles.Float, invariantCulture, out var result2) && float.TryParse(array[2], NumberStyles.Float, invariantCulture, out var result3)) { return new Vector3(result, result2, result3); } return null; } public void ArmTerrainRandomizerSuppressionAll() { try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_ArmTerrainRandomizerSuppression", (RpcTarget)0, Array.Empty()); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.ArmTerrainRandomizerSuppressionAll failed: " + ex.Message)); } } } public void SyncDayCountAll(int dayCount) { try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_SyncDayCount", (RpcTarget)1, new object[1] { dayCount }); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.SyncDayCountAll failed: " + ex.Message)); } } } public bool ApplyStatusesViaVanilla(Character character, float[] statuses) { if ((Object)(object)character == (Object)null || statuses == null || statuses.Length == 0) { return false; } try { CharacterAfflictions val = character.refs?.afflictions; if ((Object)(object)val == (Object)null) { return false; } PhotonView val2 = PhotonView.Get((Component)(object)val); if ((Object)(object)val2 == (Object)null) { return false; } val2.RPC("RPC_ApplyStatusesFromFloatArray", ((MonoBehaviourPun)character).photonView.Owner, new object[1] { statuses }); return true; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.ApplyStatusesViaVanilla failed (" + ex.Message + "); falling back to our own channel, which only reaches clients running this mod.")); } return false; } } public void ApplyAfflictionsTo(PhotonView playerView, string userId, float[] statuses, float extraStamina, int petrifyAmount) { try { if (!((Object)(object)playerView == (Object)null) && playerView.Owner != null) { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_ApplyAfflictions", playerView.Owner, new object[4] { userId, statuses, extraStamina, petrifyAmount }); } } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.ApplyAfflictionsTo failed: " + ex.Message)); } } } public void EquipHeldItemFor(PhotonView playerView, string userId) { try { if (!((Object)(object)playerView == (Object)null) && playerView.Owner != null) { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_EquipHeldItem", playerView.Owner, new object[1] { userId }); } } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.EquipHeldItemFor failed: " + ex.Message)); } } } public void RestoreThornsFor(PhotonView playerView, string userId, int[] thornIndices) { try { if (!((Object)(object)playerView == (Object)null) && playerView.Owner != null) { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_RestoreThorns", playerView.Owner, new object[2] { userId, thornIndices }); } } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.RestoreThornsFor failed: " + ex.Message)); } } } public void RestoreAchievementProgressFor(PhotonView playerView, string userId, string achievementProgressJson) { try { if ((Object)(object)playerView == (Object)null || playerView.Owner == null) { _log.Trace("[achievement-debug] RestoreAchievementProgressFor('" + userId + "'): no player view/owner, nothing sent."); return; } _log.Trace("[achievement-debug] RestoreAchievementProgressFor('" + userId + "'): sending " + (string.IsNullOrEmpty(achievementProgressJson) ? "EMPTY (no saved progress for them)" : (achievementProgressJson.Length + " chars of saved progress")) + " " + $"to actor #{playerView.Owner.ActorNumber}."); PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_ApplyAchievementProgress", playerView.Owner, new object[2] { userId, achievementProgressJson ?? "" }); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.RestoreAchievementProgressFor failed: " + ex.Message)); } } } public void SuppressWatchdogForRestoredDeath(PhotonView playerView, string userId) { try { if (!((Object)(object)playerView == (Object)null) && playerView.Owner != null) { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_SuppressWatchdogForRestoredDeath", playerView.Owner, new object[1] { userId }); } } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.SuppressWatchdogForRestoredDeath failed: " + ex.Message)); } } } public void RecentlyLitCampfireOthers() { try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_RecentlyLitCampfire", (RpcTarget)1, Array.Empty()); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.RecentlyLitCampfireOthers failed: " + ex.Message)); } } } public void ClientPresentationOthers(bool show) { if (show) { _playerPresentationDone.Clear(); } try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_ClientPresentation", (RpcTarget)1, new object[1] { show }); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.ClientPresentationOthers failed: " + ex.Message)); } } } internal void OnClientPresentationDone(string userId) { try { if (!_playerPresentationDone.ContainsKey(userId)) { _playerPresentationDone[userId] = true; } _log.Trace("OwnNetwork: RPC_ClientPresentationDone userId=" + userId + "."); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"OwnNetwork.OnClientPresentationDone failed: {arg}"); } } } public bool AllClientsPresentationDone() { try { bool flag = _levelEnteredAt >= 0f && Time.unscaledTime - _levelEnteredAt >= 10f; Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { if ((Object)(object)val == (Object)null) { continue; } Character character = val.character; if (!((Object)(object)character == (Object)null)) { string userId = NetworkingUtilities.GetUserId(character.player); if (!((MonoBehaviourPun)character).photonView.Owner.IsMasterClient && !_playerPresentationDone.ContainsKey(userId) && (!flag || _playerModVersions.ContainsKey(userId))) { return false; } } } return true; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.AllClientsPresentationDone failed (assuming done): " + ex.Message)); } return true; } } public void RequestSaveToMaster() { try { PhotonView pv = _pv; if (pv != null) { pv.RPC("RPC_RequestSave", (RpcTarget)2, Array.Empty()); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnNetwork.RequestSaveToMaster failed: " + ex.Message)); } } } internal void SavePlayerCoopFromRpc() { OwnSaveCapture.SavePlayerCoop(_cfg, _log, this); } internal void LogError(string message) { ManualLogSource log = _log; if (log != null) { log.LogError((object)message); } } } public class OwnNetworkRpc : MonoBehaviourPun { internal OwnNetwork Owner; private const float AchievementRestoreWaitSeconds = 30f; [PunRPC] public void RPC_SendReadyStatusToMaster(string userId, string userName) { Owner?.OnClientReportedReady(userId, userName); } [PunRPC] public void RPC_ReportModVersion(string userId, string version) { Owner?.OnClientReportedVersion(userId, version); } [PunRPC] public void RPC_ClientPresentation(bool show) { Owner?.HandleClientPresentation(show); } [PunRPC] public void RPC_ClientPresentationDone(string userId) { Owner?.OnClientPresentationDone(userId); } [PunRPC] public void RPC_RequestSave() { try { if (PhotonNetwork.IsMasterClient) { Owner?.SavePlayerCoopFromRpc(); Owner?.EntryPoints?.ArmRecentlyLitCampfireCooldown(32f); } } catch { } } [PunRPC] public void RPC_RecentlyLitCampfire() { if (!PhotonNetwork.IsMasterClient) { Owner?.EntryPoints?.ArmRecentlyLitCampfireCooldown(32f); } } [PunRPC] public void RPC_RequestFalldamageProtection(int seconds) { OwnFallDamageProtection.Activate(seconds); } [PunRPC] public void RPC_ArmTerrainRandomizerSuppression() { OwnLoadEntryPoints.ArmSuppressExternalTerrainRandomizerOnce(); } [PunRPC] public void RPC_SendMessage(string message, string colorKey, string seconds) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) float result = 4f; float.TryParse(seconds, NumberStyles.Float, CultureInfo.InvariantCulture, out result); Color color = (Color)(colorKey switch { "error" => new Color(1f, 0.5f, 0.5f, 1f), "success" => new Color(0.5f, 1f, 0.5f, 1f), "warning" => new Color(1f, 0.8f, 0.4f, 1f), _ => new Color(0.6f, 0.8f, 1f, 1f), }); Owner?.MessageOverlay?.Show(message, color, result); } [PunRPC] public void RPC_Loadingscreen(string enable, string targetPayload) { if (enable == "true") { Owner?.Watchdog?.BeginLoadWindow(); HeightAchievementGuard.Suppress("client load window"); } else { HeightAchievementGuard.Release("client load window"); Owner?.Watchdog?.ArmPendingWatch(OwnNetwork.ParseVector(targetPayload)); } } [PunRPC] public void RPC_SyncDayCount(int dayCount) { try { DayNightManager val = Object.FindFirstObjectByType(); if ((Object)(object)val != (Object)null) { val.dayCount = dayCount; } } catch (Exception arg) { Owner?.LogError($"RPC_SyncDayCount error: {arg}"); } } [PunRPC] public void RPC_CloseEndscreen() { try { EndScreen val = Object.FindFirstObjectByType(); if ((Object)(object)val != (Object)null && ((MenuWindow)val).isOpen) { AccessTools.Method(typeof(MenuWindow), "Close", (Type[])null, (Type[])null)?.Invoke(val, null); } } catch { } } [PunRPC] public void RPC_ApplyAfflictions(string userId, float[] statuses, float extraStamina, int petrifyAmount) { try { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || NetworkingUtilities.GetUserId(localCharacter.player) != userId) { return; } CharacterAfflictions afflictions = localCharacter.refs.afflictions; AfflictionArrayCompat.CopyOverlap(statuses, afflictions.currentStatuses); try { localCharacter.SetExtraStamina((extraStamina > 0f && extraStamina <= 1f) ? extraStamina : 0f); } catch { } try { localCharacter.data.SetPetrify(petrifyAmount); } catch { } } catch (Exception arg) { Owner?.LogError($"RPC_ApplyAfflictions error: {arg}"); } } [PunRPC] public void RPC_EquipHeldItem(string userId) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) try { Character localCharacter = Character.localCharacter; if (!((Object)(object)localCharacter == (Object)null) && !(NetworkingUtilities.GetUserId(localCharacter.player) != userId) && localCharacter.player?.tempFullSlot != null && !localCharacter.player.tempFullSlot.IsEmpty()) { localCharacter.refs.items.EquipSlot(Optionable.Some((byte)250)); } } catch (Exception arg) { Owner?.LogError($"RPC_EquipHeldItem error: {arg}"); } } [PunRPC] public void RPC_RestoreThorns(string userId, int[] thornIndices) { try { Character localCharacter = Character.localCharacter; if (!((Object)(object)localCharacter == (Object)null) && !(NetworkingUtilities.GetUserId(localCharacter.player) != userId) && thornIndices != null) { List list = new List(thornIndices.Length); foreach (int num in thornIndices) { list.Add((ushort)num); } ThornsAndTicksRestore.ApplyThorns(localCharacter, list, Owner?.Log); } } catch (Exception arg) { Owner?.LogError($"RPC_RestoreThorns error: {arg}"); } } [PunRPC] public void RPC_SuppressWatchdogForRestoredDeath(string userId) { try { Character localCharacter = Character.localCharacter; if (!((Object)(object)localCharacter == (Object)null) && !(NetworkingUtilities.GetUserId(localCharacter.player) != userId)) { Owner?.Watchdog?.SuppressForRestoredDeath(); } } catch (Exception arg) { Owner?.LogError($"RPC_SuppressWatchdogForRestoredDeath error: {arg}"); } } [PunRPC] public void RPC_ApplyAchievementProgress(string userId, string achievementProgressJson) { try { ((MonoBehaviour)this).StartCoroutine(ApplyAchievementProgressWhenReady(userId, achievementProgressJson)); } catch (Exception arg) { Owner?.LogError($"RPC_ApplyAchievementProgress error: {arg}"); } } private IEnumerator ApplyAchievementProgressWhenReady(string userId, string achievementProgressJson) { ManualLogSource log = Owner?.Log; float deadline = Time.realtimeSinceStartup + 30f; string localUserId = null; while (Time.realtimeSinceStartup < deadline) { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.player != (Object)null) { localUserId = null; try { localUserId = NetworkingUtilities.GetUserId(localCharacter.player); } catch { } if (!string.IsNullOrEmpty(localUserId)) { break; } } yield return null; } if (string.IsNullOrEmpty(localUserId)) { if (log != null) { log.LogWarning((object)("[achievement-debug] RPC_ApplyAchievementProgress('" + userId + "'): DROPPED - no local " + $"character after {30f:F0}s, so this run's saved achievement progress " + "was not restored on this machine.")); } } else if (localUserId != userId) { log.Trace("[achievement-debug] RPC_ApplyAchievementProgress('" + userId + "'): ignored, this machine is '" + localUserId + "'."); } else { log.Trace("[achievement-debug] RPC_ApplyAchievementProgress('" + userId + "'): applying " + (string.IsNullOrEmpty(achievementProgressJson) ? "EMPTY (no saved progress for this player)" : (achievementProgressJson.Length + " chars")) + "."); AchievementProgressIO.ApplyLocal(AchievementProgressIO.FromJson(achievementProgressJson, log), log); } } } public static class OwnSaveCapture { private static Player _cachedLocalPlayer; public unsafe static void SavePlayerCoop(PluginConfig cfg, ManualLogSource log, OwnNetwork network, Campfire igniteBuffSource = null) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Invalid comparison between Unknown and I4 //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Invalid comparison between Unknown and I4 //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Invalid comparison between Unknown and I4 //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Invalid comparison between Unknown and I4 //IL_05bc: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_044a: Unknown result type (might be due to invalid IL or missing references) //IL_04a4: Unknown result type (might be due to invalid IL or missing references) //IL_04a6: Unknown result type (might be due to invalid IL or missing references) //IL_0564: Unknown result type (might be due to invalid IL or missing references) try { SaveTarget target = (RunLauncher.IsCustomRun ? SaveTarget.Custom() : SaveTarget.Normal(Ascents.currentAscent)); string text = OwnSavePaths.NewEventStamp(); string text2 = OwnSavePaths.LocalUserId(); List list = new List(); Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); Player[] array2 = array; foreach (Player val in array2) { if ((Object)(object)val != (Object)null) { list.Add(val.character.characterName); } } Vector3 val2 = ResolveWorldAnchor(array, log); HashSet claimed = new HashSet(); AncientStatueRestore.Capture(val2, claimed, log, out var state); LuggageRestore.Capture(val2, claimed, log, out var states); WorldItemRestore.Capture(val2, claimed, log, out var items); DeployableRestore.CaptureStoves(val2, log, out var states2); DeployableRestore.CaptureCannons(val2, log, out var states3); Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; float timePlayed = (float)Math.Round(RunTimerCompat.Read(Object.FindFirstObjectByType()), 3); DayNightManager obj = Object.FindFirstObjectByType(); float timeOfDay = (float)Math.Round(obj.timeOfDay, 3); int dayCount = obj.dayCount; MapHandler val3 = Object.FindFirstObjectByType(); Segment val4 = (Segment)(((Object)(object)val3 != (Object)null) ? ((int)val3.GetCurrentSegment()) : 0); string legacyName = ((object)(*(Segment*)(&val4))/*cast due to .constrained prefix*/).ToString(); List list2 = new List(); foreach (BiomeType biome in val3.biomes) { list2.Add(((object)(*(BiomeType*)(&biome))/*cast due to .constrained prefix*/).ToString()); if ((int)biome == 7 && (int)val4 == 1) { legacyName = ((object)(*(BiomeType*)(&biome))/*cast due to .constrained prefix*/).ToString(); } else if ((int)biome == 6 && (int)val4 == 2) { legacyName = ((object)(*(BiomeType*)(&biome))/*cast due to .constrained prefix*/).ToString(); } } legacyName = AreaNameCompat.ResolveAreaName(val4, legacyName, log); array2 = array; ReconnectData val6 = default(ReconnectData); SerializableRunBasedValues val7 = default(SerializableRunBasedValues); foreach (Player val5 in array2) { if ((Object)(object)val5 == (Object)null) { if (log != null) { log.LogError((object)"OwnSaveCapture.SavePlayerCoop: no Player found - cannot save progress."); } continue; } string userId = NetworkingUtilities.GetUserId(val5); string path = OwnSavePaths.For(target, offline: false, userId, text); PhotonView component = ((Component)val5).GetComponent(); bool flag = (((Object)(object)component != (Object)null) ? component.IsMine : (text2.Length == 0 || userId == text2)); Character character = val5.character; if ((Object)(object)character == (Object)null && log != null) { log.LogWarning((object)"OwnSaveCapture.SavePlayerCoop: Character is null for this player."); } List list3 = CaptureInventory(val5, cfg, log); List backpackItemStates = CaptureBackpack(val5, cfg, log); OwnSavedItemState heldItemState = CaptureHeldItem(val5, log); List stuckThornIndices = ThornsAndTicksRestore.CaptureThorns(character); bool hasTick = ThornsAndTicksRestore.CaptureTick(character); float[] array3 = character.refs.afflictions.currentStatuses.ToArray(); float num = character.data.extraStamina; int num2 = character.data.petrifyAmount; if (cfg.CaptureCampfireIgniteBuff.Value && (Object)(object)igniteBuffSource != (Object)null) { (num, num2) = CampfireIgniteBuffCompat.Apply(igniteBuffSource, character, num, num2, array3); } num = Mathf.Clamp(num, 0f, 1f); num = (float)Math.Round(num, 2); OwnSavedAchievementProgress achievementProgress = (((Object)(object)component != (Object)null && component.IsMine) ? AchievementProgressIO.CaptureLocal(log) : (ReconnectHandler.TryGetReconnectData(userId, ref val6, ref val7) ? AchievementProgressIO.ToSaved(val7, log) : null)); OwnSaveData ownSaveData = new OwnSaveData { settingsVersion = 8, saveDate = DateTime.Now.ToString("dd.MM.yyyy | HH:mm:ss"), hasBackpack = BackpackTypeCompat.HasAny(val5), backpackType = BackpackTypeCompat.Capture(val5), backpackOwnValues = CaptureBackpackOwnValues(val5, log), isSkeleton = character.data.isSkeleton, isDead = ((Object)(object)character != (Object)null && character.data.dead), inventoryItemStates = list3, backpackItemStates = backpackItemStates, heldItemState = heldItemState, stuckThornIndices = stuckThornIndices, hasTick = hasTick, afflictions_current = array3, extraStamina = ((num > 0f && num <= 1f) ? num : 0f), petrifyAmount = num2, achievementProgress = achievementProgress, gameVersion = GameVersionCompat.Current }; if (flag) { ownSaveData.posX = val2.x; ownSaveData.posY = val2.y; ownSaveData.posZ = val2.z; ownSaveData.playerNames = list; ownSaveData.campfireName = legacyName; ownSaveData.timePlayed = timePlayed; ownSaveData.timeOfDay = timeOfDay; ownSaveData.dayCount = dayCount; ownSaveData.sceneName = name; ownSaveData.biomes = val3.biomes; ownSaveData.biome_names = list2; ownSaveData.segment = val4; ownSaveData.ancientStatue = state; ownSaveData.luggageStates = states; ownSaveData.worldItemStates = items; ownSaveData.portableStoves = states2; ownSaveData.scoutCannons = states3; ownSaveData.nadirCommunerUserId = NadirCommuner.PendingUserId; ownSaveData.nadirCommunerName = NadirCommuner.PendingName; } Directory.CreateDirectory(Path.GetDirectoryName(path)); File.WriteAllText(path, JsonConvert.SerializeObject((object)ownSaveData, (Formatting)1)); if (log != null) { log.LogInfo((object)("OwnSaveCapture.SavePlayerCoop: saved " + (flag ? "host" : "client") + " file for " + $"{userId} (event {text}). Items: {list3.Count}" + (flag ? $", Pos: {val2}, Scene: {name}." : "."))); } } string text3 = MessagesLocalization.Get(MsgKey.SavedGameProgress); network?.MessageOverlay?.Show(text3, new Color(0.5f, 1f, 0.5f, 1f)); network?.SendMessageOthers(text3, "success", 4f); BackpackSaveMitigation.ApplyPendingRestores(offline: false, text, log); } catch (Exception arg) { if (log != null) { log.LogError((object)$"OwnSaveCapture.SavePlayerCoop failed: {arg}"); } } } private static Vector3 ResolveWorldAnchor(Player[] allPlayers, ManualLogSource log) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0145: 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) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (NadirCommuner.TryGetPendingAnchor(out var anchor)) { return anchor; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null && !localCharacter.data.dead) { return localCharacter.Head; } if (log != null) { log.LogWarning((object)"OwnSaveCapture: the host is dead (or has no character) at save time, so their own position is the off-map death zone - anchoring this save's world state on a living player instead."); } if (allPlayers != null) { foreach (Player val in allPlayers) { Character val2 = (((Object)(object)val != (Object)null) ? val.character : null); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)localCharacter) && !val2.data.dead) { log.Trace($"OwnSaveCapture: world state anchored on {val2.characterName} at {val2.Head}."); return val2.Head; } } } try { Campfire previousCampfire = MapHandler.PreviousCampfire; if ((Object)(object)previousCampfire != (Object)null) { Vector3 position = ((Component)previousCampfire).transform.position; if (log != null) { log.LogWarning((object)("OwnSaveCapture: no living player found either - anchoring on the checkpoint's " + $"own campfire at {position}.")); } return position; } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("OwnSaveCapture: could not read the current campfire as a fallback anchor: " + ex.Message)); } } if (log != null) { log.LogError((object)"OwnSaveCapture: could not resolve any sane world anchor for this save - falling back to the host's own position. If the host was dead, this checkpoint's world state and teleport target will be wrong (the load path detects and works around this, see OwnTeleportSequence)."); } if (!((Object)(object)localCharacter != (Object)null)) { return Vector3.zero; } return localCharacter.Head; } public unsafe static void SavePlayerOffline(PluginConfig cfg, ManualLogSource log, OwnMessageOverlay messageOverlay = null, Campfire igniteBuffSource = null) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Invalid comparison between Unknown and I4 //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Invalid comparison between Unknown and I4 //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Invalid comparison between Unknown and I4 //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_029e: 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_032a: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Invalid comparison between Unknown and I4 //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Unknown result type (might be due to invalid IL or missing references) try { Player val = FindLocalPlayer(log); if ((Object)(object)val == (Object)null) { if (log != null) { log.LogError((object)"OwnSaveCapture: no Player found - cannot save progress."); } return; } SaveTarget target = (RunLauncher.IsCustomRun ? SaveTarget.Custom() : SaveTarget.Normal(Ascents.currentAscent)); string stamp = OwnSavePaths.NewEventStamp(); string path = OwnSavePaths.For(target, offline: true, "", stamp); Character localCharacter = Character.localCharacter; Vector3 val2 = (((Object)(object)localCharacter != (Object)null) ? localCharacter.Head : ((Component)val).transform.position); if ((Object)(object)localCharacter == (Object)null && log != null) { log.LogWarning((object)"OwnSaveCapture: Character.localCharacter is null - used player.transform as fallback."); } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; List list = CaptureInventory(val, cfg, log); List backpackItemStates = CaptureBackpack(val, cfg, log); OwnSavedItemState heldItemState = CaptureHeldItem(val, log); List stuckThornIndices = ThornsAndTicksRestore.CaptureThorns(localCharacter); bool hasTick = ThornsAndTicksRestore.CaptureTick(localCharacter); HashSet claimed = new HashSet(); AncientStatueRestore.Capture(val2, claimed, log, out var state); LuggageRestore.Capture(val2, claimed, log, out var states); WorldItemRestore.Capture(val2, claimed, log, out var items); DeployableRestore.CaptureStoves(val2, log, out var states2); DeployableRestore.CaptureCannons(val2, log, out var states3); float[] array = Character.localCharacter.refs.afflictions.currentStatuses.ToArray(); float num = Character.localCharacter.data.extraStamina; int num2 = Character.localCharacter.data.petrifyAmount; if (cfg.CaptureCampfireIgniteBuff.Value && (Object)(object)igniteBuffSource != (Object)null) { (num, num2) = CampfireIgniteBuffCompat.Apply(igniteBuffSource, localCharacter, num, num2, array); } num = Mathf.Clamp(num, 0f, 1f); num = (float)Math.Round(num, 2); float timePlayed = (float)Math.Round(RunTimerCompat.Read(Object.FindFirstObjectByType()), 3); DayNightManager obj = Object.FindFirstObjectByType(); float timeOfDay = (float)Math.Round(obj.timeOfDay, 3); int dayCount = obj.dayCount; MapHandler val3 = Object.FindFirstObjectByType(); Segment val4 = (Segment)(((Object)(object)val3 != (Object)null) ? ((int)val3.GetCurrentSegment()) : 0); string legacyName = ((object)(*(Segment*)(&val4))/*cast due to .constrained prefix*/).ToString(); List list2 = new List(); foreach (BiomeType biome in val3.biomes) { list2.Add(((object)(*(BiomeType*)(&biome))/*cast due to .constrained prefix*/).ToString()); if ((int)biome == 7 && (int)val4 == 1) { legacyName = ((object)(*(BiomeType*)(&biome))/*cast due to .constrained prefix*/).ToString(); } else if ((int)biome == 6 && (int)val4 == 2) { legacyName = ((object)(*(BiomeType*)(&biome))/*cast due to .constrained prefix*/).ToString(); } } legacyName = AreaNameCompat.ResolveAreaName(val4, legacyName, log); OwnSavedAchievementProgress achievementProgress = AchievementProgressIO.CaptureLocal(log); OwnSaveData ownSaveData = new OwnSaveData { settingsVersion = 8, posX = val2.x, posY = val2.y, posZ = val2.z, saveDate = DateTime.Now.ToString("dd.MM.yyyy | HH:mm:ss"), playerNames = new List { val.character.characterName }, campfireName = legacyName, timePlayed = timePlayed, timeOfDay = timeOfDay, dayCount = dayCount, sceneName = name, biomes = val3.biomes, biome_names = list2, segment = val4, hasBackpack = BackpackTypeCompat.HasAny(val), backpackType = BackpackTypeCompat.Capture(val), backpackOwnValues = CaptureBackpackOwnValues(val, log), isSkeleton = Character.localCharacter.data.isSkeleton, isDead = Character.localCharacter.data.dead, inventoryItemStates = list, backpackItemStates = backpackItemStates, heldItemState = heldItemState, stuckThornIndices = stuckThornIndices, hasTick = hasTick, afflictions_current = array, extraStamina = ((num > 0f && num <= 1f) ? num : 0f), petrifyAmount = num2, ancientStatue = state, luggageStates = states, worldItemStates = items, achievementProgress = achievementProgress, portableStoves = states2, scoutCannons = states3, nadirCommunerUserId = NadirCommuner.PendingUserId, nadirCommunerName = NadirCommuner.PendingName, gameVersion = GameVersionCompat.Current }; Directory.CreateDirectory(Path.GetDirectoryName(path)); File.WriteAllText(path, JsonConvert.SerializeObject((object)ownSaveData, (Formatting)1)); if (log != null) { log.LogInfo((object)$"OwnSaveCapture: position + inventory saved. Pos: {val2} Scene: {name}, Items: {list.Count}."); } messageOverlay?.Show(MessagesLocalization.Get(MsgKey.SavedGameProgress), new Color(0.5f, 1f, 0.5f, 1f)); BackpackSaveMitigation.ApplyPendingRestores(offline: true, stamp, log); } catch (Exception arg) { if (log != null) { log.LogError((object)$"OwnSaveCapture.SavePlayerOffline failed: {arg}"); } } } private static List CaptureInventory(Player localPlayer, PluginConfig cfg, ManualLogSource log) { List list = new List(); if (localPlayer.itemSlots == null) { return list; } int num = 0; ItemSlot[] itemSlots = localPlayer.itemSlots; foreach (ItemSlot val in itemSlots) { if (val != null && !val.IsEmpty() && !((Object)(object)val.prefab == (Object)null)) { ItemInstanceData data = val.data; if (data != null) { OwnSavedItemState ownSavedItemState = new OwnSavedItemState { itemId = val.prefab.itemID, slotIndex = num }; CaptureItemStateValues(data, ownSavedItemState.values); list.Add(ownSavedItemState); num++; } } } return list; } private static Dictionary CaptureBackpackOwnValues(Player localPlayer, ManualLogSource log) { try { if (!BackpackTypeCompat.HasAny(localPlayer)) { return null; } BackpackSlot backpackSlot = localPlayer.backpackSlot; ItemInstanceData val = ((backpackSlot != null) ? ((ItemSlot)backpackSlot).data : null); if (val == null) { return null; } Dictionary dictionary = new Dictionary(); CaptureItemStateValues(val, dictionary); BackpackSlot backpackSlot2 = localPlayer.backpackSlot; BackpackTypeCompat.EnsureFuelCaptured((backpackSlot2 != null) ? ((ItemSlot)backpackSlot2).prefab : null, val, dictionary, log); return (dictionary.Count > 0) ? dictionary : null; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("OwnSaveCapture: worn-backpack state capture failed (non-fatal): " + ex.Message)); } return null; } } private static List CaptureBackpack(Player localPlayer, PluginConfig cfg, ManualLogSource log) { List list = new List(); try { BackpackData backpackData = OwnInventoryRestore.GetBackpackData(localPlayer); if (backpackData?.itemSlots == null) { return list; } for (byte b = 0; b < backpackData.itemSlots.Length; b++) { ItemSlot val = backpackData.itemSlots[b]; if (val != null && !val.IsEmpty() && !((Object)(object)val.prefab == (Object)null)) { ItemInstanceData data = val.data; if (data != null) { OwnSavedBackpackItemState ownSavedBackpackItemState = new OwnSavedBackpackItemState { slotIndex = b, itemId = val.prefab.itemID }; CaptureItemStateValues(data, ownSavedBackpackItemState.values); list.Add(ownSavedBackpackItemState); } } } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("OwnSaveCapture: backpackData capture failed (non-fatal): " + ex.Message)); } } return list; } private static OwnSavedItemState CaptureHeldItem(Player localPlayer, ManualLogSource log) { ItemSlot val = localPlayer?.tempFullSlot; if (val == null || val.IsEmpty() || (Object)(object)val.prefab == (Object)null) { return null; } ItemInstanceData data = val.data; if (data == null) { return null; } OwnSavedItemState ownSavedItemState = new OwnSavedItemState { itemId = val.prefab.itemID, slotIndex = 250 }; CaptureItemStateValues(data, ownSavedItemState.values); return ownSavedItemState; } private static void CaptureItemStateValues(ItemInstanceData instanceData, Dictionary values) { foreach (KeyValuePair item in OwnItemStateIO.ReadItemStateValues(instanceData)) { values[item.Key] = new OwnSavedEntry { type = item.Value.TypeName, value = item.Value.Value }; } } private static Player FindLocalPlayer(ManualLogSource log) { if ((Object)(object)_cachedLocalPlayer != (Object)null) { return _cachedLocalPlayer; } Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { PhotonView component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && component.IsMine) { _cachedLocalPlayer = val; log.Trace("OwnSaveCapture: local Player via PhotonView.IsMine found."); return _cachedLocalPlayer; } } Player[] array2 = Object.FindObjectsByType((FindObjectsSortMode)0); if (array2.Length != 0) { _cachedLocalPlayer = array2[0]; if (log != null) { log.LogWarning((object)"OwnSaveCapture: local Player randomised (used first Player)."); } } return _cachedLocalPlayer; } } public class OwnSaveData { public int settingsVersion; public string saveDate; public List playerNames; public string campfireName; public float timePlayed; public float timeOfDay; public int dayCount; public float posX; public float posY; public float posZ; public string sceneName; public List biomes; public List biome_names; public Segment segment; public bool hasBackpack; public int backpackType; public Dictionary backpackOwnValues; public bool isSkeleton; public List inventoryItemStates; public List backpackItemStates; public float[] afflictions_current; public float extraStamina; public int petrifyAmount; public OwnSavedItemState heldItemState; public List stuckThornIndices; public bool hasTick; public OwnSavedStatueState ancientStatue; public List luggageStates; public List worldItemStates; public bool extModsPeakapaloozaPEAKTOBEACH; public OwnSavedAchievementProgress achievementProgress; public List portableStoves; public List scoutCannons; public bool isDead; public string nadirCommunerUserId; public string nadirCommunerName; public string gameVersion; } public class OwnSavedItemState { public int slotIndex; public ushort itemId; public Dictionary values = new Dictionary(); } public class OwnSavedBackpackItemState { public byte slotIndex; public ushort itemId; public Dictionary values = new Dictionary(); } public class OwnSavedStatueState { public bool broken; public OwnSavedPositionedItem item; } public class OwnSavedLuggageState { public bool opened; public List items = new List(); } public class OwnSavedPositionedItem { public ushort itemId; public float posX; public float posY; public float posZ; public float rotX; public float rotY; public float rotZ; public float rotW; public Dictionary values = new Dictionary(); public List backpackContents; } public class OwnSavedEntry { public string type; public float value; } public class OwnSavedAchievementProgress { public Dictionary runBasedInts = new Dictionary(); public Dictionary runBasedFloats = new Dictionary(); public List runBasedFruitsEaten = new List(); public List shroomBerriesEaten = new List(); public List nonToxicMushroomsEaten = new List(); public List gourmandRequirementsEaten = new List(); public List achievementsEarnedThisRun = new List(); public List completedAscentsThisRun = new List(); } public class OwnSavedDeployableState { public float posX; public float posY; public float posZ; public float rotX; public float rotY; public float rotZ; public float rotW; } public static class OwnSavePaths { public const string StampFormat = "yyyyMMdd_HHmmss_fff"; public const string StampSeparator = "__"; public static string ArchiveRoot => Path.Combine(Paths.PluginPath, "QuickResume", "Archive"); public static string ArchiveDir(bool offline) { return Path.Combine(ArchiveRoot, offline ? "Offline" : "Coop"); } public static string NewEventStamp() { return DateTime.UtcNow.ToString("yyyyMMdd_HHmmss_fff", CultureInfo.InvariantCulture); } public static string Stem(SaveTarget target, bool offline, string userId) { string text = (target.IsCustom ? "CustomRun" : target.Ascent.ToString(CultureInfo.InvariantCulture)); if (!offline) { return "peak_save_" + text + "_" + userId; } return "peak_save_" + text + "_offline"; } public static string For(SaveTarget target, bool offline, string userId, string stamp) { return Path.Combine(ArchiveDir(offline), Stem(target, offline, userId) + "__" + stamp + ".json"); } public static bool TrySplit(string fileName, out string stem, out string stamp) { stem = ""; stamp = ""; string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); if (string.IsNullOrEmpty(fileNameWithoutExtension)) { return false; } int num = fileNameWithoutExtension.LastIndexOf("__", StringComparison.Ordinal); if (num <= 0 || num >= fileNameWithoutExtension.Length - "__".Length) { return false; } stem = fileNameWithoutExtension.Substring(0, num); stamp = fileNameWithoutExtension.Substring(num + "__".Length); return stamp.Length > 0; } public static bool TryGetCoopUserId(string stem, out string userId) { userId = ""; if (string.IsNullOrEmpty(stem)) { return false; } int num = stem.LastIndexOf('_'); if (num <= 0 || num >= stem.Length - 1) { return false; } userId = stem.Substring(num + 1); if (userId.Length > 0) { return userId != "offline"; } return false; } public static string LocalUserId() { try { Player localPlayer = PhotonNetwork.LocalPlayer; return ((localPlayer != null) ? localPlayer.UserId : null) ?? ""; } catch { return ""; } } } public class OwnTeleportSequence : MonoBehaviour { private ManualLogSource _log; private PluginConfig _cfg; private OwnLoadEntryPoints _entryPoints; private OwnWakeUpEffect _wakeUpEffect; private OwnLoadingScreen _loadingScreen; private const string ReviveRpcName = "ReviveCharacter"; private const string ReviveAtPositionRpcName = "RPCA_ReviveAtPosition"; private bool _clientWarpSettled = true; private bool _clientWarpAllArrived = true; private const float LateAchievementRestoreSeconds = 45f; private const float RisingFieldClientGraceSeconds = 20f; private const float RisingFieldHoldCeilingSeconds = 90f; private const float RisingFieldStartConfirmSeconds = 15f; private Coroutine _risingFieldHold; private const int ReviveSettleSteps = 10; private const int RequiredStableFrames = 5; public bool IsRunning { get; private set; } public bool RestoreComplete { get; private set; } public void Init(ManualLogSource log, PluginConfig cfg, OwnLoadEntryPoints entryPoints, OwnWakeUpEffect wakeUpEffect = null, OwnLoadingScreen loadingScreen = null) { _log = log; _cfg = cfg; _entryPoints = entryPoints; _wakeUpEffect = wakeUpEffect; _loadingScreen = loadingScreen; } public void Begin(OwnSaveData data, SaveSelection selection) { ((MonoBehaviour)this).StartCoroutine(RunSequenceWrapper(data, selection)); } private IEnumerator RunSequenceWrapper(OwnSaveData data, SaveSelection selection) { IsRunning = true; RestoreComplete = false; HeightAchievementGuard.Suppress("teleport sequence"); IEnumerator sequence = RunSequence(data, selection); while (true) { object obj = null; bool flag; try { flag = sequence.MoveNext(); if (flag) { obj = sequence.Current; } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)("OwnTeleportSequence: the restore sequence threw and was aborted. The loading " + $"screen is being torn down so the game is not left on \"LOADING SAVE...\" forever: {arg}")); } break; } if (!flag) { break; } yield return obj; } IsRunning = false; RestoreComplete = true; HeightAchievementGuard.Release("teleport sequence"); if ((Object)(object)_loadingScreen != (Object)null) { bool flag2 = false; try { flag2 = ((Behaviour)_loadingScreen).isActiveAndEnabled; } catch { } if (flag2) { yield return _loadingScreen.FadeOut(0.5f); } } } private IEnumerator RunSequence(OwnSaveData data, SaveSelection selection) { bool offline = selection.Offline; _clientWarpSettled = true; _clientWarpAllArrived = true; Segment finalSegment = data.segment; Vector3 savedPos = new Vector3(data.posX, data.posY, data.posZ); float waitTime = Mathf.Max(0f, _cfg.OwnJumpLogicWaitTime.Value); bool deathZoneSave = IsDeathZonePosition(savedPos); if (deathZoneSave) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)($"OwnTeleportSequence: this checkpoint's saved position {savedPos} is the game's " + "off-map death zone - it was written while the host was dead by a build that still anchored world state on them. Retargeting to the saved segment's own campfire after the jump.")); } } if (_cfg.RestoreAchievements.Value) { HashSet hashSet = new HashSet(); AchievementProgressIO.RestoreAllPlayers(selection, _entryPoints, _log, hashSet); if (!offline) { ((MonoBehaviour)this).StartCoroutine(RestoreAchievementsForLateSpawners(selection, hashSet)); } } float stepWait = ((offline && _cfg.OwnFastSoloTeleport.Value) ? 0f : waitTime); if (!offline) { yield return WaitForEveryPlayerToRegister(); } ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)($"OwnTeleportSequence: executing custom jump to: {finalSegment}" + ((stepWait < waitTime) ? " (fast solo cadence)" : ""))); } bool wakeUpEnabled = _cfg.OwnWakeUpAnimationEnabled.Value; bool showLoadingScreen = wakeUpEnabled && !_cfg.DebugDisableLoadingScreen.Value; if (wakeUpEnabled) { yield return (object)new WaitForSeconds(Mathf.Max(0f, _cfg.OwnLoadingScreenFadeInDelay.Value)); } if (wakeUpEnabled) { _entryPoints.Network?.ClientPresentationOthers(show: true); } if (showLoadingScreen && (Object)(object)_loadingScreen != (Object)null) { yield return _loadingScreen.FadeIn(_cfg.OwnLoadingScreenFadeTime.Value); } _entryPoints.Network?.Watchdog?.BeginLoadWindow(); _entryPoints.Network?.LoadingScreenOthers(enable: true); yield return (object)new WaitForSeconds(waitTime); _entryPoints.Network?.RequestFalldamageProtectionAll(30); yield return (object)new WaitForSeconds(waitTime); ReviveDeadPlayers(savedPos + new Vector3(0f, 4f, 0f)); yield return (object)new WaitForSeconds(waitTime); TryCloseLingeringEndScreen(); MapHandler instance = Singleton.Instance; int index = (int)finalSegment; MapSegment targetSegment = (((Object)(object)instance != (Object)null && index >= 0 && index < instance.segments.Length) ? instance.segments[index] : null); Vector3 spawnPos = savedPos; spawnPos.y += 5f; if ((int)finalSegment == 6 && (Object)(object)VoidBiome.instance == (Object)null) { Vector3 val = (((Object)(object)Character.localCharacter != (Object)null) ? Character.localCharacter.Center : savedPos); ManualLogSource log3 = _log; if (log3 != null) { log3.LogError((object)("OwnTeleportSequence: this checkpoint is in Nadir, but this level scene has no " + $"VoidBiome to jump into. Skipping the segment jump and restoring at {val} instead, so " + "nobody is dropped into an area that was never instantiated.")); } savedPos = val; spawnPos = val; spawnPos.y += 5f; finalSegment = (Segment)(((Object)(object)instance != (Object)null) ? ((int)instance.GetCurrentSegment()) : 0); index = (int)finalSegment; } try { if (offline) { MapHandler.SetSegmentOnSpawn(finalSegment, (int)finalSegment); } else { MapHandler.JumpToSegment(finalSegment); } } catch (Exception ex) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogError((object)($"OwnTeleportSequence: the segment jump to {finalSegment} threw ({ex.Message}) - most " + "likely a player was mid-respawn and left a destroyed Character in PlayerHandler. Continuing with the restore; the warps below place everyone regardless.")); } } if (!offline) { SyncLastRevivedSegment(finalSegment); } if ((int)finalSegment == 6 && RunLauncher.IsHost) { PreCommuneWithScoutmasterSoul(data, savedPos); if (_risingFieldHold != null) { ((MonoBehaviour)this).StopCoroutine(_risingFieldHold); } _risingFieldHold = ((MonoBehaviour)this).StartCoroutine(HoldRisingFieldUntilEveryoneHasControl()); } if (offline) { Campfire previousCampfire = MapHandler.PreviousCampfire; if ((Object)(object)previousCampfire != (Object)null && !previousCampfire.Lit) { previousCampfire.LightWithoutReveal(); } } if (deathZoneSave) { Campfire previousCampfire2 = MapHandler.PreviousCampfire; if ((Object)(object)previousCampfire2 != (Object)null) { savedPos = ((Component)previousCampfire2).transform.position; spawnPos = savedPos; spawnPos.y += 5f; ManualLogSource log5 = _log; if (log5 != null) { log5.LogWarning((object)("OwnTeleportSequence: death-zone checkpoint retargeted to the " + $"{finalSegment} campfire at {savedPos}.")); } } else { ManualLogSource log6 = _log; if (log6 != null) { log6.LogError((object)"OwnTeleportSequence: this checkpoint's position is the off-map death zone and no campfire could be found for the saved segment - loading it anyway, but expect to land off the map. Re-save from a fresh checkpoint to replace it."); } } } if (RunLauncher.IsHost && (_cfg.RestoreDaytime.Value || _cfg.RestoreDay.Value)) { DayNightManager val2 = Object.FindFirstObjectByType(); if ((Object)(object)val2 != (Object)null) { if (_cfg.RestoreDaytime.Value && data.timeOfDay != 0f) { val2.setTimeOfDay(data.timeOfDay); } if (_cfg.RestoreDay.Value && data.dayCount != 0) { val2.dayCount = data.dayCount; _entryPoints.Network?.SyncDayCountAll(data.dayCount); } } } if (RunLauncher.IsHost && _entryPoints.LoadedSaveFileThisRound) { OwnWorldLootReset.DestroyLeftoverHeldItems(_log); } if ((int)finalSegment >= 5) { index--; } if ((int)finalSegment == 6 && (Object)(object)instance != (Object)null && index >= 0 && instance.segments != null && index < instance.segments.Length) { targetSegment = instance.segments[index]; } if (offline && (int)finalSegment == 6) { try { OrbFogHandler instance2 = Singleton.Instance; if (instance2 != null) { instance2.SetFogOrigin(index); } } catch (Exception ex2) { ManualLogSource log7 = _log; if (log7 != null) { log7.LogWarning((object)("OwnTeleportSequence: switching the fog sphere off for Nadir failed (cosmetic only, continuing): " + ex2.Message)); } } } yield return (object)new WaitForSeconds(stepWait); OwnWorldLootReset.ResetWorldLoot(_log); if (RunLauncher.IsHost) { WorldItemRestore.Restore(data, savedPos, _cfg, _log); if (_cfg.RestoreAncientStatue.Value) { AncientStatueRestore.Restore(data, savedPos, _log); } if (_cfg.RestoreLuggage.Value) { LuggageRestore.Restore(data, savedPos, _log); } } if (RunLauncher.IsHost) { if (_entryPoints.LoadedSaveFileThisRound && targetSegment != null) { try { ISpawner[] componentsInChildren = targetSegment.segmentParent.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].TrySpawnItems(); } } catch (Exception arg) { ManualLogSource log8 = _log; if (log8 != null) { log8.LogError((object)$"OwnTeleportSequence: TrySpawnItems failed: {arg}"); } } } yield return (object)new WaitForSeconds(stepWait); } if (_entryPoints.LoadedSaveFileThisRound) { OwnWorldLootReset.DestroyStaleWorldObjects(_log); } if (RunLauncher.IsHost && _cfg.RestoreDeployables.Value) { DeployableRestore.RestoreStoves(data, savedPos, _log); DeployableRestore.RestoreCannons(data, savedPos, _log); } bool isFoggedSegment = (int)finalSegment >= 0 && (int)finalSegment <= 4; if (isFoggedSegment) { yield return (object)new WaitForSeconds(stepWait); ((MonoBehaviour)this).StartCoroutine(OwnEnvironmentReset.ResetFogAfterLoad(index, finalSegment, _log)); } if ((int)finalSegment == 2) { Tornado[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Tornado val3 in array) { if ((Object)(object)val3 != (Object)null && ((Object)val3).name.Contains("Clone")) { try { Object.Destroy((Object)(object)((Component)val3).gameObject); } catch { } } } } if ((int)finalSegment == 4 && _entryPoints.LoadedSaveFileThisRound) { OwnEnvironmentReset.ResetLavaAfterLoad(_log); yield return (object)new WaitForSeconds(0.5f); } yield return (object)new WaitForSeconds(waitTime); if (RunLauncher.IsHost) { yield return ((MonoBehaviour)this).StartCoroutine(TeleportToPosition(spawnPos)); if ((int)finalSegment == 4 && Ascents.currentAscent < 4) { ((MonoBehaviour)this).StartCoroutine(OwnEnvironmentReset.SpawnFlaresAtPeak()); } if ((int)finalSegment == 4) { StrangeGemRestore.Restore(_log); } } HashSet restoringAsDead = (_cfg.RestoreDeathState.Value ? DeathStateRestore.ResolveSavedDeadUserIds(selection, _log) : new HashSet()); bool inventoryRestoreDone = !RunLauncher.IsHost; if (RunLauncher.IsHost) { ManualLogSource log9 = _log; if (log9 != null) { log9.LogInfo((object)"OwnTeleportSequence: starting the inventory/world restore."); } ((MonoBehaviour)this).StartCoroutine(RunInventoryRestoreAndSignal()); } if (isFoggedSegment) { ((MonoBehaviour)this).StartCoroutine(OwnEnvironmentReset.ResetFogAfterLoad(index, finalSegment, _log, extendedTime: true)); } if (wakeUpEnabled && (Object)(object)_wakeUpEffect != (Object)null) { _wakeUpEffect.Collapse(); } if (wakeUpEnabled) { for (float elapsed = 0f; (!inventoryRestoreDone && elapsed < 10f) || (!_clientWarpSettled && elapsed < 32f); elapsed += Time.unscaledDeltaTime) { _wakeUpEffect?.RefreshHold(); yield return null; } } if (RunLauncher.IsHost) { DeathStateRestore.EnsureUnsavedPlayersAlive(restoringAsDead, _log); DeathStateRestore.ApplySavedDeaths(restoringAsDead, _entryPoints, _log); } RestoreComplete = true; if (wakeUpEnabled) { _entryPoints.Network?.ClientPresentationOthers(show: false); } yield return FairootsCompat.WaitUntilReady(_log, delegate { _wakeUpEffect?.RefreshHold(); }); if (wakeUpEnabled) { float elapsed = 0f; for (float fadeOutDelay = Mathf.Max(0f, _cfg.OwnLoadingScreenFadeOutDelay.Value); elapsed < fadeOutDelay; elapsed += Time.unscaledDeltaTime) { _wakeUpEffect?.RefreshHold(); yield return null; } } if (showLoadingScreen && (Object)(object)_loadingScreen != (Object)null) { yield return _loadingScreen.FadeOut(_cfg.OwnLoadingScreenFadeTime.Value); } if (wakeUpEnabled && (Object)(object)_wakeUpEffect != (Object)null) { yield return _wakeUpEffect.Wake(_cfg.OwnWakeUpStandTime.Value); } if (wakeUpEnabled && !_clientWarpAllArrived) { _entryPoints.Network?.MessageOverlay?.Show(MessagesLocalization.Get(MsgKey.PlayersTimedOut), new Color(1f, 0.5f, 0.5f, 1f)); } _entryPoints.MarkLoadedThisRound(); IEnumerator RunInventoryRestoreAndSignal() { try { yield return ((MonoBehaviour)this).StartCoroutine(OwnInventoryRestore.RestoreAll(selection, _cfg, _entryPoints, _log, restoringAsDead)); } finally { inventoryRestoreDone = true; } } } private IEnumerator RestoreAchievementsForLateSpawners(SaveSelection selection, HashSet restored) { float deadline = Time.realtimeSinceStartup + 45f; while (Time.realtimeSinceStartup < deadline) { int num = ((PhotonNetwork.CurrentRoom != null) ? PhotonNetwork.CurrentRoom.PlayerCount : 0); if (num > 0 && restored.Count >= num) { _log.Trace($"[achievement-debug] RestoreAllPlayers: all {num} player(s) in the room handled."); yield break; } yield return (object)new WaitForSeconds(0.5f); AchievementProgressIO.RestoreAllPlayers(selection, _entryPoints, _log, restored); } int num2 = ((PhotonNetwork.CurrentRoom != null) ? PhotonNetwork.CurrentRoom.PlayerCount : 0); if (num2 > restored.Count) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)($"[achievement-debug] RestoreAllPlayers: gave up after {45f:F0}s " + $"with {restored.Count}/{num2} player(s) restored - whoever is missing never got a Player " + "object in time, so their per-run badge progress restarts from zero this run.")); } } } private static bool IsDeathZonePosition(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) Vector3 val = pos - new Vector3(0f, 5000f, -5000f); return ((Vector3)(ref val)).sqrMagnitude < 2500f; } private void ReviveDeadPlayers(Vector3 pos) { Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { Character character = array[i].character; if ((Object)(object)character == (Object)null || (!character.data.dead && !character.data.passedOut && !character.data.fullyPassedOut)) { continue; } if (!PhotonNetwork.OfflineMode && (Object)(object)((MonoBehaviourPun)character).photonView != (Object)null) { try { ((MonoBehaviourPun)character).photonView.RPC("ReviveCharacter", (RpcTarget)0, new object[1] { false }); _log.Trace("OwnTeleportSequence.ReviveDeadPlayers: revived " + character.characterName + " (networked)."); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnTeleportSequence.ReviveDeadPlayers: ReviveCharacter failed for " + character.characterName + " (" + ex.Message + "); falling back to a local-only revive.")); } goto IL_00f7; } continue; } goto IL_00f7; IL_00f7: try { character.refs.ragdoll.ToggleCollision(true); } catch { } character.data.dead = false; character.data.deathTimer = 0f; character.data.passedOut = false; character.data.fullyPassedOut = false; character.data.sinceGrounded = 0f; character.refs.afflictions.ClearAllStatus(true, true); character.refs.afflictions.AdjustStatus((STATUSTYPE)13, -0.75f, false); ThornsAndTicksRestore.ClearThornsSilently(character, _log); character.refs.afflictions.ClearAllAfflictions(); character.data.fallSeconds = 0f; } } private void PreCommuneWithScoutmasterSoul(OwnSaveData data, Vector3 anchorPos) { //IL_0079: 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) try { Character val = NadirCommuner.ResolveSavedCommuner(data, _log); if ((Object)(object)val == (Object)null || (Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"OwnTeleportSequence: no character available to stand in for the interactor, so Nadir's soul pillar is left unbroken. The invisible walls stay up until somebody communes."); } return; } ScoutmasterSoulPillar val2 = null; float num = float.MaxValue; ScoutmasterSoulPillar[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (ScoutmasterSoulPillar val3 in array) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)((MonoBehaviourPun)val3).photonView == (Object)null)) { float num2 = Vector3.Distance(((Component)val3).transform.position, anchorPos); if (num2 < num) { num = num2; val2 = val3; } } } if ((Object)(object)val2 == (Object)null) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)"OwnTeleportSequence: this is a Nadir checkpoint but no active ScoutmasterSoulPillar was found to pre-commune with. Loading anyway; the invisible walls will stay up until somebody communes in-game."); } return; } ScoutmasterSoulPillarAutoSavePatch.SuppressNextBreak(10f); ((MonoBehaviourPun)val2).photonView.RPC("RPC_Break", (RpcTarget)0, new object[2] { 0, ((MonoBehaviourPun)val).photonView }); ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)($"OwnTeleportSequence: pre-communed with the soul pillar {num:F1}m from the " + "checkpoint as " + val.characterName + ", restoring Nadir to its post-commune state.")); } ((MonoBehaviour)this).StartCoroutine(EnsureSoulFreed()); } catch (Exception arg) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogError((object)("OwnTeleportSequence: pre-communing with Nadir's soul pillar failed (non-fatal, " + $"the walls will stay up until somebody communes in-game): {arg}")); } } } private IEnumerator EnsureSoulFreed() { yield return (object)new WaitForSeconds(12f); if (VoidBiome.SoulFreedStatus >= 0) { yield break; } try { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"OwnTeleportSequence: the pre-commune never reported the soul as freed, so the game's own break routine must have been cut short. Firing the soul-freed event directly to make sure Nadir's invisible walls come down."); } GlobalEvents.TriggerSoulFreed(0); GlobalEvents.TriggerSoulFreed(1); } catch (Exception arg) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)$"OwnTeleportSequence: the soul-freed failsafe threw (non-fatal): {arg}"); } } } private IEnumerator HoldRisingFieldUntilEveryoneHasControl() { bool waitForClients = !PhotonNetwork.OfflineMode && _cfg.OwnWakeUpAnimationEnabled.Value; OwnNetwork network = _entryPoints?.Network; float deadline = Time.realtimeSinceStartup + 90f; float clientDeadline = -1f; LavaRising field = null; string timedOutBecause = null; bool released = false; while (true) { if ((Object)(object)field == (Object)null) { field = NadirRisingField.Find(_log); if ((Object)(object)field != (Object)null) { NadirRisingField.Describe(field, _log); if (!Ascents.fogEnabled) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"OwnTeleportSequence: this run has fog off (Tenderfoot, or a custom run with it disabled), so Nadir's rising field stays down - the game's own rule, the same one that keeps the Caldera's lava down. Leaving it alone."); } yield break; } NadirRisingField.BroadcastParked(field, _log); ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"OwnTeleportSequence: Nadir's rising field parked until every player has control."); } } } if (Time.realtimeSinceStartup >= deadline) { timedOutBecause = $"the restore never finished within {90f:F0}s"; break; } bool isRunning = IsRunning; if (isRunning) { clientDeadline = -1f; } else if (clientDeadline < 0f) { clientDeadline = Time.realtimeSinceStartup + 20f; } bool flag = false; if (!isRunning && waitForClients && (Object)(object)network != (Object)null) { try { flag = !network.AllClientsPresentationDone(); } catch { flag = false; } } if (!isRunning && !flag) { released = true; break; } if (!isRunning && Time.realtimeSinceStartup >= clientDeadline) { timedOutBecause = $"somebody still hadn't finished loading {20f:F0}s " + "after the host was back in control"; break; } NadirRisingField.Park(field); yield return null; } if ((Object)(object)field == (Object)null) { field = NadirRisingField.Find(_log); } if ((Object)(object)field == (Object)null) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogError((object)"OwnTeleportSequence: Nadir's rising field never turned up in this scene, so the biome is running without its hazard. Nothing else can start it - the commune, which already happened, is its only trigger."); } yield break; } NadirRisingField.Park(field); NadirRisingField.Release(field, _log); if (released) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)"OwnTeleportSequence: everyone has control, Nadir's rising field released."); } } else { ManualLogSource log5 = _log; if (log5 != null) { log5.LogWarning((object)("OwnTeleportSequence: gave up holding Nadir's rising field - " + timedOutBecause + ". Starting the hazard anyway rather than leaving the biome without its one mechanic.")); } } yield return ConfirmRisingFieldStarted(field); } private IEnumerator ConfirmRisingFieldStarted(LavaRising field) { float deadline = Time.realtimeSinceStartup + 15f; int rearms = 0; while (Time.realtimeSinceStartup < deadline) { if ((Object)(object)field == (Object)null) { yield break; } if (field.started) { if (rearms > 0) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)($"OwnTeleportSequence: Nadir's rising field is climbing (took {rearms} extra " + "attempt(s) to arm).")); } } yield break; } if (!NadirRisingField.IsArmed(field)) { rearms++; ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)"OwnTeleportSequence: something cleared Nadir's rising field right after it was started - arming it again."); } NadirRisingField.Release(field, _log); } yield return null; } if ((Object)(object)field != (Object)null && !field.started) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogError((object)("OwnTeleportSequence: Nadir's rising field still isn't climbing " + $"{15f:F0}s after it was started (armed=" + $"{NadirRisingField.IsArmed(field)}, fogEnabled={Ascents.fogEnabled}) - the biome is running " + "without its hazard.")); } } } private void TryCloseLingeringEndScreen() { try { EndScreen val = Object.FindFirstObjectByType(); if ((Object)(object)val != (Object)null && ((MenuWindow)val).isOpen) { AccessTools.Method(typeof(MenuWindow), "Close", (Type[])null, (Type[])null)?.Invoke(val, null); _entryPoints.Network?.CloseEndscreenOthers(); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnTeleportSequence: closing a lingering EndScreen failed (non-fatal): " + ex.Message)); } } } private IEnumerator TeleportToPosition(Vector3 pos) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Character.localCharacter == (Object)null) { yield break; } if (ReviveBeforeWarp(Character.localCharacter)) { for (int i = 0; i < 10; i++) { yield return (object)new WaitForFixedUpdate(); } if ((Object)(object)Character.localCharacter == (Object)null) { yield break; } } Vector3 warpPos = pos + new Vector3(0f, 0.5f, 0f); _entryPoints.Network?.Watchdog?.SetKnownTarget(warpPos); ((MonoBehaviourPun)Character.localCharacter).photonView.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { warpPos, false }); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)($"OwnTeleportSequence: warping the host to {warpPos} (broadcast to ALL machines, " + "so every client warps its own copy of the host rather than having the position sync drag that ragdoll across the map).")); } float startTime = Time.time; float lastSend = Time.time; int tried = 0; int framesToWait = Mathf.Max(1, _cfg.OwnTeleportFramesToWait.Value); int maxResends = Mathf.Max(0, _cfg.OwnMaxClientWarpResends.Value); float resendGrace = Mathf.Max(0f, _cfg.OwnClientWarpResendGraceSeconds.Value); bool arrived = false; for (int i = 0; i < framesToWait; i++) { yield return null; } while (Time.time - startTime < 30f && (Object)(object)Character.localCharacter != (Object)null) { if (Mathf.Abs(Character.localCharacter.Head.y - warpPos.y) > 3f) { if (Time.time - lastSend < resendGrace) { for (int i = 0; i < framesToWait; i++) { yield return null; } continue; } if (tried >= maxResends) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("OwnTeleportSequence: " + ((Object)Character.localCharacter.player).name + " still isn't at " + $"{warpPos} after {tried} re-warp(s); giving up on re-sending (the watchdog/position " + "recovery takes it from here) rather than spamming warps that abort each other.")); } break; } try { PhotonView photonView = ((MonoBehaviourPun)Character.localCharacter).photonView; if (photonView != null) { photonView.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { warpPos, false }); } _log.Trace($"OwnTeleportSequence: warped {((Object)Character.localCharacter.player).name} to {warpPos} " + $"(previous position: {Character.localCharacter.Head}, resend {tried + 1}/{maxResends})."); } catch (Exception arg) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)$"OwnTeleportSequence: TeleportToPosition warp failed: {arg}"); } } lastSend = Time.time; tried++; } else if (Mathf.Abs(Character.localCharacter.Head.x - warpPos.x) < 6f && Mathf.Abs(Character.localCharacter.Head.z - warpPos.z) < 6f) { _log.Trace($"OwnTeleportSequence: warped {((Object)Character.localCharacter.player).name} after {tried} attempts."); arrived = true; yield return (object)new WaitForSeconds(0.5f); if (!PhotonNetwork.OfflineMode) { _clientWarpSettled = false; ((MonoBehaviour)this).StartCoroutine(RunClientWarpAndSignal(warpPos)); } break; } for (int i = 0; i < framesToWait; i++) { yield return null; } } if (!arrived) { Character localCharacter = Character.localCharacter; string text = (((Object)(object)localCharacter != (Object)null) ? ((object)localCharacter.Head/*cast due to .constrained prefix*/).ToString() : "(no character)"); bool flag = (Object)(object)localCharacter != (Object)null && localCharacter.data.dead; ManualLogSource log4 = _log; if (log4 != null) { log4.LogError((object)($"OwnTeleportSequence: the HOST's own warp to {warpPos} never landed after " + $"{tried} attempt(s) / {Time.time - startTime:0.#}s - still at {text} (dead={flag}). " + "The restore runs against the wrong position from here, so the load will not complete properly. A character that is dead cannot be warped at all (see ReviveBeforeWarp).")); } } } private IEnumerator RunClientWarpAndSignal(Vector3 hostPos) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) try { yield return ((MonoBehaviour)this).StartCoroutine(TeleportClientsToHost(hostPos)); } finally { _clientWarpSettled = true; } } private void SyncLastRevivedSegment(Segment segment) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected I4, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected I4, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected I4, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) try { MapHandler instance = Singleton.Instance; if ((Object)(object)instance == (Object)null) { return; } MethodInfo methodInfo = AccessTools.PropertySetter(typeof(MapHandler), "LastRevivedSegment"); if (methodInfo != null) { methodInfo.Invoke(instance, new object[1] { (int)segment }); } else { FieldInfo fieldInfo = AccessTools.Field(typeof(MapHandler), "_lastRevivedSegment"); if (fieldInfo == null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"OwnTeleportSequence: could not find MapHandler.LastRevivedSegment; a late-joining client may be left dead at DeathPos for us to revive by hand."); } return; } fieldInfo.SetValue(instance, (int)segment); } _log.Trace($"OwnTeleportSequence: LastRevivedSegment set to {(int)segment} ({segment})."); } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("OwnTeleportSequence: could not sync LastRevivedSegment (" + ex.Message + "); a late-joining client may be left dead at DeathPos for us to revive by hand.")); } } } private bool ReviveDeadClientAtPosition(Character ch, Vector3 target) { //IL_0049: 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_00b1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ch == (Object)null || (Object)(object)((MonoBehaviourPun)ch).photonView == (Object)null || (Object)(object)ch.data == (Object)null || !ch.data.dead) { return false; } try { ((MonoBehaviourPun)ch).photonView.RPC("RPCA_ReviveAtPosition", (RpcTarget)0, new object[3] { target, false, -1 }); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)("OwnTeleportSequence: " + ch.characterName + " was dead before being warped " + $"(joined-late arrival kill, most likely) - body was at {ch.Head}. Revived AND placed at " + string.Format("{0} in one atomic {1}, so there is no 7km warp to fight.", target, "RPCA_ReviveAtPosition"))); } return true; } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("OwnTeleportSequence: atomic revive-at-position for " + ch.characterName + " failed (" + ex.Message + "); falling back to the separate revive + warp path.")); } return false; } } private bool ReviveBeforeWarp(Character ch) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ch == (Object)null || (Object)(object)((MonoBehaviourPun)ch).photonView == (Object)null) { return false; } if (!ch.data.dead) { return false; } try { ((MonoBehaviourPun)ch).photonView.RPC("ReviveCharacter", (RpcTarget)0, new object[1] { false }); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)("OwnTeleportSequence: " + ch.characterName + " was dead before being warped " + $"(joined-late arrival kill, most likely) - revived so the warp can land (body was at {ch.Head}). " + $"Letting it settle {10} physics steps before warping.")); } return true; } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("OwnTeleportSequence: could not revive " + ch.characterName + " before warping (" + ex.Message + "); the warp will probably not stick.")); } return false; } } private Vector3 SpreadTargetFor(Character ch, Vector3 hostPos) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) float num = ((_cfg != null) ? Mathf.Max(0f, _cfg.OwnClientWarpSpreadRadius.Value) : 0f); if (num <= 0f) { return hostPos; } int num3; try { PhotonView photonView = ((MonoBehaviourPun)ch).photonView; int? obj; if (photonView == null) { obj = null; } else { Player owner = photonView.Owner; obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null)); } int? num2 = obj; num3 = num2.GetValueOrDefault(); } catch { num3 = 0; } float num4 = (float)num3 * 137.5f * (MathF.PI / 180f); return hostPos + new Vector3(Mathf.Cos(num4) * num, 0f, Mathf.Sin(num4) * num); } private IEnumerator WaitForEveryPlayerToRegister() { float timeout = Mathf.Max(1f, (_cfg != null) ? _cfg.CoopReadyTimeout.Value : 30f); float started = Time.time; int expected = 0; int stableFrames = 0; while (Time.time - started < timeout) { stableFrames = (PlayerRegistration.AllRegistered(out var registered, out expected) ? (stableFrames + 1) : 0); if (stableFrames >= 5) { _log.Trace($"OwnTeleportSequence: all {registered}/{expected} player character(s) registered and " + $"stable for {5} frames after {Time.time - started:F1}s; safe to advance " + "the segment."); yield break; } yield return null; } ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnTeleportSequence: not every player had registered a character after " + $"{timeout:F0}s (expected {expected}); advancing the segment anyway. Anyone still spawning may " + "be killed on arrival by the game and warped back from DeathPos.")); } } private IEnumerator TeleportClientsToHost(Vector3 hostPos) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) int framesToWait = Mathf.Max(1, _cfg.OwnTeleportFramesToWait.Value); int maxResends = Mathf.Max(0, _cfg.OwnMaxClientWarpResends.Value); float resendGrace = Mathf.Max(0f, _cfg.OwnClientWarpResendGraceSeconds.Value); for (int i = 0; i < framesToWait; i++) { yield return null; } Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { Character ch = val.character; if ((Object)(object)ch == (Object)null || (Object)(object)ch == (Object)(object)Character.localCharacter) { continue; } Vector3 clientTarget = SpreadTargetFor(ch, hostPos); if (clientTarget != hostPos) { ManualLogSource log = _log; string[] obj = new string[5] { "OwnTeleportSequence.TeleportClientsToHost: ", ch.characterName, " lands at ", null, null }; object arg = clientTarget; Vector3 val2 = clientTarget - hostPos; obj[3] = $"{arg} ({((Vector3)(ref val2)).magnitude:F1}m off the host's own arrival "; obj[4] = "point, so the bodies do not arrive inside each other)."; log.Trace(string.Concat(obj)); } bool revivedAtPosition = ReviveDeadClientAtPosition(ch, clientTarget); if (revivedAtPosition) { for (int j = 0; j < 10; j++) { yield return (object)new WaitForFixedUpdate(); } if ((Object)(object)ch != (Object)null && (Object)(object)ch.data != (Object)null && ch.data.dead) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("OwnTeleportSequence: " + ch.characterName + " died AGAIN right after being revived (a late DeathOnArrival kill, most likely); re-placing them once more.")); } ReviveDeadClientAtPosition(ch, clientTarget); for (int j = 0; j < 10; j++) { yield return (object)new WaitForFixedUpdate(); } } } else if (ReviveBeforeWarp(ch)) { for (int j = 0; j < 10; j++) { yield return (object)new WaitForFixedUpdate(); } } if ((Object)(object)ch == (Object)null || (Object)(object)((MonoBehaviourPun)ch).photonView == (Object)null) { continue; } if (!revivedAtPosition) { ((MonoBehaviourPun)ch).photonView.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { clientTarget, false }); } float startTime = Time.time; float lastSend = Time.time; int tried = 0; bool arrived = false; while (Time.time - startTime < 30f) { if (Mathf.Abs(ch.Head.x - clientTarget.x) < 6f && Mathf.Abs(ch.Head.z - clientTarget.z) < 6f) { _log.Trace($"OwnTeleportSequence.TeleportClientsToHost: warped {((Object)ch.player).name} after {tried} attempts."); arrived = true; break; } if (Time.time - lastSend >= resendGrace) { if (tried >= maxResends) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)("OwnTeleportSequence.TeleportClientsToHost: still don't see " + ((Object)ch.player).name + " " + $"near {clientTarget} after {tried} re-warp(s); giving up (client's own watchdog/position " + "recovery will handle it) rather than spamming further warps.")); } break; } try { PhotonView photonView = ((MonoBehaviourPun)ch).photonView; if (photonView != null) { photonView.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { clientTarget, false }); } _log.Trace($"OwnTeleportSequence.TeleportClientsToHost: warped {((Object)ch.player).name} to {clientTarget} " + $"(previous position: {ch.Head}, resend {tried + 1}/{maxResends})."); } catch (Exception arg2) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogWarning((object)$"OwnTeleportSequence.TeleportClientsToHost failed: {arg2}"); } } lastSend = Time.time; tried++; } for (int j = 0; j < framesToWait; j++) { yield return null; } } if (arrived) { continue; } _clientWarpAllArrived = false; if (Time.time - startTime >= 30f) { ManualLogSource log5 = _log; if (log5 != null) { log5.LogWarning((object)("OwnTeleportSequence.TeleportClientsToHost: gave up waiting for " + ((Object)ch.player).name + " " + $"near {clientTarget} after a 30s timeout.")); } } } } } public class OwnWakeUpEffect : MonoBehaviour { private ManualLogSource _log; public void Init(ManualLogSource log) { _log = log; } public void Collapse() { Character val = ResolveCharacter("Collapse"); if (!((Object)(object)val == (Object)null)) { _log.Trace("OwnWakeUpEffect: collapsing into the passed-out pose."); SnapPassOut(val, value: true); } } public void RefreshHold() { Character val = null; try { val = Character.localCharacter; } catch { } if ((Object)(object)val != (Object)null && val.data.passedOut) { val.data.lastPassedOut = Time.time; } } public IEnumerator Wake(float standTime) { Character val = ResolveCharacter("Wake"); if ((Object)(object)val != (Object)null) { _log.Trace("OwnWakeUpEffect: waking up (starting the native stand-up recovery)."); SnapPassOut(val, value: false); } yield return (object)new WaitForSeconds(Mathf.Max(0f, standTime)); } private Character ResolveCharacter(string caller) { Character val = null; try { val = Character.localCharacter; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("OwnWakeUpEffect." + caller + ": could not read Character.localCharacter: " + ex.Message)); } } if ((Object)(object)val == (Object)null) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("OwnWakeUpEffect." + caller + ": Character.localCharacter is null; skipping.")); } } return val; } private void SnapPassOut(Character character, bool value) { try { CharacterCustomization val = character.refs?.customization; if ((Object)(object)val != (Object)null) { if (value) { val.CharacterPassedOut(); } else { val.OnRevive_RPC(); } } character.data.passedOut = value; if (value) { character.data.lastPassedOut = Time.time; } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)$"OwnWakeUpEffect.SnapPassOut({value}) failed (non-fatal): {ex.Message}"); } } } } public static class OwnWorldLootReset { private static readonly string[] LuggageResetMethodNames = new string[6] { "ResetRecievedData", "SetKinematicAndResetSyncData", "Reset", "Refresh", "Rebuild", "Invalidate" }; private static readonly string[] ChestResetMethodNames = new string[4] { "Reset", "Refresh", "Rebuild", "Invalidate" }; private static readonly string[] StaleSegmentObjectNames = new string[11] { "ChainShootable", "RopeAnchor", "RopeDynamic", "PortableStovetop_Placed", "ClimbingSpikeHammered", "CloudFungus", "Flag_Planted_Checkpoint", "ShelfShroom", "ScoutCannon_Placed", "BounceShroomSpawn", "MagicBean" }; public static void ResetWorldLoot(ManualLogSource log) { try { int num = 0; int num2 = 0; MonoBehaviour[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (MonoBehaviour val in array) { if (!((Object)(object)val == (Object)null)) { string name = ((object)val).GetType().Name; if (name.IndexOf("Luggage", StringComparison.OrdinalIgnoreCase) >= 0) { num++; ResetLootObjectGeneric(val, LuggageResetMethodNames); } else if (name.IndexOf("RespawnChest", StringComparison.OrdinalIgnoreCase) >= 0 || (name.IndexOf("Chest", StringComparison.OrdinalIgnoreCase) >= 0 && name.IndexOf("Respawn", StringComparison.OrdinalIgnoreCase) >= 0)) { num2++; ResetLootObjectGeneric(val, ChestResetMethodNames); } } } log.Trace($"OwnWorldLootReset: ResetWorldLoot touched luggage={num}, chests={num2}."); } catch (Exception arg) { if (log != null) { log.LogError((object)$"OwnWorldLootReset.ResetWorldLoot (luggage/chests) failed: {arg}"); } } try { int num3 = 0; int num4 = 0; PhotonView[] array2 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (PhotonView val2 in array2) { if ((Object)(object)val2 == (Object)null) { continue; } bool isRoomView; try { isRoomView = val2.IsRoomView; } catch { num4++; continue; } if (isRoomView) { continue; } GameObject gameObject = ((Component)val2).gameObject; if ((Object)(object)gameObject == (Object)null) { continue; } if ((Object)(object)gameObject.GetComponentInParent(true) != (Object)null || (Object)(object)gameObject.GetComponentInParent(true) != (Object)null) { num4++; continue; } if (!((Object)(object)gameObject.GetComponentInChildren(true) != (Object)null) || (!((Object)(object)gameObject.GetComponent("Item") != (Object)null) && !((Object)(object)gameObject.GetComponent("ItemPickup") != (Object)null) && ((Object)gameObject).name.IndexOf("Item", StringComparison.OrdinalIgnoreCase) < 0 && ((Object)gameObject).name.IndexOf("Pickup", StringComparison.OrdinalIgnoreCase) < 0 && ((Object)gameObject).name.IndexOf("Drop", StringComparison.OrdinalIgnoreCase) < 0)) { num4++; continue; } if (!PhotonNetwork.OfflineMode) { PhotonNetwork.Destroy(val2); } else { Object.Destroy((Object)(object)gameObject); } num3++; } log.Trace($"OwnWorldLootReset: ResetDroppedRuntimeItems destroyed={num3}, skipped={num4}."); } catch (Exception arg2) { if (log != null) { log.LogError((object)$"OwnWorldLootReset.ResetWorldLoot (dropped items) failed: {arg2}"); } } } public static void DestroyStaleWorldObjects(ManualLogSource log) { try { GameObject[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (GameObject val in array) { try { if ((Object)(object)val == (Object)null || ((Object)(object)val.transform.parent != (Object)null && (Object)(object)val.GetComponentInParent() != (Object)null)) { continue; } PhotonView component = val.GetComponent(); if ((Object)(object)component == (Object)null) { continue; } string[] staleSegmentObjectNames = StaleSegmentObjectNames; foreach (string value in staleSegmentObjectNames) { if (((Object)val).name.Contains(value) && component.CreatorActorNr > 0 && !component.IsRoomView) { PhotonNetwork.Destroy(val); break; } } } catch { } } } catch (Exception arg) { if (log != null) { log.LogError((object)$"OwnWorldLootReset.DestroyStaleWorldObjects (segment objects) failed: {arg}"); } } try { MagicBeanVine[] array2 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (MagicBeanVine val2 in array2) { if (!((Object)(object)val2 == (Object)null) && ((Object)(object)((Component)val2).transform.parent == (Object)null || (Object)(object)((Component)val2).GetComponentInParent() == (Object)null) && ((Object)val2).name.Contains("MagicBeanVine")) { Object.Destroy((Object)(object)((Component)val2).gameObject); } } } catch (Exception arg2) { if (log != null) { log.LogError((object)$"OwnWorldLootReset.DestroyStaleWorldObjects (MagicBeanVine) failed: {arg2}"); } } } public static void DestroyLeftoverHeldItems(ManualLogSource log) { try { Item[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Item val in array) { if (!((Object)(object)val == (Object)null) && (!((Object)(object)((Component)val).transform.parent != (Object)null) || !((Object)(object)((Component)val).GetComponentInParent() != (Object)null)) && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) && ((MonoBehaviourPun)val).photonView.IsMine && !((Object)val).name.Contains("Marshmallow") && !((Object)val).name.Contains("Glizzy")) { PhotonNetwork.Destroy(((MonoBehaviourPun)val).photonView); } } } catch (Exception arg) { if (log != null) { log.LogError((object)$"OwnWorldLootReset.DestroyLeftoverHeldItems failed: {arg}"); } } } private static void ResetLootObjectGeneric(MonoBehaviour obj, string[] methodNames) { TrySetBoolFieldOrProp(obj, "opened", value: false); TrySetBoolFieldOrProp(obj, "isOpened", value: false); TrySetBoolFieldOrProp(obj, "hasOpened", value: false); TrySetBoolFieldOrProp(obj, "looted", value: false); TrySetBoolFieldOrProp(obj, "isLooted", value: false); TrySetEnumToDefault(obj, "state"); TrySetEnumToDefault(obj, "luggageState"); foreach (string methodName in methodNames) { if (TryInvokeNoArg(obj, methodName)) { break; } } TrySetBoolFieldOrProp(obj, "canInteract", value: true); TrySetBoolFieldOrProp(obj, "interactable", value: true); } private static bool TryInvokeNoArg(object inst, string methodName) { try { MethodInfo method = inst.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null || method.GetParameters().Length != 0) { return false; } method.Invoke(inst, null); return true; } catch { return false; } } private static void TrySetBoolFieldOrProp(object inst, string name, bool value) { try { Type type = inst.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { field.SetValue(inst, value); return; } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite && property.PropertyType == typeof(bool)) { property.SetValue(inst, value); } } catch { } } private static void TrySetEnumToDefault(object inst, string name) { try { Type type = inst.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType.IsEnum) { field.SetValue(inst, Enum.ToObject(field.FieldType, 0)); return; } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite && property.PropertyType.IsEnum) { property.SetValue(inst, Enum.ToObject(property.PropertyType, 0)); } } catch { } } } internal enum ButtonLabel { Restart, ReturnToAirport } internal enum ConfirmDialog { Restart, ReturnToAirport } internal static class PauseMenuLocalization { private static readonly Dictionary _table = new Dictionary { [ButtonLabel.Restart] = new string[15] { "RESTART", "REDÉMARRER", "RIAVVIA", "NEUSTART", "REINICIAR", "REINICIAR", "REINICIAR", "ПЕРЕЗАПУСК", "ПЕРЕЗАПУСК", "重新开始", "重新開始", "リスタート", "재시작", "RESTART", "YENİDEN BAŞLAT" }, [ButtonLabel.ReturnToAirport] = new string[15] { "RETURN TO AIRPORT", "RETOUR À L'AÉROPORT", "TORNA ALL'AEROPORTO", "ZURÜCK ZUM FLUGHAFEN", "VOLVER AL AEROPUERTO", "VOLVER AL AEROPUERTO", "VOLTAR AO AEROPORTO", "ВЕРНУТЬСЯ В АЭРОПОРТ", "ПОВЕРНУТИСЯ В АЕРОПОРТ", "返回机场", "返回機場", "空港に戻る", "공항으로 돌아가기", "WRÓĆ NA LOTNISKO", "HAVAALANINA DÖN" } }; private static readonly Dictionary _dialogTable = new Dictionary { [ConfirmDialog.Restart] = new string[15] { "Restart this run? Everyone will return to the Airport and a fresh run of the same difficulty will start immediately (no checkpoint will be loaded).", "Recommencer cette partie ? Tout le monde retournera à l'aéroport et une nouvelle partie de la même difficulté commencera immédiatement (aucune sauvegarde ne sera chargée).", "Riavviare questa partita? Tutti torneranno all'aeroporto e una nuova partita della stessa difficoltà inizierà immediatamente (nessun checkpoint verrà caricato).", "Diesen Lauf neu starten? Alle kehren zum Flughafen zurück und ein neuer Lauf mit demselben Schwierigkeitsgrad beginnt sofort (es wird kein Speicherstand geladen).", "¿Reiniciar esta partida? Todos volverán al aeropuerto y comenzará de inmediato una nueva partida de la misma dificultad (no se cargará ningún punto de guardado).", "¿Reiniciar esta partida? Todos volverán al aeropuerto y comenzará de inmediato una nueva partida de la misma dificultad (no se cargará ningún punto de guardado).", "Reiniciar esta corrida? Todos vão voltar para o Aeroporto e uma nova corrida da mesma dificuldade vai começar imediatamente (nenhum checkpoint será carregado).", "Перезапустить этот забег? Все вернутся в аэропорт, и сразу начнётся новый забег той же сложности (сохранение не будет загружено).", "Перезапустити цей забіг? Усі повернуться в аеропорт, і одразу почнеться новий забіг тієї ж складності (збереження не буде завантажено).", "重新开始本局游戏?所有人将返回机场,并立即开始相同难度的新一局(不会加载任何存档)。", "要重新開始這一局嗎?所有人將返回機場,並立即開始相同難度的新一局(不會載入任何存檔)。", "このランを再開始しますか?全員が空港に戻り、同じ難易度の新しいランがすぐに始まります(チェックポイントは読み込まれません)。", "이번 런을 재시작하시겠습니까? 모두 공항으로 돌아가고 동일한 난이도의 새로운 런이 즉시 시작됩니다 (체크포인트는 불러오지 않습니다).", "Zrestartować ten przebieg? Wszyscy wrócą na lotnisko i natychmiast rozpocznie się nowy przebieg tej samej trudności (żaden zapis nie zostanie wczytany).", "Bu koşuyu yeniden başlat? Herkes Havaalanı'na dönecek ve aynı zorlukta yeni bir koşu hemen başlayacak (herhangi bir kayıt yüklenmeyecek)." }, [ConfirmDialog.ReturnToAirport] = new string[15] { "Return everyone to the Airport now?", "Renvoyer tout le monde à l'aéroport maintenant ?", "Riportare tutti all'aeroporto ora?", "Jetzt alle zum Flughafen zurückbringen?", "¿Volver todos al aeropuerto ahora?", "¿Volver todos al aeropuerto ahora?", "Voltar todos para o Aeroporto agora?", "Вернуть всех в аэропорт сейчас?", "Повернути всіх в аеропорт зараз?", "现在让所有人返回机场?", "現在讓所有人返回機場嗎?", "今すぐ全員を空港に戻しますか?", "지금 모두를 공항으로 돌려보내시겠습니까?", "Odesłać teraz wszystkich na lotnisko?", "Herkes şimdi Havaalanı'na döndürülsün mü?" } }; public static string Get(ButtonLabel label) { return Resolve(_table[label]); } public static string Get(ConfirmDialog dialog) { return Resolve(_dialogTable[dialog]); } private static string Resolve(string[] arr) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected I4, but got Unknown int num = (int)LocalizedText.CURRENT_LANGUAGE; if (num >= 0 && num < arr.Length && !string.IsNullOrEmpty(arr[num])) { return arr[num]; } return arr[0]; } } public static class PauseMenuPatch { private class ButtonEntry { public GameObject GameObject; public TextMeshProUGUI Text; public Func Localize; } private class Buttons { public ButtonEntry Restart; public ButtonEntry ReturnToAirport; public ButtonEntry OpenKiosk; } private static ManualLogSource _log; private static PluginConfig _cfg; private static MethodInfo _windowOpen; private static MethodInfo _windowClose; private static FieldInfo _templateButtonField; private static FieldInfo _quitButtonField; private static FieldInfo _confirmOkField; private static FieldInfo _confirmCancelField; private static readonly ConditionalWeakTable _built = new ConditionalWeakTable(); public static void Apply(Harmony harmony, PluginConfig cfg, ManualLogSource log) { //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Expected O, but got Unknown _log = log; _cfg = cfg; try { _windowOpen = AccessTools.Method(typeof(MenuWindow), "Open", (Type[])null, (Type[])null); _windowClose = AccessTools.Method(typeof(MenuWindow), "Close", (Type[])null, (Type[])null); _templateButtonField = AccessTools.Field(typeof(PauseMenuMainPage), "m_accoladesButton"); _quitButtonField = AccessTools.Field(typeof(PauseMenuMainPage), "m_quitButton"); _confirmOkField = AccessTools.Field(typeof(PauseMenuMainPage), "m_confirmOkButton"); _confirmCancelField = AccessTools.Field(typeof(PauseMenuMainPage), "m_confirmCancelButton"); if (_windowOpen == null || _windowClose == null || _templateButtonField == null || _quitButtonField == null || _confirmOkField == null || _confirmCancelField == null) { log.LogWarning((object)"PauseMenuPatch: one or more pause menu members not found; Restart / Return to Airport / Board Flight buttons will not be added. The pause menu itself is unaffected."); return; } MethodInfo methodInfo = AccessTools.Method(typeof(PauseMenuMainPage), "Start", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(PauseMenuMainPage), "OnEnable", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PauseMenuPatch), "StartPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(PauseMenuPatch), "OnEnablePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"PauseMenuPatch: patched PauseMenuMainPage.Start/OnEnable (Restart / Return to Airport / Board Flight buttons)."); } catch (Exception arg) { log.LogError((object)$"PauseMenuPatch.Apply failed (non-fatal, pause menu unaffected): {arg}"); } } private static void StartPostfix(PauseMenuMainPage __instance) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown try { if (_built.TryGetValue(__instance, out var _)) { return; } Button val = (Button)_templateButtonField.GetValue(__instance); Button val2 = (Button)_quitButtonField.GetValue(__instance); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { _log.LogWarning((object)"PauseMenuPatch: template/anchor button missing on this instance; skipping."); return; } Transform parent = ((Component)val).transform.parent; int siblingIndex = ((Component)val2).transform.GetSiblingIndex(); Buttons buttons = new Buttons { Restart = MakeButton(val, parent, siblingIndex++, (Func)(() => PauseMenuLocalization.Get(ButtonLabel.Restart)), (UnityAction)delegate { OnRestartClicked(__instance); }, (Color?)new Color(0.8f, 0.2f, 0.15f)), ReturnToAirport = MakeButton(val, parent, siblingIndex++, (Func)(() => PauseMenuLocalization.Get(ButtonLabel.ReturnToAirport)), (UnityAction)delegate { OnReturnToAirportClicked(__instance); }, (Color?)new Color(0.12f, 0.55f, 0.58f)), OpenKiosk = MakeButton(val, parent, siblingIndex++, (Func)(() => LocalizedText.GetText("BOARDFLIGHT", true).ToUpperInvariant()), (UnityAction)delegate { OnOpenKioskClicked(__instance); }, (Color?)new Color(0.12f, 0.55f, 0.58f)) }; _built.Add(__instance, buttons); RectTransform val3 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val3 != null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val3); } UpdateVisibility(buttons); } catch (Exception arg) { _log.LogError((object)$"PauseMenuPatch.StartPostfix failed (non-fatal): {arg}"); } } private static void OnEnablePostfix(PauseMenuMainPage __instance) { try { if (_built.TryGetValue(__instance, out var value)) { UpdateVisibility(value); } } catch (Exception arg) { _log.LogError((object)$"PauseMenuPatch.OnEnablePostfix failed (non-fatal): {arg}"); } } private static void UpdateVisibility(Buttons b) { bool flag = RunLauncher.IsHost && RunLauncher.InLevel; SetActiveAndRefresh(b.Restart, flag && _cfg.ShowRestartButton.Value); SetActiveAndRefresh(b.ReturnToAirport, flag && _cfg.ShowReturnToAirportButton.Value); SetActiveAndRefresh(b.OpenKiosk, RunLauncher.InAirport && _cfg.ShowBoardFlightButton.Value); } private static void SetActiveAndRefresh(ButtonEntry entry, bool active) { entry.GameObject.SetActive(active); if (active && (Object)(object)entry.Text != (Object)null) { ((TMP_Text)entry.Text).text = entry.Localize(); } } private static ButtonEntry MakeButton(Button template, Transform parent, int siblingIndex, Func localize, UnityAction onClick, Color? bannerColor = null) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: 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) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) string text = localize(); GameObject val = Object.Instantiate(((Component)template).gameObject, parent); ((Object)val).name = "PEAKQuickResume_" + text.Replace(" ", ""); val.transform.SetSiblingIndex(siblingIndex); Button component = val.GetComponent