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

FREE CustomVendingSetup : 2.6.0 2.6.0

Нет прав для скачивания
C#:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
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.Linq;
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.6.0")]
    [Description("Allows editing orders at NPC vending machines.")]
    internal class CustomVendingSetup : CovalencePlugin {
#region Fields
        [PluginReference]
        private Plugin MonumentFinder, VendingInStock;
        private static CustomVendingSetup _pluginInstance;
        private static SavedData _pluginData;
        private Configuration _pluginConfig;
        private const string PermissionUse = "customvendingsetup.use";
        private const string StoragePrefab = "assets/prefabs/deployable/large wood storage/box.wooden.large.prefab";
        private const int BlueprintItemId = -996920608;
        private const int ItemsPerRow = 6;
        // 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 readonly object _boxedTrue = true;
        private readonly object _boxedFalse = false;
        private DataProviderRegistry _dataProviderRegistry = new DataProviderRegistry();
        private ComponentTracker<NPCVendingMachine, VendingMachineComponent> _componentTracker = new ComponentTracker<NPCVendingMachine, VendingMachineComponent>();
        private ComponentFactory<NPCVendingMachine, VendingMachineComponent> _componentFactory;
        private VendingMachineManager _vendingMachineManager;
        private ItemDefinition _noteItemDefinition;
        private bool _serverInitialized = false;
        private bool _performingInstantRestock = false;
        public CustomVendingSetup() {
            _componentFactory = new ComponentFactory<NPCVendingMachine, VendingMachineComponent>(_componentTracker);
            _vendingMachineManager = new VendingMachineManager(_componentFactory, _dataProviderRegistry);
        }
#endregion
#region Hooks
        private void Init() {
            _pluginInstance = this;
            _pluginConfig.Init(this);
            _pluginData = SavedData.Load();
            permission.RegisterPermission(PermissionUse, this);
            Unsubscribe(nameof(OnEntitySpawned));
        }
        private void OnServerInitialized() {
            if (CheckDependencies()) {
                // 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);
                    }
                }
                );
            }
            Subscribe(nameof(OnEntitySpawned));
            _noteItemDefinition = ItemManager.FindItemDefinition("note");
            _serverInitialized = true;
        }
        private void Unload() {
            _vendingMachineManager.ResetAll();
            _pluginData = null;
            _pluginInstance = null;
        }
        private void OnPluginLoaded(Plugin plugin) {
            // Check whether initialized to detect only late (re)loads.// Note: We are not dynamically subscribing to OnPluginLoaded since that interferes with [PluginReference] for some reason.if (_serverInitialized && plugin == MonumentFinder)
            {
                NextTick(_vendingMachineManager.SetupAll);
            }
        }
        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) return;
                _vendingMachineManager.OnVendingMachineSpawned(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);
            }
        }
        private object OnVendingTransaction(NPCVendingMachine vendingMachine, BasePlayer player, int sellOrderIndex, int numberOfTransactions, ItemContainer targetContainer) {
            // Only override transaction logic if the vending machine is customized by this plugin.var vendingProfile = _vendingMachineManager.GetController(vendingMachine)?.Profile;
            if (vendingProfile?.Offers == null) {
                return null;
            }
            var offer = vendingProfile.GetOfferForSellOrderIndex(sellOrderIndex);
            if (offer == null) {
                return null;
            }
            // Get all item stacks in the vending machine that match the sold item.var sellableItems = Facepunch.Pool.GetList<Item>();
            var amountAvailable = 0;
            var sellItemSpec = offer.SellItem.GetItemSpec().Value;
            sellItemSpec.FindAllInContainer(vendingMachine.inventory, sellableItems, ref amountAvailable);
            if (sellableItems.Count == 0) {
                Facepunch.Pool.FreeList(ref sellableItems);
                return _boxedFalse;
            }
            // Verify the vending machine has sufficient stock.numberOfTransactions = Mathf.Clamp(numberOfTransactions, 1, sellableItems[0].hasCondition ? 1 : 1000000);
            var amountRequested = offer.SellItem.Amount * numberOfTransactions;
            if (amountRequested > amountAvailable) {
                Facepunch.Pool.FreeList(ref sellableItems);
                return _boxedFalse;
            }
            // Get all item stacks in the player inventory that match the currency item.var currencyItems = Facepunch.Pool.GetList<Item>();
            var currencyAvailable = 0;
            var currencyItemSpec = offer.CurrencyItem.GetItemSpec().Value;
            var currencyMatchOptions = MatchOptions.All;
            if (currencyItemSpec.Skin == 0) {
                // Allow any skin to match if the currency item does not require a skin.currencyMatchOptions &= ~MatchOptions.Skin;
            }
            currencyItemSpec.FindAllInInventory(player.inventory, currencyItems, ref currencyAvailable, currencyMatchOptions);
            if (currencyItems.Count == 0) {
                Facepunch.Pool.FreeList(ref sellableItems);
                Facepunch.Pool.FreeList(ref currencyItems);
                return _boxedFalse;
            }
            // Verify the player has enough currency.var currencyRequired = offer.CurrencyItem.Amount * numberOfTransactions;
            if (currencyAvailable < currencyRequired) {
                Facepunch.Pool.FreeList(ref sellableItems);
                Facepunch.Pool.FreeList(ref currencyItems);
                return _boxedFalse;
            }
            var marketTerminal = targetContainer?.entityOwner as MarketTerminal;
            // Temporarily allow the vending machine internal storage to accept items.// Currency items are temporarily added to the vending machine internal storage before deleting.vendingMachine.transactionActive = true;
            var currencyToTake = currencyRequired;
            foreach (var currencyItem in currencyItems) {
                var amountToTake = Mathf.Min(currencyToTake, currencyItem.amount);
                currencyToTake -= amountToTake;
                var itemToTake = currencyItem.amount > amountToTake ? currencyItemSpec.Split(currencyItem, amountToTake) : currencyItem;
                vendingMachine.TakeCurrencyItem(itemToTake);
                marketTerminal?._onCurrencyRemovedCached?.Invoke(player, itemToTake);
                if (currencyToTake <= 0) {
                    break;
                }
            }
            vendingMachine.transactionActive = false;
            Facepunch.Pool.FreeList(ref currencyItems);
            // Perform instant restock if VendingInStock is loaded, since we are replacing its behavior.if (offer.RefillDelay == 0)
            {
                sellableItems[0].amount += amountRequested;
                _performingInstantRestock = true;
            }
            var maxStackSize = _pluginConfig.GetItemMaxStackSize(sellableItems[0]);
            var amountToGive = amountRequested;
            foreach (var sellableItem in sellableItems) {
                while (amountToGive > maxStackSize && sellableItem.amount > maxStackSize) {
                    var itemToGive1 = sellItemSpec.Split(sellableItem, maxStackSize);
                    amountToGive -= itemToGive1.amount;
                    object canPurchaseHookResult1 = CallHookCanPurchaseItem(player, itemToGive1, marketTerminal?._onItemPurchasedCached, vendingMachine, targetContainer);
                    if (canPurchaseHookResult1 is bool) {
                        Facepunch.Pool.FreeList(ref sellableItems);
                        return canPurchaseHookResult1;
                    }
                    GiveSoldItem(vendingMachine, itemToGive1, player, marketTerminal, targetContainer);
                }
                var itemToGive2 = sellableItem.amount > amountToGive ? sellItemSpec.Split(sellableItem, amountToGive) : sellableItem;
                amountToGive -= itemToGive2.amount;
                object canPurchaseHookResult2 = CallHookCanPurchaseItem(player, itemToGive2, marketTerminal?._onItemPurchasedCached, vendingMachine, targetContainer);
                if (canPurchaseHookResult2 is bool) {
                    Facepunch.Pool.FreeList(ref sellableItems);
                    return canPurchaseHookResult2;
                }
                GiveSoldItem(vendingMachine, itemToGive2, player, marketTerminal, targetContainer);
                if (amountToGive <= 0) {
                    break;
                }
            }
            _performingInstantRestock = false;
            Facepunch.Pool.FreeList(ref sellableItems);
            vendingMachine.UpdateEmptyFlag();
            return _boxedTrue;
        }
        // This hook is exposed by plugin: Vending In Stock (VendingInStock).private object CanVendingStockRefill(NPCVendingMachine vendingMachine, Item soldItem, BasePlayer player)
        {
            if (!IsCustomized(vendingMachine)) {
                return null;
            }
            // If refill delay is 0, the item has already been restocked.if (_performingInstantRestock)
            {
                return _boxedFalse;
            }
            var itemSpec = ItemSpec.FromItem(soldItem);
            // Override VendingInStock behavior to prevent creating new items in the container.// This also ensures additional item attributes are preserved.var existingItem = itemSpec.FirstInContainer(vendingMachine.inventory);
            if (existingItem != null) {
                existingItem.amount += soldItem.amount;
                existingItem.MarkDirty();
                return _boxedFalse;
            }
            var newItem = itemSpec.Create(soldItem.amount);
            vendingMachine.transactionActive = true;
            if (!newItem.MoveToContainer(vendingMachine.inventory, allowStack : false)) {
                newItem.Remove();
            }
            vendingMachine.transactionActive = false;
            return _boxedFalse;
        }
