1770 lines
65 KiB
Python
1770 lines
65 KiB
Python
|
import bge
|
||
|
import time
|
||
|
import math
|
||
|
import bpy
|
||
|
import random
|
||
|
import numpy
|
||
|
import aud
|
||
|
import mathutils
|
||
|
|
||
|
import inspect
|
||
|
|
||
|
import Reuse
|
||
|
import Distraction
|
||
|
import Opt
|
||
|
|
||
|
import Character_Controll
|
||
|
|
||
|
deleteAtDistance = 500
|
||
|
fixGaragePosition = [-754.61, -1043.9, 409.32]
|
||
|
|
||
|
|
||
|
raceNames = {"RedKissBox":"Tali",
|
||
|
"NeonSpeedsterBox":"Jack"}
|
||
|
|
||
|
def start_car():
|
||
|
|
||
|
# This function runs when the car is created.
|
||
|
# It creates the Bullet Physics RayCast Vehicle
|
||
|
# wrapper for the car.
|
||
|
|
||
|
# Getting basic things
|
||
|
cont = bge.logic.getCurrentController()
|
||
|
scene = bge.logic.getCurrentScene()
|
||
|
car = cont.owner
|
||
|
|
||
|
# Creating the vehicle constraint itself
|
||
|
vehicle = bge.constraints.createVehicle(car.getPhysicsId())
|
||
|
|
||
|
# Saving the ID of the constraint so we could access it later.
|
||
|
car["cid"] = vehicle.getConstraintId()
|
||
|
bge.logic.globalDict[car["cid"]] = {"wheels":[]}
|
||
|
|
||
|
|
||
|
###### SETTING UP WHEELS #####
|
||
|
|
||
|
# LEFT BACK
|
||
|
wheel0 = car.children[car.get("wheelsname", "Wheel")+""]
|
||
|
bge.logic.globalDict[car["cid"]]["wheels"].append(wheel0)
|
||
|
wheel0.removeParent()
|
||
|
vehicle.addWheel(wheel0,
|
||
|
[car.get("wheelswidth", 0.89), car.get("wheelsback", 1.65), car.get("wheelsdown", -0.1)], # Pos Local
|
||
|
[0, 0, -1], # Dir Local
|
||
|
[-1, 0, 0], # Axl Local
|
||
|
0.2, # Suspention length
|
||
|
car.get("wheelradius", 0.42), # Wheel Radius
|
||
|
1
|
||
|
)
|
||
|
|
||
|
# RIGHT BACK
|
||
|
wheel1 = car.children[car.get("wheelsname", "Wheel")+".001"]
|
||
|
bge.logic.globalDict[car["cid"]]["wheels"].append(wheel1)
|
||
|
wheel1.removeParent()
|
||
|
vehicle.addWheel(wheel1,
|
||
|
[-car.get("wheelswidth", 0.89), car.get("wheelsback", 1.65), car.get("wheelsdown", -0.1)], # Pos Local
|
||
|
[0, 0, -1], # Dir Local
|
||
|
[-1, 0, 0], # Axl Local
|
||
|
0.2, # Suspention length
|
||
|
car.get("wheelradius", 0.42), # Wheel Radius
|
||
|
1
|
||
|
)
|
||
|
|
||
|
# LEFT FRONT
|
||
|
wheel2 = car.children[car.get("wheelsname", "Wheel")+".002"]
|
||
|
bge.logic.globalDict[car["cid"]]["wheels"].append(wheel2)
|
||
|
wheel2.removeParent()
|
||
|
vehicle.addWheel(wheel2,
|
||
|
[car.get("wheelswidth", 0.89), car.get("wheelsfront", -1.25), car.get("wheelsdown", -0.1)], # Pos Local
|
||
|
[0, 0, -1], # Dir Local
|
||
|
[-1, 0, 0], # Axl Local
|
||
|
0.2, # Suspention length
|
||
|
car.get("wheelradius", 0.42), # Wheel Radius
|
||
|
1
|
||
|
)
|
||
|
|
||
|
# RIGHT FRONT
|
||
|
wheel3 = car.children[car.get("wheelsname", "Wheel")+".003"]
|
||
|
bge.logic.globalDict[car["cid"]]["wheels"].append(wheel3)
|
||
|
wheel3.removeParent()
|
||
|
vehicle.addWheel(wheel3,
|
||
|
[-car.get("wheelswidth", 0.89), car.get("wheelsfront", -1.25), car.get("wheelsdown", -0.1)], # Pos Local
|
||
|
[0, 0, -1], # Dir Local
|
||
|
[-1, 0, 0], # Axl Local
|
||
|
0.2, # Suspention length
|
||
|
car.get("wheelradius", 0.42), # Wheel Radius
|
||
|
1
|
||
|
)
|
||
|
|
||
|
# Setting up wheel physics characteristics.
|
||
|
# ( NOTE: ) This is not the only place where those are set.
|
||
|
# Damage and Rescue functions update those values.
|
||
|
|
||
|
car["suspensions"] = []
|
||
|
|
||
|
for i in range(4):
|
||
|
vehicle.setSuspensionStiffness(car.get("suspension", 200), i)
|
||
|
car["suspensions"].append(car.get("suspension", 200))
|
||
|
vehicle.setTyreFriction(car.get("friction", 1), i)
|
||
|
vehicle.setRollInfluence(car.get("roll", 0), i)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
##### SETTING UP THE DAMAGE MODEL #####
|
||
|
|
||
|
|
||
|
def collision_callback(object, point, normal):
|
||
|
|
||
|
# This function will run whenever the car colides with anything.
|
||
|
|
||
|
# Dani object ( main human character )
|
||
|
dani = scene.objects["Dani_Box"]
|
||
|
|
||
|
toFPS = Opt.ToFPS()
|
||
|
|
||
|
# Near misses - when the player drives quickly
|
||
|
# near a different vehicle. If colision happens,
|
||
|
# near misses are canceled.
|
||
|
car["veryclose"] = None
|
||
|
|
||
|
|
||
|
# If anywhere within the proximity of the car,
|
||
|
# an explosion was set off ( car colided with the shockwave sphere ).
|
||
|
# we want to setup a timer, to blow car up in a few ticks after that.
|
||
|
# Doing it immediately makes the second explosion almost inconciaveable.
|
||
|
if object.get("explosion"):
|
||
|
car["blowuptimer"] = 20
|
||
|
|
||
|
# Canceling the rest of the calculation if we can't see it, or it is too far from the camera.
|
||
|
cam = scene.active_camera
|
||
|
if car.getDistanceTo("Dani_Box") > 20 or cam.pointInsideFrustum(car.worldPosition) == cam.OUTSIDE:
|
||
|
return
|
||
|
|
||
|
# If human pushes the car, there is no damage
|
||
|
if dani == object:
|
||
|
return
|
||
|
|
||
|
# Separation is an invisible object that bots see to navigate.
|
||
|
# We don't want it to cause damage.
|
||
|
if "Separation" in object.name:
|
||
|
return
|
||
|
|
||
|
#### WATER INTERACTIONS ####
|
||
|
|
||
|
if object.name == "Water":
|
||
|
|
||
|
car["underwater"] = True
|
||
|
if car["active"]:
|
||
|
dani["underwater"] = True
|
||
|
|
||
|
# Sound
|
||
|
device = bge.logic.globalDict["SoundDevice"]
|
||
|
s = "//sfx/splash.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 = point
|
||
|
sound["play"].velocity = car.getLinearVelocity()
|
||
|
sound["play"].relative = False
|
||
|
sound["play"].distance_maximum = 100
|
||
|
sound["play"].distance_reference = 1
|
||
|
sound["play"].attenuation = 1
|
||
|
|
||
|
|
||
|
return
|
||
|
|
||
|
# Returning if performance is not good
|
||
|
if not Opt.GoodFPS("Car Collisions", 0.9) and not car.get("active"):
|
||
|
return
|
||
|
|
||
|
# Velocity of the car
|
||
|
cv = car.getLinearVelocity()
|
||
|
|
||
|
# Averagin the values
|
||
|
av = sum(cv) / 3
|
||
|
if av < 0: av *= -1
|
||
|
|
||
|
av /= (toFPS ** 2)
|
||
|
|
||
|
# Setting off sparks if the velocity is beyond a certain amount
|
||
|
if av > 0.1:
|
||
|
Distraction.particles("Sparkle", point,
|
||
|
despawn = random.randint(30, 100),
|
||
|
amount = random.randint(0, 3),
|
||
|
max = 50,
|
||
|
spread = 0,
|
||
|
normal = 0.04,
|
||
|
velocity = cv * 0.015)
|
||
|
|
||
|
# Restricting all the rest of the calculations based on a timer
|
||
|
# between the colisions.
|
||
|
if car.get("lastdamage"):
|
||
|
return
|
||
|
car["lastdamage"] = random.randint(10, 60)
|
||
|
|
||
|
|
||
|
# Adding nitro on colision ( based on the speed of the car )
|
||
|
car["nitro"] += av*20
|
||
|
|
||
|
|
||
|
|
||
|
#### ENEMY VOICES ####
|
||
|
|
||
|
# NPC cars might get angry and start attacking you if you hit them.
|
||
|
# When they get angry they swear at you. This is the logic of the
|
||
|
# swearing sounds. And the getting angry logic.
|
||
|
|
||
|
if object.get("engine") and car.get("npc") and car.get("npc") != "racer" and object.get("enemy") != car:
|
||
|
|
||
|
device = bge.logic.globalDict["SoundDevice"]
|
||
|
|
||
|
if not car["npc"] == "enemy":
|
||
|
|
||
|
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"]
|
||
|
|
||
|
if object.get("active"):
|
||
|
scene.objects["Angry_pusuit"]["pusuit"] = True
|
||
|
if not dani.get("active_sound"):
|
||
|
dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_ohno"]["sound"])
|
||
|
bge.logic.globalDict["sounds"]["dani_ohno"]["play"] = dani["active_sound"]
|
||
|
dani["active_sound_is"] = "dani_ohno"
|
||
|
|
||
|
elif car["enemy"] == object and ( 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"]
|
||
|
|
||
|
if object.get("active") and not dani.get("active_sound_is") == "dani_ohnoindeed" and not dani.get("active_sound"):
|
||
|
dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_ohnoindeed"]["sound"])
|
||
|
bge.logic.globalDict["sounds"]["dani_ohnoindeed"]["play"] = dani["active_sound"]
|
||
|
dani["active_sound_is"] = "dani_ohnoindeed"
|
||
|
|
||
|
# Getting angry
|
||
|
|
||
|
car["npc"] = "enemy"
|
||
|
car["enemy"] = object
|
||
|
object["chased"] = True
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# Getting car's body.
|
||
|
rig = car.children[car.get("rig","RIG")]
|
||
|
body = rig.children["Car_body"]
|
||
|
|
||
|
|
||
|
# Getting the velocity of the colision ( comparing to the valocity of the object we are coliding with )
|
||
|
ov = object.getLinearVelocity()
|
||
|
ov /= (toFPS ** 2)
|
||
|
sp = cv - ov
|
||
|
|
||
|
# We can't simply average the velocity since it could be negative.
|
||
|
sm = 0
|
||
|
for i in sp:
|
||
|
if i > 0:
|
||
|
sm += i
|
||
|
else:
|
||
|
sm += i*-1
|
||
|
sp = sm / 3
|
||
|
|
||
|
# Adding the mass of both objects to the calculation
|
||
|
obmass = object.mass
|
||
|
if not obmass: obmass = 1
|
||
|
breakMultiply = max(0, min(1, 1-(sp * obmass)/55)) ** 0.05
|
||
|
|
||
|
breakMultiply *= (toFPS ** 2)
|
||
|
|
||
|
# Blow up when getting a 0 breakMultiply
|
||
|
if not breakMultiply:
|
||
|
car["blowuptimer"] = 10
|
||
|
|
||
|
|
||
|
####### BREAKING THE CAR #######
|
||
|
|
||
|
if breakMultiply < 0.999 or object.get("explosion"):
|
||
|
|
||
|
|
||
|
# Hit sound
|
||
|
device = bge.logic.globalDict["SoundDevice"]
|
||
|
|
||
|
# Hit versus scrape sound
|
||
|
if breakMultiply < 0.98:
|
||
|
s = "//sfx/hit.ogg"
|
||
|
else:
|
||
|
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]
|
||
|
sound["play"] = device.play(sound["sound"])
|
||
|
|
||
|
sound["play"].location = point
|
||
|
sound["play"].relative = False
|
||
|
sound["play"].distance_maximum = 100
|
||
|
sound["play"].distance_reference = 1
|
||
|
sound["play"].attenuation = 1
|
||
|
sound["play"].pitch = random.choice([1, 0.8, 1.2, 0.6, 1.4])
|
||
|
sound["play"].volume = 1 / max(0.1, breakMultiply )
|
||
|
|
||
|
|
||
|
# slecting what area of the car to effect
|
||
|
areas = {"Front_Left" : normal[0] < 0.1 and normal[1] < 0.1,
|
||
|
"Front_Right": normal[0] < 0.1 and normal[1] > -0.1,
|
||
|
"Back_Left" : normal[0] > -0.1 and normal[1] < 0.1,
|
||
|
"Back_Right" : normal[0] > -0.1 and normal[1] < 0.1}
|
||
|
|
||
|
wheels = {"Front_Left" : 2,
|
||
|
"Front_Right": 3,
|
||
|
"Back_Left" : 0,
|
||
|
"Back_Right" : 1}
|
||
|
|
||
|
for area in areas:
|
||
|
if ( areas[area] and car[area] > 0 ) or object.get("explosion"):
|
||
|
|
||
|
# Reducing health
|
||
|
car[area] *= breakMultiply
|
||
|
|
||
|
# If broken toally we pop a wheel
|
||
|
if car[area] <= 0.01:
|
||
|
|
||
|
# We can't simply undo the wheel from the constraint. But
|
||
|
# we can do the next best thing.
|
||
|
bge.logic.globalDict[car["cid"]]["wheels"][wheels[area]].setVisible(False)
|
||
|
vehicle.applyBraking(10, wheels[area])
|
||
|
vehicle.setSuspensionStiffness(0,wheels[area])
|
||
|
|
||
|
# If it is an npc, we make him give up trying to drive.
|
||
|
if not car.get("racing"):
|
||
|
car["npc"] = ""
|
||
|
|
||
|
# Tire Pop
|
||
|
sound = bge.logic.globalDict["sounds"]["tire_pop"]
|
||
|
sound["play"] = device.play(sound["sound"])
|
||
|
sound["play"].location = point
|
||
|
sound["play"].relative = False
|
||
|
sound["play"].distance_maximum = 100
|
||
|
sound["play"].distance_reference = 1
|
||
|
sound["play"].attenuation = 1
|
||
|
sound["play"].volume = 2
|
||
|
|
||
|
# If we are not totally broken, make the wheel worse handeling-wise.
|
||
|
elif car["Front_Left"] < 0.7:
|
||
|
vehicle.setSuspensionStiffness(car.get("suspension", 200)*car["Front_Left"],wheels[area])
|
||
|
vehicle.setRollInfluence(1-car[area], wheels[area])
|
||
|
|
||
|
# If below a certain health, we make the car body look borked
|
||
|
if car[area] < 0.7:
|
||
|
body.replaceMesh(car.get("borked", "Borked"))
|
||
|
|
||
|
# If the heal even lower.
|
||
|
if car[area] < 0.3:
|
||
|
|
||
|
# Removing left door.
|
||
|
if "Left" in area:
|
||
|
try:
|
||
|
door = rig.children["Door_Pease.001"]
|
||
|
door.visible = False
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
# Removing right door.
|
||
|
elif "Right" in area:
|
||
|
|
||
|
try:
|
||
|
door = rig.children["Door_Pease"]
|
||
|
door.visible = False
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
|
||
|
# Adding the collision function to the car's collision functions.
|
||
|
car.collisionCallbacks.append(collision_callback)
|
||
|
|
||
|
def update():
|
||
|
|
||
|
# This function is called on ever game logic tick for every car
|
||
|
# in the scene. This is the logic of the car.
|
||
|
|
||
|
# BGE logic stuff
|
||
|
cont = bge.logic.getCurrentController()
|
||
|
scene = bge.logic.getCurrentScene()
|
||
|
car = cont.owner
|
||
|
|
||
|
toFPS = Opt.ToFPS()
|
||
|
|
||
|
carRacing = car.get("racing")
|
||
|
carEnemy = car.get("enemy")
|
||
|
carBehaviour = car.get("npc")
|
||
|
carActive = car.get("active")
|
||
|
carSpeed = car.get("speed")
|
||
|
|
||
|
rig = car.children[car.get("rig","RIG")]
|
||
|
|
||
|
cam = scene.active_camera
|
||
|
camtarget = car.children["CarCamTarget"]
|
||
|
|
||
|
# Is the car in view
|
||
|
distanceToCam = car.getDistanceTo(cam)
|
||
|
inview = ( cam.pointInsideFrustum(car.worldPosition) == cam.INSIDE and distanceToCam < deleteAtDistance / 2 ) or distanceToCam < deleteAtDistance / 3 or distanceToCam > deleteAtDistance
|
||
|
|
||
|
# Dani related things
|
||
|
dani = scene.objects["Dani_Box"]
|
||
|
|
||
|
# Delete vehicle ( at distance, usually done for NPC cars ).
|
||
|
|
||
|
if car.getDistanceTo(cam) > deleteAtDistance and not carRacing and car in bge.logic.globalDict["allcars"]:
|
||
|
|
||
|
def RemovingTheCar(car, dani):
|
||
|
|
||
|
# If car is waiting for a race to start, we don't delete it
|
||
|
if carBehaviour == "racer":
|
||
|
racename = car["race"]
|
||
|
race = bge.logic.globalDict["races"][racename]
|
||
|
starter = race["starters"][0]
|
||
|
if dani.getDistanceTo(starter["location"]) < deleteAtDistance or car["racing"]:
|
||
|
return
|
||
|
|
||
|
|
||
|
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
|
||
|
if Reuse.reuse.get(car.name):# or len(bge.logic.globalDict["allcars"]) > bge.logic.globalDict["maxCars"]:
|
||
|
for i in bge.logic.globalDict[car["cid"]]["wheels"]:
|
||
|
i.endObject()
|
||
|
Reuse.EndObject(car)
|
||
|
# Otherwise storing it for later.
|
||
|
else:
|
||
|
Reuse.Delete(car, inactive=True)
|
||
|
|
||
|
|
||
|
|
||
|
# Undoing angry pursuit
|
||
|
try:
|
||
|
if car["enemy"]["active"]:
|
||
|
scene.objects["Angry_pusuit"]["pusuit"] = False
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
if Reuse.reuse.get(car.name) or len(bge.logic.globalDict["allcars"]) > bge.logic.globalDict["maxCars"]:
|
||
|
Opt.ScheduleTask("Removing Car ["+car.name+"]", 1.0, RemovingTheCar, car, dani)
|
||
|
else: RemovingTheCar(car, dani)
|
||
|
|
||
|
|
||
|
|
||
|
# Cutting down on unnesesary running of the script
|
||
|
if ( not carRacing and not carActive ) and ( not inview ):
|
||
|
car["deactivated"] = True
|
||
|
car.suspendPhysics()
|
||
|
return
|
||
|
elif car.get("deactivated"):
|
||
|
car.restorePhysics()
|
||
|
car["deactivated"] = False
|
||
|
|
||
|
|
||
|
|
||
|
# Physics constraint for the vehicle
|
||
|
vehicle = bge.constraints.getVehicleConstraint(car["cid"])
|
||
|
|
||
|
# Sound device for sounds.
|
||
|
device = bge.logic.globalDict["SoundDevice"]
|
||
|
|
||
|
# Car's health
|
||
|
health = sum([car["Front_Left"], car["Front_Right"], car["Back_Left"], car["Back_Right"]])/4
|
||
|
engineHealth = ( car["Front_Left"] + car["Front_Right"] ) / 2
|
||
|
car["health"] = health
|
||
|
|
||
|
# Fixing the car ( when in the garage)
|
||
|
if distanceToCam < 30 and car.getDistanceTo(fixGaragePosition) < 2 and health < 1:
|
||
|
bge.logic.globalDict["print"] = "Press F to fix the car."
|
||
|
|
||
|
# Fix car if F is pressed
|
||
|
if 28 in bge.logic.globalDict["keys"]:
|
||
|
body = rig.children["Car_body"]
|
||
|
body.replaceMesh( body["good"] )
|
||
|
for i in range(4):
|
||
|
vehicle.setSuspensionStiffness(car.get("suspension", 200), i)
|
||
|
vehicle.applyBraking(0, i)
|
||
|
vehicle.setTyreFriction(car.get("friction", 1), i)
|
||
|
vehicle.setRollInfluence(car.get("roll", 0), i)
|
||
|
bge.logic.globalDict[car["cid"]]["wheels"][i].setVisible(True)
|
||
|
for i in ["Front_Right", "Front_Left", "Back_Left", "Back_Right"]:
|
||
|
car[i] = 1
|
||
|
|
||
|
# Doors ( some cars have them falling )
|
||
|
try:
|
||
|
rig.children["Door_Pease.001"].visible = True
|
||
|
rig.children["Door_Pease"].visible = True
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
elif carActive and health < 0.98:
|
||
|
toprint = "You can fix the car in the garage at home."
|
||
|
if toprint not in bge.logic.globalDict["done-prints"]:
|
||
|
bge.logic.globalDict["print"] = toprint
|
||
|
|
||
|
# Whether the car is on graound.
|
||
|
graypos = car.position.copy()
|
||
|
graypos[2] -= 1.5
|
||
|
onGround = car.rayCastTo(graypos)
|
||
|
|
||
|
|
||
|
# Car's rotation
|
||
|
rot = car.localOrientation.to_euler()
|
||
|
upsideDown = not ( -2.5 < rot[0] < 2.5 and -2.5 < rot[1] < 2.5 )
|
||
|
|
||
|
|
||
|
# Colision timer
|
||
|
if car.get("lastdamage"):
|
||
|
car["lastdamage"] -= 1
|
||
|
|
||
|
# Lauchtime timer
|
||
|
if "launchtime" not in car:
|
||
|
car["launchtime"] = 300
|
||
|
car["launchtime"] -= 1
|
||
|
|
||
|
# Water boyancy
|
||
|
if car.position[2] < -9.5 and car.get("underwater"):
|
||
|
car.applyForce([0,0,-5*(car.position[2]+9.5)])
|
||
|
|
||
|
if car["speed"] < -1.5:
|
||
|
car.applyForce([0,-80,0], True)
|
||
|
car.orientation = [0,0,rot[2]]
|
||
|
|
||
|
elif car.position[2] > -9.5:
|
||
|
car["underwater"] = False
|
||
|
|
||
|
# Speed calculation
|
||
|
psp = vehicle.getWheelRotation(0)+vehicle.getWheelRotation(1)
|
||
|
sp = psp - car["prevwheelrot"]
|
||
|
car["prevwheelrot"] = psp
|
||
|
car["speed"] = sp
|
||
|
|
||
|
# sp will be speed normalized
|
||
|
if sp < 0: sp *= -1
|
||
|
|
||
|
# Down Force
|
||
|
dforce = -5*sp*car.get("downforce", 1)
|
||
|
if onGround:
|
||
|
dforce = min(-5, dforce)
|
||
|
car.applyForce([0,0, Opt.Force(dforce)], True)
|
||
|
|
||
|
|
||
|
|
||
|
# Gage
|
||
|
if carActive:
|
||
|
gagerotation = sp * 0.214 # 400 km/h == 4.27 radiance on the gage
|
||
|
gage = scene.objects["Gage_Arrow"]
|
||
|
gage.orientation = [0,0,-gagerotation]
|
||
|
|
||
|
gage_health = scene.objects["Gage_Health"]
|
||
|
gage_health["Health"] = int(( (health ** 2 ** 3) / (1 ** 2 ** 3 ) )*100)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
###### NITRO ######
|
||
|
|
||
|
# Making sure that nitro isn't overboard.
|
||
|
car["nitro"] = max(0,min(10000, car.get("nitro", 0.0)))
|
||
|
if carActive:
|
||
|
gage_nitro = scene.objects["Gage_Nitro"]
|
||
|
gage_nitro["Nitro"] = car["nitro"]/100
|
||
|
|
||
|
|
||
|
# Function activated when Nitro is used
|
||
|
def nitro():
|
||
|
|
||
|
if car["nitro"] <= 0:
|
||
|
stopNitro()
|
||
|
return
|
||
|
|
||
|
|
||
|
# Actual Nitro Effect on the car
|
||
|
#if car["engine"] < car.get("maxengine", 2)-0.1:
|
||
|
car["engine"] += car.get("maxaccel",0.003)* 5 * toFPS
|
||
|
#else:
|
||
|
# car["engine"] = car.get("maxengine", 2)*1.5
|
||
|
car["nitro"] -= 20
|
||
|
|
||
|
# Anti bank
|
||
|
if car.get("roll",0):
|
||
|
car.applyRotation([0.05*car.get("roll",0),0,0], True)
|
||
|
car.applyForce([0,0, -20*car.get("roll",0)], True)
|
||
|
|
||
|
|
||
|
# Nitro Sound
|
||
|
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
|
||
|
|
||
|
car["nitroing"] = True
|
||
|
|
||
|
if not carActive and not Opt.GoodFPS("Nitro", 0.9):
|
||
|
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)
|
||
|
|
||
|
# Camera effect
|
||
|
if carActive:
|
||
|
camtarget = car.children["CarCamTarget"]
|
||
|
if not car.get("zoom"):
|
||
|
car["zoom"] = camtarget.scaling[0]
|
||
|
camtarget.scaling *= 1.02
|
||
|
|
||
|
# Unction activated when nitro isn't used.
|
||
|
def stopNitro():
|
||
|
|
||
|
# Grpaphics stop
|
||
|
|
||
|
for nitroObject in car.children:
|
||
|
if "nitro" in nitroObject.name:
|
||
|
if nitroObject.get("nitroCone"):
|
||
|
nitroObject["nitroCone"].removeParent()
|
||
|
Reuse.Delete(nitroObject["nitroCone"])
|
||
|
nitroObject["nitroCone"] = None
|
||
|
|
||
|
# Sound stop
|
||
|
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
|
||
|
|
||
|
# Activating nitro
|
||
|
if carActive and 48 in bge.logic.globalDict["keys"]:
|
||
|
# 48 is Z
|
||
|
nitro()
|
||
|
else:
|
||
|
stopNitro()
|
||
|
|
||
|
if not onGround:
|
||
|
car["nitro"] += 5
|
||
|
|
||
|
# Telling the player that the car is availble
|
||
|
if dani.get("doesntknowhowtositintocars", True) and not dani["driving"] and distanceToCam < 7:
|
||
|
bge.logic.globalDict["print"] = "Press E to sit into the car."
|
||
|
dani["doesntknowhowtositintocars"] = False
|
||
|
|
||
|
# Camera changes ( help camera show you where you are going ).
|
||
|
|
||
|
if carActive and not upsideDown and sp > 0.1:
|
||
|
|
||
|
dontseecar = cam.rayCastTo(car) != car
|
||
|
|
||
|
if dontseecar and camtarget.scaling[0] > 0.1:
|
||
|
if not car.get("zoom"):
|
||
|
car["zoom"] = camtarget.scaling[0]
|
||
|
camtarget.scaling *= 0.97
|
||
|
|
||
|
elif car.get("zoom") and scene.objects["SecondaryCamSensor"].rayCastTo(car) == car:
|
||
|
|
||
|
newzoom = camtarget.scaling[0] + (( car["zoom"] - camtarget.scaling[0] ) / ( 30 / toFPS ) )
|
||
|
camtarget.scaling = [ newzoom, newzoom, newzoom ]
|
||
|
if camtarget.scaling[0] == car["zoom"]:
|
||
|
car["zoom"] = False
|
||
|
|
||
|
|
||
|
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 backorforth and camrot[2] < 0:
|
||
|
camnewrot[2] -= math.pi * 2
|
||
|
|
||
|
camnewrot = mathutils.Vector(camnewrot)
|
||
|
camrot = camrot + (( camnewrot - camrot ) / 10 )
|
||
|
camtarget.localOrientation = camrot
|
||
|
|
||
|
elif carActive and cam.rayCastTo(car) != car:
|
||
|
camrot = camtarget.localOrientation.to_euler()
|
||
|
camrot[0] -= 0.02
|
||
|
#camrot[2] += 0.01
|
||
|
camtarget.localOrientation= camrot
|
||
|
|
||
|
|
||
|
# Smoke ( if health too low )
|
||
|
if not car["underwater"] and inview:
|
||
|
|
||
|
smokeCoefitient = max(0, 1-engineHealth*1.1)*0.2
|
||
|
fireCoefitient = max(0, 1-engineHealth*2)*0.2
|
||
|
smokeemiter = car.children["SmokeEmmiter"]
|
||
|
Distraction.Smoke(smokeemiter.position, smokeCoefitient)
|
||
|
Distraction.Fire(smokeemiter.position, fireCoefitient)
|
||
|
|
||
|
|
||
|
# Exposion
|
||
|
|
||
|
if engineHealth < 0.2 and not car.get("blown"):
|
||
|
boompos = smokeemiter.position.copy()
|
||
|
boompos[2] -= 2
|
||
|
Distraction.Explosion(boompos, mass=0.5, size=2)
|
||
|
car["blown"] = True
|
||
|
|
||
|
# Blowuptimer
|
||
|
if car.get("blowuptimer") != None:
|
||
|
if car["blowuptimer"] == 0 and not car.get("blown"):
|
||
|
Distraction.Explosion(car.children["SmokeEmmiter"].position.copy(), mass=0.5, size=2)
|
||
|
car["blown"] = True
|
||
|
else:
|
||
|
car["blowuptimer"] -= 1
|
||
|
|
||
|
|
||
|
######### SOUND ########
|
||
|
|
||
|
|
||
|
if carActive:
|
||
|
|
||
|
# Dani yelling when in air
|
||
|
|
||
|
if not onGround and not car["underwater"] and not dani.get("active_sound"):
|
||
|
dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_yelling"]["sound"])
|
||
|
bge.logic.globalDict["sounds"]["dani_yelling"]["play"] = dani["active_sound"]
|
||
|
dani["active_sound_is"] = "dani_yelling"
|
||
|
elif onGround and dani["active_sound"] and dani["active_sound_is"] == "dani_yelling":
|
||
|
dani["active_sound"].stop()
|
||
|
dani["active_sound"] = None
|
||
|
|
||
|
# Sinking
|
||
|
|
||
|
if car.get("underwater") and not dani.get("active_sound") and not dani.get("active_sound_is") == "dani_wearesinking" and dani.get("sinking_timer", 0) < time.time():
|
||
|
dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_wearesinking"]["sound"])
|
||
|
bge.logic.globalDict["sounds"]["dani_wearesinking"]["play"] = dani["active_sound"]
|
||
|
dani["active_sound_is"] = "dani_wearesinking"
|
||
|
dani["sinking_timer"] = time.time() + 40
|
||
|
|
||
|
# Very close to another car
|
||
|
|
||
|
|
||
|
try:
|
||
|
for i in bge.logic.globalDict["allcars"]:
|
||
|
if i != car and i.getDistanceTo(car) < 6:
|
||
|
car["veryclose"] = i
|
||
|
break
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
|
||
|
if sp < 0.3:
|
||
|
car["veryclose"] = None
|
||
|
|
||
|
elif car.get("veryclose") and car["veryclose"].getDistanceTo(car) > 7:
|
||
|
if not dani.get("active_sound"):
|
||
|
dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_ohgod"]["sound"])
|
||
|
bge.logic.globalDict["sounds"]["dani_ohgod"]["play"] = dani["active_sound"]
|
||
|
dani["active_sound_is"] = "dani_ohgod"
|
||
|
car["nitro"] += 200
|
||
|
car["veryclose"] = None
|
||
|
|
||
|
if not dani.get("active_sound") or not dani["active_sound"].status:
|
||
|
dani["active_sound"] = None
|
||
|
|
||
|
# Engine sound
|
||
|
|
||
|
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]
|
||
|
esp = car["engine"]
|
||
|
if esp < 0: esp *= -1
|
||
|
esp = math.log1p(esp) * 5
|
||
|
|
||
|
if 0.01 < esp or car.get("engineStarted"):
|
||
|
|
||
|
car["engineStarted"] = True
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
|
||
|
revs = ( esp + ( esp % 1.5 ) ) * 0.5
|
||
|
|
||
|
sound["play"].pitch = max(0.4, revs )
|
||
|
sound["play"].volume = engineHealth * 1
|
||
|
|
||
|
if carActive:
|
||
|
scene.objects["Gage_Tachometer"]["Health"] = revs * 30
|
||
|
|
||
|
else:
|
||
|
|
||
|
try:
|
||
|
bge.logic.globalDict["sounds"][code]["play"].stop()
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
|
||
|
|
||
|
# Grind sound
|
||
|
|
||
|
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 0.01 < sp:
|
||
|
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, engineHealth) * 2
|
||
|
sound["play"].volume = (1-engineHealth) * min(1.5, sp *8) * 1
|
||
|
|
||
|
else:
|
||
|
|
||
|
try:
|
||
|
bge.logic.globalDict["sounds"][code]["play"].stop()
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# Drift sound
|
||
|
|
||
|
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]
|
||
|
if car.get("deccelerating"):
|
||
|
slide = car.localLinearVelocity[1]
|
||
|
if slide < 0:
|
||
|
slide *= -1
|
||
|
|
||
|
sound = bge.logic.globalDict["sounds"][code]
|
||
|
if slide > 1 and -0.5 < rot[0] < 0.5 and -0.5 < rot[1] < 0.5 and onGround:
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
#### RACE RESCUE FUNCTION ####
|
||
|
|
||
|
def rescue():
|
||
|
|
||
|
if car.get("launchtime", 0) > 0:
|
||
|
return
|
||
|
|
||
|
# Sparkles on disappearing positiong
|
||
|
Distraction.particles("Sparkle", car.position.copy(),
|
||
|
despawn = random.randint(30, 100),
|
||
|
amount = 30,
|
||
|
max = 30,
|
||
|
spread = 2,
|
||
|
normal = 0.05,
|
||
|
scale = 7)
|
||
|
|
||
|
# Camera
|
||
|
if carActive:
|
||
|
def resetCarZoom():
|
||
|
car["zoom"] = False
|
||
|
Character_Controll.ChangeCameraTarget(car.children["CarCamTarget"], 100, [3,3,3],callable=resetCarZoom)
|
||
|
car.children["CarCamTarget"].orientation = [0.2,0,0]
|
||
|
|
||
|
# Teleportation
|
||
|
car.position = car["rescue"]
|
||
|
car.localLinearVelocity = [0,0,0]
|
||
|
|
||
|
|
||
|
car["checkpoint"] = ( car.get("rescueTo", car["checkpoint"]-1) + 1 ) % len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"])
|
||
|
nextcheckpointn = car["checkpoint"]
|
||
|
nextcheckpoint = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][ nextcheckpointn ]["location"]
|
||
|
|
||
|
prevcheckpoint = ( car["checkpoint"]-2 ) % len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"])
|
||
|
car["prevrescue"] = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][ prevcheckpoint ]["location"]
|
||
|
|
||
|
print("Rescue at: Car:", car, "Checkpoint:", car["checkpoint"])
|
||
|
|
||
|
# Sparkles on the new position
|
||
|
Distraction.particles("Sparkle", car.position,
|
||
|
despawn = random.randint(30, 100),
|
||
|
amount = 50,
|
||
|
max = 100,
|
||
|
spread = 2,
|
||
|
normal = 0.05,
|
||
|
scale = 5)
|
||
|
|
||
|
|
||
|
|
||
|
# Rotation the car based on the locations of checkpoints.
|
||
|
tocheck = car.getVectTo(nextcheckpoint)
|
||
|
car.alignAxisToVect(tocheck[1], 1, 1.0)
|
||
|
car.alignAxisToVect( (0,0,1), 2, 1.0 )
|
||
|
|
||
|
# Rotating the car 180 degrees since forward it -Y not Y.
|
||
|
rot = car.worldOrientation.to_euler()
|
||
|
car.worldOrientation = [rot[0], rot[1], rot[2]+math.pi]
|
||
|
|
||
|
# Adding the rotation of the previous checkpoint.
|
||
|
# if car.get("prevrescue"):
|
||
|
# tocheck = car.getVectTo(car.get("prevrescue"))
|
||
|
# car.alignAxisToVect(tocheck[1], 1, 0.7)
|
||
|
# car.alignAxisToVect( (0,0,1), 2, 1.0 )
|
||
|
|
||
|
# Putting the car a bit higher than the road.
|
||
|
car.position[2] += 2
|
||
|
car["launchtime"] = 200
|
||
|
car["nitro"] = max(1000, car["nitro"])
|
||
|
car["underwater"] = False
|
||
|
car["engine"] = 0
|
||
|
|
||
|
# Restoring the wheels.
|
||
|
for i in range(4):
|
||
|
#vehicle.setSuspensionStiffness(car.get("suspension", 200), i)
|
||
|
car["suspensions"][i] = car.get("suspension", 200)
|
||
|
vehicle.applyBraking(0, i)
|
||
|
vehicle.setTyreFriction(car.get("friction", 1), i)
|
||
|
vehicle.setRollInfluence(car.get("roll", 0), i)
|
||
|
bge.logic.globalDict[car["cid"]]["wheels"][i].setVisible(True)
|
||
|
for i in ["Front_Right", "Front_Left", "Back_Left", "Back_Right"]:
|
||
|
car[i] = max(0.8, car[i])
|
||
|
|
||
|
|
||
|
# Rescuing the active car in the race when pressing Backspace ( code 59 )
|
||
|
if carActive and dani.get("race") and dani.get("rescue") and ( ( car["speed"] > -0.02 and car["engine"] > 0.5 ) or engineHealth <= 0.2 or car["underwater"]):
|
||
|
#if carActive and dani.get("race") and dani.get("rescue") and 59 in bge.logic.globalDict["keys"] and car["launchtime"] <=0:
|
||
|
|
||
|
if not car.get("toresquetimer", 50):
|
||
|
|
||
|
nextcheckpoint = bge.logic.globalDict["races"][dani.get("race")]["checkpoints"][ ( dani.get("checkpoint") + 1 ) % len(bge.logic.globalDict["races"][dani.get("race")]["checkpoints"]) ]["location"]
|
||
|
|
||
|
car["rescue"] = dani["rescue"]
|
||
|
car["race"] = dani["race"]
|
||
|
car["checkpoint"] = dani["checkpoint"]
|
||
|
car["rescueTo"] = dani["rescueTo"]
|
||
|
car["prevrescue"] = dani["prevrescue"]
|
||
|
rescue()
|
||
|
|
||
|
car["toresquetimer"] = 50
|
||
|
else:
|
||
|
car["toresquetimer"] = car.get("toresquetimer", 50) -1
|
||
|
|
||
|
bge.logic.globalDict["cheat"] = [0]
|
||
|
|
||
|
|
||
|
###### NPC LOGIC #######
|
||
|
|
||
|
if car.get("npc") and bge.logic.globalDict["derby-cheat"]:
|
||
|
|
||
|
debryclosest = None
|
||
|
derbydistance = 1000
|
||
|
for i in scene.objects:
|
||
|
if "speed" in i and i != car:
|
||
|
idist = car.getDistanceTo(i)
|
||
|
if idist < derbydistance and car.rayCastTo(i.position) == i:
|
||
|
derbydistance = idist
|
||
|
debryclosest = i
|
||
|
|
||
|
if derbydistance < 10:
|
||
|
break
|
||
|
|
||
|
if debryclosest:
|
||
|
car["npc"] = "enemy"
|
||
|
car["enemy"] = debryclosest
|
||
|
|
||
|
|
||
|
## NPC PREPARATIONS ##
|
||
|
|
||
|
if car.get("npc"):
|
||
|
|
||
|
#### CAR VISION OF THE ROAD AND OBSTACLES ####
|
||
|
|
||
|
# To make the vision work we are going to use Ray Casting
|
||
|
# but instead of doing a whole path tracing thing to see
|
||
|
# everything in front of the car ( which you be slow )
|
||
|
# instead we trace only 5 specifically chosen points.
|
||
|
|
||
|
# Each car model has those 5 points positioned by hand
|
||
|
# in respecive car blend files. Those 5 points are.
|
||
|
|
||
|
left = 50 # To the forward - left diagonal \
|
||
|
# } On the height of the car
|
||
|
right = 50 # To the forward - right diagonal /
|
||
|
|
||
|
leftf = 50 # To the forward - left diagonal \
|
||
|
# } Slightly lower than the car ( to scan road )
|
||
|
rightf = 50 # To the forward - right diagonal /
|
||
|
|
||
|
forward = 50 # Directly in front of the car.
|
||
|
|
||
|
|
||
|
forwardData = None # Data of the ray casting for the forward.
|
||
|
leftm = "" # Material on the left of the car
|
||
|
rightm = "" # Material on the right of the car
|
||
|
|
||
|
# Each ray has it's default value be 50. During ray casting, the value will
|
||
|
# change to the distance of whatever the car "sees". If it sees nothing, the
|
||
|
# values will stay 50.
|
||
|
|
||
|
# Few more things to consider.
|
||
|
road = "Track2" # Material of the road ( to ignore )
|
||
|
separator = "Track_light" # Material of the separator line ( drawn in the middle of the road ).
|
||
|
separator_object = "Separation" # Name of an invisible object sticking out of this separator line.
|
||
|
|
||
|
|
||
|
# Left, slightly lower ray
|
||
|
ray = car.rayCast(car.children["Left"], car, poly=True)
|
||
|
|
||
|
# rayCast returns a list of values [ Hit Object , Hit Location, Hit Angle, Hit Polygon ]
|
||
|
|
||
|
# If we have any hit object ( exluding the road separator line if the car is a racing npc )
|
||
|
if ray[1] and ( not carRacing or not str(ray[0]) == separator_object ):
|
||
|
|
||
|
# Excluding if the Polygon's material is road material
|
||
|
if not ray[3] or road not in str(ray[3].material):
|
||
|
|
||
|
# Getting the distance to the hit location and storing it as both
|
||
|
left = car.getDistanceTo(ray[1]) # left and
|
||
|
leftf = left # leftf
|
||
|
try: leftm = str(ray[3].material)# And storing the material name for later
|
||
|
except: pass
|
||
|
|
||
|
# Doing something similar to the left point on the same heigh as the car
|
||
|
ray = car.rayCast(car.children["Left.001"],car, poly=True)
|
||
|
if ray[1] and ( not carRacing or not str(ray[0]) == separator_object ):
|
||
|
if not ray[3] or road not in str(ray[3].material):
|
||
|
|
||
|
# Only instead of doing the complex appending, we only
|
||
|
# overwrite the left variable, if the distance is lower.
|
||
|
# Basically left will end up the closest visible point from
|
||
|
# the left of the car. And leftf will be the original left.
|
||
|
|
||
|
left = min(car.getDistanceTo(ray[1]), left)
|
||
|
|
||
|
|
||
|
# Doing something similar for the right side as well
|
||
|
ray = car.rayCast(car.children["Right"],car, poly=True)
|
||
|
|
||
|
# Only now we exclude the separator line regardless if it
|
||
|
# is a racer or not. So if the car ends up on the wrong
|
||
|
# side of the road and goes British, it could find its way
|
||
|
# back to the right side of the road.
|
||
|
|
||
|
if ray[1] and not str(ray[0]) == separator_object:
|
||
|
if not ray[3] or road not in str(ray[3].material) :
|
||
|
right = car.getDistanceTo(ray[1])
|
||
|
rightf = right
|
||
|
try: rightm = str(ray[3].material)
|
||
|
except: pass
|
||
|
|
||
|
# And we slightly enfoce it to go to the right side of
|
||
|
# the road. By folling it to think that there is something
|
||
|
# of the left.
|
||
|
|
||
|
elif not carRacing and str(ray[0]) == separator_object:
|
||
|
left = min(car.getDistanceTo(ray[1]), left)
|
||
|
|
||
|
# And now for the slightly higher point on the right.
|
||
|
ray = car.rayCast(car.children["Right.001"],car, poly=True)
|
||
|
if ray[1] and not str(ray[0]) == separator_object:
|
||
|
if not ray[3] or road not in str(ray[3].material):
|
||
|
right = min(car.getDistanceTo(ray[1]), right)
|
||
|
elif not carRacing and str(ray[0]) == separator_object:
|
||
|
left = min(car.getDistanceTo(ray[1]), left)
|
||
|
|
||
|
# And for the forward.
|
||
|
ray = car.rayCast(car.children["Forward"],car, poly=True)
|
||
|
forwardData = ray
|
||
|
if ray[1] and str(ray[0]) == separator_object:
|
||
|
if not ray[3] or road not in str(ray[3].material):
|
||
|
forward = car.getDistanceTo(ray[1])
|
||
|
|
||
|
# Minimum value will be the smallest distance out of all of them
|
||
|
# which is useful for all sorts of things.
|
||
|
minimum = min(left, right, forward)
|
||
|
|
||
|
# And now we calculate how much the car needs to actually turn.
|
||
|
turn = max(-0.8, min(0.8, (( max(0, min(1, (right/12))) - max(0, min(1, (left/12))) ) *-2 )))
|
||
|
|
||
|
## NORMAL NPC CARS ##
|
||
|
|
||
|
if car.get("npc") == "npc":
|
||
|
|
||
|
# Normal NPC cars are those you can see driving around the city
|
||
|
# at a regular speed.
|
||
|
|
||
|
|
||
|
# Activate enemy logic when pursuit cheat is enabled
|
||
|
|
||
|
if distanceToCam < 100 and bge.logic.globalDict["pursuit-cheat"]:
|
||
|
car["npc"] = "enemy"
|
||
|
for i in scene.objects:
|
||
|
if i.get("active"):
|
||
|
car["enemy"] = i
|
||
|
break
|
||
|
|
||
|
# Applying the turn calculated in vision step
|
||
|
car["turn"] = turn
|
||
|
|
||
|
|
||
|
# Accelerating the car, with the target speed based on distance
|
||
|
# to the closest seen point.
|
||
|
|
||
|
targetSpeed = 4 * ((minimum/12)-0.4)
|
||
|
|
||
|
# We also ignore target speed, and make the car drive as fast
|
||
|
# as it can, when it is chased by somebody.
|
||
|
|
||
|
if (sp < targetSpeed or car.get("chased")) and minimum > 2:
|
||
|
accelerate(car)
|
||
|
else:
|
||
|
deccelerate(car)
|
||
|
|
||
|
# To help it avoid obstacles better.
|
||
|
if minimum < 2 and car.get("accelerating"):
|
||
|
car["turn"] = -0.8
|
||
|
|
||
|
# If we about to hit a car head first
|
||
|
if forwardData[0] and forwardData[0].get("speed"):
|
||
|
turn = max(-0.8, min(0.8, turn*10))
|
||
|
car["engine"] = car["speed"]*10
|
||
|
|
||
|
# Cliffs
|
||
|
# turnback = True
|
||
|
# if rot[0] > 0.1 and not car.get("chased"):
|
||
|
# accelerate(car)
|
||
|
# elif rot[0] < -0.9 and sp < 0.9 and onGround:
|
||
|
# car.applyForce([0,-20, 0], True)
|
||
|
# turnback = False
|
||
|
#
|
||
|
#
|
||
|
#
|
||
|
|
||
|
# if minimum < 5:
|
||
|
# deccelerate(car)
|
||
|
# if 0.9 < turn or turn < -0.9:
|
||
|
# car["turn"] = turn *-1
|
||
|
# car.applyTorque([0.0,0.0,turn*-30],1)
|
||
|
#
|
||
|
# elif turnback:
|
||
|
# car["turn"] = 0.3
|
||
|
# car.applyTorque([0.0,0.0,car["turn"]*30],1)
|
||
|
#
|
||
|
# if "launchtime" not in car:
|
||
|
# car["launchtime"] = 300
|
||
|
# car["launchtime"] -= 1
|
||
|
# if 0.03 > sp and ( car["engine"] / max(0.1, engineHealth) > 1 ) and car["launchtime"] < 0:
|
||
|
# if not rot[0] < -0.1 and (leftm or rightm):
|
||
|
# car.applyTorque([0.0,0.0,50],1)
|
||
|
# car.position[2] += 0.05
|
||
|
#
|
||
|
#
|
||
|
# if sp > 2.8 and (0.3 < turn or turn < -0.3):
|
||
|
# for i in range(4):
|
||
|
# vehicle.applyEngineForce(car["speed"]*5* toFPS, i)
|
||
|
# if sp < 0: sp *= -1
|
||
|
# car.applyTorque([0.0,0.0,car["turn"]*sp*5* toFPS],1)
|
||
|
#
|
||
|
|
||
|
|
||
|
#for i in range(4):
|
||
|
# vehicle.applyEngineForce(car["engine"], i)
|
||
|
|
||
|
## ANGRY CARS GUIDED TO A POINT ##
|
||
|
|
||
|
elif car.get("npc") == "enemy" or ( car.get("npc") == "racer" and car.get("racing")):
|
||
|
|
||
|
# If racing
|
||
|
if car.get("npc") == "racer":
|
||
|
|
||
|
|
||
|
checkpoint = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][car.get("checkpoint")].copy()
|
||
|
if "raceData" in bge.logic.globalDict["races"][car.get("race")]:
|
||
|
raceData = bge.logic.globalDict["races"][car.get("race")]["raceData"]
|
||
|
targetTime = raceData["times"][car["checkpoint"]]
|
||
|
try: prevTime = raceData["times"][car["checkpoint"]-1]
|
||
|
except: prevTime = 0
|
||
|
lastCheckPointTime = car.get("last-checkpoint-time", bge.logic.globalDict["races"][car.get("race")]["start-time"])
|
||
|
currentTime = bge.logic.getRealTime() - bge.logic.globalDict["races"][car.get("race")]["start-time"]
|
||
|
|
||
|
targetTimeToCheckpoint = targetTime - prevTime
|
||
|
currentTimeToCheckpoint = currentTime - lastCheckPointTime
|
||
|
else:
|
||
|
targetTimeToCheckpoint = 1
|
||
|
currentTimeToCheckpoint = 1
|
||
|
|
||
|
car["enemy"] = checkpoint["location"]
|
||
|
|
||
|
# If the racing bot is following the player, it will mimic the player
|
||
|
#racePositions = bge.logic.globalDict["races"][car.get("race")].get("positions", [dani, car])
|
||
|
#if dani in racePositions and dani.get("driving") != car and racePositions.index(dani) < racePositions.index(car):
|
||
|
# car["enemy"] = dani["driving"]
|
||
|
|
||
|
# If reached checkpoint
|
||
|
if car.getDistanceTo(car["enemy"]) < checkpoint["radius"]:
|
||
|
if not checkpoint.get("OnLoop"):
|
||
|
car["prevrescue"] = car["rescue"]
|
||
|
car["rescue"] = checkpoint["location"]
|
||
|
car["rescueTo"] = car["checkpoint"]
|
||
|
|
||
|
# Printing if the bot is on time
|
||
|
if "raceData" in bge.logic.globalDict["races"][car.get("race")]:
|
||
|
car["last-checkpoint-time"] = currentTime
|
||
|
|
||
|
|
||
|
print("Race Time of [", car, "]:", round(currentTimeToCheckpoint, 2), round(targetTimeToCheckpoint - currentTimeToCheckpoint, 2))
|
||
|
|
||
|
# Calibrating the stopping distance
|
||
|
if checkpoint.get("Uturn"):
|
||
|
targetSpeed = -checkpoint.get("Uturn") * toFPS
|
||
|
car["stopdistance"] = car.get("stopdistance", -18) * ( car["speed"] / targetSpeed )
|
||
|
print("Race Stopping Calibration [", car, "]:", car.get("stopdistance", -18), int( ( car["speed"] / targetSpeed ) * 100), "%")
|
||
|
|
||
|
car["checkpoint"] = car["checkpoint"]+1
|
||
|
car["launchtime"] = 50
|
||
|
|
||
|
try:
|
||
|
car["distanceBetweenCheckpoints"] = car.getDistanceTo(bge.logic.globalDict["races"][car.get("race")]["checkpoints"][car.get("checkpoint")]["location"])
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
# If finished lap
|
||
|
if car.get("checkpoint") == len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"]):
|
||
|
print(car, "LAP")
|
||
|
car["checkpoint"] = 0
|
||
|
car["lap"] += 1
|
||
|
|
||
|
# If finished race
|
||
|
if car["lap"] == bge.logic.globalDict["races"][car.get("race")]["laps"]:
|
||
|
car["racing"] = False
|
||
|
car["npc"] = ""
|
||
|
bge.logic.globalDict["print"] = raceNames.get(car.name, car.name) + " finished race!"
|
||
|
|
||
|
# Making rescuing less sensitive when passing a Uturn
|
||
|
if bge.logic.globalDict["races"][car.get("race")]["checkpoints"][car.get("checkpoint")].get("Uturn"):
|
||
|
car["launchtime"] = 500
|
||
|
|
||
|
nextcheckpoint = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][ ( car.get("checkpoint") + 1 ) % len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"]) ]["location"]
|
||
|
tonextcheckpoint = car.getVectTo(nextcheckpoint)
|
||
|
|
||
|
# 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]
|
||
|
|
||
|
|
||
|
|
||
|
try:
|
||
|
toenemy = car.getVectTo(car.get("enemy", dani))
|
||
|
|
||
|
|
||
|
if car.get("npc") == "enemy" and sum([car["enemy"].get("Front_Right"), car["enemy"].get("Front_Left"), car["enemy"].get("Back_Right"), car["enemy"].get("Back_Left")])/4 < 0.2:
|
||
|
car["npc"] = "npc"
|
||
|
car["enemy"] = ""
|
||
|
except:
|
||
|
car["npc"] = "npc"
|
||
|
car["enemy"] = ""
|
||
|
return
|
||
|
|
||
|
enemyturn = min(0.8, toenemy[2][0]*2)
|
||
|
onenemy = toenemy[2][1] *-1
|
||
|
oldenemyturn = enemyturn
|
||
|
onoldenemy = onenemy
|
||
|
|
||
|
# If we are too slow. NITRO
|
||
|
if car.get("npc") == "racer":
|
||
|
|
||
|
betweencheckpoints = car.get("distanceBetweenCheckpoints", toenemy[0]) - checkpoint["radius"]
|
||
|
fractionDistance = 1 - (toenemy[0] / betweencheckpoints)
|
||
|
|
||
|
if ( currentTimeToCheckpoint * 1.2 > targetTimeToCheckpoint * fractionDistance or onenemy > 0.6 ) and not car.get("deccelerating") and onGround:
|
||
|
nitro()
|
||
|
|
||
|
else:
|
||
|
stopNitro()
|
||
|
|
||
|
try:
|
||
|
tonext = 1-min(1, toenemy[0]/(car["speed"]*-50) ) ** 0.2
|
||
|
except:
|
||
|
tonext = 0
|
||
|
|
||
|
if type(tonext) == complex:
|
||
|
tonext = float(tonext.real)
|
||
|
|
||
|
targetSpeed = 0
|
||
|
if car.get("npc") == "racer":
|
||
|
|
||
|
if "raceData" in bge.logic.globalDict["races"][car.get("race")]:
|
||
|
|
||
|
raceData = bge.logic.globalDict["races"][car.get("race")]["raceData"]
|
||
|
checkpoint["Uturn"] = raceData["targetSpeed"][car.get("checkpoint")]
|
||
|
|
||
|
if checkpoint.get("Uturn"):
|
||
|
|
||
|
# At current configuration car looses 1 speed at about 12.2 units of distance
|
||
|
targetSpeed = -checkpoint.get("Uturn") * toFPS
|
||
|
brakingDistance = ( car["speed"] - targetSpeed ) * -18# car.get("stopdistance", -18)
|
||
|
|
||
|
|
||
|
|
||
|
if toenemy[0] <= brakingDistance and car["speed"] < targetSpeed:
|
||
|
deccelerate(car)
|
||
|
|
||
|
|
||
|
# We want to record the stopdistance
|
||
|
# if targetSpeed <= car["speed"]:
|
||
|
# car["stopdistance"] = car.get("stopdistance", -18) + toenemy[0]
|
||
|
# print(car, "stopdistance Calibrated at:", car["stopdistance"])
|
||
|
# elif car["speed"] > targetSpeed and onenemy > 0.8:
|
||
|
# nitro()
|
||
|
|
||
|
|
||
|
if onenemy > 0.7 and minimum > 7:
|
||
|
|
||
|
nextenemyturn = min(0.8, tonextcheckpoint[2][0]*2)
|
||
|
nextonenemy = tonextcheckpoint[2][1] *-1
|
||
|
|
||
|
enemyturn = enemyturn + ( ( nextenemyturn - enemyturn ) * tonext )
|
||
|
onenemy = onenemy + ( ( nextonenemy - onenemy ) * tonext )
|
||
|
|
||
|
if type(onenemy) == complex:
|
||
|
onenemy = float(onenemy.real)
|
||
|
enemyturn = float(enemyturn.real)
|
||
|
|
||
|
|
||
|
|
||
|
if car["speed"] > -0.2 or engineHealth <= 0.2 or car["underwater"]:# or onenemy < 0.4:
|
||
|
rescue()
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
smartturn = ( turn * onoldenemy ) + ( oldenemyturn * onenemy) + ( enemyturn ) / ( onoldenemy + onenemy + 1)
|
||
|
#if onenemy < 0.6:
|
||
|
# smartturn = oldenemyturn
|
||
|
#if toenemy[0] < 60:
|
||
|
#smartturn = enemyturn
|
||
|
#if onenemy < 0.9:
|
||
|
# smartturn = enemyturn
|
||
|
#if onoldenemy < 0.7:
|
||
|
# smartturn = oldenemyturn
|
||
|
|
||
|
# If we about to hit a car head first
|
||
|
#if forwardData[0] and forwardData[0].get("speed"):
|
||
|
# handbreak(car)
|
||
|
# turn = max(-0.8, min(0.8, turn*-1000))
|
||
|
# smartturn = turn
|
||
|
|
||
|
|
||
|
if (( minimum < 10 and onenemy > 0.3 ) or ( minimum < 5 and onenemy > 0.1 )) and toenemy[0] > 50:
|
||
|
smartturn = turn
|
||
|
|
||
|
if minimum < 7:
|
||
|
car["turn"] = max(-0.5, min( 0.8 , car["turn"] * 1.5 ))
|
||
|
|
||
|
|
||
|
if carRacing: angleAt = -0.7
|
||
|
else: angleAt = 0
|
||
|
|
||
|
if onenemy > angleAt:
|
||
|
|
||
|
car["turn"] = smartturn
|
||
|
t = car["turn"]
|
||
|
if t < 0: t *= -1
|
||
|
|
||
|
# If we turning, turn harder
|
||
|
if t > 0.3 and car["speed"] < -1:
|
||
|
car["turn"] *= 3
|
||
|
# If we are turning hard enough, drift
|
||
|
#if t > 0.4 and car["speed"] < -2 and car["speed"] < targetSpeed / 2:
|
||
|
# handbreak(car)
|
||
|
# If we are turning very hard, use breaks
|
||
|
if t > 0.4 and car["speed"] < -1.5 and car["speed"] < targetSpeed:
|
||
|
deccelerate(car)
|
||
|
|
||
|
|
||
|
|
||
|
if not car.get("deccelerating"):
|
||
|
accelerate(car)
|
||
|
|
||
|
|
||
|
|
||
|
else:
|
||
|
|
||
|
if car.get("npc") == "racer":
|
||
|
rescue()
|
||
|
|
||
|
car["turn"] = enemyturn *-1
|
||
|
|
||
|
deccelerate(car)
|
||
|
|
||
|
#for i in range(4):
|
||
|
# vehicle.applyEngineForce(car["engine"], i)
|
||
|
|
||
|
################### APPLYING ##########################
|
||
|
|
||
|
# This is here to trigger an error for if car's object
|
||
|
# has been deleted from the game during the execution
|
||
|
# of this script. Otherwise, applying things to the vehicle
|
||
|
# constraints might result in Segmentation Fault ( the engine
|
||
|
# crashing ).
|
||
|
car.position
|
||
|
|
||
|
|
||
|
|
||
|
# Auto deceleration
|
||
|
if not car.get("accelerating") and car["engine"] > 0:
|
||
|
car["engine"] -= car.get("maxaccel",2) * toFPS
|
||
|
elif not car.get("accelerating") and car["engine"] < 0:
|
||
|
car["engine"] += car.get("maxaccel",2) * toFPS
|
||
|
else:
|
||
|
car["accelerating"] = False
|
||
|
|
||
|
|
||
|
# Auto going straiter
|
||
|
if not car.get("turning"):
|
||
|
car["turn"] *= 0.5 ** toFPS
|
||
|
else:
|
||
|
car["turning"] = False
|
||
|
|
||
|
ApplyEnginePower(car, vehicle)
|
||
|
# if not car.get("deccelerating"):
|
||
|
# minusfrac = 1
|
||
|
# if car["engine"] < 0: minusfrac = -1
|
||
|
# applyforce = math.log1p(car["engine"]*minusfrac) / 4
|
||
|
# if -0.05 < applyforce < 0.05:
|
||
|
# applyforce = 0
|
||
|
# else:
|
||
|
# #applyforce = Opt.Force(applyforce**0.1*3*minusfrac)
|
||
|
# applyforce = Opt.Force(HorsePowerToNewton(applyforce))*minusfrac
|
||
|
#
|
||
|
# if car.get("nitroing"):
|
||
|
# applyforce *= Opt.Force(0.3)
|
||
|
#
|
||
|
# vehicle.applyEngineForce(applyforce*car["Back_Left"], 0)
|
||
|
# vehicle.applyEngineForce(applyforce*car["Back_Right"], 1)
|
||
|
# vehicle.applyEngineForce(0, 2)
|
||
|
# vehicle.applyEngineForce(0, 3)
|
||
|
|
||
|
if not car.get("nitroing"):
|
||
|
car["engine"] = min( car.get("maxengine", 800)*engineHealth, max( -car.get("maxengine", 800)*engineHealth, car["engine"]))
|
||
|
else:
|
||
|
car["nitroing"] = False
|
||
|
|
||
|
if car.get("deccelerating"):
|
||
|
car["deccelerating"] = False
|
||
|
|
||
|
# Making sure the car is not turning more than it is possible.
|
||
|
car["turn"] = max(-0.8, min(0.8, car["turn"]))
|
||
|
|
||
|
|
||
|
# Suspension updates ( important due to FPS flactuations )
|
||
|
|
||
|
ApplySuspension(car, vehicle)
|
||
|
|
||
|
# Turning Force Update
|
||
|
|
||
|
vehicle.setSteeringValue(car["turn"]*car["Front_Left"] , 2)
|
||
|
vehicle.setSteeringValue(car["turn"]*car["Front_Left"] , 3)
|
||
|
|
||
|
# Adding fake torque to the car, to make it easier to turn
|
||
|
#if carSpeed < -0.05 and onGround:
|
||
|
# car.applyTorque([0.0,0.0,Opt.Force(car["turn"]*10)],1)
|
||
|
#elif carSpeed > 0.05 and onGround:
|
||
|
# car.applyTorque([0.0,0.0,Opt.Force(car["turn"]*-10)],1)
|
||
|
|
||
|
def ApplySuspension(car, vehicle):
|
||
|
|
||
|
for i in range(4):
|
||
|
suspension = car["suspensions"][i] / 100
|
||
|
suspense = Opt.Force(suspension*car.mass)
|
||
|
damping = 0.3
|
||
|
|
||
|
vehicle.setSuspensionCompression(1, i)
|
||
|
vehicle.setSuspensionStiffness(suspense, i)
|
||
|
vehicle.setSuspensionDamping(1, i)
|
||
|
|
||
|
def ApplyEnginePower(car, vehicle):
|
||
|
|
||
|
if not car.get("deccelerating"):
|
||
|
# minusfrac = 1
|
||
|
# if car["engine"] < 0: minusfrac = -1
|
||
|
# applyforce = math.log1p(car["engine"]*minusfrac) / 4
|
||
|
# if -0.05 < applyforce < 0.05:
|
||
|
# applyforce = 0
|
||
|
# else:
|
||
|
# #applyforce = Opt.Force(applyforce**0.1*3*minusfrac)
|
||
|
# applyforce = Opt.Force(HorsePowerToNewton(applyforce))*minusfrac
|
||
|
#
|
||
|
# if car.get("nitroing"):
|
||
|
# applyforce *= Opt.Force(0.3)
|
||
|
|
||
|
|
||
|
|
||
|
# Getting car's horse power
|
||
|
horses = car.get("horses", 750)
|
||
|
rpm = car["engine"]
|
||
|
maxrpm = car.get("maxengine", 800)
|
||
|
|
||
|
horses = EngineCurve(rpm, maxrpm, horses)
|
||
|
|
||
|
if car["active"]:
|
||
|
print(horses)
|
||
|
|
||
|
applyforce = Opt.Force(HorsePowerToNewton(horses / 2000))
|
||
|
|
||
|
vehicle.applyEngineForce(0, 0)
|
||
|
vehicle.applyEngineForce(0, 1)
|
||
|
vehicle.applyEngineForce(applyforce*car["Front_Left"], 2)
|
||
|
vehicle.applyEngineForce(applyforce*car["Front_Left"], 3)
|
||
|
|
||
|
def HorsePowerToNewton(value):
|
||
|
return value * 745.6998715823
|
||
|
|
||
|
def EngineCurve(rpm, maxrpm, horses):
|
||
|
|
||
|
# This function draws a simulated
|
||
|
# Engine RPM curve and outputs
|
||
|
# house power.
|
||
|
|
||
|
return math.sin(rpm/maxrpm*(math.pi/4*3))*horses
|
||
|
|
||
|
def accelerate(car):
|
||
|
|
||
|
|
||
|
if type(car) == bge.types.SCA_PythonController:
|
||
|
|
||
|
cont = bge.logic.getCurrentController()
|
||
|
scene = bge.logic.getCurrentScene()
|
||
|
car = cont.owner
|
||
|
if not car["active"]:
|
||
|
return
|
||
|
|
||
|
|
||
|
toFPS = Opt.ToFPS()
|
||
|
|
||
|
vehicle = bge.constraints.getVehicleConstraint(car["cid"])
|
||
|
|
||
|
|
||
|
|
||
|
car["engine"] += car.get("maxaccel",2) * toFPS
|
||
|
if car["engine"] > 0 and car["speed"] <= 0.2:
|
||
|
|
||
|
car["accelerating"]= True
|
||
|
|
||
|
|
||
|
else:
|
||
|
car["engine"] *= 0.95 ** toFPS
|
||
|
car["deccelerating"]= True
|
||
|
for i in range(4):
|
||
|
vehicle.applyEngineForce(Opt.Force(car["speed"]*5*car.mass), i)
|
||
|
|
||
|
|
||
|
|
||
|
def deccelerate(car):
|
||
|
|
||
|
|
||
|
|
||
|
if type(car) == bge.types.SCA_PythonController:
|
||
|
|
||
|
cont = bge.logic.getCurrentController()
|
||
|
scene = bge.logic.getCurrentScene()
|
||
|
car = cont.owner
|
||
|
if not car["active"]:
|
||
|
return
|
||
|
|
||
|
|
||
|
toFPS = Opt.ToFPS()
|
||
|
|
||
|
vehicle = bge.constraints.getVehicleConstraint(car["cid"])
|
||
|
|
||
|
|
||
|
car["engine"] -= car.get("maxaccel",0.003) * toFPS
|
||
|
|
||
|
|
||
|
if car["engine"] < 0 and car["speed"] >= -0.2:
|
||
|
car["accelerating"]= True
|
||
|
|
||
|
else:
|
||
|
car["engine"] *= 0.95 ** toFPS
|
||
|
car["deccelerating"]= True
|
||
|
for i in range(4):
|
||
|
vehicle.applyEngineForce(Opt.Force(car["speed"]*5*car.mass), i)
|
||
|
|
||
|
|
||
|
|
||
|
def turn():
|
||
|
|
||
|
cont = bge.logic.getCurrentController()
|
||
|
scene = bge.logic.getCurrentScene()
|
||
|
car = cont.owner
|
||
|
if not car["active"]:
|
||
|
return
|
||
|
|
||
|
toFPS = Opt.ToFPS()
|
||
|
|
||
|
if car["turn"] < 0: car["turn"] = 0
|
||
|
car["turn"] += 0.005 * toFPS
|
||
|
car["turning"]= True
|
||
|
|
||
|
def unturn():
|
||
|
|
||
|
cont = bge.logic.getCurrentController()
|
||
|
scene = bge.logic.getCurrentScene()
|
||
|
car = cont.owner
|
||
|
if not car["active"]:
|
||
|
return
|
||
|
|
||
|
toFPS = Opt.ToFPS()
|
||
|
|
||
|
if car["turn"] > 0: car["turn"] = 0
|
||
|
car["turn"] -= 0.005 * toFPS
|
||
|
car["turning"]= True
|
||
|
|
||
|
def handbreak(car):
|
||
|
|
||
|
if type(car) == bge.types.SCA_PythonController:
|
||
|
|
||
|
cont = bge.logic.getCurrentController()
|
||
|
scene = bge.logic.getCurrentScene()
|
||
|
car = cont.owner
|
||
|
if not car["active"]:
|
||
|
return
|
||
|
|
||
|
toFPS = Opt.ToFPS()
|
||
|
|
||
|
vehicle = bge.constraints.getVehicleConstraint(car["cid"])
|
||
|
for i in range(4):
|
||
|
vehicle.applyEngineForce(Opt.Force(car["speed"]*2*car.mass), i)
|
||
|
|
||
|
car["deccelerating"]= True
|
||
|
sp = car["speed"]
|
||
|
if sp < 0: sp *= -1
|
||
|
car.applyTorque([0.0,0.0,Opt.Force(car["turn"]*sp*2*car.mass)],1)
|
||
|
|
||
|
|
||
|
|
||
|
def get_in():
|
||
|
|
||
|
return
|
||
|
|
||
|
# This function has been moved to Character Controlls
|
||
|
|