diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 3760ac2524..8fa1e29a30 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -52,6 +52,7 @@ - [How to use Generic Spots](tutorials/how_to_spots.md) - [How to use Dynamic Shop](tutorials/how_to_dynamic_shop.md) - [How to use Overworld Wild Encounters](tutorials/how_to_overworld_wild_encounters.md) + - [How to use Dynamic Weather](tutorials/how_to_dynamic_weather.md) - [Changelog](./CHANGELOG.md) - [1.15.x]() - [Version 1.15.2](changelogs/1.15.x/1.15.2.md) diff --git a/docs/tutorials/how_to_dynamic_weather.md b/docs/tutorials/how_to_dynamic_weather.md new file mode 100644 index 0000000000..b5601ba485 --- /dev/null +++ b/docs/tutorials/how_to_dynamic_weather.md @@ -0,0 +1,38 @@ +# How to Use Dynamic Weather +*Written by Jordan/harbingerofruination* + +## What Is Dynamic Weather? + +WEATHER_DYNAMIC applies random daily weather to any map that uses it in its header, updating the weather any time the daily seed resets. + +## How Do I Use Dynamic Weather? + +Using dynamic weather was made to be as simple as possible, while still allowing for some customization. It's only a two-step process. + +### Step 1: Set Map to Use Dynamic Weather + +In either porymap or the map json, change the weather header for the map that you want to have dynamic weather to WEATHER_DYNAMIC. + +### Step 2: Define Chooseable Weathers + +By default, using WEATHER_DYNAMIC will randomly select from `sDefaultDynamicWeathers[]` in `src/field_weather_effect.c`, which contains sun, rain, snow, sandstorm, volcanic ash, thunderstorm, and drought as possible weathers. However, this may not be ideal for some locations. Let's say, for example, you want Dewford town to have dynamic weather. You'll need to define an array containing the possible weathers that you want. There is currently one commented out you can use as a template, sDynamicWeathers_DewfordTown[]. Let's take a look: + +``` +static const u8 sDynamicWeathers_DewfordTown[] = +{ + WEATHER_SUNNY, + WEATHER_RAIN, + WEATHER_RAIN_THUNDERSTORM, +}; +``` + +With this, we can make Dewford feel more tropical, without the sandstorm, volcanic ash, and snow. We will also need to make entry in sDynamicWeatherPools, such that any maps in MAPSEC_DEWFORD_TOWN share that dynamic weather pool as well as the specific daily weather. That'll look like this: + +``` +static const struct DynamicWeatherPool sDynamicWeatherPools[] = +{ + { MAPSEC_DEWFORD_TOWN, DYNAMIC_WEATHER_POOL(sDynamicWeathers_DewfordTown) }, +}; +``` + +Now, any maps in MAPSEC_DEWFORD_TOWN with WEATHER_DYNAMIC in their header will select from `sDynamicWeathers_DewfordTown[]` instead of the default `sDefaultDynamicWeathers[]` array. \ No newline at end of file diff --git a/include/constants/weather.h b/include/constants/weather.h index d79e8f1717..e7bae218fb 100644 --- a/include/constants/weather.h +++ b/include/constants/weather.h @@ -20,7 +20,8 @@ #define WEATHER_ROUTE119_CYCLE 20 #define WEATHER_ROUTE123_CYCLE 21 #define WEATHER_FOG 22 // Aggregate of WEATHER_FOG_HORIZONTAL and WEATHER_FOG_DIAGONAL -#define WEATHER_COUNT 23 +#define WEATHER_DYNAMIC 23 +#define WEATHER_COUNT 24 // These are used in maps' coord_weather_event entries. // They are not a one-to-one mapping with the engine's diff --git a/src/field_weather_effect.c b/src/field_weather_effect.c index e6da3acf84..52fe9cb5f0 100644 --- a/src/field_weather_effect.c +++ b/src/field_weather_effect.c @@ -6,6 +6,7 @@ #include "overworld.h" #include "random.h" #include "script.h" +#include "constants/region_map_sections.h" #include "constants/weather.h" #include "constants/songs.h" #include "constants/rgb.h" @@ -2509,6 +2510,7 @@ static void CreateAbnormalWeatherTask(void) static u8 TranslateWeatherNum(u8); static void UpdateRainCounter(u8, u8); +static u8 GetDynamicWeather(void); void SetSavedWeather(u32 weather) { @@ -2596,6 +2598,76 @@ static const u8 sWeatherCycleRoute123[WEATHER_CYCLE_LENGTH] = WEATHER_SUNNY, }; +#define DYNAMIC_WEATHER_POOL(pool) pool, ARRAY_COUNT(pool) + +struct DynamicWeatherPool +{ + mapsec_u16_t mapSec; + const u8 *weathers; + u8 count; +}; + +static const u8 sDefaultDynamicWeathers[] = +{ + WEATHER_SUNNY, + WEATHER_RAIN, + WEATHER_SNOW, + WEATHER_SANDSTORM, + WEATHER_VOLCANIC_ASH, + WEATHER_RAIN_THUNDERSTORM, + WEATHER_DROUGHT, +}; + +/*static const u8 sDynamicWeathers_DewfordTown[] = +{ + WEATHER_SUNNY, + WEATHER_RAIN, + WEATHER_RAIN_THUNDERSTORM, +};*/ + +static const struct DynamicWeatherPool sDynamicWeatherPools[] = +{ + /*{ MAPSEC_DEWFORD_TOWN, DYNAMIC_WEATHER_POOL(sDynamicWeathers_DewfordTown) },*/ +}; + +static const u8 *GetDynamicWeatherPool(u8 *count) +{ + u16 mapSec = gMapHeader.regionMapSectionId; + + for (u32 i = 0; i < ARRAY_COUNT(sDynamicWeatherPools); i++) + { + if (sDynamicWeatherPools[i].mapSec == mapSec && sDynamicWeatherPools[i].count != 0) + { + *count = sDynamicWeatherPools[i].count; + return sDynamicWeatherPools[i].weathers; + } + } + + *count = ARRAY_COUNT(sDefaultDynamicWeathers); + return sDefaultDynamicWeathers; +} + +static u8 GetDynamicWeather(void) +{ + u8 count; + const u8 *weathers = GetDynamicWeatherPool(&count); + rng_value_t localRngState; + const u32 hashPieces[] = + { + gSaveBlock1Ptr->dailySeed, + gSaveBlock1Ptr->location.mapGroup, + gSaveBlock1Ptr->location.mapNum, + gMapHeader.mapLayoutId, + gMapHeader.regionMapSectionId, + }; + + if (count == 0) + return WEATHER_NONE; + + localRngState = LocalRandomSeed(Crc32B((const u8 *)hashPieces, sizeof(hashPieces))); + return weathers[LocalRandom32(&localRngState) % count]; +} + static u8 TranslateWeatherNum(u8 weather) { switch (weather) @@ -2618,6 +2690,7 @@ static u8 TranslateWeatherNum(u8 weather) case WEATHER_ABNORMAL: return WEATHER_ABNORMAL; case WEATHER_ROUTE119_CYCLE: return sWeatherCycleRoute119[gSaveBlock1Ptr->weatherCycleStage]; case WEATHER_ROUTE123_CYCLE: return sWeatherCycleRoute123[gSaveBlock1Ptr->weatherCycleStage]; + case WEATHER_DYNAMIC: return GetDynamicWeather(); default: return WEATHER_NONE; } }