Decompiled source of REPOInstantSave v1.0.0

REPO-Instant-Save.dll

Decompiled a day ago
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<HotkeyManager>();
				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<WorldSnapshot>(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<string, PrefabRef> _modulePrefabs = new Dictionary<string, PrefabRef>();

		private static void BuildPrefabMap(LevelGenerator lg)
		{
			_modulePrefabs = new Dictionary<string, PrefabRef>();
			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<PrefabRef> list)
		{
			if (list == null)
			{
				return;
			}
			foreach (PrefabRef item in list)
			{
				if (item != null && !((Object)(object)((PrefabRef<GameObject>)(object)item).Prefab == (Object)null))
				{
					string key = Clean(((Object)((PrefabRef<GameObject>)(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<Module>();
			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<GameObject>(((PrefabRef<GameObject>)(object)pr).Prefab, pos, rot);
			}
			return PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(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<SceneObjectDto> level { get; set; } = new List<SceneObjectDto>();

		public List<ValuableDto> valuables { get; set; } = new List<ValuableDto>();

		public List<HaulerDto> haulers { get; set; } = new List<HaulerDto>();

		public List<ExtractionDto> extraction { get; set; } = new List<ExtractionDto>();

		public List<PlayerDto> players { get; set; } = new List<PlayerDto>();

		public RoundDto round { get; set; } = new RoundDto();

		public List<HingeDto> hinges { get; set; } = new List<HingeDto>();

		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<TileDto> tiles { get; set; } = new List<TileDto>();
	}
	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<Module>();
				string kind = (((Object)(object)component != (Object)null) ? "module" : (((Object)(object)((Component)val).GetComponent<ModuleConnectObject>() != (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<PhysGrabObject, PhysGrabObject> dictionary = BuildCartContainment();
			PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
			foreach (PhysGrabObject val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ValuableObject component = ((Component)val).GetComponent<ValuableObject>();
				string text = (((Object)(object)((Component)val).GetComponent<PhysGrabCart>() != (Object)null) ? "cart" : (((Object)(object)component != (Object)null) ? "valuable" : (((Object)(object)((Component)val).GetComponent<ItemAttributes>() != (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<ItemValuableBox>();
			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<PhysGrabObject, PhysGrabObject> 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<PhysGrabObject, PhysGrabObject> dictionary = new Dictionary<PhysGrabObject, PhysGrabObject>();
			PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>();
			foreach (PhysGrabCart val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val.inCart == (Object)null)
				{
					continue;
				}
				PhysGrabObject component = ((Component)val).GetComponent<PhysGrabObject>();
				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<PhysGrabObject>();
						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<ExtractionPoint>();
			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<PhysGrabHinge>();
			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<ValuableObject>();
			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<Rigidbody>());
						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<Rigidbody>();
					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<ExtractionPoint> list = Object.FindObjectsOfType<ExtractionPoint>().ToList();
			HashSet<ExtractionPoint> hashSet = new HashSet<ExtractionPoint>();
			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<PhotonView>();
				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<PhysGrabHinge> list = Object.FindObjectsOfType<PhysGrabHinge>().ToList();
			HashSet<PhysGrabHinge> hashSet = new HashSet<PhysGrabHinge>();
			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<PhysGrabObject>();
					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<Rigidbody>());
						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<PhysGrabObject> live = Object.FindObjectsOfType<PhysGrabObject>().ToList();
			HashSet<PhysGrabObject> hashSet = new HashSet<PhysGrabObject>();
			Dictionary<string, ValuableSource> map = BuildValuablePrefabMap();
			List<PhysGrabObject> list = new List<PhysGrabObject>();
			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<Rigidbody>());
					GraceImpact(val4);
					if (valuable.kind == "valuable")
					{
						ValuableObject component = ((Component)val4).GetComponent<ValuableObject>();
						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<Rigidbody>());
						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<PhysGrabObject> 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<Rigidbody>());
						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<PhysGrabObject> 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<Rigidbody>();
					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<PhysGrabObject> live, HashSet<PhysGrabObject> 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<PhotonView>();
				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<ItemValuableBox> list = Object.FindObjectsOfType<ItemValuableBox>().ToList();
			HashSet<ItemValuableBox> hashSet = new HashSet<ItemValuableBox>();
			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<string, ValuableSource> 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<GameObject>(value.Prefab, val, val2));
			if ((Object)(object)val3 == (Object)null)
			{
				return null;
			}
			ValuableObject component = val3.GetComponent<ValuableObject>();
			if ((Object)(object)component != (Object)null)
			{
				component.dollarValueOverride = Mathf.RoundToInt(dto.value);
			}
			return val3.GetComponent<PhysGrabObject>();
		}

		private static Dictionary<string, ValuableSource> BuildValuablePrefabMap()
		{
			Dictionary<string, ValuableSource> dictionary = new Dictionary<string, ValuableSource>();
			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<string, ValuableSource> map, List<PrefabRef> list)
		{
			if (list == null)
			{
				return;
			}
			foreach (PrefabRef item in list)
			{
				if (item != null && !((Object)(object)((PrefabRef<GameObject>)(object)item).Prefab == (Object)null))
				{
					string key = Clean(((Object)((PrefabRef<GameObject>)(object)item).Prefab).name);
					if (!map.ContainsKey(key))
					{
						map[key] = new ValuableSource(((PrefabRef<GameObject>)(object)item).Prefab, ((PrefabRef<GameObject>)(object)item).ResourcePath);
					}
				}
			}
		}

		private static void AddSurplus(Dictionary<string, ValuableSource> 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<Rigidbody>();
					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<PhysGrabObjectImpactDetector>();
			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<KeyCode> FullSaveKey { get; }

		public ConfigEntry<bool> EnableHotkeys { get; }

		public ConfigEntry<bool> PreventDeathDelete { get; }

		public PluginConfig(ConfigFile file)
		{
			FullSaveKey = file.Bind<KeyCode>("Hotkeys", "FullSaveNow", (KeyCode)288, "Capture an Instant Save of the whole level right now (host only).");
			EnableHotkeys = file.Bind<bool>("Hotkeys", "EnableHotkeys", true, "Enable the Instant Save hotkey (F7 by default).");
			PreventDeathDelete = file.Bind<bool>("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
	{
	}
}