Decompiled source of SODQoL v1.1.5

SOD.QoL.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Rewired;
using SOD.Common;
using SOD.Common.BepInEx;
using SOD.Common.BepInEx.Configuration;
using SOD.Common.Extensions;
using SOD.Common.Helpers;
using SOD.QoL.Objects;
using SOD.QoL.Patches;
using UnityEngine;
using UnityEngine.UI;
using UniverseLib;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SOD.QoL")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+9e4b5c64ffebce151406b3e28ce732d42c57bdc4")]
[assembly: AssemblyProduct("SOD.QoL")]
[assembly: AssemblyTitle("SOD.QoL")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace SOD.QoL
{
	public interface IPluginBindings : IConversationBindings, IMainMenuBindings, IMapBindings, IGameplayPatchBindings, IJobExpirationBindings
	{
	}
	public interface IGameplayPatchBindings
	{
		[Binding(true, "Fixes the player never getting tired.", "QoL.Gameplay.FixTiredness")]
		bool FixTiredness { get; set; }

		[Binding(12, "The percentage that is taken of alertness and added to energy restore for caffeine items. (12 seems balanced)", "QoL.Gameplay.PercentageEnergyRestore")]
		int PercentageEnergyRestore { get; set; }
	}
	public interface IJobExpirationBindings
	{
		[Binding(true, "Side jobs and LostAndFound jobs will automatically expire after some in-game hours to prevent stale evidence. (accepted side jobs are excluded)", "QoL.Gameplay.AutoExpireJobs")]
		bool AutoExpireJobs { get; set; }

		[Binding(true, "Should the expire time be randomized for more immersion in the game world? (If false, it will take ExpireTimeMax)", "QoL.Gameplay.RandomizeExpireTime")]
		bool RandomizeExpireTime { get; set; }

		[Binding(24, "The minimum time for expiration to occur.", "QoL.Gameplay.ExpireTimeMin")]
		int ExpireTimeMin { get; set; }

		[Binding(48, "The maximum time for expiration to occur.", "QoL.Gameplay.ExpireTimeMax")]
		int ExpireTimeMax { get; set; }
	}
	public interface IConversationBindings
	{
		[Binding(true, "Allow ending conversations with menu key", "QoL.Conversations.CanEndWithMenuKey")]
		bool EndConversationPatch { get; set; }
	}
	public interface IMainMenuBindings
	{
		[Binding(true, "Resume the game from paused state when exiting the mainmenu with menu key", "QoL.MainMenu.UnpauseGameWithMenuKey")]
		bool UnpauseGameOnMainMenuExit { get; set; }

		[Binding(true, "Skips the press any key screen at the start of the game if no joysticks are connected.", "QoL.MainMenu.SkipPressAnyKeyScreenIfNotUsingJoysticks")]
		bool SkipPressAnyKeyScreenIfNotUsingJoysticks { get; set; }
	}
	public interface IMapBindings
	{
		[Binding(true, "Fixes the center on player after zooming and moving the camera.", "QoL.Map.FixCenterOnPlayer")]
		bool FixCenterOnPlayer { get; set; }

		[Binding(true, "Zooms the minimap out by default?", "QoL.Map.ZoomOutOnStart")]
		bool ZoomOutOnStart { get; set; }

		[Binding(true, "Enlarge player marker?", "QoL.Map.EnlargePlayerMarker")]
		bool EnlargePlayerMarker { get; set; }

		[Binding("(2.5, 2.5)", "Player marker size. Game default: (1,1)", "QoL.Map.PlayerMarkerSize")]
		string PlayerMarkerSize { get; set; }

		[Binding(true, "Change player marker color?", "QoL.Map.ChangePlayerMarkerColor")]
		bool ChangePlayerMarkerColor { get; set; }

		[Binding("#008000", "The hex color code for the player marker, default: Green", "QoL.Map.PlayerMarkerColor")]
		string PlayerMarkerColor { get; set; }
	}
	[BepInPlugin("Venomaus.SOD.QoL", "QoL", "1.1.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : PluginController<Plugin, IPluginBindings>
	{
		public const string PLUGIN_GUID = "Venomaus.SOD.QoL";

		public const string PLUGIN_NAME = "QoL";

		public const string PLUGIN_VERSION = "1.1.5";

		internal static Random Random { get; } = new Random(1337);


		public override void Load()
		{
			base.Harmony.PatchAll(Assembly.GetExecutingAssembly());
			PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Plugin is patched.");
			Lib.SaveGame.OnBeforeLoad += SaveGame_OnBeforeLoad;
			Lib.SaveGame.OnBeforeNewGame += SaveGame_OnBeforeNewGame;
			Lib.SaveGame.OnAfterNewGame += SaveGame_OnAfterNewGame;
			Lib.SaveGame.OnBeforeSave += SaveGame_OnBeforeSave;
			Lib.SaveGame.OnAfterDelete += SaveGame_OnAfterDelete;
			if (base.Config.AutoExpireJobs)
			{
				Lib.Time.OnMinuteChanged += Time_OnMinuteChanged;
			}
		}

		private void SaveGame_OnAfterDelete(object sender, SaveGameArgs e)
		{
			SideJobPatches.DeleteSaveData(e.FilePath);
			LostAndFoundPatches.DeleteSaveData(e.FilePath);
		}

		private void SaveGame_OnBeforeSave(object sender, SaveGameArgs e)
		{
			SideJobPatches.SaveExpireTimes(e.FilePath);
			LostAndFoundPatches.SaveExpireTimes(e.FilePath);
		}

		private void SaveGame_OnBeforeNewGame(object sender, EventArgs e)
		{
			SideJobPatches.InitializeExpireTimes(null);
			LostAndFoundPatches.InitializeExpireTimes(null);
		}

		private void SaveGame_OnBeforeLoad(object sender, SaveGameArgs e)
		{
			SideJobPatches.InitializeExpireTimes(e.FilePath);
			LostAndFoundPatches.InitializeExpireTimes(e.FilePath);
		}

		private void Time_OnMinuteChanged(object sender, TimeChangedArgs e)
		{
			SideJobPatches.ExpireTimedOutJobs();
			LostAndFoundPatches.ExpireTimedOutLafs();
		}

		private void SaveGame_OnAfterNewGame(object sender, EventArgs e)
		{
			UnlockBusinessDoors();
		}

		private static void UnlockBusinessDoors()
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Expected O, but got Unknown
			int num = 0;
			Enumerator<Company> enumerator = CityData.Instance.companyDirectory.GetEnumerator();
			while (enumerator.MoveNext())
			{
				Company current = enumerator.Current;
				if (!((Object)(object)current.placeOfBusiness.thisAsAddress != (Object)null) || !current.IsOpenAtThisTime(SessionData.Instance.gameTime))
				{
					continue;
				}
				Enumerator<NodeAccess> enumerator2 = ((NewGameLocation)current.address).entrances.GetEnumerator();
				while (enumerator2.MoveNext())
				{
					NodeAccess current2 = enumerator2.Current;
					if ((Object)(object)current2.door != (Object)null)
					{
						current2.door.SetLocked(false, (Actor)null, false);
					}
				}
				Enumerator<NewRoom> enumerator3 = ((NewGameLocation)current.address).rooms.GetEnumerator();
				while (enumerator3.MoveNext())
				{
					enumerator3.Current.SetMainLights(true, "SOD.QoL: Unlock business door on new game.", (Actor)null, true, true);
				}
				current.SetOpen(true, true);
				num++;
			}
			ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(27, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unlocked \"");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" business doors.");
			}
			log.LogInfo(val);
		}
	}
}
namespace SOD.QoL.Patches
{
	internal class CitizenBehaviourPatches
	{
		[HarmonyPatch(typeof(CitizenBehaviour), "GameWorldCheck")]
		internal class CitizenBehaviour_GameWorldCheck
		{
			internal readonly struct PlayerState
			{
				public float Energy { get; }

				public float WellRested { get; }

				public float Alertness { get; }

				public float TickChange { get; }

				public PlayerState(float energy, float wellRested, float alertness, float tickChange)
				{
					Energy = energy;
					WellRested = wellRested;
					Alertness = alertness;
					TickChange = tickChange;
				}
			}

			[HarmonyPrefix]
			internal static void Prefix(CitizenBehaviour __instance, ref PlayerState __state)
			{
				if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.FixTiredness)
				{
					float tickChange = SessionData.Instance.gameTime - __instance.timeOnLastGameWorldUpdate;
					__state = new PlayerState(((Human)Player.Instance).energy, ((Human)Player.Instance).wellRested, ((Human)Player.Instance).alertness, tickChange);
				}
			}

			[HarmonyPostfix]
			internal static void Postfix(ref PlayerState __state)
			{
				//IL_0069: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Invalid comparison between Unknown and I4
				if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.FixTiredness)
				{
					return;
				}
				((Human)Player.Instance).energy = __state.Energy;
				((Human)Player.Instance).alertness = __state.Alertness;
				((Human)Player.Instance).wellRested = __state.WellRested;
				if (Player.Instance.spendingTimeMode && InteractionController.Instance.lockedInInteraction != null && (int)InteractionController.Instance.lockedInInteraction.preset.specialCaseFlag == 1)
				{
					if (((Human)Player.Instance).energy >= 0.8f)
					{
						((Human)Player.Instance).AddWellRested(__state.TickChange / 2f);
					}
					((Human)Player.Instance).AddEnergy(__state.TickChange / 4f);
					return;
				}
				Chapter val = default(Chapter);
				int num = default(int);
				if (!Game.Instance.disableSurvivalStatusesInStory || !Toolbox.Instance.IsStoryMissionActive(ref val, ref num))
				{
					Player instance = Player.Instance;
					((Human)instance).alertness = ((Human)instance).alertness + GameplayControls.Instance.playerTirednessRate * (0f - __state.TickChange);
					((Human)Player.Instance).alertness = Mathf.Clamp01(((Human)Player.Instance).alertness);
					Player.Instance.StatusCheckEndOfFrame();
					((Human)Player.Instance).AddEnergy(GameplayControls.Instance.playerTirednessRate * (0f - __state.TickChange));
				}
				((Human)Player.Instance).AddWellRested(__state.TickChange * -0.5f);
			}
		}
	}
	internal class ControlDetectControllerPatches
	{
		[HarmonyPatch(typeof(ControlDetectController), "Start")]
		internal class ControlDetectController_Start
		{
			[HarmonyPrefix]
			private static void Prefix(ControlDetectController __instance)
			{
				if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.SkipPressAnyKeyScreenIfNotUsingJoysticks)
				{
					Il2CppStringArray joystickNames = Input.GetJoystickNames();
					if (joystickNames != null && ((Il2CppArrayBase<string>)(object)joystickNames).Length > 0)
					{
						__instance.loadSceneTriggered = false;
						return;
					}
					PlayerPrefs.SetInt("controlMethod", 1);
					__instance.loadSceneTriggered = true;
				}
			}
		}
	}
	internal class FirstPersonItemControllerPatches
	{
		[HarmonyPatch(typeof(FirstPersonItemController), "Update")]
		internal class FirstPersonItemController_Update
		{
			internal readonly struct PlayerState
			{
				public float Alertness { get; }

				public float Energy { get; }

				public Interactable Interactable { get; }

				public PlayerState(float alertness, float energy, Interactable interactable)
				{
					Alertness = alertness;
					Energy = energy;
					Interactable = interactable;
				}
			}

			[HarmonyPrefix]
			internal static void Prefix(FirstPersonItemController __instance, ref PlayerState __state)
			{
				if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.FixTiredness || (Object)(object)Player.Instance == (Object)null || (Object)(object)BioScreenController.Instance == (Object)null)
				{
					return;
				}
				if (__instance.isConsuming && !__instance.takeOneActive && BioScreenController.Instance.selectedSlot != null && BioScreenController.Instance.selectedSlot.interactableID > -1)
				{
					Interactable interactable = BioScreenController.Instance.selectedSlot.GetInteractable();
					if (interactable != null && interactable.cs > 0f)
					{
						__state = new PlayerState(((Human)Player.Instance).alertness, ((Human)Player.Instance).energy, interactable);
					}
				}
				else
				{
					__state = new PlayerState(((Human)Player.Instance).alertness, ((Human)Player.Instance).energy, null);
				}
			}

			[HarmonyPostfix]
			internal static void Postfix(ref PlayerState __state)
			{
				//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f0: Invalid comparison between Unknown and I4
				if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.FixTiredness || (Object)(object)Player.Instance == (Object)null)
				{
					return;
				}
				((Human)Player.Instance).alertness = __state.Alertness;
				((Human)Player.Instance).energy = __state.Energy;
				if (__state.Interactable == null || !(__state.Interactable.cs > 0f))
				{
					return;
				}
				Interactable interactable = __state.Interactable;
				object obj;
				if (interactable == null)
				{
					obj = null;
				}
				else
				{
					InteractablePreset preset = interactable.preset;
					obj = ((preset != null) ? preset.retailItem : null);
				}
				if ((SoCustomComparison)obj != (SoCustomComparison)null)
				{
					Player instance = Player.Instance;
					((Human)instance).alertness = ((Human)instance).alertness + interactable.preset.retailItem.alertness / interactable.preset.consumableAmount * Time.deltaTime;
					((Human)Player.Instance).alertness = Mathf.Clamp01(((Human)Player.Instance).alertness);
					Player.Instance.StatusCheckEndOfFrame();
					if ((int)interactable.preset.retailItem.desireCategory == 2 && interactable.preset.retailItem.energy <= 0f)
					{
						interactable.preset.retailItem.energy = (float)Math.Round(interactable.preset.retailItem.alertness / 100f * (float)((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.PercentageEnergyRestore, 2);
					}
					Player instance2 = Player.Instance;
					((Human)instance2).energy = ((Human)instance2).energy + interactable.preset.retailItem.energy / interactable.preset.consumableAmount * Time.deltaTime;
					((Human)Player.Instance).energy = Mathf.Clamp01(((Human)Player.Instance).energy);
					Player.Instance.StatusCheckEndOfFrame();
				}
			}
		}

		[HarmonyPatch(typeof(FirstPersonItemController), "TakeOne")]
		internal class FirstPersonItemController_TakeOne
		{
			[HarmonyPrefix]
			internal static bool Prefix(FirstPersonItemController __instance)
			{
				//IL_0074: Unknown result type (might be due to invalid IL or missing references)
				if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.FixTiredness)
				{
					return true;
				}
				if (BioScreenController.Instance.selectedSlot != null && BioScreenController.Instance.selectedSlot.interactableID > -1)
				{
					Interactable interactable = BioScreenController.Instance.selectedSlot.GetInteractable();
					if (interactable != null)
					{
						InteractablePreset preset = interactable.preset;
						if ((SoCustomComparison)(object)((preset != null) ? preset.takeOneEvent : null) != (SoCustomComparison)null)
						{
							AudioController.Instance.Play2DSound(interactable.preset.takeOneEvent, (List<FMODParam>)null, 1f);
						}
					}
				}
				if (!__instance.takeOneActive)
				{
					RuntimeHelper.StartCoroutine(TakeOneExecute(__instance));
				}
				return false;
			}

			private static IEnumerator TakeOneExecute(FirstPersonItemController __instance)
			{
				float progress = 0f;
				__instance.takeOneActive = true;
				__instance.SetConsuming(true);
				Interactable consumable = null;
				if (BioScreenController.Instance.selectedSlot != null && BioScreenController.Instance.selectedSlot.interactableID > -1)
				{
					consumable = BioScreenController.Instance.selectedSlot.GetInteractable();
				}
				while (progress < 1f && consumable != null && consumable.cs > 0f)
				{
					float num = Time.deltaTime / 1.8f;
					if ((SoCustomComparison)(object)consumable.preset.retailItem != (SoCustomComparison)null)
					{
						((Human)Player.Instance).AddNourishment(consumable.preset.retailItem.nourishment * num);
						((Human)Player.Instance).AddHydration(consumable.preset.retailItem.hydration * num);
						HandleEnergyAndAlertness(consumable, num);
						((Human)Player.Instance).AddExcitement(consumable.preset.retailItem.excitement * num);
						((Human)Player.Instance).AddChores(consumable.preset.retailItem.chores * num);
						((Human)Player.Instance).AddHygiene(consumable.preset.retailItem.hygiene * num);
						((Human)Player.Instance).AddBladder(consumable.preset.retailItem.bladder * num);
						((Human)Player.Instance).AddHeat(consumable.preset.retailItem.heat * num);
						((Human)Player.Instance).AddDrunk(consumable.preset.retailItem.drunk * num);
						((Human)Player.Instance).AddSick(consumable.preset.retailItem.sick * num);
						((Human)Player.Instance).AddHeadache(consumable.preset.retailItem.headache * num);
						((Human)Player.Instance).AddWet(consumable.preset.retailItem.wet * num);
						((Human)Player.Instance).AddBrokenLeg(consumable.preset.retailItem.brokenLeg * num);
						((Human)Player.Instance).AddBruised(consumable.preset.retailItem.bruised * num);
						((Human)Player.Instance).AddBlackEye(consumable.preset.retailItem.blackEye * num);
						((Human)Player.Instance).AddBlackedOut(consumable.preset.retailItem.blackedOut * num);
						((Human)Player.Instance).AddNumb(consumable.preset.retailItem.numb * num);
						((Human)Player.Instance).AddBleeding(consumable.preset.retailItem.bleeding * num);
						((Human)Player.Instance).AddBreath(consumable.preset.retailItem.breath * num);
						((Human)Player.Instance).AddStarchAddiction(consumable.preset.retailItem.starchAddiction * num);
						((Human)Player.Instance).AddPoisoned(consumable.preset.retailItem.poisoned * num, (Human)null);
						((Actor)Player.Instance).AddHealth(consumable.preset.retailItem.health * num, true, false);
					}
					progress += num;
					yield return null;
				}
				if (consumable != null)
				{
					Interactable obj = consumable;
					obj.cs -= 1f;
					if (consumable.cs <= 0f)
					{
						__instance.OnConsumableFinished(consumable);
						if (consumable.preset.destroyWhenAllConsumed)
						{
							__instance.EmptySlot(BioScreenController.Instance.selectedSlot, false, true, true, false);
							int childCount = __instance.rightHandObjectParent.GetChildCount();
							List<Transform> list = new List<Transform>();
							for (int i = 0; i < childCount; i++)
							{
								list.Add(__instance.rightHandObjectParent.GetChild(i));
							}
							foreach (Transform item in list)
							{
								Object.Destroy((Object)(object)((Component)item).gameObject);
							}
						}
					}
				}
				__instance.takeOneActive = false;
				__instance.SetConsuming(false);
				if (consumable.cs <= 0f)
				{
					__instance.RefreshHeldObjects();
				}
			}

			private static void HandleEnergyAndAlertness(Interactable consumable, float num)
			{
				//IL_0051: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Invalid comparison between Unknown and I4
				Player instance = Player.Instance;
				((Human)instance).alertness = ((Human)instance).alertness + consumable.preset.retailItem.alertness * num;
				((Human)Player.Instance).alertness = Mathf.Clamp01(((Human)Player.Instance).alertness);
				Player.Instance.StatusCheckEndOfFrame();
				if ((int)consumable.preset.retailItem.desireCategory == 2 && consumable.preset.retailItem.energy <= 0f)
				{
					consumable.preset.retailItem.energy = (float)Math.Round(consumable.preset.retailItem.alertness / 100f * (float)((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.PercentageEnergyRestore, 2);
				}
				Player instance2 = Player.Instance;
				((Human)instance2).energy = ((Human)instance2).energy + consumable.preset.retailItem.energy * num;
				((Human)Player.Instance).energy = Mathf.Clamp01(((Human)Player.Instance).energy);
				Player.Instance.StatusCheckEndOfFrame();
			}
		}
	}
	internal class InputPatches
	{
		[HarmonyPatch(typeof(InputController), "Update")]
		internal class InputController_Update
		{
			[HarmonyPrefix]
			internal static bool Prefix(InputController __instance)
			{
				try
				{
					if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.EndConversationPatch && !((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.UnpauseGameOnMainMenuExit)
					{
						return true;
					}
					if (!((Behaviour)__instance).enabled || !ReInput.isReady || PopupMessageController.Instance.active)
					{
						return true;
					}
					if (__instance.player != null && __instance.player.GetButtonDown("Menu"))
					{
						if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.EndConversationPatch && SessionData.Instance.startedGame && SessionData.Instance.play && !MainMenuController.Instance.mainMenuActive && ((Actor)Player.Instance).interactingWith != null && ((Actor)Player.Instance).interactingWith.objectRef != null && TypeExtensions.IsAssignableFrom(((Actor)Player.Instance).interactingWith.objectRef, typeof(Actor)))
						{
							ActionController.Instance.Return((Interactable)null, (NewNode)null, (Actor)(object)Player.Instance);
							return false;
						}
						if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.UnpauseGameOnMainMenuExit && SessionData.Instance.startedGame && !SessionData.Instance.play && MainMenuController.Instance.mainMenuActive && ((Object)(object)CityConstructor.Instance == (Object)null || !CityConstructor.Instance.preSimActive))
						{
							SessionData.Instance.ResumeGame();
							MainMenuController.Instance.EnableMainMenu(false, true, true, (Component)1);
							return false;
						}
					}
					return true;
				}
				catch (Exception)
				{
					return true;
				}
			}
		}
	}
	internal class LostAndFoundPatches
	{
		private const string _expirationSaveFile = "LostAndFoundSaveData_{0}.json";

		private static ExpirationSaveData _expirationSaveData;

		private static readonly List<(NewBuilding, LostAndFound)> _toBeDeleted = new List<(NewBuilding, LostAndFound)>();

		internal static void ExpireTimedOutLafs()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			if (!Lib.Time.IsInitialized || _expirationSaveData == null)
			{
				return;
			}
			CityBuildings instance = HighlanderSingleton<CityBuildings>.Instance;
			if ((Object)(object)instance == (Object)null || instance.buildingDirectory == null)
			{
				return;
			}
			TimeData currentDateTime = Lib.Time.CurrentDateTime;
			Enumerator<NewBuilding> enumerator = instance.buildingDirectory.GetEnumerator();
			while (enumerator.MoveNext())
			{
				NewBuilding current = enumerator.Current;
				if (current.lostAndFound == null)
				{
					continue;
				}
				Enumerator<LostAndFound> enumerator2 = current.lostAndFound.GetEnumerator();
				while (enumerator2.MoveNext())
				{
					LostAndFound current2 = enumerator2.Current;
					string uniqueId = GetUniqueId(current2);
					if (_expirationSaveData.Expirations.TryGetValue(uniqueId, out var value))
					{
						if (value <= currentDateTime)
						{
							ExpireLostAndFound(current, current2);
							_expirationSaveData.Expirations.Remove(uniqueId);
						}
					}
					else
					{
						int num = (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.RandomizeExpireTime ? Plugin.Random.Next(((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ExpireTimeMin, ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ExpireTimeMax + 1) : ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ExpireTimeMax);
						Dictionary<string, TimeData> expirations = _expirationSaveData.Expirations;
						TimeData currentDateTime2 = Lib.Time.CurrentDateTime;
						expirations[uniqueId] = ((TimeData)(ref currentDateTime2)).AddHours(num);
					}
				}
			}
			foreach (var item in _toBeDeleted)
			{
				item.Item1.lostAndFound.Remove(item.Item2);
				if (item.Item1.preset.maxLostAndFound - item.Item1.lostAndFound.Count > 0)
				{
					item.Item1.TriggerNewLostAndFound();
				}
			}
			_toBeDeleted.Clear();
		}

		internal static void InitializeExpireTimes(string saveFilePath)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			Lazy<string> lazy = new Lazy<string>(delegate
			{
				string text = $"LostAndFoundSaveData_{Lib.SaveGame.GetUniqueString(saveFilePath)}.json";
				return Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), text);
			});
			if (saveFilePath == null || !File.Exists(lazy.Value))
			{
				_expirationSaveData = new ExpirationSaveData
				{
					Expirations = new Dictionary<string, TimeData>()
				};
				return;
			}
			try
			{
				new JsonSerializerOptions
				{
					Converters = { (JsonConverter)new TimeDataJsonConverter() },
					WriteIndented = true
				};
				_expirationSaveData = ExpirationSaveData.Deserialize(File.ReadAllText(lazy.Value));
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Loaded LostAndFoundSaveData from file.");
			}
			catch (Exception ex)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(55, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to read LostAndFoundSaveData file (corrupted?): ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
				_expirationSaveData = new ExpirationSaveData
				{
					Expirations = new Dictionary<string, TimeData>()
				};
			}
		}

		internal static void SaveExpireTimes(string saveFilePath)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			if (_expirationSaveData == null || _expirationSaveData.Expirations.Count <= 0)
			{
				return;
			}
			string text = $"LostAndFoundSaveData_{Lib.SaveGame.GetUniqueString(saveFilePath)}.json";
			string savestoreDirectoryPath = Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), text);
			try
			{
				string contents = _expirationSaveData.Serialize();
				File.WriteAllText(savestoreDirectoryPath, contents);
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Saved LostAndFoundSaveData to file.");
			}
			catch (Exception ex)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(45, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to save LostAndFoundSaveData to file: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
			}
		}

		internal static void DeleteSaveData(string saveFilePath)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			string text = $"LostAndFoundSaveData_{Lib.SaveGame.GetUniqueString(saveFilePath)}.json";
			string savestoreDirectoryPath = Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), text);
			if (!File.Exists(savestoreDirectoryPath))
			{
				return;
			}
			try
			{
				File.Delete(savestoreDirectoryPath);
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Deleted LostAndFoundSaveData file");
			}
			catch (Exception ex)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(44, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to delete LostAndFoundSaveData file: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogInfo(val);
			}
		}

		internal static string GetUniqueId(LostAndFound laf)
		{
			string text = laf.preset + "|" + laf.buildingID + "|" + laf.ownerID + "|" + laf.spawnedItem + "|" + laf.spawnedNote;
			return Lib.SaveGame.GetUniqueString(text);
		}

		internal static void ExpireLostAndFound(NewBuilding building, LostAndFound laf)
		{
			Interactable val = default(Interactable);
			if (CityData.Instance.savableInteractableDictionary.TryGetValue(laf.spawnedNote, ref val))
			{
				val.MarkAsTrash(true, false, 0f);
				val.SafeDelete(false);
			}
			if (CityData.Instance.savableInteractableDictionary.TryGetValue(laf.spawnedItem, ref val))
			{
				val.MarkAsTrash(true, false, 0f);
			}
			_toBeDeleted.Add((building, laf));
		}
	}
	internal class MapPatches
	{
		[HarmonyPatch(typeof(MapController), "Setup")]
		internal class MapController_Setup
		{
			[HarmonyPrefix]
			internal static void Prefix()
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0079: Unknown result type (might be due to invalid IL or missing references)
				if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ChangePlayerMarkerColor)
				{
					Color color = GetColor(((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.PlayerMarkerColor);
					PrefabControls.Instance.characterMarkerColor = color;
				}
				if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.EnlargePlayerMarker)
				{
					var (num, num2) = ExtractFloats(((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.PlayerMarkerSize);
					PrefabControls.Instance.playerMarker.transform.localScale = new Vector3(num, num2, 1f);
				}
			}

			[HarmonyPostfix]
			internal static void Postfix(MapController __instance)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ZoomOutOnStart)
				{
					float x = __instance.zoomController.zoomLimit.x;
					__instance.zoomController.SetPivotPoint(0f, (ZoomPivot)1);
					__instance.zoomController.SetZoom(x);
					((Transform)((Component)__instance.zoomController).GetComponent<RectTransform>()).localScale = new Vector3(x, x, 1f);
				}
			}

			private static Color GetColor(string hexColor)
			{
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				hexColor = hexColor.TrimStart('#');
				if (hexColor.Length != 6)
				{
					PluginController<Plugin, IPluginBindings>.Log.LogWarning((object)"Invalid hex color code in configuration, it should be in the format #RRGGBB. Falling back to default green color.");
					hexColor = "008000";
				}
				int num = int.Parse(hexColor.Substring(0, 2), NumberStyles.HexNumber);
				int num2 = int.Parse(hexColor.Substring(2, 2), NumberStyles.HexNumber);
				int num3 = int.Parse(hexColor.Substring(4, 2), NumberStyles.HexNumber);
				return new Color((float)num, (float)num2, (float)num3);
			}

			private static (float x, float y) ExtractFloats(string input)
			{
				string pattern = "\\((-?\\d+(\\.\\d+)?), (-?\\d+(\\.\\d+)?)\\)";
				Match match = Regex.Match(input, pattern);
				if (match.Success)
				{
					float item = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
					float item2 = float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture);
					return (item, item2);
				}
				PluginController<Plugin, IPluginBindings>.Log.LogWarning((object)"Invalid player marker size configuration, using the default value (2.5, 2.5)");
				return (2.5f, 2.5f);
			}
		}

		[HarmonyPatch(typeof(MapController), "CentreOnObject")]
		internal class MapController_CentreOnObject
		{
			private static bool _calledBefore;

			[HarmonyPostfix]
			internal static void Postfix(MapController __instance, RectTransform mapObj, bool instant, bool showPointer)
			{
				if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.FixCenterOnPlayer)
				{
					if (_calledBefore)
					{
						_calledBefore = false;
						return;
					}
					_calledBefore = true;
					__instance.CentreOnObject(mapObj, instant, showPointer);
				}
			}
		}
	}
	internal class RoutePlotPatches
	{
		[HarmonyPatch(typeof(EvidenceLocationalControls), "OnPlotRoute")]
		internal class EvidenceLocationalControls_OnPlotRoute
		{
			[HarmonyPrefix]
			internal static bool Prefix(EvidenceLocationalControls __instance)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_0099: Unknown result type (might be due to invalid IL or missing references)
				//IL_009e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
				Vector3Int? val = null;
				if (MapController.Instance.playerRoute != null)
				{
					val = MapController.Instance.playerRoute.end.nodeCoord;
					MapController.Instance.playerRoute.Remove();
					if ((Object)(object)__instance.plotRouteButton != (Object)null)
					{
						((Graphic)((Selectable)__instance.plotRouteButton.button).image).color = __instance.plotRouteButton.baseColour;
					}
				}
				MapController.Instance.PlotPlayerRoute(__instance.parentWindow.passedEvidence);
				if (val.HasValue)
				{
					Vector3Int nodeCoord = MapController.Instance.playerRoute.end.nodeCoord;
					Vector3Int? val2 = val;
					if (val2.HasValue && nodeCoord == val2.GetValueOrDefault())
					{
						MapController.Instance.playerRoute.Remove();
					}
				}
				return false;
			}
		}
	}
	internal class SideJobPatches
	{
		private const string _expirationSaveFile = "SideJobSaveData_{0}.json";

		private static ExpirationSaveData _expirationSaveData;

		internal static void ExpireTimedOutJobs()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Invalid comparison between Unknown and I4
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			SideJobController instance = SideJobController.Instance;
			if (!Lib.Time.IsInitialized || (Object)(object)instance == (Object)null || _expirationSaveData == null)
			{
				return;
			}
			TimeData currentDateTime = Lib.Time.CurrentDateTime;
			for (int i = 0; i < instance.jobTracking.Count; i++)
			{
				JobTracking val = instance.jobTracking[i];
				for (int j = 0; j < val.activeJobs.Count; j++)
				{
					SideJob val2 = val.activeJobs[j];
					if (_expirationSaveData.Expirations.ContainsKey(val2.jobID.ToString()) && val2.accepted)
					{
						_expirationSaveData.Expirations.Remove(val2.jobID.ToString());
					}
					else
					{
						if (val2.accepted || (int)val2.state != 1)
						{
							continue;
						}
						if (_expirationSaveData.Expirations.TryGetValue(val2.jobID.ToString(), out var value))
						{
							if (value <= currentDateTime)
							{
								val2.End();
								_expirationSaveData.Expirations.Remove(val2.jobID.ToString());
							}
						}
						else
						{
							int num = (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.RandomizeExpireTime ? Plugin.Random.Next(((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ExpireTimeMin, ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ExpireTimeMax + 1) : ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ExpireTimeMax);
							Dictionary<string, TimeData> expirations = _expirationSaveData.Expirations;
							string key = val2.jobID.ToString();
							TimeData currentDateTime2 = Lib.Time.CurrentDateTime;
							expirations[key] = ((TimeData)(ref currentDateTime2)).AddHours(num);
						}
					}
				}
			}
		}

		internal static void InitializeExpireTimes(string saveFilePath)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			Lazy<string> lazy = new Lazy<string>(delegate
			{
				string text = $"SideJobSaveData_{Lib.SaveGame.GetUniqueString(saveFilePath)}.json";
				return Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), text);
			});
			if (saveFilePath == null || !File.Exists(lazy.Value))
			{
				_expirationSaveData = new ExpirationSaveData
				{
					Expirations = new Dictionary<string, TimeData>()
				};
				return;
			}
			try
			{
				new JsonSerializerOptions
				{
					Converters = { (JsonConverter)new TimeDataJsonConverter() },
					WriteIndented = true
				};
				_expirationSaveData = ExpirationSaveData.Deserialize(File.ReadAllText(lazy.Value));
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Loaded SideJobSaveData from file.");
			}
			catch (Exception ex)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(50, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to read SideJobSaveData file (corrupted?): ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
				_expirationSaveData = new ExpirationSaveData
				{
					Expirations = new Dictionary<string, TimeData>()
				};
			}
		}

		internal static void SaveExpireTimes(string saveFilePath)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			if (_expirationSaveData == null || _expirationSaveData.Expirations.Count <= 0)
			{
				return;
			}
			string text = $"SideJobSaveData_{Lib.SaveGame.GetUniqueString(saveFilePath)}.json";
			string savestoreDirectoryPath = Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), text);
			try
			{
				string contents = _expirationSaveData.Serialize();
				File.WriteAllText(savestoreDirectoryPath, contents);
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Saved SideJobSaveData to file.");
			}
			catch (Exception ex)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(51, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to save side job expiration timers to file: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
			}
		}

		internal static void DeleteSaveData(string saveFilePath)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			string text = $"SideJobSaveData_{Lib.SaveGame.GetUniqueString(saveFilePath)}.json";
			string savestoreDirectoryPath = Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), text);
			if (!File.Exists(savestoreDirectoryPath))
			{
				return;
			}
			try
			{
				File.Delete(savestoreDirectoryPath);
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Deleted SideJobSaveData file");
			}
			catch (Exception ex)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to delete SideJobSaveData file: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogInfo(val);
			}
		}
	}
	internal class ToolboxPatches
	{
		[HarmonyPatch(typeof(Toolbox), "LoadAll")]
		internal static class Toolbox_LoadAll
		{
			[HarmonyPostfix]
			internal static void Postfix(Toolbox __instance)
			{
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: Expected O, but got Unknown
				if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.FixTiredness)
				{
					return;
				}
				bool flag = default(bool);
				foreach (RetailItemPreset item in EnumerableExtensions.Where<RetailItemPreset>(__instance.allItems, (Func<RetailItemPreset, bool>)((RetailItemPreset a) => (int)a.desireCategory == 2)))
				{
					if (item.energy <= 0f)
					{
						item.energy = (float)Math.Round(item.alertness / 100f * 12f, 2);
						ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
						BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(44, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Adjusted energy restore amount for \"");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)item).name);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" to \"");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(item.energy);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\".");
						}
						log.LogInfo(val);
					}
				}
			}
		}
	}
}
namespace SOD.QoL.Objects
{
	internal class ExpirationSaveData
	{
		public Dictionary<string, TimeData> Expirations { get; set; }

		public string Serialize()
		{
			JsonSerializerOptions options = new JsonSerializerOptions
			{
				Converters = { (JsonConverter)new TimeDataJsonConverter() },
				WriteIndented = true
			};
			return JsonSerializer.Serialize(this, options);
		}

		public static ExpirationSaveData Deserialize(string json)
		{
			JsonSerializerOptions options = new JsonSerializerOptions
			{
				Converters = { (JsonConverter)new TimeDataJsonConverter() },
				WriteIndented = true
			};
			return JsonSerializer.Deserialize<ExpirationSaveData>(json, options);
		}
	}
	internal class TimeDataJsonConverter : JsonConverter<TimeData>
	{
		public override TimeData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (reader.TokenType == JsonTokenType.String)
			{
				return TimeData.Deserialize(reader.GetString());
			}
			throw new JsonException("Expected a string for TimeData.");
		}

		public override void Write(Utf8JsonWriter writer, TimeData value, JsonSerializerOptions options)
		{
			writer.WriteStringValue(((TimeData)(ref value)).Serialize());
		}
	}
}