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

FREE ZombieHorde : 0.6.27 0.6.27

Нет прав для скачивания
C#:
using Facepunch;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Plugins;
using Oxide.Ext.ChaosNPC;
using Rust;
using Rust.Ai;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using Rust.Ai.Gen2;
using UnityEngine;
using UnityEngine.AI;
using Random = UnityEngine.Random;
namespace Oxide.Plugins {
    [Info("ZombieHorde", "k1lly0u", "0.6.27")]
    class ZombieHorde : RustPlugin, IChaosNPCPlugin {
        [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));
            if (!Configuration.Member.DespawnDudExplosives && !Configuration.Member.ExplodeDudExplosives) Unsubscribe(nameof(OnExplosiveDud));
            DefaultRoamSpeed = ParseType<BaseNavigator.NavigationSpeed>(Configuration.Horde.DefaultRoamSpeed);
            ValidateLoadoutProfiles();
            ValidateSpawnSystem();
            Horde.SpawnOrder.InitializeSpawnOrders();
            CreateMonumentHordeOrders();
        }
        protected override void LoadDefaultMessages() => lang.RegisterMessages(new Dictionary<string, string> {
            ["Notification.BeginSpawn"]
            = "<color=#ce422b>CAUTION!</color> Zombies have been spotted in the area", ["Notification.BeginDespawn"] = "<color=#ce422b>CAUTION!</color> The zombies appear to be dispersing"
        }
        , this);
        private void OnEntityTakeDamage(BaseCombatEntity baseCombatEntity, HitInfo hitInfo) {
            ZombieNPC zombieNpc = hitInfo?.InitiatorPlayer as ZombieNPC;
            if (!zombieNpc || !baseCombatEntity || hitInfo == null) return;
            bool hasExplosionDamage = hitInfo.damageTypes.Get(DamageType.Explosion) > 0;
            if (baseCombatEntity is BuildingBlock or SimpleBuildingBlock or Door) {
                if (hasExplosionDamage) {
                    if (Configuration.Member.IgnoreBuildingMultiplierNotOwner || Configuration.Member.DisableBuildingMultiplierNotOwner) {
                        BasePlayer target = zombieNpc.CurrentTarget as BasePlayer;
                        if (!target) goto SCALE_DAMAGE;
                        if (baseCombatEntity.OwnerID == target.userID) goto SCALE_DAMAGE;
                        BuildingPrivlidge buildingPrivilege = (baseCombatEntity as DecayEntity).GetBuildingPrivilege();
                        if (buildingPrivilege && buildingPrivilege.IsAuthed(target.userID)) goto SCALE_DAMAGE;
                        if (Configuration.Member.DisableBuildingMultiplierNotOwner) hitInfo.damageTypes.Scale(DamageType.Explosion, 0);
                        return;
                    }
                    SCALE_DAMAGE : hitInfo.damageTypes.Scale(DamageType.Explosion, Configuration.Member.ExplosiveBuildingDamageMultiplier);
                    return;
                }
                AttackEntity attackEntity = zombieNpc.GetAttackEntity();
                if (attackEntity is BaseMelee) {
                    hitInfo.damageTypes.ScaleAll(Configuration.Member.MeleeBuildingDamageMultiplier);
                    return;
                }
            }
            if (hasExplosionDamage) {
                if (baseCombatEntity is BasePlayer) {
                    if ((baseCombatEntity as BasePlayer).IsNpc) goto APPLY_MULTIPLIER;
                    hitInfo.damageTypes.ScaleAll(ConVar.Halloween.scarecrow_beancan_vs_player_dmg_modifier);
                    return;
                }
                hitInfo.damageTypes.ScaleAll(0);
                return;
            }
            APPLY_MULTIPLIER : float damageMultiplier = zombieNpc.Loadout.DamageMultiplier;
            if (!Mathf.Approximately(damageMultiplier, 1f)) hitInfo.damageTypes.ScaleAll(damageMultiplier);
        }
        private object OnExplosiveDud(DudTimedExplosive dudTimedExplosive) {
            if (dudTimedExplosive.creatorEntity is ZombieNPC) {
                if (Configuration.Member.ExplodeDudExplosives) return false;
                NextFrame(() => {
                    if (dudTimedExplosive && !dudTimedExplosive.IsDestroyed) dudTimedExplosive.KillMessage();
                }
                );
            }
            return null;
        }
        private void OnPlayerDeath(BasePlayer player, HitInfo hitInfo) {
            if (player == null || hitInfo == null) return;
            if (player is ZombieNPC zombieNpc) {
                zombieNpc.Horde.OnMemberKilled(zombieNpc, hitInfo.Initiator);
                return;
            }
            if (Configuration.Horde.CreateOnDeath && hitInfo.InitiatorPlayer is ZombieNPC initiatorNpc) initiatorNpc.Horde.OnPlayerKilled(player);
        }
        private void OnEntityKill(ZombieNPC zombieNpc) {
            if (zombieNpc != null && zombieNpc.Horde is {
                isDespawning : false
            }
            ) 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 && !(autoTurret is NPCAutoTurret)) return null;
            if ((Configuration.Member.TargetedByPeaceKeeperTurrets || Configuration.Member.TargetedByTurrets) && autoTurret is NPCAutoTurret) {
                if (autoTurret.target == null) autoTurret.SetTarget(zombieNpc);
                return null;
            }
            if (autoTurret.PeacekeeperMode() && 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 && !zombieNpc.Brain.sleeping ? null : (object)true;
        private object OnNpcTarget(BaseNpc baseNpc, ZombieNPC zombieNpc) => Configuration.Member.TargetedByAnimals && !zombieNpc.Brain.sleeping ? null : (object)true;
        private object OnNpcTarget(BaseNPC2 baseNpc, ZombieNPC zombieNpc) => Configuration.Member.TargetedByAnimals && !zombieNpc.Brain.sleeping ? null : (object)true;
        private object OnEntityEnter(TriggerSafeZone triggerSafeZone, ZombieNPC zombieNpc) {
            if (triggerSafeZone && zombieNpc) {
                if (triggerSafeZone.contents != null) triggerSafeZone.contents.Remove(zombieNpc.gameObject);
                zombieNpc.LeaveTrigger(triggerSafeZone);
                return true;
            }
            return null;
        }
        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);
            }
        }
        private static bool IsInOrOnBuilding(BaseEntity baseEntity) {
            if (!baseEntity || baseEntity.IsDestroyed) return false;
            const int CONSTRUCTION_LAYER = 1 << 21;
            Vector3 position = baseEntity.transform.position;
            if (Physics.Raycast(position, Vector3.up, out RaycastHit raycastHit, 40f, CONSTRUCTION_LAYER) || Physics.Raycast(position, Vector3.down, out raycastHit, 20f, CONSTRUCTION_LAYER)) {
                BaseEntity hitEntity = raycastHit.collider.ToBaseEntity();
                return hitEntity is BuildingBlock or SimpleBuildingBlock;
            }
            return false;
        }
        private static Quaternion SafeLookRotation(Vector3 forward, Vector3 upwards) {
            if (forward == Vector3.zero) return Quaternion.identity;
            return Quaternion.LookRotation(forward, upwards);
        }
