using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using AmmoCounter.Configuration;
using AmmoCounter.Localization;
using AmmoCounter.UI;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using HowtoFishIdentityAPI.Api;
using HowtoFishLocalizationAPI.Api;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("AmmoCounter")]
[assembly: AssemblyDescription("Weapon ammo HUD for How to Fish by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("AmmoCounter")]
[assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")]
[assembly: ComVisible(false)]
[assembly: Guid("54e4d8be-3a42-4a63-8cd2-6b1d8f9ce1b7")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.3.0")]
namespace AmmoCounter
{
[BepInPlugin("IceBoxStudio.HowToFish.AmmoCounter", "AmmoCounter", "1.0.3")]
[BepInDependency("IceBoxStudio.HowToFish.LocalizationAPI", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class AmmoCounterPlugin : BaseUnityPlugin
{
private Harmony _harmony;
internal static ManualLogSource Log { get; private set; }
private void Awake()
{
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
try
{
Log.LogInfo((object)"=============================================");
Log.LogInfo((object)("AmmoCounter " + I18n.Text("plugin.initializing")));
Log.LogInfo((object)(I18n.Text("plugin.author_prefix") + "Ice Box Studio(https://steamcommunity.com/id/ibox666/)"));
ConfigManager.Init(((BaseUnityPlugin)this).Config);
AmmoRuntime.Init(((BaseUnityPlugin)this).Logger);
_harmony = new Harmony("IceBoxStudio.HowToFish.AmmoCounter");
_harmony.PatchAll(Assembly.GetExecutingAssembly());
Log.LogInfo((object)("AmmoCounter " + I18n.Text("plugin.initialized")));
Log.LogInfo((object)"=============================================");
}
catch (Exception arg)
{
Log.LogError((object)$"Failed to initialize AmmoCounter: {arg}");
}
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "IceBoxStudio.HowToFish.AmmoCounter";
public const string PLUGIN_NAME = "AmmoCounter";
public const string PLUGIN_VERSION = "1.0.3";
}
internal static class AmmoRuntime
{
internal static void Init(ManualLogSource log)
{
StatTrakRuntime.Init(log);
}
internal static void Start(Player player)
{
StatTrakRuntime.Start(player);
if (!((Object)(object)player == (Object)null) && !(((NetworkBehaviour)player).Owner == (NetworkConnection)null) && ((NetworkBehaviour)player).Owner.IsLocalClient)
{
AmmoUi.Attach();
}
}
internal static void Stop(Player player)
{
StatTrakRuntime.Stop(player);
if (!((Object)(object)player == (Object)null) && !(((NetworkBehaviour)player).Owner == (NetworkConnection)null) && ((NetworkBehaviour)player).Owner.IsLocalClient)
{
AmmoUi.Stop();
}
}
internal static void Tick()
{
StatTrakRuntime.Tick();
AmmoUi.Tick();
}
internal static void Kill()
{
StatTrakRuntime.AddKill();
}
}
public struct StatTrakKillBroadcast : IBroadcast
{
public int ItemId;
public StatTrakKillBroadcast(int itemId)
{
ItemId = itemId;
}
}
public struct StatTrakSyncBroadcast : IBroadcast
{
public int ItemId;
public int Kills;
public StatTrakSyncBroadcast(int itemId, int kills)
{
ItemId = itemId;
Kills = kills;
}
}
internal static class StatTrakRuntime
{
[Serializable]
private sealed class SaveData
{
public List<SaveEntry> Entries = new List<SaveEntry>();
}
[Serializable]
private sealed class SaveEntry
{
public string PlayerKey;
public int ItemId;
public int Kills;
}
private static readonly Dictionary<string, Dictionary<int, int>> ServerKills = new Dictionary<string, Dictionary<int, int>>();
private static readonly Dictionary<int, int> ClientKills = new Dictionary<int, int>();
private static ManualLogSource _log;
private static string _path;
private static bool _serverReady;
private static bool _serverDirty;
private static bool _clientRegistered;
private static bool _serverRegistered;
private static bool _serializersReady;
private static bool _identityHook;
internal static void Init(ManualLogSource log)
{
_log = log;
}
internal static void Start(Player player)
{
if (!ConfigManager.ShowStatTrak || !HasIdentity())
{
return;
}
SetupNet();
if (InstanceFinder.IsServerStarted)
{
if (!_serverReady)
{
ServerStart();
}
if ((Object)(object)player != (Object)null && ((NetworkBehaviour)player).Owner != (NetworkConnection)null && IdentityApi.IsVerified(((NetworkBehaviour)player).Owner))
{
IdentityReady(((NetworkBehaviour)player).Owner);
}
}
}
internal static void Stop(Player player)
{
if (!((Object)(object)player == (Object)null) && !(((NetworkBehaviour)player).Owner == (NetworkConnection)null) && ((NetworkBehaviour)player).Owner.IsLocalClient)
{
ClientKills.Clear();
}
}
internal static void Tick()
{
if (ConfigManager.ShowStatTrak && HasIdentity())
{
SetupNet();
if (InstanceFinder.IsServerStarted && !_serverReady)
{
ServerStart();
}
}
}
internal static int Get(Item item)
{
if (!ConfigManager.ShowStatTrak || !Object.op_Implicit((Object)(object)item))
{
return 0;
}
int num = ItemKey(item.ID);
string playerKey = default(string);
if (InstanceFinder.IsServerStarted && (Object)(object)Player.LocalPlayer != (Object)null && HasIdentity() && IdentityApi.TryGetKey(Player.LocalPlayer, ref playerKey))
{
return GetServer(playerKey, num);
}
if (!ClientKills.TryGetValue(num, out var value))
{
return 0;
}
return value;
}
internal static void AddKill()
{
if (!ConfigManager.ShowStatTrak || !HasIdentity())
{
return;
}
Player localPlayer = Player.LocalPlayer;
Item val = ((Object.op_Implicit((Object)(object)localPlayer) && Object.op_Implicit((Object)(object)localPlayer.Holding)) ? localPlayer.Holding.HeldItem : null);
if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.Weapon))
{
return;
}
if (InstanceFinder.IsServerStarted)
{
AddServerKill(localPlayer, val.ID);
return;
}
SetupNet();
if (_clientRegistered && (Object)(object)InstanceFinder.ClientManager != (Object)null)
{
InstanceFinder.ClientManager.Broadcast<StatTrakKillBroadcast>(new StatTrakKillBroadcast(val.ID), (Channel)0);
}
}
internal static void Save()
{
if (_serverReady)
{
Write();
}
}
internal static void ServerStop()
{
Write();
ServerKills.Clear();
_path = null;
_serverReady = false;
_serverDirty = false;
_serverRegistered = false;
}
internal static void SetupNet()
{
if (ConfigManager.ShowStatTrak && HasIdentity())
{
HookIdentity();
SetupSerializers();
if (!_clientRegistered && (Object)(object)InstanceFinder.ClientManager != (Object)null)
{
InstanceFinder.ClientManager.RegisterBroadcast<StatTrakSyncBroadcast>((Action<StatTrakSyncBroadcast, Channel>)GotSync);
_clientRegistered = true;
}
if (!_serverRegistered && (Object)(object)InstanceFinder.ServerManager != (Object)null)
{
InstanceFinder.ServerManager.RegisterBroadcast<StatTrakKillBroadcast>((Action<NetworkConnection, StatTrakKillBroadcast, Channel>)GotKill, true);
_serverRegistered = true;
}
}
}
internal static void ServerStart()
{
if (!_serverReady && ConfigManager.ShowStatTrak && HasIdentity() && InstanceFinder.IsServerStarted && SaveManager.CurServerSave != null && !string.IsNullOrEmpty(SaveManager.CurServerSave.Name))
{
ServerKills.Clear();
_path = PathFor(SaveManager.CurServerSave.Name);
_serverReady = true;
_serverDirty = false;
SetupNet();
Load();
}
}
private static void IdentityReady(NetworkConnection connection)
{
string key = default(string);
if (!ConfigManager.ShowStatTrak || !HasIdentity() || !_serverReady || !InstanceFinder.IsServerStarted || connection == (NetworkConnection)null || !IdentityApi.IsVerified(connection) || !IdentityApi.TryGetKey(connection, ref key))
{
return;
}
Player val = FindPlayer(connection);
if ((Object)(object)val == (Object)null || (Object)(object)InstanceFinder.ServerManager == (Object)null || !ServerKills.TryGetValue(key, out var value))
{
return;
}
foreach (KeyValuePair<int, int> item in value)
{
Send(val, item.Key, item.Value);
}
}
private static void GotKill(NetworkConnection connection, StatTrakKillBroadcast message, Channel channel)
{
if (ConfigManager.ShowStatTrak && _serverReady && HasIdentity() && !(connection == (NetworkConnection)null) && IdentityApi.IsVerified(connection))
{
Player val = FindPlayer(connection);
Item val2 = ((Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.Holding)) ? val.Holding.HeldItem : null);
if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.Weapon) && val2.ID == message.ItemId)
{
AddServerKill(val, message.ItemId);
}
}
}
private static void AddServerKill(Player player, int itemId)
{
string key = default(string);
if (_serverReady && HasIdentity() && !((Object)(object)player == (Object)null) && !(((NetworkBehaviour)player).Owner == (NetworkConnection)null) && IdentityApi.TryGetKey(player, ref key))
{
int key2 = ItemKey(itemId);
if (!ServerKills.TryGetValue(key, out var value))
{
value = new Dictionary<int, int>();
ServerKills[key] = value;
}
int value2;
int num = (value.TryGetValue(key2, out value2) ? value2 : 0);
int kills = (value[key2] = ((num == int.MaxValue) ? num : (num + 1)));
_serverDirty = true;
Write();
Send(player, itemId, kills);
}
}
private static void Send(Player player, int itemId, int kills)
{
if (HasIdentity() && !((Object)(object)player == (Object)null) && !(((NetworkBehaviour)player).Owner == (NetworkConnection)null) && !((Object)(object)InstanceFinder.ServerManager == (Object)null) && IdentityApi.IsVerified(((NetworkBehaviour)player).Owner))
{
InstanceFinder.ServerManager.Broadcast<StatTrakSyncBroadcast>(((NetworkBehaviour)player).Owner, new StatTrakSyncBroadcast(itemId, kills), true, (Channel)0);
}
}
private static void GotSync(StatTrakSyncBroadcast message, Channel channel)
{
if (ConfigManager.ShowStatTrak)
{
ClientKills[ItemKey(message.ItemId)] = Mathf.Max(0, message.Kills);
}
}
private static int GetServer(string playerKey, int itemKey)
{
if (!ServerKills.TryGetValue(playerKey, out var value) || !value.TryGetValue(itemKey, out var value2))
{
return 0;
}
return value2;
}
private static Player FindPlayer(NetworkConnection connection)
{
if (connection == (NetworkConnection)null)
{
return null;
}
foreach (Player player in PlayerManager.Players)
{
if ((Object)(object)player != (Object)null && ((NetworkBehaviour)player).Owner != (NetworkConnection)null && ((NetworkBehaviour)player).Owner.ClientId == connection.ClientId)
{
return player;
}
}
return null;
}
private static int ItemKey(int itemId)
{
return itemId & 0xFF;
}
private static void Load()
{
if (!File.Exists(_path))
{
return;
}
try
{
SaveData saveData = JsonConvert.DeserializeObject<SaveData>(File.ReadAllText(_path));
if (saveData == null || saveData.Entries == null)
{
return;
}
for (int i = 0; i < saveData.Entries.Count; i++)
{
SaveEntry saveEntry = saveData.Entries[i];
if (saveEntry != null && !string.IsNullOrEmpty(saveEntry.PlayerKey))
{
if (!ServerKills.TryGetValue(saveEntry.PlayerKey, out var value))
{
value = new Dictionary<int, int>();
ServerKills[saveEntry.PlayerKey] = value;
}
int key = ItemKey(saveEntry.ItemId);
int value2;
int num = (value.TryGetValue(key, out value2) ? value2 : 0);
int num2 = Mathf.Max(0, saveEntry.Kills);
value[key] = ((num > int.MaxValue - num2) ? int.MaxValue : (num + num2));
}
}
}
catch (Exception ex)
{
ManualLogSource log = _log;
if (log != null)
{
log.LogError((object)("Failed to load StatTrak save: " + ex));
}
}
}
private static void Write()
{
if (!_serverDirty || string.IsNullOrEmpty(_path))
{
return;
}
try
{
string directoryName = Path.GetDirectoryName(_path);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
SaveData saveData = new SaveData();
foreach (KeyValuePair<string, Dictionary<int, int>> serverKill in ServerKills)
{
foreach (KeyValuePair<int, int> item in serverKill.Value)
{
saveData.Entries.Add(new SaveEntry
{
PlayerKey = serverKill.Key,
ItemId = item.Key,
Kills = item.Value
});
}
}
File.WriteAllText(_path, JsonConvert.SerializeObject((object)saveData));
_serverDirty = false;
}
catch (Exception ex)
{
ManualLogSource log = _log;
if (log != null)
{
log.LogError((object)("Failed to save StatTrak data: " + ex));
}
}
}
private static string PathFor(string name)
{
char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
foreach (char oldChar in invalidFileNameChars)
{
name = name.Replace(oldChar, '_');
}
return Path.Combine(Application.persistentDataPath, "Saves", name + ".AmmoCounter.StatTrak.json");
}
private static bool HasIdentity()
{
return Chainloader.PluginInfos.ContainsKey("IceBoxStudio.HowToFish.IdentityAPI");
}
private static void HookIdentity()
{
if (!_identityHook && HasIdentity())
{
IdentityApi.Verified += IdentityReady;
_identityHook = true;
}
}
private static void SetupSerializers()
{
if (!_serializersReady)
{
GenericWriter<StatTrakKillBroadcast>.SetWrite((Action<Writer, StatTrakKillBroadcast>)WriteKill);
GenericReader<StatTrakKillBroadcast>.SetRead((Func<Reader, StatTrakKillBroadcast>)ReadKill);
GenericWriter<StatTrakSyncBroadcast>.SetWrite((Action<Writer, StatTrakSyncBroadcast>)WriteSync);
GenericReader<StatTrakSyncBroadcast>.SetRead((Func<Reader, StatTrakSyncBroadcast>)ReadSync);
_serializersReady = true;
}
}
private static void WriteKill(Writer writer, StatTrakKillBroadcast message)
{
writer.WriteInt32(message.ItemId);
}
private static StatTrakKillBroadcast ReadKill(Reader reader)
{
return new StatTrakKillBroadcast(reader.ReadInt32());
}
private static void WriteSync(Writer writer, StatTrakSyncBroadcast message)
{
writer.WriteInt32(message.ItemId);
writer.WriteInt32(message.Kills);
}
private static StatTrakSyncBroadcast ReadSync(Reader reader)
{
return new StatTrakSyncBroadcast(reader.ReadInt32(), reader.ReadInt32());
}
}
}
namespace AmmoCounter.UI
{
internal sealed class AmmoUi : MonoBehaviour
{
private const int PreviewSize = 64;
private const float PreviewDistance = 10f;
private const float PanelWidth = 238f;
private const float PanelHeight = 104f;
private const float ContentRight = 16f;
private const float AttachmentBaseSize = 20f;
private const float AttachmentBaseGap = 4f;
private const float AttachmentTopGap = 6f;
private static readonly int PreviewLayer = LayerMask.NameToLayer("UI");
private static AmmoUi _current;
private RectTransform _root;
private CanvasGroup _canvas;
private Image _background;
private Color _backgroundColor;
private TextMeshProUGUI _weaponName;
private TextMeshProUGUI _statTrakText;
private TextMeshProUGUI _ammoText;
private RawImage[] _attachmentImages;
private Camera _previewCamera;
private Light _previewLight;
private Weapon _lastWeapon;
private int _lastAmmo = -1;
private int _lastKills = -1;
private int _lastAttachments = -1;
private bool _visible;
private bool _built;
internal static void Attach()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Expected O, but got Unknown
if (Object.op_Implicit((Object)(object)_current))
{
return;
}
Transform fXCanvasTrans = PlayerUI.FXCanvasTrans;
if (Object.op_Implicit((Object)(object)fXCanvasTrans))
{
GameObject val = new GameObject("AmmoCounter UI", new Type[1] { typeof(RectTransform) });
val.transform.SetParent(fXCanvasTrans, false);
_current = val.AddComponent<AmmoUi>();
if (!_current.Build())
{
Object.Destroy((Object)(object)val);
_current = null;
}
}
}
internal static void Tick()
{
if (Object.op_Implicit((Object)(object)_current))
{
_current.Run();
}
}
internal static void Stop()
{
if (Object.op_Implicit((Object)(object)_current))
{
Object.Destroy((Object)(object)((Component)_current).gameObject);
}
_current = null;
}
private bool Build()
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: 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_0149: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
TextMeshProUGUI canvasTextPrefab = PlayerUI.CanvasTextPrefab;
if (!Object.op_Implicit((Object)(object)canvasTextPrefab))
{
return false;
}
_root = ((Component)this).GetComponent<RectTransform>();
_root.anchorMin = new Vector2(1f, 0f);
_root.anchorMax = new Vector2(1f, 0f);
_root.pivot = new Vector2(1f, 0f);
_root.sizeDelta = new Vector2(238f, 104f);
AddBackground();
_canvas = ((Component)this).gameObject.AddComponent<CanvasGroup>();
_canvas.alpha = 0f;
_canvas.blocksRaycasts = false;
_canvas.interactable = false;
float num = 206f;
_weaponName = AddText(canvasTextPrefab, "WeaponName", 19f, new Vector2(0f, 27f), new Vector2(num, 24f), (TextAlignmentOptions)516);
_statTrakText = AddText(canvasTextPrefab, "StatTrak", 15f, new Vector2(0f, 4f), new Vector2(num, 18f), (TextAlignmentOptions)516);
_ammoText = AddText(canvasTextPrefab, "Ammo", 34f, new Vector2(0f, -28f), new Vector2(num, 40f), (TextAlignmentOptions)516);
_attachmentImages = (RawImage[])(object)new RawImage[4]
{
AddImage("Sight"),
AddImage("Barrel"),
AddImage("Laser"),
AddImage("Magazine")
};
_previewCamera = MakeCamera();
_previewLight = MakeLight();
if (!Object.op_Implicit((Object)(object)_weaponName) || !Object.op_Implicit((Object)(object)_statTrakText) || !Object.op_Implicit((Object)(object)_ammoText) || !Object.op_Implicit((Object)(object)_previewCamera) || !Object.op_Implicit((Object)(object)_previewLight))
{
return false;
}
_built = true;
LocalizationApi.LanguageChanged += OnLanguageChanged;
Layout();
Show(to: false);
return true;
}
private void Run()
{
if (!_built || !Object.op_Implicit((Object)(object)Player.LocalPlayer))
{
Show(to: false);
return;
}
AddBackground();
Layout();
Player localPlayer = Player.LocalPlayer;
if (!Object.op_Implicit((Object)(object)localPlayer.Holding) || !Object.op_Implicit((Object)(object)localPlayer.Holding.HeldItem) || !Object.op_Implicit((Object)(object)localPlayer.Holding.HeldItem.Weapon) || localPlayer.Dying.IsDead || PauseManager.IsPaused || PlayerThinking.IsThinking || PlayerUI.UIDisabled)
{
Show(to: false);
return;
}
Weapon weapon = localPlayer.Holding.HeldItem.Weapon;
if (ConfigManager.HideWhileAiming && weapon.IsAds)
{
Show(to: false);
return;
}
int num = AttachmentState(weapon.Attachments);
Item heldItem = localPlayer.Holding.HeldItem;
int num2 = StatTrakRuntime.Get(heldItem);
if ((Object)(object)_lastWeapon != (Object)(object)weapon || _lastAmmo != weapon.Ammo || _lastKills != num2)
{
_lastWeapon = weapon;
_lastAmmo = weapon.Ammo;
_lastKills = num2;
SetText(heldItem, weapon, num2);
}
if (_lastAttachments != num)
{
_lastAttachments = num;
SetAttachments(weapon);
}
LayoutAttachments();
SetColors(weapon);
Show(to: true);
}
private void SetText(Item item, Weapon weapon, int kills)
{
((TMP_Text)_weaponName).text = I18n.Text("ammo.ui.weapon", item.GetName());
((TMP_Text)_statTrakText).text = I18n.Text("ammo.ui.stattrak", kills);
((TMP_Text)_ammoText).text = I18n.Text("ammo.ui.ammo", weapon.Ammo);
((Component)_weaponName).gameObject.SetActive(ConfigManager.ShowWeaponName);
((Component)_statTrakText).gameObject.SetActive(ConfigManager.ShowStatTrak);
}
private void SetColors(Weapon weapon)
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: 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)
//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
int num = Mathf.Max(1, weapon.Attachments.AmmoPerMag);
bool num2 = weapon.Ammo <= 1;
bool flag = !num2 && weapon.Ammo <= Mathf.CeilToInt((float)num * ConfigManager.LowAmmoThreshold);
Color val = (num2 ? ConfigManager.LastBulletColor : (flag ? ConfigManager.LowAmmoColor : ConfigManager.NormalColor));
float num3 = ((num2 && ConfigManager.FlashLastRound) ? Mathf.Lerp(0.35f, 1f, Mathf.PingPong(Time.unscaledTime * 4f, 1f)) : 1f);
val.a = Mathf.Clamp01(val.a * ConfigManager.Opacity * num3);
((Graphic)_ammoText).color = val;
Color normalColor = ConfigManager.NormalColor;
normalColor.a = Mathf.Clamp01(normalColor.a * ConfigManager.Opacity);
((Graphic)_weaponName).color = normalColor;
((Component)_weaponName).gameObject.SetActive(ConfigManager.ShowWeaponName);
((Graphic)_statTrakText).color = normalColor;
((Component)_statTrakText).gameObject.SetActive(ConfigManager.ShowStatTrak);
for (int i = 0; i < _attachmentImages.Length; i++)
{
if (((Component)_attachmentImages[i]).gameObject.activeSelf)
{
((Graphic)_attachmentImages[i]).color = new Color(1f, 1f, 1f, ConfigManager.Opacity);
}
}
}
private void Layout()
{
//IL_0011: 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_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
_root.anchoredPosition = new Vector2(0f - ConfigManager.OffsetX, ConfigManager.OffsetY);
((Transform)_root).localScale = Vector3.one * ConfigManager.Scale;
if (Object.op_Implicit((Object)(object)_background))
{
Color backgroundColor = _backgroundColor;
backgroundColor.a = Mathf.Clamp01(backgroundColor.a * ConfigManager.Opacity);
((Graphic)_background).color = backgroundColor;
}
}
private void Show(bool to)
{
if (_visible != to)
{
_visible = to;
_canvas.alpha = (to ? 1f : 0f);
}
}
private void SetAttachments(Weapon weapon)
{
Attachments attachments = weapon.Attachments;
DrawAttachment(0, (attachments.Sight == 0) ? null : ((Component)GetSight(attachments)).gameObject, temporary: false);
DrawAttachment(1, (attachments.BarrelAttachment == 0) ? null : ((Component)GetBarrel(attachments)).gameObject, temporary: false);
DrawAttachment(2, attachments.LaserSight ? ((Component)GetLaser(attachments)).gameObject : null, temporary: false);
DrawAttachment(3, attachments.ExtendedMag ? WeaponPreview(weapon) : null, temporary: true);
LayoutAttachments();
}
private void DrawAttachment(int index, GameObject model, bool temporary)
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: 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)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
//IL_015c: Expected O, but got Unknown
RawImage val = _attachmentImages[index];
((Component)val).gameObject.SetActive((Object)(object)model != (Object)null);
if (!Object.op_Implicit((Object)(object)model))
{
return;
}
GameObject val2 = Object.Instantiate<GameObject>(model);
if (temporary)
{
Object.Destroy((Object)(object)model);
}
((Object)val2).name = "AmmoCounter Preview Model";
val2.transform.position = Vector3.down * 10000f;
val2.transform.rotation = Quaternion.Euler(20f, 145f, 0f);
SetLayer(val2.transform, PreviewLayer);
DisableScripts(val2);
MeshRenderer[] componentsInChildren = val2.GetComponentsInChildren<MeshRenderer>(true);
if (componentsInChildren.Length == 0)
{
((Component)val).gameObject.SetActive(false);
Object.Destroy((Object)(object)val2);
return;
}
Bounds bounds = ((Renderer)componentsInChildren[0]).bounds;
for (int i = 1; i < componentsInChildren.Length; i++)
{
((Bounds)(ref bounds)).Encapsulate(((Renderer)componentsInChildren[i]).bounds);
}
((Component)_previewCamera).transform.position = ((Bounds)(ref bounds)).center - Vector3.forward * 10f;
((Component)_previewCamera).transform.rotation = Quaternion.identity;
_previewCamera.orthographicSize = Mathf.Max(((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.y) * 1.3f;
_previewCamera.targetTexture = (RenderTexture)val.texture;
_previewCamera.Render();
_previewCamera.targetTexture = null;
val2.SetActive(false);
Object.Destroy((Object)(object)val2);
}
private static Sight GetSight(Attachments attachments)
{
return (AccessTools.Field(typeof(Attachments), "_sights").GetValue(attachments) as List<Sight>)[attachments.Sight];
}
private static BarrelAttachment GetBarrel(Attachments attachments)
{
return (AccessTools.Field(typeof(Attachments), "_barrelAttachments").GetValue(attachments) as List<BarrelAttachment>)[attachments.BarrelAttachment];
}
private static LaserSight GetLaser(Attachments attachments)
{
object? value = AccessTools.Field(typeof(Attachments), "_laserSight").GetValue(attachments);
return (LaserSight)((value is LaserSight) ? value : null);
}
private static GameObject WeaponPreview(Weapon weapon)
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Expected O, but got Unknown
GameObject val = new GameObject("AmmoCounter Weapon Preview", new Type[2]
{
typeof(MeshFilter),
typeof(MeshRenderer)
});
val.GetComponent<MeshFilter>().sharedMesh = ((Item)weapon).Mesh;
List<Renderer> list = AccessTools.Field(typeof(Item), "_renderers").GetValue(weapon) as List<Renderer>;
((Renderer)val.GetComponent<MeshRenderer>()).sharedMaterials = list[0].sharedMaterials;
return val;
}
private static int AttachmentState(Attachments attachments)
{
return attachments.Sight | (attachments.BarrelAttachment << 8) | (attachments.LaserSight ? 65536 : 0) | (attachments.ExtendedMag ? 131072 : 0);
}
private void OnLanguageChanged(string language)
{
_lastWeapon = null;
_lastKills = -1;
}
private void OnDestroy()
{
LocalizationApi.LanguageChanged -= OnLanguageChanged;
if (Object.op_Implicit((Object)(object)_previewCamera))
{
Object.Destroy((Object)(object)((Component)_previewCamera).gameObject);
}
if (Object.op_Implicit((Object)(object)_previewLight))
{
Object.Destroy((Object)(object)((Component)_previewLight).gameObject);
}
int num = 0;
while (_attachmentImages != null && num < _attachmentImages.Length)
{
Texture texture = _attachmentImages[num].texture;
RenderTexture val = (RenderTexture)(object)((texture is RenderTexture) ? texture : null);
if (val != null)
{
val.Release();
Object.Destroy((Object)(object)val);
}
num++;
}
if ((Object)(object)_current == (Object)(object)this)
{
_current = null;
}
}
private static TextMeshProUGUI AddText(TextMeshProUGUI template, string name, float size, Vector2 position, Vector2 dimensions, TextAlignmentOptions alignment)
{
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
TextMeshProUGUI obj = Object.Instantiate<TextMeshProUGUI>(template, ((Component)_current).transform, false);
((Object)((Component)obj).gameObject).name = name;
((Component)obj).gameObject.SetActive(true);
DisableScripts(((Component)obj).gameObject);
((TMP_Text)obj).fontSize = size;
((TMP_Text)obj).alignment = alignment;
((Graphic)obj).raycastTarget = false;
((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
RectTransform rectTransform = ((TMP_Text)obj).rectTransform;
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.anchoredPosition = position;
rectTransform.sizeDelta = dimensions;
return obj;
}
private RawImage AddImage(string name)
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Expected O, but got Unknown
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Expected O, but got Unknown
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject(name, new Type[3]
{
typeof(RectTransform),
typeof(CanvasRenderer),
typeof(RawImage)
});
val.transform.SetParent(((Component)this).transform, false);
RawImage component = val.GetComponent<RawImage>();
((Graphic)component).raycastTarget = false;
component.texture = (Texture)new RenderTexture(64, 64, 16, (RenderTextureFormat)0);
((RenderTexture)component.texture).Create();
RectTransform rectTransform = ((Graphic)component).rectTransform;
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.anchoredPosition = new Vector2(52f, 10f);
rectTransform.sizeDelta = new Vector2(20f, 20f);
val.SetActive(false);
return component;
}
private void LayoutAttachments()
{
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
int num = 0;
for (int i = 0; i < _attachmentImages.Length; i++)
{
if (((Component)_attachmentImages[i]).gameObject.activeSelf)
{
num++;
}
}
float num2 = 20f * ConfigManager.AttachmentScale;
float num3 = 4f * ConfigManager.AttachmentScale;
float num4 = 52f + num2 * 0.5f + 6f;
float num5 = (float)num * num2 + (float)(num - 1) * num3;
float num6 = 103f - num5 + num2 * 0.5f;
for (int j = 0; j < _attachmentImages.Length; j++)
{
if (((Component)_attachmentImages[j]).gameObject.activeSelf)
{
((Graphic)_attachmentImages[j]).rectTransform.sizeDelta = new Vector2(num2, num2);
((Graphic)_attachmentImages[j]).rectTransform.anchoredPosition = new Vector2(num6, num4);
num6 += num2 + num3;
}
}
}
private void AddBackground()
{
//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)
if (!Object.op_Implicit((Object)(object)_background))
{
Image val = FindBackground();
if (Object.op_Implicit((Object)(object)val))
{
_background = ((Component)this).gameObject.AddComponent<Image>();
CopyImage(val, _background);
((Graphic)_background).raycastTarget = false;
_backgroundColor = ((Graphic)_background).color;
}
}
}
private static Image FindBackground()
{
Player localPlayer = Player.LocalPlayer;
if (!Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)localPlayer.Inventory))
{
return null;
}
if (!(AccessTools.Field(typeof(PlayerInventory), "_itemSlots").GetValue(localPlayer.Inventory) is List<InventorySlot> list))
{
return null;
}
for (int i = 0; i < list.Count; i++)
{
if (!Object.op_Implicit((Object)(object)list[i]))
{
continue;
}
Image[] componentsInChildren = ((Component)list[i]).GetComponentsInChildren<Image>(true);
foreach (Image val in componentsInChildren)
{
if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)((Graphic)val).material) && ((Object)((Graphic)val).material).name == "UIBlur")
{
return val;
}
}
}
return null;
}
private static void CopyImage(Image source, Image target)
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
target.sprite = source.sprite;
target.type = source.type;
target.preserveAspect = source.preserveAspect;
target.fillCenter = source.fillCenter;
target.fillMethod = source.fillMethod;
target.fillAmount = source.fillAmount;
target.fillClockwise = source.fillClockwise;
target.fillOrigin = source.fillOrigin;
target.pixelsPerUnitMultiplier = source.pixelsPerUnitMultiplier;
((Graphic)target).material = ((Graphic)source).material;
((Graphic)target).color = ((Graphic)source).color;
}
private static Camera MakeCamera()
{
//IL_0018: 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)
Camera component = new GameObject("AmmoCounter Preview Camera", new Type[1] { typeof(Camera) }).GetComponent<Camera>();
((Behaviour)component).enabled = false;
component.orthographic = true;
component.clearFlags = (CameraClearFlags)2;
component.backgroundColor = Color.clear;
component.cullingMask = 1 << PreviewLayer;
component.nearClipPlane = 0.01f;
component.farClipPlane = 20f;
return component;
}
private static Light MakeLight()
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
Light component = new GameObject("AmmoCounter Preview Light", new Type[1] { typeof(Light) }).GetComponent<Light>();
component.type = (LightType)1;
component.intensity = 1.2f;
component.cullingMask = 1 << PreviewLayer;
((Component)component).transform.rotation = Quaternion.Euler(35f, -35f, 0f);
return component;
}
private static void SetLayer(Transform root, int layer)
{
((Component)root).gameObject.layer = layer;
for (int i = 0; i < root.childCount; i++)
{
SetLayer(root.GetChild(i), layer);
}
}
private static void DisableScripts(GameObject root)
{
MonoBehaviour[] componentsInChildren = root.GetComponentsInChildren<MonoBehaviour>(true);
foreach (MonoBehaviour val in componentsInChildren)
{
if (!(val is Graphic) && !(val is Selectable) && !(val is TMP_Text) && !(val is Shadow) && !(val is LayoutGroup) && !(val is LayoutElement) && !(val is ContentSizeFitter) && !(val is RectMask2D))
{
((Behaviour)val).enabled = false;
}
}
}
}
}
namespace AmmoCounter.Patches
{
[HarmonyPatch(typeof(PlayerUI), "Update")]
internal static class AmmoInputPatch
{
private static void Postfix()
{
AmmoRuntime.Tick();
}
}
[HarmonyPatch(typeof(Player), "OnStartClient")]
internal static class PlayerStartPatch
{
private static void Postfix(Player __instance)
{
AmmoRuntime.Start(__instance);
}
}
[HarmonyPatch(typeof(Player), "OnStopClient")]
internal static class PlayerStopPatch
{
private static void Postfix(Player __instance)
{
AmmoRuntime.Stop(__instance);
}
}
[HarmonyPatch(typeof(PlayerSkills), "OnKill")]
internal static class StatTrakPatch
{
private static void Postfix()
{
AmmoRuntime.Kill();
}
}
[HarmonyPatch(typeof(MoneyManager), "OnStartServer")]
internal static class StatTrakServerStartPatch
{
private static void Postfix()
{
StatTrakRuntime.ServerStart();
}
}
[HarmonyPatch(typeof(MoneyManager), "OnStartClient")]
internal static class StatTrakClientStartPatch
{
private static void Postfix()
{
StatTrakRuntime.SetupNet();
}
}
[HarmonyPatch(typeof(SaveManager), "SaveServer")]
internal static class StatTrakSavePatch
{
private static void Prefix()
{
StatTrakRuntime.Save();
}
}
[HarmonyPatch(typeof(Server), "OnStopServer")]
internal static class StatTrakStopPatch
{
private static void Postfix()
{
StatTrakRuntime.ServerStop();
}
}
}
namespace AmmoCounter.Localization
{
internal static class I18n
{
private const string FileName = "AmmoCounter.Localization.json";
private static readonly ModLocalizer _localizer = Load();
internal static string Text(string key, params object[] args)
{
return _localizer.GetLocalizedText(key, args);
}
private static ModLocalizer Load()
{
string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
ModLocalizer obj = LocalizationApi.For("IceBoxStudio.HowToFish.AmmoCounter");
obj.RegisterJson(Path.Combine(directoryName, "AmmoCounter.Localization.json"));
return obj;
}
}
}
namespace AmmoCounter.Configuration
{
internal static class ConfigManager
{
private static ConfigEntry<float> _offsetX;
private static ConfigEntry<float> _offsetY;
private static ConfigEntry<float> _scale;
private static ConfigEntry<float> _attachmentScale;
private static ConfigEntry<float> _opacity;
private static ConfigEntry<float> _lowAmmoThreshold;
private static ConfigEntry<bool> _showWeaponName;
private static ConfigEntry<bool> _showStatTrak;
private static ConfigEntry<bool> _hideWhileAiming;
private static ConfigEntry<bool> _flashLastRound;
private static ConfigEntry<Color> _normalColor;
private static ConfigEntry<Color> _lowAmmoColor;
private static ConfigEntry<Color> _lastBulletColor;
internal static float OffsetX => Mathf.Max(0f, _offsetX.Value);
internal static float OffsetY => Mathf.Max(0f, _offsetY.Value);
internal static float Scale => Mathf.Clamp(_scale.Value, 0.5f, 2f);
internal static float AttachmentScale => Mathf.Clamp(_attachmentScale.Value, 0.5f, 3f);
internal static float Opacity => Mathf.Clamp01(_opacity.Value);
internal static float LowAmmoThreshold => Mathf.Clamp01(_lowAmmoThreshold.Value);
internal static bool ShowWeaponName => _showWeaponName.Value;
internal static bool ShowStatTrak => _showStatTrak.Value;
internal static bool HideWhileAiming => _hideWhileAiming.Value;
internal static bool FlashLastRound => _flashLastRound.Value;
internal static Color NormalColor => _normalColor.Value;
internal static Color LowAmmoColor => _lowAmmoColor.Value;
internal static Color LastBulletColor => _lastBulletColor.Value;
internal static void Init(ConfigFile config)
{
//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
//IL_0223: Unknown result type (might be due to invalid IL or missing references)
_offsetX = config.Bind<float>("UI", "OffsetX", 32f, I18n.Text("ammo.config.offset_x"));
_offsetY = config.Bind<float>("UI", "OffsetY", 32f, I18n.Text("ammo.config.offset_y"));
_scale = config.Bind<float>("UI", "Scale", 1f, I18n.Text("ammo.config.scale"));
_attachmentScale = config.Bind<float>("UI", "AttachmentScale", 1f, I18n.Text("ammo.config.attachment_scale"));
_opacity = config.Bind<float>("UI", "Opacity", 0.86f, I18n.Text("ammo.config.opacity"));
_lowAmmoThreshold = config.Bind<float>("UI", "LowAmmoThreshold", 0.25f, I18n.Text("ammo.config.low_threshold"));
_showWeaponName = config.Bind<bool>("UI", "ShowWeaponName", true, I18n.Text("ammo.config.show_weapon_name"));
_showStatTrak = config.Bind<bool>("UI", "ShowStatTrak", true, I18n.Text("ammo.config.show_stattrak"));
_hideWhileAiming = config.Bind<bool>("UI", "HideWhileAiming", false, I18n.Text("ammo.config.hide_aiming"));
_flashLastRound = config.Bind<bool>("UI", "FlashLastRound", true, I18n.Text("ammo.config.flash_last_round"));
_normalColor = config.Bind<Color>("Colors", "NormalColor", new Color(0.86f, 0.94f, 1f, 1f), I18n.Text("ammo.config.normal_color"));
_lowAmmoColor = config.Bind<Color>("Colors", "LowAmmoColor", new Color(1f, 0.82f, 0.35f, 1f), I18n.Text("ammo.config.low_color"));
_lastBulletColor = config.Bind<Color>("Colors", "LastBulletColor", new Color(1f, 0.38f, 0.24f, 1f), I18n.Text("ammo.config.last_color"));
config.Save();
}
}
}