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

FREE FurnaceSplitter : 2.1.2 2.1.2

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;




namespace Oxide.Plugins
{
	[Info("Furnace Splitter", "Skipcast", "2.1.2", ResourceId = 2406)]
	[Description("Splits up resources in furnaces automatically and shows useful furnace information.")]
	public class FurnaceSplitter : RustPlugin
	{
		private static class Extensions
		{
			public static T GetBaseEntity<T>(EntityComponent<T> entityComponent) where T : BaseEntity
			{
				var propertyInfo = entityComponent.GetType().GetProperty("baseEntity", BindingFlags.Instance | BindingFlags.NonPublic);
				return (T)propertyInfo.GetValue(entityComponent, null);
			}

			public static float GetWorkTemperature(BaseOven oven)
			{
				return oven.cookingTemperature;
			}

			public static IEnumerable<T> ToIEnumerable<T>(IEnumerator<T> enumerator)
			{
				while (enumerator.MoveNext())
				{
					yield return enumerator.Current;
				}
			}
		}

		private class OvenSlot
		{
			/// <summary>The item in this slot.May be null.</summary>
			public Item Item;

			/// <summary>The slot position</summary>
			public int? Position;

			/// <summary>The slot's index in the itemList list.</summary>
			public int Index;

			/// <summary>How much should be added/removed from stack</summary>
			public int DeltaAmount;
		}

		public class OvenInfo
		{
			public float ETA;
			public float FuelNeeded;
		}

		private class StoredData
		{
			public Dictionary<ulong, PlayerOptions> AllPlayerOptions { get; private set; } = new Dictionary<ulong, PlayerOptions>();
		}

		private class PluginConfig
		{
			//[JsonRequired]
			public Vector2 UiPosition { get; set; } = new Vector2(0.6505f, 0.022f);
		}

		private class PlayerOptions
		{
			public bool Enabled;
			public Dictionary<string, int> TotalStacks = new Dictionary<string, int>();
		}

		public enum MoveResult
		{
			Ok,
			SlotsFilled,
			NotEnoughSlots
		}

		private class Vector2Converter : JsonConverter
		{
			public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
			{
				var vec = (Vector2) value;
				serializer.Serialize(writer, new { vec.x, vec.y });
			}

			public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
			{
				Vector2 result = new Vector2();
				JObject jVec = JObject.Load(reader);

				result.x = jVec["x"].ToObject<float>();
				result.y = jVec["y"].ToObject<float>();
				
				return result;
			}

			public override bool CanConvert(Type objectType)
			{
				return objectType == typeof (Vector2);
			}
		}

		private StoredData storedData;
		private PluginConfig config;
		private Dictionary<ulong, PlayerOptions> allPlayerOptions => storedData.AllPlayerOptions;

		private readonly Dictionary<BaseOven, List<BasePlayer>> looters = new Dictionary<BaseOven, List<BasePlayer>>();

		private readonly string[] compatibleOvens =
		{
			"furnace",
			"furnace.large",
			"campfire",
			"refinery_small_deployed",
			"hobobarrel_static"
		};

		private void OnPlayerInit(BasePlayer player)
		{
			InitPlayer(player);
		}

		private void Init()
		{
			// Only add if it's not already been added in LoadDefaultConfig.That would be the case the first time the plugin is initialized.if (!Config.Settings.Converters.Any(conv => conv.GetType() == typeof (Vector2Converter)))
				Config.Settings.Converters.Add(new Vector2Converter());
		}

		private void Loaded()
		{
			storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("FurnaceSplitter");

			if (!permission.PermissionExists("furnacesplitter.use", this))
				permission.RegisterPermission("furnacesplitter.use", this);

			if (config == null)
			{
				// Default config not created, load existing config.config = Config.ReadObject<PluginConfig>();
			}
			else
			{
				// Save default config.Config.WriteObject(config);
			}
		}
		
		#region Configuration

