Skip to content
RealCataclyptic edited this page Jun 18, 2026 · 3 revisions

This tutorial will allow you to assign a point score to every card in the game and reference either individual cards or all cards in the deck. By doing this, one can create several fun scenarios in a game, like implementing alternate game modes based on point limitations, disallowing duels unless the deck contains X points or less/more, and so much more because the system is so flexible.

Note that this tutorial uses base poketcg as its code source, if you are using another base then some lines may need to be edited.

poketcg poketcg9 poketcg5 poketcg2

Contents

  1. Point System Versions
  2. Creating the Full Control Version
  3. Printing Points on Cards
  4. Print Total Points in a Deck
  5. Use Case 1: Duelists
  6. Use Case 2: Hard Mode
  7. Use Case 3: Card Trading
  8. Use Case 4: Pocket TCG Style Points

0. Point System Versions

There are two ways to create a point system in poketcg. The first system is the Full Control version, which allows you to assign any point value on any card, with no restrictions. This version is the harder of the two to implement because it requires adding a new variable to card data in order for it to work.

The second system is the Rarity version. This one is the easier of the two to make, it essentially assigns all cards of X rarity X points. All commons for instance would be worth 1 point, all uncommons 2, all rares 4, or whatever value you wish. The main limitation of this system is that you cannot alter points between rarities; all commons must be worth exactly 1 point with no variation, for instance.

If you wish to use the Full Control System, proceed to step 1. If you wish to use the Rarity system, proceed to step 2 instead.


1. Creating the Full Control Version

In order to maximize the most space and to ensure nigh universal compatibility between bases, the full control version will utilize the CARD_DATA_UNUSED byte that is already present in the code. Note: At any point in this tutorial, you may change the name of the Unused byte to whatever you want, but this tutorial will stick with the original "unused" name.

Warning: The following actions will increase the size of cards.asm. If you are using the base version of poketcg, you will have to delete 1 card from cards.asm in order for the data to fit. (Imakuni? is the reccomended card to delete) This warning does not apply if you can add an additional cards2.asm or any base which has that.

Go to constants -> card_data_constants, find the unused byte and delete it:

; 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
...

Then go up above to the 'all cards types' section and place it back in just below rarity:

; all card types
DEF CARD_DATA_TYPE                  EQU $00
DEF CARD_DATA_GFX                   EQU $01
DEF CARD_DATA_NAME                  EQU $03
DEF CARD_DATA_RARITY                EQU $05
+DEF CARD_DATA_UNUSED                EQU $38

Now that the card data definition has been moved, we now go to data -> cards.asm in order to move all instances of the unused byte from below pokedex number to below rarity. This is incredibly tedious to do by hand, so it recommended to instead do the following:

Using VS code or a similar code modifier tool, highlight both "; pokedex number" and the db 0 below it, then right click and select "change all occurrences", replace it with just "; pokedex number" to delete the db 0. Next, using the same logic, highlight ";rarity", select "change all occurrences" and then replace it with "; rarity", enter and "db 1". This will make all card point values 1 for now and in the correct position for the game to process our new code.

This is done to ensure that ALL cards can be assigned point values, not just pokemon cards. If this is something you don't care about and only want to assign points to pokemon, you may skip the above steps. If not, you will have to delete 1 card from cards.asm if using base poketcg as stated in the warning.


2. Printing Points on Cards

Regardless of if you did step 1 or wanted to do the rarity version, they both converge here. You now have points that can be assigned to cards, however the player will never know it because it won't show up. So our next task is to make it clear which cards are worth what points.

First, go to both text_offsets and a free text document and make a points sign, this tutorial's looks like this:

PointsText:
	text "Points:"
	done

Next, go to engine -> duel -> core.asm and put in the following:

PointsText2: ; core version
+	textitem 15, 6, PointsText ; coordinates of where we want "points:" to be
+	db $ff
+	ret```

And then we will create the WritePoints code.

```diff
+; Full Control Version
+WritePoints:
+	ld hl, PointsText2      ; Loads the core version of the text we want
+	call PlaceTextItems     
+	lb bc, 15, 7 		; prints point value above the rarity symbol at these coordinates
+	ld a, [wLoadedCard1Unused]  ; Loads the value in "unused", AKA points
+	call WriteTwoDigitNumberInTxSymbolFormat ; And writes it in text format
+	ret

That is the full control version. If you are using the rarity version, instead you will write it like this:

