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

FREE SpawnNPC : 1.0.24 1.0.24

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Rust;
using System;
using Facepunch;
using Oxide.Core;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using System.Collections.Generic;
using Newtonsoft.Json.Converters;
using UnityEngine.SceneManagement;
#region License
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
/*The "AI Scarecrow" part of the code is added with the permission of "Spiikesan", from the "Better Scarecrows" plugin from the umod website.Thanks to "Spiikesan" for helping with the plugin.*/
#endregion
namespace Oxide.Plugins {
    [Info("Spawn NPC", "Author mod from Flames", "1.0.24")]
    [Description("Spawn NPC on monuments")]
#region class NPC RustPlugin
    class SpawnNPC : RustPlugin {
        [PluginReference]
        Plugin Kits, MarkerManager;
        static SpawnNPC Instance;
        private PluginConfig config;
#region Permission
        private const string PermChatCommand = "spawnnpc.command";
#endregion
#region NPCType variable
        private const string PrefabScientist = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam.prefab";
        private const string PrefabScarecrow = "assets/prefabs/npc/scarecrow/scarecrow.prefab";
#endregion
#region loot crate variable
        static readonly Dictionary<string, string> lootPrefabs = new Dictionary<string, string> {
            ["radtown_loot_component_test"]
            = "assets/bundled/prefabs/radtown/loot_component_test.prefab", ["radtown_crate_elite"] = "assets/bundled/prefabs/radtown/crate_elite.prefab", ["radtown_bradley_crate"] = "assets/prefabs/npc/m2bradley/bradley_crate.prefab", ["radtown_heli_crate"] = "assets/prefabs/npc/patrol helicopter/heli_crate.prefab", ["radtown_foodbox"] = "assets/bundled/prefabs/radtown/foodbox.prefab", ["radtown_crate_normal"] = "assets/bundled/prefabs/radtown/crate_normal.prefab", ["radtown_crate_normal_2"] = "assets/bundled/prefabs/radtown/crate_normal_2.prefab", ["radtown_crate_normal_2_medical"] = "assets/bundled/prefabs/radtown/crate_normal_2_medical.prefab", ["radtown_crate_basic"] = "assets/bundled/prefabs/radtown/crate_basic.prefab", ["radtown_crate_tools"] = "assets/bundled/prefabs/radtown/crate_tools.prefab", ["underwater_labs_crate_normal"] = "assets/bundled/prefabs/radtown/underwater_labs/crate_normal.prefab", ["underwater_labs_crate_normal_2"] = "assets/bundled/prefabs/radtown/underwater_labs/crate_normal_2.prefab", ["underwater_labs_crate_tools"] = "assets/bundled/prefabs/radtown/underwater_labs/crate_tools.prefab", ["underwater_labs_crate_food_2"] = "assets/bundled/prefabs/radtown/underwater_labs/crate_food_2.prefab", ["underwater_labs_tech_parts_2"] = "assets/bundled/prefabs/radtown/underwater_labs/tech_parts_2.prefab", ["underwater_labs_crate_elite"] = "assets/bundled/prefabs/radtown/underwater_labs/crate_elite.prefab", ["radtown_crate_underwater_basic"] = "assets/bundled/prefabs/radtown/crate_underwater_basic.prefab", ["radtown_crate_underwater_advanced"] = "assets/bundled/prefabs/radtown/crate_underwater_advanced.prefab", ["chinooklockedcrate"] = "assets/prefabs/deployable/chinooklockedcrate/codelockedhackablecrate.prefab", ["supply_drop"] = "assets/prefabs/misc/supply drop/supply_drop.prefab", ["presentdrop"] = "assets/prefabs/misc/xmas/sleigh/presentdrop.prefab"
        }
        ;
#endregion
        private readonly List<BaseCombatEntity> MyNPCs = new List<BaseCombatEntity>();
        private readonly List<ExpectedCorpse> expectedCorpses = new List<ExpectedCorpse>();
        private readonly List<GameObject> availableMonuments = new List<GameObject>();
        private readonly LootGenerator lootGenerator = new LootGenerator();
#region class NPC Inventories
        public Dictionary<ulong, Inv> botInventories = new Dictionary<ulong, Inv>();
        public class Inv {
            public List<InvContents>[] inventory = {
                new List<InvContents>(), new List<InvContents>(), new List<InvContents>()
            }
            ;
        }
        public class InvContents {
            public int ID;
            public int amount;
            public ulong skinID;
        }
#endregion
#region class LootGenerator
        class LootGenerator {
            private Dictionary<string, LootSpawner> spawners = new Dictionary<string, LootSpawner>();
            public LootSpawner Get(string name) {
                if (!lootPrefabs.ContainsKey(name)) {
                    Instance.PrintWarning($"Unknown crate NpcType: {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;
            }
        }
#endregion
#region class LootSpawner
        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));
                Instance.NextTick(() => {
                    this.LootContainer.Kill();
                }
                );
            }
            public void Cleanup() {
                if (this.IsContainerOk) {
                    this.LootContainer.Kill();
                }
            }
            public void SpawnLoot() {
                this.LootContainer.inventory.Clear();
                ItemManager.DoRemoves();
                this.LootContainer.PopulateLoot();
            }
            public ItemContainer Inventory {
                get {
                    return this.LootContainer.inventory;
                }
            }
        }
