Improvement over [1639] SGB Pocket Monsters: Green "warp glitch" by p4wn3r in 04:24.37 and #9444: Fortranm's GB Pocket Monsters: Green "warp glitch" in 04:22.88. This submission is intended to have Japanese non-save corruption runs be a separate branch.
Game objectives
- Completes the game as fast as possible without save data corruption
ACE setup
Instead of using the select glitch and Dokokashira door glitch to simply perform a series of warps to get to the Hall Of Fame room, run opts to ultimately trigger ACE to trigger the Hall Of Fame sequence.
The select glitch ultimately sets up a case where the game will look for "Pokemon" outside of the actual party to decrease HP for "poisoned" ""Pokemon"". In some cases, this decreases the map ID in some of the warp entries, leading to the Dokokashira door glitch. However, it can possibly decrease another particularly useful value, the current map ID. This map ID is used by the game to determine which bank the current map script should be ran under. With a different bank being used than expected, the game will most likely "crash" in some way.
This "crash" could be an invalid opcode or stop, which ends execution immediately. It can however be one of the rst opcodes, which will eventually trigger rst $38. In Japanese Gen 1 games (along with some other unrelated games), rst $38 contains
jp 0xF080
. This was changed to be another rst $38 in Japanese Yellow 1.1, and subsequently all later versions of Gen 1 have rst $38 in rst $38. It's presumed that this jump was intended as some sort of leftover debug code, but in the case of the actual released game, it just gives easy access to ACE.
0xF080 contains data about the last sprite decompressed. A lot of it is junk, but it happens the beginning of this data is the sprite width and height. These have some variation, but they ultimately end up being some form of one of the
jr
opcodes, which allow for jumping forward, past most of this data. After that data, however, is a buffer of move strings. These move strings will end up having invalid opcodes in them, completely stopping execution. This buffer can be cleared by save and quitting, so a save and quit is required.
Eventually, more controllable bytes are reached.
wIgnoreInputCounter
, wStepCounter
, wPlayerName
, wPartyMon2OTID
, wPartyMon2DVs
. All of these are used to ultimately redirect execution to the Hall Of Fame sequence.
wIgnoreInputCounter
is just a counter that just ticks down every frame. For the payload, the run ensures it is 0xF8. wStepCounter
is made to be 0xFC (note that the step counter ticks downwards, not upwards, and resets upon a save and quit). The player is named "ギホゴ". The trainer ID is manipulated to be 0x0616. Multiple other TIDs could have been used, but this was the fastest one to manipulate. The starter DVs are manipulated to be 0xC4AA (0xC2AA also would work, but 0xC4AA is what I ended up getting). This results in the following payload:
; wIgnoreInputCounter + wStepCounter ld hl,sp-4 ; hl = 0xDFF1 ; player name ld b,0x9C ; bc = 0x9C00 add hl,bc ; hl = 0x7BF1 ; starter pokemon ; catch rate dec l ; hl = 0x7BF0 ; growl dec l ; hl = 0x7BEF ; tid ld b,0x16 ; dvs and pp call nz,0x23AA
The end of the payload does a jump to
0x23AA
. This is a ROM address, within a far jump into some map code. This ends up calling the Bankswitch
function in the game, which switches the ROM bank over to whatever b
has and jumps to whatever hl
has. This means the game bankswitches to bank 0x16 (keep in mind the game only has 0x20 ROM banks, so any b
value higher than 0x1F effectively gets moduloed by 0x20, so 0x16, 0x36, 0x56, etc all work), and jumps to 0x7BEF
. 0x7BEF
in bank 0x16 is the middle of the Hall of Fame script.
Route
- The trainer ID is manipulated to be 0x0616. 0x0636, 0x0656, 0x0676, 0x0696, 0x06B6, 0x06D6, and 0x06F6 also work, but 0x0616 ended up being the fastest to manipulate.
- The player is named "ギホゴ" and the Rival is set to the first preset option.
- Charmander is picked as the starter, with 0xC4AA DVs. 0xC2AA would also work for this TAS. In theory, a ton of other DVs would work for the ACE payload, but 0xC2AA and 0xC4AA are the best in this case, as they are the only ones which give Charmander 0 HP DVs, and thus gives Charmander 18 HP (even 1 HP DV would bump Charmander's HP to 19). It is not nicknamed, as the name is not shown enough to save time nicknaming. Squirtle and Bulbasaur cannot be used, as they have Tackle, which ruins the payload used.
- The Rival fight is purposefully lost, as it does not need to be won and it is fastest to lose. This is done in 3 turns, each comprising of a 1/256 miss with Growl, and a 1/39 crit with Tackle. A 1/39 Tackle crit does 6 damage to Charmander, so having 18 HP allows for a 3 turn fight. Who doesn't love a 1/1178114537369 Rival fight.
- Options are not set for the Rival fight. Keeping animations on during the Rival fight saves a bit of time due to Tackle being faster with animations on rather than off. It also saves time as it avoids ever going into the options menu, which is completely unneeded for this run.
- A Pidgey is encountered at the end of Route 1. This has the shortest cry of the Pokemon in Route 1. Its encounter location is also manipulated to allow for the earliest poison tick to occur on entering Pallet Town.
- A warp is done near the southern entrance to Fuschia City.
- A save+quit is done once two poison ticks are done, as to ensure the next one after the save+quit triggers the ACE payload.
- The Hall of Fame still needs input to clear some textboxes to advance to the credits, so input doesn't end until those textboxes are cleared.
DrD2k9: Claiming for judging.