From 8795c15ab9d2fb255ee2c11db436ff0c1ebe5c70 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:17:51 -0400 Subject: [PATCH 1/8] feat: Allowed custom `COLOR_MAP_*` in `field_effect_scripts`. Credit: SDH --- asm/macros/field_effect_script.inc | 12 +++++++++--- include/field_weather.h | 2 ++ src/field_effect.c | 7 +++++-- src/field_weather.c | 22 +++++++++++++++++++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/asm/macros/field_effect_script.inc b/asm/macros/field_effect_script.inc index 38f7e31750..3e00bc734d 100644 --- a/asm/macros/field_effect_script.inc +++ b/asm/macros/field_effect_script.inc @@ -5,9 +5,11 @@ .4byte \address .endm - .macro field_eff_loadfadedpal address:req + @ 1 = COLOR_MAP_DARK_CONTRAST + .macro field_eff_loadfadedpal address:req, color_map_type=1 .byte 1 .4byte \address + .byte \color_map_type .endm .macro field_eff_loadpal address:req @@ -24,10 +26,12 @@ .byte 4 .endm - .macro field_eff_loadgfx_callnative tiles_address:req, palette_address:req, function_address:req + @ 1 = COLOR_MAP_DARK_CONTRAST + .macro field_eff_loadgfx_callnative tiles_address:req, palette_address:req, function_address:req, color_map_type=1 .byte 5 .4byte \tiles_address .4byte \palette_address + .byte \color_map_type .4byte \function_address .endm @@ -37,8 +41,10 @@ .4byte \function_address .endm - .macro field_eff_loadfadedpal_callnative palette_address:req, function_address:req + @ 1 = COLOR_MAP_DARK_CONTRAST + .macro field_eff_loadfadedpal_callnative palette_address:req, function_address:req, color_map_type=1 .byte 7 .4byte \palette_address + .byte \color_map_type .4byte \function_address .endm diff --git a/include/field_weather.h b/include/field_weather.h index fbd90f2405..d741bdd2b0 100644 --- a/include/field_weather.h +++ b/include/field_weather.h @@ -170,7 +170,9 @@ void PlayRainStoppingSoundEffect(void); u8 IsWeatherChangeComplete(void); void SetWeatherScreenFadeOut(void); void SetWeatherPalStateIdle(void); +const u8* SetPaletteColorMapType(u8 paletteIndex, u8 colorMapType); void PreservePaletteInWeather(u8 preservedPalIndex); +void ResetPaletteColorMapType(u8 paletteIndex); void ResetPreservedPalettesInWeather(void); // field_weather_effect.c diff --git a/src/field_effect.c b/src/field_effect.c index 6ae2d509a4..5df664908d 100644 --- a/src/field_effect.c +++ b/src/field_effect.c @@ -781,9 +781,11 @@ void FieldEffectScript_LoadTiles(u8 **script) void FieldEffectScript_LoadFadedPalette(u8 **script) { struct SpritePalette *palette = (struct SpritePalette *)FieldEffectScript_ReadWord(script); - LoadSpritePalette(palette); - UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(palette->tag), TRUE); + u32 paletteSlot = LoadSpritePalette(palette); (*script) += 4; + SetPaletteColorMapType(paletteSlot + 16, T1_READ_8(*script)); + UpdateSpritePaletteWithWeather(paletteSlot, TRUE); + (*script)++; } void FieldEffectScript_LoadPalette(u8 **script) @@ -839,6 +841,7 @@ void FieldEffectFreePaletteIfUnused(u8 paletteNum) for (i = 0; i < MAX_SPRITES; i++) if (gSprites[i].inUse && gSprites[i].oam.paletteNum == paletteNum) return; + ResetPaletteColorMapType(paletteNum + 16); FreeSpritePaletteByTag(tag); } } diff --git a/src/field_weather.c b/src/field_weather.c index 1a3a150c4c..e2fb1e77e6 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -1136,11 +1136,27 @@ void SetWeatherPalStateIdle(void) gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE; } +const u8* SetPaletteColorMapType(u8 paletteIndex, u8 colorMapType) { + if (sPaletteColorMapTypes[paletteIndex] == colorMapType) + return sPaletteColorMapTypes; + // setup field effect color map + if (sPaletteColorMapTypes != sFieldEffectPaletteColorMapTypes) { + CpuFastCopy(sBasePaletteColorMapTypes, sFieldEffectPaletteColorMapTypes, 32); + sPaletteColorMapTypes = sFieldEffectPaletteColorMapTypes; + } + sFieldEffectPaletteColorMapTypes[paletteIndex] = colorMapType; + return sPaletteColorMapTypes; +} + void PreservePaletteInWeather(u8 preservedPalIndex) { - CpuCopy16(sBasePaletteColorMapTypes, sFieldEffectPaletteColorMapTypes, 32); - sFieldEffectPaletteColorMapTypes[preservedPalIndex] = COLOR_MAP_NONE; - sPaletteColorMapTypes = sFieldEffectPaletteColorMapTypes; + SetPaletteColorMapType(preservedPalIndex, COLOR_MAP_NONE); +} + +void ResetPaletteColorMapType(u8 paletteIndex) { + if (sPaletteColorMapTypes == sBasePaletteColorMapTypes) + return; + sFieldEffectPaletteColorMapTypes[paletteIndex] = sBasePaletteColorMapTypes[paletteIndex]; } void ResetPreservedPalettesInWeather(void) From d3a48398084612f40bc2d05b7bca3e2ae3cf4f83 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Sun, 8 Sep 2024 16:02:51 -0400 Subject: [PATCH 2/8] meta: included `COLOR_MAP` constants in `field_effect_script`. Credit: SDH --- asm/macros/field_effect_script.inc | 10 ++++------ include/constants/field_weather.h | 5 +++++ src/field_weather.c | 7 ------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/asm/macros/field_effect_script.inc b/asm/macros/field_effect_script.inc index 3e00bc734d..68d8502a9d 100644 --- a/asm/macros/field_effect_script.inc +++ b/asm/macros/field_effect_script.inc @@ -1,3 +1,4 @@ +#include "constants/field_weather.h" @ The first .byte argument of each macro below is an index into gFieldEffectScriptFuncs .macro field_eff_loadtiles address:req @@ -5,8 +6,7 @@ .4byte \address .endm - @ 1 = COLOR_MAP_DARK_CONTRAST - .macro field_eff_loadfadedpal address:req, color_map_type=1 + .macro field_eff_loadfadedpal address:req, color_map_type=COLOR_MAP_DARK_CONTRAST .byte 1 .4byte \address .byte \color_map_type @@ -26,8 +26,7 @@ .byte 4 .endm - @ 1 = COLOR_MAP_DARK_CONTRAST - .macro field_eff_loadgfx_callnative tiles_address:req, palette_address:req, function_address:req, color_map_type=1 + .macro field_eff_loadgfx_callnative tiles_address:req, palette_address:req, function_address:req, color_map_type=COLOR_MAP_DARK_CONTRAST .byte 5 .4byte \tiles_address .4byte \palette_address @@ -41,8 +40,7 @@ .4byte \function_address .endm - @ 1 = COLOR_MAP_DARK_CONTRAST - .macro field_eff_loadfadedpal_callnative palette_address:req, function_address:req, color_map_type=1 + .macro field_eff_loadfadedpal_callnative palette_address:req, function_address:req, color_map_type=COLOR_MAP_DARK_CONTRAST .byte 7 .4byte \palette_address .byte \color_map_type diff --git a/include/constants/field_weather.h b/include/constants/field_weather.h index e84dbc48c4..dedd1a3c0a 100644 --- a/include/constants/field_weather.h +++ b/include/constants/field_weather.h @@ -1,6 +1,11 @@ #ifndef GUARD_CONSTANTS_FIELD_WEATHER_H #define GUARD_CONSTANTS_FIELD_WEATHER_H +// sPaletteColorMapTypes & field_effect_scripts +#define COLOR_MAP_NONE 0 +#define COLOR_MAP_DARK_CONTRAST 1 +#define COLOR_MAP_CONTRAST 2 + #define MAX_RAIN_SPRITES 24 #define NUM_CLOUD_SPRITES 3 #define NUM_FOG_HORIZONTAL_SPRITES 20 diff --git a/src/field_weather.c b/src/field_weather.c index e2fb1e77e6..eaa25078b6 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -20,13 +20,6 @@ #define DROUGHT_COLOR_INDEX(color) ((((color) >> 1) & 0xF) | (((color) >> 2) & 0xF0) | (((color) >> 3) & 0xF00)) -enum -{ - COLOR_MAP_NONE, - COLOR_MAP_DARK_CONTRAST, - COLOR_MAP_CONTRAST, -}; - struct RGBColor { u16 r:5; From 3e3229190728b2d7ac502d3851b9c9bb5d36e4bb Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Sun, 8 Sep 2024 17:05:17 -0400 Subject: [PATCH 3/8] fix: fixed shadows not spawning across maps for OWs with the same `localId` --- src/event_object_movement.c | 4 ++-- src/field_effect_helpers.c | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 878aeac807..f8dd625410 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2441,7 +2441,7 @@ static void SpawnLightSprite(s16 x, s16 y, s16 camX, s16 camY, u32 lightType) { sprite->centerToCornerVecX = -(32 >> 1); sprite->centerToCornerVecY = -(32 >> 1); sprite->oam.priority = 1; - sprite->oam.objMode = 1; // BLEND + sprite->oam.objMode = ST_OAM_OBJ_BLEND; sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; sprite->x += 8; sprite->y += 22 + sprite->centerToCornerVecY; @@ -2451,7 +2451,7 @@ static void SpawnLightSprite(s16 x, s16 y, s16 camX, s16 camY, u32 lightType) { sprite->centerToCornerVecY = -(16 >> 1); sprite->oam.priority = 2; sprite->subpriority = 0xFF; - sprite->oam.objMode = 1; // BLEND + sprite->oam.objMode = ST_OAM_OBJ_BLEND; } } diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 70f0db7f1b..606f3b1f1a 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -56,8 +56,8 @@ u32 FldEff_Shadow(void); void SetUpShadow(struct ObjectEvent *objectEvent, struct Sprite *sprite) { gFieldEffectArguments[0] = objectEvent->localId; - gFieldEffectArguments[1] = gSaveBlock1Ptr->location.mapNum; - gFieldEffectArguments[2] = gSaveBlock1Ptr->location.mapGroup; + gFieldEffectArguments[1] = objectEvent->mapNum; + gFieldEffectArguments[2] = objectEvent->mapGroup; FldEff_Shadow(); } @@ -329,7 +329,11 @@ u32 FldEff_Shadow(void) s32 i; for (i = MAX_SPRITES - 1; i > -1; i--) { // Search backwards, because of CreateSpriteAtEnd // Return early if a shadow sprite already exists - if (gSprites[i].data[0] == gFieldEffectArguments[0] && gSprites[i].callback == UpdateShadowFieldEffect) + if (gSprites[i].callback == UpdateShadowFieldEffect && + gSprites[i].sLocalId == gFieldEffectArguments[0] && + gSprites[i].sMapNum == gFieldEffectArguments[1] && + gSprites[i].sMapGroup == gFieldEffectArguments[2] + ) return 0; } objectEventId = GetObjectEventIdByLocalIdAndMap(gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]); @@ -341,7 +345,7 @@ u32 FldEff_Shadow(void) if (spriteId != MAX_SPRITES) { // SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(8, 12)); - gSprites[spriteId].oam.objMode = 1; // BLEND + gSprites[spriteId].oam.objMode = ST_OAM_OBJ_BLEND; gSprites[spriteId].coordOffsetEnabled = TRUE; gSprites[spriteId].sLocalId = gFieldEffectArguments[0]; gSprites[spriteId].sMapNum = gFieldEffectArguments[1]; From faf773a54bc04a3fd23569a711a72f1469194468 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Wed, 25 Dec 2024 19:26:24 -0500 Subject: [PATCH 4/8] feat: added time-blend override --- include/overworld.h | 16 +++-- include/palette.h | 7 +++ src/cable_car.c | 1 - src/field_weather.c | 4 +- src/overworld.c | 139 +++++++++++++++++++++++++++----------------- 5 files changed, 103 insertions(+), 64 deletions(-) diff --git a/include/overworld.h b/include/overworld.h index a9354ad80c..ba34836410 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -30,6 +30,11 @@ #define TIME_OF_DAY_DAY 2 #define TIME_OF_DAY_MAX TIME_OF_DAY_DAY +// trigger a time-of-day blend once +#define HOURS_BLEND_ONCE 25 +// don't update currentTimeBlend +#define HOURS_FREEZE_BLEND 26 + struct InitialPlayerAvatarState { u8 transitionFlags; @@ -44,14 +49,6 @@ struct LinkPlayerObjectEvent u8 movementMode; }; -struct __attribute__((packed)) TimeBlendSettings { - u16 weight:9; - u16 time1:3; - u16 time0:3; - u16 unused:1; - u16 altWeight; -}; - // Exported RAM declarations extern struct WarpData gLastUsedWarp; extern struct LinkPlayerObjectEvent gLinkPlayerObjectEvents[4]; @@ -65,7 +62,7 @@ extern bool8 (*gFieldCallback2)(void); extern u8 gLocalLinkPlayerId; extern u8 gFieldLinkPlayerCount; extern u8 gTimeOfDay; -extern u16 gTimeUpdateCounter; +extern s16 gTimeUpdateCounter; extern struct TimeBlendSettings currentTimeBlend; @@ -175,5 +172,6 @@ bool32 Overworld_RecvKeysFromLinkIsRunning(void); bool32 Overworld_SendKeysToLinkIsRunning(void); bool32 IsSendingKeysOverCable(void); void ClearLinkPlayerObjectEvents(void); +bool16 SetTimeOfDay(u16 hours); #endif // GUARD_OVERWORLD_H diff --git a/include/palette.h b/include/palette.h index 930499b877..f86ab00a63 100644 --- a/include/palette.h +++ b/include/palette.h @@ -45,6 +45,13 @@ struct BlendSettings { u32 coeff:5; }; +struct __attribute__((packed)) TimeBlendSettings { + struct BlendSettings bld0; + struct BlendSettings bld1; + u16 weight; + u16 altWeight; +}; + struct PaletteFadeControl { u32 multipurpose1; // This field needs to exist or errors will occur diff --git a/src/cable_car.c b/src/cable_car.c index 8a666a536e..d4fcfec59d 100644 --- a/src/cable_car.c +++ b/src/cable_car.c @@ -1058,4 +1058,3 @@ static void InitGroundTilemapData(bool8 goingDown) sCableCar->groundTimer = 0; } - diff --git a/src/field_weather.c b/src/field_weather.c index eaa25078b6..9f41014ff3 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -799,8 +799,8 @@ void FadeScreen(u8 mode, s8 delay) if (MapHasNaturalLight(gMapHeader.mapType)) { UpdateAltBgPalettes(PALETTES_BG); BeginTimeOfDayPaletteFade(PALETTES_ALL, delay, 16, 0, - (struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time0], - (struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time1], + ¤tTimeBlend.bld0, + ¤tTimeBlend.bld1, currentTimeBlend.weight, fadeColor); } else { BeginNormalPaletteFade(PALETTES_ALL, delay, 16, 0, fadeColor); diff --git a/src/overworld.c b/src/overworld.c index cbe0347768..8a99173c6c 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -194,7 +194,7 @@ u8 gFieldLinkPlayerCount; u8 gTimeOfDay; struct TimeBlendSettings currentTimeBlend; -u16 gTimeUpdateCounter; // playTimeVBlanks will eventually overflow, so this is used to update TOD +s16 gTimeUpdateCounter; // playTimeVBlanks will eventually overflow, so this is used to update TOD // EWRAM vars EWRAM_DATA static u8 sObjectEventLoadFlag = 0; @@ -206,6 +206,7 @@ EWRAM_DATA static u16 sLastMapSectionId = 0; EWRAM_DATA static struct InitialPlayerAvatarState sInitialPlayerAvatarState = {0}; EWRAM_DATA static u16 sAmbientCrySpecies = 0; EWRAM_DATA static bool8 sIsAmbientCryWaterMon = FALSE; +EWRAM_DATA static u8 sHoursOverride = 0; // used to override apparent time of day hours EWRAM_DATA struct LinkPlayerObjectEvent gLinkPlayerObjectEvents[4] = {0}; static const struct WarpData sDummyWarpData = @@ -853,6 +854,8 @@ static void LoadMapFromWarp(bool32 a1) CheckLeftFriendsSecretBase(); TrySetMapSaveWarpStatus(); ClearTempFieldEventData(); + // reset hours override on every warp + sHoursOverride = 0; ResetCyclingRoadChallengeData(); RestartWildEncounterImmunitySteps(); TryUpdateRandomTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum); @@ -1481,44 +1484,65 @@ const struct BlendSettings gTimeOfDayBlend[] = u8 UpdateTimeOfDay(void) { s32 hours, minutes; RtcCalcLocalTime(); - hours = gLocalTime.hours; - minutes = gLocalTime.minutes; - if (hours < 4) { // night + hours = sHoursOverride ? sHoursOverride : gLocalTime.hours; + minutes = sHoursOverride ? 0 : gLocalTime.minutes; + switch (hours) + { + case 0 ... 3: // night + gTimeOfDay = TIME_OF_DAY_NIGHT; + currentTimeBlend.bld0 = currentTimeBlend.bld1 = gTimeOfDayBlend[gTimeOfDay]; currentTimeBlend.weight = 256; currentTimeBlend.altWeight = 0; - gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_NIGHT; - } else if (hours < 7) { // night->twilight - currentTimeBlend.time0 = TIME_OF_DAY_NIGHT; - currentTimeBlend.time1 = TIME_OF_DAY_TWILIGHT; + break; + case 4 ... 6: // night -> twilight + currentTimeBlend.bld0 = gTimeOfDayBlend[TIME_OF_DAY_NIGHT]; + currentTimeBlend.bld1 = gTimeOfDayBlend[TIME_OF_DAY_TWILIGHT]; currentTimeBlend.weight = 256 - 256 * ((hours - 4) * 60 + minutes) / ((7-4)*60); currentTimeBlend.altWeight = (256 - currentTimeBlend.weight) / 2; gTimeOfDay = TIME_OF_DAY_DAY; - } else if (hours < 10) { // twilight->day - currentTimeBlend.time0 = TIME_OF_DAY_TWILIGHT; - currentTimeBlend.time1 = TIME_OF_DAY_DAY; + break; + case 7 ... 9: // twilight -> day + currentTimeBlend.bld0 = gTimeOfDayBlend[TIME_OF_DAY_TWILIGHT]; + currentTimeBlend.bld1 = gTimeOfDayBlend[TIME_OF_DAY_DAY]; currentTimeBlend.weight = 256 - 256 * ((hours - 7) * 60 + minutes) / ((10-7)*60); currentTimeBlend.altWeight = (256 - currentTimeBlend.weight) / 2 + 128; gTimeOfDay = TIME_OF_DAY_DAY; - } else if (hours < 18) { // day + break; + case 10 ... 17: // day + gTimeOfDay = TIME_OF_DAY_DAY; + currentTimeBlend.bld0 = currentTimeBlend.bld1 = gTimeOfDayBlend[gTimeOfDay]; currentTimeBlend.weight = currentTimeBlend.altWeight = 256; - gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_DAY; - } else if (hours < 20) { // day->twilight - currentTimeBlend.time0 = TIME_OF_DAY_DAY; - currentTimeBlend.time1 = TIME_OF_DAY_TWILIGHT; + break; + case 18 ... 19: // day -> twilight + currentTimeBlend.bld0 = gTimeOfDayBlend[TIME_OF_DAY_DAY]; + currentTimeBlend.bld1 = gTimeOfDayBlend[TIME_OF_DAY_TWILIGHT]; currentTimeBlend.weight = 256 - 256 * ((hours - 18) * 60 + minutes) / ((20-18)*60); currentTimeBlend.altWeight = currentTimeBlend.weight / 2 + 128; gTimeOfDay = TIME_OF_DAY_TWILIGHT; - } else if (hours < 22) { // twilight->night - currentTimeBlend.time0 = TIME_OF_DAY_TWILIGHT; - currentTimeBlend.time1 = TIME_OF_DAY_NIGHT; + break; + case 20 ... 21: // twilight -> night + currentTimeBlend.bld0 = gTimeOfDayBlend[TIME_OF_DAY_TWILIGHT]; + currentTimeBlend.bld1 = gTimeOfDayBlend[TIME_OF_DAY_NIGHT]; currentTimeBlend.weight = 256 - 256 * ((hours - 20) * 60 + minutes) / ((22-20)*60); currentTimeBlend.altWeight = currentTimeBlend.weight / 2; gTimeOfDay = TIME_OF_DAY_NIGHT; - } else { // 22-24, night + break; + case 22 ... 24: + gTimeOfDay = TIME_OF_DAY_NIGHT; + currentTimeBlend.bld0 = currentTimeBlend.bld1 = gTimeOfDayBlend[gTimeOfDay]; currentTimeBlend.weight = 256; currentTimeBlend.altWeight = 0; - gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_NIGHT; + break; + // special value; always causes a blend update, + // and increments sHoursOverride + case HOURS_BLEND_ONCE: + currentTimeBlend.weight ^= 1; + sHoursOverride++; + break; + case HOURS_FREEZE_BLEND: + break; } + return gTimeOfDay; } @@ -1565,32 +1589,32 @@ void UpdatePalettesWithTime(u32 palettes) { if (IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(i))) palettes &= ~(mask); - palettes &= PALETTES_MAP | PALETTES_OBJECTS; // Don't blend UI pals - if (!palettes) - return; - TimeMixPalettes( - palettes, - gPlttBufferUnfaded, - gPlttBufferFaded, - (struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time0], - (struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time1], - currentTimeBlend.weight - ); + palettes &= PALETTES_MAP | PALETTES_OBJECTS; // Don't blend UI pals + if (!palettes) + return; + TimeMixPalettes( + palettes, + gPlttBufferUnfaded, + gPlttBufferFaded, + ¤tTimeBlend.bld0, + ¤tTimeBlend.bld1, + currentTimeBlend.weight + ); } } u8 UpdateSpritePaletteWithTime(u8 paletteNum) { if (MapHasNaturalLight(gMapHeader.mapType)) { if (IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(paletteNum))) - return paletteNum; - TimeMixPalettes( - 1, - &gPlttBufferUnfaded[OBJ_PLTT_ID(paletteNum)], - &gPlttBufferFaded[OBJ_PLTT_ID(paletteNum)], - (struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time0], - (struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time1], - currentTimeBlend.weight - ); + return paletteNum; + TimeMixPalettes( + 1, + &gPlttBufferUnfaded[OBJ_PLTT_ID(paletteNum)], + &gPlttBufferFaded[OBJ_PLTT_ID(paletteNum)], + ¤tTimeBlend.bld0, + ¤tTimeBlend.bld1, + currentTimeBlend.weight + ); } return paletteNum; } @@ -1607,18 +1631,16 @@ static void OverworldBasic(void) UpdateTilesetAnimations(); DoScheduledBgTilemapCopiesToVram(); // Every minute if no palette fade is active, update TOD blending as needed - if (!gPaletteFade.active && ++gTimeUpdateCounter >= 3600) { - struct TimeBlendSettings cachedBlend = { - .time0 = currentTimeBlend.time0, - .time1 = currentTimeBlend.time1, - .weight = currentTimeBlend.weight, - }; - gTimeUpdateCounter = 0; + if (!gPaletteFade.active && --gTimeUpdateCounter <= 0) { + struct TimeBlendSettings cachedBlend = currentTimeBlend; + u32 *bld0 = (u32*)&cachedBlend; + u32 *bld1 = (u32*)¤tTimeBlend; + gTimeUpdateCounter = 3600; UpdateTimeOfDay(); - if (cachedBlend.time0 != currentTimeBlend.time0 - || cachedBlend.time1 != currentTimeBlend.time1 - || cachedBlend.weight != currentTimeBlend.weight) - { + if (bld0[0] != bld1[0] + || bld0[1] != bld1[1] + || bld0[2] != bld1[2] + ) { UpdateAltBgPalettes(PALETTES_BG); UpdatePalettesWithTime(PALETTES_ALL); } @@ -3380,3 +3402,16 @@ static void SpriteCB_LinkPlayer(struct Sprite *sprite) sprite->data[7]++; } } + +// returns old sHoursOverride +u16 SetTimeOfDay(u16 hours) { + u16 oldHours = sHoursOverride; + sHoursOverride = hours; + gTimeUpdateCounter = 0; + return oldHours; +} + +bool8 ScrFunc_settimeofday(struct ScriptContext *ctx) { + SetTimeOfDay(ScriptReadByte(ctx)); + return FALSE; +} From 4047f9b7644ece47f0f4c7240641b7e9b4c52cd4 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Wed, 25 Dec 2024 19:48:54 -0500 Subject: [PATCH 5/8] feat: added FadeScreenHardware utility --- include/field_weather.h | 1 + include/palette.h | 2 +- src/field_weather.c | 29 +++++++++++++++++++++++++++++ src/palette.c | 5 +++-- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/include/field_weather.h b/include/field_weather.h index d741bdd2b0..a8550845cc 100644 --- a/include/field_weather.h +++ b/include/field_weather.h @@ -152,6 +152,7 @@ void SetCurrentAndNextWeatherNoDelay(u8 weather); void ApplyWeatherColorMapIfIdle(s8 colorMapIndex); void ApplyWeatherColorMapIfIdle_Gradual(u8 colorMapIndex, u8 targetColorMapIndex, u8 colorMapStepDelay); void FadeScreen(u8 mode, s8 delay); +u16 FadeScreenHardware(u8 mode, s8 delay); bool8 IsWeatherNotFadingIn(void); void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex, bool8 allowFog); void ApplyWeatherColorMapToPal(u8 paletteIndex); diff --git a/include/palette.h b/include/palette.h index f86ab00a63..30592a4ecb 100644 --- a/include/palette.h +++ b/include/palette.h @@ -99,7 +99,7 @@ void InvertPlttBuffer(u32 selectedPalettes); void TintPlttBuffer(u32 selectedPalettes, s8 r, s8 g, s8 b); void UnfadePlttBuffer(u32 selectedPalettes); void BeginFastPaletteFade(u8 submode); -void BeginHardwarePaletteFade(u8 blendCnt, u8 delay, u8 y, u8 targetY, u8 shouldResetBlendRegisters); +void BeginHardwarePaletteFade(u16 blendCnt, u8 delay, u8 y, u8 targetY, u8 shouldResetBlendRegisters); void BlendPalettes(u32 selectedPalettes, u8 coeff, u16 color); void BlendPalettesFine(u32 palettes, u16 *src, u16 *dst, u32 coeff, u32 color); void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u16 color); diff --git a/src/field_weather.c b/src/field_weather.c index 9f41014ff3..2bcaa2f857 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -815,6 +815,35 @@ void FadeScreen(u8 mode, s8 delay) } } +// fades screen using BLDY +// Note: This enables blending in all windows; +// These bits may need to be disabled later +u16 FadeScreenHardware(u8 mode, s8 delay) { + u16 bldCnt = GetGpuReg(REG_OFFSET_BLDCNT) & BLDCNT_TGT2_ALL; + bldCnt |= BLDCNT_TGT1_ALL; + // enable blend in all windows + SetGpuRegBits(REG_OFFSET_WININ, WININ_WIN0_CLR | WININ_WIN1_CLR); + SetGpuRegBits(REG_OFFSET_WINOUT, WINOUT_WIN01_CLR); + + switch (mode) + { + case FADE_FROM_BLACK: + BeginHardwarePaletteFade(bldCnt | BLDCNT_EFFECT_DARKEN, delay, 16, 0, TRUE); + break; + case FADE_TO_BLACK: + BeginHardwarePaletteFade(bldCnt | BLDCNT_EFFECT_DARKEN, delay, 0, 16, FALSE); + break; + case FADE_FROM_WHITE: + BeginHardwarePaletteFade(bldCnt | BLDCNT_EFFECT_LIGHTEN, delay, 16, 0, TRUE); + break; + case FADE_TO_WHITE: + BeginHardwarePaletteFade(bldCnt | BLDCNT_EFFECT_LIGHTEN, delay, 0, 16, FALSE); + break; + } + + return 0; +} + bool8 IsWeatherNotFadingIn(void) { return (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_SCREEN_FADING_IN); diff --git a/src/palette.c b/src/palette.c index ddc0688209..3ea2a08195 100644 --- a/src/palette.c +++ b/src/palette.c @@ -896,7 +896,7 @@ static u8 UpdateFastPaletteFade(void) return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE; } -void BeginHardwarePaletteFade(u8 blendCnt, u8 delay, u8 y, u8 targetY, u8 shouldResetBlendRegisters) +void BeginHardwarePaletteFade(u16 blendCnt, u8 delay, u8 y, u8 targetY, u8 shouldResetBlendRegisters) { gPaletteFade_blendCnt = blendCnt; gPaletteFade.delayCounter = delay; @@ -950,7 +950,8 @@ static u8 UpdateHardwarePaletteFade(void) { if (gPaletteFade.shouldResetBlendRegisters) { - gPaletteFade_blendCnt = 0; + // clear TGT1 + gPaletteFade_blendCnt &= ~0xFF; gPaletteFade.y = 0; } gPaletteFade.shouldResetBlendRegisters = FALSE; From 090c4f39362d2852ff3071e012425a8ab569b803 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Fri, 24 Jan 2025 02:02:21 -0500 Subject: [PATCH 6/8] feat: hardware fades now affect BLDALPHA too --- include/gba/io_reg.h | 3 +++ src/palette.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/gba/io_reg.h b/include/gba/io_reg.h index 53021185e3..75e3b56117 100644 --- a/include/gba/io_reg.h +++ b/include/gba/io_reg.h @@ -599,6 +599,7 @@ #define BLDCNT_EFFECT_BLEND (1 << 6) // 1st+2nd targets mixed (controlled by BLDALPHA) #define BLDCNT_EFFECT_LIGHTEN (2 << 6) // 1st target becomes whiter (controlled by BLDY) #define BLDCNT_EFFECT_DARKEN (3 << 6) // 1st target becomes blacker (controlled by BLDY) +#define BLDCNT_EFFECT_EFF_MASK (3 << 6) // mask to check effect // Bits 8-13 select layers for the 2nd target #define BLDCNT_TGT2_BG0 (1 << 8) #define BLDCNT_TGT2_BG1 (1 << 9) @@ -611,6 +612,8 @@ // BLDALPHA #define BLDALPHA_BLEND(target1, target2) (((target2) << 8) | (target1)) +#define BLDALPHA_TGT1(bld) ((bld) & 0x1F) +#define BLDALPHA_TGT2(bld) (((bld) >> 8) & 0x1F) // SOUNDCNT_H #define SOUND_CGB_MIX_QUARTER 0x0000 diff --git a/src/palette.c b/src/palette.c index 3ea2a08195..303e6ced42 100644 --- a/src/palette.c +++ b/src/palette.c @@ -962,10 +962,43 @@ static u8 UpdateHardwarePaletteFade(void) return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE; } +// Only called for hardware fades static void UpdateBlendRegisters(void) { SetGpuReg(REG_OFFSET_BLDCNT, (u16)gPaletteFade_blendCnt); SetGpuReg(REG_OFFSET_BLDY, gPaletteFade.y); + // If fade-out, also adjust BLDALPHA and DISPCNT + if (!gPaletteFade.yDec /*&& gPaletteFade.mode == HARDWARE_FADE*/) { + u16 bldAlpha = GetGpuReg(REG_OFFSET_BLDALPHA); + u8 tgt1 = BLDALPHA_TGT1(bldAlpha); + u8 tgt2 = BLDALPHA_TGT2(bldAlpha); + u8 bldFade; + + switch (gPaletteFade_blendCnt & BLDCNT_EFFECT_EFF_MASK) + { + // FADE_TO_BLACK + case BLDCNT_EFFECT_DARKEN: + bldFade = BLDALPHA_TGT1(max(0, 16 - gPaletteFade.y)); + SetGpuReg( + REG_OFFSET_BLDALPHA, + BLDALPHA_BLEND(min(tgt1, bldFade), min(tgt2, bldFade)) + ); + break; + // FADE_TO_WHITE + case BLDCNT_EFFECT_LIGHTEN: + SetGpuReg( + REG_OFFSET_BLDALPHA, + BLDALPHA_BLEND(min(++tgt1, 31), min(++tgt2, 31)) + ); + // cause display to show white when finished + // (otherwise blend-mode sprites will still be visible) + if (gPaletteFade.hardwareFadeFinishing && gPaletteFade.y >= 16) + SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_FORCED_BLANK); + break; + } + } else + ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_FORCED_BLANK); + if (gPaletteFade.hardwareFadeFinishing) { gPaletteFade.hardwareFadeFinishing = FALSE; From 16c31d61b44e7ece18e0d2039449d2ae4a0710e0 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Sat, 25 Jan 2025 20:58:22 -0500 Subject: [PATCH 7/8] fix: removed fadescreenswapbuffers palette dependence, via hardware fade --- asm/macros/event.inc | 9 +++++---- src/event_object_movement.c | 2 ++ src/field_weather.c | 6 +++++- src/scrcmd.c | 17 +++++++++-------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 7125e71643..06396af8d2 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -1674,13 +1674,14 @@ .4byte \text .endm - @ Equivalent to fadescreen but copies gPlttBufferUnfaded to gPaletteDecompressionBuffer on the fade out - @ and the reverse on the fade in, in effect saving gPlttBufferUnfaded to restore it. - @ If nowait set, does not wait for the fade to complete + @ Equivalent to fadescreen but uses a hardware fade and darken/lighten blend modes, + @ to avoid modifying palettes at all. + @ Useful for fade-out/fade-in without leaving the overworld or entering a new scene. + @ If nowait set, doesn't wait for the fade to complete .macro fadescreenswapbuffers mode:req, nowait=0 .byte 0xdc .byte \mode - .byte \nowait + .byte \nowait .endm @ Buffers the specified trainer's class name to the given string var. diff --git a/src/event_object_movement.c b/src/event_object_movement.c index f7f0f3e2df..18ca30bb96 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2397,6 +2397,8 @@ void UpdateLightSprite(struct Sprite *sprite) { return; } + // Note to self: Don't set window registers during hardware fade! + switch (sprite->data[5]) { // lightType case 0: if (gPaletteFade.active) { // if palette fade is active, don't flicker since the timer won't be updated diff --git a/src/field_weather.c b/src/field_weather.c index 2bcaa2f857..199cff15e1 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -818,6 +818,7 @@ void FadeScreen(u8 mode, s8 delay) // fades screen using BLDY // Note: This enables blending in all windows; // These bits may need to be disabled later +// (i.e if blending lighting effects using WINOBJ) u16 FadeScreenHardware(u8 mode, s8 delay) { u16 bldCnt = GetGpuReg(REG_OFFSET_BLDCNT) & BLDCNT_TGT2_ALL; bldCnt |= BLDCNT_TGT1_ALL; @@ -1008,7 +1009,10 @@ void Weather_SetBlendCoeffs(u8 eva, u8 evb) gWeatherPtr->currBlendEVB = evb; gWeatherPtr->targetBlendEVA = eva; gWeatherPtr->targetBlendEVB = evb; - SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(eva, evb)); + + // don't update BLDALPHA if a hardware fade is on-screen + if ((GetGpuReg(REG_OFFSET_BLDCNT) & BLDCNT_EFFECT_EFF_MASK) < BLDCNT_EFFECT_LIGHTEN) + SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(eva, evb)); } void Weather_SetTargetBlendCoeffs(u8 eva, u8 evb, int delay) diff --git a/src/scrcmd.c b/src/scrcmd.c index a51e81aa91..edca8dbbbe 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -23,6 +23,7 @@ #include "field_tasks.h" #include "field_weather.h" #include "fieldmap.h" +#include "gpu_regs.h" #include "item.h" #include "lilycove_lady.h" #include "main.h" @@ -661,19 +662,19 @@ bool8 ScrCmd_fadescreenswapbuffers(struct ScriptContext *ctx) switch (mode) { - case FADE_TO_BLACK: - case FADE_TO_WHITE: - default: - CpuCopy32(gPlttBufferUnfaded, gPaletteDecompressionBuffer, PLTT_SIZE); - FadeScreen(mode, 0); - break; case FADE_FROM_BLACK: case FADE_FROM_WHITE: - CpuCopy32(gPaletteDecompressionBuffer, gPlttBufferUnfaded, PLTT_SIZE); - FadeScreen(mode, 0); + // Restore last weather blend before fading in, + // since BLDALPHA was modified by fade-out + SetGpuReg( + REG_OFFSET_BLDALPHA, + BLDALPHA_BLEND(gWeatherPtr->currBlendEVA, gWeatherPtr->currBlendEVB) + ); break; } + FadeScreenHardware(mode, 0); + if (nowait) return FALSE; SetupNativeScript(ctx, IsPaletteNotActive); From 521bea6bc3b3446bc69f4911df68653256a12b14 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Sat, 25 Jan 2025 20:59:08 -0500 Subject: [PATCH 8/8] fix: allowed loading non-word-aligned tileset palettes --- src/fieldmap.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/fieldmap.c b/src/fieldmap.c index 1c792cb10e..dbda92b2f7 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -881,7 +881,7 @@ static void LoadTilesetPalette(struct Tileset const *tileset, u16 destOffset, u1 { // LoadPalette(&black, destOffset, 2); if (skipFaded) - CpuFastCopy(tileset->palettes, &gPlttBufferUnfaded[destOffset], size); + CpuFastCopy(tileset->palettes, &gPlttBufferUnfaded[destOffset], size); // always word-aligned else LoadPaletteFast(tileset->palettes, destOffset, size); gPlttBufferFaded[destOffset] = gPlttBufferUnfaded[destOffset] = RGB_BLACK; // why does it have to be black? @@ -891,10 +891,14 @@ static void LoadTilesetPalette(struct Tileset const *tileset, u16 destOffset, u1 } else if (tileset->isSecondary == TRUE) { - // (void*) is to silence 'source potentially unaligned' error - // All 'gTilesetPalettes_' arrays should have ALIGNED(4) in them + // All 'gTilesetPalettes_' arrays should have ALIGNED(4) in them, + // but we use SmartCopy here just in case they don't if (skipFaded) - CpuFastCopy((void*)tileset->palettes[NUM_PALS_IN_PRIMARY], &gPlttBufferUnfaded[destOffset], size); + #ifdef CpuSmartCopy16 + CpuSmartCopy16(tileset->palettes[NUM_PALS_IN_PRIMARY], &gPlttBufferUnfaded[destOffset], size); + #else + CpuCopy16(tileset->palettes[NUM_PALS_IN_PRIMARY], &gPlttBufferUnfaded[destOffset], size); + #endif else LoadPaletteFast(tileset->palettes[NUM_PALS_IN_PRIMARY], destOffset, size); low = NUM_PALS_IN_PRIMARY;