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

FREE NPC : 1.5.4 1.5.4

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

namespace Oxide.Plugins {
    [Info("NPC", "Egor Blagov", "1.5.4")]
    [Description("Spawns and configures enemy NPCs")]
    class NPC : RustPlugin {
        static NPC Instance;
        const float HeightToRaycast = 250;
        const float RaycastDistance = 500;
        const float RaycastDiffThreshold = 0.4f;
        const int SpawnTrials = 150;
        const string perm = "npc.adm";
        private const float SupplyRadius = 30.0f;

        enum NPCType { Murderer, Scarecrow, Scientist, Military };
        static Dictionary<NPCType, string> prefabs = new Dictionary<NPCType, string> {
            [NPCType.Murderer] = "assets/prefabs/npc/murderer/murderer.prefab",
            [NPCType.Scarecrow] = "assets/prefabs/npc/scarecrow/scarecrow.prefab",
            [NPCType.Scientist] = "assets/prefabs/npc/scientist/scientist.prefab",
            [NPCType.Military] = "assets/prefabs/npc/scientist/htn/scientist_full_mp5.prefab"
        };
        static Dictionary<string, string> lootPrefabs = new Dictionary<string, string> {
            ["crate_elite"] = "assets/bundled/prefabs/radtown/crate_elite.prefab",
            ["crate_normal"] = "assets/bundled/prefabs/radtown/crate_normal.prefab",
            ["crate_normal_2"] = "assets/bundled/prefabs/radtown/crate_normal_2.prefab",
            ["crate_basic"] = "assets/bundled/prefabs/radtown/crate_basic.prefab",
            ["crate_tools"] = "assets/bundled/prefabs/radtown/crate_tools.prefab",
            ["supply_drop"] = "assets/prefabs/misc/supply drop/supply_drop.prefab",
            ["bradley_crate"] = "assets/prefabs/npc/m2bradley/bradley_crate.prefab",
            ["heli_crate"] = "assets/prefabs/npc/patrol helicopter/heli_crate.prefab",
            ["loot_component_test"] = "assets/bundled/prefabs/radtown/loot_component_test.prefab",
            ["presentdrop"] = "assets/prefabs/misc/xmas/sleigh/presentdrop.prefab"
        };
        private Dictionary<uint, BaseCombatEntity> spawnedNpcs = new Dictionary<uint, BaseCombatEntity>();
        private Dictionary<uint, NonUserSupplyDrop> nonUserSupplyDrops = new Dictionary<uint, NonUserSupplyDrop>();
        private List<ExpectedCorpse> expectedCorpses = new List<ExpectedCorpse>();
        private List<GameObject> availableMonuments = new List<GameObject>();
        private LootGenerator lootGenerator = new LootGenerator();
        private List<Vector3> supplyDrops = new List<Vector3>();
        class NonUserSupplyDrop {
            public List<NPCController> npcs = new List<NPCController>();
        }
        class UserSupplyDrop {
            static private int nextId = 1;
            public int id = nextId++;
            public DateTime SupplySignalMaxStamp;

            public uint SupplySignalID = 0;
            public uint SupplyDropID = 0;

            public bool EqualDrop(SupplyDrop drop) {
                return drop.net.ID == SupplyDropID;
            }

            [JsonIgnore]
            public CargoPlane CargoPlane;

            [JsonIgnore]
            public bool IsInvalid => SupplySignalMaxStamp < DateTime.Now && CargoPlane == null && SupplyDropID == 0;

        }
        class StoredData {
            public List<UserSupplyDrop> userSupplyDrops = new List<UserSupplyDrop>();
            public bool DropIsHandled(SupplyDrop supplyDrop) {
                if (userSupplyDrops.Find(u => u.EqualDrop(supplyDrop)) != null) {
                    return true;
                }

                foreach (var drop in userSupplyDrops) {
                    if (drop.CargoPlane != null && drop.CargoPlane.transform.position == supplyDrop.transform.position) {
                        drop.SupplyDropID = supplyDrop.net.ID;
                        return true;
                    }
                }

                return false;
            }

            public void HandleSupplySignal(SupplySignal supplySignal) {
                userSupplyDrops.Add(new UserSupplyDrop {
                    SupplySignalID = supplySignal.net.ID,
                    SupplySignalMaxStamp = DateTime.Now.AddSeconds(supplySignal.timerAmountMax + 4.0f)
                });
            }

            public void HandleCargoPlane(CargoPlane cargoPlane) {
                userSupplyDrops.RemoveAll(dr => dr.IsInvalid);
                UserSupplyDrop nonCalledDrop = null;
                foreach (var drop in userSupplyDrops) {
                    if (drop.CargoPlane != null || drop.SupplyDropID != 0) {
                        continue;
                    }
                    nonCalledDrop = drop;
                    break;
                }
                    
                if (nonCalledDrop == null) {
                    return;
                }

                nonCalledDrop.CargoPlane = cargoPlane;
            }

            public void OnKillSupplyDrop(SupplyDrop supplyDrop) {
                userSupplyDrops.RemoveAll(dr => dr.EqualDrop(supplyDrop));
            }

