Newer
Older
HierarchicalFSM-Unity3D / Assets / Scripts / NPC / FSM / Conditions / Combat / TryToFindCoverLocation.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

[CreateAssetMenu(menuName = "Finite State Machine/Condition/TryToFindCoverLocation")]
public class TryToFindCoverLocation : Condition
{
    [SerializeField] private CheckTargetState targetState;
    [SerializeField] private string tag; //Target object tab
    [SerializeField] private ushort numberOftries = 3; //Number of try checks to find a cover location

    public override bool CheckCondition(FSM fsm)
    {
        Debug.Log("CheckCondition");
        //Checks if the target is on specific state
        if (targetState.CheckCondition(fsm))
        {
            Debug.Log($"Find Spot: {FindSpot(fsm)}");

            if (FindSpot(fsm) != null)
            {
                RangedCombatState state = (RangedCombatState)fsm.CurrentState().GetParentState();

                if (state != null)
                {
                    if (state is RangedCombatState)
                    {
                       state.coverObject = FindSpot(fsm);
                       return true;
                    }

                    return false;
                }
                return false;
            }

            return false;
        }

        else return false;
    }

    //tris to find a cover location for the NPC
    private GameObject FindSpot(FSM fsm)
    {
        Awareness agentAware = fsm.Controller.AgentAwareness;
        GameObject target = fsm.Controller.AgentFOV.GetTarget();

        if (agentAware != null && target != null)
        {
            //Use the sensor
            agentAware.SetTargetTag(tag);
            agentAware.Enable = true;

            if (agentAware.TargetsList.Count > 0)
            {
                //Closest target
                float closestTargetDistance = float.MaxValue;
                GameObject coverObj = null;

                foreach (GameObject g in agentAware.TargetsList)
                {
                    float distance = Vector3.Distance(fsm.transform.position, g.transform.position);

                    if (distance < closestTargetDistance || distance <= 0f)
                    {
                        if (IsSpotProtected(target.transform, g.transform))
                        {
                            coverObj = g;
                            closestTargetDistance = distance;
                        }
                    }
                }

                return coverObj;
            }

            return null;
            
        }

        return null;
    }

    //If there are any available spots check if the spot protects from the player
    private bool IsSpotProtected(Transform target, Transform coverObject)
    {
        RaycastHit hit;

        //Checks if either target or cover object is not null
        if (target != null && coverObject != null)
        {
            //Calculates the direction 
            Vector3 directionToPlayer = (target.position - coverObject.position).normalized;

            //Performs the raycast to check if the cover object will provide actual cover to the NPC
            if (Physics.Raycast(coverObject.position, directionToPlayer, out hit))
            {
                return hit.transform != target;
            }

            Debug.LogWarning($"Raycast failed for cover check.");
            return false;
        }

        else
        {
            Debug.LogWarning($"Missing: {nameof(target)} or {nameof(coverObject)}");
            return false;
        }
    }
}