Decompiled source of DisplayGunStats v1.4.2

BepInEx/plugins/DisplayGunStats.dll

Decompiled a month ago
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;
using UnityEngine.UI;

[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("1.4.2.0")]
[assembly: AssemblyInformationalVersion("1.4.2")]
[assembly: AssemblyProduct("DisplayGunStats")]
[assembly: AssemblyTitle("DisplayGunStats")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.4.2.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 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<bool> EnableMenuWindow { get; private set; }

	public static ConfigEntry<bool> EnableGunStatsHUD { get; private set; }

	public static ConfigEntry<bool> ShowLastShotDamage { get; private set; }

	public static ConfigEntry<bool> PredictiveModifyBulletData { get; private set; }

	public static HudAnchors Anchors { get; private set; }

	public static ConfigColor DamageColor { get; private set; }

	public static ConfigColor FireRateColor { get; private set; }

	public static ConfigColor AmmoColor { get; private set; }

	public static ConfigColor ExplosionColor { get; private set; }

	public static ConfigColor RangeColor { get; private set; }

	public static ConfigColor DefaultColor { get; private set; }

	public static void Initialize(ConfigFile configFile, ManualLogSource log)
	{
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		config = configFile;
		logger = log;
		EnableMenuWindow = config.Bind<bool>("General", "Enable Menu Window", true, "Show gun stats window when editing weapons in Gear Details");
		EnableGunStatsHUD = config.Bind<bool>("General", "Enable Gun HUD", true, "If true, the gun stats HUD will be displayed.");
		ShowLastShotDamage = config.Bind<bool>("General", "Show Last Shot", true, "If true, show last-shot bullet damage next to the effective (predicted) bullet damage on the HUD.");
		PredictiveModifyBulletData = config.Bind<bool>("General", "Predictive Bullet Data", false, "If true, run gun.ModifyBulletData for live conditional damage prediction. Default false uses sheet GunData + last-shot only (safe vs Home Cooking self-damage). When true, best-effort damage guards run during prediction; residual risk remains if a path bypasses them.");
		Anchors = HudAnchors.BindKeys(config, "Display X", "Display Y", 0.03123011f, 0.9360327f, "HUD Positioning", "GunStatsHud");
		DamageColor = ConfigColor.Bind(config, "Colors", "Damage Color", UIColors.Rose, "Rich-text color for Damage (hex RRGGBB or #RRGGBB).");
		FireRateColor = ConfigColor.Bind(config, "Colors", "Fire Rate Color", UIColors.Macaroon, "Rich-text color for Fire Rate, Burst, Bullets per Shot, Fire Mode (hex RRGGBB or #RRGGBB).");
		AmmoColor = ConfigColor.Bind(config, "Colors", "Ammo Color", UIColors.Sky, "Rich-text color for Magazine/Ammo, Reload, Charge (hex RRGGBB or #RRGGBB).");
		ExplosionColor = ConfigColor.Bind(config, "Colors", "Explosion Color", UIColors.Orchid, "Rich-text color for Explosion Size (hex RRGGBB or #RRGGBB).");
		RangeColor = ConfigColor.Bind(config, "Colors", "Range Color", UIColors.Shamrock, "Rich-text color for Range, Recoil, Spread (hex RRGGBB or #RRGGBB).");
		DefaultColor = ConfigColor.Bind(config, "Colors", "Default Color", UIColors.TextPrimary, "Rich-text fallback color for other stats (hex RRGGBB or #RRGGBB).");
		EnableMenuWindow.SettingChanged += OnSettingChanged;
		EnableGunStatsHUD.SettingChanged += OnSettingChanged;
		ShowLastShotDamage.SettingChanged += OnSettingChanged;
		PredictiveModifyBulletData.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 (IOException)
		{
			reloadPending = true;
		}
		catch (Exception ex2)
		{
			logger.LogError((object)("Error reloading config: " + ex2.Message));
		}
	}

	public static bool ConsumePendingRefresh()
	{
		if (!pendingRefresh)
		{
			return false;
		}
		pendingRefresh = false;
		return true;
	}

	public static void Dispose()
	{
		if (EnableMenuWindow != null)
		{
			EnableMenuWindow.SettingChanged -= OnSettingChanged;
		}
		if (EnableGunStatsHUD != null)
		{
			EnableGunStatsHUD.SettingChanged -= OnSettingChanged;
		}
		if (ShowLastShotDamage != null)
		{
			ShowLastShotDamage.SettingChanged -= OnSettingChanged;
		}
		if (PredictiveModifyBulletData != null)
		{
			PredictiveModifyBulletData.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.displaygunstats.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;
	}
}
public class GearDetailsStats
{
	private const int MaxLines = 32;

	private const float UpdateInterval = 0.5f;

	private const string BarId = "gun_stats";

	private readonly List<UIText> _lineTexts = new List<UIText>();

	private UIWindow _window;

	private IWeapon currentWeapon;

	private GearDetailsWindow currentWindow;

	private bool enableGearDetailsStatsWindow;

	private bool inWeaponDetails;

	private bool statsPanelOpen;

	private float updateTimer;

	public GearDetailsStats(bool enable)
	{
		enableGearDetailsStatsWindow = enable;
	}

	public void SetEnable(bool enable)
	{
		enableGearDetailsStatsWindow = enable;
		if (!enable)
		{
			TearDownUi();
		}
	}

	public void Update()
	{
		if (!enableGearDetailsStatsWindow)
		{
			TearDownUi();
			return;
		}
		if (!GearActionBar.IsGearMenuOpen())
		{
			LeaveWeaponDetails();
			return;
		}
		GearActionBar.Register("gun_stats", statsPanelOpen ? "Hide Stats" : "Gun Stats", 150, (Action)OnGunStatsClicked, (UIButtonStyle)1);
		ResolveWeaponContext(out var weaponDetails, out var weapon, out var gearWindow);
		if (!weaponDetails)
		{
			LeaveWeaponDetails();
			GearActionBar.SetSlotVisible("gun_stats", false);
			return;
		}
		bool num = !inWeaponDetails || currentWeapon != weapon;
		inWeaponDetails = true;
		currentWeapon = weapon;
		currentWindow = gearWindow;
		GearActionBar.SetSlotVisible("gun_stats", true);
		GearActionBar.SetText("gun_stats", statsPanelOpen ? "Hide Stats" : "Gun Stats");
		if (num)
		{
			statsPanelOpen = false;
			updateTimer = 0f;
			HideStatsPanel();
		}
		if (statsPanelOpen)
		{
			EnsureStatsWindow();
			updateTimer += Time.deltaTime;
			if (updateTimer >= 0.5f)
			{
				updateTimer = 0f;
				UpdateGearDetailsStats();
			}
		}
	}

	private static void ResolveWeaponContext(out bool weaponDetails, out IWeapon weapon, out GearDetailsWindow gearWindow)
	{
		weaponDetails = false;
		weapon = null;
		gearWindow = null;
		try
		{
			if ((Object)(object)Menu.Instance == (Object)null || !LevelData.CanModifyGear)
			{
				return;
			}
			WindowSystem windowSystem = Menu.Instance.WindowSystem;
			Window obj = ((windowSystem != null) ? windowSystem.GetTop() : null);
			GearDetailsWindow val = (GearDetailsWindow)(object)((obj is GearDetailsWindow) ? obj : null);
			if (val != null && !val.InSkinMode)
			{
				IUpgradable upgradablePrefab = val.UpgradablePrefab;
				IWeapon val2 = (IWeapon)(object)((upgradablePrefab is IWeapon) ? upgradablePrefab : null);
				if (val2 != null)
				{
					weaponDetails = true;
					gearWindow = val;
					weapon = val2;
				}
			}
		}
		catch
		{
		}
	}

	private void LeaveWeaponDetails()
	{
		if (inWeaponDetails || currentWeapon != null)
		{
			inWeaponDetails = false;
			currentWeapon = null;
			currentWindow = null;
			statsPanelOpen = false;
			HideStatsPanel();
			GearActionBar.SetSlotVisible("gun_stats", false);
		}
	}

	private void OnGunStatsClicked()
	{
		if (!inWeaponDetails || currentWeapon == null)
		{
			return;
		}
		statsPanelOpen = !statsPanelOpen;
		GearActionBar.SetText("gun_stats", statsPanelOpen ? "Hide Stats" : "Gun Stats");
		if (statsPanelOpen)
		{
			EnsureStatsWindow();
			updateTimer = 0f;
			UpdateGearDetailsStats();
			UIWindow window = _window;
			if (window != null)
			{
				window.Show();
			}
		}
		else
		{
			HideStatsPanel();
		}
	}

	private void EnsureStatsWindow()
	{
		//IL_0046: 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)
		if (_window != null && (Object)(object)_window.GameObject == (Object)null)
		{
			_window = null;
			_lineTexts.Clear();
		}
		if (_window == null)
		{
			_window = UIWindow.Create("GunStatsPreview", (Vector2?)new Vector2(360f, 480f), "Gun Stats Preview", true, true, (int?)(UITheme.WindowSortingOrder + 2));
			NeutralizeModalBackdrop(_window);
			_window.OnClose((Action)delegate
			{
				statsPanelOpen = false;
				GearActionBar.SetText("gun_stats", "Gun Stats");
			});
			Transform content = _window.Content;
			_lineTexts.Clear();
			for (int num = 0; num < 32; num++)
			{
				UIText val = UIText.Create(content, $"Line{num}", "", (num == 0) ? UITheme.ScaledFontHeader : UITheme.ScaledFontBody, (Color?)UIColors.TextPrimary, (TextAlignmentOptions)513, true);
				GameObject gameObject = val.GameObject;
				float? num2 = UITheme.S((num == 0) ? 28f : 20f);
				UIHelpers.EnsureLayoutElement(gameObject, (float?)null, num2, (float?)null);
				_lineTexts.Add(val);
			}
			_window.Hide(false);
		}
	}

	private static void NeutralizeModalBackdrop(UIWindow window)
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)((window != null) ? window.GameObject : null) == (Object)null)
		{
			return;
		}
		try
		{
			Transform parent = window.GameObject.transform.parent;
			if ((Object)(object)parent == (Object)null)
			{
				return;
			}
			for (int i = 0; i < parent.childCount; i++)
			{
				Transform child = parent.GetChild(i);
				if (!((Object)(object)child == (Object)null) && !(((Object)child).name != "Backdrop"))
				{
					Image component = ((Component)child).GetComponent<Image>();
					if ((Object)(object)component != (Object)null)
					{
						((Graphic)component).raycastTarget = false;
						((Graphic)component).color = Color.clear;
					}
					((Component)child).gameObject.SetActive(false);
					break;
				}
			}
		}
		catch
		{
		}
	}

	private void HideStatsPanel()
	{
		if (_window != null)
		{
			_window.Hide(false);
		}
	}

	private void UpdateGearDetailsStats()
	{
		if (currentWeapon != null && _lineTexts.Count != 0)
		{
			GunStatSnapshot gunStatSnapshot = GunStatSnapshot.CapturePreview(currentWeapon, currentWindow);
			List<string> list = new List<string>(32);
			if (gunStatSnapshot.IsValid)
			{
				gunStatSnapshot.AppendLines(list, GunStatColors.FromTheme(), showLastShotDamage: false);
			}
			else
			{
				list.Add("Weapon Stats Preview:");
				list.Add("Unable to read weapon stats.");
			}
			for (int i = 0; i < _lineTexts.Count; i++)
			{
				_lineTexts[i].Text = ((i < list.Count) ? list[i] : "");
			}
		}
	}

	private void TearDownUi()
	{
		LeaveWeaponDetails();
		if (_window != null)
		{
			_window.Destroy();
			_window = null;
		}
		_lineTexts.Clear();
		GearActionBar.Unregister("gun_stats");
	}

	public void Destroy()
	{
		TearDownUi();
	}
}
public readonly struct GunStatColors
{
	public readonly Color Damage;

	public readonly Color FireRate;

	public readonly Color Ammo;

	public readonly Color Explosion;

	public readonly Color Range;

	public readonly Color Default;

	public GunStatColors(Color damage, Color fireRate, Color ammo, Color explosion, Color range, Color defaultColor)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		Damage = damage;
		FireRate = fireRate;
		Ammo = ammo;
		Explosion = explosion;
		Range = range;
		Default = defaultColor;
	}

	public static GunStatColors FromTheme()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		return new GunStatColors(UIColors.Rose, UIColors.Macaroon, UIColors.Sky, UIColors.Orchid, UIColors.Shamrock, UIColors.TextPrimary);
	}

	public static GunStatColors FromConfig()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		return new GunStatColors(ConfigManager.DamageColor.Value, ConfigManager.FireRateColor.Value, ConfigManager.AmmoColor.Value, ConfigManager.ExplosionColor.Value, ConfigManager.RangeColor.Value, ConfigManager.DefaultColor.Value);
	}
}
public static class GunStatPreview
{
	private static FieldInfo equipSlotsField;