            public void CleanupSupplyDrops() {
                List<SupplyDrop> actualDrops = Facepunch.Pool.GetList<SupplyDrop>();
                foreach (var ent in BaseNetworkable.serverEntities) {
                    if (ent is SupplyDrop) {
                        actualDrops.Add(ent as SupplyDrop);
                    }
                }

                userSupplyDrops.RemoveAll(dr => actualDrops.FindIndex(actualDrop => dr.EqualDrop(actualDrop)) == -1);
                Facepunch.Pool.FreeList(ref actualDrops);
            }
        }

        private StoredData storedData;
        #region LOOT HANDLING
        class LootGenerator {
            private Dictionary<string, LootSpawner> spawners = new Dictionary<string, LootSpawner>();
            public LootSpawner get(string name) {
                if (!lootPrefabs.ContainsKey(name)) {
                    Instance.PrintError($"Unknown crate type: {name}");
                    return null;
                }

                if (!spawners.ContainsKey(name)) {
                    spawners[name] = new LootSpawner(lootPrefabs[name]);
                }

                if (!spawners[name].isContainerOk) {
                    spawners[name].Cleanup();
                    spawners[name] = new LootSpawner(lootPrefabs[name]);
                }

                return spawners[name];
            }

            public void Cleanup() {
                foreach (var spawner in spawners.Values) {
                    spawner.Cleanup();
                }
                spawners.Clear();
                spawners = null;
            }
        }
        class LootSpawner {
            private string fullPrefabName;
            private LootContainer lootContainer;
            public bool isContainerOk {
                get {
                    return this.lootContainer != null && !this.lootContainer.IsDestroyed;
                }
            }

            public LootSpawner(string prefabName) {
                this.fullPrefabName = prefabName;
                this.lootContainer = GameManager.server.CreateEntity(this.fullPrefabName, new Vector3(), new Quaternion(), true) as LootContainer;
                this.lootContainer.Spawn();
                this.lootContainer.CancelInvoke(new Action(this.lootContainer.SpawnLoot));
            }

            public void Cleanup() {
                if (this.isContainerOk) {
                    this.lootContainer.Kill();
                }
            }

            public void SpawnLoot() {
                if (Instance.config.LootUpdatedByThirdPartyPlugins) {
                    this.lootContainer.SpawnLoot();
                } else {
                    this.lootContainer.inventory.Clear();
                    ItemManager.DoRemoves();
                    this.lootContainer.PopulateLoot();
                }
            }

            public ItemContainer inventory {
                get {
                    return this.lootContainer.inventory;
                }
            }
        }
        #endregion
        class ExpectedCorpse {
            public ulong id;
            public NPCPreset preset;
        }
        #region NPC DESCRIPTION
        class NPCItemDefinition {
            public string shortname;
            public ulong[] skin = { 0 };

            public virtual Item createItem() {
                ulong data2Inf_unknwn = 0;
                if (this.skin.Length > 0) {
                    data2Inf_unknwn = this.skin.GetRandom();
                }

                return ItemManager.CreateByName(shortname, 1, data2Inf_unknwn);
            }
        }

        class NPCItemSpendableDefinition : NPCItemDefinition {
            public int amount = 1;

            public override Item createItem() {
                var item = base.createItem();
                item.amount = amount;
                return item;
            }
        }

        class NPCExtraLootDefinition {
            public class Item {
                public string shortname;
                public uint skinId;
                public int minAmount;
                public int maxAmount;
                public float probability;
            }

            public Item[] items;

            public List<global::Item> GetItems() {
                List<global::Item> result = new List<global::Item>();
                foreach (var item in items) {
                    if (getRandom(item.probability)) {
                        result.Add(ItemManager.CreateByName(item.shortname, UnityEngine.Random.Range(item.minAmount, item.maxAmount + 1), item.skinId));
                    }
                }
                return result;
            }
        }

        class NPCLootDefinition {
            public string[] lootCrates = { };
            public uint crateItemsMin = 1;
            public uint crateItemsMax = 10;
            public NPCExtraLootDefinition extra;
            public string[] excludeList;
            public void spawnItemsToContainer(ItemContainer container) {
                clearContainer(container);

                if (lootCrates.Length == 0 && extra == null) {
                    return;
                }

                uint min = Math.Min(crateItemsMax, crateItemsMin);
                uint max = Math.Max(crateItemsMax, crateItemsMin);
                uint spawnedItems = 0;

                for (int k = 0; k < max; k++) { // not giving infinite cycle just in case
                    for (int i = 0; i < lootCrates.Length; i++) {
                        var spawner = Instance.lootGenerator.get(lootCrates[i]);
                        spawner.SpawnLoot();
                        while (spawner.inventory.itemList.Count > 0) {
                            var item = spawner.inventory.itemList[0];
                            if (excludeList != null && excludeList.Contains(item.info.shortname)) {
                                item.RemoveFromContainer();
                                item.Remove();
                                continue;
                            }
                            item.MoveToContainer(container);
                            spawnedItems++;
                            if (spawnedItems >= max) {
                                break;
                            }
                        }

                        if (spawnedItems >= max) {
                            break;
                        }
                    }

                    if (spawnedItems >= min) {
                        break;
                    }
                }

                if (extra != null) {
                    var extraItems = extra.GetItems();
                    foreach (var item in extraItems) {
                        item.MoveToContainer(container);
                    }
                }
            }
        }

