/** * maps.js - Tile-based world map definitions * * Tile types: * 0 = floor * 1 = wall * 2 = grass * 3 = workshop table * 4 = puzzle door locked * 5 = puzzle door open * 6 = NPC spot * 7 = path * 8 = water * 9 = terminal */ // Helper to fill a rectangular area with a tile type function fillRect(tiles, x, y, w, h, tileType) { for (let row = y; row < y + h; row++) { for (let col = x; col < x + w; col++) { if (row >= 0 && row < tiles.length && col >= 0 && col < tiles[0].length) { tiles[row][col] = tileType; } } } } // Helper to place a horizontal line function hline(tiles, x, y, length, tileType) { for (let i = 0; i < length; i++) { if (y >= 0 && y < tiles.length && x + i >= 0 && x + i < tiles[0].length) { tiles[y][x + i] = tileType; } } } // Helper to place a vertical line function vline(tiles, x, y, length, tileType) { for (let i = 0; i < length; i++) { if (x >= 0 && x < tiles[0].length && y + i >= 0 && y + i < tiles.length) { tiles[y + i][x] = tileType; } } } /** * MAP 1: CIRCUIT LAB (20×15) * Indoor tech lab with workstations, terminals, and a puzzle door */ function createLabMap() { const width = 20; const height = 15; // Start with all floor const tiles = Array(height).fill(null).map(() => Array(width).fill(0)); // Border walls for (let i = 0; i < width; i++) { tiles[0][i] = 1; // top tiles[height - 1][i] = 1; // bottom } for (let i = 0; i < height; i++) { tiles[i][0] = 1; // left tiles[i][width - 1] = 1; // right } // Internal wall structure - create lab rooms // Vertical divider wall down the middle area vline(tiles, 13, 1, 10, 1); // Horizontal divider hline(tiles, 1, 7, 12, 1); // Workshop area - left side with tables tiles[4][3] = 3; // workshop table 1 tiles[4][5] = 3; // workshop table 2 tiles[4][7] = 3; // workshop table 3 tiles[6][3] = 3; // workshop table 4 tiles[6][5] = 3; // workshop table 5 // Terminal near workshop area tiles[5][10] = 9; // terminal // Professor NPC spot at top tiles[2][10] = 6; // NPC spawn location // Puzzle door leading to back room (top-right area) tiles[2][16] = 4; // locked puzzle door // Door opening in the internal wall for navigation tiles[7][7] = 0; // create passage // Exit point at bottom (to town) tiles[13][10] = 0; // clear exit path return { id: 'lab', name: 'Circuit Lab', width: width, height: height, tiles: tiles, spawn: { x: 10, y: 12 }, exits: [ { x: 10, y: 13, targetMap: 'town', targetX: 15, targetY: 1 } ], npcs: [ { id: 'professor', type: 0, x: 10, y: 2, facing: 'down', dialog: [ 'Welcome to the Circuit Lab!', 'I\'m the Professor. We study logic gates here.', 'Try using the workshop tables to design circuits.', 'Once you\'ve created some components, you can use them to solve puzzles.' ] } ], interactions: [ { x: 3, y: 4, type: 'workshop', action: 'openWorkshop', label: 'Workshop Table' }, { x: 5, y: 4, type: 'workshop', action: 'openWorkshop', label: 'Workshop Table' }, { x: 7, y: 4, type: 'workshop', action: 'openWorkshop', label: 'Workshop Table' }, { x: 10, y: 5, type: 'terminal', action: 'openTerminal', label: 'Terminal' }, { x: 16, y: 2, type: 'puzzle_door', puzzleId: 'lab_door_1', requiredOutputs: [1, 0, 1, 1], label: 'Locked Door' } ] }; } /** * MAP 2: NEON TOWN (30×20) * Outdoor town with buildings, NPCs, water feature, and puzzle areas */ function createTownMap() { const width = 30; const height = 20; // Start with grass const tiles = Array(height).fill(null).map(() => Array(width).fill(2)); // Add some paths (lighter grass/paths) hline(tiles, 0, 10, width, 7); // horizontal path vline(tiles, 15, 0, height, 7); // vertical path // Water feature on the left (pond) fillRect(tiles, 2, 5, 5, 6, 8); // Lab entrance at top tiles[0][15] = 0; // entrance floor tiles[1][15] = 6; // NPC spawn for entrance // Building 1 (top-left) - House structure fillRect(tiles, 5, 2, 7, 5, 1); // walls fillRect(tiles, 6, 3, 5, 3, 0); // interior floor tiles[4][8] = 0; // door to building 1 // Building 2 (top-right) - Shop fillRect(tiles, 20, 2, 7, 5, 1); // walls fillRect(tiles, 21, 3, 5, 3, 0); // interior floor tiles[4][23] = 0; // door to building 2 // Building 3 (bottom-left) - Guard post fillRect(tiles, 5, 14, 7, 5, 1); // walls fillRect(tiles, 6, 15, 5, 3, 0); // interior floor tiles[13][8] = 0; // door to guard post // Building 4 (bottom-right) - Town Hall fillRect(tiles, 20, 14, 7, 5, 1); // walls fillRect(tiles, 21, 15, 5, 3, 0); // interior floor tiles[13][23] = 0; // door to town hall // Merchant NPC in center town square tiles[10][15] = 6; // NPC spawn // Guard NPC at guard post entrance tiles[13][8] = 6; // NPC spawn (overlays door, but NPC takes priority) // Puzzle door to eastern area (locked) tiles[10][28] = 4; // locked puzzle door return { id: 'town', name: 'Neon Town', width: width, height: height, tiles: tiles, spawn: { x: 15, y: 2 }, exits: [ { x: 15, y: 0, targetMap: 'lab', targetX: 10, targetY: 13 } ], npcs: [ { id: 'merchant', type: 0, x: 15, y: 10, facing: 'down', dialog: [ 'Welcome to Neon Town!', 'I trade in rare logic components.', 'Show me what circuits you\'ve designed, and maybe we can make a deal.', 'Some items are only available if you\'ve solved certain puzzles.' ] }, { id: 'guard', type: 0, x: 8, y: 13, facing: 'right', dialog: [ 'I guard the eastern territories.', 'You need to solve the puzzle at the gate before you can pass.', 'Bring me a component that produces the right output pattern!' ] } ], interactions: [ { x: 8, y: 4, type: 'door', action: 'openBuilding', label: 'House', buildingId: 'house_1' }, { x: 23, y: 4, type: 'door', action: 'openBuilding', label: 'Shop', buildingId: 'shop_1' }, { x: 8, y: 13, type: 'door', action: 'openBuilding', label: 'Guard Post', buildingId: 'guardpost_1' }, { x: 23, y: 13, type: 'door', action: 'openBuilding', label: 'Town Hall', buildingId: 'townhall_1' }, { x: 28, y: 10, type: 'puzzle_door', puzzleId: 'town_gate_1', requiredOutputs: [0, 1, 1, 0], label: 'Locked Gate' } ] }; } // Map registry const maps = { lab: createLabMap(), town: createTownMap() }; /** * Get a complete map by ID */ export function getMap(id) { return maps[id] || null; } /** * Get tile type at position */ export function getTile(mapId, x, y) { const map = maps[mapId]; if (!map) return null; if (x < 0 || x >= map.width || y < 0 || y >= map.height) return null; return map.tiles[y][x]; } /** * Get interaction at position (if any) */ export function getInteraction(mapId, x, y) { const map = maps[mapId]; if (!map) return null; return map.interactions.find(inter => inter.x === x && inter.y === y) || null; } /** * Get NPC at position (if any) */ 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; } /** * Get exit at position (if any) */ export function getExit(mapId, x, y) { const map = maps[mapId]; if (!map) return null; return map.exits.find(exit => exit.x === x && exit.y === y) || null; } /** * Check if a tile is walkable * Walls (1), water (8), workshop tables (3), terminals (9), locked puzzle doors (4), and NPCs are not walkable */ export function isWalkable(mapId, x, y) { const tile = getTile(mapId, x, y); // Out of bounds if (tile === null) return false; // Non-walkable tiles const nonWalkable = [1, 3, 4, 8, 9]; if (nonWalkable.includes(tile)) return false; // Check for NPC if (getNPC(mapId, x, y)) return false; return true; } export { maps };