ShitboxInfinity/tests/smoke.gd
m3ultra 1753b4c0b0 SPOTTO: knock the fuel cap off a yellow one
Burnout's yellow warning diamonds are the game telling you it noticed,
and half of why it feels good. src/callout.gd builds them: diamond,
outlined text, punch-scale in (overshoot then snap, which reads as
IMPACT), stack of three with the older ones drifting up and out.
TAKEDOWN!, NEAR MISS, BIG AIR with the airborne time -- and the new
Australian one.

Roughly one traffic car in six is now painted yellow, deterministically
per car so junction convoys stay learnable puzzles, via a surface
OVERRIDE -- painting the shared fleet material would have turned every
copy of that model yellow across the whole city, the same trap the
garage's stat upgrades hit.

Sideswipe any car hard enough and its FUEL CAP pings off: a real
physics prop, chrome (or yellow), spat off the correct flank behind the
midline with sparks, tumbling into the road for ten seconds. Do it to a
yellow one and it's SPOTTO! -- 0.45 boost, cash or crash-score, a
running session tally, and a sting pitched up 35%. Merely near-missing
a yellow one at speed still earns the shout, because that IS the game
as played in the back of every Falcon on the Bruce Highway.

Sideness is judged in the victim's local frame before it goes dynamic
(|x| vs |z|), so a rear-ender is a shunt, not a sideswipe -- asserted,
along with one-cap-per-car and the cap prop actually existing.

Airtime rides along: car.gd tracks time with no wheel down and emits
landed() past 0.55 s, which pays boost and shouts BIG AIR.

Callout placement note: Control.position is relative to the PARENT, not
the anchor, so anchoring the row and then tweening position:y yanked
every badge to the top of the screen. Screen-space maths instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 13:51:09 +10:00

572 lines
25 KiB
GDScript

