summaryrefslogtreecommitdiff
path: root/src/party_menu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/party_menu.c')
-rw-r--r--src/party_menu.c1787
1 files changed, 1787 insertions, 0 deletions
diff --git a/src/party_menu.c b/src/party_menu.c
index 674b329c2..73965c4ae 100644
--- a/src/party_menu.c
+++ b/src/party_menu.c
@@ -21,12 +21,14 @@
#include "fldeff.h"
#include "gpu_regs.h"
#include "graphics.h"
+#include "help_system.h"
#include "item.h"
#include "item_menu.h"
#include "item_use.h"
#include "link.h"
#include "link_rfu.h"
#include "mail.h"
+#include "mail_data.h"
#include "main.h"
#include "menu.h"
#include "menu_helpers.h"
@@ -150,6 +152,117 @@ void CursorCB_FieldMove(u8 taskId);
bool8 SetUpFieldMove_Fly(void);
bool8 SetUpFieldMove_Waterfall(void);
bool8 SetUpFieldMove_Surf(void);
+void CB2_InitPartyMenu(void);
+void ResetPartyMenu(void);
+bool8 ShowPartyMenu(void);
+void SetPartyMonsAllowedInMinigame(void);
+void ExitPartyMenu(void);
+bool8 CreatePartyMonSpritesLoop(void);
+bool8 AllocPartyMenuBg(void);
+bool8 AllocPartyMenuBgGfx(void);
+void InitPartyMenuWindows(u8 layout);
+void InitPartyMenuBoxes(u8 layout);
+void LoadPartyMenuPokeballGfx(void);
+void LoadPartyMenuAilmentGfx(void);
+bool8 RenderPartyMenuBoxes(void);
+void CreateCancelConfirmPokeballSprites(void);
+void CreateCancelConfirmWindows(bool8 chooseHalf);
+void Task_ExitPartyMenu(u8 taskId);
+void FreePartyPointers(void);
+void PartyPaletteBufferCopy(u8 offset);
+void DisplayPartyPokemonDataForMultiBattle(u8 slot);
+void DisplayPartyPokemonDataForChooseHalf(u8 slot);
+bool8 DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(u8 slot);
+void DisplayPartyPokemonData(u8 slot);
+void DisplayPartyPokemonDataForWirelessMinigame(u8 slot);
+void LoadPartyBoxPalette(struct PartyMenuBox *menuBox, u8 palFlags);
+void DrawEmptySlot(u8 windowId);
+void DisplayPartyPokemonNickname(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c);
+void DisplayPartyPokemonLevelCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c);
+void DisplayPartyPokemonGenderNidoranCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c);
+void DisplayPartyPokemonHPCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c);
+void DisplayPartyPokemonMaxHPCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c);
+void DisplayPartyPokemonHPBarCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox);
+void DisplayPartyPokemonDescriptionText(u8 stringId, struct PartyMenuBox *menuBox, u8 c);
+bool8 GetBattleEntryEligibility(struct Pokemon *mon);
+bool8 IsMonAllowedInMinigame(u8 slot);
+void DisplayPartyPokemonDataToTeachMove(u8 slot, u16 item, u8 tutor);
+u8 CanMonLearnTMTutor(struct Pokemon *mon, u16 item, u8 tutor);
+void DisplayPartyPokemonBarDetail(u8 windowId, const u8 *str, u8 color, const u8 *align);
+void DisplayPartyPokemonLevel(u8 level, struct PartyMenuBox *menuBox);
+void DisplayPartyPokemonGender(u8 gender, u16 species, u8 *nickname, struct PartyMenuBox *menuBox);
+void DisplayPartyPokemonHP(u16 hp, struct PartyMenuBox *menuBox);
+void DisplayPartyPokemonMaxHP(u16 maxhp, struct PartyMenuBox *menuBox);
+void DisplayPartyPokemonHPBar(u16 hp, u16 maxhp, struct PartyMenuBox *menuBox);
+void CreatePartyMonIconSpriteParameterized(u16 species, u32 pid, struct PartyMenuBox *menuBox, u8 priority, bool32 handleDeoxys);
+void CreatePartyMonHeldItemSpriteParameterized(u16 species, u16 item, struct PartyMenuBox *menuBox);
+void CreatePartyMonPokeballSpriteParameterized(u16 species, struct PartyMenuBox *menuBox);
+void CreatePartyMonStatusSpriteParameterized(u16 species, u8 status, struct PartyMenuBox *menuBox);
+void CreatePartyMonIconSprite(struct Pokemon *mon, struct PartyMenuBox *menuBox, u32 slot);
+void CreatePartyMonHeldItemSprite(struct Pokemon *mon, struct PartyMenuBox *menuBox);
+void CreatePartyMonPokeballSprite(struct Pokemon *mon, struct PartyMenuBox *menuBox);
+void CreatePartyMonStatusSprite(struct Pokemon *mon, struct PartyMenuBox *menuBox);
+void CreateCancelConfirmPokeballSprites(void);
+void DrawCancelConfirmButtons(void);
+u8 CreatePokeballButtonSprite(u8 x, u8 y);
+u8 CreateSmallPokeballButtonSprite(u8 x, u8 y);
+u8 GetPartyBoxPaletteFlags(u8 slot, u8 animNum);
+void AnimateSelectedPartyIcon(u8 spriteId, u8 animNum);
+void PartyMenuStartSpriteAnim(u8 spriteId, u8 animNum);
+void Task_ClosePartyMenuAndSetCB2(u8 taskId);
+void UpdatePartyToFieldOrder(void);
+s8 *GetCurrentPartySlotPtr(void);
+u16 PartyMenuButtonHandler(s8 *slotPtr);
+void HandleChooseMonSelection(u8 taskId, s8 *slotPtr);
+void HandleChooseMonCancel(u8 taskId, s8 *slotPtr);
+void MoveCursorToConfirm(void);
+bool8 IsSelectedMonNotEgg(u8 *slotPtr);
+void TryTutorSelectedMon(u8 taskId);
+void TryGiveMailToSelectedMon(u8 taskId);
+void SwitchSelectedMons(u8 taskId);
+void TryEnterMonForMinigame(u8 taskId, u8 slot);
+void Task_TryCreateSelectionWindow(u8 taskId);
+void TryGiveItemOrMailToSelectedMon(u8 taskId);
+void PartyMenuRemoveWindow(u8 *ptr);
+void CB2_SetUpExitToBattleScreen(void);
+void Task_ClosePartyMenuAfterText(u8 taskId);
+void FinishTwoMonAction(u8 taskId);
+void CancelParticipationPrompt(u8 taskId);
+void DisplayCancelChooseMonYesNo(u8 taskId);
+void Task_CancelChooseMonYesNo(u8 taskId);
+void Task_HandleCancelChooseMonYesNoInput(u8 taskId);
+void PartyMenuDisplayYesNoMenu(void);
+void Task_ReturnToChooseMonAfterText(u8 taskId);
+void UpdateCurrentPartySelection(s8 *slotPtr, s8 movementDir);
+void UpdatePartySelectionSingleLayout(s8 *slotPtr, s8 movementDir);
+void UpdatePartySelectionDoubleLayout(s8 *slotPtr, s8 movementDir);
+s8 GetNewSlotDoubleLayout(s8 slotId, s8 movementDir);
+void Task_PrintAndWaitForText(u8 taskId);
+void PartyMenuPrintText(const u8 *text);
+void sub_8124B60(struct Pokemon *mon, u16 item, u16 item2);
+bool16 IsMonAllowedInPokemonJump(struct Pokemon *mon);
+bool16 IsMonAllowedInDodrioBerryPicking(struct Pokemon *mon);
+void Task_CancelParticipationYesNo(u8 taskId);
+void Task_HandleCancelParticipationYesNoInput(u8 taskId);
+void Task_TryCreateSelectionWindow(u8 taskId);
+u16 GetTutorMove(u8 tutor);
+bool8 CanLearnTutorMove(u16 species, u8 tutor);
+void sub_8120C6C(u8 taskId);
+void sub_8120CA8(u8 taskId);
+void sub_8120CD8(u8 taskId);
+void sub_8120D08(u8 taskId);
+void sub_8120D40(u8 taskId);
+void sub_8120D7C(u8 taskId);
+void sub_8120DAC(u8 taskId);
+void sub_8120DE0(u8 taskId);
+void sub_8120E1C(u8 taskId);
+void sub_8120E58(u8 taskId);
+void sub_8120EE0(u8 taskId);
+bool8 sub_8120F78(u8 taskId);
+bool8 sub_81220D4(void);
+void sub_8122084(u8 windowId, const u8 *str);
+void sub_8122110(u8 windowId);
+void CreateSelectionWindow(void);
EWRAM_DATA struct PartyMenuInternal *sPartyMenuInternal = NULL;
EWRAM_DATA struct PartyMenu gPartyMenu = {0};
@@ -170,3 +283,1677 @@ void (*gItemUseCB)(u8, TaskFunc);
#include "data/pokemon/tutor_learnsets.h"
#include "data/party_menu.h"
+
+void InitPartyMenu(u8 menuType, u8 layout, u8 partyAction, bool8 keepCursorPos, u8 messageId, TaskFunc task, MainCallback callback)
+{
+ u16 i;
+
+ ResetPartyMenu();
+ sPartyMenuInternal = Alloc(sizeof(struct PartyMenuInternal));
+ if (sPartyMenuInternal == NULL)
+ {
+ SetMainCallback2(callback);
+ }
+ else
+ {
+ gPartyMenu.menuType = menuType;
+ gPartyMenu.exitCallback = callback;
+ gPartyMenu.action = partyAction;
+ sPartyMenuInternal->messageId = messageId;
+ sPartyMenuInternal->task = task;
+ sPartyMenuInternal->exitCallback = NULL;
+ sPartyMenuInternal->lastSelectedSlot = 0;
+ if (menuType == PARTY_MENU_TYPE_CHOOSE_HALF)
+ sPartyMenuInternal->chooseHalf = TRUE;
+ else
+ sPartyMenuInternal->chooseHalf = FALSE;
+ if (layout != KEEP_PARTY_LAYOUT)
+ gPartyMenu.layout = layout;
+ for (i = 0; i < NELEMS(sPartyMenuInternal->data); ++i)
+ sPartyMenuInternal->data[i] = 0;
+ for (i = 0; i < NELEMS(sPartyMenuInternal->windowId); ++i)
+ sPartyMenuInternal->windowId[i] = 0xFF;
+ if (!keepCursorPos)
+ gPartyMenu.slotId = 0;
+ else if (gPartyMenu.slotId > PARTY_SIZE - 1 || GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_SPECIES) == SPECIES_NONE)
+ gPartyMenu.slotId = 0;
+ gTextFlags.autoScroll = FALSE;
+ CalculatePlayerPartyCount();
+ SetMainCallback2(CB2_InitPartyMenu);
+ }
+}
+
+void CB2_UpdatePartyMenu(void)
+{
+ RunTasks();
+ AnimateSprites();
+ BuildOamBuffer();
+ DoScheduledBgTilemapCopiesToVram();
+ UpdatePaletteFade();
+}
+
+void VBlankCB_PartyMenu(void)
+{
+ LoadOam();
+ ProcessSpriteCopyRequests();
+ TransferPlttBuffer();
+}
+
+void CB2_InitPartyMenu(void)
+{
+ while (TRUE)
+ {
+ if (sub_80BF748() == TRUE || ShowPartyMenu() == TRUE || MenuHelpers_LinkSomething() == TRUE)
+ break;
+ }
+}
+
+bool8 ShowPartyMenu(void)
+{
+ switch (gMain.state)
+ {
+ case 0:
+ SetVBlankHBlankCallbacksToNull();
+ ResetVramOamAndBgCntRegs();
+ ClearScheduledBgCopiesToVram();
+ ++gMain.state;
+ break;
+ case 1:
+ ScanlineEffect_Stop();
+ ++gMain.state;
+ break;
+ case 2:
+ ResetPaletteFade();
+ gPaletteFade.bufferTransferDisabled = TRUE;
+ ++gMain.state;
+ break;
+ case 3:
+ ResetSpriteData();
+ ++gMain.state;
+ break;
+ case 4:
+ FreeAllSpritePalettes();
+ ++gMain.state;
+ break;
+ case 5:
+ if (!MenuHelpers_LinkSomething())
+ ResetTasks();
+ ++gMain.state;
+ break;
+ case 6:
+ SetPartyMonsAllowedInMinigame();
+ ++gMain.state;
+ break;
+ case 7:
+ if (!AllocPartyMenuBg())
+ {
+ ExitPartyMenu();
+ return TRUE;
+ }
+ else
+ {
+ sPartyMenuInternal->data[0] = 0;
+ ++gMain.state;
+ }
+ break;
+ case 8:
+ if (AllocPartyMenuBgGfx())
+ ++gMain.state;
+ break;
+ case 9:
+ InitPartyMenuWindows(gPartyMenu.layout);
+ ++gMain.state;
+ break;
+ case 10:
+ InitPartyMenuBoxes(gPartyMenu.layout);
+ sPartyMenuInternal->data[0] = 0;
+ ++gMain.state;
+ break;
+ case 11:
+ LoadHeldItemIcons();
+ ++gMain.state;
+ break;
+ case 12:
+ LoadPartyMenuPokeballGfx();
+ ++gMain.state;
+ break;
+ case 13:
+ LoadPartyMenuAilmentGfx();
+ ++gMain.state;
+ break;
+ case 14:
+ LoadMonIconPalettes();
+ ++gMain.state;
+ break;
+ case 15:
+ if (CreatePartyMonSpritesLoop())
+ {
+ sPartyMenuInternal->data[0] = 0;
+ ++gMain.state;
+ }
+ break;
+ case 16:
+ if (RenderPartyMenuBoxes())
+ {
+ sPartyMenuInternal->data[0] = 0;
+ ++gMain.state;
+ }
+ break;
+ case 17:
+ CreateCancelConfirmPokeballSprites();
+ ++gMain.state;
+ break;
+ case 18:
+ CreateCancelConfirmWindows(sPartyMenuInternal->chooseHalf);
+ ++gMain.state;
+ break;
+ case 19:
+ HelpSystem_SetSomeVariable2(5);
+ ++gMain.state;
+ break;
+ case 20:
+ CreateTask(sPartyMenuInternal->task, 0);
+ DisplayPartyMenuStdMessage(sPartyMenuInternal->messageId);
+ ++gMain.state;
+ break;
+ case 21:
+ BlendPalettes(0xFFFFFFFF, 16, RGB_BLACK);
+ ++gMain.state;
+ break;
+ case 22:
+ BeginNormalPaletteFade(0xFFFFFFFF, -2, 16, 0, RGB_BLACK);
+ gPaletteFade.bufferTransferDisabled = FALSE;
+ ++gMain.state;
+ break;
+ default:
+ SetVBlankCallback(VBlankCB_PartyMenu);
+ SetMainCallback2(CB2_UpdatePartyMenu);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void ExitPartyMenu(void)
+{
+ BeginNormalPaletteFade(0xFFFFFFFF, -2, 0, 16, RGB_BLACK);
+ CreateTask(Task_ExitPartyMenu, 0);
+ SetVBlankCallback(VBlankCB_PartyMenu);
+ SetMainCallback2(CB2_UpdatePartyMenu);
+}
+
+void Task_ExitPartyMenu(u8 taskId)
+{
+ if (!gPaletteFade.active)
+ {
+ SetMainCallback2(gPartyMenu.exitCallback);
+ FreePartyPointers();
+ DestroyTask(taskId);
+ }
+}
+
+void ResetPartyMenu(void)
+{
+ sPartyMenuInternal = NULL;
+ sPartyBgTilemapBuffer = NULL;
+ sPartyMenuBoxes = NULL;
+ sPartyBgGfxTilemap = NULL;
+}
+
+bool8 AllocPartyMenuBg(void)
+{
+ ResetAllBgsCoordinatesAndBgCntRegs();
+ sPartyBgTilemapBuffer = Alloc(0x800);
+ if (sPartyBgTilemapBuffer == NULL)
+ return FALSE;
+ memset(sPartyBgTilemapBuffer, 0, 0x800);
+ ResetBgsAndClearDma3BusyFlags(0);
+ InitBgsFromTemplates(0, sPartyMenuBgTemplates, NELEMS(sPartyMenuBgTemplates));
+ SetBgTilemapBuffer(1, sPartyBgTilemapBuffer);
+ ScheduleBgCopyTilemapToVram(1);
+ SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
+ SetGpuReg(REG_OFFSET_BLDCNT, 0);
+ ShowBg(0);
+ ShowBg(1);
+ ShowBg(2);
+ return TRUE;
+}
+
+bool8 AllocPartyMenuBgGfx(void)
+{
+ u32 sizeout;
+
+ switch (sPartyMenuInternal->data[0])
+ {
+ case 0:
+ sPartyBgGfxTilemap = MallocAndDecompress(gPartyMenuBg_Gfx, &sizeout);
+ LoadBgTiles(1, sPartyBgGfxTilemap, sizeout, 0);
+ ++sPartyMenuInternal->data[0];
+ break;
+ case 1:
+ if (!IsDma3ManagerBusyWithBgCopy())
+ {
+ LZDecompressWram(gPartyMenuBg_Tilemap, sPartyBgTilemapBuffer);
+ ++sPartyMenuInternal->data[0];
+ }
+ break;
+ case 2:
+ LoadCompressedPalette(gPartyMenuBg_Pal, 0, 0x160);
+ CpuCopy16(gPlttBufferUnfaded, sPartyMenuInternal->palBuffer, 0x160);
+ ++sPartyMenuInternal->data[0];
+ break;
+ case 3:
+ PartyPaletteBufferCopy(4);
+ ++sPartyMenuInternal->data[0];
+ break;
+ case 4:
+ PartyPaletteBufferCopy(5);
+ ++sPartyMenuInternal->data[0];
+ break;
+ case 5:
+ PartyPaletteBufferCopy(6);
+ ++sPartyMenuInternal->data[0];
+ break;
+ case 6:
+ PartyPaletteBufferCopy(7);
+ ++sPartyMenuInternal->data[0];
+ break;
+ case 7:
+ PartyPaletteBufferCopy(8);
+ ++sPartyMenuInternal->data[0];
+ break;
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void PartyPaletteBufferCopy(u8 offset)
+{
+ offset *= 16;
+ CpuCopy16(&gPlttBufferUnfaded[0x30], &gPlttBufferUnfaded[offset], 32);
+ CpuCopy16(&gPlttBufferUnfaded[0x30], &gPlttBufferFaded[offset], 32);
+}
+
+void FreePartyPointers(void)
+{
+ if (sPartyMenuInternal)
+ Free(sPartyMenuInternal);
+ if (sPartyBgTilemapBuffer)
+ Free(sPartyBgTilemapBuffer);
+ if (sPartyBgGfxTilemap)
+ Free(sPartyBgGfxTilemap);
+ if (sPartyMenuBoxes)
+ Free(sPartyMenuBoxes);
+ FreeAllWindowBuffers();
+}
+
+void InitPartyMenuBoxes(u8 layout)
+{
+ u8 i;
+
+ sPartyMenuBoxes = Alloc(sizeof(struct PartyMenuBox[PARTY_SIZE]));
+ for (i = 0; i < PARTY_SIZE; ++i)
+ {
+ sPartyMenuBoxes[i].infoRects = &sPartyBoxInfoRects[PARTY_BOX_RIGHT_COLUMN];
+ sPartyMenuBoxes[i].spriteCoords = sPartyMenuSpriteCoords[layout][i];
+ sPartyMenuBoxes[i].windowId = i;
+ }
+ // The first party mon goes in the left column
+ sPartyMenuBoxes[0].infoRects = &sPartyBoxInfoRects[PARTY_BOX_LEFT_COLUMN];
+ if (layout == PARTY_LAYOUT_MULTI_SHOWCASE)
+ sPartyMenuBoxes[3].infoRects = &sPartyBoxInfoRects[PARTY_BOX_LEFT_COLUMN];
+ else if (layout != PARTY_LAYOUT_SINGLE)
+ sPartyMenuBoxes[1].infoRects = &sPartyBoxInfoRects[PARTY_BOX_LEFT_COLUMN];
+}
+
+void RenderPartyMenuBox(u8 slot)
+{
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_MULTI_SHOWCASE && slot >= MULTI_PARTY_SIZE)
+ {
+ DisplayPartyPokemonDataForMultiBattle(slot);
+ LoadPartyBoxPalette(&sPartyMenuBoxes[slot], PARTY_PAL_MULTI_ALT);
+ CopyWindowToVram(sPartyMenuBoxes[slot].windowId, 2);
+ PutWindowTilemap(sPartyMenuBoxes[slot].windowId);
+ ScheduleBgCopyTilemapToVram(2);
+ }
+ else
+ {
+ if (GetMonData(&gPlayerParty[slot], MON_DATA_SPECIES) == SPECIES_NONE)
+ {
+ DrawEmptySlot(sPartyMenuBoxes[slot].windowId);
+ CopyWindowToVram(sPartyMenuBoxes[slot].windowId, 2);
+ }
+ else
+ {
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_CHOOSE_HALF)
+ DisplayPartyPokemonDataForChooseHalf(slot);
+ else if (gPartyMenu.menuType == PARTY_MENU_TYPE_MINIGAME)
+ DisplayPartyPokemonDataForWirelessMinigame(slot);
+ else if (!DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(slot))
+ DisplayPartyPokemonData(slot);
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_MULTI_SHOWCASE)
+ AnimatePartySlot(slot, 0);
+ else if (gPartyMenu.slotId == slot)
+ AnimatePartySlot(slot, 1);
+ else
+ AnimatePartySlot(slot, 0);
+ }
+ PutWindowTilemap(sPartyMenuBoxes[slot].windowId);
+ ScheduleBgCopyTilemapToVram(0);
+ }
+}
+
+void DisplayPartyPokemonData(u8 slot)
+{
+ if (GetMonData(&gPlayerParty[slot], MON_DATA_IS_EGG))
+ {
+ sPartyMenuBoxes[slot].infoRects->blitFunc(sPartyMenuBoxes[slot].windowId, 0, 0, 0, 0, TRUE);
+ DisplayPartyPokemonNickname(&gPlayerParty[slot], &sPartyMenuBoxes[slot], 0);
+ }
+ else
+ {
+ sPartyMenuBoxes[slot].infoRects->blitFunc(sPartyMenuBoxes[slot].windowId, 0, 0, 0, 0, FALSE);
+ DisplayPartyPokemonNickname(&gPlayerParty[slot], &sPartyMenuBoxes[slot], 0);
+ DisplayPartyPokemonLevelCheck(&gPlayerParty[slot], &sPartyMenuBoxes[slot], 0);
+ DisplayPartyPokemonGenderNidoranCheck(&gPlayerParty[slot], &sPartyMenuBoxes[slot], 0);
+ DisplayPartyPokemonHPCheck(&gPlayerParty[slot], &sPartyMenuBoxes[slot], 0);
+ DisplayPartyPokemonMaxHPCheck(&gPlayerParty[slot], &sPartyMenuBoxes[slot], 0);
+ DisplayPartyPokemonHPBarCheck(&gPlayerParty[slot], &sPartyMenuBoxes[slot]);
+ }
+}
+
+void DisplayPartyPokemonDescriptionData(u8 slot, u8 stringId)
+{
+ struct Pokemon *mon = &gPlayerParty[slot];
+
+ sPartyMenuBoxes[slot].infoRects->blitFunc(sPartyMenuBoxes[slot].windowId, 0, 0, 0, 0, TRUE);
+ DisplayPartyPokemonNickname(mon, &sPartyMenuBoxes[slot], 0);
+ if (!GetMonData(mon, MON_DATA_IS_EGG))
+ {
+ DisplayPartyPokemonLevelCheck(mon, &sPartyMenuBoxes[slot], 0);
+ DisplayPartyPokemonGenderNidoranCheck(mon, &sPartyMenuBoxes[slot], 0);
+ }
+ DisplayPartyPokemonDescriptionText(stringId, &sPartyMenuBoxes[slot], 0);
+}
+
+void DisplayPartyPokemonDataForChooseHalf(u8 slot)
+{
+ u8 i;
+ struct Pokemon *mon = &gPlayerParty[slot];
+ u8 *order = gSelectedOrderFromParty;
+ u8 r3;
+
+ if (!GetBattleEntryEligibility(mon))
+ {
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE);
+ }
+ else
+ {
+ if (gPartyMenu.unk_8_6 == 2)
+ r3 = 2;
+ else
+ r3 = 3;
+ for (i = 0; i < r3; ++i)
+ {
+ if (order[i] != 0 && (order[i] - 1) == slot)
+ {
+ DisplayPartyPokemonDescriptionData(slot, i + PARTYBOX_DESC_FIRST);
+ return;
+ }
+ }
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE_3);
+ }
+}
+
+void DisplayPartyPokemonDataForWirelessMinigame(u8 slot)
+{
+ if (IsMonAllowedInMinigame(slot) == TRUE)
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE);
+ else
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE);
+}
+
+// Returns TRUE if teaching move or cant evolve with item (i.e. description data is shown), FALSE otherwise
+bool8 DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(u8 slot)
+{
+ struct Pokemon *currentPokemon = &gPlayerParty[slot];
+ u16 item = gSpecialVar_ItemId;
+
+ if (gPartyMenu.action == PARTY_ACTION_MOVE_TUTOR)
+ {
+ gSpecialVar_Result = FALSE;
+ if (gSpecialVar_0x8005 > 14)
+ return FALSE;
+ DisplayPartyPokemonDataToTeachMove(slot, 0, gSpecialVar_0x8005);
+ }
+ else
+ {
+ if (gPartyMenu.action != PARTY_ACTION_USE_ITEM)
+ return FALSE;
+ switch (CheckIfItemIsTMHMOrEvolutionStone(item))
+ {
+ default:
+ return FALSE;
+ case 1: // TM/HM
+ DisplayPartyPokemonDataToTeachMove(slot, item, 0);
+ break;
+ case 2: // Evolution stone
+ if (!GetMonData(currentPokemon, MON_DATA_IS_EGG) && GetEvolutionTargetSpecies(currentPokemon, 3, item) != SPECIES_NONE)
+ return FALSE;
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NO_USE);
+ break;
+ }
+ }
+ return TRUE;
+}
+
+void DisplayPartyPokemonDataToTeachMove(u8 slot, u16 item, u8 tutor)
+{
+ switch (CanMonLearnTMTutor(&gPlayerParty[slot], item, tutor))
+ {
+ case CANNOT_LEARN_MOVE:
+ case CANNOT_LEARN_MOVE_IS_EGG:
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE_2);
+ break;
+ case ALREADY_KNOWS_MOVE:
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_LEARNED);
+ break;
+ default:
+ DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE_2);
+ break;
+ }
+}
+
+void DisplayPartyPokemonDataForMultiBattle(u8 slot)
+{
+ struct PartyMenuBox *menuBox = &sPartyMenuBoxes[slot];
+ u8 actualSlot = slot - (3);
+
+ if (gMultiPartnerParty[actualSlot].species == SPECIES_NONE)
+ {
+ DrawEmptySlot(menuBox->windowId);
+ }
+ else
+ {
+ menuBox->infoRects->blitFunc(menuBox->windowId, 0, 0, 0, 0, FALSE);
+ StringCopy(gStringVar1, gMultiPartnerParty[actualSlot].nickname);
+ StringGetEnd10(gStringVar1);
+ if (StringLength(gStringVar1) <= 5)
+ ConvertInternationalString(gStringVar1, 1);
+ DisplayPartyPokemonBarDetail(menuBox->windowId, gStringVar1, 0, menuBox->infoRects->dimensions);
+ DisplayPartyPokemonLevel(gMultiPartnerParty[actualSlot].level, menuBox);
+ DisplayPartyPokemonGender(gMultiPartnerParty[actualSlot].gender, gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].nickname, menuBox);
+ DisplayPartyPokemonHP(gMultiPartnerParty[actualSlot].hp, menuBox);
+ DisplayPartyPokemonMaxHP(gMultiPartnerParty[actualSlot].maxhp, menuBox);
+ DisplayPartyPokemonHPBar(gMultiPartnerParty[actualSlot].hp, gMultiPartnerParty[actualSlot].maxhp, menuBox);
+ }
+}
+
+bool8 RenderPartyMenuBoxes(void)
+{
+ RenderPartyMenuBox(sPartyMenuInternal->data[0]);
+ if (++sPartyMenuInternal->data[0] == PARTY_SIZE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+u8 *GetPartyMenuBgTile(u16 tileId)
+{
+ return &sPartyBgGfxTilemap[tileId << 5];
+}
+
+void CreatePartyMonSprites(u8 slot)
+{
+ u8 actualSlot;
+
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_MULTI_SHOWCASE && slot >= MULTI_PARTY_SIZE)
+ {
+ u8 status;
+
+ actualSlot = slot - MULTI_PARTY_SIZE;
+ if (gMultiPartnerParty[actualSlot].species != SPECIES_NONE)
+ {
+ CreatePartyMonIconSpriteParameterized(gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].personality, &sPartyMenuBoxes[slot], 0, FALSE);
+ CreatePartyMonHeldItemSpriteParameterized(gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].heldItem, &sPartyMenuBoxes[slot]);
+ CreatePartyMonPokeballSpriteParameterized(gMultiPartnerParty[actualSlot].species, &sPartyMenuBoxes[slot]);
+ if (gMultiPartnerParty[actualSlot].hp == 0)
+ status = AILMENT_FNT;
+ else
+ status = GetAilmentFromStatus(gMultiPartnerParty[actualSlot].status);
+ CreatePartyMonStatusSpriteParameterized(gMultiPartnerParty[actualSlot].species, status, &sPartyMenuBoxes[slot]);
+ }
+ }
+ else if (GetMonData(&gPlayerParty[slot], MON_DATA_SPECIES) != SPECIES_NONE)
+ {
+ CreatePartyMonIconSprite(&gPlayerParty[slot], &sPartyMenuBoxes[slot], slot);
+ CreatePartyMonHeldItemSprite(&gPlayerParty[slot], &sPartyMenuBoxes[slot]);
+ CreatePartyMonPokeballSprite(&gPlayerParty[slot], &sPartyMenuBoxes[slot]);
+ CreatePartyMonStatusSprite(&gPlayerParty[slot], &sPartyMenuBoxes[slot]);
+ }
+}
+
+bool8 CreatePartyMonSpritesLoop(void)
+{
+ CreatePartyMonSprites(sPartyMenuInternal->data[0]);
+ if (++sPartyMenuInternal->data[0] == PARTY_SIZE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+void CreateCancelConfirmPokeballSprites(void)
+{
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_MULTI_SHOWCASE)
+ {
+ // The showcase has no Cancel/Confirm buttons
+ FillBgTilemapBufferRect(1, 14, 23, 17, 7, 2, 1);
+ }
+ else
+ {
+ if (sPartyMenuInternal->chooseHalf)
+ {
+ sPartyMenuInternal->spriteIdConfirmPokeball = CreateSmallPokeballButtonSprite(0xBF, 0x88);
+ DrawCancelConfirmButtons();
+ sPartyMenuInternal->spriteIdCancelPokeball = CreateSmallPokeballButtonSprite(0xBF, 0x98);
+ }
+ else
+ {
+ sPartyMenuInternal->spriteIdCancelPokeball = CreatePokeballButtonSprite(198, 148);
+ }
+ AnimatePartySlot(gPartyMenu.slotId, 1);
+ }
+}
+
+void AnimatePartySlot(u8 slot, u8 animNum)
+{
+ u8 spriteId;
+
+ switch (slot)
+ {
+ default:
+ if (GetMonData(&gPlayerParty[slot], MON_DATA_SPECIES) != SPECIES_NONE)
+ {
+ LoadPartyBoxPalette(&sPartyMenuBoxes[slot], GetPartyBoxPaletteFlags(slot, animNum));
+ AnimateSelectedPartyIcon(sPartyMenuBoxes[slot].monSpriteId, animNum);
+ PartyMenuStartSpriteAnim(sPartyMenuBoxes[slot].pokeballSpriteId, animNum);
+ }
+ return;
+ case PARTY_SIZE: // Confirm
+ if (animNum == 0)
+ SetBgTilemapPalette(1, 23, 16, 7, 2, 1);
+ else
+ SetBgTilemapPalette(1, 23, 16, 7, 2, 2);
+ spriteId = sPartyMenuInternal->spriteIdConfirmPokeball;
+ break;
+ case PARTY_SIZE + 1: // Cancel
+ // The position of the Cancel button changes if Confirm is present
+ if (!sPartyMenuInternal->chooseHalf)
+ {
+ if (animNum == 0)
+ SetBgTilemapPalette(1, 23, 17, 7, 2, 1);
+ else
+ SetBgTilemapPalette(1, 23, 17, 7, 2, 2);
+ }
+ else if (animNum == 0)
+ {
+ SetBgTilemapPalette(1, 23, 18, 7, 2, 1);
+ }
+ else
+ {
+ SetBgTilemapPalette(1, 23, 18, 7, 2, 2);
+ }
+ spriteId = sPartyMenuInternal->spriteIdCancelPokeball;
+ break;
+ }
+ PartyMenuStartSpriteAnim(spriteId, animNum);
+ ScheduleBgCopyTilemapToVram(1);
+}
+
+u8 GetPartyBoxPaletteFlags(u8 slot, u8 animNum)
+{
+ u8 palFlags = 0;
+
+ if (animNum == 1)
+ palFlags |= PARTY_PAL_SELECTED;
+ if (GetMonData(&gPlayerParty[slot], MON_DATA_HP) == 0)
+ palFlags |= PARTY_PAL_FAINTED;
+ if (gPartyMenu.layout == PARTY_LAYOUT_MULTI
+ && (slot == 1 || slot == 4 || slot == 5))
+ palFlags |= PARTY_PAL_MULTI_ALT;
+ if (gPartyMenu.action == PARTY_ACTION_SWITCHING)
+ palFlags |= PARTY_PAL_SWITCHING;
+ if (gPartyMenu.action == PARTY_ACTION_SWITCH)
+ {
+ if (slot == gPartyMenu.slotId || slot == gPartyMenu.slotId2)
+ palFlags |= PARTY_PAL_TO_SWITCH;
+ }
+ if (gPartyMenu.action == PARTY_ACTION_SOFTBOILED && slot == gPartyMenu.slotId )
+ palFlags |= PARTY_PAL_TO_SOFTBOIL;
+ return palFlags;
+}
+
+void DrawCancelConfirmButtons(void)
+{
+ CopyToBgTilemapBufferRect_ChangePalette(1, sConfirmButton_Tilemap, 23, 16, 7, 2, 17);
+ CopyToBgTilemapBufferRect_ChangePalette(1, sCancelButton_Tilemap, 23, 18, 7, 2, 17);
+ ScheduleBgCopyTilemapToVram(1);
+}
+
+bool8 IsMultiBattle(void)
+{
+ if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattleTypeFlags & BATTLE_TYPE_LINK)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+void SwapPartyPokemon(struct Pokemon *mon1, struct Pokemon *mon2)
+{
+ struct Pokemon *buffer = Alloc(sizeof(struct Pokemon));
+
+ *buffer = *mon1;
+ *mon1 = *mon2;
+ *mon2 = *buffer;
+ Free(buffer);
+}
+
+void Task_ClosePartyMenu(u8 taskId)
+{
+ BeginNormalPaletteFade(0xFFFFFFFF, -2, 0, 16, RGB_BLACK);
+ gTasks[taskId].func = Task_ClosePartyMenuAndSetCB2;
+}
+
+void Task_ClosePartyMenuAndSetCB2(u8 taskId)
+{
+ if (!gPaletteFade.active)
+ {
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_BATTLE)
+ UpdatePartyToFieldOrder();
+ if (sPartyMenuInternal->exitCallback != NULL)
+ SetMainCallback2(sPartyMenuInternal->exitCallback);
+ else
+ SetMainCallback2(gPartyMenu.exitCallback);
+ FreePartyPointers();
+ DestroyTask(taskId);
+ }
+}
+
+u8 GetCursorSelectionMonId(void)
+{
+ return gPartyMenu.slotId;
+}
+
+u8 GetPartyMenuType(void)
+{
+ return gPartyMenu.menuType;
+}
+
+void Task_HandleChooseMonInput(u8 taskId)
+{
+ if (!gPaletteFade.active && sub_80BF748() != TRUE)
+ {
+ s8 *slotPtr = GetCurrentPartySlotPtr();
+
+ switch (PartyMenuButtonHandler(slotPtr))
+ {
+ case 1: // Selected mon
+ HandleChooseMonSelection(taskId, slotPtr);
+ break;
+ case 2: // Selected Cancel
+ HandleChooseMonCancel(taskId, slotPtr);
+ break;
+ case 8: // Start button
+ if (sPartyMenuInternal->chooseHalf)
+ {
+ PlaySE(SE_SELECT);
+ MoveCursorToConfirm();
+ }
+ break;
+ }
+ }
+}
+
+s8 *GetCurrentPartySlotPtr(void)
+{
+ if (gPartyMenu.action == PARTY_ACTION_SWITCH || gPartyMenu.action == PARTY_ACTION_SOFTBOILED)
+ return &gPartyMenu.slotId2;
+ else
+ return &gPartyMenu.slotId;
+}
+
+void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
+{
+ if (*slotPtr == PARTY_SIZE)
+ {
+ gPartyMenu.task(taskId);
+ }
+ else
+ {
+ switch (gPartyMenu.action - 3)
+ {
+ case PARTY_ACTION_SOFTBOILED - 3:
+ if (IsSelectedMonNotEgg((u8 *)slotPtr))
+ Task_TryUseSoftboiledOnPartyMon(taskId);
+ break;
+ case PARTY_ACTION_USE_ITEM - 3:
+ if (IsSelectedMonNotEgg((u8 *)slotPtr))
+ {
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_BATTLE)
+ sPartyMenuInternal->exitCallback = CB2_SetUpExitToBattleScreen;
+ gItemUseCB(taskId, Task_ClosePartyMenuAfterText);
+ }
+ break;
+ case PARTY_ACTION_MOVE_TUTOR - 3:
+ if (IsSelectedMonNotEgg((u8 *)slotPtr))
+ {
+ PlaySE(SE_SELECT);
+ TryTutorSelectedMon(taskId);
+ }
+ break;
+ case PARTY_ACTION_GIVE_MAILBOX_MAIL - 3:
+ if (IsSelectedMonNotEgg((u8 *)slotPtr))
+ {
+ PlaySE(SE_SELECT);
+ TryGiveMailToSelectedMon(taskId);
+ }
+ break;
+ case PARTY_ACTION_GIVE_ITEM - 3:
+ case PARTY_ACTION_GIVE_PC_ITEM - 3:
+ if (IsSelectedMonNotEgg((u8 *)slotPtr))
+ {
+ PlaySE(SE_SELECT);
+ TryGiveItemOrMailToSelectedMon(taskId);
+ }
+ break;
+ case PARTY_ACTION_SWITCH - 3:
+ PlaySE(SE_SELECT);
+ SwitchSelectedMons(taskId);
+ break;
+ case PARTY_ACTION_CHOOSE_AND_CLOSE - 3:
+ PlaySE(SE_SELECT);
+ gSpecialVar_0x8004 = *slotPtr;
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_MOVE_RELEARNER)
+ gSpecialVar_0x8005 = GetNumberOfRelearnableMoves(&gPlayerParty[*slotPtr]);
+ Task_ClosePartyMenu(taskId);
+ break;
+ case PARTY_ACTION_MINIGAME - 3:
+ if (IsSelectedMonNotEgg((u8 *)slotPtr))
+ TryEnterMonForMinigame(taskId, (u8)*slotPtr);
+ break;
+ default:
+ case PARTY_ACTION_ABILITY_PREVENTS - 3:
+ case PARTY_ACTION_SWITCHING - 3:
+ PlaySE(SE_SELECT);
+ Task_TryCreateSelectionWindow(taskId);
+ break;
+ }
+ }
+}
+
+bool8 IsSelectedMonNotEgg(u8 *slotPtr)
+{
+ if (GetMonData(&gPlayerParty[*slotPtr], MON_DATA_IS_EGG) == TRUE)
+ {
+ PlaySE(SE_HAZURE);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void HandleChooseMonCancel(u8 taskId, s8 *slotPtr)
+{
+ switch (gPartyMenu.action)
+ {
+ case PARTY_ACTION_SEND_OUT:
+ PlaySE(SE_HAZURE);
+ break;
+ case PARTY_ACTION_SWITCH:
+ case PARTY_ACTION_SOFTBOILED:
+ PlaySE(SE_SELECT);
+ FinishTwoMonAction(taskId);
+ break;
+ case PARTY_ACTION_MINIGAME:
+ PlaySE(SE_SELECT);
+ CancelParticipationPrompt(taskId);
+ break;
+ default:
+ PlaySE(SE_SELECT);
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_CHOOSE_HALF)
+ {
+ DisplayCancelChooseMonYesNo(taskId);
+ }
+ else
+ {
+ if (!MenuHelpers_LinkSomething())
+ gSpecialVar_0x8004 = PARTY_SIZE + 1;
+ gPartyMenuUseExitCallback = FALSE;
+ *slotPtr = PARTY_SIZE + 1;
+ Task_ClosePartyMenu(taskId);
+ }
+ break;
+ }
+}
+
+void DisplayCancelChooseMonYesNo(u8 taskId)
+{
+ PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]);
+ StringExpandPlaceholders(gStringVar4, gUnknown_84176CF);
+ DisplayPartyMenuMessage(gStringVar4, TRUE);
+ gTasks[taskId].func = Task_CancelChooseMonYesNo;
+}
+
+void Task_CancelChooseMonYesNo(u8 taskId)
+{
+ if (IsPartyMenuTextPrinterActive() != TRUE)
+ {
+ PartyMenuDisplayYesNoMenu();
+ gTasks[taskId].func = Task_HandleCancelChooseMonYesNoInput;
+ }
+}
+
+void Task_HandleCancelChooseMonYesNoInput(u8 taskId)
+{
+ switch (Menu_ProcessInputNoWrapClearOnChoose())
+ {
+ case 0:
+ gPartyMenuUseExitCallback = FALSE;
+ gPartyMenu.slotId = PARTY_SIZE + 1;
+ ClearSelectedPartyOrder();
+ Task_ClosePartyMenu(taskId);
+ break;
+ case MENU_B_PRESSED:
+ PlaySE(SE_SELECT);
+ // fallthrough
+ case 1:
+ Task_ReturnToChooseMonAfterText(taskId);
+ break;
+ }
+}
+
+u16 PartyMenuButtonHandler(s8 *slotPtr)
+{
+ s8 movementDir;
+
+ switch (gMain.newAndRepeatedKeys)
+ {
+ case DPAD_UP:
+ movementDir = MENU_DIR_UP;
+ break;
+ case DPAD_DOWN:
+ movementDir = MENU_DIR_DOWN;
+ break;
+ case DPAD_LEFT:
+ movementDir = MENU_DIR_LEFT;
+ break;
+ case DPAD_RIGHT:
+ movementDir = MENU_DIR_RIGHT;
+ break;
+ default:
+ switch (GetLRKeysPressedAndHeld())
+ {
+ case MENU_L_PRESSED:
+ movementDir = MENU_DIR_UP;
+ break;
+ case MENU_R_PRESSED:
+ movementDir = MENU_DIR_DOWN;
+ break;
+ default:
+ movementDir = 0;
+ break;
+ }
+ break;
+ }
+ if (JOY_NEW(START_BUTTON))
+ return 8;
+ if (movementDir)
+ {
+ UpdateCurrentPartySelection(slotPtr, movementDir);
+ return 0;
+ }
+ // Pressed Cancel
+ if (JOY_NEW(A_BUTTON) && *slotPtr == PARTY_SIZE + 1)
+ return 2;
+ return JOY_NEW(A_BUTTON | B_BUTTON);
+}
+
+void UpdateCurrentPartySelection(s8 *slotPtr, s8 movementDir)
+{
+ s8 newSlotId = *slotPtr;
+ u8 layout = gPartyMenu.layout;
+
+ if (layout == PARTY_LAYOUT_SINGLE)
+ UpdatePartySelectionSingleLayout(slotPtr, movementDir);
+ else
+ UpdatePartySelectionDoubleLayout(slotPtr, movementDir);
+ if (*slotPtr != newSlotId)
+ {
+ PlaySE(SE_SELECT);
+ AnimatePartySlot(newSlotId, 0);
+ AnimatePartySlot(*slotPtr, 1);
+ }
+}
+
+void UpdatePartySelectionSingleLayout(s8 *slotPtr, s8 movementDir)
+{
+ // PARTY_SIZE + 1 is Cancel, PARTY_SIZE is Confirm
+ switch (movementDir)
+ {
+ case MENU_DIR_UP:
+ if (*slotPtr == 0)
+ {
+ *slotPtr = PARTY_SIZE + 1;
+ }
+ else if (*slotPtr == PARTY_SIZE)
+ {
+ *slotPtr = gPlayerPartyCount - 1;
+ }
+ else if (*slotPtr == PARTY_SIZE + 1)
+ {
+ if (sPartyMenuInternal->chooseHalf)
+ *slotPtr = PARTY_SIZE;
+ else
+ *slotPtr = gPlayerPartyCount - 1;
+ }
+ else
+ {
+ --*slotPtr;
+ }
+ break;
+ case MENU_DIR_DOWN:
+ if (*slotPtr == PARTY_SIZE + 1)
+ {
+ *slotPtr = 0;
+ }
+ else
+ {
+ if (*slotPtr == gPlayerPartyCount - 1)
+ {
+ if (sPartyMenuInternal->chooseHalf)
+ *slotPtr = PARTY_SIZE;
+ else
+ *slotPtr = PARTY_SIZE + 1;
+ }
+ else
+ {
+ ++*slotPtr;
+ }
+ }
+ break;
+ case MENU_DIR_RIGHT:
+ if (gPlayerPartyCount != 1 && *slotPtr == 0)
+ {
+ if (sPartyMenuInternal->lastSelectedSlot == 0)
+ *slotPtr = 1;
+ else
+ *slotPtr = sPartyMenuInternal->lastSelectedSlot;
+ }
+ break;
+ case MENU_DIR_LEFT:
+ if (*slotPtr != 0 && *slotPtr != PARTY_SIZE && *slotPtr != PARTY_SIZE + 1)
+ {
+ sPartyMenuInternal->lastSelectedSlot = *slotPtr;
+ *slotPtr = 0;
+ }
+ break;
+ }
+}
+
+void UpdatePartySelectionDoubleLayout(s8 *slotPtr, s8 movementDir)
+{
+ // PARTY_SIZE + 1 is Cancel, PARTY_SIZE is Confirm
+ // newSlot is used temporarily as a movement direction during its later assignment
+ s8 newSlot = movementDir;
+
+ switch (movementDir)
+ {
+ case MENU_DIR_UP:
+ if (*slotPtr == 0)
+ {
+ *slotPtr = PARTY_SIZE + 1;
+ break;
+ }
+ else if (*slotPtr == PARTY_SIZE)
+ {
+ *slotPtr = gPlayerPartyCount - 1;
+ break;
+ }
+ else if (*slotPtr == PARTY_SIZE + 1)
+ {
+ if (sPartyMenuInternal->chooseHalf)
+ {
+ *slotPtr = PARTY_SIZE;
+ break;
+ }
+ --*slotPtr;
+ }
+ newSlot = GetNewSlotDoubleLayout(*slotPtr, newSlot);
+ if (newSlot != -1)
+ *slotPtr = newSlot;
+ break;
+ case MENU_DIR_DOWN:
+ if (*slotPtr == PARTY_SIZE)
+ {
+ *slotPtr = PARTY_SIZE + 1;
+ }
+ else if (*slotPtr == PARTY_SIZE + 1)
+ {
+ *slotPtr = 0;
+ }
+ else
+ {
+ newSlot = GetNewSlotDoubleLayout(*slotPtr, MENU_DIR_DOWN);
+ if (newSlot == -1)
+ {
+ if (sPartyMenuInternal->chooseHalf)
+ *slotPtr = PARTY_SIZE;
+ else
+ *slotPtr = PARTY_SIZE + 1;
+ }
+ else
+ {
+ *slotPtr = newSlot;
+ }
+ }
+ break;
+ case MENU_DIR_RIGHT:
+ if (*slotPtr == 0)
+ {
+ if (sPartyMenuInternal->lastSelectedSlot == 3)
+ {
+ if (GetMonData(&gPlayerParty[3], MON_DATA_SPECIES) != SPECIES_NONE)
+ *slotPtr = 3;
+ }
+ else if (GetMonData(&gPlayerParty[2], MON_DATA_SPECIES) != SPECIES_NONE)
+ {
+ *slotPtr = 2;
+ }
+ }
+ else if (*slotPtr == 1)
+ {
+ if (sPartyMenuInternal->lastSelectedSlot == 5)
+ {
+ if (GetMonData(&gPlayerParty[5], MON_DATA_SPECIES) != SPECIES_NONE)
+ *slotPtr = 5;
+ }
+ else if (GetMonData(&gPlayerParty[4], MON_DATA_SPECIES) != SPECIES_NONE)
+ {
+ *slotPtr = 4;
+ }
+ }
+ break;
+ case MENU_DIR_LEFT:
+ if (*slotPtr == 2 || *slotPtr == 3)
+ {
+ sPartyMenuInternal->lastSelectedSlot = *slotPtr;
+ *slotPtr = 0;
+ }
+ else if (*slotPtr == 4 || *slotPtr == 5)
+ {
+ sPartyMenuInternal->lastSelectedSlot = *slotPtr;
+ *slotPtr = 1;
+ }
+ break;
+ }
+}
+
+s8 GetNewSlotDoubleLayout(s8 slotId, s8 movementDir)
+{
+ while (TRUE)
+ {
+ slotId += movementDir;
+ if ((u8)slotId >= PARTY_SIZE)
+ return -1;
+ if (GetMonData(&gPlayerParty[slotId], MON_DATA_SPECIES) != SPECIES_NONE)
+ return slotId;
+ }
+}
+
+u8 *GetMonNickname(struct Pokemon *mon, u8 *dest)
+{
+ GetMonData(mon, MON_DATA_NICKNAME, dest);
+ return StringGetEnd10(dest);
+}
+
+#define tKeepOpen data[0]
+
+u8 DisplayPartyMenuMessage(const u8 *str, bool8 keepOpen)
+{
+ u8 taskId;
+
+ PartyMenuPrintText(str);
+ taskId = CreateTask(Task_PrintAndWaitForText, 1);
+ gTasks[taskId].tKeepOpen = keepOpen;
+ return taskId;
+}
+
+void Task_PrintAndWaitForText(u8 taskId)
+{
+ if (RunTextPrinters_CheckActive(6) != TRUE)
+ {
+ if (gTasks[taskId].tKeepOpen == FALSE)
+ {
+ ClearStdWindowAndFrameToTransparent(6, 0);
+ ClearWindowTilemap(6);
+ }
+ DestroyTask(taskId);
+ }
+}
+
+#undef tKeepOpen
+
+bool8 IsPartyMenuTextPrinterActive(void)
+{
+ return FuncIsActiveTask(Task_PrintAndWaitForText);
+}
+
+void Task_WaitForLinkAndReturnToChooseMon(u8 taskId)
+{
+ if (sub_80BF748() != TRUE)
+ {
+ DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON);
+ gTasks[taskId].func = Task_HandleChooseMonInput;
+ }
+}
+
+void Task_ReturnToChooseMonAfterText(u8 taskId)
+{
+ if (IsPartyMenuTextPrinterActive() != TRUE)
+ {
+ ClearStdWindowAndFrameToTransparent(6, 0);
+ ClearWindowTilemap(6);
+ if (MenuHelpers_LinkSomething() == TRUE)
+ {
+ gTasks[taskId].func = Task_WaitForLinkAndReturnToChooseMon;
+ }
+ else
+ {
+ DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON);
+ gTasks[taskId].func = Task_HandleChooseMonInput;
+ }
+ }
+}
+
+void DisplayGaveHeldItemMessage(struct Pokemon *mon, u16 item, bool8 keepOpen, u8 a4)
+{
+ if (!a4)
+ ItemUse_SetQuestLogEvent(5, mon, item, 0xFFFF);
+ else if (gPartyMenu.action == PARTY_ACTION_GIVE_PC_ITEM)
+ ItemUse_SetQuestLogEvent(7, mon, item, 0xFFFF);
+ else
+ ItemUse_SetQuestLogEvent(6, mon, item, 0xFFFF);
+ GetMonNickname(mon, gStringVar1);
+ CopyItemName(item, gStringVar2);
+ StringExpandPlaceholders(gStringVar4, gText_PkmnWasGivenItem);
+ DisplayPartyMenuMessage(gStringVar4, keepOpen);
+ ScheduleBgCopyTilemapToVram(2);
+}
+
+void DisplayTookHeldItemMessage(struct Pokemon *mon, u16 item, bool8 keepOpen)
+{
+ ItemUse_SetQuestLogEvent(8, mon, item, 0xFFFF);
+ GetMonNickname(mon, gStringVar1);
+ CopyItemName(item, gStringVar2);
+ StringExpandPlaceholders(gStringVar4, gText_ReceivedItemFromPkmn);
+ DisplayPartyMenuMessage(gStringVar4, keepOpen);
+ ScheduleBgCopyTilemapToVram(2);
+}
+
+void DisplayAlreadyHoldingItemSwitchMessage(struct Pokemon *mon, u16 item, bool8 keepOpen)
+{
+ GetMonNickname(mon, gStringVar1);
+ CopyItemName(item, gStringVar2);
+ StringExpandPlaceholders(gStringVar4, gText_PkmnAlreadyHoldingItemSwitch);
+ DisplayPartyMenuMessage(gStringVar4, keepOpen);
+ ScheduleBgCopyTilemapToVram(2);
+}
+
+void DisplaySwitchedHeldItemMessage(u16 item, u16 item2, bool8 keepOpen)
+{
+ sub_8124B60(&gPlayerParty[gPartyMenu.slotId], item2, item);
+ CopyItemName(item, gStringVar1);
+ CopyItemName(item2, gStringVar2);
+ StringExpandPlaceholders(gStringVar4, gText_SwitchedPkmnItem);
+ DisplayPartyMenuMessage(gStringVar4, keepOpen);
+ ScheduleBgCopyTilemapToVram(2);
+}
+
+void GiveItemToMon(struct Pokemon *mon, u16 item)
+{
+ u8 itemBytes[2];
+
+ if (ItemIsMail(item) == TRUE)
+ {
+ if (GiveMailToMon(mon, item) == 0xFF)
+ return;
+ }
+ itemBytes[0] = item;
+ itemBytes[1] = item >> 8;
+ SetMonData(mon, MON_DATA_HELD_ITEM, itemBytes);
+}
+
+u8 TryTakeMonItem(struct Pokemon *mon)
+{
+ u16 item = GetMonData(mon, MON_DATA_HELD_ITEM);
+
+ if (item == ITEM_NONE)
+ return 0;
+ if (AddBagItem(item, 1) == FALSE)
+ return 1;
+ item = ITEM_NONE;
+ SetMonData(mon, MON_DATA_HELD_ITEM, &item);
+ return 2;
+}
+
+void BufferBagFullCantTakeItemMessage(u16 itemId)
+{
+ const u8 *string;
+
+ switch (ItemId_GetPocket(itemId))
+ {
+ default:
+ string = gStartMenuText_Bag;
+ break;
+ case POCKET_TM_CASE:
+ string = ItemId_GetName(ITEM_TM_CASE);
+ break;
+ case POCKET_BERRY_POUCH:
+ string = ItemId_GetName(ITEM_BERRY_POUCH);
+ break;
+ }
+ StringCopy(gStringVar1, string);
+ StringExpandPlaceholders(gStringVar4, gText_BagFullCouldNotRemoveItem);
+}
+
+#define tHP data[0]
+#define tMaxHP data[1]
+#define tHPIncrement data[2]
+#define tHPToAdd data[3]
+#define tPartyId data[4]
+#define tStartHP data[5]
+
+void Task_PartyMenuModifyHP(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+
+ tHP += tHPIncrement;
+ --tHPToAdd;
+ SetMonData(&gPlayerParty[tPartyId], MON_DATA_HP, &tHP);
+ DisplayPartyPokemonHPCheck(&gPlayerParty[tPartyId], &sPartyMenuBoxes[tPartyId], 1);
+ DisplayPartyPokemonHPBarCheck(&gPlayerParty[tPartyId], &sPartyMenuBoxes[tPartyId]);
+ if (tHPToAdd == 0 || tHP == 0 || tHP == tMaxHP)
+ {
+ // If HP was recovered, buffer the amount recovered
+ if (tHP > tStartHP)
+ ConvertIntToDecimalStringN(gStringVar2, tHP - tStartHP, STR_CONV_MODE_LEFT_ALIGN, 3);
+ SwitchTaskToFollowupFunc(taskId);
+ }
+}
+
+void PartyMenuModifyHP(u8 taskId, u8 slot, s8 hpIncrement, s16 hpDifference, TaskFunc task)
+{
+ struct Pokemon *mon = &gPlayerParty[slot];
+ s16 *data = gTasks[taskId].data;
+
+ tHP = GetMonData(mon, MON_DATA_HP);
+ tMaxHP = GetMonData(mon, MON_DATA_MAX_HP);
+ tHPIncrement = hpIncrement;
+ tHPToAdd = hpDifference;
+ tPartyId = slot;
+ tStartHP = tHP;
+ SetTaskFuncWithFollowupFunc(taskId, Task_PartyMenuModifyHP, task);
+}
+
+void ResetHPTaskData(u8 taskId, u8 caseId, u32 hp)
+{
+ s16 *data = gTasks[taskId].data;
+
+ switch (caseId) // always zero
+ {
+ case 0:
+ tHP = hp;
+ tStartHP = hp;
+ break;
+ case 1:
+ tMaxHP = hp;
+ break;
+ case 2:
+ tHPIncrement = hp;
+ break;
+ case 3:
+ tHPToAdd = hp;
+ break;
+ case 4:
+ tPartyId = hp;
+ break;
+ case 5:
+ SetTaskFuncWithFollowupFunc(taskId, Task_PartyMenuModifyHP, (TaskFunc)hp); // >casting hp as a taskfunc
+ break;
+ }
+}
+
+#undef tHP
+#undef tMaxHP
+#undef tHPIncrement
+#undef tHPToAdd
+#undef tPartyId
+#undef tStartHP
+
+u8 GetAilmentFromStatus(u32 status)
+{
+ if (status & STATUS1_PSN_ANY)
+ return AILMENT_PSN;
+ if (status & STATUS1_PARALYSIS)
+ return AILMENT_PRZ;
+ if (status & STATUS1_SLEEP)
+ return AILMENT_SLP;
+ if (status & STATUS1_FREEZE)
+ return AILMENT_FRZ;
+ if (status & STATUS1_BURN)
+ return AILMENT_BRN;
+ return AILMENT_NONE;
+}
+
+u8 GetMonAilment(struct Pokemon *mon)
+{
+ u8 ailment;
+
+ if (GetMonData(mon, MON_DATA_HP) == 0)
+ return AILMENT_FNT;
+ ailment = GetAilmentFromStatus(GetMonData(mon, MON_DATA_STATUS));
+ if (ailment != AILMENT_NONE)
+ return ailment;
+ if (CheckPartyPokerus(mon, 0))
+ return AILMENT_PKRS;
+ return AILMENT_NONE;
+}
+
+void SetPartyMonsAllowedInMinigame(void)
+{
+ u16 *ptr;
+
+ if (gPartyMenu.menuType == PARTY_MENU_TYPE_MINIGAME)
+ {
+ u8 i;
+
+ ptr = &gPartyMenu.data1;
+ gPartyMenu.data1 = 0;
+ if (gSpecialVar_0x8005 == 0)
+ {
+ for (i = 0; i < gPlayerPartyCount; ++i)
+ *ptr += IsMonAllowedInPokemonJump(&gPlayerParty[i]) << i;
+ }
+ else
+ {
+ for (i = 0; i < gPlayerPartyCount; ++i)
+ *ptr += IsMonAllowedInDodrioBerryPicking(&gPlayerParty[i]) << i;
+ }
+ }
+}
+
+bool16 IsMonAllowedInPokemonJump(struct Pokemon *mon)
+{
+ if (GetMonData(mon, MON_DATA_IS_EGG) != TRUE && IsSpeciesAllowedInPokemonJump(GetMonData(mon, MON_DATA_SPECIES)))
+ return TRUE;
+ return FALSE;
+}
+
+
+bool16 IsMonAllowedInDodrioBerryPicking(struct Pokemon *mon)
+{
+ if (GetMonData(mon, MON_DATA_IS_EGG) != TRUE && GetMonData(mon, MON_DATA_SPECIES) == SPECIES_DODRIO)
+ return TRUE;
+ return FALSE;
+}
+
+bool8 IsMonAllowedInMinigame(u8 slot)
+{
+ if (!((gPartyMenu.data1 >> slot) & 1))
+ return FALSE;
+ return TRUE;
+}
+
+void TryEnterMonForMinigame(u8 taskId, u8 slot)
+{
+ if (IsMonAllowedInMinigame(slot) == TRUE)
+ {
+ PlaySE(SE_SELECT);
+ gSpecialVar_0x8004 = slot;
+ Task_ClosePartyMenu(taskId);
+ }
+ else
+ {
+ PlaySE(SE_HAZURE);
+ DisplayPartyMenuMessage(gText_PkmnCantParticipate, FALSE);
+ ScheduleBgCopyTilemapToVram(2);
+ gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
+ }
+}
+
+void CancelParticipationPrompt(u8 taskId)
+{
+ DisplayPartyMenuMessage(gText_CancelParticipation, TRUE);
+ ScheduleBgCopyTilemapToVram(2);
+ gTasks[taskId].func = Task_CancelParticipationYesNo;
+}
+
+void Task_CancelParticipationYesNo(u8 taskId)
+{
+ if (IsPartyMenuTextPrinterActive() != TRUE)
+ {
+ PartyMenuDisplayYesNoMenu();
+ gTasks[taskId].func = Task_HandleCancelParticipationYesNoInput;
+ }
+}
+
+void Task_HandleCancelParticipationYesNoInput(u8 taskId)
+{
+ switch (Menu_ProcessInputNoWrapClearOnChoose())
+ {
+ case 0:
+ gSpecialVar_0x8004 = PARTY_SIZE + 1;
+ Task_ClosePartyMenu(taskId);
+ break;
+ case MENU_B_PRESSED:
+ PlaySE(SE_SELECT);
+ // fallthrough
+ case 1:
+ gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
+ break;
+ }
+}
+
+u8 CanMonLearnTMTutor(struct Pokemon *mon, u16 item, u8 tutor)
+{
+ u16 move;
+
+ if (GetMonData(mon, MON_DATA_IS_EGG))
+ return CANNOT_LEARN_MOVE_IS_EGG;
+
+ if (item >= ITEM_TM01_FOCUS_PUNCH)
+ {
+ if (CanMonLearnTMHM(mon, item - ITEM_TM01_FOCUS_PUNCH))
+ move = ItemIdToBattleMoveId(item);
+ else
+ return CANNOT_LEARN_MOVE;
+ do
+ {
+ } while (0);
+ }
+ else if (CanLearnTutorMove(GetMonData(mon, MON_DATA_SPECIES), tutor) == FALSE)
+ {
+ return CANNOT_LEARN_MOVE;
+ }
+ else
+ {
+ move = GetTutorMove(tutor);
+ }
+ if (MonKnowsMove(mon, move) == TRUE)
+ return ALREADY_KNOWS_MOVE;
+ else
+ return CAN_LEARN_MOVE;
+}
+
+u16 GetTutorMove(u8 tutor)
+{
+ switch (tutor)
+ {
+ case TUTOR_MOVE_FRENZY_PLANT:
+ return MOVE_FRENZY_PLANT;
+ case TUTOR_MOVE_BLAST_BURN:
+ return MOVE_BLAST_BURN;
+ case TUTOR_MOVE_HYDRO_CANNON:
+ return MOVE_HYDRO_CANNON;
+ default:
+ return sTutorMoves[tutor];
+ }
+}
+
+bool8 CanLearnTutorMove(u16 species, u8 tutor)
+{
+ switch (tutor)
+ {
+ case TUTOR_MOVE_FRENZY_PLANT:
+ if (species == SPECIES_VENUSAUR)
+ return TRUE;
+ else
+ return FALSE;
+ case TUTOR_MOVE_BLAST_BURN:
+ if (species == SPECIES_CHARIZARD)
+ return TRUE;
+ else
+ return FALSE;
+ case TUTOR_MOVE_HYDRO_CANNON:
+ if (species == SPECIES_BLASTOISE)
+ return TRUE;
+ else
+ return FALSE;
+ default:
+ if (sTutorLearnsets[species] & (1 << tutor))
+ return TRUE;
+ else
+ return FALSE;
+ }
+}
+
+void sub_8120C3C(u8 taskId)
+{
+ if (!gPaletteFade.active)
+ gTasks[taskId].func = sub_8120C6C;
+}
+
+void sub_8120C6C(u8 taskId)
+{
+ BeginNormalPaletteFade(0xFFFF1FFF, 4, 0, 6, RGB_BLACK);
+ gTasks[taskId].func = sub_8120CA8;
+}
+
+void sub_8120CA8(u8 taskId)
+{
+ if (!gPaletteFade.active)
+ gTasks[taskId].func = sub_8120CD8;
+}
+
+void sub_8120CD8(u8 taskId)
+{
+ gTasks[taskId].data[0] = sub_81220D4();
+ gTasks[taskId].func = sub_8120D08;
+}
+
+void sub_8120D08(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+
+ if (RunTextPrinters_CheckActive((u8)data[0]) != TRUE)
+ gTasks[taskId].func = sub_8120D40;
+}
+
+void sub_8120D40(u8 taskId)
+{
+ BeginNormalPaletteFade(0xFFFF0008, 4, 6, 0, RGB_BLACK);
+ gTasks[taskId].func = sub_8120D7C;
+}
+
+void sub_8120D7C(u8 taskId)
+{
+ if (!gPaletteFade.active)
+ gTasks[taskId].func = sub_8120DAC;
+}
+
+void sub_8120DAC(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+
+ sub_8122084(data[0], gUnknown_8417494);
+ gTasks[taskId].func = sub_8120DE0;
+}
+
+void sub_8120DE0(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+
+ if (RunTextPrinters_CheckActive((u8)data[0]) != TRUE)
+ {
+ sub_8122110((u8)data[0]);
+ gTasks[taskId].func = sub_8120E1C;
+ }
+}
+
+void sub_8120E1C(u8 taskId)
+{
+ BeginNormalPaletteFade(0x0000FFF7, 4, 6, 0, RGB_BLACK);
+ gTasks[taskId].func = sub_8120E58;
+}
+
+void sub_8120E58(u8 taskId)
+{
+ if (!gPaletteFade.active)
+ {
+ TextWindow_SetUserSelectedFrame(0, 0x4F, 0xD0);
+ TextWindow_SetStdFrame0_WithPal(0, 0x58, 0xF0);
+ if (gPartyMenu.action == PARTY_ACTION_USE_ITEM)
+ DisplayPartyMenuStdMessage(PARTY_MSG_USE_ON_WHICH_MON);
+ else
+ DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON);
+ gTasks[taskId].func = Task_HandleChooseMonInput;
+ }
+}
+
+void sub_8120EBC(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+
+ data[0] = 0;
+ gTasks[taskId].func = sub_8120EE0;
+}
+
+void sub_8120EE0(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+
+ if (!gPaletteFade.active && sub_8120F78(taskId) != TRUE)
+ {
+ switch (data[0])
+ {
+ case 80:
+ #ifndef NONMATCHING
+ asm("":::"r5");
+ #endif
+ UpdateCurrentPartySelection(&gPartyMenu.slotId, 2);
+ ++data[0];
+ break;
+ case 160:
+ PlaySE(SE_SELECT);
+ CreateSelectionWindow();
+ ++data[0];
+ break;
+ case 240:
+ PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[2]);
+ sCursorOptions[sPartyMenuInternal->actions[0]].func(taskId);
+ // fall through
+ default:
+ ++data[0];
+ break;
+ }
+ }
+}