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

FREE ZombieHorde : 0.3.4 0.3.4

Нет прав для скачивания
C#:
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Plugins;
using Rust.Ai.HTN;
using Rust.Ai.HTN.Sensors;
using Rust.Ai.HTN.Reasoning;
using Rust.Ai.HTN.Murderer;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
namespace Oxide.Plugins {
    [Info("ZombieHorde", "k1lly0u", "0.3.4")]
    class ZombieHorde : RustPlugin {
        [PluginReference]
        private Plugin Kits, Spawns;
        private static ZombieHorde Instance {
            get;
            set;
        }
        private const string SCARECROW_PREFAB = "assets/prefabs/npc/scarecrow/scarecrow.prefab";
        private const int WORLD_LAYER = 65536;
#region Oxide Hooks
        private void OnServerInitialized() {
            Instance = this;
            HordeThinkManager.Create();
            permission.RegisterPermission("zombiehorde.admin", this);
            permission.RegisterPermission("zombiehorde.ignore", this);
            _blueprintBase = ItemManager.FindItemDefinition("blueprintbase");
            _glowEyes = ItemManager.FindItemDefinition("gloweyes");
            if (!configData.Member.TargetedByPeaceKeeperTurrets) Unsubscribe(nameof(CanEntityBeHostile));
            ValidateLoadoutProfiles();
            ValidateSpawnSystem();
            CreateMonumentHordeOrders();
            NextTick(() => CreateRandomHordes());
        }
        private void OnEntityTakeDamage(BaseCombatEntity baseCombatEntity, HitInfo hitInfo) {
            if (hitInfo != null) {
                HordeMember hordeMember;
                if (hitInfo.InitiatorPlayer != null) {
                    hordeMember = hitInfo.InitiatorPlayer.GetComponent<HordeMember>();
                    if (hordeMember != null) {
                        if (hordeMember.damageMultiplier != 1f) hitInfo.damageTypes.ScaleAll(hordeMember.damageMultiplier);
                        return;
                    }
                }
                hordeMember = baseCombatEntity.GetComponent<HordeMember>();
                if (hordeMember != null) {
                    if (configData.Member.HeadshotKills && hitInfo.isHeadshot) hitInfo.damageTypes.ScaleAll(1000);
                }
            }
        }
        private void OnPlayerDeath(BasePlayer player, HitInfo hitInfo) {
            if (player == null || hitInfo == null) return;
            HordeMember hordeMember = player.GetComponent<HordeMember>();
            if (hordeMember != null) {
                if (configData.Loot.DropInventory) hordeMember.PrepareInventory();
                hordeMember.Manager.OnMemberDeath(hordeMember, hitInfo.Initiator as BaseCombatEntity);
                return;
            }
            if (configData.Horde.CreateOnDeath && hitInfo.InitiatorPlayer != null) {
                HordeMember attacker = hitInfo.InitiatorPlayer.GetComponent<HordeMember>();
                if (attacker != null && attacker.Manager != null) attacker.Manager.OnPlayerDeath(player, attacker);
            }
        }
        private void OnEntityKill(HTNPlayer htnPlayer) {
            HordeMember hordeMember = htnPlayer.GetComponent<HordeMember>();
            if (hordeMember != null && hordeMember.Manager != null) hordeMember.Manager.OnMemberDeath(hordeMember, null);
        }
        private object CanBeTargeted(BasePlayer player, MonoBehaviour behaviour) {
            HordeMember hordeMember = player.GetComponent<HordeMember>();
            if (hordeMember != null) {
                if (((behaviour is AutoTurret) || (behaviour is GunTrap) || (behaviour is FlameTurret)) && configData.Member.TargetedByTurrets) return null;
                return false;
            }
            return null;
        }
        private object CanEntityBeHostile(HTNPlayer htnPlayer) => htnPlayer != null && htnPlayer.GetComponent<HordeMember>() != null ? (object)true : null;
        private object CanBradleyApcTarget(BradleyAPC bradleyAPC, HTNPlayer htnPlayer) {
            if (htnPlayer != null) {
                HordeMember hordeMember = htnPlayer.GetComponent<HordeMember>();
                if (hordeMember != null && !configData.Member.TargetedByAPC) return false;
            }
            return null;
        }
        private object OnCorpsePopulate(HTNPlayer htnPlayer, NPCPlayerCorpse npcPlayerCorpse) {
            if (htnPlayer != null && npcPlayerCorpse != null) {
                HordeMember hordeMember = htnPlayer.GetComponent<HordeMember>();
                if (hordeMember == null) return null;
                if (configData.Loot.DropInventory) {
                    hordeMember.MoveInventoryTo(npcPlayerCorpse);
                    return npcPlayerCorpse;
                }
                SpawnIntoContainer(npcPlayerCorpse);
                return npcPlayerCorpse;
            }
            return null;
        }
        private object CanPopulateLoot(HTNPlayer htnPlayer, NPCPlayerCorpse corpse) => htnPlayer != null && htnPlayer.GetComponent<HordeMember>() != null ? (object)false : null;
        private void Unload() {
            HordeManager.Order.OnUnload();
            _hordeThinkManager.Destroy();
            for (int i = HordeManager._allHordes.Count - 1; i >= 0; i--) HordeManager._allHordes[i].Destroy(true, true);
            HordeManager._allHordes.Clear();
            _spawnState = SpawnState.Spawn;
            configData = null;
            Instance = null;
        }
#endregion
#region Functions
#region Horde Spawning
        private List<Vector3> _spawnPoints;
        private SpawnSystem _spawnSystem = SpawnSystem.None;
        private bool ValidateSpawnSystem() {
            _spawnSystem = ParseType<SpawnSystem>(configData.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(configData.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", configData.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 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.Offshore | TerrainTopology.Enum.Summit);
        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", configData.Horde.SpawnFile);
                    return spawnPoint;
                }
            }
            float size = (World.Size / 2f) * 0.75f;
            NavMeshHit navMeshHit;
            for (int i = 0; i < 10; i++) {
                Vector2 randomInCircle = UnityEngine.Random.insideUnitCircle * size;
                Vector3 position = new Vector3(randomInCircle.x, 0, randomInCircle.y);
                position.y = TerrainMeta.HeightMap.GetHeight(position);
                if (NavMesh.SamplePosition(position, out navMeshHit, 25f, 1)) {
                    position = navMeshHit.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 >= configData.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") && configData.Monument.Powerplant.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-30.8f, 0.2f, -15.8f)), configData.Monument.Powerplant);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("military_tunnel_1") && configData.Monument.Tunnels.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-7.4f, 13.4f, 53.8f)), configData.Monument.Tunnels);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("harbor_1") && configData.Monument.LargeHarbor.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(54.7f, 5.1f, -39.6f)), configData.Monument.LargeHarbor);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("harbor_2") && configData.Monument.SmallHarbor.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-66.6f, 4.9f, 16.2f)), configData.Monument.SmallHarbor);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("airfield_1") && configData.Monument.Airfield.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-12.4f, 0.2f, -28.9f)), configData.Monument.Airfield);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("trainyard_1") && configData.Monument.Trainyard.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(35.8f, 0.2f, -0.8f)), configData.Monument.Trainyard);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("water_treatment_plant_1") && configData.Monument.WaterTreatment.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(11.1f, 0.3f, -80.2f)), configData.Monument.WaterTreatment);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("warehouse") && configData.Monument.Warehouse.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(16.6f, 0.1f, -7.5f)), configData.Monument.Warehouse);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("satellite_dish") && configData.Monument.Satellite.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(18.6f, 6.0f, -7.5f)), configData.Monument.Satellite);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("sphere_tank") && configData.Monument.Dome.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(1518.7f, -11.4f, 175.4f)), configData.Monument.Dome);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("radtown_small_3") && configData.Monument.Radtown.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-16.3f, -2.1f, -3.3f)), configData.Monument.Radtown);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("launch_site_1") && configData.Monument.LaunchSite.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(222.1f, 3.3f, 0.0f)), configData.Monument.LaunchSite);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("gas_station_1") && configData.Monument.GasStation.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-9.8f, 3.0f, 7.2f)), configData.Monument.GasStation);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("supermarket_1") && configData.Monument.Supermarket.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(5.5f, 0.0f, -20.5f)), configData.Monument.Supermarket);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("mining_quarry_c") && configData.Monument.HQMQuarry.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(15.8f, 4.5f, -1.5f)), configData.Monument.HQMQuarry);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("mining_quarry_a") && configData.Monument.SulfurQuarry.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-0.8f, 0.6f, 11.4f)), configData.Monument.SulfurQuarry);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("mining_quarry_b") && configData.Monument.StoneQuarry.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(-7.6f, 0.2f, 12.3f)), configData.Monument.StoneQuarry);
                        count++;
                        continue;
                    }
                    if (gobject.name.Contains("junkyard_1") && configData.Monument.Junkyard.Enabled) {
                        HordeManager.Order.CreateOrder(tr.TransformPoint(new Vector3(12.8f, 10.8f, -58.4f)), configData.Monument.Junkyard);
                        count++;
                        continue;
                    }
                }
            }
        }
        private void CreateRandomHordes() {
            int amountToCreate = configData.Horde.MaximumHordes - HordeManager._allHordes.Count;
            for (int i = 0; i < amountToCreate; i++) {
                float roamDistance = configData.Horde.LocalRoam ? configData.Horde.RoamDistance : -1;
                string profile = configData.Horde.UseProfiles ? configData.HordeProfiles.Keys.ToArray().GetRandom() : string.Empty;
                HordeManager.Order.CreateOrder(GetSpawnPoint(), configData.Horde.InitialMemberCount, configData.Horde.MaximumMemberCount, roamDistance, profile);
            }
        }
#endregion
#region Inventory and Loot
        private ItemDefinition _blueprintBase;
        private ItemDefinition _glowEyes;
        private static MurdererDefinition _defaultDefinition;
        private static MurdererDefinition DefaultDefinition {
            get {
                if (_defaultDefinition == null) _defaultDefinition = GameManager.server.FindPrefab(SCARECROW_PREFAB).GetComponent<HTNPlayer>().AiDefinition as MurdererDefinition;
                return _defaultDefinition;
            }
        }
        private void ValidateLoadoutProfiles() {
            Puts("Validating horde profiles...");
            bool hasChanged = false;
            for (int i = configData.HordeProfiles.Count - 1; i >= 0; i--) {
                string key = configData.HordeProfiles.ElementAt(i).Key;
                for (int y = configData.HordeProfiles[key].Count - 1; y >= 0; y--) {
                    string loadoutId = configData.HordeProfiles[key][y];
                    if (!configData.Member.Loadouts.Any(x => x.LoadoutID == loadoutId)) {
                        Puts($"Loadout profile {loadoutId} does not exist.Removing from config");
                        configData.HordeProfiles[key].Remove(loadoutId);
                        hasChanged = true;
                    }
                }
                if (configData.HordeProfiles[key].Count <= 0) {
                    Puts($"Horde profile {key} does not have any valid loadouts.Removing from config");
                    configData.HordeProfiles.Remove(key);
                    hasChanged = true;
                }
            }
            foreach (ConfigData.MemberOptions.Loadout loadout in configData.Member.Loadouts) {
                if (loadout.Vitals == null) {
                    loadout.Vitals = new ConfigData.MemberOptions.Loadout.VitalStats() {
                        Health = DefaultDefinition.Vitals.HP
                    }
                    ;
                    hasChanged = true;
                }
                if (loadout.Movement == null) {
                    loadout.Movement = new ConfigData.MemberOptions.Loadout.MovementStats() {
                        Acceleration = DefaultDefinition.Movement.Acceleration, DuckSpeed = DefaultDefinition.Movement.DuckSpeed, RunSpeed = DefaultDefinition.Movement.RunSpeed, TurnSpeed = DefaultDefinition.Movement.TurnSpeed, WalkSpeed = DefaultDefinition.Movement.WalkSpeed
                    }
                    ;
                    hasChanged = true;
                }
                if (loadout.Sensory == null) {
                    loadout.Sensory = new ConfigData.MemberOptions.Loadout.SensoryStats() {
                        FOV = DefaultDefinition.Sensory.FieldOfView, HearingRange = DefaultDefinition.Sensory.HearingRange, VisionRange = DefaultDefinition.Sensory.VisionRange
                    }
                    ;
                    hasChanged = true;
                }
            }
            if (hasChanged) SaveConfig();
        }
        private void SpawnIntoContainer(LootableCorpse lootableCorpse) {
            int count = UnityEngine.Random.Range(configData.Loot.Random.Minimum, configData.Loot.Random.Maximum);
            int spawnedCount = 0;
            int loopCount = 0;
            while (true) {
                loopCount++;
                if (loopCount > 3) return;
                float probability = UnityEngine.Random.Range(0f, 1f);
                List<ConfigData.LootTable.RandomLoot.LootDefinition> definitions = new List<ConfigData.LootTable.RandomLoot.LootDefinition>(configData.Loot.Random.List);
                for (int i = 0; i < configData.Loot.Random.List.Count; i++) {
                    ConfigData.LootTable.RandomLoot.LootDefinition lootDefinition = definitions.GetRandom();
                    definitions.Remove(lootDefinition);
                    if (lootDefinition.Probability >= probability) {
                        CreateItem(lootDefinition, lootableCorpse.containers[0]);
                        spawnedCount++;
                        if (spawnedCount >= count) return;
                    }
                }
            }
        }
        private void CreateItem(ConfigData.LootTable.RandomLoot.LootDefinition lootDefinition, ItemContainer container) {
            Item item;
            if (!lootDefinition.IsBlueprint) item = ItemManager.CreateByName(lootDefinition.Shortname, lootDefinition.GetAmount(), lootDefinition.SkinID);
            else {
                item = ItemManager.Create(_blueprintBase);
                item.blueprintTarget = ItemManager.FindItemDefinition(lootDefinition.Shortname).itemid;
            }
            if (item != null) {
                item.OnVirginSpawn();
                if (!item.MoveToContainer(container, -1, true)) item.Remove(0f);
            }
            if (lootDefinition.Required != null) CreateItem(lootDefinition.Required, container);
        }
        private static void StripInventory(BasePlayer player, bool skipWear = false) {
            List<Item> list = Pool.GetList<Item>();
            player.inventory.AllItemsNoAlloc(ref list);
            for (int i = list.Count - 1; i >= 0; i--) {
                Item item = list[i];
                if (skipWear && item?.parent == player.inventory.containerWear) continue;
                item.RemoveFromContainer();
                item.Remove();
            }
            Pool.FreeList(ref list);
        }
        private static void ClearContainer(ItemContainer container, bool skipWear = false) {
            if (container == null || container.itemList == null) return;
            while (container.itemList.Count > 0) {
                Item item = container.itemList[0];
                item.RemoveFromContainer();
                item.Remove(0f);
            }
        }
#endregion
#region Spawning
        private static HTNPlayer InstantiateEntity(Vector3 position) {
            GameObject gameObject = Instantiate.GameObject(GameManager.server.FindPrefab(SCARECROW_PREFAB), position, Quaternion.identity);
            gameObject.name = SCARECROW_PREFAB;
            UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
            UnityEngine.Object.Destroy(gameObject.GetComponent<Spawnable>());
            if (!gameObject.activeSelf) gameObject.SetActive(true);
            HTNPlayer component = gameObject.GetComponent<HTNPlayer>();
            return component;
        }
        private static NavMeshHit navHit;
        private static RaycastHit raycastHit;
        private static Collider[] _buffer = new Collider[256];
        private static object FindPointOnNavmesh(Vector3 targetPosition, float maxDistance = 4f) {
            for (int i = 0; i < 10; i++) {
                Vector3 position = i == 0 ? targetPosition : targetPosition + (UnityEngine.Random.onUnitSphere * maxDistance);
                if (NavMesh.SamplePosition(position, out navHit, maxDistance, 1)) {
                    if (IsInRockPrefab(navHit.position)) continue;
                    if (IsNearWorldCollider(navHit.position)) continue;
                    return navHit.position;
                }
            }
            return null;
        }
        private static bool IsInRockPrefab(Vector3 position) {
            Physics.queriesHitBackfaces = true;
            bool isInRock = Physics.Raycast(position, Vector3.up, out raycastHit, 20f, WORLD_LAYER, QueryTriggerInteraction.Ignore) && blockedColliders.Any(s => raycastHit.collider?.gameObject?.name.Contains(s) ?? false);
            Physics.queriesHitBackfaces = false;
            return isInRock;
        }
        private static bool IsNearWorldCollider(Vector3 position) {
            Physics.queriesHitBackfaces = true;
            int count = Physics.OverlapSphereNonAlloc(position, 2f, _buffer, WORLD_LAYER, QueryTriggerInteraction.Ignore);
            Physics.queriesHitBackfaces = false;
            int removed = 0;
            for (int i = 0; i < count; i++) {
                if (acceptedColliders.Any(s => _buffer[i].gameObject.name.Contains(s))) removed++;
            }
            return count - removed > 0;
        }
        private static readonly string[] acceptedColliders = new string[] {
            "road", "rocket_factory", "range", "train_track", "runway", "_grounds", "concrete_slabs", "lighthouse", "cave", "office", "walkways", "sphere", "tunnel", "industrial", "junkyard"
        }
        ;
        private static readonly string[] blockedColliders = new string[] {
            "rock", "junk", "range", "invisible"
        }
        ;
#endregion
        private T ParseType<T>(string type) {
            try {
                return (T)Enum.Parse(typeof(T), type, true);
            }
            catch {
                return default(T);
            }
        }
        private static bool ContainsTopologyAtPoint(TerrainTopology.Enum mask, Vector3 position) => (TerrainMeta.TopologyMap.GetTopology(position) & (int)mask) != 0;
#endregion
#region Think Manager
        private HordeThinkManager _hordeThinkManager;
        private static SpawnState _spawnState = SpawnState.Spawn;
        private class HordeThinkManager : MonoBehaviour {
            internal static void Create() => Instance._hordeThinkManager = new GameObject("ZombieHorde-ThinkManager").AddComponent<HordeThinkManager>();
            private void Awake() {
                _spawnState = configData.TimedSpawns.Enabled ? (ShouldSpawn() ? SpawnState.Spawn : SpawnState.Despawn) : SpawnState.Spawn;
                if (configData.TimedSpawns.Enabled) InvokeHandler.InvokeRepeating(this, CheckTimeTick, 0f, 1f);
            }
            internal void Update() {
                HordeManager._hordeTickQueue.RunQueue(0.5);
                HordeMember._memberTickQueue.RunQueue(0.5);
            }
            internal void Destroy() {
                if (configData.TimedSpawns.Enabled) InvokeHandler.CancelInvoke(this, CheckTimeTick);
                Destroy(gameObject);
            }
            private bool ShouldSpawn() {
                float currentTime = TOD_Sky.Instance.Cycle.Hour;
                if (configData.TimedSpawns.Start > configData.TimedSpawns.End) return currentTime > configData.TimedSpawns.Start || currentTime < configData.TimedSpawns.End;
                else return currentTime > configData.TimedSpawns.Start && currentTime < configData.TimedSpawns.End;
            }
            private void CheckTimeTick() {
                if (ShouldSpawn()) {
                    if (_spawnState == SpawnState.Despawn) {
                        _spawnState = SpawnState.Spawn;
                        HordeManager.Order.StopDespawning();
                        HordeManager.Order.BeginSpawning();
                    }
                }
                else {
                    if (_spawnState == SpawnState.Spawn) {
                        _spawnState = SpawnState.Despawn;
                        if (configData.TimedSpawns.Despawn) {
                            HordeManager.Order.StopSpawning();
                            HordeManager.Order.BeginDespawning();
                        }
                    }
                }
            }
        }
        internal class HordeMemberTickQueue : ObjectWorkQueue<HordeMember> {
            protected override void RunJob(HordeMember hordeMember) {
                if (!ShouldAdd(hordeMember)) return;
                hordeMember.OnTick();
            }
            protected override bool ShouldAdd(HordeMember hordeMember) {
                if (!base.ShouldAdd(hordeMember)) return false;
                return hordeMember != null && hordeMember.Entity != null && hordeMember.Entity.IsValid();
            }
        }
        internal class HordeTickQueue : ObjectWorkQueue<HordeManager> {
            protected override void RunJob(HordeManager hordeManager) {
                if (!ShouldAdd(hordeManager)) return;
                hordeManager.HordeTick();
                Instance.timer.In(3f, ()=> HordeManager._hordeTickQueue.Add(hordeManager));
            }
            protected override bool ShouldAdd(HordeManager hordeManager) {
                if (!base.ShouldAdd(hordeManager)) return false;
                return hordeManager != null && !hordeManager.isDestroyed && hordeManager.members?.Count > 0;
            }
        }
#endregion
#region Horde Manager
        internal class HordeManager {
            internal static List<HordeManager> _allHordes = new List<HordeManager>();
            internal static HordeTickQueue _hordeTickQueue = new HordeTickQueue();
            internal List<HordeMember> members;
            internal Vector3 Destination;
            internal Vector3 AverageLocation;
            internal BaseCombatEntity PrimaryTarget;
            internal NpcPlayerInfo NpcPlayerInfo;
            internal AnimalInfo AnimalInfo;
            internal bool DebugMode {
                get;
                set;
            }
            = false;
            private bool isRoaming = true;
            private bool isRegrouping = false;
            internal bool isDestroyed = false;
            private Vector3 initialSpawnPosition;
            private int initialMemberCount;
            private bool isLocalHorde = false;
            private float maximumRoamDistance;
            internal string hordeProfile;
            private float nextGrowthTime = Time.time + configData.Horde.GrowthRate;
            private int maximumMemberCount;
            private float nextMergeTime = Time.time + MERGE_COOLDOWN;
            private float refreshRoamTime;
            private float verifyTargetTime;
            internal bool PathStateFailed {
                get;
                set;
            }
            private const float MERGE_COOLDOWN = 180f;
            private const float ROAM_REFRESH_RATE = 1f;
            private const float TARGET_VERIFY_RATE = 3f;
            internal static bool Create(Order order) {
                HordeManager manager = new HordeManager {
                    members = Pool.GetList<HordeMember>(), initialSpawnPosition = order.position, isLocalHorde = order.maximumRoamDistance > 0, maximumRoamDistance = order.maximumRoamDistance, initialMemberCount = order.initialMemberCount, maximumMemberCount = order.maximumMemberCount, hordeProfile = order.hordeProfile
                }
                ;
                for (int i = 0; i < order.initialMemberCount; i++) manager.SpawnMember(order.position, false);
                if (manager.members.Count == 0) {
                    manager.Destroy();
                    return false;
                }
                _allHordes.Add(manager);
                _hordeTickQueue.Add(manager);
                return true;
            }
            internal void Destroy(bool permanent = false, bool killNpcs = false) {
                isDestroyed = true;
                if (killNpcs) {
                    for (int i = members.Count - 1; i >= 0; i--) {
                        HordeMember hordeMember = members[i];
                        if (hordeMember != null && hordeMember.Entity != null && !hordeMember.Entity.IsDestroyed) {
                            StripInventory(hordeMember.Entity);
                            hordeMember.Entity.Kill();
                        }
                    }
                }
                members.Clear();
                Pool.FreeList(ref members);
                _allHordes.Remove(this);
                if (!permanent && _allHordes.Count <= configData.Horde.MaximumHordes) InvokeHandler.Invoke(Instance._hordeThinkManager, () => Order.CreateOrder(isLocalHorde ? initialSpawnPosition : Instance.GetSpawnPoint(), initialMemberCount, maximumMemberCount, isLocalHorde ? maximumRoamDistance : -1f, hordeProfile), configData.Horde.RespawnTime);
            }
            internal void HordeTick() {
                if (members.Count == 0 || isDestroyed) return;
                if (DebugMode) {
                    foreach(BasePlayer player in BasePlayer.activePlayerList) {
                        if (player.IsAdmin) player.SendConsoleCommand("ddraw.text", 10f, Color.green, AverageLocation + new Vector3(0, 1.5f, 0), $"<size=20>Horde {_allHordes.IndexOf(this)}</size>");
                    }
                }
                bool hasSeentargetRecently = false;
                bool isDormant = true;
                for (int i = members.Count - 1; i >= 0; i--) {
                    HordeMember member = members[i];
                    if (!member.Entity.IsDormant) isDormant = false;
                    if (!isRoaming && !hasSeentargetRecently) {
                        if (Time.time - member.lastSeenTargetTime < configData.Horde.ForgetTime) {
                            hasSeentargetRecently = true;
                            if (PrimaryTarget != null) Destination = PrimaryTarget.ServerPosition;
                        }
                    }
                }
                if (isDormant) return;
                AverageLocation = GetAverageVector();
                TryMergeHordes();
                TryGrowHorde();
                bool hasValidTarget = PrimaryTarget != null && IsValidTarget(PrimaryTarget);
                if (hasSeentargetRecently && Time.time > verifyTargetTime) {
                    verifyTargetTime = Time.time + TARGET_VERIFY_RATE;
                    if (!hasValidTarget) {
                        if (DebugMode) Debug.Log($"Target is invalid, set roaming");
                        hasSeentargetRecently = false;
                        SetPrimaryTarget(null);
                    }
                }
                if (!isRoaming && !hasSeentargetRecently) {
                    if (DebugMode) Debug.Log($"No targets, set roaming");
                    isRoaming = true;
                }
                if (isRoaming) {
                    if (Time.time > refreshRoamTime) {
                        refreshRoamTime = Time.time + ROAM_REFRESH_RATE;
                        if ((!isRegrouping && GetMaximumSeperation() > 15f) || (isRegrouping && GetMaximumSeperation() > 5)) {
                            isRegrouping = true;
                            Destination = members.GetRandom().Transform.position;
                            if (DebugMode) Debug.Log($"Regroup at {Destination}");
                            goto UPDATE_ROAM;
                        }
                        if (Destination == Vector3.zero || Vector3.Distance(Destination, AverageLocation) < 10f || members.All(x => x.Domain.NavAgent.isPathStale)) {
                            isRegrouping = false;
                            Destination = GetRandomLocation(isLocalHorde ? initialSpawnPosition : AverageLocation);
                            if (DebugMode) Debug.Log($"Set new destination at {Destination}");
                            goto UPDATE_ROAM;
                        }
                        if (PathStateFailed) {
                            Destination = GetRandomLocation(isLocalHorde ? initialSpawnPosition : AverageLocation);
                            PathStateFailed = false;
                            if (DebugMode) Debug.Log($"Path has failed.New destination {Destination}");
                        }
                        UPDATE_ROAM : if (DebugMode) Debug.Log($"Update roam target | Current {AverageLocation} | Destination {Destination} | Distance {Vector3.Distance(AverageLocation, Destination)}");
                        UpdateRoamTarget();
                    }
                }
            }
            internal void EvaluateTarget(NpcPlayerInfo target) {
                if (target.Player == null || target.Player.IsDead() || target.Player.transform == null || PrimaryTarget == target.Player) return;
                if (PrimaryTarget == null || PrimaryTarget.IsDead()) {
                    SetPrimaryTarget(target.Player, target);
                    return;
                }
                if (PrimaryTarget.transform == null) return;
                if (Vector3.Distance(AverageLocation, target.Player.transform.position) < Vector3.Distance(AverageLocation, PrimaryTarget.transform.position) || PrimaryTarget is BaseNpc) {
                    SetPrimaryTarget(target.Player, target);
                    return;
                }
            }
            internal void EvaluateTarget(AnimalInfo target) {
                if (target.Animal == null || target.Animal.IsDead() || target.Animal.transform == null || PrimaryTarget == target.Animal) return;
                if (PrimaryTarget == null || PrimaryTarget.IsDead()) {
                    SetPrimaryTarget(target.Animal, target);
                    return;
                }
                if (PrimaryTarget.transform == null || PrimaryTarget is BasePlayer) return;
                if (Vector3.Distance(AverageLocation, target.Animal.transform.position) < Vector3.Distance(AverageLocation, PrimaryTarget.transform.position)) {
                    SetPrimaryTarget(target.Animal, target);
                    return;
                }
            }
            internal void SetPrimaryTarget(BaseCombatEntity baseCombatEntity, NpcPlayerInfo info) {
                if (baseCombatEntity == null || baseCombatEntity.transform == null) return;
                SetPrimaryTarget(baseCombatEntity);
                PrimaryTarget = baseCombatEntity;
                NpcPlayerInfo = info;
                AnimalInfo = default(AnimalInfo);
                for (int i = 0; i < members.Count; i++) {
                    members[i].OnTargetUpdated(info);
                }
            }
            internal void SetPrimaryTarget(BaseCombatEntity baseCombatEntity, AnimalInfo info) {
                if (baseCombatEntity == null || baseCombatEntity.transform == null) return;
                SetPrimaryTarget(baseCombatEntity);
                PrimaryTarget = baseCombatEntity;
                NpcPlayerInfo = default(NpcPlayerInfo);
                AnimalInfo = info;
                for (int i = 0; i < members.Count; i++) {
                    members[i].OnTargetUpdated(info);
                }
            }
            internal void SetPrimaryTarget(BaseCombatEntity baseCombatEntity) {
                if (baseCombatEntity == null || baseCombatEntity.transform == null) {
                    NpcPlayerInfo = default(NpcPlayerInfo);
                    AnimalInfo = default(AnimalInfo);
                    isRoaming = true;
                    Destination = AverageLocation;
                }
                else {
                    isRoaming = false;
                    isRegrouping = false;
                    Destination = baseCombatEntity.transform.position;
                }
            }
            private void UpdateRoamTarget() {
                for (int i = 0; i < members.Count; i++) {
                    HordeMember hordeMember = members[i];
                    if (hordeMember != null) hordeMember.SetRoamToDestination();
                }
            }
            internal Vector3 GetAverageVector() {
                Vector3 location = Vector3.zero;
                if (members.Count == 0) return location;
                int count = 0;
                for (int i = 0; i < members.Count; i++) {
                    HordeMember hordeMember = members[i];
                    if (hordeMember == null || hordeMember.Entity == null) continue;
                    location += hordeMember.Transform.position;
                    count++;
                }
                return location /= count;
            }
            private const TerrainTopology.Enum DESTINATION_TOPOLOGY_MASK = (TerrainTopology.Enum.Ocean | TerrainTopology.Enum.River | TerrainTopology.Enum.Lake | TerrainTopology.Enum.Offshore | TerrainTopology.Enum.Cliff);
            private Vector3 GetRandomLocation(Vector3 from) {
                for (int i = 0; i < 10; i++) {
                    Vector2 vector2 = UnityEngine.Random.insideUnitCircle * (isLocalHorde ? maximumRoamDistance : 100f);
                    Vector3 destination = from + new Vector3(vector2.x, 0f, vector2.y);
                    if (TerrainMeta.HeightMap != null) destination.y = TerrainMeta.HeightMap.GetHeight(destination);
                    NavMeshHit navMeshHit;
                    if (NavMesh.FindClosestEdge(destination, out navMeshHit, 1)) {
                        destination = navMeshHit.position;
                        if (WaterLevel.GetWaterDepth(destination, true, null) <= 0.01f && !ContainsTopologyAtPoint(DESTINATION_TOPOLOGY_MASK, destination)) return destination;
                    }
                    else if (NavMesh.SamplePosition(destination, out navMeshHit, 5f, 1) && !ContainsTopologyAtPoint(DESTINATION_TOPOLOGY_MASK, destination)) {
                        destination = navMeshHit.position;
                        if (WaterLevel.GetWaterDepth(destination, true, null) <= 0.01f) return destination;
                    }
                }
                return AverageLocation;
            }
            private float GetMaximumSeperation() {
                float distance = 0;
                for (int i = 0; i < members.Count; i++) {
                    HordeMember hordeMember = members[i];
                    if (hordeMember != null && hordeMember.Entity != null) {
                        float d = Vector3.Distance(hordeMember.Transform.position, AverageLocation);
                        if (d > distance) distance = d;
                    }
                }
                return distance;
            }
            internal bool IsValidTarget(BaseCombatEntity baseCombatEntity) {
                if (baseCombatEntity == null || baseCombatEntity.IsDestroyed || baseCombatEntity.transform == null) return false;
                if (baseCombatEntity.Health() <= 0f) return false;
                if (baseCombatEntity is BasePlayer) {
                    BasePlayer player = baseCombatEntity as BasePlayer;
                    if (player.IsDead()) return false;
                    if (player._limitedNetworking) return false;
                    if (player.IsFlying) return false;
                    if (player is HTNPlayer && (player as HTNPlayer).AiDefinition?.Info?.Family == BaseNpcDefinition.Family.Murderer) return false;
                    if (!configData.Member.TargetNPCs) {
                        if (player.IsNpc) return false;
                        if (player is NPCPlayer || player is HTNPlayer) return false;
                    }
                    if (!configData.Member.TargetHumanNPCs && !player.userID.IsSteamId() && !player.IsNpc) return false;
                    if (player.HasPlayerFlag(BasePlayer.PlayerFlags.SafeZone)) return false;
                    if (configData.Member.IgnoreSleepers && player.IsSleeping()) return false;
                    if (player.userID.IsSteamId() && Instance.permission.UserHasPermission(player.UserIDString, "zombiehorde.ignore")) return false;
                }
                return true;
            }
            internal bool SpawnMember(Vector3 position, bool alreadyInitialized = true) {
                HTNPlayer htnPlayer = InstantiateEntity(position);
                htnPlayer.enableSaving = false;
                htnPlayer.Spawn();
                HordeMember member = htnPlayer.gameObject.AddComponent<HordeMember>();
                member.Manager = this;
                members.Add(member);
                if (alreadyInitialized) {
                    if (PrimaryTarget == null || PrimaryTarget is BasePlayer) member.OnTargetUpdated(NpcPlayerInfo);
                    else if (PrimaryTarget is BaseNpc) member.OnTargetUpdated(AnimalInfo);
                }
                return true;
            }
            internal void OnPlayerDeath(BasePlayer player, HordeMember hordeMember) {
                if (hordeMember == null || !members.Contains(hordeMember)) return;
                if (members.Count < maximumMemberCount) SpawnMember(hordeMember.Transform.position);
            }
            internal void OnMemberDeath(HordeMember hordeMember, BaseCombatEntity initiator) {
                if (isDestroyed || members == null) return;
                members.Remove(hordeMember);
                if (members.Count == 0) Destroy();
                else {
                    if (PrimaryTarget == null && initiator is BasePlayer) {
                        SetPrimaryTarget(initiator, new NpcPlayerInfo() {
                            Player = initiator as BasePlayer, Time = Time.time, SqrDistance = (initiator.transform.position - AverageLocation).sqrMagnitude, BodyVisible = true, HeadVisible = true,
                        }
                        );
                    }
                }
            }
            private void TryGrowHorde() {
                if (nextGrowthTime < Time.time) {
                    if (members.Count < maximumMemberCount) {
                        for (int i = 0; i < 5; i++) {
                            if (SpawnMember(members.GetRandom().Transform.position)) break;
                        }
                    }
                    nextGrowthTime = Time.time + configData.Horde.GrowthRate;
                }
            }
            private void TryMergeHordes() {
                if (!configData.Horde.MergeHordes || nextMergeTime > Time.time) return;
                for (int y = _allHordes.Count - 1; y >= 0; y--) {
                    HordeManager manager = _allHordes[y];
                    if (manager == this) continue;
                    if (members.Count >= maximumMemberCount) return;
                    if (Vector3.Distance(AverageLocation, manager.AverageLocation) < 20) {
                        int amountToMerge = maximumMemberCount - members.Count;
                        if (amountToMerge >= manager.members.Count) {
                            for (int i = 0; i < manager.members.Count; i++) {
                                HordeMember member = manager.members[i];
                                members.Add(member);
                                member.Manager = this;
                            }
                            manager.members.Clear();
                            manager.Destroy();
                            nextMergeTime = Time.time + MERGE_COOLDOWN;
                        }
                        else {
                            bool hasMerged = false;
                            for (int i = 0; i < amountToMerge; i++) {
                                if (manager.members.Count > 0)
// ... [Code preview truncated due to length. Full source in attached file] ...

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