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

from Scripts import Reuse
from Scripts import Destruction
from Scripts import Opt
from Scripts.Common import *


from Scripts import Map
from Scripts import Script
from Scripts import Vehicle
from Scripts import Settings
from Scripts import Tools
from Scripts import Money
from Scripts import Garage


from Scripts import Character_Controll
from Scripts import Multiplayer_Client

def main():

    
    
    # SPAWNING ASSETS AND STUFF

    spawnAtDistance = 250
    maxCars = 6

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

    #dani["driving"]["nitro"] = 10.0

    #Script.Story["Jack"].position = dani.position
    #Character_Controll.getOutCar(Script.Story["Jack"])
    #Script.Story["DarkShadow"] = dani["driving"]
    # try:
    #     Destruction.Explosion(dani["driving"].position, mass=10, size=10)
    # except:pass

    loading = scene.objects["Loading"]
    if not loading.get("loaded"):
        loading.scaling = [1,1,1]
        loading["loaded"] = True
        return

    
    
    if "fps-calc" not in bge.logic.globalDict:
        bge.logic.globalDict["fps-calc"] = {"second":0,
                                            "fps-list":[]}

    Opt.ExecuteScheduled()
    goodFPS = Opt.GoodFPS(traceback=True)
    fpsindicator = int(Opt.GoodFPS( boolean=False)*100)
    scene.objects["Dani_Fps_Good_Indicator"]["Text"] = str(int(bge.logic.getAverageFrameRate()))+"\n"+str(fpsindicator)+"%"
    scene.objects["Gage_FPS"]["Health"] = fpsindicator
  

    #camSurround = Opt.Surround(cam.position, spawnAtDistance/2)#, cam.orientation.to_euler())
    camSurroundCars = Opt.Surround(cam.position, spawnAtDistance, cam.orientation.to_euler())

    #changedCamSurround = Opt.SurroundChanged("camSurround", camSurround)
    #changedCamSurroundCars = Opt.SurroundChanged("camSurroundCars", camSurroundCars)

    inCinema = scene.objects["CinemaColider"]["InCinema"]

    # Destroy those needing destroying
    Reuse.SelfDestructDo()
        


    # Available car to spawn
    # NPC_cars = ["NeonSpeedsterBox",
    #             "RedKissBox",
    #             "DarkShadowBox"]
    #             #"HatchBack01-white", 
    #             #"HatchBack01-orange", 
    #             #"HatchBack01-blue", 
    #             #"HatchBack01-black"]

    # npccp    = [1,
    #             1,
    #             1]
    #             #10,
    #             #8,
    #             #5,
    #             #3]

    # NPC_cars_probability = []
    # for i in npccp:
    #     NPC_cars_probability.append( 1 / sum(npccp) * i)
    #print(sum(NPC_cars_probability), NPC_cars_probability)

    # Storing some values to the globalDict
    
    bge.logic.globalDict["spawnAtDistance"] = spawnAtDistance
    bge.logic.globalDict["maxCars"] = maxCars


    def GenerateDoorObject(i):
        # Location doesn't matter when you dealing with rotation
        doorObject = {"ClosedRotX":i.get("ClosedRotX", i.orientation.to_euler()[0]),
                      "ClosedRotY":i.get("ClosedRotY", i.orientation.to_euler()[1]),
                      "ClosedRotZ":i.get("ClosedRotZ", i.orientation.to_euler()[2]),

                      "OpenRotX":i.get("OpenRotX", i.orientation.to_euler()[0]),
                      "OpenRotY":i.get("OpenRotY", i.orientation.to_euler()[1]),
                      "OpenRotZ":i.get("OpenRotZ", i.orientation.to_euler()[2]),

                      "ClosedLocX":i.get("ClosedLocX", i.position[0]),
                      "ClosedLocY":i.get("ClosedLocY", i.position[1]),
                      "ClosedLocZ":i.get("ClosedLocZ", i.position[2]),

                      "door": i
                }
        # But rotation and location both matter when you dealing with location
        move = [0,0,0]
        moveback = [0,0,0]

        for naxis, axis in enumerate(["X", "Y", "Z"]):
            code = "OpenLoc"+axis
            move[naxis] = i.get(code, 0)
            moveback[naxis] = -i.get(code, 0)

        i.applyMovement(move, True)
        for naxis, axis in enumerate(["X", "Y", "Z"]):
            code = "OpenLoc"+axis   
            doorObject[code] = i.position[naxis]
        i.applyMovement(moveback, True)

        return doorObject

    def OpenCloseDoor(door, openClose):

        onTarget = []

        for n, axis in enumerate(["X", "Y", "Z"]):

            targetRot = door[openClose+"Rot"+axis]
            targetLoc = door[openClose+"Loc"+axis]

            dRot = door["door"].orientation.to_euler()

            onRotTarget = round(targetRot, 2) == round(dRot[n], 2)
            onLocTraget = round(targetLoc, 1) == round(door["door"].position[n], 1)

            if not onRotTarget:
                dRot[n] += (targetRot - dRot[n])/15


            else:
                dRot[n] = targetRot

            door["door"].orientation = dRot

            if not onLocTraget:

                door["door"].position[n] += (targetLoc - door["door"].position[n])/15


            else:
                door["door"].position[n] = targetLoc

            onTarget.append( onRotTarget and onLocTraget )

        return all( onTarget )

    # INITIALIZING

    # This runs only on the first frame

    if not Opt.chunks:

        dani.suspendPhysics()

        

        # Making settings executed
        Settings.Execute()
        settings = Settings.load_settings()
        bge.logic.globalDict["settings"] = settings
        
        Settings.LoadGame()

        Script.StatusText(" ")

        if not bge.logic.globalDict.get("start-time"):
            bge.logic.globalDict["start-time"] = datetime.datetime.now().hour + datetime.datetime.now().minute / 60
        

        # Stuff

        bge.logic.globalDict["netObjects"] = {
            "pythonId":{},
            "netId":{}
        }
        
        bge.logic.globalDict["elevators"] = {}
        bge.logic.globalDict["sound-ambiances"] = []
        bge.logic.globalDict["doors"] = {}
        bge.logic.globalDict["races"] = {}
        bge.logic.globalDict["garage-crates"] = []

        # Cars related

        bge.logic.globalDict["spawns"] = {}
        bge.logic.globalDict["allcars"] = []
        bge.logic.globalDict["cars"] = []
        bge.logic.globalDict["spawnedCarModels"] = []

        # Cheat code modes

        bge.logic.globalDict["pursuit-cheat"] = False
        bge.logic.globalDict["derby-cheat"] = False   
        bge.logic.globalDict["gravity"] = True

        # Navigation for NPCs
        bge.logic.globalDict["Navigation"] = {"road"   :[],
                                              "parking":[]}

        Navigation = bge.logic.globalDict["Navigation"]
        
        for navtag in bpy.data.collections["Navigation"].objects:

            tagdata = {"position":navtag.location,
                       "orientation":navtag.rotation_euler,
                       "radius":navtag.scale[0],
                       "connected":[]}
            
            if navtag.get("parking"):
                Navigation["parking"].append(tagdata)
            else:
                Navigation["road"].append(tagdata)

                    
                
        # Races
        for collection in bpy.data.collections['Races'].children:

            race = {"starters":[],
                    "checkpoints":[],
                    "racers":[],
                    "racer_spawns": [],
                    "started":False }

            # Getting racing data
            rdf = os.listdir(bge.logic.expandPath("//racedata"))
            
            
            if collection.name in rdf:

                fol = "//racedata/"+collection.name
                race["raceData"] = {}
                for f in os.listdir(bge.logic.expandPath(fol)):
                    if f.endswith(".json"):
                        with open(bge.logic.expandPath(fol+"/"+f)) as jf:
                            rd = json.load(jf)
                            race["raceData"][f.replace(".json", "")] = rd

                
                
                

            for object in collection.objects:
                tag = {"location":object.location,
                        "rotation":object.rotation_euler,
                        "radius":object.scale[0],
                        "OnLoop":object.get("OnLoop"),
                        "IgnoreVision":object.get("IgnoreVision"),
                        "Uturn":object.get("Uturn"),
                        "cylinder":None}

                # Race starters ( the blue cylinder )

                if object.name.startswith("Starter"):
                    race["starters"].append(tag)
                    race["laps"]  = object["laps"]
                    race["after"]  = object.get("after")
                    race["during"] = object.get("during")
                    race["type"]   = object.get("type", "race-car")
                    race["reward"] = object.get("reward", 1000)

                # Race checkpoints ( the yellow cylinder )

                else:
                    race["checkpoints"].append(tag)

            bge.logic.globalDict["races"][collection.name] = race

        # Characters

        Reuse.Delete(scene.objects["PapsBox"])
        #Reuse.Delete(scene.objects["MoriaBox"])
        Reuse.Delete(scene.objects["JackBox"])

        # Objects

        for object in scene.objects:

            if "spawn" in object:

                objectData = {"position": object.worldPosition.copy(),
                              "orientation": object.worldOrientation.copy(),
                              "spawn": object.get("spawn"),
                              "to_spawn": object["to_spawn"],
                              "race":object.get("race"),
                              "npc":object.get("npc"),
                              "selected":object.blenderObject.select_get()}

                if object.get("race"):
                    race = bge.logic.globalDict["races"][object.get("race")]
                    if "amount-racers" not in race:
                        race["amount-racers"] = 0
                    race["amount-racers"] += 1
                
                addr = Opt.Address(objectData["position"], spawnAtDistance)
                if addr not in bge.logic.globalDict["spawns"]:
                    bge.logic.globalDict["spawns"][addr] = []

                bge.logic.globalDict["spawns"][addr].append(objectData)

                # For testing we will ignore optimization for selected ones.
                if objectData["selected"]:

                    if "selected" not in bge.logic.globalDict["spawns"]:
                        bge.logic.globalDict["spawns"]["selected"] = []
                    bge.logic.globalDict["spawns"]["selected"].append(objectData)
                    print("Selected", object)
                    
                object.endObject()

            # Crates            
            if str(object.name).startswith("Crate"):
                crates = bge.logic.globalDict["garage-crates"]
                crates.append(object)

            # Spawn objects
            if "LightSpawn" in object:
                name = object["LightSpawn"]

                if name == "LightStand":
                    if settings.get("poles"):
                        spawnObject = Opt.RegisterObject(object, spawnAtDistance)
                        spawnObject["name"] = object["LightSpawn"]
                        spawnObject["scaling"] = [1,1,1]
                        spawnObject["suspendDynamics"] = object.get("suspendDynamics", True)
                    else:
                        object.endObject()

                elif "Gate" in name:
                    if settings.get("fences"):
                        spawnObject = Opt.RegisterObject(object, spawnAtDistance)
                        spawnObject["name"] = object["LightSpawn"]
                        spawnObject["scaling"] = [1,1,1]
                        spawnObject["suspendDynamics"] = object.get("suspendDynamics", True)
                    else:
                        object.endObject()

                else:
                    spawnObject = Opt.RegisterObject(object, spawnAtDistance)
                    spawnObject["name"] = object["LightSpawn"]
                    spawnObject["scaling"] = [1,1,1]
                    spawnObject["suspendDynamics"] = object.get("suspendDynamics", True)

                


            # Trees
            elif "Tree" in object.name:
                if settings.get("trees"):
                    treeObject = Opt.RegisterObject(object, spawnAtDistance)
                    treeObject["lods"] = {  
                        "NormalTreeTrunk": 100,
                        #"TreeLowres": 200, # The model is ugly
                        "TreeBillboard": 5000
                    }
                    treeObject["name"] = "TreeBillboard"
                    treeObject["scaling"] /= 1.603
                else:
                    object.endObject()
                    
                    
            elif "Palm" in object.name:
                if settings.get("trees"):
                    treeObject = Opt.RegisterObject(object, spawnAtDistance)
                    treeObject["lods"] = {  
                        "NormalPalmTrunk": 100,
                        "PalmLow": 200,
                        "PalmCutout": 5000
                                        }
                    treeObject["name"] = "PalmCutout"
                    treeObject["scaling"] /= 1.445
                else:
                    object.endObject()

            # Elevators
            elif "Elevator" in object:
                elevator = {"elevator":object,
                            "radius":object.get("Radius", 5),
                            "levels":[],
                            "doors":{}}
                for level in range(10):
                    if "Level"+str(level) in object:
                        elevator["levels"].append(object["Level"+str(level)])


                for door in scene.objects:
                    if door.get("ElevatorDoor") == object["Elevator"]:


                        # Door's motion matrix
                        doorObject = GenerateDoorObject(door)
                        elevator["doors"][door["Level"]] = doorObject

                bge.logic.globalDict["elevators"][object["Elevator"]] = elevator

            elif "Door" in object:

                doorObject = GenerateDoorObject(object)
                if object["Door"] not in bge.logic.globalDict["doors"]:
                    bge.logic.globalDict["doors"][object["Door"]] = {"doors":[],
                                                                "radius":3}
                bge.logic.globalDict["doors"][object["Door"]]["doors"].append(doorObject)
                if "Radius" in object:
                    bge.logic.globalDict["doors"][object["Door"]]["radius"] = object["Radius"]
                    bge.logic.globalDict["doors"][object["Door"]]["position"] = object.position.copy()


            elif "ambiance-sound" in object:
                bge.logic.globalDict["sound-ambiances"].append(object)


        # Precalculating objects for smoothness
        preData = {"MetalGate_good":100,
                   "GatePart":100,
                   "LightStand": 50,
                   "NormalTreeTrunk":30,
                   "TreeBillboard":50,
                   "PalmCutout":32,
                   "PalmLow":16,
                   "NormalPalmTrunk":16,
                   "House_Shelf":4,
                   "Moria's Bed":1,
                   "Moria's Bed.001":1,
                   "Sparkle": 100,
                   "Smoke": 20,
                   "LightStand.Borked.Head": 5,
                   "LightStand.Borked.Tail": 5,   
                   "GatePart.Level0": 50,
                   "GatePart.Level1": 50,
                   "Fire": 200,
                   "NitroCone":5,
                   "Road Blocker": 12
                  }

        print("Precalculating... ")
        for n, object in enumerate(preData):
            for i in range( preData[object] ):
                Reuse.Create(object, selfDestructFrames = 1, selfDestructInactive = False )

        # Exiting scene
        def OnExit():
            print("\nScene Spawn Totals:\n")
            for obj in Reuse.amounts:

                precached = preData.get(obj, 0)
                cached = Reuse.amounts[obj]

                d = cached - precached
                if d > 0:
                    dc = clr["tdrd"]
                    d = "+"+str(d)
                elif d <= 0:
                    dc = clr["tdgr"]

                print("   "+clr["bold"]+obj+clr["norm"]+" Exited with"+clr["bold"], cached, clr["norm"]+", Started with"+clr["bold"], precached, clr["norm"]+", Difference"+dc, d , clr["norm"] )


            print()
            Settings.SaveGame()
            print()
                
        scene.onRemove.append(OnExit)

    
        bge.logic.globalDict["restore-physics-timer"] = 100
        bge.logic.globalDict["restore-physics"] = True

        # Running multiplayer daemon
        if settings.get("multiplayer"):
            multiplayer = threading.Thread(target=Multiplayer_Client.MainLoop)
            multiplayer.setDaemon(True)
            multiplayer.start()


        
    
        
    if bge.logic.globalDict["restore-physics-timer"]:
        bge.logic.globalDict["restore-physics-timer"] -= 2
        return
    elif bge.logic.globalDict["restore-physics"]:
        bge.logic.globalDict["restore-physics"] = False
        dani.restorePhysics()

        loading.visible = False

        #dpos = dani.position.copy()
        #dpos.z += 0.5
        #Destruction.AttatchFireBall(dpos,
        #                            dani,
        #                            1)
                                    
                                
        
    settings = bge.logic.globalDict["settings"]
        
    # Updating all the objects
    #if changedCamSurroundCars:
    Opt.ScheduleTask("Scene Chunk Updates", 0.85, Opt.UpdateScene, 
                         cam, spawnAtDistance, cam.orientation.to_euler())


    # Running the story of the game
    Script.Run(scene, dani)

    # Multiplayer scene updates
    Multiplayer_Client.SecondaryLoop()

    
    # SPAWNING CARS

    spawnedCarModels = bge.logic.globalDict["spawnedCarModels"]
    spawnedCars = len( bge.logic.globalDict["allcars"] )

    Opt.ScheduleTask("Car Updates", 0.80, Vehicle.SpawnLogic, 
                         camSurroundCars)

    #Vehicle.SpawnLogic(camSurroundCars)
    Vehicle.UnspawnLogic()
    
    #print("Cars:", spawnedCars)
    
    # SpawnsCloseEnough = []
    # for i in camSurroundCars:
    #     SpawnsCloseEnough += bge.logic.globalDict["spawns"].get(i, [])


    # for spawnPoint in SpawnsCloseEnough:

    #     inview = cam.pointInsideFrustum(spawnPoint["position"]) == cam.INSIDE
    #     distance = cam.getDistanceTo(spawnPoint["position"])

    #     # Car information
    #     carModel = spawnPoint["spawn"]
    #     carBehaviour = spawnPoint.get("npc")
    #     carRace = spawnPoint.get("race")

    #     # If this is a road npc, we want to choose a random car model
    #     if carBehaviour == "npc":

    #         carModel = numpy.random.choice(NPC_cars, p=NPC_cars_probability)    
    #         toSpawn = spawnPoint["to_spawn"] and inview and spawnAtDistance / 2 < distance < spawnAtDistance and spawnedCars < maxCars 

    #     else:    
    #         toSpawn = spawnPoint["to_spawn"] and inview and distance < spawnAtDistance

            
    #     # Trying not to add any more cars than nessesary
    #     if toSpawn and carModel in Reuse.amounts and not Reuse.reuse.get(carModel):

    #         force_add = False

    #         # Making sure that the race still works
    #         if carRace and not dani.get("race"):
    #             for car in bge.logic.globalDict["allcars"]:
    #                 if car.name == carModel:
    #                     if not car.get("active"):
    #                         Reuse.Delete(car, inactive=True)
    #                     else:
    #                         force_add = True
    #                         break
    #         if carRace:
    #             force_add = True        

    #         if not force_add:
    #             continue


    #     # Spawning a car
    #     if toSpawn:        

    #         def AddCar(carModel, carBehaviour, carRace, spawnPoint):

    #             print("Spawning Car at distance from Dani:", dani.getDistanceTo(spawnPoint["position"]))
                
    #             # Making car
    #             car, new = Reuse.Create(carModel, declarenew=True)

    #             # If you claimed the car, one more car!
    #             if car["active"]:
    #                 car, new = Reuse.Create(carModel, declarenew=True)
                    
    #             Vehicle.Spawn(car,
    #                           spawnPoint["position"],
    #                           spawnPoint["orientation"],
    #                           color="pallete")
                    
    #             if carBehaviour == "npc":
    #                 car.setLinearVelocity([0,-15,0], True)
    #             else:
    #                 car["spawnPoint"] = spawnPoint        

    #             car["npc"] = carBehaviour
    #             car["anger"] = random.random()
    #             car["enemy"] = ""
    #             car["chased"] = False
    #             car["engine"] = 0
    #             car["launchtime"] = 300

    #             if carRace:

    #                 car["race"] = carRace
    #                 car["racing"] = False
    #                 car["checkpoint"] = 0
    #                 car["rescue"] = bge.logic.globalDict["races"][carRace]["starters"][0]["location"]
    #                 car["lap"] = 0
    #                 car["blown"] = False
    #                 bge.logic.globalDict["races"][carRace]["racers"].append(car)
    #                 bge.logic.globalDict["races"][carRace]["racer_spawns"].append(spawnPoint)

    #             # Cars are deformable during game, so we will need to restore them
    #             Vehicle.Fix(car)




    #         # Scheduling it for a task
    #         if not Reuse.reuse.get(carModel):
    #             Opt.ScheduleTask("Adding Car ["+carModel+"]", 0.80, AddCar, 
    #                     carModel, carBehaviour, carRace, spawnPoint)
    #         else:
    #             AddCar(carModel, carBehaviour, carRace, spawnPoint)

    #         spawnPoint["to_spawn"] = False
    #         spawnPoint["to_spawn_timer"] = 500

    #     # If player is standing still keep spawning cars near by
    #     if not spawnPoint["to_spawn"] and spawnPoint["to_spawn_timer"]:
    #         spawnPoint["to_spawn_timer"] -= 1
    #     elif not spawnPoint["to_spawn"] and carBehaviour == "npc":
    #         spawnPoint["to_spawn"] = True


    # # Removing cars that were deleted
    # for car in bge.logic.globalDict["allcars"]:
    #     try:
    #         car.position
    #     except:
    #         bge.logic.globalDict["allcars"].remove(car)


    # ELEVATORS

    # Seeing on what level dani is.
    def atLevel(object):
        levelis = 0
        closest = 1000
        for n, i in enumerate(elevator["levels"]):
            dist = object.position[2] - i
            if dist < 0: dist *= -1

            if dist < closest:
                closest = dist
                levelis = n
        return levelis

    for elevatorname in bge.logic.globalDict["elevators"]:

        elevator = bge.logic.globalDict["elevators"][elevatorname]

        # Seeing if Dani is anywhere near an elevator
        eloc = elevator["elevator"].position.copy()
        eloc[2] = dani.position[2]

        if dani.getDistanceTo(eloc) < elevator["radius"]:



            # If dani not at the same level as the elevator
            if atLevel(dani) != atLevel(elevator["elevator"]):
                toprint = "Press L to call Elevator."
                if toprint not in bge.logic.globalDict.get("done-prints",[]):
                    bge.logic.globalDict["print"] = toprint

                # Pressing L to call it
                if 34 in bge.logic.globalDict["keys"]:
                    elevator["going_to"] = atLevel(dani)
                    elevator["closing_doors"] = True

            else:
                toprint = "Press 0-"+str(len(elevator["levels"])-1)+" activate the elevator."
                if toprint not in bge.logic.globalDict["done-prints"]:
                    bge.logic.globalDict["print"] = toprint

                numbers = [13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
                for num in numbers:
                    if num in bge.logic.globalDict["keys"] and numbers.index(num) < len(elevator["levels"]):
                        elevator["going_to"] = numbers.index(num)
                        elevator["closing_doors"] = True

        # Elevator coming
        if elevator.get("going_to") != None and not elevator.get("closing_doors"):
            destination = elevator["levels"][elevator["going_to"]]
            position = elevator["elevator"].position[2]

            if destination < position:
                elevator["elevator"].position[2] -= 0.1 * elevator["elevator"].get("speedFactor", 1)
            else:    
                elevator["elevator"].position[2] += 0.1 * elevator["elevator"].get("speedFactor", 1)

            if round(destination, 1) == round(position, 1):
                elevator["going_to"] = None
                elevator["closing_doors"] = True

        # Doors   
        if elevator.get("closing_doors"):

            cancelBucket = []

            # If elevator is moving, all doors should be closed.
            if elevator.get("going_to") != None:
                openLevel = -1
            else:
                openLevel = atLevel(elevator["elevator"])

            for level in elevator["doors"]:

                door = elevator["doors"][level]

                # The elevator is at the current door's level,
                # the door should be open.    
                if level == openLevel:
                    openClose = "Open"
                else:
                    openClose = "Closed"

                onTarget = OpenCloseDoor(door, openClose)

                cancelBucket.append(onTarget)

            if all(cancelBucket):
                elevator["closing_doors"] = False

    # DOOR ( in general )

    for doorname in bge.logic.globalDict["doors"]:

        doors = bge.logic.globalDict["doors"][doorname]

        if doors.get("doOpenClose"):
            cancelBucket = []
            for door in doors["doors"]:
                onTarget = OpenCloseDoor(door, doors["OpenClosed"])
                cancelBucket.append(onTarget)
            if all(cancelBucket):
                doors["doOpenClose"] = False

        else:



            if doors.get("OpenClosed", "Closed") == "Closed" and dani.getDistanceTo(doors["position"]) < doors["radius"]:

                doors["OpenClosed"] = "Open"
                doors["doOpenClose"] = True

            elif doors.get("OpenClosed") == "Open" and dani.getDistanceTo(doors["position"]) > doors["radius"]:

                doors["OpenClosed"] = "Closed"
                doors["doOpenClose"] = True

            # Sound
            if doors.get("doOpenClose"):

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

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

                    s = "//sfx/home_door.ogg"
                    code = str(n)+s
                    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 = door["door"].position
                    sound["play"].velocity = door["door"].getLinearVelocity()
                    sound["play"].relative = False
                    sound["play"].distance_maximum = 100
                    sound["play"].distance_reference = 1
                    sound["play"].attenuation = 1
                    sound["play"].pitch = random.uniform(1, 2)
                    sound["play"].volume = 0.3


    # WHEEL UP DOWN

    cam_parent = scene.objects["DaniCam_Parent"]

    wheelup = cont.sensors["wheel_up"]
    wheeldown = cont.sensors["wheel_down"]
    if wheelup.status:
        cam_parent.scaling /= 1.1
    elif wheeldown.status and cam_parent.scaling[0] < 20:
        cam_parent.scaling *= 1.1

    # CHEAT CODES

    cheat_spawn = scene.objects["Dani_Cheater"]
    
    keys = bge.logic.keyboard

    bge.logic.globalDict["keys"] = []
    for keyn in keys.inputs:
        key = keys.inputs[keyn]
        if key.active:
            bge.logic.globalDict["keys"].append(keyn)
            
    keys = bge.logic.mouse
    bge.logic.globalDict["mouse"] = []
    for keyn in keys.inputs:
        key = keys.inputs[keyn]
        if key.active:
            bge.logic.globalDict["mouse"].append(keyn)

    keys = bge.logic.globalDict["keys"]
            
    # Control release
    if "mouse-active" not in bge.logic.globalDict:
        bge.logic.globalDict["mouse-active"] = True
    if keycodes["Tab"] in keys and not bge.logic.globalDict.get("mouse-active-timer"):
        bge.logic.globalDict["mouse-active"] = not bge.logic.globalDict["mouse-active"]
        bge.logic.globalDict["mouse-active-timer"] = 10
    if bge.logic.globalDict.get("mouse-active-timer"):
        bge.logic.globalDict["mouse-active-timer"] -= 1
    
        
    

    
    
    if keys and settings.get("dev-cheats"):

        #print("Key pressed code:",keys.events)

        if "cheat" not in bge.logic.globalDict:
            bge.logic.globalDict["cheat"] = [0]

        for i in keys:
            if i != bge.logic.globalDict["cheat"][-1]:
                bge.logic.globalDict["cheat"].append(i)

        # SPAWN CHEATS

        cheats = {
            "NeonSpeedsterBox":  "NEONSPEDSTER",
            "RedKissBox":        "REDKIS",
            "DarkShadowBox":     "DARKSHADOW",
            "TruckBox":          "THETRUCK",
            "HatchBack01Box":    "HATCHBACK1",
        }

        for i in cheats:
            code = bge.logic.globalDict["cheat"][-(len(cheats[i])):]

            numericalcode = []
            for letter in cheats[i]:
                numericalcode.append(keycodes[letter])

            if code == numericalcode:
                print("Spawning:", i)

                car = Vehicle.Spawn(i,
                                    cheat_spawn.position,
                                    cheat_spawn.orientation,
                                    color="pallete")
                
                                
                bge.logic.globalDict["cheat"] = [0]


                bge.logic.globalDict["print"] = "A Vehicle Is Waiting For You, Sir."
                bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

        teleportations = [
            #[[-75.19, 1028, 0],         "RACETRACK"],
            [[-68.35, 946.6, 4.738],    "RACETRACK"],
            [[-752.78, -937.4, 411.08], "HOUSE"],
            [[-755.1, -1041, 405.8],    "GARAGE"],
            [[-792.10,-980.60, 405.50], "PARKING"],
            [[-937.3, -277.1, 257.7],   "MANSION"],
            [[-254.4, -508.1, 189.2],   "BIGBUILDING"],
            [[232.9, -120.4, 42.22],    "NEARSEA"],
            [[229.9, -811.9, 175.3],    "PITOSTATUE"],
            [[659.4, -616.1,202.9],     "CONSTRUCTION"],
            [[-2.122, 1322, 99.85],     "TOWER0"],      
            [[-172.4, 1147, 99.68],     "TOWER1"],      
            [[200, 1204, 100],          "TOWER2"],            
            [[12.19, 1127, 139.3],      "TOWER3"],      
            [[148.4, 941.7, 98.83],     "TOWER4"],       
            [[-725, -984.6, 409.2],     "CINEMA"]     
        ]

        for i in teleportations:
            code = bge.logic.globalDict["cheat"][-(len(i[1])):]

            numericalcode = []
            for letter in i[1]:
                numericalcode.append(keycodes[letter])

            if code == numericalcode:
                if not dani.get("driving"):
                    Character_Controll.ChangeCameraTarget(dani.children["Dani_cam_Target"], 100, [1,1,1])
                    dani.position = i[0]

                else:
                    dani["driving"].position = i[0]
                
                bge.logic.globalDict["cheat"] = [0]

                bge.logic.globalDict["print"] = "Teleportation Finished, Sir."
                bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

        # Money code
        code = bge.logic.globalDict["cheat"][-(len("givememoney")):]
        if code == [29, 31, 44, 27, 35, 27, 35, 37, 36, 27, 47]:
            Money.Recieve(569)
            bge.logic.globalDict["cheat"] = [0]
            bge.logic.globalDict["print"] = "Enjoy The Money, Sir."
                
        # Puruit code
        code = bge.logic.globalDict["cheat"][-(len("pursuit")):]
        if code == [38, 43, 40, 41, 43, 31, 42]:
            bge.logic.globalDict["pursuit-cheat"] = not bge.logic.globalDict["pursuit-cheat"]
            bge.logic.globalDict["cheat"] = [0]

            if bge.logic.globalDict["pursuit-cheat"]:
                bge.logic.globalDict["print"] = "Everybody Hates You, Sir. At Your Request, Sir."
            else:
                bge.logic.globalDict["print"] = "Everybody Stopped Hating You, Sir."

            bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

        # Derby code
        code = bge.logic.globalDict["cheat"][-(len("derby")):]
        if code == [26, 27, 40, 24, 47]:
            bge.logic.globalDict["derby-cheat"] = not bge.logic.globalDict["derby-cheat"]
            bge.logic.globalDict["cheat"] = [0]

            if bge.logic.globalDict["derby-cheat"]:
                bge.logic.globalDict["print"] = "Everybody Is Crazy, Sir."
            else:
                bge.logic.globalDict["print"] = "Everybody Stopped Being Crazy, Sir."


            bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding



        # Gravity code
        code = bge.logic.globalDict["cheat"][-(len("gravity")):]
        if code == [29, 40, 23, 44, 31, 42, 47]:
            bge.logic.globalDict["gravity"] = not bge.logic.globalDict["gravity"]
            if bge.logic.globalDict["gravity"]:
                bge.constraints.setGravity(0, 0, -9.8)
            else:
                bge.constraints.setGravity(0, 0, 0)

            bge.logic.globalDict["cheat"] = [0]

            if bge.logic.globalDict["gravity"]:
                bge.logic.globalDict["print"] = "We're Back To Earth, Sir."
            else:
                bge.logic.globalDict["print"] = "We're Up In Space, Sir."

            bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

        # Explosion code
        code = bge.logic.globalDict["cheat"][-(len("bom")):]
        if code == [24,37,35]:   
            expos = cheat_spawn.position.copy()
            expos[2] = dani.position[2]
            Destruction.Explosion(expos, mass=10, size=10)

            bge.logic.globalDict["print"] = "Boom! , Sir."
            bge.logic.globalDict["cheat"] = [0]

            # no ding here, the boom is enough

        # Fix car code:
        code = bge.logic.globalDict["cheat"][-(len("fixcar")):]
        if code == [28, 31, 46, 25, 23, 40]:
            car = dani.get("driving")
            Vehicle.Fix(car)
            bge.logic.globalDict["print"] = "The Car is like New, Sir."
            bge.logic.globalDict["cheat"] = [0]
            bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

        # Color Car code:
        code = bge.logic.globalDict["cheat"][-(len("colorcar")):]
        if code == [25, 37, 34, 37, 40, 25, 23, 40]:
            car = dani.get("driving")
            Vehicle.SmartColor(car, color="pallete")
            bge.logic.globalDict["print"] = "The Car is colored, Sir."
            bge.logic.globalDict["cheat"] = [0]
            bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

        # Color Car code:
        code = bge.logic.globalDict["cheat"][-(len("carnpc")):]
        if code == [25, 23, 40, 36, 38, 25]:
            car = dani.get("driving")
            car["target"] = []
            car["npc"]    = "npc"
            car["active"] = False
            bge.logic.globalDict["print"] = "The Car is NPC, Sir."
            bge.logic.globalDict["cheat"] = [0]
            bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

            
    # RACES
    for racename in bge.logic.globalDict["races"]:
        race = bge.logic.globalDict["races"][racename]

        pointer = scene.objects["PointingArrow"]
        posindicator = scene.objects["Dani_Pos_Indicator"]
        timeindicator = scene.objects["Dani_Time_Indicator"]
        lapindicator = scene.objects["Dani_Lap_Indicator"]

        reward = race.get("reward", 1000)
        bid    = reward / (race.get("amount-racers", 1)+1)

        after = race.get("after")
        during = race.get("during")
        duringcheck = dani.get("race") == after and Script.Story.get(during)
        aftercheck = duringcheck or after in Script.Story["passed"] or not after
       
        

        
        # Show them on the map
        if ( not dani.get("race") and aftercheck and Money.Have(bid)) or duringcheck:

            Map.Show(race["starters"][0]["location"], icon="Map_Circle", color=[0.01,0.01,1], ID=racename)
        
        if not race["started"] and ( ( not dani.get("race") and Money.Have(bid) ) or duringcheck  ) and race["racers"] and aftercheck:

            for starter in race["starters"]:

                if not starter["cylinder"] and dani.getDistanceTo(starter["location"]) < spawnAtDistance:
                    starter["cylinder"] = Reuse.Create("Starter.Cylinder")
                    starter["cylinder"].position = starter["location"]
                    starter["cylinder"].orientation = starter["rotation"]
                    starter["cylinder"].scaling[0] = starter["radius"]
                    starter["cylinder"].scaling[1] = starter["radius"]

                if dani.getDistanceTo(starter["location"]) < starter["radius"]:
                    NPC_type_cars = bge.logic.globalDict.get("NPC_type_cars", {})
                    cartype = race.get("type")
                    if dani["driving"] and dani["driving"].name in NPC_type_cars.get(cartype,[]):
                        if not duringcheck:
                            bge.logic.globalDict["print"] = "Press R to start the race.\nEverybody bids $"+str(int(bid))+"\nWinner gets $"+str(int(race.get("reward")))
                        else:
                            bge.logic.globalDict["print"] = "Press R to start the race."

                        if keycodes["R"] in keys:

                            if not duringcheck:
                                Money.Pay(bid)

                            race["started"] = True
                            Reuse.Delete(starter["cylinder"])                        
                            starter["cylinder"] = None

                            race["start-time"] = bge.logic.getRealTime()

                            for racer in race["racers"]:
                                racer["racing"] = True
                                racer["launchtime"] = 100
                                racer["race"] = racename
                                
                                # Testing
                                racer["npc"] = "racer"
                                racer["active"] = False

                                # Beam of energy
                                racer["beam"] = scene.addObject("Racer.Indicator", "Racer.Indicator")

                            dani["race"] = racename
                            dani["checkpoint"] = 0   
                            dani["lap"] = 0


                            posindicator.visible = True
                            pointer.visible = True
                            timeindicator.visible = True
                            lapindicator.visible = True

                            bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding

                    else:
                        bge.logic.globalDict["print"] = "Come here with a "+cartype+" to race."


        # If this race is going
        elif dani.get("race") == racename:
            nextcheck = race["checkpoints"][dani["checkpoint"]]
            nextcheckpoint = race["checkpoints"][ ( dani.get("checkpoint") + 1 ) % len(race["checkpoints"]) ]

            # Alight the arrow to the checkpoint
            tocheck = pointer.getVectTo(nextcheck["location"])
            pointer.alignAxisToVect(tocheck[1], 1, 0.1)

            # Alighn the arrow to the next checkpoint if close enough to it.        
            if cam.pointInsideFrustum(nextcheck["location"]) == cam.INSIDE:
                tonextcheck = pointer.getVectTo(nextcheckpoint["location"])
                pointer.alignAxisToVect(tonextcheck[1], 1, min(1, max(0, 1-(tocheck[0]/500))) ** 5 ) 

            # Align the arrow vertically
            pointer.alignAxisToVect( (0,0,1), 2, 1.0 )

            # Time of the race
            currentRaceTime = bge.logic.getRealTime() - race["start-time"]
            formattedTime = str(datetime.timedelta(seconds=currentRaceTime))[:-4]
            while formattedTime.startswith("0") or formattedTime.startswith(":"):
                formattedTime = formattedTime[1:]
            if formattedTime.startswith("."):
                formattedTime = "0"+formattedTime

            timeindicator["Text"] = formattedTime
            lapindicator["Text"] = str(dani["lap"]+1)+"/"+str(race["laps"])

            if not nextcheck["cylinder"] and dani.getDistanceTo(nextcheck["location"]) < spawnAtDistance:
                nextcheck["cylinder"] = Reuse.Create("Tag.Cylinder")
                nextcheck["cylinder"].position = nextcheck["location"]
                nextcheck["cylinder"].orientation = nextcheck["rotation"]
                nextcheck["cylinder"].scaling[0] = nextcheck["radius"] * 0.8
                nextcheck["cylinder"].scaling[1] = nextcheck["radius"] * 0.8

            Map.Show(nextcheck["location"], icon="Map_Circle", color=[1,0.3,0], ID="RaceCheckpoint")
            Map.Show(nextcheckpoint["location"], icon="Map_Circle", color=[1,0.1,0], ID="RaceCheckpoint2")

            if dani.getDistanceTo(nextcheck["location"]) < nextcheck["radius"]*1.5:

                if not nextcheck.get("OnLoop"):
                    dani["prevrescue"] = dani.get("rescue")
                    dani["rescue"] = nextcheck["location"]
                    dani["rescueTo"] = dani["checkpoint"]

                Reuse.Delete(nextcheck["cylinder"])
                nextcheck["cylinder"] = None

                # Logging speed for better AI
                #if "raceData" not in race:
                nextcheck["speed"] = -dani["driving"].localLinearVelocity[1] 
                nextcheck["time"] = currentRaceTime
                nextcheck["rot"] = list(dani["driving"].orientation.to_euler())
                #if race["positions"].index(dani) == 0:
                #    race["raceData"]["speed"][dani["checkpoint"]] = -dani["driving"].localLinearVelocity[1]
                #    race["raceData"]["time"][dani["checkpoint"]] = currentRaceTime
                #    race["raceData"]["rotz"][dani["checkpoint"]] = dani["driving"].orientation.to_euler()[2]

                dani["checkpoint"] += 1
                bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["checkpoint"]["sound"]) # Play a Ding

            if dani["checkpoint"] == len(race["checkpoints"]):
                dani["checkpoint"] = 0
                dani["lap"] += 1

                print('"'+racename+'" : {')
                print('"speed": [')
                for c in race["checkpoints"]:
                    print(c.get("speed"),",")
                print('],\n"time":[')
                for c in race["checkpoints"]:
                    print(c.get("time"),",")
                print('],\n"rot":[')
                for c in race["checkpoints"]:
                    print(c.get("rot"),",")
                print(']},')



            # If finished race
            if dani["lap"] == race["laps"]:# or not dani["driving"]:



                dani["race"] = None
                race["started"] = False

                posindicator.visible = False
                timeindicator.visible = False
                pointer.visible = False
                lapindicator.visible = False


                race["racer_spawns"] = []

                for racer in race["racers"]:
                    racer["npc"] = "npc"
                    racer["beam"].endObject()

                race["racers"] = []

                if nextcheck["cylinder"]:
                    Reuse.Delete(nextcheck["cylinder"])
                    nextcheck["cylinder"] = None

                # If won
                if race["positions"].index(dani) == 0:
                    bge.logic.globalDict["print"] = "You've Won This Race!\nTime: "+formattedTime+"\nReward: $"+str(int(race.get("reward", 0)))
                    Money.Recieve(race.get("reward", 0))
                # If finished
                else:
                    bge.logic.globalDict["print"] = "You finished number "+str(race["positions"].index(dani)+1) + " Time: "+formattedTime

                Script.StatusText(" ")
                continue


            positions = []
            for racer in race["racers"]:
                racer["into_race"] = [race["laps"] - racer["lap"], len(race["checkpoints"]) - racer["checkpoint"], racer.getDistanceTo(race["checkpoints"][racer["checkpoint"]]["location"])]
                positions.append([racer["into_race"], racer])

            dani["into_race"] = [race["laps"] - dani["lap"], len(race["checkpoints"]) - dani["checkpoint"], dani.getDistanceTo(race["checkpoints"][dani["checkpoint"]]["location"])]
            positions.append([dani["into_race"], dani])

            positions = sorted(positions)
            race["positions"] = []
            for position, racer in positions:
                race["positions"].append(racer)

            posindicator["Text"] = str(race["positions"].index(dani)+1)

            # The positions text ( with Dani's car being Bold and yellow )
            
            pt = ""
            bold = []
            for n, p in enumerate(race["positions"]):
                n += 1
                if p == dani:
                    try: t = dani.get("driving", "On Foot")["specs"]["name"]+" | "+str(n)+"\n"
                    except: t = str(dani.get("driving", "On Foot"))+" | "+str(n)+"\n"

                    bold = range(len(pt), len(pt)+len(t))
                    
                else:
                    try: t = p["specs"]["name"]+" | "+str(n)+"\n"
                    except: t = str(p)+" | "+str(n)+"\n"
                pt = pt + t
            Script.StatusText(pt, bold)
                    
                

    # Messages

    if "done-prints" not in bge.logic.globalDict:
        bge.logic.globalDict["done-prints"] = []

    if bge.logic.globalDict.get("print") and bge.logic.globalDict.get("last_print") != bge.logic.globalDict["print"]:

        print()
        for line in bge.logic.globalDict["print"].split("\n"):
            print("    "+clr["bold"]+clr["tdyl"]+line+clr["norm"])

        print()        
        
        #scene.objects["Messages"]["Text"] = str(bge.logic.globalDict.get("print"))
        scene.objects["Messages"].blenderObject.data.body = str(bge.logic.globalDict.get("print"))
        scene.objects["Messages"].stopAction()
        scene.objects["Messages"].playAction("MessagesAction", 0, 100)

        bge.logic.globalDict["print-change-size"] = True

        
        
        bge.logic.globalDict["last_print"] = bge.logic.globalDict["print"]

        
        if bge.logic.globalDict["print"] not in bge.logic.globalDict["done-prints"]:
            bge.logic.globalDict["done-prints"].append(bge.logic.globalDict["print"])

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

    elif bge.logic.globalDict.get("print-change-size"):

        # Changing the size of the element under the text
        # This is done in a different frame, because we need
        # to let it update the depsgraph with the text dimensions.

        camscale = scene.objects["DaniCam_Parent"].scaling[0]
                
        textSize = scene.objects["Messages"].blenderObject.dimensions
        thingSize = scene.objects["Message_Background"].blenderObject.dimensions 
        thingScale =  scene.objects["Message_Background"].worldScale

        textSize = mathutils.Vector((
            textSize.x + ( 0.04 * camscale ),
            textSize.y + ( 0.02 * camscale ),
            textSize.z
        ))
        
        thingSize = mathutils.Vector((
            thingSize.x / thingScale.x,
            thingSize.y / thingScale.y,
            thingSize.z / thingScale.z
        ))


        thingScale = mathutils.Vector((
            textSize.x / thingSize.x,
            textSize.y / thingSize.y,
            1.0 # It doesn't matter, and making it 0 collapes the object
        ))
        
        scene.objects["Message_Background"].worldScale = thingScale 
        point = Vehicle.RelativePoint(scene.objects["Messages"],
                                      (-0.007 * camscale,
                                       (-0.010 * camscale) + (textSize.y / 2),
                                       -0.001))
        scene.objects["Message_Background"].worldPosition = point
        
        bge.logic.globalDict["print-change-size"] = False


    # SOUND

    #print(dir(aud))

    if "SoundDevice" not in bge.logic.globalDict:
        bge.logic.globalDict["SoundDevice"] = aud.Device()
        bge.logic.globalDict["SoundDevice"].distance_model = aud.DISTANCE_MODEL_INVERSE_CLAMPED
        bge.logic.globalDict["sounds"] = {}

        bge.logic.globalDict["sounds"]["active"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/active.ogg")),
                                                 "play":None}

        # Angry pursuit sounds

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

        for i in range(8):
            i += 1
            i = str(i)
            if len(i) < 2:
                i = "0"+i
            i = "//sfx/voices/angry_pursuit/start_"+i+".ogg"
            bge.logic.globalDict["sounds"][i] = {"sound":aud.Sound(bge.logic.expandPath(i)),
                                                 "play":None}
            bge.logic.globalDict["sounds_angry_start"].append(i)


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

        for i in range(5):
            i += 1
            i = str(i)
            if len(i) < 2:
                i = "0"+i
            i = "//sfx/voices/angry_pursuit/hit_"+i+".ogg"
            bge.logic.globalDict["sounds"][i] = {"sound":aud.Sound(bge.logic.expandPath(i)),
                                                 "play":None}
            bge.logic.globalDict["sounds_angry_hit"].append(i)

        # Dani voices

        bge.logic.globalDict["sounds"]["dani_yelling"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/yelling.ogg")),
                                                          "play":None}
        bge.logic.globalDict["sounds"]["dani_entering"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/entering.ogg")),
                                                          "play":None}
        bge.logic.globalDict["sounds"]["dani_ohno"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohno.ogg")),
                                                          "play":None}
        bge.logic.globalDict["sounds"]["dani_ohnoindeed"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohnoindeed.ogg")),
                                                          "play":None}
        bge.logic.globalDict["sounds"]["dani_wearesinking"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/wearesinking.ogg")),
                                                          "play":None}
        bge.logic.globalDict["sounds"]["dani_ohgod"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohgod.ogg")),
                                                          "play":None}

        bge.logic.globalDict["sounds"]["dani_jump"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/jump.ogg")),
                                                          "play":None}

        bge.logic.globalDict["sounds"]["dani_land"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/land.ogg")),
                                                          "play":None}
        bge.logic.globalDict["sounds"]["dani_walk"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/walk.ogg")),
                                                          "play":None}

        # Jack voices
        bge.logic.globalDict["JackBoxVoices"] = {"crash":[],
                                               "near" :[],
                                               "speed":[]}

        
        
        JV = bge.logic.globalDict["JackBoxVoices"]
        JF = bge.logic.expandPath("//sfx/voices/jack/")
        for t in ["crash", "near", "speed"]:
            for f in os.listdir(JF):
                if f.startswith(t) and f.endswith(".ogg"):
                    name = f.replace(".ogg", "")
                    bge.logic.globalDict["sounds"]["jack_"+name] = {"sound":aud.Sound(JF+f),
                                                          "play":None}
                    JV[t].append(bge.logic.globalDict["sounds"]["jack_"+name])


        # Car Sounds

        bge.logic.globalDict["sounds"]["door"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/door.ogg")),
                                                          "play":None}
        bge.logic.globalDict["sounds"]["tire_pop"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/tire_pop.ogg")),
                                                          "play":None}

        # Race sounds

        bge.logic.globalDict["sounds"]["checkpoint"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/checkpoint.ogg")),
                                                          "play":None}


    device = bge.logic.globalDict["SoundDevice"]
    device.listener_location = scene.active_camera.worldPosition
    device.listener_orientation = scene.active_camera.worldOrientation.to_quaternion()
    device.listener_velocity = scene.active_camera.getLinearVelocity()

    for i in bge.logic.globalDict["sound-ambiances"]:
        s = i["ambiance-sound"]
        #s = "//sfx/ambiance/forest.ogg" 
        if s not in bge.logic.globalDict["sounds"]:
            bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)),
                                                 "play":None}

        sound = bge.logic.globalDict["sounds"][s]

        if not sound["play"] or not sound["play"].status:
            sound["play"] =  device.play(sound["sound"])

        scale = i.scaling[0]
        distance = i.getDistanceTo(dani)

        if inCinema:
            sound["inCinema"] = sound.get("inCinema", 1) * 0.95
        elif sound.get("inCinema", 1) < 1:
            if sound["inCinema"] < 0.1:
                sound["inCinema"] = 0.1
            sound["inCinema"] = sound.get("inCinema", 1) * 1.05
        sound["play"].volume = max(0, min(1, (1-(distance / scale ))*5))*i.get("volume", 1) * sound.get("inCinema", 1)

    ########## LODS FOR CHUNKS OF THE ROAD #############
    if "LODchunks" not in bge.logic.globalDict:
        bge.logic.globalDict["LODchunks"] = {"TheRacetrack":{
                                                "object":scene.objects["TheRacetrack"],
                                                "high":"TheRacetrackHigh",
                                                #"low":"TheRacetrackLow",
                                                "radius":1000,
                                                "now":"high",
                                                "name":"Papses Racetrack"
                                                },

                                            "TheCity":{
                                                "object":scene.objects["TheCity"],
                                                "high":"TheCityHigh",
                                                #"low":"TheCityLow",
                                                "radius":1500,
                                                "now":"high",
                                                "name":"Dune Town"
                                                },    

                                            "TheHouse":{
                                                "object":scene.objects["TheHouse"],
                                                "high":"TheHouseGood",
                                                "low":"TheHouseBorked",
                                                "radius":200,
                                                "now":"high",
                                                "name":"Looparound 8\nDani's Home"
                                                },  


                                            }

    chunks = bge.logic.globalDict["LODchunks"]

    for chunkname in chunks:
        chunk = chunks[chunkname]

        if  chunk["now"] == "low" and dani.getDistanceTo(chunk["object"]) < chunk["radius"]:
            print(consoleForm(chunk["object"]), "is now highres.")
            if "low" in chunk:
                chunk["object"].replaceMesh(chunk["high"])
            else:
                chunk["object"].visible = True
            chunk["object"].restorePhysics()
            chunk["now"] = "high"

            bge.logic.globalDict["print"] = chunk["name"]

        elif chunk["now"] == "high" and dani.getDistanceTo(chunk["object"]) > chunk["radius"]:
            print(consoleForm(chunk["object"]), "is now lowres.")
            if "low" in chunk:
                chunk["object"].replaceMesh(chunk["low"])
            else:
                chunk["object"].visible = False
                
            if not dani.get("race"):
                chunk["object"].suspendPhysics()
                
            chunk["now"] = "low"

        
    ############ Camera UI ###########
    
    Map.UpdateMapUI()

    #Map.Show([0,0,0], icon="Map_Circle")

    ############ GARAGE ##########

    # The idea is that all cars inside of the garage will
    # stay in the garage.

    garage = scene.objects["GarageColider"]
    garageDistance = 70

    if dani.getDistanceTo(garage) > garageDistance:
        Garage.Encode()
    elif dani.getDistanceTo(garage) < garageDistance - 10:
        Garage.Decode()

    # Crates
    # There are crates in the house, in the garage. They are rigid body objects.
    # the problem is that when you go far enough away, the ground beneath them
    # stops being solid, so we need to deactiave their physics.

    crates = bge.logic.globalDict["garage-crates"]
    for crate in crates:
        if dani.getDistanceTo(crate) > 100:
            crate.suspendPhysics()
        elif dani.getDistanceTo(crate) < 90:
            crate.restorePhysics()

    # Tools
    Tools.MainLoop()
    Garage.Computer()


    ################ DAY NIGHT CYCLE ################


    clock = scene.objects["Dani_Clock_Indicator"]
    bge.logic.globalDict["time"] = bge.logic.globalDict["start-time"] + ( bge.logic.getRealTime() / 3600 * settings.get("clockspeed", 20) )
    while bge.logic.globalDict["time"] > 24:
        bge.logic.globalDict["time"] -= 24
    timeis = bge.logic.globalDict["time"]
    
    # Printing the clock
    hrtext = str(int(timeis))
    if len(hrtext) < 2: hrtext = "0" +hrtext
    mntext = str(int((timeis-int(timeis))*60))
    if len(mntext) < 2: mntext =  "0" + mntext
    clock["Text"] = hrtext+":"+mntext

    # Getting the time outthere
    timefactor = timeis / 24


    # we want 100 sunlight to be at 12 AM not at 24 hours
    #timefactor -= 0.5
    #if timefactor < 0: timefactor *= -1
    #timefactor *= -2
    #timefactor += 1
    
    timecontroller = scene.objects["TimeController"]
    timecontroller.position.x = ( timefactor * 100 ) - 100

    # Sun rotation
    sun = scene.objects["Sun"]
    sunrot = sun.worldOrientation.to_euler()
    sunrot.y = ( -timefactor * math.pi * 2 ) +  math.pi
    sun.worldOrientation = sunrot

    # trying to limit the next operations a bit
    if not bge.logic.globalDict.get("time-update-timer"):

    
        TheCity = scene.objects["TheCity"]
        TheCity.blenderObject["time"] = timefactor
        TheCity.applyRotation((0,0,0))

        TheRacetrack = scene.objects["TheRacetrack"]
        TheRacetrack.blenderObject["time"] = timefactor
        TheRacetrack.applyRotation((0,0,0))

        Water = scene.objects["Water"]
        Water.blenderObject["time"] = timefactor
        Water.applyRotation((0,0,0))

        # we want 100 sunlight to be at 12 AM not at 24 hours
        timefactor -= 0.5
        if timefactor < 0: timefactor *= -1
        timefactor *= -2
        timefactor += 1

        sun.blenderObject.data.energy = 3 * timefactor

        bge.logic.globalDict["time-update-timer"] = 500
        
    else:
        bge.logic.globalDict["time-update-timer"] -= 1


    # Background racetrack / city backdrop calcualtion

    city_at = 137
    racetrack_at = 630
    now_at = float(cam.worldPosition.y)

    if now_at > racetrack_at: crFactor = 1.0
    elif now_at < city_at: crFactor = 0.0
    else:
        crFactor = (now_at - city_at) / ( racetrack_at - city_at )
    timecontroller.position.y = ( crFactor * 100 ) - 100
    

    # Moving the background up and down depending on the position of
    # the camera.

    max_z = 39
    min_z = -12
    now_z = float(cam.worldPosition.z)
    move_z = 14

    if now_z > max_z: mzFactor = 0.0
    elif now_z < min_z:    mzFactor = 1.0
    else:
        mzFactor = 1- ((now_z - min_z) / ( max_z - min_z ))
    timecontroller.position.z = ( mzFactor * move_z )

    

    # Now to position the racetrack properly we want to rotate it

    p1 = [7.39, -805.686]
    p2 = [30  , 95.5932]
    cur_x = float(cam.worldPosition.x)
    rotFactor = (cur_x - p1[1]) /  (p2[1] - p1[1]) * ( p2[0] - p1[0] ) + p1[0]
    rotationcontroller = scene.objects["Racetrack_rotaion"]
    rotationcontroller.position.x = rotFactor