mirror of
https://github.com/resetes12/pokeemerald
synced 2025-10-05 16:02:43 +02:00
Ported heart and shiny icon indicator from HnS (Credit: dylanfalzone1)
This commit is contained in:
BIN
graphics/summary_screen/heart.png
Executable file
BIN
graphics/summary_screen/heart.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 242 B |
BIN
graphics/summary_screen/shiny_icon.png
Executable file
BIN
graphics/summary_screen/shiny_icon.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 177 B |
@@ -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 1–2 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; we’ll 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)
|
||||
|
Reference in New Issue
Block a user