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>
210 lines
5.8 KiB
JavaScript
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 };
|