Advertisement
Guest User

BotSpawn V1.9.0

a guest
Dec 11th, 2019
697
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 93.64 KB | None | 0 0
  1. using Facepunch;
  2. using System;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Globalization;
  6. using System.Collections.Generic;
  7. using Oxide.Core;
  8. using Oxide.Core.Plugins;
  9. using Oxide.Game.Rust;
  10. using UnityEngine;
  11. using UnityEngine.AI;
  12. using UnityEngine.SceneManagement;
  13. using Newtonsoft.Json;
  14. using Newtonsoft.Json.Linq;
  15.  
  16. //Added named skull to corpse harvesting.
  17. namespace Oxide.Plugins
  18. {
  19.     [Info("BotSpawn", "Steenamaroo", "1.9.0", ResourceId = 2580)]
  20.  
  21.     [Description("Spawn tailored AI with kits at monuments, custom locations, or randomly.")]
  22.  
  23.     class BotSpawn : RustPlugin
  24.     {
  25.         [PluginReference]
  26.         Plugin Vanish, Kits;
  27.  
  28.         int no_of_AI;
  29.         static BotSpawn botSpawn;
  30.         System.Single currentTime;
  31.         const string permAllowed = "botspawn.allowed";
  32.         static System.Random random = new System.Random();
  33.         public List<ulong> coolDownPlayers = new List<ulong>();
  34.         public Dictionary<ulong, Timer> weaponCheck = new Dictionary<ulong, Timer>();
  35.         public Dictionary<string, List<Vector3>> spawnLists = new Dictionary<string, List<Vector3>>();
  36.         bool HasPermission(string id, string perm) => permission.UserHasPermission(id, perm);
  37.  
  38.         public Timer aridTimer, temperateTimer, tundraTimer, arcticTimer;
  39.  
  40.         bool IsBiome(string name) => name == "BiomeArid" || name == "BiomeTemperate" || name == "BiomeTundra" || name == "BiomeArctic";
  41.  
  42.         #region Data  
  43.         class StoredData
  44.         {
  45.             public Dictionary<string, DataProfile> DataProfiles = new Dictionary<string, DataProfile>();
  46.             public Dictionary<string, ProfileRelocation> MigrationDataDoNotEdit = new Dictionary<string, ProfileRelocation>();
  47.         }
  48.  
  49.         StoredData storedData;
  50.         #endregion
  51.         void OnServerInitialized()
  52.         {
  53.             botSpawn = this;
  54.             FindMonuments();
  55.         }
  56.  
  57.         void Init()
  58.         {
  59.             JsonConvert.DefaultSettings = () => new JsonSerializerSettings
  60.             {
  61.                 Formatting = Formatting.Indented,
  62.                 ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
  63.             };
  64.             var filter = RustExtension.Filter.ToList();//Thanks Fuji. :)
  65.             filter.Add("cover points");
  66.             filter.Add("resulted in a conflict");
  67.             RustExtension.Filter = filter.ToArray();
  68.             no_of_AI = 0;
  69.             LoadConfigVariables();
  70.         }
  71.  
  72.         void Loaded()
  73.         {
  74.             ConVar.AI.npc_families_no_hurt = false;
  75.             spawnLists.Add("BiomeArid", new List<Vector3>());
  76.             spawnLists.Add("BiomeTemperate", new List<Vector3>());
  77.             spawnLists.Add("BiomeTundra", new List<Vector3>());
  78.             spawnLists.Add("BiomeArctic", new List<Vector3>());
  79.  
  80.             lang.RegisterMessages(Messages, this);
  81.             permission.RegisterPermission(permAllowed, this);
  82.  
  83.             storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("BotSpawn");
  84.             SaveData();
  85.         }
  86.  
  87.         void Unload()
  88.         {
  89.             var filter = RustExtension.Filter.ToList();
  90.             filter.Remove("cover points");
  91.             filter.Remove("resulted in a conflict");
  92.             RustExtension.Filter = filter.ToArray();
  93.             Wipe();
  94.         }
  95.  
  96.         bool IsAuth(BasePlayer player) => !(player.net.connection != null && player.net.connection.authLevel < 2);
  97.  
  98.         void UpdateRecords(NPCPlayerApex player)
  99.         {
  100.             if (NPCPlayers.ContainsKey(player.userID))
  101.                 NPCPlayers.Remove(player.userID);
  102.  
  103.             if (weaponCheck.ContainsKey(player.userID))
  104.             {
  105.                 weaponCheck[player.userID].Destroy();
  106.                 weaponCheck.Remove(player.userID);
  107.             }
  108.         }
  109.  
  110.         void Wipe()
  111.         {
  112.             foreach (var bot in NPCPlayers.Where(bot => bot.Value != null))
  113.                 bot.Value.Kill();
  114.         }
  115.  
  116.         public static string Get(ulong v) => Facepunch.RandomUsernames.Get((int)(v % 2147483647uL));
  117.  
  118.         #region BiomeSpawnsSetup
  119.         void GenerateSpawnPoints(string name, int number, Timer myTimer, int biomeNo)
  120.         {
  121.             int getBiomeAttempts = 0;
  122.             var spawnlist = spawnLists[name];
  123.             myTimer = timer.Repeat(0.1f, 0, () =>
  124.             {
  125.                 int halfish = Convert.ToInt16((ConVar.Server.worldsize / 2) / 1.1f);
  126.                 int x = random.Next(-halfish, halfish);
  127.                 int z = random.Next(-halfish, halfish);
  128.                 Vector3 randomSpot = new Vector3(x, 0, z);
  129.                 bool finished = true;
  130.  
  131.                 if (spawnlist.Count < number + 10)
  132.                 {
  133.                     getBiomeAttempts++;
  134.                     if (getBiomeAttempts > 200 && spawnlist.Count == 0)
  135.                     {
  136.                         PrintWarning(lang.GetMessage("noSpawn", this), name);
  137.                         myTimer.Destroy();
  138.                         return;
  139.                     }
  140.  
  141.                     finished = false;
  142.                     x = random.Next(-halfish, halfish);
  143.                     z = random.Next(-halfish, halfish);
  144.                     if (TerrainMeta.BiomeMap.GetBiome(randomSpot, biomeNo) > 0.5f)
  145.                     {
  146.                         var point = CalculateGroundPos(new Vector3(randomSpot.x, 200, randomSpot.z));
  147.                         if (point != Vector3.zero)
  148.                             spawnlist.Add(CalculateGroundPos(new Vector3(randomSpot.x, 200, randomSpot.z)));
  149.                     }
  150.                 }
  151.                 if (finished)
  152.                 {
  153.                     int i = 0;
  154.                     timer.Repeat(2, number, () =>
  155.                     {
  156.                         SpawnBots(name, AllProfiles[name], "biome", null, spawnlist[i]);
  157.                         i++;
  158.                     });
  159.                     myTimer.Destroy();
  160.                 }
  161.             });
  162.         }
  163.  
  164.         public static Vector3 CalculateGroundPos(Vector3 pos)
  165.         {
  166.             pos.y = TerrainMeta.HeightMap.GetHeight(pos);
  167.             NavMeshHit navMeshHit;
  168.  
  169.             if (!NavMesh.SamplePosition(pos, out navMeshHit, 2, 1))
  170.                 pos = Vector3.zero;
  171.             else if (Physics.RaycastAll(navMeshHit.position + new Vector3(0, 100, 0), Vector3.down, 99f, 1235288065).Any())
  172.                 pos = Vector3.zero;
  173.             else
  174.                 pos = navMeshHit.position;
  175.             return pos;
  176.  
  177.         }
  178.  
  179.         Vector3 TryGetSpawn(Vector3 pos, int radius)
  180.         {
  181.             int attempts = 0;
  182.             var spawnPoint = Vector3.zero;
  183.             Vector2 rand;
  184.             Vector3 attempt;
  185.  
  186.             while (attempts < 200 && spawnPoint == Vector3.zero)
  187.             {
  188.                 attempts++;
  189.                 rand = UnityEngine.Random.insideUnitCircle * radius;
  190.                 pos += new Vector3(rand.x, 0, rand.y);
  191.                 attempt = CalculateGroundPos(pos);
  192.                 if (attempt != Vector3.zero)
  193.                     spawnPoint = attempt;
  194.             }
  195.             return spawnPoint;
  196.         }
  197.         #endregion
  198.  
  199.         #region BotSetup
  200.         void AttackPlayer(Vector3 location, string name, DataProfile profile, string group)
  201.         {
  202.             timer.Repeat(1f, profile.Bots, () => SpawnBots(name, profile, "Attack", group, location));
  203.         }
  204.  
  205.         void SpawnBots(string name, DataProfile zone, string type, string group, Vector3 location)
  206.         {
  207.             var pos = new Vector3(zone.LocationX, zone.LocationY, zone.LocationZ);
  208.             var finalPoint = Vector3.zero;
  209.             if (location != Vector3.zero)
  210.                 pos = location;
  211.  
  212.             var randomTerrainPoint = TryGetSpawn(pos, zone.Radius);
  213.             if (randomTerrainPoint == Vector3.zero)
  214.                 return;
  215.             finalPoint = randomTerrainPoint + new Vector3(0, 0.5f, 0);
  216.  
  217.             if (zone.Chute)
  218.                 finalPoint = (type == "AirDrop") ? pos - new Vector3(0, 40, 0) : new Vector3(randomTerrainPoint.x, 200, randomTerrainPoint.z);
  219.  
  220.             NPCPlayer entity = (NPCPlayer)InstantiateSci(finalPoint, Quaternion.Euler(0, 0, 0), zone.Murderer);
  221.             var npc = entity.GetComponent<NPCPlayerApex>();
  222.  
  223.             var bData = npc.gameObject.AddComponent<BotData>();
  224.             bData.monumentName = name;
  225.             npc.Spawn();
  226.  
  227.             if (!NPCPlayers.ContainsKey(npc.userID))
  228.                 NPCPlayers.Add(npc.userID, npc);
  229.             else
  230.             {
  231.                 npc.Kill();
  232.                 PrintWarning(lang.GetMessage("dupID", this));
  233.                 return;
  234.             }
  235.             npc.AiContext.Human.NextToolSwitchTime = Time.realtimeSinceStartup * 10;
  236.             npc.AiContext.Human.NextWeaponSwitchTime = Time.realtimeSinceStartup * 10;
  237.             npc.CommunicationRadius = 0;
  238.  
  239.             no_of_AI++;
  240.  
  241.             bData.group = group ?? null;
  242.  
  243.             bData.spawnPoint = randomTerrainPoint;
  244.             bData.accuracy = zone.Bot_Accuracy;
  245.             bData.damage = zone.Bot_Damage;
  246.             bData.respawn = true;
  247.             bData.roamRange = zone.Roam_Range;
  248.             bData.dropweapon = zone.Weapon_Drop_Chance;
  249.             bData.keepAttire = zone.Keep_Default_Loadout;
  250.             bData.peaceKeeper = zone.Peace_Keeper;
  251.             bData.chute = zone.Chute;
  252.             bData.peaceKeeper_CoolDown = zone.Peace_Keeper_Cool_Down;
  253.             bData.profile = zone;
  254.  
  255.             npc.startHealth = zone.BotHealth;
  256.             npc.InitializeHealth(zone.BotHealth, zone.BotHealth);
  257.  
  258.             bData.biome = (type == "biome");
  259.             if (zone.Chute)
  260.                 AddChute(npc, finalPoint);
  261.  
  262.             int kitRnd;
  263.             kitRnd = random.Next(zone.Kit.Count);
  264.  
  265.             if (zone.BotNames.Count == zone.Kit.Count && zone.Kit.Count != 0)
  266.                 SetName(zone, npc, kitRnd);
  267.             else
  268.                 SetName(zone, npc, random.Next(zone.BotNames.Count));
  269.  
  270.             GiveKit(npc, zone, kitRnd);
  271.  
  272.             SortWeapons(npc);
  273.  
  274.             int suicInt = random.Next(zone.Suicide_Timer, zone.Suicide_Timer + 10);//slightly randomise suicide de-spawn time
  275.             if (type == "AirDrop" || type == "Attack")
  276.             {
  277.                 bData.respawn = false;
  278.                 RunSuicide(npc, suicInt);
  279.             }
  280.  
  281.             if (zone.Disable_Radio)
  282.                 npc.RadioEffect = new GameObjectRef();
  283.  
  284.             npc.Stats.AggressionRange = zone.Sci_Aggro_Range;
  285.             npc.Stats.DeaggroRange = zone.Sci_DeAggro_Range;
  286.         }
  287.  
  288.         BaseEntity InstantiateSci(Vector3 position, Quaternion rotation, bool murd)//Spawn population spam fix - credit Fujikura
  289.         {
  290.             string type = (murd) ? "murderer" : "scientist";
  291.             string prefabname = $"assets/prefabs/npc/{type}/{type}.prefab";
  292.  
  293.             var prefab = GameManager.server.FindPrefab(prefabname);
  294.             GameObject gameObject = Instantiate.GameObject(prefab, position, rotation);
  295.             gameObject.name = prefabname;
  296.             SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
  297.             if (gameObject.GetComponent<Spawnable>())
  298.                 UnityEngine.Object.Destroy(gameObject.GetComponent<Spawnable>());
  299.             if (!gameObject.activeSelf)
  300.                 gameObject.SetActive(true);
  301.             BaseEntity component = gameObject.GetComponent<BaseEntity>();
  302.             return component;
  303.         }
  304.  
  305.         void AddChute(NPCPlayerApex npc, Vector3 newPos)
  306.         {
  307.             float wind = random.Next(0, 25) / 10f;
  308.             float fall = random.Next(40, 80) / 10f;
  309.             var rb = npc.gameObject.GetComponent<Rigidbody>();
  310.             rb.isKinematic = false;
  311.             rb.useGravity = false;
  312.             rb.drag = 0f;
  313.             npc.gameObject.layer = 0;//prevent_build layer fix
  314.             var fwd = npc.transform.forward;
  315.             rb.velocity = new Vector3(fwd.x * wind, 0, fwd.z * wind) - new Vector3(0, fall, 0);
  316.  
  317.             var col = npc.gameObject.AddComponent<BoxCollider>();
  318.             col.size = new Vector3(1, 1f, 1);  //feet above ground
  319.  
  320.             var Chute = GameManager.server.CreateEntity("assets/prefabs/misc/parachute/parachute.prefab", newPos, Quaternion.Euler(0, 0, 0));
  321.             Chute.gameObject.Identity();
  322.             Chute.SetParent(npc, "parachute");
  323.             Chute.Spawn();
  324.         }
  325.  
  326.         void SetName(DataProfile zone, NPCPlayerApex npc, int number)
  327.         {
  328.             if (zone.BotNames.Count == 0 || zone.BotNames.Count <= number || zone.BotNames[number] == String.Empty)
  329.             {
  330.                 npc.displayName = Get(npc.userID);
  331.                 npc.displayName = char.ToUpper(npc.displayName[0]) + npc.displayName.Substring(1);
  332.             }
  333.             else
  334.                 npc.displayName = zone.BotNames[number];
  335.  
  336.             if (zone.BotNamePrefix != String.Empty)
  337.                 npc.displayName = zone.BotNamePrefix + " " + npc.displayName;
  338.         }
  339.  
  340.         void GiveKit(NPCPlayerApex npc, DataProfile zone, int kitRnd)
  341.         {
  342.             var bData = npc.GetComponent<BotData>();
  343.             string type = (zone.Murderer) ? "Murderer" : "Scientist";
  344.             if (zone.Kit.Count != 0 && zone.Kit[kitRnd] != null)
  345.             {
  346.                 object checkKit = (Kits.CallHook("GetKitInfo", zone.Kit[kitRnd], true));
  347.                 if (checkKit == null)
  348.                 {
  349.                     PrintWarning($"Kit {zone.Kit[kitRnd]} does not exist - Spawning default {type}.");
  350.                 }
  351.                 else
  352.                 {
  353.                     bool weaponInBelt = false;
  354.                     JObject kitContents = checkKit as JObject;
  355.                     if (kitContents != null)
  356.                     {
  357.                         JArray items = kitContents["items"] as JArray;
  358.                         foreach (var weap in items)
  359.                         {
  360.                             JObject item = weap as JObject;
  361.                             if (item["container"].ToString() == "belt")
  362.                                 weaponInBelt = true;//doesn't actually check for weapons - just any item.
  363.                         }
  364.                     }
  365.                     if (!weaponInBelt)
  366.                     {
  367.                         PrintWarning($"Kit {zone.Kit[kitRnd]} has no items in belt - Spawning default {type}.");
  368.                     }
  369.                     else
  370.                     {
  371.                         if (bData.keepAttire == false)
  372.                             npc.inventory.Strip();
  373.                         Kits.Call($"GiveKit", npc, zone.Kit[kitRnd], true);
  374.                         if (!(KitList.ContainsKey(npc.userID)))
  375.                         {
  376.                             KitList.Add(npc.userID, new KitData
  377.                             {
  378.                                 Kit = zone.Kit[kitRnd],
  379.                                 Wipe_Belt = zone.Wipe_Belt,
  380.                                 Wipe_Clothing = zone.Wipe_Clothing,
  381.                                 Allow_Rust_Loot = zone.Allow_Rust_Loot,
  382.                             });
  383.                         }
  384.                     }
  385.                 }
  386.             }
  387.             else
  388.             {
  389.                 if (!KitList.ContainsKey(npc.userID))
  390.                 {
  391.                     KitList.Add(npc.userID, new KitData
  392.                     {
  393.                         Kit = String.Empty,
  394.                         Wipe_Belt = zone.Wipe_Belt,
  395.                         Wipe_Clothing = zone.Wipe_Clothing,
  396.                         Allow_Rust_Loot = zone.Allow_Rust_Loot,
  397.                     });
  398.                 }
  399.             }
  400.         }
  401.  
  402.         void SortWeapons(NPCPlayerApex npc)
  403.         {
  404.             var bData = npc.GetComponent<BotData>();
  405.             foreach (Item item in npc.inventory.containerBelt.itemList)//store organised weapons lists
  406.             {
  407.                 var held = item.GetHeldEntity();
  408.                 if (held as HeldEntity != null)
  409.                 {
  410.                     if (held.name.Contains("bow") || held.name.Contains("launcher"))
  411.                         continue;
  412.                     if (held as BaseMelee != null || held as TorchWeapon != null)
  413.                         bData.MeleeWeapons.Add(item);
  414.                     else
  415.                     {
  416.                         if (held as BaseProjectile != null)
  417.                         {
  418.                             bData.AllProjectiles.Add(item);
  419.                             if (held.name.Contains("m92") || held.name.Contains("pistol") || held.name.Contains("python") || held.name.Contains("waterpipe"))
  420.                                 bData.CloseRangeWeapons.Add(item);
  421.                             else if (held.name.Contains("bolt"))
  422.                                 bData.LongRangeWeapons.Add(item);
  423.                             else
  424.                                 bData.MediumRangeWeapons.Add(item);
  425.                         }
  426.                     }
  427.                 }
  428.             }
  429.             weaponCheck.Add(npc.userID, timer.Repeat(2.99f, 0, () => SelectWeapon(npc)));
  430.         }
  431.  
  432.         void RunSuicide(NPCPlayerApex npc, int suicInt)
  433.         {
  434.             if (!NPCPlayers.ContainsKey(npc.userID))
  435.                 return;
  436.             timer.Once(suicInt, () =>
  437.             {
  438.                 if (npc == null)
  439.                     return;
  440.                 if (npc.AttackTarget != null && Vector3.Distance(npc.transform.position, npc.AttackTarget.transform.position) < 10 && npc.GetNavAgent.isOnNavMesh)
  441.                 {
  442.                     var position = npc.AttackTarget.transform.position;
  443.                     npc.svActiveItemID = 0;
  444.                     npc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
  445.                     npc.inventory.UpdatedVisibleHolsteredItems();
  446.                     timer.Repeat(0.05f, 100, () =>
  447.                     {
  448.                         if (npc == null)
  449.                             return;
  450.                         npc.SetDestination(position);
  451.                     });
  452.                 }
  453.                 timer.Once(4, () =>
  454.                 {
  455.                     if (npc == null)
  456.                         return;
  457.                     Effect.server.Run("assets/prefabs/weapons/rocketlauncher/effects/rocket_explosion.prefab", npc.transform.position);
  458.                     HitInfo nullHit = new HitInfo();
  459.                     nullHit.damageTypes.Add(Rust.DamageType.Explosion, 10000);
  460.                     npc.IsInvinsible = false;
  461.                     npc.Die(nullHit);
  462.                 }
  463.                 );
  464.             });
  465.         }
  466.         #endregion
  467.         static BasePlayer FindPlayerByName(string name)
  468.         {
  469.             BasePlayer result = null;
  470.             foreach (BasePlayer current in BasePlayer.activePlayerList)
  471.             {
  472.                 if (current.displayName.Equals(name, StringComparison.OrdinalIgnoreCase)
  473.                     || current.UserIDString.Contains(name, CompareOptions.OrdinalIgnoreCase)
  474.                     || current.displayName.Contains(name, CompareOptions.OrdinalIgnoreCase))
  475.                     result = current;
  476.             }
  477.             return result;
  478.         }
  479.         #region Hooks  
  480.  
  481.         object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
  482.         {
  483.             var botMelee = info?.Initiator as BaseMelee;
  484.             bool melee = false;
  485.             if (botMelee != null)
  486.             {
  487.                 melee = true;
  488.                 info.Initiator = botMelee.GetOwnerPlayer();
  489.             }
  490.             NPCPlayerApex bot = entity as NPCPlayerApex;
  491.             var attackNPC = info?.Initiator as NPCPlayerApex;
  492.             var attackPlayer = info?.Initiator as BasePlayer;
  493.             BotData bData;
  494.  
  495.             if (bot?.userID != null)
  496.             {
  497.                 if (!NPCPlayers.ContainsKey(bot.userID))
  498.                     return null;
  499.                 bData = bot.GetComponent<BotData>();
  500.                 if (info.Initiator?.ToString() == null && configData.Global.Pve_Safe)
  501.                     info.damageTypes.ScaleAll(0);
  502.                 if (attackPlayer != null && attackNPC == null)//bots wont retaliate to vanished players  
  503.                 {
  504.                     var canNetwork = Vanish?.Call("IsInvisible", info.Initiator);
  505.                     if ((canNetwork is bool) && (bool)canNetwork)
  506.                     {
  507.                         info.Initiator = null;
  508.                         return true;
  509.                     }
  510.  
  511.                     if (bData.peaceKeeper)//prevent melee farming with peacekeeper on
  512.                     {
  513.                         var heldMelee = info.Weapon as BaseMelee;
  514.                         var heldTorchWeapon = info.Weapon as TorchWeapon;
  515.                         if (heldMelee != null || heldTorchWeapon != null)
  516.                             info.damageTypes.ScaleAll(0);
  517.                     }
  518.                 }
  519.                 bData.goingHome = false;
  520.             }
  521.  
  522.             if (attackNPC?.userID != null && entity is BasePlayer)//add in bot accuracy
  523.             {
  524.                 if (!NPCPlayers.ContainsKey(attackNPC.userID))
  525.                     return null;
  526.  
  527.                 bData = attackNPC.GetComponent<BotData>();
  528.                 int rand = random.Next(1, 100);
  529.                 float distance = (Vector3.Distance(info.Initiator.transform.position, entity.transform.position));
  530.  
  531.                 var newAccuracy = (bData.accuracy * 10f);
  532.                 var newDamage = (bData.damage);
  533.                 if (distance > 100f)
  534.                 {
  535.                     newAccuracy = ((bData.accuracy * 10f) / (distance / 100f));
  536.                     newDamage = bData.damage / (distance / 100f);
  537.                 }
  538.                 if (!melee && newAccuracy < rand)//scale bot attack damage
  539.                     return true;
  540.                 info.damageTypes.ScaleAll(newDamage);
  541.             }
  542.             return null;
  543.         }
  544.  
  545.         void OnEntityKill(BaseNetworkable entity)
  546.         {
  547.             NPCPlayerApex npc = entity as NPCPlayerApex;
  548.             if (npc?.userID != null && NPCPlayers.ContainsKey(npc.userID))
  549.             {
  550.                 var bData = npc.GetComponent<BotData>();
  551.                 Item activeItem = npc.GetActiveItem();
  552.  
  553.                 int chance = random.Next(0, 100);
  554.                 if (bData.dropweapon > chance && activeItem != null)
  555.                 {
  556.                     using (TimeWarning timeWarning = TimeWarning.New("PlayerBelt.DropActive", 0.1f))
  557.                     {
  558.                         activeItem.Drop(npc.eyes.position, new Vector3(), new Quaternion());
  559.                         npc.svActiveItemID = 0;
  560.                         npc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
  561.                         KitRemoveList.Add(npc.userID, activeItem.info.name);
  562.                     }
  563.                 }
  564.                 if (AllProfiles[bData.monumentName].Disable_Radio == true)
  565.                     npc.DeathEffect = new GameObjectRef();//kill radio effects
  566.  
  567.                 DeadNPCPlayerIds.Add(npc.userID);
  568.                 no_of_AI--;
  569.                 if (bData.respawn == false)
  570.                 {
  571.                     UnityEngine.Object.Destroy(npc.GetComponent<BotData>());
  572.                     UpdateRecords(npc);
  573.                     return;
  574.                 }
  575.                 if (bData.biome && spawnLists.ContainsKey(bData.monumentName))
  576.                 {
  577.                     List<Vector3> spawnList = new List<Vector3>();
  578.                     spawnList = spawnLists[bData.monumentName];
  579.                     int spawnPos = random.Next(spawnList.Count);
  580.                     timer.Once(AllProfiles[bData.monumentName].Respawn_Timer, () =>
  581.                     {
  582.                         if (AllProfiles.ContainsKey(bData.monumentName))
  583.                             SpawnBots(bData.monumentName, AllProfiles[bData.monumentName], "biome", null, spawnList[spawnPos]);
  584.                     });
  585.                     UpdateRecords(npc);
  586.                     return;
  587.                 }
  588.                 foreach (var profile in AllProfiles)
  589.                 {
  590.                     timer.Once(profile.Value.Respawn_Timer, () =>
  591.                     {
  592.                         if (profile.Key == bData.monumentName)
  593.                             SpawnBots(profile.Key, profile.Value, null, null, new Vector3());
  594.  
  595.                     });
  596.                 }
  597.                 UnityEngine.Object.Destroy(npc.GetComponent<BotData>());
  598.                 UpdateRecords(npc);
  599.             }
  600.         }
  601.  
  602.         void OnPlayerDie(BasePlayer player) => OnEntityKill(player);
  603.  
  604.         public static readonly FieldInfo AllScientists = typeof(Scientist).GetField("AllScientists", (BindingFlags.Static | BindingFlags.NonPublic)); //NRE AskQuestion workaround
  605.  
  606.         void OnEntitySpawned(BaseEntity entity) // handles smoke signals, backpacks, corpses(applying kit)
  607.         {
  608.             NPCPlayerCorpse corpse = entity as NPCPlayerCorpse;
  609.             Scientist sci = entity as Scientist;
  610.  
  611.             if (sci != null)
  612.                 AllScientists.SetValue(sci, new HashSet<Scientist>());//NRE AskQuestion workaround
  613.  
  614.             var KitDetails = new KitData();
  615.             if (entity == null)
  616.                 return;
  617.  
  618.             var pos = entity.transform.position;
  619.  
  620.             if (corpse != null)
  621.             {
  622.                 corpse.ResetRemovalTime(configData.Global.Corpse_Duration);  
  623.  
  624.                 if (KitList.ContainsKey(corpse.playerSteamID))
  625.                 {
  626.                     KitDetails = KitList[corpse.playerSteamID];
  627.                     NextTick(() =>
  628.                     {
  629.                         if (corpse == null)
  630.                             return;
  631.  
  632.                         List<Item> toDestroy = new List<Item>();
  633.                         foreach (var item in corpse.containers[0].itemList)
  634.                         {
  635.                             if (item.ToString().ToLower().Contains("keycard") && configData.Global.Remove_KeyCard)
  636.                                 toDestroy.Add(item);
  637.                         }
  638.  
  639.                         foreach (var item in toDestroy)
  640.                             item.RemoveFromContainer();
  641.  
  642.                         if (!KitDetails.Allow_Rust_Loot)
  643.                         {
  644.                             corpse.containers[0].Clear();
  645.                             corpse.containers[1].Clear();
  646.                             corpse.containers[2].Clear();
  647.                         }
  648.  
  649.                         //Skull
  650.                         Item playerSkull = ItemManager.CreateByName("skull.human", 1);
  651.                         playerSkull.name = string.Concat($"Skull of {corpse.playerName}");
  652.                         ItemAmount SkullInfo = new ItemAmount()
  653.                         {
  654.                             itemDef = playerSkull.info,
  655.                             amount = 1,
  656.                             startAmount = 1
  657.                         };
  658.                         var dispenser = corpse.GetComponent<ResourceDispenser>();
  659.                         if (dispenser != null)
  660.                         {
  661.                             dispenser.containedItems.Add(SkullInfo);
  662.                             dispenser.Initialize();
  663.                         }
  664.                         //EndSkull
  665.  
  666.                         if (KitDetails.Kit != String.Empty)
  667.                         {
  668.                             var tempbody = GameManager.server.CreateEntity("assets/prefabs/player/player.prefab", (corpse.transform.position - new Vector3(0, -100, 0)), corpse.transform.rotation).ToPlayer();
  669.                             tempbody.Spawn();
  670.  
  671.                             Kits?.Call($"GiveKit", tempbody, KitDetails.Kit, true);
  672.                             var source = new ItemContainer[] { tempbody.inventory.containerMain, tempbody.inventory.containerWear, tempbody.inventory.containerBelt };
  673.  
  674.                             for (int i = 0; i < (int)source.Length; i++)
  675.                             {
  676.                                 Item[] array = source[i].itemList.ToArray();
  677.                                 for (int j = 0; j < (int)array.Length; j++)
  678.                                 {
  679.                                     Item item = array[j];
  680.                                     if (!item.MoveToContainer(corpse.containers[i], -1, true))
  681.                                         item.Remove(0f);
  682.                                 }
  683.                             }
  684.                             tempbody.Kill();
  685.                         }
  686.                         if (KitList[corpse.playerSteamID].Wipe_Belt)
  687.                             corpse.containers[2].Clear();
  688.                         else
  689.                         if (KitRemoveList.ContainsKey(corpse.playerSteamID))
  690.                         {
  691.                             foreach (var thing in corpse.containers[2].itemList)//If weapon drop is enabled, this removes the weapon from the corpse's inventory.
  692.                             {
  693.                                 if (KitRemoveList[corpse.playerSteamID] == thing.info.name)
  694.                                 {
  695.                                     thing.Remove();
  696.                                     KitRemoveList.Remove(corpse.playerSteamID);
  697.                                     break;
  698.                                 }
  699.                             }
  700.                         }
  701.  
  702.                         if (KitList[corpse.playerSteamID].Wipe_Clothing)
  703.                             corpse.containers[1].Clear();
  704.  
  705.                         KitList.Remove(corpse.playerSteamID);
  706.                     });
  707.                 }
  708.             }
  709.  
  710.             var container = entity as DroppedItemContainer;
  711.             if (container != null)
  712.             {
  713.                 NextTick(() =>
  714.                 {
  715.                     if (container == null || container.IsDestroyed)
  716.                         return;
  717.  
  718.                     ulong ownerID = container.playerSteamID;
  719.                     if (ownerID == 0) return;
  720.                     if (configData.Global.Remove_BackPacks)
  721.                     {
  722.                         if (DeadNPCPlayerIds.Contains(ownerID))
  723.                         {
  724.                             entity.Kill();
  725.                             DeadNPCPlayerIds.Remove(ownerID);
  726.                             return;
  727.                         }
  728.                     }
  729.  
  730.                 });
  731.             }
  732.  
  733.             if (entity.name.Contains("grenade.supplysignal.deployed"))
  734.                 timer.Once(2.3f, () =>
  735.                 {
  736.                     if (entity != null)
  737.                         SmokeGrenades.Add(new Vector3(entity.transform.position.x, 0, entity.transform.position.z));
  738.                 });
  739.  
  740.             if (!(entity.name.Contains("supply_drop")))
  741.                 return;
  742.  
  743.             Vector3 dropLocation = new Vector3(entity.transform.position.x, 0, entity.transform.position.z);
  744.  
  745.             if (!(configData.Global.Supply_Enabled))
  746.             {
  747.                 foreach (var location in SmokeGrenades.Where(location => Vector3.Distance(location, dropLocation) < 35f))
  748.                 {
  749.                     SmokeGrenades.Remove(location);
  750.                     return;
  751.                 }
  752.             }
  753.             if (AllProfiles.ContainsKey("AirDrop") && AllProfiles["AirDrop"].AutoSpawn == true)
  754.             {
  755.                 var profile = AllProfiles["AirDrop"];
  756.                 timer.Repeat(0.1f, profile.Bots, () =>
  757.                 {
  758.                     profile.LocationX = entity.transform.position.x;
  759.                     profile.LocationY = entity.transform.position.y;
  760.                     profile.LocationZ = entity.transform.position.z;
  761.                     SpawnBots("AirDrop", profile, "AirDrop", null, new Vector3());
  762.                 });
  763.             }
  764.         }
  765.         #endregion
  766.  
  767.         #region WeaponSwitching
  768.         void SelectWeapon(NPCPlayerApex npcPlayer)
  769.         {
  770.             if (npcPlayer == null) return;
  771.             if (npcPlayer.svActiveItemID == 0)
  772.                 return;
  773.  
  774.             var bData = npcPlayer.GetComponent<BotData>();
  775.             if (bData == null) return;
  776.  
  777.             List<int> weapons = new List<int>();//check all their weapons
  778.             foreach (Item item in npcPlayer.inventory.containerBelt.itemList)
  779.             {
  780.                 var held = item.GetHeldEntity();
  781.                 if (held is BaseProjectile || held is BaseMelee || held is TorchWeapon)
  782.                     weapons.Add(Convert.ToInt16(item.position));
  783.             }
  784.  
  785.             if (weapons.Count == 0)
  786.             {
  787.                 PrintWarning(lang.GetMessage("noWeapon", this), bData.monumentName);
  788.                 return;
  789.             }
  790.  
  791.             var victim = npcPlayer.AttackTarget;
  792.             var active = npcPlayer.GetActiveItem();
  793.             HeldEntity heldEntity1 = null;
  794.  
  795.             if (active != null)
  796.                 heldEntity1 = active.GetHeldEntity() as HeldEntity;
  797.  
  798.             if (npcPlayer.AttackTarget == null)
  799.             {
  800.                 if (active != null) SetRange(npcPlayer);
  801.                 currentTime = TOD_Sky.Instance.Cycle.Hour;
  802.                 uint UID = 0;
  803.                 if (currentTime > 20 || currentTime < 8)
  804.                 {
  805.                     if (active != null && !(active.ToString().Contains("flashlight")))
  806.                     {
  807.                         List<Item> lightList = new List<Item>();
  808.                         if (active != null && active.contents != null)
  809.                             lightList = active.contents.itemList;
  810.  
  811.                         foreach (var mod in lightList.Where(mod => mod.info.shortname.Contains("flashlight")))
  812.                             return;
  813.                         foreach (Item item in npcPlayer.inventory.containerBelt.itemList)
  814.                         {
  815.                             if (item.ToString().Contains("flashlight"))
  816.                             {
  817.                                 if (heldEntity1 != null)
  818.                                     heldEntity1.SetHeld(false);
  819.                                 UID = item.uid;
  820.                                 ChangeWeapon(npcPlayer, item);
  821.                                 break;
  822.                             }
  823.                             if (item.contents != null)
  824.                                 foreach (var mod in item.contents.itemList)
  825.                                 {
  826.                                     if (mod.info.shortname.Contains("flashlight"))
  827.                                     {
  828.                                         UID = item.uid;
  829.                                         ChangeWeapon(npcPlayer, item);
  830.                                         break;
  831.                                     }
  832.                                 }
  833.                         }
  834.                     }
  835.                 }
  836.                 else
  837.                 {
  838.                     if (active != null && active.ToString().Contains("flashlight"))
  839.                     {
  840.                         foreach (Item item in npcPlayer.inventory.containerBelt.itemList)//pick one at random to start with
  841.                         {
  842.                             if (item.position == weapons[random.Next(weapons.Count)])
  843.                             {
  844.                                 if (heldEntity1 != null)
  845.                                     heldEntity1.SetHeld(false);
  846.                                 ChangeWeapon(npcPlayer, item);
  847.                             }
  848.                         }
  849.                     }
  850.                 }
  851.             }
  852.             else
  853.             {
  854.                 if (active != null) SetRange(npcPlayer);
  855.                 if (heldEntity1 == null)
  856.                     bData.currentWeaponRange = 0;
  857.  
  858.                 float distance = Vector3.Distance(npcPlayer.transform.position, victim.transform.position);
  859.                 int newCurrentRange = 0;
  860.                 int noOfAvailableWeapons = 0;
  861.                 int selectedWeapon;
  862.                 Item chosenWeapon = null;
  863.                 HeldEntity held = null;
  864.  
  865.  
  866.                 List<Item> rangeToUse = new List<Item>();
  867.                 if (distance < 2f && bData.MeleeWeapons.Count != 0)
  868.                 {
  869.                     bData.enemyDistance = 1;
  870.                     rangeToUse = bData.MeleeWeapons;
  871.                     newCurrentRange = 1;
  872.                 }
  873.                 else if (distance > 1f && distance < 20f)
  874.                 {
  875.                     if (bData.CloseRangeWeapons.Count > 0)
  876.                     {
  877.                         bData.enemyDistance = 2;
  878.                         rangeToUse = bData.CloseRangeWeapons;
  879.                         newCurrentRange = 2;
  880.                     }
  881.                     else
  882.                     {
  883.                         rangeToUse = bData.MediumRangeWeapons;
  884.                         newCurrentRange = 3;
  885.                     }
  886.                 }
  887.                 else if (distance > 19f && distance < 40f && bData.MediumRangeWeapons.Count > 0)
  888.                 {
  889.                     bData.enemyDistance = 3;
  890.                     rangeToUse = bData.MediumRangeWeapons;
  891.                     newCurrentRange = 3;
  892.                 }
  893.                 else if (distance > 39)
  894.                 {
  895.                     if (bData.LongRangeWeapons.Count > 0)
  896.                     {
  897.                         bData.enemyDistance = 4;
  898.                         rangeToUse = bData.LongRangeWeapons;
  899.                         newCurrentRange = 4;
  900.                     }
  901.                     else
  902.                     {
  903.                         rangeToUse = bData.MediumRangeWeapons;
  904.                         newCurrentRange = 3;
  905.                     }
  906.                 }
  907.  
  908.                 if (rangeToUse.Count > 0)
  909.                 {
  910.                     selectedWeapon = random.Next(rangeToUse.Count);
  911.                     chosenWeapon = rangeToUse[selectedWeapon];
  912.                 }
  913.  
  914.                 if (chosenWeapon == null)                                               //if no weapon suited to range, pick any random bullet weapon
  915.                 {                                                                       //prevents sticking with melee @>2m when no pistol is available
  916.                     bData.enemyDistance = 5;
  917.                     if (heldEntity1 != null && bData.AllProjectiles.Contains(active))   //prevents choosing a random weapon if the existing one is fine
  918.                         return;
  919.                     foreach (var weap in bData.AllProjectiles)
  920.                     {
  921.                         noOfAvailableWeapons++;
  922.                     }
  923.                     if (noOfAvailableWeapons > 0)
  924.                     {
  925.                         selectedWeapon = random.Next(bData.AllProjectiles.Count);
  926.                         chosenWeapon = bData.AllProjectiles[selectedWeapon];
  927.                         newCurrentRange = 5;
  928.                     }
  929.                 }
  930.                 if (chosenWeapon == null) return;
  931.                 if (newCurrentRange == bData.currentWeaponRange) return;
  932.                 bData.currentWeaponRange = newCurrentRange;
  933.                 held = chosenWeapon.GetHeldEntity() as HeldEntity;
  934.                 if (heldEntity1 != null && heldEntity1.name == held.name)
  935.                     return;
  936.                 if (heldEntity1 != null && heldEntity1.name != held.name)
  937.                     heldEntity1.SetHeld(false);
  938.                 ChangeWeapon(npcPlayer, chosenWeapon);
  939.             }
  940.         }
  941.  
  942.         void ChangeWeapon(NPCPlayerApex npc, Item item)
  943.         {
  944.             Item activeItem1 = npc.GetActiveItem();
  945.             npc.svActiveItemID = 0U;
  946.             if (activeItem1 != null)
  947.             {
  948.                 HeldEntity heldEntity = activeItem1.GetHeldEntity() as HeldEntity;
  949.                 if (heldEntity != null)
  950.                     heldEntity.SetHeld(false);
  951.             }
  952.             npc.svActiveItemID = item.uid;
  953.             npc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
  954.             Item activeItem2 = npc.GetActiveItem();
  955.             if (activeItem2 != null)
  956.             {
  957.                 HeldEntity heldEntity = activeItem2.GetHeldEntity() as HeldEntity;
  958.                 if (heldEntity != null)
  959.                     heldEntity.SetHeld(true);
  960.             }
  961.             npc.inventory.UpdatedVisibleHolsteredItems();
  962.             npc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
  963.             SetRange(npc);
  964.         }
  965.  
  966.         void SetRange(NPCPlayerApex npcPlayer)
  967.         {
  968.             if (npcPlayer != null && npcPlayer is NPCMurderer)
  969.             {
  970.                 var attackEntity = npcPlayer.GetHeldEntity();
  971.                 var bData = npcPlayer.GetComponent<BotData>();
  972.                 if (bData == null || attackEntity == null)
  973.                     return;
  974.                 var held = attackEntity as AttackEntity;
  975.                 if (held == null)
  976.                     return;
  977.                 if (bData.currentWeaponRange == 0 || bData.currentWeaponRange == 1)
  978.                     held.effectiveRange = 2;
  979.                 else if (bData.currentWeaponRange == 2 || bData.currentWeaponRange == 3)
  980.                     held.effectiveRange = 100;
  981.                 else if (bData.currentWeaponRange == 5)
  982.                     held.effectiveRange = 200;
  983.             }
  984.         }
  985.         #endregion
  986.  
  987.         #region behaviour hooks
  988.         object OnNpcPlayerResume(NPCPlayerApex player)
  989.         {
  990.             var bData = player.GetComponent<BotData>();
  991.             return (bData != null && bData.inAir) ? true : (object)null;
  992.         }
  993.  
  994.         object OnNpcDestinationSet(NPCPlayerApex player)
  995.         {
  996.             var bData = player.GetComponent<BotData>();
  997.             return (bData != null && bData.goingHome) ? true : (object)null;
  998.         }
  999.         #endregion
  1000.  
  1001.         #region OnNpcHooks
  1002.         object OnNpcPlayerTarget(NPCPlayerApex npcPlayer, BaseEntity entity)
  1003.         {
  1004.             if (npcPlayer == null || entity == null)
  1005.                 return null;
  1006.             bool vicIsMine = false;
  1007.             bool attackerIsMine = NPCPlayers.ContainsKey(npcPlayer.userID);
  1008.             var conf = configData.Global;
  1009.  
  1010.             NPCPlayer botVictim = entity as NPCPlayer;
  1011.             if (botVictim != null)
  1012.             {
  1013.                 vicIsMine = NPCPlayers.ContainsKey(botVictim.userID);
  1014.                 if (npcPlayer == botVictim)
  1015.                     return null;
  1016.  
  1017.                 if (vicIsMine && !attackerIsMine && !conf.NPCs_Attack_BotSpawn)//stop oustideNPCs attacking BotSpawn bots
  1018.                     return true;
  1019.  
  1020.                 if (!attackerIsMine)
  1021.                     return null;
  1022.  
  1023.                 if (npcPlayer.GetType() == botVictim.GetType())
  1024.                     return true;
  1025.  
  1026.                 if (!vicIsMine)
  1027.                 {
  1028.                     if (!conf.BotSpawn_Attacks_NPCs)//stop BotSpawn bots attacking outsideNPCs                                                                                  
  1029.                         return true;
  1030.                 }
  1031.                 else if (!conf.BotSpawn_Attacks_BotSpawn)//stop BotSpawn murd+sci fighting each other
  1032.                     return true;
  1033.             }
  1034.  
  1035.             if (!attackerIsMine)
  1036.                 return null;
  1037.  
  1038.             BasePlayer victim = entity as BasePlayer;
  1039.  
  1040.             if (victim != null)
  1041.             {
  1042.                 if (!victim.userID.IsSteamId() && conf.Ignore_HumanNPC)//stops bots targeting humannpc
  1043.                     return true;
  1044.  
  1045.                 var bData = npcPlayer.GetComponent<BotData>();
  1046.  
  1047.                 currentTime = TOD_Sky.Instance.Cycle.Hour;
  1048.                 bData.goingHome = false;
  1049.                 var active = npcPlayer.GetActiveItem();
  1050.  
  1051.                 HeldEntity heldEntity1 = null;
  1052.                 if (active != null)
  1053.                     heldEntity1 = active.GetHeldEntity() as HeldEntity;
  1054.  
  1055.                 if (heldEntity1 == null)//freshspawn catch, pre weapon draw.
  1056.                     return null;
  1057.                 if (currentTime > 20 || currentTime < 8)
  1058.                     heldEntity1.SetLightsOn(true);
  1059.                 else
  1060.                     heldEntity1.SetLightsOn(false);
  1061.  
  1062.                 var heldWeapon = victim.GetHeldEntity() as BaseProjectile;
  1063.                 var heldFlame = victim.GetHeldEntity() as FlameThrower;
  1064.  
  1065.                 if (bData.peaceKeeper)
  1066.                 {
  1067.                     if (heldWeapon != null || heldFlame != null)
  1068.                         if (!AggroPlayers.Contains(victim.userID))
  1069.                             AggroPlayers.Add(victim.userID);
  1070.  
  1071.                     if ((heldWeapon == null && heldFlame == null) || (victim.svActiveItemID == 0u))
  1072.                     {
  1073.                         if (AggroPlayers.Contains(victim.userID) && !coolDownPlayers.Contains(victim.userID))
  1074.                         {
  1075.                             coolDownPlayers.Add(victim.userID);
  1076.                             timer.Once(bData.peaceKeeper_CoolDown, () =>
  1077.                             {
  1078.                                 if (AggroPlayers.Contains(victim.userID))
  1079.                                 {
  1080.                                     AggroPlayers.Remove(victim.userID);
  1081.                                     coolDownPlayers.Remove(victim.userID);
  1082.                                 }
  1083.                             });
  1084.                         }
  1085.                         if (!(AggroPlayers.Contains(victim.userID)))
  1086.                             return true;
  1087.                     }
  1088.                 }
  1089.  
  1090.                 if (npcPlayer is NPCMurderer)
  1091.                 {
  1092.                     var path1 = AllProfiles[bData.monumentName];
  1093.                     var distance = Vector3.Distance(npcPlayer.transform.position, victim.transform.position);
  1094.  
  1095.                     if (npcPlayer.lastAttacker != victim && (path1.Peace_Keeper || distance > path1.Sci_Aggro_Range))
  1096.                         return true;
  1097.  
  1098.                     if (npcPlayer.TimeAtDestination > 5)
  1099.                     {
  1100.                         npcPlayer.AttackTarget = null;
  1101.                         npcPlayer.lastAttacker = null;
  1102.                         npcPlayer.SetFact(NPCPlayerApex.Facts.HasEnemy, 0, true, true);
  1103.                         return true;
  1104.                     }
  1105.                     Vector3 vector3;
  1106.                     float single, single1, single2;
  1107.                     Rust.Ai.BestPlayerDirection.Evaluate(npcPlayer, victim.ServerPosition, out vector3, out single);
  1108.                     Rust.Ai.BestPlayerDistance.Evaluate(npcPlayer, victim.ServerPosition, out single1, out single2);
  1109.                     var info = new Rust.Ai.Memory.ExtendedInfo();
  1110.                     npcPlayer.AiContext.Memory.Update(victim, victim.ServerPosition, 1, vector3, single, single1, 1, true, 1f, out info);
  1111.                 }
  1112.                 bData.goingHome = false;
  1113.  
  1114.                 if (victim.IsSleeping() && conf.Ignore_Sleepers)
  1115.                     return true;
  1116.             }
  1117.  
  1118.             return (entity.name.Contains("agents/") || (entity is HTNPlayer && conf.Ignore_HTN))
  1119.                 ? true
  1120.                 : (object)null;
  1121.         }
  1122.  
  1123.         object OnNpcTarget(BaseNpc npc, BaseEntity entity)//stops animals targeting bots
  1124.         {
  1125.             NPCPlayer npcPlayer = entity as NPCPlayer;
  1126.             return (npcPlayer != null && NPCPlayers.ContainsKey(npcPlayer.userID) && configData.Global.Animal_Safe) ? true : (object)null;
  1127.         }
  1128.  
  1129.         object OnNpcStopMoving(NPCPlayerApex npcPlayer)
  1130.         {
  1131.             return NPCPlayers.ContainsKey(npcPlayer.userID) ? true : (object)null;
  1132.         }
  1133.         #endregion
  1134.  
  1135.         private object CanBeTargeted(BaseCombatEntity player, MonoBehaviour turret)//stops autoturrets targetting bots
  1136.         {
  1137.             NPCPlayer npcPlayer = player as NPCPlayer;
  1138.             return (npcPlayer != null && NPCPlayers.ContainsKey(npcPlayer.userID) && configData.Global.Turret_Safe) ? false : (object)null; ;
  1139.         }
  1140.  
  1141.  
  1142.         private object CanBradleyApcTarget(BradleyAPC bradley, BaseEntity target)//stops bradley targeting bots
  1143.         {
  1144.             NPCPlayer npcPlayer = target as NPCPlayer;
  1145.             return (npcPlayer != null && NPCPlayers.ContainsKey(npcPlayer.userID) && configData.Global.APC_Safe) ? false : (object)null;
  1146.         }
  1147.  
  1148.         #region SetUpLocations
  1149.         private void FindMonuments()
  1150.         {
  1151.             AllProfiles.Clear();
  1152.             var allobjects = UnityEngine.Object.FindObjectsOfType<GameObject>();
  1153.             Vector3 pos;
  1154.             float rot;
  1155.             int miningoutpost = 0, lighthouse = 0, gasstation = 0, spermket = 0, compound = 0;
  1156.  
  1157.             foreach (var gobject in allobjects)
  1158.             {
  1159.                 pos = gobject.transform.position;
  1160.                 rot = gobject.transform.eulerAngles.y;
  1161.  
  1162.                 if (gobject.name.Contains("autospawn/monument") && pos != new Vector3(0, 0, 0))
  1163.                 {
  1164.                     if (gobject.name.Contains("airfield_1"))
  1165.                     {
  1166.                         AddProfile("Airfield", configData.Monuments.Airfield, pos, rot);
  1167.                         continue;
  1168.                     }
  1169.                     if (gobject.name.Contains("compound") && compound == 0)
  1170.                     {
  1171.                         AddProfile("Compound", configData.Monuments.Compound, pos, rot);
  1172.                         compound++;
  1173.                         continue;
  1174.                     }
  1175.                     if (gobject.name.Contains("compound") && compound == 1)
  1176.                     {
  1177.                         AddProfile("Compound1", configData.Monuments.Compound1, pos, rot);
  1178.                         compound++;
  1179.                         continue;
  1180.                     }
  1181.                     if (gobject.name.Contains("compound") && compound == 2)
  1182.                     {
  1183.                         AddProfile("Compound2", configData.Monuments.Compound2, pos, rot);
  1184.                         compound++;
  1185.                         continue;
  1186.                     }
  1187.                     if (gobject.name.Contains("sphere_tank"))
  1188.                     {
  1189.                         AddProfile("Dome", configData.Monuments.Dome, pos, rot);
  1190.                         continue;
  1191.                     }
  1192.                     if (gobject.name.Contains("gas_station_1") && gasstation == 0)
  1193.                     {
  1194.                         AddProfile("GasStation", configData.Monuments.GasStation, pos, rot);
  1195.                         gasstation++;
  1196.                         continue;
  1197.                     }
  1198.                     if (gobject.name.Contains("gas_station_1") && gasstation == 1)
  1199.                     {
  1200.                         AddProfile("GasStation1", configData.Monuments.GasStation1, pos, rot);
  1201.                         gasstation++;
  1202.                         continue;
  1203.                     }
  1204.                     if (gobject.name.Contains("harbor_1"))
  1205.                     {
  1206.                         AddProfile("Harbor1", configData.Monuments.Harbor1, pos, rot);
  1207.                         continue;
  1208.                     }
  1209.  
  1210.                     if (gobject.name.Contains("harbor_2"))
  1211.                     {
  1212.                         AddProfile("Harbor2", configData.Monuments.Harbor2, pos, rot);
  1213.                         continue;
  1214.                     }
  1215.                     if (gobject.name.Contains("junkyard"))
  1216.                     {
  1217.                         AddProfile("Junkyard", configData.Monuments.Junkyard, pos, rot);
  1218.                         continue;
  1219.                     }
  1220.                     if (gobject.name.Contains("launch_site"))
  1221.                     {
  1222.                         AddProfile("Launchsite", configData.Monuments.Launchsite, pos, rot);
  1223.                         continue;
  1224.                     }
  1225.                     if (gobject.name.Contains("lighthouse") && lighthouse == 0)
  1226.                     {
  1227.                         AddProfile("Lighthouse", configData.Monuments.Lighthouse, pos, rot);
  1228.                         lighthouse++;
  1229.                         continue;
  1230.                     }
  1231.  
  1232.                     if (gobject.name.Contains("lighthouse") && lighthouse == 1)
  1233.                     {
  1234.                         AddProfile("Lighthouse1", configData.Monuments.Lighthouse1, pos, rot);
  1235.                         lighthouse++;
  1236.                         continue;
  1237.                     }
  1238.  
  1239.                     if (gobject.name.Contains("lighthouse") && lighthouse == 2)
  1240.                     {
  1241.                         AddProfile("Lighthouse2", configData.Monuments.Lighthouse2, pos, rot);
  1242.                         lighthouse++;
  1243.                         continue;
  1244.                     }
  1245.  
  1246.                     if (gobject.name.Contains("military_tunnel_1"))
  1247.                     {
  1248.                         AddProfile("MilitaryTunnel", configData.Monuments.MilitaryTunnel, pos, rot);
  1249.                         continue;
  1250.                     }
  1251.                     if (gobject.name.Contains("powerplant_1"))
  1252.                     {
  1253.                         AddProfile("PowerPlant", configData.Monuments.PowerPlant, pos, rot);
  1254.                         continue;
  1255.                     }
  1256.                     if (gobject.name.Contains("mining_quarry_c"))
  1257.                     {
  1258.                         AddProfile("QuarryHQM", configData.Monuments.QuarryHQM, pos, rot);
  1259.                         continue;
  1260.                     }
  1261.                     if (gobject.name.Contains("mining_quarry_b"))
  1262.                     {
  1263.                         AddProfile("QuarryStone", configData.Monuments.QuarryStone, pos, rot);
  1264.                         continue;
  1265.                     }
  1266.                     if (gobject.name.Contains("mining_quarry_a"))
  1267.                     {
  1268.                         AddProfile("QuarrySulphur", configData.Monuments.QuarrySulphur, pos, rot);
  1269.                         continue;
  1270.                     }
  1271.                     if (gobject.name.Contains("radtown_small_3"))
  1272.                     {
  1273.                         AddProfile("SewerBranch", configData.Monuments.SewerBranch, pos, rot);
  1274.                         continue;
  1275.                     }
  1276.                     if (gobject.name.Contains("satellite_dish"))
  1277.                     {
  1278.                         AddProfile("Satellite", configData.Monuments.Satellite, pos, rot);
  1279.                         continue;
  1280.                     }
  1281.                     if (gobject.name.Contains("supermarket_1") && spermket == 0)
  1282.                     {
  1283.                         AddProfile("SuperMarket", configData.Monuments.SuperMarket, pos, rot);
  1284.                         spermket++;
  1285.                         continue;
  1286.                     }
  1287.  
  1288.                     if (gobject.name.Contains("supermarket_1") && spermket == 1)
  1289.                     {
  1290.                         AddProfile("SuperMarket1", configData.Monuments.SuperMarket1, pos, rot);
  1291.                         spermket++;
  1292.                         continue;
  1293.                     }
  1294.                     if (gobject.name.Contains("trainyard_1"))
  1295.                     {
  1296.                         AddProfile("Trainyard", configData.Monuments.Trainyard, pos, rot);
  1297.                         continue;
  1298.                     }
  1299.                     if (gobject.name.Contains("warehouse") && miningoutpost == 0)
  1300.                     {
  1301.                         AddProfile("MiningOutpost", configData.Monuments.MiningOutpost, pos, rot);
  1302.                         miningoutpost++;
  1303.                         continue;
  1304.                     }
  1305.  
  1306.                     if (gobject.name.Contains("warehouse") && miningoutpost == 1)
  1307.                     {
  1308.                         AddProfile("MiningOutpost1", configData.Monuments.MiningOutpost1, pos, rot);
  1309.                         miningoutpost++;
  1310.                         continue;
  1311.                     }
  1312.  
  1313.                     if (gobject.name.Contains("warehouse") && miningoutpost == 2)
  1314.                     {
  1315.                         AddProfile("MiningOutpost2", configData.Monuments.MiningOutpost2, pos, rot);
  1316.                         miningoutpost++;
  1317.                         continue;
  1318.                     }
  1319.                     if (gobject.name.Contains("water_treatment_plant_1"))
  1320.                     {
  1321.                         AddProfile("Watertreatment", configData.Monuments.Watertreatment, pos, rot);
  1322.                         continue;
  1323.                     }
  1324.  
  1325.                     if (gobject.name.Contains("swamp_a"))
  1326.                     {
  1327.                         AddProfile("Swamp_A", configData.Monuments.Swamp_A, pos, rot);
  1328.                         continue;
  1329.                     }
  1330.                     if (gobject.name.Contains("swamp_b"))
  1331.                     {
  1332.                         AddProfile("Swamp_B", configData.Monuments.Swamp_B, pos, rot);
  1333.                         continue;
  1334.                     }
  1335.                     if (gobject.name.Contains("swamp_c"))
  1336.                     {
  1337.                         AddProfile("Abandoned_Cabins", configData.Monuments.Abandoned_Cabins, pos, rot);
  1338.                         continue;
  1339.                     }
  1340.                     if (gobject.name.Contains("bandit_town"))
  1341.                     {
  1342.                         AddProfile("Bandit_Town", configData.Monuments.Bandit_Town, pos, rot);
  1343.                         continue;
  1344.                     }
  1345.                     if (gobject.name.Contains("compound") && compound > 2)
  1346.                         continue;
  1347.                     if (gobject.name.Contains("gas_station_1") && gasstation > 1)
  1348.                         continue;
  1349.                     if (gobject.name.Contains("lighthouse") && lighthouse > 2)
  1350.                         continue;
  1351.                     if (gobject.name.Contains("supermarket_1") && spermket > 1)
  1352.                         continue;
  1353.                     if (gobject.name.Contains("miningoutpost") && miningoutpost > 2)
  1354.                         continue;
  1355.                 }
  1356.             }
  1357.  
  1358.             if (configData.Biomes.BiomeArid.AutoSpawn == true)
  1359.             {
  1360.                 AddProfile("BiomeArid", configData.Biomes.BiomeArid, new Vector3(), 0f);
  1361.                 GenerateSpawnPoints("BiomeArid", configData.Biomes.BiomeArid.Bots, aridTimer, 1);
  1362.             }
  1363.             if (configData.Biomes.BiomeTemperate.AutoSpawn == true)
  1364.             {
  1365.                 AddProfile("BiomeTemperate", configData.Biomes.BiomeTemperate, new Vector3(), 0f);
  1366.                 GenerateSpawnPoints("BiomeTemperate", configData.Biomes.BiomeTemperate.Bots, temperateTimer, 2);
  1367.             }
  1368.             if (configData.Biomes.BiomeTundra.AutoSpawn == true)
  1369.             {
  1370.                 AddProfile("BiomeTundra", configData.Biomes.BiomeTundra, new Vector3(), 0f);
  1371.                 GenerateSpawnPoints("BiomeTundra", configData.Biomes.BiomeTundra.Bots, tundraTimer, 4);
  1372.             }
  1373.             if (configData.Biomes.BiomeArctic.AutoSpawn == true)
  1374.             {
  1375.                 AddProfile("BiomeArctic", configData.Biomes.BiomeArctic, new Vector3(), 0f);
  1376.                 GenerateSpawnPoints("BiomeArctic", configData.Biomes.BiomeArctic.Bots, arcticTimer, 8);
  1377.             }
  1378.  
  1379.             var drop = JsonConvert.SerializeObject(configData.Monuments.AirDrop);
  1380.             DataProfile Airdrop = JsonConvert.DeserializeObject<DataProfile>(drop);
  1381.             AllProfiles.Add("AirDrop", Airdrop);
  1382.  
  1383.             foreach (var profile in storedData.DataProfiles)
  1384.             {
  1385.  
  1386.                 if (!(storedData.MigrationDataDoNotEdit.ContainsKey(profile.Key)))
  1387.                     storedData.MigrationDataDoNotEdit.Add(profile.Key, new ProfileRelocation());
  1388.  
  1389.                 if (profile.Value.Parent_Monument != String.Empty)
  1390.                 {
  1391.                     var path = storedData.MigrationDataDoNotEdit[profile.Key];
  1392.  
  1393.                     if (AllProfiles.ContainsKey(profile.Value.Parent_Monument) && !IsBiome(profile.Value.Parent_Monument))
  1394.                     {
  1395.                         var configPath = AllProfiles[profile.Value.Parent_Monument];
  1396.  
  1397.                         path.ParentMonumentX = configPath.LocationX; //Incase user changed Parent after load
  1398.                         path.ParentMonumentY = configPath.LocationY;
  1399.                         path.ParentMonumentZ = configPath.LocationZ;
  1400.  
  1401.                         if (Mathf.Approximately(path.OldParentMonumentX, 0.0f)) //If it's a new entry, save current monument location info
  1402.                         {
  1403.                             Puts($"Saved migration data for {profile.Key}");
  1404.                             path.OldParentMonumentX = configPath.LocationX;
  1405.                             path.OldParentMonumentY = configPath.LocationY;
  1406.                             path.OldParentMonumentZ = configPath.LocationZ;
  1407.                             path.oldRotation = path.worldRotation;
  1408.                         }
  1409.  
  1410.                         if (!(Mathf.Approximately(path.ParentMonumentX, path.OldParentMonumentX))) //if old and new aren't equal
  1411.                         {
  1412.                             bool userChanged = false;
  1413.                             foreach (var monument in AllProfiles)
  1414.                                 if (Mathf.Approximately(monument.Value.LocationX, path.OldParentMonumentX)) //but old matches some other monument, then the user must have switched Parent
  1415.                                 {
  1416.                                     userChanged = true;
  1417.                                     break;
  1418.                                 }
  1419.  
  1420.                             if (userChanged)
  1421.                             {
  1422.                                 Puts($"Parent_Monument change detected - Saving {profile.Key} location relative to {profile.Value.Parent_Monument}");
  1423.                                 path.OldParentMonumentX = path.ParentMonumentX;
  1424.                                 path.OldParentMonumentY = path.ParentMonumentY;
  1425.                                 path.OldParentMonumentZ = path.ParentMonumentZ;
  1426.                                 path.oldRotation = path.worldRotation;
  1427.                             }
  1428.                             else
  1429.                             {
  1430.                                 Puts($"Map seed change detected - Updating {profile.Key} location relative to new {profile.Value.Parent_Monument}");
  1431.                                 Vector3 oldloc = new Vector3(profile.Value.LocationX, profile.Value.LocationY, profile.Value.LocationZ);
  1432.                                 Vector3 oldMonument = new Vector3(path.OldParentMonumentX, path.OldParentMonumentY, path.OldParentMonumentZ);
  1433.                                 Vector3 newMonument = new Vector3(path.ParentMonumentX, path.ParentMonumentY, path.ParentMonumentZ);
  1434.                                 //Map Seed Changed  
  1435.  
  1436.                                 var newTrans = new GameObject().transform;
  1437.                                 newTrans.transform.position = oldloc;
  1438.                                 newTrans.transform.RotateAround(oldMonument, Vector3.down, path.oldRotation);
  1439.  
  1440.                                 //spin old loc around old monument until mon-rotation is 0
  1441.                                 //get relationship between old location(rotated) minus monument
  1442.                                 Vector3 newLocPreRot = newMonument + (newTrans.transform.position - oldMonument);               //add that difference to the new monument location
  1443.  
  1444.                                 newTrans.transform.position = newLocPreRot;
  1445.                                 newTrans.transform.RotateAround(newMonument, Vector3.down, -path.worldRotation);
  1446.                                 Vector3 newLocation = newTrans.transform.position;                                              //rotate that number around the monument by new mon Rotation
  1447.  
  1448.                                 profile.Value.LocationX = newLocation.x;
  1449.                                 profile.Value.LocationY = newLocation.y;
  1450.                                 profile.Value.LocationZ = newLocation.z;
  1451.  
  1452.                                 path.oldRotation = path.worldRotation;
  1453.                                 path.OldParentMonumentX = configPath.LocationX;
  1454.                                 path.OldParentMonumentY = configPath.LocationY;
  1455.                                 path.OldParentMonumentZ = configPath.LocationZ;
  1456.                                 path.ParentMonumentX = configPath.LocationX;
  1457.                                 path.ParentMonumentY = configPath.LocationY;
  1458.                                 path.ParentMonumentZ = configPath.LocationZ;
  1459.                             }
  1460.                         }
  1461.                     }
  1462.                     else
  1463.                     {
  1464.                         Puts($"Parent monument {profile.Value.Parent_Monument} does not exist for custom profile {profile.Key}");
  1465.                         profile.Value.AutoSpawn = false;
  1466.                         SaveData();
  1467.                     }
  1468.                 }
  1469.                 SaveData();
  1470.                 AllProfiles.Add(profile.Key, profile.Value);
  1471.             }
  1472.  
  1473.             foreach (var profile in AllProfiles)
  1474.             {
  1475.                 if (IsBiome(profile.Key))
  1476.                     continue;
  1477.                 if (profile.Value.Kit.Count > 0 && Kits == null)
  1478.                 {
  1479.                     PrintWarning(lang.GetMessage("nokits", this), profile.Key);
  1480.                     continue;
  1481.                 }
  1482.  
  1483.                 if (profile.Value.AutoSpawn == true && profile.Value.Bots > 0 && !profile.Key.Contains("AirDrop"))
  1484.                 {
  1485.                     timer.Repeat(2, profile.Value.Bots, () =>
  1486.                     {
  1487.                         if (AllProfiles.Contains(profile))
  1488.                             SpawnBots(profile.Key, profile.Value, null, null, new Vector3());
  1489.                     });
  1490.                 }
  1491.             }
  1492.         }
  1493.  
  1494.         void AddProfile(string name, ConfigProfile monument, Vector3 pos, float rotation)//bring config data into live data
  1495.         {
  1496.             var toAdd = JsonConvert.SerializeObject(monument);
  1497.             DataProfile toAddDone = JsonConvert.DeserializeObject<DataProfile>(toAdd);
  1498.             if (AllProfiles.ContainsKey(name)) return;
  1499.             AllProfiles.Add(name, toAddDone);
  1500.             AllProfiles[name].LocationX = pos.x;
  1501.             AllProfiles[name].LocationY = pos.y;
  1502.             AllProfiles[name].LocationZ = pos.z;
  1503.             foreach (var custom in storedData.DataProfiles)
  1504.             {
  1505.                 if (custom.Value.Parent_Monument == name && storedData.MigrationDataDoNotEdit.ContainsKey(custom.Key))
  1506.                 {
  1507.                     var path = storedData.MigrationDataDoNotEdit[custom.Key];
  1508.                     if (Mathf.Approximately(path.oldRotation, 0))
  1509.                         path.oldRotation = rotation;
  1510.  
  1511.                     path.worldRotation = rotation;
  1512.                     path.ParentMonumentX = pos.x;
  1513.                     path.ParentMonumentY = pos.y;
  1514.                     path.ParentMonumentZ = pos.z;
  1515.                 }
  1516.             }
  1517.             SaveData();
  1518.         }
  1519.         #endregion
  1520.  
  1521.         void SaveData()
  1522.         {
  1523.             Interface.Oxide.DataFileSystem.WriteObject("BotSpawn", storedData);
  1524.         }
  1525.         #region Commands
  1526.         [ConsoleCommand("bot.count")]
  1527.         void CmdBotCount()
  1528.         {
  1529.             string msg = (NPCPlayers.Count == 1) ? "numberOfBot" : "numberOfBots";
  1530.             PrintWarning(lang.GetMessage(msg, this), NPCPlayers.Count);
  1531.         }
  1532.  
  1533.         [ConsoleCommand("botspawn")]
  1534.         private void CmdBotSpawn(ConsoleSystem.Arg arg)
  1535.         {
  1536.             if (arg.Args == null || arg.Args.Length != 2) return;
  1537.             foreach (var bot in NPCPlayers.Where(bot => bot.Value.GetComponent<BotData>().monumentName == arg.Args[0]))
  1538.                 foreach (var gun in bot.Value.GetComponent<BotData>().AllProjectiles.Where(gun => gun.info.shortname == arg.Args[1]))
  1539.                     ChangeWeapon(bot.Value, gun);
  1540.         }
  1541.  
  1542.         [ChatCommand("botspawn")]
  1543.         void Botspawn(BasePlayer player, string command, string[] args)
  1544.         {
  1545.             if (HasPermission(player.UserIDString, permAllowed) || IsAuth(player))
  1546.                 if (args != null && args.Length == 1)
  1547.                 {
  1548.                     if (args[0] == "list")
  1549.                     {
  1550.                         var outMsg = lang.GetMessage("ListTitle", this);
  1551.  
  1552.                         foreach (var profile in storedData.DataProfiles)
  1553.                         {
  1554.                             outMsg += $"\n{profile.Key}";
  1555.                         }
  1556.                         PrintToChat(player, outMsg);
  1557.                     }
  1558.                     else
  1559.                         SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("error", this));
  1560.                 }
  1561.                 else if (args != null && args.Length == 2)
  1562.                 {
  1563.                     if (args[0] == "add")
  1564.                     {
  1565.                         var name = args[1];
  1566.                         if (AllProfiles.ContainsKey(name))
  1567.                         {
  1568.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("alreadyexists", this), name);
  1569.                             return;
  1570.                         }
  1571.                         Vector3 pos = player.transform.position;
  1572.  
  1573.                         var customSettings = new DataProfile()
  1574.                         {
  1575.                             AutoSpawn = false,
  1576.                             BotNames = new List<string> { String.Empty },
  1577.                             LocationX = pos.x,
  1578.                             LocationY = pos.y,
  1579.                             LocationZ = pos.z,
  1580.                         };
  1581.  
  1582.                         storedData.DataProfiles.Add(name, customSettings);
  1583.                         SaveData();
  1584.                         SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("customsaved", this), player.transform.position);
  1585.                     }
  1586.  
  1587.                     else if (args[0] == "move")
  1588.                     {
  1589.                         var name = args[1];
  1590.                         if (storedData.DataProfiles.ContainsKey(name))
  1591.                         {
  1592.                             storedData.DataProfiles[name].LocationX = player.transform.position.x;
  1593.                             storedData.DataProfiles[name].LocationY = player.transform.position.y;
  1594.                             storedData.DataProfiles[name].LocationZ = player.transform.position.z;
  1595.                             SaveData();
  1596.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("custommoved", this), name);
  1597.                         }
  1598.                         else
  1599.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("noprofile", this));
  1600.                     }
  1601.  
  1602.                     else if (args[0] == "remove")
  1603.                     {
  1604.                         var name = args[1];
  1605.                         if (storedData.DataProfiles.ContainsKey(name))
  1606.                         {
  1607.                             foreach (var bot in NPCPlayers)
  1608.                             {
  1609.                                 if (bot.Value == null)
  1610.                                     continue;
  1611.  
  1612.                                 var bData = bot.Value.GetComponent<BotData>();
  1613.                                 if (bData.monumentName == name)
  1614.                                     bot.Value.Kill();
  1615.                             }
  1616.                             AllProfiles.Remove(name);
  1617.                             storedData.DataProfiles.Remove(name);
  1618.                             SaveData();
  1619.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("customremoved", this), name);
  1620.                         }
  1621.                         else
  1622.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("noprofile", this));
  1623.                     }
  1624.                     else
  1625.                         SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("error", this));
  1626.                 }
  1627.                 else if (args != null && args.Length == 3)
  1628.                 {
  1629.                     if (args[0] == "toplayer")
  1630.                     {
  1631.                         var name = args[1];
  1632.                         var profile = args[2].ToLower();
  1633.                         BasePlayer target = FindPlayerByName(name);
  1634.                         Vector3 location = (CalculateGroundPos(player.transform.position));
  1635.                         var found = false;
  1636.                         if (target == null)
  1637.                         {
  1638.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("namenotfound", this), name);
  1639.                             return;
  1640.                         }
  1641.                         foreach (var entry in AllProfiles.Where(entry => entry.Key.ToLower() == profile))
  1642.                         {
  1643.                             AttackPlayer(location, entry.Key, entry.Value, null);
  1644.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("deployed", this), entry.Key, target.displayName);
  1645.                             found = true;
  1646.                             return;
  1647.                         }
  1648.                         if (!found)
  1649.                         {
  1650.                             SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("noprofile", this));
  1651.                             return;
  1652.                         }
  1653.  
  1654.                     }
  1655.                     else
  1656.                         SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("error", this));
  1657.                 }
  1658.                 else
  1659.                     SendReply(player, "<color=orange>" + lang.GetMessage("Title", this) + "</color>" + lang.GetMessage("error", this));
  1660.         }
  1661.         #endregion
  1662.  
  1663.         public List<ulong> DeadNPCPlayerIds = new List<ulong>(); //to tracebackpacks
  1664.         public Dictionary<ulong, KitData> KitList = new Dictionary<ulong, KitData>();
  1665.         public Dictionary<ulong, string> KitRemoveList = new Dictionary<ulong, string>();
  1666.         public List<Vector3> SmokeGrenades = new List<Vector3>();
  1667.         public List<ulong> AggroPlayers = new List<ulong>();
  1668.  
  1669.         #region BotMono
  1670.         public class KitData
  1671.         {
  1672.             public string Kit;
  1673.             public bool Wipe_Belt;
  1674.             public bool Wipe_Clothing;
  1675.             public bool Allow_Rust_Loot;
  1676.         }
  1677.  
  1678.         public class BotData : MonoBehaviour
  1679.         {
  1680.             public NPCPlayerApex npc;
  1681.             public DataProfile profile;
  1682.             public Vector3 spawnPoint;
  1683.             public float enemyDistance, damage;
  1684.             public List<Item> AllProjectiles = new List<Item>();
  1685.             public List<Item> MeleeWeapons = new List<Item>();
  1686.             public List<Item> CloseRangeWeapons = new List<Item>();
  1687.             public List<Item> MediumRangeWeapons = new List<Item>();
  1688.             public List<Item> LongRangeWeapons = new List<Item>();
  1689.             public int dropweapon, roamRange, accuracy, currentWeaponRange, LongRangeAttack = 120, peaceKeeper_CoolDown = 5, landingAttempts;
  1690.             public string monumentName, group; //external hook identifier
  1691.             public bool chute, inAir, peaceKeeper, keepAttire, goingHome, biome, respawn;
  1692.  
  1693.             Vector3 landingDirection = new Vector3(0, 0, 0);
  1694.             int updateCounter;
  1695.  
  1696.             void Start()
  1697.             {
  1698.                 npc = GetComponent<NPCPlayerApex>();
  1699.                 if (chute)
  1700.                 {
  1701.                     inAir = true;
  1702.                     npc.Stats.AggressionRange = 300f;
  1703.                     npc.utilityAiComponent.enabled = true;
  1704.                 }
  1705.                 float delay = random.Next(300, 1200);
  1706.                 InvokeRepeating("Relocate", delay, delay);
  1707.             }
  1708.  
  1709.             void OnDestroy() => CancelInvoke("Relocate");
  1710.  
  1711.             void Relocate()
  1712.             {
  1713.                 if (biome)
  1714.                 {
  1715.                     var ranNum = random.Next(botSpawn.spawnLists[monumentName].Count);
  1716.                     spawnPoint = botSpawn.spawnLists[monumentName][ranNum];
  1717.                     return;
  1718.                 }
  1719.                 var pos = new Vector3(profile.LocationX, profile.LocationY, profile.LocationZ);
  1720.                 var randomTerrainPoint = botSpawn.TryGetSpawn(pos, profile.Radius);
  1721.                 if (randomTerrainPoint != new Vector3())
  1722.                     spawnPoint = randomTerrainPoint + new Vector3(0, 0.5f, 0);
  1723.             }
  1724.  
  1725.             void OnCollisionEnter(Collision collision)
  1726.             {
  1727.                 if (!inAir)
  1728.                     return;
  1729.                 var rb = npc.gameObject.GetComponent<Rigidbody>();
  1730.                 var terrainDif = npc.transform.position.y - CalculateGroundPos(npc.transform.position).y;
  1731.                 if (landingAttempts == 0)
  1732.                     landingDirection = npc.transform.forward;
  1733.  
  1734.                 if (collision.collider.name.Contains("npc"))
  1735.                     return;
  1736.                 if (collision.collider.name.Contains("Terrain") || landingAttempts > 5)
  1737.                 {
  1738.                     NavMeshHit hit;
  1739.                     if (NavMesh.SamplePosition(npc.transform.position, out hit, 50, 1))
  1740.                     {
  1741.                         rb.isKinematic = true;
  1742.                         rb.useGravity = false;
  1743.                         npc.gameObject.layer = 17;
  1744.                         npc.ServerPosition = hit.position;
  1745.                         npc.GetNavAgent.Warp(npc.ServerPosition);
  1746.                         npc.Stats.AggressionRange = botSpawn.AllProfiles[monumentName].Sci_Aggro_Range;
  1747.                         foreach (var child in npc.children.Where(child => child.name.Contains("parachute")))
  1748.                         {
  1749.                             child.SetParent(null);
  1750.                             child.Kill();
  1751.                             break;
  1752.                         }
  1753.                         SetSpawn(npc);
  1754.                         landingAttempts = 0;
  1755.                     }
  1756.                 }
  1757.                 else
  1758.                 {
  1759.                     landingAttempts++;
  1760.                     rb.useGravity = true;
  1761.                     rb.velocity = new Vector3(landingDirection.x * 15, 11, landingDirection.z * 15);
  1762.                     rb.drag = 1f;
  1763.                 }
  1764.             }
  1765.  
  1766.             void SetSpawn(NPCPlayerApex bot)
  1767.             {
  1768.                 inAir = false;
  1769.                 spawnPoint = bot.transform.position;
  1770.                 bot.SpawnPosition = bot.transform.position;
  1771.                 bot.Resume();
  1772.             }
  1773.  
  1774.             void Update()
  1775.             {
  1776.                 updateCounter++;
  1777.                 if (updateCounter == 1000)
  1778.                 {
  1779.                     updateCounter = 0;
  1780.                     if (inAir)
  1781.                     {
  1782.                         if (npc.AttackTarget is BasePlayer && !(npc.AttackTarget is NPCPlayer))
  1783.                             npc.SetAimDirection((npc.AttackTarget.transform.position - npc.GetPosition()).normalized);
  1784.                         goingHome = false;
  1785.                     }
  1786.                     else
  1787.                     {
  1788.                         if ((npc.GetFact(NPCPlayerApex.Facts.IsAggro)) == 0 && npc.GetNavAgent.isOnNavMesh)
  1789.                         {
  1790.                             var distance = Vector3.Distance(npc.transform.position, spawnPoint);
  1791.                             if (!goingHome && distance > roamRange)
  1792.                                 goingHome = true;
  1793.  
  1794.  
  1795.                             if (goingHome && distance > 5)
  1796.                             {
  1797.                                 npc.CurrentBehaviour = BaseNpc.Behaviour.Wander;
  1798.                                 npc.SetFact(NPCPlayerApex.Facts.Speed, (byte)NPCPlayerApex.SpeedEnum.Walk, true, true);
  1799.                                 npc.TargetSpeed = 2.4f;
  1800.                                 npc.GetNavAgent.SetDestination(spawnPoint);
  1801.                                 npc.Destination = spawnPoint;
  1802.                             }
  1803.                             else
  1804.                                 goingHome = false;
  1805.                         }
  1806.                     }
  1807.                 }
  1808.             }
  1809.         }
  1810.         #endregion
  1811.  
  1812.         #region Config
  1813.         private ConfigData configData;
  1814.  
  1815.         public Dictionary<ulong, NPCPlayerApex> NPCPlayers = new Dictionary<ulong, NPCPlayerApex>();
  1816.         public Dictionary<string, DataProfile> AllProfiles = new Dictionary<string, DataProfile>();
  1817.  
  1818.         public class Global
  1819.         {
  1820.             public bool NPCs_Attack_BotSpawn = true;
  1821.             public bool BotSpawn_Attacks_NPCs = true;
  1822.             public bool BotSpawn_Attacks_BotSpawn;
  1823.             public bool APC_Safe = true;
  1824.             public bool Turret_Safe = true;
  1825.             public bool Animal_Safe = true;
  1826.             public bool Supply_Enabled;
  1827.             public bool Remove_BackPacks = true;
  1828.             public bool Remove_KeyCard = true;
  1829.             public bool Ignore_HumanNPC = true;
  1830.             public bool Ignore_HTN = true;
  1831.             public bool Ignore_Sleepers = true;
  1832.             public bool Pve_Safe = true;
  1833.             public int Corpse_Duration = 60;
  1834.         }
  1835.         public class Monuments
  1836.         {
  1837.             public AirDropProfile AirDrop = new AirDropProfile { };
  1838.             public ConfigProfile Airfield = new ConfigProfile { };
  1839.             public ConfigProfile Dome = new ConfigProfile { };
  1840.             public ConfigProfile Compound = new ConfigProfile { };
  1841.             public ConfigProfile Compound1 = new ConfigProfile { };
  1842.             public ConfigProfile Compound2 = new ConfigProfile { };
  1843.             public ConfigProfile GasStation = new ConfigProfile { };
  1844.             public ConfigProfile GasStation1 = new ConfigProfile { };
  1845.             public ConfigProfile Harbor1 = new ConfigProfile { };
  1846.             public ConfigProfile Harbor2 = new ConfigProfile { };
  1847.             public ConfigProfile Junkyard = new ConfigProfile { };
  1848.             public ConfigProfile Launchsite = new ConfigProfile { };
  1849.             public ConfigProfile Lighthouse = new ConfigProfile { };
  1850.             public ConfigProfile Lighthouse1 = new ConfigProfile { };
  1851.             public ConfigProfile Lighthouse2 = new ConfigProfile { };
  1852.             public ConfigProfile MilitaryTunnel = new ConfigProfile { };
  1853.             public ConfigProfile PowerPlant = new ConfigProfile { };
  1854.             public ConfigProfile QuarrySulphur = new ConfigProfile { };
  1855.             public ConfigProfile QuarryStone = new ConfigProfile { };
  1856.             public ConfigProfile QuarryHQM = new ConfigProfile { };
  1857.             public ConfigProfile SuperMarket = new ConfigProfile { };
  1858.             public ConfigProfile SuperMarket1 = new ConfigProfile { };
  1859.             public ConfigProfile SewerBranch = new ConfigProfile { };
  1860.             public ConfigProfile Satellite = new ConfigProfile { };
  1861.             public ConfigProfile Trainyard = new ConfigProfile { };
  1862.             public ConfigProfile MiningOutpost = new ConfigProfile { };
  1863.             public ConfigProfile MiningOutpost1 = new ConfigProfile { };
  1864.             public ConfigProfile MiningOutpost2 = new ConfigProfile { };
  1865.             public ConfigProfile Watertreatment = new ConfigProfile { };
  1866.             public ConfigProfile Swamp_A = new ConfigProfile { };
  1867.             public ConfigProfile Swamp_B = new ConfigProfile { };
  1868.             public ConfigProfile Abandoned_Cabins = new ConfigProfile { };
  1869.             public ConfigProfile Bandit_Town = new ConfigProfile { };
  1870.         }
  1871.  
  1872.         public class Biomes
  1873.         {
  1874.             public ConfigProfile BiomeArid = new ConfigProfile { };
  1875.             public ConfigProfile BiomeTemperate = new ConfigProfile { };
  1876.             public ConfigProfile BiomeTundra = new ConfigProfile { };
  1877.             public ConfigProfile BiomeArctic = new ConfigProfile { };
  1878.         }
  1879.         public class AirDropProfile
  1880.         {
  1881.             public bool AutoSpawn;
  1882.             public bool Murderer;
  1883.             public int Bots = 5;
  1884.             public int BotHealth = 100;
  1885.             public int Radius = 100;
  1886.             public List<string> Kit = new List<string>();
  1887.             public string BotNamePrefix = String.Empty;
  1888.             public List<string> BotNames = new List<string>();
  1889.             public int Bot_Accuracy = 4;
  1890.             public float Bot_Damage = 0.4f;
  1891.             public bool Disable_Radio = true;
  1892.             public int Roam_Range = 40;
  1893.             public bool Peace_Keeper = true;
  1894.             public int Peace_Keeper_Cool_Down = 5;
  1895.             public int Weapon_Drop_Chance = 100;
  1896.             public bool Keep_Default_Loadout;
  1897.             public bool Wipe_Belt = true;
  1898.             public bool Wipe_Clothing = true;
  1899.             public bool Allow_Rust_Loot = true;
  1900.             public int Suicide_Timer = 300;
  1901.             public bool Chute;
  1902.             public int Sci_Aggro_Range = 30;
  1903.             public int Sci_DeAggro_Range = 40;
  1904.         }
  1905.  
  1906.         public class ConfigProfile
  1907.         {
  1908.             public bool AutoSpawn;
  1909.             public bool Murderer;
  1910.             public int Bots = 5;
  1911.             public int BotHealth = 100;
  1912.             public int Radius = 100;
  1913.             public List<string> Kit = new List<string>();
  1914.             public string BotNamePrefix = String.Empty;
  1915.             public List<string> BotNames = new List<string>();
  1916.             public int Bot_Accuracy = 4;
  1917.             public float Bot_Damage = 0.4f;
  1918.             public bool Disable_Radio = true;
  1919.             public int Roam_Range = 40;
  1920.             public bool Peace_Keeper = true;
  1921.             public int Peace_Keeper_Cool_Down = 5;
  1922.             public int Weapon_Drop_Chance = 100;
  1923.             public bool Keep_Default_Loadout;
  1924.             public bool Wipe_Belt = true;
  1925.             public bool Wipe_Clothing = true;
  1926.             public bool Allow_Rust_Loot = true;
  1927.             public int Suicide_Timer = 300;
  1928.             public bool Chute;
  1929.             public int Respawn_Timer = 60;
  1930.             public int Sci_Aggro_Range = 30;
  1931.             public int Sci_DeAggro_Range = 40;
  1932.         }
  1933.  
  1934.         public class DataProfile
  1935.         {
  1936.             public bool AutoSpawn;
  1937.             public bool Murderer;
  1938.             public int Bots = 5;
  1939.             public int BotHealth = 100;
  1940.             public int Radius = 100;
  1941.             public List<string> Kit = new List<string>();
  1942.             public string BotNamePrefix = String.Empty;
  1943.             public List<string> BotNames = new List<string>();
  1944.             public int Bot_Accuracy = 4;
  1945.             public float Bot_Damage = 0.4f;
  1946.             public bool Disable_Radio = true;
  1947.             public int Roam_Range = 40;
  1948.             public bool Peace_Keeper = true;
  1949.             public int Peace_Keeper_Cool_Down = 5;
  1950.             public int Weapon_Drop_Chance = 100;
  1951.             public bool Keep_Default_Loadout;
  1952.             public bool Wipe_Belt = true;
  1953.             public bool Wipe_Clothing = true;
  1954.             public bool Allow_Rust_Loot = true;
  1955.             public int Suicide_Timer = 300;
  1956.             public bool Chute;
  1957.             public int Respawn_Timer = 60;
  1958.             public float LocationX;
  1959.             public float LocationY;
  1960.             public float LocationZ;
  1961.             public string Parent_Monument = String.Empty;
  1962.             public int Sci_Aggro_Range = 30;
  1963.             public int Sci_DeAggro_Range = 40;
  1964.         }
  1965.  
  1966.         public class ProfileRelocation
  1967.         {
  1968.             public float OldParentMonumentX;
  1969.             public float OldParentMonumentY;
  1970.             public float OldParentMonumentZ;
  1971.             public float ParentMonumentX;
  1972.             public float ParentMonumentY;
  1973.             public float ParentMonumentZ;
  1974.             public float oldRotation;
  1975.             public float worldRotation;
  1976.         }
  1977.  
  1978.         class ConfigData
  1979.         {
  1980.             public Global Global = new Global();
  1981.             public Monuments Monuments = new Monuments();
  1982.             public Biomes Biomes = new Biomes();
  1983.         }
  1984.  
  1985.         private void LoadConfigVariables()
  1986.         {
  1987.             configData = Config.ReadObject<ConfigData>();
  1988.             SaveConfig(configData);
  1989.         }
  1990.         protected override void LoadDefaultConfig()
  1991.         {
  1992.             Puts("Creating new config file.");
  1993.             var config = new ConfigData();
  1994.             SaveConfig(config);
  1995.         }
  1996.  
  1997.         void SaveConfig(ConfigData config)
  1998.         {
  1999.             Config.WriteObject(config, true);
  2000.         }
  2001.         #endregion
  2002.  
  2003.         #region Messages    
  2004.         Dictionary<string, string> Messages = new Dictionary<string, string>()
  2005.         {
  2006.             {"Title", "BotSpawn : " },
  2007.             {"error", "/botspawn commands are - list - add - remove - move - toplayer" },
  2008.             {"customsaved", "Custom Location Saved @ {0}" },
  2009.             {"custommoved", "Custom Location {0} has been moved to your current position." },
  2010.             {"alreadyexists", "Custom Location already exists with the name {0}." },
  2011.             {"customremoved", "Custom Location {0} Removed." },
  2012.             {"deployed", "'{0}' bots deployed to {1}." },
  2013.             {"ListTitle", "Custom Locations" },
  2014.             {"noprofile", "There is no profile by that name in config or data BotSpawn.json files." },
  2015.             {"namenotfound", "Player '{0}' was not found" },
  2016.             {"nokits", "Kits is not installed but you have declared custom kits at {0}." },
  2017.             {"noWeapon", "A bot at {0} has no weapon. Check your kits." },
  2018.             {"numberOfBot", "There is {0} spawned bot alive." },
  2019.             {"numberOfBots", "There are {0} spawned bots alive." },
  2020.             {"dupID", "Duplicate userID save attempted. Please notify author." },
  2021.             {"noSpawn", "Failed to find spawnpoints at {0}." }
  2022.         };
  2023.         #endregion
  2024.  
  2025.         #region ExternalHooks
  2026.         private Dictionary<string, List<ulong>> BotSpawnBots()
  2027.         {
  2028.             var BotSpawnBots = new Dictionary<string, List<ulong>>();
  2029.  
  2030.             foreach (var monument in AllProfiles.Where(monument => monument.Value.AutoSpawn))
  2031.                 BotSpawnBots.Add(monument.Key, new List<ulong>());
  2032.  
  2033.             foreach (var bot in NPCPlayers)
  2034.             {
  2035.                 var bData = bot.Value.GetComponent<BotData>();
  2036.                 if (BotSpawnBots.ContainsKey(bData.monumentName))
  2037.                     BotSpawnBots[bData.monumentName].Add(bot.Key);
  2038.             }
  2039.             return BotSpawnBots;
  2040.         }
  2041.  
  2042.         private string[] AddGroupSpawn(Vector3 location, string profileName, string group)
  2043.         {
  2044.             if (location == new Vector3() || profileName == null || group == null)
  2045.                 return new string[] { "error", "Null parameter" };
  2046.             string lowerProfile = profileName.ToLower();
  2047.  
  2048.             foreach (var entry in AllProfiles)
  2049.             {
  2050.                 if (entry.Key.ToLower() == lowerProfile)
  2051.                 {
  2052.                     var profile = entry.Value;
  2053.                     AttackPlayer(location, entry.Key, profile, group.ToLower());
  2054.                     return new string[] { "true", "Group successfully added" };
  2055.                 }
  2056.             }
  2057.             return new string[] { "false", "Group add failed - Check profile name and try again" };
  2058.         }
  2059.  
  2060.         private string[] RemoveGroupSpawn(string group)
  2061.         {
  2062.             if (group == null)
  2063.                 return new string[] { "error", "No group specified." };
  2064.  
  2065.             List<NPCPlayerApex> toDestroy = new List<NPCPlayerApex>();
  2066.             foreach (var bot in NPCPlayers)
  2067.             {
  2068.                 if (bot.Value == null)
  2069.                     continue;
  2070.                 var bData = bot.Value.GetComponent<BotData>();
  2071.                 if (bData.group == group.ToLower())
  2072.                     toDestroy.Add(bot.Value);
  2073.             }
  2074.             if (toDestroy.Count == 0)
  2075.                 return new string[] { "true", $"There are no bots belonging to {group}" };
  2076.             foreach (var killBot in toDestroy)
  2077.             {
  2078.                 UpdateRecords(killBot);
  2079.                 killBot.Kill();
  2080.             }
  2081.             return new string[] { "true", $"Group {group} was destroyed." };
  2082.  
  2083.         }
  2084.  
  2085.         private string[] CreateNewProfile(string name, string profile)
  2086.         {
  2087.             if (name == null)
  2088.                 return new string[] { "error", "No name specified." };
  2089.             if (profile == null)
  2090.                 return new string[] { "error", "No profile settings specified." };
  2091.  
  2092.             DataProfile newProfile = JsonConvert.DeserializeObject<DataProfile>(profile);
  2093.  
  2094.             if (storedData.DataProfiles.ContainsKey(name))
  2095.             {
  2096.                 storedData.DataProfiles[name] = newProfile;
  2097.                 AllProfiles[name] = newProfile;
  2098.                 return new string[] { "true", $"Profile {name} was updated" };
  2099.             }
  2100.  
  2101.             storedData.DataProfiles.Add(name, newProfile);
  2102.             SaveData();
  2103.             AllProfiles.Add(name, newProfile);
  2104.             return new string[] { "true", $"New Profile {name} was created." };
  2105.         }
  2106.  
  2107.         private string[] ProfileExists(string name)
  2108.         {
  2109.             if (name == null)
  2110.                 return new string[] { "error", "No name specified." };
  2111.  
  2112.             if (AllProfiles.ContainsKey(name))
  2113.                 return new string[] { "true", $"{name} exists." };
  2114.  
  2115.             return new string[] { "false", $"{name} Does not exist." };
  2116.         }
  2117.  
  2118.         private string[] RemoveProfile(string name)
  2119.         {
  2120.             if (name == null)
  2121.                 return new string[] { "error", "No name specified." };
  2122.  
  2123.             if (storedData.DataProfiles.ContainsKey(name))
  2124.             {
  2125.                 foreach (var bot in NPCPlayers)
  2126.                 {
  2127.                     if (bot.Value == null)
  2128.                         continue;
  2129.  
  2130.                     var bData = bot.Value.GetComponent<BotData>();
  2131.                     if (bData.monumentName == name)
  2132.                         bot.Value.Kill();
  2133.                 }
  2134.                 AllProfiles.Remove(name);
  2135.                 storedData.DataProfiles.Remove(name);
  2136.                 SaveData();
  2137.                 return new string[] { "true", $"Profile {name} was removed." };
  2138.             }
  2139.             else
  2140.                 return new string[] { "false", $"Profile {name} Does Not Exist." };
  2141.         }
  2142.         #endregion
  2143.     }
  2144. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement