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

FREE CustomVendingSetup : 2.10.2 2.10.2

Нет прав для скачивания
C#:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using Facepunch;
using UnityEngine;
using VLB;
using static ProtoBuf.VendingMachine;
using static VendingMachine;
using CustomGetDataCallback = System.Func<Newtonsoft.Json.Linq.JObject>;
using CustomSaveDataCallback = System.Action<Newtonsoft.Json.Linq.JObject>;
namespace Oxide.Plugins {
    [Info("Custom Vending Setup", "WhiteThunder", "2.10.2")]
    [Description("Allows editing orders at NPC vending machines.")]
    internal class CustomVendingSetup : CovalencePlugin {
#region Fields
        [PluginReference]
        private readonly Plugin BagOfHolding, Economics, ItemRetriever, MonumentFinder, ServerRewards;
        private SavedData _pluginData;
        private Configuration _config;
        private const string PermissionUse = "customvendingsetup.use";
        private const string StoragePrefab = "assets/prefabs/deployable/large wood storage/box.wooden.large.prefab";
        private const int ItemsPerRow = 6;
        private const int InventorySize = 24;
        // Going over 7 causes offers to get cut off regardless of resolution.private const int MaxVendingOffers = 7;
        private const int ShopNameNoteSlot = 29;
        private const int ContainerCapacity = 30;
        private const int MaxItemRows = ContainerCapacity / ItemsPerRow;
        private const int BlueprintItemId = -996920608;
        private const float MinCurrencyCondition = 0.5f;
        private readonly object True = true;
        private readonly object False = false;
        private ItemRetrieverAdapter _itemRetrieverAdapter;
        private DataProviderRegistry _dataProviderRegistry = new DataProviderRegistry();
        private ComponentTracker<NPCVendingMachine, VendingMachineComponent> _componentTracker = new ComponentTracker<NPCVendingMachine, VendingMachineComponent>();
        private ComponentFactory<NPCVendingMachine, VendingMachineComponent> _componentFactory;
        private MonumentFinderAdapter _monumentFinderAdapter;
        private VendingMachineManager _vendingMachineManager;
        private BagOfHoldingLimitManager _bagOfHoldingLimitManager;
        private DynamicHookSubscriber<BaseVendingController> _inaccessibleVendingMachines;
        private DynamicHookSubscriber<BasePlayer> _playersNeedingFakeInventory;
        private PaymentProviderResolver _paymentProviderResolver;
        private ItemDefinition _noteItemDefinition;
        private bool _isServerInitialized;
        private bool _performingInstantRestock;
        private VendingItem _itemBeingSold;
        private Dictionary<string, object> _itemRetrieverQuery = new Dictionary<string, object>();
        private List<Item> _reusableItemList = new List<Item>();
        private object[] _objectArray1 = new object[1];
        private object[] _objectArray2 = new object[2];
        public CustomVendingSetup() {
            _monumentFinderAdapter = new MonumentFinderAdapter(this);
            _itemRetrieverAdapter = new ItemRetrieverAdapter(this);
            _componentFactory = new ComponentFactory<NPCVendingMachine, VendingMachineComponent>(this, _componentTracker);
            _vendingMachineManager = new VendingMachineManager(this, _componentFactory, _dataProviderRegistry);
            _bagOfHoldingLimitManager = new BagOfHoldingLimitManager(this);
            _paymentProviderResolver = new PaymentProviderResolver(this);
            _inaccessibleVendingMachines = new DynamicHookSubscriber<BaseVendingController>(this, nameof(CanAccessVendingMachine));
            _playersNeedingFakeInventory = new DynamicHookSubscriber<BasePlayer>(this, nameof(OnEntitySaved), nameof(OnInventoryNetworkUpdate));
        }
#endregion
#region Hooks
        private void Init() {
            _config.Init();
            _pluginData = SavedData.Load();
            permission.RegisterPermission(PermissionUse, this);
            Unsubscribe(nameof(OnEntitySpawned));
            _inaccessibleVendingMachines.UnsubscribeAll();
            _playersNeedingFakeInventory.UnsubscribeAll();
        }
        private void OnServerInitialized() {
            _isServerInitialized = true;
            if (MonumentFinder == null) {
                LogError("MonumentFinder is not loaded, get it at http://umod.org.");
            }
            else {
                // Delay to allow Monument Finder to register monuments via its `OnServerInitialized()` hook.NextTick(() =>
                {
                    _vendingMachineManager.SetupAll();
                    foreach (var player in BasePlayer.activePlayerList) {
                        var container = player.inventory.loot.containers.FirstOrDefault();
                        if (container == null) continue;
                        var vendingMachine = container.entityOwner as NPCVendingMachine;
                        if (vendingMachine != null) {
                            OnVendingShopOpened(vendingMachine, player);
                        }
                    }
                }
                );
            }
            if (ItemRetriever != null) {
                _itemRetrieverAdapter.HandleItemRetrieverLoaded();
            }
            _bagOfHoldingLimitManager.OnServerInitialized();
            Subscribe(nameof(OnEntitySpawned));
            _noteItemDefinition = ItemManager.FindItemDefinition("note");
        }
        private void Unload() {
            _vendingMachineManager.ResetAll();
            ObjectCache.Clear<int>();
            ObjectCache.Clear<float>();
            ObjectCache.Clear<ulong>();
        }
        private void OnPluginLoaded(Plugin plugin) {
            switch (plugin.Name) {
                case nameof(MonumentFinder) : {
                    // Check whether initialized to detect only late (re)loads.// Note: We are not dynamically subscribing to OnPluginLoaded since that interferes with the PluginReference attribute.if (_isServerInitialized)
                    {
                        // Delay to ensure MonumentFinder's `OnServerInitialized` method is called.NextTick(_vendingMachineManager.SetupAll);
                    }
                    return;
                }
                case nameof(BagOfHolding) : _bagOfHoldingLimitManager.HandleBagOfHoldingLoadedChanged();
                return;
                case nameof(ItemRetriever) : _itemRetrieverAdapter.HandleItemRetrieverLoaded();
                return;
            }
        }
        private void OnPluginUnloaded(Plugin plugin) {
            switch (plugin.Name) {
                case nameof(ItemRetriever) : _itemRetrieverAdapter.HandleItemRetrieverUnloaded();
                return;
            }
        }
        private void OnEntitySpawned(NPCVendingMachine vendingMachine) {
            // Delay to give other plugins a chance to save a reference so they can block setup.NextTick(() =>
            {
                if (vendingMachine == null || vendingMachine.IsDestroyed) return;
                _vendingMachineManager.HandleVendingMachineSpawned(vendingMachine);
            }
            );
        }
        private void OnEntityKill(NPCVendingMachine vendingMachine) {
            _vendingMachineManager.HandleVendingMachineKilled(vendingMachine);
        }
        private void OnVendingShopOpened(NPCVendingMachine vendingMachine, BasePlayer player) {
            var controller = _vendingMachineManager.GetController(vendingMachine);
            if (controller == null) return;
            var component = _componentTracker.GetComponent(vendingMachine);
            if (component == null) return;
            if (permission.UserHasPermission(player.UserIDString, PermissionUse)) {
                component.ShowAdminUI(player);
            }
            var profile = controller.Profile;
            if (profile?.Offers == null) return;
            if (_config.ShopUISettings.EnableSkinOverlays) {
                component.ShowShopUI(player);
            }
            if ((_config.Economics.EnabledAndValid && profile.HasPaymentProviderCurrency(_config.Economics)) || (_config.ServerRewards.EnabledAndValid && profile.HasPaymentProviderCurrency(_config.ServerRewards))) {
                // Make sure OnEntitySaved/OnInventoryNetworkUpdate are subscribed to modify network updates._playersNeedingFakeInventory.Add(player);
                // Mark inventory dirty to send a network update, which will be modified by hooks.player.inventory.containerMain.MarkDirty();
            }
        }
        private object OnVendingTransaction(NPCVendingMachine vendingMachine, BasePlayer player, int sellOrderIndex, int numberOfTransactions, ItemContainer targetContainer) {
            var vendingProfile = _vendingMachineManager.GetController(vendingMachine)?.Profile;
            if (vendingProfile?.Offers == null) {
                // Don't override the transaction logic because the vending machine is not customized by this plugin.return null;
            }
            var component = _componentTracker.GetComponent(vendingMachine);
            if (component == null) return null;
            var offer = vendingProfile.GetOfferForSellOrderIndex(sellOrderIndex);
            if (offer == null) {
                // Something is wrong.No valid offer exists at the specified index.return null;
            }
            numberOfTransactions = Mathf.Clamp(numberOfTransactions, 1, HasCondition(offer.SellItem.ItemDefinition) ? 1 : 1000000);
            var sellAmount = offer.SellItem.Amount * numberOfTransactions;
            var sellItemQuery = ItemQuery.FromSellItem(offer.SellItem);
            if (ItemUtils.SumContainerItems(vendingMachine.inventory, ref sellItemQuery) < sellAmount) {
                // The vending machine has insufficient stock.return False;
            }
            var currencyAmount = offer.CurrencyItem.Amount * numberOfTransactions;
            var currencyProvider = _paymentProviderResolver.Resolve(offer.CurrencyItem);
            if (currencyProvider.GetBalance(player) < currencyAmount) {
                // The player has insufficient currency.return False;
            }
            _reusableItemList.Clear();
            currencyProvider.TakeBalance(player, currencyAmount, _reusableItemList);
            var onMarketplaceItemPurchase = (targetContainer?.entityOwner as MarketTerminal)?._onItemPurchasedCached;
            // Note: The list will be empty if Economics or Server Rewards currency were used.foreach (var currencyItem in _reusableItemList)
            {
                MaybeGiveWeaponAmmo(currencyItem, player);
                // Show a notice on the marketplace UI that the item was taken.onMarketplaceItemPurchase?.Invoke(player, currencyItem);
                // Instead of calling `vendingMachine.TakeCurrencyItem(itemToTake)`, just remove the item.// This fixes an "issue" where the item would go into the vending machine storage if there was a matching stack.// Note: The "OnTakeCurrencyItem" hook is not called because Item Retriever always takes the items.currencyItem.RemoveFromContainer();
                currencyItem.Remove();
            }
            _reusableItemList.Clear();
            if (offer.RefillDelay <= 0) {
                // Don't change the stock amount.Instead, we will just leave the items in the vending machine.// The "CanVendingStockRefill" hook will use this flag to skip all logic._performingInstantRestock = true;
            }
            else {
                // The "CanVendingStockRefill" hook may use this to add stock._itemBeingSold = offer.SellItem;
            }
            _paymentProviderResolver.Resolve(offer.SellItem).AddBalance(player, sellAmount, new TransactionContext {
                VendingMachine = vendingMachine, SellItem = offer.SellItem, TargetContainer = targetContainer, OnMarketplaceItemPurchase = onMarketplaceItemPurchase,
            }
            );
            // These can now be unset since the "CanVendingStockRefill" hook can no longer be called after this point._performingInstantRestock = false;
            _itemBeingSold = null;
            if (offer.RefillDelay > 0) {
                // Remove stock only after the items have been given to the player,
                // so that max stack size can be determined by an item in stock.ItemUtils.TakeContainerItems(vendingMachine.inventory, ref sellItemQuery, sellAmount);
            }
            vendingMachine.UpdateEmptyFlag();
            // Reopen the UI if it was closed due to a transaction delay.if (!component.HasUI(player) && IsLootingVendingMachine(player, vendingMachine))
            {
                OnVendingShopOpened(vendingMachine, player);
            }
            if (offer.CustomSettings?.Count > 0) {
                ExposedHooks.OnCustomVendingSetupTransactionWithCustomSettings(vendingMachine, offer.CustomSettings);
            }
            return True;
        }
        private void OnBuyVendingItem(NPCVendingMachine vendingMachine, BasePlayer player, int sellOrderID, int amount) {
            if (!IsCustomized(vendingMachine)) return;
            var component = _componentTracker.GetComponent(vendingMachine);
            if (component == null) return;
            ScheduleRemoveUI(vendingMachine, player, component);
        }
        // This hook is exposed by plugin: Vending In Stock (VendingInStock).private object CanVendingStockRefill(NPCVendingMachine vendingMachine, Item soldItem, BasePlayer player)
        {
            if (!IsCustomized(vendingMachine)) {
                // Allow VendingInStock to restock the item.return null;
            }
            if (_performingInstantRestock) {
                // Don't restock the item, since it was never removed from the vending machine in the first place.return False;
            }
            // Override VendingInStock behavior to prevent creating new items in the container.// This also ensures additional item attributes are preserved.var itemQuery = ItemQuery.FromSellItem(_itemBeingSold);
            var existingItem = ItemUtils.FindFirstContainerItem(vendingMachine.inventory, ref itemQuery);
            if (existingItem != null) {
                existingItem.amount += soldItem.amount;
                existingItem.MarkDirty();
                return False;
            }
            if (_itemBeingSold == null) {
                // Something is wrong.The "CanPurchaseItem" hook was not called via this plugin.return null;
            }
            var newItem = _itemBeingSold.Create(soldItem.amount);
            vendingMachine.transactionActive = true;
            if (!newItem.MoveToContainer(vendingMachine.inventory, allowStack : false)) {
                newItem.Remove();
            }
            vendingMachine.transactionActive = false;
            return False;
        }
        private object CanAccessVendingMachine(DeliveryDroneConfig deliveryDroneConfig, NPCVendingMachine vendingMachine) {
            if (!vendingMachine.IsBroadcasting()) return null;
            var controller = _vendingMachineManager.GetController(vendingMachine);
            if (controller == null) return null;
            if (_inaccessibleVendingMachines.Contains(controller)) return False;
            return null;
        }
        private void OnEntitySaved(BasePlayer player, BaseNetworkable.SaveInfo saveInfo) {
            AddCurrencyToContainerSnapshot(player, saveInfo.msg.basePlayer.inventory.invMain);
        }
        private void OnInventoryNetworkUpdate(PlayerInventory inventory, ItemContainer container, ProtoBuf.UpdateItemContainer updatedItemContainer, PlayerInventory.Type inventoryType) {
            if (inventoryType != PlayerInventory.Type.Main) return;
            AddCurrencyToContainerSnapshot(inventory.baseEntity, updatedItemContainer.container[0]);
        }