; Rarity Version
+WritePoints: ; POINTS MOD, Full control and rarity based version
+	ld hl, PointsText2
+	call PlaceTextItems 
+	lb bc, 15, 7 		; same as above code
+	ld a, [wLoadedCard1Rarity] ; loads card rarity
+	cp CIRCLE               ; check if it's a circle card
+	jr nz, .CheckUncommon   ; if not go to check if it's an uncommon
+	ld a, 1                 ; if yes, load 1 (point)
+	jr .FoundRarity         ; and go to the end where it can written
+.CheckUncommon                 ; repeat until all card rarities have been tested
+	cp DIAMOND
+	jr nz, .CheckRare
+	ld a, 2
+	jr .FoundRarity 
+.CheckRare
+	cp STAR
+	jr nz, .CheckPromo
+	ld a, 3
+	jr .FoundRarity 
+.CheckPromo
+	ld a, 4
+.FoundRarity
+	call WriteTwoDigitNumberInTxSymbolFormat
+	ret

And then we need to find a good place to report these values. For this tutorial, they will be printed on the last page of a pokemon card and the first page of a trainer or energy card.

DisplayEnergyOrTrainerCardPage:
	...
        ...
	call DrawCardPageSet2AndRarityIcons
	pop hl
	call PrintAttackOrNonPokemonCardDescription
+	call WritePoints 
        ret


DisplayCardPage_PokemonDescription:
        ...
        ...
.print_description
	ld a, 19 ; line length
	call InitTextPrintingInTextbox
	ld hl, wLoadedCard1Description
	call ProcessTextFromPointerToID
	call SetOneLineSeparation
+	call WritePoints
	ret

And there you have it, every card in the game will now display a point value in an area that easy to access for the player. However, there's one more useful thing we can print for the player: total amount of points in the current deck.


3. Print Total Points in a Deck

Go to engine -> menus -> deck_configuration and put in the following code to tally up all the points in the current player's deck:

; Full control version
GetTotalPointsInPlayersDeck:
	push hl
	push bc
	push de
	ld hl, wCurDeckCards
	ld c, 0
.LoopCards
	ld a, [hli]
	ld e, a
;	ld a, [hli] 	; Uncomment if using the Extended Base
;	ld d, a		; Uncomment if using the Extended Base
	or e
	jr z, .done
	call LoadCardDataToBuffer1_FromCardID
	ld a, [wLoadedCard1Unused] 
	add c
	ld c, a
	jr .LoopCards
.done
	ld a, c
	pop de
	pop bc
	pop hl
	ret

Or, if you're using the rarity version,

; Rarity version
GetTotalPointsInPlayersDeck: 
	push hl
	push bc
	push de
	ld hl, wCurDeckCards
	ld c, 0
.LoopCards
	ld a, [hli]
	ld e, a
;	ld a, [hli]	; Uncomment if using the Extended Base
;	ld d, a		; Uncomment if using the Extended Base
	or e
	jr z, .done
	call LoadCardDataToBuffer1_FromCardID
	ld a, [wLoadedCard1Rarity]
	cp CIRCLE
	jr nz, .CheckUncommon
	ld a, 1
	jr .FoundRarity 
.CheckUncommon
	cp DIAMOND
	jr nz, .CheckRare
	ld a, 2
	jr .FoundRarity 
.CheckRare
	cp STAR
	jr nz, .CheckPromo
	ld a, 3
	jr .FoundRarity 
.CheckPromo
	ld a, 4
.FoundRarity 
	add c
	ld c, a
	jr .LoopCards
.done
	ld a, c
	pop de
	pop bc
	pop hl
	ret

Now that we've put in functions that can get the total number of points in a player's deck, it's time to report them to the player.

; prints the total number of points in a deck at coordinates bc
+PrintTotalPointsNumber: 
+	call GetTotalPointsInPlayersDeck
+	lb bc, 5, 0
+	bank1call WriteTwoByteNumberInTxSymbolFormat
+	ret

+"prints points:"
+PointsText: 
+	textitem 1, 0, PointsText
+	db $ff
+	ret


DrawCardTypeIconsAndPrintCardCounts:
	call Set_OBJ_8x8
	call PrepareMenuGraphics
	lb bc, 0, 5
	ld a, SYM_BOX_TOP
	call FillBGMapLineWithA
	call DrawCardTypeIcons		
	call PrintCardTypeCounts 	
	lb de, 15, 0			
	call PrintTotalCardCount	
	lb de, 17, 0			
	call PrintSlashSixty		

+	ld hl, PointsText 		; Places the text "points:" in a specified location
+	call PlaceTextItems			
+	call PrintTotalPointsNumber     ; prints total points in this space here
        call EnableLCD
	ret

