diff --git a/asm/macros/event.inc b/asm/macros/event.inc index f621dd16cd..3a651191de 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -1790,11 +1790,14 @@ .4byte \text .endm - @ Equivalent to fadescreen but copies gPlttBufferUnfaded to an allocated buffer on the fade out - @ and the reverse on the fade in, in effect saving gPlttBufferUnfaded to restore it. - .macro fadescreenswapbuffers mode:req + @ 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 SCR_OP_FADESCREENSWAPBUFFERS .byte \mode + .byte \nowait .endm @ Buffers the specified trainer's class name to the given string var. diff --git a/asm/macros/field_effect_script.inc b/asm/macros/field_effect_script.inc index 38f7e31750..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,9 +6,10 @@ .4byte \address .endm - .macro field_eff_loadfadedpal address:req + .macro field_eff_loadfadedpal address:req, color_map_type=COLOR_MAP_DARK_CONTRAST .byte 1 .4byte \address + .byte \color_map_type .endm .macro field_eff_loadpal address:req @@ -24,10 +26,11 @@ .byte 4 .endm - .macro field_eff_loadgfx_callnative tiles_address:req, palette_address:req, function_address:req + .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 + .byte \color_map_type .4byte \function_address .endm @@ -37,8 +40,9 @@ .4byte \function_address .endm - .macro field_eff_loadfadedpal_callnative palette_address:req, function_address:req + .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 .4byte \function_address .endm diff --git a/data/event_scripts.s b/data/event_scripts.s index 960a5fa084..b887876f6e 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -760,12 +760,12 @@ Common_EventScript_PlayGymBadgeFanfare:: return Common_EventScript_OutOfCenterPartyHeal:: - fadescreen FADE_TO_BLACK + fadescreenswapbuffers FADE_TO_BLACK playfanfare MUS_HEAL waitfanfare special HealPlayerParty callnative UpdateFollowingPokemon - fadescreen FADE_FROM_BLACK + fadescreenswapbuffers FADE_FROM_BLACK return EventScript_RegionMap:: diff --git a/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc index 6279cacf27..edede9d737 100644 --- a/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc @@ -476,6 +476,7 @@ BattleFrontier_BattleDomeBattleRoom_EventScript_SetUpObjects:: call BattleFrontier_EventScript_SetBrainObjectGfx setobjectxyperm LOCALID_OPPONENT, 13, 9 removeobject LOCALID_OPPONENT + delay 1 addobject LOCALID_OPPONENT applymovement LOCALID_OPPONENT, BattleFrontier_BattleDomeBattleRoom_Movement_SetInvisibleFacingUp BattleFrontier_BattleDomeBattleRoom_EventScript_EndSetUpObjects:: diff --git a/data/maps/MtChimney/scripts.inc b/data/maps/MtChimney/scripts.inc index fbd0da31ae..48616e8caa 100644 --- a/data/maps/MtChimney/scripts.inc +++ b/data/maps/MtChimney/scripts.inc @@ -54,13 +54,13 @@ MtChimney_EventScript_Maxie:: msgbox MtChimney_Text_MaxieYouHaventSeenLastOfMagma, MSGBOX_DEFAULT closemessage delay 30 - fadescreen FADE_TO_BLACK + fadescreenswapbuffers FADE_TO_BLACK removeobject LOCALID_MAXIE removeobject LOCALID_MAGMA_GRUNT_1 removeobject LOCALID_TABITHA removeobject LOCALID_MAGMA_GRUNT_2 setflag FLAG_HIDE_MT_CHIMNEY_TEAM_MAGMA - fadescreen FADE_FROM_BLACK + fadescreenswapbuffers FADE_FROM_BLACK setobjectxyperm LOCALID_ARCHIE, 10, 12 addobject LOCALID_ARCHIE call_if_eq VAR_FACING, DIR_EAST, MtChimney_EventScript_ArchieApproachPlayerEast diff --git a/data/maps/MtPyre_Summit/scripts.inc b/data/maps/MtPyre_Summit/scripts.inc index 3de60df3f2..2d85cf17f5 100644 --- a/data/maps/MtPyre_Summit/scripts.inc +++ b/data/maps/MtPyre_Summit/scripts.inc @@ -49,7 +49,7 @@ MtPyre_Summit_EventScript_TeamAquaExits:: call_if_eq VAR_0x8008, 2, MtPyre_Summit_EventScript_ArchieFacePlayer2 msgbox MtPyre_Summit_Text_ArchieWeGotTheOrbLetsGo, MSGBOX_DEFAULT closemessage - fadescreen FADE_TO_BLACK + fadescreenswapbuffers FADE_TO_BLACK removeobject LOCALID_ARCHIE removeobject LOCALID_GRUNT_1 removeobject LOCALID_GRUNT_2 @@ -58,7 +58,7 @@ MtPyre_Summit_EventScript_TeamAquaExits:: setflag FLAG_HIDE_MT_PYRE_SUMMIT_ARCHIE setflag FLAG_HIDE_MT_PYRE_SUMMIT_TEAM_AQUA fadedefaultbgm - fadescreen FADE_FROM_BLACK + fadescreenswapbuffers FADE_FROM_BLACK delay 20 setvar VAR_MT_PYRE_STATE, 1 call_if_eq VAR_0x8008, 0, MtPyre_Summit_EventScript_OldLadyApproachPlayer0 diff --git a/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc b/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc index 53514bb5e9..76b6784562 100644 --- a/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc +++ b/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc @@ -48,6 +48,7 @@ SlateportCity_BattleTentBattleRoom_EventScript_EnterRoom:: factory_setopponentgfx setobjectxyperm LOCALID_OPPONENT, 5, 1 removeobject LOCALID_OPPONENT + delay 1 addobject LOCALID_OPPONENT applymovement LOCALID_OPPONENT, SlateportCity_BattleTentBattleRoom_Movement_OpponentEnter waitmovement 0 diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index aff65a4ca2..2d521b6a97 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -17,6 +17,7 @@ - [v1.6.x and earlier](tutorials/how_to_new_pokemon_1_6_0.md) - [How to use the Testing System](tutorials/how_to_testing_system.md) - [How to add new Trainer Slides](tutorials/how_to_new_trainer_slide.md) + - [Day/Night System FAQ](tutorials/dns.md) - [Changelog](./CHANGELOG.md) - [1.11.x]() - [Version 1.11.1](changelogs/1.11.x/1.11.1.md) diff --git a/docs/tutorials/dns.md b/docs/tutorials/dns.md new file mode 100644 index 0000000000..41d306c500 --- /dev/null +++ b/docs/tutorials/dns.md @@ -0,0 +1,25 @@ +## Day/Night system FAQ + +### Q: How do I disable DNS? +A: Set `OW_ENABLE_DNS` to `FALSE` in `include/config/overworld.h`. + +### Q: How do I mark certain colors in a palette as light-blended? +A: Create a `.pla` file in the same folder as the `.pal` with the same name. + +In this file you can enter color indices [0,15] +on separate lines to mark those colors as being light-blended, i.e: + +`06.pla:` +``` +# A comment +0 # if color 0 is listed, uses it to blend with instead of the default! +1 +9 +10 +``` + +### Q: How do I return to using regular shadows? +A: Set `OW_OBJECT_VANILLA_SHADOWS` to `TRUE` in `include/config/overworld.h`. + +### Q: How do I disable shadows for certain locations? +A: Shadows can be disabled for certain locations by modifying the `CurrentMapHasShadows` function in `src/overworld.c`. diff --git a/graphics/object_events/palettes/light.pal b/graphics/object_events/palettes/light.pal new file mode 100644 index 0000000000..92a4477f76 --- /dev/null +++ b/graphics/object_events/palettes/light.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +110 198 165 +64 61 42 +77 73 50 +85 81 58 +100 94 66 +111 105 75 +119 112 77 +140 131 92 +154 145 103 +171 159 111 +186 174 123 +197 183 128 +209 195 139 +224 209 146 +234 219 155 +249 232 161 diff --git a/graphics/object_events/palettes/light2.pal b/graphics/object_events/palettes/light2.pal new file mode 100644 index 0000000000..3d61a3020d --- /dev/null +++ b/graphics/object_events/palettes/light2.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +106 202 166 +58 54 31 +67 62 33 +96 89 53 +110 102 57 +121 111 67 +128 119 66 +156 143 82 +166 155 96 +185 169 100 +199 183 112 +211 192 113 +223 203 123 +237 218 129 +247 227 137 +255 237 147 diff --git a/graphics/object_events/palettes/neon_light.pal b/graphics/object_events/palettes/neon_light.pal new file mode 100644 index 0000000000..dd287fc645 --- /dev/null +++ b/graphics/object_events/palettes/neon_light.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +104 203 168 +19 48 64 +35 87 115 +58 132 140 +69 156 166 +77 193 254 +106 239 254 +27 68 89 +64 22 19 +115 39 34 +166 57 49 +191 66 57 +222 77 67 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/object_events/pics/misc/light.png b/graphics/object_events/pics/misc/light.png index d1a0402d10..22687a7c00 100644 Binary files a/graphics/object_events/pics/misc/light.png and b/graphics/object_events/pics/misc/light.png differ diff --git a/graphics/object_events/pics/misc/mart_light.png b/graphics/object_events/pics/misc/mart_light.png new file mode 100644 index 0000000000..7ec1a434ea Binary files /dev/null and b/graphics/object_events/pics/misc/mart_light.png differ diff --git a/graphics/object_events/pics/misc/poke_center_light.png b/graphics/object_events/pics/misc/poke_center_light.png new file mode 100644 index 0000000000..42d19f51c6 Binary files /dev/null and b/graphics/object_events/pics/misc/poke_center_light.png differ diff --git a/include/clock.h b/include/clock.h index 0cfd3db9ed..95d0929950 100644 --- a/include/clock.h +++ b/include/clock.h @@ -5,5 +5,6 @@ void InitTimeBasedEvents(void); void DoTimeBasedEvents(void); +void FormChangeTimeUpdate(); #endif // GUARD_CLOCK_H diff --git a/include/config/overworld.h b/include/config/overworld.h index 90aa546d48..d3382913de 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -62,31 +62,13 @@ #define OW_FOLLOWERS_COPY_WILD_PKMN FALSE // If TRUE, follower Pokémon that know Transform or have Illusion/Imposter will copy wild Pokémon at random. #define OW_BATTLE_ONLY_FORMS TRUE // If TRUE, loads overworld sprites for battle-only forms like Mega Evos. Requires OW_POKEMON_OBJECT_EVENTS. #define B_FLAG_FOLLOWERS_DISABLED 0 // Enables / Disables followers by using a flag. Helpful to disable followers for a period of time. - - -#define OW_FOLLOWERS_SCRIPT_MOVEMENT TRUE // TRUE: Script collisions hide follower, FLAG_SAFE_FOLLOWER_MOVEMENT on by default - // FALSE: Script collisions unhandled, FLAG_SAFE_FOLLOWER_MOVEMENT off by default - -// If set, the only pokemon allowed to follow you -// will be those matching species, met location, -// and/or met level; -// These accept vars, too: VAR_TEMP_1, etc -#define OW_MON_ALLOWED_SPECIES (0) -#define OW_MON_ALLOWED_MET_LVL (0) -#define OW_MON_ALLOWED_MET_LOC (0) -// Examples: -// Yellow Pikachu: -// #define OW_MON_ALLOWED_SPECIES (SPECIES_PIKACHU) -// #define OW_MON_ALLOWED_MET_LVL (0) -// #define OW_MON_ALLOWED_MET_LOC (MAPSEC_PALLET_TOWN) -// Hoenn Starter: -// #define OW_MON_ALLOWED_SPECIES (0) -// #define OW_MON_ALLOWED_MET_LVL (5) -// #define OW_MON_ALLOWED_MET_LOC (MAPSEC_ROUTE_101) -// Species set in VAR_XXXX: -// #define OW_MON_ALLOWED_SPECIES (VAR_XXXX) -// #define OW_MON_ALLOWED_MET_LVL (0) -// #define OW_MON_ALLOWED_MET_LOC (0) +#define OW_FOLLOWERS_SCRIPT_MOVEMENT TRUE // If TRUE, follower Pokémon only go back to their Poké Ball if a non-player collides with them by setting the FLAG_SAFE_FOLLOWER_MOVEMENT flag by default. +// Follower Pokémon Restrictions +// If set, the only pokemon allowed to follow you will be those matching species, met location, and/or met level; These accept vars, too: VAR_TEMP_1, etc +// For examples, see "docs/tutorials/how_to_new_pokemon.md" +#define OW_FOLLOWERS_ALLOWED_SPECIES (0) +#define OW_FOLLOWERS_ALLOWED_MET_LVL (0) +#define OW_FOLLOWERS_ALLOWED_MET_LOC (0) // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. @@ -111,6 +93,14 @@ #define OW_TIME_OF_DAY_DEFAULT TIME_MORNING // Should be set to whatever is the first value in the TimeOfDay enum in rtc.h #define OW_TIME_OF_DAY_FALLBACK OW_TIME_OF_DAY_DEFAULT // The time of day that encounter tables fall back to. Defaults to whatever OW_TIME_OF_DAY_FALLBACK is set to. +// Lighting +#define OW_SHADOW_INTENSITY 4 // Ranges from 0 to 16, where 0 is fully transparent and 16 is black. +#define OW_OBJECT_SUBPRIORITY 148 // The higher the value, the farther back compared to other sprites. Shadows should be behind object events. +#define OW_ENABLE_DNS TRUE // If set to TRUE, the overworld will be tinted depending on time of day. + +// Object Event Shadows +#define OW_OBJECT_VANILLA_SHADOWS FALSE // In vanilla shadows in the overworld are only shown when jumping. + // Overworld flags // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. // Eg: Replace with FLAG_UNUSED_0x264 so you can use that flag to toggle the feature. diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index db8d96cae4..7cd210275c 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -242,11 +242,12 @@ #define OBJ_EVENT_GFX_HOOH 238 #define OBJ_EVENT_GFX_POKE_BALL 239 #define OBJ_EVENT_GFX_OW_MON 240 +#define OBJ_EVENT_GFX_LIGHT_SPRITE 241 // NOTE: The maximum amount of object events has been expanded from 255 to 65535. // Since dynamic graphics ids still require at least 16 free values, the actual limit // is 65519, but even considering follower Pokémon, this should be more than enough :) -#define NUM_OBJ_EVENT_GFX 241 +#define NUM_OBJ_EVENT_GFX 242 // These are dynamic object gfx ids. @@ -270,6 +271,7 @@ #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) +// Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) #define OBJ_EVENT_MON_FEMALE (1u << 12) @@ -305,12 +307,19 @@ #define TRACKS_SPOT 4 #define TRACKS_BUG 5 +#define LIGHT_TYPE_BALL 0 +#define LIGHT_TYPE_PKMN_CENTER_SIGN 1 +#define LIGHT_TYPE_POKE_MART_SIGN 2 + #define FIRST_DECORATION_SPRITE_GFX OBJ_EVENT_GFX_PICHU_DOLL #define OBJ_KIND_NORMAL 0 #define OBJ_KIND_CLONE 255 // Exclusive to FRLG // Special object event local ids +// Used for link player OWs in CreateLinkPlayerSprite +#define OBJ_EVENT_ID_DYNAMIC_BASE 0xF0 + #define OBJ_EVENT_ID_PLAYER 0xFF #define OBJ_EVENT_ID_CAMERA 0x7F #define OBJ_EVENT_ID_FOLLOWER 0xFE @@ -421,7 +430,10 @@ #endif //OW_FOLLOWERS_POKEBALLS // Used as a placeholder follower graphic #define OBJ_EVENT_PAL_TAG_SUBSTITUTE 0x7611 -#define OBJ_EVENT_PAL_TAG_EMOTES 0x8002 +#define OBJ_EVENT_PAL_TAG_LIGHT 0x8001 +#define OBJ_EVENT_PAL_TAG_LIGHT_2 0x8002 +#define OBJ_EVENT_PAL_TAG_EMOTES 0x8003 +#define OBJ_EVENT_PAL_TAG_NEON_LIGHT 0x8004 // Not a real OW palette tag; used for the white flash applied to followers #define OBJ_EVENT_PAL_TAG_WHITE (OBJ_EVENT_PAL_TAG_NONE - 1) #define OBJ_EVENT_PAL_TAG_NONE 0x11FF diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index 6c64572c21..e47cc292c3 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -133,5 +133,12 @@ #define FLDEFF_PAL_TAG_HOF_MONITOR 0x1010 #define FLDEFF_PAL_TAG_UNKNOWN 0x1011 #define FLDEFF_PAL_TAG_CAVE_DUST 0x1012 +#define FLDEFF_PAL_TAG_FIELD_MOVE_MON 0x8400 + +// tile tags, for field effects that may have many copies on screen at once +#define FLDEFF_TILE_TAG_SHADOW_SMALL 0x1400 +#define FLDEFF_TILE_TAG_SHADOW_MEDIUM 0x1401 +#define FLDEFF_TILE_TAG_SHADOW_LARGE 0x1402 +#define FLDEFF_TILE_TAG_SHADOW_EXTRA_LARGE 0x1403 #endif // GUARD_FIELD_EFFECT_CONSTANTS_H diff --git a/include/constants/field_weather.h b/include/constants/field_weather.h index fe7eb6a1ae..133e6d935a 100644 --- a/include/constants/field_weather.h +++ b/include/constants/field_weather.h @@ -1,6 +1,16 @@ #ifndef GUARD_CONSTANTS_FIELD_WEATHER_H #define GUARD_CONSTANTS_FIELD_WEATHER_H +#include "config/overworld.h" + +// sPaletteColorMapTypes & field_effect_scripts +enum ColorMapType +{ + COLOR_MAP_NONE, + COLOR_MAP_DARK_CONTRAST, + COLOR_MAP_CONTRAST, +}; + #define MAX_RAIN_SPRITES 24 #define NUM_CLOUD_SPRITES 3 #define NUM_FOG_HORIZONTAL_SPRITES 20 @@ -22,4 +32,8 @@ #define FADE_FROM_WHITE 2 #define FADE_TO_WHITE 3 +// Shadows values +#define BASE_SHADOW_INTENSITY (16 - OW_SHADOW_INTENSITY) +#define SHADOW_COLOR_INDEX 9 // Within the weather palette, shadow sprites' color index + #endif // GUARD_CONSTANTS_FIELD_WEATHER_H diff --git a/include/constants/rgb.h b/include/constants/rgb.h index f73ab53b4b..9d50e8cd8b 100644 --- a/include/constants/rgb.h +++ b/include/constants/rgb.h @@ -15,6 +15,7 @@ #define RGB_BLACK RGB(0, 0, 0) #define RGB_WHITE RGB(31, 31, 31) +#define RGB_GRAY RGB(15, 15, 15) #define RGB_RED RGB(31, 0, 0) #define RGB_GREEN RGB(0, 31, 0) #define RGB_BLUE RGB(0, 0, 31) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 679ad80ea4..85e3c62413 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -145,13 +145,17 @@ u16 GetOverworldWeatherSpecies(u16 species); void UpdateFollowingPokemon(void); void RemoveFollowingPokemon(void); struct ObjectEvent *GetFollowerObject(void); +u8 GetDirectionToFace(s16, s16, s16, s16); +void UpdateLightSprite(struct Sprite *); void TrySpawnObjectEvents(s16 cameraX, s16 cameraY); +u8 CreateObjectGraphicsSpriteWithTag(u16 graphicsId, void (*callback)(struct Sprite *), s16 x, s16 y, u8 subpriority, u16 paletteTag); u8 CreateObjectGraphicsSprite(u16, void (*)(struct Sprite *), s16 x, s16 y, u8 subpriority); u8 TrySpawnObjectEvent(u8 localId, u8 mapNum, u8 mapGroup); u8 SpawnSpecialObjectEventParameterized(u16 graphicsId, u8 movementBehavior, u8 localId, s16 x, s16 y, u8 elevation); u8 SpawnSpecialObjectEvent(struct ObjectEventTemplate *); void SetSpritePosToMapCoords(s16 mapX, s16 mapY, s16 *destX, s16 *destY); void CameraObjectReset(void); +u8 LoadObjectEventPalette(u16); u8 UpdateSpritePaletteByTemplate(const struct SpriteTemplate *, struct Sprite *); void ObjectEventSetGraphicsId(struct ObjectEvent *, u16 graphicsId); void ObjectEventTurn(struct ObjectEvent *, u8 direction); diff --git a/include/field_effect_helpers.h b/include/field_effect_helpers.h index 22dcb119a4..890e5ca3ac 100644 --- a/include/field_effect_helpers.h +++ b/include/field_effect_helpers.h @@ -21,7 +21,7 @@ bool8 UpdateRevealDisguise(struct ObjectEvent *); void StartRevealDisguise(struct ObjectEvent *); void StartAshFieldEffect(s16, s16, u16, s16); void SetUpReflection(struct ObjectEvent *, struct Sprite *, u8); -void SetUpShadow(struct ObjectEvent *, struct Sprite *); +void SetUpShadow(struct ObjectEvent *); u32 StartFieldEffectForObjectEvent(u8, struct ObjectEvent *); u8 FindTallGrassFieldEffectSpriteId(u8 localId, u8 mapNum, u8 mapGroup, s16 x, s16 y); void UpdateRayquazaSpotlightEffect(struct Sprite *); diff --git a/include/field_weather.h b/include/field_weather.h index cff2eb5bc3..e8e34f4940 100644 --- a/include/field_weather.h +++ b/include/field_weather.h @@ -44,7 +44,8 @@ struct Weather s8 targetColorMapIndex; u8 colorMapStepDelay; u8 colorMapStepCounter; - u16 fadeDestColor; + u16 fadeDestColor:15; + u16 noShadows:1; // Certain weathers require blend coeffs that do not work nice with shadows u8 palProcessingState; u8 fadeScreenCounter; bool8 readyForInit; @@ -147,9 +148,10 @@ void SetCurrentAndNextWeatherNoDelay(u8 weather); void ApplyWeatherColorMapIfIdle(s8 colorMapIndex); void ApplyWeatherColorMapIfIdle_Gradual(u8 colorMapIndex, u8 targetColorMapIndex, u8 colorMapStepDelay); void FadeScreen(u8 mode, s8 delay); +void FadeScreenHardware(u32 mode, s32 delay); bool8 IsWeatherNotFadingIn(void); -void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex); -void ApplyWeatherColorMapToPal(u8 paletteIndex); +void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex, bool8 allowFog); +void ApplyWeatherColorMapToPals(u8 startPalIndex, u8 numPalettes); void LoadCustomWeatherSpritePalette(const u16 *palette); void ResetDroughtWeatherPaletteLoading(void); bool8 LoadDroughtWeatherPalettes(void); @@ -164,7 +166,9 @@ void PlayRainStoppingSoundEffect(void); u8 IsWeatherChangeComplete(void); void SetWeatherScreenFadeOut(void); void SetWeatherPalStateIdle(void); +const u8 *SetPaletteColorMapType(u8 paletteIndex, enum ColorMapType colorMapType); void PreservePaletteInWeather(u8 preservedPalIndex); +void ResetPaletteColorMapType(u8 paletteIndex); void ResetPreservedPalettesInWeather(void); bool32 IsWeatherAlphaBlend(void); @@ -190,6 +194,7 @@ void Thunderstorm_Main(void); void Thunderstorm_InitAll(void); bool8 Thunderstorm_Finish(void); void FogHorizontal_InitVars(void); +u8 UpdateShadowColor(u16 color); void FogHorizontal_Main(void); void FogHorizontal_InitAll(void); bool8 FogHorizontal_Finish(void); diff --git a/include/fieldmap.h b/include/fieldmap.h index ecb1e49c4d..318e916039 100644 --- a/include/fieldmap.h +++ b/include/fieldmap.h @@ -45,7 +45,7 @@ void InitTrainerHillMap(void); void InitBattlePyramidMap(bool8 setPlayerPosition); void CopyMapTilesetsToVram(struct MapLayout const *mapLayout); void LoadMapTilesetPalettes(struct MapLayout const *mapLayout); -void LoadSecondaryTilesetPalette(struct MapLayout const *mapLayout); +void LoadSecondaryTilesetPalette(struct MapLayout const *mapLayout, bool8 skipFaded); void CopySecondaryTilesetToVramUsingHeap(struct MapLayout const *mapLayout); void CopyPrimaryTilesetToVram(const struct MapLayout *); void CopySecondaryTilesetToVram(const struct MapLayout *); diff --git a/include/follower_helper.h b/include/follower_helper.h index 27bf1f700e..a909313112 100644 --- a/include/follower_helper.h +++ b/include/follower_helper.h @@ -111,6 +111,8 @@ enum { COND_MSG_LEAVES, COND_MSG_ICE, COND_MSG_BURN, + COND_MSG_DAY, + COND_MSG_NIGHT, COND_MSG_COUNT, }; diff --git a/include/gba/io_reg.h b/include/gba/io_reg.h index db219aeb35..fd4b525b06 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/include/global.fieldmap.h b/include/global.fieldmap.h index 5a4ffca26b..5f2a84017f 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -37,8 +37,11 @@ typedef void (*TilesetCB)(void); struct Tileset { - /*0x00*/ bool8 isCompressed; + /*0x00*/ u8 isCompressed:1; + /*0x00*/ u8 swapPalettes:7; // Bitmask determining whether palette has an alternate, night-time palette /*0x01*/ bool8 isSecondary; + /*0x02*/ u8 lightPalettes; // Bitmask determining whether a palette should be time-blended as a light + /*0x03*/ u8 customLightColor; // Bitmask determining which light palettes have custom light colors (color 15) /*0x04*/ const u32 *tiles; /*0x08*/ const u16 (*palettes)[16]; /*0x0C*/ const u16 *metatiles; @@ -188,14 +191,15 @@ struct ObjectEvent u32 inShallowFlowingWater:1; u32 inSandPile:1; u32 inHotSprings:1; - u32 hasShadow:1; + u32 noShadow:1; u32 spriteAnimPausedBackup:1; /*0x03*/ u32 spriteAffineAnimPausedBackup:1; u32 disableJumpLandingGroundEffect:1; u32 fixedPriority:1; u32 hideReflection:1; u32 shiny:1; // OW mon shininess - u32 padding:3; + u32 jumpDone:1; + u32 padding:2; /*0x04*/ u16 graphicsId; // 12 bits for species; high 4 bits for form /*0x06*/ u8 movementType; /*0x07*/ u8 trainerType; diff --git a/include/global.h b/include/global.h index 0ba5cd28b9..b263322651 100644 --- a/include/global.h +++ b/include/global.h @@ -569,6 +569,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 ea23c15b30..4408fd5a9d 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -26,6 +26,11 @@ #define SKIP_OBJECT_EVENT_LOAD 1 +// trigger a time-of-day blend once +#define HOURS_BLEND_ONCE 25 +// don't update gTimeBlend +#define HOURS_FREEZE_BLEND 26 + struct InitialPlayerAvatarState { u8 transitionFlags; @@ -40,6 +45,7 @@ struct LinkPlayerObjectEvent u8 movementMode; }; +// Exported RAM declarations extern struct WarpData gLastUsedWarp; extern struct LinkPlayerObjectEvent gLinkPlayerObjectEvents[4]; @@ -53,6 +59,10 @@ extern u8 gLocalLinkPlayerId; extern u8 gFieldLinkPlayerCount; extern bool8 gExitStairsMovementDisabled; extern bool8 gSkipShowMonAnim; +extern u8 gTimeOfDay; +extern s16 gTimeUpdateCounter; + +extern struct TimeBlendSettings gTimeBlend; extern const struct UCoords32 gDirectionToVectors[]; @@ -130,6 +140,11 @@ void CleanupOverworldWindowsAndTilemaps(void); bool32 IsOverworldLinkActive(void); void CB1_Overworld(void); void CB2_OverworldBasic(void); +void UpdateTimeOfDay(void); +bool32 MapHasNaturalLight(u8 mapType); +bool32 CurrentMapHasShadows(void); +void UpdateAltBgPalettes(u16 palettes); +void UpdatePalettesWithTime(u32); void CB2_Overworld(void); void SetMainCallback1(void (*cb)(void)); void SetUnusedCallback(void *func); @@ -156,6 +171,7 @@ bool32 Overworld_RecvKeysFromLinkIsRunning(void); bool32 Overworld_SendKeysToLinkIsRunning(void); bool32 IsSendingKeysOverCable(void); void ClearLinkPlayerObjectEvents(void); +bool16 SetTimeOfDay(u16 hours); // Item Description Headers enum ItemObtainFlags diff --git a/include/palette.h b/include/palette.h index 13c06b0078..27b8976607 100644 --- a/include/palette.h +++ b/include/palette.h @@ -1,10 +1,10 @@ #ifndef GUARD_PALETTE_H #define GUARD_PALETTE_H -#define gPaletteFade_selectedPalettes (gPaletteFade.multipurpose1) // normal and fast fade -#define gPaletteFade_blendCnt (gPaletteFade.multipurpose1) // hardware fade -#define gPaletteFade_delay (gPaletteFade.multipurpose2) // normal and hardware fade -#define gPaletteFade_submode (gPaletteFade.multipurpose2) // fast fade +#define gPaletteFadeSelectedPalettes (gPaletteFade.multipurpose1) // normal and fast fade +#define gPaletteFadeBlendCnt (gPaletteFade.multipurpose1) // hardware fade +#define gPaletteFadeDelay (gPaletteFade.multipurpose2) // normal and hardware fade +#define gPaletteFadeSubmode (gPaletteFade.multipurpose2) // fast fade #define PLTT_BUFFER_SIZE (PLTT_SIZE / sizeof(u16)) @@ -14,6 +14,8 @@ #define PALETTE_FADE_STATUS_LOADING 0xFF #define PALETTES_BG 0x0000FFFF +// like PALETTES_BG but excludes UI pals [13, 15] +#define PALETTES_MAP 0x00001FFF #define PALETTES_OBJECTS 0xFFFF0000 #define PALETTES_ALL (PALETTES_BG | PALETTES_OBJECTS) @@ -24,6 +26,10 @@ #define OBJ_PLTT_ID(n) (OBJ_PLTT_OFFSET + PLTT_ID(n)) #define OBJ_PLTT_ID2(n) (PLTT_ID((n) + 16)) +// Used to determine whether a sprite palette tag should be excluded from time (and weather) blending +#define BLEND_IMMUNE_FLAG (1 << 15) +#define IS_BLEND_IMMUNE_TAG(tag) ((tag) & BLEND_IMMUNE_FLAG) + enum { FAST_FADE_IN_FROM_WHITE, @@ -32,26 +38,50 @@ enum FAST_FADE_OUT_TO_BLACK, }; +struct BlendSettings +{ + u32 blendColor:24; + u32 isTint:1; + u32 coeff:5; + u32 unused:2; +}; + +struct TimeBlendSettings +{ + struct BlendSettings startBlend; + struct BlendSettings endBlend; + u16 weight; + u16 altWeight; +}; + struct PaletteFadeControl { - u32 multipurpose1; - u8 delayCounter:6; - u16 y:5; // blend coefficient - u16 targetY:5; // target blend coefficient - u16 blendColor:15; - bool16 active:1; - u16 multipurpose2:6; - bool16 yDec:1; // whether blend coefficient is decreasing - bool16 bufferTransferDisabled:1; - u16 mode:2; - bool16 shouldResetBlendRegisters:1; - bool16 hardwareFadeFinishing:1; - u16 softwareFadeFinishingCounter:5; - bool16 softwareFadeFinishing:1; - bool16 objPaletteToggle:1; - u8 deltaY:4; // rate of change of blend coefficient + u32 multipurpose1; // This field needs to exist or errors will occur + // These three are only used for TOD blending + struct BlendSettings *bld0; + struct BlendSettings *bld1; + u32 weight:9; // [0, 256], so must be 9 bits + u32 delayCounter:6; + u32 y:5; // blend coefficient + u32 targetY:5; // target blend coefficient + u32 multipurpose2:6; + bool32 active:1; + u32 blendColor:15; + // end of word + bool32 yDec:1; // whether blend coefficient is decreasing + bool32 bufferTransferDisabled:1; + u32 mode:2; + bool32 shouldResetBlendRegisters:1; + bool32 hardwareFadeFinishing:1; + u32 softwareFadeFinishingCounter:5; + bool32 softwareFadeFinishing:1; + bool32 objPaletteToggle:1; + u32 deltaY:4; // rate of change of blend coefficient + u32 padding:15; }; +extern const struct BlendSettings gTimeOfDayBlend[]; + extern struct PaletteFadeControl gPaletteFade; extern u32 gPlttBufferTransferPending; extern u16 ALIGNED(4) gPlttBufferUnfaded[PLTT_BUFFER_SIZE]; @@ -59,11 +89,14 @@ extern u16 ALIGNED(4) gPlttBufferFaded[PLTT_BUFFER_SIZE]; void LoadCompressedPalette(const u32 *src, u32 offset, u32 size); void LoadPalette(const void *src, u32 offset, u32 size); +void LoadPaletteFast(const void *src, u32 offset, u32 size); void FillPalette(u32 value, u32 offset, u32 size); void TransferPlttBuffer(void); u32 UpdatePaletteFade(void); void ResetPaletteFade(void); bool32 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, u32 blendColor); +bool32 BeginTimeOfDayPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, struct BlendSettings *bld0, struct BlendSettings *bld1, u32 weight, u32 color); +void PaletteStruct_ResetById(u32 id); void ResetPaletteFadeControl(void); void InvertPlttBuffer(u32 selectedPalettes); void TintPlttBuffer(u32 selectedPalettes, s8 r, s8 g, s8 b); @@ -71,8 +104,13 @@ void UnfadePlttBuffer(u32 selectedPalettes); void BeginFastPaletteFade(u32 submode); void BeginHardwarePaletteFade(u32 blendCnt, u32 delay, u32 y, u32 targetY, u32 shouldResetBlendRegisters); void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color); +void BlendPalettesFine(u32 palettes, u16 *src, u16 *dst, u32 coeff, u32 color); void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u32 color); void BlendPalettesGradually(u32 selectedPalettes, s8 delay, u8 coeff, u8 coeffTarget, u16 color, u8 priority, u8 id); +void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor); +void TintPalette_RGB_Copy(u16 palOffset, u32 blendColor); +void TimeMixPalettes(u32 palettes, u16 *src, u16 *dst, struct BlendSettings *blend0, struct BlendSettings *blend1, u16 weight0); +void AvgPaletteWeighted(u16 *src0, u16 *src1, u16 *dst, u16 weight0); void TintPalette_GrayScale(u16 *palette, u32 count); void TintPalette_GrayScale2(u16 *palette, u32 count); void TintPalette_SepiaTone(u16 *palette, u32 count); diff --git a/include/sprite.h b/include/sprite.h index 138c2ea506..64ea88b293 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -318,6 +318,7 @@ void RequestSpriteSheetCopy(const struct SpriteSheet *sheet); u16 LoadSpriteSheetDeferred(const struct SpriteSheet *sheet); void FreeAllSpritePalettes(void); u32 LoadSpritePalette(const struct SpritePalette *palette); +u8 LoadSpritePaletteInSlot(const struct SpritePalette *palette, u8 paletteNum); void LoadSpritePalettes(const struct SpritePalette *palettes); u32 AllocSpritePalette(u16 tag); u32 IndexOfSpritePaletteTag(u16 tag); diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 0382285fd9..858c897d97 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -2065,18 +2065,23 @@ static void SetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId) HandleLowHpMusicChange(&party[gBattlerPartyIndexes[battler]], battler); } -// In normal singles, if follower pokemon is out, have it slide in instead of being thrown -static bool8 ShouldDoSlideInAnim(void) +// In normal singles, if follower Pokémon exists, and the Pokémon following is being sent out, have it slide in instead of being thrown +static bool8 ShouldDoSlideInAnim(u32 battler) { struct ObjectEvent *followerObj = GetFollowerObject(); if (!followerObj || followerObj->invisible) return FALSE; + if (gBattleTypeFlags & ( BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_RECORDED | BATTLE_TYPE_TRAINER_HILL) ) return FALSE; + + if (GetFirstLiveMon() != &gPlayerParty[gBattlerPartyIndexes[battler]]) + return FALSE; + return TRUE; } @@ -2857,7 +2862,7 @@ void BtlController_HandleIntroTrainerBallThrow(u32 battler, u16 tagTrainerPal, c if (side == B_SIDE_PLAYER) { StoreSpriteCallbackInData6(&gSprites[gBattleStruct->trainerSlideSpriteIds[battler]], SpriteCB_FreePlayerSpriteLoadMonSprite); - StartSpriteAnim(&gSprites[gBattleStruct->trainerSlideSpriteIds[battler]], ShouldDoSlideInAnim() ? 2 : 1); + StartSpriteAnim(&gSprites[gBattleStruct->trainerSlideSpriteIds[battler]], ShouldDoSlideInAnim(battler) ? 2 : 1); paletteNum = AllocSpritePalette(tagTrainerPal); LoadCompressedPalette(trainerPal, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP); @@ -2916,17 +2921,17 @@ static void Task_StartSendOutAnim(u8 taskId) if (TwoMonsAtSendOut(battler)) { gBattleResources->bufferA[battler][1] = gBattlerPartyIndexes[battler]; - StartSendOutAnim(battler, FALSE, FALSE, ShouldDoSlideInAnim()); + StartSendOutAnim(battler, FALSE, FALSE, ShouldDoSlideInAnim(battler)); battlerPartner = battler ^ BIT_FLANK; gBattleResources->bufferA[battlerPartner][1] = gBattlerPartyIndexes[battlerPartner]; BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerPartner]], battlerPartner); - StartSendOutAnim(battlerPartner, FALSE, FALSE, ShouldDoSlideInAnim()); + StartSendOutAnim(battlerPartner, FALSE, FALSE, ShouldDoSlideInAnim(battler)); } else { gBattleResources->bufferA[battler][1] = gBattlerPartyIndexes[battler]; - StartSendOutAnim(battler, FALSE, FALSE, ShouldDoSlideInAnim()); + StartSendOutAnim(battler, FALSE, FALSE, ShouldDoSlideInAnim(battler)); } gBattlerControllerFuncs[battler] = (void*)(GetWordTaskArg(taskId, tControllerFunc_1)); DestroyTask(taskId); diff --git a/src/battle_transition.c b/src/battle_transition.c index b131769ba4..bc993cc61f 100644 --- a/src/battle_transition.c +++ b/src/battle_transition.c @@ -1355,6 +1355,7 @@ static void InitPatternWeaveTransition(struct Task *task) sTransitionData->WIN0V = DISPLAY_HEIGHT; sTransitionData->BLDCNT = BLDCNT_TGT1_BG0 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL; sTransitionData->BLDALPHA = BLDALPHA_BLEND(task->tBlendTarget2, task->tBlendTarget1); + UpdateShadowColor(RGB_GRAY); for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = DISPLAY_WIDTH; @@ -3885,6 +3886,8 @@ static void VBlankCB_AngledWipes(void) #define tFadeFromGrayIncrement data[5] #define tDelayTimer data[6] #define tBlend data[7] +#define tBldCntSaved data[8] +#define tShadowColor data[9] static void CreateIntroTask(s16 fadeToGrayDelay, s16 fadeFromGrayDelay, s16 numFades, s16 fadeToGrayIncrement, s16 fadeFromGrayIncrement) { @@ -3912,17 +3915,28 @@ void Task_BattleTransition_Intro(u8 taskId) static bool8 TransitionIntro_FadeToGray(struct Task *task) { + u8 paletteNum = IndexOfSpritePaletteTag(TAG_WEATHER_START); + u16 index = OBJ_PLTT_ID(paletteNum) + SHADOW_COLOR_INDEX; if (task->tDelayTimer == 0 || --task->tDelayTimer == 0) { task->tDelayTimer = task->tFadeToGrayDelay; task->tBlend += task->tFadeToGrayIncrement; if (task->tBlend > 16) task->tBlend = 16; + if (paletteNum < 16) + task->tShadowColor = gPlttBufferFaded[index]; BlendPalettes(PALETTES_ALL, task->tBlend, RGB(11, 11, 11)); + if (paletteNum < 16) + gPlttBufferFaded[index] = task->tShadowColor; } if (task->tBlend >= 16) { // Fade to gray complete, start fade back + // Save BLDCNT and turn off targets temporarily + task->tBldCntSaved = GetGpuReg(REG_OFFSET_BLDCNT); + SetGpuReg(REG_OFFSET_BLDCNT, task->tBldCntSaved & ~BLDCNT_TGT2_BG_ALL); + if (paletteNum < 16) + gPlttBufferFaded[index] = RGB(11, 11, 11); task->tState++; task->tDelayTimer = task->tFadeFromGrayDelay; } @@ -3933,11 +3947,19 @@ static bool8 TransitionIntro_FadeFromGray(struct Task *task) { if (task->tDelayTimer == 0 || --task->tDelayTimer == 0) { + u8 paletteNum = IndexOfSpritePaletteTag(TAG_WEATHER_START); task->tDelayTimer = task->tFadeFromGrayDelay; task->tBlend -= task->tFadeFromGrayIncrement; if (task->tBlend < 0) task->tBlend = 0; BlendPalettes(PALETTES_ALL, task->tBlend, RGB(11, 11, 11)); + // Restore BLDCNT + SetGpuReg(REG_OFFSET_BLDCNT, task->tBldCntSaved); + if (paletteNum < 16) + { + u16 index = OBJ_PLTT_ID(paletteNum) + SHADOW_COLOR_INDEX; + gPlttBufferFaded[index] = task->tShadowColor; + } } if (task->tBlend == 0) { @@ -4230,6 +4252,7 @@ static bool8 FrontierLogoWave_Init(struct Task *task) LZ77UnCompVram(sFrontierLogo_Tileset, tileset); LoadPalette(sFrontierLogo_Palette, BG_PLTT_ID(15), sizeof(sFrontierLogo_Palette)); sTransitionData->cameraY = 0; + UpdateShadowColor(RGB_GRAY); task->tState++; return FALSE; diff --git a/src/clock.c b/src/clock.c index d122fc782c..f0a632c9ff 100644 --- a/src/clock.c +++ b/src/clock.c @@ -1,4 +1,5 @@ #include "global.h" +#include "clock.h" #include "event_data.h" #include "rtc.h" #include "time_events.h" @@ -15,7 +16,6 @@ static void UpdatePerDay(struct Time *localTime); static void UpdatePerMinute(struct Time *localTime); -static void FormChangeTimeUpdate(); void InitTimeBasedEvents(void) { @@ -72,12 +72,11 @@ static void UpdatePerMinute(struct Time *localTime) { BerryTreeTimeUpdate(minutes); gSaveBlock2Ptr->lastBerryTreeUpdate = *localTime; - FormChangeTimeUpdate(); } } } -static void FormChangeTimeUpdate() +void FormChangeTimeUpdate() { s32 i; for (i = 0; i < PARTY_SIZE; i++) 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 ee00023a51..7173ffbe58 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 @@ extern const struct SpriteTemplate gFieldEffectObjectTemplate_AshLaunch; extern const struct SpriteTemplate gFieldEffectObjectTemplate_Bubbles; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SmallSparkle; extern const struct SpriteTemplate gFieldEffectObjectTemplate_Rayquaza; +extern const struct SpriteTemplate gFieldEffectObjectTemplate_BallLight; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SlitherTracks; extern const struct SpriteTemplate gFieldEffectObjectTemplate_BugTracks; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SpotTracks; diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index ecea0350c5..5638ea46e2 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -28,8 +28,56 @@ static const struct SpriteFrameImage sPicTable_ShadowExtraLarge[] = { obj_frame_tiles(gFieldEffectObjectPic_ShadowExtraLarge), }; -const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowSmall = { +const struct SpriteFrameImage gFieldEffectObjectPicTable_BallLight[] = { + obj_frame_tiles(gFieldEffectObjectPic_BallLight), +}; + +const struct SpriteFrameImage gFieldEffectObjectPicTable_PokeCenterLight[] = { + obj_frame_tiles(gFieldEffectObjectPic_PokeCenterLight), +}; + +const struct SpriteFrameImage gFieldEffectObjectPicTable_MartLight[] = { + obj_frame_tiles(gFieldEffectObjectPic_MartLight), +}; + +const struct SpriteTemplate gFieldEffectObjectTemplate_BallLight = { + .tileTag = OBJ_EVENT_PAL_TAG_LIGHT, + .paletteTag = OBJ_EVENT_PAL_TAG_LIGHT, + .oam = &gObjectEventBaseOam_32x32, + .anims = sAnimTable_Inanimate, + .images = gFieldEffectObjectPicTable_BallLight, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = UpdateLightSprite, +}; + +const struct SpriteTemplate gFieldEffectObjectTemplate_PokeCenterLight = { .tileTag = TAG_NONE, + .paletteTag = OBJ_EVENT_PAL_TAG_NEON_LIGHT, + .oam = &gObjectEventBaseOam_16x16, + .anims = sAnimTable_Inanimate, + .images = gFieldEffectObjectPicTable_PokeCenterLight, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = UpdateLightSprite, +}; + +const struct SpriteTemplate gFieldEffectObjectTemplate_MartLight = { + .tileTag = TAG_NONE, + .paletteTag = OBJ_EVENT_PAL_TAG_NEON_LIGHT, + .oam = &gObjectEventBaseOam_16x16, + .anims = sAnimTable_Inanimate, + .images = gFieldEffectObjectPicTable_MartLight, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = UpdateLightSprite, +}; + +const struct SpriteTemplate *const gFieldEffectLightTemplates[] = { + &gFieldEffectObjectTemplate_BallLight, + &gFieldEffectObjectTemplate_PokeCenterLight, + &gFieldEffectObjectTemplate_MartLight, +}; + +const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowSmall = { + .tileTag = FLDEFF_TILE_TAG_SHADOW_SMALL, .paletteTag = TAG_WEATHER_START, .oam = &gObjectEventBaseOam_8x8, .anims = sAnimTable_Shadow, @@ -39,7 +87,7 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowSmall = { }; const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowMedium = { - .tileTag = TAG_NONE, + .tileTag = FLDEFF_TILE_TAG_SHADOW_MEDIUM, .paletteTag = TAG_WEATHER_START, .oam = &gObjectEventBaseOam_16x8, .anims = sAnimTable_Shadow, @@ -49,7 +97,7 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowMedium = { }; const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowLarge = { - .tileTag = TAG_NONE, + .tileTag = FLDEFF_TILE_TAG_SHADOW_LARGE, .paletteTag = TAG_WEATHER_START, .oam = &gObjectEventBaseOam_32x8, .anims = sAnimTable_Shadow, @@ -59,7 +107,7 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowLarge = { }; const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowExtraLarge = { - .tileTag = TAG_NONE, + .tileTag = FLDEFF_TILE_TAG_SHADOW_EXTRA_LARGE, .paletteTag = TAG_WEATHER_START, .oam = &gObjectEventBaseOam_64x32, .anims = sAnimTable_Shadow, diff --git a/src/data/object_events/object_event_graphics.h b/src/data/object_events/object_event_graphics.h index 577263e77b..c7b92def91 100755 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -293,7 +293,9 @@ 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"); -static const u32 sFiller[0x48] = {}; +const u32 gFieldEffectObjectPic_BallLight[] = INCBIN_U32("graphics/object_events/pics/misc/light.4bpp"); +const u32 gFieldEffectObjectPic_PokeCenterLight[] = INCBIN_U32("graphics/object_events/pics/misc/poke_center_light.4bpp"); +const u32 gFieldEffectObjectPic_MartLight[] = INCBIN_U32("graphics/object_events/pics/misc/mart_light.4bpp"); 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"); const u16 gFieldEffectPal_CutGrass[] = INCBIN_U16("graphics/field_effects/palettes/cut_grass.gbapal"); @@ -380,9 +382,12 @@ const u32 gObjectEventPic_RayquazaCutscene[] = INCBIN_U32("graphics/object_event const u16 gObjectEventPal_HoOh[] = INCBIN_U16("graphics/object_events/palettes/ho_oh.gbapal"); const u16 gObjectEventPal_Lugia[] = INCBIN_U16("graphics/object_events/palettes/lugia.gbapal"); -const u16 gObjectEventPal_Substitute[] = INCBIN_U16("graphics/pokemon/question_mark/overworld.gbapal"); +const u16 gObjectEventPaletteLight[] = INCBIN_U16("graphics/object_events/palettes/light.gbapal"); +const u16 gObjectEventPaletteLight2[] = INCBIN_U16("graphics/object_events/palettes/light2.gbapal"); +const u16 gObjectEventPal_Substitute[] = INCBIN_U16("graphics/object_events/pics/pokemon_old/substitute.gbapal"); const u16 gObjectEventPaletteEmotes[] = INCBIN_U16("graphics/misc/emotes.gbapal"); +const u16 gObjectEventPaletteNeonLight[] = INCBIN_U16("graphics/object_events/palettes/neon_light.gbapal"); #if OW_FOLLOWERS_POKEBALLS const u32 gObjectEventPic_MasterBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_master.4bpp"); diff --git a/src/data/object_events/object_event_graphics_info.h b/src/data/object_events/object_event_graphics_info.h index 5b8f971a49..91b3ab2c9d 100755 --- a/src/data/object_events/object_event_graphics_info.h +++ b/src/data/object_events/object_event_graphics_info.h @@ -4671,3 +4671,22 @@ const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Follower = { .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, }; + +const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_BallLight = { + .tileTag = TAG_NONE, + .paletteTag = OBJ_EVENT_PAL_TAG_LIGHT, + .reflectionPaletteTag = OBJ_EVENT_PAL_TAG_LIGHT_2, + .size = 512, + .width = 32, + .height = 32, + .paletteSlot = PALSLOT_NPC_1, + .shadowSize = SHADOW_SIZE_NONE, + .inanimate = TRUE, + .compressed = FALSE, + .tracks = TRACKS_NONE, + .oam = &gObjectEventBaseOam_32x32, + .subspriteTables = sOamTables_32x32, + .anims = sAnimTable_Inanimate, + .images = gFieldEffectObjectPicTable_BallLight, + .affineAnims = gDummySpriteAffineAnimTable, +}; diff --git a/src/data/object_events/object_event_graphics_info_pointers.h b/src/data/object_events/object_event_graphics_info_pointers.h index 60dc6997db..117187739b 100755 --- a/src/data/object_events/object_event_graphics_info_pointers.h +++ b/src/data/object_events/object_event_graphics_info_pointers.h @@ -239,6 +239,7 @@ extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_HoOh; // Begin pokemon event objects extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_PokeBall; extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Follower; +extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_BallLight; extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Bard; extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Hipster; @@ -490,6 +491,7 @@ const struct ObjectEventGraphicsInfo *const gObjectEventGraphicsInfoPointers[NUM [OBJ_EVENT_GFX_HOOH] = &gObjectEventGraphicsInfo_HoOh, [OBJ_EVENT_GFX_POKE_BALL] = &gObjectEventGraphicsInfo_PokeBall, [OBJ_EVENT_GFX_OW_MON] = &gObjectEventGraphicsInfo_Follower, + [OBJ_EVENT_GFX_LIGHT_SPRITE] = &gObjectEventGraphicsInfo_BallLight, }; const struct ObjectEventGraphicsInfo *const gMauvilleOldManGraphicsInfoPointers[] = { diff --git a/src/data/object_events/object_event_pic_tables.h b/src/data/object_events/object_event_pic_tables.h index 1fdfc6d492..c1f8fd6f1c 100755 --- a/src/data/object_events/object_event_pic_tables.h +++ b/src/data/object_events/object_event_pic_tables.h @@ -1117,8 +1117,9 @@ static const struct SpriteFrameImage sPicTable_PokeBall[] = { overworld_frame(gObjectEventPic_PokeBall, 2, 4, 0), }; -#if OW_FOLLOWERS_POKEBALLS +extern const struct SpriteFrameImage gFieldEffectObjectPicTable_BallLight[]; +#if OW_FOLLOWERS_POKEBALLS #define POKEBALL_PIC_FRAMES(name) \ overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 0), \ overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 1), \ diff --git a/src/data/tilesets/headers.h b/src/data/tilesets/headers.h index 2eae1be1ea..2401b65309 100644 --- a/src/data/tilesets/headers.h +++ b/src/data/tilesets/headers.h @@ -1,3 +1,8 @@ +#include "fieldmap.h" + +// Whether a palette has a night version, located at ((x + 9) % 16).pal +#define SWAP_PAL(x) ((x) < NUM_PALS_IN_PRIMARY ? 1 << (x) : 1 << ((x) - NUM_PALS_IN_PRIMARY)) + const struct Tileset gTileset_General = { .isCompressed = TRUE, diff --git a/src/decoration.c b/src/decoration.c index c07bf30573..8d8033fef5 100644 --- a/src/decoration.c +++ b/src/decoration.c @@ -2131,7 +2131,7 @@ static u8 AddDecorationIconObjectFromObjectEvent(u16 tilesTag, u16 paletteTag, u } else { - spriteId = CreateObjectGraphicsSprite(sPlaceDecorationGraphicsDataBuffer.decoration->tiles[0], SpriteCallbackDummy, 0, 0, 1); + spriteId = CreateObjectGraphicsSpriteWithTag(sPlaceDecorationGraphicsDataBuffer.decoration->tiles[0], SpriteCallbackDummy, 0, 0, 1, paletteTag); } return spriteId; } diff --git a/src/event_object_movement.c b/src/event_object_movement.c index a8d1605438..6564ece843 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -30,6 +30,7 @@ #include "pokeball.h" #include "random.h" #include "region_map.h" +#include "rtc.h" #include "script.h" #include "sound.h" #include "sprite.h" @@ -186,7 +187,7 @@ static void SetSpriteDataForNormalStep(struct Sprite *, u8, u8); static void InitSpriteForFigure8Anim(struct Sprite *); static bool8 AnimateSpriteInFigure8(struct Sprite *); u8 GetDirectionToFace(s16 x1, s16 y1, s16 x2, s16 y2); -static void FollowerSetGraphics(struct ObjectEvent * objectEvent, u32 species, bool32 shiny, bool32 female); +static void FollowerSetGraphics(struct ObjectEvent *objEvent, u32 species, bool32 shiny, bool32 female); static void ObjectEventSetGraphics(struct ObjectEvent *, const struct ObjectEventGraphicsInfo *); static void SpriteCB_VirtualObject(struct Sprite *); static void DoShadowFieldEffect(struct ObjectEvent *); @@ -545,7 +546,10 @@ static const struct SpritePalette sObjectEventSpritePalettes[] = { #endif //ITEM_STRANGE_BALL #endif //OW_FOLLOWERS_POKEBALLS {gObjectEventPal_Substitute, OBJ_EVENT_PAL_TAG_SUBSTITUTE}, + {gObjectEventPaletteLight, OBJ_EVENT_PAL_TAG_LIGHT}, + {gObjectEventPaletteLight2, OBJ_EVENT_PAL_TAG_LIGHT_2}, {gObjectEventPaletteEmotes, OBJ_EVENT_PAL_TAG_EMOTES}, + {gObjectEventPaletteNeonLight, OBJ_EVENT_PAL_TAG_NEON_LIGHT}, #ifdef BUGFIX {NULL, OBJ_EVENT_PAL_TAG_NONE}, #else @@ -1348,7 +1352,7 @@ u8 GetFirstInactiveObjectEventId(void) u8 GetObjectEventIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroupId) { - if (localId < OBJ_EVENT_ID_FOLLOWER) + if (localId < OBJ_EVENT_ID_DYNAMIC_BASE) return GetObjectEventIdByLocalIdAndMapInternal(localId, mapNum, mapGroupId); return GetObjectEventIdByLocalId(localId); @@ -1833,7 +1837,7 @@ static u32 LoadDynamicFollowerPaletteFromGraphicsId(u16 graphicsId, struct Sprit } // Used to create a sprite using a graphicsId associated with object events. -u8 CreateObjectGraphicsSprite(u16 graphicsId, void (*callback)(struct Sprite *), s16 x, s16 y, u8 subpriority) +u8 CreateObjectGraphicsSpriteWithTag(u16 graphicsId, void (*callback)(struct Sprite *), s16 x, s16 y, u8 subpriority, u16 paletteTag) { struct SpriteTemplate *spriteTemplate; const struct SubspriteTable *subspriteTables; @@ -1880,6 +1884,11 @@ u8 CreateObjectGraphicsSprite(u16 graphicsId, void (*callback)(struct Sprite *), return spriteId; } +u8 CreateObjectGraphicsSprite(u16 graphicsId, void (*callback)(struct Sprite *), s16 x, s16 y, u8 subpriority) +{ + return CreateObjectGraphicsSpriteWithTag(graphicsId, callback, x, y, subpriority, TAG_NONE); +} + #define sVirtualObjId data[0] #define sVirtualObjElev data[1] @@ -1935,12 +1944,10 @@ struct Pokemon *GetFirstLiveMon(void) for (i = 0; i < PARTY_SIZE; i++) { struct Pokemon *mon = &gPlayerParty[i]; - if ((OW_MON_ALLOWED_SPECIES && GetMonData(mon, MON_DATA_SPECIES_OR_EGG) != VarGet(OW_MON_ALLOWED_SPECIES)) - || (OW_MON_ALLOWED_MET_LVL && GetMonData(mon, MON_DATA_MET_LEVEL) != VarGet(OW_MON_ALLOWED_MET_LVL)) - || (OW_MON_ALLOWED_MET_LOC && GetMonData(mon, MON_DATA_MET_LOCATION) != VarGet(OW_MON_ALLOWED_MET_LOC))) - { + if ((OW_FOLLOWERS_ALLOWED_SPECIES && GetMonData(mon, MON_DATA_SPECIES_OR_EGG) != VarGet(OW_FOLLOWERS_ALLOWED_SPECIES)) + || (OW_FOLLOWERS_ALLOWED_MET_LVL && GetMonData(mon, MON_DATA_MET_LEVEL) != VarGet(OW_FOLLOWERS_ALLOWED_MET_LVL)) + || (OW_FOLLOWERS_ALLOWED_MET_LOC && GetMonData(mon, MON_DATA_MET_LOCATION) != VarGet(OW_FOLLOWERS_ALLOWED_MET_LOC))) continue; - } if (gPlayerParty[i].hp > 0 && !(gPlayerParty[i].box.isEgg || gPlayerParty[i].box.isBadEgg)) return &gPlayerParty[i]; @@ -2061,7 +2068,7 @@ static u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female) } if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog - UpdateSpritePaletteWithWeather(paletteNum); + UpdateSpritePaletteWithWeather(paletteNum, FALSE); return paletteNum; } @@ -2080,10 +2087,6 @@ static void FollowerSetGraphics(struct ObjectEvent *objEvent, u32 species, bool3 sprite->inUse = TRUE; sprite->oam.paletteNum = LoadDynamicFollowerPalette(species, shiny, female); } - else if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog - { - UpdateSpritePaletteWithWeather(gSprites[objEvent->spriteId].oam.paletteNum); - } } // Like FollowerSetGraphics, but does not recenter sprite on a metatile @@ -2128,7 +2131,7 @@ static void RefreshFollowerGraphics(struct ObjectEvent *objEvent) { UpdateSpritePalette(&sObjectEventSpritePalettes[i], sprite); if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog - UpdateSpritePaletteWithWeather(sprite->oam.paletteNum); + UpdateSpritePaletteWithWeather(sprite->oam.paletteNum, FALSE); } } @@ -2341,9 +2344,15 @@ bool32 CheckMsgCondition(const struct MsgCondition *cond, struct Pokemon *mon, u return (multi == cond->data.bytes[0] || multi == cond->data.bytes[1]); case MSG_COND_MUSIC: return (cond->data.raw == GetCurrentMapMusic()); - // Added on `lighting` branch - // case MSG_COND_TIME_OF_DAY: - // break; + case MSG_COND_TIME_OF_DAY: + { + // Must match time of day, have natural light on the map, + // and not have weather that obscures the sky + u32 weather = GetCurrentWeather(); + return (cond->data.raw == gTimeOfDay + && MapHasNaturalLight(gMapHeader.mapType) + && (weather == WEATHER_NONE || weather == WEATHER_SUNNY_CLOUDS || weather == WEATHER_SUNNY)); + } case MSG_COND_NEAR_MB: multi = FindMetatileBehaviorWithinRange(obj->currentCoords.x, obj->currentCoords.y, @@ -2563,6 +2572,156 @@ void GetFollowerAction(struct ScriptContext *ctx) // Essentially a big switch fo gFollowerBasicMessages[emotion].script); } +#define sLightType data[5] +#define sLightXPos data[6] +#define sLightYPos data[7] + +// Sprite 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->sLightXPos; + s16 y = sprite->sLightYPos; + u16 sheetTileStart; + u32 paletteNum; + if (!(x >= left && x <= right && y >= top && y <= bottom)) + { + sheetTileStart = sprite->sheetTileStart; + paletteNum = sprite->oam.paletteNum; + DestroySprite(sprite); + FieldEffectFreeTilesIfUnused(sheetTileStart); + FieldEffectFreePaletteIfUnused(paletteNum); + Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY); // TODO: Restore original blend coeffs at dawn + return; + } + + if (gTimeOfDay != TIME_NIGHT) + { + sprite->invisible = TRUE; + return; + } + + // Note: Don't set window registers during hardware fade! + switch (sprite->sLightType) + { + default: + case LIGHT_TYPE_BALL: + if (gPaletteFade.active) // if palette fade is active, don't flicker since the timer won't be updated + { + Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY); + sprite->invisible = FALSE; + } + else if (gPlayerAvatar.tileTransitionState) + { + Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY); // As long as the second coefficient stays 12, shadows will not change + sprite->invisible = FALSE; + if (GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum) == OBJ_EVENT_PAL_TAG_LIGHT_2) + LoadSpritePaletteInSlot(&sObjectEventSpritePalettes[FindObjectEventPaletteIndexByTag(OBJ_EVENT_PAL_TAG_LIGHT)], sprite->oam.paletteNum); + } + else if ((sprite->invisible = gTimeUpdateCounter & 1)) + { + Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY); + sprite->invisible = FALSE; + if (GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum) == OBJ_EVENT_PAL_TAG_LIGHT_2) + LoadSpritePaletteInSlot(&sObjectEventSpritePalettes[FindObjectEventPaletteIndexByTag(OBJ_EVENT_PAL_TAG_LIGHT)], sprite->oam.paletteNum); + } + break; + case LIGHT_TYPE_PKMN_CENTER_SIGN: + case LIGHT_TYPE_POKE_MART_SIGN: + Weather_SetBlendCoeffs(12, BASE_SHADOW_INTENSITY); + sprite->invisible = FALSE; + break; + } +} + +// Spawn a light at a map coordinate +static void SpawnLightSprite(s16 x, s16 y, s16 camX, s16 camY, u32 lightType) +{ + struct Sprite *sprite; + const struct SpriteTemplate *template; + u32 i; + for (i = 0; i < MAX_SPRITES; i++) + { + sprite = &gSprites[i]; + if (sprite->inUse && sprite->callback == UpdateLightSprite && sprite->sLightXPos == x && sprite->sLightYPos == y) + return; + } + lightType = min(lightType, ARRAY_COUNT(gFieldEffectLightTemplates) - 1); // bounds checking + template = gFieldEffectLightTemplates[lightType]; + LoadSpriteSheetByTemplate(template, 0, 0); + sprite = &gSprites[CreateSprite(template, 0, 0, 0)]; + if (lightType == 0 && (i = IndexOfSpritePaletteTag(template->paletteTag + 1)) < 16) + sprite->oam.paletteNum = i; + else + UpdateSpritePaletteByTemplate(template, sprite); + GetMapCoordsFromSpritePos(x + camX, y + camY, &sprite->x, &sprite->y); + sprite->sLightType = lightType; + sprite->sLightXPos = x; + sprite->sLightYPos = y; + sprite->affineAnims = gDummySpriteAffineAnimTable; + sprite->affineAnimBeginning = TRUE; + sprite->coordOffsetEnabled = TRUE; + switch (lightType) + { + default: + case LIGHT_TYPE_BALL: + sprite->centerToCornerVecX = -(32 >> 1); + sprite->centerToCornerVecY = -(32 >> 1); + sprite->oam.priority = 1; + sprite->oam.objMode = ST_OAM_OBJ_BLEND; + sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; + sprite->x += 8; + sprite->y += 22 + sprite->centerToCornerVecY; + break; + case LIGHT_TYPE_PKMN_CENTER_SIGN: + case LIGHT_TYPE_POKE_MART_SIGN: + sprite->centerToCornerVecX = -(16 >> 1); + sprite->centerToCornerVecY = -(16 >> 1); + sprite->oam.priority = 2; + sprite->subpriority = 0xFF; + sprite->oam.objMode = ST_OAM_OBJ_BLEND; + break; + } +} + +#undef sLightType +#undef sLightXPos +#undef sLightYPos + +void TrySpawnLightSprites(s16 camX, s16 camY) +{ + u32 i; + u8 objectCount; + s16 left = gSaveBlock1Ptr->pos.x - 2; + s16 right = gSaveBlock1Ptr->pos.x + MAP_OFFSET_W + 2; + s16 top = gSaveBlock1Ptr->pos.y; + s16 bottom = gSaveBlock1Ptr->pos.y + MAP_OFFSET_H + 2; + if (gMapHeader.events == NULL) + return; + + if (InBattlePyramid()) + objectCount = GetNumBattlePyramidObjectEvents(); + else if (InTrainerHill()) + objectCount = 2; + else + objectCount = gMapHeader.events->objectEventCount; + + for (i = 0; i < objectCount; i++) + { + struct ObjectEventTemplate *template = &gSaveBlock1Ptr->objectEventTemplates[i]; + s16 npcX = template->x + MAP_OFFSET; + s16 npcY = template->y + MAP_OFFSET; + if (top <= npcY && bottom >= npcY + && left <= npcX && right >= npcX + && !FlagGet(template->flagId) + && template->graphicsId == OBJ_EVENT_GFX_LIGHT_SPRITE) // event is light sprite instead + SpawnLightSprite(npcX, npcY, camX, camY, template->trainerRange_berryTreeId); + } +} + void TrySpawnObjectEvents(s16 cameraX, s16 cameraY) { u8 i; @@ -2588,9 +2747,13 @@ void TrySpawnObjectEvents(s16 cameraX, s16 cameraY) s16 npcX = template->x + MAP_OFFSET; s16 npcY = template->y + MAP_OFFSET; - if (top <= npcY && bottom >= npcY && left <= npcX && right >= npcX - && !FlagGet(template->flagId)) - TrySpawnObjectEventTemplate(template, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, cameraX, cameraY); + if (top <= npcY && bottom >= npcY && left <= npcX && right >= npcX && !FlagGet(template->flagId)) + { + if (template->graphicsId == OBJ_EVENT_GFX_LIGHT_SPRITE) + SpawnLightSprite(npcX, npcY, cameraX, cameraY, template->trainerRange_berryTreeId); // light sprite instead + else + TrySpawnObjectEventTemplate(template, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, cameraX, cameraY); + } } } } @@ -2646,6 +2809,7 @@ void SpawnObjectEventsOnReturnToField(s16 x, s16 y) SpawnObjectEventOnReturnToField(i, x, y); } CreateReflectionEffectSprites(); + TrySpawnLightSprites(x, y); } static void SpawnObjectEventOnReturnToField(u8 objectEventId, s16 x, s16 y) @@ -2720,7 +2884,7 @@ static void ResetObjectEventFldEffData(struct ObjectEvent *objectEvent) { objectEvent->singleMovementActive = FALSE; objectEvent->triggerGroundEffectsOnMove = TRUE; - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; objectEvent->hasReflection = FALSE; objectEvent->inShortGrass = FALSE; objectEvent->inShallowFlowingWater = FALSE; @@ -2744,7 +2908,17 @@ static u8 UpdateSpritePalette(const struct SpritePalette *spritePalette, struct sprite->inUse = FALSE; FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); sprite->inUse = TRUE; - return sprite->oam.paletteNum = LoadSpritePalette(spritePalette); + if (IndexOfSpritePaletteTag(spritePalette->tag) == 0xFF) + { + sprite->oam.paletteNum = LoadSpritePalette(spritePalette); + UpdateSpritePaletteWithWeather(sprite->oam.paletteNum, FALSE); + } + else + { + sprite->oam.paletteNum = LoadSpritePalette(spritePalette); + } + + return sprite->oam.paletteNum; } // Find and update based on template's paletteTag @@ -2999,12 +3173,15 @@ static void UNUSED LoadObjectEventPaletteSet(u16 *paletteTags) LoadObjectEventPalette(paletteTags[i]); } +// Really just loads the palette and applies weather fade static u8 LoadSpritePaletteIfTagExists(const struct SpritePalette *spritePalette) { u8 paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); if (paletteNum != 0xFF) // don't load twice; return return paletteNum; paletteNum = LoadSpritePalette(spritePalette); + if (paletteNum != 0xFF) + UpdateSpritePaletteWithWeather(paletteNum, FALSE); return paletteNum; } @@ -5542,7 +5719,7 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri direction = gObjectEvents[gPlayerAvatar.objectEventId].movementDirection; objectEvent->facingDirectionLocked = TRUE; } - + MoveCoords(direction, &x, &y); GetCollisionAtCoords(objectEvent, x, y, direction); // Sets directionOverwrite for stairs if (GetLedgeJumpDirection(x, y, direction) != DIR_NONE) @@ -7050,6 +7227,8 @@ static void InitJump(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 static void InitJumpRegular(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 direction, u8 distance, u8 type) { + if (OW_OBJECT_VANILLA_SHADOWS) + SetUpShadow(objectEvent); // For follower only, match the anim duration of the player's movement, whether dashing, walking or jumping if (objectEvent->localId == OBJ_EVENT_ID_FOLLOWER && type == JUMP_TYPE_HIGH @@ -7088,6 +7267,10 @@ static u8 UpdateJumpAnim(struct ObjectEvent *objectEvent, struct Sprite *sprite, objectEvent->triggerGroundEffectsOnStop = TRUE; objectEvent->landingJump = TRUE; sprite->animPaused = TRUE; + if (OW_OBJECT_VANILLA_SHADOWS) + // Somewhat ugly workaround, the shadow is disabled in UpdateShadowFieldEffect, + // but due to code changes from DNS, it needs new signaling + objectEvent->jumpDone = TRUE; } return result; } @@ -7144,7 +7327,7 @@ bool8 MovementAction_Jump2Down_Step1(struct ObjectEvent *objectEvent, struct Spr { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -7161,7 +7344,7 @@ bool8 MovementAction_Jump2Up_Step1(struct ObjectEvent *objectEvent, struct Sprit { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -7178,7 +7361,7 @@ bool8 MovementAction_Jump2Left_Step1(struct ObjectEvent *objectEvent, struct Spr { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -7195,7 +7378,7 @@ bool8 MovementAction_Jump2Right_Step1(struct ObjectEvent *objectEvent, struct Sp { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8081,7 +8264,7 @@ bool8 MovementAction_JumpDown_Step1(struct ObjectEvent *objectEvent, struct Spri { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8098,7 +8281,7 @@ bool8 MovementAction_JumpUp_Step1(struct ObjectEvent *objectEvent, struct Sprite { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8115,7 +8298,7 @@ bool8 MovementAction_JumpLeft_Step1(struct ObjectEvent *objectEvent, struct Spri { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8132,7 +8315,7 @@ bool8 MovementAction_JumpRight_Step1(struct ObjectEvent *objectEvent, struct Spr { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8149,7 +8332,7 @@ bool8 MovementAction_JumpInPlaceDown_Step1(struct ObjectEvent *objectEvent, stru { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8166,7 +8349,7 @@ bool8 MovementAction_JumpInPlaceUp_Step1(struct ObjectEvent *objectEvent, struct { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8183,7 +8366,7 @@ bool8 MovementAction_JumpInPlaceLeft_Step1(struct ObjectEvent *objectEvent, stru { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8200,7 +8383,7 @@ bool8 MovementAction_JumpInPlaceRight_Step1(struct ObjectEvent *objectEvent, str { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8217,7 +8400,7 @@ bool8 MovementAction_JumpInPlaceDownUp_Step1(struct ObjectEvent *objectEvent, st { if (DoJumpInPlaceAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8234,7 +8417,7 @@ bool8 MovementAction_JumpInPlaceUpDown_Step1(struct ObjectEvent *objectEvent, st { if (DoJumpInPlaceAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8251,7 +8434,7 @@ bool8 MovementAction_JumpInPlaceLeftRight_Step1(struct ObjectEvent *objectEvent, { if (DoJumpInPlaceAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8268,7 +8451,7 @@ bool8 MovementAction_JumpInPlaceRightLeft_Step1(struct ObjectEvent *objectEvent, { if (DoJumpInPlaceAnim(objectEvent, sprite)) { - objectEvent->hasShadow = 0; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8713,7 +8896,7 @@ bool8 MovementAction_AcroWheelieHopFaceDown_Step1(struct ObjectEvent *objectEven { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8730,7 +8913,7 @@ bool8 MovementAction_AcroWheelieHopFaceUp_Step1(struct ObjectEvent *objectEvent, { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8747,7 +8930,7 @@ bool8 MovementAction_AcroWheelieHopFaceLeft_Step1(struct ObjectEvent *objectEven { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8764,7 +8947,7 @@ bool8 MovementAction_AcroWheelieHopFaceRight_Step1(struct ObjectEvent *objectEve { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8781,7 +8964,7 @@ bool8 MovementAction_AcroWheelieHopDown_Step1(struct ObjectEvent *objectEvent, s { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8798,7 +8981,7 @@ bool8 MovementAction_AcroWheelieHopUp_Step1(struct ObjectEvent *objectEvent, str { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8818,7 +9001,7 @@ bool8 MovementAction_AcroWheelieHopLeft_Step1(struct ObjectEvent *objectEvent, s { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8838,7 +9021,7 @@ bool8 MovementAction_AcroWheelieHopRight_Step1(struct ObjectEvent *objectEvent, { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8855,7 +9038,7 @@ bool8 MovementAction_AcroWheelieJumpDown_Step1(struct ObjectEvent *objectEvent, { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8872,7 +9055,7 @@ bool8 MovementAction_AcroWheelieJumpUp_Step1(struct ObjectEvent *objectEvent, st { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8892,7 +9075,7 @@ bool8 MovementAction_AcroWheelieJumpLeft_Step1(struct ObjectEvent *objectEvent, { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -8912,7 +9095,7 @@ bool8 MovementAction_AcroWheelieJumpRight_Step1(struct ObjectEvent *objectEvent, { if (DoJumpAnim(objectEvent, sprite)) { - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = FALSE; sprite->sActionFuncId = 2; return TRUE; } @@ -10025,14 +10208,17 @@ static void (*const sGroundEffectFuncs[])(struct ObjectEvent *objEvent, struct S static void DoFlaggedGroundEffects(struct ObjectEvent *objEvent, struct Sprite *sprite, u32 flags) { - u8 i; - + u32 i; if (ObjectEventIsFarawayIslandMew(objEvent) == TRUE && !ShouldMewShakeGrass(objEvent)) return; for (i = 0; i < ARRAY_COUNT(sGroundEffectFuncs); i++, flags >>= 1) if (flags & 1) sGroundEffectFuncs[i](objEvent, sprite); + if (!OW_OBJECT_VANILLA_SHADOWS && CurrentMapHasShadows() && !(gWeatherPtr->noShadows || objEvent->inHotSprings || objEvent->inSandPile || MetatileBehavior_IsPuddle(objEvent->currentMetatileBehavior))) + { + SetUpShadow(objEvent); + } } void filters_out_some_ground_effects(struct ObjectEvent *objEvent, u32 *flags) @@ -10791,9 +10977,9 @@ u32 StartFieldEffectForObjectEvent(u8 fieldEffectId, struct ObjectEvent *objectE static void DoShadowFieldEffect(struct ObjectEvent *objectEvent) { - if (!objectEvent->hasShadow) + if (objectEvent->noShadow) { - objectEvent->hasShadow = TRUE; + objectEvent->noShadow = FALSE; StartFieldEffectForObjectEvent(FLDEFF_SHADOW, objectEvent); } } diff --git a/src/faraway_island.c b/src/faraway_island.c index 4f34cf30e6..68be69d267 100755 --- a/src/faraway_island.c +++ b/src/faraway_island.c @@ -392,7 +392,7 @@ void SetMewAboveGrass(void) gSprites[mew->spriteId].subpriority = 1; LoadSpritePalette(&gSpritePalette_GeneralFieldEffect1); - UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(gSpritePalette_GeneralFieldEffect1.tag)); + UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(gSpritePalette_GeneralFieldEffect1.tag), FALSE); x = mew->currentCoords.x; y = mew->currentCoords.y; diff --git a/src/field_effect.c b/src/field_effect.c index 3d6e6e67cb..dc3a87b955 100644 --- a/src/field_effect.c +++ b/src/field_effect.c @@ -791,9 +791,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)); + u32 paletteSlot = LoadSpritePalette(palette); (*script) += 4; + SetPaletteColorMapType(paletteSlot + 16, T1_READ_8(*script)); + UpdateSpritePaletteWithWeather(paletteSlot, TRUE); + (*script)++; } void FieldEffectScript_LoadPalette(u8 **script) @@ -849,6 +851,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); } } @@ -1626,6 +1629,7 @@ static bool8 EscalatorWarpOut_WaitForPlayer(struct Task *task) if (!ObjectEventIsMovementOverridden(objectEvent) || ObjectEventClearHeldMovementIfFinished(objectEvent)) { ObjectEventSetHeldMovement(objectEvent, GetFaceDirectionMovementAction(GetPlayerFacingDirection())); + objectEvent->noShadow = TRUE; // hide shadow for cleaner movement task->tState++; task->data[2] = 0; task->data[3] = 0; @@ -1747,6 +1751,7 @@ static bool8 EscalatorWarpIn_Init(struct Task *task) u8 behavior; CameraObjectFreeze(); objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId]; + objectEvent->noShadow = TRUE; ObjectEventSetHeldMovement(objectEvent, GetFaceDirectionMovementAction(DIR_EAST)); PlayerGetDestCoords(&x, &y); behavior = MapGridGetMetatileBehaviorAt(x, y); @@ -1843,6 +1848,7 @@ static bool8 EscalatorWarpIn_End(struct Task *task) { struct ObjectEvent *objectEvent; objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId]; + objectEvent->noShadow = FALSE; if (ObjectEventClearHeldMovementIfFinished(objectEvent)) { CameraObjectReset(); @@ -1995,6 +2001,7 @@ static bool8 LavaridgeGymB1FWarpEffect_Init(struct Task *task, struct ObjectEven SetCameraPanningCallback(NULL); gPlayerAvatar.preventStep = TRUE; objectEvent->fixedPriority = 1; + objectEvent->noShadow = TRUE; task->data[1] = 1; task->data[0]++; if (objectEvent->localId == OBJ_EVENT_ID_PLAYER) // Hide follower before warping @@ -2191,6 +2198,7 @@ static bool8 LavaridgeGym1FWarpEffect_Init(struct Task *task, struct ObjectEvent CameraObjectFreeze(); gPlayerAvatar.preventStep = TRUE; objectEvent->fixedPriority = 1; + objectEvent->noShadow = TRUE; task->data[0]++; if (objectEvent->localId == OBJ_EVENT_ID_PLAYER) // Hide follower before warping HideFollowerForFieldEffect(); @@ -3314,7 +3322,7 @@ static void FlyOutFieldEffect_FlyOffWithBird(struct Task *task) struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId]; ObjectEventClearHeldMovementIfActive(objectEvent); objectEvent->inanimate = FALSE; - objectEvent->hasShadow = FALSE; + objectEvent->noShadow = TRUE; SetFlyBirdPlayerSpriteId(task->tBirdSpriteId, objectEvent->spriteId); CameraObjectFreeze(); task->tState++; @@ -3536,6 +3544,7 @@ static void FlyInFieldEffect_BirdSwoopDown(struct Task *task) ObjectEventTurn(objectEvent, DIR_WEST); StartSpriteAnim(&gSprites[objectEvent->spriteId], ANIM_GET_ON_OFF_POKEMON_WEST); objectEvent->invisible = FALSE; + objectEvent->noShadow = TRUE; task->tBirdSpriteId = CreateFlyBirdSprite(); StartFlyBirdSwoopDown(task->tBirdSpriteId); SetFlyBirdPlayerSpriteId(task->tBirdSpriteId, objectEvent->spriteId); diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index c17803d0f2..8174d11135 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -54,11 +54,11 @@ u32 FldEff_Shadow(void); #define sReflectionVerticalOffset data[2] #define sIsStillReflection data[7] -void SetUpShadow(struct ObjectEvent *objectEvent, struct Sprite *sprite) +void SetUpShadow(struct ObjectEvent *objectEvent) { gFieldEffectArguments[0] = objectEvent->localId; - gFieldEffectArguments[1] = gSaveBlock1Ptr->location.mapNum; - gFieldEffectArguments[2] = gSaveBlock1Ptr->location.mapGroup; + gFieldEffectArguments[1] = objectEvent->mapNum; + gFieldEffectArguments[2] = objectEvent->mapGroup; FldEff_Shadow(); } @@ -114,15 +114,16 @@ static void LoadObjectReflectionPalette(struct ObjectEvent *objectEvent, struct // Apply a blue tint effect to a palette static void ApplyPondFilter(u8 paletteNum, u16 *dest) { - u32 i, r, g, b; - // CpuCopy16(gPlttBufferUnfaded + 0x100 + paletteNum * 16, dest, 32); + u32 i; + s32 r, g, b; u16 *src = gPlttBufferUnfaded + OBJ_PLTT_ID(paletteNum); *dest++ = *src++; // copy transparency for (i = 0; i < 16 - 1; i++) { - r = GET_R(src[i]); - g = GET_G(src[i]); - b = GET_B(src[i]); + u32 color = *src++; + r = (color << 27) >> 27; + g = (color << 22) >> 27; + b = (color << 17) >> 27; b += 10; if (b > 31) b = 31; @@ -133,21 +134,22 @@ static void ApplyPondFilter(u8 paletteNum, u16 *dest) // Apply a ice tint effect to a palette static void ApplyIceFilter(u8 paletteNum, u16 *dest) { - u32 i, r, g, b; - // CpuCopy16(gPlttBufferUnfaded + 0x100 + paletteNum * 16, dest, 32); + u32 i; + s32 r, g, b; u16 *src = gPlttBufferUnfaded + OBJ_PLTT_ID(paletteNum); *dest++ = *src++; // copy transparency for (i = 0; i < 16 - 1; i++) { - r = GET_R(src[i]); + u32 color = *src++; + r = (color << 27) >> 27; + g = (color << 22) >> 27; + b = (color << 17) >> 27; r -= 5; - if (r > 31) + if (r < 0) r = 0; - g = GET_G(src[i]); g += 3; if (g > 31) g = 31; - b = GET_B(src[i]); b += 16; if (b > 31) b = 31; @@ -171,7 +173,7 @@ static void LoadObjectRegularReflectionPalette(struct ObjectEvent *objectEvent, else ApplyIceFilter(mainSprite->oam.paletteNum, filteredData); paletteNum = LoadSpritePalette(&filteredPal); - UpdateSpritePaletteWithWeather(paletteNum); + UpdateSpritePaletteWithWeather(paletteNum, TRUE); } sprite->oam.paletteNum = paletteNum; sprite->oam.objMode = ST_OAM_OBJ_BLEND; @@ -185,7 +187,7 @@ static void LoadObjectHighBridgeReflectionPalette(struct ObjectEvent *objectEven struct SpritePalette bluePalette = {.tag = HIGH_BRIDGE_PAL_TAG, .data = blueData}; CpuFill16(0x55C9, blueData, PLTT_SIZE_4BPP); sprite->oam.paletteNum = LoadSpritePalette(&bluePalette); - UpdateSpritePaletteWithWeather(sprite->oam.paletteNum); + UpdateSpritePaletteWithWeather(sprite->oam.paletteNum, TRUE); } static void UpdateObjectReflectionSprite(struct Sprite *reflectionSprite) @@ -221,7 +223,7 @@ static void UpdateObjectReflectionSprite(struct Sprite *reflectionSprite) else ApplyIceFilter(mainSprite->oam.paletteNum, filteredData); paletteNum = LoadSpritePalette(&filteredPal); - UpdateSpritePaletteWithWeather(paletteNum); + UpdateSpritePaletteWithWeather(paletteNum, TRUE); } reflectionSprite->oam.paletteNum = paletteNum; } @@ -333,22 +335,26 @@ u32 FldEff_Shadow(void) u8 objectEventId; const struct ObjectEventGraphicsInfo *graphicsInfo; u8 spriteId; - u8 i; - for (i = 0; i < MAX_SPRITES; i++) + 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]); graphicsInfo = GetObjectEventGraphicsInfo(gObjectEvents[objectEventId].graphicsId); if (graphicsInfo->shadowSize == SHADOW_SIZE_NONE) // don't create a shadow at all return 0; - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[sShadowEffectTemplateIds[graphicsInfo->shadowSize]], 0, 0, 0x94); + LoadSpriteSheetByTemplate(gFieldEffectObjectTemplatePointers[sShadowEffectTemplateIds[graphicsInfo->shadowSize]], 0, 0); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[sShadowEffectTemplateIds[graphicsInfo->shadowSize]], 0, 0, OW_OBJECT_SUBPRIORITY + 1); 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]; @@ -383,8 +389,19 @@ void UpdateShadowFieldEffect(struct Sprite *sprite) sprite->y = linkedSprite->y + sprite->sYOffset; #endif sprite->invisible = linkedSprite->invisible; - if (!objectEvent->active || !objectEvent->hasShadow + if (objectEvent->jumpDone) + { + // Ugly signaling to disable shadows after a jump + objectEvent->noShadow = TRUE; + objectEvent->jumpDone = FALSE; + } + if (!objectEvent->active + || objectEvent->noShadow + || objectEvent->inHotSprings + || objectEvent->inSandPile + || gWeatherPtr->noShadows || MetatileBehavior_IsPokeGrass(objectEvent->currentMetatileBehavior) + || MetatileBehavior_IsPuddle(objectEvent->currentMetatileBehavior) || MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior) || MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->previousMetatileBehavior)) { @@ -1516,7 +1533,7 @@ static u32 ShowDisguiseFieldEffect(u8 fldEff, u8 fldEffObj, u8 paletteNum) if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId]; - UpdateSpritePaletteByTemplate(gFieldEffectObjectTemplatePointers[fldEffObj], sprite); + sprite->oam.paletteNum = LoadObjectEventPalette(gFieldEffectObjectTemplatePointers[fldEffObj]->paletteTag); sprite->coordOffsetEnabled ++; sprite->sFldEff = fldEff; sprite->sLocalId = gFieldEffectArguments[0]; diff --git a/src/field_screen_effect.c b/src/field_screen_effect.c index 4d272b9700..726f5539b7 100644 --- a/src/field_screen_effect.c +++ b/src/field_screen_effect.c @@ -1157,6 +1157,7 @@ static void Task_OrbEffect(u8 taskId) ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN1_ON); SetGpuRegBits(REG_OFFSET_BLDCNT, gOrbEffectBackgroundLayerFlags[0]); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(12, 7)); + UpdateShadowColor(RGB(9, 8, 8)); SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR); SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_BG1 | WINOUT_WIN01_BG2 | WINOUT_WIN01_BG3 | WINOUT_WIN01_OBJ); SetBgTilemapPalette(0, 0, 0, DISPLAY_TILE_WIDTH, DISPLAY_TILE_HEIGHT, 0xF); @@ -1224,6 +1225,7 @@ static void Task_OrbEffect(u8 taskId) SetGpuReg(REG_OFFSET_DISPCNT, tDispCnt); SetGpuReg(REG_OFFSET_BLDCNT, tBldCnt); SetGpuReg(REG_OFFSET_BLDALPHA, tBldAlpha); + UpdateShadowColor(RGB_BLACK); SetGpuReg(REG_OFFSET_WININ, tWinIn); SetGpuReg(REG_OFFSET_WINOUT, tWinOut); ScriptContext_Enable(); diff --git a/src/field_specials.c b/src/field_specials.c index 30b121acd0..db86f99f32 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -68,6 +68,7 @@ #include "constants/battle_frontier.h" #include "constants/weather.h" #include "constants/metatile_labels.h" +#include "constants/rgb.h" #include "palette.h" #include "battle_util.h" #include "naming_screen.h" @@ -3281,6 +3282,7 @@ static void ChangeDeoxysRockLevel(u8 rockLevel) { u8 paletteNum = IndexOfSpritePaletteTag(OBJ_EVENT_PAL_TAG_BIRTH_ISLAND_STONE); LoadPalette(&sDeoxysRockPalettes[rockLevel], OBJ_PLTT_ID(paletteNum), PLTT_SIZEOF(4)); + UpdateSpritePaletteWithWeather(paletteNum, FALSE); if (rockLevel == 0) PlaySE(SE_M_CONFUSE_RAY); // Failure sound @@ -3332,7 +3334,7 @@ void SetDeoxysRockPalette(void) u32 paletteNum = IndexOfSpritePaletteTag(OBJ_EVENT_PAL_TAG_BIRTH_ISLAND_STONE); LoadPalette(&sDeoxysRockPalettes[(u8)VarGet(VAR_DEOXYS_ROCK_LEVEL)], OBJ_PLTT_ID(paletteNum), PLTT_SIZEOF(4)); // Set faded to all black, weather blending handled during fade-in - CpuFill16(0, &gPlttBufferFaded[OBJ_PLTT_ID(paletteNum)], 32); + CpuFill16(RGB_BLACK, &gPlttBufferFaded[OBJ_PLTT_ID(paletteNum)], PLTT_SIZE_4BPP); } void SetPCBoxToSendMon(u8 boxId) diff --git a/src/field_weather.c b/src/field_weather.c index ae69289d8a..7e8d83b2a5 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -3,6 +3,7 @@ #include "constants/weather.h" #include "constants/rgb.h" #include "util.h" +#include "decompress.h" #include "event_object_movement.h" #include "field_weather.h" #include "fieldmap.h" @@ -18,16 +19,10 @@ #include "trig.h" #include "gpu_regs.h" #include "field_camera.h" +#include "overworld.h" #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; @@ -208,7 +203,7 @@ void StartWeather(void) sPaletteColorMapTypes = sBasePaletteColorMapTypes; gWeatherPtr->contrastColorMapSpritePalIndex = index; - gWeatherPtr->weatherPicSpritePalIndex = AllocSpritePalette(PALTAG_WEATHER_2); + gWeatherPtr->weatherPicSpritePalIndex = 0xFF; // defer allocation until needed gWeatherPtr->rainSpriteCount = 0; gWeatherPtr->curRainSpriteIndex = 0; gWeatherPtr->cloudSpritesCreated = 0; @@ -318,7 +313,8 @@ static void Task_WeatherMain(u8 taskId) static void None_Init(void) { - Weather_SetBlendCoeffs(8, 12); // Indoor shadows + Weather_SetBlendCoeffs(8, BASE_SHADOW_INTENSITY); // Indoor shadows + gWeatherPtr->noShadows = FALSE; gWeatherPtr->targetColorMapIndex = 0; gWeatherPtr->colorMapStepDelay = 0; } @@ -458,18 +454,31 @@ static void ApplyColorMap(u8 startPalIndex, u8 numPalettes, s8 colorMapIndex) if (colorMapIndex > 0) { + // Create the palette mask + u32 palettes = PALETTES_ALL; + numPalettes += startPalIndex; + palettes = (palettes >> startPalIndex) << startPalIndex; + palettes = (palettes << (32 - numPalettes)) >> (32 - numPalettes); + numPalettes -= startPalIndex; colorMapIndex--; palOffset = PLTT_ID(startPalIndex); + UpdateAltBgPalettes(palettes & PALETTES_BG); + // Thunder gamma-shift looks bad on night-blended palettes, so ignore time blending in some situations + if (colorMapIndex <= 3 && MapHasNaturalLight(gMapHeader.mapType)) + UpdatePalettesWithTime(palettes); + else + CpuFastCopy(gPlttBufferUnfaded + palOffset, gPlttBufferFaded + palOffset, PLTT_SIZE_4BPP * numPalettes); numPalettes += startPalIndex; curPalIndex = startPalIndex; // Loop through the specified palette range and apply necessary color maps. while (curPalIndex < numPalettes) { - if (sPaletteColorMapTypes[curPalIndex] == COLOR_MAP_NONE) + // don't blend special palettes immune to blending + if (sPaletteColorMapTypes[curPalIndex] == COLOR_MAP_NONE || + (curPalIndex >= 16 && IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(curPalIndex - 16)))) { // No palette change. - CpuFastCopy(&gPlttBufferUnfaded[palOffset], &gPlttBufferFaded[palOffset], PLTT_SIZE_4BPP); palOffset += 16; } else @@ -484,7 +493,7 @@ static void ApplyColorMap(u8 startPalIndex, u8 numPalettes, s8 colorMapIndex) for (i = 0; i < 16; i++) { // Apply color map to the original color. - struct RGBColor baseColor = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset]; + struct RGBColor baseColor = *(struct RGBColor *)&gPlttBufferFaded[palOffset]; r = colorMap[baseColor.r]; g = colorMap[baseColor.g]; b = colorMap[baseColor.b]; @@ -525,8 +534,17 @@ static void ApplyColorMap(u8 startPalIndex, u8 numPalettes, s8 colorMapIndex) } else { - // No palette blending. - CpuFastCopy(&gPlttBufferUnfaded[PLTT_ID(startPalIndex)], &gPlttBufferFaded[PLTT_ID(startPalIndex)], numPalettes * PLTT_SIZE_4BPP); + if (MapHasNaturalLight(gMapHeader.mapType)) + { + // Time-blend + u32 palettes = ((1 << numPalettes) - 1) << startPalIndex; + UpdateAltBgPalettes(palettes & PALETTES_BG); + UpdatePalettesWithTime(palettes); + } + else + { + CpuFastCopy(&gPlttBufferUnfaded[PLTT_ID(startPalIndex)], &gPlttBufferFaded[PLTT_ID(startPalIndex)], numPalettes * PLTT_SIZE_4BPP); + } } } @@ -547,10 +565,13 @@ static void ApplyColorMapWithBlend(u8 startPalIndex, u8 numPalettes, s8 colorMap while (curPalIndex < numPalettes) { + UpdateAltBgPalettes((1 << (palOffset >> 4)) & PALETTES_BG); + CpuFastCopy(gPlttBufferUnfaded + palOffset, gPlttBufferFaded + palOffset, 16 * sizeof(u16)); + UpdatePalettesWithTime(1 << (palOffset >> 4)); // Apply TOD blend if (sPaletteColorMapTypes[curPalIndex] == COLOR_MAP_NONE) { // No color map. Simply blend the colors. - BlendPalette(palOffset, 16, blendCoeff, blendColor); + BlendPalettesFine(1, gPlttBufferFaded + palOffset, gPlttBufferFaded + palOffset, blendCoeff, blendColor); palOffset += 16; } else @@ -564,7 +585,7 @@ static void ApplyColorMapWithBlend(u8 startPalIndex, u8 numPalettes, s8 colorMap for (i = 0; i < 16; i++) { - struct RGBColor baseColor = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset]; + struct RGBColor baseColor = *(struct RGBColor *)&gPlttBufferFaded[palOffset]; u8 r = colorMap[baseColor.r]; u8 g = colorMap[baseColor.g]; u8 b = colorMap[baseColor.b]; @@ -636,51 +657,28 @@ static void ApplyDroughtColorMapWithBlend(s8 colorMapIndex, u8 blendCoeff, u32 b } } +// This is only called during fade-in/fade-out in fog +// blendCoeff & blendColor are the *fade* colors, not fog colors static void ApplyFogBlend(u8 blendCoeff, u32 blendColor) { - struct RGBColor color; - u8 rBlend; - u8 gBlend; - u8 bBlend; - u16 curPalIndex; + u32 curPalIndex; + u16 fogCoeff = min((gTimeOfDay + 1) * 4, 12); - BlendPalette(BG_PLTT_ID(0), 16 * 16, blendCoeff, blendColor); - color = *(struct RGBColor *)&blendColor; - rBlend = color.r; - gBlend = color.g; - bBlend = color.b; + // First blend all palettes with time + UpdateAltBgPalettes(PALETTES_BG); + CpuFastCopy(gPlttBufferUnfaded, gPlttBufferFaded, PLTT_BUFFER_SIZE * 2); + UpdatePalettesWithTime(PALETTES_ALL); + // Then blend tile palettes [0, 12] faded->faded with fadeIn color + BlendPalettesFine(PALETTES_MAP, gPlttBufferFaded, gPlttBufferFaded, blendCoeff, blendColor); + // Do fog blending on marked sprite palettes for (curPalIndex = 16; curPalIndex < 32; curPalIndex++) { if (LightenSpritePaletteInFog(curPalIndex)) - { - u16 palEnd = PLTT_ID(curPalIndex + 1); - u16 palOffset = PLTT_ID(curPalIndex); - - while (palOffset < palEnd) - { - struct RGBColor color = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset]; - u8 r = color.r; - u8 g = color.g; - u8 b = color.b; - - r += ((28 - r) * 3) >> 2; - g += ((31 - g) * 3) >> 2; - b += ((28 - b) * 3) >> 2; - - r += ((rBlend - r) * blendCoeff) >> 4; - g += ((gBlend - g) * blendCoeff) >> 4; - b += ((bBlend - b) * blendCoeff) >> 4; - - gPlttBufferFaded[palOffset] = RGB2(r, g, b); - palOffset++; - } - } - else - { - BlendPalette(PLTT_ID(curPalIndex), 16, blendCoeff, blendColor); - } + BlendPalettesFine(1, gPlttBufferFaded + PLTT_ID(curPalIndex), gPlttBufferFaded + PLTT_ID(curPalIndex), fogCoeff, RGB(28, 31, 28)); } + // Finally blend all sprite palettes faded->faded with fadeIn color + BlendPalettesFine(PALETTES_OBJECTS, gPlttBufferFaded, gPlttBufferFaded, blendCoeff, blendColor); } static void MarkFogSpritePalToLighten(u8 paletteIndex) @@ -694,7 +692,10 @@ static void MarkFogSpritePalToLighten(u8 paletteIndex) static bool8 LightenSpritePaletteInFog(u8 paletteIndex) { - u16 i; + u32 i; + + if (paletteIndex >= 16 && IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(paletteIndex - 16))) + return FALSE; for (i = 0; i < gWeatherPtr->lightenedFogSpritePalsCount; i++) { @@ -772,8 +773,10 @@ void FadeScreen(u8 mode, s8 delay) if (fadeOut) { - if (useWeatherPal) - CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, PLTT_SIZE); + // Note: Copying faded -> unfaded like this works fine, except if the screen is faded back in + // without transitioning to a different screen + // For cases like that, use fadescreenswapbuffers + CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, PLTT_BUFFER_SIZE * 2); BeginNormalPaletteFade(PALETTES_ALL, delay, 0, 16, fadeColor); gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_OUT; @@ -781,10 +784,20 @@ void FadeScreen(u8 mode, s8 delay) else { gWeatherPtr->fadeDestColor = fadeColor; + UpdateTimeOfDay(); if (useWeatherPal) - gWeatherPtr->fadeScreenCounter = 0; + { + gWeatherPtr->fadeScreenCounter = 0; // Triggers gamma-shift-based fade-in + } + else if (MapHasNaturalLight(gMapHeader.mapType)) + { + UpdateAltBgPalettes(PALETTES_BG); + BeginTimeOfDayPaletteFade(PALETTES_ALL, delay, 16, 0, &gTimeBlend.startBlend, &gTimeBlend.endBlend, gTimeBlend.weight, fadeColor); + } else + { BeginNormalPaletteFade(PALETTES_ALL, delay, 16, 0, fadeColor); + } gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_IN; gWeatherPtr->fadeInFirstFrame = TRUE; @@ -794,12 +807,41 @@ 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) +void FadeScreenHardware(u32 mode, s32 delay) +{ + u32 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; + } +} + bool8 IsWeatherNotFadingIn(void) { return (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_SCREEN_FADING_IN); } -void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex) +void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex, bool8 allowFog) { u16 paletteIndex = 16 + spritePaletteIndex; u16 i; @@ -826,20 +868,37 @@ void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex) default: if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) { - ApplyColorMap(paletteIndex, 1, gWeatherPtr->colorMapIndex); + if (gWeatherPtr->colorMapIndex) + ApplyColorMap(paletteIndex, 1, gWeatherPtr->colorMapIndex); + else + UpdateSpritePaletteWithTime(spritePaletteIndex); } else { - paletteIndex = PLTT_ID(paletteIndex); - BlendPalette(paletteIndex, 16, 12, RGB(28, 31, 28)); + // In horizontal fog, only specific palettes should be fog-blended + if (allowFog) + { + i = min((gTimeOfDay + 1) * 4, 12); // fog coeff, highest in day and lowest at night + paletteIndex = PLTT_ID(paletteIndex); + // First blend with time + CpuFastCopy(gPlttBufferUnfaded + paletteIndex, gPlttBufferFaded + paletteIndex, PLTT_SIZE_4BPP); + UpdateSpritePaletteWithTime(spritePaletteIndex); + // Then blend faded->faded with fog coeff + BlendPalettesFine(1, gPlttBufferFaded + paletteIndex, gPlttBufferFaded + paletteIndex, i, RGB(28, 31, 28)); + } + else + { + // Otherwise, just time-blend the palette + UpdateSpritePaletteWithTime(spritePaletteIndex); + } } break; } } -void ApplyWeatherColorMapToPal(u8 paletteIndex) +void ApplyWeatherColorMapToPals(u8 startPalIndex, u8 numPalettes) { - ApplyColorMap(paletteIndex, 1, gWeatherPtr->colorMapIndex); + ApplyColorMap(startPalIndex, numPalettes, gWeatherPtr->colorMapIndex); } static bool8 UNUSED IsFirstFrameOfWeatherFadeIn(void) @@ -852,8 +911,11 @@ static bool8 UNUSED IsFirstFrameOfWeatherFadeIn(void) void LoadCustomWeatherSpritePalette(const u16 *palette) { + if (gWeatherPtr->weatherPicSpritePalIndex > 16 // haven't allocated palette yet + && (gWeatherPtr->weatherPicSpritePalIndex = AllocSpritePalette(PALTAG_WEATHER_2)) > 16) + return; LoadPalette(palette, OBJ_PLTT_ID(gWeatherPtr->weatherPicSpritePalIndex), PLTT_SIZE_4BPP); - UpdateSpritePaletteWithWeather(gWeatherPtr->weatherPicSpritePalIndex); + UpdateSpritePaletteWithWeather(gWeatherPtr->weatherPicSpritePalIndex, TRUE); } static void LoadDroughtWeatherPalette(u8 *palsIndex, u8 *palsOffset) @@ -934,7 +996,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) @@ -1084,11 +1149,29 @@ void SetWeatherPalStateIdle(void) gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE; } +const u8 *SetPaletteColorMapType(u8 paletteIndex, enum ColorMapType colorMapType) +{ + if (sPaletteColorMapTypes[paletteIndex] == colorMapType) + return sPaletteColorMapTypes; + // setup field effect color map + if (sPaletteColorMapTypes != sFieldEffectPaletteColorMapTypes) + { + CpuCopy16(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) + sFieldEffectPaletteColorMapTypes[paletteIndex] = sBasePaletteColorMapTypes[paletteIndex]; } void ResetPreservedPalettesInWeather(void) diff --git a/src/field_weather_effect.c b/src/field_weather_effect.c index 234c9c56d3..8b41fca458 100644 --- a/src/field_weather_effect.c +++ b/src/field_weather_effect.c @@ -8,11 +8,13 @@ #include "script.h" #include "constants/weather.h" #include "constants/songs.h" +#include "constants/rgb.h" #include "sound.h" #include "sprite.h" #include "task.h" #include "trig.h" #include "gpu_regs.h" +#include "palette.h" EWRAM_DATA static u8 sCurrentAbnormalWeather = 0; @@ -93,6 +95,7 @@ static const struct SpriteTemplate sCloudSpriteTemplate = void Clouds_InitVars(void) { + gWeatherPtr->noShadows = FALSE; gWeatherPtr->targetColorMapIndex = 0; gWeatherPtr->colorMapStepDelay = 20; gWeatherPtr->weatherGfxLoaded = FALSE; @@ -149,11 +152,14 @@ bool8 Clouds_Finish(void) return FALSE; } +STATIC_ASSERT(OW_SHADOW_INTENSITY >= 0 && OW_SHADOW_INTENSITY <= 16, ObjEventShadowTransparencyNotInRange) + void Sunny_InitVars(void) { gWeatherPtr->targetColorMapIndex = 0; gWeatherPtr->colorMapStepDelay = 20; - Weather_SetBlendCoeffs(8, 12); + Weather_SetBlendCoeffs(8, BASE_SHADOW_INTENSITY); // preserve shadow darkness + gWeatherPtr->noShadows = FALSE; } void Sunny_InitAll(void) @@ -237,6 +243,7 @@ void Drought_InitVars(void) gWeatherPtr->weatherGfxLoaded = FALSE; gWeatherPtr->targetColorMapIndex = 0; gWeatherPtr->colorMapStepDelay = 0; + gWeatherPtr->noShadows = FALSE; } void Drought_InitAll(void) @@ -479,6 +486,8 @@ void Rain_InitVars(void) gWeatherPtr->targetColorMapIndex = 3; gWeatherPtr->colorMapStepDelay = 20; SetRainStrengthFromSoundEffect(SE_RAIN); + Weather_SetBlendCoeffs(8, BASE_SHADOW_INTENSITY); // preserve shadow darkness + gWeatherPtr->noShadows = FALSE; } void Rain_InitAll(void) @@ -772,6 +781,8 @@ void Snow_InitVars(void) gWeatherPtr->colorMapStepDelay = 20; gWeatherPtr->targetSnowflakeSpriteCount = NUM_SNOWFLAKE_SPRITES; gWeatherPtr->snowflakeVisibleCounter = 0; + Weather_SetBlendCoeffs(8, BASE_SHADOW_INTENSITY); // preserve shadow darkness + gWeatherPtr->noShadows = FALSE; } void Snow_InitAll(void) @@ -1019,6 +1030,8 @@ void Thunderstorm_InitVars(void) gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment gWeatherPtr->thunderEnqueued = FALSE; SetRainStrengthFromSoundEffect(SE_THUNDERSTORM); + Weather_SetBlendCoeffs(8, BASE_SHADOW_INTENSITY); // preserve shadow darkness + gWeatherPtr->noShadows = FALSE; } void Thunderstorm_InitAll(void) @@ -1047,6 +1060,8 @@ void Downpour_InitVars(void) gWeatherPtr->colorMapStepDelay = 20; gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment SetRainStrengthFromSoundEffect(SE_DOWNPOUR); + Weather_SetBlendCoeffs(8, BASE_SHADOW_INTENSITY); // preserve shadow darkness + gWeatherPtr->noShadows = FALSE; } void Downpour_InitAll(void) @@ -1339,6 +1354,26 @@ void FogHorizontal_Main(void); static void CreateFogHorizontalSprites(void); static void DestroyFogHorizontalSprites(void); +// Updates just the color of shadows to match special weather blending +u8 UpdateShadowColor(u16 color) +{ + u8 paletteNum = IndexOfSpritePaletteTag(TAG_WEATHER_START); + u16 ALIGNED(4) tempBuffer[16]; + u16 blendedColor; + if (paletteNum < 16) + { + u16 index = OBJ_PLTT_ID(paletteNum) + SHADOW_COLOR_INDEX; + gPlttBufferUnfaded[index] = gPlttBufferFaded[index] = color; + // Copy to temporary buffer, blend, and keep just the shadow color index + CpuFastCopy(&gPlttBufferFaded[index - SHADOW_COLOR_INDEX], tempBuffer, PLTT_SIZE_4BPP); + UpdateSpritePaletteWithTime(paletteNum); + blendedColor = gPlttBufferFaded[index]; + CpuFastCopy(tempBuffer, &gPlttBufferFaded[index - SHADOW_COLOR_INDEX], PLTT_SIZE_4BPP); + gPlttBufferFaded[index] = blendedColor; + } + return paletteNum; +} + void FogHorizontal_InitVars(void) { gWeatherPtr->initStep = 0; @@ -1352,6 +1387,7 @@ void FogHorizontal_InitVars(void) gWeatherPtr->fogHScrollPosX = 0; Weather_SetBlendCoeffs(0, 16); } + gWeatherPtr->noShadows = FALSE; } void FogHorizontal_InitAll(void) @@ -1374,9 +1410,14 @@ void FogHorizontal_Main(void) case 0: CreateFogHorizontalSprites(); if (gWeatherPtr->currWeather == WEATHER_FOG_HORIZONTAL) + { Weather_SetTargetBlendCoeffs(12, 8, 3); + UpdateShadowColor(RGB_GRAY); + } else + { Weather_SetTargetBlendCoeffs(4, 16, 0); + } gWeatherPtr->initStep++; break; case 1: @@ -1413,6 +1454,7 @@ bool8 FogHorizontal_Finish(void) gWeatherPtr->finishStep++; break; default: + UpdateShadowColor(RGB_BLACK); return FALSE; } return TRUE; @@ -1503,9 +1545,9 @@ void Ash_InitVars(void) gWeatherPtr->ashUnused = 20; // Never read if (!gWeatherPtr->ashSpritesCreated) { - Weather_SetBlendCoeffs(0, 16); - SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(64, 63)); // These aren't valid blend coefficients! + Weather_SetBlendCoeffs(0, BASE_SHADOW_INTENSITY); } + gWeatherPtr->noShadows = FALSE; } void Ash_InitAll(void) @@ -1531,7 +1573,7 @@ void Ash_Main(void) if (!gWeatherPtr->ashSpritesCreated) CreateAshSprites(); - Weather_SetTargetBlendCoeffs(16, 0, 1); + Weather_SetTargetBlendCoeffs(10, 12, 1); gWeatherPtr->initStep++; break; case 2: @@ -1552,7 +1594,7 @@ bool8 Ash_Finish(void) switch (gWeatherPtr->finishStep) { case 0: - Weather_SetTargetBlendCoeffs(0, 16, 1); + Weather_SetTargetBlendCoeffs(0, 12, 1); gWeatherPtr->finishStep++; break; case 1: @@ -1722,6 +1764,7 @@ void FogDiagonal_InitVars(void) gWeatherPtr->fogDPosY = 0; Weather_SetBlendCoeffs(0, 16); } + gWeatherPtr->noShadows = TRUE; } void FogDiagonal_InitAll(void) @@ -1935,6 +1978,7 @@ void Sandstorm_InitVars(void) Weather_SetBlendCoeffs(0, 16); } + gWeatherPtr->noShadows = FALSE; } void Sandstorm_InitAll(void) @@ -1959,7 +2003,8 @@ void Sandstorm_Main(void) gWeatherPtr->initStep++; break; case 1: - Weather_SetTargetBlendCoeffs(16, 0, 0); + Weather_SetTargetBlendCoeffs(16, 2, 0); + UpdateShadowColor(RGB_GRAY); gWeatherPtr->initStep++; break; case 2: @@ -1985,9 +2030,12 @@ bool8 Sandstorm_Finish(void) case 1: if (Weather_UpdateBlend()) gWeatherPtr->finishStep++; + if (gWeatherPtr->currBlendEVB == 12) + UpdateShadowColor(RGB_BLACK); break; case 2: DestroySandstormSprites(); + UpdateShadowColor(RGB_BLACK); gWeatherPtr->finishStep++; break; default: @@ -2221,6 +2269,8 @@ void Shade_InitVars(void) gWeatherPtr->initStep = 0; gWeatherPtr->targetColorMapIndex = 3; gWeatherPtr->colorMapStepDelay = 20; + Weather_SetBlendCoeffs(8, BASE_SHADOW_INTENSITY); // preserve shadow darkness + gWeatherPtr->noShadows = FALSE; } void Shade_InitAll(void) @@ -2282,6 +2332,7 @@ void Bubbles_InitVars(void) gWeatherPtr->bubblesCoordsIndex = 0; gWeatherPtr->bubblesSpriteCount = 0; } + gWeatherPtr->noShadows = TRUE; } void Bubbles_InitAll(void) diff --git a/src/fieldmap.c b/src/fieldmap.c index 84e3c564fc..3a452a5d25 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -877,22 +877,27 @@ static void UNUSED ApplyGlobalTintToPaletteSlot(u8 slot, u8 count) } -static void LoadTilesetPalette(struct Tileset const *tileset, u16 destOffset, u16 size) +static void LoadTilesetPalette(struct Tileset const *tileset, u16 destOffset, u16 size, bool8 skipFaded) { - u16 black = RGB_BLACK; - if (tileset) { if (tileset->isSecondary == FALSE) { - LoadPalette(&black, destOffset, PLTT_SIZEOF(1)); - LoadPalette(tileset->palettes[0] + 1, destOffset + 1, size - PLTT_SIZEOF(1)); - ApplyGlobalTintToPaletteEntries(destOffset + 1, (size - PLTT_SIZEOF(1)) >> 1); + if (skipFaded) + CpuFastCopy(tileset->palettes, &gPlttBufferUnfaded[destOffset], size); // always word-aligned + else + LoadPaletteFast(tileset->palettes, destOffset, size); + gPlttBufferFaded[destOffset] = gPlttBufferUnfaded[destOffset] = RGB_BLACK; + ApplyGlobalTintToPaletteEntries(destOffset + 1, (size - 2) >> 1); } else if (tileset->isSecondary == TRUE) { - LoadPalette(tileset->palettes[NUM_PALS_IN_PRIMARY], destOffset, size); - ApplyGlobalTintToPaletteEntries(destOffset, size >> 1); + // All 'gTilesetPalettes_' arrays should have ALIGNED(4) in them, + // but we use SmartCopy here just in case they don't + if (skipFaded) + CpuCopy16(tileset->palettes[NUM_PALS_IN_PRIMARY], &gPlttBufferUnfaded[destOffset], size); + else + LoadPaletteFast(tileset->palettes[NUM_PALS_IN_PRIMARY], destOffset, size); } else { @@ -919,12 +924,12 @@ void CopySecondaryTilesetToVramUsingHeap(struct MapLayout const *mapLayout) static void LoadPrimaryTilesetPalette(struct MapLayout const *mapLayout) { - LoadTilesetPalette(mapLayout->primaryTileset, BG_PLTT_ID(0), NUM_PALS_IN_PRIMARY * PLTT_SIZE_4BPP); + LoadTilesetPalette(mapLayout->primaryTileset, 0, NUM_PALS_IN_PRIMARY * PLTT_SIZE_4BPP, FALSE); } -void LoadSecondaryTilesetPalette(struct MapLayout const *mapLayout) +void LoadSecondaryTilesetPalette(struct MapLayout const *mapLayout, bool8 skipFaded) { - LoadTilesetPalette(mapLayout->secondaryTileset, BG_PLTT_ID(NUM_PALS_IN_PRIMARY), (NUM_PALS_TOTAL - NUM_PALS_IN_PRIMARY) * PLTT_SIZE_4BPP); + LoadTilesetPalette(mapLayout->secondaryTileset, NUM_PALS_IN_PRIMARY * 16, (NUM_PALS_TOTAL - NUM_PALS_IN_PRIMARY) * PLTT_SIZE_4BPP, skipFaded); } void CopyMapTilesetsToVram(struct MapLayout const *mapLayout) @@ -941,6 +946,6 @@ void LoadMapTilesetPalettes(struct MapLayout const *mapLayout) if (mapLayout) { LoadPrimaryTilesetPalette(mapLayout); - LoadSecondaryTilesetPalette(mapLayout); + LoadSecondaryTilesetPalette(mapLayout, FALSE); } } diff --git a/src/follower_helper.c b/src/follower_helper.c index 02b39902eb..76481bb75f 100644 --- a/src/follower_helper.c +++ b/src/follower_helper.c @@ -2,6 +2,8 @@ #include "data.h" #include "event_scripts.h" #include "follower_helper.h" +#include "overworld.h" +#include "rtc.h" #include "constants/battle.h" #include "constants/followers.h" #include "constants/metatile_behaviors.h" @@ -66,6 +68,12 @@ static const u8 sCondMsg40[] = _("{STR_VAR_1} is gnawing at the ice."); static const u8 sCondMsg41[] = _("{STR_VAR_1} is touching the ice."); static const u8* const sIceTexts[] = {sCondMsg26, sCondMsg40, sCondMsg41, NULL}; static const u8 sCondMsg42[] = _("{STR_VAR_1}'s burn looks painful!"); +static const u8 sCondMsg43[] = _("{STR_VAR_1} is happy to see what's\noutdoors!"); +static const u8 sCondMsg44[] = _("{STR_VAR_1} is looking up at the\nsky."); +static const u8* const sDayTexts[] = {sCondMsg43, sCondMsg44, NULL}; +static const u8 sCondMsg45[] = _("Your POKéMON is staring spellbound\nat the night sky!"); +static const u8 sCondMsg46[] = _("Your POKéMON is happily gazing at\nthe beautiful, starry sky!"); +static const u8* const sNightTexts[] = {sCondMsg45, sCondMsg46, NULL}; // See the struct definition in follower_helper.h for more info const struct FollowerMsgInfoExtended gFollowerConditionalMessages[COND_MSG_COUNT] = @@ -350,6 +358,26 @@ const struct FollowerMsgInfoExtended gFollowerConditionalMessages[COND_MSG_COUNT MATCH_STATUS(STATUS1_BURN), }, }, + [COND_MSG_DAY] = + { + .text = (u8*)sDayTexts, + .textSpread = 1, + .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = + { + MATCH_TIME_OF_DAY(TIME_DAY), + }, + }, + [COND_MSG_NIGHT] = + { + .text = (u8*)sNightTexts, + .textSpread = 1, + .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = + { + MATCH_TIME_OF_DAY(TIME_NIGHT), + }, + }, }; // Pool of "unconditional" follower messages diff --git a/src/graphics.c b/src/graphics.c index 59659ab015..21e752c646 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -1996,7 +1996,7 @@ const u32 gUnionRoomChat_Keyboard_Gfx[] = INCBIN_U32("graphics/union_room_chat/k const u32 gUnionRoomChat_Keyboard_Tilemap[] = INCBIN_U32("graphics/union_room_chat/keyboard.bin.lz"); const u32 gUnionRoomChat_RButtonLabels[] = INCBIN_U32("graphics/union_room_chat/r_button_labels.4bpp.lz"); -const u16 gTilesetPalettes_General[][16] = +const u16 ALIGNED(4) gTilesetPalettes_General[][16] = { INCBIN_U16("data/tilesets/primary/general/palettes/00.gbapal"), INCBIN_U16("data/tilesets/primary/general/palettes/01.gbapal"), diff --git a/src/map_name_popup.c b/src/map_name_popup.c index 500d45e866..fe10073fc8 100644 --- a/src/map_name_popup.c +++ b/src/map_name_popup.c @@ -41,6 +41,7 @@ enum MapPopUp_Themes_BW // static functions static void Task_MapNamePopUpWindow(u8 taskId); +static void UpdateSecondaryPopUpWindow(u8 secondaryPopUpWindowId); static void ShowMapNamePopUpWindow(void); static void LoadMapNamePopUpWindowBg(void); @@ -423,6 +424,7 @@ static void Task_MapNamePopUpWindow(u8 taskId) break; case STATE_WAIT: // Wait while the window is fully onscreen. + UpdateSecondaryPopUpWindow(GetSecondaryPopUpWindowId()); if (++task->tOnscreenTimer > 120) { task->tOnscreenTimer = 0; @@ -500,6 +502,20 @@ void HideMapNamePopUpWindow(void) } } +static void UpdateSecondaryPopUpWindow(u8 secondaryPopUpWindowId) +{ + u8 mapDisplayHeader[24]; + u8 *withoutPrefixPtr = &(mapDisplayHeader[0]); + + if (OW_POPUP_BW_TIME_MODE != OW_POPUP_BW_TIME_NONE) + { + RtcCalcLocalTime(); + FormatDecimalTimeWithoutSeconds(withoutPrefixPtr, gLocalTime.hours, gLocalTime.minutes, OW_POPUP_BW_TIME_MODE == OW_POPUP_BW_TIME_24_HR); + AddTextPrinterParameterized(secondaryPopUpWindowId, FONT_SMALL, mapDisplayHeader, GetStringRightAlignXOffset(FONT_SMALL, mapDisplayHeader, DISPLAY_WIDTH) - 5, 8, TEXT_SKIP_DRAW, NULL); + } + CopyWindowToVram(secondaryPopUpWindowId, COPYWIN_FULL); +} + static void ShowMapNamePopUpWindow(void) { u8 mapDisplayHeader[24]; @@ -550,16 +566,8 @@ static void ShowMapNamePopUpWindow(void) if (OW_POPUP_GENERATION == GEN_5) { AddTextPrinterParameterized(mapNamePopUpWindowId, FONT_SHORT, mapDisplayHeader, 8, 2, TEXT_SKIP_DRAW, NULL); - - if (OW_POPUP_BW_TIME_MODE != OW_POPUP_BW_TIME_NONE) - { - RtcCalcLocalTime(); - FormatDecimalTimeWithoutSeconds(withoutPrefixPtr, gLocalTime.hours, gLocalTime.minutes, OW_POPUP_BW_TIME_MODE == OW_POPUP_BW_TIME_24_HR); - AddTextPrinterParameterized(secondaryPopUpWindowId, FONT_SMALL, mapDisplayHeader, GetStringRightAlignXOffset(FONT_SMALL, mapDisplayHeader, DISPLAY_WIDTH) - 5, 8, TEXT_SKIP_DRAW, NULL); - } - CopyWindowToVram(mapNamePopUpWindowId, COPYWIN_FULL); - CopyWindowToVram(secondaryPopUpWindowId, COPYWIN_FULL); + UpdateSecondaryPopUpWindow(secondaryPopUpWindowId); } else { diff --git a/src/overworld.c b/src/overworld.c index 7f38405bac..ae7d361cdc 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -10,9 +10,11 @@ #include "event_data.h" #include "event_object_movement.h" #include "event_scripts.h" +#include "fake_rtc.h" #include "field_camera.h" #include "field_control_avatar.h" #include "field_effect.h" +#include "field_effect_helpers.h" #include "field_message_box.h" #include "field_player_avatar.h" #include "field_screen_effect.h" @@ -66,6 +68,7 @@ #include "vs_seeker.h" #include "frontier_util.h" #include "constants/abilities.h" +#include "constants/event_objects.h" #include "constants/layouts.h" #include "constants/map_types.h" #include "constants/region_map_sections.h" @@ -197,6 +200,11 @@ COMMON_DATA bool8 (*gFieldCallback2)(void) = NULL; COMMON_DATA u8 gLocalLinkPlayerId = 0; // This is our player id in a multiplayer mode. COMMON_DATA u8 gFieldLinkPlayerCount = 0; +u8 gTimeOfDay; +struct TimeBlendSettings gTimeBlend; +s16 gTimeUpdateCounter; // playTimeVBlanks will eventually overflow, so this is used to update TOD + +// EWRAM vars EWRAM_DATA static u8 sObjectEventLoadFlag = 0; EWRAM_DATA struct WarpData gLastUsedWarp = {0}; EWRAM_DATA static struct WarpData sWarpDestination = {0}; // new warp position @@ -206,6 +214,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}; EWRAM_DATA bool8 gExitStairsMovementDisabled = FALSE; @@ -828,8 +837,6 @@ bool8 SetDiveWarpDive(u16 x, u16 y) void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) { - s32 paletteIndex; - SetWarpDestination(mapGroup, mapNum, WARP_ID_NONE, -1, -1); // Dont transition map music between BF Outside West/East @@ -859,10 +866,9 @@ if (I_VS_SEEKER_CHARGING != 0) RunOnTransitionMapScript(); InitMap(); CopySecondaryTilesetToVramUsingHeap(gMapHeader.mapLayout); - LoadSecondaryTilesetPalette(gMapHeader.mapLayout); + LoadSecondaryTilesetPalette(gMapHeader.mapLayout, TRUE); // skip copying to Faded, gamma shift will take care of it - for (paletteIndex = NUM_PALS_IN_PRIMARY; paletteIndex < NUM_PALS_TOTAL; paletteIndex++) - ApplyWeatherColorMapToPal(paletteIndex); + ApplyWeatherColorMapToPals(NUM_PALS_IN_PRIMARY, NUM_PALS_TOTAL - NUM_PALS_IN_PRIMARY); // palettes [6,12] InitSecondaryTilesetAnimation(); UpdateLocationHistoryForRoamer(); @@ -907,6 +913,8 @@ static void LoadMapFromWarp(bool32 a1) TrySetMapSaveWarpStatus(); ClearTempFieldEventData(); ResetDexNavSearch(); + // reset hours override on every warp + sHoursOverride = 0; ResetCyclingRoadChallengeData(); RestartWildEncounterImmunitySteps(); #if FREE_MATCH_CALL == FALSE @@ -1535,6 +1543,155 @@ void CB1_Overworld(void) DoCB1_Overworld(gMain.newKeys, gMain.heldKeys); } +#define TINT_NIGHT Q_8_8(0.456) | Q_8_8(0.456) << 8 | Q_8_8(0.615) << 16 + +const struct BlendSettings gTimeOfDayBlend[] = +{ + [TIME_MORNING] = {.coeff = 4, .blendColor = 0xA8B0E0, .isTint = TRUE}, + [TIME_DAY] = {.coeff = 0, .blendColor = 0, .isTint = FALSE}, + [TIME_EVENING] = {.coeff = 4, .blendColor = 0xA8B0E0, .isTint = TRUE}, + [TIME_NIGHT] = {.coeff = 10, .blendColor = TINT_NIGHT, .isTint = TRUE}, +}; + +#define DEFAULT_WEIGHT 256 +#define TIME_BLEND_WEIGHT(begin, end) (DEFAULT_WEIGHT - (DEFAULT_WEIGHT * ((hours - begin) * MINUTES_PER_HOUR + minutes) / ((end - begin) * MINUTES_PER_HOUR))) + +#define MORNING_HOUR_MIDDLE (MORNING_HOUR_BEGIN + ((MORNING_HOUR_END - MORNING_HOUR_BEGIN) / 2)) + +void UpdateTimeOfDay(void) +{ + if (!OW_ENABLE_DNS) + return; + s32 hours, minutes; + RtcCalcLocalTime(); + hours = sHoursOverride ? sHoursOverride : gLocalTime.hours; + minutes = sHoursOverride ? 0 : gLocalTime.minutes; + + if (IsBetweenHours(hours, MORNING_HOUR_BEGIN, MORNING_HOUR_MIDDLE)) // night->morning + { + gTimeBlend.startBlend = gTimeOfDayBlend[TIME_NIGHT]; + gTimeBlend.endBlend = gTimeOfDayBlend[TIME_MORNING]; + gTimeBlend.weight = TIME_BLEND_WEIGHT(MORNING_HOUR_BEGIN, MORNING_HOUR_MIDDLE); + gTimeBlend.altWeight = (DEFAULT_WEIGHT - gTimeBlend.weight) / 2; + gTimeOfDay = TIME_MORNING; + } + else if (IsBetweenHours(hours, MORNING_HOUR_MIDDLE, MORNING_HOUR_END)) // morning->day + { + gTimeBlend.startBlend = gTimeOfDayBlend[TIME_MORNING]; + gTimeBlend.endBlend = gTimeOfDayBlend[TIME_DAY]; + gTimeBlend.weight = TIME_BLEND_WEIGHT(MORNING_HOUR_MIDDLE, MORNING_HOUR_END); + gTimeBlend.altWeight = (DEFAULT_WEIGHT - gTimeBlend.weight) / 2 + (DEFAULT_WEIGHT / 2); + gTimeOfDay = TIME_MORNING; + } + else if (IsBetweenHours(hours, EVENING_HOUR_BEGIN, EVENING_HOUR_END)) // evening + { + gTimeBlend.startBlend = gTimeOfDayBlend[TIME_DAY]; + gTimeBlend.endBlend = gTimeOfDayBlend[TIME_EVENING]; + gTimeBlend.weight = TIME_BLEND_WEIGHT(EVENING_HOUR_BEGIN, EVENING_HOUR_END); + gTimeBlend.altWeight = gTimeBlend.weight / 2 + (DEFAULT_WEIGHT / 2); + gTimeOfDay = TIME_EVENING; + } + else if (IsBetweenHours(hours, NIGHT_HOUR_BEGIN, NIGHT_HOUR_BEGIN + 1)) // evening->night + { + gTimeBlend.startBlend = gTimeOfDayBlend[TIME_EVENING]; + gTimeBlend.endBlend = gTimeOfDayBlend[TIME_NIGHT]; + gTimeBlend.weight = TIME_BLEND_WEIGHT(NIGHT_HOUR_BEGIN, NIGHT_HOUR_BEGIN + 1); + gTimeBlend.altWeight = gTimeBlend.weight / 2; + gTimeOfDay = TIME_NIGHT; + } + else if (IsBetweenHours(hours, NIGHT_HOUR_BEGIN, NIGHT_HOUR_END)) // night + { + gTimeBlend.weight = DEFAULT_WEIGHT; + gTimeBlend.altWeight = 0; + gTimeBlend.startBlend = gTimeBlend.endBlend = gTimeOfDayBlend[TIME_NIGHT]; + gTimeOfDay = TIME_NIGHT; + } + else // day + { + gTimeBlend.weight = gTimeBlend.altWeight = DEFAULT_WEIGHT; + gTimeBlend.startBlend = gTimeBlend.endBlend = gTimeOfDayBlend[TIME_DAY]; + gTimeOfDay = TIME_DAY; + } +} + +#undef MORNING_HOUR_MIDDLE +#undef TIME_BLEND_WEIGHT +#undef DEFAULT_WEIGHT + +// Whether a map type is naturally lit/outside +bool32 MapHasNaturalLight(u8 mapType) +{ + return (mapType == MAP_TYPE_TOWN + || mapType == MAP_TYPE_CITY + || mapType == MAP_TYPE_ROUTE + || mapType == MAP_TYPE_OCEAN_ROUTE); +} + +bool32 CurrentMapHasShadows(void) +{ + // Add all conditionals here for maps that shouldn't have shadows + // By default only cave maps are excluded from having shadows under object events + return (gMapHeader.mapType != MAP_TYPE_UNDERGROUND); +} + +// Update & mix day / night bg palettes (into unfaded) +void UpdateAltBgPalettes(u16 palettes) +{ + if (!OW_ENABLE_DNS) + return; + const struct Tileset *primary = gMapHeader.mapLayout->primaryTileset; + const struct Tileset *secondary = gMapHeader.mapLayout->secondaryTileset; + u32 i = 1; + if (!MapHasNaturalLight(gMapHeader.mapType)) + return; + palettes &= ~((1 << NUM_PALS_IN_PRIMARY) - 1) | primary->swapPalettes; + palettes &= ((1 << NUM_PALS_IN_PRIMARY) - 1) | (secondary->swapPalettes << NUM_PALS_IN_PRIMARY); + palettes &= PALETTES_MAP ^ (1 << 0); // don't blend palette 0, [13,15] + palettes >>= 1; // start at palette 1 + if (!palettes) + return; + while (palettes) + { + if (palettes & 1) + { + if (i < NUM_PALS_IN_PRIMARY) + AvgPaletteWeighted(&((u16 *)primary->palettes)[i * 16], &((u16 *)primary->palettes)[((i + 9) % 16) * 16], gPlttBufferUnfaded + i * 16, gTimeBlend.altWeight); + else + AvgPaletteWeighted(&((u16 *)secondary->palettes)[i * 16], &((u16 *)secondary->palettes)[((i + 9) % 16) * 16], gPlttBufferUnfaded + i * 16, gTimeBlend.altWeight); + } + i++; + palettes >>= 1; + } +} + +void UpdatePalettesWithTime(u32 palettes) +{ + if (!OW_ENABLE_DNS || !MapHasNaturalLight(gMapHeader.mapType)) + return; + u32 i; + u32 mask = 1 << 16; + if (palettes >= (1 << 16)) + for (i = 0; i < 16; i++, mask <<= 1) + { + 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, &gTimeBlend.startBlend, &gTimeBlend.endBlend, gTimeBlend.weight); +} + +u8 UpdateSpritePaletteWithTime(u8 paletteNum) +{ + if (OW_ENABLE_DNS + && MapHasNaturalLight(gMapHeader.mapType) + && !IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(paletteNum))) + TimeMixPalettes(1, &gPlttBufferUnfaded[OBJ_PLTT_ID(paletteNum)], &gPlttBufferFaded[OBJ_PLTT_ID(paletteNum)], &gTimeBlend.startBlend, &gTimeBlend.endBlend, gTimeBlend.weight); + return paletteNum; +} + static void OverworldBasic(void) { ScriptContext_RunScript(); @@ -1546,6 +1703,23 @@ static void OverworldBasic(void) UpdatePaletteFade(); UpdateTilesetAnimations(); DoScheduledBgTilemapCopiesToVram(); + // Every minute if no palette fade is active, update TOD blending as needed + if (!gPaletteFade.active && --gTimeUpdateCounter <= 0) + { + struct TimeBlendSettings cachedBlend = gTimeBlend; + u32 *bld0 = (u32*)&cachedBlend; + u32 *bld1 = (u32*)&gTimeBlend; + gTimeUpdateCounter = (SECONDS_PER_MINUTE * 60 / FakeRtc_GetSecondsRatio()); + UpdateTimeOfDay(); + FormChangeTimeUpdate(); + if (bld0[0] != bld1[0] + || bld0[1] != bld1[1] + || bld0[2] != bld1[2]) + { + UpdateAltBgPalettes(PALETTES_BG); + UpdatePalettesWithTime(PALETTES_ALL); + } + } } // This CB2 is used when starting @@ -3283,6 +3457,8 @@ static void CreateLinkPlayerSprite(u8 linkPlayerId, u8 gameVersion) sprite->coordOffsetEnabled = TRUE; sprite->data[0] = linkPlayerId; objEvent->triggerGroundEffectsOnMove = FALSE; + objEvent->localId = OBJ_EVENT_ID_DYNAMIC_BASE + linkPlayerId; + SetUpShadow(objEvent); } } @@ -3516,3 +3692,17 @@ void ScriptHideItemDescription(struct ScriptContext *ctx) #endif // OW_SHOW_ITEM_DESCRIPTIONS +// 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; +} diff --git a/src/palette.c b/src/palette.c index f9a074d41e..2979ca9076 100644 --- a/src/palette.c +++ b/src/palette.c @@ -13,11 +13,13 @@ enum NORMAL_FADE, FAST_FADE, HARDWARE_FADE, + TIME_OF_DAY_FADE, }; static u32 UpdateNormalPaletteFade(void); static void BeginFastPaletteFadeInternal(u32); static u32 UpdateFastPaletteFade(void); +static u8 UpdateTimeOfDayPaletteFade(void); static u32 UpdateHardwarePaletteFade(void); static void UpdateBlendRegisters(void); static bool32 IsSoftwarePaletteFadeFinishing(void); @@ -53,6 +55,16 @@ void LoadPalette(const void *src, u32 offset, u32 size) CpuCopy16(src, &gPlttBufferFaded[offset], size); } +// Drop in replacement for LoadPalette, uses CpuFastCopy, size must be 0 % 32 +void LoadPaletteFast(const void *src, u32 offset, u32 size) +{ + if ((u32)src & 3) // In case palette is not 4 byte aligned + return LoadPalette(src, offset, size); + CpuFastCopy(src, &gPlttBufferUnfaded[offset], size); + // Copying from EWRAM->EWRAM is faster than ROM->EWRAM + CpuFastCopy(&gPlttBufferUnfaded[offset], &gPlttBufferFaded[offset], size); +} + void FillPalette(u32 value, u32 offset, u32 size) { CpuFill16(value, &gPlttBufferUnfaded[offset], size); @@ -83,6 +95,8 @@ u32 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(); @@ -114,9 +128,9 @@ bool32 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targ delay = 0; } - gPaletteFade_selectedPalettes = selectedPalettes; + gPaletteFadeSelectedPalettes = selectedPalettes; gPaletteFade.delayCounter = delay; - gPaletteFade_delay = delay; + gPaletteFadeDelay = delay; gPaletteFade.y = startY; gPaletteFade.targetY = targetY; gPaletteFade.blendColor = blendColor; @@ -141,6 +155,52 @@ bool32 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targ } } +// Like normal palette fade but respects sprite/tile palettes immune to time of day fading +bool32 BeginTimeOfDayPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, struct BlendSettings *bld0, struct BlendSettings *bld1, u32 weight, u32 color) +{ + u8 temp; + + if (gPaletteFade.active) + return FALSE; + + gPaletteFade.deltaY = 2; + + if (delay < 0) + { + gPaletteFade.deltaY += (delay * -1); + delay = 0; + } + + gPaletteFadeSelectedPalettes = selectedPalettes; + gPaletteFade.delayCounter = delay; + gPaletteFadeDelay = delay; + gPaletteFade.y = startY; + gPaletteFade.targetY = targetY; + gPaletteFade.active = 1; + gPaletteFade.mode = TIME_OF_DAY_FADE; + + gPaletteFade.blendColor = color; + gPaletteFade.bld0 = bld0; + gPaletteFade.bld1 = bld1; + gPaletteFade.weight = weight; + + 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; +} + void ResetPaletteFadeControl(void) { gPaletteFade.multipurpose1 = 0; @@ -161,6 +221,117 @@ void ResetPaletteFadeControl(void) gPaletteFade.deltaY = 2; } +// Like normal palette fade, but respects sprite/tile palettes immune to time of day fading +static u8 UpdateTimeOfDayPaletteFade(void) +{ + u16 paletteOffset; + u16 selectedPalettes; + u16 timePalettes = 0; // palettes passed to the time-blender + u16 copyPalettes; + u16 *src; + u16 *dst; + + 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 < gPaletteFadeDelay) + { + gPaletteFade.delayCounter++; + return PALETTE_FADE_STATUS_DELAY; + } + gPaletteFade.delayCounter = 0; + } + + paletteOffset = 0; + + if (!gPaletteFade.objPaletteToggle) + { + selectedPalettes = gPaletteFadeSelectedPalettes; + } + else + { + selectedPalettes = gPaletteFadeSelectedPalettes >> 16; + paletteOffset = 256; + } + + src = gPlttBufferUnfaded + paletteOffset; + dst = gPlttBufferFaded + paletteOffset; + + // First pply TOD blend to relevant subset of palettes + if (gPaletteFade.objPaletteToggle) // Sprite palettes, don't blend those with tags + { + u32 i; + u32 j = 1; + for (i = 0; i < 16; i++, j <<= 1) // Mask out palettes that should not be light blended + { + if ((selectedPalettes & j) && !IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(i))) + timePalettes |= j; + } + } + else // tile palettes, don't blend [13, 15] + { + timePalettes = selectedPalettes & PALETTES_MAP; + } + TimeMixPalettes(timePalettes, src, dst, gPaletteFade.bld0, gPaletteFade.bld1, gPaletteFade.weight); + + // palettes that were not blended above must be copied through + if ((copyPalettes = ~timePalettes)) + { + u16 *src1 = src; + u16 *dst1 = dst; + while (copyPalettes) + { + if (copyPalettes & 1) + CpuFastCopy(src1, dst1, 32); + copyPalettes >>= 1; + src1 += 16; + dst1 += 16; + } + } + + // Then, blend from faded->faded with native BlendPalettes + BlendPalettesFine(selectedPalettes, dst, dst, gPaletteFade.y, gPaletteFade.blendColor); + + gPaletteFade.objPaletteToggle ^= 1; + + if (!gPaletteFade.objPaletteToggle) + { + if ((gPaletteFade.yDec && gPaletteFade.y == 0) || (!gPaletteFade.yDec && gPaletteFade.y == gPaletteFade.targetY)) + { + gPaletteFadeSelectedPalettes = 0; + gPaletteFade.softwareFadeFinishing = 1; + } + else + { + s8 val; + + if (!gPaletteFade.yDec) + { + val = gPaletteFade.y + gPaletteFade.deltaY; + if (val > gPaletteFade.targetY) + val = gPaletteFade.targetY; + gPaletteFade.y = val; + } + else + { + val = gPaletteFade.y - 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 u32 UpdateNormalPaletteFade(void) { u16 paletteOffset; @@ -177,10 +348,10 @@ static u32 UpdateNormalPaletteFade(void) { if (!gPaletteFade.objPaletteToggle) { - if (gPaletteFade.delayCounter < gPaletteFade_delay) + if (gPaletteFade.delayCounter < gPaletteFadeDelay) { gPaletteFade.delayCounter++; - return 2; + return PALETTE_FADE_STATUS_DELAY; } gPaletteFade.delayCounter = 0; } @@ -189,11 +360,11 @@ static u32 UpdateNormalPaletteFade(void) if (!gPaletteFade.objPaletteToggle) { - selectedPalettes = gPaletteFade_selectedPalettes; + selectedPalettes = gPaletteFadeSelectedPalettes; } else { - selectedPalettes = gPaletteFade_selectedPalettes >> 16; + selectedPalettes = gPaletteFadeSelectedPalettes >> 16; paletteOffset = OBJ_PLTT_OFFSET; } @@ -215,7 +386,7 @@ static u32 UpdateNormalPaletteFade(void) { if (gPaletteFade.y == gPaletteFade.targetY) { - gPaletteFade_selectedPalettes = 0; + gPaletteFadeSelectedPalettes = 0; gPaletteFade.softwareFadeFinishing = TRUE; } else @@ -312,7 +483,7 @@ void BeginFastPaletteFade(u32 submode) static void BeginFastPaletteFadeInternal(u32 submode) { gPaletteFade.y = 31; - gPaletteFade_submode = submode & 0x3F; + gPaletteFadeSubmode = submode & 0x3F; gPaletteFade.active = TRUE; gPaletteFade.mode = FAST_FADE; @@ -355,7 +526,7 @@ static u32 UpdateFastPaletteFade(void) paletteOffsetEnd = OBJ_PLTT_OFFSET; } - switch (gPaletteFade_submode) + switch (gPaletteFadeSubmode) { case FAST_FADE_IN_FROM_WHITE: for (i = paletteOffsetStart; i < paletteOffsetEnd; i++) @@ -460,7 +631,7 @@ static u32 UpdateFastPaletteFade(void) if (gPaletteFade.y == 0) { - switch (gPaletteFade_submode) + switch (gPaletteFadeSubmode) { case FAST_FADE_IN_FROM_WHITE: case FAST_FADE_IN_FROM_BLACK: @@ -485,9 +656,9 @@ static u32 UpdateFastPaletteFade(void) void BeginHardwarePaletteFade(u32 blendCnt, u32 delay, u32 y, u32 targetY, u32 shouldResetBlendRegisters) { - gPaletteFade_blendCnt = blendCnt; + gPaletteFadeBlendCnt = blendCnt; gPaletteFade.delayCounter = delay; - gPaletteFade_delay = delay; + gPaletteFadeDelay = delay; gPaletteFade.y = y; gPaletteFade.targetY = targetY; gPaletteFade.active = TRUE; @@ -506,7 +677,7 @@ static u32 UpdateHardwarePaletteFade(void) if (!gPaletteFade.active) return PALETTE_FADE_STATUS_DONE; - if (gPaletteFade.delayCounter < gPaletteFade_delay) + if (gPaletteFade.delayCounter < gPaletteFadeDelay) { gPaletteFade.delayCounter++; return PALETTE_FADE_STATUS_DELAY; @@ -537,7 +708,8 @@ static u32 UpdateHardwarePaletteFade(void) { if (gPaletteFade.shouldResetBlendRegisters) { - gPaletteFade_blendCnt = 0; + // clear TGT1 + gPaletteFadeBlendCnt &= ~0xFF; gPaletteFade.y = 0; } gPaletteFade.shouldResetBlendRegisters = FALSE; @@ -548,15 +720,48 @@ static u32 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_BLDCNT, (u16)gPaletteFadeBlendCnt); SetGpuReg(REG_OFFSET_BLDY, gPaletteFade.y); + // If fade-out, also adjust BLDALPHA and DISPCNT + if (!gPaletteFade.yDec) + { + u16 bldAlpha = GetGpuReg(REG_OFFSET_BLDALPHA); + u8 tgt1 = BLDALPHA_TGT1(bldAlpha); + u8 tgt2 = BLDALPHA_TGT2(bldAlpha); + u8 bldFade; + + switch (gPaletteFadeBlendCnt & 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; gPaletteFade.mode = 0; - gPaletteFade_blendCnt = 0; + gPaletteFadeBlendCnt = 0; gPaletteFade.y = 0; gPaletteFade.active = FALSE; } @@ -585,15 +790,285 @@ static bool32 IsSoftwarePaletteFadeFinishing(void) } } +// optimized based on lucktyphlosion's BlendPalettesFine +void BlendPalettesFine(u32 palettes, u16 *src, u16 *dst, u32 coeff, u32 color) +{ + s32 newR, newG, newB; + + if (!palettes) + return; + + coeff *= 2; + newR = (color << 27) >> 27; + newG = (color << 22) >> 27; + newB = (color << 17) >> 27; + + do + { + if (palettes & 1) + { + u16 *srcEnd = src + 16; + while (src != srcEnd) // Transparency is blended (for backdrop reasons) + { + u32 srcColor = *src; + s32 r = (srcColor << 27) >> 27; + s32 g = (srcColor << 22) >> 27; + s32 b = (srcColor << 16) >> 26; + + *dst++ = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0) + | ((g + (((newG - g) * (s32)coeff) >> 5)) << 5) + | ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10); + src++; + } + } + else + { + src += 16; + dst += 16; + } + palettes >>= 1; + } while (palettes); +} + void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color) { - u16 paletteOffset; + BlendPalettesFine(selectedPalettes, gPlttBufferUnfaded, gPlttBufferFaded, coeff, color); +} - for (paletteOffset = 0; selectedPalettes; paletteOffset += 16) +#define DEFAULT_LIGHT_COLOR 0x3f9f + +// Like BlendPalette, but ignores blendColor if the transparency high bit is set +// Optimization help by lucktyphlosion +void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor) +{ + s32 newR, newG, newB, defR, defG, defB; + u16 *src = gPlttBufferUnfaded + palOffset; + u16 *dst = gPlttBufferFaded + palOffset; + u32 defaultBlendColor = DEFAULT_LIGHT_COLOR; + u16 *srcEnd = src + 16; + u32 altBlendColor = *dst++ = *src++; // color 0 is copied through unchanged + + coeff *= 2; + newR = (blendColor << 27) >> 27; + newG = (blendColor << 22) >> 27; + newB = (blendColor << 17) >> 27; + + if (altBlendColor >> 15) // Transparency high bit set; alt blend color { - if (selectedPalettes & 1) - BlendPalette(paletteOffset, 16, coeff, color); - selectedPalettes >>= 1; + defR = (altBlendColor << 27) >> 27; + defG = (altBlendColor << 22) >> 27; + defB = (altBlendColor << 17) >> 27; + } + else + { + defR = (defaultBlendColor << 27) >> 27; + defG = (defaultBlendColor << 22) >> 27; + defB = (defaultBlendColor << 17) >> 27; + altBlendColor = 0; + } + while (src != srcEnd) + { + u32 srcColor = *src; + s32 r = (srcColor << 27) >> 27; + s32 g = (srcColor << 22) >> 27; + s32 b = (srcColor << 16) >> 26; + + if (srcColor >> 15) + { + *dst = ((r + (((defR - r) * (s32)coeff) >> 5)) << 0) + | ((g + (((defG - g) * (s32)coeff) >> 5)) << 5) + | ((b + (((defB - (b & 31)) * (s32)coeff) >> 5)) << 10); + } + else // Use provided blend color + { + *dst = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0) + | ((g + (((newG - g) * (s32)coeff) >> 5)) << 5) + | ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10); + } + src++; + dst++; + } +} + +// Blends a weighted average of two blend parameters +// Parameters can be either blended (as in BlendPalettes) or tinted (as in TintPaletteRGB_Copy) +void TimeMixPalettes(u32 palettes, u16 *src, u16 *dst, struct BlendSettings *blend0, struct BlendSettings *blend1, u16 weight0) +{ + s32 r0, g0, b0, r1, g1, b1, defR, defG, defB, altR, altG, altB; + u32 color0, coeff0, color1, coeff1; + bool8 tint0, tint1; + u32 defaultColor = DEFAULT_LIGHT_COLOR; + + if (!palettes) + return; + + color0 = blend0->blendColor; + tint0 = blend0->isTint; + coeff0 = tint0 ? 16 : blend0->coeff * 2; + color1 = blend1->blendColor; + tint1 = blend1->isTint; + coeff1 = tint1 ? 16 : blend1->coeff * 2; + + if (tint0) + { + r0 = (color0 << 24) >> 24; + g0 = (color0 << 16) >> 24; + b0 = (color0 << 8) >> 24; + } + else + { + r0 = (color0 << 27) >> 27; + g0 = (color0 << 22) >> 27; + b0 = (color0 << 17) >> 27; + } + if (tint1) + { + r1 = (color1 << 24) >> 24; + g1 = (color1 << 16) >> 24; + b1 = (color1 << 8) >> 24; + } + else + { + r1 = (color1 << 27) >> 27; + g1 = (color1 << 22) >> 27; + b1 = (color1 << 17) >> 27; + } + defR = (defaultColor << 27) >> 27; + defG = (defaultColor << 22) >> 27; + defB = (defaultColor << 17) >> 27; + + do + { + if (palettes & 1) + { + u16 *srcEnd = src + 16; + u32 altBlendColor = *dst++ = *src++; // color 0 is copied through + if (altBlendColor >> 15) // Transparency high bit set; alt blend color + { + altR = (altBlendColor << 27) >> 27; + altG = (altBlendColor << 22) >> 27; + altB = (altBlendColor << 17) >> 27; + } + else + { + altBlendColor = 0; + } + while (src != srcEnd) + { + u32 srcColor = *src; + s32 r = (srcColor << 27) >> 27; + s32 g = (srcColor << 22) >> 27; + s32 b = (srcColor << 17) >> 27; + s32 r2, g2, b2; + + if (srcColor >> 15) + { + if (altBlendColor) + { + // Use alternate blend color + r2 = r + (((altR - r) * (s32)coeff1) >> 5); + g2 = g + (((altG - g) * (s32)coeff1) >> 5); + b2 = b + (((altB - b) * (s32)coeff1) >> 5); + r = r + (((altR - r) * (s32)coeff0) >> 5); + g = g + (((altG - g) * (s32)coeff0) >> 5); + b = b + (((altB - b) * (s32)coeff0) >> 5); + } + else + { + // Use default blend color + r2 = r + (((defR - r) * (s32)coeff1) >> 5); + g2 = g + (((defG - g) * (s32)coeff1) >> 5); + b2 = b + (((defB - b) * (s32)coeff1) >> 5); + r = r + (((defR - r) * (s32)coeff0) >> 5); + g = g + (((defG - g) * (s32)coeff0) >> 5); + b = b + (((defB - b) * (s32)coeff0) >> 5); + } + } + else + { + // Use provided blend colors + if (!tint1) + { + // blend-based + r2 = (r + (((r1 - r) * (s32)coeff1) >> 5)); + g2 = (g + (((g1 - g) * (s32)coeff1) >> 5)); + b2 = (b + (((b1 - b) * (s32)coeff1) >> 5)); + } + else + { + // tint-based + r2 = (u16)((r1 * r)) >> 8; + g2 = (u16)((g1 * g)) >> 8; + b2 = (u16)((b1 * b)) >> 8; + if (r2 > 31) + r2 = 31; + if (g2 > 31) + g2 = 31; + if (b2 > 31) + b2 = 31; + } + if (!tint0) + { + // blend-based + r = (r + (((r0 - r) * (s32)coeff0) >> 5)); + g = (g + (((g0 - g) * (s32)coeff0) >> 5)); + b = (b + (((b0 - b) * (s32)coeff0) >> 5)); + } + else + { + // tint-based + r = (u16)((r0 * r)) >> 8; + g = (u16)((g0 * g)) >> 8; + b = (u16)((b0 * b)) >> 8; + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + } + } + r = r2 + (((r - r2) * (s32)weight0) >> 8); + g = g2 + (((g - g2) * (s32)weight0) >> 8); + b = b2 + (((b - b2) * (s32)weight0) >> 8); + *dst++ = RGB2(r, g, b); + // *dst++ = RGB2(r, g, b) | (srcColor >> 15) << 15; + src++; + } + } + else + { + src += 16; + dst += 16; + } + palettes >>= 1; + } while (palettes); +} + +// Apply weighted average to palettes, preserving high bits of dst throughout +void AvgPaletteWeighted(u16 *src0, u16 *src1, u16 *dst, u16 weight0) +{ + u16 *srcEnd = src0 + 16; + src0++; + src1++; + dst++; // leave dst transparency unchanged + while (src0 != srcEnd) + { + u32 src0Color = *src0++; + s32 r0 = (src0Color << 27) >> 27; + s32 g0 = (src0Color << 22) >> 27; + s32 b0 = (src0Color << 17) >> 27; + u32 src1Color = *src1++; + s32 r1 = (src1Color << 27) >> 27; + s32 g1 = (src1Color << 22) >> 27; + s32 b1 = (src1Color << 17) >> 27; + + // Average and bitwise-OR + r0 = r1 + (((r0 - r1) * weight0) >> 8); + g0 = g1 + (((g0 - g1) * weight0) >> 8); + b0 = b1 + (((b0 - b1) * weight0) >> 8); + *dst = (*dst & RGB_ALPHA) | RGB2(r0, g0, b0); // preserve high bit of dst + dst++; } } @@ -696,6 +1171,79 @@ void TintPalette_CustomTone(u16 *palette, u32 count, u16 rTone, u16 gTone, u16 b } } +// Tints from Unfaded to Faded, using a 15-bit GBA color +void TintPalette_RGB_Copy(u16 palOffset, u32 blendColor) +{ + s32 newR, newG, newB, rTone = 0, gTone = 0, bTone = 0; + u16 *src = gPlttBufferUnfaded + palOffset; + u16 *dst = gPlttBufferFaded + palOffset; + u32 defaultBlendColor = DEFAULT_LIGHT_COLOR; + u16 *srcEnd = src + 16; + u16 altBlendIndices = *dst++ = *src++; // color 0 is copied through unchanged + u32 altBlendColor; + + newR = ((blendColor << 27) >> 27) << 3; + newG = ((blendColor << 22) >> 27) << 3; + newB = ((blendColor << 17) >> 27) << 3; + + if (altBlendIndices >> 15) // High bit set; bitmask of which colors to alt-blend + { + // Note that bit 0 of altBlendIndices specifies color 1 + altBlendColor = src[14]; // color 15 + if (altBlendColor >> 15) + { + // Set alternate blend color + rTone = ((altBlendColor << 27) >> 27) << 3; + gTone = ((altBlendColor << 22) >> 27) << 3; + bTone = ((altBlendColor << 17) >> 27) << 3; + } + else + { + // Set default blend color + rTone = ((defaultBlendColor << 27) >> 27) << 3; + gTone = ((defaultBlendColor << 22) >> 27) << 3; + bTone = ((defaultBlendColor << 17) >> 27) << 3; + } + } + else + { + altBlendIndices = 0; + } + + while (src != srcEnd) + { + u32 srcColor = *src; + s32 r = (srcColor << 27) >> 27; + s32 g = (srcColor << 22) >> 27; + s32 b = (srcColor << 17) >> 27; + + if (altBlendIndices & 1) + { + r = (u16)((rTone * r)) >> 8; + g = (u16)((gTone * g)) >> 8; + b = (u16)((bTone * b)) >> 8; + } + else + { + // Use provided blend color + r = (u16)((newR * r)) >> 8; + g = (u16)((newG * g)) >> 8; + b = (u16)((newB * b)) >> 8; + } + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + src++; + *dst++ = RGB2(r, g, b); + altBlendIndices >>= 1; + } +} + +#undef DEFAULT_LIGHT_COLOR + #define tCoeff data[0] #define tCoeffTarget data[1] #define tCoeffDelta data[2] diff --git a/src/palette_util.c b/src/palette_util.c index d24321d580..842d4bfdb1 100644 --- a/src/palette_util.c +++ b/src/palette_util.c @@ -2,6 +2,7 @@ #include "palette.h" #include "palette_util.h" #include "util.h" +#include "overworld.h" // "RouletteFlash" is more accurately a general flashing/fading util // this file handles fading the palettes for the color/icon selections on the Roulette wheel @@ -384,7 +385,11 @@ void UpdatePulseBlend(struct PulseBlend *pulseBlend) if (--pulseBlendPalette->delayCounter == 0xFF) { pulseBlendPalette->delayCounter = pulseBlendPalette->pulseBlendSettings.delay; - BlendPalette(pulseBlendPalette->pulseBlendSettings.paletteOffset, pulseBlendPalette->pulseBlendSettings.numColors, pulseBlendPalette->blendCoeff, pulseBlendPalette->pulseBlendSettings.blendColor); + CpuFastCopy(gPlttBufferUnfaded + pulseBlendPalette->pulseBlendSettings.paletteOffset, gPlttBufferFaded + pulseBlendPalette->pulseBlendSettings.paletteOffset, PLTT_SIZE_4BPP); + UpdatePalettesWithTime(1 << (pulseBlendPalette->pulseBlendSettings.paletteOffset >> 4)); + // pulseBlendSettings has a numColors field, but it is only ever set to 16 (for Mirage Tower) + // So, it's ok to use the fine blending here which blends the entire palette + BlendPalettesFine(1, gPlttBufferFaded + pulseBlendPalette->pulseBlendSettings.paletteOffset, gPlttBufferFaded + pulseBlendPalette->pulseBlendSettings.paletteOffset, pulseBlendPalette->blendCoeff, pulseBlendPalette->pulseBlendSettings.blendColor); switch (pulseBlendPalette->pulseBlendSettings.fadeType) { case 0: // Fade all the way to the max blend amount, then wrap around diff --git a/src/rotating_gate.c b/src/rotating_gate.c index 65c9784576..7215fb4c7b 100644 --- a/src/rotating_gate.c +++ b/src/rotating_gate.c @@ -741,7 +741,7 @@ static u8 RotatingGate_CreateGate(u8 gateId, s16 deltaX, s16 deltaY) template.tileTag = gate->shape + ROTATING_GATE_TILE_TAG; - spriteId = CreateSprite(&template, 0, 0, 0x93); + spriteId = CreateSprite(&template, 0, 0, OW_OBJECT_SUBPRIORITY - 1); // Above shadows if (spriteId == MAX_SPRITES) return MAX_SPRITES; diff --git a/src/rtc.c b/src/rtc.c index eb7272c8c0..10be64b31e 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -6,6 +6,7 @@ #include "strings.h" #include "text.h" #include "fake_rtc.h" +#include "overworld.h" // iwram bss static u16 sErrorStatus; @@ -329,14 +330,8 @@ bool8 IsBetweenHours(s32 hours, s32 begin, s32 end) enum TimeOfDay GetTimeOfDay(void) { - RtcCalcLocalTime(); - if (IsBetweenHours(gLocalTime.hours, MORNING_HOUR_BEGIN, MORNING_HOUR_END)) - return TIME_MORNING; - else if (IsBetweenHours(gLocalTime.hours, EVENING_HOUR_BEGIN, EVENING_HOUR_END)) - return TIME_EVENING; - else if (IsBetweenHours(gLocalTime.hours, NIGHT_HOUR_BEGIN, NIGHT_HOUR_END)) - return TIME_NIGHT; - return TIME_DAY; + UpdateTimeOfDay(); + return gTimeOfDay; } enum TimeOfDay GetTimeOfDayForDex(void) diff --git a/src/scrcmd.c b/src/scrcmd.c index f988bff6ea..f1511c7583 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -24,6 +24,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" @@ -795,6 +796,13 @@ static bool8 IsPaletteNotActive(void) return FALSE; } +// pauses script until palette fade inactive +bool8 ScrFunc_WaitPaletteNotActive(struct ScriptContext *ctx) +{ + SetupNativeScript(ctx, IsPaletteNotActive); + return TRUE; +} + bool8 ScrCmd_fadescreen(struct ScriptContext *ctx) { u32 mode = ScriptReadByte(ctx); @@ -818,37 +826,28 @@ bool8 ScrCmd_fadescreenspeed(struct ScriptContext *ctx) return TRUE; } -static EWRAM_DATA u32 *sPalBuffer = NULL; - bool8 ScrCmd_fadescreenswapbuffers(struct ScriptContext *ctx) { u8 mode = ScriptReadByte(ctx); + u8 nowait = ScriptReadByte(ctx); Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); switch (mode) { - case FADE_TO_BLACK: - case FADE_TO_WHITE: - default: - if (sPalBuffer == NULL) - { - sPalBuffer = Alloc(PLTT_SIZE); - CpuCopy32(gPlttBufferUnfaded, sPalBuffer, PLTT_SIZE); - FadeScreen(mode, 0); - } - break; case FADE_FROM_BLACK: case FADE_FROM_WHITE: - if (sPalBuffer != NULL) - { - CpuCopy32(sPalBuffer, gPlttBufferUnfaded, PLTT_SIZE); - FadeScreen(mode, 0); - FREE_AND_SET_NULL(sPalBuffer); - } + // 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); return TRUE; } diff --git a/src/shop.c b/src/shop.c index 72d4f1685b..42861e42c7 100644 --- a/src/shop.c +++ b/src/shop.c @@ -42,7 +42,7 @@ #include "constants/songs.h" #define TAG_SCROLL_ARROW 2100 -#define TAG_ITEM_ICON_BASE 2110 +#define TAG_ITEM_ICON_BASE 9110 // immune to time blending #define MAX_ITEMS_SHOWN 8 diff --git a/src/sprite.c b/src/sprite.c index 81e68f0052..f7f2f94c0f 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1603,9 +1603,17 @@ void LoadSpritePalettes(const struct SpritePalette *palettes) break; } +u8 LoadSpritePaletteInSlot(const struct SpritePalette *palette, u8 paletteNum) +{ + paletteNum = min(15, paletteNum); + sSpritePaletteTags[paletteNum] = palette->tag; + DoLoadSpritePalette(palette->data, paletteNum * 16); + return paletteNum; +} + void DoLoadSpritePalette(const u16 *src, u16 paletteOffset) { - LoadPalette(src, OBJ_PLTT_OFFSET + paletteOffset, PLTT_SIZE_4BPP); + LoadPaletteFast(src, OBJ_PLTT_OFFSET + paletteOffset, PLTT_SIZE_4BPP); } u32 AllocSpritePalette(u16 tag) diff --git a/src/util.c b/src/util.c index 002b2fabf0..6fb123662b 100644 --- a/src/util.c +++ b/src/util.c @@ -228,6 +228,7 @@ u32 CalcByteArraySum(const u8 *data, u32 length) void BlendPalette(u16 palOffset, u16 numEntries, u8 coeff, u32 blendColor) { u16 i; + struct PlttData *data2 = (struct PlttData *) & blendColor; for (i = 0; i < numEntries; i++) { u16 index = i + palOffset; @@ -235,7 +236,7 @@ void BlendPalette(u16 palOffset, u16 numEntries, u8 coeff, u32 blendColor) s8 r = data1->r; s8 g = data1->g; s8 b = data1->b; - struct PlttData *data2 = (struct PlttData *)&blendColor; + gPlttBufferFaded[index] = RGB(r + (((data2->r - r) * coeff) >> 4), g + (((data2->g - g) * coeff) >> 4), b + (((data2->b - b) * coeff) >> 4)); diff --git a/tools/gbagfx/gfx.c b/tools/gbagfx/gfx.c index 1dfc38e2d0..c413e4b0f0 100644 --- a/tools/gbagfx/gfx.c +++ b/tools/gbagfx/gfx.c @@ -14,6 +14,7 @@ #define GET_GBA_PAL_BLUE(x) (((x) >> 10) & 0x1F) #define SET_GBA_PAL(r, g, b) (((b) << 10) | ((g) << 5) | (r)) +#define SET_GBA_PAL_RGBA(r, g, b, a) (((a) << 15) | ((b) << 10) | ((g) << 5) | (r)) #define UPCONVERT_BIT_DEPTH(x) (((x) * 255) / 31) @@ -578,8 +579,9 @@ void WriteGbaPalette(char *path, struct Palette *palette) unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red); unsigned char green = DOWNCONVERT_BIT_DEPTH(palette->colors[i].green); unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue); + bool alpha = palette->colors[i].alpha; - uint16_t paletteEntry = SET_GBA_PAL(red, green, blue); + uint16_t paletteEntry = SET_GBA_PAL_RGBA(red, green, blue, alpha); fputc(paletteEntry & 0xFF, fp); fputc(paletteEntry >> 8, fp); diff --git a/tools/gbagfx/gfx.h b/tools/gbagfx/gfx.h index 1797d84dfd..30bbfeb8df 100644 --- a/tools/gbagfx/gfx.h +++ b/tools/gbagfx/gfx.h @@ -10,6 +10,7 @@ struct Color { unsigned char red; unsigned char green; unsigned char blue; + bool alpha; }; struct Palette { diff --git a/tools/gbagfx/jasc_pal.c b/tools/gbagfx/jasc_pal.c index 8d4bb137d5..3ae7f8b905 100644 --- a/tools/gbagfx/jasc_pal.c +++ b/tools/gbagfx/jasc_pal.c @@ -22,7 +22,7 @@ // Blue - "0 0 255\r\n" // Brown - "150 75 0\r\n" -#define MAX_LINE_LENGTH 11 +#define MAX_LINE_LENGTH 64 void ReadJascPaletteLine(FILE *fp, char *line) { @@ -156,6 +156,42 @@ void ReadJascPalette(char *path, struct Palette *palette) FATAL_ERROR("Garbage after color data.\n"); fclose(fp); + + // Try to parse alpha/high bit info for colors from auxiliary .pla file + char *dot = strrchr(path, '.'); + if (strcmp(dot, ".pal") != 0) + return; + strcpy(dot, ".pla"); // replace .pal with .pla + fp = fopen(path, "rb"); + + if (fp == NULL) + return; + + int i = 0; + // Keep reading lines until number of colors is reached or we run out + while (i < palette->numColors && fgets(line, MAX_LINE_LENGTH, fp) != NULL) + { + if (line[0] == '#') // comment line; ignore + continue; + + char *s = line; + char *end; + int colorIndex; + + if (!ParseNumber(s, &end, 10, &colorIndex)) + FATAL_ERROR("Failed to parse aux color index.\n"); + + s = end; + + if (colorIndex >= palette->numColors) + FATAL_ERROR("Aux color index %d out of bounds.\n", colorIndex); + + palette->colors[colorIndex].alpha = 1; // set alpha color + // fprintf(stderr, "Color index: %d\n", colorIndex); + i++; + } + + fclose(fp); } void WriteJascPalette(char *path, struct Palette *palette) diff --git a/tools/gbagfx/main.c b/tools/gbagfx/main.c index 98a1a1edf9..bb1691c59a 100644 --- a/tools/gbagfx/main.c +++ b/tools/gbagfx/main.c @@ -291,7 +291,7 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a void HandlePngToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { - struct Palette palette = {}; + struct Palette palette = {0}; ReadPngPalette(inputPath, &palette); WriteJascPalette(outputPath, &palette); @@ -299,7 +299,7 @@ void HandlePngToJascPaletteCommand(char *inputPath, char *outputPath, int argc U void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { - struct Palette palette = {}; + struct Palette palette = {0}; ReadPngPalette(inputPath, &palette); WriteGbaPalette(outputPath, &palette); @@ -307,7 +307,7 @@ void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UN void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { - struct Palette palette = {}; + struct Palette palette = {0}; ReadGbaPalette(inputPath, &palette); WriteJascPalette(outputPath, &palette); @@ -340,7 +340,7 @@ void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc, } } - struct Palette palette = {}; + struct Palette palette = {0}; ReadJascPalette(inputPath, &palette);