/********************************************************************* * \file LeafChase.cs * \author Ryan Wang Nian Jing * \brief Leaf node implementation for AI chasing the player * * * \copyright Copyright (c) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *********************************************************************/ using SHADE; using SHADE_Scripting.AIBehaviour.BehaviourTree; using SHADE_Scripting.Audio; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; //VARIABLES public partial class LeafChase : BehaviourTreeNode { //Used to move entity around private Transform transform; private RigidBody rb; private float chaseSpeed; private float turningSpeed; private float captureDistance; private float baseCaptureTime; } //FUNCTIONS public partial class LeafChase : BehaviourTreeNode { //Despite inheriting from BehaviourTreeNode, we don't have children to this node, //and hence we don't need to inherit its constructors public LeafChase(string name) : base (name) { //Debug.Log("LeafChase ctor"); } //Helper, find which waypoint player is closest to private void determinePlayerWaypoint() { List waypoints = (List)GetNodeData("waypoints"); Transform target = (Transform)GetNodeData("target"); if (waypoints == null || target == null) { return; } int nearestWaypointIndex = 0; for (int i = 0; i < waypoints.Count; ++i) { if ((target.GlobalPosition - waypoints[i].GetComponent().GlobalPosition).GetSqrMagnitude() < (target.GlobalPosition - waypoints[nearestWaypointIndex].GetComponent().GlobalPosition).GetSqrMagnitude()) { nearestWaypointIndex = i; } } //Debug.Log("Player is nearest " + nearestWaypointIndex); //Debug.Log("I'm at " + (int)GetNodeData("currentWaypointIndex")); SetNodeData("playerLastSightedWaypointIndex", nearestWaypointIndex); } public override BehaviourTreeNodeStatus Evaluate() { //Debug.LogWarning("LeafChase"); onEnter(BehaviourTreeNodeStatus.RUNNING); //Get Data if (GetNodeData("transform") == null || GetNodeData("rigidBody") == null || GetNodeData("turningSpeed") == null || GetNodeData("chaseSpeed") == null || GetNodeData("distanceToCapture") == null || GetNodeData("baseCaptureTime") == null) { status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; } else { transform = (Transform)GetNodeData("transform"); rb = (RigidBody)GetNodeData("rigidBody"); chaseSpeed = (float)GetNodeData("chaseSpeed"); turningSpeed = (float)GetNodeData("turningSpeed"); captureDistance = (float)GetNodeData("distanceToCapture"); baseCaptureTime = (float)GetNodeData("baseCaptureTime"); } //Fail if no target in blackboard if (GetNodeData("target") == null) { //Debug.Log("Failure: No target in blackboard"); return BehaviourTreeNodeStatus.FAILURE; } Transform target = (Transform)GetNodeData("target"); Vector3 normalisedDifference = target.GlobalPosition - transform.GlobalPosition; normalisedDifference.y = 0.0f; //Do not consider Y normalisedDifference /= normalisedDifference.GetMagnitude(); //Over maximum distance, stop chase if ((transform.GlobalPosition - target.GlobalPosition).GetMagnitude() > 1000.0f) { //Debug.Log("Failure: Over maximum distance"); ClearNodeData("target"); if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == true) { //AudioHandler.audioClipHandlers["BGMAdpative"].SetParameter("Detected", 0.0f); Audio.SetParameter("Detected", 0.0f); AudioHandler.audioClipHandlers["SFXHumming"].Play(); } SetNodeData("isAlert", false); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; } else if (false) //TODO If collided against a wall { //Debug.Log("Running: Collided against wall"); SetNodeData("isPathfinding", true); status = BehaviourTreeNodeStatus.RUNNING; onExit(BehaviourTreeNodeStatus.RUNNING); return BehaviourTreeNodeStatus.RUNNING; } //Keep chasing else if ((transform.GlobalPosition - target.GlobalPosition).GetMagnitude() > captureDistance) { //Debug.Log("Running: Chasing"); Quaternion targetRotation = Quaternion.LookRotation(normalisedDifference, new Vector3(0.0f, 1.0f, 0.0f)); transform.LocalRotation = Quaternion.Slerp(transform.LocalRotation, targetRotation, turningSpeed * Time.DeltaTimeF); //Determine the player's nearest waypoint as long as the AI can see the player //Head towards that waypoint in an attempt to chase the player determinePlayerWaypoint(); //TODO delete this when original intendd code above works with velocity being limited correctly //Only chase the player directly if the player's waypoint matches the AI's own if (GetNodeData("currentWaypointIndex") != null && GetNodeData("playerLastSightedWaypointIndex") != null) { if ((int)GetNodeData("currentWaypointIndex") == (int)GetNodeData("playerLastSightedWaypointIndex")) { //Debug.Log("Waypoint indicees matching. Chasing directly"); rb.LinearVelocity = normalisedDifference * chaseSpeed; } else { status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return BehaviourTreeNodeStatus.FAILURE; } } //Reset capture timing to base SetNodeData("captureTimeLeft", baseCaptureTime); //Not capturing, don't play SFX SetNodeData("isCapturing", false); status = BehaviourTreeNodeStatus.RUNNING; onExit(BehaviourTreeNodeStatus.RUNNING); return BehaviourTreeNodeStatus.RUNNING; } //Once player is close enough, perform attack else { //Debug.Log("Success: Near enough. Begin attack"); //Look at the correct direction Quaternion targetRotation = Quaternion.LookRotation(normalisedDifference, new Vector3(0.0f, 1.0f, 0.0f)); transform.LocalRotation = Quaternion.Slerp(transform.LocalRotation, targetRotation, turningSpeed * Time.DeltaTimeF); //Play SFX if (GetNodeData("isCapturing") != null && (bool)GetNodeData("isCapturing") == false) { //Debug.Log("AI Play capturing SFX"); } SetNodeData("isCapturing", true); //TODO resetting the capture time once it's less than 0 doesn't work as //there is quite some time (about .1 seconds) after the timer falls below 0 //that the capture actually happens status = BehaviourTreeNodeStatus.SUCCESS; onExit(BehaviourTreeNodeStatus.SUCCESS); return status; } } }