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

FREE CraftSystem : 1.0.4 1.0.4

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

namespace Oxide.Plugins
{	
    [Info("CraftSystem", "Nimant", "1.0.4")]
    class CraftSystem : RustPlugin
    {        
		
		#region Variables
										
		private const string InstaPerm = "craftsystem.instacraft";
		private static CraftSystem Ins = null;
		private static System.Random Rnd = new System.Random();
        private static Dictionary<string, float> SaveBlueprints = new Dictionary<string, float>();                        						      						
		private static HashSet<int> NoRealCancel = new HashSet<int>();
		private static Dictionary<ulong, TemporaryContainer> TmpConts = new Dictionary<ulong, TemporaryContainer>();
		private static Dictionary<ulong, string> PlayerLastCancelTask = new Dictionary<ulong, string>();

		private class TemporaryContainer
		{
			private string userName;
			private ulong userID;
			private Vector3 userPos;
			private Timer userTimer;
			private ItemContainer cont;
			
			public static TemporaryContainer Init(BasePlayer player)
			{
				if (!TmpConts.ContainsKey(player.userID))				
					TmpConts.Add(player.userID, new TemporaryContainer());								
				
				TmpConts[player.userID].userName = player.displayName;
				TmpConts[player.userID].userID	= player.userID;
				TmpConts[player.userID].userPos = player.transform.position;
				
				if (TmpConts[player.userID].userTimer != null)
				{
					TmpConts[player.userID].userTimer.Destroy();
					TmpConts[player.userID].userTimer = null;
				}
				
				var userID = player.userID;
				
				TmpConts[player.userID].userTimer = Ins.timer.Once(0.25f, ()=>
				{
					if (TmpConts.ContainsKey(userID))
					{
						if (TmpConts[userID].cont != null) 
							TmpConts[userID].DropBackpack();
						
						TmpConts[userID].userTimer = null;
						TmpConts[userID].cont = null;
					}
				});
				
				return TmpConts[player.userID];
			}
			
			private void DropBackpack()
			{
				var cont_ = cont;
				float dx = Rnd.Next(0,11)/10f-0.5f;
				float dy = 0.5f;
				float dz = Rnd.Next(0,11)/10f-0.5f;

				Ins.timer.Once(Rnd.Next(1,11)/10f, ()=>
				{
					var drop = ItemContainer.Drop("assets/prefabs/misc/item drop/item_drop_backpack.prefab", new Vector3(userPos.x+dx, userPos.y+dy, userPos.z+dz), Quaternion.identity, cont_);
					if (drop != null)
					{
						drop.playerName = userName;
						drop.playerSteamID = userID;
					}
				});			
			}
			
			public ItemContainer container 
			{
				get { return cont; }
				set 
				{  
					if (cont != null) DropBackpack();
					cont = value;										
				}
			}
		}
		
        #endregion
		
		#region Hooks
		
		private void Init() 
		{
			Ins = this;
			LoadVariables();
			permission.RegisterPermission(InstaPerm, this);
			
			foreach (var pair in configData.IndividualPrivCraftingRates)
				permission.RegisterPermission(pair.Key, this);
		}	

        private void OnServerInitialized()
        {						
			SaveBlueprints.Clear();
			
			var blueprintDefinitions = ItemManager.bpList;
			foreach (var bp in blueprintDefinitions)
                SaveBlueprints.Add(bp.targetItem.shortname, bp.time);
				
            foreach (var bp in blueprintDefinitions)
            {
                if (configData.IndividualItemCraftingRates.ContainsKey(bp.targetItem.displayName.english))
                    bp.time = SaveBlueprints[bp.targetItem.shortname] * configData.IndividualItemCraftingRates[bp.targetItem.displayName.english] / 100f;
                else
                    bp.time = SaveBlueprints[bp.targetItem.shortname] * configData.GlobalCraftingRate / 100f;
            }																		
        }				            

		private void OnPlayerConnected(BasePlayer player)		
		{
			if (player == null) return;
			
			if (player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot))
            {
                timer.Once(0.11f, () => OnPlayerConnected(player));
                return;
            }
			
			player.Command("note.craft_done", new object[] { 0, 0 });			
		}
		
		private void Unload()
        {
			foreach (var bp in ItemManager.bpList)
                bp.time = SaveBlueprints[bp.targetItem.shortname];		            														
        }  	
		
		private void OnShutDown()
        {					
            foreach (var player in BasePlayer.activePlayerList.Where(x=> x != null && x.userID.IsSteamId()))                           
                CancelAllCrafting(player);
        } 		
		
		private object OnItemCraft(ItemCraftTask task, BasePlayer crafter)
        {
			if (task == null || crafter == null) return null;
			