		protected override void LoadDefaultConfig()
		{
			Config.Settings.Converters.Add(new Vector2Converter());
			PrintWarning("Creating default config for FurnaceSplitter.");
			config = new PluginConfig();
		}

		#endregion

		private void OnServerInitialized()
		{
			foreach (var player in Player.Players)
			{
				InitPlayer(player);
			}

			lang.RegisterMessages(new Dictionary<string, string>
			{
				{ "turnon", "Turn On" },
				{ "turnoff", "Turn Off" },
				{ "title", "Furnace Splitter" },
				{ "eta", "ETA" },
				{ "totalstacks", "Total stacks" },
				{ "trim", "Trim fuel" },
				{ "lootsource_invalid", "Current loot source invalid" },
				{ "unsupported_furnace", "Unsupported furnace." },
				{ "nopermission", "You don't have permission to use this." }
			}, this, "en");
		}

		private void OnServerSave()
		{
			SaveData();
		}

		private void SaveData()
		{
			Interface.Oxide.DataFileSystem.WriteObject("FurnaceSplitter", storedData);
		}

		private void InitPlayer(BasePlayer player)
		{
			if (!allPlayerOptions.ContainsKey(player.userID))
			{
				allPlayerOptions[player.userID] = new PlayerOptions
				{
					Enabled = true,
					TotalStacks = new Dictionary<string, int>()
				};
			}
			
			var initialStackOptions = new Dictionary<string, int>
			{
				{"furnace", 3},
				{"furnace.large", 15},
				{"campfire", 2},
				{"refinery_small_deployed", 3},
				{"bbq.deployed", 3},
				{"hobobarrel_static", 2}
			};

			var options = allPlayerOptions[player.userID];

			foreach (var kv in initialStackOptions)
			{
				if (!options.TotalStacks.ContainsKey(kv.Key))
					options.TotalStacks.Add(kv.Key, kv.Value);
			}
		}

		public OvenInfo GetOvenInfo(BaseOven oven)
		{
			using (TimeWarning.New("FurnaceSplitter.GetOvenInfo", 0.01f))
			{
				var result = new OvenInfo();
				var smeltTimes = GetSmeltTimes(oven);

				if (smeltTimes.Count > 0)
				{
					var longestStack = smeltTimes.OrderByDescending(kv => kv.Value).First();
					var fuelUnits = oven.fuelType.GetComponent<ItemModBurnable>().fuelAmount;
					var ovenTemperature = Extensions.GetWorkTemperature(oven);
					float neededFuel = (float) Math.Ceiling((longestStack.Value * (ovenTemperature / 200.0f)) / fuelUnits);

					result.FuelNeeded = neededFuel;
					result.ETA = longestStack.Value;
				}

				return result;
			}
		}

		private void Unload()
		{
			SaveData();
		}

		private bool GetEnabled(BasePlayer player)
		{
			return allPlayerOptions[player.userID].Enabled;
		}

		private void SetEnabled(BasePlayer player, bool enabled)
		{
			allPlayerOptions[player.userID].Enabled = enabled;
		}
		
		private bool IsSlotCompatible(Item item, BaseOven oven, ItemDefinition itemDefinition)
		{
			var cookable = item.info.GetComponent<ItemModCookable>();

			if (item.info == itemDefinition)
				return true;

			if (oven.allowByproductCreation && oven.fuelType.GetComponent<ItemModBurnable>().byproductItem == item.info)
				return true;

			if (cookable == null || cookable.becomeOnCooked == itemDefinition)
				return true;

			if (CanCook(cookable, oven))
				return true;

			return false;
		}

		private List<BasePlayer> GetLooters(BaseOven oven)
		{
			if (looters.ContainsKey(oven))
				return looters[oven];

			return null;
		}

		private void AddLooter(BaseOven oven, BasePlayer player)
		{
			if (!looters.ContainsKey(oven))
				looters[oven] = new List<BasePlayer>();

			var list = looters[oven];
			list.Add(player);
		}

