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

FREE HumanNPC : 0.4.1 0.4.1

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Requires: PathFinding
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Game.Rust;
using Rust;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using UnityEngine;
using Convert = System.Convert;
namespace Oxide.Plugins {
    [Info("HumanNPC", "Reneb/Nogrod/Calytic/RFC1920/Nikedemos", "0.4.1", ResourceId = 856)]
    [Description("Adds interactive Human NPCs which can be modded by other plugins")]
    public class HumanNPC : RustPlugin {
        //////////////////////////////////////////////////////
        /// <summary>
        /// Fields
        /// </summary>
        //////////////////////////////////////////////////////
        private static Collider[] colBuffer;
        private int playerLayer;
        private static int targetLayer;
        private static Vector3 Vector3Down;
        private static int groundLayer;
        private Hash<ulong, HumanNPCInfo> humannpcs = new();
        // Nikedemos
        private Hash<ulong, HumanNPCTeamInfo> humannpcteams = new();
        //private FoFLookup fofLookup = new FoFLookup(); //constructor is empty, don't generate yet, wait for all the NPCTeamInfo to be populated first!
        public static readonly Dictionary<HumanNPCAlignment, string> FoFEnumToString = new();
        public static readonly Dictionary<string, HumanNPCAlignment> FoFStringToEnum = new();
        // Nikedemos
        private static readonly int playerMask = LayerMask.GetMask("Player (Server)");
        //static int obstructionMask = LayerMask.GetMask(new[] { "Player (Server)", "Construction", "Deployed", "Clutter" });
        private static readonly int obstructionMask = LayerMask.GetMask(new[] {
            "Construction", "Deployed", "Clutter"
        }
        );
        private static readonly int constructionMask = LayerMask.GetMask(new[] {
            "Construction", "Deployed", "Clutter"
        }
        );
        private static readonly int terrainMask = LayerMask.GetMask(new[] {
            "Terrain", "Tree"
        }
        );
        private bool save;
        private static bool debug;
        private bool relocateZero = true;
        private StoredData storedData;
        private DynamicConfigFile data;
        private Vector3 eyesPosition;
        private string chat = "<color=#FA58AC>{0}:</color> ";
        [PluginReference]
        private readonly Plugin Kits, Waypoints, Vanish;
        private static PathFinding PathFinding;
        private class StoredData {
            public HashSet<HumanNPCInfo> HumanNPCs = new();
            // Nikedemos
            public HashSet<HumanNPCTeamInfo> HumanNPCTeams = new();
        }
        public class WaypointInfo {
            public float Speed;
            public Vector3 Position;
            public WaypointInfo(Vector3 position, float speed) {
                Speed = speed;
                Position = position;
            }
        }
        public static bool IsLayerBlocked(Vector3 position, float radius, int mask) {
            List<Collider> colliders = Pool.Get<List<Collider>>();
            Vis.Colliders(position, radius, colliders, mask, QueryTriggerInteraction.Collide);
            bool blocked = colliders.Count > 0;
            Pool.FreeUnmanaged(ref colliders);
            return blocked;
        }
        //////////////////////////////////////////////////////
        /// <summary>
        /// class SpawnInfo
        ///  Spawn information, position & rotation
        ///  public => will be saved in the data file
        ///  non public => won't be saved in the data file
        /// </summary>
        //////////////////////////////////////////////////////
        public class SpawnInfo {
            public Vector3 position;
            public Quaternion rotation;
            public SpawnInfo(Vector3 position, Quaternion rotation) {
                this.position = position;
                this.rotation = rotation;
            }
            public string String() {
                return $"Pos{position} - Rot{rotation}";
            }
            public string ShortString() {
                return $"Pos({Math.Ceiling(position.x)},{Math.Ceiling(position.y)},{Math.Ceiling(position.z)})";
            }
        }
        //////////////////////////////////////////////////////
        /// <summary>
        /// class HumanTrigger
        /// MonoBehaviour: managed by UnityEngine
        ///  This takes care of all collisions and area management of humanNPCs
        /// </summary>
        //////////////////////////////////////////////////////
        public class HumanTrigger : MonoBehaviour {
            private HumanPlayer npc;
            private readonly HashSet<BasePlayer> triggerPlayers = new();
            private readonly HashSet<BaseAnimalNPC> triggerAnimals = new();
            public float collisionRadius;
            private void Awake() {
                npc = GetComponent<HumanPlayer>();
                collisionRadius = npc.info.collisionRadius;
                InvokeRepeating("UpdateTriggerArea", 2f, 1.5f);
            }
            private void OnDestroy() {
                CancelInvoke("UpdateTriggerArea");
            }
            private void UpdateTriggerArea() {
                HashSet<BasePlayer> collidePlayers = new();
                HashSet<BaseAnimalNPC> collideAnimals = new();
                List<BasePlayer> players = new();
                List<BaseAnimalNPC> animals = new();
                Vis.Entities(npc.player.transform.position, collisionRadius, players, targetLayer);
                Vis.Entities(npc.player.transform.position, collisionRadius, animals, targetLayer);
                foreach (BasePlayer player in players.Distinct().ToList()) {
                    //if (player.GetComponentInParent<HumanPlayer>()) continue;
                    collidePlayers.Add(player);
                    if (triggerPlayers.Add(player)) {
                        OnEnterCollision(player);
                    }
                    //if (debug) Interface.GetMod().LogInfo("UpdateTriggerArea: {0} found {1}", npc.player.displayName, player.displayName);
                }
                foreach (BaseAnimalNPC animal in animals.Distinct().ToList()) {
                    collideAnimals.Add(animal);
                    if (triggerAnimals.Add(animal)) {
                        OnEnterCollision(animal);
                    }
                    if (debug) Interface.GetMod().LogInfo("UpdateTriggerArea: {0} found {1}", npc.player.displayName, animal.ShortPrefabName);
                }
                HashSet<BasePlayer> removePlayers = new();
                foreach (BasePlayer player in triggerPlayers) {
                    if (!collidePlayers.Contains(player)) {
                        removePlayers.Add(player);
                    }
                }
                foreach (BasePlayer player in removePlayers) {
                    triggerPlayers.Remove(player);
                    OnLeaveCollision(player);
                }
                HashSet<BaseAnimalNPC> removeAnimals = new();
                foreach (BaseAnimalNPC animal in triggerAnimals) {
                    if (!collideAnimals.Contains(animal)) {
                        removeAnimals.Add(animal);
                    }
                }
                foreach (BaseAnimalNPC animal in removeAnimals) {
                    triggerAnimals.Remove(animal);
                    OnLeaveCollision(animal);
                }
            }
            private void OnEnterCollision(BasePlayer player) {
                Interface.GetMod().CallHook("OnEnterNPC", npc.player, player);
            }
            private void OnLeaveCollision(BasePlayer player) {
                Interface.GetMod().CallHook("OnLeaveNPC", npc.player, player);
            }
            private void OnEnterCollision(BaseAnimalNPC animal) {
                Interface.GetMod().CallHook("OnEnterNPC", npc.player, animal);
            }
            private void OnLeaveCollision(BaseAnimalNPC animal) {
                Interface.GetMod().CallHook("OnLeaveNPC", npc.player, animal);
            }
        }
        //////////////////////////////////////////////////////
        /// <summary>
        /// class HumanLocomotion
        /// MonoBehaviour: managed by UnityEngine
        ///  This takes care of all movements and attacks of HumanNPCs
        /// </summary>
        //////////////////////////////////////////////////////
        public class HumanLocomotion : MonoBehaviour {
            private HumanPlayer npc;
            public Vector3 StartPos = new(0f, 0f, 0f);
            public Vector3 EndPos = new(0f, 0f, 0f);
            public Vector3 LastPos = new(0f, 0f, 0f);
            private Vector3 nextPos = new(0f, 0f, 0f);
            private float waypointDone;
            public float secondsTaken;
            private float secondsToTake;
            public List<WaypointInfo> cachedWaypoints;
            private int currentWaypoint = -1;
            public float followDistance = 3.5f;
            private float lastHit;
            public int noPath;
            public bool shouldMove = true;
            private float startedReload;
            private float startedFollow;
            private bool reloading;
            public bool returning;
            public bool sitting;
            public bool isRiding;
            public BaseCombatEntity attackEntity;
            public BaseEntity followEntity;
            public Vector3 targetPosition = Vector3.zero;
            public List<Vector3> pathFinding;
            private HeldEntity firstWeapon;
            public void Awake() {
                npc = GetComponent<HumanPlayer>();
                UpdateWaypoints();
                npc.player.modelState.onground = true;
            }
            public void UpdateWaypoints() {
                if (string.IsNullOrEmpty(npc.info.waypoint)) {
                    return;
                }
                object cwaypoints = Interface.GetMod().CallHook("GetWaypointsList", npc.info.waypoint);
                if (cwaypoints == null) {
                    cachedWaypoints = null;
                }
                else {
                    cachedWaypoints = new List<WaypointInfo>();
                    Vector3 lastPos = npc.info.spawnInfo.position;
                    float speed = GetSpeed();
                    foreach (object cwaypoint in (List<object>)cwaypoints) {
                        foreach (KeyValuePair<Vector3, float> pair in (Dictionary<Vector3, float>)cwaypoint) {
                            if (HumanNPC.PathFinding == null) {
                                cachedWaypoints.Add(new WaypointInfo(pair.Key, pair.Value));
                                continue;
                            }
                            List<Vector3> temppathFinding = HumanNPC.PathFinding.Go(lastPos, pair.Key);
                            speed = pair.Value;
                            if (temppathFinding != null) {
                                lastPos = pair.Key;
                                foreach (Vector3 vector3 in temppathFinding) {
                                    cachedWaypoints.Add(new WaypointInfo(vector3, speed));
                                }
                            }
                            else {
                                if (debug) Interface.GetMod().LogInfo("Blocked waypoint? {0} for {1}, speed {2}", pair.Key, npc.player.displayName, pair.Value);
                                //cachedWaypoints.Add(new WaypointInfo(pair.Key, speed));
                            }
                        }
                    }
                    if (HumanNPC.PathFinding != null && lastPos != npc.info.spawnInfo.position) {
                        List<Vector3> temppathFinding = HumanNPC.PathFinding.Go(lastPos, npc.info.spawnInfo.position);
                        if (temppathFinding != null) {
                            foreach (Vector3 vector3 in temppathFinding) {
                                cachedWaypoints.Add(new WaypointInfo(vector3, speed));
                            }
                        }
                        else {
                            if (debug) Interface.GetMod().LogInfo("Blocked waypoint to spawn? {0} for {1}", lastPos, npc.player.displayName);
                        }
                    }
                    if (cachedWaypoints.Count == 0) {
                        cachedWaypoints = null;
                    }
                    if (debug) Interface.GetMod().LogInfo("Waypoints: {0} for {1}", cachedWaypoints.Count, npc.player.displayName);
                }
            }
            private void FixedUpdate() {
                TryToMove();
            }
            public void TryToMove() {
                if (npc.player.IsDead() || npc.player.IsWounded()) {
                    return;
                }
                if (attackEntity is BaseCombatEntity) {
                    if (debug) Interface.GetMod().LogInfo("TryToMove: ProcessAttack(attackEntity)");
                    ProcessAttack(attackEntity);
                }
                else if (followEntity is BaseEntity) {
                    if (debug) Interface.GetMod().LogInfo("TryToMove: ProcessFollow(followEntity)");
                    startedFollow = Time.realtimeSinceStartup;
                    ProcessFollow(followEntity.transform.position);
                }
                else if (secondsTaken == 0f) {
                    GetNextPath();
                }
                else if (targetPosition != Vector3.zero) // && !(followEntity as BaseCombatEntity).IsDead())
                {
                    if (debug) Interface.GetMod().LogInfo("TryToMove: ProcessFollow(target)");
                    startedFollow = Time.realtimeSinceStartup;
                    ProcessFollow(targetPosition);
                }
                if (StartPos != EndPos) {
                    Execute_Move();
                }
                if (waypointDone >= 1f) {
                    secondsTaken = 0f;
                }
            }
            private void Execute_Move() {
                if (!shouldMove) {
                    return;
                }
                secondsTaken += Time.deltaTime;
                waypointDone = Mathf.InverseLerp(0f, secondsToTake, secondsTaken);
                nextPos = Vector3.Lerp(StartPos, EndPos, waypointDone);
                nextPos.y = GetMoveY(nextPos);
                npc.player.MovePosition(nextPos);
                //npc.player.eyes.position = nextPos + new Vector3(0, 1.6f, 0);
                Vector3 newEyesPos = nextPos + new Vector3(0, 1.6f, 0);
                npc.player.eyes.position.Set(newEyesPos.x, newEyesPos.y, newEyesPos.z);
                npc.player.EnablePlayerCollider();
                npc.player.modelState.onground = !IsSwimming();
            }
            public void Evade() {
                if (IsSwimming()) {
                    return;
                }
                if (!npc.info.evade) {
                    return;
                }
                if (debug) Interface.GetMod().LogInfo("Evading...");
                //float evd = UnityEngine.Random.Range(-npc.info.evdist/2, npc.info.evdist/2);
                float evd = UnityEngine.Random.Range(-npc.info.evdist, npc.info.evdist);
                //Vector3 ev = new Vector3(UnityEngine.Random.Range(-npc.info.evdist, npc.info.evdist), 0, UnityEngine.Random.Range(-npc.info.evdist, npc.info.evdist));
                Vector3 ev = new(evd, 0, evd);
                Vector3 newpos = npc.player.transform.position + ev;
                if (debug) Interface.GetMod().LogInfo($"  first trying new position {newpos.ToString()}");
                RaycastHit hitinfo;
                int i = 0;
                while (Physics.OverlapSphere(newpos, npc.info.evdist, constructionMask) != null) {
                    newpos.x += UnityEngine.Random.Range(-0.2f, 0.2f);
                    newpos.y += UnityEngine.Random.Range(-0.1f, 0.1f);
                    newpos.z += UnityEngine.Random.Range(-0.2f, 0.2f);
                    if (debug) Interface.GetMod().LogInfo($"  trying new position {newpos.ToString()}");
                    if (Physics.Raycast(newpos, Vector3Down, out hitinfo, 0.1f, groundLayer)) {
                        if (debug) Interface.GetMod().LogInfo($"  found ground or construction at {newpos.ToString()}");
                        break;
                    }
                    else {
                        newpos.y = npc.locomotion.GetGroundY(newpos);
                        if (debug) Interface.GetMod().LogInfo($"  fell through floor, relocating to {newpos.ToString()}");
                    }
                    i++;
                    if (i > 100) {
                        break;
                    }
                }
                npc.player.MovePosition(newpos);
            }
            public bool IsSwimming() {
                return WaterLevel.Test(npc.player.transform.position + new Vector3(0, 0.65f, 0), true, true);
            }
            private bool CanSit() {
                if (isRiding) {
                    return false;
                }
                return sitting ? false : npc.info.allowsit;
            }
            private bool CanRide() {
                return isRiding ? false : npc.info.allowride;
            }
            public void Sit() {
                if (sitting) {
                    return;
                }
                npc.Invoke("AllowMove", 0);
                // Find a place to sit
                List<BaseChair> chairs = new();
                List<StaticInstrument> pidrxy = new();
                Vis.Entities(npc.player.transform.position, 10f, chairs);
                Vis.Entities(npc.player.transform.position, 1f, pidrxy);
                foreach (BaseChair mountable in chairs.Distinct().ToList()) {
                    if (debug) Interface.GetMod().LogInfo($"HumanNPC {npc.player.displayName} trying to sit...");
                    if (mountable.IsMounted()) {
                        if (debug) Interface.GetMod().LogInfo($"Someone is sitting here.");
                        continue;
                    }
                    if (debug) Interface.GetMod().LogInfo($"Found an empty chair.");
                    mountable.MountPlayer(npc.player);
                    npc.player.OverrideViewAngles(mountable.mountAnchor.transform.rotation.eulerAngles);
                    npc.player.eyes.NetworkUpdate(mountable.mountAnchor.transform.rotation);
                    //npc.player.ClientRPCPlayer(null, npc.player, "ForcePositionTo", npc.player.transform.position);
                    npc.player.ClientRPC(RpcTarget.Player("ForcePositionTo", npc.player), npc.player.transform.position);
                    mountable.SetFlag(BaseEntity.Flags.Busy, true, false);
                    sitting = true;
                    break;
                }
                foreach (StaticInstrument mountable in pidrxy.Distinct().ToList()) {
                    if (debug) Interface.GetMod().LogInfo($"HumanNPC {npc.player.displayName} trying to sit at instrument");
                    if (mountable.IsMounted()) {
                        if (debug) Interface.GetMod().LogInfo($"Someone is sitting here.");
                        continue;
                    }
                    if (debug) Interface.GetMod().LogInfo($"Found an empty instrument.");
                    mountable.MountPlayer(npc.player);
                    npc.player.OverrideViewAngles(mountable.mountAnchor.transform.rotation.eulerAngles);
                    npc.player.eyes.NetworkUpdate(mountable.mountAnchor.transform.rotation);
                    //npc.player.ClientRPCPlayer(null, npc.player, "ForcePositionTo", npc.player.transform.position);
                    npc.player.ClientRPC(RpcTarget.Player("ForcePositionTo", npc.player), npc.player.transform.position);
                    mountable.SetFlag(BaseEntity.Flags.Busy, true, false);
                    sitting = true;
                    if (debug) Interface.GetMod().LogInfo($"Setting instrument for {npc.player.displayName} to {mountable.ShortPrefabName}");
                    npc.info.instrument = mountable.ShortPrefabName;
                    npc.ktool = mountable;
                    //.GetParentEntity() as StaticInstrument;
                    break;
                }
            }
            public void Stand() {
                //if (CanSit() && sitting)
                if (sitting) {
                    if (debug) Interface.GetMod().LogInfo($"HumanNPC {npc.player.displayName} trying to stand...");
                    //                    npc.Invoke("AllowMove",0);
                    BaseMountable mounted = npc.player.GetMounted();
                    mounted.DismountPlayer(npc.player);
                    mounted.SetFlag(BaseEntity.Flags.Busy, false, false);
                    sitting = false;
                }
            }
            public void Ride() {
                // if (debug)                Interface.GetMod().LogInfo($"Ride() invoked for {npc.player.displayName}.");
                if (!npc.info.allowride) {
                    return;
                }
                RidableHorse horse = npc.player.GetMountedVehicle() as RidableHorse;
                if (horse == null) {
                    // Find a place to sit
                    List<RidableHorse> horses = new();
                    Vis.Entities(npc.player.transform.position, 15f, horses);
                    foreach (RidableHorse mountable in horses.Distinct().ToList()) {
                        if (debug) Interface.GetMod().LogInfo($"HumanNPC {npc.player.displayName} trying to ride...");
                        if (mountable.GetMounted() != null) {
                            if (debug) Interface.GetMod().LogInfo($"Someone is riding it.");
                            continue;
                        }
                        if (debug) Interface.GetMod().LogInfo($"Found an available horse.");
                        mountable.AttemptMount(npc.player);
                        npc.player.SetParent(mountable, true, true);
                        isRiding = true;
                        break;
                    }
                }
                if (horse == null) {
                    isRiding = false;
                    npc.player.SetParent(null, true, true);
                    npc.locomotion.PathFinding();
                    return;
                }
                Vector3 targetDir = new();
                Vector3 targetLoc = new();
                Vector3 targetHorsePos = new();
                float distance = 0f;
                bool rand = true;
                if (attackEntity != null) {
                    distance = Vector3.Distance(npc.player.transform.position, attackEntity.transform.position);
                    targetDir = attackEntity.transform.position;
                    if (debug) Interface.GetMod().LogInfo($"Riding towards attackEntity at {attackEntity.transform.position.ToString()}");
                }
                else {
                    distance = Vector3.Distance(npc.player.transform.position, StartPos);
                    targetDir = StartPos - horse.transform.position;
                    //                    rand = true;
                    if (debug) Interface.GetMod().LogInfo($"Riding towards nowhere in particular...");
                }
                bool hasMoved = targetDir != Vector3.zero && Vector3.Distance(horse.transform.position, npc.player.transform.position) > 0.5f;
                BasePlayer aepl = attackEntity as BasePlayer;
                bool isVisible = attackEntity?.IsVisible(npc.player.eyes.position, aepl.eyes.position, 200) == true;
                Vector2 randompos = UnityEngine.Random.insideUnitCircle * npc.info.damageDistance;
                if (attackEntity != null) {
                    if (isVisible) {
                        targetLoc = attackEntity.transform.position;
                        rand = false;
                    }
                    else {
                        if (Vector3.Distance(npc.player.transform.position, targetHorsePos) > 10 && !hasMoved) {
                            attackEntity = null;
                            targetLoc = new Vector3(randompos.x, 0, randompos.y);
                            targetLoc += npc.info.spawnInfo.position;
                            targetHorsePos = targetLoc;
                        }
                        else {
                            targetLoc = attackEntity.transform.position;
                        }
                    }
                }
                else {
                    if (Vector3.Distance(npc.player.transform.position, targetHorsePos) > 10 && hasMoved) {
                        targetLoc = targetHorsePos;
                    }
                    else {
                        targetLoc = new Vector3(randompos.x, 0, randompos.y);
                        targetLoc += npc.player.transform.position;
                        targetHorsePos = targetLoc;
                    }
                }
                float angle = Vector3.SignedAngle(targetDir, horse.transform.forward, Vector3.up);
                //float angle = Vector3.SignedAngle(npc.player.transform.forward, targetDir, Vector3.forward);
                //float angle = Vector3.SignedAngle(targetDir, horse.transform.forward, Vector3.forward);
                InputMessage message = new() {
                    buttons = 0
                }
                ;
                if (distance > npc.info.damageDistance) {
                    message.buttons = 2;
                    // FORWARD
                }
                if (distance > 40 && !rand) {
                    message.buttons = 130;
                    // SPRINT FORWARD
                }
                //if (horse.currentRunState == BaseRidableAnimal.RunState.sprint && distance < npc.info.maxDistance)
                if (horse.IsMoving()) {
                    message.buttons = 0;
                    // STOP ?
                }
                if (angle > 30 && angle < 180) {
                    message.buttons += 8;
                    // LEFT
                }
                if (angle < -30 && angle > -180) {
                    message.buttons += 16;
                    // RIGHT
                }
                if (debug) Interface.GetMod().LogInfo($"Sending input to horse: {message.buttons.ToString()}");
                horse.PlayerServerInput(new InputState() {
                    current = message
                }
                , npc.player);
            }
            private float GetSpeed(float speed = -1) {
                if (sitting) {
                    speed = 0;
                }
                if (returning) {
                    speed = 7;
                }
                else if (speed == -1) {
                    speed = npc.info.speed;
                }
                if (IsSwimming()) {
                    speed /= 2f;
                }
                return speed;
            }
            private void GetNextPath() {
                if (npc == null) {
                    npc = GetComponent<HumanPlayer>();
                }
                if (CanSit() && !sitting) {
                    Sit();
                }
                else if (CanRide()) // && isRiding == false)
                {
                    InvokeRepeating("Ride", 0, 0.5f);
                }
                LastPos = Vector3.zero;
                if (cachedWaypoints == null) {
                    shouldMove = false;
                    return;
                }
                shouldMove = true;
                Interface.GetMod().CallHook("OnNPCPosition", npc.player, npc.player.transform.position);
                if (currentWaypoint + 1 >= cachedWaypoints.Count) {
                    UpdateWaypoints();
                    currentWaypoint = -1;
                }
                if (cachedWaypoints == null) {
                    shouldMove = false;
                    return;
                }
                currentWaypoint++;
                WaypointInfo wp = cachedWaypoints[currentWaypoint];
                SetMovementPoint(npc.player.transform.position, wp.Position, GetSpeed(wp.Speed));
                if (npc.player.transform.position == wp.Position) {
                    npc.DisableMove();
                    npc.Invoke("AllowMove", GetSpeed(wp.Speed));
                }
            }
            public void SetMovementPoint(Vector3 startpos, Vector3 endpos, float s) {
                StartPos = startpos;
                if (endpos != Vector3.zero) {
                    EndPos = endpos;
                    EndPos.y = Math.Max(EndPos.y, TerrainMeta.HeightMap.GetHeight(EndPos));
                    if (StartPos != EndPos) {
                        secondsToTake = Vector3.Distance(EndPos, StartPos) / s;
                    }
                    npc.LookTowards(EndPos);
                }
                else {
                    if (IsInvoking("PathFinding")) {
                        CancelInvoke("PathFinding");
                    }
                }
                secondsTaken = 0f;
                waypointDone = 0f;
            }
            private bool HitChance(float chance = -1f) {
                if (chance < 0) {
                    chance = npc.info.hitchance;
                }
                if (chance > 1) {
                    chance = 0.75f;
                }
                return UnityEngine.Random.Range(1, 100) < (int)(chance * 100);
            }
            private void Move(Vector3 position, float speed = -1) {
                if (speed == -1) {
                    speed = npc.info.speed;
                }
                if (waypointDone >= 1f) {
                    if (pathFinding?.Count > 0) {
                        pathFinding.RemoveAt(pathFinding.Count - 1);
                    }
                    waypointDone = 0f;
                }
                if (pathFinding == null || pathFinding.Count < 1) {
                    return;
                }
                shouldMove = true;
                if (waypointDone == 0f) {
                    SetMovementPoint(position, pathFinding[pathFinding.Count - 1], GetSpeed(speed));
                }
            }
            private void ProcessAttack(BaseCombatEntity entity) {
                if (debug) Interface.GetMod().LogInfo("ProcessAttack: {0} -> {1}", npc.player.displayName, entity.name);
                float c_attackDistance = 0f;
                if (entity?.IsAlive() == true) {
                    if (entity is BaseAnimalNPC) {
                        c_attackDistance = Vector3.Distance(entity.transform.position + new Vector3(0, 1.6f, 0), npc.player.transform.position + new Vector3(0, 0.3f, 0));
                    }
                    else {
                        c_attackDistance = Vector3.Distance(entity.transform.position + new Vector3(0, 1.6f, 0), npc.player.transform.position + new Vector3(0, 1.6f, 0));
                    }
                    shouldMove = false;
                    bool validAttack = Vector3.Distance(LastPos, npc.player.transform.position) < npc.info.maxDistance && noPath < 5;
                    if (debug) Interface.GetMod().LogInfo("  Entity: Type {0}, alive {1}, valid {2}, distance {3}, noPath {4}", entity.GetType().FullName, entity.IsAlive(), validAttack, c_attackDistance.ToString(), noPath.ToString());
                    if (validAttack) {
                        bool range = false;
                        if (npc.info.follow) {
                            range = c_attackDistance < npc.info.damageDistance;
                        }
                        else {
                            range = c_attackDistance < npc.info.maxDistance;
                        }
                        bool see = CanSee(npc, entity);
                        if (debug) Interface.GetMod().LogInfo("  validAttack Entity: Type {0}, ranged {1}, cansee {2}", entity.GetType().FullName, range, see);
                        if (range && see) {
                            AttemptAttack(entity);
                            return;
                        }
                        if (GetSpeed() <= 0) {
                            npc.EndAttackingEntity();
                            npc.EndFollowingEntity();
                        }
                        else if (npc.info.follow) {
                            Move(npc.player.transform.position);
                        }
                    }
                    else {
                        npc.EndFollowingEntity();
                        npc.EndAttackingEntity();
                    }
                }
                else {
                    npc.EndAttackingEntity();
                }
            }
            public void ProcessFollow(Vector3 target) {
                //startedFollow += Time.deltaTime;
                if (Time.realtimeSinceStartup - startedFollow > npc.info.followtime) {
                    if (debug) Interface.GetMod().LogInfo($"ProcessFollow() Took too long...");
                    npc.EndFollowingEntity(noPath < 5);
                    return;
                }
                if (debug) Interface.GetMod().LogInfo($"ProcessFollow() called for {target.ToString()}");
                float c_followDistance = Vector3.Distance(target, npc.player.transform.position);
                shouldMove = false;
                if (debug) Interface.GetMod().LogInfo($"ProcessFollow() distance {c_followDistance.ToString()}");
                if (followEntity == null) {
                    npc.EndFollowingEntity(false);
                    return;
                }
                BaseCombatEntity fbce = followEntity as BaseCombatEntity;
                if (fbce.IsDead()) {
                    npc.EndFollowingEntity(false);
                    return;
                }
                //if (c_followDistance > 0)// && Vector3.Distance(LastPos, npc.player.transform.position) < followDistance)// && noPath < 5)
                if (c_followDistance > followDistance && Vector3.Distance(LastPos, npc.player.transform.position) < npc.info.maxDistance && noPath < 5) {
                    Move(npc.player.transform.position, npc.info.speed);
                }
                else {
                    if (followEntity is BaseEntity) {
                        if (debug) Interface.GetMod().LogInfo($"ProcessFollow() bailing out - is BaseEntity");
                        npc.EndFollowingEntity(noPath < 5);
                    }
                    else if (targetPosition != Vector3.zero) {
                        if (debug) Interface.GetMod().LogInfo($"ProcessFollow() bailing out");
                        npc.EndGo(noPath < 5);
                        npc.locomotion.GetBackToLastPos();
                    }
                }
            }
            public void PathFinding() {
                Vector3 target = Vector3.zero;
                if (attackEntity != null) {
                    //Vector3 diff = new Vector3(Core.Random.Range(-npc.info.attackDistance, npc.info.attackDistance), 0, Core.Random.Range(-npc.info.attackDistance, npc.info.attackDistance));
                    target = attackEntity.transform.position;
                    // + diff;
                }
                else if (followEntity != null) {
                    target = followEntity.transform.position;
                }
                else if (targetPosition != Vector3.zero) {
                    target = targetPosition;
                }
                if (target != Vector3.zero) {
                    PathFinding(new Vector3(target.x, GetMoveY(target), target.z));
                }
            }
            public void PathFinding(Vector3 targetPos) {
                if (gameObject == null) {
                    return;
                }
                if (IsInvoking("PathFinding")) {
                    CancelInvoke("PathFinding");
                }
                if (GetSpeed() <= 0) {
                    return;
                }
                List<Vector3> temppathFinding = HumanNPC.PathFinding?.Go(npc.player.transform.position, targetPos);
                if (temppathFinding == null) {
                    if (pathFinding == null || pathFinding.Count == 0) {
                        noPath++;
                    }
                    else {
                        noPath = 0;
                    }
                    if (noPath < 5) {
                        Invoke("PathFinding", 2);
                    }
                    else if (returning) {
                        returning = false;
                        SetMovementPoint(npc.player.transform.position, LastPos, 7f);
                        secondsTaken = 0.01f;
                    }
                }
                else {
                    noPath = 0;
                    pathFinding = temppathFinding;
                    pathFinding.Reverse();
                    waypointDone = 0f;
                    Invoke("PathFinding", pathFinding.Count / GetSpeed(npc.info.speed));
                }
            }
            public void GetBackToLastPos() {
                if (npc.player.transform.position == LastPos) {
                    return;
                }
                if (LastPos == Vector3.zero) {
                    LastPos = npc.info.spawnInfo.position;
                }
                if (Vector3.Distance(npc.player.transform.position, LastPos) < 5) {
                    SetMovementPoint(npc.player.transform.position, LastPos, 7f);
                    secondsTaken = 0.01f;
                    return;
                }
                returning = true;
                npc.StartGo(LastPos);
            }
            public void Enable() {
                //if (GetSpeed() <= 0) return;
                enabled = true;
            }
            public void Disable() {
                enabled = false;
            }
            public float GetMoveY(Vector3 position) {
                if (IsSwimming()) {
                    float point = TerrainMeta.WaterMap.GetHeight(position) - 0.65f;
                    float groundY = GetGroundY(position);
                    return groundY > point ? groundY : point - 0.65f;
                }
                return GetGroundY(position);
            }
            public float GetGroundY(Vector3 position) {
                position += Vector3.up;
                RaycastHit hitinfo;
                return Physics.Raycast(position, Vector3Down, out hitinfo, 100f, groundLayer) ? hitinfo.point.y : position.y -.5f;
            }
            public void CreateProjectileEffect(BaseCombatEntity target, BaseProjectile baseProjectile, float dmg, bool miss = false) {
                if (baseProjectile.primaryMagazine.contents <= 0) {
                    if (debug) Interface.GetMod().LogInfo("Attack failed(empty): {0} - {1}", npc.player.displayName, attackEntity.name);
                    return;
                }
                ItemModProjectile component = baseProjectile.primaryMagazine.ammoType.GetComponent<ItemModProjectile>();
                if (component == null) {
                    if (debug) Interface.GetMod().LogInfo("Attack failed(Component): {0} - {1}", npc.player.displayName, attackEntity.name);
                    return;
                }
                npc.LookTowards(target.transform.position);
                Vector3 source = npc.player.transform.position + npc.player.GetOffset();
                if (baseProjectile.MuzzlePoint != null) {
                    source += Quaternion.LookRotation(target.transform.position - npc.player.transform.position) * baseProjectile.MuzzlePoint.position;
                }
                Vector3 dir = (target.transform.position + npc.player.GetOffset() - source).normalized;
                Vector3 vector32 = dir * (component.projectileVelocity * baseProjectile.projectileVelocityScale);
                Vector3 hit;
                RaycastHit raycastHit;
                if (Vector3.Distance(npc.player.transform.position, target.transform.position) < 0.5) {
                    hit = target.transform.position + npc.player.GetOffset();
                }
                else if (!Physics.SphereCast(source, .01f, vector32, out raycastHit, float.MaxValue, targetLayer)) {
                    if (debug) Interface.GetMod().LogInfo("Attack failed: {0} - {1}", npc.player.displayName, attackEntity.name);
                    return;
                }
                else {
                    hit = raycastHit.point;
                    target = raycastHit.GetCollider().GetComponent<BaseCombatEntity>();
                    if (debug) Interface.GetMod().LogInfo("Attack failed: {0} - {1}", raycastHit.GetCollider().name, (Layer)raycastHit.GetCollider().gameObject.layer);
                    miss = miss || target == null;
                }
                baseProjectile.primaryMagazine.contents--;
                npc.ForceSignalAttack();
                if (miss) {
                    float aimCone = baseProjectile.GetAimCone();
                    vector32 += Quaternion.Euler(UnityEngine.Random.Range((float)(-aimCone * 0.5), aimCone * 0.5f), UnityEngine.Random.Range((float)(-aimCone * 0.5), aimCone * 0.5f), UnityEngine.Random.Range((float)(-aimCone * 0.5), aimCone * 0.5f)) * npc.player.eyes.HeadForward();
                }
                Effect.server.Run(baseProjectile.attackFX.resourcePath, baseProjectile, StringPool.Get(baseProjectile.handBone), Vector3.zero, Vector3.forward);
                Effect effect = new();
                effect.Init(Effect.Type.Projectile, source, vector32.normalized);
                effect.scale = vector32.magnitude;
                effect.pooledString = component.projectileObject.resourcePath;
                effect.number = UnityEngine.Random.Range(0, 2147483647);
                EffectNetwork.Send(effect);
                Vector3 dest;
                if (miss) {
                    dmg = 0;
                    dest = hit;
                }
                else {
                    dest = target.transform.position;
                }
                HitInfo hitInfo = new(npc.player, target, DamageType.Bullet, dmg, dest) {
                    DidHit = !miss, HitEntity = target, PointStart = source, PointEnd = hit, HitPositionWorld = dest, HitNormalWorld = -dir, WeaponPrefab = GameManager.server.FindPrefab(StringPool.Get(baseProjectile.prefabID)).GetComponent<AttackEntity>(), Weapon = (AttackEntity)firstWeapon, HitMaterial = StringPool.Get("Flesh")
                }
                ;
                target?.OnAttacked(hitInfo);
                Effect.server.ImpactEffect(hitInfo);
            }
            public void AttemptAttack(BaseCombatEntity entity) {
                BaseProjectile weapon = firstWeapon as BaseProjectile;
                if (weapon != null) {
                    if (!reloading && weapon.primaryMagazine.contents <= 0) {
                        reloading = true;
                        npc.player.SignalBroadcast(BaseEntity.Signal.Reload, string.Empty);
                        startedReload = Time.realtimeSinceStartup;
                        return;
                    }
                    if (reloading && Time.realtimeSinceStartup > startedReload + (npc.info.reloadDuration > 0 ? npc.info.reloadDuration : weapon.reloadTime)) {
                        reloading = false;
                        if (npc.info.needsAmmo) {
                            //weapon.primaryMagazine.Reload(npc.player);
                            weapon.TryReloadMagazine((IAmmoContainer)weapon.primaryMagazine);
                            npc.player.inventory.ServerUpdate(0f);
                        }
                        else {
                            weapon.primaryMagazine.contents = weapon.primaryMagazine.capacity;
                        }
                    }
                    if (reloading) {
                        return;
                    }
                }
                if (!(Time.realtimeSinceStartup > lastHit + npc.info.damageInterval)) {
                    return;
                }
                lastHit = Time.realtimeSinceStartup;
                DoAttack(entity, !HitChance());
            }
            public void DoAttack(BaseCombatEntity target, bool miss = false) {
                if (npc == null) {
                    return;
                }
                BaseProjectile weapon = firstWeapon as BaseProjectile;
                if (firstWeapon?.IsDestroyed != false || (weapon?.primaryMagazine.contents == 0)) {
                    firstWeapon = npc.EquipFirstWeapon();
                    weapon = firstWeapon as BaseProjectile;
                    npc.SetActive(0);
                }
                Item attackitem = firstWeapon?.GetItem();
                if (attackitem == null) {
                    npc.EndAttackingEntity();
                    return;
                }
                if (attackitem.uid != npc.player.svActiveItemID) {
                    npc.SetActive((uint)attackitem.uid.Value);
                }
                float dmg = npc.info.damageAmount * UnityEngine.Random.Range(0.8f, 1.2f);
                if (target is BaseNpc || target is BaseAnimalNPC) {
                    dmg *= 1.5f;
                }
                else if (target is AutoTurret) {
                    dmg *= 3f;
                }
                if (weapon != null) {
                    //npc.ForceSignalGesture();
                    CreateProjectileEffect(target, weapon, dmg, miss);
                }
                else {
                    HitInfo hitInfo = new(npc.player, target, DamageType.Stab, dmg, target.transform.position) {
                        PointStart = npc.player.transform.position, PointEnd = target.transform.position
                    }
                    ;
                    target.SendMessage("OnAttacked", hitInfo, SendMessageOptions.DontRequireReceiver);
                    npc.ForceSignalAttack();
                }
            }
        }
        //////////////////////////////////////////////////////
        /// <summary>
        /// class HumanPlayer : MonoBehaviour
        ///  MonoBehaviour: managed by UnityEngine
        /// Takes care of all the sub categories of the HumanNPCs
        /// </summary>
        //////////////////////////////////////////////////////
        public class HumanPlayer : MonoBehaviour {
            public HumanNPCInfo info;
            public HumanLocomotion locomotion;
            public HumanTrigger trigger;
            public ProtectionProperties protection;
            public InstrumentTool itool;
            public StaticInstrument ktool;
            public BasePlayer player;
            public float lastMessage;
            private void Awake() {
                player = GetComponent<BasePlay
// ... [Code preview truncated due to length. Full source in attached file] ...

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