#include "common.h"
#include "sprite.h"
#include "effects.h"
#include "battle/battle.h"
#include "sprite/npc/BattleWatt.h"

enum StandardPalettes {
    STANDARD_PAL_POISON     = 1,
    STANDARD_PAL_DIZZY      = 2,
    STANDARD_PAL_STATIC     = 3,
};

// lerp from A to B as alpha does from 0 to 255
#define LERP_COMPONENT(a, b, alpha) ((a) * (255 - (alpha)) + (b) * (alpha)) / 255;

#define PAL_ANIM_END 0xFF

enum PalSwapState {
    PAL_SWAP_HOLD_A     = 0,
    PAL_SWAP_A_TO_B     = 1,
    PAL_SWAP_HOLD_B     = 2,
    PAL_SWAP_B_TO_A     = 3,
};

// palette types for static palette animation
enum {
    STATIC_DEFAULT   = 0,
    STATIC_BRIGHT    = 1,
    STATIC_DARK      = 2,
};

// animated palettes for electrified sprites
// each pair gives { mode, duration }
u8 StaticPalettesAnim[] = {
    STATIC_DEFAULT,  32,
    STATIC_BRIGHT,    4,
    STATIC_DARK,      2,
    STATIC_DEFAULT,  16,
    STATIC_BRIGHT,    2,
    STATIC_DEFAULT,  64,
    STATIC_BRIGHT,    2,
    STATIC_DARK,      2,
    STATIC_DEFAULT,  28,
    STATIC_BRIGHT,    2,
    STATIC_DEFAULT,  18,
    STATIC_BRIGHT,    4,
    STATIC_DEFAULT,  16,
    STATIC_BRIGHT,    2,
    STATIC_DEFAULT,  80,
    STATIC_BRIGHT,    2,
    STATIC_DEFAULT,  16,
    STATIC_DARK,      2,
    STATIC_BRIGHT,    2,
    STATIC_DEFAULT,  32,
    STATIC_BRIGHT,    2,
    STATIC_DEFAULT,  14,
    STATIC_BRIGHT,    2,
    STATIC_DARK,      2,
    PAL_ANIM_END
};

s16 FearPaletteAnimXOffsets[] = { -2, 2, 0, 0, -2, 2, 0, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, PAL_ANIM_END };

s16 ParalyzePaletteAnimXOffsets[] = { -2, 2, 0, 0, -2, 2, 0, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, PAL_ANIM_END };

// palette types for watt palette animations
enum {
    WATT_DEFAULT   = 0,
    WATT_BRIGHTEST = 1,
    WATT_BRIGHTER  = 2,
};

// animated palettes for Watt
// each pair gives { mode, duration }
u8 bWattIdlePalettesAnim[] = {
    WATT_BRIGHTEST,   2,
    WATT_DEFAULT,    52,
    WATT_BRIGHTEST,   4,
    WATT_DEFAULT,    54,
    WATT_DEFAULT,    54,
    WATT_BRIGHTEST,   2,
    WATT_DEFAULT,    28,
    WATT_BRIGHTEST,   2,
    WATT_DEFAULT,     6,
    WATT_BRIGHTEST,   2,
    WATT_DEFAULT,    44,
    WATT_BRIGHTEST,   2,
    WATT_DEFAULT,    44,
    PAL_ANIM_END
};

u8 WattAttackPalettesAnim[] = {
    WATT_BRIGHTEST,   2,
    WATT_DEFAULT,    10,
    WATT_BRIGHTER,    4,
    WATT_DEFAULT,    14,
    WATT_BRIGHTEST,   2,
    WATT_DEFAULT,    10,
    WATT_BRIGHTER,    4,
    WATT_DEFAULT,     4,
    PAL_ANIM_END
};

s16 SparkleSpawnIntervals[] = { -1, 15, 10, 7, 5, 3, 2, 1 };

void update_player_actor_shadow(void);
void appendGfx_npc_actor(s32 isPartner, s32 actorIndex);

void create_status_chill_out(s32 iconID);
void enable_status_static(s32 iconID);
void enable_status_chill_out(s32 iconID);
void enable_status_debuff(s16);
void enable_status_transparent(s16);
void enable_status_icon_boost_jump(s32 iconID);
void enable_status_icon_boost_hammer(s32 iconID);
s32 get_npc_anim_for_status(u32*, s32);
void set_actor_pal_adjustment(Actor* actor, s32 arg1);
void create_status_icon_boost_hammer(s32 iconID);
void create_status_icon_boost_jump(s32 iconID);
void create_status_icon_peril(s32 iconID);
void create_status_icon_danger(s32 iconID);
void set_status_icons_offset(s32 iconID, s32 offsetY, s32 arg2);
void set_status_icons_properties(s32 iconID, f32 x, f32 y, f32 z, s32 arg, s32 arg2, s32 radius, s32 offsetY);

void appendGfx_npc_actor_reflection(s32, Actor*);
void make_flash_palettes(ActorPart*);
void func_unkA_draw_npc(ActorPart*, s32, Matrix4f);
void func_unkB_draw_npc(ActorPart*, s32, Matrix4f);
void func_unkA_draw_player(ActorPart*, s32, Matrix4f);
void func_unkB_draw_player(ActorPart*, s32, Matrix4f);
void part_glow_on(b32 arg0, ActorPart* part, s32 yaw, b32 arg3);
void part_flash_on(b32 arg0, ActorPart* part, s32 yaw, b32 arg3);

void add_part_decor_none(ActorPart*, s32);
void add_part_decor_golden_flames(ActorPart*, s32);
void add_part_decor_sweat(ActorPart*, s32);
void add_part_decor_seeing_stars(ActorPart*, s32);
void add_part_decor_red_flames(ActorPart*, s32);
void add_part_decor_smoky_trail(ActorPart*, s32);
void add_part_decor_fiery_trail(ActorPart*, s32);
void add_part_decor_whirlwind(ActorPart*, s32);
void add_part_decor_steam(ActorPart*, s32);
void add_part_decor_sparkles(ActorPart*, s32);
void add_part_decor_bowser_aura(ActorPart*, s32);
void add_part_decor_radiating_stars(ActorPart*, s32);

void render_without_adjusted_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_sleep_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_static_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_fear_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_poison_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_paralyze_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_berserk_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_watt_idle_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_watt_attack_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
void render_with_player_debuff_palettes(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation, s32 isPoison);
void render_with_pal_blending(b32 arg0, ActorPart* part, s32 yaw, s32 arg3, Matrix4f mtx, s32 skipAnimation);
void render_with_palset_blending(b32 arg0, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation);
s32 update_part_glow(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection);
s32 update_part_flash(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection);
s32 get_player_anim_for_status(s32 animID);
void clear_part_flash_mode(ActorPart* part);

// thresholds slightly lower than in GetDamageIntensity
s32 get_flash_damage_intensity(ActorPart* part) {
    s32 ret;

    if (gBattleStatus.lastAttackDamage < 3) {
        ret = DAMAGE_INTENSITY_LIGHT;
    } else if (gBattleStatus.lastAttackDamage < 5) {
        ret = DAMAGE_INTENSITY_MEDIUM;
    } else if (gBattleStatus.lastAttackDamage < 9) {
        ret = DAMAGE_INTENSITY_HEAVY;
    } else {
        ret = DAMAGE_INTENSITY_EXTREME;
    }

    return ret;
}

void mtx_mirror_y(Matrix4f mtx) {
    guMtxIdentF(mtx);
    mtx[0][0] = 1.0f;
    mtx[1][1] = -1.0f;
    mtx[2][2] = 1.0f;
    mtx[3][3] = 1.0f;
}

void enable_actor_blur(Actor* actor) {
    ActorPart* partsTable = actor->partsTable;
    DecorationTable* decorations = partsTable->decorationTable;
    s32 i, j;
    s32 numParts;

    decorations->blurDisableDelay = 0;
    decorations->blurEnableCount++;
    actor->flags |= ACTOR_FLAG_BLUR_ENABLED;
    partsTable = actor->partsTable;
    numParts = actor->numParts;

    for (i = 0; i < numParts; i++) {
        if (partsTable->idleAnimations != NULL && !(partsTable->flags & ACTOR_PART_FLAG_NO_DECORATIONS)) {
            decorations = partsTable->decorationTable;
            decorations->blurUnused = 0;
            decorations->blurBufferPos = 0;
            for (j = 0; j < ARRAY_COUNT(decorations->posX); j++) {
                decorations->posX[j] = partsTable->curPos.x;
                decorations->posY[j] = partsTable->curPos.y;
                decorations->posZ[j] = partsTable->curPos.z;
                decorations->yaw[j] = actor->yaw;
                decorations->rotPivotOffsetX[j] = (s32)(actor->rotPivotOffset.x * actor->scalingFactor);
                decorations->rotPivotOffsetY[j] = (s32)(actor->rotPivotOffset.y * actor->scalingFactor);

                decorations->rotX[j] = clamp_angle(actor->rot.x) * 0.5f;
                decorations->rotY[j] = clamp_angle(actor->rot.y) * 0.5f;
                decorations->rotZ[j] = clamp_angle(actor->rot.z) * 0.5f;
            }
        }
        partsTable = partsTable->nextPart;
    }
}

void disable_actor_blur(Actor* actor) {
    ActorPart* actorPart = actor->partsTable;

    if ((actorPart->idleAnimations != NULL) && !(actorPart->flags & ACTOR_PART_FLAG_NO_DECORATIONS)) {
        DecorationTable* decorations = actorPart->decorationTable;

        if (decorations->blurEnableCount != 0) {
            decorations->blurEnableCount--;
            if (decorations->blurEnableCount == 0) {
                decorations->blurDisableDelay = 20;
            }
        }
    }
}

void reset_actor_blur(Actor* actor) {
    ActorPart* actorPart = actor->partsTable;

    if ((actorPart->idleAnimations != NULL) && !(actorPart->flags & ACTOR_PART_FLAG_NO_DECORATIONS)) {
        DecorationTable* decorations = actorPart->decorationTable;

        if (decorations->blurEnableCount != 0) {
            decorations->blurEnableCount--;
            if (decorations->blurEnableCount == 0) {
                actor->flags &= ~ACTOR_FLAG_BLUR_ENABLED;
                decorations->blurDisableDelay = 1;
            }
        }
    }
}

void force_disable_actor_blur(Actor* actor) {
    ActorPart* actorPart = actor->partsTable;

    if (actorPart->idleAnimations != NULL && !(actorPart->flags & ACTOR_PART_FLAG_NO_DECORATIONS)) {
        DecorationTable* decorations = actorPart->decorationTable;

        decorations->blurEnableCount = 0;
        decorations->blurDisableDelay = 20;
    }
}

void enable_partner_blur(void) {
    enable_actor_blur(gBattleStatus.partnerActor);
}

void disable_partner_blur(void) {
    disable_actor_blur(gBattleStatus.partnerActor);
}

void reset_partner_blur(void) {
    reset_actor_blur(gBattleStatus.partnerActor);
}

void force_disable_partner_blur(void) {
    force_disable_actor_blur(gBattleStatus.partnerActor);
}

void enable_player_blur(void) {
    Actor* playerActor = gBattleStatus.playerActor;
    ActorPart* partsTable = playerActor->partsTable;
    DecorationTable* decorations = partsTable->decorationTable;
    s32 i;

    decorations->blurDisableDelay = 0;
    decorations->blurEnableCount++;
    playerActor->flags |= ACTOR_FLAG_BLUR_ENABLED;
    decorations->blurUnused = 0;
    decorations->blurBufferPos = 0;

    for (i = 0; i < ARRAY_COUNT(decorations->posX); i++) {
        decorations->posX[i] = partsTable->curPos.x;
        decorations->posY[i] = partsTable->curPos.y;
        decorations->posZ[i] = partsTable->curPos.z;
        decorations->yaw[i] = playerActor->yaw;
        decorations->rotPivotOffsetX[i] = playerActor->rotPivotOffset.x * playerActor->scalingFactor;
        decorations->rotPivotOffsetY[i] = playerActor->rotPivotOffset.y * playerActor->scalingFactor;

        decorations->rotX[i] = clamp_angle(playerActor->rot.x) * 0.5f;
        decorations->rotY[i] = clamp_angle(playerActor->rot.y) * 0.5f;
        decorations->rotZ[i] = clamp_angle(playerActor->rot.z) * 0.5f;
    }
}

void disable_player_blur(void) {
    Actor* playerActor = gBattleStatus.playerActor;
    ActorPart* partsTable = playerActor->partsTable;
    DecorationTable* decorations = partsTable->decorationTable;

    if (decorations->blurEnableCount != 0) {
        decorations->blurEnableCount--;
        if (decorations->blurEnableCount == 0) {
            decorations->blurDisableDelay = 20;
        }
    }
}

void reset_player_blur(void) {
    Actor* playerActor = gBattleStatus.playerActor;
    ActorPart* partsTable = playerActor->partsTable;
    DecorationTable* decorations = partsTable->decorationTable;

    if (decorations->blurEnableCount != 0) {
        decorations->blurEnableCount--;
        if (decorations->blurEnableCount == 0) {
            playerActor->flags &= ~ACTOR_FLAG_BLUR_ENABLED;
            decorations->blurDisableDelay = 1;
        }
    }
}

void force_disable_player_blur(void) {
    Actor* playerActor = gBattleStatus.playerActor;
    ActorPart* partsTable = playerActor->partsTable;
    DecorationTable* decorations = partsTable->decorationTable;

    decorations->blurEnableCount = 0;
    decorations->blurDisableDelay = 20;
}

void force_disable_player_blur_immediately(void) {
    Actor* playerActor = gBattleStatus.playerActor;
    ActorPart* partsTable = playerActor->partsTable;
    DecorationTable* decorations = partsTable->decorationTable;

    playerActor->flags &= ~ACTOR_FLAG_BLUR_ENABLED;
    decorations->blurEnableCount = 0;
    decorations->blurDisableDelay = 1;
}

void update_player_actor_blur_history(Actor* actor) {
    ActorPart* partsTable = actor->partsTable;
    DecorationTable* decorations = partsTable->decorationTable;

    if (!(partsTable->flags & ACTOR_PART_FLAG_INVISIBLE) && partsTable->idleAnimations != NULL) {
        s32 i = decorations->blurBufferPos;

        decorations->posX[i] = partsTable->curPos.x;
        decorations->posY[i] = partsTable->curPos.y;
        decorations->posZ[i] = partsTable->curPos.z;
        decorations->yaw[i] = actor->yaw;

        decorations->rotPivotOffsetX[i] = actor->rotPivotOffset.x * actor->scalingFactor;
        decorations->rotPivotOffsetY[i] = actor->rotPivotOffset.y * actor->scalingFactor;

        decorations->rotX[i] = clamp_angle(actor->rot.x) * 0.5f;
        decorations->rotY[i] = clamp_angle(actor->rot.y) * 0.5f;
        decorations->rotZ[i] = clamp_angle(actor->rot.z) * 0.5f;

        i++;
        if (i >= ARRAY_COUNT(decorations->posX)) {
            i = 0;
        }
        decorations->blurBufferPos = i;
    }
}

void appendGfx_player_actor_blur(Actor* actor) {
    Matrix4f mtxRotX, mtxRotY, mtxRotZ, mtxRotation;
    Matrix4f mtxScale;
    Matrix4f mtxPivotOn, mtxPivotOff, mtxTranslate;
    Matrix4f mtxTransform, mtxTemp;
    s32 strideIdx;
    s32 drawIdx;
    s32 yaw;
    ActorPart* partTable;
    DecorationTable* decorations;
    f32 rotX, rotY, rotZ;
    s32 prevOpacity;
    s32 opacity;
    s32 pivotOffsetX;
    s32 pivotOffsetY;
    s32 bufPos;
    s32 blurOpacityBase;
    s32 opacityLossIncrement;
    f32 x, y, z;

    partTable = actor->partsTable;
    decorations = partTable->decorationTable;
    if (decorations->blurDisableDelay != 0) {
        decorations->blurDisableDelay--;
        if (decorations->blurDisableDelay == 0) {
            actor->flags &= ~ACTOR_FLAG_BLUR_ENABLED;
            return;
        }
    }

    if (!(partTable->flags & ACTOR_PART_FLAG_INVISIBLE) && partTable->idleAnimations != NULL) {
        bufPos = decorations->blurBufferPos;
        strideIdx = 0;
        drawIdx = 0;

        while (TRUE) {
            bufPos--;
            strideIdx++;
            if (bufPos < 0) {
                bufPos = ARRAY_COUNT(decorations->posX) - 1;
            }
            if (bufPos == decorations->blurBufferPos) {
                break;
            }

            // only draw every third blur frame
            if (strideIdx < 3) {
                continue;
            }

            strideIdx = 0;
            drawIdx++;

            if (decorations->blurDrawCount < drawIdx) {
                break;
            }

            opacity = partTable->opacity;

            x = decorations->posX[bufPos];
            y = decorations->posY[bufPos];
            z = decorations->posZ[bufPos];

            yaw = decorations->yaw[bufPos];

            pivotOffsetX = decorations->rotPivotOffsetX[bufPos];
            pivotOffsetY = decorations->rotPivotOffsetY[bufPos];

            rotX = decorations->rotX[bufPos] * 2;
            rotY = decorations->rotY[bufPos] * 2;
            rotZ = decorations->rotZ[bufPos] * 2;

            blurOpacityBase = 120;
            opacityLossIncrement = 20;
            if (opacity < 50) {
                blurOpacityBase = 50;
                opacityLossIncrement = 8;
            } else if (opacity < 100) {
                blurOpacityBase = 70;
                opacityLossIncrement = 10;
            } else if (opacity < 150) {
                blurOpacityBase = 100;
                opacityLossIncrement = 15;
            }

            guTranslateF(mtxTranslate, x, y, z);
            guTranslateF(mtxPivotOn, -pivotOffsetX, -pivotOffsetY, 0.0f);
            guTranslateF(mtxPivotOff, pivotOffsetX, pivotOffsetY, 0.0f);
            guRotateF(mtxRotX, rotX, 1.0f, 0.0f, 0.0f);
            guRotateF(mtxRotY, rotY, 0.0f, 1.0f, 0.0f);
            guRotateF(mtxRotZ, rotZ, 0.0f, 0.0f, 1.0f);
            guMtxCatF(mtxRotX, mtxRotY, mtxTransform);
            guMtxCatF(mtxTransform, mtxRotZ, mtxRotation);
            guScaleF(mtxScale, actor->scale.x * SPRITE_WORLD_SCALE_D * actor->scalingFactor,
                            actor->scale.y * SPRITE_WORLD_SCALE_D * actor->scalingFactor * partTable->verticalStretch,
                            actor->scale.z * SPRITE_WORLD_SCALE_D);
            guMtxCatF(mtxScale, mtxPivotOn, mtxTemp);
            guMtxCatF(mtxTemp, mtxRotation, mtxTransform);
            guMtxCatF(mtxTransform, mtxPivotOff, mtxTemp);
            guMtxCatF(mtxTemp, mtxTranslate, mtxTransform);
            prevOpacity = partTable->opacity;
            partTable->opacity = blurOpacityBase - (drawIdx * opacityLossIncrement);
            render_with_adjusted_palettes(SPRITE_MODE_PLAYER, partTable, clamp_angle(yaw + 180), mtxTransform, 1);
            partTable->opacity = prevOpacity;
        }
    }
}

