I’ve been working on thickening out my Pokémon Gameboy collection, and beating the Pokémon Stadiums. I was trying to obtain another copy of Pokémon Crystal for my sister to coop with me. Its really expensive… So I looked into flashing my own cart, but got distracted by Crystal Clear and then decided to play around with ROM hacking.
Anyway, this is like project #255 for me. We’ll see how far I get before I burn out.
(Side note: Today I thought about dedicating time to this project in hours. 25 hours into Pokémon Red gets me to badge 6. Would I be willing to spend 25 hours working on this? Putting my time in relationship to the games I play makes the time seem like an easier commitment.)
When I started out I had some basic goals in mind:
- Inject Custom Text
- Change an existing NPC to give booster packs
- Clone an NPC
- Create a new NPC that talks
- Create a new NPC that battles
- Create a new card*
- Modify the Squirtle & Friends deck to contain new card
- Reassign room so that when you walk to a room you go to a different one
- Create a new basic room and assign it*
- Import new card sprite art*
The first three were surprisingly easy for me. I was able to finish those within a few hours once I got my environment setup. The ones with asterisks were my best guesses on what would be the most difficult for me to do at my skill level. Reusing existing code and tweaking it taught me about the file structure of the disassembled ROM.
Injected “Mert” into game text.
Injected free booster packs into the Tech NPC above!
Creating new functions and declaring new address space as someone who hasn’t worked in assembly for 8 years… was really difficult for me. I’m still stuck and I’m not sure if goal #4 was balanced in hindsight.
The Problem: GBC ROMs have limited space in memory. In the Pokémon TCG NPCs are declared in memory banks. This memory bank has a limited size and is difficult to extend. Or at least I think so. I don’t know what I’m doing. So I thought instead of extending it, why don’t I just used one of the NPCs that is declared as unused?
For example:
const NPC_05 ; $05 (unused)
Then I declare a header for this NPC:
NPC05NPCHeader:
db NPC_05
db SPRITE_OW_TECH
db SPRITE_ANIM_LIGHT_NPC_UP
db SPRITE_ANIM_BLUE_NPC_UP
db $00
dw Script_NPC_05
tx NPC_05NPCName
db $00
db $00
db $00
db $00
You can see that I have a Script_NPC_05
& NPC_05NPCName
. These do work with other NPCs so I won’t bother listing that code here.
Finally, I assign this NPC into the worldspace:
MasonLabNPCS:
db NPC_DRMASON, $0e, $06, SOUTH
dw Preload_DrMason
db NPC_SAM, $04, $0e, EAST
dw Preload_Sam
db NPC_TECH1, $16, $08, WEST
dw NULL
db NPC_TECH2, $16, $14, SOUTH
dw NULL
db NPC_TECH3, $16, $16, WEST
dw NULL
db NPC_TECH4, $0a, $16, EAST
dw NULL
db NPC_05, $0a, $08, EAST <--- These two lines
dw NULL <--- These two lines
db NPC_TECH5, $06, $04, SOUTH
dw Preload_Tech5
db $00
So what does this actually do? At this point I already had defeated Sam, collected my first deck, and saved. I expected a new tech sprite to appear near the professor that would execute my script when I A’d him. This is what I actually got:
WHAT?! Why are there two of you!? Why do you say the same thing? This has nothing to do with the sprite I chosen or the script that I made.
What’s even weirder is that if I do the same process with NPC_6F
(which is also marked as unused) I get an invisible collision square that I can’t interact with. Its almost like these NPCs aren’t pointing to the header I defined for them.
Anyway, I still don’t understand how all this address space is applied. I don’t understand how these NPCs load in the sprite and header information. I’ve proven I’m able to apply new scripts to the existing NPCs. I feel like I’ve hit a point where it might make sense for me to brush up on my assembly. See ya next time!
If you want to see the Pokémon TCG repo you can check it out here: https://github.com/pret/poketcg