        class NPCPreset {
            public string name;
            [JsonConverter(typeof(StringEnumConverter))]
            public NPCType type;
            public string displayName;

            public float accuracy = 1.0f;
            public float receivingDamageMultiplier = 1.0f;
            public float dealingDamageMultiplier = 1.0f;
            public float roamingRange = 40f;

            public NPCItemDefinition[] wear;
            public NPCItemSpendableDefinition[] belt;
            public NPCLootDefinition loot;

            [JsonIgnore]
            public string prefab {
                get {
                    return prefabs[this.type];
                }
            }

            public void InitPlayer(BaseCombatEntity npcEnt) {
                BasePlayer npcPlayer = npcEnt as BasePlayer;
                if (npcPlayer == null) {
                    throw new Exception("Unable to init player");
                }

                clearContainer(npcPlayer.inventory.containerBelt);
                clearContainer(npcPlayer.inventory.containerMain);
                clearContainer(npcPlayer.inventory.containerWear);
                npcPlayer.inventory.ServerUpdate(0f);

                wear.ToList().ForEach(npcItemDefintion => {
                    npcItemDefintion.createItem().MoveToContainer(npcPlayer.inventory.containerWear);
                });

                belt.ToList().ForEach(NPCItemDefinition => {
                    NPCItemDefinition.createItem().MoveToContainer(npcPlayer.inventory.containerBelt);
                });

                npcPlayer.displayName = displayName;

                //if (npcPlayer is NPCPlayerApex) {
                //    var npc = npcPlayer as NPCPlayerApex;
                //    npc.RadioEffect = new GameObjectRef();
                //}

                //if (npcPlayer is HTNPlayer) {
                //    var npc = npcPlayer as HTNPlayer;
                //    npc.AiDefinition.StopVoices(npc);
                //}
            }

            public NPCController spawn(Vector3 point, bool shouldRespawn=true) {
                BaseCombatEntity entity = InstantiateEntity(prefab, point);

                InitPlayer(entity);

                var npc = entity.gameObject.AddComponent<NPCController>();
                npc.SetInfo(name, point, roamingRange, shouldRespawn);
                Instance.spawnedNpcs.Add(entity.net.ID, entity);
                return npc;
            }

            private BaseCombatEntity InstantiateEntity(string prefab, Vector3 position) {
                var gameObject = Instantiate.GameObject(GameManager.server.FindPrefab(prefab), position, new Quaternion());
                gameObject.name = prefab;

                SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
                UnityEngine.Object.Destroy(gameObject.GetComponent<Spawnable>());

                if (!gameObject.activeSelf)
                    gameObject.SetActive(true);

                BaseCombatEntity component = gameObject.GetComponent<BaseCombatEntity>();
                component.enableSaving = false;
                component.Spawn();
                component.InitializeHealth(component.StartHealth(), component.StartMaxHealth());

                return component;
            }
        }
        public abstract class NPCSpawnRule {
            public int minCount = 1;
            public int maxCount = 4;
            public abstract void spawn();
            protected bool IsInTextures(Vector3 point) {
                var raycastStart = new Vector3(
                    point.x,
                    HeightToRaycast,
                    point.z
                );

                RaycastHit hitInfo;
                if (Physics.Raycast(raycastStart, Vector3.down, out hitInfo, RaycastDistance, Layers.Solid)) {

                    var res = Mathf.Abs(hitInfo.point.y - point.y) > RaycastDiffThreshold;
                    return res;
                }
                return true;
            }

            protected virtual int GetCount() {
                if (Instance.config.scaleNpcFromRulesToMaxCount) {
                    var minScaled = Mathf.RoundToInt(Instance.config.spawnCountFactor * (float)minCount);
                    var maxScaled = Mathf.RoundToInt(Instance.config.spawnCountFactor * (float)maxCount);
                    return UnityEngine.Random.Range(minScaled, maxScaled + 1);
                }

                return UnityEngine.Random.Range(minCount, maxCount + 1);
            }

            protected bool FindValidSpawnPoint(out Vector3 result) {
                for (int i = 0; i < SpawnTrials; i++) {
                    Vector3 randomPos = this.GetSpawnPos();

                    RaycastHit hitInfo;
                    if (Physics.Raycast(randomPos, Vector3.down, out hitInfo, RaycastDistance, Layers.Solid)) {
                        randomPos.y = hitInfo.point.y;
                    } else {
                        continue;
                    }

                    if (!this.PreNavmeshCheck(randomPos)) {
                        continue;
                    }

                    NavMeshHit hit;
                    if (NavMesh.SamplePosition(randomPos, out hit, 50, 1)) {

                        if (hit.position.y - TerrainMeta.HeightMap.GetHeight(hit.position) > 3) {
                            continue;
                        }

                        if(WaterLevel.Test(hit.position))
                        {
                            continue;
                        }
                        if (IsInTextures(hit.position)) {
                            continue;
                        }


                        result = hit.position;
                        return true;
                    }
                }
                
                result = Vector3.zero;
                return false;
            }

