Decompiled source of LethalCasinoTracker v1.0.0

LethalCasinoTracker.dll

Decompiled 12 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCasinoTracker.Patches;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace LethalCasinoTracker
{
	public enum CasinoGameType
	{
		Unknown,
		Blackjack,
		Roulette,
		Slots,
		Wheel
	}
	public static class CasinoGameNames
	{
		public static string Display(CasinoGameType t)
		{
			return t switch
			{
				CasinoGameType.Blackjack => "Blackjack", 
				CasinoGameType.Roulette => "Roulette", 
				CasinoGameType.Slots => "Slots", 
				CasinoGameType.Wheel => "The Wheel", 
				_ => "Casino", 
			};
		}

		public static CasinoGameType FromTypeName(string typeName)
		{
			return typeName switch
			{
				"Blackjack" => CasinoGameType.Blackjack, 
				"Roulette" => CasinoGameType.Roulette, 
				"SlotMachine" => CasinoGameType.Slots, 
				"TheWheel" => CasinoGameType.Wheel, 
				_ => CasinoGameType.Unknown, 
			};
		}
	}
	public static class CasinoHooks
	{
		public struct ScrapCapture
		{
			public bool valid;

			public GrabbableObject grab;

			public int before;
		}

		public static readonly string[] GameTypeNames = new string[4] { "Blackjack", "Roulette", "SlotMachine", "TheWheel" };

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

		public static void ScrapPrefix(NetworkBehaviourReference item, out ScrapCapture __state)
		{
			__state = default(ScrapCapture);
			try
			{
				GrabbableObject val = default(GrabbableObject);
				if (((NetworkBehaviourReference)(ref item)).TryGet<GrabbableObject>(ref val, (NetworkManager)null) && (Object)(object)val != (Object)null)
				{
					__state.valid = true;
					__state.grab = val;
					__state.before = val.scrapValue;
				}
			}
			catch
			{
			}
		}

		public static void ScrapPostfix(object __instance, ScrapCapture __state)
		{
			if (!__state.valid || (Object)(object)__state.grab == (Object)null || !TrackerManager.IsServer())
			{
				return;
			}
			try
			{
				int num = __state.grab.scrapValue - __state.before;
				if (num == 0)
				{
					return;
				}
				string name = __instance.GetType().Name;
				CasinoGameType game = CasinoGameNames.FromTypeName(name);
				PlayerControllerB val = ResolveOwner(__instance, name, __state.grab);
				if ((Object)(object)val == (Object)null)
				{
					if (Plugin.VerboseLogging)
					{
						Plugin.Log.LogWarning((object)("Could not resolve owner for a " + name + " payout of " + num));
					}
					return;
				}
				int num2 = 0;
				Object val2 = (Object)((__instance is Object) ? __instance : null);
				if (val2 != (Object)null)
				{
					num2 = val2.GetInstanceID();
				}
				ulong playerClientId = val.playerClientId;
				string playerUsername = val.playerUsername;
				if ((Object)(object)TrackerManager.Instance != (Object)null)
				{
					TrackerManager.Instance.RecordResolution(playerClientId, playerUsername, num, game, num2);
				}
				NetworkSync.BroadcastDelta(playerClientId, playerUsername, num, game, num2);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Casino capture error: " + ex.Message));
			}
		}

		private static PlayerControllerB ResolveOwner(object inst, string typeName, GrabbableObject grab)
		{
			Type type = inst.GetType();
			switch (typeName)
			{
			case "Blackjack":
			{
				List<GrabbableObject>[] array = GetField(type, "gambledScrap").GetValue(inst) as List<GrabbableObject>[];
				PlayerControllerB[] array2 = GetField(type, "gamblingPlayers").GetValue(inst) as PlayerControllerB[];
				if (array == null || array2 == null)
				{
					return null;
				}
				int num = Math.Min(array.Length, array2.Length);
				for (int i = 0; i < num; i++)
				{
					if (array[i] != null && array[i].Contains(grab))
					{
						return array2[i];
					}
				}
				return null;
			}
			case "Roulette":
			{
				if (GetField(type, "gambledScrapOwners").GetValue(inst) is Dictionary<GrabbableObject, PlayerControllerB> dictionary && dictionary.TryGetValue(grab, out var value2))
				{
					return value2;
				}
				return null;
			}
			case "SlotMachine":
			case "TheWheel":
			{
				FieldInfo field = GetField(type, "gamblingPlayer");
				if (!(field == null))
				{
					object? value = field.GetValue(inst);
					return (PlayerControllerB)((value is PlayerControllerB) ? value : null);
				}
				return null;
			}
			default:
				return null;
			}
		}

		private static FieldInfo GetField(Type t, string name)
		{
			string key = t.FullName + "." + name;
			if (!_fieldCache.TryGetValue(key, out var value))
			{
				value = AccessTools.Field(t, name);
				_fieldCache[key] = value;
			}
			return value;
		}

		public static void ApplyPatches(Harmony h)
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00bb: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(CasinoHooks), "ScrapPrefix", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(CasinoHooks), "ScrapPostfix", (Type[])null, (Type[])null);
			for (int i = 0; i < GameTypeNames.Length; i++)
			{
				string text = "LethalCasino.Custom." + GameTypeNames[i];
				Type type = AccessTools.TypeByName(text);
				if (type == null)
				{
					Plugin.Log.LogWarning((object)("Casino type not found: " + text));
					continue;
				}
				MethodInfo methodInfo3 = AccessTools.Method(type, "UpdateScrapValueClientRpc", (Type[])null, (Type[])null);
				if (methodInfo3 == null)
				{
					Plugin.Log.LogWarning((object)("UpdateScrapValueClientRpc not found on " + text));
					continue;
				}
				try
				{
					h.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo), new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Plugin.Log.LogInfo((object)("Patched " + GameTypeNames[i] + ".UpdateScrapValueClientRpc"));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Failed to patch " + text + ": " + ex.Message));
				}
			}
		}
	}
	public class ChalkboardController : MonoBehaviour
	{
		private Type _buildingType;

		private Component _building;

		private float _nextSearch;

		private GameObject _board;

		private Text _text;

		private bool _builtDirty = true;

		private static readonly string Rule = new string('=', 34);

		private void Start()
		{
			_buildingType = AccessTools.TypeByName("LethalCasino.Custom.CasinoBuilding");
			if (_buildingType == null)
			{
				Plugin.Log.LogWarning((object)"CasinoBuilding type not found; chalkboard disabled.");
			}
		}

		private void Update()
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ChalkboardEnabled() || _buildingType == null)
			{
				return;
			}
			if ((Object)(object)_building == (Object)null && Time.realtimeSinceStartup >= _nextSearch)
			{
				_nextSearch = Time.realtimeSinceStartup + 1.5f;
				Object val = Object.FindObjectOfType(_buildingType);
				_building = (Component)(object)((val is Component) ? val : null);
				if ((Object)(object)_building != (Object)null && (Object)(object)_board == (Object)null)
				{
					BuildBoard();
				}
			}
			if ((Object)(object)_building == (Object)null)
			{
				if ((Object)(object)_board != (Object)null)
				{
					Object.Destroy((Object)(object)_board);
					_board = null;
				}
			}
			else
			{
				if ((Object)(object)_board == (Object)null)
				{
					return;
				}
				Vector3 position = _building.transform.position + _building.transform.right * Plugin.ChalkOffsetX() + Vector3.up * Plugin.ChalkOffsetY() + _building.transform.forward * Plugin.ChalkOffsetZ();
				_board.transform.position = position;
				Camera playerCamera = GetPlayerCamera();
				if ((Object)(object)playerCamera != (Object)null)
				{
					Vector3 val2 = _board.transform.position - ((Component)playerCamera).transform.position;
					if (Plugin.ChalkUpright())
					{
						val2.y = 0f;
					}
					if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f)
					{
						_board.transform.rotation = Quaternion.LookRotation(val2);
					}
				}
				TrackerManager instance = TrackerManager.Instance;
				if ((Object)(object)instance != (Object)null && (instance.RecordsDirty || _builtDirty))
				{
					RefreshText(instance);
					instance.RecordsDirty = false;
					_builtDirty = false;
				}
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_board != (Object)null)
			{
				Object.Destroy((Object)(object)_board);
			}
		}

		private static Camera GetPlayerCamera()
		{
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				return main;
			}
			Camera[] allCameras = Camera.allCameras;
			for (int i = 0; i < allCameras.Length; i++)
			{
				if ((Object)(object)allCameras[i] != (Object)null && ((Behaviour)allCameras[i]).isActiveAndEnabled)
				{
					return allCameras[i];
				}
			}
			return Camera.current;
		}

		private void BuildBoard()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0069: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			_board = new GameObject("LCCasinoChalkboard");
			_board.transform.localScale = Vector3.one * Plugin.ChalkScale();
			Canvas val = _board.AddComponent<Canvas>();
			val.renderMode = (RenderMode)2;
			RectTransform component = ((Component)val).GetComponent<RectTransform>();
			component.sizeDelta = new Vector2(480f, 320f);
			GameObject val2 = new GameObject("BG");
			val2.transform.SetParent(_board.transform, false);
			Image val3 = val2.AddComponent<Image>();
			((Graphic)val3).color = new Color(0.04f, 0.06f, 0.05f, 0.92f);
			RectTransform component2 = val2.GetComponent<RectTransform>();
			component2.anchorMin = Vector2.zero;
			component2.anchorMax = Vector2.one;
			component2.offsetMin = Vector2.zero;
			component2.offsetMax = Vector2.zero;
			GameObject val4 = new GameObject("Text");
			val4.transform.SetParent(_board.transform, false);
			_text = val4.AddComponent<Text>();
			_text.font = ResolveFont();
			_text.fontSize = 18;
			((Graphic)_text).color = new Color(0.93f, 0.96f, 0.92f);
			_text.alignment = (TextAnchor)0;
			_text.horizontalOverflow = (HorizontalWrapMode)1;
			_text.verticalOverflow = (VerticalWrapMode)1;
			_text.supportRichText = true;
			RectTransform component3 = val4.GetComponent<RectTransform>();
			component3.anchorMin = Vector2.zero;
			component3.anchorMax = Vector2.one;
			component3.offsetMin = new Vector2(18f, 14f);
			component3.offsetMax = new Vector2(-18f, -14f);
			_builtDirty = true;
			ManualLogSource log = Plugin.Log;
			Vector3 position = _building.transform.position;
			log.LogInfo((object)("Chalkboard spawned at casino (" + ((Vector3)(ref position)).ToString("F1") + ")."));
		}

		private static Font ResolveFont()
		{
			Font val = null;
			try
			{
				val = Font.CreateDynamicFontFromOSFont(new string[3] { "Consolas", "Courier New", "Lucida Console" }, 18);
			}
			catch
			{
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = Resources.GetBuiltinResource<Font>("Arial.ttf");
				}
				catch
				{
				}
			}
			return val;
		}

		private void RefreshText(TrackerManager tm)
		{
			if ((Object)(object)_text == (Object)null)
			{
				return;
			}
			List<PlayerRunRecord> recordsSorted = tm.GetRecordsSorted();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("CASINO RECORDS — CURRENT RUN");
			stringBuilder.AppendLine(Rule);
			if (recordsSorted.Count == 0)
			{
				stringBuilder.AppendLine("No bets recorded yet.");
			}
			else
			{
				for (int i = 0; i < recordsSorted.Count; i++)
				{
					PlayerRunRecord playerRunRecord = recordsSorted[i];
					string text = Trunc(playerRunRecord.username, 10).PadRight(11);
					string text2 = "+" + playerRunRecord.totalWon.ToString("N0");
					string text3 = "-" + playerRunRecord.totalLost.ToString("N0");
					string text4 = (text2 + " / " + text3).PadRight(20);
					string text5 = SignedColored(playerRunRecord.Net);
					stringBuilder.AppendLine(text + text4 + " NET: " + text5);
				}
			}
			stringBuilder.AppendLine(Rule);
			FindExtremes(recordsSorted, out var biggestWin, out var biggestLoss);
			if (biggestWin != null && biggestWin.biggestSingleWin > 0)
			{
				stringBuilder.AppendLine("BIGGEST WIN:  " + Trunc(biggestWin.username, 10) + "  <color=#6dff7f>+" + biggestWin.biggestSingleWin.ToString("N0") + "</color>  (" + biggestWin.biggestWinGame + ")");
			}
			else
			{
				stringBuilder.AppendLine("BIGGEST WIN:  —");
			}
			if (biggestLoss != null && biggestLoss.biggestSingleLoss > 0)
			{
				stringBuilder.AppendLine("BIGGEST LOSS: " + Trunc(biggestLoss.username, 10) + "  <color=#ff6e6e>-" + biggestLoss.biggestSingleLoss.ToString("N0") + "</color>  (" + biggestLoss.biggestLossGame + ")");
			}
			else
			{
				stringBuilder.AppendLine("BIGGEST LOSS: —");
			}
			stringBuilder.AppendLine("TOTAL GAMES:  " + tm.TotalGames());
			_text.text = stringBuilder.ToString();
		}

		private static void FindExtremes(List<PlayerRunRecord> recs, out PlayerRunRecord biggestWin, out PlayerRunRecord biggestLoss)
		{
			biggestWin = null;
			biggestLoss = null;
			for (int i = 0; i < recs.Count; i++)
			{
				PlayerRunRecord playerRunRecord = recs[i];
				if (biggestWin == null || playerRunRecord.biggestSingleWin > biggestWin.biggestSingleWin)
				{
					biggestWin = playerRunRecord;
				}
				if (biggestLoss == null || playerRunRecord.biggestSingleLoss > biggestLoss.biggestSingleLoss)
				{
					biggestLoss = playerRunRecord;
				}
			}
		}

		private static string SignedColored(int v)
		{
			string text = ((v > 0) ? "+" : "") + v.ToString("N0");
			if (v > 0)
			{
				return "<color=#6dff7f>" + text + "</color>";
			}
			if (v < 0)
			{
				return "<color=#ff6e6e>" + text + "</color>";
			}
			return "<color=#dddddd>" + text + "</color>";
		}

		private static string Trunc(string s, int max)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			if (s.Length > max)
			{
				return s.Substring(0, max);
			}
			return s;
		}
	}
	public class HUDController : MonoBehaviour
	{
		private static readonly Color ColHeader = new Color(1f, 0.86f, 0.45f);

		private static readonly Color ColNeutral = new Color(0.8f, 0.8f, 0.82f);

		private static readonly Color ColUp = new Color(0.42f, 1f, 0.5f);

		private static readonly Color ColDown = new Color(1f, 0.43f, 0.43f);

		private Texture2D _panel;

		private Texture2D _line;

		private GUIStyle _title;

		private GUIStyle _nameStyle;

		private GUIStyle _numStyle;

		private void EnsureResources()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00b6: 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: Expected O, but got Unknown
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			if ((Object)(object)_panel == (Object)null)
			{
				_panel = SolidTex(new Color(0.05f, 0.05f, 0.07f, 0.86f));
			}
			if ((Object)(object)_line == (Object)null)
			{
				_line = SolidTex(new Color(1f, 1f, 1f, 0.22f));
			}
			if (_title == null)
			{
				_title = new GUIStyle(GUI.skin.label);
				_title.alignment = (TextAnchor)4;
				_title.fontStyle = (FontStyle)1;
				_title.fontSize = Plugin.HudFontSize() + 1;
				_title.normal.textColor = ColHeader;
			}
			if (_nameStyle == null)
			{
				_nameStyle = new GUIStyle(GUI.skin.label);
				_nameStyle.alignment = (TextAnchor)3;
				_nameStyle.fontSize = Plugin.HudFontSize();
			}
			if (_numStyle == null)
			{
				_numStyle = new GUIStyle(GUI.skin.label);
				_numStyle.alignment = (TextAnchor)5;
				_numStyle.fontStyle = (FontStyle)1;
				_numStyle.fontSize = Plugin.HudFontSize();
			}
		}

		private void OnGUI()
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.HudEnabled())
			{
				return;
			}
			TrackerManager instance = TrackerManager.Instance;
			StartOfRound instance2 = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || !instance2.shipHasLanded || instance2.shipIsLeaving)
			{
				return;
			}
			List<PlayerControllerB> crew = GetCrew(instance2);
			if (crew.Count == 0)
			{
				return;
			}
			EnsureResources();
			float num = 8f;
			float num2 = (float)Plugin.HudFontSize() + 8f;
			float num3 = Plugin.HudWidth();
			float num4 = (float)Screen.width - num3 - (float)Plugin.HudMarginX();
			float num5 = Plugin.HudMarginY();
			float num6 = num2;
			float num7 = num + num6 + 6f + 1f + 6f + (float)crew.Count * num2 + 6f + 1f + 6f + num2 + num;
			GUI.color = Color.white;
			GUI.DrawTexture(new Rect(num4, num5, num3, num7), (Texture)(object)_panel);
			float num8 = num5 + num;
			GUI.Label(new Rect(num4 + num, num8, num3 - num * 2f, num6), "SESSION", _title);
			num8 += num6 + 6f;
			GUI.DrawTexture(new Rect(num4 + num, num8, num3 - num * 2f, 1f), (Texture)(object)_line);
			num8 += 6f;
			for (int i = 0; i < crew.Count; i++)
			{
				PlayerControllerB val = crew[i];
				string text = (string.IsNullOrEmpty(val.playerUsername) ? ("Player " + val.playerClientId) : val.playerUsername);
				GUI.color = ColNeutral;
				GUI.Label(new Rect(num4 + num, num8, num3 * 0.55f, num2), text, _nameStyle);
				if (instance.TryGetSessionDelta(val.playerClientId, out var delta) && delta != 0)
				{
					GUI.color = ((delta > 0) ? ColUp : ColDown);
					GUI.Label(new Rect(num4 + num3 * 0.45f - num, num8, num3 * 0.55f, num2), Signed(delta), _numStyle);
				}
				num8 += num2;
			}
			num8 += 6f;
			GUI.color = Color.white;
			GUI.DrawTexture(new Rect(num4 + num, num8, num3 - num * 2f, 1f), (Texture)(object)_line);
			num8 += 6f;
			int num9 = instance.CrewNet();
			GUI.color = ColHeader;
			GUI.Label(new Rect(num4 + num, num8, num3 * 0.5f, num2), "CREW", _nameStyle);
			GUI.color = ((num9 > 0) ? ColUp : ((num9 < 0) ? ColDown : Color.white));
			GUI.Label(new Rect(num4 + num3 * 0.45f - num, num8, num3 * 0.55f, num2), (num9 == 0) ? "0" : Signed(num9), _numStyle);
			GUI.color = Color.white;
		}

		private static List<PlayerControllerB> GetCrew(StartOfRound sor)
		{
			List<PlayerControllerB> list = new List<PlayerControllerB>();
			PlayerControllerB[] allPlayerScripts = sor.allPlayerScripts;
			if (allPlayerScripts == null)
			{
				return list;
			}
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && (val.isPlayerControlled || val.isPlayerDead))
				{
					list.Add(val);
				}
			}
			return list;
		}

		public static string Signed(int v)
		{
			return ((v > 0) ? "+" : "") + v.ToString("N0");
		}

		private static Texture2D SolidTex(Color c)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false);
			val.SetPixel(0, 0, c);
			val.Apply();
			((Texture)val).wrapMode = (TextureWrapMode)0;
			((Object)val).hideFlags = (HideFlags)61;
			return val;
		}
	}
	[Serializable]
	public class DeltaMsg
	{
		public long id;

		public int delta;

		public int game;

		public int inst;

		public string name = "";
	}
	public static class NetworkSync
	{
		private const string MsgDelta = "LCCT_Delta";

		private const string MsgRequest = "LCCT_Req";

		private const string MsgState = "LCCT_State";

		private static bool _registered;

		private static NetworkManager _registeredOn;

		private static bool _requested;

		public static void Tick()
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
				{
					_registered = false;
					_registeredOn = null;
					_requested = false;
					return;
				}
				if (!_registered || (Object)(object)_registeredOn != (Object)(object)singleton)
				{
					Register(singleton);
				}
				if (!singleton.IsServer && singleton.IsConnectedClient && !_requested)
				{
					Send("LCCT_Req", 0uL, "1");
					_requested = true;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("NetworkSync.Tick: " + ex.Message));
			}
		}

		public static void RequestResync()
		{
			_requested = false;
		}

		private static void Register(NetworkManager nm)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			CustomMessagingManager customMessagingManager = nm.CustomMessagingManager;
			if (customMessagingManager != null)
			{
				customMessagingManager.RegisterNamedMessageHandler("LCCT_Delta", new HandleNamedMessageDelegate(OnDelta));
				customMessagingManager.RegisterNamedMessageHandler("LCCT_Req", new HandleNamedMessageDelegate(OnRequest));
				customMessagingManager.RegisterNamedMessageHandler("LCCT_State", new HandleNamedMessageDelegate(OnState));
				_registered = true;
				_registeredOn = nm;
				Plugin.Log.LogInfo((object)("NetworkSync handlers registered (server=" + nm.IsServer + ")."));
			}
		}

		public static void BroadcastDelta(ulong id, string username, int delta, CasinoGameType game, int instId)
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null || !singleton.IsServer || singleton.CustomMessagingManager == null)
				{
					return;
				}
				DeltaMsg deltaMsg = new DeltaMsg();
				deltaMsg.id = (long)id;
				deltaMsg.delta = delta;
				deltaMsg.game = (int)game;
				deltaMsg.inst = instId;
				deltaMsg.name = ((username == null) ? "" : username);
				string payload = JsonUtility.ToJson((object)deltaMsg);
				foreach (ulong connectedClientsId in singleton.ConnectedClientsIds)
				{
					if (connectedClientsId != 0)
					{
						Send("LCCT_Delta", connectedClientsId, payload);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("BroadcastDelta: " + ex.Message));
			}
		}

		private static void Send(string msg, ulong clientId, string payload)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(Encoding.UTF8.GetByteCount(payload) + 64, (Allocator)2, 524288);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe(payload, false);
				NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(msg, clientId, val, (NetworkDelivery)2);
			}
			finally
			{
				((FastBufferWriter)(ref val)).Dispose();
			}
		}

		private static void OnDelta(ulong sender, FastBufferReader reader)
		{
			try
			{
				if (!TrackerManager.IsServer())
				{
					string text = default(string);
					((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
					DeltaMsg deltaMsg = JsonUtility.FromJson<DeltaMsg>(text);
					if (deltaMsg != null && (Object)(object)TrackerManager.Instance != (Object)null)
					{
						TrackerManager.Instance.RecordResolution((ulong)deltaMsg.id, deltaMsg.name, deltaMsg.delta, (CasinoGameType)deltaMsg.game, deltaMsg.inst);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("OnDelta: " + ex.Message));
			}
		}

		private static void OnRequest(ulong sender, FastBufferReader reader)
		{
			try
			{
				if (TrackerManager.IsServer() && !((Object)(object)TrackerManager.Instance == (Object)null))
				{
					SyncBlob syncBlob = TrackerManager.Instance.BuildSyncBlob();
					Send("LCCT_State", sender, JsonUtility.ToJson((object)syncBlob));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("OnRequest: " + ex.Message));
			}
		}

		private static void OnState(ulong sender, FastBufferReader reader)
		{
			try
			{
				if (!TrackerManager.IsServer())
				{
					string text = default(string);
					((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
					SyncBlob b = JsonUtility.FromJson<SyncBlob>(text);
					if ((Object)(object)TrackerManager.Instance != (Object)null)
					{
						TrackerManager.Instance.ApplySyncBlob(b);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("OnState: " + ex.Message));
			}
		}
	}
	[Serializable]
	public class PlayerRunRecord
	{
		public long clientId;

		public string username = "";

		public int totalWon;

		public int totalLost;

		public int biggestSingleWin;

		public int biggestSingleLoss;

		public string biggestWinGame = "";

		public string biggestLossGame = "";

		public int gamesPlayed;

		public int Net => totalWon - totalLost;
	}
	[Serializable]
	public class RunSaveBlob
	{
		public List<PlayerRunRecord> records = new List<PlayerRunRecord>();
	}
	[Serializable]
	public class SessionEntry
	{
		public long clientId;

		public string username = "";

		public int delta;
	}
	[Serializable]
	public class SyncBlob
	{
		public List<PlayerRunRecord> records = new List<PlayerRunRecord>();

		public List<SessionEntry> session = new List<SessionEntry>();
	}
	[BepInPlugin("jaredfreedman.LethalCasinoTracker", "LethalCasinoTracker", "1.0.0")]
	[BepInDependency("mrgrm7.LethalCasino", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "jaredfreedman.LethalCasinoTracker";

		public static ManualLogSource Log;

		public static bool VerboseLogging;

		private static GameObject _host;

		private static ConfigEntry<bool> _verbose;

		private static ConfigEntry<bool> _hudEnabled;

		private static ConfigEntry<int> _hudMarginX;

		private static ConfigEntry<int> _hudMarginY;

		private static ConfigEntry<int> _hudWidth;

		private static ConfigEntry<int> _hudFont;

		private static ConfigEntry<bool> _summaryEnabled;

		private static ConfigEntry<float> _summaryDuration;

		private static ConfigEntry<bool> _sfxEnabled;

		private static ConfigEntry<float> _sfxVolume;

		private static ConfigEntry<bool> _chalkEnabled;

		private static ConfigEntry<float> _chalkX;

		private static ConfigEntry<float> _chalkY;

		private static ConfigEntry<float> _chalkZ;

		private static ConfigEntry<float> _chalkScale;

		private static ConfigEntry<bool> _chalkUpright;

		private void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			try
			{
				Harmony h = new Harmony("jaredfreedman.LethalCasinoTracker");
				GameOverPatch.ApplyPatches(h);
				CasinoHooks.ApplyPatches(h);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to apply Harmony patches: " + ex.Message));
			}
			if ((Object)(object)_host == (Object)null)
			{
				_host = new GameObject("LethalCasinoTracker");
				Object.DontDestroyOnLoad((Object)(object)_host);
				_host.AddComponent<TrackerManager>();
				_host.AddComponent<SummaryScreenController>();
				_host.AddComponent<HUDController>();
				_host.AddComponent<ChalkboardController>();
			}
			Log.LogInfo((object)"LethalCasinoTracker 1.0.0 loaded.");
		}

		private void BindConfig()
		{
			_verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Log every tracked bet result (debugging).");
			_hudEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Enabled", true, "Show the top-right session scoreboard while on a moon.");
			_hudMarginX = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "MarginX", 12, "Distance from the right edge of the screen, in pixels.");
			_hudMarginY = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "MarginY", 70, "Distance from the top edge of the screen, in pixels.");
			_hudWidth = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "Width", 215, "Panel width in pixels.");
			_hudFont = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "FontSize", 14, "Font size for HUD rows.");
			_summaryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Summary", "Enabled", true, "Show the centered session recap on takeoff.");
			_summaryDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Summary", "HoldSeconds", 4.5f, "How long the recap stays fully visible.");
			_sfxEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Summary", "Sound", true, "Play a win/loss jingle with the recap.");
			_sfxVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Summary", "Volume", 0.5f, "Recap jingle volume (0-1).");
			_chalkEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Chalkboard", "Enabled", true, "Spawn the physical casino records board near the casino.");
			_chalkX = ((BaseUnityPlugin)this).Config.Bind<float>("Chalkboard", "OffsetRight", 0f, "Board placement offset along the casino's right axis (m).");
			_chalkY = ((BaseUnityPlugin)this).Config.Bind<float>("Chalkboard", "OffsetUp", 2.6f, "Board placement offset upward from the casino (m).");
			_chalkZ = ((BaseUnityPlugin)this).Config.Bind<float>("Chalkboard", "OffsetForward", 4.5f, "Board placement offset in front of the casino (m).");
			_chalkScale = ((BaseUnityPlugin)this).Config.Bind<float>("Chalkboard", "Scale", 0.0075f, "World scale of the board.");
			_chalkUpright = ((BaseUnityPlugin)this).Config.Bind<bool>("Chalkboard", "KeepUpright", true, "Keep the board vertical when facing the player.");
			VerboseLogging = _verbose.Value;
		}

		public static bool HudEnabled()
		{
			if (_hudEnabled != null)
			{
				return _hudEnabled.Value;
			}
			return true;
		}

		public static int HudMarginX()
		{
			if (_hudMarginX != null)
			{
				return _hudMarginX.Value;
			}
			return 12;
		}

		public static int HudMarginY()
		{
			if (_hudMarginY != null)
			{
				return _hudMarginY.Value;
			}
			return 70;
		}

		public static int HudWidth()
		{
			if (_hudWidth != null)
			{
				return _hudWidth.Value;
			}
			return 215;
		}

		public static int HudFontSize()
		{
			if (_hudFont != null)
			{
				return _hudFont.Value;
			}
			return 14;
		}

		public static bool SummaryEnabled()
		{
			if (_summaryEnabled != null)
			{
				return _summaryEnabled.Value;
			}
			return true;
		}

		public static float SummaryDuration()
		{
			if (_summaryDuration != null)
			{
				return _summaryDuration.Value;
			}
			return 4.5f;
		}

		public static bool SfxEnabled()
		{
			if (_sfxEnabled != null)
			{
				return _sfxEnabled.Value;
			}
			return true;
		}

		public static float SfxVolume()
		{
			if (_sfxVolume != null)
			{
				return _sfxVolume.Value;
			}
			return 0.5f;
		}

		public static bool ChalkboardEnabled()
		{
			if (_chalkEnabled != null)
			{
				return _chalkEnabled.Value;
			}
			return true;
		}

		public static float ChalkOffsetX()
		{
			if (_chalkX != null)
			{
				return _chalkX.Value;
			}
			return 0f;
		}

		public static float ChalkOffsetY()
		{
			if (_chalkY != null)
			{
				return _chalkY.Value;
			}
			return 2.6f;
		}

		public static float ChalkOffsetZ()
		{
			if (_chalkZ != null)
			{
				return _chalkZ.Value;
			}
			return 4.5f;
		}

		public static float ChalkScale()
		{
			if (_chalkScale != null)
			{
				return _chalkScale.Value;
			}
			return 0.0075f;
		}

		public static bool ChalkUpright()
		{
			if (_chalkUpright != null)
			{
				return _chalkUpright.Value;
			}
			return true;
		}
	}
	public class SummaryScreenController : MonoBehaviour
	{
		private class Row
		{
			public string name;

			public bool has;

			public int delta;
		}

		private static readonly Color ColHeader = new Color(1f, 0.86f, 0.45f);

		private static readonly Color ColNeutral = new Color(0.82f, 0.82f, 0.84f);

		private static readonly Color ColUp = new Color(0.42f, 1f, 0.5f);

		private static readonly Color ColDown = new Color(1f, 0.43f, 0.43f);

		private bool _active;

		private float _alpha;

		private List<Row> _rows;

		private int _crewNet;

		private Coroutine _co;

		private Texture2D _panel;

		private Texture2D _line;

		private GUIStyle _title;

		private GUIStyle _name;

		private GUIStyle _num;

		private AudioSource _audio;

		private AudioClip _winClip;

		private AudioClip _loseClip;

		private AudioClip _neutralClip;

		public static SummaryScreenController Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			_audio = ((Component)this).gameObject.AddComponent<AudioSource>();
			_audio.playOnAwake = false;
			_audio.spatialBlend = 0f;
			_audio.ignoreListenerPause = true;
			_audio.ignoreListenerVolume = false;
			try
			{
				_winClip = MakeJingle("win", new float[4] { 523.25f, 659.25f, 783.99f, 1046.5f }, 0.13f, 0f);
				_loseClip = MakeJingle("lose", new float[4] { 440f, 392f, 349.23f, 293.66f }, 0.2f, -28f);
				_neutralClip = MakeJingle("neutral", new float[1] { 660f }, 0.09f, 0f);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not build recap audio: " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public void Show(Dictionary<ulong, int> sessionSnapshot, int crewNet)
		{
			if (!Plugin.SummaryEnabled())
			{
				if ((Object)(object)TrackerManager.Instance != (Object)null)
				{
					TrackerManager.Instance.EndSessionWipe();
				}
				return;
			}
			_rows = BuildRows(sessionSnapshot);
			_crewNet = crewNet;
			PlayResultSound(crewNet);
			if (_co != null)
			{
				((MonoBehaviour)this).StopCoroutine(_co);
			}
			_co = ((MonoBehaviour)this).StartCoroutine(Run());
		}

		private static List<Row> BuildRows(Dictionary<ulong, int> snap)
		{
			List<Row> list = new List<Row>();
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null && instance.allPlayerScripts != null)
			{
				PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
				foreach (PlayerControllerB val in allPlayerScripts)
				{
					if (!((Object)(object)val == (Object)null) && (val.isPlayerControlled || val.isPlayerDead))
					{
						Row row = new Row();
						row.name = (string.IsNullOrEmpty(val.playerUsername) ? ("Player " + val.playerClientId) : val.playerUsername);
						row.has = snap != null && snap.TryGetValue(val.playerClientId, out var value) && value != 0;
						row.delta = (row.has ? snap[val.playerClientId] : 0);
						list.Add(row);
					}
				}
			}
			return list;
		}

		private IEnumerator Run()
		{
			_active = true;
			float fadeIn = 0.4f;
			float hold = Plugin.SummaryDuration();
			float fadeOut = 0.6f;
			float t = 0f;
			while (t < fadeIn)
			{
				t += Time.unscaledDeltaTime;
				_alpha = Mathf.Clamp01(t / fadeIn);
				yield return null;
			}
			_alpha = 1f;
			yield return (object)new WaitForSecondsRealtime(hold);
			t = 0f;
			while (t < fadeOut)
			{
				t += Time.unscaledDeltaTime;
				_alpha = 1f - Mathf.Clamp01(t / fadeOut);
				yield return null;
			}
			_alpha = 0f;
			_active = false;
			_co = null;
			if ((Object)(object)TrackerManager.Instance != (Object)null)
			{
				TrackerManager.Instance.EndSessionWipe();
			}
		}

		private void EnsureResources()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			if ((Object)(object)_panel == (Object)null)
			{
				_panel = SolidTex(new Color(0.04f, 0.04f, 0.06f, 0.9f));
			}
			if ((Object)(object)_line == (Object)null)
			{
				_line = SolidTex(new Color(1f, 1f, 1f, 0.25f));
			}
			if (_title == null)
			{
				_title = new GUIStyle(GUI.skin.label);
				_title.alignment = (TextAnchor)4;
				_title.fontStyle = (FontStyle)1;
				_title.fontSize = 20;
			}
			if (_name == null)
			{
				_name = new GUIStyle(GUI.skin.label);
				_name.alignment = (TextAnchor)3;
				_name.fontSize = 16;
			}
			if (_num == null)
			{
				_num = new GUIStyle(GUI.skin.label);
				_num.alignment = (TextAnchor)5;
				_num.fontStyle = (FontStyle)1;
				_num.fontSize = 16;
			}
		}

		private void OnGUI()
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			if (!_active || _rows == null)
			{
				return;
			}
			EnsureResources();
			float num = 380f;
			float num2 = 16f;
			float num3 = 26f;
			float num4 = 30f;
			float num5 = num2 + num4 + 8f + 1f + 8f + (float)_rows.Count * num3 + 8f + 1f + 8f + num3 + num2;
			float num6 = ((float)Screen.width - num) * 0.5f;
			float num7 = ((float)Screen.height - num5) * 0.5f;
			float alpha = _alpha;
			GUI.color = new Color(1f, 1f, 1f, alpha);
			GUI.DrawTexture(new Rect(num6, num7, num, num5), (Texture)(object)_panel);
			float num8 = num7 + num2;
			SetColor(ColHeader, alpha);
			GUI.Label(new Rect(num6 + num2, num8, num - num2 * 2f, num4), "CASINO SESSION RECAP", _title);
			num8 += num4 + 8f;
			GUI.color = new Color(1f, 1f, 1f, alpha);
			GUI.DrawTexture(new Rect(num6 + num2, num8, num - num2 * 2f, 1f), (Texture)(object)_line);
			num8 += 8f;
			for (int i = 0; i < _rows.Count; i++)
			{
				Row row = _rows[i];
				SetColor(ColNeutral, alpha);
				GUI.Label(new Rect(num6 + num2, num8, num * 0.55f, num3), row.name, _name);
				if (row.has)
				{
					SetColor((row.delta > 0) ? ColUp : ColDown, alpha);
					GUI.Label(new Rect(num6 + num * 0.45f - num2, num8, num * 0.55f, num3), HUDController.Signed(row.delta), _num);
				}
				else
				{
					SetColor(ColNeutral, alpha);
					GUI.Label(new Rect(num6 + num * 0.45f - num2, num8, num * 0.55f, num3), "—", _num);
				}
				num8 += num3;
			}
			num8 += 8f;
			GUI.color = new Color(1f, 1f, 1f, alpha);
			GUI.DrawTexture(new Rect(num6 + num2, num8, num - num2 * 2f, 1f), (Texture)(object)_line);
			num8 += 8f;
			SetColor(ColHeader, alpha);
			GUI.Label(new Rect(num6 + num2, num8, num * 0.5f, num3), "CREW NET", _name);
			SetColor((_crewNet > 0) ? ColUp : ((_crewNet < 0) ? ColDown : Color.white), alpha);
			GUI.Label(new Rect(num6 + num * 0.45f - num2, num8, num * 0.55f, num3), (_crewNet == 0) ? "0" : HUDController.Signed(_crewNet), _num);
			GUI.color = Color.white;
		}

		private void PlayResultSound(int crewNet)
		{
			if (Plugin.SfxEnabled() && !((Object)(object)_audio == (Object)null))
			{
				AudioClip val = ((crewNet > 0) ? _winClip : ((crewNet < 0) ? _loseClip : _neutralClip));
				if ((Object)(object)val != (Object)null)
				{
					_audio.PlayOneShot(val, Plugin.SfxVolume());
				}
			}
		}

		private void SetColor(Color c, float a)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			GUI.color = new Color(c.r, c.g, c.b, c.a * a);
		}

		private static AudioClip MakeJingle(string name, float[] freqs, float noteDur, float noteGlideHz)
		{
			int num = 44100;
			int num2 = Mathf.Max(1, (int)(noteDur * (float)num));
			int num3 = num2 * freqs.Length;
			float[] array = new float[num3];
			for (int i = 0; i < freqs.Length; i++)
			{
				float num4 = freqs[i];
				for (int j = 0; j < num2; j++)
				{
					float num5 = (float)j / (float)num;
					float num6 = (float)j / (float)num2;
					float num7 = num4 + noteGlideHz * num6;
					float num8 = Mathf.Exp(-4f * num6);
					float num9 = Mathf.Clamp01(num6 * 40f);
					float num10 = Mathf.Sin((float)Math.PI * 2f * num7 * num5) * num8 * num9 * 0.28f;
					array[i * num2 + j] = num10;
				}
			}
			AudioClip val = AudioClip.Create("LCCT_" + name, num3, 1, num, false);
			val.SetData(array, 0);
			return val;
		}

		private static Texture2D SolidTex(Color c)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false);
			val.SetPixel(0, 0, c);
			val.Apply();
			((Object)val).hideFlags = (HideFlags)61;
			return val;
		}
	}
	public class TrackerManager : MonoBehaviour
	{
		private class Pending
		{
			public ulong id;

			public string name;

			public int delta;

			public CasinoGameType game;

			public float lastTouch;
		}

		public const string Es3File = "LethalCasinoTracker.es3";

		private const string Es3KeyPrefix = "CasinoTracker_RunRecords_";

		private const float FlushDelay = 0.85f;

		private readonly Dictionary<ulong, int> _sessionDeltas = new Dictionary<ulong, int>();

		private readonly Dictionary<ulong, PlayerRunRecord> _runRecords = new Dictionary<ulong, PlayerRunRecord>();

		private readonly Dictionary<string, Pending> _pending = new Dictionary<string, Pending>();

		private bool _recordsLoaded;

		private bool _wasLanded;

		private bool _wasListening;

		public static TrackerManager Instance { get; private set; }

		public bool SessionActive { get; private set; }

		public bool RecordsDirty { get; set; }

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public void RecordResolution(ulong id, string username, int delta, CasinoGameType game, int gameInstanceId)
		{
			if (delta != 0)
			{
				string text = (string.IsNullOrEmpty(username) ? ("Player " + id) : username);
				_sessionDeltas.TryGetValue(id, out var value);
				_sessionDeltas[id] = value + delta;
				string key = gameInstanceId + ":" + id;
				if (!_pending.TryGetValue(key, out var value2))
				{
					value2 = new Pending();
					value2.id = id;
					value2.name = text;
					value2.game = game;
					_pending[key] = value2;
				}
				value2.delta += delta;
				value2.game = game;
				value2.name = text;
				value2.lastTouch = Time.realtimeSinceStartup;
				if (Plugin.VerboseLogging)
				{
					Plugin.Log.LogInfo((object)("Delta " + delta + " -> " + text + " (" + CasinoGameNames.Display(game) + "), session net now " + _sessionDeltas[id]));
				}
			}
		}

		private void Update()
		{
			bool flag = (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsListening;
			if (_wasListening && !flag)
			{
				SoftResetForMenu();
			}
			_wasListening = flag;
			StartOfRound instance = StartOfRound.Instance;
			bool flag2 = (Object)(object)instance != (Object)null && instance.shipHasLanded && !instance.shipIsLeaving;
			if (flag2 && !_wasLanded)
			{
				OnShipLanded();
			}
			else if (!flag2 && _wasLanded)
			{
				OnShipLeaving();
			}
			_wasLanded = flag2;
			if (_pending.Count > 0)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				List<string> list = null;
				foreach (KeyValuePair<string, Pending> item in _pending)
				{
					if (realtimeSinceStartup - item.Value.lastTouch >= 0.85f)
					{
						if (list == null)
						{
							list = new List<string>();
						}
						list.Add(item.Key);
					}
				}
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						Pending p = _pending[list[i]];
						_pending.Remove(list[i]);
						FinalizeRound(p);
					}
				}
			}
			NetworkSync.Tick();
		}

		private void FinalizeRound(Pending p)
		{
			PlayerRunRecord orCreateRecord = GetOrCreateRecord(p.id, p.name);
			orCreateRecord.gamesPlayed++;
			if (p.delta > 0)
			{
				orCreateRecord.totalWon += p.delta;
				if (p.delta > orCreateRecord.biggestSingleWin)
				{
					orCreateRecord.biggestSingleWin = p.delta;
					orCreateRecord.biggestWinGame = CasinoGameNames.Display(p.game);
				}
			}
			else if (p.delta < 0)
			{
				int num = -p.delta;
				orCreateRecord.totalLost += num;
				if (num > orCreateRecord.biggestSingleLoss)
				{
					orCreateRecord.biggestSingleLoss = num;
					orCreateRecord.biggestLossGame = CasinoGameNames.Display(p.game);
				}
			}
			RecordsDirty = true;
		}

		private void FlushAllPendingNow()
		{
			if (_pending.Count != 0)
			{
				List<Pending> list = new List<Pending>(_pending.Values);
				_pending.Clear();
				for (int i = 0; i < list.Count; i++)
				{
					FinalizeRound(list[i]);
				}
			}
		}

		private PlayerRunRecord GetOrCreateRecord(ulong id, string name)
		{
			if (!_runRecords.TryGetValue(id, out var value))
			{
				value = new PlayerRunRecord();
				value.clientId = (long)id;
				value.username = name;
				_runRecords[id] = value;
			}
			else if (!string.IsNullOrEmpty(name))
			{
				value.username = name;
			}
			return value;
		}

		public void OnShipLanded()
		{
			SessionActive = true;
			if (IsServer())
			{
				if (!_recordsLoaded)
				{
					LoadRecords();
				}
				_recordsLoaded = true;
			}
			else
			{
				NetworkSync.RequestResync();
			}
			RecordsDirty = true;
		}

		public void OnShipLeaving()
		{
			FlushAllPendingNow();
			if (IsServer())
			{
				SaveRecords();
			}
			Dictionary<ulong, int> sessionSnapshot = new Dictionary<ulong, int>(_sessionDeltas);
			SessionActive = false;
			SummaryScreenController instance = SummaryScreenController.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.Show(sessionSnapshot, CrewNet());
			}
			else
			{
				EndSessionWipe();
			}
		}

		public void EndSessionWipe()
		{
			_sessionDeltas.Clear();
			RecordsDirty = true;
		}

		private void SoftResetForMenu()
		{
			_sessionDeltas.Clear();
			_runRecords.Clear();
			_pending.Clear();
			SessionActive = false;
			_wasLanded = false;
			_recordsLoaded = false;
			RecordsDirty = true;
		}

		public void WipeRun()
		{
			_sessionDeltas.Clear();
			_runRecords.Clear();
			_pending.Clear();
			SessionActive = false;
			RecordsDirty = true;
			if (IsServer())
			{
				try
				{
					ES3.DeleteKey(Es3Key(), "LethalCasinoTracker.es3");
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("ES3 DeleteKey failed: " + ex.Message));
				}
			}
			Plugin.Log.LogInfo((object)"Casino run records wiped.");
		}

		private static string Es3Key()
		{
			string text = "default";
			try
			{
				if ((Object)(object)GameNetworkManager.Instance != (Object)null && !string.IsNullOrEmpty(GameNetworkManager.Instance.currentSaveFileName))
				{
					text = GameNetworkManager.Instance.currentSaveFileName;
				}
			}
			catch
			{
			}
			return "CasinoTracker_RunRecords_" + text;
		}

		public void SaveRecords()
		{
			try
			{
				RunSaveBlob runSaveBlob = new RunSaveBlob();
				foreach (PlayerRunRecord value in _runRecords.Values)
				{
					runSaveBlob.records.Add(value);
				}
				string text = JsonUtility.ToJson((object)runSaveBlob);
				ES3.Save<string>(Es3Key(), text, "LethalCasinoTracker.es3");
				if (Plugin.VerboseLogging)
				{
					Plugin.Log.LogInfo((object)("Saved " + runSaveBlob.records.Count + " run records."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to save run records: " + ex.Message));
			}
		}

		public void LoadRecords()
		{
			try
			{
				_runRecords.Clear();
				if (!ES3.KeyExists(Es3Key(), "LethalCasinoTracker.es3"))
				{
					RecordsDirty = true;
					return;
				}
				string text = ES3.Load<string>(Es3Key(), "LethalCasinoTracker.es3", "");
				if (string.IsNullOrEmpty(text))
				{
					RecordsDirty = true;
					return;
				}
				RunSaveBlob runSaveBlob = JsonUtility.FromJson<RunSaveBlob>(text);
				if (runSaveBlob != null && runSaveBlob.records != null)
				{
					for (int i = 0; i < runSaveBlob.records.Count; i++)
					{
						PlayerRunRecord playerRunRecord = runSaveBlob.records[i];
						if (playerRunRecord != null)
						{
							_runRecords[(ulong)playerRunRecord.clientId] = playerRunRecord;
						}
					}
				}
				Plugin.Log.LogInfo((object)("Loaded " + _runRecords.Count + " run records."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to load run records: " + ex.Message));
			}
			RecordsDirty = true;
		}

		public SyncBlob BuildSyncBlob()
		{
			SyncBlob syncBlob = new SyncBlob();
			foreach (PlayerRunRecord value2 in _runRecords.Values)
			{
				syncBlob.records.Add(value2);
			}
			foreach (KeyValuePair<ulong, int> sessionDelta in _sessionDeltas)
			{
				SessionEntry sessionEntry = new SessionEntry();
				sessionEntry.clientId = (long)sessionDelta.Key;
				sessionEntry.delta = sessionDelta.Value;
				sessionEntry.username = (_runRecords.TryGetValue(sessionDelta.Key, out var value) ? value.username : "");
				syncBlob.session.Add(sessionEntry);
			}
			return syncBlob;
		}

		public void ApplySyncBlob(SyncBlob b)
		{
			if (b == null)
			{
				return;
			}
			_runRecords.Clear();
			if (b.records != null)
			{
				for (int i = 0; i < b.records.Count; i++)
				{
					PlayerRunRecord playerRunRecord = b.records[i];
					if (playerRunRecord != null)
					{
						_runRecords[(ulong)playerRunRecord.clientId] = playerRunRecord;
					}
				}
			}
			_sessionDeltas.Clear();
			if (b.session != null)
			{
				for (int j = 0; j < b.session.Count; j++)
				{
					SessionEntry sessionEntry = b.session[j];
					if (sessionEntry != null)
					{
						_sessionDeltas[(ulong)sessionEntry.clientId] = sessionEntry.delta;
					}
				}
			}
			_recordsLoaded = true;
			RecordsDirty = true;
			Plugin.Log.LogInfo((object)("Applied tracker state from host (" + _runRecords.Count + " records)."));
		}

		public bool TryGetSessionDelta(ulong id, out int delta)
		{
			return _sessionDeltas.TryGetValue(id, out delta);
		}

		public int CrewNet()
		{
			int num = 0;
			foreach (int value in _sessionDeltas.Values)
			{
				num += value;
			}
			return num;
		}

		public List<PlayerRunRecord> GetRecordsSorted()
		{
			List<PlayerRunRecord> list = new List<PlayerRunRecord>(_runRecords.Values);
			list.Sort((PlayerRunRecord a, PlayerRunRecord b) => b.Net.CompareTo(a.Net));
			return list;
		}

		public int TotalGames()
		{
			int num = 0;
			foreach (PlayerRunRecord value in _runRecords.Values)
			{
				num += value.gamesPlayed;
			}
			return num;
		}

		public static bool IsServer()
		{
			if ((Object)(object)NetworkManager.Singleton != (Object)null)
			{
				return NetworkManager.Singleton.IsServer;
			}
			return false;
		}
	}
}
namespace LethalCasinoTracker.Patches
{
	public static class GameOverPatch
	{
		public static void OnPlayersFired()
		{
			if ((Object)(object)TrackerManager.Instance != (Object)null)
			{
				Plugin.Log.LogInfo((object)"Crew eliminated by the company — wiping casino records.");
				TrackerManager.Instance.WipeRun();
			}
		}

		public static void OnSaveReset()
		{
			if ((!((Object)(object)StartOfRound.Instance != (Object)null) || !StartOfRound.Instance.shipHasLanded) && (Object)(object)TrackerManager.Instance != (Object)null)
			{
				TrackerManager.Instance.WipeRun();
			}
		}

		public static void ApplyPatches(Harmony h)
		{
			Patch(h, typeof(StartOfRound), "FirePlayersAfterDeadlineClientRpc", "OnPlayersFired");
			Patch(h, typeof(GameNetworkManager), "ResetSavedGameValues", "OnSaveReset");
		}

		private static void Patch(Harmony h, Type target, string method, string postfixName)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(target, method, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Plugin.Log.LogWarning((object)("Patch target not found: " + target.Name + "." + method));
					return;
				}
				MethodInfo methodInfo2 = AccessTools.Method(typeof(GameOverPatch), postfixName, (Type[])null, (Type[])null);
				h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Plugin.Log.LogInfo((object)("Patched " + target.Name + "." + method));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to patch " + target.Name + "." + method + ": " + ex.Message));
			}
		}
	}
}