using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Photon.Pun; using REPO_Instant_Save.Config; using REPO_Instant_Save.Hotkeys; using REPO_Instant_Save.Save; using REPO_Instant_Save.Util; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("REPO-Instant-Save")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+32d9fb03e1bc727c83679a38981610700a1b5eec")] [assembly: AssemblyProduct("REPO-Instant-Save")] [assembly: AssemblyTitle("REPO-Instant-Save")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 REPO_Instant_Save { internal static class ModInfo { public const string Guid = "Ashx87.REPOInstantSave"; public const string Name = "REPO Instant Save"; public const string Version = "1.0.0"; } [BepInPlugin("Ashx87.REPOInstantSave", "REPO Instant Save", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("Ashx87.REPOInstantSave"); internal static Plugin? Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal PluginConfig ModConfig { get; private set; } private void Awake() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"REPO Instant Save v1.0.0 initialising…"); try { ModConfig = new PluginConfig(((BaseUnityPlugin)this).Config); _harmony.PatchAll(); ((Component)this).gameObject.AddComponent(); Log.LogInfo((object)string.Format("{0} loaded successfully. Full-save key: {1}.", "REPO Instant Save", ModConfig.FullSaveKey.Value)); } catch (Exception arg) { Log.LogError((object)string.Format("{0} failed to initialise: {1}", "REPO Instant Save", arg)); } } } } namespace REPO_Instant_Save.Util { internal static class GameState { public static bool IsHost => SemiFunc.IsMasterClientOrSingleplayer(); public static bool InRunLevel => SemiFunc.RunIsLevel(); public static bool StatsReady => (Object)(object)StatsManager.instance != (Object)null; } internal static class HudNotify { private static readonly Color SuccessColor = new Color(0.45f, 1f, 0.45f); private static readonly Color FailureColor = new Color(1f, 0.45f, 0.45f); public static void Toast(string message, bool success) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)MissionUI.instance == (Object)null)) { SemiFunc.UIFocusText(message, success ? SuccessColor : FailureColor, Color.white, 3f); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("HUD toast failed: " + ex.Message)); } } } } namespace REPO_Instant_Save.Save { internal static class CrossSession { public static WorldSnapshot? Pending { get; private set; } public static bool RebuildActive { get; set; } public static Level? TargetLevel { get; private set; } public static void Arm(WorldSnapshot snapshot) { Pending = snapshot; RebuildActive = false; TargetLevel = FindLevel(snapshot.levelName); Plugin.Log.LogInfo((object)("Cross-session restore armed for '" + snapshot.levelName + "' " + $"(grid {snapshot.grid.tiles.Count} tiles, {snapshot.level.Count} level objects); " + "target level " + (((Object)(object)TargetLevel != (Object)null) ? "found" : "NOT FOUND") + ".")); } private static Level? FindLevel(string levelName) { RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || instance.levels == null) { return null; } foreach (Level level in instance.levels) { if ((Object)(object)level != (Object)null && ((Object)level).name == levelName) { return level; } } return null; } public static void Clear() { Pending = null; RebuildActive = false; TargetLevel = null; } public static bool ShouldRebuildCurrentLevel() { if (Pending == null || !Pending.grid.present) { return false; } Level val = (((Object)(object)RunManager.instance != (Object)null) ? RunManager.instance.levelCurrent : null); if ((Object)(object)val != (Object)null) { return ((Object)val).name == Pending.levelName; } return false; } } internal static class InstantSaveService { public static bool SaveNow() { if (!GameState.IsHost) { Plugin.Log.LogWarning((object)"Instant Save ignored: only the host can save."); return false; } if (!GameState.InRunLevel) { Plugin.Log.LogWarning((object)"Instant Save ignored: you must be inside a level."); return false; } if (!GameState.StatsReady) { Plugin.Log.LogWarning((object)"Instant Save ignored: StatsManager not ready."); return false; } try { WorldSnapshot worldSnapshot = WorldCapture.Capture(); StatsManager.instance.SaveFileSave(); string saveFileCurrent = StatsManager.instance.saveFileCurrent; InstantSaveStore.Write(saveFileCurrent, worldSnapshot); Plugin.Log.LogInfo((object)("Instant Save captured for '" + saveFileCurrent + "' [" + worldSnapshot.levelName + "]: " + $"{worldSnapshot.level.Count} level objects, {worldSnapshot.valuables.Count} valuables, " + $"{worldSnapshot.extraction.Count} extraction points, {worldSnapshot.players.Count} players.")); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"Instant Save failed: {arg}"); return false; } } } internal static class InstantSaveStore { private const string FileName = "instantsave.json"; public static string FolderFor(string saveName) { return Path.Combine(Application.persistentDataPath, "saves", saveName); } public static string PathFor(string saveName) { return Path.Combine(FolderFor(saveName), "instantsave.json"); } public static void Write(string saveName, WorldSnapshot snapshot) { Directory.CreateDirectory(FolderFor(saveName)); string contents = JsonConvert.SerializeObject((object)snapshot, (Formatting)1); File.WriteAllText(PathFor(saveName), contents); } public static WorldSnapshot? Read(string saveName) { string path = PathFor(saveName); if (!File.Exists(path)) { return null; } return JsonConvert.DeserializeObject(File.ReadAllText(path)); } public static bool Exists(string saveName) { return File.Exists(PathFor(saveName)); } public static void Delete(string saveName) { string path = PathFor(saveName); if (File.Exists(path)) { File.Delete(path); } } } internal static class LevelRebuild { private static Dictionary _modulePrefabs = new Dictionary(); private static void BuildPrefabMap(LevelGenerator lg) { _modulePrefabs = new Dictionary(); Level level = lg.Level; if (!((Object)(object)level == (Object)null)) { AddAll(level.StartRooms); AddAll(level.ModulesNormal1); AddAll(level.ModulesNormal2); AddAll(level.ModulesNormal3); AddAll(level.ModulesPassage1); AddAll(level.ModulesPassage2); AddAll(level.ModulesPassage3); AddAll(level.ModulesDeadEnd1); AddAll(level.ModulesDeadEnd2); AddAll(level.ModulesDeadEnd3); AddAll(level.ModulesExtraction1); AddAll(level.ModulesExtraction2); AddAll(level.ModulesExtraction3); AddAll(level.ModulesSpecial); } } private static void AddAll(List list) { if (list == null) { return; } foreach (PrefabRef item in list) { if (item != null && !((Object)(object)((PrefabRef)(object)item).Prefab == (Object)null)) { string key = Clean(((Object)((PrefabRef)(object)item).Prefab).name); if (!_modulePrefabs.ContainsKey(key)) { _modulePrefabs[key] = item; } } } } public static IEnumerator RebuildTiles(LevelGenerator lg, GridDto grid) { lg.waitingForSubCoroutine = true; BuildPrefabMap(lg); lg.LevelWidth = grid.width; lg.LevelHeight = grid.height; lg.LevelGrid = new Tile[grid.width, grid.height]; for (int i = 0; i < grid.width; i++) { for (int j = 0; j < grid.height; j++) { lg.LevelGrid[i, j] = new Tile { x = i, y = j, active = false }; } } foreach (TileDto tile in grid.tiles) { if (tile.x >= 0 && tile.y >= 0 && tile.x < grid.width && tile.y < grid.height) { lg.LevelGrid[tile.x, tile.y] = new Tile { x = tile.x, y = tile.y, active = tile.active, first = tile.first, connections = tile.connections, connectedTop = tile.cTop, connectedRight = tile.cRight, connectedBot = tile.cBot, connectedLeft = tile.cLeft, type = (Type)tile.type }; } } lg.ModuleAmount = grid.tiles.Count; Plugin.Log.LogInfo((object)$"Rebuild: grid restored ({grid.tiles.Count} active tiles, ModuleAmount={lg.ModuleAmount})."); yield return null; lg.waitingForSubCoroutine = false; } public static IEnumerator RebuildStartRoom(LevelGenerator lg, WorldSnapshot snap) { lg.waitingForSubCoroutine = true; SceneObjectDto sceneObjectDto = snap.level.FirstOrDefault((SceneObjectDto o) => o.kind == "module" && o.name.StartsWith("Start Room", StringComparison.Ordinal)); if (sceneObjectDto != null && _modulePrefabs.TryGetValue(sceneObjectDto.name, out PrefabRef value) && value != null) { GameObject val = Spawn(value, Vector3.zero, Quaternion.identity); if ((Object)(object)val != (Object)null) { val.transform.parent = lg.LevelParent.transform; } } else { Plugin.Log.LogWarning((object)("Rebuild: start room prefab '" + sceneObjectDto?.name + "' not found.")); } yield return null; lg.waitingForSubCoroutine = false; } public static bool SpawnSavedModule(LevelGenerator lg, int x, int y, WorldSnapshot snap) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) SceneObjectDto sceneObjectDto = snap.level.FirstOrDefault((SceneObjectDto o) => o.kind == "module" && o.gridX == x && o.gridY == y && !o.name.StartsWith("Start Room", StringComparison.Ordinal)); if (sceneObjectDto == null) { return false; } if (!_modulePrefabs.TryGetValue(sceneObjectDto.name, out PrefabRef value) || value == null) { Plugin.Log.LogWarning((object)$"Rebuild: module '{sceneObjectDto.name}' at ({x},{y}) prefab not found."); return false; } GameObject val = Spawn(value, sceneObjectDto.pos.ToVector3(), Quaternion.Euler(sceneObjectDto.euler.ToVector3())); if ((Object)(object)val == (Object)null) { return false; } val.transform.parent = lg.LevelParent.transform; Module component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.GridX = x; component.GridY = y; bool first = lg.LevelGrid[x, y].first; bool flag = lg.GridCheckActive(x, y + 1); bool flag2 = lg.GridCheckActive(x, y - 1) || first; bool flag3 = lg.GridCheckActive(x + 1, y); bool flag4 = lg.GridCheckActive(x - 1, y); component.ModuleConnectionSet(flag, flag2, flag3, flag4, first); } return true; } private static GameObject Spawn(PrefabRef pr, Vector3 pos, Quaternion rot) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (GameManager.instance.gameMode == 0) { return Object.Instantiate(((PrefabRef)(object)pr).Prefab, pos, rot); } return PhotonNetwork.InstantiateRoomObject(((PrefabRef)(object)pr).ResourcePath, pos, rot, (byte)0, (object[])null); } private static string Clean(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(); } } internal static class ProgressionSaver { public static bool SaveNow() { if (!GameState.IsHost) { Plugin.Log.LogWarning((object)"Save ignored: only the host can save the run."); return false; } if (!GameState.StatsReady) { Plugin.Log.LogWarning((object)"Save ignored: StatsManager is not ready yet."); return false; } try { StatsManager.instance.SaveFileSave(); Plugin.Log.LogInfo((object)("Run saved to '" + StatsManager.instance.saveFileCurrent + "'.")); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to save run: {arg}"); return false; } } } internal static class RestoreService { public static bool RestoreNow() { if (!GameState.IsHost) { Plugin.Log.LogWarning((object)"Restore ignored: only the host can load."); return false; } if (!GameState.StatsReady) { Plugin.Log.LogWarning((object)"Restore ignored: StatsManager not ready."); return false; } string saveFileCurrent = StatsManager.instance.saveFileCurrent; WorldSnapshot worldSnapshot = InstantSaveStore.Read(saveFileCurrent); if (worldSnapshot == null) { Plugin.Log.LogWarning((object)("No Instant Save snapshot found for '" + saveFileCurrent + "'.")); return false; } try { if ((Object)(object)Plugin.Instance == (Object)null) { Plugin.Log.LogWarning((object)"Restore ignored: plugin instance not ready."); return false; } ((MonoBehaviour)Plugin.Instance).StartCoroutine(WorldRestore.RestoreRoutine(worldSnapshot)); Plugin.Log.LogInfo((object)("Instant Save restore started from '" + saveFileCurrent + "' [" + worldSnapshot.levelName + "].")); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"Instant Save restore failed: {arg}"); return false; } } } public sealed class WorldSnapshot { public int version { get; set; } = 1; public string levelName { get; set; } = ""; public long takenUtcTicks { get; set; } public int? runLevel { get; set; } public List level { get; set; } = new List(); public List valuables { get; set; } = new List(); public List haulers { get; set; } = new List(); public List extraction { get; set; } = new List(); public List players { get; set; } = new List(); public RoundDto round { get; set; } = new RoundDto(); public List hinges { get; set; } = new List(); public GridDto grid { get; set; } = new GridDto(); } public sealed class SceneObjectDto { public string name { get; set; } = ""; public string kind { get; set; } = "other"; public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public int gridX { get; set; } = -1; public int gridY { get; set; } = -1; public int moduleType { get; set; } = -1; } public sealed class GridDto { public bool present { get; set; } public int width { get; set; } public int height { get; set; } public List tiles { get; set; } = new List(); } public sealed class TileDto { public int x { get; set; } public int y { get; set; } public bool active { get; set; } public bool first { get; set; } public int connections { get; set; } public bool cTop { get; set; } public bool cRight { get; set; } public bool cBot { get; set; } public bool cLeft { get; set; } public int type { get; set; } } public sealed class ValuableDto { public string name { get; set; } = ""; public string kind { get; set; } = "valuable"; public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public float value { get; set; } public string? inCart { get; set; } public Vec3Dto? inCartPos { get; set; } public Vec3Dto? inCartEuler { get; set; } } public sealed class HaulerDto { public string name { get; set; } = ""; public Vec3Dto pos { get; set; } = new Vec3Dto(); public float value { get; set; } } public sealed class ExtractionDto { public string name { get; set; } = ""; public Vec3Dto pos { get; set; } = new Vec3Dto(); public int haulCurrent { get; set; } public int haulGoal { get; set; } public int state { get; set; } } public sealed class RoundDto { public bool present { get; set; } public int currentHaul { get; set; } public int totalHaul { get; set; } public int currentHaulMax { get; set; } public int haulGoal { get; set; } public int haulGoalMax { get; set; } public int extractionPoints { get; set; } public int extractionPointSurplus { get; set; } public int extractionPointsCompleted { get; set; } public int extractionHaulGoal { get; set; } public bool extractionPointActive { get; set; } public bool allExtractionPointsCompleted { get; set; } } public sealed class HingeDto { public string name { get; set; } = ""; public Vec3Dto pivot { get; set; } = new Vec3Dto(); public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public bool closed { get; set; } public bool broken { get; set; } public bool dead { get; set; } } public sealed class PlayerDto { public string steamId { get; set; } = ""; public string name { get; set; } = ""; public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public int health { get; set; } } public sealed class Vec3Dto { public float x { get; set; } public float y { get; set; } public float z { get; set; } public Vec3Dto() { } public Vec3Dto(Vector3 v) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) x = v.x; y = v.y; z = v.z; } public Vector3 ToVector3() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(x, y, z); } } internal static class WorldCapture { public static WorldSnapshot Capture() { WorldSnapshot obj = new WorldSnapshot { levelName = (((Object)(object)RunManager.instance != (Object)null && (Object)(object)RunManager.instance.levelCurrent != (Object)null) ? ((Object)RunManager.instance.levelCurrent).name : "?"), takenUtcTicks = DateTime.UtcNow.Ticks, runLevel = (((Object)(object)StatsManager.instance != (Object)null) ? new int?(StatsManager.instance.GetRunStatLevel()) : ((int?)null)) }; CaptureLevelObjects(obj); CaptureGrid(obj); CaptureGrabbables(obj); CaptureHaulers(obj); CaptureExtraction(obj); CaptureRound(obj); CaptureHinges(obj); CapturePlayers(obj); return obj; } private static void CaptureLevelObjects(WorldSnapshot snap) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) LevelGenerator instance = LevelGenerator.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.LevelParent == (Object)null) { return; } foreach (Transform item in instance.LevelParent.transform) { Transform val = item; Module component = ((Component)val).GetComponent(); string kind = (((Object)(object)component != (Object)null) ? "module" : (((Object)(object)((Component)val).GetComponent() != (Object)null) ? "connect" : "other")); snap.level.Add(new SceneObjectDto { name = Clean(((Object)val).name), kind = kind, pos = new Vec3Dto(val.position), euler = new Vec3Dto(val.eulerAngles), gridX = (((Object)(object)component != (Object)null) ? component.GridX : (-1)), gridY = (((Object)(object)component != (Object)null) ? component.GridY : (-1)), moduleType = ModuleTileType(instance, component) }); } } private static int ModuleTileType(LevelGenerator lg, Module? module) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected I4, but got Unknown if ((Object)(object)module == (Object)null || lg.LevelGrid == null) { return -1; } int gridX = module.GridX; int gridY = module.GridY; if (gridX < 0 || gridY < 0 || gridX >= lg.LevelWidth || gridY >= lg.LevelHeight) { return -1; } Tile val = lg.LevelGrid[gridX, gridY]; if (val == null) { return -1; } return (int)val.type; } private static void CaptureGrid(WorldSnapshot snap) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected I4, but got Unknown LevelGenerator instance = LevelGenerator.Instance; if ((Object)(object)instance == (Object)null || instance.LevelGrid == null) { return; } snap.grid.present = true; snap.grid.width = instance.LevelWidth; snap.grid.height = instance.LevelHeight; for (int i = 0; i < instance.LevelWidth; i++) { for (int j = 0; j < instance.LevelHeight; j++) { Tile val = instance.LevelGrid[i, j]; if (val != null && val.active) { snap.grid.tiles.Add(new TileDto { x = i, y = j, active = val.active, first = val.first, connections = val.connections, cTop = val.connectedTop, cRight = val.connectedRight, cBot = val.connectedBot, cLeft = val.connectedLeft, type = (int)val.type }); } } } } private static void CaptureGrabbables(WorldSnapshot snap) { //IL_0095: 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_0126: 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_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = BuildCartContainment(); PhysGrabObject[] array = Object.FindObjectsOfType(); foreach (PhysGrabObject val in array) { if ((Object)(object)val == (Object)null) { continue; } ValuableObject component = ((Component)val).GetComponent(); string text = (((Object)(object)((Component)val).GetComponent() != (Object)null) ? "cart" : (((Object)(object)component != (Object)null) ? "valuable" : (((Object)(object)((Component)val).GetComponent() != (Object)null) ? "item" : null))); if (text != null) { ValuableDto valuableDto = new ValuableDto { name = Clean(((Object)val).name), kind = text, pos = new Vec3Dto(((Component)val).transform.position), euler = new Vec3Dto(((Component)val).transform.eulerAngles), value = (((Object)(object)component != (Object)null) ? component.dollarValueCurrent : 0f) }; if (text != "cart" && dictionary.TryGetValue(val, out var value) && (Object)(object)value != (Object)null) { Transform transform = ((Component)value).transform; valuableDto.inCart = Clean(((Object)value).name); valuableDto.inCartPos = new Vec3Dto(transform.InverseTransformPoint(((Component)val).transform.position)); Quaternion val2 = Quaternion.Inverse(transform.rotation) * ((Component)val).transform.rotation; valuableDto.inCartEuler = new Vec3Dto(((Quaternion)(ref val2)).eulerAngles); } snap.valuables.Add(valuableDto); } } } private static void CaptureHaulers(WorldSnapshot snap) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) ItemValuableBox[] array = Object.FindObjectsOfType(); foreach (ItemValuableBox val in array) { if (!((Object)(object)val == (Object)null)) { snap.haulers.Add(new HaulerDto { name = Clean(((Object)val).name), pos = new Vec3Dto(((Component)val).transform.position), value = val.CurrentValue }); } } Plugin.Log.LogInfo((object)$"Instant Save: captured {snap.haulers.Count} Hauler(s)."); } private static Dictionary BuildCartContainment() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); PhysGrabCart[] array = Object.FindObjectsOfType(); foreach (PhysGrabCart val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.inCart == (Object)null) { continue; } PhysGrabObject component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null) { continue; } Transform inCart = val.inCart; Collider[] array2 = Physics.OverlapBox(inCart.position, inCart.localScale / 2f, inCart.rotation); foreach (Collider val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { PhysGrabObject componentInParent = ((Component)val2).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent != (Object)(object)component && !dictionary.ContainsKey(componentInParent)) { dictionary[componentInParent] = component; } } } } return dictionary; } private static void CaptureExtraction(WorldSnapshot snap) { //IL_003a: 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_006d: Expected I4, but got Unknown ExtractionPoint[] array = Object.FindObjectsOfType(); foreach (ExtractionPoint val in array) { if (!((Object)(object)val == (Object)null)) { snap.extraction.Add(new ExtractionDto { name = Clean(((Object)val).name), pos = new Vec3Dto(((Component)val).transform.position), haulCurrent = val.haulCurrent, haulGoal = val.haulGoal, state = (int)val.currentState }); } } } private static void CaptureRound(WorldSnapshot snap) { RoundDirector instance = RoundDirector.instance; if (!((Object)(object)instance == (Object)null)) { snap.round = new RoundDto { present = true, currentHaul = instance.currentHaul, totalHaul = instance.totalHaul, currentHaulMax = instance.currentHaulMax, haulGoal = instance.haulGoal, haulGoalMax = instance.haulGoalMax, extractionPoints = instance.extractionPoints, extractionPointSurplus = instance.extractionPointSurplus, extractionPointsCompleted = instance.extractionPointsCompleted, extractionHaulGoal = instance.extractionHaulGoal, extractionPointActive = instance.extractionPointActive, allExtractionPointsCompleted = instance.allExtractionPointsCompleted }; } } private static void CaptureHinges(WorldSnapshot snap) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0061: 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_0089: Unknown result type (might be due to invalid IL or missing references) PhysGrabHinge[] array = Object.FindObjectsOfType(); foreach (PhysGrabHinge val in array) { if (!((Object)(object)val == (Object)null)) { Vector3 v = (((Object)(object)val.hingePoint != (Object)null) ? val.hingePoint.position : ((Component)val).transform.position); snap.hinges.Add(new HingeDto { name = Clean(((Object)val).name), pivot = new Vec3Dto(v), pos = new Vec3Dto(((Component)val).transform.position), euler = new Vec3Dto(((Component)val).transform.eulerAngles), closed = val.closed, broken = val.broken, dead = val.dead }); } } } private static void CapturePlayers(WorldSnapshot snap) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null)) { snap.players.Add(new PlayerDto { steamId = (player.steamID ?? ""), name = (player.playerName ?? ""), pos = new Vec3Dto(((Component)player).transform.position), euler = new Vec3Dto(((Component)player).transform.eulerAngles), health = (((Object)(object)player.playerHealth != (Object)null) ? player.playerHealth.health : 0) }); } } } private static string Clean(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(); } } internal static class WorldRestore { private readonly struct ValuableSource { public readonly GameObject Prefab; public readonly string ResourcePath; public ValuableSource(GameObject prefab, string resourcePath) { Prefab = prefab; ResourcePath = resourcePath; } } private const float ImpactGraceSeconds = 3f; private const int PlayerHoldFrames = 12; private static readonly List<(PhysGrabObject pgo, Vector3 pos, Quaternion rot)> HeldHinges = new List<(PhysGrabObject, Vector3, Quaternion)>(); private static readonly List<(PhysGrabObject inst, PhysGrabObject cart, Vector3 localPos, Quaternion localRot)> HeldCartItems = new List<(PhysGrabObject, PhysGrabObject, Vector3, Quaternion)>(); public static IEnumerator RestoreRoutine(WorldSnapshot snap) { RestoreGrabbables(snap); RestoreHaulers(snap); RefreshEnemies(); RestoreExtraction(snap); RestoreHinges(snap); RestoreHealth(snap); RestorePlayers(snap); for (int i = 0; i < 12; i++) { HoldLocalPlayer(snap); HoldHinges(); HoldCartItems(); yield return (object)new WaitForFixedUpdate(); } Plugin.Log.LogInfo((object)"Restore: complete."); } public static IEnumerator CrossSessionRestore(WorldSnapshot snap) { ClearSpawnedValuables(); yield return null; yield return RestoreRoutine(snap); } private static void ClearSpawnedValuables() { int num = 0; ValuableObject[] array = Object.FindObjectsOfType(); foreach (ValuableObject val in array) { if ((Object)(object)val == (Object)null) { continue; } if (SemiFunc.IsMultiplayer()) { if (PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(((Component)val).gameObject); num++; } } else { Object.Destroy((Object)(object)((Component)val).gameObject); num++; } } Plugin.Log.LogInfo((object)$"Cross-session: cleared {num} generator-spawned valuable(s)."); } public static void RestorePlayers(WorldSnapshot snap) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } int num = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null)) { PlayerDto playerDto = snap.players.Find((PlayerDto x) => x.steamId == player.steamID); if (playerDto != null) { player.Spawn(playerDto.pos.ToVector3(), Quaternion.Euler(playerDto.euler.ToVector3())); ZeroVelocity(((Component)player).GetComponent()); num++; } } } Plugin.Log.LogInfo((object)$"Restore: teleported {num} player(s) to saved positions."); } private static void HoldLocalPlayer(WorldSnapshot snap) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || !player.isLocal) { continue; } PlayerDto playerDto = snap.players.Find((PlayerDto x) => x.steamId == player.steamID); if (playerDto != null) { Vector3 position = playerDto.pos.ToVector3(); Quaternion rotation = Quaternion.Euler(playerDto.euler.ToVector3()); if ((Object)(object)PlayerController.instance != (Object)null) { ((Component)PlayerController.instance).transform.position = position; ((Component)PlayerController.instance).transform.rotation = rotation; } Rigidbody component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null) { component.position = position; ZeroVelocity(component); } ((Component)player).transform.position = position; } } } public static void RestoreHealth(WorldSnapshot snap) { if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } int num = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || (Object)(object)player.playerHealth == (Object)null) { continue; } PlayerDto playerDto = snap.players.Find((PlayerDto x) => x.steamId == player.steamID); if (playerDto != null) { int health = player.playerHealth.health; int health2 = playerDto.health; if (health2 > health) { player.playerHealth.Heal(health2 - health, false); } else if (health2 < health && health2 > 0) { player.playerHealth.Hurt(health - health2, false, -1, true); } num++; } } Plugin.Log.LogInfo((object)$"Restore: set health on {num} player(s)."); } public static void RefreshEnemies() { EnemyDirector instance = EnemyDirector.instance; if ((Object)(object)instance == (Object)null || instance.enemiesSpawned == null) { return; } int num = 0; foreach (EnemyParent item in instance.enemiesSpawned.ToList()) { if (!((Object)(object)item == (Object)null)) { try { item.Despawn(); num++; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: enemy despawn failed: " + ex.Message)); } } } Plugin.Log.LogInfo((object)$"Restore: refreshed (despawned) {num} enemy group(s)."); } public static void RestoreExtraction(WorldSnapshot snap) { //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: 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) RoundDirector instance = RoundDirector.instance; if ((Object)(object)instance != (Object)null && snap.round != null && snap.round.present) { instance.currentHaul = snap.round.currentHaul; instance.totalHaul = snap.round.totalHaul; instance.currentHaulMax = snap.round.currentHaulMax; instance.haulGoal = snap.round.haulGoal; instance.haulGoalMax = snap.round.haulGoalMax; instance.extractionPoints = snap.round.extractionPoints; instance.extractionPointSurplus = snap.round.extractionPointSurplus; instance.extractionPointsCompleted = snap.round.extractionPointsCompleted; instance.extractionHaulGoal = snap.round.extractionHaulGoal; instance.allExtractionPointsCompleted = snap.round.allExtractionPointsCompleted; } List list = Object.FindObjectsOfType().ToList(); HashSet hashSet = new HashSet(); int num = 0; foreach (ExtractionDto item in snap.extraction) { Vector3 val = item.pos.ToVector3(); ExtractionPoint val2 = null; float num2 = float.MaxValue; foreach (ExtractionPoint item2 in list) { if (!((Object)(object)item2 == (Object)null) && !hashSet.Contains(item2)) { Vector3 val3 = ((Component)item2).transform.position - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val2 = item2; } } } if ((Object)(object)val2 != (Object)null) { val2.UpdateLock(false); val2.haulGoal = item.haulGoal; if (item.state == 7) { MarkExtractionComplete(val2); } else { DriveExtractionState(val2, (State)1); } hashSet.Add(val2); num++; } } foreach (ExtractionPoint item3 in list) { if ((Object)(object)item3 != (Object)null) { item3.UpdateLock(false); } } if ((Object)(object)instance != (Object)null) { instance.extractionPointActive = false; instance.extractionPointCurrent = null; } Plugin.Log.LogInfo((object)$"Restore: reset {num} extraction point(s) + round accounting."); } private static void MarkExtractionComplete(ExtractionPoint ep) { ep.taxReturn = true; DriveExtractionState(ep, (State)7); if ((Object)(object)ep.platform != (Object)null) { ((Component)ep.platform).gameObject.SetActive(false); } } private static void DriveExtractionState(ExtractionPoint ep, State state) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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) try { PhotonView component = ((Component)ep).GetComponent(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer()) { component.RPC("StateSetRPC", (RpcTarget)0, new object[1] { state }); } else { ep.StateSetRPC(state, default(PhotonMessageInfo)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: extraction StateSet failed: " + ex.Message)); } } public static void RestoreHinges(WorldSnapshot snap) { //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_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0159: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) List list = Object.FindObjectsOfType().ToList(); HashSet hashSet = new HashSet(); int num = 0; HeldHinges.Clear(); foreach (HingeDto hinge in snap.hinges) { Vector3 val = hinge.pivot.ToVector3(); PhysGrabHinge val2 = null; float num2 = float.MaxValue; foreach (PhysGrabHinge item in list) { if (!((Object)(object)item == (Object)null) && !hashSet.Contains(item) && !(Clean(((Object)item).name) != hinge.name)) { Vector3 val3 = (((Object)(object)item.hingePoint != (Object)null) ? item.hingePoint.position : ((Component)item).transform.position) - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val2 = item; } } } if ((Object)(object)val2 != (Object)null) { PhysGrabObject component = ((Component)val2).GetComponent(); if ((Object)(object)component != (Object)null) { Vector3 val4 = hinge.pos.ToVector3(); Quaternion val5 = Quaternion.Euler(hinge.euler.ToVector3()); component.Teleport(val4, val5); ZeroVelocity(((Component)component).GetComponent()); HeldHinges.Add((component, val4, val5)); } val2.closed = hinge.closed; val2.broken = hinge.broken; val2.dead = hinge.dead; hashSet.Add(val2); num++; } } Plugin.Log.LogInfo((object)$"Restore: reset {num}/{snap.hinges.Count} hinge(s) (doors/drawers)."); } public static void RestoreGrabbables(WorldSnapshot snap) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) List live = Object.FindObjectsOfType().ToList(); HashSet hashSet = new HashSet(); Dictionary map = BuildValuablePrefabMap(); List list = new List(); List<(PhysGrabObject, ValuableDto)> list2 = new List<(PhysGrabObject, ValuableDto)>(); int num = 0; int num2 = 0; int num3 = 0; foreach (ValuableDto valuable in snap.valuables) { Vector3 val = valuable.pos.ToVector3(); Quaternion val2 = Quaternion.Euler(valuable.euler.ToVector3()); PhysGrabObject val3 = null; PhysGrabObject val4 = FindNearestUnused(live, hashSet, valuable.name, val); if ((Object)(object)val4 != (Object)null) { val4.Teleport(val, val2); ZeroVelocity(((Component)val4).GetComponent()); GraceImpact(val4); if (valuable.kind == "valuable") { ValuableObject component = ((Component)val4).GetComponent(); if ((Object)(object)component != (Object)null) { SetValuableValue(component, valuable.value); num2++; } } hashSet.Add(val4); val3 = val4; num++; } else if (valuable.kind == "valuable") { PhysGrabObject val5 = RecreateValuable(valuable, map); if ((Object)(object)val5 != (Object)null) { ZeroVelocity(((Component)val5).GetComponent()); GraceImpact(val5); val3 = val5; num3++; } } if ((Object)(object)val3 != (Object)null) { if (valuable.kind == "cart") { list.Add(val3); } else if (valuable.inCart != null && valuable.inCartPos != null) { list2.Add((val3, valuable)); } } } SeatCartContents(list, list2); Plugin.Log.LogInfo((object)$"Restore: moved {num}, revalued {num2}, recreated {num3} / {snap.valuables.Count} object(s)."); } private static void SeatCartContents(List carts, List<(PhysGrabObject inst, ValuableDto dto)> contents) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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) HeldCartItems.Clear(); if (carts.Count == 0 || contents.Count == 0) { return; } int num = 0; foreach (var (val, valuableDto) in contents) { if (!((Object)(object)val == (Object)null)) { PhysGrabObject val2 = FindCartFor(carts, valuableDto); if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = valuableDto.inCartPos.ToVector3(); Quaternion val4 = Quaternion.Euler((valuableDto.inCartEuler != null) ? valuableDto.inCartEuler.ToVector3() : Vector3.zero); val.Teleport(((Component)val2).transform.TransformPoint(val3), ((Component)val2).transform.rotation * val4); ZeroVelocity(((Component)val).GetComponent()); GraceImpact(val); HeldCartItems.Add((val, val2, val3, val4)); num++; } } } Plugin.Log.LogInfo((object)$"Restore: re-seated {num} object(s) inside {carts.Count} cart(s)."); } private static PhysGrabObject? FindCartFor(List carts, ValuableDto dto) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject result = null; float num = float.MaxValue; Vector3 val = dto.pos.ToVector3(); foreach (PhysGrabObject cart in carts) { if (!((Object)(object)cart == (Object)null) && !(Clean(((Object)cart).name) != dto.inCart)) { Vector3 val2 = ((Component)cart).transform.position - val; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = cart; } } } return result; } private static void HoldCartItems() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_0084: Unknown result type (might be due to invalid IL or missing references) foreach (var (val, val2, val3, val4) in HeldCartItems) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { Vector3 position = ((Component)val2).transform.TransformPoint(val3); Quaternion rotation = ((Component)val2).transform.rotation * val4; Rigidbody component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { component.position = position; component.rotation = rotation; ZeroVelocity(component); } ((Component)val).transform.position = position; ((Component)val).transform.rotation = rotation; } } } private static PhysGrabObject? FindNearestUnused(List live, HashSet used, string name, Vector3 target) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject result = null; float num = float.MaxValue; foreach (PhysGrabObject item in live) { if (!((Object)(object)item == (Object)null) && !used.Contains(item) && !(Clean(((Object)item).name) != name)) { Vector3 val = ((Component)item).transform.position - target; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = item; } } } return result; } private static void SetValuableValue(ValuableObject vo, float value) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) try { PhotonView component = ((Component)vo).GetComponent(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer()) { component.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { value }); } else { vo.DollarValueSetRPC(value, default(PhotonMessageInfo)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: could not set value on '" + ((Object)vo).name + "': " + ex.Message)); } } public static void RestoreHaulers(WorldSnapshot snap) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if (snap.haulers == null || snap.haulers.Count == 0) { return; } List list = Object.FindObjectsOfType().ToList(); HashSet hashSet = new HashSet(); int num = 0; foreach (HaulerDto hauler in snap.haulers) { Vector3 val = hauler.pos.ToVector3(); ItemValuableBox val2 = null; float num2 = float.MaxValue; foreach (ItemValuableBox item in list) { if (!((Object)(object)item == (Object)null) && !hashSet.Contains(item) && !(Clean(((Object)item).name) != hauler.name)) { Vector3 val3 = ((Component)item).transform.position - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val2 = item; } } } if ((Object)(object)val2 != (Object)null) { RestoreValuableBox(val2, hauler.value); hashSet.Add(val2); num++; } } Plugin.Log.LogInfo((object)$"Restore: set stored value on {num}/{snap.haulers.Count} Hauler(s) (live: {list.Count})."); } private static void RestoreValuableBox(ItemValuableBox box, float value) { try { box.UpdateValue(value); Plugin.Log.LogInfo((object)$"Restore: set Hauler '{Clean(((Object)box).name)}' stored value to {value:0}."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: could not set Hauler value on '" + ((Object)box).name + "': " + ex.Message)); } } private static PhysGrabObject? RecreateValuable(ValuableDto dto, Dictionary map) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (!map.TryGetValue(dto.name, out var value) || (Object)(object)value.Prefab == (Object)null) { Plugin.Log.LogWarning((object)("Restore: cannot re-create '" + dto.name + "' — prefab not found in level presets.")); return null; } Vector3 val = dto.pos.ToVector3(); Quaternion val2 = Quaternion.Euler(dto.euler.ToVector3()); GameObject val3 = (SemiFunc.IsMultiplayer() ? PhotonNetwork.InstantiateRoomObject(value.ResourcePath, val, val2, (byte)0, (object[])null) : Object.Instantiate(value.Prefab, val, val2)); if ((Object)(object)val3 == (Object)null) { return null; } ValuableObject component = val3.GetComponent(); if ((Object)(object)component != (Object)null) { component.dollarValueOverride = Mathf.RoundToInt(dto.value); } return val3.GetComponent(); } private static Dictionary BuildValuablePrefabMap() { Dictionary dictionary = new Dictionary(); Level val = (((Object)(object)LevelGenerator.Instance != (Object)null) ? LevelGenerator.Instance.Level : null); if ((Object)(object)val != (Object)null && val.ValuablePresets != null) { foreach (LevelValuables valuablePreset in val.ValuablePresets) { if (!((Object)(object)valuablePreset == (Object)null)) { AddAll(dictionary, valuablePreset.tiny); AddAll(dictionary, valuablePreset.small); AddAll(dictionary, valuablePreset.medium); AddAll(dictionary, valuablePreset.big); AddAll(dictionary, valuablePreset.wide); AddAll(dictionary, valuablePreset.tall); AddAll(dictionary, valuablePreset.veryTall); } } } AssetManager instance = AssetManager.instance; if ((Object)(object)instance != (Object)null) { AddSurplus(dictionary, instance.surplusValuableSmall); AddSurplus(dictionary, instance.surplusValuableMedium); AddSurplus(dictionary, instance.surplusValuableBig); } return dictionary; } private static void AddAll(Dictionary map, List list) { if (list == null) { return; } foreach (PrefabRef item in list) { if (item != null && !((Object)(object)((PrefabRef)(object)item).Prefab == (Object)null)) { string key = Clean(((Object)((PrefabRef)(object)item).Prefab).name); if (!map.ContainsKey(key)) { map[key] = new ValuableSource(((PrefabRef)(object)item).Prefab, ((PrefabRef)(object)item).ResourcePath); } } } } private static void AddSurplus(Dictionary map, GameObject? prefab) { if (!((Object)(object)prefab == (Object)null)) { string key = Clean(((Object)prefab).name); if (!map.ContainsKey(key)) { map[key] = new ValuableSource(prefab, "Valuables/" + ((Object)prefab).name); } } } private static void HoldHinges() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) foreach (var (val, position, rotation) in HeldHinges) { if (!((Object)(object)val == (Object)null)) { Rigidbody component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { component.position = position; component.rotation = rotation; ZeroVelocity(component); } ((Component)val).transform.position = position; ((Component)val).transform.rotation = rotation; } } } private static void GraceImpact(PhysGrabObject pgo) { PhysGrabObjectImpactDetector component = ((Component)pgo).GetComponent(); if ((Object)(object)component != (Object)null) { component.ImpactDisable(3f); } } private static void ZeroVelocity(Rigidbody? rb) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rb == (Object)null) && !rb.isKinematic) { rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } } private static string Clean(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(); } } } namespace REPO_Instant_Save.Patches { [HarmonyPatch(typeof(StatsManager), "LoadGame")] internal static class CrossSessionLoadPatch { [HarmonyPostfix] private static void Postfix(string fileName) { try { WorldSnapshot worldSnapshot = InstantSaveStore.Read(fileName); string reason; if (worldSnapshot == null || !worldSnapshot.grid.present) { CrossSession.Clear(); } else if (IsSnapshotStale(worldSnapshot, out reason)) { Plugin.Log.LogInfo((object)("Cross-session: discarding stale Instant Save for '" + fileName + "' (" + reason + "); resuming native progression.")); CrossSession.Clear(); InstantSaveStore.Delete(fileName); } else { CrossSession.Arm(worldSnapshot); if ((Object)(object)CrossSession.TargetLevel != (Object)null && (Object)(object)RunManager.instance != (Object)null) { RunManager.instance.debugLevel = CrossSession.TargetLevel; Plugin.Log.LogInfo((object)("Cross-session: forcing next level to '" + ((Object)CrossSession.TargetLevel).name + "'.")); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Cross-session arm failed for '" + fileName + "': " + ex.Message)); } } private static bool IsSnapshotStale(WorldSnapshot snapshot, out string reason) { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { reason = ""; return false; } int runStatSaveLevel = instance.GetRunStatSaveLevel(); if (runStatSaveLevel != 0) { reason = $"native save resumes in phase {runStatSaveLevel} (shop/lobby), not in a level"; return true; } if (snapshot.runLevel.HasValue) { int runStatLevel = instance.GetRunStatLevel(); if (runStatLevel != snapshot.runLevel.Value) { reason = $"native run level {runStatLevel} != snapshot run level {snapshot.runLevel.Value}"; return true; } } reason = ""; return false; } } [HarmonyPatch(typeof(LevelGenerator), "GenerateDone")] internal static class CrossSessionGenerateDonePatch { [HarmonyPostfix] private static void Postfix() { if (CrossSession.Pending != null && CrossSession.RebuildActive) { WorldSnapshot pending = CrossSession.Pending; Plugin.Log.LogInfo((object)("Cross-session: saved level '" + pending.levelName + "' rebuilt — applying world restore.")); if ((Object)(object)RunManager.instance != (Object)null) { RunManager.instance.debugLevel = null; } if ((Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(WorldRestore.CrossSessionRestore(pending)); } CrossSession.Clear(); } } } [HarmonyPatch(typeof(LevelGenerator), "TileGeneration")] internal static class TileGenerationRebuildPatch { [HarmonyPrefix] private static bool Prefix(LevelGenerator __instance, ref IEnumerator __result) { if (CrossSession.Pending == null || !CrossSession.ShouldRebuildCurrentLevel()) { return true; } CrossSession.RebuildActive = true; Plugin.Log.LogInfo((object)("Cross-session: rebuilding saved layout for '" + CrossSession.Pending.levelName + "'.")); __result = LevelRebuild.RebuildTiles(__instance, CrossSession.Pending.grid); return false; } } [HarmonyPatch(typeof(LevelGenerator), "StartRoomGeneration")] internal static class StartRoomRebuildPatch { [HarmonyPrefix] private static bool Prefix(LevelGenerator __instance, ref IEnumerator __result) { if (!CrossSession.RebuildActive || CrossSession.Pending == null) { return true; } __result = LevelRebuild.RebuildStartRoom(__instance, CrossSession.Pending); return false; } } [HarmonyPatch(typeof(LevelGenerator), "SpawnModule")] internal static class SpawnModuleRebuildPatch { [HarmonyPrefix] private static bool Prefix(LevelGenerator __instance, int x, int y) { if (!CrossSession.RebuildActive || CrossSession.Pending == null) { return true; } return !LevelRebuild.SpawnSavedModule(__instance, x, y, CrossSession.Pending); } } [HarmonyPatch(typeof(DataDirector), "SaveDeleteCheck")] internal static class SaveDeletePatch { [HarmonyPrefix] private static bool Prefix() { Plugin? instance = Plugin.Instance; if (instance != null && instance.ModConfig?.PreventDeathDelete.Value == true) { Plugin.Log.LogInfo((object)"Blocked REPO auto-delete of the save file (PreventDeathDelete)."); return false; } return true; } } } namespace REPO_Instant_Save.Hotkeys { internal sealed class HotkeyManager : MonoBehaviour { private void Update() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) PluginConfig pluginConfig = Plugin.Instance?.ModConfig; if (pluginConfig != null && pluginConfig.EnableHotkeys.Value && Input.GetKeyDown(pluginConfig.FullSaveKey.Value)) { bool flag = InstantSaveService.SaveNow(); HudNotify.Toast(flag ? "Instant Save complete" : "Instant Save failed", flag); } } } } namespace REPO_Instant_Save.Config { internal sealed class PluginConfig { public ConfigEntry FullSaveKey { get; } public ConfigEntry EnableHotkeys { get; } public ConfigEntry PreventDeathDelete { get; } public PluginConfig(ConfigFile file) { FullSaveKey = file.Bind("Hotkeys", "FullSaveNow", (KeyCode)288, "Capture an Instant Save of the whole level right now (host only)."); EnableHotkeys = file.Bind("Hotkeys", "EnableHotkeys", true, "Enable the Instant Save hotkey (F7 by default)."); PreventDeathDelete = file.Bind("Safety", "PreventDeathDelete", true, "Prevent REPO from deleting your save when the whole team dies or you leave a run early."); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } internal static class IsExternalInit { } }