		private void RemoveLooter(BaseOven oven, BasePlayer player)
		{
			if (!looters.ContainsKey(oven))
				return;

			looters[oven].Remove(player);
		}

		private object CanMoveItem(Item item, PlayerInventory playerLoot, uint targetContainer, int targetSlot)
		{
			int i = 0;
			try
			{
				i++;
				var player = Extensions.GetBaseEntity(playerLoot.loot);
				i++;
				var container = playerLoot.FindContainer(targetContainer);
				i++;
				var originalContainer = item.GetRootContainer();
				i++;
				if (player == null || container == null || originalContainer == null)
					return null;

				i++;
				if (container.entityOwner == null)
					return null;
				i++;
				if (container.entityOwner.ShortPrefabName.Contains("bbq"))
					return null;

				i++;
				Func<object> splitFunc = () =>
				{
					i++;
					using (TimeWarning.New("FurnaceSplitter.CanMoveItem", 0.005f))
					{
						i++;
						if (player == null || !HasPermission(player) || !GetEnabled(player))
							return null;

						i++;
						var playerOptions = allPlayerOptions[player.userID];

						i++;
						if (container == null || container == item.GetRootContainer())
							return null;

						i++;
						var oven = container.entityOwner as BaseOven;
						i++;
						var cookable = item.info.GetComponent<ItemModCookable>();

						i++;
						if (oven == null || cookable == null)
							return null;

						i++;
						int totalSlots = 2 + (oven.allowByproductCreation ? 1 : 0);

						i++;
						if (playerOptions.TotalStacks.ContainsKey(oven.ShortPrefabName))
						{
							totalSlots = playerOptions.TotalStacks[oven.ShortPrefabName];
						}

						i++;
						float workTemperature = Extensions.GetWorkTemperature(oven);

						i++;
						if (cookable.lowTemp > workTemperature || cookable.highTemp < workTemperature)
							return null;

						i++;
						MoveSplitItem(item, oven, totalSlots);
						return true;
					}
				};

				object returnValue = splitFunc();

				if (HasPermission(player) && GetEnabled(player))
				{
					var oven = container?.entityOwner as BaseOven ?? item.GetRootContainer().entityOwner as BaseOven;

					if (oven != null && compatibleOvens.Contains(oven.ShortPrefabName))
					{
						if (returnValue is bool && (bool) returnValue)
							AutoAddFuel(playerLoot, oven);
					}
				}

				return returnValue;
			}
			catch (NullReferenceException)
			{
				PrintError($"Ошибка на строке: {i}");
			}

			return 0;
		}