void update_nonplayer_actor_blur_history(b32 isPartner, Actor* actor) {
    s32 numParts = actor->numParts;
    ActorPart* partsTable = actor->partsTable;
    DecorationTable* decorations;
    s32 i, j;

    for (i = 0; i < numParts; i++) {
        if (partsTable->flags & ACTOR_PART_FLAG_INVISIBLE || partsTable->idleAnimations == NULL || partsTable->flags & ACTOR_PART_FLAG_NO_DECORATIONS) {
            partsTable = partsTable->nextPart;
        } else {
            decorations = partsTable->decorationTable;
            j = decorations->blurBufferPos;

            decorations->posX[j] = partsTable->curPos.x;
            decorations->posY[j] = partsTable->curPos.y;
            decorations->posZ[j] = partsTable->curPos.z;
            decorations->yaw[j] = actor->yaw;

            decorations->rotPivotOffsetX[j] = actor->rotPivotOffset.x;
            decorations->rotPivotOffsetY[j] = actor->rotPivotOffset.y;

            decorations->rotX[j] = clamp_angle(actor->rot.x) * 0.5f;
            decorations->rotY[j] = clamp_angle(actor->rot.y) * 0.5f;
            decorations->rotZ[j] = clamp_angle(actor->rot.z) * 0.5f;

            j++;
            if (j >= ARRAY_COUNT(decorations->posX)) {
                j = 0;
            }
            decorations->blurBufferPos = j;
        }
    }
}

void appendGfx_nonplayer_actor_blur(b32 isPartner, Actor* actor) {
    DecorationTable* decorations;
    ActorPart* partTable;
    Matrix4f mtxRotX, mtxRotY, mtxRotZ, mtxRotation;
    Matrix4f mtxScale, mtxTranslate;
    Matrix4f mtxTemp, mtxTransform;
    Matrix4f mtxPivotOn, mtxPivotOff;
    Matrix4f mtxActor, mtxPartScale;
    s32 numParts;
    s32 i, bufPos, strideIdx, drawIdx;
    f32 x, y, z;
    f32 rotX, rotY, rotZ;
    s32 yaw;
    s32 opacity;
    s32 pivotX;
    s32 pivotY;
    s32 blurOpacityBase;
    s32 opacityLossIncrement;
    s32 blurOpacity;
    s32 flags;

    guRotateF(mtxRotX, actor->rot.x, 1.0f, 0.0f, 0.0f);
    guRotateF(mtxRotY, actor->rot.y, 0.0f, 1.0f, 0.0f);
    guRotateF(mtxRotZ, actor->rot.z, 0.0f, 0.0f, 1.0f);
    guMtxCatF(mtxRotX, mtxRotY, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);
    guScaleF(mtxScale, actor->scale.x * SPRITE_WORLD_SCALE_D * actor->scalingFactor,
                    actor->scale.y * SPRITE_WORLD_SCALE_D * actor->scalingFactor,
                    actor->scale.z * SPRITE_WORLD_SCALE_D);
    guMtxCatF(mtxScale, mtxRotation, mtxActor);

    numParts = actor->numParts;
    partTable = actor->partsTable;
    for (i = 0; i < numParts; i++) {
        if ((partTable->idleAnimations == NULL) || (partTable->flags & ACTOR_PART_FLAG_NO_DECORATIONS)) {
            partTable = partTable->nextPart;
            continue;
        }

        decorations = partTable->decorationTable;
        if (decorations->blurDisableDelay != 0) {
            decorations->blurDisableDelay--;
            if (decorations->blurDisableDelay == 0) {
                actor->flags &= ~ACTOR_FLAG_BLUR_ENABLED;
                partTable = partTable->nextPart;
                continue;
            }
        }

        if (partTable->flags & ACTOR_PART_FLAG_INVISIBLE) {
            partTable = partTable->nextPart;
            continue;
        }

        if (partTable->flags & ACTOR_PART_FLAG_USE_ABSOLUTE_POSITION) {
            guScaleF(mtxPartScale, actor->scale.x * SPRITE_WORLD_SCALE_D, actor->scale.y * SPRITE_WORLD_SCALE_D, actor->scale.z * SPRITE_WORLD_SCALE_D);
        }

        bufPos = decorations->blurBufferPos;
        strideIdx = 0;
        drawIdx = 0;

        while (TRUE) {
            bufPos--;
            strideIdx++;

            if (bufPos < 0) {
                bufPos = ACTOR_BLUR_FRAMES - 1;
            }

            if (bufPos == decorations->blurBufferPos) {
                break;
            }

            // only draw every third blur frame
            if (strideIdx < 3) {
                continue;
            }

            strideIdx = 0;
            drawIdx++;

            if (decorations->blurDrawCount < drawIdx) {
                break;
            }

            opacity = partTable->opacity;

            x = decorations->posX[bufPos];
            y = decorations->posY[bufPos];
            z = decorations->posZ[bufPos];

            yaw = decorations->yaw[bufPos];

            pivotX = decorations->rotPivotOffsetX[bufPos];
            pivotY = decorations->rotPivotOffsetY[bufPos];

            rotX = decorations->rotX[bufPos] * 2;
            rotY = decorations->rotY[bufPos] * 2;
            rotZ = decorations->rotZ[bufPos] * 2;

            blurOpacityBase = 120;
            opacityLossIncrement = 20;
            if (opacity < 50) {
                blurOpacityBase = 50;
                opacityLossIncrement = 8;
            } else if (opacity < 100) {
                blurOpacityBase = 70;
                opacityLossIncrement = 10;
            } else if (opacity < 150) {
                blurOpacityBase = 100;
                opacityLossIncrement = 15;
            }

            if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
                guTranslateF(mtxPivotOn, -pivotX, -pivotY, 0.0f);
                guTranslateF(mtxPivotOff, pivotX, pivotY, 0.0f);
            } else {
                guTranslateF(mtxPivotOn, -pivotX, pivotY, 0.0f);
                guTranslateF(mtxPivotOff, pivotX, -pivotY, 0.0f);
            }

            guTranslateF(mtxTranslate, x, y, z);
            guRotateF(mtxRotX, rotX, 1.0f, 0.0f, 0.0f);
            guRotateF(mtxRotY, rotY, 0.0f, 1.0f, 0.0f);
            guRotateF(mtxRotZ, rotZ, 0.0f, 0.0f, 1.0f);
            guMtxCatF(mtxRotY, mtxRotX, mtxTemp);
            guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);
            guScaleF(mtxScale, partTable->scale.x, partTable->scale.y * partTable->verticalStretch, partTable->scale.z);
            guMtxCatF(mtxScale, mtxPivotOn, mtxTransform);
            guMtxCatF(mtxTransform, mtxRotation, mtxTemp);
            guMtxCatF(mtxTemp, mtxPivotOff, mtxTransform);

            if (!(partTable->flags & ACTOR_PART_FLAG_USE_ABSOLUTE_POSITION)) {
                guMtxCatF(mtxTransform, mtxActor, mtxTemp);
            } else {
                guMtxCatF(mtxTransform, mtxPartScale, mtxTemp);
            }
            guMtxCatF(mtxTemp, mtxTranslate, mtxTransform);

            flags = DRAW_SPRITE_OVERRIDE_ALPHA;
            blurOpacity = blurOpacityBase - drawIdx * opacityLossIncrement;
            if (!isPartner) {
                spr_draw_npc_sprite(partTable->spriteInstanceID | flags, yaw, blurOpacity, 0, mtxTransform);
            } else {
                spr_draw_npc_sprite(partTable->spriteInstanceID | flags, clamp_angle(yaw + 180), blurOpacity, 0, mtxTransform);
            }
        }
    }
}

void update_enemy_actor_blur_history(Actor* actor) {
    update_nonplayer_actor_blur_history(FALSE, actor);
}

void appendGfx_enemy_actor_blur(void* data) {
    Actor* actor = data;

    appendGfx_nonplayer_actor_blur(FALSE, actor);
}

void update_partner_actor_blur_history(Actor* actor) {
    update_nonplayer_actor_blur_history(TRUE, actor);
}

void appendGfx_partner_actor_blur(void* data) {
    Actor* actor = data;

    appendGfx_nonplayer_actor_blur(TRUE, actor);
}

void update_nonplayer_actor_shadow(b32 isPartner, Actor* actor) {
    Camera* camera = &gCameras[CAM_BATTLE];
    ActorPart* actorPart;
    Shadow* shadow;
    s32 numParts;
    f32 x1, y1, z1;
    f32 x2, y2, z2;
    f32 dist;
    s32 spriteID;
    f32 yaw;
    s32 i;

    if (actor != NULL) {
        shadow = get_shadow_by_index(actor->shadow.id);
        shadow->flags |= ENTITY_FLAG_HIDDEN;
        if (!(actor->flags & ACTOR_FLAG_INVISIBLE)) {
            if (actor->flags & ACTOR_FLAG_BLUR_ENABLED) {
                if (!isPartner) {
                    update_enemy_actor_blur_history(actor);
                } else {
                    update_partner_actor_blur_history(actor);
                }
            }

            actor->renderMode = RENDER_MODE_ALPHATEST;
            x1 = actor->curPos.x + actor->headOffset.x;
            if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
                y1 = actor->curPos.y + actor->headOffset.y;
            } else {
                y1 = actor->curPos.y - actor->headOffset.y;
            }
            z1 = actor->curPos.z + actor->headOffset.z;
            numParts = actor->numParts;
            actorPart = actor->partsTable;

            for (i = 0; i < numParts; i++) {
                if (!(actorPart->flags & ACTOR_PART_FLAG_INVISIBLE) && actorPart->idleAnimations != NULL) {
                    spriteID = actorPart->spriteInstanceID;
                    if (spriteID >= 0) {
                        spr_update_sprite(spriteID, actorPart->curAnimation, actorPart->animationRate);
                        actorPart->animNotifyValue = spr_get_notify_value(actorPart->spriteInstanceID);
                    }

                    if (!(actorPart->flags & ACTOR_PART_FLAG_USE_ABSOLUTE_POSITION)) {
                        x2 = x1 + actorPart->partOffset.x + actorPart->visualOffset.x;
                        if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
                            y2 = y1 + actorPart->partOffset.y + actorPart->visualOffset.y;
                        } else {
                            y2 = y1 - actorPart->partOffset.y - actorPart->visualOffset.y;
                        }
                        z2 = z1 + actorPart->partOffset.z + actorPart->visualOffset.z;
                        yaw = actorPart->yaw = actor->yaw;
                    } else {
                        x2 = actorPart->absolutePos.x + actorPart->visualOffset.x;
                        y2 = actorPart->absolutePos.y + actorPart->visualOffset.y;
                        z2 = actorPart->absolutePos.z + actorPart->visualOffset.z;
                        yaw = actorPart->yaw;
                    }
                    actorPart->curPos.x = x2;
                    actorPart->curPos.y = y2;
                    actorPart->curPos.z = z2;

                    if (!(actorPart->flags & ACTOR_PART_FLAG_NO_SHADOW)) {
                        shadow = get_shadow_by_index(actorPart->shadowIndex);
                        shadow->flags &= ~ENTITY_FLAG_HIDDEN;
                        x1 = actorPart->curPos.x;
                        if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
                            y1 = actorPart->curPos.y + 12.0;
                        } else {
                            y1 = actorPart->curPos.y - 12.0;
                        }
                        z1 = actorPart->curPos.z;

                        dist = 32767.0f;
                        npc_raycast_down_sides(0, &x1, &y1, &z1, &dist);

                        if (200.0f < dist) {
                            shadow->flags |= ENTITY_FLAG_HIDDEN;
                        }
                        shadow->pos.x = x1;
                        shadow->pos.y = y1;
                        shadow->pos.z = z1;
                        shadow->rot.y = clamp_angle(yaw - camera->curYaw);
                        set_standard_shadow_scale(shadow, dist);
                        shadow->scale.x *= actorPart->shadowScale;
                    }
                    if (actorPart->opacity < 255 || actorPart->flags & ACTOR_PART_FLAG_TRANSPARENT) {
                        actor->renderMode = RENDER_MODE_SURFACE_XLU_LAYER3;
                    }
                }
                actorPart = actorPart->nextPart;
            }

            shadow = get_shadow_by_index(actor->shadow.id);
            if (!(actor->flags & ACTOR_FLAG_NO_SHADOW)) {
                shadow->flags &= ~ENTITY_FLAG_HIDDEN;
            }

            x1 = actor->curPos.x + actor->headOffset.x;
            if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
                y1 = actor->curPos.y + actor->headOffset.y + 12.0;
            } else {
                y1 = actor->curPos.y - actor->headOffset.y + 12.0;
            }
            z1 = actor->curPos.z + actor->headOffset.z;

            dist = 32767.0f;
            npc_raycast_down_sides(0, &x1, &y1, &z1, &dist);

            if (200.0f < dist) {
                shadow->flags |= ENTITY_FLAG_HIDDEN;
            }
            shadow->pos.x = x1;
            shadow->pos.y = y1;
            shadow->pos.z = z1 + bActorOffsets[actor->actorType].shadow;
            shadow->rot.y = clamp_angle(actor->yaw - camera->curYaw);
            set_standard_shadow_scale(shadow, dist);
            shadow->scale.x *= actor->shadowScale * actor->scalingFactor;
        }
    }
}

void update_enemy_shadows(void) {
    BattleStatus* battleStatus = &gBattleStatus;
    s32 i;

    for (i = 0; i < ARRAY_COUNT(battleStatus->enemyActors); i++) {
        update_nonplayer_actor_shadow(FALSE, battleStatus->enemyActors[i]);
    }
}

void update_hero_shadows(void) {
    update_nonplayer_actor_shadow(TRUE, gBattleStatus.partnerActor);
    update_player_actor_shadow();
}

void func_80255FD8(void) {
}

