Decompiled source of BetterSewerKeys v1.0.1

mods/BetterSewerKeys_Il2cpp.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BetterSewerKeys;
using BetterSewerKeys.Integrations;
using BetterSewerKeys.Utils;
using HarmonyLib;
using Il2CppFishNet.Connection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Dialogue;
using Il2CppScheduleOne.Doors;
using Il2CppScheduleOne.Interaction;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Map;
using Il2CppScheduleOne.Money;
using Il2CppScheduleOne.NPCs.Relation;
using Il2CppScheduleOne.PlayerScripts;
using MelonLoader;
using Microsoft.CodeAnalysis;
using S1API.GameTime;
using S1API.Internal.Abstraction;
using S1API.Saveables;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "BetterSewerKeys", "1.0.0", "Bars", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BetterSewerKeys_Il2cpp")]
[assembly: AssemblyConfiguration("Il2cpp")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+34036a6df8f55062ef7da2ec838dbf2321b18c8b")]
[assembly: AssemblyProduct("BetterSewerKeys_Il2cpp")]
[assembly: AssemblyTitle("BetterSewerKeys_Il2cpp")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 BetterSewerKeys
{
	public class Core : MelonMod
	{
		[CompilerGenerated]
		private sealed class <DelayedDiscovery>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public Core <>4__this;

			private SewerManager <sewerManager>5__1;

			private Exception <ex>5__2;

			object? IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object? IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DelayedDiscovery>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<sewerManager>5__1 = null;
				<ex>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					try
					{
						if (BetterSewerKeysSave.Instance == null)
						{
							ModLogger.Warning("BetterSewerKeys: Save data instance not available after waiting");
							return false;
						}
						<>4__this._saveData = BetterSewerKeysSave.Instance;
						BetterSewerKeysManager.Instance.Initialize(<>4__this._saveData);
						BetterSewerKeysManager.Instance.DiscoverEntrances();
						<>4__this._saveData.ApplySaveDataAfterDiscovery();
						<sewerManager>5__1 = NetworkSingleton<SewerManager>.Instance;
						if ((Object)(object)<sewerManager>5__1 != (Object)null)
						{
							BetterSewerKeysManager.Instance.AssignKeyDistribution(<sewerManager>5__1);
						}
						<sewerManager>5__1 = null;
					}
					catch (Exception ex)
					{
						<ex>5__2 = ex;
						ModLogger.Error("Error during entrance discovery", <ex>5__2);
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private BetterSewerKeysSave? _saveData;

		public static Core? Instance { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			ModLogger.LogInitialization();
			try
			{
				HarmonyPatches.SetModInstance(this);
				ModLogger.Info("BetterSewerKeys mod initialized successfully");
			}
			catch (Exception exception)
			{
				ModLogger.Error("Failed to initialize BetterSewerKeys mod", exception);
			}
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			try
			{
				if (sceneName.Contains("Main") || sceneName.Contains("Game"))
				{
					ModLogger.Info("Scene initialized: " + sceneName + " - Discovering sewer entrances...");
					MelonCoroutines.Start(DelayedDiscovery());
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error during scene initialization", exception);
			}
		}

		[IteratorStateMachine(typeof(<DelayedDiscovery>d__7))]
		private IEnumerator DelayedDiscovery()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DelayedDiscovery>d__7(0)
			{
				<>4__this = this
			};
		}

		public override void OnApplicationQuit()
		{
			Instance = null;
		}

		public BetterSewerKeysSave? GetSaveData()
		{
			return _saveData;
		}
	}
	public class BetterSewerKeysData
	{
		public Dictionary<int, bool> UnlockedEntrances = new Dictionary<int, bool>();

		public Dictionary<int, int> KeyLocationIndices = new Dictionary<int, int>();

		public Dictionary<int, int> KeyPossessorIndices = new Dictionary<int, int>();

		public Dictionary<int, bool> IsRandomWorldKeyCollected = new Dictionary<int, bool>();

		public int LastDayKeyWasCollected = -1;
	}
	public class BetterSewerKeysSave : Saveable
	{
		[CompilerGenerated]
		private sealed class <DelayedMigration>d__22 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SewerManager sewerManager;

			public BetterSewerKeysSave <>4__this;

			private BetterSewerKeysManager <manager>5__1;

			private List<int>.Enumerator <>s__2;

			private int <entranceID>5__3;

			private Exception <ex>5__4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DelayedMigration>d__22(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<manager>5__1 = null;
				<>s__2 = default(List<int>.Enumerator);
				<ex>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(2f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					try
					{
						<manager>5__1 = BetterSewerKeysManager.Instance;
						if (<manager>5__1 == null)
						{
							ModLogger.Warning("BetterSewerKeys: Manager not initialized during migration");
							return false;
						}
						<manager>5__1.DiscoverEntrances();
						<>s__2 = <manager>5__1.GetAllEntranceIDs().GetEnumerator();
						try
						{
							while (<>s__2.MoveNext())
							{
								<entranceID>5__3 = <>s__2.Current;
								<>4__this.SetEntranceUnlocked(<entranceID>5__3, unlocked: true);
								ModLogger.Info($"BetterSewerKeys: Migrated - unlocked entrance {<entranceID>5__3}");
							}
						}
						finally
						{
							((IDisposable)<>s__2).Dispose();
						}
						<>s__2 = default(List<int>.Enumerator);
						ModLogger.Info($"BetterSewerKeys: Migration complete - unlocked {<manager>5__1.GetAllEntranceIDs().Count} entrances");
						<manager>5__1 = null;
					}
					catch (Exception ex)
					{
						<ex>5__4 = ex;
						ModLogger.Error("Error during delayed migration", <ex>5__4);
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[SaveableField("better_sewer_keysData")]
		public BetterSewerKeysData Data = new BetterSewerKeysData();

		public static BetterSewerKeysSave? Instance { get; private set; }

		public Dictionary<int, bool> UnlockedEntrances => Data.UnlockedEntrances;

		public Dictionary<int, int> KeyLocationIndices => Data.KeyLocationIndices;

		public Dictionary<int, int> KeyPossessorIndices => Data.KeyPossessorIndices;

		public Dictionary<int, bool> IsRandomWorldKeyCollected => Data.IsRandomWorldKeyCollected;

		public int LastDayKeyWasCollected => Data.LastDayKeyWasCollected;

		protected override void OnLoaded()
		{
			ModLogger.Info($"BetterSewerKeys: Loaded save data - {Data.UnlockedEntrances.Count} entrances tracked");
			Instance = this;
			if (BetterSewerKeysManager.Instance != null)
			{
				BetterSewerKeysManager.Instance.Initialize(this);
			}
			CheckAndMigrateOldSave();
		}

		protected override void OnCreated()
		{
			ModLogger.Info("BetterSewerKeys: Save data instance created");
			Instance = this;
			if (BetterSewerKeysManager.Instance != null)
			{
				BetterSewerKeysManager.Instance.Initialize(this);
			}
		}

		public void ApplySaveDataAfterDiscovery()
		{
			TimeManager.OnDayPass = (Action)Delegate.Remove(TimeManager.OnDayPass, new Action(OnDayPass));
			TimeManager.OnDayPass = (Action)Delegate.Combine(TimeManager.OnDayPass, new Action(OnDayPass));
			if (BetterSewerKeysManager.Instance != null)
			{
				BetterSewerKeysManager.Instance.ApplySaveData(this);
			}
			CheckAndSpawnNewKeyPickup();
		}

		private void OnDayPass()
		{
			ModLogger.Info("BetterSewerKeys: OnDayPass callback fired");
			CheckAndSpawnNewKeyPickup();
		}

		private void CheckAndSpawnNewKeyPickup()
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null || instance.AreAllEntrancesUnlocked())
				{
					return;
				}
				SewerManager instance2 = NetworkSingleton<SewerManager>.Instance;
				if ((Object)(object)instance2 == (Object)null || (Object)(object)instance2.RandomWorldSewerKeyPickup == (Object)null || instance2.RandomSewerKeyLocations == null || ((Il2CppArrayBase<Transform>)(object)instance2.RandomSewerKeyLocations).Length == 0)
				{
					return;
				}
				List<int> list = new List<int>();
				foreach (int allEntranceID in instance.GetAllEntranceIDs())
				{
					if (!instance.IsEntranceUnlocked(allEntranceID))
					{
						list.Add(allEntranceID);
					}
				}
				if (list.Count != 0)
				{
					int index = Random.Range(0, list.Count);
					int num = list[index];
					List<int> list2 = new List<int>();
					for (int i = 0; i < ((Il2CppArrayBase<Transform>)(object)instance2.RandomSewerKeyLocations).Length; i++)
					{
						list2.Add(i);
					}
					int num2 = list2[Random.Range(0, list2.Count)];
					SetKeyLocationIndex(num, num2);
					instance2.SetSewerKeyLocation((NetworkConnection)null, num2);
					((Component)instance2.RandomWorldSewerKeyPickup).gameObject.SetActive(true);
					ModLogger.Info($"BetterSewerKeys: Moved random key pickup to new location {num2} for entrance {num} on day pass");
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error checking for new key pickup", exception);
			}
		}

		private void CheckAndMigrateOldSave()
		{
			try
			{
				if (Data.UnlockedEntrances.Count != 0)
				{
					return;
				}
				ModLogger.Debug("BetterSewerKeys: First run detected, checking for old save migration");
				SewerManager instance = NetworkSingleton<SewerManager>.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					PropertyInfo property = typeof(SewerManager).GetProperty("IsSewerUnlocked");
					if (property != null && (bool)property.GetValue(instance))
					{
						ModLogger.Info("BetterSewerKeys: Old save detected with global sewer unlock - migrating to per-entrance system");
						MelonCoroutines.Start(DelayedMigration(instance));
					}
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error during save migration check", exception);
			}
		}

		[IteratorStateMachine(typeof(<DelayedMigration>d__22))]
		private IEnumerator DelayedMigration(SewerManager sewerManager)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DelayedMigration>d__22(0)
			{
				<>4__this = this,
				sewerManager = sewerManager
			};
		}

		protected override void OnSaved()
		{
			ModLogger.Debug($"BetterSewerKeys: Saved data - {Data.UnlockedEntrances.Count} entrances tracked");
			ModLogger.Debug($"BetterSewerKeys: OnSaved - UnlockedEntrances: {Data.UnlockedEntrances.Count}, KeyLocationIndices: {Data.KeyLocationIndices.Count}, KeyPossessorIndices: {Data.KeyPossessorIndices.Count}, IsRandomWorldKeyCollected: {Data.IsRandomWorldKeyCollected.Count}");
		}

		public bool IsEntranceUnlocked(int entranceID)
		{
			bool value;
			return Data.UnlockedEntrances.TryGetValue(entranceID, out value) && value;
		}

		public void SetEntranceUnlocked(int entranceID, bool unlocked)
		{
			Data.UnlockedEntrances[entranceID] = unlocked;
			Saveable.RequestGameSave(false);
		}

		public int GetKeyLocationIndex(int entranceID)
		{
			int value;
			return Data.KeyLocationIndices.TryGetValue(entranceID, out value) ? value : (-1);
		}

		public void SetKeyLocationIndex(int entranceID, int locationIndex)
		{
			Data.KeyLocationIndices[entranceID] = locationIndex;
			Saveable.RequestGameSave(false);
		}

		public int GetKeyPossessorIndex(int entranceID)
		{
			int value;
			return Data.KeyPossessorIndices.TryGetValue(entranceID, out value) ? value : (-1);
		}

		public void SetKeyPossessorIndex(int entranceID, int possessorIndex)
		{
			Data.KeyPossessorIndices[entranceID] = possessorIndex;
			Saveable.RequestGameSave(false);
		}

		public bool IsRandomWorldKeyCollectedForEntrance(int entranceID)
		{
			bool value;
			return Data.IsRandomWorldKeyCollected.TryGetValue(entranceID, out value) && value;
		}

		public void SetRandomWorldKeyCollected(int entranceID, bool collected)
		{
			Data.IsRandomWorldKeyCollected[entranceID] = collected;
			if (collected)
			{
				Data.LastDayKeyWasCollected = TimeManager.ElapsedDays;
			}
			Saveable.RequestGameSave(false);
		}
	}
	public class BetterSewerKeysManager
	{
		private static BetterSewerKeysManager? _instance;

		private Dictionary<int, SewerDoorController> _entranceMap = new Dictionary<int, SewerDoorController>();

		private Dictionary<SewerDoorController, int> _doorToEntranceMap = new Dictionary<SewerDoorController, int>();

		private BetterSewerKeysSave? _saveData;

		private bool _isInitialized = false;

		private int _nextEntranceID = 0;

		public static BetterSewerKeysManager Instance => _instance ?? (_instance = new BetterSewerKeysManager());

		private BetterSewerKeysManager()
		{
		}

		public void Initialize(BetterSewerKeysSave saveData)
		{
			if (!_isInitialized)
			{
				_saveData = saveData;
				ModLogger.Info("BetterSewerKeysManager: Initialized");
				_isInitialized = true;
			}
		}

		public void DiscoverEntrances()
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			if (!_isInitialized || _saveData == null)
			{
				ModLogger.Warning("BetterSewerKeysManager: Cannot discover entrances - not initialized");
				return;
			}
			Il2CppArrayBase<SewerDoorController> val = Object.FindObjectsOfType<SewerDoorController>(true);
			ModLogger.Info($"BetterSewerKeysManager: Found {val.Length} sewer door controllers");
			HashSet<SewerDoorController> hashSet = new HashSet<SewerDoorController>();
			_entranceMap.Clear();
			_doorToEntranceMap.Clear();
			_nextEntranceID = 0;
			foreach (SewerDoorController item in val)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				if (hashSet.Contains(item))
				{
					ModLogger.Debug($"BetterSewerKeysManager: Skipping duplicate door instance: {((Object)((Component)item).gameObject).name} at {((Component)item).transform.position}");
					continue;
				}
				hashSet.Add(item);
				int num = _nextEntranceID++;
				_entranceMap[num] = item;
				_doorToEntranceMap[item] = num;
				if (!_saveData.UnlockedEntrances.ContainsKey(num))
				{
					_saveData.UnlockedEntrances[num] = false;
				}
				if (!_saveData.KeyLocationIndices.ContainsKey(num))
				{
					_saveData.KeyLocationIndices[num] = -1;
				}
				if (!_saveData.KeyPossessorIndices.ContainsKey(num))
				{
					_saveData.KeyPossessorIndices[num] = -1;
				}
				if (!_saveData.IsRandomWorldKeyCollected.ContainsKey(num))
				{
					_saveData.IsRandomWorldKeyCollected[num] = false;
				}
				ModLogger.Debug($"BetterSewerKeysManager: Registered entrance {num} for door {((Object)((Component)item).gameObject).name} at position {((Component)item).transform.position}");
			}
			ModLogger.Info($"BetterSewerKeysManager: Discovered {_entranceMap.Count} entrances");
			if (_saveData != null && _entranceMap.Count > 0)
			{
				ModLogger.Debug($"BetterSewerKeysManager: Triggering save after discovering {_entranceMap.Count} entrances");
				Saveable.RequestGameSave(false);
			}
		}

		public int RegisterDoor(SewerDoorController door)
		{
			if (_doorToEntranceMap.TryGetValue(door, out var value))
			{
				return value;
			}
			int num = _nextEntranceID++;
			_entranceMap[num] = door;
			_doorToEntranceMap[door] = num;
			if (_saveData != null && !_saveData.UnlockedEntrances.ContainsKey(num))
			{
				_saveData.UnlockedEntrances[num] = false;
			}
			ModLogger.Debug($"BetterSewerKeysManager: Registered new entrance {num} for door {((Object)((Component)door).gameObject).name}");
			return num;
		}

		public int GetEntranceID(SewerDoorController door)
		{
			int value;
			return _doorToEntranceMap.TryGetValue(door, out value) ? value : (-1);
		}

		public bool IsEntranceUnlocked(int entranceID)
		{
			if (_saveData == null)
			{
				return false;
			}
			return _saveData.IsEntranceUnlocked(entranceID);
		}

		public void UnlockEntrance(int entranceID)
		{
			if (_saveData == null)
			{
				ModLogger.Warning($"BetterSewerKeysManager: Cannot unlock entrance {entranceID} - save data not initialized");
				return;
			}
			_saveData.SetEntranceUnlocked(entranceID, unlocked: true);
			ModLogger.Info($"BetterSewerKeysManager: Unlocked entrance {entranceID}");
		}

		public List<int> GetAllEntranceIDs()
		{
			return _entranceMap.Keys.ToList();
		}

		public int GetFirstLockedEntranceID()
		{
			if (_saveData == null)
			{
				return -1;
			}
			foreach (int item in _entranceMap.Keys.OrderBy((int id) => id))
			{
				if (!_saveData.IsEntranceUnlocked(item))
				{
					return item;
				}
			}
			return -1;
		}

		public bool AreAllEntrancesUnlocked()
		{
			if (_saveData == null || _entranceMap.Count == 0)
			{
				return false;
			}
			return _entranceMap.Keys.All((int id) => _saveData.IsEntranceUnlocked(id));
		}

		public void ApplySaveData(BetterSewerKeysSave saveData)
		{
			_saveData = saveData;
			ModLogger.Info($"BetterSewerKeysManager: Applied save data with {saveData.UnlockedEntrances.Count} entrances");
		}

		public BetterSewerKeysSave? GetSaveData()
		{
			return _saveData;
		}

		public void AssignKeyDistribution(SewerManager sewerManager)
		{
			if (!_isInitialized || _saveData == null || (Object)(object)sewerManager == (Object)null)
			{
				ModLogger.Warning("BetterSewerKeysManager: Cannot assign key distribution - not initialized or sewer manager missing");
				return;
			}
			if (_entranceMap.Count == 0)
			{
				ModLogger.Warning("BetterSewerKeysManager: No entrances discovered, cannot assign key distribution");
				return;
			}
			List<int> list = new List<int>();
			List<int> list2 = new List<int>();
			if (sewerManager.RandomSewerKeyLocations != null && ((Il2CppArrayBase<Transform>)(object)sewerManager.RandomSewerKeyLocations).Length > 0)
			{
				for (int i = 0; i < ((Il2CppArrayBase<Transform>)(object)sewerManager.RandomSewerKeyLocations).Length; i++)
				{
					list.Add(i);
				}
			}
			if (sewerManager.SewerKeyPossessors != null && ((Il2CppArrayBase<KeyPossessor>)(object)sewerManager.SewerKeyPossessors).Length > 0)
			{
				for (int j = 0; j < ((Il2CppArrayBase<KeyPossessor>)(object)sewerManager.SewerKeyPossessors).Length; j++)
				{
					list2.Add(j);
				}
			}
			ShuffleList(list);
			ShuffleList(list2);
			int num = 0;
			int num2 = 0;
			foreach (int item in _entranceMap.Keys.OrderBy((int id) => id))
			{
				if (num < list.Count && !_saveData.KeyLocationIndices.ContainsKey(item))
				{
					int num3 = list[num++];
					_saveData.SetKeyLocationIndex(item, num3);
					ModLogger.Debug($"Assigned key location {num3} to entrance {item}");
				}
				if (num2 < list2.Count && !_saveData.KeyPossessorIndices.ContainsKey(item))
				{
					int num4 = list2[num2++];
					_saveData.SetKeyPossessorIndex(item, num4);
					ModLogger.Debug($"Assigned key possessor {num4} to entrance {item}");
				}
			}
			ModLogger.Info($"BetterSewerKeysManager: Assigned key distribution for {_entranceMap.Count} entrances");
			if (_saveData != null)
			{
				Saveable.RequestGameSave(false);
			}
		}

		private void ShuffleList<T>(List<T> list)
		{
			Random random = new Random();
			int num = list.Count;
			while (num > 1)
			{
				num--;
				int index = random.Next(num + 1);
				T value = list[index];
				list[index] = list[num];
				list[num] = value;
			}
		}
	}
}
namespace BetterSewerKeys.Utils
{
	public static class Constants
	{
		public static class Defaults
		{
			public const bool BOOLEAN_DEFAULT = false;
		}

		public static class Constraints
		{
			public const float MIN_CONSTRAINT = 0f;

			public const float MAX_CONSTRAINT = 100f;
		}

		public static class Game
		{
			public const string GAME_STUDIO = "TVGS";

			public const string GAME_NAME = "Schedule I";
		}

		public const string MOD_NAME = "BetterSewerKeys";

		public const string MOD_VERSION = "1.0.0";

		public const string MOD_AUTHOR = "Bars";

		public const string MOD_DESCRIPTION = "Mod description...";

		public const string PREFERENCES_CATEGORY = "BetterSewerKeys";
	}
	public static class ModLogger
	{
		public static void Info(string message)
		{
			MelonLogger.Msg("[BetterSewerKeys] " + message);
		}

		public static void Warning(string message)
		{
			MelonLogger.Warning("[BetterSewerKeys] " + message);
		}

		public static void Error(string message)
		{
			MelonLogger.Error("[BetterSewerKeys] " + message);
		}

		public static void Error(string message, Exception exception)
		{
			MelonLogger.Error("[BetterSewerKeys] " + message + ": " + exception.Message);
			MelonLogger.Error("Stack trace: " + exception.StackTrace);
		}

		public static void Debug(string message)
		{
		}

		public static void LogInitialization()
		{
			Info("Initializing BetterSewerKeys v1.0.0 by Bars");
		}

		public static void LogShutdown()
		{
			Info("BetterSewerKeys shutting down");
		}
	}
}
namespace BetterSewerKeys.Integrations
{
	[HarmonyPatch]
	public static class HarmonyPatches
	{
		private static Core? _modInstance;

		public static void SetModInstance(Core modInstance)
		{
			_modInstance = modInstance;
		}
	}
	[HarmonyPatch]
	public static class SewerDoorControllerPatches
	{
		[CompilerGenerated]
		private sealed class <ClearTrackedDoor>d__11 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SewerDoorController door;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ClearTrackedDoor>d__11(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(0.1f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)_lastInteractedDoor == (Object)(object)door)
					{
						_lastInteractedDoor = null;
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly FieldInfo? EntranceIDField = typeof(SewerDoorController).GetField("_entranceID", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(SewerDoorController).GetField("EntranceID", BindingFlags.Instance | BindingFlags.Public);

		private static readonly Dictionary<SewerDoorController, int> _entranceIDMap = new Dictionary<SewerDoorController, int>();

		public static SewerDoorController? _lastInteractedDoor = null;

		private static readonly FieldInfo? ExteriorIntObjsField = typeof(DoorController).GetField("ExteriorIntObjs", BindingFlags.Instance | BindingFlags.NonPublic);

		private static void SetEntranceID(SewerDoorController door, int entranceID)
		{
			if (EntranceIDField != null)
			{
				EntranceIDField.SetValue(door, entranceID);
			}
			else
			{
				_entranceIDMap[door] = entranceID;
			}
		}

		private static int GetEntranceID(SewerDoorController door)
		{
			if (EntranceIDField != null)
			{
				return (EntranceIDField.GetValue(door) is int num) ? num : (-1);
			}
			int value;
			return _entranceIDMap.TryGetValue(door, out value) ? value : (-1);
		}

		[HarmonyPatch(typeof(SewerDoorController), "Awake")]
		[HarmonyPostfix]
		public static void SewerDoorController_Awake_Postfix(SewerDoorController __instance)
		{
			try
			{
				int num = BetterSewerKeysManager.Instance.RegisterDoor(__instance);
				SetEntranceID(__instance, num);
				ModLogger.Debug($"SewerDoorController.Awake: Registered door {((Object)((Component)__instance).gameObject).name} with entrance ID {num}");
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerDoorController.Awake postfix", exception);
			}
		}

		[HarmonyPatch(typeof(SewerDoorController), "CanPlayerAccess")]
		[HarmonyPrefix]
		public static bool SewerDoorController_CanPlayerAccess_Prefix(SewerDoorController __instance, EDoorSide side, ref bool __result, ref string reason)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			try
			{
				reason = string.Empty;
				if ((int)side == 1 && !((DoorController)__instance).IsOpen)
				{
					int entranceID = GetEntranceID(__instance);
					if (entranceID == -1)
					{
						return true;
					}
					if (BetterSewerKeysManager.Instance != null && BetterSewerKeysManager.Instance.IsEntranceUnlocked(entranceID))
					{
						__result = true;
						return false;
					}
					SewerManager instance = NetworkSingleton<SewerManager>.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						PlayerInventory instance2 = PlayerSingleton<PlayerInventory>.Instance;
						if ((Object)(object)instance2 != (Object)null && instance2.GetAmountOfItem(instance.SewerKeyItem.ID) != 0)
						{
							__result = true;
							return false;
						}
						reason = instance.SewerKeyItem.Name + " required";
						__result = false;
						return false;
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerDoorController.CanPlayerAccess prefix", exception);
				return true;
			}
		}

		private static InteractableObject[]? GetExteriorIntObjs(DoorController door)
		{
			if (ExteriorIntObjsField != null)
			{
				return ExteriorIntObjsField.GetValue(door) as InteractableObject[];
			}
			return null;
		}

		[HarmonyPatch(typeof(SewerDoorController), "ExteriorHandleInteracted")]
		[HarmonyPrefix]
		public static bool SewerDoorController_ExteriorHandleInteracted_Prefix(SewerDoorController __instance, out bool __state)
		{
			_lastInteractedDoor = __instance;
			int entranceID = GetEntranceID(__instance);
			bool flag = entranceID != -1 && BetterSewerKeysManager.Instance != null && BetterSewerKeysManager.Instance.IsEntranceUnlocked(entranceID);
			__state = flag;
			return true;
		}

		[HarmonyPatch(typeof(SewerDoorController), "ExteriorHandleInteracted")]
		[HarmonyPostfix]
		public static void SewerDoorController_ExteriorHandleInteracted_Postfix(SewerDoorController __instance, bool __state)
		{
			try
			{
				if (__state)
				{
					int entranceID = GetEntranceID(__instance);
					SewerManager instance = NetworkSingleton<SewerManager>.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						ModLogger.Debug($"ExteriorHandleInteracted: Entrance {entranceID} was already unlocked, unlock logic should be prevented");
					}
				}
				MelonCoroutines.Start(ClearTrackedDoor(__instance));
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerDoorController.ExteriorHandleInteracted postfix", exception);
			}
		}

		[IteratorStateMachine(typeof(<ClearTrackedDoor>d__11))]
		private static IEnumerator ClearTrackedDoor(SewerDoorController door)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ClearTrackedDoor>d__11(0)
			{
				door = door
			};
		}
	}
	[HarmonyPatch]
	public static class SewerManagerPatches
	{
		[HarmonyPatch(typeof(SewerManager), "get_IsSewerUnlocked")]
		[HarmonyPrefix]
		public static bool SewerManager_IsSewerUnlocked_Getter_Prefix(ref bool __result)
		{
			try
			{
				if (BetterSewerKeysManager.Instance != null)
				{
					__result = BetterSewerKeysManager.Instance.AreAllEntrancesUnlocked();
					return false;
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.IsSewerUnlocked getter prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetSewerUnlocked_Server")]
		[HarmonyPrefix]
		public static bool SewerManager_SetSewerUnlocked_Server_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return true;
				}
				FieldInfo field = typeof(SewerDoorControllerPatches).GetField("_lastInteractedDoor", BindingFlags.Static | BindingFlags.Public);
				SewerDoorController val = null;
				if (field != null)
				{
					object? value = field.GetValue(null);
					val = (SewerDoorController)((value is SewerDoorController) ? value : null);
				}
				int num = -1;
				if ((Object)(object)val != (Object)null)
				{
					MethodInfo method = typeof(SewerDoorControllerPatches).GetMethod("GetEntranceID", BindingFlags.Static | BindingFlags.NonPublic);
					if (method != null)
					{
						num = (int)method.Invoke(null, new object[1] { val });
						if (num != -1 && instance.IsEntranceUnlocked(num))
						{
							ModLogger.Debug($"SetSewerUnlocked_Server: Entrance {num} already unlocked, skipping unlock");
							PlayerInventory instance2 = PlayerSingleton<PlayerInventory>.Instance;
							if ((Object)(object)instance2 != (Object)null)
							{
								instance2.AddItemToInventory(__instance.SewerKeyItem.GetDefaultInstance(1));
								ModLogger.Debug("SetSewerUnlocked_Server: Restored key to player inventory");
							}
							return false;
						}
					}
				}
				if (num == -1)
				{
					num = instance.GetFirstLockedEntranceID();
					if (num == -1)
					{
						return true;
					}
					ModLogger.Debug($"SetSewerUnlocked_Server: Could not determine entrance ID from door, using first locked entrance: {num}");
				}
				if (num != -1)
				{
					instance.UnlockEntrance(num);
					ModLogger.Info($"SewerManager.SetSewerUnlocked_Server: Unlocked entrance {num}");
					return false;
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetSewerUnlocked_Server prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetSewerUnlocked_Client", new Type[] { typeof(NetworkConnection) })]
		[HarmonyPrefix]
		public static bool SewerManager_SetSewerUnlocked_Client_Prefix(SewerManager __instance, NetworkConnection conn)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance != null)
				{
					int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
					if (firstLockedEntranceID != -1)
					{
						instance.UnlockEntrance(firstLockedEntranceID);
						ModLogger.Info($"SewerManager.SetSewerUnlocked_Client: Unlocked entrance {firstLockedEntranceID}");
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetSewerUnlocked_Client prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "OnSpawnServer")]
		[HarmonyPostfix]
		public static void SewerManager_OnSpawnServer_Postfix(SewerManager __instance, NetworkConnection connection)
		{
			try
			{
				if (connection.IsHost)
				{
					return;
				}
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return;
				}
				BetterSewerKeysSave saveData = instance.GetSaveData();
				if (saveData == null)
				{
					return;
				}
				foreach (int allEntranceID in instance.GetAllEntranceIDs())
				{
					if (saveData.IsEntranceUnlocked(allEntranceID))
					{
						ModLogger.Debug($"SewerManager.OnSpawnServer: Entrance {allEntranceID} is unlocked, should sync to client");
					}
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.OnSpawnServer postfix", exception);
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetSewerKeyLocation")]
		[HarmonyPrefix]
		public static bool SewerManager_SetSewerKeyLocation_Prefix(SewerManager __instance, NetworkConnection conn, int locationIndex)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance != null && betterSewerKeysSave != null)
				{
					int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
					if (firstLockedEntranceID != -1 && !betterSewerKeysSave.KeyLocationIndices.ContainsKey(firstLockedEntranceID))
					{
						betterSewerKeysSave.SetKeyLocationIndex(firstLockedEntranceID, locationIndex);
						ModLogger.Debug($"Assigned key location {locationIndex} to entrance {firstLockedEntranceID}");
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetSewerKeyLocation prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetRandomKeyPossessor")]
		[HarmonyPrefix]
		public static bool SewerManager_SetRandomKeyPossessor_Prefix(SewerManager __instance, NetworkConnection conn, int possessorIndex)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance != null && betterSewerKeysSave != null)
				{
					int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
					if (firstLockedEntranceID != -1 && !betterSewerKeysSave.KeyPossessorIndices.ContainsKey(firstLockedEntranceID))
					{
						betterSewerKeysSave.SetKeyPossessorIndex(firstLockedEntranceID, possessorIndex);
						ModLogger.Debug($"Assigned key possessor {possessorIndex} to entrance {firstLockedEntranceID}");
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetRandomKeyPossessor prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "Load")]
		[HarmonyPrefix]
		public static void SewerManager_Load_Prefix(SewerManager __instance, ref bool __state)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				__state = instance != null && !instance.AreAllEntrancesUnlocked();
			}
			catch
			{
				__state = false;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "Load")]
		[HarmonyPostfix]
		public static void SewerManager_Load_Postfix(SewerManager __instance, bool __state)
		{
			try
			{
				if (!__state)
				{
					return;
				}
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return;
				}
				PropertyInfo property = typeof(SewerManager).GetProperty("IsRandomWorldKeyCollected", BindingFlags.Instance | BindingFlags.Public);
				if (!(property != null) || !property.CanWrite)
				{
					return;
				}
				property.SetValue(__instance, false);
				ModLogger.Debug("SewerManager.Load: Forced IsRandomWorldKeyCollected to false (not all entrances unlocked)");
				if (!((Object)(object)__instance.RandomWorldSewerKeyPickup != (Object)null) || ((Component)__instance.RandomWorldSewerKeyPickup).gameObject.activeSelf)
				{
					return;
				}
				BetterSewerKeysSave saveData = instance.GetSaveData();
				if (saveData == null)
				{
					return;
				}
				int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
				if (firstLockedEntranceID != -1)
				{
					int keyLocationIndex = saveData.GetKeyLocationIndex(firstLockedEntranceID);
					if (keyLocationIndex >= 0 && keyLocationIndex < ((Il2CppArrayBase<Transform>)(object)__instance.RandomSewerKeyLocations).Length)
					{
						__instance.SetSewerKeyLocation((NetworkConnection)null, keyLocationIndex);
						((Component)__instance.RandomWorldSewerKeyPickup).gameObject.SetActive(true);
						ModLogger.Debug($"SewerManager.Load: Re-enabled key pickup for entrance {firstLockedEntranceID}");
					}
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.Load postfix", exception);
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetRandomKeyCollected_Server")]
		[HarmonyPrefix]
		public static bool SewerManager_SetRandomKeyCollected_Server_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance != null && !instance.AreAllEntrancesUnlocked())
				{
					ModLogger.Debug("SewerManager.SetRandomKeyCollected_Server: Blocked RPC (not all entrances unlocked)");
					return false;
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetRandomKeyCollected_Server prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetRandomWorldKeyCollected")]
		[HarmonyPrefix]
		public static bool SewerManager_SetRandomWorldKeyCollected_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance != null && betterSewerKeysSave != null)
				{
					int randomSewerKeyLocationIndex = __instance.RandomSewerKeyLocationIndex;
					foreach (int allEntranceID in instance.GetAllEntranceIDs())
					{
						if (betterSewerKeysSave.GetKeyLocationIndex(allEntranceID) == randomSewerKeyLocationIndex)
						{
							betterSewerKeysSave.SetRandomWorldKeyCollected(allEntranceID, collected: true);
							ModLogger.Debug($"Marked world key as collected for entrance {allEntranceID} (location {randomSewerKeyLocationIndex})");
							break;
						}
					}
					if (!instance.AreAllEntrancesUnlocked())
					{
						((Component)__instance.RandomWorldSewerKeyPickup).gameObject.SetActive(false);
						return false;
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetRandomWorldKeyCollected prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "EnsureKeyPosessorHasKey")]
		[HarmonyPrefix]
		public static bool SewerManager_EnsureKeyPosessorHasKey_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance == null || betterSewerKeysSave == null || __instance.SewerKeyPossessors == null)
				{
					return true;
				}
				foreach (int allEntranceID in instance.GetAllEntranceIDs())
				{
					int keyPossessorIndex = betterSewerKeysSave.GetKeyPossessorIndex(allEntranceID);
					if (keyPossessorIndex >= 0 && keyPossessorIndex < ((Il2CppArrayBase<KeyPossessor>)(object)__instance.SewerKeyPossessors).Length)
					{
						KeyPossessor val = ((Il2CppArrayBase<KeyPossessor>)(object)__instance.SewerKeyPossessors)[keyPossessorIndex];
						if ((Object)(object)((val != null) ? val.NPC : null) != (Object)null && (Object)(object)val.NPC.Inventory != (Object)null && val.NPC.Inventory._GetItemAmount(__instance.SewerKeyItem.ID) == 0)
						{
							val.NPC.Inventory.InsertItem(__instance.SewerKeyItem.GetDefaultInstance(1), true);
							ModLogger.Debug($"Ensured possessor {keyPossessorIndex} has key for entrance {allEntranceID}");
						}
					}
				}
				return false;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.EnsureKeyPosessorHasKey prefix", exception);
				return true;
			}
		}
	}
	[HarmonyPatch]
	public static class DialogueControllerJenPatches
	{
		[HarmonyPatch(typeof(DialogueController_Jen), "CanBuyKey")]
		[HarmonyPrefix]
		public static bool DialogueController_Jen_CanBuyKey_Prefix(DialogueController_Jen __instance, ref bool __result, ref string invalidReason)
		{
			//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)
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return true;
				}
				if (instance.AreAllEntrancesUnlocked())
				{
					invalidReason = "All sewer entrances are already unlocked";
					__result = false;
					return false;
				}
				int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
				if (firstLockedEntranceID == -1)
				{
					invalidReason = "All sewer entrances are already unlocked";
					__result = false;
					return false;
				}
				if (((DialogueController)__instance).npc.RelationData.RelationDelta < __instance.MinRelationToBuyKey)
				{
					ERelationshipCategory category = RelationshipCategory.GetCategory(__instance.MinRelationToBuyKey);
					invalidReason = "'" + ((object)(ERelationshipCategory)(ref category)).ToString() + "' relationship required";
					__result = false;
					return false;
				}
				__result = true;
				return false;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in DialogueController_Jen.CanBuyKey prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(DialogueController_Jen), "ChoiceCallback")]
		[HarmonyPrefix]
		public static bool DialogueController_Jen_ChoiceCallback_Prefix(DialogueController_Jen __instance, string choiceLabel)
		{
			try
			{
				if (choiceLabel != "CHOICE_CONFIRM")
				{
					return true;
				}
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return true;
				}
				if (instance.AreAllEntrancesUnlocked())
				{
					ModLogger.Warning("DialogueController_Jen.ChoiceCallback: All entrances unlocked, cannot buy key");
					return false;
				}
				int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
				if (firstLockedEntranceID == -1)
				{
					ModLogger.Warning("DialogueController_Jen.ChoiceCallback: No locked entrances found");
					return false;
				}
				if (NetworkSingleton<MoneyManager>.Instance.cashBalance < __instance.KeyItem.BasePurchasePrice)
				{
					return true;
				}
				NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - __instance.KeyItem.BasePurchasePrice, true, true);
				((DialogueController)__instance).npc.Inventory.InsertItem((ItemInstance)(object)NetworkSingleton<MoneyManager>.Instance.GetCashInstance(__instance.KeyItem.BasePurchasePrice), true);
				PlayerSingleton<PlayerInventory>.Instance.AddItemToInventory(((ItemDefinition)__instance.KeyItem).GetDefaultInstance(1));
				ModLogger.Info($"DialogueController_Jen.ChoiceCallback: Player bought key for entrance {firstLockedEntranceID}");
				return false;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in DialogueController_Jen.ChoiceCallback prefix", exception);
				return true;
			}
		}
	}
}

