C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
#region License (GPL v2)
/*
MonBots - NPC Players that protect monuments, sort of
Copyright (c) 2021-2023 RFC1920 <desolationoutpostpve@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License v2.0.This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.Optionally you can also view the license at <http://www.gnu.org/licenses/>.*/
#endregion License (GPL v2)
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("MonBots", "RFC1920", "1.0.24")]
[Description("Adds interactive NPCs at various monuments")]
internal class MonBots : RustPlugin
{
#region vars
[PluginReference]
private readonly Plugin Kits, ZoneManager;
private ConfigData configData;
private const string sci = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam.prefab";
private List<ulong> isopen = new List<ulong>();
//private const string permNPCGuiUse = "monbot.use";
private const string NPCGUI = "monbot.editor";
private const string NPCGUK = "monbot.kitselect";
private const string NPCGUM = "monbot.monselect";
private const string NPCGUN = "monbot.kitsetnames";
private const string NPCGUP = "monbot.newprofile";
private readonly List<string> guis = new List<string>() { NPCGUI, NPCGUK, NPCGUM, NPCGUN, NPCGUP };
private bool newsave;
private bool do1017;
public static MonBots Instance;
public SortedDictionary<string, SpawnProfile> spawnpoints = new SortedDictionary<string, SpawnProfile>();
// private Dictionary<string, MonBotsZoneMap> zonemaps = new Dictionary<string, MonBotsZoneMap>();
private Dictionary<ulong, MonBotPlayer> hpcacheid = new Dictionary<ulong, MonBotPlayer>();
private static SortedDictionary<string, Vector3> monPos = new SortedDictionary<string, Vector3>();
private static SortedDictionary<string, Vector3> monSize = new SortedDictionary<string, Vector3>();
private static SortedDictionary<string, Vector3> cavePos = new SortedDictionary<string, Vector3>();
private Dictionary<ulong, Inventories> InvCache = new Dictionary<ulong, Inventories>();
private readonly static int playerMask = LayerMask.GetMask("Player (Server)");
#endregion vars
#region Message
private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
private void Message(IPlayer player, string key, params object[] args) => player.Reply(Lang(key, player.Id, args));
private void DoLog(string message)
{
if (configData.Options.debug)
{
Interface.GetMod().LogInfo($"{Name}: {message}");
}
}
#endregion Message
#region global
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["add"] = "Add",
["addhere"] = "Add Here",
["at"] = "at",
["cancel"] = "Cancel",
["close"] = "Close",
["debug"] = "Debug set to {0}",
["delete"] = "DELETE",
["detectrange"] = "Detect Range",
["dropweapon"] = "DropWeapon",
["edit"] = "Edit",
["editing"] = "Editing",
["end"] = "End",
["gotospawn"] = "Go There",
["hostile"] = "Hostile",
["silent"] = "Silence Effects",
["invulnerable"] = "Invulnerable",
["kit(s)"] = "Kit(s)",
["lootable"] = "Lootable",
["wipeclothing"] = "Wipe clothing",
["wipebelt"] = "Wipe belt",
["wipemain"] = "Wipe main",
["wipecorpsemain"] = "Wipe corpse main",
["monbots"] = "MonBots",
["movehere"] = "Move Here",
["name"] = "Name",
["name(s)"] = "Name(s)",
["needselect"] = "Select NPC",
["new"] = "Create New",
["none"] = "None",
["npcgui"] = "MonBot GUI",
["npcguikit"] = "MonBot GUI Kit Select",
["npcguimon"] = "MonBot GUI Profile Select",
["npcguinames"] = "MonBot GUI Bot Names",
["profile"] = "Profile",
["respawn"] = "Respawn",
["respawntime"] = "Respawn Time",
["roamrange"] = "Roam Range",
["select"] = "Select",
["spawncount"] = "Spawn Count",
["spawnrange"] = "Spawn Range",
["start"] = "Start"
}, this);
}
private void OnServerInitialized()
{
LoadConfigVariables();
Instance = this;
AddCovalenceCommand("mb", "cmdMB");
FindMonuments();
LoadData();
LoadBots();
foreach (KeyValuePair<string, Vector3> mon in monPos)
{
if (!spawnpoints.ContainsKey(mon.Key))
{
AddProfile(mon.Value, mon.Key);
}
}
//SaveData();
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
foreach (string gui in guis)
{
CuiHelper.DestroyUi(player, gui);
}
if (isopen.Contains(player.userID))
{
isopen.Remove(player.userID);
}
}
}
private void OnNewSave() => newsave = true;
private void Unload()
{
foreach (BasePlayer player in BasePlayer.activePlayerList.Where(x => x.IsAdmin))
{
foreach (string gui in guis)
{
CuiHelper.DestroyUi(player, gui);
}
if (isopen.Contains(player.userID))
{
isopen.Remove(player.userID);
}
}
MonBotPlayer[] bots = UnityEngine.Object.FindObjectsOfType<MonBotPlayer>();
if (bots != null)
{
foreach (MonBotPlayer bot in bots)
{
UnityEngine.Object.Destroy(bot);
}
}
foreach (KeyValuePair<string, SpawnProfile> x in new Dictionary<string, SpawnProfile>(spawnpoints))
{
x.Value.pos = new List<Vector3>();
x.Value.ids = new List<ulong>();
}
SaveData();
}
#region commands
[Command("mb")]
private void cmdMB(IPlayer iplayer, string command, string[] args)
{
if (!iplayer.IsAdmin) return;
BasePlayer player = iplayer.Object as BasePlayer;
if (args.Length > 0)
{
DoLog(string.Join(",", args));
switch (args[0])
{
// The b version of the commands here are for the GUI, the non-B are left behind for command line people, if any
case "bsc":
{
if (args.Length > 2)
{
int intval = int.Parse(args[2]);
string monname = Base64Decode(args[1]);
spawnpoints[monname].spawnCount = intval;
SaveData();
NPCProfileEditGUI(player, monname);
}
}
break;
case "sc":
{
int intval = int.Parse(args.Last());
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
newarg.RemoveAt(newarg.Count - 1);
string monname = string.Join(" ", newarg);
spawnpoints[monname].spawnCount = intval;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "bsr":
{
if (args.Length > 2)
{
int intval = int.Parse(args[2]);
string monname = Base64Decode(args[1]);
spawnpoints[monname].spawnRange = intval;
SaveData();
NPCProfileEditGUI(player, monname);
}
}
break;
case "sr":
{
int intval = int.Parse(args.Last());
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
newarg.RemoveAt(newarg.Count - 1);
string monname = string.Join(" ", newarg);
spawnpoints[monname].spawnRange = intval;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "brt":
{
if (args.Length > 2)
{
int intval = int.Parse(args[2]);
string monname = Base64Decode(args[1]);
spawnpoints[monname].respawnTime = intval;
SaveData();
NPCProfileEditGUI(player, monname);
}
}
break;
case "rt":
{
int intval = int.Parse(args.Last());
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
newarg.RemoveAt(newarg.Count - 1);
string monname = string.Join(" ", newarg);
spawnpoints[monname].respawnTime = intval;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "bdr":
{
if (args.Length > 2)
{
float fval = float.Parse(args[2]);
string monname = Base64Decode(args[1]);
spawnpoints[monname].detectRange = fval;
SaveData();
NPCProfileEditGUI(player, monname);
}
}
break;
case "dr":
{
float fval = float.Parse(args.Last());
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
newarg.RemoveAt(newarg.Count - 1);
string monname = string.Join(" ", newarg);
spawnpoints[monname].detectRange = fval;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "brr":
{
if (args.Length > 2)
{
float fval = float.Parse(args[2]);
string monname = Base64Decode(args[1]);
spawnpoints[monname].roamRange = fval;
SaveData();
NPCProfileEditGUI(player, monname);
}
}
break;
case "rr":
{
float fval = float.Parse(args.Last());
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
newarg.RemoveAt(newarg.Count - 1);
string monname = string.Join(" ", newarg);
spawnpoints[monname].roamRange = fval;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "rs":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].respawn = !spawnpoints[monname].respawn;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "inv":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].invulnerable = !spawnpoints[monname].invulnerable;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "loot":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].lootable = !spawnpoints[monname].lootable;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "wipec":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].wipeClothing = !spawnpoints[monname].wipeClothing;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "wipeb":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].wipeBelt = !spawnpoints[monname].wipeBelt;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "wipem":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].wipeMain = !spawnpoints[monname].wipeMain;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "wipecm":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].wipeCorpseMain = !spawnpoints[monname].wipeCorpseMain;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "drop":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].dropWeapon = !spawnpoints[monname].dropWeapon;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "hostile":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].hostile = !spawnpoints[monname].hostile;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "silent":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].silent = !spawnpoints[monname].silent;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "names":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
NPCNamesGUI(player, monname);
}
break;
case "name":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
newarg.RemoveAt(0);
newarg.RemoveAt(newarg.Count - 1);
string monname = string.Join(" ", newarg);
SpawnProfile sp = spawnpoints[monname];
string botname = args[1];
string newname = args.Last();
if (sp.names == null || sp.names.Count == 0)
{
sp.names = new List<string>();
}
if (botname == "NEW_NAME" && !sp.names.Contains(newname))
{
sp.names.Add(newname);
}
else if (sp.names.Contains(botname))
{
sp.names.Remove(botname);
if (newname != "DELETE_ME")
{
sp.names.Add(newname);
}
}
SaveData();
NPCNamesGUI(player, monname);
}
break;
case "delete":
{
CuiHelper.DestroyUi(player, NPCGUI);
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
foreach (MonBotPlayer bot in UnityEngine.Object.FindObjectsOfType<MonBotPlayer>())
{
if (bot.info.monument == monname)
{
DoLog($"Killing bot {bot.info.displayName} at {monname}");
UnityEngine.Object.Destroy(bot);
}
}
spawnpoints.Remove(monname);
SaveData();
NPCProfileSelectGUI(player);
}
break;
case "gothere":
{
CuiHelper.DestroyUi(player, NPCGUI);
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
Vector3 newpos = spawnpoints[monname].monpos;
newpos.y = TerrainMeta.HeightMap.GetHeight(newpos);
Teleport(player, newpos);
}
break;
case "spawnhere":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
spawnpoints[monname].monpos = player.transform.position;
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "respawn":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
foreach (MonBotPlayer bot in UnityEngine.Object.FindObjectsOfType<MonBotPlayer>())
{
if (bot.info.monument == monname)
{
DoLog($"Killing bot {bot.info.displayName} at {monname}");
UnityEngine.Object.Destroy(bot);
}
}
LoadBots(monname);
NPCProfileEditGUI(player, monname);
}
break;
case "selkit":
{
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
NPCKitSelectGUI(player, monname);
}
break;
case "kitsel":
if (args.Length > 2)
{
CuiHelper.DestroyUi(player, NPCGUK);
string kit = args[1];
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
DoLog($"Checking kits for '{monname}'");
if (spawnpoints[monname].kits == null || spawnpoints[monname].kits.Count == 0)
{
spawnpoints[monname].kits = new List<string>();
}
if (spawnpoints[monname].kits.Contains(kit))
{
spawnpoints[monname].kits.Remove(kit);
}
else
{
spawnpoints[monname].kits.Add(kit);
}
SaveData();
NPCProfileEditGUI(player, monname);
}
break;
case "addprofile":
{
NPCNewProfileGUI(player);
}
break;
case "newprofile":
{
CuiHelper.DestroyUi(player, NPCGUP);
AddProfile(player.transform.position, args[1]);
NPCProfileEditGUI(player, args[1]);
}
break;
case "monsel":
if (args.Length > 1)
{
CuiHelper.DestroyUi(player, NPCGUM);
List<string> newarg = new List<string>(args);
newarg.RemoveAt(0);
string monname = string.Join(" ", newarg);
NPCProfileEditGUI(player, monname);
}
break;
case "newprofileclose":
CuiHelper.DestroyUi(player, NPCGUP);
break;
case "newprofilecancel":
CuiHelper.DestroyUi(player, NPCGUP);
NPCProfileSelectGUI(player);
break;
case "namesclose":
CuiHelper.DestroyUi(player, NPCGUN);
break;
case "selkitclose":
CuiHelper.DestroyUi(player, NPCGUK);
break;
case "selmonclose":
CuiHelper.DestroyUi(player, NPCGUM);
break;
case "close":
IsOpen(player.userID, false);
CuiHelper.DestroyUi(player, NPCGUI);
CuiHelper.DestroyUi(player, NPCGUK);
CuiHelper.DestroyUi(player, NPCGUM);
break;
}
}
else
{
foreach (string gui in guis)
{
CuiHelper.DestroyUi(player, gui);
}
NPCProfileSelectGUI(player);
}
}
#endregion commands
private void SpawnBot(ulong userid)
{
// Used to identify bot for respawn on death
if (hpcacheid.ContainsKey(userid))
{
DoLog($"{userid} found in cached ids!");
MonBotPlayer botinfo = hpcacheid[userid];
SpawnProfile sp = spawnpoints[botinfo.info.monument];
DoLog($"Respawning bot {botinfo.info.displayName} at {sp.monname}");
SpawnBot(sp, botinfo.spawnPos, botinfo.info.kit);
}
else
{
DoLog($"{userid} not found in cached ids :(");
}
}
private void SpawnBot(SpawnProfile sp, Vector3 pos, string kit)
{
// Used for initial spawn by LoadBots()
if (pos == default(Vector3)) return;
pos = AdjustSpawnPoint(pos, sp.roamRange / 2);
spawnpoints[sp.monname].pos.Add(pos);
DoLog($"Spawning bot at {pos}");
global::HumanNPC bot = (global::HumanNPC)GameManager.server.CreateEntity(sci, pos, new Quaternion(), true);
bot.Spawn();
spawnpoints[sp.monname].ids.Add(bot.userID);
string botname = "Bot";
if (sp.names != null)
{
botname = GetBotName(sp.names.ToArray());
}
NextTick(() =>
{
DoLog($"Adding Mono to bot {botname} at {sp.monname} ({pos})");
try
{
MonBotPlayer mono = bot.gameObject.AddComponent<MonBotPlayer>();
mono.spawnPos = pos;
DoLog("Setting info object");
mono.info = new MonBotInfo(bot.userID, bot.transform.position, bot.transform.rotation)
{
displayName = botname,
userid = bot.userID,
monument = sp.monname,
lootable = sp.lootable,
wipeClothing = sp.wipeClothing,
wipeBelt = sp.wipeBelt,
wipeMain = sp.wipeMain,
wipeCorpseMain = sp.wipeCorpseMain,
health = sp.startHealth,
invulnerable = sp.invulnerable,
hostile = sp.hostile,
silent = sp.silent,
dropWeapon = sp.dropWeapon,
detectRange = sp.detectRange,
roamRange = sp.roamRange,
respawnTimer = sp.respawnTime,
respawn = sp.respawn,
loc = pos,
kit = kit
};
//mono.UpdateHealth(mono.info);
bot.startHealth = sp.startHealth;
DoLog("Setting brain object");
bot.Brain.Navigator.Agent.agentTypeID = -1372625422;
bot.Brain.Navigator.DefaultArea = "Walkable";
bot.Brain.Navigator.Init(bot, bot.Brain.Navigator.Agent);
bot.Brain.ForceSetAge(0);
bot.Brain.TargetLostRange = mono.info.detectRange;
bot.Brain.HostileTargetsOnly = !mono.info.hostile;
bot.Brain.Navigator.BestCoverPointMaxDistance = mono.info.roamRange / 2;
bot.Brain.Navigator.BestRoamPointMaxDistance = mono.info.roamRange;
bot.Brain.Navigator.MaxRoamDistanceFromHome = mono.info.roamRange;
bot.Brain.Senses.Init(bot, bot.Brain, 5f, mono.info.roamRange, mono.info.detectRange, -1f, true, false, true, mono.info.detectRange, !mono.info.hostile, false, true, EntityType.Player, false);
bot.Brain.Navigator.Resume();
DoLog("Setting name and inventory");
bot.displayName = botname;
hpcacheid.Add(bot.userID, mono);
if (kit.Length > 0)
{
bot.inventory.Strip();
Kits?.Call("GiveKit", bot, kit);
}
if (mono.info.silent)
{
DoLog("Silencing effects");
ScientistNPC npc = bot as ScientistNPC;
npc.DeathEffects = new GameObjectRef[0];
npc.RadioChatterEffects = new GameObjectRef[0];
npc.radioChatterType = ScientistNPC.RadioChatterType.NONE;
npc.SetChatterType(ScientistNPC.RadioChatterType.NONE);
}
timer.Once(5f, () => mono.activeItem = bot.GetActiveItem());
}
catch //(Exception ex)
{
DoLog($"Unable to setup bot {botname} at {sp.monname} ({pos}) - Possible navmesh issue.");
}
});
if (bot.IsHeadUnderwater())
{
bot.Kill();
}
}
private void LoadBots(string profile = "", int quantity = 0)
{
DoLog("LoadBots called");
foreach (KeyValuePair<string, SpawnProfile> sp in new Dictionary<string, SpawnProfile>(spawnpoints))
{
if (profile.Length > 0 && !sp.Key.Equals(profile)) continue;
Vector3 spawnPos = sp.Value.monpos;
if (newsave && monPos.ContainsKey(sp.Key))
{
DoLog($"Changing {sp.Key} location due to wipe to {spawnPos}");
spawnPos = monPos[sp.Key];
}
else if (newsave && !monPos.ContainsKey(sp.Key) && sp.Value.spawnCount > 0)
{
Puts($"Server wipe was detected.Will not spawn bots for profile '{sp.Key}' since it was either custom or the monument does not exist.");
continue;
}
int spawnqty = quantity > 0 ? quantity : sp.Value.spawnCount;
if (spawnqty > 0)
{
DoLog($"Working on spawn at {sp.Key}");
for (int i = 0; i < spawnqty; i++)
{
string kit;
if (sp.Value.kits == null)
{
kit = "";
}
else if (sp.Value.kits.Count == 1)
{
kit = sp.Value.kits[0];
}
else
{
kit = sp.Value.kits.GetRandom();
}
SpawnBot(sp.Value, spawnPos, kit);
SaveData();
}
}
}
newsave = false;
}
private void LoadBots(Vector3 location, string profile, string group, int quantity = 0)
{
DoLog("LoadBots by location called");
foreach (KeyValuePair<string, SpawnProfile> sp in new Dictionary<string, SpawnProfile>(spawnpoints))
{
if (!sp.Key.Equals(profile)) continue;
sp.Value.zonemap.Add(group);
Vector3 spawnPos = location;
if (quantity > 0)
{
DoLog($"Working on location spawn at {sp.Key}");
for (int i = 0; i < quantity; i++)
{
string kit;
if (sp.Value.kits == null)
{
kit = "";
}
else if (sp.Value.kits.Count == 1)
{
kit = sp.Value.kits[0];
}
else
{
kit = sp.Value.kits.GetRandom();
}
SpawnBot(sp.Value, spawnPos, kit);
SaveData();
}
}
}
}
private bool BadLocation(Vector3 location)
{
// Avoid placing in a rock or foundation, water, etc.int layerMask = LayerMask.GetMask("Construction", "World", "Water");
RaycastHit hit;
if (Physics.Raycast(new Ray(location, Vector3.down), out hit, 6f, layerMask))
{
return true;
}
else if (Physics.Raycast(new Ray(location, Vector3.up), out hit, 6f, layerMask))
{
return true;
}
else if (Physics.Raycast(new Ray(location, Vector3.forward), out hit, 6f, layerMask))
{
return true;
}
return false;
//return (TerrainMeta.HeightMap.GetHeight(location) - TerrainMeta.WaterMap.GetHeight(location)) >= 0;
}
private Vector3 AdjustSpawnPoint(Vector3 pos, float radius)
{
Vector3 newpos;// = new Vector3() { x = pos.x, y = pos.y, z = pos.z };
Vector2 rand;
bool ok = false;
int i = 0;
const int max = 250;
pos.y = TerrainMeta.HeightMap.GetHeight(pos);
while (!ok)
{
i++;
rand = UnityEngine.Random.insideUnitCircle * radius;
newpos = pos + new Vector3(rand.x, 0, rand.y);
ok = !BadLocation(newpos);
if (ok || i >= max)
{
newpos.y = TerrainMeta.HeightMap.GetHeight(newpos);
if (i >= max)
{
DoLog($"Found meh spawn position after maxing out on checks at {max}");
}
else
{
DoLog($"Found adequate spawn position after {i} check(s)");
}
newpos.y = TerrainMeta.HeightMap.GetHeight(newpos);
return newpos;
}
}
return pos;
}
private string GetBotName(string[] names)
{
if (names.Length == 1 && !string.IsNullOrEmpty(names[0]))
{
return names[0];
}
else if (names.Length > 1)
{
return names.GetRandom();
}
return "Bot";
}
#endregion global
#region Oxide Hooks
private void LoadData()
{
spawnpoints = Interface.GetMod().DataFileSystem.ReadObject<SortedDictionary<string, SpawnProfile>>(Name + "/spawnpoints");
foreach (KeyValuePair<string, SpawnProfile> sp in spawnpoints)
{
DoLog($"Loaded profile {sp.Key}");
if (monPos.ContainsKey(sp.Key))
{
sp.Value.monpos = monPos[sp.Key];
}
if (do1017)
{
DoLog("Patching profile for 1.0.17 upgrade");
sp.Value.wipeClothing = true;
sp.Value.wipeBelt = true;
}
}
do1017 = false;
}
private void SaveData()
{
Interface.GetMod().DataFileSystem.WriteObject(Name + "/spawnpoints", spawnpoints);
}
private object OnServerCommand(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (player == null) return null;
if (!arg.cmd.FullName.StartsWith("mb") && isopen.Contains(ulong.Parse(player.UserIDString)))
{
return false;
}
return null;
}
private object OnPlayerCommand(IPlayer player, string command, string[] args)
{
if (command != "mb" && isopen.Contains(ulong.Parse(player.Id)))
{
if (configData.Options.debug) Puts($"OnPlayerCommand: {command} BLOCKED");
return true;
}
return null;
}
private void OnPlayerInput(BasePlayer player, InputState input)
{
if (player == null || input == null) return;
if (!input.WasJustPressed(BUTTON.USE)) return;
RaycastHit hit;
if (Physics.Raycast(player.eyes.HeadRay(), out hit, 3f, playerMask))
{
BasePlayer pl = hit.GetEntity().ToPlayer();
MonBotPlayer hp = pl.GetComponent<MonBotPlayer>();
if (hp == null) return;
Interface.GetMod().CallHook("OnUseNPC", hp.player, player);
SaveData();
}
}
private void OnEntityDeath(global::HumanNPC humannpc, HitInfo hitinfo)
{
if (humannpc == null) return;
MonBotPlayer npc = humannpc.GetComponent<MonBotPlayer>();
if (npc == null) return;
DoLog("OnEntityDeath: Found MonBot player");
hpcacheid.Remove((uint)humannpc.net.ID.Value);
if (npc.info.dropWeapon)
{
DoLog("OnEntityDeath: Attempting to drop weapon");
if (npc.activeItem != null)
{
DoLog($"Dropping {npc.info.displayName}'s activeItem: {npc.activeItem.info.shortname}");
Vector3 vector3 = new Vector3(UnityEngine.Random.Range(-2f, 2f), 0.2f, UnityEngine.Random.Range(-2f, 2f));
npc.activeItem.Drop(humannpc.GetDropPosition(), humannpc.GetInheritedDropVelocity() + (vector3.normalized * 3f));
humannpc.svActiveItemID.Value = 0;
}
}
DoLog($"Populating inventory cache for {npc.info.userid}");
ItemContainer[] sourceinv = { humannpc.inventory.containerMain, humannpc.inventory.containerWear, humannpc.inventory.containerBelt };
InvCache.Add(humannpc.userID, new Inventories());
for (int i = 0; i < sourceinv.Length; i++)
{
foreach (Item item in sourceinv[i].itemList)
{
InvCache[humannpc.userID].inventory[i].Add(new ItemInfo
{
ID = item.info.itemid,
amount = item.amount,
skinID = item.skin
});
}
}
DoLog($"DONE populating inventory cache for {npc.info.userid}");
DoLog("Checking respawn variable");
if (npc?.info.respawn == true)
{
DoLog($"Setting {npc.info.respawnTimer} second respawn timer for {npc.info.displayName} ({npc.info.userid})");
timer.Once(npc.info.respawnTimer, () => SpawnBot(npc.info.userid));
}
}
private void OnEntitySpawned(NPCPlayerCorpse corpse)
{
if (corpse == null) return;
ulong userid = corpse?.playerSteamID ?? 0;
if (hpcacheid.ContainsKey(userid))
{
MonBotPlayer npc = hpcacheid[userid];
DoLog($"Setting corpse loot panel name to {npc.info.displayName}");
corpse._playerName = npc.info.displayName;
corpse.lootPanelName = npc.info.displayName;
timer.Once(0.1f, () =>
{
DoLog("Checking corpse lootable flag");
if (npc.info.lootable && InvCache.ContainsKey(userid))
{
for (int i = 0; i < InvCache[userid].inventory.Length; i++)
{
// For main, if wipe is true, the corpse will get a default loot collection rather than what the NPC had
if ((corpse.containers[i].capacity == 24 && !npc.info.wipeMain) ||
(corpse.containers[i].capacity == 7 && !npc.info.wipeClothing) ||
(corpse.containers[i].capacity == 6 && !npc.info.wipeBelt))
{
DoLog($"Copying cached items back from live NPC for container {i}");
foreach (ItemInfo item in InvCache[userid].inventory[i])
{
Item giveItem = ItemManager.CreateByItemID(item.ID, item.amount, item.skinID);
DoLog($"Copying {item.amount} of item {giveItem.info.displayName.english}");
if (!giveItem.MoveToContainer(corpse.containers[i], -1, true))
{
DoLog("Failed to move item :(");
giveItem.Remove();
}
}
}
}
if (npc.info.wipeCorpseMain)
{
DoLog("Wiping corpse main inventory");
corpse.containers[0].Clear();
}
timer.Once(5f, () => InvCache.Remove(userid));
ItemManager.DoRemoves();
return;
}
for (int i = 0; i < corpse.containers.Length; i++)
{
DoLog($"Clearing corpse container {i}");
corpse.containers[i].Clear();
}
ItemManager.DoRemoves();
});
}
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitinfo)
{
if (entity == null) return null;
if (hitinfo == null) return null;
MonBotPlayer hp = entity.GetComponent<MonBotPlayer>();
if (hp != null)
{
Interface.GetMod().CallHook("OnHitNPC", entity.GetComponent<BaseCombatEntity>(), hitinfo);
if (hp.info.invulnerable)
{
//hitinfo.damageTypes = new DamageTypeList();
//hitinfo.DoHitEffects = false;
//hitinfo.HitMaterial = 0;
return true;
}
//else
//{
// hp.protection.Scale(hitinfo.damageTypes);
//}
}
return null;
}
private object CanLootPlayer(global::HumanNPC target, BasePlayer player)
{
if (player == null || target == null)
{
return null;
}
MonBotPlayer hp = target.GetComponentInParent<MonBotPlayer>();
if (hp?.info.lootable == false)
{
DoLog($"Player {player.displayName}:{player.UserIDString} looting MonBot {hp.info.displayName}");
NextTick(player.EndLooting);
return true;
}
return null;
}
private object CanLootEntity(BasePlayer player, NPCPlayerCorpse corpse)
{
if (player == null || corpse == null)
{
return null;
}
if (hpcacheid.ContainsKey(corpse.playerSteamID))
{
MonBotPlayer hp = hpcacheid[corpse.playerSteamID];
if (hp == null)
{
return null;
}
DoLog($"Player {player.displayName}:{player.UserIDString} looting MonBot {corpse.name}:{corpse.playerSteamID}");
if (!hp.info.lootable)
{
NextTick(player.EndLooting);
return true;
}
}
return null;
}
//private void OnLootPlayer(BasePlayer looter, BasePlayer target)
//{
// if (npcs.ContainsKey(target.userID))
// {
// Interface.GetMod().CallHook("OnLootNPC", loote
// ... [Code preview truncated due to length. Full source in attached file] ...