Decompiled source of SpawnMaster v1.0.1

Mods\SpawnMaster.dll

Decompiled 2 days 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.Text.Json;
using BoneLib;
using BoneLib.BoneMenu;
using BoneLib.Notifications;
using HarmonyLib;
using Il2CppSLZ.Marrow;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using SpawnMaster;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(MainMod), "SpawnMaster", "1.0.0", "dimastv", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonOptionalDependencies(new string[] { "BoneLib" })]
[assembly: MelonColor(255, 50, 180, 230)]
[assembly: MelonAuthorColor(255, 100, 220, 255)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SpawnMaster")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SpawnMaster")]
[assembly: AssemblyTitle("SpawnMaster")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 SpawnMaster
{
	internal static class BoneMenuIntegration
	{
		private static Page? _mainPage;

		private static Page? _infoPage;

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void SetupMenu()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			_mainPage = Page.Root.CreatePage("Spawn Master", new Color(0.2f, 0.8f, 1f), 0, true);
			_mainPage.CreateBool("Mod Enabled", Color.white, ModConfig.Enabled.Value, (Action<bool>)delegate(bool val)
			{
				ModConfig.Enabled.Value = val;
				ModConfig.PrefCategory.SaveToFile(true);
			});
			_mainPage.CreateBool("Spawn at Death Location", Color.yellow, ModConfig.SpawnAtDeathLocation.Value, (Action<bool>)delegate(bool val)
			{
				ModConfig.SpawnAtDeathLocation.Value = val;
				ModConfig.PrefCategory.SaveToFile(true);
				if (val)
				{
					NotificationHelper.ShowSuccess("Spawn Master", "Spawn at death location: ENABLED");
				}
				else
				{
					NotificationHelper.Show("Spawn Master", "Spawn at death location: DISABLED", (NotificationType)0);
				}
				RefreshInfoPage();
			});
			_mainPage.CreateFunction("Set Spawn Point Here", Color.green, (Action)delegate
			{
				SpawnManager.SetSpawnAtCurrentPlayerLocation();
				RefreshInfoPage();
			});
			_mainPage.CreateFunction("Teleport to Spawn Point", Color.cyan, (Action)delegate
			{
				SpawnManager.TeleportToCurrentSpawnPoint();
			});
			_mainPage.CreateFunction("Clear Custom Spawn", Color.red, (Action)delegate
			{
				SpawnManager.ClearCustomSpawnForCurrentLevel();
				RefreshInfoPage();
			});
			_mainPage.CreateFunction("Teleport to Last Death", new Color(1f, 0.4f, 0.8f), (Action)delegate
			{
				SpawnManager.TeleportToLastDeathLocation();
			});
			_infoPage = _mainPage.CreatePage("Settings & Info", Color.white, 0, true);
			BuildInfoPage();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void RefreshInfoPage()
		{
			if (_infoPage != null)
			{
				_infoPage.RemoveAll();
				BuildInfoPage();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void BuildInfoPage()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_0322: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Unknown result type (might be due to invalid IL or missing references)
			if (_infoPage != null)
			{
				_infoPage.CreateFunction("Refresh Info", Color.green, (Action)delegate
				{
					RefreshInfoPage();
				});
				_infoPage.CreateBool("Spawn On Level Load", Color.white, ModConfig.SpawnAtCustomPointOnLoad.Value, (Action<bool>)delegate(bool val)
				{
					ModConfig.SpawnAtCustomPointOnLoad.Value = val;
					ModConfig.PrefCategory.SaveToFile(true);
				});
				_infoPage.CreateBool("No Level Reload on Death", Color.white, ModConfig.PreventLevelReloadOnDeath.Value, (Action<bool>)delegate(bool val)
				{
					ModConfig.PreventLevelReloadOnDeath.Value = val;
					ModConfig.PrefCategory.SaveToFile(true);
				});
				_infoPage.CreateBool("Prevent Void Loop", Color.white, ModConfig.PreventVoidDeathLoop.Value, (Action<bool>)delegate(bool val)
				{
					ModConfig.PreventVoidDeathLoop.Value = val;
					ModConfig.PrefCategory.SaveToFile(true);
				});
				_infoPage.CreateBool("Show Notifications", Color.white, ModConfig.ShowNotifications.Value, (Action<bool>)delegate(bool val)
				{
					ModConfig.ShowNotifications.Value = val;
					ModConfig.PrefCategory.SaveToFile(true);
				});
				_infoPage.CreateFunction("Clear Last Death Position", Color.red, (Action)delegate
				{
					SpawnManager.ClearLastDeathPosition();
					RefreshInfoPage();
				});
				string text = ((!string.IsNullOrEmpty(SpawnManager.CurrentLevelTitle)) ? SpawnManager.CurrentLevelTitle : "(None)");
				_infoPage.CreateFunction("Map: " + text, Color.gray, (Action)delegate
				{
				});
				SpawnPointData customSpawn = ModConfig.GetCustomSpawn(SpawnManager.CurrentLevelKey);
				if (customSpawn != null)
				{
					string text2 = $"({customSpawn.X:F1}, {customSpawn.Y:F1}, {customSpawn.Z:F1})";
					_infoPage.CreateFunction("Custom Spawn: " + text2, Color.cyan, (Action)delegate
					{
					});
				}
				else
				{
					_infoPage.CreateFunction("Custom Spawn: (Default Map Spawn)", Color.gray, (Action)delegate
					{
					});
				}
				if (SpawnManager.HasDeathPosition)
				{
					Vector3 value = SpawnManager.LastDeathPosition.Value;
					string text3 = $"({value.x:F1}, {value.y:F1}, {value.z:F1})";
					_infoPage.CreateFunction("Last Death: " + text3, new Color(1f, 0.4f, 0.8f), (Action)delegate
					{
					});
				}
				else
				{
					_infoPage.CreateFunction("Last Death: (None)", Color.gray, (Action)delegate
					{
					});
				}
			}
		}
	}
	public class MainMod : MelonMod
	{
		public override void OnInitializeMelon()
		{
			ModConfig.Initialize();
			MelonLogger.Msg("SpawnMaster initialized successfully!");
		}

		public override void OnLateInitializeMelon()
		{
			TryInitBoneLib();
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			SpawnManager.OnSceneLoadedDirect(sceneName);
			BoneMenuIntegration.RefreshInfoPage();
		}

		private void TryInitBoneLib()
		{
			try
			{
				if (AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "BoneLib"))
				{
					HookBoneLibEvents();
					BoneMenuIntegration.SetupMenu();
					MelonLogger.Msg("BoneLib integration & BoneMenu initialized successfully!");
				}
				else
				{
					MelonLogger.Warning("BoneLib not detected. Running without BoneMenu.");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("BoneLib integration failed to load: " + ex.Message);
			}
		}

		private void HookBoneLibEvents()
		{
			Hooking.OnLevelLoaded += delegate(LevelInfo levelInfo)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					SpawnManager.OnLevelLoaded(levelInfo);
					BoneMenuIntegration.RefreshInfoPage();
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Error on LevelLoaded hook: " + ex.Message);
				}
			};
			Hooking.OnPlayerDeath += delegate(RigManager rm)
			{
				try
				{
					if (ModConfig.Enabled.Value)
					{
						SpawnManager.RecordDeath(rm);
						BoneMenuIntegration.RefreshInfoPage();
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Error on Hooking.OnPlayerDeath: " + ex.Message);
				}
			};
			Hooking.OnPlayerResurrected += delegate
			{
				try
				{
					BoneMenuIntegration.RefreshInfoPage();
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Error on Hooking.OnPlayerResurrected: " + ex.Message);
				}
			};
		}
	}
	public static class ModConfig
	{
		private static readonly string FolderPath = Path.Combine(MelonEnvironment.UserDataDirectory, "SpawnMaster");

		private static readonly string SpawnsFilePath = Path.Combine(FolderPath, "saved_spawns.json");

		private static readonly Dictionary<string, SpawnPointData> _savedSpawns = new Dictionary<string, SpawnPointData>(StringComparer.OrdinalIgnoreCase);

		private static readonly object _lock = new object();

		public static MelonPreferences_Category PrefCategory { get; private set; } = null;

		public static MelonPreferences_Entry<bool> Enabled { get; private set; } = null;

		public static MelonPreferences_Entry<bool> SpawnAtDeathLocation { get; private set; } = null;

		public static MelonPreferences_Entry<bool> SpawnAtCustomPointOnLoad { get; private set; } = null;

		public static MelonPreferences_Entry<bool> PreventLevelReloadOnDeath { get; private set; } = null;

		public static MelonPreferences_Entry<bool> PreventVoidDeathLoop { get; private set; } = null;

		public static MelonPreferences_Entry<float> SafetyElevationOffset { get; private set; } = null;

		public static MelonPreferences_Entry<bool> ShowNotifications { get; private set; } = null;

		public static void Initialize()
		{
			PrefCategory = MelonPreferences.CreateCategory("SpawnMaster", "Spawn Master");
			Enabled = PrefCategory.CreateEntry<bool>("Enabled", true, "Mod Enabled", "Master toggle for SpawnMaster mod", false, false, (ValueValidator)null, (string)null);
			SpawnAtDeathLocation = PrefCategory.CreateEntry<bool>("SpawnAtDeathLocation", true, "Spawn At Death Location", "When enabled, player respawns directly at the place where they died", false, false, (ValueValidator)null, (string)null);
			SpawnAtCustomPointOnLoad = PrefCategory.CreateEntry<bool>("SpawnAtCustomPointOnLoad", true, "Spawn On Level Load", "Automatically spawn at custom spawn point when loading into the level", false, false, (ValueValidator)null, (string)null);
			PreventLevelReloadOnDeath = PrefCategory.CreateEntry<bool>("PreventLevelReloadOnDeath", true, "Prevent Level Reload On Death", "Prevent reloading the entire level on death, respawning seamlessly in place", false, false, (ValueValidator)null, (string)null);
			PreventVoidDeathLoop = PrefCategory.CreateEntry<bool>("PreventVoidDeathLoop", true, "Prevent Void Death Loop", "If dying repeatedly below void thresholds, safely fall back to spawn point", false, false, (ValueValidator)null, (string)null);
			SafetyElevationOffset = PrefCategory.CreateEntry<float>("SafetyElevationOffset", 0.15f, "Safety Elevation Offset", "Height offset above ground to prevent feet clipping into floor collisions", false, false, (ValueValidator)null, (string)null);
			ShowNotifications = PrefCategory.CreateEntry<bool>("ShowNotifications", true, "Show Notifications", "Display VR notifications when setting spawn points or respawning", false, false, (ValueValidator)null, (string)null);
			LoadSavedSpawns();
		}

		public static void LoadSavedSpawns()
		{
			lock (_lock)
			{
				_savedSpawns.Clear();
				try
				{
					if (!Directory.Exists(FolderPath))
					{
						Directory.CreateDirectory(FolderPath);
					}
					if (!File.Exists(SpawnsFilePath))
					{
						return;
					}
					List<SpawnPointData> list = JsonSerializer.Deserialize<List<SpawnPointData>>(File.ReadAllText(SpawnsFilePath));
					if (list != null)
					{
						foreach (SpawnPointData item in list)
						{
							if (!string.IsNullOrEmpty(item.LevelKey))
							{
								_savedSpawns[item.LevelKey] = item;
							}
						}
					}
					MelonLogger.Msg($"Loaded {_savedSpawns.Count} custom spawn point(s) from disk.");
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Failed to load saved spawns: " + ex.Message);
				}
			}
		}

		public static void SaveToFile()
		{
			lock (_lock)
			{
				try
				{
					if (!Directory.Exists(FolderPath))
					{
						Directory.CreateDirectory(FolderPath);
					}
					List<SpawnPointData> value = new List<SpawnPointData>(_savedSpawns.Values);
					JsonSerializerOptions options = new JsonSerializerOptions
					{
						WriteIndented = true
					};
					string contents = JsonSerializer.Serialize(value, options);
					File.WriteAllText(SpawnsFilePath, contents);
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Failed to save spawn points to file: " + ex.Message);
				}
			}
		}

		public static bool HasCustomSpawn(string levelKey)
		{
			if (string.IsNullOrEmpty(levelKey))
			{
				return false;
			}
			lock (_lock)
			{
				return _savedSpawns.ContainsKey(levelKey);
			}
		}

		public static SpawnPointData? GetCustomSpawn(string levelKey)
		{
			if (string.IsNullOrEmpty(levelKey))
			{
				return null;
			}
			lock (_lock)
			{
				SpawnPointData value;
				return _savedSpawns.TryGetValue(levelKey, out value) ? value : null;
			}
		}

		public static void SetCustomSpawn(string levelKey, Vector3 position, Vector3 forward, string title)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(levelKey))
			{
				return;
			}
			lock (_lock)
			{
				_savedSpawns[levelKey] = new SpawnPointData(position, forward, levelKey, title);
				SaveToFile();
			}
		}

		public static bool RemoveCustomSpawn(string levelKey)
		{
			if (string.IsNullOrEmpty(levelKey))
			{
				return false;
			}
			lock (_lock)
			{
				bool num = _savedSpawns.Remove(levelKey);
				if (num)
				{
					SaveToFile();
				}
				return num;
			}
		}
	}
	public static class NotificationHelper
	{
		public static void Show(string title, string message, NotificationType type = (NotificationType)0, float duration = 2.5f)
		{
			//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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			if (!ModConfig.ShowNotifications.Value)
			{
				return;
			}
			try
			{
				Notifier.Send(new Notification
				{
					Title = new NotificationText(title, Color.cyan, false),
					Message = new NotificationText(message, Color.white, false),
					Type = type,
					PopupLength = duration,
					ShowTitleOnPopup = true
				});
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Could not send BoneLib notification: " + ex.Message);
			}
		}

		public static void ShowSuccess(string title, string message, float duration = 2.5f)
		{
			Show(title, message, (NotificationType)3, duration);
		}

		public static void ShowWarning(string title, string message, float duration = 3f)
		{
			Show(title, message, (NotificationType)1, duration);
		}
	}
	[Serializable]
	public class SpawnPointData
	{
		public float X { get; set; }

		public float Y { get; set; }

		public float Z { get; set; }

		public float FwdX { get; set; }

		public float FwdY { get; set; }

		public float FwdZ { get; set; }

		public string LevelKey { get; set; } = string.Empty;

		public string LevelTitle { get; set; } = string.Empty;

		public string SavedAt { get; set; } = string.Empty;

		public SpawnPointData()
		{
		}

		public SpawnPointData(Vector3 position, Vector3 forward, string levelKey, string levelTitle)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0058: 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)
			X = position.x;
			Y = position.y;
			Z = position.z;
			FwdX = forward.x;
			FwdY = forward.y;
			FwdZ = forward.z;
			LevelKey = levelKey;
			LevelTitle = levelTitle;
			SavedAt = DateTime.UtcNow.ToString("o");
		}

		public Vector3 GetPosition()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(X, Y, Z);
		}

		public Vector3 GetForward()
		{
			//IL_002f: 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)
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(FwdX, FwdY, FwdZ);
			if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f))
			{
				return Vector3.forward;
			}
			return ((Vector3)(ref val)).normalized;
		}
	}
	public static class SpawnManager
	{
		private static float _lastDeathTime = -999f;

		private static int _quickDeathCount = 0;

		private const float VoidKillHeightThreshold = -120f;

		private const float QuickDeathThresholdSeconds = 2f;

		public static string CurrentLevelKey { get; private set; } = string.Empty;

		public static string CurrentLevelTitle { get; private set; } = string.Empty;

		public static Vector3? LastDeathPosition { get; private set; }

		public static Vector3? LastDeathForward { get; private set; }

		public static bool HasDeathPosition => LastDeathPosition.HasValue;

		public static void OnLevelLoaded(LevelInfo levelInfo)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene;
			string currentLevelKey;
			if (string.IsNullOrEmpty(levelInfo.barcode))
			{
				activeScene = SceneManager.GetActiveScene();
				currentLevelKey = ((Scene)(ref activeScene)).name;
			}
			else
			{
				currentLevelKey = levelInfo.barcode;
			}
			CurrentLevelKey = currentLevelKey;
			string currentLevelTitle;
			if (string.IsNullOrEmpty(levelInfo.title))
			{
				activeScene = SceneManager.GetActiveScene();
				currentLevelTitle = ((Scene)(ref activeScene)).name;
			}
			else
			{
				currentLevelTitle = levelInfo.title;
			}
			CurrentLevelTitle = currentLevelTitle;
			_quickDeathCount = 0;
			_lastDeathTime = -999f;
			MelonLogger.Msg($"[SpawnMaster] Level loaded: '{CurrentLevelTitle}' ({CurrentLevelKey})");
			if (ModConfig.Enabled.Value && ModConfig.SpawnAtCustomPointOnLoad.Value && ModConfig.HasCustomSpawn(CurrentLevelKey))
			{
				MelonCoroutines.Start(CoDelayedLevelLoadSpawn());
			}
		}

		public static void OnSceneLoadedDirect(string sceneName)
		{
			if (string.IsNullOrEmpty(CurrentLevelKey) || CurrentLevelKey == sceneName)
			{
				CurrentLevelKey = sceneName;
				CurrentLevelTitle = sceneName;
			}
		}

		public static void RecordDeath(RigManager? rm = null)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: 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_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rm == (Object)null)
			{
				rm = Player.RigManager;
			}
			if ((Object)(object)rm == (Object)null)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup - _lastDeathTime < 2f)
			{
				_quickDeathCount++;
			}
			else
			{
				_quickDeathCount = 1;
			}
			_lastDeathTime = realtimeSinceStartup;
			Vector3 val = (((Object)(object)rm.physicsRig != (Object)null && (Object)(object)rm.physicsRig.feet != (Object)null) ? rm.physicsRig.feet.transform.position : ((!((Object)(object)rm.physicsRig != (Object)null) || !((Object)(object)((Rig)rm.physicsRig).m_pelvis != (Object)null)) ? ((Component)rm).transform.position : (((Rig)rm.physicsRig).m_pelvis.position - Vector3.up * 0.9f)));
			Vector3 value = Vector3.forward;
			if ((Object)(object)rm.physicsRig != (Object)null && (Object)(object)((Rig)rm.physicsRig).m_head != (Object)null)
			{
				Vector3 val2 = Vector3.ProjectOnPlane(((Rig)rm.physicsRig).m_head.forward, Vector3.up);
				if (((Vector3)(ref val2)).sqrMagnitude > 0.01f)
				{
					value = ((Vector3)(ref val2)).normalized;
				}
			}
			else if ((Object)(object)rm.physicsRig != (Object)null && (Object)(object)((Rig)rm.physicsRig).m_pelvis != (Object)null)
			{
				Vector3 val3 = Vector3.ProjectOnPlane(((Rig)rm.physicsRig).m_pelvis.forward, Vector3.up);
				if (((Vector3)(ref val3)).sqrMagnitude > 0.01f)
				{
					value = ((Vector3)(ref val3)).normalized;
				}
			}
			LastDeathPosition = val;
			LastDeathForward = value;
			MelonLogger.Msg($"[SpawnMaster] Death recorded at ({val.x:F2}, {val.y:F2}, {val.z:F2}). QuickDeathCount: {_quickDeathCount}");
		}

		public static bool HandleRespawn(Player_Health health)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.Enabled.Value)
			{
				return false;
			}
			RigManager val = (((Object)(object)((Health)health)._rigManager != (Object)null) ? ((Health)health)._rigManager : Player.RigManager);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (ModConfig.SpawnAtDeathLocation.Value && HasDeathPosition)
			{
				Vector3 value = LastDeathPosition.Value;
				bool flag = value.y < -120f;
				bool flag2 = _quickDeathCount >= 3;
				if (ModConfig.PreventVoidDeathLoop.Value && (flag || flag2))
				{
					MelonLogger.Warning($"[SpawnMaster] Void death loop prevented (Y: {value.y:F1}, QuickDeaths: {_quickDeathCount}). Falling back to safe spawn.");
					NotificationHelper.ShowWarning("Void Loop Prevented", "Teleporting to safe spawn point instead of the void.");
					return TeleportToCustomOrDefaultSpawn(val);
				}
				Vector3 val2 = value + Vector3.up * ModConfig.SafetyElevationOffset.Value;
				Vector3 forward = (Vector3)(((??)LastDeathForward) ?? Vector3.forward);
				ExecuteTeleport(val, val2, forward);
				NotificationHelper.ShowSuccess("Respawned", $"Respawned at death location ({val2.x:F1}, {val2.y:F1}, {val2.z:F1})");
				return true;
			}
			if (ModConfig.HasCustomSpawn(CurrentLevelKey))
			{
				SpawnPointData customSpawn = ModConfig.GetCustomSpawn(CurrentLevelKey);
				if (customSpawn != null)
				{
					Vector3 val3 = customSpawn.GetPosition() + Vector3.up * ModConfig.SafetyElevationOffset.Value;
					Vector3 forward2 = customSpawn.GetForward();
					ExecuteTeleport(val, val3, forward2);
					NotificationHelper.ShowSuccess("Respawned", $"Respawned at custom spawn ({val3.x:F1}, {val3.y:F1}, {val3.z:F1})");
					return true;
				}
			}
			return false;
		}

		public static bool SetSpawnAtCurrentPlayerLocation()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00b2: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				NotificationHelper.ShowWarning("Spawn Master", "Player rig not found!");
				return false;
			}
			Vector3 val = (((Object)(object)rigManager.physicsRig != (Object)null && (Object)(object)rigManager.physicsRig.feet != (Object)null) ? rigManager.physicsRig.feet.transform.position : ((!((Object)(object)rigManager.physicsRig != (Object)null) || !((Object)(object)((Rig)rigManager.physicsRig).m_pelvis != (Object)null)) ? ((Component)rigManager).transform.position : (((Rig)rigManager.physicsRig).m_pelvis.position - Vector3.up * 0.9f)));
			Vector3 val2 = Vector3.forward;
			if ((Object)(object)rigManager.physicsRig != (Object)null && (Object)(object)((Rig)rigManager.physicsRig).m_head != (Object)null)
			{
				Vector3 val3 = Vector3.ProjectOnPlane(((Rig)rigManager.physicsRig).m_head.forward, Vector3.up);
				if (((Vector3)(ref val3)).sqrMagnitude > 0.01f)
				{
					val2 = ((Vector3)(ref val3)).normalized;
				}
			}
			string text;
			if (string.IsNullOrEmpty(CurrentLevelKey))
			{
				Scene activeScene = SceneManager.GetActiveScene();
				text = ((Scene)(ref activeScene)).name;
			}
			else
			{
				text = CurrentLevelKey;
			}
			string text2 = text;
			string text3 = ((!string.IsNullOrEmpty(CurrentLevelTitle)) ? CurrentLevelTitle : text2);
			ModConfig.SetCustomSpawn(text2, val, val2, text3);
			rigManager.checkpointPosition = val;
			rigManager.checkpointFwd = val2;
			MelonLogger.Msg($"[SpawnMaster] Custom spawn point saved for '{text3}': ({val.x:F2}, {val.y:F2}, {val.z:F2})");
			NotificationHelper.ShowSuccess("Spawn Point Set!", $"New spawn set at ({val.x:F1}, {val.y:F1}, {val.z:F1})");
			return true;
		}

		public static bool ClearCustomSpawnForCurrentLevel()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			string text;
			if (string.IsNullOrEmpty(CurrentLevelKey))
			{
				Scene activeScene = SceneManager.GetActiveScene();
				text = ((Scene)(ref activeScene)).name;
			}
			else
			{
				text = CurrentLevelKey;
			}
			string text2 = text;
			bool num = ModConfig.RemoveCustomSpawn(text2);
			if (num)
			{
				NotificationHelper.ShowWarning("Spawn Master", "Custom spawn point cleared. Reverted to default.");
				MelonLogger.Msg("[SpawnMaster] Custom spawn cleared for '" + text2 + "'");
				return num;
			}
			NotificationHelper.Show("Spawn Master", "No custom spawn point was set for this map.", (NotificationType)0);
			return num;
		}

		public static bool TeleportToCurrentSpawnPoint()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				NotificationHelper.ShowWarning("Spawn Master", "Player rig not found!");
				return false;
			}
			string levelKey;
			if (string.IsNullOrEmpty(CurrentLevelKey))
			{
				Scene activeScene = SceneManager.GetActiveScene();
				levelKey = ((Scene)(ref activeScene)).name;
			}
			else
			{
				levelKey = CurrentLevelKey;
			}
			SpawnPointData customSpawn = ModConfig.GetCustomSpawn(levelKey);
			if (customSpawn != null)
			{
				Vector3 position = customSpawn.GetPosition() + Vector3.up * ModConfig.SafetyElevationOffset.Value;
				Vector3 forward = customSpawn.GetForward();
				ExecuteTeleport(rigManager, position, forward);
				NotificationHelper.ShowSuccess("Teleported", "Teleported to custom spawn point!");
				return true;
			}
			ExecuteTeleport(rigManager, rigManager.checkpointPosition, rigManager.checkpointFwd);
			NotificationHelper.Show("Teleported", "Teleported to map checkpoint.", (NotificationType)0);
			return true;
		}

		public static bool TeleportToLastDeathLocation()
		{
			//IL_004b: 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_005f: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				NotificationHelper.ShowWarning("Spawn Master", "Player rig not found!");
				return false;
			}
			if (!HasDeathPosition)
			{
				NotificationHelper.Show("Spawn Master", "No death location recorded yet.", (NotificationType)0);
				return false;
			}
			Vector3 val = LastDeathPosition.Value + Vector3.up * ModConfig.SafetyElevationOffset.Value;
			Vector3 forward = (Vector3)(((??)LastDeathForward) ?? Vector3.forward);
			ExecuteTeleport(rigManager, val, forward);
			NotificationHelper.ShowSuccess("Teleported", $"Teleported to death location ({val.x:F1}, {val.y:F1}, {val.z:F1})");
			return true;
		}

		public static void ClearLastDeathPosition()
		{
			LastDeathPosition = null;
			LastDeathForward = null;
			_quickDeathCount = 0;
			NotificationHelper.Show("Spawn Master", "Cleared last death position.", (NotificationType)0);
		}

		private static bool TeleportToCustomOrDefaultSpawn(RigManager rm)
		{
			//IL_0049: 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_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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.HasCustomSpawn(CurrentLevelKey))
			{
				SpawnPointData customSpawn = ModConfig.GetCustomSpawn(CurrentLevelKey);
				if (customSpawn != null)
				{
					ExecuteTeleport(rm, customSpawn.GetPosition() + Vector3.up * ModConfig.SafetyElevationOffset.Value, customSpawn.GetForward());
					return true;
				}
			}
			ExecuteTeleport(rm, rm.checkpointPosition, rm.checkpointFwd);
			return true;
		}

		private static void ExecuteTeleport(RigManager rm, Vector3 position, Vector3 forward)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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)
			try
			{
				if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
				{
					forward = Vector3.forward;
				}
				rm.Teleport(position, forward, true);
				rm.checkpointPosition = position;
				rm.checkpointFwd = forward;
				if ((Object)(object)rm.physicsRig != (Object)null)
				{
					if ((Object)(object)rm.physicsRig.rbFeet != (Object)null)
					{
						rm.physicsRig.rbFeet.velocity = Vector3.zero;
						rm.physicsRig.rbFeet.angularVelocity = Vector3.zero;
					}
					if ((Object)(object)rm.physicsRig.rbKnee != (Object)null)
					{
						rm.physicsRig.rbKnee.velocity = Vector3.zero;
						rm.physicsRig.rbKnee.angularVelocity = Vector3.zero;
					}
				}
			}
			catch (Exception value)
			{
				MelonLogger.Error($"[SpawnMaster] Error during teleport: {value}");
			}
		}

		private static IEnumerator CoDelayedLevelLoadSpawn()
		{
			yield return (object)new WaitForSeconds(0.8f);
			int attempts = 0;
			while ((Object)(object)Player.RigManager == (Object)null && attempts < 20)
			{
				yield return (object)new WaitForSeconds(0.2f);
				attempts++;
			}
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager != (Object)null && ModConfig.HasCustomSpawn(CurrentLevelKey))
			{
				SpawnPointData customSpawn = ModConfig.GetCustomSpawn(CurrentLevelKey);
				if (customSpawn != null)
				{
					Vector3 val = customSpawn.GetPosition() + Vector3.up * ModConfig.SafetyElevationOffset.Value;
					Vector3 forward = customSpawn.GetForward();
					ExecuteTeleport(rigManager, val, forward);
					NotificationHelper.Show("Spawn Master", "Spawned at custom level spawn point!", (NotificationType)0);
					MelonLogger.Msg($"[SpawnMaster] Applied custom spawn on level load: {val}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player_Health), "Death")]
	internal static class Patch_PlayerHealth_Death
	{
		[HarmonyPrefix]
		public static void Prefix(Player_Health __instance)
		{
			try
			{
				if (ModConfig.Enabled.Value)
				{
					if (ModConfig.PreventLevelReloadOnDeath.Value)
					{
						__instance.reloadLevelOnDeath = false;
					}
					SpawnManager.RecordDeath(((Health)__instance)._rigManager);
				}
			}
			catch (Exception value)
			{
				MelonLogger.Error($"[SpawnMaster] Error in Player_Health.Death prefix: {value}");
			}
		}
	}
	[HarmonyPatch(typeof(Player_Health), "TeleportToCheckpoint")]
	internal static class Patch_PlayerHealth_TeleportToCheckpoint
	{
		[HarmonyPrefix]
		public static bool Prefix(Player_Health __instance)
		{
			try
			{
				if (!ModConfig.Enabled.Value)
				{
					return true;
				}
				if (SpawnManager.HandleRespawn(__instance))
				{
					return false;
				}
			}
			catch (Exception value)
			{
				MelonLogger.Error($"[SpawnMaster] Error in TeleportToCheckpoint prefix: {value}");
			}
			return true;
		}
	}
}