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

FREE BetterLoot : 3.0.1 3.0.1

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

namespace Oxide.Plugins
{
	[Info("BetterLoot", "FuJiCuRa / Borland", "3.0.1")]
	[Description("A light ALL lootcontainer modification system")]
	public class BetterLoot : RustPlugin
	{
		[PluginReference]
		Plugin CustomLootSpawns;

		bool Changed = false;
		int populatedContainers;
		StoredExportNames storedExportNames = new StoredExportNames();
		Random rng = new Random();
		bool initialized = false;
		Dictionary<string, List<string>[]> Items = new Dictionary<string, List<string>[]>();
        Dictionary<string, List<string>[]> Blueprints = new Dictionary<string, List<string>[]>();
		Dictionary<string, int[]> itemWeights = new Dictionary<string, int[]>();
        Dictionary<string, int[]> blueprintWeights = new Dictionary<string, int[]>();
		Dictionary<string, int> totalItemWeight = new Dictionary<string, int>();
		Dictionary<string, int> totalBlueprintWeight = new Dictionary<string, int>();
		DynamicConfigFile lootTable;

		DynamicConfigFile getFile(string file) => Interface.Oxide.DataFileSystem.GetDatafile($"{this.Title}/{file}");
		bool chkFile(string file) => Interface.Oxide.DataFileSystem.ExistsDatafile($"{this.Title}/{file}");
		Dictionary<string, object> lootTables = null;

		static List<object> lootPrefabDefaults()
		{
			var dp = new List<object>()
			{
				"assets/bundled/prefabs/radtown/crate_basic.prefab",
				"assets/bundled/prefabs/radtown/crate_elite.prefab",
				"assets/bundled/prefabs/radtown/crate_mine.prefab",
				"assets/bundled/prefabs/radtown/crate_normal.prefab",
				"assets/bundled/prefabs/radtown/crate_normal_2.prefab",
				"assets/bundled/prefabs/radtown/crate_normal_2_food.prefab",
				"assets/bundled/prefabs/radtown/crate_normal_2_medical.prefab",
				"assets/bundled/prefabs/radtown/crate_tools.prefab",
				"assets/bundled/prefabs/radtown/crate_underwater_advanced.prefab",
				"assets/bundled/prefabs/radtown/crate_underwater_basic.prefab",
				"assets/bundled/prefabs/radtown/foodbox.prefab",
				"assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
				"assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
				"assets/bundled/prefabs/radtown/loot_trash.prefab",
				"assets/bundled/prefabs/radtown/minecart.prefab",
				"assets/bundled/prefabs/radtown/oil_barrel.prefab",
				"assets/prefabs/npc/m2bradley/bradley_crate.prefab",
				"assets/prefabs/npc/patrol helicopter/heli_crate.prefab",
				"assets/prefabs/deployable/chinooklockedcrate/codelockedhackablecrate.prefab",
				"assets/prefabs/misc/supply drop/supply_drop.prefab",
    "assets/bundled/prefabs/autospawn/resource/loot/loot-barrel-2.prefab",
      "assets/bundled/prefabs/autospawn/resource/loot/loot-barrel-1.prefab"
			};
			return dp;
		}

		void LoadAllContainers()
        {
			try { lootTable = getFile("LootTables");}
			catch (JsonReaderException e)
			{
				PrintWarning($"JSON error in 'LootTables' > Line: {e.LineNumber} | {e.Path}");
				Interface.GetMod().UnloadPlugin(this.Title);
				return;
			}
			lootTables = new Dictionary<string, object>();
			lootTables = lootTable["LootTables"] as Dictionary<string, object>;
			if (lootTables == null)
				lootTables = new Dictionary<string, object>();
			bool wasAdded = false;
			foreach (var lootPrefab in lootPrefabsToUse)
            {
				if (!lootTables.ContainsKey((string)lootPrefab))
				{
					var loot = GameManager.server.FindPrefab((string)lootPrefab)?.GetComponent<LootContainer>();
					if (loot == null)
						continue;
					var container = new Dictionary<string, object>();
					container.Add("Enabled", ( (((string)lootPrefab).Contains("bradley_crate")) || (((string)lootPrefab).Contains("heli_crate")) ) ? false : true);
					container.Add("Scrap", loot.scrapAmount);
					int slots = 0;
					if (loot.LootSpawnSlots.Length > 0)
					{
						LootContainer.LootSpawnSlot[] lootSpawnSlots = loot.LootSpawnSlots;
						for (int i = 0; i < lootSpawnSlots.Length; i++)
							slots += lootSpawnSlots[i].numberToSpawn;
					}
					else
						slots = loot.maxDefinitionsToSpawn;
					container.Add("ItemsMin", slots);
					container.Add("ItemsMax", slots);
					container.Add("MaxBPs", 1);
					var itemList = new Dictionary<string, object>();
					if (loot.lootDefinition != null)
						GetLootSpawn(loot.lootDefinition, ref itemList);
					else if(loot.LootSpawnSlots.Length > 0)
					{
						LootContainer.LootSpawnSlot[] lootSpawnSlots = loot.LootSpawnSlots;
						for (int i = 0; i < lootSpawnSlots.Length; i++)
						{
							LootContainer.LootSpawnSlot lootSpawnSlot = lootSpawnSlots[i];
							GetLootSpawn(lootSpawnSlot.definition, ref itemList);
						}
					}
					container.Add("ItemList", itemList);
					lootTables.Add((string)lootPrefab, container );
					wasAdded = true;
				}

			}
			if (wasAdded)
			{
				lootTable.Set("LootTables", lootTables);
				lootTable.Save();
			}
			wasAdded = false;
			bool wasRemoved = false;
			int activeTypes = 0;
			foreach (var lootTable in lootTables.ToList())
			{
				var loot = GameManager.server.FindPrefab(lootTable.Key)?.GetComponent<LootContainer>();
				if (loot == null)
				{
					lootTables.Remove(lootTable.Key);
					wasRemoved = true;
					continue;
				}
				var container = lootTable.Value as Dictionary<string, object>;
				if (!container.ContainsKey("Enabled"))
				{
					container.Add("Enabled", true);
					wasAdded = true;
				}
				if ((bool)container["Enabled"])
					activeTypes++;
				if (!container.ContainsKey("Scrap"))
				{
					container.Add("Scrap", loot.scrapAmount);
					wasAdded = true;
				}

				int slots = 0;
				if (loot.LootSpawnSlots.Length > 0)
				{
					LootContainer.LootSpawnSlot[] lootSpawnSlots = loot.LootSpawnSlots;
					for (int i = 0; i < lootSpawnSlots.Length; i++)
						slots += lootSpawnSlots[i].numberToSpawn;
				}
				else
					slots = loot.maxDefinitionsToSpawn;
				if (!container.ContainsKey("MaxBPs"))
				{
					container.Add("MaxBPs", 1);
					wasAdded = true;
				}
				if (!container.ContainsKey("ItemsMin"))
				{
					container.Add("ItemsMin", slots);
					wasAdded = true;
				}
				if (!container.ContainsKey("ItemsMax"))
				{
					container.Add("ItemsMax", slots);
					wasAdded = true;
				}
				if (!container.ContainsKey("ItemsMax"))
				{
					container.Add("ItemsMax", slots);
					wasAdded = true;
				}
				if (!container.ContainsKey("ItemList"))
				{
					var itemList = new Dictionary<string, object>();
					if (loot.lootDefinition != null)
						GetLootSpawn(loot.lootDefinition, ref itemList);
					else if(loot.LootSpawnSlots.Length > 0)
					{
						LootContainer.LootSpawnSlot[] lootSpawnSlots = loot.LootSpawnSlots;
						for (int i = 0; i < lootSpawnSlots.Length; i++)
						{
							LootContainer.LootSpawnSlot lootSpawnSlot = lootSpawnSlots[i];
							GetLootSpawn(lootSpawnSlot.definition, ref itemList);
						}
					}
					container.Add("ItemList", itemList);
					wasAdded = true;
				}
				Items.Add(lootTable.Key, new List<string>[5]);
				Blueprints.Add(lootTable.Key, new List<string>[5]);
				for (var i = 0; i < 5; ++i)
				{
                    Items[lootTable.Key][i] = new List<string>();
					Blueprints[lootTable.Key][i] = new List<string>();
				}
				foreach (var itemEntry in container["ItemList"] as Dictionary<string, object>)
				{
					bool isBP = itemEntry.Key.EndsWith(".blueprint") ? true : false;
					var def = ItemManager.FindItemDefinition(itemEntry.Key.Replace(".blueprint", ""));

					if (def != null)
					{
						if (isBP && def.Blueprint != null && def.Blueprint.isResearchable)
						{
							int index = (int)def.rarity;
								if (!Blueprints[lootTable.Key][index].Contains(def.shortname))
									Blueprints[lootTable.Key][index].Add(def.shortname);
						}
						else
						{
							int index = 0;
							object indexoverride;
							if (rarityItemOverride.TryGetValue(def.shortname, out indexoverride))
								index = Convert.ToInt32(indexoverride);
							else
								index = (int)def.rarity;
							if (!Items[lootTable.Key][index].Contains(def.shortname))
								Items[lootTable.Key][index].Add(def.shortname);
						}
					}
				}
				totalItemWeight.Add(lootTable.Key, 0);
                totalBlueprintWeight.Add(lootTable.Key, 0);
				itemWeights.Add(lootTable.Key, new int[5]);
				blueprintWeights.Add(lootTable.Key, new int[5]);
				for (var i = 0; i < 5; ++i)
				{
                    totalItemWeight[lootTable.Key] += (itemWeights[lootTable.Key][i] = ItemWeight(baseItemRarity, i) * Items[lootTable.Key][i].Count);
					totalBlueprintWeight[lootTable.Key] += (blueprintWeights[lootTable.Key][i] = ItemWeight(baseItemRarity, i) * Blueprints[lootTable.Key][i].Count);
				}

			}
			if (wasAdded || wasRemoved)
			{
				lootTable.Set("LootTables", lootTables);
				lootTable.Save();
			}
			lootTable.Clear();
			Puts($"Using '{activeTypes}' active of '{lootTables.Count}' supported containertypes");
        }

