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

FREE ZombieHorde : 0.4.8 0.4.8

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Plugins;
using ProtoBuf;
using Rust;
using Rust.Ai;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.AI;
using Random = UnityEngine.Random;
namespace Oxide.Plugins {
    [Info("ZombieHorde", "k1lly0u", "0.4.8")]
    class ZombieHorde : RustPlugin {
        [PluginReference]
        private Plugin Kits, Spawns;
        private static ZombieHorde Instance {
            get;
            set;
        }
        public enum SpawnSystem {
            None, Random, SpawnsDatabase
        }
        public enum SpawnState {
            Spawn, Despawn
        }
        private static BaseNavigator.NavigationSpeed DefaultRoamSpeed;
        private const string ADMIN_PERMISSION = "zombiehorde.admin";
        private const string IGNORE_PERMISSION = "zombiehorde.ignore";
#region Oxide Hooks
        private void OnServerInitialized() {
            Instance = this;
            permission.RegisterPermission(ADMIN_PERMISSION, this);
            permission.RegisterPermission(IGNORE_PERMISSION, this);
            if (!Configuration.Member.TargetedByPeaceKeeperTurrets) Unsubscribe(nameof(CanEntityBeHostile));
            if (Configuration.Member.TargetedByAPC) Unsubscribe(nameof(CanBradleyApcTarget));
            DefaultRoamSpeed = ParseType<BaseNavigator.NavigationSpeed>(Configuration.Horde.DefaultRoamSpeed);
            ValidateLoadoutProfiles();
            ValidateSpawnSystem();
            CreateMonumentHordeOrders();
        }
        private void OnEntityTakeDamage(BaseCombatEntity baseCombatEntity, HitInfo hitInfo) {
            if (hitInfo != null) {
                if (hitInfo.InitiatorPlayer is ZombieNPC) {
                    if (hitInfo.damageTypes.Get(DamageType.Explosion) > 0) {
                        hitInfo.damageTypes.ScaleAll(ConVar.Halloween.scarecrow_beancan_vs_player_dmg_modifier);
                        return;
                    }
                    float damageMultiplier = (hitInfo.InitiatorPlayer as ZombieNPC).Loadout.DamageMultiplier;
                    if (damageMultiplier != 1f) hitInfo.damageTypes.ScaleAll(damageMultiplier);
                }
            }
        }
        private void OnPlayerDeath(BasePlayer player, HitInfo hitInfo) {
            if (player == null || hitInfo == null) return;
            if (player is ZombieNPC) {
                (player as ZombieNPC).Horde.OnMemberKilled((player as ZombieNPC), hitInfo.Initiator);
                return;
            }
            if (Configuration.Horde.CreateOnDeath && hitInfo.InitiatorPlayer is ZombieNPC) (hitInfo.InitiatorPlayer as ZombieNPC).Horde.OnPlayerKilled(player);
        }
        private void OnEntityKill(ZombieNPC zombieNPC) {
            if (zombieNPC != null && zombieNPC.Horde != null && !zombieNPC.Horde.isDespawning) zombieNPC.Horde.OnMemberKilled(zombieNPC, null);
        }
        private object CanBeTargeted(ZombieNPC zombieNPC, GunTrap gunTrap) => Configuration.Member.TargetedByTurrets ? null : (object)false;
        private object CanBeTargeted(ZombieNPC zombieNPC, FlameTurret flameTurret) => Configuration.Member.TargetedByTurrets ? null : (object)false;
        private object CanBeTargeted(ZombieNPC zombieNPC, AutoTurret autoTurret) {
            if (Configuration.Member.TargetedByTurrets) return null;
            if ((autoTurret.PeacekeeperMode() || autoTurret is NPCAutoTurret) && Configuration.Member.TargetedByPeaceKeeperTurrets) return null;
            return false;
        }
        private object CanEntityBeHostile(ZombieNPC zombieNPC) => true;
        private object CanBradleyApcTarget(BradleyAPC bradleyAPC, ZombieNPC zombieNPC) => false;
        private object OnNpcTarget(NPCPlayer npcPlayer, ZombieNPC zombieNPC) => Configuration.Member.TargetedByNPCs ? null : (object)true;
        private object OnNpcTarget(BaseNpc baseNpc, ZombieNPC zombieNPC) => Configuration.Member.TargetedByAnimals ? null : (object)true;
        private void Unload() {
            Horde.SpawnOrder.OnUnload();
            for (int i = Horde.AllHordes.Count - 1; i >= 0; i--) Horde.AllHordes[i].Destroy(true, true);
            Horde.AllHordes.Clear();
            ZombieNPC[] zombies = UnityEngine.Object.FindObjectsOfType<ZombieNPC>();
            for (int i = 0; i < zombies?.Length; i++) zombies[i].Kill(BaseNetworkable.DestroyMode.None);
            Configuration = null;
            Instance = null;
        }
#endregion
#region Sensations
        private void OnEntityKill(TimedExplosive timedExplosive) {
            if (!Configuration.Horde.UseSenses) return;
            Sense.Stimulate(new Sensation() {
                Type = SensationType.Gunshot, Position = timedExplosive.transform.position, Radius = 80f,
            }
            );
        }
        private void OnEntityKill(TreeEntity treeEntity) {
            if (!Configuration.Horde.UseSenses) return;
            Sense.Stimulate(new Sensation() {
                Type = SensationType.Gunshot, Position = treeEntity.transform.position, Radius = 30f,
            }
            );
        }
        private void OnEntityKill(OreResourceEntity oreResourceEntity) {
            if (!Configuration.Horde.UseSenses) return;
            Sense.Stimulate(new Sensation() {
                Type = SensationType.Gunshot, Position = oreResourceEntity.transform.position, Radius = 30f,
            }
            );
        }
        private void OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item) {
            if (!Configuration.Horde.UseSenses) return;
            Sense.Stimulate(new Sensation() {
                Type = SensationType.Gunshot, Position = dispenser.transform.position, Radius = 20f
            }
            );
        }
#endregion
#region Functions
        private T ParseType<T>(string type) {
            try {
                return (T)Enum.Parse(typeof(T), type, true);
            }
            catch {
                return default(T);
            }
        }
#region Horde Spawning
        private List<Vector3> _spawnPoints;
        private SpawnSystem _spawnSystem = SpawnSystem.None;
        private const int SPAWN_RAYCAST_MASK = 1 << 0 | 1 << 8 | 1 << 15 | 1 << 17 | 1 << 21 | 1 << 29;
        private const TerrainTopology.Enum SPAWN_TOPOLOGY_MASK = (TerrainTopology.Enum.Ocean | TerrainTopology.Enum.River | TerrainTopology.Enum.Lake | TerrainTopology.Enum.Cliff | TerrainTopology.Enum.Cliffside | TerrainTopology.Enum.Offshore | TerrainTopology.Enum.Summit | TerrainTopology.Enum.Decor);
        private static bool ContainsTopologyAtPoint(TerrainTopology.Enum mask, Vector3 position) => (TerrainMeta.TopologyMap.GetTopology(position, 1f) & (int)mask) != 0;
        private bool ValidateSpawnSystem() {
            _spawnSystem = ParseType<SpawnSystem>(Configuration.Horde.SpawnType);
            if (_spawnSystem == SpawnSystem.None) {
                PrintError("You have set an invalid value in the config entry \"Spawn Type\".Unable to spawn hordes!");
                return false;
            }
            else if (_spawnSystem == SpawnSystem.SpawnsDatabase) {
                if (Spawns != null) {
                    if (string.IsNullOrEmpty(Configuration.Horde.SpawnFile)) {
                        PrintError("You have selected SpawnsDatabase as your method of spawning hordes, however you have not specified a spawn file.Unable to spawn hordes!");
                        return false;
                    }
                    object success = Spawns?.Call("LoadSpawnFile", Configuration.Horde.SpawnFile);
                    if (success is List<Vector3>) {
                        _spawnPoints = success as List<Vector3>;
                        if (_spawnPoints.Count > 0) return true;
                    }
                    PrintError("You have selected SpawnsDatabase as your method of spawning hordes, however the spawn file you have chosen is either invalid, or has no spawn points.Unable to spawn hordes!");
                    return false;
                }
                else PrintError("You have selected SpawnsDatabase as your method of spawning hordes, however SpawnsDatabase is not loaded on your server.Unable to spawn hordes!");
                return false;
            }
            return true;
        }
        private Vector3 GetSpawnPoint() {
            switch (_spawnSystem) {
                case SpawnSystem.None : break;
                case SpawnSystem.SpawnsDatabase : {
                    if (Spawns == null) {
                        PrintError("Tried getting a spawn point but SpawnsDatabase is null.Make sure SpawnsDatabase is still loaded to continue using custom spawn points");
                        break;
                    }
                    if (_spawnPoints == null || _spawnPoints.Count == 0) {
                        PrintError("No spawnpoints have been loaded from the designated spawnfile.Defaulting to Rust spawns");
                        break;
                    }
                    Vector3 spawnPoint = _spawnPoints.GetRandom();
                    _spawnPoints.Remove(spawnPoint);
                    if (_spawnPoints.Count == 0) _spawnPoints = (List<Vector3>)Spawns.Call("LoadSpawnFile", Configuration.Horde.SpawnFile);
                    return spawnPoint;
                }
            }
            float size = (World.Size / 2f) * 0.75f;
            for (int i = 0; i < 10; i++) {
                Vector2 randomInCircle = Random.insideUnitCircle * size;
                Vector3 position = new Vector3(randomInCircle.x, 0, randomInCircle.y);
                position.y = TerrainMeta.HeightMap.GetHeight(position);
                if (NavmeshSpawnPoint.Find(position, 25f, out position)) {
                    if (Physics.SphereCast(new Ray(position + (Vector3.up * 5f), Vector3.down), 10f, 10f, SPAWN_RAYCAST_MASK)) continue;
                    if (ContainsTopologyAtPoint(SPAWN_TOPOLOGY_MASK, position)) continue;
                    if (WaterLevel.GetWaterDepth(position, true, null) <= 0.01f) return position;
                }
            }
            return ServerMgr.FindSpawnPoint().pos;
        }
        private void CreateMonumentHordeOrders() {
            int count = 0;
            GameObject[] allobjects = UnityEngine.Object.FindObjectsOfType<GameObject>();
            foreach (GameObject gobject in allobjects) {
                if (count >= Configuration.Horde.MaximumHordes) break;
                if (gobject.name.Contains("autospawn/monument")) {
                    Transform tr = gobject.transform;
                    Vector3 position = tr.position;
                    if (position == Vector3.zero) continue;
                    if (gobject.name.Contains("powerplant_1") && Configuration.Monument.Powerplant.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-30.8f, 0.2f, -15.8f)), Configuration.Monument.Powerplant);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("military_tunnel_1") && Configuration.Monument.Tunnels.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-7.4f, 13.4f, 53.8f)), Configuration.Monument.Tunnels);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("harbor_1") && Configuration.Monument.LargeHarbor.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(54.7f, 5.1f, -39.6f)), Configuration.Monument.LargeHarbor);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("harbor_2") && Configuration.Monument.SmallHarbor.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-66.6f, 4.9f, 16.2f)), Configuration.Monument.SmallHarbor);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("airfield_1") && Configuration.Monument.Airfield.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-12.4f, 0.2f, -28.9f)), Configuration.Monument.Airfield);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("trainyard_1") && Configuration.Monument.Trainyard.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(35.8f, 0.2f, -0.8f)), Configuration.Monument.Trainyard);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("water_treatment_plant_1") && Configuration.Monument.WaterTreatment.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(11.1f, 0.3f, -80.2f)), Configuration.Monument.WaterTreatment);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("warehouse") && Configuration.Monument.Warehouse.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(16.6f, 0.1f, -7.5f)), Configuration.Monument.Warehouse);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("satellite_dish") && Configuration.Monument.Satellite.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(18.6f, 6.0f, -7.5f)), Configuration.Monument.Satellite);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("sphere_tank") && Configuration.Monument.Dome.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-44.6f, 5.8f, -3.0f)), Configuration.Monument.Dome);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("radtown_small_3") && Configuration.Monument.Radtown.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-16.3f, -2.1f, -3.3f)), Configuration.Monument.Radtown);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("launch_site_1") && Configuration.Monument.LaunchSite.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(222.1f, 3.3f, 0.0f)), Configuration.Monument.LaunchSite);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("gas_station_1") && Configuration.Monument.GasStation.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-9.8f, 3.0f, 7.2f)), Configuration.Monument.GasStation);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("supermarket_1") && Configuration.Monument.Supermarket.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(5.5f, 0.0f, -20.5f)), Configuration.Monument.Supermarket);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("mining_quarry_c") && Configuration.Monument.HQMQuarry.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(15.8f, 4.5f, -1.5f)), Configuration.Monument.HQMQuarry);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("mining_quarry_a") && Configuration.Monument.SulfurQuarry.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-0.8f, 0.6f, 11.4f)), Configuration.Monument.SulfurQuarry);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("mining_quarry_b") && Configuration.Monument.StoneQuarry.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-7.6f, 0.2f, 12.3f)), Configuration.Monument.StoneQuarry);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("junkyard_1") && Configuration.Monument.Junkyard.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-16.7f, 0.2f, 1.4f)), Configuration.Monument.Junkyard);
                        count++;
                        continue;
                    }
                }
            }
            foreach(ConfigData.MonumentSpawn.CustomSpawnPoints customSpawnPoint in Configuration.Monument.Custom) {
                if (customSpawnPoint.Enabled) {
                    Horde.SpawnOrder.Create(customSpawnPoint.Location, Configuration.Horde.InitialMemberCount, customSpawnPoint.HordeSize, customSpawnPoint.RoamDistance, customSpawnPoint.Profile);
                    count++;
                }
            }
            if (count < Configuration.Horde.MaximumHordes) CreateRandomHordes();
        }
        private void CreateRandomHordes() {
            int amountToCreate = Configuration.Horde.MaximumHordes - Horde.AllHordes.Count;
            for (int i = 0; i < amountToCreate; i++) {
                float roamDistance = Configuration.Horde.LocalRoam ? Configuration.Horde.RoamDistance : -1;
                string profile = Configuration.Horde.UseProfiles ? Configuration.HordeProfiles.Keys.ToArray().GetRandom() : string.Empty;
                Horde.SpawnOrder.Create(GetSpawnPoint(), Configuration.Horde.InitialMemberCount, Configuration.Horde.MaximumMemberCount, roamDistance, profile);
            }
        }