#endregion
#region class ExpectedCorpse
        class ExpectedCorpse {
            public ulong id;
            public NPCPreset preset;
        }
#endregion
#region class NPCItemDefinition
        class NPCItemDefinition {
            [JsonProperty(PropertyName = "Short Name Item")]
            public string ItemShortName;
            [JsonProperty(PropertyName = "SkinID Item")]
            public ulong[] ItemSkinID = {
                0
            }
            ;
            public virtual Item CreateItem() {
                ulong data2Inf_unknwn = 0;
                if (this.ItemSkinID.Length > 0) {
                    data2Inf_unknwn = this.ItemSkinID.GetRandom();
                }
                return ItemManager.CreateByName(ItemShortName, 1, data2Inf_unknwn);
            }
        }
#endregion
#region class NPCItemSpendableDefinition
        class NPCItemSpendableDefinition : NPCItemDefinition {
            [JsonProperty(PropertyName = "Amount Item")]
            public int ItemAmount;
            public override Item CreateItem() {
                var item = base.CreateItem();
                item.amount = ItemAmount;
                return item;
            }
        }
#endregion
#region class NPCExtraLootDefinition
        class NPCExtraLootDefinition {
            [JsonProperty(PropertyName = "Items List")]
            public Item[] Items;
            public class Item {
                [JsonProperty(PropertyName = "Short Name Item")]
                public string ItemShortName;
                [JsonProperty(PropertyName = "SkinID Item")]
                public uint ItemSkinID;
                [JsonProperty(PropertyName = "Min Item")]
                public int ItemsCountMin;
                [JsonProperty(PropertyName = "Max Item")]
                public int ItemsCountMax;
                [JsonProperty(PropertyName = "Probability Item Spawn")]
                public float Probability;
            }
            public List<global : : Item> GetItems() {
                List<global : : Item> result = new List<global : : Item>(Items.Length);
                foreach (var item in Items) {
                    if (GetItemRandom(item.Probability)) {
                        result.Add(ItemManager.CreateByName(item.ItemShortName, UnityEngine.Random.Range(item.ItemsCountMin, item.ItemsCountMax), item.ItemSkinID));
                    }
                }
                return result;
            }
            private static bool GetItemRandom(float Probability) {
                return UnityEngine.Random.Range(0f, 1f) < Probability;
            }
        }
#endregion
#region class NPCLootDefinition
        class NPCLootDefinition {
            [JsonProperty(PropertyName = "Use loot Rust")]
            public bool UseLootRust;
            [JsonProperty(PropertyName = "Use loot Kit")]
            public bool UseLootKit;
            [JsonProperty(PropertyName = "Use Loot Extra")]
            public bool UseExtraLoot;
            [JsonProperty(PropertyName = "Use loot Crates")]
            public bool UseCratesLoot;
            [JsonProperty(PropertyName = "Name crates loot")]
            public string[] NameCratesLoot = {
            }
            ;
            [JsonProperty(PropertyName = "Min Item")]
            public int ItemsMin;
            [JsonProperty(PropertyName = "Max Item")]
            public int ItemsMax;
            [JsonProperty(PropertyName = "List exclude items")]
            public string[] ListExcludeItems;
            [JsonProperty(PropertyName = "Extra loot definition")]
            public NPCExtraLootDefinition ExtraLootDefinition;
            public void SpawnItemsToContainer(ItemContainer container) {
                if (UseLootRust == true) {
                    return;
                }
                if (UseCratesLoot == true && NameCratesLoot.Length != 0) {
                    ClearContainer(container);
                    uint spawnedItems = 0;
                    for (int i = 0; i < NameCratesLoot.Length; i++) {
                        var spawner = Instance.lootGenerator.Get(NameCratesLoot[i]);
                        spawner.SpawnLoot();
                        while (spawner.Inventory.itemList.Count > 0) {
                            var item = spawner.Inventory.itemList[0];
                            if (ListExcludeItems != null && ListExcludeItems.Contains(item.info.shortname)) {
                                item.RemoveFromContainer();
                                item.Remove();
                                continue;
                            }
                            item.MoveToContainer(container);
                            spawnedItems++;
                            if (spawnedItems >= UnityEngine.Random.Range(ItemsMin, ItemsMax)) continue;
                        }
                        if (spawnedItems >= ItemsMax) break;
                    }
                }
                else if (UseCratesLoot == true && NameCratesLoot.Length == 0) {
                    Instance.PrintWarning($"The list is empty, Default Loot for this NPC, check the settings (Crates Loot!)");
                }
                if (UseExtraLoot == true && ExtraLootDefinition != null) {
                    ClearContainer(container);
                    var extraItems = ExtraLootDefinition.GetItems();
                    foreach (var item in extraItems) {
                        item.MoveToContainer(container);
                    }
                }
                else if (UseExtraLoot == true && ExtraLootDefinition == null) {
                    Instance.PrintWarning($"The list is empty, Default Loot for this NPC, check the settings (Extra Loot!)");
                }
            }
        }
