public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        if (currentCover != null)
        {
            Debug.Log("Enemy is taking cover");

            if (AIFunction.LineOfSight(currentCover.position, attacker, coverCriteria))
            {
                Debug.Log("Cover is compromised");
                // Reset and find a new cover point
                currentCover = null;
                //currentCover = FindCover(attacker, ai.na, coverCheckRadius, numberOfChecks, coverCriteria);
            }
        }

        if (currentCover == null)
        {
            currentCover = FindCover(attacker, ai.na, coverCheckRadius, numberOfChecks, coverCriteria);
        }

        if (currentCover != null)
        {
            Debug.Log("Setting destination for " + ai.name + " from TakeCover behaviour");
            ai.na.SetDestination(currentCover.position);
        }
    }
    Character AcquireTarget()
    {
        // Use Physics.OverlapSphere

        Collider[] thingsInEnvironment = Physics.OverlapSphere(LookOrigin, viewRange);
        foreach (Collider thing in thingsInEnvironment)
        {
            /*
             * if (AIFunction.LineOfSightCheckWithExceptions(LookOrigin, thing.transform.position, viewDetection, characterData.HealthData.hitboxes, thing))
             * {
             *
             * }
             */
            if (AIFunction.LineOfSight(LookOrigin, thing.transform, viewDetection))
            {
                //print("Line of sight established between agent and " + thing.name);
                Character targetCharacter = thing.transform.root.GetComponent <Character>();
                if (targetCharacter != null && characterData.HostileTowards(targetCharacter))
                {
                    return(targetCharacter);
                }
            }
        }

        return(null);
    }
    public NullableVector3 FindCover(Transform attacker, NavMeshAgent na, float coverCheckRadius, int numberOfChecks, LayerMask coverCriteria)
    {
        NullableVector3 cover     = null;
        NavMeshPath     coverPath = null;

        for (int i = 0; i < numberOfChecks; i++)
        {
            // Obtains a random position within a certain vicinity of the agent
            Vector3    randomPosition = ai.transform.position + Random.insideUnitSphere * coverCheckRadius;
            NavMeshHit coverCheck;
            // Checks if there is an actual point on the navmesh close to the randomly selected position
            if (NavMesh.SamplePosition(randomPosition, out coverCheck, na.height * 2, NavMesh.AllAreas))
            {
                if (AIFunction.LineOfSight(coverCheck.position, attacker, coverCriteria) == false) // If line of sight is not established
                {
                    // Ensures that the agent can actually move to the cover position.
                    NavMeshPath newPathToTest = new NavMeshPath();
                    if (na.CalculatePath(coverCheck.position, newPathToTest))
                    {
                        // Checks if the new cover position is easier to get to than the old one.
                        if (cover == null || AIFunction.NavMeshPathLength(newPathToTest) < AIFunction.NavMeshPathLength(coverPath)) // Use OR statement, and check navmesh path cost between transform.position and the cover point currently being checked.
                        {
                            // If so, new cover position is established, and navmesh path is stored for next comparison
                            cover     = new NullableVector3(coverCheck.position);
                            coverPath = newPathToTest;
                        }
                    }
                }
            }
        }

        return(cover);
    }
    void PursueTargetUpdate()
    {
        if (currentTarget == null) // If the AI has not acquired a target, check for one
        {
            currentTarget = AcquireTarget();
        }

        if (currentTarget != null) // If the AI has a target, check if said target is still worth pursuing
        {
            // If the AI cannot find their target (out of range or line of sight broken)
            //if (Vector3.Distance(transform.position, currentTarget.transform.position) > pursueRange || AIFunction.SimpleLineOfSightCheck(currentTarget.transform.position, LookOrigin, viewDetection) == false)
            bool lineOfSightBroken = !AIFunction.LineOfSightCheckWithExceptions(currentTarget.transform.position, LookOrigin, viewDetection, characterData.health.hitboxes, currentTarget.health.hitboxes);
            bool outOfRange        = Vector3.Distance(transform.position, currentTarget.transform.position) > pursueRange;
            if (outOfRange || lineOfSightBroken)
            {
                // Count up a timer and continue pursuing until the timer expires
                patienceTimer = 0;
            }

            patienceTimer += Time.deltaTime;



            if (patienceTimer >= pursuePatience || currentTarget.health == null || currentTarget.health.IsDead)
            {
                print("Target out of range, cannot be attacked or is dead");
                currentTarget = null;
            }
        }
    }