The UI for the deck configuration menu will now display the total number of points in the current deck. Now this on its own is fine, but we can go even further by causing the point values to update in real time as the player adds and removes cards from the deck. To do this, add the following:

AddCardToDeckAndUpdateCount:
	call TryAddCardToDeck
	ret c ; failed to add card
	push de
	call PrintCardTypeCounts
	lb de, 15, 0
	call PrintTotalCardCount
+	call PrintTotalPointsNumber
	pop de
	call GetCountOfCardInCurDeck
	call PrintNumberValueInCursorYPos
	ret


RemoveCardFromDeckAndUpdateCount:
	call RemoveCardFromDeck
	ret nc
	push de
	call PrintCardTypeCounts
	lb de, 15, 0
	call PrintTotalCardCount
+	call PrintTotalPointsNumber 
	pop de
	call GetCountOfCardInCurDeck
	call PrintNumberValueInCursorYPos
	ret

And that's it for the basic system. Doing all of the above will create a points system and make it clear visually to the player how many points a given card is worth and how many points the current deck has. However, right now all of this is just visuals with no substance- the points still don't really do anything.

You can of course use this points to do whatever you want by referencing the new functions, but this tutorial will now diverge a little bit to explore some things one can do with the points system.


4. Use Case 1: Duelists

What if certain NPC duelists won't duel you if your deck's point total is too high? Or too low? This section will create that situation, allowing for diversity among the NPCs in your game.

First, we need to create a new script command that allows the game to calculate the number of points in the current deck, and then jump to a label if it's greater than a certain number. In order to save space, we will overwrite one of the unused "end scripts" and suit it for our purposes. Go to macros -> scripts.asm and go to the bottom of the script_command list.

const ScriptCommand_JumpIfEventFalse_index                               ; $62
	const ScriptCommand_IncrementEventValue_index                            ; $63
-       const ScriptCommand_EndScript7_index                                     ; $64
+	const ScriptCommand_JumpIfEnoughDeckPoints_index                         ; $64
	const ScriptCommand_EndScript8_index                                     ; $65
	const ScriptCommand_EndScript9_index                                     ; $66
	const ScriptCommand_EndScript10_index                                    ; $67

Then we write the actual script. Remember to place it in the same spot as the constant, so in this case we have to write it underneath IncrementEventEalue_index.

; Increments given event's value (truncates the new value)
MACRO increment_event_value
	run_command ScriptCommand_IncrementEventValue
	db \1 ; event (ex EVENT_IMAKUNI_WIN_COUNT)
ENDM

+; Jumps to a label if points are equal to or greater than a value
+MACRO Jump_If_Enough_Deck_Points
+	run_command ScriptCommand_JumpIfEnoughDeckPoints
+	db \1 ; amount of points needed
+	dw \2 ; script label
+ENDM

With the macro defined, go to data -> script_table.asm and define it there too. Again, place it in the exact position the game expects it to be.

...
dw ScriptCommand_JumpIfEventFalse
	dw ScriptCommand_IncrementEventValue
-       dw ScriptCommand_EndScript
+	dw ScriptCommand_JumpIfEnoughDeckPoints
	dw ScriptCommand_EndScript
	dw ScriptCommand_EndScript
	dw ScriptCommand_EndScript
	assert_table_length NUM_SCRIPT_COMMANDS

Finally, go to engine -> overworld -> scripting.asm where we tell the script what we need it to do. Once again, define it where the game expects it to be.

ScriptCommand_IncrementEventValue:
	ld a, c
	push af
	call GetEventValue
	inc a
	ld c, a
	pop af
	call SetEventValue
	jp IncreaseScriptPointerBy2

+ScriptCommand_JumpIfEnoughDeckPoints:
+  	push bc
+  	farcall GetTotalPointsInPlayersDeck ; gets total points in the current player's deck
+  	pop bc
+ 	cp c           ; Subtracts from a define number c in the script
+ 	jp c, ScriptCommand_JumpIfEventFalse.fail      ; if it's less, go to the false jump
+	jp ScriptCommand_JumpIfEventTrue.pass_try_jump  ; otherwise go to the true jump

We can now use this command however we want. As an example, let's pick on Jennifer from the Lightning Club. As in the photo above, let's say that if you have over 100 points in your deck, she'll refuse to duel you. To set this up, go to her script in scripts -> lightning_club.asm.