            protected abstract bool PreNavmeshCheck(Vector3 randomPos);
            protected abstract Vector3 GetSpawnPos();
        }
        class NPCMonumentSpawnRule : NPCSpawnRule {
            public string monumentName;
            public string npcPresetName;

            public float minSpread = 0;
            public float maxSpread = 10;
            private List<Vector3> monumentPositions;
            private Vector3 currentMonumentPos;
            private bool inited = false;
            public override void spawn() {
                if (!inited) {
                    inited = true;
                    var monuments = Instance.availableMonuments.FindAll(g => g.name.Contains(this.monumentName));
                    if (monuments.Count == 0) {
                        return;
                    }
                    monumentPositions = monuments.Select(x => x.transform.position).ToList();
                }

                if (monumentPositions.Count == 0) {
                    return;
                }
                foreach (var monumentPosition in monumentPositions) {
                    currentMonumentPos = monumentPosition;
                    Vector3 point;
                    var count = this.GetCount();
                    int beforeSpawn = Instance.spawnedNpcs.Count;
                    for (int i = 0; i < count; i++) {
                        if (FindValidSpawnPoint(out point)) {
                            Instance.config.getNPCPreset(npcPresetName).spawn(point);
                            if (Instance.spawnedNpcs.Count() >= Instance.config.maxNpcs) {
                                return;
                            }
                        }
                    }
                    int spawnedCount = Instance.spawnedNpcs.Count - beforeSpawn;
                    if (count != spawnedCount) {
                        Instance.PrintWarning($"Unable to spawn {count} at {monumentName}, result is {spawnedCount}");
                    }
                }
                currentMonumentPos = new Vector3();
            }

            protected override Vector3 GetSpawnPos() {
                float angle = UnityEngine.Random.Range(0, Mathf.PI * 2);
                float radius = UnityEngine.Random.Range(minSpread, maxSpread);
                float x = radius * Mathf.Cos(angle);
                float y = radius * Mathf.Sin(angle);
                return currentMonumentPos + new Vector3(x, HeightToRaycast, y);
            }

            protected override bool PreNavmeshCheck(Vector3 randomPos) {
                return true;
            }
        }
        class NPCBiomeSpawnRule : NPCSpawnRule {
            [JsonConverter(typeof(StringEnumConverter))]
            public TerrainBiome.Enum biomeName;
            public string npcPresetName;

            public override void spawn() {
                Vector3 point;
                var count = this.GetCount();
                for (int i = 0; i < count; i++) {
                    if (FindValidSpawnPoint(out point)) {
                        Instance.config.getNPCPreset(npcPresetName).spawn(point);
                        if (Instance.spawnedNpcs.Count() >= Instance.config.maxNpcs) {
                            return;
                        }
                    }
                }
            }

            protected override Vector3 GetSpawnPos() {
                return new Vector3(
                    UnityEngine.Random.Range(-TerrainMeta.Size.x / 2, TerrainMeta.Size.x / 2),
                    HeightToRaycast,
                    UnityEngine.Random.Range(-TerrainMeta.Size.z / 2, TerrainMeta.Size.z / 2)
                );
            }

            protected override bool PreNavmeshCheck(Vector3 randomPos) {
                return (TerrainBiome.Enum)TerrainMeta.BiomeMap.GetBiomeMaxType(randomPos) == this.biomeName;
            }
        }
        class NPCAirdropSpawnRule : NPCSpawnRule {
            public string npcPresetName;
            public float minSpread = 0;
            public float maxSpread = 10;

            private SupplyDrop currentDrop;
            private NonUserSupplyDrop supplyDropInfo;
            public void SetDrop(SupplyDrop drop) {
                this.currentDrop = drop;
            }

            public override void spawn() {
                if (this.currentDrop == null) {
                    return;
                }

                Vector3 point;
                var count = this.GetCount();
                for (int i = 0; i < count; i++) {
                    if (FindValidSpawnPoint(out point)) {
                        supplyDropInfo.npcs.Add(Instance.config.getNPCPreset(npcPresetName).spawn(point, false));
                    }
                }
                this.currentDrop = null;
            }

            protected override Vector3 GetSpawnPos() {
                float angle = UnityEngine.Random.Range(0, Mathf.PI * 2);
                float radius = UnityEngine.Random.Range(minSpread, maxSpread);
                float x = radius * Mathf.Cos(angle);
                float y = radius * Mathf.Sin(angle);
                return this.currentDrop.transform.position.XZ() + new Vector3(x, HeightToRaycast, y);
            }

            protected override bool PreNavmeshCheck(Vector3 randomPos) {
                return true;
            }