#endregion
#region API
        private bool API_IsCustomized(NPCVendingMachine vendingMachine) {
            return IsCustomized(vendingMachine);
        }
        private void API_RefreshDataProvider(NPCVendingMachine vendingMachine) {
            _vendingMachineManager.HandleVendingMachineKilled(vendingMachine);
            _vendingMachineManager.HandleVendingMachineSpawned(vendingMachine);
        }
        // Undocumented.Intended for MonumentAddons migration to become a Data Provider.private JObject API_MigrateVendingProfile(NPCVendingMachine vendingMachine)
        {
            var location = MonumentRelativePosition.FromVendingMachine(_monumentFinderAdapter, vendingMachine);
            if (location == null) {
                // This can happen if a vending machine was moved outside a monument's bounds.return null;
            }
            var vendingProfile = _pluginData.FindProfile(location);
            if (vendingProfile == null) {
                return null;
            }
            JObject jObject;
            try {
                jObject = JObject.FromObject(vendingProfile);
            }
            catch (Exception e) {
                LogError($"Unable to migrate vending profile\n{e}");
                return null;
            }
            _pluginData.VendingProfiles.Remove(vendingProfile);
            _pluginData.Save();
            return jObject;
        }
#endregion
#region Dependencies
        private class MonumentAdapter {
            public string PrefabName => (string)_monumentInfo["PrefabName"];
            public string Alias => (string)_monumentInfo["Alias"];
            public Vector3 Position => (Vector3)_monumentInfo["Position"];
            private Dictionary<string, object> _monumentInfo;
            public MonumentAdapter(Dictionary<string, object> monumentInfo) {
                _monumentInfo = monumentInfo;
            }
            public Vector3 InverseTransformPoint(Vector3 worldPosition) => ((Func<Vector3, Vector3>)_monumentInfo["InverseTransformPoint"]).Invoke(worldPosition);
            public bool IsInBounds(Vector3 position) => ((Func<Vector3, bool>)_monumentInfo["IsInBounds"]).Invoke(position);
        }
        private class MonumentFinderAdapter {
            private CustomVendingSetup _plugin;
            private Plugin _monumentFinder => _plugin.MonumentFinder;
            public MonumentFinderAdapter(CustomVendingSetup plugin) {
                _plugin = plugin;
            }
            public MonumentAdapter GetMonumentAdapter(Vector3 position) {
                var dictResult = _monumentFinder?.Call("API_GetClosest", position) as Dictionary<string, object>;
                if (dictResult == null) return null;
                var monument = new MonumentAdapter(dictResult);
                return monument.IsInBounds(position) ? monument : null;
            }
            public MonumentAdapter GetMonumentAdapter(BaseEntity entity) {
                return GetMonumentAdapter(entity.transform.position);
            }
        }
        private class BagOfHoldingLimitManager {
            private class CustomLimitProfile {
                [JsonProperty("Max total bags")]
                public int MaxTotalBags = -1;
            }
            private CustomVendingSetup _plugin;
            private object _limitProfile;
            public BagOfHoldingLimitManager(CustomVendingSetup plugin) {
                _plugin = plugin;
            }
            public void OnServerInitialized() {
                HandleBagOfHoldingLoadedChanged();
            }
            public void HandleBagOfHoldingLoadedChanged() {
                if (_plugin.BagOfHolding == null) return;
                _limitProfile = _plugin.BagOfHolding.Call("API_CreateLimitProfile", JsonConvert.SerializeObject(new CustomLimitProfile()));
                if (_limitProfile == null) {
                    LogError("Failed to create limit profile.");
                }
            }
            public void SetLimitProfile(ItemContainer container) {
                if (_limitProfile == null || _plugin.BagOfHolding == null) return;
                var result = _plugin.BagOfHolding.Call("API_SetLimitProfile", container, _limitProfile);
                if (!(result is bool) || (bool)result == false) {
                    LogError("Failed to set limit profile for vending container");
                }
            }
            public void RemoveLimitProfile(ItemContainer container) {
                if (_limitProfile == null || _plugin.BagOfHolding == null) return;
                _plugin.BagOfHolding.Call("API_RemoveLimitProfile", container);
            }
        }
        private class ItemRetrieverApi {
            public Func<BasePlayer, Dictionary<string, object>, int> SumPlayerItems {
                get;
            }
            public Func<BasePlayer, Dictionary<string, object>, int, List<Item>, int> TakePlayerItems {
                get;
            }
            public ItemRetrieverApi(Dictionary<string, object> apiDict) {
                SumPlayerItems = apiDict[nameof(SumPlayerItems)] as Func<BasePlayer, Dictionary<string, object>, int>;
                TakePlayerItems = apiDict[nameof(TakePlayerItems)] as Func<BasePlayer, Dictionary<string, object>, int, List<Item>, int>;
            }
        }
        private class ItemRetrieverAdapter {
            public ItemRetrieverApi Api {
                get;
                private set;
            }
            private CustomVendingSetup _plugin;
            private Plugin ItemRetriever => _plugin.ItemRetriever;
            public ItemRetrieverAdapter(CustomVendingSetup plugin) {
                _plugin = plugin;
            }
            public void HandleItemRetrieverLoaded() {
                Api = new ItemRetrieverApi(ItemRetriever.Call("API_GetApi") as Dictionary<string, object>);
            }
            public void HandleItemRetrieverUnloaded() {
                Api = null;
            }
        }