void appendGfx_npc_actor(b32 isPartner, s32 actorIndex) {
    BattleStatus* battleStatus = &gBattleStatus;
    Matrix4f mtxRotX, mtxRotY, mtxRotZ, mtxRotation;
    Matrix4f mtxScale, mtxScaleMod;
    Matrix4f mtxPivotOn, mtxPivotOff, mtxTranslate;
    Matrix4f mtxTemp, mtxActor, mtxTransform, mtxPartScale;
    Actor* actor;
    ActorPart* part;
    EffectInstance* effect;
    s32 numParts;
    f32 actorPosX, actorPosY, actorPosZ;
    f32 partPosX, partPosY, partPosZ, partYaw;
    u32 lastAnim;
    s32 animChanged;
    s32 palChanged;
    s32 decorChanged;
    s32 i;

    if (!isPartner) {
        actor = battleStatus->enemyActors[actorIndex];
    } else {
        actor = battleStatus->partnerActor;
    }
    actorPosX = actor->curPos.x + actor->headOffset.x;
    if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
        actorPosY = actor->curPos.y + actor->headOffset.y + actor->verticalRenderOffset;
    } else {
        actorPosY = actor->curPos.y - actor->headOffset.y + actor->verticalRenderOffset;
    }
    actorPosZ = actor->curPos.z + actor->headOffset.z;

    actor->disableEffect->data.disableX->pos.x = actorPosX +
        (actor->actorBlueprint->statusIconOffset.x + actor->statusIconOffset.x) * actor->scalingFactor;
    actor->disableEffect->data.disableX->pos.y = actorPosY +
        (actor->actorBlueprint->statusIconOffset.y + actor->statusIconOffset.y) * actor->scalingFactor;
    actor->disableEffect->data.disableX->pos.z = actorPosZ;

    if (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (actor->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)) {
        if (actor->disableDismissTimer != 0) {
            actor->disableDismissTimer--;
            actor->disableEffect->data.disableX->pos.y = NPC_DISPOSE_POS_Y;
        } else {
            actor->disableEffect->data.disableX->scale = (actor->scalingFactor * 0.75);
        }
    } else {
        actor->disableEffect->data.disableX->pos.y = NPC_DISPOSE_POS_Y;
        actor->disableDismissTimer = 10;
    }
    if (actor->debuff == STATUS_KEY_FROZEN) {
        effect = actor->icePillarEffect;
        if (actor->icePillarEffect != NULL) {
            if ((gBattleStatus.flags1 & BS_FLAGS1_SHOW_PLAYER_DECORATIONS) ||
                (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (actor->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)))
            {
                effect->data.icePillar->pos.x = actorPosX;
                effect->data.icePillar->pos.y = actorPosY;
                effect->data.icePillar->pos.z = actorPosZ;
                effect->data.icePillar->scale = actor->size.y / 24.0;
            } else {
                effect->data.icePillar->pos.x = NPC_DISPOSE_POS_X;
                effect->data.icePillar->pos.y = NPC_DISPOSE_POS_Y;
                effect->data.icePillar->pos.z = NPC_DISPOSE_POS_Z;
            }
        }
    } else {
        effect = actor->icePillarEffect;
        if (effect != NULL) {
            effect->flags |= FX_INSTANCE_FLAG_DISMISS;
            actor->icePillarEffect = NULL;
        }
    }
    set_status_icons_properties(actor->hudElementDataIndex, actorPosX, actorPosY, actorPosZ,
        (actor->actorBlueprint->statusIconOffset.x + actor->statusIconOffset.x) * actor->scalingFactor,
        (actor->actorBlueprint->statusIconOffset.y + actor->statusIconOffset.y) * actor->scalingFactor,
        (actor->actorBlueprint->statusTextOffset.x + actor->statusTextOffset.x) * actor->scalingFactor,
        (actor->actorBlueprint->statusTextOffset.y + actor->statusTextOffset.y) * actor->scalingFactor);

    if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
        set_status_icons_offset(actor->hudElementDataIndex,
            actor->size.y * actor->scalingFactor,
            actor->size.x * actor->scalingFactor);
    } else {
        set_status_icons_offset(actor->hudElementDataIndex,
            -actor->size.y * actor->scalingFactor,
             actor->size.x * actor->scalingFactor);
    }

    do {
        if (actor->debuff == STATUS_KEY_SHRINK) {
            actor->scalingFactor += ((0.4 - actor->scalingFactor) / 6.0);
        } else {
            actor->scalingFactor += ((1.0 - actor->scalingFactor) / 6.0);
        }
    } while (0); // required to match

    if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
        guTranslateF(mtxPivotOn,
            -actor->rotPivotOffset.x * actor->scalingFactor,
            -actor->rotPivotOffset.y * actor->scalingFactor,
            -actor->rotPivotOffset.z * actor->scalingFactor);
        guTranslateF(mtxPivotOff,
            actor->rotPivotOffset.x * actor->scalingFactor,
            actor->rotPivotOffset.y * actor->scalingFactor,
            actor->rotPivotOffset.z * actor->scalingFactor);
    } else {
        guTranslateF(mtxPivotOn,
            -actor->rotPivotOffset.x * actor->scalingFactor,
             actor->rotPivotOffset.y * actor->scalingFactor,
            -actor->rotPivotOffset.z * actor->scalingFactor);
        guTranslateF(mtxPivotOff,
             actor->rotPivotOffset.x * actor->scalingFactor,
            -actor->rotPivotOffset.y * actor->scalingFactor,
             actor->rotPivotOffset.z * actor->scalingFactor);
    }
    guRotateF(mtxRotX, actor->rot.x, 1.0f, 0.0f, 0.0f);
    guRotateF(mtxRotY, actor->rot.y, 0.0f, 1.0f, 0.0f);
    guRotateF(mtxRotZ, actor->rot.z, 0.0f, 0.0f, 1.0f);
    guMtxCatF(mtxRotY, mtxRotX, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);
    guScaleF(mtxScale,
        actor->scale.x * SPRITE_WORLD_SCALE_D * actor->scalingFactor,
        actor->scale.y * SPRITE_WORLD_SCALE_D * actor->scalingFactor,
        actor->scale.z * SPRITE_WORLD_SCALE_D);
    guScaleF(mtxScaleMod, actor->scaleModifier.x, actor->scaleModifier.y, actor->scaleModifier.z);
    guMtxCatF(mtxPivotOn, mtxScale, mtxTemp);
    guMtxCatF(mtxTemp, mtxScaleMod, mtxScale);
    guMtxCatF(mtxScale, mtxRotation, mtxTransform);
    guMtxCatF(mtxTransform, mtxPivotOff, mtxActor);

    part = actor->partsTable;
    numParts = actor->numParts;
    for (i = 0; i < numParts; i++) {
        if (!(part->flags & ACTOR_PART_FLAG_USE_ABSOLUTE_POSITION)) {
            partPosX = actorPosX + part->partOffset.x + part->visualOffset.x;
            if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
                partPosY = actorPosY + part->partOffset.y + part->visualOffset.y;
            } else {
                partPosY = (actorPosY - part->partOffset.y) - part->visualOffset.y;
            }
            partPosZ = actorPosZ + part->partOffset.z + part->visualOffset.z;
            partYaw = part->yaw = actor->yaw;
        } else {
            partPosX = part->absolutePos.x + part->visualOffset.x;
            partPosY = part->absolutePos.y + part->visualOffset.y;
            partPosZ = part->absolutePos.z + part->visualOffset.z;
            guScaleF(mtxPartScale,
                actor->scale.x * SPRITE_WORLD_SCALE_D,
                actor->scale.y * SPRITE_WORLD_SCALE_D,
                actor->scale.z * SPRITE_WORLD_SCALE_D);
            partYaw = part->yaw;
        }
        part->curPos.x = partPosX;
        part->curPos.y = partPosY;
        part->curPos.z = partPosZ;

        if (part->flags & ACTOR_PART_FLAG_INVISIBLE) {
            part = part->nextPart;
            continue;
        }
        if (part->idleAnimations == NULL) {
            part = part->nextPart;
            continue;
        }

        if (actor->transparentStatus == STATUS_KEY_TRANSPARENT) {
            part->flags |= ACTOR_PART_FLAG_TRANSPARENT;
        } else {
            part->flags &= ~ACTOR_PART_FLAG_TRANSPARENT;
        }

        do {
            lastAnim = part->curAnimation;
            animChanged = FALSE;
            palChanged = FALSE;
            decorChanged = FALSE;
        } while (0); // required to match

        if (isPartner) {
            if (!(gBattleStatus.flags2 & (BS_FLAGS2_OVERRIDE_INACTIVE_PARTNER))
                && (gBattleStatus.flags2 & BS_FLAGS2_PARTNER_TURN_USED)
            ) {
                do {
                    if (actor->koStatus == 0) {
                        part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_INACTIVE);
                        spr_update_sprite(part->spriteInstanceID, part->curAnimation, part->animationRate);
                        animChanged = TRUE;
                    }
                } while (0); // required to match
                set_actor_pal_adjustment(actor, ACTOR_PAL_ADJUST_PLAYER_DEBUFF);
                palChanged = TRUE;
                set_actor_glow_pal(actor, GLOW_PAL_OFF);
                decorChanged = TRUE;
            }
            if (isPartner && (gPlayerData.curPartner == PARTNER_WATT)) {
                if (!palChanged) {
                    set_actor_pal_adjustment(actor, ACTOR_PAL_ADJUST_WATT_IDLE);
                }
                palChanged = TRUE;
            }
        }
        if (actor->isGlowing) {
            if (!decorChanged) {
                set_actor_glow_pal(actor, GLOW_PAL_ON);
            }
            decorChanged = TRUE;
        }
        if (actor->debuff == STATUS_KEY_POISON) {
            if (!palChanged) {
                set_actor_pal_adjustment(actor, ACTOR_PAL_ADJUST_POISON);
            }
            palChanged = TRUE;
        }
        if (actor->debuff == STATUS_KEY_PARALYZE) {
            if (!palChanged) {
                set_actor_pal_adjustment(actor, ACTOR_PAL_ADJUST_PARALYZE);
            }
            palChanged = TRUE;
        }
        if (actor->debuff == STATUS_KEY_FEAR) {
            if (!palChanged) {
                set_actor_pal_adjustment(actor, ACTOR_PAL_ADJUST_FEAR);
            }
            palChanged = TRUE;
        }
        if (actor->staticStatus == STATUS_KEY_STATIC) {
            if (!palChanged) {
                set_actor_pal_adjustment(actor, ACTOR_PAL_ADJUST_STATIC);
            }
            palChanged = TRUE;
        }
        if (!palChanged && !(part->flags & ACTOR_PART_FLAG_HAS_PAL_EFFECT)) {
            set_actor_pal_adjustment(actor, ACTOR_PAL_ADJUST_NONE);
        }
        if (!decorChanged && !(part->flags & ACTOR_PART_FLAG_HAS_PAL_EFFECT)) {
            set_actor_glow_pal(actor, GLOW_PAL_OFF);
        }

        // adjust idle animation for status
        if (actor->flags & ACTOR_FLAG_USING_IDLE_ANIM) {
            if (!(part->flags & ACTOR_PART_FLAG_NO_STATUS_ANIMS)) {
                if (actor->debuff == STATUS_KEY_FROZEN) {
                    if (!animChanged) {
                        part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_FROZEN);
                        animChanged = TRUE;
                    }
                } else if (actor->debuff != STATUS_KEY_SHRINK) {
                    if (actor->debuff == STATUS_KEY_POISON) {
                        if (!animChanged) {
                            part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_POISON);
                            animChanged = TRUE;
                        }
                    } else if (actor->debuff == STATUS_KEY_DIZZY) {
                        if (!animChanged) {
                            part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_DIZZY);
                            animChanged = TRUE;
                        }
                    } else if (actor->debuff == STATUS_KEY_FEAR) {
                        if (!animChanged) {
                            part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_FEAR);
                            animChanged = TRUE;
                        }
                    } else if (actor->debuff == STATUS_KEY_SLEEP) {
                        if (!animChanged) {
                            part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_SLEEP);
                            animChanged = TRUE;
                        }
                    } else if (actor->debuff == STATUS_KEY_PARALYZE) {
                        if (!animChanged) {
                            part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_PARALYZE);
                            animChanged = TRUE;
                        }
                    }
                }

                if (actor->staticStatus == STATUS_KEY_STATIC) {
                    if (!animChanged) {
                        part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_STATIC);
                        animChanged = TRUE;
                    }

                    do {} while (0); // required to match
                }
                if (!animChanged) {
                    part->curAnimation = get_npc_anim_for_status(part->idleAnimations, 1);
                }

                if (isPartner) {
                    if (actor->koStatus == STATUS_KEY_DAZE) {
                        part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_DAZE);
                        animChanged = TRUE;
                    } else {
                        s32 temp = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_NORMAL);
                        do {
                            if (temp == get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_DAZE)) {
                                part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_NORMAL);
                            }
                        } while (0); // required to match
                    }
                }
                if (actor->debuff == STATUS_KEY_STOP) {
                    part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_STOP);
                    create_status_debuff(actor->hudElementDataIndex, STATUS_KEY_STOP);
                } else if (!animChanged) {
                    s32 temp = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_NORMAL);
                    do {
                        if (temp == get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_STOP)) {
                            part->curAnimation = get_npc_anim_for_status(part->idleAnimations, STATUS_KEY_NORMAL);
                        }
                    } while (0); // required to match
                }
            }
        }

        if (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (actor->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)) {
            do {
                if (actor->debuff == STATUS_KEY_POISON) {
                    create_status_debuff(actor->hudElementDataIndex, STATUS_KEY_POISON);
                } else if (actor->debuff == STATUS_KEY_DIZZY) {
                    create_status_debuff(actor->hudElementDataIndex, STATUS_KEY_DIZZY);
                } else if (actor->debuff == STATUS_KEY_SLEEP) {
                    create_status_debuff(actor->hudElementDataIndex, STATUS_KEY_SLEEP);
                } else if (actor->debuff == STATUS_KEY_PARALYZE) {
                    create_status_debuff(actor->hudElementDataIndex, STATUS_KEY_PARALYZE);
                } else if (actor->debuff == STATUS_KEY_SHRINK) {
                    create_status_debuff(actor->hudElementDataIndex, STATUS_KEY_SHRINK);
                } else if (actor->debuff == STATUS_KEY_FROZEN) {
                    create_status_debuff(actor->hudElementDataIndex, STATUS_KEY_FROZEN);
                }
            } while (0); // required to match

            if (actor->staticStatus == STATUS_KEY_STATIC) {
                create_status_static(actor->hudElementDataIndex, STATUS_KEY_STATIC);
            }
            if ((actor->transparentStatus == STATUS_KEY_TRANSPARENT) || (part->flags & ACTOR_PART_FLAG_TRANSPARENT)) {
                create_status_transparent(actor->hudElementDataIndex, STATUS_KEY_TRANSPARENT);
            }
            if (actor->chillOutAmount != 0) {
                create_status_chill_out(actor->hudElementDataIndex);
            }
        } else {
            enable_status_debuff(actor->hudElementDataIndex);
            enable_status_static(actor->hudElementDataIndex);
            enable_status_transparent(actor->hudElementDataIndex);
            enable_status_chill_out(actor->hudElementDataIndex);
        }
        if (part->spriteInstanceID >= 0) {
            if (lastAnim != part->curAnimation) {
                spr_update_sprite(part->spriteInstanceID, part->curAnimation, part->animationRate);
                part->animNotifyValue = spr_get_notify_value(part->spriteInstanceID);
            }
        }
        if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
            guTranslateF(mtxPivotOn,
                -part->rotPivotOffset.x * actor->scalingFactor,
                -part->rotPivotOffset.y * actor->scalingFactor,
                -part->rotPivotOffset.z * actor->scalingFactor);
            guTranslateF(mtxPivotOff,
                part->rotPivotOffset.x * actor->scalingFactor,
                part->rotPivotOffset.y * actor->scalingFactor,
                part->rotPivotOffset.z * actor->scalingFactor);
        } else {
            guTranslateF(mtxPivotOn,
                -part->rotPivotOffset.x * actor->scalingFactor,
                 part->rotPivotOffset.y * actor->scalingFactor,
                -part->rotPivotOffset.z * actor->scalingFactor);
            guTranslateF(mtxPivotOff,
                 part->rotPivotOffset.x * actor->scalingFactor,
                -part->rotPivotOffset.y * actor->scalingFactor,
                 part->rotPivotOffset.z * actor->scalingFactor);
        }
        guTranslateF(mtxTranslate,
            partPosX + part->palAnimPosOffset[0],
            partPosY + part->palAnimPosOffset[1],
            partPosZ);
        guRotateF(mtxRotX, part->rot.x, 1.0f, 0.0f, 0.0f);
        guRotateF(mtxRotY, part->rot.y, 0.0f, 1.0f, 0.0f);
        guRotateF(mtxRotZ, part->rot.z, 0.0f, 0.0f, 1.0f);
        guMtxCatF(mtxRotY, mtxRotX, mtxTemp);
        guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);
        guScaleF(mtxScale, part->scale.x, part->scale.y * part->verticalStretch, part->scale.z);
        guMtxCatF(mtxScale, mtxPivotOn, mtxTransform);
        guMtxCatF(mtxTransform, mtxRotation, mtxTemp);
        guMtxCatF(mtxTemp, mtxPivotOff, mtxTransform);

        if (!(part->flags & ACTOR_PART_FLAG_USE_ABSOLUTE_POSITION)) {
            guMtxCatF(mtxTransform, mtxActor, mtxTemp);
        } else {
            guMtxCatF(mtxTransform, mtxPartScale, mtxTemp);
        }
        guMtxCatF(mtxTemp, mtxTranslate, mtxTransform);

        part->curPos.x = partPosX + part->palAnimPosOffset[0];
        part->curPos.y = partPosY + part->palAnimPosOffset[1];
        part->curPos.z = partPosZ;

        if (part->spriteInstanceID >= 0) {
            if (!isPartner) {
                update_part_glow(TRUE, part, partYaw, FALSE);
                update_part_flash(TRUE, part, partYaw, FALSE);
                render_with_adjusted_palettes(SPRITE_MODE_NPC, part, partYaw, mtxTransform, 0);
            } else {
                update_part_glow(TRUE, part, clamp_angle(180.0f - partYaw), FALSE);
                update_part_flash(TRUE, part, clamp_angle(180.0f - partYaw), FALSE);
                render_with_adjusted_palettes(SPRITE_MODE_NPC, part, clamp_angle(180.0f - partYaw), mtxTransform, 0);
            }

            _add_part_decoration(part);
        }

        part = part->nextPart;
    }
}

