update GUI

This commit is contained in:
pobadoba
2026-05-10 17:26:54 +09:00
parent c28a1d1f6a
commit 808b2545e2
7 changed files with 537 additions and 72 deletions

View File

@@ -39,28 +39,6 @@
</div>
</div>
<div class="control-group">
<h3>Settings</h3>
<div class="slider-group">
<label>Maze width:
<input id="slider-width" type="range" min="9" max="31" step="2" value="11" />
<span id="value-width">11</span> cells
</label>
</div>
<div class="slider-group">
<label>Maze height:
<input id="slider-height" type="range" min="9" max="31" step="2" value="11" />
<span id="value-height">11</span> cells
</label>
</div>
<div class="slider-group">
<label>Min chest dead-ends:
<input id="slider-deadends" type="range" min="1" max="10" step="1" value="2" />
<span id="value-deadends">2</span>
</label>
</div>
</div>
<div class="control-group">
<h3>Status</h3>
<div class="status-display">
@@ -71,13 +49,15 @@
<div class="status-line"><strong>Time left:</strong> <span id="status-time">60.0</span></div>
<div class="status-line"><strong>Key:</strong> <span id="status-key">no</span></div>
<div class="status-line"><strong>Rounds:</strong> <span id="status-rounds">0</span></div>
<div class="status-message" id="status-message">Adjust settings, then start a run.</div>
</div>
</div>
</div>
</section>
</main>
<script type="module" src="/src/multi_sketch.js"></script>
<script type="module">
import "./src/babylon_panel.js";
import "./src/html_panel.js";
</script>
</body>
</html>

526
src/babylon_panel.js Normal file
View File