            protected override int GetCount() {
                var res = UnityEngine.Random.Range(minCount, maxCount+1);
                return res;
            }

            public void SetSupplyDropInfo(NonUserSupplyDrop info) {
                supplyDropInfo = info;
            }
        }
        #endregion
        #region CONFIG
        private PluginConfig config;
        class PluginConfig {
            #region READONLY
            public string[] monumentNamesReadonly {
                get {
                    return new string[] {
                        "lighthouse", "powerplant_1", "military_tunnel_1", "harbor_1", "harbor_2", "airfield_1",
                        "trainyard_1", "water_treatment_plant_1", "warehouse", "satellite_dish", "sphere_tank",
                        "radtown_small_3", "launch_site_1", "gas_station_1", "supermarket_1", "mining_quarry_c",
                        "mining_quarry_a", "mining_quarry_b", "junkyard_1"
                    };
                }
            }

            public string[] biomeNamesReadonly {
                get {
                    return new string[] {
                        TerrainBiome.Enum.Arctic.ToString(), TerrainBiome.Enum.Arid.ToString(), TerrainBiome.Enum.Temperate.ToString(), TerrainBiome.Enum.Tundra.ToString()
                    };
                }
            }

            public string[] crateNamesReadonly {
                get {
                    return lootPrefabs.Keys.ToArray();
                }
            }

            public string[] npcTypesReadonly {
                get {
                    return new string[] {
                        NPCType.Murderer.ToString(),
                        NPCType.Scarecrow.ToString(),
                        NPCType.Scientist.ToString()
                    };
                }
            }
            #endregion
            [JsonIgnore]
            public List<NPCSpawnRule> AllRules {
                get {
                    return this.biomeSpawnRules.Cast<NPCSpawnRule>().Concat(this.spawnRules).ToList();
                }
            }

            #region COMMON PARAMS
            public bool CanBeAttackedByNPCs = true;
            public bool CanBeAttackedByTurrets = true;
            public bool CanBeAttackedByHeli = true;
            public bool CanBeAttackedByBradley = true;
            public bool CanBeAttackedByAnimals = true;

            public bool LootUpdatedByThirdPartyPlugins = true;

            public int respawnPeriodSeconds = 300;
            public int maxNpcs = 100;
            public bool scaleNpcFromRulesToMaxCount = true;
            public int aidropZombiesRemoveDelaySeconds = 120;
            #endregion

            public NPCPreset[] npcPresets = {
                new NPCPreset {
                    name = "zombieCarrion",
                    type = NPCType.Scarecrow,
                    displayName = "Zombie Carrion",
                    accuracy = 1.0f,
                    receivingDamageMultiplier = 2.0f,
                    dealingDamageMultiplier = 0.5f,
                    wear = new NPCItemDefinition[] {
                        new NPCItemDefinition {
                            shortname = "burlap.shoes",
                            skin = new ulong[] { 820952520 }
                        },
                        new NPCItemDefinition {
                            shortname = "burlap.shirt"
                        }
                    },

                    belt = new NPCItemSpendableDefinition[] {
                        new NPCItemSpendableDefinition {
                            shortname = "spear.stone"
                        }
                    },

                    loot = new NPCLootDefinition {
                        crateItemsMin = 1,
                        crateItemsMax = 3,
                        lootCrates = new string[] { "crate_normal_2" }
                    }
                }
            };

            public NPCPreset getNPCPreset(string presetName) {
                return this.npcPresets.ToList().Find(np => np.name.Equals(presetName));
            }

            public NPCMonumentSpawnRule[] spawnRules = {
                new NPCMonumentSpawnRule {
                    monumentName = "sphere_tank",
                    minCount = 3,
                    maxCount = 10,
                    minSpread = 0f,
                    maxSpread = 25.0f,
                    npcPresetName = "zombieCarrion"
                }
            };

            public NPCBiomeSpawnRule[] biomeSpawnRules = {
                new NPCBiomeSpawnRule {
                    biomeName = TerrainBiome.Enum.Arctic,
                    minCount = 10,
                    maxCount = 20,
                    npcPresetName = "zombieCarrion",
                }
            };

            public NPCAirdropSpawnRule[] airdropSpawnRules = {
                new NPCAirdropSpawnRule {
                    maxCount = 10,
                    minCount = 5,
                    maxSpread = 7,
                    minSpread = 2,
                    npcPresetName = "zombieCarrion"
                }
            };

            [JsonIgnore]
            public float spawnCountFactor = 1.0f;

            public void Init() {
                validate();
                initMonuments();
                initSpawnScaleFactor();
                InitSpawns();
                Instance.Puts($"Successfully spawned {Instance.spawnedNpcs.Count} NPCs");
            }

