feat(minimotorways): render roads connecting to houses and buildings

- Add logic to find valid road adjacencies for structures, including diagonal connections
- Draw road circles at structure locations to visualize connections
- Adjust road rendering depth
This commit is contained in:
Brian Fertig 2026-06-12 14:22:35 -06:00
parent d57275c496
commit 2451e8fc66
1 changed files with 54 additions and 1 deletions

View File

@ -13,9 +13,15 @@ const CELL = 64;
const WORLD_PX_W = WORLD_W * CELL; const WORLD_PX_W = WORLD_W * CELL;
const WORLD_PX_H = WORLD_H * CELL; const WORLD_PX_H = WORLD_H * CELL;
const DIRS = [
[1, 0], [-1, 0], [0, 1], [0, -1],
[1, 1], [1, -1], [-1, 1], [-1, -1],
];
const inBounds = (x, y) => x >= 0 && x < WORLD_W && y >= 0 && y < WORLD_H;
// World-space depths. // World-space depths.
const D = { const D = {
land: 0, waterFx: 1, roads: 2, items: 3, ghost: 3.5, land: 0, waterFx: 1, roads: 1.5, items: 3, ghost: 3.5,
structures: 4, cars: 5, motorway: 6, pins: 7, fx: 8, night: 9, structures: 4, cars: 5, motorway: 6, pins: 7, fx: 8, night: 9,
}; };
@ -379,6 +385,51 @@ export default class MiniMotorwaysGame extends Phaser.Scene {
} }
} }
// Extend roads into adjacent house/building cells so the road is visible
// underneath the structure. Collect struct cells adjacent to roads, then
// add virtual edges and circle targets.
const structRoadCells = new Set();
for (const h of sim.houses) {
const x = xOf(h.k); const y = yOf(h.k);
for (const [dx, dy] of DIRS) {
if (dx === 0 && dy === 0) continue;
const nx = x + dx; const ny = y + dy;
if (inBounds(nx, ny) && sim.roads.has(keyOf(nx, ny))) {
// Diagonal crossing rule
if (dx !== 0 && dy !== 0) {
if (sim.roads.has(keyOf(x + dx, y)) && sim.roads.has(keyOf(x, y + dy))) continue;
}
const rk = keyOf(nx, ny);
if (!edges.find(e => (e[0] === rk && e[1] === h.k) || (e[0] === h.k && e[1] === rk))) {
edges.push([rk, h.k]);
}
structRoadCells.add(h.k);
break;
}
}
}
for (const b of sim.buildings) {
for (const c of b.cells) {
if (structRoadCells.has(c)) continue;
const x = xOf(c); const y = yOf(c);
for (const [dx, dy] of DIRS) {
if (dx === 0 && dy === 0) continue;
const nx = x + dx; const ny = y + dy;
if (inBounds(nx, ny) && sim.roads.has(keyOf(nx, ny))) {
if (dx !== 0 && dy !== 0) {
if (sim.roads.has(keyOf(x + dx, y)) && sim.roads.has(keyOf(x, y + dy))) continue;
}
const rk = keyOf(nx, ny);
if (!edges.find(e => (e[0] === rk && e[1] === c) || (e[0] === c && e[1] === rk))) {
edges.push([rk, c]);
}
structRoadCells.add(c);
break;
}
}
}
}
// Bridge plinths sit under everything. // Bridge plinths sit under everything.
for (const k of sim.bridgeCells) { for (const k of sim.bridgeCells) {
g.fillStyle(darken(pal.roadEdge, 0.62), 1); g.fillStyle(darken(pal.roadEdge, 0.62), 1);
@ -391,11 +442,13 @@ export default class MiniMotorwaysGame extends Phaser.Scene {
for (const [a, b] of edges) g.lineBetween(cellCx(a), cellCy(a), cellCx(b), cellCy(b)); for (const [a, b] of edges) g.lineBetween(cellCx(a), cellCy(a), cellCx(b), cellCy(b));
g.fillStyle(pal.roadEdge, 1); g.fillStyle(pal.roadEdge, 1);
for (const k of sim.roads) g.fillCircle(cellCx(k), cellCy(k), CELL * 0.31); for (const k of sim.roads) g.fillCircle(cellCx(k), cellCy(k), CELL * 0.31);
for (const k of structRoadCells) g.fillCircle(cellCx(k), cellCy(k), CELL * 0.31);
g.lineStyle(CELL * 0.5, pal.road, 1); g.lineStyle(CELL * 0.5, pal.road, 1);
for (const [a, b] of edges) g.lineBetween(cellCx(a), cellCy(a), cellCx(b), cellCy(b)); for (const [a, b] of edges) g.lineBetween(cellCx(a), cellCy(a), cellCx(b), cellCy(b));
g.fillStyle(pal.road, 1); g.fillStyle(pal.road, 1);
for (const k of sim.roads) g.fillCircle(cellCx(k), cellCy(k), CELL * 0.25); for (const k of sim.roads) g.fillCircle(cellCx(k), cellCy(k), CELL * 0.25);
for (const k of structRoadCells) g.fillCircle(cellCx(k), cellCy(k), CELL * 0.25);
// Bridge side rails over the deck. // Bridge side rails over the deck.
for (const k of sim.bridgeCells) { for (const k of sim.bridgeCells) {