diff --git a/include/overworld.h b/include/overworld.h index be7c33ff03..2180a87a9b 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -44,6 +44,12 @@ struct LinkPlayerObjectEvent u8 movementMode; }; +struct __attribute__((packed)) TimeBlendSettings { + u16 weight:9; + u16 time1:3; + u16 time0:3; +}; + // Exported RAM declarations extern struct WarpData gLastUsedWarp; extern struct LinkPlayerObjectEvent gLinkPlayerObjectEvents[4]; diff --git a/include/palette.h b/include/palette.h index 406f56b84a..a9e6cd7671 100644 --- a/include/palette.h +++ b/include/palette.h @@ -77,9 +77,9 @@ void BlendPalettes(u32 selectedPalettes, u8 coeff, u16 color); void BlendPalettesUnfaded(u32, u8, u16); void BlendPalettesGradually(u32 selectedPalettes, s8 delay, u8 coeff, u8 coeffTarget, u16 color, u8 priority, u8 id); void AveragePalettes(u16 *palette0, u16* palette1, u16* dest, u16 weight); -void TimeBlendPalette(u16 palOffset, u16 numEntries, u8 coeff, u16 blendColor); +void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor); void TintPalette_RGB_Copy(u16 palOffset, u16 numEntries, u8 coeff, u16 blendColor); -void TimeBlendPalettes(u32 palettes, u8 coeff, u16 color); +void TimeBlendPalettes(u32 palettes, u32 coeff, u32 blendColor); void TintPalette_GrayScale(u16 *palette, u16 count); void TintPalette_GrayScale2(u16 *palette, u16 count); void TintPalette_SepiaTone(u16 *palette, u16 count); diff --git a/src/overworld.c b/src/overworld.c index 0ac310e653..6c7196a515 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -186,7 +186,9 @@ static u16 (*sPlayerKeyInterceptCallback)(u32); static bool8 sReceivingFromLink; static u8 sRfuKeepAliveTimer; -u8 static gTimeOfDayState; +static u8 gTimeOfDayState; +static u8 timeCounter; +static struct TimeBlendSettings currentTimeBlend; // IWRAM common u16 *gBGTilemapBuffers1; @@ -1472,12 +1474,39 @@ static const struct TimeOfDayBlend sTimeOfDayBlendVars[] = }; u8 UpdateTimeOfDay(void) { + s8 hours, minutes; RtcCalcLocalTime(); - if (gLocalTime.hours >= 20 || gLocalTime.hours < 4) - return gTimeOfDay = TIME_OF_DAY_NIGHT; - else if (gLocalTime.hours >= 10 && gLocalTime.hours < 20) + hours = gLocalTime.hours; + minutes = gLocalTime.minutes; + if (hours >= 22 || hours < 4) { + currentTimeBlend.weight = 256; + return gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_NIGHT; + } else if (hours >= 4 && hours < 7) { // night->twilight + currentTimeBlend.time0 = TIME_OF_DAY_NIGHT; + currentTimeBlend.time1 = TIME_OF_DAY_TWILIGHT; + currentTimeBlend.weight = 256 - 256 * ((hours - 4) * 60 + minutes) / ((7-4)*60); return gTimeOfDay = TIME_OF_DAY_DAY; - return gTimeOfDay = TIME_OF_DAY_TWILIGHT; + } else if (hours >= 7 && hours < 10) { // twilight->day + currentTimeBlend.time0 = TIME_OF_DAY_TWILIGHT; + currentTimeBlend.time1 = TIME_OF_DAY_DAY; + currentTimeBlend.weight = 256 - 256 * ((hours - 7) * 60 + minutes) / ((10-7)*60); + return gTimeOfDay = TIME_OF_DAY_DAY; + } else if (hours >= 10 && hours < 18) { // day + currentTimeBlend.weight = 256; + return gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_DAY; + } else if (hours >= 18 && hours < 20) { // day->twilight + currentTimeBlend.time0 = TIME_OF_DAY_DAY; + currentTimeBlend.time1 = TIME_OF_DAY_TWILIGHT; + currentTimeBlend.weight = 256 - 256 * ((hours - 18) * 60 + minutes) / ((20-18)*60); + return gTimeOfDay = TIME_OF_DAY_TWILIGHT; + } else if (hours >= 20 && hours < 22) { // twilight->night + currentTimeBlend.time0 = TIME_OF_DAY_TWILIGHT; + currentTimeBlend.time1 = TIME_OF_DAY_NIGHT; + currentTimeBlend.weight = 256 - 256 * ((hours - 20) * 60 + minutes) / ((22-20)*60); + return gTimeOfDay = TIME_OF_DAY_NIGHT; + } else { // This should never occur + return TIME_OF_DAY_MAX; + } } static bool8 MapHasNaturalLight(u8 mapType) { // Weather a map type is naturally lit/outside @@ -1485,7 +1514,7 @@ static bool8 MapHasNaturalLight(u8 mapType) { // Weather a map type is naturally || mapType == MAP_TYPE_OCEAN_ROUTE; } -static bool8 FadePalettesWithTime(void) { +static bool8 FadePalettesWithTime(void) { // Only used to fade back in gTimeOfDayState = 0; gTimeOfDay = UpdateTimeOfDay(); if (MapHasNaturalLight(gMapHeader.mapType)) { @@ -1495,8 +1524,7 @@ static bool8 FadePalettesWithTime(void) { } void UpdatePalettesWithTime(u32 palettes) { - // Only blend if not transitioning between times and the map type allows - if (gTimeOfDayState == 0 && MapHasNaturalLight(gMapHeader.mapType)) { + if (MapHasNaturalLight(gMapHeader.mapType)) { u8 i, j; u16 tempPaletteBuffer[16]; for (i = 0; i < 16; i++) { @@ -1509,10 +1537,14 @@ void UpdatePalettesWithTime(u32 palettes) { return; for (i = 0; palettes; i++) { if (palettes & 1) { - TimeBlendPalette(i*16, 16, sTimeOfDayBlendVars[gTimeOfDay].coeff, sTimeOfDayBlendVars[gTimeOfDay].blendColor); + TimeBlendPalette(i*16, sTimeOfDayBlendVars[currentTimeBlend.time0].coeff, sTimeOfDayBlendVars[currentTimeBlend.time0].blendColor); + if (currentTimeBlend.weight == 256) { + palettes >>= 1; + continue; + } CpuFastCopy(&gPlttBufferFaded[i*16], tempPaletteBuffer, 32); - TimeBlendPalette(i*16, 16, sTimeOfDayBlendVars[TIME_OF_DAY_TWILIGHT].coeff, sTimeOfDayBlendVars[TIME_OF_DAY_TWILIGHT].blendColor); - AveragePalettes(tempPaletteBuffer, &gPlttBufferFaded[i*16], &gPlttBufferFaded[i*16], 256); + TimeBlendPalette(i*16, sTimeOfDayBlendVars[currentTimeBlend.time1].coeff, sTimeOfDayBlendVars[currentTimeBlend.time1].blendColor); + AveragePalettes(tempPaletteBuffer, &gPlttBufferFaded[i*16], &gPlttBufferFaded[i*16], currentTimeBlend.weight); } palettes >>= 1; } @@ -1535,6 +1567,19 @@ static void OverworldBasic(void) UpdatePaletteFade(); UpdateTilesetAnimations(); DoScheduledBgTilemapCopiesToVram(); + if (!(gPaletteFade.active || (timeCounter++ % 60))) { + struct TimeBlendSettings cachedBlend = { + .time0 = currentTimeBlend.time0, + .time1 = currentTimeBlend.time1, + .weight = currentTimeBlend.weight, + }; + timeCounter = 0; + UpdateTimeOfDay(); + if (cachedBlend.time0 != currentTimeBlend.time0 + || cachedBlend.time1 != currentTimeBlend.time1 + || cachedBlend.weight != currentTimeBlend.weight) + UpdatePalettesWithTime(0xFFFFFFFF); + } } // This CB2 is used when starting @@ -1545,9 +1590,7 @@ void CB2_OverworldBasic(void) void CB2_Overworld(void) { - u32 *debugPtr = (u32*) 0x0203de00; bool32 fading = (gPaletteFade.active != 0); - *debugPtr = (u32) &gTimeOfDay; if (fading) SetVBlankCallback(NULL); OverworldBasic(); diff --git a/src/palette.c b/src/palette.c index a285a48257..ff5f210606 100644 --- a/src/palette.c +++ b/src/palette.c @@ -496,13 +496,13 @@ static u8 UpdateTimeOfDayPaletteFade(void) if (gPaletteFade.yDec) { if (gPaletteFade.objPaletteToggle) { // sprite palettes if (gPaletteFade.y >= gPaletteFade.targetY || GetSpritePaletteTagByPaletteNum(paletteNum) & 0x8000) - TimeBlendPalette(paletteOffset, 16, gPaletteFade.y, gPaletteFade.blendColor); + TimeBlendPalette(paletteOffset, gPaletteFade.y, gPaletteFade.blendColor); // tile palettes } else if (gPaletteFade.y >= gPaletteFade.targetY || (paletteNum >= 13 && paletteNum <= 15)) { - TimeBlendPalette(paletteOffset, 16, gPaletteFade.y, gPaletteFade.blendColor); + TimeBlendPalette(paletteOffset, gPaletteFade.y, gPaletteFade.blendColor); } } else { - TimeBlendPalette(paletteOffset, 16, gPaletteFade.y, gPaletteFade.blendColor); + TimeBlendPalette(paletteOffset, gPaletteFade.y, gPaletteFade.blendColor); } } } @@ -1010,45 +1010,125 @@ void AveragePalettes(u16 *palette0, u16* palette1, u16* dest, u16 weight) { #define DEFAULT_LIGHT_COLOR 0x3f9f // Like BlendPalette, but ignores blendColor if the transparency high bit is set -void TimeBlendPalette(u16 palOffset, u16 numEntries, u8 coeff, u16 blendColor) { - u16 i; - u16 defaultBlendColor = DEFAULT_LIGHT_COLOR; - s8 r, g, b; - struct PlttData *data2 = (struct PlttData *)&blendColor; - struct PlttData *data3; - struct PlttData *blendData; - u16 altBlendIndices = 0; - for (i = 0; i < numEntries; i++) { - u16 index = i + palOffset; - struct PlttData *data1 = (struct PlttData *)&gPlttBufferUnfaded[index]; - if (i == 0) { - if (data1->unused_15) { // Color 0 is a bitmask for which colors to blend; color 15 is the alt blend color - gPlttBufferFaded[index] = gPlttBufferUnfaded[index]; - altBlendIndices = gPlttBufferUnfaded[index] << 1; // bit 0 specifies color 1, etc. - data3 = (struct PlttData *)&gPlttBufferUnfaded[index+15]; - if (!data3->unused_15) // use default blend color instead - data3 = (struct PlttData *)&defaultBlendColor; - } - continue; +// Optimization help by lucktyphlosion +void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor) { + s32 newR, newG, newB, defR, defG, defB; + u16 * palDataSrc = gPlttBufferUnfaded + palOffset; + u16 * palDataDst = gPlttBufferFaded + palOffset; + u32 defaultBlendColor = DEFAULT_LIGHT_COLOR; + u16 *palDataSrcEnd = palDataSrc + 16; + u16 altBlendIndices = *palDataDst++ = *palDataSrc++; // color 0 is copied through unchanged + u32 altBlendColor; + + coeff *= 2; + newR = (blendColor << 27) >> 27; + newG = (blendColor << 22) >> 27; + newB = (blendColor << 17) >> 27; + + if (altBlendIndices >> 15) { // High bit set; bitmask of which colors to alt-blend + // Note that bit 0 of altBlendIndices specifies color 1 + altBlendColor = palDataSrc[14]; // color 15 + if (altBlendColor >> 15) { // Set alternate blend color + defR = (altBlendColor << 27) >> 27; + defG = (altBlendColor << 22) >> 27; + defB = (altBlendColor << 17) >> 27; + } else { // Set default blend color + defR = (defaultBlendColor << 27) >> 27; + defG = (defaultBlendColor << 22) >> 27; + defB = (defaultBlendColor << 17) >> 27; } - r = data1->r; - g = data1->g; - b = data1->b; - blendData = (altBlendIndices && altBlendIndices & (1 << i)) ? data3 : data2; - gPlttBufferFaded[index] = RGB(r + (((blendData->r - r) * coeff) >> 4), - g + (((blendData->g - g) * coeff) >> 4), - b + (((blendData->b - b) * coeff) >> 4)); + } else { + altBlendIndices = 0; + } + while (palDataSrc != palDataSrcEnd) { + u32 palDataSrcColor = *palDataSrc; + s32 r = (palDataSrcColor << 27) >> 27; + s32 g = (palDataSrcColor << 22) >> 27; + s32 b = (palDataSrcColor << 17) >> 27; + + if (altBlendIndices & 1) { + *palDataDst = ((r + (((defR - r) * coeff) >> 5)) << 0) + | ((g + (((defG - g) * coeff) >> 5)) << 5) + | ((b + (((defB - b) * coeff) >> 5)) << 10); + } else { // Use provided blend color + *palDataDst = ((r + (((newR - r) * coeff) >> 5)) << 0) + | ((g + (((newG - g) * coeff) >> 5)) << 5) + | ((b + (((newB - b) * coeff) >> 5)) << 10); + } + palDataSrc++; + palDataDst++; + altBlendIndices >>= 1; } } -// Apply time effect to a series of palettes -void TimeBlendPalettes(u32 palettes, u8 coeff, u16 color) { - u16 paletteOffset; - for (paletteOffset = 0; palettes; paletteOffset += 16) { - if (palettes & 1) - TimeBlendPalette(paletteOffset, 16, coeff, color); +void TimeBlendPalettes(u32 palettes, u32 coeff, u32 blendColor) { + s32 newR, newG, newB, defR, defG, defB, altR, altG, altB; + u16 * palDataSrc; + u16 * palDataDst; + u32 defaultBlendColor = DEFAULT_LIGHT_COLOR; + + if (!palettes) + return; + + coeff *= 2; + newR = (blendColor << 27) >> 27; + newG = (blendColor << 22) >> 27; + newB = (blendColor << 17) >> 27; + defR = (defaultBlendColor << 27) >> 27; + defG = (defaultBlendColor << 22) >> 27; + defB = (defaultBlendColor << 17) >> 27; + palDataSrc = gPlttBufferUnfaded; + palDataDst = gPlttBufferFaded; + + do { + if (palettes & 1) { + u16 *palDataSrcEnd = palDataSrc + 16; + u16 altBlendIndices = *palDataDst++ = *palDataSrc++; // color 0 is copied through + u32 altBlendColor; + if (altBlendIndices >> 15) { // High bit set; bitmask of which colors to alt-blend + // Note that bit 0 of altBlendIndices specifies color 1 + altBlendColor = palDataSrc[14]; // color 15 + if (altBlendColor >> 15) { // Set alternate blend color + altR = (altBlendColor << 27) >> 27; + altG = (altBlendColor << 22) >> 27; + altB = (altBlendColor << 17) >> 27; + } else { + altBlendColor = 0; + } + } else { + altBlendIndices = 0; + } + while (palDataSrc != palDataSrcEnd) { + u32 palDataSrcColor = *palDataSrc; + s32 r = (palDataSrcColor << 27) >> 27; + s32 g = (palDataSrcColor << 22) >> 27; + s32 b = (palDataSrcColor << 17) >> 27; + + if (altBlendIndices & 1) { + if (altBlendColor) { // Use alternate blend color + *palDataDst = ((r + (((altR - r) * coeff) >> 5)) << 0) + | ((g + (((altG - g) * coeff) >> 5)) << 5) + | ((b + (((altB - b) * coeff) >> 5)) << 10); + } else { // Use default blend color + *palDataDst = ((r + (((defR - r) * coeff) >> 5)) << 0) + | ((g + (((defG - g) * coeff) >> 5)) << 5) + | ((b + (((defB - b) * coeff) >> 5)) << 10); + } + } else { // Use provided blend color + *palDataDst = ((r + (((newR - r) * coeff) >> 5)) << 0) + | ((g + (((newG - g) * coeff) >> 5)) << 5) + | ((b + (((newB - b) * coeff) >> 5)) << 10); + } + palDataSrc++; + palDataDst++; + altBlendIndices >>= 1; + } + } else { + palDataSrc += 16; + palDataDst += 16; + } palettes >>= 1; - } + } while (palettes); } void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u16 color)