#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 | TerrainTopology.Enum.Monument);
        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;
            }
            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;
                }
                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, false, 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("ferry_terminal_1") && Configuration.Monument.Ferry.Enabled) {
                        Horde.SpawnOrder.Create(tr.TransformPoint(new Vector3(-6.9f, 5.3f, 6.2f)), Configuration.Monument.Ferry);
                        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 && customSpawnPoint.Location.IsValid) {
                    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 Chaos NPC
        public bool InitializeStates(BaseAIBrain customNpcBrain) {
            customNpcBrain.AddState(new BaseAIBrain.BaseIdleState());
            customNpcBrain.AddState(new ZombieNPCBrain.ZombieChaseState());
            customNpcBrain.AddState(new ZombieNPCBrain.ZombieAttackState());
            customNpcBrain.AddState(new ZombieNPCBrain.ZombieRoamState());
            customNpcBrain.AddState(new ZombieNPCBrain.ZombieMountedState());
            return true;
        }
        public bool WantsToPopulateLoot(CustomScientistNPC customNpc, NPCPlayerCorpse npcplayerCorpse) {
            if (Configuration.Loot.DropDefault) return false;
            ConfigData.LootTable.RandomLoot randomLoot = Configuration.Loot.Random;
            ZombieNPC zombieNpc = customNpc as ZombieNPC;
            if (zombieNpc && zombieNpc.Loadout.LootOverride?.List?.Count > 0) randomLoot = zombieNpc.Loadout.LootOverride;
            int count = Random.Range(randomLoot.Minimum, randomLoot.Maximum);
            int spawnedCount = 0;
            int loopCount = 0;
            while (true) {
                loopCount++;
                if (loopCount > 3) goto EndLootSpawn;
                float probability = Random.Range(0f, 1f);
                List<ConfigData.LootTable.RandomLoot.LootDefinition> definitions = new List<ConfigData.LootTable.RandomLoot.LootDefinition>(randomLoot.List);
                for (int i = 0; i < randomLoot.List.Count; i++) {
                    ConfigData.LootTable.RandomLoot.LootDefinition lootDefinition = definitions.GetRandom();
                    definitions.Remove(lootDefinition);
                    if (lootDefinition.Probability >= probability) {
                        lootDefinition.Create(npcplayerCorpse.containers[0]);
                        spawnedCount++;
                        if (spawnedCount >= count) goto EndLootSpawn;
                    }
                }
            }
            EndLootSpawn : return true;
        }
        private bool ChaosNPC_PreSpawn(ZombieNPC zombieNpc) => false;
        public byte[] GetCustomDesign() => new byte[] {
            8, 1, 8, 3, 8, 15, 8, 2, 8, 6, 18, 75, 8, 0, 16, 1, 26, 12, 8, 17, 16, 4, 24, 0, 32, 4, 40, 3, 48, 0, 26, 12, 8, 14, 16, 1, 24, 0, 32, 0, 40, 0, 48, 1, 26, 25, 8, 0, 16, 3, 24, 0, 32, 0, 40, 0, 48, 2, 162, 6, 10, 13, 0, 0, 0, 0, 21, 0, 0, 0, 0, 26, 12, 8, 3, 16, 1, 24, 0, 32, 4, 40, 0, 48, 3, 32, 0, 18, 158, 1, 8, 1, 16, 3, 26, 12, 8, 17, 16, 4, 24, 0, 32, 4, 40, 3, 48, 0, 26, 12, 8, 2, 16, 0, 24, 0, 32, 0, 40, 0, 48, 1, 26, 12, 8, 4, 16, 0, 24, 0, 32, 0, 40, 0, 48, 2, 26, 12, 8, 5, 16, 2, 24, 0, 32, 0, 40, 0, 48, 3, 26, 12, 8, 18, 16, 0, 24, 0, 32, 0, 40, 0, 48, 4, 26, 12, 8, 15, 16, 2, 24, 0, 32, 0, 40, 0, 48, 5, 26, 29, 8, 12, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 24, 0, 32, 0, 40, 0, 48, 6, 218, 6, 5, 13, 0, 0, 160, 65, 26, 21, 8, 14, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 24, 0, 32, 0, 40, 0, 48, 7, 26, 12, 8, 20, 16, 0, 24, 0, 32, 0, 40, 0, 48, 8, 32, 0, 18, 113, 8, 2, 16, 15, 26, 12, 8, 17, 16, 4, 24, 0, 32, 4, 40, 3, 48, 0, 26, 12, 8, 4, 16, 0, 24, 0, 32, 0, 40, 0, 48, 1, 26, 12, 8, 2, 16, 0, 24, 0, 32, 0, 40, 0, 48, 2, 26, 12, 8, 20, 16, 0, 24, 0, 32, 0, 40, 0, 48, 3, 26, 12, 8, 5, 16, 1, 24, 1, 32, 0, 40, 0, 48, 4, 26, 12, 8, 15, 16, 1, 24, 1, 32, 0, 40, 0, 48, 5, 26, 21, 8, 14, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 24, 0, 32, 0, 40, 0, 48, 6, 32, 0, 18, 62, 8, 3, 16, 2, 26, 12, 8, 4, 16, 0, 24, 0, 32, 0, 40, 0, 48, 0, 26, 12, 8, 2, 16, 0, 24, 0, 32, 0, 40, 0, 48, 1, 26, 12, 8, 14, 16, 1, 24, 0, 32, 0, 40, 0, 48, 2, 26, 12, 8, 3, 16, 1, 24, 0, 32, 4, 40, 0, 48, 3, 32, 0, 18, 48, 8, 4, 16, 6, 26, 12, 8, 17, 16, 0, 24, 1, 32, 4, 40, 3, 48, 0, 26, 12, 8, 2, 16, 0, 24, 0, 32, 0, 40, 0, 48, 1, 26, 12, 8, 4, 16, 0, 24, 0, 32, 0, 40, 0, 48, 2, 32, 0, 24, 0, 34, 13, 90, 111, 109, 98, 105, 101, 32, 68, 101, 115, 105, 103, 110, 40, 0, 48, 0
        }
        ;
#endregion
#region Loadouts
        private static Definitions _zombieDefinition;
        private static Definitions ZombieDefinition {
            get {
                if (_zombieDefinition == null) {
                    ScarecrowNPC scarecrowNpc = GameManager.server.FindPrefab("assets/prefabs/npc/scarecrow/scarecrow.prefab").GetComponent<ScarecrowNPC>();
                    _zombieDefinition = new Definitions {
                        Loadouts = scarecrowNpc.loadouts, LootSpawns = scarecrowNpc.LootSpawnSlots
                    }
                    ;
                }
                return _zombieDefinition;
            }
        }
        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.All(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
#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 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 float NextThrownWeaponTime {
                get;
                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.Get<List<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 (IsSleeping) return;
                HordeGrid.Move(this, CentralLocation.x, CentralLocation.z);
                TryMergeHordes();
                TryGrowHorde();
                TryCongregateHorde();
                MoveRoamersTowardsTarget();
            }
            private void MoveRoamersTowardsTarget() {
                HordeOnAlert = AnyHasTarget(out BaseEntity target);
                if (Leader && !Leader.IsDestroyed && target && !target.IsDestroyed) SetLeaderRoamTarget(target.transform.position);
            }
            private void TryCongregateHorde() {
                if (Time.time > nextSeperationCheckTime) {
                    nextSeperationCheckTime = Time.time + SEPERATION_CHECK_RATE;
                    if (GetLargestSeperation() > 30f) {
                        if (Leader.CurrentState <= AIState.Roam) SetLeaderRoamTarget(CentralLocation);
                    }
                }
            }
            public void RegisterInterestInTarget(ZombieNPC interestedMember, BaseEntity baseEntity, bool force) {
                if (baseEntity == null || members == null) return;
                for (int i = 0; i < members.Count; i++) {
                    ZombieNPC hordeMember = members[i];
                    if (!hordeMember || hordeMember.IsDestroyed || interestedMember == hordeMember) continue;
                    hordeMember.Brain.Senses.Memory.SetKnown(baseEntity, hordeMember, null);
                    if (force) {
                        hordeMember.Brain.Events.Memory.Entity.Set(baseEntity, hordeMember.Brain.Events.CurrentInputMemorySlot);
                        hordeMember.Brain.SwitchToState(AIState.Chase, 1);
                    }
                }
                if (!force && Leader && !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 HasHumanTarget() {
                if (members != null) {
                    for (int i = 0; i < members.Count; i++) {
                        ZombieNPC hordeMember = members[i];
                        if (hordeMember.HasHumanTargetInRange) 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;
                for (int i = 0; i < members.Count; i++) {
                    ZombieNPC hordeMember = members[i];
                    if (hordeMember.IsGroupLeader) continue;
                    hordeMember.ResetRoamState();
                }
            }
            public void SetLeaderRoamTarget(Vector3 position) {
                if (Leader != null && !Leader.IsDestroyed && !Leader.RecentlySetDestination) {
                    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, zombieNpc.isHidingInside || zombieNpc.unreachableLastFrame);
                }
            }
            public void OnPlayerKilled(BasePlayer player) {
                if (Configuration.Horde.CreateOnDeath && MemberCount < maximumMemberCount) {
                    if (NavmeshSpawnPoint.Find(player.transform.position, 10f, out Vector3 position)) SpawnMember(position);
                }
            }
            public void SpawnMember(Vector3 position) {
                ConfigData.MemberOptions.Loadout loadout = null;
                if (!string.IsNullOrEmpty(hordeProfile) && Configuration.HordeProfiles.ContainsKey(hordeProfile)) {
                    string loadoutId = Configuration.HordeProfiles[hordeProfile].GetRandom();
                    loadout = Configuration.Member.Loadouts.FirstOrDefault(x => x.LoadoutID == loadoutId);
                }
                if (loadout == null) loadout = Configuration.Member.Loadouts.GetRandom();
                ZombieNPC zombieNpc = ChaosNPC.SpawnNPC<ZombieNPC, ZombieNPCBrain>(Instance, position, loadout.NPCSettings);
                if (!zombieNpc) return;
                zombieNpc.Loadout = loadout;
                zombieNpc.Horde = this;
                zombieNpc.gameObject.AwakeFromInstantiate();
                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 && !zombieNpc.IsDestroyed) zombieNpc.Kill();
                    }
                }
                members.Clear();
                Pool.FreeUnmanaged(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.IsGroupLeader = false;
                                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.IsGroupLeader = false;
                                    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 || !zombieNpc.Brain) continue;
                    if (zombieNpc.Brain.sleeping == sleep) continue;
                    ((ZombieNPCBrain)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 || !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;
                public static void InitializeSpawnOrders() {
                    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();
                    }

// ... [Code preview truncated due to length. Full source in attached file] ...

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