-
Notifications
You must be signed in to change notification settings - Fork 93
Add a new card
This tutorial is for how to add a new card, allowing for up to 254 cards in the game. Card IDs in this game are 8-bit, which means they can range in value between 0 and 255 inclusive. However, the IDs $00
and $FF
are reserved for special list handling, so effectively only 254 card IDs are considered valid. As an example, we'll add this Chikorita card.
Edit src/constants/card_constants.asm:
const TANGELA_LV12 ; $2d
const SCYTHER ; $2e
const PINSIR ; $2f
+ const CHIKORITA
const CHARMANDER ; $30
const CHARMELEON ; $31
const CHARIZARD ; $32
Here we respected the logical order of the cards, inserting it as the last Grass Pokémon card. This order will matter for the edits in the rest of the files. At this point, if you try to run make
, then you will get some errors warning you that some tables are the wrong length. Indeed, with the addition of a new card constant, we need to update some data entries. So let's get to it!
Note: You might notice that there will be a gap between
; $2f
and; $30
in the list of constants. These are merely comments to document which constant refers to which hex value. Since a new constant is introduced right after$2f
, thenCHIKORITA
will become the new$30
and all other subsequent entries will be shifted up by 1. At this point it's probably a good idea to either update these values or just delete these comments, so as to not introduce any confusion in the constant values.
Edit src/data/card.asm:
CardPointers::
table_width 2, CardPointers
dw NULL
dw GrassEnergyCard
...
dw TangelaLv12Card
dw ScytherCard
dw PinsirCard
+ dw ChikoritaCard
dw CharmanderCard
dw CharmeleonCard
dw CharizardCard
After adding this pointer, we need to fill in the data that we want to assign to the card. Edit the file again some lines down, after the data corresponding to Pinsir:
tx PinsirDescription ; description
db 0
+ChikoritaCard:
+ db TYPE_PKMN_GRASS ; type
+ gfx ChikoritaCardGfx ; gfx
+ tx ChikoritaName ; name
+ db CIRCLE ; rarity
+ db COLOSSEUM | NONE ; sets
+ db CHIKORITA
+ db 50 ; hp
+ db BASIC ; stage
+ dw NONE ; pre-evo name
+
+ ; attack 1
+ energy COLORLESS, 1 ; energies
+ tx TackleName ; name
+ dw NONE ; description
+ dw NONE ; description (cont)
+ db 10 ; damage
+ db DAMAGE_NORMAL ; category
+ dw NONE ; effect commands
+ db NONE ; flags 1
+ db NONE ; flags 2
+ db NONE ; flags 3
+ db 0
+ db ATK_ANIM_HIT ; animation
+
+ ; attack 2
+ energy GRASS, 1 ; energies
+ tx DeflectorName ; name
+ tx DeflectorDescription ; description
+ tx DeflectorDescriptionCont ; description (cont)
+ db 0 ; damage
+ db RESIDUAL ; category
+ dw ElectabuzzLightScreenEffectCommands ; effect commands
+ db NONE ; flags 1
+ db NULLIFY_OR_WEAKEN_ATTACK ; flags 2
+ db NONE ; flags 3
+ db 0
+ db ATK_ANIM_BARRIER ; animation
+
+ db 1 ; retreat cost
+ db WR_FIRE ; weakness
+ db NONE ; resistance
+ tx LeafName ; category
+ db 152 ; Pokedex number
+ db 0
+ db 12 ; level
+ db 2, 11 ; length
+ dw 14 * 10 ; weight
+ tx ChikoritaDescription ; description
+ db 0
+
CharmanderCard:
db TYPE_PKMN_FIRE ; type
gfx CharmanderCardGfx ; gfx
Here's the meaning of each field in order:
- type of card, either Grass, Fire, Water, Trainer, etc.
- card picture graphics.
- text ID of card name.
- rarity, either Circle, Diamond, Star or Promo.
- card set, which booster pack will it belong to, and also optionally an indication for the Jungle, Fossil, GB or Promo icon.
- card constant that this card corresponds to.
- amount of HP.
- what Pokémon stage it is, either Basic, 1 Stage or 2 Stage.
- text ID of pre-evolution card name, if any.
- attack 1 and attack 2:
- energy cost.
- name text ID.
- description text ID (page 1).
- description text ID (page 2).
- damage.
- category, which will be tied to how damage is calculated.
- flags, which are associated to how the AI scores each attack.
- animation index, which will play when the attack is used.
- retreat cost
- weakness, will be a WR_* constant
- resistance, will be a WR_* constant
- flavor related information
- category text ID, (i.e. "Leaf" in "Leaf Pokémon")
- Pokédex number
- level
- length, in feet and inches
- weight in lbs, which is multiplied by 10 so that the decimal can be shown
- text ID of Pokédex description
A couple of things of note here. Firstly, there's a lot of stuff that is not defined yet, namely ChikoritaCardGfx
, and ChikoritaName
, DeflectorName
, DeflectorDescription
, DeflectorDescriptionCont
, LeafName
and ChikoritaDescription
. Secondly, we are reusing the attack effect already implemented for Electabuzz's Light Screen, ElectabuzzLightScreenEffectCommands
, which has an identical effect to Deflector. That will save us the trouble of implementing the attack ourselves. When in doubt, the data of a similar card (e.g. Bulbasaur) can be copied and then you can modify what is needed. Additionally, it's important to keep in mind that this is specific to Pokémon cards. Energy cards and Trainer cards will have decidedly different formats. You can check the documentation in the disassembly to learn of how they are structured.
For the card picture, you'll need a 64x48 PNG file with only 4 colors. As for this example, we'll use the following picture:
Save it as chikorita.png inside src/gfx/cards. Then edit src/gfx.asm and insert the picture right after the Recycle card graphics:
RecycleCardGfx::
INCBIN "gfx/cards/recycle.2bpp"
INCBIN "gfx/cards/recycle.pal"
+
+ChikoritaCardGfx::
+ INCBIN "gfx/cards/chikorita.2bpp"
+ INCBIN "gfx/cards/chikorita.pal"
For this case we will put the picture at the end of the card graphics so as to not move them around the banks. By using INCBIN
, we are signalling that a .2bpp
and .pal
file need to be generated from the Chikorita PNG file, which will happen when you run make
.
We'll need to include in the ROM all the text needed for displaying the card information. Before we can add new text, we need to make some space in the bank that holds the text offsets by deleting some unused text. Edit src/text/text_offsets.asm:
TextOffsets::
...
textpointer WeaknessText ; 0x0008
textpointer ResistanceText ; 0x0009
textpointer PKMNPWRText ; 0x000a
- textpointer UnusedText000b ; 0x000b
textpointer LengthText ; 0x000c
textpointer WeightText ; 0x000d
textpointer PokemonText ; 0x000e
...
textpointer TrainerCardText ; 0x001f
textpointer EnergyCardText ; 0x0020
textpointer DeckPrinterText ; 0x0021
- textpointer UnusedText0022 ; 0x0022
textpointer NoPokemonOnTheBenchText ; 0x0023
textpointer UnableDueToSleepText ; 0x0024
textpointer UnableDueToParalysisText ; 0x0025
Then delete the text corresponding to these entries in src/text/text1.asm:
PKMNPWRText:
text "PKMN PWR"
done
-UnusedText000b: ; Unused (Pokemon Card)
- textfw "ポケモンカード"
- done
-
LengthText:
text "Length"
done
...
DeckPrinterText:
text "Deck"
done
-UnusedText0022: ; Unused
- text "Attack"
- done
-
NoPokemonOnTheBenchText:
text "No Pokémon on the Bench."
done
Now we can go ahead and add the text that we need. Edit src/text/text_offsets.asm again, to add the entries for the new text:
TextOffsets::
...
textpointer GamblerDescription ; 0x0bab
textpointer RecycleName ; 0x0bac
textpointer RecycleDescription ; 0x0bad
+ textpointer ChikoritaName
+ textpointer ChikoritaDescription
+ textpointer LeafName
+ textpointer DeflectorName
+ textpointer DeflectorDescription
+ textpointer DeflectorDescriptionCont
Finally, we are going to add the new text at the end of src/text/text13.asm:
RecycleDescription:
line "in your discard pile on top of your"
line "deck."
done
+
+ChikoritaName:
+ text "Chikorita"
+ done
+
+ChikoritaDescription:
+ text "A sweet aroma gently wafts from the"
+ line "leaf on its head. It is docile and"
+ line "loves to soak up the sun's rays."
+ done
+
+LeafName:
+ text "Leaf"
+ done
+
+DeflectorName:
+ text "Deflector"
+ done
+
+DeflectorDescription:
+ text "Whenever an attack does damage to"
+ line "Chikorita (after applying Weakness"
+ line "and Resistance) during your"
+ line "opponent's next turn, that attack"
+ line "only does half the damage to"
+ line "Chikorita (rounded down to the"
+ line "nearest 10)."
+ done
+
+DeflectorDescriptionCont:
+ text "(Any other effects of attacks still"
+ line "happen.)"
+ done
There's just one more pesky detail that needs to be addressed. The data we added for the Chikorita card makes the bank holding all the card data overflow. To be able to assemble the ROM, we will need to move some data around. Luckily, we can just move the "Cards"
section to another empty bank and it will be solved. But we can do a little trick by editing src/layout.link:
ROMX $0b
"Effect Functions"
ROMX $0c
"Decks"
- "Cards"
ROMX $0d
"Text 1"
ROMX $0e
Here we can see that the "Cards"
section was hardcoded to be in ROM $0c
. By removing the section inside layout.link
, we are creating a floating section. This means the assembler will place it in the first spot in ROM where there is space for it. If you prefer to have it hardcoded, you can alternatively move it to an empty bank (say ROM $0a
).
And that is it, you just created your first card!