mods/BetterSewerKeys_Mono.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BetterSewerKeys;
using BetterSewerKeys.Integrations;
using BetterSewerKeys.Utils;
using FishNet.Connection;
using HarmonyLib;
using MelonLoader;
using Microsoft.CodeAnalysis;
using S1API.GameTime;
using S1API.Internal.Abstraction;
using S1API.Saveables;
using ScheduleOne.DevUtilities;
using ScheduleOne.Dialogue;
using ScheduleOne.Doors;
using ScheduleOne.Interaction;
using ScheduleOne.ItemFramework;
using ScheduleOne.Map;
using ScheduleOne.Money;
using ScheduleOne.NPCs.Relation;
using ScheduleOne.PlayerScripts;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "BetterSewerKeys", "1.0.0", "Bars", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BetterSewerKeys_Mono")]
[assembly: AssemblyConfiguration("Mono")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f831208a976f48df7cff82e3a7fd8ceb0780d4e0")]
[assembly: AssemblyProduct("BetterSewerKeys_Mono")]
[assembly: AssemblyTitle("BetterSewerKeys_Mono")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 BetterSewerKeys
{
	public class Core : MelonMod
	{
		[CompilerGenerated]
		private sealed class <DelayedDiscovery>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public Core <>4__this;

			private SewerManager <sewerManager>5__1;

			private Exception <ex>5__2;

			object? IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object? IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DelayedDiscovery>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<sewerManager>5__1 = null;
				<ex>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					try
					{
						if (BetterSewerKeysSave.Instance == null)
						{
							ModLogger.Warning("BetterSewerKeys: Save data instance not available after waiting");
							return false;
						}
						<>4__this._saveData = BetterSewerKeysSave.Instance;
						BetterSewerKeysManager.Instance.Initialize(<>4__this._saveData);
						BetterSewerKeysManager.Instance.DiscoverEntrances();
						<>4__this._saveData.ApplySaveDataAfterDiscovery();
						<sewerManager>5__1 = NetworkSingleton<SewerManager>.Instance;
						if ((Object)(object)<sewerManager>5__1 != (Object)null)
						{
							BetterSewerKeysManager.Instance.AssignKeyDistribution(<sewerManager>5__1);
						}
						<sewerManager>5__1 = null;
					}
					catch (Exception ex)
					{
						<ex>5__2 = ex;
						ModLogger.Error("Error during entrance discovery", <ex>5__2);
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private BetterSewerKeysSave? _saveData;

		public static Core? Instance { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			ModLogger.LogInitialization();
			try
			{
				HarmonyPatches.SetModInstance(this);
				ModLogger.Info("BetterSewerKeys mod initialized successfully");
			}
			catch (Exception exception)
			{
				ModLogger.Error("Failed to initialize BetterSewerKeys mod", exception);
			}
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			try
			{
				if (sceneName.Contains("Main") || sceneName.Contains("Game"))
				{
					ModLogger.Info("Scene initialized: " + sceneName + " - Discovering sewer entrances...");
					MelonCoroutines.Start(DelayedDiscovery());
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error during scene initialization", exception);
			}
		}

		[IteratorStateMachine(typeof(<DelayedDiscovery>d__7))]
		private IEnumerator DelayedDiscovery()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DelayedDiscovery>d__7(0)
			{
				<>4__this = this
			};
		}

		public override void OnApplicationQuit()
		{
			Instance = null;
		}

		public BetterSewerKeysSave? GetSaveData()
		{
			return _saveData;
		}
	}
	public class BetterSewerKeysData
	{
		public Dictionary<int, bool> UnlockedEntrances = new Dictionary<int, bool>();

		public Dictionary<int, int> KeyLocationIndices = new Dictionary<int, int>();

		public Dictionary<int, int> KeyPossessorIndices = new Dictionary<int, int>();

		public Dictionary<int, bool> IsRandomWorldKeyCollected = new Dictionary<int, bool>();

		public int LastDayKeyWasCollected = -1;
	}
	public class BetterSewerKeysSave : Saveable
	{
		[CompilerGenerated]
		private sealed class <DelayedMigration>d__22 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SewerManager sewerManager;

			public BetterSewerKeysSave <>4__this;

			private BetterSewerKeysManager <manager>5__1;

			private List<int>.Enumerator <>s__2;

			private int <entranceID>5__3;

			private Exception <ex>5__4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DelayedMigration>d__22(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<manager>5__1 = null;
				<>s__2 = default(List<int>.Enumerator);
				<ex>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(2f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					try
					{
						<manager>5__1 = BetterSewerKeysManager.Instance;
						if (<manager>5__1 == null)
						{
							ModLogger.Warning("BetterSewerKeys: Manager not initialized during migration");
							return false;
						}
						<manager>5__1.DiscoverEntrances();
						<>s__2 = <manager>5__1.GetAllEntranceIDs().GetEnumerator();
						try
						{
							while (<>s__2.MoveNext())
							{
								<entranceID>5__3 = <>s__2.Current;
								<>4__this.SetEntranceUnlocked(<entranceID>5__3, unlocked: true);
								ModLogger.Info($"BetterSewerKeys: Migrated - unlocked entrance {<entranceID>5__3}");
							}
						}
						finally
						{
							((IDisposable)<>s__2).Dispose();
						}
						<>s__2 = default(List<int>.Enumerator);
						ModLogger.Info($"BetterSewerKeys: Migration complete - unlocked {<manager>5__1.GetAllEntranceIDs().Count} entrances");
						<manager>5__1 = null;
					}
					catch (Exception ex)
					{
						<ex>5__4 = ex;
						ModLogger.Error("Error during delayed migration", <ex>5__4);
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[SaveableField("better_sewer_keysData")]
		public BetterSewerKeysData Data = new BetterSewerKeysData();

		public static BetterSewerKeysSave? Instance { get; private set; }

		public Dictionary<int, bool> UnlockedEntrances => Data.UnlockedEntrances;

		public Dictionary<int, int> KeyLocationIndices => Data.KeyLocationIndices;

		public Dictionary<int, int> KeyPossessorIndices => Data.KeyPossessorIndices;

		public Dictionary<int, bool> IsRandomWorldKeyCollected => Data.IsRandomWorldKeyCollected;

		public int LastDayKeyWasCollected => Data.LastDayKeyWasCollected;

		protected override void OnLoaded()
		{
			ModLogger.Info($"BetterSewerKeys: Loaded save data - {Data.UnlockedEntrances.Count} entrances tracked");
			Instance = this;
			if (BetterSewerKeysManager.Instance != null)
			{
				BetterSewerKeysManager.Instance.Initialize(this);
			}
			CheckAndMigrateOldSave();
		}

		protected override void OnCreated()
		{
			ModLogger.Info("BetterSewerKeys: Save data instance created");
			Instance = this;
			if (BetterSewerKeysManager.Instance != null)
			{
				BetterSewerKeysManager.Instance.Initialize(this);
			}
		}

		public void ApplySaveDataAfterDiscovery()
		{
			TimeManager.OnDayPass = (Action)Delegate.Remove(TimeManager.OnDayPass, new Action(OnDayPass));
			TimeManager.OnDayPass = (Action)Delegate.Combine(TimeManager.OnDayPass, new Action(OnDayPass));
			if (BetterSewerKeysManager.Instance != null)
			{
				BetterSewerKeysManager.Instance.ApplySaveData(this);
			}
			CheckAndSpawnNewKeyPickup();
		}

		private void OnDayPass()
		{
			ModLogger.Info("BetterSewerKeys: OnDayPass callback fired");
			CheckAndSpawnNewKeyPickup();
		}

		private void CheckAndSpawnNewKeyPickup()
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null || instance.AreAllEntrancesUnlocked())
				{
					return;
				}
				SewerManager instance2 = NetworkSingleton<SewerManager>.Instance;
				if ((Object)(object)instance2 == (Object)null || (Object)(object)instance2.RandomWorldSewerKeyPickup == (Object)null || instance2.RandomSewerKeyLocations == null || instance2.RandomSewerKeyLocations.Length == 0)
				{
					return;
				}
				List<int> list = new List<int>();
				foreach (int allEntranceID in instance.GetAllEntranceIDs())
				{
					if (!instance.IsEntranceUnlocked(allEntranceID))
					{
						list.Add(allEntranceID);
					}
				}
				if (list.Count != 0)
				{
					int index = Random.Range(0, list.Count);
					int num = list[index];
					List<int> list2 = new List<int>();
					for (int i = 0; i < instance2.RandomSewerKeyLocations.Length; i++)
					{
						list2.Add(i);
					}
					int num2 = list2[Random.Range(0, list2.Count)];
					SetKeyLocationIndex(num, num2);
					instance2.SetSewerKeyLocation((NetworkConnection)null, num2);
					((Component)instance2.RandomWorldSewerKeyPickup).gameObject.SetActive(true);
					ModLogger.Info($"BetterSewerKeys: Moved random key pickup to new location {num2} for entrance {num} on day pass");
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error checking for new key pickup", exception);
			}
		}

		private void CheckAndMigrateOldSave()
		{
			try
			{
				if (Data.UnlockedEntrances.Count != 0)
				{
					return;
				}
				ModLogger.Debug("BetterSewerKeys: First run detected, checking for old save migration");
				SewerManager instance = NetworkSingleton<SewerManager>.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					PropertyInfo property = typeof(SewerManager).GetProperty("IsSewerUnlocked");
					if (property != null && (bool)property.GetValue(instance))
					{
						ModLogger.Info("BetterSewerKeys: Old save detected with global sewer unlock - migrating to per-entrance system");
						MelonCoroutines.Start(DelayedMigration(instance));
					}
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error during save migration check", exception);
			}
		}

		[IteratorStateMachine(typeof(<DelayedMigration>d__22))]
		private IEnumerator DelayedMigration(SewerManager sewerManager)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DelayedMigration>d__22(0)
			{
				<>4__this = this,
				sewerManager = sewerManager
			};
		}

		protected override void OnSaved()
		{
			ModLogger.Debug($"BetterSewerKeys: Saved data - {Data.UnlockedEntrances.Count} entrances tracked");
			ModLogger.Debug($"BetterSewerKeys: OnSaved - UnlockedEntrances: {Data.UnlockedEntrances.Count}, KeyLocationIndices: {Data.KeyLocationIndices.Count}, KeyPossessorIndices: {Data.KeyPossessorIndices.Count}, IsRandomWorldKeyCollected: {Data.IsRandomWorldKeyCollected.Count}");
		}

		public bool IsEntranceUnlocked(int entranceID)
		{
			bool value;
			return Data.UnlockedEntrances.TryGetValue(entranceID, out value) && value;
		}

		public void SetEntranceUnlocked(int entranceID, bool unlocked)
		{
			Data.UnlockedEntrances[entranceID] = unlocked;
			Saveable.RequestGameSave(false);
		}

		public int GetKeyLocationIndex(int entranceID)
		{
			int value;
			return Data.KeyLocationIndices.TryGetValue(entranceID, out value) ? value : (-1);
		}

		public void SetKeyLocationIndex(int entranceID, int locationIndex)
		{
			Data.KeyLocationIndices[entranceID] = locationIndex;
			Saveable.RequestGameSave(false);
		}

		public int GetKeyPossessorIndex(int entranceID)
		{
			int value;
			return Data.KeyPossessorIndices.TryGetValue(entranceID, out value) ? value : (-1);
		}

		public void SetKeyPossessorIndex(int entranceID, int possessorIndex)
		{
			Data.KeyPossessorIndices[entranceID] = possessorIndex;
			Saveable.RequestGameSave(false);
		}

		public bool IsRandomWorldKeyCollectedForEntrance(int entranceID)
		{
			bool value;
			return Data.IsRandomWorldKeyCollected.TryGetValue(entranceID, out value) && value;
		}

		public void SetRandomWorldKeyCollected(int entranceID, bool collected)
		{
			Data.IsRandomWorldKeyCollected[entranceID] = collected;
			if (collected)
			{
				Data.LastDayKeyWasCollected = TimeManager.ElapsedDays;
			}
			Saveable.RequestGameSave(false);
		}
	}
	public class BetterSewerKeysManager
	{
		private static BetterSewerKeysManager? _instance;

		private Dictionary<int, SewerDoorController> _entranceMap = new Dictionary<int, SewerDoorController>();

		private Dictionary<SewerDoorController, int> _doorToEntranceMap = new Dictionary<SewerDoorController, int>();

		private BetterSewerKeysSave? _saveData;

		private bool _isInitialized = false;

		private int _nextEntranceID = 0;

		public static BetterSewerKeysManager Instance => _instance ?? (_instance = new BetterSewerKeysManager());

		private BetterSewerKeysManager()
		{
		}

		public void Initialize(BetterSewerKeysSave saveData)
		{
			if (!_isInitialized)
			{
				_saveData = saveData;
				ModLogger.Info("BetterSewerKeysManager: Initialized");
				_isInitialized = true;
			}
		}

		public void DiscoverEntrances()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			if (!_isInitialized || _saveData == null)
			{
				ModLogger.Warning("BetterSewerKeysManager: Cannot discover entrances - not initialized");
				return;
			}
			SewerDoorController[] array = Object.FindObjectsOfType<SewerDoorController>(true);
			ModLogger.Info($"BetterSewerKeysManager: Found {array.Length} sewer door controllers");
			HashSet<SewerDoorController> hashSet = new HashSet<SewerDoorController>();
			_entranceMap.Clear();
			_doorToEntranceMap.Clear();
			_nextEntranceID = 0;
			SewerDoorController[] array2 = array;
			foreach (SewerDoorController val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if (hashSet.Contains(val))
				{
					ModLogger.Debug($"BetterSewerKeysManager: Skipping duplicate door instance: {((Object)((Component)val).gameObject).name} at {((Component)val).transform.position}");
					continue;
				}
				hashSet.Add(val);
				int num = _nextEntranceID++;
				_entranceMap[num] = val;
				_doorToEntranceMap[val] = num;
				if (!_saveData.UnlockedEntrances.ContainsKey(num))
				{
					_saveData.UnlockedEntrances[num] = false;
				}
				if (!_saveData.KeyLocationIndices.ContainsKey(num))
				{
					_saveData.KeyLocationIndices[num] = -1;
				}
				if (!_saveData.KeyPossessorIndices.ContainsKey(num))
				{
					_saveData.KeyPossessorIndices[num] = -1;
				}
				if (!_saveData.IsRandomWorldKeyCollected.ContainsKey(num))
				{
					_saveData.IsRandomWorldKeyCollected[num] = false;
				}
				ModLogger.Debug($"BetterSewerKeysManager: Registered entrance {num} for door {((Object)((Component)val).gameObject).name} at position {((Component)val).transform.position}");
			}
			ModLogger.Info($"BetterSewerKeysManager: Discovered {_entranceMap.Count} entrances");
			if (_saveData != null && _entranceMap.Count > 0)
			{
				ModLogger.Debug($"BetterSewerKeysManager: Triggering save after discovering {_entranceMap.Count} entrances");
				Saveable.RequestGameSave(false);
			}
		}

		public int RegisterDoor(SewerDoorController door)
		{
			if (_doorToEntranceMap.TryGetValue(door, out var value))
			{
				return value;
			}
			int num = _nextEntranceID++;
			_entranceMap[num] = door;
			_doorToEntranceMap[door] = num;
			if (_saveData != null && !_saveData.UnlockedEntrances.ContainsKey(num))
			{
				_saveData.UnlockedEntrances[num] = false;
			}
			ModLogger.Debug($"BetterSewerKeysManager: Registered new entrance {num} for door {((Object)((Component)door).gameObject).name}");
			return num;
		}

		public int GetEntranceID(SewerDoorController door)
		{
			int value;
			return _doorToEntranceMap.TryGetValue(door, out value) ? value : (-1);
		}

		public bool IsEntranceUnlocked(int entranceID)
		{
			if (_saveData == null)
			{
				return false;
			}
			return _saveData.IsEntranceUnlocked(entranceID);
		}

		public void UnlockEntrance(int entranceID)
		{
			if (_saveData == null)
			{
				ModLogger.Warning($"BetterSewerKeysManager: Cannot unlock entrance {entranceID} - save data not initialized");
				return;
			}
			_saveData.SetEntranceUnlocked(entranceID, unlocked: true);
			ModLogger.Info($"BetterSewerKeysManager: Unlocked entrance {entranceID}");
		}

		public List<int> GetAllEntranceIDs()
		{
			return _entranceMap.Keys.ToList();
		}

		public int GetFirstLockedEntranceID()
		{
			if (_saveData == null)
			{
				return -1;
			}
			foreach (int item in _entranceMap.Keys.OrderBy((int id) => id))
			{
				if (!_saveData.IsEntranceUnlocked(item))
				{
					return item;
				}
			}
			return -1;
		}

		public bool AreAllEntrancesUnlocked()
		{
			if (_saveData == null || _entranceMap.Count == 0)
			{
				return false;
			}
			return _entranceMap.Keys.All((int id) => _saveData.IsEntranceUnlocked(id));
		}

		public void ApplySaveData(BetterSewerKeysSave saveData)
		{
			_saveData = saveData;
			ModLogger.Info($"BetterSewerKeysManager: Applied save data with {saveData.UnlockedEntrances.Count} entrances");
		}

		public BetterSewerKeysSave? GetSaveData()
		{
			return _saveData;
		}

		public void AssignKeyDistribution(SewerManager sewerManager)
		{
			if (!_isInitialized || _saveData == null || (Object)(object)sewerManager == (Object)null)
			{
				ModLogger.Warning("BetterSewerKeysManager: Cannot assign key distribution - not initialized or sewer manager missing");
				return;
			}
			if (_entranceMap.Count == 0)
			{
				ModLogger.Warning("BetterSewerKeysManager: No entrances discovered, cannot assign key distribution");
				return;
			}
			List<int> list = new List<int>();
			List<int> list2 = new List<int>();
			if (sewerManager.RandomSewerKeyLocations != null && sewerManager.RandomSewerKeyLocations.Length != 0)
			{
				for (int i = 0; i < sewerManager.RandomSewerKeyLocations.Length; i++)
				{
					list.Add(i);
				}
			}
			if (sewerManager.SewerKeyPossessors != null && sewerManager.SewerKeyPossessors.Length != 0)
			{
				for (int j = 0; j < sewerManager.SewerKeyPossessors.Length; j++)
				{
					list2.Add(j);
				}
			}
			ShuffleList(list);
			ShuffleList(list2);
			int num = 0;
			int num2 = 0;
			foreach (int item in _entranceMap.Keys.OrderBy((int id) => id))
			{
				if (num < list.Count && !_saveData.KeyLocationIndices.ContainsKey(item))
				{
					int num3 = list[num++];
					_saveData.SetKeyLocationIndex(item, num3);
					ModLogger.Debug($"Assigned key location {num3} to entrance {item}");
				}
				if (num2 < list2.Count && !_saveData.KeyPossessorIndices.ContainsKey(item))
				{
					int num4 = list2[num2++];
					_saveData.SetKeyPossessorIndex(item, num4);
					ModLogger.Debug($"Assigned key possessor {num4} to entrance {item}");
				}
			}
			ModLogger.Info($"BetterSewerKeysManager: Assigned key distribution for {_entranceMap.Count} entrances");
			if (_saveData != null)
			{
				Saveable.RequestGameSave(false);
			}
		}

		private void ShuffleList<T>(List<T> list)
		{
			Random random = new Random();
			int num = list.Count;
			while (num > 1)
			{
				num--;
				int index = random.Next(num + 1);
				T value = list[index];
				list[index] = list[num];
				list[num] = value;
			}
		}
	}
}
namespace BetterSewerKeys.Utils
{
	public static class Constants
	{
		public static class Defaults
		{
			public const bool BOOLEAN_DEFAULT = false;
		}

		public static class Constraints
		{
			public const float MIN_CONSTRAINT = 0f;

			public const float MAX_CONSTRAINT = 100f;
		}

		public static class Game
		{
			public const string GAME_STUDIO = "TVGS";

			public const string GAME_NAME = "Schedule I";
		}

		public const string MOD_NAME = "BetterSewerKeys";

		public const string MOD_VERSION = "1.0.0";

		public const string MOD_AUTHOR = "Bars";

		public const string MOD_DESCRIPTION = "Mod description...";

		public const string PREFERENCES_CATEGORY = "BetterSewerKeys";
	}
	public static class ModLogger
	{
		public static void Info(string message)
		{
			MelonLogger.Msg("[BetterSewerKeys] " + message);
		}

		public static void Warning(string message)
		{
			MelonLogger.Warning("[BetterSewerKeys] " + message);
		}

		public static void Error(string message)
		{
			MelonLogger.Error("[BetterSewerKeys] " + message);
		}

		public static void Error(string message, Exception exception)
		{
			MelonLogger.Error("[BetterSewerKeys] " + message + ": " + exception.Message);
			MelonLogger.Error("Stack trace: " + exception.StackTrace);
		}

		public static void Debug(string message)
		{
		}

		public static void LogInitialization()
		{
			Info("Initializing BetterSewerKeys v1.0.0 by Bars");
		}

		public static void LogShutdown()
		{
			Info("BetterSewerKeys shutting down");
		}
	}
}
namespace BetterSewerKeys.Integrations
{
	[HarmonyPatch]
	public static class HarmonyPatches
	{
		private static Core? _modInstance;

		public static void SetModInstance(Core modInstance)
		{
			_modInstance = modInstance;
		}
	}
	[HarmonyPatch]
	public static class SewerDoorControllerPatches
	{
		[CompilerGenerated]
		private sealed class <ClearTrackedDoor>d__11 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SewerDoorController door;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ClearTrackedDoor>d__11(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(0.1f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)_lastInteractedDoor == (Object)(object)door)
					{
						_lastInteractedDoor = null;
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly FieldInfo? EntranceIDField = typeof(SewerDoorController).GetField("_entranceID", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(SewerDoorController).GetField("EntranceID", BindingFlags.Instance | BindingFlags.Public);

		private static readonly Dictionary<SewerDoorController, int> _entranceIDMap = new Dictionary<SewerDoorController, int>();

		public static SewerDoorController? _lastInteractedDoor = null;

		private static readonly FieldInfo? ExteriorIntObjsField = typeof(DoorController).GetField("ExteriorIntObjs", BindingFlags.Instance | BindingFlags.NonPublic);

		private static void SetEntranceID(SewerDoorController door, int entranceID)
		{
			if (EntranceIDField != null)
			{
				EntranceIDField.SetValue(door, entranceID);
			}
			else
			{
				_entranceIDMap[door] = entranceID;
			}
		}

		private static int GetEntranceID(SewerDoorController door)
		{
			if (EntranceIDField != null)
			{
				return (EntranceIDField.GetValue(door) is int num) ? num : (-1);
			}
			int value;
			return _entranceIDMap.TryGetValue(door, out value) ? value : (-1);
		}

		[HarmonyPatch(typeof(SewerDoorController), "Awake")]
		[HarmonyPostfix]
		public static void SewerDoorController_Awake_Postfix(SewerDoorController __instance)
		{
			try
			{
				int num = BetterSewerKeysManager.Instance.RegisterDoor(__instance);
				SetEntranceID(__instance, num);
				ModLogger.Debug($"SewerDoorController.Awake: Registered door {((Object)((Component)__instance).gameObject).name} with entrance ID {num}");
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerDoorController.Awake postfix", exception);
			}
		}

		[HarmonyPatch(typeof(SewerDoorController), "CanPlayerAccess")]
		[HarmonyPrefix]
		public static bool SewerDoorController_CanPlayerAccess_Prefix(SewerDoorController __instance, EDoorSide side, ref bool __result, ref string reason)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			try
			{
				reason = string.Empty;
				if ((int)side == 1 && !((DoorController)__instance).IsOpen)
				{
					int entranceID = GetEntranceID(__instance);
					if (entranceID == -1)
					{
						return true;
					}
					if (BetterSewerKeysManager.Instance != null && BetterSewerKeysManager.Instance.IsEntranceUnlocked(entranceID))
					{
						__result = true;
						return false;
					}
					SewerManager instance = NetworkSingleton<SewerManager>.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						PlayerInventory instance2 = PlayerSingleton<PlayerInventory>.Instance;
						if ((Object)(object)instance2 != (Object)null && instance2.GetAmountOfItem(instance.SewerKeyItem.ID) != 0)
						{
							__result = true;
							return false;
						}
						reason = instance.SewerKeyItem.Name + " required";
						__result = false;
						return false;
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerDoorController.CanPlayerAccess prefix", exception);
				return true;
			}
		}

		private static InteractableObject[]? GetExteriorIntObjs(DoorController door)
		{
			if (ExteriorIntObjsField != null)
			{
				return ExteriorIntObjsField.GetValue(door) as InteractableObject[];
			}
			return null;
		}

		[HarmonyPatch(typeof(SewerDoorController), "ExteriorHandleInteracted")]
		[HarmonyPrefix]
		public static bool SewerDoorController_ExteriorHandleInteracted_Prefix(SewerDoorController __instance, out bool __state)
		{
			_lastInteractedDoor = __instance;
			int entranceID = GetEntranceID(__instance);
			bool flag = entranceID != -1 && BetterSewerKeysManager.Instance != null && BetterSewerKeysManager.Instance.IsEntranceUnlocked(entranceID);
			__state = flag;
			return true;
		}

		[HarmonyPatch(typeof(SewerDoorController), "ExteriorHandleInteracted")]
		[HarmonyPostfix]
		public static void SewerDoorController_ExteriorHandleInteracted_Postfix(SewerDoorController __instance, bool __state)
		{
			try
			{
				if (__state)
				{
					int entranceID = GetEntranceID(__instance);
					SewerManager instance = NetworkSingleton<SewerManager>.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						ModLogger.Debug($"ExteriorHandleInteracted: Entrance {entranceID} was already unlocked, unlock logic should be prevented");
					}
				}
				MelonCoroutines.Start(ClearTrackedDoor(__instance));
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerDoorController.ExteriorHandleInteracted postfix", exception);
			}
		}

		[IteratorStateMachine(typeof(<ClearTrackedDoor>d__11))]
		private static IEnumerator ClearTrackedDoor(SewerDoorController door)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ClearTrackedDoor>d__11(0)
			{
				door = door
			};
		}
	}
	[HarmonyPatch]
	public static class SewerManagerPatches
	{
		[HarmonyPatch(typeof(SewerManager), "get_IsSewerUnlocked")]
		[HarmonyPrefix]
		public static bool SewerManager_IsSewerUnlocked_Getter_Prefix(ref bool __result)
		{
			try
			{
				if (BetterSewerKeysManager.Instance != null)
				{
					__result = BetterSewerKeysManager.Instance.AreAllEntrancesUnlocked();
					return false;
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.IsSewerUnlocked getter prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetSewerUnlocked_Server")]
		[HarmonyPrefix]
		public static bool SewerManager_SetSewerUnlocked_Server_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return true;
				}
				FieldInfo field = typeof(SewerDoorControllerPatches).GetField("_lastInteractedDoor", BindingFlags.Static | BindingFlags.Public);
				SewerDoorController val = null;
				if (field != null)
				{
					object? value = field.GetValue(null);
					val = (SewerDoorController)((value is SewerDoorController) ? value : null);
				}
				int num = -1;
				if ((Object)(object)val != (Object)null)
				{
					MethodInfo method = typeof(SewerDoorControllerPatches).GetMethod("GetEntranceID", BindingFlags.Static | BindingFlags.NonPublic);
					if (method != null)
					{
						num = (int)method.Invoke(null, new object[1] { val });
						if (num != -1 && instance.IsEntranceUnlocked(num))
						{
							ModLogger.Debug($"SetSewerUnlocked_Server: Entrance {num} already unlocked, skipping unlock");
							PlayerInventory instance2 = PlayerSingleton<PlayerInventory>.Instance;
							if ((Object)(object)instance2 != (Object)null)
							{
								instance2.AddItemToInventory(__instance.SewerKeyItem.GetDefaultInstance(1));
								ModLogger.Debug("SetSewerUnlocked_Server: Restored key to player inventory");
							}
							return false;
						}
					}
				}
				if (num == -1)
				{
					num = instance.GetFirstLockedEntranceID();
					if (num == -1)
					{
						return true;
					}
					ModLogger.Debug($"SetSewerUnlocked_Server: Could not determine entrance ID from door, using first locked entrance: {num}");
				}
				if (num != -1)
				{
					instance.UnlockEntrance(num);
					ModLogger.Info($"SewerManager.SetSewerUnlocked_Server: Unlocked entrance {num}");
					return false;
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetSewerUnlocked_Server prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetSewerUnlocked_Client", new Type[] { typeof(NetworkConnection) })]
		[HarmonyPrefix]
		public static bool SewerManager_SetSewerUnlocked_Client_Prefix(SewerManager __instance, NetworkConnection conn)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance != null)
				{
					int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
					if (firstLockedEntranceID != -1)
					{
						instance.UnlockEntrance(firstLockedEntranceID);
						ModLogger.Info($"SewerManager.SetSewerUnlocked_Client: Unlocked entrance {firstLockedEntranceID}");
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetSewerUnlocked_Client prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "OnSpawnServer")]
		[HarmonyPostfix]
		public static void SewerManager_OnSpawnServer_Postfix(SewerManager __instance, NetworkConnection connection)
		{
			try
			{
				if (connection.IsHost)
				{
					return;
				}
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return;
				}
				BetterSewerKeysSave saveData = instance.GetSaveData();
				if (saveData == null)
				{
					return;
				}
				foreach (int allEntranceID in instance.GetAllEntranceIDs())
				{
					if (saveData.IsEntranceUnlocked(allEntranceID))
					{
						ModLogger.Debug($"SewerManager.OnSpawnServer: Entrance {allEntranceID} is unlocked, should sync to client");
					}
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.OnSpawnServer postfix", exception);
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetSewerKeyLocation")]
		[HarmonyPrefix]
		public static bool SewerManager_SetSewerKeyLocation_Prefix(SewerManager __instance, NetworkConnection conn, int locationIndex)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance != null && betterSewerKeysSave != null)
				{
					int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
					if (firstLockedEntranceID != -1 && !betterSewerKeysSave.KeyLocationIndices.ContainsKey(firstLockedEntranceID))
					{
						betterSewerKeysSave.SetKeyLocationIndex(firstLockedEntranceID, locationIndex);
						ModLogger.Debug($"Assigned key location {locationIndex} to entrance {firstLockedEntranceID}");
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetSewerKeyLocation prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetRandomKeyPossessor")]
		[HarmonyPrefix]
		public static bool SewerManager_SetRandomKeyPossessor_Prefix(SewerManager __instance, NetworkConnection conn, int possessorIndex)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance != null && betterSewerKeysSave != null)
				{
					int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
					if (firstLockedEntranceID != -1 && !betterSewerKeysSave.KeyPossessorIndices.ContainsKey(firstLockedEntranceID))
					{
						betterSewerKeysSave.SetKeyPossessorIndex(firstLockedEntranceID, possessorIndex);
						ModLogger.Debug($"Assigned key possessor {possessorIndex} to entrance {firstLockedEntranceID}");
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetRandomKeyPossessor prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "Load")]
		[HarmonyPrefix]
		public static void SewerManager_Load_Prefix(SewerManager __instance, ref bool __state)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				__state = instance != null && !instance.AreAllEntrancesUnlocked();
			}
			catch
			{
				__state = false;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "Load")]
		[HarmonyPostfix]
		public static void SewerManager_Load_Postfix(SewerManager __instance, bool __state)
		{
			try
			{
				if (!__state)
				{
					return;
				}
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return;
				}
				PropertyInfo property = typeof(SewerManager).GetProperty("IsRandomWorldKeyCollected", BindingFlags.Instance | BindingFlags.Public);
				if (!(property != null) || !property.CanWrite)
				{
					return;
				}
				property.SetValue(__instance, false);
				ModLogger.Debug("SewerManager.Load: Forced IsRandomWorldKeyCollected to false (not all entrances unlocked)");
				if (!((Object)(object)__instance.RandomWorldSewerKeyPickup != (Object)null) || ((Component)__instance.RandomWorldSewerKeyPickup).gameObject.activeSelf)
				{
					return;
				}
				BetterSewerKeysSave saveData = instance.GetSaveData();
				if (saveData == null)
				{
					return;
				}
				int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
				if (firstLockedEntranceID != -1)
				{
					int keyLocationIndex = saveData.GetKeyLocationIndex(firstLockedEntranceID);
					if (keyLocationIndex >= 0 && keyLocationIndex < __instance.RandomSewerKeyLocations.Length)
					{
						__instance.SetSewerKeyLocation((NetworkConnection)null, keyLocationIndex);
						((Component)__instance.RandomWorldSewerKeyPickup).gameObject.SetActive(true);
						ModLogger.Debug($"SewerManager.Load: Re-enabled key pickup for entrance {firstLockedEntranceID}");
					}
				}
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.Load postfix", exception);
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetRandomKeyCollected_Server")]
		[HarmonyPrefix]
		public static bool SewerManager_SetRandomKeyCollected_Server_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance != null && !instance.AreAllEntrancesUnlocked())
				{
					ModLogger.Debug("SewerManager.SetRandomKeyCollected_Server: Blocked RPC (not all entrances unlocked)");
					return false;
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetRandomKeyCollected_Server prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "SetRandomWorldKeyCollected")]
		[HarmonyPrefix]
		public static bool SewerManager_SetRandomWorldKeyCollected_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance != null && betterSewerKeysSave != null)
				{
					int randomSewerKeyLocationIndex = __instance.RandomSewerKeyLocationIndex;
					foreach (int allEntranceID in instance.GetAllEntranceIDs())
					{
						if (betterSewerKeysSave.GetKeyLocationIndex(allEntranceID) == randomSewerKeyLocationIndex)
						{
							betterSewerKeysSave.SetRandomWorldKeyCollected(allEntranceID, collected: true);
							ModLogger.Debug($"Marked world key as collected for entrance {allEntranceID} (location {randomSewerKeyLocationIndex})");
							break;
						}
					}
					if (!instance.AreAllEntrancesUnlocked())
					{
						((Component)__instance.RandomWorldSewerKeyPickup).gameObject.SetActive(false);
						return false;
					}
				}
				return true;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.SetRandomWorldKeyCollected prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(SewerManager), "EnsureKeyPosessorHasKey")]
		[HarmonyPrefix]
		public static bool SewerManager_EnsureKeyPosessorHasKey_Prefix(SewerManager __instance)
		{
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				BetterSewerKeysSave betterSewerKeysSave = instance?.GetSaveData();
				if (instance == null || betterSewerKeysSave == null || __instance.SewerKeyPossessors == null)
				{
					return true;
				}
				foreach (int allEntranceID in instance.GetAllEntranceIDs())
				{
					int keyPossessorIndex = betterSewerKeysSave.GetKeyPossessorIndex(allEntranceID);
					if (keyPossessorIndex >= 0 && keyPossessorIndex < __instance.SewerKeyPossessors.Length)
					{
						KeyPossessor val = __instance.SewerKeyPossessors[keyPossessorIndex];
						if ((Object)(object)val?.NPC != (Object)null && (Object)(object)val.NPC.Inventory != (Object)null && val.NPC.Inventory._GetItemAmount(__instance.SewerKeyItem.ID) == 0)
						{
							val.NPC.Inventory.InsertItem(__instance.SewerKeyItem.GetDefaultInstance(1), true);
							ModLogger.Debug($"Ensured possessor {keyPossessorIndex} has key for entrance {allEntranceID}");
						}
					}
				}
				return false;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in SewerManager.EnsureKeyPosessorHasKey prefix", exception);
				return true;
			}
		}
	}
	[HarmonyPatch]
	public static class DialogueControllerJenPatches
	{
		[HarmonyPatch(typeof(DialogueController_Jen), "CanBuyKey")]
		[HarmonyPrefix]
		public static bool DialogueController_Jen_CanBuyKey_Prefix(DialogueController_Jen __instance, ref bool __result, ref string invalidReason)
		{
			//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)
			try
			{
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return true;
				}
				if (instance.AreAllEntrancesUnlocked())
				{
					invalidReason = "All sewer entrances are already unlocked";
					__result = false;
					return false;
				}
				int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
				if (firstLockedEntranceID == -1)
				{
					invalidReason = "All sewer entrances are already unlocked";
					__result = false;
					return false;
				}
				if (((DialogueController)__instance).npc.RelationData.RelationDelta < __instance.MinRelationToBuyKey)
				{
					ERelationshipCategory category = RelationshipCategory.GetCategory(__instance.MinRelationToBuyKey);
					invalidReason = "'" + ((object)(ERelationshipCategory)(ref category)).ToString() + "' relationship required";
					__result = false;
					return false;
				}
				__result = true;
				return false;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in DialogueController_Jen.CanBuyKey prefix", exception);
				return true;
			}
		}

		[HarmonyPatch(typeof(DialogueController_Jen), "ChoiceCallback")]
		[HarmonyPrefix]
		public static bool DialogueController_Jen_ChoiceCallback_Prefix(DialogueController_Jen __instance, string choiceLabel)
		{
			try
			{
				if (choiceLabel != "CHOICE_CONFIRM")
				{
					return true;
				}
				BetterSewerKeysManager instance = BetterSewerKeysManager.Instance;
				if (instance == null)
				{
					return true;
				}
				if (instance.AreAllEntrancesUnlocked())
				{
					ModLogger.Warning("DialogueController_Jen.ChoiceCallback: All entrances unlocked, cannot buy key");
					return false;
				}
				int firstLockedEntranceID = instance.GetFirstLockedEntranceID();
				if (firstLockedEntranceID == -1)
				{
					ModLogger.Warning("DialogueController_Jen.ChoiceCallback: No locked entrances found");
					return false;
				}
				if (NetworkSingleton<MoneyManager>.Instance.cashBalance < __instance.KeyItem.BasePurchasePrice)
				{
					return true;
				}
				NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - __instance.KeyItem.BasePurchasePrice, true, true);
				((DialogueController)__instance).npc.Inventory.InsertItem((ItemInstance)(object)NetworkSingleton<MoneyManager>.Instance.GetCashInstance(__instance.KeyItem.BasePurchasePrice), true);
				PlayerSingleton<PlayerInventory>.Instance.AddItemToInventory(((ItemDefinition)__instance.KeyItem).GetDefaultInstance(1));
				ModLogger.Info($"DialogueController_Jen.ChoiceCallback: Player bought key for entrance {firstLockedEntranceID}");
				return false;
			}
			catch (Exception exception)
			{
				ModLogger.Error("Error in DialogueController_Jen.ChoiceCallback prefix", exception);
				return true;
			}
		}
	}
}