/** * maps.js - PNG-based world maps with wall coordinate arrays * * Each map has a pre-rendered PNG background image and defines: * - walls: coordinate-based collision data { row: [col1, col2, ...] } * - npcs, interactions, exits as position-based objects * * Map images are drawn at 3x scale (16px native → 48px on screen) */ // ==================== Map definitions ==================== /** * MAP: LAB (10×12 tiles — lab.png is 160×192) * Pokemon professor's lab interior */ const labMap = { id: 'lab', name: 'Circuit Lab', image: 'map:lab', widthTiles: 10, heightTiles: 12, spawn: { x: 4, y: 10 }, // Walls: { row: [col, col, ...] } // Row 0-1: top shelves/machines walls: { 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 2: [0, 1, 2, 7, 8, 9], 3: [0, 3, 4, 5, 6, 9], 4: [0, 3, 4, 5, 6, 9], 5: [0, 3, 4, 5, 6, 9], 6: [0, 9], 7: [0, 9], 8: [0, 1, 2, 7, 8, 9], 9: [0, 1, 8, 9], 10: [0, 1, 2, 3, 6, 7, 8, 9], 11: [0, 1, 2, 3, 4, 6, 7, 8, 9] }, exits: [ // Exit at bottom center — door to town { x: 4, y: 11, targetMap: 'town', targetX: 12, targetY: 10 }, { x: 5, y: 11, targetMap: 'town', targetX: 12, targetY: 10 } ], npcs: [ { id: 'professor', x: 4, y: 3, 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: [ // Workshop tables (the big table in the middle of lab) { x: 3, y: 5, type: 'workshop', label: 'Workshop Table' }, { x: 4, y: 5, type: 'workshop', label: 'Workshop Table' }, { x: 5, y: 5, type: 'workshop', label: 'Workshop Table' }, { x: 6, y: 5, type: 'workshop', label: 'Workshop Table' }, // Machine/bookshelf { x: 8, y: 2, type: 'terminal', label: 'Terminal', dialog: ['Circuit analysis terminal.', 'Connect components to solve puzzles.'] }, // Puzzle door in top-right area { x: 8, y: 3, type: 'puzzle_door', puzzleId: 'lab_door_1', requiredOutputs: [1, 0, 1, 1], label: 'Locked Door' } ] }; /** * MAP: TOWN (20×18 tiles — pallet-town.png is 320×288) * Pokemon-style starting town with houses and paths */ const townMap = { id: 'town', name: 'Neon Town', image: 'map:pallet-town', widthTiles: 20, heightTiles: 18, spawn: { x: 9, y: 9 }, // Walls based on pallet-town visual layout // Trees around border, houses, fences, water walls: (() => { const w = {}; // Helper to add walls function addWall(row, cols) { if (!w[row]) w[row] = []; w[row].push(...cols); } function addRange(row, from, to) { const cols = []; for (let c = from; c <= to; c++) cols.push(c); addWall(row, cols); } function addRect(rowStart, rowEnd, colStart, colEnd) { for (let r = rowStart; r <= rowEnd; r++) addRange(r, colStart, colEnd); } // Top border (trees/fence) — rows 0-1 addRange(0, 0, 19); addRange(1, 0, 19); // Left border trees for (let r = 2; r <= 17; r++) addWall(r, [0, 1]); // Right border trees for (let r = 2; r <= 17; r++) addWall(r, [18, 19]); // Bottom border addRange(17, 0, 19); // House 1 (top-left area) — roughly rows 3-6, cols 3-7 addRect(3, 5, 3, 7); // House 2 (top-right area) — rows 3-6, cols 12-16 addRect(3, 5, 12, 16); // Fence segments addRange(7, 2, 7); addRange(7, 12, 17); // Water/pond (bottom-left) addRect(13, 15, 2, 5); // Some trees/obstacles in bottom area addWall(16, [2, 3, 4, 5, 6, 7]); addWall(16, [12, 13, 14, 15, 16, 17]); return w; })(), exits: [ // North exit — goes to lab { x: 12, y: 9, targetMap: 'lab', targetX: 4, targetY: 10 }, // Route 1 south (future) // { x: 9, y: 17, targetMap: 'route1', targetX: 10, targetY: 0 } ], npcs: [ { id: 'merchant', x: 9, y: 11, facing: 'down', dialog: [ 'Welcome to Neon Town!', 'I trade in rare logic components.', 'Craft some circuits in the Lab workshop!', 'Some doors need special output patterns to open.' ] }, { id: 'guide', x: 14, y: 9, facing: 'left', dialog: [ 'The Circuit Lab is just up ahead.', 'Professor Oak.. I mean, the Professor can teach you about logic gates!', 'Press TAB anytime to open your Workshop.' ] } ], interactions: [ // House 1 door { x: 5, y: 6, type: 'door', label: 'House', dialog: ['The door is locked.', 'Nobody seems to be home.'] }, // House 2 door { x: 14, y: 6, type: 'door', label: 'House', dialog: ['This is the component shop.', 'Coming soon!'] }, // Sign { x: 10, y: 8, type: 'sign', label: 'Sign', dialog: ['Welcome to Neon Town!', 'Circuit Lab ↑'] } ] }; // ==================== Map registry ==================== const maps = { lab: labMap, town: townMap }; // ==================== Public API ==================== export function getMap(id) { return maps[id] || null; } /** * Check if a tile position is a wall */ export function isWall(mapId, x, y) { const map = maps[mapId]; if (!map) return true; // Out of bounds = wall if (x < 0 || x >= map.widthTiles || y < 0 || y >= map.heightTiles) return true; const row = map.walls[y]; if (!row) return false; return row.includes(x); } /** * Check if a tile is walkable (not a wall and no NPC blocking) */ export function isWalkable(mapId, x, y) { if (isWall(mapId, x, y)) return false; if (getNPC(mapId, x, y)) return false; return true; } /** * Get interaction at position */ 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; } /** * Get NPC at position */ 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 */ 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; } // No longer needed but keep for compat — returns null always export function getTile(mapId, x, y) { return isWall(mapId, x, y) ? 1 : 0; } export { maps };