@name Wheels/Suspention
@inputs Pod:wirelink Engine:entity
@outputs Throttle MPH Turn [Body Chip]:entity Angles:angle Brakes
@persist Vector:vector Spawn Spin AltSpin Slide Skid Grip Front Rear Left Right Ranger
@persist Active State Steer Hold Pitch Yaw Roll Max Min Flood WPitch Speed Power Z [EngineStart EngineSound EngineStop]:string
@persist Mass Ground WheelX WheelY FWD RWD FWSteer RWSteer TireSize TravelU TravelD StartHeight

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

if (Spawn < 8){
    interval(100)
    
    Ground = 45 # the height from the ground you want the chassis to be
    TravelUp = 20 # the amount the wheels push up into the body
    TravelDown = -14 # the amount the wheels hang down when in the air
    
    StartHeight = 10 # The start height of the suspension rangers
    
    WheelX = 50 # the length of the wheelbase
    WheelY = 20 # the width of the wheelbase
    
    WheelSize = 2.5 # The size of the wheel middle
    WheelWidth = 0.7  # The width of the wheel middle
    
    TireSize = WheelSize * 1.1 # The size of the tyres [Don't need to change]
    TireWidth = WheelWidth * 4.5 # The width of the tyres [Don't need to change]
    
    FWD = 0 # Front Wheel Drive: 1=YES / 0=NO
    RWD = 1 # Rear Wheel Drive: 1=YES / 0=NO
    FWSteer = 1  # Front Wheels Steer: 1=YES / 0=NO
    RWSteer = 0 # Rear Wheels Steer: 1=YES / 0=NO
    
    Power = 12 # How much power the engine has: Limit[10 ~ 20]
    
    EngineStart = "vehicles/v8/v8_start_loop1.wav"
    EngineSound = "acf_engines/i5_petrolsmall.wav"
    EngineStop = "vehicles/v8/v8_stop1.wav"
    
    Mass = 2000
    Body:setMass(Mass)
    
    TravelU = TravelUp + TireSize*2
    TravelD = TravelDown - TireSize*2
    
    Spawn++
    holoCreate(Spawn)
    
    if (Spawn < 5) {
        holoAng(Spawn,Body:toWorld(ang(0,90,90)))
        holoParent(Spawn,Body)
        holoAlpha(Spawn,0)
    } else {
        holoModel(Spawn,"models/sprops/trans/wheel_a/t_wheel35.mdl")
        holoAng(Spawn,Body:toWorld(ang(0,90,0)))
        holoMaterial(Spawn,"models/debug/debugwhite")
        holoParent(5,1)
        holoParent(6,2)
        holoParent(7,3)
        holoParent(8,4)
    }         
} else {
    Active = Pod:number("R")
    if (changed(Active) & Active) {if(State == 0) {State = 1} else {State = 0}}
    
    if (State == 1) {
        W = Pod:number("W")
        A = Pod:number("A")
        S = Pod:number("S")
        D = Pod:number("D")
        Shift = Pod:number("Shift")
        Space = Pod:number("Space")
        
        Flood = entity():isWeldedTo():isUnderWater()
        
        if (State == 1 & $State == 1 & !Flood) {
            Engine:soundPlay(1,2,EngineStart),soundVolume(1,10)
            Engine:soundPlay(2,0,EngineSound),soundStop(5),soundStop(6)
        } elseif (State == 1 & !Flood & $Flood) {
            Body:soundPlay(1,2,EngineStart),soundVolume(1,10)
            Engine:soundPlay(2,0,EngineSound),soundStop(5),soundStop(6)
        } elseif (Flood & $Flood) {
            Hold = 2,
            soundStop(1),soundStop(2),Engine:soundPlay(5,5,"vehicles/jetski/jetski_off.wav"),Chip:soundPlay(6,5,"ambient/energy/newspark10.wav")
        }   
    } else {
        if ($State == 1 & !Flood) {Engine:soundPlay(5,0,EngineStop)}
        if (Speed > 200) {Hold = 0.1}
        else {Hold = 1.8}
    }

    runOnTick(1)

    if (tickClk()) {
        Up = Body:up()
        Side = Body:right()
        Forward = Body:forward()
        BPos = Body:pos()
        Angles = Body:angles()
        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}
        
        rangerHitEntities(1)
        rangerFilter(players())
        rangerFilter(entity():isWeldedTo())
        RangerRL = rangerOffset(1000,Body:toWorld(vec(WheelX,WheelY, StartHeight)),-Up)
        FrontL = RangerRL:distance()
        
        rangerHitEntities(1)
        rangerFilter(players())
        rangerFilter(entity():isWeldedTo())
        RangerRL = rangerOffset(1000,Body:toWorld(vec(WheelX,-WheelY, StartHeight)),-Up)
        FrontR =  RangerRL:distance()
        
        rangerHitEntities(1)
        rangerFilter(players())
        rangerFilter(entity():isWeldedTo())
        RangerFL = rangerOffset(1000,Body:toWorld(vec(-WheelX,WheelY, StartHeight)),-Up)
        RearL =  RangerFL:distance()
        
        rangerHitEntities(1)
        rangerFilter(players())
        rangerFilter(entity():isWeldedTo())
        RangerFL = rangerOffset(1000,Body:toWorld(vec(-WheelX,-WheelY, StartHeight)),-Up)
        RearR =  RangerFL: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 (Active & !Flood) {
            GripZ =  Ground + StartHeight
            Grip = (((FrontL < GripZ) + (FrontR < GripZ) + (RearL < GripZ) + (RearR < GripZ))-Slide)
                
            if (W|S) {Sign = W-S}
            elseif (Speed > 10) {Sign = 0}
            
            if (Sign) {
                if (Sign != sign(VelX)) {
                    if (Speed > 50) {Brakes = 1, Throttle += Sign*6}
                    elseif (Throttle & sign(Throttle) > 50 & WPitch>20) {Brakes = 0}
                } else {
                    Brakes = 0
                    if (Speed < 100 & Sign != sign(Throttle)) {Throttle = 0}
                }
            } else {Brakes = 0}

            if (!Sign & Speed < 40) {Brakes = 1, Hold = 1.8}
    
            elseif (Brakes == 1) {if (Speed > 150 & WPitch<10) {Hold = 0.04}
                elseif (Speed < 10 & WPitch>=10) {Hold = 1.8}
                else {Hold = 0.08}
            } else {if(Space) {Hold = 0.008} else {Hold = 0}
                if (Throttle * sign(Throttle) > Speed) {Spin = angnorm(Spin+Throttle/(TireSize*12))}
                else {Spin = angnorm(Spin+VelX/(TireSize*12))}
            }
  
            Throttle = clamp(Throttle,Min,Max) 
            
            if (!Space & Sign) {
                if (Sign == 1) {
                    Throttle+= clamp((Max - Throttle),0,6-Grip)
                }
                
                elseif (Sign == -1) {Throttle-= 2}
            } else {Throttle-=(inrange(Throttle,-1,1) ? Throttle : sign(Throttle)*3)}
             
            Max = clamp((Speed*8)/(8+Grip),200*(1+Slide),1400)
            
            if (VelX < 100) {Min = clamp(Speed * -2,-400,-200)} else {Min = 0}
            
            if (A|D) {Steer = (A-D)} else {Steer = 0}
    
            if (A|D) {
                Turn = clamp(Turn+(Steer)*0.8,-40,40)
                
                if ((Steer) & sign(Turn) != (Steer)) {Turn -= (inrange(Turn,-1,1) ? Turn : sign(Turn))*0.008*clamp(Speed,0,200)}
            } elseif (!inrange(Turn,-1,1)) {Turn -= sign(Turn)*0.004*clamp(Speed,0,200)} else {Turn = 0}
            
            Slide = clamp(Slide + (Space+(Speed*Turn*sign(Turn)*0.000001) - Slide*0.00002 * (Speed > 400 ? Speed : 400)),0,1.56)
            
            Yaw = Turn*(0.7/(WheelX/WheelY))*(Slide*0.2*((RWD-FWD)+Space)+1)*((Speed)<200 ? (VelX/200) : clamp(12000/VelX,-1,1))

            } else {Throttle=0,Yaw=0,Brakes = 0,Slide=0,soundStop(1),soundStop(2)}
            
            soundPitch(2,abs(Throttle)*0.1+30)
            soundVolume(2,abs(Throttle)*0.001+10)
            
            if (Speed >= 1000) {Top = Speed*Speed*0.000000001} else {Top = 0}
            
            if ((Ranger-StartHeight) <Ground & WPitch < 85) {
            
            if (Brakes & Speed > 50) {Skid = 1}
            elseif (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+Top))))*Mass
            
            Torque = rotationVector(quat(ang(Pitch,Yaw,Roll)-(Body:angVel()/4)))
            Body:applyTorque((Torque * 50)*Body:inertia())
            
            Vector =  Forward*Throttle*((3+Grip)-Slide)*Power
            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,Slide=1.5,Spin=angnorm(Spin+Throttle/24),Body:applyAngForce(ang(0,0,0)-(Body:angVel()*1000))} 
            
            WheelZ = StartHeight+2+TireSize*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)) {
                if (!FWD | (!RWD &!Space)) {AltSpin = angnorm(AltSpin+VelX/(TireSize*12))}
                
                holoAng(1,Body:toWorld(ang(FWD ? Spin : AltSpin,FWSteer * Turn,90)))
                holoAng(2,Body:toWorld(ang(FWD ? Spin : AltSpin,FWSteer * Turn,90)))
                holoAng(3,Body:toWorld(ang(RWD ? Spin : AltSpin,RWSteer * -Turn,90)))
                holoAng(4,Body:toWorld(ang(RWD ? Spin : AltSpin,RWSteer * -Turn,90)))
            }
    }  
}

