using System.Linq; using System.Collections.Generic; using Oxide.Core.Plugins; using Oxide.Core; using UnityEngine; using Newtonsoft.Json; //Made 'Wipe_Default_Inventory' optional. //Added health per npc type namespace Oxide.Plugins { [Info("NPCKits", "Steenamaroo", "1.0.3", ResourceId = 0)] [Description("Give custom Kits to all default Rust npc types.")] public class NPCKits : RustPlugin { #region Declarations [PluginReference] Plugin Kits; bool loaded; public System.Random random = new System.Random(); public Dictionary LiveNpcs = new Dictionary(); public Dictionary botInventories = new Dictionary(); public class Inv { public List[] inventory = { new List(), new List(), new List() }; } public class InvContents { public int ID; public int amount; public ulong skinID; } #endregion #region Hooks void OnServerInitialized() { LoadConfigVariables(); if (!loaded) loaded = true; foreach (BasePlayer player in UnityEngine.Object.FindObjectsOfType()) if (player is NPCPlayerApex || player is HTNPlayer || player is global::HumanNPC) AddToBotList(player); } #endregion #region BotHandling void OnEntitySpawned(BaseEntity entity) { if (!loaded || entity == null) return; timer.Once(0.3f, () => { if (entity != null && entity is NPCPlayer || entity is HTNPlayer) AddToBotList(entity.GetComponent()); }); var corpse = entity as LootableCorpse; if (corpse != null && LiveNpcs.ContainsKey(corpse.playerSteamID)) { var pos = corpse.transform.position; Inv botInv = new Inv(); ulong id = corpse.playerSteamID; Settings record; record = LiveNpcs[id]; timer.Once(0.1f, () => { if (corpse == null || !botInventories.ContainsKey(id)) return; botInv = botInventories[id]; var ARL = record.Default_Rust_Loot_Percent >= random.Next(1, 101); var WM = record.Wipe_Main_Inventory_Percent >= random.Next(1, 101); var WC = record.Wipe_Clothing_Percent >= random.Next(1, 101); var WB = record.Wipe_Belt_Percent >= random.Next(1, 101); if (!ARL) corpse.containers[0].Clear(); for (int i = 0; i < botInv.inventory.Length; i++) { if (i == 0 && WM || i == 1 && WC || i == 2 && WB) continue; foreach (var item in botInv.inventory[i]) { var giveItem = ItemManager.CreateByItemID(item.ID, item.amount, item.skinID); giveItem.MoveToContainer(corpse.containers[i], -1, true); } } LiveNpcs.Remove(corpse.playerSteamID); botInventories.Remove(id); }); } } float GetPercent(int min, int max, float weapMax) { min = Mathf.Max(1, min); max = Mathf.Max(1, max); if (min >= max) return weapMax / 100f * max; return weapMax / 100f * random.Next(min, max); } void OnPlayerDeath(BasePlayer player, HitInfo info) => OnEntityKill(player); void OnEntityKill(BaseNetworkable entity) { NPCPlayerApex npc = entity as NPCPlayerApex; if (npc?.userID != null && LiveNpcs.ContainsKey(npc.userID) && !botInventories.ContainsKey(npc.userID)) { Item activeItem = npc.GetActiveItem(); var record = LiveNpcs[npc.userID]; int chance = random.Next(1, 101); if (record.Weapon_Drop_Percent >= chance && activeItem != null) { activeItem.condition = GetPercent(record.Min_Weapon_Drop_Condition_Percent, record.Max_Weapon_Drop_Condition_Percent, activeItem.info.condition.max); var held = activeItem.GetHeldEntity(); if (held != null) { BaseProjectile gun = held as BaseProjectile; if (gun != null) { bool wipeAmmo = record.Dropped_Weapon_Has_Ammo_Percent_Chance < random.Next(1, 101); gun.primaryMagazine.contents = wipeAmmo ? 0 : gun.primaryMagazine.capacity; gun.SendNetworkUpdateImmediate(); activeItem.Drop(npc.eyes.position, new Vector3(), new Quaternion()); npc.svActiveItemID = 0; npc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update); } } } ItemContainer[] source = { npc.inventory.containerMain, npc.inventory.containerWear, npc.inventory.containerBelt }; Inv botInv = new Inv(); botInventories.Add(npc.userID, botInv); for (int i = 0; i < source.Length; i++) { foreach (var item in source[i].itemList) { botInv.inventory[i].Add(new InvContents { ID = item.info.itemid, amount = item.amount, skinID = item.skin, }); } } } } void AddToBotList(BasePlayer player) { if (Interface.CallHook("OnNpcKits", player.userID) != null) return; if (Interface.CallHook("OnNpcKits", player) != null) return; HTNPlayer htnNpc; NPCPlayerApex npc; Rust.Ai.AiLocationManager spawner; string loc = string.Empty, locationType = string.Empty; if (player is global::HumanNPC) { var PSpawn = player?.GetComponent()?.parentSpawnGroup; if (PSpawn == null) ProcessNpc(player, "HeavyScientist"); var name = PSpawn?.GetComponentInParent()?.ToString(); if (name != null) { if (name.Contains("oilrig")) ProcessNpc(player, "OilRig"); else if (name.Contains("excavator")) ProcessNpc(player, "Excavator"); } } else if (player is NPCPlayerApex) { npc = player as NPCPlayerApex; var agent = (player as NPCPlayerApex)?.AiContext; if (npc is NPCMurderer) ProcessNpc(player, "Murderer"); else if (npc is Scientist) { if (npc.ShortPrefabName.Contains("scientist_gunner")) { ProcessNpc(player, "MountedScientist"); return; } spawner = npc.AiContext?.AiLocationManager; if (npc.GetComponent() == null) { ProcessNpc(player, "Scientist"); return; } loc = spawner?.LocationType.ToString(); if (loc == null) return; if (loc == "JunkpileA" || loc == "JunkpileG") ProcessNpc(player, "JunkPileScientist"); else if (loc == "MilitaryTunnels") ProcessNpc(player, "MilitaryTunnelScientist"); else if (loc == "Compound") ProcessNpc(player, "CompoundScientist"); else if (loc == "BanditTown") ProcessNpc(player, "BanditTown"); else ProcessNpc(player, "Scientist"); } } else if (player is HTNPlayer) { htnNpc = player as HTNPlayer; var agent = htnNpc?.AiDomain?.NpcContext; if (agent != null) { var turretContext = agent as Rust.Ai.HTN.NPCTurret.NPCTurretContext; var aStarContext = agent as Rust.Ai.HTN.ScientistAStar.ScientistAStarContext; var scientistContext = agent as Rust.Ai.HTN.Scientist.ScientistContext; var murdContext = agent as Rust.Ai.HTN.Murderer.MurdererContext; var jpContext = agent as Rust.Ai.HTN.ScientistJunkpile.ScientistJunkpileContext; if (turretContext != null) loc = turretContext.Location.LocationType.ToString(); if (aStarContext != null) loc = aStarContext.Location.LocationType.ToString(); if (scientistContext != null) ProcessNpc(player, "MilitaryTunnelScientist"); if (murdContext != null) ProcessNpc(player, "ScareCrow"); else if (jpContext != null) ProcessNpc(player, "JunkPileScientist"); else if (loc.Contains("CargoShip")) ProcessNpc(player, "CargoShip"); } } } object GetKitInfo(string name) => Kits?.Call("GetKitInfo", name, true); object GiveKit(BasePlayer npc, string name) => Kits?.Call($"GiveKit", npc, name, true); void ProcessNpc(BasePlayer player, string NPCType) { var record = configData.CorpseTypes[NPCType]; if (record == null) return; if (record.Kits != null && record.Kits.Count != 0) { int kitRnd = random.Next(record.Kits.Count); if (record.Kits[kitRnd] != null) { object checkKit = GetKitInfo(record.Kits[kitRnd]); if (checkKit == null) PrintWarning($"Kit {record.Kits[kitRnd]} does not exist."); else { if (record.Wipe_Default_Inventory) player.inventory.Strip(); GiveKit(player, record.Kits[kitRnd]); player.InitializeHealth(record.Health, record.Health); var htn = player as HTNPlayer; if (htn != null) { Item item = htn.inventory.containerBelt.GetSlot(0); if (item == null) return; htn.svActiveItemID = item.uid; htn.UpdateActiveItem(item.uid); var held = item.GetHeldEntity(); if (held != null) (held as HeldEntity)?.SetHeld(true); htn.inventory.UpdatedVisibleHolsteredItems(); htn.SendNetworkUpdate(BasePlayer.NetworkQueue.Update); } if (player == null) return; if (player is global::HumanNPC) (player as global::HumanNPC).EquipWeapon(); if (player is NPCPlayer) ((NPCPlayer)player).EquipWeapon(); } } } LiveNpcs.Add(player.userID, record); } #endregion #region Config private ConfigData configData; class ConfigData { 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}, }; } public class Settings { [JsonProperty(Order = 1)] public List Kits = new List(); [JsonProperty(Order = 2)] public int Health = 150; [JsonProperty(Order = 3)] public int Weapon_Drop_Percent = 100; [JsonProperty(Order = 4)] public int Min_Weapon_Drop_Condition_Percent = 100; [JsonProperty(Order = 5)] public int Max_Weapon_Drop_Condition_Percent = 100; [JsonProperty(Order = 6)] public int Dropped_Weapon_Has_Ammo_Percent_Chance = 100; [JsonProperty(Order = 7)] public bool Wipe_Default_Inventory = true; public int Wipe_Main_Inventory_Percent = 100; public int Wipe_Clothing_Percent = 100; public int Wipe_Belt_Percent = 100; public int Default_Rust_Loot_Percent = 100; } private void LoadConfigVariables() { configData = Config.ReadObject(); SaveConfig(configData); } protected override void LoadDefaultConfig() { Puts("Creating new config file."); } void SaveConfig(ConfigData config) { config.CorpseTypes = config.CorpseTypes.OrderBy(x => x.Key).ToDictionary(pair => pair.Key, pair => pair.Value); Config.WriteObject(config, true); } #endregion } }