#endregion
#region class NPCPreset
        class NPCPreset {
            [JsonProperty(PropertyName = "Preset name")]
            public string PresetName;
            [JsonProperty(PropertyName = "How many seconds until the respawns")]
            public int RespawnNPCPeriodSeconds;
            [JsonProperty(PropertyName = "Use random appearance on the map, after the destruction of the NPC")]
            public bool RespawnRandomPosition;
            [JsonProperty(PropertyName = "NPC Type (Scientist = true | Scarecrow = false)")]
            public bool NPCTypeScientist;
            [JsonProperty(PropertyName = "Display name")]
            public string NpcDisplayName;
            [JsonProperty(PropertyName = "Health")]
            public float NpcHealth;
            [JsonProperty(PropertyName = "At what distance does the NPC see the enemy")]
            public float NpcLookRadius;
            [JsonProperty(PropertyName = "At what distance does the NPC attack the enemy")]
            public float NpcAttackRadius;
            [JsonProperty(PropertyName = "Roam range, (affects the Scientist only)")]
            public float NpcRoamRange;
            [JsonProperty(PropertyName = "Enable map marker NPC")]
            public bool EnableMarker;
            [JsonProperty(PropertyName = "Radio disabled")]
            public bool DesableRadio;
            [JsonProperty(PropertyName = "Enable weapon damage multiplier")]
            public bool EnableDamageMultiplier;
            [JsonProperty(PropertyName = "Weapon damage multiplier")]
            public float WeaponDamageMultiplier;
            [JsonProperty(PropertyName = "Use kit (clothing and Loot)")]
            public bool UseKit;
            [JsonProperty(PropertyName = "Kit ID")]
            public string KitName = "";
            [JsonProperty(PropertyName = "Wear")]
            public NPCItemDefinition[] Wear;
            [JsonProperty(PropertyName = "Belt")]
            public NPCItemSpendableDefinition[] Belt;
            [JsonProperty(PropertyName = "Loot")]
            public NPCLootDefinition Loot;
            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);
                object checkKit = Instance.Kits?.CallHook("GetKitInfo", KitName);
                if (UseKit == false) {
                    foreach (var wearItem in Wear) {
                        wearItem.CreateItem().MoveToContainer(npcPlayer.inventory.containerWear);
                    }
                    foreach (var beltItem in Belt) {
                        beltItem.CreateItem().MoveToContainer(npcPlayer.inventory.containerBelt);
                    }
                }
                if (UseKit == true && checkKit != null) {
                    Instance.Kits?.Call("GiveKit", npcPlayer, KitName);
                }
                else if (UseKit == true && checkKit == null) {
                    Instance.PrintWarning($"Kit ID not filled this [NPC preset name - {PresetName}] check the settings (Kit ID)!");
                }
                npcPlayer.displayName = NpcDisplayName;
                npcPlayer.startHealth = NpcHealth;
                npcPlayer.InitializeHealth(NpcHealth, NpcHealth);
                if (npcPlayer is ScientistNPC && NPCTypeScientist == true) {
                    ScientistNPC _scientist = npcPlayer as ScientistNPC;
                    if (EnableMarker) Interface.CallHook("API_CreateMarker", _scientist, "scientist", 0, 3f, 0.0f, NpcDisplayName, "0", "0", 0.0f);
                    if (DesableRadio) {
                        _scientist.DeathEffects = new GameObjectRef[0];
                        _scientist.radioChatterType = ScientistNPC.RadioChatterType.NONE;
                        _scientist.RadioChatterEffects = new GameObjectRef[0];
                    }
                    for (int i = 0; i < _scientist.inventory.containerBelt.itemList.ToList().Count;
                    i++) {
                        Item item = _scientist.inventory.containerBelt.itemList.ToList()[i];
                        if (item == null) continue;
                        BaseProjectile weaponProjectile = (item?.GetHeldEntity() as HeldEntity) as BaseProjectile;
                        if (weaponProjectile == null) return;
                        weaponProjectile.effectiveRange = NpcAttackRadius;
                        weaponProjectile.CanUseAtMediumRange = true;
                        weaponProjectile.CanUseAtLongRange = true;
                    }
                }
                if (npcPlayer is ScarecrowNPC && NPCTypeScientist == false) {
                    ScarecrowNPC _scarecrow = npcPlayer as ScarecrowNPC;
                    if (EnableMarker) Interface.CallHook("API_CreateMarker", _scarecrow, "scarecrow", 0, 3f, 0.0f, NpcDisplayName, "0", "0", 0.0f);
                }
            }
            public NPCNavigationComponent Spawn(Vector3 position, bool shouldRespawn = true) {
                if (NPCTypeScientist == true) {
                    BaseCombatEntity baseCombatEntity = InstantiateEntity(PrefabScientist, position);
                    InitPlayer(baseCombatEntity);
                    var npc = baseCombatEntity.gameObject.AddComponent<NPCNavigationComponent>();
                    npc.SetInfo(PresetName, position, NpcRoamRange, shouldRespawn);
                    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;
                        if (baseNavigator.topologyPreference == 0) baseNavigator.topologyPreference = (TerrainTopology.Enum)1673010749;
                        baseNavigator.topologyPreference |= TerrainTopology.Enum.Monument | TerrainTopology.Enum.Building;
                    }
                    Instance.timer.In(1f, () => {
                        Instance.ScientistBrain(baseCombatEntity, position);
                    }
                    );
                    Instance.MyNPCs.Add(baseCombatEntity);
                    return npc;
                }
                else if (NPCTypeScientist == false) {
                    BaseCombatEntity baseCombatEntity = InstantiateEntity(PrefabScarecrow, position);
                    InitPlayer(baseCombatEntity);
                    var npc = baseCombatEntity.gameObject.AddComponent<NPCNavigationComponent>();
                    npc.SetInfo(PresetName, position, NpcRoamRange, shouldRespawn);
                    Instance.MyNPCs.Add(baseCombatEntity);
                    return npc;
                }
                return null;
            }
            private BaseCombatEntity InstantiateEntity(string prefab, Vector3 position) {
                var entity = GameManager.server.CreateEntity(prefab, position, new Quaternion()) as BaseCombatEntity;
                entity.name = prefab;
                SceneManager.MoveGameObjectToScene(entity.gameObject, Rust.Server.EntityScene);
                UnityEngine.Object.Destroy(entity.GetComponent<Spawnable>());
                if (!entity.gameObject.activeSelf) entity.SetActive(true);
                entity.enableSaving = false;
                entity.Spawn();
                return entity;
            }
        }
#endregion
#region AI Scientist
        private void ScientistBrain(BaseCombatEntity baseCombatEntity, Vector3 position) {
            if (baseCombatEntity == null) return;
            if (baseCombatEntity is global : : HumanNPC) ScientistBrain<global : : HumanNPC>(baseCombatEntity, position);
        }
        private void ScientistBrain<T>(BaseCombatEntity baseCombatEntity, Vector3 position) where T : BaseEntity {
            BaseAIBrain baseAIBrain = baseCombatEntity.GetComponent<BaseAIBrain>();
            if (baseAIBrain != null) {
                ScientistNPC _scientist = baseCombatEntity as ScientistNPC;
                NPCNavigationComponent npcPlayer = _scientist.GetComponent<NPCNavigationComponent>();
                var preset = Instance.config.GetNPCPreset(npcPlayer.presetName);
                _scientist.Brain.Navigator.Init(_scientist, _scientist.Brain.Navigator.Agent);
                _scientist.Brain.ForceSetAge(0);
                _scientist.Brain.states.Remove(AIState.TakeCover);
                _scientist.Brain.states.Remove(AIState.Roam);
                _scientist.Brain.states.Remove(AIState.Chase);
                _scientist.Brain.states.Remove(AIState.Idle);
                _scientist.Brain.states.Remove(AIState.Flee);
                _scientist.Brain.Navigator.BestCoverPointMaxDistance = preset.NpcRoamRange / 2;
                _scientist.Brain.Navigator.BestRoamPointMaxDistance = preset.NpcRoamRange;
                _scientist.Brain.Navigator.MaxRoamDistanceFromHome = preset.NpcRoamRange;
                _scientist.Brain.AddState(new TakeCoverState {
                    brain = _scientist.Brain, Position = position
                }
                );
                _scientist.Brain.AddState(new IdleState(_scientist) {
                    brain = _scientist.Brain
                }
                );
                _scientist.Brain.AddState(new ChaseState(_scientist) {
                    brain = _scientist.Brain
                }
                );
                baseAIBrain.Events.Memory.Position.Set(position, 4);
                GenericRoamState<T> genericRoamState = new GenericRoamState<T>();
                genericRoamState.brain = baseAIBrain;
                baseAIBrain.states[AIState.Roam] = genericRoamState;
                _scientist.Brain.Navigator.PlaceOnNavMesh();
                _scientist.Brain.Senses.Init(_scientist, _scientist.Brain, 5f, preset.NpcLookRadius, preset.NpcLookRadius * 2, -1f, true, true, true, preset.NpcLookRadius, false, false, true, EntityType.Player, false);
            }
        }
        private Vector3 GetHomePosition(BaseCombatEntity baseCombatEntity) {
            if (baseCombatEntity != null) {
                if (baseCombatEntity is global : : HumanNPC) return GetHomePosition<global : : HumanNPC>(baseCombatEntity);
            }
            return Vector3.zero;
        }
        private Vector3 GetHomePosition<T>(BaseCombatEntity baseCombatEntity) where T : BaseEntity => baseCombatEntity.GetComponent<BaseAIBrain>().Events.Memory.Position.Get(4);
        public class IdleState : ScientistBrain.BasicAIState {
            private readonly ScientistNPC _npc;
            public IdleState(ScientistNPC npc) : base(AIState.Idle) {
                _npc = npc;
            }
            public override float GetWeight() => 50f;
            public override void StateEnter(BaseAIBrain brain, BaseEntity entity) {
                base.StateEnter(brain, entity);
                _npc.SetPlayerFlag(BasePlayer.PlayerFlags.Relaxed, true);
            }
            public override void StateLeave(BaseAIBrain brain, BaseEntity entity) {
                base.StateLeave(brain, entity);
                _npc.SetPlayerFlag(BasePlayer.PlayerFlags.Relaxed, false);
            }
        }
        public class ChaseState : ScientistBrain.BasicAIState {
            private StateStatus status = StateStatus.Error;
            private float nextPositionUpdateTime;
            public ChaseState(ScientistNPC npc) : base(AIState.Chase) {
                AgrresiveState = true;
            }
            public override void StateEnter(BaseAIBrain brain, BaseEntity entity) {
                base.StateEnter(brain, entity);
                status = StateStatus.Error;
                if (brain.PathFinder == null) return;
                status = StateStatus.Running;
                nextPositionUpdateTime = 0.0f;
            }
            public override void StateLeave(BaseAIBrain brain, BaseEntity entity) {
                base.StateLeave(brain, entity);
                Stop();
            }
            public override StateStatus StateThink(float delta, BaseAIBrain brain, BaseEntity entity) {
                if (status == StateStatus.Error) return status;
                var baseEntity = brain.Events.Memory.Entity.Get(brain.Events.CurrentInputMemorySlot);
                if (baseEntity == null) return StateStatus.Error;
                var distance = Vector3.Distance(entity.transform.position, baseEntity.transform.position);
                if (brain.Senses.Memory.IsLOS(baseEntity) || (double)distance <= brain.Navigator.MaxRoamDistanceFromHome) brain.Navigator.SetFacingDirectionEntity(baseEntity);
                else brain.Navigator.ClearFacingDirectionOverride();
                brain.Navigator.SetCurrentSpeed(distance <= brain.Navigator.MaxRoamDistanceFromHome ? BaseNavigator.NavigationSpeed.Normal : BaseNavigator.NavigationSpeed.Fast);
                if (Time.time > nextPositionUpdateTime) {
                    nextPositionUpdateTime = Time.time + UnityEngine.Random.Range(0.5f, 1f);
                    brain.Navigator.SetDestination(baseEntity.transform.position, BaseNavigator.NavigationSpeed.Normal);
                }
                return brain.Navigator.Moving ? StateStatus.Running : StateStatus.Finished;
            }
            private void Stop() {
                brain.Navigator.Stop();
                brain.Navigator.ClearFacingDirectionOverride();
            }
        }
        public class TakeCoverState : ScientistBrain.BasicAIState {
            private StateStatus status = StateStatus.Error;
            private BaseEntity coverFromEntity;
            public Vector3 Position;
            public TakeCoverState() : base(AIState.TakeCover) {
            }
            public override void StateEnter(BaseAIBrain brain, BaseEntity entity) {
                base.StateEnter(brain, entity);
                status = StateStatus.Running;
                if (StartMovingToCover()) return;
                status = StateStatus.Error;
            }
            public override void StateLeave(BaseAIBrain brain, BaseEntity entity) {
                base.StateLeave(brain, entity);
                brain.Navigator.ClearFacingDirectionOverride();
                ClearCoverPointUsage(entity);
            }
            public override StateStatus StateThink(float delta, BaseAIBrain brain, BaseEntity entity) {
                FaceCoverFromEntity();
                if (status == StateStatus.Error) return status;
                return brain.Navigator.Moving ? StateStatus.Running : StateStatus.Finished;
            }
            private void ClearCoverPointUsage(BaseEntity entity) {
                var aiPoint = brain.Events.Memory.AIPoint.Get(4);
                if (aiPoint == null) return;
                aiPoint.ClearIfUsedBy(entity);
            }
            private bool StartMovingToCover() => brain.Navigator.SetDestination(Position, BaseNavigator.NavigationSpeed.Normal);
            private void FaceCoverFromEntity() {
                coverFromEntity = brain.Events.Memory.Entity.Get(brain.Events.CurrentInputMemorySlot);
                if (coverFromEntity != null) brain.Navigator.SetFacingDirectionEntity(coverFromEntity);
            }
        }
        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) {
                NPCNavigationComponent npcPlayer = brain.GetComponent<NPCNavigationComponent>();
                var preset = Instance.config.GetNPCPreset(npcPlayer.presetName);
                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, preset.NpcRoamRange);
                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, UnityEngine.Random.Range(minRange, maxRange), degree + UnityEngine.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 (UnityEngine.Random.Range(0f, 1f) <= 0.9f && preferredTopologyIndex > 0) chosenPosition = preferedTopologySamples[UnityEngine.Random.Range(0, preferredTopologyIndex)];
                else if (topologyIndex > 0) chosenPosition = topologySamples[UnityEngine.Random.Range(0, topologyIndex)];
                else chosenPosition = fallback;
                return chosenPosition;
            }
        }