#endregion
#region API
        private bool API_IsCustomized(NPCVendingMachine vendingMachine) {
            return IsCustomized(vendingMachine);
        }
        private void API_RefreshDataProvider(NPCVendingMachine vendingMachine) {
            _vendingMachineManager.HandleVendingMachineKilled(vendingMachine);
            _vendingMachineManager.OnVendingMachineSpawned(vendingMachine);
        }
        // Undocumented.Intended for MonumentAddons migration to become a Data Provider.private JObject API_MigrateVendingProfile(NPCVendingMachine vendingMachine)
        {
            var location = MonumentRelativePosition.FromVendingMachine(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 bool CheckDependencies() {
            if (MonumentFinder == null) {
                LogError("MonumentFinder is not loaded, get it at http://umod.org.");
                return false;
            }
            return true;
        }
        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 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;
        }
        private MonumentAdapter GetMonumentAdapter(BaseEntity entity) => GetMonumentAdapter(entity.transform.position);
#endregion
#region Exposed Hooks
        private static bool SetupVendingMachineWasBlocked(NPCVendingMachine vendingMachine) {
            object hookResult = Interface.CallHook("OnCustomVendingSetup", vendingMachine);
            return hookResult is bool && (bool)hookResult == false;
        }
        private static object CallHookCanPurchaseItem(BasePlayer player, Item item, Action<BasePlayer, Item> onItemPurchased, NPCVendingMachine vendingMachine, ItemContainer targetContainer) {
            return Interface.CallHook("CanPurchaseItem", player, item, onItemPurchased, vendingMachine, targetContainer);
        }
        private static Dictionary<string, object> CallHookDataProvider(NPCVendingMachine vendingMachine) {
            return Interface.CallHook("OnCustomVendingSetupDataProvider", vendingMachine) as Dictionary<string, object>;
        }
#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";
        }
        [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.Save : vendingController.HandleSave(vendingMachine);
                vendingMachine.FullUpdate();
                OpenVendingMachine(basePlayer, vendingMachine);
                break;
                case UICommands.Cancel : OpenVendingMachine(basePlayer, vendingMachine);
                break;
            }
        }
#endregion
#region Helper Methods
        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 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(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(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 StorageContainer CreateOrdersContainer(BasePlayer player, VendingOffer[] vendingOffers, string shopName) {
            var containerEntity = CreateContainerEntity(StoragePrefab);
            var container = containerEntity.inventory;
            container.allowedContents = ItemContainer.ContentsType.Generic;
            container.capacity = ContainerCapacity;
            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(_pluginInstance._noteItemDefinition);
                if (settingsItem == null) continue;
                var refillMaxLabel = _pluginInstance.GetMessage(player, Lang.SettingsRefillMax);
                var refillDelayLabel = _pluginInstance.GetMessage(player, Lang.SettingsRefillDelay);
                var refillAmountLabel = _pluginInstance.GetMessage(player, Lang.SettingsRefillAmount);
                settingsItem.text = $"{refillMaxLabel}: {offer?.RefillMax ?? VendingOffer.DefaultRefillMax}" + $"\n{refillDelayLabel}: {offer?.RefillDelay ?? VendingOffer.DefaultRefillDelay}" + $"\n{refillAmountLabel}: {offer?.RefillAmount ?? VendingOffer.DefaultRefillAmount}";
                var destinationSlot = OrderIndexToSlot(orderIndex);
                if (!settingsItem.MoveToContainer(container, destinationSlot + 2)) settingsItem.Remove();
            }
            var generalSettingsItem = ItemManager.Create(_pluginInstance._noteItemDefinition);
            if (generalSettingsItem != null) {
                generalSettingsItem.text = shopName;
                if (!generalSettingsItem.MoveToContainer(container, ShopNameNoteSlot)) generalSettingsItem.Remove();
            }
            return containerEntity;
        }
        private static void GiveSoldItem(NPCVendingMachine vendingMachine, Item item, BasePlayer player, MarketTerminal marketTerminal, ItemContainer targetContainer) {
            if (targetContainer == null) {
                vendingMachine.GiveSoldItem(item, player);
            }
            else if (!item.MoveToContainer(targetContainer)) {
                item.Drop(targetContainer.dropPosition, targetContainer.dropVelocity);
            }
            marketTerminal?._onItemPurchasedCached?.Invoke(player, item);
        }
        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;
            uint vendingMachineId;
            if (args.Length == 0 || !uint.TryParse(args[0], out vendingMachineId)) return false;
            vendingMachine = BaseNetworkable.serverEntities.Find(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 = 21;
            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 IconMaterial = "assets/icons/iconmaterial.mat";
            public const string AnchorMin = "0.5 0";
            public const string AnchorMax = "0.5 0";
        }
        private class EditFormState {
            public static EditFormState FromVendingMachine(NPCVendingMachine vendingMachine) {
                return new EditFormState {
                    Broadcast = vendingMachine.IsBroadcasting(),
                }
                ;
            }
            public bool Broadcast;
        }
        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 static string RenderContainerUI(BasePlayer player, NPCVendingMachine vendingMachine, EditFormState uiState) {
                var offsetX = 192;
                var offsetY = 139;
                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)}",
                            }
                            ,
                        }
                        , "Overlay", UIName
                    }
                }
                ;
                var saveButtonText = _pluginInstance.GetMessage(player, Lang.ButtonSave);
                var cancelButtonText = _pluginInstance.GetMessage(player, Lang.ButtonCancel);
                var vendingMachineId = vendingMachine.net.ID;
                AddButton(cuiElements, vendingMachineId, saveButtonText, UICommands.Save, 1, UIConstants.SaveButtonColor, UIConstants.SaveButtonTextColor);
                AddButton(cuiElements, vendingMachineId, cancelButtonText, UICommands.Cancel, 0, UIConstants.CancelButtonColor, UIConstants.CancelButtonTextColor);
                AddBroadcastButton(cuiElements, vendingMachine, uiState);
                var headerOffset = -6;
                cuiElements.Add(new CuiElement {
                    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}",
                        }
                    }
                    , Parent = UIName, Name = TipUIName,
                }
                );
                var forSaleText = _pluginInstance.GetMessage(player, Lang.InfoForSale);
                var costText = _pluginInstance.GetMessage(player, Lang.InfoCost);
                var settingsText = _pluginInstance.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 CuiHelper.ToJson(cuiElements);
            }
            private static void AddHeaderLabel(CuiElementContainer cuiElements, int index, string text) {
                float xMin = 6 + index * (UIConstants.ItemBoxSize + UIConstants.ItemSpacing);
                float xMax = xMin + UIConstants.ItemBoxSize;
                cuiElements.Add(new CuiLabel {
                    Text = {
                        Text = text, Color = UIConstants.CancelButtonTextColor, Align = TextAnchor.MiddleCenter, FontSize = 13,
                    }
                    , RectTransform = {
                        AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"{xMin} 0", OffsetMax = $"{xMax} {UIConstants.HeaderHeight}",
                    }
                }
                , TipUIName );
            }
            private static void AddBroadcastButton(CuiElementContainer cuiElements, NPCVendingMachine vendingMachine, EditFormState uiState) {
                var iconSize = UIConstants.ButtonHeight;
                var xMax = GetButtonOffset(2);
                var xMin = xMax - iconSize;
                cuiElements.Add(new CuiElement {
                    Components = {
                        new CuiButtonComponent {
                            Color = "0 0 0 0", Command = $"customvendingsetup.ui {vendingMachine.net.ID} {UICommands.ToggleBroadcast}",
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"{xMin} 0", OffsetMax = $"{xMax} {UIConstants.ButtonHeight}",
                        }
                        ,
                    }
                    , Parent = UIName, Name = BroadcastUIName,
                }
                );
                cuiElements.Add(new CuiElement {
                    Components = {
                        new CuiRawImageComponent {
                            Color = uiState.Broadcast ? UIConstants.SaveButtonTextColor : UIConstants.CancelButtonTextColor, Sprite = UIConstants.BroadcastIcon, Material = UIConstants.IconMaterial,
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = "0 0", OffsetMax = $"{iconSize} {iconSize}",
                        }
                        ,
                    }
                    , Parent = BroadcastUIName,
                }
                );
            }
            public static string RenderBroadcastUI(BasePlayer player, NPCVendingMachine vendingMachine, EditFormState uiState) {
                var cuiElements = new CuiElementContainer();
                AddBroadcastButton(cuiElements, vendingMachine, uiState);
                return CuiHelper.ToJson(cuiElements);
            }
            private static float GetButtonOffset(int buttonIndex) {
                return UIConstants.PanelWidth - buttonIndex * (UIConstants.ButtonWidth + UIConstants.ButtonHorizontalSpacing);
            }
            private static void AddButton(CuiElementContainer cuiElements, uint vendingMachineId, string text, string subCommand, int buttonIndex, string color, string textColor) {
                var xMax = GetButtonOffset(buttonIndex);
                var xMin = xMax - UIConstants.ButtonWidth;
                cuiElements.Add(new CuiButton {
                    Text = {
                        Text = text, Color = textColor, Align = TextAnchor.MiddleCenter, FontSize = 18,
                    }
                    , Button = {
                        Color = color, FadeIn = 0.1f, Command = $"customvendingsetup.ui {vendingMachineId} {subCommand}",
                    }
                    , RectTransform = {
                        AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"{xMin} 0", OffsetMax = $"{xMax} {UIConstants.ButtonHeight}",
                    }
                    ,
                }
                , UIName );
            }
        }
        private static class AdminUIRenderer {
            public const string UIName = "CustomVendingSetup.AdminUI";
            public static string RenderAdminUI(BasePlayer player, NPCVendingMachine vendingMachine, VendingProfile profile) {
                var numSellOrders = vendingMachine.sellOrders?.sellOrders.Count ?? 0;
                var offsetY = 136 + 74 * numSellOrders;
                var offsetX = 192;
                var cuiElements = new CuiElementContainer {
                    {
                        new CuiPanel {
                            RectTransform = {
                                AnchorMin = UIConstants.AnchorMin, AnchorMax = UIConstants.AnchorMax, OffsetMin = $"{offsetX} {offsetY}", OffsetMax = $"{offsetX} {offsetY}",
                            }
                            ,
                        }
                        , "Overlay", UIName
                    }
                }
                ;
                var buttonIndex = 0;
                var vendingMachineId = vendingMachine.net.ID;
                if (profile != null) {
                    var resetButtonText = _pluginInstance.GetMessage(player, Lang.ButtonReset);
                    AddVendingButton(cuiElements, vendingMachineId, resetButtonText, UICommands.Reset, buttonIndex, UIConstants.ResetButtonColor, UIConstants.ResetButtonTextColor);
                    buttonIndex++;
                }
                var editButtonText = _pluginInstance.GetMessage(player, Lang.ButtonEdit);
                AddVendingButton(cuiElements, vendingMachineId, editButtonText, UICommands.Edit, buttonIndex, UIConstants.SaveButtonColor, UIConstants.SaveButtonTextColor);
                return CuiHelper.ToJson(cuiElements);
            }
            private static float GetButtonOffset(int reverseButtonIndex) {
                return UIConstants.PanelWidth - reverseButtonIndex * (UIConstants.ButtonWidth + UIConstants.ButtonHorizontalSpacing);
            }
            private static void AddVendingButton(CuiElementContainer cuiElements, uint vendingMachineId, string text, string subCommand, int reverseButtonIndex, string color, string textColor) {
                var xMax = GetButtonOffset(reverseButtonIndex);
                var xMin = xMax - UIConstants.ButtonWidth;
                cuiElements.Add(new CuiButton {
                    Text = {
                        Text = text, Color = textColor, Align = TextAnchor.MiddleCenter, FontSize = 18,
                    }
                    , Button = {
                        Color = color, FadeIn = 0.1f, Command = $"customvendingsetup.ui {vendingMachineId} {subCommand}",
                    }
                    , RectTransform = {
                        AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"{xMin} 0", OffsetMax = $"{xMax} {UIConstants.ButtonHeight}",
                    }
                    ,
                }
                , UIName );
            }
        }
