Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SellItems v1.0.0
plugins/RepoSellMod.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RepoSellMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+40b412640cf136b1be8f1ec4faadc63cb76a0d48")] [assembly: AssemblyProduct("RepoSellMod")] [assembly: AssemblyTitle("RepoSellMod")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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; } } } namespace RepoSellMod { internal static class MyPluginInfo { public const string PLUGIN_GUID = "com.mods.sellitems"; public const string PLUGIN_NAME = "SellItems"; public const string PLUGIN_VERSION = "1.0.0"; } [HarmonyPatch(typeof(ShopManager), "ShopInitialize")] internal static class ShopManager_ShopInitialize_Patch { private static void Postfix(ShopManager __instance) { if (!((Object)(object)SellManager.Instance == (Object)null)) { CacheItemList(__instance.potentialItems); CacheItemList(__instance.potentialItemUpgrades); CacheItemList(__instance.potentialItemConsumables); CacheItemList(__instance.potentialItemHealthPacks); Plugin.Log.LogDebug((object)"[SellMod] Price cache updated on shop open."); } } private static void CacheItemList(List<Item> items) { if (items == null) { return; } foreach (Item item in items) { if (!((Object)(object)item == (Object)null)) { int num = Mathf.CeilToInt(item.value.valueMax / 1000f * 4f); if (num > 0) { SellManager.Instance.RegisterItemPrice(((Object)item).name, num); } } } } } [HarmonyPatch(typeof(ShopManager), "ShoppingListItemAdd")] internal static class ShopManager_ShoppingListItemAdd_Patch { private static void Prefix(ItemAttributes item) { if (!((Object)(object)item?.item == (Object)null) && !((Object)(object)SellManager.Instance == (Object)null)) { int num = Mathf.CeilToInt(item.item.value.valueMax / 1000f * 4f); if (num > 0) { SellManager.Instance.RegisterItemPrice(((Object)item.item).name, num); Plugin.Log.LogDebug((object)$"[SellMod] Added to cart: {((Object)item.item).name} = {num}$"); } } } } [BepInPlugin("com.mods.sellitems", "SellItems", "1.0.0")] public class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("com.mods.sellitems"); internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } public static ConfigEntry<KeyCode> SellKey { get; private set; } public static ConfigEntry<float> SellRefundRatio { get; private set; } public static ConfigEntry<bool> ShowSellHint { get; private set; } public static ConfigEntry<bool> SellWeapons { get; private set; } public static ConfigEntry<bool> SellUpgrades { get; private set; } public static ConfigEntry<bool> SellConsumables { get; private set; } public static ConfigEntry<bool> SellAll { get; private set; } public static ConfigEntry<bool> ShowDebugOverlay { get; private set; } private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"=== RepoSellMod: Awake() started ==="); BindConfig(); Log.LogInfo((object)$"Config loaded. Sell key: [{SellKey.Value}], Refund: {SellRefundRatio.Value * 100f:0}%"); _harmony.PatchAll(); Log.LogInfo((object)"Harmony patches applied."); ((Component)this).gameObject.AddComponent<SellManager>(); Log.LogInfo((object)"SellManager added."); Log.LogInfo((object)"=== SellItems v1.0.0 loaded! ==="); } private void BindConfig() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown SellKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "SellKey", (KeyCode)120, "Key to initiate selling the held item"); SellRefundRatio = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SellRefundRatio", 0.5f, new ConfigDescription("Fraction of the item price returned on sell (0.0–1.0). 0.5 = 50%", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); ShowSellHint = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowSellHint", true, "Show the [X] Sell hint while holding a sellable item"); SellWeapons = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "SellWeapons", true, "Allow selling weapons"); SellUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "SellUpgrades", true, "Allow selling upgrades"); SellConsumables = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "SellConsumables", false, "Allow selling consumables"); SellAll = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "SellAll", false, "Allow selling ANY item (overrides all category flags above)"); ShowDebugOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowDebugOverlay", false, "Show the debug overlay in the top-left corner"); } private void OnDestroy() { _harmony.UnpatchSelf(); } } public enum ItemCategory { Unknown, Weapon, Upgrade, Consumable, Other } public class SellManager : MonoBehaviour { private readonly Dictionary<string, int> _priceCache = new Dictionary<string, int>(); private GUIStyle _hintStyle; private bool _styleInit; private bool _updateLogged; private GameObject _pendingSell; private float _pendingTimer; private static readonly FieldInfo _grabbedObjField = typeof(PhysGrabber).GetField("grabbedPhysGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); public static SellManager Instance { get; private set; } private void Awake() { Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log.LogInfo((object)"[SellMod] SellManager.Awake() — OK"); } private void Update() { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) if (!_updateLogged) { Plugin.Log.LogInfo((object)"[SellMod] SellManager.Update() is running"); _updateLogged = true; } if (!IsInLobby()) { _pendingSell = null; return; } if ((Object)(object)_pendingSell != (Object)null && (Object)(object)GetHeldItem() != (Object)(object)_pendingSell) { _pendingSell = null; } if ((Object)(object)_pendingSell != (Object)null) { _pendingTimer -= Time.deltaTime; if (_pendingTimer <= 0f) { _pendingSell = null; Plugin.Log.LogInfo((object)"[SellMod] Confirmation timed out"); } } if ((Object)(object)_pendingSell != (Object)null && Input.GetKeyDown((KeyCode)13)) { GameObject pendingSell = _pendingSell; _pendingSell = null; TrySell(pendingSell); } else { if (!Input.GetKeyDown(Plugin.SellKey.Value)) { return; } if ((Object)(object)_pendingSell != (Object)null) { _pendingSell = null; SemiFunc.UIFocusText("Sale cancelled.", Color.white, Color.white, 1.5f); return; } GameObject heldItem = GetHeldItem(); if (!((Object)(object)heldItem == (Object)null)) { if (!IsSellable(heldItem, out var reason)) { Plugin.Log.LogInfo((object)("[SellMod] Cannot sell: " + reason)); SemiFunc.UIFocusText("Cannot sell: " + reason, Color.red, Color.white, 2f); return; } int refundAmount = GetRefundAmount(heldItem); _pendingSell = heldItem; _pendingTimer = 5f; SemiFunc.UIFocusText((refundAmount > 0) ? $"Sell for {refundAmount}$? [Enter] yes [X] cancel" : "Discard item? [Enter] yes [X] cancel", Color.yellow, Color.white, 5f); Plugin.Log.LogInfo((object)("[SellMod] Awaiting confirmation: " + ((Object)heldItem).name)); } } } private void OnGUI() { //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) try { InitStyle(); bool flag = IsInLobby(); PhysGrabber instance = PhysGrabber.instance; bool flag2 = (Object)(object)instance != (Object)null && instance.grabbed; PhysGrabObject val = (PhysGrabObject)(flag2 ? /*isinst with value type is only supported in some contexts*/: null); if (Plugin.ShowDebugOverlay.Value) { object[] obj = new object[4] { flag, flag2, null, null }; object obj2; if (val == null) { obj2 = null; } else { GameObject gameObject = ((Component)val).gameObject; obj2 = ((gameObject != null) ? ((Object)gameObject).name : null); } if (obj2 == null) { obj2 = "none"; } obj[2] = obj2; GameObject pendingSell = _pendingSell; obj[3] = ((pendingSell != null) ? ((Object)pendingSell).name : null) ?? "none"; string text = string.Format("[SellMod] lobby:{0} grabbed:{1} item:{2} pending:{3}", obj); GUI.Label(new Rect(10f, 10f, 700f, 24f), text, _hintStyle); } if (!Plugin.ShowSellHint.Value || !flag || (Object)(object)val == (Object)null) { return; } string text2; if ((Object)(object)_pendingSell == (Object)(object)((Component)val).gameObject) { text2 = "[Enter] confirm sell [X] cancel"; } else { if (!IsSellable(((Component)val).gameObject, out var _)) { return; } int refundAmount = GetRefundAmount(((Component)val).gameObject); text2 = ((refundAmount > 0) ? $"[{Plugin.SellKey.Value}] Sell ({refundAmount}$)" : $"[{Plugin.SellKey.Value}] Discard (0$)"); } float num = 340f; float num2 = 32f; float num3 = ((float)Screen.width - num) / 2f; float num4 = (float)Screen.height - num2 - 80f; GUI.Label(new Rect(num3 - 1f, num4 + 1f, num, num2), text2, ShadowStyle()); GUI.Label(new Rect(num3, num4, num, num2), text2, _hintStyle); } catch (Exception arg) { Plugin.Log.LogError((object)$"[SellMod] OnGUI exception: {arg}"); } } public bool TrySell(GameObject item) { //IL_0036: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if (!IsSellable(item, out var reason)) { Plugin.Log.LogInfo((object)("[SellMod] Cannot sell '" + ((Object)item).name + "': " + reason)); SemiFunc.UIFocusText("Cannot sell: " + reason, Color.red, Color.white, 2f); return false; } int refundAmount = GetRefundAmount(item); if (refundAmount > 0) { GiveMoneyToPlayer(refundAmount); } ItemAttributes obj = item.GetComponentInParent<ItemAttributes>(true) ?? item.GetComponent<ItemAttributes>(); object obj2; if (obj == null) { obj2 = null; } else { Item item2 = obj.item; obj2 = ((item2 != null) ? ((Object)item2).name : null); } string text = (string)obj2; SemiFunc.UIFocusText((refundAmount > 0) ? $"Sold for {refundAmount}$!" : "Item discarded.", Color.green, Color.white, 2f); Plugin.Log.LogInfo((object)$"[SellMod] Sold '{((Object)item).name}' for {refundAmount}$"); DestroyItem(item); if (!string.IsNullOrEmpty(text)) { RemoveFromStats(text); } else { Plugin.Log.LogWarning((object)("[SellMod] Could not get itemName for " + ((Object)item).name)); } return true; } public bool IsSellable(GameObject item, out string reason) { reason = string.Empty; if ((Object)(object)item == (Object)null) { reason = "null"; return false; } if ((Object)(object)item.GetComponentInParent<PlayerAvatar>(true) != (Object)null) { reason = "that's a player"; return false; } ItemAttributes obj = item.GetComponentInParent<ItemAttributes>(true) ?? item.GetComponent<ItemAttributes>(); object obj2; if (obj == null) { obj2 = null; } else { Item item2 = obj.item; obj2 = ((item2 != null) ? ((Object)item2).name : null); } string text = (string)obj2; if (!string.IsNullOrEmpty(text)) { StatsManager instance = StatsManager.instance; if ((Object)(object)instance != (Object)null && (!instance.itemsPurchased.ContainsKey(text) || instance.itemsPurchased[text] <= 0)) { reason = "item was not purchased"; return false; } } if (Plugin.SellAll.Value) { return true; } switch (GetCategory(item)) { case ItemCategory.Weapon: if (Plugin.SellWeapons.Value) { return true; } reason = "weapon selling is disabled"; return false; case ItemCategory.Upgrade: if (Plugin.SellUpgrades.Value) { return true; } reason = "upgrade selling is disabled"; return false; case ItemCategory.Consumable: if (Plugin.SellConsumables.Value) { return true; } reason = "consumable selling is disabled"; return false; default: reason = "unknown category, enable SellAll"; return false; } } public static ItemCategory GetCategory(GameObject item) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 if ((Object)(object)item == (Object)null) { return ItemCategory.Unknown; } ItemAttributes val = item.GetComponentInChildren<ItemAttributes>(true) ?? item.GetComponentInParent<ItemAttributes>(true); if ((Object)(object)val?.item != (Object)null) { itemType itemType = val.item.itemType; if ((int)itemType == 3) { return ItemCategory.Upgrade; } if ((int)itemType == 8) { return ItemCategory.Consumable; } if ((int)itemType == 5) { return ItemCategory.Consumable; } return ItemCategory.Weapon; } string source = ((Object)item).name.ToLowerInvariant().Replace("(clone)", "").Trim(); if (ContainsAny(source, "upgrade", "module", "boost", "enhancement", "chip")) { return ItemCategory.Upgrade; } if (ContainsAny(source, "medkit", "health", "crystal", "grenade", "bandage", "stim", "pack", "ammo")) { return ItemCategory.Consumable; } if (ContainsAny(source, "gun", "rifle", "pistol", "shotgun", "weapon", "sword", "blade", "melee", "bow", "launcher")) { return ItemCategory.Weapon; } Component[] components = item.GetComponents<Component>(); foreach (Component val2 in components) { if (!((Object)(object)val2 == (Object)null)) { string source2 = ((object)val2).GetType().Name.ToLowerInvariant(); if (ContainsAny(source2, "weapon", "gun", "shoot", "fire")) { return ItemCategory.Weapon; } if (ContainsAny(source2, "upgrade", "module", "boost")) { return ItemCategory.Upgrade; } } } return ItemCategory.Unknown; } public int GetRefundAmount(GameObject item) { return Mathf.RoundToInt((float)GetItemPrice(item) * Plugin.SellRefundRatio.Value); } public int GetItemPrice(GameObject item) { if ((Object)(object)item == (Object)null) { return 0; } string itemKey = GetItemKey(item); if (_priceCache.TryGetValue(itemKey, out var value)) { return value; } ItemAttributes val = item.GetComponentInChildren<ItemAttributes>(true) ?? item.GetComponentInParent<ItemAttributes>(true); if ((Object)(object)val?.item != (Object)null) { int num = Mathf.CeilToInt(val.item.value.valueMax / 1000f * 4f); if (num > 0) { _priceCache[itemKey] = num; return num; } } return 0; } public void RegisterItemPrice(string itemName, int price) { _priceCache[itemName.ToLowerInvariant()] = price; Plugin.Log.LogInfo((object)$"[SellMod] Price registered: {itemName} = {price}$"); } private static bool IsInLobby() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } if ((int)GameDirector.instance.currentState != 2) { return false; } return SemiFunc.RunIsLobby(); } catch { return false; } } private static GameObject GetHeldItem() { PhysGrabber instance = PhysGrabber.instance; if ((Object)(object)instance == (Object)null || !instance.grabbed) { return null; } object? obj = _grabbedObjField?.GetValue(instance); object? obj2 = ((obj is PhysGrabObject) ? obj : null); if (obj2 == null) { return null; } return ((Component)obj2).gameObject; } private static void GiveMoneyToPlayer(int amount) { int num = SemiFunc.StatGetRunCurrency(); SemiFunc.StatSetRunCurrency(num + amount); SemiFunc.ShopUpdateCost(); Plugin.Log.LogInfo((object)$"[SellMod] +{amount}$ (was {num}$, now {num + amount}$)"); } private static void DestroyItem(GameObject item) { PhysGrabObject val = item.GetComponentInParent<PhysGrabObject>(true) ?? item.GetComponent<PhysGrabObject>(); GameObject val2 = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : item); PhotonView component = val2.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && PhotonNetwork.IsConnected) { if (!component.IsMine) { component.TransferOwnership(PhotonNetwork.MasterClient); } if (PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(component); Plugin.Log.LogInfo((object)"[SellMod] Item destroyed via PhotonNetwork.Destroy (host)"); return; } PhotonView component2 = ((Component)Instance).GetComponent<PhotonView>(); if (component2 != null) { component2.RPC("RequestDestroyRPC", (RpcTarget)2, new object[1] { component.ViewID }); } Plugin.Log.LogInfo((object)$"[SellMod] RPC destroy request sent to host, viewID:{component.ViewID}"); } else { Object.Destroy((Object)(object)val2); Plugin.Log.LogInfo((object)"[SellMod] Item destroyed locally (no PhotonView)"); } } [PunRPC] public void RequestDestroyRPC(int viewID) { if (PhotonNetwork.IsMasterClient) { PhotonView val = PhotonView.Find(viewID); if ((Object)(object)val != (Object)null) { Plugin.Log.LogInfo((object)$"[SellMod] Host destroying viewID:{viewID} on client request"); PhotonNetwork.Destroy(val); } } } private static void RemoveFromStats(string itemName) { try { StatsManager instance = StatsManager.instance; if (!((Object)(object)instance == (Object)null)) { if (instance.itemsPurchased.ContainsKey(itemName) && instance.itemsPurchased[itemName] > 0) { instance.itemsPurchased[itemName]--; Plugin.Log.LogInfo((object)$"[SellMod] Purchase count '{itemName}': {instance.itemsPurchased[itemName] + 1} → {instance.itemsPurchased[itemName]}"); } else { Plugin.Log.LogWarning((object)("[SellMod] '" + itemName + "' not found in itemsPurchased or already 0")); } } } catch (Exception ex) { Plugin.Log.LogError((object)("[SellMod] RemoveFromStats: " + ex.Message)); } } private static string GetItemKey(GameObject item) { return ((Object)item).name.ToLowerInvariant().Replace("(clone)", "").Trim(); } private static bool ContainsAny(string source, params string[] kw) { return kw.Any(source.Contains); } private void InitStyle() { //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) //IL_0021: 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) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown if (!_styleInit) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; val.normal.textColor = Color.white; _hintStyle = val; _styleInit = true; } } private GUIStyle ShadowStyle() { //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_001c: Expected O, but got Unknown GUIStyle val = new GUIStyle(_hintStyle); val.normal.textColor = Color.black; return val; } } public static class SellModAPI { public static void RegisterItemPrice(string itemName, int price) { if ((Object)(object)SellManager.Instance == (Object)null) { Plugin.Log.LogWarning((object)"[SellMod API] SellManager is not yet initialized."); } else { SellManager.Instance.RegisterItemPrice(itemName, price); } } public static bool TrySell(GameObject item) { if ((Object)(object)SellManager.Instance == (Object)null) { return false; } return SellManager.Instance.TrySell(item); } public static bool IsSellable(GameObject item) { if ((Object)(object)SellManager.Instance == (Object)null) { return false; } string reason; return SellManager.Instance.IsSellable(item, out reason); } public static ItemCategory GetCategory(GameObject item) { return SellManager.GetCategory(item); } public static int GetRefundAmount(GameObject item) { if ((Object)(object)SellManager.Instance == (Object)null) { return 0; } return SellManager.Instance.GetRefundAmount(item); } public static int GetItemPrice(GameObject item) { if ((Object)(object)SellManager.Instance == (Object)null) { return 0; } return SellManager.Instance.GetItemPrice(item); } } }