Automatic battle weather on certain maps - pret/pokecrystal GitHub Wiki
Gen 2 introduced the weather moves Sunny Day, Rain Dance, and Sandstorm; but Gen 3 introduced automatic weather on certain maps, like rain on Routes 119 and 120. We can't replicate the overworld effects of weather on the GameBoy Color, but we can trigger weather in battle based on your map.
(The code for this feature was adapted from Pokémon Orange.)
Contents
1. Define new constants and data tables
Create data/battle/automatic_weather.asm:
+; AutomaticWeatherEffects indexes
+ const_def 1
+ const AUTOMATIC_SUN
+ const AUTOMATIC_RAIN
+ const AUTOMATIC_SANDSTORM
+
+AutomaticWeatherMaps:
+MACRO auto_weather_map
+;\1: map id
+;\2: AUTOMATIC_* weather index
+ map_id \1
+ db \2
+ENDM
+ auto_weather_map TIN_TOWER_ROOF, AUTOMATIC_SUN
+ auto_weather_map ROUTE_43, AUTOMATIC_RAIN
+ auto_weather_map LAKE_OF_RAGE, AUTOMATIC_RAIN
+ auto_weather_map ROUTE_45, AUTOMATIC_SANDSTORM
+ db 0 ; end
+
+AutomaticWeatherEffects:
+; entries correspond to AUTOMATIC_* constants
+MACRO auto_weather_effect
+;\1: battle weather
+;\2: animation
+;\3: text
+ db \1
+ dw \2
+ dw \3
+ENDM
+ auto_weather_effect WEATHER_SUN, SUNNY_DAY, SunGotBrightText
+ auto_weather_effect WEATHER_RAIN, RAIN_DANCE, DownpourText
+ auto_weather_effect WEATHER_SANDSTORM, ANIM_IN_SANDSTORM, SandstormBrewedText
This file defines a set of constants and two data tables.
The AUTOMATIC_*
constants are used by entries in the AutomaticWeatherMaps
table, and are indexes for the AutomaticWeatherEffects
table.
The AutomaticWeatherMaps
table lists our maps and their corresponding automatic weather effects, with a 0 to mark the end of the table.
The AutomaticWeatherEffects
table lists the data needed to start a weather effect: the effect ID, animation ID, and starting text.
Both of these tables use macros to define their entries. This helps to avoid mixing up db
, dw
, etc, and clearly marks each entry on its own line.
2. Start weather based on the data tables
Edit engine/battle/core.asm:
DoBattle:
...
.not_linked_2
+ call StartAutomaticBattleWeather
jp BattleTurn
.tutorial_debug
jp BattleMenu
+
+StartAutomaticBattleWeather:
+ call GetAutomaticBattleWeather
+ and a
+ ret z
+; get current AutomaticWeatherEffects entry
+ dec a
+ ld hl, AutomaticWeatherEffects
+ ld bc, 5 ; size of one entry
+ call AddNTimes
+; [wBattleWeather] = weather
+ ld a, [hli]
+ ld [wBattleWeather], a
+; de = animation
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a
+; hl = text pointer
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+; start weather for 255 turns
+ ld a, 255
+ ld [wWeatherCount], a
+ push hl
+ call Call_PlayBattleAnim ; uses de
+ pop hl
+ call StdBattleTextbox ; uses hl
+ jp EmptyBattleTextbox
+
+GetAutomaticBattleWeather:
+ ld hl, AutomaticWeatherMaps
+ ld a, [wMapGroup]
+ ld b, a
+ ld a, [wMapNumber]
+ ld c, a
+.loop
+ ld a, [hli] ; group
+ and a
+ ret z ; end
+ cp b
+ jr nz, .wrong_group
+ ld a, [hli] ; map
+ cp c
+ jr nz, .wrong_map
+ ld a, [hl] ; weather
+ ret
+
+.wrong_group:
+ inc hl ; skip map
+.wrong_map
+ inc hl ; skip weather
+ jr .loop
+
+INCLUDE "data/battle/automatic_weather.asm"
Here we define two routines, StartAutomaticBattleWeather
and GetAutomaticBattleWeather
.
StartAutomaticBattleWeather
is called at the very end of battle setup, just before the alternating player and enemy turns start. It calls GetAutomaticBattleWeather
to see if any weather should start, and if it should, it does so in five steps:
- Set
[wBattleWeather]
to the right weather ID - Set
[wWeatherCount]
to 255 turns (the maximum possible in one byte) - Call
Call_PlayBattleAnim
to play the right animation, stored inde
- Call
StdBattleTextbox
to show the right starting text, stored inhl
- Clear the text it just printed so the player's turn can start
These steps are adapted from the individual weather move effects in engine/battle/move_effects/sunny_day.asm, rain_dance.asm, and sandstorm.asm. The relevant data values are read from AutomaticWeatherEffects
.
GetAutomaticBattleWeather
compares the map group and number, in [wMapGroup]
and [wMapNumber]
, to the values in each entry of AutomaticWeatherMaps
. If one matches, it uses the automatic weather index at the end of the entry.
Now we have automatic sun in Ho-Oh's domain, the roof of Tin Tower; rain around Lake of Rage; and sandstorm in the canyon of Route 45:
By implementing this feature with abstract data tables, instead of hard-coding everything, it's easy to update. You can add automatic rain in WHIRL_ISLAND_LUGIA_CHAMBER
just by adding one line to AutomaticWeatherMaps
, without having to rewrite any assembly code.
The techniques used here can be applied to many different features. If you want to take similar but different actions depending on certain game state, then make tables that associate the state values with the corresponding action parameters; then implement the action in an abstract way so that it can get its parameters from the table.