# THIS MODULE IS TO REPLACE THE OLD Car_controll.py MODULE
# WHICH ENDED TOO LARGE AND TOO HARD TO MAINTAIN.

import os
import bge
import time
import math
import json
import bpy
import random
import numpy
import aud
import mathutils


from Scripts import Reuse
from Scripts import Destruction
from Scripts import Opt
from Scripts import Map
from Scripts import Script
from Scripts import Tools
from Scripts import Money
from Scripts import Garage
from Scripts import Mouse

from Scripts import Multiplayer_Shared
from Scripts import Multiplayer_Client

from Scripts import Character_Controll

from Scripts.Common import *

controls = {

    "forward" :keycodes["UpArrow"],
    "backward":keycodes["DownArrow"],
    "left"    :keycodes["LeftArrow"],
    "right"   :keycodes["RightArrow"],   
    "drift"   :keycodes["LCtrl"],
    "nitro"   :keycodes["Z"],
    "resque"  :keycodes["BackSpace"],
    "gearup"  :keycodes["W"],
    "geardown":keycodes["S"],
    "autogear":keycodes["Q"],
    "dynamcam":keycodes["C"],
    
    # Truck
    "unload"  :keycodes["D"]
}

if "veh_controls" not in bge.logic.globalDict:
    bge.logic.globalDict["veh_controls"] = controls
controls = bge.logic.globalDict["veh_controls"]    

# Data about various cars in the game

def LoadCarData():

    # Loading Json animation data files.
    
    folder = bge.logic.expandPath("//cardata/")

    if "carData" not in bge.logic.globalDict:
        
        carData = {}
    
        for f in os.listdir(folder):
            if f.endswith(".json"):
                print("Reading", f, "...")
                with open(folder+f) as fl:
                    data = json.load(fl)

                name = f.replace(".json", "")

                carData[name] = data
        bge.logic.globalDict["carData"] = carData
        
    return bge.logic.globalDict["carData"]
    
carData = LoadCarData()

def AssingProbablity(carData):

    if "NPC_cars" not in bge.logic.globalDict:
    
        NPC_cars = list(carData.keys())
        NPC_type_cars = {}

        for carname in carData:
            car = carData[carname]
            t   = car.get("type", "race-car")
            if t not in NPC_type_cars:
                NPC_type_cars[t] = []
            NPC_type_cars[t].append(carname)
        
        npccp = []
        for carname in carData:
            car = carData[carname]
            npccp.append(car.get("probability",1))
        NPC_cars_probability = []
        for i in npccp:
            NPC_cars_probability.append( 1 / sum(npccp) * i)
        bge.logic.globalDict["NPC_cars"] = NPC_cars
        bge.logic.globalDict["NPC_type_cars"] = NPC_type_cars
        bge.logic.globalDict["NPC_cars_probability"] = NPC_cars_probability

    return bge.logic.globalDict["NPC_cars"], bge.logic.globalDict["NPC_type_cars"], bge.logic.globalDict["NPC_cars_probability"]
    
NPC_cars, NPC_type_cars, NPC_cars_probability = AssingProbablity(carData)
    
# Position of the garage fix point.
fixGaragePosition = [-754.61, -1043.9, 409.32]

def UpdateCar(car):

    # This function runs on every frame for each car, 
    # making the car interactable in the game.

    
    # Clear the car and get the scene
    scene, car = GetSceneAndCar(car)
        

    
    # Mapping all cars on the minimap ( for debuging )
    #Map.Show(car, color=GetColor(car))
    
    # Performace optimization method.
    if car.get("racing") or car.get("active") or InView(car):

        if not car.get("inview"):
            if not car.get("isCargo"):
                car.restorePhysics()
            car["inview"] = True
            

        # Make sure this car is not deleted when we see it
        if CloseToCamera(car, 200):
            if car in Reuse.reuse.get(car.name):
                Reuse.reuse[car.name].remove(car)
                print(consoleForm(car),clr["bold"]+clr["tdyl"],"Removed from Reuse.", clr["norm"])
                car["nodespawn"] = 100
            if car not in bge.logic.globalDict["allcars"]:
                bge.logic.globalDict["allcars"].append(car)
                print(consoleForm(car),clr["bold"]+clr["tdyl"],"Restored.", clr["norm"])
                car["nodespawn"] = 100
        
        # Updating the car physics parameters
        PhysicsUpdate(car)

        # Truck updates
        if "Truck" in str(car):
            UpdateCargo(car)

        # Car Control
        if car.get("active"):
            UserControl(car)
            
            
        elif car.get("npc") == "npc":
            NormalNPC(car)

        elif car.get("npc") == "pursuit":
            AngryPursuit(car)

        elif car.get("npc") == "story":
            pass # If it is controlled from story
        
            
        elif car.get("racing"):
            RacingAI(car)
            
        # If nobody controls the car.
        else:
            IdleBraking(car)

        
        # Apply  Turn    
        ApplyTurn(car)

        # Automatic Transmission
        if car.get("autogear", True):
            AutomaticTransmission(car)

        # Forces ( Drag Force, Down Force )
        x = 0
        y = DragForce(car)
        z = -DownForce(car)
        car.applyForce([x, y, 0], True)
        car.applyForce([0, 0, z], True)
        if not car.get("underwater"):
            car.applyTorque([-z/2*(car.localLinearVelocity[1] < 0)
                             , 0,0], True)

        # Give car the sound
        EngineSound(car)
        DriftSound(car)

        # Smoke and fire
        SmokeAndFire(car)
        GroundPuffsOfSmoke(car)
        
        # Manual fixing.
        # Moved to Tools.py

    else:
        if car.get("inview", True):
            car.suspendPhysics()
            car["inview"] = False

        if car.get("nodespawn", 0): car["nodespawn"] -= 1
           
def GetSceneAndCar(car):
    
    # This module parses the inputs of other modules.
    # And gives back bge.scene and the car object.
    
    # We don't know if what we get here is the controller
    # where the script is running, or the object itself.
    
    if type(car) == bge.types.SCA_PythonController:
        car = car.owner
        
    scene = bge.logic.getCurrentScene()
    
    return scene, car

def PhysicsUpdate(car):
    
    # Updating the car physics to 
    # work with a more realistic
    # effect, while cheating at some
    # things.

    scene, car = GetSceneAndCar(car)
    toFPS = Opt.ToFPS()

    # If car has no physics yet, give it physics
    if not getCarPhysics(car):
        InitCarPhysics(car)

    # Doors Swining
    GetPhysicsForces(car)
    SwingDoors(car)
        
    for n, wheel in enumerate(car["wheels"]):

        wheelObject = car["wheels"][n]
        health      = wheelObject["health"] ** 2

        # Damping
        suspension = wheel["suspension"] * health   
        k = car["specs"]["suspentionDamping"]
        suspensionDamping = 2 * k * math.sqrt(suspension)

        
        
        getCarPhysics(car).setSuspensionStiffness(suspension, n)
        getCarPhysics(car).setSuspensionDamping(suspensionDamping, n)
        getCarPhysics(car).setTyreFriction(car["specs"]["grip"]*math.sqrt(car.mass), n)
        getCarPhysics(car).setRollInfluence(car["specs"]["roll"], n)

    # Applying the engine acceleration
    # to the wheels.
    ApplyEngine(car)

    # Making sure to clear the acceleration
    # for the next frame.

    if not car.get("braking"):
        car["driftturn"] = 0.0
    car["accelerating"] = False
    car["braking"]      = False

def getCarPhysics(car):

    # Getting car physics. Car physics
    # is referring to the Vehicle Constraint
    # from the bullet physics. Not the rigid
    # body of the main part of the car.
    
    try:
        return bge.constraints.getVehicleConstraint(car["VehicleConstraintId"])
    except:
        return None
        
def InitCarPhysics(car):
    
    # This part of code runs once per car. It generates the
    # Bullet Physics Vehicle Constraint setup.
    
    # Clear the car and get the scene
    scene, car = GetSceneAndCar(car)
    
    # Get the data about this car
    car["specs"] = GetCarSpecs(car.get("carData", car)).copy()
    
    # Create a constraint
    carPhysics = bge.constraints.createVehicle(car.getPhysicsId())
    car["cid"] = carPhysics.getConstraintId()
    car["VehicleConstraintId"] = carPhysics.getConstraintId()

    
    # Adding wheels
    for n, wheel in enumerate(car["specs"]["wheels"]):
        AddWheelToCar(car, wheel)
        car["wheels"][n].position = carPhysics.getWheelPosition(n)

        
    # Connect doors and other hinged objects
    car["doors"] = []


    
    for obj in car.childrenRecursive:
        if obj.get("door"):
            car["doors"].append(obj)
            obj.suspendPhysics()
            
    
        
    # Init Various things we might need
    
    # Engine things
    car["rpm"]    = float(0.0)
    car["health"] = float(1.0)
    car["nitro"]  = float(0.0)

    car["shake"]  = float(0.0)

    # Set the mass of the car. We divide the mass
    # by 10, since there is a limit of how strong
    # can be car's suspension. UPBGE does not provide
    # a way ( yet ) to override this limit.
    car.mass = car["specs"]["mass"] / 10

    # Create a callback for collisions
    def OnCollisionDo(obj, point, normal, points):
        
        # For readability reason, I put the code
        # for the task itself in a separate funtion
        # below.

        if car["active"] or (InView(car) and Opt.GoodFPS("Collision"),0.6):
            OnCollision(car, obj, point, normal, points)

        # I would love to be able to do a Scheduling instead of simple
        # FPS checking for non player cars. But this results in Seg-Faults
        # because apperately the script tries to read "points" that are
        # freed after the simulation step of the collision is over.
            
    car.collisionCallbacks.append(OnCollisionDo)

    # For trucks
    UnloadCargo(car)

    
    print(consoleForm(car),clr["bold"]+clr["tdrd"],"Physics Initilized.", clr["norm"])
    
def GetCarSpecs(car):
    
    # This function returns the car specs
    # for a given car.
    
    # Clearing the key
    try: key = car["carData"]
    except:
        try: key = car.name
        except: key = str(car)
    
    # Return the data, or some data if there is none.
    if key in carData:
        return carData[key]
    else:
        return carData[list(carData.keys())[0]]
    
def AddWheelToCar(car, wheel):
    
    # This function adds a new wheel.
    
    # Getting the object of the wheel
    wheelname = car.get("wheelname", car.name.replace("Box", "Wheel"))+wheel["object"]
    wheelObject = car.children[wheelname]

    
    
    # Add suspension data to the wheel
    wheelObject["suspension"] = car["specs"]["suspention"]

    # Add health data to the wheel
    wheelObject["health"] = 1.0
    
    # Adding this wheel in to the car data for later
    # use.
    
    if "wheels" not in car:
        car["wheels"] = []
    
    car["wheels"].append(wheelObject)
    
    # Removing the parent of the wheel ( it can mess things up if we don't )
    wheelObject.removeParent()
    wheelObject.suspendPhysics()    
    
    # Adding the wheel to the car physics
    getCarPhysics(car).addWheel(wheelObject,
                                wheel["xyz"],
                                wheel["down"],
                                wheel["axel"],
                                wheel["suspensionRest"],
                                wheel["radius"],
                                wheel["front"])
    
def UserControl(car):
    
    # This function makes user control
    scene, car = GetSceneAndCar(car)

    keys = bge.logic.globalDict["keys"]
    settings = bge.logic.globalDict["settings"]

    if settings.get("veh_mouse"):

        MouseControl(car)

    else:
        # Acceleration
    
        if controls["forward"] in keys:
            Accelerate(car)

        if controls["backward"] in keys:
            Accelerate(car, -1)

        if controls["backward"] not in keys and controls["forward"] not in keys:

            # Making sure you can actually stop the car
            v = -car.localLinearVelocity[1]
            if v < 0: v *= -1
            if v > 5:
                ReleaseBreakes(car)
            else:
                IdleBraking(car)

        # Turning
        
        if controls["right"] in keys:
            TurnRight(car)

        elif controls["left"] in keys:
            TurnLeft(car)
    

    if controls["nitro"] in keys:
        StartNitro(car)
    else:
        StopNitro(car)
        
    

    if controls["drift"]in keys:
        HandBrake(car)

    # Racing
    if controls["resque"]in keys:
        car["abort-resque"] = 0
        ResqueRacer(car)
        
    # Gears
        
    if controls["gearup"] in keys and not car.get("geartoggletimer"):
        GearUp(car)
        car["geartoggletimer"] = 10

    elif controls["geardown"] in keys and not car.get("geartoggletimer"):
        GearDown(car)
        car["geartoggletimer"] = 10

        
    # Toggling automatic transmission on and off with Q
    if controls["autogear"] in keys and not car.get("geartoggletimer"):
        car["autogear"] = not car.get("autogear", True)
        car["geartoggletimer"] = 10

    # Counting down the timer
    if car.get("geartoggletimer"):
        car["geartoggletimer"] -= 1


    #Toggling truck stuff

    if controls["unload"] in keys and not car.get("cargocountdown"):
        ChangeCargo(car)
        car["cargocountdown"] = 100

    if car.get("cargocountdown"):
        car["cargocountdown"] -= 1

    # Toggling dynamic camera
    if controls["dynamcam"] in keys and not car.get("camtoggletimer"):
        car["dynamiccam"] = not car.get("dynamiccam", True)
        car["shake"] = 0.0
        car["camtoggletimer"] = 10

    if car.get("camtoggletimer"):
        car["camtoggletimer"] -= 1
        
        
            
    # User UI
    if car.get("dynamiccam", True):
        DynamicCamera(car)
    else:
        Mouse.MouseLookActual(car.children["CarCamTarget"], True)
        
        
    UpdateTachometer(car)
    UpdateSpeedometer(car)
    UpdateGearUI(car)
    UpdateRedline(car)
    UpdateHealthGauge(car)
    UpdateNitroMeter(car)
    UpdateFixMap(car)

    # Various things that happen only to the user's car
    CloseCall(car)

    # Grabbing control when on multiplayer
    if settings.get("multiplayer"):
        Multiplayer_Client.GrabOwnership(car)

def MouseControl(car):

    # This fucntion will control the car with a mouse.

    
    scene, car = GetSceneAndCar(car)

    # Getting mouse position on the screen.
    mouse = bge.logic.mouse
    pos = list(mouse.position)

        
    # Making sure that the mouse doesn't escape the window
    if pos[0] > 0.9:
        bge.render.setMousePosition(int(bge.render.getWindowWidth()*0.9),
                                    int(bge.render.getWindowHeight() * pos[1]))
    elif pos[0] < 0.1:
        bge.render.setMousePosition(int(bge.render.getWindowWidth() * 0.1),
                                    int(bge.render.getWindowHeight() * pos[1]))

    if pos[1] > 0.9:
        bge.render.setMousePosition(int(bge.render.getWindowWidth() * pos[0]),
                                    int(bge.render.getWindowHeight()*0.9))
    elif pos[1] < 0.1:
        bge.render.setMousePosition(int(bge.render.getWindowWidth() * pos[0]),
                                    int(bge.render.getWindowHeight() * 0.1))
    
    pos[0] -= 0.5
    pos[1] -= 0.5
    pos[0] *= 2.2
    pos[1] *= 2.2

    # Blind spot
    bs = 0.1
    
    if pos[0] > 0: p0 =  1
    else:          p0 = -1
    if pos[1] > 0: p1 =  1
    else:          p1 = -1

    # if pos[0] > bs or pos[0] < -bs:
    #     p = pos[0]
    #     pos[0] = (( (pos[0] * p0) * (1+bs) ) - bs) * p0
       
    # else: pos[0] = 0.0

    a = pos[1]
    t = pos[0]
    
    if pos[1] > bs or pos[1] < -bs:
        
        pos[1] = (( (pos[1] * p1) * (1+bs) ) - bs) * p1
        pos[1] = ( ( ( pos[1] * p1 ) ** 3 ) * p1)
        Accelerate(car, -pos[1] , mouse=True)
    else:
        pos[1] = 0.0
        v = -car.localLinearVelocity[1]
        if v < 0: v *= -1
        if v > 5:
            ReleaseBreakes(car)
        else:
            IdleBraking(car)

    
    car["turn"] = -( ( ( pos[0] * p0 ) ** 3 ) * p0)

    # UI indication
    turnpiece = scene.objects["Mouse_Turn_UI_Piece"]
    turnframe = scene.objects["Mouse_Turn_UI"]
    m = 0.062012 * turnpiece.worldScale[0]
    turnpiece.worldPosition = RelativePoint(turnframe,
                                       (m*t,0,0)
                                       )
    accelpiece = scene.objects["Mouse_Accelation_UI_Piece"]
    accelframe = scene.objects["Mouse_Accelation_UI"]

    m = 0.062012 * accelpiece.worldScale[0]
    accelpiece.worldPosition = RelativePoint(accelframe,
                                       (m*a,0,0)
                                        )
                                        
def Accelerate(car, factor=1, mouse=False):
    
    # This function accelerates the car

    # When changing direction we want it to act like
    # brakes.
    v = -car.localLinearVelocity[1]
    if v * factor < -0.5:

        # Activating breaks
        ActivateBreaks(car, factor, True)
        
    else:

        # Reverting to NOT breaking.
        ReleaseBreakes(car)

        # Reving the engine, and telling the code that
        # we are accelerating now.

        Rev(car, factor, mouse)
        car["accelerating"] = True
        
def ActivateBreaks(car, factor=1, stable=False):

    # Activating brakes.
    
    Abs  = ABS(car)
    turn = Turning(car)
    for n, wheel in enumerate(car["specs"]["wheels"]):

        wheelObject = car["wheels"][n]
        health      = wheelObject["health"] ** 5

        # If the car moves forward stopping only with back
        # wheels so it will not fly into the air.
        if ( factor < 0 and not wheel["front"] ) or ( factor > 0 and wheel["front"] ):
            getCarPhysics(car).applyBraking(Abs*health, n)
        else:
            getCarPhysics(car).applyBraking(0, n)
            
    # Stablizing the brakes
    if stable:
        r = car.localAngularVelocity[2]
        car.applyTorque([0,0,-r*car.mass*20], True)
            
    car["braking"] = True
    
def ReleaseBreakes(car):

    for n, wheel in enumerate(car["specs"]["wheels"]):
            getCarPhysics(car).applyBraking(0, n)

            
def StartNitro(car):

    scene, car = GetSceneAndCar(car)
    
    # Skipping
    if not car.get("active") and not Opt.GoodFPS("Nitro", 0.9) or not car.get("NitroCan") :
        return

    # Nitro Ended
    if car.get("nitro", 0) < 0.04:
        StopNitro(car)
        RemoveNitroCan(car)
        car["nitro"] = 0.0
        return

    
    for nitroObject in car.children:
        if "nitro" in nitroObject.name:            
            if not nitroObject.get("nitroCone"):
                nitroCone = Reuse.Create("NitroCone")
                nitroObject["nitroCone"] = nitroCone                
                nitroCone.position = nitroObject.worldPosition 
                nitroCone.orientation = nitroObject.worldOrientation
                nitroCone.setParent(nitroObject)

    if car.get("active"):
        cam = scene.active_camera
        if cam.lens > 15:
            cam.lens -= 0.3
                
    NitroSound(car)
    ReduceNitro(car, 0.015)
    

    # Fake push on the car forward
    car.applyForce([0,-30*car.mass, 0], True)
    
    car["nitroing"] = True

def StopNitro(car):

    scene, car = GetSceneAndCar(car)

    for nitroObject in car.children:
        if "nitro" in nitroObject.name:
            if nitroObject.get("nitroCone"):
                nitroObject["nitroCone"].removeParent()
                Reuse.Delete(nitroObject["nitroCone"])
                nitroObject["nitroCone"] = None

    if car.get("active"):
        cam = scene.active_camera
        if cam.lens < 20:
            cam.lens += 0.3
                
    NitroSoundStop(car)
                
    car["nitroing"] = False

def AddNitro(car, amount):

    # Deprecated
    return
    if amount < 0: amount *= -1
    car["nitro"] = max(0, car.get("nitro", 0) + amount)

def ReduceNitro(car, amount):

    if amount < 0: amount *= -1
    car["nitro"] = min(car.get("nitro", 0) - amount, 10)

def AddNitroCan(car):
    
    if "NitroCanProxy" not in car.childrenRecursive:
        return

    
    nitrocan   = Reuse.Create("NitroCan")
    nitroproxy = car.childrenRecursive["NitroCanProxy"]

    nitrocan.position = nitroproxy.position
    nitrocan.worldOrientation = nitroproxy.worldOrientation
    nitrocan.setParent(nitroproxy)
    car["NitroCan"] = True
    car["NitroCanObject"] = nitrocan

def RemoveNitroCan(car):

    if not car.get("NitroCan"):
        return

    
    nitrocan = car["NitroCanObject"]
    nitrocan.removeParent()
    Reuse.Delete(nitrocan)
    car["nitro"] = 0.0
    car["NitroCan"] = False


def AddSpoiler(car, spoilertype=None):

    if "SpoilerProxy" not in car.childrenRecursive:
        return

    if not spoilertype:
        spoilers = []
        for name in Garage.shop:
            usemodel  = Garage.shop[name].get("usemodel")
            if "Spoiler" in name and usemodel:
                spoilers.append(usemodel)
        spoilertype = random.choice(spoilers)
                
    

    spoiler = Reuse.Create(spoilertype)
    spoilerproxy = car.childrenRecursive["SpoilerProxy"]

    spoiler.position = spoilerproxy.position
    spoiler.worldOrientation = spoilerproxy.worldOrientation
    spoiler.scaling = spoilerproxy.scaling
    spoiler.setParent(spoilerproxy)
    ColorPart(car, spoiler)
    car["Spoiler"] = spoilertype
    car["SpoilerObject"] = spoiler

def RemoveSpoiler(car):

    if not car.get("Spoiler"):
        return

    spoilertype = car["Spoiler"]
    spoiler = car["SpoilerObject"]
    spoiler.removeParent()
    Reuse.Delete(spoiler)
    car["Spoiler"] = None
    car["SpoilerObject"] = None
    return spoilertype

def GetTunnedEffects(car):

    # This function will return a sum total of all effects added
    # to the car.

    effects = {}

    names = []
    if car.get("Spoiler"):
        for name in Garage.shop:
            item = Garage.shop[name]
            if item.get("usemodel") == car["Spoiler"]:
                names.append(name)

    for name in names:
        item = Garage.shop.get(name, {})
        effe = item.get("effects", {})
        for e in effe:
            effect = effe[e]
            effects[e] = effects.get(e,1) * effect

    return effects

def UpdateNitroMeter(car):

    # This function shows nitro amount
    # for the user.

    scene, car = GetSceneAndCar(car)
    
    nitrometer = scene.objects["Gage_Nitro"]
    nitrovalue = car.get("nitro", 0) / 10 * 100
    nitrometer["Nitro"] = nitrovalue

    
def ABS(car):

    # This function implements a basic
    # Anti-lock Braking System. And
    # returns a braking value, to be
    # used in the Bullet's Car's Braking
    # function.

    # Due to it being a game and not a
    # perfect simulation of car dynamics
    # it will not be your typical ABS
    # but rather something that acts similarly.

    brakes = car["specs"]["brakes"]
    cb = car.mass * brakes

    # Here is the magic. The strong the "abs" value
    # for a specific car, the more damping will any
    # sudden burst of rotation on Z axis recieve.
    # Basically, higher "abs" = lower drifting.
    
    strength = car["specs"]["abs"]
    car.localAngularVelocity[2] *= Turning(car) ** strength
    
    return cb

def IdleBraking(car):

    # When car is just standing still
    # and nobody is driving it, we
    # want to car to keep standing where
    # it is standing and not roll down hills
    # and stuff. For this we activate breaks.

    Abs  = ABS(car)
    for n, wheel in enumerate(car["specs"]["wheels"]):
        getCarPhysics(car).applyBraking(Abs, n)
    
def GearUp(car):

    # First we gear up.
    oldgear     = car.get("gear", 0)
    gears       = car["specs"]["gears"]
    car["gear"] = min( len( gears ) - 1 , car.get("gear", 0) + 1 )

    # Then we calculate how much to change the RPM
    car["rpm"] *= gears[car["gear"]] / gears[oldgear]
    GearShiftSound(car)
    
def GearDown(car):

    # Same as GearUP() but in reverse.
    
    oldgear     = car.get("gear", 0)
    gears       = car["specs"]["gears"]
    car["gear"] = max( 0 , car.get("gear", 0) - 1 )
    car["rpm"] *= gears[car["gear"]] / gears[oldgear]
    GearShiftSound(car)
    
def GetGearRatio(car):

    # Gets a ratio between engine
    # speed and wheel speed based on
    # the current selected gear.
    
    gear = car.get("gear", 0)
    gearRatio = car["specs"]["gears"][gear]
    return gearRatio
    
def AutomaticTransmission(car):

    # This function automatically shifts
    # gears.

    idle     = car["specs"]["idle"]
    redline  = car["specs"]["redline"]
    gears    = car["specs"]["gears"]

    # Gearing up when reaching the redline
    # We add a bit to the rpm to avoid damage
    # to the engine.
    if car["rpm"]*1.1 >= redline and car.get("gear",0)+1 != len( gears ):
        GearUp(car)

    # Gearing down when reaching idle
    elif car["rpm"] < idle and car.get("gear"):
        GearDown(car)

    # Reverse gear is gear 1 and if we have too much resistance
    # also gear 1.    
    if car["rpm"] <= 0 or EngineResistance(car) < 0.1:
        car["gear"] = 0
    
def Rev(car, factor=1, mouse=False):

    # This function simulates the acceleration of
    # the internals of the engine recorded in
    # car["rpm"]. 
    
    torque = Torque(car)
    radius = car["specs"]["wheels"][0]["radius"]
    mass   = car["specs"]["mass"]
    maxrpm = car["specs"]["maxrpm"]
    
    # Get how many RPM there will be in 1 second
    force = torque / radius
    
    resistance = EngineResistance(car)
        
    accel = Opt.Force(force) / max( 0.1, ( mass / GetGearRatio(car) ) ) * resistance
    addrot = accel / ( math.pi * 2 )
    addRPM = addrot * 60
    
    # Get how many RPM there will be in 1 frame
    addRPM /= bge.logic.getAverageFrameRate()

    # Add idle RPM. Starting engine from the idle
    # instead of from 0.
    withIdle = RPMWithIdle(car, factor) 

    # Calculating everything.
    if withIdle < 0: flip = -1
    else           : flip =  1

    
    if not mouse:
        car["rpm"] = ( ( math.sqrt( withIdle * flip) * flip ) + (addRPM * factor) ) ** 2 * factor
    elif factor < 0 and car["rpm"] > factor * maxrpm:
        car["rpm"] = ( ( math.sqrt( withIdle * flip) * flip ) - (addRPM) ) ** 2 *-1
    elif factor > 0 and car["rpm"] < factor * maxrpm:
        car["rpm"] = ( ( math.sqrt( withIdle * flip) * flip ) + (addRPM) ) ** 2 
    
    
    # Making sure not to exceed the maximum RPM
    car["rpm"] = min( maxrpm, car["rpm"])
    car["rpm"] = max(-maxrpm, car["rpm"])

    # If revs are above the redline add damage to the engine
    redline = car["specs"]["redline"]
    factor  = ( car["rpm"] - redline ) / ( maxrpm - redline ) * int( car["rpm"] > redline )
    car["health"] = max(0, car.get("health", 1) - ( factor / 1000 ))
    if factor:
        RedlineSound(car, volume=min(3, factor*10))

    # Making sure to set 0th gear at negative RPM
    if car["rpm"] < 0: car["gear"] = 0
    
def EngineResistance(car):

    # This function simulates the resistance
    # ground and various obsticles on the engine
    # of the car. If you push against the wall,
    # this function will prevent the engine from
    # reving hard. Also it is used to make it
    # nearly impossible to start the car from a
    # high gear. 

    gear = GetGearRatio(car)
    if not car.get("gear"):
        return 1
    
    # First we need to calculate expected speed
    radius = car["specs"]["wheels"][0]["radius"]
    ev     = car["rpm"] / gear / 60 * radius

    # Then we get the real value
    v = -car.localLinearVelocity[1]

    # Then return the fraction
    try   : return min(1, v / ev )
    except: return 0 # If the expected velocity is 0, we return 0.

def RPMWithIdle(car, factor=None, normalize=False):

    # Returns RPM of the car with Idle RPM present.

    # Sometimes the factor should be specified manually.
    if factor == None:
        if car["rpm"] < 0: factor = -1
        else             : factor =  1

    # Making the idle
    idle = car["specs"]["idle"] * factor
    if factor < 0: withIdle = min(idle, car["rpm"])
    else         : withIdle = max(idle, car["rpm"])

    # Sometimes we want to normalize it ( like for sound ).
    if normalize and withIdle < 0: withIdle *= -1
    
    return withIdle
    
def HorsePower(car):

    # This function calculates estimated
    # horse power given the current RPM
    # of the engine.

    # The approximation is done using a Sinewave
    # from values of 0 to 3 forths of Pi.
    
    rpm    = car["rpm"]
    maxrpm = car["specs"]["maxrpm"]
    horses = car["specs"]["hp"]

    # Nitro
    if car.get("nitroing"):
        return horses * 10
    
    return math.sin(rpm/maxrpm*(math.pi/4*3)) * horses

def Torque(car):

    # This function calculates torque of
    # the car. Based on an estimate of
    # a torque curve.

    rpm    = car["rpm"]
    maxrpm = car["specs"]["maxrpm"]
    torque = car["specs"]["torque"]
    health = car.get("health", 1)

    # Nitro
    if car.get("nitroing"):
        return torque * 10
    
    return math.sin((rpm/maxrpm*(math.pi/4*3))+math.pi/4) * torque * health

def HorsePowerToWatts(value):
    return value * 745.6998715823

def AirDencity(car, ignoreWater=False):

    # Air density function will return
    # the density of air at various elevations
    # including the dencity of water under water.

    # Water is a bit below the center of the game map.
    waterlevel = -9.5

    if car.position[2] > waterlevel or not car.get("underwater") or ignoreWater:
    
        # We will aproximate the air dencity due to elevation
        # with a simple sine wave. Since the graph looks a bit
        # like the sine way between the values of pi and pi + half a pi.
        # Or in the case of this code, flipped sine from 0 to half a pi.
        
        maxelevation = 10000 # Roughly where the air is least dense
        mindensity   = 0.023 # Roughly the density at mount everest
        maxdensity   = 1.2   # Roughly density of air at sea level

        heightproxy   = 1 - ( ( car.position[2] - waterlevel ) / maxelevation )
        density       = math.sin( math.pi / 2 * heightproxy ) * maxdensity
        density       = min(maxdensity, density)
        
        # Making sure to check that the car is not underwater anymore
        car["underwater"] = False
        
        return density

    else:

        # Completerly faking water density.
        
        return max(2, min( 20, ( car.position[2] - waterlevel ) * -10 ))

def WaterBoyancy(car):

    # This function moves car upward,
    # if the car is underwater.
    # See DownForce() for where it is
    # running.

    g = -9.8
    m = car.mass
    v = 0.9 # Less then 1 so car could sink, but slowly.

    return g * m * v - DragForce(car)
    
def DragForce(car):

    effects = GetTunnedEffects(car)
    cd = car["specs"]["drag"]
    cd = cd * effects.get("drag", 1)

    p  = AirDencity(car)
    v = -car.localLinearVelocity[1]
    A = 1.5 # Frontal area of the car. I estimate it here.

    if v > 0: flip = 1
    else    : flip = -1
    
    return (cd / 2 * p * ( v ** 2 ) * A ) * flip

def DownForce(car):

    if not car.get("underwater"):

        effects = GetTunnedEffects(car)
        cl = car["specs"]["downforce"]
        cl = cl * effects.get("downforce", 1)
        
        p = AirDencity(car, ignoreWater=True)
        v = -car.localLinearVelocity[1]
        A = 0.1 # Based on nothing

        df = cl * p * ( v ** 2 ) * A

        # making sure that the car will not derail

        # Distance to the ground when suspension is holding it in place
        wp  = car["specs"]["wheels"][0]["xyz"][2]
        wr  = car["specs"]["wheels"][0]["radius"]
        dtg = wp-wr-0.2

        down = RelativePoint(car, [0,0,dtg])
        hit = car.rayCastTo(down)

        # is the car is on the ground, limit downforce
        # to the force of the suspension. 
        if hit:
            df = min(car["specs"]["suspention"]/2, df)
            
        return df
    
    else:
        return WaterBoyancy(car)
    
def ApplyEngine(car):

    toFPS = Opt.ToFPS()
    
    hp = HorsePower(car)
    force = HorsePowerToWatts(hp) / 100
    grip = car["specs"]["grip"]

    # Here is a little cheat to help the car start
    resistance = EngineResistance(car)
    if car.get("gear",0) == 0:
        resistance = 1
    
    for n, wheel in enumerate(car["specs"]["wheels"]):

        wheelObject = car["wheels"][n]
        health      = wheelObject["health"] ** 2

        rightwheel = ( wheel["front"] and force > 1 ) or ( not wheel["front"] and force < 1 )
        
        if rightwheel and not car.get("braking"):
            getCarPhysics(car).applyEngineForce(force * health * resistance / GetGearRatio(car) / grip, n)
        else:
            getCarPhysics(car).applyEngineForce(0, n)
    

    # Decrease RPMS
    if not car.get("accelerating"):
        car["rpm"] *= 0.98 * EngineResistance(car)
   
def ApplyTurn(car):

    # This function applies turn
    
    toFPS = Opt.ToFPS()
    
    # Making sure the car is not turning more than it is possible.

    maxturn = car["specs"]["maxturn"]
    car["turn"] = max(-maxturn,
                      min(maxturn,
                          car["turn"]
                          )
                      )
    
    for n, wheel in enumerate(car["specs"]["wheels"]):

        wheelObject = car["wheels"][n]
        health      = wheelObject["health"] ** 2
        
        if wheel["front"]:
            getCarPhysics(car).setSteeringValue(car["turn"]*health, n)
        else:
            getCarPhysics(car).setSteeringValue(0, n)

    # Fake turn Aid
    vo = car.localLinearVelocity[1]
    v = vo
    if v < 0: v *= -1

    if v > 0.01 and vo < 0:
        car.applyTorque([0,0,car["turn"] * 20 * car.mass],True)
    elif v > 0.01 and vo > 0:
        car.applyTorque([0,0,car["turn"] * -20 * car.mass],True)
        

    # Auto going straiter
    if not car.get("turning"):
        if car["turn"] < 0:
            car["turn"] += 0.05 * toFPS
            if car["turn"] > 0:
                car["turn"] = 0
        else:
            car["turn"] -= 0.05 * toFPS
            if car["turn"] < 0:
                car["turn"] = 0
                
    car["turning"] = False
    
def TurnRight(car):
    
    toFPS = Opt.ToFPS()
    
    if car["turn"] > 0: car["turn"] *= -0.5
    car["turn"] -= 0.005 * toFPS
    car["turning"]= True

def TurnLeft(car):
    
    toFPS = Opt.ToFPS()
    
    if car["turn"] < 0: car["turn"] *= -0.5
    car["turn"] += 0.005 * toFPS
    car["turning"]= True

def HandBrake(car):

    # This is an implementation of
    # a fake handbrake. It is fake
    # since normal drifting could
    # be technically simulated using
    # the physics engine. But it is
    # a very delicate thing that results
    # in borderline undrivable cars.
    # So instead there is this function
    # that is used instead to approximate
    # the behaviour of the drift.

    factor = car.localLinearVelocity[1]
    ActivateBreaks(car, factor)

    
    # Recording the turn at which the drift started.
    # And using it to add a fake torque to the car.
    # which will simulate drifting experience a bit
    # more accurately.

    dt = car.get("driftturn",0)
    if dt < 0: dt *= -1

    # If turn increased in the same direction as the drift turn, we increase drift turn
    if dt > Turning(car) and ( ( car.get("driftturn",0 ) < 0 ) == ( car["turn"] < 0 ) ):
        car["driftturn"] = float(car["turn"])
        
    # Adding fake turn to the car
    car.applyTorque([-car.mass*5,
                     0,
                     car["driftturn"]*car.mass*10
                     ], True)
            
def Turning(car):

    # This funtion returns the fraction
    # of how far the front wheels have
    # been turned for turning the car.

    t = car["turn"]
    if t < 0: t *= -1
    maxturn = car["specs"]["maxturn"]

    return t / maxturn
    
def UpSideDown(car):

    rot = car.localOrientation.to_euler()
    upsideDown = not ( -2.5 < rot[0] < 2.5 and -2.5 < rot[1] < 2.5 )
    return upsideDown
    
def DynamicCamera(car):

    # This is a function that moves the camera
    # in a dynamic way.

    scene, car = GetSceneAndCar(car)

    
    toFPS = Opt.ToFPS()

    cam = scene.active_camera
    camtarget = car.children["CarCamTarget"]

    onground = OnGround(car)
    someanim = bge.logic.globalDict.get("CameraTargetChange")

    
    
    
    spin = GetSpin(car)

    dontseecar = cam.rayCastTo(car) not in [car]+list(car.childrenRecursive)

    # The effect of showing the car if
    # the car went up side down.

    upsidedown = UpSideDown(car) 
    
    # if upsidedown and dontseecar:
    #     camrot = camtarget.localOrientation.to_euler()
    #     camrot[0] -= 0.05
    #     camtarget.localOrientation= camrot

    #if upsidedown and spin < 1:
    #    return # To cancel the rest of the effect

    ########### AIR CAMERA DISCONNECT EFFECT ##########
    
    if not onground and not someanim:
        Character_Controll.ChangeCameraTarget(camtarget,
                                              0,
                                              rotate=False)
        car["return-cam"] = True

    
    if onground and car.get("return-cam"):
        
        car["return-cam"] = False
        
        Character_Controll.ChangeCameraTarget(camtarget,
                                              30,
                                              rotate=True)
        car["zoom"] = 2
        return

    #######################################################

    
    # The effect is zooming in when something
    # covers up the car.
        
    # if dontseecar and camtarget.scaling[0] > 0.1:
    #     camtarget.scaling *= 0.97

    # elif camtarget.scaling[0] <= 0.8 and scene.objects["SecondaryCamSensor"].rayCastTo(car) in [car]+list(car.childrenRecursive):
    #     camtarget.scaling *= 1.1

    # if camtarget.scaling[0] < 0.1:
    #     camtarget.scaling = [0.1, 0.1, 0.1]
    # if camtarget.scaling[0] > 2:
    #     camtarget.scaling = [2, 2, 2]
        
    # Then we ar going to rotate the camera around
    # the car ( using a parenting point in the middle
    # of the car ) based on various momentums of
    # the car.

    camrot = mathutils.Vector(camtarget.localOrientation.to_euler())
                
    reference = car.getVectTo(car.position + car.getLinearVelocity())[2]
    rotref = car.localAngularVelocity

    backorfoth = ((reference[1]+1)/2)
    if backorfoth > 0.6: backorforth = 1
    else: backorforth = 0

    if camtarget["back"] and backorforth: backorforth = 0
    elif camtarget["back"]: backorforth = 1

    camnewrot = mathutils.Vector( [ 0.3 - max(-0.1, min(0.2, rotref[0]/2)),# - (car["engine"]*0.005),
                                    0 - (rotref[2]/10) - (rotref[1]/6),
                                    ( math.pi * backorforth ) - (rotref[2]/4)] )

    # If the car spins too hard, we want to offset it.
    camnewrot -= rotref / 10 * min(1,spin)
    
    if backorforth and camrot[2] < 0:
        camnewrot[2] -= math.pi * 2

    camnewrot = mathutils.Vector(camnewrot)
    camrot = camrot + (( camnewrot - camrot ) / 10 )     
    camtarget.localOrientation = camrot


    # Shake ( shift ) from speed and collisions
    v = car.localLinearVelocity[1]
    if v < 0: v *= -1
    v = max(0, v - 15)
    v = v / 20000

    if car.get("shake", 0) > 0.01:
        v += car["shake"] / 60
        car["shake"] *= 0.92
    
    cam.shift_x = random.uniform(0,v)
    cam.shift_y = random.uniform(0,v)

def UpdateTachometer(car):

    # This function shows engine RPMs
    # for the user.

    scene, car = GetSceneAndCar(car)
    
    tachometer = scene.objects["Gage_Tachometer"]
    rpmvalue = RPMWithIdle(car, normalize=True) / car["specs"]["maxrpm"] * 100
    tachometer["Health"] = rpmvalue

def UpdateHealthGauge(car):

    # This function shows engine RPMs
    # for the user.

    scene, car = GetSceneAndCar(car)
    
    tachometer = scene.objects["Gage_Health"]
    tachometer["Health"] = car.get("health", 1) * 100

def UpdateSpeedometer(car):

    # This function shows car speed
    # to the user.
    
    scene, car = GetSceneAndCar(car)
    
    kmh = GetSpeed(car)

    speedometer = scene.objects["Gage_Arrow"]

    # Speedometer in game shows between 0 and 400 kmh
    # and the whole rotation takes -245 degrees or
    # -4.276 radians.

    whole = -4.276

    # And with that we can apply the rotation of the gauage.
    rotation = kmh / 400 * whole
    speedometer.orientation = [0,0, rotation]

def UpdateRedline(car):

    # This function updates redline.

    scene, car = GetSceneAndCar(car)

    
    redline = car["specs"]["redline"]
    arrow   = scene.objects["Gage_Redline"]
    maxrpm  = car["specs"]["maxrpm"]
    whole   = -4.276  # See UpdateSpeedometer
    
    rotation = redline / maxrpm * whole
    arrow.orientation = [0,0,rotation]
    
def UpdateGearUI(car):

    # This function shows to the user
    # in what gear the car is right now.

    scene, car = GetSceneAndCar(car)

    # Getting gear number
    gear = car.get("gear",0) + 1 # First gear is 0 in the system

    # Reverse gear
    if car["rpm"] < 0:
        gear = "R"
        
    scene.objects["Gage_Gear"]["Text"] = str(gear)

def UpdateFixMap(car):

    # This shows the fix icon on the map if
    # the car is broken

    if car.get("health", 1.0) < 1.0:

        Map.Show(fixGaragePosition, icon="Map_Fix", color=[0.8,0.8,0.8])
         
def GetSpeed(car, real=False):
        
    # When car moves forward, it is moving
    # backwards in the engine
    v = -car.localLinearVelocity[1]

    # Now we can find kmh
    kmh = v / 3.6

    # But in the actuall game this real value feels
    # too small compared to the mayhem that is happening
    # in the game. So we quadrupple the speed, thus
    # 25 KM/H will show up as 100 KM/H to the user.

    kmh *= 4
    
    # Sometimes we need the real speed ( as in
    # when we go backwards, the speed will be < 0 ).
    # But sometimes we just want to know the speed
    # no matter the direction.

    if not real and kmh < 0:
        kmh *= -1

    return kmh

def GetSpin(car):

    # This function returns average
    # normalized angular velocity.
    # Basically, how hard does the car
    # spin in any direction.

    # Getting angular velocity
    xyz = car.getAngularVelocity()

    # Normalizing it
    for n, i in enumerate(xyz):
        if i < 0:
            xyz[n] *= -1

    # Averaging it
    av = sum(xyz) / 3

    # Returning it
    return av

def RelativePoint(car, point, rotation=None):

    # This will return any relative point
    # in relation to the car in 3D space.

    # First we want to convert the point to
    # a vector.
    point = mathutils.Vector(point)

    # And get the car's rotation in eulers.
    if not rotation:
        rotation = car.orientation.to_euler()
        
    # Then we rotate the point by the car's rotation
    # using, the blessing, that is the mathutils
    # module.
    point.rotate(rotation)

    # And finally we move the point to the car's
    # world location.
    point = point + car.worldPosition

    # Returning
    return point

def OnGround(car):

    # Returns whether the car
    # in on the ground or not.

    # Getting the relative position of the ground
    down = RelativePoint(car, [0,0,-1])

    # See if there is grown under the car
    ray = car.rayCast(down, poly=True)

    # If nothing is sensed, we return None
    if not ray[1]:
        return None

    # Else we return the material of the object underneith
    # Useful to see on what kind of ground the car is.
    else:
        try: return ray[3].material
        # If there is no material data it will game the name
        # of the object
        except: return ray[0].name

