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 UnityEngine;
using Oxide.Game.Rust;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using UnityEngine.SceneManagement;
using Facepunch;
using Rust;
namespace Oxide.Plugins
{
[Info("BotSpawn", "Steenamaroo", "1.6.8", ResourceId = 2580)]
[Description("Spawn tailored AI with kits at monuments, custom locations, or randomly.")]
class BotSpawn : RustPlugin
{
[PluginReference]
Plugin Vanish, Kits;
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 Dictionary<string, List<Vector3>> spawnLists = new Dictionary<string, List<Vector3>>();
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()
{
}
}
///////////////////////////////////////////////////////////////////MIGRATION INFO TO REMOVE in 1.6.7
class StoredDataOld
{
public Dictionary<string, MonumentSettings> CustomProfiles = new Dictionary<string, MonumentSettings>();
public StoredDataOld()
{
}
}
public class MonumentSettings
{
public bool AutoSpawn = false;
public bool Murderer = false;
public int Bots = 5;
public int BotHealth = 100;
public int Radius = 100;
public List<string> Kit = new List<string>();
public string BotNamePrefix = "";
public List<string> BotNames = new List<string>();
public int Bot_Accuracy = 4;
public float Bot_Damage = 0.4f;
public int Respawn_Timer = 60;
public bool Disable_Radio = true;
public float LocationX;
public float LocationY;
public float LocationZ;
public int Roam_Range = 40;
public bool Peace_Keeper = true;
public int Peace_Keeper_Cool_Down = 5;
public bool Weapon_Drop = true;
public bool Keep_Default_Loadout = false;
public bool Wipe_Belt = true;
public bool Wipe_Clothing = true;
public bool Allow_Rust_Loot = true;
public int Suicide_Timer = 300;
public bool Chute = false;
public int Long_Attack_Distance = 120;
}
StoredDataOld storedDataOld;
///////////////////////////////////////////////////////////////////////////
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 OnServerInitialized()
{
FindMonuments();
}
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");
///////////////////////////////////////////////////////////////////////////MIGRATION INFO TO REMOVE in 1.6.7
storedDataOld = Interface.Oxide.DataFileSystem.ReadObject<StoredDataOld>("BotSpawn");
var oldDeser = JsonConvert.SerializeObject(storedDataOld);
if (oldDeser.ToString().Length != 28 && oldDeser.ToString().Length != 26)
{
oldDeser = oldDeser.Replace(("\"CustomProfiles\":"), ("\"DataProfiles\":"));
int lastPos = oldDeser.LastIndexOf('}');
oldDeser = oldDeser.Insert(lastPos, ",\"MigrationDataDoNotEdit\":{}");
var migratedData = JsonConvert.DeserializeObject<StoredData>(oldDeser);
Interface.Oxide.DataFileSystem.WriteObject("BotSpawn", migratedData);
storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("BotSpawn");
foreach (var profile in storedData.DataProfiles)
{
storedData.MigrationDataDoNotEdit.Add(profile.Key, new ProfileRelocation());
};
}
///////////////////////////////////////////////////////////////////////////
SaveData();
}
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);
}
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) //credit Wulf & Nogrod
{
RaycastHit hitInfo;
if (Physics.Raycast(sourcePos, Vector3.down, out hitInfo, 1000f, LayerMask.GetMask("Terrain", "World", "Construction", "Water"), QueryTriggerInteraction.Ignore))
{
if (hitInfo.collider.tag == "Main Terrain" && Biome)
{
sourcePos.y = hitInfo.point.y;
sourcePos.y = Mathf.Max(sourcePos.y, TerrainMeta.HeightMap.GetHeight(sourcePos));
return sourcePos;
}
if (!Biome)
{
sourcePos.y = hitInfo.point.y;
sourcePos.y = Mathf.Max(sourcePos.y, TerrainMeta.HeightMap.GetHeight(sourcePos));
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++;
RaycastHit hitInfo;
int X = random.Next((-radius), (radius));
int Z = random.Next((-radius), (radius));
if (CalculateGroundPos(new Vector3((centerPoint.x + X), 200, (centerPoint.z + Z)), false) != new Vector3())
{
spawnPoint = CalculateGroundPos(new Vector3(centerPoint.x + X, 200, centerPoint.z + Z), 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;
}
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);
var botapex = entity.GetComponent<NPCPlayerApex>();
TempRecord.NPCPlayers.Add(botapex);
botapex.Spawn();
botapex.SendNetworkUpdateImmediate();
no_of_AI++;
var bData = botapex.gameObject.AddComponent<botData>();
if (group != null)
bData.group = group;
else
bData.group = null;
bData.spawnPoint = CalculateGroundPos(randomTerrainPoint, false);
bData.accuracy = zone.Bot_Accuracy;
bData.damage = zone.Bot_Damage;
bData.health = zone.BotHealth;
bData.monumentName = name;
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;
if (type == "biome")
bData.biome = true;
if (zone.Long_Attack_Distance < 55) zone.Long_Attack_Distance = 55;
bData.LongRangeAttack = zone.Long_Attack_Distance;
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.VisionRange = zone.Long_Attack_Distance + 20f; // to allow out of range attack over ride
if (!zone.Murderer)
{
timer.Once(4, () =>
{
if (botapex != null)
{
botapex.Stats.IsMobile = true;
botapex.SetFact(NPCPlayerApex.Facts.IsRoamReady, 1, true, true);
botapex.SetFact(NPCPlayerApex.Facts.IsMoving, 1, true, true);
}
});
}
}
BaseEntity InstantiateSci(Vector3 position, Quaternion rotation, bool murd) //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, 0.5f, 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]} does not exist - 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);
}
}
}
}
}
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;
}
private void OnEntityKill(BaseNetworkable entity)
{
NPCPlayerApex npc = entity.GetComponent<NPCPlayerApex>();
if (npc != null && npc is NPCMurderer)
NPCSensesLoadBalancer.NpcSensesLoadBalancer.Remove(npc);
}
#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);
}
}
float multiplier = 100f / bData.health;
info.damageTypes.ScaleAll(multiplier);
}
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;
}
else
{
info.damageTypes.ScaleAll(newDamage);
return null;
}
}
}
return null;
}
void OnPlayerDie(BasePlayer player)
{
string respawnLocationName = "";
NPCPlayerApex Scientist = null;
if (player is NPCPlayerApex)
{
Scientist = player as NPCPlayerApex;
if (!TempRecord.NPCPlayers.Contains(Scientist))
return;
if (TempRecord.NPCPlayers.Contains(Scientist))
{
var bData = Scientist.GetComponent<botData>();
Item activeItem = player.GetActiveItem();
if (bData.dropweapon == true && activeItem != null)
{
using (TimeWarning timeWarning = TimeWarning.New("PlayerBelt.DropActive", 0.1f))
{
activeItem.Drop(player.eyes.position, new Vector3(), new Quaternion());
player.svActiveItemID = 0;
player.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
kitRemoveList.Add(player.userID, activeItem.info.name);
}
}
no_of_AI--;
respawnLocationName = bData.monumentName;
DeadNPCPlayerIds.Add(Scientist.userID);
if (TempRecord.AllProfiles[respawnLocationName].Disable_Radio == true)
Scientist.DeathEffect = new GameObjectRef(); //kill radio effects
if (bData.respawn == false)
{
UnityEngine.Object.Destroy(Scientist.GetComponent<botData>());
UpdateRecords(Scientist);
return;
}
if (bData.biome)
{
List<Vector3> spawnList = new List<Vector3>();
if (spawnLists.ContainsKey(bData.monumentName))
{
spawnList = spawnLists[bData.monumentName];
int spawnPos = random.Next(spawnList.Count);
SpawnBots(bData.monumentName, TempRecord.AllProfiles[bData.monumentName], "biome", null, spawnList[spawnPos]);
return;
}
else return;
}
foreach (var profile in TempRecord.AllProfiles)
{
timer.Once(profile.Value.Respawn_Timer, () => {
if (profile.Key == respawnLocationName)
{
SpawnBots(profile.Key, profile.Value, null, null, new Vector3());
}
});
UnityEngine.Object.Destroy(Scientist.GetComponent<botData>());
UpdateRecords(Scientist);
}
}
}
}
void OnEntitySpawned(BaseEntity entity) // handles smoke signals, backpacks, corpses(applying kit)
{
NPCPlayerApex npc = entity.GetComponent<NPCPlayerApex>();
if (npc != null && npc is NPCMurderer)
npc.NewAI = false;
var KitDetails = new kitData();
if (entity != null)
{
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;
if (!KitDetails.Allow_Rust_Loot)
{
corpse.containers[0].Clear();
corpse.containers[1].Clear();
corpse.containers[2].Clear();
}
if (KitDetails.Kit != "")
{
string[] checkKit = (Kits.CallHook("GetKitContents", KitDetails.Kit)) as string[];
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.smoke.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, BasePlayer victim, bool hasAttacker)
{
if (npcPlayer == null)
return;
if (npcPlayer.svActiveItemID == 0)
return;
var active = npcPlayer.GetActiveItem();
HeldEntity heldEntity1 = null;
if (active != null)
heldEntity1 = active.GetHeldEntity() as HeldEntity;
var bData = npcPlayer.GetComponent<botData>();
if (hasAttacker == false)
{
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;
}
int index = random.Next(weapons.Count);
var currentTime = TOD_Sky.Instance.Cycle.Hour;
if (currentTime > 20 || currentTime < 8)
{
foreach (Item item in npcPlayer.inventory.containerBelt.itemList)
{
HeldEntity held = item.GetHeldEntity() as HeldEntity;
if (item.ToString().Contains("flashlight"))
{
if (heldEntity1 != null)
heldEntity1.SetHeld(false);
var UID = item.uid;
ChangeWeapon(npcPlayer, held, UID);
return;
}
}
}
else
{
foreach (Item item in npcPlayer.inventory.containerBelt.itemList) //pick one at random to start with
{
HeldEntity held = item.GetHeldEntity() as HeldEntity;
if (item.position == weapons[index])
{
if (heldEntity1 != null)
heldEntity1.SetHeld(false);
var UID = npcPlayer.inventory.containerBelt.GetSlot(weapons[index]).uid;
ChangeWeapon(npcPlayer, held, UID);
return;
}
}
}
}
if (hasAttacker == true)
{
bData.canChangeWeapon++;
if (bData.canChangeWeapon > 3)
{
bData.canChangeWeapon = 0;
if (npcPlayer == null)
return;
if (heldEntity1 == null)
bData.currentWeaponRange = 0;
float distance = Vector3.Distance(npcPlayer.transform.position, victim.transform.position);
int noOfAvailableWeapons = 0;
int selectedWeapon;
Item chosenWeapon = null;
HeldEntity held = null;
int newCurrentRange = 0;
var currentTime = TOD_Sky.Instance.Cycle.Hour;
bool night = false;
if (currentTime > 20 || currentTime < 8)
night = true;
if (npcPlayer.AttackTarget == null && night)
{
foreach (var weap in bData.MeleeWeapons)
{
if (weap.ToString().Contains("flashlight"))
{
chosenWeapon = weap;
newCurrentRange = 1;
}
}
}
else
{
if (distance < 2f && bData.MeleeWeapons.Count != 0)
{
bData.enemyDistance = 1;
foreach (var weap in bData.MeleeWeapons)
{
noOfAvailableWeapons++;
}
if (noOfAvailableWeapons > 0)
{
selectedWeapon = random.Next(bData.MeleeWeapons.Count);
chosenWeapon = bData.MeleeWeapons[selectedWeapon];
newCurrentRange = 1;
}
}
else if (distance > 1f && distance < 20f && bData.CloseRangeWeapons != null)
{
bData.enemyDistance = 2;
foreach (var weap in bData.CloseRangeWeapons)
{
noOfAvailableWeapons++;
}
if (noOfAvailableWeapons > 0)
{
selectedWeapon = random.Next(bData.CloseRangeWeapons.Count);
chosenWeapon = bData.CloseRangeWeapons[selectedWeapon];
newCurrentRange = 2;
}
else
{
foreach (var weap in bData.MediumRangeWeapons) //if no close weapon, prioritise medium
{
noOfAvailableWeapons++;
}
if (noOfAvailableWeapons > 0)
{
selectedWeapon = random.Next(bData.MediumRangeWeapons.Count);
chosenWeapon = bData.MediumRangeWeapons[selectedWeapon];
newCurrentRange = 3;
}
}
}
else if (distance > 19f && distance < 40f && bData.MediumRangeWeapons != null)
{
bData.enemyDistance = 3;
foreach (var weap in bData.MediumRangeWeapons)
{
noOfAvailableWeapons++;
}
if (noOfAvailableWeapons > 0)
{
selectedWeapon = random.Next(bData.MediumRangeWeapons.Count);
chosenWeapon = bData.MediumRangeWeapons[selectedWeapon];
newCurrentRange = 3;
}
}
else if (distance > 39 && bData.LongRangeWeapons != null)
{
bData.enemyDistance = 4;
foreach (var weap in bData.LongRangeWeapons)
{
noOfAvailableWeapons++;
}
if (noOfAvailableWeapons > 0)
{
selectedWeapon = random.Next(bData.LongRangeWeapons.Count);
chosenWeapon = bData.LongRangeWeapons[selectedWeapon];
newCurrentRange = 4;
}
else
{
foreach (var weap in bData.MediumRangeWeapons) //if no long weapon, prioritise medium
{
noOfAvailableWeapons++;
}
if (noOfAvailableWeapons > 0)
{
selectedWeapon = random.Next(bData.MediumRangeWeapons.Count);
chosenWeapon = bData.MediumRangeWeapons[selectedWeapon];
newCurrentRange = 3;
}
}
}
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++;
}
// ... [Code preview truncated due to length. Full source in attached file] ...