# Jeopardy! 3D Studio

A browser recreation using recovered assets from the PS3 game NPUA80227. This is not a completed 1:1 port of the retail executable.

## Playing

- Choose one to three occupied podiums, each local or CPU, with at least one local player.
- Click or tap a dollar amount on the actual 3D board. Arrow keys and Enter also select clues.
- Dollar values brighten subtly on hover. Read the full-screen clue; buzzers open automatically after reading. Buzz with the on-screen button, Space, or player keys 1/2/3. Category and clue stay near the top above the podiums while choosing one of four original responses, or pressing A/B/C/D. The clue text hides after submission.
- CPU players highlight successive choices and pause on their selected response. Results show the selected response over the podiums until Continue is pressed. Studio Options can enable a saved three-second auto-continue timer. Both paths cut to the board for one second before returning control or reopening remaining buzzers.
- CPU knowledge, confidence, planned responses and passes follow the recovered PS3 routines. Each opponent has a persistent personal bias and different knowledge for each category; harder clue rows reduce confidence. Opponents do not always buzz, and saved games/rebounds preserve their original plans. Buzzer and response delays deliberately remain slower than retail.
- Daily Double shows its category, the selecting player's score, and the other occupied podiums' scores. Wagers start at $1; True Daily Double locks the player's entire positive score in one click (disabled at $0 or below).
- Scores and names appear on the physical podiums, not duplicate HUD cards.
- Jeopardy and Double Jeopardy introduce all six categories in order using the recovered GUI slide/fade tracks, round graphics, and a normalized category-reveal effect. Selection stays locked until the sequence ends; Enter or Skip Categories bypasses it. Dollar values use Swiss 911 and smaller separate dollar signs; category names use Swiss 921, matching the original Flash fields.
- Active podium timer lights extinguish inward in five steps using the recovered light artwork and atlas positions. The on-screen response countdown shares the same clock and adds a paired red-light strip. Both pause in Options and clear after a response or timeout.
- Automatic cameras use recovered animated shots. Sound and music toggles are on the main game view. Studio Options exposes all cameras, saved-game resume, and a clue reveal that forfeits additional points.
- Reflections are enabled by default and can be switched off in Studio Options. Each of the three floor levels has its own live planar capture, blended into the original opaque floor material using its gloss map. Reflective props use a static environment capture of the actual studio; emissive artwork and screens are excluded.
- Complete Jeopardy and Double Jeopardy boards, then wager in Final Jeopardy. Local Final wagers and responses are collected sequentially; pass the device privately.

## Recovered Sources

- `data/models/sets/stage5/stage5.nif.soe`: 157 meshes, 80 embedded texture images, 31 cameras, and 22 animated camera tracks. Native Y-up coordinates and parent transforms are retained. Tracks account for controller frequency, quaternion rotation, and quadratic translation keys.
- `data/game/content.txt.soe`: 533 categories and 2,501 clues, including 41 single-clue Final categories. Questions, response prefixes, correct responses, and distractors are from this catalog.
- `data/swf/{tileboard,cluecard,podiums,gui,gameshell}`: exported fonts, images, ActionScript reference, and vector artwork. The raised answer-tile border is original `tileboard.gfx` shape 10, not a CSS approximation. Korinna, Swiss 911/921, and handwritten podium names use the recovered fonts.
- `data_ps3/audio/*.fsb`: 33 sound/music assets, normalized with FFmpeg `loudnorm` to -20 LUFS and -3 dBTP. Browser playback shares Wheel's limiter and ducking infrastructure.
- Category text reveal uses `categorybeep`, not the separate `CategoryReveal` or `CategoryRevealAlt1` events. In the decrypted NPUA80227 ELF, callback registration at `0x4b36c`/`0x4b370` pairs `postCategoryTextReveal` with function descriptor `0xd43590`, which resolves to handler `0x44078` (TOC `0xda3428`). Both handler branches load `categorybeep` at `0x440b0`/`0x440f0` and dispatch through virtual slot `0x1cc`. `jeopardy.fev` maps that event to `categorybeep.wav`, OneShot sample 5. The recovered Flash callback occurs when category text starts fading in; the browser fires the cue at the same point.
- Native CPU logic is transcribed in `cpu.js`: player bias from `0x3f1d0`, difficulty/category knowledge from `0x3f108`, and clue plans from `0x3f390`. Bias is `U * .5 - .25`; category knowledge is clamped to 0-1 from `.5 * (U + bias)` on Easy, `.15 + .6 * (U + bias)` on Medium, and `.3 + .7 * (U + bias)` on Hard. Confidence interpolates by row from `.6 + knowledge * .29999995` to `.15 + knowledge * .6`. Daily Double and Final use the latter confidence and a `.5` reflex without passing. Two shared random draws determine both answer correctness and whether the CPU attempts; normal clues pass when their average reaches confidence. Positive reflex starts at `1 - 2 * (confidence - average)`, with values below `.2` replaced by `.1 + U * .1`. Medium then squares and adds `.12`; Hard cubes and adds `.07` (default fields initialized at `0x38058`-`0x3806c`). Correct source response is index 0; wrong responses use upper-exclusive `RandInt(1,3)` (`0x75250`), i.e. index 1 or 2, before presentation shuffle. Discarded random draws are retained locally, but browser randomness and floating-point arithmetic are not a bit-exact PS3 RNG/PPU emulator.
- The full-screen clue background is the user-supplied `blank_jeopardy_screen_v2_by_drewmandew_dfj4q3r.png`, preserved as `assets/presentation/cluecard/user-clue-background.png`. It is not an extracted PS3 asset. The stage-backed response presentation and brief result cuts are also intentional user-requested designs, not proven retail timeline behavior.

