From 30d5cfd8465477bdaa14c7a8291478967d1b0aef Mon Sep 17 00:00:00 2001 From: m3ultra Date: Thu, 30 Jul 2026 13:58:02 +1000 Subject: [PATCH] Loose shopping trolleys in the Woolies carpark 18 kg of chrome chaos each: procedural basket + handle + castors on a low-friction RigidBody, so they roll away when nudged and FLY when hit at speed. Four strays around every trolley bay plus five rogues claiming parking spots, as is tradition. Co-Authored-By: Claude Opus 5 --- levels/woolies_carpark/carpark.gd | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/levels/woolies_carpark/carpark.gd b/levels/woolies_carpark/carpark.gd index 0980fc2..1fd86f8 100644 --- a/levels/woolies_carpark/carpark.gd +++ b/levels/woolies_carpark/carpark.gd @@ -46,6 +46,12 @@ func _ready() -> void: _planter_island(Vector3(x, 0, -60.0 + j * 30.0), rng) for p in [Vector3(-52, 0, 44), Vector3(52, 0, 44), Vector3(-52, 0, -46)]: _trolley_bay(p) + for i in 4: # strays that never made it back to the bay + _trolley(p + Vector3(rng.randf_range(-7.0, 7.0), 0, rng.randf_range(-6.0, 6.0)), + rng.randf() * TAU) + for i in 5: # rogue trolleys claiming parking spots, as is tradition + _trolley(Vector3(rng.randf_range(-60.0, 60.0), 0, rng.randf_range(-40.0, 40.0)), + rng.randf() * TAU) _race_path() func _mat(tex: String, scale: float, rough := 0.9) -> StandardMaterial3D: @@ -179,6 +185,64 @@ func _trolley_bay(pos: Vector3) -> void: for j in 5: # the trolleys themselves _slab(pos + Vector3(0, 0.5, -3.2 + j * 1.6), Vector3(0.9, 1.0, 1.2), steel, false) +func _trolley(pos: Vector3, yaw: float) -> void: + ## A loose shopping trolley: 18 kg of chrome chaos. Light enough to FLY when + ## clipped at speed, low-friction so it rolls away like the real thing. + var b := RigidBody3D.new() + b.mass = 18.0 + var pm := PhysicsMaterial.new() + pm.friction = 0.25 + pm.bounce = 0.25 + b.physics_material_override = pm + var cs := CollisionShape3D.new() + var bs := BoxShape3D.new() + bs.size = Vector3(0.58, 0.95, 0.92) + cs.shape = bs + cs.position = Vector3(0, 0.5, 0) + b.add_child(cs) + var steel := _flat(Color(0.62, 0.63, 0.66), 0.35) + steel.metallic = 0.7 + # basket: floor + four walls, wider at the top like the real thing + var basket := [ + [Vector3(0, 0.42, 0), Vector3(0.50, 0.03, 0.84)], # floor + [Vector3(0, 0.66, 0.44), Vector3(0.54, 0.45, 0.03)], # back + [Vector3(0, 0.66, -0.42), Vector3(0.50, 0.45, 0.03)], # front + [Vector3(0.27, 0.66, 0), Vector3(0.03, 0.45, 0.86)], # right + [Vector3(-0.27, 0.66, 0), Vector3(0.03, 0.45, 0.86)], # left + ] + for spec in basket: + var mi := MeshInstance3D.new() + var bm := BoxMesh.new() + bm.size = spec[1] + bm.material = steel + mi.mesh = bm + mi.position = spec[0] + b.add_child(mi) + var handle := MeshInstance3D.new() + var hm := BoxMesh.new() + hm.size = Vector3(0.58, 0.04, 0.04) + hm.material = steel + handle.mesh = hm + handle.position = Vector3(0, 0.97, 0.50) + b.add_child(handle) + for sx in [-0.22, 0.22]: + for sz in [-0.36, 0.36]: + var c := MeshInstance3D.new() + var cm := CylinderMesh.new() + cm.top_radius = 0.05 + cm.bottom_radius = 0.05 + cm.height = 0.04 + cm.radial_segments = 8 + cm.material = _flat(Color(0.12, 0.12, 0.13), 0.7) + c.mesh = cm + c.rotation_degrees = Vector3(0, 0, 90) + c.position = Vector3(sx, 0.05, sz) + b.add_child(c) + add_child(b) + b.position = pos + Vector3.UP * 0.05 + b.rotate_y(yaw) + b.sleeping = true + func _race_path() -> void: # perimeter loop road around the parking rows -- race line + traffic route var path := Path3D.new()