diff --git a/common_syms/overworld.txt b/common_syms/overworld.txt index e136f54449..4833362098 100644 --- a/common_syms/overworld.txt +++ b/common_syms/overworld.txt @@ -6,3 +6,4 @@ gFieldCallback gFieldCallback2 gLocalLinkPlayerId gFieldLinkPlayerCount +gTimeOfDay diff --git a/data/tilesets/secondary/rustboro/metatile_attributes.bin b/data/tilesets/secondary/rustboro/metatile_attributes.bin index e2b728c657..56b728b08c 100644 Binary files a/data/tilesets/secondary/rustboro/metatile_attributes.bin and b/data/tilesets/secondary/rustboro/metatile_attributes.bin differ diff --git a/graphics/object_events/palettes/light.pal b/graphics/object_events/palettes/light.pal new file mode 100644 index 0000000000..3beca89bdf --- /dev/null +++ b/graphics/object_events/palettes/light.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +110 198 165 +255 213 18 +255 214 38 +254 217 39 +254 218 53 +255 220 65 +255 221 76 +255 223 86 +254 225 95 +254 228 104 +254 229 112 +254 230 120 +255 232 132 +255 235 148 +255 237 162 +255 240 176 diff --git a/graphics/object_events/pics/misc/light.png b/graphics/object_events/pics/misc/light.png new file mode 100644 index 0000000000..d1a0402d10 Binary files /dev/null and b/graphics/object_events/pics/misc/light.png differ diff --git a/include/event_object_movement.h b/include/event_object_movement.h index b6b1551950..937857f7e9 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -98,6 +98,7 @@ void UpdateFollowingPokemon(void); void RemoveFollowingPokemon(void); struct ObjectEvent * GetFollowerObject(void); u8 GetDirectionToFace(s16, s16, s16, s16); +void UpdateLightSprite(struct Sprite *); void TrySpawnObjectEvents(s16, s16); u8 CreateObjectSprite(u8 graphicsId, u8 a1, s16 x, s16 y, u8 z, u8 direction); u8 AddPseudoObjectEvent(u16, void (*)(struct Sprite *), s16 x, s16 y, u8 subpriority); diff --git a/include/global.h b/include/global.h index 1992ff8196..c1f4eb85d1 100644 --- a/include/global.h +++ b/include/global.h @@ -488,6 +488,8 @@ struct SaveBlock2 extern struct SaveBlock2 *gSaveBlock2Ptr; +extern u8 UpdateSpritePaletteWithTime(u8); + struct SecretBaseParty { u32 personality[PARTY_SIZE]; diff --git a/include/overworld.h b/include/overworld.h index a2eac7e29a..98564f91d6 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -15,7 +15,7 @@ // These two are a hack to stop user input until link stuff can be // resolved. -#define LINK_KEY_CODE_HANDLE_RECV_QUEUE 0x1B +#define LINK_KEY_CODE_HANDLE_RECV_QUEUE 0x1B #define LINK_KEY_CODE_HANDLE_SEND_QUEUE 0x1C #define LINK_KEY_CODE_UNK_7 0x1D #define LINK_KEY_CODE_UNK_8 0x1E @@ -25,6 +25,10 @@ #define MOVEMENT_MODE_SCRIPTED 2 #define SKIP_OBJECT_EVENT_LOAD 1 +#define TIME_OF_DAY_NIGHT 0 +#define TIME_OF_DAY_TWILIGHT 1 +#define TIME_OF_DAY_DAY 2 +#define TIME_OF_DAY_MAX TIME_OF_DAY_DAY struct InitialPlayerAvatarState { @@ -52,6 +56,7 @@ extern void (*gFieldCallback)(void); extern bool8 (*gFieldCallback2)(void); extern u8 gLocalLinkPlayerId; extern u8 gFieldLinkPlayerCount; +extern u8 gTimeOfDay; // Exported ROM declarations extern const struct UCoords32 gDirectionToVectors[]; @@ -130,6 +135,7 @@ void CleanupOverworldWindowsAndTilemaps(void); bool32 IsUpdateLinkStateCBActive(void); void CB1_Overworld(void); void CB2_OverworldBasic(void); +void BlendPalettesWithTime(u32); void CB2_Overworld(void); void SetMainCallback1(void (*cb)(void)); void SetUnusedCallback(void *a0); diff --git a/include/palette.h b/include/palette.h index 072edef563..ebc1cc0bb4 100644 --- a/include/palette.h +++ b/include/palette.h @@ -60,6 +60,7 @@ u8 UpdatePaletteFade(void); void ResetPaletteFade(void); void ReadPlttIntoBuffers(void); bool8 BeginNormalPaletteFade(u32, s8, u8, u8, u16); +bool8 BeginTimeOfDayPaletteFade(u32, s8, u8, u8, u16); bool8 unref_sub_8073D3C(u32, u8, u8, u8, u16); void unref_sub_8073D84(u8, u32 *); void ResetPaletteStructByUid(u16); diff --git a/src/data/field_effects/field_effect_object_template_pointers.h b/src/data/field_effects/field_effect_object_template_pointers.h index 41d6271bf2..eb8e52029d 100755 --- a/src/data/field_effects/field_effect_object_template_pointers.h +++ b/src/data/field_effects/field_effect_object_template_pointers.h @@ -35,6 +35,7 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_AshLaunch; const struct SpriteTemplate gFieldEffectObjectTemplate_Bubbles; const struct SpriteTemplate gFieldEffectObjectTemplate_SmallSparkle; const struct SpriteTemplate gFieldEffectObjectTemplate_Rayquaza; +const struct SpriteTemplate gFieldEffectObjectTemplate_BallLight; const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_SHADOW_S] = &gFieldEffectObjectTemplate_ShadowSmall, diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index cd1aa58fe1..9497895412 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -28,6 +28,10 @@ static const struct SpriteFrameImage sPicTable_ShadowExtraLarge[] = { obj_frame_tiles(gFieldEffectObjectPic_ShadowExtraLarge), }; +const struct SpriteFrameImage gFieldEffectObjectPicTable_BallLight[] = { + obj_frame_tiles(gFieldEffectObjectPic_BallLight), +}; + const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowSmall = {0xFFFF, OBJ_EVENT_PAL_TAG_MAY, &gObjectEventBaseOam_8x8, gFieldEffectObjectImageAnimTable_Shadow, gFieldEffectObjectPicTable_ShadowSmall, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowMedium = {0xFFFF, OBJ_EVENT_PAL_TAG_MAY, &gObjectEventBaseOam_16x8, gFieldEffectObjectImageAnimTable_Shadow, gFieldEffectObjectPicTable_ShadowMedium, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; @@ -36,6 +40,8 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowLarge = {0xFFFF, OB const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowExtraLarge = {0xFFFF, OBJ_EVENT_PAL_TAG_MAY, &gObjectEventBaseOam_64x32, gFieldEffectObjectImageAnimTable_Shadow, gFieldEffectObjectPicTable_ShadowExtraLarge, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; +const struct SpriteTemplate gFieldEffectObjectTemplate_BallLight = {0xFFFF, OBJ_EVENT_PAL_TAG_LIGHT, &gObjectEventBaseOam_32x32, gObjectEventImageAnimTable_Inanimate, gFieldEffectObjectPicTable_BallLight, gDummySpriteAffineAnimTable, UpdateLightSprite}; + static const struct SpriteFrameImage sPicTable_TallGrass[] = { overworld_frame(gFieldEffectObjectPic_TallGrass, 2, 2, 0), overworld_frame(gFieldEffectObjectPic_TallGrass, 2, 2, 1), diff --git a/src/data/object_events/object_event_graphics.h b/src/data/object_events/object_event_graphics.h index 7204232bcf..1b8222e2af 100755 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -283,6 +283,7 @@ const u32 gFieldEffectObjectPic_ShadowSmall[] = INCBIN_U32("graphics/field_effec const u32 gFieldEffectObjectPic_ShadowMedium[] = INCBIN_U32("graphics/field_effects/pics/shadow_medium.4bpp"); const u32 gFieldEffectObjectPic_ShadowLarge[] = INCBIN_U32("graphics/field_effects/pics/shadow_large.4bpp"); const u32 gFieldEffectObjectPic_ShadowExtraLarge[] = INCBIN_U32("graphics/field_effects/pics/shadow_extra_large.4bpp"); +const u32 gFieldEffectObjectPic_BallLight[] = INCBIN_U32("graphics/object_events/pics/misc/light.4bpp"); static const u32 sFiller[0x48] = {}; const u8 gFieldEffectPic_CutGrass[] = INCBIN_U8("graphics/field_effects/pics/cut_grass.4bpp"); const u32 gFieldEffectPic_CutGrass_Copy[] = INCBIN_U32("graphics/field_effects/pics/cut_grass.4bpp"); @@ -344,14 +345,7 @@ const u32 gObjectEventPic_Greta[] = INCBIN_U32("graphics/object_events/pics/peop const u32 gObjectEventPic_Noland[] = INCBIN_U32("graphics/object_events/pics/people/frontier_brains/noland.4bpp"); const u32 gObjectEventPic_Lucy[] = INCBIN_U32("graphics/object_events/pics/people/frontier_brains/lucy.4bpp"); const u32 gObjectEventPic_Brandon[] = INCBIN_U32("graphics/object_events/pics/people/frontier_brains/brandon.4bpp"); -<<<<<<< HEAD -const u32 gObjectEventPic_Lugia[] = INCBIN_U32("graphics/object_events/pics/pokemon/lugia.4bpp"); -const u16 gObjectEventPal_Lugia[] = INCBIN_U16("graphics/object_events/palettes/lugia.gbapal"); -const u32 gObjectEventPic_HoOh[] = INCBIN_U32("graphics/object_events/pics/pokemon/ho_oh.4bpp"); -const u16 gObjectEventPal_HoOh[] = INCBIN_U16("graphics/object_events/palettes/ho_oh.gbapal"); -======= const u16 gObjectEventPalette32[] = INCBIN_U16("graphics/object_events/palettes/32.gbapal"); ->>>>>>> e8c5f3ad6 (Added all 386 follower sprites.) const u32 gObjectEventPic_AnimatedBall[] = INCBIN_U32("graphics/object_events/pics/misc/animated_ball.4bpp"); const u16 gObjectEventPalette31[] = INCBIN_U16("graphics/object_events/palettes/31.gbapal"); const u32 gObjectEventPic_Bulbasaur[] = INCBIN_U32("graphics/object_events/pics/pokemon/bulbasaur.4bpp"); @@ -784,3 +778,4 @@ const u32 gObjectEventPic_LugiaOld[] = INCBIN_U32("graphics/object_events/pics/p const u32 gObjectEventPic_HoOhOld[] = INCBIN_U32("graphics/object_events/pics/pokemon/ho_oh_old.4bpp"); const u32 gObjectEventPic_GroudonOld[] = INCBIN_U32("graphics/object_events/pics/pokemon/groudon_old.4bpp"); const u32 gObjectEventPic_KyogreOld[] = INCBIN_U32("graphics/object_events/pics/pokemon/kyogre_old.4bpp"); +const u16 gObjectEventPaletteLight[] = INCBIN_U16("graphics/object_events/palettes/light.gbapal"); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 5a22b3f59d..348d65fdc8 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -451,6 +451,7 @@ const u8 gInitialMovementTypeFacingDirections[] = { #define OBJ_EVENT_PAL_TAG_RS_BRENDAN 0x1122 #define OBJ_EVENT_PAL_TAG_RS_MAY 0x1123 #define OBJ_EVENT_PAL_TAG_DYNAMIC 0x1124 +#define OBJ_EVENT_PAL_TAG_LIGHT 0x8001 #define OBJ_EVENT_PAL_TAG_NONE 0x11FF #include "data/object_events/object_event_graphics_info_pointers.h" @@ -498,6 +499,7 @@ static const struct SpritePalette sObjectEventSpritePalettes[] = { {gObjectEventPal_RubySapphireBrendan, OBJ_EVENT_PAL_TAG_RS_BRENDAN}, {gObjectEventPal_RubySapphireMay, OBJ_EVENT_PAL_TAG_RS_MAY}, {gObjectEventPalette0, OBJ_EVENT_PAL_TAG_DYNAMIC}, + {gObjectEventPaletteLight, OBJ_EVENT_PAL_TAG_LIGHT}, {NULL, 0x0000}, }; @@ -1753,6 +1755,87 @@ bool8 ScrFunc_followerfly(struct ScriptContext *ctx) { return FALSE; } +// Callback for light sprites +void UpdateLightSprite(struct Sprite *sprite) { + s16 left = gSaveBlock1Ptr->pos.x - 2; + s16 right = gSaveBlock1Ptr->pos.x + 17; + s16 top = gSaveBlock1Ptr->pos.y; + s16 bottom = gSaveBlock1Ptr->pos.y + 15; + s16 x = sprite->data[6]; + s16 y = sprite->data[7]; + u16 sheetTileStart; + u32 paletteNum; + bool8 finished = TRUE; + // Ripped from RemoveObjectEventIfOutsideView + if (x >= left && x <= right + && y >= top && y <= bottom) + finished = FALSE; + if (x >= left && x <= right + && y >= top && y <= bottom) + finished = FALSE; + finished = finished ? finished : gTimeOfDay != TIME_OF_DAY_NIGHT; + if (finished) { + sheetTileStart = sprite->sheetTileStart; + paletteNum = sprite->oam.paletteNum; + DestroySprite(sprite); + FieldEffectFreeTilesIfUnused(sheetTileStart); + FieldEffectFreePaletteIfUnused(paletteNum); + return; + } + + if (gPlayerAvatar.tileTransitionState) { + Weather_SetBlendCoeffs(7, 12); + sprite->invisible = FALSE; + } else { + Weather_SetBlendCoeffs(12, 12); + sprite->invisible = gSaveBlock2Ptr->playTimeVBlanks & 1; + } +} + +// Spawn a light at a map coordinate based on metatile behavior +static void SpawnLightSprite(s16 x, s16 y, s16 camX, s16 camY, u32 behavior) { + struct Sprite *sprite; + u8 i; + for (i = 0; i < MAX_SPRITES; i++) { + sprite = &gSprites[i]; + if (sprite->inUse && sprite->callback == UpdateLightSprite && sprite->data[6] == x && sprite->data[7] == y) + return; + } + sprite = &gSprites[CreateSprite(&gFieldEffectObjectTemplate_BallLight, 0, 0, 0)]; + UpdateSpritePaletteByTemplate(&gFieldEffectObjectTemplate_BallLight, sprite); + sub_8092FF0(x + camX, y + camY, &sprite->pos1.x, &sprite->pos1.y); + sprite->data[6] = x; + sprite->data[7] = y; + sprite->affineAnims = gDummySpriteAffineAnimTable; + sprite->affineAnimBeginning = TRUE; + sprite->centerToCornerVecX = -(32 >> 1); + sprite->centerToCornerVecY = -(32 >> 1); + sprite->oam.priority = 1; + sprite->oam.objMode = 1; // BLEND + sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; + sprite->coordOffsetEnabled = TRUE; + sprite->pos1.x += 8; + sprite->pos1.y += 22 + sprite->centerToCornerVecY; +} + +void TrySpawnLightSprites(s16 camX, s16 camY) { + s16 left = gSaveBlock1Ptr->pos.x - 2; + s16 right = gSaveBlock1Ptr->pos.x + 17; + s16 top = gSaveBlock1Ptr->pos.y; + s16 bottom = gSaveBlock1Ptr->pos.y + 16; + s16 x, y; + u32 behavior; + if (gTimeOfDay != TIME_OF_DAY_NIGHT) + return; + for (x = left; x <= right; x++) { + for (y = top; y <= bottom; y++) { + behavior = MapGridGetMetatileBehaviorAt(x, y); + if (behavior == 0x04) // TODO: Use an actual constant + SpawnLightSprite(x, y, camX, camY, behavior); + } + } +} + void TrySpawnObjectEvents(s16 cameraX, s16 cameraY) { u8 i; @@ -1789,6 +1872,7 @@ void TrySpawnObjectEvents(s16 cameraX, s16 cameraY) TrySpawnObjectEventTemplate(template, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, cameraX, cameraY); } } + TrySpawnLightSprites(cameraX, cameraY); } void RemoveObjectEventsOutsideView(void) @@ -1829,7 +1913,7 @@ static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) RemoveObjectEvent(objectEvent); } -void sub_808E16C(s16 x, s16 y) +void sub_808E16C(s16 x, s16 y) // Called when returning to field { u8 i; @@ -1842,6 +1926,7 @@ void sub_808E16C(s16 x, s16 y) } } CreateReflectionEffectSprites(); + TrySpawnLightSprites(x, y); } static void sub_808E1B8(u8 objectEventId, s16 x, s16 y) @@ -1934,7 +2019,13 @@ static u8 UpdateSpritePalette(const struct SpritePalette * spritePalette, struct sprite->inUse = FALSE; FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); sprite->inUse = TRUE; - sprite->oam.paletteNum = LoadSpritePalette(spritePalette); + if (IndexOfSpritePaletteTag(spritePalette->tag) == 0xFF) { + sprite->oam.paletteNum = LoadSpritePalette(spritePalette); + UpdateSpritePaletteWithTime(sprite->oam.paletteNum); + } else { + sprite->oam.paletteNum = LoadSpritePalette(spritePalette); + } + return sprite->oam.paletteNum; } @@ -2186,11 +2277,14 @@ void Unused_LoadObjectEventPaletteSet(u16 *paletteTags) static u8 sub_808E8F4(const struct SpritePalette *spritePalette) { + u8 paletteNum; if (IndexOfSpritePaletteTag(spritePalette->tag) != 0xFF) { return 0xFF; } - return LoadSpritePalette(spritePalette); + paletteNum = LoadSpritePalette(spritePalette); + UpdateSpritePaletteWithTime(paletteNum); + return paletteNum; } void PatchObjectPalette(u16 paletteTag, u8 paletteSlot) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 994a8ada23..05ade09d9e 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -146,11 +146,10 @@ static void LoadObjectRegularReflectionPalette(struct ObjectEvent *objectEvent, } paletteNum = LoadSpritePalette(&filteredPalette); UpdateSpritePaletteWithWeather(paletteNum); + UpdateSpritePaletteWithTime(paletteNum); } sprite->oam.paletteNum = paletteNum; - // Apply alpha blending - // SetGpuReg(REG_OFFSET_BLDALPHA, sprite->data[7] == FALSE ? BLDALPHA_BLEND(8, 12) : BLDALPHA_BLEND(8, 8)); - sprite->oam.objMode = 1; // BLEND + sprite->oam.objMode = 1; // Alpha blending } #define HIGH_BRIDGE_PAL_TAG 0x4010 @@ -200,6 +199,7 @@ static void UpdateObjectReflectionSprite(struct Sprite *reflectionSprite) } paletteNum = LoadSpritePalette(&filteredPalette); UpdateSpritePaletteWithWeather(paletteNum); + UpdateSpritePaletteWithTime(paletteNum); } reflectionSprite->oam.paletteNum = paletteNum; } diff --git a/src/field_weather.c b/src/field_weather.c index 655e24590e..73f22d56c8 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -254,6 +254,7 @@ static void Task_WeatherMain(u8 taskId) static void None_Init(void) { + Weather_SetBlendCoeffs(8, 12); // Indoor shadows gWeatherPtr->gammaTargetIndex = 0; gWeatherPtr->gammaStepDelay = 0; } @@ -860,6 +861,7 @@ void LoadCustomWeatherSpritePalette(const u16 *palette) { LoadPalette(palette, 0x100 + gWeatherPtr->weatherPicSpritePalIndex * 16, 32); UpdateSpritePaletteWithWeather(gWeatherPtr->weatherPicSpritePalIndex); + UpdateSpritePaletteWithTime(gWeatherPtr->weatherPicSpritePalIndex); } static void LoadDroughtWeatherPalette(u8 *gammaIndexPtr, u8 *a1) diff --git a/src/field_weather_effect.c b/src/field_weather_effect.c index 4281117d1f..0d0b71fe27 100644 --- a/src/field_weather_effect.c +++ b/src/field_weather_effect.c @@ -478,7 +478,7 @@ void Rain_InitVars(void) gWeatherPtr->rainSpriteVisibleDelay = 8; gWeatherPtr->isDownpour = FALSE; gWeatherPtr->targetRainSpriteCount = 10; - gWeatherPtr->gammaTargetIndex = 3; + gWeatherPtr->gammaTargetIndex = gTimeOfDay == TIME_OF_DAY_DAY ? 3 : 0; gWeatherPtr->gammaStepDelay = 20; SetRainStrengthFromSoundEffect(SE_RAIN); } @@ -1020,7 +1020,7 @@ void Thunderstorm_InitVars(void) gWeatherPtr->rainSpriteVisibleDelay = 4; gWeatherPtr->isDownpour = FALSE; gWeatherPtr->targetRainSpriteCount = 16; - gWeatherPtr->gammaTargetIndex = 3; + gWeatherPtr->gammaTargetIndex = gTimeOfDay == TIME_OF_DAY_DAY ? 3 : 0; gWeatherPtr->gammaStepDelay = 20; gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment gWeatherPtr->thunderTriggered = 0; @@ -1116,6 +1116,8 @@ void Thunderstorm_Main(void) if (--gWeatherPtr->unknown_6E6 == 0) { sub_80ABC48(3); + if (gTimeOfDay != TIME_OF_DAY_DAY) + BlendPalettesWithTime(0xFFFFFFFF); gWeatherPtr->unknown_6EA = 1; if (--gWeatherPtr->unknown_6EC != 0) { @@ -1152,7 +1154,7 @@ void Thunderstorm_Main(void) case 13: if (--gWeatherPtr->unknown_6E6 == 0) { - sub_80ABC7C(19, 3, 5); + gTimeOfDay == TIME_OF_DAY_DAY ? sub_80ABC7C(19, 3, 5) : BlendPalettesWithTime(0xFFFFFFFF); gWeatherPtr->initStep++; } break; diff --git a/src/overworld.c b/src/overworld.c index 369db2173d..32bd469cf8 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -42,6 +42,7 @@ #include "random.h" #include "roamer.h" #include "rotating_gate.h" +#include "rtc.h" #include "safari_zone.h" #include "save.h" #include "save_location.h" @@ -175,6 +176,8 @@ static u16 (*sPlayerKeyInterceptCallback)(u32); static bool8 sUnknown_03000E18; static u8 sRfuKeepAliveTimer; +u8 static gTimeOfDayState; + // IWRAM common u16 *gBGTilemapBuffers1; u16 *gBGTilemapBuffers2; @@ -185,6 +188,8 @@ bool8 (*gFieldCallback2)(void); u8 gLocalLinkPlayerId; // This is our player id in a multiplayer mode. u8 gFieldLinkPlayerCount; +u8 gTimeOfDay; + // EWRAM vars EWRAM_DATA static u8 sObjectEventLoadFlag = 0; EWRAM_DATA struct WarpData gLastUsedWarp = {0}; @@ -197,6 +202,7 @@ EWRAM_DATA static u16 sAmbientCrySpecies = 0; EWRAM_DATA static bool8 sIsAmbientCryWaterMon = FALSE; EWRAM_DATA struct LinkPlayerObjectEvent gLinkPlayerObjectEvents[4] = {0}; + // const rom data static const struct WarpData sDummyWarpData = { @@ -811,6 +817,7 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) RoamerMove(); DoCurrentWeather(); ResetFieldTasksArgs(); + BlendPalettesWithTime(0xFFFFFFFF); RunOnResumeMapScript(); if (gMapHeader.regionMapSectionId != MAPSEC_BATTLE_FRONTIER @@ -1442,6 +1449,59 @@ void CB1_Overworld(void) DoCB1_Overworld(gMain.newKeys, gMain.heldKeys); } +struct TimeOfDayBlend { + u8 coeff:4; + u16 blendColor; +}; + +static const struct TimeOfDayBlend sTimeOfDayBlendVars[] = +{ + [TIME_OF_DAY_NIGHT] = {.coeff = 10, .blendColor = 0x1400}, + [TIME_OF_DAY_TWILIGHT] = {.coeff = 4, .blendColor = 0x155D}, + [TIME_OF_DAY_DAY] = {.coeff = 0, .blendColor = 0}, +}; + +u8 UpdateTimeOfDay(void) { + RtcCalcLocalTime(); + if (gLocalTime.hours >= 20 || gLocalTime.hours < 4) + return gTimeOfDay = TIME_OF_DAY_NIGHT; + else if (gLocalTime.hours >= 10 && gLocalTime.hours < 20) + return gTimeOfDay = TIME_OF_DAY_DAY; + return gTimeOfDay = TIME_OF_DAY_TWILIGHT; +} + +static bool8 MapHasNaturalLight(u8 mapType) { // Weather a map type is naturally lit/outside + return mapType == MAP_TYPE_TOWN || mapType == MAP_TYPE_CITY || mapType == MAP_TYPE_ROUTE; +} + +static bool8 FadePalettesWithTime(void) { + gTimeOfDayState = 0; + gTimeOfDay = UpdateTimeOfDay(); + if (MapHasNaturalLight(gMapHeader.mapType)) { + ResetPaletteFade(); + BeginTimeOfDayPaletteFade(0xFFFFFFFF, 0, 16, sTimeOfDayBlendVars[gTimeOfDay].coeff, sTimeOfDayBlendVars[gTimeOfDay].blendColor); + } +} + +void BlendPalettesWithTime(u32 palettes) { + // Only blend if not transitioning between times and the map type allows + if (gTimeOfDayState == 0 && MapHasNaturalLight(gMapHeader.mapType)) { + u8 i; + for (i = 0; i < 16; i++) { + if (GetSpritePaletteTagByPaletteNum(i) & 0x8000) // Don't blend special sprite palette tags + palettes &= ~(1 << (i + 16)); + } + palettes &= ~0xE000; // Don't blend tile palettes [13,15] + gTimeOfDay = gTimeOfDay > TIME_OF_DAY_MAX ? TIME_OF_DAY_MAX : gTimeOfDay; + BlendPalettes(palettes, sTimeOfDayBlendVars[gTimeOfDay].coeff, sTimeOfDayBlendVars[gTimeOfDay].blendColor); + } +} + +u8 UpdateSpritePaletteWithTime(u8 paletteNum) { + BlendPalettesWithTime(1 << (paletteNum + 16)); + return paletteNum; +} + static void OverworldBasic(void) { ScriptContext2_RunScript(); @@ -1463,12 +1523,16 @@ void CB2_OverworldBasic(void) void CB2_Overworld(void) { + u32 *debugPtr = (u32*) 0x0203de00; bool32 fading = (gPaletteFade.active != 0); + *debugPtr = (u32) &gTimeOfDay; if (fading) SetVBlankCallback(NULL); OverworldBasic(); - if (fading) - SetFieldVBlankCallback(); + if (fading) { + SetFieldVBlankCallback(); + return; + } } void SetMainCallback1(MainCallback cb) @@ -1564,6 +1628,7 @@ static void CB2_LoadMap2(void) DoMapLoadLoop(&gMain.state); SetFieldVBlankCallback(); SetMainCallback1(CB1_Overworld); + FadePalettesWithTime(); SetMainCallback2(CB2_Overworld); } @@ -1620,6 +1685,7 @@ static void CB2_ReturnToFieldLocal(void) if (ReturnToFieldLocal(&gMain.state)) { SetFieldVBlankCallback(); + FadePalettesWithTime(); SetMainCallback2(CB2_Overworld); } } @@ -1966,7 +2032,6 @@ static bool32 ReturnToFieldLocal(u8 *state) case 3: return TRUE; } - return FALSE; } diff --git a/src/palette.c b/src/palette.c index cbaae8da28..a367a5ed43 100644 --- a/src/palette.c +++ b/src/palette.c @@ -11,6 +11,7 @@ enum NORMAL_FADE, FAST_FADE, HARDWARE_FADE, + TIME_OF_DAY_FADE, }; // These are structs for some unused palette system. @@ -50,6 +51,7 @@ static u8 UpdateNormalPaletteFade(void); static void BeginFastPaletteFadeInternal(u8); static u8 UpdateFastPaletteFade(void); static u8 UpdateHardwarePaletteFade(void); +static u8 UpdateTimeOfDayPaletteFade(void); static void UpdateBlendRegisters(void); static bool8 IsSoftwarePaletteFadeFinishing(void); static void Task_BlendPalettesGradually(u8 taskId); @@ -123,6 +125,8 @@ u8 UpdatePaletteFade(void) result = UpdateNormalPaletteFade(); else if (gPaletteFade.mode == FAST_FADE) result = UpdateFastPaletteFade(); + else if (gPaletteFade.mode == TIME_OF_DAY_FADE) + result = UpdateTimeOfDayPaletteFade(); else result = UpdateHardwarePaletteFade(); @@ -199,6 +203,53 @@ bool8 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targe } } +// Like normal palette fade but respects sprite/tile palettes immune to time of day fading +bool8 BeginTimeOfDayPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, u16 blendColor) +{ + u8 temp; + u16 color = blendColor; + + if (gPaletteFade.active) + { + return FALSE; + } + else + { + gPaletteFade.deltaY = 2; + + if (delay < 0) + { + gPaletteFade.deltaY += (delay * -1); + delay = 0; + } + + gPaletteFade_selectedPalettes = selectedPalettes; + gPaletteFade.delayCounter = delay; + gPaletteFade_delay = delay; + gPaletteFade.y = startY; + gPaletteFade.targetY = targetY; + gPaletteFade.blendColor = color; + gPaletteFade.active = 1; + gPaletteFade.mode = TIME_OF_DAY_FADE; + + if (startY < targetY) + gPaletteFade.yDec = 0; + else + gPaletteFade.yDec = 1; + + UpdatePaletteFade(); + + temp = gPaletteFade.bufferTransferDisabled; + gPaletteFade.bufferTransferDisabled = 0; + CpuCopy32(gPlttBufferFaded, (void *)PLTT, PLTT_SIZE); + sPlttBufferTransferPending = 0; + if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active) + UpdateBlendRegisters(); + gPaletteFade.bufferTransferDisabled = temp; + return TRUE; + } +} + bool8 unref_sub_80A1C1C(u32 a1, u8 a2, u8 a3, u8 a4, u16 a5) { ReadPlttIntoBuffers(); @@ -405,6 +456,94 @@ static u8 GetPaletteNumByUid(u16 uid) return 16; } +// Like normal palette fade, but respects sprite/tile palettes immune to time of day fading +static u8 UpdateTimeOfDayPaletteFade(void) // Like normal, but respects sprite palettes immune to fading +{ + u8 paletteNum; + u16 paletteOffset; + u16 selectedPalettes; + + if (!gPaletteFade.active) + return PALETTE_FADE_STATUS_DONE; + + if (IsSoftwarePaletteFadeFinishing()) + return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE; + + if (!gPaletteFade.objPaletteToggle) + { + if (gPaletteFade.delayCounter < gPaletteFade_delay) + { + gPaletteFade.delayCounter++; + return 2; + } + gPaletteFade.delayCounter = 0; + } + + paletteOffset = 0; + + if (!gPaletteFade.objPaletteToggle) + { + selectedPalettes = gPaletteFade_selectedPalettes; + } + else + { + selectedPalettes = gPaletteFade_selectedPalettes >> 16; + paletteOffset = 256; + } + + for (paletteNum = 0; paletteNum < 16; paletteNum++, selectedPalettes >>= 1, paletteOffset += 16) { + if (selectedPalettes & 1) { + if (gPaletteFade.yDec) { + if (gPaletteFade.objPaletteToggle) { // sprite palettes + if (gPaletteFade.y >= gPaletteFade.targetY || GetSpritePaletteTagByPaletteNum(paletteNum) & 0x8000) + BlendPalette(paletteOffset, 16, gPaletteFade.y, gPaletteFade.blendColor); + // tile palettes + } else if (gPaletteFade.y >= gPaletteFade.targetY || (paletteNum >= 13 && paletteNum <= 15)) { + BlendPalette(paletteOffset, 16, gPaletteFade.y, gPaletteFade.blendColor); + } + } else { + BlendPalette(paletteOffset, 16, gPaletteFade.y, gPaletteFade.blendColor); + } + } + } + + gPaletteFade.objPaletteToggle ^= 1; + + if (!gPaletteFade.objPaletteToggle) + { + if ((gPaletteFade.yDec && gPaletteFade.y == 0) || (!gPaletteFade.yDec && gPaletteFade.y == gPaletteFade.targetY)) + { + gPaletteFade_selectedPalettes = 0; + gPaletteFade.softwareFadeFinishing = 1; + } + else + { + s8 val; + + if (!gPaletteFade.yDec) + { + val = gPaletteFade.y; + val += gPaletteFade.deltaY; + if (val > gPaletteFade.targetY) + val = gPaletteFade.targetY; + gPaletteFade.y = val; + } + else + { + val = gPaletteFade.y; + val -= gPaletteFade.deltaY; + if (val < 0) + val = 0; + gPaletteFade.y = val; + } + } + } + + // gPaletteFade.active cannot change since the last time it was checked. So this + // is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;` + return PALETTE_FADE_STATUS_ACTIVE; +} + static u8 UpdateNormalPaletteFade(void) { u16 paletteOffset; @@ -586,7 +725,6 @@ static u8 UpdateFastPaletteFade(void) if (IsSoftwarePaletteFadeFinishing()) return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE; - if (gPaletteFade.objPaletteToggle) { @@ -721,7 +859,6 @@ static u8 UpdateFastPaletteFade(void) gPaletteFade.mode = NORMAL_FADE; gPaletteFade.softwareFadeFinishing = 1; } - // gPaletteFade.active cannot change since the last time it was checked. So this // is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;` return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;