@name Instant Suspension
@inputs Turn Yaw Vector:vector Hold Slide
@outputs MPH Speed [Body Chip]:entity Angles:angle
@persist Vector:vector Spawn Spin Slide Skid Front Rear Left Right Ranger
@persist Pitch Roll WPitch Speed Z
@persist Mass Ground WheelX WheelY FWSteer RWSteer TyreSize TravelU TravelD StartHeight

if(dupefinished()|first()){Chip = entity(),Body=entity():isWeldedTo(),Spawn = 0}

# -- CIPHER_ULTRA'S INSTANT SUSPENSION -- #

# -- Guidelines -- #

# 1. This E2 will work on most props, but I recommend flat squares or cubes such as: 
#      * models/props_phx/construct/metal_plate1.mdl
#      * models/hunter/plates/plate1x1.mdl
#      * models/props_c17/FurnitureShelf001b.mdl

# 2. Make sure any other constrained props (like a car body) all have a mass of '1' or less.
#    If you need some to be heavier, do not make them weigh over 100.
#    Try to balance any larger (10 ~ 100) weights equally over the chassis.  

# 3. I also recommend parenting all props and using the 'Main' prop,
#    (The one the chip is placed on) as the Parent.
#    Parenting makes contraptions more stable and reduces lag.

# 4. For advanced users: It is also best to leave some props unparented to
#    create a collision box around the vehicle, such as bumpers.


if(Spawn < 8){interval(100)


# -- CUSTOMISATION -- #

# -- Suspension Settings -- #

    Ground = 30 # the height from the ground you want the chassis to be
    TravelUp = 5 # the amount the wheels push up into the body
    TravelDown = -5 # the amount the wheels hang down when in the air
    
    StartHeight = 10 # The start height of the suspension rangers...
                     # (Only need to lower if the any bodywork, like wheel arches,
                     # are very close to the top of the wheels.
                    
  
# -- Wheel Settings -- #

    WheelX=70 # the length of the wheelbase
    WheelY=40 # the width of the wheelbase
    
    WheelSize = 1.8 # The size of the wheel middle
    WheelWidth = 0.8  # The width of the wheel middle
        
    TyreSize = WheelSize * 1.1 # The size of the tyres [Don't need to change]
    TyreWidth = WheelWidth * 4.5 # The width of the tyres [Don't need to change]

    WheelMaterial = "phoenix_storms/gear_top"
    TyreMaterial = "phoenix_storms/car_tire"


# -- Drivetrain Settings -- #

    FWSteer = 1  # Front Wheels Steer: 1=YES / 0=NO
    RWSteer = 0 # Rear Wheels Steer: 1=YES / 0=NO


# -- END of settings -- #




# ----------------------------------------------------------------------------------------#


# -- No need to change any of the following code -- #
# -- Only advanced E2 users should change the following code! -- #


# ----------------------------------------------------------------------------------------#
   



    Mass = 2000
    Body:setMass(Mass)
    
    TravelU = TravelUp + TyreSize*2
    TravelD = TravelDown - TyreSize*2
    
    Spawn++
    holoCreate(Spawn)
                   
    if(Spawn < 5){holoModel(Spawn,"cylinder") # Wheel middle
        holoScale(Spawn,vec(WheelSize,WheelSize,WheelWidth))
        holoAng(Spawn,Body:toWorld(ang(0,0,90)))
         holoMaterial(Spawn,WheelMaterial)
        holoParent(Spawn,Body)
    }else{holoModel(Spawn,"hqtorus2") # Tyres
        holoAng(Spawn,Body:toWorld(ang(0,0,90)))
        holoScale(Spawn,vec(TyreSize,TyreSize,TyreWidth))
        holoMaterial(Spawn, TyreMaterial)
        holoParent(5,1)
        holoParent(6,2)
        holoParent(7,3)
        holoParent(8,4)
    }

             
}else{


runOnTick(1)
if(tickClk()){
    
#### Variables ###
Up = Body:up()
Side = Body:right()
Forward = Body:forward()
WPitch = abs(Angles:pitch())
Vel = Body:velL()
VelX = Vel:x()
VelY = Vel:y()
Speed = VelX * sign(VelX)
if(Speed > 20){MPH = round(toUnit("mph",Speed))}else{MPH = 0}


# -- The Suspension Code -- #
    
            rangerHitEntities(1) 
            rangerFilter(players())
            RangerFL = rangerOffset(1000,Body:toWorld(vec(WheelX,WheelY,StartHeight)),-Up)
            FrontL = RangerFL:distance()
                  
            rangerHitEntities(1)
            rangerFilter(players())
            RangerFR = rangerOffset(1000,Body:toWorld(vec(WheelX,-WheelY,StartHeight)),-Up)
            FrontR =  RangerFR:distance()
                        
            rangerHitEntities(1)
            rangerFilter(players())
            RangerRL = rangerOffset(1000,Body:toWorld(vec(-WheelX,WheelY,StartHeight)),-Up)
            RearL =  RangerRL:distance()
            
            rangerHitEntities(1)
            rangerFilter(players())
            RangerRR = rangerOffset(1000,Body:toWorld(vec(-WheelX,-WheelY,StartHeight)),-Up)
            RearR =  RangerRR:distance()            
            
            Front = (FrontL + FrontR)/2 # average of the the front rangers
            Rear = (RearL + RearR)/2 # etc
            Left = (FrontL + RearL)/2
            Right = (FrontR + RearR)/2


Ranger = min(FrontL,FrontR,RearL,RearR)  # smallest of the rangers

  Pitch = ((Front-Rear))
  Roll =  ((Right-Left))

   
    if(Ranger-StartHeight <Ground & WPitch < 90){
                
            Spin = angnorm(Spin+VelX/(TyreSize*12))
                
            if(Slide > 1.2){Skid = 1}
            else{Skid = 0}
                        
  if(Skid & $Skid){Body:soundPlay(3,0,"vehicles/v8/skid_normalfriction.wav")}
        elseif(!Skid & $Skid){soundStop(3)}


ForceStable=((VelY * Side * (1.6 - Slide)) + (VelX * Forward * (-0.02 - (Hold))))*Mass

Torque = rotationVector(quat(ang(Pitch,Yaw,Roll)-(Body:angVel()/4)))
Body:applyTorque((Torque * 50)*Body:inertia())


    Z=(4000)*clamp((StartHeight+(Ground-5))-Ranger,0,StartHeight+(Ground-5)) 
    ForceMain=Up*(Mass+Z) 
    ForceZ = Up*-Vel:z()*Mass/12 
    
    
     Body:applyForce(((ForceMain+ForceZ)+ForceStable+Vector)) 
}else{Z=0,Body:applyForce(Up*(sign(Vel:z())==1 ? -Vel:z()*Mass/12 : 0)),Skid = 0,Body:applyAngForce(ang(0,0,0)-(Body:angVel()*1000))} 

        
            
# -- Wheel Animations -- #
                           
    WheelZ = StartHeight+2+TyreSize*6
                                
    FLPos = Body:toWorld(vec(WheelX,WheelY,clamp(WheelZ - FrontL,TravelD,TravelU)))
    FRPos = Body:toWorld(vec(WheelX,-WheelY,clamp(WheelZ -FrontR,TravelD,TravelU)))
    RLPos = Body:toWorld(vec(-WheelX,WheelY,clamp(WheelZ - RearL,TravelD,TravelU)))
    RRPos = Body:toWorld(vec(-WheelX,-WheelY,clamp(WheelZ - RearR,TravelD,TravelU)))
    
    holoPos(1,FLPos)
    holoPos(2,FRPos)
    holoPos(3,RLPos)
    holoPos(4,RRPos)
            
    
    if(changed(Spin)|changed(Turn)){
    
         holoAng(1,Body:toWorld(ang(Spin,FWSteer * Turn,90)))
         holoAng(2,Body:toWorld(ang(Spin,FWSteer * Turn,90)))
         holoAng(3,Body:toWorld(ang(Spin,RWSteer * -Turn,90)))
         holoAng(4,Body:toWorld(ang(Spin,RWSteer * -Turn,90)))
    }
                        
# -- END of Wheel Animations -- #
    
}#end tick

  
}

# -- CIPHER_ULTRA 2011 -- #

