diff options
author | PikalaxALT <PikalaxALT@users.noreply.github.com> | 2020-03-17 08:53:24 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-17 08:53:24 -0400 |
commit | d52f2aa5712d042f344409d8925ed80d7109706e (patch) | |
tree | bd32fd38e0409fa6f899b6fbc02433bbccb3a237 /src | |
parent | f8915246a77a4453135ac3a4e6a61c098d557451 (diff) | |
parent | 8a64f8b431b0d68453f8d4302474926adf407bb1 (diff) |
Merge pull request #305 from PikalaxALT/field_weather
Field weather
Diffstat (limited to 'src')
-rw-r--r-- | src/credits.c | 4 | ||||
-rw-r--r-- | src/field_weather.c | 1169 | ||||
-rw-r--r-- | src/field_weather_effects.c | 2320 | ||||
-rw-r--r-- | src/field_weather_util.c | 105 | ||||
-rw-r--r-- | src/ice.c | 4 | ||||
-rw-r--r-- | src/title_screen.c | 16 |
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); +} @@ -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) |