Legacy:Manual Shift Car - Unreal Wiki

# Legacy:Manual Shift Car

### From Unreal Wiki, The Unreal Engine Documentation Site

## Breakdown

This is a convertion of ONSWheeledCraft. This class allows for a stick shift or otherwise known as a manual transmission car. Why stick shift?

- It doesn't cost much in terms of fps
- Lets you rev it in neutral :)
- Fun to drive
- Will allow rear/front/all wheel drive
- Holding the brake and the rear wheels keep spinning (no rear brakes)

- I hope you can use what I (CIpen) have created

## The Goods

### To start the class off

    class WheeledClutch_Brake extends ONSVehicle;

### Critical variables

    var() float             WheelSoftness;
    var() float             WheelPenScale;
    var() float             WheelPenOffset;
    var() float             WheelRestitution;
    var() float             WheelAdhesion;
    var() float             WheelInertia;   // I believe this is how much to resist change in motion

        //I(CIpen) made these an array so we could have different slips on each surface
    var() array<float>         WheelLongSlip; // I think this is so we can have the wheels slip when you take off
    var() array<InterpCurve>   WheelLongFrictionFunc;//this is the curve for how slippery the wheels are allong the X axis(forwards slip)
    var() array<InterpCurve>   WheelLatSlipFunc;//this is the curve for how slippery the wheels are allong the Y axis(left/right slip)
    var() array<float>         WheelLongFrictionScale;  // quick way to change the ammount of friction without having to draw out
    var() array<float>         WheelLatFrictionScale;   // a new friction curve

    var() float                             WheelHandbrakeSlip;
    var() float                             WheelHandbrakeFriction;
    var() float                             WheelSuspensionTravel;
    var() float                             WheelSuspensionOffset;
    var() float                             WheelSuspensionMaxRenderTravel;
    var() float                             FTScale;
    var() float                             ChassisTorqueScale;
    var() float                             MinBrakeFriction;
    var() InterpCurve                       MaxSteerAngleCurve; // degrees, this makes the wheels turn like real car wheels*
    var() float                             GearRatios[8];  // 0 is reverse, 1-4 f
    var() int                               NumForwardGears;
    var() float                             TransRatio;   // Other(constant)gears  i.e. so we can have driveshaft ratio(if one applies)
    var() float                             ChangeUpPoint;   //EngineRPM that signals gear up change
    var() float                             ChangeDownPoint; //EngineRPM that signals gear down change

    var   int                               bGearUp;
    var   int                               bGearDown;

    var() float                             LSDFactor;
    var() float                             EngineBrakeFactor;
    var() float                             EngineBrakeRPMScale;
    var() float                             MaxBrakeTorque;
    var() float                             SteerSpeed;         // How fast it turns
    var() float                             TurnDamping;
    var() float                             StopThreshold;
    var() float                             HandbrakeThresh;
    var() float                             EngineInertia;     // Pre-gear box engine (piston mass)
    var   bool                              bClutching;        // If holding the clutch
    var   bool                              bOldClutching;
    var() float                             IdleRPM;              //RPM to idle at
    var() float                             EngineRPMSoundRange;   //RPM sound range
    var() name                              SteerBoneName;
    var() EAxis                             SteerBoneAxis;
    var() float                             SteerBoneMaxAngle; // degrees

    var   float                             OutputBrake;    //How much we are braking
    var   float                             OutputGas;      //How much are we giving gas
    var   float                             OutputPitch;
    var   bool                              OutputHandbrake;

    var   int                               Gear;           //What gear we are in
    var   float                             ForwardVel;
    var   bool                              bIsInverted;
    var   bool                              bIsDriving;
    var   float                             NumPoweredWheels;
    var   float                             NeutralRPM;

    var   InterpCurve                       RPMtoGas;           // This is what I use to convert the current in gear RPM to the amount of gas so the RPM doesn't go to zero when we hit the clutch
    var() InterpCurve                       TorqueCurve;        // Engine output torque
    var() InterpCurve                       EngineS;
    var() InterpCurve                       BrakeCurve;     //This simulates more lifelike braking, where you apply braking

    var   float                             Gas, Gas2, hBrake;

    var   float                             RPM2;    //Neutral RPM
    var   float                             RPM;     //Neutral RPM
    var   float                             NRPM;
    var   bool                              bBraking;   //if we are braking
    var   bool                              bThrot, bNThrot, bNoGas;
    var   bool                              bInGear;          //if we are completly in gear
    var   bool                              bRadians;
    var   float                             TotalSpinVel;
    var   float                             EngineRPM;
    var   float                             WheelRPM;// used to get the wheel RPM at anytime(so we don't change EngineRPM which is what the engine sound is tied to)
    var   float                             CarMPH;
    var   float                             ETorque;
    var   float                             ActualSteering;
    var   float                             SteerBoneAngle;
    var   float                             EnginePitch;
    var   Vector                            worldForward;
    var   Vector                            worldRight;
    var   Vector                            worldUp;
    var   Matrix                            carTM;
    var   Rotator                           SteerRot;
    var   bool                              bCurrentOnGround;
    var   float                             DeltaPitch;
    var   float                             DeltaHeading;
    var   float                             DeltaRoll;
    var   float                             VRate;

    var   vector                            one, two, three;
    var   vector                            Dist1, Dist2;     // unused but this was for doing the daredevil stuff
    var   float                             Dist3;
    var   bool                              bBrakeFrontWheelsOnly, bBrakeFrontWheels;
    var   Vector                            UDForce, UDTorque;
    var   Vector                            WForce, WTorque;
    var   Coords                            WheelCoords;
    var   Coords                            OldCoords;
    var   Coords                            Coords;
    var   Vector                            ForwardsInOldPlane;
    var   Pawn                              OldDriver;

    struct native SCarState
    {
              var vector                    ChassisPosition;
              var Quat                      ChassisQuaternion;
              var vector                    ChassisLinVel;
              var vector                    ChassisAngVel;

              var byte                      ServerHandbrake;
              var byte                      ServerBrake;
              var byte                      ServerGas;
              var byte                      ServerGear;
              var byte                      ServerSteering;
              var int                       ServerViewPitch;
              var int                       ServerViewYaw;
    };

    var   byte                     FudgeByte;
    var   SCarState                CarState;
    var   SCarState                OldCarState;
    var   KRigidBodyState          ChassisState;
    var   bool                     bNewCarState;
    var   bool                     bOldVehicleOnGround;
    var   float                    TheDeltaTime;

### Not so critical variables

    var   class<CameraEffect>           myBlur;
    var   myMotionBlur                  ClientMotionBlur;

    var   array<ONSDirtSlipEffect>          Dust; // FL, FR, RL, RR
    var() float                             DustSlipRate;
    var() float                             DustSlipThresh;
    var() float                         RevMeterPosX;
    var() float                         RevMeterPosY;
    var() float                         RevMeterScale;
    var() float                         RevMeterSizeY;

    var() bool                          bDoStuntInfo;
    var() bool                          bAllowAirControl;
    var() bool                          bAllowChargingJump;
    var   bool                            bAllowBigWheels;

    var   bool                            bPushDown; //jump is being charged
    var   bool                          bOldPushDown;
    var   bool                          bAllWheelsOnGround;

    var() float                         MaxJumpForce;
    var   float                         JumpForce;
    var() float                         MaxJumpSpin;
    var   float                         JumpSpin;

    var() float                         JumpChargeTime;
    var   float                           DesiredJumpForce; //used by AI
    var   string                  JumpFeedbackForce;

    var   sound                             JumpSound;

    var() float                         JumpMeterOriginX;
    var() float                         JumpMeterOriginY;
    var() float                         JumpMeterWidth;
    var() float                         JumpMeterHeight;
    var() float                         JumpMeterSpacing;
    var() color                         JumpMeterColor;
    var() color                         SpinMeterColor;
    var   Texture                 JumpMeterTexture;

    var() float                         AirTurnTorque;
    var() float                         AirPitchTorque;
    var() float                         AirPitchDamping;
    var() float                         AirRollTorque;
    var() float                         AirRollDamping;
    var() float                         MinAirControlDamping;
    var   float                           FenderBenderSpeed;

    var() bool                          bMakeBrakeLights;
    var() vector                               BrakeLightOffset[2];
    var   ONSBrakelightCorona              BrakeLight[2];
    var() Material                           BrakeLightMaterial;

    var   Rotator                 OldRotation;
    var   Vector                  LastOnGroundLocation;
    var   float                           LastOnGroundTime;

    var   float                           InAirSpin; // Degrees
    var   float                           InAirPitch; // Degrees
    var   float                           InAirRoll; // Degrees
    var   float                           InAirTime; // Second
    var   float                           InAirDistance; // Meters
    var   int                             DaredevilPoints;
    var   config int                    IntSteerBoneAngle;

    var() float                         DaredevilThreshInAirSpin;
    var() float                         DaredevilThreshInAirPitch;
    var() float                         DaredevilThreshInAirRoll;
    var() float                         DaredevilThreshInAirTime;
    var() float                         DaredevilThreshInAirDistance;
    var() class<LocalMessage>              DaredevilMessageClass;

