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

FREE RecycleManager : 2.1.0 2.1.0

Нет прав для скачивания
C#:
// #define ENABLE_TESTS
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Oxide.Core;
using Oxide.Core.Libraries;
using UnityEngine;
#if ENABLE_TESTS
using System.Collections;
using System.Reflection;
using Oxide.Core.Plugins;
#endif
namespace Oxide.Plugins {
    [Info("Recycle Manager", "WhiteThunder", "2.1.0")]
    [Description("Allows customizing recycler speed, input, and output")]
    internal class RecycleManager : CovalencePlugin {
#region Fields
        private Configuration _config;
        private const string PermissionAdmin = "recyclemanager.admin";
        private const int ScrapItemId = -932201673;
        private const float ClassicRecycleEfficiency = 0.5f;
        private const float VanillaMaxItemsInStackFraction = 0.1f;
        private readonly object True = true;
        private readonly object False = false;
        private const int NumInputSlots = 6;
        private const int NumOutputSlots = 6;
        private readonly RecyclerComponentManager _recyclerComponentManager = new();
        private readonly RecycleEditManager _recycleEditManager;
        private readonly float[] _recycleTime = new float[1];
        private readonly float[] _recycleEfficiency = new float[1];
        private static readonly FieldInfo ScrapRemainderField = typeof(Recycler).GetField("scrapRemainder", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        private bool IsEditUIEnabled => !_config.UsingDefaults && _config.EditUISettings.Enabled;
#if ENABLE_TESTS
        private readonly RecycleManagerTests _testRunner;
#endif
        public RecycleManager() {
#if ENABLE_TESTS
            _testRunner = new RecycleManagerTests(this);
#endif
            _recycleEditManager = new RecycleEditManager(this);
        }
#endregion
#region Hooks
        private void Init() {
            _config.Init(this);
            _recyclerComponentManager.Init(this);
            permission.RegisterPermission(PermissionAdmin, this);
            if (!_config.Speed.Enabled && !_config.Efficiency.Enabled) {
                Unsubscribe(nameof(OnRecyclerToggle));
            }
            if (!IsEditUIEnabled) {
                Unsubscribe(nameof(OnLootEntity));
            }
        }
        private void OnServerInitialized() {
#if ENABLE_TESTS
            _testRunner.Run();
#endif
            if (IsEditUIEnabled) {
                foreach (var player in BasePlayer.activePlayerList) {
                    var container = player.inventory.loot.containers.FirstOrDefault();
                    if (container == null) continue;
                    var recycler = container.entityOwner as Recycler;
                    if ((object)recycler == null) continue;
                    OnLootEntity(player, recycler);
                }
            }
        }
        private void Unload() {
#if ENABLE_TESTS
            _testRunner.Interrupt();
#endif
            _recyclerComponentManager.Unload();
            _recycleEditManager.Unload();
        }
        // This hook is primarily used to determine whether an item can be placed into the recycler input,
        // but it's also called when processing each item.private object CanBeRecycled(Item item, Recycler recycler)
        {
            if (item == null) return null;
            if (_config.RestrictedInputItems.IsDisallowed(item)) {
                // Defensively return null if vanilla would *disallow* recycling, to avoid hook conflicts.return IsVanillaRecyclable(item) && !RecyclableWasBlocked(item, recycler)
                ? False : null;
            }
            if (_config.OverrideOutput.GetBestOverride(item) != null) {
                // Defensively return null if vanilla would *allow* recycling, to avoid hook conflicts.return IsVanillaRecyclable(item) || RecyclableWasBlocked(item, recycler)
                ? null : True;
            }
            return null;
        }
        private object CanRecycle(Recycler recycler, Item item) {
            return CanBeRecycled(item, recycler);
        }
        private void OnRecyclerToggle(Recycler recycler, BasePlayer player) {
            _recyclerComponentManager.HandleRecyclerToggle(recycler, player);
        }
        private object OnItemRecycle(Item item, Recycler recycler) {
            if (RecycleItemWasBlocked(item, recycler)) return null;
            var maxItemsInStackFraction = _config.MaxItemsPerRecycle.GetPercent(item) / 100f;
            var recycleAmount = DetermineConsumptionAmount(recycler, item, maxItemsInStackFraction);
            if (recycleAmount <= 0) return False;
            var customIngredientList = _config.OverrideOutput.GetBestOverride(item);
            if (customIngredientList != null) {
                item.UseItem(recycleAmount);
                // Overrides already account for standard recycle efficiency, so only calculate based on item condition.if (PopulateOutputWithOverride(recycler, customIngredientList, recycleAmount, DetermineItemRecycleEfficiency(item, 1f)))
                {
                    recycler.StopRecycling();
                }
                return False;
            }
            // If the item is not vanilla recyclable, and this plugin doesn't have an override,
            // that probably means another plugin is going to handle recycling it.if (!IsVanillaRecyclable(item))
            return null;
            // Issue: Last known player will always be null if speed and efficiency are both disabled, since toggle hook
            // is not subscribed to improve performance.var lastKnownPlayer = _recyclerComponentManager.EnsureRecyclerComponent(recycler).Player;
            var recycleEfficiency = DetermineItemRecycleEfficiency(item, GetRecyclerEfficiency(recycler, lastKnownPlayer, out var vanillaEfficiency));
            if (recycleEfficiency == vanillaEfficiency && maxItemsInStackFraction == VanillaMaxItemsInStackFraction && !IsOutputCustomized(item.info.Blueprint)) return null;
            item.UseItem(recycleAmount);
            var outputIsFull = PopulateOutputVanilla(_config, recycler, item, recycleAmount, recycleEfficiency);
            if (outputIsFull || !recycler.HasRecyclable()) {
                recycler.StopRecycling();
            }
            return False;
        }
        private void OnLootEntity(BasePlayer player, Recycler recycler) {
            if (!recycler.onlyOneUser) return;
            if (permission.UserHasPermission(player.UserIDString, PermissionAdmin)) {
                var player2 = player;
                var recycler2 = recycler;
                NextTick(() => {
                    var lootingContainer = player2.inventory.loot.containers.FirstOrDefault();
                    if (lootingContainer == null || lootingContainer != recycler2.inventory) return;
                    _recycleEditManager.HandlePlayerStartedLooting(player2, recycler2);
                }
                );
            }
        }
#endregion
#region Exposed Hooks
        private static class ExposedHooks {
            public static object OnRecycleManagerItemRecyclable(Item item, Recycler recycler) {
                return Interface.CallHook("OnRecycleManagerItemRecyclable", item, recycler);
            }
            public static object OnRecycleManagerSpeed(Recycler recycler, BasePlayer player, float[] recycleTime) {
                return Interface.CallHook("OnRecycleManagerSpeed", recycler, player, recycleTime);
            }
            public static void OnRecycleManagerEfficiency(Recycler recycler, BasePlayer player, float[] recyclerEfficiency) {
                Interface.CallHook("OnRecycleManagerEfficiency", recycler, player, recyclerEfficiency);
            }
            public static object OnRecycleManagerRecycle(Item item, Recycler recycler) {
                return Interface.CallHook("OnRecycleManagerRecycle", item, recycler);
            }
        }
#endregion
#region Commands
        [Command("recyclemanager.add", "recman.add")]
        private void CommandAddItem(IPlayer player, string cmd, string[] args) {
            if (!VerifyHasPermission(player, PermissionAdmin) || !VerifyConfigLoaded(player)) return;
            if (!VerifyValidItemIdOrShortName(player, args.ElementAtOrDefault(0), out var itemDefinition, cmd)) return;
            if (!_config.OverrideOutput.AddOverride(this, itemDefinition)) {
                ReplyToPlayer(player, LangEntry.AddExists, itemDefinition.shortname);
                return;
            }
            SaveConfig();
            ReplyToPlayer(player, LangEntry.AddSuccess, itemDefinition.shortname);
        }
        [Command("recyclemanager.reset", "recman.reset")]
        private void CommandResetItem(IPlayer player, string cmd, string[] args) {
            if (!VerifyHasPermission(player, PermissionAdmin) || !VerifyConfigLoaded(player)) return;
            if (!VerifyValidItemIdOrShortName(player, args.ElementAtOrDefault(0), out var itemDefinition, cmd)) return;
            _config.OverrideOutput.ResetOverride(this, itemDefinition);
            SaveConfig();
            ReplyToPlayer(player, LangEntry.ResetSuccess, itemDefinition.shortname);
        }
        [Command("recyclemanager.ui")]
        private void CommandEdit(IPlayer player, string cmd, string[] args) {
            if (player.IsServer || !player.HasPermission(PermissionAdmin)) return;
            var basePlayer = player.Object as BasePlayer;
            _recycleEditManager.GetController(basePlayer)?.HandleUICommand(basePlayer, args);
        }
#endregion
#region Utilities
        private readonly struct ReflectionAdapter<T> {
            private readonly FieldInfo _fieldInfo;
            private readonly object _object;
            public ReflectionAdapter(object obj, FieldInfo fieldInfo) {
                _object = obj;
                _fieldInfo = fieldInfo;
            }
            public T Value {
                get => _fieldInfo == null ? default : (T)_fieldInfo.GetValue(_object);
                set => _fieldInfo?.SetValue(_object, value);
            }
            public static implicit operator T(ReflectionAdapter<T> adapter) {
                return adapter.Value;
            }
        }
#endregion
#region Helper Methods - Instance
        private bool VerifyHasPermission(IPlayer player, string perm) {
            if (player.HasPermission(perm)) return true;
            ReplyToPlayer(player, LangEntry.ErrorNoPermission);
            return false;
        }
        private bool VerifyConfigLoaded(IPlayer player) {
            if (!_config.UsingDefaults) return true;
            ReplyToPlayer(player, LangEntry.ErrorConfig);
            return false;
        }
        private bool VerifyValidItemIdOrShortName(IPlayer player, string itemArg, out ItemDefinition itemDefinition, string command) {
            if (itemArg == null) {
                itemDefinition = (player.Object as BasePlayer)?.GetActiveItem()?.info;
                if (itemDefinition != null) return true;
                ReplyToPlayer(player, LangEntry.ItemSyntax, command);
                return false;
            }
            if (int.TryParse(itemArg, out var itemId)) {
                itemDefinition = ItemManager.FindItemDefinition(itemId);
                if (itemDefinition != null) return true;
            }
            itemDefinition = ItemManager.FindItemDefinition(itemArg);
            if (itemDefinition != null) return true;
            ReplyToPlayer(player, LangEntry.ErrorInvalidItem, itemArg);
            return false;
        }
        private float GetRecyclerEfficiency(Recycler recycler, BasePlayer player, out float vanillaEfficiency) {
            vanillaEfficiency = DetermineVanillaRecycleEfficiency(recycler);
            _recycleEfficiency[0] = _config.Efficiency.Enabled ? _config.Efficiency.GetRecyclerEfficiency(recycler) : vanillaEfficiency;
            ExposedHooks.OnRecycleManagerEfficiency(recycler, player, _recycleEfficiency);
            return Mathf.Max(0, _recycleEfficiency[0]);
        }
        private bool TryDetermineRecycleTime(Recycler recycler, BasePlayer player, out float recycleTime) {
            _recycleTime[0] = _config.Speed.DefaultRecycleTime * _config.Speed.GetTimeMultiplierForPlayer(player);
            if (_config.Speed.SafeZoneTimeMultiplier != 1 && player.InSafeZone()) {
                _recycleTime[0] *= _config.Speed.SafeZoneTimeMultiplier;
            }
            if (ExposedHooks.OnRecycleManagerSpeed(recycler, player, _recycleTime) is false) {
                recycleTime = 0;
                return false;
            }
            recycleTime = Math.Max(0, _recycleTime[0]);
            return true;
        }
        private object CallCanBeRecycled(Item item, Recycler recycler) {
            Unsubscribe(nameof(CanBeRecycled));
            var hookResult = Interface.CallHook(nameof(CanBeRecycled), item, recycler);
            Subscribe(nameof(CanBeRecycled));
            return hookResult;
        }
        private bool IsOutputCustomized(ItemBlueprint blueprint) {
            if (blueprint.scrapFromRecycle > 0 && _config.OutputMultipliers.GetOutputMultiplier(ScrapItemId) != 1) return true;
            foreach (var ingredient in blueprint.ingredients) {
                // Skip scrap since it's handled separately.if (ingredient.itemDef.itemid == ScrapItemId)
                continue;
                if (_config.OutputMultipliers.GetOutputMultiplier(ingredient.itemid) != 1) return true;
            }
            return false;
        }
#endregion
#region Helper Methods - Static
        public static void LogError(string message) => Interface.Oxide.LogError($"[Recycle Manager] {message}");
        public static void LogWarning(string message) => Interface.Oxide.LogWarning($"[Recycle Manager] {message}");
        private static void Swap<T>(ref T a, ref T b) {
            (a, b) = (b, a);
        }
        private static bool IsVanillaRecyclable(Item item) {
            return item.info.Blueprint != null;
        }
        private static bool RecyclableWasBlocked(Item item, Recycler recycler) {
            return ExposedHooks.OnRecycleManagerItemRecyclable(item, recycler) is false;
        }
        private static bool RecycleItemWasBlocked(Item item, Recycler recycler) {
            return ExposedHooks.OnRecycleManagerRecycle(item, recycler) is false;
        }
        private static Item CreateItem(ItemDefinition itemDefinition, int amount, ulong skinId, string displayName = null) {
            var item = ItemManager.Create(itemDefinition, amount, skinId);
            if (!string.IsNullOrWhiteSpace(displayName)) {
                item.name = displayName;
            }
            return item;
        }
        private static int CalculateOutputAmountVanillaRandom(int recycleAmount, float adjustedIngredientChance) {
            var outputAmount = 0;
            // Roll a random number for every item to consume.for (var i = 0; i < recycleAmount; i++)
            {
                if (UnityEngine.Random.Range(0f, 1f) <= adjustedIngredientChance) {
                    outputAmount++;
                }
            }
            return outputAmount;
        }
        private static int CalculateOutputAmountFast(int recycleAmount, float ingredientAmount) {
            // To save on performance, don't generate hundreds/thousands/millions of random numbers.var outputAmountDecimal = ingredientAmount * recycleAmount;
            var outputAmountInt = (int)outputAmountDecimal;
            // Roll a random number to see if the the remainder should be given.var remainderFractionalOutputAmount = outputAmountDecimal - outputAmountInt;
            if (remainderFractionalOutputAmount > 0 && UnityEngine.Random.Range(0f, 1f) <= remainderFractionalOutputAmount) {
                outputAmountInt++;
            }
            return outputAmountInt;
        }
        private static int CalculateOutputAmountRandom(int recycleAmount, float ingredientAmount, float recycleEfficiency, float outputMultiplier) {
            var adjustedIngredientAmount = ingredientAmount * outputMultiplier;
            if (adjustedIngredientAmount <= 1 && recycleAmount <= 100) return CalculateOutputAmountVanillaRandom(recycleAmount, adjustedIngredientAmount * recycleEfficiency);
            return CalculateOutputAmountFast(recycleAmount, adjustedIngredientAmount * recycleEfficiency);
        }
        private static int CalculateOutputAmountNoRandom(int recycleAmount, float ingredientAmount, float recycleEfficiency, float outputMultiplier) {
            var adjustedIngredientAmount = Mathf.CeilToInt(ingredientAmount * recycleEfficiency) * outputMultiplier;
            return CalculateOutputAmountFast(recycleAmount, adjustedIngredientAmount);
        }
        private static int CalculateOutputAmount(int recycleAmount, float ingredientAmount, float recycleEfficiency, float outputMultiplier = 1) {
            if (ingredientAmount <= 1) return CalculateOutputAmountRandom(recycleAmount, ingredientAmount, recycleEfficiency, outputMultiplier);
            return CalculateOutputAmountNoRandom(recycleAmount, ingredientAmount, recycleEfficiency, outputMultiplier);
        }
        private static bool AddItemToRecyclerOutput(Recycler recycler, ItemDefinition itemDefinition, int ingredientAmount, ulong skinId = 0, string displayName = null) {
            var outputIsFull = false;
            var numStacks = Mathf.CeilToInt(ingredientAmount / (float)itemDefinition.stackable);
            for (var i = 0; i < numStacks; i++) {
                var amountForStack = Math.Min(ingredientAmount, itemDefinition.stackable);
                var outputItem = CreateItem(itemDefinition, amountForStack, skinId, displayName);
                if (!recycler.MoveItemToOutput(outputItem)) {
                    outputIsFull = true;
                }
                ingredientAmount -= amountForStack;
                if (ingredientAmount <= 0) break;
            }
            return outputIsFull;
        }
        private static float DetermineVanillaRecycleEfficiency(Recycler recycler) {
            return recycler.IsSafezoneRecycler() ? recycler.safezoneRecycleEfficiency : recycler.radtownRecycleEfficiency;
        }
        private static IngredientInfo[] GetVanillaOutput(ItemDefinition itemDefinition) {
            if (itemDefinition.Blueprint?.ingredients == null) return Array.Empty<IngredientInfo>();
            var ingredientList = new List<IngredientInfo>();
            if (itemDefinition.Blueprint.scrapFromRecycle > 0) {
                var ingredientInfo = new IngredientInfo {
                    ShortName = "scrap", Amount = itemDefinition.Blueprint.scrapFromRecycle,
                }
                ;
                ingredientInfo.Init();
                ingredientList.Add(ingredientInfo);
            }
            foreach (var blueprintIngredient in itemDefinition.Blueprint.ingredients) {
                if (blueprintIngredient.itemid == ScrapItemId) continue;
                var amount = blueprintIngredient.amount / itemDefinition.Blueprint.amountToCreate * ClassicRecycleEfficiency;
                if (amount > 1) {
                    amount = Mathf.CeilToInt(amount);
                }
                var ingredientInfo = new IngredientInfo {
                    ShortName = blueprintIngredient.itemDef.shortname, Amount = amount,
                }
                ;
                ingredientInfo.Init();
                ingredientList.Add(ingredientInfo);
            }
            return ingredientList.ToArray();
        }
        private static float DetermineItemRecycleEfficiency(Item item, float recyclerEfficiency) {
            return item.hasCondition ? Mathf.Clamp01(recyclerEfficiency * Mathf.Clamp(item.conditionNormalized * item.maxConditionNormalized, 0.1f, 1f)) : recyclerEfficiency;
        }
        private static int DetermineConsumptionAmount(Recycler recycler, Item item, float maxItemsInStackFraction) {
            var recycleAmount = 1;
            if (item.amount > 1) {
                recycleAmount = Mathf.CeilToInt(Mathf.Min(item.amount, item.MaxStackable() * maxItemsInStackFraction));
                // In case the configured multiplier is 0, ensure at least 1 item is recycled.recycleAmount = Math.Max(recycleAmount, 1);
            }
            // Call standard Oxide hook for compatibility.if (Interface.CallHook("OnItemRecycleAmount", item, recycleAmount, recycler) is int overrideAmount)
            return overrideAmount;
            return recycleAmount;
        }
        private static bool PopulateOutputWithOverride(Recycler recycler, IngredientInfo[] customIngredientList, int recycleAmount, float recycleEfficiency = 1, bool forEditor = false) {
            var outputIsFull = false;
            foreach (var ingredientInfo in customIngredientList) {
                if (ingredientInfo.ItemDefinition == null) continue;
                var ingredientAmount = ingredientInfo.Amount;
                if (ingredientAmount <= 0) continue;
                var outputAmount = CalculateOutputAmount(recycleAmount, ingredientAmount, recycleEfficiency);
                if (outputAmount <= 0) continue;
                if (forEditor && outputAmount < 1) {
                    outputAmount = 1;
                }
                if (AddItemToRecyclerOutput(recycler, ingredientInfo.ItemDefinition, outputAmount, ingredientInfo.SkinId, ingredientInfo.DisplayName)) {
                    outputIsFull = true;
                }
            }
            return outputIsFull;
        }
        private static bool PopulateOutputVanilla(Configuration config, Recycler recycler, Item item, int recycleAmount, float recycleEfficiency) {
            var outputIsFull = false;
            if (item.info.Blueprint.scrapFromRecycle > 0) {
                var scrapOutputMultiplier = config.OutputMultipliers.GetOutputMultiplier(ScrapItemId);
                var scrapAmountDecimal = item.info.Blueprint.scrapFromRecycle * (float)recycleAmount * scrapOutputMultiplier;
                if (item.MaxStackable() == 1 && item.hasCondition) {
                    scrapAmountDecimal *= item.conditionNormalized;
                }
                scrapAmountDecimal *= recycleEfficiency / ClassicRecycleEfficiency;
                var scrapAmountInt = Mathf.FloorToInt(scrapAmountDecimal);
                var scrapRemainderForThisCycle = scrapAmountDecimal - scrapAmountInt;
                if (scrapRemainderForThisCycle > 0) {
                    var scrapRemainderAdapter = new ReflectionAdapter<float>(recycler, ScrapRemainderField);
                    var recyclerScrapRemainder = scrapRemainderAdapter.Value;
                    recyclerScrapRemainder += scrapRemainderForThisCycle;
                    var scrapRemainderToOutput = Mathf.FloorToInt(recyclerScrapRemainder);
                    if (scrapRemainderToOutput > 0) {
                        recyclerScrapRemainder -= scrapRemainderToOutput;
                        scrapAmountInt += scrapRemainderToOutput;
                    }
                    scrapRemainderAdapter.Value = recyclerScrapRemainder;
                }
                if (scrapAmountInt >= 1) {
                    var scrapItem = ItemManager.CreateByItemID(ScrapItemId, scrapAmountInt);
                    recycler.MoveItemToOutput(scrapItem);
                }
            }
            foreach (var ingredient in item.info.Blueprint.ingredients) {
                // Skip scrap since it's handled separately.if (ingredient.itemDef.itemid == ScrapItemId)
                continue;
                var ingredientAmount = ingredient.amount / item.info.Blueprint.amountToCreate;
                if (ingredientAmount <= 0) continue;
                var outputAmount = CalculateOutputAmount(recycleAmount, ingredientAmount, recycleEfficiency, config.OutputMultipliers.GetOutputMultiplier(ingredient.itemid) );
                if (outputAmount <= 0) continue;
                if (AddItemToRecyclerOutput(recycler, ingredient.itemDef, outputAmount)) {
                    outputIsFull = true;
                }
            }
            return outputIsFull;
        }
#endregion
#region UI
        private enum IdentificationType {
            Item, Skin, DisplayName,
        }
        private enum OutputType {
            NotRecyclable, Default, Custom,
        }
        private enum UICommand {
            Edit, Reset, Save, Cancel, InputPercentage, ChangeIdentificationType, ChangeOutputType,
        }
        [Flags]
        private enum LayoutOptions {
            AnchorBottom = 1 << 0, AnchorRight = 1 << 1, Vertical = 1 << 2,
        }
        private class LayoutProvider {
            public const string AnchorBottomLeft = "0 0";
            public const string AnchorBottomRight = "1 0";
            public const string AnchorTopLeft = "0 1";
            public const string AnchorTopRight = "1 1";
            public static LayoutProvider Once(float width = 0, float height = 0) {
                return _reusable.WithOffset().WithDimensions(width, height).WithOptions(0).WithSpacing(0);
            }
            private static LayoutProvider _reusable = new(0, 0);
            private LayoutOptions _options;
            private string _anchor;
            private float _x, _y;
            private float _xSpacing, _ySpacing;
            private float _width, _height;
            private bool _isVertical => (_options & LayoutOptions.Vertical) != 0;
            private bool _isLeftToRight => (_options & LayoutOptions.AnchorRight) == 0;
            private bool _isTopToBottom => (_options & LayoutOptions.AnchorBottom) == 0;
            private int _xSign => _isLeftToRight ? 1 : -1;
            private int _ySign => _isTopToBottom ? -1 : 1;
            private float XMin, XMax, YMin, YMax;
            public string AnchorMin => _anchor;
            public string AnchorMax => _anchor;
            public string OffsetMin => $"{XMin.ToString()} {YMin.ToString()}";
            public string OffsetMax => $"{XMax.ToString()} {YMax.ToString()}";
            public LayoutProvider(float width, float height) {
                WithDimensions(width, height);
                WithOptions(0);
            }
            public LayoutProvider WithDimensions(float width, float height) {
                _width = width;
                _height = height;
                return this;
            }
            public LayoutProvider WithOptions(LayoutOptions options) {
                _options = options;
                _anchor = DetermineAnchor();
                return this;
            }
            public LayoutProvider WithSpacing(float x, float y = float.MaxValue) {
                _xSpacing = x;
                _ySpacing = y != float.MaxValue ? y : x;
                return this;
            }
            public LayoutProvider WithOffset(float x = 0, float y = 0) {
                _x = x * _xSign;
                _y = y * _ySign;
                return this;
            }
            public LayoutProvider Next() {
                XMin = _x + _xSpacing * _xSign;
                YMin = _y + _ySpacing * _ySign;
                XMax = XMin + _width * _xSign;
                YMax = YMin + _height * _ySign;
                if (_isVertical) {
                    _y = YMax;
                }
                else {
                    _x = XMax;
                }
                if (YMin > YMax) {
                    Swap(ref YMin, ref YMax);
                }
                if (XMin > XMax) {
                    Swap(ref XMin, ref XMax);
                }
                return this;
            }
            public CuiRectTransformComponent GetRectTransform() {
                return new CuiRectTransformComponent {
                    AnchorMin = _anchor, AnchorMax = _anchor, OffsetMin = OffsetMin, OffsetMax = OffsetMax,
                }
                ;
            }
            private string DetermineAnchor() {
                return _isTopToBottom ? _isLeftToRight ? AnchorTopLeft : AnchorTopRight : _isLeftToRight ? AnchorBottomLeft : AnchorBottomRight;
            }
        }
        private class ButtonColor {
            public readonly string Color;
            public readonly string TextColor;
            public ButtonColor(string color, string textColor) {
                Color = color;
                TextColor = textColor;
            }
        }
        private class ButtonColorScheme {
            public ButtonColor Active;
            public ButtonColor Enabled;
            public ButtonColor Disabled;
            public ButtonColor Get(bool active = false, bool enabled = false) {
                return active ? Active : enabled ? Enabled : Disabled;
            }
        }
        private class CuiInputFieldComponentHud : CuiInputFieldComponent {
            [JsonProperty("hudMenuInput", DefaultValueHandling = DefaultValueHandling.Ignore)]
            public bool HudMenuInput {
                get;
                set;
            }
        }
        private class CuiElementRecreate : CuiElement {
            [JsonProperty("destroyUi", DefaultValueHandling = DefaultValueHandling.Ignore)]
            public string DestroyUi {
                get;
                set;
            }
        }
        private static class EditUI {
            private const string UIName = "RecycleManager.UI";
            private const string EditPanelUIName = "RecycleManager.UI.EditPanel";
            private const string PercentagePanelUIName = "RecycleManager.UI.EditPanel.Percentages";
            private const string AnchorMin = "0.5 0";
            private const string AnchorMax = "0.5 0";
            private const float PanelWidth = 380.5f;
            private const float PanelHeight = 93f;
            private const float HeaderHeight = 21;
            private const int ItemPaddingLeft = 6;
            private const int ItemSize = 58;
            private const int ItemSpacing = 4;
            private const string BaseUICommand = "recyclemanager.ui";
            private const string TextColor = "0.8 0.8 0.8 1";
            private const string BackgroundColor = "0.25 0.25 0.25 1";
            private static ButtonColorScheme DefaultButtonColorScheme = new() {
                Active = new ButtonColor("0.25 0.5 0.75 1", "0.75 0.85 1 1"), Enabled = new ButtonColor("0.4 0.4 0.4 1", "0.71 0.71 0.71 1"), Disabled = new ButtonColor("0.4 0.4 0.4 0.5", "0.71 0.71 0.71 0.5"),
            }
            ;
            private static ButtonColorScheme SaveButtonColorScheme = new() {
                Enabled = new ButtonColor("0.451 0.553 0.271 1", "0.659 0.918 0.2 1"), Disabled = new ButtonColor("0.451 0.553 0.271 0.5", "0.659 0.918 0.2 0.5"),
            }
            ;
            private static ButtonColorScheme ResetButtonColorScheme = new() {
                Enabled = new ButtonColor("0.9 0.5 0.2 1", "1 0.9 0.7 1"), Disabled = new ButtonColor("0.9 0.5 0.2 0.25", "1 0.9 0.7 0.25"),
            }
            ;
            public static void DestroyUI(BasePlayer player) {
                CuiHelper.DestroyUi(player, UIName);
            }
            public static void DrawUI(RecycleManager plugin, BasePlayer player, EditState state) {
                if (state == null) {
                    DrawDefaultUI(plugin, player);
                }
                else {
                    DrawEditUI(plugin, player, state);
                }
            }
            private static void DrawDefaultUI(RecycleManager plugin, BasePlayer player) {
                var elements = CreateContainer();
                AddEditButton(elements, plugin, player);
                CuiHelper.AddUi(player, elements);
            }
            private static void DrawEditUI(RecycleManager plugin, BasePlayer player, EditState state) {
                var elements = CreateContainer();
                AddEditPanel(elements, plugin, player, state);
                CuiHelper.AddUi(player, elements);
            }
            private static CuiElementContainer CreateContainer() {
                var offsetY = 109.5f;
                var offsetX = 192f;
                return new CuiElementContainer {
                    new CuiElementRecreate {
                        Parent = "Hud.Menu", Name = UIName, DestroyUi = UIName, Components = {
                            new CuiRectTransformComponent {
                                AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = $"{offsetX} {offsetY}", OffsetMax = $"{offsetX} {offsetY}",
                            }
                            ,
                        }
                        ,
                    }
                    ,
                }
                ;
            }
            private static void AddEditButton(CuiElementContainer elements, RecycleManager plugin, BasePlayer player) {
                var buttonWidth = 80f;
                var offsetX = PanelWidth - buttonWidth;
                var offsetY = 266f;
                elements.Add(new CuiButton {
                    Text = {
                        Text = plugin.GetMessage(player.UserIDString, LangEntry.UIButtonAdmin), Color = SaveButtonColorScheme.Enabled.TextColor, Align = TextAnchor.MiddleCenter, FontSize = 15,
                    }
                    , Button = {
                        Color = SaveButtonColorScheme.Enabled.Color, Command = $"{BaseUICommand} {UICommand.Edit}", FadeIn = 0.1f,
                    }
                    , RectTransform = {
                        AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"{offsetX} {offsetY}", OffsetMax = $"{offsetX + buttonWidth} {offsetY + HeaderHeight}",
                    }
                    ,
                }
                , UIName);
            }
            private static void AddEditHeader(CuiElementContainer elements, RecycleManager plugin, BasePlayer player) {
                var offsetY = 266f;
                elements.Add(new CuiElement {
                    Parent = EditPanelUIName, Components = {
                        new CuiRawImageComponent {
                            Color = BackgroundColor, Sprite = "assets/content/ui/ui.background.tiletex.psd",
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"0 {offsetY}", OffsetMax = $"{PanelWidth} {offsetY + HeaderHeight}",
                        }
                        ,
                    }
                    ,
                }
                );
                elements.Add(new CuiElement {
                    Parent = EditPanelUIName, Components = {
                        new CuiTextComponent {
                            Text = plugin.GetMessage(player.UserIDString, LangEntry.UIHeader), Align = TextAnchor.MiddleLeft, FontSize = 14, Color = TextColor,
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"5 {offsetY}", OffsetMax = $"{5 + PanelWidth} {offsetY + HeaderHeight}",
                        }
                        ,
                    }
                    ,
                }
                );
                var buttonWidth = 80f;
                var offsetX = PanelWidth - buttonWidth;
                elements.Add(new CuiButton {
                    Text = {
                        Text = plugin.GetMessage(player.UserIDString, LangEntry.UIButtonClose), Color = DefaultButtonColorScheme.Enabled.TextColor, Align = TextAnchor.MiddleCenter, FontSize = 15,
                    }
                    , Button = {
                        Color = DefaultButtonColorScheme.Enabled.Color, Command = $"{BaseUICommand} {UICommand.Cancel}",
                    }
                    , RectTransform = {
                        AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"{offsetX} {offsetY}", OffsetMax = $"{offsetX + buttonWidth} {offsetY + HeaderHeight}",
                    }
                    ,
                }
                , UIName);
            }
            private static void AddEditPanel(CuiElementContainer elements, RecycleManager plugin, BasePlayer player, EditState state) {
                elements.Add(new CuiElement {
                    Parent = UIName, Name = EditPanelUIName, Components = {
                        new CuiRawImageComponent {
                            Color = BackgroundColor, Sprite = "assets/content/ui/ui.background.tiletex.psd",
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = "0 0", OffsetMax = $"{PanelWidth} {PanelHeight}",
                        }
                        ,
                    }
                    ,
                }
                );
                AddEditHeader(elements, plugin, player);
                if (state.BlockedByAnotherPlugin) {
                    elements.Add(new CuiElement {
                        Parent = EditPanelUIName, Components = {
                            new CuiTextComponent {
                                Text = plugin.GetMessage(player.UserIDString, LangEntry.UIItemBlocked), Align = TextAnchor.MiddleCenter, FontSize = 12, Color = TextColor,
                            }
                            , new CuiRectTransformComponent {
                                AnchorMin = "0 0", AnchorMax = "1 1",
                            }
                            ,
                        }
                        ,
                    }
                    );
                    return;
                }
                AddPercentageControllers(elements, state);
                if (state.InputItem == null) {
                    elements.Add(new CuiElement {
                        Parent = EditPanelUIName, Components = {
                            new CuiTextComponent {
                                Text = plugin.GetMessage(player.UserIDString, LangEntry.UIEmptyState), Align = TextAnchor.MiddleCenter, FontSize = 12, Color = TextColor,
                            }
                            , new CuiRectTransformComponent {
                                AnchorMin = "0 0", AnchorMax = "1 1",
                            }
                            ,
                        }
                        ,
                    }
                    );
                    return;
                }
                AddItemIdentificationControls(elements, plugin, player, state);
                AddItemAllowedControls(elements, plugin, player, state);
                AddPrimaryControls(elements, plugin, player, state);
            }
            private static void AddPercentageControllers(CuiElementContainer elements, EditState state) {
                var offsetY = 169;
                elements.Add(new CuiElement {
                    Name = PercentagePanelUIName, Parent = EditPanelUIName, Components = {
                        new CuiRawImageComponent {
                            Color = BackgroundColor, Sprite = "assets/content/ui/ui.background.tiletex.psd",
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"0 {offsetY}", OffsetMax = $"{PanelWidth} {offsetY + HeaderHeight}",
                        }
                        ,
                    }
                    ,
                }
                );
                if (state.InputItem == null) return;
                for (var i = 0; i < 6; i++) {
                    if (state.Chances[i] <= 0) continue;
                    var offsetX = ItemPaddingLeft + i * (ItemSize + ItemSpacing);
                    elements.Add(new CuiElement {
                        Parent = PercentagePanelUIName, Components = {
                            new CuiInputFieldComponentHud {
                                Align = TextAnchor.MiddleCenter, HudMenuInput = true, Text = $"{state.Chances[i]:0.##}%", Color = TextColor, CharsLimit = 6, Command = $"{BaseUICommand} {UICommand.InputPercentage} {i}",
                            }
                            , new CuiRectTransformComponent {
                                AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = $"{offsetX} 0", OffsetMax = $"{offsetX + ItemSize} {HeaderHeight}",
                            }
                            ,
                        }
                        ,
                    }
                    );
                }
            }
            private static void AddButton(CuiElementContainer elements, LayoutProvider layoutProvider, ButtonColorScheme buttonColorScheme, string parent, string text, string command, bool active = false, bool enabled = true) {
                var buttonColor = buttonColorScheme.Get(active, enabled);
                elements.Add(new CuiButton {
                    Text = {
                        Text = text, Color = buttonColor.TextColor, Align = TextAnchor.MiddleCenter, FontSize = 12,
                    }
                    , Button = {
                        Color = buttonColor.Color, Command = enabled && !active ? command : null,
                    }
                    , RectTransform = {
                        AnchorMin = layoutProvider.AnchorMin, AnchorMax = layoutProvider.AnchorMax, OffsetMin = layoutProvider.OffsetMin, OffsetMax = layoutProvider.OffsetMax,
                    }
                    ,
                }
                , parent);
            }
            private static void AddItemIdentificationControls(CuiElementContainer elements, RecycleManager plugin, BasePlayer player, EditState state) {
                var spacingX = 5;
                var spacingY = 2;
                var paddingY = 5;
                var numElements = 4;
                var columnWidth = PanelWidth / 3f;
                var elementHeight = (PanelHeight - 2 * paddingY - (numElements - 1) * spacingY) / numElements;
                var layoutProvider = LayoutProvider.Once(columnWidth - 2 * spacingX, elementHeight).WithOffset(0, paddingY - spacingY).WithSpacing(spacingX, spacingY).WithOptions(LayoutOptions.Vertical);
                elements.Add(new CuiElement {
                    Parent = EditPanelUIName, Components = {
                        new CuiTextComponent {
                            Text = plugin.GetMessage(player.UserIDString, LangEntry.UILabelConfigureBy), Align = TextAnchor.MiddleCenter, FontSize = 12, Color = TextColor,
                        }
                        , layoutProvider.Next().GetRectTransform(),
                    }
                    ,
                }
                );
                AddButton(elements, layoutProvider.Next(), DefaultButtonColorScheme, EditPanelUIName, plugin.GetMessage(player.UserIDString, LangEntry.UIButtonItem), $"{BaseUICommand} {UICommand.ChangeIdentificationType} {IdentificationType.Item}", state.IdentificationType == IdentificationType.Item, state.InputItem.skin != 0);
                AddButton(elements, layoutProvider.Next(), DefaultButtonColorScheme, EditPanelUIName, plugin.GetMessage(player.UserIDString, LangEntry.UIButtonSkin), $"{BaseUICommand} {UICommand.ChangeIdentificationType} {IdentificationType.Skin}", state.IdentificationType == IdentificationType.Skin, state.InputItem.skin != 0);
                AddButton(elements, layoutProvider.Next(), DefaultButtonColorScheme, EditPanelUIName, plugin.GetMessage(player.UserIDString, LangEntry.UIButtonDisplayName), $"{BaseUICommand} {UICommand.ChangeIdentificationType} {IdentificationType.DisplayName}", state.IdentificationType == IdentificationType.DisplayName, !string.IsNullOrWhiteSpace(state.InputItem.name));
            }
            private static void AddItemAllowedControls(CuiElementContainer elements, RecycleManager plugin, BasePlayer player, EditState state) {
                var spacingX = 5;
                var spacingY = 2;
                var paddingY = 5;
                var numElements = 4;
                var columnWidth = PanelWidth / 3f;
                var elementHeight = (PanelHeight - 2 * paddingY - (numElements - 1) * spacingY) / numElements;
                var layoutProvider = LayoutProvider.Once(columnWidth - 2 * spacingX, elementHeight).WithOffset(columnWidth, paddingY - spacingY).WithSpacing(spacingX, spacingY).WithOptions(LayoutOptions.Vertical);
                elements.Add(new CuiElement {
                    Parent = EditPanelUIName, Components = {
                        new CuiTextComponent {
                            Text = plugin.GetMessage(player.UserIDString, LangEntry.UILabelOutput), Align = TextAnchor.MiddleCenter, FontSize = 12, Color = TextColor,
                        }
                        , layoutProvider.Next().GetRectTransform(),
                    }
                    ,
                }
                );
                AddButton(elements, layoutProvider.Next(), DefaultButtonColorScheme, EditPanelUIName, plugin.GetMessage(player.UserIDString, LangEntry.UIButtonNotRecyclable), $"{BaseUICommand} {UICommand.ChangeOutputType} {OutputType.NotRecyclable}", state.OutputType == OutputType.NotRecyclable);
                AddButton(elements, layoutProvider.Next(), DefaultButtonColorScheme, EditPanelUIName, plugin.GetMessage(player.UserIDString, LangEntry.UIButtonDefaultOutput), $"{BaseUICommand} {UICommand.ChangeOutputType} {OutputType.Default}", state.OutputType == OutputType.Default, state.IdentificationType != IdentificationType.Item || IsVanillaRecyclable(state.InputItem));
                AddButton(elements, layoutProvider.Next(), DefaultButtonColorScheme, EditPanelUIName, plugin.GetMessage(player.UserIDString, LangEntry.UIButtonCustomOutput), $"{BaseUICommand} {UICommand.ChangeOutputType} {OutputType.Custom}", state.OutputType == OutputType.Custom);
            }
            private static void AddPrimaryControls(CuiElementContainer elements, RecycleManager plugin, BasePlayer player, EditState state) {
                var spacingX = 5;
                var spacingY = 2;
                var paddingY = 5;
                var numElements = 4;
                var columnWidth = PanelWidth / 3f;
                var elementHeight = (PanelHeight - 2 * paddingY - (numElements - 1) * spacingY) / numElements;
                var layoutProvider = LayoutProvider.Once(columnWidth - 2 * spacingX, elementHeight).WithOffset(2 * columnWidth, paddingY - spacingY).WithSpacing(spacingX, spacingY).WithOptions(LayoutOptions.Vertical);
                elements.Add(new CuiElement {
                    Parent = EditPanelUIName, Components = {
                        new CuiTextComponent {
                            Text = plugin.GetMessage(player.UserIDString, LangEntry.UILabelActions), Align = TextAnchor.MiddleCenter, FontSize = 12, Color = TextColor,
                        }
                        , layoutProvider.Next().GetRectTransform(),
                    }
                    ,
                }
 
// ... [Code preview truncated due to length. Full source in attached file] ...

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