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

FREE BuildingWorkbench : 1.3.2 1.3.2

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

namespace Oxide.Plugins
{
    [Info("Building Workbench", "MJSU", "1.3.2")]
    [Description("Extends the range of the workbench to work inside the entire building")]
    public class BuildingWorkbench : RustPlugin
    {
        #region Class Fields
        [PluginReference] private readonly Plugin GameTipAPI;

        private PluginConfig _pluginConfig; //Plugin Config

        private WorkbenchBehavior _wb;
        private GameObject _go;
        private BuildingWorkbenchTrigger _tb;

        private const string UsePermission = "buildingworkbench.use";
        private const string CancelCraftPermission = "buildingworkbench.cancelcraft";
        private const string AccentColor = "#de8732";

        private readonly List<ulong> _notifiedPlayer = new List<ulong>();
        private readonly Hash<ulong, PlayerData> _playerData = new Hash<ulong, PlayerData>();
        private readonly Hash<uint, BuildingData> _buildingData = new Hash<uint, BuildingData>();
        
        //private static BuildingWorkbench _ins;
        #endregion

        #region Setup & Loading
        private void Init()
        {
            //_ins = this;
            permission.RegisterPermission(UsePermission, this);
            permission.RegisterPermission(CancelCraftPermission, this);
            
            Unsubscribe(nameof(OnEntitySpawned));
            Unsubscribe(nameof(OnEntityKill));
        }
        
        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                [LangKeys.Chat] = $"<color=#bebebe>[<color={AccentColor}>{Title}</color>] {{0}}</color>",
                [LangKeys.Notification] = "Your workbench range has been increased to work inside your building",
                [LangKeys.CraftCanceled] = "Your workbench level has changed.Crafts that required a higher level have been cancelled."
            }, this);
        }
        
        protected override void LoadDefaultConfig()
        {
            PrintWarning("Loading Default Config");
        }

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

        private void OnServerInitialized()
        {
            if (_pluginConfig.BaseDistance < 3f)
            {
                PrintWarning("Distance from base to be considered inside building (Meters) cannot be less than 3 meters");
                _pluginConfig.BaseDistance = 3f;
            }
            
            _go = new GameObject("BuildingWorkbenchObject");
            _wb = _go.AddComponent<WorkbenchBehavior>();
            _tb = _go.AddComponent<BuildingWorkbenchTrigger>();

            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                OnPlayerConnected(player);
            }
            
            _wb.InvokeRepeating(StartUpdatingWorkbench, 1f, _pluginConfig.UpdateRate);
             
            Subscribe(nameof(OnEntitySpawned));
            Subscribe(nameof(OnEntityKill));
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            player.nextCheckTime = float.MaxValue;
            player.EnterTrigger(_tb);
        }
        
        private void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            player.nextCheckTime = 0;
            player.cachedCraftLevel = 0;
            Hash<uint, BuildingData> playerData = _playerData[player.userID]?.BuildingData;
            if (playerData != null)
            {
                foreach (BuildingData data in playerData.Values)
                {
                    data.LeaveBuilding(player);
                }
            }

            _playerData.Remove(player.userID);
            player.LeaveTrigger(_tb);
        }

        private void Unload()
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                OnPlayerDisconnected(player, null);
            }

            if (!_wb.IsUnityNull())
            {
                _wb.CancelInvoke(StartUpdatingWorkbench);
                _wb.StopAllCoroutines();
            }

            GameObject.Destroy(_go);
            //_ins = null;
        }
        #endregion

        #region Workbench Handler
        public void StartUpdatingWorkbench()
        {
            if (BasePlayer.activePlayerList.Count == 0)
            {
                return;
            }
            
            _wb.StartCoroutine(HandleWorkbenchUpdate());
        }

        public IEnumerator HandleWorkbenchUpdate()
        {
            float frameWait = 0;
            for (int i = 0; i < BasePlayer.activePlayerList.Count; i++)
            {
                BasePlayer player = BasePlayer.activePlayerList[i];

                if (!HasPermission(player, UsePermission))
                {
                    if (player.nextCheckTime == float.MaxValue)
                    {
                        player.nextCheckTime = 0;
                        player.cachedCraftLevel = 0;
                    }
                    
                    continue;
                }

                PlayerData data = GetPlayerData(player.userID);
                if (Vector3.Distance(player.transform.position, data.Position) < _pluginConfig.RequiredDistance)
                {
                    continue;
                }

                if (player.FindTrigger<BuildingWorkbenchTrigger>().IsUnityNull())
                {
                    player.EnterTrigger(_tb);
                }

                data.Position = player.transform.position;
                
                UpdatePlayerPriv(player, data);

                float waitForFrames = Performance.report.frameRate * _pluginConfig.UpdateRate / BasePlayer.activePlayerList.Count * 0.9f;
                if (waitForFrames < 1)
                {
                    frameWait += waitForFrames;
                    if (frameWait >= 1)
                    {
                        frameWait = 0;
                        yield return null;
                    }
                    
                    continue;
                }

                yield return null;
            }
        }

        public void UpdatePlayerPriv(BasePlayer player, PlayerData data)
        {
            List<uint> currentBuildings = Pool.GetList<uint>();
            GetNearbyAuthorizedBuildings(player, currentBuildings);

            List<uint> leftBuildings = Pool.GetList<uint>();
            foreach (uint buildingId in data.BuildingData.Keys)
            {
                if (!currentBuildings.Contains(buildingId))
                {
                    leftBuildings.Add(buildingId);
                }
            }

            foreach (uint leftBuilding in leftBuildings)
            {
                OnPlayerLeftBuilding(player, leftBuilding);
            }
            
            foreach (uint currentBuilding in currentBuildings)
            {
                if (!data.BuildingData.ContainsKey(currentBuilding))
                {
                    OnPlayerEnterBuilding(player, currentBuilding);
                }
            }
            
            UpdatePlayerWorkbenchLevel(player);
            
            //Puts($"{nameof(BuildingData)}.{nameof(UpdatePlayerPriv)} {player.displayName} In: {string.Join(",", currentBuildings.Select(b => b.ToString().ToArray()))} Left: {string.Join(",", leftBuildings.Select(b => b.ToString().ToArray()))}");
            
            Pool.FreeList(ref currentBuildings);
            Pool.FreeList(ref leftBuildings);
        }

        public void OnPlayerEnterBuilding(BasePlayer player, uint buildingId)
        {
            BuildingData building = GetBuildingData(buildingId);
            building.EnterBuilding(player);
            Hash<uint, BuildingData> playerBuildings = GetPlayerData(player.userID).BuildingData;
            playerBuildings[buildingId] = building;
        }

        public void OnPlayerLeftBuilding(BasePlayer player, uint buildingId)
        {
            BuildingData building = GetBuildingData(buildingId);
            building.LeaveBuilding(player);
            Hash<uint, BuildingData> playerBuildings = GetPlayerData(player.userID).BuildingData;
            if (!playerBuildings.Remove(buildingId))
            {
                return;
            }

            if (player.inventory.crafting.queue.Count != 0 && HasPermission(player, CancelCraftPermission))
            {
                bool canceled = false;
                foreach (ItemCraftTask task in player.inventory.crafting.queue)
                {
                    if (player.cachedCraftLevel < task.blueprint.workbenchLevelRequired)
                    {
                        player.inventory.crafting.CancelTask(task.taskUID, true);
                        canceled = true;
                    }
                }
                
                if (canceled && _pluginConfig.CancelCraftNotification)
                {
                    Chat(player, Lang(LangKeys.CraftCanceled, player));
                }
            }
        }
        #endregion

        #region Oxide Hooks
        private void OnEntitySpawned(Workbench bench)
        {
            //Needs to be in NextTick since other plugins can spawn Workbenches
            NextTick(() =>
            {
                BuildingData building = GetBuildingData(bench.buildingID);
                building.OnBenchBuilt(bench);
                UpdateBuildingPlayers(building);
            
                if (!_pluginConfig.BuiltNotification)
                {
                    return;
                }
            
                BasePlayer player = BasePlayer.FindByID(bench.OwnerID);
                if (player.IsUnityNull())
                {
                    return;
                }
            
                if (_notifiedPlayer.Contains(player.userID))
                {
                    return;
                }
            
                _notifiedPlayer.Add(player.userID);
            
                if (GameTipAPI == null)
                {
                    Chat(player, Lang(LangKeys.Notification, player));
                }
                else
                {
                    GameTipAPI.Call("ShowGameTip", player, Lang(LangKeys.Notification, player), 6f);
                }
            });
        }

        private void OnEntityKill(Workbench bench)
        {
            BuildingData building = GetBuildingData(bench.buildingID);
            building.OnBenchKilled(bench);
            UpdateBuildingPlayers(building);
        }
        
        private void OnEntityKill(BuildingPrivlidge tc)
        {
            OnCupboardClearList(tc);
        }
        
        private void OnCupboardAuthorize(BuildingPrivlidge privilege, BasePlayer player)
        {
            OnPlayerEnterBuilding(player, privilege.buildingID);
            UpdatePlayerWorkbenchLevel(player);
        }
        
        private void OnCupboardDeauthorize(BuildingPrivlidge privilege, BasePlayer player)
        {
            OnPlayerLeftBuilding(player, privilege.buildingID);
            UpdatePlayerWorkbenchLevel(player);
        }

        private void OnCupboardClearList(BuildingPrivlidge privilege)
        {
            BuildingData data = GetBuildingData(privilege.buildingID);
            for (int index = data.Players.Count - 1; index >= 0; index--)
            {
                BasePlayer player = data.Players[index];
                OnPlayerLeftBuilding(player, privilege.buildingID);
                UpdatePlayerWorkbenchLevel(player);
            }
        }
        
        private void OnEntityEnter(TriggerWorkbench trigger, BasePlayer player)
        {
            if (player.IsNpc)
            {
                return;
            }
            
            UpdatePlayerWorkbenchLevel(player);
        }
        
        private void OnEntityLeave(TriggerWorkbench trigger, BasePlayer player)
        {
            if (player.IsNpc)
            {
                return;
            }
            
            UpdatePlayerWorkbenchLevel(player);
        }
        
        private void OnEntityLeave(BuildingWorkbenchTrigger trigger, BasePlayer player)
        {
            if (player.IsNpc)
            {
                return;
            }
            
            //_ins.Puts($"{nameof(BuildingWorkbench)}.{nameof(OnEntityLeave)} {nameof(BuildingWorkbenchTrigger)} {player.displayName}");
            
            NextTick(() =>
            {
                player.EnterTrigger(_tb);
            });
        }
        #endregion

        #region Helper Methods
        public void UpdateBuildingPlayers(BuildingData building)
        {
            foreach (BasePlayer player in building.Players)
            {
                UpdatePlayerWorkbenchLevel(player);
            }
        }
        
        public void UpdatePlayerWorkbenchLevel(BasePlayer player)
        {
            float level = 0;
            Hash<uint, BuildingData> playerBuildings = _playerData[player.userID]?.BuildingData;
            if (playerBuildings != null)
            {
                foreach (BuildingData building in playerBuildings.Values)
                {
                    level = Mathf.Max(level, building.GetBuildingLevel());
                }
            }
            
            if (level != 3 && player.triggers != null)
            {
                for (int index = 0; index < player.triggers.Count; index++)
                {
                    TriggerWorkbench trigger = player.triggers[index] as TriggerWorkbench;
                    if (trigger != null)
                    {
                        level = Mathf.Max(level, trigger.parentBench.Workbenchlevel);
                    }
                }
            }

            if (player.cachedCraftLevel == level)
            {
                return;
            }

            //_ins.Puts($"{nameof(BuildingWorkbench)}.{nameof(UpdatePlayerWorkbenchLevel)} {player.displayName} -> {level}");
            player.nextCheckTime = float.MaxValue;
            player.cachedCraftLevel = level;
            player.SetPlayerFlag(BasePlayer.PlayerFlags.Workbench1, player.cachedCraftLevel == 1f);
            player.SetPlayerFlag(BasePlayer.PlayerFlags.Workbench2, player.cachedCraftLevel == 2f);
            player.SetPlayerFlag(BasePlayer.PlayerFlags.Workbench3, player.cachedCraftLevel == 3f);
            player.SendNetworkUpdateImmediate();
        }
        
        public PlayerData GetPlayerData(ulong playerId)
        {
            PlayerData data = _playerData[playerId];
            if (data == null)
            {
                data = new PlayerData();
                _playerData[playerId] = data;
            }

            return data;
        }
        
        public BuildingData GetBuildingData(uint buildingId)
        {
            BuildingData data = _buildingData[buildingId];
            if (data == null)
            {
                data = new BuildingData(buildingId);
                _buildingData[buildingId] = data;
            }

            return data;
        }

        public void GetNearbyAuthorizedBuildings(BasePlayer player, List<uint> authorizedPrivs)
        {
            List<uint> processedBuildings = Pool.GetList<uint>();
            OBB obb = player.WorldSpaceBounds();
            List<BuildingBlock> blocks = Pool.GetList<BuildingBlock>();
            float baseDistance = _pluginConfig.BaseDistance;
            Vis.Entities(obb.position, baseDistance + obb.extents.magnitude, blocks, Rust.Layers.Construction);
            for (int index = 0; index < blocks.Count; index++)
            {
                BuildingBlock block = blocks[index];
                if (processedBuildings.Contains(block.buildingID) || !(obb.Distance(block.WorldSpaceBounds()) <= baseDistance))
                {
                    continue;
                }
                
                processedBuildings.Add(block.buildingID);
                BuildingPrivlidge priv = block.GetBuilding()?.GetDominatingBuildingPrivilege();
                if (priv.IsUnityNull() || !priv.IsAuthed(player))
                {
                    continue;
                }
                
                authorizedPrivs.Add(priv.buildingID);
            }
            
            Pool.FreeList(ref blocks);
            Pool.FreeList(ref processedBuildings);
        }

        public void Chat(BasePlayer player, string format, params object[] args) => PrintToChat(player, Lang(LangKeys.Chat, player, format), args);
        
        public bool HasPermission(BasePlayer player, string perm) => permission.UserHasPermission(player.UserIDString, perm);
        
        public string Lang(string key, BasePlayer player = null, params object[] args) => string.Format(lang.GetMessage(key, this, player?.UserIDString), args);
        #endregion

        #region Building Data
        public class BuildingData
        {
            public uint BuildingId { get; }
            public Workbench BestWorkbench { get; set; }
            public List<BasePlayer> Players { get; } = new List<BasePlayer>();
            public List<Workbench> Workbenches { get; } = new List<Workbench>();

            public BuildingData(uint buildingId)
            {
                BuildingId = buildingId;
                Workbenches = BuildingManager.server.GetBuilding(buildingId)?.decayEntities.OfType<Workbench>().ToList() ?? new List<Workbench>();
                UpdateBestBench();
            }

            public void EnterBuilding(BasePlayer player)
            {
                //_ins.Puts($"{nameof(BuildingData)}.{nameof(EnterBuilding)} {player.displayName}");
                Players.Add(player);
            }

            public void LeaveBuilding(BasePlayer player)
            {
                //_ins.Puts($"{nameof(BuildingData)}.{nameof(LeaveBuilding)} {player.displayName}");
                Players.Remove(player);
            }

            public void OnBenchBuilt(Workbench workbench)
            {
                Workbenches.Add(workbench);
                if (BestWorkbench != null && workbench.Workbenchlevel < BestWorkbench.Workbenchlevel)
                {
                    return;
                }

                BestWorkbench = workbench;
            }

            public void OnBenchKilled(Workbench workbench)
            {
                Workbenches.Remove(workbench);
                UpdateBestBench();
            }
            
            public float GetBuildingLevel()
            {
                if (BestWorkbench.IsUnityNull())
                {
                    return 0f;
                }

                return BestWorkbench.Workbenchlevel;
            }

            private void UpdateBestBench()
            {
                BestWorkbench = null;
                for (int index = 0; index < Workbenches.Count; index++)
                {
                    Workbench workbench = Workbenches[index];
                    if (BestWorkbench.IsUnityNull() || BestWorkbench.Workbenchlevel < workbench.Workbenchlevel)
                    {
                        BestWorkbench = workbench;
                    }
                }
            }
        }
        #endregion

        #region Classes
        private class PluginConfig
        {
            [DefaultValue(true)]
            [JsonProperty(PropertyName = "Display workbench built notification")]
            public bool BuiltNotification { get; set; }

            [DefaultValue(true)]
            [JsonProperty(PropertyName = "Display cancel craft notification")]
            public bool CancelCraftNotification { get; set; }
            
            [DefaultValue(3f)]
            [JsonProperty(PropertyName = "Inside building check frequency (Seconds)")]
            public float UpdateRate { get; set; }
            
            [DefaultValue(16f)]
            [JsonProperty(PropertyName = "Distance from base to be considered inside building (Meters)")]
            public float BaseDistance { get; set; }
            
            [DefaultValue(5)]
            [JsonProperty(PropertyName = "Required distance from last update (Meters)")]
            public float RequiredDistance { get; set; }
        }

        public class PlayerData
        {
            public Vector3 Position { get; set; }
            public Hash<uint, BuildingData> BuildingData { get; } = new Hash<uint, BuildingData>();
        }

        private class LangKeys
        {
            public const string Chat = nameof(Chat);
            public const string Notification = nameof(Notification);
            public const string CraftCanceled = nameof(CraftCanceled) + "V1";
        }

        public class WorkbenchBehavior : FacepunchBehaviour
        {
            
        }

        public class BuildingWorkbenchTrigger : TriggerBase
        {
            
        }
        #endregion
    }
}

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