// gameMode.js - Central coordinator: switches between World and Workshop modes import { worldState, setPlayerPosition, warpToMap, solvePuzzle, isPuzzleSolved } from './worldState.js'; import { initWorldRenderer, startWorldLoop, stopWorldLoop } from './worldRenderer.js'; import { initWorldInput, destroyWorldInput, setInteractionHandler } from './worldInput.js'; import { getMap } from './maps.js'; // Circuit editor stop function (to stop its render loop when switching modes) import { stopCircuitLoop } from '../renderer.js'; // Circuit editor modules (registered from app.js to avoid circular deps) let circuitEditorInit = null; let circuitEditorDestroy = null; let currentMode = 'none'; // 'world' | 'workshop' /** * Register the circuit editor's init/destroy functions. * Called from app.js so we don't create circular imports. */ export function registerCircuitEditor(initFn, destroyFn) { circuitEditorInit = initFn; circuitEditorDestroy = destroyFn; } /** * Boot the game — start in world mode */ export function startGame() { // Set spawn const map = getMap(worldState.currentMap); if (map && map.spawn) { setPlayerPosition(map.spawn.x, map.spawn.y); } // Wire up interaction handler setInteractionHandler(handleInteraction); // Enter world mode enterWorldMode(); } // ==================== Mode switching ==================== export function enterWorldMode() { if (currentMode === 'world') return; // Tear down workshop if active if (currentMode === 'workshop') { stopCircuitLoop(); if (circuitEditorDestroy) circuitEditorDestroy(); hideWorkshopUI(); } currentMode = 'world'; worldState.mode = 'world'; showWorldUI(); initWorldRenderer(); initWorldInput(); startWorldLoop(); console.log('[gameMode] entered world mode'); } export function enterWorkshopMode() { if (currentMode === 'workshop') return; // Tear down world if (currentMode === 'world') { stopWorldLoop(); destroyWorldInput(); hideWorldUI(); } currentMode = 'workshop'; worldState.mode = 'workshop'; showWorkshopUI(); if (circuitEditorInit) circuitEditorInit(); console.log('[gameMode] entered workshop mode'); } export function getCurrentMode() { return currentMode; } // ==================== Interaction handler ==================== function handleInteraction(event) { switch (event.type) { case 'enterWorkshop': enterWorkshopMode(); break; case 'puzzleDoor': { const inter = event.data; if (isPuzzleSolved(inter.puzzleId)) { // Already solved — could open door, show message, etc. return; } // For now, show a hint dialog. Later: open puzzle UI worldState.dialog = { lines: [ 'This door requires a logic circuit to open.', `Required output pattern: [${inter.requiredOutputs.join(', ')}]`, 'Craft a component in your Workshop (TAB)!' ], currentLine: 0, speakerName: 'System' }; worldState.mode = 'dialog'; break; } case 'mapExit': { const { targetMap, targetX, targetY } = event.data; const p = worldState.player; // Save return point: where the player is NOW (one tile back from the exit) // so when they leave the target map, they return here worldState.returnPoints.push({ fromMap: worldState.currentMap, fromX: p.x, fromY: p.y }); // If exit has explicit coordinates, use them // Otherwise, check for a stored return point for the target map if (targetX != null && targetY != null) { warpToMap(targetMap, targetX, targetY); } else { // Pop the most recent return point for this map const retIdx = worldState.returnPoints.findLastIndex( rp => rp.fromMap === targetMap ); if (retIdx >= 0) { const ret = worldState.returnPoints[retIdx]; worldState.returnPoints.splice(retIdx, 1); warpToMap(ret.fromMap, ret.fromX, ret.fromY); } else { // Fallback: use map spawn point const destMap = getMap(targetMap); const sx = destMap?.spawn?.x ?? 0; const sy = destMap?.spawn?.y ?? 0; warpToMap(targetMap, sx, sy); } } console.log(`[gameMode] warped to ${worldState.currentMap} (${worldState.player.x}, ${worldState.player.y})`); break; } case 'openInventory': // TODO: inventory UI console.log('[gameMode] inventory:', worldState.inventory); break; } } // ==================== UI visibility ==================== function showWorldUI() { // Hide workshop-specific elements const toolbar = document.getElementById('toolbar'); const wavePanel = document.getElementById('waveform-panel'); const canvas = document.getElementById('canvas'); if (toolbar) toolbar.style.display = 'none'; if (wavePanel) wavePanel.style.display = 'none'; if (canvas) { canvas.style.top = '0'; canvas.style.cursor = 'default'; } // Show back-to-world button (hidden since we're IN world) const backBtn = document.getElementById('back-to-world-btn'); if (backBtn) backBtn.style.display = 'none'; } function hideWorldUI() { // Nothing special to hide — canvas stays } function showWorkshopUI() { const toolbar = document.getElementById('toolbar'); const canvas = document.getElementById('canvas'); if (toolbar) toolbar.style.display = 'flex'; if (canvas) { canvas.style.top = '56px'; canvas.style.cursor = 'default'; } // Show back-to-world button const backBtn = document.getElementById('back-to-world-btn'); if (backBtn) backBtn.style.display = 'flex'; } function hideWorkshopUI() { const toolbar = document.getElementById('toolbar'); if (toolbar) toolbar.style.display = 'none'; const backBtn = document.getElementById('back-to-world-btn'); if (backBtn) backBtn.style.display = 'none'; }