#endregion
#region Exposed Hooks
        private static class ExposedHooks {
            public static object OnCustomVendingSetup(NPCVendingMachine vendingMachine) {
                return Interface.CallHook("OnCustomVendingSetup", vendingMachine);
            }
            public static object CanPurchaseItem(BasePlayer player, Item item, Action<BasePlayer, Item> onItemPurchased, NPCVendingMachine vendingMachine, ItemContainer targetContainer) {
                return Interface.CallHook("CanPurchaseItem", player, item, onItemPurchased, vendingMachine, targetContainer);
            }
            public static Dictionary<string, object> OnCustomVendingSetupDataProvider(NPCVendingMachine vendingMachine) {
                return Interface.CallHook("OnCustomVendingSetupDataProvider", vendingMachine) as Dictionary<string, object>;
            }
            public static void OnCustomVendingSetupOfferSettingsParse(CaseInsensitiveDictionary<string> localizedSettings, CaseInsensitiveDictionary<object> customSettings) {
                Interface.CallHook("OnCustomVendingSetupOfferSettingsParse", localizedSettings, customSettings);
            }
            public static void OnCustomVendingSetupOfferSettingsDisplay(CaseInsensitiveDictionary<object> customSettings, CaseInsensitiveDictionary<string> localizedSettings) {
                Interface.CallHook("OnCustomVendingSetupOfferSettingsDisplay", customSettings, localizedSettings);
            }
            public static void OnCustomVendingSetupTransactionWithCustomSettings(NPCVendingMachine vendingMachine, CaseInsensitiveDictionary<object> customSettings) {
                Interface.CallHook("OnCustomVendingSetupTransactionWithCustomSettings", vendingMachine, customSettings);
            }
        }