#endregion
#region Loadouts
        private static LootContainer.LootSpawnSlot[] _defaultLootSpawns;
        private static PlayerInventoryProperties[] _defaultLoadouts;
        private static LootContainer.LootSpawnSlot[] DefaultLootSpawns {
            get {
                if (_defaultLootSpawns == null) {
                    ScarecrowNPC scarecrowNPC = GameManager.server.FindPrefab("assets/prefabs/npc/scarecrow/scarecrow.prefab").GetComponent<ScarecrowNPC>();
                    _defaultLootSpawns = scarecrowNPC.LootSpawnSlots;
                    _defaultLoadouts = scarecrowNPC.loadouts;
                }
                return _defaultLootSpawns;
            }
        }
        private static PlayerInventoryProperties[] DefaultLoadouts {
            get {
                if (_defaultLoadouts == null) {
                    ScarecrowNPC scarecrowNPC = GameManager.server.FindPrefab("assets/prefabs/npc/scarecrow/scarecrow.prefab").GetComponent<ScarecrowNPC>();
                    _defaultLootSpawns = scarecrowNPC.LootSpawnSlots;
                    _defaultLoadouts = scarecrowNPC.loadouts;
                }
                return _defaultLoadouts;
            }
        }
        private void ValidateLoadoutProfiles() {
            Puts("Validating horde profiles...");
            bool hasChanged = false;
            for (int i = Configuration.HordeProfiles.Count - 1; i >= 0; i--) {
                string key = Configuration.HordeProfiles.ElementAt(i).Key;
                for (int y = Configuration.HordeProfiles[key].Count - 1; y >= 0; y--) {
                    string loadoutId = Configuration.HordeProfiles[key][y];
                    if (!Configuration.Member.Loadouts.Any(x => x.LoadoutID == loadoutId)) {
                        Puts($"Loadout profile {loadoutId} does not exist.Removing from config");
                        Configuration.HordeProfiles[key].Remove(loadoutId);
                        hasChanged = true;
                    }
                }
                if (Configuration.HordeProfiles[key].Count <= 0) {
                    Puts($"Horde profile {key} does not have any valid loadouts.Removing from config");
                    Configuration.HordeProfiles.Remove(key);
                    hasChanged = true;
                }
            }
            if (hasChanged) SaveConfig();
        }
#endregion
#region Spawning
        private static ZombieNPC _reference;
        private static ZombieNPC Reference {
            get {
                if (_reference == null) {
                    const string SCIENTIST_PREFAB = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_heavy.prefab";
                    GameObject gameObject = Instantiate.GameObject(GameManager.server.FindPrefab(SCIENTIST_PREFAB), Vector3.zero, Quaternion.identity);
                    gameObject.name = SCIENTIST_PREFAB;
                    gameObject.SetActive(false);
                    ScientistNPC scientistNPC = gameObject.GetComponent<ScientistNPC>();
                    ScientistBrain scientistBrain = gameObject.GetComponent<ScientistBrain>();
                    NPCPlayerNavigator scientistNavigator = gameObject.GetComponent<NPCPlayerNavigator>();
                    ZombieNPC zombieNPC = gameObject.AddComponent<ZombieNPC>();
                    ZombieNPCBrain zombieNPCBrain = gameObject.AddComponent<ZombieNPCBrain>();
                    ZombieNavigator zombieNavigator = gameObject.AddComponent<ZombieNavigator>();
                    CopySerializeableFields<NPCPlayer>(scientistNPC, zombieNPC);
                    CopySerializeableFields<NPCPlayerNavigator>(scientistNavigator, zombieNavigator);
                    zombieNPC.enableSaving = false;
                    zombieNPCBrain.UseQueuedMovementUpdates = scientistBrain.UseQueuedMovementUpdates;
                    zombieNPCBrain.AllowedToSleep = Configuration.Member.EnableDormantSystem;
                    zombieNPCBrain.DefaultDesignSO = scientistBrain.DefaultDesignSO;
                    zombieNPCBrain.Designs = new List<AIDesignSO>(scientistBrain.Designs);
                    zombieNPCBrain.InstanceSpecificDesign = scientistBrain.InstanceSpecificDesign;
                    zombieNPCBrain.CheckLOS = scientistBrain.CheckLOS;
                    zombieNPCBrain.UseAIDesign = true;
                    zombieNPCBrain.Pet = false;
                    scientistBrain._baseEntity = scientistNPC;
                    UnityEngine.Object.DestroyImmediate(scientistNavigator);
                    UnityEngine.Object.DestroyImmediate(scientistBrain, true);
                    UnityEngine.Object.DestroyImmediate(scientistNPC, true);
                    _reference = zombieNPC;
                }
                return _reference;
            }
        }
        private static ZombieNPC InstantiateEntity(Vector3 position) {
            ZombieNPC zombieNPC = UnityEngine.Object.Instantiate<ZombieNPC>(Reference, position, Quaternion.identity);
            zombieNPC.name = Reference.name;
            UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(zombieNPC.gameObject, Rust.Server.EntityScene);
            zombieNPC.gameObject.SetActive(true);
            return zombieNPC;
        }
        private static void CopySerializeableFields<T>(T src, T dst) {
            FieldInfo[] srcFields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo field in srcFields) {
                object value = field.GetValue(src);
                field.SetValue(dst, value);
            }
        }
        private static class NavmeshSpawnPoint {
            private static NavMeshHit navmeshHit;
            private static RaycastHit raycastHit;
            private static readonly Collider[] _buffer = new Collider[256];
            private const int WORLD_LAYER = 65536;
            public static bool Find(Vector3 targetPosition, float maxDistance, out Vector3 position) {
                for (int i = 0; i < 10; i++) {
                    position = i == 0 ? targetPosition : targetPosition + (Random.onUnitSphere * maxDistance);
                    if (NavMesh.SamplePosition(position, out navmeshHit, maxDistance, 1)) {
                        if (IsInRockPrefab(navmeshHit.position)) continue;
                        if (IsNearWorldCollider(navmeshHit.position)) continue;
                        if (navmeshHit.position.y < TerrainMeta.WaterMap.GetHeight(navmeshHit.position)) continue;
                        position = navmeshHit.position;
                        return true;
                    }
                }
                position = default(Vector3);
                return false;
            }
            private static bool IsInRockPrefab(Vector3 position) {
                Physics.queriesHitBackfaces = true;
                bool isInRock = Physics.Raycast(position, Vector3.up, out raycastHit, 20f, WORLD_LAYER, QueryTriggerInteraction.Ignore) && BLOCKED_COLLIDERS.Any(s => raycastHit.collider?.gameObject?.name.Contains(s, CompareOptions.OrdinalIgnoreCase) ?? false);
                Physics.queriesHitBackfaces = false;
                return isInRock;
            }
            private static bool IsNearWorldCollider(Vector3 position) {
                Physics.queriesHitBackfaces = true;
                int count = Physics.OverlapSphereNonAlloc(position, 2f, _buffer, WORLD_LAYER, QueryTriggerInteraction.Ignore);
                Physics.queriesHitBackfaces = false;
                int removed = 0;
                for (int i = 0; i < count; i++) {
                    if (ACCEPTED_COLLIDERS.Any(s => _buffer[i].gameObject.name.Contains(s, CompareOptions.OrdinalIgnoreCase))) removed++;
                }
                return count - removed > 0;
            }
            private static readonly string[] ACCEPTED_COLLIDERS = new string[] {
                "road", "carpark", "rocket_factory", "range", "train_track", "runway", "_grounds", "concrete_slabs", "lighthouse", "cave", "office", "walkways", "sphere", "tunnel", "industrial", "junkyard"
            }
            ;
            private static readonly string[] BLOCKED_COLLIDERS = new string[] {
                "rock", "cliff", "junk", "range", "invisible"
            }
            ;
        }
