add back game over from chest
This commit is contained in:
@@ -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
BIN
img/img_door.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
BIN
img/img_jobapplication.png
Normal file
BIN
img/img_jobapplication.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
@@ -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>
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user