void appendGfx_npc_actor_reflection(s32 flipYaw, Actor* actor) {
    Matrix4f mtxRotX, mtxRotY, mtxRotZ, mtxRotation, mtxScale;
    Matrix4f mtxPivotOn, mtxPivotOff, mtxTranslate;
    Matrix4f mtxTemp, mtxTransform, mtxMirror;
    Matrix4f mtxActor, mtxPartScale;
    ActorPart* part;
    f32 actorPosX, actorPosY, actorPosZ;
    f32 partPosX, partPosY, partPosZ;
    f32 partYaw;
    s32 numParts;
    s32 i;

    actorPosX = actor->curPos.x + actor->headOffset.x;
    if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
        actorPosY = actor->curPos.y + actor->headOffset.y;
    } else {
        actorPosY = actor->curPos.y - actor->headOffset.y;
    }
    actorPosZ = actor->curPos.z + actor->headOffset.z - 5.0f;

    if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
        guTranslateF(mtxPivotOn,
            -actor->rotPivotOffset.x * actor->scalingFactor,
            -actor->rotPivotOffset.y * actor->scalingFactor,
            -actor->rotPivotOffset.z * actor->scalingFactor);
         guTranslateF(mtxPivotOff,
             actor->rotPivotOffset.x * actor->scalingFactor,
             actor->rotPivotOffset.y * actor->scalingFactor,
             actor->rotPivotOffset.z * actor->scalingFactor);
    } else {
        guTranslateF(mtxPivotOn,
            -actor->rotPivotOffset.x * actor->scalingFactor,
             actor->rotPivotOffset.y * actor->scalingFactor,
            -actor->rotPivotOffset.z * actor->scalingFactor);
         guTranslateF(mtxPivotOff,
             actor->rotPivotOffset.x * actor->scalingFactor,
             -actor->rotPivotOffset.y * actor->scalingFactor,
             actor->rotPivotOffset.z * actor->scalingFactor);
    }

    guRotateF(mtxRotX, actor->rot.x, 1.0f, 0.0f, 0.0f);
    guRotateF(mtxRotY, actor->rot.y, 0.0f, 1.0f, 0.0f);
    guRotateF(mtxRotZ, actor->rot.z, 0.0f, 0.0f, 1.0f);
    guMtxCatF(mtxRotY, mtxRotX, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);

    guScaleF(mtxScale,
        actor->scale.x * SPRITE_WORLD_SCALE_D * actor->scalingFactor,
        actor->scale.y * SPRITE_WORLD_SCALE_D * actor->scalingFactor,
        actor->scale.z * SPRITE_WORLD_SCALE_D);

    guMtxCatF(mtxPivotOn, mtxScale, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotation, mtxTransform);
    guMtxCatF(mtxTransform, mtxPivotOff, mtxActor);

    part = actor->partsTable;
    numParts = actor->numParts;
    for (i = 0; i < numParts; i++) {
        // determine part position
        if (!(part->flags & ACTOR_PART_FLAG_USE_ABSOLUTE_POSITION)) {
            partPosX = actorPosX + part->partOffset.x + part->visualOffset.x;
            if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
                partPosY = actorPosY + part->partOffset.y + part->visualOffset.y;
            } else {
                partPosY = actorPosY - part->partOffset.y - part->visualOffset.y;
            }
            partPosZ = actorPosZ + part->partOffset.z + part->visualOffset.z;
            partYaw = part->yaw = actor->yaw;
        } else {
            partPosX = part->absolutePos.x + part->visualOffset.x;
            partPosY = part->absolutePos.y + part->visualOffset.y;
            partPosZ = part->absolutePos.z + part->visualOffset.z;
            guScaleF(mtxPartScale,
                actor->scale.x * SPRITE_WORLD_SCALE_D,
                actor->scale.y * SPRITE_WORLD_SCALE_D,
                actor->scale.z * SPRITE_WORLD_SCALE_D);
            partYaw = part->yaw;
        }

        if (part->flags & ACTOR_PART_FLAG_INVISIBLE) {
            part = part->nextPart;
            continue;
        }
        if (part->idleAnimations == NULL) {
            part = part->nextPart;
            continue;
        }

        if (!(actor->flags & ACTOR_FLAG_UPSIDE_DOWN)) {
            guTranslateF(mtxPivotOn,
                -part->rotPivotOffset.x * actor->scalingFactor,
                -part->rotPivotOffset.y * actor->scalingFactor,
                -part->rotPivotOffset.z * actor->scalingFactor);
            guTranslateF(mtxPivotOff,
                 part->rotPivotOffset.x * actor->scalingFactor,
                 part->rotPivotOffset.y * actor->scalingFactor,
                 part->rotPivotOffset.z * actor->scalingFactor);
        } else {
            guTranslateF(mtxPivotOn,
                -part->rotPivotOffset.x * actor->scalingFactor,
                 part->rotPivotOffset.y * actor->scalingFactor,
                -part->rotPivotOffset.z * actor->scalingFactor);
            guTranslateF(mtxPivotOff,
                 part->rotPivotOffset.x * actor->scalingFactor,
                -part->rotPivotOffset.y * actor->scalingFactor,
                 part->rotPivotOffset.z * actor->scalingFactor);
        }
        guTranslateF(mtxTranslate,
            partPosX + part->palAnimPosOffset[0],
            partPosY + part->palAnimPosOffset[1],
            partPosZ - 1.0f);

        guRotateF(mtxRotX, part->rot.x, 1.0f, 0.0f, 0.0f);
        guRotateF(mtxRotY, part->rot.y, 0.0f, 1.0f, 0.0f);
        guRotateF(mtxRotZ, part->rot.z, 0.0f, 0.0f, 1.0f);
        guMtxCatF(mtxRotY, mtxRotX, mtxTemp);
        guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);

        guScaleF(mtxScale, part->scale.x, part->scale.y * part->verticalStretch, part->scale.z);
        mtx_mirror_y(mtxMirror);
        guMtxCatF(mtxScale, mtxPivotOn, mtxTemp);
        guMtxCatF(mtxTemp, mtxRotation, mtxTransform);
        guMtxCatF(mtxTransform, mtxPivotOff, mtxTemp);
        if (!(part->flags & ACTOR_PART_FLAG_USE_ABSOLUTE_POSITION)) {
            guMtxCatF(mtxTemp, mtxActor, mtxTransform);
        } else {
            guMtxCatF(mtxTemp, mtxPartScale, mtxTransform);
        }
        guMtxCatF(mtxTransform, mtxTranslate, mtxTemp);
        guMtxCatF(mtxTemp, mtxMirror, mtxTransform);

        if (flipYaw == 0) {
            update_part_glow(TRUE, part, partYaw, TRUE);
            update_part_flash(TRUE, part, partYaw, TRUE);
            render_with_adjusted_palettes(SPRITE_MODE_NPC, part, partYaw, mtxTransform, 1);
        } else {
            update_part_glow(TRUE, part, clamp_angle(partYaw + 180.0f), TRUE);
            update_part_flash(TRUE, part, clamp_angle(partYaw + 180.0f), TRUE);
            render_with_adjusted_palettes(SPRITE_MODE_NPC, part, clamp_angle(partYaw + 180.0f), mtxTransform, 1);
        }

        part = part->nextPart;
    }
}

void appendGfx_enemy_actor(void* data) {
    appendGfx_npc_actor(FALSE, (s32) data);
}

void appendGfx_partner_actor(void* data) {
    appendGfx_npc_actor(TRUE, (s32) data);
}

void appendGfx_enemy_actor_reflection(void* data) {
    Actor* actor = data;

    appendGfx_npc_actor_reflection(0, actor);
}

void appendGfx_partner_actor_reflection(void* data) {
    appendGfx_npc_actor_reflection(1, gBattleStatus.partnerActor);
}

void update_player_actor_shadow(void) {
    BattleStatus* battleStatus = &gBattleStatus;
    Camera* camera = &gCameras[CAM_BATTLE];
    Actor* player = battleStatus->playerActor;
    ActorPart* parts = player->partsTable;
    Shadow* shadow;
    f32 x, y, z, distance;

    parts->animNotifyValue = spr_update_player_sprite(PLAYER_SPRITE_MAIN, parts->curAnimation, parts->animationRate);

    if (player->flags & ACTOR_FLAG_BLUR_ENABLED) {
        update_player_actor_blur_history(player);
    }

    shadow = get_shadow_by_index(player->shadow.id);
    shadow->flags &= ~ENTITY_FLAG_HIDDEN;

    if (!battleStatus->outtaSightActive) {
        shadow->alpha = 128;
    } else {
        shadow->alpha = 40;
    }

    distance = 32767.0f;
    x = player->curPos.x + player->headOffset.x;
    z = player->curPos.z + player->headOffset.z;
    y = player->curPos.y + player->headOffset.y + 12.0;
    npc_raycast_down_sides(0, &x, &y, &z, &distance);

    if (distance > 200.0f) {
        shadow->flags |= ENTITY_FLAG_HIDDEN;
    }
    shadow->pos.x = x;
    shadow->pos.y = y;
    shadow->pos.z = z;
    shadow->rot.y = clamp_angle(player->yaw - camera->curYaw);
    set_standard_shadow_scale(shadow, distance);
    shadow->scale.x *= player->shadowScale * player->scalingFactor;

    if (parts->opacity >= 255 && !(parts->flags & ACTOR_PART_FLAG_TRANSPARENT)) {
        player->renderMode = RENDER_MODE_ALPHATEST;
    } else {
        player->renderMode = RENDER_MODE_SURFACE_XLU_LAYER3;
    }
}

void appendGfx_player_actor(void* arg0) {
    BattleStatus* battleStatus = &gBattleStatus;
    PlayerData* playerData = &gPlayerData;
    Matrix4f mtxRotX, mtxRotY, mtxRotZ, mtxRotation;
    Matrix4f mtxScale;
    Matrix4f mtxPivotOn, mtxPivotOff, mtxTranslate;
    Matrix4f mtxTemp, mtxTransform;

    Actor* partner;
    Actor* player;
    ActorPart* playerParts;
    EffectInstance* effect;
    f32 playerPosX, playerPosY, playerPosZ, playerYaw;
    s32 animChanged, palChanged, decorChanged, cond4;
    u32 lastAnim;

    player = battleStatus->playerActor;
    partner = battleStatus->partnerActor;
    playerParts = player->partsTable;

    playerPosX = player->curPos.x + player->headOffset.x;
    playerPosY = player->curPos.y + player->headOffset.y + player->verticalRenderOffset;
    playerPosZ = player->curPos.z + player->headOffset.z;

    playerYaw = playerParts->yaw = player->yaw;

    player->disableEffect->data.disableX->pos.x = playerPosX +
        (player->actorBlueprint->statusIconOffset.x + player->statusIconOffset.x) * player->scalingFactor;
    player->disableEffect->data.disableX->pos.y = playerPosY +
        (player->actorBlueprint->statusIconOffset.y + player->statusIconOffset.y) * player->scalingFactor;
    player->disableEffect->data.disableX->pos.z = playerPosZ;

    if (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)) {
        if (player->disableDismissTimer != 0) {
            player->disableDismissTimer--;
            player->disableEffect->data.disableX->pos.y = NPC_DISPOSE_POS_Y;
        } else {
            player->disableEffect->data.disableX->scale = player->scalingFactor * 0.75;
        }
    } else {
        player->disableEffect->data.disableX->pos.y = NPC_DISPOSE_POS_Y;
        player->disableDismissTimer = 10;
    }

    if (battleStatus->waterBlockTurnsLeft != 0) {
        if ((gBattleStatus.flags1 & BS_FLAGS1_SHOW_PLAYER_DECORATIONS) ||
            (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)))
        {
            effect = battleStatus->waterBlockEffect;
            effect->data.waterBlock->pos.x = playerPosX;
            effect->data.waterBlock->pos.y = playerPosY;
            effect->data.waterBlock->pos.z = playerPosZ;
        } else {
            effect = battleStatus->waterBlockEffect;
            effect->data.waterBlock->pos.x = playerPosX;
            effect->data.waterBlock->pos.y = NPC_DISPOSE_POS_Y;
            effect->data.waterBlock->pos.z = playerPosZ;
        }
    }
    if (battleStatus->cloudNineTurnsLeft != 0) {
        if ((gBattleStatus.flags1 & BS_FLAGS1_SHOW_PLAYER_DECORATIONS) ||
            (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)))
        {
            effect = battleStatus->cloudNineEffect;
            effect->data.endingDecals->pos.x = playerPosX;
            effect->data.endingDecals->pos.y = playerPosY;
            effect->data.endingDecals->pos.z = playerPosZ;
            effect->data.endingDecals->scale = player->scalingFactor;
        } else {
            effect = battleStatus->cloudNineEffect;
            effect->data.endingDecals->pos.x = playerPosX;
            effect->data.endingDecals->pos.y = NPC_DISPOSE_POS_Y;
            effect->data.endingDecals->pos.z = playerPosZ;
        }
    }
    if (player->debuff == STATUS_KEY_FROZEN) {
        effect = player->icePillarEffect;
        if (player->icePillarEffect != NULL) {
            if ((gBattleStatus.flags1 & BS_FLAGS1_SHOW_PLAYER_DECORATIONS) ||
                (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)))
            {
                effect->data.icePillar->pos.x = playerPosX - 8.0f;
                effect->data.icePillar->pos.y = playerPosY;
                effect->data.icePillar->pos.z = playerPosZ;
                effect->data.icePillar->scale = player->size.y / 24.0;
            } else {
                effect->data.icePillar->pos.x = NPC_DISPOSE_POS_X;
                effect->data.icePillar->pos.y = NPC_DISPOSE_POS_Y;
                effect->data.icePillar->pos.z = NPC_DISPOSE_POS_Z;
            }
        } else {
            effect->data.icePillar->pos.x = NPC_DISPOSE_POS_X;
            effect->data.icePillar->pos.y = NPC_DISPOSE_POS_Y;
            effect->data.icePillar->pos.z = NPC_DISPOSE_POS_Z;
        }
    } else {
        effect = player->icePillarEffect;
        if (effect != NULL) {
            effect->flags |= FX_INSTANCE_FLAG_DISMISS;
            player->icePillarEffect = NULL;
        }
    }

    if (!(gBattleStatus.flags2 & BS_FLAGS2_HIDE_BUFF_COUNTERS)
        && !(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN)
        && (player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)
    ) {
        battleStatus->buffEffect->data.partnerBuff->visible = TRUE;
    } else {
        battleStatus->buffEffect->data.partnerBuff->visible = FALSE;
    }

    do {
        if (player->debuff == STATUS_KEY_SHRINK) {
            player->scalingFactor += (0.4 - player->scalingFactor) / 6.0;
        } else {
            player->scalingFactor += (1.0 - player->scalingFactor) / 6.0;
        }
    } while (0); // required to match

    if (player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS) {
        if (battleStatus->hammerCharge > 0) {
            create_status_icon_boost_hammer(player->hudElementDataIndex);
            remove_status_icon_boost_jump(player->hudElementDataIndex);
        } else {
            remove_status_icon_boost_hammer(player->hudElementDataIndex);
        }
        if (battleStatus->jumpCharge > 0) {
            create_status_icon_boost_jump(player->hudElementDataIndex);
            remove_status_icon_boost_hammer(player->hudElementDataIndex);
        } else {
            remove_status_icon_boost_jump(player->hudElementDataIndex);
        }
    } else {
        enable_status_icon_boost_jump(player->hudElementDataIndex);
        enable_status_icon_boost_hammer(player->hudElementDataIndex);
    }

    if ((player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS) && !(gBattleStatus.flags2 & BS_FLAGS2_PEACH_BATTLE)) {
        if (playerData->curHP > PERIL_THRESHOLD) {
            remove_status_icon_peril(player->hudElementDataIndex);
            do {
                if (playerData->curHP <= DANGER_THRESHOLD) {
                    create_status_icon_danger(player->hudElementDataIndex);
                    remove_status_icon_peril(player->hudElementDataIndex);
                } else {
                    remove_status_icon_danger(player->hudElementDataIndex);
                }
            } while (0); // required to match
        } else {
            create_status_icon_peril(player->hudElementDataIndex);
            remove_status_icon_danger(player->hudElementDataIndex);
        }
    } else {
        remove_status_icon_peril(player->hudElementDataIndex);
        remove_status_icon_danger(player->hudElementDataIndex);
    }

    if (player->transparentStatus == STATUS_KEY_TRANSPARENT) {
        playerParts->flags |= ACTOR_PART_FLAG_TRANSPARENT;

        if (FALSE) { // TODO required to match - also whyyyyyy compiler, whyyyyy
    back:
            playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_STOP);
            create_status_debuff(player->hudElementDataIndex, STATUS_KEY_STOP);
            goto end;
        }
    } else {
        playerParts->flags &= ~ACTOR_PART_FLAG_TRANSPARENT;
    }

    do {
        animChanged = FALSE;
        palChanged = FALSE;
        decorChanged = FALSE;
        cond4 = FALSE;
        lastAnim = playerParts->curAnimation;
    } while (0); // required to match

    if ((((!(gBattleStatus.flags2 & BS_FLAGS2_OVERRIDE_INACTIVE_PLAYER)
            && (gBattleStatus.flags2 & BS_FLAGS2_PLAYER_TURN_USED))
                && (partner != NULL))
            || (battleStatus->outtaSightActive > 0))
        && !(player->flags & ACTOR_FLAG_NO_INACTIVE_ANIM)
        && !((partner != NULL) && (partner->flags & ACTOR_FLAG_NO_ATTACK))
    ) {
        if (!(gBattleStatus.flags2 & BS_FLAGS2_NO_PLAYER_PAL_ADJUST)) {
            if ((player->debuff != STATUS_KEY_FEAR)
                && (player->debuff != STATUS_KEY_PARALYZE)
                && (player->debuff != STATUS_KEY_FROZEN)
                && (player->debuff != STATUS_KEY_STOP)
            ) {
                if ((player->transparentStatus != STATUS_KEY_TRANSPARENT) &&
                    (player->stoneStatus != STATUS_KEY_STONE) &&
                    ((battleStatus->outtaSightActive > 0) || (gBattleStatus.flags2 & BS_FLAGS2_PLAYER_TURN_USED)))
                {
                    if (is_ability_active(ABILITY_BERSERKER)) {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_INACTIVE_BERSERK);
                    } else  if (player->debuff == STATUS_KEY_SLEEP) {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_INACTIVE_SLEEP);
                    } else if (player->debuff == STATUS_KEY_DIZZY) {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_INACTIVE_DIZZY);
                    } else {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_INACTIVE);
                    }
                    spr_update_player_sprite(PLAYER_SPRITE_MAIN, playerParts->curAnimation, playerParts->animationRate);
                    animChanged = TRUE;
                }
            }

            if (player->debuff != STATUS_KEY_POISON) {
                set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_PLAYER_DEBUFF);
            } else {
                set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_PLAYER_POISON);
            }
            palChanged = TRUE;

            set_actor_glow_pal(player, GLOW_PAL_OFF);
            decorChanged = TRUE;
        }
    }

    if (player->stoneStatus == STATUS_KEY_STONE) {
        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_STONE);
        spr_update_player_sprite(PLAYER_SPRITE_MAIN, playerParts->curAnimation, playerParts->animationRate);
        animChanged = TRUE;

        if (!palChanged) {
            set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_NONE);
        }
        set_actor_glow_pal(player, GLOW_PAL_OFF);
        palChanged = TRUE;
        enable_status_debuff(player->hudElementDataIndex);
        decorChanged = TRUE;
        enable_status_static(player->hudElementDataIndex);
        cond4 = TRUE;

        enable_status_transparent(player->hudElementDataIndex);
        enable_status_chill_out(player->hudElementDataIndex);
    }

    if ((player->flags & ACTOR_FLAG_USING_IDLE_ANIM) && !animChanged) {
        s32 temp = playerParts->curAnimation;
        if (temp == get_player_anim_for_status(STATUS_KEY_STONE)) {
            playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_NORMAL);
        }
    }

    if (is_ability_active(ABILITY_BERSERKER)) {
        if (!palChanged) {
            set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_BERSERK);
        }
        palChanged = TRUE;
    }
    if (player->debuff == STATUS_KEY_POISON) {
        if (!palChanged) {
            set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_POISON);
        }
        palChanged = TRUE;
    }
    if (player->debuff == STATUS_KEY_PARALYZE) {
        if (!palChanged) {
            set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_PARALYZE);
        }
        palChanged = TRUE;
    }
    if (player->staticStatus == STATUS_KEY_STATIC) {
        if (!palChanged) {
            set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_STATIC);
        }
        palChanged = TRUE;
    }
    if (battleStatus->turboChargeTurnsLeft != 0) {
        if (!decorChanged) {
            set_actor_glow_pal(player, GLOW_PAL_ON);
        }
        decorChanged = TRUE;
    }
    if (is_ability_active(ABILITY_ZAP_TAP)) {
        if (!palChanged) {
            set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_STATIC);
        }
        palChanged = TRUE;
    }
    if (!palChanged) {
        set_actor_pal_adjustment(player, ACTOR_PAL_ADJUST_NONE);
    }
    if (!decorChanged) {
        set_actor_glow_pal(player, GLOW_PAL_OFF);
    }
    if (player->flags & ACTOR_FLAG_USING_IDLE_ANIM) {
        if (battleStatus->hustleTurns != 0) {
            playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_HUSTLE);
            animChanged = TRUE;
        } else if (!animChanged) {
            s32 temp = get_player_anim_for_status(STATUS_KEY_NORMAL);
            do {
                if (temp == get_player_anim_for_status(STATUS_KEY_HUSTLE)) {
                    playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_NORMAL);
                }
            } while (0); // required to match
        }

        do {
            if (player->debuff == STATUS_KEY_FROZEN) {
                if (!animChanged) {
                    playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_FROZEN);
                    animChanged = TRUE;
                }
            } else if (player->debuff != STATUS_KEY_SHRINK) {
                if (player->debuff == STATUS_KEY_POISON) {
                    if (!animChanged) {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_POISON);
                        animChanged = TRUE;
                    }
                } else if (player->debuff == STATUS_KEY_DIZZY) {
                    if (!animChanged) {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_DIZZY);
                        animChanged = TRUE;
                    }
                } else if (player->debuff == STATUS_KEY_SLEEP) {
                    if (!animChanged) {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_SLEEP);
                        animChanged = TRUE;
                    }
                } else if (player->debuff == STATUS_KEY_PARALYZE) {
                    if (!animChanged) {
                        playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_PARALYZE);
                        animChanged = TRUE;
                    }
                 } else {
                    if (player_team_is_ability_active(player, ABILITY_BERSERKER)) {
                        if (!animChanged) {
                            playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_BERSERK);
                            animChanged = TRUE;
                        }
                    }
                }
            }
            if (is_ability_active(ABILITY_ZAP_TAP)) {
                if (!animChanged) {
                    playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_STATIC);
                    animChanged = TRUE;
                }
                player->staticStatus = STATUS_KEY_STATIC;
                player->staticDuration = 127;
            } else if ((player->staticStatus == STATUS_KEY_STATIC) && !animChanged) {
                playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_STATIC);
                animChanged = TRUE;
            }
            if ((player->transparentStatus == STATUS_KEY_TRANSPARENT) || (playerParts->flags & ACTOR_PART_FLAG_TRANSPARENT)) {
                if (!animChanged) {
                    playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_TRANSPARENT);
                    animChanged = TRUE;
                }
                create_status_transparent(player->hudElementDataIndex, STATUS_KEY_TRANSPARENT);
            }
            if (!animChanged) {
                playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_NORMAL);
            }
        } while (0); // needed to match
    }

    if (!(gBattleStatus.flags1 & BS_FLAGS1_TATTLE_OPEN) && (player->flags & ACTOR_FLAG_SHOW_STATUS_ICONS)) {
        if (!cond4) {
            do {
                if (player->debuff == STATUS_KEY_POISON) {
                    create_status_debuff(player->hudElementDataIndex, STATUS_KEY_POISON);
                } else if (player->debuff == STATUS_KEY_SLEEP) {
                    create_status_debuff(player->hudElementDataIndex, STATUS_KEY_SLEEP);
                } else if (player->debuff == STATUS_KEY_PARALYZE) {
                    create_status_debuff(player->hudElementDataIndex, STATUS_KEY_PARALYZE);
                } else if (player->debuff == STATUS_KEY_DIZZY) {
                    create_status_debuff(player->hudElementDataIndex, STATUS_KEY_DIZZY);
                } else if (player->debuff == STATUS_KEY_SHRINK) {
                    create_status_debuff(player->hudElementDataIndex, STATUS_KEY_SHRINK);
                } else if (player->debuff == STATUS_KEY_FROZEN) {
                    create_status_debuff(player->hudElementDataIndex, STATUS_KEY_FROZEN);
                }
            } while (0); // required to match
            if (!cond4 && (is_ability_active(ABILITY_ZAP_TAP) || (player->staticStatus == STATUS_KEY_STATIC))) {
                create_status_static(player->hudElementDataIndex, STATUS_KEY_STATIC);
            }
        }
        if ((player->transparentStatus == STATUS_KEY_TRANSPARENT) || (playerParts->flags & ACTOR_PART_FLAG_TRANSPARENT)) {
            create_status_transparent(player->hudElementDataIndex, STATUS_KEY_TRANSPARENT);
        }
    } else {
        enable_status_debuff(player->hudElementDataIndex);
        enable_status_static(player->hudElementDataIndex);
        enable_status_transparent(player->hudElementDataIndex);
        enable_status_chill_out(player->hudElementDataIndex);
    }

    if (player->debuff != STATUS_KEY_STOP) {
        if (!animChanged) {
            s32 temp = playerParts->curAnimation;
            if (temp == get_player_anim_for_status(STATUS_KEY_STOP)) {
                playerParts->curAnimation = get_player_anim_for_status(STATUS_KEY_NORMAL);
            }
        }
    } else {
        goto back;
    }

