Phase 1: road network and heat

Roads are a jittered 7x7 grid with ~a quarter of the edges thinned out, kept
connected. The loops are the point: "take a different road" is not a decision
without alternative routes.

Every metre driven adds heat to that segment; all roads shed it slowly. Crossing
a threshold escalates Clear -> Patrol -> Barricade -> Turret, each of which puts
real obstructions on the tarmac. Hysteresis stops a road on a boundary from
rebuilding its barricade every few seconds.

Level changes are deferred while the car is on the segment. Building a barricade
around the player would spawn a static collider inside the chassis, and the
player is meant to discover escalation by returning to a road, not by watching
it assemble behind them.

Checkpoint positions are seeded from the segment id, so a stretch of road always
fortifies in the same place — recognising it is part of learning the map.

Scenery now avoids the tarmac and the car spawns at a junction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dejvino
2026-08-07 07:47:19 +02:00
co-authored by Claude Opus 5
parent a66032674d
commit 506d07be5d
14 changed files with 830 additions and 49 deletions
+21
View File
@@ -60,6 +60,27 @@ export function createScene(model: WorldModel): SceneView {
grid.position.y = 0.02;
scene.add(grid);
// --- Roads ---
const roadMat = new THREE.MeshStandardMaterial({ color: 0x23262a, roughness: 1 });
const quad = new THREE.PlaneGeometry(1, 1).rotateX(-Math.PI / 2);
for (const s of model.roads.segments) {
const strip = new THREE.Mesh(quad, roadMat);
strip.scale.set(s.width, 1, s.length);
strip.position.set((s.ax + s.bx) / 2, 0.03, (s.az + s.bz) / 2);
strip.rotation.y = Math.atan2(s.bx - s.ax, s.bz - s.az);
strip.receiveShadow = true;
scene.add(strip);
}
// Discs fill the wedge-shaped gaps where segments meet at an angle.
const junction = new THREE.CircleGeometry(1, 20).rotateX(-Math.PI / 2);
for (const n of model.roads.nodes) {
const disc = new THREE.Mesh(junction, roadMat);
disc.scale.setScalar(4.5);
disc.position.set(n.x, 0.031, n.z);
disc.receiveShadow = true;
scene.add(disc);
}
// --- Obstacles ---
const blockMat = new THREE.MeshStandardMaterial({ color: 0x767c82, roughness: 0.9 });
const crateMat = new THREE.MeshStandardMaterial({ color: 0xa9773f, roughness: 0.8 });