Original retail files remain untouched. Only converted browser assets are published; no executable, decryption keys, bulk SWF/XML exports, or player-body assets are shipped. Asset ownership remains with the original rights holders.

## Remaining Parity Gaps

- Rules are a browser implementation, not execution of the original native game code. CPU clue decisions use recovered formulas, but CPU board selection, wagers, navigation choreography and native state-machine timing are not fully replicated. Daily Double placement uses the user's supplied TV heatmaps as per-cell weights, normalized for rounding: one weighted clue in Jeopardy and two weighted distinct clues in Double Jeopardy (without replacement). The charts do not specify a joint two-clue distribution; this is not a recovered PS3 placement routine. Existing saved boards retain their original placements; old saves without CPU profiles migrate once on resume.
- Clue reading time remains words / 6 plus a half-second settling pause, with a 1.5-second minimum. Longer clues keep both human and CPU buzzers locked proportionally longer; this also precedes Daily Double and Final responses. After buzzers open, attempting CPUs wait 6-8 seconds on Easy, 5-7 on Medium, or 4-6 on Hard, including rebounds; their recovered reflex controls placement inside that slower window. CPU answer deliberation/highlighting remains slower and the normal answer clock stays at ten seconds, per the user's preference. Native `0x21af8` estimates reading as space-delimited word count / 3, and `0x1d758` adds five times reflex for CPU wait. The scheduling caller `0x20f38` subtracts one second or that reading estimate depending on clue byte `+0xb1`; that flag and clock origin are not fully traced, so the expression is not claimed as an exact delay from our buzzer-open event. Flash `cluecard.gfx` defaults to a ten-second clue/buzz window, five-second response timer, and thirty-second Final timer. Its podium message separately requests ten seconds; this mismatch is not silently treated as proven synchronized retail behavior. The browser intentionally retains its shared response/podium clock. Full retail runtime timing remains unverified.
- Camera paths are recovered, but mapping each shot to gameplay events has not been proven against the native state machine.
- Gamebryo light linking, complete ambient-animation state changes, and exact retail shader composition are not fully reproduced. Original dark-map UV channels are preserved; the web lighting rig is approximate. The new planar-floor and environment-probe effects are browser approximations, not recovered native reflection shader code. Visible floor captures update with the camera on every rendered frame; prop environments are static.
- Category slides/fades are extracted from `gui.gfx` sprites 15/16 at 30 Hz, driven by the original callbacks, and use original background/border artwork. The reveal effect is mapped from the native callback; host category speech remains unproven. Other Flash choreography, host delivery, all result screens, and original simultaneous Final multiplayer timing remain incomplete. Player bodies and character creation are deliberately omitted. The on-screen red timer strip is a browser presentation of the recovered podium countdown, not an extracted GUI strip.
- Audio files are normalized and browser-decoded, but every source sound's event mapping has not been validated by listening against retail gameplay.

## Rebuilding and Checking

Run `tools/build_assets.py` with Python that can import Pillow, NumPy, and the workspace NIF dependencies. It expects the original dump and converter in the parent `JEOPARDY` workspace, Java 17, the local FFDec JAR, and FFmpeg on PATH. It writes source investigation exports under ignored `qa/source/`. The supplied clue background must remain available separately; extraction does not overwrite it.

From the site repository:

```powershell
node --test jeopardy/game.test.mjs
python -m http.server 8092 --bind 127.0.0.1
node jeopardy/tools/browser_qa.mjs
```

The browser harness uses this machine's bundled Playwright and Chrome paths. Override `JEOPARDY_QA_URL` for a deployed page. It tests actual projected 3D board clicks, animated intro motion, six sequential category introductions and sound dispatches, frame-synchronized floor captures during camera movement and cuts, clue-length reading delays, response/podium timers, True Daily Double and opponent scores, saved-game resume, timer pause, mobile layout, CPU planned passes and real scheduled buzz/answer highlighting, and decoding all 33 audio assets. CPU cases use deterministic recovered-plan fixtures; Final UI coverage uses a fixture to skip the preceding boards. Neither establishes a full human-played show or retail runtime parity. `tools/recover_presentation.py` re-exports the native category and podium-timer presentation after the Flash extraction.