		int ItemWeight(double baseRarity, int index) { return (int)(Math.Pow(baseRarity, 4 - index) * 1000); }

		void GetLootSpawn(LootSpawn lootSpawn, ref Dictionary<string, object> items)
        {
			if (lootSpawn.subSpawn != null && lootSpawn.subSpawn.Length > 0)
            {
				foreach (var entry in lootSpawn.subSpawn)
                    GetLootSpawn(entry.category, ref items);
                return;
            }
			if (lootSpawn.items != null && lootSpawn.items.Length > 0)
            {
				foreach (var amount in lootSpawn.items)
                {
					object options = GetAmounts(amount, 1);
					string itemName = amount.itemDef.shortname;
					if (amount.itemDef.spawnAsBlueprint)
						itemName += ".blueprint";
					if (!items.ContainsKey(itemName))
						items.Add(itemName, options);
                }
            }
        }

		object GetAmounts(ItemAmount amount, int mul = 1)
		{
			if (amount.itemDef.isWearable || (amount.itemDef.condition.enabled && amount.itemDef.GetComponent<ItemModDeployable>() == null))
				mul = 1;
			object options = new Dictionary<string, object>
			{
				["Min"] = (int)amount.amount * mul,
				["Max"] = (amount as ItemAmountRanged).maxAmount > 0f && (amount as ItemAmountRanged).maxAmount > amount.amount ? (int)(amount as ItemAmountRanged).maxAmount * mul : (int)amount.amount * mul
			};
			return options;
		}