Script_Jennifer:
	start_script
	print_npc_text Text061b      ; "... "Hey, do you want to duel my Pikachu Deck?"
	ask_question_jump Text061c, .ows_e415     ; Ask yes or no, if yes, jump to .ows_e415
	print_npc_text Text061d
	quit_script_fully

.ows_e415 
; The macro now process the number next to it and stores it in c to cp it as a test.
	Jump_If_Enough_Deck_Points 100, .TooManyPoints  ; If player's deck points >100, go to .Too Many Points
	print_npc_text Text061e
	start_duel PRIZES_4, PIKACHU_DECK_ID, MUSIC_DUEL_THEME_1   ; otherwise print normal duel text and start the duel.
	quit_script_fully

.TooManyPoints
	print_npc_text TooManyPoints   ; "Hey! Your deck has over 100 points! I won't duel you!"
	quit_script_fully              ; End the dialouge

You can also switch around the logic of this jump in order to make an NPC duel you if your deck has below 100 points in this way.


5. Use Case 2: Hard Mode

We all know that the NPCs in this game aren't very hard to beat. Yes we can give them better decks, but the AI can only do so much even with the sequel's improvements.

So what if instead, it was possible to make the game harder via another axis? Using the points system, it becomes possible to limit how good the player's deck can be by capping it at a maximum number of points! In order to not make this tutorial too long, implementing a hard mode has been done in a different tutorial, found here: New Game Modes. But you have a new game mode enabled, read on for how to do a hard mode using the points system.

Since the points system assigns point values to every card, ranging from 0 to 4, these points can be tallied up for the entire deck and displayed on screen. This fact can be used to create a new, more difficult mode, wherein the NPCs have no restrictions but the player's deck must be below a certain number of points. In the example above, the maximum point total was 100, so we will reference that as an example of how to do hard mode using points.

Special note: It is not recommended to have your card's point values be 5 or above. This is because the game can only display values between 0-255, with 4 as the max point value, the maximum number of points in a deck is 240, under the limit. But with 5+ it may go over the limit and cause strange graphical errors.

First to establish a points system hard mode, go to engine -> menus -> deck_configuration and add the following under DrawCardTypeIconsAndPrintCardCounts:

DrawCardTypeIconsAndPrintCardCounts:
	call Set_OBJ_8x8
	call PrepareMenuGraphics
	lb bc, 0, 5
	ld a, SYM_BOX_TOP
	call FillBGMapLineWithA
	call DrawCardTypeIcons		
	call PrintCardTypeCounts 	
	lb de, 15, 0			
	call PrintTotalCardCount	
	lb de, 17, 0			
	call PrintSlashSixty		

+	farcall IsHardModeEnabled       ; Check if hard mode is enablded
+	jr c, .No                       ; if not, go to .no
+	ld hl, PointsText 		; places "Points:" on the deck building screen
+	call PlaceTextItems			
+	call PrintTotalPointsNumber     ; Places the total number of points in the deck on the deck building screen
+	lb de, 8, 0                     ; coordinates of...
+	call PrintSlashMaxPoints        ; A symbol displaying the maximum number of points a deck should have
+.No
	call EnableLCD
	ret

+PrintSlashMaxPoints: ; Prints "/X" where x is the maximum amount of points
+	ld hl, wDefaultText
+	ld a, TX_SYMBOL
+	ld [hli], a
+	ld a, SYM_SLASH ; print /
+	ld [hli], a
+	ld a, TX_SYMBOL
+	ld [hli], a
+	ld a, SYM_1 ; print 1
+	ld [hli], a
+	ld a, TX_SYMBOL
+	ld [hli], a
+	ld a, SYM_0 ; print 0
+	ld [hli], a
+	ld a, TX_SYMBOL
+	ld [hli], a
+	ld a, SYM_0 ; print 0
+	ld [hli], a
+	ld [hl], TX_END
+	call InitTextPrinting
+	ld hl, wDefaultText
+	call ProcessText
+	ret

Do a similar check for hard mode in engine -> duel -> core.asm for the card page descriptions

DisplayEnergyOrTrainerCardPage:
...
...
	call FillRectangle
	; print the set 2 icon and rarity symbol of the card
	call DrawCardPageSet2AndRarityIcons
	pop hl
	call PrintAttackOrNonPokemonCardDescription

+	farcall IsHardModeEnabled ; Hard mode mod
+	jr c, .No
+	call WritePoints ; print points above rarity symbol if trainer or energy.
+.No
	ret

DisplayCardPage_PokemonDescription:
...
...
	jr nc, .print_description
	inc e ; move a line down, as the description is short enough to fit in three lines