def GroundPuffsOfSmoke(car):

    # This will puff smoke from wheels
    # if driving on anything with "ground" in
    # it's name.

    if "ground" in str(OnGround(car)).lower():
        speed = min(1, GetSpeed(car) / 30)
        for wheel in car["wheels"]:
            pos = wheel.position.copy()
            Destruction.Dust(pos, 0.05*speed)

def CloseToCamera(car, d=50):

    cam      = bge.logic.getCurrentScene().active_camera
    distance = car.getDistanceTo(cam)

    return distance < d
    
def OnCollision(car, obj, point, normal, points):

    # This function is responsible for
    # breaking the car when it hits things.

    #print("--------", car, obj)
    
    if not CloseToCamera(car):
        return
    
    # We have a set of objects called separators
    # for bots to see the middle of the road.
    # we want to ignore those for this calculation.
    if "Separation" in str(obj) or "Dani" in str(obj):
        return

    if car.get("abort-resque", 0) > 10:
        car["abort-resque"] -= 10

        
    # To find whether we want to do sparkles or
    # dust, we need to find which material we
    # collide with. Unfortunately there is no
    # simple way to do it. But rather we will
    # need to cast a ray toward the point.

    # First we fire the ray
    ray = car.rayCast(point,              # Adding a bit to the distance
                      dist=car.getDistanceTo(point)+0.01, # to insure that
                      poly=True)          # it will actually hit something.

    # And then we extract the material from this operation.
    if ray[3]: material = ray[3].material
    else     : material = ""


    # For when we collide with water, we want to
    # make it so the car will behave underwaterly.
    if "water" in str(material).lower():

        # Setting car as underwater
        if not car.get("underwater"):
            car["underwater"] = True
            if car.get("driver"):
                car["driver"]["underwater"] = True

        # Splashing
        if not car.get("splashtimer"):
            z = car.getLinearVelocity()[2]
            y = car.localLinearVelocity[2]
            if z < 0: z *= -1
            if y < 0: y *= -1
            force = z + y
            Destruction.WaterSplash(car.position.copy(),
                                    1,
                                    car.orientation.to_euler()[2],
                                    force)
            car["splashtimer"] = 10
        else:
            car["splashtimer"] -= 1 
        return

        
    # If the car already blew up we don't want
    # to continue breaking it
    if car.get("blown"):
        return

    v = -car.localLinearVelocity[1]

    # Trying to optimize the colisions a bit
    if not car.get("active") and not obj.get("active") and not Opt.GoodFPS("NPC Collision", 0.8):
        return
    elif not Opt.GoodFPS("Active Collision", 0.5) and v < 20:
       return
    

    
    # Making sure not to count close calls if you
    # actually hit something.
    
    bge.logic.globalDict["closeCallIgnore"] = True
    
    # Now for every detected collision at the moment.
    for point in [points[0]]:

        force    = point.appliedImpulse / car.mass

        if UpSideDown(car) and not OnGround(car):
            force = 3
        
        # Under a low enough value we ignore everything
        if force < 0.1:

            force = 0.5
            
            # Camera shake
            car["shake"] = max(car.get("shake",0), force)

            
            # Nitro
            #AddNitro(car, force/20)
            
            return
        else:

            # If we colide with ground, do dust
            if "ground" in str(material).lower():
                Destruction.Dust(point.worldPoint,
                                 0.05 * min(1, force/20))
            else: # do particles.
                Destruction.particles("Sparkle", point.worldPoint, 
                                      despawn = random.randint(30, 100),
                                      amount = random.randint(0, 3),
                                      max = 50,
                                      spread = 0,
                                      normal = 0.04 * min(1, force),
                                      velocity = car.getLinearVelocity() * 0.015)

            car["health"] = max(0, car["health"] - ( force / 100 ))

                    
        # Scrape sound
        if force < 2:
            ScrapeSound(car, position=point.worldPoint, volume=max(1,min(3,force)))
        else:
            HitSound(car, position=point.worldPoint, volume=min(3,force/20))

        # For each wheel, we are going to specify health as well
        # so that the car to loose handling as well as engine power
        # due to breaking.

        for n, wheel in enumerate(car["specs"]["wheels"]):

            wheelObject = car["wheels"][n]
            if  ( wheel["xyz"][0] < 0 ) == ( point.localPointA[0] < 0 ) \
            and ( wheel["xyz"][1] < 0 ) == ( point.localPointA[1] < 0 ) :
                wheelObject["health"] = max(0, wheelObject["health"] - ( force / 100 ))

            # Popping the wheel.

            # Disconneting whe wheel from the car is impossible with the
            # current implementation of the Vehicle constraint. But we
            # can do the next best thing. Hiding it and spawing a new
            # wheel in it's stead.

            if wheelObject["health"] < 0.1 and wheelObject.visible:

                wheelObject.visible  = False

                newWheel             = Reuse.Create(wheelObject.name, 500)
                newWheel.position    = wheelObject.position
                newWheel.orientation = wheelObject.orientation
                newWheel.scaling     = wheelObject.scaling
                ColorPart(car, newWheel)

                # Now a little physics cheat to make this pop
                # more exciting! Let the wheel have the same
                # initial velocity as the car.

                newWheel.worldLinearVelocity = car.worldLinearVelocity

                # And of course a Poping sound

                WheelPopSound(car, newWheel.position)

        # Opening and destroying doors
        if force > 0.5:
            closestDistance = 100
            for door in car["doors"]:
                distance = door.getDistanceTo(point.worldPoint)
                if distance < closestDistance:
                    closestDistance = distance
                    thedoor = door

            # Opening the door
            try:
                thedoor["locked"] = False
                thedoor["health"] = max(0, thedoor["health"] - ( force / 20 ))
                if "Borked" in thedoor:
                    thedoor.replaceMesh(thedoor["Borked"])
                    GlassSound(car, position=car.position, volume=3)
            except:
                pass

                
    # Consequences to the car
    if car["health"] < 0.3:
        ChangeBody(car, good=False)
        RemoveSpoiler(car)

    # Explosion
    if car["health"] < 0.03 and not car.get("blown"):
        Destruction.Explosion(point.worldPoint, mass=50, size=30)
        car["blown"] = True

        RemoveNitroCan(car)
        RemoveFireBall(car)

        # Push everybody out of the car
        if car.get("driver"):
            character = car["driver"]
            Character_Controll.getOutCar(character)
            car.worldLinearVelocity  = [0,0,10]
            car.localAngularVelocity = [10,0,0]
            
            
        for p in car.get("passangers", []):
            Character_Controll.getOutCar(p)
            
        # Detatch all wheels.
        for n, wheel in enumerate(car["specs"]["wheels"]):
            # Detatching all wheels:
            wheelObject = car["wheels"][n]
            wheelObject.visible  = False
            wheelObject["health"] = 0
            newWheel             = Reuse.Create(wheelObject.name, 500)
            newWheel.position    = wheelObject.position
            newWheel.orientation = wheelObject.orientation
            newWheel.scaling     = wheelObject.scaling
            ColorPart(car, newWheel)
            WheelPopSound(car, newWheel.position)

        # Detach all doors
        for door in car.get("doors",[]):
            door["health"] = 0
        
    # Camera shake
    car["shake"] = car.get("shake",0) + force
    car["crash"] = force

    # Nitro
    #AddNitro(car, force/20)

    # Angry Pursuit
    if car.get("npc") == "npc" and obj in bge.logic.globalDict["allcars"]\
       and obj.get("target") != car:
        car["target"] = obj
        car["npc"] = "pursuit"

        # Voice
        StartEnemySounds(car)
        
    elif car.get("npc") == "pursuit" and obj == car.get("target"):

        HitEnemySound(car)

def Material(car, value=(0,1,0), attribute="MainColor"):

    # This function edits car's attributes in the blender space
    # mainly to edit the parameters of materials.

    if "colors" not in car:
        car["colors"] = {}
        
    car["colors"][attribute] = value
    
    # Getting objects
    for obj in car.childrenRecursive:
        ColorPart(car, obj)
        
    for wheel in car["wheels"]:
        ColorPart(car, wheel)

    # Neon
    if attribute == "SecondaryColor":

        if "Neon" not in car:
            light = Reuse.Create("NeonLamp")
            light.blenderObject.data = light.blenderObject.data.copy()
            light.position = car.position
            light.setParent(car)
            car["Neon"] = light

        light = car["Neon"]
        light.blenderObject.data.color = value
        light.blenderObject.data.use_shadow = False

def SmartColor(car, color=None):
    
    # Coloring the car. This is not easy
    # since we have multiple possible attributes.

    # If there is nothing passed into the color.
    # we choose the default color for the car.
    if color == None:
        for attribute in car["specs"].get("material", []):
            value = car["specs"]["material"][attribute]
            Material(car, value, attribute)

    elif color == "random":
        for attribute in car["specs"].get("material", []):
            value = (random.random(),
                     random.random(),
                     random.random())
            Material(car, value, attribute)

    elif color == "pallete":

        # This is a bit more comlex to make the colors
        # look good with one another. But this option
        # is trying to do just that.

        maincol   = mathutils.Color((0.5,0.5,0.5))
        secondcol = mathutils.Color((0.5,0.5,0.5))

        # We gonna select whether it is low or high
        # saturation first.
        
        maincol.s = numpy.random.choice([0, 1], p=[0.6,0.4])
        
        # Let's deal with gray scale first
        if not maincol.s:

            # We don't ever want the car to be white
            maincol.v = random.choice([0.001,0.65])

            # If it is closer to black we want to select a
            # strong color for the secondary. Otherwise secondary
            # is white.
            if maincol.v < 0.2:
                secondcol.s = 1
                secondcol.v = 1
                secondcol.h = random.random()
            else:
                secondcol.s = 2
                secondcol.v = 1
                secondcol.h = random.random()
            

        else:

            maincol.h = random.random()
            maincol.v = random.random()


            # For this the secondary color is complex.
            # Since it is very specific and not everything
            # looks good together.

            if maincol.h < 0.16: # Red and orange.
                secondcol.s = 1
                secondcol.v = 1
                secondcol.h = random.uniform(maincol.h, 0.16)

            elif maincol.h < 0.333: # Green to yellowish
                secondcol.s = 1
                secondcol.v = 1
                secondcol.h = random.uniform(0.16, 0.3)

            elif maincol.h < 0.6: # Green-Blue to desaturated.
                secondcol.s = random.uniform(0,0.7)
                secondcol.v = 1
                secondcol.h = maincol.h

            elif maincol.h < 0.67: # Blue to Green-Blueish.
                secondcol.s = 1
                secondcol.v = 1
                secondcol.h = random.uniform(0.444, 0.67)

            else: # Magenta and pink to desaturated.
                secondcol.s = random.uniform(0,0.7)
                secondcol.v = 1
                secondcol.h = maincol.h
                
        color = {"MainColor":maincol, "SecondaryColor":secondcol}
        
        for attribute in color:
            value = color[attribute]
            Material(car, value, attribute)
         
def ColorPart(car, part):

    # This function colors a specific part
    # of the car, into the colors of the car.

    for attribute in car.get("colors", []):
        value = car["colors"][attribute]
        part.blenderObject[attribute] = value

def GetColor(car):

    try: return car["colors"]["MainColor"]
    except: [1,0,0]
     
def ChangeBody(car, good=False):

    # This function changes the car body.
    
    # Getting objects
    #rig = car.children[car.get("rig","RIG")]
    body = car.childrenRecursive["Car_body"]

    # If we are breakingt he car, record what
    # we had before so we could fix it later.
    if not good:
        if "good" not in body:
            body["good"] = body.meshes[0].name

        # And then execute the change of mesh
        body.replaceMesh(car.get("borked", "Borked"))
        GlassSound(car, position=car.position, volume=3)

        if "Neon" in car:
            car["Neon"].blenderObject.data.energy = 0

    # Otherwise restore the car
    else:
        try:
            body.replaceMesh(body["good"])
        except:
            pass

        RemoveFireBall(car)
    

        if "Neon" in car:
            car["Neon"].blenderObject.data.energy = 1000        
    
def SmokeAndFire(car):

    # This function drawns smoke and fire
    if not car.get("underwater"):
        health = car.get("health", 1)
        smokeCoefitient = max(0, 1-health*2)*0.1
        fireCoefitient = max(0, 1-health*4)*0.1
        smokeemiter = car.children["SmokeEmmiter"]
        Destruction.Smoke(smokeemiter.position, smokeCoefitient)
        Destruction.Fire(smokeemiter.position, fireCoefitient)

        if "FireBall" not in car and health < 0.3:
            car["FireBall"] = Destruction.AttatchFireBall(smokeemiter.position,
                                                          smokeemiter,
                                                          1.5)
    else:
        RemoveFireBall(car)    

def RemoveFireBall(car):

    if "FireBall" in car:
            Destruction.DeleteFireBall(car["FireBall"])
            del car["FireBall"]

def Fix(car):

    # Fixing the car. Restoring
    # the car to the unbroken,
    # 100% health, state.

    # Restoring health values
    car["health"] = 1
    car["blown"] = False
    for wheel in car.get("wheels", []):
        wheel["health"] = 1
        wheel.visible   = True

    # Restoring the doors.
    for door in car.get("doors",[]):

        # Restoring rotation of the door
        rot = door.orientation.to_euler()
        for i in range(3):
            if i == door.get("axis",2):
                rot[i] = door.get("lockat", 0)
            else:
                rot[i] = 0
        
        door.orientation = rot
        door["locked"] = True
        
        # Restoring the shape
        door.visible = True
        if "Good" in door:
            door.replaceMesh(door["Good"])
        door.suspendPhysics()

        door["health"] = 1.0
        
    # Restoring the body work.
    ChangeBody(car, good=True)

    # Flipping it over
    
    rot = car.orientation.to_euler()
    rot.y = 0
    rot.x = 0
    car.orientation = rot
        
def InView(car):

    # This function will determen if the
    # car is visible from the perspective
    # of the active camera.

    scene, car = GetSceneAndCar(car)

    camera         = scene.active_camera
    deleteDistance = 500

    # To avoid false positives, any car very
    # close to the camera will be flagged as
    # visible. Even if it is not.
    
    if car.getDistanceTo(camera) < 100:
        return True
    
    # Then we check whether the car is farther
    # then the delete distance ( in which case
    # it will soon despawn anyway ).

    if car.getDistanceTo(camera) > deleteDistance:
        return False

    # Next we check if the car is toward the direction
    # of the camera.
    
    if not camera.pointInsideFrustum(car.worldPosition) == camera.INSIDE:
        return False

    # And finally we check if any visible object
    # is obscuring the car.

    try:
        obj = camera.rayCastTo(car)
        if obj not in [car]+list(car.childrenRecursive) and obj.visible:
            return False
    except:
        pass

    # If all tests are passed, the car is visible.
    return True

def GetPhysicsForces(car):

    # It would be great if UPBGE had a way
    # to measure forces on the car. But
    # there isn't such a way, so I will make
    # my stupid attemt at appriximating them.

    # To do it I will be comparing linear velocity
    # of the car, to linear velocity car used
    # to have a frame ago. This multiplying by the
    # FPS will result in a crude approximation of
    # the forces that are acting on the car.

    v = car.localLinearVelocity
    pv = car.get("prevLocalLinearVelocity", mathutils.Vector((0,0,0)))

    car["localLinearForces"] = ( v - pv ) * bge.logic.getAverageFrameRate()

    car["prevLocalLinearVelocity"] = v.copy()
    
