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

FREE AnimalSpawn : 1.0.5 1.0.5

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using Oxide.Core;
using System.Collections.Generic;
using Facepunch;
using UnityEngine;
using UnityEngine.AI;
using System.Reflection;
using System.IO;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using UnityEngine.Assertions;
using Oxide.Plugins.AnimalSpawnExtensionMethods;
namespace Oxide.Plugins {
    [Info("AnimalSpawn", "KpucTaJl", "1.0.5")]
    internal class AnimalSpawn : RustPlugin {
#region Config
        private const bool En = true;
        private PluginConfig _config;
        protected override void LoadDefaultConfig() {
            Puts("Creating a default config...");
            _config = PluginConfig.DefaultConfig();
            _config.PluginVersion = Version;
            SaveConfig();
            Puts("Creation of the default config completed!");
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            _config = Config.ReadObject<PluginConfig>();
            if (_config.PluginVersion < Version) UpdateConfigValues();
        }
        private void UpdateConfigValues() {
            Puts("Config update detected! Updating config values...");
            _config.PluginVersion = Version;
            Puts("Config update completed!");
            SaveConfig();
        }
        protected override void SaveConfig() => Config.WriteObject(_config);
        private class PluginConfig {
            [JsonProperty(En ? "Can AnimalSpawn animals attack other animals? [true/false]" : "Могут ли кастомные животные атаковать других животных? [true/false]")]
            public bool CanTargetAnimal {
                get;
                set;
            }
            [JsonProperty(En ? "Can AnimalSpawn animals attack NPCs? [true/false]" : "Могут ли кастомные животные атаковать NPC? [true/false]")]
            public bool CanTargetNpc {
                get;
                set;
            }
            [JsonProperty(En ? "Can AnimalSpawn animals attack sleeping players? [true/false]" : "Могут ли кастомные животные атаковать спящих игроков? [true/false]")]
            public bool CanTargetSleepingPlayer {
                get;
                set;
            }
            [JsonProperty(En ? "Can AnimalSpawn animals attack wounded players? [true/false]" : "Могут ли кастомные животные атаковать игроков в состоянии Wounded? [true/false]")]
            public bool CanTargetWoundedPlayer {
                get;
                set;
            }
            [JsonProperty(En ? "Configuration version" : "Версия конфигурации")]
            public VersionNumber PluginVersion {
                get;
                set;
            }
            public static PluginConfig DefaultConfig() {
                return new PluginConfig {
                    CanTargetAnimal = false, CanTargetNpc = false, CanTargetSleepingPlayer = false, CanTargetWoundedPlayer = false, PluginVersion = new VersionNumber()
                }
                ;
            }
        }
#endregion Config
#region AnimalConfig
        internal class AnimalConfig {
            public string Prefab {
                get;
                set;
            }
            public float Health {
                get;
                set;
            }
            public float RoamRange {
                get;
                set;
            }
            public float ChaseRange {
                get;
                set;
            }
            public float SenseRange {
                get;
                set;
            }
            public float ListenRange {
                get;
                set;
            }
            public float AttackRange {
                get;
                set;
            }
            public bool CheckVisionCone {
                get;
                set;
            }
            public float VisionCone {
                get;
                set;
            }
            public bool HostileTargetsOnly {
                get;
                set;
            }
            public float AttackDamage {
                get;
                set;
            }
            public float AttackRate {
                get;
                set;
            }
            public float TurretDamageScale {
                get;
                set;
            }
            public bool CanRunAwayWater {
                get;
                set;
            }
            public bool CanSleep {
                get;
                set;
            }
            public float SleepDistance {
                get;
                set;
            }
            public float Speed {
                get;
                set;
            }
            public int AreaMask {
                get;
                set;
            }
            public int AgentTypeID {
                get;
                set;
            }
            public string HomePosition {
                get;
                set;
            }
            public float MemoryDuration {
                get;
                set;
            }
            public HashSet<string> States {
                get;
                set;
            }
        }
#endregion AnimalConfig
#region Methods
        private static bool IsCustomAnimal(BaseEntity entity) => entity != null && entity.skinID == 11491311214163;
        private void CreatePreset(string preset, JObject configJson) {
            if (_presets.ContainsKey(preset)) return;
            AnimalConfig config = configJson.ToObject<AnimalConfig>();
            Interface.Oxide.DataFileSystem.WriteObject($"AnimalSpawn/{preset}", config);
            _presets.Add(preset, config);
        }
        private BaseAnimalNPC SpawnPreset(Vector3 position, string preset) => _presets.ContainsKey(preset) ? CreateCustomAnimal(position, _presets[preset]) : null;
        private BaseAnimalNPC SpawnAnimal(Vector3 position, JObject configJson) => CreateCustomAnimal(position, configJson.ToObject<AnimalConfig>());
        private CustomAnimalNpc CreateCustomAnimal(Vector3 position, AnimalConfig config) {
            BaseAnimalNPC animalNpc = GameManager.server.CreateEntity(config.Prefab, position, Quaternion.identity, false) as BaseAnimalNPC;
            AnimalBrain animalBrain = animalNpc.GetComponent<AnimalBrain>();
            CustomAnimalNpc customAnimal = animalNpc.gameObject.AddComponent<CustomAnimalNpc>();
            CustomAnimalBrain customAnimalBrain = animalNpc.gameObject.AddComponent<CustomAnimalBrain>();
            CopySerializableFields(animalNpc, customAnimal);
            CopySerializableFields(animalBrain, customAnimalBrain);
            UnityEngine.Object.DestroyImmediate(animalNpc, true);
            UnityEngine.Object.DestroyImmediate(animalBrain, true);
            customAnimal.Config = config;
            customAnimal.brain = customAnimalBrain;
            customAnimal.enableSaving = false;
            customAnimal.gameObject.AwakeFromInstantiate();
            customAnimal.Spawn();
            customAnimal.skinID = 11491311214163;
            _animals.Add(customAnimal.net.ID.Value, customAnimal);
            return customAnimal;
        }
        private static void CopySerializableFields<T>(T src, T dst) {
            FieldInfo[] srcFields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo field in srcFields) {
                object value = field.GetValue(src);
                field.SetValue(dst, value);
            }
        }
        private void SetParentEntity(CustomAnimalNpc animal, BaseEntity parent, Vector3 pos) {
            if (IsCustomAnimal(animal) && parent != null) animal.SetParentEntity(parent, pos);
        }
        private void SetHomePosition(CustomAnimalNpc animal, Vector3 pos) {
            if (IsCustomAnimal(animal)) animal.HomePosition = pos;
        }
        private void SetCustomNavMesh(CustomAnimalNpc animal, Transform transform, string navMeshName) {
            if (!IsCustomAnimal(animal) || transform == null || !_allNavMeshes.ContainsKey(navMeshName)) return;
            Dictionary<int, Dictionary<int, PointNavMeshFile>> navMesh = _allNavMeshes[navMeshName];
            for (int i = 0; i < navMesh.Count; i++) {
                if (!animal.CustomNavMesh.ContainsKey(i)) animal.CustomNavMesh.Add(i, new Dictionary<int, PointNavMesh>());
                for (int j = 0; j < navMesh[i].Count; j++) {
                    PointNavMeshFile pointNavMesh = navMesh[i][j];
                    animal.CustomNavMesh[i].Add(j, new PointNavMesh {
                        Position = transform.TransformPoint(pointNavMesh.Position.ToVector3()), Enabled = pointNavMesh.Enabled
                    }
                    );
                }
            }
            animal.InitCustomNavMesh();
        }
        private void AddStates(CustomAnimalNpc animal, HashSet<string> states) {
            if (states.Contains("RoamState")) animal.brain.AddState(new CustomAnimalBrain.RoamState(animal));
            if (states.Contains("ChaseState")) animal.brain.AddState(new CustomAnimalBrain.ChaseState(animal));
            if (states.Contains("CombatState")) animal.brain.AddState(new CustomAnimalBrain.CombatState(animal));
            if (states.Contains("DestroyState")) animal.brain.AddState(new CustomAnimalBrain.DestroyState(animal));
        }
#endregion Methods
#region Controller
        public class CustomAnimalNpc : BaseAnimalNPC {
            public AnimalConfig Config {
                get;
                set;
            }
            public Vector3 HomePosition {
                get;
                set;
            }
            public float DistanceFromBase => Vector3.Distance(transform.position, HomePosition);
            public override void ServerInit() {
                base.ServerInit();
                HomePosition = string.IsNullOrEmpty(Config.HomePosition) ? transform.position : Config.HomePosition.ToVector3();
                if (NavAgent == null) NavAgent = GetComponent<NavMeshAgent>();
                if (NavAgent != null) {
                    NavAgent.areaMask = Config.AreaMask;
                    NavAgent.agentTypeID = Config.AgentTypeID;
                }
                AttackDamage = Config.AttackDamage;
                AttackRate = Config.AttackRate;
                AttackRange = Config.AttackRange;
                startHealth = Config.Health;
                _health = Config.Health;
                _maxHealth = Config.Health;
                InvokeRepeating(UpdateTick, 1f, 2f);
            }
            private void OnDestroy() => CancelInvoke();
            private void UpdateTick() {
                if (CanRunAwayWater()) RunAwayWater();
                UpdateSleep();
            }
            public override void OnDied(HitInfo hitInfo = null) {
                if (hitInfo != null) {
                    BasePlayer initiatorPlayer = hitInfo.InitiatorPlayer;
                    if (initiatorPlayer != null) {
                        if (UnityEngine.Application.isEditor || ConVar.Server.official) {
                            initiatorPlayer.ClientRPC<string>(RpcTarget.Player("RecieveAchievement", initiatorPlayer), "KILL_ANIMAL");
                        }
                        if (!string.IsNullOrEmpty(deathStatName)) {
                            initiatorPlayer.stats.Add(deathStatName, 1, global : : Stats.Steam | global : : Stats.Life);
                            initiatorPlayer.stats.Save();
                        }
                        initiatorPlayer.LifeStoryKill(this);
                    }
                }
                Assert.IsTrue(isServer, "OnDied called on client!");
                if (Interface.Oxide.CallHook("CanCustomAnimalSpawnCorpse", this) == null) {
                    BaseCorpse baseCorpse = DropCorpse(CorpsePrefab.resourcePath);
                    if (baseCorpse) {
                        baseCorpse.Spawn();
                        baseCorpse.TakeChildren(this);
                    }
                }
                KillMessage();
            }
#region Targeting
            internal void UpdateTarget() {
                BaseEntity target = null;
                float single = -1f;
                foreach (BaseEntity entity in brain.Senses.Players) {
                    if (!CanTargetEntity(entity)) continue;
                    float single2 = GetSingle2(entity);
                    if (single2 <= single) continue;
                    target = entity;
                    single = single2;
                }
                if (brain.Senses.senseTypes.HasFlag(EntityType.NPC)) {
                    foreach (BaseEntity entity in brain.Senses.Memory.LOS) {
                        if (!CanTargetEntity(entity)) continue;
                        float single2 = GetSingle2(entity);
                        if (single2 <= single) continue;
                        target = entity;
                        single = single2;
                    }
                    foreach (BaseEntity entity in brain.Senses.Memory.Targets) {
                        if (!CanTargetEntity(entity)) continue;
                        float single2 = GetSingle2(entity);
                        if (single2 <= single) continue;
                        target = entity;
                        single = single2;
                    }
                    foreach (BaseEntity entity in brain.Senses.Memory.Threats) {
                        if (!CanTargetEntity(entity)) continue;
                        float single2 = GetSingle2(entity);
                        if (single2 <= single) continue;
                        target = entity;
                        single = single2;
                    }
                    foreach (BaseEntity entity in brain.Senses.Memory.Friendlies) {
                        if (!CanTargetEntity(entity)) continue;
                        float single2 = GetSingle2(entity);
                        if (single2 <= single) continue;
                        target = entity;
                        single = single2;
                    }
                }
                AttackTarget = target;
            }
            private float GetSingle2(BaseEntity entity) {
                float single2 = 1f - Mathf.InverseLerp(1f, brain.SenseRange, Vector3.Distance(entity.transform.position, transform.position));
                single2 += Mathf.InverseLerp(brain.VisionCone, 1f, Vector3.Dot((entity.transform.position - transform.position).normalized, transform.forward)) / 2f;
                single2 += brain.Senses.Memory.IsLOS(entity) ? 2f : 0f;
                return single2;
            }
            internal bool CanTargetEntity(BaseEntity target) {
                if (target == null || target.Health() <= 0f) return false;
                object hook = Interface.CallHook("OnCustomAnimalTarget", this, target);
                if (hook is bool) return (bool)hook;
                if (target is BasePlayer) {
                    BasePlayer basePlayer = target as BasePlayer;
                    if (basePlayer.IsDead()) return false;
                    if (basePlayer.userID.IsSteamId()) return CanTargetPlayer(basePlayer);
                    else if (basePlayer is NPCPlayer) return CanTargetNpcPlayer(basePlayer as NPCPlayer);
                    else return false;
                }
                else if (target is BaseAnimalNPC) return CanTargetAnimal(target as BaseAnimalNPC);
                else return false;
            }
            internal bool CanTargetPlayer(BasePlayer target) {
                if (!_ins._config.CanTargetSleepingPlayer && target.IsSleeping()) return false;
                if (!_ins._config.CanTargetWoundedPlayer && target.IsWounded()) return false;
                if (target._limitedNetworking) return false;
                return true;
            }
            internal bool CanTargetNpcPlayer(NPCPlayer target) {
                if (target is FrankensteinPet) return true;
                if (target.skinID == 11162132011012) return false;
                return _ins._config.CanTargetNpc;
            }
            internal bool CanTargetAnimal(BaseAnimalNPC animal) {
                if (animal.IsDead()) return false;
                return _ins._config.CanTargetAnimal;
            }
            public float DistanceToTarget => Vector3.Distance(transform.position, AttackTarget.transform.position);
#endregion Targeting
#region Run Away Water
            internal bool IsRunAwayWater {
                get;
                set;
            }
            = false;
            private bool CanRunAwayWater() {
                if (!Config.CanRunAwayWater || IsRunAwayWater) return false;
                if (AttackTarget == null) {
                    if (transform.position.y < -0.25f) return true;
                    else return false;
                }
                if (transform.position.y > -0.25f || TerrainMeta.HeightMap.GetHeight(AttackTarget.transform.position) > -0.25f) return false;
                if (DistanceToTarget < EngagementRange()) return false;
                return true;
            }
            private void RunAwayWater() {
                IsRunAwayWater = true;
                AttackTarget = null;
                Invoke(FinishRunAwayWater, 20f);
            }
            private void FinishRunAwayWater() => IsRunAwayWater = false;
#endregion Run Away Water
#region Parent
            private BaseEntity ParentEntity {
                get;
                set;
            }
            = null;
            private Vector3 LocalPos {
                get;
                set;
            }
            = Vector3.zero;
            internal void SetParentEntity(BaseEntity parent, Vector3 pos) {
                ParentEntity = parent;
                LocalPos = pos;
                InvokeRepeating(UpdateHomePositionParent, 0f, 0.1f);
            }
            private void UpdateHomePositionParent() {
                if (ParentEntity != null) HomePosition = ParentEntity.transform.TransformPoint(LocalPos);
                else {
                    LocalPos = Vector3.zero;
                    CancelInvoke(UpdateHomePositionParent);
                }
            }
#endregion Parent
#region Custom Move
            internal Dictionary<int, Dictionary<int, PointNavMesh>> CustomNavMesh = new Dictionary<int, Dictionary<int, PointNavMesh>>();
            internal int CurrentI {
                get;
                set;
            }
            internal int CurrentJ {
                get;
                set;
            }
            internal List<PointPath> Path {
                get;
                set;
            }
            = new List<PointPath>();
            internal CustomNavMeshController NavMeshController {
                get;
                set;
            }
            public class PointPath {
                public Vector3 Position;
                public int I;
                public int J;
            }
            internal void InitCustomNavMesh() {
                if (NavAgent.enabled) NavAgent.enabled = false;
                NavMeshController = gameObject.AddComponent<CustomNavMeshController>();
                NavMeshController.enabled = false;
                Vector3 result = Vector3.zero;
                float finishDistance = float.PositiveInfinity;
                for (int i = 0; i < CustomNavMesh.Count; i++) {
                    for (int j = 0; j < CustomNavMesh[i].Count; j++) {
                        PointNavMesh pointNavMesh = CustomNavMesh[i][j];
                        if (!pointNavMesh.Enabled) continue;
                        float pointDistance = Vector3.Distance(pointNavMesh.Position, transform.position);
                        if (pointDistance < finishDistance) {
                            result = pointNavMesh.Position;
                            CurrentI = i;
                            CurrentJ = j;
                            finishDistance = pointDistance;
                        }
                    }
                }
                transform.position = result;
            }
            private void CalculatePath(Vector3 targetPos) {
                if (Path.Count > 0 && Vector3.Distance(Path.Last().Position, targetPos) <= 1.5f) return;
                Vector3 finishPos = GetNearPos(targetPos);
                if (Vector3.Distance(transform.position, finishPos) <= 1.5f) return;
                HashSet<Vector3> blacklist = new HashSet<Vector3>();
                Vector3 currentPos = CustomNavMesh[CurrentI][CurrentJ].Position, nextPos = Vector3.zero;
                int currentI = CurrentI, nextI, currentJ = CurrentJ, nextJ;
                List<PointPath> unsortedPath = Pool.Get<List<PointPath>>();
                int protection = 500;
                while (nextPos != finishPos && protection > 0) {
                    protection--;
                    FindNextPosToTarget(currentI, currentJ, blacklist, targetPos, out nextPos, out nextI, out nextJ);
                    if (nextPos == Vector3.zero) break;
                    blacklist.Add(currentPos);
                    currentPos = nextPos;
                    currentI = nextI;
                    currentJ = nextJ;
                    unsortedPath.Add(new PointPath {
                        Position = nextPos, I = nextI, J = nextJ
                    }
                    );
                }
                PointPath currentPoint = unsortedPath.Last();
                List<PointPath> reversePath = Pool.Get<List<PointPath>>();
                reversePath.Add(currentPoint);
                protection = 500;
                while ((Math.Abs(currentPoint.I - CurrentI) > 1 || Math.Abs(currentPoint.J - CurrentJ) > 1) && protection > 0) {
                    protection--;
                    PointPath nextPoint = null;
                    float finishDistance = float.PositiveInfinity;
                    foreach (PointPath point in unsortedPath) {
                        if (Math.Abs(point.I - currentPoint.I) > 1) continue;
                        if (Math.Abs(point.J - currentPoint.J) > 1) continue;
                        if (reversePath.Contains(point)) continue;
                        float pointDistance = Vector3.Distance(point.Position, transform.position);
                        if (pointDistance < finishDistance) {
                            nextPoint = point;
                            finishDistance = pointDistance;
                        }
                    }
                    if (nextPoint == null) break;
                    reversePath.Add(nextPoint);
                    currentPoint = nextPoint;
                }
                Pool.FreeUnmanaged(ref unsortedPath);
                Path.Clear();
                for (int i = reversePath.Count - 1; i >= 0; i--) Path.Add(reversePath[i]);
                Pool.FreeUnmanaged(ref reversePath);
                if (!NavMeshController.enabled && Path.Count > 0) NavMeshController.enabled = true;
            }
            private void FindNextPosToTarget(int currentI, int currentJ, ICollection<Vector3> blacklist, Vector3 targetPos, out Vector3 nextPos, out int nextI, out int nextJ) {
                nextPos = Vector3.zero;
                nextI = 0;
                nextJ = 0;
                float finishDistance = float.PositiveInfinity, pointDistance;
                if (currentI > 0) {
                    if (currentJ + 1 < CustomNavMesh[currentI - 1].Count) {
                        PointNavMesh point1 = CustomNavMesh[currentI - 1][currentJ + 1];
                        if (IsPointEnabled(point1, blacklist)) {
                            pointDistance = Vector3.Distance(point1.Position, targetPos);
                            if (pointDistance < finishDistance) {
                                nextPos = point1.Position;
                                nextI = currentI - 1;
                                nextJ = currentJ + 1;
                                finishDistance = pointDistance;
                            }
                        }
                    }
                    PointNavMesh point4 = CustomNavMesh[currentI - 1][currentJ];
                    if (IsPointEnabled(point4, blacklist)) {
                        pointDistance = Vector3.Distance(point4.Position, targetPos);
                        if (pointDistance < finishDistance) {
                            nextPos = point4.Position;
                            nextI = currentI - 1;
                            nextJ = currentJ;
                            finishDistance = pointDistance;
                        }
                    }
                    if (currentJ > 0) {
                        PointNavMesh point6 = CustomNavMesh[currentI - 1][currentJ - 1];
                        if (IsPointEnabled(point6, blacklist)) {
                            pointDistance = Vector3.Distance(point6.Position, targetPos);
                            if (pointDistance < finishDistance) {
                                nextPos = point6.Position;
                                nextI = currentI - 1;
                                nextJ = currentJ - 1;
                                finishDistance = pointDistance;
                            }
                        }
                    }
                }
                if (currentJ + 1 < CustomNavMesh[currentI].Count) {
                    PointNavMesh point2 = CustomNavMesh[currentI][currentJ + 1];
                    if (IsPointEnabled(point2, blacklist)) {
                        pointDistance = Vector3.Distance(point2.Position, targetPos);
                        if (pointDistance < finishDistance) {
                            nextPos = point2.Position;
                            nextI = currentI;
                            nextJ = currentJ + 1;
                            finishDistance = pointDistance;
                        }
                    }
                }
                if (currentJ > 0) {
                    PointNavMesh point7 = CustomNavMesh[currentI][currentJ - 1];
                    if (IsPointEnabled(point7, blacklist)) {
                        pointDistance = Vector3.Distance(point7.Position, targetPos);
                        if (pointDistance < finishDistance) {
                            nextPos = point7.Position;
                            nextI = currentI;
                            nextJ = currentJ - 1;
                            finishDistance = pointDistance;
                        }
                    }
                }
                if (currentI + 1 < CustomNavMesh.Count) {
                    if (currentJ + 1 < CustomNavMesh[currentI + 1].Count) {
                        PointNavMesh point3 = CustomNavMesh[currentI + 1][currentJ + 1];
                        if (IsPointEnabled(point3, blacklist)) {
                            pointDistance = Vector3.Distance(point3.Position, targetPos);
                            if (pointDistance < finishDistance) {
                                nextPos = point3.Position;
                                nextI = currentI + 1;
                                nextJ = currentJ + 1;
                                finishDistance = pointDistance;
                            }
                        }
                    }
                    PointNavMesh point5 = CustomNavMesh[currentI + 1][currentJ];
                    if (IsPointEnabled(point5, blacklist)) {
                        pointDistance = Vector3.Distance(point5.Position, targetPos);
                        if (pointDistance < finishDistance) {
                            nextPos = point5.Position;
                            nextI = currentI + 1;
                            nextJ = currentJ;
                            finishDistance = pointDistance;
                        }
                    }
                    if (currentJ > 0) {
                        PointNavMesh point8 = CustomNavMesh[currentI + 1][currentJ - 1];
                        if (IsPointEnabled(point8, blacklist)) {
                            pointDistance = Vector3.Distance(point8.Position, targetPos);
                            if (pointDistance < finishDistance) {
                                nextPos = point8.Position;
                                nextI = currentI + 1;
                                nextJ = currentJ - 1;
                                finishDistance = pointDistance;
                            }
                        }
                    }
                }
            }
            private static bool IsPointEnabled(PointNavMesh point, ICollection<Vector3> blacklist) => point.Enabled && !blacklist.Contains(point.Position);
            private Vector3 GetNearPos(Vector3 targetPos) {
                Vector3 result = Vector3.zero;
                float finishDistance = float.PositiveInfinity;
                for (int i = 0; i < CustomNavMesh.Count; i++) {
                    for (int j = 0; j < CustomNavMesh[i].Count; j++) {
                        PointNavMesh pointNavMesh = CustomNavMesh[i][j];
                        if (!pointNavMesh.Enabled) continue;
                        float pointDistance = Vector3.Distance(pointNavMesh.Position, targetPos);
                        if (pointDistance < finishDistance) {
                            result = pointNavMesh.Position;
                            finishDistance = pointDistance;
                        }
                    }
                }
                return result;
            }
            internal class CustomNavMeshController : FacepunchBehaviour {
                private Vector3 _startPos;
                private Vector3 _finishPos;
                private float _secondsTaken;
                private float _secondsToTake;
                private float _waypointDone = 1f;
                internal float Speed;
                private CustomAnimalNpc _animal;
                private void Awake() {
                    _animal = GetComponent<CustomAnimalNpc>();
                }
                private void FixedUpdate() {
                    if (_waypointDone >= 1f) {
                        if (_animal.Path.Count == 0) {
                            enabled = false;
                            return;
                        }
                        _startPos = _animal.transform.position;
                        PointPath point = _animal.Path[0];
                        if (point.Position != _startPos) {
                            _animal.CurrentI = point.I;
                            _animal.CurrentJ = point.J;
                            _finishPos = point.Position;
                            _secondsTaken = 0f;
                            _secondsToTake = Vector3.Distance(_finishPos, _startPos) / Speed;
                            _waypointDone = 0f;
                        }
                        _animal.Path.RemoveAt(0);
                    }
                    if (_startPos != _finishPos) {
                        _secondsTaken += Time.deltaTime;
                        _waypointDone = Mathf.InverseLerp(0f, _secondsToTake, _secondsTaken);
                        _animal.transform.position = Vector3.Lerp(_startPos, _finishPos, _waypointDone);
                        if (!_animal.brain.Navigator.IsOverridingFacingDirection) _animal.brain.Navigator.SetFacingDirectionOverride((_finishPos - _startPos).normalized);
                    }
                    else _waypointDone = 1f;
                }
            }
#endregion Custom Move
#region Move
            internal void SetDestination(Vector3 pos, float radius, BaseNavigator.NavigationSpeed speed) {
                if (CustomNavMesh.Count > 0 && NavMeshController != null) {
                    CalculatePath(pos);
                    NavMeshController.Speed = brain.Navigator.Speed * brain.Navigator.GetSpeedFraction(speed);
                }
                else {
                    Vector3 sample = GetSamplePosition(pos, radius);
                    sample.y += 2f;
                    if (!sample.IsEqualVector3(brain.Navigator.Destination)) brain.Navigator.SetDestination(sample, speed);
                }
            }
            internal Vector3 GetSamplePosition(Vector3 source, float radius) {
                NavMeshHit navMeshHit;
                if (NavMesh.SamplePosition(source, out navMeshHit, radius, NavAgent.areaMask)) {
                    NavMeshPath path = new NavMeshPath();
                    if (NavMesh.CalculatePath(transform.position, navMeshHit.position, NavAgent.areaMask, path)) {
                        if (path.status == NavMeshPathStatus.PathComplete) return navMeshHit.position;
                        else return path.corners.Last();
                    }
                }
                return source;
            }
            internal Vector3 GetRandomPos(Vector3 source, float radius) {
                Vector2 vector2 = UnityEngine.Random.insideUnitCircle * radius;
                return source + new Vector3(vector2.x, 0f, vector2.y);
            }
            internal bool IsPath(Vector3 start, Vector3 finish) {
                if (NavAgent == null) return false;
                NavMeshHit navMeshHit;
                if (NavMesh.SamplePosition(finish, out navMeshHit, AttackRange, NavAgent.areaMask)) {
                    NavMeshPath path = new NavMeshPath();
                    if (NavMesh.CalculatePath(start, navMeshHit.position, NavAgent.areaMask, path)) {
                        if (path.status == NavMeshPathStatus.PathComplete) return Vector3.Distance(navMeshHit.position, finish) < AttackRange;
                        else return Vector3.Distance(path.corners.Last(), finish) < AttackRange;
                    }
                    else return false;
                }
                else return false;
            }
            internal bool IsMoving => NavMeshController != null ? NavMeshController.enabled : brain.Navigator.Moving;
#endregion Move
#region States
            internal bool CanChaseState() {
                if (IsRunAwayWater) return false;
                if (DistanceFromBase > Config.ChaseRange) return false;
                if (AttackTarget == null) return false;
                return true;
            }
            internal bool CanCombatState() {
                if (IsRunAwayWater) return false;
                if (!AttackReady()) return false;
                if (AttackTarget == null) return false;
                if (DistanceToTarget > AttackRange) return false;
                if (!CanSeeTarget(AttackTarget)) return false;
                return true;
            }
            internal bool CanDestroyState() {
                if (AttackTarget != null && CanSeeTarget(AttackTarget) && IsPath(transform.position, AttackTarget.transform.position) && DistanceToTarget < DistanceFromBase) return false;
                else return true;
            }
#endregion States
#region Sleep
            private void UpdateSleep() {
                if (!Config.CanSleep) return;
                bool sleep = Query.Server.PlayerGrid.Query(transform.position.x, transform.position.z, Config.SleepDistance, AIBrainSenses.playerQueryResults, x => x.IsPlayer() && !x.IsSleeping()) == 0;
                if (brain.sleeping == sleep) return;
                brain.sleeping = sleep;
                if (brain.sleeping) SetDestination(HomePosition, 2f, BaseNavigator.NavigationSpeed.Fast);
                else NavAgent.enabled = true;
            }
#endregion Sleep
        }
        public class CustomAnimalBrain : AnimalBrain {
            private CustomAnimalNpc _animal = null;
            public override void AddStates() {
                if (_animal == null) _animal = GetEntity() as CustomAnimalNpc;
                states = new Dictionary<AIState, BasicAIState>();
                if (_animal.Config.States.Contains("IdleState")) AddState(new IdleState(_animal));
                if (_animal.Config.States.Contains("RoamState")) AddState(new RoamState(_animal));
                if (_animal.Config.States.Contains("ChaseState")) AddState(new ChaseState(_animal));
                if (_animal.Config.States.Contains("CombatState")) AddState(new CombatState(_animal));
                if (_animal.Config.States.Contains("DestroyState")) AddState(new DestroyState(_animal));
            }
            public override void InitializeAI() {
                if (_animal == null) _animal = GetEntity() as CustomAnimalNpc;
                _animal.HasBrain = true;
                Navigator = GetComponent<BaseNavigator>();
                Navigator.Speed = _animal.Config.Speed;
                InvokeRandomized(DoMovementTick, 1f, 0.1f, 0.01f);
                AttackRangeMultiplier = 1f;
                MemoryDuration = _animal.Config.MemoryDuration;
                SenseRange = _animal.Config.SenseRange;
                TargetLostRange = SenseRange * 2f;
                VisionCone = Vector3.Dot(Vector3.forward, Quaternion.Euler(0f, _animal.Config.VisionCone, 0f) * Vector3.forward);
                CheckVisionCone = _animal.Config.CheckVisionCone;
                CheckLOS = true;
                IgnoreNonVisionSneakers = true;
                MaxGroupSize = 0;
                ListenRange = _animal.Config.ListenRange;
                HostileTargetsOnly = _animal.Config.HostileTargetsOnly;
                IgnoreSafeZonePlayers = !HostileTargetsOnly;
                SenseTypes = EntityType.Player;
                if (_ins._config.CanTargetNpc) SenseTypes |= EntityType.BasePlayerNPC;
                if (_ins._config.CanTargetAnimal) SenseTypes |= EntityType.NPC;
                RefreshKnownLOS = false;
                IgnoreNonVisionMaxDistance = ListenRange / 3f;
                IgnoreSneakersMaxDistance = IgnoreNonVisionMaxDistance / 3f;
                Senses.Init(_animal, this, MemoryDuration, SenseRange, TargetLostRange, VisionCone, CheckVisionCone, CheckLOS, IgnoreNonVisionSneakers, ListenRange, HostileTargetsOnly, false, IgnoreSafeZonePlayers, SenseTypes, RefreshKnownLOS);
                ThinkMode = AIThinkMode.Interval;
                thinkRate = 0.25f;
                PathFinder = new BasePathFinder();
            }
            public override void Think(float delta) {
                if (_animal == null) return;
                lastThinkTime = Time.time;
                if (sleeping) {
                    if (_animal.NavAgent.enabled && _animal.DistanceFromBase < _animal.Config.RoamRange) _animal.NavAgent.enabled = false;
                    return;
                }
                if (!_animal.IsRunAwayWater) {
                    Senses.Update();
                    _animal.UpdateTarget();
                }
                CurrentState?.StateThink(delta, this, _animal);
                float single = 0f;
                BasicAIState newState = null;
                foreach (BasicAIState value in states.Values) {
                    if (value == null) continue;
                    float weight = value.GetWeight();
                    if (weight < single) continue;
                    single = weight;
                    newState = value;
                }
                if (newState != CurrentState) {
                    CurrentState?.StateLeave(this, _animal);
                    CurrentState = newState;
                    CurrentState?.StateEnter(this, _animal);
                }
            }
            public new class IdleState : BasicAIState {
                private readonly CustomAnimalNpc _animal;
                public IdleState(CustomAnimalNpc animal) : base(AIState.Idle) {
                    _animal = animal;
                }
                public override float GetWeight() => 10f;
            }
            public new class RoamState : BasicAIState {
                private readonly CustomAnimalNpc _animal;
                public RoamState(CustomAnimalNpc animal) : base(AIState.Roam) {
                    _animal = animal;
                }
                public override float GetWeight() => 20f;
                public override StateStatus StateThink(float delta, BaseAIBrain brain, BaseEntity entity) {
                    if (_animal.DistanceFromBase > _animal.Config.RoamRange) _animal.SetDestination(_animal.HomePosition, 2f, _animal.Config.RoamRange == 0f && _animal.DistanceFromBase < 10f ? BaseNavigator.NavigationSpeed.Slowest : BaseNavigator.NavigationSpeed.Fast);
                    else if (!_animal.IsMoving && _animal.Config.RoamRange > 2f) _animal.SetDestination(_animal.GetRandomPos(_animal.HomePosition, _animal.Config.RoamRange - 2f), 2f, BaseNavigator.NavigationSpeed.Slowest);
                    return StateStatus.Running;
                }
            }
            public new class ChaseState : BasicAIState {
                private readonly CustomAnimalNpc _animal;
                public ChaseState(CustomAnimalNpc animal) : base(AIState.Chase) {
                    _animal = animal;
                }
                public override float GetWeight() => _animal.CanChaseState() ? 30f : 0f;
                public override StateStatus StateThink(float delta, BaseAIBrain brain, BaseEntity entity) {
                    if (_animal.AttackTarget == null) return StateStatus.Error;
                    _animal.SetDestination(_animal.AttackTarget.transform.position, 2f, BaseNavigator.NavigationSpeed.Fast);
                    return StateStatus.Running;
                }
            }
            public new class CombatState : BasicAIState {
                private readonly CustomAnimalNpc _animal;
                public CombatState(CustomAnimalNpc animal) : base(AIState.Combat) {
                    _animal = animal;
                }
                public override float GetWeight() => _animal.CanCombatState() ? 40f : 0f;
                public override void StateLeave(BaseAIBrain brain, BaseEntity entity) => brain.Navigator.ClearFacingDirectionOverride();
                public override StateStatus StateThink(float delta, BaseAIBrain brain, BaseEntity entity) {
                    if (_animal.AttackTarget == null) return StateStatus.Error;
                    brain.Navigator.SetFacingDirectionEntity(_animal.AttackTarget);
                    _animal.nextAttackTime = Time.realtimeSinceStartup + _animal.AttackRate;
                    _animal.CombatTarget.Hurt(_animal.AttackDamage, _animal.AttackDamageType, _animal, true);
                    _animal.ClientRPC<Vector3>(null, "Attack", _animal.AttackTarget.transform.position);
                    _animal.SetDestination(_animal.AttackTarget.transform.position, 2f, BaseNavigator.NavigationSpeed.Fast);
                    return StateStatus.Running;
                }
            }
            public class DestroyState : BasicAIState {
                private readonly CustomAnimalNpc _animal;
                public DestroyState(CustomAnimalNpc animal) : base(AIState.Cooldown) {
                    _animal = animal;
                }
                public override float GetWeight() => _animal.CanDestroyState() ? 50f : 0f;
                public override void StateLeave(BaseAIBrain brain, BaseEntity entity) => brain.Navigator.ClearFacingDirectionOverride();
                public override StateStatus StateThink(float delta, BaseAIBrain brain, BaseEntity entity) {
                    if (_animal.DistanceFromBase <= _animal.AttackRange) {
                        brain.Navigator.SetFacingDirectionOverride((_animal.HomePosition - _animal.transform.position).normalized);
                        if (_animal.AttackReady()) {
                            _animal.nextAttackTime = Time.realtimeSinceStartup + _animal.AttackRate;
                            _animal.ClientRPC<Vector3>(null, "Attack", _animal.HomePosition);
                        }
                    }
                    else if (!_animal.IsMoving) _animal.SetDestination(GetMovePos(), _animal.AttackRange / 2f, BaseNavigator.NavigationSpeed.Fast);
                    return StateStatus.Running;
                }
                private Vector3 GetMovePos() {
                    Vector3 normal3 = (_animal.transform.position - _animal.HomePosition).normalized;
                    Vector2 vector2 = new Vector2(normal3.x, normal3.z) * _animal.AttackRange / 2f;
                    return _animal.HomePosition + new Vector3(vector2.x, 0f, vector2.y);
                }
            }
        }
#endregion Controller
#region Oxide Hooks
        private static AnimalSpawn _ins;
        private void Init() => _ins = this;
        private void OnServerInitialized() {
            CreateAllFolders();
            LoadPresets();
            LoadNavMeshes();
            CheckVersionPlugin();
        }
        private void Unload() {
            foreach (CustomAnimalNpc animal in _animals.Values) if (animal.IsExists()) animal.Kill();
            _ins = null;
        }
        private void OnEntityKill(CustomAnimalNpc animal) {
            if (animal != null && animal.net != null && _animals.ContainsKey(animal.net.ID.Value)) _animals.Remove(animal.net.ID.Value);
        }
        private object OnNpcTarget(BaseAnimalNPC attacker, CustomAnimalNpc victim) {
            if (attacker == null || !IsCustomAnimal(victim)) return null;
            if (_config.CanTargetAnimal) return null;
            else return true;
        }
        private object OnNpcTarget(NPCPlayer attacker, CustomAnimalNpc victim) {
            if (attacker == null || !IsCustomAnimal(victim)) return null;
            if (_config.CanTargetNpc) return null;
            else return true;
        }
        private object OnEntityTakeDamage(BaseCombatEntity victim, HitInfo info) {
            if (victim == null || info == null) return null;
            BaseEntity attacker = info.Initiator;
            if (IsCustomAnimal(victim)) {
                if (attacker == null || attacker.skinID == 11491311214163) return true;
                CustomAnimalNpc victimAnimal = victim as CustomAnimalNpc;
                if (attacker is AutoTurret || attacker is GunTrap || attacker is FlameTurret) {
                    info.damageTypes.ScaleAll(victimAnimal.Config.TurretDamageScale);
                    return null;
                }
                if (attacker is BasePlayer) {
                    BasePlayer attackerBasePlayer = attacker as BasePlayer;
                    if (attackerBasePlayer.userID.IsSteamId()) {
                        if (victimAnimal.CanTargetPlayer(attackerBasePlayer)) victimAnimal.brain.Senses.Memory.SetKnown(attackerBasePlayer, victimAnimal, victimAnimal.brain.Senses);
                        return null;
                    }
                    if (attackerBasePlayer is NPCPlayer) {
                        NPCPlayer attackerNpcPlayer = attackerBasePlayer as NPCPlayer;
                        if (victimAnimal.CanTargetNpcPlayer(attackerNpcPlayer)) {
                            victimAnimal.brain.Senses.Memory.SetKnown(attackerNpcPlayer, victimAnimal, victimAnimal.brain.Senses);
                            return null;
                       
// ... [Code preview truncated due to length. Full source in attached file] ...

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