1 Expanded Weakness Resistance Modifiers
RealCataclyptic edited this page 2026-05-05 20:34:15 -06:00

In the official trading card game, Pokemon's weaknesses and resistances are relatively static. If you are weak to a type, you take x2 or +20 or whatever extra damage from those attacks. Same with resistance. This makes sense for a paper game so that the rules can be more easily explained.

But here in the TCG GB, we have digitalized cards. And with hacking, it becomes possible to go beyond this system set in place and in a way that is easily understandable. Which is why in this tutorial, we create a function to have complete control over the weaknesses and resistances of every Pokémon. Each and every pokemon card can take any amount of extra damage for weakness, and any amount less damage for resistance.

PoketcgWRfullmod
  1. Initial Setup
  2. Adding the New Code
  3. Graphical Changes
  4. How to Edit

1. Initial Setup

In order to add a modifier value to weaknesses and resistances easily, you have to create new categories that go into cards.asm and wram.asm. However, this will also increase the total size of every card in the file, and the fact that cards sizes are larger may mess with other parts of the game. Although it is totally possible to do, due to the inherent difficulty of modifying card sizes and everything associated with it, this tutorial will instead use a more 'universal' approach that works with any version of poketcg.

Instead of adding new data, we will re-appropriate two bytes to set up the functions: Length and Width. Most people glaze over this data when playing the game, so most people will not miss them. This lets us not change the size of cards while adding the full modifier controls.

First, delete everything that has to do with length and width, including the text just to be safe. These locations are found in:

Engine -> Duel -> Core

DisplayCardPage_PokemonDescription:
	DisplayCardPage_PokemonDescription:
	; print surrounding box, card name at 5,1, type, set 2, and rarity
	call PrintPokemonCardPageGenericInformation
	call LoadDuelCardSymbolTiles2
-	; print "LENGTH", "WEIGHT", "Lv", and "HP" where it corresponds in the page
-	ld hl, CardPageLengthWeightTextData
-	call PlaceTextItems
	ld hl, CardPageLvHPTextTileData
	call WriteDataBlocksToBGMap0
	; draw the card symbol associated to its TYPE_* at 3,2
	lb de, 3, 2
	call DrawCardSymbol
	; print the Level and HP numbers at 12,2 and 16,2 respectively
	lb bc, 12, 2
	ld a, [wLoadedCard1Level]
	call WriteTwoDigitNumberInTxSymbolFormat
	lb bc, 16, 2
	ld a, [wLoadedCard1HP]
	call WriteTwoByteNumberInTxSymbolFormat
	; print the Pokemon's category at 1,10 (just above the length and weight texts)
	lb de, 1, 10
	ld hl, wLoadedCard1Category
	call InitTextPrinting_ProcessTextFromPointerToID
	ld a, TX_KATAKANA
	call ProcessSpecialTextCharacter
	ldtx hl, PokemonText
	call ProcessTextFromID
-	; print the length and weight values at 5,11 and 5,12 respectively
-	lb bc, 5, 11
-	ld hl, wLoadedCard1Length
-	ld a, [hli]
-	ld l, [hl]
-	ld h, a
-	call PrintPokemonCardLength
-	lb bc, 5, 12
-	ld hl, wLoadedCard1Weight
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	call PrintPokemonCardWeight
-	ldtx hl, LbsText
-	call InitTextPrinting_ProcessTextFromID
	; print the card's description without line separation
	call SetNoLineSeparation
	ld hl, wLoadedCard1Description
	ld a, [hli]
	ld h, [hl]
	ld l, a
	call CountLinesOfTextFromID
	lb de, 1, 13
	cp 4
	jr nc, .print_description
	inc e ; move a line down, as the description is short enough to fit in three lines

...

-CardPageLengthWeightTextData:
-	textitem 1, 11, LengthText
-	textitem 1, 12, WeightText
-	db $ff

...

-PrintPokemonCardWeight:
- (delete this whole section)

-PrintPokemonCardLength:
- (delete this whole section)

Then, delete LengthText and WeightText found in text_offsets and text1.