	private static bool equipSlotsFieldResolved;

	public static GunStatSnapshot Capture(IWeapon weapon, GearDetailsWindow window)
	{
		if (weapon == null)
		{
			return GunStatSnapshot.Invalid;
		}
		try
		{
			List<UpgradeInstance> list = CollectEquippedUpgrades(window);
			list.Sort(CompareUpgradePriority);
			if (TryCaptureOnClone(weapon, list, out var snapshot))
			{
				return snapshot;
			}
			DisplayGunStatsPlugin.Logger.LogWarning((object)"GunStatPreview.Capture: clone failed; using guarded in-place preview.");
			return CaptureInPlace(weapon, list);
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)("GunStatPreview.Capture failed: " + ex.Message));
			return GunStatSnapshot.Invalid;
		}
	}

	private static bool TryCaptureOnClone(IWeapon sourceWeapon, List<UpgradeInstance> upgrades, out GunStatSnapshot snapshot)
	{
		snapshot = GunStatSnapshot.Invalid;
		Component val = (Component)(object)((sourceWeapon is Component) ? sourceWeapon : null);
		if (val == null || (Object)(object)val == (Object)null)
		{
			return false;
		}
		GameObject gameObject = val.gameObject;
		if ((Object)(object)gameObject == (Object)null)
		{
			return false;
		}
		GameObject val2 = null;
		try
		{
			val2 = Object.Instantiate<GameObject>(gameObject);
			((Object)val2).name = ((Object)gameObject).name + "_DisplayGunStatsPreview";
			((Object)val2).hideFlags = (HideFlags)61;
			val2.SetActive(false);
			IWeapon val3 = FindWeaponComponent(val2);
			if (val3 == null)
			{
				return false;
			}
			IGear val4 = (IGear)(object)val3;
			if (val4 != null)
			{
				IUpgradable val5 = null;
				if (sourceWeapon != null)
				{
					val5 = ((IUpgradable)sourceWeapon).GetPrefab();
				}
				((IUpgradable)val4).Prefab = (IUpgradable)(((object)val5) ?? ((object)sourceWeapon));
			}
			ApplyPrefabBaseline(val3, sourceWeapon);
			ApplyUpgradesForPreview(val3, upgrades);
			snapshot = GunStatSnapshot.BuildFromWeapon(val3, "Weapon Stats Preview:");
			return snapshot.IsValid;
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)("TryCaptureOnClone failed: " + ex.Message));
			snapshot = GunStatSnapshot.Invalid;
			return false;
		}
		finally
		{
			if ((Object)(object)val2 != (Object)null)
			{
				try
				{
					Object.Destroy((Object)(object)val2);
				}
				catch (Exception ex2)
				{
					DisplayGunStatsPlugin.Logger.LogWarning((object)("Failed to destroy preview clone: " + ex2.Message));
				}
			}
		}
	}

	private static GunStatSnapshot CaptureInPlace(IWeapon weapon, List<UpgradeInstance> upgrades)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
		GunData gunData = weapon.GunData;
		GearUpgradeFlags? val = null;
		if (weapon != null)
		{
			val = ((IGear)weapon).UpgradeFlags;
		}
		try
		{
			ApplyPrefabBaseline(weapon, weapon);
			ApplyUpgradesForPreview(weapon, upgrades);
			return GunStatSnapshot.BuildFromWeapon(weapon, "Weapon Stats Preview:");
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)("CaptureInPlace failed: " + ex.Message));
			return GunStatSnapshot.Invalid;
		}
		finally
		{
			try
			{
				weapon.GunData = gunData;
			}
			catch (Exception ex2)
			{
				DisplayGunStatsPlugin.Logger.LogError((object)("CRITICAL: failed to restore GunData after in-place preview — prefab may be corrupted until restart: " + ex2.Message));
			}
			if (val.HasValue)
			{
				if (weapon != null)
				{
					try
					{
						((IGear)weapon).UpgradeFlags = val.Value;
					}
					catch (Exception ex3)
					{
						DisplayGunStatsPlugin.Logger.LogWarning((object)("Failed to restore UpgradeFlags after preview: " + ex3.Message));
					}
				}
			}
		}
	}

	private static void ApplyUpgradesForPreview(IWeapon weapon, List<UpgradeInstance> upgrades)
	{
		if (upgrades == null)
		{
			return;
		}
		if (weapon == null)
		{
			return;
		}
		foreach (UpgradeInstance upgrade in upgrades)
		{
			try
			{
				Upgrade obj = ((upgrade != null) ? upgrade.Upgrade : null);
				GearUpgrade val = (GearUpgrade)(object)((obj is GearUpgrade) ? obj : null);
				if (val != null)
				{
					val.Apply((IGear)(object)weapon, upgrade);
				}
			}
			catch (Exception ex)
			{
				string text = (((Object)(object)((upgrade != null) ? upgrade.Upgrade : null) != (Object)null) ? upgrade.Upgrade.Name : "?");
				DisplayGunStatsPlugin.Logger.LogWarning((object)("Error applying upgrade " + text + " on preview clone: " + ex.Message));
			}
		}
	}

	private static IWeapon FindWeaponComponent(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return null;
		}
		Gun component = go.GetComponent<Gun>();
		if ((Object)(object)component != (Object)null)
		{
			return (IWeapon)(object)component;
		}
		component = go.GetComponentInChildren<Gun>(true);
		if ((Object)(object)component != (Object)null)
		{
			return (IWeapon)(object)component;
		}
		MonoBehaviour[] componentsInChildren = go.GetComponentsInChildren<MonoBehaviour>(true);
		foreach (MonoBehaviour obj in componentsInChildren)
		{
			IWeapon val = (IWeapon)(object)((obj is IWeapon) ? obj : null);
			if (val != null)
			{
				return val;
			}
		}
		return null;
	}

	private static void ApplyPrefabBaseline(IWeapon target, IWeapon sourceForPrefabLookup)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		GunData baselineGunData = GetBaselineGunData(sourceForPrefabLookup);
		Transform firePoint = target.GunData.firePoint;
		baselineGunData.firePoint = firePoint;
		target.GunData = baselineGunData;
	}

	private static GunData GetBaselineGunData(IWeapon weapon)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		if (weapon != null)
		{
			IUpgradable prefab = ((IUpgradable)weapon).GetPrefab();
			IWeapon val = (IWeapon)(object)((prefab is IWeapon) ? prefab : null);
			if (val != null)
			{
				return val.GunData;
			}
		}
		return weapon.GunData;
	}

	private static List<UpgradeInstance> CollectEquippedUpgrades(GearDetailsWindow window)
	{
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		List<UpgradeInstance> list = new List<UpgradeInstance>();
		if ((Object)(object)window == (Object)null)
		{
			return list;
		}
		try
		{
			if (!equipSlotsFieldResolved)
			{
				equipSlotsField = typeof(GearDetailsWindow).GetField("equipSlots", BindingFlags.Instance | BindingFlags.NonPublic);
				equipSlotsFieldResolved = true;
			}
			object? obj = equipSlotsField?.GetValue(window);
			ModuleEquipSlots val = (ModuleEquipSlots)((obj is ModuleEquipSlots) ? obj : null);
			if (val == null)
			{
				return list;
			}
			HexMap hexMap = val.HexMap;
			if (hexMap == null)
			{
				return list;
			}
			for (int i = 0; i < hexMap.Width; i++)
			{
				for (int j = 0; j < hexMap.Height; j++)
				{
					Node val2 = hexMap[i, j];
					if (val2.enabled && val2.upgrade != null && !list.Contains(val2.upgrade))
					{
						list.Add(val2.upgrade);
					}
				}
			}
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)("CollectEquippedUpgrades failed: " + ex.Message));
		}
		return list;
	}

	private static int CompareUpgradePriority(UpgradeInstance a, UpgradeInstance b)
	{
		Upgrade val = ((a != null) ? a.Upgrade : null);
		Upgrade val2 = ((b != null) ? b.Upgrade : null);
		if ((Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null)
		{
			return 0;
		}
		if ((Object)(object)val == (Object)null)
		{
			return 1;
		}
		if ((Object)(object)val2 == (Object)null)
		{
			return -1;
		}
		return val.CompareTo(val2);
	}
}
public class GunStatsHud
{
	private const float UpdateInterval = 0.5f;