#endregion
#region class NPCSpawnRule
        public abstract class NPCSpawnRule {
            [JsonProperty(PropertyName = "Min Amount Npc")]
            public int MinAmountNpc;
            [JsonProperty(PropertyName = "Max Amount Npc")]
            public int MaxAmountNpc;
            private static NavMeshHit navmeshHit;
            protected bool FindValidSpawnPoint(out Vector3 result) {
                for (int i = 0; i < 250; i++) {
                    Vector3 randomPos = this.GetSpawnPos();
                    RaycastHit hitInfo;
                    if (Physics.Raycast(randomPos, Vector3.down, out hitInfo, 500.0f, Layers.Solid)) {
                        randomPos.y = hitInfo.point.y;
                    }
                    else {
                        continue;
                    }
                    if (!this.PreNavmeshCheck(randomPos)) {
                        continue;
                    }
                    if (NavMesh.SamplePosition(randomPos, out navmeshHit, 50, 1)) {
                        if (!NavMesh.SamplePosition(navmeshHit.position, out navmeshHit, 10f, 1)) {
                            continue;
                        }
                        if (navmeshHit.position.y - TerrainMeta.HeightMap.GetHeight(navmeshHit.position) > 0.8f) {
                            continue;
                        }
                        if (navmeshHit.position.y < TerrainMeta.WaterMap.GetHeight(navmeshHit.position)) {
                            continue;
                        }
                        if (IsInTextures(navmeshHit.position)) {
                            continue;
                        }
                        if (IsInRockPrefab(navmeshHit.position)) {
                            continue;
                        }
                        if (IsNearWorldCollider(navmeshHit.position)) {
                            continue;
                        }
                        if (IsBuildingBlocked(navmeshHit.position)) {
                            continue;
                        }
                        if (IsInObject(navmeshHit.position)) {
                            continue;
                        }
                        if (AntiHack.TestInsideTerrain(navmeshHit.position)) {
                            continue;
                        }
                        result = navmeshHit.position;
                        return true;
                    }
                }
                result = Vector3.zero;
                return false;
            }
            private static bool IsInTextures(Vector3 point) {
                var raycastStart = new Vector3(point.x, 250.0f, point.z);
                RaycastHit hitInfo;
                if (Physics.Raycast(raycastStart, Vector3.down, out hitInfo, 500.0f, Layers.Solid)) {
                    var res = Mathf.Abs(hitInfo.point.y - point.y) > 0.4f;
                    return res;
                }
                return true;
            }
            protected static RaycastHit raycastHit;
            protected static Collider[] _buffer = new Collider[256];
            protected const int WORLD_LAYER = 65536;
            protected static readonly string[] AcceptedColliders = new string[] {
                "road", "carpark", "rocket_factory", "range", "train_track", "runway", "_grounds", "concrete_slabs", "lighthouse", "cave", "office", "walkways", "sphere", "tunnel", "industrial", "junkyard"
            }
            ;
            protected static readonly string[] BlockedColliders = new string[] {
                "cliff", "rock", "junk", "range", "invisible"
            }
            ;
            protected static bool IsInRockPrefab(Vector3 position) {
                Physics.queriesHitBackfaces = true;
                bool isInRock = Physics.Raycast(position, Vector3.up, out raycastHit, 20f, WORLD_LAYER, QueryTriggerInteraction.Ignore) && BlockedColliders.Any(s => raycastHit.collider?.gameObject?.name.Contains(s, System.Globalization.CompareOptions.OrdinalIgnoreCase) ?? false);
                Physics.queriesHitBackfaces = false;
                return isInRock;
            }
            protected 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 (AcceptedColliders.Any(s => _buffer[i].gameObject.name.Contains(s))) removed++;
                }
                return removed != count;
            }
            private static bool IsBuildingBlocked(Vector3 position) => GamePhysics.CheckSphere<BuildingPrivlidge>(position, 30.0f);
            protected bool IsInObject(Vector3 position) {
                int layerMask = LayerMask.GetMask("Default", "Tree", "Construction", "World", "Vehicle_Detailed", "Deployed");
                return Physics.OverlapSphere(position, 0.5f, layerMask).Length > 0;
            }
            protected virtual int GetCount() {
                return UnityEngine.Random.Range(MinAmountNpc, MaxAmountNpc);
            }
            protected abstract bool PreNavmeshCheck(Vector3 randomPos);
            protected abstract Vector3 GetSpawnPos();
            public abstract void Spawn();
        }