def SwingDoors(car):

    # Doors and other hinged objects might
    # be swang around by the momentum of
    # the car.   
    
    reference = ( car["localLinearForces"] + car.localLinearVelocity ) / 2
    
    
    for door in car.get("doors",[]):

        # If locked, skip
        if door.get("locked", True) or not door.visible:
            continue
        
        # Getting door data
        minrot = door.get("minrot", 0)
        maxrot = door.get("maxrot", math.pi/2)
        lockat = door.get("lockat", 0)
        breakat= door.get("breakat", math.pi/2)
        axis   = door.get("axis", 2)
        factor = door.get("factor", 1.0)
        offset = door.get("offset", math.pi)

        # Rotating door  
        rot = door.localOrientation.to_euler()
        rot[axis] = (math.atan2(*(i for n, i in enumerate(reference) if n != axis )) + offset ) * factor
        for n in range(3):
            if n != axis:
                rot[n] = 0

        # Gravity assisted autolocking.
        # For hoods and things like that.
        # Where not moving means that gravity pushes
        # the door down.
        
        if door.get("gravity", axis != 2):

            if axis == 1: gravityaxis = 2
            else        : gravityaxis = 1
            
            
            gravityfactor = reference[gravityaxis]
            if gravityfactor < 0: gravityfactor *= -1
            gravityfactor = min(30, gravityfactor)/30
            gravityfactor = 1 - gravityfactor
            
            rot[axis] = rot[axis]+((lockat - rot[axis])*gravityfactor)
            

        # Rotation clamping.
        maxrot    -= minrot
        rot[axis] -= minrot
        if rot[axis] < 0: rot[axis] += math.pi*2
        
        if   rot[axis] > (maxrot/2)+math.pi:
            rot[axis] = 0
        
            
        elif rot[axis] > maxrot:
            rot[axis] = maxrot
        
            
        rot[axis] += minrot
      
        # Damping the effect a little.
        oldrot = door.localOrientation.to_euler()[axis]
        diff = ( rot[axis] - oldrot )
        if   diff >  math.pi: diff -= math.pi*2
        elif diff < -math.pi: diff += math.pi*2
        rot[axis] = oldrot + ( diff / 5 )
        
        # Applying rotation.
        door.localOrientation = rot

        # Locking door
        if door["health"] > 0.5 and round(rot[axis],2) == round(lockat, 2):
            door["locked"] = True
            rot[axis] = lockat

        # Detaching the door
        if round(rot[axis],3) == round(breakat, 3):
            door["health"] -= 0.003

        if door["health"] < 0.1:

            door.visible = False
            
            newdoor = Reuse.Create(door.name, 500)
            if "Borked" in door:
                newdoor.replaceMesh(door["Borked"])
            newdoor.position = door.position
            newdoor.orientation = door.orientation
            newdoor.worldLinearVelocity = car.worldLinearVelocity / 1.5
            ColorPart(car, newdoor)

def CloseCall(car):

    # This function will measure close calls
    # of almost colisions to other cars. And
    # give rewards to the player based on them.

    allcars = bge.logic.globalDict["allcars"]
    

    
    for othercar in allcars:
        if othercar != car:
            distance = car.getDistanceTo(othercar)

            if distance < 7:

                # Ignoring if actually hit something, see OnCollision()
                if bge.logic.globalDict.get("closeCallIgnore"):
                    bge.logic.globalDict["closeCallScore"] = 0
                    bge.logic.globalDict["closeCallCar"]   = None
                    break

                # Calculating score
                v = -car.localLinearVelocity[1]
                if v < 0: v *= -1
                score = ( -( distance - 7 ) * v ) / 100
                
                if score > bge.logic.globalDict.get("closeCallScore", 0):
                    bge.logic.globalDict["closeCallScore"] = score
                    bge.logic.globalDict["closeCallCar"]   = othercar

                    
            elif bge.logic.globalDict.get("closeCallCar") == othercar:
                
                score = bge.logic.globalDict["closeCallScore"]
                bge.logic.globalDict["closeCallScore"] = 0
                bge.logic.globalDict["closeCallCar"] = None

                if score > 1:
                    bge.logic.globalDict["print"] = "Oh god"+("!"*int(round(score)))
                    DaniOhGodSound(car)
                    Money.Recieve(score*25)
                    
                car["shake"] = score
                car["near"] = score
                
                

            else:
                bge.logic.globalDict["closeCallIgnore"] = False


##### PAPSES TRUCK SPECIFIC FUNCTIONS ###

def ChangeCargo(car):

    # Making sure it is a truck
    if "Truck" not in car.name:
        return

    # Telling the Updater function whether the
    # truck should be unloaded or not.
    car["be_unloaded"] = not car.get("be_unloaded", False)

def UnloadCargo(car):

    # This function unloads cargo if the car
    # is the truck.

    # Making sure it is a truck
    if "Truck" not in car.name:
        return

    rig = car.children[car.get("rig","RIG")]
    anim = "Papses_Truck_RigAction"


    cargoray = car.childrenRecursive["TruckCargoRay"]
    cargomover = car.childrenRecursive["TruckCargoMover"]
    # 1.28613 m

    if car.get("unloaded"):
        rig.playAction(anim, 0, 50)


    else:
        rig.playAction(anim, 50, 101)
                
def UpdateCargo(car):

    # This runs on trucks.
    # The function updates the cargo area.

    # Getting objects related to the operation
    rig = car.children[car.get("rig","RIG")]
    anim = "Papses_Truck_RigAction"
    cargoray = car.childrenRecursive["TruckCargoRay"]
    cargomover = rig.channels["Down_Part"]
    chain = car.childrenRecursive["Chain"]
    backdoor = car.childrenRecursive["BackDoor"]
    bdt = -(math.pi / 3 * 2)

    # Getting states of the operation
    beUnloaded = car.get("be_unloaded", False)
    isUnloaded = car.get("is_unloaded", False)
    isExtended  = rig.getActionFrame(0) == 50
    isShrunk    = rig.getActionFrame(0) in [0,100]
    

    
    # Extending the opertations
    if not isExtended and beUnloaded:
        rig.playAction(anim, 0, 50)

    if not isUnloaded and not isShrunk and not beUnloaded:
        rig.playAction(anim, 50, 100)
        
    if isExtended and not isUnloaded and beUnloaded:

        # Finding the ground
        to  = RelativePoint(cargoray, (0,0,-1) )
        fro = cargoray.position
        ray = BeautyRayCast(car, "left", to, fro, dist=100)
        if ray[1]: d = cargoray.getDistanceTo(ray[1])
        else: d = 100

        # Moving the bone down
        loc = cargomover.location
        if d > 0.5 and d != 100:
            loc.x += 0.05
            cargomover.location = loc

            # Extending chain
            chain.blenderObject.modifiers["Array"].fit_length += 0.05

            # Unrolling the door
            backdoor["locked"] = True
            if loc.x > 0.4:
                r = backdoor.localOrientation.to_euler()
                if r.y > bdt: r.y -= 0.1
                else: r.y = bdt
                backdoor.localOrientation = r

        # Telling the system that it's done.
        else:

            # Cargo
            if car.get("cargo"):
                c = car["cargo"]
                c.removeParent()
                c.restorePhysics()
                c["isCargo"] = False
                car["cargo"] = False

                car.mass -= c.mass

                c.position = cargoray.position
                c.position.z += 1.5
                c.orientation = car.orientation

                for wheel in c["wheels"]:
                    wheel.visible = True
            
            car["is_unloaded"] = True

            r = backdoor.localOrientation.to_euler()
            r.y = bdt
            backdoor.localOrientation = r

    if isExtended and isUnloaded and not beUnloaded:

        # Getting a cargo
        if not car.get("cargo"):

            # Looking through all cars.
            for c in bge.logic.globalDict["allcars"]:
                if c.getDistanceTo(cargoray) < 2:
                    PackCargo(car, c)

        
        # Moving the bone up
        loc = cargomover.location
        if loc.x > 0:
            loc.x -= 0.05
            cargomover.location = loc

            # Retracting chain
            chain.blenderObject.modifiers["Array"].fit_length -= 0.05

            # Closing the door
            backdoor["locked"] = True
            r = backdoor.localOrientation.to_euler()
            if r.y < 0: r.y += 0.1
            else: r.y = 0
            backdoor.localOrientation = r

        # Telling the system that it's done.
        else:

            
            
            loc.x = 0
            cargomover.location = loc
            chain.blenderObject.modifiers["Array"].fit_length = 0
            car["is_unloaded"] = False

            r = backdoor.localOrientation.to_euler()
            r.y = 0
            backdoor.localOrientation = r

def PackCargo(car, c):

    cargoray = car.childrenRecursive["TruckCargoRay"]

    c["isCargo"] = True
    car["cargo"] = c
    car.mass += c.mass
    
    c.position = cargoray.position
    c.orientation = car.orientation
    
    c.suspendPhysics()
    c.setParent(cargoray, False, False)
    
    for wheel in c["wheels"]:
        wheel.visible = False       

    print("Packed", c, "into", car)

    
##### SPAWNING AND UNSPAWNING OF CARS ###

def SpawnLogic(camSurroundCars):

    # Logic of autospanning cars.
    # Challenges:
        # Have enough cars.
        # Have enough performace.
        # Have enough variety.

    settings = bge.logic.globalDict["settings"]

    
    spawnedCarModels = bge.logic.globalDict["spawnedCarModels"]
    spawnedCars = len( bge.logic.globalDict["allcars"] )
    
    maxCars = settings.get("maxcars", 4)
    cam  = bge.logic.getCurrentScene().active_camera
    dani = bge.logic.getCurrentScene().objects["Dani_Box"]
    
    # First lets see how many cars are on screen
    # right now.
    # notinframe = []
    # inframe    = []
    # for car in bge.logic.globalDict["allcars"]:
    #     if cam.pointInsideFrustum(car.position) == cam.INSIDE:
    #         inframe.append(car)
    #     else:
    #         notinframe.append(car)

    # Getting all of the points that are close enough.
    spawns = []
    for i in camSurroundCars:
        spawns += bge.logic.globalDict["spawns"].get(i, [])

        # For testing
        if "selected" in bge.logic.globalDict["spawns"]:
            spawns = bge.logic.globalDict["spawns"]["selected"]
            break
        
        
    # Itterating the spawn points
    spawnSorted = []

    for spawn in spawns:
        
        # If the camera is pointed ( roughly ) at it
        vect = cam.getVectTo(spawn["position"])
        vectfactor = vect[0] / 300
        score = vect[0]
        spawnSorted.append((score, spawn))
            
    spawnSorted = sorted(spawnSorted)

    # Model of the car to spawn.
    carModel = str(numpy.random.choice(NPC_cars, p=NPC_cars_probability))
    
    
    for n, spawn in enumerate(spawnSorted):

        distance, spawn = spawn
        
        # If it's way too close, abort
        if len(spawnSorted) > n+1 and distance < 100 \
           and spawn.get("npc") != "racer":
            continue

        # Spawn timer ( to avoid, over spawning )
        if spawn.get("to_spawn_timer", 0) > 0:
            spawn["to_spawn_timer"] -= 1
            continue
        spawn["to_spawn_timer"] = 100


        # Checking race
        if spawn.get("race"):

            # Getting the race data
            
            racename = spawn.get("race")
            race = bge.logic.globalDict["races"][racename]
            after = race.get("after")
            during = race.get("during")
            duringcheck = dani.get("race") == after and Script.Story.get(during)
            aftercheck = after in Script.Story["passed"] or not after or duringcheck

            racetype = race.get("type", "race-car")
            try:
                carModel = random.choice(NPC_type_cars[racetype])
            except Exception as e:
                print(e)
                pass
            
        else:
            aftercheck = False
        
        # For NPC cars
        if ( spawn.get("npc") == "npc" )\
           or  ( spawn.get("npc") == "racer" and aftercheck
                 and spawn.get("to_spawn") and ( not dani.get("race") or duringcheck )):
            
            # Spawning the car.

            car =   Spawn(
                carModel,
                spawn["position"],
                spawn["orientation"],
                True,
                "pallete",
                spawnanyway=False,
                spawn=spawn)

            if not car and (spawnedCars < maxCars or spawn.get("npc") == "racer"):

                car =   Spawn(
                    carModel,
                    spawn["position"],
                    spawn["orientation"],
                    True,
                    "pallete",
                    spawnanyway=True,
                    spawn=spawn)
                
            
            if not car: return

            try:
                if spawn.get("race") and dani.get("driving", {}).get("NitroCan"):
                    AddNitroCan(car)
                    car["nitro"] = 10
            except:pass

            try:
                if spawn.get("race") and dani.get("driving", {}).get("Spoiler"):
                    AddSpoiler(car)
            except:pass
                    

            car.setLinearVelocity([0,-15,0], True)
            spawn["to_spawn"] = False
            car["spawn"] = spawn

            return
            
def Spawn(carname, position, orientation,
          fix=True,         # Whether to fix the car
          color=None,       # What color to assign to the car
          spawnanyway=True, # Spawn it no matter what.
          anything=False,   # Spawn anything at all.
          spawn=None):      # Spawn data from SpawnLogic()

    # This function is simplify spawning
    # of the car.

    # If we recieved a name of a car and not an object.
    if type(carname) == str:

        # Trying to get the car from already spawned
        car = SafeDespawn(bge.logic.globalDict["allcars"],
                          carname,
                          anything=anything)

        spawnedmessage = clr["tdgr"]+" Spawned Reused!"
    
        # Making the car anyway.
        if spawnanyway and not car:
            car = Reuse.Create(carname)

            spawnedmessage = clr["tdyl"]+" Spawned New!"+clr["norm"]+" ( "+str(len(bge.logic.globalDict["allcars"])+1)+" )"

        # Otherwice return nothing.
        elif not car:
            return False

    else:
        car = carname

        spawnedmessage = clr["tdbu"]+" Spawned by object input!"

    if car.get("active"):
        return car
        
    if "spawn" in car:
        car["spawn"]["to_spawn"] = True

    car.position = position
    car.orientation = orientation
    car.worldLinearVelocity = [0,0,0]
    car.worldAngularVelocity = [0,0,0]

    # Putting the car in the list of all cars
    if car not in bge.logic.globalDict["allcars"]:
        bge.logic.globalDict["allcars"].append(car)
    if car not in bge.logic.globalDict["cars"]:
        bge.logic.globalDict["cars"].append(car)

    car["nodespawn"] = 100
    car["stuck"]     = 300

    # Removing the car from races if it used to be in one
    for racename in bge.logic.globalDict["races"]:
        race = bge.logic.globalDict["races"][racename]
        try:
            race["racers"].remove(car)
        except:
            pass

    
    # If a spawn point is given
    if spawn:
        car["npc"]   = spawn.get("npc")
        car["anger"] = spawn.get("anger", random.random())
        if spawn.get("race"):
            car["race"]  = spawn.get("race")
            car["racing"]= False
            race = bge.logic.globalDict["races"][spawn.get("race")]
            if car not in race["racers"]:
                race["racers"].append(car)
    else:
        car["npc"]    = ""
        car["race"]   = ""
        car["racing"] = False

    # Removing stuff on new cars.
        
    RemoveNetId(car)

    try:RemoveNitroCan(car)
    except: pass
    car["nitro"] = 0.0

    try: RemoveSpoiler(car)
    except: pass
        
    print(consoleForm(car),clr["bold"], spawnedmessage, clr["norm"])


    # Updating the car, to make sure everything
    # is configured.

    
    try:
        PhysicsUpdate(car)
        SmartColor(car, color)
        if fix: Fix(car)
    except:
        pass


        
    return car

