#include "global.h"
#include "palette.h"
#include "util.h"
#include "decompress.h"
#include "gpu_regs.h"
#include "task.h"
#include "constants/rgb.h"
// include/palette.h
struct PaletteFadeControl
{
/* 0x00 */ u32 multipurpose1;
/* 0x04 */ u8 delayCounter:6;
/* 0x04 */ u16 mode:2;
/* 0x05 */ u16 deltaY:6; // rate of change of blend coefficient
/* 0x05 */ bool16 active:1;
/* 0x05 */ bool16 yDec:1; // whether blend coefficient is decreasing
/* 0x06 */ u16 targetY:6; // target blend coefficient
/* 0x06 */ bool16 bufferTransferDisabled:1;
/* 0x06 */ bool16 shouldResetBlendRegisters:1;
/* 0x07 */ u16 multipurpose2:6;
/* 0x07 */ bool16 hardwareFadeFinishing:1;
/* 0x07 */ bool16 objPaletteToggle:1;
/* 0x08 */ u16 blendColor:15;
/* 0x08 */ u8 yChanged:1;
/* 0x09 */ u16 softwareFadeFinishingCounter:3;
/* 0x09 */ u16 doEndDelay:1;
/* 0x0a */ u16 y; // blend coefficient
/* 0x0c */ u16 denominator;
};
// src/palette.c
static bool8 BeginNormalPaletteFadeInternal(u32 selectedPalettes, u8 startY, u8 targetY, u16 blendColor, u32 denominator, u32 doEndDelay, u32 dontCopy);
enum
{
NORMAL_FADE,
FAST_FADE,
HARDWARE_FADE,
};
bool8 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, u16 blendColor)
{
// all this is to make fades more smooth
// while keeping the same delay as vanilla
// in case some code relies on timing
u8 diff;
u16 denominator;
if (startY < targetY) {
diff = targetY - startY;
} else {
diff = startY - targetY;
}
if (delay >= 0) {
if (delay > 63) {
delay = 63;
}
denominator = ((diff + 1)/2) * (delay + 2) + 1;
} else {
u8 i;
u8 deltaY;
s8 y;
if (delay < -14) {
delay = -14;
}
deltaY = 2 + (delay * -1);
y = startY;
if (y < targetY) {
for (i = 0; y < targetY; i++) {
y += deltaY;
}
} else {
for (i = 0; y > targetY; i++) {
y -= deltaY;
}
}
denominator = i * 2 + 1;
}
return BeginNormalPaletteFadeInternal(selectedPalettes, startY * 2, targetY * 2, blendColor, denominator, TRUE, FALSE);
}
static bool8 BeginNormalPaletteFadeForDuration_Common(u32 selectedPalettes, u16 fadeDuration, u8 startY, u8 targetY, u16 blendColor, u32 doEndDelay, bool32 dontCopy);
bool8 BeginNormalPaletteFadeForDuration_DontImmediatelyCopy(u32 selectedPalettes, u16 fadeDuration, u8 startY, u8 targetY, u16 blendColor, u32 doEndDelay)
{
return BeginNormalPaletteFadeForDuration_Common(selectedPalettes, fadeDuration, startY, targetY, blendColor, doEndDelay, TRUE);
}
bool8 BeginNormalPaletteFadeForDuration(u32 selectedPalettes, u16 fadeDuration, u8 startY, u8 targetY, u16 blendColor, u32 doEndDelay)
{
return BeginNormalPaletteFadeForDuration_Common(selectedPalettes, fadeDuration, startY, targetY, blendColor, doEndDelay, FALSE);
}
static bool8 BeginNormalPaletteFadeForDuration_Common(u32 selectedPalettes, u16 fadeDuration, u8 startY, u8 targetY, u16 blendColor, u32 doEndDelay, bool32 dontCopy)
{
if (fadeDuration > 1) {
fadeDuration--;
} else {
fadeDuration = 1;
}
return BeginNormalPaletteFadeInternal(selectedPalettes, startY, targetY, blendColor, fadeDuration, doEndDelay, dontCopy);
}
static bool8 BeginNormalPaletteFadeInternal(u32 selectedPalettes, u8 startY, u8 targetY, u16 blendColor, u32 denominator, u32 doEndDelay, u32 dontCopy)
{
u8 temp;
u16 color = blendColor;
if (gPaletteFade.active)
{
return FALSE;
}
else
{
gPaletteFade_selectedPalettes = selectedPalettes;
gPaletteFade.y = startY * denominator;
gPaletteFade.targetY = targetY;
gPaletteFade.denominator = denominator;
gPaletteFade.blendColor = color;
gPaletteFade.active = TRUE;
gPaletteFade.mode = NORMAL_FADE;
gPaletteFade.objPaletteToggle = 0;
gPaletteFade.yChanged = TRUE;
gPaletteFade.doEndDelay = doEndDelay;
if (startY < targetY) {
gPaletteFade.deltaY = targetY - startY;
gPaletteFade.yDec = 0;
} else {
gPaletteFade.deltaY = startY - targetY;
gPaletteFade.yDec = 1;
}
UpdatePaletteFade();
temp = gPaletteFade.bufferTransferDisabled;
gPaletteFade.bufferTransferDisabled = TRUE;
if (!dontCopy) {
CpuFastCopy(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 = 0;
gPaletteFade.multipurpose2 = 0; // assign same value twice
gPaletteFade.yDec = 0;
gPaletteFade.bufferTransferDisabled = 0;
gPaletteFade.shouldResetBlendRegisters = 0;
gPaletteFade.hardwareFadeFinishing = 0;
gPaletteFade.softwareFadeFinishingCounter = 0;
gPaletteFade.objPaletteToggle = 0;
gPaletteFade.deltaY = 2;
}
static u8 UpdateNormalPaletteFade(void)
{
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (IsSoftwarePaletteFadeFinishing())
{
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
else
{
u16 targetY = gPaletteFade.targetY * gPaletteFade.denominator;
u16 yWholeValue = gPaletteFade.y / gPaletteFade.denominator;
if (gPaletteFade.yChanged) {
BlendPalettesFine(gPaletteFade_selectedPalettes, yWholeValue, gPaletteFade.blendColor);
}
if (gPaletteFade.y == targetY)
{
gPaletteFade_selectedPalettes = 0;
gPaletteFade.softwareFadeFinishingCounter = 1;
if (!gPaletteFade.doEndDelay) {
gPaletteFade.active = 0;
gPaletteFade.softwareFadeFinishingCounter = 0;
}
}
else
{
u16 newY;
if (!gPaletteFade.yDec)
{
newY = gPaletteFade.y + gPaletteFade.deltaY;
if (newY > targetY) {
newY = targetY;
}
}
else
{
newY = gPaletteFade.y - gPaletteFade.deltaY;
if (newY < targetY) {
newY = targetY;
}
}
gPaletteFade.yChanged = (newY / gPaletteFade.denominator) != yWholeValue;
gPaletteFade.y = newY;
}
// 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;
}
}
static bool8 IsSoftwarePaletteFadeFinishing(void)
{
if (gPaletteFade.softwareFadeFinishingCounter > 0)
{
if (!gPaletteFade.doEndDelay || gPaletteFade.softwareFadeFinishingCounter == 5)
{
gPaletteFade.active = 0;
gPaletteFade.softwareFadeFinishingCounter = 0;
}
else
{
gPaletteFade.softwareFadeFinishingCounter++;
}
return TRUE;
}
else
{
return FALSE;
}
}