#endregion
#region Commands
        private static class UICommands {
            public const string Edit = "edit";
            public const string Reset = "reset";
            public const string Save = "save";
            public const string Cancel = "cancel";
            public const string ToggleBroadcast = "togglebroadcast";
            public const string ToggleDroneAccessible = "toggledroneaccessible";
        }
        [Command("customvendingsetup.ui")]
        private void CommandUI(IPlayer player, string cmd, string[] args) {
            if (args.Length < 2) return;
            NPCVendingMachine vendingMachine;
            BaseVendingController vendingController;
            if (!PassesUICommandChecks(player, args, out vendingMachine, out vendingController)) return;
            var basePlayer = player.Object as BasePlayer;
            var subCommand = args[1];
            switch (subCommand) {
                case UICommands.Edit : if (vendingController.EditController != null) {
                    basePlayer.EndLooting();
                    ChatMessage(basePlayer, Lang.ErrorCurrentlyBeingEdited, vendingController.EditController.EditorPlayer.displayName);
                    return;
                }
                vendingController.StartEditing(basePlayer, vendingMachine);
                break;
                case UICommands.Reset : vendingController.HandleReset();
                vendingMachine.FullUpdate();
                basePlayer.EndLooting();
                basePlayer.inventory.loot.SendImmediate();
                OpenVendingMachineDelayed(basePlayer, vendingMachine);
                break;
                case UICommands.ToggleBroadcast : vendingController.EditController?.ToggleBroadcast();
                break;
                case UICommands.ToggleDroneAccessible : vendingController.EditController?.ToggleDroneAccessible();
                break;
                case UICommands.Save : vendingController.HandleSave(vendingMachine);
                vendingMachine.FullUpdate();
                OpenVendingMachine(basePlayer, vendingMachine);
                break;
                case UICommands.Cancel : OpenVendingMachine(basePlayer, vendingMachine);
                break;
            }
        }