def SafeDespawn(cars, model=None, anything=False):

    # This function returns a despawned car from a list
    # of input cars.

    for car in cars:

        # If we need a specific model skip anything else
        if model and car.name != model:
            continue

        # Logic to determen whether its safe to despawn.

        if ( not car.get("racing")                # Not a racing car
             and not car.get("active")            # Not one Dani's drivin
             and not InView(car)                  # Not on screen
             and car.get("nodespawn", 0) < 1      # Not just spawned
             and car not in Script.Story.values() # Not a story related car
            ):
            
            
            # Deleting the car in an inactive way.
            Reuse.Delete(car, inactive=True)

            # Removing it from the list of all cars.
            if car in bge.logic.globalDict["allcars"]:
                 bge.logic.globalDict["allcars"].remove(car)

            # Declaring that the car is despawned.
            print(consoleForm(car),clr["bold"]+clr["tdgr"],"Safely Despawned!", clr["norm"])
                 
            # Returning the result
            return car

    # If we need anything at all, but the model car
    # was not found. We try again, without the model
    # in the arguments.
    
    if anything:
        return SafeDespawn(cars)

    # If nothing found, returning False.                
    return False
    
def DespawnLogic(car):

    # This function will despans the car when the car
    # is outside of a visible range, for respawning it
    # in a different place.

    scene, car = GetSceneAndCar(car)

    # No despawn timer
    if car.get("nodespawn", 0) > 0:
        car["nodespawn"] -= 1
        return

    dani           = scene.objects["Dani_Box"]
    camera         = scene.active_camera
    deleteDistance = 400

    if car.getDistanceTo(camera) > deleteDistance \
    and not car.get("racing")                     \
    and car in bge.logic.globalDict["allcars"]    :


        # This function will be scheduled in Opt
        def RemovingTheCar(car, dani):

            
            
            # If car is waiting for a race to start, we don't delete it
            if car.get("npc") == "racer":
                racename = car["race"]
                race = bge.logic.globalDict["races"][racename]
                starter = race["starters"][0]
                if dani.getDistanceTo(starter["location"]) < deleteDistance or car["racing"]:
                    return
                
            # if "spawn" in car:
            #     car["spawn"]["to_spawn"] = True    
                                  
            print("Removing Car:", car)
            
            # Making sure it can be respawned
            if car.get("spawnPoint"):
                car["spawnPoint"]["to_spawn"] = True
            
            # Removing the car from races
            for racename in bge.logic.globalDict["races"]:
                race = bge.logic.globalDict["races"][racename]
                try:
                    race["racers"].remove(car)
                except:
                    pass
                
            if car in bge.logic.globalDict["allcars"]:
                bge.logic.globalDict["allcars"].remove(car)        
                
                # Removing from cache if too many cars in cache

                # TODO make this removing of cars actually work and
                # not sig-fault
                
                #if Reuse.reuse.get(car.name) or len(bge.logic.globalDict["allcars"]) > bge.logic.globalDict["maxCars"]:
                #    for i in car["wheels"]:
                #        i.endObject()
                #    car.suspendPhysics(True)
                #    Reuse.EndObject(car)
                # Otherwise storing it for later.
                #else:
                
                Reuse.Delete(car)
        
        # Scheduling
        RemovingTheCar(car, dani)

def UnspawnLogic():

    for car in bge.logic.globalDict["allcars"]:

        if car in Reuse.reuse:
            bge.logic.globalDict["allcars"].remove(car)
            continue
        
        try:
            car.position
        except:
            bge.logic.globalDict["allcars"].remove(car)

                
##### AI NPCS AND RACERS FUNCTIONS ####

def NormalNPC(car):

    # Redouing the NPC shannanigans
    vturn, mindist = VisionTurn(car, True)
    vturn *= 2#max(2,v)

    
    t = vturn
    if t < 0: t *= -1
    anger = car.get("anger", 0.2)
    v = -car.localLinearVelocity[1]

    
    maxv = mindist / (t+1) * 1.5

    # Stopping in front of Dani, making the cars more stealable.
    dani = bge.logic.getCurrentScene().objects["Dani_Box"]
    if not dani.get("driving"):
        danivect = car.getVectTo(dani)
        if danivect[0] < 10 and danivect[2][1] < -0.3:
            maxv = 0
    
    #The unstuck protocol
    if v < 1 and car.get("stuck",0) < 1:
        car["unstuck"] = 100
        car["stuck"]   = 300
        car["stuckturn"] = -vturn * 100

    elif car.get("stuck",0) > 0:
        car["stuck"] -= 1
    
    # If everything is okay moving forward
    nitro = False
    if maxv > v / 3 and car.get("unstuck", 0) < 1:
        Accelerate(car)
        nitro = True

    # If turing or needing to go back
    else:
        Accelerate(car, -1)
        if car.get("unstuck", 0) > 0:
            car["unstuck"] -= 1

    if car.get("unstuck", 0) < 1:
        car["turn"] = vturn
        #SoftTurn(car, vturn)
    else:
        car["turn"] = -vturn * 100
        #SoftTurn(car, -vturn * 100)
        

    # if nitro:
    #     StartNitro(car)
    # else:
    #     StopNitro(car)

    # PURSUIT CHEAT
    scene = bge.logic.getCurrentScene()
    dani = scene.objects["Dani_Box"]
    
    if bge.logic.globalDict["pursuit-cheat"] and dani.get("driving"):

        car["target"] = dani["driving"]
        car["npc"] = "pursuit"

def AngryPursuit(car):

    # This is the code for NPC that are pursuing
    # you.

    # Getting the pursuit target
    try:
        target = car["target"]

        # Deactivate Argy Pursuit if the target car is blown
        if target.get("blown"):
            car["npc"] = "npc"

        if car.get("blown"):
            car["npc"] = ""
        
    except:
        car["npc"] = "npc"
        return

    # Showing on the map
    scene, car = GetSceneAndCar(car)
    dani = scene.objects["Dani_Box"]
    if target == dani.get("driving"):
        Map.Show(car)
    
    # Vector to the target
    vect = car.getVectTo(target)
    distance = vect[0]
    ontarget = -vect[2][1]

    # If following Dani, don't despwant as much
    if vect[0] < 300 and target == dani.get("driving"):
        car["nodespawn"] = 300
    
    # Turn
    pturn = vect[2][0] * 4
    vturn, mindist = VisionTurn(car, True)
    if vect[0] > 50:
        turn = (pturn*(1-ontarget)) + (vturn*ontarget)
    else:
        turn = pturn
        
    nitro = False
    if ontarget > -0.3:
        Accelerate(car, 1)
        #car["turn"] = turn
        SoftTurn(car, turn)
        if OnGround(car):
            nitro = True
        
    else:
        Accelerate(car, -1)
        SoftTurn(car, -turn)
        #car["turn"] = -turn

    if nitro:
        v = -car.localLinearVelocity[1]
        tv = -car["target"].localLinearVelocity[1]
        if v < tv or vect[0] < 50:
            car["nitro"] = 10.0
        StartNitro(car)
    else:
        StopNitro(car)

def RacingAI(car):

    # This is the code for NPC that are pursuing
    # you.

    scene, car = GetSceneAndCar(car)
    dani = scene.objects["Dani_Box"]
    cam = scene.active_camera

    
    
    onground = OnGround(car)
    
    
    # Getting race info
    try:
        racename = car["race"]
    except:
        car["npc"] = "npc"
        return

    race = bge.logic.globalDict["races"][racename]

    # Race position
    try: pos = race["positions"].index(car)
    except: pos = 0

    # Making sure car has lap andcheckpoint info
    if "lap" not in car:
        car["lap"] = 0
    if "checkpoint" not in car:
        car["checkpoint"] = 0

    # Getting next checkpoint and the one after that
    checknumber = car["checkpoint"]
    
    checkpoint1 = race["checkpoints"][checknumber]
    try:    checkpoint2 = race["checkpoints"][ checknumber + 1 ]
    except: checkpoint2 = race["checkpoints"][ 0 ]

    target  = checkpoint1["location"]
    target2 = checkpoint2["location"]

    # Vector to the first checkpoint
    vect = car.getVectTo(target)
    distance = vect[0]
    ontarget = -vect[2][1]

    # Vector to the second checkpoint
    vect2 = car.getVectTo(target2)
    distance2 = vect2[0]
    ontarget2 = -vect2[2][1]

    # Getting recording data
    targets = GetRacingTargets(car, racename, checknumber)
    pretargets = GetRacingTargets(car, racename, checknumber - 1)
    
    # Going to the next checkpoint ( when car is close enough to
    # the current one ).
    if vect[0] < checkpoint1["radius"]:
        car["checkpoint"] += 1

        if checkpoint1.get("OnLoop"):
            car["abort-resque"] = 200

        # Testing how well the car is doing
        if targets.get("time"):
            currentRaceTime = bge.logic.getRealTime() - race.get("start-time", 0)
            timediff = targets["time"] - currentRaceTime
            car["racetd"] = timediff - car.get("timediff",0)
            car["timediff"] = timediff

            ptdiff = str(round(car["racetd"], 1))
            if car["racetd"] > 0:
                ptdiff = "+"+ptdiff
            
            ActivePrint(car,
                        round(currentRaceTime, 1),
                        round(targets["time"], 1),
                        ptdiff)

            
        # Same thing by for laps too.
        if car["checkpoint"] == len(race["checkpoints"]):
            car["checkpoint"] = 0
            car["lap"] += 1

    # Velocity of the car
    v = -car.localLinearVelocity[1]

    # Turn target based on checkpoint vectors
    pturn  = vect[2][0] * 5
    pturn2 = vect2[2][0] * 5

    # Distance to the first checkpoint ( 0 to 1 )
    # This one is used for turning calclations
    closeFrac = -(min(1, distance/max(0.1,v))-1)

    # Distance to the first checkpoint from the last one.
    # This one to calculate target speed.
    try:
        prevcheck  = race["checkpoints"][ checknumber - 1 ]
        distbetween = math.dist( target, prevcheck["location"] ) - (checkpoint1["radius"] + prevcheck["radius"])
        speedFrac = min(1, max(0, 1 - ( (vect[0]-checkpoint1["radius"]) / distbetween )))        
    except:
        speedFrac = closeFrac

    
        
    

    # In certain cases we want to update the turning fraction
    # closeFrac to act in strange ways for a better racing line.
    orcloseFrac = closeFrac
    if ( (pturn > 0) != (pturn2 > 0) ):
        closeFrac =  TargetingCurve(speedFrac)

    # Rotation targeting ( from the recording data )
    # if targets.get("rot"):
    #     trz = targets["rot"][2]
    #     rz  = car.orientation.to_euler()[2]
    #     frac, diff = RotDiff(trz, rz)
    #     pturn = (pturn*(1-orcloseFrac)) + (diff*orcloseFrac)
        
    # Vision based rotation data
    vturn, mindist = VisionTurn(car, True)
    vclose = -(min(1, mindist/10)-1)

    # Calulating rotation between checkpoints
    tturn = (pturn*(1-closeFrac)) + (pturn2*closeFrac)

    # When there are elevations ( such on the loops in the
    # Racetrack ) it messes up the vision a bit, and car
    # go crazy. It's better for those to just use the checkpoints
    # for navigation.
    if not checkpoint1.get("OnLoop") and not checkpoint1.get("IgnoreVision"):
        turn  = (tturn*(1-vclose)) + (vturn*vclose)
        ontarget = ontarget ** 2
    else:
        turn = pturn

    # Anti-tumble turning ( if the car rotates sideways, it
    # counteracts this by turing the other way to return the
    # car back to the wheels ).
    tumble = car.localOrientation.to_euler()[1]
    turn += tumble / 2
    
    # Getting how much the car is turning
    t = turn
    if t < 0: t *= -1

    # Calculating target acceleration
    if not pretargets.get("speed"):
        accel = 20 
    else:
        accel = pretargets["speed"]

      
    # Calculating target speed by interpelating target speed
    # between two sets of data. Of previous and next checkpoints.

    if targets.get("time"):

        # but first we need to find the original acceleration between
        # this to next checkpoint so the time matches.

        try:
            pt =  pretargets.get("time", 0)
            nt =  targets.get("time", 0)
            timeIs = nt - pt + ( car.get("racetd", 0) * 2 )
            
            
            accel = max( distbetween / timeIs, accel )
        except:
            pass
        
    
    if targets.get("speed"):

        accel = (accel*(1-(speedFrac))) + ( targets["speed"] * (speedFrac) )
        if ontarget > 0.9:
            accel = max( accel , targets["speed"] ) 


            
        
    # Targeting the timing of the race.
    # Accelerating faster if not on time with the
    # recording.
    #if car.get("racetd",0) < 0 and ontarget > 0.9:
    #    accel *= min(1-(max(0,car.get("racetd",0))/5), 3)

    # If the car is not on target, slow it down accordingly.
    accel *= ontarget
        
    # Draw beam
    RacerBeam(car)

    # If the car is stuck, resque it.
    if 1 > v or ( UpSideDown(car) and not onground and
                  not checkpoint1.get("OnLoop") ) or (ontarget < 0.3
                                                         and not checkpoint1.get("OnLoop")):
        if ResqueRacer(car):
            #car.localLinearVelocity[1] = -accel
            car.setLinearVelocity([0,-pretargets.get("speed", accel), 0], True)
            car["nitro"] = 10.0
        
    # Cheating resque, so that to give more challenge to
    # the player. If the player is too far ahead, the cars
    # will resque not too far away from him.
    # if pos != 0 and race["positions"].index(dani) == pos - 1\
    #    and dani.get("checkpoint") not in [checknumber, checknumber + 1]:

    #     # But we don't want it to be noticeable, so more checks.
    #     tc = race["checkpoints"][dani.get("checkpoint", 0) - 1]
    #     if cam.pointInsideFrustum(tc["location"]) != cam.INSIDE \
    #        and cam.pointInsideFrustum(car.position) != cam.INSIDE \
    #        and not tc.get("OnLoop")\
    #        and dani.getDistanceTo(tc["location"]) > tc["radius"]*2:

    #         # The resquing itself.
    #         car["checkpoint"] = dani.get("checkpoint", 0)
    #         car["lap"] = dani.get("lap",0)
    #         car["abort-resque"] = 0
    #         ResqueRacer(car, visible=False)
    #         daniv = dani["driving"].localLinearVelocity[1]
    #         car.localLinearVelocity[1] = daniv
    
    # Showing on the map
    #if pos < race["positions"].index(dani):
    Map.Show(car, color=GetColor(car))
    

    # If there is a nessesity, use handbrake.
    if t > 0.3 and v > 7 and onground:
        HandBrake(car)

    # Applying acceleration. ( also ignore the accel if you see that the road is straight )
    if v < accel or (ontarget > 0.8
                     and ( ontarget2 > 0.9 or vect[0] < 100)
                     and not checkpoint1.get("OnLoop")):
        Accelerate(car, 1)
        
    else:
        Accelerate(car, -1)

    
    SoftTurn(car, turn)
    
        
    # Nitro

    # A little bit of a cheat. If the AI racer is not first
    # it gets free nitro.
    if pos != 0:
        car["nitro"] = 10.0

    # Nitro logic.
    if (v < max(accel, targets.get("speed", 0)) and ontarget > 0.5 )\
       and onground:
        StartNitro(car)
    else:
        StopNitro(car)