	private const int NUM_STAT_LINES = 28;

	private static bool loggedUpdateError;

	public static Gun currentGun;

	public static float currentGunActualDamage;

	public static bool hasLastShotDamage;

	public static bool SuppressLastShotCapture;

	public static bool IsPredictingStats;

	private static bool damageGuardsReady;

	private static FieldInfo playerField;

	private static PropertyInfo activeProp;

	private HudHandle hud;

	private float updateTimer;

	public static bool AllowPredictiveModifyBulletData { get; private set; }

	private bool IsHudAlive
	{
		get
		{
			if (HudHandle.IsValid(hud) && hud.Lines != null)
			{
				return hud.Lines.Length != 0;
			}
			return false;
		}
	}

	private static bool CanAttachHud
	{
		get
		{
			if ((Object)(object)Player.LocalPlayer != (Object)null && (Object)(object)Player.LocalPlayer.PlayerLook != (Object)null)
			{
				return (Object)(object)Player.LocalPlayer.PlayerLook.Reticle != (Object)null;
			}
			return false;
		}
	}

	public bool IsActive
	{
		get
		{
			if (IsHudAlive)
			{
				return hud.IsActive;
			}
			return false;
		}
	}

	public Vector2 GetSize
	{
		get
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (!IsHudAlive)
			{
				return Vector2.zero;
			}
			return hud.Size;
		}
	}

	public GunStatsHud(Harmony harmony)
	{
		try
		{
			playerField = typeof(Gun).GetField("player", BindingFlags.Instance | BindingFlags.NonPublic);
			activeProp = typeof(IGear).GetProperty("Active");
			GunStatsPatches.Apply(harmony);
			damageGuardsReady = PredictDamageGuards.Install(harmony);
			RefreshPredictionGate();
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogError((object)("Failed to initialize GunStatsHud: " + ex.Message));
			AllowPredictiveModifyBulletData = false;
		}
	}

	public void RefreshPredictionGate()
	{
		bool flag = (AllowPredictiveModifyBulletData = ConfigManager.PredictiveModifyBulletData != null && ConfigManager.PredictiveModifyBulletData.Value);
		DisplayGunStatsPlugin.Logger.LogInfo((object)($"PredictiveModifyBulletData cfg={flag} → active={AllowPredictiveModifyBulletData}, " + "guards=" + (damageGuardsReady ? "ready" : "missing")));
		if (flag && !damageGuardsReady)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)"PredictiveModifyBulletData is ON but damage guards did not fully attach — prediction will run; chance upgrades may still self-damage.");
		}
	}

	public void UpdateHudVisibility()
	{
		if (!IsHudAlive)
		{
			ClearDestroyedHud();
		}
		else
		{
			hud.SetActive(ConfigManager.EnableGunStatsHUD.Value);
		}
	}

	private void ClearDestroyedHud()
	{
		if (hud != null)
		{
			hud = null;
		}
	}

	private void CreateGunStatsHUD()
	{
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		if (IsHudAlive)
		{
			return;
		}
		ClearDestroyedHud();
		if (!CanAttachHud)
		{
			return;
		}
		hud = HudBuilder.Create("GunStatsHUD").ParentToReticle(true).Anchor(ConfigManager.Anchors.XValue, ConfigManager.Anchors.YValue)
			.Pivot(new Vector2(0f, 1f))
			.Size(350f, 460f, true)
			.AddLines(29, UITheme.FontHud, (TextAlignmentOptions)513)
			.Build();
		if (!IsHudAlive)
		{
			return;
		}
		if (hud.Lines != null)
		{
			for (int i = 1; i < hud.Lines.Length; i++)
			{
				if (hud.Lines[i] != null)
				{
					hud.Lines[i].FontSize = UITheme.ScaledFontBody;
				}
			}
		}
		hud.EnableReposition("sparroh.displaygunstats", "Gun Stats", ConfigManager.Anchors);
		UpdateHudVisibility();
	}

	public void Update()
	{
		try
		{
			if (ConfigManager.EnableGunStatsHUD == null || !ConfigManager.EnableGunStatsHUD.Value)
			{
				return;
			}
			if (hud != null && !IsHudAlive)
			{
				ClearDestroyedHud();
			}
			if (!CanAttachHud)
			{
				return;
			}
			if (!IsHudAlive)
			{
				CreateGunStatsHUD();
			}
			if (IsHudAlive)
			{
				updateTimer += Time.deltaTime;
				if (updateTimer >= 0.5f)
				{
					updateTimer = 0f;
					UpdateCurrentGun();
					UpdateGunStatsHUD();
				}
			}
		}
		catch (Exception arg)
		{
			if (!loggedUpdateError)
			{
				loggedUpdateError = true;
				DisplayGunStatsPlugin.Logger.LogError((object)$"Error in GunStatsHud.Update(): {arg}");
			}
		}
	}

	public Vector2 GetGunStatsHUDSize()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		if (!IsHudAlive)
		{
			return Vector2.zero;
		}
		return hud.Size;
	}

	private void SetLine(int index, string text)
	{
		if (IsHudAlive && index >= 0 && index < hud.Lines.Length)
		{
			UIText val = hud.Lines[index];
			if (val != null && !((Object)(object)val.Tmp == (Object)null))
			{
				val.Text = text ?? string.Empty;
			}
		}
	}

	private void UpdateGunStatsHUD()
	{
		if (!IsHudAlive)
		{
			return;
		}
		if ((Object)(object)currentGun == (Object)null)
		{
			SetLine(0, "No Gun Active");
			for (int i = 1; i < hud.Lines.Length; i++)
			{
				SetLine(i, "");
			}
			return;
		}
		try
		{
			GunStatSnapshot gunStatSnapshot = GunStatSnapshot.CaptureLive(currentGun, currentGunActualDamage, hasLastShotDamage);
			if (!gunStatSnapshot.IsValid)
			{
				SetLine(0, "No Gun Active");
				for (int j = 1; j < hud.Lines.Length; j++)
				{
					SetLine(j, "");
				}
				return;
			}
			GunStatColors colors = GunStatColors.FromConfig();
			bool showLastShotDamage = ConfigManager.ShowLastShotDamage != null && ConfigManager.ShowLastShotDamage.Value;
			List<string> list = new List<string>(28);
			gunStatSnapshot.AppendLines(list, colors, showLastShotDamage);
			for (int k = 0; k < list.Count && k < hud.Lines.Length; k++)
			{
				SetLine(k, list[k]);
			}
			for (int l = list.Count; l < hud.Lines.Length; l++)
			{
				SetLine(l, "");
			}
		}
		catch (Exception ex)
		{
			SetLine(0, "No Gun Active");
			if (!loggedUpdateError)
			{
				loggedUpdateError = true;
				DisplayGunStatsPlugin.Logger.LogWarning((object)("GunStatsHud stats update skipped: " + ex.Message));
			}
		}
	}

	public static void UpdateCurrentGun()
	{
		if ((Object)(object)Player.LocalPlayer == (Object)null)
		{
			currentGun = null;
			return;
		}
		if (playerField == null || activeProp == null)
		{
			currentGun = null;
			return;
		}
		try
		{
			Gun[] array = Object.FindObjectsOfType<Gun>();
			if (array == null)
			{
				currentGun = null;
				return;
			}
			Gun[] array2 = array;
			bool flag = default(bool);
			foreach (Gun val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				try
				{
					object? value = playerField.GetValue(val);
					if ((Object)((value is Player) ? value : null) != (Object)(object)Player.LocalPlayer)
					{
						continue;
					}
					IGear val2 = (IGear)(object)val;
					if (val2 == null)
					{
						continue;
					}
					object value2 = activeProp.GetValue(val2);
					int num;
					if (value2 is bool)
					{
						flag = (bool)value2;
						num = 1;
					}
					else
					{
						num = 0;
					}
					if (((uint)num & (flag ? 1u : 0u)) == 0)
					{
						continue;
					}
					if ((Object)(object)currentGun != (Object)(object)val)
					{
						currentGunActualDamage = 0f;
						hasLastShotDamage = false;
					}
					currentGun = val;
					return;
				}
				catch
				{
				}
			}
		}
		catch
		{
		}
		currentGun = null;
	}

	public void OnDestroy()
	{
		try
		{
			if (hud != null)
			{
				if (hud.IsAlive)
				{
					hud.Destroy();
				}
				hud = null;
			}
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogError((object)("Error in GunStatsHud.OnDestroy(): " + ex.Message));
		}
	}
}
public readonly struct GunStatSnapshot
{
	public readonly bool IsValid;

	public readonly string Title;

	public readonly string DamageType;

	public readonly Color DamageTypeColor;

	public readonly float SheetDamage;

	public readonly int BulletsPerShot;

	public readonly float EffectiveBulletDamage;

	public readonly float LastShotDamage;

	public readonly bool HasLastShotDamage;

	public readonly float FireInterval;

	public readonly float FireRatePerSecond;

	public readonly int BurstSize;

	public readonly float BurstFireInterval;

	public readonly int MagazineSize;

	public readonly int AmmoCapacity;

	public readonly float ReloadDuration;

	public readonly float ChargeDuration;

	public readonly bool HasCharge;

	public readonly float HitForce;

	public readonly float Range;

	public readonly string RecoilText;

	public readonly string SpreadText;

	public readonly string FireMode;

	public readonly float BulletSpeed;

	public readonly float BulletForce;

	public readonly float BulletGravity;

	public readonly float BulletRange;

	public readonly EffectType BulletEffect;

	public readonly float BulletEffectAmount;

	public readonly bool HasBulletEffect;

	public static GunStatSnapshot Invalid => default(GunStatSnapshot);

	public GunStatSnapshot(bool isValid, string title, string damageType, Color damageTypeColor, float sheetDamage, int bulletsPerShot, float effectiveBulletDamage, float lastShotDamage, bool hasLastShotDamage, float fireInterval, float fireRatePerSecond, int burstSize, float burstFireInterval, int magazineSize, int ammoCapacity, float reloadDuration, float chargeDuration, bool hasCharge, float hitForce, float range, string recoilText, string spreadText, string fireMode, float bulletSpeed, float bulletForce, float bulletGravity, float bulletRange, EffectType bulletEffect, float bulletEffectAmount, bool hasBulletEffect)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		IsValid = isValid;
		Title = title;
		DamageType = damageType;
		DamageTypeColor = damageTypeColor;
		SheetDamage = sheetDamage;
		BulletsPerShot = bulletsPerShot;
		EffectiveBulletDamage = effectiveBulletDamage;
		LastShotDamage = lastShotDamage;
		HasLastShotDamage = hasLastShotDamage;
		FireInterval = fireInterval;
		FireRatePerSecond = fireRatePerSecond;
		BurstSize = burstSize;
		BurstFireInterval = burstFireInterval;
		MagazineSize = magazineSize;
		AmmoCapacity = ammoCapacity;
		ReloadDuration = reloadDuration;
		ChargeDuration = chargeDuration;
		HasCharge = hasCharge;
		HitForce = hitForce;
		Range = range;
		RecoilText = recoilText;
		SpreadText = spreadText;
		FireMode = fireMode;
		BulletSpeed = bulletSpeed;
		BulletForce = bulletForce;
		BulletGravity = bulletGravity;
		BulletRange = bulletRange;
		BulletEffect = bulletEffect;
		BulletEffectAmount = bulletEffectAmount;
		HasBulletEffect = hasBulletEffect;
	}

	public static GunStatSnapshot CaptureLive(Gun gun, float lastShotDamage, bool hasLastShot)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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)
		if ((Object)(object)gun == (Object)null)
		{
			return Invalid;
		}
		try
		{
			ref GunData gunData = ref gun.GunData;
			float fireInterval = SafeFireInterval(gun, gunData.fireInterval);
			float reloadDuration = SafeReloadDuration(gun, gunData.reloadDuration);
			BulletData bullet = BuildDisplayBullet(gun, ref gunData);
			return Build("Current Gun Stats:", (IWeapon)(object)gun, ref gunData, fireInterval, reloadDuration, bullet, lastShotDamage, hasLastShot);
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)("GunStatSnapshot.CaptureLive failed: " + ex.Message));
			return Invalid;
		}
	}

	public static GunStatSnapshot CapturePreview(IWeapon weapon, GearDetailsWindow window)
	{
		return GunStatPreview.Capture(weapon, window);
	}

	internal static GunStatSnapshot BuildFromWeapon(IWeapon weapon, string title)
	{
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		ref GunData gunData = ref weapon.GunData;
		Gun val = (Gun)(object)((weapon is Gun) ? weapon : null);
		float num = ((gunData.fireInterval > 0f) ? gunData.fireInterval : 0.2f);
		float num2 = gunData.reloadDuration;
		if ((Object)(object)val != (Object)null)
		{
			num = SafeFireInterval(val, num);
			num2 = SafeReloadDuration(val, num2);
		}
		BulletData bullet = BuildDisplayBullet(val, ref gunData);
		return Build(title, weapon, ref gunData, num, num2, bullet, 0f, hasLastShot: false);
	}

	public void AppendLines(List<string> lines, GunStatColors colors, bool showLastShotDamage)
	{
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_024a: Unknown result type (might be due to invalid IL or missing references)
		//IL_026c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_030c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0332: Unknown result type (might be due to invalid IL or missing references)
		//IL_0358: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
		if (!IsValid || lines == null)
		{
			return;
		}
		lines.Add(Title ?? "Gun Stats:");
		string text = Format(EffectiveBulletDamage);
		if (BulletsPerShot > 1)
		{
			text = $"{text}x{BulletsPerShot}";
		}
		if (showLastShotDamage && HasLastShotDamage)
		{
			text = text + " (last " + Format(LastShotDamage) + ")";
		}
		lines.Add(RichText.Labeled("Bullet Damage", text, colors.Damage));
		lines.Add(RichText.Labeled("Bullets per Shot", BulletsPerShot.ToString(), colors.FireRate));
		if (HasBulletEffect)
		{
			try
			{
				StatusEffectData effect = Global.GetEffect(BulletEffect);
				lines.Add(RichText.Labeled("Bullet Effect", effect.EffectName + " x" + Format(BulletEffectAmount), effect.iconColor));
			}
			catch
			{
				lines.Add(RichText.Labeled("Bullet Effect", $"{BulletEffect} x{Format(BulletEffectAmount)}", colors.Default));
			}
		}
		lines.Add(RichText.Labeled("Fire Rate", $"{FireRatePerSecond:F1} /s", colors.FireRate));
		lines.Add(RichText.Labeled("Burst Size", BurstSize.ToString(), colors.FireRate));
		lines.Add(RichText.Labeled("Burst Interval", BurstFireInterval.ToString("F2"), colors.FireRate));
		lines.Add(RichText.Labeled("Magazine Size", MagazineSize.ToString(), colors.Ammo));
		lines.Add(RichText.Labeled("Reserve Ammo", AmmoCapacity.ToString(), colors.Ammo));
		lines.Add(RichText.Labeled("Reload Duration", Format(ReloadDuration), colors.Ammo));
		if (HasCharge)
		{
			lines.Add(RichText.Labeled("Charge Duration", Format(ChargeDuration), colors.Ammo));
		}
		lines.Add(RichText.Labeled("Explosion Size", Mathf.Round(HitForce).ToString(), colors.Explosion));
		lines.Add(RichText.Labeled("Range", Format(Range, 0), colors.Range));
		lines.Add(RichText.Labeled("Recoil", RecoilText, colors.Range));
		lines.Add(RichText.Labeled("Spread", SpreadText, colors.Range));
		lines.Add(RichText.Labeled("Fire Mode", FireMode, colors.FireRate));
		lines.Add(RichText.Labeled("Bullet Speed", BulletSpeed.ToString("F1"), colors.Default));
		lines.Add(RichText.Labeled("Bullet Force", BulletForce.ToString("F1"), colors.Default));
		lines.Add(RichText.Labeled("Bullet Range", BulletRange.ToString("F0"), colors.Range));
		lines.Add(RichText.Labeled("Bullet Gravity", BulletGravity.ToString("F2"), colors.Default));
	}

	private static GunStatSnapshot Build(string title, IWeapon weapon, ref GunData data, float fireInterval, float reloadDuration, BulletData bullet, float lastShotDamage, bool hasLastShot)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Expected O, but got Unknown
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Invalid comparison between Unknown and I4
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: 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)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: 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_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		string damageType = "Normal";
		Color damageTypeColor = default(Color);
		try
		{
			UpgradeStatChanges val = new UpgradeStatChanges();
			IEnumerator<StatInfo> enumerator = ((IGear)weapon).EnumeratePrimaryStats(val);
			while (enumerator.MoveNext())
			{
				if (enumerator.Current.name == "Damage Type")
				{
					damageType = enumerator.Current.value;
					damageTypeColor = enumerator.Current.color;
					break;
				}
			}
		}
		catch
		{
		}
		float num = Mathf.Max(fireInterval, 0.0001f);
		float fireRatePerSecond = 1f / num;
		string recoilText = $"X({Mathf.Round(data.recoilData.recoilX.x)}, {Mathf.Round(data.recoilData.recoilX.y)}) " + $"Y({Mathf.Round(data.recoilData.recoilY.x)}, {Mathf.Round(data.recoilData.recoilY.y)})";
		string spreadText = $"Size({Mathf.Round(data.spreadData.spreadSize.x)}, {Mathf.Round(data.spreadData.spreadSize.y)})";
		bool hasCharge = data.chargeData.duration > 0f;
		bool hasBulletEffect = (int)bullet.damageEffect > 0;
		return new GunStatSnapshot(isValid: true, title, damageType, damageTypeColor, data.damage, data.bulletsPerShot, bullet.damage, lastShotDamage, hasLastShot, fireInterval, fireRatePerSecond, data.burstSize, data.burstFireInterval, data.magazineSize, data.ammoCapacity, reloadDuration, data.chargeData.duration, hasCharge, data.hitForce, data.rangeData.falloffEndDistance, recoilText, spreadText, (data.automatic == 1) ? "Automatic" : "Semi Automatic", bullet.speed, bullet.force, bullet.gravity, bullet.range.falloffEndDistance, bullet.damageEffect, bullet.damageEffectAmount, hasBulletEffect);
	}

	private static BulletData BuildSheetBullet(ref GunData data)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		Vector3 zero = Vector3.zero;
		Quaternion identity = Quaternion.identity;
		return ((GunData)(ref data)).GetBulletData(ref zero, ref identity);
	}

	private static BulletData BuildDisplayBullet(Gun gun, ref GunData data)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)gun != (Object)null && GunStatsHud.AllowPredictiveModifyBulletData)
		{
			return BuildPredictedBullet(gun, ref data);
		}
		return BuildSheetBullet(ref data);
	}

	private static BulletData BuildPredictedBullet(Gun gun, ref GunData data)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		BulletData result = BuildSheetBullet(ref data);
		if ((Object)(object)gun == (Object)null)
		{
			return result;
		}
		bool suppressLastShotCapture = GunStatsHud.SuppressLastShotCapture;
		bool isPredictingStats = GunStatsHud.IsPredictingStats;
		GunStatsHud.SuppressLastShotCapture = true;
		GunStatsHud.IsPredictingStats = true;
		try
		{
			gun.ModifyBulletData(ref result, (BulletFlags)0);
		}
		catch (Exception ex)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)("Predicted ModifyBulletData failed: " + ex.Message));
		}
		finally
		{
			GunStatsHud.IsPredictingStats = isPredictingStats;
			GunStatsHud.SuppressLastShotCapture = suppressLastShotCapture;
		}
		return result;
	}

	private static float SafeFireInterval(Gun gun, float fallback)
	{
		try
		{
			float fireInterval = gun.FireInterval;
			if (fireInterval > 0f && !float.IsNaN(fireInterval) && !float.IsInfinity(fireInterval))
			{
				return fireInterval;
			}
		}
		catch
		{
		}
		if (!(fallback > 0f))
		{
			return 0.2f;
		}
		return fallback;
	}

	private static float SafeReloadDuration(Gun gun, float fallback)
	{
		try
		{
			float reloadDuration = gun.GetReloadDuration();
			if (reloadDuration >= 0f && !float.IsNaN(reloadDuration) && !float.IsInfinity(reloadDuration))
			{
				return reloadDuration;
			}
		}
		catch
		{
		}
		return fallback;
	}

	private static string Format(float value, int decimals = 1)
	{
		return Math.Round(value, decimals).ToString($"F{decimals}");
	}
}
public static class GunStatsPatches
{
	[HarmonyPatch(typeof(Gun), "Enable")]
	private static class GunEnablePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Gun __instance)
		{
			GunStatsHud.UpdateCurrentGun();
		}
	}

	[HarmonyPatch(typeof(Gun), "Disable")]
	private static class GunDisablePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Gun __instance)
		{
			GunStatsHud.UpdateCurrentGun();
		}
	}

	[HarmonyPatch(typeof(Gun), "ModifyBulletData")]
	private static class GunModifyBulletDataPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Gun __instance, ref BulletData data, BulletFlags flags)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!GunStatsHud.SuppressLastShotCapture && !GunStatsHud.IsPredictingStats && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)GunStatsHud.currentGun) && (flags & 1) != 0)
			{
				GunStatsHud.currentGunActualDamage = data.damage;
				GunStatsHud.hasLastShotDamage = true;
			}
		}
	}

	public static void Apply(Harmony harmony)
	{
		harmony.PatchAll(typeof(GunEnablePatch));
		harmony.PatchAll(typeof(GunDisablePatch));
		harmony.PatchAll(typeof(GunModifyBulletDataPatch));
	}
}
[BepInPlugin("sparroh.displaygunstats", "DisplayGunStats", "1.4.2")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class DisplayGunStatsPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.displaygunstats";

	public const string PluginName = "DisplayGunStats";

	public const string PluginVersion = "1.4.2";

	internal static ManualLogSource Logger;

	public static DisplayGunStatsPlugin Instance;

	private GearDetailsStats gearDetailsStats;

	private GunStatsHud gunStatsHud;

	private Harmony harmony;

	private void Awake()
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Expected O, but got Unknown
		Logger = ((BaseUnityPlugin)this).Logger;
		Instance = this;
		try
		{
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger);
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Failed to initialize config: " + ex.Message));
			return;
		}
		gearDetailsStats = new GearDetailsStats(ConfigManager.EnableMenuWindow.Value);
		try
		{
			harmony = new Harmony("sparroh.displaygunstats");
			gunStatsHud = new GunStatsHud(harmony);
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Failed to initialize HUD gun stats: " + ex2.Message));
		}
		Logger.LogInfo((object)"DisplayGunStats v1.4.2 loaded successfully.");
	}

	private void Update()
	{
		ConfigManager.Tick();
		if (ConfigManager.ConsumePendingRefresh())
		{
			OnConfigChanged();
		}
		if (gearDetailsStats != null)
		{
			gearDetailsStats.SetEnable(ConfigManager.EnableMenuWindow.Value);
			gearDetailsStats.Update();
		}
		if (gunStatsHud != null)
		{
			try
			{
				gunStatsHud.UpdateHudVisibility();
				gunStatsHud.Update();
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Error updating GunStatsHud: " + ex.Message));
			}
		}
	}

	private void OnDestroy()
	{
		try
		{
			gearDetailsStats?.Destroy();
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error destroying GearDetailsStats: " + ex.Message));
		}
		try
		{
			gunStatsHud?.OnDestroy();
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Error in GunStatsHud.OnDestroy(): " + ex2.Message));
		}
		try
		{
			ConfigManager.Dispose();
		}
		catch (Exception ex3)
		{
			Logger.LogError((object)("Error disposing config: " + ex3.Message));
		}
		try
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
		catch (Exception ex4)
		{
			Logger.LogError((object)("Error unpatching Harmony: " + ex4.Message));
		}
	}

	private void OnConfigChanged()
	{
		try
		{
			gearDetailsStats?.SetEnable(ConfigManager.EnableMenuWindow.Value);
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("Failed to refresh GearDetailsStats after config change: " + ex.Message));
		}
		try
		{
			gunStatsHud?.RefreshPredictionGate();
			gunStatsHud?.UpdateHudVisibility();
		}
		catch (Exception ex2)
		{
			Logger.LogWarning((object)("Failed to refresh GunStatsHud after config change: " + ex2.Message));
		}
	}
}
public static class PredictDamageGuards
{
	public static bool Install(Harmony harmony)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		int num = 0;
		HarmonyMethod val = new HarmonyMethod(typeof(PredictDamageGuards), "PrefixSkipBool", (Type[])null);
		HarmonyMethod val2 = new HarmonyMethod(typeof(PredictDamageGuards), "PrefixSkipVoid", (Type[])null);
		MethodInfo[] methods = typeof(IDamageSource).GetMethods(BindingFlags.Static | BindingFlags.Public);
		foreach (MethodInfo methodInfo in methods)
		{
			if (!(methodInfo.Name != "DamageTarget"))
			{
				try
				{
					harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
					DisplayGunStatsPlugin.Logger.LogInfo((object)$"Predict guard: patched IDamageSource.DamageTarget ({methodInfo.GetParameters().Length} params)");
				}
				catch (Exception ex)
				{
					DisplayGunStatsPlugin.Logger.LogWarning((object)("Predict guard: failed IDamageSource.DamageTarget: " + ex.Message));
				}
			}
		}
		methods = typeof(ITarget).GetMethods(BindingFlags.Static | BindingFlags.Public);
		foreach (MethodInfo methodInfo2 in methods)
		{
			if (!(methodInfo2.Name != "ApplyStatusEffect") || !(methodInfo2.Name != "ApplyStatusEffectIgnoreDead"))
			{
				try
				{
					harmony.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					DisplayGunStatsPlugin.Logger.LogInfo((object)("Predict guard: patched ITarget." + methodInfo2.Name));
				}
				catch (Exception ex2)
				{
					DisplayGunStatsPlugin.Logger.LogWarning((object)("Predict guard: failed ITarget." + methodInfo2.Name + ": " + ex2.Message));
				}
			}
		}
		try
		{
			methods = typeof(Player).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo3 in methods)
			{
				if (methodInfo3.Name != "Damage")
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo3.GetParameters();
				if (parameters.Length < 2)
				{
					continue;
				}
				try
				{
					if (methodInfo3.ReturnType == typeof(bool))
					{
						harmony.Patch((MethodBase)methodInfo3, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					else
					{
						harmony.Patch((MethodBase)methodInfo3, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					DisplayGunStatsPlugin.Logger.LogInfo((object)$"Predict guard: patched Player.Damage ({parameters.Length} params)");
				}
				catch (Exception ex3)
				{
					DisplayGunStatsPlugin.Logger.LogWarning((object)("Predict guard: failed Player.Damage: " + ex3.Message));
				}
			}
		}
		catch (Exception ex4)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)("Predict guard: Player.Damage scan failed: " + ex4.Message));
		}
		if (num == 0)
		{
			DisplayGunStatsPlugin.Logger.LogWarning((object)"Predict guard: primary DamageTarget path NOT patched.");
			return false;
		}
		return true;
	}

	public static bool PrefixSkipBool(ref bool __result)
	{
		if (!GunStatsHud.IsPredictingStats)
		{
			return true;
		}
		__result = false;
		return false;
	}

	public static bool PrefixSkipVoid()
	{
		return !GunStatsHud.IsPredictingStats;
	}
}
namespace Sparroh.DisplayGunStats
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "DisplayGunStats";

		public const string PLUGIN_NAME = "DisplayGunStats";

		public const string PLUGIN_VERSION = "1.4.2";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}