Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ A Godot 4 arcade collection starring Hoppy, the CodeRabbit mascot. It has a reac
```sh
godot --path hoppys-adventure-arcade
```
3. Choose **Platformer Game → Level Select**, **Endless Runner**, or **Beat’em Up → Play Code City Brawl / Play Heroes vs. Villains**.
3. Choose **Platformer Game** (it opens on Level Select), **Endless Runner**, or **Beat’em Up → Play Code City Brawl / Play Heroes vs. Villains**.

| Action | Keyboard | Controller |
|---|---|---|
Expand Down
2 changes: 1 addition & 1 deletion docs/bay-area-blitz.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Implemented September 20, 2026 against Godot 4.7.1. Design references: the share

## Play

Run the project normally: **Platformer Game → Level Select → Play Bay Area Blitz**. For direct iteration, run `res://scenes/platformer/bay_area_blitz.tscn`.
Run the project normally: **Platformer Game → Play Bay Area Blitz**. For direct iteration, run `res://scenes/platformer/bay_area_blitz.tscn`.

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

## Play

Choose **Platformer Game → Level Select → Play Enchanted Repo**. Direct scene: `res://scenes/platformer/enchanted_repo.tscn`.
Choose **Platformer Game → 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 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.

Expand Down
2 changes: 1 addition & 1 deletion docs/orbital-ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Implemented October 1, 2026 in Godot 4.7.1. Source references: the current share

## Play

Launch the project, choose **Platformer Game → Level Select → Play Orbital Ops**. Direct scene: `res://scenes/platformer/orbital_ops.tscn`.
Launch the project, choose **Platformer Game → Play Orbital Ops**. Direct scene: `res://scenes/platformer/orbital_ops.tscn`.

- Move: A/D, left/right arrows, gamepad stick or D-pad.
- Jump: Space/W/up, gamepad A/Cross. Release early for a shorter normal-gravity jump.
Expand Down
30 changes: 5 additions & 25 deletions hoppys-adventure-arcade/scenes/platformer/platformer_menu.gd
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
extends Control
const Style = preload("res://scenes/main_menu/menu_style.gd")
var selecting := false
var column: VBoxContainer
func _ready() -> void:
theme = Style.theme()
Expand All @@ -22,32 +21,14 @@ func _ready() -> void:
column = VBoxContainer.new()
column.add_theme_constant_override("separation", 20)
margin.add_child(column)
_main()
_select()
func _clear() -> void:
for child in column.get_children():
column.remove_child(child)
child.queue_free()
func _main() -> void:
selecting = false
_clear()
column.add_child(Style.label("HOPPY'S ADVENTURE ARCADE / 01", 16, Style.MINT))
column.add_child(Style.label("PLATFORMER", 52))
column.add_child(Style.label("Small rabbit. Big commits.", 22))
column.add_child(Style.label("Cross the city, explore orbit, or brave the enchanted forest. Clear the blockers and ship the build.", 17))
var select := Style.button("Level Select →")
column.add_child(select)
select.pressed.connect(_select)
var back := Style.button("Return to Main Menu")
column.add_child(back)
back.pressed.connect(func(): AppRouter.go("hub"))
var quit := Style.button("Quit Game")
column.add_child(quit)
quit.pressed.connect(func(): AppRouter.quit_game())
select.grab_focus()
func _select() -> void:
selecting = true
_clear()
column.add_child(Style.label("CHOOSE YOUR NEXT COMMIT", 16, Style.MINT))
column.add_child(Style.label("HOPPY'S ADVENTURE ARCADE / 01 · PLATFORMER", 16, Style.MINT))
column.add_child(Style.label("LEVEL SELECT", 42))
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 22)
Expand Down Expand Up @@ -86,10 +67,9 @@ func _select() -> void:
play.pressed.connect(func(): AppRouter.go("orbital_ops"))
else:
play.pressed.connect(func(): AppRouter.go("enchanted_repo"))
var back := Style.button("← Back")
var back := Style.button("← Main Menu")
column.add_child(back)
back.pressed.connect(_main)
back.pressed.connect(func(): AppRouter.go("hub"))
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel"):
if selecting: _main()
else: AppRouter.go("hub")
AppRouter.go("hub")
30 changes: 29 additions & 1 deletion hoppys-adventure-arcade/tests/platformer_smoke.gd
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ func capture(path: String) -> void:
if DisplayServer.get_name() != "headless":
await RenderingServer.frame_post_draw
root.get_texture().get_image().save_png(path)
## Waits up to 3 s for the router to open `path` and finish its fade (it refuses routes while busy).
func settles_on(path: String) -> bool:
var router := root.get_node("AppRouter")
# A wall-clock deadline: headless frames can run far faster than the router's 0.5 s of fades.
var deadline := Time.get_ticks_msec() + 3000
while Time.get_ticks_msec() < deadline:
if current_scene != null and current_scene.scene_file_path == path and not router.busy:
return true
await process_frame
return current_scene != null and current_scene.scene_file_path == path and not router.busy
func run() -> void:
for binding in [["move_left", KEY_LEFT], ["move_right", KEY_RIGHT], ["jump", KEY_UP]]:
var key := InputEventKey.new()
Expand Down Expand Up @@ -198,11 +208,29 @@ func run() -> void:
paused = false
root.get_node("AppRouter").go("platformer")
await create_timer(0.7).timeout
current_scene._select()
check(current_scene.column.get_child(1).text == "LEVEL SELECT", "Platformer opens straight on Level Select")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
await frames(3)
await capture("/tmp/hoppy-level-select.png")
check(not root.get_node("AudioService").music.stream_paused, "Menu music restored")
check(root.get_node("AudioService").music.process_mode == Node.PROCESS_MODE_INHERIT, "The hub music pauses with the tree again outside the level")
# Both ways back from Level Select land on the hub.
var back: Button = current_scene.column.get_child(current_scene.column.get_child_count() - 1)
check(back.text == "← Main Menu", "Level Select offers the way back to the hub")
back.pressed.emit()
check(await settles_on("res://scenes/main_menu/main_menu.tscn"), "← Main Menu returns to the hub")
check(await root.get_node("AppRouter").go("platformer") and await settles_on("res://scenes/platformer/platformer_menu.tscn"), "Platformer reopens on Level Select")
if current_scene.scene_file_path != "res://scenes/platformer/platformer_menu.tscn":
quit(failures)
return
var cancel := InputEventAction.new()
cancel.action = "ui_cancel"
cancel.pressed = true
# Real input dispatch, so a focused control that swallowed ui_cancel would fail this check.
Input.parse_input_event(cancel)
var release := cancel.duplicate() as InputEventAction
release.pressed = false
Input.parse_input_event(release)
check(await settles_on("res://scenes/main_menu/main_menu.tscn"), "ui_cancel on Level Select returns to the hub")
print("PLATFORMER SMOKE: ", "PASS" if failures == 0 else "FAIL", " (", failures, " failures)")
await frames(35)
await root.get_node("AudioService").settle()
Expand Down
Loading