Иконка ресурса

FREE SimpleBotSpawner : 1.0.0 1.0.0

Нет прав для скачивания
C#:
using System.Collections.Generic;
using Oxide.Core;
using UnityEngine;
using Rust;
using Oxide.Core.Plugins;
using Facepunch;

namespace Oxide.Plugins
{
    [Info("SimpleBotSpawner", "AI Assistant", "1.0.0")]
    [Description("Спавнит 30 голых ботов с луком рядом с игроками и заставляет их атаковать")]
    class SimpleBotSpawner : RustPlugin
    {
        private List<NPCPlayer> spawnedBots = new List<NPCPlayer>();
        private int botCount = 30;
        private float spawnRadius = 150f;
        private string[] botNames = new string[] {
            "ShadowReaper", "ToxicWolf", "RustyNail", "DeadlyAim", "SilentHunter", "MadDog", "NightStalker", "LoneSurvivor", "IronFist", "GhostRider",
            "SavageKing", "BulletStorm", "FrostBite", "Wasteland", "Bloodhound", "Vandal", "Nomad", "Bandit", "Outlaw", "SniperQueen",
            "Venom", "Rogue", "Mercenary", "Warlord", "Blaze", "Viper", "Maverick", "Raider", "Butcher", "Grim",
            "AlphaWolf", "Omega", "Phoenix", "Raven", "Havoc", "Rebel", "Juggernaut", "Specter", "Predator", "Berserk",
            "RustyBlade", "BoneCrusher", "WildCard", "Storm", "Dusk", "Echo", "Pulse", "Zero", "Trigger", "Bullet"
        };
        private Dictionary<NPCPlayer, bool> botEvading = new Dictionary<NPCPlayer, bool>();
        private Dictionary<NPCPlayer, float> botReturnTime = new Dictionary<NPCPlayer, float>();
        private Vector3? lastEvadePos = null;
        private enum BotActionType { Chase, Circle, Flank, Retreat, Patrol, Idle, Hide }
        private Dictionary<NPCPlayer, BotActionType> botActions = new Dictionary<NPCPlayer, BotActionType>();
        private Dictionary<NPCPlayer, float> botActionChangeTime = new Dictionary<NPCPlayer, float>();
        private System.Random sysRand = new System.Random();

        void OnPlayerRespawned(BasePlayer player)
        {
            SpawnBotNearPlayer(player);
        }

        void Unload()
        {
            foreach (var bot in spawnedBots)
            {
                if (bot != null && !bot.IsDestroyed)
                    bot.Kill();
            }
            spawnedBots.Clear();
            botEvading.Clear();
            botReturnTime.Clear();
        }

        private void SpawnBotNearPlayer(BasePlayer player)
        {
            Vector3 spawnPos = Vector3.zero;
            bool found = false;
            for (int i = 0; i < 150; i++)
            {
                Vector2 offset = UnityEngine.Random.insideUnitCircle * spawnRadius;
                Vector3 tryPos = new Vector3(player.transform.position.x + offset.x, 0, player.transform.position.z + offset.y);
                tryPos.y = TerrainMeta.HeightMap.GetHeight(tryPos);
                var blocks = Pool.GetList<BuildingBlock>();
                Vis.Entities<BuildingBlock>(tryPos, 2f, blocks, 1 << 21);
                if (blocks.Count > 0)
                {
                    Pool.FreeList(ref blocks);
                    continue;
                }
                Pool.FreeList(ref blocks);
                var ents = Pool.GetList<BaseEntity>();
                Vis.Entities<BaseEntity>(tryPos, 2f, ents, -1, QueryTriggerInteraction.Ignore);
                bool hasPrefab = false;
                foreach (var ent in ents)
                {
                    if (ent is BuildingBlock) continue;
                    if (ent.PrefabName.Contains("door") || ent.PrefabName.Contains("wall") || ent.PrefabName.Contains("monument") || ent.PrefabName.Contains("prefab") || ent.PrefabName.Contains("item"))
                    {
                        hasPrefab = true;
                        break;
                    }
                }
                Pool.FreeList(ref ents);
                if (hasPrefab)
                {
                    continue;
                }
                RaycastHit upHit;
                if (Physics.Raycast(tryPos + Vector3.up * 1f, Vector3.up, out upHit, 30f))
                {
                    continue;
                }
                RaycastHit downHit;
                Vector3 skyPos = new Vector3(tryPos.x, 1000f, tryPos.z);
                if (Physics.Raycast(skyPos, Vector3.down, out downHit, 2000f))
                {
                    if (Mathf.Abs(downHit.point.y - tryPos.y) > 0.5f) {
                        continue;
                    }
                    if (downHit.collider.gameObject.layer != LayerMask.NameToLayer("Terrain")) {
                        continue;
                    }
                    spawnPos = downHit.point;
                    found = true;
                    break;
                } else {
                }
            }
            if (!found) {
                return;
            }

            var bot = GameManager.server.CreateEntity("assets/rust.ai/agents/npcplayer/pet/frankensteinpet.prefab", spawnPos) as NPCPlayer;
            if (bot == null) return;

            bot.Spawn();
            bot.inventory.Strip();
            var bow = ItemManager.CreateByName("bow.hunting", 1);
            var arrows = ItemManager.CreateByName("arrow.wooden", 30);
            bot.inventory.GiveItem(bow, bot.inventory.containerBelt);
            bot.inventory.GiveItem(arrows, bot.inventory.containerMain);
            // Медицинские предметы
            for (int i = 0; i < 5; i++)
            {
                var med = ItemManager.CreateByName("syringe.medical", 1);
                if (med != null) bot.inventory.GiveItem(med, bot.inventory.containerBelt);
            }
            for (int i = 0; i < 10; i++)
            {
                var bandage = ItemManager.CreateByName("bandage", 1);
                if (bandage != null) bot.inventory.GiveItem(bandage, bot.inventory.containerBelt);
            }
            string randomName = botNames[UnityEngine.Random.Range(0, botNames.Length)];
            bot.displayName = randomName;
            bot.InitializeHealth(100, 100);
            bot.UpdateProtectionFromClothing();
            bot.UpdateActiveItem(bow.uid);

            var insideBlocks = Pool.GetList<BuildingBlock>();
            Vis.Entities<BuildingBlock>(bot.transform.position, 0.5f, insideBlocks, 1 << 21);
            if (insideBlocks.Count > 0)
            {
                bot.Kill();
                Pool.FreeList(ref insideBlocks);
                return;
            }
            Pool.FreeList(ref insideBlocks);

            spawnedBots.Add(bot);
            botEvading[bot] = false;
            HuntPlayer(bot, player);
        }

        private void HuntPlayer(NPCPlayer bot, BasePlayer target)
        {
            timer.Repeat(0.5f, 0, () => {
                if (botEvading.ContainsKey(bot) && botEvading[bot]) return;
                if (bot == null || bot.IsDestroyed || target == null || target.IsDead()) return;
                if (botReturnTime.ContainsKey(bot) && Time.realtimeSinceStartup < botReturnTime[bot]) return;

                float now = Time.realtimeSinceStartup;
                if (!botActions.ContainsKey(bot) || now > botActionChangeTime.GetValueOrDefault(bot, 0))
                {
                    BotActionType newAction = ChooseBotAction(bot, target);
                    botActions[bot] = newAction;
                    botActionChangeTime[bot] = now + sysRand.Next(2, 6); // 2-5 сек
                }
                BotActionType action = botActions[bot];
                DoBotAction(bot, target, action);
            });
        }

        private BotActionType ChooseBotAction(NPCPlayer bot, BasePlayer target)
        {
            float dist = Vector3.Distance(bot.transform.position, target.transform.position);
            int roll = sysRand.Next(100);
            if (dist > 40f) return BotActionType.Patrol;
            if (dist < 10f && roll < 10) return BotActionType.Retreat;
            if (dist < 25f && roll < 20) return BotActionType.Circle;
            if (roll < 10) return BotActionType.Flank;
            if (roll < 20) return BotActionType.Hide;
            if (roll < 60) return BotActionType.Chase;
            return BotActionType.Idle;
        }