		static Dictionary<string,object> defaultItemOverride()
		{
			var dp = new Dictionary<string, object>();
			dp.Add("autoturret", 4);
			dp.Add("lmg.m249", 4);
			dp.Add("targeting.computer", 3);
			return dp;
		}

		double baseItemRarity;
		double blueprintProbability;
		bool removeStackedContainers;
		bool listUpdatesOnLoaded;
		Dictionary<string,object> rarityItemOverride = null;
		List<object> lootPrefabsToUse = null;

		object GetConfig(string menu, string datavalue, object defaultValue)
		{
			var data = Config[menu] as Dictionary<string, object>;
			if (data == null)
			{
				data = new Dictionary<string, object>();
				Config[menu] = data;
				Changed = true;
			}
			object value;
			if (!data.TryGetValue(datavalue, out value))
			{
				value = defaultValue;
				data[datavalue] = value;
				Changed = true;
			}
			return value;
		}

		void LoadVariables()
		{
			baseItemRarity = 2;
			rarityItemOverride = (Dictionary<string, object>)GetConfig("Rarity", "Override", defaultItemOverride());
			lootPrefabsToUse = (List<object>)GetConfig("Generic", "WatchedPrefabs", lootPrefabDefaults());
			listUpdatesOnLoaded = Convert.ToBoolean(GetConfig("Generic", "listUpdatesOnLoaded", true));
			removeStackedContainers = Convert.ToBoolean(GetConfig("Generic", "removeStackedContainers", true));
			blueprintProbability = Convert.ToDouble(GetConfig("Generic", "blueprintProbability", 0.11));

			if (!Changed) return;
			SaveConfig();
			Changed = false;
		}

		protected override void LoadDefaultConfig()
		{
			Config.Clear();
			LoadVariables();
		}

		void Init()
		{
			LoadVariables();
		}

		void OnServerInitialized()
		{
			ItemManager.Initialize();
			LoadAllContainers();
			UpdateInternals(listUpdatesOnLoaded);
		}

		void Unload() {}

		void UpdateInternals(bool doLog)
		{
			SaveExportNames();
			if (Changed)
			{
				SaveConfig();
				Changed = false;
			}
			Puts("Updating internals...");
			populatedContainers = 0;
			NextTick( () =>
			{
				if (removeStackedContainers)
					FixLoot();
				foreach (var container in BaseNetworkable.serverEntities.Where(p => p != null &&  p.GetComponent<BaseEntity>() != null && p  is LootContainer).Cast<LootContainer>().ToList())
				{
					if (container == null)
						continue;
					if (CustomLootSpawns && (bool)CustomLootSpawns?.Call("IsLootBox", container.GetComponent<BaseEntity>()))
						continue;
					if (PopulateContainer(container))
						populatedContainers++;
				}


				Puts($"Populated '{populatedContainers}' supported containers.");
				initialized = true;
				populatedContainers = 0;
				ItemManager.DoRemoves();
			});
		}


		void FixLoot()
		{
			var spawns = Resources.FindObjectsOfTypeAll<LootContainer>().Where(c => c.isActiveAndEnabled).OrderBy(c => c.transform.position.x).ThenBy(c => c.transform.position.z).ThenBy(c => c.transform.position.z).ToList();

			var count = spawns.Count();
			var racelimit = count * count;

			var antirace = 0;
			var deleted = 0;

			for (var i = 0; i < count; i++)
			{
				var box = spawns[i];
				var pos = new Vector2(box.transform.position.x, box.transform.position.z);

				if (++antirace > racelimit)
				{
					return;
				}

				var next = i + 1;
				while (next < count)
				{
					var box2 = spawns[next];
					var pos2 = new Vector2(box2.transform.position.x, box2.transform.position.z);
					var distance = Vector2.Distance(pos, pos2);

					if (++antirace > racelimit)
					{
						return;
					}

					if (distance < 0.25f)
					{
						spawns.RemoveAt(next);
						count--;
						(box2 as BaseEntity).KillMessage();
						deleted++;
					}
					else break;
				}
			}

			if (deleted > 0)
				Puts($"Removed {deleted} stacked LootContainer");
			else
				Puts($"No stacked LootContainer found.");
			ItemManager.DoRemoves();
		}