            private void validate() {
                bool valid = true;
                if (npcPresets.Select(preset => preset.name).Distinct().Count() != npcPresets.Length) {
                    Instance.PrintError("There are duplicating NPC preset names, check the config!");
                    valid = false;
                }

                foreach (var preset in npcPresets) {
                    foreach (var nid in preset.wear) {
                        if (ItemManager.itemList.FindIndex(id => id.shortname == nid.shortname) == -1) {
                            Instance.PrintError($"NPC preset {preset.name}: shortname {nid.shortname} doesn't exist");
                            valid = false;
                        }
                    }

                    foreach (var nid in preset.belt) {
                        if (ItemManager.itemList.FindIndex(id => id.shortname == nid.shortname) == -1) {
                            Instance.PrintError($"NPC preset {preset.name}: shortname {nid.shortname} doesn't exist");
                            valid = false;
                        }
                    }

                    foreach (var crateName in preset.loot.lootCrates) {
                        if (!crateNamesReadonly.Contains(crateName)) {
                            Instance.PrintError($"NPC preset {preset.name}: crate {crateName} doesn't exist");
                            valid = false;
                        }
                    }
                }

                foreach (var rule in spawnRules) {
                    if (npcPresets.ToList().FindIndex(pr => pr.name.Equals(rule.npcPresetName)) == -1) {
                        Instance.PrintError($"Unknown NPC preset {rule.npcPresetName}, check the config!");
                        valid = false;
                    }

                    if (!monumentNamesReadonly.Contains(rule.monumentName)) {
                        Instance.PrintError($"Unknown monument name {rule.monumentName}, check the config!");
                        valid = false;
                    }
                }

                foreach (var rule in biomeSpawnRules) {
                    if (npcPresets.ToList().FindIndex(pr => pr.name.Equals(rule.npcPresetName)) == -1) {
                        Instance.PrintError($"Unknown NPC preset {rule.npcPresetName}, check the config!");
                        valid = false;
                    }

                    if (!biomeNamesReadonly.Contains(rule.biomeName.ToString())) {
                        Instance.PrintError($"Unknown biome name {rule.biomeName.ToString()}, check the config!");
                        valid = false;
                    }
                }

                foreach (var rule in airdropSpawnRules) {
                    if (npcPresets.ToList().FindIndex(pr => pr.name.Equals(rule.npcPresetName)) == -1) {
                        Instance.PrintError($"Unknown NPC preset {rule.npcPresetName}, check the config!");
                        valid = false;
                    }
                }

                if (!valid) {
                    throw new Exception("Config is invalid check the console");
                }
            }
            public void InitSpawns() {
                foreach (var rule in AllRules) {
                    rule.spawn();
                    
                    if (Instance.spawnedNpcs.Count >= maxNpcs) {
                        Instance.PrintWarning($"NPC limit reached {maxNpcs}");
                        return;
                    }
                }
            }
            private void initSpawnScaleFactor() {
                int maxFromRules = 0;
                foreach (var monument in Instance.availableMonuments) {
                    var monumentName = monumentNamesReadonly.ToList().Find(name => monument.name.Contains(name));
                    if (monumentName != null) {
                        foreach (var rule in spawnRules) {
                            if (rule.monumentName.Equals(monumentName)) {
                                maxFromRules += rule.maxCount;
                            }
                        }
                    }
                }

                foreach (var rule in biomeSpawnRules) {
                    maxFromRules += rule.maxCount;
                }

                if (maxFromRules == 0) {
                    maxFromRules = 1;
                }
                Instance.Puts($"max zombies from rules: {maxFromRules}");
                Instance.Puts($"max from config: {maxNpcs}");
                if (scaleNpcFromRulesToMaxCount) { 
                    spawnCountFactor = (float)maxNpcs / (float)maxFromRules;
                    Instance.Puts($"resulting factor: {spawnCountFactor}");
                } else {
                    Instance.Puts($"No scaling, will try to spawn {maxFromRules} NPCs");
                }
            }
            private void initMonuments() {
                var allobjects = UnityEngine.Object.FindObjectsOfType<GameObject>();
                foreach (var gobject in allobjects) {
                    if (gobject.name.Contains("autospawn/monument")) {
                        if (gobject.transform.position == new Vector3(0, 0, 0)) {
                            continue;
                        }
                        Instance.availableMonuments.Add(gobject);
                    }
                }

                Instance.Puts($"Found {Instance.availableMonuments.Count} monuments");
            }
        }
        #endregion
        private static bool getRandom(float probability = 0.5f) {
            return UnityEngine.Random.Range(0f, 1f) < probability;
        }
        private static void clearContainer(ItemContainer container) {
            while (container.itemList.Count > 0) {
                var item = container.itemList[0];
                item.RemoveFromContainer();
                item.Remove(0f);
            }
        }

        public class NPCController : MonoBehaviour {
            public BasePlayer npcPlayer;
            public string presetName;
            public float roamingRange;
            public Vector3 homePos;
            public bool naturalDeath = false;
            public bool handledKill;
            public bool shouldRespawn;

            private void Awake() {
                npcPlayer = GetComponent<BasePlayer>();
                enabled = false;
            }

            private void OnDestroy() {
                InvokeHandler.CancelInvoke(this, CheckLocation);
                if (npcPlayer != null && npcPlayer.IsValid() && !npcPlayer.IsDestroyed && !naturalDeath) {
                    npcPlayer.Kill();
                }

                if (!this.handledKill) {
                    Instance.PrintError("Unhandled kill of NPC.Maybe it's another plugin tryies to remove NPCs");
                }
            }

