Use unique colors for each thrown Poké Ball - pret/pokecrystal GitHub Wiki
Gen 2 showed colored Poké Balls in battle, but the colors were picked from a limited set of six. Gen 3 started showing each Poké Ball with its unique graphics and colors.
This tutorial will add unique colors for each type of Ball, although they'll all still use the same graphic.
(The code for this feature was adapted from Sour Crystal.)
Note: For older versions of the pokecrystal disassembly, the 'BATTLE_ANIM_GFX_POKE_BALL' AnimObjGFX index was 'ANIM_GFX_POKE_BALL'. It was changed on Sep 18, 2023.
Contents
- Define unique colors for each Ball
- Make every Ball use
PAL_BATTLE_OB_RED
- Load the unique colors into the palette
1. Define unique colors for each Ball
Edit data/battle_anims/ball_colors.asm:
; colors of balls thrown in battle
BallColors:
- db MASTER_BALL, PAL_BATTLE_OB_GREEN
- db ULTRA_BALL, PAL_BATTLE_OB_YELLOW
- db GREAT_BALL, PAL_BATTLE_OB_BLUE
- db POKE_BALL, PAL_BATTLE_OB_RED
- db HEAVY_BALL, PAL_BATTLE_OB_GRAY
- db LEVEL_BALL, PAL_BATTLE_OB_BROWN
- db LURE_BALL, PAL_BATTLE_OB_BLUE
- db FAST_BALL, PAL_BATTLE_OB_BLUE
- db FRIEND_BALL, PAL_BATTLE_OB_YELLOW
- db MOON_BALL, PAL_BATTLE_OB_GRAY
- db LOVE_BALL, PAL_BATTLE_OB_RED
- db -1, PAL_BATTLE_OB_GRAY
+ db MASTER_BALL
+ RGB 31,31,31, 20,08,23
+ db ULTRA_BALL
+ RGB 31,31,31, 10,10,12
+ db GREAT_BALL
+ RGB 31,31,31, 09,13,30
+ db POKE_BALL
+ RGB 31,31,31, 30,08,05
+ db HEAVY_BALL
+ RGB 31,31,31, 06,10,12
+ db LEVEL_BALL
+ RGB 31,31,31, 30,24,00
+ db LURE_BALL
+ RGB 31,31,31, 04,14,30
+ db FAST_BALL
+ RGB 31,31,31, 31,16,04
+ db FRIEND_BALL
+ RGB 31,31,31, 04,17,04
+ db MOON_BALL
+ RGB 31,31,31, 07,19,25
+ db LOVE_BALL
+ RGB 31,31,31, 30,11,22
+ db PARK_BALL
+ RGB 31,31,31, 18,18,05
+ db -1 ; end
+ RGB 31,31,31, 16,16,16
Each Ball used to use one of the palettes meant for move animations (since throwing a Ball is technically an animation): gray, yellow, red, green, blue, or brown. Here we've replaced each PAL_BATTLE_OB_*
color index with a pair of RGB colors: the first for the bottom half of the Ball, the second for the top half.
Here's how they'll look:
We just changed the BallColors
table to assign each Ball a pair of actual RGB colors, instead of a palette index. So putting back the palette index will be the next step.
PAL_BATTLE_OB_RED
2. Make every Ball use Edit engine/battle_anims/functions.asm:
GetBallAnimPal:
- ld hl, BallColors
- ldh a, [rSVBK]
- push af
- ld a, BANK(wCurItem)
- ldh [rSVBK], a
- ld a, [wCurItem]
- ld e, a
- pop af
- ldh [rSVBK], a
-.IsInArray:
- ld a, [hli]
- cp -1
- jr z, .load
- cp e
- jr z, .load
- inc hl
- jr .IsInArray
-
-.load
- ld a, [hl]
ld hl, BATTLEANIMSTRUCT_PALETTE
add hl, bc
- ld [hl], a
+ ld [hl], PAL_BATTLE_OB_RED
ret
-
-INCLUDE "data/battle_anims/ball_colors.asm"
Since we know every Ball will use the same palette index, the next step is to replace that palette's colors with the correct Ball-specific ones at the right time.
3. Load the unique colors into the palette
Edit engine/battle_anims/helpers.asm:
LoadBattleAnimGFX:
push hl
+ cp BATTLE_ANIM_GFX_POKE_BALL
+ call z, .LoadBallPalette
ld l, a
ld h, 0
add hl, hl
add hl, hl
ld de, AnimObjGFX
add hl, de
ld c, [hl]
inc hl
ld b, [hl]
inc hl
ld a, [hli]
ld h, [hl]
ld l, a
pop de
push bc
call DecompressRequest2bpp
pop bc
ret
+
+.LoadBallPalette:
+ ; save the current WRAM bank
+ ld a, [rSVBK]
+ push af
+ ; switch to the WRAM bank of wCurItem so we can read it
+ ld a, BANK(wCurItem)
+ ld [rSVBK], a
+ ; store the current item in b
+ ld a, [wCurItem]
+ ld b, a
+ ; seek for the BallColors entry matching the current item
+ ld hl, BallColors
+.loop
+ ld a, [hli]
+ cp b ; did we find the current ball?
+ jr z, .done
+ cp -1 ; did we reach the end of the list?
+ jr z, .done
+rept PAL_COLOR_SIZE * 2
+ inc hl ; skip over the two RGB colors to the next entry
+endr
+ jr .loop
+.done
+ ; switch to the WRAM bank of wOBPals2 so we can write to it
+ ld a, BANK(wOBPals2)
+ ld [rSVBK], a
+ ; load the RGB colors into the middle two colors of PAL_BATTLE_OB_RED
+ ld de, wOBPals2 palette PAL_BATTLE_OB_RED color 1
+rept PAL_COLOR_SIZE * 2 - 1
+ ld a, [hli]
+ ld [de], a
+ inc de
+endr
+ ld a, [hl]
+ ld [de], a
+ ; apply the updated colors to the palette RAM
+ ld a, $1
+ ldh [hCGBPalUpdate], a
+ ; restore the previous WRAM bank
+ pop af
+ ld [rSVBK], a
+ ; restore the graphics index to be loaded
+ ld a, BATTLE_ANIM_GFX_POKE_BALL
+ ret
+
+INCLUDE "data/battle_anims/ball_colors.asm"
LoadBattleAnimGFX
is called with an BATTLE_ANIM_GFX_*
constant in a
; it loads the appropriate graphics for that constant in preparation for animating the move. If we inspect data/moves/animations.asm, we can see that BATTLE_ANIM_GFX_POKE_BALL
is used for throwing Poké Balls, and only for throwing Poké Balls; so if we know it's being loaded, that's the right time to load our unique colors too.
Now we can see the unique colors in action: