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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("DisplayGunStats")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DisplayGunStats")]
[assembly: AssemblyTitle("DisplayGunStats")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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 DisplayGunDataMod
{
private bool enableGearDetailsStatsWindow;
private IWeapon currentWeapon;
private GearDetailsWindow currentWindow;
private bool showStatsWindow;
private bool statsWindowVisible;
private UIWindow _window;
private bool _barRegistered;
private readonly List<UIText> _lineTexts = new List<UIText>();
private const int MaxLines = 32;
private const float UpdateInterval = 0.5f;
private float updateTimer;
public DisplayGunDataMod(bool enable)
{
enableGearDetailsStatsWindow = enable;
}
public void SetEnable(bool enable)
{
enableGearDetailsStatsWindow = enable;
if (!enable)
{
HideAll();
}
}
public void Update()
{
if (!enableGearDetailsStatsWindow)
{
HideAll();
return;
}
if ((Object)(object)Menu.Instance != (Object)null && LevelData.CanModifyGear)
{
Window top = Menu.Instance.WindowSystem.GetTop();
GearDetailsWindow val = (GearDetailsWindow)(object)((top is GearDetailsWindow) ? top : null);
if (val != null && !val.InSkinMode)
{
IUpgradable upgradablePrefab = val.UpgradablePrefab;
IWeapon val2 = (IWeapon)(object)((upgradablePrefab is IWeapon) ? upgradablePrefab : null);
if (val2 != null)
{
if (currentWeapon != val2)
{
currentWeapon = val2;
currentWindow = val;
showStatsWindow = true;
updateTimer = 0f;
EnsureUi();
UpdateGearDetailsStats();
}
}
else
{
ClearWeapon();
}
}
else
{
ClearWeapon();
}
}
else
{
ClearWeapon();
}
if (showStatsWindow && currentWeapon != null)
{
EnsureUi();
updateTimer += Time.deltaTime;
if (updateTimer >= 0.5f)
{
updateTimer = 0f;
UpdateGearDetailsStats();
}
}
}
private void ClearWeapon()
{
currentWeapon = null;
currentWindow = null;
showStatsWindow = false;
HideAll();
}
private void EnsureUi()
{
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
GearActionBar.Tick();
if (!_barRegistered)
{
GearActionBar.Register("gun_stats", statsWindowVisible ? "Hide Stats" : "Gun Stats", 150, (Action)delegate
{
statsWindowVisible = !statsWindowVisible;
ApplyVisibility();
}, (UIButtonStyle)1);
_barRegistered = true;
}
if (_window == null)
{
_window = UIWindow.Create("GunStatsPreview", (Vector2?)new Vector2(360f, 480f), "Gun Stats Preview", true, true, (int?)(UITheme.WindowSortingOrder + 2));
_window.OnClose((Action)delegate
{
statsWindowVisible = 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);
}
}
ApplyVisibility();
}
private void ApplyVisibility()
{
GearActionBar.SetText("gun_stats", statsWindowVisible ? "Hide Stats" : "Gun Stats");
GearActionBar.SetSlotVisible("gun_stats", showStatsWindow);
if (_window != null)
{
if (showStatsWindow && statsWindowVisible)
{
_window.Show();
}
else
{
_window.Hide(false);
}
}
}
private void HideAll()
{
if (_window != null)
{
_window.Hide(false);
}
GearActionBar.SetSlotVisible("gun_stats", 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] : "");
}
}
}
public void OnGUI()
{
}
public void Destroy()
{
if (_window != null)
{
_window.Destroy();
_window = null;
}
GearActionBar.Unregister("gun_stats");
_barRegistered = false;
_lineTexts.Clear();
}
}
public class GunDisplay
{
[HarmonyPatch(typeof(Gun), "Enable")]
private class GunEnablePatch
{
[HarmonyPostfix]
private static void Postfix(Gun __instance)
{
UpdateCurrentGun();
}
}
[HarmonyPatch(typeof(Gun), "Disable")]
private class GunDisablePatch
{
[HarmonyPostfix]
private static void Postfix(Gun __instance)
{
UpdateCurrentGun();
}
}
[HarmonyPatch(typeof(Gun), "ModifyBulletData")]
private class GunModifyBulletDataPatch
{
[HarmonyPostfix]
private static void Postfix(Gun __instance, ref BulletData data, BulletFlags flags)
{
//IL_001f: 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)
if (!SuppressLastShotCapture && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)currentGun) && (flags & 1) != 0)
{
currentGunActualDamage = data.damage;
hasLastShotDamage = true;
}
}
}
private ConfigEntry<bool> enableGunStatsHUD;
private ConfigEntry<bool> showLastShotDamage;
private ConfigEntry<float> gunDisplayAnchorX;
private ConfigEntry<float> gunDisplayAnchorY;
private ConfigColor damageColor;
private ConfigColor fireRateColor;
private ConfigColor ammoColor;
private ConfigColor explosionColor;
private ConfigColor rangeColor;
private ConfigColor defaultColor;
private float updateTimer;
private const float UpdateInterval = 0.5f;
private HudHandle hud;
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;
private static FieldInfo playerField;
private static PropertyInfo activeProp;
private readonly ConfigFile configFile;
private readonly Harmony harmony;
private bool IsHudAlive
{
get
{
if (hud != null && (Object)(object)hud.GameObject != (Object)null && 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 GunDisplay(ConfigFile configFile, Harmony harmony)
{
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_015d: Unknown result type (might be due to invalid IL or missing references)
//IL_017d: Unknown result type (might be due to invalid IL or missing references)
this.configFile = configFile;
this.harmony = harmony;
try
{
enableGunStatsHUD = configFile.Bind<bool>("General", "EnableGunStatsHUD", true, "If true, the gun stats HUD will be displayed.");
enableGunStatsHUD.SettingChanged += OnEnableGunStatsHUDChanged;
showLastShotDamage = configFile.Bind<bool>("General", "ShowLastShotDamage", true, "If true, show last-shot bullet damage next to the effective (predicted) bullet damage on the HUD.");
gunDisplayAnchorX = configFile.Bind<float>("HUD Positioning", "GunDisplayAnchorX", 0.03123011f, "X anchor position for GunDisplay (0-1).");
gunDisplayAnchorY = configFile.Bind<float>("HUD Positioning", "GunDisplayAnchorY", 0.9360327f, "Y anchor position for GunDisplay (0-1).");
gunDisplayAnchorX.SettingChanged += OnAnchorChanged;
gunDisplayAnchorY.SettingChanged += OnAnchorChanged;
damageColor = ConfigColor.Bind(configFile, "Colors", "DamageColor", UIColors.Rose, "Rich-text color for Damage (hex RRGGBB or #RRGGBB).");
fireRateColor = ConfigColor.Bind(configFile, "Colors", "FireRateColor", UIColors.Macaroon, "Rich-text color for Fire Rate, Burst, Bullets per Shot, Fire Mode (hex RRGGBB or #RRGGBB).");
ammoColor = ConfigColor.Bind(configFile, "Colors", "AmmoColor", UIColors.Sky, "Rich-text color for Magazine/Ammo, Reload, Charge (hex RRGGBB or #RRGGBB).");
explosionColor = ConfigColor.Bind(configFile, "Colors", "ExplosionColor", UIColors.Orchid, "Rich-text color for Explosion Size (hex RRGGBB or #RRGGBB).");
rangeColor = ConfigColor.Bind(configFile, "Colors", "RangeColor", UIColors.Shamrock, "Rich-text color for Range, Recoil, Spread (hex RRGGBB or #RRGGBB).");
defaultColor = ConfigColor.Bind(configFile, "Colors", "DefaultColor", UIColors.TextPrimary, "Rich-text fallback color for other stats (hex RRGGBB or #RRGGBB).");
playerField = typeof(Gun).GetField("player", BindingFlags.Instance | BindingFlags.NonPublic);
activeProp = typeof(IGear).GetProperty("Active");
harmony.PatchAll(typeof(GunEnablePatch));
harmony.PatchAll(typeof(GunDisablePatch));
harmony.PatchAll(typeof(GunModifyBulletDataPatch));
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("Failed to initialize GunDisplay: " + ex.Message));
}
}
public void UpdateHudVisibility()
{
if (!IsHudAlive)
{
ClearDestroyedHud();
}
else
{
hud.SetActive(enableGunStatsHUD.Value);
}
}
private void OnEnableGunStatsHUDChanged(object sender, EventArgs e)
{
UpdateHudVisibility();
}
private void OnAnchorChanged(object sender, EventArgs e)
{
UpdateAnchors();
}
private void UpdateAnchors()
{
if (!IsHudAlive)
{
ClearDestroyedHud();
}
else
{
hud.SetAnchor(gunDisplayAnchorX.Value, gunDisplayAnchorY.Value);
}
}
private void ClearDestroyedHud()
{
if (hud == null)
{
return;
}
try
{
if ((Object)(object)hud.Rect != (Object)null)
{
HudRepositionClient.Unregister("sparroh.displaygunstats");
}
}
catch
{
}
hud = null;
}
private void CreateGunStatsHUD()
{
//IL_004d: 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(gunDisplayAnchorX.Value, gunDisplayAnchorY.Value)
.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;
}
}
}
HudRepositionClient.Register("sparroh.displaygunstats", "Gun Stats", hud.Rect, gunDisplayAnchorX, gunDisplayAnchorY);
UpdateHudVisibility();
}
public void Update()
{
try
{
if (enableGunStatsHUD == null || !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;
SparrohPlugin.Logger.LogError((object)$"Error in GunDisplay.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()
{
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Unknown result type (might be due to invalid IL or missing references)
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 = new GunStatColors(damageColor.Value, fireRateColor.Value, ammoColor.Value, explosionColor.Value, rangeColor.Value, defaultColor.Value);
List<string> list = new List<string>(28);
gunStatSnapshot.AppendLines(list, colors, showLastShotDamage != null && showLastShotDamage.Value);
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;
SparrohPlugin.Logger.LogWarning((object)("GunDisplay 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
{
HudRepositionClient.Unregister("sparroh.displaygunstats");
if (hud != null)
{
if ((Object)(object)hud.GameObject != (Object)null)
{
hud.Destroy();
}
hud = null;
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("Error in GunDisplay.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;
private static FieldInfo equipSlotsField;
private static bool equipSlotsFieldResolved;
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 = BuildModifiedBullet(gun, ref gunData);
return Build("Current Gun Stats:", (IWeapon)(object)gun, ref gunData, fireInterval, reloadDuration, bullet, lastShotDamage, hasLastShot);
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("GunStatSnapshot.CaptureLive failed: " + ex.Message));
return Invalid;
}
}
public static GunStatSnapshot CapturePreview(IWeapon weapon, GearDetailsWindow window)
{
//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_0182: Unknown result type (might be due to invalid IL or missing references)
//IL_0183: Unknown result type (might be due to invalid IL or missing references)
//IL_0133: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_0144: Unknown result type (might be due to invalid IL or missing references)
if (weapon == null)
{
return Invalid;
}
GunData gunData = weapon.GunData;
try
{
ResetToPrefabBaseline(weapon);
List<UpgradeInstance> list = CollectEquippedUpgrades(window);
list.Sort(CompareUpgradePriority);
foreach (UpgradeInstance item in list)
{
try
{
Upgrade obj = ((item != null) ? item.Upgrade : null);
GearUpgrade val = (GearUpgrade)(object)((obj is GearUpgrade) ? obj : null);
if (val != null)
{
val.Apply((IGear)(object)weapon, item);
}
}
catch (Exception ex)
{
string text = (((Object)(object)((item != null) ? item.Upgrade : null) != (Object)null) ? item.Upgrade.Name : "?");
SparrohPlugin.Logger.LogWarning((object)("Error applying upgrade " + text + ": " + ex.Message));
}
}
ref GunData gunData2 = ref weapon.GunData;
Gun val2 = (Gun)(object)((weapon is Gun) ? weapon : null);
float fireInterval = (((Object)(object)val2 != (Object)null) ? SafeFireInterval(val2, gunData2.fireInterval) : gunData2.fireInterval);
float reloadDuration = (((Object)(object)val2 != (Object)null) ? SafeReloadDuration(val2, gunData2.reloadDuration) : gunData2.reloadDuration);
BulletData bullet = BuildModifiedBullet(val2, ref gunData2);
return Build("Weapon Stats Preview:", weapon, ref gunData2, fireInterval, reloadDuration, bullet, 0f, hasLastShot: false);
}
catch (Exception ex2)
{
SparrohPlugin.Logger.LogWarning((object)("GunStatSnapshot.CapturePreview failed: " + ex2.Message));
return Invalid;
}
finally
{
try
{
weapon.GunData = gunData;
}
catch (Exception ex3)
{
SparrohPlugin.Logger.LogWarning((object)("Failed to restore GunData after preview: " + ex3.Message));
}
}
}
public void AppendLines(List<string> lines, GunStatColors colors, bool showLastShotDamage)
{
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: 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)
//IL_019c: Unknown result type (might be due to invalid IL or missing references)
//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
//IL_0206: Unknown result type (might be due to invalid IL or missing references)
//IL_0259: Unknown result type (might be due to invalid IL or missing references)
//IL_027b: Unknown result type (might be due to invalid IL or missing references)
//IL_0297: Unknown result type (might be due to invalid IL or missing references)
//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0321: Unknown result type (might be due to invalid IL or missing references)
//IL_034a: Unknown result type (might be due to invalid IL or missing references)
//IL_0373: Unknown result type (might be due to invalid IL or missing references)
//IL_0230: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: 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));
int bulletsPerShot = BulletsPerShot;
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));
bulletsPerShot = BurstSize;
lines.Add(RichText.Labeled("Burst Size", bulletsPerShot.ToString(), colors.FireRate));
float burstFireInterval = BurstFireInterval;
lines.Add(RichText.Labeled("Burst Interval", burstFireInterval.ToString("F2"), colors.FireRate));
bulletsPerShot = MagazineSize;
lines.Add(RichText.Labeled("Magazine Size", bulletsPerShot.ToString(), colors.Ammo));
bulletsPerShot = AmmoCapacity;
lines.Add(RichText.Labeled("Reserve Ammo", bulletsPerShot.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));
burstFireInterval = BulletSpeed;
lines.Add(RichText.Labeled("Bullet Speed", burstFireInterval.ToString("F1"), colors.Default));
burstFireInterval = BulletForce;
lines.Add(RichText.Labeled("Bullet Force", burstFireInterval.ToString("F1"), colors.Default));
burstFireInterval = BulletRange;
lines.Add(RichText.Labeled("Bullet Range", burstFireInterval.ToString("F0"), colors.Range));
burstFireInterval = BulletGravity;
lines.Add(RichText.Labeled("Bullet Gravity", burstFireInterval.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 BuildModifiedBullet(Gun gun, 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)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
Vector3 zero = Vector3.zero;
Quaternion identity = Quaternion.identity;
BulletData bulletData = ((GunData)(ref data)).GetBulletData(ref zero, ref identity);
if ((Object)(object)gun != (Object)null)
{
bool suppressLastShotCapture = GunDisplay.SuppressLastShotCapture;
GunDisplay.SuppressLastShotCapture = true;
try
{
gun.ModifyBulletData(ref bulletData, (BulletFlags)11);
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("ModifyBulletData failed: " + ex.Message));
}
finally
{
GunDisplay.SuppressLastShotCapture = suppressLastShotCapture;
}
}
return bulletData;
}
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 void ResetToPrefabBaseline(IWeapon weapon)
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: 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)
try
{
if (weapon != null)
{
IUpgradable prefab = ((IUpgradable)weapon).Prefab;
IWeapon val = (IWeapon)(object)((prefab is IWeapon) ? prefab : null);
if (val != null)
{
Transform firePoint = weapon.GunData.firePoint;
GunData gunData = val.GunData;
gunData.firePoint = firePoint;
weapon.GunData = gunData;
}
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("ResetToPrefabBaseline failed: " + ex.Message));
}
}
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)
{
SparrohPlugin.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);
}
private static string Format(float value, int decimals = 1)
{
return Math.Round(value, decimals).ToString($"F{decimals}");
}
}
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 class HudRepositionClient
{
private const string ApiTypeName = "HudRepositionAPI";
private static Type _apiType;
private static MethodInfo _register;
private static MethodInfo _unregister;
private static bool _available;
public static bool IsAvailable
{
get
{
EnsureResolved();
return _available;
}
}
public static void Register(string id, string displayName, RectTransform rect, ConfigEntry<float> anchorX, ConfigEntry<float> anchorY)
{
if (!_available)
{
EnsureResolved(force: true);
}
if (!_available || (Object)(object)rect == (Object)null || anchorX == null || anchorY == null)
{
return;
}
try
{
_register.Invoke(null, new object[5] { id, displayName, rect, anchorX, anchorY });
}
catch (Exception ex)
{
Debug.LogWarning((object)("[HudRepositionClient] Register failed: " + (ex.InnerException?.Message ?? ex.Message)));
}
}
public static void Unregister(string id)
{
if (!_available)
{
EnsureResolved(force: true);
}
if (!_available || string.IsNullOrEmpty(id))
{
return;
}
try
{
_unregister.Invoke(null, new object[1] { id });
}
catch (Exception)
{
}
}
private static void EnsureResolved(bool force = false)
{
if (_available && !force)
{
return;
}
_apiType = null;
_register = null;
_unregister = null;
_available = false;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
Type type = assembly.GetType("HudRepositionAPI", throwOnError: false);
if (type != null)
{
_apiType = type;
break;
}
}
catch
{
}
}
if (_apiType == null)
{
return;
}
MethodInfo[] methods = _apiType.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (MethodInfo methodInfo in methods)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
if (methodInfo.Name == "Register" && parameters.Length == 5 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(string) && typeof(RectTransform).IsAssignableFrom(parameters[2].ParameterType))
{
_register = methodInfo;
}
else if (methodInfo.Name == "Unregister" && parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
{
_unregister = methodInfo;
}
}
_available = _register != null && _unregister != null;
}
}
[BepInPlugin("sparroh.displaygunstats", "DisplayGunStats", "1.4.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
public const string PluginGUID = "sparroh.displaygunstats";
public const string PluginName = "DisplayGunStats";
public const string PluginVersion = "1.4.0";
private ConfigEntry<bool> EnableDisplayGunData;
private DisplayGunDataMod displayGunDataMod;
private GunDisplay gunDisplay;
private Harmony harmony;
internal static ManualLogSource Logger;
public static SparrohPlugin Instance;
private FileSystemWatcher configWatcher;
private volatile bool reloadPending;
private float reloadCooldown;
private const float ReloadDebounceSeconds = 0.25f;
private void Awake()
{
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Expected O, but got Unknown
Logger = ((BaseUnityPlugin)this).Logger;
Instance = this;
EnableDisplayGunData = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Display Gun Stats", false, "Show gun stats window when editing weapons in Gear Details");
EnableDisplayGunData.SettingChanged += delegate
{
if (displayGunDataMod != null)
{
displayGunDataMod.SetEnable(EnableDisplayGunData.Value);
}
};
displayGunDataMod = new DisplayGunDataMod(EnableDisplayGunData.Value);
try
{
harmony = new Harmony("sparroh.displaygunstats");
gunDisplay = new GunDisplay(((BaseUnityPlugin)this).Config, harmony);
}
catch (Exception ex)
{
Logger.LogError((object)("Failed to initialize HUD gun stats: " + ex.Message));
}
SetupConfigHotReload();
Logger.LogInfo((object)"DisplayGunStats v1.4.0 loaded successfully.");
}
private void SetupConfigHotReload()
{
try
{
string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
string directoryName = Path.GetDirectoryName(configFilePath);
string fileName = Path.GetFileName(configFilePath);
if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(fileName))
{
Logger.LogWarning((object)"Could not set up config hot reload: invalid config path.");
return;
}
configWatcher = new FileSystemWatcher(directoryName, fileName)
{
NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
EnableRaisingEvents = true
};
configWatcher.Changed += OnConfigFileChanged;
configWatcher.Created += OnConfigFileChanged;
configWatcher.Renamed += OnConfigFileChanged;
Logger.LogInfo((object)("Config hot reload enabled for " + fileName));
}
catch (Exception ex)
{
Logger.LogWarning((object)("Failed to set up config hot reload: " + ex.Message));
}
}
private void OnConfigFileChanged(object sender, FileSystemEventArgs e)
{
reloadPending = true;
}
private void Update()
{
if (reloadPending)
{
reloadPending = false;
reloadCooldown = 0.25f;
}
if (reloadCooldown > 0f)
{
reloadCooldown -= Time.unscaledDeltaTime;
if (reloadCooldown <= 0f)
{
TryReloadConfig();
}
}
if (displayGunDataMod != null)
{
displayGunDataMod.SetEnable(EnableDisplayGunData.Value);
displayGunDataMod.Update();
}
if (gunDisplay != null)
{
try
{
gunDisplay.UpdateHudVisibility();
gunDisplay.Update();
}
catch (Exception ex)
{
Logger.LogError((object)("Error updating GunDisplay: " + ex.Message));
}
}
}
private void TryReloadConfig()
{
try
{
((BaseUnityPlugin)this).Config.Reload();
Logger.LogInfo((object)"Config reloaded from disk.");
}
catch (IOException)
{
reloadPending = true;
}
catch (Exception ex2)
{
Logger.LogWarning((object)("Failed to reload config: " + ex2.Message));
}
}
private void OnGUI()
{
displayGunDataMod?.OnGUI();
}
private void OnDestroy()
{
if (configWatcher != null)
{
configWatcher.EnableRaisingEvents = false;
configWatcher.Changed -= OnConfigFileChanged;
configWatcher.Created -= OnConfigFileChanged;
configWatcher.Renamed -= OnConfigFileChanged;
configWatcher.Dispose();
configWatcher = null;
}
try
{
displayGunDataMod?.Destroy();
}
catch (Exception ex)
{
Logger.LogError((object)("Error destroying DisplayGunDataMod: " + ex.Message));
}
try
{
gunDisplay?.OnDestroy();
}
catch (Exception ex2)
{
Logger.LogError((object)("Error in GunDisplay.OnDestroy(): " + ex2.Message));
}
try
{
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
}
catch (Exception ex3)
{
Logger.LogError((object)("Error unpatching Harmony: " + ex3.Message));
}
}
}
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.0.0";
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}