Иконка ресурса

FREE InfiniteVendingStock : 1.3.4 1.3.4

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Reflection;
using Oxide.Core.Plugins;

namespace Oxide.Plugins
{
    [Info("Infinite Vending Stock", "Rustic0, Improvements made by SeesAll", "1.3.4")]
    [Description("Keeps stock high for NPC vending machines, including shops managed by CustomVendingSetup.")]
    public class InfiniteVendingStock : RustPlugin
    {
        [PluginReference]
        private Plugin CustomVendingSetup;

        private const int TargetSalesPerOrder = 100000;

        private readonly HashSet<ulong> _queuedRefreshes = new HashSet<ulong>();
        private readonly HashSet<ulong> _queuedUiUpdates = new HashSet<ulong>();

        private static readonly FieldInfo SellOrderSellSkinIdField = GetSellOrderField("sellSkinId", "itemToSellSkin");
        private static readonly FieldInfo SellOrderItemToSellAsBpField = GetSellOrderField("itemToSellAsBP");

        private void Unload()
        {
            _queuedRefreshes.Clear();
            _queuedUiUpdates.Clear();
        }

        private void OnServerInitialized()
        {
            foreach (BaseNetworkable entity in BaseNetworkable.serverEntities)
            {
                NPCVendingMachine vendingMachine = entity as NPCVendingMachine;
                if (vendingMachine == null)
                    continue;

                ScheduleInitialization(vendingMachine);
            }
        }

        private void OnEntitySpawned(NPCVendingMachine vendingMachine)
        {
            ScheduleInitialization(vendingMachine);
        }

        private void OnVendingTransaction(
            NPCVendingMachine vendingMachine,
            BasePlayer buyer,
            int sellOrderId,
            int numberOfTransactions,
            ItemContainer targetContainer)
        {
            ApplyInfiniteStock(vendingMachine);
            QueueRefresh(vendingMachine);
        }

        private void OnRefreshVendingStock(VendingMachine vendingMachine, ItemDefinition itemDefinition)
        {
            NPCVendingMachine npcVendingMachine = vendingMachine as NPCVendingMachine;
            if (npcVendingMachine == null)
                return;

            // Rust invokes this hook immediately before it calculates sellOrder.inStock.
            // Replenish synchronously so that calculation sees the increased amount.
            ApplyInfiniteStock(npcVendingMachine);
        }

        private void ScheduleInitialization(NPCVendingMachine vendingMachine)
        {
            QueueRefresh(vendingMachine);

            // NPC inventories may be populated after the entity/server hook during startup.
            // These bounded retries avoid a permanent miss without adding a polling timer.
            timer.Once(1f, delegate { QueueRefresh(vendingMachine); });
            timer.Once(5f, delegate { QueueRefresh(vendingMachine); });
        }

        private void QueueRefresh(NPCVendingMachine vendingMachine)
        {
            if (!ShouldManage(vendingMachine))
                return;

            ulong networkId = GetQueueKey(vendingMachine);
            if (!_queuedRefreshes.Add(networkId))
                return;

            NextTick(delegate()
            {
                _queuedRefreshes.Remove(networkId);

                if (!ShouldManage(vendingMachine))
                    return;

                ApplyInfiniteStock(vendingMachine);
            });
        }

        private bool ApplyInfiniteStock(NPCVendingMachine vendingMachine)
        {
            if (!ShouldManage(vendingMachine))
                return false;

            ItemContainer inventory = vendingMachine.inventory;
            if (inventory == null || inventory.itemList == null || vendingMachine.sellOrders == null || vendingMachine.sellOrders.sellOrders == null)
                return false;

            List<Item> itemList = inventory.itemList;
            bool isCustomized = IsCustomized(vendingMachine);
            int orderCount = vendingMachine.sellOrders.sellOrders.Count;
            bool changed = false;

            for (int orderIndex = 0; orderIndex < orderCount; orderIndex++)
            {
                ProtoBuf.VendingMachine.SellOrder sellOrder = vendingMachine.sellOrders.sellOrders[orderIndex];
                if (sellOrder == null)
                    continue;

                int itemId = GetSoldItemId(sellOrder);
                int amountPerSale = GetSoldAmount(sellOrder);
                bool isBlueprint = GetSoldIsBlueprint(sellOrder);
                ulong skinId = GetSoldSkin(sellOrder);

                if (itemId == 0 || amountPerSale <= 0)
                    continue;

                int desiredAmount = GetDesiredBackingAmount(amountPerSale);
                if (desiredAmount <= 0)
                    continue;

                Item matchingItem = FindBackingItem(itemList, itemId, skinId, isBlueprint);
                if (matchingItem == null)
                {
                    // CustomVendingSetup offers may carry ammo, capacity, instance data, or
                    // other attributes that cannot be recreated from a sell order alone.
                    // Existing items are safe to top up, but never replace a missing custom item.
                    if (isCustomized)
                        continue;

                    matchingItem = CreateBackingItem(inventory, itemId, skinId, isBlueprint, desiredAmount);
                    if (matchingItem == null)
                        continue;

                    changed = true;
                }

                if (matchingItem.amount < desiredAmount)
                {
                    matchingItem.amount = desiredAmount;
                    matchingItem.MarkDirty();
                    changed = true;
                }

                int availableSales = matchingItem.amount / amountPerSale;
                if (sellOrder.inStock != availableSales)
                {
                    sellOrder.inStock = availableSales;
                    changed = true;
                }
            }

            if (!changed)
                return false;

            vendingMachine.UpdateEmptyFlag();
            QueueUiUpdate(vendingMachine);
            return true;
        }

