Ported heart and shiny icon indicator from HnS (Credit: dylanfalzone1)

This commit is contained in:
resetes12
2025-09-30 09:26:09 +02:00
parent d3a9ae35ff
commit c5d0386a16
3 changed files with 260 additions and 5 deletions

BIN
graphics/summary_screen/heart.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

View File

@@ -111,23 +111,127 @@ enum {
#define MOVE_SELECTOR_SPRITES_COUNT 10
#define TYPE_ICON_SPRITE_COUNT (MAX_MON_MOVES + 1)
#define TAG_FRIENDSHIP_ICON 30007 // pick an unused tag (30005/30006 are used in your file)
enum
{
FRIENDSHIP_LEVEL_0,
FRIENDSHIP_LEVEL_1,
FRIENDSHIP_LEVEL_2,
FRIENDSHIP_LEVEL_3,
FRIENDSHIP_LEVEL_4,
FRIENDSHIP_LEVEL_5,
FRIENDSHIP_LEVEL_6,
FRIENDSHIP_LEVEL_COUNT
};
// for the spriteIds field in PokemonSummaryScreenData
enum
{
SPRITE_ARR_ID_MON,
SPRITE_ARR_ID_BALL,
SPRITE_ARR_ID_STATUS,
SPRITE_ARR_ID_TYPE, // 2 for mon types, 5 for move types(4 moves and 1 to learn), used interchangeably, because mon types and move types aren't shown on the same screen
SPRITE_ARR_ID_MOVE_SELECTOR1 = SPRITE_ARR_ID_TYPE + TYPE_ICON_SPRITE_COUNT, // 10 sprites that make up the selector
SPRITE_ARR_ID_FRIENDSHIP, // NEW: friendship heart icon
SPRITE_ARR_ID_TYPE, // 2 for mon types, 5 for move types..., used interchangeably
SPRITE_ARR_ID_MOVE_SELECTOR1 = SPRITE_ARR_ID_TYPE + TYPE_ICON_SPRITE_COUNT,
SPRITE_ARR_ID_MOVE_SELECTOR2 = SPRITE_ARR_ID_MOVE_SELECTOR1 + MOVE_SELECTOR_SPRITES_COUNT,
SPRITE_ARR_ID_COUNT = SPRITE_ARR_ID_MOVE_SELECTOR2 + MOVE_SELECTOR_SPRITES_COUNT
};
#define TILE_EMPTY_APPEAL_HEART 0x1039
#define TILE_FILLED_APPEAL_HEART 0x103A
#define TILE_FILLED_JAM_HEART 0x103C
#define TILE_EMPTY_JAM_HEART 0x103D
// Tags — use your free slots (you told me 30006/30005 were open earlier)
#define TAG_SHINY_STAR_TILE 30006
#define TAG_SHINY_STAR_PAL 30005
static const u16 sFriendshipLevelToThreshold[FRIENDSHIP_LEVEL_COUNT] = { 0, 42, 85, 128, 170, 212, 250 };
static const u16 sFriendshipIcon_Pal[] = INCBIN_U16("graphics/summary_screen/heart.gbapal");
static const u32 sFriendshipIcon_Gfx[] = INCBIN_U32("graphics/summary_screen/heart.4bpp.lz");
static const u16 sMarkings_Pal[] = INCBIN_U16("graphics/summary_screen/markings.gbapal");
// Use your shiny_icon asset. Toolchain should emit the .4bpp.lz during build.
static const u32 sStarObjTiles[] = INCBIN_U32("graphics/summary_screen/shiny_icon.4bpp.lz");
// 8x8, 4bpp OAM (small star)
static const struct OamData sStarObjOamData =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(8x8),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(8x8),
.tileNum = 0,
.priority = 3, // ontop of mon
.paletteNum = 0,
};
static const struct OamData sOamData_FriendshipIcon =
{
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(8x8), // was 16x16
.size = SPRITE_SIZE(8x8), // was 16x16
.priority = 3, // ontop of mon
};
static const union AnimCmd sAnim_Friendship_0[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END };
static const union AnimCmd sAnim_Friendship_1[] = { ANIMCMD_FRAME(1, 0), ANIMCMD_END };
static const union AnimCmd sAnim_Friendship_2[] = { ANIMCMD_FRAME(2, 0), ANIMCMD_END };
static const union AnimCmd sAnim_Friendship_3[] = { ANIMCMD_FRAME(3, 0), ANIMCMD_END };
static const union AnimCmd sAnim_Friendship_4[] = { ANIMCMD_FRAME(4, 0), ANIMCMD_END };
static const union AnimCmd sAnim_Friendship_5[] = { ANIMCMD_FRAME(5, 0), ANIMCMD_END };
static const union AnimCmd sAnim_Friendship_6[] = { ANIMCMD_FRAME(6, 0), ANIMCMD_END };
static const union AnimCmd *const sAnimTable_FriendshipIcon[] =
{
sAnim_Friendship_0,
sAnim_Friendship_1,
sAnim_Friendship_2,
sAnim_Friendship_3,
sAnim_Friendship_4,
sAnim_Friendship_5,
sAnim_Friendship_6,
};
static const struct SpriteTemplate sSpriteTemplate_FriendshipIcon =
{
.tileTag = TAG_FRIENDSHIP_ICON,
.paletteTag = TAG_FRIENDSHIP_ICON,
.oam = &sOamData_FriendshipIcon,
.anims = sAnimTable_FriendshipIcon,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
// Single-frame anim
static const union AnimCmd sStarObjAnim0[] = {
ANIMCMD_FRAME(0, 0),
ANIMCMD_END,
};
static const union AnimCmd *const sStarObjAnimTable[] = {
sStarObjAnim0,
};
// Small holder like FR
struct ShinyStarObjData {
struct Sprite *sprite;
u16 tileTag, palTag;
};
static EWRAM_DATA struct ShinyStarObjData *sShinyStarObjData = NULL;
static EWRAM_DATA struct PokemonSummaryScreenData
{
/*0x00*/ union {
@@ -755,6 +859,14 @@ static const struct OamData sOamData_SplitIcons =
.priority = 0,
};
static const struct CompressedSpriteSheet sSpriteSheet_FriendshipIcon =
{
.data = sFriendshipIcon_Gfx,
.size = 7 * 32, // 7 tiles * 32 bytes per 8×8 4bpp tile
.tag = TAG_FRIENDSHIP_ICON
};
static const struct CompressedSpriteSheet sSpriteSheet_SplitIcons =
{
.data = sSplitIcons_Gfx,
@@ -762,6 +874,13 @@ static const struct CompressedSpriteSheet sSpriteSheet_SplitIcons =
.tag = TAG_SPLIT_ICONS,
};
static const struct SpritePalette sSpritePal_FriendshipIcon =
{
.data = sFriendshipIcon_Pal,
.tag = TAG_FRIENDSHIP_ICON
};
static const struct SpritePalette sSpritePal_SplitIcons =
{
.data = sSplitIcons_Pal,
@@ -1150,7 +1269,6 @@ static const struct SpriteTemplate sSpriteTemplate_StatusCondition =
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
static const u16 sMarkings_Pal[] = INCBIN_U16("graphics/summary_screen/markings.gbapal");
// code
static u8 ShowSplitIcon(u32 split)
@@ -1213,6 +1331,100 @@ void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex,
SetMainCallback2(CB2_InitSummaryScreen);
}
// Create (FR pattern: decompress into temp buffer, then LoadSpriteSheet/Palette)
static void CreateShinyStarObj(u16 tileTag, u16 palTag)
{
void *gfxBuffer = NULL;
if (sShinyStarObjData != NULL)
return;
sShinyStarObjData = AllocZeroed(sizeof(*sShinyStarObjData));
if (sShinyStarObjData == NULL)
return;
// One 8x8 tile (32 bytes). Keep 0x40 (FR style) to be safe if your converter emits 2 tiles.
gfxBuffer = AllocZeroed(0x20 * 2);
if (gfxBuffer == NULL)
goto fail;
LZ77UnCompWram(sStarObjTiles, gfxBuffer);
struct SpriteSheet sheet = {
.data = gfxBuffer,
.size = 0x20 * 2, // safe for 12 tiles
.tag = tileTag,
};
struct SpritePalette pal = { .data = sFriendshipIcon_Pal, .tag = palTag };
struct SpriteTemplate tmpl = {
.tileTag = tileTag,
.paletteTag = palTag,
.oam = &sStarObjOamData,
.anims = sStarObjAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
LoadSpriteSheet(&sheet);
LoadSpritePalette(&pal);
// Position: bottom-left, just below the Poké Ball (ball is at 16,136 in your file).
// Tweak a couple pixels if you want it tighter/looser.
u8 spriteId = CreateSprite(&tmpl, 68, 37, 0); //HnS Shiny star location
sShinyStarObjData->sprite = &gSprites[spriteId];
sShinyStarObjData->tileTag = tileTag;
sShinyStarObjData->palTag = palTag;
// Start hidden; well reveal if shiny.
sShinyStarObjData->sprite->invisible = TRUE;
fail:
if (gfxBuffer)
Free(gfxBuffer);
}
static void DestroyShinyStarObj(void)
{
if (sShinyStarObjData == NULL)
return;
if (sShinyStarObjData->sprite != NULL)
DestroySpriteAndFreeResources(sShinyStarObjData->sprite);
Free(sShinyStarObjData);
sShinyStarObjData = NULL;
}
// FR-style gate: only visible if current mon is shiny and not an egg
static void HideShowShinyStar(bool8 invisible)
{
if (sShinyStarObjData == NULL || sShinyStarObjData->sprite == NULL)
return;
if (IsMonShiny(&sMonSummaryScreen->currentMon) && !sMonSummaryScreen->summary.isEgg)
sShinyStarObjData->sprite->invisible = invisible;
else
sShinyStarObjData->sprite->invisible = TRUE;
// Keep it anchored bottom-left. If you later add a page with a different layout,
// you can branch here like FR and move the icon.
sShinyStarObjData->sprite->x = 68;
sShinyStarObjData->sprite->y = 37;//HnS Shiny star location
}
static void ShowShinyStarObjIfMonShiny(void)
{
if (sShinyStarObjData == NULL || sShinyStarObjData->sprite == NULL)
return;
if (IsMonShiny(&sMonSummaryScreen->currentMon) && !sMonSummaryScreen->summary.isEgg)
HideShowShinyStar(FALSE);
else
HideShowShinyStar(TRUE);
}
void ShowSelectMovePokemonSummaryScreen(struct Pokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void), u16 newMove)
{
ShowPokemonSummaryScreen(SUMMARY_MODE_SELECT_MOVE, mons, monIndex, maxMonIndex, callback);
@@ -1246,6 +1458,32 @@ static void CB2_InitSummaryScreen(void)
while (MenuHelpers_ShouldWaitForLinkRecv() != TRUE && LoadGraphics() != TRUE && MenuHelpers_IsLinkActive() != TRUE);
}
static void SetFriendshipSprite(void)
{
u8 *spriteId = &sMonSummaryScreen->spriteIds[SPRITE_ARR_ID_FRIENDSHIP];
u16 friendship = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_FRIENDSHIP);
u8 level = 0;
// Map friendship (0..255) into 6 animation levels using BW thresholds.
// 0:[0-49], 1:[50-99], 2:[100-149], 3:[150-199], 4:[200-249], 5:[250-255]
while (level + 1 < FRIENDSHIP_LEVEL_COUNT
&& friendship >= sFriendshipLevelToThreshold[level + 1])
{
level++;
}
StartSpriteAnim(&gSprites[*spriteId], level);
if (*spriteId == SPRITE_NONE)
*spriteId = CreateSprite(&sSpriteTemplate_FriendshipIcon, 68, 92, 0); //HnS Friendship heart location
StartSpriteAnim(&gSprites[*spriteId], level);
SetSpriteInvisibility(SPRITE_ARR_ID_FRIENDSHIP, FALSE);
}
static void TrySetInfoPageIcons(void)
{
SetFriendshipSprite();
}
static bool8 LoadGraphics(void)
{
switch (gMain.state)
@@ -1327,6 +1565,7 @@ static bool8 LoadGraphics(void)
break;
case 17:
sMonSummaryScreen->spriteIds[SPRITE_ARR_ID_MON] = LoadMonGfxAndSprite(&sMonSummaryScreen->currentMon, &sMonSummaryScreen->switchCounter);
ShowShinyStarObjIfMonShiny();
if (sMonSummaryScreen->spriteIds[SPRITE_ARR_ID_MON] != SPRITE_NONE)
{
sMonSummaryScreen->switchCounter = 0;
@@ -1339,6 +1578,8 @@ static bool8 LoadGraphics(void)
break;
case 19:
CreateCaughtBallSprite(&sMonSummaryScreen->currentMon);
CreateShinyStarObj(TAG_SHINY_STAR_TILE, TAG_SHINY_STAR_PAL);
ShowShinyStarObjIfMonShiny(); // add this line
gMain.state++;
break;
case 20:
@@ -1347,6 +1588,7 @@ static bool8 LoadGraphics(void)
break;
case 21:
SetTypeIcons();
TrySetInfoPageIcons();
gMain.state++;
break;
case 22:
@@ -1453,6 +1695,8 @@ static bool8 DecompressGraphics(void)
LoadCompressedPalette(gMoveTypes_Pal, OBJ_PLTT_ID(13), 3 * PLTT_SIZE_4BPP);
LoadCompressedSpriteSheet(&sSpriteSheet_SplitIcons);
LoadSpritePalette(&sSpritePal_SplitIcons);
LoadCompressedSpriteSheet(&sSpriteSheet_FriendshipIcon);
LoadSpritePalette(&sSpritePal_FriendshipIcon);
sMonSummaryScreen->switchCounter = 0;
return TRUE;
}
@@ -1609,6 +1853,7 @@ static void CloseSummaryScreen(u8 taskId)
gLastViewedMonIndex = sMonSummaryScreen->curMonIndex;
SummaryScreen_DestroyAnimDelayTask();
ResetSpriteData();
DestroyShinyStarObj();
FreeAllSpritePalettes();
StopCryAndClearCrySongs();
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100);
@@ -1771,6 +2016,8 @@ static void Task_ChangeSummaryMon(u8 taskId)
break;
case 6:
CreateCaughtBallSprite(&sMonSummaryScreen->currentMon);
CreateShinyStarObj(TAG_SHINY_STAR_TILE, TAG_SHINY_STAR_PAL);
ShowShinyStarObjIfMonShiny(); // add this line
break;
case 7:
if (sMonSummaryScreen->summary.ailment != AILMENT_NONE)
@@ -1780,14 +2027,17 @@ static void Task_ChangeSummaryMon(u8 taskId)
break;
case 8:
sMonSummaryScreen->spriteIds[SPRITE_ARR_ID_MON] = LoadMonGfxAndSprite(&sMonSummaryScreen->currentMon, &data[1]);
ShowShinyStarObjIfMonShiny();
if (sMonSummaryScreen->spriteIds[SPRITE_ARR_ID_MON] == SPRITE_NONE)
return;
gSprites[sMonSummaryScreen->spriteIds[SPRITE_ARR_ID_MON]].data[2] = 1;
ShowShinyStarObjIfMonShiny();
TryDrawExperienceProgressBar();
data[1] = 0;
break;
case 9:
SetTypeIcons();
TrySetInfoPageIcons();
break;
case 10:
PrintMonInfo();
@@ -1932,6 +2182,8 @@ static void PssScrollRightEnd(u8 taskId) // display right
DrawPagination();
PutPageWindowTilemaps(sMonSummaryScreen->currPageIndex);
SetTypeIcons();
TrySetInfoPageIcons();
ShowShinyStarObjIfMonShiny();
TryDrawExperienceProgressBar();
SwitchTaskToFollowupFunc(taskId);
}
@@ -1981,6 +2233,8 @@ static void PssScrollLeftEnd(u8 taskId) // display left
DrawPagination();
PutPageWindowTilemaps(sMonSummaryScreen->currPageIndex);
SetTypeIcons();
TrySetInfoPageIcons();
ShowShinyStarObjIfMonShiny();
TryDrawExperienceProgressBar();
SwitchTaskToFollowupFunc(taskId);
}
@@ -4025,9 +4279,8 @@ static void SetSpriteInvisibility(u8 spriteArrayId, bool8 invisible)
static void HidePageSpecificSprites(void)
{
// Keeps Pok<EFBFBD>mon, caught ball and status sprites visible.
// Keeps Pokémon, caught ball, status, and friendship sprites visible.
u8 i;
for (i = SPRITE_ARR_ID_TYPE; i < ARRAY_COUNT(sMonSummaryScreen->spriteIds); i++)
{
if (sMonSummaryScreen->spriteIds[i] != SPRITE_NONE)
@@ -4035,6 +4288,8 @@ static void HidePageSpecificSprites(void)
}
}
static void SetTypeIcons(void)
{
switch (sMonSummaryScreen->currPageIndex)