#endregion
#endregion
        public class Horde {
            public static List<Horde> AllHordes = new List<Horde>();
            private static readonly Spatial.Grid<Horde> HordeGrid = new Spatial.Grid<Horde>(32, 8096f);
            private static readonly Horde[] HordeGridQueryResults = new Horde[4];
            private static readonly BasePlayer[] PlayerVicinityQueryResults = new BasePlayer[32];
            public List<ZombieNPC> members;
            //public bool DebugMode = false;
            public readonly Vector3 InitialPosition;
            public readonly bool IsLocalHorde;
            public readonly float MaximumRoamDistance;
            private readonly int initialMemberCount;
            private readonly int maximumMemberCount;
            private readonly string hordeProfile;
            private float nextUpdateTime;
            private float nextSeperationCheckTime;
            private float nextGrowthTime;
            private float nextMergeTime;
            private float nextSleepTime;
            internal bool isDespawning;
            private const float HORDE_UPDATE_RATE = 1f;
            private const float SEPERATION_CHECK_RATE = 10f;
            private const float MERGE_CHECK_RATE = 10f;
            private const float SLEEP_CHECK_RATE = 5f;
            public ZombieNPC Leader {
                get;
                private set;
            }
            public bool IsSleeping {
                get;
                private set;
            }
            public Vector3 CentralLocation {
                get;
                private set;
            }
            public bool HordeOnAlert {
                get;
                private set;
            }
            public int MemberCount => members.Count;
            public static bool Create(SpawnOrder spawnOrder) {
                Horde horde = new Horde(spawnOrder);
                for (int i = 0; i < spawnOrder.InitialMemberCount; i++) horde.SpawnMember(spawnOrder.Position);
                if (horde.members.Count == 0) {
                    horde.Destroy();
                    return false;
                }
                AllHordes.Add(horde);
                horde.CentralLocation = horde.CalculateCentralLocation();
                HordeGrid.Add(horde, horde.CentralLocation.x, horde.CentralLocation.z);
                return true;
            }
            public Horde(SpawnOrder spawnOrder) {
                members = Pool.GetList<ZombieNPC>();
                InitialPosition = CentralLocation = spawnOrder.Position;
                IsLocalHorde = spawnOrder.MaximumRoamDistance > 0;
                MaximumRoamDistance = spawnOrder.MaximumRoamDistance;
                initialMemberCount = spawnOrder.InitialMemberCount;
                maximumMemberCount = spawnOrder.MaximumMemberCount;
                hordeProfile = spawnOrder.HordeProfile;
                nextSeperationCheckTime = Time.time + SEPERATION_CHECK_RATE;
                nextGrowthTime = Time.time + Configuration.Horde.GrowthRate;
                nextMergeTime = Time.time + MERGE_CHECK_RATE;
                nextSleepTime = Time.time + SLEEP_CHECK_RATE + Random.Range(1f, 5f);
            }
            public void Update() {
                if (members == null || members.Count == 0) return;
                if (Time.time < nextUpdateTime) return;
                nextUpdateTime = Time.time + HORDE_UPDATE_RATE;
                CentralLocation = CalculateCentralLocation();
                if (Configuration.Member.EnableDormantSystem) DoSleepChecks();
                //if (DebugMode)
                //{
                //    Debug.Log($"Horde Update ({CentralLocation}).Sleeping? {IsSleeping}");
                //    for (int i = 0; i < members.Count; i++)
                //    {
                //        ZombieNPC zombieNPC = members[i];
                //        Debug.Log($"Member #{i} state {zombieNPC.Brain.CurrentState.StateType} (Is Leader? {zombieNPC.IsGroupLeader}");
                //    }
                //}
                if (IsSleeping) return;
                HordeGrid.Move(this, CentralLocation.x, CentralLocation.z);
                TryMergeHordes();
                TryGrowHorde();
                TryCongregateHorde();
                MoveRoamersTowardsTarget();
            }
            private void MoveRoamersTowardsTarget() {
                BaseEntity target;
                HordeOnAlert = AnyHasTarget(out target);
                if (Leader != null && !Leader.IsDestroyed && target != null && !target.IsDestroyed) SetLeaderRoamTarget(target.transform.position);
            }
            private void TryCongregateHorde() {
                if (Time.time > nextSeperationCheckTime) {
                    nextSeperationCheckTime = Time.time + SEPERATION_CHECK_RATE;
                    if (GetLargestSeperation() > 30f) {
                        //if (DebugMode)
                        //    Debug.Log($"Horde is seperated, attempt re-group.Leader state {Leader.CurrentState}");
                        if (Leader.CurrentState <= AIState.Roam) SetLeaderRoamTarget(CentralLocation);
                        for (int i = 0; i < members.Count; i++) members[i].IsAlert = true;
                    }
                }
            }
            public void RegisterInterestInTarget(ZombieNPC interestedMember, BaseEntity baseEntity) {
                if (baseEntity == null || members == null) return;
                //if (DebugMode)
                //    Debug.Log($"Register interest in {baseEntity.ShortPrefabName} ({baseEntity.transform.position}).");
                for (int i = 0; i < members.Count; i++) {
                    ZombieNPC hordeMember = members[i];
                    if (hordeMember == null || hordeMember.IsDestroyed || interestedMember == hordeMember) continue;
                    hordeMember.Brain.Senses.Memory.SetKnown(baseEntity, hordeMember, null);
                }
                if (Leader != null && !Leader.IsDestroyed && !Leader.HasTarget) SetLeaderRoamTarget(baseEntity.transform.position);
            }
            public bool HasTarget() {
                if (members != null) {
                    for (int i = 0; i < members.Count; i++) {
                        ZombieNPC hordeMember = members[i];
                        if (hordeMember.HasTarget) return true;
                    }
                }
                return false;
            }
            public bool AnyHasTarget(out BaseEntity target) {
                if (members != null) {
                    for (int i = 0; i < members.Count; i++) {
                        ZombieNPC hordeMember = members[i];
                        if (hordeMember.HasTarget) {
                            target = hordeMember.CurrentTarget;
                            return true;
                        }
                    }
                }
                target = null;
                return false;
            }
            public void ResetRoamTarget() {
                if (members == null) return;
                //if (DebugMode)
                //    Debug.Log("Reset roam target");
                for (int i = 0; i < members.Count; i++) {
                    ZombieNPC hordeMember = members[i];
                    if (hordeMember.IsGroupLeader) continue;
                    hordeMember.ResetRoamState();
                }
            }
            public void SetLeaderRoamTarget(Vector3 position) {
                //if (DebugMode)
                //    Debug.Log($"Set leader roam target {position}");
                if (Leader != null && !Leader.IsDestroyed) Leader.SetRoamTargetOverride(position);
                ResetRoamTarget();
            }
            public Vector3 GetLeaderDestination() {
                if (Leader == null || Leader.IsDestroyed || Leader.Brain == null || Leader.Brain.Events == null) return CentralLocation;
                return Leader.Brain.Navigator.Destination;
            }
            public void OnMemberKilled(ZombieNPC zombieNPC, BaseEntity initiator) {
                if (zombieNPC == null) return;
                if (members == null || !members.Contains(zombieNPC)) return;
                members.Remove(zombieNPC);
                if (members.Count == 0) Destroy();
                else {
                    if (zombieNPC.IsGroupLeader) {
                        Leader = members.GetRandom();
                        Leader.IsGroupLeader = true;
                    }
                    if ((initiator is BasePlayer && Leader.CanTargetBasePlayer(initiator as BasePlayer)) || (initiator is BaseNpc && Leader.CanTargetEntity(initiator))) RegisterInterestInTarget(null, initiator);
                }
            }
            public void OnPlayerKilled(BasePlayer player) {
                if (Configuration.Horde.CreateOnDeath && MemberCount < maximumMemberCount) {
                    Vector3 position;
                    if (NavmeshSpawnPoint.Find(player.transform.position, 10f, out position)) SpawnMember(position);
                }
            }
            public void SpawnMember(Vector3 position) {
                ZombieNPC zombieNPC = InstantiateEntity(position);
                zombieNPC.SetHordeProfile(hordeProfile);
                zombieNPC.Horde = this;
                zombieNPC.Spawn();
                members.Add(zombieNPC);
                if (members.Count == 1) {
                    Leader = zombieNPC;
                    Leader.IsGroupLeader = true;
                }
                else zombieNPC.Invoke(zombieNPC.OnInitialSpawn, 1f);
            }
            public void Destroy(bool permanent = false, bool killNpcs = true) {
                isDespawning = true;
                if (killNpcs) {
                    for (int i = members.Count - 1; i >= 0; i--) {
                        ZombieNPC zombieNPC = members[i];
                        if (zombieNPC != null && !zombieNPC.IsDestroyed) zombieNPC.Kill();
                    }
                }
                members.Clear();
                Pool.FreeList(ref members);
                HordeGrid.Remove(this);
                AllHordes.Remove(this);
                if (!permanent && AllHordes.Count <= Configuration.Horde.MaximumHordes) Instance.timer.In(Configuration.Horde.RespawnTime, () => SpawnOrder.Create(this));
            }
            private Vector3 CalculateCentralLocation() {
                Vector3 location = Vector3.zero;
                if (members == null || members.Count == 0) return location;
                int count = 0;
                for (int i = 0; i < members.Count; i++) {
                    ZombieNPC zombieNPC = members[i];
                    if (zombieNPC == null || zombieNPC.IsDestroyed) continue;
                    location += zombieNPC.Transform.position;
                    count++;
                }
                return location /= count;
            }
            private float GetLargestSeperation() {
                float distance = 0;
                if (members != null) {
                    for (int i = 0; i < members.Count; i++) {
                        ZombieNPC zombieNPC = members[i];
                        if (zombieNPC != null && !zombieNPC.IsDestroyed) {
                            float d = Vector3.Distance(zombieNPC.Transform.position, CentralLocation);
                            if (d > distance) distance = d;
                        }
                    }
                }
                return distance;
            }
            private void TryGrowHorde() {
                if (Configuration.Horde.GrowthRate <= 0 || nextGrowthTime < Time.time) {
                    if (MemberCount < maximumMemberCount) SpawnMember(members.GetRandom().Transform.position);
                    nextGrowthTime = Time.time + Configuration.Horde.GrowthRate;
                }
            }
#region Horde Merging
            private static bool HordeMergeQuery(Horde horde) => horde.MemberCount < horde.maximumMemberCount;
            private void TryMergeHordes() {
                if (!Configuration.Horde.MergeHordes || nextMergeTime > Time.time) return;
                nextMergeTime = Time.time + MERGE_CHECK_RATE;
                if (members == null || MemberCount >= maximumMemberCount) return;
                int results = HordeGrid.Query(CentralLocation.x, CentralLocation.z, 30f, HordeGridQueryResults, HordeMergeQuery);
                if (results > 1) {
                    int amountToMerge = maximumMemberCount - members.Count;
                    for (int i = 0; i < results; i++) {
                        Horde otherHorde = HordeGridQueryResults[i];
                        if (otherHorde == this) continue;
                        if (MemberCount >= maximumMemberCount || otherHorde.members == null) break;
                        if (amountToMerge >= otherHorde.members.Count) {
                            for (int y = 0; y < otherHorde.members.Count; y++) {
                                ZombieNPC zombieNPC = otherHorde.members[y];
                                members.Add(zombieNPC);
                                zombieNPC.Horde = this;
                                zombieNPC.OnInitialSpawn();
                            }
                            otherHorde.members.Clear();
                            otherHorde.Destroy();
                        }
                        else {
                            for (int y = 0; y < amountToMerge; y++) {
                                if (otherHorde.members.Count > 0) {
                                    ZombieNPC zombieNPC = otherHorde.members[otherHorde.MemberCount - 1];
                                    members.Add(zombieNPC);
                                    zombieNPC.Horde = this;
                                    zombieNPC.OnInitialSpawn();
                                    otherHorde.members.Remove(zombieNPC);
                                }
                            }
                        }
                    }
                }
            }
#endregion
#region Sleeping
            private void DoSleepChecks() {
                if (Time.time >= nextSleepTime) {
                    nextSleepTime = Time.time + SLEEP_CHECK_RATE + Random.Range(1f, 5f);
                    int count = BaseEntity.Query.Server.GetPlayersInSphere(CentralLocation, AiManager.ai_to_player_distance_wakeup_range, PlayerVicinityQueryResults, HordeSleepPlayerFilter);
                    if (count > 0) {
                        if (IsSleeping) SetSleeping(false);
                    }
                    else {
                        if (!IsSleeping) SetSleeping(true);
                    }
                }
            }
            private void SetSleeping(bool sleep) {
                if (members == null) return;
                for (int i = 0; i < members.Count; i++) {
                    ZombieNPC zombieNPC = members[i];
                    if (zombieNPC == null || zombieNPC.Brain == null) continue;
                    if (zombieNPC.Brain.sleeping == sleep) continue;
                    zombieNPC.Brain.SetSleeping(sleep);
                }
                IsSleeping = sleep;
            }
            public void ForceWakeFromSleep() {
                if (!IsSleeping) return;
                SetSleeping(false);
            }
            private static bool HordeSleepPlayerFilter(BaseEntity entity) {
                BasePlayer basePlayer = entity as BasePlayer;
                if (basePlayer == null || !basePlayer.IsConnected) return false;
                if (basePlayer is ZombieNPC) return false;
                if (Configuration.Member.IgnoreSleepers && basePlayer.IsSleeping()) return false;
                return true;
            }
#endregion
            public int GetMemberIndex(ZombieNPC zombieNPC) => members.IndexOf(zombieNPC);
            public class SpawnOrder {
                public Vector3 Position {
                    get;
                    private set;
                }
                public int InitialMemberCount {
                    get;
                    private set;
                }
                public int MaximumMemberCount {
                    get;
                    private set;
                }
                public float MaximumRoamDistance {
                    get;
                    private set;
                }
                public string HordeProfile {
                    get;
                    private set;
                }
                public SpawnOrder(Vector3 position, int initialMemberCount, int maximumMemberCount, float maximumRoamDistance, string hordeProfile) {
                    this.Position = position;
                    this.InitialMemberCount = initialMemberCount;
                    this.MaximumMemberCount = maximumMemberCount;
                    this.MaximumRoamDistance = maximumRoamDistance;
                    this.HordeProfile = hordeProfile;
                }
#region Static
                private static Queue<SpawnOrder> _spawnOrders = new Queue<SpawnOrder>();
                private static Coroutine _spawnRoutine;
                private static Coroutine _despawnRoutine;
                private static bool _isSpawning;
                private static bool _isDespawning;
                public static SpawnState State;
                static SpawnOrder() {
                    State = Configuration.TimedSpawns.Enabled ? (ShouldSpawn() ? SpawnState.Spawn : SpawnState.Despawn) : SpawnState.Spawn;
                    if (Configuration.TimedSpawns.Enabled) StartTimer();
                }
                internal static void Create(Vector3 position, int initialMemberCount, int maximumMemberCount, float maximumRoamDistance, string hordeProfile) {
                    if (NavmeshSpawnPoint.Find(position, 10f, out position)) {
                        _spawnOrders.Enqueue(new SpawnOrder(position, initialMemberCount, maximumMemberCount, maximumRoamDistance, hordeProfile));
                        if (!_isSpawning && State == SpawnState.Spawn) DequeueAndSpawn();
                    }
                }
                internal static void Create(Horde horde) {
                    Vector3 position;
                    if (NavmeshSpawnPoint.Find(horde.IsLocalHorde ? horde.InitialPosition : Instance.GetSpawnPoint(), 10f, out position)) {
                        _spawnOrders.Enqueue(new SpawnOrder(position, horde.initialMemberCount, horde.maximumMemberCount, horde.IsLocalHorde ? horde.MaximumRoamDistance : -1, horde.hordeProfile));
                        if (!_isSpawning && State == SpawnState.Spawn) DequeueAndSpawn();
                    }
                }
                internal static void Create(Vector3 position, ConfigData.MonumentSpawn.MonumentSettings settings) {
                    if (NavmeshSpawnPoint.Find(position, 10f, out position)) {
                        _spawnOrders.Enqueue(new SpawnOrder(position, Configuration.Horde.InitialMemberCount, settings.HordeSize, settings.RoamDistance, settings.Profile));
                        if (!_isSpawning && State == SpawnState.Spawn) DequeueAndSpawn();
                    }
                }
                private static void DequeueAndSpawn() {
                    _spawnRoutine = ServerMgr.Instance.StartCoroutine(ProcessSpawnOrders());
                }
                private static void QueueAndDespawn() {
                    _despawnRoutine = ServerMgr.Instance.StartCoroutine(ProcessDespawn());
                }
                internal static void StopSpawning() {
                    if (_spawnRoutine != null) ServerMgr.Instance.StopCoroutine(_spawnRoutine);
                    _isSpawning = false;
                }
                internal static void StopDespawning() {
                    if (_despawnRoutine != null) ServerMgr.Instance.StopCoroutine(_despawnRoutine);
                    _isDespawning = false;
                }
                private static IEnumerator ProcessSpawnOrders() {
                    if (_spawnOrders.Count == 0) yield break;
                    _isSpawning = true;
                    RESTART : if (_isDespawning) StopDespawning();
                    while (AllHordes.Count > Configuration.Horde.MaximumHordes) yield return CoroutineEx.waitForSeconds(10f);
                    SpawnOrder spawnOrder = _spawnOrders.Dequeue();
                    if (spawnOrder != null) Horde.Create(spawnOrder);
                    if (_spawnOrders.Count > 0) {
                        yield return CoroutineEx.waitForSeconds(3f);
                        goto RESTART;
                    }
                    _isSpawning = false;
                }
                private static IEnumerator ProcessDespawn() {
                    _isDespawning = true;
                    if (_isSpawning) StopSpawning();
         
// ... [Code preview truncated due to length. Full source in attached file] ...

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