Esempio n. 5
0
 private void StartWander()
 {
     if (GetWanderLocation(out Destination))
     {
         Agent.destination = Destination;
         CurrentAI         = Wander;
         NextAI            = MoveToWorkTarget;
     }
 }
Esempio n. 6
0
 private void Idle()
 {
     timer += Time.deltaTime;
     if (timer > currentTimer)
     {
         CurrentAI = NextAI;
         timer     = 0;
     }
 }
    public static AttackMessage Ranged(Character attacker, Vector3 origin, Vector3 direction, float maxRange, float projectileDiameter, float coneAngle, float velocity, LayerMask hitDetection)
    {
        AttackMessage m = new AttackMessage();



        m.attacker           = attacker;
        m.type               = AttackType.Ranged;
        m.timeInitiated      = Time.time;
        m.origin             = origin;
        m.direction          = direction;
        m.maxRange           = maxRange;
        m.projectileDiameter = projectileDiameter;
        m.coneAngle          = coneAngle;
        m.velocity           = velocity;
        m.hitDetection       = hitDetection;

        #region Get characters at risk



        List <Character> list = new List <Character>();
        // Perform a vision cone check
        RaycastHit[] thingsInLineOfFire = AIFunction.VisionCone(origin, direction, Vector3.up, coneAngle, maxRange, damageableThings, hitDetection);

        /*
         * string debugString = "Characters at risk from " + m.attacker.name + " on frame " + Time.frameCount + ":";
         * if (thingsInLineOfFire.Length < 1)
         * {
         *  debugString = "Attack from " + m.attacker.name + " at time " + m.timeInitiated + " will not hit anything.";
         *  //Misc.PauseForDebuggingPurposes();
         * }
         */
        for (int i = 0; i < thingsInLineOfFire.Length; i++)
        {
            // Check raycasthit collider to see if it is a character with a faction
            Character c = Character.FromObject(thingsInLineOfFire[i].collider.gameObject);
            // If there is a character class
            // If the character class is not already in the list
            // If the character class is considered an enemy of the attacker
            if (c != null && list.Contains(c) == false && attacker.HostileTowards(c))
            {
                // If so, the character is in the attack's danger zone
                //Debug.Log(c.name + " is in the line of fire of " + attacker.name + "'s attack");
                list.Add(c);
                //debugString += "\n" + c.name;
            }
        }
        //Debug.Log(debugString);

        m.charactersAtRisk = list.ToArray(); // Performs a calculation to find all enemies within the attack's boundaries. DO THIS LAST, after all the proper variables have been established for accurate calculations
        #endregion

        return(m);
    }
Esempio n. 8
0
 private void Wander()
 {
     if (!Agent.hasPath)
     {
         timer += Time.deltaTime;
         if (timer > WanderIdleTime)
         {
             timer     = 0;
             CurrentAI = NextAI;
         }
     }
 }
    // Checks if a position is in the line of fire
    public bool IsPositionSafe(Vector3 position, DamageHitbox[] characterColliders)
    {
        switch (type)
        {
        case AttackType.Ranged:

            // Check if the character is inside the cone of fire

            // If inside range
            if (Vector3.Distance(origin, position) < maxRange)
            {
                // If inside angle
                if (Vector3.Angle(direction, position - origin) < coneAngle)
                {
                    // If inside line of sight
                    if (AIFunction.LineOfSightCheckWithExceptions(position, origin, hitDetection, characterColliders))
                    {
                        return(true);
                    }
                }
            }

            break;

        case AttackType.Melee:


            // Save this until I have an actual melee attack system


            break;

        case AttackType.AreaOfEffect:

            // Check if the character is inside the blast radius and behind cover


            break;

        case AttackType.ExplosiveRanged:


            // ?? Somehow combine a vision cone check with a blast radius check

            break;

        default:

            break;
        }

        return(false);
    }