.print_description
	ld a, 19 ; line length
	call InitTextPrintingInTextbox
	ld hl, wLoadedCard1Description
	call ProcessTextFromPointerToID
	call SetOneLineSeparation
+	farcall IsHardModeEnabled ; hard mode mod
+	jr c, .No
+	call WritePoints
+.No
	ret

So, what just happened? Well, assuming the codebase already has the points system in place, we've created a situation where in the deck building menu and in the card descriptions menu, the game checks if hard mode has been enabled and if so, it will display the number of points in the current card or deck and a /XX in the deck building menu where X is the number of maximum points you want the player to have (in this example, 100).

All of this only occurs if hard mode is enabled, since we defined normal mode to have no restrictions, so adding points in normal mode would just be confusing for the player. With that being said, currently the code only displays visual elements of hard mode, so the next task is to force the player into abiding by the point restrictions of hard mode.

Further down in deck_configuration, we find the SaveDeckConfiguration: function. Here is where we insert some new code.

SaveDeckConfiguration:
+	farcall IsHardModeEnabled               ; check if hard mode is enabled
+	jr c, .Checkif60Cards                   ; if not, go to the second check of if the deck has 60 cards
+	call GetTotalPointsInPlayersDeck        ; Get all the points in the players deck
+	cp 100					; Points needed, must be equal to or less to be valid.
+	jr c, .Checkif60Cards                   ; If < 100, go to the second check of if the deck has 60 cards
+	ldtx hl, ThisDeckHasTooManyPoints       ; if >100, tell the player that they have too many points in deck
+	call DrawWideTextBox_WaitForInput
+	jr .ReturntoConfigQuestion_Points       ; then go to a specific menu for points

+.Checkif60Cards
; handle deck configuration size
	ld a, [wTotalCardCount] 		
	cp DECK_SIZE				
	jr z, .ask_to_save_deck ; can be jr	
	ldtx hl, ThisIsntA60CardDeckText	
	call DrawWideTextBox_WaitForInput	
	ldtx hl, ReturnToOriginalConfigurationText	
	call YesOrNoMenuWithText
	jr c, .print_deck_size_warning
; return no carry
	add sp, $2
	or a
	ret
.print_deck_size_warning
	ldtx hl, TheDeckMustInclude60CardsText
	call DrawWideTextBox_WaitForInput
	jr .go_back

; basically the same as the "not 60 card" warning above, but with text specific to points. 
+.ReturntoConfigQuestion_Points				
+	ldtx hl, ReturnToOriginalConfigurationText
+	call YesOrNoMenuWithText        ; ask to return deck to its original configuration
+	jr c, .print_points_warning     ; if "no" is selected, print a second warning and do not save deck
+; return no carry
+	add sp, $2
+	or a
+	ret
+.print_points_warning
+	ldtx hl, ThisDeckNeedsXOrLessPoints
+	call DrawWideTextBox_WaitForInput
+	jr .go_back				


.ask_to_save_deck
	ldtx hl, SaveThisDeckText
	call YesOrNoMenuWithText

And then in text_offsets and another text document, add the text for all of this.

	textpointer ThisDeckHasTooManyPoints
	textpointer ThisDeckNeedsXOrLessPoints
ThisDeckHasTooManyPoints:
	text "This deck has over 100 points!"
	done

ThisDeckNeedsXOrLessPoints:
	text "This deck needs 100 points or less!"
	done

With this code, the game checks if hard mode is enabled; if no, go to the normal 60 card deck check from the base game. But if yes, check the player's total deck points against a number (100 in this case) and if the points value is greater, go through a series of options where the player either has to return their deck to the original configuration or the deck simply will not save. Either way, the player is now forced to abide by the point restrictions set by you, thus making it harder to build a good deck!


6. Use Case 3: Card Trading

The points system can also be used as a type of in game currency. For example, it is possible for an NPC to remove cards from the player's collection and store the total point value of those cards. Then, if the player has enough points, they can purchase a booster pack, or perhaps a rare card.

[This section of the tutorial is not made]


7. Use Case 4: Pocket TCG Style Points

Pokemon TCG Pocket, the popular mobile app made in 2024, uses a unique system compared to the official trading card game in that players do not take prizes when KOing a pokemon, but instead take a certain number of points. When enough points are accumulated, that player wins the duel. The points system described here can be used to replicate that effect by setting all pokemon to give some points on KO and altering the win condition to being victorious after a certain number of points have been gotten.

[This section of the tutorial is not made]

Clone this wiki locally