CCCPaste

pokeemerald Smooth optimized palettes

#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;
    }
}