From febdb7616ee781654e809b9f7fce896b70ba7174 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Wed, 22 Dec 2021 23:28:27 -0500 Subject: [PATCH 1/2] Updated README.md. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 851faed92e..c524149913 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,12 @@ There are several branches, each with additional features compared to vanilla: **romhack** branch: * [HGSS-style pokémon followers](https://bulbapedia.bulbagarden.net/wiki/Walking_Pok%C3%A9mon#Pok.C3.A9mon_HeartGold_and_SoulSilver) for all 386 pokémon, including emotes, the 28 Unown forms and a majority of follower messages. * Dynamic overworld palettes & reflections compatible with vanilla berry trees. -* HGSS-style alpha-blended shadows for object events. * A way to change a pokemon's nature while mangling its PID as little as possible. * Function to detect newer emulators/new GBA hardware. **icons** branch: * Everything from the **romhack** branch. -* All pokemon icons updated to nicer looking Gen 6 style, based on [this repo](https://github.com/msikma/pokesprite/tree/master/icons/pokemon/regular) +* All pokemon icons updated to Gen 6, based on [this repo](https://github.com/msikma/pokesprite/tree/master/icons/pokemon/regular) * This includes compatibility with the PC, trade, contests, mail, Battle Dome. Examples: ![PC](https://i.imgur.com/wzwJfd1.png) ![Party](https://i.imgur.com/8hbE88t.png) @@ -27,6 +26,7 @@ There are several branches, each with additional features compared to vanilla: * Day/night shading compatible with weather. * GSC-style window lights. * WIP interframe-blended lamp lights at night, i.e in Rustboro. +* HGSS-style alpha-blended shadows for object events. To set up the repository, see [INSTALL.md](INSTALL.md). From 58a3e55b3928019796670d6b7fa3f65b32f1b62a Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Thu, 23 Dec 2021 01:35:11 -0500 Subject: [PATCH 2/2] Fixed follower appearance in shops. --- src/event_object_movement.c | 92 ++++++++++++++++++++++++++----------- src/shop.c | 12 ++++- 2 files changed, 77 insertions(+), 27 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index ee12d3d579..8405718708 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -174,6 +174,9 @@ static u8 DoJumpSpecialSpriteMovement(struct Sprite *sprite); static void CreateLevitateMovementTask(struct ObjectEvent *); static void DestroyLevitateMovementTask(u8); static bool8 NpcTakeStep(struct Sprite *sprite); +static bool8 GetFollowerInfo(u16 *species, u8 *form, u8 *shiny); +static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool8 shiny); +static const struct ObjectEventGraphicsInfo * SpeciesToGraphicsInfo(u16 species, u8 form); static const struct SpriteFrameImage sPicTable_PechaBerryTree[]; @@ -1557,10 +1560,30 @@ u8 CreateObjectGraphicsSprite(u16 graphicsId, void (*callback)(struct Sprite *), const struct SubspriteTable *subspriteTables; struct Sprite *sprite; u8 spriteId; + u16 species; + u8 form; + bool8 shiny; + u8 paletteNum; spriteTemplate = malloc(sizeof(struct SpriteTemplate)); - CopyObjectGraphicsInfoToSpriteTemplate(graphicsId, callback, spriteTemplate, &subspriteTables); - if (spriteTemplate->paletteTag != TAG_NONE) + if (graphicsId == OBJ_EVENT_GFX_OW_MON && GetFollowerInfo(&species, &form, &shiny)) { + const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form); + spriteTemplate->tileTag = graphicsInfo->tileTag; + spriteTemplate->paletteTag = graphicsInfo->paletteTag; + spriteTemplate->oam = graphicsInfo->oam; + spriteTemplate->anims = graphicsInfo->anims; + spriteTemplate->images = graphicsInfo->images; + spriteTemplate->affineAnims = graphicsInfo->affineAnims; + spriteTemplate->callback = callback; + subspriteTables = graphicsInfo->subspriteTables; + } else + CopyObjectGraphicsInfoToSpriteTemplate(graphicsId, callback, spriteTemplate, &subspriteTables); + + if (spriteTemplate->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { + const struct CompressedSpritePalette *spritePalette = &(shiny ? gMonShinyPaletteTable : gMonPaletteTable)[species]; + paletteNum = LoadDynamicFollowerPalette(species, form, shiny); + spriteTemplate->paletteTag = spritePalette->tag; + } else if (spriteTemplate->paletteTag != TAG_NONE) LoadObjectEventPalette(spriteTemplate->paletteTag); spriteId = CreateSprite(spriteTemplate, x, y, subpriority); @@ -1661,41 +1684,61 @@ static const struct ObjectEventGraphicsInfo * SpeciesToGraphicsInfo(u16 species, return graphicsInfo->tileTag == 0xFFFF ? graphicsInfo : &gPokemonObjectGraphics[SPECIES_PORYGON]; // avoid OOB access } -// Set graphics & sprite for a follower object event by species & shininess. -static void FollowerSetGraphics(struct ObjectEvent *objectEvent, u16 species, u8 form, bool8 shiny) { - const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form); - objectEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; - ObjectEventSetGraphics(objectEvent, graphicsInfo); - objectEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; - objectEvent->extra.mon.species = species; - objectEvent->extra.mon.form = form; - objectEvent->extra.mon.shiny = shiny; - if (graphicsInfo->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { // Use palette from species palette table - struct Sprite *sprite = &gSprites[objectEvent->spriteId]; +// Find, or load, the palette for the specified pokemon info +static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool8 shiny) { + u8 paletteNum; // Note that the shiny palette tag is `species + SPECIES_SHINY_TAG`, which must be increased with more pokemon // so that palette tags do not overlap const struct CompressedSpritePalette *spritePalette = &(shiny ? gMonShinyPaletteTable : gMonPaletteTable)[species]; - // Free palette if otherwise unused - sprite->inUse = FALSE; - FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); - sprite->inUse = TRUE; - if (IndexOfSpritePaletteTag(spritePalette->tag) == 0xFF) { // Load compressed palette + if ((paletteNum = IndexOfSpritePaletteTag(spritePalette->tag)) == 0xFF) { // Load compressed palette LoadCompressedSpritePalette(spritePalette); - sprite->oam.paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present - } else - sprite->oam.paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present + paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present + } + return paletteNum; +} + +// Set graphics & sprite for a follower object event by species & shininess. +static void FollowerSetGraphics(struct ObjectEvent *objEvent, u16 species, u8 form, bool8 shiny) { + const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form); + objEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; + ObjectEventSetGraphics(objEvent, graphicsInfo); + objEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; + objEvent->extra.mon.species = species; + objEvent->extra.mon.form = form; + objEvent->extra.mon.shiny = shiny; + if (graphicsInfo->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { // Use palette from species palette table + struct Sprite *sprite = &gSprites[objEvent->spriteId]; + // Free palette if otherwise unused + sprite->inUse = FALSE; + FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); + sprite->inUse = TRUE; + sprite->oam.paletteNum = LoadDynamicFollowerPalette(species, form, shiny); } } +// Retrieve graphic information about the following pokemon, if any +static bool8 GetFollowerInfo(u16 *species, u8 *form, u8 *shiny) { + struct Pokemon *mon = GetFirstLiveMon(); + if (!mon) { + *species = SPECIES_NONE; + *form = 0; + *shiny = 0; + return FALSE; + } + *species = GetMonData(mon, MON_DATA_SPECIES); + *shiny = IsMonShiny(mon); + *form = *species == SPECIES_UNOWN ? GET_UNOWN_LETTER(mon->box.personality) : 0; + return TRUE; +} + void UpdateFollowingPokemon(void) { // Update following pokemon if any struct ObjectEvent *objEvent = GetFollowerObject(); - struct Pokemon *mon = GetFirstLiveMon(); struct Sprite *sprite; u16 species; bool8 shiny; u8 form; // Avoid spawning large (64x64) follower pokemon inside buildings - if (mon && !(gMapHeader.mapType == MAP_TYPE_INDOOR && SpeciesToGraphicsInfo(GetMonData(mon, MON_DATA_SPECIES), 0)->width == 64)) { + if (GetFollowerInfo(&species, &form, &shiny) && !(gMapHeader.mapType == MAP_TYPE_INDOOR && SpeciesToGraphicsInfo(species, 0)->width == 64)) { if (objEvent == NULL) { // Spawn follower struct ObjectEventTemplate template = { .localId = OBJ_EVENT_ID_FOLLOWER, @@ -1709,9 +1752,6 @@ void UpdateFollowingPokemon(void) { // Update following pokemon if any objEvent->invisible = TRUE; } sprite = &gSprites[objEvent->spriteId]; - species = GetMonData(mon, MON_DATA_SPECIES); - shiny = IsMonShiny(mon); - form = species == SPECIES_UNOWN ? GET_UNOWN_LETTER(mon->box.personality) : 0; // Follower appearance changed; move to player and set invisible if (species != objEvent->extra.mon.species || shiny != objEvent->extra.mon.shiny || form != objEvent->extra.mon.form) { MoveObjectEventToMapCoords(objEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); diff --git a/src/shop.c b/src/shop.c index f41ae315f1..25a998307b 100755 --- a/src/shop.c +++ b/src/shop.c @@ -34,6 +34,7 @@ #include "text_window.h" #include "tv.h" #include "constants/decorations.h" +#include "constants/event_objects.h" #include "constants/items.h" #include "constants/metatile_behaviors.h" #include "constants/rgb.h" @@ -805,7 +806,8 @@ static void BuyMenuCollectObjectEventData(void) { u8 objEventId = GetObjectEventIdByXY(facingX - 4 + x, facingY - 2 + y); - if (objEventId != OBJECT_EVENTS_COUNT) + // skip if invalid or an overworld pokemon that is not following the player + if (objEventId != OBJECT_EVENTS_COUNT && !(gObjectEvents[objEventId].active && gObjectEvents[objEventId].graphicsId == OBJ_EVENT_GFX_OW_MON && gObjectEvents[objEventId].localId != OBJ_EVENT_ID_FOLLOWER)) { sShopData->viewportObjects[r8][OBJ_EVENT_ID] = objEventId; sShopData->viewportObjects[r8][X_COORD] = x; @@ -839,7 +841,12 @@ static void BuyMenuDrawObjectEvents(void) u8 i; u8 spriteId; const struct ObjectEventGraphicsInfo *graphicsInfo; + u8 weatherTemp = gWeatherPtr->palProcessingState; + // This function runs during fadeout, so the weather palette processing state must be temporarily changed, + // so that time-blending will work properly + if (weatherTemp == WEATHER_PAL_STATE_SCREEN_FADING_OUT) + gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_IDLE; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { if (sShopData->viewportObjects[i][OBJ_EVENT_ID] == OBJECT_EVENTS_COUNT) @@ -862,6 +869,9 @@ static void BuyMenuDrawObjectEvents(void) StartSpriteAnim(&gSprites[spriteId], sShopData->viewportObjects[i][ANIM_NUM]); } + + gWeatherPtr->palProcessingState = weatherTemp; // restore weather state + CpuFastCopy(gPlttBufferFaded + 16*16, gPlttBufferUnfaded + 16*16, PLTT_BUFFER_SIZE); } static bool8 BuyMenuCheckIfObjectEventOverlapsMenuBg(s16 *object)