19 — Playing My Own Game

A dedicated play session turned up a list of things to fix.


The Bug Report

The last few sessions were heavy on infrastructure — CI tests, offset verification, code audits. Good work, but not the kind where you play through the game. I reserved this session for that. New game, walked around, triggered battles, opened menus. Within ten minutes I had a list.

The first item: the Town Portal crashed the game. Not a soft crash — hard. The debug warp worked because that path calls SetMainCallback2(CB2_LoadMap) before fading. The EMBER submenu portal skipped that call. One-liner fix, but it only surfaces when you use the feature end to end.

Second item: a level 13 Meowth as my second wild encounter out of Pallet Town. Treasure Pokémon — a 5% chance encounter with an Epic+ gear drop — were spawning from the start. The player has no business fighting a level 13 anything on Route 1. I added a 2-badge gate. Now they show up around Vermilion, which is about right.

Third item: the EMBER stats panel was cluttered. STREAK, GEAR, and a 0/3 QUESTS counter were visible from the first step out of the lab. None of those mean anything at the start. STREAK needs battles. GEAR needs items. QUESTS shouldn’t show a counter when you have no quests. Now each element only appears when it has something to show.

Launching the Wiki

The site has had a devlog since day one, but nothing explaining how the game works. A new player (or my own future self) would have no idea what gear rarities exist, how corrupted zones work, or what the Altar of Recrafting does.

I wrote 9 wiki articles covering the core systems: gear, relics, shards, combat mechanics, the loot economy, corrupted zones, quests, merchants, and the Rift. Combined them with the devlog under a single site with unified header nav. Both sections share the same URL with tabs to switch between them.

The wiki doesn’t need to stay in sync with every commit — it’s a snapshot, not live documentation. I’ll update it when systems change.

Getting Out of the Bedroom

The vanilla FireRed opening is a seven-minute cinematic: wake up, watch TV, walk down stairs, trigger Oak on Route 1, get stopped, walk back, sit through a four-person dialogue about choosing starters. Every single time you start a new game.

I cut all of it. New game now starts in Oak’s Lab, directly in front of the three Poké Balls. One custom dialogue box — “Ancient Ember awaits” — then you pick your starter. The bedroom, the Route 1 trigger, the lab walk-in animation, Oak’s four-box speech, the rival’s interruption: all gone.

This required flag surgery. The EventScript_ResetAllMapFlags call that fires on new game sets FLAG_HIDE_OAK_IN_HIS_LAB, so Oak would be invisible without an explicit FlagClear right after. I removed the outdoor Oak coord events from map.json (source of truth, not the generated .inc file). The starter scene had two paths — the cinematic one I removed, and the national dex scene used in post-game. Those shared movement scripts, so the national dex path had to survive the gutting.

222 lines removed.

No Tutorial Battle

The first rival battle in Oak’s Lab has a special mode: BATTLE_TYPE_FIRST_BATTLE. When set, Oak narrates the fight with voiceover prompts (“Inflicting damage is key!”), crits are suppressed, accuracy checks auto-pass, and running is blocked with a Birch speech. It’s a full tutorial harness bolted onto the battle engine.

I removed it. The rival battle is now just a normal trainer battle. You fight, you win or lose, life goes on.

Removing the flag required touching eight files — battle_setup.c, battle_controller_player.c, battle_controller_opponent.c, battle_controllers.c, battle_main.c, battle_script_commands.c, battle_bg.c, and a battle script. The dead branches in battle_controller_oak_old_man.c came down in a follow-up audit.

The Dead Code Audit

With the tutorial gone and BAG moved out of the start menu, I swept for dead code.

From the BAG removal: STARTMENU_BAG enum entry, its action table row, StartMenuBagCallback, the help text string, the extern declaration. All gone.

From the tutorial removal: BATTLE_TYPE_FIRST_BATTLE branches across battle_controller_oak_old_man.c (9 dead conditionals). Then the functions those branches called: OakOldManHandleInputChooseMove, three PrintOakText_* functions, BtlCtrl_OakOldMan_TestState2Flag/SetState2Flag. Then the party menu tutorial chain: 10 Task_FirstBattleEnterParty_* functions, their helper functions, the Oak voiceover window template, two Oak speech strings. Then the declarations, the flag defines, the bit definition itself.

529 lines removed in a single commit. The BATTLE_TYPE_FIRST_BATTLE bit is now vacant — a gap between bit 3 (TRAINER) and bit 5 (LINK_IN_BATTLE).

Hold to Scroll

Every list UI in the game — gear bag, forge, altar, merchant, chest, portal, Pokédex — required a separate button press per row. On a 40-item gear bag, that’s exhausting.

GBA’s gMain.newAndRepeatedKeys fires on fresh press, then again after a 40-frame delay (≈0.7s), then every 5 frames (≈83ms) while held. I replaced JOY_NEW(DPAD_UP/DOWN) with a JOY_REPEAT macro across 8 files. Eight sed commands.

BAG in the EMBER Menu

The start menu had both BAG and EMBER sitting next to each other. BAG opened the item bag. EMBER opened a submenu with gear-specific tools. It made more sense to put BAG inside EMBER — one fewer top-level menu item, and the two bag options live together.

EMBER submenu now has five items: GEAR BAG, BAG, CODEX, PORTAL, DEBUG — all caps, consistent with GBA menu convention. The submenu window grew from 8 tiles tall to 10 to fit the extra item, and the stats panel’s VRAM baseBlock was shifted from 0x26D to 0x281 to avoid overlap.


By the Numbers

Metric Value
Commits 16
Copilot requests 18
Tool executions 537
Sub-agents spawned 3

16 commits. Time to play it some more.

Back to README