            public void SetInfo(string presetName, Vector3 homePos, float roamingRange, bool shouldRespawn) {
                this.presetName = presetName;
                this.homePos = homePos;
                this.roamingRange = roamingRange;
                this.shouldRespawn = shouldRespawn;
                InvokeHandler.InvokeRepeating(this, CheckLocation, 1f, 20f);
            }

            private void CheckLocation() {
                if (Vector3.Distance(npcPlayer.transform.position, homePos) > roamingRange) {
                    //((npcPlayer as HTNPlayer)?.AiDomain as MurdererDomain)?.SetDestination(homePos);
                    var comp = npcPlayer.gameObject.GetComponent<ScientistBrain>();
                    if (comp)
                    {
                        comp.Navigator.SetDestination(homePos);
                        return;
                    }

                    var scare = npcPlayer.gameObject.GetComponent<ScarecrowBrain>();
                    if (scare)
                    {
                        scare.Navigator.SetDestination(homePos);
                        return;
                    }
                    (npcPlayer as NPCPlayer)?.SetDestination(homePos);
                }
            }
        }

        protected override void LoadDefaultConfig() {
            Config.WriteObject(new PluginConfig(), true);
        }

        #region Hooks

        object OnNpcTarget(BaseNpc npc, BaseEntity target) {
            if (!this.config.CanBeAttackedByAnimals) {
                if (target?.net?.ID != null && this.spawnedNpcs.ContainsKey(target.net.ID)) {
                    return false;
                }
            }

            return null;
        }

        //TODO: FIX CAN TARGET
        //object OnNpcPlayerTarget(NPCPlayerApex npcPlayer, BaseEntity target) {
        //    if (!this.config.CanBeAttackedByNPCs) {
        //        if (target?.net?.ID != null && this.spawnedNpcs.ContainsKey(target.net.ID)) {
        //            return false;
        //        }
        //    }

        //    return null;
        //}

        object CanBradleyApcTarget(BradleyAPC apc, BaseEntity target) {
            if (!this.config.CanBeAttackedByBradley) {
                if (target?.net?.ID != null && this.spawnedNpcs.ContainsKey(target.net.ID)) {
                    return false;
                }
            }

            return null;
        }

        object CanHelicopterTarget(PatrolHelicopterAI heli, BasePlayer target) {
            if (!this.config.CanBeAttackedByHeli) {
                if (target?.net?.ID != null && this.spawnedNpcs.ContainsKey(target.net.ID)) {
                    return false;
                }
            }

            return null;
        }

        object CanBeTargeted(BaseCombatEntity target, MonoBehaviour turret) {
            if (!this.config.CanBeAttackedByTurrets) {
                if (target?.net?.ID != null && this.spawnedNpcs.ContainsKey(target.net.ID)) {
                    return false;
                }
            }

            return null;
        }

        private void Init() {
            permission.RegisterPermission(perm, this);
            Instance = this;
            config = Config.ReadObject<PluginConfig>();
            Config.WriteObject(config);
            storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
        }

        private void OnServerInitialized() {
            config.Init();
            timer.Once(1.0f, storedData.CleanupSupplyDrops);
        }

        private void OnNewSave() {
            storedData = new StoredData();
            Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
        }

        private void OnServerSave() {
            if (storedData != null) {
                Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
            }
        }
        private void OnEntityKill(BaseEntity entity) {
            if (entity is SupplyDrop) {
                storedData.OnKillSupplyDrop(entity as SupplyDrop);

                if (nonUserSupplyDrops.ContainsKey(entity.net.ID)) {
                    uint id = entity.net.ID;
                    timer.Once(config.aidropZombiesRemoveDelaySeconds, () => {
                        foreach (var npcController in nonUserSupplyDrops[id].npcs) {
                            npcController.handledKill = true;
                            UnityEngine.Object.Destroy(npcController);
                        }

                        nonUserSupplyDrops.Remove(id);
                    });
                }
            }
        }
        private void OnEntityDeath(BaseCombatEntity entity, HitInfo info) {
            if (entity?.net?.ID == null)
                return;

            if (spawnedNpcs.ContainsKey(entity.net.ID)) {
                NPCController npcPlayer = entity.GetComponent<NPCController>();
                if (npcPlayer != null) {
                    npcPlayer.naturalDeath = true;
                    var preset = config.getNPCPreset(npcPlayer.presetName);

                    expectedCorpses.Add(new ExpectedCorpse {
                        id = npcPlayer.npcPlayer.userID,
                        preset = preset
                    });
                    if (npcPlayer.shouldRespawn) {
                        timer.In(config.respawnPeriodSeconds, () => {
                            preset.spawn(npcPlayer.homePos);
                        });
                    }

                    npcPlayer.handledKill = true;
                    UnityEngine.Object.Destroy(npcPlayer);
                }

                spawnedNpcs.Remove(entity.net.ID);
            }
        }