#endregion
#region Helper Methods
        public static void LogInfo(string message) => Interface.Oxide.LogInfo($"[Custom Vending Setup] {message}");
        public static void LogError(string message) => Interface.Oxide.LogError($"[Custom Vending Setup] {message}");
        public static void LogWarning(string message) => Interface.Oxide.LogWarning($"[Custom Vending Setup] {message}");
        private static bool IsLootingVendingMachine(BasePlayer player, NPCVendingMachine vendingMachine) {
            return player.inventory.loot.containers.FirstOrDefault()?.entityOwner == vendingMachine;
        }
        private static bool AreVectorsClose(Vector3 a, Vector3 b, float xZTolerance = 0.001f, float yTolerance = 10) {
            // Allow a generous amount of vertical distance given that plugins may snap entities to terrain.return Math.Abs(a.y - b.y) < yTolerance
            && Math.Abs(a.x - b.x) < xZTolerance && Math.Abs(a.z - b.z) < xZTolerance;
        }
        private static bool HasCondition(ItemDefinition itemDefinition) {
            return itemDefinition.condition.enabled && itemDefinition.condition.max > 0;
        }
        private static void OpenVendingMachine(BasePlayer player, NPCVendingMachine vendingMachine) {
            if (vendingMachine.OccupiedCheck(player) && Interface.CallHook("OnVendingShopOpen", vendingMachine, player) == null) {
                vendingMachine.SendSellOrders(player);
                vendingMachine.PlayerOpenLoot(player, vendingMachine.customerPanel);
                Interface.CallHook(nameof(OnVendingShopOpened), vendingMachine, player);
            }
        }
        private static VendingOffer[] GetOffersFromVendingMachine(NPCVendingMachine vendingMachine) {
            var vanillaOffers = vendingMachine.sellOrders.sellOrders;
            var offers = new VendingOffer[vanillaOffers.Count];
            for (var i = 0; i < offers.Length; i++) {
                var manifestEntry = vendingMachine.vendingOrders.orders.ElementAtOrDefault(i);
                offers[i] = VendingOffer.FromVanillaSellOrder(vanillaOffers[i], manifestEntry);
            }
            return offers;
        }
        private static VendingOffer[] GetOffersFromContainer(CustomVendingSetup plugin, BasePlayer player, ItemContainer container) {
            var offers = new List<VendingOffer>();
            for (var columnIndex = 0; columnIndex < 2; columnIndex++) {
                for (var rowIndex = 0; rowIndex < MaxItemRows; rowIndex++) {
                    var sellItemSlot = rowIndex * ItemsPerRow + columnIndex * 3;
                    var sellItem = container.GetSlot(sellItemSlot);
                    var currencyItem = container.GetSlot(sellItemSlot + 1);
                    var settingsItem = container.GetSlot(sellItemSlot + 2);
                    if (sellItem == null || currencyItem == null) continue;
                    offers.Add(VendingOffer.FromItems(plugin, player, sellItem, currencyItem, settingsItem));
                }
            }
            return offers.ToArray();
        }
        private static StorageContainer CreateContainerEntity(string prefabPath) {
            var entity = GameManager.server.CreateEntity(prefabPath);
            if (entity == null) return null;
            var container = entity as StorageContainer;
            if (container == null) {
                UnityEngine.Object.Destroy(entity);
                return null;
            }
            UnityEngine.Object.DestroyImmediate(container.GetComponent<DestroyOnGroundMissing>());
            UnityEngine.Object.DestroyImmediate(container.GetComponent<GroundWatch>());
            container.limitNetworking = true;
            container.EnableSaving(false);
            container.Spawn();
            return container;
        }
        private static int OrderIndexToSlot(int orderIndex) {
            if (orderIndex < MaxItemRows) return orderIndex * ItemsPerRow;
            return (orderIndex % MaxItemRows) * ItemsPerRow + 3;
        }
        private static string CreateNoteContents(Dictionary<string, string> settingsMap) {
            var lines = new List<string>();
            foreach (var entry in settingsMap) {
                lines.Add($"{entry.Key}: {entry.Value}");
            }
            return string.Join("\n", lines);
        }
        private static StorageContainer CreateOrdersContainer(CustomVendingSetup plugin, BasePlayer player, VendingOffer[] vendingOffers, string shopName) {
            var containerEntity = CreateContainerEntity(StoragePrefab);
            var container = containerEntity.inventory;
            container.allowedContents = ItemContainer.ContentsType.Generic;
            container.capacity = ContainerCapacity;
            plugin._bagOfHoldingLimitManager.SetLimitProfile(container);
            for (var orderIndex = 0; orderIndex < vendingOffers.Length && orderIndex < 9; orderIndex++) {
                var offer = vendingOffers[orderIndex];
                var sellItem = offer.SellItem.Create();
                if (sellItem == null) continue;
                var currencyItem = offer.CurrencyItem.Create();
                if (currencyItem == null) {
                    sellItem.Remove();
                    continue;
                }
                var destinationSlot = OrderIndexToSlot(orderIndex);
                if (!sellItem.MoveToContainer(container, destinationSlot)) sellItem.Remove();
                if (!currencyItem.MoveToContainer(container, destinationSlot + 1)) currencyItem.Remove();
            }
            // Add 7 note items, so the user doesn't have to make them.for (var orderIndex = 0; orderIndex < 7; orderIndex++)
            {
                var offer = vendingOffers.Length > orderIndex ? vendingOffers[orderIndex] : null;
                var settingsItem = ItemManager.Create(plugin._noteItemDefinition);
                if (settingsItem == null) continue;
                var refillMaxLabel = plugin.GetMessage(player, Lang.SettingsRefillMax);
                var refillDelayLabel = plugin.GetMessage(player, Lang.SettingsRefillDelay);
                var refillAmountLabel = plugin.GetMessage(player, Lang.SettingsRefillAmount);
                var settingsMap = new CaseInsensitiveDictionary<string> {
                    [refillMaxLabel]
                    = (offer?.RefillMax ?? VendingOffer.DefaultRefillMax).ToString(), [refillDelayLabel] = (offer?.RefillDelay ?? VendingOffer.DefaultRefillDelay).ToString(), [refillAmountLabel] = (offer?.RefillAmount ?? VendingOffer.DefaultRefillAmount).ToString(),
                }
                ;
                // Allow other plugins to parse the custom settings and display localized options.ExposedHooks.OnCustomVendingSetupOfferSettingsDisplay(
                offer?.CustomSettings ?? new CaseInsensitiveDictionary<object>(), settingsMap);
                settingsItem.text = CreateNoteContents(settingsMap);
                var destinationSlot = OrderIndexToSlot(orderIndex);
                if (!settingsItem.MoveToContainer(container, destinationSlot + 2)) settingsItem.Remove();
            }
            var generalSettingsItem = ItemManager.Create(plugin._noteItemDefinition);
            if (generalSettingsItem != null) {
                generalSettingsItem.text = shopName;
                if (!generalSettingsItem.MoveToContainer(container, ShopNameNoteSlot)) generalSettingsItem.Remove();
            }
            return containerEntity;
        }
        private static void MaybeGiveWeaponAmmo(Item item, BasePlayer player) {
            var heldEntity = item.GetHeldEntity();
            if (heldEntity == null) return;
            if (heldEntity.creationFrame == Time.frameCount) {
                // The item was probably split off another item, so don't refund its ammo.return;
            }
            var baseProjectile = heldEntity as BaseProjectile;
            if ((object)baseProjectile != null) {
                var ammoType = baseProjectile.primaryMagazine?.ammoType;
                if (ammoType != null && baseProjectile.primaryMagazine.contents > 0) {
                    var ammoItem = ItemManager.Create(ammoType, baseProjectile.primaryMagazine.contents);
                    if (ammoItem != null) {
                        player.GiveItem(ammoItem);
                    }
                }
                return;
            }
            var flameThrower = heldEntity as FlameThrower;
            if ((object)flameThrower != null) {
                if (flameThrower.fuelType != null && flameThrower.ammo > 0) {
                    var ammoItem = ItemManager.Create(flameThrower.fuelType, flameThrower.ammo);
                    if (ammoItem != null) {
                        player.GiveItem(ammoItem);
                    }
                }
            }
        }
        private static void GiveSoldItem(Item item, BasePlayer player, ref TransactionContext transaction) {
            var vendingMachine = transaction.VendingMachine;
            var targetContainer = transaction.TargetContainer;
            // Unset the placeholder flag to allow Enchanted Items to transform the artifact.item.SetFlag(Item.Flag.Placeholder, false);
            if (targetContainer == null) {
                vendingMachine.GiveSoldItem(item, player);
            }
            else if (!item.MoveToContainer(targetContainer)) {
                item.Drop(targetContainer.dropPosition, targetContainer.dropVelocity);
            }
            transaction.OnMarketplaceItemPurchase?.Invoke(player, item);
        }
        private static int GetHighestUsedSlot(ProtoBuf.ItemContainer containerData) {
            var highestUsedSlot = -1;
            for (var i = 0; i < containerData.contents.Count; i++) {
                var item = containerData.contents[i];
                if (item.slot > highestUsedSlot) {
                    highestUsedSlot = item.slot;
                }
            }
            return highestUsedSlot;
        }
        private static void AddItemForNetwork(ProtoBuf.ItemContainer containerData, int slot, int itemId, int amount, ItemId uid) {
            var itemData = Pool.Get<ProtoBuf.Item>();
            itemData.slot = slot;
            itemData.itemid = itemId;
            itemData.amount = amount;
            itemData.UID = uid;
            containerData.contents.Add(itemData);
        }
        private object CallPlugin<T1>(Plugin plugin, string methodName, T1 arg1) {
            _objectArray1[0] = ObjectCache.Get(arg1);
            return plugin.Call(methodName, _objectArray1);
        }
        private object CallPlugin<T1, T2>(Plugin plugin, string methodName, T1 arg1, T2 arg2) {
            _objectArray2[0] = ObjectCache.Get(arg1);
            _objectArray2[1] = ObjectCache.Get(arg2);
            return plugin.Call(methodName, _objectArray2);
        }
        private void ScheduleRemoveUI(NPCVendingMachine vendingMachine, BasePlayer player, VendingMachineComponent component) {
            component.Invoke(() => {
                if (vendingMachine == null || vendingMachine.IsDestroyed) return;
                if (IsLootingVendingMachine(player, vendingMachine) && !vendingMachine.IsInvoking(vendingMachine.CompletePendingOrder)) return;
                // Remove the UI because the player stopped viewing the vending machine or the transaction is pending.component.RemoveUI(player);
            }
            , 0);
        }
        private void AddCurrencyToContainerSnapshot(BasePlayer player, ProtoBuf.ItemContainer containerData) {
            if (containerData == null || containerData.slots < InventorySize || !_playersNeedingFakeInventory.Contains(player)) return;
            var lootingContainer = player.inventory.loot.containers.FirstOrDefault();
            var vendingMachine = lootingContainer?.entityOwner as NPCVendingMachine;
            if ((object)vendingMachine == null) return;
            var profile = _componentTracker.GetComponent(vendingMachine)?.Profile;
            if (profile == null) return;
            var nextInvisibleSlot = Math.Max(containerData.slots, GetHighestUsedSlot(containerData) + 1);
            if (_config.Economics.EnabledAndValid && profile.HasPaymentProviderCurrency(_config.Economics)) {
                AddItemForNetwork(containerData, slot : nextInvisibleSlot, itemId : _config.Economics.ItemDefinition.itemid, amount : _paymentProviderResolver.EconomicsPaymentProvider.GetBalance(player), uid : new ItemId(ulong.MaxValue - (ulong)nextInvisibleSlot) );
                nextInvisibleSlot++;
            }
            if (_config.ServerRewards.EnabledAndValid && profile.HasPaymentProviderCurrency(_config.ServerRewards)) {
                AddItemForNetwork(containerData, slot : nextInvisibleSlot, itemId : _config.ServerRewards.ItemDefinition.itemid, amount : _paymentProviderResolver.ServerRewardsPaymentProvider.GetBalance(player), uid : new ItemId(ulong.MaxValue - (ulong)nextInvisibleSlot) );
                nextInvisibleSlot++;
            }
            containerData.slots = nextInvisibleSlot;
        }
        private Dictionary<string, object> SetupItemRetrieverQuery(ref ItemQuery itemQuery) {
            _itemRetrieverQuery.Clear();
            _itemRetrieverQuery["MinCondition"] = ObjectCache.Get(MinCurrencyCondition);
            _itemRetrieverQuery["RequireEmpty"] = True;
            if (itemQuery.BlueprintId != 0) _itemRetrieverQuery["BlueprintId"] = ObjectCache.Get(itemQuery.BlueprintId);
            if (itemQuery.DataInt != 0) _itemRetrieverQuery["DataInt"] = ObjectCache.Get(itemQuery.DataInt);
            if (itemQuery.ItemId != 0) _itemRetrieverQuery["ItemId"] = ObjectCache.Get(itemQuery.ItemId);
            if (itemQuery.SkinId.HasValue) _itemRetrieverQuery["SkinId"] = ObjectCache.Get(itemQuery.SkinId.Value);
            return _itemRetrieverQuery;
        }
        private int SumPlayerItems(BasePlayer player, ref ItemQuery itemQuery) {
            return _itemRetrieverAdapter?.Api?.SumPlayerItems.Invoke(player, SetupItemRetrieverQuery(ref itemQuery)) ?? ItemUtils.SumPlayerItems(player, ref itemQuery);
        }
        private int TakePlayerItems(BasePlayer player, ref ItemQuery itemQuery, int amount, List<Item> collect = null) {
            return _itemRetrieverAdapter?.Api?.TakePlayerItems.Invoke(player, SetupItemRetrieverQuery(ref itemQuery), amount, collect) ?? ItemUtils.TakePlayerItems(player, ref itemQuery, amount, collect);
        }
        private bool PassesUICommandChecks(IPlayer player, string[] args, out NPCVendingMachine vendingMachine, out BaseVendingController controller) {
            vendingMachine = null;
            controller = null;
            if (player.IsServer || !player.HasPermission(PermissionUse)) return false;
            ulong vendingMachineId;
            if (args.Length == 0 || !ulong.TryParse(args[0], out vendingMachineId)) return false;
            vendingMachine = BaseNetworkable.serverEntities.Find(new NetworkableId(vendingMachineId)) as NPCVendingMachine;
            if (vendingMachine == null) return false;
            controller = _vendingMachineManager.GetController(vendingMachine);
            if (controller == null) return false;
            return true;
        }
        private void OpenVendingMachineDelayed(BasePlayer player, NPCVendingMachine vendingMachine, float delay = 0.1f) {
            timer.Once(delay, () => {
                if (player == null || vendingMachine == null || vendingMachine.IsDestroyed) return;
                OpenVendingMachine(player, vendingMachine);
            }
            );
        }
        private bool IsCustomized(NPCVendingMachine vendingMachine) => _vendingMachineManager.GetController(vendingMachine)?.Profile?.Offers != null;
