Decompiled source of TheRegulars v1.0.0

Mods/TheRegulars.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne;
using Il2CppScheduleOne.Core.Items.Framework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Effects;
using Il2CppScheduleOne.Events;
using Il2CppScheduleOne.GameTime;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Levelling;
using Il2CppScheduleOne.Map;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.NPCs.Framework;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.SpecialCustomers;
using Il2CppScheduleOne.SpecialCustomers.UI;
using Il2CppScheduleOne.UI;
using Il2CppScheduleOne.UI.Phone;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using TheRegulars;
using TheRegulars.Data;
using TheRegulars.Engine;
using TheRegulars.Phone;
using TheRegulars.Phone.Kit;
using TheRegulars.Services;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(RegularsMod), "The Regulars", "1.0.0", "thedoc15", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("TheRegulars")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c14b50228f911f5e49af3775cc25daeb45a3f088")]
[assembly: AssemblyProduct("TheRegulars")]
[assembly: AssemblyTitle("TheRegulars")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TheRegulars
{
	public static class Guard
	{
		public static class Features
		{
			public const string FixFirstVisit = "FixFirstVisit";

			public const string SleepWatchdog = "SleepWatchdog";

			public const string LateJoinSync = "LateJoinSync";

			public const string CoopMapPins = "CoopMapPins";

			public const string Alerts = "Alerts";

			public const string Tunables = "Tunables";

			public const string Steering = "Steering";

			public const string CallIn = "CallIn";

			public const string MarkOnMap = "MarkOnMap";

			public const string Snapshot = "Snapshot";

			public const string Engine = "Engine";

			public const string DebugDump = "DebugDump";

			public const string Faces = "Faces";
		}

		private static readonly List<string> Paused = new List<string>();

		private static readonly HashSet<string> PausedSet = new HashSet<string>(StringComparer.Ordinal);

		private static readonly HashSet<Delegate> BrokenListeners = new HashSet<Delegate>();

		private static string[] _pausedView = Array.Empty<string>();

		public static IReadOnlyList<string> PausedFeatures => _pausedView;

		public static event Action<string> FeatureDisabled;

		public static bool IsPaused(string feature)
		{
			if (feature != null)
			{
				return PausedSet.Contains(feature);
			}
			return false;
		}

		public static bool Run(string feature, Action body)
		{
			if (body == null || IsPaused(feature))
			{
				return false;
			}
			try
			{
				body();
				return true;
			}
			catch (Exception ex)
			{
				Trip(feature, ex);
				return false;
			}
		}

		public static T Run<T>(string feature, Func<T> body, T fallback)
		{
			if (body == null || IsPaused(feature))
			{
				return fallback;
			}
			try
			{
				return body();
			}
			catch (Exception ex)
			{
				Trip(feature, ex);
				return fallback;
			}
		}

		public static void Trip(string feature, Exception ex)
		{
			string name = (string.IsNullOrEmpty(feature) ? "Unknown" : feature);
			try
			{
				if (!PausedSet.Add(name))
				{
					return;
				}
				Paused.Add(name);
				_pausedView = Paused.ToArray();
				Log.Error("\"" + name + "\" hit an error and is paused until you restart the game. The game itself keeps running normally.", ex);
			}
			catch
			{
			}
			RaiseEach("Guard.FeatureDisabled", Guard.FeatureDisabled, delegate(Delegate h)
			{
				((Action<string>)h)(name);
			});
		}

		internal static void RaiseEach(string eventName, Delegate multicast, Action<Delegate> invoke)
		{
			if ((object)multicast == null || invoke == null)
			{
				return;
			}
			Delegate[] invocationList;
			try
			{
				invocationList = multicast.GetInvocationList();
			}
			catch
			{
				return;
			}
			foreach (Delegate obj2 in invocationList)
			{
				if ((object)obj2 == null || BrokenListeners.Contains(obj2))
				{
					continue;
				}
				try
				{
					invoke(obj2);
				}
				catch (Exception ex)
				{
					BrokenListeners.Add(obj2);
					try
					{
						Log.Error("A listener of " + eventName + " (" + Describe(obj2) + ") failed and was switched off.", ex);
					}
					catch
					{
					}
				}
			}
		}

		private static string Describe(Delegate d)
		{
			try
			{
				MethodInfo method = d.Method;
				return ((method.DeclaringType != null) ? (method.DeclaringType.FullName + ".") : string.Empty) + method.Name;
			}
			catch
			{
				return "unknown listener";
			}
		}
	}
	public static class ModInfo
	{
		public const string Name = "The Regulars";

		public const string Version = "1.0.0";

		public const string Author = "thedoc15";

		public const string NexusUrl = "";
	}
	public sealed class RegularsMod : MelonMod
	{
		public override void OnInitializeMelon()
		{
			Settings.Init();
			try
			{
				ServiceHost.Init();
				Steering.CanServe = Products.CanServe;
				Hooks.NoteRepair = History.NoteRepair;
				Hooks.CrewIcon = Faces.CrewIcon;
				Hooks.VisitCash = delegate(string id)
				{
					VisitRecord current = History.Current;
					return (current == null || !(current.CrewId == id)) ? (-1) : current.Cash;
				};
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Faces/products/history could not start; the fixes still run.", ex);
			}
			try
			{
				PhoneSupport.Init();
			}
			catch (Exception ex2)
			{
				((MelonBase)this).LoggerInstance.Error("The phone app could not start; the fixes still run.", ex2);
			}
			((MelonBase)this).LoggerInstance.Msg("The Regulars 1.0.0 loaded");
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (sceneName == "Main")
			{
				TheRegulars.Engine.Engine.Start();
				ServiceHost.Start();
			}
		}

		public override void OnSceneWasUnloaded(int buildIndex, string sceneName)
		{
			if (sceneName == "Main")
			{
				StopAll();
			}
		}

		public override void OnUpdate()
		{
			try
			{
				Settings.Tick();
			}
			catch
			{
			}
			TheRegulars.Engine.Engine.Update();
		}

		private static void StopAll()
		{
			TheRegulars.Engine.Engine.Stop();
			try
			{
				ServiceHost.Stop();
			}
			catch (Exception ex)
			{
				MelonLogger.Error("Services stop failed", ex);
			}
		}

		public override void OnApplicationQuit()
		{
			StopAll();
			Settings.Flush();
		}

		public override void OnDeinitializeMelon()
		{
			StopAll();
			Settings.Flush();
		}
	}
	public static class Settings
	{
		public static class Keys
		{
			public const string FixFirstVisit = "FixFirstVisit";

			public const string SleepWatchdog = "SleepWatchdog";

			public const string LateJoinSync = "LateJoinSync";

			public const string CoopMapPins = "CoopMapPins";

			public const string Alerts = "Alerts";

			public const string FirstVisitDelay = "FirstVisitDelay";

			public const string NightsBetweenVisits = "NightsBetweenVisits";

			public const string GuaranteedWhenDue = "GuaranteedWhenDue";

			public const string WarningNights = "WarningNights";

			public const string StayNights = "StayNights";

			public const string OrderSizePercent = "OrderSizePercent";

			public const string SkippedCrews = "SkippedCrews";

			public const string OnlyServableCrews = "OnlyServableCrews";

			public const string AllowCallIn = "AllowCallIn";

			public const string DebugKey = "DebugKey";
		}

		public const string CategoryId = "TheRegulars";

		public const string FileName = "TheRegulars.cfg";

		public const int GameFirstVisitDelay = 7;

		public const int GameNightsBetweenMin = 4;

		public const int GameNightsBetweenMax = 8;

		public const int GameWarningNights = 3;

		public const int GameStayNights = 3;

		public const int GameOrderSizePercent = 100;

		private const long SaveDelayMs = 500L;

		private static MelonPreferences_Category _category;

		private static MelonPreferences_Entry<bool> _fixFirstVisit;

		private static MelonPreferences_Entry<bool> _sleepWatchdog;

		private static MelonPreferences_Entry<bool> _lateJoinSync;

		private static MelonPreferences_Entry<bool> _coopMapPins;

		private static MelonPreferences_Entry<bool> _alerts;

		private static MelonPreferences_Entry<int> _firstVisitDelay;

		private static MelonPreferences_Entry<int> _nightsBetweenVisits;

		private static MelonPreferences_Entry<bool> _guaranteedWhenDue;

		private static MelonPreferences_Entry<int> _warningNights;

		private static MelonPreferences_Entry<int> _stayNights;

		private static MelonPreferences_Entry<int> _orderSizePercent;

		private static MelonPreferences_Entry<string> _skippedCrews;

		private static MelonPreferences_Entry<bool> _onlyServableCrews;

		private static MelonPreferences_Entry<bool> _allowCallIn;

		private static MelonPreferences_Entry<bool> _debugKey;

		private static string[] _skippedView = Array.Empty<string>();

		private static readonly Stopwatch Clock = Stopwatch.StartNew();

		private static bool _dirty;

		private static long _saveDueMs;

		private static int _batchDepth;

		private static bool _batchChanged;

		public static bool IsLoaded => _category != null;

		public static bool FixFirstVisit
		{
			get
			{
				return Get(_fixFirstVisit, fallback: true);
			}
			set
			{
				Set(_fixFirstVisit, value);
			}
		}

		public static bool SleepWatchdog
		{
			get
			{
				return Get(_sleepWatchdog, fallback: true);
			}
			set
			{
				Set(_sleepWatchdog, value);
			}
		}

		public static bool LateJoinSync
		{
			get
			{
				return Get(_lateJoinSync, fallback: true);
			}
			set
			{
				Set(_lateJoinSync, value);
			}
		}

		public static bool CoopMapPins
		{
			get
			{
				return Get(_coopMapPins, fallback: true);
			}
			set
			{
				Set(_coopMapPins, value);
			}
		}

		public static bool Alerts
		{
			get
			{
				return Get(_alerts, fallback: true);
			}
			set
			{
				Set(_alerts, value);
			}
		}

		public static int FirstVisitDelay
		{
			get
			{
				return Get(_firstVisitDelay, 0);
			}
			set
			{
				Set(_firstVisitDelay, ClampOverride(value, 14));
			}
		}

		public static int NightsBetweenVisits
		{
			get
			{
				return Get(_nightsBetweenVisits, 0);
			}
			set
			{
				Set(_nightsBetweenVisits, ClampOverride(value, 14));
			}
		}

		public static bool GuaranteedWhenDue
		{
			get
			{
				return Get(_guaranteedWhenDue, fallback: false);
			}
			set
			{
				Set(_guaranteedWhenDue, value);
			}
		}

		public static int WarningNights
		{
			get
			{
				return Get(_warningNights, 0);
			}
			set
			{
				Set(_warningNights, ClampOverride(value, 7));
			}
		}

		public static int StayNights
		{
			get
			{
				return Get(_stayNights, 0);
			}
			set
			{
				Set(_stayNights, ClampOverride(value, 7));
			}
		}

		public static int OrderSizePercent
		{
			get
			{
				return Get(_orderSizePercent, 100);
			}
			set
			{
				Set(_orderSizePercent, NormalizePercent(value));
			}
		}

		public static string SkippedCrews
		{
			get
			{
				return Get(_skippedCrews, string.Empty);
			}
			set
			{
				Set(_skippedCrews, NormalizeCsv(value));
			}
		}

		public static bool OnlyServableCrews
		{
			get
			{
				return Get(_onlyServableCrews, fallback: false);
			}
			set
			{
				Set(_onlyServableCrews, value);
			}
		}

		public static bool AllowCallIn
		{
			get
			{
				return Get(_allowCallIn, fallback: true);
			}
			set
			{
				Set(_allowCallIn, value);
			}
		}

		public static bool DebugKey
		{
			get
			{
				return Get(_debugKey, fallback: false);
			}
			set
			{
				Set(_debugKey, value);
			}
		}

		public static IReadOnlyList<string> SkippedCrewIds => _skippedView;

		public static event Action Changed;

		public static bool IsCrewSkipped(string crewId)
		{
			if (string.IsNullOrEmpty(crewId))
			{
				return false;
			}
			string[] skippedView = _skippedView;
			for (int i = 0; i < skippedView.Length; i++)
			{
				if (string.Equals(skippedView[i], crewId, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		public static void SetCrewSkipped(string crewId, bool skipped)
		{
			if (string.IsNullOrWhiteSpace(crewId))
			{
				return;
			}
			string item = crewId.Trim().ToLowerInvariant();
			List<string> list = new List<string>(_skippedView);
			bool flag = list.Contains(item);
			if (skipped && !flag)
			{
				list.Add(item);
			}
			else
			{
				if (!(!skipped && flag))
				{
					return;
				}
				list.Remove(item);
			}
			SkippedCrews = string.Join(",", list);
		}

		public static bool IsHostOnly(string key)
		{
			switch (key)
			{
			case "CoopMapPins":
			case "Alerts":
				return false;
			case "AllowCallIn":
			case "FixFirstVisit":
			case "SleepWatchdog":
			case "WarningNights":
			case "LateJoinSync":
			case "SkippedCrews":
			case "GuaranteedWhenDue":
			case "OnlyServableCrews":
			case "FirstVisitDelay":
			case "NightsBetweenVisits":
			case "StayNights":
			case "OrderSizePercent":
				return true;
			default:
				return false;
			}
		}

		public static void ResetToGame()
		{
			_batchDepth++;
			try
			{
				FirstVisitDelay = 0;
				NightsBetweenVisits = 0;
				GuaranteedWhenDue = false;
				WarningNights = 0;
				StayNights = 0;
				OrderSizePercent = 100;
				SkippedCrews = string.Empty;
				OnlyServableCrews = false;
			}
			finally
			{
				_batchDepth--;
			}
			if (_batchDepth == 0 && _batchChanged)
			{
				_batchChanged = false;
				RaiseChanged();
			}
		}

		public static void Init()
		{
			if (_category != null)
			{
				return;
			}
			try
			{
				string text = Path.Combine(MelonEnvironment.UserDataDirectory, "TheRegulars.cfg");
				bool num = File.Exists(text);
				_category = MelonPreferences.CreateCategory("TheRegulars", "The Regulars");
				_category.SetFilePath(text, true, false);
				_fixFirstVisit = _category.CreateEntry<bool>("FixFirstVisit", true, "Fix the first visit", "Repairs the special-customer visit the game breaks on new saves (the \"Hippies\" name bug). Host only.", false, false, (ValueValidator)null, (string)null);
				_sleepWatchdog = _category.CreateEntry<bool>("SleepWatchdog", true, "Sleep watchdog", "If the game silently skips special customers on a night, move them along anyway. Host only.", false, false, (ValueValidator)null, (string)null);
				_lateJoinSync = _category.CreateEntry<bool>("LateJoinSync", true, "Sync late joiners", "Tell players who join mid-game where the special customers are at. Host only.", false, false, (ValueValidator)null, (string)null);
				_coopMapPins = _category.CreateEntry<bool>("CoopMapPins", true, "Map pin for co-op players", "Show the crew's camp on your map when you are not the host (the game only gives the host a pin).", false, false, (ValueValidator)null, (string)null);
				_alerts = _category.CreateEntry<bool>("Alerts", true, "Alerts", "Game notifications when a crew is coming, arrives, has its last night, or leaves.", false, false, (ValueValidator)null, (string)null);
				_firstVisitDelay = _category.CreateEntry<int>("FirstVisitDelay", 0, "First visit delay", "Sleeps before the first crew can be picked on a new save. 0 = game (7), else 1 to 14. Host only.", false, false, (ValueValidator)null, (string)null);
				_nightsBetweenVisits = _category.CreateEntry<int>("NightsBetweenVisits", 0, "Nights between visits", "Sleeps after a crew leaves before the next pick. 0 = game (4 to 8), else 1 to 14. Host only.", false, false, (ValueValidator)null, (string)null);
				_guaranteedWhenDue = _category.CreateEntry<bool>("GuaranteedWhenDue", false, "Skip the luck roll", "After the wait, the game only has a 25% chance each night to pick a crew (+10% per miss). On = one comes the first night. Host only.", false, false, (ValueValidator)null, (string)null);
				_warningNights = _category.CreateEntry<int>("WarningNights", 0, "Warning nights", "Sleeps between \"coming to town\" and arrival. 0 = game (3), else 1 to 7. Host only.", false, false, (ValueValidator)null, (string)null);
				_stayNights = _category.CreateEntry<int>("StayNights", 0, "Stay nights", "Nights a crew stays in town. 0 = game (3), else 1 to 7. Host only.", false, false, (ValueValidator)null, (string)null);
				_orderSizePercent = _category.CreateEntry<int>("OrderSizePercent", 100, "Order size", "How much crews buy per day, in percent of the game's amount. 50 to 300 in steps of 25. Host only.", false, false, (ValueValidator)null, (string)null);
				_skippedCrews = _category.CreateEntry<string>("SkippedCrews", string.Empty, "Skipped crews", "Comma-separated crew ids the game should not pick (bikers, businessmen, hippies, partybus). Host only.", false, false, (ValueValidator)null, (string)null);
				_onlyServableCrews = _category.CreateEntry<bool>("OnlyServableCrews", false, "Only crews I can serve", "Skip crews whose drugs you can't make yet. Host only.", false, false, (ValueValidator)null, (string)null);
				_allowCallIn = _category.CreateEntry<bool>("AllowCallIn", true, "Call-ins and send-aways", "Allow \"Call them in\" and \"Send them away\" in the phone app. Host only.", false, false, (ValueValidator)null, (string)null);
				_debugKey = _category.CreateEntry<bool>("DebugKey", false, "Debug dump key (F10)", "Press F10 in game to write a status report to MelonLoader/Latest.log (only needed for bug reports).", false, false, (ValueValidator)null, (string)null);
				Sanitize();
				if (!num || _dirty)
				{
					SaveNow();
				}
			}
			catch (Exception ex)
			{
				Log.Error("Settings could not be loaded; the game's own behaviour is used.", ex);
			}
		}

		public static void Tick()
		{
			if (_dirty && Clock.ElapsedMilliseconds >= _saveDueMs)
			{
				SaveNow();
			}
		}

		public static void Flush()
		{
			if (_dirty)
			{
				SaveNow();
			}
		}

		private static T Get<T>(MelonPreferences_Entry<T> entry, T fallback)
		{
			try
			{
				return (entry != null) ? entry.Value : fallback;
			}
			catch
			{
				return fallback;
			}
		}

		private static void Set<T>(MelonPreferences_Entry<T> entry, T value)
		{
			if (entry != null && !EqualityComparer<T>.Default.Equals(entry.Value, value))
			{
				entry.Value = value;
				if ((object)entry == _skippedCrews)
				{
					RebuildSkippedView();
				}
				MarkDirty();
				if (_batchDepth > 0)
				{
					_batchChanged = true;
				}
				else
				{
					RaiseChanged();
				}
			}
		}

		private static void MarkDirty()
		{
			_dirty = true;
			_saveDueMs = Clock.ElapsedMilliseconds + 500;
		}

		private static void SaveNow()
		{
			_dirty = false;
			try
			{
				MelonPreferences_Category category = _category;
				if (category != null)
				{
					category.SaveToFile(false);
				}
			}
			catch (Exception ex)
			{
				Log.Error("Settings could not be saved to UserData/TheRegulars.cfg.", ex);
			}
		}

		private static void RaiseChanged()
		{
			Guard.RaiseEach("Settings.Changed", Settings.Changed, delegate(Delegate h)
			{
				((Action)h)();
			});
		}

		private static void Sanitize()
		{
			SanitizeInt(_firstVisitDelay, ClampOverride(_firstVisitDelay.Value, 14));
			SanitizeInt(_nightsBetweenVisits, ClampOverride(_nightsBetweenVisits.Value, 14));
			SanitizeInt(_warningNights, ClampOverride(_warningNights.Value, 7));
			SanitizeInt(_stayNights, ClampOverride(_stayNights.Value, 7));
			SanitizeInt(_orderSizePercent, NormalizePercent(_orderSizePercent.Value));
			string text = NormalizeCsv(_skippedCrews.Value);
			if (!string.Equals(text, _skippedCrews.Value, StringComparison.Ordinal))
			{
				_skippedCrews.Value = text;
				MarkDirty();
			}
			RebuildSkippedView();
		}

		private static void SanitizeInt(MelonPreferences_Entry<int> entry, int sane)
		{
			if (entry.Value != sane)
			{
				entry.Value = sane;
				MarkDirty();
			}
		}

		private static int ClampOverride(int value, int max)
		{
			if (value <= 0)
			{
				return 0;
			}
			if (value <= max)
			{
				return value;
			}
			return max;
		}

		private static int NormalizePercent(int value)
		{
			int num = (int)Math.Round((double)value / 25.0, MidpointRounding.AwayFromZero) * 25;
			if (num < 50)
			{
				return 50;
			}
			if (num <= 300)
			{
				return num;
			}
			return 300;
		}

		private static string NormalizeCsv(string csv)
		{
			if (string.IsNullOrWhiteSpace(csv))
			{
				return string.Empty;
			}
			IEnumerable<string> values = (from s in csv.Split(new char[6] { ',', ';', ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim().ToLowerInvariant() into s
				where s.Length > 0
				select s).Distinct<string>(StringComparer.Ordinal);
			return string.Join(",", values);
		}

		private static void RebuildSkippedView()
		{
			string text = Get(_skippedCrews, string.Empty);
			_skippedView = (string.IsNullOrEmpty(text) ? Array.Empty<string>() : text.Split(','));
		}
	}
}
namespace TheRegulars.Services
{
	public static class Faces
	{
		private static Sprite _vanIconCache;

		private static bool _vanIconLookupFailed;

		public static event Action<string> Updated;

		internal static void Init()
		{
			PurgeOldRenderCache();
		}

		internal static void Start()
		{
		}

		internal static void Stop()
		{
			_vanIconCache = null;
			_vanIconLookupFailed = false;
		}

		public static Sprite Get(string npcId)
		{
			return null;
		}

		public static Sprite CrewIcon(string crewId)
		{
			return VanIcon();
		}

		private static Sprite VanIcon()
		{
			if ((Object)(object)_vanIconCache != (Object)null)
			{
				return _vanIconCache;
			}
			if (_vanIconLookupFailed)
			{
				return null;
			}
			try
			{
				IncomingSpecialCustomerInfoPopup val = Object.FindObjectOfType<IncomingSpecialCustomerInfoPopup>();
				if ((Object)(object)val != (Object)null && Object.op_Implicit((Object)(object)val))
				{
					Image groupIconImage = val._groupIconImage;
					if ((Object)(object)groupIconImage != (Object)null && Object.op_Implicit((Object)(object)groupIconImage) && (Object)(object)groupIconImage.sprite != (Object)null)
					{
						_vanIconCache = groupIconImage.sprite;
						return _vanIconCache;
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] Faces: van icon lookup failed: " + ex.Message);
			}
			_vanIconLookupFailed = true;
			return null;
		}

		private static void PurgeOldRenderCache()
		{
			try
			{
				string path = Path.Combine(MelonEnvironment.UserDataDirectory, "TheRegulars");
				int num = PurgeCacheFolder(Path.Combine(path, "faces")) + PurgeCacheFolder(Path.Combine(path, "faces-v2"));
				if (num > 0)
				{
					MelonLogger.Msg($"[Faces] purged {num} stale render(s) from the old runtime-render cache.");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] Faces: could not purge the old faces cache: " + ex.Message);
			}
		}

		private static int PurgeCacheFolder(string dir)
		{
			if (!Directory.Exists(dir))
			{
				return 0;
			}
			string[] files = Directory.GetFiles(dir, "*.png");
			string[] array = files;
			for (int i = 0; i < array.Length; i++)
			{
				File.Delete(array[i]);
			}
			if (Directory.GetFileSystemEntries(dir).Length == 0)
			{
				Directory.Delete(dir);
			}
			return files.Length;
		}
	}
	public sealed class VisitRecord
	{
		public string CrewId;

		public int ArrivedDay;

		public int LeftDay;

		public int Deals;

		public int Units;

		public long Cash;
	}
	public static class History
	{
		public sealed class Document
		{
			public int Version = 1;

			public List<VisitRecord> Visits = new List<VisitRecord>();

			public VisitRecord Current;

			public int RepairsThisSave;
		}

		private const float DebounceSeconds = 2f;

		private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			WriteIndented = true,
			IncludeFields = true
		};

		private static Document _doc;

		private static string _saveKey;

		private static bool _loggedCorrupt;

		private static object _tickToken;

		private static float _dirtySecondsRemaining = -1f;

		public static IReadOnlyList<VisitRecord> Visits
		{
			get
			{
				try
				{
					EnsureLoaded();
					return _doc.Visits;
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[TheRegulars] History.Visits threw: " + ex.Message);
					return Array.Empty<VisitRecord>();
				}
			}
		}

		public static VisitRecord Current
		{
			get
			{
				try
				{
					EnsureLoaded();
					return _doc.Current;
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[TheRegulars] History.Current threw: " + ex.Message);
					return null;
				}
			}
		}

		public static int RepairsThisSave
		{
			get
			{
				try
				{
					EnsureLoaded();
					return _doc.RepairsThisSave;
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[TheRegulars] History.RepairsThisSave threw: " + ex.Message);
					return 0;
				}
			}
		}

		public static event Action Changed;

		internal static void Start()
		{
			CrewEvents.Arrived += OnArrived;
			CrewEvents.Left += OnLeft;
			CrewEvents.Deal += OnDeal;
			CrewEvents.SceneReady += OnSceneReady;
			if (_tickToken == null)
			{
				try
				{
					_tickToken = MelonCoroutines.Start(TickLoop());
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[TheRegulars] History: could not start the flush timer: " + ex.Message);
				}
			}
		}

		internal static void Stop()
		{
			CrewEvents.Arrived -= OnArrived;
			CrewEvents.Left -= OnLeft;
			CrewEvents.Deal -= OnDeal;
			CrewEvents.SceneReady -= OnSceneReady;
			if (_tickToken != null)
			{
				try
				{
					MelonCoroutines.Stop(_tickToken);
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[TheRegulars] History: error stopping the flush timer: " + ex.Message);
				}
				_tickToken = null;
			}
			SafeFlush();
			_doc = null;
			_saveKey = null;
			_loggedCorrupt = false;
			_dirtySecondsRemaining = -1f;
		}

		public static long TotalFrom(string crewId)
		{
			try
			{
				EnsureLoaded();
				if (string.IsNullOrEmpty(crewId))
				{
					return 0L;
				}
				long num = 0L;
				foreach (VisitRecord visit in _doc.Visits)
				{
					if (string.Equals(visit.CrewId, crewId, StringComparison.OrdinalIgnoreCase))
					{
						num += visit.Cash;
					}
				}
				if (_doc.Current != null && string.Equals(_doc.Current.CrewId, crewId, StringComparison.OrdinalIgnoreCase))
				{
					num += _doc.Current.Cash;
				}
				return num;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History.TotalFrom threw: " + ex.Message);
				return 0L;
			}
		}

		public static void NoteRepair()
		{
			try
			{
				EnsureLoaded();
				_doc.RepairsThisSave++;
				MarkDirty();
				RaiseChanged();
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History.NoteRepair threw: " + ex.Message);
			}
		}

		private static void OnArrived(string crewId)
		{
			try
			{
				if (!string.IsNullOrEmpty(crewId))
				{
					EnsureLoaded();
					if (_doc.Current != null && !string.Equals(_doc.Current.CrewId, crewId, StringComparison.OrdinalIgnoreCase))
					{
						CloseCurrent(unknownLeftDay: true);
					}
					if (_doc.Current == null)
					{
						_doc.Current = new VisitRecord
						{
							CrewId = crewId,
							ArrivedDay = CurrentGameDay(),
							LeftDay = -1
						};
						MarkDirty();
						RaiseChanged();
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History: Arrived handler threw: " + ex.Message);
			}
		}

		private static void OnLeft(string crewId)
		{
			try
			{
				EnsureLoaded();
				if (_doc.Current != null && (string.IsNullOrEmpty(crewId) || string.Equals(_doc.Current.CrewId, crewId, StringComparison.OrdinalIgnoreCase)))
				{
					CloseCurrent(unknownLeftDay: false);
					MarkDirty();
					RaiseChanged();
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History: Left handler threw: " + ex.Message);
			}
		}

		private static void OnDeal(DealInfo deal)
		{
			try
			{
				if (deal != null && deal.BySelf)
				{
					EnsureLoaded();
					if (_doc.Current == null)
					{
						_doc.Current = new VisitRecord
						{
							CrewId = deal.CrewId,
							ArrivedDay = -1,
							LeftDay = -1
						};
					}
					else if (!string.Equals(_doc.Current.CrewId, deal.CrewId, StringComparison.OrdinalIgnoreCase))
					{
						return;
					}
					_doc.Current.Deals++;
					_doc.Current.Units += deal.Units;
					_doc.Current.Cash += deal.Cash;
					MarkDirty();
					RaiseChanged();
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History: Deal handler threw: " + ex.Message);
			}
		}

		private static void OnSceneReady()
		{
			try
			{
				EnsureLoaded();
				Snapshot now = Crews.Now;
				if (now != null && now.Phase == CrewPhase.InTown && !string.IsNullOrEmpty(now.ActiveCrewId))
				{
					if (_doc.Current != null && !string.Equals(_doc.Current.CrewId, now.ActiveCrewId, StringComparison.OrdinalIgnoreCase))
					{
						CloseCurrent(unknownLeftDay: true);
					}
					if (_doc.Current == null)
					{
						_doc.Current = new VisitRecord
						{
							CrewId = now.ActiveCrewId,
							ArrivedDay = -1,
							LeftDay = -1
						};
						MarkDirty();
						RaiseChanged();
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History: SceneReady handler threw: " + ex.Message);
			}
		}

		private static void CloseCurrent(bool unknownLeftDay)
		{
			VisitRecord current = _doc.Current;
			if (current != null)
			{
				current.LeftDay = (unknownLeftDay ? (-1) : CurrentGameDay());
				_doc.Visits.Insert(0, current);
				_doc.Current = null;
			}
		}

		private static int CurrentGameDay()
		{
			return Crews.Now?.GameDay ?? (-1);
		}

		private static void RaiseChanged()
		{
			try
			{
				History.Changed?.Invoke();
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History.Changed handler threw: " + ex.Message);
			}
		}

		private static void MarkDirty()
		{
			_dirtySecondsRemaining = 2f;
		}

		private static IEnumerator TickLoop()
		{
			while (true)
			{
				yield return null;
				if (!(_dirtySecondsRemaining < 0f))
				{
					_dirtySecondsRemaining -= Time.unscaledDeltaTime;
					if (_dirtySecondsRemaining <= 0f)
					{
						_dirtySecondsRemaining = -1f;
						SafeFlush();
					}
				}
			}
		}

		private static void SafeFlush()
		{
			if (_doc == null || string.IsNullOrEmpty(_saveKey))
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(SavesDir());
				string text = DocPath(_saveKey);
				string text2 = text + ".tmp";
				string contents = JsonSerializer.Serialize(_doc, JsonOptions);
				File.WriteAllText(text2, contents);
				File.Move(text2, text, overwrite: true);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History: write failed: " + ex.Message);
			}
		}

		private static void EnsureLoaded()
		{
			if (_doc == null)
			{
				string text = null;
				try
				{
					text = ResolveSaveKey();
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[TheRegulars] History: could not resolve the active save: " + ex.Message);
				}
				if (string.IsNullOrEmpty(text))
				{
					_saveKey = null;
					_doc = new Document();
				}
				else
				{
					_saveKey = text;
					_doc = LoadOrCreate(text);
				}
			}
		}

		private static string ResolveSaveKey()
		{
			LoadManager instance = Singleton<LoadManager>.Instance;
			if ((Object)(object)instance == (Object)null || !Object.op_Implicit((Object)(object)instance))
			{
				return null;
			}
			string text = null;
			SaveInfo activeSaveInfo = instance.ActiveSaveInfo;
			if (activeSaveInfo != null)
			{
				text = activeSaveInfo.SavePath;
			}
			if (string.IsNullOrEmpty(text))
			{
				text = instance.LoadedGameFolderPath;
			}
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			string[] array = text.Replace('\\', '/').TrimEnd('/').Split('/');
			string text2 = ((array.Length >= 2) ? (array[^2] + "_" + array[^1]) : ((array.Length == 1) ? array[0] : null));
			if (!string.IsNullOrEmpty(text2))
			{
				return SanitizeFileName(text2);
			}
			return null;
		}

		private static string SanitizeFileName(string name)
		{
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			StringBuilder stringBuilder = new StringBuilder(name.Length);
			foreach (char c in name)
			{
				stringBuilder.Append((Array.IndexOf(invalidFileNameChars, c) >= 0) ? '_' : c);
			}
			return stringBuilder.ToString();
		}

		private static string SavesDir()
		{
			return Path.Combine(MelonEnvironment.UserDataDirectory, "TheRegulars", "saves");
		}

		private static string DocPath(string key)
		{
			return Path.Combine(SavesDir(), key + ".json");
		}

		private static Document LoadOrCreate(string key)
		{
			string text;
			try
			{
				Directory.CreateDirectory(SavesDir());
				text = DocPath(key);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] History: could not prepare the saves folder: " + ex.Message);
				return new Document();
			}
			if (!File.Exists(text))
			{
				return new Document();
			}
			try
			{
				Document? obj = JsonSerializer.Deserialize<Document>(File.ReadAllText(text), JsonOptions) ?? throw new InvalidDataException("empty document");
				Document document = obj;
				if (document.Visits == null)
				{
					document.Visits = new List<VisitRecord>();
				}
				return obj;
			}
			catch (Exception ex2)
			{
				if (!_loggedCorrupt)
				{
					_loggedCorrupt = true;
					MelonLogger.Warning($"[TheRegulars] History: save file for '{key}' was unreadable ({ex2.Message}); backing it up as .bad and starting fresh.");
				}
				try
				{
					string text2 = text + ".bad";
					if (File.Exists(text2))
					{
						File.Delete(text2);
					}
					File.Move(text, text2);
				}
				catch (Exception ex3)
				{
					MelonLogger.Warning("[TheRegulars] History: could not back up the corrupt file: " + ex3.Message);
				}
				return new Document();
			}
		}
	}
	public sealed class ProductMatch
	{
		public string ProductId;

		public string Name;

		public DrugKind Drug;

		public int Matches;

		public float Multiplier;

		public int EstPricePerUnit;

		public bool Accepted;
	}
	public static class Products
	{
		private sealed class Candidate
		{
			public string Id;

			public string Name;

			public DrugKind Drug;

			public List<string> MatchedIds;

			public float Multiplier;

			public int EstPricePerUnit;
		}

		public static IReadOnlyList<ProductMatch> BestFor(string crewId, int max)
		{
			try
			{
				CrewInfo crewInfo = ResolveCrew(crewId);
				if (crewInfo == null)
				{
					return Array.Empty<ProductMatch>();
				}
				List<Candidate> list = BuildCandidates(crewInfo);
				SortCandidates(list);
				int num = ((max > 0) ? Math.Min(max, list.Count) : list.Count);
				List<ProductMatch> list2 = new List<ProductMatch>(num);
				for (int i = 0; i < num; i++)
				{
					Candidate candidate = list[i];
					list2.Add(new ProductMatch
					{
						ProductId = candidate.Id,
						Name = candidate.Name,
						Drug = candidate.Drug,
						Matches = candidate.MatchedIds.Count,
						Multiplier = candidate.Multiplier,
						EstPricePerUnit = candidate.EstPricePerUnit,
						Accepted = true
					});
				}
				return list2;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] Products.BestFor threw: " + ex.Message);
				return Array.Empty<ProductMatch>();
			}
		}

		public static bool CanServe(string crewId)
		{
			try
			{
				CrewInfo crewInfo = ResolveCrew(crewId);
				if (crewInfo == null || crewInfo.Drugs == null || crewInfo.Drugs.Count == 0)
				{
					return true;
				}
				HashSet<DrugKind> hashSet = new HashSet<DrugKind>(crewInfo.Drugs);
				foreach (ProductDefinition item in DiscoveredProductDefinitions())
				{
					if (TryDrugKind(item, out var drug) && hashSet.Contains(drug))
					{
						return true;
					}
				}
				return false;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] Products.CanServe threw: " + ex.Message);
				return true;
			}
		}

		public static IReadOnlyList<string> MatchingEffectIds(string crewId)
		{
			try
			{
				CrewInfo crewInfo = ResolveCrew(crewId);
				if (crewInfo == null || crewInfo.Effects == null || crewInfo.Effects.Count == 0)
				{
					return Array.Empty<string>();
				}
				List<Candidate> list = BuildCandidates(crewInfo);
				if (list.Count == 0)
				{
					return Array.Empty<string>();
				}
				SortCandidates(list);
				Candidate candidate = list[0];
				if (candidate.MatchedIds.Count == 0)
				{
					return Array.Empty<string>();
				}
				HashSet<string> hashSet = new HashSet<string>(candidate.MatchedIds, StringComparer.OrdinalIgnoreCase);
				List<string> list2 = new List<string>();
				foreach (EffectInfo effect in crewInfo.Effects)
				{
					if (effect != null && !string.IsNullOrEmpty(effect.Id) && hashSet.Contains(effect.Id))
					{
						list2.Add(effect.Id);
					}
				}
				return list2;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[TheRegulars] Products.MatchingEffectIds threw: " + ex.Message);
				return Array.Empty<string>();
			}
		}

		private static CrewInfo ResolveCrew(string crewId)
		{
			if (!string.IsNullOrEmpty(crewId))
			{
				return Crews.Get(crewId);
			}
			return null;
		}

		private static List<Candidate> BuildCandidates(CrewInfo crew)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (crew.Effects != null)
			{
				foreach (EffectInfo effect in crew.Effects)
				{
					if (effect != null && !string.IsNullOrEmpty(effect.Id))
					{
						hashSet.Add(effect.Id);
					}
				}
			}
			HashSet<DrugKind> hashSet2 = ((crew.Drugs != null) ? new HashSet<DrugKind>(crew.Drugs) : new HashSet<DrugKind>());
			List<Candidate> list = new List<Candidate>();
			foreach (ProductDefinition item in DiscoveredProductDefinitions())
			{
				if (!TryDrugKind(item, out var drug) || !hashSet2.Contains(drug))
				{
					continue;
				}
				List<string> list2 = new List<string>();
				List<Effect> properties = ((PropertyItemDefinition)item).Properties;
				if (properties != null)
				{
					Enumerator<Effect> enumerator3 = properties.GetEnumerator();
					while (enumerator3.MoveNext())
					{
						Effect current3 = enumerator3.Current;
						if (!((Object)(object)current3 == (Object)null) && Object.op_Implicit((Object)(object)current3))
						{
							string iD = current3.ID;
							if (!string.IsNullOrEmpty(iD) && hashSet.Contains(iD))
							{
								list2.Add(iD);
							}
						}
					}
				}
				float num = 1f + 0.4f * (float)list2.Count;
				int estPricePerUnit = (int)Math.Round(item.MarketValue * num, MidpointRounding.AwayFromZero);
				list.Add(new Candidate
				{
					Id = ((BaseItemDefinition)item).ID,
					Name = ((BaseItemDefinition)item).Name,
					Drug = drug,
					MatchedIds = list2,
					Multiplier = num,
					EstPricePerUnit = estPricePerUnit
				});
			}
			return list;
		}

		private static void SortCandidates(List<Candidate> candidates)
		{
			candidates.Sort(delegate(Candidate a, Candidate b)
			{
				int num = b.Multiplier.CompareTo(a.Multiplier);
				return (num == 0) ? b.EstPricePerUnit.CompareTo(a.EstPricePerUnit) : num;
			});
		}

		private static bool TryDrugKind(ProductDefinition def, out DrugKind drug)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected I4, but got Unknown
			drug = DrugKind.Weed;
			if ((Object)(object)def == (Object)null || !Object.op_Implicit((Object)(object)def))
			{
				return false;
			}
			try
			{
				drug = (DrugKind)def.DrugType;
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static List<ProductDefinition> DiscoveredProductDefinitions()
		{
			List<ProductDefinition> list = new List<ProductDefinition>();
			ProductManager instance = NetworkSingleton<ProductManager>.Instance;
			if ((Object)(object)instance == (Object)null || !Object.op_Implicit((Object)(object)instance))
			{
				return list;
			}
			List<ProductDefinition> discoveredProducts = ProductManager.DiscoveredProducts;
			if (discoveredProducts == null)
			{
				return list;
			}
			Enumerator<ProductDefinition> enumerator = discoveredProducts.GetEnumerator();
			while (enumerator.MoveNext())
			{
				ProductDefinition current = enumerator.Current;
				if ((Object)(object)current != (Object)null && Object.op_Implicit((Object)(object)current))
				{
					list.Add(current);
				}
			}
			return list;
		}
	}
	public static class ServiceHost
	{
		private static bool _started;

		public static void Init()
		{
			Faces.Init();
		}

		public static void Start()
		{
			if (!_started)
			{
				_started = true;
				Faces.Start();
				History.Start();
			}
		}

		public static void Stop()
		{
			if (_started)
			{
				_started = false;
				History.Stop();
				Faces.Stop();
			}
		}
	}
}
namespace TheRegulars.Phone
{
	internal static class AppLog
	{
		private sealed class ErrorState
		{
			internal int Suppressed;

			internal float NextSummaryAt;
		}

		private const float SummaryInterval = 60f;

		private static readonly Dictionary<string, ErrorState> Errors = new Dictionary<string, ErrorState>(StringComparer.Ordinal);

		internal static void Info(string message)
		{
			MelonLogger.Msg("[Phone] " + message);
		}

		internal static void Warn(string message)
		{
			MelonLogger.Warning("[Phone] " + message);
		}

		internal static void Error(string key, Exception ex)
		{
			float num = SafeNow();
			if (!Errors.TryGetValue(key, out var value))
			{
				Errors[key] = new ErrorState
				{
					NextSummaryAt = num + 60f
				};
				MelonLogger.Error("[Phone] " + key + " failed: " + ex);
				return;
			}
			value.Suppressed++;
			if (num >= value.NextSummaryAt)
			{
				MelonLogger.Error($"[Phone] {key} failed {value.Suppressed} more time(s) in the last minute. Latest: {ex.GetType().Name}: {ex.Message}");
				value.Suppressed = 0;
				value.NextSummaryAt = num + 60f;
			}
		}

		internal static bool Guard(string key, Action action)
		{
			try
			{
				action();
				return true;
			}
			catch (Exception ex)
			{
				Error(key, ex);
				return false;
			}
		}

		private static float SafeNow()
		{
			try
			{
				return Time.realtimeSinceStartup;
			}
			catch
			{
				return 0f;
			}
		}
	}
	internal sealed class AppShell
	{
		private sealed class PageSlot
		{
			internal IAppPage Page;

			internal RectTransform Root;

			internal CanvasGroup Group;

			internal ScrollRect Scroll;

			internal RectTransform Content;

			internal bool Built;
		}

		private sealed class TabSlot
		{
			internal TabId Id;

			internal string Label;

			internal string IconName;

			internal Func<IAppPage> RootFactory;

			internal readonly List<PageSlot> Stack = new List<PageSlot>(4);

			internal Button NavButton;

			internal Image NavIcon;

			internal TextMeshProUGUI NavLabel;
		}

		private enum NavAnim
		{
			TabSwitch,
			Push,
			Pop
		}

		private const float FallbackWidth = 360f;

		private const float FallbackHeight = 640f;

		private const float ReferenceWidth = 360f;

		private readonly Func<bool> _isOpen;

		private readonly List<TabSlot> _tabs = new List<TabSlot>(4);

		private TabSlot _currentTab;

		private RectTransform _root;

		private RectTransform _topBarRow;

		private Button _backButton;

		private TextMeshProUGUI _title;

		private Chip _infoChip;

		private RectTransform _contentHost;

		private RectTransform _tabBarRow;

		private Overlay _overlay;

		private object _loopToken;

		private bool _alive;

		private bool _wasOpen;

		private bool _refreshRequested;

		private float _refreshTimer;

		internal static AppShell Current { get; private set; }

		internal float Width { get; private set; } = 360f;

		internal float Height { get; private set; } = 640f;

		private float ContainerWidth { get; set; } = 360f;

		private float ContainerHeight { get; set; } = 640f;

		internal RectTransform OverlayLayer => _overlay?.Layer;

		internal Overlay OverlayHost => _overlay;

		internal bool IsOpen => _wasOpen;

		internal bool CanGoBack
		{
			get
			{
				if (_currentTab != null)
				{
					return _currentTab.Stack.Count > 1;
				}
				return false;
			}
		}

		internal TabId CurrentTab => _currentTab?.Id ?? TabId.Overview;

		private AppShell(Func<bool> isOpen)
		{
			_isOpen = isOpen;
		}

		internal static void Build(GameObject container, Func<bool> isOpen)
		{
			Shutdown();
			AppShell appShell = (Current = new AppShell(isOpen));
			try
			{
				appShell.BuildUi(container);
				appShell.StartLoop();
			}
			catch
			{
				Current = null;
				appShell.Dispose();
				throw;
			}
		}

		internal static void Shutdown()
		{
			AppShell current = Current;
			Current = null;
			current?.Dispose();
		}

		private void Dispose()
		{
			_alive = false;
			if (_loopToken != null)
			{
				try
				{
					MelonCoroutines.Stop(_loopToken);
				}
				catch (Exception ex)
				{
					AppLog.Error("loop.stop", ex);
				}
				_loopToken = null;
			}
			PageSlot pageSlot = CurrentSlot();
			if (_wasOpen && pageSlot != null && pageSlot.Built)
			{
				AppLog.Guard("page.hide", pageSlot.Page.OnHide);
			}
			if (_overlay != null)
			{
				AppLog.Guard("shutdown.overlay", _overlay.Destroy);
			}
			Tween.Clear();
			ControlLoop.Reset();
			Ui.Reset();
			if (!Ui.Dead((Object)(object)_root))
			{
				try
				{
					Object.Destroy((Object)(object)((Component)_root).gameObject);
				}
				catch
				{
				}
			}
			_tabs.Clear();
			_currentTab = null;
			_root = null;
			_topBarRow = null;
			_backButton = null;
			_title = null;
			_infoChip = null;
			_contentHost = null;
			_tabBarRow = null;
			_overlay = null;
			_wasOpen = false;
		}

		private void StartLoop()
		{
			_alive = true;
			_loopToken = MelonCoroutines.Start(Loop());
		}

		private IEnumerator Loop()
		{
			while (_alive)
			{
				yield return null;
				if (!_alive)
				{
					break;
				}
				if (Ui.Dead((Object)(object)_root))
				{
					_loopToken = null;
					if (Current == this)
					{
						Shutdown();
					}
					else
					{
						Dispose();
					}
					break;
				}
				bool flag;
				try
				{
					flag = _isOpen();
				}
				catch
				{
					flag = false;
				}
				try
				{
					if (flag)
					{
						if (!_wasOpen)
						{
							_wasOpen = true;
							OnOpened();
						}
						TickOpen(Time.unscaledDeltaTime);
					}
					else if (_wasOpen)
					{
						_wasOpen = false;
						OnClosed();
					}
				}
				catch (Exception ex)
				{
					AppLog.Error("loop", ex);
				}
			}
		}

		private void OnOpened()
		{
			_refreshTimer = 0f;
			_refreshRequested = false;
			Remeasure();
			PageSlot pageSlot = CurrentSlot();
			if (pageSlot != null && pageSlot.Built)
			{
				AppLog.Guard("page.show", pageSlot.Page.OnShow);
			}
			_overlay?.RefreshSheet();
		}

		private void OnClosed()
		{
			_overlay?.OnAppClosed();
			TextField.ReleaseTyping();
			Tween.CompleteAll();
			ControlLoop.OnAppClosed();
			PageSlot pageSlot = CurrentSlot();
			if (pageSlot != null && pageSlot.Built)
			{
				AppLog.Guard("page.hide", pageSlot.Page.OnHide);
			}
		}

		private void TickOpen(float dt)
		{
			Tween.Tick(dt);
			ControlLoop.Tick(dt);
			_overlay?.Tick(dt);
			_refreshTimer += dt;
			if (_refreshTimer >= 1f || _refreshRequested)
			{
				_refreshTimer = 0f;
				_refreshRequested = false;
				RefreshVisible();
			}
		}

		private void RefreshVisible()
		{
			PageSlot pageSlot = CurrentSlot();
			if (pageSlot != null && pageSlot.Built)
			{
				AppLog.Guard("page.refresh", pageSlot.Page.Refresh);
			}
			_overlay?.RefreshSheet();
		}

		internal void Push(IAppPage page)
		{
			if (_currentTab != null && page != null)
			{
				_overlay?.CloseDropdown();
				_overlay?.CloseSheet();
				HideCurrentSlot();
				_currentTab.Stack.Add(new PageSlot
				{
					Page = page
				});
				ShowTopOfStack(NavAnim.Push);
			}
		}

		internal void Pop()
		{
			if (CanGoBack)
			{
				_overlay?.CloseDropdown();
				_overlay?.CloseSheet();
				HideCurrentSlot();
				PageSlot slot = _currentTab.Stack[_currentTab.Stack.Count - 1];
				_currentTab.Stack.RemoveAt(_currentTab.Stack.Count - 1);
				DestroySlot(slot);
				ShowTopOfStack(NavAnim.Pop);
			}
		}

		internal void SelectTab(TabId id)
		{
			TabSlot tabSlot = _tabs.Find((TabSlot t) => t.Id == id);
			if (tabSlot == null)
			{
				return;
			}
			_overlay?.CloseDropdown();
			_overlay?.CloseSheet();
			if (tabSlot == _currentTab)
			{
				if (tabSlot.Stack.Count > 1)
				{
					PopToRoot(tabSlot);
				}
				else
				{
					ScrollToTop((tabSlot.Stack.Count > 0) ? tabSlot.Stack[0] : null);
				}
				return;
			}
			HideCurrentSlot();
			_currentTab = tabSlot;
			if (tabSlot.Stack.Count == 0)
			{
				tabSlot.Stack.Add(new PageSlot
				{
					Page = tabSlot.RootFactory()
				});
			}
			ShowTopOfStack(NavAnim.TabSwitch);
		}

		internal bool HandleBack()
		{
			if (_overlay == null)
			{
				return false;
			}
			if (_overlay.HasDropdown)
			{
				_overlay.CloseDropdown();
				return true;
			}
			if (_overlay.HasSheet)
			{
				_overlay.CloseSheet();
				return true;
			}
			if (CanGoBack)
			{
				Pop();
				return true;
			}
			return false;
		}

		internal void OpenSheet(IAppSheet sheet)
		{
			_overlay?.OpenSheet(sheet);
		}

		internal void CloseSheet()
		{
			_overlay?.CloseSheet();
		}

		internal void OpenConfirm(string title, string body, string confirmLabel, Action onConfirm, string cancelLabel = "Cancel", bool danger = false)
		{
			_overlay?.OpenConfirm(title, body, confirmLabel, onConfirm, cancelLabel, danger);
		}

		internal void Toast(string text, ToastKind kind = ToastKind.Info)
		{
			if (_wasOpen)
			{
				_overlay?.Toast(text, kind);
			}
		}

		internal void RefreshNow()
		{
			_refreshRequested = true;
		}

		internal void SetInfoChip(string text)
		{
			_infoChip?.SetText(text ?? string.Empty);
		}

		internal void RunGuarded(string key, Action action)
		{
			AppLog.Guard(key, action);
		}

		private void BuildUi(GameObject container)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = container.GetComponent<RectTransform>();
			if (Ui.Dead((Object)(object)component))
			{
				throw new InvalidOperationException("The phone kit's app container has no RectTransform.");
			}
			Canvas val = FindRootCanvas((Transform)(object)component);
			if (!Ui.Dead((Object)(object)val))
			{
				Sprites.Configure(val.referencePixelsPerUnit);
			}
			Canvas.ForceUpdateCanvases();
			Measure(component);
			AppLog.Info($"container {ContainerWidth:0}x{ContainerHeight:0}");
			_root = Ui.Node("TheRegularsApp", (Transform)(object)component);
			_root.anchorMin = new Vector2(0.5f, 0.5f);
			_root.anchorMax = new Vector2(0.5f, 0.5f);
			_root.pivot = new Vector2(0.5f, 0.5f);
			_root.anchoredPosition = Vector2.zero;
			ApplyRootScale();
			Ui.Img((Component)(object)_root, Theme.Bg, null, raycast: true);
			Ui.ClipChildren(_root, showGraphic: true);
			BuildTopBar();
			BuildTabBar();
			BuildContentHost();
			_overlay = new Overlay(this, _root);
			RegisterTabs();
			SelectInitialTab();
		}

		private void ApplyRootScale()
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			Width = 360f;
			Height = ((ContainerWidth > 1f) ? (360f * (ContainerHeight / ContainerWidth)) : 640f);
			Theme.SetContainerWidth(ContainerWidth);
			if (!Ui.Dead((Object)(object)_root))
			{
				_root.sizeDelta = new Vector2(Width, Height);
				float num = ((ContainerWidth > 1f) ? (ContainerWidth / 360f) : 1f);
				((Transform)_root).localScale = new Vector3(num, num, 1f);
			}
		}

		private void BuildTopBar()
		{
			//IL_001c: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Ui.Node("TopBar", (Transform)(object)_root);
			val.anchorMin = new Vector2(0f, 1f);
			val.anchorMax = new Vector2(1f, 1f);
			val.pivot = new Vector2(0.5f, 1f);
			val.sizeDelta = new Vector2(0f, 56f);
			val.anchoredPosition = Vector2.zero;
			Ui.Img((Component)(object)val, Theme.Bg);
			RectTransform obj = Ui.Node("Divider", (Transform)(object)val);
			obj.anchorMin = new Vector2(0f, 0f);
			obj.anchorMax = new Vector2(1f, 0f);
			obj.pivot = new Vector2(0.5f, 0f);
			obj.sizeDelta = new Vector2(0f, 1f);
			Ui.Img((Component)(object)obj, Theme.Border);
			_topBarRow = Ui.HStack((Transform)(object)val, 8f, (TextAnchor)3);
			Ui.Stretch(_topBarRow);
			Ui.Pad(_topBarRow, 16, 16, 0, 0);
			_backButton = Ui.IconButton((Transform)(object)_topBarRow, "chevron-left", Pop, Theme.Text2);
			Ui.SetActive((Component)(object)_backButton, active: false);
			_title = Ui.Text((Transform)(object)_topBarRow, string.Empty, 22f, Theme.Text, bold: true, (TextAlignmentOptions)513);
			Ui.Fill((Component)(object)_title);
			_infoChip = Ui.Chip((Transform)(object)_topBarRow, "—", Theme.Text2);
		}

		private void BuildTabBar()
		{
			//IL_001c: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			RectTransform val = Ui.Node("TabBar", (Transform)(object)_root);
			val.anchorMin = new Vector2(0f, 0f);
			val.anchorMax = new Vector2(1f, 0f);
			val.pivot = new Vector2(0.5f, 0f);
			val.sizeDelta = new Vector2(0f, 64f);
			val.anchoredPosition = Vector2.zero;
			Ui.Img((Component)(object)val, Theme.Card);
			RectTransform obj = Ui.Node("Divider", (Transform)(object)val);
			obj.anchorMin = new Vector2(0f, 1f);
			obj.anchorMax = new Vector2(1f, 1f);
			obj.pivot = new Vector2(0.5f, 1f);
			obj.sizeDelta = new Vector2(0f, 1f);
			Ui.Img((Component)(object)obj, Theme.Border);
			_tabBarRow = Ui.Node("Buttons", (Transform)(object)val);
			Ui.Stretch(_tabBarRow);
			HorizontalLayoutGroup obj2 = ((Component)_tabBarRow).gameObject.AddComponent<HorizontalLayoutGroup>();
			((LayoutGroup)obj2).childAlignment = (TextAnchor)4;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = true;
			((LayoutGroup)obj2).padding = new RectOffset(0, 0, 6, 8);
		}

		private void BuildContentHost()
		{
			_contentHost = Ui.Node("Content", (Transform)(object)_root);
			Ui.Stretch(_contentHost, 0f, 0f, 56f, 64f);
		}

		private void RegisterTabs()
		{
			RegisterTab(TabId.Overview, "Overview", "dashboard", () => new OverviewPage());
			RegisterTab(TabId.Crews, "Crews", "users", () => new CrewsPage());
			RegisterTab(TabId.Calendar, "Calendar", "calendar", () => new CalendarPage());
			RegisterTab(TabId.Settings, "Settings", "adjustments", () => new SettingsPage());
		}

		private void RegisterTab(TabId id, string label, string icon, Func<IAppPage> rootFactory)
		{
			TabSlot tabSlot = new TabSlot
			{
				Id = id,
				Label = label,
				IconName = icon,
				RootFactory = rootFactory
			};
			_tabs.Add(tabSlot);
			BuildTabButton(tabSlot);
		}

		private void BuildTabButton(TabSlot tab)
		{
			//IL_005c: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Ui.VStack((Transform)(object)_tabBarRow, 2f);
			((Object)val).name = "Tab_" + tab.Id;
			VerticalLayoutGroup component = ((Component)val).gameObject.GetComponent<VerticalLayoutGroup>();
			((LayoutGroup)component).childAlignment = (TextAnchor)4;
			((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false;
			Image targetGraphic = Ui.Img((Component)(object)val, Theme.Clear, Sprites.Rounded(12), raycast: true);
			tab.NavIcon = Ui.Icon((Transform)(object)val, tab.IconName, 22f, Theme.Text2);
			tab.NavLabel = Ui.Text((Transform)(object)val, tab.Label, 11f, Theme.Text2, bold: false, (TextAlignmentOptions)514);
			Button val2 = ((Component)val).gameObject.AddComponent<Button>();
			((Selectable)val2).targetGraphic = (Graphic)(object)targetGraphic;
			Ui.Tint((Selectable)(object)val2, Theme.Clear, Theme.HoverWash, Theme.PressWash);
			TabId id = tab.Id;
			Ui.OnClick(val2, delegate
			{
				SelectTab(id);
			}, "tab." + id);
			ControlLoop.RegisterPress((Selectable)(object)val2, (Transform)(object)val);
			tab.NavButton = val2;
		}

		private void SelectInitialTab()
		{
			if (_tabs.Count != 0)
			{
				_currentTab = _tabs[0];
				_currentTab.Stack.Add(new PageSlot
				{
					Page = _currentTab.RootFactory()
				});
				ShowTopOfStack(NavAnim.TabSwitch);
			}
		}

		private void BuildPage(PageSlot slot)
		{
			slot.Root = Ui.Node("Page", (Transform)(object)_contentHost);
			Ui.Stretch(slot.Root);
			slot.Group = ((Component)slot.Root).gameObject.AddComponent<CanvasGroup>();
			slot.Content = Ui.ScrollPage((Transform)(object)slot.Root, out slot.Scroll);
			PageSlot captured = slot;
			AppLog.Guard("page.build", delegate
			{
				captured.Page.Build(captured.Content, this);
			});
			slot.Built = true;
		}

		private PageSlot CurrentSlot()
		{
			if (_currentTab == null || _currentTab.Stack.Count <= 0)
			{
				return null;
			}
			return _currentTab.Stack[_currentTab.Stack.Count - 1];
		}

		private void HideCurrentSlot()
		{
			PageSlot pageSlot = CurrentSlot();
			if (pageSlot != null && pageSlot.Built && !Ui.Dead((Object)(object)pageSlot.Root))
			{
				if (_wasOpen)
				{
					AppLog.Guard("page.hide", pageSlot.Page.OnHide);
				}
				Ui.SetActive((Component)(object)pageSlot.Root, active: false);
			}
		}

		private void ShowTopOfStack(NavAnim anim)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			PageSlot pageSlot = CurrentSlot();
			if (pageSlot != null && !Ui.Dead((Object)(object)_contentHost))
			{
				if (!pageSlot.Built)
				{
					BuildPage(pageSlot);
				}
				Ui.SetActive((Component)(object)pageSlot.Root, active: true);
				((Transform)pageSlot.Root).SetAsLastSibling();
				UpdateChrome(pageSlot);
				UpdateTabBarSelection();
				float num = anim switch
				{
					NavAnim.Pop => 0f - Width, 
					NavAnim.Push => Width, 
					_ => 0f, 
				};
				float duration = ((anim == NavAnim.TabSwitch) ? 0.16f : 0.18f);
				pageSlot.Root.anchoredPosition = new Vector2(num, 0f);
				if (anim == NavAnim.TabSwitch)
				{
					pageSlot.Group.alpha = 0f;
					Tween.Alpha(pageSlot.Group, 1f, duration);
				}
				else
				{
					pageSlot.Group.alpha = 1f;
				}
				Tween.Position(pageSlot.Root, Vector2.zero, duration);
				if (_wasOpen)
				{
					AppLog.Guard("page.show", pageSlot.Page.OnShow);
				}
			}
		}

		private void PopToRoot(TabSlot tab)
		{
			HideCurrentSlot();
			while (tab.Stack.Count > 1)
			{
				PageSlot slot = tab.Stack[tab.Stack.Count - 1];
				tab.Stack.RemoveAt(tab.Stack.Count - 1);
				DestroySlot(slot);
			}
			ShowTopOfStack(NavAnim.Pop);
		}

		private void DestroySlot(PageSlot slot)
		{
			if (slot != null && !Ui.Dead((Object)(object)slot.Root))
			{
				Object.Destroy((Object)(object)((Component)slot.Root).gameObject);
			}
		}

		private void UpdateChrome(PageSlot slot)
		{
			if (!Ui.Dead((Object)(object)_title))
			{
				((TMP_Text)_title).text = slot.Page.Title ?? string.Empty;
			}
			Ui.SetActive((Component)(object)_backButton, CanGoBack);
		}

		private void UpdateTabBarSelection()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			foreach (TabSlot tab in _tabs)
			{
				bool flag = tab == _currentTab;
				Color color = (flag ? Theme.Gold : Theme.Text2);
				if (!Ui.Dead((Object)(object)tab.NavIcon))
				{
					((Graphic)tab.NavIcon).color = color;
				}
				if (!Ui.Dead((Object)(object)tab.NavLabel))
				{
					((Graphic)tab.NavLabel).color = color;
					((TMP_Text)tab.NavLabel).fontStyle = (FontStyles)(flag ? 1 : 0);
				}
			}
		}

		private void ScrollToTop(PageSlot slot)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (slot != null && !Ui.Dead((Object)(object)slot.Scroll) && !Ui.Dead((Object)(object)slot.Scroll.content))
			{
				RectTransform content = slot.Scroll.content;
				slot.Scroll.StopMovement();
				Tween.Position(content, new Vector2(content.anchoredPosition.x, 0f), 0.22f);
			}
		}

		private void Remeasure()
		{
			RectTransform val = (RectTransform)(Ui.Dead((Object)(object)_root) ? null : /*isinst with value type is only supported in some contexts*/);
			if (!Ui.Dead((Object)(object)val))
			{
				float containerWidth = ContainerWidth;
				float containerHeight = ContainerHeight;
				Measure(val);
				if (!(Mathf.Abs(containerWidth - ContainerWidth) < 1f) || !(Mathf.Abs(containerHeight - ContainerHeight) < 1f))
				{
					AppLog.Info($"container resized to {ContainerWidth:0}x{ContainerHeight:0}");
					ApplyRootScale();
				}
			}
		}

		private void Measure(RectTransform host)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = host.rect;
			bool flag = ((Rect)(ref rect)).width > 1f && ((Rect)(ref rect)).height > 1f;
			ContainerWidth = (flag ? ((Rect)(ref rect)).width : 360f);
			ContainerHeight = (flag ? ((Rect)(ref rect)).height : 640f);
		}

		private static Canvas FindRootCanvas(Transform start)
		{
			Canvas result = null;
			Transform val = start;
			int num = 0;
			while (!Ui.Dead((Object)(object)val) && num++ < 64)
			{
				Canvas component = ((Component)val).GetComponent<Canvas>();
				if (!Ui.Dead((Object)(object)component))
				{
					result = component;
				}
				val = val.parent;
			}
			return result;
		}
	}
	internal sealed class CalendarPage : IAppPage
	{
		private sealed class VisitRow
		{
			internal RectTransform Root;

			internal PageKit.FaceSlot Face;

			internal TextMeshProUGUI Days;

			internal TextMeshProUGUI Deals;
		}

		private RectTransform _timelineCard;

		private LiveText _mainLine;

		private RectTransform _likelyHost;

		private readonly List<TextMeshProUGUI> _likelyRows = new List<TextMeshProUGUI>();

		private int _lastLikelyCount = -1;

		private RectTransform _visitsHost;

		private RectTransform _visitsEmpty;

		private readonly List<VisitRow> _visitRows = new List<VisitRow>();

		private int _lastVisitCount = -1;

		public string Title => "Calendar";

		public void Build(RectTransform content, AppShell shell)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			PageKit.EnsureLiveRefreshWired();
			_timelineCard = Ui.CardFrame((Transform)(object)content);
			((Object)_timelineCard).name = "Timeline";
			_mainLine = new LiveText(Ui.Wrap(Ui.Text((Transform)(object)_timelineCard, string.Empty, 16f, Theme.Text, bold: true, (TextAlignmentOptions)513)));
			_likelyHost = Ui.VStack((Transform)(object)_timelineCard, 6f);
			Ui.SectionLabel((Transform)(object)content, "Past visits");
			_visitsHost = Ui.VStack((Transform)(object)content, 12f);
			((Object)_visitsHost).name = "PastVisits";
			_visitsEmpty = Ui.EmptyState((Transform)(object)content, "calendar", "No visits yet", "Your first crew visit will show up here.");
			Refresh();
		}

		public void OnShow()
		{
			Refresh();
		}

		public void OnHide()
		{
		}

		public void Refresh()
		{
			Snapshot now = Crews.Now;
			RefreshTimeline(now);
			RefreshPastVisits();
		}

		private void RefreshTimeline(Snapshot s)
		{
			if (s.Phase == CrewPhase.Unknown)
			{
				_mainLine.Set("Sleep once to sync with the host", flash: true);
				SetLikelyCount(0);
				return;
			}
			switch (s.Phase)
			{
			case CrewPhase.Quiet:
				if (!s.IsHost || s.SleepsUntilRolls < 0)
				{
					_mainLine.Set("Sleep once to sync with the host", flash: true);
					SetLikelyCount(0);
				}
				else
				{
					_mainLine.Set(PageKit.CalendarQuietLine(s), flash: true);
					RefreshLikelyNext(s);
				}
				break;
			case CrewPhase.Coming:
				_mainLine.Set((s.SleepsLeft < 0) ? "Sleep once to sync with the host" : ("Arrive in " + Fmt.Count(s.SleepsLeft, "sleep")), flash: true);
				SetLikelyCount(0);
				break;
			case CrewPhase.InTown:
				_mainLine.Set((s.SleepsLeft < 0) ? "Sleep once to sync with the host" : ("Leave in " + Fmt.Count(s.SleepsLeft, "night")), flash: true);
				SetLikelyCount(0);
				break;
			default:
				_mainLine.Set("Sleep once to sync with the host", flash: true);
				SetLikelyCount(0);
				break;
			}
		}

		private void RefreshLikelyNext(Snapshot s)
		{
			IReadOnlyList<(string, float)> likelyNext = s.LikelyNext;
			SetLikelyCount(likelyNext.Count);
			for (int i = 0; i < likelyNext.Count; i++)
			{
				(string, float) tuple = likelyNext[i];
				string text = Crews.Get(tuple.Item1)?.Name ?? Fmt.Humanize(tuple.Item1);
				((TMP_Text)_likelyRows[i]).text = text + " — " + Mathf.RoundToInt(Mathf.Clamp01(tuple.Item2) * 100f) + "%";
			}
		}

		private void SetLikelyCount(int count)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (count != _lastLikelyCount)
			{
				_lastLikelyCount = count;
				foreach (TextMeshProUGUI likelyRow in _likelyRows)
				{
					if (!Ui.Dead((Object)(object)likelyRow))
					{
						Object.Destroy((Object)(object)((Component)likelyRow).gameObject);
					}
				}
				_likelyRows.Clear();
				for (int i = 0; i < count; i++)
				{
					_likelyRows.Add(Ui.Text((Transform)(object)_likelyHost, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513));
				}
			}
			Ui.SetActive((Component)(object)_likelyHost, count > 0);
		}

		private void RefreshPastVisits()
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<VisitRecord> visits = History.Visits;
			Ui.SetActive((Component)(object)_visitsEmpty, visits.Count == 0);
			Ui.SetActive((Component)(object)_visitsHost, visits.Count > 0);
			if (visits.Count != _lastVisitCount)
			{
				_lastVisitCount = visits.Count;
				RebuildVisitRows(visits);
			}
			for (int i = 0; i < visits.Count && i < _visitRows.Count; i++)
			{
				VisitRecord visitRecord = visits[i];
				VisitRow visitRow = _visitRows[i];
				CrewInfo crewInfo = Crews.Get(visitRecord.CrewId);
				Color color = ((crewInfo != null) ? Theme.Accent(crewInfo.Color).Text : Theme.DefaultAccent.Text);
				visitRow.Face.Refresh(crewInfo?.Leader?.NpcId, color, visitRecord.CrewId);
				((TMP_Text)visitRow.Days).text = DaySpan(visitRecord.ArrivedDay, visitRecord.LeftDay);
				((TMP_Text)visitRow.Deals).text = Fmt.Count(visitRecord.Deals, "deal") + " · " + Fmt.Money(visitRecord.Cash);
			}
		}

		private static string DaySpan(int arrived, int left)
		{
			if (arrived >= 0 && left >= 0)
			{
				return "Day " + arrived + "–" + left;
			}
			if (arrived >= 0)
			{
				return "Since day " + arrived;
			}
			if (left >= 0)
			{
				return "Through day " + left;
			}
			return "A past visit";
		}

		private void RebuildVisitRows(IReadOnlyList<VisitRecord> visits)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			foreach (VisitRow visitRow2 in _visitRows)
			{
				if (!Ui.Dead((Object)(object)visitRow2.Root))
				{
					Object.Destroy((Object)(object)((Component)visitRow2.Root).gameObject);
				}
			}
			_visitRows.Clear();
			for (int i = 0; i < visits.Count; i++)
			{
				VisitRecord visitRecord = visits[i];
				CrewInfo crewInfo = Crews.Get(visitRecord.CrewId);
				RectTransform val = Ui.HStack((Transform)(object)_visitsHost, 12f, (TextAnchor)3);
				((Object)val).name = "Visit";
				Ui.Pad(val, 14, 14, 10, 10);
				Ui.Img((Component)(object)val, Theme.Card, Sprites.Rounded(16));
				Ui.AddOutline(val, 16, Theme.Border);
				VisitRow visitRow = new VisitRow
				{
					Root = val,
					Face = PageKit.FaceSlot.Build((Transform)(object)val, crewInfo?.Leader?.NpcId, visitRecord.CrewId, Theme.DefaultAccent.Text, 36f)
				};
				RectTransform val2 = Ui.VStack((Transform)(object)val, 2f);
				Ui.Fill((Component)(object)val2);
				Ui.Text((Transform)(object)val2, crewInfo?.Name ?? Fmt.Humanize(visitRecord.CrewId), 15f, Theme.Text, bold: true, (TextAlignmentOptions)513);
				visitRow.Days = Ui.Text((Transform)(object)val2, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
				visitRow.Deals = Ui.Text((Transform)(object)val, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)516);
				_visitRows.Add(visitRow);
			}
		}
	}
	internal sealed class CrewDetailPage : IAppPage
	{
		private sealed class MatchRow
		{
			internal readonly RectTransform Root;

			private readonly TextMeshProUGUI _name;

			private readonly TextMeshProUGUI _effects;

			private readonly TextMeshProUGUI _priceLine;

			private MatchRow(RectTransform root, TextMeshProUGUI name, TextMeshProUGUI effects, TextMeshProUGUI priceLine)
			{
				Root = root;
				_name = name;
				_effects = effects;
				_priceLine = priceLine;
			}

			internal static MatchRow Build(Transform parent)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_0079: Unknown result type (might be due to invalid IL or missing references)
				RectTransform obj = Ui.VStack(parent, 2f);
				((Object)obj).name = "Match";
				TextMeshProUGUI name = Ui.Text((Transform)(object)obj, string.Empty, 15f, Theme.Text, bold: true, (TextAlignmentOptions)513);
				RectTransform obj2 = Ui.HStack((Transform)(object)obj, 6f, (TextAnchor)3);
				((Object)obj2).name = "Line2";
				TextMeshProUGUI val = Ui.Text((Transform)(object)obj2, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
				Ui.Fill((Component)(object)val);
				TextMeshProUGUI priceLine = Ui.Text((Transform)(object)obj2, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)516);
				return new MatchRow(obj, name, val, priceLine);
			}

			internal void Set(ProductMatch m, int effectTotal)
			{
				((TMP_Text)_name).text = m.Name;
				((TMP_Text)_effects).text = m.Matches + " of " + effectTotal + " bonus effects";
				((TMP_Text)_priceLine).text = Fmt.Times(m.Multiplier) + " · ~" + Fmt.Money(m.EstPricePerUnit) + "/unit";
			}
		}

		private readonly string _crewId;

		private CrewInfo _crew;

		private AppShell _shell;

		private PageKit.FaceSlot _headerFace;

		private TextMeshProUGUI _headerName;

		private TextMeshProUGUI _headerRole;

		private Chip _visitsChip;

		private RectTransform _visitCard;

		private RectTransform _budgetKnownGroup;

		private LiveNumber _budgetLeft;

		private LiveNumber _budgetTotal;

		private ProgressBar _budgetBar;

		private TextMeshProUGUI _budgetSyncingText;

		private TextMeshProUGUI _nightsLeftText;

		private TextMeshProUGUI _salesThisVisitText;

		private List<(string EffectId, Chip Chip, Image Check)> _effectChips;

		private RectTransform _matchesHost;

		private MatchRow[] _matchesRows;

		private TextMeshProUGUI _matchesFootnote;

		private RectTransform _matchesEmpty;

		private TextMeshProUGUI _visitsStat;

		private TextMeshProUGUI _lastVisitStat;

		private TextMeshProUGUI _totalStat;

		private Button _mapButton;

		private Button _actionButton;

		private Button _sendAwayButton;

		private TextMeshProUGUI _actionHintText;

		private const int MaxBestMatches = 3;

		private readonly List<PageKit.PersonFaceSlot> _memberFaces = new List<PageKit.PersonFaceSlot>();

		public string Title => _crew?.Name ?? "Crew";

		internal CrewDetailPage(string crewId)
		{
			_crewId = crewId;
		}

		public void Build(RectTransform content, AppShell shell)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			PageKit.EnsureLiveRefreshWired();
			_shell = shell;
			_crew = Crews.Get(_crewId);
			if (_crew == null)
			{
				Ui.EmptyState((Transform)(object)content, "alert", "This crew isn't available", "Try opening it again from Crews.");
				return;
			}
			(Color, Color, Color) accent = Theme.Accent(_crew.Color);
			BuildHeader((Transform)(object)content, accent);
			BuildVisitCard((Transform)(object)content, accent);
			BuildBuyAndEffects((Transform)(object)content, accent);
			BuildBestMatches((Transform)(object)content);
			BuildMembers((Transform)(object)content, accent);
			BuildHistory((Transform)(object)content);
			BuildButtons((Transform)(object)content, accent);
			Refresh();
		}

		public void OnShow()
		{
			Refresh();
		}

		public void OnHide()
		{
		}

		public void Refresh()
		{
			//IL_0039: 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)
			if (_crew != null)
			{
				Snapshot now = Crews.Now;
				bool inTown = now.Phase == CrewPhase.InTown && string.Equals(now.ActiveCrewId, _crew.Id, StringComparison.OrdinalIgnoreCase);
				(Color, Color, Color) accent = Theme.Accent(_crew.Color);
				_headerFace.Refresh(_crew.Leader?.NpcId, accent.Item1, _crew.Id);
				CrewHistory crewHistory = PageKit.FindHistory(now, _crew.Id);
				Ui.SetActive((IControl)_visitsChip, crewHistory != null && crewHistory.Visits > 0);
				if (crewHistory != null && crewHistory.Visits > 0)
				{
					_visitsChip.SetText(Fmt.Count(crewHistory.Visits, "visit"));
				}
				RefreshVisitCard(now, inTown, accent);
				RefreshEffects();
				RefreshBestMatches();
				RefreshMemberFaces();
				RefreshHistory(now);
				RefreshButtons(inTown);
			}
		}

		private void BuildHeader(Transform parent, (Color Text, Color Tint, Color Outline) accent)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Ui.HStack(parent, 12f, (TextAnchor)3);
			((Object)val).name = "Header";
			_headerFace = PageKit.FaceSlot.Build((Transform)(object)val, _crew.Leader?.NpcId, _crew.Id, accent.Text, 80f);
			RectTransform val2 = Ui.VStack((Transform)(object)val, 4f);
			Ui.Fill((Component)(object)val2);
			_headerName = Ui.Text((Transform)(object)val2, _crew.Leader?.FullName ?? _crew.Name, 18f, Theme.Text, bold: true, (TextAlignmentOptions)513);
			_headerRole = Ui.Text((Transform)(object)val2, "Leader · deals with you", 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
			_visitsChip = PageKit.SoloChip((Transform)(object)val2, string.Empty, Theme.Text2);
			Ui.SetActive((IControl)_visitsChip, active: false);
		}

		private void BuildVisitCard(Transform parent, (Color Text, Color Tint, Color Outline) accent)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: 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_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			_visitCard = Ui.CardFrame(parent);
			((Object)_visitCard).name = "VisitCard";
			_budgetKnownGroup = Ui.VStack((Transform)(object)_visitCard, 6f);
			RectTransform parent2 = Ui.HStack((Transform)(object)_budgetKnownGroup, 6f, (TextAnchor)3);
			Ui.Fill((Component)(object)Ui.Text((Transform)(object)parent2, "Left to buy today", 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513));
			TextMeshProUGUI label = Ui.Text((Transform)(object)parent2, "0", 15f, Theme.Text, bold: true, (TextAlignmentOptions)513);
			_budgetLeft = new LiveNumber(label, (int v) => v.ToString());
			Ui.Text((Transform)(object)parent2, " of ", 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
			TextMeshProUGUI label2 = Ui.Text((Transform)(object)parent2, "0", 15f, Theme.Text, bold: true, (TextAlignmentOptions)513);
			_budgetTotal = new LiveNumber(label2, (int v) => v.ToString());
			_budgetBar = Ui.Progress((Transform)(object)_budgetKnownGroup, 0f, accent.Text, 6f);
			_budgetSyncingText = Ui.Text((Transform)(object)_visitCard, "Syncing today's numbers with the host...", 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
			Ui.Wrap(Ui.Text((Transform)(object)_visitCard, "Refills when you sleep · the last pack still pays in full", 12f, Theme.Text3, bold: false, (TextAlignmentOptions)513));
			_nightsLeftText = Ui.Text((Transform)(object)_visitCard, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
			_salesThisVisitText = Ui.Text((Transform)(object)_visitCard, string.Empty, 15f, Theme.Text, bold: true, (TextAlignmentOptions)513);
			Ui.SetActive((Component)(object)_visitCard, active: false);
		}

		private void RefreshVisitCard(Snapshot s, bool inTown, (Color Text, Color Tint, Color Outline) accent)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Ui.SetActive((Component)(object)_visitCard, inTown);
			if (inTown)
			{
				bool flag = s.BudgetToday >= 0;
				Ui.SetActive((Component)(object)_budgetKnownGroup, flag);
				Ui.SetActive((Component)(object)_budgetSyncingText, !flag);
				if (flag)
				{
					int budgetToday = s.BudgetToday;
					int num = Mathf.Clamp(s.BudgetLeftToday, 0, budgetToday);
					_budgetLeft.Set(num);
					_budgetTotal.Set(budgetToday);
					_budgetBar.Set((budgetToday > 0) ? ((float)num / (float)budgetToday) : 0f, accent.Text);
				}
				((TMP_Text)_nightsLeftText).text = PageKit.NightsLeftLine(s.SleepsLeft);
				long num2 = ((History.Current != null && string.Equals(History.Current.CrewId, _crew.Id, StringComparison.OrdinalIgnoreCase)) ? History.Current.Cash : 0);
				((TMP_Text)_salesThisVisitText).text = "Your sales this visit · " + Fmt.Money(num2);
			}
		}

		private void BuildBuyAndEffects(Transform parent, (Color Text, Color Tint, Color Outline) accent)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			RectTransform parent2 = Ui.Card(parent, "They buy");
			List<string> list = new List<string>(_crew.Drugs.Count);
			foreach (DrugKind drug in _crew.Drugs)
			{
				list.Add(PageKit.DrugName(drug));
			}
			RectTransform parent3 = Ui.HStack((Transform)(object)parent2, 6f, (TextAnchor)3);
			foreach (string item in list)
			{
				Ui.Chip((Transform)(object)parent3, item, accent.Text);
			}
			Ui.SectionLabel((Transform)(object)parent2, "Bonus effects · +40% each");
			List<string> list2 = new List<string>(_crew.Effects.Count);
			foreach (EffectInfo effect in _crew.Effects)
			{
				list2.Add(effect.Name);
			}
			float maxWidth = ((AppShell.Current != null) ? (AppShell.Current.Width - 32f - 32f) : 328f);
			List<(Chip, Image)> list3 = PageKit.ChipWrap((Transform)(object)parent2, maxWidth, list2);
			_effectChips = new List<(string, Chip, Image)>(list3.Count);
			for (int i = 0; i < list3.Count && i < _crew.Effects.Count; i++)
			{
				_effectChips.Add((_crew.Effects[i].Id, list3[i].Item1, list3[i].Item2));
			}
		}

		private void RefreshEffects()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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)
			if (_effectChips == null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(Products.MatchingEffectIds(_crew.Id), StringComparer.OrdinalIgnoreCase);
			(Color, Color, Color) tuple = Theme.Accent(_crew.Color);
			foreach (var effectChip in _effectChips)
			{
				bool flag = hashSet.Contains(effectChip.EffectId);
				string text = FindEffect(effectChip.EffectId)?.Name ?? Fmt.Humanize(effectChip.EffectId);
				Color color = (flag ? tuple.Item1 : Theme.Text2);
				effectChip.Chip.Set(text, color);
				Ui.SetActive((Component)(object)effectChip.Check, flag);
				if (!Ui.Dead((Object)(object)effectChip.Check))
				{
					((Graphic)effectChip.Check).color = color;
				}
			}
		}

		private EffectInfo FindEffect(string id)
		{
			foreach (EffectInfo effect in _crew.Effects)
			{
				if (string.Equals(effect.Id, id, StringComparison.OrdinalIgnoreCase))
				{
					return effect;
				}
			}
			return null;
		}

		private void BuildBestMatches(Transform parent)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			RectTransform parent2 = Ui.Card(parent, "Your best matches");
			_matchesHost = Ui.VStack((Transform)(object)parent2, 8f);
			_matchesRows = new MatchRow[3];
			for (int i = 0; i < 3; i++)
			{
				_matchesRows[i] = MatchRow.Build((Transform)(object)_matchesHost);
			}
			_matchesFootnote = Ui.Text((Transform)(object)parent2, "Prices at standard quality", 11f, Theme.Text3, bold: false, (TextAlignmentOptions)513);
			_matchesEmpty = Ui.EmptyState((Transform)(object)parent2, "package", "No matching products yet", "Discover a " + PageKit.DrugListLower(_crew.Drugs) + " product to sell to them.");
			RefreshBestMatches();
		}

		private void RefreshBestMatches()
		{
			IReadOnlyList<ProductMatch> readOnlyList = Products.BestFor(_crew.Id, 3);
			Ui.SetActive((Component)(object)_matchesHost, readOnlyList.Count > 0);
			Ui.SetActive((Component)(object)_matchesFootnote, readOnlyList.Count > 0);
			Ui.SetActive((Component)(object)_matchesEmpty, readOnlyList.Count == 0);
			for (int i = 0; i < _matchesRows.Length; i++)
			{
				bool flag = i < readOnlyList.Count;
				Ui.SetActive((Component)(object)_matchesRows[i].Root, flag);
				if (flag)
				{
					_matchesRows[i].Set(readOnlyList[i], _crew.Effects.Count);
				}
			}
		}

		private void BuildMembers(Transform parent, (Color Text, Color Tint, Color Outline) accent)
		{
			RectTransform parent2 = Ui.VStack((Transform)(object)Ui.Card(parent, "The crew"), 8f);
			AddMemberRow((Transform)(object)parent2, _crew.Leader, accent, "Leader");
			foreach (TheRegulars.Data.MemberInfo member in _crew.Members)
			{
				AddMemberRow((Transform)(object)parent2, member, accent, null);
			}
		}

		private void AddMemberRow(Transform parent, TheRegulars.Data.MemberInfo member, (Color Text, Color Tint, Color Outline) accent, string role)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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)
			if (member != null)
			{
				RectTransform obj = Ui.HStack(parent, 8f, (TextAnchor)3);
				((Object)obj).name = "Member";
				PageKit.PersonFaceSlot item = PageKit.PersonFaceSlot.Build((Transform)(object)obj, member.NpcId, member.FullName, accent.Text, 36f);
				_memberFaces.Add(item);
				RectTransform val = Ui.VStack((Transform)(object)obj, 0f);
				Ui.Fill((Component)(object)val);
				Ui.Text((Transform)(object)val, member.FullName, 15f, Theme.Text, bold: false, (TextAlignmentOptions)513);
				if (!string.IsNullOrEmpty(role))
				{
					Ui.Text((Transform)(object)val, role, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
				}
			}
		}

		private void RefreshMemberFaces()
		{
			for (int i = 0; i < _memberFaces.Count; i++)
			{
				_memberFaces[i].Refresh();
			}
		}

		private void BuildHistory(Transform parent)
		{
			RectTransform parent2 = Ui.Card(parent, "History");
			_visitsStat = PageKit.StatRow((Transform)(object)parent2, "Visits");
			_lastVisitStat = PageKit.StatRow((Transform)(object)parent2, "Last visit");
			_totalStat = PageKit.StatRow((Transform)(object)parent2, "Total earned (your sales)");
		}

		private void RefreshHistory(Snapshot s)
		{
			CrewHistory crewHistory = PageKit.FindHistory(s, _crew.Id);
			if (!s.IsHost)
			{
				((TMP_Text)_visitsStat).text = "—";
				((TMP_Text)_lastVisitStat).text = "Only your host can see their history.";
			}
			else if (crewHistory == null || crewHistory.Visits <= 0)
			{
				((TMP_Text)_visitsStat).text = "0";
				((TMP_Text)_lastVisitStat).text = "Never visited";
			}
			else
			{
				((TMP_Text)_visitsStat).text = crewHistory.Visits.ToString();
				((TMP_Text)_lastVisitStat).text = ((crewHistory.NightsSinceVisit == 0) ? "Last night" : (Fmt.Count(Math.Max(0, crewHistory.NightsSinceVisit), "night") + " ago"));
			}
			((TMP_Text)_totalStat).text = Fmt.Money(History.TotalFrom(_crew.Id));
		}

		private void BuildButtons(Transform parent, (Color Text, Color Tint, Color Outline) accent)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Ui.HStack(parent, 8f, (TextAnchor)3);
			((Object)val).name = "Actions";
			_mapButton = Ui.Button((Transform)(object)val, "Show on map", ButtonStyle.Secondary, OnShowOnMap, "map-pin");
			Ui.Size((Component)(object)_mapButton, -1f, -1f, 1f);
			Ui.SetActive((Component)(object)_mapButton, active: false);
			_actionButton = Ui.Button((Transform)(object)val, "Call them in", ButtonStyle.Primary, OnActionButtonTapped, "phone-call");
			Ui.Size((Component)(object)_actionButton, -1f, -1f, 1f);
			_sendAwayButton = Ui.Button((Transform)(object)val, "Send them away", ButtonStyle.Secondary, OnSendThemAwayTapped, "x");
			Ui.Size((Component)(object)_sendAwayButton, -1f, -1f, 1f);
			Ui.SetActive((Component)(object)_sendAwayButton, active: false);
			_actionHintText = Ui.Wrap(Ui.Text(parent, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513));
			((Object)_actionHintText).name = "ActionHint";
			Ui.SetActive((Component)(object)_actionHintText, active: false);
		}

		private void RefreshButtons(bool inTown)
		{
			Snapshot now = Crews.Now;
			bool flag = string.Equals(now.ActiveCrewId, _crew.Id, StringComparison.OrdinalIgnoreCase);
			bool flag2 = !inTown && now.Phase == CrewPhase.Coming && flag;
			Ui.SetActive((Component)(object)_mapButton, inTown);
			Ui.SetActive((Component)(object)_sendAwayButton, inTown);
			Ui.SetActive((Component)(object)_actionButton, !inTown);
			if (!inTown)
			{
				Ui.SetButtonText(_actionButton, flag2 ? "Bring them sooner" : "Call them in");
			}
			bool flag3;
			string reason;
			if (now.IsHost)
			{
				flag3 = ((!inTown) ? CrewActions.CanCallIn(_crew.Id, out reason) : CrewActions.CanSendAway(out reason));
			}
			else
			{
				flag3 = false;
				reason = "Host only";
			}
			Ui.SetInteractable((Component)(object)(inTown ? _sendAwayButton : _actionButton), flag3, reason);
			bool flag4 = !flag3 && !string.IsNullOrEmpty(reason);
			Ui.SetActive((Component)(object)_actionHintText, flag4);
			if (flag4)
			{
				((TMP_Text)_actionHintText).text = reason;
			}
		}

		private void OnShowOnMap()
		{
			_shell.RunGuarded("crewdetail.markonmap", delegate
			{
				ActionResult actionResult = CrewActions.MarkOnMap(_crew.Id);
				_shell.Toast(actionResult.Message, actionResult.Ok ? ToastKind.Success : ToastKind.Warn);
			});
		}

		private void OnActionButtonTapped()
		{
			_shell.RunGuarded("crewdetail.callin.confirm", delegate
			{
				Snapshot now = Crews.Now;
				int num;
				string text;
				if (now.Phase == CrewPhase.Coming)
				{
					num = (string.Equals(now.ActiveCrewId, _crew.Id, StringComparison.OrdinalIgnoreCase) ? 1 : 0);
					if (num != 0)
					{
						text = "Bring the " + _crew.Name + " sooner?";
						goto IL_0062;
					}
				}
				else
				{
					num = 0;
				}
				text = "Call the " + _crew.Name + " in?";
				goto IL_0062;
				IL_0062:
				string title = text;
				string confirmLabel = ((num != 0) ? "Bring them sooner" : "Call them in");
				_shell.OpenConfirm(title, "They arrive after your next sleep.", confirmLabel, ConfirmCallIn);
			});
		}

		private void ConfirmCallIn()
		{
			_shell.RunGuarded("crewdetail.callin", delegate
			{
				ActionResult actionResult = CrewActions.CallIn(_crew.Id);
				_shell.Toast(actionResult.Message, actionResult.Ok ? ToastKind.Success : ToastKind.Warn);
			});
		}

		private void OnSendThemAwayTapped()
		{
			_shell.RunGuarded("crewdetail.sendaway.confirm", delegate
			{
				_shell.OpenConfirm("Send the " + _crew.Name + " away?", "They leave when you sleep. Anything you sell them today still counts.", "Send away", ConfirmSendAway);
			});
		}

		private void ConfirmSendAway()
		{
			_shell.RunGuarded("crewdetail.sendaway", delegate
			{
				ActionResult actionResult = CrewActions.SendAway();
				_shell.Toast(actionResult.Message, actionResult.Ok ? ToastKind.Success : ToastKind.Warn);
			});
		}
	}
	internal sealed class CrewsPage : IAppPage
	{
		private sealed class Row
		{
			internal RectTransform Root;

			internal string CrewId;

			internal PageKit.FaceSlot Face;

			internal TextMeshProUGUI Name;

			internal TextMeshProUGUI Status;
		}

		private AppShell _shell;

		private RectTransform _host;

		private RectTransform _emptyState;

		private readonly List<Row> _rows = new List<Row>();

		private int _lastCount = -1;

		public string Title => "Crews";

		public void Build(RectTransform content, AppShell shell)
		{
			PageKit.EnsureLiveRefreshWired();
			_shell = shell;
			_host = Ui.VStack((Transform)(object)content, 12f);
			((Object)_host).name = "CrewRows";
			_emptyState = Ui.EmptyState((Transform)(object)content, "users", "No crews yet", "The game hasn't loaded them in yet.");
			Refresh();
		}

		public void OnShow()
		{
			Refresh();
		}

		public void OnHide()
		{
		}

		public void Refresh()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<CrewInfo> all = Crews.All;
			Ui.SetActive((Component)(object)_emptyState, all.Count == 0);
			Ui.SetActive((Component)(object)_host, all.Count > 0);
			if (all.Count != _lastCount)
			{
				_lastCount = all.Count;
				Rebuild(all);
			}
			Snapshot now = Crews.Now;
			for (int i = 0; i < _rows.Count; i++)
			{
				Row row = _rows[i];
				CrewInfo crewInfo = Crews.Get(row.CrewId);
				if (crewInfo != null)
				{
					(Color, Color, Color) tuple = Theme.Accent(crewInfo.Color);
					row.Face.Refresh(crewInfo.Leader?.NpcId, tuple.Item1, crewInfo.Id);
					(string, Color) tuple2 = PageKit.CrewRowStatus(now, crewInfo);
					((TMP_Text)row.Status).text = tuple2.Item1;
					((Graphic)row.Status).color = tuple2.Item2;
				}
			}
		}

		private void Rebuild(IReadOnlyList<CrewInfo> all)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			//IL_00e6: 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)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			foreach (Row row2 in _rows)
			{
				if (!Ui.Dead((Object)(object)row2.Root))
				{
					Object.Destroy((Object)(object)((Component)row2.Root).gameObject);
				}
			}
			_rows.Clear();
			for (int i = 0; i < all.Count; i++)
			{
				CrewInfo crewInfo = all[i];
				string crewId = crewInfo.Id;
				RectTransform val = Ui.Row((Transform)(object)_host, delegate
				{
					_shell.RunGuarded("crews.opencrew", delegate
					{
						_shell.Push(new CrewDetailPage(crewId));
					});
				}, 56f, chevron: true, "crews.row");
				Row row = new Row
				{
					Root = (RectTransform)((Transform)val).parent,
					CrewId = crewId,
					Face = PageKit.FaceSlot.Build((Transform)(object)val, crewInfo.Leader?.NpcId, crewId, Theme.Accent(crewInfo.Color).Text, 44f)
				};
				RectTransform val2 = Ui.VStack((Transform)(object)val, 2f);
				Ui.Fill((Component)(object)val2);
				row.Name = Ui.Text((Transform)(object)val2, crewInfo.Name, 15f, Theme.Text, bold: true, (TextAlignmentOptions)513);
				row.Status = Ui.Text((Transform)(object)val2, string.Empty, 13f, Theme.Text2, bold: false, (TextAlignmentOptions)513);
				_rows.Add(row);
			}
		}
	}
	internal enum TabId
	{
		Overview,
		Crews,
		Calendar,
		Settings
	}
	internal interface IAppPage
	{
		string Title { get; }

		void Build(RectTransform content, AppShell shell);

		void OnShow();

		void Refresh();

		void OnHide();
	}
	internal interface IAppSheet
	{
		string Title { get; }

		void Build(RectTransform body, AppShell shell);

		void Refresh();

		void OnClose();
	}
	internal sealed class OverviewPage : IAppPage
	{
		private sealed class CrewRowUi
		{
			internal RectTransform Root;

			internal PageKit.FaceSlot Face;

			internal TextMeshProUGUI Name;

			internal TextMeshProUGUI Status;
		}

		private AppShell _shell;

		private RectTransform _bannerRow;

		private TextMeshProUGUI _bannerText;

		private Image _bannerIcon;

		private bool _bannerDismissed;

		private string _bannerShownFor;

		private RectTransform _heroFaceHost;

		private PageKit.FaceSlot _heroFace;

		private Chip _heroStatusChip;

		private TextMeshProUGUI _heroName;

		private TextMeshProUGUI _heroSub;

		private LiveText _heroCountdownLabel;

		private LiveText _heroCountdownBig;

		private TextMeshProUGUI _heroChanceSuffix;

		private TextMeshProUGUI _insetBuyCaption;

		private TextMeshProUGUI _insetBuyValue;

		private TextMeshProUGUI _insetDayCaption;

		private TextMeshProUGUI _insetDayValue;

		private RectTransform _insetRow;

		private Button _heroButton;

		private Action _heroAction = delegate
		{
		};

		private readonly Dictionary<string, CrewRowUi> _otherRows = new Dictionary<string, CrewRowUi>(StringComparer.Ordinal);

		private RectTransform _otherRowsHost;

		private RectTransform _otherRowsLabel;

		private int _lastCrewCount = -1;

		public string Title => "Overview";

		public void Build(RectTransform content, AppShell shell)
		{
			PageKit.EnsureLiveRefreshWired();
			_shell = shell;
			BuildBanner((Transform)(object)content);
			BuildHero((Transform)(object)content);
			_otherRowsLabel = Ui.SectionLabel((Transform)(object)content, "All crews");
			_otherRowsHost = Ui.VStack((Transform)(object)content, 12f);
			((Object)_otherRowsHost).name = "OtherCrews";
			Refresh();
		}

		public void OnShow()
		{
			Refresh();
		}

		public void OnHide()
		{
		}

		public void Refresh()
		{
			Snapshot now = Crews.Now;
			RefreshBanner(now);
			RefreshHero(now);
			RefreshOtherCrews(now);
		}

		private void BuildBanner(Transform parent)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			_bannerRow = Ui.HStack(parent, 10f, (TextAnchor)3);
			((Object)_bannerRow).name = "Banner";
			Ui.Pad(_bannerRow, 14, 10, 10, 10);
			Ui.Img((Component)(object)_bannerRow, Theme.Card2, Sprites.Rounded(16), raycast: true);
			Ui.AddOutline(_bannerRow, 16, Theme.Border);
			_bannerIcon = Ui.Icon((Transform)(object)_bannerRow, "info", 16f, Theme.Text2);
			_bannerText = Ui.Wrap(Ui.Text((Transform)(object)_bannerRow, string.Empty, 13f, Theme.