Show an icon for the current weather - pret/pokecrystal GitHub Wiki

Since Gen 5, the current weather gets displayed during battles on the bottom screen. This tutorial will explain how to do so in Gen 2, more specifically, during move selection. It works by creating a sprite for the weather icon, so you'll also learn how to work with sprite graphics on the Game Boy Color.

Contents

  1. Add the code to display the sprites
  2. Add the graphics
  3. Optimize the code
  4. Addendum

1. Add the code to display the sprites

Edit engine/battle/core.asm:

 BattleTurn:
 ...
 .skip_iteration
	call ParsePlayerAction
+	push af
+	call ClearSprites
+	pop af
	jr nz, .loop1
 MoveSelectionScreen:
 ...
 .battle_player_moves
	call MoveInfoBox
+	call GetWeatherImage
	ld a, [wSwappedMove]
 ...
 .place_textbox_start_over
+	push hl
+	call ClearSprites
+	pop hl
	call StdBattleTextbox
	...

These changes are just to clear the sprite when we don't need it and to add the call function to the code below.

Add this function in the same file:

+GetWeatherImage:
+	ld a, [wBattleWeather]
+	and a
+	ret z
+	ld de, RainWeatherImage
+	lb bc, PAL_BATTLE_OB_BLUE, 4
+	cp WEATHER_RAIN
+	jr z, .done
+	ld de, SunWeatherImage
+	ld b, PAL_BATTLE_OB_YELLOW
+	cp WEATHER_SUN
+	jr z, .done
+	ld de, SandstormWeatherImage
+	ld b, PAL_BATTLE_OB_BROWN
+	cp WEATHER_SANDSTORM
+	ret nz
+	
+.done
+	push bc
+	ld b, BANK(WeatherImages) ; c = 4
+	ld hl, vTiles0
+	call Request2bpp
+	pop bc
+	ld hl, wShadowOAMSprite00
+	ld de, .WeatherImageOAMData
+.loop
+	ld a, [de]
+	inc de
+	ld [hli], a
+	ld a, [de]
+	inc de
+	ld [hli], a
+	dec c
+	ld a, c
+	ld [hli], a
+	ld a, b
+	ld [hli], a
+	jr nz, .loop
+	ret
+
+.WeatherImageOAMData
+; positions are backwards since
+; we load them in reverse order
+	db $88, $1c ; y/x - bottom right
+	db $88, $14 ; y/x - bottom left
+	db $80, $1c ; y/x - top right
+	db $80, $14 ; y/x - top left

Request2bpp is an important function. It grabs the image from ROM and places it where you want, usually VRAM. The description of it is kind of weird, but basically b is the image's bank, de is the image's address in memory, hl is where you want to copy it to, and c is how many tiles to copy.

If you want to convert an image into a sprite, wVirtualOAM is where you want to go. You don't want to write directly to OAM because that would require specific timings that wouldn't be available all of the time. So you must load the OAM data into wVirtualOAM. Every so often, many times per second actually, the game loads whatever is in wVirtualOAM into the actual OAM, when those timings I mentioned are available. OAM data works like this, each sprite is an 8x8 tile just like the background or map tiles, but they can be put anywhere on the screen. Each sprite uses 4 bytes in this order, Y position, X position, Tile number, Attributes. Look here for a better description of the OAM structure.

2. Add the graphics

Create gfx/battle/weather and add these graphics:

rain.png sun.png sandstorm.png

Create a new file called gfx/weather_images.asm:

+WeatherImages::
+
+RainWeatherImage:
+INCBIN "gfx/battle/weather/rain_icon.2bpp"
+
+SunWeatherImage:
+INCBIN "gfx/battle/weather/sun_icon.2bpp"
+
+SandstormWeatherImage:
+INCBIN "gfx/battle/weather/sand_icon.2bpp"

And add this section to main.asm:

+SECTION "Battle Weather Images", ROMX
+
+INCLUDE "gfx/weather_images.asm"

RGBDS will add this section to any free space in a bank unless you add "Battle Weather Images" to layout.link and specify in which bank it should be placed.

3. Optimize the code

Now, if you plan on not adding any new weather effects or weather icons, optimization is straightforward. Otherwise, there's some tricks you can use to keep the code optimized.

A quick and easy optimzation is to replace the cp WEATHER_X in GetWeatherImage to dec a like so.

 GetWeatherImage:
	ld a, [wBattleWeather]
	and a
	ret z
	ld de, RainWeatherImage
	lb bc, PAL_BATTLE_OB_BLUE, 4
-	cp WEATHER_RAIN
+	dec a
	jr z, .done
	ld de, SunWeatherImage
	ld b, PAL_BATTLE_OB_YELLOW
-	cp WEATHER_SUN
+	dec a
	jr z, .done
	ld de, SandstormWeatherImage
	ld b, PAL_BATTLE_OB_BROWN
-	cp WEATHER_SANDSTORM
+	dec a
	ret nz

You can do this because WEATHER_RAIN = 1, WEATHER_SUN = WEATHER_RAIN + 1, and WEATHER_SANDSTORM = WEATHER_SUN + 1. For more info, check out how to optimize assembly code in general.

That format can be continued to be used if whatever new weather effects equal one more than the last.

4. Addendum

Here is a pre-made graphic for Hail made by Bronzeswagger if you decide to add Hail to your hack.

hail.png