#endregion
#region UI
        private static class UIConstants {
            public const string EditButtonColor = "0.451 0.553 0.271 1";
            public const string EditButtonTextColor = "0.659 0.918 0.2 1";
            public const string ResetButtonColor = "0.9 0.5 0.2 1";
            public const string ResetButtonTextColor = "1 0.9 0.7 1";
            public const string SaveButtonColor = EditButtonColor;
            public const string SaveButtonTextColor = EditButtonTextColor;
            public const string CancelButtonColor = "0.4 0.4 0.4 1";
            public const string CancelButtonTextColor = "0.71 0.71 0.71 1";
            public const float PanelWidth = 380.5f;
            public const float HeaderHeight = 23;
            public const float ItemSpacing = 4;
            public const float ItemBoxSize = 58;
            public const int ButtonHorizontalSpacing = 6;
            public const int ButtonHeight = 32;
            public const int ButtonWidth = 80;
            public const string TexturedBackgroundSprite = "assets/content/ui/ui.background.tiletex.psd";
            public const string BroadcastIcon = "assets/icons/broadcast.png";
            public const string DroneIcon = "assets/icons/drone.png";
            public const string IconMaterial = "assets/icons/iconmaterial.mat";
            public const string GreyOutMaterial = "assets/icons/greyout.mat";
            public const string AnchorMin = "0.5 0";
            public const string AnchorMax = "0.5 0";
        }
        private class EditFormState {
            public static EditFormState FromVendingMachine(BaseVendingController vendingController, NPCVendingMachine vendingMachine) {
                return new EditFormState {
                    Broadcast = vendingController.Profile?.Broadcast ?? vendingMachine.IsBroadcasting(), DroneAccessible = vendingController.Profile?.DroneAccessible ?? true,
                }
                ;
            }
            public bool Broadcast;
            public bool DroneAccessible;
        }
        private static class ContainerUIRenderer {
            public const string UIName = "CustomVendingSetup.ContainerUI";
            public const string TipUIName = "CustomVendingSetup.ContainerUI.Tip";
            public const string BroadcastUIName = "CustomVendingSetup.ContainerUI.Broadcast";
            public const string DroneUIName = "CustomVendingSetup.ContainerUI.Drone";
            public static string RenderContainerUI(CustomVendingSetup plugin, BasePlayer player, NPCVendingMachine vendingMachine, EditFormState uiState) {
                var offsetX = 192;
                var offsetY = 141;
                var cuiElements = new CuiElementContainer {
                    {
                        new CuiPanel {
                            RectTransform = {
                                AnchorMin = UIConstants.AnchorMin, AnchorMax = UIConstants.AnchorMax, OffsetMin = $"{offsetX} {offsetY + MaxItemRows * (UIConstants.ItemBoxSize + UIConstants.ItemSpacing)}", OffsetMax = $"{offsetX} {offsetY + MaxItemRows * (UIConstants.ItemBoxSize + UIConstants.ItemSpacing)}",
                            }
                            ,
                        }
                        , "Hud.Menu", UIName
                    }
                }
                ;
                var saveButtonText = plugin.GetMessage(player, Lang.ButtonSave);
                var cancelButtonText = plugin.GetMessage(player, Lang.ButtonCancel);
                var vendingMachineId = vendingMachine.net.ID.Value;
                AddButton(cuiElements, vendingMachineId, saveButtonText, UICommands.Save, UIConstants.PanelWidth - UIConstants.ButtonWidth - UIConstants.ButtonHorizontalSpacing, UIConstants.SaveButtonColor, UIConstants.SaveButtonTextColor );
                AddButton(cuiElements, vendingMachineId, cancelButtonText, UICommands.Cancel, UIConstants.PanelWidth, UIConstants.CancelButtonColor, UIConstants.CancelButtonTextColor );
                AddBroadcastButton(cuiElements, vendingMachine, uiState);
                AddDroneButton(cuiElements, vendingMachine, uiState);
                var headerOffset = -6;
                cuiElements.Add(new CuiElement {
                    Parent = UIName, Name = TipUIName, Components = {
                        new CuiRawImageComponent {
                            Color = UIConstants.CancelButtonColor, Sprite = UIConstants.TexturedBackgroundSprite,
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = UIConstants.AnchorMin, AnchorMax = UIConstants.AnchorMax, OffsetMin = $"0 {headerOffset - UIConstants.HeaderHeight}", OffsetMax = $"{UIConstants.PanelWidth} {headerOffset}",
                        }
                    }
                    ,
                }
                );
                var forSaleText = plugin.GetMessage(player, Lang.InfoForSale);
                var costText = plugin.GetMessage(player, Lang.InfoCost);
                var settingsText = plugin.GetMessage(player, Lang.InfoSettings);
                AddHeaderLabel(cuiElements, 0, forSaleText);
                AddHeaderLabel(cuiElements, 1, costText);
                AddHeaderLabel(cuiElements, 2, settingsText);
                AddHeaderLabel(cuiElements, 3, forSaleText);
                AddHeaderLabel(cuiElements, 4, costText);
                AddHeaderLabel(cuiElements, 5, settingsText);
                return CuiH
// ... [Code preview truncated due to length. Full source in attached file] ...

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

Другие ресурсы пользователя 15FPS

Сверху