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

FREE CustomNpcSpawn : 1.0.0 1.0.0

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

namespace Oxide.Plugins
{
    [Info("Custom Npc Spawn", "Tigernaut", "1.0.0")]
    public class CustomNpcSpawn : RustPlugin
    {
        [PluginReference] private Plugin NpcSpawn;

        private const string PermissionAdmin = "customnpcspawn.admin";

        #region Configuration

        private Configuration _config;

        private class Configuration
        {
            [JsonProperty("Button Prefabs List", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<string> InteractablePrefabs = new List<string>
            {
                "assets/prefabs/deployable/playerioents/button/button.prefab",
                "assets/prefabs/io/electric/switches/pressbutton/pressbutton.prefab"
            };

            [JsonProperty("Default Animal Health")]
            public float DefaultHealth = 100f;

            [JsonProperty("Default Button Cooldown (Seconds)")]
            public float DefaultCooldown = 60f;
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                _config = Config.ReadObject<Configuration>();
                if (_config == null) LoadDefaultConfig();
            }
            catch
            {
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void LoadDefaultConfig() => _config = new Configuration();
        protected override void SaveConfig() => Config.WriteObject(_config);

        #endregion

        #region Data Structures

        private readonly Dictionary<string, PresetDataNpc> _presetsNpc = new Dictionary<string, PresetDataNpc>();
        private readonly Dictionary<ulong, SpawnContextNpc> _activeNpcs = new Dictionary<ulong, SpawnContextNpc>();
        private readonly List<Timer> _respawnTimers = new List<Timer>();

        public class PresetDataNpc
        {
            public float RespawnTimeSeconds = 60f;
            public string NpcSpawnPreset = "default";
            public List<Vector3> SpawnPoints = new List<Vector3>();
        }

        public class SpawnContextNpc
        {
            public string PresetName;
            public Vector3 Position;
        }

        private readonly Dictionary<ulong, float> _animalCooldowns = new Dictionary<ulong, float>();
        
        private readonly Dictionary<string, string> _validAnimals = new Dictionary<string, string>
        {
            ["wolf"] = "assets/rust.ai/agents/wolf/wolf.prefab",
            ["wolf2"] = "assets/rust.ai/agents/wolf/wolf2.prefab",
            ["stag"] = "assets/rust.ai/agents/stag/stag.prefab",
            ["bear"] = "assets/rust.ai/agents/bear/bear.prefab",
            ["boar"] = "assets/rust.ai/agents/boar/boar.prefab",
            ["chicken"] = "assets/rust.ai/agents/chicken/chicken.prefab",
            ["polarbear"] = "assets/rust.ai/agents/bear/polarbear.prefab",
            ["tiger"] = "assets/rust.ai/agents/tiger/tiger.prefab",
            ["crocodile"] = "assets/rust.ai/agents/crocodile/crocodile.prefab",
            ["panther"] = "assets/rust.ai/agents/panther/panther.prefab",
            ["snake"] = "assets/rust.ai/agents/snake/snake.entity.prefab"
        };

        public class SpawnPointAnimal
        {
            public float X, Y, Z;
            public string AnimalType;
            public float Health;

            [JsonIgnore]
            public Vector3 Position => new Vector3(X, Y, Z);
        }

        public class ButtonPresetAnimal
        {
            public string Name;
            public ulong ButtonId;
            public float Cooldown;
            public List<SpawnPointAnimal> Spawns = new List<SpawnPointAnimal>();
        }

        private struct PendingPrefabData
        {
            public string PrefabName;
            public float ExpiryTime;
        }

        private readonly Dictionary<ulong, PendingPrefabData> _pendingPrefabs = new Dictionary<ulong, PendingPrefabData>();

        #endregion

        #region Hooks

        private void Init()
        {
            permission.RegisterPermission(PermissionAdmin, this);
            LoadAllPresetsNpc();
        }

        private void OnServerInitialized()
        {
            if (NpcSpawn == null)
                PrintError("NpcSpawn plugin is not loaded! Spawning features will be limited.");
            
            SpawnAllNpcs();
        }

        private void OnPluginLoaded(Plugin plugin)
        {
            if (plugin.Name == "NpcSpawn")
            {
                timer.Once(2f, SpawnAllNpcs);
            }
        }

        private void OnPluginUnloaded(Plugin plugin)
        {
            if (plugin.Name == "NpcSpawn")
            {
                foreach (var t in _respawnTimers) t?.Destroy();
                _respawnTimers.Clear();
                _activeNpcs.Clear();
            }
        }

        private void Unload()
        {
            foreach (var t in _respawnTimers) t?.Destroy();
            _respawnTimers.Clear();

            foreach (var id in _activeNpcs.Keys)
            {
                var entity = BaseNetworkable.serverEntities.Find(new NetworkableId(id));
                if (entity != null && !entity.IsDestroyed) entity.Kill();
            }
        }

        private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null || info == null) return null;

            if (_validAnimals.ContainsValue(entity.PrefabName))
            {
                if (info.damageTypes.Has(DamageType.Suicide) || info.damageTypes.Has(DamageType.Fall))
                {
                    if (!(info.Initiator is BasePlayer)) return true;
                }
            }
            return null;
        }