end:
    set_status_icons_properties(player->hudElementDataIndex,
        playerPosX, playerPosY, playerPosZ,
        player->actorBlueprint->statusIconOffset.x * player->scalingFactor,
        player->actorBlueprint->statusIconOffset.y * player->scalingFactor,
        player->actorBlueprint->statusTextOffset.x * player->scalingFactor,
        player->actorBlueprint->statusTextOffset.y * player->scalingFactor);
    set_status_icons_offset(player->hudElementDataIndex,
        player->size.y * player->scalingFactor,
        player->size.x * player->scalingFactor);

    playerPosX += playerParts->palAnimPosOffset[0];
    playerPosY += playerParts->palAnimPosOffset[1];

    playerParts->curPos.x = playerPosX;
    playerParts->curPos.y = playerPosY;
    playerParts->curPos.z = playerPosZ;
    guTranslateF(mtxTranslate, playerPosX, playerPosY, playerPosZ);

    guTranslateF(mtxPivotOn,
        -player->rotPivotOffset.x * player->scalingFactor,
        -player->rotPivotOffset.y * player->scalingFactor,
        -player->rotPivotOffset.z * player->scalingFactor);
    guTranslateF(mtxPivotOff,
        player->rotPivotOffset.x * player->scalingFactor,
        player->rotPivotOffset.y * player->scalingFactor,
        player->rotPivotOffset.z * player->scalingFactor);

    guRotateF(mtxRotX, player->rot.x, 1.0f, 0.0f, 0.0f);
    guRotateF(mtxRotY, player->rot.y, 0.0f, 1.0f, 0.0f);
    guRotateF(mtxRotZ, player->rot.z, 0.0f, 0.0f, 1.0f);
    guMtxCatF(mtxRotY, mtxRotX, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);

    guScaleF(mtxScale,
        player->scale.x * SPRITE_WORLD_SCALE_D * player->scalingFactor,
        player->scale.y * SPRITE_WORLD_SCALE_D * player->scalingFactor * playerParts->verticalStretch,
        player->scale.z * SPRITE_WORLD_SCALE_D);

    guMtxCatF(mtxScale, mtxPivotOn, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotation, mtxTransform);
    guMtxCatF(mtxTransform, mtxPivotOff, mtxTemp);
    guMtxCatF(mtxTemp, mtxTranslate, mtxTransform);

    if (lastAnim != playerParts->curAnimation) {
        spr_update_player_sprite(PLAYER_SPRITE_MAIN, playerParts->curAnimation, playerParts->animationRate);
    }
    update_part_glow(FALSE, playerParts, clamp_angle(playerYaw + 180.0f), FALSE);
    update_part_flash(FALSE, playerParts, clamp_angle(playerYaw + 180.0f), FALSE);
    render_with_adjusted_palettes(SPRITE_MODE_PLAYER, playerParts, clamp_angle(playerYaw + 180.0f), mtxTransform, 0);
    _add_part_decoration(playerParts);
}

void appendGfx_player_actor_reflection(void* arg0) {
    Matrix4f mtxRotX, mtxRotY, mtxRotZ, mtxRotation, mtxScale;
    Matrix4f mtxPivotOn, mtxPivotOff, mtxTranslate;
    Matrix4f mtxTemp, mtxTransform, mtxMirror;
    Actor* player = gBattleStatus.playerActor;
    ActorPart* part = &player->partsTable[0];
    f32 playerYaw = player->yaw;
    f32 dx, dy, dz;

    dx = player->curPos.x + player->headOffset.x;
    dx += part->palAnimPosOffset[0];
    dy = player->curPos.y + player->headOffset.y;
    dy += part->palAnimPosOffset[1];
    dz = player->curPos.z + player->headOffset.z - 5.0f;
    part->yaw = playerYaw;

    guTranslateF(mtxTranslate, dx, dy, dz - 1.0f);

    guTranslateF(mtxPivotOn,
        -player->rotPivotOffset.x * player->scalingFactor,
        -player->rotPivotOffset.y * player->scalingFactor,
        -player->rotPivotOffset.z * player->scalingFactor);
    guTranslateF(mtxPivotOff,
        player->rotPivotOffset.x * player->scalingFactor,
        player->rotPivotOffset.y * player->scalingFactor,
        player->rotPivotOffset.z * player->scalingFactor);

    guRotateF(mtxRotX, player->rot.x, 1.0f, 0.0f, 0.0f);
    guRotateF(mtxRotY, player->rot.y, 0.0f, 1.0f, 0.0f);
    guRotateF(mtxRotZ, player->rot.z, 0.0f, 0.0f, 1.0f);
    guMtxCatF(mtxRotY, mtxRotX, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotZ, mtxRotation);
    guScaleF(mtxScale,
        player->scale.x * SPRITE_WORLD_SCALE_D * player->scalingFactor,
        player->scale.y * SPRITE_WORLD_SCALE_D * player->scalingFactor * part->verticalStretch,
        player->scale.z * SPRITE_WORLD_SCALE_D);
    mtx_mirror_y(mtxMirror);

    guMtxCatF(mtxScale, mtxPivotOn, mtxTemp);
    guMtxCatF(mtxTemp, mtxRotation, mtxTemp);
    guMtxCatF(mtxTemp, mtxPivotOff, mtxTemp);
    guMtxCatF(mtxTemp, mtxTranslate, mtxTransform);
    guMtxCatF(mtxTransform, mtxMirror, mtxTransform);

    update_part_glow(FALSE, part, clamp_angle(playerYaw + 180.0f), TRUE);
    update_part_flash(FALSE, part, clamp_angle(playerYaw + 180.0f), TRUE);
    render_with_adjusted_palettes(SPRITE_MODE_PLAYER, part, clamp_angle(playerYaw + 180.0f), mtxTransform, TRUE);
}

s32 render_with_adjusted_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    s32 opacity;
    s32 sprDrawOpts;

    if (part->flags & ACTOR_PART_FLAG_NO_DECORATIONS) {
        opacity = 255;
        sprDrawOpts = 0;
        if (part->opacity < 255) {
            sprDrawOpts = DRAW_SPRITE_OVERRIDE_ALPHA;
            opacity = part->opacity;
        }
        if (part->flags & ACTOR_PART_FLAG_TRANSPARENT) {
            sprDrawOpts = DRAW_SPRITE_OVERRIDE_ALPHA;
            opacity = opacity * 120 / 255;
        }
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            if (opacity == 255) {
                spr_draw_player_sprite(PLAYER_SPRITE_MAIN, yaw, 0, NULL, mtx);
            } else {
                spr_draw_player_sprite(PLAYER_SPRITE_MAIN | sprDrawOpts, yaw, opacity, NULL, mtx);
            }
        } else {
            if (opacity == 255) {
                spr_draw_npc_sprite(part->spriteInstanceID, yaw, 0, NULL, mtx);
            } else {
                spr_draw_npc_sprite(part->spriteInstanceID | sprDrawOpts, yaw, opacity, NULL, mtx);
            }
        }
        return 0;
    }

    switch (part->decorationTable->paletteAdjustment) {
        case ACTOR_PAL_ADJUST_NONE:
            render_without_adjusted_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_SLEEP:
            render_with_sleep_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_STATIC:
            render_with_static_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_FEAR:
            render_with_fear_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_POISON:
            render_with_poison_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_PARALYZE:
            render_with_paralyze_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_BERSERK:
            render_with_berserk_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_WATT_IDLE:
            render_with_watt_idle_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_WATT_ATTACK:
            render_with_watt_attack_palettes(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_PLAYER_DEBUFF:
            render_with_player_debuff_palettes(isNpcSprite, part, yaw, mtx, skipAnimation, FALSE);
            break;
        case ACTOR_PAL_ADJUST_PLAYER_POISON:
            render_with_player_debuff_palettes(isNpcSprite, part, yaw, mtx, skipAnimation, TRUE);
            break;
        case ACTOR_PAL_ADJUST_BLEND_PALETTES_UNIFORM_INTERVALS:
            render_with_pal_blending(isNpcSprite, part, yaw, FALSE, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_BLEND_PALETTES_VARYING_INTERVALS:
            render_with_pal_blending(isNpcSprite, part, yaw, TRUE, mtx, skipAnimation);
            break;
        case ACTOR_PAL_ADJUST_BLEND_PALSETS:
            render_with_palset_blending(isNpcSprite, part, yaw, mtx, skipAnimation);
            break;
        default:
            break;
    }
    return 0;
}

void make_flash_palettes(ActorPart* part) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR src;
    PAL_PTR dest;
    s32 i, j;

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        if (decorations->adjustedPalettes[i] != NULL) {
            src = decorations->adjustedPalettes[i];
            dest = decorations->copiedPalettes[1][i];

            for (j = 0; j < SPR_PAL_SIZE; j++) {
                *dest = *src | 0xFFFE; // pure white, not affecting alpha bit
                src++;
                dest++;

            }
            decorations->flashPalettes[i] = decorations->copiedPalettes[1][i];
        }
    }
}

void func_unkA_draw_npc(ActorPart* part, s32 yaw, Matrix4f mtx) {
    DecorationTable* decorations = part->decorationTable;
    s32 opacity = 255;
    s32 idMask = 0;
    PAL_PTR dest;
    PAL_PTR src;
    s32 i, j;

    if (part->opacity < 255) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = part->opacity;
    }
    if (part->flags & ACTOR_PART_FLAG_TRANSPARENT) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = (opacity * 120) / 255;
    }

    if (decorations->flashEnabled != FLASH_PAL_OFF) {
        decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 0x10);
        decorations->originalPalettesCount = 0;
        while (decorations->originalPalettesList[decorations->originalPalettesCount] != (PAL_PTR) -1) {
            decorations->originalPalettesCount++;
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            src = decorations->originalPalettesList[i];
            dest = decorations->copiedPalettes[0][i];
            if (src != NULL) {
                for (j = 0; j < ARRAY_COUNT(decorations->copiedPalettes[0][i]); j++) {
                    *dest = *src;
                    src++;
                    dest++;
                }
            }
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
        }

        func_unkB_draw_npc(part, yaw, mtx);
    } else {
        spr_draw_npc_sprite(part->spriteInstanceID | idMask, yaw, opacity, NULL, mtx);
    }
}

void func_unkB_draw_npc(ActorPart* part, s32 yaw, Matrix4f mtx) {
    DecorationTable* decorations = part->decorationTable;
    s32 opacity = 255;
    s32 idMask = 0;

    if (part->opacity < 255) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = part->opacity;
    }

    if (part->flags & ACTOR_PART_FLAG_TRANSPARENT) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = (opacity * 120) / 255;
    }

    if (decorations->flashEnabled != FLASH_PAL_OFF) {
        make_flash_palettes(part);
        spr_draw_npc_sprite(part->spriteInstanceID | DRAW_SPRITE_OVERRIDE_PALETTES | idMask, yaw, opacity, decorations->flashPalettes, mtx);
    } else {
        spr_draw_npc_sprite(part->spriteInstanceID | DRAW_SPRITE_OVERRIDE_PALETTES | idMask, yaw, opacity, decorations->adjustedPalettes, mtx);
    }
}

void func_unkA_draw_player(ActorPart* part, s32 yaw, Matrix4f mtx) {
    DecorationTable* decorations = part->decorationTable;
    s32 opacity = 255;
    s32 idMask = 0;
    PAL_PTR src;
    PAL_PTR dest;
    s32 i, j;

    if (part->opacity < 255) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = part->opacity;
    }
    if (part->flags & ACTOR_PART_FLAG_TRANSPARENT) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = (opacity * 120) / 255;
    }

    if (decorations->flashEnabled != FLASH_PAL_OFF) {
        decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
        decorations->originalPalettesCount = 0;

        while (decorations->originalPalettesList[decorations->originalPalettesCount] != (PAL_PTR) -1) {
            decorations->originalPalettesCount++;
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            src = decorations->originalPalettesList[i];
            dest = decorations->copiedPalettes[0][i];
            if (decorations->originalPalettesList[i] != NULL) {
                for (j = 0; j < ARRAY_COUNT(decorations->copiedPalettes[0][i]); j++) {
                    *dest = *src;
                    dest++;
                    src++;
                }
            }
        }
        for (i = 0; i < decorations->originalPalettesCount; i++) {
            decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
        }
        func_unkB_draw_player(part, yaw, mtx);
    } else {
        spr_draw_player_sprite(PLAYER_SPRITE_MAIN | idMask, yaw, opacity, NULL, mtx);
    }
}

void func_unkB_draw_player(ActorPart* part, s32 yaw, Matrix4f mtx) {
    DecorationTable* decorations = part->decorationTable;
    s32 opacity = 255;
    s32 idMask = 0;

    if (part->opacity < 255) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = part->opacity;
    }
    if (part->flags & ACTOR_PART_FLAG_TRANSPARENT) {
        idMask = DRAW_SPRITE_OVERRIDE_ALPHA;
        opacity = (opacity * 120) / 255;
    }

    if (decorations->flashEnabled != FLASH_PAL_OFF) {
        make_flash_palettes(part);
        idMask |= DRAW_SPRITE_OVERRIDE_PALETTES;
        spr_draw_player_sprite(PLAYER_SPRITE_MAIN | idMask, yaw, opacity, decorations->flashPalettes, mtx);
    } else {
        idMask |= DRAW_SPRITE_OVERRIDE_PALETTES;
        spr_draw_player_sprite(PLAYER_SPRITE_MAIN | idMask, yaw, opacity, decorations->adjustedPalettes, mtx);
    }
}

void render_without_adjusted_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;

    if (decorations->resetPalAdjust) {
        part->verticalStretch = 1;
        part->palAnimPosOffset[0] = 0;
        part->palAnimPosOffset[1] = 0;
        decorations->resetPalAdjust = FALSE;
    }
    if (isNpcSprite == SPRITE_MODE_PLAYER) {
        func_unkA_draw_player(part, yaw, mtx);
    } else {
        func_unkA_draw_npc(part, yaw, mtx);
    }
}

void render_with_sleep_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    s32 i, j;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        }
        decorations->palAnimState = 0;
        decorations->resetPalAdjust = FALSE;
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        PAL_PTR palIn = decorations->originalPalettesList[i];
        PAL_PTR palOut = decorations->copiedPalettes[0][i];
        decorations->adjustedPalettes[i] = palOut;
        if (palIn != NULL) {
            for (j = 0; j < SPR_PAL_SIZE; j++) {
                u8 r = UNPACK_PAL_R(*palIn);
                u8 g = UNPACK_PAL_G(*palIn);
                u8 b = UNPACK_PAL_B(*palIn);
                u8 a = UNPACK_PAL_A(*palIn);
                palIn++;

                // make colors darker and bluer
                r *= 0.2;
                g *= 0.4;
                b *= 0.7;

                *palOut++ = PACK_PAL_RGBA(r, g, b, a);
            }
        }
    }
    switch (decorations->palAnimState) {
        case 0:
        case 1:
            if (isNpcSprite == SPRITE_MODE_PLAYER) {
                func_unkB_draw_player(part, yaw, mtx);
            } else {
                func_unkB_draw_npc(part, yaw, mtx);
            }
            break;
    }
}

void render_with_static_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR palIn;
    PAL_PTR palOut;
    s32 i, j;
    s32 paletteType;
    s32 staticPalIdx;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = SPR_PLAYER_COLOR_VARIATIONS;
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = spr_get_npc_color_variations(part->curAnimation >> 16);
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            palIn = decorations->originalPalettesList[i];
            palOut = decorations->copiedPalettes[0][i];
            if (palIn != NULL) {
                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    *palOut++ = *palIn++;
                }
            }
        }

        decorations->palAnimState = -2;
        decorations->palBlendAlpha = 0;
        decorations->resetPalAdjust = FALSE;
        decorations->nextPalTime = 0;
    }

    if (!skipAnimation) {
        if (decorations->nextPalTime == 0) {
            decorations->palAnimState += 2;
            if (StaticPalettesAnim[decorations->palAnimState] == PAL_ANIM_END) {
                decorations->palAnimState = 0;
            }
            decorations->nextPalTime = StaticPalettesAnim[decorations->palAnimState + 1] / 2;
        }
        paletteType = StaticPalettesAnim[decorations->palAnimState];
        decorations->nextPalTime--;
    } else {
        paletteType = StaticPalettesAnim[decorations->palAnimState];
    }

    switch (paletteType) {
        case STATIC_DEFAULT: // no change
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                palIn = decorations->originalPalettesList[i];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
        case STATIC_BRIGHT: // bright yellow
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                staticPalIdx = decorations->spriteColorVariations * STANDARD_PAL_STATIC + i;
                palIn = decorations->originalPalettesList[staticPalIdx];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
        case STATIC_DARK: // darkened via code
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                palIn = decorations->originalPalettesList[i];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        u8 r = UNPACK_PAL_R(*palIn);
                        u8 g = UNPACK_PAL_G(*palIn);
                        u8 b = UNPACK_PAL_B(*palIn);
                        u8 a = UNPACK_PAL_A(*palIn);
                        palIn++;

                        r *= 0.1;
                        g *= 0.1;
                        b *= 0.1;

                        *palOut++ = PACK_PAL_RGBA(r, g, b, a);
                    }
                }
            }
            break;
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
    }

    if (isNpcSprite == SPRITE_MODE_PLAYER) {
        func_unkB_draw_player(part, yaw, mtx);
    } else {
        func_unkB_draw_npc(part, yaw, mtx);
    }

    if (!skipAnimation) {
        decorations->palBlendAlpha--;
    }
}

void render_with_fear_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR palIn;
    PAL_PTR palOut;
    s32 i, j;
    s32 temp;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 2;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        }

        decorations->palAnimState = 0;
        decorations->palBlendAlpha = 0;
        decorations->nextPalTime = 0;
        decorations->resetPalAdjust = FALSE;
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        palIn = decorations->originalPalettesList[i];
        palOut = decorations->copiedPalettes[0][i];
        decorations->adjustedPalettes[i] = palOut;
        if (palIn != NULL) {
            for (j = 0; j < SPR_PAL_SIZE; j++) {
                u8 r = UNPACK_PAL_R(*palIn);
                u8 g = UNPACK_PAL_G(*palIn);
                u8 b = UNPACK_PAL_B(*palIn);
                u8 a = UNPACK_PAL_A(*palIn);
                palIn++;

                // darken the color
                r /= 2;
                g /= 2;
                b /= 2;

                *palOut++ = PACK_PAL_RGBA(r, g, b, a);
            }
        }
    }

    if (decorations->nextPalTime <= 0) {
        part->palAnimPosOffset[0] = FearPaletteAnimXOffsets[abs(decorations->nextPalTime)];
        if (part->palAnimPosOffset[0] == PAL_ANIM_END) {
            part->palAnimPosOffset[0] = 0;
            // 30-90
            decorations->nextPalTime = rand_int(60) + 30;
        }
    }

    if (!skipAnimation) {
        decorations->nextPalTime--;
    }

    if (isNpcSprite == SPRITE_MODE_PLAYER) {
        func_unkB_draw_player(part, yaw, mtx);
    } else {
        func_unkB_draw_npc(part, yaw, mtx);
    }
}

void render_with_poison_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR palIn;
    PAL_PTR palOut;
    s32 i, j;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = SPR_PLAYER_COLOR_VARIATIONS;
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = spr_get_npc_color_variations(part->curAnimation >> 16);
        }

        decorations->palAnimState = 0;
        decorations->palBlendAlpha = 0;
        decorations->resetPalAdjust = FALSE;
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        palIn = decorations->originalPalettesList[i];
        palOut = decorations->copiedPalettes[0][i];
        if (palIn != NULL) {
            for (j = 0; j < SPR_PAL_SIZE; j++) {
                *palOut++ = *palIn++;
            }
        }
    }
    for (i = 0; i < decorations->spriteColorVariations; i++) {
        palIn = decorations->originalPalettesList[decorations->spriteColorVariations + i];
        palOut = decorations->copiedPalettes[0][i];
        if (palIn != NULL) {
            for (j = 0; j < SPR_PAL_SIZE; j++) {
                *palOut++ = *palIn++;
            }
        }
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
    }

    if (isNpcSprite == SPRITE_MODE_PLAYER) {
        func_unkB_draw_player(part, yaw, mtx);
    } else {
        func_unkB_draw_npc(part, yaw, mtx);
    }
}

void render_with_paralyze_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR palIn;
    PAL_PTR palOut;
    s32 i, j;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        }

        decorations->palAnimState = 0;
        decorations->nextPalTime = 0;
        decorations->palBlendAlpha = 10;
        decorations->resetPalAdjust = FALSE;
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        palIn = decorations->originalPalettesList[i];
        palOut = decorations->copiedPalettes[0][i];
        if (palIn != NULL) {
            for (j = 0; j < SPR_PAL_SIZE; j++) {
                u8 r = UNPACK_PAL_R(*palIn);
                u8 g = UNPACK_PAL_G(*palIn);
                u8 b = UNPACK_PAL_B(*palIn);
                u8 a = UNPACK_PAL_A(*palIn);
                palIn++;
                r += 4;
                if (r > 31) {
                    r = 31;
                }
                g += 4;
                if (g > 31) {
                    g = 31;
                }
                b += 4;
                if (b > 31) {
                    b = 31;
                }

                *palOut++ = PACK_PAL_RGBA(r, g, b, a);
            }
        }
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
    }

    switch (decorations->palAnimState) {
        case 0:
        case 1:
            if (decorations->nextPalTime <= 0) {
                part->palAnimPosOffset[1] = ParalyzePaletteAnimXOffsets[abs(decorations->nextPalTime)];
                if (part->palAnimPosOffset[1] == PAL_ANIM_END) {
                    part->palAnimPosOffset[1] = 0;
                    decorations->nextPalTime = rand_int(60) + 30; // 30-90
                }
            }

            if (!skipAnimation) {
                decorations->nextPalTime--;
            }

            switch (decorations->palBlendAlpha) {
                case 10:
                case 12:
                    if (isNpcSprite == SPRITE_MODE_PLAYER) {
                        func_unkB_draw_player(part, yaw, mtx);
                    } else {
                        func_unkB_draw_npc(part, yaw, mtx);
                    }
                    break;
                case 13:
                    decorations->palBlendAlpha = 0;
                    // fallthrough
                default:
                    if (isNpcSprite == SPRITE_MODE_PLAYER) {
                        func_unkA_draw_player(part, yaw, mtx);
                    } else {
                        func_unkA_draw_npc(part, yaw, mtx);
                    }
                    break;
            }

            if (!skipAnimation) {
                decorations->palBlendAlpha++;
            }
            break;
    }
}

void render_with_berserk_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    s32 i, j;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->palAnimState = 0;
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->palAnimState = 0;
        }
        decorations->palBlendAlpha = 0;
        decorations->palAnimState = 0;
        decorations->resetPalAdjust = FALSE;
    }

    // adjust each palette
    for (i = 0; i < decorations->originalPalettesCount; i++) {
        PAL_PTR palIn = decorations->originalPalettesList[i];
        PAL_PTR palOut = decorations->copiedPalettes[0][i];
        if (palIn != NULL) {
            for (j = 0; j < SPR_PAL_SIZE; j++) {
                u8 r = UNPACK_PAL_R(*palIn);
                u8 g = UNPACK_PAL_G(*palIn);
                u8 b = UNPACK_PAL_B(*palIn);
                u8 a = UNPACK_PAL_A(*palIn);
                palIn++;

                // make each color darker and redder
                r *= 0.8;
                g *= 0.6;
                b *= 0.1;

                *palOut++ = PACK_PAL_RGBA(r, g, b, a);
            }
        }
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
    }

    if (isNpcSprite == SPRITE_MODE_PLAYER) {
        func_unkB_draw_player(part, yaw, mtx);
    } else {
        func_unkB_draw_npc(part, yaw, mtx);
    }
}

void render_with_watt_idle_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR palIn;
    PAL_PTR palOut;
    s32 i, j;
    s32 palIdx;
    s32 brightnessLevel;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = SPR_PLAYER_COLOR_VARIATIONS;
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = spr_get_npc_color_variations(part->curAnimation >> 16);
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            palIn = decorations->originalPalettesList[i];
            palOut = decorations->copiedPalettes[0][i];
            if (palIn != NULL) {
                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    *palOut++ = *palIn++;
                }
            }
        }

        decorations->palAnimState = -2;
        decorations->palBlendAlpha = 0;
        decorations->resetPalAdjust = FALSE;
        decorations->nextPalTime = 0;
    }

    if (!skipAnimation) {
        if (decorations->nextPalTime == 0) {
            decorations->palAnimState += 2;
            if (bWattIdlePalettesAnim[decorations->palAnimState] == PAL_ANIM_END) {
                decorations->palAnimState = 0;
            }
            decorations->nextPalTime = bWattIdlePalettesAnim[decorations->palAnimState + 1] / 2;
        }
        brightnessLevel = bWattIdlePalettesAnim[decorations->palAnimState];
        decorations->nextPalTime--;
    } else {
        //@bug if only called with skipAnimation set, palAnimPos will always be -2 and the array access is OOB
        brightnessLevel = bWattIdlePalettesAnim[decorations->palAnimState];
    }

    switch (brightnessLevel) {
        case WATT_DEFAULT:
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                // use watt's base palettes
                palIn = decorations->originalPalettesList[i];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
        case WATT_BRIGHTEST:
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                // use watt's Brightest palettes
                palIdx = decorations->spriteColorVariations * SPR_PAL_BattleWatt_Brightest + i;
                palIn = decorations->originalPalettesList[palIdx];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
        case WATT_BRIGHTER:
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                // use watt's Brighter palettes
                palIdx = decorations->spriteColorVariations * SPR_PAL_BattleWatt_Brighter + i;
                palIn = decorations->originalPalettesList[palIdx];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
    }

    if (isNpcSprite == SPRITE_MODE_PLAYER) {
        func_unkB_draw_player(part, yaw, mtx);
    } else {
        func_unkB_draw_npc(part, yaw, mtx);
    }

    if (!skipAnimation) {
        decorations->palBlendAlpha--;
    }
}

void render_with_watt_attack_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR palIn;
    PAL_PTR palOut;
    s32 i, j;
    s32 palIdx;
    s32 brightness;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = SPR_PLAYER_COLOR_VARIATIONS;
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = spr_get_npc_color_variations(part->curAnimation >> 16);
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            palIn = decorations->originalPalettesList[i];
            palOut = decorations->copiedPalettes[0][i];
            if (palIn != NULL) {
                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    *palOut++ = *palIn++;
                }
            }
        }

        decorations->palAnimState = -2;
        decorations->palBlendAlpha = 0;
        decorations->resetPalAdjust = FALSE;
        decorations->nextPalTime = 0;
    }

    if (!skipAnimation) {
        if (decorations->nextPalTime == 0) {
            decorations->palAnimState += 2;
            if (WattAttackPalettesAnim[decorations->palAnimState] == PAL_ANIM_END) {
                decorations->palAnimState = 0;
            }
            decorations->nextPalTime = WattAttackPalettesAnim[decorations->palAnimState + 1] / 2;
        }
        brightness = WattAttackPalettesAnim[decorations->palAnimState];
        decorations->nextPalTime--;
    } else {
        //@bug if only called with skipAnimation set, palAnimPos will always be -2 and the array access is OOB
        brightness = WattAttackPalettesAnim[decorations->palAnimState];
    }

    switch (brightness) {
        case WATT_DEFAULT:
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                palIn = decorations->originalPalettesList[i];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
        case WATT_BRIGHTEST:
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                // use watt's Brightest palettes
                palIdx = decorations->spriteColorVariations * SPR_PAL_BattleWatt_Brightest + i;
                palIn = decorations->originalPalettesList[palIdx];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
        case WATT_BRIGHTER:
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                // use watt's Brighter palettes
                palIdx = decorations->spriteColorVariations * SPR_PAL_BattleWatt_Brighter + i;
                palIn = decorations->originalPalettesList[palIdx];
                palOut = decorations->copiedPalettes[0][i];
                if (palIn != NULL) {
                    for (j = 0; j < SPR_PAL_SIZE; j++) {
                        *palOut++ = *palIn++;
                    }
                }
            }
            break;
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
    }

    if (isNpcSprite == SPRITE_MODE_PLAYER) {
        func_unkB_draw_player(part, yaw, mtx);
    } else {
        func_unkB_draw_npc(part, yaw, mtx);
    }

    if (!skipAnimation) {
        decorations->palBlendAlpha--;
    }
}

void render_with_player_debuff_palettes(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation, b32 isPoison) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR color2;
    PAL_PTR color1;
    PAL_PTR palOut;
    s32 i, j;
    u8 blendAlpha;

    if (decorations->resetPalAdjust) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }

            if (gBattleStatus.flags2 & BS_FLAGS2_PEACH_BATTLE) {
                decorations->spriteColorVariations = SPR_PEACH_BTL_PAL_STRIDE;
            } else {
                decorations->spriteColorVariations = SPR_PLAYER_COLOR_VARIATIONS;
            }
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = spr_get_npc_color_variations(part->curAnimation >> 16);
        }

        if (decorations->resetPalAdjust == TRUE) {
            decorations->palAnimState = 0;
            decorations->palBlendAlpha = 0;
        } else {
            decorations->palAnimState = 0;
            decorations->palBlendAlpha = 255;
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            color2 = decorations->originalPalettesList[i];
            color1 = decorations->copiedPalettes[0][i];
            decorations->adjustedPalettes[i] = color1;
            if (color2 != NULL) {
                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    *color1++ = *color2++;
                }
            }
        }

        if (isPoison) {
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                color2 = decorations->originalPalettesList[decorations->spriteColorVariations + i];
                palOut = decorations->copiedPalettes[0][i];
                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    *palOut++ = *color2++;
                }
            }
        }

        decorations->nextPalTime = 10;
        decorations->palBlendAlpha = 0;
        decorations->palAnimState = 0;
        decorations->resetPalAdjust = FALSE;
    }

    if (decorations->palAnimState == 0) {
        if (!skipAnimation && decorations->nextPalTime != 0) {
            decorations->nextPalTime--;
        } else {
            if (!skipAnimation) {
                decorations->palBlendAlpha += 2560;
                if (decorations->palBlendAlpha > 255 * 100) {
                    decorations->palBlendAlpha = 255 * 100;
                }
            }
            blendAlpha = decorations->palBlendAlpha / 100;
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                if (!isPoison) {
                    color2 = decorations->originalPalettesList[i];
                } else {
                    color2 = decorations->originalPalettesList[decorations->spriteColorVariations * STANDARD_PAL_POISON + i];
                }
                color1 = decorations->originalPalettesList[decorations->spriteColorVariations * STANDARD_PAL_DIZZY + i];
                palOut = decorations->copiedPalettes[0][i];

                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    u8 r2 = UNPACK_PAL_R(*color2);
                    u8 g2 = UNPACK_PAL_G(*color2);
                    u8 b2 = UNPACK_PAL_B(*color2);
                    u8 r1 = UNPACK_PAL_R(*color1);
                    u8 g1 = UNPACK_PAL_G(*color1);
                    u8 b1 = UNPACK_PAL_B(*color1);
                    u8 a1 = UNPACK_PAL_A(*color1);
                    color2++;
                    color1++;

                    r1 = LERP_COMPONENT(r2, r1, blendAlpha);
                    g1 = LERP_COMPONENT(g2, g1, blendAlpha);
                    b1 = LERP_COMPONENT(b2, b1, blendAlpha);

                    *palOut++ = PACK_PAL_RGBA(r1, g1, b1, a1);
                }
            }
            if (blendAlpha == 255) {
                decorations->palAnimState = 1;
            }
        }
    }

    for (i = 0; i < decorations->originalPalettesCount; i++) {
        decorations->adjustedPalettes[i] = decorations->copiedPalettes[0][i];
    }

    switch (decorations->palAnimState) {
        case 0:
        case 1:
            if (isNpcSprite == SPRITE_MODE_PLAYER) {
                func_unkB_draw_player(part, yaw, mtx);
            } else {
                func_unkB_draw_npc(part, yaw, mtx);
            }
            break;
    }
}

