using System; using System.Linq; using System.Collections.Generic; using Oxide.Core; using Oxide.Core.Plugins; using Newtonsoft.Json; using UnityEngine; using System.Collections; namespace Oxide.Plugins { // Fixed airfield/trainyard npcs // Prevented items with 0 min or max stack from being used. // Check if bradley crates are working. [Info("CustomLoot", "Steenamaroo", "1.1.2", ResourceId = 16)] [Description("Custom loot table plugin for NPCPlayer and HTNPlayer corpses, crates, and containers.")] public class CustomLoot : RustPlugin { #region Declarations [PluginReference] Plugin BotSpawn; bool loaded; static CustomLoot cl; public bool defaultwarned = false; List LootTableNames = new List(); public System.Random random = new System.Random(); public ItemDefinition water = ItemManager.FindItemDefinition("water"); Dictionary LootTables = new Dictionary(); Dictionary> BalancedCategories = new Dictionary>(); Dictionary>>> BalancedItems = new Dictionary>>>(); Dictionary> BotSpawnBots = new Dictionary>(); public List AllContainers = new List(); public Dictionary ContainerTypesFromGame = new Dictionary(); public Dictionary GotCategories = new Dictionary(); public Dictionary> GotLootTypes = new Dictionary>(); public Dictionary> BotTypes = new Dictionary>(); BackupData backupData; #endregion [ConsoleCommand("Repop")] private void Repop(ConsoleSystem.Arg arg) { var player = arg?.Connection?.player as BasePlayer; if (player == null || IsAuth(player)) foreach (var group in SpawnHandler.Instance.SpawnGroups.ToList()) group.Fill(); } #region Hooks void OnServerInitialized() { BalancedCategories = new Dictionary>(); BalancedItems = new Dictionary>>>(); GotCategories = new Dictionary(); ContainerTypesFromGame = new Dictionary(); GotLootTypes = new Dictionary>(); BotTypes = new Dictionary>(); List names = new List(); foreach (var entry in Resources.FindObjectsOfTypeAll()) { if (entry.PrefabName.Contains("underwater_labs")) names.Add("underwater_labs_" + entry.ShortPrefabName); else names.Add(entry.ShortPrefabName); } names = names.Distinct().ToList(); foreach (var entry in names) if (!entry.Contains("test") && !entry.Contains("stocking")) ContainerTypesFromGame.Add(entry, new Settings()); cl = this; if (BotSpawn) RefreshBotSpawn(); var cats = Enum.GetNames(typeof(ItemCategory)).ToList(); List exclude = new List { "Search", "Favourite", "All", "Common" }; backupData = Interface.Oxide.DataFileSystem.ReadObject("CustomLoot/BotSpawnBackups"); LoadConfigVariables(); Interface.Oxide.DataFileSystem.WriteObject("CustomLoot/BotSpawnBackups", backupData); foreach (LootContainer container in BaseNetworkable.serverEntities.OfType()) { string name = container.PrefabName.Contains("underwater_labs") ? "underwater_labs_" + container.ShortPrefabName : container.ShortPrefabName; if (configData.ContainerTypes.ContainsKey(name) && configData.ContainerTypes[name].enabled) ProcessContainer(container, true); } timer.Once(5f, () => ServerMgr.Instance.StartCoroutine(FillContainers(AllContainers))); loaded = true; foreach (BasePlayer player in BaseNetworkable.serverEntities.OfType()) { if (player is NPCPlayer || player is HTNPlayer || player is global::HumanNPC) AddToBotList(player); } foreach (var cat in cats.Where(cat => !exclude.Contains(cat))) { GotCategories.Add(cat, new CategorySettings()); GotLootTypes.Add(cat, new Dictionary()); } foreach (var lT in LootTableNames.ToList()) { if (CreateLootTable(lT)) { BalanceCategories(lT); BalanceItems(lT); } } } #endregion #region ContainerTypes void ProcessContainer(LootContainer container, bool start) { if (Interface.CallHook("OnCustomLootContainer", container.net.ID) != null) return; if (container?.OwnerID != 0 && !(container is SupplyDrop)) return; if (start) AllContainers.Add(container); else PopulateContainer(container); } void PopulateContainer(LootContainer container) { if (container == null) return; string name = container.PrefabName.Contains("underwater_labs") ? "underwater_labs_" + container.ShortPrefabName : container.ShortPrefabName; if (container.ShortPrefabName != "heli_crate" && name != "bradley_crate") { List containers = new List(); Vis.Entities(container.transform.position, 0.1f, containers); foreach (var cont in containers) { if (cont == container) continue; NextTick(() => container?.Kill()); return; } } timer.Once(1f, () => { if (container == null) return; var id = container.OwnerID; container.OwnerID = 0; container.inventory.capacity = 36; container.onlyAcceptCategory = ItemCategory.All; container.SendNetworkUpdateImmediate(); var lootObj = MakeLoot(name); if (lootObj == null) return; if (configData.ContainerTypes[name].ClearContainerFirst) container.inventory.Clear(); foreach (var item in lootObj as List) if (!item.MoveToContainer(container.GetComponent().inventory, -1, true)) item.Remove(); else { var vessel = item?.GetHeldEntity() as BaseLiquidVessel; if (vessel != null && vessel.hasLid) { int percentage = Mathf.Clamp(configData.ContainerTypes[name].WaterPreFillPercent, 0, 100); var stack = item?.info?.GetComponent()?.maxStackSize; if (percentage == 0 || stack == null) continue; Item waterItem = ItemManager.Create(water, (int)(stack / 100f * percentage), 0); if (!waterItem.MoveToContainer(item.contents, -1, true)) waterItem.Remove(); } } container.OwnerID = id; }); } void OnLootSpawn(LootContainer container) { if (!loaded) return; string name = container.PrefabName.Contains("underwater_labs") ? "underwater_labs_" + container.ShortPrefabName : container.ShortPrefabName; timer.Once(0.3f, () => { if (container == null) return; if (configData.ContainerTypes.ContainsKey(name) && configData.ContainerTypes[name].enabled) ProcessContainer(container, false); }); } public IEnumerator FillContainers(List AllContainers) { foreach (var container in AllContainers) { cl.PopulateContainer(container); yield return new WaitForEndOfFrame(); } cl.PrintWarning("Finished populating all containers."); yield return null; } #endregion ContainerTypes #region Methods bool IsAuth(BasePlayer player) => player?.net?.connection != null && player.net.connection.authLevel == 2; Settings GetSettings(object record) { var outRecord = JsonConvert.SerializeObject(record); var response = JsonConvert.DeserializeObject(outRecord); response.enabled = true; return response; } #endregion #region BotHandling void OnEntitySpawned(LootableCorpse corpse) { if (!loaded) return; timer.Once(0.3f, () => { if (corpse != null) { ulong ID = corpse.playerSteamID; string profile = string.Empty; var path = configData.CorpseTypes; foreach (var entry in BotTypes.Where(entry => entry.Value.Contains(corpse.playerSteamID))) profile = entry.Key; if (profile == string.Empty) return; if (configData.CorpseTypes[profile].ClearContainerFirst) corpse.containers[0].Clear(); var lootObj = MakeLoot(profile); if (lootObj == null) return; foreach (var item in lootObj as List) if (!item.MoveToContainer(corpse.containers[0], -1, true)) item.Remove(); else { var vessel = item?.GetHeldEntity() as BaseLiquidVessel; if (vessel != null && vessel.hasLid) { int percentage = Mathf.Clamp(configData.CorpseTypes[profile].WaterPreFillPercent, 0, 100); var stack = item?.info?.GetComponent()?.maxStackSize; if (percentage == 0 || stack == null) continue; Item waterItem = ItemManager.Create(water, (int)(stack / 100f * percentage), 0); if (!waterItem.MoveToContainer(item.contents, -1, true)) waterItem.Remove(); } } timer.Once(0.2f, () => RemoveFromMemory(ID)); } }); } void OnEntitySpawned(BasePlayer player) { if (!loaded) return; timer.Once(0.1f, () => //delay for processing at BotSpawn end { if (player == null) return; if (BotSpawn && player is NPCPlayerApex) RefreshBotSpawn(); }); timer.Once(0.3f, () => { if (player == null) return; if (player is NPCPlayer || player is HTNPlayer) { AddToBotList(player); return; } }); } void RefreshBotSpawn() => BotSpawnBots = (Dictionary>)BotSpawn?.Call("BotSpawnBots"); void AddToBotList(BasePlayer player) { if (Interface.CallHook("OnCustomLootNPC", player.net.ID) != null) return; foreach (var entry in names) if (player.PrefabName.Contains(entry.Key)) { ProcessNpc(player, entry.Value); return; } NPCPlayerApex npc = player as NPCPlayerApex; if (npc != null) { if (BotSpawn) foreach (var monument in BotSpawnBots.Where(monument => monument.Value.Contains(player.userID))) { AddBotSpawn(npc, monument.Key); return; } if (npc is NPCMurderer) ProcessNpc(player, "Murderer"); else if (npc is Scientist) { if (npc.GetFact(NPCPlayerApex.Facts.IsMilitaryTunnelLab) == 1) ProcessNpc(player, "MilitaryTunnelScientist"); else ProcessNpc(player, "Scientist"); } } else if (player is NPCPlayer) { var instance = player?.GetComponent()?.parentSpawnPoint; if (instance != null) { var name = instance?.GetComponentInParent()?.ToString(); if (name != null) { foreach (var n in names) if (name.Contains(n.Key)) ProcessNpc(player, n.Value); } } } } Dictionary names = new Dictionary() { {"oilrig", "OilRig"}, {"excavator", "Excavator"}, {"scientistpeacekeeper", "CompoundScientist"}, {"bandit_guard", "BanditTown"}, {"scientist_gunner", "MountedScientist"}, {"junkpile", "JunkPileScientist"}, {"scarecrow", "ScareCrow"}, {"scientist_full", "MilitaryTunnelScientist"}, {"scientist_turret", "CargoShip"}, {"scientistnpc_cargo", "CargoShip"}, {"scientist_astar", "CargoShip"}, {"heavyscientist", "HeavyScientist"}, {"tunneldweller", "TunnelDweller" }, {"trainyard" , "Trainyard"}, {"underwaterdweller" , "UnderwaterDweller"}, {"airfield" , "Airfield"} }; void ProcessNpc(BasePlayer player, string NPCType) { var record = configData.CorpseTypes[NPCType]; if (record == null) return; if (record.enabled) BotTypes[NPCType].Add(player.userID); } void AddBotSpawn(NPCPlayer npc, string monument) { if (configData.GlobalSettings.corpseTypePerBotSpawnProfile) { if (!BotTypes.ContainsKey("BotSpawn-" + monument)) OnServerInitialized(); if (configData.CorpseTypes["BotSpawn-" + monument].enabled) BotTypes["BotSpawn-" + monument].Add(npc.userID); } else if (configData.CorpseTypes["BotSpawn"].enabled) BotTypes["BotSpawn"].Add(npc.userID); } void RemoveFromMemory(ulong userID) { foreach (var botList in BotTypes.Where(botList => botList.Value.Contains(userID))) { botList.Value.Remove(userID); break; } } #endregion #region LootHandling public void BalanceCategories(string lootTable) { int counter = 0; BalancedCategories.Add(lootTable, new Dictionary()); foreach (var category in LootTables[lootTable].Categories) { if (!HasValidItem(lootTable, category.Key)) continue; for (int i = 0; i < category.Value.probability; i++) { BalancedCategories[lootTable].Add(counter, category.Key); counter++; } } } public void BalanceItems(string lootTable) { BalancedItems.Add(lootTable, new Dictionary>>()); foreach (var category in LootTables[lootTable].LootTypes.Where(category => LootTables[lootTable].Categories[category.Key].probability > 0)) { int counter = 0; if (!HasValidItem(lootTable, category.Key)) continue; BalancedItems[lootTable].Add(category.Key, new Dictionary>()); foreach (var entry in category.Value.Where(x => x.Value.minStack > 0 && x.Value.maxStack > 0)) { for (int i = 0; i < entry.Value.probability; i++) { var blank = new KeyValuePair(entry.Key, entry.Value); BalancedItems[lootTable][category.Key].Add(counter, blank); counter++; } } } } public bool HasValidItem(string lootTable, string category) { if (LootTables[lootTable].LootTypes[category].Count == 0) return false; foreach (var item in LootTables[lootTable].LootTypes[category].Where(item => item.Value.probability > 0)) return true; return false; } private object MakeLoot(string config) { Settings settings = null; if (configData.CorpseTypes.ContainsKey(config)) settings = configData.CorpseTypes[config]; else if (configData.ContainerTypes.ContainsKey(config)) settings = configData.ContainerTypes[config]; else if (configData.API.ContainsKey(config)) settings = GetSettings(configData.API[config]); else { Puts("CustomLoot had a call for a profile that doesn't exist."); Puts($"Adding profile {config} using loottable 'default'."); configData.API.Add(config, new BaseSettings()); SaveConfig(configData); settings = GetSettings(configData.API[config]); } if (settings == null) return null; string lootTable = settings.lootTable; if (!LootTableNames.Contains(lootTable)) return null; int safety = 0; int number = settings.maxItems; List newLoot = new List(); if (settings.maxItems > settings.minItems) number = random.Next(settings.minItems, settings.maxItems); int BPs = 0; for (int i = 0; i < number; i++) { safety++; bool duplicate = false; if (safety > 50) { break; } string category = GetCategory(lootTable); if (category == "zero") return null; Item item = null; bool forced = false; if (LootTables[lootTable].AlwaysSpawnList.Count >= i + 1) { foreach (var cat in LootTables[lootTable].LootTypes) { if (cat.Value.ContainsKey(LootTables[lootTable].AlwaysSpawnList[i])) { forced = true; item = GetItem(settings, cat.Key, BPs, LootTables[lootTable].AlwaysSpawnList[i]); } } } if (!forced) item = GetItem(settings, category, BPs, string.Empty); if (item == null) { i--; continue; } CategorySettings confPath = LootTables[lootTable].Categories[category]; if (item.blueprintTarget != 0) BPs++; foreach (var newItem in newLoot) { if (newItem.info.shortname == item.info.shortname && !configData.GlobalSettings.allowDuplicates) duplicate = true; if (newItem.blueprintTarget != 0 && item.blueprintTarget != 0 && newItem.blueprintTarget == item.blueprintTarget && !configData.GlobalSettings.allowDuplicates) duplicate = true; } if (duplicate) { item.Remove(); i--; continue; } else { if (category.ToString() == "Weapon" && !settings.noGuns) { newLoot.Add(item); if (settings.gunsWithAmmo && item.blueprintTarget == 0) { string matchedAmmo = (item.GetHeldEntity() as BaseProjectile)?.primaryMagazine?.ammoType?.shortname; lootData ammopath; if (item.info.shortname != "flamethrower") { if (matchedAmmo == null || !LootTables[lootTable].LootTypes["Ammunition"].ContainsKey(matchedAmmo)) continue; ammopath = LootTables[lootTable].LootTypes["Ammunition"][matchedAmmo]; } else if (LootTables[lootTable].LootTypes["Resources"].ContainsKey("lowgradefuel")) { ammopath = LootTables[lootTable].LootTypes["Resources"]["lowgradefuel"]; matchedAmmo = "lowgradefuel"; } else continue; int stack = random.Next(ammopath.minStack, ammopath.maxStack); if (stack > 10) stack = (stack / 10) * 10; if (ammopath.probability > 0) { Item ammoItem = ItemManager.CreateByName(matchedAmmo, stack); newLoot.Add(ammoItem); i++; } } } else if (category.ToString() != "Weapon") newLoot.Add(item); } } if (newLoot.Count > number) foreach (var entry in newLoot.Where(entry => entry.info.category.ToString() != "Weapon" && entry.info.category.ToString() != "Ammunition").ToList()) { entry.Remove(); newLoot.Remove(entry); break; } return newLoot; } float GetCondition(int min, int max, float itemMax) { min = Mathf.Max(1, min); max = Mathf.Max(1, max); if (min >= max) { return itemMax / 100f * max; } return itemMax / 100f * random.Next(min, max); } public string GetCategory(string lootTable) { if (!BalancedCategories.ContainsKey(lootTable)) { Puts($"Balanced categories did not contain {lootTable}"); return "zero"; } if (BalancedCategories[lootTable].Count == 0) return "zero"; int catnumber = random.Next(BalancedCategories[lootTable].Count); return BalancedCategories[lootTable][catnumber]; } Dictionary> skins = new Dictionary>(); ItemDefinition bpb = ItemManager.FindItemDefinition("blueprintbase"); public Item GetItem(Settings settings, string category, int BPs, string perma) { string lootTable = settings.lootTable; Item item = new Item(); int chosenItem; string name = string.Empty; lootData path = null; if (perma != string.Empty) { name = perma; path = LootTables[lootTable].LootTypes[category][perma]; } else { chosenItem = random.Next(BalancedItems[lootTable][category].Count); name = BalancedItems[lootTable][category]?[chosenItem].Key; path = BalancedItems[lootTable]?[category]?[chosenItem].Value; } if (name == string.Empty || path == null) return null; int stack = path.maxStack; if (path.minStack < path.maxStack) stack = random.Next(path.minStack, path.maxStack); if (category == "Resources" && stack > 10) stack = (stack / 10) * 10; item = ItemManager.CreateByName(name, stack, 0); if (!skins.ContainsKey(name)) skins.Add(name, path.skins); if (path.IncludeAllApprovedSkins) { foreach (var entry in Rust.Workshop.Approved.All) { if (entry.Value?.Skinnable == null) continue; if (entry.Value.Skinnable.ItemName == name && !skins[name].Contains(entry.Value.WorkshopdId)) skins[name].Add(entry.Value.WorkshopdId); } } if (skins[name].Count > 0) item.skin = skins[name][random.Next(skins[name].Count)]; if (item.info.condition.enabled) item.condition = GetCondition(path.MinConditionPercent, path.MaxConditionPercent, item.info.condition.max); CategorySettings confPath = LootTables[lootTable].Categories[category]; ItemDefinition def = ItemManager.FindItemDefinition(item.info.itemid); if (confPath.allowBlueprints && random.Next(99) < path.blueprintChancePercent && item.info.Blueprint != null && IsBP(def)) { if (BPs == settings.MaxBps) return null; item.Remove(); item = ItemManager.Create(bpb, 1, 0uL); item.blueprintTarget = def.itemid; } if (item == null) Puts($"Null-item spawn attempted. Please notify Author - '{name}'"); else { item.MarkDirty(); BaseEntity held = item.GetHeldEntity(); if (held != null) { held.skinID = item.skin; held.SendNetworkUpdate(BasePlayer.NetworkQueue.Update); } } return item; } bool IsBP(ItemDefinition def) => def?.Blueprint != null && def.Blueprint.isResearchable && !def.Blueprint.defaultBlueprint; #endregion #region Data public class BackupData { public Dictionary BotSpawnBackups = new Dictionary(); } public class LootTable { public bool allowChristmas, allowHalloween, allowKeycards = false; public List BlackList = new List(); public List AlwaysSpawnList = new List(); public Dictionary Categories = cl.GotCategories.ToDictionary(pair => pair.Key, pair => pair.Value); public Dictionary> LootTypes = cl.GotLootTypes.ToDictionary(pair => pair.Key, pair => pair.Value); } public class CategorySettings { public int probability; public bool allowBlueprints = true; } public class lootData { [JsonIgnore] public ItemDefinition item; [JsonProperty(Order = 1)] public int probability; [JsonProperty(Order = 2)] public int minStack = 1; [JsonProperty(Order = 3)] public int maxStack = 1; [JsonProperty(Order = 4)] public int blueprintChancePercent = 0; [JsonProperty(Order = 5)] public List skins = new List(); [JsonProperty(Order = 6)] public bool IncludeAllApprovedSkins = false; [JsonProperty(Order = 7)] public int MinConditionPercent = 90; [JsonProperty(Order = 8)] public int MaxConditionPercent = 100; public bool ShouldSerializeblueprintChancePercent() => item != null && cl.IsBP(item); public bool ShouldSerializeskins() => item != null && (cl.configData.GlobalSettings.Show_Skins_For_All_Items || item.HasSkins); public bool ShouldSerializeIncludeAllApprovedSkins() => item != null && (cl.configData.GlobalSettings.Show_Skins_For_All_Items || item.HasSkins); public bool ShouldSerializeMaxConditionPercent() => item != null && item.condition.enabled; public bool ShouldSerializeMinConditionPercent() => item != null && item.condition.enabled; } public List DefaultBlackList = new List { "glue", "door.key", "note", "bleach", "ducttape", "blood", "tool.camera", "grenade.smoke", "ammo.rocket.smoke", "battery", "bone.fragments", "blueprintbase", "water.salt", "jackhammer" }; public bool CreateLootTable(string lootTable) { try { LootTables[lootTable] = Interface.Oxide.DataFileSystem.ReadObject($"CustomLoot/{lootTable}"); } catch { Puts($"There is an error in data file {lootTable}."); LootTableNames.Remove(lootTable); return false; } try { var catPath = string.Empty; var path = LootTables[lootTable]; Dictionary> itemCheck = new Dictionary>(); Dictionary> toRemove = new Dictionary>(); if (path.BlackList.Count == 0) path.BlackList = DefaultBlackList; path.BlackList.Sort(); foreach (ItemDefinition rustItem in ItemManager.itemList) { catPath = rustItem.category.ToString(); if (!path.LootTypes.ContainsKey(catPath)) { Puts($"New item category *{catPath}* added to game. Please notify author."); continue; } if (IsJunk(lootTable, rustItem.shortname) || (!path.allowChristmas && rustItem.shortname.Contains("xmas")) || (!path.allowHalloween && rustItem.shortname.Contains("halloween")) || (!path.allowKeycards && rustItem.shortname.Contains("keycard_"))) { path.LootTypes[catPath].Remove(rustItem.shortname); continue; } if (!itemCheck.ContainsKey(catPath)) itemCheck.Add(catPath, new List()); itemCheck[catPath].Add(rustItem.shortname); if (path.LootTypes[catPath].ContainsKey(rustItem.shortname)) { path.LootTypes[catPath][rustItem.shortname].item = rustItem; continue; } var stackPath = path.Categories[catPath]; path.LootTypes[catPath].Add(rustItem.shortname, new lootData { item = rustItem }); } foreach (var cat in itemCheck) foreach (var item in path.LootTypes[cat.Key]) if (!itemCheck[cat.Key].Contains(item.Key)) { if (!toRemove.ContainsKey(cat.Key)) toRemove.Add(cat.Key, new List()); toRemove[cat.Key].Add(item.Key); } foreach (var odd in toRemove) foreach (var oddItem in odd.Value) path.LootTypes[odd.Key].Remove(oddItem); LootTables[lootTable].Categories = LootTables[lootTable].Categories.OrderBy(x => x.Key).ToDictionary(pair => pair.Key, pair => pair.Value); LootTables[lootTable].LootTypes = LootTables[lootTable].LootTypes.OrderBy(x => x.Key).ToDictionary(pair => pair.Key, pair => pair.Value); foreach (var entry in LootTables[lootTable].LootTypes.ToDictionary(pair => pair.Key, pair => pair.Value)) LootTables[lootTable].LootTypes[entry.Key] = entry.Value.OrderBy(x => x.Key).ToDictionary(pair => pair.Key, pair => pair.Value); Interface.Oxide.DataFileSystem.WriteObject($"CustomLoot/{lootTable}", path); return true; } catch { Puts($"There was an error loading data file {lootTable}."); LootTableNames.Remove(lootTable); return false; } } public bool IsJunk(string lootTable, string rustItem) { List junk = LootTables[lootTable].BlackList; if (junk.Contains(rustItem)) return true; return false; } public bool IsReal(string rustItem) { var createTest = ItemManager.CreateByName(rustItem, 1); if (createTest != null) { createTest.Remove(0f); return true; } return false; } #endregion #region Config private ConfigData configData; class ConfigData { public GlobalSettings GlobalSettings = new GlobalSettings(); public static Settings Settings = new Settings(); public Dictionary CorpseTypes = new Dictionary { {"MilitaryTunnelScientist", Settings}, {"JunkPileScientist", Settings}, {"MountedScientist", Settings}, {"CompoundScientist", Settings}, {"BanditTown", Settings}, {"Murderer", Settings}, {"ScareCrow", Settings}, {"CargoShip", Settings}, {"OilRig", Settings}, {"Excavator", Settings}, {"Scientist", Settings}, {"HeavyScientist", Settings}, {"TunnelDweller", Settings}, {"Airfield", Settings}, {"Trainyard", Settings}, {"UnderwaterDweller", Settings}, {"BotSpawn", Settings} }; public Dictionary ContainerTypes = cl.ContainerTypesFromGame; public Dictionary API = new Dictionary(); } public class GlobalSettings { public bool allowDuplicates = false; public bool corpseTypePerBotSpawnProfile = false; public bool Include_DM_Crates = false; public bool Show_Skins_For_All_Items = false; } public class BaseSettings { [JsonProperty(Order = 1)] public string lootTable = "default"; [JsonProperty(Order = 2)] public int maxItems = 6; [JsonProperty(Order = 3)] public int minItems = 6; [JsonProperty(Order = 4)] public bool gunsWithAmmo; [JsonProperty(Order = 5)] public bool noGuns; [JsonProperty(Order = 6)] public int MaxBps = 3; [JsonProperty(Order = 7)] public int WaterPreFillPercent = 20; [JsonProperty(Order = 8)] public bool ClearContainerFirst = true; } public class Settings : BaseSettings { [JsonProperty(Order = 1)] public bool enabled; } List Remove = new List() {"crate_fuel","crate_ammunition","crate_food_1","crate_food_2","crate_medical","tech_parts_1","tech_parts_2"}; private void LoadConfigVariables() { BotTypes.Clear(); LootTableNames = new List() { "default" }; configData = Config.ReadObject(); bool perBotSpawn = false; foreach (var entry in configData.ContainerTypes.ToList()) if (Remove.Contains(entry.Key)) configData.ContainerTypes.Remove(entry.Key); if (configData.GlobalSettings.corpseTypePerBotSpawnProfile) { perBotSpawn = true; if (configData.CorpseTypes.ContainsKey("BotSpawn")) { if (backupData.BotSpawnBackups.ContainsKey("BotSpawn")) backupData.BotSpawnBackups["BotSpawn"] = configData.CorpseTypes["BotSpawn"]; else if (JsonConvert.SerializeObject(configData.CorpseTypes["BotSpawn"]) != JsonConvert.SerializeObject(new Settings())) backupData.BotSpawnBackups.Add("BotSpawn", configData.CorpseTypes["BotSpawn"]); configData.CorpseTypes.Remove("BotSpawn"); } foreach (var entry in BotSpawnBots.Where(entry => !configData.CorpseTypes.ContainsKey($"BotSpawn-{entry.Key}"))) { if (backupData.BotSpawnBackups.ContainsKey($"BotSpawn-{entry.Key}")) configData.CorpseTypes.Add($"BotSpawn-{entry.Key}", backupData.BotSpawnBackups[$"BotSpawn-{entry.Key}"]); else configData.CorpseTypes.Add($"BotSpawn-{entry.Key}", new Settings()); } } else { if (JsonConvert.SerializeObject(configData.CorpseTypes["BotSpawn"]) == JsonConvert.SerializeObject(new Settings())) { if (backupData.BotSpawnBackups.ContainsKey("BotSpawn")) configData.CorpseTypes["BotSpawn"] = backupData.BotSpawnBackups["BotSpawn"]; } } if (BotSpawn) { foreach (var entry in configData.CorpseTypes.ToDictionary(pair => pair.Key, pair => pair.Value).Where(entry => (!perBotSpawn && entry.Key.Contains("BotSpawn-")) || (perBotSpawn && entry.Key.Contains("BotSpawn-") && !BotSpawnBots.ContainsKey(entry.Key.Remove(0, 9))))) { if (backupData.BotSpawnBackups.ContainsKey(entry.Key)) backupData.BotSpawnBackups[entry.Key] = configData.CorpseTypes[entry.Key]; else backupData.BotSpawnBackups.Add(entry.Key, configData.CorpseTypes[entry.Key]); configData.CorpseTypes.Remove(entry.Key); } } if (!configData.GlobalSettings.Include_DM_Crates) foreach (var entry in configData.ContainerTypes.ToDictionary(pair => pair.Key, pair => pair.Value).Where(x => x.Key.Contains("dm "))) configData.ContainerTypes.Remove(entry.Key); foreach (var entry in configData.CorpseTypes.ToDictionary(pair => pair.Key, pair => pair.Value)) { BotTypes.Add(entry.Key, new List()); if (!LootTableNames.Contains(entry.Value.lootTable)) LootTableNames.Add(entry.Value.lootTable); } foreach (var entry in configData.ContainerTypes) if (!LootTableNames.Contains(entry.Value.lootTable)) LootTableNames.Add(entry.Value.lootTable); foreach (var entry in configData.API) if (!LootTableNames.Contains(entry.Value.lootTable)) LootTableNames.Add(entry.Value.lootTable); SaveConfig(configData); } protected override void LoadDefaultConfig() { Puts("Creating new config file."); } void SaveConfig(ConfigData config) { config.CorpseTypes = config.CorpseTypes.OrderBy(x => x.Key).OrderBy(x => x.Key.Contains("BotSpawn")).ToDictionary(pair => pair.Key, pair => pair.Value); configData.ContainerTypes = configData.ContainerTypes.OrderBy(x => x.Key).OrderByDescending(x => x.Key.Contains("barrel")).ToDictionary(pair => pair.Key, pair => pair.Value); configData.API = configData.API.OrderBy(x => x.Key).ToDictionary(pair => pair.Key, pair => pair.Value); Config.WriteObject(config, true); } #endregion } }