'use strict'; // === PLAYER MOVEMENT === function tryMove(dx, dy) { if (gameState.player.invincibleTimer > 0 && gameState.player.invincibleTimer < 1000) return; const px = gameState.player.x; const py = gameState.player.y; const nx = px + dx; const ny = py + dy; if (dx === 0 && dy === 0) return; // Update facing direction if (dy === 1 && gameState.player.dir !== 0) gameState.player.dir = 0; else if (dy === -1 && gameState.player.dir !== 1) gameState.player.dir = 1; else if (dx === -1 && gameState.player.dir !== 2) gameState.player.dir = 2; else if (dx === 1 && gameState.player.dir !== 3) gameState.player.dir = 3; if (!inBounds(nx, ny)) return; // Check exit before wall check if (gameState.exit && gameState.exit.x === nx && gameState.exit.y === ny) { tryExit(); return; } // Check walls if (isWall(gameState.layout, nx, ny)) return; // Check lava/water (not passable without item) if (gameState.layout[ny][nx] === TILE_TYPES.LAVA) return; if (gameState.layout[ny][nx] === TILE_TYPES.WATER) { if (!gameState.player.items.find(i => i.id === 'flippers' || i.id === 'power_boomerang')) return; } // Check traps const trap = gameState.traps.find(t => t.x === nx && t.y === ny && !t.triggered); if (trap) { trap.triggered = true; if (!gameState.player.invincible) { gameState.player.hp -= trap.damage; gameState.player.hitTimer = HIT_FLASH; showMessage(`Trap! (-${trap.damage} HP)`); } spawnParticles(nx * TILE + TILE / 2, ny * TILE + TILE / 2, trap.color || '#FF0000', 8); } // Push rocks const rock = gameState.rocks.find(r => r.x === nx && r.y === ny); if (rock) { const rnx = nx + dx; const rny = ny + dy; if (!inBounds(rnx, rny)) return; if (isWall(gameState.layout, rnx, rny)) return; if (gameState.rocks.find(r => r.x === rnx && r.y === rny)) return; if (gameState.enemies.find(e => e.x === rnx && e.y === rny)) return; if (gameState.exit && gameState.exit.x === rnx && gameState.exit.y === rny) return; if (gameState.chests.find(c => c.x === rnx && c.y === rny)) return; if (gameState.layout[rny][rnx] === TILE_TYPES.LAVA) return; rock.x = rnx; rock.y = rny; spawnParticles(rnx * TILE + TILE / 2, rny * TILE + TILE / 2, COLORS.rock, 3); } // Check enemies (can't walk through) if (gameState.enemies.find(e => e.x === nx && e.y === ny)) return; if (gameState.currentBoss && gameState.currentBoss.x === nx && gameState.currentBoss.y === ny) return; // Move player gameState.player.x = nx; gameState.player.y = ny; // Check chests const chest = gameState.chests.find(c => c.x === nx && c.y === ny && !c.opened); if (chest) { openChest(chest); return; } // Check floor items const floorItem = gameState.floorItems.find(i => i.x === nx && i.y === ny && !i.collected); if (floorItem) { collectFloorItem(floorItem); } // Check switches const sw = gameState.switches.find(s => s.x === nx && s.y === ny && !s.activated); if (sw) { sw.activated = true; showMessage(sw.message || 'Switch activated!'); spawnParticles(nx * TILE + TILE / 2, ny * TILE + TILE / 2, '#FFD700', 6); if (sw.targetDoor) { const door = gameState.doors.find(d => d.id === sw.targetDoor); if (door) { door.opened = !door.opened; showMessage(door.opened ? 'Door opened!' : 'Door closed!'); } } if (sw.openExit) { gameState.exitOpen = true; showMessage('The exit has opened!'); } } // Check levers const lever = gameState.levers.find(l => l.x === nx && l.y === ny && !l.activated); if (lever) { lever.activated = true; showMessage(lever.message || 'Lever pulled!'); spawnParticles(nx * TILE + TILE / 2, ny * TILE + TILE / 2, COLORS.lever, 6); if (lever.effect === 'open_exit') { gameState.exitOpen = true; showMessage('The exit has opened!'); } if (lever.effect === 'drop_item') { gameState.player.items.push({ ...lever.item }); showMessage(`Got ${lever.item.name}!`); } if (lever.effect === 'remove_wall') { for (const [wx, wy] of lever.wallsToRemove) { if (inBounds(wx, wy)) gameState.layout[wy][wx] = TILE_TYPES.FLOOR; } } } // Check doors const door = gameState.doors.find(d => d.x === nx && d.y === ny); if (door && !door.opened) { if (door.requiredKey && gameState.player.keys > 0) { gameState.player.keys--; door.opened = true; showMessage('Door unlocked!'); } else if (door.requiredKey) { showMessage('Need a key!'); } else { door.opened = true; } } // Check soft walls (breakable) if (isSoftWall(gameState.layout, nx, ny)) { const weapon = gameState.player.activeWeapon; if (weapon && (weapon.canBreakWalls || weapon.name === 'bomb' || weapon.name === 'hammer')) { gameState.layout[ny][nx] = TILE_TYPES.FLOOR; spawnParticles(nx * TILE + TILE / 2, ny * TILE + TILE / 2, COLORS.softwall, 6); showMessage('Wall destroyed!'); } else { showMessage('Need bombs or hammer!'); } return; } // Check puzzle checkPuzzle(); } function tryInteract() { const px = gameState.player.x; const py = gameState.player.y; const dir = DIRS[gameState.player.dir]; const tx = px + dir.x; const ty = py + dir.y; // Try doors in facing direction const door = gameState.doors.find(d => d.x === tx && d.y === ty && !d.opened); if (door) { if (door.requiredKey && gameState.player.keys > 0) { gameState.player.keys--; door.opened = true; showMessage('Door unlocked!'); } else if (door.requiredKey) { showMessage('Need a key!'); } else { door.opened = true; showMessage('Door opened!'); } return; } // Try chests in facing direction const chest = gameState.chests.find(c => c.x === tx && c.y === ty && !c.opened); if (chest) { openChest(chest); return; } } function tryExit() { if (!gameState.exit) return; if (!gameState.exitOpen) { showMessage('The door is locked!'); return; } // Check if this is the last room if (gameState.currentRoomIdx >= (gameState.dungeonRooms ? gameState.dungeonRooms.length - 1 : 0)) { completeDungeon(); return; } // Move to next room gameState.currentRoomIdx++; loadDungeonRoom(gameState.currentDungeon, gameState.currentRoomIdx); } function completeDungeon() { const dungeon = DUNGEONS[gameState.currentDungeon]; showMessage(`${dungeon.name} cleared!`); // Save progress markDungeonComplete(gameState.currentDungeon); addStats(gameState.roomsCleared, gameState.enemiesDefeated); gameState.dungeonsCompleted++; saveProgress(); // Check if all dungeons completed if (gameState.currentDungeon >= DUNGEONS.length - 1) { switchScreen(SCREEN_GAME_COMPLETE); } else { switchScreen(SCREEN_DUNGEON_COMPLETE); } } function checkPuzzle() { if (gameState.targets.length === 0) return; const allSolved = gameState.targets.every(t => gameState.rocks.some(r => r.x === t.x && r.y === t.y)); if (allSolved) { gameState.exitOpen = true; showMessage('Puzzle solved! The door has opened!'); spawnParticles(gameState.exit.x * TILE + TILE / 2, gameState.exit.y * TILE + TILE / 2, '#FFD700', 12); } } if (typeof module !== 'undefined' && module.exports) { module.exports = { tryMove, tryInteract, tryExit, completeDungeon, checkPuzzle }; }