void render_with_pal_blending(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 hasDifferentIntervals, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR color1;
    PAL_PTR color2;
    PAL_PTR outColor;
    s32 i, j;
    u8 blendAlpha;
    u8 r2, g2, b2, a1;
    u8 r1, g1, b1;

    if (decorations->resetPalAdjust != 0) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while ((s32)decorations->originalPalettesList[decorations->originalPalettesCount] != -1) {
                decorations->originalPalettesCount++;
            }
        }

        if (decorations->resetPalAdjust == 1) {
            decorations->palAnimState = 0;
            decorations->palBlendAlpha = 0;
        } else {
            decorations->palAnimState = 0;
            decorations->palBlendAlpha = 255;
        }

        for (i = 0; i < decorations->originalPalettesCount; i++) {
            color2 = decorations->originalPalettesList[i];
            color1 = decorations->copiedPalettes[0][i];
            decorations->adjustedPalettes[i] = color1;
            if (color2 != NULL) {
                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    *color1++ = *color2++;
                }
            }
        }

        if (!hasDifferentIntervals) {
            decorations->palswapTimeAtoB = decorations->palswapTimeHoldA;
            decorations->palswapTimeHoldB = decorations->palswapTimeAtoB;
            decorations->palswapTimeBtoA = decorations->palswapTimeAtoB;
            decorations->palswapTimeHoldA = 0;
        }

        decorations->nextPalTime = decorations->palswapTimeHoldA;
        decorations->palBlendAlpha = 0;
        decorations->palAnimState = PAL_SWAP_HOLD_A;
        decorations->resetPalAdjust = FALSE;
    }

    // blending from A -> B
    switch (decorations->palAnimState) {
        case PAL_SWAP_HOLD_A:
            if (skipAnimation) {
                break;
            }
            if (decorations->nextPalTime != 0) {
                decorations->nextPalTime--;
                break;
            }
            decorations->palBlendAlpha = 0;
            decorations->palAnimState = PAL_SWAP_A_TO_B;
            // fallthrough
        case PAL_SWAP_A_TO_B:
            if (!skipAnimation) {
                decorations->palBlendAlpha += 25600 / decorations->palswapTimeAtoB;
                if (decorations->palBlendAlpha > 255 * 100) {
                    decorations->palBlendAlpha = 255 * 100;
                }
            }
            blendAlpha = decorations->palBlendAlpha / 100;
            // blend two palettes
            color2 = decorations->originalPalettesList[decorations->blendPalA];
            color1 = decorations->originalPalettesList[decorations->blendPalB];
            outColor = decorations->adjustedPalettes[0] = decorations->copiedPalettes[0][0];

            for (j = 0; j < SPR_PAL_SIZE; j++) {
                r2 = UNPACK_PAL_R(*color2);
                g2 = UNPACK_PAL_G(*color2);
                b2 = UNPACK_PAL_B(*color2);
                r1 = UNPACK_PAL_R(*color1);
                g1 = UNPACK_PAL_G(*color1);
                b1 = UNPACK_PAL_B(*color1);
                a1 = UNPACK_PAL_A(*color1);
                color2++;
                color1++;

                r1 = LERP_COMPONENT(r2, r1, blendAlpha);
                g1 = LERP_COMPONENT(g2, g1, blendAlpha);
                b1 = LERP_COMPONENT(b2, b1, blendAlpha);

                *outColor++ = PACK_PAL_RGBA(r1, g1, b1, a1);
            }

            if (blendAlpha == 255) {
                decorations->palAnimState = PAL_SWAP_HOLD_B;
                decorations->nextPalTime = decorations->palswapTimeHoldB;
            }
            break;
    }

    // blending from B -> A
    switch (decorations->palAnimState) {
        case PAL_SWAP_HOLD_B:
            if (skipAnimation) {
                break;
            }
            if (decorations->nextPalTime != 0) {
                decorations->nextPalTime--;
                break;
            }
            decorations->palBlendAlpha = 0;
            decorations->palAnimState = PAL_SWAP_B_TO_A;
            // fallthrough
        case PAL_SWAP_B_TO_A:
            if (!skipAnimation) {
                decorations->palBlendAlpha += 25600 / decorations->palswapTimeBtoA;
                if (decorations->palBlendAlpha > 255 * 100) {
                    decorations->palBlendAlpha = 255 * 100;
                }
            }
            blendAlpha = decorations->palBlendAlpha / 100;
            // blend two palettes
            color2 = decorations->originalPalettesList[decorations->blendPalB];
            color1 = decorations->originalPalettesList[decorations->blendPalA];
            outColor = decorations->copiedPalettes[0][0];
            decorations->adjustedPalettes[0] = outColor;

            for (j = 0; j < SPR_PAL_SIZE; j++) {
                r2 = UNPACK_PAL_R(*color2);
                g2 = UNPACK_PAL_G(*color2);
                b2 = UNPACK_PAL_B(*color2);
                r1 = UNPACK_PAL_R(*color1);
                g1 = UNPACK_PAL_G(*color1);
                b1 = UNPACK_PAL_B(*color1);
                a1 = UNPACK_PAL_A(*color1);
                color2++;
                color1++;

                r1 = LERP_COMPONENT(r2, r1, blendAlpha);
                g1 = LERP_COMPONENT(g2, g1, blendAlpha);
                b1 = LERP_COMPONENT(b2, b1, blendAlpha);

                *outColor++ = PACK_PAL_RGBA(r1, g1, b1, a1);
            }
            if (blendAlpha == 255) {
                decorations->palAnimState = PAL_SWAP_HOLD_A;
                decorations->nextPalTime = decorations->palswapTimeHoldA;
            }
            break;
    }

    switch (decorations->palAnimState) {
        case PAL_SWAP_HOLD_A:
        case PAL_SWAP_A_TO_B:
        case PAL_SWAP_HOLD_B:
        case PAL_SWAP_B_TO_A:
            if (isNpcSprite == SPRITE_MODE_PLAYER) {
                func_unkB_draw_player(part, yaw, mtx);
            } else {
                func_unkB_draw_npc(part, yaw, mtx);
            }
            break;
    }
}

void render_with_palset_blending(b32 isNpcSprite, ActorPart* part, s32 yaw, Matrix4f mtx, b32 skipAnimation) {
    DecorationTable* decorations = part->decorationTable;
    PAL_PTR color1;
    PAL_PTR color2;
    PAL_PTR outColor;
    s32 i, j;
    u8 blendAlpha;
    u8 r2, g2, b2, a1;
    u8 r1, g1, b1;

    // copy palettes from sprite data
    if (decorations->resetPalAdjust != 0) {
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            decorations->originalPalettesList = spr_get_player_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while (decorations->originalPalettesList[decorations->originalPalettesCount] != (PAL_PTR) -1) {
                decorations->originalPalettesCount++;
            }
        } else {
            decorations->originalPalettesList = spr_get_npc_palettes(part->curAnimation >> 16);
            decorations->originalPalettesCount = 0;
            while (decorations->originalPalettesList[decorations->originalPalettesCount] != (PAL_PTR) -1) {
                decorations->originalPalettesCount++;
            }
            decorations->spriteColorVariations = spr_get_npc_color_variations(part->curAnimation >> 16);
        }

        if (decorations->resetPalAdjust == 1) {
            decorations->palAnimState = PAL_SWAP_HOLD_A;
            decorations->palBlendAlpha = 0;
        } else {
            decorations->palAnimState = PAL_SWAP_HOLD_A;
            decorations->palBlendAlpha = 255;
        }

         for (i = 0; i < decorations->originalPalettesCount; i++) {
            color2 = decorations->originalPalettesList[i];
            color1 = decorations->copiedPalettes[0][i];
            decorations->adjustedPalettes[i] = color1;
            if (color2 != NULL) {
                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    *color1++ = *color2++;
                }
            }
        }

        decorations->nextPalTime = decorations->palswapTimeHoldA;
        decorations->palBlendAlpha = 0;
        decorations->palAnimState = PAL_SWAP_HOLD_A;
        decorations->resetPalAdjust = FALSE;
    }

    // blending from A -> B
    switch (decorations->palAnimState) {
        case PAL_SWAP_HOLD_A:
            if (skipAnimation) {
                break;
            }
            if (decorations->nextPalTime != 0) {
                decorations->nextPalTime--;
                break;
            }
            decorations->palBlendAlpha = 0;
            decorations->palAnimState = PAL_SWAP_A_TO_B;
            // fallthrough
        case PAL_SWAP_A_TO_B:
            if (!skipAnimation) {
                decorations->palBlendAlpha += 25600 / decorations->palswapTimeAtoB;
                if (decorations->palBlendAlpha > 255 * 100) {
                    decorations->palBlendAlpha = 255 * 100;
                }
            }
            blendAlpha = decorations->palBlendAlpha / 100;
            // blend all palettes from two palette sets
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                color2 = decorations->originalPalettesList[decorations->blendPalA * decorations->spriteColorVariations + i];
                color1 = decorations->originalPalettesList[decorations->blendPalB * decorations->spriteColorVariations + i];
                outColor = decorations->copiedPalettes[0][i];
                decorations->adjustedPalettes[i] = outColor;

                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    r2 = UNPACK_PAL_R(*color2);
                    g2 = UNPACK_PAL_G(*color2);
                    b2 = UNPACK_PAL_B(*color2);
                    r1 = UNPACK_PAL_R(*color1);
                    g1 = UNPACK_PAL_G(*color1);
                    b1 = UNPACK_PAL_B(*color1);
                    a1 = UNPACK_PAL_A(*color1);
                    color2++;
                    color1++;

                    r1 = LERP_COMPONENT(r2, r1, blendAlpha);
                    g1 = LERP_COMPONENT(g2, g1, blendAlpha);
                    b1 = LERP_COMPONENT(b2, b1, blendAlpha);

                    *outColor++ = PACK_PAL_RGBA(r1, g1, b1, a1);
                }
            }
            if (blendAlpha == 255) {
                decorations->palAnimState = PAL_SWAP_HOLD_B;
                decorations->nextPalTime = decorations->palswapTimeHoldB;
            }
            break;
    }

    switch (decorations->palAnimState) {
        case PAL_SWAP_HOLD_B:
            if (skipAnimation) {
                break;
            }
            if (decorations->nextPalTime != 0) {
                decorations->nextPalTime--;
                break;
            }
            decorations->palBlendAlpha = 0;
            decorations->palAnimState = PAL_SWAP_B_TO_A;
            // fallthrough
        case PAL_SWAP_B_TO_A:
            if (!skipAnimation) {
                decorations->palBlendAlpha += 25600 / decorations->palswapTimeBtoA;
                if (decorations->palBlendAlpha > 255 * 100) {
                    decorations->palBlendAlpha = 255 * 100;
                }
            }
            blendAlpha = decorations->palBlendAlpha / 100;
            // blend all palettes from two palette sets
            for (i = 0; i < decorations->spriteColorVariations; i++) {
                color2 = decorations->originalPalettesList[decorations->blendPalA * decorations->spriteColorVariations + i];
                color1 = decorations->originalPalettesList[decorations->blendPalB * decorations->spriteColorVariations + i];
                outColor = decorations->copiedPalettes[0][i];
                decorations->adjustedPalettes[i] = outColor;

                for (j = 0; j < SPR_PAL_SIZE; j++) {
                    r2 = UNPACK_PAL_R(*color2);
                    g2 = UNPACK_PAL_G(*color2);
                    b2 = UNPACK_PAL_B(*color2);
                    r1 = UNPACK_PAL_R(*color1);
                    g1 = UNPACK_PAL_G(*color1);
                    b1 = UNPACK_PAL_B(*color1);
                    a1 = UNPACK_PAL_A(*color1);
                    color2++;
                    color1++;

                    r1 = LERP_COMPONENT(r2, r1, blendAlpha);
                    g1 = LERP_COMPONENT(g2, g1, blendAlpha);
                    b1 = LERP_COMPONENT(b2, b1, blendAlpha);

                    *outColor++ = PACK_PAL_RGBA(r1, g1, b1, a1);
                }
            }
            if (blendAlpha == 255) {
                decorations->palAnimState = PAL_SWAP_HOLD_A;
                decorations->nextPalTime = decorations->palswapTimeHoldA;
            }
            break;
    }

    switch (decorations->palAnimState) {
        case PAL_SWAP_HOLD_A:
        case PAL_SWAP_A_TO_B:
        case PAL_SWAP_HOLD_B:
        case PAL_SWAP_B_TO_A:
            if (isNpcSprite == SPRITE_MODE_PLAYER) {
                func_unkB_draw_player(part, yaw, mtx);
            } else {
                func_unkB_draw_npc(part, yaw, mtx);
            }
            break;
    }
}

s32 update_part_glow(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection) {
    if (!(part->flags & ACTOR_PART_FLAG_NO_DECORATIONS)) {
        switch (part->decorationTable->glowState) {
            case GLOW_PAL_OFF:
                part_glow_off(isNpcSprite, part, yaw, isReflection);
                return 0;
            case GLOW_PAL_ON:
                part_glow_on(isNpcSprite, part, yaw, isReflection);
                break;
        }
    }
    return 0;
}

void part_glow_off(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection) {
    if (part->decorationTable->glowStateChanged) {
        part->decorationTable->glowStateChanged = FALSE;
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            set_player_imgfx_all(PLAYER_SPRITE_MAIN, IMGFX_CLEAR, 0, 0, 0, 0, 0);
        } else {
            set_npc_imgfx_all(part->spriteInstanceID, IMGFX_CLEAR, 0, 0, 0, 0, 0);
        }
    }
}

void part_glow_on(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection) {
    DecorationTable* decorations = part->decorationTable;
    u8 rbuf[20];
    u8 gbuf[20];
    u8 bbuf[20];
    s32 color;
    s32 alpha;
    s32 i;

    if (decorations->glowStateChanged) {
        decorations->glowUnk1 = -2;
        decorations->glowUnk3 = 0;
        decorations->glowStateChanged = FALSE;
        decorations->glowUnk2 = 0;
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            set_player_imgfx_all(PLAYER_SPRITE_MAIN, IMGFX_ALLOC_COLOR_BUF, 20, 0, 0, 255, 0);
        } else {
            set_npc_imgfx_all(part->spriteInstanceID, IMGFX_ALLOC_COLOR_BUF, 20, 0, 0, 255, 0);
        }
    }

    decorations->glowPhase += 7;

    if (decorations->glowPhase >= 360) {
        decorations->glowPhase %= 360;
    }

    for (i = 0; i < ARRAY_COUNT(rbuf); i++) {
        rbuf[i] = (cosine(decorations->glowPhase + (25 * i)) + 1.0) * 112.0;
        gbuf[i] = (cosine(decorations->glowPhase + (25 * i) + 45) + 1.0) * 112.0;
        bbuf[i] = (cosine(decorations->glowPhase + (25 * i) + 90) + 1.0) * 112.0;
    }

    alpha = 255;
    if (part->opacity < 255) {
        alpha = part->opacity;
    }
    if (part->flags & ACTOR_PART_FLAG_TRANSPARENT) {
        alpha = (alpha * 120) / 255;
    }

    for (i = 0; i < ARRAY_COUNT(rbuf); i++) {
        color = (rbuf[i] << 0x18) | (gbuf[i] << 0x10) | (bbuf[i] << 8) | alpha;
        if (isNpcSprite == SPRITE_MODE_PLAYER) {
            set_player_imgfx_all(PLAYER_SPRITE_MAIN, IMGFX_COLOR_BUF_SET_MODULATE, i, color, 0, 255, 0);
        } else {
            set_npc_imgfx_all(part->spriteInstanceID, IMGFX_COLOR_BUF_SET_MODULATE, i, color, 0, 255, 0);
        }
    }

    if (!isReflection) {
        decorations->glowUnk3--;
    }
}

s32 update_part_flash(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection) {
    if (!(part->flags & ACTOR_PART_FLAG_NO_DECORATIONS)) {
        switch (part->decorationTable->flashState) {
            case 0:
                part_flash_off(isNpcSprite, part, yaw, isReflection);
                return 0;
            case 1:
                part_flash_on(isNpcSprite, part, yaw, isReflection);
                return 0;
        }
    }
    return 0;
}

void part_flash_off(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection) {
    DecorationTable* decorations = part->decorationTable;

    if (decorations->flashStateChanged) {
        decorations->flashStateChanged = FALSE;
    }
    decorations->flashEnabled = FLASH_PAL_OFF;
}

