#include "global.h" #include "malloc.h" #include "battle.h" #include "battle_anim.h" #include "battle_controllers.h" #include "battle_gfx_sfx_util.h" #include "battle_interface.h" #include "battle_pike.h" #include "battle_pyramid.h" #include "battle_pyramid_bag.h" #include "bg.h" #include "contest.h" #include "data.h" #include "decompress.h" #include "easy_chat.h" #include "event_data.h" #include "evolution_scene.h" #include "field_control_avatar.h" #include "field_effect.h" #include "field_player_avatar.h" #include "field_screen_effect.h" #include "field_specials.h" #include "field_weather.h" #include "fieldmap.h" #include "fldeff.h" #include "fldeff_misc.h" #include "frontier_util.h" #include "gpu_regs.h" #include "graphics.h" #include "international_string_util.h" #include "item.h" #include "item_menu.h" #include "item_use.h" #include "link.h" #include "link_rfu.h" #include "mail.h" #include "main.h" #include "menu.h" #include "menu_helpers.h" #include "menu_specialized.h" #include "metatile_behavior.h" #include "overworld.h" #include "palette.h" #include "party_menu.h" #include "player_pc.h" #include "pokemon.h" #include "pokemon_icon.h" #include "pokemon_jump.h" #include "pokemon_storage_system.h" #include "pokemon_summary_screen.h" #include "region_map.h" #include "reshow_battle_screen.h" #include "scanline_effect.h" #include "script.h" #include "sound.h" #include "sprite.h" #include "start_menu.h" #include "string_util.h" #include "strings.h" #include "task.h" #include "text.h" #include "text_window.h" #include "trade.h" #include "union_room.h" #include "window.h" #include "constants/battle.h" #include "constants/battle_frontier.h" #include "constants/easy_chat.h" #include "constants/field_effects.h" #include "constants/flags.h" #include "constants/item_effects.h" #include "constants/items.h" #include "constants/maps.h" #include "constants/moves.h" #include "constants/party_menu.h" #include "constants/rgb.h" #include "constants/songs.h" #include "constants/species.h" #include "constants/vars.h" #define PARTY_PAL_SELECTED (1 << 0) #define PARTY_PAL_FAINTED (1 << 1) #define PARTY_PAL_TO_SWITCH (1 << 2) #define PARTY_PAL_MULTI_ALT (1 << 3) #define PARTY_PAL_SWITCHING (1 << 4) #define PARTY_PAL_TO_SOFTBOIL (1 << 5) #define PARTY_PAL_NO_MON (1 << 6) #define PARTY_PAL_UNUSED (1 << 7) enum { CAN_LEARN_MOVE, CANNOT_LEARN_MOVE, ALREADY_KNOWS_MOVE, CANNOT_LEARN_MOVE_IS_EGG }; struct PartyMenuBoxInfoRects { void (*blitFunc)(u8, u8, u8, u8, u8, u8); u8 dimensions[24]; u8 descTextLeft; u8 descTextTop; u8 descTextWidth; u8 descTextHeight; }; struct PartyMenuInternal { TaskFunc task; MainCallback exitCallback; u32 chooseHalf:1; u32 unk8_1:3; u32 spriteIdConfirmPokeball:7; u32 spriteIdCancelPokeball:7; u32 messageId:14; u8 windowId[3]; u8 actions[8]; u8 numActions; // In vanilla Emerald, only the first 0xB0 hwords (0x160 bytes) are actually used. // However, a full 0x100 hwords (0x200 bytes) are allocated. // It is likely that the 0x160 value used below is a constant defined by // bin2c, the utility used to encode the compressed palette data. u16 palBuffer[BG_PLTT_SIZE / sizeof(u16)]; s16 data[16]; }; struct PartyMenuBox { const struct PartyMenuBoxInfoRects *infoRects; const u8 *spriteCoords; u8 windowId; u8 monSpriteId; u8 itemSpriteId; u8 pokeballSpriteId; u8 statusSpriteId; }; // EWRAM vars static EWRAM_DATA struct PartyMenuInternal *sPartyMenuInternal = NULL; EWRAM_DATA struct PartyMenu gPartyMenu = {0}; static EWRAM_DATA struct PartyMenuBox *sPartyMenuBoxes = NULL; static EWRAM_DATA u8 *sPartyBgGfxTilemap = NULL; static EWRAM_DATA u8 *sPartyBgTilemapBuffer = NULL; EWRAM_DATA bool8 gUnknown_0203CEE8 = 0; // executed party action -ish EWRAM_DATA u8 gUnknown_0203CEE9 = 0; // party id for something EWRAM_DATA MainCallback gPostMenuFieldCallback = NULL; static EWRAM_DATA u16 *sSlot1TilemapBuffer = 0; // for switching party slots static EWRAM_DATA u16 *sSlot2TilemapBuffer = 0; // EWRAM_DATA u8 gSelectedOrderFromParty[4] = {0}; static EWRAM_DATA u16 sPartyMenuItemId = 0; static EWRAM_DATA u16 sUnused_0203CEFE = 0; EWRAM_DATA u8 gBattlePartyCurrentOrder[3] = {0}; // bits 0-3 are the current pos of Slot 1, 4-7 are Slot 2, and so on // IWRAM common void (*gItemUseCB)(u8, TaskFunc); static void ResetPartyMenu(void); static void CB2_InitPartyMenu(void); static bool8 ShowPartyMenu(void); static void SetPartyMonsAllowedInMinigame(void); static void ExitPartyMenu(void); static bool8 AllocPartyMenuBg(void); static bool8 AllocPartyMenuBgGfx(void); static void InitPartyMenuWindows(u8); static void InitPartyMenuBoxes(u8); static void LoadPartyMenuPokeballGfx(void); static void LoadPartyMenuAilmentGfx(void); static bool8 party_menu_add_per_mon_objects(void); static bool8 RenderPartyMenuBoxes(void); static void CreateCancelConfirmPokeballSprites(void); static void sub_81B2428(u8); static void Task_ExitPartyMenu(u8); static void FreePartyPointers(void); static void PartyPaletteBufferCopy(u8); static void DisplayPartyPokemonDataForMultiBattle(u8); static void LoadPartyBoxPalette(struct PartyMenuBox *, u8); static void DrawEmptySlot(u8 windowId); static void DisplayPartyPokemonDataForRelearner(u8); static void DisplayPartyPokemonDataForContest(u8); static void DisplayPartyPokemonDataForChooseHalf(u8); static void DisplayPartyPokemonDataForWirelessMinigame(u8); static void DisplayPartyPokemonDataForBattlePyramidHeldItem(u8); static bool8 DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(u8); static void DisplayPartyPokemonData(u8); static void DisplayPartyPokemonNickname(struct Pokemon *, struct PartyMenuBox *, u8); static void DisplayPartyPokemonLevelCheck(struct Pokemon *, struct PartyMenuBox *, u8); static void DisplayPartyPokemonGenderNidoranCheck(struct Pokemon *, struct PartyMenuBox *, u8); static void DisplayPartyPokemonHPCheck(struct Pokemon *, struct PartyMenuBox *, u8); static void DisplayPartyPokemonMaxHPCheck(struct Pokemon *, struct PartyMenuBox *, u8); static void DisplayPartyPokemonHPBarCheck(struct Pokemon *, struct PartyMenuBox *); static void DisplayPartyPokemonDescriptionText(u8, struct PartyMenuBox *, u8); static bool8 IsMonAllowedInMinigame(u8); static void DisplayPartyPokemonDataToTeachMove(u8, u16, u8); static u8 CanMonLearnTMTutor(struct Pokemon *, u16, u8); static void DisplayPartyPokemonBarDetail(u8, const u8*, u8, const u8*); static void DisplayPartyPokemonLevel(u8, struct PartyMenuBox *); static void DisplayPartyPokemonGender(u8, u16, u8*, struct PartyMenuBox *); static void DisplayPartyPokemonHP(u16, struct PartyMenuBox *); static void DisplayPartyPokemonMaxHP(u16, struct PartyMenuBox *); static void DisplayPartyPokemonHPBar(u16, u16, struct PartyMenuBox *); static void party_menu_link_mon_icon_anim(u16, u32, struct PartyMenuBox *, u8, u32); static void party_menu_link_mon_held_item_object(u16, u16, struct PartyMenuBox *); static void party_menu_link_mon_pokeball_object(u16, struct PartyMenuBox *); static void party_menu_link_mon_status_condition_object(u16, u8, struct PartyMenuBox *); static void party_menu_held_item_object(struct Pokemon *, struct PartyMenuBox *); static void party_menu_pokeball_object(struct Pokemon *, struct PartyMenuBox *); static void party_menu_icon_anim(struct Pokemon *, struct PartyMenuBox *, u32); static void party_menu_status_condition_object(struct Pokemon *, struct PartyMenuBox *); static u8 CreateMenuPokeballSprite(u8, u8); static void sub_81B120C(void); static u8 CreateCancelPokeballSprite(u8, u8); static void AnimateSelectedPartyIcon(u8, u8); static void PartyMenuStartSpriteAnim(u8, u8); static u8 GetPartyBoxPaletteFlags(u8, u8); static bool8 PartyBoxPal_ParnterOrDisqualifiedInArena(u8); static u8 GetPartyIdFromBattleSlot(u8); static void Task_ClosePartyMenuAndSetCB2(u8); static void UpdatePartyToFieldOrder(void); static void sub_81B4F88(void); static void HandleChooseMonCancel(u8, s8*); static void HandleChooseMonSelection(u8, s8*); static u16 PartyMenuButtonHandler(s8*); static s8* GetCurrentPartySlotPtr(void); static bool8 IsSelectedMonNotEgg(u8*); static void PartyMenuRemoveWindow(u8*); static void CB2_SetUpExitToBattleScreen(void); static void Task_ClosePartyMenuAfterText(u8); static void TryTutorSelectedMon(u8); static void TryGiveMailToSelectedMon(u8); static void TryGiveItemToSelectedMon(u8); static void SwitchSelectedMons(u8); static void TryEnterMonForMinigame(u8, u8); static void Task_TryCreateSelectionWindow(u8); static void FinishTwoMonAction(u8); static void CancelParticipationPrompt(u8); static bool8 DisplayCancelChooseMonYesNo(u8); static const u8* GetFacilityCancelString(void); static void Task_CancelChooseMonYesNo(u8); static void PartyMenuDisplayYesNoMenu(void); static void Task_HandleCancelChooseMonYesNoInput(u8); static void Task_ReturnToChooseMonAfterText(u8); static void UpdateCurrentPartySelection(s8*, s8); static void UpdatePartySelectionSingleLayout(s8*, s8); static void UpdatePartySelectionDoubleLayout(s8*, s8); static s8 sub_81B1B00(s8, s8); static void PartyMenuPrintText(const u8*); static void Task_PrintAndWaitForText(u8); static bool16 IsMonAllowedInPokemonJump(struct Pokemon*); static bool16 IsMonAllowedInDodrioBerryPicking(struct Pokemon*); static void Task_CancelParticipationYesNo(u8); static void Task_HandleCancelParticipationYesNoInput(u8); static bool8 CanLearnTutorMove(u16, u8); static u16 GetTutorMove(u8); static bool8 sub_81B314C(void); static void SetPartyMonFieldSelectionActions(struct Pokemon*, u8); static u8 GetPartyMenuActionsTypeInBattle(struct Pokemon*); static u8 GetPartySlotEntryStatus(s8); static void Task_UpdateHeldItemSprite(u8); static void Task_HandleSelectionMenuInput(u8); static void CB2_ShowPokemonSummaryScreen(void); static void UpdatePartyToBattleOrder(void); static void CB2_ReturnToPartyMenuFromSummaryScreen(void); static void SlidePartyMenuBoxOneStep(u8); static void Task_SlideSelectedSlotsOffscreen(u8); static void SwitchPartyMon(void); static void Task_SlideSelectedSlotsOnscreen(u8); static void CB2_SelectBagItemToGive(void); static void CB2_GiveHoldItem(void); static void CB2_WriteMailToGiveMon(void); static void Task_SwitchHoldItemsPrompt(u8); static void Task_GiveHoldItem(u8); static void Task_SwitchItemsYesNo(u8); static void Task_HandleSwitchItemsYesNoInput(u8); static void Task_WriteMailToGiveMonAfterText(u8); static void CB2_ReturnToPartyMenuFromWritingMail(void); static void sub_81B4624(u8); static void UpdatePartyMonHeldItemSprite(struct Pokemon*, struct PartyMenuBox*); static void Task_TossHeldItemYesNo(u8 taskId); static void Task_HandleTossHeldItemYesNoInput(u8); static void Task_TossHeldItem(u8); static void sub_81B4A98(void); static void sub_81B4AE0(void); static void sub_81B4B6C(u8); static void sub_81B4BA0(u8); static void sub_81B4C60(u8); static void sub_81B4C94(u8); static bool8 TrySwitchInPokemon(void); static void Task_SpinTradeYesNo(u8); static void Task_HandleSpinTradeYesNoInput(u8); static void Task_CancelAfterAorBPress(u8); static void DisplayFieldMoveExitAreaMessage(u8); static void DisplayCantUseFlashMessage(void); static void DisplayCantUseSurfMessage(void); static void Task_FieldMoveExitAreaYesNo(u8); static void Task_HandleFieldMoveExitAreaYesNoInput(u8); static void Task_FieldMoveWaitForFade(u8); static u16 GetFieldMoveMonSpecies(void); static void UpdatePartyMonHPBar(u8, struct Pokemon*); static void UpdatePartyMonIconFrame(struct Sprite*); static void UpdatePartyMonIconFrameAndBounce(struct Sprite*); static void ShowOrHideHeldItemSprite(u16, struct PartyMenuBox*); static void CreateHeldItemSpriteForTrade(u8, bool8); static void SpriteCB_HeldItem(struct Sprite*); static void SetPartyMonAilmentGfx(struct Pokemon*, struct PartyMenuBox*); static void UpdatePartyMonAilmentGfx(u8, struct PartyMenuBox*); static u8 GetPartyLayoutFromBattleType(void); static void sub_81B6280(u8); static void CB2_ReturnToBagMenu(void); static void Task_DisplayHPRestoredMessage(u8); static u16 ItemEffectToMonEv(struct Pokemon*, u8); static void ItemEffectToStatString(u8, u8*); static void ReturnToUseOnWhichMon(u8); static void SetSelectedMoveForPPItem(u8); static void TryUsePPItem(u8); static void Task_LearnedMove(u8); static void Task_ReplaceMoveYesNo(u8); static void Task_DoLearnedMoveFanfareAfterText(u8); static void Task_LearnNextMoveOrClosePartyMenu(u8); static void Task_TryLearningNextMove(u8); static void Task_HandleReplaceMoveYesNoInput(u8); static void Task_ShowSummaryScreenToForgetMove(u8); static void StopLearningMovePrompt(u8); static void CB2_ShowSummaryScreenToForgetMove(void); static void CB2_ReturnToPartyMenuWhileLearningMove(void); static void Task_ReturnToPartyMenuWhileLearningMove(u8); static void DisplayPartyMenuForgotMoveMessage(u8); static void Task_PartyMenuReplaceMove(u8); static void Task_StopLearningMoveYesNo(u8); static void Task_HandleStopLearningMoveYesNoInput(u8); static void Task_TryLearningNextMoveAfterText(u8); static void BufferMonStatsToTaskData(struct Pokemon*, s16*); static void UpdateMonDisplayInfoAfterRareCandy(u8, struct Pokemon*); static void Task_DisplayLevelUpStatsPg1(u8); static void DisplayLevelUpStatsPg1(u8); static void Task_DisplayLevelUpStatsPg2(u8); static void DisplayLevelUpStatsPg2(u8); static void Task_TryLearnNewMoves(u8); static void PartyMenuTryEvolution(u8); static void DisplayMonNeedsToReplaceMove(u8); static void DisplayMonLearnedMove(u8, u16); static void sub_81B7A28(u8); static void task_sacred_ash_party_loop(u8); static void Task_SacredAshDisplayHPRestored(u8); static void GiveItemToSelectedMon(u8); static void sub_81B83B8(u8); static void sub_81B82A0(u8); static void RemoveItemToGiveFromBag(u16); static void CB2_WriteMailToGiveMonFromBag(void); static void sub_81B8088(u8); static void Task_UpdateHeldItemSpriteAndClosePartyMenu(u8); static void CB2_ReturnToPartyOrBagMenuFromWritingMail(void); static bool8 sub_81B841C(u16); static void sub_81B8230(u8); static void sub_81B82D4(u8); static void Task_ValidateChosenHalfParty(u8); static bool8 GetBattleEntryEligibility(struct Pokemon*); static bool8 HasPartySlotAlreadyBeenSelected(u8); static u8 GetBattleEntryLevelCap(void); static u8 GetMaxBattleEntries(void); static u8 GetMinBattleEntries(void); static void Task_ContinueChoosingHalfParty(u8); static void sub_81B8C88(u8*, bool8); static void sub_81B8D88(u8*, u8, u8); static void Task_InitMultiPartnerPartySlideIn(u8); static void Task_MultiPartnerPartySlideIn(u8); static void SlideMultiPartyMenuBoxSpritesOneStep(u8); static void Task_WaitAfterMultiPartnerPartySlideIn(u8); static void BufferMonSelection(void); static void Task_PartyMenuWaitForFade(u8 taskId); static void Task_ChooseContestMon(u8 taskId); static void CB2_ChooseContestMon(void); static void Task_ChoosePartyMon(u8 taskId); static void Task_ChooseMonForMoveRelearner(u8 taskId); static void CB2_ChooseMonForMoveRelearner(void); static void Task_BattlePyramidChooseMonHeldItems(u8 taskId); static void ShiftMoveSlot(struct Pokemon*, u8, u8); static void BlitBitmapToPartyWindow_LeftColumn(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 f); static void BlitBitmapToPartyWindow_RightColumn(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 f); static void CursorCb_Summary(u8 taskId); static void CursorCb_Switch(u8 taskId); static void CursorCb_Cancel1(u8 taskId); static void CursorCb_Item(u8 taskId); static void CursorCb_Give(u8 taskId); static void CursorCb_TakeItem(u8 taskId); static void CursorCb_Mail(u8 taskId); static void CursorCb_Read(u8 taskId); static void CursorCb_TakeMail(u8 taskId); static void CursorCb_Cancel2(u8 taskId); static void CursorCb_SendMon(u8 taskId); static void CursorCb_Enter(u8 taskId); static void CursorCb_NoEntry(u8 taskId); static void CursorCb_Store(u8 taskId); static void CursorCb_Register(u8 taskId); static void CursorCb_Trade1(u8 taskId); static void CursorCb_Trade2(u8 taskId); static void CursorCb_Toss(u8 taskId); static void CursorCb_FieldMove(u8 taskId); static bool8 SetUpFieldMove_Surf(void); static bool8 SetUpFieldMove_Fly(void); static bool8 SetUpFieldMove_Waterfall(void); static bool8 SetUpFieldMove_Dive(void); // static const data #include "data/pokemon/tutor_learnsets.h" static const struct BgTemplate sPartyMenuBgTemplates[] = { { .bg = 0, .charBaseIndex = 0, .mapBaseIndex = 31, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0 }, { .bg = 1, .charBaseIndex = 0, .mapBaseIndex = 30, .screenSize = 0, .paletteMode = 0, .priority = 2, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 0, .mapBaseIndex = 28, .screenSize = 1, .paletteMode = 0, .priority = 0, .baseTile = 0 }, }; enum { PARTY_BOX_LEFT_COLUMN, PARTY_BOX_RIGHT_COLUMN }; static const struct PartyMenuBoxInfoRects sPartyBoxInfoRects[] = { [PARTY_BOX_LEFT_COLUMN] = { BlitBitmapToPartyWindow_LeftColumn, { //The below are the x, y, width, and height for each of the following info 24, 11, 40, 13, // Nickname 32, 20, 32, 8, // Level 64, 20, 8, 8, // Gender 38, 37, 24, 8, // HP 53, 37, 24, 8, // Max HP 24, 35, 48, 3 // HP bar }, 12, 34, 64, 16 // Description text (e.g. NO USE) }, [PARTY_BOX_RIGHT_COLUMN] = { BlitBitmapToPartyWindow_RightColumn, { // See above comment 22, 3, 40, 13, // Nickname 30, 12, 32, 8, // Level 62, 12, 8, 8, // Gender 102, 12, 24, 8, // HP 117, 12, 24, 8, // Max HP 88, 10, 48, 3 // HP bar }, 77, 4, 64, 16 // Description text }, }; // Each layout array has the sprite coords for each of the party mons in the following order // Pokemon icon (x, y), held item (x, y), status condition (x, y), menu pokeball (x, y) static const u8 sPartyMenuSpriteCoords[PARTY_LAYOUT_COUNT][PARTY_SIZE][4 * 2] = { [PARTY_LAYOUT_SINGLE] = { { 16, 40, 20, 50, 50, 52, 16, 34}, {104, 18, 108, 28, 136, 27, 102, 25}, {104, 42, 108, 52, 136, 51, 102, 49}, {104, 66, 108, 76, 136, 75, 102, 73}, {104, 90, 108, 100, 136, 99, 102, 97}, {104, 114, 108, 124, 136, 123, 102, 121}, }, [PARTY_LAYOUT_DOUBLE] = { {16, 24, 20, 34, 50, 36, 16, 18}, {16, 80, 20, 90, 50, 92, 16, 74}, {104, 18, 108, 28, 136, 27, 102, 25}, {104, 50, 108, 60, 136, 59, 102, 57}, {104, 82, 108, 92, 136, 91, 102, 89}, {104, 114, 108, 124, 136, 123, 102, 121}, }, [PARTY_LAYOUT_MULTI] = { {16, 24, 20, 34, 50, 36, 16, 18}, {16, 80, 20, 90, 50, 92, 16, 74}, {104, 26, 106, 36, 136, 35, 102, 33}, {104, 50, 106, 60, 136, 59, 102, 57}, {104, 82, 106, 92, 136, 91, 102, 89}, {104, 106, 106, 116, 136, 115, 102, 113}, }, [PARTY_LAYOUT_MULTI_SHOWCASE] = { {16, 32, 20, 42, 50, 44, 16, 26}, {104, 34, 106, 44, 136, 43, 102, 41}, {104, 58, 106, 68, 136, 67, 102, 65}, {16, 104, 20, 114, 50, 116, 16, 98}, {104, 106, 106, 116, 136, 115, 102, 113}, {104, 130, 106, 140, 136, 139, 102, 137}, }, }; static const u32 sConfirmButtonPokeball_Tilemap[] = INCBIN_U32("graphics/interface/unknown_6157C4.bin"); static const u32 sCancelButtonPokeball_Tilemap[] = INCBIN_U32("graphics/interface/unknown_6157E0.bin"); static const u8 sFontColorTable[][3] = { {0, 3, 2}, {0, 1, 6}, {0, 11, 12}, {1, 2, 3}, {1, 8, 9}, {0, 1, 2}, }; static const struct WindowTemplate sSinglePartyMenuWindowTemplate[] = { { .bg = 0, .tilemapLeft = 1, .tilemapTop = 3, .width = 10, .height = 7, .paletteNum = 3, .baseBlock = 0x63, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 1, .width = 18, .height = 3, .paletteNum = 4, .baseBlock = 0xA9, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 4, .width = 18, .height = 3, .paletteNum = 5, .baseBlock = 0xDF, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 7, .width = 18, .height = 3, .paletteNum = 6, .baseBlock = 0x115, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 10, .width = 18, .height = 3, .paletteNum = 7, .baseBlock = 0x14B, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 13, .width = 18, .height = 3, .paletteNum = 8, .baseBlock = 0x181, }, { .bg = 2, .tilemapLeft = 1, .tilemapTop = 15, .width = 28, .height = 4, .paletteNum = 14, .baseBlock = 0x1DF, }, DUMMY_WIN_TEMPLATE }; static const struct WindowTemplate sDoublePartyMenuWindowTemplate[] = { { .bg = 0, .tilemapLeft = 1, .tilemapTop = 1, .width = 10, .height = 7, .paletteNum = 3, .baseBlock = 0x63, }, { .bg = 0, .tilemapLeft = 1, .tilemapTop = 8, .width = 10, .height = 7, .paletteNum = 4, .baseBlock = 0xA9, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 1, .width = 18, .height = 3, .paletteNum = 5, .baseBlock = 0xEF, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 5, .width = 18, .height = 3, .paletteNum = 6, .baseBlock = 0x125, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 9, .width = 18, .height = 3, .paletteNum = 7, .baseBlock = 0x15B, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 13, .width = 18, .height = 3, .paletteNum = 8, .baseBlock = 0x191, }, { .bg = 2, .tilemapLeft = 1, .tilemapTop = 15, .width = 28, .height = 4, .paletteNum = 14, .baseBlock = 0x1DF, }, DUMMY_WIN_TEMPLATE }; static const struct WindowTemplate sMultiPartyMenuWindowTemplate[] = { { .bg = 0, .tilemapLeft = 1, .tilemapTop = 1, .width = 10, .height = 7, .paletteNum = 3, .baseBlock = 0x63, }, { .bg = 0, .tilemapLeft = 1, .tilemapTop = 8, .width = 10, .height = 7, .paletteNum = 4, .baseBlock = 0xA9, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 2, .width = 18, .height = 3, .paletteNum = 5, .baseBlock = 0xEF, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 5, .width = 18, .height = 3, .paletteNum = 6, .baseBlock = 0x125, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 9, .width = 18, .height = 3, .paletteNum = 7, .baseBlock = 0x15B, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 12, .width = 18, .height = 3, .paletteNum = 8, .baseBlock = 0x191, }, { .bg = 2, .tilemapLeft = 1, .tilemapTop = 15, .width = 28, .height = 4, .paletteNum = 14, .baseBlock = 0x1DF, }, DUMMY_WIN_TEMPLATE }; static const struct WindowTemplate sShowcaseMultiPartyMenuWindowTemplate[] = { { .bg = 0, .tilemapLeft = 1, .tilemapTop = 2, .width = 10, .height = 7, .paletteNum = 3, .baseBlock = 0x63, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 3, .width = 18, .height = 3, .paletteNum = 5, .baseBlock = 0xA9, }, { .bg = 0, .tilemapLeft = 12, .tilemapTop = 6, .width = 18, .height = 3, .paletteNum = 6, .baseBlock = 0xDF, }, { .bg = 2, .tilemapLeft = 1, .tilemapTop = 11, .width = 10, .height = 7, .paletteNum = 4, .baseBlock = 0x115, }, { .bg = 2, .tilemapLeft = 12, .tilemapTop = 12, .width = 18, .height = 3, .paletteNum = 7, .baseBlock = 0x16B, }, { .bg = 2, .tilemapLeft = 12, .tilemapTop = 15, .width = 18, .height = 3, .paletteNum = 8, .baseBlock = 0x1A1, }, DUMMY_WIN_TEMPLATE }; static const struct WindowTemplate gUnknown_08615908 = { .bg = 0, .tilemapLeft = 24, .tilemapTop = 17, .width = 6, .height = 2, .paletteNum = 3, .baseBlock = 0x1C7, }; static const struct WindowTemplate gUnknown_08615910 = { .bg = 0, .tilemapLeft = 24, .tilemapTop = 18, .width = 6, .height = 2, .paletteNum = 3, .baseBlock = 0x1C7, }; static const struct WindowTemplate gUnknown_08615918 = { .bg = 0, .tilemapLeft = 24, .tilemapTop = 16, .width = 6, .height = 2, .paletteNum = 3, .baseBlock = 0x1D3, }; static const struct WindowTemplate sDefaultPartyMsgWindowTemplate = { .bg = 2, .tilemapLeft = 1, .tilemapTop = 17, .width = 21, .height = 2, .paletteNum = 15, .baseBlock = 0x24F, }; static const struct WindowTemplate sDoWhatWithMonMsgWindowTemplate = { .bg = 2, .tilemapLeft = 1, .tilemapTop = 17, .width = 16, .height = 2, .paletteNum = 15, .baseBlock = 0x279, }; static const struct WindowTemplate sDoWhatWithItemMsgWindowTemplate = { .bg = 2, .tilemapLeft = 1, .tilemapTop = 17, .width = 20, .height = 2, .paletteNum = 15, .baseBlock = 0x299, }; static const struct WindowTemplate sDoWhatWithMailMsgWindowTemplate = { .bg = 2, .tilemapLeft = 1, .tilemapTop = 17, .width = 18, .height = 2, .paletteNum = 15, .baseBlock = 0x299, }; static const struct WindowTemplate sWhichMoveMsgWindowTemplate = { .bg = 2, .tilemapLeft = 1, .tilemapTop = 17, .width = 16, .height = 2, .paletteNum = 15, .baseBlock = 0x299, }; static const struct WindowTemplate sAlreadyHoldingOneMsgWindowTemplate = { .bg = 2, .tilemapLeft = 1, .tilemapTop = 15, .width = 20, .height = 4, .paletteNum = 15, .baseBlock = 0x299, }; static const struct WindowTemplate sItemGiveTakeWindowTemplate = { .bg = 2, .tilemapLeft = 23, .tilemapTop = 13, .width = 6, .height = 6, .paletteNum = 14, .baseBlock = 0x39D, }; static const struct WindowTemplate sMailReadTakeWindowTemplate = { .bg = 2, .tilemapLeft = 21, .tilemapTop = 13, .width = 8, .height = 6, .paletteNum = 14, .baseBlock = 0x39D, }; static const struct WindowTemplate sMoveSelectWindowTemplate = { .bg = 2, .tilemapLeft = 19, .tilemapTop = 11, .width = 10, .height = 8, .paletteNum = 14, .baseBlock = 0x2E9, }; static const struct WindowTemplate sPartyMenuYesNoWindowTemplate = { .bg = 2, .tilemapLeft = 21, .tilemapTop = 9, .width = 5, .height = 4, .paletteNum = 14, .baseBlock = 0x2E9, }; static const struct WindowTemplate sLevelUpStatsWindowTemplate = { .bg = 2, .tilemapLeft = 19, .tilemapTop = 1, .width = 10, .height = 11, .paletteNum = 14, .baseBlock = 0x2E9, }; static const struct WindowTemplate sUnusedWindowTemplate_08615978 = { .bg = 2, .tilemapLeft = 2, .tilemapTop = 15, .width = 27, .height = 4, .paletteNum = 14, .baseBlock = 0x1DF, }; static const struct WindowTemplate sUnusedWindowTemplate_08615980 = { .bg = 2, .tilemapLeft = 0, .tilemapTop = 13, .width = 18, .height = 3, .paletteNum = 12, .baseBlock = 0x39D, }; // Tile nums static const u8 sMainSlotTileNums[] = {24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 40, 59, 60, 58, 58, 58, 58, 58, 58, 61, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 46, 47, 47, 47, 47, 47, 47, 47, 47, 48}; static const u8 sMainSlotTileNums_Egg[] = {24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 40, 41, 41, 41, 41, 41, 41, 41, 41, 42, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 46, 47, 47, 47, 47, 47, 47, 47, 47, 48}; static const u8 sOtherSlotsTileNums[] = {43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 49, 33, 33, 33, 33, 33, 33, 33, 33, 52, 53, 51, 51, 51, 51, 51, 51, 54, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57}; static const u8 sOtherSlotsTileNums_Egg[] = {43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 49, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 50, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57}; static const u8 sEmptySlotTileNums[] = {21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39}; // Palette offsets static const u8 sGenderPalOffsets[] = {11, 12}; static const u8 sHPBarPalOffsets[] = {9, 10}; static const u8 sPartyBoxPalOffsets1[] = {4, 5, 6}; static const u8 sPartyBoxPalOffsets2[] = {1, 7, 8}; static const u8 sPartyBoxNoMonPalOffsets[] = {1, 11, 12}; // Palette ids static const u8 sGenderMalePalIds[] = {59, 60}; static const u8 sGenderFemalePalIds[] = {75, 76}; static const u8 sHPBarGreenPalIds[] = {57, 58}; static const u8 sHPBarYellowPalIds[] = {73, 74}; static const u8 sHPBarRedPalIds[] = {89, 90}; static const u8 sPartyBoxEmptySlotPalIds1[] = {52, 53, 54}; static const u8 sPartyBoxMultiPalIds1[] = {68, 69, 70}; static const u8 sPartyBoxFaintedPalIds1[] = {84, 85, 86}; static const u8 sPartyBoxCurrSelectionPalIds1[] = {116, 117, 118}; static const u8 sPartyBoxCurrSelectionMultiPalIds[] = {132, 133, 134}; static const u8 sPartyBoxCurrSelectionFaintedPalIds[] = {148, 149, 150}; static const u8 sPartyBoxSelectedForActionPalIds1[] = {100, 101, 102}; static const u8 sPartyBoxEmptySlotPalIds2[] = {49, 55, 56}; static const u8 sPartyBoxMultiPalIds2[] = {65, 71, 72}; static const u8 sPartyBoxFaintedPalIds2[] = {81, 87, 88}; static const u8 sPartyBoxCurrSelectionPalIds2[] = {97, 103, 104}; static const u8 sPartyBoxSelectedForActionPalIds2[] = {161, 167, 168}; static const u8 sPartyBoxNoMonPalIds[] = {17, 27, 28}; //TOOD: IDs? static const u8 *const sActionStringTable[] = { gText_ChoosePokemon, gText_ChoosePokemonCancel, gText_ChoosePokemonConfirm, gText_MoveToWhere, gText_TeachWhichPokemon, gText_UseOnWhichPokemon, gText_GiveToWhichPokemon, gText_NothingToCut, gText_CantSurfHere, gText_AlreadySurfing, gText_CurrentIsTooFast, gText_EnjoyCycling, gText_InUseAlready_PM, gText_CantUseHere, gText_NoPokemonForBattle, gText_ChoosePokemon2, gText_NotEnoughHp, gText_PokemonAreNeeded, gText_PokemonCantBeSame, gText_NoIdenticalHoldItems, gText_EmptyString2, gText_DoWhatWithPokemon, gText_RestoreWhichMove, gText_BoostPp, gText_DoWhatWithItem, gText_DoWhatWithMail, gText_AlreadyHoldingOne, }; static const u8 *const sDescriptionStringTable[] = { [PARTYBOX_DESC_NO_USE] = gText_NoUse, [PARTYBOX_DESC_ABLE_3] = gText_Able, [PARTYBOX_DESC_FIRST] = gText_First_PM, [PARTYBOX_DESC_SECOND] = gText_Second_PM, [PARTYBOX_DESC_THIRD] = gText_Third_PM, [PARTYBOX_DESC_FOURTH] = gText_Fourth, [PARTYBOX_DESC_ABLE] = gText_Able2, [PARTYBOX_DESC_NOT_ABLE] = gText_NotAble, [PARTYBOX_DESC_ABLE_2] = gText_Able3, [PARTYBOX_DESC_NOT_ABLE_2] = gText_NotAble2, [PARTYBOX_DESC_LEARNED] = gText_Learned, [PARTYBOX_DESC_HAVE] = gText_Have, [PARTYBOX_DESC_DONT_HAVE] = gText_DontHave, }; static const u16 sUnused_08615B94[] = { 0x0108, 0x0151, 0x0160, 0x015b, 0x002e, 0x005c, 0x0102, 0x0153, 0x014b, 0x00ed, 0x00f1, 0x010d, 0x003a, 0x003b, 0x003f, 0x0071, 0x00b6, 0x00f0, 0x00ca, 0x00db, 0x00da, 0x004c, 0x00e7, 0x0055, 0x0057, 0x0059, 0x00d8, 0x005b, 0x005e, 0x00f7, 0x0118, 0x0068, 0x0073, 0x015f, 0x0035, 0x00bc, 0x00c9, 0x007e, 0x013d, 0x014c, 0x0103, 0x0107, 0x0122, 0x009c, 0x00d5, 0x00a8, 0x00d3, 0x011d, 0x0121, 0x013b, 0x000f, 0x0013, 0x0039, 0x0046, 0x0094, 0x00f9, 0x007f, 0x0123, }; enum { MENU_SUMMARY, MENU_SWITCH, MENU_CANCEL1, MENU_ITEM, MENU_GIVE, MENU_TAKE_ITEM, MENU_MAIL, MENU_TAKE_MAIL, MENU_READ, MENU_CANCEL2, MENU_SHIFT, MENU_SEND_OUT, MENU_ENTER, MENU_NO_ENTRY, MENU_STORE, MENU_REGISTER, MENU_TRADE1, MENU_TRADE2, MENU_TOSS, MENU_FIELD_MOVES, }; enum { FIELD_MOVE_CUT, FIELD_MOVE_FLASH, FIELD_MOVE_ROCK_SMASH, FIELD_MOVE_STRENGTH, FIELD_MOVE_SURF, FIELD_MOVE_FLY, FIELD_MOVE_DIVE, FIELD_MOVE_WATERFALL, FIELD_MOVE_TELEPORT, FIELD_MOVE_DIG, FIELD_MOVE_SECRET_POWER, FIELD_MOVE_MILK_DRINK, FIELD_MOVE_SOFT_BOILED, FIELD_MOVE_SWEET_SCENT, }; // What a weird choice of table termination; #define FIELD_MOVE_TERMINATOR MOVE_SWORDS_DANCE 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_MAIL] = {gText_Mail, CursorCb_Mail}, [MENU_TAKE_MAIL] = {gText_Take2, CursorCb_TakeMail}, [MENU_READ] = {gText_Read2, CursorCb_Read}, [MENU_CANCEL2] = {gText_Cancel2, CursorCb_Cancel2}, [MENU_SHIFT] = {gText_Shift, CursorCb_SendMon}, [MENU_SEND_OUT] = {gText_SendOut, CursorCb_SendMon}, [MENU_ENTER] = {gText_Enter, CursorCb_Enter}, [MENU_NO_ENTRY] = {gText_NoEntry, CursorCb_NoEntry}, [MENU_STORE] = {gText_Store, CursorCb_Store}, [MENU_REGISTER] = {gText_Register, CursorCb_Register}, [MENU_TRADE1] = {gText_Trade4, CursorCb_Trade1}, [MENU_TRADE2] = {gText_Trade4, CursorCb_Trade2}, [MENU_TOSS] = {gMenuText_Toss, CursorCb_Toss}, [MENU_FIELD_MOVES + FIELD_MOVE_CUT] = {gMoveNames[MOVE_CUT], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_FLASH] = {gMoveNames[MOVE_FLASH], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_ROCK_SMASH] = {gMoveNames[MOVE_ROCK_SMASH], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_STRENGTH] = {gMoveNames[MOVE_STRENGTH], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_SURF] = {gMoveNames[MOVE_SURF], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_FLY] = {gMoveNames[MOVE_FLY], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_DIVE] = {gMoveNames[MOVE_DIVE], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_WATERFALL] = {gMoveNames[MOVE_WATERFALL], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_TELEPORT] = {gMoveNames[MOVE_TELEPORT], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_DIG] = {gMoveNames[MOVE_DIG], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_SECRET_POWER] = {gMoveNames[MOVE_SECRET_POWER], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_MILK_DRINK] = {gMoveNames[MOVE_MILK_DRINK], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_SOFT_BOILED] = {gMoveNames[MOVE_SOFT_BOILED], CursorCb_FieldMove}, [MENU_FIELD_MOVES + FIELD_MOVE_SWEET_SCENT] = {gMoveNames[MOVE_SWEET_SCENT], CursorCb_FieldMove}, }; static const u8 sPartyMenuAction_SummarySwitchCancel[] = {MENU_SUMMARY, MENU_SWITCH, MENU_CANCEL1}; static const u8 sPartyMenuAction_ShiftSummaryCancel[] = {MENU_SHIFT, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_SendOutSummaryCancel[] = {MENU_SEND_OUT, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_SummaryCancel[] = {MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_EnterSummaryCancel[] = {MENU_ENTER, MENU_SUMMARY, MENU_CANCEL1}; 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_ReadTakeMailCancel[] = {MENU_READ, MENU_TAKE_MAIL, MENU_CANCEL2}; static const u8 sPartyMenuAction_RegisterSummaryCancel[] = {MENU_REGISTER, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_TradeSummaryCancel1[] = {MENU_TRADE1, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_TradeSummaryCancel2[] = {MENU_TRADE2, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_TakeItemTossCancel[] = {MENU_TAKE_ITEM, MENU_TOSS, MENU_CANCEL1}; // IDs for the action lists that appear when a party mon is selected enum { ACTIONS_NONE, ACTIONS_SWITCH, ACTIONS_SHIFT, ACTIONS_SEND_OUT, ACTIONS_ENTER, ACTIONS_NO_ENTRY, ACTIONS_STORE, ACTIONS_SUMMARY_ONLY, ACTIONS_ITEM, ACTIONS_MAIL, ACTIONS_REGISTER, ACTIONS_TRADE, ACTIONS_SPIN_TRADE, ACTIONS_TAKEITEM_TOSS }; static const u8 *const sPartyMenuActions[] = { [ACTIONS_NONE] = NULL, [ACTIONS_SWITCH] = sPartyMenuAction_SummarySwitchCancel, [ACTIONS_SHIFT] = sPartyMenuAction_ShiftSummaryCancel, [ACTIONS_SEND_OUT] = sPartyMenuAction_SendOutSummaryCancel, [ACTIONS_ENTER] = sPartyMenuAction_EnterSummaryCancel, [ACTIONS_NO_ENTRY] = sPartyMenuAction_NoEntrySummaryCancel, [ACTIONS_STORE] = sPartyMenuAction_StoreSummaryCancel, [ACTIONS_SUMMARY_ONLY] = sPartyMenuAction_SummaryCancel, [ACTIONS_ITEM] = sPartyMenuAction_GiveTakeItemCancel, [ACTIONS_MAIL] = sPartyMenuAction_ReadTakeMailCancel, [ACTIONS_REGISTER] = sPartyMenuAction_RegisterSummaryCancel, [ACTIONS_TRADE] = sPartyMenuAction_TradeSummaryCancel1, [ACTIONS_SPIN_TRADE] = sPartyMenuAction_TradeSummaryCancel2, [ACTIONS_TAKEITEM_TOSS] = sPartyMenuAction_TakeItemTossCancel, }; static const u8 sPartyMenuActionCounts[] = { [ACTIONS_NONE] = 0, [ACTIONS_SWITCH] = ARRAY_COUNT(sPartyMenuAction_SummarySwitchCancel), [ACTIONS_SHIFT] = ARRAY_COUNT(sPartyMenuAction_ShiftSummaryCancel), [ACTIONS_SEND_OUT] = ARRAY_COUNT(sPartyMenuAction_SendOutSummaryCancel), [ACTIONS_ENTER] = ARRAY_COUNT(sPartyMenuAction_EnterSummaryCancel), [ACTIONS_NO_ENTRY] = ARRAY_COUNT(sPartyMenuAction_NoEntrySummaryCancel), [ACTIONS_STORE] = ARRAY_COUNT(sPartyMenuAction_StoreSummaryCancel), [ACTIONS_SUMMARY_ONLY] = ARRAY_COUNT(sPartyMenuAction_SummaryCancel), [ACTIONS_ITEM] = ARRAY_COUNT(sPartyMenuAction_GiveTakeItemCancel), [ACTIONS_MAIL] = ARRAY_COUNT(sPartyMenuAction_ReadTakeMailCancel), [ACTIONS_REGISTER] = ARRAY_COUNT(sPartyMenuAction_RegisterSummaryCancel), [ACTIONS_TRADE] = ARRAY_COUNT(sPartyMenuAction_TradeSummaryCancel1), [ACTIONS_SPIN_TRADE] = ARRAY_COUNT(sPartyMenuAction_TradeSummaryCancel2), [ACTIONS_TAKEITEM_TOSS] = ARRAY_COUNT(sPartyMenuAction_TakeItemTossCancel) }; static const u16 sFieldMoves[] = { MOVE_CUT, MOVE_FLASH, MOVE_ROCK_SMASH, MOVE_STRENGTH, MOVE_SURF, MOVE_FLY, MOVE_DIVE, MOVE_WATERFALL, MOVE_TELEPORT, MOVE_DIG, MOVE_SECRET_POWER, MOVE_MILK_DRINK, MOVE_SOFT_BOILED, MOVE_SWEET_SCENT, FIELD_MOVE_TERMINATOR }; struct { bool8 (*fieldMoveFunc)(void); u8 msgId; } static const sFieldMoveCursorCallbacks[] = { [FIELD_MOVE_CUT] = {SetUpFieldMove_Cut, PARTY_MSG_NOTHING_TO_CUT}, [FIELD_MOVE_FLASH] = {SetUpFieldMove_Flash, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_ROCK_SMASH] = {SetUpFieldMove_RockSmash, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_STRENGTH] = {SetUpFieldMove_Strength, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_SURF] = {SetUpFieldMove_Surf, PARTY_MSG_CANT_SURF_HERE}, [FIELD_MOVE_FLY] = {SetUpFieldMove_Fly, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_DIVE] = {SetUpFieldMove_Dive, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_WATERFALL] = {SetUpFieldMove_Waterfall, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_TELEPORT] = {SetUpFieldMove_Teleport, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_DIG] = {SetUpFieldMove_Dig, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_SECRET_POWER] = {SetUpFieldMove_SecretPower, PARTY_MSG_CANT_USE_HERE}, [FIELD_MOVE_MILK_DRINK] = {SetUpFieldMove_SoftBoiled, PARTY_MSG_NOT_ENOUGH_HP}, [FIELD_MOVE_SOFT_BOILED] = {SetUpFieldMove_SoftBoiled, PARTY_MSG_NOT_ENOUGH_HP}, [FIELD_MOVE_SWEET_SCENT] = {SetUpFieldMove_SweetScent, PARTY_MSG_CANT_USE_HERE}, }; static const u8 *const sUnionRoomTradeMessages[] = { [UR_TRADE_MSG_NOT_MON_PARTNER_WANTS - 1] = gText_NotPkmnOtherTrainerWants, [UR_TRADE_MSG_NOT_EGG - 1] = gText_ThatIsntAnEgg, [UR_TRADE_MSG_MON_CANT_BE_TRADED_1 - 1] = gText_PkmnCantBeTradedNow, [UR_TRADE_MSG_MON_CANT_BE_TRADED_2 - 1] = gText_PkmnCantBeTradedNow, [UR_TRADE_MSG_PARTNERS_MON_CANT_BE_TRADED - 1] = gText_OtherTrainersPkmnCantBeTraded, [UR_TRADE_MSG_EGG_CANT_BE_TRADED -1] = gText_EggCantBeTradedNow, [UR_TRADE_MSG_PARTNER_CANT_ACCEPT_MON - 1] = gText_OtherTrainerCantAcceptPkmn, [UR_TRADE_MSG_CANT_TRADE_WITH_PARTNER_1 - 1] = gText_CantTradeWithTrainer, [UR_TRADE_MSG_CANT_TRADE_WITH_PARTNER_2 - 1] = gText_CantTradeWithTrainer, }; static const u32 sHeldItemGfx[] = INCBIN_U32("graphics/interface/hold_icons.4bpp"); static const u16 sHeldItemPalette[] = INCBIN_U16("graphics/interface/hold_icons.gbapal"); static const struct OamData sOamData_HeldItem = { .y = 0, .affineMode = 0, .objMode = 0, .mosaic = 0, .bpp = 0, .shape = SPRITE_SHAPE(8x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(8x8), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0, }; static const union AnimCmd sSpriteAnim_HeldItem[] = { ANIMCMD_FRAME(0, 1), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_HeldMail[] = { ANIMCMD_FRAME(1, 1), ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_HeldItem[] = { sSpriteAnim_HeldItem, sSpriteAnim_HeldMail, }; static const struct SpriteSheet sSpriteSheet_HeldItem = { sHeldItemGfx, sizeof(sHeldItemGfx), 0xd750 }; static const struct SpritePalette sSpritePalette_HeldItem = { sHeldItemPalette, 0xd750 }; static const struct SpriteTemplate sSpriteTemplate_HeldItem = { 0xd750, 0xd750, &sOamData_HeldItem, sSpriteAnimTable_HeldItem, NULL, gDummySpriteAffineAnimTable, SpriteCallbackDummy }; static const struct OamData sOamData_MenuPokeball = { .y = 0, .affineMode = 0, .objMode = 0, .mosaic = 0, .bpp = 0, .shape = SPRITE_SHAPE(32x32), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x32), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0 }; static const union AnimCmd sPokeballAnim_Closed[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; static const union AnimCmd sPokeballAnim_Open[] = { ANIMCMD_FRAME(16, 0), ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_Pokeball[] = { sPokeballAnim_Closed, sPokeballAnim_Open }; static const struct CompressedSpriteSheet sSpriteSheet_MenuPokeball = { gPartyMenuPokeball_Gfx, 0x400, 0x04b0 }; static const struct CompressedSpritePalette sSpritePalette_MenuPokeball = { gPartyMenuPokeball_Pal, 0x04b0 }; // TODO: menu pokeball is vague, these are mon menu pokeballs, but not confirm/cancel pokeballs static const struct SpriteTemplate sSpriteTemplate_MenuPokeball = { .tileTag = 0x04b0, .paletteTag = 0x04b0, .oam = &sOamData_MenuPokeball, .anims = sSpriteAnimTable_Pokeball, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct OamData sOamData_8615F20 = { .y = 0, .affineMode = 0, .objMode = 0, .mosaic = 0, .bpp = 0, .shape = SPRITE_SHAPE(16x16), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(16x16), .tileNum = 0, .priority = 2, .paletteNum = 0, .affineParam = 0 }; static const union AnimCmd sSpriteAnim_8615F28[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_8615F30[] = { ANIMCMD_FRAME(4, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_8615F38[] = { ANIMCMD_FRAME(8, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_8615F40[] = { ANIMCMD_FRAME(12, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_8615F48[] = { ANIMCMD_FRAME(16, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_8615F50[] = { ANIMCMD_FRAME(20, 0), ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_8615F58[] = { sSpriteAnim_8615F28, sSpriteAnim_8615F30, sSpriteAnim_8615F38, sSpriteAnim_8615F40, sSpriteAnim_8615F48, sSpriteAnim_8615F50 }; static const struct CompressedSpriteSheet sSpriteSheet_MenuPokeballSmall = { gPartyMenuPokeballSmall_Gfx, 0x0300, 0x04b1 }; static const struct SpriteTemplate gSpriteTemplate_8615F78 = { .tileTag = 1201, .paletteTag = 1200, .oam = &sOamData_8615F20, .anims = sSpriteAnimTable_8615F58, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct OamData sOamData_StatusCondition = { .y = 0, .affineMode = 0, .objMode = 0, .mosaic = 0, .bpp = 0, .shape = SPRITE_SHAPE(32x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x8), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0 }; static const union AnimCmd sSpriteAnim_StatusPoison[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_StatusParalyzed[] = { ANIMCMD_FRAME(4, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_StatusSleep[] = { ANIMCMD_FRAME(8, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_StatusFrozen[] = { ANIMCMD_FRAME(12, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_StatusBurn[] = { ANIMCMD_FRAME(16, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_StatusPokerus[] = { ANIMCMD_FRAME(20, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_StatusFaint[] = { ANIMCMD_FRAME(24, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_Blank[] = { ANIMCMD_FRAME(28, 0), ANIMCMD_END }; static const union AnimCmd *const sSpriteTemplate_StatusCondition[] = { sSpriteAnim_StatusPoison, sSpriteAnim_StatusParalyzed, sSpriteAnim_StatusSleep, sSpriteAnim_StatusFrozen, sSpriteAnim_StatusBurn, sSpriteAnim_StatusPokerus, sSpriteAnim_StatusFaint, sSpriteAnim_Blank }; static const struct CompressedSpriteSheet sSpriteSheet_StatusIcons = { gStatusGfx_Icons, 0x400, 1202 }; static const struct CompressedSpritePalette sSpritePalette_StatusIcons = { gStatusPal_Icons, 1202 }; static const struct SpriteTemplate sSpriteTemplate_StatusIcons = { .tileTag = 1202, .paletteTag = 1202, .oam = &sOamData_StatusCondition, .anims = sSpriteTemplate_StatusCondition, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; // Mask for the partners party in a multi battle // TRUE if in the partners party, FALSE otherwise // The last two slots after the 6 party members are Confirm/Cancel TODO: this doesnt make sense, if confirm/cancel are present the partners party isnt either static const bool8 sMultiBattlePartnersPartyMask[PARTY_SIZE + 2] = { FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE }; static const u8 *const sUnused_StatStrings[] = { gText_HP4, gText_Attack3, gText_Defense3, gText_SpAtk4, gText_SpDef4, gText_Speed2 }; static const u16 sTMHMMoves[] = { MOVE_FOCUS_PUNCH, MOVE_DRAGON_CLAW, MOVE_WATER_PULSE, MOVE_CALM_MIND, MOVE_ROAR, MOVE_TOXIC, MOVE_HAIL, MOVE_BULK_UP, MOVE_BULLET_SEED, MOVE_HIDDEN_POWER, MOVE_SUNNY_DAY, MOVE_TAUNT, MOVE_ICE_BEAM, MOVE_BLIZZARD, MOVE_HYPER_BEAM, MOVE_LIGHT_SCREEN, MOVE_PROTECT, MOVE_RAIN_DANCE, MOVE_GIGA_DRAIN, MOVE_SAFEGUARD, MOVE_FRUSTRATION, MOVE_SOLAR_BEAM, MOVE_IRON_TAIL, MOVE_THUNDERBOLT, MOVE_THUNDER, MOVE_EARTHQUAKE, MOVE_RETURN, MOVE_DIG, MOVE_PSYCHIC, MOVE_SHADOW_BALL, MOVE_BRICK_BREAK, MOVE_DOUBLE_TEAM, MOVE_REFLECT, MOVE_SHOCK_WAVE, MOVE_FLAMETHROWER, MOVE_SLUDGE_BOMB, MOVE_SANDSTORM, MOVE_FIRE_BLAST, MOVE_ROCK_TOMB, MOVE_AERIAL_ACE, MOVE_TORMENT, MOVE_FACADE, MOVE_SECRET_POWER, MOVE_REST, MOVE_ATTRACT, MOVE_THIEF, MOVE_STEEL_WING, MOVE_SKILL_SWAP, MOVE_SNATCH, MOVE_OVERHEAT, MOVE_CUT, MOVE_FLY, MOVE_SURF, MOVE_STRENGTH, MOVE_FLASH, MOVE_ROCK_SMASH, MOVE_WATERFALL, MOVE_DIVE, }; // code static 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->unk8_1 = 0; sPartyMenuInternal->spriteIdConfirmPokeball = 0x7F; sPartyMenuInternal->spriteIdCancelPokeball = 0x7F; 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 < ARRAY_COUNT(sPartyMenuInternal->data); i++) sPartyMenuInternal->data[i] = 0; for (i = 0; i < ARRAY_COUNT(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 = 0; CalculatePlayerPartyCount(); SetMainCallback2(CB2_InitPartyMenu); } } static void CB2_UpdatePartyMenu(void) { RunTasks(); AnimateSprites(); BuildOamBuffer(); do_scheduled_bg_tilemap_copies_to_vram(); UpdatePaletteFade(); } static void VBlankCB_PartyMenu(void) { LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); } static void CB2_InitPartyMenu(void) { while (TRUE) { if (sub_81221EC() == TRUE || ShowPartyMenu() == TRUE || sub_81221AC() == TRUE) break; } } static bool8 ShowPartyMenu(void) { switch (gMain.state) { case 0: SetVBlankHBlankCallbacksToNull(); ResetVramOamAndBgCntRegs(); clear_scheduled_bg_copies_to_vram(); 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 (!sub_81221AC()) 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 (party_menu_add_per_mon_objects()) { 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: sub_81B2428(sPartyMenuInternal->chooseHalf); gMain.state++; break; case 19: gMain.state++; break; case 20: CreateTask(sPartyMenuInternal->task, 0); DisplayPartyMenuStdMessage(sPartyMenuInternal->messageId); gMain.state++; break; case 21: BlendPalettes(0xFFFFFFFF, 16, 0); gPaletteFade.bufferTransferDisabled = FALSE; gMain.state++; break; case 22: BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB_BLACK); gMain.state++; break; default: SetVBlankCallback(VBlankCB_PartyMenu); SetMainCallback2(CB2_UpdatePartyMenu); return TRUE; } return FALSE; } static void ExitPartyMenu(void) { BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK); CreateTask(Task_ExitPartyMenu, 0); SetVBlankCallback(VBlankCB_PartyMenu); SetMainCallback2(CB2_UpdatePartyMenu); } static void Task_ExitPartyMenu(u8 taskId) { if (!gPaletteFade.active) { SetMainCallback2(gPartyMenu.exitCallback); FreePartyPointers(); DestroyTask(taskId); } } static void ResetPartyMenu(void) { sPartyMenuInternal = NULL; sPartyBgTilemapBuffer = NULL; sPartyMenuBoxes = NULL; sPartyBgGfxTilemap = NULL; } static bool8 AllocPartyMenuBg(void) { sPartyBgTilemapBuffer = Alloc(0x800); if (sPartyBgTilemapBuffer == NULL) return FALSE; memset(sPartyBgTilemapBuffer, 0, 0x800); ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(0, sPartyMenuBgTemplates, ARRAY_COUNT(sPartyMenuBgTemplates)); SetBgTilemapBuffer(1, sPartyBgTilemapBuffer); ResetAllBgsCoordinates(); schedule_bg_copy_tilemap_to_vram(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; } static bool8 AllocPartyMenuBgGfx(void) { u32 sizeout; switch (sPartyMenuInternal->data[0]) { case 0: sPartyBgGfxTilemap = malloc_and_decompress(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; } static void PartyPaletteBufferCopy(u8 offset) { offset *= 16; CpuCopy16(&gPlttBufferUnfaded[0x30], &gPlttBufferUnfaded[offset], 32); CpuCopy16(&gPlttBufferUnfaded[0x30], &gPlttBufferFaded[offset], 32); } static void FreePartyPointers(void) { if (sPartyMenuInternal) Free(sPartyMenuInternal); if (sPartyBgTilemapBuffer) Free(sPartyBgTilemapBuffer); if (sPartyBgGfxTilemap) Free(sPartyBgGfxTilemap); if (sPartyMenuBoxes) Free(sPartyMenuBoxes); FreeAllWindowBuffers(); } static 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; sPartyMenuBoxes[i].monSpriteId = 0xFF; sPartyMenuBoxes[i].itemSpriteId = 0xFF; sPartyMenuBoxes[i].pokeballSpriteId = 0xFF; sPartyMenuBoxes[i].statusSpriteId = 0xFF; } // 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]; } static void RenderPartyMenuBox(u8 slot) { if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_MULTI_BATTLE && slot > 2) { DisplayPartyPokemonDataForMultiBattle(slot); if (gMultiPartnerParty[slot - 3].species == SPECIES_NONE) LoadPartyBoxPalette(&sPartyMenuBoxes[slot], PARTY_PAL_NO_MON); else LoadPartyBoxPalette(&sPartyMenuBoxes[slot], PARTY_PAL_MULTI_ALT); CopyWindowToVram(sPartyMenuBoxes[slot].windowId, 2); PutWindowTilemap(sPartyMenuBoxes[slot].windowId); schedule_bg_copy_tilemap_to_vram(2); } else { if (GetMonData(&gPlayerParty[slot], MON_DATA_SPECIES) == SPECIES_NONE) { DrawEmptySlot(sPartyMenuBoxes[slot].windowId); LoadPartyBoxPalette(&sPartyMenuBoxes[slot], PARTY_PAL_NO_MON); CopyWindowToVram(sPartyMenuBoxes[slot].windowId, 2); } else { if (gPartyMenu.menuType == PARTY_MENU_TYPE_MOVE_RELEARNER) DisplayPartyPokemonDataForRelearner(slot); else if (gPartyMenu.menuType == PARTY_MENU_TYPE_CONTEST) DisplayPartyPokemonDataForContest(slot); else if (gPartyMenu.menuType == PARTY_MENU_TYPE_CHOOSE_HALF) DisplayPartyPokemonDataForChooseHalf(slot); else if (gPartyMenu.menuType == PARTY_MENU_TYPE_MINIGAME) DisplayPartyPokemonDataForWirelessMinigame(slot); else if (gPartyMenu.menuType == PARTY_MENU_TYPE_STORE_PYRAMID_HELD_ITEMS) DisplayPartyPokemonDataForBattlePyramidHeldItem(slot); else if (!DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(slot)) DisplayPartyPokemonData(slot); if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_MULTI_BATTLE) AnimatePartySlot(slot, 0); else if (gPartyMenu.slotId == slot) AnimatePartySlot(slot, 1); else AnimatePartySlot(slot, 0); } PutWindowTilemap(sPartyMenuBoxes[slot].windowId); schedule_bg_copy_tilemap_to_vram(0); } } static 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]); } } static 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); } static void DisplayPartyPokemonDataForChooseHalf(u8 slot) { u8 i; struct Pokemon *mon = &gPlayerParty[slot]; u8 *order = gSelectedOrderFromParty; if (!GetBattleEntryEligibility(mon)) { DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE); return; } else { for (i = 0; i < GetMaxBattleEntries(); i++) { if (order[i] != 0 && (order[i] - 1) == slot) { DisplayPartyPokemonDescriptionData(slot, i + PARTYBOX_DESC_FIRST); return; } } DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE_3); } } static void DisplayPartyPokemonDataForContest(u8 slot) { switch (GetContestEntryEligibility(&gPlayerParty[slot])) { case CANT_ENTER_CONTEST: case CANT_ENTER_CONTEST_EGG: case CANT_ENTER_CONTEST_FAINTED: DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE); break; case CAN_ENTER_CONTEST_EQUAL_RANK: case CAN_ENTER_CONTEST_HIGH_RANK: DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE); break; } } static void DisplayPartyPokemonDataForRelearner(u8 slot) { if (GetNumberOfRelearnableMoves(&gPlayerParty[slot]) == 0) DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE_2); else DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE_2); } static void DisplayPartyPokemonDataForWirelessMinigame(u8 slot) { if (IsMonAllowedInMinigame(slot) == TRUE) DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE); else DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE); } static void DisplayPartyPokemonDataForBattlePyramidHeldItem(u8 slot) { if (GetMonData(&gPlayerParty[slot], MON_DATA_HELD_ITEM)) DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_HAVE); else DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_DONT_HAVE); } // Returns TRUE if teaching move or cant evolve with item (i.e. description data is shown), FALSE otherwise static bool8 DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(u8 slot) { struct Pokemon *currentPokemon = &gPlayerParty[slot]; u16 item = gSpecialVar_ItemId; if (gPartyMenu.action == PARTY_ACTION_MOVE_TUTOR) { gSpecialVar_Result = 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; } static 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; } } // TODO: sort out unknowns static 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); sub_81DB52C(gStringVar1); 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); } } static bool8 RenderPartyMenuBoxes(void) { RenderPartyMenuBox(sPartyMenuInternal->data[0]); if (++sPartyMenuInternal->data[0] == PARTY_SIZE) return TRUE; else return FALSE; } static u8* GetPartyMenuBgTile(u16 tileId) { return &sPartyBgGfxTilemap[tileId << 5]; } //TODO: rename and all related functions static void party_menu_add_per_mon_objects_internal(u8 slot) { u8 actualSlot; if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_MULTI_BATTLE && slot > 2) { u8 status; actualSlot = slot - 3; if (gMultiPartnerParty[actualSlot].species != SPECIES_NONE) { party_menu_link_mon_icon_anim(gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].personality, &sPartyMenuBoxes[slot], 0, 0); party_menu_link_mon_held_item_object(gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].heldItem, &sPartyMenuBoxes[slot]); party_menu_link_mon_pokeball_object(gMultiPartnerParty[actualSlot].species, &sPartyMenuBoxes[slot]); if (gMultiPartnerParty[actualSlot].hp == 0) status = AILMENT_FNT; else status = GetAilmentFromStatus(gMultiPartnerParty[actualSlot].status); party_menu_link_mon_status_condition_object(gMultiPartnerParty[actualSlot].species, status, &sPartyMenuBoxes[slot]); } } else if (GetMonData(&gPlayerParty[slot], MON_DATA_SPECIES) != SPECIES_NONE) { party_menu_icon_anim(&gPlayerParty[slot], &sPartyMenuBoxes[slot], slot); party_menu_held_item_object(&gPlayerParty[slot], &sPartyMenuBoxes[slot]); party_menu_pokeball_object(&gPlayerParty[slot], &sPartyMenuBoxes[slot]); party_menu_status_condition_object(&gPlayerParty[slot], &sPartyMenuBoxes[slot]); } } static bool8 party_menu_add_per_mon_objects(void) { party_menu_add_per_mon_objects_internal(sPartyMenuInternal->data[0]); if (++sPartyMenuInternal->data[0] == PARTY_SIZE) return TRUE; else return FALSE; } // TODO Fix name, first if doesnt do that static void CreateCancelConfirmPokeballSprites(void) { if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_MULTI_BATTLE) { FillBgTilemapBufferRect(1, 14, 23, 17, 7, 2, 1); } else { if (sPartyMenuInternal->chooseHalf) { sPartyMenuInternal->spriteIdConfirmPokeball = CreateMenuPokeballSprite(0xBF, 0x88); sub_81B120C(); sPartyMenuInternal->spriteIdCancelPokeball = CreateMenuPokeballSprite(0xBF, 0x98); } else { sPartyMenuInternal->spriteIdCancelPokeball = CreateCancelPokeballSprite(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: if (animNum == 0) sub_8199C30(1, 23, 16, 7, 2, 1); else sub_8199C30(1, 23, 16, 7, 2, 2); spriteId = sPartyMenuInternal->spriteIdConfirmPokeball; break; case PARTY_SIZE + 1: // Cancel if (!sPartyMenuInternal->chooseHalf) { if (animNum == 0) sub_8199C30(1, 23, 17, 7, 2, 1); else sub_8199C30(1, 23, 17, 7, 2, 2); } else if (animNum == 0) { sub_8199C30(1, 23, 18, 7, 2, 1); } else { sub_8199C30(1, 23, 18, 7, 2, 2); } spriteId = sPartyMenuInternal->spriteIdCancelPokeball; break; } PartyMenuStartSpriteAnim(spriteId, animNum); schedule_bg_copy_tilemap_to_vram(1); } static 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 (PartyBoxPal_ParnterOrDisqualifiedInArena(slot) == TRUE) 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; } static bool8 PartyBoxPal_ParnterOrDisqualifiedInArena(u8 slot) { if (gPartyMenu.layout == PARTY_LAYOUT_MULTI && (slot == 1 || slot == 4 || slot == 5)) return TRUE; if (slot < 3 && (gBattleTypeFlags & BATTLE_TYPE_ARENA) && gMain.inBattle && (gBattleStruct->arenaLostPlayerMons >> GetPartyIdFromBattleSlot(slot) & 1)) return TRUE; return FALSE; } //TODO: tilemaps for confirm/cancel buttons in choose 3, double check static void sub_81B120C(void) { CopyToBgTilemapBufferRect_ChangePalette(1, sConfirmButtonPokeball_Tilemap, 23, 16, 7, 2, 17); CopyToBgTilemapBufferRect_ChangePalette(1, sCancelButtonPokeball_Tilemap, 23, 18, 7, 2, 17); schedule_bg_copy_tilemap_to_vram(1); } bool8 IsMultiBattle(void) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattleTypeFlags & BATTLE_TYPE_TRAINER && gMain.inBattle) return TRUE; else return FALSE; } static void SwapPartyPokemon(struct Pokemon *mon1, struct Pokemon *mon2) { struct Pokemon *temp = Alloc(sizeof(struct Pokemon)); *temp = *mon1; *mon1 = *mon2; *mon2 = *temp; Free(temp); } static void Task_ClosePartyMenu(u8 taskId) { BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK); gTasks[taskId].func = Task_ClosePartyMenuAndSetCB2; } static 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); ResetSpriteData(); FreePartyPointers(); DestroyTask(taskId); } } u8 GetCursorSelectionMonId(void) { return gPartyMenu.slotId; } u8 GetPartyMenuType(void) { return gPartyMenu.menuType; } void Task_HandleChooseMonInput(u8 taskId) { if (!gPaletteFade.active && sub_81221EC() != 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); sub_81B4F88(); } break; } } } static s8* GetCurrentPartySlotPtr(void) { if (gPartyMenu.action == PARTY_ACTION_SWITCH || gPartyMenu.action == PARTY_ACTION_SOFTBOILED) return &gPartyMenu.slotId2; else return &gPartyMenu.slotId; } static 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)) { PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); 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; PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); gItemUseCB(taskId, Task_ClosePartyMenuAfterText); } break; case PARTY_ACTION_MOVE_TUTOR - 3: if (IsSelectedMonNotEgg((u8*)slotPtr)) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); TryTutorSelectedMon(taskId); } break; case PARTY_ACTION_GIVE_MAILBOX_MAIL - 3: if (IsSelectedMonNotEgg((u8*)slotPtr)) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); TryGiveMailToSelectedMon(taskId); } break; case PARTY_ACTION_GIVE_ITEM - 3: case PARTY_ACTION_GIVE_PC_ITEM - 3: if (IsSelectedMonNotEgg((u8*)slotPtr)) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); TryGiveItemToSelectedMon(taskId); } break; case PARTY_ACTION_SWITCH - 3: PlaySE(SE_SELECT); SwitchSelectedMons(taskId); break; case PARTY_ACTION_CHOOSE_AND_CLOSE - 3: PlaySE(SE_SELECT); 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; } } } static bool8 IsSelectedMonNotEgg(u8 *slotPtr) { if (GetMonData(&gPlayerParty[*slotPtr], MON_DATA_IS_EGG) == TRUE) { PlaySE(SE_HAZURE); return FALSE; } return TRUE; } static 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 (DisplayCancelChooseMonYesNo(taskId) != TRUE) { if (!sub_81221AC()) gSpecialVar_0x8004 = PARTY_SIZE + 1; gUnknown_0203CEE8 = FALSE; *slotPtr = PARTY_SIZE + 1; Task_ClosePartyMenu(taskId); } break; } } static bool8 DisplayCancelChooseMonYesNo(u8 taskId) { const u8* stringPtr = NULL; if (gPartyMenu.menuType == PARTY_MENU_TYPE_CONTEST) stringPtr = gText_CancelParticipation; else if (gPartyMenu.menuType == PARTY_MENU_TYPE_CHOOSE_HALF) stringPtr = GetFacilityCancelString(); if (stringPtr == NULL) return FALSE; PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); StringExpandPlaceholders(gStringVar4, stringPtr); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_CancelChooseMonYesNo; return TRUE; } static void Task_CancelChooseMonYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleCancelChooseMonYesNoInput; } } static void Task_HandleCancelChooseMonYesNoInput(u8 taskId) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: gUnknown_0203CEE8 = 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; } } static u16 PartyMenuButtonHandler(s8 *slotPtr) { s8 movementDir; switch (gMain.newAndRepeatedKeys) { case DPAD_UP: movementDir = -1; break; case DPAD_DOWN: movementDir = 1; break; case DPAD_LEFT: movementDir = -2; break; case DPAD_RIGHT: movementDir = 2; break; default: switch (GetLRKeysPressedAndHeld()) { case MENU_L_PRESSED: movementDir = -1; break; case MENU_R_PRESSED: movementDir = 1; break; default: movementDir = 0; break; } break; } if (gMain.newKeys & START_BUTTON) return 8; if (movementDir) { UpdateCurrentPartySelection(slotPtr, movementDir); return 0; } // Pressed Cancel if ((gMain.newKeys & A_BUTTON) && *slotPtr == PARTY_SIZE + 1) return 2; return gMain.newKeys & (A_BUTTON | B_BUTTON); } static void UpdateCurrentPartySelection(s8 *slotPtr, s8 movementDir) { s8 newSlotId = *slotPtr; u8 layout = gPartyMenu.layout; if (layout == PARTY_LAYOUT_SINGLE) UpdatePartySelectionSingleLayout(slotPtr, movementDir); else // remaining double/multi layouts handle movement the same way UpdatePartySelectionDoubleLayout(slotPtr, movementDir); if (*slotPtr != newSlotId) { PlaySE(SE_SELECT); AnimatePartySlot(newSlotId, 0); AnimatePartySlot(*slotPtr, 1); } } // TODO: clean up static void UpdatePartySelectionSingleLayout(s8 *slotPtr, s8 movementDir) { // PARTY_SIZE + 1 is Cancel, the 7th party menu selection switch (movementDir) { case -1: 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 1: 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 2: if (gPlayerPartyCount != 1 && *slotPtr == 0) { if (sPartyMenuInternal->unk8_1 == 0) *slotPtr = 1; else *slotPtr = sPartyMenuInternal->unk8_1; } break; case -2: if (*slotPtr != 0 && *slotPtr != PARTY_SIZE && *slotPtr != PARTY_SIZE + 1) { sPartyMenuInternal->unk8_1 = *slotPtr; *slotPtr = 0; } break; } } // TODO: clean up static void UpdatePartySelectionDoubleLayout(s8 *slotPtr, s8 movementDir) { // PARTY_SIZE + 1 is Cancel, the 7th party menu selection s8 unk2 = movementDir; switch (movementDir) { case -1: 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)--; } unk2 = sub_81B1B00(*slotPtr, unk2); if (unk2 != -1) *slotPtr = unk2; break; case 1: if (*slotPtr == PARTY_SIZE) { *slotPtr = PARTY_SIZE + 1; } else if (*slotPtr == PARTY_SIZE + 1) { *slotPtr = 0; } else { unk2 = sub_81B1B00(*slotPtr, 1); if (unk2 == -1) { if (sPartyMenuInternal->chooseHalf) *slotPtr = PARTY_SIZE; else *slotPtr = PARTY_SIZE + 1; } else { *slotPtr = unk2; } } break; case 2: if (*slotPtr == 0) { if (sPartyMenuInternal->unk8_1 == 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->unk8_1 == 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 -2: if (*slotPtr == 2 || *slotPtr == 3) { sPartyMenuInternal->unk8_1 = *slotPtr; *slotPtr = 0; } else if (*slotPtr == 4 || *slotPtr == 5) { sPartyMenuInternal->unk8_1 = *slotPtr; *slotPtr = 1; } break; } } static s8 sub_81B1B00(s8 slotId, s8 b) { while (TRUE) { slotId += b; 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; } static void Task_PrintAndWaitForText(u8 taskId) { if (RunTextPrintersRetIsActive(6) != TRUE) { if (gTasks[taskId].tKeepOpen == FALSE) { ClearStdWindowAndFrameToTransparent(6, 0); ClearWindowTilemap(6); } DestroyTask(taskId); } } #undef tKeepOpen bool8 IsPartyMenuTextPrinterActive(void) { return FuncIsActiveTask(Task_PrintAndWaitForText); } static void Task_WaitForLinkAndReturnToChooseMon(u8 taskId) { if (sub_81221EC() != TRUE) { DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON); gTasks[taskId].func = Task_HandleChooseMonInput; } } static void Task_ReturnToChooseMonAfterText(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { ClearStdWindowAndFrameToTransparent(6, 0); ClearWindowTilemap(6); if (sub_81221AC() == TRUE) { gTasks[taskId].func = Task_WaitForLinkAndReturnToChooseMon; } else { DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON); gTasks[taskId].func = Task_HandleChooseMonInput; } } } static void DisplayGaveHeldItemMessage(struct Pokemon *mon, u16 item, bool8 keepOpen, u8 unused) { GetMonNickname(mon, gStringVar1); CopyItemName(item, gStringVar2); StringExpandPlaceholders(gStringVar4, gText_PkmnWasGivenItem); DisplayPartyMenuMessage(gStringVar4, keepOpen); schedule_bg_copy_tilemap_to_vram(2); } static void DisplayTookHeldItemMessage(struct Pokemon *mon, u16 item, bool8 keepOpen) { GetMonNickname(mon, gStringVar1); CopyItemName(item, gStringVar2); StringExpandPlaceholders(gStringVar4, gText_ReceivedItemFromPkmn); DisplayPartyMenuMessage(gStringVar4, keepOpen); schedule_bg_copy_tilemap_to_vram(2); } static void DisplayAlreadyHoldingItemSwitchMessage(struct Pokemon *mon, u16 item, bool8 keepOpen) { GetMonNickname(mon, gStringVar1); CopyItemName(item, gStringVar2); StringExpandPlaceholders(gStringVar4, gText_PkmnAlreadyHoldingItemSwitch); DisplayPartyMenuMessage(gStringVar4, keepOpen); schedule_bg_copy_tilemap_to_vram(2); } static void DisplaySwitchedHeldItemMessage(u16 item, u16 item2, bool8 keepOpen) { CopyItemName(item, gStringVar1); CopyItemName(item2, gStringVar2); StringExpandPlaceholders(gStringVar4, gText_SwitchedPkmnItem); DisplayPartyMenuMessage(gStringVar4, keepOpen); schedule_bg_copy_tilemap_to_vram(2); } static 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); } static 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; } static void BufferBagFullCantTakeItemMessage(u16 itemUnused) { 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] static 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 followUpFunc) { 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, followUpFunc); } // The usage of hp in this function is mostly nonsense // Because caseId is always passed 0, none of the other cases ever occur static 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; } static void SetPartyMonsAllowedInMinigame(void) { u16 *ptr; if (gPartyMenu.menuType == PARTY_MENU_TYPE_MINIGAME) { u8 i; ptr = &gPartyMenu.unkE; gPartyMenu.unkE = 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; } } } static bool16 IsMonAllowedInPokemonJump(struct Pokemon *mon) { if (GetMonData(mon, MON_DATA_IS_EGG) != TRUE && IsSpeciesAllowedInPokemonJump(GetMonData(mon, MON_DATA_SPECIES))) return TRUE; return FALSE; } static bool16 IsMonAllowedInDodrioBerryPicking(struct Pokemon *mon) { if (GetMonData(mon, MON_DATA_IS_EGG) != TRUE && GetMonData(mon, MON_DATA_SPECIES) == SPECIES_DODRIO) return TRUE; return FALSE; } static bool8 IsMonAllowedInMinigame(u8 slot) { if (!((gPartyMenu.unkE >> slot) & 1)) return FALSE; return TRUE; } static 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); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } } static void CancelParticipationPrompt(u8 taskId) { DisplayPartyMenuMessage(gText_CancelParticipation, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_CancelParticipationYesNo; } static void Task_CancelParticipationYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleCancelParticipationYesNoInput; } } static 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; } } static 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); // :morphon: } 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; } static u16 GetTutorMove(u8 tutor) { return gTutorMoves[tutor]; } static bool8 CanLearnTutorMove(u16 species, u8 tutor) { if (sTutorLearnsets[species] & (1 << tutor)) return TRUE; else return FALSE; } static void InitPartyMenuWindows(u8 layout) { u8 i; switch (layout) { case PARTY_LAYOUT_SINGLE: InitWindows(sSinglePartyMenuWindowTemplate); break; case PARTY_LAYOUT_DOUBLE: InitWindows(sDoublePartyMenuWindowTemplate); break; case PARTY_LAYOUT_MULTI: InitWindows(sMultiPartyMenuWindowTemplate); break; default: // PARTY_LAYOUT_MULTI_SHOWCASE InitWindows(sShowcaseMultiPartyMenuWindowTemplate); break; } DeactivateAllTextPrinters(); for (i = 0; i < PARTY_SIZE; i++) FillWindowPixelBuffer(i, PIXEL_FILL(0)); LoadUserWindowBorderGfx(0, 0x4F, 0xD0); LoadPalette(GetOverworldTextboxPalettePtr(), 0xE0, 0x20); LoadPalette(gUnknown_0860F074, 0xF0, 0x20); } // TODO static void sub_81B2428(bool8 chooseHalf) { u8 firstWindowId; u8 windowId; u8 offset; u8 mainOffset; if (gPartyMenu.menuType != PARTY_MENU_TYPE_IN_MULTI_BATTLE) { if (chooseHalf == TRUE) { firstWindowId = AddWindow(&gUnknown_08615918); FillWindowPixelBuffer(firstWindowId, PIXEL_FILL(0)); mainOffset = GetStringCenterAlignXOffset(0, gMenuText_Confirm, 48); AddTextPrinterParameterized4(firstWindowId, 0, mainOffset, 1, 0, 0, sFontColorTable[0], -1, gMenuText_Confirm); PutWindowTilemap(firstWindowId); CopyWindowToVram(firstWindowId, 2); windowId = AddWindow(&gUnknown_08615910); offset = 0; } else { windowId = AddWindow(&gUnknown_08615908); offset = 3; } FillWindowPixelBuffer(windowId, PIXEL_FILL(0)); if (gPartyMenu.menuType != PARTY_MENU_TYPE_SPIN_TRADE) { mainOffset = GetStringCenterAlignXOffset(0, gText_Cancel, 48); AddTextPrinterParameterized3(windowId, 0, mainOffset + offset, 1, sFontColorTable[0], -1, gText_Cancel); } else { // Identical to above, Spin Trade wasnt implemented mainOffset = GetStringCenterAlignXOffset(0, gText_Cancel2, 48); AddTextPrinterParameterized3(windowId, 0, mainOffset + offset, 1, sFontColorTable[0], -1, gText_Cancel2); } PutWindowTilemap(windowId); CopyWindowToVram(windowId, 2); schedule_bg_copy_tilemap_to_vram(0); } } static u16* GetPartyMenuPalBufferPtr(u8 paletteId) { return &sPartyMenuInternal->palBuffer[paletteId]; } static void BlitBitmapToPartyWindow(u8 windowId, const u8 *b, u8 c, u8 x, u8 y, u8 width, u8 height) { u8 *pixels = AllocZeroed(height * width * 32); u8 i, j; if (pixels != NULL) { for (i = 0; i < height; i++) { for (j = 0; j < width; j++) CpuCopy16(GetPartyMenuBgTile(b[x + j + ((y + i) * c)]), &pixels[(i * width + j) * 32], 32); } BlitBitmapToWindow(windowId, pixels, x * 8, y * 8, width * 8, height * 8); Free(pixels); } } static void BlitBitmapToPartyWindow_LeftColumn(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 isEgg) { if (width == 0 && height == 0) { width = 10; height = 7; } if (isEgg == FALSE) BlitBitmapToPartyWindow(windowId, sMainSlotTileNums, 10, x, y, width, height); else BlitBitmapToPartyWindow(windowId, sMainSlotTileNums_Egg, 10, x, y, width, height); } static void BlitBitmapToPartyWindow_RightColumn(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 isEgg) { if (width == 0 && height == 0) { width = 18; height = 3; } if (isEgg == FALSE) BlitBitmapToPartyWindow(windowId, sOtherSlotsTileNums, 18, x, y, width, height); else BlitBitmapToPartyWindow(windowId, sOtherSlotsTileNums_Egg, 18, x, y, width, height); } static void DrawEmptySlot(u8 windowId) { BlitBitmapToPartyWindow(windowId, sEmptySlotTileNums, 18, 0, 0, 18, 3); } // If LoadPartyBoxPalette is too dense, this macro could be used to replace the LoadPalette triplets #define LOAD_PARTY_BOX_PAL(paletteIds, paletteOffsets) \ { \ LoadPalette(GetPartyMenuPalBufferPtr(paletteIds[0]), paletteOffsets[0] + palNum, 2); \ LoadPalette(GetPartyMenuPalBufferPtr(paletteIds[1]), paletteOffsets[1] + palNum, 2); \ LoadPalette(GetPartyMenuPalBufferPtr(paletteIds[2]), paletteOffsets[2] + palNum, 2); \ } static void LoadPartyBoxPalette(struct PartyMenuBox *menuBox, u8 palFlags) { u8 palNum = GetWindowAttribute(menuBox->windowId, WINDOW_PALETTE_NUM) * 16; if (palFlags & PARTY_PAL_NO_MON) { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxNoMonPalIds[0]), sPartyBoxNoMonPalOffsets[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxNoMonPalIds[1]), sPartyBoxNoMonPalOffsets[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxNoMonPalIds[2]), sPartyBoxNoMonPalOffsets[2] + palNum, 2); } else if (palFlags & PARTY_PAL_TO_SOFTBOIL) { if (palFlags & PARTY_PAL_SELECTED) { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } else { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } } else if (palFlags & PARTY_PAL_SWITCHING) { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } else if (palFlags & PARTY_PAL_TO_SWITCH) { if (palFlags & PARTY_PAL_SELECTED) { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } else { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxSelectedForActionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } } else if (palFlags & PARTY_PAL_FAINTED) { if (palFlags & PARTY_PAL_SELECTED) { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionFaintedPalIds[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionFaintedPalIds[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionFaintedPalIds[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } else { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxFaintedPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxFaintedPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxFaintedPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxFaintedPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxFaintedPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxFaintedPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } } else if (palFlags & PARTY_PAL_MULTI_ALT) { if (palFlags & PARTY_PAL_SELECTED) { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionMultiPalIds[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionMultiPalIds[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionMultiPalIds[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } else { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxMultiPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxMultiPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxMultiPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxMultiPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxMultiPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxMultiPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } } else if (palFlags & PARTY_PAL_SELECTED) { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxCurrSelectionPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } else { LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxEmptySlotPalIds1[0]), sPartyBoxPalOffsets1[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxEmptySlotPalIds1[1]), sPartyBoxPalOffsets1[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxEmptySlotPalIds1[2]), sPartyBoxPalOffsets1[2] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxEmptySlotPalIds2[0]), sPartyBoxPalOffsets2[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxEmptySlotPalIds2[1]), sPartyBoxPalOffsets2[1] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sPartyBoxEmptySlotPalIds2[2]), sPartyBoxPalOffsets2[2] + palNum, 2); } } static void DisplayPartyPokemonBarDetail(u8 windowId, const u8 *str, u8 color, const u8 *align) { AddTextPrinterParameterized3(windowId, 0, align[0], align[1], sFontColorTable[color], 0, str); } static void DisplayPartyPokemonNickname(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c) { u8 nickname[POKEMON_NAME_LENGTH + 1]; if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) { if (c == 1) menuBox->infoRects->blitFunc(menuBox->windowId, menuBox->infoRects->dimensions[0] >> 3, menuBox->infoRects->dimensions[1] >> 3, menuBox->infoRects->dimensions[2] >> 3, menuBox->infoRects->dimensions[3] >> 3, FALSE); GetMonNickname(mon, nickname); DisplayPartyPokemonBarDetail(menuBox->windowId, nickname, 0, menuBox->infoRects->dimensions); } } static void DisplayPartyPokemonLevelCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c) { if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) { u8 ailment = GetMonAilment(mon); if (ailment == AILMENT_NONE || ailment == AILMENT_PKRS) { if (c != 0) menuBox->infoRects->blitFunc(menuBox->windowId, menuBox->infoRects->dimensions[4] >> 3, (menuBox->infoRects->dimensions[5] >> 3) + 1, menuBox->infoRects->dimensions[6] >> 3, menuBox->infoRects->dimensions[7] >> 3, FALSE); if (c != 2) DisplayPartyPokemonLevel(GetMonData(mon, MON_DATA_LEVEL), menuBox); } } } static void DisplayPartyPokemonLevel(u8 level, struct PartyMenuBox *menuBox) { ConvertIntToDecimalStringN(gStringVar2, level, STR_CONV_MODE_LEFT_ALIGN, 3); StringCopy(gStringVar1, gText_LevelSymbol); StringAppend(gStringVar1, gStringVar2); DisplayPartyPokemonBarDetail(menuBox->windowId, gStringVar1, 0, &menuBox->infoRects->dimensions[4]); } static void DisplayPartyPokemonGenderNidoranCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c) { u8 nickname[POKEMON_NAME_LENGTH + 1]; if (c == 1) menuBox->infoRects->blitFunc(menuBox->windowId, menuBox->infoRects->dimensions[8] >> 3, (menuBox->infoRects->dimensions[9] >> 3) + 1, menuBox->infoRects->dimensions[10] >> 3, menuBox->infoRects->dimensions[11] >> 3, FALSE); GetMonNickname(mon, nickname); DisplayPartyPokemonGender(GetMonGender(mon), GetMonData(mon, MON_DATA_SPECIES), nickname, menuBox); } static void DisplayPartyPokemonGender(u8 gender, u16 species, u8 *nickname, struct PartyMenuBox *menuBox) { u8 palNum = GetWindowAttribute(menuBox->windowId, WINDOW_PALETTE_NUM) * 16; if (species == SPECIES_NONE) return; if ((species == SPECIES_NIDORAN_M || species == SPECIES_NIDORAN_F) && StringCompare(nickname, gSpeciesNames[species]) == 0) return; switch (gender) { case MON_MALE: LoadPalette(GetPartyMenuPalBufferPtr(sGenderMalePalIds[0]), sGenderPalOffsets[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sGenderMalePalIds[1]), sGenderPalOffsets[1] + palNum, 2); DisplayPartyPokemonBarDetail(menuBox->windowId, gText_MaleSymbol, 2, &menuBox->infoRects->dimensions[8]); break; case MON_FEMALE: LoadPalette(GetPartyMenuPalBufferPtr(sGenderFemalePalIds[0]), sGenderPalOffsets[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sGenderFemalePalIds[1]), sGenderPalOffsets[1] + palNum, 2); DisplayPartyPokemonBarDetail(menuBox->windowId, gText_FemaleSymbol, 2, &menuBox->infoRects->dimensions[8]); break; } } static void DisplayPartyPokemonHPCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c) { if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) { if (c != 0) menuBox->infoRects->blitFunc(menuBox->windowId, menuBox->infoRects->dimensions[12] >> 3, (menuBox->infoRects->dimensions[13] >> 3) + 1, menuBox->infoRects->dimensions[14] >> 3, menuBox->infoRects->dimensions[15] >> 3, FALSE); if (c != 2) DisplayPartyPokemonHP(GetMonData(mon, MON_DATA_HP), menuBox); } } static void DisplayPartyPokemonHP(u16 hp, struct PartyMenuBox *menuBox) { u8 *strOut = ConvertIntToDecimalStringN(gStringVar1, hp, STR_CONV_MODE_RIGHT_ALIGN, 3); strOut[0] = CHAR_SLASH; strOut[1] = EOS; DisplayPartyPokemonBarDetail(menuBox->windowId, gStringVar1, 0, &menuBox->infoRects->dimensions[12]); } static void DisplayPartyPokemonMaxHPCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c) { if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) { if (c != 0) menuBox->infoRects->blitFunc(menuBox->windowId, (menuBox->infoRects->dimensions[16] >> 3) + 1, (menuBox->infoRects->dimensions[17] >> 3) + 1, menuBox->infoRects->dimensions[18] >> 3, menuBox->infoRects->dimensions[19] >> 3, FALSE); if (c != 2) DisplayPartyPokemonMaxHP(GetMonData(mon, MON_DATA_MAX_HP), menuBox); } } static void DisplayPartyPokemonMaxHP(u16 maxhp, struct PartyMenuBox *menuBox) { ConvertIntToDecimalStringN(gStringVar2, maxhp, STR_CONV_MODE_RIGHT_ALIGN, 3); StringCopy(gStringVar1, gText_Slash); StringAppend(gStringVar1, gStringVar2); DisplayPartyPokemonBarDetail(menuBox->windowId, gStringVar1, 0, &menuBox->infoRects->dimensions[16]); } static void DisplayPartyPokemonHPBarCheck(struct Pokemon *mon, struct PartyMenuBox *menuBox) { if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) DisplayPartyPokemonHPBar(GetMonData(mon, MON_DATA_HP), GetMonData(mon, MON_DATA_MAX_HP), menuBox); } static void DisplayPartyPokemonHPBar(u16 hp, u16 maxhp, struct PartyMenuBox *menuBox) { u8 palNum = GetWindowAttribute(menuBox->windowId, WINDOW_PALETTE_NUM) * 16; u8 hpFraction; switch (GetHPBarLevel(hp, maxhp)) { case HP_BAR_GREEN: case HP_BAR_FULL: LoadPalette(GetPartyMenuPalBufferPtr(sHPBarGreenPalIds[0]), sHPBarPalOffsets[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sHPBarGreenPalIds[1]), sHPBarPalOffsets[1] + palNum, 2); break; case HP_BAR_YELLOW: LoadPalette(GetPartyMenuPalBufferPtr(sHPBarYellowPalIds[0]), sHPBarPalOffsets[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sHPBarYellowPalIds[1]), sHPBarPalOffsets[1] + palNum, 2); break; default: LoadPalette(GetPartyMenuPalBufferPtr(sHPBarRedPalIds[0]), sHPBarPalOffsets[0] + palNum, 2); LoadPalette(GetPartyMenuPalBufferPtr(sHPBarRedPalIds[1]), sHPBarPalOffsets[1] + palNum, 2); break; } hpFraction = GetScaledHPFraction(hp, maxhp, menuBox->infoRects->dimensions[22]); FillWindowPixelRect(menuBox->windowId, sHPBarPalOffsets[1], menuBox->infoRects->dimensions[20], menuBox->infoRects->dimensions[21], hpFraction, 1); FillWindowPixelRect(menuBox->windowId, sHPBarPalOffsets[0], menuBox->infoRects->dimensions[20], menuBox->infoRects->dimensions[21] + 1, hpFraction, 2); if (hpFraction != menuBox->infoRects->dimensions[22]) { // This appears to be an alternating fill FillWindowPixelRect(menuBox->windowId, 0x0D, menuBox->infoRects->dimensions[20] + hpFraction, menuBox->infoRects->dimensions[21], menuBox->infoRects->dimensions[22] - hpFraction, 1); FillWindowPixelRect(menuBox->windowId, 0x02, menuBox->infoRects->dimensions[20] + hpFraction, menuBox->infoRects->dimensions[21] + 1, menuBox->infoRects->dimensions[22] - hpFraction, 2); } CopyWindowToVram(menuBox->windowId, 2); } static void DisplayPartyPokemonDescriptionText(u8 stringID, struct PartyMenuBox *menuBox, u8 c) { if (c) { int width = ((menuBox->infoRects->descTextLeft % 8) + menuBox->infoRects->descTextWidth + 7) / 8; int height = ((menuBox->infoRects->descTextTop % 8) + menuBox->infoRects->descTextHeight + 7) / 8; menuBox->infoRects->blitFunc(menuBox->windowId, menuBox->infoRects->descTextLeft >> 3, menuBox->infoRects->descTextTop >> 3, width, height, TRUE); } if (c != 2) AddTextPrinterParameterized3(menuBox->windowId, 1, menuBox->infoRects->descTextLeft, menuBox->infoRects->descTextTop, sFontColorTable[0], 0, sDescriptionStringTable[stringID]); } static void PartyMenuRemoveWindow(u8 *ptr) { if (*ptr != 0xFF) { ClearStdWindowAndFrameToTransparent(*ptr, 0); RemoveWindow(*ptr); *ptr = 0xFF; schedule_bg_copy_tilemap_to_vram(2); } } void DisplayPartyMenuStdMessage(u32 stringID) { u8 *windowPtr = &sPartyMenuInternal->windowId[1]; if (*windowPtr != 0xFF) PartyMenuRemoveWindow(windowPtr); if (stringID != PARTY_MSG_NONE) { switch (stringID) { case PARTY_MSG_DO_WHAT_WITH_MON: *windowPtr = AddWindow(&sDoWhatWithMonMsgWindowTemplate); break; case PARTY_MSG_DO_WHAT_WITH_ITEM: *windowPtr = AddWindow(&sDoWhatWithItemMsgWindowTemplate); break; case PARTY_MSG_DO_WHAT_WITH_MAIL: *windowPtr = AddWindow(&sDoWhatWithMailMsgWindowTemplate); break; case PARTY_MSG_RESTORE_WHICH_MOVE: case PARTY_MSG_BOOST_PP_WHICH_MOVE: *windowPtr = AddWindow(&sWhichMoveMsgWindowTemplate); break; case PARTY_MSG_ALREADY_HOLDING_ONE: *windowPtr = AddWindow(&sAlreadyHoldingOneMsgWindowTemplate); break; default: *windowPtr = AddWindow(&sDefaultPartyMsgWindowTemplate); break; } if (stringID == PARTY_MSG_CHOOSE_MON) { if (sPartyMenuInternal->chooseHalf) stringID = PARTY_MSG_CHOOSE_MON_AND_CONFIRM; else if (sub_81B314C() == FALSE) stringID = PARTY_MSG_CHOOSE_MON_OR_CANCEL; } DrawStdFrameWithCustomTileAndPalette(*windowPtr, FALSE, 0x4F, 0xD); StringExpandPlaceholders(gStringVar4, sActionStringTable[stringID]); AddTextPrinterParameterized(*windowPtr, 1, gStringVar4, 0, 1, 0, 0); schedule_bg_copy_tilemap_to_vram(2); } } // TOOD: TRUE if should be forced to choose mon, but why when nummons > 1 static bool8 sub_81B314C(void) { struct Pokemon *party = gPlayerParty; u8 i; u8 numAliveMons = 0; if (gPartyMenu.action == PARTY_ACTION_SEND_OUT) return TRUE; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE && (GetMonData(&party[i], MON_DATA_HP) != 0 || GetMonData(&party[i], MON_DATA_IS_EGG))) numAliveMons++; if (numAliveMons > 1) return TRUE; } return FALSE; } static u8 DisplaySelectionWindow(u8 windowType) { struct WindowTemplate window; u8 cursorDimension; u8 fontAttribute; u8 i; switch (windowType) { case SELECTWINDOW_ACTIONS: SetWindowTemplateFields(&window, 2, 19, 19 - (sPartyMenuInternal->numActions * 2), 10, sPartyMenuInternal->numActions * 2, 14, 0x2E9); break; case SELECTWINDOW_ITEM: window = sItemGiveTakeWindowTemplate; break; case SELECTWINDOW_MAIL: window = sMailReadTakeWindowTemplate; break; default: // SELECTWINDOW_MOVES window = sMoveSelectWindowTemplate; break; } sPartyMenuInternal->windowId[0] = AddWindow(&window); DrawStdFrameWithCustomTileAndPalette(sPartyMenuInternal->windowId[0], FALSE, 0x4F, 13); if (windowType == 3) return sPartyMenuInternal->windowId[0]; cursorDimension = GetMenuCursorDimensionByFont(1, 0); fontAttribute = GetFontAttribute(1, 2); for (i = 0; i < sPartyMenuInternal->numActions; i++) { u8 unk = (sPartyMenuInternal->actions[i] >= MENU_FIELD_MOVES) ? 4 : 3; AddTextPrinterParameterized4(sPartyMenuInternal->windowId[0], 1, cursorDimension, (i * 16) + 1, fontAttribute, 0, sFontColorTable[unk], 0, sCursorOptions[sPartyMenuInternal->actions[i]].text); } InitMenuInUpperLeftCorner(sPartyMenuInternal->windowId[0], sPartyMenuInternal->numActions, 0, 1); schedule_bg_copy_tilemap_to_vram(2); return sPartyMenuInternal->windowId[0]; } static void PartyMenuPrintText(const u8 *text) { DrawStdFrameWithCustomTileAndPalette(6, FALSE, 0x4F, 13); gTextFlags.canABSpeedUpPrint = TRUE; AddTextPrinterParameterized2(6, 1, text, GetPlayerTextSpeedDelay(), 0, 2, 1, 3); } static void PartyMenuDisplayYesNoMenu(void) { CreateYesNoMenu(&sPartyMenuYesNoWindowTemplate, 0x4F, 13, 0); } static u8 CreateLevelUpStatsWindow(void) { sPartyMenuInternal->windowId[0] = AddWindow(&sLevelUpStatsWindowTemplate); DrawStdFrameWithCustomTileAndPalette(sPartyMenuInternal->windowId[0], FALSE, 0x4F, 13); return sPartyMenuInternal->windowId[0]; } static void RemoveLevelUpStatsWindow(void) { ClearWindowTilemap(sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); } static void SetPartyMonSelectionActions(struct Pokemon *mons, u8 slotId, u8 action) { u8 i; if (action == ACTIONS_NONE) { SetPartyMonFieldSelectionActions(mons, slotId); } else { sPartyMenuInternal->numActions = sPartyMenuActionCounts[action]; for (i = 0; i < sPartyMenuInternal->numActions; i++) sPartyMenuInternal->actions[i] = sPartyMenuActions[action][i]; } } static void SetPartyMonFieldSelectionActions(struct Pokemon *mons, u8 slotId) { u8 i, j; sPartyMenuInternal->numActions = 0; AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_SUMMARY); // Add field moves to action list for (i = 0; i < MAX_MON_MOVES; i++) { for (j = 0; sFieldMoves[j] != FIELD_MOVE_TERMINATOR; j++) { if (GetMonData(&mons[slotId], i + MON_DATA_MOVE1) == sFieldMoves[j]) { AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, j + MENU_FIELD_MOVES); break; } } } if (!InBattlePike()) { if (GetMonData(&mons[1], MON_DATA_SPECIES) != SPECIES_NONE) AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_SWITCH); if (ItemIsMail(GetMonData(&mons[slotId], MON_DATA_HELD_ITEM))) AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_MAIL); else AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_ITEM); } AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_CANCEL1); } static u8 GetPartyMenuActionsType(struct Pokemon *mon) { u32 actionType; switch (gPartyMenu.menuType) { case PARTY_MENU_TYPE_FIELD: if (InMultiBattleRoom() == TRUE || GetMonData(mon, MON_DATA_IS_EGG)) actionType = ACTIONS_SWITCH; else actionType = ACTIONS_NONE; // actions populated by SetPartyMonFieldSelectionActions break; case PARTY_MENU_TYPE_IN_BATTLE: actionType = GetPartyMenuActionsTypeInBattle(mon); break; case PARTY_MENU_TYPE_CHOOSE_HALF: switch (GetPartySlotEntryStatus(gPartyMenu.slotId)) { default: // Not eligible actionType = ACTIONS_SUMMARY_ONLY; break; case 0: // Eligible actionType = ACTIONS_ENTER; break; case 1: // Already selected actionType = ACTIONS_NO_ENTRY; break; } break; case PARTY_MENU_TYPE_DAYCARE: actionType = (GetMonData(mon, MON_DATA_IS_EGG)) ? ACTIONS_SUMMARY_ONLY : ACTIONS_STORE; break; case PARTY_MENU_TYPE_UNION_ROOM_REGISTER: actionType = ACTIONS_REGISTER; break; case PARTY_MENU_TYPE_UNION_ROOM_TRADE: actionType = ACTIONS_TRADE; break; case PARTY_MENU_TYPE_SPIN_TRADE: actionType = ACTIONS_SPIN_TRADE; break; case PARTY_MENU_TYPE_STORE_PYRAMID_HELD_ITEMS: actionType = ACTIONS_TAKEITEM_TOSS; break; // The following have no selection actions because they exit immediately upon selection // PARTY_MENU_TYPE_CONTEST // PARTY_MENU_TYPE_CHOOSE_MON // PARTY_MENU_TYPE_IN_MULTI_BATTLE ?? TODO // PARTY_MENU_TYPE_MOVE_RELEARNER // PARTY_MENU_TYPE_MINIGAME default: actionType = ACTIONS_NONE; break; } return actionType; } static bool8 CreateSelectionWindow(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 item; GetMonNickname(mon, gStringVar1); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); if (gPartyMenu.menuType != PARTY_MENU_TYPE_STORE_PYRAMID_HELD_ITEMS) { SetPartyMonSelectionActions(gPlayerParty, gPartyMenu.slotId, GetPartyMenuActionsType(mon)); DisplaySelectionWindow(SELECTWINDOW_ACTIONS); DisplayPartyMenuStdMessage(PARTY_MSG_DO_WHAT_WITH_MON); } else { item = GetMonData(mon, MON_DATA_HELD_ITEM); if (item != ITEM_NONE) { SetPartyMonSelectionActions(gPlayerParty, gPartyMenu.slotId, GetPartyMenuActionsType(mon)); DisplaySelectionWindow(SELECTWINDOW_ITEM); CopyItemName(item, gStringVar2); DisplayPartyMenuStdMessage(PARTY_MSG_ALREADY_HOLDING_ONE); } else { StringExpandPlaceholders(gStringVar4, gText_PkmnNotHolding); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_UpdateHeldItemSprite; return FALSE; } } return TRUE; } static void Task_TryCreateSelectionWindow(u8 taskId) { if (CreateSelectionWindow(taskId)) { gTasks[taskId].data[0] = 0xFF; gTasks[taskId].func = Task_HandleSelectionMenuInput; } } static void Task_HandleSelectionMenuInput(u8 taskId) { if (!gPaletteFade.active && sub_81221EC() != TRUE) { s8 input; s16 *data = gTasks[taskId].data; if (sPartyMenuInternal->numActions <= 3) input = Menu_ProcessInputNoWrapAround_other(); else input = ProcessMenuInput_other(); data[0] = Menu_GetCursorPos(); switch (input) { case MENU_NOTHING_CHOSEN: break; case MENU_B_PRESSED: PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[2]); sCursorOptions[sPartyMenuInternal->actions[sPartyMenuInternal->numActions - 1]].func(taskId); break; default: PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[2]); sCursorOptions[sPartyMenuInternal->actions[input]].func(taskId); break; } } } static void CursorCb_Summary(u8 taskId) { PlaySE(SE_SELECT); sPartyMenuInternal->exitCallback = CB2_ShowPokemonSummaryScreen; Task_ClosePartyMenu(taskId); } static void CB2_ShowPokemonSummaryScreen(void) { if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_BATTLE) { UpdatePartyToBattleOrder(); ShowPokemonSummaryScreen(PSS_MODE_UNK1, gPlayerParty, gPartyMenu.slotId, gPlayerPartyCount - 1, CB2_ReturnToPartyMenuFromSummaryScreen); } else { ShowPokemonSummaryScreen(PSS_MODE_NORMAL, gPlayerParty, gPartyMenu.slotId, gPlayerPartyCount - 1, CB2_ReturnToPartyMenuFromSummaryScreen); } } static void CB2_ReturnToPartyMenuFromSummaryScreen(void) { gPaletteFade.bufferTransferDisabled = TRUE; gPartyMenu.slotId = gLastViewedMonIndex; InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_DO_WHAT_WITH_MON, Task_TryCreateSelectionWindow, gPartyMenu.exitCallback); } static void CursorCb_Switch(u8 taskId) { PlaySE(SE_SELECT); gPartyMenu.action = PARTY_ACTION_SWITCH; PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); DisplayPartyMenuStdMessage(PARTY_MSG_MOVE_TO_WHERE); AnimatePartySlot(gPartyMenu.slotId, 1); gPartyMenu.slotId2 = gPartyMenu.slotId; gTasks[taskId].func = Task_HandleChooseMonInput; } #define tSlot1Left data[0] #define tSlot1Top data[1] #define tSlot1Width data[2] #define tSlot1Height data[3] #define tSlot2Left data[4] #define tSlot2Top data[5] #define tSlot2Width data[6] #define tSlot2Height data[7] #define tSlot1Offset data[8] #define tSlot2Offset data[9] #define tSlot1SlideDir data[10] #define tSlot2SlideDir data[11] static void SwitchSelectedMons(u8 taskId) { s16 *data = gTasks[taskId].data; u8 windowIds[2]; if (gPartyMenu.slotId2 == gPartyMenu.slotId) { FinishTwoMonAction(taskId); } else { // Initialize switching party mons slide animation windowIds[0] = sPartyMenuBoxes[gPartyMenu.slotId].windowId; tSlot1Left = GetWindowAttribute(windowIds[0], WINDOW_TILEMAP_LEFT); tSlot1Top = GetWindowAttribute(windowIds[0], WINDOW_TILEMAP_TOP); tSlot1Width = GetWindowAttribute(windowIds[0], WINDOW_WIDTH); tSlot1Height = GetWindowAttribute(windowIds[0], WINDOW_HEIGHT); tSlot1Offset = 0; if (tSlot1Width == 10) tSlot1SlideDir = -1; else tSlot1SlideDir = 1; windowIds[1] = sPartyMenuBoxes[gPartyMenu.slotId2].windowId; tSlot2Left = GetWindowAttribute(windowIds[1], WINDOW_TILEMAP_LEFT); tSlot2Top = GetWindowAttribute(windowIds[1], WINDOW_TILEMAP_TOP); tSlot2Width = GetWindowAttribute(windowIds[1], WINDOW_WIDTH); tSlot2Height = GetWindowAttribute(windowIds[1], WINDOW_HEIGHT); tSlot2Offset = 0; if (tSlot2Width == 10) tSlot2SlideDir = -1; else tSlot2SlideDir = 1; sSlot1TilemapBuffer = Alloc(tSlot1Width * (tSlot1Height << 1)); sSlot2TilemapBuffer = Alloc(tSlot2Width * (tSlot2Height << 1)); CopyToBufferFromBgTilemap(0, sSlot1TilemapBuffer, tSlot1Left, tSlot1Top, tSlot1Width, tSlot1Height); CopyToBufferFromBgTilemap(0, sSlot2TilemapBuffer, tSlot2Left, tSlot2Top, tSlot2Width, tSlot2Height); ClearWindowTilemap(windowIds[0]); ClearWindowTilemap(windowIds[1]); gPartyMenu.action = PARTY_ACTION_SWITCHING; AnimatePartySlot(gPartyMenu.slotId, 1); AnimatePartySlot(gPartyMenu.slotId2, 1); SlidePartyMenuBoxOneStep(taskId); gTasks[taskId].func = Task_SlideSelectedSlotsOffscreen; } } // returns FALSE if the slot has slid fully offscreen / back onscreen static bool8 TryMovePartySlot(s16 a, s16 srcWidth, u8 *srcX, u8 *srcHeight, u8 *destY) { if ((a + srcWidth) < 0) return FALSE; if (a > 31) return FALSE; if (a < 0) { *srcX = a * -1; *srcHeight = 0; *destY = srcWidth + a; } else { *srcX = 0; *srcHeight = a; if ((a + srcWidth) > 31) *destY = 32 - a; else *destY = srcWidth; } return TRUE; } static void MoveAndBufferPartySlot(const void *rectSrc, s16 a, s16 destX, s16 srcWidth, s16 rectWidth, s16 offset) { u8 srcX, srcHeight, destY; if (TryMovePartySlot(a, srcWidth, &srcX, &srcHeight, &destY)) { FillBgTilemapBufferRect_Palette0(0, 0, srcHeight, destX, destY, rectWidth); if (TryMovePartySlot(a + offset, srcWidth, &srcX, &srcHeight, &destY)) CopyRectToBgTilemapBufferRect(0, rectSrc, srcX, 0, srcWidth, rectWidth, srcHeight, destX, destY, rectWidth, 17, 0, 0); } } static void MovePartyMenuBoxSprites(struct PartyMenuBox *menuBox, s16 offset) { gSprites[menuBox->pokeballSpriteId].pos2.x += offset * 8; gSprites[menuBox->itemSpriteId].pos2.x += offset * 8; gSprites[menuBox->monSpriteId].pos2.x += offset * 8; gSprites[menuBox->statusSpriteId].pos2.x += offset * 8; } static void SlidePartyMenuBoxSpritesOneStep(u8 taskId) { s16 *data = gTasks[taskId].data; if (tSlot1SlideDir != 0) MovePartyMenuBoxSprites(&sPartyMenuBoxes[gPartyMenu.slotId], tSlot1SlideDir); if (tSlot2SlideDir != 0) MovePartyMenuBoxSprites(&sPartyMenuBoxes[gPartyMenu.slotId2], tSlot2SlideDir); } static void SlidePartyMenuBoxOneStep(u8 taskId) { s16 *data = gTasks[taskId].data; if (tSlot1SlideDir != 0) MoveAndBufferPartySlot(sSlot1TilemapBuffer, tSlot1Left + tSlot1Offset, tSlot1Top, tSlot1Width, tSlot1Height, tSlot1SlideDir); if (tSlot2SlideDir != 0) MoveAndBufferPartySlot(sSlot2TilemapBuffer, tSlot2Left + tSlot2Offset, tSlot2Top, tSlot2Width, tSlot2Height, tSlot2SlideDir); schedule_bg_copy_tilemap_to_vram(0); } static void Task_SlideSelectedSlotsOffscreen(u8 taskId) { s16 *data = gTasks[taskId].data; u16 slidingSlotPositions[2]; SlidePartyMenuBoxOneStep(taskId); SlidePartyMenuBoxSpritesOneStep(taskId); tSlot1Offset += tSlot1SlideDir; tSlot2Offset += tSlot2SlideDir; slidingSlotPositions[0] = tSlot1Left + tSlot1Offset; slidingSlotPositions[1] = tSlot2Left + tSlot2Offset; // Both slots have slid offscreen if (slidingSlotPositions[0] > 33 && slidingSlotPositions[1] > 33) { tSlot1SlideDir *= -1; tSlot2SlideDir *= -1; SwitchPartyMon(); DisplayPartyPokemonData(gPartyMenu.slotId); DisplayPartyPokemonData(gPartyMenu.slotId2); PutWindowTilemap(sPartyMenuBoxes[gPartyMenu.slotId].windowId); PutWindowTilemap(sPartyMenuBoxes[gPartyMenu.slotId2].windowId); CopyToBufferFromBgTilemap(0, sSlot1TilemapBuffer, tSlot1Left, tSlot1Top, tSlot1Width, tSlot1Height); CopyToBufferFromBgTilemap(0, sSlot2TilemapBuffer, tSlot2Left, tSlot2Top, tSlot2Width, tSlot2Height); ClearWindowTilemap(sPartyMenuBoxes[gPartyMenu.slotId].windowId); ClearWindowTilemap(sPartyMenuBoxes[gPartyMenu.slotId2].windowId); gTasks[taskId].func = Task_SlideSelectedSlotsOnscreen; } } static void Task_SlideSelectedSlotsOnscreen(u8 taskId) { s16 *data = gTasks[taskId].data; SlidePartyMenuBoxOneStep(taskId); SlidePartyMenuBoxSpritesOneStep(taskId); // Both slots have slid back onscreen if (tSlot1SlideDir == 0 && tSlot2SlideDir == 0) { PutWindowTilemap(sPartyMenuBoxes[gPartyMenu.slotId].windowId); PutWindowTilemap(sPartyMenuBoxes[gPartyMenu.slotId2].windowId); schedule_bg_copy_tilemap_to_vram(0); Free(sSlot1TilemapBuffer); Free(sSlot2TilemapBuffer); FinishTwoMonAction(taskId); } // Continue sliding else { tSlot1Offset += tSlot1SlideDir; tSlot2Offset += tSlot2SlideDir; if (tSlot1Offset == 0) tSlot1SlideDir = 0; if (tSlot2Offset == 0) tSlot2SlideDir = 0; } } static void SwitchMenuBoxSprites(u8 *spriteIdPtr1, u8 *spriteIdPtr2) { u8 spriteIdBuffer = *spriteIdPtr1; u16 xBuffer1, yBuffer1, xBuffer2, yBuffer2; *spriteIdPtr1 = *spriteIdPtr2; *spriteIdPtr2 = spriteIdBuffer; xBuffer1 = gSprites[*spriteIdPtr1].pos1.x; yBuffer1 = gSprites[*spriteIdPtr1].pos1.y; xBuffer2 = gSprites[*spriteIdPtr1].pos2.x; yBuffer2 = gSprites[*spriteIdPtr1].pos2.y; gSprites[*spriteIdPtr1].pos1.x = gSprites[*spriteIdPtr2].pos1.x; gSprites[*spriteIdPtr1].pos1.y = gSprites[*spriteIdPtr2].pos1.y; gSprites[*spriteIdPtr1].pos2.x = gSprites[*spriteIdPtr2].pos2.x; gSprites[*spriteIdPtr1].pos2.y = gSprites[*spriteIdPtr2].pos2.y; gSprites[*spriteIdPtr2].pos1.x = xBuffer1; gSprites[*spriteIdPtr2].pos1.y = yBuffer1; gSprites[*spriteIdPtr2].pos2.x = xBuffer2; gSprites[*spriteIdPtr2].pos2.y = yBuffer2; } static void SwitchPartyMon(void) { struct PartyMenuBox *menuBoxes[2]; struct Pokemon *mon1, *mon2; struct Pokemon *monBuffer; menuBoxes[0] = &sPartyMenuBoxes[gPartyMenu.slotId]; menuBoxes[1] = &sPartyMenuBoxes[gPartyMenu.slotId2]; mon1 = &gPlayerParty[gPartyMenu.slotId]; mon2 = &gPlayerParty[gPartyMenu.slotId2]; monBuffer = Alloc(sizeof(struct Pokemon)); *monBuffer = *mon1; *mon1 = *mon2; *mon2 = *monBuffer; Free(monBuffer); SwitchMenuBoxSprites(&menuBoxes[0]->pokeballSpriteId, &menuBoxes[1]->pokeballSpriteId); SwitchMenuBoxSprites(&menuBoxes[0]->itemSpriteId, &menuBoxes[1]->itemSpriteId); SwitchMenuBoxSprites(&menuBoxes[0]->monSpriteId, &menuBoxes[1]->monSpriteId); SwitchMenuBoxSprites(&menuBoxes[0]->statusSpriteId, &menuBoxes[1]->statusSpriteId); } // Finish switching mons or using Softboiled static void FinishTwoMonAction(u8 taskId) { PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); gPartyMenu.action = PARTY_ACTION_CHOOSE_MON; AnimatePartySlot(gPartyMenu.slotId, 0); gPartyMenu.slotId = gPartyMenu.slotId2; AnimatePartySlot(gPartyMenu.slotId2, 1); DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON); gTasks[taskId].func = Task_HandleChooseMonInput; } #undef tSlot1Left #undef tSlot1Top #undef tSlot1Width #undef tSlot1Height #undef tSlot2Left #undef tSlot2Top #undef tSlot2Width #undef tSlot2Height #undef tSlot1Offset #undef tSlot2Offset #undef tSlot1SlideDir #undef tSlot2SlideDir static void CursorCb_Cancel1(u8 taskId) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); if (gPartyMenu.menuType == PARTY_MENU_TYPE_DAYCARE) DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON_2); else DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON); gTasks[taskId].func = Task_HandleChooseMonInput; } static void CursorCb_Item(u8 taskId) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); SetPartyMonSelectionActions(gPlayerParty, gPartyMenu.slotId, ACTIONS_ITEM); DisplaySelectionWindow(SELECTWINDOW_ITEM); DisplayPartyMenuStdMessage(PARTY_MSG_DO_WHAT_WITH_ITEM); gTasks[taskId].data[0] = 0xFF; gTasks[taskId].func = Task_HandleSelectionMenuInput; } static void CursorCb_Give(u8 taskId) { PlaySE(SE_SELECT); sPartyMenuInternal->exitCallback = CB2_SelectBagItemToGive; Task_ClosePartyMenu(taskId); } static void CB2_SelectBagItemToGive(void) { if (InBattlePyramid() == FALSE) GoToBagMenu(RETURN_LOCATION_POKEMON_LIST, POCKETS_COUNT, CB2_GiveHoldItem); else GoToBattlePyramidBagMenu(2, CB2_GiveHoldItem); } static void CB2_GiveHoldItem(void) { if (gSpecialVar_ItemId == ITEM_NONE) { InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_NONE, Task_TryCreateSelectionWindow, gPartyMenu.exitCallback); } else { sPartyMenuItemId = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_HELD_ITEM); // Already holding item if (sPartyMenuItemId != ITEM_NONE) { InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_NONE, Task_SwitchHoldItemsPrompt, gPartyMenu.exitCallback); } // Give mail else if (ItemIsMail(gSpecialVar_ItemId)) { RemoveBagItem(gSpecialVar_ItemId, 1); GiveItemToMon(&gPlayerParty[gPartyMenu.slotId], gSpecialVar_ItemId); CB2_WriteMailToGiveMon(); } // Give item else { InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_NONE, Task_GiveHoldItem, gPartyMenu.exitCallback); } } } static void Task_GiveHoldItem(u8 taskId) { u16 item; if (!gPaletteFade.active) { item = gSpecialVar_ItemId; DisplayGaveHeldItemMessage(&gPlayerParty[gPartyMenu.slotId], item, FALSE, 0); GiveItemToMon(&gPlayerParty[gPartyMenu.slotId], item); RemoveBagItem(item, 1); gTasks[taskId].func = Task_UpdateHeldItemSprite; } } static void Task_SwitchHoldItemsPrompt(u8 taskId) { if (!gPaletteFade.active) { DisplayAlreadyHoldingItemSwitchMessage(&gPlayerParty[gPartyMenu.slotId], sPartyMenuItemId, TRUE); gTasks[taskId].func = Task_SwitchItemsYesNo; } } static void Task_SwitchItemsYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleSwitchItemsYesNoInput; } } static void Task_HandleSwitchItemsYesNoInput(u8 taskId) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: // Yes, switch items RemoveBagItem(gSpecialVar_ItemId, 1); // No room to return held item to bag if (AddBagItem(sPartyMenuItemId, 1) == FALSE) { AddBagItem(gSpecialVar_ItemId, 1); BufferBagFullCantTakeItemMessage(sPartyMenuItemId); DisplayPartyMenuMessage(gStringVar4, FALSE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } // Giving mail else if (ItemIsMail(gSpecialVar_ItemId)) { GiveItemToMon(&gPlayerParty[gPartyMenu.slotId], gSpecialVar_ItemId); gTasks[taskId].func = Task_WriteMailToGiveMonAfterText; } // Giving item else { GiveItemToMon(&gPlayerParty[gPartyMenu.slotId], gSpecialVar_ItemId); DisplaySwitchedHeldItemMessage(gSpecialVar_ItemId, sPartyMenuItemId, TRUE); gTasks[taskId].func = Task_UpdateHeldItemSprite; } break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: // No gTasks[taskId].func = Task_ReturnToChooseMonAfterText; break; } } static void Task_WriteMailToGiveMonAfterText(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { sPartyMenuInternal->exitCallback = CB2_WriteMailToGiveMon; Task_ClosePartyMenu(taskId); } } static void CB2_WriteMailToGiveMon(void) { u8 mail = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_MAIL); DoEasyChatScreen( EASY_CHAT_TYPE_MAIL, gSaveBlock1Ptr->mail[mail].words, CB2_ReturnToPartyMenuFromWritingMail, EASY_CHAT_PERSON_DISPLAY_NONE); } static void CB2_ReturnToPartyMenuFromWritingMail(void) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 item = GetMonData(mon, MON_DATA_HELD_ITEM); if (gSpecialVar_Result == FALSE) { TakeMailFromMon(mon); SetMonData(mon, MON_DATA_HELD_ITEM, &sPartyMenuItemId); RemoveBagItem(sPartyMenuItemId, 1); AddBagItem(item, 1); InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_CHOOSE_MON, Task_TryCreateSelectionWindow, gPartyMenu.exitCallback); } else { InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_NONE, sub_81B4624, gPartyMenu.exitCallback); } } static void sub_81B4624(u8 taskId) { if (!gPaletteFade.active) { if (sPartyMenuItemId == ITEM_NONE) DisplayGaveHeldItemMessage(&gPlayerParty[gPartyMenu.slotId], gSpecialVar_ItemId, FALSE, 0); else DisplaySwitchedHeldItemMessage(gSpecialVar_ItemId, sPartyMenuItemId, FALSE); gTasks[taskId].func = Task_UpdateHeldItemSprite; } } static void Task_UpdateHeldItemSprite(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; if (IsPartyMenuTextPrinterActive() != TRUE) { UpdatePartyMonHeldItemSprite(mon, &sPartyMenuBoxes[gPartyMenu.slotId]); if (gPartyMenu.menuType == PARTY_MENU_TYPE_STORE_PYRAMID_HELD_ITEMS) { if (GetMonData(mon, MON_DATA_HELD_ITEM) != ITEM_NONE) DisplayPartyPokemonDescriptionText(PARTYBOX_DESC_HAVE, &sPartyMenuBoxes[gPartyMenu.slotId], 1); else DisplayPartyPokemonDescriptionText(PARTYBOX_DESC_DONT_HAVE, &sPartyMenuBoxes[gPartyMenu.slotId], 1); } Task_ReturnToChooseMonAfterText(taskId); } } static void CursorCb_TakeItem(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 item = GetMonData(mon, MON_DATA_HELD_ITEM); PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); switch (TryTakeMonItem(mon)) { case 0: // Not holding item GetMonNickname(mon, gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnNotHolding); DisplayPartyMenuMessage(gStringVar4, TRUE); break; case 1: // No room to take item BufferBagFullCantTakeItemMessage(item); DisplayPartyMenuMessage(gStringVar4, TRUE); break; default: // Took item DisplayTookHeldItemMessage(mon, item, TRUE); break; } schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_UpdateHeldItemSprite; } static void CursorCb_Toss(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 item = GetMonData(mon, MON_DATA_HELD_ITEM); PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); if (item == ITEM_NONE) { GetMonNickname(mon, gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnNotHolding); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_UpdateHeldItemSprite; } else { CopyItemName(item, gStringVar1); StringExpandPlaceholders(gStringVar4, gText_ThrowAwayItem); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_TossHeldItemYesNo; } } static void Task_TossHeldItemYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleTossHeldItemYesNoInput; } } static void Task_HandleTossHeldItemYesNoInput(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: CopyItemName(GetMonData(mon, MON_DATA_HELD_ITEM), gStringVar1); StringExpandPlaceholders(gStringVar4, gText_ItemThrownAway); DisplayPartyMenuMessage(gStringVar4, FALSE); gTasks[taskId].func = Task_TossHeldItem; break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: gTasks[taskId].func = Task_ReturnToChooseMonAfterText; break; } } static void Task_TossHeldItem(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; if (IsPartyMenuTextPrinterActive() != TRUE) { u16 item = ITEM_NONE; SetMonData(mon, MON_DATA_HELD_ITEM, &item); UpdatePartyMonHeldItemSprite(mon, &sPartyMenuBoxes[gPartyMenu.slotId]); DisplayPartyPokemonDescriptionText(PARTYBOX_DESC_DONT_HAVE, &sPartyMenuBoxes[gPartyMenu.slotId], 1); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } } static void CursorCb_Mail(u8 taskId) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); SetPartyMonSelectionActions(gPlayerParty, gPartyMenu.slotId, ACTIONS_MAIL); DisplaySelectionWindow(SELECTWINDOW_MAIL); DisplayPartyMenuStdMessage(PARTY_MSG_DO_WHAT_WITH_MAIL); gTasks[taskId].data[0] = 0xFF; gTasks[taskId].func = Task_HandleSelectionMenuInput; } static void CursorCb_Read(u8 taskId) { PlaySE(SE_SELECT); sPartyMenuInternal->exitCallback = sub_81B4A98; Task_ClosePartyMenu(taskId); } static void sub_81B4A98(void) { ReadMail(&gSaveBlock1Ptr->mail[GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_MAIL)], sub_81B4AE0, 1); } static void sub_81B4AE0(void) { gPaletteFade.bufferTransferDisabled = TRUE; InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_DO_WHAT_WITH_MON, Task_TryCreateSelectionWindow, gPartyMenu.exitCallback); } static void CursorCb_TakeMail(u8 taskId) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); DisplayPartyMenuMessage(gText_SendMailToPC, TRUE); gTasks[taskId].func = sub_81B4B6C; } static void sub_81B4B6C(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = sub_81B4BA0; } } static void sub_81B4BA0(u8 taskId) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: if (TakeMailFromMon2(&gPlayerParty[gPartyMenu.slotId]) != 0xFF) { DisplayPartyMenuMessage(gText_MailSentToPC, FALSE); gTasks[taskId].func = Task_UpdateHeldItemSprite; } else { DisplayPartyMenuMessage(gText_PCMailboxFull, FALSE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: DisplayPartyMenuMessage(gText_MailMessageWillBeLost, TRUE); gTasks[taskId].func = sub_81B4C60; break; } } static void sub_81B4C60(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = sub_81B4C94; } } static void sub_81B4C94(u8 taskId) { u16 item; switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: item = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_HELD_ITEM); if (AddBagItem(item, 1) == TRUE) { TakeMailFromMon(&gPlayerParty[gPartyMenu.slotId]); DisplayPartyMenuMessage(gText_MailTakenFromPkmn, FALSE); gTasks[taskId].func = Task_UpdateHeldItemSprite; } else { BufferBagFullCantTakeItemMessage(item); DisplayPartyMenuMessage(gStringVar4, FALSE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: gTasks[taskId].func = Task_ReturnToChooseMonAfterText; break; } } static void CursorCb_Cancel2(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); SetPartyMonSelectionActions(gPlayerParty, gPartyMenu.slotId, GetPartyMenuActionsType(mon)); if (gPartyMenu.menuType != PARTY_MENU_TYPE_STORE_PYRAMID_HELD_ITEMS) { DisplaySelectionWindow(SELECTWINDOW_ACTIONS); DisplayPartyMenuStdMessage(PARTY_MSG_DO_WHAT_WITH_MON); } else { DisplaySelectionWindow(SELECTWINDOW_ITEM); CopyItemName(GetMonData(mon, MON_DATA_HELD_ITEM), gStringVar2); DisplayPartyMenuStdMessage(PARTY_MSG_ALREADY_HOLDING_ONE); } gTasks[taskId].data[0] = 0xFF; gTasks[taskId].func = Task_HandleSelectionMenuInput; } static void CursorCb_SendMon(u8 taskId) { PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); if (TrySwitchInPokemon() == TRUE) { Task_ClosePartyMenu(taskId); } else { // gStringVar4 below is the error message buffered by TrySwitchInPokemon PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } } static void CursorCb_Enter(u8 taskId) { u8 maxBattlers; u8 i; PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); maxBattlers = GetMaxBattleEntries(); for (i = 0; i < maxBattlers; i++) { if (gSelectedOrderFromParty[i] == 0) { PlaySE(SE_SELECT); gSelectedOrderFromParty[i] = gPartyMenu.slotId + 1; DisplayPartyPokemonDescriptionText(i + PARTYBOX_DESC_FIRST, &sPartyMenuBoxes[gPartyMenu.slotId], 1); if (i == (maxBattlers - 1)) sub_81B4F88(); DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON); gTasks[taskId].func = Task_HandleChooseMonInput; return; } } ConvertIntToDecimalStringN(gStringVar1, maxBattlers, STR_CONV_MODE_LEFT_ALIGN, 1); StringExpandPlaceholders(gStringVar4, gText_NoMoreThanVar1Pkmn); PlaySE(SE_HAZURE); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } static void sub_81B4F88(void) { AnimatePartySlot(gPartyMenu.slotId, 0); gPartyMenu.slotId = PARTY_SIZE; AnimatePartySlot(gPartyMenu.slotId, 1); } static void CursorCb_NoEntry(u8 taskId) { u8 maxBattlers; u8 i, j; PlaySE(SE_SELECT); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); maxBattlers = GetMaxBattleEntries(); for (i = 0; i < maxBattlers; i++) { if (gSelectedOrderFromParty[i] == (gPartyMenu.slotId + 1)) { for (j = i; j < (maxBattlers - 1); j++) gSelectedOrderFromParty[j] = gSelectedOrderFromParty[j + 1]; gSelectedOrderFromParty[j] = 0; break; } } DisplayPartyPokemonDescriptionText(PARTYBOX_DESC_ABLE_3, &sPartyMenuBoxes[gPartyMenu.slotId], 1); for (i = 0; i < (maxBattlers - 1); i++) { if (gSelectedOrderFromParty[i] != 0) DisplayPartyPokemonDescriptionText(i + PARTYBOX_DESC_FIRST, &sPartyMenuBoxes[gSelectedOrderFromParty[i] - 1], 1); } DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON); gTasks[taskId].func = Task_HandleChooseMonInput; } static void CursorCb_Store(u8 taskId) { PlaySE(SE_SELECT); Task_ClosePartyMenu(taskId); } // Register mon for the Trading Board in Union Room static void CursorCb_Register(u8 taskId) { u16 species2 = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_SPECIES2); u16 species = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_SPECIES); u8 obedience = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_OBEDIENCE); switch (CanRegisterMonForTradingBoard(*(struct UnkLinkRfuStruct_02022B14Substruct *)sub_800F7DC(), species2, species, obedience)) { case CANT_REGISTER_MON: StringExpandPlaceholders(gStringVar4, gText_PkmnCantBeTradedNow); break; case CANT_REGISTER_EGG: StringExpandPlaceholders(gStringVar4, gText_EggCantBeTradedNow); break; default: PlaySE(SE_SELECT); Task_ClosePartyMenu(taskId); return; } PlaySE(SE_HAZURE); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); StringAppend(gStringVar4, gText_PauseUntilPress); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } static void CursorCb_Trade1(u8 taskId) { u16 species2 = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_SPECIES2); u16 species = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_SPECIES); u8 obedience = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_OBEDIENCE); u32 stringId = GetUnionRoomTradeMessageId(*(struct UnkLinkRfuStruct_02022B14Substruct *)sub_800F7DC(), gUnknown_02022C38, species2, gUnionRoomOfferedSpecies, gUnionRoomRequestedMonType, species, obedience); if (stringId != UR_TRADE_MSG_NONE) { StringExpandPlaceholders(gStringVar4, sUnionRoomTradeMessages[stringId - 1]); PlaySE(SE_HAZURE); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); StringAppend(gStringVar4, gText_PauseUntilPress); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } else { PlaySE(SE_SELECT); Task_ClosePartyMenu(taskId); } } // Spin Trade (based on the translation of the Japanese trade prompt) // Not fully implemented, and normally unreachable because PARTY_MENU_TYPE_SPIN_TRADE is never used static void CursorCb_Trade2(u8 taskId) { PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); switch (CanSpinTradeMon(gPlayerParty, gPartyMenu.slotId)) { case CANT_TRADE_LAST_MON: StringExpandPlaceholders(gStringVar4, gText_OnlyPkmnForBattle); break; case CANT_TRADE_NATIONAL: StringExpandPlaceholders(gStringVar4, gText_PkmnCantBeTradedNow); break; case CANT_TRADE_EGG_YET: StringExpandPlaceholders(gStringVar4, gText_EggCantBeTradedNow); break; default: // CAN_TRADE_MON PlaySE(SE_SELECT); GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1); StringExpandPlaceholders(gStringVar4, gJPText_AreYouSureYouWantToSpinTradeMon); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_SpinTradeYesNo; return; } PlaySE(SE_HAZURE); StringAppend(gStringVar4, gText_PauseUntilPress); DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } static void Task_SpinTradeYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleSpinTradeYesNoInput; } } // See comment on CursorCb_Trade2. Selecting YES (0) to spin trade just closes the party menu static void Task_HandleSpinTradeYesNoInput(u8 taskId) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: Task_ClosePartyMenu(taskId); break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: Task_ReturnToChooseMonAfterText(taskId); break; } } static void CursorCb_FieldMove(u8 taskId) { u8 fieldMove = sPartyMenuInternal->actions[Menu_GetCursorPos()] - MENU_FIELD_MOVES; const struct MapHeader *mapHeader; PlaySE(SE_SELECT); if (sFieldMoveCursorCallbacks[fieldMove].fieldMoveFunc == NULL) return; PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); if (sub_81221AC() == TRUE || InUnionRoom() == TRUE) { if (fieldMove == FIELD_MOVE_MILK_DRINK || fieldMove == FIELD_MOVE_SOFT_BOILED) DisplayPartyMenuStdMessage(PARTY_MSG_CANT_USE_HERE); else DisplayPartyMenuStdMessage(sFieldMoveCursorCallbacks[fieldMove].msgId); gTasks[taskId].func = Task_CancelAfterAorBPress; } else { // All field moves before WATERFALL are HMs. if (fieldMove <= FIELD_MOVE_WATERFALL && FlagGet(FLAG_BADGE01_GET + fieldMove) != TRUE) { DisplayPartyMenuMessage(gText_CantUseUntilNewBadge, TRUE); gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } else if (sFieldMoveCursorCallbacks[fieldMove].fieldMoveFunc() == TRUE) { switch (fieldMove) { case FIELD_MOVE_MILK_DRINK: case FIELD_MOVE_SOFT_BOILED: ChooseMonForSoftboiled(taskId); break; case FIELD_MOVE_TELEPORT: mapHeader = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->lastHealLocation.mapGroup, gSaveBlock1Ptr->lastHealLocation.mapNum); GetMapNameGeneric(gStringVar1, mapHeader->regionMapSectionId); StringExpandPlaceholders(gStringVar4, gText_ReturnToHealingSpot); DisplayFieldMoveExitAreaMessage(taskId); sPartyMenuInternal->data[0] = fieldMove; break; case FIELD_MOVE_DIG: mapHeader = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->escapeWarp.mapGroup, gSaveBlock1Ptr->escapeWarp.mapNum); GetMapNameGeneric(gStringVar1, mapHeader->regionMapSectionId); StringExpandPlaceholders(gStringVar4, gText_EscapeFromHere); DisplayFieldMoveExitAreaMessage(taskId); sPartyMenuInternal->data[0] = fieldMove; break; case FIELD_MOVE_FLY: gPartyMenu.exitCallback = MCB2_FlyMap; Task_ClosePartyMenu(taskId); break; default: gPartyMenu.exitCallback = CB2_ReturnToField; Task_ClosePartyMenu(taskId); break; } } // Cant use Field Move else { switch (fieldMove) { case FIELD_MOVE_SURF: DisplayCantUseSurfMessage(); break; case FIELD_MOVE_FLASH: DisplayCantUseFlashMessage(); break; default: DisplayPartyMenuStdMessage(sFieldMoveCursorCallbacks[fieldMove].msgId); break; } gTasks[taskId].func = Task_CancelAfterAorBPress; } } } static void DisplayFieldMoveExitAreaMessage(u8 taskId) { DisplayPartyMenuMessage(gStringVar4, TRUE); gTasks[taskId].func = Task_FieldMoveExitAreaYesNo; } static void Task_FieldMoveExitAreaYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleFieldMoveExitAreaYesNoInput; } } static void Task_HandleFieldMoveExitAreaYesNoInput(u8 taskId) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: gPartyMenu.exitCallback = CB2_ReturnToField; Task_ClosePartyMenu(taskId); break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: gFieldCallback2 = NULL; gPostMenuFieldCallback = NULL; Task_ReturnToChooseMonAfterText(taskId); break; } } bool8 FieldCallback_PrepareFadeInFromMenu(void) { pal_fill_black(); CreateTask(Task_FieldMoveWaitForFade, 8); return TRUE; } static void Task_FieldMoveWaitForFade(u8 taskId) { if (IsWeatherNotFadingIn() == TRUE) { gFieldEffectArguments[0] = GetFieldMoveMonSpecies(); gPostMenuFieldCallback(); DestroyTask(taskId); } } static u16 GetFieldMoveMonSpecies(void) { return GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_SPECIES); } static void Task_CancelAfterAorBPress(u8 taskId) { if ((gMain.newKeys & A_BUTTON) || (gMain.newKeys & B_BUTTON)) CursorCb_Cancel1(taskId); } static void DisplayCantUseFlashMessage(void) { if (FlagGet(FLAG_SYS_USE_FLASH) == TRUE) DisplayPartyMenuStdMessage(PARTY_MSG_ALREADY_IN_USE); else DisplayPartyMenuStdMessage(PARTY_MSG_CANT_USE_HERE); } static void FieldCallback_Surf(void) { gFieldEffectArguments[0] = GetCursorSelectionMonId(); FieldEffectStart(FLDEFF_USE_SURF); } static bool8 SetUpFieldMove_Surf(void) { if (PartyHasMonWithSurf() == TRUE && IsPlayerFacingSurfableFishableWater() == TRUE) { gFieldCallback2 = FieldCallback_PrepareFadeInFromMenu; gPostMenuFieldCallback = FieldCallback_Surf; return TRUE; } return FALSE; } static void DisplayCantUseSurfMessage(void) { if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING)) DisplayPartyMenuStdMessage(PARTY_MSG_ALREADY_SURFING); else DisplayPartyMenuStdMessage(PARTY_MSG_CANT_SURF_HERE); } static bool8 SetUpFieldMove_Fly(void) { if (Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType) == TRUE) return TRUE; else return FALSE; } void CB2_ReturnToPartyMenuFromFlyMap(void) { InitPartyMenu(PARTY_MENU_TYPE_FIELD, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_MON, TRUE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, CB2_ReturnToFieldWithOpenMenu); } static void FieldCallback_Waterfall(void) { gFieldEffectArguments[0] = GetCursorSelectionMonId(); FieldEffectStart(FLDEFF_USE_WATERFALL); } static bool8 SetUpFieldMove_Waterfall(void) { s16 x, y; GetXYCoordsOneStepInFrontOfPlayer(&x, &y); if (MetatileBehavior_IsWaterfall(MapGridGetMetatileBehaviorAt(x, y)) == TRUE && IsPlayerSurfingNorth() == TRUE) { gFieldCallback2 = FieldCallback_PrepareFadeInFromMenu; gPostMenuFieldCallback = FieldCallback_Waterfall; return TRUE; } return FALSE; } static void FieldCallback_Dive(void) { gFieldEffectArguments[0] = GetCursorSelectionMonId(); FieldEffectStart(FLDEFF_USE_DIVE); } static bool8 SetUpFieldMove_Dive(void) { gFieldEffectArguments[1] = TrySetDiveWarp(); if (gFieldEffectArguments[1] != 0) { gFieldCallback2 = FieldCallback_PrepareFadeInFromMenu; gPostMenuFieldCallback = FieldCallback_Dive; return TRUE; } return FALSE; } static void party_menu_icon_anim(struct Pokemon *mon, struct PartyMenuBox *menuBox, u32 slot) { bool32 bit = 1; u16 species2; if (IsMultiBattle() == TRUE && gMain.inBattle) bit = (sMultiBattlePartnersPartyMask[slot] ^ bit) ? 1 : 0; species2 = GetMonData(mon, MON_DATA_SPECIES2); party_menu_link_mon_icon_anim(species2, GetMonData(mon, MON_DATA_PERSONALITY), menuBox, 1, bit); UpdatePartyMonHPBar(menuBox->monSpriteId, mon); } static void party_menu_link_mon_icon_anim(u16 species, u32 pid, struct PartyMenuBox *menuBox, u8 priority, bool32 bit) { if (species != SPECIES_NONE) { menuBox->monSpriteId = CreateMonIcon(species, UpdateTradeMonIconFrame, menuBox->spriteCoords[0], menuBox->spriteCoords[1], 4, pid, bit); gSprites[menuBox->monSpriteId].oam.priority = priority; } } static void UpdateHPBar(u8 spriteId, u16 hp, u16 maxhp) { switch (GetHPBarLevel(hp, maxhp)) { case HP_BAR_FULL: SetPartyHPBarSprite(&gSprites[spriteId], 0); break; case HP_BAR_GREEN: SetPartyHPBarSprite(&gSprites[spriteId], 1); break; case HP_BAR_YELLOW: SetPartyHPBarSprite(&gSprites[spriteId], 2); break; case HP_BAR_RED: SetPartyHPBarSprite(&gSprites[spriteId], 3); break; default: SetPartyHPBarSprite(&gSprites[spriteId], 4); break; } } static void UpdatePartyMonHPBar(u8 spriteId, struct Pokemon *mon) { UpdateHPBar(spriteId, GetMonData(mon, MON_DATA_HP), GetMonData(mon, MON_DATA_MAX_HP)); } static void AnimateSelectedPartyIcon(u8 spriteId, u8 animNum) { gSprites[spriteId].data[0] = 0; if (animNum == 0) { if (gSprites[spriteId].pos1.x == 16) { gSprites[spriteId].pos2.x = 0; gSprites[spriteId].pos2.y = -4; } else { gSprites[spriteId].pos2.x = -4; gSprites[spriteId].pos2.y = 0; } gSprites[spriteId].callback = UpdatePartyMonIconFrame; } else { gSprites[spriteId].pos2.x = 0; gSprites[spriteId].pos2.y = 0; gSprites[spriteId].callback = UpdatePartyMonIconFrameAndBounce; } } static void UpdatePartyMonIconFrameAndBounce(struct Sprite *sprite) { u8 unk = UpdateMonIconFrame(sprite); if (unk != 0) { if (unk & 1) sprite->pos2.y = -3; else sprite->pos2.y = 1; } } static void UpdatePartyMonIconFrame(struct Sprite *sprite) { UpdateMonIconFrame(sprite); } static void party_menu_held_item_object(struct Pokemon *mon, struct PartyMenuBox *menuBox) { if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) { menuBox->itemSpriteId = CreateSprite(&sSpriteTemplate_HeldItem, menuBox->spriteCoords[2], menuBox->spriteCoords[3], 0); UpdatePartyMonHeldItemSprite(mon, menuBox); } } static void party_menu_link_mon_held_item_object(u16 species, u16 item, struct PartyMenuBox *menuBox) { if (species != SPECIES_NONE) { menuBox->itemSpriteId = CreateSprite(&sSpriteTemplate_HeldItem, menuBox->spriteCoords[2], menuBox->spriteCoords[3], 0); gSprites[menuBox->itemSpriteId].oam.priority = 0; ShowOrHideHeldItemSprite(item, menuBox); } } static void UpdatePartyMonHeldItemSprite(struct Pokemon *mon, struct PartyMenuBox *menuBox) { ShowOrHideHeldItemSprite(GetMonData(mon, MON_DATA_HELD_ITEM), menuBox); } static void ShowOrHideHeldItemSprite(u16 item, struct PartyMenuBox *menuBox) { if (item == ITEM_NONE) { gSprites[menuBox->itemSpriteId].invisible = TRUE; } else { if (ItemIsMail(item)) StartSpriteAnim(&gSprites[menuBox->itemSpriteId], 1); else StartSpriteAnim(&gSprites[menuBox->itemSpriteId], 0); gSprites[menuBox->itemSpriteId].invisible = FALSE; } } void LoadHeldItemIcons(void) { LoadSpriteSheet(&sSpriteSheet_HeldItem); LoadSpritePalette(&sSpritePalette_HeldItem); } void DrawHeldItemIconsForTrade(u8 *partyCounts, u8 *partySpriteIds, u8 whichParty) { u16 i; u16 item; switch (whichParty) { case TRADE_PLAYER: for (i = 0; i < partyCounts[TRADE_PLAYER]; i++) { item = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); if (item != ITEM_NONE) CreateHeldItemSpriteForTrade(partySpriteIds[i], ItemIsMail(item)); } break; case TRADE_PARTNER: for (i = 0; i < partyCounts[TRADE_PARTNER]; i++) { item = GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM); if (item != ITEM_NONE) CreateHeldItemSpriteForTrade(partySpriteIds[i + PARTY_SIZE], ItemIsMail(item)); } break; } } static void CreateHeldItemSpriteForTrade(u8 spriteId, bool8 isMail) { u8 subpriority = gSprites[spriteId].subpriority; u8 newSpriteId = CreateSprite(&sSpriteTemplate_HeldItem, 250, 170, subpriority - 1); gSprites[newSpriteId].pos2.x = 4; gSprites[newSpriteId].pos2.y = 10; gSprites[newSpriteId].callback = SpriteCB_HeldItem; gSprites[newSpriteId].data[7] = spriteId; StartSpriteAnim(&gSprites[newSpriteId], isMail); gSprites[newSpriteId].callback(&gSprites[newSpriteId]); } static void SpriteCB_HeldItem(struct Sprite *sprite) { u8 otherSpriteId = sprite->data[7]; if (gSprites[otherSpriteId].invisible) { sprite->invisible = TRUE; } else { sprite->invisible = FALSE; sprite->pos1.x = gSprites[otherSpriteId].pos1.x + gSprites[otherSpriteId].pos2.x; sprite->pos1.y = gSprites[otherSpriteId].pos1.y + gSprites[otherSpriteId].pos2.y; } } static void party_menu_pokeball_object(struct Pokemon *mon, struct PartyMenuBox *menuBox) { if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) menuBox->pokeballSpriteId = CreateSprite(&sSpriteTemplate_MenuPokeball, menuBox->spriteCoords[6], menuBox->spriteCoords[7], 8); } static void party_menu_link_mon_pokeball_object(u16 species, struct PartyMenuBox *menuBox) { if (species != SPECIES_NONE) { menuBox->pokeballSpriteId = CreateSprite(&sSpriteTemplate_MenuPokeball, menuBox->spriteCoords[6], menuBox->spriteCoords[7], 8); gSprites[menuBox->pokeballSpriteId].oam.priority = 0; } } static u8 CreateCancelPokeballSprite(u8 x, u8 y) { u8 spriteId = CreateSprite(&sSpriteTemplate_MenuPokeball, x, y, 8); gSprites[spriteId].oam.priority = 2; return spriteId; } // For Confirm and Cancel in Choose 3 party menus static u8 CreateMenuPokeballSprite(u8 x, u8 y) { return CreateSprite(&gSpriteTemplate_8615F78, x, y, 8); } static void PartyMenuStartSpriteAnim(u8 spriteId, u8 animNum) { StartSpriteAnim(&gSprites[spriteId], animNum); } static void sub_81B5FBC(u8 spriteId, u8 spriteId2, u8 a) { if (a == 0) { StartSpriteAnim(&gSprites[spriteId], 2); StartSpriteAnim(&gSprites[spriteId2], 4); gSprites[spriteId].pos2.y = 0; gSprites[spriteId2].pos2.y = 0; } else { StartSpriteAnim(&gSprites[spriteId], 3); StartSpriteAnim(&gSprites[spriteId2], 5); gSprites[spriteId].pos2.y = -4; gSprites[spriteId2].pos2.y = 4; } } static void LoadPartyMenuPokeballGfx(void) { LoadCompressedSpriteSheet(&sSpriteSheet_MenuPokeball); LoadCompressedSpriteSheet(&sSpriteSheet_MenuPokeballSmall); LoadCompressedSpritePalette(&sSpritePalette_MenuPokeball); } static void party_menu_status_condition_object(struct Pokemon *mon, struct PartyMenuBox *menuBox) { if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE) { menuBox->statusSpriteId = CreateSprite(&sSpriteTemplate_StatusIcons, menuBox->spriteCoords[4], menuBox->spriteCoords[5], 0); SetPartyMonAilmentGfx(mon, menuBox); } } static void party_menu_link_mon_status_condition_object(u16 species, u8 status, struct PartyMenuBox *menuBox) { if (species != SPECIES_NONE) { menuBox->statusSpriteId = CreateSprite(&sSpriteTemplate_StatusIcons, menuBox->spriteCoords[4], menuBox->spriteCoords[5], 0); UpdatePartyMonAilmentGfx(status, menuBox); gSprites[menuBox->statusSpriteId].oam.priority = 0; } } static void SetPartyMonAilmentGfx(struct Pokemon *mon, struct PartyMenuBox *menuBox) { UpdatePartyMonAilmentGfx(GetMonAilment(mon), menuBox); } static void UpdatePartyMonAilmentGfx(u8 status, struct PartyMenuBox *menuBox) { switch (status) { case AILMENT_NONE: case AILMENT_PKRS: gSprites[menuBox->statusSpriteId].invisible = TRUE; break; default: StartSpriteAnim(&gSprites[menuBox->statusSpriteId], status - 1); gSprites[menuBox->statusSpriteId].invisible = FALSE; break; } } static void LoadPartyMenuAilmentGfx(void) { LoadCompressedSpriteSheet(&sSpriteSheet_StatusIcons); LoadCompressedSpritePalette(&sSpritePalette_StatusIcons); } void CB2_ShowPartyMenuForItemUse(void) { MainCallback callback = CB2_ReturnToBagMenu; u8 partyLayout; u8 menuType; u8 i; u8 msgId; TaskFunc task; if (gMain.inBattle) { menuType = PARTY_MENU_TYPE_IN_BATTLE; partyLayout = GetPartyLayoutFromBattleType(); } else { menuType = PARTY_MENU_TYPE_FIELD; partyLayout = PARTY_LAYOUT_SINGLE; } if (GetItemEffectType(gSpecialVar_ItemId) == ITEM_EFFECT_SACRED_ASH) { gPartyMenu.slotId = 0; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE && GetMonData(&gPlayerParty[i], MON_DATA_HP) == 0) { gPartyMenu.slotId = i; break; } } task = sub_81B6280; msgId = PARTY_MSG_NONE; } else { if (GetPocketByItemId(gSpecialVar_ItemId) == POCKET_TM_HM) msgId = PARTY_MSG_TEACH_WHICH_MON; else msgId = PARTY_MSG_USE_ON_WHICH_MON; task = Task_HandleChooseMonInput; } InitPartyMenu(menuType, partyLayout, PARTY_ACTION_USE_ITEM, TRUE, msgId, task, callback); } static void CB2_ReturnToBagMenu(void) { if (InBattlePyramid() == FALSE) GoToBagMenu(RETURN_LOCATION_UNCHANGED, POCKETS_COUNT, NULL); else GoToBattlePyramidBagMenu(4, gPyramidBagCursorData.callback); } static void sub_81B6280(u8 taskId) { if (!gPaletteFade.active) { if (gPartyMenu.menuType == PARTY_MENU_TYPE_IN_BATTLE) sPartyMenuInternal->exitCallback = CB2_SetUpExitToBattleScreen; gItemUseCB(taskId, Task_ClosePartyMenuAfterText); } } static bool8 IsHPRecoveryItem(u16 item) { const u8 *effect; if (item == ITEM_ENIGMA_BERRY) effect = gSaveBlock1Ptr->enigmaBerry.itemEffect; else effect = gItemEffectTable[item - ITEM_POTION]; if (effect[4] & ITEM4_HEAL_HP) return TRUE; else return FALSE; } static void GetMedicineItemEffectMessage(u16 item) { switch (GetItemEffectType(item)) { case ITEM_EFFECT_CURE_POISON: StringExpandPlaceholders(gStringVar4, gText_PkmnCuredOfPoison); break; case ITEM_EFFECT_CURE_SLEEP: StringExpandPlaceholders(gStringVar4, gText_PkmnWokeUp2); break; case ITEM_EFFECT_CURE_BURN: StringExpandPlaceholders(gStringVar4, gText_PkmnBurnHealed); break; case ITEM_EFFECT_CURE_FREEZE: StringExpandPlaceholders(gStringVar4, gText_PkmnThawedOut); break; case ITEM_EFFECT_CURE_PARALYSIS: StringExpandPlaceholders(gStringVar4, gText_PkmnCuredOfParalysis); break; case ITEM_EFFECT_CURE_CONFUSION: StringExpandPlaceholders(gStringVar4, gText_PkmnSnappedOutOfConfusion); break; case ITEM_EFFECT_CURE_INFATUATION: StringExpandPlaceholders(gStringVar4, gText_PkmnGotOverInfatuation); break; case ITEM_EFFECT_CURE_ALL_STATUS: StringExpandPlaceholders(gStringVar4, gText_PkmnBecameHealthy); break; case ITEM_EFFECT_HP_EV: StringCopy(gStringVar2, gText_HP3); StringExpandPlaceholders(gStringVar4, gText_PkmnBaseVar2StatIncreased); break; case ITEM_EFFECT_ATK_EV: StringCopy(gStringVar2, gText_Attack3); StringExpandPlaceholders(gStringVar4, gText_PkmnBaseVar2StatIncreased); break; case ITEM_EFFECT_DEF_EV: StringCopy(gStringVar2, gText_Defense3); StringExpandPlaceholders(gStringVar4, gText_PkmnBaseVar2StatIncreased); break; case ITEM_EFFECT_SPEED_EV: StringCopy(gStringVar2, gText_Speed2); StringExpandPlaceholders(gStringVar4, gText_PkmnBaseVar2StatIncreased); break; case ITEM_EFFECT_SPATK_EV: StringCopy(gStringVar2, gText_SpAtk3); StringExpandPlaceholders(gStringVar4, gText_PkmnBaseVar2StatIncreased); break; case ITEM_EFFECT_SPDEF_EV: StringCopy(gStringVar2, gText_SpDef3); StringExpandPlaceholders(gStringVar4, gText_PkmnBaseVar2StatIncreased); break; case ITEM_EFFECT_PP_UP: case ITEM_EFFECT_PP_MAX: StringExpandPlaceholders(gStringVar4, gText_MovesPPIncreased); break; case ITEM_EFFECT_HEAL_PP: StringExpandPlaceholders(gStringVar4, gText_PPWasRestored); break; default: StringExpandPlaceholders(gStringVar4, gText_WontHaveEffect); break; } } static bool8 NotUsingHPEVItemOnShedinja(struct Pokemon *mon, u16 item) { if (GetItemEffectType(item) == ITEM_EFFECT_HP_EV && GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA) return FALSE; return TRUE; } static bool8 IsItemFlute(u16 item) { if (item == ITEM_BLUE_FLUTE || item == ITEM_RED_FLUTE || item == ITEM_YELLOW_FLUTE) return TRUE; return FALSE; } static bool8 ExecuteTableBasedItemEffect_(u8 partyMonIndex, u16 item, u8 monMoveIndex) { if (gMain.inBattle) return ExecuteTableBasedItemEffect(&gPlayerParty[partyMonIndex], item, GetPartyIdFromBattleSlot(partyMonIndex), monMoveIndex); else return ExecuteTableBasedItemEffect(&gPlayerParty[partyMonIndex], item, partyMonIndex, monMoveIndex); } void ItemUseCB_Medicine(u8 taskId, TaskFunc task) { u16 hp = 0; struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 item = gSpecialVar_ItemId; bool8 canHeal; if (NotUsingHPEVItemOnShedinja(mon, item)) { canHeal = IsHPRecoveryItem(item); if (canHeal == TRUE) { hp = GetMonData(mon, MON_DATA_HP); if (hp == GetMonData(mon, MON_DATA_MAX_HP)) canHeal = FALSE; } if (ExecuteTableBasedItemEffect_(gPartyMenu.slotId, item, 0)) { iTriedHonestlyIDid: gUnknown_0203CEE8 = FALSE; PlaySE(SE_SELECT); DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = task; return; } } else { goto iTriedHonestlyIDid; //TODO: resolve this goto } gUnknown_0203CEE8 = TRUE; if (!IsItemFlute(item)) { PlaySE(SE_KAIFUKU); if (gPartyMenu.action != PARTY_ACTION_REUSABLE_ITEM) RemoveBagItem(item, 1); } else { PlaySE(SE_BIDORO); } SetPartyMonAilmentGfx(mon, &sPartyMenuBoxes[gPartyMenu.slotId]); if (gSprites[sPartyMenuBoxes[gPartyMenu.slotId].statusSpriteId].invisible) DisplayPartyPokemonLevelCheck(mon, &sPartyMenuBoxes[gPartyMenu.slotId], 1); if (canHeal == TRUE) { if (hp == 0) AnimatePartySlot(gPartyMenu.slotId, 1); PartyMenuModifyHP(taskId, gPartyMenu.slotId, 1, GetMonData(mon, MON_DATA_HP) - hp, Task_DisplayHPRestoredMessage); ResetHPTaskData(taskId, 0, hp); return; } else { GetMonNickname(mon, gStringVar1); GetMedicineItemEffectMessage(item); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = task; } } static void Task_DisplayHPRestoredMessage(u8 taskId) { GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnHPRestoredByVar2); DisplayPartyMenuMessage(gStringVar4, FALSE); schedule_bg_copy_tilemap_to_vram(2); HandleBattleLowHpMusicChange(); gTasks[taskId].func = Task_ClosePartyMenuAfterText; } static void Task_ClosePartyMenuAfterText(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { if (gUnknown_0203CEE8 == FALSE) sPartyMenuInternal->exitCallback = NULL; Task_ClosePartyMenu(taskId); } } void ItemUseCB_ReduceEV(u8 taskId, TaskFunc task) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 item = gSpecialVar_ItemId; u8 effectType = GetItemEffectType(item); u16 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP); u16 ev = ItemEffectToMonEv(mon, effectType); bool8 cannotUseEffect = ExecuteTableBasedItemEffect_(gPartyMenu.slotId, item, 0); u16 newFriendship = GetMonData(mon, MON_DATA_FRIENDSHIP); u16 newEv = ItemEffectToMonEv(mon, effectType); if (cannotUseEffect || (friendship == newFriendship && ev == newEv)) { gUnknown_0203CEE8 = FALSE; PlaySE(SE_SELECT); DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = task; } else { gUnknown_0203CEE8 = TRUE; PlaySE(SE_KAIFUKU); RemoveBagItem(item, 1); GetMonNickname(mon, gStringVar1); ItemEffectToStatString(effectType, gStringVar2); if (friendship != newFriendship) { if (ev != newEv) StringExpandPlaceholders(gStringVar4, gText_PkmnFriendlyBaseVar2Fell); else StringExpandPlaceholders(gStringVar4, gText_PkmnFriendlyBaseVar2CantFall); } else { StringExpandPlaceholders(gStringVar4, gText_PkmnAdoresBaseVar2Fell); } DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = task; } } static u16 ItemEffectToMonEv(struct Pokemon *mon, u8 effectType) { switch (effectType) { case ITEM_EFFECT_HP_EV: if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_SHEDINJA) return GetMonData(mon, MON_DATA_HP_EV); break; case ITEM_EFFECT_ATK_EV: return GetMonData(mon, MON_DATA_ATK_EV); case ITEM_EFFECT_DEF_EV: return GetMonData(mon, MON_DATA_DEF_EV); case ITEM_EFFECT_SPEED_EV: return GetMonData(mon, MON_DATA_SPEED_EV); case ITEM_EFFECT_SPATK_EV: return GetMonData(mon, MON_DATA_SPATK_EV); case ITEM_EFFECT_SPDEF_EV: return GetMonData(mon, MON_DATA_SPDEF_EV); } return 0; } static void ItemEffectToStatString(u8 effectType, u8 *dest) { switch (effectType) { case ITEM_EFFECT_HP_EV: StringCopy(dest, gText_HP3); break; case ITEM_EFFECT_ATK_EV: StringCopy(dest, gText_Attack3); break; case ITEM_EFFECT_DEF_EV: StringCopy(dest, gText_Defense3); break; case ITEM_EFFECT_SPEED_EV: StringCopy(dest, gText_Speed2); break; case ITEM_EFFECT_SPATK_EV: StringCopy(dest, gText_SpAtk3); break; case ITEM_EFFECT_SPDEF_EV: StringCopy(dest, gText_SpDef3); break; } } static void ShowMoveSelectWindow(u8 slot) { u8 i; u8 moveCount = 0; u8 fontId = 1; u8 windowId = DisplaySelectionWindow(SELECTWINDOW_MOVES); u16 move; for (i = 0; i < MAX_MON_MOVES; i++) { move = GetMonData(&gPlayerParty[slot], MON_DATA_MOVE1 + i); AddTextPrinterParameterized(windowId, fontId, gMoveNames[move], 8, (i * 16) + 1, TEXT_SPEED_FF, NULL); if (move != MOVE_NONE) moveCount++; } InitMenuInUpperLeftCornerPlaySoundWhenAPressed(windowId, moveCount, 0); schedule_bg_copy_tilemap_to_vram(2); } static void Task_HandleWhichMoveInput(u8 taskId) { s8 input = Menu_ProcessInput(); if (input != MENU_NOTHING_CHOSEN) { if (input == MENU_B_PRESSED) { PlaySE(SE_SELECT); ReturnToUseOnWhichMon(taskId); } else { PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); SetSelectedMoveForPPItem(taskId); } } } void ItemUseCB_PPRecovery(u8 taskId, TaskFunc task) { const u8 *effect; u16 item = gSpecialVar_ItemId; if (item == ITEM_ENIGMA_BERRY) effect = gSaveBlock1Ptr->enigmaBerry.itemEffect; else effect = gItemEffectTable[item - ITEM_POTION]; if (!(effect[4] & ITEM4_HEAL_PP_ONE)) { gPartyMenu.unkE = 0; TryUsePPItem(taskId); } else { PlaySE(SE_SELECT); DisplayPartyMenuStdMessage(PARTY_MSG_RESTORE_WHICH_MOVE); ShowMoveSelectWindow(gPartyMenu.slotId); gTasks[taskId].func = Task_HandleWhichMoveInput; } } static void SetSelectedMoveForPPItem(u8 taskId) { PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); gPartyMenu.unkE = Menu_GetCursorPos(); TryUsePPItem(taskId); } static void ReturnToUseOnWhichMon(u8 taskId) { gTasks[taskId].func = Task_HandleChooseMonInput; sPartyMenuInternal->exitCallback = NULL; PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); DisplayPartyMenuStdMessage(PARTY_MSG_USE_ON_WHICH_MON); } static void TryUsePPItem(u8 taskId) { u16 move = MOVE_NONE; s16 *moveSlot = &gPartyMenu.unkE; u16 item = gSpecialVar_ItemId; struct PartyMenu *ptr = &gPartyMenu; struct Pokemon *mon; if (ExecuteTableBasedItemEffect_(ptr->slotId, item, *moveSlot)) { gUnknown_0203CEE8 = FALSE; PlaySE(SE_SELECT); DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_ClosePartyMenuAfterText; } else { gUnknown_0203CEE8 = TRUE; mon = &gPlayerParty[ptr->slotId]; PlaySE(SE_KAIFUKU); RemoveBagItem(item, 1); move = GetMonData(mon, MON_DATA_MOVE1 + *moveSlot); StringCopy(gStringVar1, gMoveNames[move]); GetMedicineItemEffectMessage(item); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_ClosePartyMenuAfterText; } } void ItemUseCB_PPUp(u8 taskId, TaskFunc task) { PlaySE(SE_SELECT); DisplayPartyMenuStdMessage(PARTY_MSG_BOOST_PP_WHICH_MOVE); ShowMoveSelectWindow(gPartyMenu.slotId); gTasks[taskId].func = Task_HandleWhichMoveInput; } u16 ItemIdToBattleMoveId(u16 item) { u16 tmNumber = item - ITEM_TM01_FOCUS_PUNCH; return sTMHMMoves[tmNumber]; } bool8 IsMoveHm(u16 move) { u8 i; for (i = 0; i < NUM_HIDDEN_MACHINES; i++) { if (sTMHMMoves[i + NUM_TECHNICAL_MACHINES] == move) return TRUE; } return FALSE; } bool8 MonKnowsMove(struct Pokemon *mon, u16 move) { u8 i; for (i = 0; i < MAX_MON_MOVES; i++) { if (GetMonData(mon, MON_DATA_MOVE1 + i) == move) return TRUE; } return FALSE; } static void DisplayLearnMoveMessage(const u8 *str) { StringExpandPlaceholders(gStringVar4, str); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); } static void DisplayLearnMoveMessageAndClose(u8 taskId, const u8 *str) { DisplayLearnMoveMessage(str); gTasks[taskId].func = Task_ClosePartyMenuAfterText; } // move[1] doesn't use constants cause I don't know if it's actually a move ID storage void ItemUseCB_TMHM(u8 taskId, TaskFunc task) { struct Pokemon *mon; s16 *move; u16 item; PlaySE(SE_SELECT); mon = &gPlayerParty[gPartyMenu.slotId]; move = &gPartyMenu.unkE; item = gSpecialVar_ItemId; GetMonNickname(mon, gStringVar1); move[0] = ItemIdToBattleMoveId(item); StringCopy(gStringVar2, gMoveNames[move[0]]); move[1] = 0; switch (CanMonLearnTMTutor(mon, item, 0)) { case CANNOT_LEARN_MOVE: DisplayLearnMoveMessageAndClose(taskId, gText_PkmnCantLearnMove); return; case ALREADY_KNOWS_MOVE: DisplayLearnMoveMessageAndClose(taskId, gText_PkmnAlreadyKnows); return; } if (GiveMoveToMon(mon, move[0]) != MON_HAS_MAX_MOVES) { gTasks[taskId].func = Task_LearnedMove; } else { DisplayLearnMoveMessage(gText_PkmnNeedsToReplaceMove); gTasks[taskId].func = Task_ReplaceMoveYesNo; } } static void Task_LearnedMove(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; s16 *move = &gPartyMenu.unkE; u16 item = gSpecialVar_ItemId; if (move[1] == 0) { AdjustFriendship(mon, 4); if (item < ITEM_HM01_CUT) RemoveBagItem(item, 1); } GetMonNickname(mon, gStringVar1); StringCopy(gStringVar2, gMoveNames[move[0]]); StringExpandPlaceholders(gStringVar4, gText_PkmnLearnedMove3); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_DoLearnedMoveFanfareAfterText; } static void Task_DoLearnedMoveFanfareAfterText(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PlayFanfare(MUS_FANFA1); gTasks[taskId].func = Task_LearnNextMoveOrClosePartyMenu; } } static void Task_LearnNextMoveOrClosePartyMenu(u8 taskId) { if (IsFanfareTaskInactive() && ((gMain.newKeys & A_BUTTON) || (gMain.newKeys & B_BUTTON))) { if (gPartyMenu.learnMoveState == 1) Task_TryLearningNextMove(taskId); else { if (gPartyMenu.learnMoveState == 2) // never occurs gSpecialVar_Result = TRUE; Task_ClosePartyMenu(taskId); } } } static void Task_ReplaceMoveYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleReplaceMoveYesNoInput; } } static void Task_HandleReplaceMoveYesNoInput(u8 taskId) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: DisplayPartyMenuMessage(gText_WhichMoveToForget, TRUE); gTasks[taskId].func = Task_ShowSummaryScreenToForgetMove; break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: StopLearningMovePrompt(taskId); break; } } static void Task_ShowSummaryScreenToForgetMove(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { sPartyMenuInternal->exitCallback = CB2_ShowSummaryScreenToForgetMove; Task_ClosePartyMenu(taskId); } } static void CB2_ShowSummaryScreenToForgetMove(void) { ShowSelectMovePokemonSummaryScreen(gPlayerParty, gPartyMenu.slotId, gPlayerPartyCount - 1, CB2_ReturnToPartyMenuWhileLearningMove, gPartyMenu.unkE); } static void CB2_ReturnToPartyMenuWhileLearningMove(void) { InitPartyMenu(PARTY_MENU_TYPE_FIELD, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_MON, TRUE, PARTY_MSG_NONE, Task_ReturnToPartyMenuWhileLearningMove, gPartyMenu.exitCallback); } static void Task_ReturnToPartyMenuWhileLearningMove(u8 taskId) { if (!gPaletteFade.active) { if (GetMoveSlotToReplace() != MAX_MON_MOVES) DisplayPartyMenuForgotMoveMessage(taskId); else StopLearningMovePrompt(taskId); } } static void DisplayPartyMenuForgotMoveMessage(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 move = GetMonData(mon, MON_DATA_MOVE1 + GetMoveSlotToReplace()); GetMonNickname(mon, gStringVar1); StringCopy(gStringVar2, gMoveNames[move]); DisplayLearnMoveMessage(gText_12PoofForgotMove); gTasks[taskId].func = Task_PartyMenuReplaceMove; } static void Task_PartyMenuReplaceMove(u8 taskId) { struct Pokemon *mon; u16 move; if (IsPartyMenuTextPrinterActive() != TRUE) { mon = &gPlayerParty[gPartyMenu.slotId]; RemoveMonPPBonus(mon, GetMoveSlotToReplace()); move = gPartyMenu.unkE; SetMonMoveSlot(mon, move, GetMoveSlotToReplace()); Task_LearnedMove(taskId); } } static void StopLearningMovePrompt(u8 taskId) { StringCopy(gStringVar2, gMoveNames[gPartyMenu.unkE]); StringExpandPlaceholders(gStringVar4, gText_StopLearningMove2); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_StopLearningMoveYesNo; } static void Task_StopLearningMoveYesNo(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = Task_HandleStopLearningMoveYesNoInput; } } static void Task_HandleStopLearningMoveYesNoInput(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: GetMonNickname(mon, gStringVar1); StringCopy(gStringVar2, gMoveNames[gPartyMenu.unkE]); StringExpandPlaceholders(gStringVar4, gText_MoveNotLearned); DisplayPartyMenuMessage(gStringVar4, TRUE); if (gPartyMenu.learnMoveState == 1) { gTasks[taskId].func = Task_TryLearningNextMoveAfterText; } else { if (gPartyMenu.learnMoveState == 2) // never occurs gSpecialVar_Result = FALSE; gTasks[taskId].func = Task_ClosePartyMenuAfterText; } break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: GetMonNickname(mon, gStringVar1); StringCopy(gStringVar2, gMoveNames[gPartyMenu.unkE]); DisplayLearnMoveMessage(gText_PkmnNeedsToReplaceMove); gTasks[taskId].func = Task_ReplaceMoveYesNo; break; } } static void Task_TryLearningNextMoveAfterText(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) Task_TryLearningNextMove(taskId); } void ItemUseCB_RareCandy(u8 taskId, TaskFunc task) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; struct PartyMenuInternal *ptr = sPartyMenuInternal; s16 *arrayPtr = ptr->data; u16 *itemPtr = &gSpecialVar_ItemId; bool8 cannotUseEffect; if (GetMonData(mon, MON_DATA_LEVEL) != MAX_LEVEL) { BufferMonStatsToTaskData(mon, arrayPtr); cannotUseEffect = ExecuteTableBasedItemEffect_(gPartyMenu.slotId, *itemPtr, 0); BufferMonStatsToTaskData(mon, &ptr->data[6]); } else { cannotUseEffect = TRUE; } PlaySE(SE_SELECT); if (cannotUseEffect) { gUnknown_0203CEE8 = FALSE; DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = task; } else { gUnknown_0203CEE8 = TRUE; PlayFanfareByFanfareNum(0); UpdateMonDisplayInfoAfterRareCandy(gPartyMenu.slotId, mon); RemoveBagItem(gSpecialVar_ItemId, 1); GetMonNickname(mon, gStringVar1); ConvertIntToDecimalStringN(gStringVar2, GetMonData(mon, MON_DATA_LEVEL), STR_CONV_MODE_LEFT_ALIGN, 3); StringExpandPlaceholders(gStringVar4, gText_PkmnElevatedToLvVar2); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_DisplayLevelUpStatsPg1; } } static void UpdateMonDisplayInfoAfterRareCandy(u8 slot, struct Pokemon *mon) { SetPartyMonAilmentGfx(mon, &sPartyMenuBoxes[slot]); if (gSprites[sPartyMenuBoxes[slot].statusSpriteId].invisible) DisplayPartyPokemonLevelCheck(mon, &sPartyMenuBoxes[slot], 1); DisplayPartyPokemonHPCheck(mon, &sPartyMenuBoxes[slot], 1); DisplayPartyPokemonMaxHPCheck(mon, &sPartyMenuBoxes[slot], 1); DisplayPartyPokemonHPBarCheck(mon, &sPartyMenuBoxes[slot]); UpdatePartyMonHPBar(sPartyMenuBoxes[slot].monSpriteId, mon); AnimatePartySlot(slot, 1); schedule_bg_copy_tilemap_to_vram(0); } static void Task_DisplayLevelUpStatsPg1(u8 taskId) { if (WaitFanfare(FALSE) && IsPartyMenuTextPrinterActive() != TRUE && ((gMain.newKeys & A_BUTTON) || (gMain.newKeys & B_BUTTON))) { PlaySE(SE_SELECT); DisplayLevelUpStatsPg1(taskId); gTasks[taskId].func = Task_DisplayLevelUpStatsPg2; } } static void Task_DisplayLevelUpStatsPg2(u8 taskId) { if ((gMain.newKeys & A_BUTTON) || (gMain.newKeys & B_BUTTON)) { PlaySE(SE_SELECT); DisplayLevelUpStatsPg2(taskId); gTasks[taskId].func = Task_TryLearnNewMoves; } } static void DisplayLevelUpStatsPg1(u8 taskId) { s16 *arrayPtr = sPartyMenuInternal->data; arrayPtr[12] = CreateLevelUpStatsWindow(); DrawLevelUpWindowPg1(arrayPtr[12], arrayPtr, &arrayPtr[6], 1, 2, 3); CopyWindowToVram(arrayPtr[12], 2); schedule_bg_copy_tilemap_to_vram(2); } static void DisplayLevelUpStatsPg2(u8 taskId) { s16 *arrayPtr = sPartyMenuInternal->data; DrawLevelUpWindowPg2(arrayPtr[12], &arrayPtr[6], 1, 2, 3); CopyWindowToVram(arrayPtr[12], 2); schedule_bg_copy_tilemap_to_vram(2); } static void Task_TryLearnNewMoves(u8 taskId) { u16 learnMove; if (WaitFanfare(0) && ((gMain.newKeys & A_BUTTON) || (gMain.newKeys & B_BUTTON))) { RemoveLevelUpStatsWindow(); learnMove = MonTryLearningNewMove(&gPlayerParty[gPartyMenu.slotId], TRUE); gPartyMenu.learnMoveState = 1; switch (learnMove) { case 0: // No moves to learn PartyMenuTryEvolution(taskId); break; case MON_HAS_MAX_MOVES: DisplayMonNeedsToReplaceMove(taskId); break; case MON_ALREADY_KNOWS_MOVE: gTasks[taskId].func = Task_TryLearningNextMove; break; default: DisplayMonLearnedMove(taskId, learnMove); break; } } } static void Task_TryLearningNextMove(u8 taskId) { u16 result = MonTryLearningNewMove(&gPlayerParty[gPartyMenu.slotId], FALSE); switch (result) { case 0: // No moves to learn PartyMenuTryEvolution(taskId); break; case MON_HAS_MAX_MOVES: DisplayMonNeedsToReplaceMove(taskId); break; case MON_ALREADY_KNOWS_MOVE: return; default: DisplayMonLearnedMove(taskId, result); break; } } static void PartyMenuTryEvolution(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 targetSpecies = GetEvolutionTargetSpecies(mon, 0, 0); if (targetSpecies != SPECIES_NONE) { FreePartyPointers(); gCB2_AfterEvolution = gPartyMenu.exitCallback; BeginEvolutionScene(mon, targetSpecies, 1, gPartyMenu.slotId); DestroyTask(taskId); } else { gTasks[taskId].func = Task_ClosePartyMenuAfterText; } } static void DisplayMonNeedsToReplaceMove(u8 taskId) { GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1); StringCopy(gStringVar2, gMoveNames[gMoveToLearn]); StringExpandPlaceholders(gStringVar4, gText_PkmnNeedsToReplaceMove); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gPartyMenu.unkE = gMoveToLearn; gTasks[taskId].func = Task_ReplaceMoveYesNo; } static void DisplayMonLearnedMove(u8 taskId, u16 move) { GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1); StringCopy(gStringVar2, gMoveNames[move]); StringExpandPlaceholders(gStringVar4, gText_PkmnLearnedMove3); DisplayPartyMenuMessage(gStringVar4, TRUE); schedule_bg_copy_tilemap_to_vram(2); gPartyMenu.unkE = move; gTasks[taskId].func = Task_DoLearnedMoveFanfareAfterText; } static void BufferMonStatsToTaskData(struct Pokemon *mon, s16 *data) { data[0] = GetMonData(mon, MON_DATA_MAX_HP); data[1] = GetMonData(mon, MON_DATA_ATK); data[2] = GetMonData(mon, MON_DATA_DEF); data[4] = GetMonData(mon, MON_DATA_SPATK); data[5] = GetMonData(mon, MON_DATA_SPDEF); data[3] = GetMonData(mon, MON_DATA_SPEED); } void ItemUseCB_SacredAsh(u8 taskId, TaskFunc task) { sPartyMenuInternal->data[0] = 0; sPartyMenuInternal->data[1] = 0; sPartyMenuInternal->data[2] = gPartyMenu.slotId; sub_81B7A28(taskId); } static void sub_81B7A28(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 hp; if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_NONE) { gTasks[taskId].func = task_sacred_ash_party_loop; return; } hp = GetMonData(mon, MON_DATA_HP); if (ExecuteTableBasedItemEffect_(gPartyMenu.slotId, gSpecialVar_ItemId, 0)) { gTasks[taskId].func = task_sacred_ash_party_loop; return; } PlaySE(SE_KAIFUKU); SetPartyMonAilmentGfx(mon, &sPartyMenuBoxes[gPartyMenu.slotId]); if (gSprites[sPartyMenuBoxes[gPartyMenu.slotId].statusSpriteId].invisible) DisplayPartyPokemonLevelCheck(mon, &sPartyMenuBoxes[gPartyMenu.slotId], 1); AnimatePartySlot(sPartyMenuInternal->data[2], 0); AnimatePartySlot(gPartyMenu.slotId, 1); PartyMenuModifyHP(taskId, gPartyMenu.slotId, 1, GetMonData(mon, MON_DATA_HP) - hp, Task_SacredAshDisplayHPRestored); ResetHPTaskData(taskId, 0, hp); sPartyMenuInternal->data[0] = 1; sPartyMenuInternal->data[1] = 1; } static void task_sacred_ash_party_loop(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { if (sPartyMenuInternal->data[0] == 1) { sPartyMenuInternal->data[0] = 0; sPartyMenuInternal->data[2] = gPartyMenu.slotId; } if (++(gPartyMenu.slotId) == PARTY_SIZE) { if (sPartyMenuInternal->data[1] == 0) { gUnknown_0203CEE8 = FALSE; DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); schedule_bg_copy_tilemap_to_vram(2); } else { gUnknown_0203CEE8 = TRUE; RemoveBagItem(gSpecialVar_ItemId, 1); } gTasks[taskId].func = Task_ClosePartyMenuAfterText; gPartyMenu.slotId = 0; } else { sub_81B7A28(taskId); } } } static void Task_SacredAshDisplayHPRestored(u8 taskId) { GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnHPRestoredByVar2); DisplayPartyMenuMessage(gStringVar4, FALSE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = task_sacred_ash_party_loop; } void ItemUseCB_EvolutionStone(u8 taskId, TaskFunc task) { PlaySE(SE_SELECT); gCB2_AfterEvolution = gPartyMenu.exitCallback; if (ExecuteTableBasedItemEffect_(gPartyMenu.slotId, gSpecialVar_ItemId, 0)) { gUnknown_0203CEE8 = FALSE; DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = task; } else { RemoveBagItem(gSpecialVar_ItemId, 1); FreePartyPointers(); } } u8 GetItemEffectType(u16 item) { const u8 *itemEffect; u32 statusCure; if (!ITEM_HAS_EFFECT(item)) return ITEM_EFFECT_NONE; // Read the item's effect properties. if (item == ITEM_ENIGMA_BERRY) itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect; else itemEffect = gItemEffectTable[item - ITEM_POTION]; if ((itemEffect[0] & (ITEM0_HIGH_CRIT | ITEM0_X_ATTACK)) || itemEffect[1] || itemEffect[2] || (itemEffect[3] & ITEM3_MIST)) return ITEM_EFFECT_X_ITEM; else if (itemEffect[0] & ITEM0_SACRED_ASH) return ITEM_EFFECT_SACRED_ASH; else if (itemEffect[3] & ITEM3_LEVEL_UP) return ITEM_EFFECT_RAISE_LEVEL; statusCure = itemEffect[3] & ITEM3_STATUS_ALL; if (statusCure || (itemEffect[0] >> 7)) { if (statusCure == ITEM3_SLEEP) return ITEM_EFFECT_CURE_SLEEP; else if (statusCure == ITEM3_POISON) return ITEM_EFFECT_CURE_POISON; else if (statusCure == ITEM3_BURN) return ITEM_EFFECT_CURE_BURN; else if (statusCure == ITEM3_FREEZE) return ITEM_EFFECT_CURE_FREEZE; else if (statusCure == ITEM3_PARALYSIS) return ITEM_EFFECT_CURE_PARALYSIS; else if (statusCure == ITEM3_CONFUSION) return ITEM_EFFECT_CURE_CONFUSION; else if (itemEffect[0] >> 7 && !statusCure) return ITEM_EFFECT_CURE_INFATUATION; else return ITEM_EFFECT_CURE_ALL_STATUS; } if (itemEffect[4] & (ITEM4_REVIVE | ITEM4_HEAL_HP)) return ITEM_EFFECT_HEAL_HP; else if (itemEffect[4] & ITEM4_EV_ATK) return ITEM_EFFECT_ATK_EV; else if (itemEffect[4] & ITEM4_EV_HP) return ITEM_EFFECT_HP_EV; else if (itemEffect[5] & ITEM5_EV_SPATK) return ITEM_EFFECT_SPATK_EV; else if (itemEffect[5] & ITEM5_EV_SPDEF) return ITEM_EFFECT_SPDEF_EV; else if (itemEffect[5] & ITEM5_EV_SPEED) return ITEM_EFFECT_SPEED_EV; else if (itemEffect[5] & ITEM5_EV_DEF) return ITEM_EFFECT_DEF_EV; else if (itemEffect[4] & ITEM4_EVO_STONE) return ITEM_EFFECT_EVO_STONE; else if (itemEffect[4] & ITEM4_PP_UP) return ITEM_EFFECT_PP_UP; else if (itemEffect[5] & ITEM5_PP_MAX) return ITEM_EFFECT_PP_MAX; else if (itemEffect[4] & (ITEM4_HEAL_PP_ALL | ITEM4_HEAL_PP_ONE)) return ITEM_EFFECT_HEAL_PP; else return ITEM_EFFECT_NONE; } static void TryTutorSelectedMon(u8 taskId) { struct Pokemon *mon; s16 *move; if (!gPaletteFade.active) { mon = &gPlayerParty[gPartyMenu.slotId]; move = &gPartyMenu.unkE; GetMonNickname(mon, gStringVar1); gPartyMenu.unkE = GetTutorMove(gSpecialVar_0x8005); StringCopy(gStringVar2, gMoveNames[gPartyMenu.unkE]); move[1] = 2; switch (CanMonLearnTMTutor(mon, 0, gSpecialVar_0x8005)) { case CANNOT_LEARN_MOVE: DisplayLearnMoveMessageAndClose(taskId, gText_PkmnCantLearnMove); return; case ALREADY_KNOWS_MOVE: DisplayLearnMoveMessageAndClose(taskId, gText_PkmnAlreadyKnows); return; default: if (GiveMoveToMon(mon, gPartyMenu.unkE) != MON_HAS_MAX_MOVES) { Task_LearnedMove(taskId); return; } break; } DisplayLearnMoveMessage(gText_PkmnNeedsToReplaceMove); gTasks[taskId].func = Task_ReplaceMoveYesNo; } } void CB2_PartyMenuFromStartMenu(void) { InitPartyMenu(PARTY_MENU_TYPE_FIELD, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, CB2_ReturnToFieldWithOpenMenu); } // Giving an item by selecting Give from the bag menu // As opposted to by selecting Give in the party menu, which is handled by CursorCb_Give void CB2_ChooseMonToGiveItem(void) { MainCallback callback = (InBattlePyramid() == FALSE) ? CB2_ReturnToBagMenu : CB2_ReturnToPyramidBagMenu; InitPartyMenu(PARTY_MENU_TYPE_FIELD, PARTY_LAYOUT_SINGLE, PARTY_ACTION_GIVE_ITEM, FALSE, PARTY_MSG_GIVE_TO_WHICH_MON, Task_HandleChooseMonInput, callback); gPartyMenu.bagItem = gSpecialVar_ItemId; } //TODO next static void TryGiveItemToSelectedMon(u8 taskId) { sPartyMenuItemId = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_HELD_ITEM); if (sPartyMenuItemId == ITEM_NONE) { GiveItemToSelectedMon(taskId); } else if (ItemIsMail(sPartyMenuItemId)) { sub_81B83B8(taskId); } else { DisplayAlreadyHoldingItemSwitchMessage(&gPlayerParty[gPartyMenu.slotId], sPartyMenuItemId, TRUE); gTasks[taskId].func = sub_81B82A0; } } static void GiveItemToSelectedMon(u8 taskId) { if (ItemIsMail(gPartyMenu.bagItem)) { RemoveItemToGiveFromBag(gPartyMenu.bagItem); sPartyMenuInternal->exitCallback = CB2_WriteMailToGiveMonFromBag; Task_ClosePartyMenu(taskId); } else { sub_81B8088(taskId); } } static void sub_81B8088(u8 taskId) { u16 item; if (!gPaletteFade.active) { item = gPartyMenu.bagItem; DisplayGaveHeldItemMessage(&gPlayerParty[gPartyMenu.slotId], item, FALSE, 1); GiveItemToMon(&gPlayerParty[gPartyMenu.slotId], item); RemoveItemToGiveFromBag(item); gTasks[taskId].func = Task_UpdateHeldItemSpriteAndClosePartyMenu; } } static void Task_UpdateHeldItemSpriteAndClosePartyMenu(u8 taskId) { s8 slot = gPartyMenu.slotId; if (IsPartyMenuTextPrinterActive() != TRUE) { UpdatePartyMonHeldItemSprite(&gPlayerParty[slot], &sPartyMenuBoxes[slot]); Task_ClosePartyMenu(taskId); } } static void CB2_WriteMailToGiveMonFromBag(void) { u8 mail; GiveItemToMon(&gPlayerParty[gPartyMenu.slotId], gPartyMenu.bagItem); mail = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_MAIL); DoEasyChatScreen( EASY_CHAT_TYPE_MAIL, gSaveBlock1Ptr->mail[mail].words, CB2_ReturnToPartyOrBagMenuFromWritingMail, EASY_CHAT_PERSON_DISPLAY_NONE); } static void CB2_ReturnToPartyOrBagMenuFromWritingMail(void) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; u16 item = GetMonData(mon, MON_DATA_HELD_ITEM); if (gSpecialVar_Result == FALSE) { TakeMailFromMon(mon); SetMonData(mon, MON_DATA_HELD_ITEM, &sPartyMenuItemId); RemoveBagItem(sPartyMenuItemId, 1); sub_81B841C(item); SetMainCallback2(gPartyMenu.exitCallback); } else { InitPartyMenu(gPartyMenu.menuType, KEEP_PARTY_LAYOUT, gPartyMenu.action, TRUE, PARTY_MSG_NONE, sub_81B8230, gPartyMenu.exitCallback); } } static void sub_81B8230(u8 taskId) { if (!gPaletteFade.active) { if (sPartyMenuItemId != ITEM_NONE) DisplaySwitchedHeldItemMessage(gPartyMenu.bagItem, sPartyMenuItemId, FALSE); else DisplayGaveHeldItemMessage(&gPlayerParty[gPartyMenu.slotId], gPartyMenu.bagItem, FALSE, 1); gTasks[taskId].func = Task_UpdateHeldItemSpriteAndClosePartyMenu; } } static void sub_81B82A0(u8 taskId) { if (IsPartyMenuTextPrinterActive() != TRUE) { PartyMenuDisplayYesNoMenu(); gTasks[taskId].func = sub_81B82D4; } } static void sub_81B82D4(u8 taskId) { u16 item; switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: item = gPartyMenu.bagItem; RemoveItemToGiveFromBag(item); if (AddBagItem(sPartyMenuItemId, 1) == FALSE) { sub_81B841C(item); BufferBagFullCantTakeItemMessage(sPartyMenuItemId); DisplayPartyMenuMessage(gStringVar4, FALSE); gTasks[taskId].func = Task_UpdateHeldItemSpriteAndClosePartyMenu; } else if (ItemIsMail(item)) { sPartyMenuInternal->exitCallback = CB2_WriteMailToGiveMonFromBag; Task_ClosePartyMenu(taskId); } else { GiveItemToMon(&gPlayerParty[gPartyMenu.slotId], item); DisplaySwitchedHeldItemMessage(item, sPartyMenuItemId, TRUE); gTasks[taskId].func = Task_UpdateHeldItemSpriteAndClosePartyMenu; } break; case MENU_B_PRESSED: PlaySE(SE_SELECT); // fallthrough case 1: gTasks[taskId].func = Task_UpdateHeldItemSpriteAndClosePartyMenu; break; } } static void sub_81B83B8(u8 taskId) { DisplayPartyMenuMessage(gText_RemoveMailBeforeItem, TRUE); schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_UpdateHeldItemSpriteAndClosePartyMenu; } static void RemoveItemToGiveFromBag(u16 item) { if (gPartyMenu.action == PARTY_ACTION_GIVE_PC_ITEM) // Unused, never occurs RemovePCItem(item, 1); else RemoveBagItem(item, 1); } static bool8 sub_81B841C(u16 item) { if (gPartyMenu.action == PARTY_ACTION_GIVE_ITEM) return AddBagItem(item, 1); else return AddPCItem(item, 1); } void ChooseMonToGiveMailFromMailbox(void) { InitPartyMenu(PARTY_MENU_TYPE_FIELD, PARTY_LAYOUT_SINGLE, PARTY_ACTION_GIVE_MAILBOX_MAIL, FALSE, PARTY_MSG_GIVE_TO_WHICH_MON, Task_HandleChooseMonInput, Mailbox_ReturnToMailListAfterDeposit); } static void TryGiveMailToSelectedMon(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; struct MailStruct *mail; gUnknown_0203CEE8 = FALSE; mail = &gSaveBlock1Ptr->mail[playerPCItemPageInfo.itemsAbove + 6 + playerPCItemPageInfo.cursorPos]; if (GetMonData(mon, MON_DATA_HELD_ITEM) != ITEM_NONE) { DisplayPartyMenuMessage(gText_PkmnHoldingItemCantHoldMail, TRUE); } else { GiveMailToMon2(mon, mail); ClearMailStruct(mail); DisplayPartyMenuMessage(gText_MailTransferredFromMailbox, TRUE); } schedule_bg_copy_tilemap_to_vram(2); gTasks[taskId].func = Task_UpdateHeldItemSpriteAndClosePartyMenu; } void InitChooseHalfPartyForBattle(u8 unused) { ClearSelectedPartyOrder(); InitPartyMenu(PARTY_MENU_TYPE_CHOOSE_HALF, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, gMain.savedCallback); gPartyMenu.task = Task_ValidateChosenHalfParty; } void ClearSelectedPartyOrder(void) { memset(gSelectedOrderFromParty, 0, sizeof(gSelectedOrderFromParty)); } static u8 GetPartySlotEntryStatus(s8 slot) { if (GetBattleEntryEligibility(&gPlayerParty[slot]) == FALSE) return 2; if (HasPartySlotAlreadyBeenSelected(slot + 1) == TRUE) return 1; return 0; } static bool8 GetBattleEntryEligibility(struct Pokemon *mon) { u16 i = 0; u16 species; if (GetMonData(mon, MON_DATA_IS_EGG) || GetMonData(mon, MON_DATA_LEVEL) > GetBattleEntryLevelCap() || (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY) && GetMonData(mon, MON_DATA_HELD_ITEM) != ITEM_NONE)) { return FALSE; } switch (VarGet(VAR_FRONTIER_FACILITY)) { case FACILITY_MULTI_OR_EREADER: if (GetMonData(mon, MON_DATA_HP) != 0) return TRUE; return FALSE; case FACILITY_UNION_ROOM: return TRUE; default: // Battle Frontier species = GetMonData(mon, MON_DATA_SPECIES); for (; gFrontierBannedSpecies[i] != 0xFFFF; i++) { if (gFrontierBannedSpecies[i] == species) return FALSE; } return TRUE; } } static u8 CheckBattleEntriesAndGetMessage(void) { u8 maxBattlers; u8 i, j; u8 facility; struct Pokemon *party = gPlayerParty; u8 minBattlers = GetMinBattleEntries(); u8 *order = gSelectedOrderFromParty; if (order[minBattlers - 1] == 0) { if (minBattlers == 1) return PARTY_MSG_NO_MON_FOR_BATTLE; ConvertIntToDecimalStringN(gStringVar1, minBattlers, STR_CONV_MODE_LEFT_ALIGN, 1); return PARTY_MSG_X_MONS_ARE_NEEDED; } facility = VarGet(VAR_FRONTIER_FACILITY); if (facility == FACILITY_UNION_ROOM || facility == FACILITY_MULTI_OR_EREADER) return 0xFF; maxBattlers = GetMaxBattleEntries(); for (i = 0; i < maxBattlers - 1; i++) { u16 species = GetMonData(&party[order[i] - 1], MON_DATA_SPECIES); u16 item = GetMonData(&party[order[i] - 1], MON_DATA_HELD_ITEM); for (j = i + 1; j < maxBattlers; j++) { if (species == GetMonData(&party[order[j] - 1], MON_DATA_SPECIES)) return PARTY_MSG_MONS_CANT_BE_SAME; if (item != ITEM_NONE && item == GetMonData(&party[order[j] - 1], MON_DATA_HELD_ITEM)) return PARTY_MSG_NO_SAME_HOLD_ITEMS; } } return 0xFF; } static bool8 HasPartySlotAlreadyBeenSelected(u8 slot) { u8 i; for (i = 0; i < ARRAY_COUNT(gSelectedOrderFromParty); i++) { if (gSelectedOrderFromParty[i] == slot) return TRUE; } return FALSE; } static void Task_ValidateChosenHalfParty(u8 taskId) { u8 msgId = CheckBattleEntriesAndGetMessage(); if (msgId != 0xFF) { PlaySE(SE_HAZURE); DisplayPartyMenuStdMessage(msgId); gTasks[taskId].func = Task_ContinueChoosingHalfParty; } else { PlaySE(SE_SELECT); Task_ClosePartyMenu(taskId); } } static void Task_ContinueChoosingHalfParty(u8 taskId) { if ((gMain.newKeys & A_BUTTON) || (gMain.newKeys & B_BUTTON)) { PlaySE(SE_SELECT); DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_MON); gTasks[taskId].func = Task_HandleChooseMonInput; } } static u8 GetMaxBattleEntries(void) { switch (VarGet(VAR_FRONTIER_FACILITY)) { case FACILITY_MULTI_OR_EREADER: return 3; case FACILITY_UNION_ROOM: return 2; default: // Battle Frontier return gSpecialVar_0x8005; } } static u8 GetMinBattleEntries(void) { switch (VarGet(VAR_FRONTIER_FACILITY)) { case FACILITY_MULTI_OR_EREADER: return 1; case FACILITY_UNION_ROOM: return 2; default: // Battle Frontier return gSpecialVar_0x8005; } } static u8 GetBattleEntryLevelCap(void) { switch (VarGet(VAR_FRONTIER_FACILITY)) { case FACILITY_MULTI_OR_EREADER: return MAX_LEVEL; case FACILITY_UNION_ROOM: return 30; default: // Battle Frontier if (gSpecialVar_0x8004 == FRONTIER_LVL_50) return 50; return MAX_LEVEL; } } static const u8* GetFacilityCancelString(void) { u8 facilityNum = VarGet(VAR_FRONTIER_FACILITY); if (!(facilityNum != FACILITY_UNION_ROOM && facilityNum != FACILITY_MULTI_OR_EREADER)) return gText_CancelBattle; else if (facilityNum == FRONTIER_FACILITY_DOME && gSpecialVar_0x8005 == 2) return gText_ReturnToWaitingRoom; else return gText_CancelChallenge; } void ChooseMonForTradingBoard(u8 initArg, MainCallback callback) { InitPartyMenu(initArg, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, callback); } void ChooseMonForMoveTutor(void) { InitPartyMenu(PARTY_MENU_TYPE_FIELD, PARTY_LAYOUT_SINGLE, PARTY_ACTION_MOVE_TUTOR, FALSE, PARTY_MSG_TEACH_WHICH_MON, Task_HandleChooseMonInput, CB2_ReturnToFieldContinueScriptPlayMapMusic); } void ChooseMonForWirelessMinigame(void) { InitPartyMenu(PARTY_MENU_TYPE_MINIGAME, PARTY_LAYOUT_SINGLE, PARTY_ACTION_MINIGAME, FALSE, PARTY_MSG_CHOOSE_MON_OR_CANCEL, Task_HandleChooseMonInput, CB2_ReturnToFieldContinueScriptPlayMapMusic); } static u8 GetPartyLayoutFromBattleType(void) { if (IsDoubleBattle() == FALSE) return PARTY_LAYOUT_SINGLE; if (IsMultiBattle() == TRUE) return PARTY_LAYOUT_MULTI; return PARTY_LAYOUT_DOUBLE; } void OpenPartyMenuInBattle(u8 partyAction) { InitPartyMenu(PARTY_MENU_TYPE_IN_BATTLE, GetPartyLayoutFromBattleType(), partyAction, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, CB2_SetUpReshowBattleScreenAfterMenu); nullsub_35(); UpdatePartyToBattleOrder(); } void ChooseMonForInBattleItem(void) { InitPartyMenu(PARTY_MENU_TYPE_IN_BATTLE, GetPartyLayoutFromBattleType(), PARTY_ACTION_USE_ITEM, FALSE, PARTY_MSG_USE_ON_WHICH_MON, Task_HandleChooseMonInput, CB2_ReturnToBagMenu); nullsub_35(); UpdatePartyToBattleOrder(); } static u8 GetPartyMenuActionsTypeInBattle(struct Pokemon *mon) { if (GetMonData(&gPlayerParty[1], MON_DATA_SPECIES) != SPECIES_NONE && GetMonData(mon, MON_DATA_IS_EGG) == FALSE) { if (gPartyMenu.action == PARTY_ACTION_SEND_OUT) return ACTIONS_SEND_OUT; if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA)) return ACTIONS_SHIFT; } return ACTIONS_SUMMARY_ONLY; } static bool8 TrySwitchInPokemon(void) { u8 slot = GetCursorSelectionMonId(); u8 newSlot; u8 i; // In a multi battle, slots 1, 4, and 5 are the partner's pokemon if (IsMultiBattle() == TRUE && (slot == 1 || slot == 4 || slot == 5)) { StringCopy(gStringVar1, GetTrainerPartnerName()); StringExpandPlaceholders(gStringVar4, gText_CantSwitchWithAlly); return FALSE; } if (GetMonData(&gPlayerParty[slot], MON_DATA_HP) == 0) { GetMonNickname(&gPlayerParty[slot], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnHasNoEnergy); return FALSE; } for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) == B_SIDE_PLAYER && GetPartyIdFromBattleSlot(slot) == gBattlerPartyIndexes[i]) { GetMonNickname(&gPlayerParty[slot], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnAlreadyInBattle); return FALSE; } } if (GetMonData(&gPlayerParty[slot], MON_DATA_IS_EGG)) { StringExpandPlaceholders(gStringVar4, gText_EggCantBattle); return FALSE; } if (GetPartyIdFromBattleSlot(slot) == gBattleStruct->field_8B) { GetMonNickname(&gPlayerParty[slot], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnAlreadySelected); return FALSE; } if (gPartyMenu.action == PARTY_ACTION_ABILITY_PREVENTS) { SetMonPreventsSwitchingString(); return FALSE; } if (gPartyMenu.action == PARTY_ACTION_CANT_SWITCH) { u8 currBattler = gBattlerInMenuId; GetMonNickname(&gPlayerParty[GetBattlerPartyId(gBattlerPartyIndexes[currBattler])], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_PkmnCantSwitchOut); return FALSE; } gUnknown_0203CEE9 = GetPartyIdFromBattleSlot(slot); gUnknown_0203CEE8 = TRUE; newSlot = GetBattlerPartyId(gBattlerPartyIndexes[gBattlerInMenuId]); SwitchPartyMonSlots(newSlot, slot); SwapPartyPokemon(&gPlayerParty[newSlot], &gPlayerParty[slot]); return TRUE; } void sub_81B8C68(void) { sub_81B8C88(gBattlePartyCurrentOrder, sub_806D7EC()); } static void sub_81B8C88(u8 *partyBattleOrder, bool8 multiplayerFlag) { u8 partyIndexes[PARTY_SIZE]; int i, j; if (IsMultiBattle() == TRUE) { if (multiplayerFlag) { partyBattleOrder[0] = 0 | (3 << 4); partyBattleOrder[1] = 5 | (4 << 4); partyBattleOrder[2] = 2 | (1 << 4); } else { partyBattleOrder[0] = 3 | (0 << 4); partyBattleOrder[1] = 2 | (1 << 4); partyBattleOrder[2] = 5 | (4 << 4); } return; } else if (IsDoubleBattle() == FALSE) { j = 1; partyIndexes[0] = gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)]; for (i = 0; i < PARTY_SIZE; i++) { if (i != partyIndexes[0]) { partyIndexes[j] = i; j++; } } } else { j = 2; partyIndexes[0] = gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)]; partyIndexes[1] = gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)]; for (i = 0; i < PARTY_SIZE; i++) { if (i != partyIndexes[0] && i != partyIndexes[1]) { partyIndexes[j] = i; j++; } } } for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) partyBattleOrder[i] = (partyIndexes[0 + (i * 2)] << 4) | partyIndexes[1 + (i * 2)]; } void sub_81B8D64(u8 battlerId, u8 multiplayerFlag) { sub_81B8D88(gBattleStruct->field_60[battlerId], multiplayerFlag, battlerId); } static void sub_81B8D88(u8 *ptr, bool8 multiplayerFlag, u8 battlerId) { u8 partyIndexes[PARTY_SIZE]; int i, j; u8 leftBattler; u8 rightBattler; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) { leftBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); rightBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } else { leftBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); rightBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); } if (IsMultiBattle() == TRUE) { if (multiplayerFlag) { ptr[0] = 0 | (3 << 4); ptr[1] = 5 | (4 << 4); ptr[2] = 2 | (1 << 4); } else { ptr[0] = 3 | (0 << 4); ptr[1] = 2 | (1 << 4); ptr[2] = 5 | (4 << 4); } return; } else if (IsDoubleBattle() == FALSE) { j = 1; partyIndexes[0] = gBattlerPartyIndexes[leftBattler]; for (i = 0; i < PARTY_SIZE; i++) { if (i != partyIndexes[0]) { partyIndexes[j] = i; j++; } } } else { j = 2; partyIndexes[0] = gBattlerPartyIndexes[leftBattler]; partyIndexes[1] = gBattlerPartyIndexes[rightBattler]; for (i = 0; i < PARTY_SIZE; i++) { if (i != partyIndexes[0] && i != partyIndexes[1]) { partyIndexes[j] = i; j++; } } } for (i = 0; i < 3; i++) ptr[i] = (partyIndexes[0 + (i * 2)] << 4) | partyIndexes[1 + (i * 2)]; } void sub_81B8E80(u8 battlerId, u8 unk, u8 arrayIndex) { u8 possiblePartyIndexes[PARTY_SIZE]; u8 unk2 = 0; int i, j; u8 *battleStructRelated; u8 possiblePartyIndexBuffer; if (IsMultiBattle()) { battleStructRelated = gBattleStruct->field_60[battlerId]; for (i = j = 0; i < 3; j++, i++) { possiblePartyIndexes[j] = battleStructRelated[i] >> 4; j++; possiblePartyIndexes[j] = battleStructRelated[i] & 0xF; } possiblePartyIndexBuffer = possiblePartyIndexes[arrayIndex]; for (i = 0; i < PARTY_SIZE; i++) { if (possiblePartyIndexes[i] == unk) { unk2 = possiblePartyIndexes[i]; possiblePartyIndexes[i] = possiblePartyIndexBuffer; break; } } if (i != PARTY_SIZE) { possiblePartyIndexes[arrayIndex] = unk2; battleStructRelated[0] = (possiblePartyIndexes[0] << 4) | possiblePartyIndexes[1]; battleStructRelated[1] = (possiblePartyIndexes[2] << 4) | possiblePartyIndexes[3]; battleStructRelated[2] = (possiblePartyIndexes[4] << 4) | possiblePartyIndexes[5]; } } } static u8 GetPartyIdFromBattleSlot(u8 slot) { u8 modResult = slot & 1; u8 retVal; slot /= 2; if (modResult != 0) retVal = gBattlePartyCurrentOrder[slot] & 0xF; else retVal = gBattlePartyCurrentOrder[slot] >> 4; return retVal; } static void SetPartyIdAtBattleSlot(u8 slot, u8 setVal) { bool32 modResult = slot & 1; slot /= 2; if (modResult != 0) gBattlePartyCurrentOrder[slot] = (gBattlePartyCurrentOrder[slot] & 0xF0) | setVal; else gBattlePartyCurrentOrder[slot] = (gBattlePartyCurrentOrder[slot] & 0xF) | (setVal << 4); } void SwitchPartyMonSlots(u8 slot, u8 slot2) { u8 partyId = GetPartyIdFromBattleSlot(slot); SetPartyIdAtBattleSlot(slot, GetPartyIdFromBattleSlot(slot2)); SetPartyIdAtBattleSlot(slot2, partyId); } // TODO: rename? get party id from battle party id? u8 GetBattlerPartyId(u8 slot) { u8 i, j; for (j = i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); j++, i++) { if ((gBattlePartyCurrentOrder[i] >> 4) != slot) { j++; if ((gBattlePartyCurrentOrder[i] & 0xF) == slot) return j; } else { return j; } } return 0; } static void UpdatePartyToBattleOrder(void) { struct Pokemon *partyBuffer = Alloc(sizeof(gPlayerParty)); u8 i; memcpy(partyBuffer, gPlayerParty, sizeof(gPlayerParty)); for (i = 0; i < PARTY_SIZE; i++) memcpy(&gPlayerParty[GetBattlerPartyId(i)], &partyBuffer[i], sizeof(struct Pokemon)); Free(partyBuffer); } static void UpdatePartyToFieldOrder(void) { struct Pokemon *partyBuffer = Alloc(sizeof(gPlayerParty)); u8 i; memcpy(partyBuffer, gPlayerParty, sizeof(gPlayerParty)); for (i = 0; i < PARTY_SIZE; i++) memcpy(&gPlayerParty[GetPartyIdFromBattleSlot(i)], &partyBuffer[i], sizeof(struct Pokemon)); Free(partyBuffer); } // Unused static void SwitchAliveMonIntoLeadSlot(void) { u8 i; struct Pokemon *mon; u8 partyId; for (i = 1; i < PARTY_SIZE; i++) { mon = &gPlayerParty[GetPartyIdFromBattleSlot(i)]; if (GetMonData(mon, MON_DATA_SPECIES) != SPECIES_NONE && GetMonData(mon, MON_DATA_HP) != 0) { partyId = GetPartyIdFromBattleSlot(0); SwitchPartyMonSlots(0, i); SwapPartyPokemon(&gPlayerParty[partyId], mon); break; } } } static void CB2_SetUpExitToBattleScreen(void) { SetMainCallback2(CB2_SetUpReshowBattleScreenAfterMenu); } void ShowPartyMenuToShowcaseMultiBattleParty(void) { InitPartyMenu(PARTY_MENU_TYPE_IN_MULTI_BATTLE, PARTY_LAYOUT_MULTI_SHOWCASE, PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_NONE, Task_InitMultiPartnerPartySlideIn, gMain.savedCallback); } #define tXPos data[0] // set up multi party partner slide static void Task_InitMultiPartnerPartySlideIn(u8 taskId) { // The first slide step also sets the sprites offscreen gTasks[taskId].tXPos = 256; SlideMultiPartyMenuBoxSpritesOneStep(taskId); ChangeBgX(2, 0x10000, 0); gTasks[taskId].func = Task_MultiPartnerPartySlideIn; } static void Task_MultiPartnerPartySlideIn(u8 taskId) { s16 *data = gTasks[taskId].data; u8 i; if (!gPaletteFade.active) { tXPos -= 8; SlideMultiPartyMenuBoxSpritesOneStep(taskId); if (tXPos == 0) { for (i = 3; i < PARTY_SIZE; i++) { if (gMultiPartnerParty[i - 3].species != SPECIES_NONE) AnimateSelectedPartyIcon(sPartyMenuBoxes[i].monSpriteId, 0); } PlaySE(SE_W231); // The Harden SE plays once the partners party mons have slid on screen gTasks[taskId].func = Task_WaitAfterMultiPartnerPartySlideIn; } } } static void Task_WaitAfterMultiPartnerPartySlideIn(u8 taskId) { s16 *data = gTasks[taskId].data; // data[0] used as a timer afterwards rather than the x pos if (++data[0] == 256) Task_ClosePartyMenu(taskId); } static void MoveMultiPartyMenuBoxSprite(u8 spriteId, s16 x) { if (x >= 0) gSprites[spriteId].pos2.x = x; } static void SlideMultiPartyMenuBoxSpritesOneStep(u8 taskId) { s16 *data = gTasks[taskId].data; u8 i; for (i = 3; i < PARTY_SIZE; i++) { if (gMultiPartnerParty[i - 3].species != SPECIES_NONE) { MoveMultiPartyMenuBoxSprite(sPartyMenuBoxes[i].monSpriteId, tXPos - 8); MoveMultiPartyMenuBoxSprite(sPartyMenuBoxes[i].itemSpriteId, tXPos - 8); MoveMultiPartyMenuBoxSprite(sPartyMenuBoxes[i].pokeballSpriteId, tXPos - 8); MoveMultiPartyMenuBoxSprite(sPartyMenuBoxes[i].statusSpriteId, tXPos - 8); } } ChangeBgX(2, 0x800, 1); } #undef tXpos void ChooseMonForDaycare(void) { InitPartyMenu(PARTY_MENU_TYPE_DAYCARE, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON_2, Task_HandleChooseMonInput, BufferMonSelection); } // Unused static void ChoosePartyMonByMenuType(u8 menuType) { gFieldCallback2 = CB2_FadeFromPartyMenu; InitPartyMenu(menuType, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_AND_CLOSE, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, CB2_ReturnToField); } static void BufferMonSelection(void) { gSpecialVar_0x8004 = GetCursorSelectionMonId(); if (gSpecialVar_0x8004 >= PARTY_SIZE) gSpecialVar_0x8004 = 0xFF; gFieldCallback2 = CB2_FadeFromPartyMenu; SetMainCallback2(CB2_ReturnToField); } bool8 CB2_FadeFromPartyMenu(void) { pal_fill_black(); CreateTask(Task_PartyMenuWaitForFade, 10); return TRUE; } static void Task_PartyMenuWaitForFade(u8 taskId) { if (IsWeatherNotFadingIn()) { DestroyTask(taskId); ScriptContext2_Disable(); EnableBothScriptContexts(); } } void ChooseContestMon(void) { ScriptContext2_Enable(); FadeScreen(FADE_TO_BLACK, 0); CreateTask(Task_ChooseContestMon, 10); } static void Task_ChooseContestMon(u8 taskId) { if (!gPaletteFade.active) { CleanupOverworldWindowsAndTilemaps(); InitPartyMenu(PARTY_MENU_TYPE_CONTEST, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_AND_CLOSE, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, CB2_ChooseContestMon); DestroyTask(taskId); } } static void CB2_ChooseContestMon(void) { gContestMonPartyIndex = GetCursorSelectionMonId(); if (gContestMonPartyIndex >= PARTY_SIZE) gContestMonPartyIndex = 0xFF; gSpecialVar_0x8004 = gContestMonPartyIndex; gFieldCallback2 = CB2_FadeFromPartyMenu; SetMainCallback2(CB2_ReturnToField); } // Used as a script special for showing a party mon to various npcs (e.g. in-game trades, move deleter) void ChoosePartyMon(void) { ScriptContext2_Enable(); FadeScreen(FADE_TO_BLACK, 0); CreateTask(Task_ChoosePartyMon, 10); } static void Task_ChoosePartyMon(u8 taskId) { if (!gPaletteFade.active) { CleanupOverworldWindowsAndTilemaps(); InitPartyMenu(PARTY_MENU_TYPE_CHOOSE_MON, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_AND_CLOSE, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, BufferMonSelection); DestroyTask(taskId); } } void ChooseMonForMoveRelearner(void) { ScriptContext2_Enable(); FadeScreen(FADE_TO_BLACK, 0); CreateTask(Task_ChooseMonForMoveRelearner, 10); } static void Task_ChooseMonForMoveRelearner(u8 taskId) { if (!gPaletteFade.active) { CleanupOverworldWindowsAndTilemaps(); InitPartyMenu(PARTY_MENU_TYPE_MOVE_RELEARNER, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_AND_CLOSE, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, CB2_ChooseMonForMoveRelearner); DestroyTask(taskId); } } static void CB2_ChooseMonForMoveRelearner(void) { gSpecialVar_0x8004 = GetCursorSelectionMonId(); if (gSpecialVar_0x8004 >= PARTY_SIZE) gSpecialVar_0x8004 = 0xFF; else gSpecialVar_0x8005 = GetNumberOfRelearnableMoves(&gPlayerParty[gSpecialVar_0x8004]); gFieldCallback2 = CB2_FadeFromPartyMenu; SetMainCallback2(CB2_ReturnToField); } void DoBattlePyramidMonsHaveHeldItem(void) { u8 i; gSpecialVar_Result = FALSE; for (i = 0; i < 3; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) != ITEM_NONE) { gSpecialVar_Result = TRUE; break; } } } // Can be called if the Battle Pyramid Bag is full on exiting and at least one party mon still has held items // The player can then select to toss items from the bag or take/toss held items from the party void BattlePyramidChooseMonHeldItems(void) { ScriptContext2_Enable(); FadeScreen(FADE_TO_BLACK, 0); CreateTask(Task_BattlePyramidChooseMonHeldItems, 10); } static void Task_BattlePyramidChooseMonHeldItems(u8 taskId) { if (!gPaletteFade.active) { CleanupOverworldWindowsAndTilemaps(); InitPartyMenu(PARTY_MENU_TYPE_STORE_PYRAMID_HELD_ITEMS, PARTY_LAYOUT_SINGLE, PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, BufferMonSelection); DestroyTask(taskId); } } void MoveDeleterChooseMoveToForget(void) { ShowPokemonSummaryScreen(PSS_MODE_SELECT_MOVE, gPlayerParty, gSpecialVar_0x8004, gPlayerPartyCount - 1, CB2_ReturnToField); gFieldCallback = FieldCallback_ReturnToEventScript2; } void GetNumMovesSelectedMonHas(void) { u8 i; gSpecialVar_Result = 0; for (i = 0; i < MAX_MON_MOVES; i++) { if (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_MOVE1 + i) != MOVE_NONE) gSpecialVar_Result++; } } void BufferMoveDeleterNicknameAndMove(void) { struct Pokemon *mon = &gPlayerParty[gSpecialVar_0x8004]; u16 move = GetMonData(mon, MON_DATA_MOVE1 + gSpecialVar_0x8005); GetMonNickname(mon, gStringVar1); StringCopy(gStringVar2, gMoveNames[move]); } void MoveDeleterForgetMove(void) { u16 i; SetMonMoveSlot(&gPlayerParty[gSpecialVar_0x8004], MOVE_NONE, gSpecialVar_0x8005); RemoveMonPPBonus(&gPlayerParty[gSpecialVar_0x8004], gSpecialVar_0x8005); for (i = gSpecialVar_0x8005; i < MAX_MON_MOVES - 1; i++) ShiftMoveSlot(&gPlayerParty[gSpecialVar_0x8004], i, i + 1); } static void ShiftMoveSlot(struct Pokemon *mon, u8 slotTo, u8 slotFrom) { u16 move1 = GetMonData(mon, MON_DATA_MOVE1 + slotTo); u16 move0 = GetMonData(mon, MON_DATA_MOVE1 + slotFrom); u8 pp1 = GetMonData(mon, MON_DATA_PP1 + slotTo); u8 pp0 = GetMonData(mon, MON_DATA_PP1 + slotFrom); u8 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES); u8 ppBonusMask1 = gPPUpGetMask[slotTo]; u8 ppBonusMove1 = (ppBonuses & ppBonusMask1) >> (slotTo * 2); u8 ppBonusMask2 = gPPUpGetMask[slotFrom]; u8 ppBonusMove2 = (ppBonuses & ppBonusMask2) >> (slotFrom * 2); ppBonuses &= ~ppBonusMask1; ppBonuses &= ~ppBonusMask2; ppBonuses |= (ppBonusMove1 << (slotFrom * 2)) + (ppBonusMove2 << (slotTo * 2)); SetMonData(mon, MON_DATA_MOVE1 + slotTo, &move0); SetMonData(mon, MON_DATA_MOVE1 + slotFrom, &move1); SetMonData(mon, MON_DATA_PP1 + slotTo, &pp0); SetMonData(mon, MON_DATA_PP1 + slotFrom, &pp1); SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonuses); } void IsSelectedMonEgg(void) { if (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_IS_EGG)) gSpecialVar_Result = TRUE; else gSpecialVar_Result = FALSE; } void IsLastMonThatKnowsSurf(void) { u16 move; u32 i, j; gSpecialVar_Result = FALSE; move = GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_MOVE1 + gSpecialVar_0x8005); if (move == MOVE_SURF) { for (i = 0; i < CalculatePlayerPartyCount(); i++) { if (i != gSpecialVar_0x8004) { for (j = 0; j < MAX_MON_MOVES; j++) { if (GetMonData(&gPlayerParty[i], MON_DATA_MOVE1 + j) == MOVE_SURF) return; } } } if (AnyStorageMonWithMove(move) != TRUE) gSpecialVar_Result = TRUE; } }