#endregion
#region Utilities
        private interface IMonumentRelativePosition {
            string GetMonumentPrefabName();
            string GetMonumentAlias();
            Vector3 GetPosition();
            Vector3 GetLegacyPosition();
        }
        private static bool LocationsMatch(IMonumentRelativePosition a, IMonumentRelativePosition b) {
            var monumentsMatch = a.GetMonumentAlias() != null && a.GetMonumentAlias() == b.GetMonumentAlias() || a.GetMonumentPrefabName() == b.GetMonumentPrefabName();
            if (!monumentsMatch) return false;
            return AreVectorsClose(a.GetPosition(), b.GetPosition()) || AreVectorsClose(a.GetLegacyPosition(), b.GetLegacyPosition());
        }
        private class MonumentRelativePosition : IMonumentRelativePosition {
            public static MonumentRelativePosition FromVendingMachine(NPCVendingMachine vendingMachine) {
                var monument = _pluginInstance.GetMonumentAdapter(vendingMachine);
                if (monument == null) return null;
                return new MonumentRelativePosition {
                    _monument = monument, _position = monument.InverseTransformPoint(vendingMachine.transform.position), _legacyPosition = vendingMachine.transform.InverseTransformPoint(monument.Position),
                }
                ;
            }
            private MonumentAdapter _monument;
            private Vector3 _position;
            private Vector3 _legacyPosition;
            // IMonumentRelativePosition members.public string GetMonumentPrefabName() => _monument.PrefabName;
            public string GetMonumentAlias() => _monument.Alias;
            public Vector3 GetPosition() => _position;
            public Vector3 GetLegacyPosition() => _legacyPosition;
        }
        private enum MatchOptions {
            Skin = 1 << 0, All = ~0,
        }
        private struct ItemSpec {
            public static ItemSpec FromItem(Item item) {
                List<ItemSpec> contents = null;
                if (item.contents != null && item.contents.itemList.Count > 0) {
                    contents = new List<ItemSpec>(item.contents.itemList.Count);
                    foreach (var childItem in item.contents.itemList) {
                        contents.Add(ItemSpec.FromItem(childItem));
                    }
                }
                ItemDefinition ammoType;
                var ammoAmount = GetAmmoAmountAndType(item, out ammoType);
                return new ItemSpec {
                    ItemId = item.info.itemid, BlueprintTarget = item.blueprintTarget, Name = item.name, Skin = item.skin, DataInt = item.instanceData?.dataInt ?? 0, Amount = item.amount, AmmoAmount = ammoAmount, AmmoType = ammoType, Position = item.position, Capacity = item.contents?.capacity ?? 0, Contents = contents,
                }
                ;
            }
            private static int GetAmmoAmountAndType(Item item, out ItemDefinition ammoType) {
                ammoType = null;
                var heldEntity = item.GetHeldEntity();
                if (heldEntity == null) return -1;
                var baseProjectile = heldEntity as BaseProjectile;
                if ((object)baseProjectile != null) {
                    ammoType = baseProjectile.primaryMagazine?.ammoType;
                    return baseProjectile.primaryMagazine?.contents ?? 0;
                }
                var flameThrower = heldEntity as FlameThrower;
                if ((object)flameThrower != null) {
                    return flameThrower.ammo;
                }
                return -1;
            }
            public int ItemId;
            public int BlueprintTarget;
            public string Name;
            public ulong Skin;
            public int DataInt;
            public int Amount;
            public int AmmoAmount;
            public ItemDefinition AmmoType;
            public int Position;
            public int Capacity;
            public List<ItemSpec> Contents;
            public int TargetItemId => IsBlueprint() ? BlueprintTarget : ItemId;
            private ItemDefinition _itemDefinition;
            public ItemDefinition Definition {
                get {
                    if (_itemDefinition == null) _itemDefinition = ItemManager.FindItemDefinition(ItemId);
                    return _itemDefinition;
                }
            }
            public bool IsBlueprint() => BlueprintTarget != 0;
            public Item Create(int amount) {
                var item = ItemManager.Create(Definition, amount, Skin);
                if (BlueprintTarget != 0) {
                    item.blueprintTarget = BlueprintTarget;
                }
                item.name = Name;
                item.position = Position;
                if (DataInt != 0) {
                    if (item.instanceData == null) {
                        item.instanceData = new ProtoBuf.Item.InstanceData();
                        item.instanceData.ShouldPool = false;
                    }
                    item.instanceData.dataInt = DataInt;
                }
                if (Contents != null && Contents.Count > 0) {
                    if (item.contents == null) {
                        item.contents = new ItemContainer();
                        item.contents.ServerInitialize(null, Math.Max(Capacity, Contents.Count));
                        item.contents.GiveUID();
                        item.contents.parent = item;
                    }
                    else {
                        item.contents.capacity = Math.Max(item.contents.capacity, Capacity);
                    }
                    foreach (var childItemSpec in Contents) {
                        var childItem = childItemSpec.Create(childItemSpec.Amount);
                        if (!childItem.MoveToContainer(item.contents, childItemSpec.Position)) {
                            childItem.Remove();
                        }
                    }
                }
                var heldEntity = item.GetHeldEntity();
                if (heldEntity != null) {
                    var baseProjectile = heldEntity as BaseProjectile;
                    if ((object)baseProjectile != null) {
                        var magazine = baseProjectile.primaryMagazine;
                        if (magazine != null) {
                            if (AmmoAmount >= 0) {
                                magazine.contents = AmmoAmount;
                            }
                            if (AmmoType != null) {
                                magazine.ammoType = AmmoType;
                            }
                        }
                    }
                    var flameThrower = heldEntity as FlameThrower;
                    if ((object)flameThrower != null) {
                        flameThrower.ammo = AmmoAmount;
                    }
                }
                return item;
            }
            public Item Split(Item item, int amount) {
                if (amount <= 0 || amount >= item.amount) {
                    return null;
                }
                item.amount -= amount;
                item.MarkDirty();
                return Create(amount);
            }
            public bool Matches(Item item, MatchOptions matchOptions = MatchOptions.All, float minCondition = 0) {
                if (BlueprintTarget != 0) {
                    return BlueprintTarget == item.blueprintTarget;
                }
                if (item.info.itemid != ItemId) {
                    return false;
                }
                if ((matchOptions & MatchOptions.Skin) != 0 && item.skin != Skin) {
                    return false;
                }
                if (minCondition > 0 && item.hasCondition && (item.conditionNormalized < minCondition || item.maxConditionNormalized < minCondition)) {
                    return false;
                }
                if (DataInt != (item.instanceData?.dataInt ?? 0)) {
                    return false;
                }
                return true;
            }
            public Item FirstInContainer(ItemContainer container, MatchOptions matchOptions = MatchOptions.All) {
                foreach (var item in container.itemList) {
                    if (Matches(item, matchOptions)) {
                        return item;
                    }
                }
                return null;
            }
            public int GetAmountInContainer(ItemContainer container, MatchOptions matchOptions = MatchOptions.All) {
                var count = 0;
                foreach (var item in container.itemList) {
                    if (Matches(item, matchOptions)) {
                        count += item.amount;
                    }
                }
                return count;
            }
            public void FindAllInContainer(ItemContainer container, List<Item> resultItemList, ref int sum, MatchOptions matchOptions = MatchOptions.All, float minCondition = 0) {
                foreach (var item in container.itemList) {
                    if (Matches(item, matchOptions, minCondition)) {
                        resultItemList.Add(item);
                        sum += item.amount;
                    }
                }
            }
            public void FindAllInInventory(PlayerInventory playerInventory, List<Item> resultItemList, ref int sum, Mat
// ... [Code preview truncated due to length. Full source in attached file] ...

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