def RotDiff(rot1, rot2):

    # This function compares 2 rotations values

    # 360 degress in radiance is 2 pi
    turn = 2 * math.pi
    diff = rot1 - rot2
    if diff > math.pi:
        diff -= math.pi * 2
    elif diff < -math.pi:
        diff += math.pi * 2

    frac = diff
    if frac < 0: frac *= -1
    frac = 1 - ( frac / ( math.pi * 2 ) )

    return frac, diff
    
def TargetingCurve(value):

    # To make the car direct it's momentum
    # properly toward the checkpoint, it needs
    # to first turn away from the next checkpoint
    # and the at it. To alight the checkpoint with
    # the next checkpoint.

    return -math.sin(1.2*((value)*math.pi)) * ( value**5 ) * 1

def RacerBeam(car):

    scene = bge.logic.getCurrentScene()
    dani = scene.objects["Dani_Box"]
    cam = scene.active_camera

    if not car.get("beam"):
        return
    
    # Moving the indicator
    car["beam"].position = car.position
    car["beam"].position[2] += 1  
    tocam = car["beam"].getVectTo(cam)
    car["beam"].alignAxisToVect(tocam[1], 1, 1.0)
    car["beam"].alignAxisToVect( (0,0,1), 2, 1.0 )
    beamscale = max( 0.2,  car.getDistanceTo(dani) / 100 )
    car["beam"].scaling = [beamscale, beamscale, beamscale]
    
def ResqueRacer(car, visible=True):

    # Resque system for racing cars.

    # Getting race data

    try:
        racename = car["driver"]["race"]
    
    except:
        try: racename = car["race"]
        except: return False

    
    # Abort system for not overresqueing
    if "abort-resque" not in car:
        car["abort-resque"] = 50
    if car["abort-resque"] > 0:
        car["abort-resque"] -= 1
        return False
    
    car["abort-resque"] = 50

        
    race = bge.logic.globalDict["races"][racename]

    # Getting checkpoints
    try: checknumber = car["checkpoint"]
    except: checknumber = car["driver"]["checkpoint"]
    
    try: checkpoint1 = race["checkpoints"][ checknumber - 1 ]
    except: checkpoint1 = race["checkpoints"][ 0 ]

    while checkpoint1.get("OnLoop"):
        print(car, checkpoint1.get("OnLoop"), checknumber)
        checknumber -= 1
        try: checkpoint1 = race["checkpoints"][ checknumber - 1 ]
        except: break

    if not car.get("active"):
        #car["driver"]["checkpoint"] = checknumber
        car["checkpoint"] = checknumber
    
    checkpoint2 = race["checkpoints"][ checknumber ]

    # Fixing the car ( but not fully )
    car["health"] = max( car["health"], 0.5 ) # Only upto 50% health restored
    car["blown"]  = False                     # Making car blowup-able
    car["underwater"] = False

    # Fixing wheels
    for wheel in car.get("wheels", []):
        wheel["health"] = max(wheel["health"], 0.7)
        wheel.visible   = True  

    # Nothing is done to restore the look of the car, so the user will be
    # reminded that the car is broken.
        
    if visible:
        # Sparkles on disappearing position
        Destruction.particles("Sparkle", car.position.copy(), 
                              despawn = random.randint(30, 100),
                              amount = 30,
                              max = 30,
                              spread = 2,
                              normal = 0.05,
                              scale = 7) 
    
    car.position = checkpoint1["location"]
    car.position.z += 1

    car.setLinearVelocity([0,0,0], True)

    # Rotation
    # targets = GetRacingTargets(car, racename, checknumber)
    
    # if targets.get("rot"):
        
    #     car.orientation = targets.get("rot")
        
    # else:
    
    tocheck = car.getVectTo(checkpoint2["location"])
    car.alignAxisToVect( (0,0,1), 2, 1.0 )
    car.alignAxisToVect(-tocheck[1], 1, 1.0)
    #if UpSideDown(car):
    

    if visible:
        # Sparkles on the new position
        Destruction.particles("Sparkle", car.position, 
                              despawn = random.randint(30, 100),
                              amount = 50,
                              max = 100,
                              spread = 2,
                              normal = 0.05,
                              scale = 5)
    return True
    
def GetRacingTargets(car, racename, checkpoint):

    # This function gives racing targets
    # such as target speed and such.

    race = bge.logic.globalDict["races"][racename]

    
    # Choosing the recoring
    if "race-recording" not in car:
        try:
            car["race-recording"] = random.choice(list(race.get("raceData", {}).keys()))
        except:
            return {}

    rr = car["race-recording"]
    
    answer = {}
    
    

    # Speed
    try: answer["speed"] = race["raceData"][rr]["speed"][checkpoint]
    except: pass

    # Rotation
    try: answer["rot"] = race["raceData"][rr]["rot"][checkpoint]
    except: pass

    # Time
    try: answer["time"] = race["raceData"][rr]["time"][checkpoint]
    except: pass

    return answer
    
def TargetedNPC(car):

    # Making sure that the NPC is
    # going somewhere

    if not car.get("target"):
        RandomTarget(car)
        
    if not car.get("path"):
        car["path"] = FindPathTo(car, car["target"])


    # Getting the path
    path = car.get("path", [])

    # Pointing the car at the next node on the path
    # or at the target.
    if len(path) > 1:
        nexttarget = path[-2]
    else:
        nexttarget = car["target"]

    # Finiding vector to the nexttarget
    vect = car.getVectTo(nexttarget)
    distance = vect[0]
    ontarget = -vect[2][1]

    # Finding a path to the target.
    if distance < 15:
        car["path"] = FindPathTo(car, car["target"])
    
    
    # Turning calculation
    pturn = vect[2][0]
    vturn, mindist = VisionTurn(car, True)
    vturn *= 2

    maxv = 10
    
    if vect[0] > 50:
        turn = (pturn*(1-ontarget)) + (vturn*ontarget)
    else:
        turn = pturn
        
    # Cant's velocity
    v = -car.localLinearVelocity[1]

    maxv *= max(0.2, ontarget)

    if maxv > v and ontarget > -0.3:
        Accelerate(car, 1)
        car["turn"] = turn
        #SoftTurn(car, turn)
        if OnGround(car):
            nitro = True
        
    else:
        Accelerate(car, -1)
        #SoftTurn(car, -turn)
        car["turn"] = -turn
    
        
    # else:
    #     car["stuck"] = True

    # if car.get("stuck") and ontarget < 0:
    #     if 0.5 > -v:
    #         Accelerate(car, -1)
    #     SoftTurn(car, -turn*10)

    # else:
    #     car["stuck"] = False

    # Is the car already at the target place?
    #finished = closeFrac > 0.5 and car["target"] == nexttarget

    #if finished: IdleBraking(car)
    
    # Termination of NPC logic
    if car.get("underwater")\
       or car.getDistanceTo(car.get("target")) < 5:
        car["npc"] = False
        car["target"] = []

def RandomTarget(car):

    Navigation = bge.logic.globalDict["Navigation"]
    car["target"] = random.choice(Navigation["parking"])["position"]
    
def VisionTurn(car, return_min=False):

    # Calculating turn depending on what the
    # car car see.


    # There is this object used for separating
    # the road sides.
    sep = "Separation"

    # There is also a material for the road which
    # we will ignore
    road = "ROAD"

    vo = car.get("visionOffset", 0)

    # Making sure we scan a wider range
    height = -3#random.uniform(-6, -3)
    width  = -10#random.uniform(-15, -10)

    # Getting the rays
    toright = RelativePoint(car, (-5, width, height))
    fromright = RelativePoint(car, (-0.8,0+vo,0.5))
    rightray = BeautyRayCast(car, "right", toright, fromright, dist=50, poly=True)
    try:   rightm  = str(rightray[3].material).upper()
    except:rightm  = ""

    # And for the left too
    toleft = RelativePoint(car, (5, width, height))
    fromleft = RelativePoint(car, (0.8,0+vo,0.5))
    leftray  = BeautyRayCast(car, "left", toleft, fromleft, dist=50, poly=True)
    try:   leftm = str(leftray[3].material).upper()
    except:leftm = ""

    # Upper Forward rays

    toupright = RelativePoint(car, (-5, width, 0.5))
    fromupright = RelativePoint(car, (-0.8,0+vo,0.5))
    uprightray  = BeautyRayCast(car, "upright", toupright, fromupright, dist=50, poly=True)
    try:   uprightm  = str(uprightray[3].material).upper()
    except:uprightm  = ""


    toupleft = RelativePoint(car, (5, width, 0.5))
    fromupleft = RelativePoint(car, (0.8,0+vo,0.5))
    upleftray  = BeautyRayCast(car, "upleft", toupleft, fromupleft, dist=50, poly=True)
    try:   upleftm = str(upleftray[3].material).upper()
    except:upleftm = ""



    
    # Forward ray
    #if return_min:
    toforward  = RelativePoint(car, (0, -10, -1))
    fromforward    = RelativePoint(car, (0,-2+vo,0.5))
    forwardray  = BeautyRayCast(car, "forward", toforward, fromforward, dist=50, poly=True)
    try:   forwardm = str(forwardray[3].material).upper()
    except:forwardm = ""
    
    toupforward  = RelativePoint(car, (0, -10, 0.5))
    fromupforward    = RelativePoint(car, (0,-2+vo,0.5))        
    upforwardray  = BeautyRayCast(car, "upforward", toupforward, fromupforward, dist=50, poly=True)
    try:   upforwardm = str(upforwardray[3].material).upper()
    except:upforwardm = ""
        
    
    right   = 50
    left    = 50
    upright = 50
    upleft  = 50
    forward = 50
    upforward = 50 * 4

    LINE = "MATRACK_LIGHT.002"


    if forwardray[1] and road not in forwardm:
        forward = car.getDistanceTo(forwardray[1])

    if upforwardray[1] and road not in upforwardm:
        upforward = car.getDistanceTo(upforwardray[1])
    
    if leftray[1] and road not in leftm:
        left  = car.getDistanceTo(leftray[1])

    if rightray[1] and road not in rightm:
        right = car.getDistanceTo(rightray[1])

    if upleftray[1] and road not in upleftm:
        upleft  = car.getDistanceTo(upleftray[1])

    if uprightray[1] and road not in uprightm:
        upright = car.getDistanceTo(uprightray[1])

    left  = min(left, upleft)
    right = min(right, upright)
    forward = min(forward, upforward / 4)

    # Anti britishing department
    if uprightm == LINE and not car.get("race"):
        left *= 0.9
        right = max(left*1.01, right)

    # Race ignoring line
    if car.get("racing"):
        if uprightm == LINE or rightm == LINE:
            right = 10
        if upleftm == LINE or rightm == LINE:
            left = 10

    forward /= 2
    mindist = min(left, right, forward, upright, upleft)
    #maxdist = max(left, right, forward, upright, upleft)

    st = 5
    turn =  max(-0.8, min(0.8, (( max(0, min(1, ( right / st ))) - max(0, min(1, ( left / st ))) ) *-2 )))

    # Another version of the turn calculation
    
    # minturn = min(left, right)
    # maxturn = max(left, right)
    
    # turn = ( ( left / maxturn  ) - ( right / maxturn )  ) * ( maxturn / minturn / 4 )

    # Avoiding cars and stuff
    if upforwardray[0]:
        obj = upforwardray[0]
        if "speed" in car and obj != car.get("target"):
            car["avoid"] = obj

    if car.get("avoid"):
        obj = car["avoid"]
        vect = car.getVectTo(obj)
        v = -car.localLinearVelocity[1]
        closeFrac = -(min(1, vect[0]/(v*5))-1)
        if vect[2][0] > 0: pturn = -1
        else:              pturn =  1
        turn = (turn*(1-closeFrac)) + (pturn*closeFrac)

        onobj = -vect[2][1]
        if onobj < 1-closeFrac:
            car["avoid"] = None
    
    if not return_min:
        return turn
    else:
        return turn, mindist
   
def SoftTurn(car, turn):

    if turn < 0 and car["turn"] > turn:
        TurnRight(car)
    elif turn > 0 and car["turn"] < turn:
        TurnLeft(car)
    
def FindPathTo(car, location):

    # Since this could be an object,
    # we will try to unlcoking the location

    try: location = location.position
    except: pass
    location = mathutils.Vector(location)

    path = []

    Navigation = bge.logic.globalDict["Navigation"]

    def findClosest(car, location, points, exclude):


        cd = 1000
        nd = None

        for node in points:

            if location == None: location = node
            distance = math.dist(location, node )
            distance += car.getDistanceTo(node) / 4
            
            if distance < cd and node not in exclude:
                cd = distance
                nd = node

        return nd


    points = list(n["position"] for n in Navigation["road"]) + [car.position]
    
    while not path or path[-1] != car.position:

        if not path:
            path.append(findClosest(car, location, points, path))
        else:
            path.append(findClosest(car, path[-1], points, path))
            
    for n, node in enumerate(path):
        if n == 0:
            DrawLine(car, "Main", node, location)
        else:
            DrawLine(car, n, node, path[n-1])

    return path

def StraightLine(car, point1, point2):

    ray = car.rayCast(point1, point2)
    if ray[1]:
        cd  = math.dist(point1, point2)
        ad  = math.dist(point2, ray[1])
        if round(cd, 1) == round(ad, 1):
            return True
        else:
            return False
    return True


##### SOUND RELATED FUNCTIONS ####
    