#### Replication =

    replication
    {
            reliable if (Role == ROLE_Authority)
                    CarState, FudgeByte;                       // make sure we get what we need to the client
            reliable if (bNetInitial && Role == ROLE_Authority)
                    bAllowAirControl, bAllowChargingJump;        // showoff stuff
            reliable if (bNetInitial && bDoStuntInfo && Role == ROLE_Authority)
                    DaredevilThreshInAirDistance, DaredevilThreshInAirTime, DaredevilThreshInAirSpin, DaredevilThreshInAirPitch, DaredevilThreshInAirRoll;
    }

### Functions

PostNetBeginPlay

    simulated function PostNetBeginPlay()
    {
            local int i;
            NumPoweredWheels = 0.0;
            for(i=0; i<=1; i++)
            {
                    NumPoweredWheels += 1.0;
            }
           SVehicleUpdateParams();
           Super.PostNetBeginPlay();
    }

PostNetReceive
  Here is were we deal with stuff coming from the server.

    simulated function PostNetReceive()
    {
            Super.PostNetReceive();

            if(OldCarState.ChassisPosition == CarState.ChassisPosition &&
                    OldCarState.ChassisQuaternion.X == CarState.ChassisQuaternion.X &&
                    OldCarState.ChassisQuaternion.Y == CarState.ChassisQuaternion.Y &&
                    OldCarState.ChassisQuaternion.Z == CarState.ChassisQuaternion.Z &&
                    OldCarState.ChassisQuaternion.W == CarState.ChassisQuaternion.W &&
                    OldCarState.ChassisLinVel == CarState.ChassisLinVel &&
                    OldCarState.ChassisAngVel == CarState.ChassisAngVel &&
                    OldCarState.ServerHandbrake == CarState.ServerHandbrake &&
                    OldCarState.ServerBrake == CarState.ServerBrake &&
                    OldCarState.ServerGas == CarState.ServerGas &&
                    OldCarState.ServerGear == CarState.ServerGear &&
                    OldCarState.ServerSteering == CarState.ServerSteering &&
                    OldCarState.ServerViewPitch == CarState.ServerViewPitch &&
                    OldCarState.ServerViewYaw == CarState.ServerViewYaw)
                    return;

            ChassisState.Position.X = CarState.ChassisPosition.X;
            ChassisState.Position.Y = CarState.ChassisPosition.Y;
            ChassisState.Position.Z = CarState.ChassisPosition.Z;

            ChassisState.Quaternion = CarState.ChassisQuaternion;

            ChassisState.LinVel.X = 0.1f * CarState.ChassisLinVel.X;
            ChassisState.LinVel.Y = 0.1f * CarState.ChassisLinVel.Y;
            ChassisState.LinVel.Z = 0.1f * CarState.ChassisLinVel.Z;

            ChassisState.AngVel.X = 0.001f * CarState.ChassisAngVel.X;
            ChassisState.AngVel.Y = 0.001f * CarState.ChassisAngVel.Y;
            ChassisState.AngVel.Z = 0.001f * CarState.ChassisAngVel.Z;

            bNewCarState = true;
            OldCarState.ChassisPosition = CarState.ChassisPosition;
            OldCarState.ChassisQuaternion = CarState.ChassisQuaternion;
            OldCarState.ChassisLinVel = CarState.ChassisLinVel;
            OldCarState.ChassisAngVel = CarState.ChassisAngVel;
            OldCarState.ServerHandbrake = CarState.ServerHandbrake;
            OldCarState.ServerBrake = CarState.ServerBrake;
            OldCarState.ServerGas = CarState.ServerGas;
            OldCarState.ServerGear = CarState.ServerGear;
            OldCarState.ServerSteering = CarState.ServerSteering;
            OldCarState.ServerViewPitch = CarState.ServerViewPitch;
            OldCarState.ServerViewYaw = CarState.ServerViewYaw;

            OutputPitch = RangeByteToFloat(CarState.ServerHandbrake);
            OutputHandbrake = (OutputPitch > 0.01);
            OutputBrake = RangeByteToFloat(CarState.ServerBrake);
            OutputGas = RangeByteToFloat(CarState.ServerGas);
            Gear = CarState.ServerGear;
            Steering = RangeByteToFloat(CarState.ServerSteering);
            DriverViewPitch = CarState.ServerViewPitch;
            DriverViewYaw = CarState.ServerViewYaw;
    }

PrecacheAnnouncer

    simulated function PrecacheAnnouncer(AnnouncerVoice V, bool bRewardSounds)
    {

            if (bRewardSounds && !bSoundsPrecached)
                    V.PrecacheSound('fender_bender');

            Super.PrecacheAnnouncer(V, bRewardSounds);
    }

KUpdateState
  This function tells when we should update.

    event bool KUpdateState(out KRigidBodyState newState)
    {
            if(Role == ROLE_Authority || !bNewCarState)
                    return false;

            newState = ChassisState;
            bNewCarState = false;
            return true;
    }

SVehicleUpdateParams

    event SVehicleUpdateParams()
    {
            local int i;

            Super.SVehicleUpdateParams();

            for(i=0; i<Wheels.Length; i++)
            {
                    Wheels[i].Softness = WheelSoftness;
                    Wheels[i].PenScale = WheelPenScale;
                    Wheels[i].PenOffset = WheelPenOffset;
                    Wheels[i].LongSlip = WheelLongSlip[0];
                    Wheels[i].LatSlipFunc = WheelLatSlipFunc[0];
                    Wheels[i].Restitution = WheelRestitution;
                    Wheels[i].Adhesion = WheelAdhesion;
                    Wheels[i].WheelInertia = WheelInertia;
                    Wheels[i].LongFrictionFunc = WheelLongFrictionFunc[0];
                    Wheels[i].HandbrakeFrictionFactor = WheelHandbrakeFriction;
                    Wheels[i].HandbrakeSlipFactor = WheelHandbrakeSlip;
                    Wheels[i].SuspensionTravel = WheelSuspensionTravel;
                    Wheels[i].SuspensionOffset = WheelSuspensionOffset;
                    Wheels[i].SuspensionMaxRenderTravel = WheelSuspensionMaxRenderTravel;
            }
            if(Level.NetMode != NM_DedicatedServer && bMakeBrakeLights)
            {
                    for(i=0; i<2; i++)
                    {
                         if (BrakeLight[i] != None)
                         {
                                    BrakeLight[i].SetBase(None);
                                    BrakeLight[i].SetLocation( Location + (BrakelightOffset[i] >> Rotation) );
                                    BrakeLight[i].SetBase(self);
                                    BrakeLight[i].SetRelativeRotation( rot(0,32768,0) );
                                    BrakeLight[i].Skins[0] = BrakeLightMaterial;
                          }
                    }
            }
    }

UpdateVehicle
  This is the function were everything happens

              Also see Manual Shift Car/C++ Version

    function UpdateVehicle(float DeltaTime)
    {
            local Matrix carTM;
            local Vector worldUp, worldRight, worldForward;

            local KarmaParams KP;
            local KRigidBodyState rbState;
            local int   i;
            local float maxSteerAngle, maxSteer, deltaSteer;
            local float DriveTorque, GripTorque, EngineTorque, EngineBraking, EngineWheelRatio;
            local float BrakeTorque;
            local float NewTotalSpinVel, LSDSplit, EvenSplit, UseSplit;
            local float TransInertia;
            local float LimitBrakeTorque;
            local float WheelTorque, VehicleForce, TransAcc;
            local float TurnAngVel, DampingScale, TurnDampingMag;
            local float PitchAngVel, RollAngVel, PitchDampingMag, RollDampingMag;

            local Vector AirControlTorque, AngVel;
            local float  VRate;
            local Vector Force, Torque;
            local int z;

            WForce = vect(0, 0, 0);
            WTorque = vect(0, 0, 0);

            if(!KIsAwake())
                    return;
            KP = KarmaParams(KParams);
            if(KP == None)
                    return;

            if(Controller != None)
            {
                  //Calc up (z), right(y) and forward (x) vectors
                  GetAxes(Rotation, worldRight, worldForward, worldUp);

                  /////////// STEERING ///////////
                  maxSteerAngle = InterpCurveEval(MaxSteerAngleCurve, VRate);
                  if(maxSteerAngle==0)
                        maxSteer = DeltaTime * SteerSpeed * maxSteerAngle;
                  else
                        maxSteer = DeltaTime * SteerSpeed;

                  deltaSteer = (-Steering * maxSteerAngle) - ActualSteering; // Amount we want to move (target - current)
                  deltaSteer = Clamp(deltaSteer, -maxSteer, maxSteer);
                  ActualSteering += deltaSteer;

                  if(bAuto==True)
                  {
                          EngineTorque = OutputGas * InterpCurveEval(TorqueCurve, EngineRPM);
                          EngineBraking = (1.0f - OutputGas) * (EngineBrakeRPMScale*EngineRPM * EngineBrakeRPMScale*EngineRPM * EngineBrakeFactor);
                          EngineTorque -= EngineBraking;
                          EngineWheelRatio = GearRatios[Gear] * TransRatio;
                          NewTotalSpinVel=0.0;
                          EngineRPM = 0.0;
                          for(i=0; i<Wheels.length; i++)
                          {
                                  EvenSplit = 1/NumPoweredWheels;
                                  if(TotalSpinVel > 0.1)
                                          LSDSplit = (TotalSpinVel - Wheels[i].SpinVel)/((NumPoweredWheels-1) * TotalSpinVel);
                                  else
                                          LSDSplit = EvenSplit;
                                  UseSplit = ((1-LSDFactor) * EvenSplit) + (LSDFactor * LSDSplit);
                                  DriveTorque = UseSplit * (EngineTorque / EngineWheelRatio);
                                  GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));
                                  if(Wheels[i].SlipVel < 0.0)
                                          GripTorque *= -1.0;
                                  TransInertia = (EngineInertia / Abs(GearRatios[Gear] * TransRatio)) + Wheels[i].WheelInertia;
                                  if(Wheels[i].SpinVel > 0.0)
                                          BrakeTorque = -OutputBrake * MaxBrakeTorque;
                                  else
                                          BrakeTorque = OutputBrake * MaxBrakeTorque;

                                  LimitBrakeTorque = ( Abs(Wheels[i].SpinVel) * TransInertia ) / TheDeltaTime; // Size of torque needed to completely stop wheel spinning.
                                  BrakeTorque = Clamp(BrakeTorque, -LimitBrakeTorque, LimitBrakeTorque); // Never apply more than this!
                                  WheelTorque = DriveTorque + BrakeTorque - GripTorque;
                                  VehicleForce = GripTorque / (FTScale * Wheels[i].WheelRadius);
                                  if( OutputBrake > 0.0 ||  (DriveTorque + BrakeTorque) * Wheels[i].SpinVel < 0.0)
                                  {
                                          Wheels[i].DriveForce = 0.0;
                                          Wheels[i].LongFriction = Abs(VehicleForce) + (OutputBrake * MinBrakeFriction);
                                  }
                                  else
                                  {
                                          Wheels[i].DriveForce = VehicleForce;
                                          Wheels[i].LongFriction = 0.0;
                                  }
                                  if (Wheels[i].bWheelOnGround)
                                          Wheels[i].ChassisTorque = -1.0 * (DriveTorque + BrakeTorque) * ChassisTorqueScale;
                                   else
                                          Wheels[i].ChassisTorque = 0.0;
                                  TransAcc = WheelTorque / TransInertia;
                                  Wheels[i].SpinVel += TransAcc * DeltaTime;
                                  if(Gear == 0 && Wheels[i].SpinVel > 0.0)
                                          Wheels[i].SpinVel = 0.0;
                                  else if(Gear > 0 && Wheels[i].SpinVel < 0.0)
                                          Wheels[i].SpinVel = 0.0;
                                  NewTotalSpinVel += Wheels[i].SpinVel;
                                  EngineRPM += Wheels[i].SpinVel / EngineWheelRatio;
                                  Wheels[i].LatFriction = WheelLatFrictionScale[0] * Wheels[i].TireLoad;
                                  Wheels[i].LatSlip = InterpCurveEval(Wheels[i].LatSlipFunc, Wheels[i].SlipAngle);
                                  if(OutputHandbrake && Wheels[i].bHandbrakeWheel)
                                  {
                                          Wheels[i].LatFriction *= Wheels[i].HandbrakeFrictionFactor;
                                          Wheels[i].LatSlip *= Wheels[i].HandbrakeSlipFactor;
                                  }
                                  if(Wheels[i].SteerType == VST_Steered)
                                          Wheels[i].Steer = ActualSteering;
                                  else if(Wheels[i].SteerType == VST_Inverted)
                                          Wheels[i].Steer = -ActualSteering;
                                  else
                                          Wheels[i].Steer = 0.0;
                          }
                          EngineRPM /= NumPoweredWheels;
                          EngineRPM = Max(EngineRPM, 0.01); // ensure always positive!
                          TotalSpinVel = NewTotalSpinVel;
                  }
    ///////////////STICK SHIFT///////////////////////////////
                  else
                  {
                       if(bClutching)
                       {
                            EngineTorque = InterpCurveEval(TorqueCurve, EngineRPM);
                       }
                       else
                       {
                            EngineTorque = InterpCurveEval(TorqueCurve, EngineRPM);
                            if(EngineTorque >= 0)
                            {
                                       // Calculate torque at output of engine. Combination of throttle, current RPM and engine braking.
                                    EngineTorque = OutputGas * InterpCurveEval(TorqueCurve, EngineRPM);
                            }
                            else if(EngineTorque < 0)
                            {
                                    EngineTorque = InterpCurveEval(TorqueCurve, EngineRPM);
                            }

                            EngineBraking = (1.0f - OutputGas) * (EngineBrakeRPMScale*EngineRPM * EngineBrakeRPMScale*EngineRPM * EngineBrakeFactor);
                       }

                      // EngineRPM = OutputGas * InterpCurveEval(RPMCurve, EngineTorque);

                       EngineTorque -= EngineBraking;
                       ETorque = EngineTorque;
                       //DebugInfo = FString::Printf(TEXT("OutputBrake: %f      EngineRPM: %f    EngineTorque: %f"), OutputBrake, EngineRPM, EngineTorque);

                       // Total gear ratio between engine and differential (ie before being split between wheels).
                       // A higher gear ratio and the torque at the wheels is reduced.
                       EngineWheelRatio = GearRatios[Gear] * TransRatio;

                       if(bClutching)
                       {
                            //EngineRPM = 0.0;
                            WheelRPM = 0.0;
                       }
                       else
                       {
                            if(Wheels[i].bPoweredWheel)
                            {
                                 // Reset engine RPM. We calculate this by adding the component of each wheel spinning.
                                 NewTotalSpinVel=0.0;
                                 if(bInGear)
                                 {
                                       WheelRPM = 0.0;
                                       EngineRPM = 0.0;
                                 }
                                 else if(!bInGear)
                                       WheelRPM = 0.0;
                            }
                       }

                  // Do model for each wheel.
                  //  Okay this needs to be fixed because the way this is, all the wheels get power

                       for(i=0;i<Wheels.length; i++)
                       {
                          //Wheels[i];
                          /////////// DRIVE ///////////
                          // Heuristic to divide torque up so that the wheels that are spinning slower get more of it.
                          // Sum of LSDFactor across all wheels should be 1.
                          // JTODO: Do we need to handle the case of vehicles with different size wheels?

                          EvenSplit = 1/NumPoweredWheels;

                          // If no wheels are spinning, just do an even split.
                          if(TotalSpinVel > 0.1)
                                  LSDSplit = (TotalSpinVel - Wheels[i].SpinVel)/((NumPoweredWheels-1) * TotalSpinVel);
                          else
                                  LSDSplit = EvenSplit;

                          UseSplit = ((1-LSDFactor) * EvenSplit) + (LSDFactor * LSDSplit);

                          if(bClutching)
                          {
                               EngineRPM = RPM;

                               //DriveTorque = UseSplit * (EngineTorque / EngineWheelRatio);
                          }
                          else
                          {
                               if(Wheels[i].bPoweredWheel)
                               {
                                    //EngineRPM = RPM;

                                    // Calculate Drive Torque : applied at wheels (ie after gearbox and differential)
                                    // This is an 'open differential' ie. equal torque to each wheel
                                    DriveTorque = UseSplit * (EngineTorque / EngineWheelRatio);
                               }
                          }

                          /////////// LONGITUDINAL ///////////
                          // Calculate Grip Torque : longitudinal force against ground * distance of action (radius of tyre)
                          // LongFrictionFunc is assumed to be reflected for negative Slip Ratio
                          GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));

                          if(Wheels[i].SlipVel < 0.0)
                                  GripTorque *= -1.0;

                          if(Wheels[i].bPoweredWheel)
                          {
                               // GripTorque can't be more than the torque needed to invert slip ratio.
                               TransInertia = (EngineInertia / Abs(GearRatios[Gear] * TransRatio)) + Wheels[i].WheelInertia;
                          }

                          //FLOAT SlipAngVel = Wheels[i].SlipVel/Wheels[i].WheelRadius;

                          if(bBraking)
                          {
                               // Brake torque acts to stop wheels (ie against direction of motion)
                               BrakeTorque = 0.0;
                               if(Wheels[i].SpinVel > 0.0)
                                    BrakeTorque = -OutputBrake * MaxBrakeTorque;
                              else
                                    BrakeTorque = OutputBrake * MaxBrakeTorque;

                               LimitBrakeTorque = ( Abs(Wheels[i].SpinVel) * TransInertia ) / TheDeltaTime; // Size of torque needed to completely stop wheel spinning.
                               BrakeTorque = Clamp(BrakeTorque, -LimitBrakeTorque, LimitBrakeTorque); // Never apply more than this!
                               mBrakeTorque = BrakeTorque;
                          }
                          else
                          {
                               BrakeTorque = 0;
                               mBrakeTorque = BrakeTorque;
                          }
    //_____________________________________________

                          if(bClutching)
                          {
                                  WheelTorque = GripTorque;
                                  //Log("GripTorque");
                                  //Log(GripTorque);
                          }
                          else     // we are in full gear
                          {
                               if(!Wheels[i].bPoweredWheel)
                               {
                                  //Don't do this if 4 wheel drive!!!!
                                  WheelTorque = GripTorque * 2;
                               }
                               else
                               {
                                  // Resultant torque at wheel : torque applied from engine + brakes + equal-and-opposite from tire-road interaction.
                                  WheelTorque = DriveTorque + BrakeTorque - GripTorque;
                              /*    Log("WheelTorque");
                                  Log(WheelTorque);
                                  Log("GripTorque");
                                  Log(GripTorque);      */
                               }
                          }
    //____________________________________________

                          // Resultant linear force applied to car. (GripTorque applied at road)
                          VehicleForce = GripTorque / (FTScale * Wheels[i].WheelRadius);

                          // If the wheel torque is opposing the direction of spin (ie braking) we use friction to apply it.
                          if( OutputBrake > 0 && bBraking)//  || (DriveTorque + BrakeTorque) * Wheels[i].SpinVel < 0.0
                          {
                                  Wheels[i].DriveForce = 0.0;
                                  Wheels[i].LongFriction = Abs(VehicleForce) + (OutputBrake * MinBrakeFriction);
                          }
                          else if(bClutching)
                          {
                                  Wheels[i].DriveForce = 0.0;
                                  Wheels[i].LongFriction = 0.0;
                          }
                          else
                          {
                                  if(!Wheels[i].bPoweredWheel)
                                  {
                                         Wheels[i].DriveForce = 0.0;
                                         Wheels[i].LongFriction = 0.0;
                                  }
                                  else
                                  {
                                       Wheels[i].DriveForce = VehicleForce;
                                       Wheels[i].LongFriction = 0.0;
                                       //Wheels[i].LongFriction = 0.0;
                                  }
                          }

                          if(Wheels[i].bPoweredWheel)
                          {
                               // Calculate torque applied back to chassis if wheel is on the ground
                               if (Wheels[i].bWheelOnGround)
                                  Wheels[i].ChassisTorque = -1.0 * (DriveTorque + BrakeTorque) * ChassisTorqueScale;
                               else
                                  Wheels[i].ChassisTorque = 0.0;
                          }

                          // Calculate new wheel speed.
                          // The lower the gear ratio, the harder it is to accelerate the engine.

                          if(bClutching)
                          {
                                 TransAcc = WheelTorque / -1;
                                 Wheels[i].SpinVel += TransAcc * DeltaTime;
                          }
                          else
                          {
                                 if(!Wheels[i].bPoweredWheel)
                                 {
                                       TransAcc = WheelTorque / -1;
                                       Wheels[i].SpinVel += TransAcc * DeltaTime;
                                 }
                                 else
                                 {
                                       TransAcc = WheelTorque / TransInertia;
                                       Wheels[i].SpinVel += TransAcc * DeltaTime;
                                 }
                          }

    //  -----this is were the engine needs to be slowly put into gear-----

                          if(bClutching)
                          {

                              WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
                              //TorqueConverter();
                          }
                          else   // we are in full gear
                          {
                              if(!Wheels[i].bPoweredWheel)
                              {
                                   WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
                              }
                              else
                              {
                                   // Accumulate wheel spin speeds to find engine RPM.
                                   // The lower the gear ratio, the faster the engine spins for a given wheel speed.
                                   NewTotalSpinVel += Wheels[i].SpinVel;
                                   if(!bInGear)
                                   {
    //                                  EngineRPM += (Wheels[i].SpinVel / EngineWheelRatio);
                                        WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
                                        TorqueConverter();

                                   }
                                   if(bInGear)
                                   {
                                        WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
                                        EngineRPM += (Wheels[i].SpinVel / EngineWheelRatio);
                                   }
                              }

                          }
    //  ---------------------------------------------------------------

                          // Make sure the wheel can't spin in the wrong direction for the current gear.
                          if(bClutching)
                          {
                          }
                          else
                          {
                               if(Wheels[i].bPoweredWheel)
                               {
                                      if(Gear == 0 && Wheels[i].SpinVel > 0.0)
                                           Wheels[i].SpinVel = 0.0;
                                      else if(Gear > 0 && Wheels[i].SpinVel < 0.0)
                                           Wheels[i].SpinVel = 0.0;
                               }
                          }

                          /////////// LATERAL ///////////
                          if(!Wheels[i].bPoweredWheel)
                          {
                               Wheels[i].LatFriction = (WheelLatFrictionScale[0] * 1.5) * Wheels[i].TireLoad ;
                               Wheels[i].LatSlip = InterpCurveEval(Wheels[i].LatSlipFunc, Wheels[i].SlipAngle);
                          }
                          else
                          {
                               Wheels[i].LatFriction = WheelLatFrictionScale[0] * Wheels[i].TireLoad;
                               Wheels[i].LatSlip = InterpCurveEval(Wheels[i].LatSlipFunc, Wheels[i].SlipAngle);
                          }

                          if(OutputHandbrake && Wheels[i].bHandbrakeWheel)
                          {
                                  Wheels[i].LatFriction *= Wheels[i].HandbrakeFrictionFactor;
                                  Wheels[i].LatSlip *= Wheels[i].HandbrakeSlipFactor;
                          }

                          /////////// STEERING  ///////////

                          // Pass on steering to wheels that want it.
                          if(Wheels[i].SteerType == VST_Steered)
                                  Wheels[i].Steer = ActualSteering;
                          else if(Wheels[i].SteerType == VST_Inverted)
                                  Wheels[i].Steer = -ActualSteering;
                          else
                                  Wheels[i].Steer = 0.0;
                     }
                  }
                  if(bClutching)
                  {
                  }
                  else
                  {
                       // EngineRPM is in radians per second, want in revolutions per minute
                       EngineRPM /= 4;
    //               EngineRPM /= 2.0 * PI; // revs per sec
    //               EngineRPM *= 60;
                       EngineRPM = Max(EngineRPM, 0.00); // ensure always positive!

                       RPM /= NumPoweredWheels;
    //               RPM /= 2.0 * PI; // revs per sec
    //               RPM *= 60;
                       RPM = Max(EngineRPM, 0.00); // ensure always positive!
                  }

                  if(!bClutching)
                  {
                       // Update total wheel spin vel
                       TotalSpinVel = NewTotalSpinVel;
                  }

                  // Turn (yaw) damping.
                  carTM = CachedLocalToWorld;  // was  carTM = LocalToWorld()
                  //worldUp(carTM.M[2][0], carTM.M[2][1], carTM.M[2][2]);
                  //worldRight(carTM.M[1][0], carTM.M[1][1], carTM.M[1][2]);
                  //worldForward(carTM.M[0][0], carTM.M[0][1], carTM.M[0][2]);

                //  KGetRigidBodyState(rbState);
                //  AngVel = KRBVecToVector(rbState.AngVel);
                  AngVel.X = rbState.AngVel.X;
                  AngVel.Y = rbState.AngVel.Y;
                  AngVel.Z = rbState.AngVel.Z;
                  TurnAngVel = AngVel dot worldUp;

                  DampingScale = 1.0 - MinAirControlDamping;
                  if(bAllowAirControl && !bVehicleOnGround)
                  {
              //            Log("bVehicleOnGround");
              //            Log(bVehicleOnGround);
                          TurnDampingMag = (1.0 - DampingScale*Abs(Steering)) * TurnDamping * TurnAngVel;
                          //KAddImpulse( WForce, -TurnDampingMag * worldUp );
                          Force += (WForce + (-1 * TurnDampingMag) * worldUp);
                  }

                  else
                  {
                          TurnDampingMag = (1.0 - Abs(ActualSteering)) * TurnDamping * TurnAngVel;
                          //KAddImpulse( WForce, -TurnDampingMag * worldUp );
                          Force += (WForce + (-1 + TurnDampingMag) * worldUp);
                  }

                  // If vehicle is in the air and we are allowing air control...
                  if(!bVehicleOnGround)
                  {
                          PitchAngVel = AngVel dot worldRight;
                          RollAngVel = AngVel dot worldForward;

                          if(bAllowAirControl)
                          {
                                  AirControlTorque = worldRight * OutputPitch * -AirPitchTorque;

                                  if(bIsWalking)
                                  {
            //                          Log("bIsWalking");
            //                          Log(bIsWalking);
                                          AirControlTorque += (worldForward * Steering * -AirRollTorque);
                                          }
                                  else
                                  {
                                          AirControlTorque += (worldUp * Steering * -AirTurnTorque);
                                  }
                                  //KAddImpulse( WForce, AirControlTorque );
                                  Force += AirControlTorque;

                                  // Damping forces
                                  PitchDampingMag = (1.0 - DampingScale*Abs(OutputPitch)) * AirPitchDamping * PitchAngVel;
                                  RollDampingMag = (1.0 - DampingScale*Abs(Steering)) * AirRollDamping * RollAngVel;

                                  //KAddImpulse( WForce, (-PitchDampingMag * worldRight) + (-RollDampingMag * worldForward) );
                                  Force += (WForce + ((-1 + PitchDampingMag) * worldRight) + ((-1 + RollDampingMag) * worldForward));

                                  UDForce = Force;
                                  UDTorque = Torque;
                          }
                          else
                          {
                             PitchDampingMag = AirPitchDamping * PitchAngVel;
                             //KAddImpulse( WForce, -PitchDampingMag * worldRight );
                             Force += (WForce + (-1 + PitchDampingMag) * worldRight);
                          }
                  }
                  else
                  {
                       UDForce = Force;
                       UDTorque = Torque;
                  }
            }
    }

Engine

    function Engine()
    {
         Gas = Gas2;
         RPM = InterpCurveEval(EngineS, Gas);
    }

StoptheCar

- This function allows for slower, more real life like breaking

    function StoptheCar()
    {
       if(bBraking)
       {
          if(hBrake<=21 && hBrake>=0)
          {
               hBrake += 0.5;
          }
          if(hBrake>21)
                 hBrake = 21;
       }
       if(!bBraking)
       {
               if(hBrake<=21 && hBrake>=0)
               {
                    hBrake -= 0.5;
               }
               if(hBrake<0)
                    hBrake = 0;
       }
    }

TorqueConverter

- This function should prolly have a different name, I named it this thinking I would need to accually have a torque converter for the tranny but I turned out making it a function that eases into gear and fixes the rpm to equal that of the engine rpm when the clutch is pressed.

    function TorqueConverter()
    {
        local  float    rpmDifference;
        local  float    myRPM;
        rpmDifference = 0.0;
        myRPM = WheelRPM;
        myRPM *= 6.28; // convert to radians per sec
        myRPM /= 60;
             if(EngineRPM == myRPM)
                  bInGear = True;
             else if(EngineRPM > myRPM)
             {
                  if(myRPM < 0)
                        rpmDifference = EngineRPM - (2 * Abs(myRPM));

                  else if(myRPM >= 0)
                        rpmDifference = EngineRPM - myRPM;
                  if(rpmDifference <= 200.0)
                  {
                       bInGear = True;
                  }
                  else if(rpmDifference > 200.0)
                  {
                       bInGear = False;
                       rpmDifference = rpmDifference * 0.2;
                       EngineRPM -= rpmDifference;
                       bRadians = True;
                  }
             }
             else if(EngineRPM < myRPM)
             {
                  rpmDifference = myRPM - EngineRPM;
                  if(rpmDifference <= 200.0)
                  {
                       bInGear = True;
                  }
                  else if(rpmDifference > 200.0)
                  {
                       bInGear = False;
                       rpmDifference = rpmDifference * 0.2;
                       EngineRPM -= rpmDifference;
                       bRadians = True;
                  }
             }
    }

