Credits: Zeturic, Deokishisu, Scyrous
Adds a MOVE option to the ITEM menu which allows you to move held items directly between Pokémon in your party, instead of having to use the bag as a middle man. Originally posted by Zeturic in this pokecommunity post. This guide also includes the mail fix by Deokishisu in this post and the pokeemerald code is much more recent.
Lastly, some changes have been made compared to the code in the pokecommunity posts:
-
When two items are swapped, the dialogue box will now display all four buffered strings (two Pokémon, two items) in just two lines of dialogue, rather than four lines. This has been tested with max length Pokémon/item names in mind.
-
After moving/swapping items, the selection cursor will now stay on the second Pokémon instead of returning to the first. This matches the behavior seen when switching Pokémon or using Softboiled in the party menu.
-
After selecting MOVE in the ITEM menu, the text in the bottom-left corner will now read ''Move {STR_VAR_2} to where?'' instead of ''Move item to where?'', with STR_VAR_2 being the item you're attempting to move. This makes it much easier to identify what item the Pokémon is currently holding.
-
BUGFIX: when
BattlePyramidChooseMonHeldItems
is called, the party menu window displays the options TAKE/TOSS/CANCEL. Since we're adding a new MOVE option and adjusting the window size, we need to ensure this specific Battle Pyramid window retains its original dimensions, otherwise there will be a blank entry at the bottom.
1. In include/strings.h
, end of the file:
+extern const u8 gMenuText_Move[];
extern const u8 gText_MoveItemWhere[];
extern const u8 gText_XsYAnd[];
extern const u8 gText_XsYWereSwapped[];
#endif // GUARD_STRINGS_H
2. In src/strings.c
, end of the file:
const u8 gText_Berry[] = _("BERRY");
const u8 gText_Berries[] = _("BERRIES");
+const u8 gMenuText_Move[] = _("MOVE");
+const u8 gText_MoveItemWhere[] = _("Move {STR_VAR_2} to where?");
+const u8 gText_XsYAnd[] = _("{STR_VAR_1}'s {STR_VAR_2} and\n");
+const u8 gText_XsYWereSwapped[] = _("{STR_VAR_1}'s {STR_VAR_2} were swapped!{PAUSE_UNTIL_PRESS}");
3. In src/party_menu.c
:
static void CursorCb_Cancel1(u8);
static void CursorCb_Item(u8);
static void CursorCb_Give(u8);
static void CursorCb_TakeItem(u8);
+static void CursorCb_MoveItem(u8);
static void CursorCb_Mail(u8);
static void CursorCb_Read(u8);
static void CursorCb_TakeMail(u8);
static void CursorCb_Cancel2(u8);
Also:
enum {
MENU_SUMMARY,
MENU_SWITCH,
MENU_CANCEL1,
MENU_ITEM,
MENU_GIVE,
MENU_TAKE_ITEM,
+ MENU_MOVE_ITEM,
MENU_MAIL,
MENU_TAKE_MAIL,
MENU_READ,
Also in DisplaySelectionWindow
:
case SELECTWINDOW_ITEM:
window = sItemGiveTakeWindowTemplate;
break;
+ case SELECTWINDOW_PYRAMID:
+ window = sItemPyramidTakeTossWindowTemplate;
+ break;
case SELECTWINDOW_MAIL:
window = sMailReadTakeWindowTemplate;
break;
In CreateSelectionWindow
:
item = GetMonData(mon, MON_DATA_HELD_ITEM);
if (item != ITEM_NONE)
{
SetPartyMonSelectionActions(gPlayerParty, gPartyMenu.slotId, GetPartyMenuActionsType(mon));
- DisplaySelectionWindow(SELECTWINDOW_ITEM);
+ DisplaySelectionWindow(SELECTWINDOW_PYRAMID);
CopyItemName(item, gStringVar2);
DisplayPartyMenuStdMessage(PARTY_MSG_ALREADY_HOLDING_ONE);
}
And in CursorCb_Cancel2
:
else
{
- DisplaySelectionWindow(SELECTWINDOW_ITEM);
+ DisplaySelectionWindow(SELECTWINDOW_PYRAMID);
CopyItemName(GetMonData(mon, MON_DATA_HELD_ITEM), gStringVar2);
DisplayPartyMenuStdMessage(PARTY_MSG_ALREADY_HOLDING_ONE);
}
At the end of the file, add the following two functions:
void CursorCb_MoveItemCallback(u8 taskId)
{
u16 item1, item2;
u8 buffer[100];
if (gPaletteFade.active || MenuHelpers_ShouldWaitForLinkRecv())
return;
switch (PartyMenuButtonHandler(&gPartyMenu.slotId2))
{
case 2: // User hit B or A while on Cancel
HandleChooseMonCancel(taskId, &gPartyMenu.slotId2);
break;
case 1: // User hit A on a Pokemon
// Pokemon can't give away items to eggs or themselves
if (GetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_IS_EGG)
|| gPartyMenu.slotId == gPartyMenu.slotId2)
{
PlaySE(SE_FAILURE);
return;
}
if(GetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_HELD_ITEM) >= ITEM_ORANGE_MAIL && GetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_HELD_ITEM) <= ITEM_RETRO_MAIL)
{
PlaySE(SE_FAILURE);
return;
}
PlaySE(SE_SELECT);
gPartyMenu.action = PARTY_ACTION_CHOOSE_MON;
// look up held items
item1 = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_HELD_ITEM);
item2 = GetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_HELD_ITEM);
// swap the held items
SetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_HELD_ITEM, &item2);
SetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_HELD_ITEM, &item1);
// update the held item icons
UpdatePartyMonHeldItemSprite(
&gPlayerParty[gPartyMenu.slotId],
&sPartyMenuBoxes[gPartyMenu.slotId]
);
UpdatePartyMonHeldItemSprite(
&gPlayerParty[gPartyMenu.slotId2],
&sPartyMenuBoxes[gPartyMenu.slotId2]
);
// create the string describing the move
if (item2 == ITEM_NONE)
{
GetMonNickname(&gPlayerParty[gPartyMenu.slotId2], gStringVar1);
CopyItemName(item1, gStringVar2);
StringExpandPlaceholders(gStringVar4, gText_PkmnWasGivenItem);
}
else
{
GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1);
CopyItemName(item1, gStringVar2);
StringExpandPlaceholders(buffer, gText_XsYAnd);
StringAppend(buffer, gText_XsYWereSwapped);
GetMonNickname(&gPlayerParty[gPartyMenu.slotId2], gStringVar1);
CopyItemName(item2, gStringVar2);
StringExpandPlaceholders(gStringVar4, buffer);
}
// display the string
DisplayPartyMenuMessage(gStringVar4, TRUE);
// update colors of selected boxes, move selection cursor to second mon
AnimatePartySlot(gPartyMenu.slotId, 0);
gPartyMenu.slotId = gPartyMenu.slotId2;
AnimatePartySlot(gPartyMenu.slotId2, 1);
// return to the main party menu
ScheduleBgCopyTilemapToVram(2);
gTasks[taskId].func = Task_UpdateHeldItemSprite;
break;
}
}
void CursorCb_MoveItem(u8 taskId)
{
struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId];
u16 item = GetMonData(mon, MON_DATA_HELD_ITEM);
PlaySE(SE_SELECT);
// delete old windows
PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]);
PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]);
if (GetMonData(mon, MON_DATA_HELD_ITEM) != ITEM_NONE)
{
gPartyMenu.action = PARTY_ACTION_SWITCH;
// show "Move {STR_VAR_2} to where?" in bottom left
CopyItemName(item, gStringVar2);
DisplayPartyMenuStdMessage(PARTY_MSG_MOVE_ITEM_WHERE);
// update color of first selected box
AnimatePartySlot(gPartyMenu.slotId, 1);
// set up callback
gPartyMenu.slotId2 = gPartyMenu.slotId;
gTasks[taskId].func = CursorCb_MoveItemCallback;
}
else
{
// create and display string about lack of hold item
GetMonNickname(mon, gStringVar1);
StringExpandPlaceholders(gStringVar4, gText_PkmnNotHolding);
DisplayPartyMenuMessage(gStringVar4, TRUE);
// return to the main party menu
ScheduleBgCopyTilemapToVram(2);
gTasks[taskId].func = Task_UpdateHeldItemSprite;
}
}
4. In include/constants/party_menu.h
:
#define PARTY_MSG_BOOST_PP_WHICH_MOVE 23
#define PARTY_MSG_DO_WHAT_WITH_ITEM 24
#define PARTY_MSG_DO_WHAT_WITH_MAIL 25
#define PARTY_MSG_ALREADY_HOLDING_ONE 26
+#define PARTY_MSG_MOVE_ITEM_WHERE 27
#define PARTY_MSG_NONE 127
Also:
#define SELECTWINDOW_ACTIONS 0
#define SELECTWINDOW_ITEM 1
#define SELECTWINDOW_MAIL 2
#define SELECTWINDOW_MOVES 3
+#define SELECTWINDOW_PYRAMID 4
5. In src/data/party_menu.h
:
struct
{
const u8 *text;
TaskFunc func;
} static const sCursorOptions[] =
{
[MENU_SUMMARY] = {gText_Summary5, CursorCb_Summary},
[MENU_SWITCH] = {gText_Switch2, CursorCb_Switch},
[MENU_CANCEL1] = {gText_Cancel2, CursorCb_Cancel1},
[MENU_ITEM] = {gText_Item, CursorCb_Item},
[MENU_GIVE] = {gMenuText_Give, CursorCb_Give},
[MENU_TAKE_ITEM] = {gText_Take, CursorCb_TakeItem},
+ [MENU_MOVE_ITEM] = {gMenuText_Move, CursorCb_MoveItem},
[MENU_MAIL] = {gText_Mail, CursorCb_Mail},
[MENU_TAKE_MAIL] = {gText_Take2, CursorCb_TakeMail},
In const sActionStringTable[]
:
[PARTY_MSG_DO_WHAT_WITH_ITEM] = gText_DoWhatWithItem,
[PARTY_MSG_DO_WHAT_WITH_MAIL] = gText_DoWhatWithMail,
[PARTY_MSG_ALREADY_HOLDING_ONE] = gText_AlreadyHoldingOne,
+ [PARTY_MSG_MOVE_ITEM_WHERE] = gText_MoveItemWhere,
And also:
static const u8 sPartyMenuAction_NoEntrySummaryCancel[] = {MENU_NO_ENTRY, MENU_SUMMARY, MENU_CANCEL1};
static const u8 sPartyMenuAction_StoreSummaryCancel[] = {MENU_STORE, MENU_SUMMARY, MENU_CANCEL1};
-static const u8 sPartyMenuAction_GiveTakeItemCancel[] = {MENU_GIVE, MENU_TAKE_ITEM, MENU_CANCEL2};
+static const u8 sPartyMenuAction_GiveTakeItemCancel[] = {MENU_GIVE, MENU_TAKE_ITEM, MENU_MOVE_ITEM, MENU_CANCEL2};
static const u8 sPartyMenuAction_ReadTakeMailCancel[] = {MENU_READ, MENU_TAKE_MAIL, MENU_CANCEL2};
static const u8 sPartyMenuAction_RegisterSummaryCancel[] = {MENU_REGISTER, MENU_SUMMARY, MENU_CANCEL1};
And finally:
static const struct WindowTemplate sItemGiveTakeWindowTemplate =
{
.bg = 2,
.tilemapLeft = 23,
- .tilemapTop = 13,
+ .tilemapTop = 11,
.width = 6,
- .height = 6,
+ .height = 8,
.paletteNum = 14,
.baseBlock = 0x39D,
};
+static const struct WindowTemplate sItemPyramidTakeTossWindowTemplate =
+{
+ .bg = 2,
+ .tilemapLeft = 23,
+ .tilemapTop = 13,
+ .width = 6,
+ .height = 6,
+ .paletteNum = 14,
+ .baseBlock = 0x39D,
+};
That is all!