C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Facepunch.Extend;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Plugins.LootManagerExtensionMethods;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Random = UnityEngine.Random;
namespace Oxide.Plugins {
[Info("LootManager", "Adem", "1.0.3")]
internal class LootManager : RustPlugin {
#region Variables
[PluginReference]
Plugin AlphaLoot, CustomLoot;
private static LootManager _ins;
private readonly HashSet<string> _subscribeMethods = new HashSet<string> {
"OnCrateSpawned", "OnCorpsePopulate", "OnEntityKill", "OnContainerPopulate", "CanPopulateLoot"
}
;
private ItemDefinition _bluePrintItemDefinition;
private readonly Dictionary<string, string> _imageIDs = new Dictionary<string, string>();
private readonly Dictionary<string, LootTableData> _lootTables = new Dictionary<string, LootTableData>();
private readonly Dictionary<ulong, EditingLootTableData> _editingLootTables = new Dictionary<ulong, EditingLootTableData>();
private readonly Dictionary<ulong, string> _crates = new Dictionary<ulong, string>();
private readonly Dictionary<ulong, string> _npcs = new Dictionary<ulong, string>();
private readonly Dictionary<ulong, string> _corpses = new Dictionary<ulong, string>();
private readonly Dictionary<ulong, string> _bradleys = new Dictionary<ulong, string>();
private readonly Dictionary<ulong, string> _helies = new Dictionary<ulong, string>();
#endregion Variables
#region Api
private HashSet<string> GetMissingLootTables(HashSet<string> lootTableNames) {
HashSet<string> result = lootTableNames.Where(x => !string.IsNullOrEmpty(x) && !_lootTables.ContainsKey(x));
if (result.Count == 0) return result;
LoadAllLootTables();
result = lootTableNames.Where(x => !string.IsNullOrEmpty(x) && !_lootTables.ContainsKey(x));
return result;
}
private string TryAddLootTable(string lootTablePreset, string pluginName, JObject lootTableJson, bool returnSame, HashSet<string> pluginFilters, string lootTableNameFilter) {
LootTableData lootTableData = lootTableJson.ToObject<LootTableData>();
if (lootTableData == null) return String.Empty;
lootTableData.CheckAndUpdateValues();
if (returnSame) {
KeyValuePair<string, LootTableData> identicalLootTable = _lootTables.FirstOrDefault(x => (string.IsNullOrEmpty(lootTableNameFilter) || x.Key.Contains(lootTableNameFilter)) && (pluginFilters.IsNullOrEmpty() || pluginFilters.Any(y => x.Value.Description.Contains(y))) && x.Value.DeepEquals(lootTableData));
if (!string.IsNullOrEmpty(identicalLootTable.Key)) return identicalLootTable.Key;
}
lootTablePreset = GetNameForNewLootTable(lootTablePreset);
if (_lootTables.TryAdd(lootTablePreset, lootTableData)) SaveLootTable(lootTablePreset, lootTableData);
return lootTablePreset;
}
private void AddCrate(StorageContainer storageContainer, string lootTableName) {
if (string.IsNullOrEmpty(lootTableName) || storageContainer == null || storageContainer.net == null || storageContainer.inventory == null) return;
if (!_lootTables.TryGetValue(lootTableName, out LootTableData lootTableData)) return;
LootContainer lootContainer = storageContainer as LootContainer;
if (lootContainer != null) {
LootController.UpdateLootContainer(lootContainer, lootTableData);
return;
}
LootController.FillContainer(storageContainer.inventory, lootTableData);
}
private void AddCrate(Fridge fridge, string lootTableName) {
if (string.IsNullOrEmpty(lootTableName) || fridge == null || fridge.net == null) return;
if (!_lootTables.TryGetValue(lootTableName, out LootTableData lootTableData)) return;
LootController.UpdateFridge(fridge, lootTableData);
}
private void AddCrate(DroppedItemContainer droppedItemContainer, string lootTableName) {
if (string.IsNullOrEmpty(lootTableName) || droppedItemContainer == null || droppedItemContainer.net == null) return;
if (!_lootTables.TryGetValue(lootTableName, out LootTableData lootTableData)) return;
LootController.FillContainer(droppedItemContainer.inventory, lootTableData);
}
private void AddNpc(ulong netId, string lootTableName) {
if (string.IsNullOrEmpty(lootTableName) || !_lootTables.ContainsKey(lootTableName)) return;
_npcs.TryAdd(netId, lootTableName);
}
private void AddBradley(ulong netId, string lootTableName) {
if (string.IsNullOrEmpty(lootTableName) || !_lootTables.ContainsKey(lootTableName)) return;
_bradleys.TryAdd(netId, lootTableName);
}
private void AddHeli(ulong netId, string lootTableName) {
if (string.IsNullOrEmpty(lootTableName) || !_lootTables.ContainsKey(lootTableName)) return;
_helies.TryAdd(netId, lootTableName);
}
#endregion Api
#region Hooks
private void Init() {
Unsubscribes();
}
private void OnServerInitialized() {
_ins = this;
RegisterPermissions();
LoadDefaultMessages();
ImageLoader.LoadImages();
LoadAllLootTables();
PrefabController.CachePrefabs();
_bluePrintItemDefinition = ItemManager.FindItemDefinition("blueprintbase");
Subscribes();
}
private void Unload() {
DestroyGuiForAllPlayers();
}
private void OnCrateSpawned(BradleyAPC bradleyApc, LockedByEntCrate crate) {
if (bradleyApc == null || bradleyApc.net == null || crate == null) return;
if (!_bradleys.TryGetValue(bradleyApc.net.ID.Value, out string lootTableName)) return;
LootController.UpdateLootContainer(crate, lootTableName);
}
private void OnCrateSpawned(PatrolHelicopter patrolHelicopter, LockedByEntCrate crate) {
if (patrolHelicopter == null || patrolHelicopter.net == null || crate == null) return;
if (!_helies.TryGetValue(patrolHelicopter.net.ID.Value, out string lootTableName)) return;
LootController.UpdateLootContainer(crate, lootTableName);
}
private void OnCorpsePopulate(ScientistNPC scientistNpc, NPCPlayerCorpse corpse) {
if (scientistNpc == null || corpse == null || scientistNpc.net == null || corpse.net == null) return;
if (!_npcs.TryGetValue(scientistNpc.net.ID.Value, out string lootTableName)) return;
LootController.UpdateNpc(corpse, lootTableName);
_corpses.TryAdd(corpse.net.ID.Value, lootTableName);
}
private void OnEntityKill(LootContainer lootContainer) {
if (lootContainer == null || lootContainer.net == null) return;
_crates.Remove(lootContainer.net.ID.Value);
}
private void OnEntityKill(Fridge fridge) {
if (fridge == null || fridge.net == null) return;
_crates.Remove(fridge.net.ID.Value);
}
private void OnEntityKill(DroppedItemContainer droppedItemContainer) {
if (droppedItemContainer == null || droppedItemContainer.net == null) return;
_crates.Remove(droppedItemContainer.net.ID.Value);
}
private void OnEntityKill(BradleyAPC bradley) {
if (bradley == null || bradley.net == null) return;
_bradleys.Remove(bradley.net.ID.Value);
}
private void OnEntityKill(PatrolHelicopter heli) {
if (heli == null || heli.net == null) return;
_helies.Remove(heli.net.ID.Value);
}
private void OnEntityKill(ScientistNPC npc) {
if (npc == null || npc.net == null) return;
_npcs.Remove(npc.net.ID.Value);
}
private void OnEntityKill(NPCPlayerCorpse corpse) {
if (corpse == null || corpse.net == null) return;
_corpses.Remove(corpse.net.ID.Value);
}
#region LootTablePlugin
private object OnContainerPopulate(LootContainer lootContainer) {
if (lootContainer == null || lootContainer.net == null) return null;
return HandleOtherPluginPopulate(lootContainer.net.ID.Value, _crates, data => !data.IsLootTablePlugin);
}
private object OnCorpsePopulate(NPCPlayerCorpse corpse) {
if (corpse == null || corpse.net == null) return null;
return HandleOtherPluginPopulate(corpse.net.ID.Value, _corpses, data => !data.IsLootTablePlugin);
}
#endregion LootTablePlugin
#region AlphaLoot
private object CanPopulateLoot(LootContainer lootContainer) {
if (lootContainer == null || lootContainer.net == null) return null;
return HandleOtherPluginPopulate(lootContainer.net.ID.Value, _crates, data => !data.IsAlphaLoot || !string.IsNullOrEmpty(data.AlphaLootPreset));
}
private object CanPopulateLoot(ScientistNPC scientistNpc, NPCPlayerCorpse corpse) {
if (scientistNpc == null || scientistNpc.net == null) return null;
return HandleOtherPluginPopulate(scientistNpc.net.ID.Value, _npcs, data => !data.IsAlphaLoot || !string.IsNullOrEmpty(data.AlphaLootPreset));
}
#endregion AlphaLoot
#region CustomLoot
private object OnCustomLootContainer(NetworkableId net) {
return HandleOtherPluginPopulate(net.Value, _crates, data => !data.IsCustomLoot);
}
private object OnCustomLootNPC(NetworkableId net) {
return HandleOtherPluginPopulate(net.Value, _npcs, data => !data.IsCustomLoot);
}
#endregion CustomLoot
private static object HandleOtherPluginPopulate(ulong netId, Dictionary<ulong, string> targetDictionary, Func<LootTableData, bool> cancelFunction) {
if (!targetDictionary.TryGetValue(netId, out string presetName)) return null;
LootTableData ltd = LootController.GetDataByPresetName(presetName);
if (ltd == null) return null;
return cancelFunction(ltd) ? true : null;
}
#endregion Hooks
#region Methods
private void Unsubscribes() {
foreach (string hook in _subscribeMethods) Unsubscribe(hook);
}
private void Subscribes() {
foreach (string hook in _subscribeMethods) Subscribe(hook);
}
private static void Debug(params object[] arg) {
string result = "";
foreach (object obj in arg) result += obj.ToString() + " ";
_ins.Puts(result);
}
private void RegisterPermissions() {
permission.RegisterPermission(_config.Permission, this);
}
private bool IsPlayerHavePermission(string userIDString) {
return _ins.permission.UserHasPermission(userIDString, _ins._config.Permission);
}
private string GetNameForNewLootTable(string name) {
if (!_lootTables.ContainsKey(name)) return name;
HashSet<string> matchingNames = _lootTables.Keys.Where(x => x.Contains(name));
int number = 0;
foreach (string matchName in matchingNames) {
string fileNumberString = matchName.Split('_').Last();
if (int.TryParse(fileNumberString, out int fileNumber) && fileNumber > number) number = fileNumber;
}
number++;
return $"{name}_{number}";
}
#endregion Methods
#region Classes
private readonly Dictionary<string, PrefabLootInfo> _prefabLootTables = new Dictionary<string, PrefabLootInfo>();
private static class LootController {
public static LootTableData GetDataByPresetName(string presetName) {
return _ins._lootTables.GetValueOrDefault(presetName);
}
public static void UpdateLootContainer(LootContainer lootContainer, string lootTableName) {
if (!_ins._lootTables.TryGetValue(lootTableName, out LootTableData lootTableData)) return;
UpdateLootContainer(lootContainer, lootTableData);
}
public static void UpdateLootContainer(LootContainer lootContainer, LootTableData lootTableData) {
lootContainer.Invoke(() => {
FillContainer(lootContainer.inventory, lootTableData);
}
, 2f);
}
public static void UpdateFridge(Fridge fridge, LootTableData lootTableData) {
fridge.OnlyAcceptCategory = ItemCategory.All;
FillContainer(fridge.inventory, lootTableData);
}
public static void UpdateNpc(NPCPlayerCorpse corpse, string lootTableName) {
if (!_ins._lootTables.TryGetValue(lootTableName, out LootTableData lootTableData)) return;
if (corpse.containers.IsNullOrEmpty() || corpse.containers[0] == null) return;
ItemContainer container = corpse.containers[0];
_ins.NextTick(() => {
if (container != null) FillContainer(container, lootTableData);
}
);
}
public static void FillContainer(ItemContainer container, LootTableData lootTableData) {
if (lootTableData.ClearDefaultItems) ClearContainer(container);
if (lootTableData.UseItemList) {
if (lootTableData.UseMinMaxForItems) {
int itemsAmount = Random.Range(lootTableData.MinItemsAmount, lootTableData.MaxItemsAmount + 1);
HashSet<ItemData> itemsForSpawn = GetElementsForSpawn(lootTableData.Items, itemsAmount);
if (itemsForSpawn.Count > 0) ItemListController.FillContainer(container, itemsForSpawn);
}
else if (lootTableData.Items.Count > 0) {
ItemListController.FillContainerWithoutMinMax(container, lootTableData.Items);
}
}
if (lootTableData.UsePrefabList) {
int prefabsAmount = Random.Range(lootTableData.MinPrefabsAmount, lootTableData.MaxPrefabsAmount + 1);
HashSet<PrefabData> prefabsForSpawn = GetElementsForSpawn(lootTableData.Prefabs, prefabsAmount);
PrefabController.FillContainer(container, prefabsForSpawn);
}
if (!string.IsNullOrEmpty(lootTableData.AlphaLootPreset)) {
if (_ins.plugins.Exists("AlphaLoot") && (bool)_ins.AlphaLoot.Call("ProfileExists", lootTableData.AlphaLootPreset)) {
_ins.AlphaLoot.Call("PopulateLoot", container, lootTableData.AlphaLootPreset);
}
}
if (!string.IsNullOrEmpty(lootTableData.CustomLootPreset)) {
List<Item> items = (List<Item>)_ins.CustomLoot?.Call("MakeLoot", lootTableData.CustomLootPreset);
if (items != null) foreach (Item item in items) if (item == null || !item.MoveToContainer(container)) item.Remove();
}
}
private static void ClearContainer(ItemContainer container) {
for (int i = container.itemList.Count - 1; i >= 0; i--) {
Item item = container.itemList[i];
item.RemoveFromContainer();
item.Remove();
}
}
private static HashSet<T> GetElementsForSpawn<T>(List<T> elements, int targetAmount) where T : LootElementChance {
HashSet<T> result = new HashSet<T>();
if (elements.Count == 0 || targetAmount <= 0) return result;
HashSet<int> includedIndexes = new HashSet<int>();
int counter = 50;
while (result.Count < targetAmount && counter-- > 0) {
for (int i = 0; i < elements.Count && result.Count < targetAmount; i++) {
if (includedIndexes.Contains(i)) continue;
T lootElement = elements[i];
float chance = elements[i].Chance;
float roll = Random.Range(0f, 100f);
if (roll >= chance) continue;
includedIndexes.Add(i);
result.Add(lootElement);
}
}
return result;
}
}
private static class ItemListController {
public static void FillContainer(ItemContainer container, HashSet<ItemData> items) {
int itemsCount = container.itemList.Count + items.Count;
if (container.capacity < itemsCount) container.capacity = itemsCount;
foreach (ItemData itemData in items) {
Item item = CreateItem(itemData);
if (item == null || !item.MoveToContainer(container)) item.Remove();
}
}
public static void FillContainerWithoutMinMax(ItemContainer container, List<ItemData> items) {
foreach (ItemData itemData in items) {
float roll = Random.Range(0f, 100f);
if (roll >= itemData.Chance) continue;
Item item = CreateItem(itemData);
if (item == null || !item.MoveToContainer(container)) item.Remove();
}
}
private static Item CreateItem(ItemData itemData) {
int amount = Random.Range(itemData.MinAmount, itemData.MaxAmount + 1);
Item item;
if (itemData.IsBluePrint) {
item = ItemManager.CreateByName("blueprintbase");
item.blueprintTarget = itemData.ItemId;
}
else {
item = ItemManager.CreateByName(itemData.ShortName, amount, itemData.Skin);
}
if (item == null) {
_ins.PrintWarning($"Failed to create item! ({itemData.ShortName})");
return null;
}
if (!string.IsNullOrEmpty(itemData.CustomDisplayName)) item.name = itemData.CustomDisplayName;
if (!string.IsNullOrEmpty(itemData.Genomes)) {
string[] genomes = itemData.Genomes.Replace(" ", "").Split(",");
string genome = genomes.GetRandom();
UpdateGenome(item, genome);
}
return item;
}
private static void UpdateGenome(Item item, string genome) {
if (genome.Length != 6) return;
GrowableGenes growableGenes = new GrowableGenes();
for (int i = 0; i < 6 && i < genome.Length; ++i) {
GrowableGenetics.GeneType geneType = GrowableGenetics.GeneType.Empty;
switch (genome[i]) {
case 'g' : geneType = GrowableGenetics.GeneType.GrowthSpeed;
break;
case 'y' : geneType = GrowableGenetics.GeneType.Yield;
break;
case 'h' : geneType = GrowableGenetics.GeneType.Hardiness;
break;
case 'w' : geneType = GrowableGenetics.GeneType.WaterRequirement;
break;
}
growableGenes.Genes[i].Set(geneType, true);
}
GrowableGeneEncoding.EncodeGenesToItem(GrowableGeneEncoding.EncodeGenesToInt(growableGenes), item);
}
}
private static class PrefabController {
public static void CachePrefabs() {
foreach (LootTableData lootTableData in _ins._lootTables.Values) {
if (!lootTableData.UsePrefabList) continue;
foreach (PrefabData prefabData in lootTableData.Prefabs) CachePrefab(prefabData);
}
}
public static void CachePrefab(PrefabData prefabData) {
if (_ins._prefabLootTables.ContainsKey(prefabData.PrefabName)) return;
GameObject gameObject = GameManager.server.FindPrefab(prefabData.PrefabName);
if (gameObject == null) return;
LootContainer lootContainer = gameObject.GetComponent<LootContainer>();
if (lootContainer != null) {
SavePrefabLootInfo(prefabData.PrefabName, lootContainer.LootSpawnSlots, lootContainer.scrapAmount, lootContainer.lootDefinition, lootContainer.maxDefinitionsToSpawn);
return;
}
global : : HumanNPC humanNpc = gameObject.GetComponent<global : : HumanNPC>();
if (humanNpc != null) {
SavePrefabLootInfo(prefabData.PrefabName, humanNpc.LootSpawnSlots);
return;
}
global : : ScarecrowNPC scarecrowNpc = gameObject.GetComponent<global : : ScarecrowNPC>();
if (scarecrowNpc != null) {
SavePrefabLootInfo(prefabData.PrefabName, scarecrowNpc.LootSpawnSlots);
}
}
private static void SavePrefabLootInfo(string prefabName, LootContainer.LootSpawnSlot[] lootSpawnSlot, int scrapAmount = 0, LootSpawn lootDefinition = null, int maxDefinitionsToSpawn = 0) {
PrefabLootInfo prefabLootInfo = new PrefabLootInfo {
LootSpawnSlots = lootSpawnSlot, LootDefinition = lootDefinition, MaxDefinitionsToSpawn = maxDefinitionsToSpawn, ScrapAmount = scrapAmount
}
;
_ins._prefabLootTables.TryAdd(prefabName, prefabLootInfo);
}
public static void FillContainer(ItemContainer container, HashSet<PrefabData> prefabs) {
foreach (PrefabData prefabData in prefabs) FillContainer(container, prefabData);
}
private static void FillContainer(ItemContainer container, PrefabData prefabData) {
if (!_ins._prefabLootTables.TryGetValue(prefabData.PrefabName, out PrefabLootInfo prefabLootInfo)) return;
int lootScale = Random.Range(prefabData.MinAmount, prefabData.MaxAmount + 1);
for (int i = 0; i < lootScale; i++) {
if (prefabLootInfo.LootSpawnSlots != null) {
foreach (LootContainer.LootSpawnSlot lootSpawnSlot in prefabLootInfo.LootSpawnSlots) {
if (lootSpawnSlot.eras == null || lootSpawnSlot.eras.Length == 0 || Array.IndexOf(lootSpawnSlot.eras, ConVar.Server.Era) != -1) {
for (int j = 0; j < lootSpawnSlot.numberToSpawn; j++) {
if (Random.Range(0f, 1f) <= lootSpawnSlot.probability) {
lootSpawnSlot.definition.SpawnIntoContainer(container);
}
}
}
}
}
if (prefabLootInfo.LootDefinition != null) for (int j = 0; j < prefabLootInfo.MaxDefinitionsToSpawn; j++) prefabLootInfo.LootDefinition.SpawnIntoContainer(container);
if (prefabLootInfo.ScrapAmount > 0) {
Item item = ItemManager.CreateByName("scrap", prefabLootInfo.ScrapAmount);
if (!item.MoveToContainer(container)) item.Remove();
}
}
}
}
private class PrefabLootInfo {
public LootContainer.LootSpawnSlot[] LootSpawnSlots;
public LootSpawn LootDefinition;
public int MaxDefinitionsToSpawn;
public int ScrapAmount;
}
private static class ImageLoader {
internal static void LoadImages() {
ServerMgr.Instance.StartCoroutine(LoadIconsCoroutine());
}
private static IEnumerator LoadIconsCoroutine() {
HashSet<ImageInfo> imageInfos = GetImagesInfosForLoad();
char separator = Path.DirectorySeparatorChar;
string path = $"file://{Interface.Oxide.DataDirectory}{separator}{_ins.Name}{separator}Images{separator}";
foreach (ImageInfo imageInfo in imageInfos) {
string url = path + $"{imageInfo.FolderName}{separator}" + imageInfo.ImageName + ".png";
using UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture(url);
yield return unityWebRequest.SendWebRequest();
if (unityWebRequest.result != UnityWebRequest.Result.Success) {
OnImageSaveFailed(imageInfo.ImageName);
break;
}
Texture2D texture = DownloadHandlerTexture.GetContent(unityWebRequest);
uint imageId = FileStorage.server.Store(texture.EncodeToPNG(), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
_ins._imageIDs.Add(imageInfo.ImageName, imageId.ToString());
UnityEngine.Object.DestroyImmediate(texture);
}
}
private static HashSet<ImageInfo> GetImagesInfosForLoad() {
HashSet<ImageInfo> result = new HashSet<ImageInfo> {
new ImageInfo("Plus_Icon", "Icons"), new ImageInfo("Lock_Icon", "Icons"), new ImageInfo("Search_Icon", "Icons"), new ImageInfo("Copy_Icon", "Icons"), new ImageInfo("Delete_Icon", "Icons"), new ImageInfo("Edit_Icon", "Icons"), new ImageInfo("Error_Icon", "Icons")
}
;
foreach (PrefabInfo crateInfo in _ins._prefabInfos) {
if (result.Any(x => x.ImageName == crateInfo.ImageName)) continue;
result.Add(new ImageInfo(crateInfo.ImageName, "Prefabs"));
}
return result;
}
private static void OnImageSaveFailed(string imageName) {
_ins.PrintError($"Image {imageName} was not found. Maybe you didn't upload it to the .../oxide/data/{_ins.Name}/Images/ folder");
Interface.Oxide.UnloadPlugin(_ins.Name);
}
private class ImageInfo {
public readonly string ImageName;
public readonly string FolderName;
internal ImageInfo(string imageName, string imageFolder) {
ImageName = imageName;
FolderName = imageFolder;
}
}
}
#endregion Classes
#region Data
private readonly HashSet<PrefabInfo> _prefabInfos = new HashSet<PrefabInfo> {
new ("assets/bundled/prefabs/autospawn/resource/loot/loot-barrel-1.prefab", "loot-barrel-1", "Yellow Loot Barrel"), new ("assets/bundled/prefabs/autospawn/resource/loot/loot-barrel-2.prefab", "loot-barrel-2", "Blue Loot Barrel"), new ("assets/content/structures/excavator/prefabs/diesel_collectable.prefab", "diesel_collectable", "Diesel Fuel"), new ("assets/bundled/prefabs/radtown/oil_barrel.prefab", "oil_barrel", "Oil Barrel"), new ("assets/prefabs/misc/xmas/giftbox/giftbox_loot.prefab", "giftbox_loot", "Present"), new ("assets/bundled/prefabs/radtown/crate_basic.prefab", "crate_basic", "Basic Crate"), new ("assets/bundled/prefabs/radtown/crate_basic_jungle.prefab", "crate_basic_jungle", "Crate Basic Jungle"), new ("assets/bundled/prefabs/radtown/crate_elite.prefab", "crate_elite", "Elite Crate"), new ("assets/prefabs/npc/m2bradley/bradley_crate.prefab", "crate_elite", "Elite Crate - Bradley"), new ("assets/prefabs/npc/patrol helicopter/heli_crate.prefab", "crate_elite", "Elite Crate - Heli"), new ("assets/prefabs/misc/food cache/food_cache_001.prefab", "food_cache_001", "Food Cache"), new ("assets/bundled/prefabs/radtown/foodbox.prefab", "foodbox", "Foodbox"), new ("assets/prefabs/deployable/chinooklockedcrate/codelockedhackablecrate.prefab", "codelockedhackablecrate", "Locked Crate"), new ("assets/prefabs/deployable/chinooklockedcrate/codelockedhackablecrate_oilrig.prefab", "codelockedhackablecrate", "Locked Crate - Oilrig"), new ("assets/bundled/prefabs/radtown/crate_normal.prefab", "crate_normal", "Military Crate"), new ("assets/bundled/prefabs/radtown/crate_normal_2.prefab", "crate_normal_2", "Normal Crate"), new ("assets/bundled/prefabs/radtown/crate_mine.prefab", "crate_normal_2", "Normal Crate - Cave"), new ("assets/bundled/prefabs/radtown/crate_normal_2_food.prefab", "crate_normal_2_food", "Normal Crate - Food"), new ("assets/bundled/prefabs/radtown/crate_normal_2_medical.prefab", "crate_normal_2_medical", "Normal Crate - Medical"), new ("assets/prefabs/misc/supply drop/supply_drop.prefab", "supply_drop", "Supply Drop"), new ("assets/bundled/prefabs/radtown/crate_tools.prefab", "crate_tools", "Tools Crate"), new ("assets/bundled/prefabs/radtown/crate_underwater_advanced.prefab", "crate_underwater_advanced", "Underwater Advanced Crate"), new ("assets/bundled/prefabs/radtown/crate_underwater_basic.prefab", "crate_basic", "Underwater Basic Crate"), new ("assets/bundled/prefabs/radtown/underwater_labs/crate_ammunition.prefab", "crate_ammunition", "Underwater Lab - Ammunition Crate"), new ("assets/bundled/prefabs/radtown/underwater_labs/crate_food_1.prefab", "crate_food_1", "Underwater Lab - Food Crate"), new ("assets/bundled/prefabs/radtown/underwater_labs/crate_fuel.prefab", "crate_fuel", "Underwater Lab - Fuel Crate"), new ("assets/bundled/prefabs/radtown/underwater_labs/crate_medical.prefab", "crate_medical", "Underwater Lab - Medical Crate"), new ("assets/bundled/prefabs/radtown/underwater_labs/tech_parts_1.prefab", "tech_parts_1", "Underwater Lab - Tier 2 Components"), new ("assets/bundled/prefabs/radtown/underwater_labs/tech_parts_2.prefab", "tech_parts_2", "Underwater Lab - Tier 3 Components"), new ("assets/bundled/prefabs/radtown/underwater_labs/vehicle_parts.prefab", "vehicle_parts", "Vehicle Parts"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_junkpile_pistol.prefab", "scientistnpc", "Road Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_excavator.prefab", "scientistnpc", "Monument Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_bradley_heavy.prefab", "scientistnpc_heavy", "Heavy Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam_nvg_variant.prefab", "scientistnpc_tunnel", "Missile Silo Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_shotgun.prefab", "scientistnpc_tunnel", "Military Tunnel Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/tunneldweller/npc_tunneldweller.prefab", "npc_tunneldweller", "Tunnel/ Underwater Dweller"), new ("assets/prefabs/npc/gingerbread/gingerbread_dungeon.prefab", "gingerbread_dungeon", "Gingerbread"), new ("assets/prefabs/npc/scarecrow/scarecrow_dungeon.prefab", "scarecrow_dungeon", "Scarecrow"),
}
;
private class PrefabInfo {
public readonly string PrefabName;
public readonly string ShortPrefabName;
public readonly string Name;
public readonly string ImageName;
public PrefabInfo(string prefabName, string imageName, string name) {
PrefabName = prefabName;
ImageName = imageName;
ShortPrefabName = PrefabName.Split('/').Last().Split('.').First();
Name = name;
}
}
private class EditingLootTableData : LootTableData {
public string PresetName;
public string OldPresetName = string.Empty;
public EditingLootTableData (string presetName, LootTableData lootTableData) : base(lootTableData) {
PresetName = presetName;
}
}
public class LootTableData {
public string Description;
public bool ClearDefaultItems;
public bool IsAlphaLoot;
public string AlphaLootPreset = string.Empty;
public bool IsCustomLoot;
public string CustomLootPreset = string.Empty;
public bool IsLootTablePlugin;
public string LootTablePluginLootPreset = string.Empty;
public bool UseItemList;
public bool UseMinMaxForItems = true;
public int MinItemsAmount;
public int MaxItemsAmount;
public List<ItemData> Items = new List<ItemData>();
public bool UsePrefabList;
public int MinPrefabsAmount;
public int MaxPrefabsAmount;
public List<PrefabData> Prefabs = new List<PrefabData>();
public LootTableData() {
}
public LootTableData(LootTableData other) {
ClearDefaultItems = other.ClearDefaultItems;
Description = other.Description;
IsAlphaLoot = other.IsAlphaLoot;
AlphaLootPreset = other.AlphaLootPreset;
IsCustomLoot = other.IsCustomLoot;
CustomLootPreset = other.CustomLootPreset;
IsLootTablePlugin = other.IsLootTablePlugin;
LootTablePluginLootPreset = other.LootTablePluginLootPreset;
UseItemList = other.UseItemList;
UseMinMaxForItems = other.UseMinMaxForItems;
MinItemsAmount = other.MinItemsAmount;
MaxItemsAmount = other.MaxItemsAmount;
Items = other.Items.Select(i => i.Clone()).ToList();
UsePrefabList = other.UsePrefabList;
MinPrefabsAmount = other.MinPrefabsAmount;
MaxPrefabsAmount = other.MaxPrefabsAmount;
Prefabs = other.Prefabs.Select(i => i.Clone()).ToList();
}
public bool DeepEquals(LootTableData other) {
if (ClearDefaultItems != other.ClearDefaultItems || IsAlphaLoot != other.IsAlphaLoot || AlphaLootPreset != other.AlphaLootPreset || IsCustomLoot != other.IsCustomLoot || CustomLootPreset != other.CustomLootPreset || IsLootTablePlugin != other.IsLootTablePlugin || LootTablePluginLootPreset != other.LootTablePluginLootPreset || UseItemList != other.UseItemList || UseMinMaxForItems != other.UseMinMaxForItems || MinItemsAmount != other.MinItemsAmount || MaxItemsAmount != other.MaxItemsAmount || UsePrefabList != other.UsePrefabList || MinPrefabsAmount != other.MinPrefabsAmount || MaxPrefabsAmount != other.MaxPrefabsAmount) return false;
if (Items.Count != other.Items.Count || Prefabs.Count != other.Prefabs.Count) return false;
for (int i = 0; i < Items.Count; i++) {
if (!Items[i].DeepEquals(other.Items[i])) return false;
}
for (int i = 0; i < Prefabs.Count; i++) {
if (!Prefabs[i].DeepEquals(other.Prefabs[i])) return false;
}
return true;
}
public void CheckAndUpdateValues() {
Items = Items.OrderBy(x => x.Chance);
Prefabs = Prefabs.OrderBy(x => x.Chance);
foreach (ItemData itemData in Items) itemData.CheckAndUpdateValues();
foreach (PrefabData prefabData in Prefabs) prefabData.CheckAndUpdateValues();
int countOfGoodItems = Items.Where(x => x.Chance >= 0.1f).Count;
MinItemsAmount = Mathf.Clamp(MinItemsAmount, 0, countOfGoodItems);
MaxItemsAmount = Mathf.Clamp(MaxItemsAmount, 0, countOfGoodItems);
int countOfGoodPrefabs = Prefabs.Where(x => x.Chance >= 0.1f).Count;
MinPrefabsAmount = Mathf.Clamp(MinPrefabsAmount, 0, countOfGoodPrefabs);
MaxPrefabsAmount = Mathf.Clamp(MaxPrefabsAmount, 0, countOfGoodPrefabs);
if (UsePrefabList) foreach (PrefabData prefabData in Prefabs) PrefabController.CachePrefab(prefabData);
if (ClearDefaultItems) {
if (IsAlphaLoot && string.IsNullOrEmpty(AlphaLootPreset) || IsCustomLoot && string.IsNullOrEmpty(CustomLootPreset) || IsLootTablePlugin && string.IsNullOrEmpty(LootTablePluginLootPreset)) ClearDefaultItems = false;
}
}
}
public class ItemData : LootElementChance {
public string ShortName;
public int ItemId;
public string CustomDisplayName;
public string DefaultDisplayName;
public string OwnerDisplayName;
public ulong Skin;
public bool IsBluePrint;
public string Genomes;
private ItemData() {
}
public static ItemData CreateDefault(string shortName, int itemId, string defaultDisplayName) {
return new ItemData {
ShortName = shortName, ItemId = itemId, CustomDisplayName = string.Empty, DefaultDisplayName = defaultDisplayName, OwnerDisplayName = string.Empty, Skin = 0, IsBluePrint = false, MinAmount = 1, MaxAmount = 1, Chance = 10f, Genomes = string.Empty
}
;
}
public void CheckAndUpdateValues() {
if (MinAmount > MaxAmount) MinAmount = MaxAmount;
if (MaxAmount < MinAmount) MaxAmount = MinAmount;
Chance = Mathf.Clamp(Chance, 0, 100);
if (ItemId == 0 || string.IsNullOrEmpty(DefaultDisplayName)) {
ItemDefinition itemDefinition = ItemManager.FindItemDefinition(ShortName);
if (itemDefinition != null) {
DefaultDisplayName = itemDefinition.displayName.english.ToLower();
ItemId = itemDefinition.itemid;
}
}
UpdateGenomes();
}
private void UpdateGenomes() {
if (string.IsNullOrEmpty(Genomes)) {
Genomes = string.Empty;
return;
}
Genomes = Genomes.Replace(" ", "");
Genomes = Genomes.Replace(",", ", ");
}
public bool DeepEquals(ItemData other) {
if (other == null) return false;
return ShortName == other.ShortName && CustomDisplayName == other.CustomDisplayName && OwnerDisplayName == other.OwnerDisplayName && Skin == other.Skin && IsBluePrint == other.IsBluePrint && MinAmount == other.MinAmount && MaxAmount == other.MaxAmount && Math.Abs(Chance - other.Chance) < 0.0001f && Genomes == other.Genomes;
}
public ItemData Clone() {
return new ItemData() {
ShortName = ShortName, ItemId = ItemId, CustomDisplayName = CustomDisplayName, DefaultDisplayName = DefaultDisplayName, OwnerDisplayName = OwnerDisplayName, Skin = Skin, IsBluePrint = IsBluePrint, MinAmount = MinAmount, MaxAmount = MaxAmount, Chance = Chance, Genomes = Genomes
}
;
}
}
public class PrefabData : LootElementChance {
public string PrefabName;
public string ShortPrefabName;
private PrefabData() {
}
public PrefabData(string prefabName, string shortPrefabName) {
PrefabName = prefabName;
ShortPrefabName = shortPrefabName;
MinAmount = 1;
MaxAmount = 1;
Chance = 10f;
}
public bool DeepEquals(PrefabData other) {
if (other == null) return false;
return PrefabName == other.PrefabName && MinAmount == other.MinAmount && MaxAmount == other.MaxAmount && Math.Abs(Chance - other.Chance) < 0.0001f;
}
public void CheckAndUpdateValues() {
if (MinAmount > MaxAmount) MinAmount = MaxAmount;
if (MaxAmount < MinAmount) MaxAmount = MinAmount;
Chance = Mathf.Clamp(Chance, 0, 100);
if (string.IsNullOrEmpty(ShortPrefabName)) ShortPrefabName = PrefabName.Split('/').Last().Replace(".prefab", string.Empty);
}
public PrefabData Clone() {
return new PrefabData() {
PrefabName = PrefabName, ShortPrefabName = ShortPrefabName, MinAmount = MinAmount, MaxAmount = MaxAmount, Chance = Chance
}
;
}
}
public class LootElementChance {
public int MinAmount;
public int MaxAmount;
public float Chance;
}
private static void DeleteLootTable(string presetName) {
string path = $"{_ins.Name}/LootTables/{presetName}";
Interface.Oxide.DataFileSystem.DeleteDataFile(path);
}
private void LoadAllLootTables() {
string path = $"{_ins.Name}/LootTables/";
foreach (string name in Interface.Oxide.DataFileSystem.GetFiles(path)) {
string fileName = name.Split('/').Last().Split('.')[0].Replace(" ", "");
if (_lootTables.ContainsKey(fileName)) continue;
LootTableData lootTableData = LoadLootTable(fileName);
if (lootTableData == null) {
PrintError($"File {fileName} is corrupted and cannot be loaded!");
}
else {
lootTableData.CheckAndUpdateValues();
_lootTables.TryAdd(fileName, lootTableData);
}
}
}
private static LootTableData LoadLootTable(string fileName) {
string path = $"{_ins.Name}/LootTables/{fileName}";
LootTableData lootTableData = Interface.Oxide.DataFileSystem.ReadObject<LootTableData>(path);
return lootTableData;
}
private void SaveLootTable(string presetName, LootTableData lootTableData) {
string path = $"{_ins.Name}/LootTables/{presetName}";
Interface.Oxide.DataFileSystem.WriteObject(path, lootTableData);
}
#endregion Data
#region GUI
private const string BackgroundName = "LootManager_Background";
private const string ErrorBackgroundName = "LootManager_ErrorBackground";
private string GetStringFromArgs(string[] args, int startIndex) {
string result = "";
for (int i = startIndex; i < args.Length; i++) {
if (i > startIndex) result += " ";
string word = args[i];
result += word;
}
return result;
}
[ChatCommand("lm")]
private void OpenChatCommand(BasePlayer player, string command, string[] arg) {
if (!IsPlayerHavePermission(player.UserIDString)) {
PrintToChat(player, $"[{Name}] You don’t have permission!");
return;
}
CreateGui(player);
}
private void CreateGui(BasePlayer player) {
DestroyGuiForPlayer(player);
CreateBackground(player);
TableListPanelGUI.OpenTablesList(player, string.Empty, 0);
}
private void CreateBackground(BasePlayer player) {
DestroyGuiForPlayer(player);
CuiElementContainer container = new CuiElementContainer();
CuiPanel cuiPanel = new CuiPanel() {
Image = {
Color = "0 0 0 0.6", Material = "assets/content/ui/uibackgroundblur.mat"
}
, RectTransform = {
AnchorMin = "0 0", AnchorMax = "1 1"
}
, CursorEnabled = true, KeyboardEnabled = true
}
;
container.Add(cuiPanel, "Overlay", BackgroundName);
CuiHelper.AddUi(player, container);
}
private static void OnPageChanged(BasePlayer player) {
CuiHelper.DestroyUi(player, TableListPanelGUI.LayerName);
CuiHelper.DestroyUi(player, "PresetEdit_LeftBackground");
CuiHelper.DestroyUi(player, EditPanelGUI.RightPanel.LayerName);
}
[ConsoleCommand("Close_LootManager")]
private void CloseGUICommand(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if (player == null) return;
DestroyGuiForPlayer(player);
}
private void DestroyGuiForAllPlayers() {
foreach (BasePlayer player in BasePlayer.activePlayerList) if (player != null) DestroyGuiForPlayer(player);
}
private void DestroyGuiForPlayer(BasePlayer player) {
_editingLootTables.Remove(player.userID);
CuiHelper.DestroyUi(player, BackgroundName);
CuiHelper.DestroyUi(player, ErrorBackgroundName);
}
[ConsoleCommand("TablesList_SetPage_LootManager")]
private void TablesListEditPagePresetCommand(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if (player == null || !IsPlayerHavePermission(player.UserIDString)) return;
if (arg.Args == null || arg.Args.Length < 1) return;
int page = arg.Args[0].ToInt();
string filter = arg.Args.Length > 1 ? arg.Args[1] : String.Empty;
TableListPanelGUI.OpenTablesList(player, filter, page);
}
[ConsoleCommand("TablesList_RemovePreset_LootManager")]
private void TablesListRemoveCommand(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if (player == null || !IsPlayerHavePermission(player.UserIDString)) return;
if (arg.Args == null || arg.Args.Length < 2) return;
string preset = arg.Args[0];
int page = arg.Args[1].ToInt();
string filter = arg.Args.Length > 2 ? arg.Args[2] : string.Empty;
_lootTables.Remove(preset);
DeleteLootTable(preset);
TableListPanelGUI.OpenTablesList(player, filter, page);
}
[ConsoleCommand("TablesList_SearchPreset_LootManager")]
private void TablesListSearchCommand(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if (player == null || !IsPlayerHavePermission(player.UserIDString)) return;
if (arg.Args == null || arg.Args.Length < 1) {
TableListPanelGUI.OpenTablesList(player, string.Empty, 0);
return;
}
string filter = arg.Args[0];
TableListPanelGUI.OpenTablesList(player, filter, 0);
}
[ConsoleCommand("TablesList_CreateNewPreset_LootManager")]
private void TablesListCreateNewCommand(ConsoleSystem.Arg arg) {
BasePlayer pla
// ... [Code preview truncated due to length. Full source in attached file] ...