organize, normalize, melhore as nomenclaturas desse plugin:
using System.Collections.Generic;
using System.Linq;
using Facepunch;
using Network;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Quarry Computer", "Marte6", "1.0.0")]
    [Description("Manages Quarry Computers")]
    public class QuarryComputer : CovalencePlugin
    {
        [PluginReference]
        Plugin ImageLibrary;

        #region Constants
        private const bool ForceConfigUpdate = false;
        private const Item.Flag ExtractedItemFlag = (Item.Flag)(1 << 78);
        private const ulong QuarrySkinId = 3341965622;
        private const string BaseItemName = "arcade.machine.chippy";
        private const string BaseDeployedItemName = "chippyarcademachine";
        private const string QuarryPrefabPath = "assets/content/structures/excavator/prefabs/excavatorsignalcomputer.prefab";
        private const string QuarryOutputStoragePrefab = "assets/prefabs/deployable/hot air balloon/subents/hab_storage.prefab";
        private const string QuarryFuelStoragePrefab = "assets/content/vehicles/boats/rowboat/subents/rowboat_storage.prefab";
        private const string StorageAdaptorPrefab = "assets/prefabs/deployable/playerioents/industrialadaptors/storageadaptor.deployed.prefab";
        #endregion

        #region Fields
        private static QuarryComputer _instance;
        private Configuration _config;
        private readonly List<string> _permissions = new() { "quarrycomputer.give", "quarrycomputer.industrial", "quarrycomputer.vip" };
        private static readonly HashSet<BasePlayer> playersWithPopup = new();
        #endregion

        #region Initialization
        private void OnServerInitialized()
        {
            _instance = this;
            foreach (string perm in _permissions)
                permission.RegisterPermission(perm, this);

            AddCovalenceCommand(_config.CommandsToGiveQuarry, "GiveQuarryCommand");
            AddCovalenceCommand(_config.CommandsToRemoveQuarry, "RemoveQuarryCommand");

            foreach (var signalComputer in UnityEngine.Object.FindObjectsOfType<ExcavatorSignalComputer>())
            {
                if (signalComputer.OwnerID != 0)
                {
                    signalComputer.gameObject.AddComponent<QuarryComputerBehaviour>();
                }
            }
        }

        private void Init()
        {
            DestroyAllUi();
        }

        private void Unload()
        {
            var behaviors = UnityEngine.Object.FindObjectsOfType<QuarryComputerBehaviour>();
            foreach (var behavior in behaviors)
            {
                behavior.Unload();
                UnityEngine.Object.Destroy(behavior);
            }
        }
        #endregion

        #region Configuration
        private class Configuration
        {
            [JsonProperty("Version")]
            public string Version { get; set; }

            [JsonProperty("Give Quarry Command")]
            public string[] CommandsToGiveQuarry { get; set; }

            [JsonProperty("Remove Quarry Command")]
            public string[] CommandsToRemoveQuarry { get; set; }

            [JsonProperty("Resource Extraction Interval In Seconds")]
            public int ResourceExtractionIntervalSeconds { get; set; }

            [JsonProperty("Low Grade Fuel Required Per Extraction")]
            public int FuelPerExtraction { get; set; }

            [JsonProperty("Resources Output Per Extraction")]
            public List<ResourceItem> ResourceOutput { get; set; }

            [JsonProperty("No Upgrades")]
            public NoUpgrades NoUpgrades { get; set; }

            [JsonProperty("Upgrades")]
            public List<UpgradeSettings> UpgradesList { get; set; }

            [JsonProperty("Keep Upgrades On Remove Quarry")]
            public bool RetainUpgrades { get; set; }

            [JsonProperty("Profiles")]
            public UserProfiles Profiles { get; set; }
        }

        private class NoUpgrades
        {
            [JsonProperty("Fuel Storage Slots")]
            public int FuelStorageSlots { get; set; }

            [JsonProperty("Output Storage Slots")]
            public int OutputStorageSlots { get; set; }
        }

        private class ResourceItem
        {
            [JsonProperty("Shortname")]
            public string ItemShortName { get; set; }

            [JsonProperty("Amount")]
            public int Amount { get; set; }
        }

        private class UpgradeSettings
        {
            [JsonProperty("Items To Upgrade")]
            public List<ResourceItem> ItemsForUpgrading { get; set; }

            [JsonProperty("Fuel Storage Slots")]
            public int FuelStorageSlots { get; set; }

            [JsonProperty("Output Storage Slots")]
            public int OutputStorageSlots { get; set; }

            [JsonProperty("Gather Multiplier")]
            public float GatherMultiplier { get; set; }
        }

        private class UserProfiles
        {
            [JsonProperty("Default (No Permission)")]
            public Profile DefaultProfile { get; set; }

            [JsonProperty("Vip (quarrycomputer.vip Permission)")]
            public Profile VIPProfile { get; set; }
        }

        private class Profile
        {
            [JsonProperty("Quarry Limit")]
            public int QuarryLimit { get; set; }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            _config = Config.ReadObject<Configuration>();

            EnsureConfigUpToDate();

            SaveConfig();
        }

        protected override void LoadDefaultConfig() => _config = InitializeDefaultConfig();

        protected override void SaveConfig() => Config.WriteObject(_config, true);

        private void EnsureConfigUpToDate()
        {
            if (ForceConfigUpdate || string.Compare(_config.Version, Version.ToString()) < 0)
            {
                PrintWarning("Config changes detected, updating...");

                _config = InitializeDefaultConfig();

                PrintWarning($"Config update complete, updated to version {Version}");
                _config.Version = Version.ToString();
            }
        }

        private Configuration InitializeDefaultConfig()
        {
            return new Configuration
            {
                Version = Version.ToString(),
                CommandsToGiveQuarry = new[] { "gq", "givequarry" },
                CommandsToRemoveQuarry = new[] { "rq", "removequarry" },
                ResourceExtractionIntervalSeconds = 5,
                FuelPerExtraction = 5,
                ResourceOutput = new List<ResourceItem>
                {
                    new ResourceItem { ItemShortName = "crude.oil", Amount = 3 },
                    new ResourceItem { ItemShortName = "hq.metal.ore", Amount = 2 },
                    new ResourceItem { ItemShortName = "metal.ore", Amount = 42 },
                    new ResourceItem { ItemShortName = "stones", Amount = 300 },
                    new ResourceItem { ItemShortName = "sulfur.ore", Amount = 40 },
                },
                NoUpgrades = new NoUpgrades { FuelStorageSlots = 3, OutputStorageSlots = 6 },
                UpgradesList = new List<UpgradeSettings>
                {
                    new UpgradeSettings
                    {
                        ItemsForUpgrading = new List<ResourceItem>
                        {
                            new ResourceItem { ItemShortName = "scrap", Amount = 5000 },
                            new ResourceItem { ItemShortName = "bone.fragments", Amount = 10000 },
                        },
                        FuelStorageSlots = 6,
                        OutputStorageSlots = 12,
                        GatherMultiplier = 1.5f,
                    },
                    new UpgradeSettings
                    {
                        ItemsForUpgrading = new List<ResourceItem>
                        {
                            new ResourceItem { ItemShortName = "lowgradefuel", Amount = 10000 },
                            new ResourceItem { ItemShortName = "scrap", Amount = 10000 },
                            new ResourceItem { ItemShortName = "bone.fragments", Amount = 20000 },
                        },
                        FuelStorageSlots = 12,
                        OutputStorageSlots = 24,
                        GatherMultiplier = 2f,
                    },
                },
                RetainUpgrades = true,
                Profiles = new UserProfiles
                {
                    DefaultProfile = new Profile { QuarryLimit = 1 },
                    VIPProfile = new Profile { QuarryLimit = 3 },
                },
            };
        }
        #endregion

        #region Localization
        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(
                new Dictionary<string, string>
                {
                    ["NoPerms"] = "You don't have permissions to use this command",
                    ["NoBuildingAuth"] = "You must have building auth to use this",
                    ["NotMiningQuarry"] = "Este objeto não é uma Mining Quarry",
                    ["NotOwn"] = "Objeto de outro proprietário",
                    ["QuarryRemoved"] = "Mining Quarry Removida",
                    ["NoObjectsFound"] = "No Object found",
                },
                this
            );
        }
        #endregion

        #region Commands
        [Command("ui")]
        private void CmdOpenUi(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;

            DestroyUi(player);
            CreateUi(player);
        }

        [Command("quarrycompui.close")]
        private void CmdClose(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;
            if (player != null)
            {
                DestroyUi(player);
                NotifyPlayer(iplayer, "Close button clicked.");
            }
        }

        [Command("quarrycompui.remove")]
        private void CmdRemoveQuarry(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;
            TogglePopup(player, "Remove the Quarry?", "quarrycompui.confirmremove", "quarrycompui.cancelremove");
        }

        [Command("quarrycompui.confirmremove")]
        private void CmdConfirmRemove(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;
            if (player != null)
            {
                ClosePopupAndNotify(player, "Quarry removed.");
            }
        }

        [Command("quarrycompui.cancelremove")]
        private void CmdCancelRemove(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;
            if (player != null)
            {
                ClosePopupAndNotify(player, "Removal cancelled.");
            }
        }

        [Command("quarrycompui.upgrade")]
        private void CmdUpgradeQuarry(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;
            TogglePopup(player, "Upgrade the Quarry?", "quarrycompui.confirmupgrade", "quarrycompui.cancelupgrade");
        }

        [Command("quarrycompui.confirmupgrade")]
        private void CmdConfirmUpgrade(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;
            if (player != null)
            {
                ClosePopupAndNotify(player, "Quarry upgraded.");
            }
        }

        [Command("quarrycompui.cancelupgrade")]
        private void CmdCancelUpgrade(IPlayer iplayer)
        {
            var player = (BasePlayer)iplayer.Object;
            if (player != null)
            {
                ClosePopupAndNotify(player, "Upgrade cancelled.");
            }
        }
        #endregion

        #region Ui

        private void CreateUi(BasePlayer player)
        {
            var container = new CuiElementContainer();

            AddBackground(container);
            AddMainPanel(container);
            AddImage(container);

            AddLeftPanel(container);
            AddRightPanel(container);

            CuiHelper.AddUi(player, container);
        }

        private void AddBackground(CuiElementContainer container)
        {
            AddPanel(container, "0 0", "1 1", "QuarryCompUi_Background", "Overlay", "0 0 0 0.9", true);
        }

        private void AddMainPanel(CuiElementContainer container)
        {
            AddPanel(container, "0.235 0.18", "0.765 0.784", "QuarryCompUi_MainPanel", "QuarryCompUi_Background", "0.6 0.6 0.6 0.95", true);
            AddInnerSquare(container, "QuarryCompUi_MainPanel", "0 0.9570", "1 1", "0.39 0.39 0.39 1");
            AddText(container, "QuarryCompUi_MainPanel", "0.02 0.96", "1 1", "Extraction Interval: 1 sec | Fuel per extraction: 1 Low Grade Fuel", 13, TextAnchor.MiddleLeft);
            AddCloseButton(container, "QuarryCompUi_MainPanel");
        }

        private void AddImage(CuiElementContainer container)
        {
            AddImage(container, "QuarryCompUi_Background", "crude.oil", "0.1 0.1", "0.2 0.2", "ResImg");
        }

        private void AddLeftPanel(CuiElementContainer container)
        {
            var parent = "QuarryCompUi_Full_Left";
            AddPanel(container, "0.0 0", "0.5 0.94", parent, "QuarryCompUi_MainPanel", "0 0 0 0");
            AddInnerPanelStructure(container, parent, "Quarry Level 1", "0 0 1 1");

            Texts(new[] { "Storage", "Fuel: 3 slots", "Resource: 6 slots" }, 0.1f, 0.8f, 0.04f, container, parent);
            AddHorizontalLine(container, parent, "0.06 0.70", "0.89 0.705");

            Texts(new[] { "Extraction", "Crude Oil: 10", "HQM Ore: 60", "Metal Ore: 100", "Stones: 250", "Sulfur Ore: 160" }, 0.1f, 0.635f, 0.04f, container, parent);

            AddButton(container, parent, "Remove Quarry", "quarrycompui.remove", "0.28", "0.11", "0.70", "0.16", "0.6 0.6 0.6 0.95", "1 0 0 1");
        }

        private void AddRightPanel(CuiElementContainer container)
        {
            var parent = "QuarryCompUi_Full_Right";
            AddPanel(container, "0.5 0", "1 0.94", parent, "QuarryCompUi_MainPanel", "0 0 0 0");
            AddInnerPanelStructure(container, parent, "Upgrade to Level 2", "0.5 0 0 1");

            Texts(new[] { "Storage", "Fuel: 6 slots", "Resource: 12 slots" }, 0.1f, 0.8f, 0.04f, container, parent);
            AddHorizontalLine(container, parent, "0.06 0.70", "0.89 0.705");

            Texts(new[] { "Extraction", "Crude Oil: 20", "HQM Ore: 100", "Metal Ore: 150", "Stones: 300", "Sulfur Ore: 200" }, 0.1f, 0.635f, 0.04f, container, parent);

            AddHorizontalLine(container, parent, "0.06 0.425", "0.89 0.430");

            Texts(new[] { "Requirements", "Scrap: 5000", "Bone Fragments: 10000" }, 0.1f, 0.360f, 0.04f, container, parent);

            AddButton(container, parent, "Upgrade", "quarrycompui.upgrade", "0.35", "0.11", "0.65", "0.16", "0.6 0.6 0.6 0.95", "0 1 0 1");
        }

        private void AddInnerPanelStructure(CuiElementContainer container, string parent, string title, string color)
        {
            AddInnerSquare(container, parent, "0.10 0.05", "0.95 0.90", "0 0 0 0.97");
            AddInnerSquare(container, parent, "0.054 0.1033", "0.895 0.945", "0.8 0.8 0.8 1");
            AddInnerSquare(container, parent, "0.06 0.108", "0.89 0.94", color);
            AddHorizontalLine(container, parent, "0.06 0.865", "0.89 0.870");

            AddText(container, parent, "0.1 0.84", "0.9 0.98", title, 15, TextAnchor.MiddleCenter);
        }

        private void AddHorizontalLine(CuiElementContainer container, string parent, string anchorMin, string anchorMax)
        {
            AddInnerSquare(container, parent, anchorMin, anchorMax, "0.8 0.8 0.8 1");
        }

        private void Texts(string[] texts, float anchorX, float initialAnchorY, float spacing, CuiElementContainer container, string parent)
        {
            for (int i = 0; i < texts.Length; i++)
            {
                float anchorY = initialAnchorY - (spacing * i);
                AddText(container, parent, $"{anchorX} {anchorY}", $"{anchorX + 0.8f} {anchorY + 0.06}", texts[i], 12, TextAnchor.MiddleLeft);
            }
        }

        private void AddImage(CuiElementContainer container, string parent, string resourceName, string anchorMin, string anchorMax, string imageName)
        {
            string imageUrl = ImageLibrary?.Call("GetImage", resourceName) as string;
            if (string.IsNullOrEmpty(imageUrl))
            {
                PrintWarning($"Failed to get image for item: {resourceName}");
                return;
            }

            container.Add(
                new CuiElement
                {
                    Name = imageName,
                    Parent = parent,
                    Components =
                    {
                        new CuiRawImageComponent { Png = imageUrl },
                        new CuiRectTransformComponent { AnchorMin = anchorMin, AnchorMax = anchorMax },
                    },
                }
            );
        }

        private void AddCloseButton(CuiElementContainer container, string parent)
        {
            container.Add(
                new CuiButton
                {
                    Button =
                    {
                        Color = "1 0 0 1",
                        Command = "quarrycompui.close",
                        Close = "QuarryCompUi_Background",
                    },
                    RectTransform = { AnchorMin = "0.965 0.957", AnchorMax = "1 1" },
                    Text =
                    {
                        Text = "✖",
                        FontSize = 13,
                        Align = TextAnchor.MiddleCenter,
                        Color = "1 1 1 0.5",
                    },
                },
                parent
            );
        }

        private void DestroyUi(BasePlayer player)
        {
            if (player == null)
                return;

            var componentNames = new[]
            {
                "QuarryCompUi_Background",
                "QuarryCompUi_MainPanel",
                "Overlay_Block",
                "Popup_Shadow",
                "Popup_Border",
                "Popup_Background",
                "QuarryCompUi_Full_Left",
                "QuarryCompUi_Full_Right",
            };

            foreach (var name in componentNames)
            {
                CuiHelper.DestroyUi(player, name);
            }

            playersWithPopup.Remove(player);
        }

        private void DestroyAllUi()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyUi(player);
                timer.Once(1f, () => CreateUi(player));
            }
        }

        private void TogglePopup(BasePlayer player, string message, string confirmCmd, string cancelCmd)
        {
            if (player != null && !playersWithPopup.Contains(player))
            {
                playersWithPopup.Add(player);
                ShowPopup(player, message, confirmCmd, cancelCmd);
            }
        }

        private void ShowPopup(BasePlayer player, string message, string confirmCmd, string cancelCmd)
        {
            var container = new CuiElementContainer();
            AddPanel(container, "0 0", "1 1", "Overlay_Block", "Overlay", "0 0 0 0.9", true);
            AddPanel(container, "0.4 0.4", "0.6 0.6", "Popup_Shadow", "Overlay_Block", "0 0 0 0.85");
            AddPanel(container, "0.0 0.09", "0.95 1.0", "Popup_Border", "Popup_Shadow", "1 1 1 1");
            AddPanel(container, "0.01 0.01", "0.991 0.985", "Popup_Background", "Popup_Border", "0 0 0 0.97");
            AddText(container, "Popup_Background", "0.1 0.6", "0.9 0.9", message, 16, TextAnchor.MiddleCenter);
            AddButton(container, "Popup_Background", "Yes", confirmCmd, "0.25", "0.05", "0.45", "0.15", "0.6 0.6 0.6 0.95", "0 1 0 1");
            AddButton(container, "Popup_Background", "No", cancelCmd, "0.55", "0.05", "0.75", "0.15", "0.6 0.6 0.6 0.95", "1 0 0 1");
            CuiHelper.AddUi(player, container);
        }

        private void ClosePopupAndNotify(BasePlayer player, string message)
        {
            if (player != null)
            {
                foreach (var elem in new[] { "Overlay_Block", "Popup_Shadow", "Popup_Border", "Popup_Background" })
                {
                    CuiHelper.DestroyUi(player, elem);
                }

                playersWithPopup.Remove(player);
                player.ChatMessage(message);
            }
        }
        #endregion

        #region UI Helpers

        private void AddPanel(CuiElementContainer container, string anchorMin, string anchorMax, string panelName, string parent, string color, bool isCursorEnabled = false)
        {
            var panel = new CuiPanel
            {
                Image = { Color = color },
                RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax },
                CursorEnabled = isCursorEnabled,
            };

            container.Add(panel, parent, panelName);
        }

        private void AddInnerSquare(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string color)
        {
            container.Add(new CuiPanel { Image = { Color = color }, RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax } }, parent);
        }

        private void AddText(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string text, int fontSize, TextAnchor alignment)
        {
            container.Add(
                new CuiLabel
                {
                    Text =
                    {
                        Text = text,
                        FontSize = fontSize,
                        Font = "DroidSansMono.ttf",
                        Align = alignment,
                        Color = "1 1 1 1",
                    },
                    RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax },
                },
                parent
            );
        }

        private void AddButton(
            CuiElementContainer container,
            string parent,
            string text,
            string command,
            string anchorMinX,
            string anchorMinY,
            string anchorMaxX,
            string anchorMaxY,
            string bgColor,
            string textColor
        )
        {
            container.Add(
                new CuiButton
                {
                    Button =
                    {
                        Color = bgColor,
                        Command = command,
                        Close = "",
                    },
                    RectTransform = { AnchorMin = $"{anchorMinX} {anchorMinY}", AnchorMax = $"{anchorMaxX} {anchorMaxY}" },
                    Text =
                    {
                        Text = text,
                        FontSize = 14,
                        Font = "DroidSansMono.ttf",
                        Align = TextAnchor.MiddleCenter,
                        Color = textColor,
                    },
                },
                parent
            );
        }
        #endregion

        #region Game Hooks
        private void OnEntityBuilt(Planner planner, GameObject builtObject)
        {
            var player = planner?.GetOwnerPlayer();
            var entity = builtObject.GetComponent<BaseEntity>();

            if (player == null || entity == null || entity.ShortPrefabName != BaseDeployedItemName || entity.skinID != QuarrySkinId)
                return;

            var computerCountForPlayer = BaseNetworkable.serverEntities.OfType<ExcavatorSignalComputer>().Count(x => x.OwnerID == player.userID);
            var quarryLimitForPlayer = _config.Profiles.DefaultProfile.QuarryLimit;

            if (permission.UserHasPermission(player.UserIDString, "quarrycomputer.vip"))
            {
                quarryLimitForPlayer = _config.Profiles.VIPProfile.QuarryLimit;
            }

            if (!player.IsBuildingAuthed() || computerCountForPlayer >= quarryLimitForPlayer)
            {
                if (entity && !entity.IsDestroyed)
                    entity.Kill();

                NotifyPlayer(player.IPlayer, !player.IsBuildingAuthed() ? "NoBuildingAuth" : "AtLimit");

                timer.Once(1, () => GiveQuarryItem(player));
                return;
            }

            DeployQuarry(player, entity);
            NextTick(() =>
            {
                entity?.Kill();
            });
        }

        private ItemContainer.CanAcceptResult? CanAcceptItem(ItemContainer container, Item item)
        {
            if ((container.entityOwner is not StorageContainer storageContainer) || container.entityOwner.name == null)
                return null;

            QuarryComputerBehaviour computerBehaviour = container.entityOwner.GetComponentInParent<QuarryComputerBehaviour>();
            if (computerBehaviour == null)
                return null;

            if (container.entityOwner.name == QuarryFuelStoragePrefab && item.info.shortname != "lowgradefuel")
                return ItemContainer.CanAcceptResult.CannotAccept;

            if (container.entityOwner.name == QuarryOutputStoragePrefab && !item.flags.HasFlag(ExtractedItemFlag))
                return ItemContainer.CanAcceptResult.CannotAccept;

            return null;
        }

        private object CanPickupEntity(BasePlayer player, IndustrialStorageAdaptor entity)
        {
            return entity.GetComponentInParent<QuarryComputerBehaviour>() != null ? (object)false : null;
        }

        private object OnExcavatorSuppliesRequest(ExcavatorSignalComputer computer, BasePlayer player)
        {
            return computer.GetComponent<QuarryComputerBehaviour>() != null ? (object)true : null;
        }

        private object OnButtonPress(PressButton button, BasePlayer player)
        {
            var parentEntity = button.GetParentEntity();
            if (parentEntity != null && parentEntity.GetComponentInParent<QuarryComputerBehaviour>() != null)
            {
                if (parentEntity.GetParentEntity().OwnerID == player.userID)
                {
                    DestroyUi(player);
                    CreateUi(player);
                }
                else
                {
                    Puts("Belongs to another user");
                }
            }
            return null;
        }
        #endregion

        #region Commands
        private void GiveQuarryCommand(IPlayer iplayer, string command, string[] args)
        {
            var player = iplayer?.Object as BasePlayer;
            if (player == null || args == null || args.Length < 1)
                return;

            if (!iplayer.HasPermission("quarrycomputer.give"))
            {
                NotifyPlayer(iplayer, "NotAllowed");
                return;
            }

            var targetPlayer = BasePlayer.Find(args[0]);
            if (targetPlayer == null)
            {
                NotifyPlayer(iplayer, "PlayerNotFoundError");
                return;
            }

            NotifyPlayer(iplayer, GiveQuarryItem(targetPlayer) ? "QuarryReceived" : "QuarryDroppedOff");
        }

        private void RemoveQuarryCommand(IPlayer iplayer, string command, string[] args)
        {
            var player = iplayer?.Object as BasePlayer;
            if (player == null)
                return;

            if (!TryFindQuarryInCrosshair(player, out var quarryEntity))
            {
                NotifyPlayer(iplayer, "NotMiningQuarry");
                return;
            }

            if (quarryEntity.OwnerID != player.userID)
            {
                NotifyPlayer(iplayer, "NotAuthorizedOwner");
                return;
            }

            quarryEntity.Kill();
            NotifyPlayer(iplayer, "QuarryRemoved");
        }

        private bool TryFindQuarryInCrosshair(BasePlayer player, out BaseEntity quarryEntity)
        {
            quarryEntity = null;
            int layerMask = LayerMask.GetMask("Default", "TransparentFX", "IgnoreRaycast", "Water");

            if (!Physics.Raycast(player.eyes.HeadRay(), out var raycastHit, 3f, layerMask, QueryTriggerInteraction.Ignore))
                return false;

            quarryEntity = raycastHit.collider.ToBaseEntity();
            return quarryEntity != null && quarryEntity.name == QuarryPrefabPath;
        }
        #endregion

        #region Methods
        private Item CreateQuarryItem()
        {
            var item = ItemManager.CreateByName(BaseItemName, 1, QuarrySkinId);
            item.name = "Quarry Computer";
            return item;
        }

        private bool GiveQuarryItem(BasePlayer player)
        {
            var item = CreateQuarryItem();

            if (!player.inventory.GiveItem(item))
            {
                item.Drop(player.inventory.containerMain.dropPosition, player.inventory.containerMain.dropVelocity);
                return false;
            }

            player.Command("note.inv", item.info.itemid, 1, item.name, (int)BaseEntity.GiveItemReason.PickedUp);
            return true;
        }

        private void DeployQuarry(BasePlayer player, BaseEntity entity)
        {
            var position = entity.transform.position;
            var rotation = Quaternion.Euler(0, entity.transform.rotation.eulerAngles.y + 90, 0);
            var quarry = GameManager.server.CreateEntity(QuarryPrefabPath, position, rotation);
            quarry.OwnerID = player.userID;
            quarry.Spawn();
            quarry.SendNetworkUpdateImmediate();

            AttachContainer(quarry, QuarryOutputStoragePrefab, new Vector3(0.05f, 0.75f, -0.21f), new Vector3(0, 180, 0), false, player);
            AttachContainer(quarry, QuarryFuelStoragePrefab, new Vector3(0.05f, 0.75f, 0.32f), new Vector3(90, 0, 0), true, player);

            quarry.gameObject.AddComponent<QuarryComputerBehaviour>();
        }

        private void AttachContainer(BaseEntity entity, string prefab, Vector3 localPosition, Vector3 localEulerAngles, bool isFuelContainer, BasePlayer player)
        {
            var container = GameManager.server.CreateEntity(prefab) as StorageContainer;
            container.SetParent(entity);
            container.transform.localPosition = localPosition;
            container.transform.localRotation = Quaternion.Euler(localEulerAngles);
            container.Spawn();
            container.SendNetworkUpdateImmediate();

            if (permission.UserHasPermission(player.UserIDString, "quarrycomputer.industrial"))
            {
                var adaptor = GameManager.server.CreateEntity(StorageAdaptorPrefab, container.transform.position, container.transform.rotation) as IndustrialStorageAdaptor;
                adaptor.SetParent(container, true, true);
                adaptor.transform.localPosition = isFuelContainer ? new Vector3(0.00f, -0.06f, -0.26f) : new Vector3(0.00f, 0.26f, 0.05f);
                adaptor.transform.rotation = container.transform.rotation * Quaternion.Euler(isFuelContainer ? new Vector3(0, 270, 90) : new Vector3(0, 90, 0));
                adaptor.Spawn();
                adaptor.SendNetworkUpdateImmediate();
            }

            if (!isFuelContainer)
            {
                var button = GameManager.server.CreateEntity("assets/prefabs/io/electric/switches/pressbutton/pressbutton.prefab") as PressButton;
                button.SetParent(container, true, true);
                button.transform.localPosition = new Vector3(0.40f, 1.60f, -0.13f);
                button.transform.rotation = container.transform.rotation * Quaternion.Euler(new Vector3(10, 90, 180));
                button.Spawn();
                button.SendNetworkUpdateImmediate();
            }
        }

        public bool CanTransferAllResources(StorageContainer outputContainer)
        {
            foreach (var resource in _config.ResourceOutput)
            {
                if (!TryTransferResourceItem(outputContainer, resource.ItemShortName, resource.Amount))
                {
                    return false;
                }
            }
            return true;
        }

        public void HandleFuelConsumption(Item fuel, int requiredFuelAmount)
        {
            if (fuel.amount > requiredFuelAmount)
            {
                SafelyRemoveItem(fuel.SplitItem(requiredFuelAmount));
            }
            else
            {
                SafelyRemoveItem(fuel);
            }
        }

        public void TransferResources(StorageContainer outputContainer)
        {
            foreach (var resourceProperty in _config.ResourceOutput)
            {
                int resourceAmount = resourceProperty.Amount;
                TryTransferResourceItem(outputContainer, resourceProperty.ItemShortName, resourceAmount, true);
            }
        }

        public bool TryTransferResourceItem(StorageContainer container, string itemShortName, int quantity, bool doTransfer = false)
        {
            if (quantity <= 0)
                return false;

            var item = ItemManager.CreateByName(itemShortName, quantity, 0uL);
            if (item == null)
                return false;

            if (doTransfer)
                item.SetFlag(ExtractedItemFlag, true);

            bool successfulTransfer = AttemptItemTransfer(container, item, doTransfer);
            if (!successfulTransfer && doTransfer)
            {
                SafelyRemoveItem(item);
            }
            return successfulTransfer;
        }

        private bool AttemptItemTransfer(StorageContainer container, Item item, bool doTransfer)
        {
            for (int slotIndex = 0; slotIndex < container.inventory.capacity; slotIndex++)
            {
                var currentSlot = container.inventory.GetSlot(slotIndex);
                if (currentSlot != null && currentSlot.info == item.info && currentSlot.CanStack(item))
                {
                    if (TryStackItem(currentSlot, item, doTransfer, container))
                        return true;
                }
            }

            return AttemptToPlaceInEmptySlot(container, item, doTransfer);
        }

        private bool TryStackItem(Item slotItem, Item item, bool doTransfer, StorageContainer container)
        {
            int availableSpace = slotItem.MaxStackable() - slotItem.amount;
            if (availableSpace <= 0)
                return false;

            if (availableSpace >= item.amount)
            {
                if (doTransfer && item.MoveToContainer(container.inventory, slotItem.position))
                    item.SetFlag(ExtractedItemFlag, false);

                return true;
            }
            else
            {
                UpdateItemStack(item, slotItem, availableSpace);
                return false;
            }
        }

        private bool AttemptToPlaceInEmptySlot(StorageContainer container, Item item, bool doTransfer)
        {
            for (int slotIndex = 0; slotIndex < container.inventory.capacity; slotIndex++)
            {
                if (container.inventory.GetSlot(slotIndex) == null && container.inventory.canAcceptItem(item, slotIndex))
                {
                    if (doTransfer && item.MoveToContainer(container.inventory, slotIndex))
                    {
                        item.SetFlag(ExtractedItemFlag, false);
                        return true;
                    }
                    return true;
                }
            }
            return false;
        }

        private void UpdateItemStack(Item item, Item slotItem, int spaceToFill)
        {
            item.amount -= spaceToFill;
            slotItem.amount += spaceToFill;
            slotItem.MarkDirty();
        }
        #endregion

        #region Utility Functions
        private string GetLocalizedText(string key, string playerId = null, params object[] arguments)
        {
            return string.Format(lang.GetMessage(key, this, playerId), arguments);
        }

        private void NotifyPlayer(IPlayer player, string messageKey, params object[] arguments)
        {
            string message = GetLocalizedText(messageKey, player.Id, arguments);
            if (player.IsConnected)
                player.Message(message);
            else
                Puts(message);
        }

        public void SafelyRemoveItem(Item item)
        {
            item?.RemoveFromWorld();
            item?.RemoveFromContainer();
            item?.Remove();
        }
        #endregion

        #region MonoBehaviour
        private class QuarryComputerBehaviour : MonoBehaviour
        {
            private ExcavatorSignalComputer signalComputer;
            public StorageContainer outputContainer;
            public StorageContainer fuelContainer;

            private void Awake()
            {
                InitializeComputer();
                InitializeContainers();
                ScheduleRoutineTasks();
            }

            private void InitializeComputer()
            {
                signalComputer = GetComponent<ExcavatorSignalComputer>();
                signalComputer.SetFlag(BaseEntity.Flags.Reserved8, false);
                signalComputer.chargePower = 0f;
            }

            private void InitializeContainers()
            {
                foreach (var storageContainer in signalComputer.children.Cast<StorageContainer>())
                {
                    if (storageContainer.name == QuarryOutputStoragePrefab)
                        SetupOutputContainer(storageContainer);
                    else if (storageContainer.name == QuarryFuelStoragePrefab)
                        SetupFuelContainer(storageContainer);
                }
            }

            private void SetupOutputContainer(StorageContainer container)
            {
                outputContainer = container;
                container.inventory.capacity = 6;
            }

            private void SetupFuelContainer(StorageContainer container)
            {
                fuelContainer = container;
                container.inventory.capacity = 3;
            }

            private void ScheduleRoutineTasks()
            {
                InvokeRepeating(nameof(CheckForGroundIntegrity), 5f, 5f);
                InvokeRepeating(nameof(ExtractResources), 0f, _instance._config.ResourceExtractionIntervalSeconds);
            }

            private void ExtractResources()
            {
                if (fuelContainer == null || outputContainer == null)
                    return;

                var fuelItem = fuelContainer.inventory.itemList.FirstOrDefault(x => x.info.shortname == "lowgradefuel");
                int requiredFuel = _instance._config.FuelPerExtraction;

                if (fuelItem == null || fuelItem.amount < requiredFuel)
                {
                    SetSignalComputerState(false);
                    return;
                }

                if (_instance.CanTransferAllResources(outputContainer))
                {
                    _instance.HandleFuelConsumption(fuelItem, requiredFuel);
                    _instance.TransferResources(outputContainer);
                    SetSignalComputerState(true);
                }
                else
                {
                    SetSignalComputerState(false);
                }
            }

            private void SetSignalComputerState(bool isActive)
            {
                signalComputer.SetFlag(BaseEntity.Flags.Reserved8, isActive);
                signalComputer.chargePower = isActive ? 1 : 0;
            }

            private void CheckForGroundIntegrity()
            {
                if (signalComputer.HasParent() && signalComputer.GetParentEntity() is Tugboat)
                {
                    CancelInvoke(nameof(CheckForGroundIntegrity));
                    return;
                }

                Vector3 originPoint = signalComputer.transform.position + new Vector3(0, 0.1f, 0);
                if (!Physics.Raycast(originPoint, Vector3.down, out var hitInfo, 4f, LayerMask.GetMask("Terrain", "Construction")) || hitInfo.distance > 0.2f)
                {
                    HandleMissingGround();
                }
            }

            private void HandleMissingGround()
            {
                CancelInvoke(nameof(ExtractResources));
                CancelInvoke(nameof(CheckForGroundIntegrity));
                signalComputer.Kill();
                _instance.CreateQuarryItem()?.Drop(signalComputer.transform.position, Vector3.zero);
                Effect.server.Run("assets/prefabs/deployable/quarry/effects/mining-quarry-deploy.prefab", signalComputer.transform.position);
            }

            private void OnDestroy()
            {
                CancelInvoke(nameof(ExtractResources));
                CancelInvoke(nameof(CheckForGroundIntegrity));
            }

            public void Unload()
            {
                signalComputer.SetFlag(BaseEntity.Flags.Reserved8, false);
                signalComputer.chargePower = 0f;
            }
        }
        #endregion
    }
}