Once you have deleted everything associated with length and weight, it's time to modify a few factors.

First, go to constants -> card_data_constants and alter the following:

...
; TYPE_PKMN card only
DEF CARD_DATA_RETREAT_COST          EQU $32
DEF CARD_DATA_WEAKNESS              EQU $33
DEF CARD_DATA_RESISTANCE            EQU $34
DEF CARD_DATA_CATEGORY              EQU $35
DEF CARD_DATA_POKEDEX_NUMBER        EQU $37
DEF CARD_DATA_UNUSED                EQU $38
DEF CARD_DATA_LEVEL                 EQU $39
-DEF CARD_DATA_LENGTH               EQU $3a
-DEF CARD_DATA_WEIGHT                EQU $3c
+DEF CARD_DATA_WEAKNESS_MOD               EQU $3a
+DEF CARD_DATA_RESISTANCE_MOD                EQU $3c
DEF CARD_DATA_PKMN_DESCRIPTION      EQU $3e
DEF CARD_DATA_AI_INFO               EQU $40

Next, go to macros -> wram.asm:

...
\1PokedexNumber:: ds 1
ds 1
\1Level::         ds 1
-\1Length::        ds 2
-\1Weight::        ds 2
+\1WeaknessMod::        ds 2
+\1ResistanceMod::        ds 2
\1Description::   ds 2
\1AIInfo::        ds 1
...

The setup is complete, now we can the actual effects.


2. Adding the New Code

To define the logic of the new code, we have to go to the the part where the game calculated weaknesses and resistances. This is found in home -> duel under ApplyDamageModifiers_DamageToTarget:: and ApplyDamageModifiers_DamageToSelf::, both of which use similar code.


ApplyDamageModifiers_DamageToTarget::
...
	
.affected_by_wr
	call HandleDoubleDamageSubstatus
	ld a, e
	or d
	ret z
	ldh a, [hTempPlayAreaLocation_ff9d]
	call GetPlayAreaCardColor
	call TranslateColorToWR
	ld b, a
	call SwapTurn
	call GetArenaCardWeakness
	call SwapTurn
	and b
	jr z, .not_weak
-	sla e
-	rl d
+       call SwapTurn 
+	ld a,  DUELVARS_ARENA_CARD
+	call GetTurnDuelistVariable ; Gets the opponent's card info
+	call LoadCardDataToBuffer2_FromDeckIndex 
+       ld a, [wLoadedCard2WeaknessMod] ; loads the new weakness modifier we just put in
+	call SwapTurn
+	ld l, a
+	ld h, 0
+	add hl, de ; loads the extra damage to de for weakness

	ld hl, wDamageEffectiveness
	set WEAKNESS, [hl]
.not_weak
	call SwapTurn
	call GetArenaCardResistance
	call SwapTurn
	and b
	jr z, .check_pluspower_and_defender ; jump if not resistant
-	ld hl, -30

+       call SwapTurn
+	ld a,  DUELVARS_ARENA_CARD
+	call GetTurnDuelistVariable
+	call LoadCardDataToBuffer2_FromDeckIndex
+       ld a, [wLoadedCard2ResistanceMod] ; loads the resistance modifier
+	ld l, a
+	ld a, [wLoadedCard2ResistanceMod + 1] ; helps to process to make it a negative value
+	ld h, a
+	call SwapTurn
	add hl, de ; adds the negative value to de

	ld e, l
...


ApplyDamageModifiers_DamageToSelf::
...
	jr z, .not_weak
-	sla e
-	rl d
+       call SwapTurn ; basically the same logic as before
+	ld a,  DUELVARS_ARENA_CARD
+	call GetTurnDuelistVariable
+	call LoadCardDataToBuffer2_FromDeckIndex
+       ld a, [wLoadedCard2WeaknessMod]
+	call SwapTurn
+	ld l, a
+	ld h, 0
+	add hl, de


	ld hl, wDamageEffectiveness
	set WEAKNESS, [hl]
.not_weak
	call GetArenaCardResistance
	and b
	jr z, .not_resistant
-	ld hl, -30

+       ld a,  DUELVARS_ARENA_CARD 
+	call GetTurnDuelistVariable
+	call LoadCardDataToBuffer2_FromDeckIndex
+       ld a, [wLoadedCard2ResistanceMod]
+	ld l, a
+	ld a, [wLoadedCard2ResistanceMod + 1]
+	ld h, a
+	call SwapTurn


	add hl, de
	ld e, l
	ld d, h
	ld hl, wDamageEffectiveness
	set RESISTANCE, [hl]
.not_resistant
	ld b, CARD_LOCATION_ARENA
	call ApplyAttachedPlusPower
	ld b, CARD_LOCATION_ARENA
	call ApplyAttachedDefender
	bit 7, d ; test for underflow
	ret z
.no_damage
	ld de, 0
	ret

The function is now active! One problem though: the AI will not understand this. Therefore, we should let the AI know what we did. Using similar code to what was just done, go to duel -> AI -> damage_calculation and input the new code.


CalculateDamage_VersusDefendingPokemon:
...

; handle weakness
	ldh a, [hTempPlayAreaLocation_ff9d]
	call GetPlayAreaCardColor
	call TranslateColorToWR
	ld b, a
	call SwapTurn
	call GetArenaCardWeakness
	call SwapTurn
	and b
	jr z, .not_weak
-       sla e
-	rl d

+	call SwapTurn 
+	ld a,  DUELVARS_ARENA_CARD
+	call GetTurnDuelistVariable
+	call LoadCardDataToBuffer2_FromDeckIndex
+        ld a, [wLoadedCard2WeaknessMod]
+	call SwapTurn
+	ld l, a
+	ld h, 0
+	add hl, de
...

.not_weak
; handle resistance
	call SwapTurn
	call GetArenaCardResistance
	call SwapTurn
	and b
	jr z, .not_resistant
-	ld hl, -30
+       call SwapTurn
+	ld a,  DUELVARS_ARENA_CARD
+	call GetTurnDuelistVariable
+	call LoadCardDataToBuffer2_FromDeckIndex
+        ld a, [wLoadedCard2ResistanceMod]
+	ld l, a
+	ld a, [wLoadedCard2ResistanceMod + 1]
+	ld h, a
+	call SwapTurn
	add hl, de
	ld e, l
	ld d, h

...

CalculateDamage_FromDefendingPokemon:
...

.unchanged_weak
	and b
	jr z, .not_weak
	; double de
-	sla e
-	rl d

+       call SwapTurn
+	ld a,  DUELVARS_ARENA_CARD
+	call GetTurnDuelistVariable
+	call LoadCardDataToBuffer2_FromDeckIndex
+        ld a, [wLoadedCard2WeaknessMod]
+	call SwapTurn
+	ld l, a
+	ld h, 0
+	add hl, de


.not_weak
; handle resistance
...
.unchanged_res
	and b
	jr z, .not_resistant
-	ld hl, -30
+       call SwapTurn ; WR MOD
+	ld a,  DUELVARS_ARENA_CARD
+	call GetTurnDuelistVariable
+	call LoadCardDataToBuffer2_FromDeckIndex
+       ld a, [wLoadedCard2ResistanceMod]
+	ld l, a
+	ld a, [wLoadedCard2ResistanceMod + 1]
+	ld h, a
+	call SwapTurn
	add hl, de
	ld e, l
	ld d, h
...

That's it, the code is now complete. However, before we get into what to do in order to completely modify any weakness and resistance to nearly any value, one more very important thing has to be done.


3. Graphical Changes

Since we just upended the weaknesses and resistances formulas, how is the player going to know what any given weakness or resistance is? After all, the game will still only show something as being weak or resistant to something and that's it. We need to go into the graphics portion in order to effectively communicate what any given weakness or resistance is on a Pokémon.

Go to Engine -> duel -> core and track down DisplayCardPage_PokemonOverview: first of all to add a new section.


DisplayCardPage_PokemonOverview:
...