KApplyForce

    function KApplyForce(out vector Force, out vector Torque)
    {
        Super.KApplyForce(Force, Torque);
        Force += UDForce;
        Torque += UDTorque;
        if (bBoosting == true && bVehicleOnGround == true)
            Force += vector(Rotation) * SpeedBoost;
    }

KImpact

    event KImpact(Actor Other, vector Pos, vector ImpactVel, vector ImpactNorm)
    {
        if (Role == ROLE_Authority)
        {

            ImpactInfo.Other = Other;
            ImpactInfo.Pos = Pos;
            ImpactInfo.ImpactVel = ImpactVel;
            ImpactInfo.ImpactNorm = ImpactNorm;
            ImpactInfo.ImpactAccel = KParams.KAcceleration;
            ImpactTicksLeft = ImpactDamageTicks;
        }
    }

DrivingStatusChanged

    event DrivingStatusChanged()
    {
            local int i;
            Super.DrivingStatusChanged();
        if (bDriving && Level.NetMode != NM_DedicatedServer && !bDropDetail)
            {
            Dust.length = Wheels.length;
            for(i=0; i<Wheels.Length; i++)
                if (Dust[i] == None)
                {
                            // Create wheel dust emitters.
                            WheelCoords = GetBoneCoords(Wheels[i].BoneName);
                            Dust[i] = spawn(class'ONSDirtSlipEffect', self,, WheelCoords.Origin + ((vect(0,0,-1) * Wheels[i].WheelRadius) >> Rotation));
                            Dust[i].SetBase(self);
                                Dust[i].SetDirtColor( Level.DustColor );
                    }
                    if(bMakeBrakeLights)
                    {
                            for(i=0; i<2; i++)
                            if (BrakeLight[i] == None)
                            {
                                    BrakeLight[i] = spawn(class'ONSBrakelightCorona', self,, Location + (BrakeLightOffset[i] >> Rotation) );
                                    BrakeLight[i].SetBase(self);
                                    BrakeLight[i].SetRelativeRotation( rot(0,32768,0) ); // Point lights backwards.
                                    BrakeLight[i].Skins[0] = BrakeLightMaterial;
                            }
                    }
            }
        else
        {
            if (Level.NetMode != NM_DedicatedServer)
            {
                for(i=0; i<Dust.Length; i++)
                    Dust[i].Destroy();

                Dust.Length = 0;

                if(bMakeBrakeLights)
                {
                    for(i=0; i<2; i++)
                        if (BrakeLight[i] != None)
                            BrakeLight[i].Destroy();
                }
            }
            TurnDamping = 0.0;
        }
    }

KDriverEnter

    function KDriverEnter(Pawn P)
    {
            Super.KDriverEnter(P);
    }

ClientKDriverLeave

    simulated function ClientKDriverLeave(PlayerController PC)
    {
            Super.ClientKDriverLeave(PC);

            bWeaponIsAltFiring = false;
            PC.EndZoom();
    }

SpecialCalcFirstPersonView

    simulated function SpecialCalcFirstPersonView(PlayerController PC, out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
    {
            ViewActor = self;
            CameraLocation = Location + (FPCamPos >> Rotation);
    }

Fire

    function Fire(optional float F)
    {
           VehicleFire(False);
    }

AltFire

    function AltFire(optional float F)
    {

            VehicleFire(true);
    }

VehicleFire

    function VehicleFire(bool bWasAltFire)
    {
             if(!bWasAltFire)
             {
                     bGearUp = 1;
                     ChangeGear(bGearUp);
             }

             else if(bWasAltFire)
             {
                     bGearDown = -1;
                     ChangeGear(bGearDown);
             }
    }

VehicleCeaseFire

    function VehicleCeaseFire(bool bWasAltFire)
    {
             if(!bWasAltFire)
             {
                     bGearUp = 0;
                     ChangeGear(bGearUp);
             }
             else if(bWasAltFire)
             {
                     bGearDown = 0;
                     ChangeGear(bGearDown);
             }
        Super.VehicleCeaseFire(bWasAltFire);
    }

ClientVehicleCeaseFire

    simulated function ClientVehicleCeaseFire(bool bWasAltFire)
    {
        Super.ClientVehicleCeaseFire(bWasAltFire);
    }

Tick

    function Tick(float DT)
    {
        local int               i, x;
        local bool              lostTraction;
        local KarmaParams       KP;
        local KRigidBodyState   BodyState;

        TheDeltaTime = DT;
        Super.Tick(DT);

        VRate = VSize(Velocity);
        KP = KarmaParams(KParams);
        KGetRigidBodyState(BodyState);

        SteerBoneAxis = AXIS_Z;
        KWake();
        if(Role == ROLE_Authority)
        {
            if(bDriving)
            {
                // Update ForwardVel, CarMPH and bIsInverted on both server and client.
                GetAxes(Rotation, worldForward, worldRight, worldUp);

                ForwardVel = Velocity dot worldForward;
                CarMPH = Abs((ForwardVel * 3600.0) / 140800.0); // Convert from units per sec to miles per hour.

                bIsInverted = worldUp.Z < 0.2;

                // Update engine sound pitch
                EnginePitch = 255.0 * ((EngineRPM+IdleRPM)/EngineRPMSoundRange);
                EnginePitch = Clamp(EnginePitch, 0.0, 255.0);
                SoundPitch = EnginePitch;

                SteerBoneAngle = (ActualSteering/InterpCurveEval(MaxSteerAngleCurve, VRate)) * SteerBoneMaxAngle * (65535.0/360.0);

                one.X = 0;
                one.Y = 0;
                one.Z = SteerBoneAngle;

                two.X = SteerBoneAngle;
                two.Y = 0;
                two.Z = 0;

                three.X = 0;
                three.Y = SteerBoneAngle;
                three.Z = 0;

                if(SteerBoneAxis == AXIS_X)
                     //SteerRot = Rotator(0, 0, SteerBoneAngle);
                     SteerRot = Rotator(one);

                else if(SteerBoneAxis == AXIS_Y)
                     //SteerRot = Rotator(SteerBoneAngle, 0, 0);
                     SteerRot = Rotator(two);

                else
                     //SteerRot = Rotator(0, SteerBoneAngle, 0);
                     SteerRot = Rotator(three);

                SetBoneDirection(SteerBoneName, SteerRot, WForce, 1, 0);
            }
        }
            // Update any stunt variables.
            if(bDoStuntInfo)
            {
                    ForwardsInOldPlane = Coords.XAxis - (Coords.XAxis dot OldCoords.ZAxis) * OldCoords.ZAxis;
                    //ForwardsInOldPlane = SafeNormal(ForwardsInOldPlane);
                    DeltaHeading = Acos( Clamp( ForwardsInOldPlane dot OldCoords.XAxis, -1.0, 1.0 ) );
                    if( (ForwardsInOldPlane dot OldCoords.YAxis) < 0.0)
                            DeltaHeading *= -1.0;
                    DeltaPitch = Asin( Clamp(Coords.XAxis dot OldCoords.ZAxis, -1.0f, 1.0) );
                    DeltaRoll = Asin( Clamp(Coords.YAxis dot OldCoords.ZAxis, -1.0f, 1.0) );
                    //debugf( TEXT("DR:%f DP:%f"), DeltaRoll, DeltaPitch );

                    bCurrentOnGround = (bVehicleOnGround || KP.bContactingLevel);
                    DaredevilPoints = 0;

                    if(bCurrentOnGround)
                    {
                            if(!bOldVehicleOnGround)
                            {
                                    // We just landed - see if we should display Daredevil 'message'
                                    InAirTime = Level.TimeSeconds - LastOnGroundTime;

                                    Dist1 = Location;
                                    Dist2 = LastOnGroundLocation;
                                    Vect  = Dist1 - Dist2;
                                    Dist3 = Vect.X * Vect.X - Vect.Y * Vect.Y;
                                    Dist3 = Sqrt(Dist3);

                                    DaredevilPoints += Max( Ceil( Abs(InAirSpin)/(0.5*DaredevilThreshInAirSpin) ) - 1, 0 );
                                    DaredevilPoints += Max( Ceil( Abs(InAirPitch)/(0.5*DaredevilThreshInAirPitch) ) - 1, 0 );
                                    DaredevilPoints += Max( Ceil( Abs(InAirRoll)/(0.5*DaredevilThreshInAirRoll) ) - 1, 0 );
                                    DaredevilPoints += Max( Ceil( InAirTime/(0.5*DaredevilThreshInAirTime) ) - 1, 0 );
                                    DaredevilPoints += Max( Ceil( InAirDistance/(0.5f*DaredevilThreshInAirDistance) ) - 1, 0 );
                                    DaredevilPoints *= 10;

                                    // A wheel must be touching the ground on landing to get a daredevil
                                    if( bVehicleOnGround && DaredevilPoints > 0 )
                                    {
                                            OnDaredevil();
                                    }
                            }

                            LastOnGroundLocation = Location;
                            LastOnGroundTime = Level.TimeSeconds;
                            InAirSpin = 0.0;
                            InAirPitch = 0.0;
                            InAirRoll = 0.0;
                    }
                    else
                    {
                            InAirSpin += (180.0/PI) * DeltaHeading;
                            InAirPitch += (180.0/PI) * DeltaPitch;
                            InAirRoll += (180.0/PI) * DeltaRoll;
                    }

                    OldRotation = Rotation;
                    bOldVehicleOnGround = bCurrentOnGround;
            }

            if(!bDriving)
            {
                    bOldVehicleOnGround = true; // If no-one is in the vehicle, dont consider it for daredevil status.
            }

            if(bAllowChargingJump)
            {
                    // Check if all wheels are on the ground.
                    if(bAllWheelsOnGround == true)
                    {
                         for(x = 0; x<Wheels.Length; x++)
                         {
                             if (Wheels[x].bWheelOnGround != false)
                                    bAllWheelsOnGround = false;
                         }
                    }
                    else
                    {
                         // If any wheels are off the ground - we can just reset everything.
                         if(!bVehicleOnGround)
                         {
                              JumpForce = 0.0;
                              JumpSpin = 0.0;
    //                          bPushDown = false;
                         }

                    }
            }
          //Engine(Throttle);
         // If on the server - we work out OutputGas, OutputBrake etc, and pack them to be sent to the client.
         if(!bClutching)
               Gas2 = InterpCurveEval(RPMtoGas, EngineRPM);
         ProcessCarInput();
         PackState();
    }

ProcessCarInput

    function ProcessCarInput()
    {
            local bool     bReverse;

            bReverse = false;
            if(Driver == none)
            {
                    OutputBrake = 1.0;
                    OutputGas = 0.0;
            }
            else
            {
                    if(Throttle >= 0) // pressing forwards
                    {
                            OutputGas = Abs(Throttle);
                            OutputBrake = 0.0;
                            if(bClutching)
                            {
                                 if(Throttle == 0)
                                 {
                                      if(Gas2<=21 && Gas2>=0)
                                      {
                                            Gas2 -= 0.5;
                                      }
                                      if(Gas2<0)
                                            Gas2 = 0;
                                 }
                                 else
                                 {
                                      if(Gas2<=21 && Gas2>=0)
                                      {
                                             Gas2 += 0.5;
                                      }
                                      if(Gas2>21)
                                             Gas2 = 21;
                                 }
                                 if(Gas2 >=21)
                                      Gas2 = 21;
                             }
                             if(!bClutching)

                     }

           StoptheCar();
           OutputBrake = Abs(InterpCurveEval(BrakeCurve, hBrake));

            OutputPitch = Rise;
            }
            Engine();
            KWake();
    }

Clutch

    exec function Clutch(bool bEnabled)
    {
           if(!bEnabled)
           {
               bClutching = False;
               bInGear = False;
           }

           else if(bEnabled)
           {
               bClutching = True;

               RPM = EngineRPM;
           }
    }

Brake

    exec function Brake(bool bEnabled)
    {
          if(bEnabled)
          {
               bBraking = True;
               OutputHandbrake = true;
          }
          if(!bEnabled)
          {
               bBraking = false;
               OutputHandbrake = false;
          }
    }

ChangeGear

    function ChangeGear(int GearChoice)
    {
          if(bClutching){
            if(Gear >= 1 && Gear <= 7)
            {
                 if(GearChoice == 1)
                 {
                      Gear++;
                      bGearUp = 0;
                 }
                 else if(GearChoice == -1)
                 {
                      Gear--;
                      bGearDown = 0;
                 }
                 else if(GearChoice == 0)
                 {

                 }
            }
            else if(Gear == 0 && GearChoice == 1)
            {
                  Gear++;
                  bGearUp = 0;
            }
            else if(Gear == 8 && GearChoice == -1)
            {
                  Gear--;
                  bGearDown = 0;
            }
            else
            {
                 bGearUp = 0;
                 bGearDown = 0;
            }
          }
    }

PackState

    function PackState()
    {
            local KRigidBodyState   RBState;
            local rotator           ViewRot;
            if(!KIsAwake())
                    return;
            KGetRigidBodyState(RBState);
            CActiveWeapon=SActiveWeapon;
            CarState.ChassisPosition.X = RBState.Position.X;
            CarState.ChassisPosition.Y = RBState.Position.Y;
            CarState.ChassisPosition.Z = RBState.Position.Z;
            CarState.ChassisQuaternion = RBState.Quaternion;
            CarState.ChassisLinVel.X = 10.0 * RBState.LinVel.X;
            CarState.ChassisLinVel.Y = 10.0 * RBState.LinVel.Y;
            CarState.ChassisLinVel.Z = 10.0 * RBState.LinVel.Z;
            CarState.ChassisAngVel.X = 1000.0 * RBState.AngVel.X;
            CarState.ChassisAngVel.Y = 1000.0 * RBState.AngVel.Y;
            CarState.ChassisAngVel.Z = 1000.0 * RBState.AngVel.Z;
            CarState.ServerHandbrake = FloatToRangeByte(OutputPitch);
            CarState.ServerBrake = FloatToRangeByte(OutputBrake);
            CarState.ServerGas = FloatToRangeByte(OutputGas);
            CarState.ServerGear = Gear;
            CarState.ServerSteering = FloatToRangeByte(Steering);
            if(Controller != None)
            {
                    if(IsHumanControlled())
                    {
                            DriverViewPitch = Controller.Rotation.Pitch;
                            DriverViewYaw = Controller.Rotation.Yaw;
                    }
                    else
                    {
                            ViewRot = rotator(Controller.FocalPoint - Location);
                            DriverViewPitch = ViewRot.Pitch;
                            DriverViewYaw = ViewRot.Yaw;
                    }
            }
            else
            {
                    DriverViewPitch = Rotation.Pitch;
                    DriverViewYaw = Rotation.Yaw;
            }
            CarState.ServerViewPitch = DriverViewPitch;
            CarState.ServerViewYaw = DriverViewYaw;
            FudgeByte++;
    }

Motion Blur (still in the works)

    function addblur()
    {
          local PlayerController PC;

          PC = PlayerController(Controller);

          PC.AddCameraEffect(new myBlur);
    }

LimitPitch

    function int LimitPitch(int pitch)
    {
            if (bAllowAirControl && !bVehicleOnGround)
                    return pitch;
            return Super.LimitPitch(pitch);
    }

Destroyed

    simulated function Destroyed()
    {
        if (LeftTrail != None)
            LeftTrail.Destroy();

        if (RightTrail != None)
            RightTrail.Destroy();

        Super.Destroyed();
    }

TakeImpactDamage

    event TakeImpactDamage(float AccelMag)
    {
        local int Damage;
            Damage = int(VSize(ImpactInfo.Other.Velocity) * 20 * ImpactDamageModifier());
        if (Vehicle(ImpactInfo.Other) != None)
            TakeDamage(Damage, Vehicle(ImpactInfo.Other), ImpactInfo.Pos, vect(0,0,0), class'DamType');
        else
            TakeDamage(int(AccelMag * ImpactDamageModifier())/ WallCollisionResistance , Self, ImpactInfo.Pos, vect(0,0,0), class'DamType');

        //FIXME - scale sound volume to damage amount
        if (ImpactDamageSounds.Length > 0)        PlaySound(ImpactDamageSounds[Rand(ImpactDamageSounds.Length-1)],,TransientSoundVolume*2.5);
        if (Health < 0 && (Level.TimeSeconds - LastImpactExplosionTime) > TimeBetweenImpactExplosions)
        {
            VehicleExplosion(Normal(ImpactInfo.ImpactNorm), 0.5);
            LastImpactExplosionTime = Level.TimeSeconds;
        }
    }

Died

    function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
    {
        if (damageType == none)
           return;
        if (Killer != None)        PlayerController(Killer).ReceiveLocalizedMessage(class'VehicleKillMessage', 7);
        Super.Died(Killer, damageType, HitLocation);
    }

FloatToRangeByte

    simulated function byte FloatToRangeByte(float f)
    {
            f= FClamp(f, 0, 1);
            f = Round(f * 255);
            return byte(f);
    }

RangeByteToFloat

    //The opposite of the above function.
    simulated function float RangeByteToFloat(byte b)
    {
            local float f;
            f = b;
            f /= 255;
            return f;
    }

#### Defaults

    defaultproperties
    {
         myBlur=Class'WhatEverPackage.myMotionBlur'

         NumForwardGears=7

         DustSlipRate=2.800000
         DustSlipThresh=50.000000
         DaredevilThreshInAirSpin=100.000000
         DaredevilThreshInAirPitch=300.000000
         DaredevilThreshInAirRoll=300.000000
         DaredevilThreshInAirTime=1.500000
         DaredevilThreshInAirDistance=17.000000
         DaredevilMessageClass=Class'Onslaught.ONSDaredevilMessage'
         bOldVehicleOnGround=True
         MaxJumpSpin=30000.000000
         JumpChargeTime=1.000000
         JumpFeedbackForce="HoverBikeJump"
         JumpSound=Sound'ONSVehicleSounds-S.Hydraulics.Hydraulic10'
         JumpMeterOriginX=0.275000
         JumpMeterOriginY=0.943000
         JumpMeterWidth=0.136000
         JumpMeterHeight=0.057000
         JumpMeterSpacing=0.010000
         JumpMeterColor=(R=215,A=255)
         SpinMeterColor=(B=215,G=100,A=255)
         JumpMeterTexture=Texture'Engine.WhiteTexture'
         MinAirControlDamping=0.100000
         bBrakeFrontWheelsOnly=False
         ChangeUpPoint=2800
         StopThreshold=0.2
         CrosshairHitFeedbackTex=Texture'ONSInterface-TX.tankBarrelAligned'
         DefaultCrosshair=Material'InterfaceContent.Hud.fbBombFocus'
         CrosshairScale=0.125
         bTeamLocked=false
         bGearUp=True
         bInGear=True
         bEjectPassengersWhenFlipped=False
         bCanFlip=true
         DisintegrationHealth=0.0
         bShowChargingBar = false;
         // Damages
         WallCollisionResistance = 5 // Collision resistance: the bigger it is, the less damage taken
         ExplosionDamage=0.0
         DriverDamageMult=0
         CenterSpringRangePitch=5000
         CenterSpringRangeRoll=5000
         StolenAnnouncement="Play"

         bNetNotify=true
    }

- Global variables that would need to be added to the above uscript:

    enum ETextAnchor
    {
        TA_Left,
        TA_Right,
        TA_Middle
    };
    var   float                         ScaleX, ScaleY;

CIpen: Here is some simple code you can add to print data on the screen to avoid logging 24/7. This is what I used to check numbers such as RPM, Gas, EngineRPM, and whatever else I could fit on 1024x768 resolution. I've found it to be most handy. I'm sure you can find this elsewhere but I'm saving you the effort of looking.

- And here is the DrawHUD function that we must have to draw on the screen when a player has possesion of the vehicle:

    simulated function DrawHUD(Canvas C)
    {
        local PlayerController PC;

        PC = PlayerController(Controller);

        ScaleX = C.ClipX/1024;
        ScaleY = C.ClipY/768;

        C.Style = ERenderStyle.STY_Additive;
        C.Font = PC.MyHUD.GetFontSizeIndex(C, -2);
        C.DrawColor = C.MakeColor(255,255,255);

        DrawEngineStuff(C);
    }

- Here is the DrawEngineStuff(C) which is isn't a required function but is there to organize:

    simulated function DrawEngineStuff(Canvas C)
    {
        C.Font = C.TinyFont;

        C.SetPos(40*ScaleX, 150*ScaleY);
        C.DrawText("Engine stuff");

        C.SetPos(40*ScaleX, 160*ScaleY);
        C.DrawText("Engine RPM: " @ EngineRPM);
    }

40*ScaleX sets the vertical starting position, and 150*ScaleY sets the horizontal starting position.

#### Notes

If you are to subclass, you need to know that the engine rpm is in radians per min. This means that if you were to create a torque curve and wanted the rpm range to be from 500 to 7000, you would need to convert all the points from revolutions per min to radians per sec. Take the rpm(real life engine rpm) of 7000, divide by 60, then multiply by 2 PI. Like so:

(7000/60)*6.2831853

this yields 733.038285 as the correct rpm

bNetNotify=true is prolly the most critical thing needed for this class to work online/lan

I've got a lot of changes that need to be made to this class, so be patient. Additions include, properly calculating wheel torque if the wheel is powered or not, better collisions with walls(currently if you hit a wall flat at 300 mph the vehicle just stops and takes no damage), available auto transmision, ...

## Related Topics

- Vehicles – Main topics for Vehicles.
- Vehicle – Parent class for all vehicles and turrets.
  - SVehicle
    - ONSVehicle
      - ONSWheeledCraft
- Making Mods

## Discussion

Graphik: Very nice idea, but a bit unsuited for an FPS, more for a racing game like UnWheel. It seems to me that Epic made sure that the vehicles were as "dumbed down" as possible to keep the focus on fragging people with a vehicle rather than the minutia of driving one. I recommend sending this in to UnWheel when you're done. If they don't actually use it I'm sure they'd at the very least find it interesting.

CIpen: Accually I got a thumbs up on that at the same time I created this page. The bounds of this script is almost limitless, as it would be easy to have say the tire blow out when shot. As soon as I get a free day I'll update this page with all the new additions (infact that might be something good to do tonight). The big change is that I added the option for auto or stick control and the the power is ditributed over the wheels that are powered.

Foxpaw: Why do you divide by 60 when converting rotations per minute to radians per minute? The radians per minute should be greater than the roatations per minute, as there's more than one radian per rotation. Or is that supposed to say, radians per second?

CIpen: Yes, you pointed out a typo :D. Yes, it should say radians per second. Yet your own message has typos, it's not rotations per minute, but revolutions per minute. :)

Foxpaw: Err, ehm, well, I guess that depends on where you measure it from. I'd measure it from the flywheel which definately rotates. I suppose some parts of the crankshaft do revolve though, so either could be correct.

Zxanphorian: WOW, that is a lot of scriptage there! I have an idea, to make it randomly stall :B

CIpen: Ya, there are a lot of cool possibilities with this class. You could attach a model to the wheel bone, add another bone for disk brakes (to attach a disk brake mesh to that turns when the wheels turn), make the engine blow up if you downgear and the engine rpm goes to high, have an axle break, have more wheel slip functions for different traction of different surfaces... any number of things....

OlympusMons: I would have to agree CIpen. Has anyone given any thought to making a proper automatic car you know the manual automatic ones. :S Like what mafia did. I havent really had time to look through all this code although I'd like to, I was just wondering if all of this is actually needed to make a manual shift car or if there are other features which have been added through all of this script. Actually another cool thing to do would be to make a proper 4wd manual shift. Hmm!

CIpen: Good! Lets get to the meat! The first thing you need to know is that the function UpdateVehicle() is somehow tied to native code. To see this all you need to do is log the DeltaTime from inside the Tick() function and from inside UpdateVehicle() function. You should see that UpdateVehicle() always has the same DeltaTime so matter what your FPS is (at least this is what I remember it being when I tested it).

The next serious thing is that, unless you do your own wheel calculations, you'll have to build around the SVehicleWheel variables that are calculated each frame. It's both good and bad I guess. Take a minute to thing about this: below is the line that calculates the GripTorque.

    GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));

Now, lets say we want to be the biggest uscript hot shot around, you might change this line to look like this:

    if(bPlayerIsOnHood)
         GripTorque = FTScale * Wheels[i].WheelRadius * (Wheels[i].TireLoad + PlayerMass) * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));
    else
         GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));

Thus simulating player weight on the car(more or less). That is after you do some stuff to get if the player is on the vehicle or not.

btw I just updated it UpdateVehicle() because it was far behind.

Tarquin: I moved this back to its original name, as the class name didn't seem very descriptive.

WinterHummer: Can someong give me a good scorpion subclass of this for me and other lazy people?

Category: Legacy Tutorial

##### Views

- Legacy
- Discussion
- Current revision

##### Search