using System; using Drawing; using log4net.Appender; using NodeCanvas.Framework; using ParadoxNotion.Design; using ParadoxNotion.Services; using Reset.Core.Tools; using Reset.Units; using Sirenix.Serialization; using Unity.Cinemachine; using UnityEngine; namespace NodeCanvas.Tasks.Actions { [Category("Reset/Movement")] [Description("Pulls the agent towards a position with a spring-like effect")] public class DoGrapplePull : ActionTask{ public BBParameter grapplePoint; public BBParameter offset; public BBParameter pullAccelerationSpeed; public BBParameter pullDeaccelerationSpeed; [Tooltip("X is minimum speed, Y is maximum speed")] public BBParameter pullTimeRange; [Tooltip("X is the distance where the curve will first be evaluated, Y is the distance where the curve will last be evaluated")] public BBParameter pullSpeedRange; public BBParameter slowdownDistance; public BBParameter pullSpeedCurve; public BBParameter endDeaccelerationCurve; private float startTime; private Vector3 velocityOnStart; private Vector3 directionOnStart; private Vector3 originalDirection; public float breakAtDistance; public float breakAtDotProduct; private float currentSpeed; private Vector3 smoothedInput; private Vector3 smoothedInputRefVelocity; private Vector3 gizmoSwingDirection; private Vector3 gizmoPointDirection; private Vector3 gizmoFinalDirection; private Vector3 gizmosSmoothedInput; private float gizmoVertValue; private Transform camera; private float referenceSpeed; private Vector3 finalDirection; private float yChangeMultipler; //Use for initialization. This is called only once in the lifetime of the task. //Return null if init was successfull. Return an error string otherwise protected override string OnInit(){ DebugOverlayDrawer.AddOnOverlay("Grapple", "Composite Swing Direction", Vector3.zero); DebugOverlayDrawer.AddOnOverlay("Grapple", "Target Swing Direction", Vector3.zero); DebugOverlayDrawer.AddOnOverlay("Grapple", "Smoothed Input", Vector3.zero); return null; } //This is called once each time the task is enabled. //Call EndAction() to mark the action as finished, either in success or failure. //EndAction can be called from anywhere. protected override void OnExecute(){ camera = Camera.main.transform; MonoManager.current.onLateUpdate += DrawGrappleGizmo; // Set the initial direction directionOnStart = agent.transform.position.DirectionTo(grapplePoint.value); // Get the current move direction velocityOnStart = agent.outputMoveDirection; // Lerp the initial direction more towards the point of the grapple and less towards current momentum if not moving fast finalDirection = Vector3.Lerp(velocityOnStart, directionOnStart, velocityOnStart.magnitude / 10f); startTime = Time.time; currentSpeed = pullSpeedCurve.value[0].value * pullSpeedRange.value.y; smoothedInput = agent.GetComponent().velocity.normalized.Flatten(null, 0f, 0f); } //Called once per frame while the action is active. protected override void OnUpdate(){ // Get direction to the point Vector3 directionToPoint = agent.transform.position.DirectionTo(grapplePoint.value); // Calculate input Vector2 rawInput = agent.GetComponent().rawMoveInput; Vector3 input = new(rawInput.x, rawInput.y, 0f); smoothedInput = Vector3.SmoothDamp(smoothedInput, input, ref smoothedInputRefVelocity, 2); DebugOverlayDrawer.ChangeValue("Grapple", "Smoothed Input", smoothedInput.ToString()); // Change input handling based on position if (directionToPoint.y < 0) { } else { } // Create the distance variables float currentDist = Vector3.Distance(agent.transform.position, grapplePoint.value); // Calculate the swing direction. // Vector3 swingDirection = Quaternion.LookRotation(smoothedInput) * directionToPoint * smoothedInput.magnitude; // Old Vector3 sidewaysSwingAngle = Quaternion.AngleAxis(90f, Vector3.up) * directionToPoint; Vector3 upwardsSwingAngle = Quaternion.AngleAxis(-90f, Vector3.right) * directionToPoint; // Vector3 downwardsSwingAngle = Quaternion.AngleAxis(90f, Vector3.right) * directionToPoint; // why not just use upwards but negative based on input on Y // Create the composite swing direction and the target swing direction // The composite is the output while the target is the one used in calculating Vector3 compositeSwingDirection; Vector3 targetSwingDirection; // if (directionToPoint.y > 0) { DebugOverlayDrawer.ChangeValue("Grapple", "Target Swing Direction", Mathf.Abs((smoothedInput.y + 1f) / 2).ToString()); Vector3 swingAngleAbovePoint = -upwardsSwingAngle; if (Mathf.Abs(smoothedInput.y) > .01f){ swingAngleAbovePoint = Vector3.Slerp(-upwardsSwingAngle, upwardsSwingAngle, Mathf.Abs((smoothedInput.y + 1f) / 2)); } Vector3 axisFromInput = Vector3.zero; if (smoothedInput.x > 0) { axisFromInput = Vector3.Slerp(swingAngleAbovePoint, sidewaysSwingAngle, smoothedInput.x); } if (smoothedInput.x < 0) { axisFromInput = Vector3.Slerp(swingAngleAbovePoint, -sidewaysSwingAngle, Mathf.Abs(smoothedInput.x)); } compositeSwingDirection = Vector3.Slerp(swingAngleAbovePoint, axisFromInput, smoothedInput.x); // } else { // compositeSwingDirection = Vector3.Slerp(upwardsSwingAngle, sidewaysSwingAngle, smoothedInput.magnitude); // } // Some math for getting the Y yChangeMultipler = Mathf.Lerp(yChangeMultipler, 0f, elapsedTime * .5f); // Starts at 1 so that the player has more ability to change height on start of swing, then smooths to zero DebugOverlayDrawer.ChangeValue("Grapple", "Composite Swing Direction", compositeSwingDirection.ToString()); Debug.Log(Vector3.Dot(directionToPoint, Vector3.down)); // Speed float evaluatedSpeed = pullSpeedCurve.value.Evaluate(Mathf.Clamp((Time.time - startTime) / 6f, 0f, Mathf.Infinity)); float speedAgainstCurve = Mathf.Lerp(pullSpeedRange.value.x, pullSpeedRange.value.y, evaluatedSpeed); // Find how far from 0-1 the player is from the max and minimum distance // Get the base distance then account for the minimum distance to the point so that being the minimum away will Lerp to 1 // float currentDistMinimumAccounted = (currentDist - pullSpeedDistances.value.x); if (currentDist < slowdownDistance.value) { float change = endDeaccelerationCurve.value.Evaluate((slowdownDistance.value - currentDist) / slowdownDistance.value); speedAgainstCurve = speedAgainstCurve * change; // Debug.Log($"prev: {speedAgainstCurve}, norm: {(slowdownDistance.value - currentDist) / slowdownDistance.value}, change: {change}, output: {speedAgainstCurve * change} "); } // Evaluate the normalized value // float normaled = Mathf.Lerp(0, 1f, 1f - elapsedTime / (pullSpeedDistances.value.y - pullSpeedDistances.value.x)); // Use the curve evaluation to set the speed // float outputSpeed = Mathf.Lerp(pullTimeRange.value.x, pullTimeRange.value.y, evaluatedSpeed); // Soften the speed changes currentSpeed = Mathf.Lerp(currentSpeed, speedAgainstCurve, 10f * Time.deltaTime); // Gizmos gizmoVertValue = finalDirection.y; gizmosSmoothedInput = smoothedInput; gizmoPointDirection = directionToPoint; gizmoSwingDirection = swingAngleAbovePoint; gizmoFinalDirection = finalDirection; //Test finalDirection = gizmoSwingDirection; agent.SetNewDirection(finalDirection.Flatten(null, 0)); agent.SetNewGravity(finalDirection.y); agent.SmoothToSpeed(0f, 1f * Time.deltaTime, out referenceSpeed); // agent.SmoothToDirection(finalDirection.Flatten(null, 0).normalized * evaluatedSpeed, 1f * Time.deltaTime, out referenceDirection); // agent.SmoothToGravitation(finalDirection.y, 1f, out referenceGravity); if (Vector3.Dot(directionOnStart, directionToPoint.normalized) < breakAtDotProduct) { // EndAction(true); } else if (currentDist < breakAtDistance) { EndAction(true); } } public void DrawGrappleGizmo(){ // Destination gizmos using (Draw.WithColor(Color.blue)){ Vector3 offsetTowardsCamera = grapplePoint.value.DirectionTo(camera.transform.position); // Grapple Point Draw.SolidCircle(grapplePoint.value + offsetTowardsCamera, grapplePoint.value.DirectionTo(camera.transform.position), .4f); Draw.Label2D(grapplePoint.value + offsetTowardsCamera * 2f + Vector3.up, "Grapple Point"); using (Draw.WithLineWidth(1.5f)) { // Final Direction Draw.Line(agent.transform.position + Vector3.up, agent.transform.position + Vector3.up + gizmoFinalDirection.normalized * 2f); Draw.ArrowheadArc(agent.transform.position + Vector3.up, gizmoFinalDirection.normalized, 2f, 15f); // Colors for faded arrows Color swingColor = Color.Lerp(Color.blue, Color.blue.Alpha(.4f), (elapsedTime * .6f)); Color dirColor = Color.Lerp(Color.blue.Alpha(.4f), Color.blue, (elapsedTime * .6f)); // Swing Direction using (Draw.WithColor(swingColor)) { float swingLength = 2.2f; Vector3 swingStart = agent.transform.position + Vector3.up * .4f; Vector3 swingDir = swingStart + gizmoSwingDirection.normalized * swingLength; Draw.DashedLine(swingStart, swingDir, .2f, .2f); Draw.ArrowheadArc(swingStart, gizmoSwingDirection.normalized, swingLength, 15f); Draw.Label2D(swingDir + Vector3.up * .4f, "Swing Direction"); } // Point Direction using (Draw.WithColor(dirColor)) { float pointLength = 1.2f; Vector3 pointStart = agent.transform.position + Vector3.up * .2f; Vector3 pointDir = pointStart + gizmoPointDirection.normalized * pointLength; Draw.DashedLine(pointStart, pointDir, .2f, .2f); Draw.ArrowheadArc(pointStart, gizmoPointDirection.normalized, pointLength, 15f); Draw.Label2D(pointDir + Vector3.up * .4f, "Grapple Point Direction"); } } } // Input Gizmos using (Draw.WithColor(Color.red)) { // Input left and right, up and down for size Vector3 inputGizmoOffset = (agent.transform.position + camera.rotation * Vector3.back + Vector3.up * .5f); Vector3 inputGizmoPosition = inputGizmoOffset + camera.rotation * Vector3.Lerp(Vector3.left, Vector3.right, (gizmosSmoothedInput.x - 1 ) / 2f + 1f); Draw.Line(inputGizmoOffset + camera.rotation * Vector3.left, inputGizmoOffset + camera.rotation * Vector3.right); Draw.SolidCircle(inputGizmoPosition, inputGizmoPosition.DirectionTo(camera.position), Mathf.Lerp(.2f, .6f, (gizmosSmoothedInput.y - 1 ) / 2f + 1f)); } // Up and down using (Draw.WithColor(Color.yellow)) { Vector3 vertGizmoPosition = agent.transform.position + camera.rotation * Vector3.left; float vertGizmoWidth = .25f; float vertGizmoHeight = 1.75f; Vector3 vertGizmoStart = vertGizmoPosition + Vector3.up * vertGizmoHeight; Vector3 vertGizmoEnd = vertGizmoPosition + Vector3.up * .35f; Vector3 circlePos = Vector3.Lerp(vertGizmoEnd, vertGizmoStart, gizmoVertValue); Draw.Line(vertGizmoStart, vertGizmoEnd); Draw.SolidCircle(circlePos, circlePos.DirectionTo(camera.position), .4f); Draw.Label2D(vertGizmoStart + camera.rotation * Vector3.left * 1.5f, gizmoVertValue.ToString()); Vector3 vertArrowUpPosition = vertGizmoStart + camera.rotation * Vector3.left * 1f + Vector3.up * .2f; Vector3 vertArrowDownPosition = vertGizmoStart + camera.rotation * Vector3.left * 1f + Vector3.down * .2f; if (gizmoVertValue > 0) { Draw.SolidTriangle(vertArrowUpPosition + camera.rotation * Vector3.left/4, vertArrowUpPosition + Vector3.up/2, vertArrowUpPosition + camera.rotation * Vector3.right/4); } else { Draw.SolidTriangle(vertArrowDownPosition + camera.rotation * Vector3.left/4, vertArrowDownPosition + Vector3.down/2, vertArrowDownPosition + camera.rotation * Vector3.right/4); } } } //Called when the task is disabled. protected override void OnStop() { MonoManager.current.onLateUpdate -= DrawGrappleGizmo; } //Called when the task is paused. protected override void OnPause() { } } }