            var itemName = task.blueprint.targetItem.displayName.english;         																																																																				
			
			// блок крафта запрещенных предметов
			if (configData.BlockedItems.Contains(itemName))
			{
				task.cancelled = true;
				SendReply(crafter, string.Format("Крафт предмета <color=yellow>{0}</color> запрещен.", itemName));	            
				
				RefundIngredients(task);
					
				task.owner.inventory.containerMain.MarkDirty();
				task.owner.inventory.containerBelt.MarkDirty();					            

				return false;
			}	

			// инстакрафт
			if (configData.GlobalCraftingRate == 0f || HasPermission(crafter.UserIDString, InstaPerm) || IsNearInstaWB(crafter))
			{								
				if (!CanTake(crafter, task.blueprint.targetItem.itemid, task.blueprint.amountToCreate*task.amount))
				{
					task.cancelled = true;
					SendReply(crafter, "В инвентаре недостаточно места.Освободите слоты.");
					
					RefundIngredients(task);
					
					task.owner.inventory.containerMain.MarkDirty();
					task.owner.inventory.containerBelt.MarkDirty();								
						
					return false;
				}	
			
				task.endTime = 1f;
				return null;
			}																			
			
			ChangeRate(task?.owner, task.taskUID);
			
			return null;						
        }        			

        private void OnItemCraftFinished(ItemCraftTask task, Item item)
        {
			if (task == null || item == null) return;
			
			// финишируем инстакрафт
			if (configData.GlobalCraftingRate == 0f || HasPermission(task.owner.UserIDString, InstaPerm) || IsNearInstaWB(task.owner))
			{
				var crafting = task.owner.inventory.crafting;
				if (crafting.queue.Count == 0) return;																							
				
				if (!CanTake(task.owner, task.blueprint.targetItem.itemid, task.blueprint.amountToCreate * task.amount)) return;
							
				for(int ii = 0; ii < task.amount; ii++)
				{																	
					Item t = ItemManager.CreateByItemID(item.info.itemid, 1, (ulong)task.skinID);			
					t.amount = item.amount;
					if (!t.MoveToContainer(task.owner.inventory.containerMain, -1, true))						
						if (!t.MoveToContainer(task.owner.inventory.containerBelt, -1, true))
							t.Drop(task.owner.eyes.position, task.owner.eyes.BodyForward() * 2f); 
				}												
				
				task.owner.inventory.containerMain.MarkDirty();
				task.owner.inventory.containerBelt.MarkDirty();		
				
				if (!NoRealCancel.Contains(task.taskUID))
					NoRealCancel.Add(task.taskUID);
				
				crafting.CancelTask(task.taskUID, false);
				return;
			}
			
			ChangeRate(task?.owner);						
        }										
		
		private void OnItemCraftCancelled(ItemCraftTask itemCraftTask)
		{
			if (itemCraftTask == null || itemCraftTask.owner == null) return;
			
			// исключаем обработку отмены крафта вызванную инстакрафтом
			if (NoRealCancel.Contains(itemCraftTask.taskUID)) 
			{
				NoRealCancel.Remove(itemCraftTask.taskUID);
				return;
			}			
			
			// отменяем крафт игрока включая безопасное выпадение ресурсов, если игрок бежал на крафте и его убили, 
			// но количество возвращаемых предметов не влезает в инвентарь, то все не влезшие предметы будут заключены в доп рюкзаки,
			// что бы не дудосить сервер взорвавшимися предметами
			if (configData.SafeCancelCraft && itemCraftTask.takenItems != null && itemCraftTask.takenItems.Count > 0)
			{
				if (PlayerLastCancelTask.ContainsKey(itemCraftTask.owner.userID))
				{
					var taskUID = Convert.ToInt32(PlayerLastCancelTask[itemCraftTask.owner.userID].Split('|')[0]);
					var time = Convert.ToSingle(PlayerLastCancelTask[itemCraftTask.owner.userID].Split('|')[1]);
					
					// отменяем складирование ресов в рюкзак, при ручной отмене крафта и переполненом инвентаре (все будет падать на землю)
					if ((Time.realtimeSinceStartup - time) < 1f && taskUID == itemCraftTask.taskUID)					
						return;					
				}
				
				var items = itemCraftTask.takenItems.ToList();
				var player = itemCraftTask.owner;
				var userID = player.userID;				
				itemCraftTask.takenItems.Clear();
								
				var tmpCont = TemporaryContainer.Init(player);				
				
				foreach (Item takenItem in items)
				{
					if (takenItem != null && takenItem.amount > 0)
					{							
						if (takenItem.IsBlueprint() && takenItem.blueprintTargetDef == itemCraftTask.blueprint.targetItem)						
							takenItem.UseItem(itemCraftTask.numCrafted);						
						
						if (takenItem.amount <= 0) continue; 
						
						if (tmpCont.container == null)							
							if (!takenItem.MoveToContainer(player.inventory.containerMain, -1, true))
								if (!takenItem.MoveToContainer(player.inventory.containerBelt, -1, true))
									tmpCont.container = NewItemCont();
						
						if (tmpCont.container == null) continue;												
						
						if (!takenItem.MoveToContainer(tmpCont.container, -1, true))
						{
							tmpCont.container = NewItemCont();
							
							if (!takenItem.MoveToContainer(tmpCont.container, -1, true))
								takenItem.Drop(player.inventory.containerMain.dropPosition, player.inventory.containerMain.dropVelocity);												
						}	
					}
				}
			}
			
			var lastTask = itemCraftTask.owner.inventory.crafting.queue.FirstOrDefault();
			if (lastTask.taskUID == itemCraftTask.taskUID)			
				ChangeRate(itemCraftTask?.owner, 0);
		}
		
