From b88f47c70dfd7ade28a4167d2331a0679ba5fea5 Mon Sep 17 00:00:00 2001 From: pobadoba Date: Sun, 10 May 2026 17:43:03 +0900 Subject: [PATCH] feat: game over screen --- css/style.css | 58 ++++++++ index.html | 14 +- package-lock.json | 335 +++++++++++++++++++++++++++++++++++++++++-- package.json | 3 +- src/babylon_panel.js | 28 ++-- src/html_panel.js | 86 +++++++---- src/p5_particles.js | 120 ++++++++++++++++ 7 files changed, 593 insertions(+), 51 deletions(-) create mode 100644 src/p5_particles.js diff --git a/css/style.css b/css/style.css index f2663fa..428be20 100644 --- a/css/style.css +++ b/css/style.css @@ -136,6 +136,64 @@ canvas { display: none; } +.p5-particles-container { + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; +} + +.p5-game-over-panel { + position: fixed; + inset: 0; + width: 100vw; + height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 20px; + background: rgba(0, 0, 0, 0.95); + z-index: 9999; +} + +.p5-game-over-panel[hidden] { + display: none; + pointer-events: none; +} + +.p5-sketch-container { + position: relative; + width: 100%; + height: 100%; + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} + +.game-over-info { + position: relative; + z-index: 10; + text-align: center; + padding: 20px; +} + +.game-over-info .game-over-text { + margin-top: 0; + font-size: clamp(24px, 4vw, 40px); + font-weight: 700; + color: #f3f7ff; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.game-over-info .game-over-subtext { + font-size: 14px; + color: #c9d8ea; + letter-spacing: 0.05em; +} + .game-over-image { width: min(70%, 460px); max-height: 52vh; diff --git a/index.html b/index.html index c31d9a4..4ae24df 100644 --- a/index.html +++ b/index.html @@ -19,11 +19,15 @@
Has keyno
Rounds0
- + + + + diff --git a/package-lock.json b/package-lock.json index 28934f5..092322b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,36 @@ { - "name": "p5js-vite", + "name": "untitled-maze-game", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "p5js-vite", + "name": "untitled-maze-game", "version": "0.1.0", "dependencies": { "babylonjs": "^9.5.1", + "p5": "^2.2.3", "p5js-wrapper": "^1.2.3" }, "devDependencies": { "vite": "^8.0.10" } }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@davepagurek/bezier-path": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@davepagurek/bezier-path/-/bezier-path-0.0.7.tgz", + "integrity": "sha512-CVlnCOrV1iy4Z12T756i9l4G6kF7r8uhlnb+xqDemAMmWQB+8Q0b+8VEqIiUfywgZDSiDr18Rm7pZlnA69rE8Q==", + "license": "MIT" + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -49,6 +65,12 @@ "tslib": "^2.4.0" } }, + "node_modules/@japont/unicode-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@japont/unicode-range/-/unicode-range-1.0.0.tgz", + "integrity": "sha512-BckHvA2XdjRBVAWe2uceNuRf78lBeI28kyWEbfr/Q2pE17POkwuZ6WWY/UMv8FL9iBxhW4xfDoNLM9UVZaTeUQ==", + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", @@ -371,6 +393,30 @@ "tslib": "^2.4.0" } }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/babylonjs": { "version": "9.5.1", "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-9.5.1.tgz", @@ -378,6 +424,16 @@ "hasInstallScript": true, "license": "Apache-2.0" }, + "node_modules/colorjs.io": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.6.1.tgz", + "integrity": "sha512-8lyR2wHzuIykCpqHKgluGsqQi5iDm3/a2IgP2GBZrasn2sBRkE4NOGsglZxWLs/jZQoNkmA/KM/8NV16rLUdBg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/color" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -388,6 +444,58 @@ "node": ">=8" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -421,6 +529,36 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/gifenc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gifenc/-/gifenc-1.0.3.tgz", + "integrity": "sha512-xdr6AdrfGBcfzncONUOlXMBuc5wJDtOueE3c5rdG0oNgtINLD+f2iFZltrBRZYzACRbKr+mSVU/x98zv2u3jmw==", + "license": "MIT" + }, + "node_modules/i18next": { + "version": "19.9.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", + "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-4.3.1.tgz", + "integrity": "sha512-KIToAzf8zwWvacgnRwJp63ase26o24AuNUlfNVJ5YZAFmdGhsJpmFClxXPuk9rv1FMI4lnc8zLSqgZPEZMrW4g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5" + } + }, + "node_modules/libtess": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/libtess/-/libtess-1.2.2.tgz", + "integrity": "sha512-Nps8HPeVVcsmJxUvFLKVJcCgcz+1ajPTXDVAVPs6+giOQP4AHV31uZFFkh+CKow/bkB7GbZWKmwmit7myaqDSw==", + "license": "SGI-B-2.0" + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -713,10 +851,32 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "license": "MIT" + }, "node_modules/p5": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/p5/-/p5-1.4.1.tgz", - "integrity": "sha512-3/X+qb0bK2Cg8nuZNpZZvzxkeUSRghOf0S+l8c+U8yIkUTVSbbcV0R8y96rx3InVBVhk8cH9kFC93VlZZElqSw==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/p5/-/p5-2.2.3.tgz", + "integrity": "sha512-jz9uy0k3Fcj9vKSOafQlIrpaPZZjO4rAEBZF6dGkbokisshP0M3aFm4qtLHYCoEW1XJSkFaVaOMILCQAQxUHHA==", + "license": "LGPL-2.1", + "dependencies": { + "@davepagurek/bezier-path": "^0.0.7", + "@japont/unicode-range": "^1.0.0", + "acorn": "^8.15.0", + "acorn-walk": "^8.3.4", + "colorjs.io": "^0.6.0", + "escodegen": "^2.1.0", + "gifenc": "^1.0.3", + "i18next": "^19.0.2", + "i18next-browser-languagedetector": "^4.0.1", + "libtess": "^1.2.2", + "omggif": "^1.0.10", + "pako": "^2.1.0", + "zod": "^4.2.1" + } }, "node_modules/p5js-wrapper": { "version": "1.2.3", @@ -726,6 +886,18 @@ "p5": "^1.4.1" } }, + "node_modules/p5js-wrapper/node_modules/p5": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/p5/-/p5-1.11.13.tgz", + "integrity": "sha512-gfGo4AkyuNMs6Ko7UNFM9K2edqFRGyLrFaYUB+XXF127JVdEPu0BIaC5uDDNJpsRMOD9hJMUpsOH4HkfuNhvhA==", + "license": "LGPL-2.1" + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -809,6 +981,16 @@ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -921,9 +1103,28 @@ "optional": true } } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { + "@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==" + }, + "@davepagurek/bezier-path": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@davepagurek/bezier-path/-/bezier-path-0.0.7.tgz", + "integrity": "sha512-CVlnCOrV1iy4Z12T756i9l4G6kF7r8uhlnb+xqDemAMmWQB+8Q0b+8VEqIiUfywgZDSiDr18Rm7pZlnA69rE8Q==" + }, "@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -955,6 +1156,11 @@ "tslib": "^2.4.0" } }, + "@japont/unicode-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@japont/unicode-range/-/unicode-range-1.0.0.tgz", + "integrity": "sha512-BckHvA2XdjRBVAWe2uceNuRf78lBeI28kyWEbfr/Q2pE17POkwuZ6WWY/UMv8FL9iBxhW4xfDoNLM9UVZaTeUQ==" + }, "@napi-rs/wasm-runtime": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", @@ -1097,17 +1303,61 @@ "tslib": "^2.4.0" } }, + "acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==" + }, + "acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "requires": { + "acorn": "^8.11.0" + } + }, "babylonjs": { "version": "9.5.1", "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-9.5.1.tgz", "integrity": "sha512-EXROfSz1k+Mg88Qh/SrElEz1zp5QVFG4H+2OyCZ+uEXe4fFeY667N9kjMx2+6Y2XFCxKxf4q+za6k+B1x1qbGg==" }, + "colorjs.io": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.6.1.tgz", + "integrity": "sha512-8lyR2wHzuIykCpqHKgluGsqQi5iDm3/a2IgP2GBZrasn2sBRkE4NOGsglZxWLs/jZQoNkmA/KM/8NV16rLUdBg==" + }, "detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, "fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1122,6 +1372,32 @@ "dev": true, "optional": true }, + "gifenc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gifenc/-/gifenc-1.0.3.tgz", + "integrity": "sha512-xdr6AdrfGBcfzncONUOlXMBuc5wJDtOueE3c5rdG0oNgtINLD+f2iFZltrBRZYzACRbKr+mSVU/x98zv2u3jmw==" + }, + "i18next": { + "version": "19.9.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", + "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", + "requires": { + "@babel/runtime": "^7.12.0" + } + }, + "i18next-browser-languagedetector": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-4.3.1.tgz", + "integrity": "sha512-KIToAzf8zwWvacgnRwJp63ase26o24AuNUlfNVJ5YZAFmdGhsJpmFClxXPuk9rv1FMI4lnc8zLSqgZPEZMrW4g==", + "requires": { + "@babel/runtime": "^7.5.5" + } + }, + "libtess": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/libtess/-/libtess-1.2.2.tgz", + "integrity": "sha512-Nps8HPeVVcsmJxUvFLKVJcCgcz+1ajPTXDVAVPs6+giOQP4AHV31uZFFkh+CKow/bkB7GbZWKmwmit7myaqDSw==" + }, "lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -1225,10 +1501,30 @@ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true }, + "omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==" + }, "p5": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/p5/-/p5-1.4.1.tgz", - "integrity": "sha512-3/X+qb0bK2Cg8nuZNpZZvzxkeUSRghOf0S+l8c+U8yIkUTVSbbcV0R8y96rx3InVBVhk8cH9kFC93VlZZElqSw==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/p5/-/p5-2.2.3.tgz", + "integrity": "sha512-jz9uy0k3Fcj9vKSOafQlIrpaPZZjO4rAEBZF6dGkbokisshP0M3aFm4qtLHYCoEW1XJSkFaVaOMILCQAQxUHHA==", + "requires": { + "@davepagurek/bezier-path": "^0.0.7", + "@japont/unicode-range": "^1.0.0", + "acorn": "^8.15.0", + "acorn-walk": "^8.3.4", + "colorjs.io": "^0.6.0", + "escodegen": "^2.1.0", + "gifenc": "^1.0.3", + "i18next": "^19.0.2", + "i18next-browser-languagedetector": "^4.0.1", + "libtess": "^1.2.2", + "omggif": "^1.0.10", + "pako": "^2.1.0", + "zod": "^4.2.1" + } }, "p5js-wrapper": { "version": "1.2.3", @@ -1236,8 +1532,20 @@ "integrity": "sha512-nG4xiyydY7v+q0+/6wbE2gj+L7RPf3FDwM68govCcuyrBckXgrfDy+0SKCTNTGwbpypxjVAu5juU4XOlz4IRPw==", "requires": { "p5": "^1.4.1" + }, + "dependencies": { + "p5": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/p5/-/p5-1.11.13.tgz", + "integrity": "sha512-gfGo4AkyuNMs6Ko7UNFM9K2edqFRGyLrFaYUB+XXF127JVdEPu0BIaC5uDDNJpsRMOD9hJMUpsOH4HkfuNhvhA==" + } } }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1286,6 +1594,12 @@ "@rolldown/pluginutils": "1.0.0-rc.17" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, "source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1322,6 +1636,11 @@ "rolldown": "1.0.0-rc.17", "tinyglobby": "^0.2.16" } + }, + "zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==" } } } diff --git a/package.json b/package.json index 88f0a62..7b42e28 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "p5js-vite", + "name": "untitled-maze-game", "version": "0.1.0", "scripts": { "dev": "vite", @@ -11,6 +11,7 @@ }, "dependencies": { "babylonjs": "^9.5.1", + "p5": "^2.2.3", "p5js-wrapper": "^1.2.3" } } diff --git a/src/babylon_panel.js b/src/babylon_panel.js index e983f11..a39ad73 100644 --- a/src/babylon_panel.js +++ b/src/babylon_panel.js @@ -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(); } } diff --git a/src/html_panel.js b/src/html_panel.js index 07e1e39..691d30d 100644 --- a/src/html_panel.js +++ b/src/html_panel.js @@ -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); diff --git a/src/p5_particles.js b/src/p5_particles.js new file mode 100644 index 0000000..ead932a --- /dev/null +++ b/src/p5_particles.js @@ -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; + } +}