        private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
        {
            if (entity?.net == null) return;

            ulong id = entity.net.ID.Value;
            if (_activeNpcs.TryGetValue(id, out SpawnContextNpc context))
            {
                _activeNpcs.Remove(id);
                if (_presetsNpc.TryGetValue(context.PresetName, out PresetDataNpc data))
                {
                    _respawnTimers.Add(timer.Once(data.RespawnTimeSeconds, () => 
                        SpawnSingleNpc(context.PresetName, context.Position, data.NpcSpawnPreset)));
                }
            }
        }

        private void ProcessTrigger(BaseEntity entity, BasePlayer player)
        {
            if (entity == null || player == null || !_config.InteractablePrefabs.Contains(entity.PrefabName)) return;

            ulong entityId = entity.net.ID.Value;
            
            var preset = GetAllPresetsAnimal().FirstOrDefault(p => p != null && p.ButtonId == entityId);

            if (preset == null) return;

            float now = Time.realtimeSinceStartup;
            if (_animalCooldowns.TryGetValue(entityId, out float nextTime) && now < nextTime) return;

            _animalCooldowns[entityId] = now + preset.Cooldown;

            foreach (var sp in preset.Spawns) SpawnSingleAnimal(sp, player);
        }

        private void OnButtonPress(PressButton button, BasePlayer player) => ProcessTrigger(button, player);
        private void OnSwitchToggle(ElectricSwitch switchEnt, BasePlayer player) => ProcessTrigger(switchEnt, player);
        private void OnLootEntity(BasePlayer player, BaseEntity entity) => ProcessTrigger(entity, player);
        private void OnDoorOpened(Door door, BasePlayer player) => ProcessTrigger(door, player);
        private void OnOvenToggle(BaseOven oven, BasePlayer player) => ProcessTrigger(oven, player);

        private void OnEntityEnter(TriggerBase trigger, BaseEntity entity)
        {
            if (trigger == null || entity == null) return;
            BasePlayer player = entity as BasePlayer;
            if (player == null || player.userID == 0) return;
            BaseEntity parent = trigger.gameObject.ToBaseEntity();
            if (parent != null) ProcessTrigger(parent, player);
        }

        #endregion

        #region Commands

        [ChatCommand("npc")]
        private void CmdNpc(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PermissionAdmin))
            {
                SendReply(player, "<color=#ce3f27>Access denied.</color>");
                return;
            }

            if (args.Length < 2)
            {
                SendReply(player, "Usage:\n<color=#55aaff>/npc create <name></color>\n<color=#55aaff>/npc spawn <name> <npcspawn_preset></color>");
                return;
            }

