Decompiled source of ActuallyWorkingEmployees v1.0.0

ActuallyWorkingEmployees.dll

Decompiled 14 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using ActuallyWorkingEmployees;
using ActuallyWorkingEmployees.Compat;
using ActuallyWorkingEmployees.Config;
using ActuallyWorkingEmployees.Features;
using ActuallyWorkingEmployees.Features.Diagnostics;
using ActuallyWorkingEmployees.Features.Packaging;
using ActuallyWorkingEmployees.Features.Reliability;
using ActuallyWorkingEmployees.Networking;
using DooDesch;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppFishNet.Connection;
using Il2CppFishNet.Managing;
using Il2CppFishNet.Object;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.Core.Items.Framework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Employees;
using Il2CppScheduleOne.EntityFramework;
using Il2CppScheduleOne.GameTime;
using Il2CppScheduleOne.Growing;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Management;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.NPCs.Behaviour;
using Il2CppScheduleOne.Networking;
using Il2CppScheduleOne.ObjectScripts;
using Il2CppScheduleOne.Property;
using Il2CppScheduleOne.StationFramework;
using Il2CppScheduleOne.Tools;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using S1API.Lifecycle;
using Sideload.Api;
using UnityEngine;
using UnityEngine.AI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "Actually Working Employees", "1.0.0", "DooDesch", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees")]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: AssemblyMetadata("SourceNotice", "ActuallyWorkingEmployees by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG.")]
[assembly: AssemblyMetadata("License", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees/blob/main/LICENSE.md")]
[assembly: AssemblyMetadata("SourceCode", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees")]
[assembly: AssemblyMetadata("tdm-reservation", "1")]
[assembly: AssemblyMetadata("tdm-policy", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees/blob/main/LICENSE.md")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch. MIT License. Source code: https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees")]
[assembly: AssemblyDescription("Employees that keep working, with every employee option in a phone app.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d53d6ac3326c0d97d293bd8954a466a1ca00f612")]
[assembly: AssemblyProduct("Actually Working Employees")]
[assembly: AssemblyTitle("Actually Working Employees")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DooDesch
{
	[AttributeUsage(AttributeTargets.All, Inherited = false)]
	internal sealed class SourceNoticeAttribute : Attribute
	{
		public string Notice { get; }

		public SourceNoticeAttribute(string notice)
		{
			Notice = notice;
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class SourceNotice
	{
		internal const string Text = "ActuallyWorkingEmployees by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG.";

		internal const string Short = "ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.";
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class ModVersion
	{
		internal const string Current = "1.0.0";
	}
}
namespace Sideload.Api
{
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	public static class Apps
	{
		private static bool _bound;

		private static int _probeAttempts;

		private static readonly List<Action> _pending = new List<Action>();

		private static Action<string, string, string, string, Assembly> _registerApp;

		private static Action<string, string, Func<string, string, string>> _handle;

		private static Action<string, string, string> _emit;

		private static Action<string, string> _allowHost;

		private static Action<string, string> _declareOrientations;

		private static Action<string, int> _setBadge;

		private static Action<string, string, string> _notify;

		private static Action<string, string, string, float> _notifyFor;

		private static Func<string, bool> _isOnScreen;

		private static Action<string, string, byte[]> _setImage;

		private static Action<string, bool> _setIconHidden;

		private static Action<string, bool> _setAppOpen;

		private static Func<string, bool> _isAppOpen;

		private static Func<bool, bool> _setPhoneRaised;

		private static Func<bool> _isPhoneRaised;

		private static Action<string, string, Func<string, string, bool>> _claimKeys;

		private static Func<object, string, string, Assembly, float, bool> _mountSurface;

		private static Action<string> _unmountSurface;

		private static Func<string, bool> _isSurfaceMounted;

		private static Action<string> _reloadApp;

		private static Action<string> _unregisterApp;

		private static Action<string, string> _allowGameWrites;

		public static bool Available
		{
			get
			{
				EnsureBound();
				return _bound;
			}
		}

		internal static bool HasLiveEditing
		{
			get
			{
				EnsureBound();
				if (_reloadApp != null)
				{
					return _unregisterApp != null;
				}
				return false;
			}
		}

		internal static bool HasSurfaces
		{
			get
			{
				EnsureBound();
				return _mountSurface != null;
			}
		}

		internal static bool HasKeys
		{
			get
			{
				EnsureBound();
				return _claimKeys != null;
			}
		}

		internal static bool HasPhone
		{
			get
			{
				EnsureBound();
				return _setPhoneRaised != null;
			}
		}

		internal static bool HasOpen
		{
			get
			{
				EnsureBound();
				if (_setAppOpen != null)
				{
					return _setIconHidden != null;
				}
				return false;
			}
		}

		public static AppHandle Register(string id, string bundlePrefix, string title = null, string iconLabel = null, Assembly hostAssembly = null)
		{
			AppHandle result = new AppHandle(id);
			if (string.IsNullOrEmpty(id))
			{
				return result;
			}
			Assembly asm = hostAssembly ?? Assembly.GetCallingAssembly();
			string t = title;
			string il = iconLabel;
			string prefix = bundlePrefix;
			EnsureBound();
			if (_registerApp != null)
			{
				_registerApp(id, t, il, prefix, asm);
			}
			else
			{
				_pending.Add(delegate
				{
					_registerApp?.Invoke(id, t, il, prefix, asm);
				});
			}
			return result;
		}

		internal static void WhenBound(Action work)
		{
			if (work != null)
			{
				EnsureBound();
				if (_bound)
				{
					work();
				}
				else
				{
					_pending.Add(work);
				}
			}
		}

		internal static void HandleCall(string appId, string name, Func<string, string> handler)
		{
			if (_handle != null && handler != null)
			{
				_handle(appId, name, (string app, string argument) => handler(argument));
			}
		}

		internal static void EmitEvent(string appId, string name, string payload)
		{
			_emit?.Invoke(appId, name, payload);
		}

		internal static void AllowNetHost(string appId, string host)
		{
			_allowHost?.Invoke(appId, host);
		}

		internal static void Orient(string appId, string orientations)
		{
			_declareOrientations?.Invoke(appId, orientations);
		}

		internal static void Badge(string appId, int count)
		{
			_setBadge?.Invoke(appId, count);
		}

		internal static void Notify(string appId, string title, string subtitle, float seconds)
		{
			if (_notifyFor != null)
			{
				_notifyFor(appId, title, subtitle, seconds);
			}
			else if (_notify != null)
			{
				_notify(appId, title, subtitle);
			}
		}

		internal static bool OnScreen(string appId)
		{
			if (_isOnScreen != null)
			{
				return _isOnScreen(appId);
			}
			return false;
		}

		internal static void SetImage(string appId, string name, byte[] png)
		{
			_setImage?.Invoke(appId, name, png);
		}

		internal static void HideIcon(string appId, bool hidden)
		{
			_setIconHidden?.Invoke(appId, hidden);
		}

		internal static void OpenApp(string appId, bool open)
		{
			_setAppOpen?.Invoke(appId, open);
		}

		internal static bool AppIsOpen(string appId)
		{
			if (_isAppOpen != null)
			{
				return _isAppOpen(appId);
			}
			return false;
		}

		internal static bool RaisePhone(bool raised)
		{
			if (_setPhoneRaised != null)
			{
				return _setPhoneRaised(raised);
			}
			return false;
		}

		internal static bool PhoneIsRaised()
		{
			if (_isPhoneRaised != null)
			{
				return _isPhoneRaised();
			}
			return false;
		}

		internal static void ClaimKeys(string appId, string keys, Func<string, string, bool> handler)
		{
			_claimKeys?.Invoke(appId, keys, handler);
		}

		internal static bool MountSurface(object rect, string id, string prefix, Assembly host, float shortSide)
		{
			if (_mountSurface != null)
			{
				return _mountSurface(rect, id, prefix, host, shortSide);
			}
			return false;
		}

		internal static void UnmountSurface(string id)
		{
			_unmountSurface?.Invoke(id);
		}

		internal static bool SurfaceIsMounted(string id)
		{
			if (_isSurfaceMounted != null)
			{
				return _isSurfaceMounted(id);
			}
			return false;
		}

		internal static void ReloadApp(string appId)
		{
			_reloadApp?.Invoke(appId);
		}

		internal static void UnregisterApp(string appId)
		{
			_unregisterApp?.Invoke(appId);
		}

		internal static void AllowGameWrites(string appId, string scope)
		{
			_allowGameWrites?.Invoke(appId, scope);
		}

		private static void EnsureBound()
		{
			if (_bound)
			{
				return;
			}
			try
			{
				Type type = FindBridge(_probeAttempts++ % 30 == 0);
				if (type == null || (type.GetField("AbiVersion", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is int num && num < 1))
				{
					return;
				}
				_registerApp = Get<Action<string, string, string, string, Assembly>>(type, "RegisterApp");
				if (_registerApp == null)
				{
					return;
				}
				_handle = Get<Action<string, string, Func<string, string, string>>>(type, "Handle");
				_emit = Get<Action<string, string, string>>(type, "Emit");
				_allowHost = Get<Action<string, string>>(type, "AllowHost");
				_declareOrientations = Get<Action<string, string>>(type, "DeclareOrientations");
				_setBadge = Get<Action<string, int>>(type, "SetBadge");
				_notify = Get<Action<string, string, string>>(type, "Notify");
				_notifyFor = Get<Action<string, string, string, float>>(type, "NotifyFor");
				_isOnScreen = Get<Func<string, bool>>(type, "IsAppOnScreen");
				_setImage = Get<Action<string, string, byte[]>>(type, "SetImage");
				_setIconHidden = Get<Action<string, bool>>(type, "SetIconHidden");
				_setAppOpen = Get<Action<string, bool>>(type, "SetAppOpen");
				_isAppOpen = Get<Func<string, bool>>(type, "IsAppOpen");
				_setPhoneRaised = Get<Func<bool, bool>>(type, "SetPhoneRaised");
				_isPhoneRaised = Get<Func<bool>>(type, "IsPhoneRaised");
				_claimKeys = Get<Action<string, string, Func<string, string, bool>>>(type, "ClaimKeys");
				_mountSurface = Get<Func<object, string, string, Assembly, float, bool>>(type, "MountSurface");
				_unmountSurface = Get<Action<string>>(type, "UnmountSurface");
				_isSurfaceMounted = Get<Func<string, bool>>(type, "IsSurfaceMounted");
				_reloadApp = Get<Action<string>>(type, "ReloadApp");
				_unregisterApp = Get<Action<string>>(type, "UnregisterApp");
				_allowGameWrites = Get<Action<string, string>>(type, "AllowGameWrites");
				_bound = true;
				for (int i = 0; i < _pending.Count; i++)
				{
					try
					{
						_pending[i]();
					}
					catch
					{
					}
				}
				_pending.Clear();
			}
			catch
			{
			}
		}

		private static T Get<T>(Type t, string field) where T : class
		{
			return t.GetField(field, BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as T;
		}

		private static Type FindBridge(bool scan)
		{
			Type type = Type.GetType("Sideload.Bridge.SideloadBridge, Sideload", throwOnError: false);
			if (type != null || !scan)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetType("Sideload.Bridge.SideloadBridge", throwOnError: false);
					if (type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	public sealed class AppHandle
	{
		private readonly string _id;

		public string Id => _id;

		public bool IsOnScreen
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.OnScreen(_id);
				}
				return false;
			}
		}

		public bool IsOpen
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.AppIsOpen(_id);
				}
				return false;
			}
		}

		public static bool CanOpenProgrammatically
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasOpen;
				}
				return false;
			}
		}

		public static bool CanClaimKeys
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasKeys;
				}
				return false;
			}
		}

		public static bool CanEditLive
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasLiveEditing;
				}
				return false;
			}
		}

		internal AppHandle(string id)
		{
			_id = id ?? "";
		}

		public AppHandle OnCall(string name, Func<string, string> handler)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.HandleCall(id, name, handler);
			});
			return this;
		}

		public void Emit(string name, string payload = "")
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.EmitEvent(id, name, payload);
			});
		}

		public AppHandle AllowHost(string host)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.AllowNetHost(id, host);
			});
			return this;
		}

		public AppHandle Orientation(params string[] supported)
		{
			string id = _id;
			string list = ((supported == null) ? "" : string.Join(",", supported));
			Apps.WhenBound(delegate
			{
				Apps.Orient(id, list);
			});
			return this;
		}

		public AppHandle Badge(int count)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.Badge(id, count);
			});
			return this;
		}

		public AppHandle Notify(string title, string subtitle = "", float seconds = 0f)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.Notify(id, title, subtitle, seconds);
			});
			return this;
		}

		public AppHandle Image(string name, byte[] png)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.SetImage(id, name, png);
			});
			return this;
		}

		public AppHandle Icon(bool visible)
		{
			Apps.HideIcon(_id, !visible);
			return this;
		}

		public AppHandle NoIcon()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.HideIcon(id, hidden: true);
			});
			return this;
		}

		public AppHandle Open()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.OpenApp(id, open: true);
			});
			return this;
		}

		public AppHandle Close()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.OpenApp(id, open: false);
			});
			return this;
		}

		public bool Show()
		{
			if (!Apps.RaisePhone(raised: true))
			{
				return false;
			}
			Apps.OpenApp(_id, open: true);
			return true;
		}

		public AppHandle OnKey(string keys, Func<string, bool> handler)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.ClaimKeys(id, keys, (handler == null) ? null : ((Func<string, string, bool>)((string _, string key) => handler(key))));
			});
			return this;
		}

		public AppHandle Reload()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.ReloadApp(id);
			});
			return this;
		}

		public void Unregister()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.UnregisterApp(id);
			});
		}

		public AppHandle AllowGameWrites(string scope)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.AllowGameWrites(id, scope);
			});
			return this;
		}

		public AppHandle Hide()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.OpenApp(id, open: false);
				Apps.RaisePhone(raised: false);
			});
			return this;
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	public static class Surfaces
	{
		public static bool Available
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasSurfaces;
				}
				return false;
			}
		}

		public static SurfaceHandle Mount(object hostRect, string id, string bundlePrefix, float designShortSide = 0f, Assembly hostAssembly = null)
		{
			SurfaceHandle result = new SurfaceHandle(id);
			if (string.IsNullOrEmpty(id) || hostRect == null)
			{
				return result;
			}
			Assembly host = hostAssembly ?? Assembly.GetCallingAssembly();
			Apps.MountSurface(hostRect, id, bundlePrefix, host, designShortSide);
			return result;
		}

		public static void Unmount(string id)
		{
			Apps.UnmountSurface(id);
		}

		public static bool IsMounted(string id)
		{
			return Apps.SurfaceIsMounted(id);
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	public sealed class SurfaceHandle
	{
		private readonly string _id;

		public string Id => _id;

		internal SurfaceHandle(string id)
		{
			_id = id;
		}

		public SurfaceHandle OnCall(string name, Func<string, string> handler)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.HandleCall(id, name, handler);
			});
			return this;
		}

		public SurfaceHandle Emit(string name, string payload = "")
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.EmitEvent(id, name, payload);
			});
			return this;
		}

		public SurfaceHandle AllowHost(string host)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.AllowNetHost(id, host);
			});
			return this;
		}

		public SurfaceHandle Image(string name, byte[] png)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.SetImage(id, name, png);
			});
			return this;
		}

		public void Unmount()
		{
			Apps.UnmountSurface(_id);
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	public static class PhoneScreen
	{
		public static bool IsRaised
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.PhoneIsRaised();
				}
				return false;
			}
		}

		public static bool Available
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasPhone;
				}
				return false;
			}
		}

		public static bool Raise()
		{
			return Apps.RaisePhone(raised: true);
		}

		public static bool Lower()
		{
			return Apps.RaisePhone(raised: false);
		}
	}
}
namespace ActuallyWorkingEmployees
{
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	public sealed class Core : MelonMod
	{
		private static readonly HashSet<string> _reported = new HashSet<string>();

		public static Core Instance { get; private set; }

		public static Instance Log { get; private set; }

		public static bool InWorld { get; private set; }

		public static bool Verbose => Options.VerboseLogging.Value;

		internal static event Action OnceKeysReset;

		public static void LogDebug(string msg)
		{
			if (Verbose)
			{
				Instance log = Log;
				if (log != null)
				{
					log.Msg(msg);
				}
			}
		}

		private static void ResetOnceKeys()
		{
			_reported.Clear();
			Action onceKeysReset = Core.OnceKeysReset;
			if (onceKeysReset == null)
			{
				return;
			}
			Delegate[] invocationList = onceKeysReset.GetInvocationList();
			foreach (Delegate obj in invocationList)
			{
				try
				{
					((Action)obj)();
				}
				catch (Exception e)
				{
					Failed("core: reset log-once keys (" + obj.Method.DeclaringType?.Name + ")", e);
				}
			}
		}

		internal static void Caught(string where, Exception e)
		{
			if (_reported.Add(where))
			{
				Instance log = Log;
				if (log != null)
				{
					log.Warning("[Caught] " + where + ": " + e.GetType().Name + ": " + e.Message);
				}
			}
		}

		internal static void WarnOnce(string where, string message)
		{
			if (_reported.Add(where))
			{
				Instance log = Log;
				if (log != null)
				{
					log.Warning("[" + where + "] " + message);
				}
			}
		}

		internal static void Failed(string where, Exception e)
		{
			if (_reported.Add(where))
			{
				Instance log = Log;
				if (log != null)
				{
					log.Error("[Failed] " + where + ": " + e.GetType().Name + ": " + e.Message);
				}
			}
		}

		public override void OnInitializeMelon()
		{
			Instance = this;
			Log = ((MelonBase)this).LoggerInstance;
			NativeMethods.Flush();
			Options.Initialize();
			OnceKeysReset += Rel.ResetOnceKeys;
			FeatureHost.Discover();
			GameLifecycle.OnPreLoad += ResetState;
			GameLifecycle.OnPreSceneChange += ResetState;
			Log.Msg($"Actually Working Employees v{"1.0.0"} loaded, {Options.All.Count} options.");
		}

		public override void OnLateInitializeMelon()
		{
			HweDetector.Check();
			NativeMethods.ReportSharedPatchedAddresses();
		}

		private static void ResetState()
		{
			ResetOnceKeys();
			FeatureHost.ResetAll();
			Instance log = Log;
			if (log != null)
			{
				log.Msg("[Core] State reset (save load or scene change).");
			}
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			if (!(sceneName != "Main"))
			{
				InWorld = true;
				Instance log = Log;
				if (log != null)
				{
					log.Msg("[Core] Main scene loaded.");
				}
			}
		}

		public override void OnSceneWasUnloaded(int buildIndex, string sceneName)
		{
			if (!(sceneName != "Main"))
			{
				InWorld = false;
				ResetState();
			}
		}

		public override void OnUpdate()
		{
			FeatureHost.TickAll(Time.deltaTime);
			OptionsFile.Tick();
		}

		public override void OnApplicationQuit()
		{
			OptionsFile.Flush();
		}
	}
}
namespace ActuallyWorkingEmployees.Networking
{
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class Net
	{
		internal static bool IsServer
		{
			get
			{
				try
				{
					NetworkManager networkManager = InstanceFinder.NetworkManager;
					return (Object)(object)networkManager != (Object)null && networkManager.IsServer;
				}
				catch (Exception e)
				{
					Core.Caught("net: read NetworkManager.IsServer", e);
					return false;
				}
			}
		}

		internal static bool IsClientOnly
		{
			get
			{
				try
				{
					NetworkManager networkManager = InstanceFinder.NetworkManager;
					return (Object)(object)networkManager != (Object)null && networkManager.IsClient && !networkManager.IsServer;
				}
				catch (Exception e)
				{
					Core.Caught("net: read NetworkManager.IsClient", e);
					return false;
				}
			}
		}

		internal static bool IsCoop()
		{
			return LobbyPlayers() > 1;
		}

		internal static int LobbyPlayers()
		{
			try
			{
				Lobby instance = Singleton<Lobby>.Instance;
				return ((Object)(object)instance != (Object)null && instance.IsInLobby) ? instance.PlayerCount : 0;
			}
			catch (Exception e)
			{
				Core.Caught("net: read the Steam lobby", e);
				return 0;
			}
		}

		internal static int RemoteClients()
		{
			try
			{
				NetworkManager networkManager = InstanceFinder.NetworkManager;
				if ((Object)(object)networkManager == (Object)null || !networkManager.IsServer || (Object)(object)networkManager.ServerManager == (Object)null || networkManager.ServerManager.Clients == null)
				{
					return 0;
				}
				return Math.Max(0, networkManager.ServerManager.Clients.Count - (networkManager.IsClient ? 1 : 0));
			}
			catch (Exception e)
			{
				Core.Caught("net: read the server's client list", e);
				return 0;
			}
		}

		internal static string RoleText()
		{
			if (IsClientOnly)
			{
				return "client";
			}
			if (IsServer)
			{
				if (!IsCoop())
				{
					return "single-player";
				}
				return "host";
			}
			return "offline";
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal sealed class OptionSync : IFeature
	{
		internal const string LobbyKey = "awe_opts";

		private const string WireVersion = "2";

		private const string LegacyWireVersion = "1";

		private const float PollSeconds = 2f;

		private const float RetrySeconds = 10f;

		private const float InfoThrottleSeconds = 10f;

		private const int WarnBytes = 3000;

		private float _poll;

		private float _retryIn;

		private float _clock;

		private float _lastInfo = -999f;

		private string _lastWritten = "";

		private int _writes;

		private int _readbackFail;

		private int _lastBytes;

		private string _lastHash = "";

		private string _appliedHash = "";

		private int _applied;

		private string _badWire = "";

		private bool _wasClient;

		private int _guestWas = -1;

		private static Dictionary<Option, string> _shortByOption;

		private static Dictionary<string, Option> _optionByShort;

		public string Name => "coop-sync";

		public string Status => "role=" + Net.RoleText() + " bytes=" + _lastBytes + " wire=" + WireBytes() + " writes=" + _writes + " readback_fail=" + _readbackFail + " applied=" + _applied + " hash=" + ((_lastHash.Length > 0) ? _lastHash : "-");

		public void Init()
		{
		}

		public void Reset()
		{
			bool isClientOnly = Net.IsClientOnly;
			if (_appliedHash.Length > 0 && !isClientOnly)
			{
				ClearHostValues("state reset");
			}
			_lastWritten = "";
			_lastHash = "";
			_lastBytes = 0;
			_poll = 0f;
			_retryIn = 0f;
			_guestWas = -1;
			if (!isClientOnly)
			{
				_wasClient = false;
			}
		}

		private static int WireBytes()
		{
			if (!Net.IsServer)
			{
				return 0;
			}
			string hash;
			return Utf8("awe_opts") + Utf8(Encode(out hash));
		}

		public void Tick(float dt)
		{
			_clock += dt;
			_poll += dt;
			if (_poll < 2f)
			{
				return;
			}
			_poll = 0f;
			if (Net.IsClientOnly)
			{
				_wasClient = true;
				ClientPoll();
				return;
			}
			if (_wasClient)
			{
				_wasClient = false;
				if (_appliedHash.Length > 0)
				{
					ClearHostValues("left the host's session");
				}
			}
			if (Net.IsServer)
			{
				HostPoll();
			}
		}

		private void HostPoll()
		{
			if (_retryIn > 0f)
			{
				_retryIn = Math.Max(0f, _retryIn - 2f);
			}
			bool flag = Net.IsCoop();
			if ((flag ? 1 : 0) != _guestWas)
			{
				if (flag || _guestWas == 1)
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Msg("[CoopSync] " + (flag ? "guest present, publishing" : "no guest, publishing paused") + ": lobby players " + Net.LobbyPlayers() + ", remote clients " + Net.RemoteClients() + ".");
					}
				}
				_guestWas = (flag ? 1 : 0);
			}
			if (!flag || _retryIn > 0f)
			{
				return;
			}
			Lobby val = SafeLobby("host poll");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string hash;
			string text = Encode(out hash);
			_lastHash = hash;
			_lastBytes = Utf8("awe_opts") + Utf8(text);
			if (ReadKey(val) == text)
			{
				_lastWritten = text;
				return;
			}
			if (_lastBytes > 3000)
			{
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Warning("[CoopSync] payload is " + _lastBytes + " bytes of Steam's ~8192 lobby budget.");
				}
			}
			Write(val, text, hash);
		}

		private void Write(Lobby lobby, string wire, string hash)
		{
			bool flag = false;
			try
			{
				lobby.SetLobbyData("awe_opts", wire);
			}
			catch (Exception ex)
			{
				flag = true;
				_readbackFail++;
				_retryIn = 10f;
				Instance log = Core.Log;
				if (log != null)
				{
					log.Error("[CoopSync] SetLobbyData threw for key awe_opts (" + _lastBytes + " bytes): " + ex.GetType().Name + ": " + ex.Message);
				}
			}
			if (flag)
			{
				return;
			}
			string text = ReadKey(lobby);
			if (text != wire)
			{
				_readbackFail++;
				_retryIn = 10f;
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Error("[CoopSync] lobby write not confirmed for key awe_opts (" + _lastBytes + " bytes, wrote hash " + hash + ", read back " + ((text == null) ? "nothing" : (Utf8(text) + " bytes")) + "). Steam refuses writes once the lobby's ~8 KB are used up.");
				}
			}
			else
			{
				_lastWritten = wire;
				_writes++;
				Info("[CoopSync] published " + OptionCountOf(wire) + " changed option(s), key awe_opts, " + _lastBytes + " bytes, hash " + hash + ".");
			}
		}

		private void ClientPoll()
		{
			Lobby val = SafeLobby("client poll");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string text = ReadKey(val);
			if (string.IsNullOrEmpty(text))
			{
				if (_appliedHash.Length > 0)
				{
					ClearHostValues("the host's key is gone");
				}
				return;
			}
			if (!TryDecode(text, out var hash, out var values, out var error))
			{
				if (text != _badWire)
				{
					_badWire = text;
					Instance log = Core.Log;
					if (log != null)
					{
						log.Error("[CoopSync] could not decode the host's options (" + Utf8(text) + " bytes), local values untouched: " + error);
					}
				}
				return;
			}
			_lastHash = hash;
			_lastBytes = Utf8("awe_opts") + Utf8(text);
			if (!(hash == _appliedHash))
			{
				ApplyHostValues(values);
				_appliedHash = hash;
				_applied++;
				Info("[CoopSync] applied the host's options: " + values.Count + " changed, hash " + hash + ", " + _lastBytes + " bytes.");
			}
		}

		private static void ApplyHostValues(List<KeyValuePair<Option, string>> changed)
		{
			Dictionary<Option, string> dictionary = new Dictionary<Option, string>();
			foreach (KeyValuePair<Option, string> item in changed)
			{
				dictionary[item.Key] = item.Value;
			}
			foreach (Option item2 in Options.All)
			{
				if (item2.HostOnly)
				{
					item2.SetHostValue(dictionary.TryGetValue(item2, out var value) ? value : item2.DefaultText);
				}
			}
		}

		private void ClearHostValues(string why)
		{
			int num = 0;
			foreach (Option item in Options.All)
			{
				if (item.HostOnly)
				{
					try
					{
						item.ClearHostValue();
						num++;
					}
					catch (Exception e)
					{
						Core.Failed("coop-sync: clear host value " + item.Key, e);
					}
				}
			}
			_appliedHash = "";
			Info("[CoopSync] cleared the host's values on " + num + " option(s): " + why + ".");
		}

		internal static string Encode(out string hash)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Option item in Options.All)
			{
				if (!item.HostOnly)
				{
					continue;
				}
				string text = EffectiveText(item);
				if (text == item.DefaultText)
				{
					continue;
				}
				string text2 = ((item.Kind != OptionKind.Bool) ? text : ((text == "true") ? "1" : "0"));
				string value;
				if (!Safe(text2))
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Error("[CoopSync] option " + item.Key + " skipped: its value '" + text2 + "' contains a reserved character.");
					}
				}
				else if (!ShortKeys().TryGetValue(item, out value))
				{
					Instance log2 = Core.Log;
					if (log2 != null)
					{
						log2.Error("[CoopSync] option " + item.Key + " skipped: its short key collides with another option.");
					}
				}
				else
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(',');
					}
					stringBuilder.Append(value).Append('=').Append(text2);
				}
			}
			string text3 = stringBuilder.ToString();
			hash = Fnv(text3);
			return "2|" + hash + "|" + text3;
		}

		private static Dictionary<Option, string> ShortKeys()
		{
			if (_shortByOption != null && _shortByOption.Count > 0)
			{
				return _shortByOption;
			}
			Dictionary<Option, string> dictionary = new Dictionary<Option, string>();
			Dictionary<string, Option> dictionary2 = new Dictionary<string, Option>();
			HashSet<string> hashSet = new HashSet<string>();
			foreach (Option item in Options.All)
			{
				if (!item.HostOnly)
				{
					continue;
				}
				string text = ShortKey(item.Key);
				if (dictionary2.TryGetValue(text, out var value))
				{
					if (hashSet.Add(text))
					{
						dictionary.Remove(value);
						Instance log = Core.Log;
						if (log != null)
						{
							log.Error("[CoopSync] options " + value.Key + " and " + item.Key + " share the short key " + text + "; neither is synced.");
						}
					}
					else
					{
						Instance log2 = Core.Log;
						if (log2 != null)
						{
							log2.Error("[CoopSync] option " + item.Key + " also shares the short key " + text + "; not synced.");
						}
					}
				}
				else
				{
					dictionary2[text] = item;
					dictionary[item] = text;
				}
			}
			foreach (string item2 in hashSet)
			{
				dictionary2.Remove(item2);
			}
			_optionByShort = dictionary2;
			_shortByOption = dictionary;
			return dictionary;
		}

		private static string ShortKey(string key)
		{
			uint num = 2166136261u;
			byte[] bytes = Encoding.UTF8.GetBytes(key);
			foreach (byte b in bytes)
			{
				num ^= b;
				num *= 16777619;
			}
			uint num2 = num % 1679616;
			char[] array = new char[4];
			for (int num3 = 3; num3 >= 0; num3--)
			{
				uint num4 = num2 % 36;
				array[num3] = (char)((num4 < 10) ? (48 + num4) : (97 + (num4 - 10)));
				num2 /= 36;
			}
			return new string(array);
		}

		private static string EffectiveText(Option o)
		{
			if (!o.Balance || !Options.VanillaForced)
			{
				return o.ValueText;
			}
			return o.DefaultText;
		}

		private static bool Safe(string s)
		{
			return s.IndexOfAny(new char[5] { ';', ',', '=', ':', '|' }) < 0;
		}

		internal static bool TryDecode(string wire, out string hash, out List<KeyValuePair<Option, string>> values, out string error)
		{
			hash = "";
			values = new List<KeyValuePair<Option, string>>();
			error = null;
			string[] array = wire.Split(new char[1] { '|' }, 3);
			if (array.Length != 3)
			{
				error = "not in the form version|hash|body";
				return false;
			}
			bool flag = array[0] == "1";
			if (array[0] != "2" && !flag)
			{
				error = "unsupported wire version '" + array[0] + "' (this mod reads 1 and 2)";
				return false;
			}
			hash = array[1];
			string text = array[2];
			if (Fnv(text) != hash)
			{
				error = "hash mismatch, the lobby value is cut off or damaged (header " + hash + ", body " + Fnv(text) + ")";
				return false;
			}
			List<string> list = new List<string>();
			if (!flag)
			{
				ShortKeys();
				if (text.Length > 0)
				{
					string[] array2 = text.Split(',');
					foreach (string text2 in array2)
					{
						int num = text2.IndexOf('=');
						if (num <= 0)
						{
							error = "entry without a value: '" + text2 + "'";
							return false;
						}
						string text3 = text2.Substring(0, num);
						string raw = text2.Substring(num + 1);
						if (_optionByShort == null || !_optionByShort.TryGetValue(text3, out var value))
						{
							list.Add(text3);
							continue;
						}
						if (!TryValue(value, raw, out var text4, out var why))
						{
							error = value.Key + ": " + why;
							return false;
						}
						values.Add(new KeyValuePair<Option, string>(value, text4));
					}
				}
			}
			else if (text.Length > 0)
			{
				string[] array2 = text.Split(';');
				foreach (string text5 in array2)
				{
					int num2 = text5.IndexOf(':');
					if (num2 <= 0)
					{
						error = "group entry without a name: '" + text5 + "'";
						return false;
					}
					string text6 = text5.Substring(0, num2);
					string[] array3 = text5.Substring(num2 + 1).Split(',');
					foreach (string text7 in array3)
					{
						int num3 = text7.IndexOf('=');
						if (num3 <= 0)
						{
							error = "entry without a value: '" + text7 + "'";
							return false;
						}
						string text8 = text6 + "." + text7.Substring(0, num3);
						string raw2 = text7.Substring(num3 + 1);
						Option option = Options.Find(text8);
						if (option == null || !option.HostOnly)
						{
							list.Add(text8);
							continue;
						}
						if (!TryValue(option, raw2, out var text9, out var why2))
						{
							error = text8 + ": " + why2;
							return false;
						}
						values.Add(new KeyValuePair<Option, string>(option, text9));
					}
				}
			}
			if (list.Count > 0)
			{
				Core.WarnOnce("coop-sync unknown " + hash, "[CoopSync] the host sent " + list.Count + " option(s) this version does not know, skipped: " + string.Join(", ", list.Take(8)) + ((list.Count > 8) ? ", ..." : ""));
			}
			return true;
		}

		private static bool TryValue(Option o, string raw, out string text, out string why)
		{
			text = raw;
			why = null;
			switch (o.Kind)
			{
			case OptionKind.Bool:
				if (raw == "1")
				{
					text = "true";
					return true;
				}
				if (raw == "0")
				{
					text = "false";
					return true;
				}
				why = "expected 1 or 0, got '" + raw + "'";
				return false;
			case OptionKind.Int:
			{
				if (int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _))
				{
					return true;
				}
				why = "expected a whole number, got '" + raw + "'";
				return false;
			}
			case OptionKind.Float:
			{
				if (float.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && !float.IsNaN(result2))
				{
					return true;
				}
				why = "expected a number, got '" + raw + "'";
				return false;
			}
			case OptionKind.Choice:
			{
				string text2 = ((ChoiceOption)o).Choices.FirstOrDefault((string c) => string.Equals(c, raw, StringComparison.OrdinalIgnoreCase));
				if (text2 != null)
				{
					text = text2;
					return true;
				}
				why = "'" + raw + "' is not one of the choices";
				return false;
			}
			default:
				why = "unknown option kind";
				return false;
			}
		}

		private static string Fnv(string s)
		{
			uint num = 2166136261u;
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			foreach (byte b in bytes)
			{
				num ^= b;
				num *= 16777619;
			}
			return num.ToString("x8", CultureInfo.InvariantCulture);
		}

		private static int Utf8(string s)
		{
			if (s != null)
			{
				return Encoding.UTF8.GetByteCount(s);
			}
			return 0;
		}

		private static int OptionCountOf(string wire)
		{
			int num = wire.LastIndexOf('|');
			string text = ((num >= 0) ? wire.Substring(num + 1) : "");
			if (text.Length != 0)
			{
				return text.Count((char c) => c == '=');
			}
			return 0;
		}

		private static Lobby SafeLobby(string where)
		{
			try
			{
				Lobby instance = Singleton<Lobby>.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					Core.WarnOnce("coop-sync no lobby " + where, "[CoopSync] " + where + ": no Lobby singleton yet.");
				}
				return instance;
			}
			catch (Exception e)
			{
				Core.Caught("coop-sync: Lobby singleton (" + where + ")", e);
				return null;
			}
		}

		private static string ReadKey(Lobby lobby)
		{
			try
			{
				ILobbyService lobbyService = lobby._lobbyService;
				if (lobbyService == null)
				{
					Core.WarnOnce("coop-sync no service", "[CoopSync] the lobby has no lobby service, cannot read awe_opts.");
					return null;
				}
				return lobbyService.GetLobbyData("awe_opts") ?? "";
			}
			catch (Exception e)
			{
				Core.Failed("coop-sync: GetLobbyData awe_opts", e);
				return null;
			}
		}

		private void Info(string msg)
		{
			if (!(_clock - _lastInfo < 10f))
			{
				_lastInfo = _clock;
				Instance log = Core.Log;
				if (log != null)
				{
					log.Msg(msg);
				}
			}
		}
	}
}
namespace ActuallyWorkingEmployees.Host
{
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class Calls
	{
		internal const string HostMsg = "Set by the host.";

		internal static readonly Dictionary<string, Func<JObject, JObject>> Handlers = new Dictionary<string, Func<JObject, JObject>>(StringComparer.Ordinal)
		{
			["list"] = (JObject _) => BuildState(),
			["role"] = (JObject _) => Role(),
			["set"] = Set,
			["reset"] = Reset,
			["resetAll"] = (JObject _) => ResetAll(),
			["preset"] = Preset,
			["diag"] = DiagCalls.Diag,
			["doctorRun"] = DiagCalls.DoctorRun,
			["analyticsReset"] = DiagCalls.AnalyticsReset
		};

		internal static string Run(string name, string arg, Func<JObject, JObject> handler)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				JObject arg2 = (JObject)((!string.IsNullOrWhiteSpace(arg)) ? ((object)JObject.Parse(arg)) : ((object)new JObject()));
				return ((JToken)(handler(arg2) ?? Fail("Something went wrong."))).ToString((Formatting)0);
			}
			catch (Exception ex)
			{
				Core.Failed("app: call " + name, ex);
				Instance log = Core.Log;
				if (log != null)
				{
					log.Error("[App] " + name + "(" + arg + ") failed: " + ex.Message);
				}
				return ((JToken)Fail("Something went wrong.")).ToString((Formatting)0);
			}
		}

		internal static JObject Fail(string msg)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			return new JObject
			{
				["ok"] = JToken.op_Implicit(false),
				["msg"] = JToken.op_Implicit(msg)
			};
		}

		internal static JObject BuildState()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			JArray val = new JArray();
			Dictionary<string, JArray> dictionary = new Dictionary<string, JArray>(StringComparer.OrdinalIgnoreCase);
			foreach (string group in Options.Groups)
			{
				JArray val2 = (dictionary[group] = new JArray());
				val.Add((JToken)new JObject
				{
					["name"] = JToken.op_Implicit(group),
					["options"] = (JToken)(object)val2
				});
			}
			foreach (Option item in Options.All)
			{
				if (!dictionary.TryGetValue(item.Group, out var value))
				{
					Core.WarnOnce("app: group " + item.Group, "option " + item.Key + " is in a group the app does not list, skipped.");
				}
				else
				{
					value.Add((JToken)(object)Describe(item));
				}
			}
			JObject obj = Role();
			obj["groups"] = (JToken)(object)val;
			obj["notice"] = JToken.op_Implicit(DiagCalls.Notice());
			obj["hints"] = (JToken)(object)DiagCalls.Hints();
			return obj;
		}

		private static JObject Role()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["role"] = JToken.op_Implicit(Net.RoleText()),
				["locked"] = JToken.op_Implicit(Net.IsClientOnly),
				["vanilla"] = JToken.op_Implicit(Options.VanillaForced)
			};
		}

		private static JObject Describe(Option o)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Expected O, but got Unknown
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Expected O, but got Unknown
			string text = o.HostValueText ?? o.ValueText;
			string text2 = ((o.HostValueText != null || o.Editable) ? o.DisplayText : o.Describe(o.ValueText));
			JObject val = new JObject
			{
				["key"] = JToken.op_Implicit(o.Key),
				["label"] = JToken.op_Implicit(o.Label),
				["help"] = JToken.op_Implicit(o.Help),
				["kind"] = JToken.op_Implicit(o.Kind.ToString().ToLowerInvariant()),
				["value"] = JToken.op_Implicit(text),
				["text"] = JToken.op_Implicit(text2),
				["def"] = JToken.op_Implicit(o.DefaultText),
				["defText"] = JToken.op_Implicit(o.Describe(o.DefaultText)),
				["isDefault"] = JToken.op_Implicit(o.IsDefault),
				["editable"] = JToken.op_Implicit(o.Editable),
				["waiting"] = JToken.op_Implicit(!o.Editable && o.HostValueText == null),
				["hostOnly"] = JToken.op_Implicit(o.HostOnly),
				["balance"] = JToken.op_Implicit(o.Balance)
			};
			if (!(o is IntOption intOption))
			{
				if (!(o is FloatOption floatOption))
				{
					if (o is ChoiceOption choiceOption)
					{
						object[] choices = choiceOption.Choices;
						val["choices"] = (JToken)new JArray(choices);
					}
				}
				else
				{
					val["min"] = JToken.op_Implicit(Math.Round(floatOption.Min, 3));
					val["max"] = JToken.op_Implicit(Math.Round(floatOption.Max, 3));
					val["step"] = JToken.op_Implicit(Math.Round(floatOption.Step, 3));
					val["sentinel"] = JToken.op_Implicit(floatOption.VanillaSentinel);
				}
			}
			else
			{
				val["min"] = JToken.op_Implicit(intOption.Min);
				val["max"] = JToken.op_Implicit(intOption.Max);
				val["step"] = JToken.op_Implicit(intOption.Step);
				val["sentinel"] = JToken.op_Implicit(intOption.VanillaSentinel);
			}
			return val;
		}

		private static JObject Set(JObject a)
		{
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Expected O, but got Unknown
			Option option = Options.Find((string)a["key"]);
			if (option == null)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("[App] set: no option '" + (string)a["key"] + "'.");
				}
				return Fail("Unknown setting.");
			}
			if (!option.Editable)
			{
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Warning("[App] set " + option.Key + " refused: the host controls it.");
				}
				return Fail("Set by the host.");
			}
			string text = ((string)a["value"]) ?? "";
			if (!option.TrySet(text, out var error))
			{
				Instance log3 = Core.Log;
				if (log3 != null)
				{
					log3.Warning("[App] set " + option.Key + " = '" + text + "' rejected: " + error);
				}
				return Fail(error);
			}
			Instance log4 = Core.Log;
			if (log4 != null)
			{
				log4.Msg("[App] " + option.Key + " = " + option.ValueText + ".");
			}
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["option"] = (JToken)(object)Describe(option)
			};
		}

		private static JObject Reset(JObject a)
		{
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			Option option = Options.Find((string)a["key"]);
			if (option == null)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("[App] reset: no option '" + (string)a["key"] + "'.");
				}
				return Fail("Unknown setting.");
			}
			if (!option.Editable)
			{
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Warning("[App] reset " + option.Key + " refused: the host controls it.");
				}
				return Fail("Set by the host.");
			}
			option.ResetToDefault();
			Instance log3 = Core.Log;
			if (log3 != null)
			{
				log3.Msg("[App] " + option.Key + " reset to " + option.ValueText + ".");
			}
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["option"] = (JToken)(object)Describe(option)
			};
		}

		private static JObject ResetAll()
		{
			int num = Options.ResetAllToDefault();
			Instance log = Core.Log;
			if (log != null)
			{
				log.Msg("[App] reset all (" + Net.RoleText() + "): " + num + " setting(s) back to default.");
			}
			JObject obj = BuildState();
			obj["changed"] = JToken.op_Implicit(num);
			return obj;
		}

		private static JObject Preset(JObject a)
		{
			string text = (string)a["name"];
			Presets.Result result = Presets.Apply(text);
			if (result.Refused != null)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("[App] preset " + text + " refused: " + result.Refused);
				}
				return Fail(Net.IsClientOnly ? "Set by the host." : "Something went wrong.");
			}
			if (result.Errors.Count > 0)
			{
				return Fail("Something went wrong.");
			}
			Instance log2 = Core.Log;
			if (log2 != null)
			{
				log2.Msg("[App] preset " + text + " (" + Net.RoleText() + "): " + result.Changed + " of " + result.Total + " setting(s) changed.");
			}
			JObject obj = BuildState();
			obj["changed"] = JToken.op_Implicit(result.Changed);
			return obj;
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class DiagCalls
	{
		internal const string LockNotice = "Some settings are set by the host.";

		internal const string StationHint = "Saved mixes and stations that already run keep their old time.";

		private const int MaxAnalyticsRows = 30;

		private const int MaxReasonsPerRow = 3;

		internal static JObject Hints()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			JObject val = new JObject();
			if (Options.ShowStationSpeedWarning.Value && StationSpeedChanged())
			{
				val["Stations"] = JToken.op_Implicit("Saved mixes and stations that already run keep their old time.");
			}
			return val;
		}

		internal static string Notice()
		{
			if (!Net.IsClientOnly || !Options.ShowCoopLockNotice.Value)
			{
				return "";
			}
			return "Some settings are set by the host.";
		}

		private static bool StationSpeedChanged()
		{
			foreach (Option item in Options.InGroup("Stations"))
			{
				if (item.Balance && !item.IsDefault)
				{
					return true;
				}
			}
			return false;
		}

		internal static JObject Diag(JObject _)
		{
			return Build();
		}

		internal static JObject DoctorRun(JObject _)
		{
			if (!SetupDoctor.Run(out var refusal))
			{
				return Calls.Fail(refusal);
			}
			JObject obj = Build();
			obj["ran"] = JToken.op_Implicit(true);
			return obj;
		}

		internal static JObject AnalyticsReset(JObject _)
		{
			if (!Net.IsServer)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("[App] analytics reset refused: host only.");
				}
				return Calls.Fail("Host only.");
			}
			IdleAnalytics.Clear();
			Instance log2 = Core.Log;
			if (log2 != null)
			{
				log2.Msg("[App] analytics cleared.");
			}
			return Build();
		}

		internal static JObject Build()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			bool isServer = Net.IsServer;
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["host"] = JToken.op_Implicit(isServer),
				["badges"] = (JToken)(object)BadgesJson(isServer),
				["doctor"] = (JToken)(object)DoctorJson(isServer),
				["analytics"] = (JToken)(object)AnalyticsJson(isServer)
			};
		}

		private static JObject BadgesJson(bool host)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			bool value = Options.ShowProblemBadges.Value;
			JArray val = new JArray();
			int num = 0;
			if (host && value)
			{
				foreach (ProblemBadges.Badge item in ProblemBadges.Build())
				{
					if (item.HasProblem)
					{
						num++;
					}
					val.Add((JToken)new JObject
					{
						["name"] = JToken.op_Implicit(item.Name),
						["role"] = JToken.op_Implicit(item.Role),
						["sev"] = JToken.op_Implicit(item.HasProblem ? Finding.SevText(item.Severity) : "ok"),
						["text"] = JToken.op_Implicit(item.Text)
					});
				}
			}
			return new JObject
			{
				["enabled"] = JToken.op_Implicit(value),
				["problems"] = JToken.op_Implicit(num),
				["items"] = (JToken)(object)val
			};
		}

		private static JObject DoctorJson(bool host)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Expected O, but got Unknown
			JArray val = new JArray();
			bool flag = host && SetupDoctor.HasRun;
			if (flag)
			{
				for (int i = 0; i < SetupDoctor.Shown; i++)
				{
					Finding finding = SetupDoctor.Findings[i];
					val.Add((JToken)new JObject
					{
						["sev"] = JToken.op_Implicit(Finding.SevText(finding.Severity)),
						["cat"] = JToken.op_Implicit(finding.Category),
						["subject"] = JToken.op_Implicit(finding.Subject),
						["msg"] = JToken.op_Implicit(finding.Message),
						["fix"] = JToken.op_Implicit(finding.Fix),
						["count"] = JToken.op_Implicit(finding.Count)
					});
				}
			}
			return new JObject
			{
				["enabled"] = JToken.op_Implicit(Options.EnableSetupDoctor.Value),
				["ran"] = JToken.op_Implicit(flag),
				["total"] = JToken.op_Implicit(flag ? SetupDoctor.Total : 0),
				["shown"] = JToken.op_Implicit(flag ? SetupDoctor.Shown : 0),
				["summary"] = JToken.op_Implicit(flag ? SetupDoctor.Summary() : ""),
				["ageSec"] = JToken.op_Implicit(flag ? ((int)(Time.realtimeSinceStartup - SetupDoctor.RanAt)) : 0),
				["findings"] = (JToken)(object)val
			};
		}

		private static JObject AnalyticsJson(bool host)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Expected O, but got Unknown
			JArray val = new JArray();
			if (host && Options.EnableAnalytics.Value)
			{
				foreach (IdleAnalytics.Stat item in IdleAnalytics.Stats.Values.OrderByDescending((IdleAnalytics.Stat s) => (s.Samples != 0) ? ((double)s.IdleSamples / (double)s.Samples) : 0.0).Take(30))
				{
					JArray val2 = new JArray();
					foreach (KeyValuePair<string, int> item2 in item.Reasons.OrderByDescending((KeyValuePair<string, int> r) => r.Value).Take(3))
					{
						val2.Add((JToken)new JObject
						{
							["text"] = JToken.op_Implicit(item2.Key),
							["count"] = JToken.op_Implicit(item2.Value)
						});
					}
					val.Add((JToken)new JObject
					{
						["name"] = JToken.op_Implicit(item.Name),
						["role"] = JToken.op_Implicit(item.Role),
						["samples"] = JToken.op_Implicit(item.Samples),
						["idle"] = JToken.op_Implicit(item.IdleSamples),
						["idlePct"] = JToken.op_Implicit((item.Samples != 0) ? ((int)Math.Round(100.0 * (double)item.IdleSamples / (double)item.Samples)) : 0),
						["reasons"] = (JToken)(object)val2
					});
				}
			}
			return new JObject
			{
				["enabled"] = JToken.op_Implicit(Options.EnableAnalytics.Value),
				["interval"] = JToken.op_Implicit(Math.Round(Options.AnalyticsIntervalSeconds.Value, 1)),
				["samples"] = JToken.op_Implicit(IdleAnalytics.TotalSamples),
				["items"] = (JToken)(object)val
			};
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal sealed class PhoneApp : IFeature
	{
		internal const string AppId = "awe";

		internal const string Bundle = "ActuallyWorkingEmployees.Assets.app";

		private static AppHandle _app;

		private static volatile bool _dirty;

		private static string _roleSig = "";

		private static float _sinceRoleCheck;

		private static bool _hooked;

		public string Name => "PhoneApp";

		public string Status
		{
			get
			{
				if (_app != null)
				{
					return "registered, sideload " + (Apps.Available ? "present" : "absent") + ", page " + (_app.IsOpen ? "open" : "closed");
				}
				return "not registered";
			}
		}

		public void Init()
		{
			try
			{
				_app = Apps.Register("awe", "ActuallyWorkingEmployees.Assets.app", "Employees", "Employees");
				_app.Orientation("landscape", "portrait");
				foreach (KeyValuePair<string, Func<JObject, JObject>> handler in Calls.Handlers)
				{
					KeyValuePair<string, Func<JObject, JObject>> h = handler;
					_app.OnCall(h.Key, (string arg) => Calls.Run(h.Key, arg, h.Value));
				}
				HookOptions();
				Instance log = Core.Log;
				if (log != null)
				{
					log.Msg(Apps.Available ? "[App] registered with Sideload as 'awe'." : "[App] Sideload is not loaded yet, the registration is queued.");
				}
			}
			catch (Exception e)
			{
				Core.Failed("app: register the phone app", e);
			}
		}

		public void Tick(float dt)
		{
			if (_app == null)
			{
				return;
			}
			_sinceRoleCheck += dt;
			if (_sinceRoleCheck >= 0.5f)
			{
				_sinceRoleCheck = 0f;
				string text = Net.RoleText();
				if (text != _roleSig)
				{
					_roleSig = text;
					_dirty = true;
				}
			}
			if (!_dirty)
			{
				return;
			}
			_dirty = false;
			try
			{
				_app.Emit("options.changed", _roleSig);
			}
			catch (Exception e)
			{
				Core.Caught("app: emit options.changed", e);
			}
		}

		public void Reset()
		{
			_dirty = true;
		}

		private static void HookOptions()
		{
			if (_hooked)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Warning("[App] option hooks already installed, skipped.");
				}
				return;
			}
			_hooked = true;
			foreach (Option item in Options.All)
			{
				item.Changed += delegate
				{
					_dirty = true;
				};
			}
		}
	}
}
namespace ActuallyWorkingEmployees.Features
{
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal interface IFeature
	{
		string Name { get; }

		string Status { get; }

		void Init();

		void Tick(float dt);

		void Reset();
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class FeatureHost
	{
		private static readonly List<IFeature> _all = new List<IFeature>();

		internal static IReadOnlyList<IFeature> All => _all;

		internal static void Discover()
		{
			Type[] source;
			try
			{
				source = typeof(FeatureHost).Assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				Core.Caught("features: GetTypes", ex);
				source = ex.Types.Where((Type t) => t != null).ToArray();
			}
			foreach (Type item in from t in source
				where !t.IsAbstract && !t.IsInterface && typeof(IFeature).IsAssignableFrom(t)
				orderby t.FullName
				select t)
			{
				IFeature feature;
				try
				{
					feature = (IFeature)Activator.CreateInstance(item, nonPublic: true);
				}
				catch (Exception e)
				{
					Core.Failed("features: create " + item.Name, e);
					continue;
				}
				try
				{
					feature.Init();
					_all.Add(feature);
					if (Core.Verbose)
					{
						Instance log = Core.Log;
						if (log != null)
						{
							log.Msg("[Features] " + feature.Name + " ready.");
						}
					}
				}
				catch (Exception e2)
				{
					Core.Failed("features: init " + item.Name, e2);
				}
			}
			Instance log2 = Core.Log;
			if (log2 != null)
			{
				log2.Msg("[Features] " + _all.Count + " active: " + string.Join(", ", _all.Select((IFeature f) => f.Name)));
			}
		}

		internal static void TickAll(float dt)
		{
			if (!Core.InWorld)
			{
				return;
			}
			for (int i = 0; i < _all.Count; i++)
			{
				IFeature feature = _all[i];
				try
				{
					feature.Tick(dt);
				}
				catch (Exception e)
				{
					Core.Failed("features: tick " + feature.Name, e);
				}
			}
		}

		internal static void ResetAll()
		{
			foreach (IFeature item in _all)
			{
				try
				{
					item.Reset();
				}
				catch (Exception e)
				{
					Core.Failed("features: reset " + item.Name, e);
				}
			}
		}
	}
}
namespace ActuallyWorkingEmployees.Features.World
{
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal sealed class EmployeeCollision : IFeature
	{
		private struct AgentOriginal
		{
			internal float Radius;

			internal int Priority;

			internal ObstacleAvoidanceType Avoidance;

			internal bool AvoidanceFlag;
		}

		internal const string FeatureName = "collision";

		private const float AgentRadius = 0.15f;

		private const float EmployeePass = 1f;

		private const float FurniturePass = 5f;

		private const float FurnitureRescan = 30f;

		private readonly Dictionary<int, List<Collider>> _changedColliders = new Dictionary<int, List<Collider>>();

		private readonly Dictionary<int, AgentOriginal> _agents = new Dictionary<int, AgentOriginal>();

		private readonly Dictionary<int, Component> _agentOwner = new Dictionary<int, Component>();

		private readonly Dictionary<int, NPCMovement> _movement = new Dictionary<int, NPCMovement>();

		private readonly List<NavMeshObstacle> _disabled = new List<NavMeshObstacle>();

		private readonly HashSet<int> _scannedItems = new HashSet<int>();

		private bool _empWas;

		private bool _furnWas;

		private int _lastItemCount = -1;

		private int _obstaclesSeen;

		private float _nextEmp;

		private float _nextFurn;

		private float _nextRescan;

		public string Name => "collision";

		public string Status => "employees=" + (Options.DisableEmployeeCollision.Value ? 1 : 0) + " furniture=" + (Options.DisableFurnitureCollision.Value ? 1 : 0) + " triggerEmployees=" + _changedColliders.Count + " agents=" + _agents.Count + " avoidanceOff=" + AvoidanceOffCount() + " obstaclesOff=" + _disabled.Count + " obstaclesSeen=" + _obstaclesSeen;

		public void Init()
		{
		}

		public void Reset()
		{
			_changedColliders.Clear();
			_agents.Clear();
			_agentOwner.Clear();
			_movement.Clear();
			_disabled.Clear();
			_scannedItems.Clear();
			_empWas = (_furnWas = false);
			_lastItemCount = -1;
			_obstaclesSeen = 0;
			_nextEmp = (_nextFurn = (_nextRescan = 0f));
		}

		public void Tick(float dt)
		{
			float unscaledTime = Time.unscaledTime;
			bool value = Options.DisableEmployeeCollision.Value;
			bool value2 = Options.DisableFurnitureCollision.Value;
			if (!value && !_empWas && !value2 && !_furnWas)
			{
				return;
			}
			if (!Net.IsServer)
			{
				Rel.MsgOnce("collision:client", "[collision] not the host, vanilla collision on this client.");
				return;
			}
			if (unscaledTime >= _nextEmp && (value || _empWas))
			{
				_nextEmp = unscaledTime + 1f;
				EmployeePassNow(value);
			}
			if (unscaledTime >= _nextFurn && (value2 || _furnWas))
			{
				_nextFurn = unscaledTime + 5f;
				FurniturePassNow(value2, unscaledTime);
			}
		}

		private void EmployeePassNow(bool on)
		{
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			if (!on)
			{
				RestoreEmployees();
				_empWas = false;
				return;
			}
			if (!_empWas)
			{
				Instance log = Core.Log;
				if (log != null)
				{
					log.Msg("[collision] employees pass through each other.");
				}
			}
			_empWas = true;
			foreach (Employee item in Rel.Employees())
			{
				int instanceID = ((Object)item).GetInstanceID();
				if (_changedColliders.ContainsKey(instanceID))
				{
					if (_movement.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null && value._obstacleAvoidanceEnabled)
					{
						value.SetObstacleAvoidanceEnabled(false);
					}
					continue;
				}
				List<Collider> list = new List<Collider>(1);
				NPCMovement movement = ((NPC)item).Movement;
				CapsuleCollider val = (((Object)(object)movement != (Object)null) ? movement._capsuleCollider : null);
				if ((Object)(object)val == (Object)null)
				{
					Core.WarnOnce("collision:nocapsule:" + instanceID, Rel.Name((NPC)(object)item) + " has no movement capsule; its collision stays vanilla.");
				}
				else if (!((Collider)val).isTrigger)
				{
					((Collider)val).isTrigger = true;
					list.Add((Collider)(object)val);
				}
				_changedColliders[instanceID] = list;
				NavMeshAgent component = ((Component)item).GetComponent<NavMeshAgent>();
				if ((Object)(object)component == (Object)null)
				{
					Core.WarnOnce("collision:noagent:" + instanceID, Rel.Name((NPC)(object)item) + " has no NavMeshAgent; only its capsule was changed.");
					continue;
				}
				_agents[instanceID] = new AgentOriginal
				{
					Radius = component.radius,
					Priority = component.avoidancePriority,
					Avoidance = component.obstacleAvoidanceType,
					AvoidanceFlag = ((Object)(object)movement == (Object)null || movement._obstacleAvoidanceEnabled)
				};
				_agentOwner[instanceID] = (Component)(object)component;
				if (component.radius > 0.15f)
				{
					component.radius = 0.15f;
				}
				component.obstacleAvoidanceType = (ObstacleAvoidanceType)0;
				component.avoidancePriority = 0;
				if ((Object)(object)movement == (Object)null)
				{
					Core.WarnOnce("collision:nomovement:" + instanceID, Rel.Name((NPC)(object)item) + " has no NPCMovement; obstacle avoidance comes back when a player is near.");
					continue;
				}
				_movement[instanceID] = movement;
				movement.SetObstacleAvoidanceEnabled(false);
			}
		}

		private void RestoreEmployees()
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (KeyValuePair<int, List<Collider>> changedCollider in _changedColliders)
			{
				foreach (Collider item in changedCollider.Value)
				{
					if ((Object)(object)item != (Object)null)
					{
						item.isTrigger = false;
						num++;
					}
				}
			}
			foreach (KeyValuePair<int, AgentOriginal> agent in _agents)
			{
				if (!_agentOwner.TryGetValue(agent.Key, out var value))
				{
					continue;
				}
				NavMeshAgent val = ((Il2CppObjectBase)value).TryCast<NavMeshAgent>();
				if (!((Object)(object)val == (Object)null))
				{
					val.radius = agent.Value.Radius;
					val.avoidancePriority = agent.Value.Priority;
					val.obstacleAvoidanceType = agent.Value.Avoidance;
					if (_movement.TryGetValue(agent.Key, out var value2) && (Object)(object)value2 != (Object)null)
					{
						value2.SetObstacleAvoidanceEnabled(agent.Value.AvoidanceFlag);
					}
				}
			}
			Instance log = Core.Log;
			if (log != null)
			{
				log.Msg("[collision] employee collision restored on " + _changedColliders.Count + " employee(s), " + num + " collider(s).");
			}
			_changedColliders.Clear();
			_agents.Clear();
			_agentOwner.Clear();
			_movement.Clear();
		}