        private void Unload() {
            foreach (var npc in spawnedNpcs) {
                if (npc.Value != null) {
                    var controller = npc.Value.GetComponent<NPCController>();
                    if (controller) {
                        controller.handledKill = true;
                        UnityEngine.Object.Destroy(controller);
                    }
                }
            }

            var npcs = UnityEngine.Object.FindObjectsOfType<NPCController>();
            if (npcs != null) {
                foreach (var gameObj in npcs) {
                    gameObj.handledKill = true;
                    UnityEngine.Object.Destroy(gameObj);
                }
            }

            lootGenerator.Cleanup();
            spawnedNpcs.Clear();
            Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
        }

        private void OnEntityTakeDamage(BaseCombatEntity victimEntity, HitInfo hitInfo) {
            if (victimEntity?.net?.ID != null && spawnedNpcs.ContainsKey(victimEntity.net.ID)) {
                NPCController npcPlayer = victimEntity.GetComponent<NPCController>();
                if (npcPlayer != null) {
                    hitInfo.damageTypes.ScaleAll(config.getNPCPreset(npcPlayer.presetName).receivingDamageMultiplier);

                    if (hitInfo.Initiator != null && hitInfo.Initiator is BradleyAPC) {
                        hitInfo.damageTypes.ScaleAll(0);
                    }
                }
            }

            if (hitInfo.Initiator?.net?.ID != null && spawnedNpcs.ContainsKey(hitInfo.Initiator.net.ID)) {
                NPCController npcPlayer = hitInfo.Initiator.GetComponent<NPCController>();
                var preset = config.getNPCPreset(npcPlayer.presetName);
                if (npcPlayer != null) {
                    hitInfo.damageTypes.ScaleAll(preset.dealingDamageMultiplier);
                }

                if (getRandom(1 - preset.accuracy)) { // accuracy emulation
                    hitInfo.damageTypes = new DamageTypeList();
                    hitInfo.HitMaterial = 0;
                    hitInfo.PointStart = Vector3.zero;
                }
            }
        }

        void OnEntitySpawned(BaseEntity entity) {
            if (entity is LootableCorpse) {
                LootableCorpse corpse = entity as LootableCorpse;

                var corpseID = corpse.playerSteamID;
                var expectedCorpse = expectedCorpses.Find(ex => ex.id == corpseID);
                if (expectedCorpse == null)
                    return;
                var preset = expectedCorpse.preset;
                expectedCorpses.Remove(expectedCorpse);

                NextTick(() => {
                    if (corpse != null && corpse.IsValid()) {
                        preset.loot.spawnItemsToContainer(corpse.containers[0]);
                    }
                });

            } else if (entity is SupplySignal) {
                storedData.HandleSupplySignal(entity as SupplySignal);
            } else if (entity is CargoPlane) {
                storedData.HandleCargoPlane(entity as CargoPlane);
            } else if (entity is SupplyDrop) {
                if (storedData.DropIsHandled(entity as SupplyDrop)) {
                    return;
                }

                nonUserSupplyDrops.Add(entity.net.ID, new NonUserSupplyDrop());
                foreach (var rule in this.config.airdropSpawnRules) {
                    rule.SetDrop(entity as SupplyDrop);
                    rule.SetSupplyDropInfo(nonUserSupplyDrops[entity.net.ID]);
                    rule.spawn();
                }
            }
        }

        #endregion
        [ChatCommand("npc_killall")]
        private void KillAllCommand(BasePlayer player, string command, string[] args) {
            if (!permission.UserHasPermission(player.UserIDString, perm)) {
                SendReply(player, "У вас нет привилегии для выполнения этой команды");
                return;
            }

            int count = spawnedNpcs.Count;
            foreach (var npc in spawnedNpcs) {
                var controller = npc.Value.GetComponent<NPCController>();
                if (controller) {
                    controller.handledKill = true;
                    UnityEngine.Object.Destroy(controller);
                }
            }
            spawnedNpcs.Clear();
            SendReply(player, $"{count} NPC удалено");
        }
        [ChatCommand("npc_count")]
        private void SpawnCount(BasePlayer player, string command, string[] args) {
            if (!permission.UserHasPermission(player.UserIDString, perm)) {
                SendReply(player, "У вас нет привилегии для выполнения этой команды");
                return;
            }
            SendReply(player, $"на карте {this.spawnedNpcs.Count} NPC");
        }

        [ChatCommand("npc_spawn")]
        private void SpawnCommand(BasePlayer player, string command, string[] args) {
            if (!permission.UserHasPermission(player.UserIDString, perm)) {
                SendReply(player, "У вас нет привилегии для выполнения этой команды");
                return;
            }

           if (args.Length == 1) {
                var presetName = args[0];
                var preset = this.config.getNPCPreset(presetName);
                if (preset == null) {
                    SendReply(player, $"Пресет {presetName} не найден");
                    return;
                }

                try {
                    var point = getSpawnPoint(player);
                    preset.spawn(point, false);
       
// ... [Code preview truncated due to length. Full source in attached file] ...

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