Files
logic-gates/js/world/maps.js
Jose Luis f9492bff4c feat: module interaction system with wiring panel
Add a new "module" interaction type where doors/devices define ports
(in/out) and a JS verify function. Players wire their gadget's I/O
to the module's ports via a canvas-rendered wiring panel, then execute
to verify the circuit logic.

- New wiringPanel.js: full wiring UI with keyboard nav, bezier wires,
  mini circuit evaluator, and verify execution
- AND-gate example door in Circuit Lab (tile 9,1)
- Editor support: module type with ports editor and JS verify textarea
- Integrated into gameMode, worldInput, worldRenderer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 17:59:25 +01:00

210 lines
5.8 KiB
JavaScript

/**
* maps.js - PNG-based world maps (auto-generated by Level Editor)
*/
function buildWallSet(wallData) {
const set = new Set();
for (const [row, cols] of Object.entries(wallData)) {
for (const col of cols) set.add(col + ',' + row);
}
return set;
}
function r(a, b) { const arr = []; for (let i = a; i <= b; i++) arr.push(i); return arr; }
// ==================== Circuit Lab ====================
const labWalls = {
0: [...r(0,9)],
1: [0,1,2,3,6,7,8,9],
3: [6,7,8],
6: [0,1,2,3,6,7,8,9],
7: [0,1,2,3,6,7,8,9],
};
const labMap = {
id: 'lab',
name: 'Circuit Lab',
image: 'map:lab',
widthTiles: 10,
heightTiles: 12,
// No spawn — player enters via door from town
wallSet: buildWallSet(labWalls),
exits: [
// Bidirectional: these doors return to the specific town door (12,12 = tile in front of lab entrance)
{ x: 4, y: 11, targetMap: 'town', targetX: 12, targetY: 12 },
{ x: 5, y: 11, targetMap: 'town', targetX: 12, targetY: 12 },
],
npcs: [
{ id: 'professor', x: 5, y: 1, facing: 'down', dialog: ["Welcome to the Circuit Lab!","I\"m the Professor. We study logic gates here.","Use the workshop tables to design circuits.","Press TAB to open the Workshop anytime!"] },
],
interactions: [
{ x: 7, y: 3, type: 'workshop', label: 'Workshop Table' },
{ x: 8, y: 3, type: 'workshop', label: 'Workshop Table' },
{ x: 6, y: 3, type: 'workshop', label: 'Workshop Table' },
{ x: 1, y: 7, type: 'sign', label: 'Bookshelf', dialog: ["A collection of logic circuit manuals."] },
{ x: 7, y: 7, type: 'sign', label: 'Bookshelf', dialog: ["Advanced boolean algebra textbooks."] },
{ x: 0, y: 1, type: 'terminal', label: 'Terminal', dialog: ["Circuit analysis terminal.","Connect components to solve puzzles."] },
// Module door example: requires AND(A, B) → C
{
x: 9, y: 1, type: 'module',
moduleId: 'lab_and_door',
label: 'AND Gate Door',
ports: [
{ name: 'A', dir: 'out', bits: 1 },
{ name: 'B', dir: 'out', bits: 1 },
{ name: 'C', dir: 'in', bits: 1 }
],
verify: `(test) => {
return test({A:0, B:0}).C === 0
&& test({A:0, B:1}).C === 0
&& test({A:1, B:0}).C === 0
&& test({A:1, B:1}).C === 1;
}`
},
]
};
// ==================== Neon Town ====================
const palletTownWalls = {
0: [...r(0,19)],
1: [0,1,2,3,4,5,6,7,8,9,10,11,18,19],
2: [0,19],
3: [0,4,5,6,7,12,13,14,15,19],
4: [0,4,5,6,7,12,13,14,15,19],
5: [0,3,4,5,6,7,11,12,13,14,15,19],
6: [0,19],
7: [0,19],
8: [0,10,11,12,13,14,15,19],
9: [0,4,5,6,7,10,11,12,13,14,15,19],
10: [0,10,11,12,13,14,15,19],
11: [0,10,11,13,14,15,19],
12: [0,19],
13: [0,10,11,12,13,14,15,19],
14: [0,19],
15: [0,19],
16: [0,19],
17: [0,1,8,9,10,11,12,13,14,15,16,17,18,19],
};
const palletTownMap = {
id: 'town',
name: 'Neon Town',
image: 'map:pallet-town',
widthTiles: 20,
heightTiles: 18,
spawn: { x: 12, y: 12 },
wallSet: buildWallSet(palletTownWalls),
exits: [
{ x: 12, y: 11, targetMap: 'lab', targetX: 4, targetY: 10 },
],
npcs: [
{ id: 'merchant', x: 8, y: 10, facing: 'right', dialog: ["Welcome to Neon Town!","I trade in rare logic components."] },
{ id: 'guide', x: 11, y: 12, facing: 'down', dialog: ["The Circuit Lab is in the big building up north.","Press TAB anytime to open your Workshop."] },
],
interactions: [
{ x: 3, y: 5, type: 'door', label: 'House', dialog: ["The door is locked."] },
{ x: 7, y: 9, type: 'sign', label: 'Sign', dialog: ["Welcome to Neon Town!","Circuit Lab ↑"] },
{ x: 11, y: 5, type: 'sign', label: 'Sign', dialog: ["CIRCUIT LAB","Open for research!"] },
]
};
// ==================== House Interior ====================
const houseA1fWalls = {
};
const houseA1fMap = {
id: 'house-a-1f',
name: 'House Interior',
image: 'map:house-a-1f',
widthTiles: 8,
heightTiles: 8,
wallSet: buildWallSet(houseA1fWalls),
exits: [
],
npcs: [
],
interactions: [
]
};
// ==================== Route 1 ====================
const route1Walls = {
};
const route1Map = {
id: 'route-1',
name: 'Route 1',
image: 'map:route-1',
widthTiles: 20,
heightTiles: 36,
wallSet: buildWallSet(route1Walls),
exits: [
],
npcs: [
],
interactions: [
]
};
// ==================== Registry ====================
const maps = {
'lab': labMap,
'town': palletTownMap,
'house-a-1f': houseA1fMap,
'route-1': route1Map,
};
export function getMap(id) { return maps[id] || null; }
export function isWall(mapId, x, y) {
const map = maps[mapId];
if (!map) return true;
if (x < 0 || x >= map.widthTiles || y < 0 || y >= map.heightTiles) return true;
return map.wallSet.has(x + ',' + y);
}
export function isWalkable(mapId, x, y) {
if (isWall(mapId, x, y)) return false;
if (getNPC(mapId, x, y)) return false;
return true;
}
export function getInteraction(mapId, x, y) {
const map = maps[mapId];
if (!map) return null;
return map.interactions.find(i => i.x === x && i.y === y) || null;
}
export function getNPC(mapId, x, y) {
const map = maps[mapId];
if (!map) return null;
return map.npcs.find(npc => npc.x === x && npc.y === y) || null;
}
export function getExit(mapId, x, y) {
const map = maps[mapId];
if (!map) return null;
return map.exits.find(e => e.x === x && e.y === y) || null;
}
export function getTile(mapId, x, y) { return isWall(mapId, x, y) ? 1 : 0; }
export { maps };