        private void QueueUiUpdate(NPCVendingMachine vendingMachine)
        {
            if (!ShouldManage(vendingMachine))
                return;

            ulong networkId = GetQueueKey(vendingMachine);
            if (!_queuedUiUpdates.Add(networkId))
                return;

            NextTick(delegate()
            {
                _queuedUiUpdates.Remove(networkId);

                if (!ShouldManage(vendingMachine))
                    return;

                vendingMachine.SendNetworkUpdateImmediate();
            });
        }

        private bool ShouldManage(NPCVendingMachine vendingMachine)
        {
            if (vendingMachine == null || vendingMachine.IsDestroyed)
                return false;

            if (vendingMachine is RentableShopVendingMachine)
                return false;

            return true;
        }

        private bool IsCustomized(NPCVendingMachine vendingMachine)
        {
            if (CustomVendingSetup == null)
                return false;

            object isCustomized = CustomVendingSetup.Call("API_IsCustomized", vendingMachine);
            return isCustomized is bool && (bool)isCustomized;
        }

        private static ulong GetNetworkId(NPCVendingMachine vendingMachine)
        {
            if (vendingMachine == null || vendingMachine.net == null)
                return 0UL;

            try
            {
                return Convert.ToUInt64(vendingMachine.net.ID.Value);
            }
            catch
            {
                try
                {
                    return Convert.ToUInt64(vendingMachine.net.ID);
                }
                catch
                {
                    return 0UL;
                }
            }
        }

        private static ulong GetQueueKey(NPCVendingMachine vendingMachine)
        {
            ulong networkId = GetNetworkId(vendingMachine);
            if (networkId != 0UL)
                return networkId;

            return (1UL << 63) | unchecked((uint)vendingMachine.GetInstanceID());
        }

        private static int GetDesiredBackingAmount(int amountPerSale)
        {
            if (amountPerSale <= 0)
                return 0;

            long desired = (long)amountPerSale * TargetSalesPerOrder;
            return desired > int.MaxValue ? int.MaxValue : (int)desired;
        }

        private static Item FindBackingItem(List<Item> itemList, int soldItemId, ulong soldSkinId, bool soldAsBlueprint)
        {
            int backingItemId = soldAsBlueprint && ItemManager.blueprintBaseDef != null
                ? ItemManager.blueprintBaseDef.itemid
                : soldItemId;

            int blueprintTarget = soldAsBlueprint ? soldItemId : 0;

            for (int i = 0; i < itemList.Count; i++)
            {
                Item item = itemList[i];
                if (item == null || item.info == null)
                    continue;

                if (item.info.itemid != backingItemId)
                    continue;

                if (item.blueprintTarget != blueprintTarget)
                    continue;

                if (item.skin != soldSkinId)
                    continue;

                return item;
            }

            return null;
        }

        private static Item CreateBackingItem(ItemContainer inventory, int soldItemId, ulong soldSkinId, bool soldAsBlueprint, int amount)
        {
            int backingItemId = soldAsBlueprint && ItemManager.blueprintBaseDef != null
                ? ItemManager.blueprintBaseDef.itemid
                : soldItemId;

            if (backingItemId == 0)
                return null;

            Item item = ItemManager.CreateByItemID(backingItemId, amount, soldAsBlueprint ? 0UL : soldSkinId);
            if (item == null)
                return null;

            if (soldAsBlueprint)
                item.blueprintTarget = soldItemId;

            if (item.MoveToContainer(inventory))
                return item;

            item.Remove();
            return null;
        }

        private static int GetSoldItemId(ProtoBuf.VendingMachine.SellOrder sellOrder)
        {
            return sellOrder == null ? 0 : sellOrder.itemToSellID;
        }

        private static int GetSoldAmount(ProtoBuf.VendingMachine.SellOrder sellOrder)
        {
            return sellOrder == null ? 0 : sellOrder.itemToSellAmount;
        }

        private static ulong GetSoldSkin(ProtoBuf.VendingMachine.SellOrder sellOrder)
        {
            if (sellOrder == null || SellOrderSellSkinIdField == null)
                return 0UL;

            object value = SellOrderSellSkinIdField.GetValue(sellOrder);
            if (value == null)
                return 0UL;

            try
            {
                return Convert.ToUInt64(value);
            }
            catch
            {
                return 0UL;
            }
        }

        private static bool GetSoldIsBlueprint(ProtoBuf.VendingMachine.SellOrder sellOrder)
        {
            if (sellOrder == null)
                return false;

            if (sellOrder.itemToSellIsBP)
                return true;

            if (SellOrderItemToSellAsBpField == null)
                return false;

            object value = SellOrderItemToSellAsBpField.GetValue(sellOrder);
            return value is bool && (bool)value;
        }

        private static FieldInfo GetSellOrderField(params string[] fieldNames)
        {
            const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            Type sellOrderType = typeof(ProtoBuf.VendingMachine.SellOrder);

            for (int i = 0; i < fieldNames.Length; i++)
            {
                FieldInfo field = sellOrderType.GetField(fieldNames[i], Flags);
                if (field != null)
                    return field;
            }

            return null;
        }
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху