pokeemmo/src/palette.c
2025-11-01 14:56:09 +01:00

1358 lines
38 KiB
C

#include "global.h"
#include "palette.h"
#include "util.h"
#include "decompress.h"
#include "field_weather.h"
#include "malloc.h"
#include "menu.h"
#include "gpu_regs.h"
#include "task.h"
#include "constants/field_weather.h"
#include "constants/rgb.h"
enum
{
NORMAL_FADE,
FAST_FADE,
HARDWARE_FADE,
TIME_OF_DAY_FADE,
};
static u32 UpdateNormalPaletteFade(void);
static void BeginFastPaletteFadeInternal(u32);
static u32 UpdateFastPaletteFade(void);
static u8 UpdateTimeOfDayPaletteFade(void);
static u32 UpdateHardwarePaletteFade(void);
static void UpdateBlendRegisters(void);
static bool32 IsSoftwarePaletteFadeFinishing(void);
static void Task_BlendPalettesGradually(u8 taskId);
// palette buffers require alignment with agbcc because
// unaligned word reads are issued in BlendPalette otherwise
ALIGNED(4) EWRAM_DATA u16 gPlttBufferUnfaded[PLTT_BUFFER_SIZE] = {0};
ALIGNED(4) EWRAM_DATA u16 gPlttBufferFaded[PLTT_BUFFER_SIZE] = {0};
EWRAM_DATA struct PaletteFadeControl gPaletteFade = {0};
static EWRAM_DATA u32 sPlttBufferTransferPending = 0;
static const u8 sRoundedDownGrayscaleMap[] = {
0, 0, 0, 0, 0,
5, 5, 5, 5, 5,
11, 11, 11, 11, 11,
16, 16, 16, 16, 16,
21, 21, 21, 21, 21,
27, 27, 27, 27, 27,
31, 31
};
void LoadPalette(const void *src, u32 offset, u32 size)
{
CpuCopy16(src, &gPlttBufferUnfaded[offset], size);
CpuCopy16(src, &gPlttBufferFaded[offset], size);
}
// Drop in replacement for LoadPalette, uses CpuFastCopy, size must be 0 % 32
void LoadPaletteFast(const void *src, u32 offset, u32 size)
{
if ((u32)src & 3) // In case palette is not 4 byte aligned
return LoadPalette(src, offset, size);
CpuFastCopy(src, &gPlttBufferUnfaded[offset], size);
// Copying from EWRAM->EWRAM is faster than ROM->EWRAM
CpuFastCopy(&gPlttBufferUnfaded[offset], &gPlttBufferFaded[offset], size);
}
void FillPalette(u32 value, u32 offset, u32 size)
{
CpuFill16(value, &gPlttBufferUnfaded[offset], size);
CpuFill16(value, &gPlttBufferFaded[offset], size);
}
void TransferPlttBuffer(void)
{
if (!gPaletteFade.bufferTransferDisabled)
{
void *src = gPlttBufferFaded;
void *dest = (void *)PLTT;
DmaCopy16(3, src, dest, PLTT_SIZE);
sPlttBufferTransferPending = FALSE;
if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
UpdateBlendRegisters();
}
}
u32 UpdatePaletteFade(void)
{
u32 result;
if (sPlttBufferTransferPending)
return PALETTE_FADE_STATUS_LOADING;
if (gPaletteFade.mode == NORMAL_FADE)
result = UpdateNormalPaletteFade();
else if (gPaletteFade.mode == FAST_FADE)
result = UpdateFastPaletteFade();
else if (gPaletteFade.mode == TIME_OF_DAY_FADE)
result = UpdateTimeOfDayPaletteFade();
else
result = UpdateHardwarePaletteFade();
sPlttBufferTransferPending = gPaletteFade.multipurpose1;
return result;
}
void ResetPaletteFade(void)
{
ResetPaletteFadeControl();
}
bool32 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, u32 blendColor)
{
u8 temp;
if (gPaletteFade.active)
{
return FALSE;
}
else
{
gPaletteFade.deltaY = 2;
if (delay < 0)
{
gPaletteFade.deltaY += (delay * -1);
delay = 0;
}
gPaletteFadeSelectedPalettes = selectedPalettes;
gPaletteFade.delayCounter = delay;
gPaletteFadeDelay = delay;
gPaletteFade.y = startY;
gPaletteFade.targetY = targetY;
gPaletteFade.blendColor = blendColor;
gPaletteFade.active = TRUE;
gPaletteFade.mode = NORMAL_FADE;
if (startY < targetY)
gPaletteFade.yDec = 0;
else
gPaletteFade.yDec = 1;
UpdatePaletteFade();
temp = gPaletteFade.bufferTransferDisabled;
gPaletteFade.bufferTransferDisabled = FALSE;
CpuCopy32(gPlttBufferFaded, (void *)PLTT, PLTT_SIZE);
sPlttBufferTransferPending = FALSE;
if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
UpdateBlendRegisters();
gPaletteFade.bufferTransferDisabled = temp;
return TRUE;
}
}
// Like normal palette fade but respects sprite/tile palettes immune to time of day fading
bool32 BeginTimeOfDayPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, struct BlendSettings *bld0, struct BlendSettings *bld1, u32 weight, u32 color)
{
u8 temp;
if (gPaletteFade.active)
return FALSE;
gPaletteFade.deltaY = 2;
if (delay < 0)
{
gPaletteFade.deltaY += (delay * -1);
delay = 0;
}
gPaletteFadeSelectedPalettes = selectedPalettes;
gPaletteFade.delayCounter = delay;
gPaletteFadeDelay = delay;
gPaletteFade.y = startY;
gPaletteFade.targetY = targetY;
gPaletteFade.active = 1;
gPaletteFade.mode = TIME_OF_DAY_FADE;
gPaletteFade.blendColor = color;
gPaletteFade.bld0 = bld0;
gPaletteFade.bld1 = bld1;
gPaletteFade.weight = weight;
if (startY < targetY)
gPaletteFade.yDec = 0;
else
gPaletteFade.yDec = 1;
UpdatePaletteFade();
temp = gPaletteFade.bufferTransferDisabled;
gPaletteFade.bufferTransferDisabled = 0;
CpuCopy32(gPlttBufferFaded, (void *)PLTT, PLTT_SIZE);
sPlttBufferTransferPending = 0;
if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
UpdateBlendRegisters();
gPaletteFade.bufferTransferDisabled = temp;
return TRUE;
}
void ResetPaletteFadeControl(void)
{
gPaletteFade.multipurpose1 = 0;
gPaletteFade.multipurpose2 = 0;
gPaletteFade.delayCounter = 0;
gPaletteFade.y = 0;
gPaletteFade.targetY = 0;
gPaletteFade.blendColor = 0;
gPaletteFade.active = FALSE;
gPaletteFade.multipurpose2 = 0; // assign same value twice
gPaletteFade.yDec = 0;
gPaletteFade.bufferTransferDisabled = FALSE;
gPaletteFade.shouldResetBlendRegisters = FALSE;
gPaletteFade.hardwareFadeFinishing = FALSE;
gPaletteFade.softwareFadeFinishing = FALSE;
gPaletteFade.softwareFadeFinishingCounter = 0;
gPaletteFade.objPaletteToggle = 0;
gPaletteFade.deltaY = 2;
}
// Like normal palette fade, but respects sprite/tile palettes immune to time of day fading
static u8 UpdateTimeOfDayPaletteFade(void)
{
u16 paletteOffset;
u16 selectedPalettes;
u16 timePalettes = 0; // palettes passed to the time-blender
u16 copyPalettes;
u16 *src;
u16 *dst;
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (IsSoftwarePaletteFadeFinishing())
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
if (!gPaletteFade.objPaletteToggle)
{
if (gPaletteFade.delayCounter < gPaletteFadeDelay)
{
gPaletteFade.delayCounter++;
return PALETTE_FADE_STATUS_DELAY;
}
gPaletteFade.delayCounter = 0;
}
paletteOffset = 0;
if (!gPaletteFade.objPaletteToggle)
{
selectedPalettes = gPaletteFadeSelectedPalettes;
}
else
{
selectedPalettes = gPaletteFadeSelectedPalettes >> 16;
paletteOffset = 256;
}
src = gPlttBufferUnfaded + paletteOffset;
dst = gPlttBufferFaded + paletteOffset;
// First pply TOD blend to relevant subset of palettes
if (gPaletteFade.objPaletteToggle) // Sprite palettes, don't blend those with tags
{
u32 i;
u32 j = 1;
for (i = 0; i < 16; i++, j <<= 1) // Mask out palettes that should not be light blended
{
if ((selectedPalettes & j) && !IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(i)))
timePalettes |= j;
}
}
else // tile palettes, don't blend [13, 15]
{
timePalettes = selectedPalettes & PALETTES_MAP;
}
TimeMixPalettes(timePalettes, src, dst, gPaletteFade.bld0, gPaletteFade.bld1, gPaletteFade.weight);
// palettes that were not blended above must be copied through
if ((copyPalettes = ~timePalettes))
{
u16 *src1 = src;
u16 *dst1 = dst;
while (copyPalettes)
{
if (copyPalettes & 1)
CpuFastCopy(src1, dst1, 32);
copyPalettes >>= 1;
src1 += 16;
dst1 += 16;
}
}
// Then, blend from faded->faded with native BlendPalettes
BlendPalettesFine(selectedPalettes, dst, dst, gPaletteFade.y, gPaletteFade.blendColor);
gPaletteFade.objPaletteToggle ^= 1;
if (!gPaletteFade.objPaletteToggle)
{
if ((gPaletteFade.yDec && gPaletteFade.y == 0) || (!gPaletteFade.yDec && gPaletteFade.y == gPaletteFade.targetY))
{
gPaletteFadeSelectedPalettes = 0;
gPaletteFade.softwareFadeFinishing = 1;
}
else
{
s8 val;
if (!gPaletteFade.yDec)
{
val = gPaletteFade.y + gPaletteFade.deltaY;
if (val > gPaletteFade.targetY)
val = gPaletteFade.targetY;
gPaletteFade.y = val;
}
else
{
val = gPaletteFade.y - gPaletteFade.deltaY;
if (val < 0)
val = 0;
gPaletteFade.y = val;
}
}
}
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return PALETTE_FADE_STATUS_ACTIVE;
}
static u32 UpdateNormalPaletteFade(void)
{
u16 paletteOffset;
u16 selectedPalettes;
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (IsSoftwarePaletteFadeFinishing())
{
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
else
{
if (!gPaletteFade.objPaletteToggle)
{
if (gPaletteFade.delayCounter < gPaletteFadeDelay)
{
gPaletteFade.delayCounter++;
return PALETTE_FADE_STATUS_DELAY;
}
gPaletteFade.delayCounter = 0;
}
paletteOffset = 0;
if (!gPaletteFade.objPaletteToggle)
{
selectedPalettes = gPaletteFadeSelectedPalettes;
}
else
{
selectedPalettes = gPaletteFadeSelectedPalettes >> 16;
paletteOffset = OBJ_PLTT_OFFSET;
}
while (selectedPalettes)
{
if (selectedPalettes & 1)
BlendPalette(
paletteOffset,
16,
gPaletteFade.y,
gPaletteFade.blendColor);
selectedPalettes >>= 1;
paletteOffset += 16;
}
gPaletteFade.objPaletteToggle ^= 1;
if (!gPaletteFade.objPaletteToggle)
{
if (gPaletteFade.y == gPaletteFade.targetY)
{
gPaletteFadeSelectedPalettes = 0;
gPaletteFade.softwareFadeFinishing = TRUE;
}
else
{
s8 val;
if (!gPaletteFade.yDec)
{
val = gPaletteFade.y;
val += gPaletteFade.deltaY;
if (val > gPaletteFade.targetY)
val = gPaletteFade.targetY;
gPaletteFade.y = val;
}
else
{
val = gPaletteFade.y;
val -= gPaletteFade.deltaY;
if (val < gPaletteFade.targetY)
val = gPaletteFade.targetY;
gPaletteFade.y = val;
}
}
}
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
}
void InvertPlttBuffer(u32 selectedPalettes)
{
u16 paletteOffset = 0;
while (selectedPalettes)
{
if (selectedPalettes & 1)
{
u32 i;
for (i = 0; i < 16; i++)
gPlttBufferFaded[paletteOffset + i] = ~gPlttBufferFaded[paletteOffset + i];
}
selectedPalettes >>= 1;
paletteOffset += 16;
}
}
void TintPlttBuffer(u32 selectedPalettes, s8 r, s8 g, s8 b)
{
u16 paletteOffset = 0;
while (selectedPalettes)
{
if (selectedPalettes & 1)
{
u32 i;
for (i = 0; i < 16; i++)
{
struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[paletteOffset + i];
data->r += r;
data->g += g;
data->b += b;
}
}
selectedPalettes >>= 1;
paletteOffset += 16;
}
}
void UnfadePlttBuffer(u32 selectedPalettes)
{
u16 paletteOffset = 0;
while (selectedPalettes)
{
if (selectedPalettes & 1)
{
u8 i;
for (i = 0; i < 16; i++)
gPlttBufferFaded[paletteOffset + i] = gPlttBufferUnfaded[paletteOffset + i];
}
selectedPalettes >>= 1;
paletteOffset += 16;
}
}
void BeginFastPaletteFade(u32 submode)
{
gPaletteFade.deltaY = 2;
BeginFastPaletteFadeInternal(submode);
}
static void BeginFastPaletteFadeInternal(u32 submode)
{
gPaletteFade.y = 31;
gPaletteFadeSubmode = submode & 0x3F;
gPaletteFade.active = TRUE;
gPaletteFade.mode = FAST_FADE;
if (submode == FAST_FADE_IN_FROM_BLACK)
CpuFill16(RGB_BLACK, gPlttBufferFaded, PLTT_SIZE);
if (submode == FAST_FADE_IN_FROM_WHITE)
CpuFill16(RGB_WHITE, gPlttBufferFaded, PLTT_SIZE);
UpdatePaletteFade();
}
static u32 UpdateFastPaletteFade(void)
{
u32 i;
u16 paletteOffsetStart;
u16 paletteOffsetEnd;
s8 r0;
s8 g0;
s8 b0;
s8 r;
s8 g;
s8 b;
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (IsSoftwarePaletteFadeFinishing())
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
if (gPaletteFade.objPaletteToggle)
{
paletteOffsetStart = OBJ_PLTT_OFFSET;
paletteOffsetEnd = PLTT_BUFFER_SIZE;
}
else
{
paletteOffsetStart = 0;
paletteOffsetEnd = OBJ_PLTT_OFFSET;
}
switch (gPaletteFadeSubmode)
{
case FAST_FADE_IN_FROM_WHITE:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *unfaded;
struct PlttData *faded;
unfaded = (struct PlttData *)&gPlttBufferUnfaded[i];
r0 = unfaded->r;
g0 = unfaded->g;
b0 = unfaded->b;
faded = (struct PlttData *)&gPlttBufferFaded[i];
r = faded->r - 2;
g = faded->g - 2;
b = faded->b - 2;
if (r < r0)
r = r0;
if (g < g0)
g = g0;
if (b < b0)
b = b0;
gPlttBufferFaded[i] = RGB(r, g, b);
}
break;
case FAST_FADE_OUT_TO_WHITE:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[i];
r = data->r + 2;
g = data->g + 2;
b = data->b + 2;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
gPlttBufferFaded[i] = RGB(r, g, b);
}
break;
case FAST_FADE_IN_FROM_BLACK:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *unfaded;
struct PlttData *faded;
unfaded = (struct PlttData *)&gPlttBufferUnfaded[i];
r0 = unfaded->r;
g0 = unfaded->g;
b0 = unfaded->b;
faded = (struct PlttData *)&gPlttBufferFaded[i];
r = faded->r + 2;
g = faded->g + 2;
b = faded->b + 2;
if (r > r0)
r = r0;
if (g > g0)
g = g0;
if (b > b0)
b = b0;
gPlttBufferFaded[i] = RGB(r, g, b);
}
break;
case FAST_FADE_OUT_TO_BLACK:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[i];
r = data->r - 2;
g = data->g - 2;
b = data->b - 2;
if (r < 0)
r = 0;
if (g < 0)
g = 0;
if (b < 0)
b = 0;
gPlttBufferFaded[i] = RGB(r, g, b);
}
}
gPaletteFade.objPaletteToggle ^= 1;
if (gPaletteFade.objPaletteToggle)
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
if (gPaletteFade.y - gPaletteFade.deltaY < 0)
gPaletteFade.y = 0;
else
gPaletteFade.y -= gPaletteFade.deltaY;
if (gPaletteFade.y == 0)
{
switch (gPaletteFadeSubmode)
{
case FAST_FADE_IN_FROM_WHITE:
case FAST_FADE_IN_FROM_BLACK:
CpuCopy32(gPlttBufferUnfaded, gPlttBufferFaded, PLTT_SIZE);
break;
case FAST_FADE_OUT_TO_WHITE:
CpuFill32(0xFFFFFFFF, gPlttBufferFaded, PLTT_SIZE);
break;
case FAST_FADE_OUT_TO_BLACK:
CpuFill32(0x00000000, gPlttBufferFaded, PLTT_SIZE);
break;
}
gPaletteFade.mode = NORMAL_FADE;
gPaletteFade.softwareFadeFinishing = TRUE;
}
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
void BeginHardwarePaletteFade(u32 blendCnt, u32 delay, u32 y, u32 targetY, u32 shouldResetBlendRegisters)
{
gPaletteFadeBlendCnt = blendCnt;
gPaletteFade.delayCounter = delay;
gPaletteFadeDelay = delay;
gPaletteFade.y = y;
gPaletteFade.targetY = targetY;
gPaletteFade.active = TRUE;
gPaletteFade.mode = HARDWARE_FADE;
gPaletteFade.shouldResetBlendRegisters = shouldResetBlendRegisters & 1;
gPaletteFade.hardwareFadeFinishing = FALSE;
if (y < targetY)
gPaletteFade.yDec = 0;
else
gPaletteFade.yDec = 1;
}
static u32 UpdateHardwarePaletteFade(void)
{
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (gPaletteFade.delayCounter < gPaletteFadeDelay)
{
gPaletteFade.delayCounter++;
return PALETTE_FADE_STATUS_DELAY;
}
gPaletteFade.delayCounter = 0;
if (!gPaletteFade.yDec)
{
gPaletteFade.y++;
if (gPaletteFade.y > gPaletteFade.targetY)
{
gPaletteFade.hardwareFadeFinishing++;
gPaletteFade.y--;
}
}
else
{
s32 y = gPaletteFade.y--;
if (y - 1 < gPaletteFade.targetY)
{
gPaletteFade.hardwareFadeFinishing++;
gPaletteFade.y++;
}
}
if (gPaletteFade.hardwareFadeFinishing)
{
if (gPaletteFade.shouldResetBlendRegisters)
{
// clear TGT1
gPaletteFadeBlendCnt &= ~0xFF;
gPaletteFade.y = 0;
}
gPaletteFade.shouldResetBlendRegisters = FALSE;
}
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
// Only called for hardware fades
static void UpdateBlendRegisters(void)
{
SetGpuReg(REG_OFFSET_BLDCNT, (u16)gPaletteFadeBlendCnt);
SetGpuReg(REG_OFFSET_BLDY, gPaletteFade.y);
// if TGT2 enabled, also adjust BLDALPHA and DISPCNT
if (((u16)gPaletteFadeBlendCnt) & BLDCNT_TGT2_ALL)
{
u16 bldAlpha = GetGpuReg(REG_OFFSET_BLDALPHA);
u8 tgt1 = BLDALPHA_TGT1(bldAlpha);
u8 tgt2 = BLDALPHA_TGT2(bldAlpha);
u8 mode = (gPaletteFadeBlendCnt & BLDCNT_EFFECT_EFF_MASK) == BLDCNT_EFFECT_LIGHTEN ? FADE_FROM_WHITE : FADE_FROM_BLACK;
if (!gPaletteFade.yDec)
mode++;
ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_FORCED_BLANK);
switch (mode)
{
case FADE_FROM_BLACK:
// increment each target until reaching weather's values
SetGpuReg(
REG_OFFSET_BLDALPHA,
BLDALPHA_BLEND(
min(++tgt1, gWeatherPtr->currBlendEVA),
min(++tgt2, gWeatherPtr->currBlendEVB)
)
);
break;
case FADE_TO_BLACK:
bldAlpha = BLDALPHA_TGT1(max(0, 16 - gPaletteFade.y));
SetGpuReg(
REG_OFFSET_BLDALPHA,
BLDALPHA_BLEND(min(tgt1, bldAlpha), min(tgt2, bldAlpha))
);
break;
// Not handled; blend sprites will pop in,
// but the effect coming from white looks okay
// case FADE_FROM_WHITE:
// break;
case FADE_TO_WHITE:
SetGpuReg(
REG_OFFSET_BLDALPHA,
BLDALPHA_BLEND(min(++tgt1, 31), min(++tgt2, 31))
);
// cause display to show white when finished
// (otherwise blend-mode sprites will still be visible)
if (gPaletteFade.hardwareFadeFinishing && gPaletteFade.y >= 16)
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_FORCED_BLANK);
break;
}
}
if (gPaletteFade.hardwareFadeFinishing)
{
gPaletteFade.hardwareFadeFinishing = FALSE;
gPaletteFade.mode = 0;
gPaletteFadeBlendCnt = 0;
gPaletteFade.y = 0;
gPaletteFade.active = FALSE;
}
}
static bool32 IsSoftwarePaletteFadeFinishing(void)
{
if (gPaletteFade.softwareFadeFinishing)
{
if (gPaletteFade.softwareFadeFinishingCounter == 4)
{
gPaletteFade.active = FALSE;
gPaletteFade.softwareFadeFinishing = FALSE;
gPaletteFade.softwareFadeFinishingCounter = 0;
}
else
{
gPaletteFade.softwareFadeFinishingCounter++;
}
return TRUE;
}
else
{
return FALSE;
}
}
// optimized based on lucktyphlosion's BlendPalettesFine
void BlendPalettesFine(u32 palettes, u16 *src, u16 *dst, u32 coeff, u32 color)
{
s32 newR, newG, newB;
if (!palettes)
return;
coeff *= 2;
newR = (color << 27) >> 27;
newG = (color << 22) >> 27;
newB = (color << 17) >> 27;
do
{
if (palettes & 1)
{
u16 *srcEnd = src + 16;
while (src != srcEnd) // Transparency is blended (for backdrop reasons)
{
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 16) >> 26;
*dst++ = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0)
| ((g + (((newG - g) * (s32)coeff) >> 5)) << 5)
| ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10);
src++;
}
}
else
{
src += 16;
dst += 16;
}
palettes >>= 1;
} while (palettes);
}
void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color)
{
BlendPalettesFine(selectedPalettes, gPlttBufferUnfaded, gPlttBufferFaded, coeff, color);
}
#define DEFAULT_LIGHT_COLOR RGB2GBA(248, 224, 120)
// Like BlendPalette, but ignores blendColor if the transparency high bit is set
// Optimization help by lucktyphlosion
void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor)
{
s32 newR, newG, newB, defR, defG, defB;
u16 *src = gPlttBufferUnfaded + palOffset;
u16 *dst = gPlttBufferFaded + palOffset;
u32 defaultBlendColor = DEFAULT_LIGHT_COLOR;
u16 *srcEnd = src + 16;
u32 altBlendColor = *dst++ = *src++; // color 0 is copied through unchanged
coeff *= 2;
newR = (blendColor << 27) >> 27;
newG = (blendColor << 22) >> 27;
newB = (blendColor << 17) >> 27;
if (altBlendColor >> 15) // Transparency high bit set; alt blend color
{
defR = (altBlendColor << 27) >> 27;
defG = (altBlendColor << 22) >> 27;
defB = (altBlendColor << 17) >> 27;
}
else
{
defR = (defaultBlendColor << 27) >> 27;
defG = (defaultBlendColor << 22) >> 27;
defB = (defaultBlendColor << 17) >> 27;
altBlendColor = 0;
}
while (src != srcEnd)
{
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 16) >> 26;
if (srcColor >> 15)
{
*dst = ((r + (((defR - r) * (s32)coeff) >> 5)) << 0)
| ((g + (((defG - g) * (s32)coeff) >> 5)) << 5)
| ((b + (((defB - (b & 31)) * (s32)coeff) >> 5)) << 10);
}
else // Use provided blend color
{
*dst = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0)
| ((g + (((newG - g) * (s32)coeff) >> 5)) << 5)
| ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10);
}
src++;
dst++;
}
}
// Blends a weighted average of two blend parameters
// Parameters can be either blended (as in BlendPalettes) or tinted (as in TintPaletteRGB_Copy)
void TimeMixPalettes(u32 palettes, u16 *src, u16 *dst, struct BlendSettings *blend0, struct BlendSettings *blend1, u16 weight0)
{
s32 r0, g0, b0, r1, g1, b1, defR, defG, defB, altR, altG, altB;
u32 color0, coeff0, color1, coeff1;
bool8 tint0, tint1;
u32 defaultColor = DEFAULT_LIGHT_COLOR;
if (!palettes)
return;
color0 = blend0->blendColor;
tint0 = blend0->isTint;
coeff0 = tint0 ? 16 : blend0->coeff * 2;
color1 = blend1->blendColor;
tint1 = blend1->isTint;
coeff1 = tint1 ? 16 : blend1->coeff * 2;
if (tint0)
{
r0 = (color0 << 24) >> 24;
g0 = (color0 << 16) >> 24;
b0 = (color0 << 8) >> 24;
}
else
{
r0 = (color0 << 27) >> 27;
g0 = (color0 << 22) >> 27;
b0 = (color0 << 17) >> 27;
}
if (tint1)
{
r1 = (color1 << 24) >> 24;
g1 = (color1 << 16) >> 24;
b1 = (color1 << 8) >> 24;
}
else
{
r1 = (color1 << 27) >> 27;
g1 = (color1 << 22) >> 27;
b1 = (color1 << 17) >> 27;
}
defR = (defaultColor << 27) >> 27;
defG = (defaultColor << 22) >> 27;
defB = (defaultColor << 17) >> 27;
do
{
if (palettes & 1)
{
u16 *srcEnd = src + 16;
u32 altBlendColor = *dst++ = *src++; // color 0 is copied through
if (altBlendColor >> 15) // Transparency high bit set; alt blend color
{
altR = (altBlendColor << 27) >> 27;
altG = (altBlendColor << 22) >> 27;
altB = (altBlendColor << 17) >> 27;
}
else
{
altBlendColor = 0;
}
while (src != srcEnd)
{
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 17) >> 27;
s32 r2, g2, b2;
if (srcColor >> 15)
{
if (altBlendColor)
{
// Use alternate blend color
r2 = r + (((altR - r) * (s32)coeff1) >> 5);
g2 = g + (((altG - g) * (s32)coeff1) >> 5);
b2 = b + (((altB - b) * (s32)coeff1) >> 5);
r = r + (((altR - r) * (s32)coeff0) >> 5);
g = g + (((altG - g) * (s32)coeff0) >> 5);
b = b + (((altB - b) * (s32)coeff0) >> 5);
}
else
{
// Use default blend color
r2 = r + (((defR - r) * (s32)coeff1) >> 5);
g2 = g + (((defG - g) * (s32)coeff1) >> 5);
b2 = b + (((defB - b) * (s32)coeff1) >> 5);
r = r + (((defR - r) * (s32)coeff0) >> 5);
g = g + (((defG - g) * (s32)coeff0) >> 5);
b = b + (((defB - b) * (s32)coeff0) >> 5);
}
}
else
{
// Use provided blend colors
if (!tint1)
{
// blend-based
r2 = (r + (((r1 - r) * (s32)coeff1) >> 5));
g2 = (g + (((g1 - g) * (s32)coeff1) >> 5));
b2 = (b + (((b1 - b) * (s32)coeff1) >> 5));
}
else
{
// tint-based
r2 = (u16)((r1 * r)) >> 8;
g2 = (u16)((g1 * g)) >> 8;
b2 = (u16)((b1 * b)) >> 8;
if (r2 > 31)
r2 = 31;
if (g2 > 31)
g2 = 31;
if (b2 > 31)
b2 = 31;
}
if (!tint0)
{
// blend-based
r = (r + (((r0 - r) * (s32)coeff0) >> 5));
g = (g + (((g0 - g) * (s32)coeff0) >> 5));
b = (b + (((b0 - b) * (s32)coeff0) >> 5));
}
else
{
// tint-based
r = (u16)((r0 * r)) >> 8;
g = (u16)((g0 * g)) >> 8;
b = (u16)((b0 * b)) >> 8;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
}
}
r = r2 + (((r - r2) * (s32)weight0) >> 8);
g = g2 + (((g - g2) * (s32)weight0) >> 8);
b = b2 + (((b - b2) * (s32)weight0) >> 8);
*dst++ = RGB2(r, g, b);
// *dst++ = RGB2(r, g, b) | (srcColor >> 15) << 15;
src++;
}
}
else
{
src += 16;
dst += 16;
}
palettes >>= 1;
} while (palettes);
}
// Apply weighted average to palettes, preserving high bits of dst throughout
void AvgPaletteWeighted(u16 *src0, u16 *src1, u16 *dst, u16 weight0)
{
u16 *srcEnd = src0 + 16;
src0++;
src1++;
dst++; // leave dst transparency unchanged
while (src0 != srcEnd)
{
u32 src0Color = *src0++;
s32 r0 = (src0Color << 27) >> 27;
s32 g0 = (src0Color << 22) >> 27;
s32 b0 = (src0Color << 17) >> 27;
u32 src1Color = *src1++;
s32 r1 = (src1Color << 27) >> 27;
s32 g1 = (src1Color << 22) >> 27;
s32 b1 = (src1Color << 17) >> 27;
// Average and bitwise-OR
r0 = r1 + (((r0 - r1) * weight0) >> 8);
g0 = g1 + (((g0 - g1) * weight0) >> 8);
b0 = b1 + (((b0 - b1) * weight0) >> 8);
*dst = (*dst & RGB_ALPHA) | RGB2(r0, g0, b0); // preserve high bit of dst
dst++;
}
}
void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u32 color)
{
void *src = gPlttBufferUnfaded;
void *dest = gPlttBufferFaded;
DmaCopy32(3, src, dest, PLTT_SIZE);
BlendPalettes(selectedPalettes, coeff, color);
}
void TintPalette_GrayScale(u16 *palette, u32 count)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
*palette++ = RGB2(gray, gray, gray);
}
}
void TintPalette_GrayScale2(u16 *palette, u32 count)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
if (gray > 31)
gray = 31;
gray = sRoundedDownGrayscaleMap[gray];
*palette++ = RGB2(gray, gray, gray);
}
}
void TintPalette_SepiaTone(u16 *palette, u32 count)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
r = (u16)((Q_8_8(1.2) * gray)) >> 8;
g = (u16)((Q_8_8(1.0) * gray)) >> 8;
b = (u16)((Q_8_8(0.94) * gray)) >> 8;
if (r > 31)
r = 31;
*palette++ = RGB2(r, g, b);
}
}
void TintPalette_CustomTone(u16 *palette, u32 count, u16 rTone, u16 gTone, u16 bTone)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
r = (u16)((rTone * gray)) >> 8;
g = (u16)((gTone * gray)) >> 8;
b = (u16)((bTone * gray)) >> 8;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
*palette++ = RGB2(r, g, b);
}
}
// Tints from Unfaded to Faded, using a 15-bit GBA color
void TintPalette_RGB_Copy(u16 palOffset, u32 blendColor)
{
s32 newR, newG, newB, rTone = 0, gTone = 0, bTone = 0;
u16 *src = gPlttBufferUnfaded + palOffset;
u16 *dst = gPlttBufferFaded + palOffset;
u32 defaultBlendColor = DEFAULT_LIGHT_COLOR;
u16 *srcEnd = src + 16;
u16 altBlendIndices = *dst++ = *src++; // color 0 is copied through unchanged
u32 altBlendColor;
newR = ((blendColor << 27) >> 27) << 3;
newG = ((blendColor << 22) >> 27) << 3;
newB = ((blendColor << 17) >> 27) << 3;
if (altBlendIndices >> 15) // High bit set; bitmask of which colors to alt-blend
{
// Note that bit 0 of altBlendIndices specifies color 1
altBlendColor = src[14]; // color 15
if (altBlendColor >> 15)
{
// Set alternate blend color
rTone = ((altBlendColor << 27) >> 27) << 3;
gTone = ((altBlendColor << 22) >> 27) << 3;
bTone = ((altBlendColor << 17) >> 27) << 3;
}
else
{
// Set default blend color
rTone = ((defaultBlendColor << 27) >> 27) << 3;
gTone = ((defaultBlendColor << 22) >> 27) << 3;
bTone = ((defaultBlendColor << 17) >> 27) << 3;
}
}
else
{
altBlendIndices = 0;
}
while (src != srcEnd)
{
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 17) >> 27;
if (altBlendIndices & 1)
{
r = (u16)((rTone * r)) >> 8;
g = (u16)((gTone * g)) >> 8;
b = (u16)((bTone * b)) >> 8;
}
else
{
// Use provided blend color
r = (u16)((newR * r)) >> 8;
g = (u16)((newG * g)) >> 8;
b = (u16)((newB * b)) >> 8;
}
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
src++;
*dst++ = RGB2(r, g, b);
altBlendIndices >>= 1;
}
}
#undef DEFAULT_LIGHT_COLOR
#define tCoeff data[0]
#define tCoeffTarget data[1]
#define tCoeffDelta data[2]
#define tDelay data[3]
#define tDelayTimer data[4]
#define tPalettes 5 // data[5] and data[6], set/get via Set/GetWordTaskArg
#define tColor data[7]
#define tId data[8]
// Blend the selected palettes in a series of steps toward or away from the color.
// Only used by the Groudon/Kyogre fight scene to flash the screen for lightning.
// One call is used to fade the bg from white, while another fades the duo from black
void BlendPalettesGradually(u32 selectedPalettes, s8 delay, u8 coeff, u8 coeffTarget, u16 color, u8 priority, u8 id)
{
u8 taskId;
taskId = CreateTask(Task_BlendPalettesGradually, priority);
gTasks[taskId].tCoeff = coeff;
gTasks[taskId].tCoeffTarget = coeffTarget;
if (delay >= 0)
{
gTasks[taskId].tDelay = delay;
gTasks[taskId].tCoeffDelta = 1;
}
else
{
gTasks[taskId].tDelay = 0;
gTasks[taskId].tCoeffDelta = -delay + 1;
}
if (coeffTarget < coeff)
gTasks[taskId].tCoeffDelta *= -1;
SetWordTaskArg(taskId, tPalettes, selectedPalettes);
gTasks[taskId].tColor = color;
gTasks[taskId].tId = id;
gTasks[taskId].func(taskId);
}
static bool32 UNUSED IsBlendPalettesGraduallyTaskActive(u8 id)
{
int i;
for (i = 0; i < NUM_TASKS; i++)
if ((gTasks[i].isActive == TRUE)
&& (gTasks[i].func == Task_BlendPalettesGradually)
&& (gTasks[i].tId == id))
return TRUE;
return FALSE;
}
static void UNUSED DestroyBlendPalettesGraduallyTask(void)
{
u8 taskId;
while (1)
{
taskId = FindTaskIdByFunc(Task_BlendPalettesGradually);
if (taskId == TASK_NONE)
break;
DestroyTask(taskId);
}
}
static void Task_BlendPalettesGradually(u8 taskId)
{
u32 palettes;
s16 *data;
s16 target;
data = gTasks[taskId].data;
palettes = GetWordTaskArg(taskId, tPalettes);
if (++tDelayTimer > tDelay)
{
tDelayTimer = 0;
BlendPalettes(palettes, tCoeff, tColor);
target = tCoeffTarget;
if (tCoeff == target)
{
DestroyTask(taskId);
}
else
{
tCoeff += tCoeffDelta;
if (tCoeffDelta >= 0)
{
if (tCoeff < target)
return;
}
else if (tCoeff > target)
{
return;
}
tCoeff = target;
}
}
}