		bool PopulateContainer(LootContainer container)
		{
			Dictionary<string, object> con;
            object containerobj;
            int lootitemcount;
			if (!lootTables.TryGetValue(container.PrefabName, out containerobj))
				return false;
			con = containerobj as Dictionary<string, object>;
			if (!(bool)con["Enabled"])
				return false;
			lootitemcount = (int)(con["ItemList"] as Dictionary<string, object>).Count();
			int itemCount = Mathf.RoundToInt(UnityEngine.Random.Range(Convert.ToSingle(Mathf.Min((int)con["ItemsMin"],(int)con["ItemsMax"]))*100f, Convert.ToSingle(Mathf.Max((int)con["ItemsMin"],(int)con["ItemsMax"]))*100f) / 100f);
			if (lootitemcount > 0 && itemCount > lootitemcount && lootitemcount < 36)
				itemCount = lootitemcount;
			if (container.inventory == null)
			{
				container.inventory = new ItemContainer();
				container.inventory.ServerInitialize(null, 36);
				container.inventory.GiveUID();
			}
			else
			{
				while (container.inventory.itemList.Count > 0)
				{
					var item = container.inventory.itemList[0];
					item.RemoveFromContainer();
					item.Remove(0f);
				}
				container.inventory.capacity = 36;
			}
			var items = new List<Item>();
            var itemNames = new List<string>();
            var itemBlueprints = new List<int>();
			var maxRetry = 10;
			for (int i = 0; i < itemCount; ++i)
            {
                if (maxRetry == 0)
				{
					break;
				}
                var item = MightyRNG(container.PrefabName, itemCount, (bool)(itemBlueprints.Count >= (int)con["MaxBPs"]));

				if (item == null)
				{
					--maxRetry;
					--i;
					continue;
				}
                if (itemNames.Contains(item.info.shortname) || (item.IsBlueprint() && itemBlueprints.Contains(item.blueprintTarget)))
                {
                    item.Remove(0f);
                    --maxRetry;
                    --i;
                    continue;
                }
                else
                    if (item.IsBlueprint())
						itemBlueprints.Add(item.blueprintTarget);
					else
						itemNames.Add(item.info.shortname);
                items.Add(item);
            }
            foreach (var item in items.Where(x => x != null && x.IsValid()))
				item.MoveToContainer(container.inventory, -1, false);
			if ((int)con["Scrap"] > 0)
			{
				int scrapCount = (int)con["Scrap"];
				Item item = ItemManager.Create(ItemManager.FindItemDefinition("scrap"), scrapCount, 0uL);
				item.MoveToContainer(container.inventory, -1, false);
			}
			container.inventory.capacity = container.inventory.itemList.Count;
			container.inventory.MarkDirty();
			container.SendNetworkUpdate();
			populatedContainers++;
			return true;
		}