            string action = args[0].ToLower();
            string presetName = args[1];

            if (action == "create")
            {
                if (_presetsNpc.ContainsKey(presetName))
                {
                    SendReply(player, $"Preset <color=#ce3f27>{presetName}</color> already exists.");
                    return;
                }
                _presetsNpc[presetName] = new PresetDataNpc();
                SavePresetNpc(presetName, _presetsNpc[presetName]);
                SendReply(player, $"Preset <color=#738d43>{presetName}</color> created.");
            }
            else if (action == "spawn")
            {
                if (args.Length < 3) 
                {
                    SendReply(player, "Usage: <color=#55aaff>/npc spawn <name> <npcspawn_preset></color>");
                    return;
                }
                if (!_presetsNpc.TryGetValue(presetName, out var data)) 
                {
                    SendReply(player, $"<color=#ce3f27>Preset '{presetName}' not found.</color>");
                    return;
                }
                data.NpcSpawnPreset = args[2];
                data.SpawnPoints.Add(player.transform.position);
                SavePresetNpc(presetName, data);
                SpawnSingleNpc(presetName, player.transform.position, args[2]);
                SendReply(player, "Spawn point registered.");
            }
        }

        [ChatCommand("animal")]
        private void CmdAnimal(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PermissionAdmin)) return;

            if (args.Length < 1)
            {
                SendReply(player, "Commands:\n<color=#eeaa00>/animal button <name></color>\n<color=#eeaa00>/animal spawn <name> <type></color>\n<color=#eeaa00>/animal addprefab</color>");
                return;
            }

            switch (args[0].ToLower())
            {
                case "button":
                    if (args.Length < 2) return;
                    if (Physics.Raycast(player.eyes.HeadRay(), out RaycastHit hit, 4f))
                    {
                        var entity = hit.GetEntity();
                        if (entity != null)
                        {
                            if (_config.InteractablePrefabs.Contains(entity.PrefabName))
                            {
                                string pName = args[1];
                                ulong targetId = entity.net.ID.Value;
                                var all = GetAllPresetsAnimal().ToList();

                                if (all.Any(x => x != null && x.ButtonId == targetId))
                                {
                                    SendReply(player, $"<color=#ce3f27>This entity is already used in a preset.</color>");
                                    return;
                                }
                                if (all.Any(x => x != null && x.Name.Equals(pName, StringComparison.OrdinalIgnoreCase)))
                                {
                                    SendReply(player, $"<color=#ce3f27>Preset '{pName}' already exists.</color>");
                                    return;
                                }

                                var p = new ButtonPresetAnimal { Name = pName, Cooldown = _config.DefaultCooldown, ButtonId = targetId };
                                SavePresetAnimal(p);
                                SendReply(player, $"Preset <color=#55ff55>{pName}</color> bound to <color=#55ff55>{entity.PrefabName}</color>");
                            }
                            else
                            {
                                _pendingPrefabs[player.userID] = new PendingPrefabData { PrefabName = entity.PrefabName, ExpiryTime = Time.realtimeSinceStartup + 60f };
                                SendReply(player, $"Unregistered prefab: {entity.PrefabName}. Use /animal addprefab to whitelist.");
                            }
                        }
                    }
                    break;

                case "addprefab":
                    if (_pendingPrefabs.TryGetValue(player.userID, out var pending) && Time.realtimeSinceStartup <= pending.ExpiryTime)
                    {
                        if (!_config.InteractablePrefabs.Contains(pending.PrefabName))
                        {
                            _config.InteractablePrefabs.Add(pending.PrefabName);
                            SaveConfig();
                            SendReply(player, $"Added <color=#55ff55>{pending.PrefabName}</color> to config.");
                        }
                    }
                    break;

                case "spawn":
                    if (args.Length < 3) return;
                    string type = args[2].ToLower();
                    if (!_validAnimals.ContainsKey(type))
                    {
                        SendReply(player, $"<color=#ce3f27>Invalid type!</color> Available: {string.Join(", ", _validAnimals.Keys)}");
                        return;
                    }
                    var preset = LoadPresetAnimal(args[1]);
                    if (preset == null) return;
                    preset.Spawns.Add(new SpawnPointAnimal { X = player.transform.position.x, Y = player.transform.position.y, Z = player.transform.position.z, AnimalType = type, Health = _config.DefaultHealth });
                    SavePresetAnimal(preset);
                    SendReply(player, $"Added {type} to {args[1]}.");
                    break;
            }
        }

        #endregion

        #region Helpers

        private void SpawnAllNpcs()
        {
            foreach (var kvp in _presetsNpc)
                foreach (var pos in kvp.Value.SpawnPoints)
                    SpawnSingleNpc(kvp.Key, pos, kvp.Value.NpcSpawnPreset);
        }

        private void SpawnSingleNpc(string presetName, Vector3 position, string npcSpawnPreset)
        {
            if (NpcSpawn == null || !NpcSpawn.IsLoaded) return; 
            
            var entity = NpcSpawn.Call("SpawnPreset", position, npcSpawnPreset) as BaseEntity;
            if (entity?.net != null)
                _activeNpcs[entity.net.ID.Value] = new SpawnContextNpc { PresetName = presetName, Position = position };
        }

        private void LoadAllPresetsNpc()
        {
            string path = Path.Combine(Interface.Oxide.DataDirectory, Name, "NPCs");
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);
            foreach (var file in Directory.GetFiles(path, "*.json"))
            {
                string name = Path.GetFileNameWithoutExtension(file);
                var data = Interface.Oxide.DataFileSystem.ReadObject<PresetDataNpc>($"{Name}/NPCs/{name}");
                if (data != null) _presetsNpc[name] = data;
            }
        }

        private void SavePresetNpc(string name, PresetDataNpc data) => Interface.Oxide.DataFileSystem.WriteObject($"{Name}/NPCs/{name}", data);

        private void SpawnSingleAnimal(SpawnPointAnimal sp, BasePlayer target)
        {
            if (!_validAnimals.TryGetValue(sp.AnimalType, out string prefab)) return;
            var entity = GameManager.server.CreateEntity(prefab, sp.Position + new Vector3(0, 0.5f, 0), Quaternion.identity);
            if (entity == null) return;
            entity.Spawn();
            var npc = entity as BaseNpc;
            if (npc != null) { npc.StopMoving(); npc.CancelInvoke(npc.TickAi); if (target != null) npc.AttackTarget = target; }
            var combat = entity as BaseCombatEntity;
            if (combat != null) { combat.InitializeHealth(sp.Health, sp.Health); combat.lifestate = BaseCombatEntity.LifeState.Alive; }
        }

        private void SavePresetAnimal(ButtonPresetAnimal preset) => Interface.Oxide.DataFileSystem.WriteObject($"{Name}/Animals/{preset.Name}", preset);

        private ButtonPresetAnimal LoadPresetAnimal(string name) => 
            Interface.Oxide.DataFileSystem.ExistsDatafile($"{Name}/Animals/{name}") ? 
            Interface.Oxide.DataFileSystem.ReadObject<ButtonPresetAnimal>($"{Name}/Animals/{name}") : null;

        private IEnumerable<ButtonPresetAnimal> GetAllPresetsAnimal()
        {
            string path = Path.Combine(Interface.Oxide.DataDirectory, Name, "Animals");
            if (!Directory.Exists(path)) yield break;
            foreach (string file in Directory.GetFiles(path, "*.json"))
            {
                yield return LoadPresetAnimal(Path.GetFileNameWithoutExtension(file));
            }
        }

        #endregion
    }
}

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