From 81ed13b1ffda8a0da5cfa022b50112a7e2cf486c Mon Sep 17 00:00:00 2001 From: m3ultra Date: Tue, 28 Jul 2026 16:16:32 +1000 Subject: [PATCH] FLOORED LAZZA 1.5: John's actual first car, '82 KA Laser hatch Built from his photos: white 5-door wedge, black bumpers + belt-line rubbing strip, amber corner indicators, wide dark grille bezel (pale headlights vanish on white paint without it), and the crown jewel -- rear hatch louvres, five slats that shed one by one on impact like a dropped pie. New optional spec keys: louvres, indicators, side_strip, wide_grille; build_cars.py now takes `-- ` to rebuild a single car; screenshot harness gains cam=rear. Reversed-stats rule applied: 54 kW carby IRL -> 21.0 power / 860 kg / 7.8 grip here. Second-quickest thing in the game behind the Excel, and the chuckable-est. Co-Authored-By: Claude Opus 5 --- README.md | 6 +++-- cars/lazza_ka/car.glb | Bin 0 -> 42360 bytes cars/lazza_ka/car.glb.import | 45 +++++++++++++++++++++++++++++++++++ cars/lazza_ka/stats.tres | 13 ++++++++++ tests/screenshot.gd | 6 +++++ tools/build_cars.py | 45 ++++++++++++++++++++++++++++++++--- 6 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 cars/lazza_ka/car.glb create mode 100644 cars/lazza_ka/car.glb.import create mode 100644 cars/lazza_ka/stats.tres diff --git a/README.md b/README.md index ee0fc77..183e5e8 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,10 @@ Model convention (any GLB works, hand-modelled or generated): - Node origin 0.75 m above ground level (the controller's ride height). - Objects named `det_*` are detachable — the game rips them off on impact (doors, mirrors, bumpers, bonnet, boot). -- Current fleet: Datto 120Y, Gemmy TX, Kingswood HQ, VL Terbo, XF Fungoon, Excel Hilton. - Piss-take names on purpose: real model names and badges are trademarks. +- Current fleet: 30 shitboxes, from the Datto 120Y to the FLOORED LAZZA 1.5 ('82 + Laser hatch, rear louvres that shed slat-by-slat — John's actual first car, + built from his photos). Piss-take names on purpose: real model names and + badges are trademarks. To improve a silhouette: supply a side-on photo + front three-quarter photo, tweak the profile points in the spec, rebuild, screenshot. diff --git a/cars/lazza_ka/car.glb b/cars/lazza_ka/car.glb new file mode 100644 index 0000000000000000000000000000000000000000..ea28e06d9add2f4444e24714cc5a6ca6cb26db2d GIT binary patch literal 42360 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 literal 0 HcmV?d00001 diff --git a/cars/lazza_ka/car.glb.import b/cars/lazza_ka/car.glb.import new file mode 100644 index 0000000..3b18828 --- /dev/null +++ b/cars/lazza_ka/car.glb.import @@ -0,0 +1,45 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://bbottmecxqhu1" +path="res://.godot/imported/car.glb-edc9533c079c72a53d09ab51d6c7dcf4.scn" + +[deps] + +source_file="res://cars/lazza_ka/car.glb" +dest_files=["res://.godot/imported/car.glb-edc9533c079c72a53d09ab51d6c7dcf4.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +mesh_library/use_node_names_as_mesh_names=false +array_mesh/deduplicate_surfaces=true +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={} +gltf/naming_version=2 +gltf/embedded_image_handling=1 +gltf/texture_map_mode=1 diff --git a/cars/lazza_ka/stats.tres b/cars/lazza_ka/stats.tres new file mode 100644 index 0000000..4d5f338 --- /dev/null +++ b/cars/lazza_ka/stats.tres @@ -0,0 +1,13 @@ +[gd_resource type="Resource" script_class="CarStats" load_steps=2 format=3] + +[ext_resource type="Script" path="res://src/car_stats.gd" id="1"] + +[resource] +script = ExtResource("1") +display_name = "FLOORED LAZZA 1.5" +model_path = "res://cars/lazza_ka/car.glb" +engine_power = 21.0 +mass = 860.0 +grip = 7.8 +length = 3.96 +width = 1.61 diff --git a/tests/screenshot.gd b/tests/screenshot.gd index f602b12..6ab6a7e 100644 --- a/tests/screenshot.gd +++ b/tests/screenshot.gd @@ -35,6 +35,12 @@ func _run() -> void: game.cam.look_at(game.car.global_position + Vector3.UP * 0.4) await process_frame await process_frame + elif opts.get("cam", "") == "rear": + game.set_process(false) + game.cam.global_position = game.car.global_position + game.car.global_basis * Vector3(2.6, 1.6, 6.5) + game.cam.look_at(game.car.global_position + Vector3.UP * 0.4) + await process_frame + await process_frame elif opts.get("cam", "") == "top": game.set_process(false) var alt := float(opts.get("alt", "150")) diff --git a/tools/build_cars.py b/tools/build_cars.py index 39df500..01e3213 100644 --- a/tools/build_cars.py +++ b/tools/build_cars.py @@ -1,6 +1,7 @@ """Procedural Aussie shitbox factory. Run: /Applications/Blender.app/Contents/MacOS/Blender -b -P tools/build_cars.py + (append `-- [...]` to rebuild only those cars) Each car: side-silhouette profile extruded across the width (body + greenhouse), cylinder wheels, and separate det_* panel objects (doors, mirrors, bumpers, @@ -15,6 +16,7 @@ side-on photos per car when a shape needs to get closer. """ import os import math +import sys import bpy import bmesh from mathutils import Matrix @@ -30,7 +32,9 @@ LIGHT_R = (0.7, 0.1, 0.08, 1) # L, W, heights: nose/belt/tail, lengths: bonnet/boot, roof height + rear roof end, # axle overhangs, wheel radius, paint, chrome bumpers, hatch rear. -# Optional: doors=2, bonnet_color (two-tone accent), roof_cap (vinyl roof), spoiler. +# Optional: doors=2, bonnet_color (two-tone accent), roof_cap (vinyl roof), spoiler, +# louvres (hatch-glass slat stack, each slat sheds on impact), indicators (amber +# corner blinkers), side_strip (black rubbing strip along the belt line). CARS = { "datto_120y": dict(L=3.92, W=1.50, nose=0.62, belt=0.78, tail=0.72, bonnet=1.00, boot=0.85, roof=1.32, roof_rear=0.75, @@ -84,6 +88,13 @@ CARS = { ohang_f=0.85, ohang_r=0.95, wheel=0.29, paint=(0.70, 0.72, 0.58, 1), chrome_bumpers=True, hatch=False, doors=2, van=True), + # John's actual first car: '82 KA Laser 1.5 hatch, white, rear louvres. + # Gutless IRL (54 kW carby) -> reversed-stats rule makes it a weapon here. + "lazza_ka": dict(L=3.96, W=1.61, nose=0.58, belt=0.79, tail=0.90, + bonnet=1.02, boot=0.35, roof=1.37, roof_rear=0.72, + ohang_f=0.79, ohang_r=0.72, wheel=0.26, + paint=(0.92, 0.92, 0.89, 1), chrome_bumpers=False, hatch=True, + louvres=True, indicators=True, side_strip=True, wide_grille=True), # mokes use the open-tub builder: style="moke" "mini_merk": dict(style="moke", L=3.05, W=1.42, nose=0.60, tub=0.66, canopy=1.38, bonnet=0.85, ohang_f=0.55, ohang_r=0.55, wheel=0.26, @@ -215,6 +226,19 @@ def build(car_id, s): parts.append(box("det_bumper_f", (0, f + 0.05, bottom + 0.16), (W + 0.06, 0.15, 0.16), bumper_mat)) parts.append(box("det_bumper_r", (0, r - 0.05, bottom + 0.16), (W + 0.06, 0.15, 0.16), bumper_mat)) + if s.get("louvres") and s["hatch"]: + # venetian-blind slat stack over the hatch glass; every slat is its own + # det_ part, so a rear-ender sheds them one by one like a dropped pie + ty, tz = boot_y + s["roof_rear"], s["roof"] # glass top edge + ly, lz = rr_y, rr_z # glass bottom edge + g_ang = math.atan2(tz - lz, ty - ly) + for i in range(5): + t = 0.14 + i * 0.18 + sy = ty + (ly - ty) * t + sz = tz + (lz - tz) * t + 0.045 + parts.append(box("det_louvre_%d" % i, (0, sy, sz), + (W * 0.80, 0.20, 0.022), dark, rot_x=-g_ang * 0.45)) + if s.get("van"): # cargo box from cab rear to tail, plus rip-off tailgate parts.append(box("van_box", (0, (boot_y + r) / 2, (s["belt"] + s["roof"]) / 2), (W * 0.94, boot_y - r - 0.05, s["roof"] - s["belt"]), paint)) @@ -240,13 +264,23 @@ def build(car_id, s): (W * 0.86, cap_f - cap_r + 0.1, 0.035), material("vinyl", s["roof_cap"], rough=0.9))) if s.get("spoiler"): # bootlid ducktail trim.append(box("trim_spoiler", (0, r + 0.22, s["tail"] + 0.05), (W * 0.8, 0.28, 0.09), paint)) + grille_w = W * (0.78 if s.get("wide_grille") else 0.5) # wide = lights sit in a dark bezel band trim += [ - box("trim_grille", (0, f + 0.01, s["nose"] - 0.14), (W * 0.5, 0.05, 0.12), dark), + box("trim_grille", (0, f + 0.01, s["nose"] - 0.14), (grille_w, 0.05, 0.14), dark), box("trim_light_fl", (-W * 0.33, f + 0.02, s["nose"] - 0.13), (0.24, 0.05, 0.13), material("lf", LIGHT_F)), box("trim_light_fr", (W * 0.33, f + 0.02, s["nose"] - 0.13), (0.24, 0.05, 0.13), material("lf2", LIGHT_F)), box("trim_light_rl", (-W * 0.32, r - 0.02, s["tail"] - 0.14), (0.26, 0.05, 0.12), material("lr", LIGHT_R)), box("trim_light_rr", (W * 0.32, r - 0.02, s["tail"] - 0.14), (0.26, 0.05, 0.12), material("lr2", LIGHT_R)), ] + if s.get("indicators"): + amber = material("amber", (0.95, 0.55, 0.05, 1)) + trim.append(box("trim_ind_l", (-W * 0.44, f + 0.02, s["nose"] - 0.13), (0.11, 0.05, 0.12), amber)) + trim.append(box("trim_ind_r", (W * 0.44, f + 0.02, s["nose"] - 0.13), (0.11, 0.05, 0.12), amber)) + if s.get("side_strip"): + strip_len = L * 0.80 + for sx in (-1, 1): + trim.append(box("trim_strip_%s" % ("l" if sx < 0 else "r"), + (sx * (hw + 0.015), -L * 0.02, 0.52), (0.03, strip_len, 0.055), dark)) ax_f, ax_r = f - s["ohang_f"], r + s["ohang_r"] wx = hw - 0.10 @@ -337,9 +371,14 @@ def build_moke(car_id, s): print("built", car_id, "->", path) +only = set(sys.argv[sys.argv.index("--") + 1:]) if "--" in sys.argv else set() +built = 0 for car_id, spec in CARS.items(): + if only and car_id not in only: + continue if spec.get("style") == "moke": build_moke(car_id, spec) else: build(car_id, spec) -print("done: %d cars" % len(CARS)) + built += 1 +print("done: %d cars" % built)