using System; using System.Collections; 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 Microsoft.CodeAnalysis; 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: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("headclef")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1+5bb025f820942f9fe51d19886e2fd1dfc851b172")] [assembly: AssemblyProduct("Character Stats")] [assembly: AssemblyTitle("Character Stats")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 Character_Stats { [BepInPlugin("headclef.CharacterStats", "Character Stats", "1.2.1")] public class Character_Stats : BaseUnityPlugin { private const string PluginGuid = "headclef.CharacterStats"; private const string PluginName = "Character Stats"; private const string PluginVersion = "1.2.1"; private static readonly Dictionary> _playerStats = new Dictionary>(); private static bool _statsReady; private static readonly Dictionary> _tempBonuses = new Dictionary>(); private static readonly (string Key, string Field)[] _statKeyMap = new(string, string)[13] { ("Health", "playerUpgradeHealth"), ("Stamina", "playerUpgradeStamina"), ("Speed", "playerUpgradeSpeed"), ("Strength", "playerUpgradeStrength"), ("Range", "playerUpgradeRange"), ("Throw", "playerUpgradeThrow"), ("Extra Jump", "playerUpgradeExtraJump"), ("Launch", "playerUpgradeLaunch"), ("Crouch Rest", "playerUpgradeCrouchRest"), ("Map Player Count", "playerUpgradeMapPlayerCount"), ("Tumble Climb", "playerUpgradeTumbleClimb"), ("Tumble Wings", "playerUpgradeTumbleWings"), ("Death Head Battery", "playerUpgradeDeathHeadBattery") }; private static readonly Dictionary _dictFieldCache = new Dictionary(); internal static Character_Stats Instance { get; private set; } = null; internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } public static bool AreStatsReady => _statsReady; private void Awake() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0049: Expected O, but got Unknown Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); } private void OnDestroy() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static int GetUpgradeLevel(string steamId, string upgradeKey) { int num = 0; if (_playerStats.TryGetValue(steamId, out Dictionary value) && value.TryGetValue(upgradeKey, out var value2)) { num = value2; } return num + GetTemporaryBonus(steamId, upgradeKey); } public static Dictionary GetAllUpgrades(string steamId) { Dictionary dictionary = new Dictionary(); if (_playerStats.TryGetValue(steamId, out Dictionary value)) { foreach (KeyValuePair item in value) { dictionary[item.Key] = item.Value; } } if (_tempBonuses.TryGetValue(steamId, out Dictionary value2)) { foreach (KeyValuePair item2 in value2) { dictionary[item2.Key] = (dictionary.TryGetValue(item2.Key, out var value3) ? value3 : 0) + item2.Value; } } return dictionary; } public static void SetTemporaryBonus(string steamId, string upgradeKey, int amount) { if (string.IsNullOrEmpty(steamId) || string.IsNullOrEmpty(upgradeKey)) { return; } if (amount == 0) { ClearTemporaryBonus(steamId, upgradeKey); return; } if (!_tempBonuses.TryGetValue(steamId, out Dictionary value)) { value = new Dictionary(); _tempBonuses[steamId] = value; } value[upgradeKey] = amount; } public static void ClearTemporaryBonus(string steamId, string upgradeKey) { if (_tempBonuses.TryGetValue(steamId, out Dictionary value)) { value.Remove(upgradeKey); if (value.Count == 0) { _tempBonuses.Remove(steamId); } } } public static void ClearAllTemporaryBonuses(string steamId) { _tempBonuses.Remove(steamId); } public static int GetTemporaryBonus(string steamId, string upgradeKey) { if (_tempBonuses.TryGetValue(steamId, out Dictionary value) && value.TryGetValue(upgradeKey, out var value2)) { return value2; } return 0; } public static IEnumerable GetTrackedPlayers() { return _playerStats.Keys; } public static string? GetLocalSteamId() { return PlayerController.instance?.playerSteamID; } private static Dictionary? GetStatDict(string fieldName) { if (!_dictFieldCache.TryGetValue(fieldName, out FieldInfo value)) { value = typeof(StatsManager).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _dictFieldCache[fieldName] = value; } return value?.GetValue(StatsManager.instance) as Dictionary; } internal static void RefreshStats() { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { return; } List list = SemiFunc.PlayerGetAll(); if (list == null || list.Count == 0) { return; } Dictionary> dictionary = new Dictionary>(); foreach (PlayerAvatar item3 in list) { string text = SemiFunc.PlayerGetSteamID(item3); if (string.IsNullOrEmpty(text)) { continue; } Dictionary dictionary2 = new Dictionary(); try { Dictionary dictionary3 = instance.FetchPlayerUpgrades(text); if (dictionary3 != null) { foreach (KeyValuePair item4 in dictionary3) { dictionary2[item4.Key] = item4.Value; } } } catch (Exception ex) { Logger.LogDebug((object)("FetchPlayerUpgrades threw for " + text + ": " + ex.Message)); } (string, string)[] statKeyMap = _statKeyMap; for (int i = 0; i < statKeyMap.Length; i++) { (string, string) tuple = statKeyMap[i]; string item = tuple.Item1; string item2 = tuple.Item2; Dictionary statDict = GetStatDict(item2); if (statDict != null && statDict.TryGetValue(text, out var value)) { dictionary2[item] = value; } } dictionary[text] = dictionary2; } if (dictionary.Count == 0) { return; } _playerStats.Clear(); foreach (KeyValuePair> item5 in dictionary) { _playerStats[item5.Key] = item5.Value; } _statsReady = true; } internal static void ClearStats() { _playerStats.Clear(); _statsReady = false; } } } namespace Character_Stats.Patches { [HarmonyPatch] internal static class StatReaderPatch { private static Coroutine? _refreshLoop; [HarmonyPatch(typeof(GameDirector), "Start")] [HarmonyPostfix] private static void GameDirector_Start_Postfix() { if (_refreshLoop != null) { ((MonoBehaviour)Character_Stats.Instance).StopCoroutine(_refreshLoop); } _refreshLoop = ((MonoBehaviour)Character_Stats.Instance).StartCoroutine(ReadStatsDelayed()); } private static IEnumerator ReadStatsDelayed() { while (!SemiFunc.LevelGenDone()) { yield return (object)new WaitForSeconds(0.5f); } if (SemiFunc.RunIsLevel()) { yield return (object)new WaitForSeconds(1f); while (SemiFunc.RunIsLevel()) { Character_Stats.RefreshStats(); yield return (object)new WaitForSeconds(1f); } _refreshLoop = null; } } [HarmonyPatch(typeof(PunManager), "ReceiveSyncData")] [HarmonyPostfix] private static void ReceiveSyncData_Postfix(bool finalChunk) { if (finalChunk) { ((MonoBehaviour)Character_Stats.Instance).StartCoroutine(ReadStatsAfterSync()); } } private static IEnumerator ReadStatsAfterSync() { yield return null; yield return null; yield return null; yield return (object)new WaitForSeconds(1f); Character_Stats.RefreshStats(); } [HarmonyPatch(typeof(RunManager), "ResetProgress")] [HarmonyPostfix] private static void RunManager_ResetProgress_Postfix() { Character_Stats.ClearStats(); } } }