		private int AvoidanceOffCount()
		{
			int num = 0;
			foreach (KeyValuePair<int, NPCMovement> item in _movement)
			{
				if ((Object)(object)item.Value != (Object)null && !item.Value._obstacleAvoidanceEnabled)
				{
					num++;
				}
			}
			return num;
		}

		private void FurniturePassNow(bool on, float now)
		{
			if (!on)
			{
				int num = 0;
				for (int i = 0; i < _disabled.Count; i++)
				{
					if ((Object)(object)_disabled[i] != (Object)null)
					{
						((Behaviour)_disabled[i]).enabled = true;
						num++;
					}
				}
				Instance log = Core.Log;
				if (log != null)
				{
					log.Msg("[collision] furniture obstacles restored on " + num + " obstacle(s).");
				}
				if (num > 0)
				{
					ObstaclesChanged("furniture obstacles are back");
				}
				_disabled.Clear();
				_scannedItems.Clear();
				_lastItemCount = -1;
				_furnWas = false;
				return;
			}
			if (!_furnWas)
			{
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Msg("[collision] employees ignore furniture colliders.");
				}
			}
			_furnWas = true;
			int num2 = CountItems();
			if (num2 == _lastItemCount && now < _nextRescan)
			{
				return;
			}
			_lastItemCount = num2;
			_nextRescan = now + 30f;
			int count = _disabled.Count;
			ScanSources();
			if (_disabled.Count != count)
			{
				Instance log3 = Core.Log;
				if (log3 != null)
				{
					log3.Msg("[collision] " + (_disabled.Count - count) + " more obstacle(s) switched off (" + _disabled.Count + " in total, " + _obstaclesSeen + " found).");
				}
				ObstaclesChanged("furniture obstacles were switched off");
			}
		}

