diff --git a/README.md b/README.md index 4b66791..d0985bc 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,27 @@ # THREETRIS -POV Matrix tetris. Bricks fly at your face in bullet time — dodge them or catch them, -then throw them back through the hole in the oncoming wall, rotating them mid-flight -so their silhouette fits exactly. Flat tetrominoes + full 3D rotation means pitching a -piece changes its silhouette (an I-piece end-on is a 1×1 hole). +POV Matrix tetris. Bricks fly at your face in bullet time — catch the green ones, +**dodge the red agents** (they home in on you; touching them hurts), then throw +caught bricks back through the hole in the oncoming wall, rotating them mid-flight +so their silhouette fits exactly. Flat tetrominoes + full 3D rotation means pitching +a piece changes its silhouette (an I-piece end-on is a 1×1 hole). **Play:** open `index.html` over http (`python3 -m http.server`), any modern browser. -No build, no deps — single file, Three.js from CDN. +No build step — single file, Three.js from CDN. - **Mouse** — move your head (dodge / reach) -- **Click** — catch an incoming brick when it's close, or throw the held brick +- **Click** — catch an incoming green brick, or throw the held brick - **W/S · A/D · Q/E** — pitch / yaw / roll 90°, works while held and mid-flight -- Catch +25, fit +100, 3 lives. Speed ramps per fit. +- Catch +25 · fit +100 × combo · dodge an agent +10 · 3 lives. Speed and agent + frequency ramp per fit; consecutive fits build the combo multiplier. -Cheats/debug in console: `_g` (state), `_auto()` (rotate held brick to fit), `_m(x,y)` (move head). +## Assets (all generated free & local on MODELBEAST) + +Everything in `assets/` came off the local render farm — FLUX (logo, circuit-board +brick texture, city backdrop, sentinel concept art), RMBG background removal + +Hunyuan3D image→GLB (the sentinel flyby), Kokoro TTS (operator voice lines), +ACE-Step (music loop). Procedural Web Audio covers the small SFX. The game degrades +gracefully if `assets/` is missing — flat colors, no music, same gameplay. + +Cheats/debug in console: `_g` (state), `_auto()` (rotate held brick to fit), +`_m(x,y)` (move head), `_start()` (skip title). diff --git a/assets/circuit.png b/assets/circuit.png new file mode 100644 index 0000000..6f6c2ab Binary files /dev/null and b/assets/circuit.png differ diff --git a/assets/city.png b/assets/city.png new file mode 100644 index 0000000..366888c Binary files /dev/null and b/assets/city.png differ diff --git a/assets/failure.m4a b/assets/failure.m4a new file mode 100644 index 0000000..5ab2ee3 Binary files /dev/null and b/assets/failure.m4a differ diff --git a/assets/incoming.m4a b/assets/incoming.m4a new file mode 100644 index 0000000..6a69cd1 Binary files /dev/null and b/assets/incoming.m4a differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..8ded9e1 Binary files /dev/null and b/assets/logo.png differ diff --git a/assets/sentinel.glb b/assets/sentinel.glb new file mode 100644 index 0000000..eb52cf2 Binary files /dev/null and b/assets/sentinel.glb differ diff --git a/assets/wake.m4a b/assets/wake.m4a new file mode 100644 index 0000000..640355a Binary files /dev/null and b/assets/wake.m4a differ diff --git a/index.html b/index.html index d2552ec..7f8e0a6 100644 --- a/index.html +++ b/index.html @@ -8,45 +8,110 @@ html, body { margin: 0; height: 100%; overflow: hidden; background: #000; cursor: crosshair; } #hud { position: fixed; inset: 0; pointer-events: none; font-family: "Courier New", monospace; color: #00ff41; text-shadow: 0 0 8px #00ff41; } #score { position: absolute; top: 16px; left: 20px; font-size: 22px; } + #level { position: absolute; top: 44px; left: 20px; font-size: 14px; opacity: .7; } #health { position: absolute; top: 16px; right: 20px; font-size: 22px; letter-spacing: 4px; } #msg { position: absolute; top: 38%; width: 100%; text-align: center; font-size: 34px; letter-spacing: 6px; opacity: 0; transition: opacity .15s; } + #msg.red { color: #ff3344; text-shadow: 0 0 8px #ff3344; } #keys { position: absolute; bottom: 14px; width: 100%; text-align: center; font-size: 13px; opacity: .6; } #flash { position: fixed; inset: 0; background: radial-gradient(ellipse at center, transparent 40%, rgba(255,0,60,.55)); opacity: 0; pointer-events: none; transition: opacity .1s; } - #over { position: fixed; inset: 0; display: none; place-items: center; text-align: center; font-family: "Courier New", monospace; color: #00ff41; text-shadow: 0 0 12px #00ff41; background: rgba(0,0,0,.7); } - #over h1 { font-size: 52px; letter-spacing: 12px; margin: 0 0 12px; } + .screen { position: fixed; inset: 0; display: grid; place-items: center; text-align: center; font-family: "Courier New", monospace; color: #00ff41; text-shadow: 0 0 12px #00ff41; background: rgba(0,0,0,.75); } + .screen h1 { font-size: 52px; letter-spacing: 12px; margin: 0 0 12px; } + #title img { max-width: min(640px, 80vw); mix-blend-mode: screen; } + #over { display: none; } + .blink { animation: blink 1.2s step-end infinite; } + @keyframes blink { 50% { opacity: .25; } }
+