Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalCasinoTracker v1.0.0
LethalCasinoTracker.dll
Decompiled 12 hours agousing 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)); } } } }