# 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 import traceback 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) elif NetworkControlled(car): pass # If nobody controls the car. else: IdleBraking(car) # Network control if NetworkControlled(car): NetworkControl(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) if car.get("handbraking"): HandBraking(car) # 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) # Centering the cursor when pressing RMB mouseinput = bge.logic.globalDict["mouse"] if mousecodes["RMB"] in mouseinput: Mouse.CenterCursor() # LMB brakes if mousecodes["LMB"] in mouseinput: v = -car.localLinearVelocity[1] ActivateBreaks(car, v, True) # 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 ) ** 2 ) * 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 ) ** 2 ) * 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. strenght = factor if strenght < 0: strenght *= -1 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*strenght, n) else: getCarPhysics(car).applyBraking(0, n) # Stablizing the brakes if stable and car["specs"].get("stableBrake", True): 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"] + car["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") and not NetworkControlled(car): 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 not car.get("handbraking", 0): S = car["specs"].get("turnFake", 20) if v > 0.01 and vo < 0: car.applyTorque([0,0,car["turn"] * S * car.mass],True) elif v > 0.01 and vo > 0: car.applyTorque([0,0,car["turn"] * -S * 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() settings = bge.logic.globalDict.get("settings", {}) if car["turn"] > 0: car["turn"] *= -0.5 car["turn"] -= 0.005 * toFPS * settings.get("turn", 1.0) car["turning"]= True def TurnLeft(car): toFPS = Opt.ToFPS() settings = bge.logic.globalDict.get("settings", {}) if car["turn"] < 0: car["turn"] *= -0.5 car["turn"] += 0.005 * toFPS * settings.get("turn", 1.0) car["turning"]= True def HandBrake(car): car["handbraking"] = 30 def HandBraking(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. cv = car.localLinearVelocity v = cv.y if v <0: v *= -1 car["handbraking"] = car.get("handbraking", 30) - 1 if v < 10: car["handbraking"] = 0 if car.get("accelerating"): return turning = Turning(car) if turning and not car["handbraking"]: car["handbraking"] = 2 hbf = car["handbraking"] / 30 if car["handbraking"] <= 0: car["driftturn"] = 0.0 if not OnGround(car): return factor = car.localLinearVelocity[1] if factor > 0: factor = 1.0 else : factor = -1.0 if car.get("accelerating"): factor /= 20 ActivateBreaks(car, factor*hbf) # 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 and ( ( car.get("driftturn",0 ) < 0 ) == ( car["turn"] < 0 ) ) )\ or not car.get("driftturn",0 ): car["driftturn"] = float(car["turn"]) # Adding fake turn to the car car.applyTorque([0, 0, ((car["driftturn"])+(car["turn"]/2))*car.mass*5*hbf ], True) # Instead of reducing friction on the wheels, this # will simulate that, but with more control. if car.get("accelerating"): cv.x += -car["driftturn"] * v / 50 *hbf cv.y += turning * v / 20 * hbf car.applyForce(cv * car.mass * 0.5 * hbf, 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() or "water" in str(obj).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 < 0.1: ScrapeSound(car, position=point.worldPoint, volume=max(1,min(3,force*200))) else: HitSound(car, position=point.worldPoint, volume=min(3,force/2)) # 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 / 50 )) # 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] character["carblewup"] = car character["target"] = car.get("target") 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.get("wheels", []): ColorPart(car, wheel) # Neon if attribute == "SecondaryColor" and car in bge.logic.globalDict["cars"]: 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 ["MainColor", "SecondaryColor"]: 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 if not body.get("mesh", "Good") == "Borked": body.replaceMesh(car.get("borked", "Borked")) GlassSound(car, position=car.position, volume=3) body["mesh"] = "Borked" if "Neon" in car: car["Neon"].blenderObject.data.energy = 0 # Otherwise restore the car else: try: if not body.get("mesh", "Good") == "Good": body.replaceMesh(body["good"]) body["mesh"] = "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, smokeemiter.scaling.x) Destruction.Fire(smokeemiter.position, fireCoefitient) if "FireBall" not in car and health < 0.3: car["FireBall"] = Destruction.AttatchFireBall(smokeemiter.position, smokeemiter, 1.5) car["FireBall"].scaling = smokeemiter.scaling 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"\ and not spawn.get("priority") == True: 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("to_spawn", True) or 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. driver = "Man1Box" if not spawn.get("npc"): carModel = spawn.get("spawn") driver = None car = Spawn( carModel, spawn["position"], spawn["orientation"], True, "pallete", spawnanyway=False, spawn=spawn, driver=driver) if not car and (spawnedCars < maxCars or spawn.get("npc") == "racer"\ or spawn.get("priority")): car = Spawn( carModel, spawn["position"], spawn["orientation"], True, "pallete", spawnanyway=True, spawn=spawn, driver=driver) 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() driver=None): #traceback.print_stack() # This function is to simplify spawning # of the car. cam = bge.logic.getCurrentScene().active_camera # 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 # Adding a driver if driver: if not car.get("driver"): if "drivers" not in bge.logic.globalDict: bge.logic.globalDict["drivers"] = [] found = False for d in bge.logic.globalDict["drivers"]: if d.name == driver and not d.get("driving")\ and d.getDistanceTo(cam) > 200: driver = d found = True print(consoleForm(driver),clr["tdgr"], "reusing driver", clr["norm"]) if not found: driver = Reuse.Create(driver) bge.logic.globalDict["drivers"].append(driver) SmartColor(driver, "random") print(consoleForm(driver),clr["tdrd"], "new driver", clr["norm"]) Character_Controll.getIntoCar(driver, car, immediately=True) elif car.get("driver"): print(consoleForm(car),clr["tdrd"], "Driver Deleted", clr["norm"]) driver = car["driver"] car["driver"] = "" Character_Controll.getOutCar(driver) Reuse.Delete(driver) 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" # Getting the target car elif not target.get("driver") and car.get("driver") and target in bge.logic.globalDict["cars"]: driver = car["driver"] Character_Controll.getOutCar(driver) driver["NPCcar"] = target car["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: if vect[0] > 5: Accelerate(car, 1) else: Accelerate(car, -1) 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: ActivateBreaks(car, v) # 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 ] try: checkpoint0 = race["checkpoints"][ checknumber - 2 ] except: checkpoint0 = 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.5) 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(checkpoint0["location"]) # car.alignAxisToVect(tocheck[1], 1, 0.5) car.alignAxisToVect( (0,0,1), 2, 1.0 ) tocheck = car.getVectTo(checkpoint2["location"]) car.alignAxisToVect(-tocheck[1], 1, 1) #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) vs = car["specs"].get("visionSize", 1.0) # Making sure we scan a wider range height = -3*vs#random.uniform(-6, -3) width = -10#random.uniform(-15, -10) # Getting the rays toright = RelativePoint(car, (-5, width, height)) fromright = RelativePoint(car, (-0.8*vs,0+vo,0.5*vs)) 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*vs,0+vo,0.5*vs)) 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*vs,0+vo,0.5*vs)) 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*vs,0+vo,0.5*vs)) 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 *vs)) 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*vs)) 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 / vs turn = max(-0.8, min(0.8, (( max(0, min(1, ( right / st ))) - max(0, min(1, ( left / st ))) ) *-2 ))) turn /= ( vs ** 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 EncodeRotation(car): rot = list(car.worldOrientation) for n, i in enumerate(rot): rot[n] = list(i) return rot 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" : EncodeRotation(car), "linvelocity" : list(car.worldLinearVelocity), "angvelocity" : list(car.worldAngularVelocity), "npc" : car.get("npc", ""), "rpm" : car.get("rpm", 0), "gear" : car.get("gear", 0), "turn" : car.get("turn", 0) } # 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) for attribute in cardata.get("colors", {}): value = cardata["colors"][attribute] Material(car, value, attribute) 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 network: latency = Multiplayer_Client.Latency(car) else: latency = 0 car["npc"] = cardata.get("npc", "") car["rpm"] = cardata.get("rpm", 0) car["gear"] = cardata.get("gear", 0) car["turn"] = cardata.get("turn", 0) car.worldLinearVelocity = cardata.get("linvelocity", car.localLinearVelocity) car.worldAngularVelocity = cardata.get("angvelocity", car.localAngularVelocity) position = mathutils.Vector(position)+(car.worldLinearVelocity*latency) if math.dist(car.position, position) > 5: car.position = position car.worldOrientation = orientation #car.applyRotation(car.worldAngularVelocity*latency,False) # 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) if door.visible != doordata.get("visible", 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] def NetworkControlled(car): return car.get("netId") in bge.logic.globalDict.get("netObjects", {}).get("netId", {})\ and car.get("onwerId") != bge.logic.globalDict.get("userId") def NetworkControl(car): return #### 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)