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

FREE RadtownAnimals : 0.3.2 0.3.2

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
namespace Oxide.Plugins {
    [Info("Radtown Animals", "k1lly0u", "0.3.2"), Description("Spawns various NPC types at monuments")]
    public class RadtownAnimals : RustPlugin {
#region Fields
        private readonly List<BaseCombatEntity> pluginSpawnedEntities = new List<BaseCombatEntity>();
        private readonly Hash<NPC, string> prefabLookup = new Hash<NPC, string> {
            [NPC.Bear]
            = "assets/rust.ai/agents/bear/bear.prefab", [NPC.Boar] = "assets/rust.ai/agents/boar/boar.prefab", [NPC.Chicken] = "assets/rust.ai/agents/chicken/chicken.prefab", [NPC.Stag] = "assets/rust.ai/agents/stag/stag.prefab", [NPC.Wolf] = "assets/rust.ai/agents/wolf/wolf.prefab", [NPC.Scarecrow] = "assets/prefabs/npc/scarecrow/scarecrow.prefab", [NPC.Scientist] = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam.prefab",
        }
        ;
        [JsonConverter(typeof(StringEnumConverter))]
        private enum NPC {
            Bear, Boar, Chicken, Stag, Wolf, Scarecrow, Scientist
        }
#endregion
#region Oxide Hooks
        protected override void LoadDefaultMessages() => lang.RegisterMessages(Messages, this);
        private void OnServerInitialized() => InitializeSpawns();
        private void OnEntityDeath(BaseCombatEntity baseCombatEntity, HitInfo hitInfo) {
            if (baseCombatEntity == null) return;
            if (pluginSpawnedEntities.Contains(baseCombatEntity)) {
                string prefabName = baseCombatEntity.PrefabName;
                Vector3 homePosition = GetHomePosition(baseCombatEntity);
                timer.In(Configuration.Settings.Respawn, () => Spawn(prefabName, homePosition));
                pluginSpawnedEntities.Remove(baseCombatEntity);
            }
        }
        private void Unload() {
            foreach (BaseCombatEntity baseCombatEntity in pluginSpawnedEntities) {
                if (baseCombatEntity != null && !baseCombatEntity.IsDead()) baseCombatEntity.Kill(BaseNetworkable.DestroyMode.None);
            }
            pluginSpawnedEntities.Clear();
            Configuration = null;
        }
#endregion
#region Initial Spawning
        private void InitializeSpawns() {
            Hash<string, ConfigData.Monuments.MonumentSettings> monumentLookup = new Hash<string, ConfigData.Monuments.MonumentSettings> {
                ["lighthouse"]
                = Configuration.MonumentSettings.Lighthouse, ["powerplant_1"] = Configuration.MonumentSettings.Powerplant, ["military_tunnel_1"] = Configuration.MonumentSettings.Tunnels, ["harbor_1"] = Configuration.MonumentSettings.LargeHarbor, ["harbor_2"] = Configuration.MonumentSettings.SmallHarbor, ["airfield_1"] = Configuration.MonumentSettings.Airfield, ["trainyard_1"] = Configuration.MonumentSettings.Trainyard, ["water_treatment_plant_1"] = Configuration.MonumentSettings.WaterTreatment, ["warehouse"] = Configuration.MonumentSettings.Warehouse, ["satellite_dish"] = Configuration.MonumentSettings.Satellite, ["sphere_tank"] = Configuration.MonumentSettings.Dome, ["radtown_small_3"] = Configuration.MonumentSettings.Radtown, ["launch_site_1"] = Configuration.MonumentSettings.RocketFactory, ["gas_station_1"] = Configuration.MonumentSettings.GasStation, ["supermarket_1"] = Configuration.MonumentSettings.Supermarket, ["mining_quarry_c"] = Configuration.MonumentSettings.Quarry_HQM, ["mining_quarry_a"] = Configuration.MonumentSettings.Quarry_Sulfur, ["mining_quarry_b"] = Configuration.MonumentSettings.Quarry_Stone, ["junkyard_1"] = Configuration.MonumentSettings.Junkyard
            }
            ;
            Transform root = HierarchyUtil.GetRoot("Monument").transform;
            for (int i = 0; i < root.childCount; i++) {
                Transform child = root.GetChild(i);
                foreach (KeyValuePair<string, ConfigData.Monuments.MonumentSettings> kvp in monumentLookup) {
                    if (child.name.Contains(kvp.Key)) {
                        if (kvp.Value.Enabled) ServerMgr.Instance.StartCoroutine(SpawnAnimals(child.position, kvp.Value.Counts));
                        break;
                    }
                }
            }
        }
        private IEnumerator SpawnAnimals(Vector3 position, Hash<NPC, int> spawnCounts) {
            foreach (KeyValuePair<NPC, int> kvp in spawnCounts) {
                for (int i = 0; i < kvp.Value; i++) {
                    if (pluginSpawnedEntities.Count >= Configuration.Settings.Total) {
                        Puts(lang.GetMessage("Notification.SpawnLimit", this));
                        yield break;
                    }
                    Spawn(kvp.Key, position);
                    yield return CoroutineEx.waitForSeconds(0.5f);
                }
            }
        }
#endregion
#region Spawning
        private void Spawn(NPC npc, Vector3 position) => Spawn(prefabLookup[npc], position);
        private void Spawn(string prefab, Vector3 position) {
            Vector2 random = Random.insideUnitCircle * Configuration.Settings.Spread;
            Vector3 spawnPosition = new Vector3(position.x + random.x, position.y, position.z + random.y);
            if (!NavmeshSpawnPoint.Find(spawnPosition, 20f, out spawnPosition)) {
                timer.In(Configuration.Settings.Respawn, () => Spawn(prefab, position));
                return;
            }
            BaseCombatEntity baseCombatEntity = InstantiateEntity(prefab, spawnPosition);
            baseCombatEntity.enableSaving = false;
            NavMeshAgent navMeshAgent = baseCombatEntity.GetComponent<NavMeshAgent>();
            if (navMeshAgent != null) {
                const int AREA_MASK = 1;
                const int AGENT_TYPE_ID = -1372625422;
                navMeshAgent.agentTypeID = AGENT_TYPE_ID;
                navMeshAgent.areaMask = AREA_MASK;
            }
            BaseNavigator baseNavigator = baseCombatEntity.GetComponent<BaseNavigator>();
            if (baseNavigator != null) {
                const string WALKABLE = "Walkable";
                baseNavigator.DefaultArea = WALKABLE;
                baseNavigator.MaxRoamDistanceFromHome = Configuration.Settings.Spread;
                baseNavigator.BestRoamPointMaxDistance = Configuration.Settings.Spread * 0.5f;
                if (baseNavigator.topologyPreference == 0) baseNavigator.topologyPreference = (TerrainTopology.Enum)1673010749;
                baseNavigator.topologyPreference |= TerrainTopology.Enum.Monument | TerrainTopology.Enum.Building;
            }
            baseCombatEntity.Spawn();
            timer.In(1f, () => SetupBrain(baseCombatEntity, position));
            pluginSpawnedEntities.Add(baseCombatEntity);
        }
        private BaseCombatEntity InstantiateEntity(string type, Vector3 position) {
            GameObject gameObject = Instantiate.GameObject(GameManager.server.FindPrefab(type), position, Quaternion.identity);
            gameObject.name = type;
            SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
            UnityEngine.Object.Destroy(gameObject.GetComponent<Spawnable>());
            if (!gameObject.activeSelf) gameObject.SetActive(true);
            BaseCombatEntity component = gameObject.GetComponent<BaseCombatEntity>();
            return component;
        }
#endregion
#region Brain Setup
        private void SetupBrain(BaseCombatEntity baseCombatEntity, Vector3 position) {
            if (baseCombatEntity == null) return;
            if (baseCombatEntity is global : : HumanNPC) SetupBrain<global : : HumanNPC>(baseCombatEntity, position);
            if (baseCombatEntity is BaseAnimalNPC) SetupBrain<BaseAnimalNPC>(baseCombatEntity, position);
            if (baseCombatEntity is ScarecrowNPC) SetupBrain<ScarecrowNPC>(baseCombatEntity, position);
        }
        private void SetupBrain<T>(BaseCombatEntity baseCombatEntity, Vector3 position) where T : BaseEntity {
            BaseAIBrain baseAIBrain = baseCombatEntity.GetComponent<BaseAIBrain>();
            if (baseAIBrain != null) {
                baseAIBrain.Events.Memory.Position.Set(position, 4);
                GenericRoamState<T> genericRoamState = new GenericRoamState<T>();
                genericRoamState.brain = baseAIBrain;
                baseAIBrain.states[AIState.Roam] = genericRoamState;
            }
        }
        private Vector3 GetHomePosition(BaseCombatEntity baseCombatEntity) {
            if (baseCombatEntity != null) {
                if (baseCombatEntity is global : : HumanNPC) return GetHomePosition<global : : HumanNPC>(baseCombatEntity);
                if (baseCombatEntity is BaseAnimalNPC) return GetHomePosition<BaseAnimalNPC>(baseCombatEntity);
                if (baseCombatEntity is ScarecrowNPC) return GetHomePosition<ScarecrowNPC>(baseCombatEntity);
            }
            return Vector3.zero;
        }
        private Vector3 GetHomePosition<T>(BaseCombatEntity baseCombatEntity) where T : BaseEntity => baseCombatEntity.GetComponent<BaseAIBrain>().Events.Memory.Position.Get(4);
#endregion
#region NavMesh
        private static class NavmeshSpawnPoint {
            private static NavMeshHit navmeshHit;
            private static RaycastHit raycastHit;
            private static readonly Collider[] _buffer = new Collider[256];
            private const int WORLD_LAYER = 65536;
            public static bool Find(Vector3 targetPosition, float maxDistance, out Vector3 position) {
                for (int i = 0; i < 10; i++) {
                    position = i == 0 ? targetPosition : targetPosition + (Random.onUnitSphere * maxDistance);
                    if (NavMesh.SamplePosition(position, out navmeshHit, maxDistance, 1)) {
                        if (IsInRockPrefab(navmeshHit.position)) continue;
                        if (IsNearWorldCollider(navmeshHit.position)) continue;
                        if (navmeshHit.position.y < TerrainMeta.WaterMap.GetHeight(navmeshHit.position)) continue;
                        position = navmeshHit.position;
                        return true;
                    }
                }
                position = default(Vector3);
                return false;
            }
            private static bool IsInRockPrefab(Vector3 position) {
                Physics.queriesHitBackfaces = true;
                bool isInRock = Physics.Raycast(position, Vector3.up, out raycastHit, 20f, WORLD_LAYER, QueryTriggerInteraction.Ignore) && BLOCKED_COLLIDERS.Any(s => raycastHit.collider?.gameObject?.name.Contains(s, CompareOptions.OrdinalIgnoreCase) ?? false);
                Physics.queriesHitBackfaces = false;
                return isInRock;
            }
            private static bool IsNearWorldCollider(Vector3 position) {
                Physics.queriesHitBackfaces = true;
                int count = Physics.OverlapSphereNonAlloc(position, 2f, _buffer, WORLD_LAYER, QueryTriggerInteraction.Ignore);
                Physics.queriesHitBackfaces = false;
                int removed = 0;
                for (int i = 0; i < count; i++) {
                    if (ACCEPTED_COLLIDERS.Any(s => _buffer[i].gameObject.name.Contains(s, CompareOptions.OrdinalIgnoreCase) || _buffer[i].gameObject.layer == 4)) removed++;
                }
                return count - removed > 0;
            }
            private static readonly string[] ACCEPTED_COLLIDERS = new string[] {
                "road", "carpark", "rocket_factory", "range", "train_track", "runway", "_grounds", "concrete_slabs", "lighthouse", "cave", "office", "walkways", "sphere", "tunnel", "industrial", "junkyard"
            }
            ;
            private static readonly string[] BLOCKED_COLLIDERS = new string[] {
                "rock", "cliff", "junk", "range", "invisible"
            }
            ;
        }
#endregion
#region Roam State
        public class GenericRoamState<T> : BaseAIBrain.BasicAIState {
            private StateStatus status = StateStatus.Error;
            private static readonly Vector3[] preferedTopologySamples = new Vector3[4];
            private static readonly Vector3[] topologySamples = new Vector3[4];
            public GenericRoamState() : base(AIState.Roam) {
            }
            public override void StateLeave(BaseAIBrain brain, BaseEntity entity) {
                base.StateLeave(brain, entity);
                brain.Navigator.Stop();
            }
            public override void StateEnter(BaseAIBrain brain, BaseEntity entity) {
                base.StateEnter(brain, entity);
                status = StateStatus.Error;
                if (brain.PathFinder == null) return;
                Vector3 destination = GetBestRoamPosition(brain.Navigator, brain.Events.Memory.Position.Get(4), brain.Events.Memory.Position.Get(4), 1f, Configuration.Settings.Spread);
                if (brain.Navigator.SetDestination(destination, BaseNavigator.NavigationSpeed.Slow, 0f, 0f)) {
                    status = StateStatus.Running;
                    return;
                }
                status = StateStatus.Error;
            }
            public override StateStatus StateThink(float delta, BaseAIBrain brain, BaseEntity entity) {
                base.StateThink(delta, brain, entity);
                if (status == StateStatus.Error) return status;
                if (brain.Navigator.Moving) return StateStatus.Running;
                return StateStatus.Finished;
            }
            private Vector3 GetBestRoamPosition(BaseNavigator navigator, Vector3 localTo, Vector3 fallback, float minRange, float maxRange) {
                int topologyIndex = 0;
                int preferredTopologyIndex = 0;
                for (float degree = 0f; degree < 360f; degree += 90f) {
                    Vector3 position;
                    Vector3 pointOnCircle = BasePathFinder.GetPointOnCircle(localTo, Random.Range(minRange, maxRange), degree + Random.Range(0f, 90f));
                    if (navigator.GetNearestNavmeshPosition(pointOnCircle, out position, 20f) && navigator.IsAcceptableWaterDepth(position)) {
                        topologySamples[topologyIndex] = position;
                        topologyIndex++;
                        if (navigator.IsPositionATopologyPreference(position)) {
                            preferedTopologySamples[preferredTopologyIndex] = position;
                            preferredTopologyIndex++;
                        }
                    }
                }
                Vector3 chosenPosition;
                if (Random.Range(0f, 1f) <= 0.9f && preferredTopologyIndex > 0) chosenPosition = preferedTopologySamples[Random.Range(0, preferredTopologyIndex)];
                else if (topologyIndex > 0) chosenPosition = topologySamples[Random.Range(0, topologyIndex)];
                else chosenPosition = fallback;
                return chosenPosition;
            }
        }
#endregion
#region Commands
        [ChatCommand("ra_killall")]
        private void ChatCommand_KillAnimals(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            foreach(BaseCombatEntity baseCombatEntity in pluginSpawnedEntities) {
                if (baseCombatEntity != null && !baseCombatEntity.IsDestroyed) baseCombatEntity.Kill(BaseNetworkable.DestroyMode.None);
            }
            pluginSpawnedEntities.Clear();
            SendReply(player, lang.GetMessage("Message.Title", this, player.UserIDString) + lang.GetMessage("Notification.KilledAll", this, player.UserIDString));
        }
        [ConsoleCommand("ra_killall")]
        private void ConsoleCommand_KillAnimals(ConsoleSystem.Arg arg) {
            if (arg.Connection == null) {
                foreach (BaseCombatEntity baseCombatEntity in pluginSpawnedEntities) {
                    if (baseCombatEntity != null && !baseCombatEntity.IsDestroyed) baseCombatEntity.Kill(BaseNetworkable.DestroyMode.None);
                }
                pluginSpawnedEntities.Clear();
                SendReply(arg, lang.GetMessage("Notification.KilledAll", this));
            }
        }
#endregion
#region Config
        private static ConfigData Configuration;
        private class ConfigData {
            public Options Settings {
                get;
                set;
            }
            [JsonProperty(PropertyName = "Monument Settings")]
            public Monuments MonumentSettings {
                get;
                set;
            }
            public class Options {
                [JsonProperty(PropertyName = "Respawn timer (seconds)")]
                public int Respawn {
                    get;
                    set;
                }
                [JsonProperty(PropertyName = "Spawn spread distance from center of monument")]
                public float Spread {
                    get;
                    set;
                }
                [JsonProperty(PropertyName = "Maximum amount of animals to spawn")]
                public int Total {
                    get;
                    set;
                }
            }
            public class Monuments {
                public MonumentSettings Airfield {
                    get;
                    set;
                }
                public MonumentSettings Dome {
                    get;
                    set;
                }
                public MonumentSettings Junkyard {
                    get;
                    set;
                }
                public MonumentSettings Lighthouse {
                    get;
                    set;
                }
                public MonumentSettings LargeHarbor {
                    get;
                    set;
                }
                public MonumentSettings GasStation {
                    get;
                    set;
                }
                public MonumentSettings Powerplant {
                    get;
                    set;
                }
                [JsonProperty(PropertyName = "Stone Quarry")]
                public MonumentSettings Quarry_Stone {
                    get;
                    set;
                }
                [JsonProperty(PropertyName = "Sulfur Quarry")]
                public MonumentSettings Quarry_Sulfur {
                    get;
                    set;
                }
                [JsonProperty(PropertyName = "HQM Quarry")]
                public MonumentSettings Quarry_HQM {
                    get;
                    set;
                }
                public MonumentSettings Radtown {
                    get;
                    set;
                }
                public MonumentSettings RocketFactory {
                    get;
                    set;
                }
                public MonumentSettings Satellite {
                    get;
                    set;
                }
                public MonumentSettings SmallHarbor {
                    get;
                    set;
                }
                public MonumentSettings Supermarket {
                    get;
                    set;
                }
                public MonumentSettings Trainyard {
                    get;
                    set;
                }
                public MonumentSettings Tunnels {
                    get;
                    set;
                }
                public MonumentSettings Warehouse {
                    get;
                    set;
                }
                public MonumentSettings WaterTreatment {
                    get;
                    set;
                }
                public class MonumentSettings {
                    [JsonProperty(PropertyName = "Enable spawning at this monument")]
                    public bool Enabled {
                        get;
                        set;
                    }
                    [JsonProperty(PropertyName = "Amount of animals to spawn at this monument")]
                    public Hash<NPC, int> Counts {
                        get;
                        set;
                    }
                    = new Hash<NPC, int> {
                        [NPC.Bear]
                        = 0, [NPC.Boar] = 0, [NPC.Chicken] = 0, [NPC.Stag] = 0, [NPC.Wolf] = 0, [NPC.Scarecrow] = 0, [NPC.Scientist] = 0,
                    }
                    ;
                }
            }
            public Oxide.Core.VersionNumber Version {
                get;
                set;
            }
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            Configuration = Config.ReadObject<ConfigData>();
            if (Configuration.Version < Version) UpdateConfigValues();
            Config.WriteObject(Configuration, true);
        }
        protected override void LoadDefaultConfig() => Configuration = GetBaseConfig();
        private ConfigData GetBaseConfig() {
            return new ConfigData {
                Settings = new ConfigData.Options {
                    Respawn = 900, Spread = 60, Total = 40
                }
                , MonumentSettings = new ConfigData.Monuments {
                    Airfield = new ConfigData.Monuments.MonumentSettings(), Dome = new ConfigData.Monuments.MonumentSettings(), GasStation = new ConfigData.Monuments.MonumentSettings(), Junkyard = new ConfigData.Monuments.MonumentSettings(), LargeHarbor = new ConfigData.Monuments.MonumentSettings(), Lighthouse = new ConfigData.Monuments.MonumentSettings(), Powerplant = new ConfigData.Monuments.MonumentSettings(), Quarry_HQM = new ConfigData.Monuments.MonumentSettings(), Quarry_Stone = new ConfigData.Monuments.MonumentSettings(), Quarry_Sulfur = new ConfigData.Monuments.MonumentSettings(), Radtown = new ConfigData.Monuments.MonumentSettings(), RocketFactory = new ConfigData.Monuments.MonumentSettings(), Satellite = new ConfigData.Monuments.MonumentSettings(), SmallHarbor = new ConfigData.Monuments.MonumentSettings(), Supermarket = new ConfigData.Monuments.MonumentSettings(), Trainyard = new ConfigData.Monuments.MonumentSettings(), Tunnels = new ConfigData.Monuments.MonumentSettings(), Warehouse = new ConfigData.Monuments.MonumentSettings(), WaterTreatment = new ConfigData.Monuments.MonumentSettings()
                }
                , Version = Version
            }
            ;
        }
        protected override void SaveConfig() => Config.WriteObject(Configuration, true);
        private void UpdateConfigValues() {
            PrintWarning("Config update detected! Updating config values...");
            ConfigData baseConfig = GetBaseConfig();
            if (Configuration.Version < new Core.VersionNumber(0, 3, 0)) Configuration = baseConfig;
            Configuration.Version = Version;
            PrintWarning("Config update completed!");
        }
#endregion
#region Messages
        private readonly Dictionary<string, string> Messages = new Dictionary<string, string>() {
            ["Message.Title"]
            = "<color=orange>Radtown Animals:</color> ", ["Notification.KilledAll"] = "<color=#939393>Killed all animals</color>", ["Notification.SpawnLimit"] = "The animal spawn limit has been hit."
        }
        ;
#endregion
    }
}

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