.got_wr
	ld a, d
	ld b, 8 
	call PrintCardPageWeaknessesOrResistances
	inc c ; 16
	ld a, e
	call PrintCardPageWeaknessesOrResistances
+	call CardPageWRModifiersOnlyData
	ret

And as you might expect, we'll add this section. Pick a blank space somewhere in this file and add the following:

+ CardPageWRModifiersOnlyData:
+	ld a, [wLoadedCard1WeaknessMod] ; load the weakness modifier
+	cp 0 ; check against 0
+	jr z, .CheckResistance ; if 0, go to the resistance check
+	lb bc, 9, 15
+	ld a, SYM_PLUS
+	call WriteByteToBGMap0 ; otherwise make a plus symbol and add it to coordinates 9,15
+	lb bc, 10, 15  ; at coordinates 10, 15...
+	ld hl, wLoadedCard1WeaknessMod
+	ld a, [hli]
+	ld l, [hl]
+	ld h, a ; -convert the weakness modifier to a,
+	call WriteTwoDigitNumberInTxSymbolFormat ; - then write it
+.CheckResistance
+	ld a, [wLoadedCard1ResistanceMod] ; load the resistance modifier
+	cp 0
+	jr z, .Done ; if 0, go to .done
+	lb bc, 9, 16
+	ld a, SYM_MINUS 
+	call WriteByteToBGMap0 ; write a minus symbol at these coordinates
+	lb bc, 10, 16
+	ld hl, wLoadedCard1ResistanceMod 
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a ; convert the resistance number to a
+	xor $ff
+	inc a ; convert from a negative number to a positive displayed number for the purposes of graphics
+	call WriteTwoDigitNumberInTxSymbolFormat ; and write it
+.Done
+	ret

Now, we have added data to the main page of the Pokémon card that accurately reflects changes made to the card. Like the image at the very top of this tutorial, the game will now display your weaknesses and resistances as + [number] and -[number] respectively. Furthermore, if the value is 0, it simply won't be displayed at all to eliminate unnecessary clutter.


4. How to Edit

Finally, you can now edit the values of each individual Pokémon card to be almost whatever you want. To edit in new numbers, go to any Pokémon's data, like Bulbasaur's for instance:

        db 1 ; retreat cost
	db WR_FIRE ; weakness
	db NONE ; resistance
	tx SeedName ; category
	db 1 ; Pokedex number
	db 0
	db 13 ; level
	db 2, 4 ; length
	dw 15 * 10 ; weight
	tx BulbasaurDescription ; description
	db HAS_EVOLUTION ; AI info```

As you might expect, at first these data values will be completely wacky because we just upended length and weight, so the game will interpret 2, 4 to be something odd. You will have to edit every single Pokémon from this point forward for the game to function, sadly. But back to the editing, the first thing you must do is convert the db of "length" into dw, this allows the game to process what's going on in all the above functions. From there, edit the former length and width data to 2 digits or a 0. However, with resistance, always make it a negative number. This is because in the new resistance math, the game will add that number to de, the damage dealt by a Pokémon. Making it a positive number will result in a card taking more damage from resistance.

So, if someone wanted to give Bulbasaur an extra resistance to water while keeping the fire weakness, it would look like this:

        db 1 ; retreat cost
	db WR_FIRE ; weakness
+	db WR_WATER ; resistance
	tx SeedName ; category
	db 1 ; Pokedex number
	db 0
	db 13 ; level
+	dw 20 ; weakness mod, adds 20 to weakness damage
+	dw -80 ; resistance mod, subtracts 80 from resistance damage
	tx BulbasaurDescription ; description
	db HAS_EVOLUTION ; AI info```

There are some limitations to keep in mind while doing this. First, a limitation of the code is that the values have to be 2 digits or zero. anything from 0 to 99 is valid, 100 or more will cause graphical errors. Second, if you add multiple weaknesses or resistances, they will be the same value as the modifier you put in. For instance, you cannot give Bulbasaur a +10 weakness to fire and a +20 weakness to psychic. But aside from these two minor things, the sky is the limit when it comes to how much you want to edit the weakness and resistance data.