Untitled

mail@pastecode.io avatar
unknown
csharp
2 years ago
23 kB
4
Indexable
Never
using System.Collections.Generic;
//using System.Diagnostics;
using System.Linq;
using Unity.Burst.CompilerServices;
using Unity.VisualScripting;
//using System.Numerics;
using UnityEngine;
using UnityEngine.SocialPlatforms;
using UnityEngine.UIElements;
using static UnityEngine.UIElements.UxmlAttributeDescription;

public class Cast_v2 : MonoBehaviour
{
    public float PullForce = 5f;
    public int Shots = 2;
    public float ClimbSpeed = 1f; //how fast you can move toward grapple point
    public float SlideSpeed = 2f; // how fast you can rappel away from grapple point
    public float Range = 12f; //grapple range
    public float shotspeed = 10f; //cast speed
    public float retractspeed = 5f; //retract speed
    public float WiggleForce = 1f; //how much you can move while hanging
    private float tshot; //timer for shot
    private float tret; //timer for retraction
    public int ShotsLeft;

    public float ropelength = 0;
    private Vector2 MousePosonClick;
    private LineRenderer lr;
    private Vector2 CharPos;
    public Rigidbody2D rb;
    private Rigidbody2D GrappleBody;
    private EdgeCollider2D GrappleObjCollider;
    public Movement mvmt;
    public DistanceJoint2D Grapple;
    public bool WillGrapple;
    public Vector2 RopeGrapplePoint;
    private Vector2[] GrappleColliderPoints;
    private Vector2 GrabPointinWorld;
    private RaycastHit2D Updatehit;
    private RaycastHit2D hit;

    //public LayerMask IgnoreLayer = 7;
    private bool clicked = false;
    private bool clicking = false;
    private bool rclicked = false; //want to eventually make it so rclick is an item grabber
    private bool rclicking = false;
    private bool ClickedCheck;
    private bool ClickingCheck;

    private float angle_storage;
    public int RangeMode = 0; //0 -- normal range; 1 -- extended range
    private Vector2 GrabPointinLocal;
    private Dictionary<int, Vector2> WrappingPoints = new();
    private Dictionary<int, Rigidbody2D> WrappingBodies = new();
    private Dictionary<int, int> WrappingAngleLookup = new();
    public bool ReadytoRetract;
    Vector2 dir = Vector2.zero;
    private bool prevmotorstate;
    private bool firstmotorstate;
    //private bool FreshlyWrapped = false;
    // Start is called before the first frame update
    void Start()
    {
        Grapple = GetComponent<DistanceJoint2D>();
        Grapple.enabled = false;
        ShotsLeft = Shots;
        lr = GetComponent<LineRenderer>();
        rb = GetComponent<Rigidbody2D>();
        mvmt = GetComponent<Movement>();

    }


    // Update is called once per frame

    void Update()
    {
        if (rb.velocity.magnitude >= 25f && RangeMode == 0) //increase range if moving fast
        {
            Range *= 1.1f;
            RangeMode = 1;
        }
        if (rb.velocity.magnitude < 25f && RangeMode == 1) // return to normal range once slowed down
        {
            Range /= 1.1f;
            RangeMode = 0;
        }

        if (clicked)
        {
            return;
        }
        else
        {
            clicked = Input.GetMouseButtonDown(0);
        }

        if (rclicked)
        {
            return;
        }
        else
        {
            rclicked = Input.GetMouseButtonDown(1);
        }

        clicking = Input.GetMouseButton(0);
        rclicking = Input.GetMouseButton(1);
 
    }


    void FixedUpdate()
    {
        Vector2 aim = Input.mousePosition;
        Vector2 target = Camera.main.ScreenToWorldPoint(aim);
        CharPos = rb.position;
        dir = -(CharPos - target).normalized; //direction from player to target

        //Check and initiate grapple
        if (clicked || rclicked)
        {
            MousePosonClick = target;

            if (clicked)
            {
                IntitialCastCheck();
                
                clicked = false;

                if (hit.rigidbody.GetComponent<SliderJoint2D>() != null)// shut off elevator motor so player can pull it down
                {
                    var slider = hit.rigidbody.GetComponent<SliderJoint2D>();
                    if (slider.limitState == JointLimitState2D.UpperLimit) 
                    {
                        prevmotorstate = slider.useMotor;
                        firstmotorstate = prevmotorstate;
                        slider.useMotor = false;
                        //???????????????????????????? wanna make it so elevators can lift us while grappling to them, but can still be pulled down by grappling
                        // maybe use a spring joint
                    }
                }
            }
            else if (rclicked)
            {
                tshot = 0;
                tret = 0;
                rclicked = false;
            }


        }

        //if not going to grapple, still run the shooting animation
        if (!WillGrapple && ReadytoRetract)
        {
            lr.SetPosition(1, CharPos);
            var shottime = Range / shotspeed;
            Vector3 End = CharPos - Range * (CharPos - MousePosonClick).normalized;
            if (tshot < shottime)
            {
                Vector3 newpos;
                newpos = Vector3.Lerp(CharPos, End, tshot / shottime);
                lr.SetPosition(0, newpos);
                tshot += Time.deltaTime;
            }
            if (tshot >= shottime)
            {
                Retract(End);
            }

        }

        //update linerenderer, movement/swinging, wrapping
        if (clicking && WillGrapple && WrappingBodies.Count > 0)
        {
            if (hit.rigidbody.GetComponent<TriggerLift>() != null) //deal with elevator trigger thats controller rotating thing
            {
                var slider = hit.rigidbody.GetComponent<SliderJoint2D>();
                var trigged = hit.rigidbody.GetComponent<TriggerLift>().isTrigged;

                if (trigged)
                {
                    prevmotorstate = false;
                }
                else
                {
                    prevmotorstate = firstmotorstate;
                }

            }

            GrabPointinLocal = WrappingPoints[WrappingPoints.Count - 1]; //grapple point in local coords -- this remains constant as object moves
            GrabPointinWorld = WrappingBodies[WrappingBodies.Count - 1].transform.TransformPoint(GrabPointinLocal); //get new grapple point in world coords -- this changes each frame

            ropelength = 0;
            RopeUpdater();
            GrappleMove();
        }

        //un-hook and retract rope
        if (!clicking && WillGrapple && ReadytoRetract)
        {
            if (hit.rigidbody.GetComponent<SliderJoint2D>() != null)//return elevator to its initial stae
            {
                var slider = hit.rigidbody.GetComponent<SliderJoint2D>();
                slider.useMotor = prevmotorstate;

            }

            Retract(WrappingBodies[WrappingBodies.Count - 1].transform.TransformPoint(WrappingPoints[WrappingPoints.Count - 1]));
            
        }
    }


    void OnCollisionExit2D(Collision2D collision) //mark as un-grounded upon leaving ground
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            ShotsLeft = Shots; //reset shots
        }
    }

    void InitialGrabCheck()
    {
        //will need to use slider joints instead of the distance joint

        tshot = 0;
        tret = 0;

        var mask = ~(1 << 7 | 1 << 2);

        hit = Physics2D.CircleCast(CharPos, 0.1f, dir, Range, mask); //raycast from character to target
        lr.positionCount = 2;
        lr.SetPosition(1, CharPos); //will stay at characer
        lr.SetPosition(0, CharPos);// will eventually go to grapple point
        if (hit.collider != null) //if the raycast hits, initiate grapple
        {

            GrappleObjCollider = hit.collider as EdgeCollider2D; //get the target's collider

            //GrappleObjCollider = hit.collider as hit.collider.GetType(); //get the target's collider
            WrappingBodies.Add(0, hit.rigidbody); //get targets rigidbody
            GrabPointinLocal = WrappingBodies[0].transform.InverseTransformPoint(hit.point); //Get the hit-point in the grapple body's ref frame. This will make 
                                                                                                //it easy to get connect to moving bodies
                                                                                                //1) move to bodies frame
                                                                                                //2) body moves in world frame, doesnt move in its own frame
                                                                                                //3) move back to world frame to get the new location of the hit point
                                                                                                //   on the body (for linerenderer)
                                                                                                //(with GrappleBody, anchor point is in terms of local coords)
            WrappingPoints.Add(0, GrabPointinLocal);
            WrappingAngleLookup.Add(0, 0);
            Grapple.connectedBody = WrappingBodies[0]; //set connected body to grapple rigid body
            Grapple.connectedAnchor = WrappingPoints[0]; //set grapple point to hit point 
            Grapple.distance = hit.distance; //set grapple distance
            WillGrapple = true;
            Grapple.enabled = true;

        }
        else if (hit.collider == null)// if raycast doesnt hit anything, dont anchor
        {
            WillGrapple = false;
        }

        ReadytoRetract = true;

        
    }
    void IntitialCastCheck()
    {
        tshot = 0;
        tret = 0;

        if (ShotsLeft > 0)
        {
            var mask = ~(1 << 7 | 1 << 2); //dont grapple on NoRope and Ignore Raycast layers

            hit = Physics2D.CircleCast(CharPos,0.75f,dir,Range, mask); //raycast from character to target
            lr.positionCount = 2;
            lr.SetPosition(1, CharPos); //will stay at characer
            lr.SetPosition(0, CharPos);// will eventually go to grapple point
            if (hit.collider != null) //if the raycast hits, initiate grapple
            {

                GrappleObjCollider = hit.collider as EdgeCollider2D; //get the target's collider

                //GrappleObjCollider = hit.collider as hit.collider.GetType(); //get the target's collider
                WrappingBodies.Add(0, hit.rigidbody); //get targets rigidbody
                GrabPointinLocal = WrappingBodies[0].transform.InverseTransformPoint(hit.point); //Get the hit-point in the grapple body's ref frame. This will make 
                                                                                                 //it easy to get connect to moving bodies
                                                                                                 //1) move to bodies frame
                                                                                                 //2) body moves in world frame, doesnt move in its own frame
                                                                                                 //3) move back to world frame to get the new location of the hit point
                                                                                                 //   on the body (for linerenderer)
                                                                                                 //(with GrappleBody, anchor point is in terms of local coords)
                WrappingPoints.Add(0, GrabPointinLocal); //store intitial grapple point
                WrappingAngleLookup.Add(0, 0); //store angle (for unwrapping)

                Grapple.connectedBody = WrappingBodies[0]; //set connected body to grapple rigid body
                Grapple.connectedAnchor = WrappingPoints[0]; //set grapple point to hit point 
                Grapple.distance = hit.distance; //set grapple distance
                WillGrapple = true; 
                Grapple.enabled = true; //enable the distance joint
                ShotsLeft--; //decrement shots remaining
            }
            else if (hit.collider == null)// if raycast doesnt hit anything, dont anchor
            {
                WillGrapple = false;
            }

            ReadytoRetract = true;

        }
    }

    void GrappleMove() //how movement works while grappled (ie. swinging)
    {
        float xMove = Input.GetAxisRaw("Horizontal"); //A,D down
        float yMove = Input.GetAxisRaw("Vertical"); //W, S down

        Vector2 GrappleAnchor = GrabPointinWorld;
        Vector2 PlayertoAnchor = -(CharPos - GrappleAnchor).normalized;

        List<float> DPs = new()  //DPs[0] = DP with up, DPs[1] = DP with right
        {
            Mathf.Abs(Vector2.Dot(PlayertoAnchor, Vector2.up)),
            Mathf.Abs(Vector2.Dot(PlayertoAnchor, Vector2.right))
        };

        int DirIndex = DPs.IndexOf(DPs.Max());


        if (DirIndex == 0) //if grapple point is above or below player
        {
            var DPVert = Vector2.Dot(PlayertoAnchor, Vector2.up);
            rb.AddForce(new Vector2(xMove * WiggleForce, 0), ForceMode2D.Impulse); //x-movement is unchanged

            if (DPVert > 0 && ropelength < Range)//if under the anchor point
            {
                rb.velocity += yMove * ClimbSpeed * PlayertoAnchor; //up is climb, down is slide
                Grapple.distance -= yMove * ClimbSpeed;

            }
            if (DPVert < 0 && ropelength < Range) // if above the anchor
            {
                rb.velocity += yMove * ClimbSpeed * PlayertoAnchor; // down is climb, up is slide
                Grapple.distance += yMove * ClimbSpeed;
            }

        }
        if (DirIndex == 1) //if grapple is to the left or right of player
        {
            var DPHoriz = Vector2.Dot(PlayertoAnchor, Vector2.right);
            rb.AddForce(new Vector2(0, yMove * WiggleForce), ForceMode2D.Impulse);

            if (DPHoriz > 0 && ropelength < Range)//if to the left of the anchor
            {
                rb.velocity += xMove * ClimbSpeed * PlayertoAnchor; // right is climb, left is slide
                Grapple.distance -= xMove * ClimbSpeed;
            }
            if (DPHoriz < 0 && ropelength < Range) // if to the right of the anchor
            {
                rb.velocity += xMove * ClimbSpeed * PlayertoAnchor; //left is climb, right is slide
                Grapple.distance += xMove * ClimbSpeed;
            }

        }

        if (ropelength >= Range) // keep rope from going over its max length
        {
            Grapple.distance -= (ropelength - Range);
        }

    }
    void RopeUpdater()
    {
        //Grapple.connectedAnchor = GrabPointinLocal;
        //Grapple.connectedBody = WrappingBodies[WrappingPoints.Count - 1];
        //initial rope cast

        Vector2 Grappoint0inWorld = WrappingBodies[0].transform.TransformPoint(WrappingPoints[0]); // the initial grapple point

        var shottime = (CharPos - Grappoint0inWorld).magnitude / shotspeed; //time to reach grapple position
        if (tshot < shottime) //cast rope to grapple point animation
        {
            Vector3 newpos;
            newpos = Vector3.Lerp(CharPos, Grappoint0inWorld, tshot / shottime);
            lr.SetPosition(0, newpos);
            tshot += Time.deltaTime;
        }
        else if (tshot > shottime) // keep rope at grapple point as it moves
        {
            //tshot = shottime;
            lr.SetPosition(0, Grappoint0inWorld);
            RopeGrapplePoint = lr.GetPosition(0);
        }

        for (int i = 0; i < lr.positionCount - 1; i++)
        {
            lr.SetPosition(i, WrappingBodies[i].transform.TransformPoint(WrappingPoints[i]));// move rope points along with moving objects
            ropelength += (lr.GetPosition(i) - lr.GetPosition(i + 1)).magnitude; // measure rope length

        }
        lr.SetPosition(lr.positionCount - 1, CharPos); // keep the last rope position on charachter 
      
        


        //Wrapping handler

        var dir2 = (GrabPointinWorld - CharPos).normalized; //from character to the current grab point
        Updatehit = Physics2D.Raycast(CharPos, dir2, (CharPos - GrabPointinWorld).magnitude - 0.5f); //a new raycast to see if we should wrap the rope

        //wrapper
        if (Updatehit.collider != null) 
        {

            var WrappingGrappleObj = Updatehit.collider as EdgeCollider2D;

            //get (possibly new) grapple object
            GrappleColliderPoints = WrappingGrappleObj.points; //get the points of the collider of the grapple object
            GrappleBody = Updatehit.rigidbody;

            Debug.DrawLine(Updatehit.point, Updatehit.point + Updatehit.normal, Color.red, 10f);

            var OrderedDistanceDict = GrappleColliderPoints.ToDictionary<Vector2, float, Vector2> // dictionary that finds the collider point thats closest to the raycast hit point
                           (position => Vector2.Distance(Updatehit.point, GrappleBody.transform.TransformPoint(position)),
                           position => GrappleBody.transform.TransformPoint(position)).OrderBy(e => e.Key); 

            GrabPointinWorld = OrderedDistanceDict.First().Value; //get closest point
            GrabPointinLocal = GrappleBody.transform.InverseTransformPoint(GrabPointinWorld); //make closest point the new grapple point



            WrappingPoints.Add(WrappingPoints.Count, GrabPointinLocal); //add the new point and rigid body to wrapping dictionaries
            WrappingBodies.Add(WrappingBodies.Count, GrappleBody);
            WrappingAngleLookup.Add(WrappingAngleLookup.Count, 0); 
            lr.positionCount++; //add a line render position
            Grapple.connectedBody = GrappleBody; // anchor to new object at the new point
            Grapple.connectedAnchor = GrabPointinLocal;
            Grapple.distance = (CharPos - GrabPointinWorld).magnitude; 
            lr.SetPosition(lr.positionCount - 1, lr.GetPosition(lr.positionCount - 2)); //put character position as the final linerender point
            lr.SetPosition(lr.positionCount - 2, GrabPointinWorld); //2nd last LR point is the new grapple point

        }
        if (WrappingPoints.Count > 1) //unwrapper
        {

            
            /* 
            Vector2 HingetoChar = (lr.GetPosition(lr.positionCount - 1) - lr.GetPosition(lr.positionCount - 2)); //vector stuff to find the unwrapping angle
            Vector2 NextHingetoCurrentHinge = (lr.GetPosition(lr.positionCount - 2) - lr.GetPosition(lr.positionCount - 3));
            var interhingeAngle = Vector2.Angle(lr.GetPosition(lr.positionCount - 3), NextHingetoCurrentHinge);

            Vector2 NextHingetoChar = (lr.GetPosition(lr.positionCount - 1) - lr.GetPosition(lr.positionCount - 3));
            var HingetoCharangle = Vector2.Angle(lr.GetPosition(lr.positionCount - 3), NextHingetoChar);
             */   
            
            // Maybe use angles in local coordinates? -- does seem to work better
            
            var lr_1 = GrappleBody.transform.InverseTransformPoint(lr.GetPosition(lr.positionCount - 1));
            var lr_2 = GrappleBody.transform.InverseTransformPoint(lr.GetPosition(lr.positionCount - 2));
            var lr_3 = GrappleBody.transform.InverseTransformPoint(lr.GetPosition(lr.positionCount - 3));
            Vector2 HingetoChar = (lr_1 - lr_2);
            Vector2 NextHingetoCurrentHinge = (lr_2 - lr_3);
            var interhingeAngle = Vector2.Angle(lr_3, NextHingetoCurrentHinge);

            Vector2 NextHingetoChar = (lr_1 - lr_3);
            var HingetoCharangle = Vector2.Angle(lr_3, NextHingetoChar);
            
            var ang = HingetoCharangle - interhingeAngle; 

            Debug.Log(ang);
            if (ang < 0)
            {
                if (WrappingAngleLookup[WrappingAngleLookup.Count - 1] == 1) //if initial angle was positive, unwrap
                {
                    UnwrapHandler();
                    return;
                }
                WrappingAngleLookup[WrappingAngleLookup.Count - 1] = -1; //set the wrapping angle indicator to -1 to indicate that the initial angle is negative
            }
            else
            {
                if (WrappingAngleLookup[WrappingAngleLookup.Count - 1] == -1) //if initial angle was negative, unwrap
                {
                    UnwrapHandler();
                    return;
                }
                WrappingAngleLookup[WrappingAngleLookup.Count - 1] = 1;//set the wrapping angle indicator to +1 to indicate that the initial angle is positive
            }

            
        }

    }
    void UnwrapHandler()
    {
        WrappingPoints.Remove(WrappingPoints.Count - 1); //remove the current grapple point from the dictionaries
        WrappingBodies.Remove(WrappingBodies.Count - 1);
        WrappingAngleLookup.Remove(WrappingAngleLookup.Count - 1);

        GrappleBody = WrappingBodies[WrappingBodies.Count - 1]; //set new grapple point to the previous grapple point
        GrabPointinLocal = WrappingPoints[WrappingPoints.Count - 1];
        GrabPointinWorld = GrappleBody.transform.TransformPoint(GrabPointinLocal);

        Grapple.connectedBody = GrappleBody;
        Grapple.connectedAnchor = GrabPointinLocal; //anchor to new point
        Grapple.distance = (GrabPointinWorld - CharPos).magnitude;

        lr.SetPosition(lr.positionCount - 2, CharPos);

        lr.positionCount--;
        Debug.Log("unwrapped");

    }    
    void Retract(Vector2 RetractFrom)
    {
        Grapple.connectedBody = null; //disconnect joint
        Grapple.enabled = false;

        var retracttime = (RetractFrom - CharPos).magnitude / retractspeed; 
        if (tret < retracttime) //run retraction animation
        {
            lr.SetPosition(lr.positionCount - 1, CharPos);
            Vector3 newposret;
            newposret = Vector3.Lerp(RetractFrom, CharPos, tret / retracttime);
            lr.SetPosition(0, newposret);
            tret += Time.deltaTime;
        }
        else if (tret >= retracttime)
        {
            WrappingPoints.Clear();
            WrappingBodies.Clear();
            WrappingAngleLookup.Clear();
            lr.positionCount = 0;
            ReadytoRetract = false;
        }

    }

}