void part_flash_on(b32 isNpcSprite, ActorPart* part, s32 yaw, b32 isReflection) {
    DecorationTable* decorations = part->decorationTable;

    if (decorations->flashStateChanged) {
        switch (get_flash_damage_intensity(part)) {
            case DAMAGE_INTENSITY_LIGHT:
                decorations->flashFramesLeft = 1;
                decorations->flashMode = FLASH_MODE_LIGHT;
                break;
            case DAMAGE_INTENSITY_MEDIUM:
                decorations->flashFramesLeft = 8;
                decorations->flashMode = FLASH_MODE_MEDIUM;
                break;
            case DAMAGE_INTENSITY_HEAVY:
            default:
                decorations->flashFramesLeft = 14;
                decorations->flashMode = FLASH_MODE_HEAVY;
                break;
        }
        decorations->flashEnabled = FLASH_PAL_OFF;
        decorations->flashStateChanged = FALSE;
    }

    if (decorations->flashMode == FLASH_MODE_DISPOSE) {
        decorations->flashEnabled = FLASH_PAL_OFF;
        clear_part_flash_mode(part);
    }

    switch (decorations->flashMode) {
        case FLASH_MODE_LIGHT:
            switch (decorations->flashFramesLeft) {
                case 0:
                    decorations->flashEnabled = FLASH_PAL_ON;
                    decorations->flashMode = FLASH_MODE_DISPOSE;
                    break;
                default:
                    decorations->flashEnabled = FLASH_PAL_OFF;
                    if (!isReflection) {
                        decorations->flashFramesLeft--;
                    }
                    break;
            }
            break;
        case FLASH_MODE_MEDIUM:
           switch (decorations->flashFramesLeft) {
                case 1:
                case 2:
                case 5:
                case 6:
                    decorations->flashEnabled = FLASH_PAL_ON;
                    break;
                case 3:
                case 4:
                case 7:
                case 8:
                    decorations->flashEnabled = FLASH_PAL_OFF;
                    break;
                case 0:
                    decorations->flashMode = FLASH_MODE_DISPOSE;
                    break;
            }
            if (!isReflection) {
                decorations->flashFramesLeft--;
            }
            break;
        case FLASH_MODE_HEAVY:
           switch (decorations->flashFramesLeft) {
                case 1:
                case 2:
                case 5:
                case 6:
                case 9:
                case 10:
                case 13:
                case 14:
                    decorations->flashEnabled = FLASH_PAL_OFF;
                    break;
                case 3:
                case 4:
                case 7:
                case 8:
                case 11:
                case 12:
                    decorations->flashEnabled = FLASH_PAL_ON;
                    break;
                case 0:
                    decorations->flashMode = FLASH_MODE_DISPOSE;
                    break;
            }
            if (!isReflection) {
                decorations->flashFramesLeft--;
            }
            break;
    }
}

void _add_part_decoration(ActorPart* actorPart) {
    DecorationTable* decorations;
    s32 i;

    if (actorPart->flags & ACTOR_PART_FLAG_NO_DECORATIONS) {
        return;
    }

    decorations = actorPart->decorationTable;
    for (i = 0; i < ARRAY_COUNT(decorations->type); i++) {
        switch (decorations->type[i]) {
            case ACTOR_DECORATION_NONE:
                add_part_decor_none(actorPart, i);
                break;
            case ACTOR_DECORATION_GOLDEN_FLAMES:
                add_part_decor_golden_flames(actorPart, i);
                break;
            case ACTOR_DECORATION_SWEAT:
                add_part_decor_sweat(actorPart, i);
                break;
            case ACTOR_DECORATION_SEEING_STARS:
                add_part_decor_seeing_stars(actorPart, i);
                break;
            case ACTOR_DECORATION_RED_FLAMES:
                add_part_decor_red_flames(actorPart, i);
                break;
            case ACTOR_DECORATION_GREY_SMOKE_TRAIL:
                add_part_decor_smoky_trail(actorPart, i);
                break;
            case ACTOR_DECORATION_FIRE_SMOKE_TRAIL:
                add_part_decor_fiery_trail(actorPart, i);
                break;
            case ACTOR_DECORATION_WHIRLWIND:
                add_part_decor_whirlwind(actorPart, i);
                break;
            case ACTOR_DECORATION_STEAM_EMITTER:
                add_part_decor_steam(actorPart, i);
                break;
            case ACTOR_DECORATION_SPARKLES:
                add_part_decor_sparkles(actorPart, i);
                break;
            case ACTOR_DECORATION_BOWSER_AURA:
                add_part_decor_bowser_aura(actorPart, i);
                break;
            case ACTOR_DECORATION_RADIAL_STAR_EMITTER:
                add_part_decor_radiating_stars(actorPart, i);
                break;
        }
    }
}

void _remove_part_decoration(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;

    switch (decorations->type[idx]) {
        case ACTOR_DECORATION_NONE:
            remove_part_decor_none(part, idx);
            break;
        case ACTOR_DECORATION_GOLDEN_FLAMES:
            remove_part_decor_golden_flames(part, idx);
            break;
        case ACTOR_DECORATION_SWEAT:
            remove_part_decor_sweat(part, idx);
            break;
        case ACTOR_DECORATION_SEEING_STARS:
            remove_part_decor_seeing_stars(part, idx);
            break;
        case ACTOR_DECORATION_RED_FLAMES:
            remove_part_decor_red_flames(part, idx);
            break;
        case ACTOR_DECORATION_GREY_SMOKE_TRAIL:
            remove_part_decor_smoky_trail(part, idx);
            break;
        case ACTOR_DECORATION_FIRE_SMOKE_TRAIL:
            remove_part_decor_fiery_trail(part, idx);
            break;
        case ACTOR_DECORATION_WHIRLWIND:
            remove_part_decor_whirlwind(part, idx);
            break;
        case ACTOR_DECORATION_STEAM_EMITTER:
            remove_part_decor_steam(part, idx);
            break;
        case ACTOR_DECORATION_SPARKLES:
            remove_part_decor_sparkles(part, idx);
            break;
        case ACTOR_DECORATION_BOWSER_AURA:
            remove_part_decor_bowser_aura(part, idx);
            break;
        case ACTOR_DECORATION_RADIAL_STAR_EMITTER:
            remove_part_decor_radiating_stars(part, idx);
            break;
    }

    decorations->type[idx] = ACTOR_DECORATION_NONE;
}

void add_part_decor_none(ActorPart* actorPart, s32 i) {
}

void remove_part_decor_none(ActorPart* part, s32 idx) {
}

void add_part_decor_golden_flames(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    EffectInstance* effect;
    AuraFXData* data;
    f32 scale;

    switch (decorations->state[idx]) {
        case 0:
            fx_aura(FX_AURA_GOLD, part->curPos.x, part->curPos.y, part->curPos.z, 0.4f, &decorations->effect[idx]);
            decorations->state[idx] = 1;
            decorations->decorData[idx].goldenFlames.scaleX = 40;
            decorations->decorData[idx].goldenFlames.scaleY = 40;
            decorations->decorData[idx].goldenFlames.offsetX = 0;
            break;
        case 1:
            effect = decorations->effect[idx];
            data = effect->data.aura;
            data->posA.x = part->curPos.x + decorations->decorData[idx].goldenFlames.offsetX;
            data->posA.y = part->curPos.y;
            data->posA.z = part->curPos.z;
            scale = decorations->decorData[idx].goldenFlames.scaleX;
            scale /= 100.0f;
            effect->data.aura->scale.x = scale;
            scale = decorations->decorData[idx].goldenFlames.scaleY;
            scale /= 100.0f;
            effect->data.aura->scale.y = scale;
            break;
    }
}

void remove_part_decor_golden_flames(ActorPart* part, s32 idx) {
    part->decorationTable->effect[idx]->data.aura->fadeTime = 5;
}

void add_part_decor_sweat(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;

    switch (decorations->state[idx]) {
        case 0:
            if (part->yaw > 90.0f) {
                fx_sweat(0, part->curPos.x, part->curPos.y + part->size.y, part->curPos.z, 5.0f, 45.0f, 20);
            } else {
                fx_sweat(0, part->curPos.x, part->curPos.y + part->size.y, part->curPos.z, 5.0f, -45.0f, 20);
            }
            decorations->stateResetTimer[idx] = 10;
            decorations->state[idx] = TRUE;
            break;
        case 1:
            if (decorations->stateResetTimer[idx] != 0) {
                decorations->stateResetTimer[idx]--;
            } else {
                decorations->state[idx] = FALSE;
            }
            break;
    }
}

void remove_part_decor_sweat(ActorPart* part, s32 idx) {
}

void add_part_decor_seeing_stars(ActorPart* part, s32 idx) {
    DecorationTable* decorations;
    StarsOrbitingFXData* data;

    decorations = part->decorationTable;
    switch (decorations->state[idx]) {
        case 0:
            fx_stars_orbiting(0, part->curPos.x, part->curPos.y + part->size.y, part->curPos.z, 20.0f, 3, &decorations->effect[idx]);
            decorations->state[idx] = 1;
            break;
        case 1:
            data = decorations->effect[idx]->data.starsOrbiting;
            data->pos.x = part->curPos.x;
            data->pos.y = part->curPos.y + part->size.y;
            data->pos.z = part->curPos.z;
            break;
    }
}

void remove_part_decor_seeing_stars(ActorPart* part, s32 idx) {
    remove_effect(part->decorationTable->effect[idx]);
}

void add_part_decor_red_flames(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    EffectInstance* effect;
    AuraFXData* data;
    f32 scale;

    switch (decorations->state[idx]) {
        case 0:
            fx_aura(FX_AURA_RED, part->curPos.x, part->curPos.y, part->curPos.z, 0.4f, &decorations->effect[idx]);
            decorations->state[idx] = 1;
            decorations->decorData[idx].redFlames.scaleX = 40;
            decorations->decorData[idx].redFlames.scaleY = 40;
            decorations->decorData[idx].redFlames.alpha = 255;
            decorations->decorData[idx].redFlames.offsetZ = 0;
            decorations->decorData[idx].redFlames.unused1 = 255;
            decorations->decorData[idx].redFlames.unused2 = 0;
            decorations->decorData[idx].redFlames.unused3 = 0;
            // fallthrough
        case 1:
            effect = decorations->effect[idx];
            data = effect->data.aura;
            data->posA.x = part->curPos.x;
            data->posA.y = part->curPos.y;
            data->posA.z = part->curPos.z + decorations->decorData[idx].redFlames.offsetZ;

            scale = decorations->decorData[idx].redFlames.scaleX;
            scale /= 100.0f;
            effect->data.aura->scale.x = scale;
            scale = decorations->decorData[idx].redFlames.scaleY;
            scale /= 100.0f;
            effect->data.aura->scale.y = scale;
            effect->data.aura->primA = decorations->decorData[idx].redFlames.alpha;
            break;
    }
}

void remove_part_decor_red_flames(ActorPart* part, s32 idx) {
    part->decorationTable->effect[idx]->data.aura->fadeTime = 5;
}

void add_part_decor_smoky_trail(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    EffectInstance* effect;

    switch (decorations->state[idx]) {
        case 0:
            decorations->effect[idx] = fx_effect_65(1, part->curPos.x, part->curPos.y, part->curPos.z, 1.0f, 0);
            decorations->state[idx] = 1;
            break;
        case 1:
            effect = decorations->effect[idx];
            effect->data.unk_65->pos.x = part->curPos.x;
            effect->data.unk_65->pos.y = part->curPos.y;
            effect->data.unk_65->pos.z = part->curPos.z;
            break;
    }
}

void remove_part_decor_smoky_trail(ActorPart* part, s32 idx) {
    part->decorationTable->effect[idx]->flags |= FX_INSTANCE_FLAG_DISMISS;
}

void add_part_decor_fiery_trail(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    EffectInstance* effect;

    switch (decorations->state[idx]) {
        case 0:
            decorations->effect[idx] = fx_effect_65(2, part->curPos.x, part->curPos.y, part->curPos.z, 1.0f, 0);
            decorations->decorData[idx].fireTrail.scale = 1;
            decorations->state[idx] = 1;
            break;
        case 1:
            effect = decorations->effect[idx];
            effect->data.unk_65->pos.x = part->curPos.x;
            effect->data.unk_65->pos.y = part->curPos.y;
            effect->data.unk_65->pos.z = part->curPos.z;
            effect->data.unk_65->scale = decorations->decorData[idx].fireTrail.scale / 100.0f;
            break;
    }
}

void remove_part_decor_fiery_trail(ActorPart* part, s32 idx) {
    part->decorationTable->effect[idx]->flags |= FX_INSTANCE_FLAG_DISMISS;
}

void add_part_decor_whirlwind(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    EffectInstance* effect;

    switch (decorations->state[idx]) {
        case 0:
            decorations->effect[idx] = fx_whirlwind(2, part->curPos.x, part->curPos.y, part->curPos.z, 1.0f, 0);
            decorations->state[idx] = 1;
            break;
        case 1:
            effect = decorations->effect[idx];
            effect->data.whirlwind->pos.x = part->curPos.x;
            effect->data.whirlwind->pos.y = part->curPos.y;
            effect->data.whirlwind->pos.z = part->curPos.z;
            break;
    }
}

void remove_part_decor_whirlwind(ActorPart* part, s32 idx) {
    part->decorationTable->effect[idx]->flags |= FX_INSTANCE_FLAG_DISMISS;
}

void add_part_decor_steam(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    f32 angle, sinA, cosA;

    switch (decorations->state[idx]) {
        case 0:
            decorations->stateResetTimer[idx] = 0;
            decorations->state[idx] = 1;
            // fallthrough
        case 1:
            decorations->stateResetTimer[idx]++;
            if (decorations->stateResetTimer[idx] >= 4) {
                decorations->stateResetTimer[idx] = 0;
                angle = DEG_TO_RAD(clamp_angle(-part->yaw));
                sinA = sin_rad(angle);
                cosA = cos_rad(angle);
                fx_walking_dust(0,
                    part->curPos.x + (part->size.x * sinA * 0.2f),
                    part->curPos.y + 1.5f,
                    part->curPos.z + (part->size.x * cosA * 0.2f),
                    sinA, cosA);
            }
            break;
    }
}

void remove_part_decor_steam(ActorPart* part, s32 idx) {
}

void add_part_decor_sparkles(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    f32 x, y, z;

    if (SparkleSpawnIntervals[decorations->decorData[idx].sparkles.spawnInterval] >= 0) {
        switch (decorations->state[idx]) {
            case 0:
                decorations->stateResetTimer[idx] = 0;
                decorations->state[idx] = 1;
                // fallthrough
            case 1:
                x = part->curPos.x;
                y = part->curPos.y + (part->size.y / 2);
                z = part->curPos.z - 5.0f;
                /// @bug this should be % 4
                if ((gGameStatusPtr->frameCounter / 4) == 0) {
                    fx_sparkles(FX_SPARKLES_1, x, y, z, 10.0f);
                }
                decorations->stateResetTimer[idx]++;
                if (SparkleSpawnIntervals[decorations->decorData[idx].sparkles.spawnInterval] < decorations->stateResetTimer[idx]) {
                    decorations->stateResetTimer[idx] = 0;
                    fx_sparkles(FX_SPARKLES_1, x, y, z, 20.0f);
                }
                break;
        }
    }
}

void remove_part_decor_sparkles(ActorPart* part, s32 idx) {
}

void add_part_decor_bowser_aura(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    EffectInstance* effect;
    AuraFXData* data;
    f32 scale;

    switch (decorations->state[idx]) {
        case 0:
            fx_aura(FX_AURA_BLUE, part->curPos.x, part->curPos.y, part->curPos.z, 1.2f, &decorations->effect[idx]);
            decorations->state[idx] = 1;
            decorations->decorData[idx].bowserAura.scaleX = 150;
            decorations->decorData[idx].bowserAura.scaleY = 150;
            decorations->decorData[idx].bowserAura.alpha = 255;
            decorations->decorData[idx].bowserAura.offsetZ = 0;
            // fallthrough
        case 1:
            effect = decorations->effect[idx];
            data = effect->data.aura;
            data->posA.x = part->curPos.x;
            data->posA.y = part->curPos.y;
            data->posA.z = part->curPos.z + decorations->decorData[idx].bowserAura.offsetZ;

            scale = decorations->decorData[idx].bowserAura.scaleX;
            scale /= 100.0f;
            effect->data.aura->scale.x = scale;
            scale = decorations->decorData[idx].bowserAura.scaleY;
            scale /= 100.0f;
            effect->data.aura->scale.y = scale * (0.8 - 1e-16); // small epsilon
            effect->data.aura->primA = decorations->decorData[idx].bowserAura.alpha;
            effect->data.aura->renderYaw = part->yaw;
            break;
    }
}

void remove_part_decor_bowser_aura(ActorPart* part, s32 idx) {
    part->decorationTable->effect[idx]->data.aura->fadeTime = 5;
}

void add_part_decor_radiating_stars(ActorPart* part, s32 idx) {
    DecorationTable* decorations = part->decorationTable;
    EnergyInOutFXData* data;
    f32 scale;

    switch (decorations->state[idx]) {
        case 0:
            decorations->effect[idx] = fx_energy_in_out(4, part->curPos.x, part->curPos.y, part->curPos.z, 1.2f, 0);
            decorations->state[idx] = 1;
            decorations->decorData[idx].stars.scalePct = 120;
            decorations->decorData[idx].stars.offsetY = 0;
            // fallthrough
        case 1:
            data = decorations->effect[idx]->data.energyInOut;
            scale = decorations->decorData[idx].stars.scalePct;
            scale /= 100.0f;
            data->unk_44 = scale;
            data->pos.x = part->curPos.x;
            data->pos.y = (part->curPos.y + (scale * 41.0f));
            data->pos.z = (part->curPos.z + decorations->decorData[idx].stars.offsetY);
            break;
    }
}

void remove_part_decor_radiating_stars(ActorPart* part, s32 idx) {
    part->decorationTable->effect[idx]->flags |= FX_INSTANCE_FLAG_DISMISS;
}