		private void OnServerCommand(ConsoleSystem.Arg arg)
        {
            if (arg == null || arg.Connection == null || arg.cmd == null || string.IsNullOrEmpty(arg.cmd.FullName) || !(arg.cmd.FullName == "craft.canceltask")) return;
            var player = arg.Player();			
			if (player == null || arg.Args == null || arg.Args.Length != 1) return;
			
			if (!PlayerLastCancelTask.ContainsKey(player.userID))
				PlayerLastCancelTask.Add(player.userID, "");
			
			PlayerLastCancelTask[player.userID] = $"{arg.Args[0]}|{Time.realtimeSinceStartup}";
		}
		
		#endregion						
		
		#region Helpers
		
		private bool HasPermission(string steamId, string perm) => permission.UserHasPermission(steamId, perm);                						
		
        private static void CancelAllCrafting(BasePlayer player)
        {
			if (player == null) return;
			
            var crafter = player.inventory.crafting;
            foreach (var task in crafter.queue)							
                crafter.CancelTask(task.taskUID, true);				
        }        
		
        private static bool CanTake(BasePlayer player, int itemId, int needAmount)
        {
			if (player == null) return false;
			
			var info = ItemManager.FindItemDefinition(itemId);
			if (info == null) return false;
			
            var containerMain = player.inventory.containerMain;
			var containerBelt = player.inventory.containerBelt;            			
			
			var sameItemsCount = containerMain.FindItemsByItemID(itemId).Count + containerBelt.FindItemsByItemID(itemId).Count;
			var freeSpaceCount = (containerMain.capacity - containerMain.itemList.Count) + (containerBelt.capacity - containerBelt.itemList.Count);
			var usedAmount = containerMain.GetAmount(itemId, true) + containerBelt.GetAmount(itemId, true);
			
			return ((sameItemsCount + freeSpaceCount) * info.stackable - usedAmount) >= needAmount;						
        }		            
		
		private static void RefundIngredients(ItemCraftTask task)
        {
			if (task == null) return;
			
			var items = new Dictionary<int, int>();
		
			foreach (var item in task.blueprint.ingredients)
			{
				if (!items.ContainsKey(item.itemid))
					items.Add(item.itemid, Convert.ToInt32(item.amount) * task.amount);
				else
					items[item.itemid] += Convert.ToInt32(item.amount) * task.amount;
			}					
			
			foreach (var item in items)
			{										
				var info = ItemManager.FindItemDefinition(item.Key);
				if (info == null) continue;
				var stackable = info.stackable;
				if (stackable <= 0) continue;
				var value = item.Value;
				
				while (value > stackable)
				{						
					Item i = ItemManager.CreateByItemID(item.Key, stackable);
					if (!i.MoveToContainer(task.owner.inventory.containerMain)) 
						if (!i.MoveToContainer(task.owner.inventory.containerBelt)) 
							i.Drop(task.owner.eyes.position, task.owner.eyes.BodyForward() * 2f); 

					value -= stackable;
				}
				
				if (value > 0)
				{
					Item i = ItemManager.CreateByItemID(item.Key, value);
					if (!i.MoveToContainer(task.owner.inventory.containerMain)) 
						if (!i.MoveToContainer(task.owner.inventory.containerBelt)) 
							i.Drop(task.owner.eyes.position, task.owner.eyes.BodyForward() * 2f); 
				}					
			}												        
        }
		
		private void ChangeRate(BasePlayer player, int taskUID = 0)
		{
			if (player == null) return;
			
			// уменьшенный рейт по привилегии
			if (configData.IndividualPrivCraftingRates.Count > 0)
			{
				var rate = float.MaxValue;
			
				foreach (var pair in configData.IndividualPrivCraftingRates.OrderBy(x=> x.Value))
				{
					if (HasPermission(player.UserIDString, pair.Key))
					{
						rate = pair.Value;
						break;
					}
				}
				
				if (rate < configData.GlobalCraftingRate)
				{					
					timer.Once(0.1f, ()=> 
					{
						if (player != null)
							ChangeCraftTime(player.userID, taskUID, rate);
					});
				}
			}
		}
		
		private void ChangeCraftTime(ulong userID, int taskUID, float rate)
		{			
			var player = BasePlayer.FindByID(userID);			
			if (player == null) return;

			var crafter = player.inventory.crafting;
			if (crafter.queue.Where(x => !x.cancelled).Count() == 0) return;						
			
			var task = crafter.queue.FirstOrDefault(x => !x.cancelled);
			if (task == null || string.IsNullOrEmpty(task.blueprint?.targetItem?.shortname)) return;
			
			if (taskUID != 0 && taskUID != task.taskUID) return;
						
			var rss = Time.realtimeSinceStartup;
			var bpTime = GetBpTime(player, task.blueprint);
			var taskEndTime = task.endTime * (100f / configData.GlobalCraftingRate); 
			
			float endTime = ( (taskEndTime - rss) > 0f && (taskEndTime - rss) <= bpTime ) ? taskEndTime : (rss + bpTime);																	
			float value = (endTime - rss) * (rate / 100f);
			if (value > bpTime) value = bpTime;
			
			timer.Once(0.1f, ()=> task.owner.Command("note.craft_start", new object[] { task.taskUID, value, task.amount }));
			
			float diff = rss + value;
			task.endTime = diff;
		}
		
		private static float GetBpTime(BasePlayer player, ItemBlueprint bp) => GetScaledDuration(bp, player.currentCraftLevel);		
		
		// копия метода из раста, но с нашим измененным bp.time
		private static float GetScaledDuration(ItemBlueprint bp, float workbenchLevel)
		{
			float single = workbenchLevel - (float)bp.workbenchLevelRequired;			
			var bpTime = SaveBlueprints.ContainsKey(bp.targetItem.shortname) ? SaveBlueprints[bp.targetItem.shortname] : bp.time;
			
			if (single == 1f)			
				return bpTime * 0.5f;
			
			if (single < 2f)			
				return bpTime;
			
			return bpTime * 0.25f;
		}
		
		private static bool IsNearInstaWB(BasePlayer player)
		{						
			if (configData.InstaCraftNearWorkBench3 && player.HasPlayerFlag(BasePlayer.PlayerFlags.Workbench3))
				return true;						
			
			return false;
		}
		
		private static ItemContainer NewItemCont()
		{
			var cont = new ItemContainer();			
            cont.ServerInitialize(null, 36);
            cont.GiveUID();			
            return cont;
		}				
		
		#endregion
		
		#region Config        
		
        private static ConfigData configData;
		
        private class ConfigData
        {
			[JsonProperty(PropertyName = "Глобальный рейт крафта (0-100%)")]
			public float GlobalCraftingRate;
			[JsonProperty(PropertyName = "Индивидуальный рейт крафта для отдельных предметов (полное имя и 0-100%)")]
			public Dictionary<string, float> IndividualItemCraftingRates;
			[JsonProperty(PropertyName = "Индивидуальный рейт крафта для отдельных привилегий (привилегия и 0-100%)")]
			public Dictionary<string, float> IndividualPrivCraftingRates;
			[JsonProperty(PropertyName = "Разрешить инстакрафт у верстака 3го уровня")]
			public bool InstaCraftNearWorkBench3;
			[JsonProperty(PropertyName = "Заблокированные для крафта предметы (полное имя)")]
			public List<string> BlockedItems;
			[JsonProperty(PropertyName = "Разрешить безопасную отмену крафта (антиддос предметами)")]
			public bool SafeCancelCraft;
        }
		
        private void LoadVariables() => LoadConfigVariables();                    
		
        private void LoadDefaultConfig()
        {
            var config = new ConfigData
            {
				BlockedItems = new List<string>(),
				GlobalCraftingRate = 100f,
				IndividualItemCraftingRates = new Dictionary<string, float>(),
				IndividualPrivCraftingRates = new Dictionary<string, float>() 
				{
					{ "craftsystem.rate50", 50f },
					{ "craftsystem.rate25", 25f }
				},
				InstaCraftNearWorkBench3 = false,
				SafeCancelCraft = true
            };
            SaveConfig(config);
			timer.Once(0.1f, ()=> SaveConfig(config));
        }
		
        private void LoadConfigVariables() => configData = Config.ReadObject<ConfigData>();
		
        private void SaveConfig(ConfigData config) => Config.WriteObject(config, true);
		
        #endregion	
				
    }
}

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