        private void DoBotAction(NPCPlayer bot, BasePlayer target, BotActionType action)
        {
            var navigator = bot.GetComponent<NPCPlayerNavigator>();
            if (navigator == null) return;
            float dist = Vector3.Distance(bot.transform.position, target.transform.position);
            switch (action)
            {
                case BotActionType.Chase:
                    if (dist > 5f)
                        navigator.SetDestination(target.transform.position, BaseNavigator.NavigationSpeed.Fast);
                    else
                        navigator.SetDestination(bot.transform.position, BaseNavigator.NavigationSpeed.Normal);
                    bot.SetAimDirection((target.transform.position - bot.transform.position).normalized);
                    break;
                case BotActionType.Circle:
                    Vector3 dir = (bot.transform.position - target.transform.position).normalized;
                    Vector3 perp = Vector3.Cross(dir, Vector3.up).normalized;
                    float sign = sysRand.Next(2) == 0 ? 1f : -1f;
                    Vector3 circlePos = target.transform.position + dir * 15f + perp * sign * 10f;
                    navigator.SetDestination(circlePos, BaseNavigator.NavigationSpeed.Normal);
                    bot.SetAimDirection((target.transform.position - bot.transform.position).normalized);
                    break;
                case BotActionType.Flank:
                    Vector3 flankDir = Quaternion.Euler(0, sysRand.Next(-90, 90), 0) * (target.transform.position - bot.transform.position).normalized;
                    Vector3 flankPos = target.transform.position + flankDir * sysRand.Next(10, 25);
                    navigator.SetDestination(flankPos, BaseNavigator.NavigationSpeed.Fast);
                    bot.SetAimDirection((target.transform.position - bot.transform.position).normalized);
                    break;
                case BotActionType.Retreat:
                    Vector3 away = (bot.transform.position - target.transform.position).normalized;
                    Vector3 retreatPos = bot.transform.position + away * sysRand.Next(10, 20);
                    navigator.SetDestination(retreatPos, BaseNavigator.NavigationSpeed.Fast);
                    break;
                case BotActionType.Patrol:
                    Vector2 offset = UnityEngine.Random.insideUnitCircle.normalized * sysRand.Next(20, 40);
                    Vector3 patrolPos = target.transform.position + new Vector3(offset.x, 0, offset.y);
                    patrolPos.y = TerrainMeta.HeightMap.GetHeight(patrolPos);
                    navigator.SetDestination(patrolPos, BaseNavigator.NavigationSpeed.Normal);
                    break;
                case BotActionType.Idle:
                    navigator.SetDestination(bot.transform.position, BaseNavigator.NavigationSpeed.Slow);
                    break;
                case BotActionType.Hide:
                    Vector3 hideDir = UnityEngine.Random.onUnitSphere; hideDir.y = 0; hideDir.Normalize();
                    Vector3 hidePos = bot.transform.position + hideDir * sysRand.Next(10, 20);
                    navigator.SetDestination(hidePos, BaseNavigator.NavigationSpeed.Fast);
                    break;
            }
        }

        private Item FindItemInInventory(PlayerInventory inv, int itemid)
        {
            foreach (var container in new[] { inv.containerBelt, inv.containerMain, inv.containerWear })
            {
                foreach (var item in container.itemList)
                {
                    if (item.info.itemid == itemid)
                        return item;
                }
            }
            return null;
        }

        private void HealBotWithItem(NPCPlayer npc, Item item)
        {
            if (item.info.shortname == "syringe.medical")
            {
                npc.health = Mathf.Min(npc.health + 30f, npc.MaxHealth());
                item.Remove(1);
            }
            else if (item.info.shortname == "bandage")
            {
                npc.health = Mathf.Min(npc.health + 10f, npc.MaxHealth());
                item.Remove(1);
            }
        }

        object OnEntityTakeDamage(NPCPlayer npc, HitInfo info)
        {
            if (npc == null || !spawnedBots.Contains(npc) || botEvading.TryGetValue(npc, out var evading) && evading)
                return null;
            var attacker = info?.Initiator as BasePlayer;
            if (attacker == null) return null;
            // Лечение при ранении
            if (npc.health < npc.MaxHealth())
            {
                int syringeId = ItemManager.FindItemDefinition("syringe.medical").itemid;
                int bandageId = ItemManager.FindItemDefinition("bandage").itemid;
                var syringe = FindItemInInventory(npc.inventory, syringeId);
                if (syringe != null)
                {
                    HealBotWithItem(npc, syringe);
                }
                else
                {
                    var bandage = FindItemInInventory(npc.inventory, bandageId);
                    if (bandage != null)
                        HealBotWithItem(npc, bandage);
                }
            }
            botEvading[npc] = true;
            EvadeSequence(npc, 0);
            return null;
        }

        private void EvadeSequence(NPCPlayer npc, int step)
        {
            if (npc == null || npc.IsDestroyed || step >= 25) {
                botEvading[npc] = false;
                botReturnTime[npc] = Time.realtimeSinceStartup + 60f;
                lastEvadePos = null;
                return;
            }
            // Случайное направление, избегаем слишком близких точек
            Vector3 randomDir;
            Vector3 evadePos;
            int attempts = 0;
            do {
                randomDir = UnityEngine.Random.onUnitSphere; randomDir.y = 0; randomDir.Normalize();
                evadePos = npc.transform.position + randomDir * 20f;
                attempts++;
            } while (lastEvadePos.HasValue && Vector3.Distance(evadePos, lastEvadePos.Value) < 5f && attempts < 10);
            lastEvadePos = evadePos;
            RaycastHit hit;
            if (Physics.Raycast(npc.transform.position, randomDir, out hit, 20f))
                evadePos = hit.point - randomDir * 2f;
            var navigator = npc.GetComponent<NPCPlayerNavigator>();
            if (navigator != null)
                navigator.SetDestination(evadePos, BaseNavigator.NavigationSpeed.Fast);
            timer.Once(2f, () => EvadeSequence(npc, step + 1));
        }
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху