Advertisement
Guest User

BikeControl.cs

a guest
May 22nd, 2018
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 52.35 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. #pragma warning disable 0414
  6.  
  7. public enum ControlMode { simple = 1, touch = 2 }
  8. public class BikeControl:MonoBehaviour {
  9.    
  10.     public bool racingModified = false;
  11.     public bool isAI = false;
  12.    
  13.     public static BikeControl Instance;
  14.     public bool isAutorotatorEnable=true;
  15.     public ControlMode controlMode = ControlMode.simple;
  16.    
  17.     //Original code was so fucking messy, adding more stuff is literally the best option
  18.     public float AI_ACCEL = 0f;
  19.     public float AI_STEER = 0f;
  20.     public float AI_BRAKE = 0f;
  21.     public float AI_HANDBRAKE = 0f;
  22.    
  23.     // Wheels Setting /////////////////////////////////
  24.  
  25.     public bool FallOnCrash = false;
  26.  
  27.     public BikeWheels bikeWheels;
  28.  
  29.     [System.Serializable]
  30.     public class BikeWheels {
  31.  
  32.         public ConnectWheel wheels;
  33.         public WheelSetting setting;
  34.     }
  35.  
  36.  
  37.     [System.Serializable]
  38.     public class ConnectWheel {
  39.  
  40.         public Transform wheelFront; // connect to Front Right Wheel transform
  41.         public Transform wheelBack; // connect to Front Left Wheel transform
  42.  
  43.         public Transform AxleFront; // connect to Back Right Wheel transform
  44.         public Transform AxleBack; // connect to Back Left Wheel transform
  45.  
  46.     }
  47.  
  48.  
  49.  
  50.  
  51.     [System.Serializable]
  52.     public class WheelSetting {
  53.  
  54.         public bool AutomaticRadius = true;
  55.         public float Radius = 0.25f; // the radius of the wheels
  56.         public float Weight = 3f; // the weight of a wheel
  57.         public float Distance = 0.2f;
  58.  
  59.     }
  60.  
  61.  
  62.  
  63.  
  64.     // Lights Setting /////////////////////////////////
  65.  
  66.     public BikeLights bikeLights;
  67.  
  68.     [System.Serializable]
  69.     public class BikeLights {
  70.  
  71.         public Light[] brakeLights;
  72.  
  73.     }
  74.  
  75.  
  76.     // Bike sounds /////////////////////////////////
  77.  
  78.     public BikeSounds bikeSounds;
  79.  
  80.     [System.Serializable]
  81.     public class BikeSounds {
  82.         public AudioSource lowCrash;
  83.         public AudioSource nitro;
  84.         public AudioSource switchGear;
  85.         public AudioSource skidding;
  86.  
  87.     }
  88.  
  89.     // Bike Particle /////////////////////////////////
  90.  
  91.     public BikeParticles bikeParticles;
  92.     private GameObject[] Particle = new GameObject[4];
  93.  
  94.     [System.Serializable]
  95.     public class BikeParticles {
  96.         public GameObject brakeParticlePerfab;
  97.         public ParticleSystem shiftParticle1,shiftParticle2;
  98.     }
  99.  
  100.  
  101.  
  102.  
  103.     [System.Serializable]
  104.     public class HitGround {
  105.         public string tag = "street";
  106.         public AudioClip groundSound;
  107.         public Color brakeColor;
  108.  
  109.     }
  110.  
  111.  
  112.     // Bike Engine Setting /////////////////////////////////
  113.  
  114.     public BikeSetting bikeSetting;
  115.     public float steeringSpeed = 8f;
  116.    
  117.     [System.Serializable]
  118.     public class BikeSetting {
  119.  
  120.  
  121.         public bool showNormalGizmos = false;
  122.  
  123.         public HitGround[] hitGround;
  124.  
  125.         public Transform bikeSteer;
  126.  
  127.         public float springs = 7000.0f;
  128.         public float dampers = 10.0f;
  129.  
  130.         public float bikePower = 70;
  131.         public float shiftPower = 100;
  132.         public float brakePower = 500;
  133.  
  134.         public Vector3 shiftCentre = new Vector3(0.0f,-0.6f,0.0f); // offset of centre of mass
  135.         public Vector3 shiftSpeed = new Vector3(0.0f,-0.5f,-0.8f); // offset of centre of mass
  136.  
  137.         public float maxSteerAngle = 30.0f; // max angle of steering wheels
  138.         public float maxTurn = 1.5f;
  139.  
  140.         public float shiftDownRPM = 1500.0f; // rpm script will shift gear down
  141.         public float shiftUpRPM = 4000.0f; // rpm script will shift gear up
  142.         public float idleRPM = 700.0f; // idle rpm
  143.  
  144.         public float stiffnessFront = 0.1f;         // for wheels, determines slip
  145.         public float stiffnessFrontSide = 0.1f;     // give both wheels separate slip values.
  146.         public float stiffnessBack = 0.1f;
  147.         public float stiffnessBackSide = 0.1f;
  148.  
  149.  
  150.  
  151.         public bool automatic = true;  // automatic, if true bike shifts auto
  152.  
  153.         public float[] gears = { -10f,9f,6f,4.5f,3f,2.5f }; // gear ratios (index 0 is reverse)
  154.  
  155.  
  156.     }
  157.  
  158.  
  159.  
  160.  
  161.  
  162.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  163.  
  164.  
  165.     private Quaternion SteerRotation;
  166.  
  167.  
  168.     [HideInInspector]
  169.     public bool grounded = true;
  170.  
  171.     private float MotorRotation;
  172.  
  173.     [HideInInspector]
  174.     public bool crash;
  175.  
  176.    
  177.     public float _steer = 0f;
  178.     public float steer {
  179.         get{ return _steer; }
  180.         set{
  181.             //Debug.Log("Steer being set.." + Time.deltaTime);
  182.             _steer = value;
  183.         }
  184.     }
  185.    
  186.     //[HideInInspector]
  187.     //public float steer = 0; // steering -1.0 .. 1.0
  188.     //[HideInInspector]
  189.     public float steer2;
  190.    
  191.     //[HideInInspector]
  192.     public float accel = 0.0f; // accelerating -1.0 .. 1.0
  193.    
  194.     //AI only.
  195.     public float brake = 0f;
  196.  
  197.     private bool shifmotor;
  198.  
  199.     [HideInInspector]
  200.     public float curTorque = 100f;
  201.  
  202.     [HideInInspector]
  203.     public float powerShift = 100;
  204.  
  205.     [HideInInspector]
  206.     public bool shift;
  207.  
  208.  
  209.  
  210.     private float torque = 100f; // the base power of the engine (per wheel, and before gears)
  211.  
  212.  
  213.     [HideInInspector]
  214.     public float speed = 0.0f;
  215.     public bool healthEnabled = false;
  216.     public float health = 1f;
  217.     public bool enableCarReset = true;
  218.  
  219.     private float lastSpeed = -10.0f;
  220.  
  221.  
  222.     // table of efficiency at certain RPM, in tableStep RPM increases, 1.0f is 100% efficient
  223.     // at the given RPM, current table has 100% at around 2000RPM
  224.     float[] efficiencyTable = { 0.6f,0.65f,0.7f,0.75f,0.8f,0.85f,0.9f,1.0f,1.0f,0.95f,0.80f,0.70f,0.60f,0.5f,0.45f,0.40f,0.36f,0.33f,0.30f,0.20f,0.10f,0.05f };
  225.  
  226.     // the scale of the indices in table, so with 250f, 750RPM translates to efficiencyTable[3].
  227.     float efficiencyTableStep = 250.0f;
  228.  
  229.  
  230.     private float shiftDelay = 0.0f;
  231.  
  232.     // shortcut to the component audiosource (engine sound).
  233.     private AudioSource audioSource;
  234.  
  235.  
  236.     [HideInInspector]
  237.     public int currentGear = 1;
  238.     [HideInInspector]
  239.     public float motorRPM = 0.0f;
  240.  
  241.  
  242.  
  243.     private float wantedRPM = 0.0f;
  244.     private float w_rotate;
  245.  
  246.     public GameObject preStartMenu;
  247.     public GameObject initMenuButtons;
  248.     public GameObject leftRigbhtButtons;
  249.     public GameObject touchometer;
  250.    
  251.     public GameObject debugBar;
  252.    
  253.     private Skidmarks m_skidmarks;
  254.     private ParticleEmitter m_skidSmoke;
  255.     public float skidMarkMin = 0.2f;
  256.     public float skidSmokeMin = 0.4f;
  257.     public float skidMarkMax = 10f;
  258.     public float skidMarkOffset = 0.1f;
  259.     public float skidVolumeMultiplier = 1.0f;
  260.     public float skidVolumeSpeed = 2.48f;
  261.     private float skidVolume = 0f;
  262.     private float skidVolumeTarg = 0f;
  263.    
  264.     ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  265.    
  266.    
  267.     public float skidValue{
  268.         get{ return skidVolume; }
  269.     }
  270.  
  271.     private WheelComponent[] wheels;
  272.  
  273.  
  274.  
  275.     private class WheelComponent {
  276.  
  277.         public Transform wheel;
  278.         public Transform axle;
  279.         public WheelCollider collider;
  280.         public Vector3 startPos;
  281.         public float rotation = 0.0f;
  282.         public float maxSteer;
  283.         public bool drive;
  284.         public float pos_y = 0.0f;
  285.         //MOD
  286.         public bool cachedGrounded = false;
  287.         public string cachedTag = "Untagged";
  288.        
  289.     }
  290.    
  291.    
  292.    
  293.     public Transform camTarget;
  294.     public Transform followTarget;
  295.     public Transform dashTarget;
  296.    
  297.    
  298.     //Nope, move it to start, so we can set isAI via script
  299.     //void Awake() {
  300.     //  Instance = this;
  301.     //}
  302.    
  303.     public float getSpeed(){
  304.         return speed;
  305.     }
  306.     public float getHealth(){
  307.         return 1;
  308.     }
  309.    
  310.     private WheelComponent SetWheelComponent(Transform wheel,Transform axle,bool drive,float maxSteer,float pos_y) {
  311.  
  312.         WheelComponent result = new WheelComponent();
  313.         GameObject wheelCol = new GameObject(wheel.name + "WheelCollider");
  314.        
  315.        
  316.         wheelCol.transform.parent = transform;
  317.         wheelCol.transform.position = wheel.position;
  318.         wheelCol.transform.eulerAngles = transform.eulerAngles;
  319.         pos_y = wheelCol.transform.localPosition.y;
  320.  
  321.  
  322.         wheel.gameObject.AddComponent<WheelCollider>();
  323.         WheelCollider col = (WheelCollider)wheelCol.AddComponent(typeof(WheelCollider));
  324.         col.transform.localScale = wheel.localScale;
  325.         col.radius = wheel.GetComponent<WheelCollider>().radius;
  326.         //Destroy(wheel.transform.GetComponent<WheelCollider>());
  327.  
  328.  
  329.         result.drive = drive;
  330.         result.wheel = wheel;
  331.         result.axle = axle;
  332.         result.collider = wheelCol.GetComponent<WheelCollider>();
  333.         result.pos_y = pos_y;
  334.         result.maxSteer = maxSteer;
  335.         result.startPos = axle.transform.localPosition;
  336.  
  337.         return result;
  338.  
  339.     }
  340.  
  341.  
  342.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  343.  
  344.     //bool useTilt = false;
  345.     bool useTilt{
  346.         get{
  347.             return !isAI && CarMain.accelerometerControls;
  348.         }
  349.     }
  350.     private bool lastUseTilt = false;
  351.  
  352.     bool moveUp = false;
  353.     bool moveDown = false;
  354.     bool moveLeft = false;
  355.     bool moveRight = false;
  356.     bool nitro = false;
  357.     float moveLeftValue = 0f;
  358.     float moveRightValue = 0f;
  359.     float moveUpValue = 0f;
  360.     float moveDownValue = 0f;
  361.     float stepForValue = 0.1f;//0.01f;
  362.     Vector3 startPos;
  363.     Quaternion startRotate;
  364.  
  365.     public void OnTiltPress() {
  366.         //useTilt = true;
  367.         //StartMenu.Instance.StartGame(true);
  368.         preStartMenu.SetActive(false);
  369.         initMenuButtons.SetActive(true);
  370.         leftRigbhtButtons.SetActive(false);
  371.         touchometer.SetActive(true);
  372.         //Game.isRunning = true;
  373.     }
  374.  
  375.     public void OnArrowsPress() {
  376.         //useTilt = false;
  377.         preStartMenu.SetActive(false);
  378.         initMenuButtons.SetActive(true);
  379.         leftRigbhtButtons.SetActive(true);
  380.         //StartMenu.Instance.StartGame(false);
  381.         touchometer.SetActive(true);
  382.         //Game.isRunning = true;
  383.     }
  384.  
  385.     public void PressNitroBtn() {
  386.         shift = true;
  387.         Debug.Log("press nitro");
  388.     }
  389.     public void ReleaseNitroBtn() {
  390.         shift = false;
  391.         Debug.Log("release nitro");
  392.     }
  393.  
  394.     public void PressMoveUpBtn() {
  395.         moveUp = true;
  396.     }
  397.     public void ReleaseMoveUpBtn() {
  398.         moveUpValue = 0f;
  399.         moveUp = false;
  400.     }
  401.  
  402.     public void PressMoveDownBtn() {
  403.         moveDown = true;
  404.     }
  405.     public void ReleaseMoveDownBtn() {
  406.         moveDownValue = 0f;
  407.         moveDown = false;
  408.     }
  409.  
  410.     public void PressMoveLeftBtn() {
  411.         moveLeft = true;
  412.     }
  413.     public void ReleaseMoveLeftBtn() {
  414.         moveLeftValue = 0f;
  415.         moveLeft = false;
  416.     }
  417.  
  418.     public void PressMoveRightBtn() {
  419.         moveRight = true;
  420.     }
  421.     public void ReleaseMoveRightBtn() {
  422.         moveRightValue = 0f;
  423.         moveRight = false;
  424.     }
  425.  
  426.     public void ToggleControlTypeBtn() {
  427.         //useTilt = !useTilt;
  428.         moveRightValue = 0f;
  429.         moveRight = false;
  430.         moveLeftValue = 0f;
  431.         moveLeft = false;
  432.         leftRigbhtButtons.SetActive(!leftRigbhtButtons.activeInHierarchy);
  433.         //StartMenu.Instance.showLeftRightButtons(!useTilt);
  434.     }
  435.    
  436.    
  437.     private int cooldownCount = 0;
  438.    
  439.    
  440.     public void Reload() {
  441.         resetVehicle( false );
  442.     }
  443.    
  444.     public void resetVehicle( bool toStart = false ){
  445.         //gameObject.transform.position = startPos;
  446.         //gameObject.transform.rotation = startRotate;
  447.         //gameObject.rigidbody.velocity = Vector3.zero;
  448.         //
  449.         if(carResetCooldown > 0f){
  450.             carResetCooldown = 1;
  451.             cooldownCount ++;
  452.             if ( cooldownCount < 3 ){
  453.                 return;
  454.             }else if ( cooldownCount == 3 ){
  455.                 Vector3 vecUp = ( this.transform.forward * -1 );
  456.                 vecUp.y = Mathf.Abs( vecUp.y );
  457.                 this.transform.forward = vecUp;
  458.                 this.transform.localEulerAngles = Vector3.zero;
  459.                 this.GetComponent<Rigidbody>().velocity = Vector3.zero;
  460.                 this.GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
  461.                 return;
  462.             }else if ( cooldownCount == 4 ){
  463.                 this.transform.position = startPos;
  464.                 this.transform.localEulerAngles = Vector3.zero;
  465.                 this.GetComponent<Rigidbody>().velocity = Vector3.zero;
  466.                 this.GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
  467.                 return;
  468.             }
  469.         }else{
  470.             cooldownCount = 0;
  471.         }
  472.        
  473.         CrashLogger.registerRestore( ((Screen_Manager.currentlyStunt)?0:1), this.transform.position, this.transform.forward, this.transform.up, false );
  474.        
  475.         carResetCooldown = 1.0f;
  476.         this.transform.localEulerAngles = new Vector3(0,this.transform.localEulerAngles.y,0);
  477.         if ( toStart ){
  478.             if ( LevelManager.getNearestRestorePoint( this.transform.position ) ){
  479.                 this.transform.position = LevelManager.lastRestorePoint;
  480.             }else{
  481.                 if ( myController != null ){
  482.                     this.transform.position = myController.startPosition;
  483.                 }else{
  484.                     Debug.LogError("Bike: Nowhere to restore!");
  485.                 }
  486.             }
  487.         }else{
  488.             this.transform.position += new Vector3(0.5f, 1.6f, 0.5f);
  489.         }
  490.         this.GetComponent<Rigidbody>().velocity = Vector3.zero;
  491.         this.GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
  492.        
  493.        
  494.        
  495.     }
  496.    
  497.     public void sendToCamera(){
  498.             CamSmoothFollow.nextCenterOfMass = GetComponent<Rigidbody>().transform;
  499.             CameraSwitch.nextDashTarget = dashTarget;
  500.             CameraSwitch.nextFollowTarget = followTarget;
  501.     }
  502.    
  503.     private Motorcycle_Controller myController;
  504.     void Start() {
  505.        
  506.         if ( !isAI ){
  507.             Instance = this;
  508.             sendToCamera();
  509.         }
  510.        
  511.         lastUseTilt = useTilt;
  512.        
  513.         m_skidmarks = FindObjectOfType(typeof(Skidmarks)) as Skidmarks;
  514.         if (m_skidmarks){
  515.             m_skidSmoke = m_skidmarks.GetComponentInChildren<ParticleEmitter>() as ParticleEmitter;
  516.         }else{
  517.             if ( Application.loadedLevel > 0 ){
  518.                 Debug.Log("No skidmarks in this scene.");
  519.             }
  520.         }
  521.        
  522.         myController = Motorcycle_Controller.getMCFromParent( this.transform );
  523.        
  524.         SteerRotation = bikeSetting.bikeSteer.localRotation;
  525.         wheels = new WheelComponent[2];
  526.  
  527.         wheels[0] = SetWheelComponent(bikeWheels.wheels.wheelFront, bikeWheels.wheels.AxleFront,    false,  bikeSetting.maxSteerAngle,  bikeWheels.wheels.AxleFront.localPosition.y);
  528.         wheels[1] = SetWheelComponent(bikeWheels.wheels.wheelBack,  bikeWheels.wheels.AxleBack,     true,   bikeSetting.maxSteerAngle,  bikeWheels.wheels.AxleBack.localPosition.y);
  529.  
  530.         startPos = transform.position;
  531.         startRotate = transform.rotation;
  532.        
  533.         updateWheelValues();
  534.        
  535.         audioSource = (AudioSource)GetComponent(typeof(AudioSource));
  536.         if(audioSource == null) {
  537.             Debug.Log("No audio please add one");
  538.         }
  539.     }
  540.  
  541.  
  542.  
  543.     public void updateWheelValues(){
  544.        
  545.         int currentWheelID = 0;
  546.         foreach(WheelComponent w in wheels) {
  547.  
  548.  
  549.             WheelCollider col = w.collider;
  550.             col.suspensionDistance = bikeWheels.setting.Distance;
  551.             JointSpring js = col.suspensionSpring;
  552.  
  553.             js.spring = bikeSetting.springs;
  554.             js.damper = bikeSetting.dampers;
  555.             col.suspensionSpring = js;
  556.            
  557.             if(!bikeWheels.setting.AutomaticRadius)
  558.                 col.radius = bikeWheels.setting.Radius;
  559.            
  560.             col.mass = bikeWheels.setting.Weight;
  561.  
  562.             float iceFactor = 1.0f;
  563.             //if ( myController != null && GameManager.playerInGame ){
  564.             //  if ( myController._in_mud ){
  565.             //      iceFactor *= 0.1f;
  566.             //  }
  567.             //}
  568.  
  569.            
  570.             WheelFrictionCurve fc = col.forwardFriction;
  571.            
  572.             fc.asymptoteValue = 5000.0f;
  573.             fc.extremumSlip = 2.0f;
  574.             fc.asymptoteSlip = 20.0f;
  575.             if ( currentWheelID == 0 ){
  576.                 fc.stiffness = bikeSetting.stiffnessFront;// * iceFactor;
  577.             }else{
  578.                 fc.stiffness = bikeSetting.stiffnessBack;// * iceFactor;
  579.             }
  580.             col.forwardFriction = fc;
  581.            
  582.             fc = col.sidewaysFriction;
  583.             fc.asymptoteValue = 7500.0f;
  584.             fc.asymptoteSlip = 2.0f;
  585.            
  586.             if ( currentWheelID == 0 ){
  587.                 fc.stiffness = bikeSetting.stiffnessFrontSide * iceFactor;
  588.             }else{
  589.                 //Don't skid while we're upside down
  590.                 if ( myController._in_gravity ){
  591.                     fc.stiffness = bikeSetting.stiffnessBackSide;
  592.                 }else{
  593.                     fc.stiffness = getRearSlide() * iceFactor;
  594.                 }
  595.             }
  596.             col.sidewaysFriction = fc;
  597.                        
  598.             currentWheelID++;
  599.         }
  600.        
  601.        
  602.        
  603.        
  604.     }
  605.    
  606.     public AnimationCurve rearSlideCurve;       //rear friction based on speed.
  607.     public AnimationCurve frictionDecreaseWithLeanCurve;    //leaning to slide a bit
  608.     public AnimationCurve rearTurnCurve;        //dampen rear turning based on speed
  609.     public AnimationCurve pitchTurnCurve;       //dampen turning when driving upa slope
  610.     public AnimationCurve bankTurnCurve;        //dampen turning when on a banked surface
  611.     public AnimationCurve bankPowerCurve;       //engine power when on banked surface.
  612.     public bool useRearSteer = true;
  613.     public float getClampedSpeedPercent(){
  614.         float percent = Mathf.Clamp01( Mathf.Abs(speed / 300));
  615.         return percent;
  616.     }
  617.     public float getRearSlide(){
  618.        
  619.         float returnVal = rearSlideCurve.Evaluate( getClampedSpeedPercent() );
  620.         if ( handbrakeButton ) returnVal *= 0.48f;
  621.         float tiltPercent = Mathf.Clamp01( (Mathf.Abs(MotorRotation) / 60) );
  622.         //float before = tiltPercent;
  623.         float tiltMultiplier = frictionDecreaseWithLeanCurve.Evaluate( tiltPercent );
  624.         returnVal *= tiltMultiplier;
  625.         //float middle = tiltMultiplier;
  626.         returnVal += ( Mathf.Abs( surfaceAngle / 90 ) );
  627.         returnVal = Mathf.Clamp01( returnVal );
  628.         //Debug.Log(" F M A " + before.ToString("F2") + "  " + middle.ToString("F2") + "   " + returnVal.ToString("F2") );
  629.         return returnVal;
  630.        
  631.     }
  632.    
  633.    
  634.    
  635.    
  636.     // handle shifting a gear up
  637.     public void ShiftUp() {
  638.         float now = Time.timeSinceLevelLoad;
  639.  
  640.         // check if we have waited long enough to shift
  641.         if(now < shiftDelay)
  642.             return;
  643.  
  644.         // check if we can shift up
  645.         if(currentGear < bikeSetting.gears.Length - 1) {
  646.  
  647.             if(!bikeSounds.switchGear.isPlaying && !AppSoundManager.MuteSfx)
  648.                 bikeSounds.switchGear.GetComponent<AudioSource>().Play();
  649.  
  650.  
  651.             currentGear++;
  652.  
  653.             // we delay the next shift with 1s. (sorry, hardcoded)
  654.             shiftDelay = now + 1.0f;
  655.         }
  656.     }
  657.  
  658.  
  659.     // handle shifting a gear down
  660.     public void ShiftDown() {
  661.         float now = Time.timeSinceLevelLoad;
  662.  
  663.         // check if we have waited long enough to shift
  664.         if(now < shiftDelay)
  665.             return;
  666.  
  667.         // check if we can shift down (note gear 0 is reverse)
  668.         if(currentGear > 0) {
  669.  
  670.             if(!bikeSounds.switchGear.isPlaying && !AppSoundManager.MuteSfx)
  671.                 bikeSounds.switchGear.GetComponent<AudioSource>().Play();
  672.  
  673.  
  674.             currentGear--;
  675.  
  676.             // we delay the next shift with 1/10s. (sorry, hardcoded)
  677.             shiftDelay = now + 0.1f;
  678.         }
  679.     }
  680.  
  681.     //Jesus christ, why was this static?
  682.     private float timeOfLastNotifiedHit = 0f;
  683.    
  684.     public void OnCollisionEnter(Collision collision) {
  685.        
  686.         if ( myController != null && myController.isInTeam ){
  687.             if ( Time.time > timeOfLastNotifiedHit + 0.5f ){
  688.                 if ( MissionManager.notifyHit( myController, collision ) ){
  689.                     timeOfLastNotifiedHit = Time.time;
  690.                 }
  691.             }
  692.         }
  693.        
  694.         if ( collision.contacts.Length > 0 ){
  695.             if ( !(collision.contacts[0].thisCollider is WheelCollider) && !(collision.contacts[0].otherCollider is WheelCollider) ){
  696.                 Vector3 rv = collision.relativeVelocity;
  697.                 if ( rv.magnitude > 10 ){
  698.                     CrashLogger.registerCrash( ((Screen_Manager.currentlyStunt)?0:1), collision.contacts[0].point, collision.relativeVelocity, collision.relativeVelocity.magnitude );
  699.                 }
  700.                
  701.             }
  702.         }
  703.         bikeSounds.lowCrash.Play();
  704.  
  705.     }
  706.  
  707.  
  708.  
  709.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  710.    
  711.     private bool forwardButton = false;
  712.     private bool backwardButton = false;
  713.     private bool leftButton = false;
  714.     private bool rightButton = false;
  715.     private bool handbrakeButton = false;
  716.    
  717.    
  718.     public bool isLeftDown{ get{ return leftButton;} }
  719.     public bool isRightDown { get{ return rightButton;} }
  720.     public bool isForwardDown{ get{ return forwardButton;} }
  721.     public bool isBackwardDown{ get{ return backwardButton;} }
  722.     public bool isHandbrakedown{ get{ return handbrakeButton;} }
  723.    
  724.     public void setKey (string key, bool state){
  725.         switch(key)
  726.         {
  727.             case "forward":
  728.                 forwardButton = state;
  729.                 break;
  730.                
  731.             case "backward":
  732.                 backwardButton = state;
  733.                 break;
  734.                
  735.             case "left":
  736.                 leftButton = state;
  737.                 break;
  738.                
  739.             case "right":
  740.                 rightButton = state;
  741.                 break;
  742.                
  743.             case "handbrake":
  744.                 handbrakeButton = state;
  745.                 break;
  746.         }
  747.     }
  748.    
  749.     public bool isOnMud(){
  750.         if ( wheels == null || wheels.Length == 0 ) return false;
  751.         foreach(WheelComponent Wheel in wheels) {
  752.             if ( Wheel.cachedGrounded && Wheel.cachedTag == "MUD" ){
  753.                 return true;
  754.             }
  755.         }
  756.         return false;
  757.     }
  758.    
  759.     public bool isOnRoad(){
  760.         if ( wheels == null || wheels.Length == 0 ) return false;
  761.         foreach(WheelComponent Wheel in wheels) {
  762.             if ( Wheel.cachedGrounded && Wheel.cachedTag == "Road" ){
  763.                 return true;
  764.             }
  765.             if ( Wheel.cachedGrounded && Wheel.cachedTag == "MUD" ){
  766.                 return true;
  767.             }
  768.         }
  769.         return false;
  770.     }
  771.    
  772.     public float relativeSteeringSpeed = 0f;
  773.     public float uprightTorque = 200;
  774.     public float tStickyFactor = 10000f;
  775.     private float flipRotate = 0.0f;
  776.     private bool OnGround = false;
  777.     void Update() {
  778.        
  779. #if UNITY_EDITOR
  780.        
  781.         if ( Input.GetKeyUp(KeyCode.F) ){
  782.             useRearSteer = !useRearSteer;
  783.             Instructor.flash( "Editor: " + TT.red("Attencione!") );
  784.             Instructor.flash("Using steer helper=" + useRearSteer.ToString());
  785.         }
  786.         if ( Input.GetKey(KeyCode.G) ){
  787.             debugPitch();
  788.         }
  789.        
  790. #endif
  791.        
  792.         carResetCooldown -= Time.deltaTime;
  793.        
  794.         OnGround = false;
  795.        
  796.         myController.wheelFLdown = false;
  797.         myController.wheelFRdown = false;
  798.    
  799.         myController.wheelRLdown = false;
  800.         myController.wheelRRdown = false;
  801.        
  802.         int wheelId = 0;
  803.         WheelHit wombat = new WheelHit();
  804.         foreach(WheelComponent Wheel in wheels) {
  805.            
  806.             if(Wheel.collider.isGrounded) {
  807.                 OnGround = true;
  808.                 Wheel.cachedGrounded = true;
  809.                 Wheel.collider.GetGroundHit( out wombat );
  810.                 Wheel.cachedTag = wombat.collider.tag;
  811.                
  812.                 if ( wheelId == 0 || wheelId == 1 ){
  813.                     myController.wheelFLdown = true;
  814.                     myController.wheelFRdown = true;
  815.                 }else{
  816.                     myController.wheelRLdown = true;
  817.                     myController.wheelRRdown = true;
  818.                 }
  819.             }else{
  820.                 Wheel.cachedGrounded = false;
  821.                 Wheel.cachedTag = "Untagged";
  822.             }
  823.             wheelId++;
  824.         }
  825.  
  826.         accel = 0f;
  827.         //shift = false;
  828.  
  829.         relativeSteeringSpeed = Time.deltaTime * steeringSpeed;
  830.        
  831.         if ( useTilt != lastUseTilt ){
  832.             leftButton = false;
  833.             rightButton = false;
  834.             moveLeftValue = 0;
  835.             moveRightValue = 0;
  836.             moveLeft = false;
  837.             moveRight = false;
  838.             Debug.Log("Tilt changed...");
  839.         }
  840.        
  841.        
  842.         if ( !isAI ){
  843.        
  844.     #if !UNITY_EDITOR
  845.                 if(moveUp || forwardButton){
  846.                     accel = 1f;
  847.                 }
  848.                
  849.                 if(moveDown || backwardButton){
  850.                     accel = -1f;
  851.                 }
  852.                
  853.                 if (shift && speed > 5 && shifmotor)
  854.                 {
  855.                     accel = 1f;
  856.                 }
  857.                
  858.                 if(!useTilt){
  859.                     steer = Mathf.MoveTowards(steer, ( (moveRight||rightButton) ? 1f : (moveLeft||leftButton) ? -1f : 0f), relativeSteeringSpeed);
  860.                 } else {
  861.                    
  862.                     if ( Input.acceleration != null ){
  863.                    
  864.                         float valueLR = Input.acceleration.x;
  865.                         if(valueLR > 0f)
  866.                         {
  867.                             //Debug.Log("LR GR ZERO " + Input.acceleration + " " + valueLR);
  868.                             if(moveLeft||leftButton)
  869.                             {
  870.                                 moveLeft = false;
  871.                                 moveLeftValue = 0f;
  872.                             }
  873.                            
  874.                             moveRight = true;
  875.                             moveRightValue = valueLR;
  876.                            
  877.                             steer = Mathf.MoveTowards(steer, moveRightValue, relativeSteeringSpeed);
  878.                         }
  879.                         else if (valueLR < 0f)
  880.                         {
  881.                            
  882.                             //Debug.Log("LR LT ZERO " + Input.acceleration + " " + valueLR);
  883.                             if(moveRight||rightButton)
  884.                             {
  885.                                 moveRight = false;
  886.                                 moveRightValue = 0f;
  887.                             }
  888.                            
  889.                             moveLeft = true;
  890.                             moveLeftValue = valueLR;
  891.                            
  892.                             steer = Mathf.MoveTowards(steer, moveLeftValue, relativeSteeringSpeed);
  893.                         }
  894.                         else
  895.                         {  
  896.                             //Debug.Log("LR EQ ZERO"  + Input.acceleration + " " + valueLR);
  897.                             moveLeft = false;
  898.                             moveRight = false;
  899.                             moveLeftValue = 0f;
  900.                             moveRightValue = 0f;
  901.                             steer = Mathf.MoveTowards(steer, 0f, relativeSteeringSpeed);
  902.                         }
  903.                    
  904.                     } else{ //! null
  905.                    
  906.                         //Debug.Log("Null accelerometer controls " + Time.time.ToString("F2") );
  907.                    
  908.                     }
  909.                    
  910.                 }
  911.     #else
  912.            
  913.             //Debug.Log("pS " + steer  +  " " + relativeSteeringSpeed + " " + steeringSpeed + "Tt " + Time.deltaTime );
  914.            
  915.             if(!crash) {
  916.                
  917.                 float steerTarg = 0;
  918.                
  919.                 if( ( !isAI && ( Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A) )) || leftButton ) {
  920.                     steer = Mathf.MoveTowards(steer,-1f,relativeSteeringSpeed);
  921.                     //steer += relativeSteeringSpeed;
  922.                     steerTarg = -1f;
  923.                 }
  924.                 else if( ( !isAI && ( Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D) )) || rightButton ) {
  925.                     steer = Mathf.MoveTowards(steer,1f,relativeSteeringSpeed);
  926.                     //steer -= relativeSteeringSpeed;
  927.                     steerTarg = 1;
  928.                 }
  929.                 else {
  930.                     steer = Mathf.MoveTowards(steer,0f,relativeSteeringSpeed * 10);
  931.                     steerTarg = 0;
  932.                 }
  933.                
  934.                 //steer += ( steerTarg - steer ) * relativeSteeringSpeed;
  935.                
  936.                 //Debug.Log(" S " + steer  +  " " + relativeSteeringSpeed + " " + steeringSpeed + "Tt " + Time.deltaTime );
  937.                
  938.                 if(!moveUp && !moveDown){
  939.                     accel = Input.GetAxis("Vertical");
  940.                 }
  941.                 if ( forwardButton ){
  942.                     accel = 1f;
  943.                 }
  944.                 if ( backwardButton ){
  945.                     accel = -1f;
  946.                 }
  947.                
  948.                 //if(!shift)
  949.                     //shift = Input.GetKey(KeyCode.LeftShift) | Input.GetKey(KeyCode.RightShift);
  950.             } else {
  951.                 steer = 0;
  952.             }
  953.     #endif
  954.            
  955.         }else{  // *IS* AI
  956.            
  957.             steer = AI_STEER;
  958.             accel = AI_ACCEL;
  959.             handbrakeButton = (AI_HANDBRAKE>0f);
  960.             brake = AI_BRAKE;
  961.             //brake handled elsewhere
  962.         }
  963.            
  964.         lastUseTilt = useTilt;
  965.  
  966.         // handle automatic shifting
  967.         if(bikeSetting.automatic && (currentGear == 1) && (accel < 0.0f)) {
  968.             if(speed < 1.0f)
  969.                 ShiftDown(); // reverse
  970.  
  971.  
  972.         }
  973.         else if(bikeSetting.automatic && (currentGear == 0) && (accel > 0.0f)) {
  974.             if(speed < 5.0f)
  975.                 ShiftUp(); // go from reverse to first gear
  976.  
  977.         }
  978.         else if(bikeSetting.automatic && (motorRPM > bikeSetting.shiftUpRPM) && (accel > 0.0f)) {
  979.             // if (speed > 20)
  980.             ShiftUp(); // shift up
  981.  
  982.         }
  983.         else if(bikeSetting.automatic && (motorRPM < bikeSetting.shiftDownRPM) && (currentGear > 1)) {
  984.             ShiftDown(); // shift down
  985.         }
  986.  
  987.  
  988.  
  989.  
  990.         if((currentGear == 0)) {
  991.             bikeSetting.shiftCentre.z = -accel / 5.0f;
  992.             if(speed < bikeSetting.gears[0] * -10)
  993.                 accel = -accel; // in automatic mode we need to hold arrow down for reverse
  994.         }
  995.         else {
  996.  
  997.             bikeSetting.shiftCentre.z = -(accel / currentGear) / 3.0f;
  998.         }
  999.  
  1000.         steer2 = Mathf.LerpAngle(steer2,steer * -bikeSetting.maxSteerAngle,Time.deltaTime * 10.0f);
  1001.  
  1002.         MotorRotation = Mathf.Lerp(MotorRotation,steer2 * bikeSetting.maxTurn * (Mathf.Clamp(speed / 100,0.0f,1.0f)),Time.deltaTime * 5.0f);
  1003.  
  1004.         if(bikeSetting.bikeSteer){
  1005.             bikeSetting.bikeSteer.localRotation = SteerRotation * Quaternion.Euler(0,wheels[0].collider.steerAngle,0); // this is 90 degrees around y axis
  1006.         }
  1007.  
  1008.         if(!crash) {
  1009.            
  1010.             /*
  1011.             Quaternion deltaRotation;
  1012.             flipRotate = (transform.eulerAngles.z > 90 && transform.eulerAngles.z < 270) ? 180.0f : 0.0f;
  1013.             deltaRotation= Quaternion.Euler(0f,0f,-fixDegrees(transform.localEulerAngles.z) + (MotorRotation));
  1014.            
  1015.             float NewX = Mathf.Clamp(fixDegrees(rigidbody.rotation.eulerAngles.x),-60f,60f);
  1016.  
  1017.             if(OnGround) {
  1018.                 NewX = rigidbody.rotation.eulerAngles.x;
  1019.             }
  1020.            
  1021.             if(isAutorotatorEnable) {
  1022.                 rigidbody.MoveRotation(Quaternion.Euler(NewX,fixDegrees(rigidbody.rotation.eulerAngles.y),fixDegrees(rigidbody.rotation.eulerAngles.z)) * deltaRotation);
  1023.                 // 1 tilt forward, -1 tilt back
  1024.                 Vector3 AngVelo = rigidbody.angularVelocity;
  1025.                 float NewAngularVeloX = ((Mathf.InverseLerp(60f,-60f,NewX) * 2) - 1); // -1 - 1
  1026.                 if(!OnGround) {
  1027.                     AngVelo.x = (AngVelo.x * (Mathf.Abs(NewAngularVeloX) >= 0.4f ? NewAngularVeloX * 1.5f : 1f));
  1028.                     rigidbody.angularVelocity = AngVelo;
  1029.                 }
  1030.                
  1031.             }
  1032.             */
  1033.  
  1034.         }//!crash
  1035.  
  1036.  
  1037.     }
  1038.    
  1039.  
  1040.    
  1041.    
  1042.    
  1043.     public static Vector3 getLocalRadians(Quaternion rotation)
  1044.     {
  1045.         float pitch = Mathf.Atan2(2*rotation.x*rotation.w - 2*rotation.y*rotation.z, 1 - 2*rotation.x*rotation.x - 2*rotation.z*rotation.z);
  1046.         float yaw = Mathf.Atan2(2*rotation.y*rotation.w - 2*rotation.x*rotation.z, 1 - 2*rotation.y*rotation.y - 2*rotation.z*rotation.z);
  1047.         float roll = Mathf.Asin(2*rotation.x*rotation.y + 2*rotation.z*rotation.w);
  1048.         return new Vector3(pitch, yaw, roll);
  1049.     }
  1050.          
  1051.     public static Vector3 getLocalDegrees(Quaternion rotation)
  1052.     {
  1053.         Vector3 radResult = getLocalRadians(rotation);
  1054.         return new Vector3(radResult.x * Mathf.Rad2Deg, radResult.y * Mathf.Rad2Deg, radResult.z * Mathf.Rad2Deg);
  1055.     }
  1056.    
  1057.    
  1058.     //Works when rotated around the Y axis
  1059.     public float getRollDegrees(){
  1060.        
  1061.         float oVal = GetComponent<Rigidbody>().transform.localEulerAngles.z;
  1062.         while (oVal < 0 ) oVal += 360;
  1063.         while (oVal >= 360) oVal -= 360;
  1064.         if ( oVal > 180 ) oVal -= 360;
  1065.         return oVal;
  1066.        
  1067.     }
  1068.    
  1069.     //Did you reformat this by the way? it's my code but not my code style. weird.
  1070.     private float fixDegrees(float iVal) {
  1071.         float oVal = iVal;
  1072.         while(oVal < 0)
  1073.             oVal += 360;
  1074.         while(oVal > 360)
  1075.             oVal -= 360;
  1076.         if(oVal > 180)
  1077.             oVal -= 360;
  1078.         return oVal;
  1079.     }
  1080.  
  1081.  
  1082.     public void putCameraOnViewLoop() {
  1083.  
  1084.  
  1085.     }
  1086.    
  1087.    
  1088.    
  1089.     //==============================
  1090.    
  1091.    
  1092.     public int barrelRollPos = 0;
  1093.     public int flipPos = 0;
  1094.     public bool flipped = false;
  1095.     public GameObject bonusText;
  1096.     string[] stuntText;
  1097.    
  1098.     public float stuntTimer = 0f;
  1099.     public float lastStuntTime = 0f;
  1100.     public int stuntComboCount = 0;
  1101.     public int totalStuntComboTime = 9;
  1102.    
  1103.     public bool leftWheelsStuntActive = false;
  1104.     public bool rightWheelsStuntActive = false;
  1105.     public bool jumpStuntActive = false;
  1106.    
  1107.    
  1108.     //===============================
  1109.     public Vector3 lastCarPosition = Vector3.zero;
  1110.     private bool gotLastPosition = false;
  1111.     private float jumpDistanceTimer = 0f;
  1112.     private float rightWheelsDistance = 0f;
  1113.     private float leftWheelsDistance = 0f;
  1114.     private float driftDistance = 0f;
  1115.     private bool driftingActive = false;
  1116.     private bool lastStuntWasDrift = false; //prevent constant drifting
  1117.     private bool wasBoosting = false;
  1118.     private bool driftCanceled = false;
  1119.     //private bool jumpCanceled = false;
  1120.    
  1121.     private float jumpStartHeight = 0f;
  1122.     private float highestJumpHeight = 0f;
  1123.     private bool highJumpActive = false;
  1124.     private bool justInAir = false;
  1125.     public bool simpleStunts = true;
  1126.     private float carResetCooldown = 0f;
  1127.    
  1128.     public void checkStunts(){
  1129.        
  1130.         Transform thisTransform = this.transform;
  1131.        
  1132.         if ( !simpleStunts ) return;
  1133.         if ( carResetCooldown > 0 ) return;
  1134.         if ( SimpleBonusHandler.Instance == null ) return;
  1135.        
  1136.         if ( !gotLastPosition ){
  1137.             lastCarPosition = thisTransform.position;
  1138.             gotLastPosition = true;
  1139.         }
  1140.         Vector3 delta = (thisTransform.position - lastCarPosition);
  1141.         delta.Scale( new Vector3( 1, 0, 1 ) ) ;
  1142.         float distanceSinceLastFrame = delta.magnitude;
  1143.        
  1144.         bool mustCancel = mobileHUD_handler.pendingRestore;
  1145.         mobileHUD_handler.pendingRestore = false;
  1146.        
  1147.         bool[] gWheel = myController.checkWheelsGrounded();
  1148.         //bool allGrounded = gWheel[0] && gWheel[1] && gWheel[2] && gWheel[3];
  1149.         bool allUnGrounded = !gWheel[0] && !gWheel[1] && !gWheel[2] && !gWheel[3];
  1150.         bool oneGrounded = gWheel[0] || gWheel[1] || gWheel[2] || gWheel[3];
  1151.        
  1152.        
  1153.         if( allUnGrounded && !mustCancel && myController.body.velocity.magnitude > 0.01f ){
  1154.             jumpDistanceTimer += distanceSinceLastFrame;
  1155.             jumpStuntActive = jumpDistanceTimer > 15f;
  1156.             if( jumpStuntActive ){
  1157.                 //createBonusText( "Jump " + jumpDistanceTimer.ToString("F2") + "m", jumpDistanceTimer );
  1158.                 if ( !isAI ) SimpleBonusHandler.Instance.registerJump( 0, jumpDistanceTimer );
  1159.             }
  1160.         } else {
  1161.             if(jumpStuntActive){
  1162.                 //jumpCanceled = false;
  1163.                 SimpleBonusHandler.Instance.finishedJump( 0 );
  1164.                 jumpStuntActive = false;
  1165.             }
  1166.             jumpDistanceTimer = 0;
  1167.         }
  1168.        
  1169.        
  1170.         if ( allUnGrounded && !mustCancel ){
  1171.             justInAir = true;
  1172.             float heightDelta = thisTransform.position.y - jumpStartHeight;
  1173.             //highJumpActive = heightDelta > 8f;
  1174.             if ( heightDelta > 8f || highestJumpHeight > 8f )  highJumpActive = true;
  1175.             if ( highJumpActive ){
  1176.                 if ( heightDelta > highestJumpHeight || (Motorcycle_Controller.playerCar != null && Motorcycle_Controller.playerCar.canFly) ){
  1177.                     highestJumpHeight = heightDelta;
  1178.                 }
  1179.                 if ( !isAI )SimpleBonusHandler.Instance.registerJump( 1, highestJumpHeight );
  1180.             }
  1181.         }else{
  1182.            
  1183.         }
  1184.         if ( oneGrounded || mustCancel ){
  1185.            
  1186.             if ( highJumpActive ){
  1187.                 highJumpActive = false;
  1188.                 SimpleBonusHandler.Instance.finishedJump( 1 );
  1189.             }
  1190.            
  1191.             //reset the high jump
  1192.             jumpStartHeight = thisTransform.position.y;
  1193.             highestJumpHeight = 0f;
  1194.             //reset the flips
  1195.             if ( justInAir ){
  1196.                 justInAir = false;
  1197.                 SimpleBonusHandler.Instance.finishedJump( 2 );
  1198.             }
  1199.         }
  1200.        
  1201.         //Flippyfliptime
  1202.         if ( mustCancel ) flipPos = 0;
  1203.         // The car is doing a flip
  1204.         if((transform.eulerAngles.x < 35 && flipped == false) || (transform.eulerAngles.x >= 270 && flipped == false) && !mustCancel){
  1205.             // Car front is pointing bottom left to top left (Tested and working)
  1206.        
  1207.             if(flipPos == 4){
  1208.                 // The car rolled from the right angle position and finished in the left angle position
  1209.                 if ( !isAI )SimpleBonusHandler.Instance.registerJump( 2, 1 );
  1210.                 lastStuntWasDrift = false;
  1211.             }
  1212.            
  1213.             // If the car flips from this angle then it will be a forward flip
  1214.             flipPos = 1;
  1215.         } else if((transform.eulerAngles.x < 35 && flipped == true) || (transform.eulerAngles.x >= 270 && flipped == true)){
  1216.             // Car front is pointing bottom right to top right (Tested and working)
  1217.        
  1218.             if(flipPos == 3){
  1219.                 // The car rolled from the left angle position and finished in the right angle position
  1220.                 if ( !isAI )SimpleBonusHandler.Instance.registerJump( 2, 1 );
  1221.                 lastStuntWasDrift = false;
  1222.             }
  1223.            
  1224.             // If the car flips from this angle then it will be a backward flip
  1225.             flipPos = 2;
  1226.         } else {
  1227.             // Were flipping!
  1228.             // This is a flip not a roll, cancel the current rolls
  1229.             barrelRollPos = 0;
  1230.                    
  1231.             if(flipPos == 1){
  1232.                 //Debug.Log("Flip started [forward]!");
  1233.                
  1234.                 // Set the flip pos to 3 so if we enter the > 200 angle we know we did a full flip
  1235.                 flipPos = 3;
  1236.             } else if(flipPos == 2) {
  1237.                 //Debug.Log("Flip started [backward]!");
  1238.                
  1239.                 // Set the flip pos to 4 so if we enter the < 160 angle we know we did a full flip
  1240.                 flipPos = 4;
  1241.             }  
  1242.         }
  1243.        
  1244.         if ( mustCancel ) barrelRollPos = 0;
  1245.         // Car is doing a barrel roll
  1246.         if(transform.eulerAngles.z < 140){
  1247.             flipped = false;
  1248.            
  1249.             if(barrelRollPos == 4){
  1250.                 // The car rolled from the right angle position and finished in the left angle position
  1251.                 //createBonusText("right roll", stuntTimer);
  1252.                 if ( !isAI )SimpleBonusHandler.Instance.registerJump( 2, 1 );
  1253.                 lastStuntWasDrift = false;
  1254.             }
  1255.            
  1256.             // If the car rolls from this angle then it will be a left roll
  1257.             barrelRollPos = 1;
  1258.         } else if(transform.eulerAngles.z > 220){
  1259.             flipped = false;
  1260.        
  1261.             if(barrelRollPos == 3){
  1262.                 // The car rolled from the left angle position and finished in the right angle position
  1263.                 //createBonusText("left roll", stuntTimer);
  1264.                 SimpleBonusHandler.Instance.registerJump( 2, 1 );
  1265.                 lastStuntWasDrift = false;
  1266.             }
  1267.            
  1268.             // If the car rolls from this angle then it will be a right roll
  1269.             barrelRollPos = 2;
  1270.         } else {
  1271.             flipped = true; // Is the y axis flipped?
  1272.        
  1273.             // Were rolling!
  1274.             if(barrelRollPos == 1){
  1275.                 //Debug.Log("Barrel roll started [rolling left]!");
  1276.                
  1277.                 // Set the barrel pos to 3 so if we enter the > 200 angle we know we did a full roll
  1278.                 barrelRollPos = 3;
  1279.             } else if(barrelRollPos == 2) {
  1280.                 //Debug.Log("Barrel roll started [rolling right]!");
  1281.                
  1282.                 // Set the barrel pos to 4 so if we enter the < 160 angle we know we did a full roll
  1283.                 barrelRollPos = 4;
  1284.             }  
  1285.         }
  1286.        
  1287.        
  1288.         lastCarPosition = thisTransform.position;
  1289.        
  1290.     }
  1291.    
  1292.    
  1293.    
  1294.    
  1295.     /// <summary>
  1296.     /// Fixes an angle between -180 and 180 degrees
  1297.     /// </summary>
  1298.     private float fixAngle( float inAngle ){
  1299.         while ( inAngle > 360 ) inAngle -= 360;
  1300.         while (inAngle < 0 ) inAngle += 360;
  1301.         if ( inAngle > 180 ) inAngle -= 360;
  1302.         return inAngle;
  1303.     }
  1304.    
  1305.    
  1306.    
  1307.     public void debugPitch(){
  1308.        
  1309.     }
  1310.    
  1311.    
  1312.     public float relativeAngleSign( Vector3 normalVector, Vector3 otherVector, Vector3 referenceVector ){
  1313.        
  1314.         //Gonna need some kinda perpendicular reference value
  1315.         Vector3 perpen = Vector3.Cross( normalVector, referenceVector );
  1316.                
  1317.         //float angle2 = Vector3.Angle( referenceVector, otherVector );
  1318.         return Mathf.Sign( Vector3.Dot( perpen, otherVector ) );
  1319.        
  1320.     }
  1321.    
  1322.     private float getCurTorque( bool shiftInstead = false ){
  1323.         float outVal = bikeSetting.bikePower;
  1324.         if ( shiftInstead ) outVal = bikeSetting.shiftPower;
  1325.         float bankVal = Mathf.Clamp01(  Mathf.Abs( surfaceAngle / 90f ) );
  1326.         float pitchVal = Mathf.Clamp01( Mathf.Abs( currentPitch / 90f ) );
  1327.         float first = (bankVal + pitchVal) * 0.5f;
  1328.         outVal *= bankPowerCurve.Evaluate( first );
  1329.         //outVal *= bankPowerCurve.Evaluate( bankVal );
  1330.         //outVal *= bankPowerCurve.Evaluate( pitchVal );
  1331.         //Debug.Log(" FOO " + first.ToString("F2") + "  " + outVal.ToString("F2") );
  1332.         return outVal;
  1333.     }
  1334.    
  1335.     private int lastSkidIndex = -1;
  1336.     private bool lastBoosting = false;
  1337.     private float surfaceAngle = 0f;
  1338.     private float currentPitch = 0f;
  1339.     void FixedUpdate() {
  1340.        
  1341.         if ( !StateManager.shouldThingsTick ) return;
  1342.        
  1343. //#if UNITY_EDITOR
  1344.         updateWheelValues();
  1345.         if ( !isAI ){
  1346.             checkStunts();
  1347.         }
  1348. //#endif
  1349.        
  1350.         bool boosting = !(myController._boostTime <=0 ); //phrased it this way 'cause the zero looks like a happy smiley
  1351.         if ( boosting && !lastBoosting ){
  1352.             PressNitroBtn();
  1353.         }else if ( !boosting && lastBoosting ){
  1354.             ReleaseNitroBtn();
  1355.         }
  1356.         lastBoosting = boosting;
  1357.        
  1358.         speed = GetComponent<Rigidbody>().velocity.magnitude * 3.6f;
  1359.        
  1360.        
  1361.         //float outForce= -tStickyFactor;
  1362.         if ( myController != null && GameManager.playerInGame ){
  1363.             if ( myController._in_gravity || (grounded && Mathf.Abs(surfaceAngle) < 70)  ){
  1364.                 GetComponent<Rigidbody>().AddForce( -Physics.gravity * GetComponent<Rigidbody>().mass * 1.05f );
  1365.                 GetComponent<Rigidbody>().AddForce( this.transform.up * -9.81f * GetComponent<Rigidbody>().mass * 1.05f );
  1366.             }
  1367.             //if ( grounded && Mathf.Abs(surfaceAngle) < 70 ){
  1368.             //  rigidbody.AddForce( -Physics.gravity * rigidbody.mass );
  1369.             //  rigidbody.AddForce( this.transform.up * -9.81f * rigidbody.mass );
  1370.             //}
  1371.         }
  1372.        
  1373.         float delta = Time.fixedDeltaTime;
  1374.         //rigidbody.centerOfMass = bikeSetting.shiftCentre;
  1375.         GetComponent<Rigidbody>().centerOfMass = camTarget.localPosition;
  1376.  
  1377.  
  1378.         foreach(Light brakeLight in bikeLights.brakeLights) {
  1379.             if(accel < 0 || speed < 1.0f) {
  1380.                 brakeLight.intensity = Mathf.Lerp(brakeLight.intensity,8,0.1f);
  1381.             } else {
  1382.                 brakeLight.intensity = Mathf.Lerp(brakeLight.intensity,0,0.1f);
  1383.             }
  1384.         }
  1385.        
  1386.        
  1387.         //applyupright
  1388.         //Apply upright force and optionally account for surface angle
  1389.         surfaceAngle = 0f; //clear it here and not above, because fucntions above
  1390.                             //might want to use the cached value
  1391.         if ( !crash ){
  1392.            
  1393.             WheelHit frontHit;
  1394.             WheelHit backHit;
  1395.             bool frontDown = false;
  1396.             bool backDown = false;
  1397.             frontDown = wheels[0].collider.GetGroundHit( out frontHit );
  1398.             backDown = wheels[1].collider.GetGroundHit( out backHit );
  1399.            
  1400.            
  1401.             if ( frontDown && backDown ){
  1402.                
  1403.                 /*
  1404.                 //For the relative funcion
  1405.                 Vector3 otherAngle = this.transform.up;
  1406.                 Vector3 referenceVector = frontHit.point - backHit.point;
  1407.                 */
  1408.                 Vector3 combinedNormal = frontHit.normal * 0.5f + backHit.normal * 0.5f;
  1409.                 Vector3 pointsVector = frontHit.point - backHit.point;
  1410.                 Vector3 actualPerpendicular = Vector3.Cross( pointsVector, combinedNormal );
  1411.                
  1412.                 Vector3 theoreticalPerpendicular = Vector3.Cross( pointsVector, Vector3.up );
  1413.                
  1414.                 surfaceAngle = Vector3.Angle( actualPerpendicular, theoreticalPerpendicular );
  1415.                
  1416.                 float signValue = Mathf.Sign( actualPerpendicular.y );
  1417.                
  1418.                 surfaceAngle *= signValue;
  1419.                
  1420.                 surfaceAngle = Mathf.Clamp( surfaceAngle, -90f, 90f );
  1421.                 //surfaceAngle *= 0.8f;
  1422.                
  1423.                 //float angle2 = surfaceAngle;
  1424.                 //Debug.Log("A2 " + surfaceAngle );
  1425.                
  1426.             }
  1427.            
  1428.            
  1429.             bool ignoreRotation = (myController._in_gravity) && grounded;
  1430.            
  1431.             float currentRotation = getLocalDegrees(transform.rotation).z;
  1432.             float wantedRotation = MotorRotation;
  1433.             //if ( !ignoreRotation ){
  1434.                 wantedRotation -= (surfaceAngle * 0.8f * 0.8f);
  1435.             //}else{
  1436.             //  Debug.Log("ignoring rotation thingie");
  1437.             //}
  1438.             float deltaZ = wantedRotation - currentRotation;    //(wantedRotation - currentRotation)
  1439.            
  1440.             if ( Mathf.Abs( currentRotation ) > 0.01f && !ignoreRotation ){
  1441.                 GetComponent<Rigidbody>().AddTorque( Quaternion.identity * this.transform.forward * deltaZ * uprightTorque );
  1442.             }
  1443.            
  1444.         }
  1445.        
  1446.         // the RPM we try to achieve.
  1447.         wantedRPM = (5500.0f * accel) * 0.1f + wantedRPM * 0.9f;
  1448.        
  1449.         float rpm = 0.0f;
  1450.         int motorizedWheels = 0;
  1451.         bool floorContact = false;
  1452.         int currentWheel = 0;
  1453.         // calc rpm from current wheel speed and do some updating
  1454.        
  1455.         foreach(WheelComponent w in wheels) {
  1456.            
  1457.             WheelHit hit;
  1458.             WheelCollider col = w.collider;
  1459.            
  1460.             // only calculate rpm on wheels that are connected to engine
  1461.             if(w.drive) {
  1462.                 rpm += col.rpm;
  1463.                 motorizedWheels++;
  1464.             }
  1465.            
  1466.             if ( !isAI ){
  1467.                 if(accel < 0.0f) {
  1468.                     wantedRPM = 0.0f;
  1469.                     col.brakeTorque = bikeSetting.brakePower;
  1470.                 } else {
  1471.                     col.brakeTorque = accel == 0 ? col.brakeTorque = 200 : col.brakeTorque = 1;
  1472.                 }
  1473.             }else{
  1474.                 col.brakeTorque = bikeSetting.brakePower * brake;
  1475.             }
  1476.            
  1477.             if(shift && speed > 5 && shifmotor) {
  1478.                
  1479.                 if(transform.localEulerAngles.x > 320) {
  1480.                     bikeSetting.shiftCentre.y = bikeSetting.shiftSpeed.y;
  1481.                     bikeSetting.shiftCentre.z = bikeSetting.shiftSpeed.z;
  1482.                 } else {
  1483.                     //makes no sense unless there's some kinda getter/setter there.
  1484.                     //bikeSetting.shiftCentre = bikeSetting.shiftCentre;
  1485.                 }
  1486.                
  1487.                 if(powerShift == 0) { shifmotor = false; }
  1488.                
  1489.                 powerShift = Mathf.MoveTowards(powerShift,0.0f,0.3f);
  1490.                
  1491.                 bikeSounds.nitro.volume = Mathf.Lerp(bikeSounds.nitro.volume,1.0f,0.1f);
  1492.                
  1493.                 if(!bikeSounds.nitro.isPlaying && !AppSoundManager.MuteSfx) {
  1494.                     bikeSounds.nitro.GetComponent<AudioSource>().Play();
  1495.                
  1496.                 }
  1497.                
  1498.                 curTorque = powerShift > 0 ? getCurTorque(true) : getCurTorque();
  1499.                 bikeParticles.shiftParticle1.emissionRate = Mathf.Lerp(bikeParticles.shiftParticle1.emissionRate,powerShift > 0 ? 50 : 0,0.2f);
  1500.                 bikeParticles.shiftParticle2.emissionRate = Mathf.Lerp(bikeParticles.shiftParticle2.emissionRate,powerShift > 0 ? 50 : 0,0.2f);
  1501.            
  1502.             } else {
  1503.                
  1504.                 //bikeSetting.shiftCentre = bikeSetting.shiftCentre;
  1505.                
  1506.                 if(powerShift > 20){
  1507.                     shifmotor = true;
  1508.                 }
  1509.                
  1510.                 bikeSounds.nitro.volume = Mathf.MoveTowards(bikeSounds.nitro.volume,0.0f,0.02f);
  1511.                
  1512.                 if(bikeSounds.nitro.volume == 0){
  1513.                     bikeSounds.nitro.Stop();
  1514.                 }
  1515.                
  1516.                 powerShift = Mathf.MoveTowards(powerShift,100.0f,0.1f);
  1517.                 curTorque = getCurTorque();
  1518.                 bikeParticles.shiftParticle1.emissionRate = Mathf.Lerp(bikeParticles.shiftParticle1.emissionRate,0,0.2f);
  1519.                 bikeParticles.shiftParticle2.emissionRate = Mathf.Lerp(bikeParticles.shiftParticle2.emissionRate,0,0.2f);
  1520.            
  1521.             }
  1522.            
  1523.             w.rotation = Mathf.Repeat(w.rotation + delta * col.rpm * 360.0f / 60.0f,360.0f);
  1524.             w.wheel.localRotation = Quaternion.Euler(w.rotation,0.0f,0.0f);
  1525.            
  1526.             // let the wheels contact the ground, if no groundhit extend max suspension distance
  1527.             Vector3 lp = w.axle.localPosition;
  1528.            
  1529.             //getGroundHit?
  1530.             if(col.GetGroundHit(out hit)) {
  1531.                
  1532.                 bikeSounds.lowCrash.volume = Mathf.Lerp(bikeSounds.lowCrash.volume,0.0f,0.02f);
  1533.                
  1534.                 if(bikeParticles.brakeParticlePerfab) {
  1535.                    
  1536.                     if(Particle[currentWheel] == null) {
  1537.                    
  1538.                         Particle[currentWheel] = Instantiate(bikeParticles.brakeParticlePerfab,w.wheel.position,Quaternion.identity) as GameObject;
  1539.                         Particle[currentWheel].name = "WheelParticle";
  1540.                         Particle[currentWheel].transform.parent = transform;
  1541.                         Particle[currentWheel].AddComponent<AudioSource>();
  1542.                    
  1543.                     }
  1544.                    
  1545.                     var pc = Particle[currentWheel].GetComponent<ParticleSystem>();
  1546.                     bool WGrounded = false;
  1547.                    
  1548.                     if(WGrounded && speed > 10) {
  1549.                         pc.enableEmission = true;
  1550.                         Particle[currentWheel].GetComponent<AudioSource>().volume = 1.0f;
  1551.                     if(!Particle[currentWheel].GetComponent<AudioSource>().isPlaying && !AppSoundManager.MuteSfx)
  1552.                         Particle[currentWheel].GetComponent<AudioSource>().Play();
  1553.                     }else {                
  1554.                         pc.enableEmission = false;
  1555.                         Particle[currentWheel].GetComponent<AudioSource>().volume = Mathf.Lerp(Particle[currentWheel].GetComponent<AudioSource>().volume,0,Time.deltaTime * 10.0f);
  1556.                     }
  1557.                
  1558.                 }
  1559.                
  1560.                
  1561.                 lp.y -= Vector3.Dot(w.wheel.position - hit.point,transform.TransformDirection(0,1,0) / transform.lossyScale.x) - (col.radius);
  1562.                 lp.y = Mathf.Clamp(lp.y,-10.0f,w.pos_y);
  1563.                
  1564.                 floorContact = floorContact || (w.drive);
  1565.                
  1566.                 if(!crash) {
  1567.                     GetComponent<Rigidbody>().angularDrag = 20.0f;
  1568.                 } else {
  1569.                     GetComponent<Rigidbody>().angularDrag = 0.0f;
  1570.                 }
  1571.                 grounded = true;
  1572.            
  1573.             //col.GetGroundHit
  1574.             } else {
  1575.                 bikeSounds.lowCrash.volume = Mathf.Lerp(bikeSounds.lowCrash.volume,1.0f,0.01f);
  1576.                 //if(Particle[1] != null) {
  1577.                 if(Particle[currentWheel] != null) {
  1578.                     var pc = Particle[currentWheel].GetComponent<ParticleSystem>();
  1579.                     Particle[currentWheel].GetComponent<AudioSource>().volume = 0;
  1580.                     pc.enableEmission = false;
  1581.                 }
  1582.                 grounded = false;
  1583.                 GetComponent<Rigidbody>().AddForce(0,-500,0);
  1584.                 lp.y = w.startPos.y - bikeWheels.setting.Distance;
  1585.             }
  1586.  
  1587.             currentWheel++;
  1588.             w.axle.localPosition = lp;
  1589.  
  1590.         }//foreach Wheel
  1591.        
  1592.        
  1593.        
  1594.        
  1595.        
  1596.         // calculate the actual motor rpm from the wheels connected to the engine
  1597.         // note we haven't corrected for gear yet.
  1598.         if(motorizedWheels > 1) {
  1599.             rpm = rpm / motorizedWheels;
  1600.         }
  1601.  
  1602.         // we do some delay of the change (should take delta instead of just 95% of
  1603.         // previous rpm, and also adjust or gears.
  1604.         motorRPM = 0.95f * motorRPM + 0.05f * Mathf.Abs(rpm * bikeSetting.gears[currentGear]);
  1605.         if(motorRPM > 5500.0f)
  1606.             motorRPM = 5200.0f;
  1607.  
  1608.         // calculate the 'efficiency' (low or high rpm have lower efficiency then the
  1609.         // ideal efficiency, say 2000RPM, see table
  1610.         int index = (int)(motorRPM / efficiencyTableStep);
  1611.         if(index >= efficiencyTable.Length)
  1612.             index = efficiencyTable.Length - 1;
  1613.         if(index < 0)
  1614.             index = 0;
  1615.  
  1616.         // calculate torque using gears and efficiency table
  1617.         float newTorque = curTorque * bikeSetting.gears[currentGear] * efficiencyTable[index];
  1618.         //Debug.Log(" NT " + this + " " + newTorque);
  1619.        
  1620.         //Apply wheel steering
  1621.         int currentWheelID = 0;
  1622.             // go set torque to the wheels
  1623.         foreach(WheelComponent w in wheels) {
  1624.            
  1625.             WheelCollider col = w.collider;
  1626.            
  1627.             // of course, only the wheels connected to the engine can get engine torque
  1628.             if(w.drive) {
  1629.                 // only set torque if wheel goes slower than the expected speed
  1630.                 if(Mathf.Abs(col.rpm) > Mathf.Abs(wantedRPM) || (currentGear == 0 && speed > 10)) {
  1631.                     // wheel goes too fast, set torque to 0
  1632.                     col.motorTorque = 0;
  1633.                 }else {
  1634.                     //
  1635.                     float curTorqueCol = col.motorTorque;
  1636.                     col.motorTorque = curTorqueCol * 0.9f + newTorque * 0.1f;
  1637.                
  1638.                 }
  1639.             }
  1640.            
  1641.             currentPitch = getLocalDegrees( this.transform.rotation ).x;
  1642.             float clampedPitch = Mathf.Clamp01( Mathf.Clamp(Mathf.Abs(currentPitch),0,90) / 90 );
  1643.             float pitchTurnMultiplier = pitchTurnCurve.Evaluate( clampedPitch );
  1644.             //Debug.Log(" CLP   PTM " + clampedPitch + "  " + pitchTurnMultiplier);
  1645.            
  1646.             float clampedBank = Mathf.Clamp01( Mathf.Clamp(Mathf.Abs(surfaceAngle),0,90) / 90 );
  1647.             float bankedTurnMultiplier = bankTurnCurve.Evaluate( clampedBank );
  1648.             if ( myController._in_gravity ){
  1649.                 bankedTurnMultiplier = 1f;
  1650.             }
  1651.            
  1652.             float SteerAngle = Mathf.Clamp((speed / transform.lossyScale.x) / bikeSetting.maxSteerAngle,1.0f,bikeSetting.maxSteerAngle);
  1653.             if ( currentWheelID == 0 ){
  1654.                 col.steerAngle = steer * (w.maxSteer / SteerAngle) * pitchTurnMultiplier * bankedTurnMultiplier;
  1655.             }else{
  1656.                
  1657.                 //Apply a small amount of turn to the rear wheel to make this fucker turn properly.
  1658.                 float speedVal = getClampedSpeedPercent();
  1659.                 float rearTurnMultiplier = rearTurnCurve.Evaluate( speedVal );
  1660.                 //Debug.Log("RTM " + rearTurnMultiplier);
  1661.                 WheelHit g;
  1662.                 if ( col.GetGroundHit( out g ) ){
  1663.                    
  1664.                     //float relSlip = Mathf.InverseLerp(col.sidewaysFriction.extremumSlip, col.sidewaysFriction.asymptoteSlip, g.sidewaysSlip );
  1665.                     float absSlip = Mathf.Abs( g.sidewaysSlip );
  1666.                     float clampedSkid = Mathf.Clamp( absSlip, 0, skidMarkMax );
  1667.                     float clampedSkidPercent = clampedSkid / skidMarkMax;
  1668.                    
  1669.                     bool soundPlaying = false;
  1670.                     if ( clampedSkidPercent > skidMarkMin ){
  1671.                         //Vector3 flattenedSpeed = rigidbody.velocity;
  1672.                         Vector3 outPoint = ( g.point + Vector3.up * 0.1f ) + ( GetComponent<Rigidbody>().velocity * skidMarkOffset) ;
  1673.                         lastSkidIndex = m_skidmarks.AddSkidMark(  outPoint , g.normal, clampedSkidPercent, 0.24f, lastSkidIndex );
  1674.                         if ( clampedSkidPercent > skidSmokeMin ){
  1675.                             soundPlaying = true;
  1676.                             skidVolumeTarg = (clampedSkidPercent - skidSmokeMin) * (1/0.6f) * skidVolumeMultiplier;//hacky
  1677.                             m_skidSmoke.Emit( outPoint, Vector3.up * 0.01f, 1, 1, Color.white, Random.Range(0f,360f), Random.Range(-10f,10f) );
  1678.                         }
  1679.                     }
  1680.                     if ( !soundPlaying ){
  1681.                         skidVolumeTarg = 0f;
  1682.                     }
  1683.                    
  1684.                     skidVolume += ( skidVolumeTarg - skidVolume ) * Time.fixedDeltaTime * skidVolumeSpeed;
  1685.                    
  1686.                     if ( skidVolume > 0.05f ){
  1687.                         if ( !bikeSounds.skidding.isPlaying ){
  1688.                             bikeSounds.skidding.Play();
  1689.                         }
  1690.                         bikeSounds.skidding.volume = skidVolume;
  1691.                     }else {
  1692.                         if ( bikeSounds.skidding.isPlaying ){
  1693.                             bikeSounds.skidding.Stop();
  1694.                         }
  1695.                     }
  1696.                 }else{
  1697.                     lastSkidIndex = -1; //reset it
  1698.                 }
  1699.                
  1700.                 //
  1701.                 if ( !useRearSteer ) rearTurnMultiplier = 0f;
  1702.                 col.steerAngle = steer * (w.maxSteer / -SteerAngle * rearTurnMultiplier * pitchTurnMultiplier * bankedTurnMultiplier);
  1703.                
  1704.             }//frontwheel, backwheel
  1705.            
  1706.             currentWheelID++;
  1707.            
  1708.         }//foreach wheel (steering)
  1709.  
  1710.  
  1711.  
  1712.         // if we have an audiosource (motorsound) adjust pitch using rpm        
  1713.         if(audioSource != null) {
  1714.             // calculate pitch (keep it within reasonable bounds)
  1715.             float pitch = Mathf.Clamp(1.0f + ((motorRPM - bikeSetting.idleRPM) / (bikeSetting.shiftUpRPM - bikeSetting.idleRPM)),1.0f,10.0f);
  1716.             audioSource.pitch = pitch;
  1717.             audioSource.volume = Mathf.MoveTowards(audioSource.volume,1.0f,0.02f);
  1718.         }
  1719.  
  1720.  
  1721.         if(crash) {
  1722.             transform.GetComponent<Rigidbody>().centerOfMass = Vector3.zero;
  1723.         }
  1724.  
  1725.  
  1726.     }
  1727.    
  1728.  
  1729.  
  1730.  
  1731.  
  1732.  
  1733.  
  1734.     /////////////// Show Normal Gizmos ////////////////////////////
  1735.  
  1736.  
  1737.     void OnDrawGizmos() {
  1738.  
  1739.         if(!bikeSetting.showNormalGizmos || Application.isPlaying)
  1740.             return;
  1741.  
  1742.         Matrix4x4 rotationMatrix = Matrix4x4.TRS(transform.position,transform.rotation,transform.lossyScale);
  1743.  
  1744.         Gizmos.matrix = rotationMatrix;
  1745.         Gizmos.color = new Color(1,0,0,0.5f);
  1746.  
  1747.         Gizmos.DrawCube(Vector3.zero,new Vector3(0.5f,1.0f,2.5f));
  1748.         Gizmos.DrawSphere(bikeSetting.shiftCentre / transform.lossyScale.x,0.2f);
  1749.  
  1750.     }
  1751.  
  1752. #pragma warning restore 0414
  1753.  
  1754. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement