-
Notifications
You must be signed in to change notification settings - Fork 806
Make evening the fourth time of day
Gen 2 used the Game Boy Color's real-time clock to implement a time of day system:
- Morning: 6 hours, 4:00 AM–9:59 AM
- Day: 8 hours, 10:00 AM–5:59 PM
- Night: 10 hours, 6:00 PM–3:59 AM
The whole feature is restricted to storing times of day in two bits. That's enough to store four values. The fourth is not a real time; it's used to represent pitch-dark areas where you need Flash (since a major effect of the time of day is to change the map colors).
This tutorial will represent Flash darkness in a different way, and use the freed fourth time value for evening. (Gen 5 was the first to have evening as a fourth time of day; it also varied the time boundaries by season.)
It depends on the DARKNESS_PALSET
constant added to pokecrystal on August 11, 2020, so if your copy of pokecrystal is older than that, apply commit ed3e70b
before following this tutorial.
(The code for this feature was adapted from Pokémon Polished Crystal.)
- Redefine the boundaries for times of day
- Handle the new time boundaries in the code
- Define palette colors for evening
- Change how Flash darkness works
- Load the right colors for the time of day
- Reuse night wild encounters for evening
- Display evening when setting the time
- Handle a caught time of evening for the Poké Seer
- Buena's Password plays in the evening
- Eevee evolves into Umbreon in the evening
- Johto wild battles use night music in the evening
- The player's Mom watches TV in the evening
- Prof. Oak's intro changes in the evening
- Pokémon Center nurses greet you in the evening
- Phone callers reuse night text in the evening
The new times of day will be:
- Morning: 6 hours, 4:00 AM–9:59 AM
- Day: 7 hours, 10:00 AM–4:59 PM
- Evening: 3 hours, 5:00 PM–7:59 PM
- Night: 8 hours, 8:00 PM–3:59 AM
Edit constants/wram_constants.asm:
; wTimeOfDay::
const_def
const MORN_F ; 0
const DAY_F ; 1
const NITE_F ; 2
- const DARKNESS_F ; 3
+ const EVE_F ; 3
NUM_DAYTIMES EQU const_value
MORN EQU 1 << MORN_F
DAY EQU 1 << DAY_F
NITE EQU 1 << NITE_F
-DARKNESS EQU 1 << DARKNESS_F
+EVE EQU 1 << EVE_F
-ANYTIME EQU MORN | DAY | NITE
+ANYTIME EQU MORN | DAY | EVE | NITE
And edit constants/misc_constants.asm:
; time of day boundaries
MORN_HOUR EQU 4 ; 4 AM
DAY_HOUR EQU 10 ; 10 AM
-NITE_HOUR EQU 18 ; 6 PM
+EVE_HOUR EQU 17 ; 5 PM
+NITE_HOUR EQU 20 ; 8 PM
NOON_HOUR EQU 12 ; 12 PM
MAX_HOUR EQU 24 ; 12 AM
Edit engine/rtc/rtc.asm:
TimesOfDay:
; hours for the time of day
-; 0400-0959 morn | 1000-1759 day | 1800-0359 nite
+; 0400-0959 morn | 1000-1659 day | 1700-1959 eve | 2000-0359 nite
db MORN_HOUR, NITE_F
db DAY_HOUR, MORN_F
- db NITE_HOUR, DAY_F
+ db EVE_HOUR, DAY_F
+ db NITE_HOUR, EVE_F
db MAX_HOUR, NITE_F
db -1, MORN_F
This will make the GetTimeOfDay
routine return EVE_F
for evening hours.
Edit engine/events/checktime.asm:
.TimeOfDayTable:
db MORN_F, MORN
db DAY_F, DAY
- db NITE_F, NITE
+ db EVE_F, EVE
db NITE_F, NITE
db -1
This will make the checktime
script command work for EVE
.
Edit home/map_objects.asm:
.TimesOfDay:
; entries correspond to TimeOfDay values
db MORN
db DAY
db NITE
+ db EVE
This will make NPCs' object_event
s work with the EVE
value to only appear at certain times of day.
The "typical" colors are during the day; morning has a yellow tint and night has a dark blue tint. Evening will have an orange tint. The roofs in different cities have unique colors that reuse the same ones for morning and day, but we'll add a third set of evening roof colors.
Edit gfx/tilesets/bg_tiles.pal:
-; dark
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; gray
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; red
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; green
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; water
- RGB 30,30,11, 00,00,00, 00,00,00, 00,00,00 ; yellow
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; brown
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; roof
- RGB 31,31,16, 31,31,16, 14,09,00, 00,00,00 ; text
+; eve
+ RGB 31,21,14, 18,16,16, 11,10,10, 06,05,05 ; gray
+ RGB 31,21,14, 25,14,18, 24,08,05, 06,05,05 ; red
+ RGB 19,23,08, 10,19,01, 04,10,00, 06,05,05 ; green
+ RGB 31,21,14, 07,09,23, 01,03,23, 06,05,05 ; water
+ RGB 31,31,07, 27,21,10, 25,12,01, 06,05,05 ; yellow
+ RGB 31,21,14, 21,13,05, 16,09,01, 06,05,05 ; brown
+ RGB 31,21,14, 13,23,23, 04,13,23, 06,05,05 ; roof
+ RGB 31,31,16, 31,31,16, 14,09,00, 00,00,00 ; text
; overworld water
- RGB 23,23,31, 18,19,31, 13,12,31, 07,07,07 ; morn/day
+ RGB 23,23,31, 18,19,31, 13,12,31, 07,07,07 ; morn
+ RGB 23,23,31, 18,19,31, 13,12,31, 07,07,07 ; day
RGB 15,13,27, 10,09,20, 04,03,18, 00,00,00 ; nite
+ RGB 31,21,14, 16,14,23, 11,09,23, 06,05,05 ; eve
And create gfx/tilesets/darkness.pal:
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; gray
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; red
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; green
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; water
+ RGB 30,30,11, 00,00,00, 00,00,00, 00,00,00 ; yellow
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; brown
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; roof
+ RGB 31,31,16, 31,31,16, 14,09,00, 00,00,00 ; text
Then edit gfx/overworld/npc_sprites.pal:
-; dark
- RGB 01,01,02, 31,19,10, 31,07,01, 00,00,00 ; red
- RGB 01,01,02, 31,19,10, 10,09,31, 00,00,00 ; blue
- RGB 01,01,02, 31,19,10, 07,23,03, 00,00,00 ; green
- RGB 01,01,02, 31,19,10, 15,10,03, 00,00,00 ; brown
- RGB 01,01,02, 31,19,10, 30,10,06, 00,00,00 ; pink
- RGB 31,31,31, 31,31,31, 13,13,13, 00,00,00 ; silver
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; tree
- RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; rock
+; eve
+ RGB 31,21,14, 31,19,10, 31,07,01, 00,00,00 ; red
+ RGB 31,21,14, 31,19,10, 10,09,31, 00,00,00 ; blue
+ RGB 31,21,14, 31,19,10, 07,23,03, 00,00,00 ; green
+ RGB 31,21,14, 31,19,10, 15,10,03, 00,00,00 ; brown
+ RGB 31,21,14, 31,19,10, 30,10,06, 00,00,00 ; pink
+ RGB 31,31,31, 31,31,31, 13,13,13, 00,00,00 ; silver
+ RGB 19,23,08, 10,19,01, 04,10,00, 06,05,05 ; tree
+ RGB 31,21,14, 21,13,05, 16,09,01, 06,05,05 ; rock
And create gfx/overworld/npc_sprites_darkness.pal:
+ RGB 01,01,02, 31,19,10, 31,07,01, 00,00,00 ; red
+ RGB 01,01,02, 31,19,10, 10,09,31, 00,00,00 ; blue
+ RGB 01,01,02, 31,19,10, 07,23,03, 00,00,00 ; green
+ RGB 01,01,02, 31,19,10, 15,10,03, 00,00,00 ; brown
+ RGB 01,01,02, 31,19,10, 30,10,06, 00,00,00 ; pink
+ RGB 31,31,31, 31,31,31, 13,13,13, 00,00,00 ; silver
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; tree
+ RGB 01,01,02, 00,00,00, 00,00,00, 00,00,00 ; rock
Edit gfx/tilesets/roofs.pal:
; group 0 (unused)
RGB 21,21,21, 11,11,11 ; morn/day
RGB 21,21,21, 11,11,11 ; nite
+ RGB 18,16,16, 09,08,08 ; eve
; group 1 (Olivine)
RGB 14,17,31, 07,11,15 ; morn/day
RGB 09,09,17, 05,07,13 ; nite
+ RGB 12,13,23, 06,08,11 ; eve
; group 2 (Mahogany)
RGB 12,19,00, 06,10,00 ; morn/day
RGB 06,09,07, 04,05,06 ; nite
+ RGB 10,14,00, 05,08,00 ; eve
; group 3 (dungeons)
RGB 21,21,21, 11,11,11 ; morn/day
RGB 21,21,21, 17,08,07 ; nite
+ RGB 18,16,16, 09,08,08 ; eve
; group 4 (Ecruteak)
RGB 31,19,00, 27,10,05 ; morn/day
RGB 15,07,02, 11,04,02 ; nite
+ RGB 25,14,00, 22,08,03 ; eve
; group 5 (Blackthorn)
RGB 11,10,16, 05,06,07 ; morn/day
RGB 03,04,08, 00,00,00 ; nite
+ RGB 09,08,12, 04,04,05 ; eve
; group 6 (Cinnabar)
RGB 31,10,00, 18,06,00 ; morn/day
RGB 18,05,09, 17,08,07 ; nite
+ RGB 25,08,00, 15,04,00 ; eve
; group 7 (Cerulean)
RGB 17,27,31, 05,15,31 ; morn/day
RGB 07,08,22, 07,07,16 ; nite
+ RGB 14,21,23, 04,11,23 ; eve
; group 8 (Azalea)
RGB 22,20,10, 17,14,03 ; morn/day
RGB 11,11,05, 10,09,07 ; nite
+ RGB 19,15,08, 14,10,01 ; eve
; group 9 (Lake of Rage)
RGB 31,08,04, 09,09,08 ; morn/day
RGB 18,05,09, 09,09,08 ; nite
+ RGB 25,06,02, 08,07,06 ; eve
; group 10 (Violet)
RGB 24,14,31, 13,07,21 ; morn/day
RGB 12,03,18, 09,03,15 ; nite
+ RGB 21,10,23, 11,05,16 ; eve
; group 11 (Goldenrod)
RGB 25,25,00, 20,17,08 ; morn/day
RGB 12,12,00, 10,09,05 ; nite
+ RGB 21,19,00, 17,13,06 ; eve
; group 12 (Vermilion)
RGB 27,23,01, 23,11,00 ; morn/day
RGB 15,11,01, 11,10,01 ; nite
+ RGB 22,18,01, 20,08,00 ; eve
; group 13 (Pallet)
RGB 27,28,31, 17,19,22 ; morn/day
RGB 14,14,18, 10,09,13 ; nite
+ RGB 25,12,10, 21,08,06 ; eve
; group 14 (Pewter)
RGB 19,19,16, 10,12,15 ; morn/day
RGB 09,09,11, 04,05,07 ; nite
+ RGB 16,14,12, 08,09,11 ; eve
; group 15 (Mount Moon Square)
RGB 14,17,31, 07,11,15 ; morn/day
RGB 09,13,19, 07,07,16 ; nite
+ RGB 12,13,23, 06,08,11 ; eve
; group 16 (Indigo)
RGB 21,21,21, 13,13,13 ; morn/day
RGB 11,11,19, 07,07,12 ; nite
+ RGB 20,19,19, 11,10,10 ; eve
; group 17 (Fuchsia)
RGB 31,18,29, 17,13,20 ; morn/day
RGB 14,06,12, 11,03,10 ; nite
+ RGB 25,13,21, 14,10,15 ; eve
; group 18 (Lavender)
RGB 23,15,31, 16,05,31 ; morn/day
RGB 12,07,17, 08,06,10 ; nite
+ RGB 20,11,23, 13,03,23 ; eve
; group 19 (Silver Cave)
RGB 21,21,25, 16,16,16 ; morn/day
RGB 13,13,13, 07,07,07 ; nite
+ RGB 08,18,22, 08,10,19 ; eve
; group 20 (Cable Club)
RGB 21,21,21, 11,11,11 ; morn/day
RGB 21,21,21, 11,11,11 ; nite
+ RGB 20,19,19, 10,09,09 ; eve
; group 21 (Celadon)
RGB 19,31,15, 31,22,02 ; morn/day
RGB 12,13,09, 09,12,03 ; nite
+ RGB 11,19,02, 04,14,04 ; eve
; group 22 (Cianwood)
RGB 15,10,31, 07,05,15 ; morn/day
RGB 06,05,17, 02,02,08 ; nite
+ RGB 13,08,23, 06,03,11 ; eve
; group 23 (Viridian)
RGB 21,31,07, 13,25,04 ; morn/day
RGB 09,14,08, 06,10,04 ; nite
+ RGB 18,24,05, 11,19,02 ; eve
; group 24 (New Bark)
RGB 20,31,14, 11,23,05 ; morn/day
RGB 09,13,08, 06,09,04 ; nite
+ RGB 17,24,10, 09,18,03 ; eve
; group 25 (Saffron)
RGB 31,26,00, 31,15,00 ; morn/day
RGB 13,13,01, 08,08,01 ; nite
+ RGB 25,20,00, 25,11,00 ; eve
; group 26 (Cherrygrove)
RGB 31,14,28, 31,05,21 ; morn/day
RGB 14,07,17, 13,00,08 ; nite
+ RGB 25,10,20, 25,03,16 ; eve
And finally, edit engine/gfx/color.asm:
RoofPals:
- table_width PAL_COLOR_SIZE * 2 * 2, RoofPals
+ table_width PAL_COLOR_SIZE * 3 * 2, RoofPals
INCLUDE "gfx/tilesets/roofs.pal"
assert_table_length NUM_MAP_GROUPS + 1
We'll write code to handle these new colors soon; but first let's fix Flash.
We're replacing darkness constants and data with evening ones, so we'll need to handle darkness differently.
Edit constants/map_data_constants.asm:
; map palettes (wEnvironment)
const_def
const PALETTE_AUTO
const PALETTE_DAY
const PALETTE_NITE
const PALETTE_MORN
- const PALETTE_DARK
+ const PALETTE_EVE
NUM_MAP_PALETTES EQU const_value
+
+IN_DARKNESS_F EQU 3
+IN_DARKNESS EQU 1 << IN_DARKNESS_F ; masked with a PALETTE_* constant
Then edit data/maps/maps.asm: replace all 13 uses of PALETTE_DARK
with PALETTE_NITE | IN_DARKNESS
. The maps affected are:
- All 8
WhirlIsland*
maps SilverCaveRoom1
-
DarkCaveVioletEntrance
andDarkCaveBlackthornEntrance
-
RockTunnel1F
andRockTunnelB1F
Next, edit constants/wram_constants.asm again:
; wTimeOfDayPalset::
+; Must be different from any in ReplaceTimeOfDayPals.BrightnessLevels
-DARKNESS_PALSET EQU (DARKNESS_F << 6) | (DARKNESS_F << 4) | (DARKNESS_F << 2) | DARKNESS_F
+DARKNESS_PALSET EQU (MORN_F << 6) | (DAY_F << 4) | (EVE_F << 2) | NITE_F
Then edit engine/tilesets/timeofday_pals.asm:
ReplaceTimeOfDayPals:
- ld hl, .BrightnessLevels
ld a, [wMapTimeOfDay]
- cp PALETTE_DARK
- jr z, .NeedsFlash
+ bit IN_DARKNESS_F, a
+ jr z, .not_dark
+ ld a, [wStatusFlags]
+ bit STATUSFLAGS_FLASH_F, a
+ jr nz, .not_dark
+ ld a, DARKNESS_PALSET
+ jr .done
+
+.not_dark:
+ ld hl, .BrightnessLevels
+ ld a, [wMapTimeOfDay]
maskbits NUM_MAP_PALETTES
add l
ld l, a
ld a, 0
adc h
ld h, a
ld a, [hl]
+.done:
ld [wTimeOfDayPalset], a
ret
-
-.NeedsFlash:
- ld a, [wStatusFlags]
- bit STATUSFLAGS_FLASH_F, a
- jr nz, .UsedFlash
- ld a, DARKNESS_PALSET
- ld [wTimeOfDayPalset], a
- ret
-
-.UsedFlash:
- ld a, (NITE_F << 6) | (NITE_F << 4) | (NITE_F << 2) | NITE_F
- ld [wTimeOfDayPalset], a
- ret
.BrightnessLevels:
; actual palettes used when time is
-; DARKNESS_F, NITE_F, DAY_F, MORN_F
+; EVE_F, NITE_F, DAY_F, MORN_F
- dc DARKNESS_F, NITE_F, DAY_F, MORN_F ; PALETTE_AUTO
+ dc EVE_F, NITE_F, DAY_F, MORN_F ; PALETTE_AUTO
dc DAY_F, DAY_F, DAY_F, DAY_F ; PALETTE_DAY
dc NITE_F, NITE_F, NITE_F, NITE_F ; PALETTE_NITE
dc MORN_F, MORN_F, MORN_F, MORN_F ; PALETTE_MORN
- dc DARKNESS_F, DARKNESS_F, DARKNESS_F, DARKNESS_F ; PALETTE_DARK
+ dc EVE_F, EVE_F, EVE_F, EVE_F ; PALETTE_EVE
- dc DARKNESS_F, NITE_F, DAY_F, MORN_F
- dc DARKNESS_F, NITE_F, DAY_F, MORN_F
- dc DARKNESS_F, NITE_F, DAY_F, MORN_F
GetTimePalette:
jumptable .TimePalettes, wTimeOfDay
.TimePalettes:
dw .MorningPalette ; MORN_F
dw .DayPalette ; DAY_F
dw .NitePalette ; NITE_F
- dw .DarknessPalette ; DARKNESS_F
+ dw .EveningPalette ; EVE_F
.MorningPalette:
ld a, [wTimeOfDayPalset]
and %00000011
ret
.DayPalette:
ld a, [wTimeOfDayPalset]
and %00001100
srl a
srl a
ret
.NitePalette:
ld a, [wTimeOfDayPalset]
and %00110000
swap a
ret
-.DarknessPalette:
+.EveningPalette:
ld a, [wTimeOfDayPalset]
and %11000000
rlca
rlca
ret
So here's how the above code works. We have four times of day, and four brightness levels, but they have to be related by the map palette according to the data in ReplaceTimeOfDayPals.BrightnessLevels
. For example, if the current map palette is PALETTE_AUTO
and the current time is NITE_F
, then the NITE_F
column in the PALETTE_AUTO
has the value NITE_F
. (These relations are all pretty obvious—PALETTE_AUTO
pairs every time of day with itself, and the other PALETTE_
s always use the same time of day—so the whole system could probably be simplified; but it's easier to stick with what already works.)
Anyway, it used to check for PALETTE_DARK
, and use a palette set for that which had all DARKNESS_F
values (DARKNESS_PALSET
). Instead, we check for the IN_DARKNESS
mask on the PALETTE_
value (IN_DARKNESS_F
is bit 3, which doesn't collide with the palette bits 0–2), and use a palette set that's a sentinel value (the actual value doesn't matter, it just has to not be a real entry in the .BrightnessLevels
table). Also, when Flash is used, we're not hard-coding an all-NITE_F
palette set; instead, it will use whatever is in the map
. So if a map's palette is PALETTE_DAY | IN_DARKNESS
, using Flash will make it have the day palette.
There are a couple more pieces of code that check for Flash darkness, so let's fix them too.
Edit engine/battle/battle_transition.asm:
ld hl, .pals
- ld a, [wTimeOfDayPal]
- maskbits NUM_DAYTIMES
- cp DARKNESS_F
+ ld a, [wTimeOfDayPalset]
+ cp DARKNESS_PALSET
jr nz, .not_dark
ld hl, .darkpals
.not_dark
And edit engine/events/poisonstep_pals.asm:
- ld a, [wTimeOfDayPal]
- maskbits NUM_DAYTIMES
- cp DARKNESS_F
+ ld a, [wTimeOfDayPalset]
+ cp DARKNESS_PALSET
ld a, %00000000
jr z, .convert_pals
ld a, %10101010
.convert_pals
Loading the right colors from bg_tiles.pal is simple. Just edit data/maps/environment_colors.asm:
-; Valid indices: $00 - $29 (see gfx/tilesets/bg_tiles.pal)
+; Valid indices: $00 - $2b (see gfx/tilesets/bg_tiles.pal)
.OutdoorColors:
db $00, $01, $02, $28, $04, $05, $06, $07 ; morn
- db $08, $09, $0a, $28, $0c, $0d, $0e, $0f ; day
- db $10, $11, $12, $29, $14, $15, $16, $17 ; nite
- db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ; dark
+ db $08, $09, $0a, $29, $0c, $0d, $0e, $0f ; day
+ db $10, $11, $12, $2a, $14, $15, $16, $17 ; nite
+ db $18, $19, $1a, $2b, $1c, $1d, $1e, $1f ; eve
.IndoorColors:
db $20, $21, $22, $23, $24, $25, $26, $07 ; morn
db $20, $21, $22, $23, $24, $25, $26, $07 ; day
db $10, $11, $12, $13, $14, $15, $16, $07 ; nite
- db $18, $19, $1a, $1b, $1c, $1d, $1e, $07 ; dark
+ db $18, $19, $1a, $1b, $1c, $1d, $1e, $07 ; eve
.DungeonColors:
db $00, $01, $02, $03, $04, $05, $06, $07 ; morn
db $08, $09, $0a, $0b, $0c, $0d, $0e, $0f ; day
db $10, $11, $12, $13, $14, $15, $16, $17 ; nite
- db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ; dark
+ db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ; eve
.Env5Colors:
db $00, $01, $02, $03, $04, $05, $06, $07 ; morn
db $08, $09, $0a, $0b, $0c, $0d, $0e, $0f ; day
db $10, $11, $12, $13, $14, $15, $16, $17 ; nite
- db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ; dark
+ db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ; eve
This was mostly a change to comments; the only data changed was in .OutdoorColors
to make the WATER
palette use the overworld water colors. (Maps with the DUNGEON
or ENVIRONMENT_5
environment do not use the overworld water colors; they have a darker, more saturated blue.)
To load the darkness.pal colors for Flash maps, we'll treat it like any other special palette (Ice Path, Battle Tower, etc). Edit engine/tilesets/tileset_palettes.asm:
LoadSpecialMapPalette:
+ call GetMapTimeOfDay
+ bit IN_DARKNESS_F, a
+ jr z, .not_dark
+ ld a, [wStatusFlags]
+ bit STATUSFLAGS_FLASH_F, a
+ jr z, .darkness
+
+.not_dark
ld a, [wMapTileset]
cp TILESET_POKECOM_CENTER
jr z, .pokecom_2f
cp TILESET_BATTLE_TOWER_INSIDE
jr z, .battle_tower_inside
cp TILESET_ICE_PATH
jr z, .ice_path
cp TILESET_HOUSE
jr z, .house
cp TILESET_RADIO_TOWER
jr z, .radio_tower
cp TILESET_MANSION
jr z, .mansion_mobile
jr .do_nothing
+
+.darkness
+ call LoadDarknessPalette
+ scf
+ ret
...
.do_nothing
and a
ret
+
+LoadDarknessPalette:
+ ld a, BANK(wBGPals1)
+ ld de, wBGPals1
+ ld hl, DarknessPalette
+ ld bc, 8 palettes
+ jp FarCopyWRAM
+
+DarknessPalette:
+INCLUDE "gfx/tilesets/darkness.pal"
As for NPCs, the npc_sprites.pal will already get loaded correctly, but we need to handle npc_sprites_darkness.pal. Add this to the end of engine/tilesets/tileset_palettes.asm:
+LoadSpecialNPCPalette:
+ call GetMapTimeOfDay
+ bit IN_DARKNESS_F, a
+ jr z, .do_nothing
+ ld a, [wStatusFlags]
+ bit STATUSFLAGS_FLASH_F, a
+ jr nz, .do_nothing
+
+;darkness
+ call LoadNPCDarknessPalette
+ scf
+ ret
+
+.do_nothing
+ and a
+ ret
+
+LoadNPCDarknessPalette:
+ ld a, BANK(wOBPals1)
+ ld de, wOBPals1
+ ld hl, NPCDarknessPalette
+ ld bc, 8 palettes
+ jp FarCopyWRAM
+
+NPCDarknessPalette:
+INCLUDE "gfx/overworld/npc_sprites_darkness.pal"
We'll just have to call LoadSpecialNPCPalette
in the right place to load the npc_sprites_darkness.pal when necessary. We also still need to handle the new roofs.pal evening colors. Both of those belong in the same place. Edit engine/gfx/color.asm:
LoadMapPals:
farcall LoadSpecialMapPalette
jr c, .got_pals
...
.got_pals
ld a, [wTimeOfDayPal]
maskbits NUM_DAYTIMES
ld bc, 8 palettes
ld hl, MapObjectPals
call AddNTimes
ld de, wOBPals1
ld bc, 8 palettes
ld a, BANK(wOBPals1)
call FarCopyWRAM
+ farcall LoadSpecialNPCPalette
ld a, [wEnvironment]
cp TOWN
jr z, .outside
cp ROUTE
ret nz
.outside
ld a, [wMapGroup]
- ld l, a
- ld h, 0
- add hl, hl
- add hl, hl
- add hl, hl
- ld de, RoofPals
+ add a
+ add a
+ ld e, a
+ ld d, 0
+ ld hl, RoofPals
+ add hl, de
+ add hl, de
add hl, de
ld a, [wTimeOfDayPal]
maskbits NUM_DAYTIMES
cp NITE_F
+ ld de, 4
+ jr z, .nite
jr c, .morn_day
-rept 4
- inc hl
-endr
+; eve
+ add hl, de
+.nite
+ add hl, de
.morn_day
...
We could define unique evening wild encounters, but defining four sets of wild data for every map would take up extra space and be mostly redundant. For now evening will have the same encounters as night.
Edit wram.asm:
- ds 2
+ ds 1
wMornEncounterRate:: db
wDayEncounterRate:: db
wNiteEncounterRate:: db
+wEveEncounterRate:: db
wWaterEncounterRate:: db
Then edit engine/overworld/wildmons.asm:
LoadWildMonData:
call _GrassWildmonLookup
jr c, .copy
ld hl, wMornEncounterRate
xor a
ld [hli], a
ld [hli], a
+ ld [hli], a
ld [hl], a
jr .done_copy
.copy
inc hl
inc hl
ld de, wMornEncounterRate
ld bc, 3
call CopyBytes
+ ld a, [wNiteEncounterRate]
+ ld [wEveEncounterRate], a
.done_copy
call _WaterWildmonLookup
ld a, 0
jr nc, .no_copy
inc hl
inc hl
ld a, [hl]
.no_copy
ld [wWaterEncounterRate], a
ret
+
+GetTimeOfDayNotEve:
+ ld a, [wTimeOfDay]
+ cp EVE_F
+ ret nz
+ ld a, NITE_F ; ld a, DAY_F to make evening use day encounters
+ ret
ChooseWildEncounter:
...
inc hl
inc hl
inc hl
call CheckOnWater
ld de, WaterMonProbTable
jr z, .watermon
inc hl
inc hl
- ld a, [wTimeOfDay]
+ call GetTimeOfDayNotEve
ld bc, NUM_GRASSMON * 2
call AddNTimes
ld de, GrassMonProbTable
.watermon
...
RandomUnseenWildMon:
...
.GetGrassmon:
push hl
ld bc, 5 + 4 * 2 ; Location of the level of the 5th wild Pokemon in that map
add hl, bc
- ld a, [wTimeOfDay]
+ call GetTimeOfDayNotEve
ld bc, NUM_GRASSMON * 2
call AddNTimes
...
RandomPhoneWildMon:
...
.ok
ld bc, 5 + 0 * 2
add hl, bc
- ld a, [wTimeOfDay]
+ call GetTimeOfDayNotEve
inc a
ld bc, NUM_GRASSMON * 2
Edit engine/pokegear/radio.asm:
OaksPKMNTalk4:
; Choose a random route, and a random Pokemon from that route.
...
; Point hl to the list of morning Pokémon., skipping percentages
rept 4
inc hl
endr
; Generate a number, either 0, 1, or 2, to choose a time of day.
+ ; Can't pick 3 since evening does not have wild data.
.loop2
call Random
maskbits NUM_DAYTIMES
- cp DARKNESS_F
+ cp EVE_F
jr z, .loop2
And edit maps/Route29.asm:
Route29CooltrainerMScript:
faceplayer
opentext
checktime DAY
iftrue .day_morn
- checktime NITE
+ checktime EVE | NITE
iftrue .nite
.day_morn
writetext Route29CooltrainerMText_WaitingForNight
waitbutton
closetext
end
.nite
writetext Route29CooltrainerMText_WaitingForMorning
waitbutton
closetext
end
Route29CooltrainerMText_WaitingForNight:
text "I'm waiting for"
line "#MON that"
- para "appear only at"
- line "night."
+ para "appear only in"
+ line "the evening or"
+ cont "at night."
done
Edit engine/rtc/timeset.asm:
GetTimeOfDayString:
ld a, c
cp MORN_HOUR
jr c, .nite
cp DAY_HOUR
jr c, .morn
- cp NITE_HOUR
- jr c, .day
+ cp EVE_HOUR
+ jr c, .day
+ cp NITE_HOUR
+ jr c, .eve
.nite
ld de, .nite_string
ret
.morn
ld de, .morn_string
ret
.day
ld de, .day_string
ret
+.eve
+ ld de, .eve_string
+ ret
.nite_string: db "NITE@"
.morn_string: db "MORN@"
.day_string: db "DAY@"
+.eve_string: db "EVE@"
The time-setting textbox is designed to fit a four-character string, so you could say "DUSK" instead of "EVE", but not "EVENING" (unless you resize the textbox). (I picked "EVE" as a pair with "MORN", since "DUSK" would go more with "DAWN".)
In Crystal, Pokémon record the time, location, level, and OT gender when they're caught. The Poké Seer in Cianwood City can tell you this information for any Pokémon. (Although since the time and level are packed into one byte, the two bits for time leave only six bits for level, which can't accurately report caught levels above 63—probably the reason why Ho-Oh and Lugia are caught at level 60, not 70.)
Edit engine/pokemon/caught_data.asm:
SetCaughtData:
ld a, [wPartyCount]
dec a
ld hl, wPartyMon1CaughtLevel
call GetPartyLocation
SetBoxmonOrEggmonCaughtData:
ld a, [wTimeOfDay]
inc a
rrca
rrca
+ and CAUGHT_TIME_MASK
ld b, a
ld a, [wCurPartyLevel]
or b
ld [hli], a
...
And edit engine/events/poke_seer.asm:
GetCaughtTime:
ld a, [wSeerCaughtData]
and CAUGHT_TIME_MASK
- jr z, .none
-
rlca
rlca
dec a
+ maskbits NUM_DAYTIMES
ld hl, .times
call GetNthString
ld d, h
ld e, l
ld hl, wSeerTimeOfDay
call CopyName2
and a
ret
-
-.none
- ld de, wSeerTimeOfDay
- call UnknownCaughtData
- ret
.times
db "Morning@"
db "Day@"
db "Night@"
+ db "Evening@"
The constants MORN_F
, DAY_F
, and NITE_F
are 0, 1, and 2; originally they get incremented to 1, 2, and 3 when stored as caught data, with 0 representing an unknown time (i.e. a Pokémon without caught data, traded from Gold, Silver, or Gen 1). Now that EVE_F
is 3, we use modular arithmetic with bitmasking so that 3 incremented is 0 instead of 4, and 0 decremented is 3 instead of −1.
Edit engine/pokegear/radio.asm again:
BuenasPasswordCheckTime:
call UpdateTime
ldh a, [hHours]
- cp NITE_HOUR
+ cp EVE_HOUR
ret
And edit maps/RadioTower2F.asm:
Buena:
faceplayer
opentext
checkflag ENGINE_ROCKETS_IN_RADIO_TOWER
iftrue .MidRocketTakeover
checkevent EVENT_MET_BUENA
iffalse .Introduction
checkflag ENGINE_BUENAS_PASSWORD_2
iftrue .PlayedAlready
readvar VAR_HOUR
- ifless NITE_HOUR, .TooEarly
+ ifless EVE_HOUR, .TooEarly
...
RadioTower2FBuenaTuneInAfterSixText:
text "BUENA: Tune in to"
line "PASSWORD every"
- para "night from six to"
- line "midnight!"
+ para "night from five"
+ line "to midnight!"
para "Tune in, then drop"
line "in for a visit!"
done
(Of course, you can use any time range here; it doesn't have to line up with a time of day boundary.)
Edit constants/pokemon_data_constants.asm:
; EVOLVE_HAPPINESS triggers
const_def 1
const TR_ANYTIME
const TR_MORNDAY
- const TR_NITE
+ const TR_EVENITE
Then edit data/pokemon/evos_attacks.asm:
; - Evolution methods:
; * db EVOLVE_LEVEL, level, species
; * db EVOLVE_ITEM, used item, species
; * db EVOLVE_TRADE, held item (or -1 for none), species
-; * db EVOLVE_HAPPINESS, TR_* constant (ANYTIME, MORNDAY, NITE), species
+; * db EVOLVE_HAPPINESS, TR_* constant (ANYTIME, MORNDAY, EVENITE), species
; * db EVOLVE_STAT, level, ATK_*_DEF constant (LT, GT, EQ), species
; - db 0 ; no more evolutions
EeveeEvosAttacks:
db EVOLVE_ITEM, THUNDERSTONE, JOLTEON
db EVOLVE_ITEM, WATER_STONE, VAPOREON
db EVOLVE_ITEM, FIRE_STONE, FLAREON
db EVOLVE_HAPPINESS, TR_MORNDAY, ESPEON
- db EVOLVE_HAPPINESS, TR_NITE, UMBREON
+ db EVOLVE_HAPPINESS, TR_EVENITE, UMBREON
db 0 ; no more evolutions
And edit engine/pokemon/evolve.asm:
-; TR_NITE
+; TR_EVENITE
ld a, [wTimeOfDay]
cp NITE_F
- jp nz, .dont_evolve_3
+ jp c, .dont_evolve_3 ; MORN_F or DAY_F < NITE_F
jr .proceed
.happiness_daylight
ld a, [wTimeOfDay]
cp NITE_F
- jp z, .dont_evolve_3
+ jp nc, .dont_evolve_3 ; NITE_F or EVE_F >= NITE_F
jr .proceed
This was mostly a renaming of TR_NITE
to TR_EVENITE
; the only code changes are the two jp
lines. We're relying on the fact that EVE_F
> NITE_F
for the two cp
comparisons to work. If you skip this step, then TR_MORNDAY
will really be "TR_MORNDAYEVE
", and Eevee will evolve into Espeon in the evening.
Edit engine/battle/start_battle.asm:
ld de, MUSIC_JOHTO_WILD_BATTLE
ld a, [wTimeOfDay]
cp NITE_F
- jr nz, .done
+ jr c, .done ; not NITE_F or EVE_F
ld de, MUSIC_JOHTO_WILD_BATTLE_NIGHT
jr .done
Just like the previous step, if you skip this one, evening will use the regular wild battle music.
After her first scene where she gives you the Pokégear, your mom has different object_event
s, one for each time of day. To add one for evening, edit maps/PlayersHouse1F.asm:
def_object_events
object_event 7, 4, SPRITE_MOM, SPRITEMOVEDATA_STANDING_LEFT, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, MomScript, EVENT_PLAYERS_HOUSE_MOM_1
object_event 2, 2, SPRITE_MOM, SPRITEMOVEDATA_STANDING_UP, 0, 0, -1, MORN, 0, OBJECTTYPE_SCRIPT, 0, MomScript, EVENT_PLAYERS_HOUSE_MOM_2
object_event 7, 4, SPRITE_MOM, SPRITEMOVEDATA_STANDING_LEFT, 0, 0, -1, DAY, 0, OBJECTTYPE_SCRIPT, 0, MomScript, EVENT_PLAYERS_HOUSE_MOM_2
+ object_event 4, 3, SPRITE_MOM, SPRITEMOVEDATA_STANDING_UP, 0, 0, -1, EVE, 0, OBJECTTYPE_SCRIPT, 0, MomScript, EVENT_PLAYERS_HOUSE_MOM_2
object_event 0, 2, SPRITE_MOM, SPRITEMOVEDATA_STANDING_UP, 0, 0, -1, NITE, 0, OBJECTTYPE_SCRIPT, 0, MomScript, EVENT_PLAYERS_HOUSE_MOM_2
object_event 4, 4, SPRITE_POKEFAN_F, SPRITEMOVEDATA_STANDING_RIGHT, 0, 0, -1, -1, PAL_NPC_RED, OBJECTTYPE_SCRIPT, 0, NeighborScript, EVENT_PLAYERS_HOUSE_1F_NEIGHBOR
The neighbor should also have appropriate text for evening; luckily the same "Good evening!" greeting makes sense for both evening and night.
NeighborScript:
faceplayer
opentext
checktime MORN
iftrue .MornScript
checktime DAY
iftrue .DayScript
- checktime NITE
+ checktime EVE | NITE
iftrue .NiteScript
Edit data/text/common_1.asm:
_OakTimeOversleptText::
text "!"
line "I overslept!"
done
_OakTimeYikesText::
text "!"
line "Yikes! I over-"
cont "slept!"
done
_OakTimeSoDarkText::
text "!"
line "No wonder it's so"
cont "dark!"
done
+
+_OakTimeNappedText::
+ text "!"
+ line "I napped for"
+ cont "too long!"
+ done
And edit engine/rtc/timeset.asm again:
OakText_ResponseToSetTime:
text_asm
decoord 1, 14
ld a, [wInitHourBuffer]
ld c, a
call PrintHour
ld [hl], ":"
inc hl
ld de, wInitMinuteBuffer
lb bc, PRINTNUM_LEADINGZEROS | 1, 2
call PrintNum
ld b, h
ld c, l
ld a, [wInitHourBuffer]
cp MORN_HOUR
jr c, .nite
cp DAY_HOUR + 1
jr c, .morn
- cp NITE_HOUR
- jr c, .day
+ cp EVE_HOUR
+ jr c, .day
+ cp NITE_HOUR
+ jr c, .eve
.nite
ld hl, .OakTimeSoDarkText
ret
.morn
ld hl, .OakTimeOversleptText
ret
.day
ld hl, .OakTimeYikesText
ret
+.eve
+ ld hl, .OakTimeNappedText
+ ret
.OakTimeOversleptText:
text_far _OakTimeOversleptText
text_end
.OakTimeYikesText:
text_far _OakTimeYikesText
text_end
.OakTimeSoDarkText:
text_far _OakTimeSoDarkText
text_end
+
+.OakTimeNappedText:
+ text_far _OakTimeNappedText
+ text_end
Edit data/text/std_text.asm:
NurseMornText:
text "Good morning!"
line "Welcome to our"
cont "#MON CENTER."
done
NurseDayText:
text "Hello!"
line "Welcome to our"
cont "#MON CENTER."
done
+
+NurseEveText:
+ text "Good evening!"
+ line "Welcome to our"
+ cont "#MON CENTER."
+ done
NurseNiteText:
text "Good evening!"
line "You're out late."
para "Welcome to our"
line "#MON CENTER."
done
PokeComNurseMornText:
text "Good morning!"
para "This is the #-"
line "MON COMMUNICATION"
para "CENTER--or the"
line "#COM CENTER."
done
PokeComNurseDayText:
text "Hello!"
para "This is the #-"
line "MON COMMUNICATION"
para "CENTER--or the"
line "#COM CENTER."
done
+
+PokeComNurseEveText:
+ text "Good evening."
+
+ para "This is the #-"
+ line "MON COMMUNICATION"
+
+ para "CENTER--or the"
+ line "#COM CENTER."
+ done
PokeComNurseNiteText:
text "Good to see you"
line "working so late."
para "This is the #-"
line "MON COMMUNICATION"
para "CENTER--or the"
line "#COM CENTER."
done
And edit engine/events/std_scripts.asm:
PokecenterNurseScript:
; EVENT_WELCOMED_TO_POKECOM_CENTER is never set
opentext
checktime MORN
iftrue .morn
checktime DAY
iftrue .day
+ checktime EVE
+ iftrue .eve
checktime NITE
iftrue .nite
sjump .ok
.morn
checkevent EVENT_WELCOMED_TO_POKECOM_CENTER
iftrue .morn_comcenter
farwritetext NurseMornText
promptbutton
sjump .ok
.morn_comcenter
farwritetext PokeComNurseMornText
promptbutton
sjump .ok
.day
checkevent EVENT_WELCOMED_TO_POKECOM_CENTER
iftrue .day_comcenter
farwritetext NurseDayText
promptbutton
sjump .ok
.day_comcenter
farwritetext PokeComNurseDayText
promptbutton
sjump .ok
+
+.eve
+ checkevent EVENT_WELCOMED_TO_POKECOM_CENTER
+ iftrue .eve_comcenter
+ farwritetext NurseEveText
+ promptbutton
+ sjump .ok
+.eve_comcenter
+ farwritetext PokeComNurseEveText
+ promptbutton
+ sjump .ok
.nite
checkevent EVENT_WELCOMED_TO_POKECOM_CENTER
iftrue .nite_comcenter
farwritetext NurseNiteText
promptbutton
sjump .ok
.nite_comcenter
farwritetext PokeComNurseNiteText
promptbutton
sjump .ok
Edit engine/phone/scripts/generic_callee.asm:
PhoneScript_AnswerPhone_Male:
checktime DAY
iftrue PhoneScript_AnswerPhone_Male_Day
- checktime NITE
+ checktime EVE | NITE
iftrue PhoneScript_AnswerPhone_Male_Nite
...
PhoneScript_AnswerPhone_Female:
checktime DAY
iftrue PhoneScript_AnswerPhone_Female_Day
- checktime NITE
+ checktime EVE | NITE
iftrue PhoneScript_AnswerPhone_Female_Nite
...
PhoneScript_GreetPhone_Male:
checktime DAY
iftrue PhoneScript_GreetPhone_Male_Day
- checktime NITE
+ checktime EVE | NITE
iftrue PhoneScript_GreetPhone_Male_Nite
...
PhoneScript_GreetPhone_Female:
checktime DAY
iftrue PhoneScript_GreetPhone_Female_Day
- checktime NITE
+ checktime EVE | NITE
iftrue PhoneScript_GreetPhone_Female_Nite
...
Edit engine/phone/scripts/bill.asm:
BillPhoneCalleeScript:
checktime DAY
iftrue .daygreet
- checktime NITE
+ checktime EVE | NITE
iftrue .nitegreet
farwritetext BillPhoneMornGreetingText
promptbutton
sjump .main
Edit engine/phone/scripts/hangups.asm:
KenjiAnswerPhoneScript:
...
.OnBreak:
checktime MORN
iftrue .Morning
- checktime NITE
+ checktime EVE | NITE
iftrue .Night
setevent EVENT_KENJI_ON_BREAK
farwritetext KenjiTakingABreakText
promptbutton
sjump PhoneScript_HangUpText_Male
And edit maps/Route45.asm:
TrainerBlackbeltKenji:
...
.Registered:
readvar VAR_KENJI_BREAK
ifnotequal 1, Route45NumberAcceptedM
checktime MORN
iftrue .Morning
- checktime NITE
+ checktime EVE | NITE
iftrue .Night
checkevent EVENT_KENJI_ON_BREAK
iffalse Route45NumberAcceptedM
...
Unless I've missed something, that's all the RTC-related features and content, now with support for the evening!
There are some features limited to night that I left alone, so they aren't extended to happen in the evening. Two in particular: Officer trainers only battle you at night, and phone call trainers who give you items only change behavior at night. So in the evening, they act like it's morning or day.
You'll need at least version 4.5.2 (or 2.5.2++) of Polished Map to view and edit evening colors. It detects that darkness has been replaced by evening in bg_tiles.pal because there are four overworld water colors. (If there were three, it would assume they apply to morning, day, and night, and that darkness still exists.) It can't automatically detect that evening roof colors exist, so you have to change the Options→Roof Palettes selection to Morn + Day, Night, Custom; then the evening colors will be loaded into the Custom palette.