using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Facepunch;
using Rust;
using Rust.Ai;
#region Changelogs and ToDo
/**********************************************************************
*
* 1.0.0 : - Initial release
* 1.0.1 : - Added a initialise check to avoid nre when renaming unfinished npc spawns
* - Added Update active npc when plugin is loaded
* - Added name Buildup to config
* 1.0.2 : - Added API : string GiveName(global::HumanNPC npc, string Title,bool UseName,bool UseLetter)
*
* if (BetterNpcNames != null) npc.displayName = (BetterNpcNames.Call<string>("GiveName",npc , configData.NpcTitle, true, true));
* 1.0.3 : - Added Corpse loot from npc inventory,belt,wearables
* - Added options to clear belt or wearables when populating the corpse
* - Fixed nullreference issue
* - Added check at serverstartup
* - Added API : string GiveName(ScarecrowNPC npc, string Title,bool UseName,bool UseLetter)
*
**********************************************************************/
#endregion
namespace Oxide.Plugins
{
[Info("Better NPC Names", "Krungh Crow", "1.0.3")]
[Description("Names the unnamed npc and fixes corpse displaynames")]
class BetterNpcNames : RustPlugin
{
#region Variables
string npcname;
System.Random Rand;
bool IsReady;
int NamingCount = 0;
#endregion
#region Configuration
void Init()
{
if (!LoadConfigVariables())
{
Puts("Config file issue detected. Please delete file, or check syntax and fix.");
return;
}
}
private ConfigData configData;
class ConfigData
{
[JsonProperty(PropertyName = "Name the npcplayertest")]
public bool UseTest = false;
[JsonProperty(PropertyName = "Npc Titles")]
public Titles Titles = new Titles();
[JsonProperty(PropertyName = "Name Buildup")]
public string Naming = "{Title} {RandomName} {RandomLetter}";
[JsonProperty(PropertyName = "Corpse setup")]
public CorpseData Corpse = new CorpseData();
}
class CorpseData
{
[JsonProperty(PropertyName = "Clear Deafault loot")]
public bool ClDefault = false;
[JsonProperty(PropertyName = "Clear Belt")]
public bool ClBelt = false;
[JsonProperty(PropertyName = "Clear Clothes")]
public bool ClClothes = false;
}
class Titles
{
[JsonProperty(PropertyName = "Frankenstein")]
public string Frank = "Frankenstein";
[JsonProperty(PropertyName = "Scarecrow")]
public string Crow = "Scarecrow";
[JsonProperty(PropertyName = "Scientist")]
public string Sci = "Scientist";
[JsonProperty(PropertyName = "UnderwaterDweller")]
public string UDwel = "UnderwaterDweller";
[JsonProperty(PropertyName = "TunnelDweller")]
public string TDwel = "TunnelDweller";
[JsonProperty(PropertyName = "npcplayertest")]
public string Test = "playertest";
}
private bool LoadConfigVariables()
{
try { configData = Config.ReadObject<ConfigData>(); }
catch { return false; }
SaveConf();
return true;
}
protected override void LoadDefaultConfig()
{
Puts("Fresh install detected Creating a new config file.");
configData = new ConfigData();
SaveConf();
}
void SaveConf() => Config.WriteObject(configData, true);
#endregion
#region external Hooks
List<ulong> BotReSpawns = new List<ulong>();
void OnBotReSpawnNPCSpawned(ScientistNPC npc, string profilename, string group) => BotReSpawns.Add((ulong)npc.userID);
void OnEntityKill(ScientistNPC npc)
{
if (npc == null)
return;
if (BotReSpawns.Contains((ulong)npc.userID))
BotReSpawns.Remove((ulong)npc.userID);
}
#endregion
#region Oxide Hooks
void OnServerInitialized(bool initial)
{
Rand = new System.Random();
if (initial)
{
IsReady = false;
CheckInit();
return;
}
IsReady = true;
if (IsReady) Puts("Server is Ready for NPC Naming");
NamingCount = 0;
foreach (BaseNetworkable baseNetworkable in BaseNetworkable.serverEntities.Where(NPC => NPC is global::NPCPlayer).Cast<global::NPCPlayer>())
{
global::NPCPlayer npc = (global::NPCPlayer)baseNetworkable;
string originalNaming = npc.displayName;
UpdateName(npc);
if (originalNaming != npc.displayName) { NamingCount++; }
}
if (NamingCount != 0) Puts($"{NamingCount} NPC's where renamed");
}
void OnEntitySpawned(global::NPCPlayer npc)
{
if (npc != null && IsReady) UpdateName(npc);
return;
}
void OnEntityDeath(global::NPCPlayer npc, HitInfo info)
{
if (npc == null || info == null) return;
npcname = npc.displayName.ToString();
ItemContainer[] source = { npc.inventory.containerMain, npc.inventory.containerWear, npc.inventory.containerBelt };
Inv npcInv = new Inv() { name = npc.displayName, };
NpcContents.Add((ulong)npc.userID, npcInv);
for (int i = 0; i < source.Length; i++)
{
foreach (var item in source[i].itemList)
{
npcInv.inventory[i].Add(new NpcInventory
{
ID = item.info.itemid,
amount = item.amount,
skinID = item.skin,
});
}
}
}
void OnEntitySpawned(NPCPlayerCorpse corpse)
{
if (corpse == null || corpse.IsDestroyed || !IsReady) return;
try
{
corpse.playerName = this.OverrideCorpseName(npcname);
ulong id = corpse.playerSteamID;
Inv npcInv = new Inv();
npcInv = NpcContents[id];
if (BotReSpawns.Contains(id)) return;
timer.Once(0.1f, () =>
{
if (corpse == null || corpse.IsDestroyed || !IsReady) return;
Item skull = ItemManager.CreateByName("skull.human", 1, 0);
ItemAmount Skull = new ItemAmount() { itemDef = skull.info, amount = 1, startAmount = 1 };
Item flesh = ItemManager.CreateByName("humanmeat.raw", 1, 0);
ItemAmount Flesh = new ItemAmount() { itemDef = flesh.info, amount = 6, startAmount = 1 };
Item fat = ItemManager.CreateByName("fat.animal", 1, 0);
ItemAmount Fat = new ItemAmount() { itemDef = fat.info, amount = 2, startAmount = 1 };
var dispenser = corpse.GetComponent<ResourceDispenser>();
if (dispenser != null && corpse != null)
{
if (Skull != null) dispenser.containedItems.Add(Skull);
if (Flesh != null) dispenser.containedItems.Add(Flesh);
if (Fat != null) dispenser.containedItems.Add(Fat);
dispenser.Initialize();
}
if (!NpcContents.ContainsKey(id) || corpse == null || corpse.IsDestroyed) return;
if (configData.Corpse.ClDefault) corpse.containers[0].Clear();//wipe main
for (int i = 0; i < npcInv.inventory.Length; i++)
{
foreach (var item in npcInv.inventory[i])
{
var giveItem = ItemManager.CreateByItemID(item.ID, item.amount, item.skinID);
if (!giveItem.MoveToContainer(corpse.containers[i], -1, true))
giveItem.Remove();
}
}
if (configData.Corpse.ClClothes) corpse.containers[1].Clear();//wipe clothing
if (configData.Corpse.ClBelt) corpse.containers[2].Clear();//wipe beltbar
timer.Once(5f, () => NpcContents?.Remove(id));
});
}
catch { }
}
protected virtual string OverrideCorpseName(string _playerName) { return _playerName; }
#endregion
#region API
private string GiveName(global::HumanNPC npc, string Title,bool UseName,bool UseLetter)
{
string RandomName = string.Empty;
string RLetter = string.Empty;
if (UseName)
{
string _RandomName = RandomUsernames.Get((int)(ulong)npc.userID);
RandomName = MakeCap(_RandomName);
}
if (UseLetter) RLetter = Letter();
npcname = (configData.Naming).Replace("{Title}", Title).Replace("{RandomName}", RandomName).Replace("{RandomLetter}", RLetter);
return npc.displayName = npcname;
}
private string GiveName(ScarecrowNPC npc, string Title, bool UseName, bool UseLetter)
{
string RandomName = string.Empty;
string RLetter = string.Empty;
if (UseName)
{
string _RandomName = RandomUsernames.Get((int)(ulong)npc.userID);
RandomName = MakeCap(_RandomName);
}
if (UseLetter) RLetter = Letter();
npcname = (configData.Naming).Replace("{Title}", Title).Replace("{RandomName}", RandomName).Replace("{RandomLetter}", RLetter);
return npc.displayName = npcname;
}
#endregion
#region Helpers Corpse
public Dictionary<ulong, Inv> NpcContents = new Dictionary<ulong, Inv>();
public class Inv
{
public string name;
public List<NpcInventory>[] inventory = { new List<NpcInventory>(), new List<NpcInventory>(), new List<NpcInventory>() };
}
public class NpcInventory
{
public int ID;
public int amount;
public ulong skinID;
}
#endregion
#region Helpers Naming
string Letter()//Generates random letter
{
int num = Rand.Next(0, 26);
char x = (char)('a' + num);
return " " + x.ToString().ToUpper();
}
public static string MakeCap(string name) //makes first letter of npc name Uppercase
{
if (string.IsNullOrEmpty(name)) return string.Empty;
char[] a = name.ToCharArray();
a[0] = char.ToUpper(a[0]);
return new string(a);
}
void UpdateName(global::NPCPlayer npc) //Names the Unnamed npc
{
npcname = npc.displayName.ToString();
if (npcname == npc.userID.ToString())
{
var _RandomName = RandomUsernames.Get((int)(ulong)npc.userID);
string RandomName = MakeCap(_RandomName);
string Title = string.Empty;
if (npc is ScientistNPC) Title = configData.Titles.Sci;
if (npc is ScarecrowNPC) Title = configData.Titles.Crow;
if (npc is FrankensteinPet) Title = configData.Titles.Frank;
if (npc is UnderwaterDweller) Title = configData.Titles.UDwel;
if (npc is TunnelDweller) Title = configData.Titles.TDwel;
else if (npc.ShortPrefabName.Contains("playertest")) Title = configData.Titles.Test;//npcplayer
npcname = (configData.Naming).Replace("{Title}", Title).Replace("{RandomName}", RandomName).Replace("{RandomLetter}", Letter());
if (npc.ShortPrefabName.Contains("playertest") && !configData.UseTest) return;
npc.displayName = npcname;
}
}
#endregion
#region Helpers Various
void CheckInit()//checks if server is fully initialised
{
timer.Once(10f, () =>
{
if (Rust.Application.isLoading)
{
Puts($"[Debugging] Server still loading....");
CheckInit();
return;
}
IsReady = true;
Puts($"[Debugging] Server is Ready for NPC Naming....");
});
}
#endregion
}
}