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](https://github.com/PiaCarrot/pokeorange).) ## Contents 1. [Define new constants and data tables](#1-define-new-constants-and-data-tables) 2. [Start weather based on the data tables](#2-start-weather-based-on-the-data-tables) ## 1. Define new constants and data tables Create **data/battle/automatic_weather.asm**: ```diff +; AutomaticWeatherEffects indexes + const_def 1 + const AUTOMATIC_SUN + const AUTOMATIC_RAIN + const AUTOMATIC_SANDSTORM + +AutomaticWeatherMaps: +auto_weather_map: MACRO + map_id \1 ; map + db \2 ; AUTOMATIC_* weather index +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 +auto_weather_effect: MACRO + db \1 ; battle weather + dw \2 ; animation + dw \3 ; text +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](../blob/master/engine/battle/core.asm): ```diff 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: 1. Set `[wBattleWeather]` to the right weather ID 2. Set `[wWeatherCount]` to 255 turns (the maximum possible in one byte) 3. Call `Call_PlayBattleAnim` to play the right animation, stored in `de` 4. Call `StdBattleTextbox` to show the right starting text, stored in `hl` 5. 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](../blob/master/engine/battle/move_effects/sunny_day.asm), [rain_dance.asm](../blob/master/engine/battle/move_effects/rain_dance.asm), and [sandstorm.asm](../blob/master/engine/battle/move_effects/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: ![Screenshot](screenshots/automatic-weather.png) 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.