C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using System.Reflection;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
using Oxide.Game.Rust;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Facepunch;
using Rust;
//Supply signal name fix
namespace Oxide.Plugins
{
[Info("BotSpawn", "Steenamaroo", "1.8.4", ResourceId = 2580)]
[Description("Spawn tailored AI with kits at monuments, custom locations, or randomly.")]
class BotSpawn : RustPlugin
{
[PluginReference]
Plugin Vanish, Kits;
static BotSpawn botSpawn = new BotSpawn();
const string permAllowed = "botspawn.allowed";
bool HasPermission(string id, string perm) => permission.UserHasPermission(id, perm);
int no_of_AI = 0;
static System.Random random = new System.Random();
public List<ulong> coolDownPlayers = new List<ulong>();
public Dictionary<string, List<Vector3>> spawnLists = new Dictionary<string, List<Vector3>>();
public Dictionary<ulong, Timer> weaponCheck = new Dictionary<ulong, Timer>();
public Timer aridTimer;
public Timer temperateTimer;
public Timer tundraTimer;
public Timer arcticTimer;
bool isBiome(string name)
{
if (name == "BiomeArid" || name == "BiomeTemperate" || name == "BiomeTundra" || name == "BiomeArctic")
return true;
return false;
}
#region Data
class StoredData
{
public Dictionary<string, DataProfile> DataProfiles = new Dictionary<string, DataProfile>();
public Dictionary<string, ProfileRelocation> MigrationDataDoNotEdit = new Dictionary<string, ProfileRelocation>();
public StoredData()
{
}
}
StoredData storedData;
#endregion
void Init()
{
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
var filter = RustExtension.Filter.ToList(); //Thanks Fuji.:)
filter.Add("cover points");
filter.Add("resulted in a conflict");
RustExtension.Filter = filter.ToArray();
no_of_AI = 0;
LoadConfigVariables();
}
void Loaded()
{
spawnLists.Add("AridSpawns", new List<Vector3>());
spawnLists.Add("TemperateSpawns", new List<Vector3>());
spawnLists.Add("TundraSpawns", new List<Vector3>());
spawnLists.Add("ArcticSpawns", new List<Vector3>());
lang.RegisterMessages(messages, this);
permission.RegisterPermission(permAllowed, this);
storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("BotSpawn");
SaveData();
}
void OnServerInitialized()
{
FindMonuments();
}
void Unload()
{
var filter = RustExtension.Filter.ToList();
filter.Remove("cover points");
filter.Remove("resulted in a conflict");
RustExtension.Filter = filter.ToArray();
Wipe();
}
bool isAuth(BasePlayer player)
{
if (player.net.connection != null)
if (player.net.connection.authLevel < 2)
return false;
return true;
}
void UpdateRecords(NPCPlayerApex player)
{
if (TempRecord.NPCPlayers.Contains(player))
TempRecord.NPCPlayers.Remove(player);
if (weaponCheck.ContainsKey(player.userID))
{
weaponCheck[player.userID].Destroy();
weaponCheck.Remove(player.userID);
}
}
void Wipe()
{
foreach (var bot in TempRecord.NPCPlayers)
{
if (bot == null)
continue;
else
bot.Kill();
}
TempRecord.NPCPlayers.Clear();
}
// Facepunch.RandomUsernames
public static string Get(ulong v) //credit Fujikura.{
return Facepunch.RandomUsernames.Get((int)(v % 2147483647uL));
}
#region BiomeSpawnsSetup
void GenerateSpawnPoints(List<Vector3> spawnlist, string name, int number, Timer myTimer, int biomeNo)
{
int getBiomeAttempts = 0;
myTimer = timer.Repeat(0.1f, 0, () =>
{
int halfish = Convert.ToInt16((ConVar.Server.worldsize / 2) / 1.1f);
int x = x = random.Next(-halfish, halfish);
int z = random.Next(-halfish, halfish);
Vector3 randomSpot = new Vector3(x, 0, z);
bool finished = true;
if (spawnlist.Count < number)
{
getBiomeAttempts++;
if (getBiomeAttempts > 50 && spawnlist.Count == 0)
{
Puts($"Failed to find spawnpoints in {name}.");
myTimer.Destroy();
return;
}
finished = false;
x = random.Next(-halfish, halfish);
z = random.Next(-halfish, halfish);
if (TerrainMeta.BiomeMap.GetBiome(randomSpot, biomeNo) > 0.5f
&& CalculateGroundPos(new Vector3(randomSpot.x, 200, randomSpot.z), true) != new Vector3())
spawnlist.Add(CalculateGroundPos(new Vector3(randomSpot.x, 200, randomSpot.z), true));
}
if (finished)
{
int i = 0;
timer.Repeat(2, number, () =>
{
SpawnBots(name, TempRecord.AllProfiles[name], "biome", null, spawnlist[i]);
i++;
});
myTimer.Destroy();
}
});
}
public static Vector3 CalculateGroundPos(Vector3 sourcePos, bool Biome)
{
RaycastHit hitInfo;
var cast = Physics.Raycast(sourcePos, Vector3.down, out hitInfo, 1000f, LayerMask.GetMask("Terrain", "Water", "World"), QueryTriggerInteraction.Ignore);
if (!hitInfo.collider.name.Contains("rock"))
cast = Physics.Raycast(sourcePos, Vector3.down, out hitInfo, 1000f, LayerMask.GetMask("Terrain", "Water"), QueryTriggerInteraction.Ignore);
if (hitInfo.collider.tag == "Main Terrain" || hitInfo.collider.name.Contains("rock"))
{
sourcePos.y = hitInfo.point.y;
return sourcePos;
}
return new Vector3();
}
Vector3 TryGetSpawn(Vector3 centerPoint, int radius)
{
int attempts = 0;
var spawnPoint = new Vector3();
while (attempts < 50 && spawnPoint == new Vector3())
{
attempts++;
int X = random.Next((-radius), (radius));
int Z = random.Next((-radius), (radius));
Vector3 testPoint = new Vector3((centerPoint.x + X), 200, (centerPoint.z + Z));
if (CalculateGroundPos(testPoint, false) != new Vector3())
{
spawnPoint = CalculateGroundPos(testPoint, false);
}
}
return spawnPoint;
}
#endregion
#region BotSetup
void AttackPlayer(Vector3 location, string name, DataProfile profile, string group)
{
timer.Repeat(1f, profile.Bots, () => SpawnBots(name, profile, "Attack", group, location));
}
void SpawnBots(string name, DataProfile zone, string type, string group, Vector3 location)
{
var pos = new Vector3(zone.LocationX, zone.LocationY, zone.LocationZ);
var finalPoint = new Vector3();
if (location != new Vector3())
pos = location;
var randomTerrainPoint = TryGetSpawn(pos, zone.Radius);
if (randomTerrainPoint == new Vector3())
{
Puts($"Failed to find a suitable spawnpoint : Moving on to next.{name}");
return;
}
else
{
finalPoint = randomTerrainPoint + new Vector3(0, 0.5f, 0);
}
if (zone.Chute)
{
if (type == "AirDrop")
finalPoint = (pos - new Vector3(0, 40, 0));
else
finalPoint = new Vector3(randomTerrainPoint.x, 200, (randomTerrainPoint.z));
}
NPCPlayer entity = (NPCPlayer)InstantiateSci(finalPoint, Quaternion.Euler(0, 0, 0), zone.Murderer, type);
var botapex = entity.GetComponent<NPCPlayerApex>();
TempRecord.NPCPlayers.Add(botapex);
var bData = botapex.gameObject.AddComponent<botData>();
bData.monumentName = name;
botapex.Spawn();
botapex.AiContext.Human.NextToolSwitchTime = Time.realtimeSinceStartup * 10;
botapex.AiContext.Human.NextWeaponSwitchTime = Time.realtimeSinceStartup * 10;
botapex.CommunicationRadius = 0;
no_of_AI++;
if (group != null)
bData.group = group;
else
bData.group = null;
bData.spawnPoint = randomTerrainPoint;
bData.accuracy = zone.Bot_Accuracy;
bData.damage = zone.Bot_Damage;
bData.respawn = true;
bData.roamRange = zone.Roam_Range;
bData.dropweapon = zone.Weapon_Drop;
bData.keepAttire = zone.Keep_Default_Loadout;
bData.peaceKeeper = zone.Peace_Keeper;
bData.chute = zone.Chute;
bData.peaceKeeper_CoolDown = zone.Peace_Keeper_Cool_Down;
botapex.startHealth = zone.BotHealth;
botapex.InitializeHealth(zone.BotHealth, zone.BotHealth);
if (type == "biome")
bData.biome = true;
if (zone.Chute)
addChute(botapex, finalPoint);
int kitRnd;
kitRnd = random.Next(zone.Kit.Count);
if (zone.BotNames.Count == zone.Kit.Count && zone.Kit.Count != 0)
setName(zone, botapex, kitRnd);
else
setName(zone, botapex, random.Next(zone.BotNames.Count));
giveKit(botapex, zone, kitRnd);
sortWeapons(botapex);
int suicInt = random.Next((zone.Suicide_Timer), (zone.Suicide_Timer + 10)); //slightly randomise suicide de-spawn time
if (type == "AirDrop" || type == "Attack")
{
bData.respawn = false;
runSuicide(botapex, suicInt);
}
if (zone.Disable_Radio)
botapex.RadioEffect = new GameObjectRef();
botapex.Stats.AggressionRange = zone.Sci_Aggro_Range;
botapex.Stats.DeaggroRange = zone.Sci_DeAggro_Range;
}
BaseEntity InstantiateSci(Vector3 position, Quaternion rotation, bool murd, string type) //Spawn population spam fix - credit Fujikura
{
string prefabname = "assets/prefabs/npc/scientist/scientist.prefab";
if (murd)
prefabname = "assets/prefabs/npc/murderer/murderer.prefab";
var prefab = GameManager.server.FindPrefab(prefabname);
GameObject gameObject = Instantiate.GameObject(prefab, position, rotation);
gameObject.name = prefabname;
SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
if (gameObject.GetComponent<Spawnable>())
UnityEngine.Object.Destroy(gameObject.GetComponent<Spawnable>());
if (!gameObject.activeSelf)
gameObject.SetActive(true);
BaseEntity component = gameObject.GetComponent<BaseEntity>();
return component;
}
void addChute(NPCPlayerApex botapex, Vector3 newPos)
{
float wind = random.Next(0, 50);
float fall = random.Next(40, 80);
var rb = botapex.gameObject.GetComponent<Rigidbody>();
rb.isKinematic = false;
rb.useGravity = false;
rb.drag = 0f;
botapex.gameObject.layer = 3;
rb.velocity = new Vector3((botapex.transform.forward.x * (wind / 20f)), 0, (botapex.transform.forward * (wind / 20f)).z) - new Vector3(0, (fall / 10f), 0);
var col = botapex.gameObject.AddComponent<BoxCollider>();
col.size = new Vector3(1, 1f, 1); //feet above ground
var Chute = GameManager.server.CreateEntity("assets/prefabs/misc/parachute/parachute.prefab", newPos, Quaternion.Euler(0, 0, 0));
Chute.gameObject.Identity();
Chute.SetParent(botapex, "parachute");
Chute.Spawn();
}
void setName(DataProfile zone, NPCPlayerApex botapex, int number)
{
if (zone.BotNames.Count == 0 || zone.BotNames.Count <= number)
{
botapex.displayName = Get(botapex.userID);
botapex.displayName = char.ToUpper(botapex.displayName[0]) + botapex.displayName.Substring(1);
}
else if (zone.BotNames[number] == "")
{
botapex.displayName = Get(botapex.userID);
botapex.displayName = char.ToUpper(botapex.displayName[0]) + botapex.displayName.Substring(1);
}
else
botapex.displayName = zone.BotNames[number];
if (zone.BotNamePrefix != "")
botapex.displayName = zone.BotNamePrefix + " " + botapex.displayName;
}
void giveKit(NPCPlayerApex botapex, DataProfile zone, int kitRnd)
{
var bData = botapex.GetComponent<botData>();
if (zone.Kit.Count != 0)
{
if (zone.Kit[kitRnd] != null)
{
object checkKit = (Kits.CallHook("GetKitInfo", zone.Kit[kitRnd], true));
if (checkKit == null)
{
if (zone.Murderer)
PrintWarning($"Kit {zone.Kit[kitRnd]} does not exist - Spawning default Murderer.");
else
PrintWarning($"Kit {zone.Kit[kitRnd]} does not exist - Spawning default Scientist.");
}
else
{
bool weaponInBelt = false;
if (checkKit != null && checkKit is JObject)
{
JObject kitContents = checkKit as JObject;
JArray items = kitContents["items"] as JArray;
foreach (var weap in items)
{
JObject item = weap as JObject;
if (item["container"].ToString() == "belt")
weaponInBelt = true; //doesn't actually check for weapons - just any item.}
}
if (!weaponInBelt)
{
if (zone.Murderer)
PrintWarning($"Kit {zone.Kit[kitRnd]} has no items in belt - Spawning default Murderer.");
else
PrintWarning($"Kit {zone.Kit[kitRnd]} has no items in belt - Spawning default Scientist.");
}
else
{
if (bData.keepAttire == false)
botapex.inventory.Strip();
Kits.Call($"GiveKit", botapex, zone.Kit[kitRnd], true);
if (!(kitList.ContainsKey(botapex.userID)))
{
kitList.Add(botapex.userID, new kitData
{
Kit = zone.Kit[kitRnd],
Wipe_Belt = zone.Wipe_Belt,
Wipe_Clothing = zone.Wipe_Clothing,
Allow_Rust_Loot = zone.Allow_Rust_Loot,
});
}
}
}
}
}
else
{
if (!(kitList.ContainsKey(botapex.userID)))
{
kitList.Add(botapex.userID, new kitData
{
Kit = "",
Wipe_Belt = zone.Wipe_Belt,
Wipe_Clothing = zone.Wipe_Clothing,
Allow_Rust_Loot = zone.Allow_Rust_Loot,
});
}
}
}
void sortWeapons(NPCPlayerApex botapex)
{
var bData = botapex.GetComponent<botData>();
foreach (Item item in botapex.inventory.containerBelt.itemList) //store organised weapons lists
{
var held = item.GetHeldEntity();
if (held as HeldEntity != null)
{
if (held.name.Contains("bow") || held.name.Contains("launcher"))
continue;
if (held as BaseMelee != null || held as TorchWeapon != null)
bData.MeleeWeapons.Add(item);
else
{
if (held as BaseProjectile != null)
{
bData.AllProjectiles.Add(item);
if (held.name.Contains("m92") || held.name.Contains("pistol") || held.name.Contains("python") || held.name.Contains("waterpipe"))
bData.CloseRangeWeapons.Add(item);
else if (held.name.Contains("bolt"))
bData.LongRangeWeapons.Add(item);
else
bData.MediumRangeWeapons.Add(item);
}
}
}
}
weaponCheck.Add(botapex.userID, timer.Repeat(2, 0, () => SelectWeapon(botapex)));
}
void runSuicide(NPCPlayerApex botapex, int suicInt)
{
if (TempRecord.NPCPlayers.Contains(botapex))
{
timer.Once(suicInt, () =>
{
if (botapex != null)
{
if (botapex.AttackTarget != null && Vector3.Distance(botapex.transform.position, botapex.AttackTarget.transform.position) < 10 && botapex.GetNavAgent.isOnNavMesh)
{
if (botapex.AttackTarget != null)
{
var position = botapex.AttackTarget.transform.position;
botapex.svActiveItemID = 0;
botapex.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
botapex.inventory.UpdatedVisibleHolsteredItems();
timer.Repeat(0.05f, 100, () =>
{
if (botapex == null) return;
botapex.SetDestination(position);
});
}
}
timer.Once(4, () =>
{
if (botapex == null) return;
Effect.server.Run("assets/prefabs/weapons/rocketlauncher/effects/rocket_explosion.prefab", botapex.transform.position);
HitInfo nullHit = new HitInfo();
nullHit.damageTypes.Add(Rust.DamageType.Explosion, 10000);
botapex.IsInvinsible = false;
botapex.Die(nullHit);
}
);
}
});
}
}
#endregion
static BasePlayer FindPlayerByName(string name)
{
BasePlayer result = null;
foreach (BasePlayer current in BasePlayer.activePlayerList)
{
if (current.displayName.Equals(name, StringComparison.OrdinalIgnoreCase))
{
BasePlayer result2 = current;
return result2;
}
if (current.UserIDString.Contains(name, CompareOptions.OrdinalIgnoreCase))
{
BasePlayer result2 = current;
return result2;
}
if (current.displayName.Contains(name, CompareOptions.OrdinalIgnoreCase))
{
result = current;
}
}
return result;
}
#region Hooks
object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
var botMelee = info.Initiator as BaseMelee;
bool melee = false;
if (botMelee != null)
{
melee = true;
info.Initiator = botMelee.GetOwnerPlayer();
}
NPCPlayerApex bot = null;
if (entity is NPCPlayerApex)
{
bot = entity as NPCPlayerApex;
if (!TempRecord.NPCPlayers.Contains(bot))
return null;
var bData = bot.GetComponent<botData>();
if (info.Initiator?.ToString() == null && configData.Global.Pve_Safe)
info.damageTypes.ScaleAll(0);
if (info.Initiator is BasePlayer && !(info.Initiator is NPCPlayer))
{
var canNetwork = Vanish?.Call("IsInvisible", info.Initiator); //bots wont retaliate to vanished players
if ((canNetwork is bool))
if ((bool)canNetwork)
info.Initiator = null;
if (bData.peaceKeeper) //prevent melee farming with peacekeeper on
{
var heldMelee = info.Weapon as BaseMelee;
var heldTorchWeapon = info.Weapon as TorchWeapon;
if (heldMelee != null || heldTorchWeapon != null)
info.damageTypes.ScaleAll(0);
}
}
bData.goingHome = false;
}
if (info?.Initiator is NPCPlayer && entity is BasePlayer) //add in bot accuracy
{
var attacker = info.Initiator as NPCPlayerApex;
if (TempRecord.NPCPlayers.Contains(attacker))
{
var bData = attacker.GetComponent<botData>();
int rand = random.Next(1, 100);
float distance = (Vector3.Distance(info.Initiator.transform.position, entity.transform.position));
var newAccuracy = (bData.accuracy * 10f);
var newDamage = (bData.damage);
if (distance > 100f)
{
newAccuracy = ((bData.accuracy * 10f) / (distance / 100f));
newDamage = bData.damage / (distance / 100f);
}
if (!melee && newAccuracy < rand) //scale bot attack damage
return true;
info.damageTypes.ScaleAll(newDamage);
}
}
return null;
}
void OnEntityKill(BaseNetworkable entity)
{
NPCPlayerApex npc = entity as NPCPlayerApex;
if (npc != null && TempRecord.NPCPlayers.Contains(npc))
{
var bData = npc.GetComponent<botData>();
Item activeItem = npc.GetActiveItem();
if (bData.dropweapon == true && activeItem != null)
{
using (TimeWarning timeWarning = TimeWarning.New("PlayerBelt.DropActive", 0.1f))
{
activeItem.Drop(npc.eyes.position, new Vector3(), new Quaternion());
npc.svActiveItemID = 0;
npc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
kitRemoveList.Add(npc.userID, activeItem.info.name);
}
}
if (TempRecord.AllProfiles[bData.monumentName].Disable_Radio == true)
{
npc.DeathEffect = new GameObjectRef(); //kill radio effects
}
DeadNPCPlayerIds.Add(npc.userID);
no_of_AI--;
if (bData.respawn == false)
{
UnityEngine.Object.Destroy(npc.GetComponent<botData>());
UpdateRecords(npc);
return;
}
else if (bData.biome)
{
List<Vector3> spawnList = new List<Vector3>();
if (spawnLists.ContainsKey(bData.monumentName))
{
spawnList = spawnLists[bData.monumentName];
int spawnPos = random.Next(spawnList.Count);
timer.Once(TempRecord.AllProfiles[bData.monumentName].Respawn_Timer, () =>
{
if (TempRecord.AllProfiles.ContainsKey(bData.monumentName))
{
SpawnBots(bData.monumentName, TempRecord.AllProfiles[bData.monumentName], "biome", null, spawnList[spawnPos]);
}
});
return;
}
else return;
}
else
{
foreach (var profile in TempRecord.AllProfiles)
{
timer.Once(profile.Value.Respawn_Timer, () =>
{
if (profile.Key == bData.monumentName)
{
SpawnBots(profile.Key, profile.Value, null, null, new Vector3());
}
});
}
}
UnityEngine.Object.Destroy(npc.GetComponent<botData>());
UpdateRecords(npc);
}
}
void OnPlayerDie(BasePlayer player)
{
OnEntityKill(player);
}
public static readonly FieldInfo AllScientists = typeof(Scientist).GetField("AllScientists", (BindingFlags.Static | BindingFlags.NonPublic)); //NRE AskQuestion workaround
void OnEntitySpawned(BaseEntity entity) // handles smoke signals, backpacks, corpses(applying kit)
{
if (entity as Scientist != null)
AllScientists.SetValue((entity as Scientist), new HashSet<Scientist>());//NRE AskQuestion workaround
var KitDetails = new kitData();
if (entity != null)
{
var pos = entity.transform.position;
if (entity is NPCPlayerCorpse)
{
var corpse = entity as NPCPlayerCorpse;
corpse.ResetRemovalTime(configData.Global.Corpse_Duration);
if (kitList.ContainsKey(corpse.playerSteamID))
{
KitDetails = kitList[corpse.playerSteamID];
NextTick(() =>
{
if (corpse == null)
return;
List<Item> toDestroy = new List<Item>();
foreach (var item in corpse.containers[0].itemList)
{
if (item.ToString().ToLower().Contains("keycard") && configData.Global.Remove_KeyCard)
{
toDestroy.Add(item);
}
}
foreach (var item in toDestroy)
item.RemoveFromContainer();
if (!KitDetails.Allow_Rust_Loot)
{
corpse.containers[0].Clear();
corpse.containers[1].Clear();
corpse.containers[2].Clear();
}
if (KitDetails.Kit != "")
{
var tempbody = GameManager.server.CreateEntity("assets/prefabs/player/player.prefab", (corpse.transform.position - new Vector3(0, -100, 0)), corpse.transform.rotation).ToPlayer();
tempbody.Spawn();
Kits?.Call($"GiveKit", tempbody, KitDetails.Kit, true);
var source = new ItemContainer[] { tempbody.inventory.containerMain, tempbody.inventory.containerWear, tempbody.inventory.containerBelt };
for (int i = 0; i < (int)source.Length; i++)
{
Item[] array = source[i].itemList.ToArray();
for (int j = 0; j < (int)array.Length; j++)
{
Item item = array[j];
if (!item.MoveToContainer(corpse.containers[i], -1, true))
{
item.Remove(0f);
}
}
}
tempbody.Kill();
}
if (kitList[corpse.playerSteamID].Wipe_Belt)
corpse.containers[2].Clear();
else
if (kitRemoveList.ContainsKey(corpse.playerSteamID))
{
foreach (var thing in corpse.containers[2].itemList) //If weapon drop is enabled, this removes the weapon from the corpse's inventory.{
if (kitRemoveList[corpse.playerSteamID] == thing.info.name)
{
thing.Remove();
kitRemoveList.Remove(corpse.playerSteamID);
break;
}
}
}
if (kitList[corpse.playerSteamID].Wipe_Clothing)
{
corpse.containers[1].Clear();
}
kitList.Remove(corpse.playerSteamID);
});
}
}
if (entity is DroppedItemContainer)
{
NextTick(() =>
{
if (entity == null || entity.IsDestroyed) return;
var container = entity as DroppedItemContainer;
ulong ownerID = container.playerSteamID;
if (ownerID == 0) return;
if (configData.Global.Remove_BackPacks)
{
if (DeadNPCPlayerIds.Contains(ownerID))
{
entity.Kill();
DeadNPCPlayerIds.Remove(ownerID);
return;
}
}
});
}
if (entity.name.Contains("grenade.supplysignal.deployed"))
timer.Once(2.3f, () =>
{
if (entity != null)
smokeGrenades.Add(new Vector3(entity.transform.position.x, 0, entity.transform.position.z));
});
if (!(entity.name.Contains("supply_drop")))
return;
Vector3 dropLocation = new Vector3(entity.transform.position.x, 0, entity.transform.position.z);
if (!(configData.Global.Supply_Enabled))
{
foreach (var location in smokeGrenades)
{
if (Vector3.Distance(location, dropLocation) < 35f)
{
smokeGrenades.Remove(location);
return;
}
}
}
if (TempRecord.AllProfiles.ContainsKey("AirDrop"))
{
var profile = TempRecord.AllProfiles["AirDrop"];
if (profile.AutoSpawn == true)
{
timer.Repeat(0.1f, profile.Bots, () =>
{
profile.LocationX = entity.transform.position.x;
profile.LocationY = entity.transform.position.y;
profile.LocationZ = entity.transform.position.z;
SpawnBots("AirDrop", profile, "AirDrop", null, new Vector3());
}
);
}
}
}
}
#endregion
#region WeaponSwitching
void SelectWeapon(NPCPlayerApex npcPlayer)
{
if (npcPlayer == null) return;
var victim = npcPlayer.AttackTarget;
var bData = npcPlayer.GetComponent<botData>();
if (bData == null) return;
if (npcPlayer.svActiveItemID == 0)
return;
var active = npcPlayer.GetActiveItem();
HeldEntity heldEntity1 = null;
if (active != null)
heldEntity1 = active.GetHeldEntity() as HeldEntity;
List<int> weapons = new List<int>(); //check all their weapons
foreach (Item item in npcPlayer.inventory.containerBelt.itemList)
{
var held = item.GetHeldEntity();
if (held is BaseProjectile || held is BaseMelee || held is TorchWeapon)
weapons.Add(Convert.ToInt16(item.position));
}
if (weapons.Count == 0)
{
PrintWarning(lang.GetMessage("noWeapon", this), bData.monumentName);
return;
}
if (npcPlayer.AttackTarget == null)
{
if (active != null) SetRange(npcPlayer);
int index = random.Next(weapons.Count);
var currentTime = TOD_Sky.Instance.Cycle.Hour;
uint UID = 0;
List<Item> lightList = new List<Item>();
if (active != null && active.contents != null)
lightList = active.contents.itemList;
if (currentTime > 20 || currentTime < 8)
{
if (active != null && !(active.ToString().Contains("flashlight")))
{
foreach (var mod in lightList)
{
if (mod.info.shortname.Contains("flashlight"))
return;
}
foreach (Item item in npcPlayer.inventory.containerBelt.itemList)
{
if (item.ToString().Contains("flashlight"))
{
if (heldEntity1 != null)
heldEntity1.SetHeld(false);
UID = item.uid;
}
if (item.contents != null)
foreach (var mod in item.contents.itemList)
{
if (mod.info.shortname.Contains("flashlight"))
UID = item.uid;
}
ChangeWeapon(npcPlayer, item);
}
}
}
else
{
if (active != null && active.ToString().Contains("flashlight"))
{
foreach (Item item in npcPlayer.inventory.containerBelt.itemList) //pick one at random to start with
{
if (item.position == weapons[index])
{
if (heldEntity1 != null)
heldEntity1.SetHeld(false);
ChangeWeapon(npcPlayer, item);
}
}
}
}
}
else
{
if (active != null) SetRange(npcPlayer);
if (heldEntity1 == null)
bData.currentWeaponRange = 0;
float distance = Vector3.Distance(npcPlayer.transform.position, victim.transform.position);
int newCurrentRange = 0;
int noOfAvailableWeapons = 0;
int selectedWeapon;
Item chosenWeapon = null;
HeldEntity held = null;
List<Item> rangeToUse = new List<Item>();
if (distance < 2f && bData.MeleeWeapons.Count != 0)
{
bData.enemyDistance = 1;
rangeToUse = bData.MeleeWeapons;
newCurrentRange = 1;
}
else if (distance > 1f && distance < 20f)
{
if (bData.CloseRangeWeapons.Count > 0)
{
bData.enemyDistance = 2;
rangeToUse = bData.CloseRangeWeapons;
newCurrentRange = 2;
}
else
{
rangeToUse = bData.MediumRangeWeapons;
newCurrentRange = 3;
}
}
else if (distance > 19f && distance < 40f && bData.MediumRangeWeapons.Count > 0)
{
bData.enemyDistance = 3;
rangeToUse = bData.MediumRangeWeapons;
newCurrentRange = 3;
}
else if (distance > 39)
{
if (bData.LongRangeWeapons.Count > 0)
{
bData.enemyDistance = 4;
rangeToUse = bData.LongRangeWeapons;
newCurrentRange = 4;
}
else
{
rangeToUse = bData.MediumRangeWeapons;
newCurrentRange = 3;
}
}
if (rangeToUse.Count > 0)
{
selectedWeapon = random.Next(rangeToUse.Count);
chosenWeapon = rangeToUse[selectedWeapon];
}
if (chosenWeapon == null) //if no weapon suited to range, pick any random bullet weapon
{ //prevents sticking with melee @>2m when no pistol is available
bData.enemyDistance = 5;
if (heldEntity1 != null && bData.AllProjectiles.Contains(active)) //prevents choosing a random weapon if the existing one is fine
return;
foreach (var weap in bData.AllProjectiles)
{
noOfAvailableWeapons++;
}
if (noOfAvailableWeapons > 0)
{
selectedWeapon = random.Next(bData.AllProjectiles.Count);
chosenWeapon = bData.AllProjectiles[selectedWeapon];
newCurrentRange = 5;
}
}
if (chosenWeapon == null) return;
if (newCurrentRange == bData.currentWeaponRange) return;
bData.currentWeaponRange = newCurrentRange;
held = chosenWeapon.GetHeldEntity() as HeldEntity;
if (heldEntity1 != null && heldEntity1.name == held.name)
return;
if (heldEntity1 != null && heldEntity1.name != held.name)
heldEntity1.SetHeld(false);
ChangeWeapon(npcPlayer, chosenWeapon);
}
}
[ConsoleCommand("botspawn")]
private void cmdBotSpawn(ConsoleSystem.Arg arg)
{
if (arg.Args == null || arg.Args.Length != 2) return;
foreach (var bot in TempRecord.NPCPlayers.Where(bot => bot.GetComponent<botData>().monumentName == arg.Args[0]))
foreach (var gun in bot.GetComponent<botData>().AllProjectiles.Where(gun=>gun.info.shortname == arg.Args[1]))
ChangeWeapon(bot, gun);
}
void ChangeWeapon(NPCPlayerApex botapex, Item item)
{
Item activeItem1 = botapex.GetActiveItem();
botapex.svActiveItemID = 0U;
if (activeItem1 != null)
{
HeldEntity heldEntity = activeItem1.GetHeldEntity() as HeldEntity;
if (heldEntity != null)
heldEntity.SetHeld(false);
}
botapex.svActiveItemID = item.uid;
botapex.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
Item activeItem2 = botapex.GetActiveItem();
if (activeItem2 != null)
{
HeldEntity heldEntity = activeItem2.GetHeldEntity() as HeldEntity;
if (heldEntity != null)
heldEntity.SetHeld(true);
}
botapex.inventory.UpdatedVisibleHolsteredItems();
botapex.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
SetRange(botapex);
}
void SetRange(NPCPlayerApex npcPlayer)
{
if (npcPlayer != null && npcPlayer is NPCMurderer)
{
if (npcPlayer.GetComponent<botData>() == null || npcPlayer.GetHeldEntity() == null || (npcPlayer.GetHeldEntity() as AttackEntity) == null)
return;
var bData = npcPlayer.GetComponent<botData>();
var held = npcPlayer.GetHeldEntity() as AttackEntity;
if (bData.currentWeaponRange == 0 || bData.currentWeaponRange == 1)
held.effectiveRange = 2;
else if (bData.currentWeaponRange == 2 || bData.currentWeaponRange == 3)
held.effectiveRange = 100;
else if (bData.currentWeaponRange == 5)
held.effectiveRange = 200;
}
}
#endregion
#region behaviour hooks
object OnNpcPlayerResume(NPCPlayerApex player)
{
if (TempRecord.NPCPlayers.Contains(player))
{
var bData = player.GetComponent<botData>();
if (bData.inAir) return false;
}
return null;
}
object OnNpcDestinationSet(NPCPlayerApex player)
{
if (TempRecord.NPCPlayers.Contains(player))
{
var bData = player.GetComponent<botData>();
if (bData.goingHome)
{
player.Destination = bData.spawnPoint;
return true;
}
return null;
}
return null;
}
#endregion
#region OnNpcHooks
object OnNpcPlayerTarget(NPCPlayerApex npcPlayer, BaseEntity entity)
{
var path = TempRecord.NPCPlayers;
var conf = configData.Global;
if (npcPlayer == null || entity == null)
return null;
if (entity is NPCPlayer)
{
NPCPlayer botVictim = entity as NPCPlayer;
if (npcPlayer == botVictim) return null;
if (path.Contains(botVictim) && !(path.Contains(npcPlayer)) && !(conf.NPCs_Attack_BotSpawn)) //stop oustideNPCs attacking BotSpawn bots
return true;
if (!TempRecord.NPCPlayers.Contains(npcPlayer))
return null;
if (!path.Contains(botVictim) && !(conf.BotSpawn_Attacks_NPCs)) //stop BotSpawn bots attacking outsideNPCs
return true;
if (path.Contains(botVictim) && !(conf.BotSpawn_Attacks_BotSpawn)) //stop BotSpawn murd+sci fighting each other
return true;
if (TempRecord.NPCPlayers.Contains(npcPlayer) && npcPlayer.GetType() == botVictim.GetType())
return true;
}
if (!TempRecord.NPCPlayers.Contains(npcPlayer))
return null;
if (entity is BasePlayer)
{
BasePlayer victim = entity as BasePlayer;
var active = npcPlayer.GetActiveItem();
var bData =
// ... [Code preview truncated due to length. Full source in attached file] ...