		public MoveResult MoveSplitItem(Item item, BaseOven oven, int totalSlots)
		{
			var container = oven.inventory;
			int invalidItemsCount = container.itemList.Count(slotItem => !IsSlotCompatible(slotItem, oven, item.info));
			int numOreSlots = Math.Min(container.capacity - invalidItemsCount, totalSlots);
			int totalMoved = 0;
			int totalAmount = Math.Min(item.amount + container.itemList.Where(slotItem => slotItem.info == item.info).Take(numOreSlots).Sum(slotItem => slotItem.amount), item.info.stackable * numOreSlots);

			if (numOreSlots <= 0)
			{
				return MoveResult.NotEnoughSlots;
			}

			//Puts("---------------------------");

			int totalStackSize = Math.Min(totalAmount / numOreSlots, item.info.stackable);
			int remaining = totalAmount - (totalAmount / numOreSlots) * numOreSlots;

			List<int> addedSlots = new List<int>();

			//Puts("total: {0}, remaining: {1}, totalStackSize: {2}", totalAmount, remaining, totalStackSize);

			List<OvenSlot> ovenSlots = new List<OvenSlot>();

			for (int i = 0; i < numOreSlots; ++i)
			{
				Item existingItem;
				var slot = FindMatchingSlotIndex(container, out existingItem, item.info, addedSlots);

				if (slot == -1) // full
				{
					Item probably = container.FindItemsByItemName("sulfur.ore") ?? container.FindItemsByItemName("metal.ore") ?? container.FindItemsByItemName("hq.metal.ore");
					int itemid = container.itemList[probably.position].info.itemid;
					int amount = 0;
					List<Item> toRemove = new List<Item>();

					foreach (var check in container.itemList.Where(p => p.info.itemid == itemid))
					{
						toRemove.Add(check);
						amount += check.amount;
					}

					foreach (var check in toRemove)
						container.itemList.Remove(check);
					
					MoveSplitItem(ItemManager.CreateByItemID(itemid, amount), oven, totalSlots / 2);
					MoveSplitItem(item, oven, totalSlots / 2);
					return MoveResult.NotEnoughSlots;
				}

				addedSlots.Add(slot);

				var ovenSlot = new OvenSlot
				{
					Position = existingItem?.position,
					Index = slot,
					Item = existingItem
				};

				int currentAmount = existingItem?.amount ?? 0;
				int missingAmount = totalStackSize - currentAmount + (i < remaining ? 1 : 0);
				ovenSlot.DeltaAmount = missingAmount;

				//Puts("[{0}] current: {1}, delta: {2}, total: {3}", slot, currentAmount, ovenSlot.DeltaAmount, currentAmount + missingAmount);

				if (currentAmount + missingAmount <= 0)
					continue;

				ovenSlots.Add(ovenSlot);
			}

			foreach (var slot in ovenSlots)
			{
				if (slot.Item == null)
				{
					var newItem = ItemManager.Create(item.info, slot.DeltaAmount, item.skin);
					slot.Item = newItem;
					newItem.MoveToContainer(container, slot.Position ?? slot.Index);
				}
				else
				{
					slot.Item.amount += slot.DeltaAmount;
				}

				totalMoved += slot.DeltaAmount;
			}

			container.MarkDirty();

			if (totalMoved >= item.amount)
			{
				item.Remove();
				item.GetRootContainer()?.MarkDirty();
				return MoveResult.Ok;
			}
			else
			{
				item.amount -= totalMoved;
				item.GetRootContainer()?.MarkDirty();
				return MoveResult.SlotsFilled;
			}

		}

		private void AutoAddFuel(PlayerInventory playerInventory, BaseOven oven)
		{
			int neededFuel = (int)Math.Ceiling(GetOvenInfo(oven).FuelNeeded);
			neededFuel -= oven.inventory.GetAmount(oven.fuelType.itemid, false);
			var playerFuel = playerInventory.FindItemIDs(oven.fuelType.itemid);

			if (neededFuel <= 0 || playerFuel.Count <= 0)
				return;
			
			foreach (var fuelItem in playerFuel)
			{
			    if (oven.inventory.CanAcceptItem(fuelItem, -1) != ItemContainer.CanAcceptResult.CanAccept)
			        break;

				var largestFuelStack = oven.inventory.itemList.Where(item => item.info == oven.fuelType).OrderByDescending(item => item.amount).FirstOrDefault();
				var toTake = Math.Min(neededFuel, oven.fuelType.stackable - (largestFuelStack?.amount ?? 0));

				if (toTake > fuelItem.amount)
					toTake = fuelItem.amount;
				
				if (toTake <= 0)
					break;
				
				neededFuel -= toTake;
				
				if (toTake >= fuelItem.amount)
				{
					fuelItem.MoveToContainer(oven.inventory);
				}
				else
				{
					var splitItem = fuelItem.SplitItem(toTake);
					if (!splitItem.MoveToContainer(oven.inventory)) // Break if oven is full
						break;
				}
			
				if (neededFuel <= 0)
					break;
			}
		}

