3 Item Limits In Battle
smithk200 edited this page 2026-01-24 15:14:37 -06:00

by smithk200

In difficulty hacks, a thing I like to do is to restrict the amount of items a player can use in battle. I like to restrict item usage to 4, so that's what I'll be going with for this tutorial. However, you can change the item limit to be whatever you want, as long as you properly code it in.

include/battle.h

First, we're going to declare the gitemLimit constant. Simply add this line at the end of all the other extern u8 declarations:

extern u8 gItemLimit;

src/battle_main.c

This is where most of the code edits are going to be.

At the end of all the EWRAM_DATA declarations in the file, add this:

EWRAM_DATA u8 gItemLimit = 0;

This initializes the item limit.

Next, go to the HandleTurnActionSelectionState function. Go down about a hundred lines or so, until you reach case B_ACTION_USE_ITEM. (It's the one that contains the codeblock

if (((gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_FRONTIER_NO_PYRAMID
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_RECORDED_LINK))
&& !gTestRunnerEnabled)
// Or if currently held by Sky Drop
|| gStatuses3[battler] & STATUS3_SKY_DROPPED)

(note: if you're using base pokeemerald, it won't have Sky Drop)

Right after these two lines (or a close approximation of these two lines, it's different for every update of expansion)

BtlController_EmitChooseItem(battler, BUFFER_A, gBattleStruct->battlerPartyOrders[battler]);
MarkBattlerForControllerExec(battler);

add these lines:

if (battler == 0)
    gItemLimit++;

This increases the item limit only on the players' turn. NOTE: this has not been tested for double battles!

Next, go to HandleEndTurn_BattleWon.

Just like before, after this line

gCurrentActionFuncId = 0;

add this line.

gItemLimit = 0;

Now go to HandleEndTurn_BattleLost.

After this line

gCurrentActionFuncId = 0;

add this line.

gItemLimit = 0;

This resets the item limit to 0 after each battle, whether the player has won or lost.

src/party_menu.c

Go to ItemUseCB_Medicine.

After these lines

    if (NotUsingHPEVItemOnShedinja(mon, item) == FALSE)
    {
        cannotUse = TRUE;
    }

add this.

    if (gItemLimit > 4)
    {
        cannotUse = TRUE;
    }

Note: if you want the limit to be higher or lower, feel free to change the "4" to whatever you like.

I like to give the player a little message once they reach the item limit. So there will be a little more code to write.

Replace the entire if statement

    if (cannotUse != FALSE)
    {
        gPartyMenuUseExitCallback = FALSE;
        PlaySE(SE_SELECT);
        DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE);
        ScheduleBgCopyTilemapToVram(2);
        if (gPartyMenu.menuType == PARTY_MENU_TYPE_FIELD)
            gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
        else
            gTasks[taskId].func = task;
        return;
    }

with this one.

    if (cannotUse != FALSE)
    {
        gPartyMenuUseExitCallback = FALSE;
        PlaySE(SE_SELECT);
        if (gItemLimit > 4)
        {
            DisplayPartyMenuMessage(gText_ItemLimitHasBeenReached, TRUE);
        }
        else
            DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE);
        ScheduleBgCopyTilemapToVram(2);
        if (gPartyMenu.menuType == PARTY_MENU_TYPE_FIELD)
            gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
        else
            gTasks[taskId].func = task;
        return;
    }

Then we need to declare the string and give a message. Here's my implementation.

include/strings.h

Declare this at the end of the file:

extern const u8 gText_ItemLimitHasBeenReached[];

src/strings.c

Add this at the end of the file.

const u8 gText_ItemLimitHasBeenReached[] = _("Four items have already been used\nin battle!{PAUSE_UNTIL_PRESS}");

And that's it!

For newer versions of pokeemerald-expansion

pokeemerald-expansion changed a few things around so that the above tutorial no longer works with it. Luckily, this can still be done.

include/battle.h

First, we're going to declare the gitemLimit constant. Simply add this line at the end of all the other extern u8 declarations:

extern u8 gItemLimit;

include/constants/flags.h

Change one of the unused flags to FLAG_USED_ITEM.

src/battle_main.c

At the end of all the EWRAM_DATA declarations in the file, add this:

EWRAM_DATA u8 gItemLimit = 0;

This initializes the item limit.

Next, go to HandleEndTurn_BattleWon.

Just like before, after this line

gCurrentActionFuncId = 0;

add this line.

gItemLimit = 0;

Now go to HandleEndTurn_BattleLost.

After this line

gCurrentActionFuncId = 0;

add this line.

gItemLimit = 0;

This resets the item limit to 0 after each battle, whether the player has won or lost.

src/party_menu.c

At the top of the function, after all the "#include" statements, add this line:

#include "constants/flags.h"

Go to Task_HandleChooseMonInput (this is what I should have done in the above tutorial)

In the case A_BUTTON switch statement, you'll see a line that says HandleChooseMonSelection(taskId, slotPtr);. Replace the line with the following:

if (FlagGet(FLAG_USED_ITEM))
            {
                HandleChooseMonSelection(taskId, slotPtr);
                if (gItemLimit >= 4)
                {
                    gItemLimit = 4;
                }
                else
                    gItemLimit++;
                FlagClear(FLAG_USED_ITEM);
            }
            else
            {
                HandleChooseMonSelection(taskId, slotPtr);
            }

Note: if you want the limit to be higher or lower, feel free to change the "4" to whatever you like. (In reality this can still work without the

if (gItemLimit >= 4)
   gItemLimit = 4;
else

but I want to avoid the issue of the player spamming this to crash the game on purpose.

In the case B_BUTTON switch statement, add this line directly after HandleChooseMonCancel(taskId, slotPtr);:

FlagClear(FLAG_USED_ITEM);

I'm using a flag because it's what I like to use to track things in the game. This helps me pinpoint where exactly in the code item use is handled.

pokeemerald-expansion also wants me to change the location of where the little message is stored. If you want to include it, here's the code for that too.

Go to ItemUseCB_BattleScript.

After this if statement

    if (CannotUseItemsInBattle(gSpecialVar_ItemId, mon))
    {
        gPartyMenuUseExitCallback = FALSE;
        PlaySE(SE_SELECT);
        DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE);
        ScheduleBgCopyTilemapToVram(2);
        gTasks[taskId].func = task;
    }

add this.

        else if (gItemLimit >= 4) //can't use more than 4 items in battle
    {
        gPartyMenuUseExitCallback = FALSE;
        PlaySE(SE_SELECT);
        DisplayPartyMenuMessage(gText_ItemLimitHasBeenReached, TRUE);
        ScheduleBgCopyTilemapToVram(2);
        gTasks[taskId].func = task;
    }

In the function ChooseMonForInBattleItem, add this line at the end of the function.

FlagSet(FLAG_USED_ITEM);

Then we need to declare the string and give a message, just like before.

include/strings.h

Declare this at the end of the file:

extern const u8 gText_ItemLimitHasBeenReached[];

src/strings.c

Add this at the end of the file.

const u8 gText_ItemLimitHasBeenReached[] = _("Four items have already been used\nin battle!{PAUSE_UNTIL_PRESS}");

data/scripts/new_game.inc

Add this line somewhere in the EventScript_ResetAllMapFlags function:

clearflag FLAG_USED_ITEM