C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Facepunch;
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 WebSocketSharp;
using Random = UnityEngine.Random;
namespace Oxide.Plugins {
[Info("LootManager", "Adem", "1.1.2")]
public class LootManager : RustPlugin {
#region Variables
[PluginReference]
Plugin AlphaLoot, CustomLoot, Loottable;
private static LootManager _ins;
private readonly HashSet<string> _subscribeMethods = new HashSet<string> {
"OnPluginUnloaded", "OnCrateSpawned", "OnCorpsePopulate", "OnEntityKill", "OnContainerPopulate", "CanPopulateLoot"
}
;
private ItemDefinition _bluePrintItemDefinition;
private readonly Dictionary<string, LootTableData> _lootTables = new Dictionary<string, LootTableData>();
private readonly Dictionary<string, string> _imageIDs = new Dictionary<string, string>();
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 void RegisterPresetUsage(string presetName, string pluginName, string category) {
if (string.IsNullOrEmpty(presetName) || string.IsNullOrEmpty(pluginName)) return;
if (_pluginUsages.TryGetValue(pluginName, out LootTableUsageInfo usageInfo)) {
usageInfo.TryAddNewUsage(presetName, category);
}
else {
LootTableUsageInfo lootTableUsageInfo = new LootTableUsageInfo();
lootTableUsageInfo.TryAddNewUsage(presetName, category);
_pluginUsages.Add(pluginName, lootTableUsageInfo);
}
}
private void UnregisterPresetUsage(string presetName, string pluginName, string category) {
if (string.IsNullOrEmpty(presetName) || string.IsNullOrEmpty(pluginName)) return;
if (_pluginUsages.TryGetValue(pluginName, out LootTableUsageInfo usageInfo)) {
usageInfo.TryAddNewUsage(presetName, category);
}
}
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;
if (!LoadAllLootTables()) return new HashSet<string>();
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);
}
private bool EditLootTable(BasePlayer player, string lootTableName) {
if (player == null || string.IsNullOrEmpty(lootTableName)) return false;
if (!_lootTables.TryGetValue(lootTableName, out LootTableData tableData)) return false;
_playerGuiInfos[player.userID] = new PlayerGuiInfo();
DestroyGuiForPlayer(player);
CreateBackground(player);
EditPanelGui.Draw(player, lootTableName);
return true;
}
#endregion Api
#region Hooks
private void Init() {
Unsubscribes();
}
private void OnServerInitialized() {
_ins = this;
UpdateConfig();
RegisterPermissions();
if (!LoadAllLootTables()) return;
ImageLoader.LoadImages();
PrefabController.CachePrefabs();
_bluePrintItemDefinition = ItemManager.FindItemDefinition("blueprintbase");
Subscribes();
NextTick(() => Interface.CallHook("OnLootManagerInitialized"));
}
private void Unload() {
DestroyGuiForAllPlayers();
}
private void OnPluginUnloaded(Plugin plugin) {
if (plugin == null) return;
_pluginUsages.Remove(plugin.Name);
}
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;
if (!_lootTables.TryGetValue(lootTableName, out LootTableData lootTableData)) return;
LootController.UpdateLootContainer(crate, lootTableData);
}
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;
if (!_lootTables.TryGetValue(lootTableName, out LootTableData lootTableData)) return;
LootController.UpdateLootContainer(crate, lootTableData);
}
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;
if (!_ins._lootTables.TryGetValue(presetName, out LootTableData lootTableData)) return null;
return cancelFunction(lootTableData) ? true : null;
}
#endregion Hooks
#region Methods
private void UpdateConfig() {
if (_config.VersionConfig != Version) {
PluginConfig defaultConfig = PluginConfig.DefaultConfig();
if (_config.VersionConfig.Minor == 0) {
if (_config.VersionConfig.Patch <= 3) {
_config.EnhancedRandomizer = defaultConfig.EnhancedRandomizer;
}
}
_config.VersionConfig = Version;
SaveConfig();
}
}
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 static bool IsPlayerHavePermission(BasePlayer player) {
return player.IsAdmin || _ins.permission.UserHasPermission(player.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}";
}
private static BaseEntity RaycastAll<T>(Ray ray, float distance = 10) where T : BaseEntity {
RaycastHit[] hits = Physics.RaycastAll(ray);
GamePhysics.Sort(hits);
foreach (RaycastHit hit in hits) {
BaseEntity ent = hit.GetEntity();
if (ent is not T || !(hit.distance < distance)) continue;
return ent;
}
return null;
}
#endregion Methods
#region Commands
[ChatCommand("lmfill")]
private void FillContainerCommand(BasePlayer player, string command, string[] arg) {
if (!IsPlayerHavePermission(player)) {
PrintToChat(player, $"[{Name}] You don’t have permission!");
return;
}
if (arg.Length == 0) {
PrintToChat(player, $"[{Name}] Preset not specified");
return;
}
if (!_ins._lootTables.TryGetValue(arg[0], out LootTableData lootTableData)) {
PrintToChat(player, $"[{Name}] Loot table preset not found!");
return;
}
BaseEntity crateEntity = RaycastAll<BaseEntity>(player.eyes.HeadRay());
if (crateEntity == null) {
PrintToChat(player, $"[{Name}] Crate not found");
return;
}
ItemContainer itemContainer = null;
if (crateEntity is Fridge fridge) itemContainer = fridge.inventory;
else if (crateEntity is StorageContainer storageContainer) itemContainer = storageContainer.inventory;
else if (crateEntity is DroppedItemContainer droppedItemContainer) itemContainer = droppedItemContainer.inventory;
if (itemContainer == null) {
PrintToChat(player, $"[{Name}] Crate not found");
return;
}
LootController.UpdateLootContainer(crateEntity as LootContainer, lootTableData);
}
#endregion Commands
#region Classes
private readonly Dictionary<string, PrefabLootInfo> _prefabLootTables = new Dictionary<string, PrefabLootInfo>();
private static class LootController {
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()) return;
corpse.Invoke(() => {
ItemContainer container = corpse.containers[0];
if (container != null) FillContainer(container, lootTableData);
}
, 0.05f);
}
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)) {
if (!_ins.plugins.Exists("CustomLoot")) return;
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();
}
if (!string.IsNullOrEmpty(lootTableData.LootTablePluginLootPreset)) {
if (!_ins.plugins.Exists("Loottable")) return;
List<Item> items = _ins.Loottable.Call<List<Item>>("MakeLoot", lootTableData.LootTablePluginLootPreset);
if (items != null) {
foreach (Item item in items) if (item == null || !item.MoveToContainer(container)) item.Remove();
Facepunch.Pool.FreeUnmanaged(ref items);
}
}
}
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 = 200;
while (result.Count < targetAmount && counter-- > 0) {
if (_ins._config.EnhancedRandomizer) {
float sumChance = 0f;
for (int i = 0; i < elements.Count; i++) {
if (includedIndexes.Contains(i)) continue;
sumChance += elements[i].Chance;
}
float random = Random.Range(0f, sumChance);
for (int i = 0; i < elements.Count; i++) {
if (includedIndexes.Contains(i)) continue;
T lootElement = elements[i];
random -= lootElement.Chance;
if (random <= 0f) {
includedIndexes.Add(i);
result.Add(lootElement);
break;
}
}
}
else {
for (int i = 0; i < elements.Count; 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);
if (result.Count >= targetAmount) break;
}
}
}
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) continue;
if (!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) continue;
if (!item.MoveToContainer(container)) item.Remove();
}
}
private static Item CreateItem(ItemData itemData) {
int amount = Random.Range(itemData.MinAmount, itemData.MaxAmount + 1);
if (amount == 0) return null;
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;
genome = genome.ToLower();
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 {
public 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;
public 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/bundled/prefabs/radtown/oil_barrel.prefab", "oil_barrel", "Oil Barrel"), 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_bradley.prefab", "scientistnpc", "Bradley Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_bradley_heavy.prefab", "scientistnpc_heavy", "Bradley Heavy Scientist"), new (new HashSet<string> {
"assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo_turret_any.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo_turret_lr300.prefab",
}
, "scientistnpc", "Cargo Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_excavator.prefab", "scientistnpc", "Excavator Scientist"), new (new HashSet<string> {
"assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_mp5.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_lr300.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_shotgun.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_any.prefab",
}
, "scientistnpc", "Military Tunnel Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_heavy.prefab", "scientistnpc_heavy", "Heavy Scientist"), new (new HashSet<string> {
"assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_junkpile_pistol.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_outbreak.prefab"
}
, "scientistnpc", "Road Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_oilrig.prefab", "scientistnpc", "Oilrig Scientist"), new (new HashSet<string> {
"assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_patrol.prefab", "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_patrol_arctic.prefab"
}
, "scientistnpc", "Patrol Scientist"), new ("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam.prefab", "scientistnpc", "Roam 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/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 HashSet<string> PrefabNames;
public readonly string ShortPrefabName;
public readonly string Name;
public readonly string ImageName;
public PrefabInfo(string prefabName, string imageName, string name) {
PrefabNames = new HashSet<string> {
prefabName
}
;
ImageName = imageName;
ShortPrefabName = PrefabNames.Count > 0 ? PrefabNames.FirstOrDefault(x => true).Split('/').Last().Split('.').First() : string.Empty;
Name = name;
}
public PrefabInfo(HashSet<string> prefabNames, string imageName, string name) {
PrefabNames = prefabNames;
ImageName = imageName;
ShortPrefabName = PrefabNames.Count > 0 ? PrefabNames.FirstOrDefault(x => true).Split('/').Last().Split('.').First() : string.Empty;
Name = name;
}
}
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) {
// ... [Code preview truncated due to length. Full source in attached file] ...