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 CheckoutStatsTW v1.5.6
CheckoutStatsTW.dll
Decompiled 4 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using Photon.Pun; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CheckoutStatsTW")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("在結帳時顯示所有玩家的詳細表現統計。")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("CheckoutStatsTW")] [assembly: AssemblyTitle("CheckoutStatsTW")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CheckoutStatsTW { [BepInPlugin("com.wuzuan.repo.checkoutstatstw", "CheckoutStatsTW", "1.5.6")] public class CheckoutStatsPlugin : BaseUnityPlugin { public class PlayerStats { public string PlayerName = "Player"; public int HaulValue; public int Kills; public int DamageValue; public int Deaths; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ScrollViewBuilderDelegate <>9__19_0; public static ScrollViewBuilderDelegate <>9__19_1; public static Action <>9__19_4; public static Action <>9__19_5; public static BuilderDelegate <>9__19_2; internal RectTransform <OpenMenuLibPopup>b__19_0(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) REPOLabel obj = MenuAPI.CreateREPOLabel("", parent, Vector2.zero); TextMeshProUGUI labelTMP = obj.labelTMP; ((TMP_Text)labelTMP).text = "<color=#A347FF>玩家</color> | <color=#47FF47>價值</color> | <color=#FFFF47>擊殺</color> | <color=#FF4747>損壞</color> | <color=#FF8C47>死亡</color>"; ((TMP_Text)labelTMP).fontSize = 18f; ((TMP_Text)labelTMP).fontStyle = (FontStyles)1; return ((REPOElement)obj).rectTransform; } internal RectTransform <OpenMenuLibPopup>b__19_1(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) REPOLabel obj = MenuAPI.CreateREPOLabel("目前尚無數據錄入...", parent, Vector2.zero); ((TMP_Text)obj.labelTMP).alignment = (TextAlignmentOptions)514; return ((REPOElement)obj).rectTransform; } internal void <OpenMenuLibPopup>b__19_2(Transform parent) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) MenuAPI.CreateREPOButton("清除數據", (Action)delegate { ClearAllStats(); Logger.LogInfo((object)"[CheckoutStatsTW] 統計數據已手動清除"); }, parent, new Vector2(-120f, -180f)); MenuAPI.CreateREPOButton("關閉視窗", (Action)delegate { ShowStatsUI = false; if ((Object)(object)StatsPopup != (Object)null) { StatsPopup.ClosePage(true); } }, parent, new Vector2(120f, -180f)); } internal void <OpenMenuLibPopup>b__19_4() { ClearAllStats(); Logger.LogInfo((object)"[CheckoutStatsTW] 統計數據已手動清除"); } internal void <OpenMenuLibPopup>b__19_5() { ShowStatsUI = false; if ((Object)(object)StatsPopup != (Object)null) { StatsPopup.ClosePage(true); } } } private readonly Harmony _harmony = new Harmony("com.wuzuan.repo.checkoutstatstw"); public static Dictionary<string, PlayerStats> CurrentStats = new Dictionary<string, PlayerStats>(); public static Dictionary<int, string> EnemyLastHitBy = new Dictionary<int, string>(); public static HashSet<int> HauledObjects = new HashSet<int>(); public static Dictionary<string, string> PlayerNames = new Dictionary<string, string>(); public static bool ShowStatsUI = false; public static REPOPopupPage? StatsPopup; public static StatsSync? SyncComponent; public static CheckoutStatsPlugin Instance { get; private set; } = null; public static ManualLogSource Logger { get; private set; } = null; private void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Instance = this; Logger = ((BaseUnityPlugin)this).Logger; GameObject val = new GameObject("CheckoutStatsSyncObject"); Object.DontDestroyOnLoad((Object)val); SyncComponent = val.AddComponent<StatsSync>(); SafePatch(typeof(Patch_PlayerAvatar_Register)); SafePatch(typeof(Patch_PlayerAvatar_Death)); SafePatch(typeof(Patch_EnemyHealth_HurtRPC)); SafePatch(typeof(Patch_EnemyHealth_Death)); SafePatch(typeof(Patch_ValuableObject_AddToHaulList)); SafePatch(typeof(Patch_PhysGrabObject_BreakRPC)); SafePatch(typeof(Patch_RunManager_ChangeLevel)); SafePatch(typeof(Patch_RunManager_LeaveToMainMenu)); SafePatch(typeof(Patch_RunManager_RestartRun)); SafePatch(typeof(Patch_GameDirector_InputHook)); Logger.LogInfo((object)"[CheckoutStatsTW] v1.5.6 已載入"); } public static void ClearAllStats() { CurrentStats.Clear(); EnemyLastHitBy.Clear(); HauledObjects.Clear(); PlayerNames.Clear(); if ((Object)(object)StatsPopup != (Object)null) { StatsPopup.ClosePage(true); StatsPopup = null; } ShowStatsUI = false; } public static void OpenMenuLibPopup() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown try { if ((Object)(object)StatsPopup != (Object)null && ((Component)StatsPopup).gameObject.activeSelf) { return; } MenuAPI.CloseAllPagesAddedOnTop(); StatsPopup = MenuAPI.CreateREPOPopupPage("本局結算統計", true, true, 0f, (Vector2?)Vector2.zero); if ((Object)(object)StatsPopup == (Object)null) { return; } REPOPopupPage? statsPopup = StatsPopup; object obj = <>c.<>9__19_0; if (obj == null) { ScrollViewBuilderDelegate val = delegate(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) REPOLabel obj5 = MenuAPI.CreateREPOLabel("", parent, Vector2.zero); TextMeshProUGUI labelTMP2 = obj5.labelTMP; ((TMP_Text)labelTMP2).text = "<color=#A347FF>玩家</color> | <color=#47FF47>價值</color> | <color=#FFFF47>擊殺</color> | <color=#FF4747>損壞</color> | <color=#FF8C47>死亡</color>"; ((TMP_Text)labelTMP2).fontSize = 18f; ((TMP_Text)labelTMP2).fontStyle = (FontStyles)1; return ((REPOElement)obj5).rectTransform; }; <>c.<>9__19_0 = val; obj = (object)val; } statsPopup.AddElementToScrollView((ScrollViewBuilderDelegate)obj, 0f, 0f); if (CurrentStats.Count == 0) { REPOPopupPage? statsPopup2 = StatsPopup; object obj2 = <>c.<>9__19_1; if (obj2 == null) { ScrollViewBuilderDelegate val2 = delegate(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) REPOLabel obj4 = MenuAPI.CreateREPOLabel("目前尚無數據錄入...", parent, Vector2.zero); ((TMP_Text)obj4.labelTMP).alignment = (TextAlignmentOptions)514; return ((REPOElement)obj4).rectTransform; }; <>c.<>9__19_1 = val2; obj2 = (object)val2; } statsPopup2.AddElementToScrollView((ScrollViewBuilderDelegate)obj2, 0f, 0f); } else { foreach (KeyValuePair<string, PlayerStats> currentStat in CurrentStats) { PlayerStats s = currentStat.Value; StatsPopup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) REPOLabel val4 = MenuAPI.CreateREPOLabel("", parent, Vector2.zero); TMP_Text labelTMP = (TMP_Text)(object)val4.labelTMP; labelTMP.text = $"{s.PlayerName,-10} | <color=#47FF47>${s.HaulValue,5}</color> | <color=#FFFF47>{s.Kills,3}</color> | <color=#FF4747>${s.DamageValue,4}</color> | <color=#FF8C47>{s.Deaths,2}</color>"; labelTMP.fontSize = 16f; return ((REPOElement)val4).rectTransform; }, 0f, 0f); } } REPOPopupPage? statsPopup3 = StatsPopup; object obj3 = <>c.<>9__19_2; if (obj3 == null) { BuilderDelegate val3 = delegate(Transform parent) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) MenuAPI.CreateREPOButton("清除數據", (Action)delegate { ClearAllStats(); Logger.LogInfo((object)"[CheckoutStatsTW] 統計數據已手動清除"); }, parent, new Vector2(-120f, -180f)); MenuAPI.CreateREPOButton("關閉視窗", (Action)delegate { ShowStatsUI = false; if ((Object)(object)StatsPopup != (Object)null) { StatsPopup.ClosePage(true); } }, parent, new Vector2(120f, -180f)); }; <>c.<>9__19_2 = val3; obj3 = (object)val3; } statsPopup3.AddElement((BuilderDelegate)obj3); StatsPopup.OpenPage(true); MenuPage component = ((Component)StatsPopup).GetComponent<MenuPage>(); if ((Object)(object)component != (Object)null) { component.PageStateSet((PageState)1); } } catch (Exception arg) { Logger.LogError((object)$"[CheckoutStatsTW] UI 建立失敗: {arg}"); } } private void SafePatch(Type patchClass) { try { _harmony.PatchAll(patchClass); Logger.LogInfo((object)("[CheckoutStatsTW] Patch 成功: " + patchClass.Name)); } catch (Exception ex) { Logger.LogWarning((object)("[CheckoutStatsTW] Patch 失敗: " + patchClass.Name + " -> " + ex.Message)); } } public static PlayerStats GetOrCreate(string steamId) { if (!CurrentStats.TryGetValue(steamId, out PlayerStats value)) { value = new PlayerStats(); if (PlayerNames.TryGetValue(steamId, out string value2)) { value.PlayerName = value2; } CurrentStats[steamId] = value; } return value; } public static bool IsMasterClient() { try { return PhotonNetwork.IsMasterClient; } catch { return true; } } } public class StatsSync : MonoBehaviour { public void BroadcastStatDelta(string steamId, string playerName, string statType, int delta) { if (!CheckoutStatsPlugin.IsMasterClient()) { return; } ApplyDelta(steamId, playerName, statType, delta); try { PhotonView component = ((Component)this).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && PhotonNetwork.IsConnected) { component.RPC("ReceiveStatDelta", (RpcTarget)1, new object[4] { steamId, playerName, statType, delta }); } } catch { } } [PunRPC] public void ReceiveStatDelta(string steamId, string playerName, string statType, int delta) { ApplyDelta(steamId, playerName, statType, delta); } public static void ApplyDelta(string steamId, string playerName, string statType, int delta) { CheckoutStatsPlugin.PlayerStats orCreate = CheckoutStatsPlugin.GetOrCreate(steamId); if (!string.IsNullOrEmpty(playerName)) { orCreate.PlayerName = playerName; } switch (statType) { case "haul": orCreate.HaulValue += delta; break; case "kill": orCreate.Kills += delta; break; case "damage": orCreate.DamageValue += delta; break; case "death": orCreate.Deaths += delta; break; } } } [HarmonyPatch(typeof(GameDirector), "Update")] public static class Patch_GameDirector_InputHook { [HarmonyPostfix] private static void Postfix(GameDirector __instance) { if ((Object)(object)((Component)__instance).GetComponent<InputDebugger>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<InputDebugger>(); } } } public class InputDebugger : MonoBehaviour { private void Update() { if (Input.GetKeyDown((KeyCode)292)) { ExecuteToggle(); } } private void ExecuteToggle() { CheckoutStatsPlugin.ShowStatsUI = !CheckoutStatsPlugin.ShowStatsUI; if (CheckoutStatsPlugin.ShowStatsUI) { CheckoutStatsPlugin.OpenMenuLibPopup(); } else if ((Object)(object)CheckoutStatsPlugin.StatsPopup != (Object)null) { CheckoutStatsPlugin.StatsPopup.ClosePage(true); CheckoutStatsPlugin.StatsPopup = null; } } } [HarmonyPatch(typeof(PlayerAvatar), "AddToStatsManagerRPC")] public static class Patch_PlayerAvatar_Register { [HarmonyPostfix] private static void Postfix(string _playerName, string _steamID) { if (!string.IsNullOrEmpty(_steamID)) { CheckoutStatsPlugin.PlayerNames[_steamID] = _playerName; if (CheckoutStatsPlugin.CurrentStats.TryGetValue(_steamID, out CheckoutStatsPlugin.PlayerStats value)) { value.PlayerName = _playerName; } } } } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")] public static class Patch_PlayerAvatar_Death { [HarmonyPostfix] private static void Postfix(PlayerAvatar __instance) { try { string text = SemiFunc.PlayerGetSteamID(__instance); if (!string.IsNullOrEmpty(text)) { string value; string playerName = (CheckoutStatsPlugin.PlayerNames.TryGetValue(text, out value) ? value : "Player"); if (CheckoutStatsPlugin.IsMasterClient()) { CheckoutStatsPlugin.SyncComponent?.BroadcastStatDelta(text, playerName, "death", 1); } else { StatsSync.ApplyDelta(text, playerName, "death", 1); } } } catch { } } } [HarmonyPatch(typeof(EnemyHealth), "HurtRPC")] public static class Patch_EnemyHealth_HurtRPC { [HarmonyPostfix] private static void Postfix(EnemyHealth __instance) { try { PlayerAvatar val = TryGetAttacker(__instance); if (!((Object)(object)val == (Object)null)) { string value = SemiFunc.PlayerGetSteamID(val); if (!string.IsNullOrEmpty(value)) { CheckoutStatsPlugin.EnemyLastHitBy[((Object)__instance).GetInstanceID()] = value; } } } catch { } } private static PlayerAvatar? TryGetAttacker(EnemyHealth health) { string[] array = new string[6] { "killedByPlayer", "lastHurtByPlayer", "culpritPlayer", "CulpritPlayer", "lastAttacker", "hurtByPlayer" }; foreach (string name in array) { object? obj = typeof(EnemyHealth).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(health); PlayerAvatar val = (PlayerAvatar)((obj is PlayerAvatar) ? obj : null); if (val != null) { return val; } } object? obj2 = typeof(EnemyHealth).GetField("enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(health); Enemy val2 = (Enemy)((obj2 is Enemy) ? obj2 : null); if (val2 != null) { array = new string[4] { "CulpritPlayer", "culpritPlayer", "lastAttacker", "hurtByPlayer" }; foreach (string name2 in array) { object? obj3 = typeof(Enemy).GetField(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val2); PlayerAvatar val3 = (PlayerAvatar)((obj3 is PlayerAvatar) ? obj3 : null); if (val3 != null) { return val3; } } } return null; } } [HarmonyPatch(typeof(EnemyHealth), "Death")] public static class Patch_EnemyHealth_Death { [HarmonyPostfix] private static void Postfix(EnemyHealth __instance) { try { int instanceID = ((Object)__instance).GetInstanceID(); if (CheckoutStatsPlugin.EnemyLastHitBy.TryGetValue(instanceID, out string value)) { string value2; string playerName = (CheckoutStatsPlugin.PlayerNames.TryGetValue(value, out value2) ? value2 : "Player"); if (CheckoutStatsPlugin.IsMasterClient()) { CheckoutStatsPlugin.SyncComponent?.BroadcastStatDelta(value, playerName, "kill", 1); } } } catch { } } } [HarmonyPatch(typeof(ValuableObject), "AddToDollarHaulList")] public static class Patch_ValuableObject_AddToHaulList { private static readonly string[] ExcludeKeywords = new string[1] { "surplus valuable" }; [HarmonyPostfix] private static void Postfix(ValuableObject __instance) { if (!CheckoutStatsPlugin.IsMasterClient()) { return; } try { int num = Mathf.RoundToInt(Traverse.Create((object)__instance).Field("dollarValueCurrent").GetValue<float>()); if (num <= 0) { return; } string text = ((Object)((Component)__instance).gameObject).name.ToLower().Replace("(clone)", "").Trim(); string[] excludeKeywords = ExcludeKeywords; foreach (string value in excludeKeywords) { if (text.Contains(value)) { CheckoutStatsPlugin.Logger.LogInfo((object)$"[CheckoutStatsTW] 跳過錢袋: '{((Object)((Component)__instance).gameObject).name}' (${num})"); return; } } int instanceID = ((Object)__instance).GetInstanceID(); if (CheckoutStatsPlugin.HauledObjects.Contains(instanceID)) { return; } CheckoutStatsPlugin.HauledObjects.Add(instanceID); PlayerAvatar val = null; object? obj = typeof(ValuableObject).GetField("physGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(__instance); PhysGrabObject val2 = (PhysGrabObject)((obj is PhysGrabObject) ? obj : null); if (val2 != null && typeof(PhysGrabObject).GetField("playerGrabbing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val2) is List<PhysGrabber> list && list.Count > 0) { val = list[0]?.playerAvatar; } if ((Object)(object)val == (Object)null) { val = PlayerAvatar.instance; } if (!((Object)(object)val == (Object)null)) { string text2 = SemiFunc.PlayerGetSteamID(val); if (!string.IsNullOrEmpty(text2)) { string value2; string playerName = (CheckoutStatsPlugin.PlayerNames.TryGetValue(text2, out value2) ? value2 : "Player"); CheckoutStatsPlugin.SyncComponent?.BroadcastStatDelta(text2, playerName, "haul", num); } } } catch { } } } [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")] public static class Patch_PhysGrabObject_BreakRPC { [HarmonyPostfix] private static void Postfix(PhysGrabObjectImpactDetector __instance, float valueLost, bool _loseValue) { try { if (!_loseValue || valueLost <= 0f) { return; } PhysGrabObject component = ((Component)__instance).GetComponent<PhysGrabObject>(); if ((Object)(object)component == (Object)null) { return; } PlayerAvatar val = null; if (typeof(PhysGrabObject).GetField("playerGrabbing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component) is List<PhysGrabber> list && list.Count > 0) { val = list[0]?.playerAvatar; } if ((Object)(object)val == (Object)null) { return; } string text = SemiFunc.PlayerGetSteamID(val); if (!string.IsNullOrEmpty(text)) { string value; string playerName = (CheckoutStatsPlugin.PlayerNames.TryGetValue(text, out value) ? value : "Player"); int delta = Mathf.RoundToInt(valueLost); if (CheckoutStatsPlugin.IsMasterClient()) { CheckoutStatsPlugin.SyncComponent?.BroadcastStatDelta(text, playerName, "damage", delta); } else { StatsSync.ApplyDelta(text, playerName, "damage", delta); } } } catch { } } } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] public static class Patch_RunManager_ChangeLevel { [HarmonyPrefix] private static void Prefix() { if (SemiFunc.RunIsShop()) { CheckoutStatsPlugin.OpenMenuLibPopup(); } } } [HarmonyPatch(typeof(RunManager), "LeaveToMainMenu")] public static class Patch_RunManager_LeaveToMainMenu { [HarmonyPrefix] private static void Prefix() { CheckoutStatsPlugin.Logger.LogInfo((object)"[CheckoutStatsTW] 回主選單,清空所有統計資料"); CheckoutStatsPlugin.ClearAllStats(); } } [HarmonyPatch(typeof(RunManager), "RestartRun")] public static class Patch_RunManager_RestartRun { [HarmonyPrefix] private static void Prefix() { CheckoutStatsPlugin.Logger.LogInfo((object)"[CheckoutStatsTW] RestartRun,清空所有統計資料"); CheckoutStatsPlugin.ClearAllStats(); } } public class PluginInfo { public const string PLUGIN_GUID = "com.wuzuan.repo.checkoutstatstw"; public const string PLUGIN_NAME = "CheckoutStatsTW"; public const string PLUGIN_VERSION = "1.5.6"; } }