def EngineSound(car):

    # Engine Sound
    
    device = bge.logic.globalDict["SoundDevice"]

    s = car.get("engine_sound","//sfx/engines/neonspeedster.ogg")
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]

    if not sound["play"] or not sound["play"].status:
        sound["play"] =  device.play(sound["sound"])
        
    sound["play"].location = car.worldPosition
    sound["play"].velocity = car.getLinearVelocity()
    sound["play"].relative = False
    sound["play"].distance_maximum = 200
    sound["play"].distance_reference = 1
    sound["play"].attenuation = 1

    pitch = RPMWithIdle(car, normalize=True) / car["specs"]["maxrpm"]

    sound["play"].pitch = pitch * 3
    sound["play"].volume = car.get("health", 1)

    # Grind sound
    v = -car.localLinearVelocity[1]
    if v < 0: v *= -1
    
    s = "//sfx/grind.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]
    if v > 0.01:
        if not sound["play"] or not sound["play"].status:
            sound["play"] =  device.play(sound["sound"])
        

        
        sound["play"].location = car.worldPosition
        sound["play"].velocity = car.getLinearVelocity()
        sound["play"].relative = False
        sound["play"].distance_maximum = 200
        sound["play"].distance_reference = 1
        sound["play"].attenuation = 1
        sound["play"].pitch = max(0.3, car.get("health", 1)) * 2
        sound["play"].volume = (1-car.get("health", 1)) * min(1.5, v *8) * 1
            
    else:
        
        try:
            bge.logic.globalDict["sounds"][code]["play"].stop()
        except:
            pass

def GearShiftSound(car):

    # This is the sound of the gear
    # being shifted.

    device = bge.logic.globalDict["SoundDevice"]

    s = "//sfx/gear_shit.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]
    sound["play"] =  device.play(sound["sound"])    
    sound["play"].location = car.worldPosition
    sound["play"].velocity = car.getLinearVelocity()
    sound["play"].relative = False
    sound["play"].distance_maximum = 200
    sound["play"].distance_reference = 1
    sound["play"].attenuation = 1
    sound["play"].volume = 3

def DriftSound(car):

    # This is a sound of rubber skidding.

    device = bge.logic.globalDict["SoundDevice"]

    s = "//sfx/drift.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    slide = car.localLinearVelocity[0]

    # Adding nitro
    AddNitro(car, slide / 5000)
    
    if car.get("braking"):
        slide = car.localLinearVelocity[1]
    if slide < 0:
        slide *= -1
    
    sound = bge.logic.globalDict["sounds"][code]
    if slide > 1 and OnGround(car):
        
        #car["nitro"] += 2
        
        if not sound["play"] or not sound["play"].status:
            sound["play"] =  device.play(sound["sound"])
        
        sound["play"].location = car.worldPosition
        sound["play"].velocity = car.getLinearVelocity()
        sound["play"].relative = False
        sound["play"].distance_maximum = 200
        sound["play"].distance_reference = 1
        sound["play"].attenuation = 1
        #sound["play"].pitch = 0.8
        sound["play"].volume = min(2, slide / 5) / 5

                
    else:
        
        try:
            bge.logic.globalDict["sounds"][code]["play"].stop()
        except:
            pass

def ScrapeSound(car, position=None, volume=3):

    # This function produces
    # a sound of scraping metal.

    device = bge.logic.globalDict["SoundDevice"]

    s = "//sfx/scrape.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]
    
    if not sound["play"] or not sound["play"].status:
        sound["play"] =  device.play(sound["sound"])

        if not position:
            sound["play"].location = car.worldPosition
        else:
            sound["play"].location = position
        sound["play"].velocity = car.getLinearVelocity()
        sound["play"].relative = False
        sound["play"].distance_maximum = 200
        sound["play"].distance_reference = 1
        sound["play"].attenuation = 1
        sound["play"].pitch = random.uniform(0.6, 1.4)
        sound["play"].volume = volume

def RedlineSound(car, position=None, volume=3):

    # This function produces
    # a sound of scraping metal.

    device = bge.logic.globalDict["SoundDevice"]

    s = "//sfx/redline.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]
    
    if not sound["play"] or not sound["play"].status:
        sound["play"] =  device.play(sound["sound"])

        if not position:
            sound["play"].location = car.worldPosition
        else:
            sound["play"].location = position
        sound["play"].velocity = car.getLinearVelocity()
        sound["play"].relative = False
        sound["play"].distance_maximum = 200
        sound["play"].distance_reference = 1
        sound["play"].attenuation = 1
        #sound["play"].pitch = random.uniform(0.6, 1.4)
        sound["play"].volume = volume

def HitSound(car, position=None, volume=3):

    # This function produces
    # a sound of scraping metal.

    device = bge.logic.globalDict["SoundDevice"]

    s = "//sfx/hit.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]
    
    if not sound["play"] or not sound["play"].status:
        sound["play"] =  device.play(sound["sound"])

        if not position:
            sound["play"].location = car.worldPosition
        else:
            sound["play"].location = position
        sound["play"].velocity = car.getLinearVelocity()
        sound["play"].relative = False
        sound["play"].distance_maximum = 200
        sound["play"].distance_reference = 1
        sound["play"].attenuation = 1
        sound["play"].pitch = random.uniform(0.6, 1.4)
        sound["play"].volume = volume

def GlassSound(car, position=None, volume=3):

    # This function produces
    # a sound of scraping metal.

    device = bge.logic.globalDict["SoundDevice"]

    s = "//sfx/glass.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]
    
    if not sound["play"] or not sound["play"].status:
        sound["play"] =  device.play(sound["sound"])

        if not position:
            sound["play"].location = car.worldPosition
        else:
            sound["play"].location = position
        sound["play"].velocity = car.getLinearVelocity()
        sound["play"].relative = False
        sound["play"].distance_maximum = 200
        sound["play"].distance_reference = 1
        sound["play"].attenuation = 1
        sound["play"].pitch = random.uniform(0.6, 1.4)
        sound["play"].volume = volume

def WheelPopSound(car, position=None, volume=3):

    # This function produces
    # a sound of scraping metal.

    device = bge.logic.globalDict["SoundDevice"]

    s = "//sfx/tire_pop.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                             "play":None}
    
    sound = bge.logic.globalDict["sounds"][code]
    
    if not sound["play"] or not sound["play"].status:
        sound["play"] =  device.play(sound["sound"])

        if not position:
            sound["play"].location = car.worldPosition
        else:
            sound["play"].location = position
        sound["play"].velocity = car.getLinearVelocity()
        sound["play"].relative = False
        sound["play"].distance_maximum = 200
        sound["play"].distance_reference = 1
        sound["play"].attenuation = 1
        sound["play"].pitch = random.uniform(0.6, 1.4)
        sound["play"].volume = volume

def NitroSound(car):

    device = bge.logic.globalDict["SoundDevice"]
    
    s = "//sfx/nitro.ogg"
    code = s+str(car["cid"])
    if code not in bge.logic.globalDict["sounds"]:
        bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                                    "play":None}
        
    sound = bge.logic.globalDict["sounds"][code]
    if not sound["play"] or not sound["play"].status:
        sound["play"] =  device.play(sound["sound"])
    sound["play"].location = car.position
    sound["play"].relative = False
    sound["play"].distance_maximum = 100
    sound["play"].distance_reference = 1
    sound["play"].attenuation = 1
    sound["play"].pitch = 1
    sound["play"].volume = 5

def NitroSoundStop(car):
    
    device = bge.logic.globalDict["SoundDevice"]
    
    s = "//sfx/nitro.ogg"
    code = s+str(car["cid"])
    if code in bge.logic.globalDict["sounds"]:
        sound = bge.logic.globalDict["sounds"][code]
        try:
            if sound["play"].volume > 0:
                sound["play"].volume -= 0.5
            else:
                sound["play"].stop()
        except:
            pass

def DaniOhGodSound(car):

    # Dani saying, oh no!

    scene, car = GetSceneAndCar(car)
    device = bge.logic.globalDict["SoundDevice"]
    dani = scene.objects["Dani_Box"]

    # Dani animation of opening mouth
    Character_Controll.ApplyAnimation(dani, "Talk")
    
    # The sound itself.
    device.play(bge.logic.globalDict["sounds"]["dani_ohgod"]["sound"])
    
def StartEnemySounds(car):

    # Angry voice when player hits somebody

    device = bge.logic.globalDict["SoundDevice"]
    
    sound = random.choice(bge.logic.globalDict["sounds_angry_start"])
    sound = bge.logic.globalDict["sounds"][sound]
    sound["play"] =  device.play(sound["sound"])
    sound["play"].location = car.position
    sound["play"].relative = False
    sound["play"].distance_maximum = 50
    sound["play"].distance_reference = 5
    sound["play"].attenuation = 1
    sound["play"].volume = 1.5
    
    
    car["enemysound"] = sound["play"]

def HitEnemySound(car):

    # Angy voice when the enemy hits player
    
    device = bge.logic.globalDict["SoundDevice"]

    if not car.get("enemysound") or not car["enemysound"].status:
                
        sound = random.choice(bge.logic.globalDict["sounds_angry_hit"])
        sound = bge.logic.globalDict["sounds"][sound]
        sound["play"] =  device.play(sound["sound"])
        sound["play"].location = car.position
        sound["play"].relative = False
        sound["play"].distance_maximum = 50
        sound["play"].distance_reference = 5
        sound["play"].attenuation = 1
        sound["play"].volume = 1.5
        
        car["enemysound"] = sound["play"] 


#### SAVE / MULTIPLAYER FUNCTIONS #####

def Encode(car):

    # This function will generate a copy of the car's
    # data that is possible to save into a Json file.

    # General data
    cardata = {
        "ID"           : id(car),
        "type"         : "veh",
        "name"         : car.name,
        "cid"          : int(car["cid"]),
        "netId"        : car.get("netId"),
        "position"     : list(car.position),
        "orientation"  : list(car.orientation.to_euler())
    }

    # Health data
    cardata["health"]         = car.get("health" , 1.0)
    cardata["nitro"]          = car.get("nitro" , 0.0)
    cardata["NitroCan"]       = car.get("NitroCan" , False)
    cardata["blown"]          = car.get("blown"  , False)
    cardata["Spoiler"]        = car.get("Spoiler", False)

    cardata["doors"]          = []
    for door in car.get("doors",[]):
        doordata = {
            "health"  : door["health"],
            "locked"  : door["locked"],
            "visible" : door.visible
        }
        cardata["doors"].append(doordata)

    cardata["wheels"]         = []
    for wheel in car.get("wheels", []):
        wheeldata = {
            "health"  : wheel["health"],
            "visible" : wheel.visible
        }
        cardata["wheels"].append(wheeldata)

    # Color data
    cardata["colors"] = car.get("colors", {}).copy()
    for colorname in cardata["colors"]:
        color = cardata["colors"][colorname]
        color = list(color)
        cardata["colors"][colorname] = color

    if not car.get("ownerId"):
        car["ownerId"] = bge.logic.globalDict.get("userId")

    netObjects = bge.logic.globalDict["netObjects"]
    if cardata["ID"] not in netObjects["pythonId"]:
        netObjects["pythonId"][cardata["ID"]] = car
        
    return cardata

def Decode(cardata, new=False, network=False):
       
    # This function will restore the car
    # from the encoded state.

    # Making the car
    carModel    = cardata.get("name"       , "NeonSpeedsterBox")
    position    = cardata.get("position"   , [0,0,0])
    orientation = cardata.get("orientation", [0,0,0])

    # Network related recognizing
    netId       = cardata.get("netId")

    netObjects = bge.logic.globalDict["netObjects"]    
        
    if netId and netId not in netObjects["netId"] or new:
        
        car     = Spawn(carModel, position, orientation)
        UpdateCar(car)

        print("Spawned cause", netId, "netId isn't in", list(netObjects["netId"].keys()))
        
        netObjects["netId"][netId] = car

        
        
    car = netObjects["netId"][netId]
    car["netId"] = netId
    car["ownerId"] = cardata.get("ownerId")

    if math.dist(car.position, position) > 0.1:
        car.position = position
    if math.dist(car.orientation.to_euler(), orientation) > 0.1:
        car.orientation = orientation
    
        
    # Health stuff

    car["health"] = cardata.get("health", 1.0)
    car["nitro"]  = cardata.get("nitro", 0.0)

    if cardata.get("NitroCan" , False):
        AddNitroCan(car)

    if cardata.get("Spoiler" , False):
        AddSpoiler(car, cardata["Spoiler"])
    
    car["blown"]  = cardata.get("blown", False)
    if car["health"] < 0.3:
        ChangeBody(car, good=False)

    for n, door in enumerate(car.get("doors",[])):

        # Getting door data
        try:
            doordata = cardata.get("doors",[])[n]
        except Exception as e:
            print("Failed to load door",n,"for", car, ":", e)
            break

        # Restoring the door
        door["health"] = doordata.get("health", 1.0)
        door["locked"] = doordata.get("locked", True)
        door.visible   = doordata.get("visible", True)

        if door["health"] < 1.0:
            if "Borked" in door and door.get("mesh", "Good") != "Borked":
                door.replaceMesh(door["Borked"])
                door["mesh"] = "Borked"

    for n, wheel in enumerate(car.get("wheels", [])):

        # Getting wheel data
        try:
            wheeldata = cardata.get("wheels",[])[n]
        except Exception as e:
            print("Failed to load wheel",n,"for", car, ":", e)
            break

        wheel["health"] = wheeldata.get("health", 1.0)
        wheel.visible   = wheeldata.get("visible", True)

    # Colors
    for attribute in cardata.get("colors", {}):
        value = cardata["colors"][attribute]
        Material(car, value, attribute)

def RemoveNetId(car):

    # Removing NetId from the car
    # for when the play has taken it
    # or when it's despawned.
    
    if "netId" in car and "netCars" in bge.logic.globalDict:

        netCars = bge.logic.globalDict["netCars"]
        netId   = car["netId"]

        if netId in netCars:
            del car["netId"]
            del netCars[netId]
        
                
#### DEBUGGIN FUNCTIONS ######

def DrawLine(car, name, point1, point2):
    # To deactivate this function.
    settings = bge.logic.globalDict["settings"]
    if not settings.get("dev-rays"): return
        
    if "lines" not in car:
        car["lines"] = {}
    if name not in car["lines"]:
        car["lines"][name] = Reuse.Create("Redline")

    line = car["lines"][name]
    line.position = point1
    vect = line.getVectTo(point2)
    line.alignAxisToVect(vect[1], 2, 1)
    line.scaling.z = vect[0]

    scale = 2
    line.scaling.x = scale
    line.scaling.y = scale

def BeautyRayCast(car, name, to, fro, *args, **kwargs):

    # This will make a KX_Object.rayCast() function be more
    # pretty utilizing DrawLine() function.

    ray = car.rayCast(to, fro, *args, **kwargs)

    if ray[1]:
        DrawLine(car, name, fro, ray[1])
    else:
        DrawLine(car, name, fro, to)

    return ray
        
def ActivePrint(car, *text):

    # Prints something only from the car
    # that is driven by the player ( or
    # one that dani is in the driver's
    # seat of, in the case of AI debugging ).
    
    scene, car = GetSceneAndCar(car)
    dani       = scene.objects["Dani_Box"]
    
    if car.get("active") or dani.get("driving") == car:
        print(*text)