		private int FindMatchingSlotIndex(ItemContainer container, out Item existingItem, ItemDefinition itemType, List<int> indexBlacklist)
		{
			existingItem = null;
			int firstIndex = -1;
			Dictionary<int, Item> existingItems = new Dictionary<int, Item>();

			for (int i = 0; i < container.capacity; ++i)
			{
				if (indexBlacklist.Contains(i))
					continue;

				Item itemSlot = container.GetSlot(i);
				if (itemSlot == null || (itemType != null && itemSlot.info == itemType))
				{
					if (itemSlot != null)
						existingItems.Add(i, itemSlot);

					if (firstIndex == -1)
					{
						existingItem = itemSlot;
						firstIndex = i;
					}
				}
			}

			if (existingItems.Count <= 0 && firstIndex != -1)
			{
				return firstIndex;
			}
			else if (existingItems.Count > 0)
			{
				var largestStackItem = existingItems.OrderByDescending(kv => kv.Value.amount).First();
				existingItem = largestStackItem.Value;
				return largestStackItem.Key;
			}

			existingItem = null;
			return -1;
		}

		private void OnLootEntity(BasePlayer player, BaseEntity entity)
		{
			var oven = entity as BaseOven;

			if (oven == null || !HasPermission(player) || !compatibleOvens.Contains(oven.ShortPrefabName))
				return;
			
			AddLooter(oven, player);
		}

		private void OnLootEntityEnd(BasePlayer player, BaseCombatEntity entity)
		{
			var oven = entity as BaseOven;

			if (oven == null || !compatibleOvens.Contains(oven.ShortPrefabName))
				return;

			RemoveLooter(oven, player);
		}

		private string FormatTime(float totalSeconds)
		{
			var hours = Math.Floor(totalSeconds / 3600);
			var minutes = Math.Floor((totalSeconds / 60) % 60);
			var seconds = totalSeconds % 60;

			if (hours <= 0 && minutes <= 0)
				return seconds + "s";
			else if (hours <= 0)
				return minutes + "m" + seconds + "s";
			else
				return hours + "h" + minutes + "m" + seconds + "s";
		}

		private Dictionary<ItemDefinition, float> GetSmeltTimes(BaseOven oven)
		{
			var container = oven.inventory;
			var cookables = container.itemList.Where(item =>
			{
				var cookable = item.info.GetComponent<ItemModCookable>();
				return cookable != null && CanCook(cookable, oven);
			}).ToList();

			if (cookables.Count == 0)
				return new Dictionary<ItemDefinition, float>();

			var distinctCookables = cookables.GroupBy(item => item.info, item => item).ToList();
			Dictionary<ItemDefinition, int> amounts = new Dictionary<ItemDefinition, int>();

			foreach (var group in distinctCookables)
			{
				int biggestAmount = Extensions.ToIEnumerable(group.GetEnumerator()).Max(item => item.amount);
				amounts.Add(group.Key, biggestAmount);
			}

			var smeltTimes = amounts.ToDictionary(kv => kv.Key, kv => GetSmeltTime(kv.Key.GetComponent<ItemModCookable>(), kv.Value));
			return smeltTimes;
		}

		private bool CanCook(ItemModCookable cookable, BaseOven oven)
		{
			float workTemperature = Extensions.GetWorkTemperature(oven);
			return workTemperature >= cookable.lowTemp && workTemperature <= cookable.highTemp;
		}

		private float GetSmeltTime(ItemModCookable cookable, int amount)
		{
			float smeltTime = cookable.cookTime * amount;
			return smeltTime;
		}

		private int? GetTotalStacksOption(BasePlayer player, BaseOven oven)
		{
			var options = allPlayerOptions[player.userID];

			if (options.TotalStacks.ContainsKey(oven.ShortPrefabName))
				return options.TotalStacks[oven.ShortPrefabName];

			return null;
		}

		[ConsoleCommand("furnacesplitter.enabled")]
		private void ConsoleCommand_Toggle(ConsoleSystem.Arg arg)
		{
			var player = arg.Player();

			if (!HasPermission(player))
			{
				player.ConsoleMessage(lang.GetMessage("nopermission", this, player.UserIDString));
				return;
			}

			if (!arg.HasArgs())
			{
				player.ConsoleMessage(GetEnabled(player).ToString());
				return;
			}

			bool enabled = arg.GetBool(0);
			SetEnabled(player, enabled);
		}

