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 Entries = new List(); } [Serializable] private sealed class SaveEntry { public string PlayerKey; public int ItemId; public int Kills; } private static readonly Dictionary> ServerKills = new Dictionary>(); private static readonly Dictionary ClientKills = new Dictionary(); 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(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((Action)GotSync); _clientRegistered = true; } if (!_serverRegistered && (Object)(object)InstanceFinder.ServerManager != (Object)null) { InstanceFinder.ServerManager.RegisterBroadcast((Action)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 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(); 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(((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(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(); 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> serverKill in ServerKills) { foreach (KeyValuePair 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.SetWrite((Action)WriteKill); GenericReader.SetRead((Func)ReadKill); GenericWriter.SetWrite((Action)WriteSync); GenericReader.SetRead((Func)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(); 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(); _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(); _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(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(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)[attachments.Sight]; } private static BarrelAttachment GetBarrel(Attachments attachments) { return (AccessTools.Field(typeof(Attachments), "_barrelAttachments").GetValue(attachments) as List)[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().sharedMesh = ((Item)weapon).Mesh; List list = AccessTools.Field(typeof(Item), "_renderers").GetValue(weapon) as List; ((Renderer)val.GetComponent()).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(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(); ((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(); 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 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(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(); ((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(); 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(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 _offsetX; private static ConfigEntry _offsetY; private static ConfigEntry _scale; private static ConfigEntry _attachmentScale; private static ConfigEntry _opacity; private static ConfigEntry _lowAmmoThreshold; private static ConfigEntry _showWeaponName; private static ConfigEntry _showStatTrak; private static ConfigEntry _hideWhileAiming; private static ConfigEntry _flashLastRound; private static ConfigEntry _normalColor; private static ConfigEntry _lowAmmoColor; private static ConfigEntry _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("UI", "OffsetX", 32f, I18n.Text("ammo.config.offset_x")); _offsetY = config.Bind("UI", "OffsetY", 32f, I18n.Text("ammo.config.offset_y")); _scale = config.Bind("UI", "Scale", 1f, I18n.Text("ammo.config.scale")); _attachmentScale = config.Bind("UI", "AttachmentScale", 1f, I18n.Text("ammo.config.attachment_scale")); _opacity = config.Bind("UI", "Opacity", 0.86f, I18n.Text("ammo.config.opacity")); _lowAmmoThreshold = config.Bind("UI", "LowAmmoThreshold", 0.25f, I18n.Text("ammo.config.low_threshold")); _showWeaponName = config.Bind("UI", "ShowWeaponName", true, I18n.Text("ammo.config.show_weapon_name")); _showStatTrak = config.Bind("UI", "ShowStatTrak", true, I18n.Text("ammo.config.show_stattrak")); _hideWhileAiming = config.Bind("UI", "HideWhileAiming", false, I18n.Text("ammo.config.hide_aiming")); _flashLastRound = config.Bind("UI", "FlashLastRound", true, I18n.Text("ammo.config.flash_last_round")); _normalColor = config.Bind("Colors", "NormalColor", new Color(0.86f, 0.94f, 1f, 1f), I18n.Text("ammo.config.normal_color")); _lowAmmoColor = config.Bind("Colors", "LowAmmoColor", new Color(1f, 0.82f, 0.35f, 1f), I18n.Text("ammo.config.low_color")); _lastBulletColor = config.Bind("Colors", "LastBulletColor", new Color(1f, 0.38f, 0.24f, 1f), I18n.Text("ammo.config.last_color")); config.Save(); } } }