@@ -0,0 +1,526 @@
import * as BABYLON from "babylonjs";
import { sharedState } from "./game/state.js";
import { seededRng, generateMazeGrid, findDeadEnds } from "./game/maze.js";
import { gridCellToWorld, isWalkableCell } from "./game/grid.js";
import { playSfx, primeSfx } from "./game/sfx.js";
import chestTextureUrl from "../img/img_chest.png";
import wallTextureUrl from "../img/img_wall.png";
import groundTextureUrl from "../img/img_ground.png";
import doorTextureUrl from "../img/img_door.png";
import gameOverImageUrl from "../img/img_jobapplication.png";
// Initialize Babylon.js engine and scene
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const canvasTime = document.getElementById("canvas-time");
const canvasKey = document.getElementById("canvas-key");
const canvasRounds = document.getElementById("canvas-rounds");
const gameOverOverlay = document.getElementById("game-over-overlay");
const gameOverImage = document.getElementById("game-over-image");
if (gameOverImage) {
gameOverImage.src = gameOverImageUrl;
}
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.05, 0.07, 0.1, 1);
const camera = new BABYLON.UniversalCamera("cam", new BABYLON.Vector3(0, 1.6, 0), scene);
camera.minZ = 0.1;
camera.speed = 1.12;
camera.angularSensibility = 1000;
camera.inertia = 0.6;
camera.keysUp = [87]; // W
camera.keysDown = [83]; // S
camera.keysLeft = [65]; // A
camera.keysRight = [68]; // D
camera.checkCollisions = true;
camera.applyGravity = true;
camera.ellipsoid = new BABYLON.Vector3(0.35, 0.9, 0.35);
camera.attachControl(canvas, true);
const overviewCamera = new BABYLON.ArcRotateCamera(
"overviewCam",
-Math.PI / 2,
Math.PI / 3.2,
40,
new BABYLON.Vector3(0, 0, 0),
scene,
);
overviewCamera.lowerBetaLimit = 0.2;
overviewCamera.upperBetaLimit = Math.PI / 2.05;
overviewCamera.lowerRadiusLimit = 8;
overviewCamera.inertia = 0.7;
let cameraMode = "fp";
scene.activeCamera = camera;
let lastFootstepPosition = null;
let footstepAccumulator = 0;
let footstepElapsed = 0;
let gameOverActive = false;
let lowTimeAlertPlayed = false;
scene.gravity = new BABYLON.Vector3(0, -0.2, 0);
scene.collisionsEnabled = true;
canvas.addEventListener("click", () => {
primeSfx();
if (cameraMode === "fp" && document.pointerLockElement !== canvas) {
canvas.requestPointerLock();
}
});
function updateOverviewCameraForMaze(w, h) {
const mazeSpan = Math.max(w, h) * cellSize;
overviewCamera.radius = Math.max(mazeSpan * 1.05, 16);
overviewCamera.target = new BABYLON.Vector3(0, 0, 0);
}
function showGameOverScreen() {
gameOverActive = true;
if (gameOverOverlay) {
gameOverOverlay.hidden = false;
}
if (document.pointerLockElement === canvas && document.exitPointerLock) {
document.exitPointerLock();
}
}
function hideGameOverScreen() {
gameOverActive = false;
if (gameOverOverlay) {
gameOverOverlay.hidden = true;
}
}
function restartRunFromGameOver() {
sharedState.runtime.runActive = true;
sharedState.runtime.hasKey = false;
sharedState.runtime.roundsCompleted = 0;
sharedState.runtime.elapsedSeconds = ROUND_TIME_SECONDS;
sharedState.runtime.message = "Run restarted.";
sharedState.config.level = 1;
hideGameOverScreen();
generateLevel();
}
function switchCameraMode() {
if (cameraMode === "fp") {
if (document.pointerLockElement === canvas && document.exitPointerLock) {
document.exitPointerLock();
}
camera.detachControl(canvas);
overviewCamera.attachControl(canvas, true);
scene.activeCamera = overviewCamera;
cameraMode = "overview";
sharedState.runtime.message = "Overview camera (press V to return to first-person).";
return;
}
overviewCamera.detachControl(canvas);
camera.attachControl(canvas, true);
scene.activeCamera = camera;
cameraMode = "fp";
sharedState.runtime.message = "First-person camera (W/A/S/D + mouse).";
}
window.addEventListener("keydown", (event) => {
if (event.code === "KeyW" || event.code === "KeyA" || event.code === "KeyS" || event.code === "KeyD" || event.code === "KeyV" || event.code === "KeyR") {
primeSfx();
}
if (event.code === "KeyR" && gameOverActive) {
restartRunFromGameOver();
return;
}
if (event.code === "KeyV") {
switchCameraMode();
}
});
new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Central sphere (hidden but kept for reference)
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 2 }, scene);
sphere.isVisible = false;
const sphereMaterial = new BABYLON.StandardMaterial("sphereMaterial", scene);
sphereMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.55, 0.95);
sphereMaterial.emissiveColor = new BABYLON.Color3(0.05, 0.12, 0.2);
sphere.material = sphereMaterial;
// Main render loop
engine.runRenderLoop(() => {
const level = sharedState.config.level;
sphere.rotation.y += 0.01;
sphere.scaling.x = 1 + (level - 1) * 0.05;
sphere.scaling.z = 1 + (level - 1) * 0.05;
if (canvasTime) {
canvasTime.textContent = `${sharedState.runtime.elapsedSeconds.toFixed(1)}s`;
}
if (canvasKey) {
canvasKey.textContent = sharedState.runtime.hasKey ? "yes" : "no";
}
if (canvasRounds) {
canvasRounds.textContent = String(sharedState.runtime.roundsCompleted);
}
scene.render();
});
window.addEventListener("resize", () => {
engine.resize();
});
// Maze data structures
let levelMeshes = [];
let chestMap = new Map(); // key: "x,y" -> {mesh, opened}
let keyChestKey = null;
let exitBox = null;
let exitGridPos = null; // track exit grid position for collision checking
let spawnGridPos = null; // track spawn grid position for validation
let spawnMarker = null;
let highlightedChest = null;
const cellSize = 2;
const ROUND_TIME_SECONDS = 60;
function setChestHighlight(mesh) {
if (highlightedChest === mesh) {
return;
}
if (highlightedChest) {
highlightedChest.renderOutline = false;
}
highlightedChest = mesh;
if (highlightedChest) {
highlightedChest.outlineColor = new BABYLON.Color3(0.85, 0.85, 0.85);
highlightedChest.outlineWidth = 0.08;
highlightedChest.renderOutline = true;
}
}
function clearLevelMeshes() {
setChestHighlight(null);
for (const m of levelMeshes) {
try { m.dispose(); } catch(e) {}
}
levelMeshes = [];
chestMap.clear();
keyChestKey = null;
if (exitBox) { try { exitBox.dispose(); } catch(e){}; exitBox = null; }
if (spawnMarker) { try { spawnMarker.dispose(); } catch(e){}; spawnMarker = null; }
exitGridPos = null;
spawnGridPos = null;
}
function isReservedCell(x, y) {
if (chestMap.has(`${x},${y}`)) return true;
if (exitGridPos && exitGridPos.x === x && exitGridPos.y === y) return true;
return false;
}
function buildLevelFromGrid(grid) {
clearLevelMeshes();
const h = grid.length;
const w = grid[0].length;
const halfW = (w * cellSize) / 2;
const halfH = (h * cellSize) / 2;
const floor = BABYLON.MeshBuilder.CreateGround('levelGround', { width: w*cellSize, height: h*cellSize }, scene);
floor.position = new BABYLON.Vector3(0, 0, 0);
floor.checkCollisions = true;
const fm = new BABYLON.StandardMaterial('floorMat', scene);
fm.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1);
fm.diffuseTexture = new BABYLON.Texture(groundTextureUrl, scene);
fm.diffuseTexture.uScale = Math.max(1, Math.floor(w / 2));
fm.diffuseTexture.vScale = Math.max(1, Math.floor(h / 2));
fm.diffuseColor = new BABYLON.Color3(0.9, 0.9, 0.9);
floor.material = fm;
levelMeshes.push(floor);
const wallMat = new BABYLON.StandardMaterial('wallMat', scene);
wallMat.diffuseTexture = new BABYLON.Texture(wallTextureUrl, scene);
wallMat.diffuseColor = new BABYLON.Color3(0.95, 0.95, 0.95);
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
if (grid[y][x] === 1) {
const box = BABYLON.MeshBuilder.CreateBox(`wall_${x}_${y}`, { size: cellSize }, scene);
box.position = new BABYLON.Vector3(x*cellSize - halfW + cellSize/2, cellSize/2, y*cellSize - halfH + cellSize/2);
box.material = wallMat;
box.checkCollisions = true;
levelMeshes.push(box);
}
}
}
}
function placeChestsOnDeadEnds(grid, deadEnds, minCount, seed) {
const rng = seededRng(seed);
for (let i = deadEnds.length - 1; i > 0; i--) {
const j = Math.floor(rng() * (i+1));
[deadEnds[i], deadEnds[j]] = [deadEnds[j], deadEnds[i]];
}
const chosen = deadEnds.slice(0, Math.min(minCount, deadEnds.length));
const halfW = (grid[0].length * cellSize) / 2;
const halfH = (grid.length * cellSize) / 2;
const chestMat = new BABYLON.StandardMaterial('chestMat', scene);
chestMat.diffuseTexture = new BABYLON.Texture(chestTextureUrl, scene);
chestMat.diffuseColor = new BABYLON.Color3(0.95, 0.95, 0.95);
for (const [x,y] of chosen) {
const c = BABYLON.MeshBuilder.CreateBox(`chest_${x}_${y}`, { width: cellSize*0.8, height: cellSize*0.6, depth: cellSize*0.6 }, scene);
c.position = new BABYLON.Vector3(x*cellSize - halfW + cellSize/2, cellSize*0.3, y*cellSize - halfH + cellSize/2);
c.material = chestMat;
c.isPickable = true;
levelMeshes.push(c);
chestMap.set(`${x},${y}`, { mesh: c, opened: false });
}
if (chosen.length > 0) {
const k = Math.floor(rng() * chosen.length);
const [kx, ky] = chosen[k];
keyChestKey = `${kx},${ky}`;
const entry = chestMap.get(keyChestKey);
if (entry) {
const km = new BABYLON.StandardMaterial('keyChestMat', scene);
km.diffuseTexture = new BABYLON.Texture(chestTextureUrl, scene);
km.diffuseColor = new BABYLON.Color3(0.95, 0.95, 0.95);
km.emissiveColor = new BABYLON.Color3(0.3, 0.22, 0.02);
entry.mesh.material = km;
}
}
}
function placeExit(grid, seed) {
const dead = findDeadEnds(grid);
const rng = seededRng(seed+1);
if (dead.length === 0) return;
// Filter out dead ends that have chests
const availableDead = dead.filter(([x, y]) => !chestMap.has(`${x},${y}`));
if (availableDead.length === 0) {
// Fallback: use any dead end if no chest-free spot available
// This shouldn't happen in normal cases
const idx = Math.floor(rng() * dead.length);
const [x,y] = dead[idx];
exitGridPos = { x, y };
// Continue below
} else {
const idx = Math.floor(rng() * availableDead.length);
const [x,y] = availableDead[idx];
exitGridPos = { x, y };
}
const [x,y] = [exitGridPos.x, exitGridPos.y];
if (!isWalkableCell(grid, x, y)) {
console.warn("Exit selected on non-walkable cell", { x, y });
return;
}
const exitWorld = gridCellToWorld(grid, x, y, cellSize);
const ex = exitWorld.x;
const ez = exitWorld.z;
const plane = BABYLON.MeshBuilder.CreatePlane('exitDoor', {
width: cellSize * 1.35,
height: cellSize * 1.85,
sideOrientation: BABYLON.Mesh.DOUBLESIDE,
}, scene);
const exitMat = new BABYLON.StandardMaterial('exitMat', scene);
exitMat.diffuseTexture = new BABYLON.Texture(doorTextureUrl, scene);
exitMat.diffuseColor = new BABYLON.Color3(0.95, 0.95, 0.95);
exitMat.emissiveColor = new BABYLON.Color3(0.07, 0.07, 0.07);
plane.material = exitMat;
plane.position = new BABYLON.Vector3(ex, cellSize * 0.92, ez);
plane.billboardMode = BABYLON.Mesh.BILLBOARDMODE_Y;
exitBox = plane;
levelMeshes.push(plane);
}
function spawnCameraAt(grid) {
const h = grid.length;
const w = grid[0].length;
let bestCell = null;
let bestDist = -1;
// Choose a valid spawn that is far from exit when possible.
for (let y = 1; y < h - 1; y++) {
for (let x = 1; x < w - 1; x++) {
if (!isWalkableCell(grid, x, y)) continue;
if (isReservedCell(x, y)) continue;
const d = exitGridPos ? Math.hypot(x - exitGridPos.x, y - exitGridPos.y) : 0;
if (d > bestDist) {
bestDist = d;
bestCell = { x, y };
}
}
}
if (!bestCell) {
console.warn("No valid spawn cell found.");
return;
}
spawnGridPos = bestCell;
const spawnWorld = gridCellToWorld(grid, bestCell.x, bestCell.y, cellSize);
const px = spawnWorld.x;
const pz = spawnWorld.z;
try {
if (camera && camera.position) {
camera.position = new BABYLON.Vector3(px, 1.6, pz);
}
} catch (e) {}
lastFootstepPosition = camera && camera.position ? camera.position.clone() : new BABYLON.Vector3(px, 1.6, pz);
footstepAccumulator = 0;
footstepElapsed = 0;
}
function generateLevel() {
hideGameOverScreen();
lowTimeAlertPlayed = false;
if (canvasTime) {
canvasTime.classList.remove("low-time");
}
const cfg = sharedState.config;
const seed = cfg.seed;
const roundScale = Math.max(0, cfg.level - 1);
const w = Math.max(9, cfg.mazeWidth + roundScale * 2);
const h = Math.max(9, cfg.mazeHeight + roundScale * 2);
const chestCount = Math.max(1, cfg.minChestDeadEnds + roundScale);
const grid = generateMazeGrid(w, h, seed + cfg.level);
updateOverviewCameraForMaze(w, h);
const dead = findDeadEnds(grid);
buildLevelFromGrid(grid);
placeChestsOnDeadEnds(grid, dead, chestCount, seed + cfg.level);
placeExit(grid, seed + cfg.level);
spawnCameraAt(grid);
const placementValid =
!!exitGridPos &&
!!spawnGridPos &&
isWalkableCell(grid, exitGridPos.x, exitGridPos.y) &&
isWalkableCell(grid, spawnGridPos.x, spawnGridPos.y) &&
!(exitGridPos.x === spawnGridPos.x && exitGridPos.y === spawnGridPos.y);
if (!placementValid) {
sharedState.runtime.message = `Placement warning: spawn/exit invalid on level ${cfg.level}.`;
console.warn("Invalid spawn/exit placement", { exitGridPos, spawnGridPos });
} else {
sharedState.runtime.message = `Level ${cfg.level} generated (spawn ${spawnGridPos.x},${spawnGridPos.y} / exit ${exitGridPos.x},${exitGridPos.y}).`;
}
lastFootstepPosition = camera && camera.position ? camera.position.clone() : lastFootstepPosition;
footstepAccumulator = 0;
footstepElapsed = 0;
window.requestAnimationFrame(()=>{ /* let scene update */ });
}
// Expose API for p5 to call
window.mazeGameApi = { generateLevel };
// Pointer interaction for chests
scene.onPointerObservable.add((pi) => {
if (pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) return;
if (!sharedState.runtime.runActive || gameOverActive) return;
const pick = scene.pick(scene.pointerX, scene.pointerY);
if (!pick || !pick.hit || !pick.pickedMesh) return;
const m = pick.pickedMesh;
if (!m.name.startsWith('chest_')) return;
const coords = m.name.split('_').slice(1).join(',');
const entry = chestMap.get(coords);
if (!entry) return;
if (entry.opened) {
primeSfx();
playSfx("chestClose", 0.8);
playSfx("lose", 0.85);
sharedState.runtime.runActive = false;
sharedState.runtime.message = 'Opened chest again — game over.';
showGameOverScreen();
return;
}
primeSfx();
playSfx("chestOpen", 0.8);
entry.opened = true;
if (coords === keyChestKey) {
sharedState.runtime.hasKey = true;
playSfx("key", 0.85);
sharedState.runtime.message = 'You found the key! Find the exit.';
} else {
sharedState.runtime.message = 'This chest was empty.';
}
});
// Level transition check
scene.registerBeforeRender(() => {
const targetRay = camera.getForwardRay(cellSize * 3.5);
const targetPick = scene.pickWithRay(targetRay, (mesh) => mesh.name.startsWith('chest_'));
if (targetPick && targetPick.hit && targetPick.pickedMesh) {
setChestHighlight(targetPick.pickedMesh);
} else {
setChestHighlight(null);
}
if (sharedState.runtime.runActive) {
const dt = engine.getDeltaTime() / 1000;
sharedState.runtime.elapsedSeconds = Math.max(0, sharedState.runtime.elapsedSeconds - dt);
const isLowTime = sharedState.runtime.elapsedSeconds < 10;
if (isLowTime && !lowTimeAlertPlayed) {
lowTimeAlertPlayed = true;
playSfx("clock", 0.75);
if (canvasTime) {
canvasTime.classList.add("low-time");
}
}
if (!isLowTime && lowTimeAlertPlayed) {
lowTimeAlertPlayed = false;
if (canvasTime) {
canvasTime.classList.remove("low-time");
}
}
if (sharedState.runtime.elapsedSeconds <= 0) {
sharedState.runtime.runActive = false;
sharedState.runtime.message = "Time up — game over.";
playSfx("lose", 0.85);
showGameOverScreen();
return;
}
}
if (sharedState.runtime.runActive && cameraMode === "fp" && camera && camera.position && document.pointerLockElement === canvas) {
const currentPosition = camera.position;
if (!lastFootstepPosition) {
lastFootstepPosition = currentPosition.clone();
}
const horizontalDistance = Math.hypot(
currentPosition.x - lastFootstepPosition.x,
currentPosition.z - lastFootstepPosition.z,
);
footstepAccumulator += horizontalDistance;
footstepElapsed += engine.getDeltaTime();
if (footstepAccumulator > 0.75 && footstepElapsed > 220) {
playSfx("step", 0.65);
footstepAccumulator = 0;
footstepElapsed = 0;
lastFootstepPosition = currentPosition.clone();
}
}
if (sharedState.runtime.hasKey && exitBox && camera && camera.position) {
const pos = camera.position;
const ex = exitBox.position.x, ez = exitBox.position.z;
const dist = Math.hypot(pos.x - ex, pos.z - ez);
if (dist < cellSize * 0.9) {
playSfx("win", 0.85);
sharedState.config.level += 1;
sharedState.runtime.hasKey = false;
sharedState.runtime.roundsCompleted += 1;
sharedState.runtime.elapsedSeconds = ROUND_TIME_SECONDS;
sharedState.runtime.message = `Level ${sharedState.config.level} starting.`;
generateLevel();
}
}
});
// Export shared state for p5 to use
export { sharedState };

View File

@@ -2,7 +2,7 @@ import * as BABYLON from "babylonjs";
import { sharedState } from "./game/state.js";
import { seededRng, generateMazeGrid, findDeadEnds } from "./game/maze.js";
import { gridCellToWorld, isWalkableCell } from "./game/grid.js";
import { playSfx, primeSfx } from "./sfx.js";
import { playSfx, primeSfx } from "./game/sfx.js";
import chestTextureUrl from "../img/img_chest.png";
import wallTextureUrl from "../img/img_wall.png";
import groundTextureUrl from "../img/img_ground.png";
@@ -138,8 +138,9 @@ window.addEventListener("keydown", (event) => {
new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Placeholder sphere
// Central sphere (hidden but kept for reference)
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 2 }, scene);
sphere.isVisible = false;
const sphereMaterial = new BABYLON.StandardMaterial("sphereMaterial", scene);
sphereMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.55, 0.95);
sphereMaterial.emissiveColor = new BABYLON.Color3(0.05, 0.12, 0.2);
@@ -151,9 +152,6 @@ engine.runRenderLoop(() => {
sphere.rotation.y += 0.01;
sphere.scaling.x = 1 + (level - 1) * 0.05;
sphere.scaling.z = 1 + (level - 1) * 0.05;
sphereMaterial.diffuseColor = sharedState.runtime.hasKey
? new BABYLON.Color3(0.25, 0.8, 0.45)
: new BABYLON.Color3(0.2, 0.55, 0.95);
if (canvasTime) {
canvasTime.textContent = `${sharedState.runtime.elapsedSeconds.toFixed(1)}s`;
}
@@ -377,18 +375,6 @@ function spawnCameraAt(grid) {
lastFootstepPosition = camera && camera.position ? camera.position.clone() : new BABYLON.Vector3(px, 1.6, pz);
footstepAccumulator = 0;
footstepElapsed = 0;
if (spawnMarker) {
try { spawnMarker.dispose(); } catch(e) {}
}
const marker = BABYLON.MeshBuilder.CreateSphere('spawnMarker', { diameter: cellSize*0.4 }, scene);
const markerMat = new BABYLON.StandardMaterial('spawnMarkerMat', scene);
markerMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 0.95);
markerMat.emissiveColor = new BABYLON.Color3(0.1, 0.3, 0.5);
marker.material = markerMat;
marker.position = new BABYLON.Vector3(px, cellSize*0.2, pz);
spawnMarker = marker;
levelMeshes.push(marker);
}
function generateLevel() {

View File

@@ -11,6 +11,6 @@ export const sharedState = (window.mazeGameState ??= {
hasKey: false,
roundsCompleted: 0,
elapsedSeconds: 60,
message: "Adjust settings, then start a run.",
message: "Press Start to play.",
},
});

View File

@@ -1,5 +1,5 @@
import { sharedState } from "./game/state.js";
import { playSfx, primeSfx } from "./sfx.js";
import { playSfx, primeSfx } from "./game/sfx.js";
// Handler functions (same as p5_panel but without p5 scoping)
function resetRun(message) {
@@ -29,9 +29,8 @@ function randomizeSeed() {
// Update display from shared state
function updateDisplay() {
document.getElementById("value-width").textContent = sharedState.config.mazeWidth;
document.getElementById("value-height").textContent = sharedState.config.mazeHeight;
document.getElementById("value-deadends").textContent = sharedState.config.minChestDeadEnds;
document.getElementById("status-seed").textContent = sharedState.config.seed;
document.getElementById("status-level").textContent = sharedState.config.level;
// Calculate effective maze size and chest count based on current level
const roundScale = Math.max(0, sharedState.config.level - 1);
@@ -39,8 +38,6 @@ function updateDisplay() {
const effectiveHeight = Math.max(9, sharedState.config.mazeHeight + roundScale * 2);
const effectiveChests = Math.max(1, sharedState.config.minChestDeadEnds + roundScale);
document.getElementById("status-seed").textContent = sharedState.config.seed;
document.getElementById("status-level").textContent = sharedState.config.level;
document.getElementById("status-maze-size").textContent = `${effectiveWidth}x${effectiveHeight}`;
document.getElementById("status-chests").textContent = effectiveChests;
document.getElementById("status-time").textContent = sharedState.runtime.elapsedSeconds.toFixed(1);
@@ -68,27 +65,6 @@ document.getElementById("btn-randomize").addEventListener("click", () => {
randomizeSeed();
});
document.getElementById("slider-width").addEventListener("input", (e) => {
primeSfx();
const value = parseInt(e.target.value) | 1;
sharedState.config.mazeWidth = value;
document.getElementById("value-width").textContent = value;
});
document.getElementById("slider-height").addEventListener("input", (e) => {
primeSfx();
const value = parseInt(e.target.value) | 1;
sharedState.config.mazeHeight = value;
document.getElementById("value-height").textContent = value;
});
document.getElementById("slider-deadends").addEventListener("input", (e) => {
primeSfx();
const value = parseInt(e.target.value);
sharedState.config.minChestDeadEnds = value;
document.getElementById("value-deadends").textContent = value;
});
// Update status display on game loop
setInterval(() => {
if (sharedState.runtime.runActive) {

View File

@@ -1,3 +0,0 @@
// Import Babylon.js game logic and HTML control panel
import "./babylon_setup.js";
import "./html_panel.js";