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 _fieldCache = new Dictionary(); public static void ScrapPrefix(NetworkBehaviourReference item, out ScrapCapture __state) { __state = default(ScrapCapture); try { GrabbableObject val = default(GrabbableObject); if (((NetworkBehaviourReference)(ref item)).TryGet(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[] array = GetField(type, "gambledScrap").GetValue(inst) as List[]; 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 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(); val.renderMode = (RenderMode)2; RectTransform component = ((Component)val).GetComponent(); component.sizeDelta = new Vector2(480f, 320f); GameObject val2 = new GameObject("BG"); val2.transform.SetParent(_board.transform, false); Image val3 = val2.AddComponent(); ((Graphic)val3).color = new Color(0.04f, 0.06f, 0.05f, 0.92f); RectTransform component2 = val2.GetComponent(); 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.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(); 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("LegacyRuntime.ttf"); } catch { } } if ((Object)(object)val == (Object)null) { try { val = Resources.GetBuiltinResource("Arial.ttf"); } catch { } } return val; } private void RefreshText(TrackerManager tm) { if ((Object)(object)_text == (Object)null) { return; } List 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) + " +" + biggestWin.biggestSingleWin.ToString("N0") + " (" + biggestWin.biggestWinGame + ")"); } else { stringBuilder.AppendLine("BIGGEST WIN: —"); } if (biggestLoss != null && biggestLoss.biggestSingleLoss > 0) { stringBuilder.AppendLine("BIGGEST LOSS: " + Trunc(biggestLoss.username, 10) + " -" + biggestLoss.biggestSingleLoss.ToString("N0") + " (" + biggestLoss.biggestLossGame + ")"); } else { stringBuilder.AppendLine("BIGGEST LOSS: —"); } stringBuilder.AppendLine("TOTAL GAMES: " + tm.TotalGames()); _text.text = stringBuilder.ToString(); } private static void FindExtremes(List 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 "" + text + ""; } if (v < 0) { return "" + text + ""; } return "" + text + ""; } 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 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 GetCrew(StartOfRound sor) { List list = new List(); 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(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(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 records = new List(); } [Serializable] public class SessionEntry { public long clientId; public string username = ""; public int delta; } [Serializable] public class SyncBlob { public List records = new List(); public List session = new List(); } [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 _verbose; private static ConfigEntry _hudEnabled; private static ConfigEntry _hudMarginX; private static ConfigEntry _hudMarginY; private static ConfigEntry _hudWidth; private static ConfigEntry _hudFont; private static ConfigEntry _summaryEnabled; private static ConfigEntry _summaryDuration; private static ConfigEntry _sfxEnabled; private static ConfigEntry _sfxVolume; private static ConfigEntry _chalkEnabled; private static ConfigEntry _chalkX; private static ConfigEntry _chalkY; private static ConfigEntry _chalkZ; private static ConfigEntry _chalkScale; private static ConfigEntry _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(); _host.AddComponent(); _host.AddComponent(); _host.AddComponent(); } Log.LogInfo((object)"LethalCasinoTracker 1.0.0 loaded."); } private void BindConfig() { _verbose = ((BaseUnityPlugin)this).Config.Bind("General", "VerboseLogging", false, "Log every tracked bet result (debugging)."); _hudEnabled = ((BaseUnityPlugin)this).Config.Bind("HUD", "Enabled", true, "Show the top-right session scoreboard while on a moon."); _hudMarginX = ((BaseUnityPlugin)this).Config.Bind("HUD", "MarginX", 12, "Distance from the right edge of the screen, in pixels."); _hudMarginY = ((BaseUnityPlugin)this).Config.Bind("HUD", "MarginY", 70, "Distance from the top edge of the screen, in pixels."); _hudWidth = ((BaseUnityPlugin)this).Config.Bind("HUD", "Width", 215, "Panel width in pixels."); _hudFont = ((BaseUnityPlugin)this).Config.Bind("HUD", "FontSize", 14, "Font size for HUD rows."); _summaryEnabled = ((BaseUnityPlugin)this).Config.Bind("Summary", "Enabled", true, "Show the centered session recap on takeoff."); _summaryDuration = ((BaseUnityPlugin)this).Config.Bind("Summary", "HoldSeconds", 4.5f, "How long the recap stays fully visible."); _sfxEnabled = ((BaseUnityPlugin)this).Config.Bind("Summary", "Sound", true, "Play a win/loss jingle with the recap."); _sfxVolume = ((BaseUnityPlugin)this).Config.Bind("Summary", "Volume", 0.5f, "Recap jingle volume (0-1)."); _chalkEnabled = ((BaseUnityPlugin)this).Config.Bind("Chalkboard", "Enabled", true, "Spawn the physical casino records board near the casino."); _chalkX = ((BaseUnityPlugin)this).Config.Bind("Chalkboard", "OffsetRight", 0f, "Board placement offset along the casino's right axis (m)."); _chalkY = ((BaseUnityPlugin)this).Config.Bind("Chalkboard", "OffsetUp", 2.6f, "Board placement offset upward from the casino (m)."); _chalkZ = ((BaseUnityPlugin)this).Config.Bind("Chalkboard", "OffsetForward", 4.5f, "Board placement offset in front of the casino (m)."); _chalkScale = ((BaseUnityPlugin)this).Config.Bind("Chalkboard", "Scale", 0.0075f, "World scale of the board."); _chalkUpright = ((BaseUnityPlugin)this).Config.Bind("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 _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(); _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 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 BuildRows(Dictionary snap) { List list = new List(); 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 _sessionDeltas = new Dictionary(); private readonly Dictionary _runRecords = new Dictionary(); private readonly Dictionary _pending = new Dictionary(); 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 list = null; foreach (KeyValuePair item in _pending) { if (realtimeSinceStartup - item.Value.lastTouch >= 0.85f) { if (list == null) { list = new List(); } 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 list = new List(_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 sessionSnapshot = new Dictionary(_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(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(Es3Key(), "LethalCasinoTracker.es3", ""); if (string.IsNullOrEmpty(text)) { RecordsDirty = true; return; } RunSaveBlob runSaveBlob = JsonUtility.FromJson(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 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 GetRecordsSorted() { List list = new List(_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)); } } } }