From 8941af51b48e5251400254dfbc61f0fa87a38b72 Mon Sep 17 00:00:00 2001 From: Erik Thorelli Date: Thu, 15 Oct 2026 20:32:24 -0700 Subject: [PATCH 1/2] feat(credits): list every original track under "Music by Erik Thorelli" The credits screen (#85) left the Original music section's track list empty so the soundtrack could be added as data. This fills it with the 20 original tracks in the game, each paired with the part of the game it plays in: the theme and its instrumental, the platformer, Code City Brawl and Heroes vs. Villains. "Roll the Credits" and two jingles stay on the album only, because they don't play in the game. The generated asset credits need no change. build_credits.py already credits only the third-party assets still in the game, and since the soundtrack PRs those are all CC0 or public domain, so no CC BY attribution remains to keep. credits_screen.gd now checks that every track has a unique title and a "used by", and that the screen shows each line. Part of #44. --- .../scenes/credits/credits.json | 83 ++++++++++++++++++- .../tests/credits_screen.gd | 10 +++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/hoppys-adventure-arcade/scenes/credits/credits.json b/hoppys-adventure-arcade/scenes/credits/credits.json index 4c850734..c3497983 100644 --- a/hoppys-adventure-arcade/scenes/credits/credits.json +++ b/hoppys-adventure-arcade/scenes/credits/credits.json @@ -6,7 +6,88 @@ "lines": [ "Music by Erik Thorelli" ], - "tracks": [] + "tracks": [ + { + "title": "Send in the Rabbit", + "used_by": "Booth attract mode" + }, + { + "title": "Send in the Rabbit (Instrumental)", + "used_by": "Title screen" + }, + { + "title": "Cable Car Hop", + "used_by": "Bay Area Blitz" + }, + { + "title": "Merge Conflict Monster", + "used_by": "Bay Area Blitz boss" + }, + { + "title": "Zero-G Deploy", + "used_by": "Orbital Ops" + }, + { + "title": "The Deprecated API", + "used_by": "Orbital Ops boss" + }, + { + "title": "The Enchanted Repo", + "used_by": "The Enchanted Repo" + }, + { + "title": "The Legacy Monolith", + "used_by": "The Enchanted Repo boss" + }, + { + "title": "Stage Clear", + "used_by": "Platformer results" + }, + { + "title": "CLI / IDE District", + "used_by": "Code City Brawl" + }, + { + "title": "Security Sector", + "used_by": "Code City Brawl" + }, + { + "title": "Git Repository HQ", + "used_by": "Code City Brawl" + }, + { + "title": "District Boss", + "used_by": "Code City Brawl: Stack Overflow and Zero-Day" + }, + { + "title": "The Grand Merge Conflict", + "used_by": "Code City Brawl final boss" + }, + { + "title": "The Generation Floor", + "used_by": "Heroes vs. Villains" + }, + { + "title": "AI SLOP", + "used_by": "Heroes vs. Villains boss" + }, + { + "title": "The Review Gauntlet", + "used_by": "Heroes vs. Villains" + }, + { + "title": "Agent Army and the 10,000-Line PR", + "used_by": "Heroes vs. Villains bosses" + }, + { + "title": "The Agentic Core", + "used_by": "Heroes vs. Villains" + }, + { + "title": "Machine-Speed Hacker", + "used_by": "Heroes vs. Villains final boss" + } + ] } ] } diff --git a/hoppys-adventure-arcade/tests/credits_screen.gd b/hoppys-adventure-arcade/tests/credits_screen.gd index b553bc5e..89692c11 100644 --- a/hoppys-adventure-arcade/tests/credits_screen.gd +++ b/hoppys-adventure-arcade/tests/credits_screen.gd @@ -54,6 +54,13 @@ func run() -> void: check(not written.is_empty() and written[0].title == "Original music" and "Music by Erik Thorelli" in written[0].lines and written[0].tracks is Array, "credits.json opens with Original music, by Erik Thorelli, and a tracks list to extend") + var music_tracks: Array = written[0].tracks if not written.is_empty() else [] + var track_titles := {} + for track: Variant in music_tracks: + if track is Dictionary and str(track.get("title", "")) != "" and str(track.get("used_by", "")) != "": + track_titles[track.title] = "%s · %s" % [track.title, track.used_by] + check(music_tracks.size() > 0 and track_titles.size() == music_tracks.size(), + "Every original track has a unique title and the part of the game it plays in (%d tracks)" % music_tracks.size()) var all_credits: Array = [] for section in generated: all_credits.append_array(section.credits) @@ -76,6 +83,9 @@ func run() -> void: var shown := texts(screen) check("ORIGINAL MUSIC" in shown and "Music by Erik Thorelli" in shown, "The screen credits the original music to Erik Thorelli") + var unlisted := track_titles.keys().filter(func(title: String) -> bool: return not track_titles[title] in shown) + check(not track_titles.is_empty() and unlisted.is_empty(), + "The screen lists every original track title%s" % ("" if unlisted.is_empty() else " (missing: %s)" % ", ".join(unlisted))) var missing: Array = [] var unlicensed: Array = [] for entry: Dictionary in all_credits: From d70fca1aeb8ee5f57b1792027b08de2a58648b8c Mon Sep 17 00:00:00 2001 From: Erik Thorelli Date: Thu, 15 Oct 2026 20:43:49 -0700 Subject: [PATCH 2/2] test(credits): require string track fields and pin the expected original tracks --- hoppys-adventure-arcade/tests/credits_screen.gd | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/hoppys-adventure-arcade/tests/credits_screen.gd b/hoppys-adventure-arcade/tests/credits_screen.gd index 89692c11..6d99e760 100644 --- a/hoppys-adventure-arcade/tests/credits_screen.gd +++ b/hoppys-adventure-arcade/tests/credits_screen.gd @@ -4,6 +4,13 @@ extends SceneTree ## license; a track added to credits.json shows up; it scrolls by itself and by hand; Esc and the ## Back button return to the hub. Run headless with --script res://tests/credits_screen.gd. const Credits := preload("res://scenes/credits/credits.gd") +## The original tracks that play in the game (Assets/Audio/**/PROMPTS.md), kept apart from credits.json +## so a track dropped from the credits fails the suite. +const ORIGINAL_TRACKS := ["Send in the Rabbit", "Send in the Rabbit (Instrumental)", "Cable Car Hop", + "Merge Conflict Monster", "Zero-G Deploy", "The Deprecated API", "The Enchanted Repo", "The Legacy Monolith", + "Stage Clear", "CLI / IDE District", "Security Sector", "Git Repository HQ", "District Boss", + "The Grand Merge Conflict", "The Generation Floor", "AI SLOP", "The Review Gauntlet", + "Agent Army and the 10,000-Line PR", "The Agentic Core", "Machine-Speed Hacker"] var failures := 0 var router: Node @@ -57,10 +64,16 @@ func run() -> void: var music_tracks: Array = written[0].tracks if not written.is_empty() else [] var track_titles := {} for track: Variant in music_tracks: - if track is Dictionary and str(track.get("title", "")) != "" and str(track.get("used_by", "")) != "": + if track is Dictionary and track.get("title", "") is String and track.get("title", "") != "" \ + and track.get("used_by", "") is String and track.get("used_by", "") != "": track_titles[track.title] = "%s · %s" % [track.title, track.used_by] check(music_tracks.size() > 0 and track_titles.size() == music_tracks.size(), "Every original track has a unique title and the part of the game it plays in (%d tracks)" % music_tracks.size()) + var expected_titles := {} + for title in ORIGINAL_TRACKS: + expected_titles[title] = true + check(track_titles.keys().size() == ORIGINAL_TRACKS.size() and track_titles.keys().all(func(t: String) -> bool: return expected_titles.has(t)), + "credits.json lists exactly the %d original tracks that play in the game" % ORIGINAL_TRACKS.size()) var all_credits: Array = [] for section in generated: all_credits.append_array(section.credits)