feat: game over screen
This commit is contained in:
@@ -3,6 +3,7 @@ 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 { startParticleSketch, stopParticleSketch } from "./p5_particles.js";
|
||||
import chestTextureUrl from "../img/img_chest.png";
|
||||
import wallTextureUrl from "../img/img_wall.png";
|
||||
import groundTextureUrl from "../img/img_ground.png";
|
||||
@@ -15,11 +16,7 @@ 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 p5GameOverPanel = document.getElementById("p5-game-over-panel");
|
||||
|
||||
const scene = new BABYLON.Scene(engine);
|
||||
scene.clearColor = new BABYLON.Color4(0.05, 0.07, 0.1, 1);
|
||||
@@ -77,8 +74,16 @@ function updateOverviewCameraForMaze(w, h) {
|
||||
|
||||
function showGameOverScreen() {
|
||||
gameOverActive = true;
|
||||
if (gameOverOverlay) {
|
||||
gameOverOverlay.hidden = false;
|
||||
const canvasStage = document.querySelector(".canvas-stage");
|
||||
if (canvasStage) {
|
||||
canvasStage.hidden = true;
|
||||
}
|
||||
if (p5GameOverPanel) {
|
||||
p5GameOverPanel.hidden = false;
|
||||
const sketchContainer = document.getElementById("p5-sketch-container");
|
||||
if (sketchContainer) {
|
||||
startParticleSketch(sketchContainer);
|
||||
}
|
||||
}
|
||||
if (document.pointerLockElement === canvas && document.exitPointerLock) {
|
||||
document.exitPointerLock();
|
||||
@@ -87,8 +92,13 @@ function showGameOverScreen() {
|
||||
|
||||
function hideGameOverScreen() {
|
||||
gameOverActive = false;
|
||||
if (gameOverOverlay) {
|
||||
gameOverOverlay.hidden = true;
|
||||
const canvasStage = document.querySelector(".canvas-stage");
|
||||
if (canvasStage) {
|
||||
canvasStage.hidden = false;
|
||||
}
|
||||
if (p5GameOverPanel) {
|
||||
p5GameOverPanel.hidden = true;
|
||||
stopParticleSketch();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,22 @@ function randomizeSeed() {
|
||||
|
||||
// Update display from shared state
|
||||
function updateDisplay() {
|
||||
document.getElementById("status-seed").textContent = sharedState.config.seed;
|
||||
document.getElementById("status-level").textContent = sharedState.config.level;
|
||||
const statusSeed = document.getElementById("status-seed");
|
||||
const statusLevel = document.getElementById("status-level");
|
||||
const statusMazeSize = document.getElementById("status-maze-size");
|
||||
const statusChests = document.getElementById("status-chests");
|
||||
const statusTime = document.getElementById("status-time");
|
||||
const statusKey = document.getElementById("status-key");
|
||||
const statusRounds = document.getElementById("status-rounds");
|
||||
const statusMessage = document.getElementById("status-message");
|
||||
|
||||
if (!statusSeed || !statusLevel || !statusMazeSize || !statusChests || !statusTime || !statusKey || !statusRounds || !statusMessage) {
|
||||
console.warn("Some status display elements are missing from DOM");
|
||||
return;
|
||||
}
|
||||
|
||||
statusSeed.textContent = sharedState.config.seed;
|
||||
statusLevel.textContent = sharedState.config.level;
|
||||
|
||||
// Calculate effective maze size and chest count based on current level
|
||||
const roundScale = Math.max(0, sharedState.config.level - 1);
|
||||
@@ -38,32 +52,42 @@ function updateDisplay() {
|
||||
const effectiveHeight = Math.max(9, sharedState.config.mazeHeight + roundScale * 2);
|
||||
const effectiveChests = Math.max(1, sharedState.config.minChestDeadEnds + roundScale);
|
||||
|
||||
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);
|
||||
document.getElementById("status-key").textContent = sharedState.runtime.hasKey ? "yes" : "no";
|
||||
document.getElementById("status-rounds").textContent = sharedState.runtime.roundsCompleted;
|
||||
document.getElementById("status-message").textContent = sharedState.runtime.message;
|
||||
statusMazeSize.textContent = `${effectiveWidth}x${effectiveHeight}`;
|
||||
statusChests.textContent = effectiveChests;
|
||||
statusTime.textContent = sharedState.runtime.elapsedSeconds.toFixed(1);
|
||||
statusKey.textContent = sharedState.runtime.hasKey ? "yes" : "no";
|
||||
statusRounds.textContent = sharedState.runtime.roundsCompleted;
|
||||
statusMessage.textContent = sharedState.runtime.message;
|
||||
}
|
||||
|
||||
// Initialize event listeners
|
||||
document.getElementById("btn-start").addEventListener("click", () => {
|
||||
primeSfx();
|
||||
playSfx("click", 0.7);
|
||||
resetRun("Run started.");
|
||||
});
|
||||
const btnStart = document.getElementById("btn-start");
|
||||
const btnRestart = document.getElementById("btn-restart");
|
||||
const btnRandomize = document.getElementById("btn-randomize");
|
||||
|
||||
document.getElementById("btn-restart").addEventListener("click", () => {
|
||||
primeSfx();
|
||||
playSfx("click", 0.7);
|
||||
restartLevel("Level restarted.");
|
||||
});
|
||||
if (btnStart) {
|
||||
btnStart.addEventListener("click", () => {
|
||||
primeSfx();
|
||||
playSfx("click", 0.7);
|
||||
resetRun("Run started.");
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("btn-randomize").addEventListener("click", () => {
|
||||
primeSfx();
|
||||
playSfx("click", 0.7);
|
||||
randomizeSeed();
|
||||
});
|
||||
if (btnRestart) {
|
||||
btnRestart.addEventListener("click", () => {
|
||||
primeSfx();
|
||||
playSfx("click", 0.7);
|
||||
restartLevel("Level restarted.");
|
||||
});
|
||||
}
|
||||
|
||||
if (btnRandomize) {
|
||||
btnRandomize.addEventListener("click", () => {
|
||||
primeSfx();
|
||||
playSfx("click", 0.7);
|
||||
randomizeSeed();
|
||||
});
|
||||
}
|
||||
|
||||
// Update status display on game loop
|
||||
setInterval(() => {
|
||||
@@ -73,11 +97,17 @@ setInterval(() => {
|
||||
const effectiveHeight = Math.max(9, sharedState.config.mazeHeight + roundScale * 2);
|
||||
const effectiveChests = Math.max(1, sharedState.config.minChestDeadEnds + roundScale);
|
||||
|
||||
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);
|
||||
document.getElementById("status-key").textContent = sharedState.runtime.hasKey ? "yes" : "no";
|
||||
document.getElementById("status-rounds").textContent = sharedState.runtime.roundsCompleted;
|
||||
const statusMazeSize = document.getElementById("status-maze-size");
|
||||
const statusChests = document.getElementById("status-chests");
|
||||
const statusTime = document.getElementById("status-time");
|
||||
const statusKey = document.getElementById("status-key");
|
||||
const statusRounds = document.getElementById("status-rounds");
|
||||
|
||||
if (statusMazeSize) statusMazeSize.textContent = `${effectiveWidth}x${effectiveHeight}`;
|
||||
if (statusChests) statusChests.textContent = effectiveChests;
|
||||
if (statusTime) statusTime.textContent = sharedState.runtime.elapsedSeconds.toFixed(1);
|
||||
if (statusKey) statusKey.textContent = sharedState.runtime.hasKey ? "yes" : "no";
|
||||
if (statusRounds) statusRounds.textContent = sharedState.runtime.roundsCompleted;
|
||||
}
|
||||
}, 100);
|
||||
|
||||
|
||||
120
src/p5_particles.js
Normal file
120
src/p5_particles.js
Normal file
@@ -0,0 +1,120 @@
|
||||
import p5 from "p5";
|
||||
import gameOverImageUrl from "../img/img_jobapplication.png";
|
||||
|
||||
let sketch;
|
||||
|
||||
export function startParticleSketch(containerElement) {
|
||||
if (sketch) {
|
||||
sketch.remove();
|
||||
}
|
||||
|
||||
sketch = new p5((p) => {
|
||||
let particles = [];
|
||||
let gameOverImg;
|
||||
const imgSize = 50;
|
||||
const particleCount = 15;
|
||||
|
||||
p.setup = async function() {
|
||||
const width = containerElement.clientWidth || 800;
|
||||
const height = containerElement.clientHeight || 600;
|
||||
console.log("p5 setup:", { width, height });
|
||||
|
||||
const canv = p.createCanvas(width, height);
|
||||
canv.parent(containerElement);
|
||||
|
||||
// Load image asynchronously
|
||||
try {
|
||||
gameOverImg = await p.loadImage(gameOverImageUrl);
|
||||
console.log("Game over image loaded:", gameOverImg.width, "x", gameOverImg.height);
|
||||
} catch (err) {
|
||||
console.error("Failed to load particle image:", err);
|
||||
}
|
||||
|
||||
// Initialize particles with random starting positions
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
particles.push({
|
||||
x: p.random(width),
|
||||
y: p.random(height),
|
||||
vx: p.random(-2, 2),
|
||||
vy: p.random(-2, 2),
|
||||
life: 200,
|
||||
rotation: p.random(p.TWO_PI),
|
||||
rotationSpeed: p.random(-0.08, 0.08),
|
||||
scale: p.random(0.7, 1.3),
|
||||
});
|
||||
}
|
||||
console.log("Particles initialized:", particles.length);
|
||||
};
|
||||
|
||||
p.draw = function() {
|
||||
// Draw background image full screen
|
||||
if (gameOverImg) {
|
||||
p.imageMode(p.CORNER);
|
||||
p.image(gameOverImg, 0, 0, p.width, p.height);
|
||||
}
|
||||
|
||||
// Semi-transparent overlay for visibility
|
||||
p.background(0, 0, 0, 20);
|
||||
|
||||
for (let i = 0; i < particles.length; i++) {
|
||||
const part = particles[i];
|
||||
|
||||
// Update position
|
||||
part.x += part.vx;
|
||||
part.y += part.vy;
|
||||
part.rotation += part.rotationSpeed;
|
||||
|
||||
// Bounce off walls with friction
|
||||
if (part.x < 0 || part.x > p.width) {
|
||||
part.vx *= -0.8;
|
||||
part.x = p.constrain(part.x, 0, p.width);
|
||||
}
|
||||
if (part.y < 0 || part.y > p.height) {
|
||||
part.vy *= -0.8;
|
||||
part.y = p.constrain(part.y, 0, p.height);
|
||||
}
|
||||
|
||||
// Add some gravity
|
||||
part.vy += 0.1;
|
||||
part.vy = p.constrain(part.vy, -5, 5);
|
||||
|
||||
// Randomly change direction slightly
|
||||
if (p.random() < 0.03) {
|
||||
part.vx += p.random(-0.5, 0.5);
|
||||
part.vy += p.random(-0.5, 0.5);
|
||||
part.vx = p.constrain(part.vx, -3, 3);
|
||||
part.vy = p.constrain(part.vy, -3, 3);
|
||||
}
|
||||
|
||||
// Draw particle
|
||||
p.push();
|
||||
p.translate(part.x, part.y);
|
||||
p.rotate(part.rotation);
|
||||
p.scale(part.scale);
|
||||
if (gameOverImg) {
|
||||
p.imageMode(p.CENTER);
|
||||
p.tint(255, 200);
|
||||
p.image(gameOverImg, 0, 0, imgSize, imgSize);
|
||||
}
|
||||
p.pop();
|
||||
}
|
||||
};
|
||||
|
||||
p.windowResized = function() {
|
||||
if (containerElement && containerElement.offsetParent !== null) {
|
||||
const width = containerElement.clientWidth;
|
||||
const height = containerElement.clientHeight;
|
||||
if (width > 0 && height > 0) {
|
||||
p.resizeCanvas(width, height);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function stopParticleSketch() {
|
||||
if (sketch) {
|
||||
sketch.remove();
|
||||
sketch = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user