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

FREE AnimalSpawn : 1.0.0 1.0.0

Нет прав для скачивания
C#:
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using Oxide.Core.Libraries;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
using Facepunch;
using Newtonsoft.Json;
namespace Oxide.Plugins {
    [Info("AnimalSpawn", "TopPlugin.ru", "1.0.0")]
    class AnimalSpawn : RustPlugin {
#region Variables
        private AnimalDataInfo AnimalData;
        private bool IsSpawning = false;
        private Dictionary<string, float> SaveOrigSpawn = new Dictionary<string, float>();
        private int GroundLayer = LayerMask.GetMask("Terrain", "World", "Construction", "Deployable", "Deployed", "Default", "Tree", "Resource", "Water");
        private class AnimalDataInfo {
            public int SquareSideSize;
            public Dictionary<int, SquareInfo> Squares = new Dictionary<int, SquareInfo>();
            public List<AnimalInfo> AnimalBody = new List<AnimalInfo>();
        }
        private class SquareInfo {
            public int minX;
            public int maxX;
            public int minY;
            public int maxY;
            public SquareInfo(int minX, int maxX, int minY, int maxY) {
                this.minX = minX;
                this.maxX = maxX;
                this.minY = minY;
                this.maxY = maxY;
            }
            public bool noNeedSpawn;
        }
        private class AnimalInfo {
            [JsonIgnore]
            public BaseEntity entity;
            public uint id;
            public string type;
            public int square;
        }
#endregion
#region Hooks
        private void Init() {
            LoadVariables();
            LoadData();
        }
        private void OnServerInitialized() {
            SaveOrigSpawn.Add("bear", Bear.Population);
            SaveOrigSpawn.Add("boar", Boar.Population);
            SaveOrigSpawn.Add("wolf", Wolf.Population);
            SaveOrigSpawn.Add("stag", Stag.Population);
            SaveOrigSpawn.Add("horse", Horse.Population);
            SaveOrigSpawn.Add("chicken", Chicken.Population);
            SaveOrigSpawn.Add("zombie", Zombie.Population);
            SaveOrigSpawn.Add("ridablehorse", RidableHorse.Population);
            Bear.Population = 0f;
            Boar.Population = 0f;
            Wolf.Population = 0f;
            Stag.Population = 0f;
            Horse.Population = 0f;
            Chicken.Population = 0f;
            Zombie.Population = 0f;
            RidableHorse.Population = 0f;
            var delAllAnimals = new HashSet<BaseEntity>(BaseNetworkable.serverEntities.OfType<BaseEntity>().Where(b => configData.AnimalsBySquare.ContainsKey(b.PrefabName)));
            foreach(var animal in delAllAnimals) {
                if (animal == null || animal.IsDestroyed) continue;
                animal.KillMessage();
            }
            if (AnimalData == null || AnimalData.SquareSideSize == 0) {
                if (AnimalData == null) AnimalData = new AnimalDataInfo();
                int count = 0;
                AnimalData.SquareSideSize = configData.SquareSideSize;
                for(int ii=0;ii<(int)Math.Round((decimal)World.Size/configData.SquareSideSize);
                ii++) for(int jj=0;jj<(int)Math.Round((decimal)World.Size/configData.SquareSideSize);
                jj++) {
                    SquareInfo si = new SquareInfo(configData.SquareSideSize*ii-(int)World.Size/2, configData.SquareSideSize*ii+(configData.SquareSideSize-1)-(int)World.Size/2, configData.SquareSideSize*jj-(int)World.Size/2, configData.SquareSideSize*jj+(configData.SquareSideSize-1)-(int)World.Size/2);
                    AnimalData.Squares.Add(count, si);
                    count++;
                }
                SaveData();
            }
            timer.Once(50f, CheckSpawn);
        }
        private void Unload() {
            IsSpawning = true;
            foreach (var animal in AnimalData.AnimalBody.ToList()) if (animal.entity != null) {
                UnityEngine.Object.Destroy(animal.entity.GetComponent<NPCController>());
                animal.entity.KillMessage();
            }
            AnimalData.AnimalBody.Clear();
            var objects = UnityEngine.Object.FindObjectsOfType<NPCController>();
            if (objects != null) foreach (var gameObj in objects) UnityEngine.Object.Destroy(gameObj);
            SaveData();
            Bear.Population = SaveOrigSpawn["bear"];
            Boar.Population = SaveOrigSpawn["boar"];
            Wolf.Population = SaveOrigSpawn["wolf"];
            Stag.Population = SaveOrigSpawn["stag"];
            Horse.Population = SaveOrigSpawn["horse"];
            Chicken.Population = SaveOrigSpawn["chicken"];
            Zombie.Population = SaveOrigSpawn["zombie"];
            RidableHorse.Population = SaveOrigSpawn["ridablehorse"];
        }
        private void OnNewSave() {
            AnimalData.SquareSideSize = 0;
            AnimalData.Squares.Clear();
            SaveData();
        }
#endregion
#region Main
        private void CheckSpawn() {
            if (!IsSpawning) {
                ClearDieAnimals();
                CommunityEntity.ServerInstance.StartCoroutine(SpawnAnimals());
            }
            timer.Once(configData.RespawnTime, CheckSpawn);
        }
        private void ClearDieAnimals() {
            foreach (var animal in AnimalData.AnimalBody.ToList()) if (animal.entity == null || animal.entity.IsDestroyed) AnimalData.AnimalBody.Remove(animal);
        }
        private IEnumerator SpawnAnimals() {
            IsSpawning = true;
            foreach(var square in AnimalData.Squares.ToDictionary(x=>x.Key, x=>x.Value)) {
                if (square.Value.noNeedSpawn) continue;
                foreach(var animalCfg in configData.AnimalsBySquare) {
                    var count = AnimalData.AnimalBody.Where(x=>x.square == square.Key && x.type == animalCfg.Key).Count();
                    for(int ii=count;ii<animalCfg.Value;ii++) {
                        BaseEntity entity = null;
                        var result = TrySpawnAnimal(animalCfg.Key, square.Value, ref entity);
                        AnimalData.Squares[square.Key].noNeedSpawn = !result;
                        if (entity != null) AnimalData.AnimalBody.Add(new AnimalInfo() {
                            entity = entity, id = entity.net.ID, type = animalCfg.Key, square = square.Key
                        }
                        );
                        yield return new WaitForSeconds(0.15f);
                    }
                }
                SaveData();
            }
            IsSpawning = false;
        }
        private bool TrySpawnAnimal(string type, SquareInfo square, ref BaseEntity entity) {
            Vector3 newPos = default(Vector3);
            for(int ii=0;ii<configData.MaxTryFindSpawn;ii++) {
                var x = UnityEngine.Random.Range(square.minX, square.maxX);
                var z = UnityEngine.Random.Range(square.minY, square.maxY);
                if (IsPlaceNorm(x, z, ref newPos)) {
                    var tmp = SpawnAnimal(type, newPos);
                    if (tmp != null) {
                        entity = tmp;
                        return true;
                    }
                }
            }
            return false;
        }
        private bool IsNobodyNear(Vector3 pos) {
            var distance = configData.MinDistanceSpawn * configData.MinDistanceSpawn;
            foreach(var player in BasePlayer.activePlayerList) if ((pos-player.transform.position).sqrMagnitude <= distance) return false;
            return true;
        }
        private bool IsFlatPlace(Vector3 pos) {
            RaycastHit hitInfo;
            var posTmp = pos + new Vector3(-1f, 500f, 0f);
            if (Physics.Raycast(posTmp, Vector3.down, out hitInfo, 1000f, GroundLayer)) {
                if (Math.Abs(pos.y - hitInfo.point.y) >= configData.SpawnDelta) return false;
            }
            else return false;
            posTmp = pos + new Vector3(0f, 500f, -1f);
            if (Physics.Raycast(posTmp, Vector3.down, out hitInfo, 1000f, GroundLayer)) {
                if (Math.Abs(pos.y - hitInfo.point.y) >= configData.SpawnDelta) return false;
            }
            else return false;
            posTmp = pos + new Vector3(1f, 500f, 0f);
            if (Physics.Raycast(posTmp, Vector3.down, out hitInfo, 1000f, GroundLayer)) {
                if (Math.Abs(pos.y - hitInfo.point.y) >= configData.SpawnDelta) return false;
            }
            else return false;
            posTmp = pos + new Vector3(0f, 500f, 1f);
            if (Physics.Raycast(posTmp, Vector3.down, out hitInfo, 1000f, GroundLayer)) {
                if (Math.Abs(pos.y - hitInfo.point.y) >= configData.SpawnDelta) return false;
            }
            else return false;
            return true;
        }
        private bool IsPlaceNorm(int x, int z, ref Vector3 newPos) {
            Vector3 pos = new Vector3(x, 500f, z);
            RaycastHit hitInfo;
            var result = Physics.SphereCast(pos, 2f, Vector3.down, out hitInfo, 1000, GroundLayer);
            if (!result) return false;
            if (hitInfo.collider.name != "Terrain") return false;
            if (hitInfo.point.y < 0) return false;
            newPos = new Vector3(x, hitInfo.point.y, z);
            if (!IsNobodyNear(newPos)) return false;
            if (!IsFlatPlace(newPos)) return false;
            return true;
        }
        private BaseEntity SpawnAnimal(string type, Vector3 newPos) {
            if (newPos == default(Vector3)) return null;
            BaseEntity entity = InstantiateEntity(type, newPos);
            entity.Spawn();
            var npc = entity.gameObject.AddComponent<NPCController>();
            npc.SetHome(newPos);
            return entity;
        }
        private BaseEntity InstantiateEntity(string type, Vector3 position) {
            var gameObject = Instantiate.GameObject(GameManager.server.FindPrefab(type), position, Quaternion.Euler(0, UnityEngine.Random.Range(0.0f, 360.0f), 0));
            gameObject.name = type;
            SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
            UnityEngine.Object.Destroy(gameObject.GetComponent<Spawnable>());
            if (!gameObject.activeSelf) gameObject.SetActive(true);
            BaseEntity component = gameObject.GetComponent<BaseEntity>();
            return component;
        }
        private class NPCController : MonoBehaviour {
            public BaseNpc npc;
            public BaseVehicle vcl;
            private Vector3 homePos;
            private void Awake() {
                npc = GetComponent<BaseNpc>();
                vcl = GetComponent<BaseVehicle>();
                enabled = false;
            }
            private void OnDestroy() => InvokeHandler.CancelInvoke(this, CheckLocation);
            public void SetHome(Vector3 homePos) {
                if (npc == null) return;
                this.homePos = homePos;
                InvokeHandler.InvokeRepeating(this, CheckLocation, 1f, 20f);
            }
            private void CheckLocation() {
                if (npc == null) {
                    Destroy(this);
                    return;
                }
                if (npc.NavAgent == null) {
                    npc.KillMessage();
                    Destroy(this);
                    return;
                }
                if (Vector3.Distance(npc.transform.position, homePos) > 100) npc.UpdateDestination(homePos);
            }
        }
        private void Output(BasePlayer player, string output, string text) {
            if (output == "chat") SendReply(player, text);
            else if (output == "console") PrintToConsole(player, text);
            else Puts(text);
        }
#endregion
#region Command
        [Oxide.Plugins.ChatCommand("animal.tp")]
        private void TpCommand(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            var list = UnityEngine.Object.FindObjectsOfType<BaseEntity>().Where(x=> x.ShortPrefabName == args[0]).ToList();
            if (list == null || list.Count==0) {
                SendReply(player, "Указанные животные не найдены !");
                return;
            }
            player.Teleport(list.GetRandom().transform.position);
        }
        [ConsoleCommand("animal.count")]
        private void CountCommand(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player != null && !player.IsAdmin) return;
            Dictionary<string, int> diff = new Dictionary<string, int>();
            var npcs = new HashSet<BaseEntity>(BaseNetworkable.serverEntities.OfType<BaseEntity>().Where(b => configData.AnimalsBySquare.ContainsKey(b.PrefabName)));
            foreach(var npc in npcs) {
                if (!diff.ContainsKey(npc.ShortPrefabName)) diff.Add(npc.ShortPrefabName, 1);
                else diff[npc.ShortPrefabName]++;
            }
            if (diff.Count == 0) Output(player, player != null ? "console" : "", "Животные не найдены !");
            else foreach(var npc in diff) Output(player, player != null ? "console" : "", string.Format("Тип NPC '{0}' - {1} шт.", npc.Key, npc.Value));
        }
#endregion
#region Config
        private static ConfigData configData;
        private class ConfigData {
            [JsonProperty(PropertyName = "Ширина стороны одного квадрата сетки спавна животных (метры)")]
            public int SquareSideSize;
            [JsonProperty(PropertyName = "Частота возрождения животных (секунды)")]
            public int RespawnTime;
            [JsonProperty(PropertyName = "Минимальное расстояние от игрока до места где может заспавнится животное (метры)")]
            public int MinDistanceSpawn;
            [JsonProperty(PropertyName = "Максимальное количество попыток найти подходящий спавн в заданном квадрате (50-500)")]
            public int MaxTryFindSpawn;
            [JsonProperty(PropertyName = "Допустимая величина отклонения неровности поверхности спавна")]
            public float SpawnDelta;
            [JsonProperty(PropertyName = "Количество животных на один квадрат сетки (целое число)")]
            public Dictionary<string, int> AnimalsBySquare;
        }
        private void LoadVariables() => configData = Config.ReadObject<ConfigData>();
        protected override void LoadDefaultConfig() {
            configData = new ConfigData {
                SquareSideSize = 1000, RespawnTime = 120, MinDistanceSpawn = 30, MaxTryFindSpawn = 100, SpawnDelta = 0.25f, AnimalsBySquare = new Dictionary<string, int>() {
                    {
                        "assets/rust.ai/agents/zombie/zombie.prefab", 0
                    }
                    , {
                        "assets/rust.ai/agents/bear/bear.prefab", 3
                    }
                    , {
                        "assets/rust.ai/agents/boar/boar.prefab", 3
                    }
                    , {
                        "assets/rust.ai/agents/chicken/chicken.prefab", 4
                    }
                    , {
                        "assets/rust.ai/agents/horse/horse.prefab", 2
                    }
                    , {
                        "assets/rust.ai/agents/stag/stag.prefab", 3
                    }
                    , {
                        "assets/rust.ai/agents/wolf/wolf.prefab", 2
                    }
                    , {
                        "assets/rust.ai/nextai/testridablehorse.prefab", 2
                    }
                    ,
                }
            }
            ;
            SaveConfig(configData);
            timer.Once(0.1f, ()=> SaveConfig(configData));
        }
        private void SaveConfig(ConfigData config) => Config.WriteObject(config, true);
#endregion
#region Data
        private void LoadData() {
            AnimalData = Interface.GetMod().DataFileSystem.ReadObject<AnimalDataInfo>("AnimalSpawnData");
            if (AnimalData == null) AnimalData = new AnimalDataInfo();
            if (AnimalData.Squares == null) AnimalData.Squares = new Dictionary<int, SquareInfo>();
            if (AnimalData.AnimalBody == null) AnimalData.AnimalBody = new List<AnimalInfo>();
        }
        private void SaveData() => Interface.GetMod().DataFileSystem.WriteObject("AnimalSpawnData", AnimalData);
#endregion
    }
}

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