		private static void ObstaclesChanged(string why)
		{
			int num = NavMeshUtility.sampleCacheKeys?.Count ?? 0;
			NavMeshUtility.ClearCache();
			Instance log = Core.Log;
			if (log != null)
			{
				log.Msg("[collision] " + num + " cached NavMesh sample(s) dropped: " + why + ".");
			}
			Fix6ReachCache.Forget(why);
		}

		private static int CountItems()
		{
			int num = 0;
			List<Property> properties = Property.Properties;
			if (properties != null)
			{
				for (int i = 0; i < properties.Count; i++)
				{
					int num2 = num;
					Property obj = properties[i];
					num = num2 + ((obj != null) ? obj.BuildableItems?.Count : ((int?)null)).GetValueOrDefault();
				}
			}
			List<Business> businesses = Business.Businesses;
			if (businesses != null)
			{
				for (int j = 0; j < businesses.Count; j++)
				{
					int num3 = num;
					Business obj2 = businesses[j];
					num = num3 + ((obj2 != null) ? ((Property)obj2).BuildableItems?.Count : ((int?)null)).GetValueOrDefault();
				}
			}
			return num;
		}

		private void ScanSources()
		{
			List<Property> properties = Property.Properties;
			if (properties != null)
			{
				for (int i = 0; i < properties.Count; i++)
				{
					Property obj = properties[i];
					ScanItems((obj != null) ? obj.BuildableItems : null);
				}
			}
			List<Business> businesses = Business.Businesses;
			if (businesses != null)
			{
				for (int j = 0; j < businesses.Count; j++)
				{
					Business obj2 = businesses[j];
					ScanItems((obj2 != null) ? ((Property)obj2).BuildableItems : null);
				}
			}
		}

		private void ScanItems(List<BuildableItem> items)
		{
			if (items == null)
			{
				return;
			}
			for (int i = 0; i < items.Count; i++)
			{
				BuildableItem val = items[i];
				if ((Object)(object)val == (Object)null || !_scannedItems.Add(((Object)val).GetInstanceID()))
				{
					continue;
				}
				Il2CppArrayBase<NavMeshObstacle> componentsInChildren = ((Component)val).GetComponentsInChildren<NavMeshObstacle>(true);
				for (int j = 0; j < componentsInChildren.Length; j++)
				{
					NavMeshObstacle val2 = componentsInChildren[j];
					if (!((Object)(object)val2 == (Object)null))
					{
						_obstaclesSeen++;
						if (((Behaviour)val2).enabled)
						{
							((Behaviour)val2).enabled = false;
							_disabled.Add(val2);
						}
					}
				}
			}
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal sealed class IdlePointSharing : IFeature
	{
		internal const string FeatureName = "idle-points";

		private static int _shared;

		private static int _unchanged;

		public string Name => "idle-points";

		public string Status => "on=" + (Options.ShareIdlePoints.Value ? 1 : 0) + " role=" + Net.RoleText() + " shared=" + _shared + " unchanged=" + _unchanged;

		public void Init()
		{
		}

		public void Tick(float dt)
		{
		}

		public void Reset()
		{
			_shared = (_unchanged = 0);
		}

		internal static Il2CppReferenceArray<Transform> Extend(Property prop)
		{
			if ((Object)(object)prop == (Object)null)
			{
				Core.WarnOnce("idle:noprop", "idle: AssignProperty ran without a property; the game's assignment runs unchanged.");
				return null;
			}
			bool isClientOnly = Net.IsClientOnly;
			if (!isClientOnly)
			{
				if (!Options.ShareIdlePoints.Value)
				{
					Core.WarnOnce("idle:off", "idle: sharing is off; the game's assignment runs unchanged.");
					return null;
				}
				if (!Net.IsServer)
				{
					Core.WarnOnce("idle:offline", "idle: neither host nor guest; the game's assignment runs unchanged.");
					return null;
				}
			}
			Il2CppReferenceArray<Transform> employeeIdlePoints = prop.EmployeeIdlePoints;
			List<Employee> employees = prop.Employees;
			if (employeeIdlePoints == null || employees == null)
			{
				Core.WarnOnce("idle:null:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + " has no idle point or employee list; the game's assignment runs unchanged.");
				return null;
			}
			int num = employees.Count + 1;
			if (((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length >= num)
			{
				_unchanged++;
				return null;
			}
			if (((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length == 0)
			{
				Core.WarnOnce("idle:zero:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + " has no idle points at all; nothing to share.");
				return null;
			}
			Il2CppReferenceArray<Transform> val = new Il2CppReferenceArray<Transform>((long)num);
			for (int i = 0; i < num; i++)
			{
				((Il2CppArrayBase<Transform>)(object)val)[i] = ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints)[i % ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length];
			}
			prop.EmployeeIdlePoints = val;
			_shared++;
			Core.WarnOnce("idle:share:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + ": " + num + " employees share " + ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length + " idle point(s)" + (isClientOnly ? " (guest side)." : "."));
			if (!isClientOnly && Net.RemoteClients() > 0)
			{
				Core.WarnOnce("idle:vanilla-guest:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + ": " + num + " employees but " + ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length + " idle point(s) while guests are connected; a guest without AWE gets an IndexOutOfRange for the extra employee.");
			}
			return employeeIdlePoints;
		}
	}
	[HarmonyPatch(typeof(Employee), "AssignProperty")]
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class Awe_IdlePoints_AssignProperty_Patch
	{
		private static bool Prepare(MethodBase original)
		{
			if (!(original == null))
			{
				return NativeMethods.AllowPatch(original);
			}
			return true;
		}

		private static void Prefix(Property prop, out Il2CppReferenceArray<Transform> __state)
		{
			__state = null;
			try
			{
				__state = IdlePointSharing.Extend(prop);
			}
			catch (Exception e)
			{
				Core.Failed("idle: AssignProperty prefix", e);
			}
		}

		private static Exception Finalizer(Property prop, Il2CppReferenceArray<Transform> __state, Exception __exception)
		{
			if (__state != null && (Object)(object)prop != (Object)null)
			{
				try
				{
					prop.EmployeeIdlePoints = __state;
				}
				catch (Exception e)
				{
					Core.Failed("idle: restore the idle points", e);
				}
			}
			return __exception;
		}
	}
}
namespace ActuallyWorkingEmployees.Features.Reliability
{
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal sealed class Fix1Standstill : IFeature
	{
		private sealed class Still
		{
			internal Vector3 Spot;

			internal float Since;

			internal int Tries;

			internal bool Stopped;

			internal bool GaveUp;
		}

		internal const string FeatureName = "fix1-standstill";

		private static readonly Dictionary<int, Still> _still = new Dictionary<int, Still>();

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

		private static int _checks;

		private static int _warps;

		private static int _gaveUp;

		private static int _partialWarps;

		private static int _partialsCounted;

		private static int _refused;

		private static int _stops;

		private float _next;

		private static readonly List<Employee> _employees = new List<Employee>();

		public string Name => "fix1-standstill";

		public string Status => "on=" + (Options.FixStandstill.Value ? 1 : 0) + (Options.FixPartialWalkLoop.Value ? 1 : 0) + " checks=" + _checks + " warps=" + _warps + " gaveup=" + _gaveUp + " partial=" + _partialsCounted + " partialwarps=" + _partialWarps + " refused=" + _refused + " stops=" + _stops;

		public void Init()
		{
		}

		public void Reset()
		{
			_still.Clear();
			_partial.Clear();
			Rel.ResetActionCounts();
		}

		public void Tick(float dt)
		{
			if (Time.time < _next)
			{
				return;
			}
			_next = Time.time + 0.5f;
			if (!Rel.Gate("fix1-standstill", Options.FixStandstill.Value))
			{
				return;
			}
			Rel.EmployeesInto(_employees);
			for (int i = 0; i < _employees.Count; i++)
			{
				Employee val = _employees[i];
				try
				{
					Check(val);
				}
				catch (Exception e)
				{
					Core.Caught("fix1: check " + Rel.Name((NPC)(object)val), e);
				}
			}
		}

		private static void Check(Employee emp)
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)emp).GetInstanceID();
			if ((Object)(object)((NPC)emp).Behaviour == (Object)null)
			{
				Rel.MsgOnce("fix1:nobeh:" + instanceID, "[fix1-standstill] " + Rel.Name((NPC)(object)emp) + " has no behaviour component.");
				return;
			}
			if ((Object)(object)((NPC)emp).Movement == (Object)null)
			{
				Rel.MsgOnce("fix1:nomove:" + instanceID, "[fix1-standstill] " + Rel.Name((NPC)(object)emp) + " has no movement component.");
				return;
			}
			Behaviour activeBehaviour = ((NPC)emp).Behaviour.activeBehaviour;
			if ((Object)(object)activeBehaviour == (Object)null || !activeBehaviour.Active || !Rel.IsWork(activeBehaviour))
			{
				_still.Remove(instanceID);
				return;
			}
			bool isMoving = ((NPC)emp).Movement.IsMoving;
			Vector3 currentDestination = ((NPC)emp).Movement.CurrentDestination;
			Vector3 position = ((Component)emp).transform.position;
			float num = Vector3.Distance(position, currentDestination);
			bool flag = ((NPC)emp).Movement._hasDestination && currentDestination != Vector3.zero && num > 1.5f;
			if (!isMoving && !flag)
			{
				_still.Remove(instanceID);
				return;
			}
			_checks++;
			if (!_still.TryGetValue(instanceID, out var value))
			{
				_still[instanceID] = new Still
				{
					Spot = position,
					Since = Time.time
				};
			}
			else if (Vector3.Distance(position, value.Spot) > Options.StandstillRadius.Value)
			{
				value.Spot = position;
				value.Since = Time.time;
				value.Tries = 0;
				value.Stopped = false;
				value.GaveUp = false;
			}
			else
			{
				if (value.GaveUp || Time.time - value.Since < Options.StandstillSeconds.Value)
				{
					return;
				}
				string text = Options.StandstillSeconds.Value.ToString("F0");
				if (isMoving && !value.Stopped)
				{
					value.Stopped = true;
					((NPC)emp).Movement.Stop();
					_stops++;
					Rel.Action("fix1.stop", "[Fix1] stopped the walk of " + Rel.Name((NPC)(object)emp) + " (" + Rel.TypeName((Il2CppObjectBase)(object)activeBehaviour) + "): no progress for " + text + "s, " + num.ToString("F1") + " m from its destination.");
					value.Spot = ((Component)emp).transform.position;
					value.Since = Time.time;
				}
				else if (!flag || value.Tries >= Options.StandstillAttempts.Value)
				{
					value.GaveUp = true;
					_gaveUp++;
					Instance log = Core.Log;
					if (log != null)
					{
						log.Warning("[Fix1] gave up on " + Rel.Name((NPC)(object)emp) + " (" + Rel.TypeName((Il2CppObjectBase)(object)activeBehaviour) + "): still " + num.ToString("F1") + " m from its destination after " + (value.Stopped ? "a stopped walk and " : "") + value.Tries + " warp(s)" + (flag ? "; the target is probably off the walkable floor." : "; no live destination far enough away to warp to."));
					}
				}
				else
				{
					value.Tries++;
					Warp(emp, currentDestination, "standstill " + text + "s, try " + value.Tries, dev: false);
					value.Spot = ((Component)emp).transform.position;
					value.Since = Time.time;
				}
			}
		}

		private unsafe static bool Warp(Employee emp, Vector3 dest, string why, bool dev)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (!Rel.TryOnNavMesh(dest, out var snapped))
			{
				_refused++;
				Instance log = Core.Log;
				if (log != null)
				{
					string[] obj = new string[7]
					{
						"[Fix1] did not warp ",
						Rel.Name((NPC)(object)emp),
						" (",
						why,
						"): no NavMesh within 5 m of its destination ",
						null,
						null
					};
					Vector3 val = dest;
					obj[5] = ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString();
					obj[6] = ".";
					log.Warning(string.Concat(obj));
				}
				return false;
			}
			Vector3 position = ((Component)emp).transform.position;
			((NPC)emp).Movement.Warp(snapped);
			_warps++;
			string text = "[Fix1] warped " + Rel.Name((NPC)(object)emp) + " to its destination (" + why + "), was " + Vector3.Distance(position, dest).ToString("F1") + " m away, now " + Vector3.Distance(((Component)emp).transform.position, dest).ToString("F1") + " m.";
			if (dev)
			{
				Instance log2 = Core.Log;
				if (log2 != null)
				{
					log2.Msg(text);
				}
			}
			else
			{
				Rel.Action("fix1.warp", text);
			}
			return true;
		}

		internal static void OnWalkResult(Behaviour b, WalkResult result, bool force)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Invalid comparison between Unknown and I4
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (!Rel.Gate("fix1-standstill:partial", Options.FixPartialWalkLoop.Value))
			{
				return;
			}
			NPC npc = b.Npc;
			if ((Object)(object)npc == (Object)null)
			{
				Rel.MsgOnce("fix1:nonpc", "[fix1-standstill] a behaviour has no NPC, walk result ignored.");
				return;
			}
			int instanceID = ((Object)npc).GetInstanceID();
			if ((int)result == 4)
			{
				_partial.Remove(instanceID);
			}
			else
			{
				if ((int)result != 3)
				{
					return;
				}
				Employee val = ((Il2CppObjectBase)npc).TryCast<Employee>();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				Vector3 currentDestination = npc.Movement.CurrentDestination;
				float num = Vector3.Distance(((Component)npc).transform.position, currentDestination);
				if (!force && (currentDestination == Vector3.zero || num <= 1.5f))
				{
					_partial.Remove(instanceID);
					return;
				}
				_partial.TryGetValue(instanceID, out var value);
				value++;
				_partialsCounted++;
				int value2 = Options.PathFailuresBeforeWarp.Value;
				if (value >= value2 || force)
				{
					_partial.Remove(instanceID);
					if (Warp(val, currentDestination, "partial walk x" + value, force))
					{
						_partialWarps++;
					}
					return;
				}
				_partial[instanceID] = value;
				int num2 = Math.Min(value, 4);
				if (b.consecutivePathingFailures < num2)
				{
					b.consecutivePathingFailures = num2;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Behaviour), "WalkCallback")]
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class Awe_Fix1_WalkCallback_Patch
	{
		private static bool Prepare(MethodBase original)
		{
			if (!(original == null))
			{
				return NativeMethods.AllowPatch(original);
			}
			return true;
		}

		private static void Postfix(Behaviour __instance, WalkResult result)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!Rel.Enter("fix1.walkcallback"))
			{
				return;
			}
			try
			{
				Fix1Standstill.OnWalkResult(__instance, result, force: false);
			}
			catch (Exception e)
			{
				Core.Failed("fix1: WalkCallback postfix", e);
			}
			finally
			{
				Rel.Leave("fix1.walkcallback");
			}
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal sealed class Fix2ChemistWork : IFeature
	{
		internal const string FeatureName = "fix2-chemist-work";

		private static readonly Dictionary<int, float> _idleSince = new Dictionary<int, float>();

		private static int _patched;

		private static int _resets;

		private float _next;

		private static readonly List<Employee> _employees = new List<Employee>();

		private static readonly List<Behaviour> _stale = new List<Behaviour>();

		public string Name => "fix2-chemist-work";

		public string Status => "on=" + (Options.FixChemistWorkInProgress.Value ? 1 : 0) + " cauldronWork=" + _patched + " idleResets=" + _resets;

		public void Init()
		{
		}

		public void Reset()
		{
			_idleSince.Clear();
		}

		internal static void Apply(Chemist chemist, ref bool result)
		{
			if (!result && Rel.Gate("fix2-chemist-work", Options.FixChemistWorkInProgress.Value))
			{
				StartCauldronBehaviour startCauldronBehaviour = chemist.StartCauldronBehaviour;
				if (!((Object)(object)startCauldronBehaviour == (Object)null) && ((Behaviour)startCauldronBehaviour).Active)
				{
					result = true;
					_patched++;
				}
			}
		}

		public void Tick(float dt)
		{
			if (Time.time < _next)
			{
				return;
			}
			_next = Time.time + 1f;
			if (!Rel.Gate("fix2-chemist-work", Options.FixChemistWorkInProgress.Value))
			{
				return;
			}
			Rel.EmployeesInto(_employees);
			for (int i = 0; i < _employees.Count; i++)
			{
				Employee val = _employees[i];
				try
				{
					Chemist val2 = ((Il2CppObjectBase)val).TryCast<Chemist>();
					if ((Object)(object)val2 != (Object)null)
					{
						CheckIdle(val2, force: false);
					}
				}
				catch (Exception e)
				{
					Core.Caught("fix2: idle check " + Rel.Name((NPC)(object)val), e);
				}
			}
		}

		private static List<Behaviour> EnabledWork(Chemist c)
		{
			List<Behaviour> stale = _stale;
			stale.Clear();
			List<Behaviour> enabledBehaviours = ((NPC)c).Behaviour.enabledBehaviours;
			if (enabledBehaviours == null)
			{
				return stale;
			}
			for (int i = 0; i < enabledBehaviours.Count; i++)
			{
				Behaviour val = enabledBehaviours[i];
				if ((Object)(object)val != (Object)null && Rel.IsWork(val))
				{
					stale.Add(val);
				}
			}
			return stale;
		}

		private static void CheckIdle(Chemist c, bool force)
		{
			int instanceID = ((Object)c).GetInstanceID();
			if ((Object)(object)((NPC)c).Behaviour == (Object)null)
			{
				Rel.MsgOnce("fix2:nobeh:" + instanceID, "[fix2-chemist-work] " + Rel.Name((NPC)(object)c) + " has no behaviour component.");
				return;
			}
			List<Behaviour> list = EnabledWork(c);
			if ((Object)(object)((NPC)c).Behaviour.activeBehaviour != (Object)null || list.Count == 0)
			{
				_idleSince.Remove(instanceID);
				return;
			}
			if (!_idleSince.TryGetValue(instanceID, out var value))
			{
				_idleSince[instanceID] = Time.time;
				value = Time.time;
			}
			float num = Time.time - value;
			if ((!force && num < Options.ChemistIdleTimeout.Value) || !Rel.Enter("fix2.reset"))
			{
				return;
			}
			try
			{
				_idleSince.Remove(instanceID);
				string text = "[Fix2] reset " + Rel.Name((NPC)(object)c) + ": " + list.Count + " task(s) enabled but none running for " + num.ToString("F1") + " s (" + string.Join(", ", list.ConvertAll(Rel.TypeName)) + ").";
				if (force)
				{
					Instance log = Core.Log;
					if (log != null)
					{
						log.Msg(text);
					}
				}
				else
				{
					Rel.Action("fix2.reset", text);
				}
				int count = list.Count;
				Behaviour[] array = (Behaviour[])(object)new Behaviour[count];
				list.CopyTo(array);
				Fix3LockLeak.BeginBatch();
				try
				{
					for (int i = 0; i < count; i++)
					{
						array[i].Disable_Networked((NetworkConnection)null);
					}
				}
				finally
				{
					Fix3LockLeak.EndBatch((Employee)(object)c, "idle reset of " + count + " task(s)");
				}
				_resets++;
			}
			finally
			{
				Rel.Leave("fix2.reset");
			}
		}
	}
	[HarmonyPatch(typeof(Chemist), "IsAnyWorkInProgress")]
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal static class Awe_Fix2_IsAnyWorkInProgress_Patch
	{
		private static bool Prepare(MethodBase original)
		{
			if (!(original == null))
			{
				return NativeMethods.AllowPatch(original);
			}
			return true;
		}

		private static void Postfix(Chemist __instance, ref bool __result)
		{
			if (!Rel.Enter("fix2.isanywork"))
			{
				return;
			}
			try
			{
				Fix2ChemistWork.Apply(__instance, ref __result);
			}
			catch (Exception e)
			{
				Core.Failed("fix2: IsAnyWorkInProgress postfix", e);
			}
			finally
			{
				Rel.Leave("fix2.isanywork");
			}
		}
	}
	[SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")]
	internal sealed class Fix3LockLeak : IFeature
	{
		internal const string FeatureName = "fix3-lock-leak";

		internal const string SleepName = "fix3-sleep-release";

		private static int _stationLocks;

		private static int _slotLocks;

		private static int _sweeps;

		private static int _sleepReleases;

		private static readonly List<Employee> _pending = new List<Employee>();

		private float _nextPending;

		private static int _deferred;

		public string Name => "fix3-lock-leak";

		public string Status => "on=" + (Options.FixLockLeak.Value ? 1 : 0) + (Options.SleepSafeRelease.Value ? 1 : 0) + " sweeps=" + _sweeps + " stationLocks=" + _stationLocks + " slotLocks=" + _slotLocks + " sleepReleases=" + _sleepReleases + " deferred=" + _deferred + " pending=" + _pending.Count;

		internal static bool Batching { get; private set; }

		public void Init()
		{
		}

		public void Reset()
		{
			_pending.Clear();
		}

		public void Tick(float dt)
		{
			if (_pending.Count == 0 || Time.time < _nextPending)
			{
				return;
			}
			_nextPending = Time.time + 1f;
			try
			{
				ReleasePending();
			}
			catch (Exception e)
			{
				Core.Failed("fix3: release deferred employees", e);
			}
		}

		private static List<BuildableItem> PlacedItems()
		{
			List<BuildableItem> items = new List<BuildableItem>();
			HashSet<IntPtr> seen = new HashSet<IntPtr>();
			List<Property> properties = Property.Properties;
			if (properties != null)
			{
				for (int i = 0; i < properties.Count; i++)
				{
					Property obj = properties[i];
					From((obj != null) ? obj.BuildableItems : null);
				}
			}
			List<Business> businesses = Business.Businesses;
			if (businesses != null)
			{
				for (int j = 0; j < businesses.Count; j++)
				{
					Business obj2 = businesses[j];
					From((obj2 != null) ? ((Property)obj2).BuildableItems : null);
				}
			}
			retu