		[ConsoleCommand("furnacesplitter.totalstacks")]
		private void ConsoleCommand_TotalStacks(ConsoleSystem.Arg arg)
		{
			var player = arg.Player();
			var lootSource = player.inventory.loot?.entitySource as BaseOven;

			if (!HasPermission(player))
			{
				player.ConsoleMessage(lang.GetMessage("nopermission", this, player.UserIDString));
				return;
			}
			
			if (lootSource == null || !compatibleOvens.Contains(lootSource.ShortPrefabName))
			{
				player.ConsoleMessage(lang.GetMessage("lootsource_invalid", this, player.UserIDString));
				return;
			}

			string ovenName = lootSource.ShortPrefabName;
			var playerOption = allPlayerOptions[player.userID];

			if (playerOption.TotalStacks.ContainsKey(ovenName))
			{
				if (!arg.HasArgs())
				{
					player.ConsoleMessage(playerOption.TotalStacks[ovenName].ToString());
				}
				else
				{
					var newValue = (int) Mathf.Clamp(arg.GetInt(0), 0, lootSource.inventory.capacity);
					playerOption.TotalStacks[ovenName] = newValue;
				}
			}
			else
			{
				Debug.LogWarning("[FurnaceSplitter] Unsupported furnace '" + ovenName + "'");
				player.ConsoleMessage(lang.GetMessage("unsupported_furnace", this, player.UserIDString));
			}

		}

		[ConsoleCommand("furnacesplitter.trim")]
		private void ConsoleCommand_Trim(ConsoleSystem.Arg arg)
		{
			var player = arg.Player();
			var lootSource = player.inventory.loot?.entitySource as BaseOven;

			if (!HasPermission(player))
			{
				player.ConsoleMessage(lang.GetMessage("nopermission", this, player.UserIDString));
				return;
			}

			if (lootSource == null || !compatibleOvens.Contains(lootSource.ShortPrefabName))
			{
				player.ConsoleMessage(lang.GetMessage("lootsource_invalid", this, player.UserIDString));
				return;
			}

			var ovenInfo = GetOvenInfo(lootSource);
			var fuelSlots = lootSource.inventory.itemList.Where(item => item.info == lootSource.fuelType).ToList();
			int totalFuel = fuelSlots.Sum(item => item.amount);
			int toRemove = (int) Math.Floor(totalFuel - ovenInfo.FuelNeeded);

			if (toRemove <= 0)
				return;
			
			foreach (var fuelItem in fuelSlots)
			{
				var toTake = Math.Min(fuelItem.amount, toRemove);
				toRemove -= toTake;

				var dropPosition = player.GetDropPosition();
				var dropVelocity = player.GetDropVelocity();

				if (toTake >= fuelItem.amount)
				{
					if (!player.inventory.GiveItem(fuelItem))
						fuelItem.Drop(dropPosition, dropVelocity, Quaternion.identity);
				}
				else
				{
					var splitItem = fuelItem.SplitItem(toTake);
					if (!player.inventory.GiveItem(splitItem))
						splitItem.Drop(dropPosition, dropVelocity, Quaternion.identity);
				}

				if (toRemove <= 0)
					break;
			}
		}

		private bool HasPermission(BasePlayer player)
		{
			return true;
		}

		#region Exposed plugin methods
		
		[HookMethod("MoveSplitItem")]
		public string Hook_MoveSplitItem(Item item, BaseOven oven, int totalSlots)
		{
			MoveResult result = MoveSplitItem(item, oven, totalSlots);
			return result.ToString();
		}
		
		[HookMethod("GetOvenInfo")]
		public JObject Hook_GetOvenInfo(BaseOven oven)
		{
			var ovenInfo = GetOvenInfo(oven);
			return JObject.FromObject(ovenInfo);
		}

		#endregion
	}
}

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