add back game over from chest

This commit is contained in:
pobadoba
2026-05-10 17:01:02 +09:00
parent 7e2d6243b2
commit 9b68630764
6 changed files with 103 additions and 30 deletions

View File

@@ -102,6 +102,48 @@ canvas {
font-weight: 600;
}
.game-over-overlay {
position: absolute;
inset: 0;
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
background: rgba(0, 0, 0, 0.78);
text-align: center;
padding: 20px;
}
.game-over-overlay[hidden] {
display: none;
}
.game-over-image {
width: min(70%, 460px);
max-height: 52vh;
object-fit: contain;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.22);
box-shadow: 0 14px 32px rgba(0, 0, 0, 0.45);
}
.game-over-text {
margin-top: 4px;
font-size: clamp(24px, 4vw, 40px);
font-weight: 700;
color: #f3f7ff;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.game-over-subtext {
font-size: 14px;
color: #c9d8ea;
letter-spacing: 0.05em;
}
.control-panel {
padding: 16px;
overflow-y: auto;

BIN
img/img_door.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
img/img_jobapplication.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

View File

@@ -19,6 +19,11 @@
<div class="canvas-hud-row"><span>Has key</span><strong id="canvas-key">no</strong></div>
<div class="canvas-hud-row"><span>Rounds</span><strong id="canvas-rounds">0</strong></div>
</div>
<div id="game-over-overlay" class="game-over-overlay" hidden>
<img id="game-over-image" alt="Game over" class="game-over-image" />
<div class="game-over-text">Game Over</div>
<div class="game-over-subtext">Press R to play again</div>
</div>
</div>
</section>

View File

@@ -6,6 +6,8 @@ import { playSfx, primeSfx } from "./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");
@@ -13,6 +15,11 @@ 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);
@@ -49,6 +56,7 @@ scene.activeCamera = camera;
let lastFootstepPosition = null;
let footstepAccumulator = 0;
let footstepElapsed = 0;
let gameOverActive = false;
scene.gravity = new BABYLON.Vector3(0, -0.2, 0);
scene.collisionsEnabled = true;
@@ -66,6 +74,34 @@ function updateOverviewCameraForMaze(w, h) {
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 = 0;
sharedState.runtime.message = "Run restarted.";
sharedState.config.level = 1;
hideGameOverScreen();
generateLevel();
}
function switchCameraMode() {
if (cameraMode === "fp") {
if (document.pointerLockElement === canvas && document.exitPointerLock) {
@@ -87,9 +123,13 @@ function switchCameraMode() {
}
window.addEventListener("keydown", (event) => {
if (event.code === "KeyW" || event.code === "KeyA" || event.code === "KeyS" || event.code === "KeyD" || event.code === "KeyV") {
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();
}
@@ -280,39 +320,21 @@ function placeExit(grid, seed) {
const exitWorld = gridCellToWorld(grid, x, y, cellSize);
const ex = exitWorld.x;
const ez = exitWorld.z;
const worldSpan = Math.max(grid[0].length, grid.length) * cellSize;
// Ground plane indicator (larger + slightly raised to avoid z-fighting)
const plane = BABYLON.MeshBuilder.CreateGround('exitZone', { width: cellSize*1.4, height: cellSize*1.4 }, scene);
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.diffuseColor = new BABYLON.Color3(0.25, 0.2, 0.03);
exitMat.emissiveColor = new BABYLON.Color3(1.0, 0.85, 0.15);
exitMat.disableLighting = true;
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, 0.08, ez);
plane.position = new BABYLON.Vector3(ex, cellSize * 0.92, ez);
plane.billboardMode = BABYLON.Mesh.BILLBOARDMODE_Y;
exitBox = plane;
levelMeshes.push(plane);
// Very tall beacon for visibility over maze walls
const beaconHeight = Math.max(18, worldSpan * 0.8);
const pillar = BABYLON.MeshBuilder.CreateCylinder('exitPillar', { diameter: cellSize*0.55, height: beaconHeight }, scene);
const pillarMat = new BABYLON.StandardMaterial('exitPillarMat', scene);
pillarMat.diffuseColor = new BABYLON.Color3(0.95, 0.8, 0.1);
pillarMat.emissiveColor = new BABYLON.Color3(1.0, 0.7, 0.1);
pillarMat.disableLighting = true;
pillar.material = pillarMat;
pillar.position = new BABYLON.Vector3(ex, beaconHeight * 0.5, ez);
levelMeshes.push(pillar);
// Bright orb at the top of beacon
const orb = BABYLON.MeshBuilder.CreateSphere('exitOrb', { diameter: cellSize * 1.0 }, scene);
const orbMat = new BABYLON.StandardMaterial('exitOrbMat', scene);
orbMat.diffuseColor = new BABYLON.Color3(1.0, 0.9, 0.3);
orbMat.emissiveColor = new BABYLON.Color3(1.0, 0.9, 0.35);
orbMat.disableLighting = true;
orb.material = orbMat;
orb.position = new BABYLON.Vector3(ex, beaconHeight + cellSize * 0.7, ez);
levelMeshes.push(orb);
}
function spawnCameraAt(grid) {
@@ -368,6 +390,7 @@ function spawnCameraAt(grid) {
}
function generateLevel() {
hideGameOverScreen();
const cfg = sharedState.config;
const seed = cfg.seed;
const w = Math.max(9, cfg.mazeWidth);
@@ -415,8 +438,10 @@ scene.onPointerObservable.add((pi) => {
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();

View File

@@ -3,6 +3,7 @@ const soundFiles = {
chestOpen: "/sfx/sfx_chest_open.wav",
click: "/sfx/sfx_click.wav",
key: "/sfx/sfx_key.wav",
lose: "/sfx/sfx_lose.wav",
step: "/sfx/sfx_step.wav",
win: "/sfx/sfx_win.wav",
};