Show an icon for the current time of day - pret/pokecrystal GitHub Wiki

Adapted from the Tutorial to show an Icon for the Current Battle Weather

This tutorial will also add an Icon in Battle to indicate the Time of Day. This is useful if you have time-sensitive items like Kurt's Pokeballs, or if you have also used the Battle Palette tutorial and need a clearer indication of what time it is.

Contents

  1. Add the code to display the sprites
  2. Call Time Of Day Image
  3. Finish calling the Time Of Day Images at the right time
  4. Clear the Icon sprite when we don't need it
  5. Refit the Contest Battle Menu

1. Add the code to display the sprites

NOTE: If you've already added the Weather Icon, skip Step 1 and go to Step 2.

Edit engine/battle/core.asm:

 BattleTurn:
 ...
 .skip_iteration
	call ParsePlayerAction
+	push af
+	call ClearSprites
+	pop af
	jr nz, .loop1

These changes are just to clear the sprite when we don't need it..

2. Call Time Of Day Image

Add this function in the same file:

+GetTimeOfDayImage:
+	ld a, [wTimeOfDay]
+	cp MORN_F
+	jr z, .DayImage
+	cp DAY_F
+	jr z, .DayImage
+	cp NITE_F
+	jr z, .NightImage
+.DayImage
+ ld de, DayTimeImage
+ lb bc, PAL_BATTLE_OB_YELLOW, 4
+ jr .done	
+	
+ .NightImage
+ ld de, NightTimeImage
+ lb bc, PAL_BATTLE_OB_BLUE, 4
+
+.done
+	push bc
+	ld b, BANK(TimeOfDayImages) ; c = 4
+	ld hl, vTiles0
+	call Request2bpp
+	pop bc
+	ld hl, wShadowOAMSprite00
+	ld de, .TimeOfDayImageOAMData
+.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
+
+.TimeOfDayImageOAMData
+; 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/timeofday and add these graphics:

day_icon

night_icon

Create a new file called gfx/timeofday_images.asm:

+TimeOfDayImages::
+
+DayTimeImage:
+INCBIN "gfx/battle/timeofday/day_icon.2bpp"
+
+NightTimeImage:
+INCBIN "gfx/battle/timeofday/night_icon.2bpp"

And add this section to main.asm:

+SECTION "Time Of Day Images", ROMX
+
+INCLUDE "gfx/timeofday_images.asm"

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

3. Finish calling the Time Of Day Images at the right time.

Edit engine/battle/core.asm again:

Find .alive2:

.alive2
	ld a, [wCurBattleMon]
	ld [wLastPlayerMon], a
	ld a, [wCurPartyMon]
	ld [wCurBattleMon], a
	inc a
	ld hl, wPartySpecies - 1
	ld c, a
	ld b, 0
	add hl, bc
	ld a, [hl]
	ld [wCurPartySpecies], a
	ld [wTempBattleMonSpecies], a
	hlcoord 1, 5
	ld a, 9
	call SlideBattlePicOut
	call LoadTilemapToTempTilemap
	call ResetBattleParticipants
	call InitBattleMon
	call ResetPlayerStatLevels
	call SendOutMonText
	call NewBattleMonStatus
	call BreakAttraction
	call SendOutPlayerMon
	call EmptyBattleTextbox
+	call GetTimeOfDayImage
	call LoadTilemapToTempTilemap
	call SetPlayerTurn
	call SpikesDamage
	ld a, [wLinkMode]
	and a
	jr z, .not_linked_2
	ldh a, [hSerialConnectionStatus]
	cp USING_INTERNAL_CLOCK
	jr nz, .not_linked_2
	xor a
	ld [wEnemySwitchMonIndex], a
	call NewEnemyMonStatus
	call ResetEnemyStatLevels
	call BreakAttraction
	call EnemySwitch
	call SetEnemyTurn
	call SpikesDamage

Find BattleMenu::

BattleMenu:
	xor a
	ldh [hBGMapMode], a
	call LoadTempTilemapToTilemap
+	call GetTimeOfDayImage

	ld a, [wBattleType]
	cp BATTLETYPE_DEBUG
	jr z, .ok
	cp BATTLETYPE_TUTORIAL
	jr z, .ok
	call EmptyBattleTextbox
	call UpdateBattleHuds
	call EmptyBattleTextbox
	call LoadTilemapToTempTilemap
.ok

Find .didnt_use_item:

.didnt_use_item
	call ClearPalettes
	call DelayFrame
	call _LoadBattleFontsHPBar
	call GetBattleMonBackpic
	call GetEnemyMonFrontpic
	call ExitMenu
	call WaitBGMap
	call FinishBattleAnim
	call LoadTilemapToTempTilemap
+	call GetTimeOfDayImage
	jp BattleMenu

Find .Cancel:

.Cancel:
	call ClearSprites
	call ClearPalettes
	call DelayFrame
	call _LoadHPBar
	call CloseWindow
	call LoadTilemapToTempTilemap
	call GetMemSGBLayout
	call SetPalettes
+	call GetTimeOfDayImage
	jp BattleMenu

4. Clear the Icon sprite when we don't need it.

Find BattleMenu_Fight:

BattleMenu_Fight:
+       call ClearSprites
	xor a
	ld [wNumFleeAttempts], a
	call SafeLoadTempTilemapToTilemap
	and a
	ret

Find BattleMenu_Run:

BattleMenu_Run:
+       call ClearSprites
	call SafeLoadTempTilemapToTilemap
	ld a, $3
	ld [wMenuCursorY], a
	ld hl, wBattleMonSpeed
	ld de, wEnemyMonSpeed
	call TryToRunAwayFromBattle
	ld a, FALSE
	ld [wFailedToFlee], a
	ret c
	ld a, [wBattlePlayerAction]
	and a ; BATTLEPLAYERACTION_USEMOVE?
	ret nz
	jp BattleMenu

Now find BattleMenu_Pack:, go down to .contest. This will clear the Time of Day Icon when using a Park Ball in the Bug-Catching Contest.

.contest
+    call ClearSprites
	ld a, PARK_BALL
	ld [wCurItem], a
	call DoItemEffect

And "Voila!", now a Time of Day Icon will appear in battle on your normal fight menu, and won't interfere with the weather icon tutorial that displays it's icon on your moves menu.

However, now that we're talking about the Bug-Catching contest, the icon display doesn't really fit correctly, so let's make some changes in the next step.

5. Refit the Contest Battle Menu

Edit engine/battle/menu.asm. This refit will provide visual consistency with the usual battle menu + time/weather icons.

Due to limited character space, I have shortened "PARKBALL" to "BALL". If POKéMON gets shortened to "PKMN" in the battle menu, then why would "PARKBALLx20" be so cumbersome? It just barely fits in the original since it lacks a time/weather icon, but sacrifices have to be made for aesthetic appeal.

ContestBattleMenuHeader:
	db MENU_BACKUP_TILES ; flags
+	menu_coords 4, 12, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1
-	menu_coords 2, 12, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1
	dw .MenuData
	db 1 ; default option

.MenuData:
	db STATICMENU_CURSOR | STATICMENU_DISABLE_B ; flags
	dn 2, 2 ; rows, columns
+	db 9 ; spacing
-	db 12 ; spacing
	dba .Text
	dba .PrintParkBallsRemaining

.Text:
	db "FIGHT@"
	db "<PKMN>@"
+	db "BALL×  @"
-	db "PARKBALL×  @"
	db "RUN@"

.PrintParkBallsRemaining:
+	hlcoord 11, 16
-	hlcoord 13, 16
	ld de, wParkBallsRemaining
	lb bc, PRINTNUM_LEADINGZEROS | 1, 2
	call PrintNum
	ret

Now you should be all done! Obviously if you make any custom battle menus outside of the usual types of normal/tutorial/contest, you'll have to make it all fit again, but for the battle types available in the base game, this should work.

⚠️ **GitHub.com Fallback** ⚠️