2022-10-29 16:48:18 -04:00

248 lines
7.6 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.InputSystem;
using Bytes.Sound;
public class GrappleHook : MonoBehaviour
{
[SerializeField] SoundPlayer soundPlayer;
[Header("Grapple settings")]
[SerializeField]
private float maxGrappleDist = 200;
[SerializeField]
private float grappleExtensionSpeed = 10;
[SerializeField]
private float grappleStrength = 4.5f * 5f;
[SerializeField]
private float grappleDamper = 7f;
[SerializeField]
private float grappleBoostSpeed = 50f;
[SerializeField]
private float grappleDrawingSpeed = 20;
[SerializeField]
private LayerMask grappleableLayer;
[SerializeField]
private float aimSpeed = 50f;
[Header("References")]
[SerializeField]
private Transform gunPos;
[SerializeField]
private RectTransform canvasRect;
[SerializeField]
private RectTransform hitMarkerRect;
private PlayerInput playerInput;
private Vector3 hitPosLocal, aimDir, aimDirTarget;
private SpringJoint joint;
private LineRenderer lr;
RaycastHit hit;
private bool grappled = false, grappling = false, boosted = false, changingLength = false, isStunned = false;
float changingLengthDir = 0;
private Vector3 currGrappleEndPos;
private GameObject hookedTo; // The obj we are hooked to
public GameObject HookedTo{get => hookedTo; set => hookedTo = value;}
public bool isGrappled()
{
return grappled;
}
#region private methods
void Start()
{
lr = gameObject.GetComponentInChildren<LineRenderer>();
lr.enabled = false;
hitMarkerRect.gameObject.SetActive(false);
playerInput = gameObject.GetComponent<PlayerInput>();
}
void Update()
{
if(isStunned){
return;
}
if(grappled){
UpdateRope(Time.deltaTime);
if(boosted){
joint.maxDistance -= grappleBoostSpeed * Time.deltaTime;
}
if(changingLength){
ChangeGrappleLength(changingLengthDir * grappleExtensionSpeed * Time.deltaTime);
}
return;
}
Aim();
}
private void Aim(){
aimDir = Vector3.Lerp(aimDir, aimDirTarget, Time.deltaTime * aimSpeed);
if(!hitMarkerRect.gameObject.activeSelf)hitMarkerRect.gameObject.SetActive(true);
hitMarkerRect.anchoredPosition = WorldToUI(gameObject.transform.position + aimDir * maxGrappleDist);
if(Physics.Raycast(gameObject.transform.position, aimDir, out hit, maxGrappleDist, grappleableLayer)){
hitMarkerRect.anchoredPosition = WorldToUI(hit.point);
hitMarkerRect.gameObject.GetComponent<Image>().color = Color.green;
if(grappling){
StartGrapple(hit);
}
}else{
hitMarkerRect.gameObject.GetComponent<Image>().color = Color.red;
}
}
private void UpdateRope(float deltaTime){
// TODO animate a wiggle on the rope
if(!grappled)return;
lr.SetPosition(0, gunPos.position);
Vector3 endPoint;
endPoint = hookedTo.transform.TransformPoint(hitPosLocal);
currGrappleEndPos = Vector3.Lerp(currGrappleEndPos, endPoint, deltaTime * grappleDrawingSpeed);
lr.SetPosition(1, currGrappleEndPos);
if(!lr.enabled)lr.enabled = true;
if(hit.rigidbody != null){
joint.connectedAnchor = hitPosLocal;
}else{
joint.connectedAnchor = endPoint;
}
}
private void StartGrapple(RaycastHit hit){
grappled = true;
grappling = false;
soundPlayer.PlaySound("PlayerGrappleHit");
//Display
hitMarkerRect.gameObject.SetActive(false);
// Rope phys
joint = gameObject.AddComponent<SpringJoint>();
joint.anchor = transform.InverseTransformPoint(gunPos.position);
joint.autoConfigureConnectedAnchor = false;
joint.maxDistance = hit.distance * 0.9f;
hitPosLocal = hit.transform.InverseTransformPoint(hit.point);
if(hit.rigidbody != null){
Rigidbody hitRb = hit.rigidbody;
joint.connectedBody = hitRb;
joint.connectedAnchor = hitPosLocal;
joint.enableCollision = true;
}else{
joint.connectedAnchor = hit.point;
}
joint.spring = grappleStrength;
joint.damper = grappleDamper;
joint.massScale = 4.5f;
if(hit.transform.tag.Equals("Vanishing")){
hit.transform.gameObject.GetComponent<Vanisher>().Begin(this);
}
hookedTo = hit.transform.gameObject;
currGrappleEndPos = transform.position;
}
private void EndGrapple(){
if (grappled)
{
soundPlayer.PlaySound("PlayerGrappleDetach");
}
grappled = false;
grappling = false;
lr.enabled = false;
boosted = false;
Destroy(joint);
hookedTo = this.gameObject;
}
private Vector2 WorldToUI(Vector3 point){
Vector2 viewPortPos = Camera.main.WorldToViewportPoint(point);
Vector2 pointScreenPos = new Vector2(((viewPortPos.x*canvasRect.sizeDelta.x)-(canvasRect.sizeDelta.x*0.5f)), ((viewPortPos.y*canvasRect.sizeDelta.y)-(canvasRect.sizeDelta.y*0.5f)));
return pointScreenPos;
}
private void ChangeGrappleLength(float value){
if(joint != null){
joint.maxDistance += value;
}
}
private void UnStun(){
isStunned = false;
}
#endregion
#region public methods
public void Stun(float duration){
isStunned = true;
EndGrapple();
Invoke("UnStun", duration);
}
public void Unhook(GameObject hookedObj){
if(hookedObj.Equals(hookedTo))
EndGrapple();
}
#endregion
#region InputActions
public void Grapple(InputAction.CallbackContext context){
if(grappling && grappled)return;
if(context.performed){
grappling = true;
soundPlayer.PlaySound("PlayerGrappleFire");
}
else if(context.canceled){
EndGrapple();
}
}
public void BoostToAnchor(InputAction.CallbackContext context){
if(context.performed && grappled && !boosted){
boosted = true;
soundPlayer.PlaySound("PlayerGrappleBoost");
}
}
public void AimInput(InputAction.CallbackContext context){
Vector2 value = context.ReadValue<Vector2>();
if(playerInput.currentControlScheme.Equals("Controller")){
if(value.magnitude == 0)return;
aimDirTarget = new Vector3(value.x, value.y, 0).normalized;
}else if(playerInput.currentControlScheme.Equals("KbMouse")){
var screenToWoldPoint = Camera.main.ScreenToWorldPoint(new Vector3(value.x, value.y, Mathf.Abs(Camera.main.transform.position.z)));
var aimDirTemp = (new Vector3(screenToWoldPoint.x-transform.position.x, screenToWoldPoint.y-transform.position.y, 0)).normalized;
if(aimDirTemp.magnitude == 0)return;
aimDirTarget = aimDirTemp;
}
}
public void RaiseLowerGrapple(InputAction.CallbackContext context){
if(context.started){
//IsMoving and value should be 1 or -1
changingLength = true;
changingLengthDir = -context.ReadValue<Vector2>().y;
}else if(context.canceled){
//is not moving and shoud be 0
changingLength = false;
changingLengthDir = context.ReadValue<Vector2>().y;
}
}
#endregion
}