summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/credits.c4
-rw-r--r--src/field_weather.c1169
-rw-r--r--src/field_weather_effects.c2320
-rw-r--r--src/field_weather_util.c105
-rw-r--r--src/ice.c4
-rw-r--r--src/title_screen.c16
6 files changed, 3614 insertions, 4 deletions
diff --git a/src/credits.c b/src/credits.c
index e0d57a2a4..4d7bbd67b 100644
--- a/src/credits.c
+++ b/src/credits.c
@@ -910,7 +910,7 @@ static s32 RollCredits(void)
case CREDITSSCRCMD_MAPNEXT:
sCreditsMgr->mainseqno = CREDITSSCENE_MAPNEXT_DESTROYWINDOW;
sCreditsMgr->whichMon = sCreditsScript[sCreditsMgr->scrcmdidx].param;
- FieldWeather_StartFadingOutCreditsMap(1, 0, 0x3FFFFFFF);
+ FadeSelectedPals(1, 0, 0x3FFFFFFF);
break;
case CREDITSSCRCMD_MAP:
sCreditsMgr->mainseqno = CREDITSSCENE_MAP_LOADMAP_CREATESPRITES;
@@ -1305,7 +1305,7 @@ static s32 RollCredits(void)
"\tldr r2, _080F3E0C @ =0x3fffffff\n"
"\tmovs r0, 0x1\n"
"\tmovs r1, 0\n"
- "\tbl FieldWeather_StartFadingOutCreditsMap\n"
+ "\tbl FadeSelectedPals\n"
"\tb _080F3E94\n"
"\t.align 2, 0\n"
"_080F3E04: .4byte sCreditsMgr\n"
diff --git a/src/field_weather.c b/src/field_weather.c
new file mode 100644
index 000000000..1185f5763
--- /dev/null
+++ b/src/field_weather.c
@@ -0,0 +1,1169 @@
+#include "global.h"
+#include "gflib.h"
+#include "blend_palette.h"
+#include "field_effect.h"
+#include "field_weather.h"
+#include "field_weather_util.h"
+#include "field_weather_effects.h"
+#include "task.h"
+#include "trig.h"
+#include "constants/field_weather.h"
+#include "constants/weather.h"
+#include "constants/songs.h"
+
+#define DROUGHT_COLOR_INDEX(color) ((((color) >> 1) & 0xF) | (((color) >> 2) & 0xF0) | (((color) >> 3) & 0xF00))
+
+enum
+{
+ GAMMA_NONE,
+ GAMMA_NORMAL,
+ GAMMA_ALT,
+};
+
+struct RGBColor
+{
+ u16 r:5;
+ u16 g:5;
+ u16 b:5;
+};
+
+struct WeatherPaletteData
+{
+ u16 gammaShiftColors[8][0x1000]; // 0x1000 is the number of bytes that make up all palettes.
+};
+
+struct WeatherCallbacks
+{
+ void (*initVars)(void);
+ void (*main)(void);
+ void (*initAll)(void);
+ bool8 (*finish)(void);
+};
+
+static EWRAM_DATA struct Weather sWeather = {};
+static EWRAM_DATA u8 sFieldEffectPaletteGammaTypes[32] = {};
+static EWRAM_DATA const u8 *sPaletteGammaTypes = NULL;
+static EWRAM_DATA u16 gUnknown_20386A8 = 0;
+
+static void Task_WeatherMain(u8 taskId);
+static void Task_WeatherInit(u8 taskId);
+static void None_Init(void);
+static void None_Main(void);
+static bool8 None_Finish(void);
+static void BuildGammaShiftTables(void);
+static void UpdateWeatherGammaShift(void);
+static void ApplyGammaShift(u8 startPalIndex, u8 numPalettes, s8 gammaIndex);
+static void ApplyGammaShiftWithBlend(u8 startPalIndex, u8 numPalettes, s8 gammaIndex, u8 blendCoeff, u16 blendColor);
+static void ApplyDroughtGammaShiftWithBlend(s8 gammaIndex, u8 blendCoeff, u16 blendColor);
+static void FadeInScreenWithWeather(void);
+static bool8 FadeInScreen_RainShowShade(void);
+static bool8 FadeInScreen_Drought(void);
+static bool8 FadeInScreen_FogHorizontal(void);
+static void DoNothing(void);
+static void ApplyFogBlend(u8 blendCoeff, u16 blendColor);
+static bool8 LightenSpritePaletteInFog(u8 paletteIndex);
+
+struct Weather *const gWeatherPtr = &sWeather;
+
+static const struct WeatherCallbacks sWeatherFuncs[] = {
+ {None_Init, None_Main, None_Init, None_Finish},
+ {Clouds_InitVars, Clouds_Main, Clouds_InitAll, Clouds_Finish},
+ {Sunny_InitVars, Sunny_Main, Sunny_InitAll, Sunny_Finish},
+ {Rain_InitVars, Rain_Main, Rain_InitAll, Rain_Finish},
+ {Snow_InitVars, Snow_Main, Snow_InitAll, Snow_Finish},
+ {Thunderstorm_InitVars, Thunderstorm_Main, Thunderstorm_InitAll, Thunderstorm_Finish},
+ {FogHorizontal_InitVars, FogHorizontal_Main, FogHorizontal_InitAll, FogHorizontal_Finish},
+ {Ash_InitVars, Ash_Main, Ash_InitAll, Ash_Finish},
+ {Sandstorm_InitVars, Sandstorm_Main, Sandstorm_InitAll, Sandstorm_Finish},
+ {FogDiagonal_InitVars, FogDiagonal_Main, FogDiagonal_InitAll, FogDiagonal_Finish},
+ {FogHorizontal_InitVars, FogHorizontal_Main, FogHorizontal_InitAll, FogHorizontal_Finish},
+ {Shade_InitVars, Shade_Main, Shade_InitAll, Shade_Finish},
+ {Drought_InitVars, Drought_Main, Drought_InitAll, Drought_Finish},
+ {Downpour_InitVars, Thunderstorm_Main, Downpour_InitAll, Thunderstorm_Finish},
+ {Bubbles_InitVars, Bubbles_Main, Bubbles_InitAll, Bubbles_Finish},
+};
+
+static void (*const sWeatherPalStateFuncs[])(void) = {
+ UpdateWeatherGammaShift,
+ FadeInScreenWithWeather,
+ DoNothing,
+ DoNothing
+};
+
+static const u8 sBasePaletteGammaTypes[32] = {
+ // background palettes
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NONE,
+ GAMMA_NONE,
+ GAMMA_NONE,
+ // sprite palettes
+ GAMMA_ALT,
+ GAMMA_NORMAL,
+ GAMMA_ALT,
+ GAMMA_ALT,
+ GAMMA_ALT,
+ GAMMA_ALT,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_ALT,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+ GAMMA_NORMAL,
+};
+
+const u16 gUnknown_83C2CE0[] = INCBIN_U16("graphics/field_effects/unk_83C2CE0.gbapal");
+const u16 gCloudsWeatherPalette[] = INCBIN_U16("graphics/weather/cloud.gbapal");
+const u16 gSandstormWeatherPalette[] = INCBIN_U16("graphics/weather/sandstorm.gbapal");
+const u8 gWeatherFogDiagonalTiles[] = INCBIN_U8("graphics/weather/fog_diagonal.4bpp");
+const u8 gWeatherFogHorizontalTiles[] = INCBIN_U8("graphics/weather/fog_horizontal.4bpp");
+const u8 gWeatherCloudTiles[] = INCBIN_U8("graphics/weather/cloud.4bpp");
+const u8 gWeatherSnow1Tiles[] = INCBIN_U8("graphics/weather/snow0.4bpp");
+const u8 gWeatherSnow2Tiles[] = INCBIN_U8("graphics/weather/snow1.4bpp");
+const u8 gWeatherBubbleTiles[] = INCBIN_U8("graphics/weather/bubble.4bpp");
+const u8 gWeatherAshTiles[] = INCBIN_U8("graphics/weather/ash.4bpp");
+const u8 gWeatherRainTiles[] = INCBIN_U8("graphics/weather/rain.4bpp");
+const u8 gWeatherSandstormTiles[] = INCBIN_U8("graphics/weather/sandstorm.4bpp");
+
+// code
+void StartWeather(void)
+{
+ if (!FuncIsActiveTask(Task_WeatherMain))
+ {
+ u8 index = AllocSpritePalette(0x1200);
+ CpuCopy32(gUnknown_83C2CE0, &gPlttBufferUnfaded[0x100 + index * 16], 32);
+ sub_8083598(index);
+ BuildGammaShiftTables();
+ gWeatherPtr->altGammaSpritePalIndex = index;
+ gWeatherPtr->weatherPicSpritePalIndex = index;
+ gWeatherPtr->rainSpriteCount = 0;
+ gWeatherPtr->curRainSpriteIndex = 0;
+ gWeatherPtr->cloudSpritesCreated = FALSE;
+ gWeatherPtr->snowflakeSpriteCount = 0;
+ gWeatherPtr->ashSpritesCreated = FALSE;
+ gWeatherPtr->fogHSpritesCreated = FALSE;
+ gWeatherPtr->fogDSpritesCreated = FALSE;
+ gWeatherPtr->sandstormSpritesCreated = FALSE;
+ gWeatherPtr->sandstormSwirlSpritesCreated = FALSE;
+ gWeatherPtr->bubblesSpritesCreated = FALSE;
+ gWeatherPtr->lightenedFogSpritePalsCount = 0;
+ Weather_SetBlendCoeffs(16, 0);
+ gWeatherPtr->currWeather = WEATHER_NONE;
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE;
+ gWeatherPtr->readyForInit = FALSE;
+ gWeatherPtr->weatherChangeComplete = TRUE;
+ gWeatherPtr->taskId = CreateTask(Task_WeatherInit, 80);
+ }
+}
+
+void SetNextWeather(u8 weather)
+{
+ if (weather != WEATHER_RAIN && weather != WEATHER_RAIN_THUNDERSTORM && weather != WEATHER_DOWNPOUR)
+ {
+ PlayRainStoppingSoundEffect();
+ }
+
+ if (gWeatherPtr->nextWeather != weather && gWeatherPtr->currWeather == weather)
+ {
+ sWeatherFuncs[weather].initVars();
+ }
+
+ gWeatherPtr->weatherChangeComplete = FALSE;
+ gWeatherPtr->nextWeather = weather;
+ gWeatherPtr->finishStep = 0;
+}
+
+
+void SetCurrentAndNextWeather(u8 weather)
+{
+ PlayRainStoppingSoundEffect();
+ gWeatherPtr->currWeather = weather;
+ gWeatherPtr->nextWeather = weather;
+}
+
+static void SetCurrentAndNextWeatherNoDelay(u8 weather)
+{
+ PlayRainStoppingSoundEffect();
+ gWeatherPtr->currWeather = weather;
+ gWeatherPtr->nextWeather = weather;
+ // Overrides the normal delay during screen fading.
+ gWeatherPtr->readyForInit = TRUE;
+}
+
+static void Task_WeatherInit(u8 taskId)
+{
+ // Waits until it's ok to initialize weather.
+ // When the screen fades in, this is set to TRUE.
+ if (gWeatherPtr->readyForInit)
+ {
+ sWeatherFuncs[gWeatherPtr->currWeather].initAll();
+ gTasks[taskId].func = Task_WeatherMain;
+ }
+}
+
+static void Task_WeatherMain(u8 taskId)
+{
+ if (gWeatherPtr->currWeather != gWeatherPtr->nextWeather)
+ {
+ if (!sWeatherFuncs[gWeatherPtr->currWeather].finish()
+ /*&& gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_SCREEN_FADING_OUT*/)
+ {
+ // Finished cleaning up previous weather. Now transition to next weather.
+ sWeatherFuncs[gWeatherPtr->nextWeather].initVars();
+ gWeatherPtr->gammaStepFrameCounter = 0;
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_CHANGING_WEATHER;
+ gWeatherPtr->currWeather = gWeatherPtr->nextWeather;
+ gWeatherPtr->weatherChangeComplete = TRUE;
+ }
+ }
+ else
+ {
+ sWeatherFuncs[gWeatherPtr->currWeather].main();
+ }
+
+ sWeatherPalStateFuncs[gWeatherPtr->palProcessingState]();
+}
+
+
+static void None_Init(void)
+{
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 0;
+}
+
+static void None_Main(void)
+{
+}
+
+static u8 None_Finish(void)
+{
+ return 0;
+}
+
+// Builds two tables that contain gamma shifts for palette colors.
+// It's unclear why the two tables aren't declared as const arrays, since
+// this function always builds the same two tables.
+static void BuildGammaShiftTables(void)
+{
+ u16 v0;
+ u8 (*gammaTable)[32];
+ u16 v2;
+ u16 v4;
+ u16 v5;
+ u16 gammaIndex;
+ u16 v9;
+ u32 v10;
+ u16 v11;
+ s16 dunno;
+
+ sPaletteGammaTypes = sBasePaletteGammaTypes;
+ for (v0 = 0; v0 <= 1; v0++)
+ {
+ if (v0 == 0)
+ gammaTable = gWeatherPtr->gammaShifts;
+ else
+ gammaTable = gWeatherPtr->altGammaShifts;
+
+ for (v2 = 0; v2 < 32; v2++)
+ {
+ v4 = v2 << 8;
+ if (v0 == 0)
+ v5 = (v2 << 8) / 16;
+ else
+ v5 = 0;
+ for (gammaIndex = 0; gammaIndex <= 2; gammaIndex++)
+ {
+ v4 = (v4 - v5);
+ gammaTable[gammaIndex][v2] = v4 >> 8;
+ }
+ v9 = v4;
+ v10 = 0x1f00 - v4;
+ if ((0x1f00 - v4) < 0)
+ {
+ v10 += 0xf;
+ }
+ v11 = v10 >> 4;
+ if (v2 < 12)
+ {
+ for (; gammaIndex < 19; gammaIndex++)
+ {
+ v4 += v11;
+ dunno = v4 - v9;
+ if (dunno > 0)
+ v4 -= (dunno + ((u16)dunno >> 15)) >> 1;
+ gammaTable[gammaIndex][v2] = v4 >> 8;
+ if (gammaTable[gammaIndex][v2] > 0x1f)
+ gammaTable[gammaIndex][v2] = 0x1f;
+ }
+ }
+ else
+ {
+ for (; gammaIndex < 19; gammaIndex++)
+ {
+ v4 += v11;
+ gammaTable[gammaIndex][v2] = v4 >> 8;
+ if (gammaTable[gammaIndex][v2] > 0x1f)
+ gammaTable[gammaIndex][v2] = 0x1f;
+ }
+ }
+ }
+ }
+}
+
+// When the weather is changing, it gradually updates the palettes
+// towards the desired gamma shift.
+static void UpdateWeatherGammaShift(void)
+{
+ if (gWeatherPtr->gammaIndex == gWeatherPtr->gammaTargetIndex)
+ {
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE;
+ }
+ else
+ {
+ if (++gWeatherPtr->gammaStepFrameCounter >= gWeatherPtr->gammaStepDelay)
+ {
+ gWeatherPtr->gammaStepFrameCounter = 0;
+ if (gWeatherPtr->gammaIndex < gWeatherPtr->gammaTargetIndex)
+ gWeatherPtr->gammaIndex++;
+ else
+ gWeatherPtr->gammaIndex--;
+
+ ApplyGammaShift(0, 32, gWeatherPtr->gammaIndex);
+ }
+ }
+}
+
+static void FadeInScreenWithWeather(void)
+{
+ if (++gWeatherPtr->unknown_6CB > 1)
+ gWeatherPtr->unknown_6CA = 0;
+
+ switch (gWeatherPtr->currWeather)
+ {
+ case WEATHER_RAIN:
+ case WEATHER_RAIN_THUNDERSTORM:
+ case WEATHER_DOWNPOUR:
+ case WEATHER_SNOW:
+ case WEATHER_SHADE:
+ if (FadeInScreen_RainShowShade() == FALSE)
+ {
+ gWeatherPtr->gammaIndex = 3;
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE;
+ }
+ break;
+ case WEATHER_DROUGHT:
+ if (FadeInScreen_Drought() == FALSE)
+ {
+ gWeatherPtr->gammaIndex = -6;
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE;
+ }
+ break;
+ case WEATHER_FOG_HORIZONTAL:
+ if (FadeInScreen_FogHorizontal() == FALSE)
+ {
+ gWeatherPtr->gammaIndex = 0;
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE;
+ }
+ break;
+ case WEATHER_VOLCANIC_ASH:
+ case WEATHER_SANDSTORM:
+ case WEATHER_FOG_DIAGONAL:
+ case WEATHER_UNDERWATER:
+ default:
+ if (!gPaletteFade.active)
+ {
+ gWeatherPtr->gammaIndex = gWeatherPtr->gammaTargetIndex;
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE;
+ }
+ break;
+ }
+}
+
+static bool8 FadeInScreen_RainShowShade(void)
+{
+ if (gWeatherPtr->fadeScreenCounter == 16)
+ return FALSE;
+
+ if (++gWeatherPtr->fadeScreenCounter >= 16)
+ {
+ ApplyGammaShift(0, 32, 3);
+ gWeatherPtr->fadeScreenCounter = 16;
+ return FALSE;
+ }
+
+ ApplyGammaShiftWithBlend(0, 32, 3, 16 - gWeatherPtr->fadeScreenCounter, gWeatherPtr->fadeDestColor);
+ return TRUE;
+}
+
+static bool8 FadeInScreen_Drought(void)
+{
+ if (gWeatherPtr->fadeScreenCounter == 16)
+ return FALSE;
+
+ if (++gWeatherPtr->fadeScreenCounter >= 16)
+ {
+ ApplyGammaShift(0, 32, -6);
+ gWeatherPtr->fadeScreenCounter = 16;
+ return FALSE;
+ }
+
+ ApplyDroughtGammaShiftWithBlend(-6, 16 - gWeatherPtr->fadeScreenCounter, gWeatherPtr->fadeDestColor);
+ return TRUE;
+}
+
+static bool8 FadeInScreen_FogHorizontal(void)
+{
+ if (gWeatherPtr->fadeScreenCounter == 16)
+ return FALSE;
+
+ gWeatherPtr->fadeScreenCounter++;
+ ApplyFogBlend(16 - gWeatherPtr->fadeScreenCounter, gWeatherPtr->fadeDestColor);
+ return TRUE;
+}
+
+static void DoNothing(void)
+{ }
+
+static void ApplyGammaShift(u8 startPalIndex, u8 numPalettes, s8 gammaIndex)
+{
+ u16 curPalIndex;
+ u16 palOffset;
+ u8 *gammaTable;
+ u16 i;
+
+ if (gammaIndex > 0)
+ {
+ gammaIndex--;
+ palOffset = startPalIndex * 16;
+ numPalettes += startPalIndex;
+ curPalIndex = startPalIndex;
+
+ // Loop through the speficied palette range and apply necessary gamma shifts to the colors.
+ while (curPalIndex < numPalettes)
+ {
+ if (sPaletteGammaTypes[curPalIndex] == GAMMA_NONE)
+ {
+ // No palette change.
+ CpuFastCopy(gPlttBufferUnfaded + palOffset, gPlttBufferFaded + palOffset, 16 * sizeof(u16));
+ palOffset += 16;
+ }
+ else
+ {
+ u8 r, g, b;
+
+ if (sPaletteGammaTypes[curPalIndex] == GAMMA_ALT || curPalIndex - 16 == gWeatherPtr->altGammaSpritePalIndex)
+ gammaTable = gWeatherPtr->altGammaShifts[gammaIndex];
+ else
+ gammaTable = gWeatherPtr->gammaShifts[gammaIndex];
+
+ for (i = 0; i < 16; i++)
+ {
+ // Apply gamma shift to the original color.
+ struct RGBColor baseColor = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset];
+ r = gammaTable[baseColor.r];
+ g = gammaTable[baseColor.g];
+ b = gammaTable[baseColor.b];
+ gPlttBufferFaded[palOffset++] = (b << 10) | (g << 5) | r;
+ }
+ }
+
+ curPalIndex++;
+ }
+ }
+ else if (gammaIndex < 0)
+ {
+ // A negative gammIndex value means that the blending will come from the special Drought weather's palette tables.
+ // Dummied out in FRLG
+
+ // gammaIndex = -gammaIndex - 1;
+ // palOffset = startPalIndex * 16;
+ // numPalettes += startPalIndex;
+ // curPalIndex = startPalIndex;
+ //
+ // CpuFastCopy(gPlttBufferUnfaded + palOffset, gPlttBufferFaded + palOffset, 16 * sizeof(u16));
+ // while (curPalIndex < numPalettes)
+ // {
+ // if (sPaletteGammaTypes[curPalIndex] == GAMMA_NONE)
+ // {
+ // // No palette change.
+ // palOffset += 16;
+ // }
+ // else
+ // {
+ //
+ // for (i = 0; i < 16; i++)
+ // {
+ // gPlttBufferFaded[palOffset] = sDroughtWeatherColors[gammaIndex][DROUGHT_COLOR_INDEX(gPlttBufferUnfaded[palOffset])];
+ // palOffset++;
+ // }
+ // }
+ //
+ // curPalIndex++;
+ // }
+ }
+ else
+ {
+ // No palette blending.
+ CpuFastCopy(gPlttBufferUnfaded + startPalIndex * 16, gPlttBufferFaded + startPalIndex * 16, numPalettes * 16 * sizeof(u16));
+ }
+}
+
+static void ApplyGammaShiftWithBlend(u8 startPalIndex, u8 numPalettes, s8 gammaIndex, u8 blendCoeff, u16 blendColor)
+{
+ u16 palOffset;
+ u16 curPalIndex;
+ u16 i;
+ struct RGBColor color = *(struct RGBColor *)&blendColor;
+ u8 rBlend = color.r;
+ u8 gBlend = color.g;
+ u8 bBlend = color.b;
+
+ palOffset = startPalIndex * 16;
+ numPalettes += startPalIndex;
+ gammaIndex--;
+ curPalIndex = startPalIndex;
+
+ while (curPalIndex < numPalettes)
+ {
+ if (sPaletteGammaTypes[curPalIndex] == GAMMA_NONE)
+ {
+ // No gamma shift. Simply blend the colors.
+ BlendPalette(palOffset, 16, blendCoeff, blendColor);
+ palOffset += 16;
+ }
+ else
+ {
+ u8 *gammaTable;
+
+ if (sPaletteGammaTypes[curPalIndex] == GAMMA_NORMAL)
+ gammaTable = gWeatherPtr->gammaShifts[gammaIndex];
+ else
+ gammaTable = gWeatherPtr->altGammaShifts[gammaIndex];
+
+ for (i = 0; i < 16; i++)
+ {
+ struct RGBColor baseColor = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset];
+ u8 r = gammaTable[baseColor.r];
+ u8 g = gammaTable[baseColor.g];
+ u8 b = gammaTable[baseColor.b];
+
+ // Apply gamma shift and target blend color to the original color.
+ r += ((rBlend - r) * blendCoeff) >> 4;
+ g += ((gBlend - g) * blendCoeff) >> 4;
+ b += ((bBlend - b) * blendCoeff) >> 4;
+ gPlttBufferFaded[palOffset++] = (b << 10) | (g << 5) | r;
+ }
+ }
+
+ curPalIndex++;
+ }
+}
+
+static void ApplyDroughtGammaShiftWithBlend(s8 gammaIndex, u8 blendCoeff, u16 blendColor)
+{
+ struct RGBColor color;
+ u8 rBlend;
+ u8 gBlend;
+ u8 bBlend;
+ u16 curPalIndex;
+ u16 palOffset;
+ u16 i;
+
+ gammaIndex = -gammaIndex - 1;
+ color = *(struct RGBColor *)&blendColor;
+ rBlend = color.r;
+ gBlend = color.g;
+ bBlend = color.b;
+ palOffset = 0;
+ for (curPalIndex = 0; curPalIndex < 32; curPalIndex++)
+ {
+ if (sPaletteGammaTypes[curPalIndex] == GAMMA_NONE)
+ {
+ // No gamma shift. Simply blend the colors.
+ BlendPalette(palOffset, 16, blendCoeff, blendColor);
+ palOffset += 16;
+ }
+ else
+ {
+ for (i = 0; i < 16; i++)
+ {
+ u32 offset;
+ struct RGBColor color1;
+ struct RGBColor color2;
+ u8 r1, g1, b1;
+ u8 r2, g2, b2;
+
+ color1 = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset];
+ r1 = color1.r;
+ g1 = color1.g;
+ b1 = color1.b;
+
+ r1 += ((rBlend - r1) * blendCoeff) >> 4;
+ g1 += ((gBlend - g1) * blendCoeff) >> 4;
+ b1 += ((bBlend - b1) * blendCoeff) >> 4;
+
+ gPlttBufferFaded[palOffset++] = (b1 << 10) | (g1 << 5) | r1;
+ }
+ }
+ }
+}
+
+static void ApplyFogBlend(u8 blendCoeff, u16 blendColor)
+{
+ struct RGBColor color;
+ u8 rBlend;
+ u8 gBlend;
+ u8 bBlend;
+ u16 curPalIndex;
+
+ BlendPalette(0, 256, blendCoeff, blendColor);
+ color = *(struct RGBColor *)&blendColor;
+ rBlend = color.r;
+ gBlend = color.g;
+ bBlend = color.b;
+
+ for (curPalIndex = 16; curPalIndex < 32; curPalIndex++)
+ {
+ if (LightenSpritePaletteInFog(curPalIndex))
+ {
+ u16 palEnd = (curPalIndex + 1) * 16;
+ u16 palOffset = curPalIndex * 16;
+
+ while (palOffset < palEnd)
+ {
+ struct RGBColor color = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset];
+ u8 r = color.r;
+ u8 g = color.g;
+ u8 b = color.b;
+
+ r += ((28 - r) * 3) >> 2;
+ g += ((31 - g) * 3) >> 2;
+ b += ((28 - b) * 3) >> 2;
+
+ r += ((rBlend - r) * blendCoeff) >> 4;
+ g += ((gBlend - g) * blendCoeff) >> 4;
+ b += ((bBlend - b) * blendCoeff) >> 4;
+
+ gPlttBufferFaded[palOffset] = (b << 10) | (g << 5) | r;
+ palOffset++;
+ }
+ }
+ else
+ {
+ BlendPalette(curPalIndex * 16, 16, blendCoeff, blendColor);
+ }
+ }
+}
+
+static void MarkFogSpritePalToLighten(u8 paletteIndex)
+{
+ if (gWeatherPtr->lightenedFogSpritePalsCount < 6)
+ {
+ gWeatherPtr->lightenedFogSpritePals[gWeatherPtr->lightenedFogSpritePalsCount] = paletteIndex;
+ gWeatherPtr->lightenedFogSpritePalsCount++;
+ }
+}
+
+static bool8 LightenSpritePaletteInFog(u8 paletteIndex)
+{
+ u16 i;
+
+ for (i = 0; i < gWeatherPtr->lightenedFogSpritePalsCount; i++)
+ {
+ if (gWeatherPtr->lightenedFogSpritePals[i] == paletteIndex)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void sub_807A790(s8 gammaIndex)
+{
+ if (gWeatherPtr->palProcessingState == WEATHER_PAL_STATE_IDLE)
+ {
+ ApplyGammaShift(0, 32, gammaIndex);
+ gWeatherPtr->gammaIndex = gammaIndex;
+ }
+}
+
+void sub_807A7C4(u8 gammaIndex, u8 gammaTargetIndex, u8 gammaStepDelay)
+{
+ if (gWeatherPtr->palProcessingState == WEATHER_PAL_STATE_IDLE)
+ {
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_CHANGING_WEATHER;
+ gWeatherPtr->gammaIndex = gammaIndex;
+ gWeatherPtr->gammaTargetIndex = gammaTargetIndex;
+ gWeatherPtr->gammaStepFrameCounter = 0;
+ gWeatherPtr->gammaStepDelay = gammaStepDelay;
+ sub_807A790(gammaIndex);
+ }
+}
+
+void FadeScreen(u8 mode, s8 delay)
+{
+ u32 fadeColor;
+ bool8 fadeOut;
+ bool8 useWeatherPal;
+
+ switch (mode)
+ {
+ case FADE_FROM_BLACK:
+ fadeColor = RGB_BLACK;
+ fadeOut = FALSE;
+ break;
+ case FADE_FROM_WHITE:
+ fadeColor = RGB_WHITEALPHA;
+ fadeOut = FALSE;
+ break;
+ case FADE_TO_BLACK:
+ fadeColor = RGB_BLACK;
+ fadeOut = TRUE;
+ break;
+ case FADE_TO_WHITE:
+ fadeColor = RGB_WHITEALPHA;
+ fadeOut = TRUE;
+ break;
+ default:
+ return;
+ }
+
+ switch (gWeatherPtr->currWeather)
+ {
+ case WEATHER_RAIN:
+ case WEATHER_RAIN_THUNDERSTORM:
+ case WEATHER_DOWNPOUR:
+ case WEATHER_SNOW:
+ case WEATHER_FOG_HORIZONTAL:
+ case WEATHER_SHADE:
+ case WEATHER_DROUGHT:
+ useWeatherPal = TRUE;
+ break;
+ default:
+ useWeatherPal = FALSE;
+ break;
+ }
+
+ if (fadeOut)
+ {
+ if (useWeatherPal)
+ CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, 0x400);
+
+ BeginNormalPaletteFade(0xFFFFFFFF, delay, 0, 16, fadeColor);
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_OUT;
+ }
+ else
+ {
+ gWeatherPtr->fadeDestColor = fadeColor;
+ if (useWeatherPal)
+ gWeatherPtr->fadeScreenCounter = 0;
+ else
+ BeginNormalPaletteFade(0xFFFFFFFF, delay, 16, 0, fadeColor);
+
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_IN;
+ gWeatherPtr->unknown_6CA = 1;
+ gWeatherPtr->unknown_6CB = 0;
+ Weather_SetBlendCoeffs(gWeatherPtr->currBlendEVA, gWeatherPtr->currBlendEVB);
+ gWeatherPtr->readyForInit = TRUE;
+ }
+}
+
+void FadeSelectedPals(u8 mode, s8 delay, u32 selectedPalettes)
+{
+ u32 fadeColor;
+ bool8 fadeOut;
+ bool8 useWeatherPal;
+
+ switch (mode)
+ {
+ case FADE_FROM_BLACK:
+ fadeColor = RGB_BLACK;
+ fadeOut = FALSE;
+ break;
+ case FADE_FROM_WHITE:
+ fadeColor = RGB_WHITEALPHA;
+ fadeOut = FALSE;
+ break;
+ case FADE_TO_BLACK:
+ fadeColor = RGB_BLACK;
+ fadeOut = TRUE;
+ break;
+ case FADE_TO_WHITE:
+ fadeColor = RGB_WHITEALPHA;
+ fadeOut = TRUE;
+ break;
+ default:
+ return;
+ }
+
+ switch (gWeatherPtr->currWeather)
+ {
+ case WEATHER_RAIN:
+ case WEATHER_RAIN_THUNDERSTORM:
+ case WEATHER_DOWNPOUR:
+ case WEATHER_SNOW:
+ case WEATHER_FOG_HORIZONTAL:
+ case WEATHER_SHADE:
+ case WEATHER_DROUGHT:
+ useWeatherPal = TRUE;
+ break;
+ default:
+ useWeatherPal = FALSE;
+ break;
+ }
+
+ if (fadeOut)
+ {
+ if (useWeatherPal)
+ CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, 0x400);
+
+ BeginNormalPaletteFade(selectedPalettes, delay, 0, 16, fadeColor);
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_OUT;
+ }
+ else
+ {
+ gWeatherPtr->fadeDestColor = fadeColor;
+ if (useWeatherPal)
+ gWeatherPtr->fadeScreenCounter = 0;
+ else
+ BeginNormalPaletteFade(selectedPalettes, delay, 16, 0, fadeColor);
+
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_IN;
+ gWeatherPtr->unknown_6CA = 1;
+ gWeatherPtr->unknown_6CB = 0;
+ Weather_SetBlendCoeffs(gWeatherPtr->currBlendEVA, gWeatherPtr->currBlendEVB);
+ gWeatherPtr->readyForInit = TRUE;
+ }
+}
+
+
+bool8 IsWeatherNotFadingIn(void)
+{
+ return (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_SCREEN_FADING_IN);
+}
+
+void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex)
+{
+ u16 paletteIndex = 16 + spritePaletteIndex;
+ u16 i;
+
+ switch (gWeatherPtr->palProcessingState)
+ {
+ case WEATHER_PAL_STATE_SCREEN_FADING_IN:
+ if (gWeatherPtr->unknown_6CA != 0)
+ {
+ if (gWeatherPtr->currWeather == WEATHER_FOG_HORIZONTAL)
+ MarkFogSpritePalToLighten(paletteIndex);
+ paletteIndex *= 16;
+ for (i = 0; i < 16; i++)
+ gPlttBufferFaded[paletteIndex + i] = gWeatherPtr->fadeDestColor;
+ }
+ break;
+ case WEATHER_PAL_STATE_SCREEN_FADING_OUT:
+ paletteIndex *= 16;
+ CpuFastCopy(gPlttBufferFaded + paletteIndex, gPlttBufferUnfaded + paletteIndex, 32);
+ BlendPalette(paletteIndex, 16, gPaletteFade.y, gPaletteFade.blendColor);
+ break;
+ // WEATHER_PAL_STATE_CHANGING_WEATHER
+ // WEATHER_PAL_STATE_CHANGING_IDLE
+ default:
+ if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL)
+ {
+ ApplyGammaShift(paletteIndex, 1, gWeatherPtr->gammaIndex);
+ }
+ else
+ {
+ paletteIndex *= 16;
+ BlendPalette(paletteIndex, 16, 12, RGB(28, 31, 28));
+ }
+ break;
+ }
+}
+
+void ApplyWeatherGammaShiftToPal(u8 paletteIndex)
+{
+ ApplyGammaShift(paletteIndex, 1, gWeatherPtr->gammaIndex);
+}
+
+static u8 sub_80ABF20(void)
+{
+ if (gWeatherPtr->palProcessingState == WEATHER_PAL_STATE_SCREEN_FADING_IN)
+ return gWeatherPtr->unknown_6CA;
+ else
+ return 0;
+}
+
+void LoadCustomWeatherSpritePalette(const u16 *palette)
+{
+ LoadPalette(palette, 0x100 + gWeatherPtr->weatherPicSpritePalIndex * 16, 32);
+ UpdateSpritePaletteWithWeather(gWeatherPtr->weatherPicSpritePalIndex);
+}
+
+static void LoadDroughtWeatherPalette(u8 *gammaIndexPtr, u8 *a1)
+{
+ // Dummied out in FRLG
+ // *gammaIndexPtr = 0x20;
+ // *a1 = 0x20;
+}
+
+void ResetDroughtWeatherPaletteLoading(void)
+{
+ gWeatherPtr->loadDroughtPalsIndex = 1;
+ gWeatherPtr->loadDroughtPalsOffset = 1;
+}
+
+bool8 LoadDroughtWeatherPalettes(void)
+{
+ if (gWeatherPtr->loadDroughtPalsIndex < 32)
+ {
+ LoadDroughtWeatherPalette(&gWeatherPtr->loadDroughtPalsIndex, &gWeatherPtr->loadDroughtPalsOffset);
+ if (gWeatherPtr->loadDroughtPalsIndex < 32)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void sub_807AC50(s8 gammaIndex)
+{
+ sub_807A790(-gammaIndex - 1);
+}
+
+void sub_807AC60(void)
+{
+ gWeatherPtr->unknown_73C = 0;
+ gWeatherPtr->unknown_740 = 0;
+ gWeatherPtr->unknown_742 = 0;
+ gWeatherPtr->unknown_73E = 0;
+ gUnknown_20386A8 = 5;
+}
+
+void sub_807AC98(void)
+{
+ switch (gWeatherPtr->unknown_742)
+ {
+ case 0:
+ if (++gWeatherPtr->unknown_740 > gUnknown_20386A8)
+ {
+ gWeatherPtr->unknown_740 = 0;
+ sub_807AC50(gWeatherPtr->unknown_73C++);
+ if (gWeatherPtr->unknown_73C > 5)
+ {
+ gWeatherPtr->unknown_73E = gWeatherPtr->unknown_73C;
+ gWeatherPtr->unknown_742 = 1;
+ gWeatherPtr->unknown_740 = 0x3C;
+ }
+ }
+ break;
+ case 1:
+ gWeatherPtr->unknown_740 = (gWeatherPtr->unknown_740 + 3) & 0x7F;
+ gWeatherPtr->unknown_73C = ((gSineTable[gWeatherPtr->unknown_740] - 1) >> 6) + 2;
+ if (gWeatherPtr->unknown_73C != gWeatherPtr->unknown_73E)
+ sub_807AC50(gWeatherPtr->unknown_73C);
+ gWeatherPtr->unknown_73E = gWeatherPtr->unknown_73C;
+ break;
+ case 2:
+ if (++gWeatherPtr->unknown_740 > gUnknown_20386A8)
+ {
+ gWeatherPtr->unknown_740 = 0;
+ sub_807AC50(--gWeatherPtr->unknown_73C);
+ if (gWeatherPtr->unknown_73C == 3)
+ gWeatherPtr->unknown_742 = 0;
+ }
+ break;
+ }
+}
+
+void Weather_SetBlendCoeffs(u8 eva, u8 evb)
+{
+ gWeatherPtr->currBlendEVA = eva;
+ gWeatherPtr->currBlendEVB = evb;
+ gWeatherPtr->targetBlendEVA = eva;
+ gWeatherPtr->targetBlendEVB = evb;
+ SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(eva, evb));
+}
+
+void Weather_SetTargetBlendCoeffs(u8 eva, u8 evb, int delay)
+{
+ gWeatherPtr->targetBlendEVA = eva;
+ gWeatherPtr->targetBlendEVB = evb;
+ gWeatherPtr->blendDelay = delay;
+ gWeatherPtr->blendFrameCounter = 0;
+ gWeatherPtr->blendUpdateCounter = 0;
+}
+
+bool8 Weather_UpdateBlend(void)
+{
+ if (gWeatherPtr->currBlendEVA == gWeatherPtr->targetBlendEVA
+ && gWeatherPtr->currBlendEVB == gWeatherPtr->targetBlendEVB)
+ return TRUE;
+
+ if (++gWeatherPtr->blendFrameCounter > gWeatherPtr->blendDelay)
+ {
+ gWeatherPtr->blendFrameCounter = 0;
+ gWeatherPtr->blendUpdateCounter++;
+
+ // Update currBlendEVA and currBlendEVB on alternate frames
+ if (gWeatherPtr->blendUpdateCounter & 1)
+ {
+ if (gWeatherPtr->currBlendEVA < gWeatherPtr->targetBlendEVA)
+ gWeatherPtr->currBlendEVA++;
+ else if (gWeatherPtr->currBlendEVA > gWeatherPtr->targetBlendEVA)
+ gWeatherPtr->currBlendEVA--;
+ }
+ else
+ {
+ if (gWeatherPtr->currBlendEVB < gWeatherPtr->targetBlendEVB)
+ gWeatherPtr->currBlendEVB++;
+ else if (gWeatherPtr->currBlendEVB > gWeatherPtr->targetBlendEVB)
+ gWeatherPtr->currBlendEVB--;
+ }
+ }
+
+ SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(gWeatherPtr->currBlendEVA, gWeatherPtr->currBlendEVB));
+
+ if (gWeatherPtr->currBlendEVA == gWeatherPtr->targetBlendEVA
+ && gWeatherPtr->currBlendEVB == gWeatherPtr->targetBlendEVB)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void sub_807AF00(u8 a)
+{
+ switch (a)
+ {
+ case 1:
+ SetWeather(WEATHER_SUNNY_CLOUDS);
+ break;
+ case 2:
+ SetWeather(WEATHER_SUNNY);
+ break;
+ case 3:
+ SetWeather(WEATHER_RAIN);
+ break;
+ case 4:
+ SetWeather(WEATHER_SNOW);
+ break;
+ case 5:
+ SetWeather(WEATHER_RAIN_THUNDERSTORM);
+ break;
+ case 6:
+ SetWeather(WEATHER_FOG_HORIZONTAL);
+ break;
+ case 7:
+ SetWeather(WEATHER_FOG_DIAGONAL);
+ break;
+ case 8:
+ SetWeather(WEATHER_VOLCANIC_ASH);
+ break;
+ case 9:
+ SetWeather(WEATHER_SANDSTORM);
+ break;
+ case 10:
+ SetWeather(WEATHER_SHADE);
+ break;
+ }
+}
+
+u8 GetCurrentWeather(void)
+{
+ return gWeatherPtr->currWeather;
+}
+
+void SetRainStrengthFromSoundEffect(u16 soundEffect)
+{
+ if (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_SCREEN_FADING_OUT)
+ {
+ switch (soundEffect)
+ {
+ case SE_T_KOAME:
+ gWeatherPtr->rainStrength = 0;
+ break;
+ case SE_T_OOAME:
+ gWeatherPtr->rainStrength = 1;
+ break;
+ case SE_T_AME:
+ gWeatherPtr->rainStrength = 2;
+ break;
+ default:
+ return;
+ }
+
+ PlaySE(soundEffect);
+ }
+}
+
+void PlayRainStoppingSoundEffect(void)
+{
+ if (IsSpecialSEPlaying())
+ {
+ switch (gWeatherPtr->rainStrength)
+ {
+ case 0:
+ PlaySE(SE_T_KOAME_E);
+ break;
+ case 1:
+ PlaySE(SE_T_OOAME_E);
+ break;
+ case 2:
+ default:
+ PlaySE(SE_T_AME_E);
+ break;
+ }
+ }
+}
+
+u8 IsWeatherChangeComplete(void)
+{
+ return gWeatherPtr->weatherChangeComplete;
+}
+
+void SetWeatherScreenFadeOut(void)
+{
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_OUT;
+}
+
+void sub_807B070(void)
+{
+ gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE;
+}
+
+void PreservePaletteInWeather(u8 preservedPalIndex)
+{
+ CpuCopy16(sBasePaletteGammaTypes, sFieldEffectPaletteGammaTypes, 32);
+ sFieldEffectPaletteGammaTypes[preservedPalIndex] = GAMMA_NONE;
+ sPaletteGammaTypes = sFieldEffectPaletteGammaTypes;
+}
+
+void ResetPreservedPalettesInWeather(void)
+{
+ sPaletteGammaTypes = sBasePaletteGammaTypes;
+}
+
+void sub_807B0C4(u16 *palbuf, u16 *unused, u32 size)
+{
+ switch (gWeatherPtr->currWeather)
+ {
+ case WEATHER_RAIN:
+ case WEATHER_SNOW:
+ case WEATHER_RAIN_THUNDERSTORM:
+ case WEATHER_SHADE:
+ case WEATHER_DOWNPOUR:
+ sub_8045314(palbuf, RGB_BLACK, 3, size);
+ break;
+ }
+}
diff --git a/src/field_weather_effects.c b/src/field_weather_effects.c
new file mode 100644
index 000000000..e6f4bfacf
--- /dev/null
+++ b/src/field_weather_effects.c
@@ -0,0 +1,2320 @@
+#include "global.h"
+#include "gflib.h"
+#include "battle_anim.h"
+#include "event_object_movement.h"
+#include "field_weather.h"
+#include "field_weather_effects.h"
+#include "overworld.h"
+#include "random.h"
+#include "script.h"
+#include "constants/weather.h"
+#include "constants/songs.h"
+#include "task.h"
+#include "trig.h"
+
+//------------------------------------------------------------------------------
+// WEATHER_SUNNY_CLOUDS
+//------------------------------------------------------------------------------
+
+static void CreateCloudSprites(void);
+static void DestroyCloudSprites(void);
+static void UpdateCloudSprite(struct Sprite *);
+
+// The clouds are positioned on the map's grid.
+// These coordinates are for the lower half of Route 120.
+static const struct Coords16 sCloudSpriteMapCoords[] = {
+ { 0, 66},
+ { 5, 73},
+ {10, 78},
+};
+
+static const struct SpriteSheet sCloudSpriteSheet = {
+ .data = gWeatherCloudTiles,
+ .size = 0x0800,
+ .tag = 0x1200
+};
+
+static const struct OamData sCloudSpriteOamData = {
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_BLEND,
+ .mosaic = FALSE,
+ .bpp = ST_OAM_4BPP,
+ .shape = SPRITE_SHAPE(64x64),
+ .x = 0,
+ .matrixNum = 0,
+ .size = SPRITE_SIZE(64x64),
+ .tileNum = 0,
+ .priority = 3,
+ .paletteNum = 0,
+ .affineParam = 0,
+};
+
+static const union AnimCmd sCloudSpriteAnimCmd[] = {
+ ANIMCMD_FRAME(0, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd *const sCloudSpriteAnimCmds[] = {
+ sCloudSpriteAnimCmd,
+};
+
+static const struct SpriteTemplate sCloudSpriteTemplate = {
+ .tileTag = 0x1200,
+ .paletteTag = 0x1200,
+ .oam = &sCloudSpriteOamData,
+ .anims = sCloudSpriteAnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = UpdateCloudSprite,
+};
+
+void Clouds_InitVars(void)
+{
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 20;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->initStep = 0;
+ if (!gWeatherPtr->cloudSpritesCreated)
+ Weather_SetBlendCoeffs(0, 16);
+}
+
+void Clouds_InitAll(void)
+{
+ Clouds_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Clouds_Main();
+}
+
+void Clouds_Main(void)
+{
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ CreateCloudSprites();
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ Weather_SetTargetBlendCoeffs(12, 8, 1);
+ gWeatherPtr->initStep++;
+ break;
+ case 2:
+ if (Weather_UpdateBlend())
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ }
+}
+
+bool8 Clouds_Finish(void)
+{
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ Weather_SetTargetBlendCoeffs(0, 16, 1);
+ gWeatherPtr->finishStep++;
+ return TRUE;
+ case 1:
+ if (Weather_UpdateBlend())
+ {
+ DestroyCloudSprites();
+ gWeatherPtr->finishStep++;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void Sunny_InitVars(void)
+{
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 20;
+}
+
+void Sunny_InitAll(void)
+{
+ Sunny_InitVars();
+}
+
+void Sunny_Main(void)
+{
+}
+
+bool8 Sunny_Finish(void)
+{
+ return FALSE;
+}
+
+static void CreateCloudSprites(void)
+{
+ u16 i;
+ u8 spriteId;
+ struct Sprite *sprite;
+
+ if (gWeatherPtr->cloudSpritesCreated == TRUE)
+ return;
+
+ LoadSpriteSheet(&sCloudSpriteSheet);
+ LoadCustomWeatherSpritePalette(gCloudsWeatherPalette);
+ for (i = 0; i < NUM_CLOUD_SPRITES; i++)
+ {
+ spriteId = CreateSprite(&sCloudSpriteTemplate, 0, 0, 0xFF);
+ if (spriteId != MAX_SPRITES)
+ {
+ gWeatherPtr->sprites.s1.cloudSprites[i] = &gSprites[spriteId];
+ sprite = gWeatherPtr->sprites.s1.cloudSprites[i];
+ SetSpritePosToMapCoords(sCloudSpriteMapCoords[i].x + 7, sCloudSpriteMapCoords[i].y + 7, &sprite->pos1.x, &sprite->pos1.y);
+ sprite->coordOffsetEnabled = TRUE;
+ }
+ else
+ {
+ gWeatherPtr->sprites.s1.cloudSprites[i] = NULL;
+ }
+ }
+
+ gWeatherPtr->cloudSpritesCreated = TRUE;
+}
+
+static void DestroyCloudSprites(void)
+{
+ u16 i;
+
+ if (!gWeatherPtr->cloudSpritesCreated)
+ return;
+
+ for (i = 0; i < NUM_CLOUD_SPRITES; i++)
+ {
+ if (gWeatherPtr->sprites.s1.cloudSprites[i] != NULL)
+ DestroySprite(gWeatherPtr->sprites.s1.cloudSprites[i]);
+ }
+
+ FreeSpriteTilesByTag(0x1200);
+ gWeatherPtr->cloudSpritesCreated = FALSE;
+}
+
+static void UpdateCloudSprite(struct Sprite *sprite)
+{
+ // Move 1 pixel left every 2 frames.
+ sprite->data[0] = (sprite->data[0] + 1) & 1;
+ if (sprite->data[0])
+ sprite->pos1.x--;
+}
+
+//------------------------------------------------------------------------------
+// WEATHER_DROUGHT
+//------------------------------------------------------------------------------
+
+static void UpdateDroughtBlend(u8);
+
+void Drought_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 0;
+}
+
+void Drought_InitAll(void)
+{
+ Drought_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Drought_Main();
+}
+
+void Drought_Main(void)
+{
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ if (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_CHANGING_WEATHER)
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ ResetDroughtWeatherPaletteLoading();
+ gWeatherPtr->initStep++;
+ break;
+ case 2:
+ if (!LoadDroughtWeatherPalettes())
+ gWeatherPtr->initStep++;
+ break;
+ case 3:
+ sub_807AC60();
+ gWeatherPtr->initStep++;
+ break;
+ case 4:
+ sub_807AC98();
+ if (gWeatherPtr->unknown_73C == 6)
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ default:
+ sub_807AC98();
+ break;
+ }
+}
+
+bool8 Drought_Finish(void)
+{
+ return FALSE;
+}
+
+void StartDroughtWeatherBlend(void)
+{
+ CreateTask(UpdateDroughtBlend, 0x50);
+}
+
+#define tState data[0]
+#define tBlendY data[1]
+#define tBlendDelay data[2]
+#define tWinRange data[3]
+
+static void UpdateDroughtBlend(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+
+ switch (task->tState)
+ {
+ case 0:
+ task->tBlendY = 0;
+ task->tBlendDelay = 0;
+ task->tWinRange = GetGpuReg(REG_OFFSET_WININ);
+ SetGpuReg(REG_OFFSET_WININ, WIN_RANGE(63, 63));
+ SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_TGT1_BG2 | BLDCNT_TGT1_BG3 | BLDCNT_TGT1_OBJ | BLDCNT_EFFECT_LIGHTEN);
+ SetGpuReg(REG_OFFSET_BLDY, 0);
+ task->tState++;
+ // fall through
+ case 1:
+ task->tBlendY += 3;
+ if (task->tBlendY > 16)
+ task->tBlendY = 16;
+ SetGpuReg(REG_OFFSET_BLDY, task->tBlendY);
+ if (task->tBlendY >= 16)
+ task->tState++;
+ break;
+ case 2:
+ task->tBlendDelay++;
+ if (task->tBlendDelay > 9)
+ {
+ task->tBlendDelay = 0;
+ task->tBlendY--;
+ if (task->tBlendY <= 0)
+ {
+ task->tBlendY = 0;
+ task->tState++;
+ }
+ SetGpuReg(REG_OFFSET_BLDY, task->tBlendY);
+ }
+ break;
+ case 3:
+ SetGpuReg(REG_OFFSET_BLDCNT, 0);
+ SetGpuReg(REG_OFFSET_BLDY, 0);
+ SetGpuReg(REG_OFFSET_WININ, task->tWinRange);
+ task->tState++;
+ break;
+ case 4:
+ EnableBothScriptContexts();
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+#undef tState
+#undef tBlendY
+#undef tBlendDelay
+#undef tWinRange
+
+//------------------------------------------------------------------------------
+// WEATHER_RAIN
+//------------------------------------------------------------------------------
+
+static void LoadRainSpriteSheet(void);
+static bool8 CreateRainSprite(void);
+static void UpdateRainSprite(struct Sprite *sprite);
+static bool8 UpdateVisibleRainSprites(void);
+static void DestroyRainSprites(void);
+
+static const struct Coords16 sRainSpriteCoords[] = {
+ { 0, 0},
+ { 0, 160},
+ { 0, 64},
+ {144, 224},
+ {144, 128},
+ { 32, 32},
+ { 32, 192},
+ { 32, 96},
+ { 72, 128},
+ { 72, 32},
+ { 72, 192},
+ {216, 96},
+ {216, 0},
+ {104, 160},
+ {104, 64},
+ {104, 224},
+ {144, 0},
+ {144, 160},
+ {144, 64},
+ { 32, 224},
+ { 32, 128},
+ { 72, 32},
+ { 72, 192},
+ { 48, 96},
+};
+
+static const struct OamData sRainSpriteOamData = {
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_NORMAL,
+ .mosaic = FALSE,
+ .bpp = ST_OAM_4BPP,
+ .shape = SPRITE_SHAPE(16x32),
+ .x = 0,
+ .matrixNum = 0,
+ .size = SPRITE_SIZE(16x32),
+ .tileNum = 0,
+ .priority = 1,
+ .paletteNum = 2,
+ .affineParam = 0,
+};
+
+static const union AnimCmd sRainSpriteFallAnimCmd[] = {
+ ANIMCMD_FRAME(0, 16),
+ ANIMCMD_JUMP(0),
+};
+
+static const union AnimCmd sRainSpriteSplashAnimCmd[] = {
+ ANIMCMD_FRAME(8, 3),
+ ANIMCMD_FRAME(32, 2),
+ ANIMCMD_FRAME(40, 2),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd sRainSpriteHeavySplashAnimCmd[] = {
+ ANIMCMD_FRAME(8, 3),
+ ANIMCMD_FRAME(16, 3),
+ ANIMCMD_FRAME(24, 4),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd *const sRainSpriteAnimCmds[] = {
+ sRainSpriteFallAnimCmd,
+ sRainSpriteSplashAnimCmd,
+ sRainSpriteHeavySplashAnimCmd,
+};
+
+static const struct SpriteTemplate sRainSpriteTemplate = {
+ .tileTag = 0x1206,
+ .paletteTag = 0x1200,
+ .oam = &sRainSpriteOamData,
+ .anims = sRainSpriteAnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = UpdateRainSprite,
+};
+
+// Q28.4 fixed-point format values
+static const s16 sRainSpriteMovement[][2] = {
+ {-0x68, 0xD0},
+ {-0xA0, 0x140},
+};
+
+// First byte is the number of frames a raindrop falls before it splashes.
+// Second byte is the maximum number of frames a raindrop can "wait" before
+// it appears and starts falling. (This is only for the initial raindrop spawn.)
+static const u16 sRainSpriteFallingDurations[][2] = {
+ {18, 7},
+ {12, 10},
+};
+
+static const struct SpriteSheet sRainSpriteSheet = {
+ .data = gWeatherRainTiles,
+ .size = 0x0600,
+ .tag = 0x1206,
+};
+
+void Rain_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->rainSpriteVisibleCounter = 0;
+ gWeatherPtr->rainSpriteVisibleDelay = 8;
+ gWeatherPtr->isDownpour = FALSE;
+ gWeatherPtr->targetRainSpriteCount = 10;
+ gWeatherPtr->gammaTargetIndex = 3;
+ gWeatherPtr->gammaStepDelay = 20;
+ SetRainStrengthFromSoundEffect(SE_T_KOAME);
+}
+
+void Rain_InitAll(void)
+{
+ Rain_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Rain_Main();
+}
+
+void Rain_Main(void)
+{
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ LoadRainSpriteSheet();
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ if (!CreateRainSprite())
+ gWeatherPtr->initStep++;
+ break;
+ case 2:
+ if (!UpdateVisibleRainSprites())
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ }
+}
+
+bool8 Rain_Finish(void)
+{
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ if (gWeatherPtr->nextWeather == WEATHER_RAIN
+ || gWeatherPtr->nextWeather == WEATHER_RAIN_THUNDERSTORM
+ || gWeatherPtr->nextWeather == WEATHER_DOWNPOUR)
+ {
+ gWeatherPtr->finishStep = 0xFF;
+ return FALSE;
+ }
+ else
+ {
+ gWeatherPtr->targetRainSpriteCount = 0;
+ gWeatherPtr->finishStep++;
+ }
+ // fall through
+ case 1:
+ if (!UpdateVisibleRainSprites())
+ {
+ DestroyRainSprites();
+ gWeatherPtr->finishStep++;
+ return FALSE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#define tCounter data[0]
+#define tRandom data[1]
+#define tPosX data[2]
+#define tPosY data[3]
+#define tState data[4]
+#define tActive data[5]
+#define tWaiting data[6]
+
+static void StartRainSpriteFall(struct Sprite *sprite)
+{
+ u32 rand;
+ u16 numFallingFrames;
+ int tileX;
+ int tileY;
+
+ if (sprite->tRandom == 0)
+ sprite->tRandom = 361;
+
+ // Standard RNG sequence.
+ rand = sprite->tRandom * 1103515245 + 12345;
+ sprite->tRandom = ((rand & 0x7FFF0000) >> 16) % 600;
+
+ numFallingFrames = sRainSpriteFallingDurations[gWeatherPtr->isDownpour][0];
+
+ tileX = sprite->tRandom % 30;
+ sprite->tPosX = tileX * 8; // Useless assignment, leftover from before fixed-point values were used
+
+ tileY = sprite->tRandom / 30;
+ sprite->tPosY = tileY * 8; // Useless assignment, leftover from before fixed-point values were used
+
+ sprite->tPosX = tileX;
+ sprite->tPosX <<= 7; // This is tileX * 8, using a fixed-point value with 4 decimal places
+
+ sprite->tPosY = tileY;
+ sprite->tPosY <<= 7; // This is tileX * 8, using a fixed-point value with 4 decimal places
+
+ // "Rewind" the rain sprites, from their ending position.
+ sprite->tPosX -= sRainSpriteMovement[gWeatherPtr->isDownpour][0] * numFallingFrames;
+ sprite->tPosY -= sRainSpriteMovement[gWeatherPtr->isDownpour][1] * numFallingFrames;
+
+ StartSpriteAnim(sprite, 0);
+ sprite->tState = 0;
+ sprite->coordOffsetEnabled = FALSE;
+ sprite->tCounter = numFallingFrames;
+}
+
+static void UpdateRainSprite(struct Sprite *sprite)
+{
+ if (sprite->tState == 0)
+ {
+ // Raindrop is in its "falling" motion.
+ sprite->tPosX += sRainSpriteMovement[gWeatherPtr->isDownpour][0];
+ sprite->tPosY += sRainSpriteMovement[gWeatherPtr->isDownpour][1];
+ sprite->pos1.x = sprite->tPosX >> 4;
+ sprite->pos1.y = sprite->tPosY >> 4;
+
+ if (sprite->tActive
+ && (sprite->pos1.x >= -8 && sprite->pos1.x <= 248)
+ && sprite->pos1.y >= -16 && sprite->pos1.y <= 176)
+ sprite->invisible = FALSE;
+ else
+ sprite->invisible = TRUE;
+
+ if (--sprite->tCounter == 0)
+ {
+ // Make raindrop splash on the ground
+ StartSpriteAnim(sprite, gWeatherPtr->isDownpour + 1);
+ sprite->tState = 1;
+ sprite->pos1.x -= gSpriteCoordOffsetX;
+ sprite->pos1.y -= gSpriteCoordOffsetY;
+ sprite->coordOffsetEnabled = TRUE;
+ }
+ }
+ else if (sprite->animEnded)
+ {
+ // The splashing animation ended.
+ sprite->invisible = TRUE;
+ StartRainSpriteFall(sprite);
+ }
+}
+
+static void WaitRainSprite(struct Sprite *sprite)
+{
+ if (sprite->tCounter == 0)
+ {
+ StartRainSpriteFall(sprite);
+ sprite->callback = UpdateRainSprite;
+ }
+ else
+ {
+ sprite->tCounter--;
+ }
+}
+
+static void InitRainSpriteMovement(struct Sprite *sprite, u16 val)
+{
+ u16 numFallingFrames = sRainSpriteFallingDurations[gWeatherPtr->isDownpour][0];
+ u16 numAdvanceRng = val / (sRainSpriteFallingDurations[gWeatherPtr->isDownpour][1] + numFallingFrames);
+ u16 frameVal = val % (sRainSpriteFallingDurations[gWeatherPtr->isDownpour][1] + numFallingFrames);
+
+ while (--numAdvanceRng != 0xFFFF)
+ StartRainSpriteFall(sprite);
+
+ if (frameVal < numFallingFrames)
+ {
+ while (--frameVal != 0xFFFF)
+ UpdateRainSprite(sprite);
+
+ sprite->tWaiting = FALSE;
+ }
+ else
+ {
+ sprite->tCounter = frameVal - numFallingFrames;
+ sprite->invisible = TRUE;
+ sprite->tWaiting = TRUE;
+ }
+}
+
+static void LoadRainSpriteSheet(void)
+{
+ LoadSpriteSheet(&sRainSpriteSheet);
+}
+
+static bool8 CreateRainSprite(void)
+{
+ u8 spriteIndex;
+ u8 spriteId;
+
+ if (gWeatherPtr->rainSpriteCount == MAX_RAIN_SPRITES)
+ return FALSE;
+
+ spriteIndex = gWeatherPtr->rainSpriteCount;
+ spriteId = CreateSpriteAtEnd(&sRainSpriteTemplate,
+ sRainSpriteCoords[spriteIndex].x, sRainSpriteCoords[spriteIndex].y, 78);
+
+ if (spriteId != MAX_SPRITES)
+ {
+ gSprites[spriteId].tActive = 0;
+ gSprites[spriteId].tRandom = spriteIndex * 145;
+ while (gSprites[spriteId].tRandom >= 600)
+ gSprites[spriteId].tRandom -= 600;
+
+ StartRainSpriteFall(&gSprites[spriteId]);
+ InitRainSpriteMovement(&gSprites[spriteId], spriteIndex * 9);
+ gSprites[spriteId].invisible = TRUE;
+ gWeatherPtr->sprites.s1.rainSprites[spriteIndex] = &gSprites[spriteId];
+ }
+ else
+ {
+ gWeatherPtr->sprites.s1.rainSprites[spriteIndex] = NULL;
+ }
+
+ if (++gWeatherPtr->rainSpriteCount == MAX_RAIN_SPRITES)
+ {
+ u16 i;
+ for (i = 0; i < MAX_RAIN_SPRITES; i++)
+ {
+ if (gWeatherPtr->sprites.s1.rainSprites[i])
+ {
+ if (!gWeatherPtr->sprites.s1.rainSprites[i]->tWaiting)
+ gWeatherPtr->sprites.s1.rainSprites[i]->callback = UpdateRainSprite;
+ else
+ gWeatherPtr->sprites.s1.rainSprites[i]->callback = WaitRainSprite;
+ }
+ }
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static bool8 UpdateVisibleRainSprites(void)
+{
+ if (gWeatherPtr->curRainSpriteIndex == gWeatherPtr->targetRainSpriteCount)
+ return FALSE;
+
+ if (++gWeatherPtr->rainSpriteVisibleCounter > gWeatherPtr->rainSpriteVisibleDelay)
+ {
+ gWeatherPtr->rainSpriteVisibleCounter = 0;
+ if (gWeatherPtr->curRainSpriteIndex < gWeatherPtr->targetRainSpriteCount)
+ {
+ gWeatherPtr->sprites.s1.rainSprites[gWeatherPtr->curRainSpriteIndex++]->tActive = 1;
+ }
+ else
+ {
+ gWeatherPtr->curRainSpriteIndex--;
+ gWeatherPtr->sprites.s1.rainSprites[gWeatherPtr->curRainSpriteIndex]->tActive = 0;
+ gWeatherPtr->sprites.s1.rainSprites[gWeatherPtr->curRainSpriteIndex]->invisible = TRUE;
+ }
+ }
+ return TRUE;
+}
+
+static void DestroyRainSprites(void)
+{
+ u16 i;
+
+ for (i = 0; i < gWeatherPtr->rainSpriteCount; i++)
+ {
+ if (gWeatherPtr->sprites.s1.rainSprites[i] != NULL)
+ DestroySprite(gWeatherPtr->sprites.s1.rainSprites[i]);
+ }
+ gWeatherPtr->rainSpriteCount = 0;
+ FreeSpriteTilesByTag(0x1206);
+}
+
+#undef tCounter
+#undef tRandom
+#undef tPosX
+#undef tPosY
+#undef tState
+#undef tActive
+#undef tWaiting
+
+//------------------------------------------------------------------------------
+// Snow
+//------------------------------------------------------------------------------
+
+static void UpdateSnowflakeSprite(struct Sprite *);
+static bool8 UpdateVisibleSnowflakeSprites(void);
+static bool8 CreateSnowflakeSprite(void);
+static bool8 DestroySnowflakeSprite(void);
+static void InitSnowflakeSpriteMovement(struct Sprite *);
+
+void Snow_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->gammaTargetIndex = 3;
+ gWeatherPtr->gammaStepDelay = 20;
+ gWeatherPtr->targetSnowflakeSpriteCount = 16;
+ gWeatherPtr->snowflakeVisibleCounter = 0;
+}
+
+void Snow_InitAll(void)
+{
+ u16 i;
+
+ Snow_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ {
+ Snow_Main();
+ for (i = 0; i < gWeatherPtr->snowflakeSpriteCount; i++)
+ UpdateSnowflakeSprite(gWeatherPtr->sprites.s1.snowflakeSprites[i]);
+ }
+}
+
+void Snow_Main(void)
+{
+ if (gWeatherPtr->initStep == 0 && !UpdateVisibleSnowflakeSprites())
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+}
+
+bool8 Snow_Finish(void)
+{
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ gWeatherPtr->targetSnowflakeSpriteCount = 0;
+ gWeatherPtr->snowflakeVisibleCounter = 0;
+ gWeatherPtr->finishStep++;
+ // fall through
+ case 1:
+ if (!UpdateVisibleSnowflakeSprites())
+ {
+ gWeatherPtr->finishStep++;
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bool8 UpdateVisibleSnowflakeSprites(void)
+{
+ if (gWeatherPtr->snowflakeSpriteCount == gWeatherPtr->targetSnowflakeSpriteCount)
+ return FALSE;
+
+ if (++gWeatherPtr->snowflakeVisibleCounter > 36)
+ {
+ gWeatherPtr->snowflakeVisibleCounter = 0;
+ if (gWeatherPtr->snowflakeSpriteCount < gWeatherPtr->targetSnowflakeSpriteCount)
+ CreateSnowflakeSprite();
+ else
+ DestroySnowflakeSprite();
+ }
+
+ return gWeatherPtr->snowflakeSpriteCount != gWeatherPtr->targetSnowflakeSpriteCount;
+}
+
+static const struct OamData sSnowflakeSpriteOamData = {
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_NORMAL,
+ .mosaic = FALSE,
+ .bpp = ST_OAM_4BPP,
+ .shape = SPRITE_SHAPE(8x8),
+ .x = 0,
+ .matrixNum = 0,
+ .size = SPRITE_SIZE(8x8),
+ .tileNum = 0,
+ .priority = 1,
+ .paletteNum = 0,
+ .affineParam = 0,
+};
+
+static const struct SpriteFrameImage sSnowflakeSpriteImages[] = {
+ {gWeatherSnow1Tiles, 0x20},
+ {gWeatherSnow2Tiles, 0x20},
+};
+
+static const union AnimCmd sSnowflakeAnimCmd0[] = {
+ ANIMCMD_FRAME(0, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd sSnowflakeAnimCmd1[] = {
+ ANIMCMD_FRAME(1, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd *const sSnowflakeAnimCmds[] = {
+ sSnowflakeAnimCmd0,
+ sSnowflakeAnimCmd1,
+};
+
+static const struct SpriteTemplate sSnowflakeSpriteTemplate = {
+ .tileTag = 0xFFFF,
+ .paletteTag = 0x1200,
+ .oam = &sSnowflakeSpriteOamData,
+ .anims = sSnowflakeAnimCmds,
+ .images = sSnowflakeSpriteImages,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = UpdateSnowflakeSprite,
+};
+
+#define tPosY data[0]
+#define tDeltaY data[1]
+#define tWaveDelta data[2]
+#define tWaveIndex data[3]
+#define tSnowflakeId data[4]
+#define tFallCounter data[5]
+#define tFallDuration data[6]
+#define tDeltaY2 data[7]
+
+static bool8 CreateSnowflakeSprite(void)
+{
+ u8 spriteId = CreateSpriteAtEnd(&sSnowflakeSpriteTemplate, 0, 0, 78);
+ if (spriteId == MAX_SPRITES)
+ return FALSE;
+
+ gSprites[spriteId].tSnowflakeId = gWeatherPtr->snowflakeSpriteCount;
+ InitSnowflakeSpriteMovement(&gSprites[spriteId]);
+ gSprites[spriteId].coordOffsetEnabled = TRUE;
+ gWeatherPtr->sprites.s1.snowflakeSprites[gWeatherPtr->snowflakeSpriteCount++] = &gSprites[spriteId];
+ return TRUE;
+}
+
+static bool8 DestroySnowflakeSprite(void)
+{
+ if (gWeatherPtr->snowflakeSpriteCount)
+ {
+ DestroySprite(gWeatherPtr->sprites.s1.snowflakeSprites[--gWeatherPtr->snowflakeSpriteCount]);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void InitSnowflakeSpriteMovement(struct Sprite *sprite)
+{
+ u16 rand;
+ u16 x = ((sprite->tSnowflakeId * 5) & 7) * 30 + (Random() % 30);
+
+ sprite->pos1.y = -3 - (gSpriteCoordOffsetY + sprite->centerToCornerVecY);
+ sprite->pos1.x = x - (gSpriteCoordOffsetX + sprite->centerToCornerVecX);
+ sprite->tPosY = sprite->pos1.y * 128;
+ sprite->pos2.x = 0;
+ rand = Random();
+ sprite->tDeltaY = (rand & 3) * 5 + 64;
+ sprite->tDeltaY2 = sprite->tDeltaY;
+ StartSpriteAnim(sprite, (rand & 1) ? 0 : 1);
+ sprite->tWaveIndex = 0;
+ sprite->tWaveDelta = ((rand & 3) == 0) ? 2 : 1;
+ sprite->tFallDuration = (rand & 0x1F) + 210;
+ sprite->tFallCounter = 0;
+}
+
+static void WaitSnowflakeSprite(struct Sprite *sprite)
+{
+ if (gWeatherPtr->unknown_6E2 > 18)
+ {
+ sprite->invisible = FALSE;
+ sprite->callback = UpdateSnowflakeSprite;
+ sprite->pos1.y = 250 - (gSpriteCoordOffsetY + sprite->centerToCornerVecY);
+ sprite->tPosY = sprite->pos1.y * 128;
+ gWeatherPtr->unknown_6E2 = 0;
+ }
+}
+
+static void UpdateSnowflakeSprite(struct Sprite *sprite)
+{
+ s16 x;
+ s16 y;
+
+ sprite->tPosY += sprite->tDeltaY;
+ sprite->pos1.y = sprite->tPosY >> 7;
+ sprite->tWaveIndex += sprite->tWaveDelta;
+ sprite->tWaveIndex &= 0xFF;
+ sprite->pos2.x = gSineTable[sprite->tWaveIndex] / 64;
+
+ x = (sprite->pos1.x + sprite->centerToCornerVecX + gSpriteCoordOffsetX) & 0x1FF;
+ if (x & 0x100)
+ x |= -0x100;
+
+ if (x < -3)
+ sprite->pos1.x = 242 - (gSpriteCoordOffsetX + sprite->centerToCornerVecX);
+ else if (x > 242)
+ sprite->pos1.x = -3 - (gSpriteCoordOffsetX + sprite->centerToCornerVecX);
+
+ y = (sprite->pos1.y + sprite->centerToCornerVecY + gSpriteCoordOffsetY) & 0xFF;
+ if (y > 163 && y < 171)
+ {
+ sprite->pos1.y = 250 - (gSpriteCoordOffsetY + sprite->centerToCornerVecY);
+ sprite->tPosY = sprite->pos1.y * 128;
+ sprite->tFallCounter = 0;
+ sprite->tFallDuration = 220;
+ }
+ else if (y > 242 && y < 250)
+ {
+ sprite->pos1.y = 163;
+ sprite->tPosY = sprite->pos1.y * 128;
+ sprite->tFallCounter = 0;
+ sprite->tFallDuration = 220;
+ sprite->invisible = TRUE;
+ sprite->callback = WaitSnowflakeSprite;
+ }
+
+ if (++sprite->tFallCounter == sprite->tFallDuration)
+ {
+ InitSnowflakeSpriteMovement(sprite);
+ sprite->pos1.y = 250;
+ sprite->invisible = TRUE;
+ sprite->callback = WaitSnowflakeSprite;
+ }
+}
+
+#undef tPosY
+#undef tDeltaY
+#undef tWaveDelta
+#undef tWaveIndex
+#undef tSnowflakeId
+#undef tFallCounter
+#undef tFallDuration
+#undef tDeltaY2
+
+//------------------------------------------------------------------------------
+// WEATHER_RAIN_THUNDERSTORM
+//------------------------------------------------------------------------------
+
+void Thunderstorm_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->rainSpriteVisibleCounter = 0;
+ gWeatherPtr->rainSpriteVisibleDelay = 4;
+ gWeatherPtr->isDownpour = FALSE;
+ gWeatherPtr->targetRainSpriteCount = 16;
+ gWeatherPtr->gammaTargetIndex = 3;
+ gWeatherPtr->gammaStepDelay = 20;
+ gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment
+ gWeatherPtr->thunderTriggered = FALSE;
+ SetRainStrengthFromSoundEffect(SE_T_AME);
+}
+
+void Thunderstorm_InitAll(void)
+{
+ Thunderstorm_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Thunderstorm_Main();
+}
+
+//------------------------------------------------------------------------------
+// WEATHER_DOWNPOUR
+//------------------------------------------------------------------------------
+
+static void UpdateThunderSound(void);
+static void SetThunderCounter(u16);
+
+void Downpour_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->rainSpriteVisibleCounter = 0;
+ gWeatherPtr->rainSpriteVisibleDelay = 4;
+ gWeatherPtr->isDownpour = TRUE;
+ gWeatherPtr->targetRainSpriteCount = 24;
+ gWeatherPtr->gammaTargetIndex = 3;
+ gWeatherPtr->gammaStepDelay = 20;
+ gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment
+ SetRainStrengthFromSoundEffect(SE_T_OOAME);
+}
+
+void Downpour_InitAll(void)
+{
+ Downpour_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Thunderstorm_Main();
+}
+
+void Thunderstorm_Main(void)
+{
+ UpdateThunderSound();
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ LoadRainSpriteSheet();
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ if (!CreateRainSprite())
+ gWeatherPtr->initStep++;
+ break;
+ case 2:
+ if (!UpdateVisibleRainSprites())
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ case 3:
+ if (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_CHANGING_WEATHER)
+ gWeatherPtr->initStep = 6;
+ break;
+ case 4:
+ gWeatherPtr->unknown_6EA = 1;
+ gWeatherPtr->unknown_6E6 = (Random() % 360) + 360;
+ gWeatherPtr->initStep++;
+ // fall through
+ case 5:
+ if (--gWeatherPtr->unknown_6E6 == 0)
+ gWeatherPtr->initStep++;
+ break;
+ case 6:
+ gWeatherPtr->unknown_6EA = 1;
+ gWeatherPtr->unknown_6EB = Random() % 2;
+ gWeatherPtr->initStep++;
+ break;
+ case 7:
+ gWeatherPtr->unknown_6EC = (Random() & 1) + 1;
+ gWeatherPtr->initStep++;
+ // fall through
+ case 8:
+ sub_807A790(19);
+ if (gWeatherPtr->unknown_6EB == 0 && gWeatherPtr->unknown_6EC == 1)
+ SetThunderCounter(20);
+
+ gWeatherPtr->unknown_6E6 = (Random() % 3) + 6;
+ gWeatherPtr->initStep++;
+ break;
+ case 9:
+ if (--gWeatherPtr->unknown_6E6 == 0)
+ {
+ sub_807A790(3);
+ gWeatherPtr->unknown_6EA = 1;
+ if (--gWeatherPtr->unknown_6EC != 0)
+ {
+ gWeatherPtr->unknown_6E6 = (Random() % 16) + 60;
+ gWeatherPtr->initStep = 10;
+ }
+ else if (gWeatherPtr->unknown_6EB == 0)
+ {
+ gWeatherPtr->initStep = 4;
+ }
+ else
+ {
+ gWeatherPtr->initStep = 11;
+ }
+ }
+ break;
+ case 10:
+ if (--gWeatherPtr->unknown_6E6 == 0)
+ gWeatherPtr->initStep = 8;
+ break;
+ case 11:
+ gWeatherPtr->unknown_6E6 = (Random() % 16) + 60;
+ gWeatherPtr->initStep++;
+ break;
+ case 12:
+ if (--gWeatherPtr->unknown_6E6 == 0)
+ {
+ SetThunderCounter(100);
+ sub_807A790(19);
+ gWeatherPtr->unknown_6E6 = (Random() & 0xF) + 30;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ case 13:
+ if (--gWeatherPtr->unknown_6E6 == 0)
+ {
+ sub_807A7C4(19, 3, 5);
+ gWeatherPtr->initStep++;
+ }
+ break;
+ case 14:
+ if (gWeatherPtr->palProcessingState == WEATHER_PAL_STATE_IDLE)
+ {
+ gWeatherPtr->unknown_6EA = 1;
+ gWeatherPtr->initStep = 4;
+ }
+ break;
+ }
+}
+
+bool8 Thunderstorm_Finish(void)
+{
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ gWeatherPtr->unknown_6EA = 0;
+ gWeatherPtr->finishStep++;
+ // fall through
+ case 1:
+ Thunderstorm_Main();
+ if (gWeatherPtr->unknown_6EA)
+ {
+ if (gWeatherPtr->nextWeather == WEATHER_RAIN
+ || gWeatherPtr->nextWeather == WEATHER_RAIN_THUNDERSTORM
+ || gWeatherPtr->nextWeather == WEATHER_DOWNPOUR)
+ return FALSE;
+
+ gWeatherPtr->targetRainSpriteCount = 0;
+ gWeatherPtr->finishStep++;
+ }
+ break;
+ case 2:
+ if (!UpdateVisibleRainSprites())
+ {
+ DestroyRainSprites();
+ gWeatherPtr->thunderTriggered = FALSE;
+ gWeatherPtr->finishStep++;
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void SetThunderCounter(u16 max)
+{
+ if (!gWeatherPtr->thunderTriggered)
+ {
+ gWeatherPtr->thunderCounter = Random() % max;
+ gWeatherPtr->thunderTriggered = TRUE;
+ }
+}
+
+static void UpdateThunderSound(void)
+{
+ if (gWeatherPtr->thunderTriggered == TRUE)
+ {
+ if (gWeatherPtr->thunderCounter == 0)
+ {
+ if (IsSEPlaying())
+ return;
+
+ if (Random() & 1)
+ PlaySE(SE_T_KAMI);
+ else
+ PlaySE(SE_T_KAMI2);
+
+ gWeatherPtr->thunderTriggered = FALSE;
+ }
+ else
+ {
+ gWeatherPtr->thunderCounter--;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// WEATHER_FOG_HORIZONTAL and WEATHER_UNDERWATER
+//------------------------------------------------------------------------------
+
+// unused data
+static const u16 unusedData_839AB1C[] = {0, 6, 6, 12, 18, 42, 300, 300};
+
+static const struct OamData gOamData_839AB2C = {
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_BLEND,
+ .mosaic = FALSE,
+ .bpp = ST_OAM_4BPP,
+ .shape = SPRITE_SHAPE(64x64),
+ .x = 0,
+ .matrixNum = 0,
+ .size = SPRITE_SIZE(64x64),
+ .tileNum = 0,
+ .priority = 2,
+ .paletteNum = 0,
+ .affineParam = 0,
+};
+
+static const union AnimCmd gSpriteAnim_839AB34[] = {
+ ANIMCMD_FRAME(0, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd gSpriteAnim_839AB3C[] = {
+ ANIMCMD_FRAME(32, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd gSpriteAnim_839AB44[] = {
+ ANIMCMD_FRAME(64, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd gSpriteAnim_839AB4C[] = {
+ ANIMCMD_FRAME(96, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd gSpriteAnim_839AB54[] = {
+ ANIMCMD_FRAME(128, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd gSpriteAnim_839AB5C[] = {
+ ANIMCMD_FRAME(160, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd *const gSpriteAnimTable_839AB64[] = {
+ gSpriteAnim_839AB34,
+ gSpriteAnim_839AB3C,
+ gSpriteAnim_839AB44,
+ gSpriteAnim_839AB4C,
+ gSpriteAnim_839AB54,
+ gSpriteAnim_839AB5C,
+};
+
+static const union AffineAnimCmd gSpriteAffineAnim_839AB7C[] = {
+ AFFINEANIMCMD_FRAME(0x200, 0x200, 0, 0),
+ AFFINEANIMCMD_END,
+};
+
+static const union AffineAnimCmd *const gSpriteAffineAnimTable_839AB8C[] = {
+ gSpriteAffineAnim_839AB7C,
+};
+
+static void FogHorizontalSpriteCallback(struct Sprite *);
+static const struct SpriteTemplate sFogHorizontalSpriteTemplate = {
+ .tileTag = 0x1201,
+ .paletteTag = 0x1200,
+ .oam = &gOamData_839AB2C,
+ .anims = gSpriteAnimTable_839AB64,
+ .images = NULL,
+ .affineAnims = gSpriteAffineAnimTable_839AB8C,
+ .callback = FogHorizontalSpriteCallback,
+};
+
+static void CreateFogHorizontalSprites(void);
+static void DestroyFogHorizontalSprites(void);
+
+void FogHorizontal_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 20;
+ if (!gWeatherPtr->fogHSpritesCreated)
+ {
+ gWeatherPtr->fogHScrollCounter = 0;
+ gWeatherPtr->fogHScrollOffset = 0;
+ gWeatherPtr->fogHScrollPosX = 0;
+ Weather_SetBlendCoeffs(0, 16);
+ }
+}
+
+void FogHorizontal_InitAll(void)
+{
+ FogHorizontal_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ FogHorizontal_Main();
+}
+
+void FogHorizontal_Main(void)
+{
+ gWeatherPtr->fogHScrollPosX = (gSpriteCoordOffsetX - gWeatherPtr->fogHScrollOffset) & 0xFF;
+ if (++gWeatherPtr->fogHScrollCounter > 3)
+ {
+ gWeatherPtr->fogHScrollCounter = 0;
+ gWeatherPtr->fogHScrollOffset++;
+ }
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ CreateFogHorizontalSprites();
+ if (gWeatherPtr->currWeather == WEATHER_FOG_HORIZONTAL)
+ Weather_SetTargetBlendCoeffs(12, 8, 3);
+ else
+ Weather_SetTargetBlendCoeffs(4, 16, 0);
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ if (Weather_UpdateBlend())
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ }
+}
+
+bool8 FogHorizontal_Finish(void)
+{
+ gWeatherPtr->fogHScrollPosX = (gSpriteCoordOffsetX - gWeatherPtr->fogHScrollOffset) & 0xFF;
+ if (++gWeatherPtr->fogHScrollCounter > 3)
+ {
+ gWeatherPtr->fogHScrollCounter = 0;
+ gWeatherPtr->fogHScrollOffset++;
+ }
+
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ Weather_SetTargetBlendCoeffs(0, 16, 3);
+ gWeatherPtr->finishStep++;
+ break;
+ case 1:
+ if (Weather_UpdateBlend())
+ gWeatherPtr->finishStep++;
+ break;
+ case 2:
+ DestroyFogHorizontalSprites();
+ gWeatherPtr->finishStep++;
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#define tSpriteColumn data[0]
+
+static void FogHorizontalSpriteCallback(struct Sprite *sprite)
+{
+ sprite->pos2.y = (u8)gSpriteCoordOffsetY;
+ sprite->pos1.x = gWeatherPtr->fogHScrollPosX + 32 + sprite->tSpriteColumn * 64;
+ if (sprite->pos1.x > 271)
+ {
+ sprite->pos1.x = 480 + gWeatherPtr->fogHScrollPosX - (4 - sprite->tSpriteColumn) * 64;
+ sprite->pos1.x &= 0x1FF;
+ }
+}
+
+static void CreateFogHorizontalSprites(void)
+{
+ u16 i;
+ u8 spriteId;
+ struct Sprite *sprite;
+
+ if (!gWeatherPtr->fogHSpritesCreated)
+ {
+ struct SpriteSheet fogHorizontalSpriteSheet = {
+ .data = gWeatherFogHorizontalTiles,
+ .size = 0x0800,
+ .tag = 0x1201,
+ };
+ LoadSpriteSheet(&fogHorizontalSpriteSheet);
+ for (i = 0; i < NUM_FOG_HORIZONTAL_SPRITES; i++)
+ {
+ spriteId = CreateSpriteAtEnd(&sFogHorizontalSpriteTemplate, 0, 0, 0xFF);
+ if (spriteId != MAX_SPRITES)
+ {
+ sprite = &gSprites[spriteId];
+ sprite->tSpriteColumn = i % 5;
+ sprite->pos1.x = (i % 5) * 64 + 32;
+ sprite->pos1.y = (i / 5) * 64 + 32;
+ gWeatherPtr->sprites.s2.fogHSprites[i] = sprite;
+ }
+ else
+ {
+ gWeatherPtr->sprites.s2.fogHSprites[i] = NULL;
+ }
+ }
+
+ gWeatherPtr->fogHSpritesCreated = TRUE;
+ }
+}
+
+static void DestroyFogHorizontalSprites(void)
+{
+ u16 i;
+
+ if (gWeatherPtr->fogHSpritesCreated)
+ {
+ for (i = 0; i < NUM_FOG_HORIZONTAL_SPRITES; i++)
+ {
+ if (gWeatherPtr->sprites.s2.fogHSprites[i] != NULL)
+ DestroySprite(gWeatherPtr->sprites.s2.fogHSprites[i]);
+ }
+
+ FreeSpriteTilesByTag(0x1201);
+ gWeatherPtr->fogHSpritesCreated = FALSE;
+ }
+}
+
+#undef tSpriteColumn
+
+//------------------------------------------------------------------------------
+// WEATHER_VOLCANIC_ASH
+//------------------------------------------------------------------------------
+
+static void LoadAshSpriteSheet(void);
+static void CreateAshSprites(void);
+static void DestroyAshSprites(void);
+static void UpdateAshSprite(struct Sprite *);
+
+void Ash_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = FALSE;
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 20;
+ gWeatherPtr->unknown_6FE = 20;
+ if (!gWeatherPtr->ashSpritesCreated)
+ {
+ Weather_SetBlendCoeffs(0, 16);
+ SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(64, 63)); // These aren't valid blend coefficients!
+ }
+}
+
+void Ash_InitAll(void)
+{
+ Ash_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Ash_Main();
+}
+
+void Ash_Main(void)
+{
+ gWeatherPtr->ashBaseSpritesX = gSpriteCoordOffsetX & 0x1FF;
+ while (gWeatherPtr->ashBaseSpritesX >= 240)
+ gWeatherPtr->ashBaseSpritesX -= 240;
+
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ LoadAshSpriteSheet();
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ if (!gWeatherPtr->ashSpritesCreated)
+ CreateAshSprites();
+
+ Weather_SetTargetBlendCoeffs(16, 0, 1);
+ gWeatherPtr->initStep++;
+ break;
+ case 2:
+ if (Weather_UpdateBlend())
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ default:
+ Weather_UpdateBlend();
+ break;
+ }
+}
+
+bool8 Ash_Finish(void)
+{
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ Weather_SetTargetBlendCoeffs(0, 16, 1);
+ gWeatherPtr->finishStep++;
+ break;
+ case 1:
+ if (Weather_UpdateBlend())
+ {
+ DestroyAshSprites();
+ gWeatherPtr->finishStep++;
+ }
+ break;
+ case 2:
+ SetGpuReg(REG_OFFSET_BLDALPHA, 0);
+ gWeatherPtr->finishStep++;
+ return FALSE;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static const struct SpriteSheet sAshSpriteSheet = {
+ .data = gWeatherAshTiles,
+ .size = 0x1000,
+ .tag = 0x1202,
+};
+
+static void LoadAshSpriteSheet(void)
+{
+ LoadSpriteSheet(&sAshSpriteSheet);
+}
+
+static const struct OamData sAshSpriteOamData = {
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_BLEND,
+ .bpp = ST_OAM_4BPP,
+ .shape = SPRITE_SHAPE(64x64),
+ .x = 0,
+ .size = SPRITE_SIZE(64x64),
+ .tileNum = 0,
+ .priority = 1,
+ .paletteNum = 15,
+};
+
+static const union AnimCmd sAshSpriteAnimCmd0[] = {
+ ANIMCMD_FRAME(0, 60),
+ ANIMCMD_FRAME(64, 60),
+ ANIMCMD_JUMP(0),
+};
+
+static const union AnimCmd *const sAshSpriteAnimCmds[] = {
+ sAshSpriteAnimCmd0,
+};
+
+static const struct SpriteTemplate sAshSpriteTemplate = {
+ .tileTag = 0x1202,
+ .paletteTag = 0x1200,
+ .oam = &sAshSpriteOamData,
+ .anims = sAshSpriteAnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = UpdateAshSprite,
+};
+
+#define tOffsetY data[0]
+#define tCounterY data[1]
+#define tSpriteColumn data[2]
+#define tSpriteRow data[3]
+
+static void CreateAshSprites(void)
+{
+ u8 i;
+ u8 spriteId;
+ struct Sprite *sprite;
+
+ if (!gWeatherPtr->ashSpritesCreated)
+ {
+ for (i = 0; i < NUM_ASH_SPRITES; i++)
+ {
+ spriteId = CreateSpriteAtEnd(&sAshSpriteTemplate, 0, 0, 0x4E);
+ if (spriteId != MAX_SPRITES)
+ {
+ sprite = &gSprites[spriteId];
+ sprite->tCounterY = 0;
+ sprite->tSpriteColumn = (u8)(i % 5);
+ sprite->tSpriteRow = (u8)(i / 5);
+ sprite->tOffsetY = sprite->tSpriteRow * 64 + 32;
+ gWeatherPtr->sprites.s2.ashSprites[i] = sprite;
+ }
+ else
+ {
+ gWeatherPtr->sprites.s2.ashSprites[i] = NULL;
+ }
+ }
+
+ gWeatherPtr->ashSpritesCreated = TRUE;
+ }
+}
+
+static void DestroyAshSprites(void)
+{
+ u16 i;
+
+ if (gWeatherPtr->ashSpritesCreated)
+ {
+ for (i = 0; i < NUM_ASH_SPRITES; i++)
+ {
+ if (gWeatherPtr->sprites.s2.ashSprites[i] != NULL)
+ DestroySprite(gWeatherPtr->sprites.s2.ashSprites[i]);
+ }
+
+ FreeSpriteTilesByTag(0x1202);
+ gWeatherPtr->ashSpritesCreated = FALSE;
+ }
+}
+
+static void UpdateAshSprite(struct Sprite *sprite)
+{
+ if (++sprite->tCounterY > 5)
+ {
+ sprite->tCounterY = 0;
+ sprite->tOffsetY++;
+ }
+
+ sprite->pos1.y = gSpriteCoordOffsetY + sprite->tOffsetY;
+ sprite->pos1.x = gWeatherPtr->ashBaseSpritesX + 32 + sprite->tSpriteColumn * 64;
+ if (sprite->pos1.x > 271)
+ {
+ sprite->pos1.x = gWeatherPtr->ashBaseSpritesX + 480 - (4 - sprite->tSpriteColumn) * 64;
+ sprite->pos1.x &= 0x1FF;
+ }
+}
+
+#undef tOffsetY
+#undef tCounterY
+#undef tSpriteColumn
+#undef tSpriteRow
+
+//------------------------------------------------------------------------------
+// WEATHER_FOG_DIAGONAL
+//------------------------------------------------------------------------------
+
+static void UpdateFogDiagonalMovement(void);
+static void CreateFogDiagonalSprites(void);
+static void DestroyFogDiagonalSprites(void);
+static void UpdateFogDiagonalSprite(struct Sprite *);
+
+void FogDiagonal_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = 0;
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 20;
+ gWeatherPtr->fogHScrollCounter = 0;
+ gWeatherPtr->fogHScrollOffset = 1;
+ if (!gWeatherPtr->fogDSpritesCreated)
+ {
+ gWeatherPtr->fogDScrollXCounter = 0;
+ gWeatherPtr->fogDScrollYCounter = 0;
+ gWeatherPtr->fogDXOffset = 0;
+ gWeatherPtr->fogDYOffset = 0;
+ gWeatherPtr->fogDBaseSpritesX = 0;
+ gWeatherPtr->fogDPosY = 0;
+ Weather_SetBlendCoeffs(0, 16);
+ }
+}
+
+void FogDiagonal_InitAll(void)
+{
+ FogDiagonal_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ FogDiagonal_Main();
+}
+
+void FogDiagonal_Main(void)
+{
+ UpdateFogDiagonalMovement();
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ CreateFogDiagonalSprites();
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ Weather_SetTargetBlendCoeffs(12, 8, 8);
+ gWeatherPtr->initStep++;
+ break;
+ case 2:
+ if (!Weather_UpdateBlend())
+ break;
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ break;
+ }
+}
+
+bool8 FogDiagonal_Finish(void)
+{
+ UpdateFogDiagonalMovement();
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ Weather_SetTargetBlendCoeffs(0, 16, 1);
+ gWeatherPtr->finishStep++;
+ break;
+ case 1:
+ if (!Weather_UpdateBlend())
+ break;
+ gWeatherPtr->finishStep++;
+ break;
+ case 2:
+ DestroyFogDiagonalSprites();
+ gWeatherPtr->finishStep++;
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void UpdateFogDiagonalMovement(void)
+{
+ if (++gWeatherPtr->fogDScrollXCounter > 2)
+ {
+ gWeatherPtr->fogDXOffset++;
+ gWeatherPtr->fogDScrollXCounter = 0;
+ }
+
+ if (++gWeatherPtr->fogDScrollYCounter > 4)
+ {
+ gWeatherPtr->fogDYOffset++;
+ gWeatherPtr->fogDScrollYCounter = 0;
+ }
+
+ gWeatherPtr->fogDBaseSpritesX = (gSpriteCoordOffsetX - gWeatherPtr->fogDXOffset) & 0xFF;
+ gWeatherPtr->fogDPosY = gSpriteCoordOffsetY + gWeatherPtr->fogDYOffset;
+}
+
+static const struct SpriteSheet gFogDiagonalSpriteSheet = {
+ .data = gWeatherFogDiagonalTiles,
+ .size = 0x0800,
+ .tag = 0x1203,
+};
+
+static const struct OamData sFogDiagonalSpriteOamData = {
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_BLEND,
+ .bpp = ST_OAM_4BPP,
+ .shape = SPRITE_SHAPE(64x64),
+ .x = 0,
+ .size = SPRITE_SIZE(64x64),
+ .tileNum = 0,
+ .priority = 2,
+ .paletteNum = 0,
+};
+
+static const union AnimCmd sFogDiagonalSpriteAnimCmd0[] = {
+ ANIMCMD_FRAME(0, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd *const sFogDiagonalSpriteAnimCmds[] = {
+ sFogDiagonalSpriteAnimCmd0,
+};
+
+static const struct SpriteTemplate sFogDiagonalSpriteTemplate = {
+ .tileTag = 0x1203,
+ .paletteTag = 0x1200,
+ .oam = &sFogDiagonalSpriteOamData,
+ .anims = sFogDiagonalSpriteAnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = UpdateFogDiagonalSprite,
+};
+
+#define tSpriteColumn data[0]
+#define tSpriteRow data[1]
+
+static void CreateFogDiagonalSprites(void)
+{
+ u16 i;
+ struct SpriteSheet fogDiagonalSpriteSheet;
+ u8 spriteId;
+ struct Sprite *sprite;
+
+ if (!gWeatherPtr->fogDSpritesCreated)
+ {
+ fogDiagonalSpriteSheet = gFogDiagonalSpriteSheet;
+ LoadSpriteSheet(&fogDiagonalSpriteSheet);
+ for (i = 0; i < NUM_FOG_DIAGONAL_SPRITES; i++)
+ {
+ spriteId = CreateSpriteAtEnd(&sFogDiagonalSpriteTemplate, 0, (i / 5) * 64, 0xFF);
+ if (spriteId != MAX_SPRITES)
+ {
+ sprite = &gSprites[spriteId];
+ sprite->tSpriteColumn = i % 5;
+ sprite->tSpriteRow = i / 5;
+ gWeatherPtr->sprites.s2.fogDSprites[i] = sprite;
+ }
+ else
+ {
+ gWeatherPtr->sprites.s2.fogDSprites[i] = NULL;
+ }
+ }
+
+ gWeatherPtr->fogDSpritesCreated = TRUE;
+ }
+}
+
+static void DestroyFogDiagonalSprites(void)
+{
+ u16 i;
+
+ if (gWeatherPtr->fogDSpritesCreated)
+ {
+ for (i = 0; i < NUM_FOG_DIAGONAL_SPRITES; i++)
+ {
+ if (gWeatherPtr->sprites.s2.fogDSprites[i])
+ DestroySprite(gWeatherPtr->sprites.s2.fogDSprites[i]);
+ }
+
+ FreeSpriteTilesByTag(0x1203);
+ gWeatherPtr->fogDSpritesCreated = FALSE;
+ }
+}
+
+static void UpdateFogDiagonalSprite(struct Sprite *sprite)
+{
+ sprite->pos2.y = gWeatherPtr->fogDPosY;
+ sprite->pos1.x = gWeatherPtr->fogDBaseSpritesX + 32 + sprite->tSpriteColumn * 64;
+ if (sprite->pos1.x > 271)
+ {
+ sprite->pos1.x = gWeatherPtr->fogDBaseSpritesX + 480 - (4 - sprite->tSpriteColumn) * 64;
+ sprite->pos1.x &= 0x1FF;
+ }
+}
+
+#undef tSpriteColumn
+#undef tSpriteRow
+
+//------------------------------------------------------------------------------
+// WEATHER_SANDSTORM
+//------------------------------------------------------------------------------
+
+static void UpdateSandstormWaveIndex(void);
+static void UpdateSandstormMovement(void);
+static void CreateSandstormSprites(void);
+static void CreateSwirlSandstormSprites(void);
+static void DestroySandstormSprites(void);
+static void UpdateSandstormSprite(struct Sprite *);
+static void WaitSandSwirlSpriteEntrance(struct Sprite *);
+static void UpdateSandstormSwirlSprite(struct Sprite *);
+
+#define MIN_SANDSTORM_WAVE_INDEX 0x20
+
+void Sandstorm_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->weatherGfxLoaded = 0;
+ gWeatherPtr->gammaTargetIndex = 0;
+ gWeatherPtr->gammaStepDelay = 20;
+ if (!gWeatherPtr->sandstormSpritesCreated)
+ {
+ gWeatherPtr->sandstormXOffset = gWeatherPtr->sandstormYOffset = 0;
+ gWeatherPtr->sandstormWaveIndex = 8;
+ gWeatherPtr->sandstormWaveCounter = 0;
+ // Dead code. How does the compiler not optimize this out?
+ if (gWeatherPtr->sandstormWaveIndex >= 0x80 - MIN_SANDSTORM_WAVE_INDEX)
+ gWeatherPtr->sandstormWaveIndex = 0x80 - gWeatherPtr->sandstormWaveIndex;
+
+ Weather_SetBlendCoeffs(0, 16);
+ }
+}
+
+void Sandstorm_InitAll(void)
+{
+ Sandstorm_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Sandstorm_Main();
+}
+
+void Sandstorm_Main(void)
+{
+ UpdateSandstormMovement();
+ UpdateSandstormWaveIndex();
+ if (gWeatherPtr->sandstormWaveIndex >= 0x80 - MIN_SANDSTORM_WAVE_INDEX)
+ gWeatherPtr->sandstormWaveIndex = MIN_SANDSTORM_WAVE_INDEX;
+
+ switch (gWeatherPtr->initStep)
+ {
+ case 0:
+ CreateSandstormSprites();
+ CreateSwirlSandstormSprites();
+ gWeatherPtr->initStep++;
+ break;
+ case 1:
+ Weather_SetTargetBlendCoeffs(16, 0, 0);
+ gWeatherPtr->initStep++;
+ break;
+ case 2:
+ if (Weather_UpdateBlend())
+ {
+ gWeatherPtr->weatherGfxLoaded = TRUE;
+ gWeatherPtr->initStep++;
+ }
+ break;
+ }
+}
+
+bool8 Sandstorm_Finish(void)
+{
+ UpdateSandstormMovement();
+ UpdateSandstormWaveIndex();
+ switch (gWeatherPtr->finishStep)
+ {
+ case 0:
+ Weather_SetTargetBlendCoeffs(0, 16, 0);
+ gWeatherPtr->finishStep++;
+ break;
+ case 1:
+ if (Weather_UpdateBlend())
+ gWeatherPtr->finishStep++;
+ break;
+ case 2:
+ DestroySandstormSprites();
+ gWeatherPtr->finishStep++;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void UpdateSandstormWaveIndex(void)
+{
+ if (gWeatherPtr->sandstormWaveCounter++ > 4)
+ {
+ gWeatherPtr->sandstormWaveIndex++;
+ gWeatherPtr->sandstormWaveCounter = 0;
+ }
+}
+
+static void UpdateSandstormMovement(void)
+{
+ gWeatherPtr->sandstormXOffset -= gSineTable[gWeatherPtr->sandstormWaveIndex] * 4;
+ gWeatherPtr->sandstormYOffset -= gSineTable[gWeatherPtr->sandstormWaveIndex];
+ gWeatherPtr->sandstormBaseSpritesX = (gSpriteCoordOffsetX + (gWeatherPtr->sandstormXOffset >> 8)) & 0xFF;
+ gWeatherPtr->sandstormPosY = gSpriteCoordOffsetY + (gWeatherPtr->sandstormYOffset >> 8);
+}
+
+static void DestroySandstormSprites(void)
+{
+ u16 i;
+
+ if (gWeatherPtr->sandstormSpritesCreated)
+ {
+ for (i = 0; i < NUM_SANDSTORM_SPRITES; i++)
+ {
+ if (gWeatherPtr->sprites.s2.sandstormSprites1[i])
+ DestroySprite(gWeatherPtr->sprites.s2.sandstormSprites1[i]);
+ }
+
+ gWeatherPtr->sandstormSpritesCreated = FALSE;
+ FreeSpriteTilesByTag(0x1204);
+ }
+
+ if (gWeatherPtr->sandstormSwirlSpritesCreated)
+ {
+ for (i = 0; i < NUM_SWIRL_SANDSTORM_SPRITES; i++)
+ {
+ if (gWeatherPtr->sprites.s2.sandstormSprites2[i] != NULL)
+ DestroySprite(gWeatherPtr->sprites.s2.sandstormSprites2[i]);
+ }
+
+ gWeatherPtr->sandstormSwirlSpritesCreated = FALSE;
+ }
+}
+
+static const struct OamData sSandstormSpriteOamData = {
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_BLEND,
+ .bpp = ST_OAM_4BPP,
+ .shape = SPRITE_SHAPE(64x64),
+ .x = 0,
+ .size = SPRITE_SIZE(64x64),
+ .tileNum = 0,
+ .priority = 1,
+ .paletteNum = 0,
+};
+
+static const union AnimCmd sSandstormSpriteAnimCmd0[] = {
+ ANIMCMD_FRAME(0, 3),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd sSandstormSpriteAnimCmd1[] = {
+ ANIMCMD_FRAME(64, 3),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd *const sSandstormSpriteAnimCmds[] = {
+ sSandstormSpriteAnimCmd0,
+ sSandstormSpriteAnimCmd1,
+};
+
+static const struct SpriteTemplate sSandstormSpriteTemplate = {
+ .tileTag = 0x1204,
+ .paletteTag = 0x1200,
+ .oam = &sSandstormSpriteOamData,
+ .anims = sSandstormSpriteAnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = UpdateSandstormSprite,
+};
+
+static const struct SpriteSheet sSandstormSpriteSheet = {
+ .data = gWeatherSandstormTiles,
+ .size = 0x0a00,
+ .tag = 0x1204,
+};
+
+// Regular sandstorm sprites
+#define tSpriteColumn data[0]
+#define tSpriteRow data[1]
+
+// Swirly sandstorm sprites
+#define tRadius data[0]
+#define tWaveIndex data[1]
+#define tRadiusCounter data[2]
+#define tEntranceDelay data[3]
+
+static void CreateSandstormSprites(void)
+{
+ u16 i;
+ u8 spriteId;
+
+ if (!gWeatherPtr->sandstormSpritesCreated)
+ {
+ LoadSpriteSheet(&sSandstormSpriteSheet);
+ LoadCustomWeatherSpritePalette(gSandstormWeatherPalette);
+ for (i = 0; i < NUM_SANDSTORM_SPRITES; i++)
+ {
+ spriteId = CreateSpriteAtEnd(&sSandstormSpriteTemplate, 0, (i / 5) * 64, 1);
+ if (spriteId != MAX_SPRITES)
+ {
+ gWeatherPtr->sprites.s2.sandstormSprites1[i] = &gSprites[spriteId];
+ gWeatherPtr->sprites.s2.sandstormSprites1[i]->tSpriteColumn = i % 5;
+ gWeatherPtr->sprites.s2.sandstormSprites1[i]->tSpriteRow = i / 5;
+ }
+ else
+ {
+ gWeatherPtr->sprites.s2.sandstormSprites1[i] = NULL;
+ }
+ }
+
+ gWeatherPtr->sandstormSpritesCreated = TRUE;
+ }
+}
+
+static const u16 sSwirlEntranceDelays[] = {0, 120, 80, 160, 40, 0};
+
+static void CreateSwirlSandstormSprites(void)
+{
+ u16 i;
+ u8 spriteId;
+
+ if (!gWeatherPtr->sandstormSwirlSpritesCreated)
+ {
+ for (i = 0; i < NUM_SWIRL_SANDSTORM_SPRITES; i++)
+ {
+ spriteId = CreateSpriteAtEnd(&sSandstormSpriteTemplate, i * 48 + 24, 208, 1);
+ if (spriteId != MAX_SPRITES)
+ {
+ gWeatherPtr->sprites.s2.sandstormSprites2[i] = &gSprites[spriteId];
+ gWeatherPtr->sprites.s2.sandstormSprites2[i]->oam.size = ST_OAM_SIZE_2;
+ gWeatherPtr->sprites.s2.sandstormSprites2[i]->tSpriteRow = i * 51;
+ gWeatherPtr->sprites.s2.sandstormSprites2[i]->tRadius = 8;
+ gWeatherPtr->sprites.s2.sandstormSprites2[i]->tRadiusCounter = 0;
+ gWeatherPtr->sprites.s2.sandstormSprites2[i]->data[4] = 0x6730; // unused value
+ gWeatherPtr->sprites.s2.sandstormSprites2[i]->tEntranceDelay = sSwirlEntranceDelays[i];
+ StartSpriteAnim(gWeatherPtr->sprites.s2.sandstormSprites2[i], 1);
+ CalcCenterToCornerVec(gWeatherPtr->sprites.s2.sandstormSprites2[i], SPRITE_SHAPE(32x32), SPRITE_SIZE(32x32), ST_OAM_AFFINE_OFF);
+ gWeatherPtr->sprites.s2.sandstormSprites2[i]->callback = WaitSandSwirlSpriteEntrance;
+ }
+ else
+ {
+ gWeatherPtr->sprites.s2.sandstormSprites2[i] = NULL;
+ }
+
+ gWeatherPtr->sandstormSwirlSpritesCreated = TRUE;
+ }
+ }
+}
+
+static void UpdateSandstormSprite(struct Sprite *sprite)
+{
+ sprite->pos2.y = gWeatherPtr->sandstormPosY;
+ sprite->pos1.x = gWeatherPtr->sandstormBaseSpritesX + 32 + sprite->tSpriteColumn * 64;
+ if (sprite->pos1.x > 271)
+ {
+ sprite->pos1.x = gWeatherPtr->sandstormBaseSpritesX + 480 - (4 - sprite->tSpriteColumn) * 64;
+ sprite->pos1.x &= 0x1FF;
+ }
+}
+
+static void WaitSandSwirlSpriteEntrance(struct Sprite *sprite)
+{
+ if (--sprite->tEntranceDelay == -1)
+ sprite->callback = UpdateSandstormSwirlSprite;
+}
+
+static void UpdateSandstormSwirlSprite(struct Sprite *sprite)
+{
+ u32 x, y;
+
+ if (--sprite->pos1.y < -48)
+ {
+ sprite->pos1.y = 208;
+ sprite->tRadius = 4;
+ }
+
+ x = sprite->tRadius * gSineTable[sprite->tWaveIndex];
+ y = sprite->tRadius * gSineTable[sprite->tWaveIndex + 0x40];
+ sprite->pos2.x = x >> 8;
+ sprite->pos2.y = y >> 8;
+ sprite->tWaveIndex = (sprite->tWaveIndex + 10) & 0xFF;
+ if (++sprite->tRadiusCounter > 8)
+ {
+ sprite->tRadiusCounter = 0;
+ sprite->tRadius++;
+ }
+}
+
+#undef tSpriteColumn
+#undef tSpriteRow
+
+#undef tRadius
+#undef tWaveIndex
+#undef tRadiusCounter
+#undef tEntranceDelay
+
+//------------------------------------------------------------------------------
+// WEATHER_SHADE
+//------------------------------------------------------------------------------
+
+void Shade_InitVars(void)
+{
+ gWeatherPtr->initStep = 0;
+ gWeatherPtr->gammaTargetIndex = 3;
+ gWeatherPtr->gammaStepDelay = 20;
+}
+
+void Shade_InitAll(void)
+{
+ Shade_InitVars();
+}
+
+void Shade_Main(void)
+{
+}
+
+bool8 Shade_Finish(void)
+{
+ return FALSE;
+}
+
+//------------------------------------------------------------------------------
+// WEATHER_UNDERWATER_BUBBLES
+//------------------------------------------------------------------------------
+
+static void CreateBubbleSprite(u16);
+static void DestroyBubbleSprites(void);
+static void UpdateBubbleSprite(struct Sprite *);
+
+static const u8 sBubbleStartDelays[] = {40, 90, 60, 90, 2, 60, 40, 30};
+
+static const struct SpriteSheet sWeatherBubbleSpriteSheet = {
+ .data = gWeatherBubbleTiles,
+ .size = 0x0040,
+ .tag = 0x1205,
+};
+
+static const s16 sBubbleStartCoords[][2] = {
+ {120, 160},
+ {376, 160},
+ { 40, 140},
+ {296, 140},
+ {180, 130},
+ {436, 130},
+ { 60, 160},
+ {436, 160},
+ {220, 180},
+ {476, 180},
+ { 10, 90},
+ {266, 90},
+ {256, 160},
+};
+
+void Bubbles_InitVars(void)
+{
+ FogHorizontal_InitVars();
+ if (!gWeatherPtr->bubblesSpritesCreated)
+ {
+ LoadSpriteSheet(&sWeatherBubbleSpriteSheet);
+ gWeatherPtr->bubblesDelayIndex = 0;
+ gWeatherPtr->bubblesDelayCounter = sBubbleStartDelays[0];
+ gWeatherPtr->bubblesCoordsIndex = 0;
+ gWeatherPtr->bubblesSpriteCount = 0;
+ }
+}
+
+void Bubbles_InitAll(void)
+{
+ Bubbles_InitVars();
+ while (!gWeatherPtr->weatherGfxLoaded)
+ Bubbles_Main();
+}
+
+void Bubbles_Main(void)
+{
+ FogHorizontal_Main();
+ if (++gWeatherPtr->bubblesDelayCounter > sBubbleStartDelays[gWeatherPtr->bubblesDelayIndex])
+ {
+ gWeatherPtr->bubblesDelayCounter = 0;
+ if (++gWeatherPtr->bubblesDelayIndex > NELEMS(sBubbleStartDelays) - 1)
+ gWeatherPtr->bubblesDelayIndex = 0;
+
+ CreateBubbleSprite(gWeatherPtr->bubblesCoordsIndex);
+ if (++gWeatherPtr->bubblesCoordsIndex > NELEMS(sBubbleStartCoords) - 1)
+ gWeatherPtr->bubblesCoordsIndex = 0;
+ }
+}
+
+bool8 Bubbles_Finish(void)
+{
+ if (!FogHorizontal_Finish())
+ {
+ DestroyBubbleSprites();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static const union AnimCmd sBubbleSpriteAnimCmd0[] = {
+ ANIMCMD_FRAME(0, 16),
+ ANIMCMD_FRAME(1, 16),
+ ANIMCMD_END,
+};
+
+static const union AnimCmd *const sBubbleSpriteAnimCmds[] = {
+ sBubbleSpriteAnimCmd0,
+};
+
+static const struct SpriteTemplate sBubbleSpriteTemplate = {
+ .tileTag = 0x1205,
+ .paletteTag = 0x1200,
+ .oam = &gOamData_AffineOff_ObjNormal_8x8,
+ .anims = sBubbleSpriteAnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = UpdateBubbleSprite,
+};
+
+#define tScrollXCounter data[0]
+#define tScrollXDir data[1]
+#define tCounter data[2]
+
+static void CreateBubbleSprite(u16 coordsIndex)
+{
+ s16 x = sBubbleStartCoords[coordsIndex][0];
+ s16 y = sBubbleStartCoords[coordsIndex][1] - gSpriteCoordOffsetY;
+ u8 spriteId = CreateSpriteAtEnd(&sBubbleSpriteTemplate, x, y, 0);
+ if (spriteId != MAX_SPRITES)
+ {
+ gSprites[spriteId].oam.priority = 1;
+ gSprites[spriteId].coordOffsetEnabled = TRUE;
+ gSprites[spriteId].tScrollXCounter = 0;
+ gSprites[spriteId].tScrollXDir = 0;
+ gSprites[spriteId].tCounter = 0;
+ gWeatherPtr->bubblesSpriteCount++;
+ }
+}
+
+static void DestroyBubbleSprites(void)
+{
+ u16 i;
+
+ for (i = 0; i < MAX_SPRITES; i++)
+ {
+ if (gSprites[i].template == &sBubbleSpriteTemplate)
+ DestroySprite(&gSprites[i]);
+ }
+
+ FreeSpriteTilesByTag(0x1205);
+}
+
+static void UpdateBubbleSprite(struct Sprite *sprite)
+{
+ ++sprite->tScrollXCounter;
+ if (++sprite->tScrollXCounter > 8) // double increment
+ {
+ sprite->tScrollXCounter = 0;
+ if (sprite->tScrollXDir == 0)
+ {
+ if (++sprite->pos2.x > 4)
+ sprite->tScrollXDir = 1;
+ }
+ else
+ {
+ if (--sprite->pos2.x <= 0)
+ sprite->tScrollXDir = 0;
+ }
+ }
+
+ sprite->pos1.y -= 3;
+ if (++sprite->tCounter >= 120)
+ DestroySprite(sprite);
+}
+
+#undef tScrollXCounter
+#undef tScrollXDir
+#undef tCounter
diff --git a/src/field_weather_util.c b/src/field_weather_util.c
new file mode 100644
index 000000000..459071736
--- /dev/null
+++ b/src/field_weather_util.c
@@ -0,0 +1,105 @@
+#include "global.h"
+#include "field_weather.h"
+#include "overworld.h"
+#include "constants/weather.h"
+
+static u8 TranslateWeatherNum(u8 weather);
+static void UpdateRainCounter(u8 newWeather, u8 oldWeather);
+
+void SetSav1Weather(u32 weather)
+{
+ u8 oldWeather = gSaveBlock1Ptr->weather;
+ gSaveBlock1Ptr->weather = TranslateWeatherNum(weather);
+ UpdateRainCounter(gSaveBlock1Ptr->weather, oldWeather);
+}
+
+u8 GetSav1Weather(void)
+{
+ return gSaveBlock1Ptr->weather;
+}
+
+void SetSav1WeatherFromCurrMapHeader(void)
+{
+ u8 oldWeather = gSaveBlock1Ptr->weather;
+ gSaveBlock1Ptr->weather = TranslateWeatherNum(gMapHeader.weather);
+ UpdateRainCounter(gSaveBlock1Ptr->weather, oldWeather);
+}
+
+void SetWeather(u32 weather)
+{
+ SetSav1Weather(weather);
+ SetNextWeather(GetSav1Weather());
+}
+
+void SetWeather_Unused(u32 weather)
+{
+ SetSav1Weather(weather);
+ SetCurrentAndNextWeather(GetSav1Weather());
+}
+
+void DoCurrentWeather(void)
+{
+ u8 weather = GetSav1Weather();
+
+ SetNextWeather(weather);
+}
+
+void ResumePausedWeather(void)
+{
+ u8 weather = GetSav1Weather();
+
+ SetCurrentAndNextWeather(weather);
+}
+
+static const u8 sWeatherCycleRoute119[] = {
+ WEATHER_SUNNY,
+ WEATHER_RAIN,
+ WEATHER_RAIN_THUNDERSTORM,
+ WEATHER_RAIN,
+};
+
+static const u8 sWeatherCycleRoute123[] = {
+ WEATHER_SUNNY,
+ WEATHER_SUNNY,
+ WEATHER_RAIN,
+ WEATHER_SUNNY,
+};
+
+static u8 TranslateWeatherNum(u8 weather)
+{
+ switch (weather)
+ {
+ case WEATHER_NONE: return WEATHER_NONE;
+ case WEATHER_SUNNY_CLOUDS: return WEATHER_SUNNY_CLOUDS;
+ case WEATHER_SUNNY: return WEATHER_SUNNY;
+ case WEATHER_RAIN: return WEATHER_RAIN;
+ case WEATHER_SNOW: return WEATHER_SNOW;
+ case WEATHER_RAIN_THUNDERSTORM: return WEATHER_RAIN_THUNDERSTORM;
+ case WEATHER_FOG_HORIZONTAL: return WEATHER_FOG_HORIZONTAL;
+ case WEATHER_VOLCANIC_ASH: return WEATHER_VOLCANIC_ASH;
+ case WEATHER_SANDSTORM: return WEATHER_SANDSTORM;
+ case WEATHER_FOG_DIAGONAL: return WEATHER_FOG_DIAGONAL;
+ case WEATHER_UNDERWATER: return WEATHER_UNDERWATER;
+ case WEATHER_SHADE: return WEATHER_SHADE;
+ case WEATHER_DROUGHT: return WEATHER_DROUGHT;
+ case WEATHER_DOWNPOUR: return WEATHER_DOWNPOUR;
+ case WEATHER_UNDERWATER_BUBBLES: return WEATHER_UNDERWATER_BUBBLES;
+ case WEATHER_ROUTE119_CYCLE: return sWeatherCycleRoute119[gSaveBlock1Ptr->weatherCycleStage];
+ case WEATHER_ROUTE123_CYCLE: return sWeatherCycleRoute123[gSaveBlock1Ptr->weatherCycleStage];
+ default: return WEATHER_NONE;
+ }
+}
+
+void UpdateWeatherPerDay(u16 increment)
+{
+ u16 weatherStage = gSaveBlock1Ptr->weatherCycleStage + increment;
+ weatherStage %= 4;
+ gSaveBlock1Ptr->weatherCycleStage = weatherStage;
+}
+
+static void UpdateRainCounter(u8 newWeather, u8 oldWeather)
+{
+ if (newWeather != oldWeather
+ && (newWeather == WEATHER_RAIN || newWeather == WEATHER_RAIN_THUNDERSTORM))
+ IncrementGameStat(GAME_STAT_GOT_RAINED_ON);
+}
diff --git a/src/ice.c b/src/ice.c
index 6843538f9..8b4269a95 100644
--- a/src/ice.c
+++ b/src/ice.c
@@ -944,7 +944,7 @@ void AnimTask_Haze1(u8 taskId)
SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X);
SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y);
sub_80752A0(&animBg);
- LoadBgTiles(animBg.bgId, gWeatherFog1Tiles, 0x800, animBg.tilesOffset);
+ LoadBgTiles(animBg.bgId, gWeatherFogHorizontalTiles, 0x800, animBg.tilesOffset);
AnimLoadCompressedBgTilemap(animBg.bgId, gBattleAnimFogTilemap);
LoadPalette(&gUnknown_83C2CE0, animBg.paletteId * 16, 32);
if (IsContest())
@@ -1042,7 +1042,7 @@ void AnimTask_LoadMistTiles(u8 taskId)
SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X);
SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y);
sub_80752A0(&animBg);
- LoadBgTiles(animBg.bgId, gWeatherFog1Tiles, 0x800, animBg.tilesOffset);
+ LoadBgTiles(animBg.bgId, gWeatherFogHorizontalTiles, 0x800, animBg.tilesOffset);
AnimLoadCompressedBgTilemap(animBg.bgId, gBattleAnimFogTilemap);
LoadPalette(&gUnknown_83C2CE0, animBg.paletteId * 16, 32);
if (IsContest())
diff --git a/src/title_screen.c b/src/title_screen.c
index 0ff0a6b96..e1bcf5dd2 100644
--- a/src/title_screen.c
+++ b/src/title_screen.c
@@ -305,6 +305,22 @@ static const u16 gUnknown_LG_83BFA10[] = {
};
#endif
+static const u32 gUnknown_83BFBE4[] = INCBIN_U32("graphics/title_screen/unk_83BFBE4.bin.lz");
+static const u32 gUnknown_83C0408[] = INCBIN_U32("graphics/title_screen/unk_83C0408.bin.lz");
+static const u32 gUnknown_83C0C00[] = INCBIN_U32("graphics/title_screen/unk_83C0C00.bin.lz");
+static const u32 gUnknown_83C139C[] = INCBIN_U32("graphics/title_screen/unk_83C139C.bin.lz");
+static const u32 gUnknown_83C1BB8[] = INCBIN_U32("graphics/title_screen/unk_83C1BB8.bin.lz");
+static const u32 gUnknown_83C2380[] = INCBIN_U32("graphics/title_screen/unk_83C2380.bin.lz");
+
+static const u32 *const gUnknown_83C2BA4[] = {
+ gUnknown_83BFBE4,
+ gUnknown_83C0408,
+ gUnknown_83C0C00,
+ gUnknown_83C139C,
+ gUnknown_83C1BB8,
+ gUnknown_83C2380
+};
+
void CB2_InitTitleScreen(void)
{
switch (gMain.state)