#endregion
#region ScarecrowNPC Random homePosition
        public Vector3 GetRandomPosition() {
            Vector3 position = Vector3.zero;
            for (int i = 0; i < 2; i++) {
                float x = UnityEngine.Random.Range(-TerrainMeta.Size.x / 2, TerrainMeta.Size.x / 2), z = UnityEngine.Random.Range(-TerrainMeta.Size.z / 2, TerrainMeta.Size.z / 2), y = TerrainMeta.HeightMap.GetHeight(new Vector3(x, 0, z));
                position = new Vector3(x, y + 0.5f, z);
                if (!NavMesh.SamplePosition(position, out navmeshHit, 10f, 1) || position.y < TerrainMeta.WaterMap.GetHeight(position) || IsInRockPrefab(position) || IsNearWorldCollider(position) || IsBuildingBlocked(position) || IsInObject(position) || AntiHack.TestInsideTerrain(position)) {
                    i = 0;
                }
                else break;
            }
            if (position == Vector3.zero) {
                position.y = TerrainMeta.HeightMap.GetHeight(0, 0);
            }
            return position;
        }
        private static NavMeshHit navmeshHit;
        private static RaycastHit raycastHit;
        private static Collider[] _buffer = new Collider[256];
        private const int WORLD_LAYER = 65536;
        private static readonly string[] AcceptedColliders = 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[] BlockedColliders = new string[] {
            "cliff", "rock", "junk", "range", "invisible"
        }
        ;
        private static bool IsInRockPrefab(Vector3 position) {
            Physics.queriesHitBackfaces = true;
            bool isInRock = Physics.Raycast(position, Vector3.up, out raycastHit, 20f, WORLD_LAYER, QueryTriggerInteraction.Ignore) && BlockedColliders.Any(s => raycastHit.collider?.gameObject?.name.Contains(s, System.Globalization.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 (AcceptedColliders.Any(s => _buffer[i].gameObject.name.Contains(s))) removed++;
            }
            return removed != count;
        }
        private static bool IsBuildingBlocked(Vector3 position) => GamePhysics.CheckSphere<BuildingPrivlidge>(position, 30.0f);
        private bool IsInObject(Vector3 position) {
            int layerMask = LayerMask.GetMask("Default", "Tree", "Construction", "World", "Vehicle_Detailed", "Deployed");
            return Physics.OverlapSphere(position, 0.5f, layerMask).Length > 0;
        }
#endregion
#region class NPCMonumentSpawnRule
        class NPCMonumentSpawnRule : NPCSpawnRule {
            [JsonProperty(PropertyName = "Name Monument")]
            public string MonumentName;
            [JsonProperty(PropertyName = "Name Preset Npc")]
            public string PresetName;
            [JsonProperty(PropertyName = "Min Radius Spawn Npc")]
            public float MinRadiusSpawn;
            [JsonProperty(PropertyName = "Max Radius Spawn Npc")]
            public float MaxRadiusSpawn;
            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.MyNPCs.Count;
                    for (int i = 0; i < count; i++) {
                        if (FindValidSpawnPoint(out point)) {
                            Instance.config.GetNPCPreset(PresetName).Spawn(point);
                        }
                    }
                    int spawnedCount = Instance.MyNPCs.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(MinRadiusSpawn, MaxRadiusSpawn);
                float x = radius * Mathf.Cos(angle);
                float y = radius * Mathf.Sin(angle);
                return currentMonumentPos + new Vector3(x, 250.0f, y);
            }
            protected override bool PreNavmeshCheck(Vector3 randomPos) {
                return true;
            }
        }
