Skip to content
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ A Godot 4 arcade collection starring Hoppy, the CodeRabbit mascot. It has a reac
| Move | A/D or arrow keys | Left stick / D-pad |
| Jump | Space | A / Cross |
| Attack | J / X | X / Square |
| Pause | Escape | Start |
| Pause | Escape or P | Start |

Each game opens with a short **How to Play** card: a few animated pages showing the goal, these controls for keyboard and controller, and tips. Jump, attack or confirm turns the page, ←/→ flip back and forth, and pause skips straight to play. Pages turn by themselves every 7 seconds, and the pause menu's **How to Play** button shows the card again. See [main-menu.md](docs/main-menu.md#how-to-play-cards).

In Orbital Ops' zero-gravity bay, hold Jump or Up to rise, hold Down to descend, and release to brake.

Expand Down
6 changes: 3 additions & 3 deletions docs/bay-area-blitz.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ Run the project normally: **Platformer Game → Level Select → Play Bay Area B

- Move: A/D, left/right arrows, controller left stick or D-pad.
- Jump: Space/W/up arrow, controller A/Cross. Release early for a shorter jump.
- Swipe: J/X, controller X/Square. Landing on an enemy also deals damage and bounces Hoppy.
- Pause/resume: Escape or controller Start. Focus loss pauses the game.
- Swipe: J/X, controller X/Square. Swiping is the only way to hurt a bug; there is no stomping, and touching an enemy from any side, including landing on it, costs a heart.
- Pause/resume: Escape, P or controller Start. Focus loss pauses the game. Entering the level from the menus opens its [how-to-play card](main-menu.md#how-to-play-cards); the pause menu's **How to Play** shows it again.
- Defeat the Merge Conflict Monster, then swipe or land on the MERGE button to finish.
- Pause and results provide fresh retry, platformer menu, and arcade return. Finishing a level (any of the three) plays a short stage-clear stinger (`Assets/Audio/Platformer/stinger_stage_clear.ogg`) over the MERGE SUCCESSFUL results while the paused level music stays silent; a knockout keeps its ducked level music instead.

## Implemented

A 5,300-pixel authored city route with two jumpable gaps, optional raised token routes, two moving cable-car platforms, parallax buildings/fog, supplied Golden Gate scenery, animated Hoppy, glitch critters, Request Changes Reviewers, broken pipelines, a moving conflict zone, and a three-headed boss. The safe lower route does not require waiting for a cable car.

Three-heart health, damage knockback, post-hit protection, variable jump height, coyote time, jump buffering, swipe hit deduplication, stomping, per-run score, boss-gated completion, pause/focus handling, death/retry, level selection, and the Bay Area music track are connected. The existing menu audio/background paths were repaired to match the reorganized Assets folder. Other levels remain explicitly unavailable.
Three-heart health, damage knockback, post-hit protection, variable jump height, coyote time, jump buffering, swipe hit deduplication, per-run score, boss-gated completion, pause/focus handling, death/retry, level selection, and the Bay Area music track are connected. The existing menu audio/background paths were repaired to match the reorganized Assets folder. Other levels remain explicitly unavailable.

All art comes from `Assets/bay-area-blitz-assets-only`; Hoppy uses that pack's existing platformer SpriteFrames. Sound uses existing project assets. No assets were generated or downloaded.

Expand Down
1 change: 1 addition & 0 deletions docs/builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ open -a "Hoppy’s Adventure Arcade.app" --args -- --booth --route=heroes_vs_vil

- **Fullscreen.** The game starts fullscreen without changing the saved settings.
- **Booth QR code.** A scannable, branded CodeRabbit link stays in the top-right corner on every route in booth mode. The provided code points to `https://coderabbit.link/ato26-ad?qr=1`, which identifies QR scans for attribution. To change the destination, regenerate `hoppys-adventure-arcade/Assets/booth/booth_qr.png` from the repository root with `swift hoppys-adventure-arcade/tools/booth/generate_qr.swift` and pass the output path and URL as optional arguments, then rebuild the game.
- **How-to-play cards.** Each visitor's first entry into a game opens its how-to-play card. Its pages turn by themselves every 7 seconds (28 seconds at most), so it starts play on its own and never holds off the idle reset. A new visitor starts whenever the attract route opens. See [main-menu.md](main-menu.md#how-to-play-cards).
- **Idle reset.** After 90 seconds with no keyboard, mouse, touch or controller input, any game returns to the attract route. That is the hub, whose rotating previews act as the attract screen, unless `--attract=` names `beatem_up` or `runner`, the other menus that start their own music.
- **Game over.** A lost game doesn't offer unlimited retries. A player gets `--retries=<n>` retries (default 0); after that, the game shows no retry screen. Instead a **GAME OVER** card thanks the player, shows their score and counts down six seconds, then the attract route opens for the next player. The count starts again whenever the attract route opens. Every game takes part: Heroes vs. Villains, Code City Brawl, the platformer levels and the Endless Runner. Without `--booth`, every game keeps its own retry screen.
- **No quitting from the menu.** The menu's **Quit Game** does nothing in booth mode; staff close the window (Cmd+Q, Alt+F4).
Expand Down
4 changes: 3 additions & 1 deletion docs/code-city-brawl.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Launch Godot 4.7.1 and choose **Beat'em Up → Play Code City Brawl** from the a
| Jump | Space | A / Cross |
| Attack; chain jab → swing → spinning kick | J or X | X / Square |
| Pick up nearby weapon | E or K | Y / Triangle |
| Pause / resume | Escape | Start |
| Pause / resume | Escape or P | Start |

The first entry from the menus opens the game's [how-to-play card](main-menu.md#how-to-play-cards) before the opening story scenes; the pause menu's **How to Play** shows it again.

W/Up changes the ground lane. Jump is a separate height above that lane. Attacks have a forward reach and depth tolerance, so standing in another lane avoids melee. Jump above ground attacks and projectiles, or sidestep their telegraphs. Late attack input buffers one next combo step; a gap of more than 0.48 seconds after recovery resets the chain. Hoppy keeps his facing through a swing, so its hit area never flips mid-attack. Pressing the other way during or right after a swing turns him for the next one, buffered combo steps included. A quick tap mid-swing is remembered until the swing ends.

Expand Down
2 changes: 1 addition & 1 deletion docs/enchanted-repo.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Implemented in Godot 4.7.1 from the current shared GDD's Platformer section, the

Choose **Platformer Game → Level Select → Play Enchanted Repo**. Direct scene: `res://scenes/platformer/enchanted_repo.tscn`.

Move with A/D or arrows, jump with Space/W/up, swipe with J/X, pause with Escape. Existing gamepad mappings remain stick/D-pad movement, A/Cross jump, X/Square attack, and Start pause. Release Jump early for a short hop. Descending onto an enemy bounces Hoppy; the Monolith takes damage only in its recovery windows.
Move with A/D or arrows, jump with Space/W/up, swipe with J/X, pause with Escape or P. Existing gamepad mappings remain stick/D-pad movement, A/Cross jump, X/Square attack, and Start pause. Release Jump early for a short hop. Descending onto an enemy bounces Hoppy; the Monolith takes damage only in its recovery windows. Entering the level from the menus opens its [how-to-play card](main-menu.md#how-to-play-cards); the pause menu's **How to Play** shows it again.

Cross the forest and ruined castle, defeat the Legacy Monolith, then land on or swipe the unlocked MERGE button. Standing beside it or rising through it does not finish the level. Retry starts a completely fresh Enchanted Repo run.

Expand Down
2 changes: 1 addition & 1 deletion docs/endless-runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The hub routes to `RunnerMenu`, a Node3D with Backdrop and Interface children. T

The menu calls the AppRouter singleton directly: Play -> `go("runner_game")`, Return/cancel -> `go("hub")`, Quit -> `quit_game()`. Local singleton references allow standalone `--check-only`. In gameplay, overlays call only facade `pause()`, `resume()`, `retry()`, `go_to_menu()`, and `submit_name(name)`. Backend owns physics, collisions, recovery, score, persistence, routes and shared audio. The frontend milestones changed no backend files or project settings. The backend contract PR added the runner [input], [layer_names] and physics_interpolation entries to project.godot.

The teaching layer uses a mint shield for power-ups, an orange wrench for CodeRabbit tools, and a striped DANGER triangle for hazards. GET READY carries the full grab/avoid legend; HOW TO PLAY repeats the three symbols before Play. First power-up collection, tool collection, and unblocked damage each queue one full-copy, kind-bordered 2.4-second hint per run. Boss intros and stage-clear cards defer these hints, including any active hint. The legend closes with GET READY, and no persistent gameplay panel is added. Coordinator capture views `get-ready` and `toast-grab` are available; no graphical capture was run.
The teaching layer uses a mint shield for power-ups, an orange wrench for CodeRabbit tools, and a striped DANGER triangle for hazards. Before the run intro, a visitor's first run opens the runner's [how-to-play card](main-menu.md#how-to-play-cards) (goal and tips over its menu screenshots, then lane change, jump, roll and pause), and the pause menu's **How to Play** button shows it again. GET READY carries the full grab/avoid legend; HOW TO PLAY repeats the three symbols before Play. First power-up collection, tool collection, and unblocked damage each queue one full-copy, kind-bordered 2.4-second hint per run. Boss intros and stage-clear cards defer these hints, including any active hint. The legend closes with GET READY, and no persistent gameplay panel is added. Coordinator capture views `get-ready` and `toast-grab` are available; no graphical capture was run.

HUD is on layer 10, PauseMenu 21 and GameOver 22. HUD listens to facade signals, uses responsive margins, counts up score, displays effects and pursuit, dims on pause and hides gameplay widgets on caught. Backend owns damage camera shake. Pause has no ui_cancel handler. Game Over appears only on run_finished, shows result stats and the local top ten, and saves qualifying names through the facade. Save success locks the name field and focuses Retry; failure stays editable. Retry creates a fresh scene. The mock scene demonstrates the same signals in 40 seconds with in-memory persistence.

Expand Down
8 changes: 6 additions & 2 deletions docs/heroes-vs-villains.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ This mode uses the armored Hoppy and the [Mode 2 asset pack](heroes-vs-villains-
| Move along the ground plane | WASD / arrows | Left stick / D-pad |
| Jump | Space | A |
| Three-hit combo / weapon | J or X | X |
| Pick up weapon | E | Y |
| Pick up weapon | E or K | Y |
| Charge REVIEW after unlock | Hold Q | Hold LB |
| Pause | Escape | Start |
| Pause | Escape or P | Start |
| Skip presentation card | Enter / keypad Enter | View / Select |

The first entry from the menus opens the game's [how-to-play card](main-menu.md#how-to-play-cards) before the opening cards; the pause menu's **How to Play** shows it again.

The armored Hoppy uses eight-frame side, away and toward-camera locomotion cycles at 14 fps, with cadence scaled to analog movement and speed effects. Base travel speed is 264 pixels/second (10% above Code City Brawl).

Hoppy has three health points, jump avoidance, hit recovery and one shield on stage entry after REVIEW is unlocked. Attack presses can buffer the next combo strike. Clear ordinary rooms, then walk through the right gate. Boss encounters end with a short Hoppy victory hop while the defeated villain collapses, followed by the reward cards. After the last reward card, Hoppy runs out of the stage on his own under a CAPABILITY DEPLOYED banner, faster than his normal run when he starts far from the right edge, so he always leaves the screen before it fades. The screen dips to black while the next stage loads, then he runs in under its title banner and play resumes; the sequence takes about 2–3 seconds and needs no input. Messages raised while the title banner fades into combat, such as the encounter counter, wait until it clears. The opening cards lead into the same run-in, and so does a retry. Boss music dips, swaps tracks at the bottom and comes back up over 0.6 seconds. The final boss instead leads to the final victory cards and results. Break props for supplies; consumables collect on contact, while weapons require pickup input. Losing the last health point knocks Hoppy out:
Expand All @@ -26,6 +28,8 @@ Hoppy has three health points, jump avoidance, hit recovery and one shield on st
- He lands flat on his front with a burst and a bounce, and lies there with dizzy stars and a "K.O." callout while the music ducks.
- About 2.2 seconds after the hit, the game-over menu offers a retry of the current stage with its entry score and capabilities, fresh health and cleared temporary effects. Pause and focus loss stop the run, including during cards and stage transitions; return-to-menu is available through pause and results.

REVIEW never hurts Hoppy. Holding Q / LB for under half a second fires a mint burst on the floor around him that stuns nearby threats; stunned enemies turn blue. For 0.85 seconds after it fires, hits can't land and Hoppy glows mint, without the blink that means he was hit. It recharges in 8 seconds.

Capabilities persist within the run, not across application launches. REVIEW unlocks after AI SLOP; TRIAGE and CHANGE STACK unlock after defeating both the fused army commander and the PR; SECURITY completes the final victory.

The three opening cards are painted story scenes:
Expand Down
13 changes: 13 additions & 0 deletions docs/main-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ The **Credits** button, after Settings and Quit Game, opens `scenes/credits/cred
- **Asset credits** are generated. `tools/credits/build_credits.py` writes `scenes/credits/asset_credits.json` from `ASSETS_LICENSES.md`. After changing that table, run `python3 tools/credits/build_credits.py`; CI fails if the two differ.
- **Hand-written credits** live in `credits.json`: a list of sections, each with a `title`, optional `lines` and optional `tracks` (`{"title": …, "used_by": …}`). Original music goes in the "Original music" section. To credit something else, add a track there or add a section.

## How-to-play cards

The first time a visitor enters a game, `AppRouter.go()` opens that game's how-to-play card (`app/how_to_play.gd`) over it. A visitor starts whenever the hub or the booth's attract route opens, so retries and returns from a game's own menu skip the card. `--route=` launches show it too.

- **Pages.** Each card is three or four pages: **Your goal**, **Controls**, **Tips**, and for Orbital Ops a **Zero gravity** page. Dots in the corner show where you are.
- **Scenes.** Each page except Controls plays a short looping scene with a line of text under it (`app/how_to_play_demo.gd`). The scenes reuse the game's own sprite sheets: Hoppy swiping the level boss down and landing on MERGE, swiping a glitch critter, rising and sinking in the zero-G bay, crossing a laser while it's off, hopping across crumbling branches, combos against Code City soldiers and AI Slop sprites, and a REVIEW burst. The runner is 3D, so its pages pan across its menu screenshots. Captions on the scene repeat the key moment ("Off: go!", "Hold Q / LB: REVIEW"). A scene is one looping Tween bound to the page, so it runs while the game is paused and stops when the page changes. Each scene is a scaled-down window onto the game's own screen: the floor line sits where the game puts it (y 600), actors keep their in-game scale and are anchored at the feet by the same pivots the game uses, the platformer scenes draw the level's sky and ground tiles, and the beat 'em up scenes draw the room panorama so actors stand on its painted floor. Bosses stand where the game places them (y 565 in the beat 'em ups).
- **Controls.** `app/how_to_play_data.gd` holds one entry per gameplay route: title, pages, control rows and `poses`. Each row names its input actions, the keys it shows and the pad inputs it shows, plus an `icon`: a small Hoppy acting out the move (running, jumping, swiping, rolling, rising, sinking). The keyboard text ("Space / W / ↑", "WASD / Arrows") and controller text are built from those. With a controller connected, buttons use that pad's names (Cross, A or B for the bottom button); without one they read "A / Cross". Face buttons also get a four-dot glyph with the one to press filled in. Orbital Ops adds its zero-G rise and sink rows; Heroes vs. Villains adds REVIEW and Skip card.
- **Holding the game.** The card pauses the tree and is the root's last child, so its `_input` sees every press first and swallows it. It passes each press to `AppRouter.note_input()` (booth idle timer) and `ControllerService.observe()` (player one's pad).
- **Paging and closing.** Presses in the first 0.5 s are ignored, so the press that picked the game can't skip a page. After that, a confirm, jump or attack press, a left click or a touch turns to the next page, and on the last page starts the game. Left/right (arrows, A/D, D-pad, one flip per stick push) go back and forth without closing. Pause (Esc, P, Start) skips straight to the game. Each page turns by itself after 7 s while its bar drains, so a whole card is at most 28 s. The game unpauses two physics frames after the card closes, so the closing press never also jumps or swipes. The countdown waits while the window is unfocused. If the game paused itself under the card (focus loss), it stays in its pause menu: games answer `is_player_paused()`.
- **Booth.** The 90 s idle timer keeps counting under the card, and a route change removes it.
- **Pause menus.** The platformer levels, the runner and both beat 'em ups have a **How to Play** button in their pause menu. It opens the same card without a countdown; its pages turn the same way, Back or Pause closes it from any page, the game stays paused and focus returns to the button.
- **Tests.** `tests/how_to_play.gd` checks that every gameplay route has a card with a controls page, that every key and pad input a card shows is bound to its row's actions, that every sprite clip and image a page or icon uses exists, that every page builds and plays, the label text, the once-per-visitor rule, paging and closing, auto-advance, booth idle, focus loss and the pause-menu buttons. Suites run with `--script` and get no cards unless they set `AppRouter.how_to_play_enabled`.

## Art and replacement points

As of 2026-09-30, `hoppy.gd` uses the supplied `Assets/Character/Hoppy/2D/hoppy-sprite-pack/hoppy` SpriteFrames: idle by default, one jump on Platformer selection, and one jab then the default idle on Beat’em Up selection. Endless Runner hover or keyboard/gamepad focus crossfades to the existing `hoppy_runner_visual.tscn` rig running in place, rendered through a transparent, isolated 512×512 SubViewport. Leaving that selection restores 2D immediately through a 0.22-second fade; idle background cycling does not switch the character. Hidden 3D rendering and rig processing are disabled. Both versions face toward the buttons and share a ground anchor. `sprite_scale` and `transition_seconds` are exposed on the Hoppy node for iteration. The title uses a system monospace fallback; no final pixel font or logo was supplied.
Expand Down
2 changes: 1 addition & 1 deletion docs/orbital-ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Launch the project, choose **Platformer Game → Level Select → Play Orbital O
- Jump: Space/W/up, gamepad A/Cross. Release early for a shorter normal-gravity jump.
- Swipe: J/X, gamepad X/Square. Descending onto an enemy also damages it and bounces Hoppy.
- Zero-gravity bay: hold Jump/Up to rise, Down/S or stick/D-pad down to descend, release vertical input to brake. Normal gravity resumes outside the cyan boundaries.
- Pause: Escape/Start or the Pause button. Focus loss also pauses.
- Pause: Escape/P/Start or the Pause button. Focus loss also pauses. Entering the level from the menus opens its [how-to-play card](main-menu.md#how-to-play-cards); the pause menu's **How to Play** shows it again. Its card adds the zero-G rise and sink rows and a Zero gravity page showing Hoppy rise, brake and sink.
- Defeat the Deprecated API, then land on or swipe the green MERGE button. Retry always starts a fresh Orbital Ops run; return buttons lead to the platformer menu or arcade.

## Level and combat
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/previews/how-to-play/orbital_ops_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/previews/how-to-play/runner_game_0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading