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.
- Add the code to display the sprites
- Call Time Of Day Image
- Finish calling the Time Of Day Images at the right time
- Clear the Icon sprite when we don't need it
- Refit the Contest Battle Menu
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..
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.
Create gfx/battle/timeofday and add these graphics:
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.
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
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.
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.