Esempio n. 10
0
 private void CheckIsBlocked(Map map, int[,] hazardMap)
 {
     // If the AI is blocked
     if (map.CollisionLayer[Engine.VectorToCell(_aiNextPosition).X, Engine.VectorToCell(_aiNextPosition).Y] ||
         hazardMap[Engine.VectorToCell(_aiNextPosition).X, Engine.VectorToCell(_aiNextPosition).Y] >= 2)
     {
         Sprite.IsAnimating = false;
         IsMoving           = false;
         // We define a new goal
         Path = AIFunction.MakeAWay(
             CellPosition,
             AIFunction.SetNewGoal(CellPosition, map.Board, map.CollisionLayer, hazardMap, map.Size),
             map.CollisionLayer, hazardMap, map.Size);
     }
 }
    public void AttackUpdate()
    {
        // If the attack has ended, cool down
        if (wielder.currentAttack == null)
        {
            cooldownTimer += Time.deltaTime;
        }

        // Stores a bool that only needs to be altered if the following if statement returns true
        bool lineOfSight = false;

        // If the AI has an assigned target
        if (wielder.currentTarget != null)
        {
            // If the AI has not started telegraphing yet, or it is but has not locked a specific position to aim at
            if (wielder.currentAttack == null || lockOntoTargetWorldPosition == false)
            {
                targetPosition = DetermineEnemyPosition(); // Determine the correct position for the AI to be aiming at
            }
            //Debug.DrawLine(wielder.LookOrigin, targetPosition, Color.black);
            Debug.DrawRay(targetPosition, Vector3.up * 5, Color.black);
            Debug.DrawRay(wielder.LookOrigin, wielder.LookDirection * Vector3.Distance(targetPosition, wielder.LookOrigin), Color.black);

            // Perform a line of sight check, making sure to ignore the AI and target's hitboxes since those obviously aren't obstacles
            lineOfSight = AIFunction.LineOfSightCheckWithExceptions(targetPosition, wielder.LookOrigin, wielder.viewDetection, wielder.characterData.health.hitboxes, wielder.currentTarget.health.hitboxes);
            if (lineOfSight)
            {
                // Aim for player
                wielder.RotateLookTowards(targetPosition, currentAimDegreesPerSecond);
                // If the AI is successfully aiming at the target and their attack is off cooldown
                if (wielder.IsLookingAt(targetPosition, aimAngleThreshold) && cooldownTimer >= cooldown)
                {
                    ExecuteAttack();
                }
            }
        }

        if (wielder.currentTarget == null || lineOfSight == false)
        {
            if (wielder.currentAttack != null)
            {
                CancelAttack();
            }
            wielder.ReturnToNeutralLookPosition();
        }
    }
Esempio n. 12
0
 private void WorkTarget()
 {
     timer += Time.deltaTime;
     //time between works
     if (timer < TimeBetweenWorks)
     {
         return;
     }
     timer = 0;
     //work upgrade til work is done
     if (CurrentWorkTarget.WorkUpgrade(CalculateWork()))
     {
         //collect resrouce
         CarriedMaterial   = CurrentWorkTarget.TakeMaterial(BuildingMaterialCarryPos);
         CurrentAI         = NextAI;
         CurrentWorkTarget = null;
     }
 }
    public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        #region Check validity of destination, and return null if no longer valid
        if (currentDestination != null)
        {
            float distance = Vector3.Distance(currentDestination.position, TargetLocation); // Obtains distance between agent and target

            Debug.DrawLine(TargetLocation, currentDestination.position, new Color(1, 0.5f, 0));

            // Checks if the AI can no longer see the target from their desired position, or if they are too close or too far
            bool lineOfSightLost = !AIFunction.LineOfSightCheckWithExceptions(TargetLocation, currentDestination.position, coverCriteria, ai.characterData.health.hitboxes, ai.currentTarget.health.hitboxes);
            bool tooClose        = distance < minimumMoveRange;
            bool tooFar          = distance > maximumMoveRange;
            if (lineOfSightLost || tooClose || tooFar)
            {
                // The AI cannot engage with the current target, position is nulled.
                currentDestination = null;
            }
            //Debug.Log(ai.name + " pathfinding status: " + lineOfSightLost + ", " + tooClose + ", " + tooFar);
        }
        #endregion

        #region Find new destination if there is none
        // If there is no position assigned, search for one.
        if (currentDestination == null)
        {
            //Debug.Log("Finding destination normally");
            currentDestination = FindFollowPosition(TargetLocation, minimumDestinationRange, maximumDestinationRange, numberOfChecks);

            //Debug.Log("Current destination = " + currentDestination);
        }
        #endregion

        #region Travel to destination
        // If a valid position is found the agent must travel to it.
        if (currentDestination != null)
        {
            Debug.DrawLine(ai.transform.position, currentDestination.position, Color.magenta);
            ai.na.SetDestination(currentDestination.position);
            Debug.DrawLine(currentDestination.position, ai.na.destination, new Color(1, 0.75f, 0), 1);
        }
        #endregion
    }
    public NullableVector3 FindAvoidPosition() // Should I have separate versions for dodging vs. taking cover? I might need this based on whether the enemy is aggressive or skittish
    {
        NullableVector3 newSafeLocation = null;
        float           maxPathDistance = maxMoveDistance;


        Vector3[] test = AIFunction.PositionsAroundPointInSpiral(ai.transform.position + ai.transform.up, ai.transform.up, minCheckRadius, maxCheckRadius, 3, 15);

        for (int i = 0; i < numberOfChecks; i++)
        {
            // Samples a random position around the target, normalises it, and randomises the magnitude to a point in betwen the min and max radii.
            // If I simply multiply by the max check radius, the position may be too close.
            Vector3 randomPosition = ai.transform.position + Random.insideUnitSphere.normalized * Random.Range(minCheckRadius, maxCheckRadius);
            // Normalising the Random.insideUnitSphere ensures the magnitude (and therefore distance value) is always 1, and the distance is calculated correctly.

            NavMeshHit followCheck;
            // Checks if there is an actual point on the navmesh close to the randomly selected position
            if (NavMesh.SamplePosition(randomPosition, out followCheck, ai.na.height * 2, NavMesh.AllAreas))
            {
                // Checks if the location is safe from the attack
                if (ai.attackToDodge.IsPositionSafe(followCheck.position, ai.characterData.health.hitboxes) == false)
                {
                    // Creates a new path for reference
                    NavMeshPath nmp = new NavMeshPath();
                    // If the agent can actually move to the location
                    if (ai.na.CalculatePath(followCheck.position, nmp) && nmp.status == NavMeshPathStatus.PathComplete)
                    {
                        float distance = AIFunction.NavMeshPathLength(nmp); // Check navmesh path cost between transform.position and the cover point currently being checked.

                        if (distance < maxPathDistance)                     // If the NPC is willing to travel that distance to the dodge zone, or if this distance is shorter than that of the previous route.
                        {
                            // If so, new cover position is established, and navmesh path is stored for next comparison
                            newSafeLocation = new NullableVector3(followCheck.position);
                            maxPathDistance = distance;
                        }
                    }
                }
            }
        }

        return(newSafeLocation);
    }
Esempio n. 15
0
 private void MoveToWorkTarget()
 {
     //get resource from priority list
     //HACK: just use trees for testing
     if (CurrentWorkTarget)
     {
         //check if pathing is done
         if (!Agent.hasPath)
         {
             CurrentAI = WorkTarget;
             NextAI    = StartWander;
         }
         return;
     }
     CurrentWorkTarget = LandMan.Instance.GetNearbyUpgrade(this, GetDesiredResource());
     if (!CurrentWorkTarget)
     {
         CurrentAI = StartWander;
         return;
     }
     Agent.SetDestination(CurrentWorkTarget.GetWorkLocation());
 }
Esempio n. 16
0
        protected override void Move(GameTime gameTime, Map map, int[,] hazardMap)
        {
            #region Walk

            // If he hasn't reach his goal => we walk to this goal
            if (_aiNextPosition != new Vector2(-1, -1) &&
                !AIFunction.HasReachNextPosition(Position, Speed, _aiNextPosition))
            {
                IsMoving           = true;
                Sprite.IsAnimating = true;

                CheckIsBlocked(map, hazardMap);

                Walk();

                ComputeWallCollision(map);
            }

            #endregion

            #region Search a goal
            // Otherwise => we find another goal
            else
            {
                // We place the player at the center of its cell
                Position = Engine.CellToVector(CellPosition);

                #region Bomb => AI

                // Try to put a bomb
                // Put a bomb
                if (!HasBadEffect || (HasBadEffect && BadEffect != BadEffect.NoBomb))
                {
                    if (AIFunction.TryToPutBomb(CellPosition, BombPower, map.Board, map.CollisionLayer, hazardMap,
                                                Config.MapSize))
                    {
                        if (CurrentBombAmount > 0)
                        {
                            Bomb bo =
                                FinalBomber.Instance.GamePlayScreen.BombList.Find(
                                    b => b.CellPosition == CellPosition);
                            if (bo == null)
                            {
                                CurrentBombAmount--;
                                var bomb = new Bomb(Id, CellPosition, BombPower, BombTimer, Speed);

                                // We define a new way (to escape the bomb)
                                Path = AIFunction.MakeAWay(
                                    CellPosition,
                                    AIFunction.SetNewDefenseGoal(CellPosition, map.CollisionLayer, hazardMap,
                                                                 Config.MapSize),
                                    map.CollisionLayer, hazardMap, Config.MapSize);

                                FinalBomber.Instance.GamePlayScreen.AddBomb(bomb);
                            }
                        }
                    }
                }

                #endregion

                if (Path == null || Path.Count == 0)
                {
                    Sprite.IsAnimating = false;
                    IsMoving           = false;
                    // We define a new goal
                    Path = AIFunction.MakeAWay(
                        CellPosition,
                        AIFunction.SetNewGoal(CellPosition, map.Board, map.CollisionLayer, hazardMap,
                                              Config.MapSize),
                        map.CollisionLayer, hazardMap, Config.MapSize);

                    if (Path != null)
                    {
                        _aiNextPosition = Engine.CellToVector(Path[Path.Count - 1]);
                        Path.Remove(Path[Path.Count - 1]);

                        CheckIsBlocked(map, hazardMap);
                    }
                }
                else
                {
                    // We finish the current way
                    _aiNextPosition = Engine.CellToVector(Path[Path.Count - 1]);
                    Path.Remove(Path[Path.Count - 1]);

                    /*
                     * // Update the way of the AI each time it changes of cell => usefull to battle against players (little bug here)
                     * aiWay = AI.MakeAWay(
                     *  CellPosition,
                     *  AI.SetNewGoal(CellPosition, map.Board, map.CollisionLayer, hazardMap),
                     *  map.CollisionLayer, hazardMap);
                     */
                }
            }

            #endregion

            UpdatePlayerPosition(map);
        }
    public NullableVector3 FindFollowPosition(Vector3 targetPosition, float minimumRange, float maximumRange, int numberOfChecks)
    {
        NullableVector3 newFollowPosition = null;
        float           currentPathLength = float.MaxValue;

        /*
         * int noValidPosition = 0;
         * int noLineOfSight = 0;
         * int noValidPath = 0;
         * int incompletePath = 0;
         * int inefficient = 0;
         */
        for (int i = 0; i < numberOfChecks; i++)
        {
            // Samples a random position around the target, outside minimumRange and inside maximumRange.
            // Normalising the Random.insideUnitSphere magnitude then multiplying it again by another random value allows me to ensure that the distance of the point is random but still within certain distance requirements.
            Vector3 randomPosition = targetPosition + Random.insideUnitSphere.normalized * Random.Range(minimumRange, maximumRange);

            NavMeshHit followCheck;
            // Checks if there is an actual point on the navmesh close to the randomly selected position
            if (NavMesh.SamplePosition(randomPosition, out followCheck, ai.na.height * 2, NavMesh.AllAreas))
            {
                Debug.DrawLine(randomPosition, followCheck.position, Colours.darkGreen);

                // Checks if line of sight is established between the new position and target. The agent is still pursuing and attacking the target, but they are just staying cautious.
                if (AIFunction.LineOfSightCheckWithExceptions(targetPosition, followCheck.position, coverCriteria, ai.characterData.health.hitboxes, ai.currentTarget.health.hitboxes))
                {
                    // Ensures that a path can be sampled to the destination.
                    NavMeshPath nmp = new NavMeshPath();
                    if (ai.na.CalculatePath(followCheck.position, nmp))
                    {
                        // Checks to make sure a whole path can be found.
                        // This if statement could probably be added to the previous one with the && operator
                        if (nmp.status == NavMeshPathStatus.PathComplete)
                        {
                            // Checks if the new cover position is a shorter route to get to than the old one.
                            // Use OR statement, and check navmesh path cost between transform.position and the cover point currently being checked.
                            float length = AIFunction.NavMeshPathLength(nmp);
                            if (newFollowPosition == null || length < currentPathLength)
                            {
                                // If so, new cover position is established, and navmesh path is stored for next comparison
                                newFollowPosition = new NullableVector3(followCheck.position);
                                currentPathLength = length;
                            }

                            /*
                             * else
                             * {
                             *  inefficient++;
                             * }
                             */
                        }

                        /*
                         * else
                         * {
                         *  incompletePath++;
                         * }
                         */
                    }

                    /*
                     * else
                     * {
                     *  noValidPath++;
                     * }
                     */
                }

                /*
                 * else
                 * {
                 *  noLineOfSight++;
                 * }
                 */
            }

            /*
             * else
             * {
             *  noValidPosition++;
             * }
             */
        }

        //Debug.Log("Path result - " + (newFollowPosition != null).ToString() + ", " + noValidPosition + " sampling errors, " + noLineOfSight + " line of sight fails, " + noValidPath + " pathing fails, " + incompletePath + " incomplete paths, and " + inefficient + " inefficient paths.");

        return(newFollowPosition);
    }