#endregion
#region class NPCBiomeSpawnRule
        class NPCBiomeSpawnRule : NPCSpawnRule {
            [JsonConverter(typeof(StringEnumConverter))]
            [JsonProperty(PropertyName = "Name Biome")]
            public TerrainBiome.Enum BiomeName;
            [JsonProperty(PropertyName = "Name Preset Npc")]
            public string PresetName;
            public override void Spawn() {
                Vector3 point;
                var count = this.GetCount();
                for (int i = 0; i < count; i++) {
                    if (FindValidSpawnPoint(out point)) {
                        Instance.config.GetNPCPreset(PresetName).Spawn(point);
                    }
                }
            }
            protected override Vector3 GetSpawnPos() {
                return new Vector3(UnityEngine.Random.Range(-TerrainMeta.Size.x / 2, TerrainMeta.Size.x / 2), 250.0f, 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;
            }
        }
#endregion
#region class NPCNavigationComponent
        public class NPCNavigationComponent : MonoBehaviour {
            public BasePlayer npcPlayer;
            public Vector3 homePosition;
            public float NpcRoamRange;
            public string presetName;
            public bool naturalDeath = false;
            public bool handledKill;
            public bool shouldRespawn;
            private void Awake() {
                npcPlayer = GetComponent<BasePlayer>();
                enabled = false;
            }
            private void Check() {
                if (npcPlayer == null || npcPlayer.IsDestroyed || npcPlayer.isMounted || !npcPlayer.HasBrain) return;
                if (Vector3.Distance(npcPlayer.transform.position, homePosition) > NpcRoamRange) {
                    ScarecrowBrain scarecrow_brain = npcPlayer.GetComponent<ScarecrowBrain>();
                    if (scarecrow_brain && scarecrow_brain.Senses.Memory.Targets.Count == 0) {
                        scarecrow_brain.Navigator.SetDestination(homePosition, BaseNavigator.NavigationSpeed.Slow, 0f, 0f);
                    }
                }
                CheckSwimming();
            }
            private void CheckSwimming() {
                if (Instance.MyNPCs.Contains(npcPlayer) && npcPlayer.IsSwimming()) {
                    var _npc = npcPlayer as BaseCombatEntity;
                    Instance.NextTick(() => {
                        _npc.Kill();
                        Destroy(this);
                        return;
                    }
                    );
                }
            }
            private void OnDestroy() {
                InvokeHandler.CancelInvoke(this, Check);
                if (npcPlayer != null && npcPlayer.IsValid() && !npcPlayer.IsDestroyed && !naturalDeath) {
                    npcPlayer.Kill();
                }
            }
            public void SetInfo(string presetName, Vector3 homePosition, float NpcRoamRange, bool shouldRespawn) {
                this.presetName = presetName;
                this.homePosition = homePosition;
                this.NpcRoamRange = NpcRoamRange;
                this.shouldRespawn = shouldRespawn;
                InvokeHandler.InvokeRepeating(this, Check, 1.0f, 10.0f);
            }
        }
#endregion
#region OnEntityDeath
      
// ... [Code preview truncated due to length. Full source in attached file] ...

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