Merge branch 'romhack' into lighting

This commit is contained in:
Ariel A 2021-12-23 04:20:38 -05:00
commit ced9fcd509
3 changed files with 81 additions and 32 deletions

View file

@ -9,13 +9,12 @@ There are several branches, each with additional features compared to vanilla:
**romhack** branch: **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. * [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. * 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. * A way to change a pokemon's nature while mangling its PID as little as possible.
* Function to detect newer emulators/new GBA hardware. * Function to detect newer emulators/new GBA hardware.
**icons** branch: **icons** branch:
* Everything from the **romhack** 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: * This includes compatibility with the PC, trade, contests, mail, Battle Dome. Examples:
![PC](https://i.imgur.com/wzwJfd1.png) ![PC](https://i.imgur.com/wzwJfd1.png)
![Party](https://i.imgur.com/8hbE88t.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. * Day/night shading compatible with weather.
* GSC-style window lights. * GSC-style window lights.
* WIP interframe-blended lamp lights at night, i.e in Rustboro. * 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). To set up the repository, see [INSTALL.md](INSTALL.md).

View file

@ -173,6 +173,9 @@ static u8 DoJumpSpecialSpriteMovement(struct Sprite *sprite);
static void CreateLevitateMovementTask(struct ObjectEvent *); static void CreateLevitateMovementTask(struct ObjectEvent *);
static void DestroyLevitateMovementTask(u8); static void DestroyLevitateMovementTask(u8);
static bool8 NpcTakeStep(struct Sprite *sprite); 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[]; static const struct SpriteFrameImage sPicTable_PechaBerryTree[];
@ -1558,10 +1561,30 @@ u8 CreateObjectGraphicsSprite(u16 graphicsId, void (*callback)(struct Sprite *),
const struct SubspriteTable *subspriteTables; const struct SubspriteTable *subspriteTables;
struct Sprite *sprite; struct Sprite *sprite;
u8 spriteId; u8 spriteId;
u16 species;
u8 form;
bool8 shiny;
u8 paletteNum;
spriteTemplate = malloc(sizeof(struct SpriteTemplate)); spriteTemplate = malloc(sizeof(struct SpriteTemplate));
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); CopyObjectGraphicsInfoToSpriteTemplate(graphicsId, callback, spriteTemplate, &subspriteTables);
if (spriteTemplate->paletteTag != TAG_NONE)
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); LoadObjectEventPalette(spriteTemplate->paletteTag);
spriteId = CreateSprite(spriteTemplate, x, y, subpriority); spriteId = CreateSprite(spriteTemplate, x, y, subpriority);
@ -1662,6 +1685,27 @@ static const struct ObjectEventGraphicsInfo * SpeciesToGraphicsInfo(u16 species,
return graphicsInfo->tileTag == 0xFFFF ? graphicsInfo : &gPokemonObjectGraphics[SPECIES_PORYGON]; // avoid OOB access return graphicsInfo->tileTag == 0xFFFF ? graphicsInfo : &gPokemonObjectGraphics[SPECIES_PORYGON]; // avoid OOB access
} }
// 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];
if ((paletteNum = IndexOfSpritePaletteTag(spritePalette->tag)) == 0xFF) { // Load compressed palette
LoadCompressedSpritePalette(spritePalette);
paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present
if (species == SPECIES_AMPHAROS) { // palette should be light-blended TODO: Add more glowing pokemon
// CHARIZARD LINE ? CHINCHOU LANTERN FLAAFY MAREEP UMBREON VOLBEAT ?
u16 * palette = &gPlttBufferUnfaded[(paletteNum+16)*16];
palette[0] |= 0x8000;
if (palette[0] & 0x4000) // If color 15 is blended, use it as the alternate color
palette[15] |= 0x8000;
}
UpdateSpritePaletteWithWeather(paletteNum, FALSE);
}
return paletteNum;
}
// Set graphics & sprite for a follower object event by species & shininess. // Set graphics & sprite for a follower object event by species & shininess.
static void FollowerSetGraphics(struct ObjectEvent *objEvent, u16 species, u8 form, bool8 shiny, bool8 doPalette) { static void FollowerSetGraphics(struct ObjectEvent *objEvent, u16 species, u8 form, bool8 shiny, bool8 doPalette) {
const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form); const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form);
@ -1672,40 +1716,38 @@ static void FollowerSetGraphics(struct ObjectEvent *objEvent, u16 species, u8 fo
objEvent->extra.mon.form = form; objEvent->extra.mon.form = form;
objEvent->extra.mon.shiny = shiny; objEvent->extra.mon.shiny = shiny;
if (graphicsInfo->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC && doPalette) { // Use palette from species palette table if (graphicsInfo->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC && doPalette) { // Use palette from species palette table
u8 paletteNum;
struct Sprite *sprite = &gSprites[objEvent->spriteId]; struct Sprite *sprite = &gSprites[objEvent->spriteId];
// 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 // Free palette if otherwise unused
sprite->inUse = FALSE; sprite->inUse = FALSE;
FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum);
sprite->inUse = TRUE; sprite->inUse = TRUE;
if ((paletteNum = IndexOfSpritePaletteTag(spritePalette->tag)) == 0xFF) { // Load compressed palette sprite->oam.paletteNum = LoadDynamicFollowerPalette(species, form, shiny);
LoadCompressedSpritePalette(spritePalette);
sprite->oam.paletteNum = paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present
if (species == SPECIES_AMPHAROS) { // palette should be light-blended TODO: Add more glowing pokemon
// CHARIZARD LINE ? CHINCHOU LANTERN FLAAFY MAREEP UMBREON VOLBEAT ?
u16 * palette = &gPlttBufferUnfaded[(paletteNum+16)*16];
palette[0] |= 0x8000;
if (palette[0] & 0x4000) // If color 15 is blended, use it as the alternate color
palette[15] |= 0x8000;
} }
UpdateSpritePaletteWithWeather(paletteNum, FALSE);
} else
sprite->oam.paletteNum = paletteNum;
} }
// 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 void UpdateFollowingPokemon(void) { // Update following pokemon if any
struct ObjectEvent *objEvent = GetFollowerObject(); struct ObjectEvent *objEvent = GetFollowerObject();
struct Pokemon *mon = GetFirstLiveMon();
struct Sprite *sprite; struct Sprite *sprite;
u16 species; u16 species;
bool8 shiny; bool8 shiny;
u8 form; u8 form;
// Avoid spawning large (64x64) follower pokemon inside buildings // 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 if (objEvent == NULL) { // Spawn follower
struct ObjectEventTemplate template = { struct ObjectEventTemplate template = {
.localId = OBJ_EVENT_ID_FOLLOWER, .localId = OBJ_EVENT_ID_FOLLOWER,
@ -1719,9 +1761,6 @@ void UpdateFollowingPokemon(void) { // Update following pokemon if any
objEvent->invisible = TRUE; objEvent->invisible = TRUE;
} }
sprite = &gSprites[objEvent->spriteId]; 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 // 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) { 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); MoveObjectEventToMapCoords(objEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y);

View file

@ -34,6 +34,7 @@
#include "text_window.h" #include "text_window.h"
#include "tv.h" #include "tv.h"
#include "constants/decorations.h" #include "constants/decorations.h"
#include "constants/event_objects.h"
#include "constants/items.h" #include "constants/items.h"
#include "constants/metatile_behaviors.h" #include "constants/metatile_behaviors.h"
#include "constants/rgb.h" #include "constants/rgb.h"
@ -805,7 +806,8 @@ static void BuyMenuCollectObjectEventData(void)
{ {
u8 objEventId = GetObjectEventIdByXY(facingX - 4 + x, facingY - 2 + y); 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][OBJ_EVENT_ID] = objEventId;
sShopData->viewportObjects[r8][X_COORD] = x; sShopData->viewportObjects[r8][X_COORD] = x;
@ -839,7 +841,12 @@ static void BuyMenuDrawObjectEvents(void)
u8 i; u8 i;
u8 spriteId; u8 spriteId;
const struct ObjectEventGraphicsInfo *graphicsInfo; 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++) for (i = 0; i < OBJECT_EVENTS_COUNT; i++)
{ {
if (sShopData->viewportObjects[i][OBJ_EVENT_ID] == OBJECT_EVENTS_COUNT) 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]); 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) static bool8 BuyMenuCheckIfObjectEventOverlapsMenuBg(s16 *object)