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

FREE BaseRepair : 1.0.24 1.0.24

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using ProtoBuf;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Base Repair", "MJSU", "1.0.24")]
    [Description("Allows player to repair their entire base")]
    internal class BaseRepair : RustPlugin
    {
        #region Class Fields

        [PluginReference] private Plugin NoEscape;

        private StoredData _storedData; //Plugin Data
        private PluginConfig _pluginConfig; //Plugin Config

        private const string UsePermission = "baserepair.use";
        private const string NoCostPermission = "baserepair.nocost";
        private const string NoAuthPermission = "baserepair.noauth";
        private const string AccentColor = "#de8732";

        private readonly List<ulong> _repairingPlayers = new List<ulong>();
        private readonly ItemAmountPool _itemAmountPool = new ItemAmountPool();
        private readonly StringBuilder _sb = new StringBuilder();

        private GameObject _go;
        private RepairBehavior _rb;

        private readonly object _true = true;
        #endregion

        #region Setup & Loading
        private void Init()
        {
            _storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
            permission.RegisterPermission(UsePermission, this);
            permission.RegisterPermission(NoCostPermission, this);
            permission.RegisterPermission(NoAuthPermission, this);
            foreach (string command in _pluginConfig.ChatCommands)
            {
                cmd.AddChatCommand(command, this, BaseRepairChatCommand);
            }

            UnsubscribeAll();
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                [LangKeys.Chat] = $"<color=#bebebe>[<color={AccentColor}>{Title}</color>] {{0}}</color>",
                [LangKeys.NoPermission] = "You do not have permission to use this command",
                [LangKeys.RepairInProcess] = "You have a current repair in progress.Please wait for that to finish before repairing again",
                [LangKeys.RecentlyDamaged] = "We failed to repair {0} because they were recently damaged",
                [LangKeys.AmountRepaired] = "We have repaired {0} damaged items in this base.",
                [LangKeys.Enabled] = "You enabled enabled building repair.Hit the building you wish to repair with the hammer and we will do the rest for you.",
                [LangKeys.Disabled] = "You have disabled building repair."
            }, this);
        }

        protected override void LoadDefaultConfig()
        {
            PrintWarning("Loading Default Config");
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate;
            _pluginConfig = AdditionalConfig(Config.ReadObject<PluginConfig>());
            Config.WriteObject(_pluginConfig);
        }

        private PluginConfig AdditionalConfig(PluginConfig config)
        {
            config.ChatCommands = config.ChatCommands ?? new List<string>
            {
                "br"
            };
            return config;
        }

        private void OnServerInitialized()
        {
            _go = new GameObject(Name);
            _rb = _go.AddComponent<RepairBehavior>();
            
            SubscribeAll();
        }

        private void Unload()
        {
            if (_rb)
            {
                _rb.StopAllCoroutines();
                _rb.DoDestroy();
            }
           
            GameObject.Destroy(_go);
        }
        #endregion

        #region Chat Command
        private void BaseRepairChatCommand(BasePlayer player, string cmd, string[] args)
        {
            if (!player.IsAdmin && !HasPermission(player, UsePermission))
            {
                Chat(player, Lang(LangKeys.NoPermission, player));
                return;
            }

            bool enabled = !_storedData.RepairEnabled[player.userID];
            _storedData.RepairEnabled[player.userID] = enabled;

            Chat(player, enabled ? Lang(LangKeys.Enabled, player) : Lang(LangKeys.Disabled, player));
            SaveData();
        }
        #endregion

        #region Oxide Hooks
        private object OnHammerHit(BasePlayer player, HitInfo info)
        {
            BaseCombatEntity entity = info?.HitEntity as BaseCombatEntity;
            if (entity == null || entity.IsDestroyed)
            {
                return null;
            }
            
            if (entity is BaseVehicle)
            {
                return null;
            }
            
            if (!HasPermission(player, UsePermission))
            {
                return null;
            }
            
            if (_storedData.RepairEnabled.ContainsKey(player.userID))
            {
                if (!_storedData.RepairEnabled[player.userID])
                {
                    return null;
                }
            }
            else if (!_pluginConfig.DefaultEnabled)
            {
                return null;
            }

            string error = NoEscape?.Call<string>("CanDo", "repair", player);
            if (!string.IsNullOrEmpty(error))
            {
                Chat(player, error);
                return null;
            }

            if (_repairingPlayers.Contains(player.userID))
            {
                Chat(player, Lang(LangKeys.RepairInProcess, player));
                return _true;
            }

            bool hasNoAuth = HasPermission(player, NoAuthPermission);
            
            BuildingPrivlidge priv = player.GetBuildingPrivilege();
            if (priv && !hasNoAuth && !priv.IsAuthed(player))
            {
                return null;
            }
            
            BuildingManager.Building building = null;
            if (entity is DecayEntity)
            {
                building = ((DecayEntity)entity).GetBuilding();
            }
            
            if (building == null)
            {
                if (!priv)
                {
                    return null;
                }
                
                building = priv.GetBuilding();
                if (building == null)
                {
                    return null;
                }
            }
            
            priv = building.GetDominatingBuildingPrivilege();
            if (!priv && !_pluginConfig.AllowNoTcRepair)
            {
                return null;
            }
            
            if (priv && !hasNoAuth && !priv.IsAuthed(player))
            {
                return null;
            }

            PlayerRepairStats stats = new PlayerRepairStats();

            if (Interface.CallHook("OnBaseRepair", building, player) != null)
            {
                return null;
            }
            
            _rb.StartCoroutine(DoBuildingRepair(player, building, stats));
            return _true;
        }
        #endregion

        #region Repair Handler

        private IEnumerator DoBuildingRepair(BasePlayer player, BuildingManager.Building building, PlayerRepairStats stats)
        {
            _repairingPlayers.Add(player.userID);
            bool noCostPerm = HasPermission(player, NoCostPermission);
            
            for (int index = 0; index < building.decayEntities.Count; index++)
            {
                DecayEntity entity = building.decayEntities[index];
                DoRepair(player, entity, stats, noCostPerm);

                for (int i = 0; i < entity.children.Count; i++)
                {
                    BaseEntity childEntity = entity.children[i];
                    if (childEntity is BaseLadder)
                    {
                        DoRepair(player, (BaseLadder)childEntity, stats, noCostPerm);
                    }
                }

                if (index % _pluginConfig.RepairsPerFrame == 0)
                {
                    yield return null;
                }
            }

            _sb.Clear();
            _sb.AppendLine(Lang(LangKeys.AmountRepaired, player, stats.TotalSuccess));

            if (stats.RecentlyDamaged > 0)
            {
                _sb.AppendLine(Lang(LangKeys.RecentlyDamaged, player, stats.RecentlyDamaged));
            }

            Chat(player, _sb.ToString());

            if (stats.TotalCantAfford > 0)
            {
                List<ItemAmount> missingAmounts = Pool.GetList<ItemAmount>();
                foreach (KeyValuePair<int, ItemAmount> missing in stats.MissingAmounts)
                {
                    float amountMissing = missing.Value.amount - player.inventory.GetAmount(missing.Key);
                    if (amountMissing <= 0)
                    {
                        ItemAmount amount = missing.Value;
                        _itemAmountPool.Free(ref amount);
                        continue;
                    }

                    missingAmounts.Add(missing.Value);
                }
                
                SendMissingItemAmounts(player, missingAmounts);
                FreeItemAmounts(missingAmounts);
            }

            foreach (KeyValuePair<int, int> taken in stats.AmountTaken)
            {
                player.Command("note.inv", taken.Key, -taken.Value);
            }

            _repairingPlayers.Remove(player.userID);
        }

        private void DoRepair(BasePlayer player, BaseCombatEntity entity, PlayerRepairStats stats, bool noCost)
        {
            if (!entity.IsValid() || entity.IsDestroyed)
            {
                return;
            }

            if (!entity.repair.enabled || entity.health == entity.MaxHealth())
            {
                return;
            }

            if (Interface.CallHook("OnStructureRepair", entity, player) != null)
            {
                return;
            }

            if (entity.SecondsSinceAttacked <= _pluginConfig.EntityRepairDelay)
            {
                entity.OnRepairFailed(null, string.Empty);
                stats.RecentlyDamaged++;
                return;
            }

            float missingHealth = entity.MaxHealth() - entity.health;
            float healthPercentage = missingHealth / entity.MaxHealth();
            if (missingHealth <= 0f || healthPercentage <= 0f)
            {
                entity.OnRepairFailed(null, string.Empty);
                return;
            }

            if (!noCost)
            {
                List<ItemAmount> itemAmounts = Pool.GetList<ItemAmount>();
                GetEntityRepairCost(entity, itemAmounts, healthPercentage);
                if (!HasRepairCost(itemAmounts))
                {
                    entity.health += missingHealth;
                    entity.SendNetworkUpdate();
                    entity.OnRepairFinished();
                    FreeItemAmounts(itemAmounts);
                    return;
                }

                if (Math.Abs(_pluginConfig.RepairCostMultiplier - 1f) > 0.001f)
                {
                    foreach (ItemAmount amount in itemAmounts)
                    {
                        amount.amount *= _pluginConfig.RepairCostMultiplier;
                    }
                }

                if (!CanAffordRepair(player, itemAmounts))
                {
                    entity.OnRepairFailed(null, string.Empty);
                    
                    foreach (ItemAmount amount in itemAmounts)
                    {
                        ItemAmount missing = stats.MissingAmounts[amount.itemid];
                        if (missing == null)
                        {
                            missing = _itemAmountPool.Get();
                            missing.itemDef = amount.itemDef;
                            missing.amount = amount.amount;
                            stats.MissingAmounts[amount.itemid] = missing;
                            continue;
                        }

                        missing.amount += amount.amount;
                    }

                    stats.TotalCantAfford++;
                    FreeItemAmounts(itemAmounts);
                    return;
                }

                List<Item> items = Pool.GetList<Item>();
                foreach (ItemAmount amount in itemAmounts)
                {
                    player.inventory.Take(items, amount.itemid, (int) amount.amount);
                    stats.AmountTaken[amount.itemid] += (int) amount.amount;
                }

                for (int index = 0; index < items.Count; index++)
                {
                    Item item = items[index];
                    item.Remove();
                }

                Pool.FreeList(ref items);
                FreeItemAmounts(itemAmounts);
            }

            entity.health += missingHealth;
            entity.SendNetworkUpdate();

            if (entity.health < entity.MaxHealth())
            {
                entity.OnRepair();
            }
            else
            {
                entity.OnRepairFinished();
            }

            stats.TotalSuccess++;
        }

        public bool CanAffordRepair(BasePlayer player, List<ItemAmount> amounts)
        {
            for (int index = 0; index < amounts.Count; index++)
            {
                ItemAmount amount = amounts[index];
                if (player.inventory.GetAmount(amount.itemid) < amount.amount)
                {
                    return false;
                }
            }

            return true;
        }

        public void GetEntityRepairCost(BaseCombatEntity entity, List<ItemAmount> repairAmounts, float missingHealthFraction)
        {
            List<ItemAmount> entityAmount = entity.BuildCost();
            if (entityAmount == null)
            {
                return;
            }
            
            float repairCostFraction = entity.RepairCostFraction();
            for (int index = 0; index < entityAmount.Count; index++)
            {
                ItemAmount itemAmount = entityAmount[index];
                int amount = Mathf.RoundToInt(itemAmount.amount * repairCostFraction * missingHealthFraction);
                if (amount > 0)
                {
                    ItemAmount repairAmount = _itemAmountPool.Get();
                    repairAmount.itemDef = itemAmount.itemDef;
                    repairAmount.amount = amount;
                    repairAmounts.Add(repairAmount);
                }
            }
        }

        public bool HasRepairCost(List<ItemAmount> amounts)
        {
            for (int index = 0; index < amounts.Count; index++)
            {
                ItemAmount amount = amounts[index];
                if (amount.amount >= 1)
                {
                    return true;
                }
            }

            return false;
        }

        public void FreeItemAmounts(List<ItemAmount> amounts)
        {
            for (int index = 0; index < amounts.Count; index++)
            {
                ItemAmount amount = amounts[index];
                _itemAmountPool.Free(ref amount);
            }
            
            Pool.FreeList(ref amounts);
        }
        #endregion

        #region Helper Methods
        public void SendMissingItemAmounts(BasePlayer player, List<ItemAmount> itemAmounts)
        {
            using (ItemAmountList itemAmountList = ItemAmount.SerialiseList(itemAmounts))
            {
                player.ClientRPCPlayer(null, player, "Client_OnRepairFailedResources", itemAmountList);
            }
        }
        
        public void SubscribeAll()
        {
            Subscribe(nameof(OnHammerHit));
        }
        
        public void UnsubscribeAll()
        {
            Unsubscribe(nameof(OnHammerHit));
        }

        private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, _storedData);

        private void Chat(BasePlayer player, string format) => PrintToChat(player, Lang(LangKeys.Chat, player, format));

        private bool HasPermission(BasePlayer player, string perm) => permission.UserHasPermission(player.UserIDString, perm);

        private string Lang(string key, BasePlayer player = null)
        {
            return lang.GetMessage(key, this, player?.UserIDString);
        }
        
        private string Lang(string key, BasePlayer player = null, params object[] args)
        {
            try
            {
                return string.Format(lang.GetMessage(key, this, player?.UserIDString), args);
            }
            catch (Exception ex)
            {
                PrintError($"Lang Key '{key}' threw exception\n:{ex}");
                throw;
            }
        }

        #endregion

        #region Behavior
        private class RepairBehavior : FacepunchBehaviour
        {
            private void Awake()
            {
                enabled = false;
            }

            public void DoDestroy()
            {
                Destroy(this);
            }
        }
        #endregion
        
        #region Classes

        private class PluginConfig
        {
            [DefaultValue(10)]
            [JsonProperty(PropertyName = "Number of entities to repair per server frame")]
            public int RepairsPerFrame { get; set; }

            [DefaultValue(false)]
            [JsonProperty(PropertyName = "Default Enabled")]
            public bool DefaultEnabled { get; set; }
            
            [DefaultValue(false)]
            [JsonProperty(PropertyName = "Allow Repairing Bases Without A Tool Cupboard")]
            public bool AllowNoTcRepair { get; set; }

            [DefaultValue(1f)]
            [JsonProperty(PropertyName = "Repair Cost Multiplier")]
            public float RepairCostMultiplier { get; set; }
            
            [DefaultValue(30f)]
            [JsonProperty(PropertyName = "How long after an entity is damaged before it can be repaired (Seconds)")]
            public float EntityRepairDelay { get; set; }

            [JsonProperty(PropertyName = "Chat Commands")]
            public List<string> ChatCommands { get; set; }
        }

        private class StoredData
        {
            public Hash<ulong, bool> RepairEnabled = new Hash<ulong, bool>();
        }

        private class PlayerRepairStats
        {
            public int TotalSuccess { get; set; }
            public int TotalCantAfford { get; set; }
            public int RecentlyDamaged { get; set; }
            public Hash<int, ItemAmount> MissingAmounts { get; } = new Hash<int, ItemAmount>();
            public Hash<int, int> AmountTaken { get; } = new Hash<int, int>();
        }

        private class LangKeys
        {
            public const string Chat = "Chat";
            public const string NoPermission = "NoPermission";
            public const string RepairInProcess = "RepairInProcess";
            public const string RecentlyDamaged = "RecentlyDamaged";
            public const string AmountRepaired = "AmountRepaired";
            public const string Enabled = "Enabled";
            public const string Disabled = "Disabled";
        }

        #endregion

        #region Pool
        private class BasePool<T> where T : class
        {
            protected readonly List<T> Pool = new List<T>();
            protected readonly Func<T> Init;

            public BasePool(Func<T> init)
            {
                Init = init;
            }
            
            public virtual T Get()
            {
                if (Pool.Count == 0)
                {
                    return Init.Invoke();
                }

                int index = Pool.Count - 1; //Removing the last element prevents an array copy.T entity = Pool[index];
                Pool.RemoveAt(index);
                
                return entity;
            }

            public virtual void Free(ref T entity)
            {
                Pool.Add(entity);
                entity = null;
            }
        }
        
        private class ItemAmountPool : BasePool<ItemAmount>
        {
            public ItemAmountPool() : base(() => new ItemAmount())
            {
            }
            
            public override void Free(ref ItemAmount ia)
            {
                ia.itemDef = null;
                ia.amount = 0;
                ia.startAmount = 0;
                base.Free(ref ia);
            }
        }
        #endregion
    }
}

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