extends SceneTree
## Headless smoke test:
## /Applications/Godot.app/Contents/MacOS/Godot --headless --path . --script tests/smoke.gd
## Registry finds content; each mode boots and simulates; the car drives; traffic
## gets smashed when driven through.
func _initialize() -> void:
_run()
func _run() -> void:
var cars := Registry.cars()
var levels := Registry.levels()
_check(not cars.is_empty(), "no cars found")
_check(not levels.is_empty(), "no levels found")
for p in cars.values():
var s: CarStats = load(p)
_check(s != null and s.engine_power > 0.0, "bad stats: %s" % p)
if s.model_path != "":
_check(load(s.model_path) is PackedScene, "bad model: %s" % s.model_path)
# datto is procedural (has det_* parts); imported models legitimately have none
Game.car_path = cars.get("datto_120y", cars.values()[0])
# the carpark is the controlled arena; city levels have walls that complicate the ram test
Game.level_path = levels.get("woolies_carpark", levels.values()[0])
# garage economy: scratch save (NEVER the real one), earn/buy roundtrip,
# stats + bolt-ons applied to the player, then reset so later stages run stock
print(" .. garage")
Garage.save_path = "user://garage_smoketest.save"
Garage._reset_for_test()
_check(Garage.bucks() == 0, "scratch garage not empty")
Garage.earn(4000)
_check(Garage.bucks() == 4000, "earn didn't bank")
_check(Garage.buy("datto_120y", "spoiler"), "couldn't buy the wing")
_check(Garage.buy("datto_120y", "turbo"), "couldn't buy the turbo")
_check(not Garage.buy("datto_120y", "spoiler"), "bought the same wing twice")
_check(not Garage.buy("datto_120y", "exhaust"), "bought a $2000 pipe with $500")
_check(Garage.bucks() == 500, "change is wrong (%d)" % Garage.bucks())
var base_power: float = (load("res://cars/datto_120y/stats.tres") as CarStats).engine_power
var gg: Game = await _boot("cruise")
_check(gg.car.stats.engine_power > base_power * 1.15, "turbo didn't boost power")
_check(gg.car.stats.grip > (load("res://cars/datto_120y/stats.tres") as CarStats).grip + 0.5,
"wing added no grip")
_check(gg.car.stats.resource_path == "", "upgrades mutated the SHARED stats resource")
gg.free()
# the shed: tunes are gated on tools, and retorque hardens the panels
Garage.earn(2000)
_check(not Garage.buy_tune("datto_120y", "retorque"), "tuned without owning the tool")
_check(Garage.buy_tool("sockets"), "couldn't buy the socket set")
_check(Garage.buy_tune("datto_120y", "retorque"), "tool owned but tune refused")
_check(not Garage.buy_tune("datto_120y", "retorque"), "same tune sold twice")
gg = await _boot("cruise")
_check(gg.car.part_dv > Car.PART_DV + 1.0, "retorque didn't harden panels (%.1f)" % gg.car.part_dv)
gg.free()
# raceday persistence: ladder rung and per-car quarter-mile PBs
_check(Garage.drag_rung() == 0, "fresh save not on ladder rung 0")
Garage.drag_advance()
_check(Garage.drag_rung() == 1, "ladder didn't advance")
_check(Garage.record_drag_pb("datto_120y", 14.5), "first ET not a PB")
_check(not Garage.record_drag_pb("datto_120y", 15.0), "slower ET counted as a PB")
_check(Garage.record_drag_pb("datto_120y", 13.9), "faster ET refused as PB")
_check(absf(Garage.drag_pb("datto_120y") - 13.9) < 0.01, "PB not stored")
# tool tiers: buying again upgrades; effects are exercised by wrench jobs
Garage.earn(1500 + 3000)
_check(Garage.tool_tier("sockets") == 1, "owned sockets not tier 1")
_check(Garage.buy_tool("sockets"), "tier 2 upgrade refused")
_check(Garage.buy_tool("sockets"), "tier 3 upgrade refused")
_check(Garage.tool_tier("sockets") == 3 and not Garage.buy_tool("sockets"),
"tier cap broken (tier %d)" % Garage.tool_tier("sockets"))
_check(Garage.has_tool("sockets"), "tiered tool lost has_tool")
# faults: break it, boot wounded, pay the shop for one, wrench the other
print(" .. repair")
Garage.add_fault("datto_120y", "gearbox")
Garage.add_fault("datto_120y", "gasket")
_check(Garage.faults("datto_120y").size() == 2, "faults didn't record")
gg = await _boot("cruise")
_check(gg.car.stats.engine_power < base_power * 0.9, "wounded car boots at full power")
_check(gg.car.leaking, "gearbox-faulted car isn't leaking")
gg.free()
Garage.earn(1000)
_check(Garage.shop_fix("datto_120y", "gasket"), "shop wouldn't take the money")
_check(not Garage.faults("datto_120y").has("gasket"), "shop fix didn't clear the fault")
# the gearbox goes through the 3D wrench: diagnose (wrong first, penalty),
# then the toolless 60 kg drag path -- no hoist owned on this save
Garage.ui_stats_path = "res://cars/datto_120y/stats.tres"
Garage.ui_fault = "gearbox"
var ui = (load("res://src/wrench.tscn") as PackedScene).instantiate()
root.add_child(ui)
await process_frame
_check(ui.steps[0]["verb"] == "diagnose", "gearbox job skipped diagnosis")
_check(ui.steps.size() == 8, "toolless gearbox sheet wrong (%d steps)" % ui.steps.size())
var right := -1
for i in ui.light_options.size():
if ui.light_options[i] == Garage.FAULTS["gearbox"]["light"]:
right = i
ui.pick_light((right + 1) % 3)
_check(ui.penalty_turns > 0.0 and ui.quality < 100.0, "wrong diagnosis cost nothing")
ui.pick_light(right)
_check(ui.step_index == 1, "right light didn't advance the job")
await _drive_wrench(ui, false)
_check(ui.done, "gearbox job never finished (step %d)" % ui.step_index)
_check(not Garage.faults("datto_120y").has("gearbox"), "wrench fix didn't clear the fault")
_check(Garage.quality("datto_120y", "gearbox") >= 85.0, "clean-ish gearbox scored %d"
% int(ui.quality))
ui.free()
# a gasket job at 85+ raises the overheat ceiling; gearbox 85+ adds a strike
Garage.add_fault("datto_120y", "gasket")
Garage.ui_fault = "gasket"
ui = (load("res://src/wrench.tscn") as PackedScene).instantiate()
root.add_child(ui)
await process_frame
await _drive_wrench(ui, false)
_check(ui.done and Garage.quality("datto_120y", "gasket") >= 85.0,
"gasket job didn't finish clean (%d)" % int(ui.quality))
ui.free()
Game.level_path = levels["willowbank_dragway"]
gg = await _boot("drag")
_check(gg.box_limit == 4, "85+ gearbox rebuild paid no extra strike (%d)" % gg.box_limit)
_check(gg.gasket_cap > 1.1, "85+ gasket job raised no ceiling (%.2f)" % gg.gasket_cap)
gg.free()
Game.level_path = levels.get("woolies_carpark", levels.values()[0])
# THE SHED home base: owned tools materialise (sockets were bought above,
# ramps were not), and rolling into the bay and holding still opens the garage
print(" .. shed")
Game.level_path = levels["the_shed"]
gg = await _boot("cruise")
_check(gg.garage_bay != null, "shed has no GarageBay")
var owned_props: Array = gg.find_children("*", "RigidBody3D", true, false).filter(
func(n): return n.has_meta("shed_tool"))
_check(owned_props.size() == 1 and owned_props[0].get_meta("shed_tool") == "sockets",
"owned-tool display wrong (%d shed_tool props)" % owned_props.size())
var bay_at: Vector3 = gg.garage_bay.global_position
var bay_xf := Transform3D(Basis.IDENTITY, bay_at + Vector3.UP * 0.75)
PhysicsServer3D.body_set_state(gg.car.get_rid(), PhysicsServer3D.BODY_STATE_TRANSFORM, bay_xf)
gg.car.linear_velocity = Vector3.ZERO
gg.car._prev_lv = Vector3.ZERO
for i in 150:
await physics_frame
gg.car.linear_velocity = Vector3.ZERO
if gg.garage_requested:
break
_check(gg.garage_requested, "rolling into the bay never opened the garage")
gg.free()
# phase 3: a driveway customer waits, gets their car fixed, and pays
print(" .. customer")
var cust := Garage.customer()
_check(not cust.is_empty(), "no customer turned up at the shed")
gg = await _boot("cruise")
_check(gg.customer_spot != null, "customer car has no CustomerSpot")
var cs_xf := Transform3D(Basis.IDENTITY, gg.customer_spot.global_position + Vector3.UP * 0.75)
PhysicsServer3D.body_set_state(gg.car.get_rid(), PhysicsServer3D.BODY_STATE_TRANSFORM, cs_xf)
gg.car.linear_velocity = Vector3.ZERO
gg.car._prev_lv = Vector3.ZERO
for i in 150:
await physics_frame
gg.car.linear_velocity = Vector3.ZERO
if gg.job_taken:
break
_check(gg.job_taken and Garage.ui_customer, "holding by the customer never took the job")
_check(Garage.ui_fault == cust["fault"], "job fault mismatch")
gg.free()
var bucks_before: int = Garage.bucks()
var rep_before: int = Garage.rep()
var cw = (load("res://src/wrench.tscn") as PackedScene).instantiate()
root.add_child(cw)
await process_frame
_check(cw.customer, "wrench didn't know it was a customer job")
await _drive_wrench(cw, false)
_check(cw.done, "customer job never finished (step %d)" % cw.step_index)
_check(Garage.bucks() > bucks_before, "customer didn't pay")
_check(Garage.rep() > rep_before, "clean customer job earned no rep")
_check(Garage.customer().is_empty(), "customer never left the driveway")
_check(Garage.quality(String(cust["car"]), String(cust["fault"])) == 0.0,
"customer job polluted own-car quality")
cw.free()
Game.level_path = levels.get("woolies_carpark", levels.values()[0])
# wrench phase 1: the 3D shed jobs. A clean flat-tyre run scores 90+ and
# buffs the car; a sabotaged torque window strips a thread on the oil job.
print(" .. wrench")
Garage.add_fault("datto_120y", "flat")
Garage.ui_stats_path = "res://cars/datto_120y/stats.tres"
Garage.ui_fault = "flat"
var wr = (load("res://src/wrench.tscn") as PackedScene).instantiate()
root.add_child(wr)
await process_frame
_check(wr.steps.size() == 7, "flat-tyre job sheet wrong (%d steps)" % wr.steps.size())
await _drive_wrench(wr, false)
_check(wr.done, "flat-tyre job never finished (step %d)" % wr.step_index)
_check(wr.quality >= 90.0, "clean flat run scored %d" % int(wr.quality))
_check(not Garage.faults("datto_120y").has("flat"), "flat fault not cleared")
_check(Garage.quality("datto_120y", "flat") >= 85.0, "workmanship not recorded")
wr.free()
gg = await _boot("cruise") # 85+ fix = grip buff on the next boot
var dstats: CarStats = load("res://cars/datto_120y/stats.tres")
_check(gg.car.stats.grip > dstats.grip + 0.7, "85+ fix paid no grip buff (%.2f vs %.2f)"
% [gg.car.stats.grip, dstats.grip])
gg.free()
Garage.add_fault("datto_120y", "oil")
Garage.ui_fault = "oil"
wr = (load("res://src/wrench.tscn") as PackedScene).instantiate()
root.add_child(wr)
await process_frame
await _drive_wrench(wr, true)
_check(wr.done, "oil job never finished (step %d)" % wr.step_index)
_check(wr.quality <= 90.0, "stripped thread cost nothing (%d)" % int(wr.quality))
_check(wr.steps.size() == 8, "extraction step never appeared (%d steps)" % wr.steps.size())
wr.free()
Garage._reset_for_test() # later stages must run stock
# cruise: throttle 3s, car must move
print(" .. cruise")
var game: Game = await _boot("cruise")
if game.car.stats.model_path != "":
_check(game.car._parts.size() >= 8, "model has no detachable det_* parts")
var start: Vector3 = game.car.global_position
Input.action_press("throttle")
for i in 180:
await physics_frame
var dist := start.distance_to(game.car.global_position)
_check(dist > 5.0, "car didn't move (%.1f m)" % dist)
Input.action_release("throttle")
game.free()
# speed kit: at pace the lens must widen, the screen pass must engage, the
# motes must fly and the wind must blow -- all off ONE number (km/h)
print(" .. speed")
game = await _boot("cruise")
_check(game.speed_fx != null and game._rush_p != null, "speed kit not built")
var fov0: float = game.cam.fov
for i in 30:
await physics_frame
game.car.linear_velocity = -game.car.global_basis.z * 45.0 # ~160 km/h
await process_frame
_check(game.cam.fov > fov0 + 8.0, "FOV never opened up (%.1f -> %.1f)" % [fov0, game.cam.fov])
_check(game.speed_fx.rush > 0.25, "screen rush never engaged (%.2f)" % game.speed_fx.rush)
_check(game.speed_fx.rect.visible, "screen pass stayed hidden at speed")
_check(game._rush_p.emitting, "rush motes not emitting at speed")
var fast_fov: float = game.cam.fov
game.car.boosting = true
await process_frame
_check(game.cam.fov > fast_fov, "boost punch didn't kick the lens")
game.free()
# crash mode: traffic spawned, ram the nearest one, score must rise
print(" .. crash")
game = await _boot("crash")
_check(game.traffic.size() == Game.TRAFFIC_COUNT["crash"], "traffic missing")
var victim: TrafficCar = game.traffic[0]
await physics_frame # traffic takes position on its first physics tick
Input.action_press("throttle")
for attempt in 8: # re-aim: victim keeps moving along the loop
if not victim.live:
break
var to_victim := (victim.global_position - game.car.global_position)
game.car.global_position = victim.global_position - victim.global_basis.z * 8.0
game.car.look_at(victim.global_position)
to_victim = (victim.global_position - game.car.global_position).normalized()
game.car.linear_velocity = to_victim * 18.0
for i in 30:
await physics_frame
if not victim.live:
break
Input.action_release("throttle")
_check(not victim.live, "rammed traffic never went dynamic")
_check(game.score > 0.0, "crash mode scored nothing")
# SPOTTO: a side-on hit knocks the fuel cap off, and a YELLOW one is the
# national game. Nose-to-tail must NOT count -- that's a shunt.
var vic2: TrafficCar = game.traffic[1]
vic2.yellow = true
var spot0: int = game.spottos
_check(not vic2.flank_hit(-vic2.global_basis.z * 12.0), "rear-ender counted as a sideswipe")
_check(vic2.flank_hit(vic2.global_basis.x * 12.0), "side hit didn't shed the cap")
_check(game.spottos == spot0 + 1, "yellow sideswipe didn't score a SPOTTO")
_check(not vic2.flank_hit(vic2.global_basis.x * 12.0), "car shed two fuel caps")
var caps := 0
for n in game.get_children():
if n is RigidBody3D and n.get_child_count() == 2 and not (n is TrafficCar) \
and not (n is Car):
caps += 1
_check(caps >= 1, "no fuel cap prop in the world")
var yellows := 0
for t2 in game.traffic:
if t2.yellow:
yellows += 1
_check(yellows >= 1, "no yellow cars on the road at all (%d)" % game.traffic.size())
var crash_score := game.score
game.free()
# junction: convoys spawn, driving straight in causes the crash -> settle ->
# crashbreaker -> tally
var jcts := Registry.junctions().filter(func(j): return j.level_id == "woolies_carpark")
_check(not jcts.is_empty(), "no woolies junction event")
Game.junction = jcts[0].data
print(" .. junction")
game = await _boot("junction")
_check(game.traffic.size() >= 8, "junction convoys missing (%d)" % game.traffic.size())
Input.action_press("throttle")
for i in 950:
await physics_frame
if game.jstate == "settle" and not game.breaker_used:
Input.action_release("throttle")
Input.action_press("boost")
elif game.breaker_used:
Input.action_release("boost")
if game.over:
break
Input.action_release("throttle")
Input.action_release("boost")
_check(game.jstate != "run", "never crashed into the convoy")
_check(game.breaker_used, "crashbreaker never fired")
_check(game.over and game.score > 0.0, "junction never tallied (score %d)" % int(game.score))
var jct_score := game.score
Game.junction = {}
game.free()
# outrun: roaming rivals exist in cruise; pulling alongside at speed arms the
# duel, and the win path pays out and clears state
game = await _boot("cruise")
_check(game.rivals.size() == Game.ROAM_RIVALS, "no roaming rivals in cruise")
var rr: Rival = game.rivals[0]
await physics_frame
game.car.global_position = rr.global_position + Vector3(3, 0.3, 0)
game.car.linear_velocity = Vector3(0, 0, -12) # above the 8 m/s arming speed
for i in 5:
await physics_frame
if game.outrun_rival != null:
break
_check(game.outrun_rival == rr, "outrun didn't arm on proximity")
var bm0: float = game.car.boost_max
game._outrun_end(true, "test")
_check(game.outrun_rival == null and game.outruns_won == 1, "outrun win didn't settle")
_check(game.car.boost_max > bm0, "outrun win paid no boost")
_check(game.outrun_cd > 0.0, "no re-arm cooldown after outrun")
game.free()
# pursuit: smashing raises heat, heat summons cops, fleeing loses them
print(" .. pursuit")
game = await _boot("cruise")
for i in 10:
game.add_heat(Game.HEAT_PER_SMASH)
_check(game.cops.size() >= Game.COPS_MIN, "heat didn't summon cops")
game.car.global_position += Vector3(900, 0, 900)
for i in 700:
await physics_frame
if game.cops.is_empty():
break
_check(game.escapes == 1 and game.cops.is_empty(), "escape never resolved")
_check(game.heat_cd > 0.0, "no cooldown after escape")
game.free()
# drag: the Willowbank quarter mile -- tree staging, ladder rival, then the box
Game.level_path = levels["willowbank_dragway"]
print(" .. drag")
game = await _boot("drag")
_check(absf(game.drag_len - 402.0) < 5.0, "quarter mile measures %.0f m" % game.drag_len)
_check(game.rivals.size() == 1, "drag had no opponent")
_check(game.drag_state == "stage", "no tree staging phase")
_check(game.drag_rung_now == 0, "fresh ladder not starting at rung 0")
_check(game.rivals[0].stats.resource_path.contains("morry"),
"rung 0 rival isn't Nanna (%s)" % game.rivals[0].stats.resource_path)
_check(game._tree_bulbs.size() == 6, "christmas tree bulbs missing (%d)" % game._tree_bulbs.size())
# TRELLIS'd cashies tools: once the library exists, the paddock is strewn
var tool_dir := DirAccess.open("res://assets/tools")
if tool_dir != null:
var glbs := 0
for f in tool_dir.get_files():
if f.ends_with(".glb"):
glbs += 1
if glbs > 0:
var strewn := 0
for n in game.find_children("*", "RigidBody3D", true, false):
if n.has_meta("cc_tool"):
strewn += 1
_check(strewn == glbs, "tool library has %d GLBs but %d spawned" % [glbs, strewn])
await physics_frame
_check(game.rivals[0].power_scale == 0.0, "rival not held at the tree")
# burnout in the box: DRIFT+GAS warms the tyres and must NOT roll the beams
Input.action_press("drift")
Input.action_press("throttle")
for i in 80:
await physics_frame
if game.drag_state != "stage":
break
_check(game.tyre_temp > 0.25, "burnout warmed nothing (%.2f)" % game.tyre_temp)
_check(not game.jumped, "burnout rolled the beams (red light)")
_check(game.car.burnout_on, "burnout fx never engaged")
Input.action_release("drift")
Input.action_release("throttle")
# hold still like a disciplined launch: no red light, wait out the ambers
var tree_spin := 0
while game.drag_state != "run" and tree_spin < 400:
await physics_frame
tree_spin += 1
_check(game.drag_state == "run", "tree never went green")
_check(not game.jumped, "stationary car scored a red light")
var green_m := (game._tree_bulbs["TreeGreen"] as MeshInstance3D).material_override as StandardMaterial3D
_check(green_m.emission_enabled, "green bulb didn't light")
Input.action_press("throttle")
for i in 10:
await physics_frame
_check(game.revs > 0.0, "revs never climbed")
var g0: int = game.gear
# shift the moment revs enter the window. First gear redlines in ~1.6 s, so
# idling here first pins revs at 1.0 and the shift correctly scores as blown.
var spin := 0
while game.revs < Game.SHIFT_LO and spin < 600: # bounded: never hang the suite
await physics_frame
spin += 1
_check(game.revs >= Game.SHIFT_LO and game.revs < Game.REDLINE,
"revs %.2f not inside the shift window [%.2f, %.2f)" % [game.revs, Game.SHIFT_LO, Game.REDLINE])
Input.action_press("boost")
await physics_frame
Input.action_release("boost")
await physics_frame
_check(game.gear == g0 + 1, "shift didn't change gear")
_check(game.clean_shifts == 1 and game.blown_shifts == 0,
"in-window shift scored as blown (%d/%d)" % [game.clean_shifts, game.blown_shifts])
# grenade the box: three panic shifts nowhere near the window
for i in 3:
game.revs = 0.10
Input.action_press("boost")
await physics_frame
Input.action_release("boost")
await physics_frame
_check(game.box_blown, "gearbox survived three blown shifts (%d strikes)" % game.box_strikes)
_check(game.car.leaking, "grenaded box isn't leaking oil")
var locked: int = game.gear
Input.action_press("boost")
await physics_frame
Input.action_release("boost")
await physics_frame
_check(game.gear == locked, "blown box still shifts")
# and cook the coolant: past 1.0 the head gasket lets go
game.eng_temp = 1.05
await physics_frame
_check(game.gasket_blown, "overcooked engine kept its head gasket")
# finishing the run sends the carnage home as persistent faults
game._drag_finish(true)
_check(Garage.faults("datto_120y").has("gearbox") and Garage.faults("datto_120y").has("gasket"),
"carnage didn't follow the car home as faults")
Garage._reset_for_test() # don't leave a wounded car for later stages
Input.action_release("throttle")
game.free()
Game.level_path = levels.get("woolies_carpark", levels.values()[0])
# derby: opponents spawn in a ring and each hunts something
print(" .. derby")
game = await _boot("derby")
_check(game.derby.size() == Game.DERBY_CARS, "derby field wrong size")
await physics_frame
for d in game.derby:
_check(d.target != null and d.target != d, "derby car has no/self target")
game.free()
# road rage: rivals spawn, a takedown near the player counts and pays
print(" .. rage")
game = await _boot("rage")
_check(game.rivals.size() == Game.RAGE_RIVALS, "rage field wrong size")
var prey: Rival = game.rivals[0]
await physics_frame
game.car.global_position = prey.global_position + Vector3(3, 0.2, 0)
await physics_frame
prey.wreck_started.emit()
_check(game.rage_downs == 1, "rage takedown didn't count (%d)" % game.rage_downs)
game.time_left = 0.05
for i in 10:
await physics_frame
_check(game.over, "rage never tallied")
game.free()
# oncoming: rolling against the road tangent at speed earns boost
game = await _boot("cruise")
await physics_frame
var curve := game.race_path.curve
var road_pt := game.race_path.to_global(curve.sample_baked(20.0))
var tan := (game.race_path.to_global(curve.sample_baked(24.0)) - road_pt).normalized()
# Teleport via the physics server: setting global_position on a
# continuous_cd body SWEEPS it there, slamming through everything en route
# -- the first version of this test wrecked the car before it could earn.
var xf := Transform3D(Basis.looking_at(-tan, Vector3.UP), road_pt + Vector3.UP * 0.75)
PhysicsServer3D.body_set_state(game.car.get_rid(), PhysicsServer3D.BODY_STATE_TRANSFORM, xf)
game.car.wrecked = false
game.car.boost = 0.0
game.car._prev_lv = -tan * 16.0 # don't let the velocity jump read as a crash
game.car.linear_velocity = -tan * 16.0
for i in 45:
await physics_frame
game.car.linear_velocity = -tan * 16.0 # hold course against drag
_check(game.car.boost > 0.05, "oncoming earned no boost (%.3f)" % game.car.boost)
game.free()
# race: grid up at Queensland Raceway -- the paperclip is ~3126 m of Path3D
Game.level_path = levels["qld_raceway"]
print(" .. race")
game = await _boot("race")
var lap: float = game.race_path.curve.get_baked_length()
_check(absf(lap - 3126.0) < 60.0, "paperclip lap measures %.0f m" % lap)
_check(game.rivals.size() == Game.RIVAL_COUNT, "rivals missing")
var p0: float = game.rivals[0].progress
for i in 240:
await physics_frame
var moved := false
for r in game.rivals:
if absf(r.progress - p0) > 3.0 or r.linear_velocity.length() > 3.0:
moved = true
_check(moved, "no rival is driving")
game.free()
print("smoke ok: %d cars, %d levels | cruise %.1f m | crash $%d | junction $%d | outrun arms+pays | pursuit spawns+escapes | quarter mile stages+shifts | derby %d cars | paperclip race AI driving" %
[cars.size(), levels.size(), dist, int(crash_score), int(jct_score), Game.DERBY_CARS])
quit(0)
func _drive_wrench(wr, sabotage: bool) -> void:
## Plays any wrench job through its API. sabotage blows the first torque
## window wide open to prove that threads strip.
var guard := 0
var sabotaged := false
while not wr.done and guard < 30:
guard += 1
var st: Dictionary = wr.steps[wr.step_index]
var need: float = (float(st.get("turns", 0.0)) + wr.penalty_turns) * TAU
match String(st["verb"]):
"diagnose":
for i in wr.light_options.size():
if wr.light_options[i] == Garage.FAULTS[wr.fault]["light"]:
wr.pick_light(i)
break
"pump":
for i in int(st["clicks"]):
wr.pump()
"twist":
if bool(st.get("window", false)):
if sabotage and not sabotaged:
sabotaged = true
wr.twist(float(st["dir"]) * (need + 4.0))
else:
wr.twist(float(st["dir"]) * (need + 0.3))
wr.twist_release()
else:
wr.twist(float(st["dir"]) * (need + 0.2))
"yank":
wr.yank_ok()
"grab":
wr.grab(String(st["item"]))
wr.drop_at(wr._drop_target(String(st["at"])))
"steady":
while not wr.done and wr.steps[wr.step_index]["verb"] == "steady":
wr.steady_tick(true, 0.5)
"clamps":
wr.clamp_on("red")
wr.clamp_on("black")
"wait":
wr.skip_wait()
await physics_frame
await physics_frame
func _check(cond: bool, msg: String) -> void:
## NOT assert(): a failed assert in a headless SceneTree script halts the
## script but leaves the tree running, so the process spins forever instead
## of failing. One bad assertion cost 53 minutes of wall clock before this.
if cond:
return
push_error(msg)
print("SMOKE FAIL: %s" % msg)
quit(1)
func _boot(mode: String) -> Game:
Game.mode = mode
var game: Game = (load("res://src/game.tscn") as PackedScene).instantiate()
root.add_child(game)
await process_frame # _ready is deferred until the main loop runs
return game