		Item MightyRNG(string type, int itemCount, bool blockBPs = false)
        {
			bool asBP = (rng.NextDouble() < blueprintProbability && !blockBPs);
			List<string> selectFrom;
			int limit = 0;
            string itemName;
            Item item;
            int maxRetry = 10 * itemCount;
			do
			{
                selectFrom = null;
				item = null;
				if (asBP)
				{
					var r = rng.Next(totalBlueprintWeight[type]);
					for (var i = 0; i < 5; ++i)
					{
						limit += blueprintWeights[type][i];
						if (r < limit)
						{
							selectFrom = Blueprints[type][i];
							break;
						}
					}
				}
				else
				{
					var r = rng.Next(totalItemWeight[type]);
					for (var i = 0; i < 5; ++i)
					{
						limit += itemWeights[type][i];
						if (r < limit)
						{
							selectFrom = Items[type][i];
							break;
						}
					}
				}
                if (selectFrom == null)
                {
                    if (--maxRetry <= 0)
						break;
                    continue;
                }
				itemName = selectFrom[rng.Next(0, selectFrom.Count)];
				ItemDefinition itemDef =  ItemManager.FindItemDefinition(itemName);
				if (asBP && itemDef.Blueprint != null && itemDef.Blueprint.isResearchable)
				{
					var blueprintBaseDef = ItemManager.FindItemDefinition("blueprintbase");
					item = ItemManager.Create(blueprintBaseDef, 1, 0uL);
					item.blueprintTarget = itemDef.itemid;
				}
				else
					item = ItemManager.CreateByName(itemName, 1);
				if (item == null || item.info == null)
					continue;
                break;
            } while (true);
			if (item == null)
				return null;
			object itemLimit;
            if ( ((lootTables[type] as Dictionary<string, object>)["ItemList"] as Dictionary<string, object>).TryGetValue(item.info.shortname, out itemLimit))
			{
				Dictionary<string, object> limits = itemLimit as Dictionary<string, object>;
				item.amount = UnityEngine.Random.Range(Math.Min((int)limits["Min"],(int)limits["Max"]), Math.Max((int)limits["Min"],(int)limits["Max"]));
            }
			item.OnVirginSpawn();
			return item;
        }

		object OnLootSpawn(LootContainer container)
		{
			if (!initialized || container == null)
				return null;
			if (CustomLootSpawns && (bool)CustomLootSpawns?.Call("IsLootBox", container.GetComponent<BaseEntity>()))
				return null;
			if (PopulateContainer(container))
			{
				ItemManager.DoRemoves();
				return true;
			}
			return null;
		}

		static int RarityIndex(Rarity rarity)
		{
			switch (rarity) {
				case Rarity.None: return 0;
				case Rarity.Common: return 1;
				case Rarity.Uncommon: return 2;
				case Rarity.Rare: return 3;
				case Rarity.VeryRare: return 4;
			}
			return -1;
		}

		bool ItemExists(string name)
		{
			foreach (var def in ItemManager.itemList) {
				if (def.shortname != name)
					continue;
				var testItem = ItemManager.CreateByName(name, 1);
				if (testItem != null) {
					testItem.Remove(0f);
					return true;
				}
			}
			return false;
		}

		bool isSupplyDropActive()
		{
			Dictionary<string, object> con;
            object containerobj;
			if (!lootTables.TryGetValue("assets/prefabs/misc/supply drop/supply_drop.prefab", out containerobj))
				return false;
			con = containerobj as Dictionary<string, object>;
			if ((bool)con["Enabled"])
				return true;
			return false;
		}

		class StoredExportNames
		{
			public int version;
			public Dictionary<string, string> AllItemsAvailable = new Dictionary<string, string>();
			public StoredExportNames()
			{
			}
		}

		void SaveExportNames()
		{
			storedExportNames = Interface.GetMod().DataFileSystem.ReadObject<StoredExportNames>("BetterLoot\\NamesList");
			if (storedExportNames.AllItemsAvailable.Count == 0 || (int)storedExportNames.version != Rust.Protocol.network)
			{
				storedExportNames = new StoredExportNames();
				var exportItems = new List<ItemDefinition>(ItemManager.itemList);
				storedExportNames.version = Rust.Protocol.network;
				foreach(var it in exportItems)
					storedExportNames.AllItemsAvailable.Add(it.shortname, it.displayName.english);
				Interface.GetMod().DataFileSystem.WriteObject("BetterLoot\\NamesList", storedExportNames);
				Puts($"Exported {storedExportNames.AllItemsAvailable.Count} items to 'NamesList'");
			}
		}
	}
}

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