When entering an interior (e.g. lab from town), the game saves the player's current position as a return point. When exiting, if the exit has no explicit targetX/targetY, the system pops the stored return point and warps back to that exact position. This means interior exits just need targetMap — the player always returns to the specific door they entered from, not a hardcoded position. Falls back to the destination map's spawn if no return point is stored. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
195 lines
5.1 KiB
JavaScript
195 lines
5.1 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,
|
|
spawn: { x: 4, y: 10 },
|
|
wallSet: buildWallSet(labWalls),
|
|
|
|
exits: [
|
|
// No targetX/targetY — uses stored return point (the door the player entered from)
|
|
{ x: 4, y: 11, targetMap: 'town' },
|
|
{ x: 5, y: 11, targetMap: 'town' },
|
|
],
|
|
|
|
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."] },
|
|
]
|
|
};
|
|
|
|
// ==================== 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,
|
|
spawn: { x: 0, y: 0 },
|
|
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,
|
|
spawn: { x: 0, y: 0 },
|
|
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 };
|