Skip to content

Make Pokeballs Usable Out Of Battle

judicornadamsfoster edited this page Aug 27, 2026 · 1 revision

original source: Lunos' pokecommunity post

From Lunos:
So, earlier, someone asked in RHH's Discord if it was possible to modify the caught ball of a certain Pokémon in the party at will.
Naturally, that's perfectly possible because the entire source code of the game is laid right before our very eyes and the code itself already does it.
Then Jaizu brought up a very neat suggestion; to make Poké Balls usable outside of battle to set the caught ball directly like that.
Honestly, that sounded very cool to me so I just went and did it.
And now that it's done, I came here to post it because why not.

I don't think there's a need for explanations. You go to the bag, try to use a Poké Ball and the magic happens. It's all pretty straightforward.

video:
https://www.youtube.com/watch?v=weJzo2Q2N3s

To implement this, you can track my GitHub repository via git remote and pull the branch oobBalls which is where I'm hosting the code:

git remote add lunos https://github.com/LOuroboros/pokeemerald
git pull lunos oobBalls

compare

And that's pretty much it.


include/item_use.h

add at end of file before #endif

void ItemUseOutOfBattle_PokeBall(u8);


include/party_menu.h

add at end of file, before #endif

void ItemUseCB_PokeBall(u8 taskId, TaskFunc task);


src/data/items.h

for this file, in each ITEM_*_BALL entry, add the following line after .type = ITEM_*_BALL - FIRST_BALL,

        .fieldUseFunc = ItemUseOutOfBattle_PokeBall,

so the entries should look like this:

header line
[ITEM_MASTER_BALL] =
{
    .name = _("MASTER BALL"),
    .itemId = ITEM_MASTER_BALL,
    .price = 0,
    .description = sMasterBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_MASTER_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_MASTER_BALL - FIRST_BALL,
},

[ITEM_ULTRA_BALL] =
{
    .name = _("ULTRA BALL"),
    .itemId = ITEM_ULTRA_BALL,
    .price = 1200,
    .description = sUltraBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_ULTRA_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_ULTRA_BALL - FIRST_BALL,
},

[ITEM_GREAT_BALL] =
{
    .name = _("GREAT BALL"),
    .itemId = ITEM_GREAT_BALL,
    .price = 600,
    .description = sGreatBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_GREAT_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_GREAT_BALL - FIRST_BALL,
},

[ITEM_POKE_BALL] =
{
    .name = _("POKé BALL"),
    .itemId = ITEM_POKE_BALL,
    .price = 200,
    .description = sPokeBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_POKE_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_POKE_BALL - FIRST_BALL,
},

[ITEM_SAFARI_BALL] =
{
    .name = _("SAFARI BALL"),
    .itemId = ITEM_SAFARI_BALL,
    .price = 0,
    .description = sSafariBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_SAFARI_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_SAFARI_BALL - FIRST_BALL,
},

[ITEM_NET_BALL] =
{
    .name = _("NET BALL"),
    .itemId = ITEM_NET_BALL,
    .price = 1000,
    .description = sNetBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_NET_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_NET_BALL - FIRST_BALL,
},

[ITEM_DIVE_BALL] =
{
    .name = _("DIVE BALL"),
    .itemId = ITEM_DIVE_BALL,
    .price = 1000,
    .description = sDiveBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_DIVE_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_DIVE_BALL - FIRST_BALL,
},

[ITEM_NEST_BALL] =
{
    .name = _("NEST BALL"),
    .itemId = ITEM_NEST_BALL,
    .price = 1000,
    .description = sNestBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_NEST_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_NEST_BALL - FIRST_BALL,
},

[ITEM_REPEAT_BALL] =
{
    .name = _("REPEAT BALL"),
    .itemId = ITEM_REPEAT_BALL,
    .price = 1000,
    .description = sRepeatBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_REPEAT_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_REPEAT_BALL - FIRST_BALL,
},

[ITEM_TIMER_BALL] =
{
    .name = _("TIMER BALL"),
    .itemId = ITEM_TIMER_BALL,
    .price = 1000,
    .description = sTimerBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_TIMER_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_TIMER_BALL - FIRST_BALL,
},

[ITEM_LUXURY_BALL] =
{
    .name = _("LUXURY BALL"),
    .itemId = ITEM_LUXURY_BALL,
    .price = 1000,
    .description = sLuxuryBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_LUXURY_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_LUXURY_BALL - FIRST_BALL,
},

[ITEM_PREMIER_BALL] =
{
    .name = _("PREMIER BALL"),
    .itemId = ITEM_PREMIER_BALL,
    .price = 200,
    .description = sPremierBallDesc,
    .pocket = POCKET_POKE_BALLS,
    .type = ITEM_PREMIER_BALL - FIRST_BALL,
    .fieldUseFunc = ItemUseOutOfBattle_PokeBall,
    .battleUsage = ITEM_B_USE_OTHER,
    .battleUseFunc = ItemUseInBattle_PokeBall,
    .secondaryId = ITEM_PREMIER_BALL - FIRST_BALL,
},

src/item_menu.c

in section static const u8 sContextMenuItems_BallsPocket[] = {

replace line ACTION_GIVE, ACTION_DUMMY,

    ACTION_USE,         ACTION_GIVE,

src/item_use.c

add to the end of file


void ItemUseOutOfBattle_PokeBall(u8 taskId)
{
    gItemUseCB = ItemUseCB_PokeBall;
    gBagMenu->newScreenCallback = CB2_ShowPartyMenuForItemUse;
    Task_FadeAndCloseBagMenu(taskId);
}


src/party_menu.c

add to end of file


void ItemUseCB_PokeBall(u8 taskId, TaskFunc task)
{
    struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId];
    u16 currBall = GetMonData(mon, MON_DATA_POKEBALL);
    u16 newBall = gSpecialVar_ItemId;
    static const u8 sText_MonBallWasChanged[] = _("{STR_VAR_1} was put in the {STR_VAR_2}.{PAUSE_UNTIL_PRESS}");

    if (currBall == newBall)
    {
        gPartyMenuUseExitCallback = FALSE;
        DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE);
        ScheduleBgCopyTilemapToVram(2);
        gTasks[taskId].func = task;
    }
    else
    {
        GetMonNickname(mon, gStringVar1);
        CopyItemName(newBall, gStringVar2);
        PlaySE(SE_SELECT);
        gPartyMenuUseExitCallback = TRUE;
        SetMonData(mon, MON_DATA_POKEBALL, &newBall);
        StringExpandPlaceholders(gStringVar4, sText_MonBallWasChanged);
        DisplayPartyMenuMessage(gStringVar4, TRUE);
        ScheduleBgCopyTilemapToVram(2);
        gTasks[taskId].func = task;
        RemoveBagItem(newBall, 1);
    }
}


Clone this wiki locally