diff --git a/editor.html b/editor.html index 91bb2a7..65fa30e 100644 --- a/editor.html +++ b/editor.html @@ -224,7 +224,7 @@ function init() { // Init map data for all maps for (const id of Object.keys(mapConfigs)) { - mapData[id] = { walls: new Set(), spawn: { x: 0, y: 0 }, npcs: [], exits: [], interactions: [] }; + mapData[id] = { walls: new Set(), spawn: null, npcs: [], exits: [], interactions: [] }; } // Load current game data @@ -283,7 +283,7 @@ function loadCurrentGameData() { 11: [0,1,2,3,6,7,8,9] }; mapData.lab.walls = wallDataToSet(labWalls); - mapData.lab.spawn = { x: 4, y: 10 }; + mapData.lab.spawn = null; // No spawn β€” player enters via door from town mapData.lab.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!'] } ]; @@ -345,12 +345,14 @@ function wallDataToSet(data) { function switchMap(id) { currentMapId = id; selectedEntity = null; - // Center camera on spawn + // Center camera on spawn or map center const md = mapData[id]; const cfg = mapConfigs[id]; if (md && cfg) { - camera.x = -(md.spawn.x * TILE_PX - canvas.width / 2 + TILE_PX / 2); - camera.y = -(md.spawn.y * TILE_PX - canvas.height / 2 + TILE_PX / 2); + const cx = md.spawn ? md.spawn.x : Math.floor(cfg.widthTiles / 2); + const cy = md.spawn ? md.spawn.y : Math.floor(cfg.heightTiles / 2); + camera.x = -(cx * TILE_PX - canvas.width / 2 + TILE_PX / 2); + camera.y = -(cy * TILE_PX - canvas.height / 2 + TILE_PX / 2); } updateEntityList(); updateProps(); @@ -419,14 +421,17 @@ function render() { ctx.stroke(); } - // Spawn - const sp = md.spawn; - ctx.fillStyle = 'rgba(0, 229, 153, 0.4)'; - ctx.fillRect(sp.x * TILE_PX, sp.y * TILE_PX, TILE_PX, TILE_PX); - ctx.strokeStyle = '#00e599'; - ctx.lineWidth = 2; - ctx.strokeRect(sp.x * TILE_PX, sp.y * TILE_PX, TILE_PX, TILE_PX); - drawLabel(ctx, '🏠', sp.x, sp.y); + // Spawn (optional β€” only needed on the starting map) + if (md.spawn) { + const sp = md.spawn; + const spSel = selectedEntity?.type === 'spawn'; + ctx.fillStyle = 'rgba(0, 229, 153, 0.4)'; + ctx.fillRect(sp.x * TILE_PX, sp.y * TILE_PX, TILE_PX, TILE_PX); + ctx.strokeStyle = spSel ? '#fff' : '#00e599'; + ctx.lineWidth = spSel ? 2.5 : 2; + ctx.strokeRect(sp.x * TILE_PX, sp.y * TILE_PX, TILE_PX, TILE_PX); + drawLabel(ctx, '🏠', sp.x, sp.y); + } // Exits md.exits.forEach((e, i) => { @@ -548,8 +553,15 @@ function onMouseDown(e) { render(); break; case 'spawn': - md.spawn = { x: tile.x, y: tile.y }; - updateEntityList(); render(); + // Toggle: if spawn already at this tile, remove it; otherwise place/move + if (md.spawn && md.spawn.x === tile.x && md.spawn.y === tile.y) { + md.spawn = null; + selectedEntity = null; + } else { + md.spawn = { x: tile.x, y: tile.y }; + selectedEntity = { type: 'spawn', index: 0 }; + } + updateEntityList(); updateProps(); render(); break; case 'npc': md.npcs.push({ id: `npc_${Date.now()}`, x: tile.x, y: tile.y, facing: 'down', dialog: ['Hello!'] }); @@ -597,7 +609,7 @@ function onMouseMove(e) { const key = `${tile.x},${tile.y}`; let info = ''; if (md.walls.has(key)) info += 'Wall '; - if (md.spawn.x === tile.x && md.spawn.y === tile.y) info += 'Spawn '; + if (md.spawn && md.spawn.x === tile.x && md.spawn.y === tile.y) info += 'Spawn '; md.npcs.forEach(n => { if (n.x === tile.x && n.y === tile.y) info += `NPC:${n.id} `; }); md.exits.forEach(e => { if (e.x === tile.x && e.y === tile.y) info += `Exitβ†’${e.targetMap}(${e.targetX ?? '?'},${e.targetY ?? '?'}) `; }); md.interactions.forEach(i => { if (i.x === tile.x && i.y === tile.y) info += `${i.type}:${i.label} `; }); @@ -655,7 +667,8 @@ function onKeyDown(e) { e.preventDefault(); const md = getData(); const { type, index } = selectedEntity; - if (type === 'npc') md.npcs.splice(index, 1); + if (type === 'spawn') md.spawn = null; + else if (type === 'npc') md.npcs.splice(index, 1); else if (type === 'exit') md.exits.splice(index, 1); else if (type === 'interaction') md.interactions.splice(index, 1); selectedEntity = null; @@ -731,13 +744,15 @@ function selectEntityAt(tx, ty) { idx = md.interactions.findIndex(i => i.x === tx && i.y === ty); if (idx >= 0) { selectedEntity = { type: 'interaction', index: idx }; updateEntityList(); updateProps(); return; } // Spawn? - if (md.spawn.x === tx && md.spawn.y === ty) { selectedEntity = { type: 'spawn', index: 0 }; updateEntityList(); updateProps(); return; } + if (md.spawn && md.spawn.x === tx && md.spawn.y === ty) { selectedEntity = { type: 'spawn', index: 0 }; updateEntityList(); updateProps(); return; } selectedEntity = null; updateEntityList(); updateProps(); } function deleteEntityAt(tx, ty) { const md = getData(); + // Spawn + if (md.spawn && md.spawn.x === tx && md.spawn.y === ty) { md.spawn = null; selectedEntity = null; updateEntityList(); updateProps(); return; } let idx = md.npcs.findIndex(n => n.x === tx && n.y === ty); if (idx >= 0) { md.npcs.splice(idx, 1); selectedEntity = null; updateEntityList(); updateProps(); return; } idx = md.exits.findIndex(e => e.x === tx && e.y === ty); @@ -751,7 +766,9 @@ function deleteEntityAt(tx, ty) { function updateEntityList() { const md = getData(); - document.getElementById('spawn-list').innerHTML = makeEntityItem('spawn', 0, '🏠', `Spawn`, md.spawn.x, md.spawn.y); + document.getElementById('spawn-list').innerHTML = md.spawn + ? makeEntityItem('spawn', 0, '🏠', `Spawn`, md.spawn.x, md.spawn.y) + : '
None (use 🏠 tool to place)
'; document.getElementById('npc-list').innerHTML = md.npcs.map((n, i) => makeEntityItem('npc', i, 'πŸ‘€', n.id, n.x, n.y, '#cc55ff') @@ -799,7 +816,7 @@ function getSelectedEntityData() { if (!selectedEntity) return null; const md = getData(); switch (selectedEntity.type) { - case 'spawn': return md.spawn; + case 'spawn': return md.spawn || null; case 'npc': return md.npcs[selectedEntity.index]; case 'exit': return md.exits[selectedEntity.index]; case 'interaction': return md.interactions[selectedEntity.index]; @@ -954,7 +971,9 @@ function generateMapsJS() { out += ` image: '${imageKey}',\n`; out += ` widthTiles: ${cfg.widthTiles},\n`; out += ` heightTiles: ${cfg.heightTiles},\n`; - out += ` spawn: { x: ${md.spawn.x}, y: ${md.spawn.y} },\n`; + if (md.spawn) { + out += ` spawn: { x: ${md.spawn.x}, y: ${md.spawn.y} },\n`; + } out += ` wallSet: buildWallSet(${varName(mapId)}Walls),\n\n`; // Exits @@ -1154,10 +1173,12 @@ function parseAndLoadMapsJS(src) { mapData[mapId].walls = wallDataToSet(wallData); } - // Parse spawn + // Parse spawn (optional β€” only present on initial map) const spawnMatch = src.match(new RegExp(`${vName}Map[\\s\\S]*?spawn:\\s*\\{\\s*x:\\s*(\\d+)\\s*,\\s*y:\\s*(\\d+)`)); if (spawnMatch) { mapData[mapId].spawn = { x: parseInt(spawnMatch[1]), y: parseInt(spawnMatch[2]) }; + } else { + mapData[mapId].spawn = null; } // Parse NPCs - find the npcs array diff --git a/js/world/maps.js b/js/world/maps.js index 9428cca..33ef076 100644 --- a/js/world/maps.js +++ b/js/world/maps.js @@ -28,7 +28,7 @@ const labMap = { image: 'map:lab', widthTiles: 10, heightTiles: 12, - spawn: { x: 4, y: 10 }, + // No spawn β€” player enters via door from town wallSet: buildWallSet(labWalls), exits: [ @@ -110,7 +110,6 @@ const houseA1fMap = { image: 'map:house-a-1f', widthTiles: 8, heightTiles: 8, - spawn: { x: 0, y: 0 }, wallSet: buildWallSet(houseA1fWalls), exits: [ @@ -134,7 +133,6 @@ const route1Map = { image: 'map:route-1', widthTiles: 20, heightTiles: 36, - spawn: { x: 0, y: 0 }, wallSet: buildWallSet(route1Walls), exits: [