using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Pigeon.Movement; using Sparroh.UI; 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("Sparroh")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.0.1.0")] [assembly: AssemblyInformationalVersion("2.0.1")] [assembly: AssemblyProduct("Carnometer")] [assembly: AssemblyTitle("Carnometer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } public class CarnometerMod { private HudHandle damageHud; public Queue<(float time, float damage)> damageQueue = new Queue<(float, float)>(); private HudHandle killHud; public float missionStartTime; public int totalCoresKilled; public float totalDamage; public int totalKills; public static CarnometerMod Instance { get; private set; } public CarnometerMod() { Instance = this; missionStartTime = Time.time; } public void OnConfigChanged() { if (!ConfigManager.EnableDamageHUD.Value && HudHandle.IsValid(damageHud)) { DestroyHud(ref damageHud); } if (!ConfigManager.EnableKillHUD.Value && HudHandle.IsValid(killHud)) { DestroyHud(ref killHud); } UpdateHudVisibility(); } public void UpdateHudVisibility() { if (HudHandle.IsValid(damageHud)) { damageHud.SetActive(ConfigManager.EnableDamageHUD.Value); } if (HudHandle.IsValid(killHud)) { killHud.SetActive(ConfigManager.EnableKillHUD.Value); } } private void CreateDamageHud() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (damageHud != null && !damageHud.IsAlive) { damageHud = null; } if (!HudHandle.IsValid(damageHud)) { damageHud = HudBuilder.Create("CarnometerDamageHUD").ParentToReticle(true).Anchor(ConfigManager.DamageAnchors.XValue, ConfigManager.DamageAnchors.YValue) .Pivot(new Vector2(0f, 1f)) .Size(320f, 50f, true) .AddLines(2, -1f, (TextAlignmentOptions)513) .Build(); if (HudHandle.IsValid(damageHud)) { damageHud.EnableReposition("sparroh.carnometer.damage", "Damage HUD", ConfigManager.DamageAnchors); UpdateHudVisibility(); } } } private void CreateKillHud() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (killHud != null && !killHud.IsAlive) { killHud = null; } if (!HudHandle.IsValid(killHud)) { killHud = HudBuilder.Create("CarnometerKillHUD").ParentToReticle(true).Anchor(ConfigManager.KillAnchors.XValue, ConfigManager.KillAnchors.YValue) .Pivot(new Vector2(0f, 1f)) .Size(320f, 50f, true) .AddLines(2, -1f, (TextAlignmentOptions)513) .Build(); if (HudHandle.IsValid(killHud)) { killHud.EnableReposition("sparroh.carnometer.kill", "Kill HUD", ConfigManager.KillAnchors); UpdateHudVisibility(); } } } private static void DestroyHud(ref HudHandle hud) { if (hud != null) { if (hud.IsAlive) { hud.Destroy(); } hud = null; } } public void Update() { //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) bool value = ConfigManager.EnableDamageHUD.Value; bool value2 = ConfigManager.EnableKillHUD.Value; if ((!value && !value2) || (Object)(object)Player.LocalPlayer == (Object)null) { return; } if (value && !HudHandle.IsValid(damageHud)) { CreateDamageHud(); } if (value2 && !HudHandle.IsValid(killHud)) { CreateKillHud(); } float time = Time.time; float value3 = ConfigManager.DpsWindowSeconds.Value; float num = time - missionStartTime; if (value && HudHandle.IsValid(damageHud) && damageHud.Lines != null && damageHud.Lines.Length >= 2) { while (damageQueue.Count > 0 && damageQueue.Peek().time < time - value3) { damageQueue.Dequeue(); } float num2 = 0f; foreach (var item in damageQueue) { num2 += item.damage; } float num3 = ((damageQueue.Count > 0) ? (num2 / value3) : 0f); float num4 = ((num > 0f) ? (totalDamage / num) : 0f); Color value4 = ConfigManager.DamageColor.Value; damageHud.Lines[0].SetRichWithRate("Total Damage", totalDamage, num4, value4, "F0", "F1"); damageHud.Lines[1].Text = RichText.LabeledWithRate($"Last {value3:F0}sec Damage", num2, num3, value4, "F0", "F1"); } if (value2 && HudHandle.IsValid(killHud) && killHud.Lines != null && killHud.Lines.Length >= 2) { float num5 = ((num > 0f) ? ((float)totalKills / num) : 0f); float num6 = ((num > 0f) ? ((float)totalCoresKilled / num) : 0f); Color value5 = ConfigManager.KillColor.Value; killHud.Lines[0].SetRichWithRate("Targets Killed", (float)totalKills, num5, value5, "F0", "F1"); killHud.Lines[1].SetRichWithRate("Cores Killed", (float)totalCoresKilled, num6, value5, "F0", "F1"); } } public void OnDestroy() { DestroyHud(ref damageHud); DestroyHud(ref killHud); } } public static class ConfigManager { private const float DebounceSeconds = 0.25f; private static ConfigFile config; private static ManualLogSource logger; private static FileSystemWatcher configWatcher; private static volatile bool pendingRefresh; private static volatile bool reloadPending; private static float lastReloadTime; public static ConfigEntry EnableDamageHUD { get; private set; } public static ConfigEntry EnableKillHUD { get; private set; } public static ConfigEntry DpsWindowSeconds { get; private set; } public static HudAnchors DamageAnchors { get; private set; } public static HudAnchors KillAnchors { get; private set; } public static ConfigColor DamageColor { get; private set; } public static ConfigColor KillColor { get; private set; } public static void Initialize(ConfigFile configFile, ManualLogSource log) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) config = configFile; logger = log; EnableDamageHUD = config.Bind("General", "Enable Damage HUD", true, "Enable or disable the damage meter HUD."); EnableKillHUD = config.Bind("General", "Enable Kill HUD", true, "Enable or disable the kill tracker HUD."); DpsWindowSeconds = config.Bind("General", "DPS Window", 5f, "Time window (in seconds) for calculating DPS. Higher values smooth out spikes."); DamageAnchors = HudAnchors.Bind(config, "DamageHUD", 0.294803f, 0.285045f, "HUD Positioning"); KillAnchors = HudAnchors.Bind(config, "KillHUD", 0.294803f, 0.225045f, "HUD Positioning"); DamageColor = ConfigColor.Bind(config, "Colors", "Damage HUD Color", UIColors.Rose, "Rich-text value color for the damage meter (hex RRGGBB or #RRGGBB)."); KillColor = ConfigColor.Bind(config, "Colors", "Kill HUD Color", UIColors.Rose, "Rich-text value color for the kill tracker (hex RRGGBB or #RRGGBB)."); EnableDamageHUD.SettingChanged += OnSettingChanged; EnableKillHUD.SettingChanged += OnSettingChanged; try { SetupFileWatcher(); } catch (Exception ex) { logger.LogError((object)("Error setting up config file watcher: " + ex.Message)); } } public static void Tick() { if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f) { return; } reloadPending = false; lastReloadTime = Time.unscaledTime; try { config.Reload(); pendingRefresh = true; logger.LogInfo((object)"Config reloaded from disk."); } catch (Exception ex) { logger.LogError((object)("Error reloading config: " + ex.Message)); } } public static bool ConsumePendingRefresh() { if (!pendingRefresh) { return false; } pendingRefresh = false; return true; } public static void Dispose() { if (EnableDamageHUD != null) { EnableDamageHUD.SettingChanged -= OnSettingChanged; } if (EnableKillHUD != null) { EnableKillHUD.SettingChanged -= OnSettingChanged; } if (configWatcher != null) { configWatcher.EnableRaisingEvents = false; configWatcher.Changed -= OnConfigFileChanged; configWatcher.Created -= OnConfigFileChanged; configWatcher.Renamed -= OnConfigFileChanged; configWatcher.Dispose(); configWatcher = null; } } private static void SetupFileWatcher() { configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.carnometer.cfg"); configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; configWatcher.Changed += OnConfigFileChanged; configWatcher.Created += OnConfigFileChanged; configWatcher.Renamed += OnConfigFileChanged; configWatcher.EnableRaisingEvents = true; } private static void OnConfigFileChanged(object sender, FileSystemEventArgs e) { reloadPending = true; } private static void OnSettingChanged(object sender, EventArgs e) { pendingRefresh = true; } } [HarmonyPatch] public static class DPSPatches { [HarmonyPrefix] [HarmonyPatch(typeof(PlayerData), "OnLocalPlayerDamageTarget")] private static bool PrefixDamage(in DamageCallbackData data) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (CarnometerMod.Instance == null) { return true; } if (data.damageData.damage > 0f) { CarnometerMod.Instance.totalDamage += data.damageData.damage; TargetType type = ((IFollowable)data.target).Type; if ((type & 1) != 0 || (type & 2) != 0) { CarnometerMod.Instance.damageQueue.Enqueue((Time.time, data.damageData.damage)); } if (((DamageCallbackData)(ref data)).KilledTarget) { CarnometerMod.Instance.totalKills++; if (typeof(EnemyCore).IsAssignableFrom(((object)data.target).GetType())) { CarnometerMod.Instance.totalCoresKilled++; } } } return true; } } [HarmonyPatch] public static class MissionPatches { [HarmonyPrefix] [HarmonyPatch(typeof(MissionManager), "SpawnHUD")] private static void MissionStart() { if (CarnometerMod.Instance != null) { CarnometerMod.Instance.totalDamage = 0f; CarnometerMod.Instance.totalKills = 0; CarnometerMod.Instance.totalCoresKilled = 0; CarnometerMod.Instance.damageQueue.Clear(); CarnometerMod.Instance.missionStartTime = Time.time; } } } [BepInPlugin("sparroh.carnometer", "Carnometer", "2.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [MycoMod(/*Could not decode attribute arguments.*/)] public class SparrohPlugin : BaseUnityPlugin { public const string PluginGUID = "sparroh.carnometer"; public const string PluginName = "Carnometer"; public const string PluginVersion = "2.1.0"; internal static ManualLogSource Logger; private CarnometerMod carnometer; private Harmony harmony; private void Awake() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; try { ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger); } catch (Exception ex) { Logger.LogError((object)("Failed to initialize config: " + ex.Message)); return; } try { harmony = new Harmony("sparroh.carnometer"); } catch (Exception ex2) { Logger.LogError((object)("Failed to create Harmony instance: " + ex2.Message)); return; } try { carnometer = new CarnometerMod(); } catch (Exception ex3) { Logger.LogError((object)("Failed to initialize Carnometer: " + ex3.Message)); } try { harmony.PatchAll(); } catch (Exception ex4) { Logger.LogError((object)("Failed to apply Harmony patches: " + ex4.Message)); } Logger.LogInfo((object)"Carnometer loaded successfully."); } private void Update() { try { ConfigManager.Tick(); if (ConfigManager.ConsumePendingRefresh() && carnometer != null) { carnometer.OnConfigChanged(); } if (carnometer != null) { carnometer.UpdateHudVisibility(); } if (carnometer != null) { carnometer.Update(); } } catch (Exception ex) { Logger.LogError((object)("Error in Carnometer.Update(): " + ex.Message)); } } private void OnDestroy() { try { if (carnometer != null) { carnometer.OnDestroy(); } } catch (Exception ex) { Logger.LogError((object)("Error in Carnometer.OnDestroy(): " + ex.Message)); } try { ConfigManager.Dispose(); } catch (Exception ex2) { Logger.LogError((object)("Error disposing config: " + ex2.Message)); } try { if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex3) { Logger.LogError((object)("Error unpatching Harmony: " + ex3.Message)); } } } namespace Carnometer { public static class MyPluginInfo { public const string PLUGIN_GUID = "Carnometer"; public const string PLUGIN_NAME = "Carnometer"; public const string PLUGIN_VERSION = "2.0.1"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }