# f1 = restart level1
# f2 = restart level2
# cu cr cd cl = control vehicle 1
# page down = reset vehicle 1
# delete = tractor beam vehicle 1
# w d s a = control vehicle 2
# e = reset vehicle 2
# q = tractor beam vehicle 2
from a7_scheduler import *
import default
import sys, os.path, random
# add_folder() works relative to the python.exe but i want it relative to the script
script = sys.argv[0]
folder = os.path.dirname(script)
folder = os.path.join(folder, "knights_on_wheels")
resource = os.path.join(folder, "resource.wrs")
add_folder(folder)
add_resource(resource)
add_folder("C:/Program Files (x86)/GStudio7/projects/carlevel")
camera_distance = 128
camera_tilt = 28
suspension_min = 6
suspension_max = 12
beam_length = 480
beam_force = 50000
vehicle_mass = 16
spring = 22750
damper = 425
motor_speed = 100
motor_power = 100
motor_brake = 180
steering_power = 100
max_laps = 3
energy_start = 100
energy_lap = 50
sd_distance = sd1_distance = 320
sd_threshold = sd1_threshold = 640
sd2_distance = 200
sd2_threshold = 416
second_camera = View()
second_camera.flags |= SHOW
vehicles = []
tires = []
tire_constraints = []
keys = (72, 77, 80, 75, 17, 32, 31, 30, 83, 16)
suspensions = ("frontleft", "frontright", "rearleft", "rearright")
needle = Bitmap("zeiger.tga")
tachometer = Bitmap("tachometer.tga")
huds = []
def set_physics(entity, hull, mhull, group, mass, friction, bounciness, min_speed, linear_damping, angular_damping):
entity.ph_settype(PH_RIGID, hull)
entity.ph_setmass(mass, mhull)
entity.ph_setfriction(friction)
entity.ph_setelasticity(bounciness, min_speed)
entity.ph_setdamping(linear_damping, angular_damping)
entity.ph_setgroup(group)
def reset_vehicle(vehicle):
print "reset", vehicle.name, "| driveline angle:", vehicle.nskills2[1]
for e in vehicle.tires + [vehicle]:
e.ph_enable(0)
v = (e.position - vehicle.position).rotateback(vehicle.orientation)
v = v.rotate((vehicle.nskills2[1], 0, 0)) + vehicle.nposition2
v.z += 64
e.position = v
if e is not vehicle and e.index in (1, 3, 5, 7): # tires of right side
o = 180
else:
o = 0
e.orientation = (vehicle.nskills2[1] - o, 0, 0)
e.ph_clearvelocity()
e.ph_enable(1)
def reset_vehicle_1():
reset_vehicle(vehicles[0])
e.on_pgdn = reset_vehicle_1
def reset_vehicle_2():
reset_vehicle(vehicles[1])
e.on_e = reset_vehicle_2
def toggle_vehicle_1_ai():
vehicles[0].ai = 1 - vehicles[0].ai
e.on_home = toggle_vehicle_1_ai
def toggle_vehicle_2_ai():
vehicles[1].ai = 1 - vehicles[1].ai
e.on_2 = toggle_vehicle_2_ai
#-------------------------------------------------------------------------------
def ang_diff(a1, a2):
a1 = ang(a1)
a2 = ang(a2)
if abs(a1 - a2) > 180: a2 += 360 * sign(a1 - a2)
return a1 - a2
@schedule
def vehicle():
my = e.my
my.index = len(vehicles)
vehicles.append(my)
print "vehicle:", my.name, my.index
my.winner = False
my.ai = False
energy = energy_start
lap = 0
elapsed_time = 0
speed = 0
last_node = 0
distance = 0
message_time = 0
if my.index == 0:
camera = second_camera
group = 2
o = 0
else:
camera = e.camera
group = 4
o = 4
set_physics(my, PH_BOX, PH_BOX, group, vehicle_mass, 40, 25, 10, 20, 20)
my.path_set("driveline")
# count nodes
nodes = 1
while my.path_getnode(nodes):
nodes += 1
print "nodes:", nodes
yield(1) # wait for initialization of all entities
front_left = tire_constraints[0+o]
front_right = tire_constraints[1+o]
rear_left = tire_constraints[2+o]
rear_right = tire_constraints[3+o]
my.tires = tires[0+o:4+o]
opponent = vehicles[1 - my.index]
global huds
if len(huds) == 2: # delete previously created panels after loading a level
for p in huds: p.remove()
huds = []
hud = Panel()
hud.pos_x = e.screen_size.x - 160 - e.screen_size.x / 2 * my.index
hud.pos_y = 10
hud.bmap = tachometer
hud.setneedle(0, 76, 76, needle, 70, 70, 130, 200, 0, lambda: speed)
hud.flags |= SHOW
huds.append(hud)
while 1:
# shortcut detection
if lap < max_laps:
elapsed_time += e.time_step / 16
node = my.path_scan(my.position, my.orientation, (360, 0, sd_distance))
if node:
my.nposition1, my.nskills1 = my.path_getnode(node)
my.nposition2, my.nskills2 = my.path_getnode(node * (last_node == 0) + last_node)
# crossed finish line
if my.nskills1[2] == 1 and my.nskills2[2] == 2:
lap += 1
energy = min(energy + energy_lap, 100)
if lap == max_laps:
if opponent.winner == False:
my.winner = True
# crossed finish line in reverse or shortcut
if (my.nskills1[2] == 2 and my.nskills2[2] == 1)\
or (distance != 0 and distance < (my.nskills1[0] - my.nskills2[0] - sd_threshold)):
message_time = elapsed_time + 3
reset_vehicle(my)
else:
last_node = node
distance = 0
else: # outside of the track
d = my.position.dist(last_position)
if d > 0.1: distance += d
last_position = my.position
# hud
message = ""
if my.winner: message = "winner!"
elif message_time > elapsed_time: message = "invalid shortcut!"
t = (my.index + 1,
elapsed_time / 60,
elapsed_time % 60,
fraction(elapsed_time) * 100,
speed * 1.2,
clamp(lap + 1, 1, max_laps),
energy,
message)
hud_string = "player %d\ntime: %02d:%02d:%02d\nspeed: %dkm/h\nlap: %d\nenergy: %d%%\n\n%s" % t
hud_string_x = e.screen_size.x / 2 + 20 - e.screen_size.x / 2 * my.index
draw_text(hud_string, hud_string_x, 20, (0,0,0))
hud.pos_x = e.screen_size.x - 160 - e.screen_size.x / 2 * my.index
# controls
if my.ai:
# very simple and stupid ai
key_up = key_right = key_down = key_left = 0
n = my.path_getnode(cycle(last_node + 3, 1, nodes))
if n:
p, s = n
draw_point3d(p, (0,255,0), 100, 20)
a = (p - my.position).to_angle().pan
d = ang_diff(my.pan, a)
key_right = clamp(d * 0.01, -1, 1)
else:
reset_vehicle(my)
n = my.path_getnode(cycle(last_node + 9, 1, nodes))
if n:
p, s = n
draw_point3d(p, (0,0,255), 100, 20)
a = s[1]
d = abs(ang_diff(my.pan, a))
target_speed = max(20, 100 - d)
if speed < target_speed:
key_up = 1
if speed - 40 > target_speed:
key_down = 1
c_trace((my.x, my.y, my.z + 100), (my.x, my.y, my.z - 100), IGNORE_ME)
if e.trace_hit:
draw_line3d(e.target, None, 100)
draw_line3d(e.target + e.normal * 100, (0,0,255), 100)
draw_line3d(e.target + e.normal * 100, (0,0,255), 100)
dot = e.normal * Vector(0, 0, 1).rotate(my.orientation)
if dot < 0.95: # brake to prevent tipping over
key_up = 0
key_down = 1
if dot < 0: # tipping over couldn't be prevented
reset_vehicle(my)
else:
# manual control
key_up = key_pressed(keys[0 + 4 * my.index])
key_right = key_pressed(keys[1 + 4 * my.index])
key_down = key_pressed(keys[2 + 4 * my.index])
key_left = key_pressed(keys[3 + 4 * my.index])
key_tractor = key_pressed(keys[8 + my.index])
motor = Vector()
motor.x = motor_speed * key_up * (not key_down)
motor.y = motor_power * key_up * (not key_down) + motor_brake * key_down
rear_left.setmotor(nullvector, motor, nullvector)
rear_right.setmotor(nullvector, motor, nullvector)
steering = Vector()
steering.y = steering_power
p1 = front_left.getposition()
steering.x = (30 * (key_right - key_left)) - p1.x * min(e.time_step, 1)
front_left.setmotor(steering, motor, nullvector)
p2 = front_right.getposition()
steering.x = (30 * (key_right - key_left)) - p2.x * min(e.time_step, 1)
front_right.setmotor(steering, motor, nullvector)
# suspension
my.animate("steering", clamp((p1.x + p2.x) / 2 * (100 / 60.0) + 50, 0, 100), 0)
# camera
if not (default.def_camera and my.index == 1):
offset = Vector(my.pan, -camera_tilt, 0).for_angle() * -camera_distance
camera.position += ((my.position + offset) - camera.position) * min(0.4 * e.time_step, 1)
if abs(my.pan - camera.pan) > 180: # always take the shorter way around
camera.pan += 360 * sign(my.pan - camera.pan)
camera.pan += (my.pan - camera.pan) * min(0.4 * e.time_step, 1)
camera.tilt = -camera_tilt * 0.5
# tractor beam
if key_tractor and energy > 0:
energy = max(energy - e.time_step, 0)
p1 = my.vec_for_vertex(1646)
p2 = p1 + my.orientation.for_angle() * beam_length
c_trace(p1, p2, IGNORE_ME|IGNORE_FLAG2)
if e.trace_hit:
p2 = e.target
if e.you is opponent:
e.you.ph_addforceglobal(-(my.orientation.for_angle() * beam_force), p2)
for i in range(0, int(p1.dist(p2)), 2):
p1 += my.orientation.for_angle() * 2 + Vector(random.random()*0.8-0.4, random.random()*0.8-0.4, random.random()*0.8-0.4)
draw_point3d(p1, (255*(my.index==0), 0, 255*(my.index==1)), 100, 2)
# get speed
v = my.ph_getvelocity(nullvector)
speed = (v.length() / 32) * 60 * 60 / 1000.0
yield(1)
#-------------------------------------------------------------------------------
@schedule
def tire():
my = e.my
my.index = len(tires)
tires.append(my)
print "tire:", my.name, my.index
if my.index < 4:
group = 2
vehicle = vehicles[0]
else:
group = 4
vehicle = vehicles[1]
set_physics(my, PH_SPHERE, PH_SPHERE, group, 30, 80, 50, 10, 20, 20)
if my.index in (0, 1, 4, 5): # front tires
steering_limit_l = -30
steering_limit_r = 30
else:
steering_limit_l = 0
steering_limit_r = 0
c = Constraint(PH_WHEEL, vehicle, my)
c.setparams(my.position, (0, 0, 1), (1, 0, 0), (steering_limit_l, steering_limit_r, 0), nullvector, (spring, damper, 0))
tire_constraints.append(c)
while 1:
# suspension
z = (my.position - vehicle.position).rotateback(vehicle.orientation).z
s = (100.0 / (suspension_max - suspension_min)) * (z - suspension_min) - 30
vehicle.animate(suspensions[my.index % 4], clamp(s, 0, 100), ANM_ADD)
yield(1)
#-------------------------------------------------------------------------------
@schedule
def water():
while 1:
e.my.v += 0.8 * e.time_step
yield(1)
#-------------------------------------------------------------------------------
@schedule
def main():
print "main!"
e.fps_max = 120
e.video_mode = 8
e.video_screen = 2
e.preload_mode = 3
e.time_smooth = 0.9
e.fog_color = 1
e.camera.fog_end = second_camera.fog_end = 75000
global sd_distance, sd_threshold, vehicles, tires, tire_constraints
vehicles = []
tires = []
tire_constraints = []
if not e.key_f2:
sd_distance = sd1_distance
sd_threshold = sd1_threshold
level_load("canyon.wmb", globals()) # pass global namespace so that the entity actions get found
else:
sd_distance = sd2_distance
sd_threshold = sd2_threshold
level_load("city.wmb", globals()) # pass global namespace so that the entity actions get found
e.camera.x = e.camera.y = e.camera.tilt = second_camera.x = second_camera.y = second_camera.tilt = 0
e.camera.pan = e.camera.z = second_camera.pan = second_camera.z = 90
e.sky_cube_level.material = e.mtl_unlit
e.on_f1 = e.on_f2 = main
ph_setgravity((0, 0, -320 * 1.4))
ph_setcorrections(25000, 0.05)
while e.key_f1 or e.key_f2: yield(1)
while not (e.key_f1 or e.key_f2):
e.camera.size_x = second_camera.size_x = second_camera.pos_x = e.screen_size.x / 2
yield(1)
main()
scheduler()