Decompiled source of ServiceTracker v1.2.1

ServiceTracker.dll

Decompiled 14 minutes ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;

[assembly: CompilationRelaxations(8)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ServiceTracker
{
	public class ServiceRecord
	{
		public string StartedAt;

		public string EndedAt;

		public string TavernName;

		public int GameDay;

		public float OpenSeconds;

		public float DrainSeconds;

		public string EndReason;

		public bool JoinedMidService;

		public int CustomersArrived;

		public int CustomersServed;

		public int VipsServed;

		public int WalkoutsAfterSeating;

		public int FledDanger;

		public int TurnedAwayFull;

		public int TurnedAwayReputation;

		public int TurnedAwayCleanliness;

		public int TurnedAwayCantReach;

		public int GotDrunk;

		public int DrunksBanished;

		public int RoomGuests;

		public int DishesServed;

		public int JustInTime;

		public Dictionary<string, int> Dishes = new Dictionary<string, int>();

		public long Income;

		public long Spent;

		public float AvgWaitSeconds;

		public float MaxWaitSeconds;

		[JsonIgnore]
		public int TurnedAway => TurnedAwayFull + TurnedAwayReputation + TurnedAwayCleanliness + TurnedAwayCantReach;

		[JsonIgnore]
		public int Lost => WalkoutsAfterSeating + FledDanger + TurnedAway;

		[JsonIgnore]
		public long Net => Income - Spent;

		[JsonIgnore]
		public float ServedPct
		{
			get
			{
				int num = CustomersServed + Lost;
				if (num == 0)
				{
					return 0f;
				}
				return 100f * (float)CustomersServed / (float)num;
			}
		}

		[JsonIgnore]
		public string TopDish
		{
			get
			{
				if (Dishes.Count == 0)
				{
					return "-";
				}
				return Dishes.OrderByDescending((KeyValuePair<string, int> k) => k.Value).First().Key;
			}
		}
	}
	public class History
	{
		public List<ServiceRecord> Records = new List<ServiceRecord>();

		private readonly string _path;

		private readonly int _max;

		public History(string path, int max)
		{
			_path = path;
			_max = Math.Max(1, max);
		}

		public void Load()
		{
			try
			{
				if (File.Exists(_path))
				{
					Records = JsonConvert.DeserializeObject<List<ServiceRecord>>(File.ReadAllText(_path)) ?? new List<ServiceRecord>();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not read history, starting fresh (old file kept as .bak): " + ex.Message));
				try
				{
					File.Copy(_path, _path + ".bak", overwrite: true);
				}
				catch
				{
				}
				Records = new List<ServiceRecord>();
			}
		}

		public void Add(ServiceRecord r)
		{
			Records.Insert(0, r);
			while (Records.Count > _max)
			{
				Records.RemoveAt(Records.Count - 1);
			}
			Save();
		}

		public void Remove(ServiceRecord r)
		{
			Records.Remove(r);
			Save();
		}

		public void Clear()
		{
			Records.Clear();
			Save();
		}

		public void Save()
		{
			try
			{
				string text = _path + ".tmp";
				File.WriteAllText(text, JsonConvert.SerializeObject((object)Records, (Formatting)1));
				if (File.Exists(_path))
				{
					File.Delete(_path);
				}
				File.Move(text, _path);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not save history: " + ex.Message));
			}
		}
	}
	[BepInPlugin("aletale.servicetracker", "Service Tracker", "1.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "aletale.servicetracker";

		public const string Name = "Service Tracker";

		public const string Version = "1.2.1";

		internal static Plugin Inst;

		internal static ManualLogSource Log;

		internal ConfigEntry<KeyboardShortcut> ToggleKey;

		internal ConfigEntry<KeyboardShortcut> EndNowKey;

		internal ConfigEntry<bool> ShowLiveHud;

		internal ConfigEntry<bool> ShowSummaryPopup;

		internal ConfigEntry<float> DrainTimeout;

		internal ConfigEntry<int> MaxHistory;

		internal Tracker Tracker;

		internal History History;

		private TrackerUI _ui;

		private void Awake()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			Inst = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "ToggleHistoryWindow", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Open or close the service history window.");
			EndNowKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "EndServiceNow", new KeyboardShortcut((KeyCode)289, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Finalize the current service report immediately (use if a customer is stuck).");
			ShowLiveHud = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowLiveHud", true, "Show a small live counter while a service is running.");
			ShowSummaryPopup = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowSummaryPopup", true, "Show a summary popup when a service ends.");
			DrainTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("Tracking", "DrainTimeoutSeconds", 240f, "After closing, finalize the report anyway if customers are still inside after this many seconds.");
			MaxHistory = ((BaseUnityPlugin)this).Config.Bind<int>("Tracking", "MaxHistory", 200, "How many past services to keep.");
			string text = Path.Combine(Paths.ConfigPath, "ServiceTracker_history.json");
			History = new History(text, MaxHistory.Value);
			History.Load();
			Tracker = new Tracker(this);
			_ui = new TrackerUI(this);
			new Harmony("aletale.servicetracker").PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)("Service Tracker 1.2.1 loaded. History file: " + text));
		}

		private void Update()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Tracker.Tick();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Tracker tick failed: " + ex));
			}
			KeyboardShortcut value = ToggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown() || (_ui.IsOpen && Input.GetKeyDown((KeyCode)27)))
			{
				_ui.Toggle();
			}
			value = EndNowKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				Tracker.ForceEnd();
			}
		}

		private void LateUpdate()
		{
			_ui.LateUpdate();
		}

		private void OnGUI()
		{
			_ui.Draw();
		}

		private void OnDestroy()
		{
			Tracker.OnShutdown();
		}
	}
	[HarmonyPatch(typeof(Customer), "OnNetworkSpawn")]
	internal static class CustomerSpawnPatch
	{
		private static void Postfix(Customer __instance)
		{
			try
			{
				Plugin.Inst.Tracker.OnCustomerSpawn(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(Customer), "OnNetworkDespawn")]
	internal static class CustomerDespawnPatch
	{
		private static void Prefix(Customer __instance)
		{
			try
			{
				Plugin.Inst.Tracker.OnCustomerDespawn(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}
	}
	public enum Phase
	{
		Idle,
		Open,
		Draining
	}
	internal class CustInfo
	{
		public ushort Id;

		public bool CountedThisSession;

		public float SatAt = -1f;

		public bool Served;

		public bool Guest;

		public bool Fled;

		public bool Banished;

		public bool Vip;
	}
	internal class Tracker
	{
		private readonly Plugin _p;

		private readonly Dictionary<Customer, CustInfo> _live = new Dictionary<Customer, CustInfo>();

		private readonly List<float> _waits = new List<float>();

		public Phase Phase;

		public ServiceRecord Current;

		public ServiceRecord LastFinished;

		public float LastFinishedAt = -999f;

		private float _openedAt;

		private float _closedAt;

		private bool? _lastOpen;

		private Game _subscribedGame;

		private Action<GameEvent, uint, int> _handler;

		private float _nextPoll;

		private readonly Dictionary<uint, string> _dishNames = new Dictionary<uint, string>();

		public int CustomersInside => _live.Where((KeyValuePair<Customer, CustInfo> kv) => (Object)(object)kv.Key != (Object)null && !kv.Value.Guest).Count();

		public float PhaseSeconds
		{
			get
			{
				if (Phase == Phase.Open)
				{
					return Time.time - _openedAt;
				}
				if (Phase == Phase.Draining)
				{
					return Time.time - _closedAt;
				}
				return 0f;
			}
		}

		public Tracker(Plugin p)
		{
			_p = p;
			_handler = OnGameEvent;
		}

		public void Tick()
		{
			EnsureSubscribed();
			GameStatus instance = GameStatus.Instance;
			if ((Object)(object)instance == (Object)null || instance.isTavernOpen == null)
			{
				if (Phase != Phase.Idle)
				{
					Finish("Left game");
				}
				_lastOpen = null;
				_live.Clear();
				return;
			}
			bool value = instance.isTavernOpen.Value;
			bool? lastOpen = _lastOpen;
			if (!lastOpen.HasValue)
			{
				if (value)
				{
					Start(midService: true);
				}
			}
			else if (value != _lastOpen.Value)
			{
				if (value)
				{
					if (Phase == Phase.Draining)
					{
						Phase = Phase.Open;
					}
					else
					{
						Start(midService: false);
					}
				}
				else if (Phase == Phase.Open)
				{
					Phase = Phase.Draining;
					_closedAt = Time.time;
					Current.OpenSeconds += _closedAt - _openedAt;
				}
			}
			_lastOpen = value;
			if (Time.time >= _nextPoll)
			{
				_nextPoll = Time.time + 0.25f;
				PollCustomers();
			}
			if (Phase == Phase.Draining)
			{
				if (CustomersInside == 0)
				{
					Finish("Emptied");
				}
				else if (Time.time - _closedAt > _p.DrainTimeout.Value)
				{
					Finish("Timed out");
				}
			}
		}

		private void EnsureSubscribed()
		{
			Game instance = Game.Instance;
			if ((Object)(object)instance == (Object)(object)_subscribedGame)
			{
				return;
			}
			if ((Object)(object)_subscribedGame != (Object)null)
			{
				try
				{
					Game subscribedGame = _subscribedGame;
					subscribedGame.onGameEvent = (Action<GameEvent, uint, int>)Delegate.Remove(subscribedGame.onGameEvent, _handler);
				}
				catch
				{
				}
			}
			_subscribedGame = instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.onGameEvent = (Action<GameEvent, uint, int>)Delegate.Combine(instance.onGameEvent, _handler);
			}
		}

		private void Start(bool midService)
		{
			Current = new ServiceRecord
			{
				StartedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
				TavernName = ReadTavernName(),
				GameDay = ReadDay(),
				JoinedMidService = midService
			};
			_waits.Clear();
			_openedAt = Time.time;
			Phase = Phase.Open;
			foreach (CustInfo value in _live.Values)
			{
				value.CountedThisSession = false;
			}
			if (!midService)
			{
				return;
			}
			foreach (CustInfo value2 in _live.Values)
			{
				value2.CountedThisSession = true;
				Current.CustomersArrived++;
			}
		}

		public void ForceEnd()
		{
			if (Phase != Phase.Idle)
			{
				bool num = Phase == Phase.Open;
				if (num)
				{
					Current.OpenSeconds += Time.time - _openedAt;
				}
				Finish("Ended manually", alreadyClosedOpenTime: true);
				if (num)
				{
					Start(midService: false);
				}
			}
		}

		public void OnShutdown()
		{
			if (Phase != Phase.Idle)
			{
				Finish("Left game");
			}
		}

		private void Finish(string reason, bool alreadyClosedOpenTime = false)
		{
			if (Current == null)
			{
				Phase = Phase.Idle;
				return;
			}
			if (Phase == Phase.Open && !alreadyClosedOpenTime)
			{
				Current.OpenSeconds += Time.time - _openedAt;
			}
			if (Phase == Phase.Draining)
			{
				Current.DrainSeconds = Time.time - _closedAt;
			}
			Current.EndedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
			Current.EndReason = reason;
			if (_waits.Count > 0)
			{
				Current.AvgWaitSeconds = _waits.Average();
				Current.MaxWaitSeconds = _waits.Max();
			}
			if (Current.CustomersArrived > 0 || Current.DishesServed > 0 || Current.Income > 0)
			{
				_p.History.Add(Current);
				LastFinished = Current;
				LastFinishedAt = Time.unscaledTime;
			}
			Current = null;
			Phase = Phase.Idle;
			foreach (CustInfo value in _live.Values)
			{
				value.CountedThisSession = false;
			}
		}

		public void OnCustomerSpawn(Customer c)
		{
			CustInfo custInfo = new CustInfo();
			try
			{
				custInfo.Id = c.id.Value;
			}
			catch
			{
			}
			_live[c] = custInfo;
			if (Phase != Phase.Idle && Current != null)
			{
				custInfo.CountedThisSession = true;
				Current.CustomersArrived++;
			}
		}

		public void OnCustomerDespawn(Customer c)
		{
			if (!_live.TryGetValue(c, out var value))
			{
				return;
			}
			_live.Remove(c);
			if (Current != null && value.CountedThisSession && !value.Served && !value.Guest && !value.Banished)
			{
				if (value.Fled)
				{
					Current.FledDanger++;
				}
				else if (value.SatAt >= 0f)
				{
					Current.WalkoutsAfterSeating++;
				}
			}
		}

		private void PollCustomers()
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected I4, but got Unknown
			List<Customer> list = null;
			foreach (KeyValuePair<Customer, CustInfo> item in _live)
			{
				Customer key = item.Key;
				if ((Object)(object)key == (Object)null)
				{
					(list ?? (list = new List<Customer>())).Add(key);
					continue;
				}
				CustInfo value = item.Value;
				if (value.Id == 0)
				{
					try
					{
						value.Id = key.id.Value;
					}
					catch
					{
					}
				}
				try
				{
					if (key.isVip != null && key.isVip.Value)
					{
						value.Vip = true;
					}
				}
				catch
				{
				}
				CustomerState value2;
				try
				{
					value2 = key.CurrentState.Value;
				}
				catch
				{
					continue;
				}
				switch (value2 - 2)
				{
				case 0:
				case 1:
					if (value.SatAt < 0f)
					{
						value.SatAt = Time.time;
					}
					break;
				case 5:
					if (!value.Served)
					{
						value.Fled = true;
					}
					break;
				case 7:
				case 8:
					value.Guest = true;
					break;
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (Customer item2 in list)
			{
				_live.Remove(item2);
			}
		}

		private CustInfo FindById(uint id)
		{
			foreach (CustInfo value in _live.Values)
			{
				if (value.Id == id)
				{
					return value;
				}
			}
			return null;
		}

		private void OnGameEvent(GameEvent e, uint obj, int value)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected I4, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected I4, but got Unknown
			if (Current == null)
			{
				return;
			}
			ServiceRecord current = Current;
			if ((int)e <= 11)
			{
				if ((int)e != 10)
				{
					if ((int)e == 11 && value > 0)
					{
						current.Spent += value;
					}
				}
				else if (value > 0)
				{
					current.Income += value;
				}
				return;
			}
			switch (e - 34)
			{
			case 0:
			{
				CustInfo custInfo2 = FindById(obj);
				if (custInfo2 != null && !custInfo2.Served)
				{
					custInfo2.Served = true;
					current.CustomersServed++;
					if (custInfo2.Vip)
					{
						current.VipsServed++;
					}
					if (custInfo2.SatAt >= 0f)
					{
						_waits.Add(Time.time - custInfo2.SatAt);
					}
				}
				else if (custInfo2 == null)
				{
					current.CustomersServed++;
				}
				return;
			}
			case 1:
			{
				current.DishesServed++;
				string key = DishName(obj);
				current.Dishes.TryGetValue(key, out var value2);
				current.Dishes[key] = value2 + 1;
				return;
			}
			case 4:
				current.GotDrunk++;
				return;
			case 5:
			{
				current.DrunksBanished++;
				CustInfo custInfo = FindById(obj);
				if (custInfo != null)
				{
					custInfo.Banished = true;
				}
				return;
			}
			case 7:
				current.RoomGuests++;
				return;
			case 2:
			case 3:
			case 6:
				return;
			}
			if ((int)e != 154)
			{
				switch (e - 220)
				{
				case 0:
					current.TurnedAwayFull++;
					break;
				case 1:
					current.TurnedAwayReputation++;
					break;
				case 2:
					current.TurnedAwayCleanliness++;
					break;
				case 3:
					current.TurnedAwayCantReach++;
					break;
				}
			}
			else
			{
				current.JustInTime++;
			}
		}

		private string DishName(uint id)
		{
			if (_dishNames.TryGetValue(id, out var value))
			{
				return value;
			}
			value = "Item #" + id;
			try
			{
				ItemData val = default(ItemData);
				if ((Object)(object)ItemManager.Instance != (Object)null && ItemManager.Instance.GetItemData(id, ref val) && (Object)(object)val != (Object)null)
				{
					string name = val.name;
					if (string.IsNullOrEmpty(name))
					{
						name = ((Object)val).name;
					}
					if (!string.IsNullOrEmpty(name))
					{
						value = name;
					}
				}
			}
			catch
			{
			}
			_dishNames[id] = value;
			return value;
		}

		private static int ReadDay()
		{
			try
			{
				return (int)(((Object)(object)DayNight.Instance != (Object)null) ? DayNight.Instance.day.Value : 0);
			}
			catch
			{
				return 0;
			}
		}

		private static string ReadTavernName()
		{
			try
			{
				GameStatus instance = GameStatus.Instance;
				FieldInfo field = typeof(GameStatus).GetField("tavernName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				object obj = ((!(field != null)) ? null : field.GetValue(instance));
				if (obj == null)
				{
					return string.Empty;
				}
				PropertyInfo property = obj.GetType().GetProperty("Value");
				object obj2 = ((!(property != null)) ? obj : property.GetValue(obj, null));
				return (obj2 == null) ? string.Empty : obj2.ToString();
			}
			catch
			{
				return string.Empty;
			}
		}
	}
	internal static class DishNames
	{
		private const string Prefix = "ItemDataName";

		private static readonly Dictionary<string, string> _cache = new Dictionary<string, string>();

		public static string Display(string key)
		{
			if (string.IsNullOrEmpty(key) || key == "-")
			{
				return key;
			}
			if (_cache.TryGetValue(key, out var value))
			{
				return value;
			}
			string text = Localized(key);
			if (text == null)
			{
				return Fallback(key);
			}
			_cache[key] = text;
			return text;
		}

		private static string Localized(string key)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)LocalizationSettings.SelectedLocale == (Object)null)
				{
					return null;
				}
				string localizedString = LocalizationSettings.StringDatabase.GetLocalizedString(TableReference.op_Implicit("ItemData"), TableEntryReference.op_Implicit(key), (Locale)null, (FallbackBehavior)0, Array.Empty<object>());
				if (string.IsNullOrEmpty(localizedString) || localizedString == key || localizedString.StartsWith("No translation found"))
				{
					return Fallback(key);
				}
				return localizedString;
			}
			catch
			{
				return null;
			}
		}

		private static string Fallback(string key)
		{
			string text = (key.StartsWith("ItemDataName") ? key.Substring("ItemDataName".Length) : key);
			StringBuilder stringBuilder = new StringBuilder(text.Length + 8);
			for (int i = 0; i < text.Length; i++)
			{
				char c = text[i];
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(text[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			return stringBuilder.ToString();
		}
	}
	internal class TrackerUI
	{
		private readonly Plugin _p;

		private bool _open;

		private Rect _win = new Rect(80f, 80f, 760f, 520f);

		private Vector2 _listScroll;

		private Vector2 _detailScroll;

		private int _sel;

		private bool _confirmClear;

		private CursorLockMode _prevLock;

		private bool _prevVisible;

		private bool _blocking;

		private bool _restorePending;

		private GUIStyle _h;

		private GUIStyle _small;

		private GUIStyle _rowSel;

		private GUIStyle _row;

		private GUIStyle _good;

		private GUIStyle _bad;

		public bool IsOpen => _open;

		public TrackerUI(Plugin p)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			_p = p;
		}

		internal static bool PlayerIsFree()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			PlayerInventory instance = PlayerInventory.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				return (int)instance.state == 0;
			}
			return true;
		}

		public void Toggle()
		{
			//IL_0080: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!_open && !PlayerIsFree())
			{
				return;
			}
			_open = !_open;
			_confirmClear = false;
			if (_open)
			{
				_prevLock = Cursor.lockState;
				_prevVisible = Cursor.visible;
				_sel = 0;
				_restorePending = false;
				_blocking = false;
				PlayerInventory instance = PlayerInventory.Instance;
				if ((Object)(object)instance != (Object)null && (int)instance.state == 0)
				{
					instance.SetState((State)1);
					_blocking = true;
				}
			}
			else
			{
				Cursor.lockState = _prevLock;
				Cursor.visible = _prevVisible;
				if (_blocking)
				{
					_restorePending = true;
				}
				_blocking = false;
			}
		}

		public void LateUpdate()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Invalid comparison between Unknown and I4
			if (_restorePending)
			{
				_restorePending = false;
				PlayerInventory instance = PlayerInventory.Instance;
				if ((Object)(object)instance != (Object)null && (int)instance.state == 1)
				{
					instance.SetState((State)0);
				}
			}
			if (!_open)
			{
				return;
			}
			if (_blocking)
			{
				PlayerInventory instance2 = PlayerInventory.Instance;
				if ((Object)(object)instance2 == (Object)null || (int)instance2.state != 1)
				{
					Toggle();
					return;
				}
			}
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
		}

		private void Styles()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_00db: 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_00fa: Expected O, but got Unknown
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if (_h == null)
			{
				_h = new GUIStyle(GUI.skin.label)
				{
					fontSize = 15,
					fontStyle = (FontStyle)1
				};
				_small = new GUIStyle(GUI.skin.label)
				{
					fontSize = 12
				};
				_row = new GUIStyle(GUI.skin.button)
				{
					alignment = (TextAnchor)3,
					fontSize = 12
				};
				_rowSel = new GUIStyle(_row)
				{
					fontStyle = (FontStyle)1
				};
				_rowSel.normal.textColor = new Color(1f, 0.85f, 0.4f);
				_good = new GUIStyle(GUI.skin.label);
				_good.normal.textColor = new Color(0.55f, 0.9f, 0.55f);
				_bad = new GUIStyle(GUI.skin.label);
				_bad.normal.textColor = new Color(1f, 0.55f, 0.5f);
			}
		}

		public void Draw()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			Styles();
			Tracker tracker = _p.Tracker;
			if (_p.ShowLiveHud.Value && tracker.Phase != Phase.Idle && tracker.Current != null && !_open)
			{
				DrawLiveHud(tracker);
			}
			if (_p.ShowSummaryPopup.Value && tracker.LastFinished != null && !_open && Time.unscaledTime - tracker.LastFinishedAt < 20f)
			{
				DrawPopup(tracker.LastFinished);
			}
			if (_open)
			{
				_win = GUILayout.Window(6177219, _win, new WindowFunction(Window), string.Concat("Service History   (", _p.ToggleKey.Value, " to close)"), Array.Empty<GUILayoutOption>());
			}
		}

		private void DrawLiveHud(Tracker t)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			ServiceRecord current = t.Current;
			string text = ((t.Phase != Phase.Open) ? ("CLOSING, " + t.CustomersInside + " still inside (" + Clock(t.PhaseSeconds) + ")") : ("OPEN " + Clock(t.PhaseSeconds)));
			string text2 = text + "   Served " + current.CustomersServed + "   Lost " + current.Lost + "   Dishes " + current.DishesServed + "   +" + current.Income + "g";
			GUI.Box(new Rect(10f, 10f, 560f, 26f), string.Empty);
			GUI.Label(new Rect(18f, 13f, 550f, 22f), text2, _small);
		}

		private void DrawPopup(ServiceRecord r)
		{
			//IL_0028: 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_01ff: Unknown result type (might be due to invalid IL or missing references)
			float num = 300f;
			float num2 = 168f;
			Rect val = default(Rect);
			((Rect)(ref val))..ctor((float)Screen.width - num - 16f, 16f, num, num2);
			GUI.Box(val, "Service complete");
			GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 12f, ((Rect)(ref val)).y + 24f, num - 24f, num2 - 30f));
			GUILayout.Label("Served " + r.CustomersServed + " of " + (r.CustomersServed + r.Lost) + "  (" + r.ServedPct.ToString("0") + "%)", _small, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Income +" + r.Income + "g   Spent -" + r.Spent + "g", _small, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Dishes " + r.DishesServed + "   Top: " + DishNames.Display(r.TopDish), _small, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Avg wait " + r.AvgWaitSeconds.ToString("0") + "s   Longest " + r.MaxWaitSeconds.ToString("0") + "s", _small, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Open " + Clock(r.OpenSeconds) + "   Wind-down " + Clock(r.DrainSeconds), _small, Array.Empty<GUILayoutOption>());
			GUILayout.Label(string.Concat("Press ", _p.ToggleKey.Value, " for full history"), _small, Array.Empty<GUILayoutOption>());
			GUILayout.EndArea();
		}

		private void Window(int id)
		{
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_046e: Unknown result type (might be due to invalid IL or missing references)
			List<ServiceRecord> records = _p.History.Records;
			Tracker tracker = _p.Tracker;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label((tracker.Phase == Phase.Idle) ? "No service running" : ((tracker.Phase != Phase.Open) ? ("Closing: " + tracker.CustomersInside + " customers still inside") : ("Service running: " + Clock(tracker.PhaseSeconds))), _h, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (tracker.Phase != Phase.Idle && GUILayout.Button("End report now", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }))
			{
				tracker.ForceEnd();
			}
			GUILayout.EndHorizontal();
			if (records.Count > 0)
			{
				List<ServiceRecord> list = records.Take(10).ToList();
				GUILayout.Label("Last " + list.Count + " services: avg served " + list.Average((ServiceRecord x) => x.ServedPct).ToString("0") + "%, avg income " + ((IEnumerable<ServiceRecord>)list).Average((Func<ServiceRecord, double>)((ServiceRecord x) => x.Income)).ToString("0") + "g, avg wait " + list.Average((ServiceRecord x) => x.AvgWaitSeconds).ToString("0") + "s", _small, Array.Empty<GUILayoutOption>());
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) });
			_listScroll = GUILayout.BeginScrollView(_listScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			if (records.Count == 0)
			{
				GUILayout.Label("No services recorded yet. Open the tavern to start one.", _small, Array.Empty<GUILayoutOption>());
			}
			for (int num = 0; num < records.Count; num++)
			{
				ServiceRecord serviceRecord = records[num];
				if (GUILayout.Button("Day " + serviceRecord.GameDay + "  " + serviceRecord.StartedAt.Substring(Math.Max(0, serviceRecord.StartedAt.Length - 5)) + "   " + serviceRecord.CustomersServed + " served  +" + serviceRecord.Income + "g", (num != _sel) ? _row : _rowSel, Array.Empty<GUILayoutOption>()))
				{
					_sel = num;
					_confirmClear = false;
				}
			}
			GUILayout.EndScrollView();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (!_confirmClear)
			{
				if (records.Count > 0 && GUILayout.Button("Clear all", Array.Empty<GUILayoutOption>()))
				{
					_confirmClear = true;
				}
			}
			else
			{
				GUILayout.Label("Delete all history?", _bad, Array.Empty<GUILayoutOption>());
				if (GUILayout.Button("Yes", Array.Empty<GUILayoutOption>()))
				{
					_p.History.Clear();
					_confirmClear = false;
					_sel = 0;
				}
				if (GUILayout.Button("No", Array.Empty<GUILayoutOption>()))
				{
					_confirmClear = false;
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			_detailScroll = GUILayout.BeginScrollView(_detailScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			if (records.Count > 0)
			{
				if (_sel >= records.Count)
				{
					_sel = records.Count - 1;
				}
				Details(records[_sel]);
			}
			GUILayout.EndScrollView();
			if (records.Count > 0 && GUILayout.Button("Delete this service", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }))
			{
				_p.History.Remove(records[_sel]);
			}
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f));
		}

		private void Details(ServiceRecord r)
		{
			GUILayout.Label(((!string.IsNullOrEmpty(r.TavernName)) ? (r.TavernName + ", ") : string.Empty) + "Day " + r.GameDay, _h, Array.Empty<GUILayoutOption>());
			GUILayout.Label(r.StartedAt + " to " + (r.EndedAt ?? "?") + "   (" + r.EndReason + ")", _small, Array.Empty<GUILayoutOption>());
			if (r.JoinedMidService)
			{
				GUILayout.Label("Joined partway through, so this service is incomplete.", _bad, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(6f);
			Section("Time");
			Row("Open", Clock(r.OpenSeconds));
			Row("Wind-down after closing", Clock(r.DrainSeconds));
			Row("Average wait for first dish", r.AvgWaitSeconds.ToString("0") + "s");
			Row("Longest wait", r.MaxWaitSeconds.ToString("0") + "s");
			Section("Customers");
			Row("Arrived", r.CustomersArrived.ToString());
			Row("Served", r.CustomersServed + "  (" + r.ServedPct.ToString("0") + "%)", _good);
			if (r.VipsServed > 0)
			{
				Row("VIPs served", r.VipsServed.ToString());
			}
			Row("Walked out after sitting", r.WalkoutsAfterSeating.ToString(), (r.WalkoutsAfterSeating <= 0) ? null : _bad);
			if (r.TurnedAwayFull > 0)
			{
				Row("Turned away: no seats", r.TurnedAwayFull.ToString(), _bad);
			}
			if (r.TurnedAwayReputation > 0)
			{
				Row("Turned away: reputation", r.TurnedAwayReputation.ToString(), _bad);
			}
			if (r.TurnedAwayCleanliness > 0)
			{
				Row("Turned away: dirty tavern", r.TurnedAwayCleanliness.ToString(), _bad);
			}
			if (r.TurnedAwayCantReach > 0)
			{
				Row("Turned away: couldn't reach seat", r.TurnedAwayCantReach.ToString(), _bad);
			}
			if (r.FledDanger > 0)
			{
				Row("Fled danger", r.FledDanger.ToString(), _bad);
			}
			if (r.GotDrunk > 0)
			{
				Row("Got drunk", r.GotDrunk.ToString());
			}
			if (r.DrunksBanished > 0)
			{
				Row("Drunks thrown out", r.DrunksBanished.ToString());
			}
			if (r.RoomGuests > 0)
			{
				Row("Room guests checked in", r.RoomGuests.ToString());
			}
			Section("Money");
			Row("Income", "+" + r.Income + "g", _good);
			Row("Spent during service", "-" + r.Spent + "g");
			Row("Net", r.Net + "g", (r.Net < 0) ? _bad : _good);
			Section("Dishes (" + r.DishesServed + " served, " + r.JustInTime + " just in time)");
			foreach (KeyValuePair<string, int> item in r.Dishes.OrderByDescending((KeyValuePair<string, int> k) => k.Value))
			{
				Row(DishNames.Display(item.Key), item.Value.ToString());
			}
		}

		private void Section(string s)
		{
			GUILayout.Space(6f);
			GUILayout.Label(s, _h, Array.Empty<GUILayoutOption>());
		}

		private void Row(string k, string v, GUIStyle vs = null)
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(k, _small, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) });
			GUILayout.Label(v, vs ?? _small, Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
		}

		private static string Clock(float s)
		{
			if (s < 0f)
			{
				s = 0f;
			}
			int num = (int)(s / 60f);
			int num2 = (int)(s % 60f);
			return num + ":" + num2.ToString("00");
		}
	}
}