Decompiled source of PEAK Quick Resume v2.3.0
PEAKQuickResume.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.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<AchievementManager>.Instance; if (instance != null) { instance.GetSteamStatInt((STEAMSTATTYPE)5, ref __state); } } catch { } } private static void RecordMaxHeight_Postfix(int meters, int __state) { try { AchievementManager instance = Singleton<AchievementManager>.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<AchievementManager>.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<AchievementManager>.Instance == (Object)null || AnyFieldMissing()) { return (fruits: 0, shroomBerries: 0, nonToxicMushrooms: 0, gourmand: 0); } object obj = Singleton<AchievementManager>.Instance.runBasedValueData; int item = (FFruits.GetValue(obj) as List<ushort>)?.Count ?? 0; int item2 = (FShroomBerries.GetValue(obj) as List<ushort>)?.Count ?? 0; int item3 = (FNonToxicMushrooms.GetValue(obj) as List<ushort>)?.Count ?? 0; int item4 = (FGourmand.GetValue(obj) as List<ushort>)?.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<AchievementManager>.Instance == (Object)null) { return null; } return ToSaved(Singleton<AchievementManager>.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<RUNBASEDVALUETYPE, int> dictionary) { foreach (KeyValuePair<RUNBASEDVALUETYPE, int> item in dictionary) { ownSavedAchievementProgress.runBasedInts[(int)item.Key] = item.Value; } } if (FRunBasedFloats.GetValue(boxedNative) is Dictionary<RUNBASEDVALUETYPE, float> dictionary2) { foreach (KeyValuePair<RUNBASEDVALUETYPE, float> item2 in dictionary2) { ownSavedAchievementProgress.runBasedFloats[(int)item2.Key] = item2.Value; } } if (FFruits.GetValue(boxedNative) is List<ushort> collection) { ownSavedAchievementProgress.runBasedFruitsEaten = new List<ushort>(collection); } if (FShroomBerries.GetValue(boxedNative) is List<ushort> collection2) { ownSavedAchievementProgress.shroomBerriesEaten = new List<ushort>(collection2); } if (FNonToxicMushrooms.GetValue(boxedNative) is List<ushort> collection3) { ownSavedAchievementProgress.nonToxicMushroomsEaten = new List<ushort>(collection3); } if (FGourmand.GetValue(boxedNative) is List<ushort> collection4) { ownSavedAchievementProgress.gourmandRequirementsEaten = new List<ushort>(collection4); } if (FEarnedThisRun.GetValue(boxedNative) is List<ACHIEVEMENTTYPE> source) { ownSavedAchievementProgress.achievementsEarnedThisRun = source.Select((ACHIEVEMENTTYPE a) => (int)a).ToList(); } if (FCompletedAscents.GetValue(boxedNative) is List<int> collection5) { ownSavedAchievementProgress.completedAscentsThisRun = new List<int>(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<AchievementManager>.Instance == (Object)null) { return; } object obj = SerializableRunBasedValues.ConstructNew(); if (saved != null && !AnyFieldMissing()) { Dictionary<RUNBASEDVALUETYPE, int> dictionary = new Dictionary<RUNBASEDVALUETYPE, int>(); if (saved.runBasedInts != null) { foreach (KeyValuePair<int, int> runBasedInt in saved.runBasedInts) { if (runBasedInt.Key != 9) { dictionary[(RUNBASEDVALUETYPE)runBasedInt.Key] = runBasedInt.Value; } } } FRunBasedInts.SetValue(obj, dictionary); Dictionary<RUNBASEDVALUETYPE, float> dictionary2 = new Dictionary<RUNBASEDVALUETYPE, float>(); if (saved.runBasedFloats != null) { foreach (KeyValuePair<int, float> runBasedFloat in saved.runBasedFloats) { dictionary2[(RUNBASEDVALUETYPE)runBasedFloat.Key] = runBasedFloat.Value; } } FRunBasedFloats.SetValue(obj, dictionary2); FFruits.SetValue(obj, new List<ushort>(saved.runBasedFruitsEaten ?? new List<ushort>())); FShroomBerries.SetValue(obj, new List<ushort>(saved.shroomBerriesEaten ?? new List<ushort>())); FNonToxicMushrooms.SetValue(obj, new List<ushort>(saved.nonToxicMushroomsEaten ?? new List<ushort>())); FGourmand.SetValue(obj, new List<ushort>(saved.gourmandRequirementsEaten ?? new List<ushort>())); List<ACHIEVEMENTTYPE> value = (saved.achievementsEarnedThisRun ?? new List<int>()).Select((int i) => (ACHIEVEMENTTYPE)i).ToList(); FEarnedThisRun.SetValue(obj, value); FCompletedAscents.SetValue(obj, new List<int>(saved.completedAscentsThisRun ?? new List<int>())); } Singleton<AchievementManager>.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<int, int> kv) => $"{(object)(RUNBASEDVALUETYPE)kv.Key}={kv.Value}")) : "(none)"); string text2 = ((saved.runBasedFloats != null && saved.runBasedFloats.Count > 0) ? string.Join(", ", saved.runBasedFloats.Select((KeyValuePair<int, float> 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<int>()) + "]"; } 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<OwnSavedAchievementProgress>(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<string> alreadyRestored = null) { try { bool offline = selection.Offline; Player[] array = Object.FindObjectsByType<Player>((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<PhotonView>(); OwnSavedAchievementProgress saved = null; if (selection.TryGetPlayerFile(text, out var path) && File.Exists(path)) { try { saved = JsonConvert.DeserializeObject<OwnSaveData>(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<Delegate> list = new List<Delegate>(); 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<Item> 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<string, OwnItemStateEntry> 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<PhotonView>(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<RespawnChest>((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<MountainProgressHandler>.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<string, TrackedDrop> _tracked = new Dictionary<string, TrackedDrop>(); private static readonly List<PendingRestore> _pending = new List<PendingRestore>(); 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<int> GetPendingBackpackViewIds() { HashSet<int> hashSet = new HashSet<int>(); 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<BackpackData>((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<string, OwnItemStateEntry> 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<string, OwnItemStateEntry> 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<Campfire>((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<Backpack>((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<ushort, Item> dictionary = SingletonAsset<ItemDatabase>.Instance?.itemLookup; if (dictionary != null) { foreach (KeyValuePair<ushort, Item> 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<string, OwnSavedEntry> values, ManualLogSource log) { if ((Object)(object)fuelSource == (Object)null || values == null || values.ContainsKey("Fuel")) { return; } try { JetpackItem component = ((Component)fuelSource).GetComponent<JetpackItem>(); if (!((Object)(object)component == (Object)null)) { FloatItemData val = default(FloatItemData); float fuel; string arg; if (data != null && data.TryGetDataEntry<FloatItemData>((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<Campfire>((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<ScoutmasterSoulPillar>((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<Player>(true) != (Object)null) { return false; } if ((Object)(object)((Component)item).GetComponentInParent<Character>(true) != (Object)null) { return false; } return true; } public static Item FindNearestFreeItem(Vector3 pos, float radius, HashSet<Item> 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<Item>((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<Item> FindFreeItemsWithin(Vector3 pos, float radius, bool includeBackpacks = false, HashSet<Item> 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<Item>((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<string, OwnSavedEntry> 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<string, OwnSavedEntry> 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<PhotonView>(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<string, OwnSavedEntry> 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<string> ResolveSavedDeadUserIds(SaveSelection selection, ManualLogSource log) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if (selection == null || selection.Offline || !PhotonNetwork.IsMasterClient) { return hashSet; } try { int num = 0; Player[] array = Object.FindObjectsByType<Player>((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<string> deadUserIds, OwnLoadEntryPoints entryPoints, ManualLogSource log) { if (deadUserIds == null || deadUserIds.Count == 0 || !PhotonNetwork.IsMasterClient) { return; } try { Player[] array = Object.FindObjectsByType<Player>((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<object>()); 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<string> deadUserIds, ManualLogSource log) { if (!PhotonNetwork.IsMasterClient || PhotonNetwork.OfflineMode) { return; } try { Player[] array = Object.FindObjectsByType<Player>((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<PhotonView>(); 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<OwnSaveData>(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<OwnSavedDeployableState> 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<OwnSavedDeployableState> 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<OwnSavedDeployableState> 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<OwnSavedDeployableState>(); try { Vector3 val = CampfireAreaHelpers.ResolveNearestCampfirePos(fallbackPos); float num = NadirSearchRadius.ForCurrentSegment(30f); List<PhotonView> 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<OwnSavedDeployableState> 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<PhotonView> 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<PhotonView> list = new List<PhotonView>(); PhotonView[] array = Object.FindObjectsByType<PhotonView>((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<AchievementManager>.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<Canvas>(); 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<Image>()).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 i