Example #1
0
        public float GoTo(Vector3 position)
        {
            IsActionLocked = true;
            Timing.KillCoroutines(MovementCoroutines.ToArray());
            Vector3 heading = (position - NPCPlayer.Position);

            heading.y = 0;
            Quaternion lookRot = Quaternion.LookRotation(heading.normalized);
            float      dist    = heading.magnitude;

            NPCPlayer.Rotations = new Vector2(lookRot.eulerAngles.x, lookRot.eulerAngles.y);
            Move(MovementDirection.FORWARD);
            float eta = Plugin.Instance.Config.MovementUpdateFrequency * (dist / (NPCPlayer.CameraTransform.forward / 10 * MovementSpeed).magnitude);

            position.y = NPCPlayer.Position.y;
            MovementCoroutines.Add(Timing.CallDelayed(eta, () =>
            {
                Move(MovementDirection.NONE);
                if (Vector3.Distance(NPCPlayer.Position, position) >= 2f)
                {
                    NPCPlayer.ReferenceHub.playerMovementSync.OverridePosition(position, 0f, true);
                }
                IsActionLocked = false;
            }));
            return(eta);
        }
Example #2
0
 public void Stop()
 {
     ClearNavTargets();
     FollowTarget             = null;
     CurrentAIRoomTarget      = null;
     CurrentAIItemNodeTarget  = null;
     CurrentAIItemGroupTarget = null;
     Timing.KillCoroutines(MovementCoroutines.ToArray());
     Move(MovementDirection.NONE);
 }
Example #3
0
        private void OnDestroy()
        {
            Dictionary.Remove(this.gameObject);
            Timing.KillCoroutines(MovementCoroutines.ToArray());
            Timing.KillCoroutines(AttachedCoroutines.ToArray());

            Log.SendRaw($"NPC {NPCPlayer.Nickname} ({NPCPlayer.Id}) deconstructed", ConsoleColor.Green);

            Player.IdsCache.Remove(NPCPlayer.Id);
            Player.Dictionary.Remove(NPCPlayer.GameObject);
        }
Example #4
0
        private IEnumerator <float> NavCoroutine()
        {
            Queue <Vector3> FollowTargetPosCache = new Queue <Vector3>();
            int             eta = 0;
            int             dormant_cache_update = 0;

            for (; ;)
            {
                if (FollowTarget != null)
                {
                    if (FollowTarget.IsAlive)
                    {
                        float dist = Vector3.Distance(FollowTarget.Position, NPCPlayer.Position);

                        //If we are far away...
                        if (dist >= Plugin.Instance.Config.MaxFollowDistance)
                        {
                            if (OnTargetLostBehaviour == TargetLostBehaviour.TELEPORT)
                            {
                                //... Teleport to player if allowed
                                NPCPlayer.Position = FollowTarget.Position;
                                eta = 0;
                                FollowTargetPosCache.Clear();
                            }
                            else
                            {
                                //Stop or try to search otherwise

                                FireEvent(new NPCTargetLostEvent(this, FollowTarget));

                                FollowTargetPosCache.Clear();
                                eta = 0;
                                Stop();
                                if (OnTargetLostBehaviour == TargetLostBehaviour.SEARCH)
                                {
                                    Room r = FollowTarget.CurrentRoom;
                                    if (r != null)
                                    {
                                        GotoRoom(r);
                                    }
                                }
                                continue;
                            }
                        }

                        //If target is not near
                        if (dist >= 1.5f)
                        {
                            //Update Pos cache each third tick
                            if (dormant_cache_update > 2)
                            {
                                FollowTargetPosCache.Enqueue(FollowTarget.Position);
                                dormant_cache_update = 0;
                            }
                            dormant_cache_update++;
                        }
                        else
                        {
                            //Otherwise just dont move
                            FollowTargetPosCache.Clear();
                            eta = 0;
                            Timing.KillCoroutines(MovementCoroutines.ToArray());
                            Move(MovementDirection.NONE);
                        }
                    }
                    else
                    {
                        // Target dead, reset
                        FollowTargetPosCache.Clear();
                        eta = 0;
                        FireEvent(new NPCFollowTargetDiedEvent(this, FollowTarget));
                        Stop();
                        continue;
                    }

                    //If we reached predicted target
                    if (eta <= 0)
                    {
                        //Schedule next position
                        if (!FollowTargetPosCache.IsEmpty())
                        {
                            float full_eta = GoTo(FollowTargetPosCache.Dequeue());
                            eta = (int)(full_eta / Plugin.Instance.Config.NavUpdateFrequency) - 1;
                        }
                    }
                    else
                    {
                        eta--;
                    }
                }
                else
                {
                    //Noone to follow, try taking nodes from nav queue

                    eta = 0;
                    FollowTargetPosCache.Clear();

                    if (CurrentNavTarget != null)
                    {
                        IsRunning = !DisableRun;
                        //There is current
                        float distance = Vector3.Distance(CurrentNavTarget.Position, NPCPlayer.Position);

                        if (distance < 3f && (!ProcessSCPLogic || NPCPlayer.Role != RoleType.Scp106))
                        {
                            //Try to open the door if there is one, so we wont collide with it
                            if (CurrentNavTarget.AttachedDoor != null && !CurrentNavTarget.AttachedDoor.NetworkisOpen)
                            {
                                ItemType prev = ItemHeld;
                                bool     open = NPCPlayer.IsBypassModeEnabled || (CurrentNavTarget.AttachedDoor.CheckpointDoor && NPCPlayer.ReferenceHub.characterClassManager.IsAnyScp());
                                if (!open)
                                {
                                    if (!CurrentNavTarget.AttachedDoor.CanBeOpenedWith(ItemHeld))
                                    {
                                        foreach (ItemType keycard in AvailableKeycards)
                                        {
                                            if (CurrentNavTarget.AttachedDoor.CanBeOpenedWith(keycard))
                                            {
                                                ItemHeld = keycard;
                                                open     = true;
                                                break;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        open = true;
                                    }
                                }
                                if (open)
                                {
                                    //All is good
                                    Timing.KillCoroutines(MovementCoroutines.ToArray());
                                    Move(MovementDirection.NONE);
                                    while (CurrentNavTarget.AttachedDoor.locked)
                                    {
                                        yield return(0f);
                                    }
                                    yield return(Timing.WaitForSeconds(0.2f));

                                    CurrentNavTarget.AttachedDoor.NetworkisOpen = true;
                                    yield return(Timing.WaitForSeconds(0.1f));

                                    GoTo(CurrentNavTarget.Position);
                                    ItemHeld = prev;
                                }
                                else
                                {
                                    //Stop otherwise
                                    Stop();
                                    continue;
                                }
                            }
                        }

                        if (distance < 6)
                        {
                            NavigationNode NextNavTarget = NavigationQueue.First?.Value;

                            NavigationNode lift_node = null;
                            if (NextNavTarget != null)
                            {
                                if (NextNavTarget.AttachedElevator != null)
                                {
                                    lift_node = NextNavTarget;
                                }
                            }
                            //If there is an elevator, try to use it
                            if (CurrentNavTarget.AttachedElevator == null && lift_node != null)
                            {
                                bool val = lift_node.AttachedElevator.Value.Value.IsClosed(lift_node.AttachedElevator.Value.Key);
                                if (val)
                                {
                                    lift_node.AttachedElevator.Value.Value.UseLift();

                                    Timing.KillCoroutines(MovementCoroutines.ToArray());
                                    Move(MovementDirection.NONE);

                                    while (!lift_node.AttachedElevator.Value.Value.operative)
                                    {
                                        yield return(0.0f);
                                    }

                                    GoTo(CurrentNavTarget.Position);
                                }
                            }
                        }

                        if (CurMovementDirection == MovementDirection.NONE)
                        {
                            //Target reached - force position to it so we wont stuck
                            Vector3 forced = new Vector3(CurrentNavTarget.Position.x, NPCPlayer.Position.y, CurrentNavTarget.Position.z);
                            NPCPlayer.ReferenceHub.playerMovementSync.OverridePosition(forced, 0f, true);

                            //If we have AI item target reached, try to find and take item
                            if (CurrentAIItemNodeTarget != null && CurrentAIItemNodeTarget == CurrentNavTarget)
                            {
                                CurrentAIItemNodeTarget = null;
                                IEnumerable <Pickup> pickups = FindObjectsOfType <Pickup>().Where(pk => Vector3.Distance(pk.Networkposition, NPCPlayer.Position) <= 5f);
                                foreach (Pickup p in pickups)
                                {
                                    if (Utils.Utils.CheckItemType(CurrentAIItemGroupTarget, p.ItemId))
                                    {
                                        yield return(Timing.WaitForSeconds(GoTo(p.position)));

                                        TakeItem(p);
                                        break;
                                    }
                                }
                                CurrentAIItemGroupTarget = null;
                            }
                            CurrentNavTarget = null;
                        }
                    }
                    else if (NavigationQueue.Count > 0)
                    {
                        //No current, but there are pending targets
                        CurrentNavTarget = NavigationQueue.First.Value;
                        NavigationQueue.RemoveFirst();
                        if (CurrentNavTarget.AttachedElevator != null && Math.Abs(CurrentNavTarget.AttachedElevator.Value.Key.target.position.y - NPCPlayer.Position.y) > 2f)
                        {
                            CurrentNavTarget.AttachedElevator.Value.Value.UseLift();
                            while (CurrentNavTarget.AttachedElevator.Value.Value.status == Lift.Status.Moving)
                            {
                                yield return(0f);
                            }

                            CurrentNavTarget = null;
                        }
                        else
                        {
                            GoTo(CurrentNavTarget.Position);
                        }
                    }
                    else if (CurrentAIRoomTarget != null)
                    {
                        //No current, no pending - room reached
                        CurrentAIRoomTarget = null;
                    }
                }
                yield return(Timing.WaitForSeconds(Plugin.Instance.Config.NavUpdateFrequency));
            }
        }