#include "global.h" #include "malloc.h" #include "bg.h" #include "data.h" #include "decompress.h" #include "dma3.h" #include "dynamic_placeholder_text_util.h" #include "event_data.h" #include "field_screen_effect.h" #include "field_weather.h" #include "fldeff_misc.h" #include "gpu_regs.h" #include "graphics.h" #include "international_string_util.h" #include "item.h" #include "item_icon.h" #include "item_menu.h" #include "mail.h" #include "main.h" #include "menu.h" #include "mon_markings.h" #include "naming_screen.h" #include "overworld.h" #include "palette.h" #include "pc_screen_effect.h" #include "pokemon.h" #include "pokemon_icon.h" #include "pokemon_summary_screen.h" #include "pokemon_storage_system.h" #include "script.h" #include "sound.h" #include "string_util.h" #include "strings.h" #include "text.h" #include "text_window.h" #include "trig.h" #include "walda_phrase.h" #include "window.h" #include "constants/items.h" #include "constants/maps.h" #include "constants/moves.h" #include "constants/rgb.h" #include "constants/songs.h" // PC main menu options enum { OPTION_WITHDRAW, OPTION_DEPOSIT, OPTION_MOVE_MONS, OPTION_MOVE_ITEMS, OPTION_EXIT, OPTIONS_COUNT }; // IDs for messages to print with PrintMessage enum { MSG_EXIT_BOX, MSG_WHAT_YOU_DO, MSG_PICK_A_THEME, MSG_PICK_A_WALLPAPER, MSG_IS_SELECTED, MSG_JUMP_TO_WHICH_BOX, MSG_DEPOSIT_IN_WHICH_BOX, MSG_WAS_DEPOSITED, MSG_BOX_IS_FULL, MSG_RELEASE_POKE, MSG_WAS_RELEASED, MSG_BYE_BYE, MSG_MARK_POKE, MSG_LAST_POKE, MSG_PARTY_FULL, MSG_HOLDING_POKE, MSG_WHICH_ONE_WILL_TAKE, MSG_CANT_RELEASE_EGG, MSG_CONTINUE_BOX, MSG_CAME_BACK, MSG_WORRIED, MSG_SURPRISE, MSG_PLEASE_REMOVE_MAIL, MSG_IS_SELECTED2, MSG_GIVE_TO_MON, MSG_PLACED_IN_BAG, MSG_BAG_FULL, MSG_PUT_IN_BAG, MSG_ITEM_IS_HELD, MSG_CHANGED_TO_ITEM, MSG_CANT_STORE_MAIL, }; // IDs for how to resolve variables in the above messages enum { MSG_VAR_NONE, MSG_VAR_MON_NAME_1, MSG_VAR_MON_NAME_2, // Unused MSG_VAR_MON_NAME_3, // Unused MSG_VAR_RELEASE_MON_1, MSG_VAR_RELEASE_MON_2, // Unused MSG_VAR_RELEASE_MON_3, MSG_VAR_ITEM_NAME, }; // IDs for menu selection items. See SetMenuText, HandleMenuInput, etc enum { MENU_CANCEL, MENU_STORE, MENU_WITHDRAW, MENU_MOVE, MENU_SHIFT, MENU_PLACE, MENU_SUMMARY, MENU_RELEASE, MENU_MARK, MENU_JUMP, MENU_WALLPAPER, MENU_NAME, MENU_TAKE, MENU_GIVE, MENU_GIVE_2, MENU_SWITCH, MENU_BAG, MENU_INFO, MENU_SCENERY_1, MENU_SCENERY_2, MENU_SCENERY_3, MENU_ETCETERA, MENU_FRIENDS, MENU_FOREST, MENU_CITY, MENU_DESERT, MENU_SAVANNA, MENU_CRAG, MENU_VOLCANO, MENU_SNOW, MENU_CAVE, MENU_BEACH, MENU_SEAFLOOR, MENU_RIVER, MENU_SKY, MENU_POLKADOT, MENU_POKECENTER, MENU_MACHINE, MENU_SIMPLE, }; #define MENU_WALLPAPER_SETS_START MENU_SCENERY_1 #define MENU_WALLPAPERS_START MENU_FOREST // Return IDs for input handlers enum { INPUT_NONE, INPUT_MOVE_CURSOR, INPUT_2, // Unused INPUT_3, // Unused INPUT_CLOSE_BOX, INPUT_SHOW_PARTY, INPUT_HIDE_PARTY, INPUT_BOX_OPTIONS, INPUT_IN_MENU, INPUT_SCROLL_RIGHT, INPUT_SCROLL_LEFT, INPUT_DEPOSIT, INPUT_WITHDRAW, INPUT_MOVE_MON, INPUT_SHIFT_MON, INPUT_PLACE_MON, INPUT_TAKE_ITEM, INPUT_GIVE_ITEM, INPUT_SWITCH_ITEMS, INPUT_PRESSED_B, INPUT_MULTIMOVE_START, INPUT_MULTIMOVE_CHANGE_SELECTION, INPUT_MULTIMOVE_SINGLE, INPUT_MULTIMOVE_GRAB_SELECTION, INPUT_MULTIMOVE_UNABLE, INPUT_MULTIMOVE_MOVE_MONS, INPUT_MULTIMOVE_PLACE_MONS, }; enum { SCREEN_CHANGE_EXIT_BOX, SCREEN_CHANGE_SUMMARY_SCREEN, SCREEN_CHANGE_NAME_BOX, SCREEN_CHANGE_ITEM_FROM_BAG, }; enum { MODE_PARTY, MODE_BOX, MODE_MOVE, }; enum { CURSOR_AREA_IN_BOX, CURSOR_AREA_IN_PARTY, CURSOR_AREA_BOX_TITLE, CURSOR_AREA_BUTTONS, // Party Pokemon and Close Box }; #define CURSOR_AREA_IN_HAND CURSOR_AREA_BOX_TITLE // Alt name for cursor area used by Move Items enum { CURSOR_ANIM_BOUNCE, CURSOR_ANIM_STILL, CURSOR_ANIM_OPEN, CURSOR_ANIM_FIST, }; // Special box ids for the choose box menu #define BOXID_NONE_CHOSEN 200 #define BOXID_CANCELED 201 enum { PALTAG_MON_ICON_0 = 56000, PALTAG_MON_ICON_1, // Used implicitly in CreateMonIconSprite PALTAG_MON_ICON_2, // Used implicitly in CreateMonIconSprite PALTAG_3, // Unused PALTAG_4, // Unused PALTAG_5, // Unused PALTAG_DISPLAY_MON, PALTAG_7, PALTAG_MARKING_COMBO, PALTAG_BOX_TITLE, PALTAG_10, PALTAG_ITEM_ICON_0, PALTAG_ITEM_ICON_1, // Used implicitly in CreateItemIconSprites PALTAG_ITEM_ICON_2, // Used implicitly in CreateItemIconSprites PALTAG_MARKING_MENU, }; enum { GFXTAG_CURSOR, GFXTAG_CURSOR_SHADOW, GFXTAG_DISPLAY_MON, GFXTAG_BOX_TITLE, GFXTAG_BOX_TITLE_ALT, GFXTAG_WAVEFORM, GFXTAG_ARROW, GFXTAG_ITEM_ICON_0, GFXTAG_ITEM_ICON_1, // Used implicitly in CreateItemIconSprites GFXTAG_ITEM_ICON_2, // Used implicitly in CreateItemIconSprites GFXTAG_CHOOSE_BOX_MENU, GFXTAG_CHOOSE_BOX_MENU_SIDES, // Used implicitly in LoadChooseBoxMenuGfx GFXTAG_12, // Unused GFXTAG_MARKING_MENU, GFXTAG_14, // Unused GFXTAG_15, // Unused GFXTAG_MARKING_COMBO, GFXTAG_17, // Unused GFXTAG_MON_ICON, }; // The maximum number of Pokémon icons that can appear on-screen. // By default the limit is 40 (though in practice only 37 can be). #define MAX_MON_ICONS (IN_BOX_COUNT + PARTY_SIZE + 1 >= 40 ? IN_BOX_COUNT + PARTY_SIZE + 1 : 40) // The maximum number of item icons that can appear on-screen while // moving held items. 1 in the cursor, and 2 more while switching // between 2 Pokémon with held items #define MAX_ITEM_ICONS 3 // IDs for the item icons affine anims enum { ITEM_ANIM_NONE, ITEM_ANIM_APPEAR, ITEM_ANIM_DISAPPEAR, ITEM_ANIM_PICK_UP, ITEM_ANIM_PUT_DOWN, ITEM_ANIM_PUT_AWAY, ITEM_ANIM_LARGE, }; // IDs for the item icon sprite callbacks enum { ITEM_CB_WAIT_ANIM, ITEM_CB_TO_HAND, ITEM_CB_TO_MON, ITEM_CB_SWAP_TO_HAND, ITEM_CB_SWAP_TO_MON, ITEM_CB_UNUSED_1, ITEM_CB_UNUSED_2, ITEM_CB_HIDE_PARTY, }; enum { RELEASE_ANIM_RELEASE, RELEASE_ANIM_CAME_BACK, }; // IDs for InitMonPlaceChange enum { CHANGE_GRAB, CHANGE_PLACE, CHANGE_SHIFT, }; // Modes for selecting and moving Pokémon in the box. // "MULTIPLE" mode allows up to an entire box to be // picked up at once by pressing Select then holding // down the A button. While holding A down, the player // may move the cursor around to select multiple Pokémon. // This is MOVE_MODE_MULTIPLE_SELECTING. After releasing A // those Pokémon will be picked up and can be moved around // as a single unit. This is MOVE_MODE_MULTIPLE_MOVING enum { MOVE_MODE_NORMAL, MOVE_MODE_MULTIPLE_SELECTING, MOVE_MODE_MULTIPLE_MOVING, }; // IDs for the main functions for moving multiple Pokémon. // Given as arguments to MultiMove_SetFunction enum { MULTIMOVE_START, MULTIMOVE_CANCEL, // If only 1 Pokémon is grabbed MULTIMOVE_CHANGE_SELECTION, MULTIMOVE_GRAB_SELECTION, MULTIMOVE_MOVE_MONS, MULTIMOVE_PLACE_MONS, }; enum { TILEMAPID_PKMN_DATA, // The "Pkmn Data" text at the top of the display TILEMAPID_PARTY_MENU, TILEMAPID_CLOSE_BUTTON, TILEMAPID_COUNT }; struct Wallpaper { const u32 *tiles; const u32 *tilemap; const u16 *palettes; }; struct StorageMessage { const u8 *text; u8 format; }; struct StorageMenu { const u8 *text; int textId; }; struct UnkStruct_2000028 { const u8 *unk_00; u8 *unk_04; u16 unk_08; u16 unk_0a; u16 newField; void (*unk_0c)(struct UnkStruct_2000028 *data); }; struct UnkStruct_2000020 { struct UnkStruct_2000028 *unk_00; u8 unk_04; u8 unk_05; }; struct ChooseBoxMenu { struct Sprite *menuSprite; struct Sprite *menuSideSprites[4]; u32 unused1[3]; struct Sprite *arrowSprites[2]; u8 unused2[0x214]; bool32 loadedPalette; u16 tileTag; u16 paletteTag; u8 curBox; u8 unused3; u8 subpriority; }; struct ItemIcon { struct Sprite *sprite; u8 *tiles; u16 palIndex; u8 area; u8 pos; bool8 active; }; struct PokemonStorageSystemData { u8 state; u8 boxOption; u8 screenChangeType; bool8 isReshowingPSS; u8 taskId; struct UnkStruct_2000020 unk_0020; struct UnkStruct_2000028 unk_0028[8]; u16 partyMenuTilemapBuffer[0x108]; u16 partyMenuUnused; // Never read u16 partyMenuY; u8 field_2C4; // Unused u8 partyMenuMoveTimer; u8 showPartyMenuState; bool8 closeBoxFlashing; u8 closeBoxFlashTimer; bool8 closeBoxFlashState; s16 newCurrBoxId; u16 bg2_X; s16 scrollSpeed; u16 scrollTimer; u8 wallpaperOffset; u8 scrollUnused1; // Never read u8 scrollToBoxIdUnused; // Never read u16 scrollUnused2; // Never read s16 scrollDirectionUnused; // Never read. u16 scrollUnused3; // Never read u16 scrollUnused4; // Never read u16 scrollUnused5; // Never read u16 scrollUnused6; // Never read u8 filler[22]; u8 boxTitleTiles[1024]; u8 boxTitleCycleId; u8 wallpaperLoadState; // Written to, but never read. u8 wallpaperLoadBoxId; s8 wallpaperLoadDir; u16 boxTitlePal[16]; u16 boxTitlePalOffset; u16 boxTitleAltPalOffset; struct Sprite *curBoxTitleSprites[2]; struct Sprite *nextBoxTitleSprites[2]; struct Sprite *arrowSprites[2]; u32 boxTitlePalBits; u8 field_73C[80]; // Unused u16 field_78C; // Never read. s16 wallpaperSetId; s16 wallpaperId; u16 wallpaperTilemap[360]; u8 wallpaperChangeState; u8 scrollState; u8 scrollToBoxId; s8 scrollDirection; u8 *wallpaperTiles; struct Sprite *movingMonSprite; struct Sprite *partySprites[PARTY_SIZE]; struct Sprite *boxMonsSprites[IN_BOX_COUNT]; struct Sprite **field_B00; struct Sprite **releaseMonSpritePtr; u16 numIconsPerSpecies[MAX_MON_ICONS]; u16 iconSpeciesList[MAX_MON_ICONS]; u16 boxSpecies[IN_BOX_COUNT]; u32 boxPersonalities[IN_BOX_COUNT]; u8 field_C5C; u8 field_C5D; u8 numPartyToCompact; u16 field_C60; s16 field_C62; s16 field_C64; u16 field_C66; u8 field_C68; s8 field_C69; u8 field_C6A; u8 field_C6B; // Never read. struct WindowTemplate menuWindow; struct StorageMenu menuItems[7]; u8 menuItemsCount; u8 menuWidth; u8 menuUnusedField; // Never read. u16 menuWindowId; struct Sprite *cursorSprite; struct Sprite *cursorShadowSprite; s32 cursorNewX; s32 cursorNewY; u32 cursorSpeedX; u32 cursorSpeedY; s16 cursorTargetX; s16 cursorTargetY; u16 cursorMoveSteps; s8 cursorVerticalWrap; s8 cursorHorizontalWrap; u8 newCursorArea; u8 newCursorPosition; u8 cursorPrevHorizPos; u8 cursorFlipTimer; u8 cursorPalNums[2]; const u32 *displayMonPalette; u32 displayMonPersonality; u16 displayMonSpecies; u16 displayMonItemId; u16 displayUnusedVar; bool8 setMosaic; u8 displayMonMarkings; u8 displayMonLevel; bool8 displayMonIsEgg; u8 displayMonName[POKEMON_NAME_LENGTH + 1]; u8 displayMonNameText[36]; u8 displayMonSpeciesName[36]; u8 displayMonGenderLvlText[36]; u8 displayMonItemName[36]; bool8 (*monPlaceChangeFunc)(void); u8 monPlaceChangeState; u8 field_D91; struct Sprite *markingComboSprite; struct Sprite *waveformSprites[2]; u16 *markingComboTilesPtr; struct MonMarkingsMenu markMenu; struct ChooseBoxMenu chooseBoxMenu; struct Pokemon movingMon; struct Pokemon tempMon; s8 canReleaseMon; bool8 releaseStatusResolved; s8 releaseCheckBoxId; s8 releaseCheckBoxPos; s8 releaseBoxId; s8 releaseBoxPos; u16 releaseCheckState; u16 restrictedReleaseMonMoves; u16 restrictedMoveList[8]; u8 summaryMaxPos; u8 summaryStartPos; u8 summaryScreenMode; union { struct Pokemon *mon; struct BoxPokemon *box; } summaryMon; u8 messageText[40]; u8 boxTitleText[40]; u8 releaseMonName[POKEMON_NAME_LENGTH + 1]; u8 itemName[20]; u8 inBoxMovingMode; u16 multiMoveWindowId; struct ItemIcon itemIcons[MAX_ITEM_ICONS]; u16 movingItemId; u16 itemInfoWindowOffset; u8 field_2238; // Unused u16 displayMonPalOffset; u16 *displayMonTilePtr; struct Sprite *displayMonSprite; u16 displayMonPalBuffer[0x40]; u8 tileBuffer[0x800]; u8 field_2AC4[0x1800]; // Unused u8 field_42C4[0x800]; u8 wallpaperBgTilemapBuffer[0x1000]; u8 displayMenuTilemapBuffer[0x800]; }; static u32 sItemIconGfxBuffer[98]; EWRAM_DATA static u8 sPreviousBoxOption = 0; EWRAM_DATA static struct ChooseBoxMenu *sChooseBoxMenu = NULL; EWRAM_DATA static struct PokemonStorageSystemData *sPSSData = NULL; EWRAM_DATA static bool8 sInPartyMenu = 0; EWRAM_DATA static u8 sCurrentBoxOption = 0; EWRAM_DATA static u8 sDepositBoxId = 0; EWRAM_DATA static u8 sWhichToReshow = 0; EWRAM_DATA static u8 sLastUsedBox = 0; EWRAM_DATA static u16 sMovingItemId = 0; EWRAM_DATA static struct Pokemon sSavedMovingMon = {0}; EWRAM_DATA static s8 sCursorArea = 0; EWRAM_DATA static s8 sCursorPosition = 0; EWRAM_DATA static bool8 sIsMonBeingMoved = 0; EWRAM_DATA static u8 sMovingMonOrigBoxId = 0; EWRAM_DATA static u8 sMovingMonOrigBoxPos = 0; EWRAM_DATA static bool8 sAutoActionOn = 0; static void CreateMainMenu(u8, s16 *); static void Cb2_EnterPSS(u8); static u8 GetCurrentBoxOption(void); static u8 HandleInput(void); static u8 GetSavedCursorPos(void); static u8 GetNumPartySpritesCompacting(void); static void LoadWallpaperGfx(u8, s8); static void CreateIncomingBoxTitle(u8, s8); static void StartBoxScrollArrowsSlide(s8); static void SetCurrentBox(u8); static void CreateInitBoxTask(u8); static void ChooseBoxMenu_CreateSprites(u8); static void TrimOldWallpaper(void *); static void ChooseBoxMenu_DestroySprites(void); static void ChooseBoxMenu_MoveLeft(void); static void ScrollBackground(void); static void ChooseBoxMenu_MoveRight(void); static void ChooseBoxMenu_PrintInfo(void); static void UpdateCloseBoxButtonFlash(void); static void ToggleCursorAutoAction(void); static void LoadSavedMovingMon(void); static void sub_80CE8E4(void); static void GiveChosenBagItem(void); static void SetUpHidePartyMenu(void); static void DestroyAllPartyMonIcons(void); static void MoveHeldItemWithPartyMenu(void); static void LoadPSSMenuGfx(void); static void LoadWaveformSpritePalette(void); static void SaveCursorPos(void); static void sub_80CD36C(void); static void sub_80CD3EC(void); static void sub_80CAC1C(void); static void ReshowDisplayMon(void); static void SetScrollingBackground(void); static void sub_80CABE0(void); static void sub_80CAEAC(void); static void CreateItemIconSprites(void); static void TryHideItemAtCursor(void); static void ClearSavedCursorPos(void); static void InitMonIconFields(void); static void sub_80CA0D8(void); static void AddMenu(void); static void InitReleaseMon(void); static void InitCanReleaseMonVars(void); static void ReleaseMon(void); static void RefreshDisplayMonData(void); static void CreateDisplayMonSprite(void); static void CreateMarkingComboSprite(void); static void CreateWaveformSprites(void); static void ReshowReleaseMon(void); static void TrySetCursorFistAnim(void); static void ClearBottomWindow(void); static void InitSupplementalTilemaps(void); static void RemoveMenu(void); static void RefreshDisplayMon(void); static void MoveItemFromCursorToBag(void); static void PrintDisplayMonInfo(void); static void UpdateWaveformAnimation(void); static void AddWallpaperSetsMenu(void); static void CreateBoxScrollArrows(void); static void InitMenu(void); static void StopBoxScrollArrowsSlide(void); static void CreateCursorSprites(void); static void TryRefreshDisplayMon(void); static void CycleBoxTitleSprites(void); static void InitItemInfoWindow(void); static void DrawItemInfoWindow(u32); static void SetPartySlotTilemaps(void); static void PrintItemDescription(void); static void SaveMovingMon(void); static void SetCursorInParty(void); static void InitSummaryScreenData(void); static void TryShowItemAtCursor(void); static void StopFlashingCloseBoxButton(void); static void FreePSSData(void); static void AddBoxMenu(void); static void CycleBoxTitleColor(void); static void MoveMon(void); static void PlaceMon(void); static void UpdatePartySlotColors(void); static void sub_80CE22C(void); static void DoCursorNewPosUpdate(void); static void CompactPartySprites(void); static void StartFlashingCloseBoxButton(void); static void SetUpDoShowPartyMenu(void); static void StartDisplayMonMosaicEffect(void); static void SpriteCB_ChooseBoxArrow(struct Sprite *); static void SpriteCB_HeldMon(struct Sprite *); static void sub_80CB278(struct Sprite *); static void SpriteCB_Arrow(struct Sprite *); static bool32 WaitForWallpaperGfxLoad(void); static bool8 InitPSSWindows(void); static bool8 ResetReleaseMonSpritePtr(void); static bool8 TryHideReleaseMon(void); static bool8 IsInitBoxActive(void); static bool8 MonPlaceChange_CursorDown(void); static bool8 MonPlaceChange_CursorUp(void); static bool8 UpdateItemInfoWindowSlideIn(void); static bool8 UpdateItemInfoWindowSlideOut(void); static bool8 DoShowPartyMenu(void); static bool8 IsItemIconAnimActive(void); static bool8 ScrollToBox(void); static bool8 UpdateCursorPos(void); static bool8 HidePartyMenu(void); static bool8 IsMovingItem(void); static bool8 IsDisplayMosaicActive(void); static bool8 DoWallpaperGfxChange(void); static bool8 DoMonPlaceChange(void); static bool8 IsMenuLoading(void); static bool8 IsRemovingLastPartyMon(void); static bool8 CanShiftMon(void); static bool8 IsCursorOnCloseBox(void); static bool8 IsCursorOnBoxTitle(void); static bool8 IsCursorInBox(void); static bool8 IsMonBeingMoved(void); static bool8 TryStorePartyMonInBox(u8); static void Cb_InitPSS(u8); static void Cb_PlaceMon(u8); static void Cb_ChangeScreen(u8); static void Cb_ShowPSS(u8); static void Cb_OnBPressed(u8); static void Cb_HandleBoxOptions(u8); static void Cb_OnSelectedMon(u8); static void Cb_OnCloseBoxPressed(u8); static void Cb_HidePartyPokemon(u8); static void Cb_DepositMenu(u8); static void Cb_MoveMon(u8); static void Cb_GiveMovingItemToMon(u8); static void Cb_SwitchSelectedItem(u8); static void Cb_TakeItemForMoving(u8); static void Cb_WithdrawMon(u8); static void Cb_ShiftMon(u8); static void Cb_ShowPartyPokemon(u8); static void Cb_ShowItemInfo(u8); static void Cb_GiveItemFromBag(u8); static void Cb_ItemToBag(u8); static void Cb_TakeItemForMoving(u8); static void Cb_ShowMarkMenu(u8); static void Cb_ShowMonSummary(u8); static void Cb_ReleaseMon(u8); static void Cb_ReshowPSS(u8); static void Cb_MainPSS(u8); static void Cb_JumpBox(u8); static void Cb_HandleWallpapers(u8); static void Cb_NameBox(u8); static void Cb_PrintCantStoreMail(u8); static void Cb_HandleMovingMonFromParty(u8); static void SetUpScrollToBox(u8); static void StartCursorAnim(u8); static void SetMovingMonPriority(u8); static void InitMonPlaceChange(u8); static void SetMonMarkings(u8); static void ShowYesNoWindow(s8); static void SetCursorBoxPosition(u8); static void AnimateBoxScrollArrows(bool8); static void UpdateCloseBoxButtonTilemap(bool8); static void CreatePartyMonsSprites(bool8); static void PrintMessage(u8 id); static s16 HandleMenuInput(void); static s8 RunCanReleaseMon(void); static u8 GetCursorPosition(void); static void TakeItemFromMon(u8, u8); static void GiveItemToMon(u8, u8); static void MoveItemFromMonToBag(u8, u8); static void SwapItemsWithMon(u8, u8); static struct Sprite *CreateChooseBoxArrows(u16, u16, u8, u8, u8); static void SetWallpaperForCurrentBox(u8); static void AddWallpapersMenu(u8); static u16 GetMovingItemId(void); static void LoadDisplayMonGfx(u16, u32); static void SpriteCB_DisplayMonMosaic(struct Sprite *); static void SpriteCB_OutgoingBoxTitle(struct Sprite *); static void SpriteCB_MovePartyMonToNextSlot(struct Sprite *); static void SpriteCB_IncomingBoxTitle(struct Sprite *); static void MovePartySprites(s16); static void SetPartySlotTilemap(u8, bool8); static const u8 *GetMovingItemName(void); static void SetMenuText(u8); static void TryLoadItemIconAtPos(u8, u8); static void TryHideItemIconAtPos(u8, u8); static void InitItemIconInCursor(u16); static struct Sprite *CreateMonIconSprite(u16, u32, s16, s16, u8, u8); static void DestroyBoxMonIcon(struct Sprite *); static void SetBoxSpeciesAndPersonalities(u8); static void MovePartySpriteToNextSlot(struct Sprite *, u16); static void Task_InitBox(u8); static void InitBoxTitle(u8); static s8 DetermineBoxScrollDirection(u8); static void DrawWallpaper(const void *, s8, u8); static s16 GetBoxTitleBaseX(const u8 *); static bool8 MonPlaceChange_Shift(void); static bool8 MonPlaceChange_Grab(void); static bool8 MonPlaceChange_Place(void); static bool8 MultiMonPlaceChange_Up(void); static bool8 MultiMonPlaceChange_Down(void); static void GetCursorCoordsByPos(u8, u8, u16 *, u16 *); static void SetShiftedMonData(u8, u8); static void SetMovingMonData(u8, u8); static void SetPlacedMonData(u8, u8); static void PurgeMonOrBoxMon(u8, u8); static void SetDisplayMonData(void *, u8); static bool32 AtLeastThreeUsableMons(void); static u8 InBoxInput_Normal(void); static u8 InBoxInput_MovingMultiple(void); static u8 InBoxInput_SelectingMultiple(void); static s8 GetMenuItemTextId(u8); static u8 SetSelectionMenuTexts(void); static bool8 SetMenuTexts_Mon(void); static bool8 SetMenuTexts_Item(void); static void sub_80D2A90(struct UnkStruct_2000020 *, struct UnkStruct_2000028 *, u32); static void sub_80D2AA4(void); static void sub_80D2B88(struct UnkStruct_2000028 *); static void sub_80D2C1C(struct UnkStruct_2000028 *); static u8 GetBoxWallpaper(u8); static void SetBoxWallpaper(u8, u8); // Functions for moving multiple Pokémon at once static void MultiMove_Free(void); static bool8 MultiMove_Init(void); static bool8 MultiMove_RunFunction(void); static bool8 MultiMove_TryMoveGroup(u8); static bool8 MultiMove_CanPlaceSelection(void); static void MultiMove_SetFunction(u8); static u8 MultiMove_GetOrigin(void); static bool8 MultiMove_Start(void); static bool8 MultiMove_Cancel(void); static bool8 MultiMove_ChangeSelection(void); static bool8 MultiMove_GrabSelection(void); static bool8 MultiMove_MoveMons(void); static bool8 MultiMove_PlaceMons(void); static void MultiMove_SetIconToBg(u8, u8); static void MultiMove_ClearIconFromBg(u8, u8); static void MultiMove_ResetBg(void); static void MultiMove_UpdateSelectedIcons(void); static void MultiMove_InitMove(u16, u16, u16); static void MultiMove_GetMonsFromSelection(void); static void MultiMove_RemoveMonsFromBox(void); static void MultiMove_CreatePlacedMonIcons(void); static void MultiMove_SetPlacedMonData(void); static u8 MultiMove_UpdateMove(void); static void MultiMove_DeselectRow(u8, u8, u8); static void MultiMove_SelectRow(u8, u8, u8); static void MultiMove_SelectColumn(u8, u8, u8); static void MultiMove_DeselectColumn(u8, u8, u8); // Functions for Move Items mode static bool32 IsItemIconAtPosition(u8, u8); static const u32 *GetItemIconPic(u16); static const u32 *GetItemIconPalette(u16); static u8 GetNewItemIconIdx(void); static void SetItemIconPosition(u8, u8, u8); static void LoadItemIconGfx(u8, const u32 *, const u32 *); static void SetItemIconAffineAnim(u8, u8); static void SetItemIconActive(u8, bool8); static u8 GetItemIconIdxByPosition(u8, u8); static void SetItemIconCallback(u8, u8, u8, u8); static void SpriteCB_ItemIcon_SetPosToCursor(struct Sprite *); static void SpriteCB_ItemIcon_WaitAnim(struct Sprite *); static void SpriteCB_ItemIcon_ToHand(struct Sprite *); static void SpriteCB_ItemIcon_ToMon(struct Sprite *); static void SpriteCB_ItemIcon_SwapToHand(struct Sprite *); static void SpriteCB_ItemIcon_HideParty(struct Sprite *); static void SpriteCB_ItemIcon_SwapToMon(struct Sprite *); // Functions for the tilemap updating utility static void TilemapUtil_SetRect(u8, u16, u16, u16, u16); static void TilemapUtil_Move(u8, u8, s8); static void TilemapUtil_SetMap(u8, u8, const void *, u16, u16); static void TilemapUtil_SetPos(u8, u16, u16); static void TilemapUtil_Init(u8); static void TilemapUtil_Free(void); static void TilemapUtil_Update(u8); static void TilemapUtil_DrawPrev(u8); static void TilemapUtil_Draw(u8); struct { const u8 *text; const u8 *desc; } static const sMainMenuTexts[OPTIONS_COUNT] = { [OPTION_WITHDRAW] = {gText_WithdrawPokemon, gText_WithdrawMonDescription}, [OPTION_DEPOSIT] = {gText_DepositPokemon, gText_DepositMonDescription}, [OPTION_MOVE_MONS] = {gText_MovePokemon, gText_MoveMonDescription}, [OPTION_MOVE_ITEMS] = {gText_MoveItems, gText_MoveItemsDescription}, [OPTION_EXIT] = {gText_SeeYa, gText_SeeYaDescription} }; static const struct WindowTemplate sWindowTemplate_MainMenu = { .bg = 0, .tilemapLeft = 1, .tilemapTop = 1, .width = 17, .height = 10, .paletteNum = 15, .baseBlock = 0x1, }; static const union AnimCmd sAnim_ChooseBoxMenu_TopLeft[] = { ANIMCMD_FRAME(0, 5), ANIMCMD_END }; static const union AnimCmd sAnim_ChooseBoxMenu_BottomLeft[] = { ANIMCMD_FRAME(4, 5), ANIMCMD_END }; static const union AnimCmd sAnim_ChooseBoxMenu_TopRight[] = { ANIMCMD_FRAME(6, 5), ANIMCMD_END }; static const union AnimCmd sAnim_ChooseBoxMenu_BottomRight[] = { ANIMCMD_FRAME(10, 5), ANIMCMD_END }; static const union AnimCmd *const sAnims_ChooseBoxMenu[] = { sAnim_ChooseBoxMenu_TopLeft, sAnim_ChooseBoxMenu_BottomLeft, sAnim_ChooseBoxMenu_TopRight, sAnim_ChooseBoxMenu_BottomRight }; static const union AffineAnimCmd sAffineAnim_ChooseBoxMenu[] = { AFFINEANIMCMD_FRAME(0xE0, 0xE0, 0, 0), AFFINEANIMCMD_END }; // Unused static const union AffineAnimCmd *const sAffineAnims_ChooseBoxMenu[] = { sAffineAnim_ChooseBoxMenu }; static const u8 sChooseBoxMenu_TextColors[] = {TEXT_COLOR_RED, TEXT_DYNAMIC_COLOR_6, TEXT_DYNAMIC_COLOR_5}; static const u8 sText_OutOf30[] = _("/30"); static const u16 sChooseBoxMenu_Pal[] = INCBIN_U16("graphics/pokemon_storage/box_selection_popup.gbapal"); static const u8 sChooseBoxMenuCenter_Gfx[] = INCBIN_U8("graphics/pokemon_storage/box_selection_popup_center.4bpp"); static const u8 sChooseBoxMenuSides_Gfx[] = INCBIN_U8("graphics/pokemon_storage/box_selection_popup_sides.4bpp"); static const u32 sScrollingBg_Gfx[] = INCBIN_U32("graphics/pokemon_storage/scrolling_bg.4bpp.lz"); static const u32 sScrollingBg_Tilemap[] = INCBIN_U32("graphics/pokemon_storage/scrolling_bg.bin.lz"); static const u16 sDisplayMenu_Pal[] = INCBIN_U16("graphics/pokemon_storage/display_menu.gbapal"); // Unused static const u32 sDisplayMenu_Tilemap[] = INCBIN_U32("graphics/pokemon_storage/display_menu.bin.lz"); static const u16 sPkmnData_Tilemap[] = { 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, }; // sInterface_Pal - parts of the display frame, "PkmnData"'s normal color, Close Box static const u16 sInterface_Pal[] = INCBIN_U16("graphics/pokemon_storage/interface.gbapal"); static const u16 sPkmnDataGray_Pal[] = INCBIN_U16("graphics/pokemon_storage/pkmn_data_gray.gbapal"); static const u16 sBg_Pal[] = INCBIN_U16("graphics/pokemon_storage/bg.gbapal"); static const u16 sBgMoveItems_Pal[] = INCBIN_U16("graphics/pokemon_storage/bg_move_items.gbapal"); static const u16 sCloseBoxButton_Tilemap[] = { 0x014c, 0x014d, 0x014e, 0x014f, 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x015c, 0x015d, 0x015e, 0x015f, 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0175, 0x0176, 0x0177, 0x0178, 0x0179, 0x017a, 0x017b, 0x017c, 0x017d, 0x0185, 0x0186, 0x0187, 0x0188, 0x0189, 0x018a, 0x018b, 0x018c, 0x018d }; static const u16 sPartySlotFilled_Tilemap[] = { 0x1140, 0x1141, 0x1141, 0x1142, 0x1150, 0x1151, 0x1151, 0x1152, 0x1160, 0x1161, 0x1161, 0x1162, }; static const u16 sPartySlotEmpty_Tilemap[] = { 0x1143, 0x1144, 0x1144, 0x1145, 0x1153, 0x1154, 0x1154, 0x1155, 0x1163, 0x1164, 0x1164, 0x1165, }; static const u16 sWaveform_Pal[] = INCBIN_U16("graphics/pokemon_storage/waveform.gbapal"); static const u32 sWaveform_Gfx[] = INCBIN_U32("graphics/pokemon_storage/waveform.4bpp"); static const u16 gUnknown_085726B4[] = INCBIN_U16("graphics/unused/unknown_5726B4.gbapal"); static const u16 gUnknown_085726F4[] = INCBIN_U16("graphics/unknown/unknown_5726F4.gbapal"); static const struct WindowTemplate sWindowTemplates[] = { { .bg = 1, .tilemapLeft = 0, .tilemapTop = 11, .width = 9, .height = 7, .paletteNum = 3, .baseBlock = 0xC0, }, { .bg = 0, .tilemapLeft = 11, .tilemapTop = 17, .width = 18, .height = 2, .paletteNum = 15, .baseBlock = 0x14, }, { .bg = 0, .tilemapLeft = 0, .tilemapTop = 13, .width = 21, .height = 7, .paletteNum = 15, .baseBlock = 0x14, }, DUMMY_WIN_TEMPLATE }; static const struct BgTemplate sBgTemplates[] = { { .bg = 0, .charBaseIndex = 0, .mapBaseIndex = 29, .screenSize = 0, .paletteMode = 0, .priority = 0, .baseTile = 0 }, { .bg = 1, .charBaseIndex = 1, .mapBaseIndex = 30, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0x100 }, { .bg = 2, .charBaseIndex = 2, .mapBaseIndex = 27, .screenSize = 1, .paletteMode = 0, .priority = 2, .baseTile = 0 }, { .bg = 3, .charBaseIndex = 3, .mapBaseIndex = 31, .screenSize = 0, .paletteMode = 0, .priority = 3, .baseTile = 0 }, }; static const struct SpritePalette gWaveformSpritePalette = { sWaveform_Pal, PALTAG_10 }; static const struct SpriteSheet sSpriteSheet_Waveform = { sWaveform_Gfx, sizeof(sWaveform_Gfx), GFXTAG_WAVEFORM }; static const struct OamData sOamData_DisplayMon; static const struct SpriteTemplate sSpriteTemplate_DisplayMon = { .tileTag = GFXTAG_DISPLAY_MON, .paletteTag = PALTAG_DISPLAY_MON, .oam = &sOamData_DisplayMon, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct StorageMessage sMessages[] = { [MSG_EXIT_BOX] = {gText_ExitFromBox, MSG_VAR_NONE}, [MSG_WHAT_YOU_DO] = {gText_WhatDoYouWantToDo, MSG_VAR_NONE}, [MSG_PICK_A_THEME] = {gText_PleasePickATheme, MSG_VAR_NONE}, [MSG_PICK_A_WALLPAPER] = {gText_PickTheWallpaper, MSG_VAR_NONE}, [MSG_IS_SELECTED] = {gText_PkmnIsSelected, MSG_VAR_MON_NAME_1}, [MSG_JUMP_TO_WHICH_BOX] = {gText_JumpToWhichBox, MSG_VAR_NONE}, [MSG_DEPOSIT_IN_WHICH_BOX] = {gText_DepositInWhichBox, MSG_VAR_NONE}, [MSG_WAS_DEPOSITED] = {gText_PkmnWasDeposited, MSG_VAR_MON_NAME_1}, [MSG_BOX_IS_FULL] = {gText_BoxIsFull2, MSG_VAR_NONE}, [MSG_RELEASE_POKE] = {gText_ReleaseThisPokemon, MSG_VAR_NONE}, [MSG_WAS_RELEASED] = {gText_PkmnWasReleased, MSG_VAR_RELEASE_MON_1}, [MSG_BYE_BYE] = {gText_ByeByePkmn, MSG_VAR_RELEASE_MON_3}, [MSG_MARK_POKE] = {gText_MarkYourPkmn, MSG_VAR_NONE}, [MSG_LAST_POKE] = {gText_ThatsYourLastPkmn, MSG_VAR_NONE}, [MSG_PARTY_FULL] = {gText_YourPartysFull, MSG_VAR_NONE}, [MSG_HOLDING_POKE] = {gText_YoureHoldingAPkmn, MSG_VAR_NONE}, [MSG_WHICH_ONE_WILL_TAKE] = {gText_WhichOneWillYouTake, MSG_VAR_NONE}, [MSG_CANT_RELEASE_EGG] = {gText_YouCantReleaseAnEgg, MSG_VAR_NONE}, [MSG_CONTINUE_BOX] = {gText_ContinueBoxOperations, MSG_VAR_NONE}, [MSG_CAME_BACK] = {gText_PkmnCameBack, MSG_VAR_MON_NAME_1}, [MSG_WORRIED] = {gText_WasItWorriedAboutYou, MSG_VAR_NONE}, [MSG_SURPRISE] = {gText_FourEllipsesExclamation, MSG_VAR_NONE}, [MSG_PLEASE_REMOVE_MAIL] = {gText_PleaseRemoveTheMail, MSG_VAR_NONE}, [MSG_IS_SELECTED2] = {gText_PkmnIsSelected, MSG_VAR_ITEM_NAME}, [MSG_GIVE_TO_MON] = {gText_GiveToAPkmn, MSG_VAR_NONE}, [MSG_PLACED_IN_BAG] = {gText_PlacedItemInBag, MSG_VAR_ITEM_NAME}, [MSG_BAG_FULL] = {gText_BagIsFull2, MSG_VAR_NONE}, [MSG_PUT_IN_BAG] = {gText_PutItemInBag, MSG_VAR_NONE}, [MSG_ITEM_IS_HELD] = {gText_ItemIsNowHeld, MSG_VAR_ITEM_NAME}, [MSG_CHANGED_TO_ITEM] = {gText_ChangedToNewItem, MSG_VAR_ITEM_NAME}, [MSG_CANT_STORE_MAIL] = {gText_MailCantBeStored, MSG_VAR_NONE}, }; static const struct WindowTemplate sYesNoWindowTemplate = { .bg = 0, .tilemapLeft = 24, .tilemapTop = 11, .width = 5, .height = 4, .paletteNum = 15, .baseBlock = 0x5C, }; static const struct OamData sOamData_DisplayMon = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x64), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(64x64), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0 }; static const struct OamData sOamData_Waveform = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(16x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(16x8), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0 }; static const union AnimCmd sAnim_Waveform_LeftOff[] = { ANIMCMD_FRAME(0, 5), ANIMCMD_END }; static const union AnimCmd sAnim_Waveform_LeftOn[] = { ANIMCMD_FRAME(2, 8), ANIMCMD_FRAME(4, 8), ANIMCMD_FRAME(6, 8), ANIMCMD_JUMP(0) }; static const union AnimCmd sAnim_Waveform_RightOff[] = { ANIMCMD_FRAME(8, 5), ANIMCMD_END }; static const union AnimCmd sAnim_Waveform_RightOn[] = { ANIMCMD_FRAME(10, 8), ANIMCMD_FRAME(4, 8), ANIMCMD_FRAME(12, 8), ANIMCMD_JUMP(0) }; static const union AnimCmd *const sAnims_Waveform[] = { sAnim_Waveform_LeftOff, sAnim_Waveform_LeftOn, sAnim_Waveform_RightOff, sAnim_Waveform_RightOn }; static const struct SpriteTemplate sSpriteTemplate_Waveform = { .tileTag = GFXTAG_WAVEFORM, .paletteTag = PALTAG_10, .oam = &sOamData_Waveform, .anims = sAnims_Waveform, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct OamData sOamData_MonIcon; static const struct SpriteTemplate sSpriteTemplate_MonIcon = { .tileTag = GFXTAG_MON_ICON, .paletteTag = PALTAG_MON_ICON_0, .oam = &sOamData_MonIcon, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct OamData sOamData_MonIcon = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(32x32), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x32), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0 }; static const union AffineAnimCmd sAffineAnim_ReleaseMon_Release[] = { AFFINEANIMCMD_FRAME(-2, -2, 0, 120), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_ReleaseMon_CameBack[] = { AFFINEANIMCMD_FRAME(16, 16, 0, 0), AFFINEANIMCMD_FRAME(16, 16, 0, 15), AFFINEANIMCMD_END }; static const union AffineAnimCmd *const sAffineAnims_ReleaseMon[] = { [RELEASE_ANIM_RELEASE] = sAffineAnim_ReleaseMon_Release, [RELEASE_ANIM_CAME_BACK] = sAffineAnim_ReleaseMon_CameBack }; #include "data/wallpapers.h" static const u16 sUnusedColor = RGB(26, 29, 8); static const struct SpriteSheet sSpriteSheet_Arrow = {sArrow_Gfx, 0x80, GFXTAG_ARROW}; static const struct OamData sOamData_BoxTitle = { .shape = SPRITE_SHAPE(32x16), .size = SPRITE_SIZE(32x16), .priority = 2 }; static const union AnimCmd sAnim_BoxTitle_Left[] = { ANIMCMD_FRAME(0, 5), ANIMCMD_END }; static const union AnimCmd sAnim_BoxTitle_Right[] = { ANIMCMD_FRAME(8, 5), ANIMCMD_END }; static const union AnimCmd *const sAnims_BoxTitle[] = { sAnim_BoxTitle_Left, sAnim_BoxTitle_Right }; static const struct SpriteTemplate sSpriteTemplate_BoxTitle = { .tileTag = GFXTAG_BOX_TITLE, .paletteTag = PALTAG_BOX_TITLE, .oam = &sOamData_BoxTitle, .anims = sAnims_BoxTitle, .images NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy }; static const struct OamData sOamData_Arrow = { .shape = SPRITE_SHAPE(8x16), .size = SPRITE_SIZE(8x16), .priority = 2 }; static const union AnimCmd sAnim_Arrow_Left[] = { ANIMCMD_FRAME(0, 5), ANIMCMD_END }; static const union AnimCmd sAnim_Arrow_Right[] = { ANIMCMD_FRAME(2, 5), ANIMCMD_END }; static const union AnimCmd *const sAnims_Arrow[] = { sAnim_Arrow_Left, sAnim_Arrow_Right }; static const struct SpriteTemplate sSpriteTemplate_Arrow = { .tileTag = GFXTAG_ARROW, .paletteTag = PALTAG_10, .oam = &sOamData_Arrow, .anims = sAnims_Arrow, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_Arrow }; static const u16 sHandCursor_Pal[] = INCBIN_U16("graphics/pokemon_storage/hand_cursor.gbapal"); static const u8 sHandCursor_Gfx[] = INCBIN_U8("graphics/pokemon_storage/hand_cursor.4bpp"); static const u8 sHandCursorShadow_Gfx[] = INCBIN_U8("graphics/pokemon_storage/hand_cursor_shadow.4bpp"); void DrawTextWindowAndBufferTiles(const u8 *string, void *dst, u8 zero1, u8 zero2, s32 bytesToBuffer) { s32 i, tileBytesToBuffer, remainingBytes; u16 windowId; u8 txtColor[3]; u8 *tileData1, *tileData2; struct WindowTemplate winTemplate = {0}; winTemplate.width = 24; winTemplate.height = 2; windowId = AddWindow(&winTemplate); FillWindowPixelBuffer(windowId, PIXEL_FILL(zero2)); tileData1 = (u8*) GetWindowAttribute(windowId, WINDOW_TILE_DATA); tileData2 = (winTemplate.width * 32) + tileData1; if (!zero1) txtColor[0] = TEXT_COLOR_TRANSPARENT; else txtColor[0] = zero2; txtColor[1] = TEXT_DYNAMIC_COLOR_6; txtColor[2] = TEXT_DYNAMIC_COLOR_5; AddTextPrinterParameterized4(windowId, 1, 0, 1, 0, 0, txtColor, -1, string); tileBytesToBuffer = bytesToBuffer; if (tileBytesToBuffer > 6u) tileBytesToBuffer = 6; remainingBytes = bytesToBuffer - 6; if (tileBytesToBuffer > 0) { for (i = tileBytesToBuffer; i != 0; i--) { CpuCopy16(tileData1, dst, 0x80); CpuCopy16(tileData2, dst + 0x80, 0x80); tileData1 += 0x80; tileData2 += 0x80; dst += 0x100; } } // Never used. bytesToBuffer is always passed <= 6, so remainingBytes is always <= 0 here if (remainingBytes > 0) CpuFill16((zero2 << 4) | zero2, dst, (u32)(remainingBytes) * 0x100); RemoveWindow(windowId); } // Unused static void UnusedDrawTextWindow(const u8 *string, void *dst, u16 offset, u8 bgColor, u8 fgColor, u8 shadowColor) { u32 tileSize; u8 windowId; u8 txtColor[3]; u8 *tileData1, *tileData2; struct WindowTemplate winTemplate = {0}; winTemplate.width = StringLength_Multibyte(string); winTemplate.height = 2; tileSize = winTemplate.width * 32; windowId = AddWindow(&winTemplate); FillWindowPixelBuffer(windowId, PIXEL_FILL(bgColor)); tileData1 = (u8*) GetWindowAttribute(windowId, WINDOW_TILE_DATA); tileData2 = (winTemplate.width * 32) + tileData1; txtColor[0] = bgColor; txtColor[1] = fgColor; txtColor[2] = shadowColor; AddTextPrinterParameterized4(windowId, 1, 0, 2, 0, 0, txtColor, -1, string); CpuCopy16(tileData1, dst, tileSize); CpuCopy16(tileData2, dst + offset, tileSize); RemoveWindow(windowId); } u8 CountMonsInBox(u8 boxId) { u16 i, count; for (i = 0, count = 0; i < IN_BOX_COUNT; i++) { if (GetBoxMonDataAt(boxId, i, MON_DATA_SPECIES) != SPECIES_NONE) count++; } return count; } s16 GetFirstFreeBoxSpot(u8 boxId) { u16 i; for (i = 0; i < IN_BOX_COUNT; i++) { if (GetBoxMonDataAt(boxId, i, MON_DATA_SPECIES) == SPECIES_NONE) return i; } return -1; // all spots are taken } u8 CountPartyNonEggMons(void) { u16 i, count; for (i = 0, count = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG)) { count++; } } return count; } u8 CountPartyAliveNonEggMonsExcept(u8 slotToIgnore) { u16 i, count; for (i = 0, count = 0; i < PARTY_SIZE; i++) { if (i != slotToIgnore && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG) && GetMonData(&gPlayerParty[i], MON_DATA_HP) != 0) { count++; } } return count; } u16 CountPartyAliveNonEggMons_IgnoreVar0x8004Slot(void) { return CountPartyAliveNonEggMonsExcept(gSpecialVar_0x8004); } u8 CountPartyMons(void) { u16 i, count; for (i = 0, count = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE) { count++; } } return count; } u8 *StringCopyAndFillWithSpaces(u8 *dst, const u8 *src, u16 n) { u8 *str; for (str = StringCopy(dst, src); str < dst + n; str++) *str = CHAR_SPACE; *str = EOS; return str; } static void sub_80C7128(u16 *dest, u16 dest_left, u16 dest_top, const u16 *src, u16 src_left, u16 src_top, u16 dest_width, u16 dest_height, u16 src_width) { u16 i; dest_width *= 2; dest += dest_top * 0x20 + dest_left; src += src_top * src_width + src_left; for (i = 0; i < dest_height; i++) { CpuCopy16(src, dest, dest_width); dest += 0x20; src += src_width; } } static void sub_80C71A4(u16 *dest, u16 dest_left, u16 dest_top, u16 width, u16 height) { u16 i; dest += dest_top * 0x20 + dest_left; width *= 2; for (i = 0; i < height; dest += 0x20, i++) Dma3FillLarge16_(0, dest, width); } enum { STATE_LOAD, STATE_FADE_IN, STATE_HANDLE_INPUT, STATE_ERROR_MSG, STATE_ENTER_PC, }; #define tState data[0] #define tSelectedOption data[1] #define tInput data[2] #define tNextOption data[3] #define tWindowId data[15] static void Task_PCMainMenu(u8 taskId) { struct Task *task = &gTasks[taskId]; switch (task->tState) { case STATE_LOAD: CreateMainMenu(task->tSelectedOption, &task->tWindowId); LoadMessageBoxAndBorderGfx(); DrawDialogueFrame(0, 0); FillWindowPixelBuffer(0, PIXEL_FILL(1)); AddTextPrinterParameterized2(0, 1, sMainMenuTexts[task->tSelectedOption].desc, TEXT_SPEED_FF, NULL, 2, 1, 3); CopyWindowToVram(0, 3); CopyWindowToVram(task->tWindowId, 3); task->tState++; break; case STATE_FADE_IN: if (IsWeatherNotFadingIn()) task->tState++; break; case STATE_HANDLE_INPUT: task->tInput = Menu_ProcessInput(); switch(task->tInput) { case MENU_NOTHING_CHOSEN: task->tNextOption = task->tSelectedOption; if (JOY_NEW(DPAD_UP) && --task->tNextOption < 0) task->tNextOption = OPTIONS_COUNT - 1; if (JOY_NEW(DPAD_DOWN) && ++task->tNextOption > OPTIONS_COUNT - 1) task->tNextOption = 0; if (task->tSelectedOption != task->tNextOption) { task->tSelectedOption = task->tNextOption; FillWindowPixelBuffer(0, PIXEL_FILL(1)); AddTextPrinterParameterized2(0, 1, sMainMenuTexts[task->tSelectedOption].desc, 0, NULL, 2, 1, 3); } break; case MENU_B_PRESSED: case OPTION_EXIT: ClearStdWindowAndFrame(task->tWindowId, TRUE); ScriptContext2_Disable(); EnableBothScriptContexts(); RemoveWindow(task->tWindowId); DestroyTask(taskId); break; default: if (task->tInput == OPTION_WITHDRAW && CountPartyMons() == PARTY_SIZE) { // Can't withdraw FillWindowPixelBuffer(0, PIXEL_FILL(1)); AddTextPrinterParameterized2(0, 1, gText_PartyFull, 0, NULL, 2, 1, 3); task->tState = STATE_ERROR_MSG; } else if (task->tInput == OPTION_DEPOSIT && CountPartyMons() == 1) { // Can't deposit FillWindowPixelBuffer(0, PIXEL_FILL(1)); AddTextPrinterParameterized2(0, 1, gText_JustOnePkmn, 0, NULL, 2, 1, 3); task->tState = STATE_ERROR_MSG; } else { // Enter PC FadeScreen(FADE_TO_BLACK, 0); task->tState = STATE_ENTER_PC; } break; } break; case STATE_ERROR_MSG: // Printed "can't do PC option message" // Wait for new input after message if (JOY_NEW(A_BUTTON | B_BUTTON)) { FillWindowPixelBuffer(0, PIXEL_FILL(1)); AddTextPrinterParameterized2(0, 1, sMainMenuTexts[task->tSelectedOption].desc, 0, NULL, 2, 1, 3); task->tState = STATE_HANDLE_INPUT; } else if (JOY_NEW(DPAD_UP)) { if (--task->tSelectedOption < 0) task->tSelectedOption = OPTIONS_COUNT - 1; Menu_MoveCursor(-1); task->tSelectedOption = Menu_GetCursorPos(); FillWindowPixelBuffer(0, PIXEL_FILL(1)); AddTextPrinterParameterized2(0, 1, sMainMenuTexts[task->tSelectedOption].desc, 0, NULL, 2, 1, 3); task->tState = STATE_HANDLE_INPUT; } else if (JOY_NEW(DPAD_DOWN)) { if (++task->tSelectedOption >= OPTIONS_COUNT - 1) task->tSelectedOption = 0; Menu_MoveCursor(1); task->tSelectedOption = Menu_GetCursorPos(); FillWindowPixelBuffer(0, PIXEL_FILL(1)); AddTextPrinterParameterized2(0, 1, sMainMenuTexts[task->tSelectedOption].desc, 0, NULL, 2, 1, 3); task->tState = STATE_HANDLE_INPUT; } break; case STATE_ENTER_PC: if (!gPaletteFade.active) { CleanupOverworldWindowsAndTilemaps(); Cb2_EnterPSS(task->tInput); RemoveWindow(task->tWindowId); DestroyTask(taskId); } break; } } void ShowPokemonStorageSystemPC(void) { u8 taskId = CreateTask(Task_PCMainMenu, 80); gTasks[taskId].tState = 0; gTasks[taskId].tSelectedOption = 0; ScriptContext2_Enable(); } static void FieldCb_ReturnToPcMenu(void) { u8 taskId; MainCallback vblankCb = gMain.vblankCallback; SetVBlankCallback(NULL); taskId = CreateTask(Task_PCMainMenu, 80); gTasks[taskId].tState = 0; gTasks[taskId].tSelectedOption = sPreviousBoxOption; Task_PCMainMenu(taskId); SetVBlankCallback(vblankCb); FadeInFromBlack(); } #undef tState #undef tSelectedOption #undef tInput #undef tNextOption #undef tWindowId static void CreateMainMenu(u8 whichMenu, s16 *windowIdPtr) { s16 windowId; struct WindowTemplate template = sWindowTemplate_MainMenu; template.width = GetMaxWidthInMenuTable((void *)sMainMenuTexts, OPTIONS_COUNT); windowId = AddWindow(&template); DrawStdWindowFrame(windowId, FALSE); PrintMenuTable(windowId, OPTIONS_COUNT, (void *)sMainMenuTexts); InitMenuInUpperLeftCornerPlaySoundWhenAPressed(windowId, OPTIONS_COUNT, whichMenu); *windowIdPtr = windowId; } static void Cb2_ExitPSS(void) { sPreviousBoxOption = GetCurrentBoxOption(); gFieldCallback = FieldCb_ReturnToPcMenu; SetMainCallback2(CB2_ReturnToField); } // Unused static s16 StorageSystemGetNextMonIndex(struct BoxPokemon *box, s8 startIdx, u8 stopIdx, u8 mode) { s16 i; s16 direction; if (mode == 0 || mode == 1) { direction = 1; } else { direction = -1; } if (mode == 1 || mode == 3) { for (i = startIdx + direction; i >= 0 && i <= stopIdx; i += direction) { if (GetBoxMonData(box + i, MON_DATA_SPECIES) != 0) return i; } } else { for (i = startIdx + direction; i >= 0 && i <= stopIdx; i += direction) { if (GetBoxMonData(box + i, MON_DATA_SPECIES) != 0 && !GetBoxMonData(box + i, MON_DATA_IS_EGG)) return i; } } return -1; } void ResetPokemonStorageSystem(void) { u16 boxId, boxPosition; SetCurrentBox(0); for (boxId = 0; boxId < TOTAL_BOXES_COUNT; boxId++) { for (boxPosition = 0; boxPosition < IN_BOX_COUNT; boxPosition++) ZeroBoxMonAt(boxId, boxPosition); } for (boxId = 0; boxId < TOTAL_BOXES_COUNT; boxId++) { u8 *dest = StringCopy(GetBoxNamePtr(boxId), gText_Box); ConvertIntToDecimalStringN(dest, boxId + 1, STR_CONV_MODE_LEFT_ALIGN, 2); } for (boxId = 0; boxId < TOTAL_BOXES_COUNT; boxId++) SetBoxWallpaper(boxId, boxId % (MAX_DEFAULT_WALLPAPER + 1)); ResetWaldaWallpaper(); } static void LoadChooseBoxMenuGfx(struct ChooseBoxMenu *menu, u16 tileTag, u16 palTag, u8 subpriority, bool32 loadPal) { struct SpritePalette palette = { sChooseBoxMenu_Pal, palTag }; struct SpriteSheet sheets[] = { {sChooseBoxMenuCenter_Gfx, 0x800, tileTag}, {sChooseBoxMenuSides_Gfx, 0x180, tileTag + 1}, {} }; if (loadPal) // Always false LoadSpritePalette(&palette); LoadSpriteSheets(sheets); sChooseBoxMenu = menu; menu->tileTag = tileTag; menu->paletteTag = palTag; menu->subpriority = subpriority; menu->loadedPalette = loadPal; } static void FreeChooseBoxMenu(void) { if (sChooseBoxMenu->loadedPalette) FreeSpritePaletteByTag(sChooseBoxMenu->paletteTag); FreeSpriteTilesByTag(sChooseBoxMenu->tileTag); FreeSpriteTilesByTag(sChooseBoxMenu->tileTag + 1); } static void CreateChooseBoxMenuSprites(u8 curBox) { ChooseBoxMenu_CreateSprites(curBox); } static void DestroyChooseBoxMenuSprites(void) { ChooseBoxMenu_DestroySprites(); } // For the popout window when choosing a box to deposit in or jump to static u8 HandleChooseBoxMenuInput(void) { if (JOY_NEW(B_BUTTON)) { PlaySE(SE_SELECT); return BOXID_CANCELED; } if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); return sChooseBoxMenu->curBox; } if (JOY_NEW(DPAD_LEFT)) { PlaySE(SE_SELECT); ChooseBoxMenu_MoveLeft(); } else if (JOY_NEW(DPAD_RIGHT)) { PlaySE(SE_SELECT); ChooseBoxMenu_MoveRight(); } return BOXID_NONE_CHOSEN; } static void ChooseBoxMenu_CreateSprites(u8 curBox) { u16 i; u8 spriteId; struct SpriteTemplate template; struct OamData oamData = {}; oamData.size = SPRITE_SIZE(64x64); oamData.paletteNum = 1; template = (struct SpriteTemplate){ 0, 0, &oamData, gDummySpriteAnimTable, NULL, gDummySpriteAffineAnimTable, SpriteCallbackDummy }; sChooseBoxMenu->curBox = curBox; template.tileTag = sChooseBoxMenu->tileTag; template.paletteTag = sChooseBoxMenu->paletteTag; spriteId = CreateSprite(&template, 160, 96, 0); sChooseBoxMenu->menuSprite = &gSprites[spriteId]; oamData.shape = SPRITE_SHAPE(8x32); oamData.size = SPRITE_SIZE(8x32); template.tileTag = sChooseBoxMenu->tileTag + 1; template.anims = sAnims_ChooseBoxMenu; for (i = 0; i < ARRAY_COUNT(sChooseBoxMenu->menuSideSprites); i++) { u16 anim; spriteId = CreateSprite(&template, 124, 80, sChooseBoxMenu->subpriority); sChooseBoxMenu->menuSideSprites[i] = &gSprites[spriteId]; anim = 0; if (i & 2) { sChooseBoxMenu->menuSideSprites[i]->pos1.x = 196; anim = 2; } if (i & 1) { sChooseBoxMenu->menuSideSprites[i]->pos1.y = 112; sChooseBoxMenu->menuSideSprites[i]->oam.size = 0; anim++; } StartSpriteAnim(sChooseBoxMenu->menuSideSprites[i], anim); } for (i = 0; i < ARRAY_COUNT(sChooseBoxMenu->arrowSprites); i++) { sChooseBoxMenu->arrowSprites[i] = CreateChooseBoxArrows(72 * i + 124, 88, i, 0, sChooseBoxMenu->subpriority); if (sChooseBoxMenu->arrowSprites[i]) { sChooseBoxMenu->arrowSprites[i]->data[0] = (i == 0 ? -1 : 1); sChooseBoxMenu->arrowSprites[i]->callback = SpriteCB_ChooseBoxArrow; } } ChooseBoxMenu_PrintInfo(); } static void ChooseBoxMenu_DestroySprites(void) { u16 i; if (sChooseBoxMenu->menuSprite) { DestroySprite(sChooseBoxMenu->menuSprite); sChooseBoxMenu->menuSprite = NULL; } for (i = 0; i < ARRAY_COUNT(sChooseBoxMenu->menuSideSprites); i++) { if (sChooseBoxMenu->menuSideSprites[i]) { DestroySprite(sChooseBoxMenu->menuSideSprites[i]); sChooseBoxMenu->menuSideSprites[i] = NULL; } } for (i = 0; i < ARRAY_COUNT(sChooseBoxMenu->arrowSprites); i++) { if (sChooseBoxMenu->arrowSprites[i]) DestroySprite(sChooseBoxMenu->arrowSprites[i]); } } static void ChooseBoxMenu_MoveRight(void) { if (++sChooseBoxMenu->curBox >= TOTAL_BOXES_COUNT) sChooseBoxMenu->curBox = 0; ChooseBoxMenu_PrintInfo(); } static void ChooseBoxMenu_MoveLeft(void) { sChooseBoxMenu->curBox = (sChooseBoxMenu->curBox == 0 ? TOTAL_BOXES_COUNT - 1 : sChooseBoxMenu->curBox - 1); ChooseBoxMenu_PrintInfo(); } static void ChooseBoxMenu_PrintInfo(void) { u8 numBoxMonsText[16]; struct WindowTemplate template; u8 windowId; u8 *boxName = GetBoxNamePtr(sChooseBoxMenu->curBox); u8 numInBox = CountMonsInBox(sChooseBoxMenu->curBox); u32 winTileData; s32 center; memset(&template, 0, sizeof(template)); template.width = 8; template.height = 4; windowId = AddWindow(&template); FillWindowPixelBuffer(windowId, PIXEL_FILL(4)); // Print box name center = GetStringCenterAlignXOffset(1, boxName, 64); AddTextPrinterParameterized3(windowId, 1, center, 1, sChooseBoxMenu_TextColors, TEXT_SPEED_FF, boxName); // Print #/30 for number of Pokémon in the box ConvertIntToDecimalStringN(numBoxMonsText, numInBox, STR_CONV_MODE_RIGHT_ALIGN, 2); StringAppend(numBoxMonsText, sText_OutOf30); center = GetStringCenterAlignXOffset(1, numBoxMonsText, 64); AddTextPrinterParameterized3(windowId, 1, center, 17, sChooseBoxMenu_TextColors, TEXT_SPEED_FF, numBoxMonsText); winTileData = GetWindowAttribute(windowId, WINDOW_TILE_DATA); CpuCopy32((void *)winTileData, (void *)OBJ_VRAM0 + 0x100 + (GetSpriteTileStartByTag(sChooseBoxMenu->tileTag) * 32), 0x400); RemoveWindow(windowId); } static void SpriteCB_ChooseBoxArrow(struct Sprite *sprite) { if (++sprite->data[1] > 3) { sprite->data[1] = 0; sprite->pos2.x += sprite->data[0]; if (++sprite->data[2] > 5) { sprite->data[2] = 0; sprite->pos2.x = 0; } } } static void VblankCb_PSS(void) { LoadOam(); ProcessSpriteCopyRequests(); sub_80D2AA4(); TransferPlttBuffer(); SetGpuReg(REG_OFFSET_BG2HOFS, sPSSData->bg2_X); } static void Cb2_PSS(void) { RunTasks(); DoScheduledBgTilemapCopiesToVram(); ScrollBackground(); UpdateCloseBoxButtonFlash(); AnimateSprites(); BuildOamBuffer(); } static void Cb2_EnterPSS(u8 boxOption) { ResetTasks(); sCurrentBoxOption = boxOption; sPSSData = Alloc(sizeof(*sPSSData)); if (sPSSData == NULL) { SetMainCallback2(Cb2_ExitPSS); } else { sPSSData->boxOption = boxOption; sPSSData->isReshowingPSS = FALSE; sMovingItemId = ITEM_NONE; sPSSData->state = 0; sPSSData->taskId = CreateTask(Cb_InitPSS, 3); sLastUsedBox = StorageGetCurrentBox(); SetMainCallback2(Cb2_PSS); } } static void Cb2_ReturnToPSS(void) { ResetTasks(); sPSSData = Alloc(sizeof(*sPSSData)); if (sPSSData == NULL) { SetMainCallback2(Cb2_ExitPSS); } else { sPSSData->boxOption = sCurrentBoxOption; sPSSData->isReshowingPSS = TRUE; sPSSData->state = 0; sPSSData->taskId = CreateTask(Cb_InitPSS, 3); SetMainCallback2(Cb2_PSS); } } static void ResetAllBgCoords(void) { SetGpuReg(REG_OFFSET_BG0HOFS, 0); SetGpuReg(REG_OFFSET_BG0VOFS, 0); SetGpuReg(REG_OFFSET_BG1HOFS, 0); SetGpuReg(REG_OFFSET_BG1VOFS, 0); SetGpuReg(REG_OFFSET_BG2HOFS, 0); SetGpuReg(REG_OFFSET_BG2VOFS, 0); SetGpuReg(REG_OFFSET_BG3HOFS, 0); SetGpuReg(REG_OFFSET_BG3VOFS, 0); } static void sub_80C7E98(void) { ResetPaletteFade(); ResetSpriteData(); FreeSpriteTileRanges(); FreeAllSpritePalettes(); ClearDma3Requests(); gReservedSpriteTileCount = 0x280; sub_80D2A90(&sPSSData->unk_0020, sPSSData->unk_0028, 8); gKeyRepeatStartDelay = 20; ClearScheduledBgCopiesToVram(); TilemapUtil_Init(TILEMAPID_COUNT); TilemapUtil_SetMap(TILEMAPID_PKMN_DATA, 1, sPkmnData_Tilemap, 8, 4); TilemapUtil_SetPos(TILEMAPID_PKMN_DATA, 1, 0); sPSSData->closeBoxFlashing = FALSE; } static void sub_80C7F1C(void) { ClearSavedCursorPos(); sInPartyMenu = (sPSSData->boxOption == OPTION_DEPOSIT); sDepositBoxId = 0; } static void SetMonIconTransparency(void) { if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT2_ALL); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(7, 11)); } SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_BG_ALL_ON | DISPCNT_OBJ_1D_MAP); } static void SetPSSCallback(TaskFunc newFunc) { gTasks[sPSSData->taskId].func = newFunc; sPSSData->state = 0; } static void Cb_InitPSS(u8 taskId) { switch (sPSSData->state) { case 0: SetVBlankCallback(NULL); SetGpuReg(REG_OFFSET_DISPCNT, 0); sub_80C7E98(); if (sPSSData->isReshowingPSS) { switch (sWhichToReshow) { case SCREEN_CHANGE_NAME_BOX - 1: // Return from naming box LoadSavedMovingMon(); break; case SCREEN_CHANGE_SUMMARY_SCREEN - 1: // Return from summary screen sub_80CE8E4(); break; case SCREEN_CHANGE_ITEM_FROM_BAG - 1: // Return from bag menu GiveChosenBagItem(); break; } } LoadPSSMenuGfx(); LoadWaveformSpritePalette(); break; case 1: if (!InitPSSWindows()) { SetPSSCallback(Cb_ChangeScreen); return; } break; case 2: PutWindowTilemap(0); ClearWindowTilemap(1); CpuFill32(0, (void *)VRAM, 0x200); LoadUserWindowBorderGfx(1, 0xB, 0xE0); break; case 3: ResetAllBgCoords(); if (!sPSSData->isReshowingPSS) sub_80C7F1C(); break; case 4: InitMonIconFields(); if (!sPSSData->isReshowingPSS) sub_80CD36C(); else sub_80CD3EC(); break; case 5: if (!MultiMove_Init()) { SetPSSCallback(Cb_ChangeScreen); return; } else { SetScrollingBackground(); sub_80CAC1C(); } break; case 6: sub_80CA0D8(); break; case 7: InitSupplementalTilemaps(); break; case 8: CreateInitBoxTask(StorageGetCurrentBox()); break; case 9: if (IsInitBoxActive()) return; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) { sPSSData->markMenu.baseTileTag = GFXTAG_MARKING_MENU; sPSSData->markMenu.basePaletteTag = PALTAG_MARKING_MENU; InitMonMarkingsMenu(&sPSSData->markMenu); BufferMonMarkingsMenuTiles(); } else { CreateItemIconSprites(); sub_80CAEAC(); } break; case 10: SetMonIconTransparency(); if (!sPSSData->isReshowingPSS) { BlendPalettes(PALETTES_ALL, 16, RGB_BLACK); SetPSSCallback(Cb_ShowPSS); } else { BlendPalettes(PALETTES_ALL, 16, RGB_BLACK); SetPSSCallback(Cb_ReshowPSS); } SetVBlankCallback(VblankCb_PSS); return; default: return; } sPSSData->state++; } static void Cb_ShowPSS(u8 taskId) { switch (sPSSData->state) { case 0: PlaySE(SE_PC_LOGIN); ComputerScreenOpenEffect(20, 0, 1); sPSSData->state++; break; case 1: if (!IsComputerScreenOpenEffectActive()) SetPSSCallback(Cb_MainPSS); break; } } static void Cb_ReshowPSS(u8 taskId) { switch (sPSSData->state) { case 0: BeginNormalPaletteFade(PALETTES_ALL, -1, 0x10, 0, RGB_BLACK); sPSSData->state++; break; case 1: if (!UpdatePaletteFade()) { if (sWhichToReshow == SCREEN_CHANGE_ITEM_FROM_BAG - 1 && gSpecialVar_ItemId != ITEM_NONE) { PrintMessage(MSG_ITEM_IS_HELD); sPSSData->state++; } else { SetPSSCallback(Cb_MainPSS); } } break; case 2: if (!IsDma3ManagerBusyWithBgCopy() && JOY_NEW(A_BUTTON | B_BUTTON)) { ClearBottomWindow(); sPSSData->state++; } break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; } } // States for the outer switch in Cb_MainPSS enum { MSTATE_HANDLE_INPUT, MSTATE_MOVE_CURSOR, MSTATE_SCROLL_BOX, MSTATE_WAIT_MSG, MSTATE_ERROR_LAST_PARTY_MON, MSTATE_ERROR_HAS_MAIL, MSTATE_WAIT_ERROR_MSG, MSTATE_MULTIMOVE_RUN, MSTATE_MULTIMOVE_RUN_CANCEL, MSTATE_MULTIMOVE_RUN_MOVED, MSTATE_SCROLL_BOX_ITEM, MSTATE_WAIT_ITEM_ANIM, }; static void Cb_MainPSS(u8 taskId) { switch (sPSSData->state) { case MSTATE_HANDLE_INPUT: switch (HandleInput()) { case INPUT_MOVE_CURSOR: PlaySE(SE_SELECT); sPSSData->state = MSTATE_MOVE_CURSOR; break; case INPUT_SHOW_PARTY: if (sPSSData->boxOption != OPTION_MOVE_MONS && sPSSData->boxOption != OPTION_MOVE_ITEMS) { PrintMessage(MSG_WHICH_ONE_WILL_TAKE); sPSSData->state = MSTATE_WAIT_MSG; } else { ClearSavedCursorPos(); SetPSSCallback(Cb_ShowPartyPokemon); } break; case INPUT_HIDE_PARTY: if (sPSSData->boxOption == OPTION_MOVE_MONS) { if (IsMonBeingMoved() && ItemIsMail(sPSSData->displayMonItemId)) sPSSData->state = MSTATE_ERROR_HAS_MAIL; else SetPSSCallback(Cb_HidePartyPokemon); } else if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { SetPSSCallback(Cb_HidePartyPokemon); } break; case INPUT_CLOSE_BOX: SetPSSCallback(Cb_OnCloseBoxPressed); break; case INPUT_PRESSED_B: SetPSSCallback(Cb_OnBPressed); break; case INPUT_BOX_OPTIONS: PlaySE(SE_SELECT); SetPSSCallback(Cb_HandleBoxOptions); break; case INPUT_IN_MENU: SetPSSCallback(Cb_OnSelectedMon); break; case INPUT_SCROLL_RIGHT: PlaySE(SE_SELECT); sPSSData->newCurrBoxId = StorageGetCurrentBox() + 1; if (sPSSData->newCurrBoxId >= TOTAL_BOXES_COUNT) sPSSData->newCurrBoxId = 0; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) { SetUpScrollToBox(sPSSData->newCurrBoxId); sPSSData->state = MSTATE_SCROLL_BOX; } else { TryHideItemAtCursor(); sPSSData->state = MSTATE_SCROLL_BOX_ITEM; } break; case INPUT_SCROLL_LEFT: PlaySE(SE_SELECT); sPSSData->newCurrBoxId = StorageGetCurrentBox() - 1; if (sPSSData->newCurrBoxId < 0) sPSSData->newCurrBoxId = TOTAL_BOXES_COUNT - 1; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) { SetUpScrollToBox(sPSSData->newCurrBoxId); sPSSData->state = MSTATE_SCROLL_BOX; } else { TryHideItemAtCursor(); sPSSData->state = MSTATE_SCROLL_BOX_ITEM; } break; case INPUT_DEPOSIT: if (!IsRemovingLastPartyMon()) { if (ItemIsMail(sPSSData->displayMonItemId)) { sPSSData->state = MSTATE_ERROR_HAS_MAIL; } else { PlaySE(SE_SELECT); SetPSSCallback(Cb_DepositMenu); } } else { sPSSData->state = MSTATE_ERROR_LAST_PARTY_MON; } break; case INPUT_MOVE_MON: if (IsRemovingLastPartyMon()) { sPSSData->state = MSTATE_ERROR_LAST_PARTY_MON; } else { PlaySE(SE_SELECT); SetPSSCallback(Cb_MoveMon); } break; case INPUT_SHIFT_MON: if (!CanShiftMon()) { sPSSData->state = MSTATE_ERROR_LAST_PARTY_MON; } else { PlaySE(SE_SELECT); SetPSSCallback(Cb_ShiftMon); } break; case INPUT_WITHDRAW: PlaySE(SE_SELECT); SetPSSCallback(Cb_WithdrawMon); break; case INPUT_PLACE_MON: PlaySE(SE_SELECT); SetPSSCallback(Cb_PlaceMon); break; case INPUT_TAKE_ITEM: PlaySE(SE_SELECT); SetPSSCallback(Cb_TakeItemForMoving); break; case INPUT_GIVE_ITEM: PlaySE(SE_SELECT); SetPSSCallback(Cb_GiveMovingItemToMon); break; case INPUT_SWITCH_ITEMS: PlaySE(SE_SELECT); SetPSSCallback(Cb_SwitchSelectedItem); break; case INPUT_MULTIMOVE_START: PlaySE(SE_SELECT); MultiMove_SetFunction(MULTIMOVE_START); sPSSData->state = MSTATE_MULTIMOVE_RUN; break; case INPUT_MULTIMOVE_SINGLE: MultiMove_SetFunction(MULTIMOVE_CANCEL); sPSSData->state = MSTATE_MULTIMOVE_RUN_CANCEL; break; case INPUT_MULTIMOVE_CHANGE_SELECTION: PlaySE(SE_SELECT); MultiMove_SetFunction(MULTIMOVE_CHANGE_SELECTION); sPSSData->state = MSTATE_MULTIMOVE_RUN_MOVED; break; case INPUT_MULTIMOVE_GRAB_SELECTION: MultiMove_SetFunction(MULTIMOVE_GRAB_SELECTION); sPSSData->state = MSTATE_MULTIMOVE_RUN; break; case INPUT_MULTIMOVE_MOVE_MONS: PlaySE(SE_SELECT); MultiMove_SetFunction(MULTIMOVE_MOVE_MONS); sPSSData->state = MSTATE_MULTIMOVE_RUN_MOVED; break; case INPUT_MULTIMOVE_PLACE_MONS: PlaySE(SE_SELECT); MultiMove_SetFunction(MULTIMOVE_PLACE_MONS); sPSSData->state = MSTATE_MULTIMOVE_RUN; break; case INPUT_MULTIMOVE_UNABLE: // When selecting/moving multiple Pokémon the // cursor may not wrap around the edges. PlaySE(SE_FAILURE); break; } break; case MSTATE_MOVE_CURSOR: if (!UpdateCursorPos()) { if (IsCursorOnCloseBox()) StartFlashingCloseBoxButton(); else StopFlashingCloseBoxButton(); if (sPSSData->setMosaic) StartDisplayMonMosaicEffect(); sPSSData->state = MSTATE_HANDLE_INPUT; } break; case MSTATE_SCROLL_BOX: if (!ScrollToBox()) { SetCurrentBox(sPSSData->newCurrBoxId); if (!sInPartyMenu && !IsMonBeingMoved()) { RefreshDisplayMon(); StartDisplayMonMosaicEffect(); } if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { TryShowItemAtCursor(); sPSSData->state = MSTATE_WAIT_ITEM_ANIM; } else { sPSSData->state = MSTATE_HANDLE_INPUT; } } break; case MSTATE_WAIT_MSG: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); sPSSData->state = MSTATE_HANDLE_INPUT; } break; case MSTATE_ERROR_LAST_PARTY_MON: PlaySE(SE_FAILURE); PrintMessage(MSG_LAST_POKE); sPSSData->state = MSTATE_WAIT_ERROR_MSG; break; case MSTATE_ERROR_HAS_MAIL: PlaySE(SE_FAILURE); PrintMessage(MSG_PLEASE_REMOVE_MAIL); sPSSData->state = MSTATE_WAIT_ERROR_MSG; break; case MSTATE_WAIT_ERROR_MSG: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); } break; case MSTATE_MULTIMOVE_RUN: if (!MultiMove_RunFunction()) sPSSData->state = MSTATE_HANDLE_INPUT; break; case MSTATE_MULTIMOVE_RUN_CANCEL: // Began a multiple Pokémon selection but // ended up selecting a single Pokémon. // Wait for multi move to cancel, then // do a normal move. if (!MultiMove_RunFunction()) SetPSSCallback(Cb_MoveMon); break; case MSTATE_MULTIMOVE_RUN_MOVED: if (!MultiMove_RunFunction()) { if (sPSSData->setMosaic) StartDisplayMonMosaicEffect(); sPSSData->state = MSTATE_HANDLE_INPUT; } break; case MSTATE_SCROLL_BOX_ITEM: if (!IsItemIconAnimActive()) { SetUpScrollToBox(sPSSData->newCurrBoxId); sPSSData->state = MSTATE_SCROLL_BOX; } break; case MSTATE_WAIT_ITEM_ANIM: if (!IsItemIconAnimActive()) sPSSData->state = MSTATE_HANDLE_INPUT; break; } } static void Cb_ShowPartyPokemon(u8 taskId) { switch (sPSSData->state) { case 0: SetUpDoShowPartyMenu(); sPSSData->state++; break; case 1: if (!DoShowPartyMenu()) SetPSSCallback(Cb_MainPSS); break; } } static void Cb_HidePartyPokemon(u8 taskId) { switch (sPSSData->state) { case 0: PlaySE(SE_SELECT); SetUpHidePartyMenu(); sPSSData->state++; break; case 1: if (!HidePartyMenu()) { SetCursorBoxPosition(GetSavedCursorPos()); sPSSData->state++; } break; case 2: if (!UpdateCursorPos()) { if (sPSSData->setMosaic) StartDisplayMonMosaicEffect(); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_OnSelectedMon(u8 taskId) { switch (sPSSData->state) { case 0: if (!IsDisplayMosaicActive()) { PlaySE(SE_SELECT); if (sPSSData->boxOption != OPTION_MOVE_ITEMS) PrintMessage(MSG_IS_SELECTED); else if (IsMovingItem() || sPSSData->displayMonItemId != ITEM_NONE) PrintMessage(MSG_IS_SELECTED2); else PrintMessage(MSG_GIVE_TO_MON); AddMenu(); sPSSData->state = 1; } break; case 1: if (!IsMenuLoading()) sPSSData->state = 2; break; case 2: switch (HandleMenuInput()) { case MENU_B_PRESSED: case MENU_CANCEL: ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); break; case MENU_MOVE: if (IsRemovingLastPartyMon()) { sPSSData->state = 3; } else { PlaySE(SE_SELECT); ClearBottomWindow(); SetPSSCallback(Cb_MoveMon); } break; case MENU_PLACE: PlaySE(SE_SELECT); ClearBottomWindow(); SetPSSCallback(Cb_PlaceMon); break; case MENU_SHIFT: if (!CanShiftMon()) { sPSSData->state = 3; } else { PlaySE(SE_SELECT); ClearBottomWindow(); SetPSSCallback(Cb_ShiftMon); } break; case MENU_WITHDRAW: PlaySE(SE_SELECT); ClearBottomWindow(); SetPSSCallback(Cb_WithdrawMon); break; case MENU_STORE: if (IsRemovingLastPartyMon()) { sPSSData->state = 3; } else if (ItemIsMail(sPSSData->displayMonItemId)) { sPSSData->state = 4; } else { PlaySE(SE_SELECT); ClearBottomWindow(); SetPSSCallback(Cb_DepositMenu); } break; case MENU_RELEASE: if (IsRemovingLastPartyMon()) { sPSSData->state = 3; } else if (sPSSData->displayMonIsEgg) { sPSSData->state = 5; // Cannot release an Egg. } else if (ItemIsMail(sPSSData->displayMonItemId)) { sPSSData->state = 4; } else { PlaySE(SE_SELECT); SetPSSCallback(Cb_ReleaseMon); } break; case MENU_SUMMARY: PlaySE(SE_SELECT); SetPSSCallback(Cb_ShowMonSummary); break; case MENU_MARK: PlaySE(SE_SELECT); SetPSSCallback(Cb_ShowMarkMenu); break; case MENU_TAKE: PlaySE(SE_SELECT); SetPSSCallback(Cb_TakeItemForMoving); break; case MENU_GIVE: PlaySE(SE_SELECT); SetPSSCallback(Cb_GiveMovingItemToMon); break; case MENU_BAG: SetPSSCallback(Cb_ItemToBag); break; case MENU_SWITCH: PlaySE(SE_SELECT); SetPSSCallback(Cb_SwitchSelectedItem); break; case MENU_GIVE_2: PlaySE(SE_SELECT); SetPSSCallback(Cb_GiveItemFromBag); break; case MENU_INFO: SetPSSCallback(Cb_ShowItemInfo); break; } break; case 3: PlaySE(SE_FAILURE); PrintMessage(MSG_LAST_POKE); sPSSData->state = 6; break; case 5: PlaySE(SE_FAILURE); PrintMessage(MSG_CANT_RELEASE_EGG); sPSSData->state = 6; break; case 4: PlaySE(SE_FAILURE); PrintMessage(MSG_PLEASE_REMOVE_MAIL); sPSSData->state = 6; break; case 6: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_MoveMon(u8 taskId) { switch (sPSSData->state) { case 0: InitMonPlaceChange(CHANGE_GRAB); sPSSData->state++; break; case 1: if (!DoMonPlaceChange()) { if (sInPartyMenu) SetPSSCallback(Cb_HandleMovingMonFromParty); else SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_PlaceMon(u8 taskId) { switch (sPSSData->state) { case 0: InitMonPlaceChange(CHANGE_PLACE); sPSSData->state++; break; case 1: if (!DoMonPlaceChange()) { if (sInPartyMenu) SetPSSCallback(Cb_HandleMovingMonFromParty); else SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_ShiftMon(u8 taskId) { switch (sPSSData->state) { case 0: InitMonPlaceChange(CHANGE_SHIFT); sPSSData->state++; break; case 1: if (!DoMonPlaceChange()) { StartDisplayMonMosaicEffect(); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_WithdrawMon(u8 taskId) { switch (sPSSData->state) { case 0: if (CalculatePlayerPartyCount() == PARTY_SIZE) { PrintMessage(MSG_PARTY_FULL); sPSSData->state = 1; } else { SaveCursorPos(); InitMonPlaceChange(CHANGE_GRAB); sPSSData->state = 2; } break; case 1: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); } break; case 2: if (!DoMonPlaceChange()) { SetMovingMonPriority(1); SetUpDoShowPartyMenu(); sPSSData->state++; } break; case 3: if (!DoShowPartyMenu()) { InitMonPlaceChange(CHANGE_PLACE); sPSSData->state++; } break; case 4: if (!DoMonPlaceChange()) { UpdatePartySlotColors(); sPSSData->state++; } break; case 5: SetPSSCallback(Cb_HidePartyPokemon); break; } } static void Cb_DepositMenu(u8 taskId) { u8 boxId; switch (sPSSData->state) { case 0: PrintMessage(MSG_DEPOSIT_IN_WHICH_BOX); LoadChooseBoxMenuGfx(&sPSSData->chooseBoxMenu, GFXTAG_CHOOSE_BOX_MENU, PALTAG_7, 3, FALSE); CreateChooseBoxMenuSprites(sDepositBoxId); sPSSData->state++; break; case 1: boxId = HandleChooseBoxMenuInput(); switch (boxId) { case BOXID_NONE_CHOSEN: break; case BOXID_CANCELED: ClearBottomWindow(); DestroyChooseBoxMenuSprites(); FreeChooseBoxMenu(); SetPSSCallback(Cb_MainPSS); break; default: if (TryStorePartyMonInBox(boxId)) { sDepositBoxId = boxId; ClearBottomWindow(); DestroyChooseBoxMenuSprites(); FreeChooseBoxMenu(); sPSSData->state = 2; } else { PrintMessage(MSG_BOX_IS_FULL); sPSSData->state = 4; } break; } break; case 2: CompactPartySlots(); CompactPartySprites(); sPSSData->state++; break; case 3: if (GetNumPartySpritesCompacting() == 0) { sub_80CE22C(); StartDisplayMonMosaicEffect(); UpdatePartySlotColors(); SetPSSCallback(Cb_MainPSS); } break; case 4: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { PrintMessage(MSG_DEPOSIT_IN_WHICH_BOX); sPSSData->state = 1; } break; } } static void Cb_ReleaseMon(u8 taskId) { switch (sPSSData->state) { case 0: PrintMessage(MSG_RELEASE_POKE); ShowYesNoWindow(1); sPSSData->state++; // fallthrough case 1: switch (Menu_ProcessInputNoWrapClearOnChoose()) { case MENU_B_PRESSED: case 1: // No ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); break; case 0: // Yes ClearBottomWindow(); InitCanReleaseMonVars(); InitReleaseMon(); sPSSData->state++; break; } break; case 2: RunCanReleaseMon(); if (!TryHideReleaseMon()) { while (1) { s8 canRelease = RunCanReleaseMon(); if (canRelease == TRUE) { sPSSData->state++; break; } else if (!canRelease) { sPSSData->state = 8; break; } } } break; case 3: ReleaseMon(); RefreshDisplayMonData(); PrintMessage(MSG_WAS_RELEASED); sPSSData->state++; break; case 4: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { PrintMessage(MSG_BYE_BYE); sPSSData->state++; } break; case 5: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); if (sInPartyMenu) { CompactPartySlots(); CompactPartySprites(); sPSSData->state++; } else { sPSSData->state = 7; } } break; case 6: if (GetNumPartySpritesCompacting() == 0) { RefreshDisplayMon(); StartDisplayMonMosaicEffect(); UpdatePartySlotColors(); sPSSData->state++; } break; case 7: SetPSSCallback(Cb_MainPSS); break; case 8: // Start "can't release" sequence PrintMessage(MSG_WAS_RELEASED); sPSSData->state++; break; case 9: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { PrintMessage(MSG_SURPRISE); sPSSData->state++; } break; case 10: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); ReshowReleaseMon(); sPSSData->state++; } break; case 11: if (!ResetReleaseMonSpritePtr()) { TrySetCursorFistAnim(); PrintMessage(MSG_CAME_BACK); sPSSData->state++; } break; case 12: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { PrintMessage(MSG_WORRIED); sPSSData->state++; } break; case 13: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_ShowMarkMenu(u8 taskId) { switch (sPSSData->state) { case 0: PrintMessage(MSG_MARK_POKE); sPSSData->markMenu.markings = sPSSData->displayMonMarkings; OpenMonMarkingsMenu(sPSSData->displayMonMarkings, 0xb0, 0x10); sPSSData->state++; break; case 1: if (!HandleMonMarkingsMenuInput()) { FreeMonMarkingsMenu(); ClearBottomWindow(); SetMonMarkings(sPSSData->markMenu.markings); RefreshDisplayMonData(); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_TakeItemForMoving(u8 taskId) { switch (sPSSData->state) { case 0: if (!ItemIsMail(sPSSData->displayMonItemId)) { ClearBottomWindow(); sPSSData->state++; } else { SetPSSCallback(Cb_PrintCantStoreMail); } break; case 1: StartCursorAnim(CURSOR_ANIM_OPEN); TakeItemFromMon(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition()); sPSSData->state++; break; case 2: if (!IsItemIconAnimActive()) { StartCursorAnim(CURSOR_ANIM_FIST); ClearBottomWindow(); RefreshDisplayMon(); PrintDisplayMonInfo(); sPSSData->state++; } break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; } } static void Cb_GiveMovingItemToMon(u8 taskId) { switch (sPSSData->state) { case 0: ClearBottomWindow(); sPSSData->state++; break; case 1: StartCursorAnim(CURSOR_ANIM_OPEN); GiveItemToMon(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition()); sPSSData->state++; break; case 2: if (!IsItemIconAnimActive()) { StartCursorAnim(CURSOR_ANIM_BOUNCE); RefreshDisplayMon(); PrintDisplayMonInfo(); PrintMessage(MSG_ITEM_IS_HELD); sPSSData->state++; } break; case 3: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); sPSSData->state++; } break; case 4: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; } } static void Cb_ItemToBag(u8 taskId) { switch (sPSSData->state) { case 0: if (!AddBagItem(sPSSData->displayMonItemId, 1)) { PlaySE(SE_FAILURE); PrintMessage(MSG_BAG_FULL); sPSSData->state = 3; } else { PlaySE(SE_SELECT); MoveItemFromMonToBag(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition()); sPSSData->state = 1; } break; case 1: if (!IsItemIconAnimActive()) { PrintMessage(MSG_PLACED_IN_BAG); sPSSData->state = 2; } break; case 2: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); RefreshDisplayMon(); PrintDisplayMonInfo(); sPSSData->state = 4; } break; case 4: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; case 3: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_SwitchSelectedItem(u8 taskId) { switch (sPSSData->state) { case 0: if (!ItemIsMail(sPSSData->displayMonItemId)) { ClearBottomWindow(); sPSSData->state++; } else { SetPSSCallback(Cb_PrintCantStoreMail); } break; case 1: StartCursorAnim(CURSOR_ANIM_OPEN); SwapItemsWithMon(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition()); sPSSData->state++; break; case 2: if (!IsItemIconAnimActive()) { StartCursorAnim(CURSOR_ANIM_FIST); RefreshDisplayMon(); PrintDisplayMonInfo(); PrintMessage(MSG_CHANGED_TO_ITEM); sPSSData->state++; } break; case 3: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); sPSSData->state++; } break; case 4: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; } } static void Cb_ShowItemInfo(u8 taskId) { switch (sPSSData->state) { case 0: ClearBottomWindow(); sPSSData->state++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { PlaySE(SE_WIN_OPEN); PrintItemDescription(); InitItemInfoWindow(); sPSSData->state++; } break; case 2: if (!UpdateItemInfoWindowSlideIn()) sPSSData->state++; break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) sPSSData->state++; break; case 4: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { PlaySE(SE_WIN_OPEN); sPSSData->state++; } break; case 5: if (!UpdateItemInfoWindowSlideOut()) sPSSData->state++; break; case 6: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; } } static void Cb_CloseBoxWhileHoldingItem(u8 taskId) { switch (sPSSData->state) { case 0: PlaySE(SE_SELECT); PrintMessage(MSG_PUT_IN_BAG); ShowYesNoWindow(0); sPSSData->state = 1; break; case 1: switch (Menu_ProcessInputNoWrapClearOnChoose()) { case MENU_B_PRESSED: case 1: // No ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); break; case 0:// Yes if (AddBagItem(sPSSData->movingItemId, 1) == TRUE) { ClearBottomWindow(); sPSSData->state = 3; } else { PrintMessage(MSG_BAG_FULL); sPSSData->state = 2; } break; } break; case 2: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); sPSSData->state = 5; } break; case 3: MoveItemFromCursorToBag(); sPSSData->state = 4; break; case 4: if (!IsItemIconAnimActive()) { StartCursorAnim(CURSOR_ANIM_BOUNCE); SetPSSCallback(Cb_MainPSS); } break; case 5: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; } } static void Cb_HandleMovingMonFromParty(u8 taskId) { switch (sPSSData->state) { case 0: CompactPartySlots(); CompactPartySprites(); sPSSData->state++; break; case 1: if (GetNumPartySpritesCompacting() == 0) { UpdatePartySlotColors(); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_PrintCantStoreMail(u8 taskId) { switch (sPSSData->state) { case 0: PrintMessage(MSG_CANT_STORE_MAIL); sPSSData->state++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) sPSSData->state++; break; case 2: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); sPSSData->state++; } break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) SetPSSCallback(Cb_MainPSS); break; } } // Handle options menu that shows when the box title bar is selected static void Cb_HandleBoxOptions(u8 taskId) { switch (sPSSData->state) { case 0: PrintMessage(MSG_WHAT_YOU_DO); AddMenu(); sPSSData->state++; break; case 1: if (IsMenuLoading()) return; sPSSData->state++; case 2: switch (HandleMenuInput()) { case MENU_B_PRESSED: case MENU_CANCEL: AnimateBoxScrollArrows(TRUE); ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); break; case MENU_NAME: PlaySE(SE_SELECT); SetPSSCallback(Cb_NameBox); break; case MENU_WALLPAPER: PlaySE(SE_SELECT); ClearBottomWindow(); SetPSSCallback(Cb_HandleWallpapers); break; case MENU_JUMP: PlaySE(SE_SELECT); ClearBottomWindow(); SetPSSCallback(Cb_JumpBox); break; } break; } } static void Cb_HandleWallpapers(u8 taskId) { switch (sPSSData->state) { case 0: AddWallpaperSetsMenu(); PrintMessage(MSG_PICK_A_THEME); sPSSData->state++; break; case 1: if (!IsMenuLoading()) sPSSData->state++; break; case 2: sPSSData->wallpaperSetId = HandleMenuInput(); switch (sPSSData->wallpaperSetId) { case MENU_B_PRESSED: AnimateBoxScrollArrows(TRUE); ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); break; case MENU_SCENERY_1: case MENU_SCENERY_2: case MENU_SCENERY_3: case MENU_ETCETERA: PlaySE(SE_SELECT); RemoveMenu(); sPSSData->wallpaperSetId -= MENU_WALLPAPER_SETS_START; sPSSData->state++; break; case MENU_FRIENDS: // New wallpaper from Walda. PlaySE(SE_SELECT); sPSSData->wallpaperId = WALLPAPER_FRIENDS; RemoveMenu(); ClearBottomWindow(); sPSSData->state = 6; break; } break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) { AddWallpapersMenu(sPSSData->wallpaperSetId); PrintMessage(MSG_PICK_A_WALLPAPER); sPSSData->state++; } break; case 4: sPSSData->wallpaperId = HandleMenuInput(); switch (sPSSData->wallpaperId) { case MENU_NOTHING_CHOSEN: break; case MENU_B_PRESSED: ClearBottomWindow(); sPSSData->state = 0; break; default: PlaySE(SE_SELECT); ClearBottomWindow(); sPSSData->wallpaperId -= MENU_WALLPAPERS_START; SetWallpaperForCurrentBox(sPSSData->wallpaperId); sPSSData->state++; break; } break; case 5: if (!DoWallpaperGfxChange()) { AnimateBoxScrollArrows(TRUE); SetPSSCallback(Cb_MainPSS); } break; case 6: if (!IsDma3ManagerBusyWithBgCopy()) { SetWallpaperForCurrentBox(sPSSData->wallpaperId); sPSSData->state = 5; } break; } } static void Cb_JumpBox(u8 taskId) { switch (sPSSData->state) { case 0: PrintMessage(MSG_JUMP_TO_WHICH_BOX); LoadChooseBoxMenuGfx(&sPSSData->chooseBoxMenu, GFXTAG_CHOOSE_BOX_MENU, PALTAG_7, 3, FALSE); CreateChooseBoxMenuSprites(StorageGetCurrentBox()); sPSSData->state++; break; case 1: sPSSData->newCurrBoxId = HandleChooseBoxMenuInput(); switch (sPSSData->newCurrBoxId) { case BOXID_NONE_CHOSEN: break; default: ClearBottomWindow(); DestroyChooseBoxMenuSprites(); FreeChooseBoxMenu(); if (sPSSData->newCurrBoxId == BOXID_CANCELED || sPSSData->newCurrBoxId == StorageGetCurrentBox()) { AnimateBoxScrollArrows(TRUE); SetPSSCallback(Cb_MainPSS); } else { sPSSData->state++; } break; } break; case 2: SetUpScrollToBox(sPSSData->newCurrBoxId); sPSSData->state++; break; case 3: if (!ScrollToBox()) { SetCurrentBox(sPSSData->newCurrBoxId); SetPSSCallback(Cb_MainPSS); } break; } } static void Cb_NameBox(u8 taskId) { switch (sPSSData->state) { case 0: SaveMovingMon(); BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); sPSSData->state++; break; case 1: if (!UpdatePaletteFade()) { sWhichToReshow = SCREEN_CHANGE_NAME_BOX - 1; sPSSData->screenChangeType = SCREEN_CHANGE_NAME_BOX; SetPSSCallback(Cb_ChangeScreen); } break; } } static void Cb_ShowMonSummary(u8 taskId) { switch (sPSSData->state) { case 0: InitSummaryScreenData(); BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); sPSSData->state++; break; case 1: if (!UpdatePaletteFade()) { sWhichToReshow = SCREEN_CHANGE_SUMMARY_SCREEN - 1; sPSSData->screenChangeType = SCREEN_CHANGE_SUMMARY_SCREEN; SetPSSCallback(Cb_ChangeScreen); } break; } } static void Cb_GiveItemFromBag(u8 taskId) { switch (sPSSData->state) { case 0: BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); sPSSData->state++; break; case 1: if (!UpdatePaletteFade()) { sWhichToReshow = SCREEN_CHANGE_ITEM_FROM_BAG - 1; sPSSData->screenChangeType = SCREEN_CHANGE_ITEM_FROM_BAG; SetPSSCallback(Cb_ChangeScreen); } break; } } static void Cb_OnCloseBoxPressed(u8 taskId) { switch (sPSSData->state) { case 0: if (IsMonBeingMoved()) { PlaySE(SE_FAILURE); PrintMessage(MSG_HOLDING_POKE); sPSSData->state = 1; } else if (IsMovingItem()) { SetPSSCallback(Cb_CloseBoxWhileHoldingItem); } else { PlaySE(SE_SELECT); PrintMessage(MSG_EXIT_BOX); ShowYesNoWindow(0); sPSSData->state = 2; } break; case 1: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); } break; case 2: switch (Menu_ProcessInputNoWrapClearOnChoose()) { case MENU_B_PRESSED: case 1: ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); break; case 0: PlaySE(SE_PC_OFF); ClearBottomWindow(); sPSSData->state++; break; } break; case 3: ComputerScreenCloseEffect(20, 0, 1); sPSSData->state++; break; case 4: if (!IsComputerScreenCloseEffectActive()) { sub_80CABE0(); gPlayerPartyCount = CalculatePlayerPartyCount(); sPSSData->screenChangeType = SCREEN_CHANGE_EXIT_BOX; SetPSSCallback(Cb_ChangeScreen); } break; } } static void Cb_OnBPressed(u8 taskId) { switch (sPSSData->state) { case 0: if (IsMonBeingMoved()) { PlaySE(SE_FAILURE); PrintMessage(MSG_HOLDING_POKE); sPSSData->state = 1; } else if (IsMovingItem()) { SetPSSCallback(Cb_CloseBoxWhileHoldingItem); } else { PlaySE(SE_SELECT); PrintMessage(MSG_CONTINUE_BOX); ShowYesNoWindow(0); sPSSData->state = 2; } break; case 1: if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY)) { ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); } break; case 2: switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: ClearBottomWindow(); SetPSSCallback(Cb_MainPSS); break; case 1: case MENU_B_PRESSED: PlaySE(SE_PC_OFF); ClearBottomWindow(); sPSSData->state++; break; } break; case 3: ComputerScreenCloseEffect(20, 0, 0); sPSSData->state++; break; case 4: if (!IsComputerScreenCloseEffectActive()) { sub_80CABE0(); gPlayerPartyCount = CalculatePlayerPartyCount(); sPSSData->screenChangeType = SCREEN_CHANGE_EXIT_BOX; SetPSSCallback(Cb_ChangeScreen); } break; } } static void Cb_ChangeScreen(u8 taskId) { struct BoxPokemon *boxMons; u8 mode, monIndex, maxMonIndex; u8 screenChangeType = sPSSData->screenChangeType; if (sPSSData->boxOption == OPTION_MOVE_ITEMS && IsMovingItem() == TRUE) sMovingItemId = GetMovingItemId(); else sMovingItemId = ITEM_NONE; switch (screenChangeType) { case SCREEN_CHANGE_EXIT_BOX: default: FreePSSData(); SetMainCallback2(Cb2_ExitPSS); break; case SCREEN_CHANGE_SUMMARY_SCREEN: boxMons = sPSSData->summaryMon.box; monIndex = sPSSData->summaryStartPos; maxMonIndex = sPSSData->summaryMaxPos; mode = sPSSData->summaryScreenMode; FreePSSData(); if (mode == SUMMARY_MODE_NORMAL && boxMons == &sSavedMovingMon.box) ShowPokemonSummaryScreenSet40EF(mode, boxMons, monIndex, maxMonIndex, Cb2_ReturnToPSS); else ShowPokemonSummaryScreen(mode, boxMons, monIndex, maxMonIndex, Cb2_ReturnToPSS); break; case SCREEN_CHANGE_NAME_BOX: FreePSSData(); DoNamingScreen(NAMING_SCREEN_BOX, GetBoxNamePtr(StorageGetCurrentBox()), 0, 0, 0, Cb2_ReturnToPSS); break; case SCREEN_CHANGE_ITEM_FROM_BAG: FreePSSData(); GoToBagMenu(ITEMMENULOCATION_PCBOX, 0, Cb2_ReturnToPSS); break; } DestroyTask(taskId); } static void GiveChosenBagItem(void) { u16 itemId = gSpecialVar_ItemId; if (itemId != ITEM_NONE) { u8 pos = GetCursorPosition(); if (sInPartyMenu) SetMonData(&gPlayerParty[pos], MON_DATA_HELD_ITEM, &itemId); else SetCurrentBoxMonData(pos, MON_DATA_HELD_ITEM, &itemId); RemoveBagItem(itemId, 1); } } static void FreePSSData(void) { TilemapUtil_Free(); MultiMove_Free(); FREE_AND_SET_NULL(sPSSData); FreeAllWindowBuffers(); } static void SetScrollingBackground(void) { SetGpuReg(REG_OFFSET_BG3CNT, BGCNT_PRIORITY(3) | BGCNT_CHARBASE(3) | BGCNT_16COLOR | BGCNT_SCREENBASE(31)); DecompressAndLoadBgGfxUsingHeap(3, sScrollingBg_Gfx, 0, 0, 0); LZ77UnCompVram(sScrollingBg_Tilemap, (void *)BG_SCREEN_ADDR(31)); } static void ScrollBackground(void) { ChangeBgX(3, 128, 1); ChangeBgY(3, 128, 2); } static void LoadPSSMenuGfx(void) { InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates)); DecompressAndLoadBgGfxUsingHeap(1, gStorageSystemMenu_Gfx, 0, 0, 0); LZ77UnCompWram(sDisplayMenu_Tilemap, sPSSData->displayMenuTilemapBuffer); SetBgTilemapBuffer(1, sPSSData->displayMenuTilemapBuffer); ShowBg(1); ScheduleBgCopyTilemapToVram(1); } static bool8 InitPSSWindows(void) { if (!InitWindows(sWindowTemplates)) { return FALSE; } else { DeactivateAllTextPrinters(); return TRUE; } } static void LoadWaveformSpritePalette(void) { LoadSpritePalette(&gWaveformSpritePalette); } static void sub_80CA0D8(void) { LoadPalette(sInterface_Pal, 0, sizeof(sInterface_Pal)); LoadPalette(sPkmnDataGray_Pal, 0x20, sizeof(sPkmnDataGray_Pal)); LoadPalette(gUnknown_085726F4, 0xF0, sizeof(gUnknown_085726F4)); if (sPSSData->boxOption != OPTION_MOVE_ITEMS) LoadPalette(sBg_Pal, 0x30, sizeof(sBg_Pal)); else LoadPalette(sBgMoveItems_Pal, 0x30, sizeof(sBgMoveItems_Pal)); SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(1) | BGCNT_CHARBASE(1) | BGCNT_16COLOR | BGCNT_SCREENBASE(30)); CreateDisplayMonSprite(); CreateMarkingComboSprite(); CreateWaveformSprites(); RefreshDisplayMonData(); } static void CreateMarkingComboSprite(void) { sPSSData->markingComboSprite = CreateMonMarkingComboSprite(GFXTAG_MARKING_COMBO, PALTAG_MARKING_COMBO, NULL); sPSSData->markingComboSprite->oam.priority = 1; sPSSData->markingComboSprite->subpriority = 1; sPSSData->markingComboSprite->pos1.x = 40; sPSSData->markingComboSprite->pos1.y = 150; sPSSData->markingComboTilesPtr = (void*) OBJ_VRAM0 + 32 * GetSpriteTileStartByTag(GFXTAG_MARKING_COMBO); } static void CreateWaveformSprites(void) { u16 i; struct SpriteSheet sheet = sSpriteSheet_Waveform; LoadSpriteSheet(&sheet); for (i = 0; i < ARRAY_COUNT(sPSSData->waveformSprites); i++) { u8 spriteId = CreateSprite(&sSpriteTemplate_Waveform, i * 63 + 8, 9, 2); sPSSData->waveformSprites[i] = &gSprites[spriteId]; } } static void RefreshDisplayMonData(void) { LoadDisplayMonGfx(sPSSData->displayMonSpecies, sPSSData->displayMonPersonality); PrintDisplayMonInfo(); UpdateWaveformAnimation(); ScheduleBgCopyTilemapToVram(0); } static void StartDisplayMonMosaicEffect(void) { RefreshDisplayMonData(); if (sPSSData->displayMonSprite) { sPSSData->displayMonSprite->oam.mosaic = TRUE; sPSSData->displayMonSprite->data[0] = 10; sPSSData->displayMonSprite->data[1] = 1; sPSSData->displayMonSprite->callback = SpriteCB_DisplayMonMosaic; SetGpuReg(REG_OFFSET_MOSAIC, (sPSSData->displayMonSprite->data[0] << 12) | (sPSSData->displayMonSprite->data[0] << 8)); } } static u8 IsDisplayMosaicActive(void) { return sPSSData->displayMonSprite->oam.mosaic; } static void SpriteCB_DisplayMonMosaic(struct Sprite *sprite) { sprite->data[0] -= sprite->data[1]; if (sprite->data[0] < 0) sprite->data[0] = 0; SetGpuReg(REG_OFFSET_MOSAIC, (sprite->data[0] << 12) | (sprite->data[0] << 8)); if (sprite->data[0] == 0) { sprite->oam.mosaic = FALSE; sprite->callback = SpriteCallbackDummy; } } static void CreateDisplayMonSprite(void) { u16 i; u16 tileStart; u8 palSlot; u8 spriteId; struct SpriteSheet sheet = {sPSSData->tileBuffer, MON_PIC_SIZE, GFXTAG_DISPLAY_MON}; struct SpritePalette palette = {sPSSData->displayMonPalBuffer, PALTAG_DISPLAY_MON}; struct SpriteTemplate template = sSpriteTemplate_DisplayMon; for (i = 0; i < MON_PIC_SIZE; i++) sPSSData->tileBuffer[i] = 0; for (i = 0; i < 16; i++) sPSSData->displayMonPalBuffer[i] = 0; sPSSData->displayMonSprite = NULL; do { tileStart = LoadSpriteSheet(&sheet); if (tileStart == 0) break; palSlot = LoadSpritePalette(&palette); if (palSlot == 0xFF) break; spriteId = CreateSprite(&template, 40, 48, 0); if (spriteId == MAX_SPRITES) break; sPSSData->displayMonSprite = &gSprites[spriteId]; sPSSData->displayMonPalOffset = palSlot * 16 + 0x100; sPSSData->displayMonTilePtr = (void*) OBJ_VRAM0 + tileStart * 32; } while (0); if (sPSSData->displayMonSprite == NULL) { FreeSpriteTilesByTag(GFXTAG_DISPLAY_MON); FreeSpritePaletteByTag(PALTAG_DISPLAY_MON); } } static void LoadDisplayMonGfx(u16 species, u32 pid) { if (sPSSData->displayMonSprite == NULL) return; if (species != SPECIES_NONE) { LoadSpecialPokePic(&gMonFrontPicTable[species], sPSSData->tileBuffer, species, pid, TRUE); LZ77UnCompWram(sPSSData->displayMonPalette, sPSSData->displayMonPalBuffer); CpuCopy32(sPSSData->tileBuffer, sPSSData->displayMonTilePtr, MON_PIC_SIZE); LoadPalette(sPSSData->displayMonPalBuffer, sPSSData->displayMonPalOffset, 0x20); sPSSData->displayMonSprite->invisible = FALSE; } else { sPSSData->displayMonSprite->invisible = TRUE; } } static void PrintDisplayMonInfo(void) { FillWindowPixelBuffer(0, PIXEL_FILL(1)); if (sPSSData->boxOption != OPTION_MOVE_ITEMS) { AddTextPrinterParameterized(0, 1, sPSSData->displayMonNameText, 6, 0, TEXT_SPEED_FF, NULL); AddTextPrinterParameterized(0, 2, sPSSData->displayMonSpeciesName, 6, 15, TEXT_SPEED_FF, NULL); AddTextPrinterParameterized(0, 2, sPSSData->displayMonGenderLvlText, 10, 29, TEXT_SPEED_FF, NULL); AddTextPrinterParameterized(0, 0, sPSSData->displayMonItemName, 6, 43, TEXT_SPEED_FF, NULL); } else { AddTextPrinterParameterized(0, 0, sPSSData->displayMonItemName, 6, 0, TEXT_SPEED_FF, NULL); AddTextPrinterParameterized(0, 1, sPSSData->displayMonNameText, 6, 13, TEXT_SPEED_FF, NULL); AddTextPrinterParameterized(0, 2, sPSSData->displayMonSpeciesName, 6, 28, TEXT_SPEED_FF, NULL); AddTextPrinterParameterized(0, 2, sPSSData->displayMonGenderLvlText, 10, 42, TEXT_SPEED_FF, NULL); } CopyWindowToVram(0, 2); if (sPSSData->displayMonSpecies != SPECIES_NONE) { UpdateMonMarkingTiles(sPSSData->displayMonMarkings, sPSSData->markingComboTilesPtr); sPSSData->markingComboSprite->invisible = FALSE; } else { sPSSData->markingComboSprite->invisible = TRUE; } } // Turn the wave animation on the sides of "Pkmn Data" on/off static void UpdateWaveformAnimation(void) { u16 i; if (sPSSData->displayMonSpecies != SPECIES_NONE) { // Start waveform animation and color "Pkmn Data" TilemapUtil_SetRect(TILEMAPID_PKMN_DATA, 0, 0, 8, 2); for (i = 0; i < ARRAY_COUNT(sPSSData->waveformSprites); i++) StartSpriteAnimIfDifferent(sPSSData->waveformSprites[i], i * 2 + 1); } else { // Stop waveform animation and gray out "Pkmn Data" TilemapUtil_SetRect(TILEMAPID_PKMN_DATA, 0, 2, 8, 2); for (i = 0; i < ARRAY_COUNT(sPSSData->waveformSprites); i++) StartSpriteAnim(sPSSData->waveformSprites[i], i * 2); } TilemapUtil_Update(TILEMAPID_PKMN_DATA); ScheduleBgCopyTilemapToVram(1); } static void InitSupplementalTilemaps(void) { LZ77UnCompWram(gStorageSystemPartyMenu_Tilemap, sPSSData->partyMenuTilemapBuffer); LoadPalette(gStorageSystemPartyMenu_Pal, 0x10, 0x20); TilemapUtil_SetMap(TILEMAPID_PARTY_MENU, 1, sPSSData->partyMenuTilemapBuffer, 12, 22); TilemapUtil_SetMap(TILEMAPID_CLOSE_BUTTON, 1, sCloseBoxButton_Tilemap, 9, 4); TilemapUtil_SetPos(TILEMAPID_PARTY_MENU, 10, 0); TilemapUtil_SetPos(TILEMAPID_CLOSE_BUTTON, 21, 0); SetPartySlotTilemaps(); if (sInPartyMenu) { UpdateCloseBoxButtonTilemap(TRUE); CreatePartyMonsSprites(TRUE); TilemapUtil_Update(TILEMAPID_CLOSE_BUTTON); TilemapUtil_Update(TILEMAPID_PARTY_MENU); } else { TilemapUtil_SetRect(TILEMAPID_PARTY_MENU, 0, 20, 12, 2); UpdateCloseBoxButtonTilemap(TRUE); TilemapUtil_Update(TILEMAPID_PARTY_MENU); TilemapUtil_Update(TILEMAPID_CLOSE_BUTTON); } ScheduleBgCopyTilemapToVram(1); sPSSData->closeBoxFlashing = FALSE; } static void SetUpShowPartyMenu(void) { sPSSData->partyMenuUnused = 20; sPSSData->partyMenuY = 2; sPSSData->partyMenuMoveTimer = 0; CreatePartyMonsSprites(FALSE); } static bool8 ShowPartyMenu(void) { if (sPSSData->partyMenuMoveTimer == 20) return FALSE; sPSSData->partyMenuUnused--; sPSSData->partyMenuY++; TilemapUtil_Move(TILEMAPID_PARTY_MENU, 3, 1); TilemapUtil_Update(TILEMAPID_PARTY_MENU); ScheduleBgCopyTilemapToVram(1); MovePartySprites(8); if (++sPSSData->partyMenuMoveTimer == 20) { sInPartyMenu = TRUE; return FALSE; } else { return TRUE; } } static void SetUpHidePartyMenu(void) { sPSSData->partyMenuUnused = 0; sPSSData->partyMenuY = 22; sPSSData->partyMenuMoveTimer = 0; if (sPSSData->boxOption == OPTION_MOVE_ITEMS) MoveHeldItemWithPartyMenu(); } static bool8 HidePartyMenu(void) { if (sPSSData->partyMenuMoveTimer != 20) { sPSSData->partyMenuUnused++; sPSSData->partyMenuY--; TilemapUtil_Move(TILEMAPID_PARTY_MENU, 3, -1); TilemapUtil_Update(TILEMAPID_PARTY_MENU); FillBgTilemapBufferRect_Palette0(1, 0x100, 10, sPSSData->partyMenuY, 12, 1); MovePartySprites(-8); if (++sPSSData->partyMenuMoveTimer != 20) { ScheduleBgCopyTilemapToVram(1); return TRUE; } else { sInPartyMenu = FALSE; DestroyAllPartyMonIcons(); CompactPartySlots(); // The close box button gets partially covered by // the party menu, restore it TilemapUtil_SetRect(TILEMAPID_CLOSE_BUTTON, 0, 0, 9, 2); TilemapUtil_Update(TILEMAPID_CLOSE_BUTTON); ScheduleBgCopyTilemapToVram(1); return FALSE; } } return FALSE; } static void UpdateCloseBoxButtonTilemap(bool8 normal) { if (normal) TilemapUtil_SetRect(TILEMAPID_CLOSE_BUTTON, 0, 0, 9, 2); else // flashing TilemapUtil_SetRect(TILEMAPID_CLOSE_BUTTON, 0, 2, 9, 2); TilemapUtil_Update(TILEMAPID_CLOSE_BUTTON); ScheduleBgCopyTilemapToVram(1); } static void StartFlashingCloseBoxButton(void) { sPSSData->closeBoxFlashing = TRUE; sPSSData->closeBoxFlashTimer = 30; sPSSData->closeBoxFlashState = TRUE; } static void StopFlashingCloseBoxButton(void) { if (sPSSData->closeBoxFlashing) { sPSSData->closeBoxFlashing = FALSE; UpdateCloseBoxButtonTilemap(TRUE); } } static void UpdateCloseBoxButtonFlash(void) { if (sPSSData->closeBoxFlashing && ++sPSSData->closeBoxFlashTimer > 30) { sPSSData->closeBoxFlashTimer = 0; sPSSData->closeBoxFlashState = (sPSSData->closeBoxFlashState == FALSE); UpdateCloseBoxButtonTilemap(sPSSData->closeBoxFlashState); } } static void SetPartySlotTilemaps(void) { u8 i; // Skips first party slot, it should always be drawn // as if it has a Pokémon in it for (i = 1; i < PARTY_SIZE; i++) { s32 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES); SetPartySlotTilemap(i, species != SPECIES_NONE); } } static void SetPartySlotTilemap(u8 partyId, bool8 hasMon) { u16 i, j, index; const u16 *data; if (hasMon) data = sPartySlotFilled_Tilemap; else data = sPartySlotEmpty_Tilemap; index = 3 * (3 * (partyId - 1) + 1); index *= 4; index += 7; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) sPSSData->partyMenuTilemapBuffer[index + j] = data[j]; data += 4; index += 12; } } static void UpdatePartySlotColors(void) { SetPartySlotTilemaps(); TilemapUtil_SetRect(TILEMAPID_PARTY_MENU, 0, 0, 12, 22); TilemapUtil_Update(TILEMAPID_PARTY_MENU); ScheduleBgCopyTilemapToVram(1); } static void SetUpDoShowPartyMenu(void) { sPSSData->showPartyMenuState = 0; PlaySE(SE_WIN_OPEN); SetUpShowPartyMenu(); } static bool8 DoShowPartyMenu(void) { switch (sPSSData->showPartyMenuState) { case 0: if (!ShowPartyMenu()) { SetCursorInParty(); sPSSData->showPartyMenuState++; } break; case 1: if (!UpdateCursorPos()) { if (sPSSData->setMosaic) StartDisplayMonMosaicEffect(); sPSSData->showPartyMenuState++; } break; case 2: return FALSE; } return TRUE; } static void sub_80CABE0(void) { if (sLastUsedBox != StorageGetCurrentBox()) { FlagClear(FLAG_SHOWN_BOX_WAS_FULL_MESSAGE); VarSet(VAR_PC_BOX_TO_SEND_MON, StorageGetCurrentBox()); } } static void sub_80CAC1C(void) { SetGpuReg(REG_OFFSET_BG0CNT, BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(29)); LoadUserWindowBorderGfx(1, 2, 208); FillBgTilemapBufferRect(0, 0, 0, 0, 32, 20, 17); CopyBgTilemapBufferToVram(0); } static void PrintMessage(u8 id) { u8 *txtPtr; DynamicPlaceholderTextUtil_Reset(); switch (sMessages[id].format) { case MSG_VAR_NONE: break; case MSG_VAR_MON_NAME_1: case MSG_VAR_MON_NAME_2: case MSG_VAR_MON_NAME_3: DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sPSSData->displayMonName); break; case MSG_VAR_RELEASE_MON_1: case MSG_VAR_RELEASE_MON_2: case MSG_VAR_RELEASE_MON_3: DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sPSSData->releaseMonName); break; case MSG_VAR_ITEM_NAME: if (IsMovingItem()) txtPtr = StringCopy(sPSSData->itemName, GetMovingItemName()); else txtPtr = StringCopy(sPSSData->itemName, sPSSData->displayMonItemName); while (*(txtPtr - 1) == CHAR_SPACE) txtPtr--; *txtPtr = EOS; DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sPSSData->itemName); break; } DynamicPlaceholderTextUtil_ExpandPlaceholders(sPSSData->messageText, sMessages[id].text); FillWindowPixelBuffer(1, PIXEL_FILL(1)); AddTextPrinterParameterized(1, 1, sPSSData->messageText, 0, 1, TEXT_SPEED_FF, NULL); DrawTextBorderOuter(1, 2, 14); PutWindowTilemap(1); CopyWindowToVram(1, 2); ScheduleBgCopyTilemapToVram(0); } static void ShowYesNoWindow(s8 cursorPos) { CreateYesNoMenu(&sYesNoWindowTemplate, 11, 14, 0); Menu_MoveCursorNoWrapAround(cursorPos); } static void ClearBottomWindow(void) { ClearStdWindowAndFrameToTransparent(1, FALSE); ScheduleBgCopyTilemapToVram(0); } static void AddWallpaperSetsMenu(void) { InitMenu(); SetMenuText(MENU_SCENERY_1); SetMenuText(MENU_SCENERY_2); SetMenuText(MENU_SCENERY_3); SetMenuText(MENU_ETCETERA); if (IsWaldaWallpaperUnlocked()) SetMenuText(MENU_FRIENDS); AddMenu(); } static void AddWallpapersMenu(u8 wallpaperSet) { InitMenu(); switch (wallpaperSet) { case MENU_SCENERY_1 - MENU_WALLPAPER_SETS_START: SetMenuText(MENU_FOREST); SetMenuText(MENU_CITY); SetMenuText(MENU_DESERT); SetMenuText(MENU_SAVANNA); break; case MENU_SCENERY_2 - MENU_WALLPAPER_SETS_START: SetMenuText(MENU_CRAG); SetMenuText(MENU_VOLCANO); SetMenuText(MENU_SNOW); SetMenuText(MENU_CAVE); break; case MENU_SCENERY_3 - MENU_WALLPAPER_SETS_START: SetMenuText(MENU_BEACH); SetMenuText(MENU_SEAFLOOR); SetMenuText(MENU_RIVER); SetMenuText(MENU_SKY); break; case MENU_ETCETERA - MENU_WALLPAPER_SETS_START: SetMenuText(MENU_POLKADOT); SetMenuText(MENU_POKECENTER); SetMenuText(MENU_MACHINE); SetMenuText(MENU_SIMPLE); break; } AddMenu(); } static u8 GetCurrentBoxOption(void) { return sCurrentBoxOption; } static void sub_80CAEAC(void) { if (!IsCursorOnBoxTitle()) { if (sInPartyMenu) TryLoadItemIconAtPos(CURSOR_AREA_IN_PARTY, GetCursorPosition()); else TryLoadItemIconAtPos(CURSOR_AREA_IN_BOX, GetCursorPosition()); } if (sMovingItemId != ITEM_NONE) { InitItemIconInCursor(sMovingItemId); StartCursorAnim(CURSOR_ANIM_FIST); } } static void InitMonIconFields(void) { u16 i; LoadMonIconPalettes(); for (i = 0; i < MAX_MON_ICONS; i++) sPSSData->numIconsPerSpecies[i] = 0; for (i = 0; i < MAX_MON_ICONS; i++) sPSSData->iconSpeciesList[i] = SPECIES_NONE; for (i = 0; i < PARTY_SIZE; i++) sPSSData->partySprites[i] = NULL; for (i = 0; i < IN_BOX_COUNT; i++) sPSSData->boxMonsSprites[i] = NULL; sPSSData->movingMonSprite = NULL; sPSSData->field_78C = 0; } static u8 GetMonIconPriorityByCursorPos(void) { return (IsCursorInBox() ? 2 : 1); } static void CreateMovingMonIcon(void) { u32 personality = GetMonData(&sPSSData->movingMon, MON_DATA_PERSONALITY); u16 species = GetMonData(&sPSSData->movingMon, MON_DATA_SPECIES2); u8 priority = GetMonIconPriorityByCursorPos(); sPSSData->movingMonSprite = CreateMonIconSprite(species, personality, 0, 0, priority, 7); sPSSData->movingMonSprite->callback = SpriteCB_HeldMon; } static void InitBoxMonSprites(u8 boxId) { u8 boxPosition; u16 i, j, count; u16 species; u32 personality; count = 0; boxPosition = 0; // For each box slot, create a Pokémon icon if a species is present for (i = 0; i < IN_BOX_ROWS; i++) { for (j = 0; j < IN_BOX_COLUMNS; j++) { species = GetBoxMonDataAt(boxId, boxPosition, MON_DATA_SPECIES2); if (species != SPECIES_NONE) { personality = GetBoxMonDataAt(boxId, boxPosition, MON_DATA_PERSONALITY); sPSSData->boxMonsSprites[count] = CreateMonIconSprite(species, personality, 8 * (3 * j) + 100, 8 * (3 * i) + 44, 2, 19 - j); } else { sPSSData->boxMonsSprites[count] = NULL; } boxPosition++; count++; } } // If in item mode, set all Pokémon icons with no item to be transparent if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { for (boxPosition = 0; boxPosition < IN_BOX_COUNT; boxPosition++) { if (GetBoxMonDataAt(boxId, boxPosition, MON_DATA_HELD_ITEM) == ITEM_NONE) sPSSData->boxMonsSprites[boxPosition]->oam.objMode = ST_OAM_OBJ_BLEND; } } } static void sub_80CB140(u8 boxPosition) { u16 species = GetCurrentBoxMonData(boxPosition, MON_DATA_SPECIES2); if (species != SPECIES_NONE) { s16 x = 8 * (3 * (boxPosition % IN_BOX_COLUMNS)) + 100; s16 y = 8 * (3 * (boxPosition / IN_BOX_COLUMNS)) + 44; u32 personality = GetCurrentBoxMonData(boxPosition, MON_DATA_PERSONALITY); sPSSData->boxMonsSprites[boxPosition] = CreateMonIconSprite(species, personality, x, y, 2, 19 - (boxPosition % IN_BOX_COLUMNS)); if (sPSSData->boxOption == OPTION_MOVE_ITEMS) sPSSData->boxMonsSprites[boxPosition]->oam.objMode = ST_OAM_OBJ_BLEND; } } static void sub_80CB1F0(s16 arg0) { u16 i; for (i = 0; i < IN_BOX_COUNT; i++) { if (sPSSData->boxMonsSprites[i] != NULL) { sPSSData->boxMonsSprites[i]->data[2] = arg0; sPSSData->boxMonsSprites[i]->data[4] = 1; sPSSData->boxMonsSprites[i]->callback = sub_80CB278; } } } static void sub_80CB234(struct Sprite *sprite) { if (sprite->data[1] != 0) { sprite->data[1]--; sprite->pos1.x += sprite->data[2]; } else { sPSSData->field_C66--; sprite->pos1.x = sprite->data[3]; sprite->callback = SpriteCallbackDummy; } } static void sub_80CB278(struct Sprite *sprite) { if (sprite->data[4] != 0) { sprite->data[4]--; } else { sprite->pos1.x += sprite->data[2]; sprite->data[5] = sprite->pos1.x + sprite->pos2.x; if (sprite->data[5] <= 68 || sprite->data[5] >= 252) sprite->callback = SpriteCallbackDummy; } } static void DestroyAllIconsInColumn(u8 column) { u16 row; u8 boxPosition = column; for (row = 0; row < IN_BOX_ROWS; row++) { if (sPSSData->boxMonsSprites[boxPosition] != NULL) { DestroyBoxMonIcon(sPSSData->boxMonsSprites[boxPosition]); sPSSData->boxMonsSprites[boxPosition] = NULL; } boxPosition += IN_BOX_COLUMNS; } } static u8 sub_80CB2F8(u8 row, u16 times, s16 xDelta) { s32 i; u16 y = 44; s16 xDest = 8 * (3 * row) + 100; u16 x = xDest - ((times + 1) * xDelta); u8 subpriority = 19 - row; u8 count = 0; u8 boxPosition = row; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) { for (i = 0; i < IN_BOX_ROWS; i++) { if (sPSSData->boxSpecies[boxPosition] != SPECIES_NONE) { sPSSData->boxMonsSprites[boxPosition] = CreateMonIconSprite(sPSSData->boxSpecies[boxPosition], sPSSData->boxPersonalities[boxPosition], x, y, 2, subpriority); if (sPSSData->boxMonsSprites[boxPosition] != NULL) { sPSSData->boxMonsSprites[boxPosition]->data[1] = times; sPSSData->boxMonsSprites[boxPosition]->data[2] = xDelta; sPSSData->boxMonsSprites[boxPosition]->data[3] = xDest; sPSSData->boxMonsSprites[boxPosition]->callback = sub_80CB234; count++; } } boxPosition += IN_BOX_COLUMNS; y += 24; } } else { for (i = 0; i < IN_BOX_ROWS; i++) { if (sPSSData->boxSpecies[boxPosition] != SPECIES_NONE) { sPSSData->boxMonsSprites[boxPosition] = CreateMonIconSprite(sPSSData->boxSpecies[boxPosition], sPSSData->boxPersonalities[boxPosition], x, y, 2, subpriority); if (sPSSData->boxMonsSprites[boxPosition] != NULL) { sPSSData->boxMonsSprites[boxPosition]->data[1] = times; sPSSData->boxMonsSprites[boxPosition]->data[2] = xDelta; sPSSData->boxMonsSprites[boxPosition]->data[3] = xDest; sPSSData->boxMonsSprites[boxPosition]->callback = sub_80CB234; if (GetBoxMonDataAt(sPSSData->field_C5C, boxPosition, MON_DATA_HELD_ITEM) == 0) sPSSData->boxMonsSprites[boxPosition]->oam.objMode = ST_OAM_OBJ_BLEND; count++; } } boxPosition += IN_BOX_COLUMNS; y += 24; } } return count; } static void sub_80CB4CC(u8 boxId, s8 direction) { sPSSData->field_C6A = 0; sPSSData->field_C6B = boxId; sPSSData->field_C69 = direction; sPSSData->field_C60 = 32; sPSSData->field_C64 = -(6 * direction); sPSSData->field_C66 = 0; SetBoxSpeciesAndPersonalities(boxId); if (direction > 0) sPSSData->field_C68 = 0; else sPSSData->field_C68 = IN_BOX_COLUMNS - 1; sPSSData->field_C62 = (24 * sPSSData->field_C68) + 100; sub_80CB1F0(sPSSData->field_C64); } static bool8 sub_80CB584(void) { if (sPSSData->field_C60 != 0) sPSSData->field_C60--; switch (sPSSData->field_C6A) { case 0: sPSSData->field_C62 += sPSSData->field_C64; if (sPSSData->field_C62 <= 64 || sPSSData->field_C62 >= 252) { DestroyAllIconsInColumn(sPSSData->field_C68); sPSSData->field_C62 += sPSSData->field_C69 * 24; sPSSData->field_C6A++; } break; case 1: sPSSData->field_C62 += sPSSData->field_C64; sPSSData->field_C66 += sub_80CB2F8(sPSSData->field_C68, sPSSData->field_C60, sPSSData->field_C64); if ((sPSSData->field_C69 > 0 && sPSSData->field_C68 == IN_BOX_COLUMNS - 1) || (sPSSData->field_C69 < 0 && sPSSData->field_C68 == 0)) { sPSSData->field_C6A++; } else { sPSSData->field_C68 += sPSSData->field_C69; sPSSData->field_C6A = 0; } break; case 2: if (sPSSData->field_C66 == 0) { sPSSData->field_C60++; return FALSE; } break; default: return FALSE; } return TRUE; } static void SetBoxSpeciesAndPersonalities(u8 boxId) { s32 i, j, boxPosition; boxPosition = 0; for (i = 0; i < IN_BOX_ROWS; i++) { for (j = 0; j < IN_BOX_COLUMNS; j++) { sPSSData->boxSpecies[boxPosition] = GetBoxMonDataAt(boxId, boxPosition, MON_DATA_SPECIES2); if (sPSSData->boxSpecies[boxPosition] != SPECIES_NONE) sPSSData->boxPersonalities[boxPosition] = GetBoxMonDataAt(boxId, boxPosition, MON_DATA_PERSONALITY); boxPosition++; } } sPSSData->field_C5C = boxId; } static void DestroyBoxMonIconAtPosition(u8 boxPosition) { if (sPSSData->boxMonsSprites[boxPosition] != NULL) { DestroyBoxMonIcon(sPSSData->boxMonsSprites[boxPosition]); sPSSData->boxMonsSprites[boxPosition] = NULL; } } static void SetBoxMonIconObjMode(u8 boxPosition, u8 objMode) { if (sPSSData->boxMonsSprites[boxPosition] != NULL) { sPSSData->boxMonsSprites[boxPosition]->oam.objMode = objMode; } } static void CreatePartyMonsSprites(bool8 visible) { u16 i, count; u16 species = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES2); u32 personality = GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY); sPSSData->partySprites[0] = CreateMonIconSprite(species, personality, 104, 64, 1, 12); count = 1; for (i = 1; i < PARTY_SIZE; i++) { species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2); if (species != SPECIES_NONE) { personality = GetMonData(&gPlayerParty[i], MON_DATA_PERSONALITY); sPSSData->partySprites[i] = CreateMonIconSprite(species, personality, 152, 8 * (3 * (i - 1)) + 16, 1, 12); count++; } else { sPSSData->partySprites[i] = NULL; } } if (!visible) { for (i = 0; i < count; i++) { sPSSData->partySprites[i]->pos1.y -= 160; sPSSData->partySprites[i]->invisible = TRUE; } } if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { for (i = 0; i < PARTY_SIZE; i++) { if (sPSSData->partySprites[i] != NULL && GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) == 0) sPSSData->partySprites[i]->oam.objMode = ST_OAM_OBJ_BLEND; } } } static void CompactPartySprites(void) { u16 i, targetSlot; sPSSData->numPartyToCompact = 0; for (i = 0, targetSlot = 0; i < PARTY_SIZE; i++) { if (sPSSData->partySprites[i] != NULL) { if (i != targetSlot) { MovePartySpriteToNextSlot(sPSSData->partySprites[i], targetSlot); sPSSData->partySprites[i] = NULL; sPSSData->numPartyToCompact++; } targetSlot++; } } } static u8 GetNumPartySpritesCompacting(void) { return sPSSData->numPartyToCompact; } #define sPartyId data[1] #define sMonX data[2] #define sMonY data[3] #define sSpeedX data[4] #define sSpeedY data[5] #define sMoveSteps data[6] static void MovePartySpriteToNextSlot(struct Sprite *sprite, u16 partyId) { s16 x, y; sprite->sPartyId = partyId; if (partyId == 0) x = 104, y = 64; else x = 152, y = 8 * (3 * (partyId - 1)) + 16; sprite->sMonX = (u16)(sprite->pos1.x) * 8; sprite->sMonY = (u16)(sprite->pos1.y) * 8; sprite->sSpeedX = ((x * 8) - sprite->sMonX) / 8; sprite->sSpeedY = ((y * 8) - sprite->sMonY) / 8; sprite->data[6] = 8; sprite->callback = SpriteCB_MovePartyMonToNextSlot; } static void SpriteCB_MovePartyMonToNextSlot(struct Sprite *sprite) { if (sprite->sMoveSteps != 0) { s16 x = sprite->sMonX += sprite->sSpeedX; s16 y = sprite->sMonY += sprite->sSpeedY; sprite->pos1.x = x / 8u; sprite->pos1.y = y / 8u; sprite->sMoveSteps--; } else { if (sprite->sPartyId == 0) { sprite->pos1.x = 104; sprite->pos1.y = 64; } else { sprite->pos1.x = 152; sprite->pos1.y = 8 * (3 * (sprite->sPartyId - 1)) + 16; } sprite->callback = SpriteCallbackDummy; sPSSData->partySprites[sprite->sPartyId] = sprite; sPSSData->numPartyToCompact--; } } #undef sPartyId #undef sMonX #undef sMonY #undef sSpeedX #undef sSpeedY #undef sMoveSteps static void DestroyMovingMonIcon(void) { if (sPSSData->movingMonSprite != NULL) { DestroyBoxMonIcon(sPSSData->movingMonSprite); sPSSData->movingMonSprite = NULL; } } static void MovePartySprites(s16 yDelta) { u16 i, posY; for (i = 0; i < PARTY_SIZE; i++) { if (sPSSData->partySprites[i] != NULL) { sPSSData->partySprites[i]->pos1.y += yDelta; posY = sPSSData->partySprites[i]->pos1.y + sPSSData->partySprites[i]->pos2.y + sPSSData->partySprites[i]->centerToCornerVecY; posY += 16; if (posY > 192) sPSSData->partySprites[i]->invisible = TRUE; else sPSSData->partySprites[i]->invisible = FALSE; } } } static void DestroyPartyMonIcon(u8 partyId) { if (sPSSData->partySprites[partyId] != NULL) { DestroyBoxMonIcon(sPSSData->partySprites[partyId]); sPSSData->partySprites[partyId] = NULL; } } static void DestroyAllPartyMonIcons(void) { u16 i; for (i = 0; i < PARTY_SIZE; i++) { if (sPSSData->partySprites[i] != NULL) { DestroyBoxMonIcon(sPSSData->partySprites[i]); sPSSData->partySprites[i] = NULL; } } } static void SetPartyMonIconObjMode(u8 partyId, u8 objMode) { if (sPSSData->partySprites[partyId] != NULL) { sPSSData->partySprites[partyId]->oam.objMode = objMode; } } static void SetMovingMonSprite(u8 mode, u8 id) { if (mode == MODE_PARTY) { sPSSData->movingMonSprite = sPSSData->partySprites[id]; sPSSData->partySprites[id] = NULL; } else if (mode == MODE_BOX) { sPSSData->movingMonSprite = sPSSData->boxMonsSprites[id]; sPSSData->boxMonsSprites[id] = NULL; } else { return; } sPSSData->movingMonSprite->callback = SpriteCB_HeldMon; sPSSData->movingMonSprite->oam.priority = GetMonIconPriorityByCursorPos(); sPSSData->movingMonSprite->subpriority = 7; } static void sub_80CBCAC(u8 boxId, u8 position) { if (boxId == TOTAL_BOXES_COUNT) // party mon { sPSSData->partySprites[position] = sPSSData->movingMonSprite; sPSSData->partySprites[position]->oam.priority = 1; sPSSData->partySprites[position]->subpriority = 12; } else { sPSSData->boxMonsSprites[position] = sPSSData->movingMonSprite; sPSSData->boxMonsSprites[position]->oam.priority = 2; sPSSData->boxMonsSprites[position]->subpriority = 19 - (position % IN_BOX_COLUMNS); } sPSSData->movingMonSprite->callback = SpriteCallbackDummy; sPSSData->movingMonSprite = NULL; } static void sub_80CBD5C(u8 boxId, u8 position) { if (boxId == TOTAL_BOXES_COUNT) // party mon sPSSData->field_B00 = &sPSSData->partySprites[position]; else sPSSData->field_B00 = &sPSSData->boxMonsSprites[position]; sPSSData->movingMonSprite->callback = SpriteCallbackDummy; sPSSData->field_C5D = 0; } static bool8 sub_80CBDC4(void) { if (sPSSData->field_C5D == 16) return FALSE; sPSSData->field_C5D++; if (sPSSData->field_C5D & 1) { (*sPSSData->field_B00)->pos1.y--; sPSSData->movingMonSprite->pos1.y++; } (*sPSSData->field_B00)->pos2.x = gSineTable[sPSSData->field_C5D * 8] / 16; sPSSData->movingMonSprite->pos2.x = -(gSineTable[sPSSData->field_C5D * 8] / 16); if (sPSSData->field_C5D == 8) { sPSSData->movingMonSprite->oam.priority = (*sPSSData->field_B00)->oam.priority; sPSSData->movingMonSprite->subpriority = (*sPSSData->field_B00)->subpriority; (*sPSSData->field_B00)->oam.priority = GetMonIconPriorityByCursorPos(); (*sPSSData->field_B00)->subpriority = 7; } if (sPSSData->field_C5D == 16) { struct Sprite *sprite = sPSSData->movingMonSprite; sPSSData->movingMonSprite = (*sPSSData->field_B00); *sPSSData->field_B00 = sprite; sPSSData->movingMonSprite->callback = SpriteCB_HeldMon; (*sPSSData->field_B00)->callback = SpriteCallbackDummy; } return TRUE; } static void SetReleaseMon(u8 mode, u8 position) { switch (mode) { case MODE_PARTY: sPSSData->releaseMonSpritePtr = &sPSSData->partySprites[position]; break; case MODE_BOX: sPSSData->releaseMonSpritePtr = &sPSSData->boxMonsSprites[position]; break; case MODE_MOVE: sPSSData->releaseMonSpritePtr = &sPSSData->movingMonSprite; break; default: return; } if (*sPSSData->releaseMonSpritePtr != NULL) { InitSpriteAffineAnim(*sPSSData->releaseMonSpritePtr); (*sPSSData->releaseMonSpritePtr)->oam.affineMode = ST_OAM_AFFINE_NORMAL; (*sPSSData->releaseMonSpritePtr)->affineAnims = sAffineAnims_ReleaseMon; StartSpriteAffineAnim(*sPSSData->releaseMonSpritePtr, RELEASE_ANIM_RELEASE); } } static bool8 TryHideReleaseMonSprite(void) { if (*sPSSData->releaseMonSpritePtr == NULL || (*sPSSData->releaseMonSpritePtr)->invisible) return FALSE; if ((*sPSSData->releaseMonSpritePtr)->affineAnimEnded) (*sPSSData->releaseMonSpritePtr)->invisible = TRUE; return TRUE; } static void DestroyReleaseMonIcon(void) { if (*sPSSData->releaseMonSpritePtr != NULL) { FreeOamMatrix((*sPSSData->releaseMonSpritePtr)->oam.matrixNum); DestroyBoxMonIcon(*sPSSData->releaseMonSpritePtr); *sPSSData->releaseMonSpritePtr = NULL; } } static void ReshowReleaseMon(void) { if (*sPSSData->releaseMonSpritePtr != NULL) { (*sPSSData->releaseMonSpritePtr)->invisible = FALSE; StartSpriteAffineAnim(*sPSSData->releaseMonSpritePtr, RELEASE_ANIM_CAME_BACK); } } static bool8 ResetReleaseMonSpritePtr(void) { if (sPSSData->releaseMonSpritePtr == NULL) return FALSE; if ((*sPSSData->releaseMonSpritePtr)->affineAnimEnded) sPSSData->releaseMonSpritePtr = NULL; return TRUE; } static void SetMovingMonPriority(u8 priority) { sPSSData->movingMonSprite->oam.priority = priority; } static void SpriteCB_HeldMon(struct Sprite *sprite) { sprite->pos1.x = sPSSData->cursorSprite->pos1.x; sprite->pos1.y = sPSSData->cursorSprite->pos1.y + sPSSData->cursorSprite->pos2.y + 4; } static u16 TryLoadMonIconTiles(u16 species) { u16 i, offset; // Search icon list for this species for (i = 0; i < MAX_MON_ICONS; i++) { if (sPSSData->iconSpeciesList[i] == species) break; } if (i == MAX_MON_ICONS) { // Species not present in the list // Find first empty spot in the list to put it for (i = 0; i < MAX_MON_ICONS; i++) { if (sPSSData->iconSpeciesList[i] == 0) break; } // Failed to find an empty spot if (i == MAX_MON_ICONS) return 0xFFFF; } // Add species to icon list and load tiles sPSSData->iconSpeciesList[i] = species; sPSSData->numIconsPerSpecies[i]++; offset = 16 * i; CpuCopy32(GetMonIconTiles(species, TRUE), (void*)(OBJ_VRAM0) + offset * 32, 0x200); return offset; } static void RemoveSpeciesFromIconList(u16 species) { u16 i; for (i = 0; i < MAX_MON_ICONS; i++) { if (sPSSData->iconSpeciesList[i] == species) { if (--sPSSData->numIconsPerSpecies[i] == 0) sPSSData->iconSpeciesList[i] = SPECIES_NONE; break; } } } static struct Sprite *CreateMonIconSprite(u16 species, u32 personality, s16 x, s16 y, u8 oamPriority, u8 subpriority) { u16 tileNum; u8 spriteId; struct SpriteTemplate template = sSpriteTemplate_MonIcon; species = GetIconSpecies(species, personality); template.paletteTag = PALTAG_MON_ICON_0 + gMonIconPaletteIndices[species]; tileNum = TryLoadMonIconTiles(species); if (tileNum == 0xFFFF) return NULL; spriteId = CreateSprite(&template, x, y, subpriority); if (spriteId == MAX_SPRITES) { RemoveSpeciesFromIconList(species); return NULL; } gSprites[spriteId].oam.tileNum = tileNum; gSprites[spriteId].oam.priority = oamPriority; gSprites[spriteId].data[0] = species; return &gSprites[spriteId]; } static void DestroyBoxMonIcon(struct Sprite *sprite) { RemoveSpeciesFromIconList(sprite->data[0]); DestroySprite(sprite); } #define tState data[0] #define tDmaIdx data[1] #define tBoxId data[2] static void CreateInitBoxTask(u8 boxId) { u8 taskId = CreateTask(Task_InitBox, 2); gTasks[taskId].tBoxId = boxId; } static bool8 IsInitBoxActive(void) { return FuncIsActiveTask(Task_InitBox); } static void Task_InitBox(u8 taskId) { struct Task *task = &gTasks[taskId]; switch (task->tState) { case 0: sPSSData->wallpaperOffset = 0; sPSSData->bg2_X = 0; task->tDmaIdx = RequestDma3Fill(0, sPSSData->wallpaperBgTilemapBuffer, sizeof(sPSSData->wallpaperBgTilemapBuffer), 1); break; case 1: if (CheckForSpaceForDma3Request(task->tDmaIdx) == -1) return; SetBgTilemapBuffer(2, sPSSData->wallpaperBgTilemapBuffer); ShowBg(2); break; case 2: LoadWallpaperGfx(task->tBoxId, 0); break; case 3: if (!WaitForWallpaperGfxLoad()) return; InitBoxTitle(task->tBoxId); CreateBoxScrollArrows(); InitBoxMonSprites(task->tBoxId); SetGpuReg(REG_OFFSET_BG2CNT, BGCNT_PRIORITY(2) | BGCNT_CHARBASE(2) | BGCNT_SCREENBASE(27) | BGCNT_TXT512x256); break; case 4: DestroyTask(taskId); break; default: task->tState = 0; return; } task->tState++; } #undef tState #undef tDmaIdx #undef tBoxId static void SetUpScrollToBox(u8 boxId) { s8 direction = DetermineBoxScrollDirection(boxId); sPSSData->scrollSpeed = (direction > 0) ? 6 : -6; sPSSData->scrollUnused1 = (direction > 0) ? 1 : 2; sPSSData->scrollTimer = 32; sPSSData->scrollToBoxIdUnused = boxId; sPSSData->scrollUnused2 = (direction <= 0) ? 5 : 0; sPSSData->scrollDirectionUnused = direction; sPSSData->scrollUnused3 = (direction > 0) ? 264 : 56; sPSSData->scrollUnused4 = (direction <= 0) ? 5 : 0; sPSSData->scrollUnused5 = 0; sPSSData->scrollUnused6 = 2; sPSSData->scrollToBoxId = boxId; sPSSData->scrollDirection = direction; sPSSData->scrollState = 0; } static bool8 ScrollToBox(void) { bool8 var; switch (sPSSData->scrollState) { case 0: LoadWallpaperGfx(sPSSData->scrollToBoxId, sPSSData->scrollDirection); sPSSData->scrollState++; case 1: if (!WaitForWallpaperGfxLoad()) return TRUE; sub_80CB4CC(sPSSData->scrollToBoxId, sPSSData->scrollDirection); CreateIncomingBoxTitle(sPSSData->scrollToBoxId, sPSSData->scrollDirection); StartBoxScrollArrowsSlide(sPSSData->scrollDirection); break; case 2: var = sub_80CB584(); if (sPSSData->scrollTimer != 0) { sPSSData->bg2_X += sPSSData->scrollSpeed; if (--sPSSData->scrollTimer != 0) return TRUE; CycleBoxTitleSprites(); StopBoxScrollArrowsSlide(); } return var; } sPSSData->scrollState++; return TRUE; } static s8 DetermineBoxScrollDirection(u8 boxId) { u8 i; u8 currentBox = StorageGetCurrentBox(); for (i = 0; currentBox != boxId; i++) { currentBox++; if (currentBox >= TOTAL_BOXES_COUNT) currentBox = 0; } return (i < TOTAL_BOXES_COUNT / 2) ? 1 : -1; } static void SetWallpaperForCurrentBox(u8 wallpaperId) { u8 boxId = StorageGetCurrentBox(); SetBoxWallpaper(boxId, wallpaperId); sPSSData->wallpaperChangeState = 0; } static bool8 DoWallpaperGfxChange(void) { switch (sPSSData->wallpaperChangeState) { case 0: BeginNormalPaletteFade(sPSSData->boxTitlePalBits, 1, 0, 16, RGB_WHITEALPHA); sPSSData->wallpaperChangeState++; break; case 1: if (!UpdatePaletteFade()) { u8 curBox = StorageGetCurrentBox(); LoadWallpaperGfx(curBox, 0); sPSSData->wallpaperChangeState++; } break; case 2: if (WaitForWallpaperGfxLoad() == TRUE) { CycleBoxTitleColor(); BeginNormalPaletteFade(sPSSData->boxTitlePalBits, 1, 16, 0, RGB_WHITEALPHA); sPSSData->wallpaperChangeState++; } break; case 3: if (!UpdatePaletteFade()) sPSSData->wallpaperChangeState++; break; case 4: return FALSE; } return TRUE; } static void LoadWallpaperGfx(u8 boxId, s8 direction) { u8 wallpaperId; const struct Wallpaper *wallpaper; void *iconGfx; u32 tilesSize, iconSize; sPSSData->wallpaperLoadState = 0; sPSSData->wallpaperLoadBoxId = boxId; sPSSData->wallpaperLoadDir = direction; if (sPSSData->wallpaperLoadDir != 0) { sPSSData->wallpaperOffset = (sPSSData->wallpaperOffset == 0); TrimOldWallpaper(sPSSData->wallpaperBgTilemapBuffer); } wallpaperId = GetBoxWallpaper(sPSSData->wallpaperLoadBoxId); if (wallpaperId != WALLPAPER_FRIENDS) { wallpaper = &sWallpapers[wallpaperId]; LZ77UnCompWram(wallpaper->tilemap, sPSSData->wallpaperTilemap); DrawWallpaper(sPSSData->wallpaperTilemap, sPSSData->wallpaperLoadDir, sPSSData->wallpaperOffset); if (sPSSData->wallpaperLoadDir != 0) LoadPalette(wallpaper->palettes, (sPSSData->wallpaperOffset * 32) + 0x40, 0x40); else CpuCopy16(wallpaper->palettes, &gPlttBufferUnfaded[(sPSSData->wallpaperOffset * 32) + 0x40], 0x40); sPSSData->wallpaperTiles = malloc_and_decompress(wallpaper->tiles, &tilesSize); LoadBgTiles(2, sPSSData->wallpaperTiles, tilesSize, sPSSData->wallpaperOffset << 8); } else { wallpaper = &sWaldaWallpapers[GetWaldaWallpaperPatternId()]; LZ77UnCompWram(wallpaper->tilemap, sPSSData->wallpaperTilemap); DrawWallpaper(sPSSData->wallpaperTilemap, sPSSData->wallpaperLoadDir, sPSSData->wallpaperOffset); CpuCopy16(wallpaper->palettes, sPSSData->wallpaperTilemap, 0x40); CpuCopy16(GetWaldaWallpaperColorsPtr(), &sPSSData->wallpaperTilemap[1], 4); CpuCopy16(GetWaldaWallpaperColorsPtr(), &sPSSData->wallpaperTilemap[17], 4); if (sPSSData->wallpaperLoadDir != 0) LoadPalette(sPSSData->wallpaperTilemap, (sPSSData->wallpaperOffset * 32) + 0x40, 0x40); else CpuCopy16(sPSSData->wallpaperTilemap, &gPlttBufferUnfaded[(sPSSData->wallpaperOffset * 32) + 0x40], 0x40); sPSSData->wallpaperTiles = malloc_and_decompress(wallpaper->tiles, &tilesSize); iconGfx = malloc_and_decompress(sWaldaWallpaperIcons[GetWaldaWallpaperIconId()], &iconSize); CpuCopy32(iconGfx, sPSSData->wallpaperTiles + 0x800, iconSize); Free(iconGfx); LoadBgTiles(2, sPSSData->wallpaperTiles, tilesSize, sPSSData->wallpaperOffset << 8); } CopyBgTilemapBufferToVram(2); } static bool32 WaitForWallpaperGfxLoad(void) { if (IsDma3ManagerBusyWithBgCopy()) return FALSE; if (sPSSData->wallpaperTiles != NULL) FREE_AND_SET_NULL(sPSSData->wallpaperTiles); return TRUE; } static void DrawWallpaper(const void *tilemap, s8 direction, u8 offset) { s16 var = (offset * 2) + 3; s16 x = ((sPSSData->bg2_X / 8 + 10) + (direction * 24)) & 0x3F; CopyRectToBgTilemapBufferRect(2, tilemap, 0, 0, 0x14, 0x12, x, 2, 0x14, 0x12, 0x11, offset << 8, var); if (direction == 0) return; if (direction > 0) x *= 1, x += 0x14; // x * 1 is needed to match, but can be safely removed as it makes no functional difference else x -= 4; FillBgTilemapBufferRect(2, 0, x, 2, 4, 0x12, 0x11); } static void TrimOldWallpaper(void *tilemap) { u16 i; u16 *dest = tilemap; s16 r3 = ((sPSSData->bg2_X / 8) + 30) & 0x3F; if (r3 <= 31) dest += r3 + 0x260; else dest += r3 + 0x640; for (i = 0; i < 0x2C; i++) { *dest++ = 0; r3 = (r3 + 1) & 0x3F; if (r3 == 0) dest -= 0x420; if (r3 == 0x20) dest += 0x3e0; } } static void InitBoxTitle(u8 boxId) { u8 tagIndex; s16 x; u16 i; struct SpriteSheet spriteSheet = {sPSSData->boxTitleTiles, 0x200, GFXTAG_BOX_TITLE}; struct SpritePalette palettes[] = { {sPSSData->boxTitlePal, PALTAG_BOX_TITLE}, {} }; u16 wallpaperId = GetBoxWallpaper(boxId); sPSSData->boxTitlePal[14] = sBoxTitleColors[wallpaperId][0]; // Shadow color sPSSData->boxTitlePal[15] = sBoxTitleColors[wallpaperId][1]; // Text Color LoadSpritePalettes(palettes); sPSSData->boxTitlePalBits = 0x3f0; tagIndex = IndexOfSpritePaletteTag(PALTAG_BOX_TITLE); sPSSData->boxTitlePalOffset = 0x10e + 16 * tagIndex; sPSSData->boxTitlePalBits |= 0x10000 << tagIndex; // The below seems intended to have separately tracked // the incoming wallpaper title's palette, but as they now // share a palette tag, all colors (and fields in some cases) // this is redundant along with the use of boxTitleAltPalOffset tagIndex = IndexOfSpritePaletteTag(PALTAG_BOX_TITLE); sPSSData->boxTitleAltPalOffset = 0x10e + 16 * tagIndex; sPSSData->boxTitlePalBits |= 0x10000 << tagIndex; StringCopyPadded(sPSSData->boxTitleText, GetBoxNamePtr(boxId), 0, 8); DrawTextWindowAndBufferTiles(sPSSData->boxTitleText, sPSSData->boxTitleTiles, 0, 0, 2); LoadSpriteSheet(&spriteSheet); x = GetBoxTitleBaseX(GetBoxNamePtr(boxId)); // Title is split across two sprites for (i = 0; i < 2; i++) { u8 spriteId = CreateSprite(&sSpriteTemplate_BoxTitle, x + i * 32, 28, 24); sPSSData->curBoxTitleSprites[i] = &gSprites[spriteId]; StartSpriteAnim(sPSSData->curBoxTitleSprites[i], i); } sPSSData->boxTitleCycleId = 0; } // Sprite data for moving title text #define sSpeed data[0] // Flipped between incoming/outgoing for some reason #define sIncomingX data[1] #define sIncomingDelay data[2] #define sOutgoingDelay data[1] #define sOutgoingX data[2] static void CreateIncomingBoxTitle(u8 boxId, s8 direction) { u16 palOffset; s16 x, adjustedX; u16 i; struct SpriteSheet spriteSheet = {sPSSData->boxTitleTiles, 0x200, GFXTAG_BOX_TITLE}; struct SpriteTemplate template = sSpriteTemplate_BoxTitle; sPSSData->boxTitleCycleId = (sPSSData->boxTitleCycleId == 0); if (sPSSData->boxTitleCycleId == 0) { spriteSheet.tag = GFXTAG_BOX_TITLE; palOffset = sPSSData->boxTitlePalOffset; } else { spriteSheet.tag = GFXTAG_BOX_TITLE_ALT; palOffset = sPSSData->boxTitlePalOffset; template.tileTag = GFXTAG_BOX_TITLE_ALT; template.paletteTag = PALTAG_BOX_TITLE; } StringCopyPadded(sPSSData->boxTitleText, GetBoxNamePtr(boxId), 0, 8); DrawTextWindowAndBufferTiles(sPSSData->boxTitleText, sPSSData->boxTitleTiles, 0, 0, 2); LoadSpriteSheet(&spriteSheet); LoadPalette(sBoxTitleColors[GetBoxWallpaper(boxId)], palOffset, sizeof(sBoxTitleColors[0])); x = GetBoxTitleBaseX(GetBoxNamePtr(boxId)); adjustedX = x; adjustedX += direction * 192; // Title is split across two sprites for (i = 0; i < 2; i++) { u8 spriteId = CreateSprite(&template, i * 32 + adjustedX, 28, 24); sPSSData->nextBoxTitleSprites[i] = &gSprites[spriteId]; sPSSData->nextBoxTitleSprites[i]->sSpeed = (-direction) * 6; sPSSData->nextBoxTitleSprites[i]->sIncomingX = i * 32 + x; sPSSData->nextBoxTitleSprites[i]->sIncomingDelay = 0; sPSSData->nextBoxTitleSprites[i]->callback = SpriteCB_IncomingBoxTitle; StartSpriteAnim(sPSSData->nextBoxTitleSprites[i], i); sPSSData->curBoxTitleSprites[i]->sSpeed = (-direction) * 6; sPSSData->curBoxTitleSprites[i]->sOutgoingDelay = 1; sPSSData->curBoxTitleSprites[i]->callback = SpriteCB_OutgoingBoxTitle; } } static void CycleBoxTitleSprites(void) { if (sPSSData->boxTitleCycleId == 0) FreeSpriteTilesByTag(GFXTAG_BOX_TITLE_ALT); else FreeSpriteTilesByTag(GFXTAG_BOX_TITLE); sPSSData->curBoxTitleSprites[0] = sPSSData->nextBoxTitleSprites[0]; sPSSData->curBoxTitleSprites[1] = sPSSData->nextBoxTitleSprites[1]; } static void SpriteCB_IncomingBoxTitle(struct Sprite *sprite) { if (sprite->sIncomingDelay != 0) sprite->sIncomingDelay--; else if ((sprite->pos1.x += sprite->sSpeed) == sprite->sIncomingX) sprite->callback = SpriteCallbackDummy; } static void SpriteCB_OutgoingBoxTitle(struct Sprite *sprite) { if (sprite->sOutgoingDelay != 0) { sprite->sOutgoingDelay--; } else { sprite->pos1.x += sprite->sSpeed; sprite->sOutgoingX = sprite->pos1.x + sprite->pos2.x; if (sprite->sOutgoingX < 64 || sprite->sOutgoingX > DISPLAY_WIDTH + 16) DestroySprite(sprite); } } #undef sSpeed #undef sIncomingX #undef sIncomingDelay #undef sOutgoingDelay #undef sOutgoingX static void CycleBoxTitleColor(void) { u8 boxId = StorageGetCurrentBox(); u8 wallpaperId = GetBoxWallpaper(boxId); if (sPSSData->boxTitleCycleId == 0) CpuCopy16(sBoxTitleColors[wallpaperId], gPlttBufferUnfaded + sPSSData->boxTitlePalOffset, 4); else CpuCopy16(sBoxTitleColors[wallpaperId], gPlttBufferUnfaded + sPSSData->boxTitleAltPalOffset, 4); } static s16 GetBoxTitleBaseX(const u8 *string) { return DISPLAY_WIDTH - 64 - GetStringWidth(1, string, 0) / 2; } // Sprite data for box scroll arrows #define sState data[0] #define sTimer data[1] #define sSpeed data[3] static void CreateBoxScrollArrows(void) { u16 i; LoadSpriteSheet(&sSpriteSheet_Arrow); for (i = 0; i < 2; i++) { u8 spriteId = CreateSprite(&sSpriteTemplate_Arrow, 92 + i * 136, 28, 22); if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId]; StartSpriteAnim(sprite, i); sprite->sSpeed = (i == 0) ? -1 : 1; sPSSData->arrowSprites[i] = sprite; } } if (IsCursorOnBoxTitle()) AnimateBoxScrollArrows(TRUE); } // Slide box scroll arrows horizontally for box change static void StartBoxScrollArrowsSlide(s8 direction) { u16 i; for (i = 0; i < 2; i++) { sPSSData->arrowSprites[i]->pos2.x = 0; sPSSData->arrowSprites[i]->sState = 2; } if (direction < 0) { sPSSData->arrowSprites[0]->sTimer = 29; sPSSData->arrowSprites[1]->sTimer = 5; sPSSData->arrowSprites[0]->data[2] = 72; sPSSData->arrowSprites[1]->data[2] = 72; } else { sPSSData->arrowSprites[0]->sTimer = 5; sPSSData->arrowSprites[1]->sTimer = 29; sPSSData->arrowSprites[0]->data[2] = DISPLAY_WIDTH + 8; sPSSData->arrowSprites[1]->data[2] = DISPLAY_WIDTH + 8; } sPSSData->arrowSprites[0]->data[7] = 0; sPSSData->arrowSprites[1]->data[7] = 1; } // New box's scroll arrows have entered, stop sliding and set their position static void StopBoxScrollArrowsSlide(void) { u16 i; for (i = 0; i < 2; i++) { sPSSData->arrowSprites[i]->pos1.x = 136 * i + 92; sPSSData->arrowSprites[i]->pos2.x = 0; sPSSData->arrowSprites[i]->invisible = FALSE; } AnimateBoxScrollArrows(TRUE); } // Bounce scroll arrows while title is selected static void AnimateBoxScrollArrows(bool8 animate) { u16 i; if (animate) { // Start arrows moving for (i = 0; i < 2; i++) { sPSSData->arrowSprites[i]->sState = 1; sPSSData->arrowSprites[i]->sTimer = 0; sPSSData->arrowSprites[i]->data[2] = 0; sPSSData->arrowSprites[i]->data[4] = 0; } } else { // Stop arrows moving for (i = 0; i < 2; i++) sPSSData->arrowSprites[i]->sState = 0; } } static void SpriteCB_Arrow(struct Sprite *sprite) { switch (sprite->sState) { case 0: sprite->pos2.x = 0; break; case 1: if (++sprite->sTimer > 3) { sprite->sTimer = 0; sprite->pos2.x += sprite->sSpeed; if (++sprite->data[2] > 5) { sprite->data[2] = 0; sprite->pos2.x = 0; } } break; case 2: sprite->sState = 3; break; case 3: sprite->pos1.x -= sPSSData->scrollSpeed; if (sprite->pos1.x <= 72 || sprite->pos1.x >= DISPLAY_WIDTH + 8) sprite->invisible = TRUE; if (--sprite->sTimer == 0) { sprite->pos1.x = sprite->data[2]; sprite->invisible = FALSE; sprite->sState = 4; } break; case 4: sprite->pos1.x -= sPSSData->scrollSpeed; break; } } #undef sState #undef sSpeed // Arrows for Deposit/Jump Box selection static struct Sprite *CreateChooseBoxArrows(u16 x, u16 y, u8 animId, u8 priority, u8 subpriority) { u8 spriteId = CreateSprite(&sSpriteTemplate_Arrow, x, y, subpriority); if (spriteId == MAX_SPRITES) return NULL; animId %= 2; StartSpriteAnim(&gSprites[spriteId], animId); gSprites[spriteId].oam.priority = priority; gSprites[spriteId].callback = SpriteCallbackDummy; return &gSprites[spriteId]; } static void sub_80CD36C(void) { if (sPSSData->boxOption != OPTION_DEPOSIT) sCursorArea = CURSOR_AREA_IN_BOX; else sCursorArea = CURSOR_AREA_IN_PARTY; sCursorPosition = 0; sIsMonBeingMoved = FALSE; sMovingMonOrigBoxId = 0; sMovingMonOrigBoxPos = 0; sAutoActionOn = FALSE; ClearSavedCursorPos(); CreateCursorSprites(); sPSSData->cursorPrevHorizPos = 1; sPSSData->inBoxMovingMode = MOVE_MODE_NORMAL; TryRefreshDisplayMon(); } static void sub_80CD3EC(void) { CreateCursorSprites(); ReshowDisplayMon(); sPSSData->cursorPrevHorizPos = 1; sPSSData->inBoxMovingMode = MOVE_MODE_NORMAL; if (sIsMonBeingMoved) { sPSSData->movingMon = sSavedMovingMon; CreateMovingMonIcon(); } } static void GetCursorCoordsByPos(u8 cursorArea, u8 cursorPosition, u16 *x, u16 *y) { switch (cursorArea) { case CURSOR_AREA_IN_BOX: *x = (cursorPosition % IN_BOX_COLUMNS) * 24 + 100; *y = (cursorPosition / IN_BOX_COLUMNS) * 24 + 32; break; case CURSOR_AREA_IN_PARTY: if (cursorPosition == 0) { *x = 104; *y = 52; } else if (cursorPosition == PARTY_SIZE) { *x = 152; *y = 132; } else { *x = 152; *y = (cursorPosition - 1) * 24 + 4; } break; case CURSOR_AREA_BOX_TITLE: *x = 162; *y = 12; break; case CURSOR_AREA_BUTTONS: *y = sIsMonBeingMoved ? 8 : 14; *x = cursorPosition * 88 + 120; break; case 4: *x = 160; *y = 96; break; } } static u16 GetSpeciesAtCursorPosition(void) { switch (sCursorArea) { case CURSOR_AREA_IN_PARTY: return GetMonData(&gPlayerParty[sCursorPosition], MON_DATA_SPECIES); case CURSOR_AREA_IN_BOX: return GetCurrentBoxMonData(sCursorPosition, MON_DATA_SPECIES); default: return SPECIES_NONE; } } static bool8 UpdateCursorPos(void) { s16 tmp; if (sPSSData->cursorMoveSteps == 0) { if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return FALSE; else return IsItemIconAnimActive(); } else if (--sPSSData->cursorMoveSteps != 0) { // Update position toward target sPSSData->cursorNewX += sPSSData->cursorSpeedX; sPSSData->cursorNewY += sPSSData->cursorSpeedY; sPSSData->cursorSprite->pos1.x = sPSSData->cursorNewX >> 8; sPSSData->cursorSprite->pos1.y = sPSSData->cursorNewY >> 8; // Limit cursor on right if (sPSSData->cursorSprite->pos1.x > DISPLAY_WIDTH + 16) { tmp = sPSSData->cursorSprite->pos1.x - (DISPLAY_WIDTH + 16); sPSSData->cursorSprite->pos1.x = tmp + 64; } // Limit cursor on left if (sPSSData->cursorSprite->pos1.x < 64) { tmp = 64 - sPSSData->cursorSprite->pos1.x; sPSSData->cursorSprite->pos1.x = DISPLAY_WIDTH + 16 - tmp; } // Limit cursor on bottom if (sPSSData->cursorSprite->pos1.y > DISPLAY_HEIGHT + 16) { tmp = sPSSData->cursorSprite->pos1.y - (DISPLAY_HEIGHT + 16); sPSSData->cursorSprite->pos1.y = tmp - 16; } // Limit cursor on top if (sPSSData->cursorSprite->pos1.y < -16) { tmp = -16 - sPSSData->cursorSprite->pos1.y; sPSSData->cursorSprite->pos1.y = DISPLAY_HEIGHT + 16 - tmp; } // Cursor flips vertically when moving on/off the top buttons if (sPSSData->cursorFlipTimer && --sPSSData->cursorFlipTimer == 0) sPSSData->cursorSprite->vFlip = (sPSSData->cursorSprite->vFlip == FALSE); } else { // Time is up for cursor movement, make sure it's exactly at target sPSSData->cursorSprite->pos1.x = sPSSData->cursorTargetX; sPSSData->cursorSprite->pos1.y = sPSSData->cursorTargetY; DoCursorNewPosUpdate(); } return TRUE; } static void InitNewCursorPos(u8 newCursorArea, u8 newCursorPosition) { u16 x, y; GetCursorCoordsByPos(newCursorArea, newCursorPosition, &x, &y); sPSSData->newCursorArea = newCursorArea; sPSSData->newCursorPosition = newCursorPosition; sPSSData->cursorTargetX = x; sPSSData->cursorTargetY = y; } static void InitCursorMove(void) { int yDistance, xDistance; if (sPSSData->cursorVerticalWrap != 0 || sPSSData->cursorHorizontalWrap != 0) sPSSData->cursorMoveSteps = 12; else sPSSData->cursorMoveSteps = 6; if (sPSSData->cursorFlipTimer) sPSSData->cursorFlipTimer = sPSSData->cursorMoveSteps >> 1; switch (sPSSData->cursorVerticalWrap) { default: // No wrap yDistance = sPSSData->cursorTargetY - sPSSData->cursorSprite->pos1.y; break; case -1: // Wrap from top to bottom yDistance = sPSSData->cursorTargetY - 192 - sPSSData->cursorSprite->pos1.y; break; case 1: // Wrap from bottom to top yDistance = sPSSData->cursorTargetY + 192 - sPSSData->cursorSprite->pos1.y; break; } switch (sPSSData->cursorHorizontalWrap) { default: // No Wrap xDistance = sPSSData->cursorTargetX - sPSSData->cursorSprite->pos1.x; break; case -1: // Wrap from left to right xDistance = sPSSData->cursorTargetX - 192 - sPSSData->cursorSprite->pos1.x; break; case 1: // Wrap from right to left xDistance = sPSSData->cursorTargetX + 192 - sPSSData->cursorSprite->pos1.x; break; } yDistance <<= 8; xDistance <<= 8; sPSSData->cursorSpeedX = xDistance / sPSSData->cursorMoveSteps; sPSSData->cursorSpeedY = yDistance / sPSSData->cursorMoveSteps; sPSSData->cursorNewX = sPSSData->cursorSprite->pos1.x << 8; sPSSData->cursorNewY = sPSSData->cursorSprite->pos1.y << 8; } static void SetCursorPosition(u8 newCursorArea, u8 newCursorPosition) { InitNewCursorPos(newCursorArea, newCursorPosition); InitCursorMove(); if (sPSSData->boxOption != OPTION_MOVE_ITEMS) { if (sPSSData->inBoxMovingMode == MOVE_MODE_NORMAL && !sIsMonBeingMoved) StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_STILL); } else { if (!IsMovingItem()) StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_STILL); } if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { if (sCursorArea == CURSOR_AREA_IN_BOX) TryHideItemIconAtPos(CURSOR_AREA_IN_BOX, sCursorPosition); else if (sCursorArea == CURSOR_AREA_IN_PARTY) TryHideItemIconAtPos(CURSOR_AREA_IN_PARTY, sCursorPosition); if (newCursorArea == CURSOR_AREA_IN_BOX) TryLoadItemIconAtPos(newCursorArea, newCursorPosition); else if (newCursorArea == CURSOR_AREA_IN_PARTY) TryLoadItemIconAtPos(newCursorArea, newCursorPosition); } if (newCursorArea == CURSOR_AREA_IN_PARTY && sCursorArea != CURSOR_AREA_IN_PARTY) { sPSSData->cursorPrevHorizPos = 1; sPSSData->cursorShadowSprite->invisible = TRUE; } switch (newCursorArea) { case CURSOR_AREA_IN_PARTY: case CURSOR_AREA_BOX_TITLE: case CURSOR_AREA_BUTTONS: sPSSData->cursorSprite->oam.priority = 1; sPSSData->cursorShadowSprite->invisible = TRUE; sPSSData->cursorShadowSprite->oam.priority = 1; break; case CURSOR_AREA_IN_BOX: if (sPSSData->inBoxMovingMode != MOVE_MODE_NORMAL) { sPSSData->cursorSprite->oam.priority = 0; sPSSData->cursorShadowSprite->invisible = TRUE; } else { sPSSData->cursorSprite->oam.priority = 2; if (sCursorArea == CURSOR_AREA_IN_BOX && sIsMonBeingMoved) SetMovingMonPriority(2); } break; } } static void DoCursorNewPosUpdate(void) { sCursorArea = sPSSData->newCursorArea; sCursorPosition = sPSSData->newCursorPosition; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) { if (sPSSData->inBoxMovingMode == MOVE_MODE_NORMAL && !sIsMonBeingMoved) StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_BOUNCE); } else { if (!IsMovingItem()) StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_BOUNCE); } TryRefreshDisplayMon(); switch (sCursorArea) { case CURSOR_AREA_BUTTONS: SetMovingMonPriority(1); break; case CURSOR_AREA_BOX_TITLE: AnimateBoxScrollArrows(TRUE); break; case CURSOR_AREA_IN_PARTY: sPSSData->cursorShadowSprite->subpriority = 13; SetMovingMonPriority(1); break; case CURSOR_AREA_IN_BOX: if (sPSSData->inBoxMovingMode == MOVE_MODE_NORMAL) { sPSSData->cursorSprite->oam.priority = 1; sPSSData->cursorShadowSprite->oam.priority = 2; sPSSData->cursorShadowSprite->subpriority = 21; sPSSData->cursorShadowSprite->invisible = FALSE; SetMovingMonPriority(2); } break; } } static void SetCursorInParty(void) { u8 partyCount; if (!sIsMonBeingMoved) { partyCount = 0; } else { partyCount = CalculatePlayerPartyCount(); if (partyCount >= PARTY_SIZE) partyCount = PARTY_SIZE - 1; } if (sPSSData->cursorSprite->vFlip) sPSSData->cursorFlipTimer = 1; SetCursorPosition(CURSOR_AREA_IN_PARTY, partyCount); } static void SetCursorBoxPosition(u8 cursorBoxPosition) { SetCursorPosition(CURSOR_AREA_IN_BOX, cursorBoxPosition); } EWRAM_DATA static u8 sSavedCursorPosition = 0; static void ClearSavedCursorPos(void) { sSavedCursorPosition = 0; } static void SaveCursorPos(void) { sSavedCursorPosition = sCursorPosition; } static u8 GetSavedCursorPos(void) { return sSavedCursorPosition; } static void InitMonPlaceChange(u8 type) { static bool8 (*const placeChangeFuncs[])(void) = { [CHANGE_GRAB] = MonPlaceChange_Grab, [CHANGE_PLACE] = MonPlaceChange_Place, [CHANGE_SHIFT] = MonPlaceChange_Shift, }; sPSSData->monPlaceChangeFunc = placeChangeFuncs[type]; sPSSData->monPlaceChangeState = 0; } // No Shift while moving multiple Pokémon, only grab and place // For both grab/place, the cursor moves down, then up static void InitMultiMonPlaceChange(bool8 up) { if (!up) sPSSData->monPlaceChangeFunc = MultiMonPlaceChange_Down; else sPSSData->monPlaceChangeFunc = MultiMonPlaceChange_Up; sPSSData->monPlaceChangeState = 0; } static bool8 DoMonPlaceChange(void) { return sPSSData->monPlaceChangeFunc(); } static bool8 MonPlaceChange_Grab(void) { switch (sPSSData->monPlaceChangeState) { case 0: if (sIsMonBeingMoved) return FALSE; StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_OPEN); sPSSData->monPlaceChangeState++; break; case 1: if (!MonPlaceChange_CursorDown()) { StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_FIST); MoveMon(); sPSSData->monPlaceChangeState++; } break; case 2: if (!MonPlaceChange_CursorUp()) sPSSData->monPlaceChangeState++; break; case 3: return FALSE; } return TRUE; } static bool8 MonPlaceChange_Place(void) { switch (sPSSData->monPlaceChangeState) { case 0: if (!MonPlaceChange_CursorDown()) { StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_OPEN); PlaceMon(); sPSSData->monPlaceChangeState++; } break; case 1: if (!MonPlaceChange_CursorUp()) { StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_BOUNCE); sPSSData->monPlaceChangeState++; } break; case 2: return FALSE; } return TRUE; } static bool8 MonPlaceChange_Shift(void) { switch (sPSSData->monPlaceChangeState) { case 0: switch (sCursorArea) { case CURSOR_AREA_IN_PARTY: sPSSData->field_D91 = TOTAL_BOXES_COUNT; break; case CURSOR_AREA_IN_BOX: sPSSData->field_D91 = StorageGetCurrentBox(); break; default: return FALSE; } StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_OPEN); sub_80CBD5C(sPSSData->field_D91, sCursorPosition); sPSSData->monPlaceChangeState++; break; case 1: if (!sub_80CBDC4()) { StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_FIST); SetShiftedMonData(sPSSData->field_D91, sCursorPosition); sPSSData->monPlaceChangeState++; } break; case 2: return FALSE; } return TRUE; } static bool8 MultiMonPlaceChange_Down(void) { return MonPlaceChange_CursorDown(); } static bool8 MultiMonPlaceChange_Up(void) { return MonPlaceChange_CursorUp(); } static bool8 MonPlaceChange_CursorDown(void) { switch (sPSSData->cursorSprite->pos2.y) { default: sPSSData->cursorSprite->pos2.y++; break; case 0: sPSSData->cursorSprite->pos2.y++; break; case 8: // Cursor has reached bottom return FALSE; } return TRUE; } static bool8 MonPlaceChange_CursorUp(void) { switch (sPSSData->cursorSprite->pos2.y) { case 0: // Cursor has reached top return FALSE; default: sPSSData->cursorSprite->pos2.y--; break; } return TRUE; } static void MoveMon(void) { switch (sCursorArea) { case CURSOR_AREA_IN_PARTY: SetMovingMonData(TOTAL_BOXES_COUNT, sCursorPosition); SetMovingMonSprite(MODE_PARTY, sCursorPosition); break; case CURSOR_AREA_IN_BOX: if (sPSSData->inBoxMovingMode == MOVE_MODE_NORMAL) { SetMovingMonData(StorageGetCurrentBox(), sCursorPosition); SetMovingMonSprite(MODE_BOX, sCursorPosition); } break; default: return; } sIsMonBeingMoved = TRUE; } static void PlaceMon(void) { u8 boxId; switch (sCursorArea) { case CURSOR_AREA_IN_PARTY: SetPlacedMonData(TOTAL_BOXES_COUNT, sCursorPosition); sub_80CBCAC(TOTAL_BOXES_COUNT, sCursorPosition); break; case CURSOR_AREA_IN_BOX: boxId = StorageGetCurrentBox(); SetPlacedMonData(boxId, sCursorPosition); sub_80CBCAC(boxId, sCursorPosition); break; default: return; } sIsMonBeingMoved = FALSE; } static void RefreshDisplayMon(void) { TryRefreshDisplayMon(); } static void SetMovingMonData(u8 boxId, u8 position) { if (boxId == TOTAL_BOXES_COUNT) sPSSData->movingMon = gPlayerParty[sCursorPosition]; else BoxMonAtToMon(boxId, position, &sPSSData->movingMon); PurgeMonOrBoxMon(boxId, position); sMovingMonOrigBoxId = boxId; sMovingMonOrigBoxPos = position; } static void SetPlacedMonData(u8 boxId, u8 position) { if (boxId == TOTAL_BOXES_COUNT) { gPlayerParty[position] = sPSSData->movingMon; } else { BoxMonRestorePP(&sPSSData->movingMon.box); SetBoxMonAt(boxId, position, &sPSSData->movingMon.box); } } static void PurgeMonOrBoxMon(u8 boxId, u8 position) { if (boxId == TOTAL_BOXES_COUNT) ZeroMonData(&gPlayerParty[position]); else ZeroBoxMonAt(boxId, position); } static void SetShiftedMonData(u8 boxId, u8 position) { if (boxId == TOTAL_BOXES_COUNT) sPSSData->tempMon = gPlayerParty[position]; else BoxMonAtToMon(boxId, position, &sPSSData->tempMon); SetPlacedMonData(boxId, position); sPSSData->movingMon = sPSSData->tempMon; SetDisplayMonData(&sPSSData->movingMon, MODE_PARTY); sMovingMonOrigBoxId = boxId; sMovingMonOrigBoxPos = position; } static bool8 TryStorePartyMonInBox(u8 boxId) { s16 boxPosition = GetFirstFreeBoxSpot(boxId); if (boxPosition == -1) return FALSE; if (sIsMonBeingMoved) { SetPlacedMonData(boxId, boxPosition); DestroyMovingMonIcon(); sIsMonBeingMoved = FALSE; } else { SetMovingMonData(TOTAL_BOXES_COUNT, sCursorPosition); SetPlacedMonData(boxId, boxPosition); DestroyPartyMonIcon(sCursorPosition); } if (boxId == StorageGetCurrentBox()) sub_80CB140(boxPosition); StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_STILL); return TRUE; } static void sub_80CE22C(void) { StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_BOUNCE); TryRefreshDisplayMon(); } static void InitReleaseMon(void) { u8 mode; if (sIsMonBeingMoved) mode = MODE_MOVE; else if (sCursorArea == CURSOR_AREA_IN_PARTY) mode = MODE_PARTY; else mode = MODE_BOX; SetReleaseMon(mode, sCursorPosition); StringCopy(sPSSData->releaseMonName, sPSSData->displayMonName); } static bool8 TryHideReleaseMon(void) { if (!TryHideReleaseMonSprite()) { StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_BOUNCE); return FALSE; } else { return TRUE; } } static void ReleaseMon(void) { u8 boxId; DestroyReleaseMonIcon(); if (sIsMonBeingMoved) { sIsMonBeingMoved = FALSE; } else { if (sCursorArea == CURSOR_AREA_IN_PARTY) boxId = TOTAL_BOXES_COUNT; else boxId = StorageGetCurrentBox(); PurgeMonOrBoxMon(boxId, sCursorPosition); } TryRefreshDisplayMon(); } static void TrySetCursorFistAnim(void) { if (sIsMonBeingMoved) StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_FIST); } // If the player is on the listed map (or any map, if none is specified), // they may not release their last Pokémon that knows the specified move. // This is to stop the player from softlocking themselves by not having // a Pokémon that knows a required field move. struct { s8 mapGroup; s8 mapNum; u16 move; } static const sRestrictedReleaseMoves[] = { {MAP_GROUPS_COUNT, 0, MOVE_SURF}, {MAP_GROUPS_COUNT, 0, MOVE_DIVE}, {MAP_GROUP(EVER_GRANDE_CITY_POKEMON_LEAGUE_1F), MAP_NUM(EVER_GRANDE_CITY_POKEMON_LEAGUE_1F), MOVE_STRENGTH}, {MAP_GROUP(EVER_GRANDE_CITY_POKEMON_LEAGUE_1F), MAP_NUM(EVER_GRANDE_CITY_POKEMON_LEAGUE_1F), MOVE_ROCK_SMASH}, {MAP_GROUP(EVER_GRANDE_CITY_POKEMON_LEAGUE_2F), MAP_NUM(EVER_GRANDE_CITY_POKEMON_LEAGUE_2F), MOVE_STRENGTH}, {MAP_GROUP(EVER_GRANDE_CITY_POKEMON_LEAGUE_2F), MAP_NUM(EVER_GRANDE_CITY_POKEMON_LEAGUE_2F), MOVE_ROCK_SMASH}, }; static void GetRestrictedReleaseMoves(u16 *moves) { s32 i; for (i = 0; i < ARRAY_COUNT(sRestrictedReleaseMoves); i++) { if (sRestrictedReleaseMoves[i].mapGroup == MAP_GROUPS_COUNT || (sRestrictedReleaseMoves[i].mapGroup == gSaveBlock1Ptr->location.mapGroup && sRestrictedReleaseMoves[i].mapNum == gSaveBlock1Ptr->location.mapNum)) { *moves = sRestrictedReleaseMoves[i].move; moves++; } } *moves = MOVES_COUNT; } static void InitCanReleaseMonVars(void) { if (!AtLeastThreeUsableMons()) { // The player only has 1 or 2 usable // Pokémon, this one can't be released sPSSData->releaseStatusResolved = TRUE; sPSSData->canReleaseMon = FALSE; return; } if (sIsMonBeingMoved) { sPSSData->tempMon = sPSSData->movingMon; sPSSData->releaseBoxId = -1; sPSSData->releaseBoxPos = -1; } else { if (sCursorArea == CURSOR_AREA_IN_PARTY) { sPSSData->tempMon = gPlayerParty[sCursorPosition]; sPSSData->releaseBoxId = TOTAL_BOXES_COUNT; } else { BoxMonAtToMon(StorageGetCurrentBox(), sCursorPosition, &sPSSData->tempMon); sPSSData->releaseBoxId = StorageGetCurrentBox(); } sPSSData->releaseBoxPos = sCursorPosition; } GetRestrictedReleaseMoves(sPSSData->restrictedMoveList); sPSSData->restrictedReleaseMonMoves = GetMonData(&sPSSData->tempMon, MON_DATA_KNOWN_MOVES, (u8*)sPSSData->restrictedMoveList); if (sPSSData->restrictedReleaseMonMoves != 0) { // Pokémon knows at least one restricted release move // Need to check if another Pokémon has this move first sPSSData->releaseStatusResolved = FALSE; } else { // Pokémon knows no restricted moves, can be released sPSSData->releaseStatusResolved = TRUE; sPSSData->canReleaseMon = TRUE; } sPSSData->releaseCheckState = 0; } static bool32 AtLeastThreeUsableMons(void) { s32 i, j; s32 count = (sIsMonBeingMoved != FALSE); // Check party for usable Pokémon for (j = 0; j < PARTY_SIZE; j++) { if (GetMonData(&gPlayerParty[j], MON_DATA_SANITY_HAS_SPECIES)) count++; } if (count >= 3) return TRUE; // Check PC for usable Pokémon for (i = 0; i < TOTAL_BOXES_COUNT; i++) { for (j = 0; j < IN_BOX_COUNT; j++) { if (CheckBoxMonSanityAt(i, j)) { if (++count >= 3) return TRUE; } } } return FALSE; } static s8 RunCanReleaseMon(void) { u16 i; u16 knownMoves; if (sPSSData->releaseStatusResolved) return sPSSData->canReleaseMon; switch (sPSSData->releaseCheckState) { case 0: // Check party for other Pokémon that know any restricted // moves the release Pokémon knows for (i = 0; i < PARTY_SIZE; i++) { // Make sure party Pokémon isn't the one we're releasing first if (sPSSData->releaseBoxId != TOTAL_BOXES_COUNT || sPSSData->releaseBoxPos != i) { knownMoves = GetMonData(&gPlayerParty[i], MON_DATA_KNOWN_MOVES, (u8*)sPSSData->restrictedMoveList); sPSSData->restrictedReleaseMonMoves &= ~(knownMoves); } } if (sPSSData->restrictedReleaseMonMoves == 0) { // No restricted moves on release Pokémon that // aren't resolved by the party, it can be released. sPSSData->releaseStatusResolved = TRUE; sPSSData->canReleaseMon = TRUE; } else { // Release Pokémon has restricted moves not resolved by the party. // Continue and check the PC next sPSSData->releaseCheckBoxId = 0; sPSSData->releaseCheckBoxPos = 0; sPSSData->releaseCheckState++; } break; case 1: // Check PC for other Pokémon that know any restricted // moves the release Pokémon knows for (i = 0; i < IN_BOX_COUNT; i++) { knownMoves = GetAndCopyBoxMonDataAt(sPSSData->releaseCheckBoxId, sPSSData->releaseCheckBoxPos, MON_DATA_KNOWN_MOVES, (u8*)sPSSData->restrictedMoveList); if (knownMoves != 0 && !(sPSSData->releaseBoxId == sPSSData->releaseCheckBoxId && sPSSData->releaseBoxPos == sPSSData->releaseCheckBoxPos)) { // Found PC Pokémon with restricted move, clear move from list sPSSData->restrictedReleaseMonMoves &= ~(knownMoves); if (sPSSData->restrictedReleaseMonMoves == 0) { // No restricted moves on release Pokémon that // aren't resolved, it can be released. sPSSData->releaseStatusResolved = TRUE; sPSSData->canReleaseMon = TRUE; break; } } if (++sPSSData->releaseCheckBoxPos >= IN_BOX_COUNT) { sPSSData->releaseCheckBoxPos = 0; if (++sPSSData->releaseCheckBoxId >= TOTAL_BOXES_COUNT) { // Checked every Pokémon in the PC, release Pokémon is // the sole owner of at least one restricted move. // It cannot be released. sPSSData->releaseStatusResolved = TRUE; sPSSData->canReleaseMon = FALSE; } } } break; } return -1; } static void SaveMovingMon(void) { if (sIsMonBeingMoved) sSavedMovingMon = sPSSData->movingMon; } static void LoadSavedMovingMon(void) { if (sIsMonBeingMoved) { // If it came from the party load a struct Pokemon, // otherwise load a BoxPokemon if (sMovingMonOrigBoxId == TOTAL_BOXES_COUNT) sPSSData->movingMon = sSavedMovingMon; else sPSSData->movingMon.box = sSavedMovingMon.box; } } static void InitSummaryScreenData(void) { if (sIsMonBeingMoved) { SaveMovingMon(); sPSSData->summaryMon.mon = &sSavedMovingMon; sPSSData->summaryStartPos = 0; sPSSData->summaryMaxPos = 0; sPSSData->summaryScreenMode = SUMMARY_MODE_NORMAL; } else if (sCursorArea == CURSOR_AREA_IN_PARTY) { sPSSData->summaryMon.mon = gPlayerParty; sPSSData->summaryStartPos = sCursorPosition; sPSSData->summaryMaxPos = CountPartyMons() - 1; sPSSData->summaryScreenMode = SUMMARY_MODE_NORMAL; } else { sPSSData->summaryMon.box = GetBoxedMonPtr(StorageGetCurrentBox(), 0); sPSSData->summaryStartPos = sCursorPosition; sPSSData->summaryMaxPos = IN_BOX_COUNT - 1; sPSSData->summaryScreenMode = SUMMARY_MODE_BOX; } } static void sub_80CE8E4(void) { if (sIsMonBeingMoved) LoadSavedMovingMon(); else sCursorPosition = gLastViewedMonIndex; } s16 CompactPartySlots(void) { s16 retVal = -1; u16 i, last; for (i = 0, last = 0; i < PARTY_SIZE; i++) { u16 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES); if (species != SPECIES_NONE) { if (i != last) gPlayerParty[last] = gPlayerParty[i]; last++; } else if (retVal == -1) { retVal = i; } } for (; last < PARTY_SIZE; last++) ZeroMonData(&gPlayerParty[last]); return retVal; } static void SetMonMarkings(u8 markings) { sPSSData->displayMonMarkings = markings; if (sIsMonBeingMoved) { SetMonData(&sPSSData->movingMon, MON_DATA_MARKINGS, &markings); } else { if (sCursorArea == CURSOR_AREA_IN_PARTY) SetMonData(&gPlayerParty[sCursorPosition], MON_DATA_MARKINGS, &markings); if (sCursorArea == CURSOR_AREA_IN_BOX) SetCurrentBoxMonData(sCursorPosition, MON_DATA_MARKINGS, &markings); } } static bool8 IsRemovingLastPartyMon(void) { if (sCursorArea == CURSOR_AREA_IN_PARTY && !sIsMonBeingMoved && CountPartyAliveNonEggMonsExcept(sCursorPosition) == 0) return TRUE; else return FALSE; } static bool8 CanShiftMon(void) { if (sIsMonBeingMoved) { if (sCursorArea == CURSOR_AREA_IN_PARTY && CountPartyAliveNonEggMonsExcept(sCursorPosition) == 0) { if (sPSSData->displayMonIsEgg || GetMonData(&sPSSData->movingMon, MON_DATA_HP) == 0) return FALSE; } return TRUE; } return FALSE; } static bool8 IsMonBeingMoved(void) { return sIsMonBeingMoved; } static bool8 IsCursorOnBoxTitle(void) { return (sCursorArea == CURSOR_AREA_BOX_TITLE); } static bool8 IsCursorOnCloseBox(void) { return (sCursorArea == CURSOR_AREA_BUTTONS && sCursorPosition == 1); } static bool8 IsCursorInBox(void) { return (sCursorArea == CURSOR_AREA_IN_BOX); } static void TryRefreshDisplayMon(void) { // If a Pokémon is currently being moved, don't start // mosaic or update display. Keep displaying the // currently held Pokémon. sPSSData->setMosaic = (sIsMonBeingMoved == FALSE); if (!sIsMonBeingMoved) { // Update display Pokémon switch (sCursorArea) { case CURSOR_AREA_IN_PARTY: if (sCursorPosition < PARTY_SIZE) { SetDisplayMonData(&gPlayerParty[sCursorPosition], MODE_PARTY); break; } // fallthrough case CURSOR_AREA_BUTTONS: case CURSOR_AREA_BOX_TITLE: SetDisplayMonData(NULL, MODE_MOVE); break; case CURSOR_AREA_IN_BOX: SetDisplayMonData(GetBoxedMonPtr(StorageGetCurrentBox(), sCursorPosition), MODE_BOX); break; } } } static void ReshowDisplayMon(void) { if (sIsMonBeingMoved) SetDisplayMonData(&sSavedMovingMon, MODE_PARTY); else TryRefreshDisplayMon(); } static void SetDisplayMonData(void *pokemon, u8 mode) { u8 *txtPtr; u16 gender; bool8 sanityIsBadEgg; sPSSData->displayMonItemId = ITEM_NONE; gender = MON_MALE; sanityIsBadEgg = FALSE; if (mode == MODE_PARTY) { struct Pokemon *mon = (struct Pokemon *)pokemon; sPSSData->displayMonSpecies = GetMonData(mon, MON_DATA_SPECIES2); if (sPSSData->displayMonSpecies != SPECIES_NONE) { sanityIsBadEgg = GetMonData(mon, MON_DATA_SANITY_IS_BAD_EGG); if (sanityIsBadEgg) sPSSData->displayMonIsEgg = TRUE; else sPSSData->displayMonIsEgg = GetMonData(mon, MON_DATA_IS_EGG); GetMonData(mon, MON_DATA_NICKNAME, sPSSData->displayMonName); StringGetEnd10(sPSSData->displayMonName); sPSSData->displayMonLevel = GetMonData(mon, MON_DATA_LEVEL); sPSSData->displayMonMarkings = GetMonData(mon, MON_DATA_MARKINGS); sPSSData->displayMonPersonality = GetMonData(mon, MON_DATA_PERSONALITY); sPSSData->displayMonPalette = GetMonFrontSpritePal(mon); gender = GetMonGender(mon); sPSSData->displayMonItemId = GetMonData(mon, MON_DATA_HELD_ITEM); } } else if (mode == MODE_BOX) { struct BoxPokemon *boxMon = (struct BoxPokemon *)pokemon; sPSSData->displayMonSpecies = GetBoxMonData(pokemon, MON_DATA_SPECIES2); if (sPSSData->displayMonSpecies != SPECIES_NONE) { u32 otId = GetBoxMonData(boxMon, MON_DATA_OT_ID); sanityIsBadEgg = GetBoxMonData(boxMon, MON_DATA_SANITY_IS_BAD_EGG); if (sanityIsBadEgg) sPSSData->displayMonIsEgg = TRUE; else sPSSData->displayMonIsEgg = GetBoxMonData(boxMon, MON_DATA_IS_EGG); GetBoxMonData(boxMon, MON_DATA_NICKNAME, sPSSData->displayMonName); StringGetEnd10(sPSSData->displayMonName); sPSSData->displayMonLevel = GetLevelFromBoxMonExp(boxMon); sPSSData->displayMonMarkings = GetBoxMonData(boxMon, MON_DATA_MARKINGS); sPSSData->displayMonPersonality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY); sPSSData->displayMonPalette = GetMonSpritePalFromSpeciesAndPersonality(sPSSData->displayMonSpecies, otId, sPSSData->displayMonPersonality); gender = GetGenderFromSpeciesAndPersonality(sPSSData->displayMonSpecies, sPSSData->displayMonPersonality); sPSSData->displayMonItemId = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM); } } else { sPSSData->displayMonSpecies = SPECIES_NONE; sPSSData->displayMonItemId = ITEM_NONE; } if (sPSSData->displayMonSpecies == SPECIES_NONE) { StringFill(sPSSData->displayMonName, CHAR_SPACE, 5); StringFill(sPSSData->displayMonNameText, CHAR_SPACE, 8); StringFill(sPSSData->displayMonSpeciesName, CHAR_SPACE, 8); StringFill(sPSSData->displayMonGenderLvlText, CHAR_SPACE, 8); StringFill(sPSSData->displayMonItemName, CHAR_SPACE, 8); } else if (sPSSData->displayMonIsEgg) { if (sanityIsBadEgg) StringCopyPadded(sPSSData->displayMonNameText, sPSSData->displayMonName, CHAR_SPACE, 5); else StringCopyPadded(sPSSData->displayMonNameText, gText_EggNickname, CHAR_SPACE, 8); StringFill(sPSSData->displayMonSpeciesName, CHAR_SPACE, 8); StringFill(sPSSData->displayMonGenderLvlText, CHAR_SPACE, 8); StringFill(sPSSData->displayMonItemName, CHAR_SPACE, 8); } else { if (sPSSData->displayMonSpecies == SPECIES_NIDORAN_F || sPSSData->displayMonSpecies == SPECIES_NIDORAN_M) gender = MON_GENDERLESS; StringCopyPadded(sPSSData->displayMonNameText, sPSSData->displayMonName, CHAR_SPACE, 5); txtPtr = sPSSData->displayMonSpeciesName; *(txtPtr)++ = CHAR_SLASH; StringCopyPadded(txtPtr, gSpeciesNames[sPSSData->displayMonSpecies], CHAR_SPACE, 5); txtPtr = sPSSData->displayMonGenderLvlText; *(txtPtr)++ = EXT_CTRL_CODE_BEGIN; *(txtPtr)++ = EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW; switch (gender) { case MON_MALE: *(txtPtr)++ = TEXT_COLOR_RED; *(txtPtr)++ = TEXT_COLOR_WHITE; *(txtPtr)++ = TEXT_COLOR_LIGHT_RED; *(txtPtr)++ = CHAR_MALE; break; case MON_FEMALE: *(txtPtr)++ = TEXT_COLOR_GREEN; *(txtPtr)++ = TEXT_COLOR_WHITE; *(txtPtr)++ = TEXT_COLOR_LIGHT_GREEN; *(txtPtr)++ = CHAR_FEMALE; break; default: *(txtPtr)++ = TEXT_COLOR_DARK_GRAY; *(txtPtr)++ = TEXT_COLOR_WHITE; *(txtPtr)++ = TEXT_COLOR_LIGHT_GRAY; *(txtPtr)++ = CHAR_GENDERLESS; break; } *(txtPtr++) = EXT_CTRL_CODE_BEGIN; *(txtPtr++) = EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW; *(txtPtr++) = TEXT_COLOR_DARK_GRAY; *(txtPtr++) = TEXT_COLOR_WHITE; *(txtPtr++) = TEXT_COLOR_LIGHT_GRAY; *(txtPtr++) = CHAR_SPACE; *(txtPtr++) = CHAR_EXTRA_SYMBOL; *(txtPtr++) = CHAR_LV_2; txtPtr = ConvertIntToDecimalStringN(txtPtr, sPSSData->displayMonLevel, STR_CONV_MODE_LEFT_ALIGN, 3); txtPtr[0] = CHAR_SPACE; txtPtr[1] = EOS; if (sPSSData->displayMonItemId != ITEM_NONE) StringCopyPadded(sPSSData->displayMonItemName, ItemId_GetName(sPSSData->displayMonItemId), CHAR_SPACE, 8); else StringFill(sPSSData->displayMonItemName, CHAR_SPACE, 8); } } static u8 HandleInput_InBox(void) { switch (sPSSData->inBoxMovingMode) { case MOVE_MODE_NORMAL: default: return InBoxInput_Normal(); case MOVE_MODE_MULTIPLE_SELECTING: return InBoxInput_SelectingMultiple(); case MOVE_MODE_MULTIPLE_MOVING: return InBoxInput_MovingMultiple(); } } static u8 InBoxInput_Normal(void) { u8 retVal; s8 cursorArea; s8 cursorPosition; do { cursorArea = sCursorArea; cursorPosition = sCursorPosition; sPSSData->cursorVerticalWrap = 0; sPSSData->cursorHorizontalWrap = 0; sPSSData->cursorFlipTimer = 0; if (JOY_REPEAT(DPAD_UP)) { retVal = INPUT_MOVE_CURSOR; if (sCursorPosition >= IN_BOX_COLUMNS) { cursorPosition -= IN_BOX_COLUMNS; } else { cursorArea = CURSOR_AREA_BOX_TITLE; cursorPosition = 0; } break; } else if (JOY_REPEAT(DPAD_DOWN)) { retVal = INPUT_MOVE_CURSOR; cursorPosition += IN_BOX_COLUMNS; if (cursorPosition >= IN_BOX_COUNT) { cursorArea = CURSOR_AREA_BUTTONS; cursorPosition -= IN_BOX_COUNT; cursorPosition /= 3; sPSSData->cursorVerticalWrap = 1; sPSSData->cursorFlipTimer = 1; } break; } else if (JOY_REPEAT(DPAD_LEFT)) { retVal = INPUT_MOVE_CURSOR; if (sCursorPosition % IN_BOX_COLUMNS != 0) { cursorPosition--; } else { sPSSData->cursorHorizontalWrap = -1; cursorPosition += (IN_BOX_COLUMNS - 1); } break; } else if (JOY_REPEAT(DPAD_RIGHT)) { retVal = INPUT_MOVE_CURSOR; if ((sCursorPosition + 1) % IN_BOX_COLUMNS != 0) { cursorPosition++; } else { sPSSData->cursorHorizontalWrap = 1; cursorPosition -= (IN_BOX_COLUMNS - 1); } break; } else if (JOY_NEW(START_BUTTON)) { retVal = INPUT_MOVE_CURSOR; cursorArea = CURSOR_AREA_BOX_TITLE; cursorPosition = 0; break; } if ((JOY_NEW(A_BUTTON)) && SetSelectionMenuTexts()) { if (!sAutoActionOn) return INPUT_IN_MENU; if (sPSSData->boxOption != OPTION_MOVE_MONS || sIsMonBeingMoved == TRUE) { switch (GetMenuItemTextId(0)) { case MENU_STORE: return INPUT_DEPOSIT; case MENU_WITHDRAW: return INPUT_WITHDRAW; case MENU_MOVE: return INPUT_MOVE_MON; case MENU_SHIFT: return INPUT_SHIFT_MON; case MENU_PLACE: return INPUT_PLACE_MON; case MENU_TAKE: return INPUT_TAKE_ITEM; case MENU_GIVE: return INPUT_GIVE_ITEM; case MENU_SWITCH: return INPUT_SWITCH_ITEMS; } } else { sPSSData->inBoxMovingMode = MOVE_MODE_MULTIPLE_SELECTING; return INPUT_MULTIMOVE_START; } } if (JOY_NEW(B_BUTTON)) return INPUT_PRESSED_B; if (gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_LR) { if (JOY_HELD(L_BUTTON)) return INPUT_SCROLL_LEFT; if (JOY_HELD(R_BUTTON)) return INPUT_SCROLL_RIGHT; } if (JOY_NEW(SELECT_BUTTON)) { ToggleCursorAutoAction(); return INPUT_NONE; } retVal = INPUT_NONE; } while (0); if (retVal) SetCursorPosition(cursorArea, cursorPosition); return retVal; } static u8 InBoxInput_SelectingMultiple(void) { if (JOY_HELD(A_BUTTON)) { if (JOY_REPEAT(DPAD_UP)) { if (sCursorPosition / IN_BOX_COLUMNS != 0) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition - IN_BOX_COLUMNS); return INPUT_MULTIMOVE_CHANGE_SELECTION; } else { return INPUT_MULTIMOVE_UNABLE; } } else if (JOY_REPEAT(DPAD_DOWN)) { if (sCursorPosition + IN_BOX_COLUMNS < IN_BOX_COUNT) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition + IN_BOX_COLUMNS); return INPUT_MULTIMOVE_CHANGE_SELECTION; } else { return INPUT_MULTIMOVE_UNABLE; } } else if (JOY_REPEAT(DPAD_LEFT)) { if (sCursorPosition % IN_BOX_COLUMNS != 0) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition - 1); return INPUT_MULTIMOVE_CHANGE_SELECTION; } else { return INPUT_MULTIMOVE_UNABLE; } } else if (JOY_REPEAT(DPAD_RIGHT)) { if ((sCursorPosition + 1) % IN_BOX_COLUMNS != 0) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition + 1); return INPUT_MULTIMOVE_CHANGE_SELECTION; } else { return INPUT_MULTIMOVE_UNABLE; } } else { return INPUT_NONE; } } else { if (MultiMove_GetOrigin() == sCursorPosition) { // Doing a multiple mon selection but only chose 1 mon sPSSData->inBoxMovingMode = MOVE_MODE_NORMAL; sPSSData->cursorShadowSprite->invisible = FALSE; return INPUT_MULTIMOVE_SINGLE; } else { sIsMonBeingMoved = (sPSSData->displayMonSpecies != SPECIES_NONE); sPSSData->inBoxMovingMode = MOVE_MODE_MULTIPLE_MOVING; sMovingMonOrigBoxId = StorageGetCurrentBox(); return INPUT_MULTIMOVE_GRAB_SELECTION; } } } static u8 InBoxInput_MovingMultiple(void) { if (JOY_REPEAT(DPAD_UP)) { if (MultiMove_TryMoveGroup(0)) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition - IN_BOX_COLUMNS); return INPUT_MULTIMOVE_MOVE_MONS; } else { return INPUT_MULTIMOVE_UNABLE; } } else if (JOY_REPEAT(DPAD_DOWN)) { if (MultiMove_TryMoveGroup(1)) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition + IN_BOX_COLUMNS); return INPUT_MULTIMOVE_MOVE_MONS; } else { return INPUT_MULTIMOVE_UNABLE; } } else if (JOY_REPEAT(DPAD_LEFT)) { if (MultiMove_TryMoveGroup(2)) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition - 1); return INPUT_MULTIMOVE_MOVE_MONS; } else { return INPUT_SCROLL_LEFT; } } else if (JOY_REPEAT(DPAD_RIGHT)) { if (MultiMove_TryMoveGroup(3)) { SetCursorPosition(CURSOR_AREA_IN_BOX, sCursorPosition + 1); return INPUT_MULTIMOVE_MOVE_MONS; } else { return INPUT_SCROLL_RIGHT; } } else if (JOY_NEW(A_BUTTON)) { if (MultiMove_CanPlaceSelection()) { sIsMonBeingMoved = FALSE; sPSSData->inBoxMovingMode = MOVE_MODE_NORMAL; return INPUT_MULTIMOVE_PLACE_MONS; } else { return INPUT_MULTIMOVE_UNABLE; } } else if (JOY_NEW(B_BUTTON)) { return INPUT_MULTIMOVE_UNABLE; } else { if (gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_LR) { if (JOY_HELD(L_BUTTON)) return INPUT_SCROLL_LEFT; if (JOY_HELD(R_BUTTON)) return INPUT_SCROLL_RIGHT; } return INPUT_NONE; } } static u8 HandleInput_InParty(void) { u8 retVal; bool8 gotoBox; s8 cursorArea; s8 cursorPosition; do { cursorArea = sCursorArea; cursorPosition = sCursorPosition; sPSSData->cursorHorizontalWrap = 0; sPSSData->cursorVerticalWrap = 0; sPSSData->cursorFlipTimer = 0; gotoBox = FALSE; retVal = INPUT_NONE; if (JOY_REPEAT(DPAD_UP)) { if (--cursorPosition < 0) cursorPosition = PARTY_SIZE; if (cursorPosition != sCursorPosition) retVal = INPUT_MOVE_CURSOR; break; } else if (JOY_REPEAT(DPAD_DOWN)) { if (++cursorPosition > PARTY_SIZE) cursorPosition = 0; if (cursorPosition != sCursorPosition) retVal = INPUT_MOVE_CURSOR; break; } else if (JOY_REPEAT(DPAD_LEFT) && sCursorPosition != 0) { retVal = INPUT_MOVE_CURSOR; sPSSData->cursorPrevHorizPos = sCursorPosition; cursorPosition = 0; break; } else if (JOY_REPEAT(DPAD_RIGHT)) { if (sCursorPosition == 0) { retVal = INPUT_MOVE_CURSOR; cursorPosition = sPSSData->cursorPrevHorizPos; } else { retVal = INPUT_HIDE_PARTY; cursorArea = CURSOR_AREA_IN_BOX; cursorPosition = 0; } break; } if (JOY_NEW(A_BUTTON)) { if (sCursorPosition == PARTY_SIZE) { if (sPSSData->boxOption == OPTION_DEPOSIT) return INPUT_CLOSE_BOX; gotoBox = TRUE; } else if (SetSelectionMenuTexts()) { if (!sAutoActionOn) return INPUT_IN_MENU; switch (GetMenuItemTextId(0)) { case MENU_STORE: return INPUT_DEPOSIT; case MENU_WITHDRAW: return INPUT_WITHDRAW; case MENU_MOVE: return INPUT_MOVE_MON; case MENU_SHIFT: return INPUT_SHIFT_MON; case MENU_PLACE: return INPUT_PLACE_MON; case MENU_TAKE: return INPUT_TAKE_ITEM; case MENU_GIVE: return INPUT_GIVE_ITEM; case MENU_SWITCH: return INPUT_SWITCH_ITEMS; } } } if (JOY_NEW(B_BUTTON)) { if (sPSSData->boxOption == OPTION_DEPOSIT) return INPUT_PRESSED_B; gotoBox = TRUE; } if (gotoBox) { retVal = INPUT_HIDE_PARTY; cursorArea = CURSOR_AREA_IN_BOX; cursorPosition = 0; } else if (JOY_NEW(SELECT_BUTTON)) { ToggleCursorAutoAction(); return INPUT_NONE; } } while (0); if (retVal != INPUT_NONE) { if (retVal != INPUT_HIDE_PARTY) SetCursorPosition(cursorArea, cursorPosition); } return retVal; } static u8 HandleInput_OnBox(void) { u8 retVal; s8 cursorArea; s8 cursorPosition; do { sPSSData->cursorHorizontalWrap = 0; sPSSData->cursorVerticalWrap = 0; sPSSData->cursorFlipTimer = 0; if (JOY_REPEAT(DPAD_UP)) { retVal = INPUT_MOVE_CURSOR; cursorArea = CURSOR_AREA_BUTTONS; cursorPosition = 0; sPSSData->cursorFlipTimer = 1; break; } else if (JOY_REPEAT(DPAD_DOWN)) { retVal = INPUT_MOVE_CURSOR; cursorArea = CURSOR_AREA_IN_BOX; cursorPosition = 2; break; } if (JOY_HELD(DPAD_LEFT)) return INPUT_SCROLL_LEFT; if (JOY_HELD(DPAD_RIGHT)) return INPUT_SCROLL_RIGHT; if (gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_LR) { if (JOY_HELD(L_BUTTON)) return INPUT_SCROLL_LEFT; if (JOY_HELD(R_BUTTON)) return INPUT_SCROLL_RIGHT; } if (JOY_NEW(A_BUTTON)) { AnimateBoxScrollArrows(FALSE); AddBoxMenu(); return INPUT_BOX_OPTIONS; } if (JOY_NEW(B_BUTTON)) return INPUT_PRESSED_B; if (JOY_NEW(SELECT_BUTTON)) { ToggleCursorAutoAction(); return INPUT_NONE; } retVal = INPUT_NONE; } while (0); if (retVal != INPUT_NONE) { if (cursorArea != CURSOR_AREA_BOX_TITLE) AnimateBoxScrollArrows(FALSE); SetCursorPosition(cursorArea, cursorPosition); } return retVal; } static u8 HandleInput_OnButtons(void) { u8 retVal; s8 cursorArea; s8 cursorPosition; do { cursorArea = sCursorArea; cursorPosition = sCursorPosition; sPSSData->cursorHorizontalWrap = 0; sPSSData->cursorVerticalWrap = 0; sPSSData->cursorFlipTimer = 0; if (JOY_REPEAT(DPAD_UP)) { retVal = INPUT_MOVE_CURSOR; cursorArea = CURSOR_AREA_IN_BOX; sPSSData->cursorVerticalWrap = -1; if (sCursorPosition == 0) cursorPosition = IN_BOX_COUNT - 1 - 5; else cursorPosition = IN_BOX_COUNT - 1; sPSSData->cursorFlipTimer = 1; break; } if (JOY_REPEAT(DPAD_DOWN | START_BUTTON)) { retVal = INPUT_MOVE_CURSOR; cursorArea = CURSOR_AREA_BOX_TITLE; cursorPosition = 0; sPSSData->cursorFlipTimer = 1; break; } if (JOY_REPEAT(DPAD_LEFT)) { retVal = INPUT_MOVE_CURSOR; if (--cursorPosition < 0) cursorPosition = 1; break; } else if (JOY_REPEAT(DPAD_RIGHT)) { retVal = INPUT_MOVE_CURSOR; if (++cursorPosition > 1) cursorPosition = 0; break; } // Button was pressed, determine which if (JOY_NEW(A_BUTTON)) return (cursorPosition == 0) ? INPUT_SHOW_PARTY : INPUT_CLOSE_BOX; if (JOY_NEW(B_BUTTON)) return INPUT_PRESSED_B; if (JOY_NEW(SELECT_BUTTON)) { ToggleCursorAutoAction(); return INPUT_NONE; } retVal = INPUT_NONE; } while (0); if (retVal != INPUT_NONE) SetCursorPosition(cursorArea, cursorPosition); return retVal; } static u8 HandleInput(void) { struct { u8 (*func)(void); s8 area; } static const inputFuncs[] = { {HandleInput_InBox, CURSOR_AREA_IN_BOX}, {HandleInput_InParty, CURSOR_AREA_IN_PARTY}, {HandleInput_OnBox, CURSOR_AREA_BOX_TITLE}, {HandleInput_OnButtons, CURSOR_AREA_BUTTONS}, {}, }; u16 i = 0; while (inputFuncs[i].func != NULL) { if (inputFuncs[i].area == sCursorArea) return inputFuncs[i].func(); i++; } return INPUT_NONE; } static void AddBoxMenu(void) { InitMenu(); SetMenuText(MENU_JUMP); SetMenuText(MENU_WALLPAPER); SetMenuText(MENU_NAME); SetMenuText(MENU_CANCEL); } static u8 SetSelectionMenuTexts(void) { InitMenu(); if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return SetMenuTexts_Mon(); else return SetMenuTexts_Item(); } static bool8 SetMenuTexts_Mon(void) { u16 species = GetSpeciesAtCursorPosition(); switch (sPSSData->boxOption) { case OPTION_DEPOSIT: if (species != SPECIES_NONE) SetMenuText(MENU_STORE); else return FALSE; break; case OPTION_WITHDRAW: if (species != SPECIES_NONE) SetMenuText(MENU_WITHDRAW); else return FALSE; break; case OPTION_MOVE_MONS: if (sIsMonBeingMoved) { if (species != SPECIES_NONE) SetMenuText(MENU_SHIFT); else SetMenuText(MENU_PLACE); } else { if (species != SPECIES_NONE) SetMenuText(MENU_MOVE); else return FALSE; } break; case OPTION_MOVE_ITEMS: default: return FALSE; } SetMenuText(MENU_SUMMARY); if (sPSSData->boxOption == OPTION_MOVE_MONS) { if (sCursorArea == CURSOR_AREA_IN_BOX) SetMenuText(MENU_WITHDRAW); else SetMenuText(MENU_STORE); } SetMenuText(MENU_MARK); SetMenuText(MENU_RELEASE); SetMenuText(MENU_CANCEL); return TRUE; } static bool8 SetMenuTexts_Item(void) { if (sPSSData->displayMonSpecies == SPECIES_EGG) return FALSE; if (!IsMovingItem()) { if (sPSSData->displayMonItemId == ITEM_NONE) { if (sPSSData->displayMonSpecies == SPECIES_NONE) return FALSE; SetMenuText(MENU_GIVE_2); } else { if (!ItemIsMail(sPSSData->displayMonItemId)) { SetMenuText(MENU_TAKE); SetMenuText(MENU_BAG); } SetMenuText(MENU_INFO); } } else { if (sPSSData->displayMonItemId == ITEM_NONE) { if (sPSSData->displayMonSpecies == SPECIES_NONE) return FALSE; SetMenuText(MENU_GIVE); } else { if (ItemIsMail(sPSSData->displayMonItemId) == TRUE) return FALSE; SetMenuText(MENU_SWITCH); } } SetMenuText(MENU_CANCEL); return TRUE; } static void SpriteCB_CursorShadow(struct Sprite *sprite) { sprite->pos1.x = sPSSData->cursorSprite->pos1.x; sprite->pos1.y = sPSSData->cursorSprite->pos1.y + 20; } static void CreateCursorSprites(void) { u16 x, y; u8 spriteId; u8 priority, subpriority; struct SpriteSheet spriteSheets[] = { {sHandCursor_Gfx, 0x800, GFXTAG_CURSOR}, {sHandCursorShadow_Gfx, 0x80, GFXTAG_CURSOR_SHADOW}, {} }; struct SpritePalette spritePalettes[] = { {sHandCursor_Pal, PALTAG_7}, {} }; static const struct OamData sOamData_Cursor = { .shape = SPRITE_SHAPE(32x32), .size = SPRITE_SIZE(32x32), .priority = 1, }; static const struct OamData sOamData_CursorShadow = { .shape = SPRITE_SHAPE(16x16), .size = SPRITE_SIZE(16x16), .priority = 1, }; static const union AnimCmd sAnim_Cursor_Bouncing[] = { ANIMCMD_FRAME(0, 30), ANIMCMD_FRAME(16, 30), ANIMCMD_JUMP(0) }; static const union AnimCmd sAnim_Cursor_Still[] = { ANIMCMD_FRAME(0, 5), ANIMCMD_END }; static const union AnimCmd sAnim_Cursor_Open[] = { ANIMCMD_FRAME(32, 5), ANIMCMD_END }; static const union AnimCmd sAnim_Cursor_Fist[] = { ANIMCMD_FRAME(48, 5), ANIMCMD_END }; static const union AnimCmd *const sAnims_Cursor[] = { [CURSOR_ANIM_BOUNCE] = sAnim_Cursor_Bouncing, [CURSOR_ANIM_STILL] = sAnim_Cursor_Still, [CURSOR_ANIM_OPEN] = sAnim_Cursor_Open, [CURSOR_ANIM_FIST] = sAnim_Cursor_Fist }; static const struct SpriteTemplate sSpriteTemplate_Cursor = { .tileTag = GFXTAG_CURSOR, .paletteTag = PALTAG_10, .oam = &sOamData_Cursor, .anims = sAnims_Cursor, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct SpriteTemplate sSpriteTemplate_CursorShadow = { .tileTag = GFXTAG_CURSOR_SHADOW, .paletteTag = PALTAG_10, .oam = &sOamData_CursorShadow, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_CursorShadow, }; LoadSpriteSheets(spriteSheets); LoadSpritePalettes(spritePalettes); sPSSData->cursorPalNums[0] = IndexOfSpritePaletteTag(PALTAG_10); // White hand, normal sPSSData->cursorPalNums[1] = IndexOfSpritePaletteTag(PALTAG_7); // Yellow hand, when auto-action is on GetCursorCoordsByPos(sCursorArea, sCursorPosition, &x, &y); spriteId = CreateSprite(&sSpriteTemplate_Cursor, x, y, 6); if (spriteId != MAX_SPRITES) { sPSSData->cursorSprite = &gSprites[spriteId]; sPSSData->cursorSprite->oam.paletteNum = sPSSData->cursorPalNums[sAutoActionOn]; sPSSData->cursorSprite->oam.priority = 1; if (sIsMonBeingMoved) StartSpriteAnim(sPSSData->cursorSprite, CURSOR_ANIM_FIST); } else { sPSSData->cursorSprite = NULL; } if (sCursorArea == CURSOR_AREA_IN_PARTY) { subpriority = 13; priority = 1; } else { subpriority = 21; priority = 2; } spriteId = CreateSprite(&sSpriteTemplate_CursorShadow, 0, 0, subpriority); if (spriteId != MAX_SPRITES) { sPSSData->cursorShadowSprite = &gSprites[spriteId]; sPSSData->cursorShadowSprite->oam.priority = priority; if (sCursorArea) sPSSData->cursorShadowSprite->invisible = TRUE; } else { sPSSData->cursorShadowSprite = NULL; } } static void ToggleCursorAutoAction(void) { sAutoActionOn = !sAutoActionOn; sPSSData->cursorSprite->oam.paletteNum = sPSSData->cursorPalNums[sAutoActionOn]; } static u8 GetCursorPosition(void) { return sCursorPosition; } static void GetCursorBoxColumnAndRow(u8 *column, u8 *row) { if (sCursorArea == CURSOR_AREA_IN_BOX) { *column = sCursorPosition % IN_BOX_COLUMNS; *row = sCursorPosition / IN_BOX_COLUMNS; } else { *column = 0; *row = 0; } } static void StartCursorAnim(u8 animNum) { StartSpriteAnim(sPSSData->cursorSprite, animNum); } // Unused static u8 GetMovingMonOriginalBoxId(void) { return sMovingMonOrigBoxId; } static void SetCursorPriorityTo1(void) { sPSSData->cursorSprite->oam.priority = 1; } static void TryHideItemAtCursor(void) { if (sCursorArea == CURSOR_AREA_IN_BOX) TryHideItemIconAtPos(CURSOR_AREA_IN_BOX, sCursorPosition); } static void TryShowItemAtCursor(void) { if (sCursorArea == CURSOR_AREA_IN_BOX) TryLoadItemIconAtPos(CURSOR_AREA_IN_BOX, sCursorPosition); } static void InitMenu(void) { sPSSData->menuItemsCount = 0; sPSSData->menuWidth = 0; sPSSData->menuWindow.bg = 0; sPSSData->menuWindow.paletteNum = 15; sPSSData->menuWindow.baseBlock = 92; } static const u8 *const sMenuTexts[] = { [MENU_CANCEL] = gPCText_Cancel, [MENU_STORE] = gPCText_Store, [MENU_WITHDRAW] = gPCText_Withdraw, [MENU_MOVE] = gPCText_Move, [MENU_SHIFT] = gPCText_Shift, [MENU_PLACE] = gPCText_Place, [MENU_SUMMARY] = gPCText_Summary, [MENU_RELEASE] = gPCText_Release, [MENU_MARK] = gPCText_Mark, [MENU_JUMP] = gPCText_Jump, [MENU_WALLPAPER] = gPCText_Wallpaper, [MENU_NAME] = gPCText_Name, [MENU_TAKE] = gPCText_Take, [MENU_GIVE] = gPCText_Give, [MENU_GIVE_2] = gPCText_Give, [MENU_SWITCH] = gPCText_Switch, [MENU_BAG] = gPCText_Bag, [MENU_INFO] = gPCText_Info, [MENU_SCENERY_1] = gPCText_Scenery1, [MENU_SCENERY_2] = gPCText_Scenery2, [MENU_SCENERY_3] = gPCText_Scenery3, [MENU_ETCETERA] = gPCText_Etcetera, [MENU_FRIENDS] = gPCText_Friends, [MENU_FOREST] = gPCText_Forest, [MENU_CITY] = gPCText_City, [MENU_DESERT] = gPCText_Desert, [MENU_SAVANNA] = gPCText_Savanna, [MENU_CRAG] = gPCText_Crag, [MENU_VOLCANO] = gPCText_Volcano, [MENU_SNOW] = gPCText_Snow, [MENU_CAVE] = gPCText_Cave, [MENU_BEACH] = gPCText_Beach, [MENU_SEAFLOOR] = gPCText_Seafloor, [MENU_RIVER] = gPCText_River, [MENU_SKY] = gPCText_Sky, [MENU_POLKADOT] = gPCText_PolkaDot, [MENU_POKECENTER] = gPCText_Pokecenter, [MENU_MACHINE] = gPCText_Machine, [MENU_SIMPLE] = gPCText_Simple, }; static void SetMenuText(u8 textId) { if (sPSSData->menuItemsCount < ARRAY_COUNT(sPSSData->menuItems)) { u8 len; struct StorageMenu *menu = &sPSSData->menuItems[sPSSData->menuItemsCount]; menu->text = sMenuTexts[textId]; menu->textId = textId; len = StringLength(menu->text); if (len > sPSSData->menuWidth) sPSSData->menuWidth = len; sPSSData->menuItemsCount++; } } static s8 GetMenuItemTextId(u8 menuIdx) { if (menuIdx >= sPSSData->menuItemsCount) return -1; else return sPSSData->menuItems[menuIdx].textId; } static void AddMenu(void) { sPSSData->menuWindow.width = sPSSData->menuWidth + 2; sPSSData->menuWindow.height = 2 * sPSSData->menuItemsCount; sPSSData->menuWindow.tilemapLeft = 29 - sPSSData->menuWindow.width; sPSSData->menuWindow.tilemapTop = 15 - sPSSData->menuWindow.height; sPSSData->menuWindowId = AddWindow(&sPSSData->menuWindow); ClearWindowTilemap(sPSSData->menuWindowId); DrawStdFrameWithCustomTileAndPalette(sPSSData->menuWindowId, FALSE, 11, 14); PrintMenuTable(sPSSData->menuWindowId, sPSSData->menuItemsCount, (void*)sPSSData->menuItems); InitMenuInUpperLeftCornerPlaySoundWhenAPressed(sPSSData->menuWindowId, sPSSData->menuItemsCount, 0); ScheduleBgCopyTilemapToVram(0); sPSSData->menuUnusedField = 0; } // Called after AddMenu to determine whether or not the handler callback should // wait to move on to the next state. Evidently there was no need to wait, and // now it always returns FALSE static bool8 IsMenuLoading(void) { return FALSE; } static s16 HandleMenuInput(void) { s32 input = MENU_NOTHING_CHOSEN; do { if (JOY_NEW(A_BUTTON)) { input = Menu_GetCursorPos(); break; } else if (JOY_NEW(B_BUTTON)) { PlaySE(SE_SELECT); input = MENU_B_PRESSED; } if (JOY_NEW(DPAD_UP)) { PlaySE(SE_SELECT); Menu_MoveCursor(-1); } else if (JOY_NEW(DPAD_DOWN)) { PlaySE(SE_SELECT); Menu_MoveCursor(1); } } while (0); if (input != MENU_NOTHING_CHOSEN) RemoveMenu(); if (input >= 0) input = sPSSData->menuItems[input].textId; return input; } static void RemoveMenu(void) { ClearStdWindowAndFrameToTransparent(sPSSData->menuWindowId, TRUE); RemoveWindow(sPSSData->menuWindowId); } //------------------------------------------------------------------------------ // SECTION: MultiMove // // The functions below handle moving and selecting multiple Pokémon at once. // The icon sprites are moved to bg 0, and this bg is manipulated to move // them as a group. //------------------------------------------------------------------------------ static const struct WindowTemplate sWindowTemplate_MultiMove = { .bg = 0, .tilemapLeft = 10, .tilemapTop = 3, .width = 20, .height = 18, .paletteNum = 9, .baseBlock = 0xA, }; EWRAM_DATA static struct { u8 funcId; u8 state; u8 fromColumn; u8 fromRow; u8 toColumn; u8 toRow; u8 cursorColumn; u8 cursorRow; u8 minColumn; u8 minRow; u8 columnsTotal; u8 rowsTotal; u16 bgX; u16 bgY; u16 bgMoveSteps; struct BoxPokemon boxMons[IN_BOX_COUNT]; } *sMultiMove = NULL; static bool8 MultiMove_Init(void) { sMultiMove = Alloc(sizeof(*sMultiMove)); if (sMultiMove != NULL) { sPSSData->multiMoveWindowId = AddWindow8Bit(&sWindowTemplate_MultiMove); if (sPSSData->multiMoveWindowId != WINDOW_NONE) { FillWindowPixelBuffer(sPSSData->multiMoveWindowId, PIXEL_FILL(0)); return TRUE; } } return FALSE; } static void MultiMove_Free(void) { if (sMultiMove != NULL) Free(sMultiMove); } static void MultiMove_SetFunction(u8 id) { sMultiMove->funcId = id; sMultiMove->state = 0; } // Returns TRUE if the called function has more to do, FALSE otherwise static bool8 MultiMove_RunFunction(void) { switch (sMultiMove->funcId) { case MULTIMOVE_START: return MultiMove_Start(); case MULTIMOVE_CANCEL: return MultiMove_Cancel(); case MULTIMOVE_CHANGE_SELECTION: return MultiMove_ChangeSelection(); case MULTIMOVE_GRAB_SELECTION: return MultiMove_GrabSelection(); case MULTIMOVE_MOVE_MONS: return MultiMove_MoveMons(); case MULTIMOVE_PLACE_MONS: return MultiMove_PlaceMons(); } return FALSE; } static bool8 MultiMove_Start(void) { switch (sMultiMove->state) { case 0: HideBg(0); sub_80D304C(0x80); sMultiMove->state++; break; case 1: GetCursorBoxColumnAndRow(&sMultiMove->fromColumn, &sMultiMove->fromRow); sMultiMove->toColumn = sMultiMove->fromColumn; sMultiMove->toRow = sMultiMove->fromRow; ChangeBgX(0, -1024, 0); ChangeBgY(0, -1024, 0); FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 0x20, 0x20); FillWindowPixelBuffer8Bit(sPSSData->multiMoveWindowId, PIXEL_FILL(0)); MultiMove_SetIconToBg(sMultiMove->fromColumn, sMultiMove->fromRow); SetBgAttribute(0, BG_ATTR_PALETTEMODE, 1); PutWindowTilemap(sPSSData->multiMoveWindowId); CopyWindowToVram8Bit(sPSSData->multiMoveWindowId, 3); BlendPalettes(0x3F00, 8, RGB_WHITE); StartCursorAnim(CURSOR_ANIM_OPEN); SetGpuRegBits(REG_OFFSET_BG0CNT, BGCNT_256COLOR); sMultiMove->state++; break; case 2: if (!IsDma3ManagerBusyWithBgCopy()) { ShowBg(0); return FALSE; } break; } return TRUE; } static bool8 MultiMove_Cancel(void) { switch (sMultiMove->state) { case 0: HideBg(0); sMultiMove->state++; break; case 1: MultiMove_ResetBg(); StartCursorAnim(CURSOR_ANIM_BOUNCE); sMultiMove->state++; break; case 2: if (!IsDma3ManagerBusyWithBgCopy()) { SetCursorPriorityTo1(); LoadPalette(GetTextWindowPalette(3), 0xD0, 0x20); ShowBg(0); return FALSE; } break; } return TRUE; } static bool8 MultiMove_ChangeSelection(void) { switch (sMultiMove->state) { case 0: if (!UpdateCursorPos()) { GetCursorBoxColumnAndRow(&sMultiMove->cursorColumn, &sMultiMove->cursorRow); MultiMove_UpdateSelectedIcons(); sMultiMove->toColumn = sMultiMove->cursorColumn; sMultiMove->toRow = sMultiMove->cursorRow; CopyWindowToVram8Bit(sPSSData->multiMoveWindowId, 2); sMultiMove->state++; } break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 MultiMove_GrabSelection(void) { bool8 movingBg, movingMon; switch (sMultiMove->state) { case 0: MultiMove_GetMonsFromSelection(); MultiMove_RemoveMonsFromBox(); InitMultiMonPlaceChange(FALSE); sMultiMove->state++; break; case 1: if (!DoMonPlaceChange()) { StartCursorAnim(CURSOR_ANIM_FIST); MultiMove_InitMove(0, 256, 8); InitMultiMonPlaceChange(TRUE); sMultiMove->state++; } break; case 2: movingBg = MultiMove_UpdateMove(); movingMon = DoMonPlaceChange(); if (!movingBg && !movingMon) return FALSE; // Finished break; } return TRUE; } static bool8 MultiMove_MoveMons(void) { bool8 movingCursor = UpdateCursorPos(); bool8 movingBg = MultiMove_UpdateMove(); if (!movingCursor && !movingBg) return FALSE; else return TRUE; } static bool8 MultiMove_PlaceMons(void) { switch (sMultiMove->state) { case 0: MultiMove_SetPlacedMonData(); MultiMove_InitMove(0, -256, 8); InitMultiMonPlaceChange(FALSE); sMultiMove->state++; break; case 1: if (!DoMonPlaceChange() && !MultiMove_UpdateMove()) { MultiMove_CreatePlacedMonIcons(); StartCursorAnim(CURSOR_ANIM_OPEN); InitMultiMonPlaceChange(TRUE); HideBg(0); sMultiMove->state++; } break; case 2: if (!DoMonPlaceChange()) { StartCursorAnim(CURSOR_ANIM_BOUNCE); MultiMove_ResetBg(); sMultiMove->state++; } break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) { LoadPalette(GetTextWindowPalette(3), 0xD0, 0x20); SetCursorPriorityTo1(); ShowBg(0); return FALSE; } break; } return TRUE; } // Returns TRUE if the movement was successful, FALSE otherwise static bool8 MultiMove_TryMoveGroup(u8 dir) { switch (dir) { case 0: // Up if (sMultiMove->minRow == 0) return FALSE; sMultiMove->minRow--; MultiMove_InitMove(0, 1024, 6); break; case 1: // Down if (sMultiMove->minRow + sMultiMove->rowsTotal >= IN_BOX_ROWS) return FALSE; sMultiMove->minRow++; MultiMove_InitMove(0, -1024, 6); break; case 2: // Left if (sMultiMove->minColumn == 0) return FALSE; sMultiMove->minColumn--; MultiMove_InitMove(1024, 0, 6); break; case 3: // Right if (sMultiMove->minColumn + sMultiMove->columnsTotal >= IN_BOX_COLUMNS) return FALSE; sMultiMove->minColumn++; MultiMove_InitMove(-1024, 0, 6); break; } return TRUE; } static void MultiMove_UpdateSelectedIcons(void) { s16 columnChange = (abs(sMultiMove->fromColumn - sMultiMove->cursorColumn)) - (abs(sMultiMove->fromColumn - sMultiMove->toColumn)); s16 rowChange = (abs(sMultiMove->fromRow - sMultiMove->cursorRow)) - (abs(sMultiMove->fromRow - sMultiMove->toRow)); if (columnChange > 0) MultiMove_SelectColumn(sMultiMove->cursorColumn, sMultiMove->fromRow, sMultiMove->toRow); if (columnChange < 0) { MultiMove_DeselectColumn(sMultiMove->toColumn, sMultiMove->fromRow, sMultiMove->toRow); MultiMove_SelectColumn(sMultiMove->cursorColumn, sMultiMove->fromRow, sMultiMove->toRow); } if (rowChange > 0) MultiMove_SelectRow(sMultiMove->cursorRow, sMultiMove->fromColumn, sMultiMove->toColumn); if (rowChange < 0) { MultiMove_DeselectRow(sMultiMove->toRow, sMultiMove->fromColumn, sMultiMove->toColumn); MultiMove_SelectRow(sMultiMove->cursorRow, sMultiMove->fromColumn, sMultiMove->toColumn); } } static void MultiMove_SelectColumn(u8 column, u8 minRow, u8 maxRow) { if (minRow > maxRow) { u8 temp; SWAP(minRow, maxRow, temp); } while (minRow <= maxRow) MultiMove_SetIconToBg(column, minRow++); } static void MultiMove_SelectRow(u8 row, u8 minColumn, u8 maxColumn) { if (minColumn > maxColumn) { u8 temp; SWAP(minColumn, maxColumn, temp); } while (minColumn <= maxColumn) MultiMove_SetIconToBg(minColumn++, row); } static void MultiMove_DeselectColumn(u8 column, u8 minRow, u8 maxRow) { if (minRow > maxRow) { u8 temp; SWAP(minRow, maxRow, temp); } while (minRow <= maxRow) MultiMove_ClearIconFromBg(column, minRow++); } static void MultiMove_DeselectRow(u8 row, u8 minColumn, u8 maxColumn) { if (minColumn > maxColumn) { u8 temp; SWAP(minColumn, maxColumn, temp); } while (minColumn <= maxColumn) MultiMove_ClearIconFromBg(minColumn++, row); } static void MultiMove_SetIconToBg(u8 x, u8 y) { u8 position = x + (IN_BOX_COLUMNS * y); u16 species = GetCurrentBoxMonData(position, MON_DATA_SPECIES2); u32 personality = GetCurrentBoxMonData(position, MON_DATA_PERSONALITY); if (species != SPECIES_NONE) { const u8 *iconGfx = GetMonIconPtr(species, personality, 1); u8 index = GetValidMonIconPalIndex(species) + 8; BlitBitmapRectToWindow4BitTo8Bit(sPSSData->multiMoveWindowId, iconGfx, 0, 0, 32, 32, 24 * x, 24 * y, 32, 32, index); } } static void MultiMove_ClearIconFromBg(u8 x, u8 y) { u8 position = x + (IN_BOX_COLUMNS * y); u16 species = GetCurrentBoxMonData(position, MON_DATA_SPECIES2); if (species != SPECIES_NONE) { FillWindowPixelRect8Bit(sPSSData->multiMoveWindowId, PIXEL_FILL(0), 24 * x, 24 * y, 32, 32); } } static void MultiMove_InitMove(u16 x, u16 y, u16 arg2) { sMultiMove->bgX = x; sMultiMove->bgY = y; sMultiMove->bgMoveSteps = arg2; } static u8 MultiMove_UpdateMove(void) { if (sMultiMove->bgMoveSteps != 0) { ChangeBgX(0, sMultiMove->bgX, 1); ChangeBgY(0, sMultiMove->bgY, 1); sMultiMove->bgMoveSteps--; } return sMultiMove->bgMoveSteps; } // Store the Pokémon that the player is picking up static void MultiMove_GetMonsFromSelection(void) { s32 i, j; s32 columnCount, rowCount; u8 boxId; u8 monArrayId; sMultiMove->minColumn = min(sMultiMove->fromColumn, sMultiMove->toColumn); sMultiMove->minRow = min(sMultiMove->fromRow, sMultiMove->toRow); sMultiMove->columnsTotal = abs(sMultiMove->fromColumn - sMultiMove->toColumn) + 1; sMultiMove->rowsTotal = abs(sMultiMove->fromRow - sMultiMove->toRow) + 1; boxId = StorageGetCurrentBox(); monArrayId = 0; columnCount = sMultiMove->minColumn + sMultiMove->columnsTotal; rowCount = sMultiMove->minRow + sMultiMove->rowsTotal; for (i = sMultiMove->minRow; i < rowCount; i++) { u8 boxPosition = (IN_BOX_COLUMNS * i) + sMultiMove->minColumn; for (j = sMultiMove->minColumn; j < columnCount; j++) { struct BoxPokemon *boxMon = GetBoxedMonPtr(boxId, boxPosition); // UB: possible null dereference #ifdef UBFIX if (boxMon != NULL) #endif sMultiMove->boxMons[monArrayId] = *boxMon; monArrayId++; boxPosition++; } } } // The Pokémon the player has picked up have been stored, now delete // them from their original positions static void MultiMove_RemoveMonsFromBox(void) { s32 i, j; s32 columnCount = sMultiMove->minColumn + sMultiMove->columnsTotal; s32 rowCount = sMultiMove->minRow + sMultiMove->rowsTotal; u8 boxId = StorageGetCurrentBox(); for (i = sMultiMove->minRow; i < rowCount; i++) { u8 boxPosition = (IN_BOX_COLUMNS * i) + sMultiMove->minColumn; for (j = sMultiMove->minColumn; j < columnCount; j++) { DestroyBoxMonIconAtPosition(boxPosition); ZeroBoxMonAt(boxId, boxPosition); boxPosition++; } } } static void MultiMove_CreatePlacedMonIcons(void) { s32 i, j; s32 columnCount = sMultiMove->minColumn + sMultiMove->columnsTotal; s32 rowCount = sMultiMove->minRow + sMultiMove->rowsTotal; u8 monArrayId = 0; for (i = sMultiMove->minRow; i < rowCount; i++) { u8 boxPosition = (IN_BOX_COLUMNS * i) + sMultiMove->minColumn; for (j = sMultiMove->minColumn; j < columnCount; j++) { if (GetBoxMonData(&sMultiMove->boxMons[monArrayId], MON_DATA_SANITY_HAS_SPECIES)) sub_80CB140(boxPosition); monArrayId++; boxPosition++; } } } static void MultiMove_SetPlacedMonData(void) { s32 i, j; s32 columnCount = sMultiMove->minColumn + sMultiMove->columnsTotal; s32 rowCount = sMultiMove->minRow + sMultiMove->rowsTotal; u8 boxId = StorageGetCurrentBox(); u8 monArrayId = 0; for (i = sMultiMove->minRow; i < rowCount; i++) { u8 boxPosition = (IN_BOX_COLUMNS * i) + sMultiMove->minColumn; for (j = sMultiMove->minColumn; j < columnCount; j++) { if (GetBoxMonData(&sMultiMove->boxMons[monArrayId], MON_DATA_SANITY_HAS_SPECIES)) SetBoxMonAt(boxId, boxPosition, &sMultiMove->boxMons[monArrayId]); boxPosition++; monArrayId++; } } } static void MultiMove_ResetBg(void) { ChangeBgX(0, 0, 0); ChangeBgY(0, 0, 0); SetBgAttribute(0, BG_ATTR_PALETTEMODE, 0); ClearGpuRegBits(REG_OFFSET_BG0CNT, BGCNT_256COLOR); FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 32, 32); CopyBgTilemapBufferToVram(0); } static u8 MultiMove_GetOrigin(void) { return (IN_BOX_COLUMNS * sMultiMove->fromRow) + sMultiMove->fromColumn; } static bool8 MultiMove_CanPlaceSelection(void) { s32 i, j; s32 columnCount = sMultiMove->minColumn + sMultiMove->columnsTotal; s32 rowCount = sMultiMove->minRow + sMultiMove->rowsTotal; u8 monArrayId = 0; for (i = sMultiMove->minRow; i < rowCount; i++) { u8 boxPosition = (IN_BOX_COLUMNS * i) + sMultiMove->minColumn; for (j = sMultiMove->minColumn; j < columnCount; j++) { if (GetBoxMonData(&sMultiMove->boxMons[monArrayId], MON_DATA_SANITY_HAS_SPECIES) && GetCurrentBoxMonData(boxPosition, MON_DATA_SANITY_HAS_SPECIES)) return FALSE; monArrayId++; boxPosition++; } } return TRUE; } //------------------------------------------------------------------------------ // SECTION: Item mode // // The functions below handle the Move Items mode //------------------------------------------------------------------------------ static const u32 sItemInfoFrame_Gfx[] = INCBIN_U32("graphics/pokemon_storage/item_info_frame.4bpp"); static const struct OamData sOamData_ItemIcon = { .y = 0, .affineMode = ST_OAM_AFFINE_NORMAL, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(32x32), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x32), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0 }; static const union AffineAnimCmd sAffineAnim_ItemIcon_Small[] = { AFFINEANIMCMD_FRAME(128, 128, 0, 0), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_ItemIcon_Appear[] = { AFFINEANIMCMD_FRAME(88, 88, 0, 0), AFFINEANIMCMD_FRAME(5, 5, 0, 8), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_ItemIcon_Disappear[] = { AFFINEANIMCMD_FRAME(128, 128, 0, 0), AFFINEANIMCMD_FRAME(-5, -5, 0, 8), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_ItemIcon_PickUp[] = { AFFINEANIMCMD_FRAME(128, 128, 0, 0), AFFINEANIMCMD_FRAME(10, 10, 0, 12), AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_ItemIcon_PutDown[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_FRAME(-10, -10, 0, 12), AFFINEANIMCMD_FRAME(128, 128, 0, 0), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_ItemIcon_PutAway[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_FRAME(-5, -5, 0, 16), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_ItemIcon_Large[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_END }; static const union AffineAnimCmd *const sAffineAnims_ItemIcon[] = { [ITEM_ANIM_NONE] = sAffineAnim_ItemIcon_Small, [ITEM_ANIM_APPEAR] = sAffineAnim_ItemIcon_Appear, [ITEM_ANIM_DISAPPEAR] = sAffineAnim_ItemIcon_Disappear, [ITEM_ANIM_PICK_UP] = sAffineAnim_ItemIcon_PickUp, [ITEM_ANIM_PUT_DOWN] = sAffineAnim_ItemIcon_PutDown, [ITEM_ANIM_PUT_AWAY] = sAffineAnim_ItemIcon_PutAway, [ITEM_ANIM_LARGE] = sAffineAnim_ItemIcon_Large }; static const struct SpriteTemplate sSpriteTemplate_ItemIcon = { .tileTag = GFXTAG_ITEM_ICON_0, .paletteTag = PALTAG_ITEM_ICON_0, .oam = &sOamData_ItemIcon, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_ItemIcon, .callback = SpriteCallbackDummy, }; static void CreateItemIconSprites(void) { s32 i; u8 spriteId; struct CompressedSpriteSheet spriteSheet; struct SpriteTemplate spriteTemplate; if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { spriteSheet.data = sItemIconGfxBuffer; spriteSheet.size = 0x200; spriteTemplate = sSpriteTemplate_ItemIcon; for (i = 0; i < MAX_ITEM_ICONS; i++) { spriteSheet.tag = GFXTAG_ITEM_ICON_0 + i; LoadCompressedSpriteSheet(&spriteSheet); sPSSData->itemIcons[i].tiles = GetSpriteTileStartByTag(spriteSheet.tag) * 32 + (void*)(OBJ_VRAM0); sPSSData->itemIcons[i].palIndex = AllocSpritePalette(PALTAG_ITEM_ICON_0 + i); sPSSData->itemIcons[i].palIndex *= 16; sPSSData->itemIcons[i].palIndex += 0x100; spriteTemplate.tileTag = GFXTAG_ITEM_ICON_0 + i; spriteTemplate.paletteTag = PALTAG_ITEM_ICON_0 + i; spriteId = CreateSprite(&spriteTemplate, 0, 0, 11); sPSSData->itemIcons[i].sprite = &gSprites[spriteId]; sPSSData->itemIcons[i].sprite->invisible = TRUE; sPSSData->itemIcons[i].active = FALSE; } } sPSSData->movingItemId = ITEM_NONE; } static void TryLoadItemIconAtPos(u8 cursorArea, u8 cursorPos) { u16 heldItem; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return; // If we've already loaded the item here, stop if (IsItemIconAtPosition(cursorArea, cursorPos)) return; switch (cursorArea) { case CURSOR_AREA_IN_BOX: if (!GetCurrentBoxMonData(cursorPos, MON_DATA_SANITY_HAS_SPECIES)) return; heldItem = GetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM); break; case CURSOR_AREA_IN_PARTY: if (cursorPos >= PARTY_SIZE || !GetMonData(&gPlayerParty[cursorPos], MON_DATA_SANITY_HAS_SPECIES)) return; heldItem = GetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM); break; default: return; } if (heldItem != ITEM_NONE) { const u32 *tiles = GetItemIconPic(heldItem); const u32 *pal = GetItemIconPalette(heldItem); u8 id = GetNewItemIconIdx(); SetItemIconPosition(id, cursorArea, cursorPos); LoadItemIconGfx(id, tiles, pal); SetItemIconAffineAnim(id, ITEM_ANIM_APPEAR); SetItemIconActive(id, TRUE); } } static void TryHideItemIconAtPos(u8 cursorArea, u8 cursorPos) { u8 id; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return; id = GetItemIconIdxByPosition(cursorArea, cursorPos); SetItemIconAffineAnim(id, ITEM_ANIM_DISAPPEAR); SetItemIconCallback(id, ITEM_CB_WAIT_ANIM, cursorArea, cursorPos); } static void TakeItemFromMon(u8 cursorArea, u8 cursorPos) { u8 id; u16 itemId; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return; id = GetItemIconIdxByPosition(cursorArea, cursorPos); itemId = ITEM_NONE; SetItemIconAffineAnim(id, ITEM_ANIM_PICK_UP); SetItemIconCallback(id, ITEM_CB_TO_HAND, cursorArea, cursorPos); SetItemIconPosition(id, CURSOR_AREA_IN_HAND, 0); if (cursorArea == CURSOR_AREA_IN_BOX) { SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &itemId); SetBoxMonIconObjMode(cursorPos, 1); } else { SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &itemId); SetPartyMonIconObjMode(cursorPos, 1); } sPSSData->movingItemId = sPSSData->displayMonItemId; } static void InitItemIconInCursor(u16 itemId) { const u32 *tiles = GetItemIconPic(itemId); const u32 *pal = GetItemIconPalette(itemId); u8 id = GetNewItemIconIdx(); LoadItemIconGfx(id, tiles, pal); SetItemIconAffineAnim(id, ITEM_ANIM_LARGE); SetItemIconCallback(id, ITEM_CB_TO_HAND, CURSOR_AREA_IN_BOX, 0); SetItemIconPosition(id, CURSOR_AREA_IN_HAND, 0); SetItemIconActive(id, TRUE); sPSSData->movingItemId = itemId; } static void SwapItemsWithMon(u8 cursorArea, u8 cursorPos) { u8 id; u16 itemId; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return; id = GetItemIconIdxByPosition(cursorArea, cursorPos); SetItemIconAffineAnim(id, ITEM_ANIM_PICK_UP); SetItemIconCallback(id, ITEM_CB_SWAP_TO_HAND, CURSOR_AREA_IN_HAND, 0); if (cursorArea == CURSOR_AREA_IN_BOX) { itemId = GetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM); SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &sPSSData->movingItemId); sPSSData->movingItemId = itemId; } else { itemId = GetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM); SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &sPSSData->movingItemId); sPSSData->movingItemId = itemId; } id = GetItemIconIdxByPosition(CURSOR_AREA_IN_HAND, 0); SetItemIconAffineAnim(id, ITEM_ANIM_PUT_DOWN); SetItemIconCallback(id, ITEM_CB_SWAP_TO_MON, cursorArea, cursorPos); } static void GiveItemToMon(u8 cursorArea, u8 cursorPos) { u8 id; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return; id = GetItemIconIdxByPosition(CURSOR_AREA_IN_HAND, 0); SetItemIconAffineAnim(id, ITEM_ANIM_PUT_DOWN); SetItemIconCallback(id, ITEM_CB_TO_MON, cursorArea, cursorPos); if (cursorArea == CURSOR_AREA_IN_BOX) { SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &sPSSData->movingItemId); SetBoxMonIconObjMode(cursorPos, 0); } else { SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &sPSSData->movingItemId); SetPartyMonIconObjMode(cursorPos, 0); } } static void MoveItemFromMonToBag(u8 cursorArea, u8 cursorPos) { u8 id; u16 itemId; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return; itemId = ITEM_NONE; id = GetItemIconIdxByPosition(cursorArea, cursorPos); SetItemIconAffineAnim(id, ITEM_ANIM_DISAPPEAR); SetItemIconCallback(id, ITEM_CB_WAIT_ANIM, cursorArea, cursorPos); if (cursorArea == CURSOR_AREA_IN_BOX) { SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &itemId); SetBoxMonIconObjMode(cursorPos, 1); } else { SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &itemId); SetPartyMonIconObjMode(cursorPos, 1); } } static void MoveItemFromCursorToBag(void) { if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { u8 id = GetItemIconIdxByPosition(CURSOR_AREA_IN_HAND, 0); SetItemIconAffineAnim(id, ITEM_ANIM_PUT_AWAY); SetItemIconCallback(id, ITEM_CB_WAIT_ANIM, CURSOR_AREA_IN_HAND, 0); } } // The party menu is being closed, if the cursor is on // a Pokémon that has a held item make sure it slides // up along with the closing menu. static void MoveHeldItemWithPartyMenu(void) { s32 i; if (sPSSData->boxOption != OPTION_MOVE_ITEMS) return; for (i = 0; i < MAX_ITEM_ICONS; i++) { if (sPSSData->itemIcons[i].active && sPSSData->itemIcons[i].area == CURSOR_AREA_IN_PARTY) SetItemIconCallback(i, ITEM_CB_HIDE_PARTY, CURSOR_AREA_IN_HAND, 0); } } static bool8 IsItemIconAnimActive(void) { s32 i; for (i = 0; i < MAX_ITEM_ICONS; i++) { if (sPSSData->itemIcons[i].active) { if (!sPSSData->itemIcons[i].sprite->affineAnimEnded && sPSSData->itemIcons[i].sprite->affineAnimBeginning) return TRUE; if (sPSSData->itemIcons[i].sprite->callback != SpriteCallbackDummy && sPSSData->itemIcons[i].sprite->callback != SpriteCB_ItemIcon_SetPosToCursor) return TRUE; } } return FALSE; } static bool8 IsMovingItem(void) { s32 i; if (sPSSData->boxOption == OPTION_MOVE_ITEMS) { for (i = 0; i < MAX_ITEM_ICONS; i++) { if (sPSSData->itemIcons[i].active && sPSSData->itemIcons[i].area == CURSOR_AREA_IN_HAND) return TRUE; } } return FALSE; } static const u8 *GetMovingItemName(void) { return ItemId_GetName(sPSSData->movingItemId); } static u16 GetMovingItemId(void) { return sPSSData->movingItemId; } static u8 GetNewItemIconIdx(void) { u8 i; for (i = 0; i < MAX_ITEM_ICONS; i++) { if (!sPSSData->itemIcons[i].active) { sPSSData->itemIcons[i].active = TRUE; return i; } } return MAX_ITEM_ICONS; } static bool32 IsItemIconAtPosition(u8 cursorArea, u8 cursorPos) { s32 i; for (i = 0; i < MAX_ITEM_ICONS; i++) { if (sPSSData->itemIcons[i].active && sPSSData->itemIcons[i].area == cursorArea && sPSSData->itemIcons[i].pos == cursorPos) return TRUE; } return FALSE; } static u8 GetItemIconIdxByPosition(u8 cursorArea, u8 cursorPos) { u8 i; for (i = 0; i < MAX_ITEM_ICONS; i++) { if (sPSSData->itemIcons[i].active && sPSSData->itemIcons[i].area == cursorArea && sPSSData->itemIcons[i].pos == cursorPos) return i; } return MAX_ITEM_ICONS; } static u8 GetItemIconIdxBySprite(struct Sprite *sprite) { u8 i; for (i = 0; i < MAX_ITEM_ICONS; i++) { if (sPSSData->itemIcons[i].active && sPSSData->itemIcons[i].sprite == sprite) return i; } return MAX_ITEM_ICONS; } static void SetItemIconPosition(u8 id, u8 cursorArea, u8 cursorPos) { u8 x, y; if (id >= MAX_ITEM_ICONS) return; switch (cursorArea) { case CURSOR_AREA_IN_BOX: x = cursorPos % IN_BOX_COLUMNS; y = cursorPos / IN_BOX_COLUMNS; sPSSData->itemIcons[id].sprite->pos1.x = (24 * x) + 112; sPSSData->itemIcons[id].sprite->pos1.y = (24 * y) + 56; sPSSData->itemIcons[id].sprite->oam.priority = 2; break; case CURSOR_AREA_IN_PARTY: if (cursorPos == 0) { sPSSData->itemIcons[id].sprite->pos1.x = 116; sPSSData->itemIcons[id].sprite->pos1.y = 76; } else { sPSSData->itemIcons[id].sprite->pos1.x = 164; sPSSData->itemIcons[id].sprite->pos1.y = 24 * (cursorPos - 1) + 28; } sPSSData->itemIcons[id].sprite->oam.priority = 1; break; } sPSSData->itemIcons[id].area = cursorArea; sPSSData->itemIcons[id].pos = cursorPos; } static void LoadItemIconGfx(u8 id, const u32 *itemTiles, const u32 *itemPal) { s32 i; if (id >= MAX_ITEM_ICONS) return; CpuFastFill(0, sPSSData->field_42C4, 0x200); LZ77UnCompWram(itemTiles, sPSSData->tileBuffer); for (i = 0; i < 3; i++) CpuFastCopy(&sPSSData->tileBuffer[i * 0x60], &sPSSData->field_42C4[i * 0x80], 0x60); CpuFastCopy(sPSSData->field_42C4, sPSSData->itemIcons[id].tiles, 0x200); LZ77UnCompWram(itemPal, sPSSData->field_42C4); LoadPalette(sPSSData->field_42C4, sPSSData->itemIcons[id].palIndex, 0x20); } static void SetItemIconAffineAnim(u8 id, u8 animNum) { if (id >= MAX_ITEM_ICONS) return; StartSpriteAffineAnim(sPSSData->itemIcons[id].sprite, animNum); } #define sItemIconId data[0] #define sState data[0] #define sCursorArea data[6] #define sCursorPos data[7] static void SetItemIconCallback(u8 id, u8 callbackId, u8 cursorArea, u8 cursorPos) { if (id >= MAX_ITEM_ICONS) return; switch (callbackId) { case ITEM_CB_WAIT_ANIM: sPSSData->itemIcons[id].sprite->sItemIconId = id; sPSSData->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_WaitAnim; break; case ITEM_CB_TO_HAND: sPSSData->itemIcons[id].sprite->sState = 0; sPSSData->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_ToHand; break; case ITEM_CB_TO_MON: sPSSData->itemIcons[id].sprite->sState = 0; sPSSData->itemIcons[id].sprite->sCursorArea = cursorArea; sPSSData->itemIcons[id].sprite->sCursorPos = cursorPos; sPSSData->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_ToMon; break; case ITEM_CB_SWAP_TO_HAND: sPSSData->itemIcons[id].sprite->sState = 0; sPSSData->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_SwapToHand; sPSSData->itemIcons[id].sprite->sCursorArea = cursorArea; sPSSData->itemIcons[id].sprite->sCursorPos = cursorPos; break; case ITEM_CB_SWAP_TO_MON: sPSSData->itemIcons[id].sprite->sState = 0; sPSSData->itemIcons[id].sprite->sCursorArea = cursorArea; sPSSData->itemIcons[id].sprite->sCursorPos = cursorPos; sPSSData->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_SwapToMon; break; case ITEM_CB_HIDE_PARTY: // If cursor is on a Pokémon with a held item and // the player closes the party menu, have the held // item follow the Pokémon as the menu slides out sPSSData->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_HideParty; break; } } static void SetItemIconActive(u8 id, bool8 active) { if (id >= MAX_ITEM_ICONS) return; sPSSData->itemIcons[id].active = active; sPSSData->itemIcons[id].sprite->invisible = (active == FALSE); } static const u32 *GetItemIconPic(u16 itemId) { return GetItemIconPicOrPalette(itemId, 0); } static const u32 *GetItemIconPalette(u16 itemId) { return GetItemIconPicOrPalette(itemId, 1); } static void PrintItemDescription(void) { const u8 *description; if (IsMovingItem()) description = ItemId_GetDescription(sPSSData->movingItemId); else description = ItemId_GetDescription(sPSSData->displayMonItemId); FillWindowPixelBuffer(2, PIXEL_FILL(1)); AddTextPrinterParameterized5(2, 1, description, 4, 0, 0, NULL, 0, 1); } static void InitItemInfoWindow(void) { sPSSData->itemInfoWindowOffset = 21; LoadBgTiles(0, sItemInfoFrame_Gfx, 0x80, 0x13A); DrawItemInfoWindow(0); } static bool8 UpdateItemInfoWindowSlideIn(void) { s32 i, pos; if (sPSSData->itemInfoWindowOffset == 0) return FALSE; sPSSData->itemInfoWindowOffset--; pos = 21 - sPSSData->itemInfoWindowOffset; for (i = 0; i < pos; i++) WriteSequenceToBgTilemapBuffer(0, GetBgAttribute(0, BG_ATTR_BASETILE) + 0x14 + sPSSData->itemInfoWindowOffset + i, i, 13, 1, 7, 15, 21); DrawItemInfoWindow(pos); return (sPSSData->itemInfoWindowOffset != 0); } static bool8 UpdateItemInfoWindowSlideOut(void) { s32 i, pos; if (sPSSData->itemInfoWindowOffset == 22) return FALSE; if (sPSSData->itemInfoWindowOffset == 0) FillBgTilemapBufferRect(0, 0, 21, 12, 1, 9, 17); sPSSData->itemInfoWindowOffset++; pos = 21 - sPSSData->itemInfoWindowOffset; for (i = 0; i < pos; i++) { WriteSequenceToBgTilemapBuffer(0, GetBgAttribute(0, BG_ATTR_BASETILE) + 0x14 + sPSSData->itemInfoWindowOffset + i, i, 13, 1, 7, 15, 21); } if (pos >= 0) DrawItemInfoWindow(pos); FillBgTilemapBufferRect(0, 0, pos + 1, 12, 1, 9, 0x11); ScheduleBgCopyTilemapToVram(0); return TRUE; } static void DrawItemInfoWindow(u32 pos) { if (pos != 0) { FillBgTilemapBufferRect(0, 0x13A, 0, 0xC, pos, 1, 0xFu); FillBgTilemapBufferRect(0, 0x93A, 0, 0x14, pos, 1, 0xFu); } FillBgTilemapBufferRect(0, 0x13B, pos, 0xD, 1, 7, 0xFu); FillBgTilemapBufferRect(0, 0x13C, pos, 0xC, 1, 1, 0xFu); FillBgTilemapBufferRect(0, 0x13D, pos, 0x14, 1, 1, 0xFu); ScheduleBgCopyTilemapToVram(0); } static void SpriteCB_ItemIcon_WaitAnim(struct Sprite *sprite) { if (sprite->affineAnimEnded) { SetItemIconActive(sprite->sItemIconId, FALSE); sprite->callback = SpriteCallbackDummy; } } static void SpriteCB_ItemIcon_ToHand(struct Sprite *sprite) { switch (sprite->sState) { case 0: sprite->data[1] = sprite->pos1.x << 4; sprite->data[2] = sprite->pos1.y << 4; sprite->data[3] = 10; sprite->data[4] = 21; sprite->data[5] = 0; sprite->sState++; case 1: sprite->data[1] -= sprite->data[3]; sprite->data[2] -= sprite->data[4]; sprite->pos1.x = sprite->data[1] >> 4; sprite->pos1.y = sprite->data[2] >> 4; if (++sprite->data[5] > 11) sprite->callback = SpriteCB_ItemIcon_SetPosToCursor; break; } } static void SpriteCB_ItemIcon_SetPosToCursor(struct Sprite *sprite) { sprite->pos1.x = sPSSData->cursorSprite->pos1.x + 4; sprite->pos1.y = sPSSData->cursorSprite->pos1.y + sPSSData->cursorSprite->pos2.y + 8; sprite->oam.priority = sPSSData->cursorSprite->oam.priority; } static void SpriteCB_ItemIcon_ToMon(struct Sprite *sprite) { switch (sprite->sState) { case 0: sprite->data[1] = sprite->pos1.x << 4; sprite->data[2] = sprite->pos1.y << 4; sprite->data[3] = 10; sprite->data[4] = 21; sprite->data[5] = 0; sprite->sState++; case 1: sprite->data[1] += sprite->data[3]; sprite->data[2] += sprite->data[4]; sprite->pos1.x = sprite->data[1] >> 4; sprite->pos1.y = sprite->data[2] >> 4; if (++sprite->data[5] > 11) { SetItemIconPosition(GetItemIconIdxBySprite(sprite), sprite->sCursorArea, sprite->sCursorPos); sprite->callback = SpriteCallbackDummy; } break; } } static void SpriteCB_ItemIcon_SwapToHand(struct Sprite *sprite) { switch (sprite->sState) { case 0: sprite->data[1] = sprite->pos1.x << 4; sprite->data[2] = sprite->pos1.y << 4; sprite->data[3] = 10; sprite->data[4] = 21; sprite->data[5] = 0; sprite->sState++; case 1: sprite->data[1] -= sprite->data[3]; sprite->data[2] -= sprite->data[4]; sprite->pos1.x = sprite->data[1] >> 4; sprite->pos1.y = sprite->data[2] >> 4; sprite->pos2.x = gSineTable[sprite->data[5] * 8] >> 4; if (++sprite->data[5] > 11) { SetItemIconPosition(GetItemIconIdxBySprite(sprite), sprite->sCursorArea, sprite->sCursorPos); sprite->pos2.x = 0; sprite->callback = SpriteCB_ItemIcon_SetPosToCursor; } break; } } static void SpriteCB_ItemIcon_SwapToMon(struct Sprite *sprite) { switch (sprite->sState) { case 0: sprite->data[1] = sprite->pos1.x << 4; sprite->data[2] = sprite->pos1.y << 4; sprite->data[3] = 10; sprite->data[4] = 21; sprite->data[5] = 0; sprite->sState++; case 1: sprite->data[1] += sprite->data[3]; sprite->data[2] += sprite->data[4]; sprite->pos1.x = sprite->data[1] >> 4; sprite->pos1.y = sprite->data[2] >> 4; sprite->pos2.x = -(gSineTable[sprite->data[5] * 8] >> 4); if (++sprite->data[5] > 11) { SetItemIconPosition(GetItemIconIdxBySprite(sprite), sprite->sCursorArea, sprite->sCursorPos); sprite->callback = SpriteCallbackDummy; sprite->pos2.x = 0; } break; } } static void SpriteCB_ItemIcon_HideParty(struct Sprite *sprite) { sprite->pos1.y -= 8; if (sprite->pos1.y + sprite->pos2.y < -16) { sprite->callback = SpriteCallbackDummy; SetItemIconActive(GetItemIconIdxBySprite(sprite), FALSE); } } #undef sState #undef sItemIconId #undef sCursorArea #undef sCursorPos //------------------------------------------------------------------------------ // SECTION: General utility //------------------------------------------------------------------------------ // Unused, leftover from FRLG static void BackupPokemonStorage(void/*struct PokemonStorage * dest*/) { //*dest = *gPokemonStoragePtr; } // Unused, leftover from FRLG static void RestorePokemonStorage(void/*struct PokemonStorage * src*/) { //*gPokemonStoragePtr = *src; } // Functions here are general utility functions. u8 StorageGetCurrentBox(void) { return gPokemonStoragePtr->currentBox; } static void SetCurrentBox(u8 boxId) { if (boxId < TOTAL_BOXES_COUNT) gPokemonStoragePtr->currentBox = boxId; } u32 GetBoxMonDataAt(u8 boxId, u8 boxPosition, s32 request) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) return GetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], request); else return 0; } void SetBoxMonDataAt(u8 boxId, u8 boxPosition, s32 request, const void *value) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) SetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], request, value); } u32 GetCurrentBoxMonData(u8 boxPosition, s32 request) { return GetBoxMonDataAt(gPokemonStoragePtr->currentBox, boxPosition, request); } void SetCurrentBoxMonData(u8 boxPosition, s32 request, const void *value) { SetBoxMonDataAt(gPokemonStoragePtr->currentBox, boxPosition, request, value); } void GetBoxMonNickAt(u8 boxId, u8 boxPosition, u8 *dst) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) GetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], MON_DATA_NICKNAME, dst); else *dst = EOS; } u32 GetBoxMonLevelAt(u8 boxId, u8 boxPosition) { u32 lvl; if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT && GetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], MON_DATA_SANITY_HAS_SPECIES)) lvl = GetLevelFromBoxMonExp(&gPokemonStoragePtr->boxes[boxId][boxPosition]); #ifdef BUGFIX else #endif lvl = 0; return lvl; } void SetBoxMonNickAt(u8 boxId, u8 boxPosition, const u8 *nick) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) SetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], MON_DATA_NICKNAME, nick); } u32 GetAndCopyBoxMonDataAt(u8 boxId, u8 boxPosition, s32 request, void *dst) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) return GetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], request, dst); else return 0; } void SetBoxMonAt(u8 boxId, u8 boxPosition, struct BoxPokemon *src) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) gPokemonStoragePtr->boxes[boxId][boxPosition] = *src; } void CopyBoxMonAt(u8 boxId, u8 boxPosition, struct BoxPokemon *dst) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) *dst = gPokemonStoragePtr->boxes[boxId][boxPosition]; } void CreateBoxMonAt(u8 boxId, u8 boxPosition, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 personality, u8 otIDType, u32 otID) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) { CreateBoxMon(&gPokemonStoragePtr->boxes[boxId][boxPosition], species, level, fixedIV, hasFixedPersonality, personality, otIDType, otID); } } void ZeroBoxMonAt(u8 boxId, u8 boxPosition) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) ZeroBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition]); } void BoxMonAtToMon(u8 boxId, u8 boxPosition, struct Pokemon *dst) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) BoxMonToMon(&gPokemonStoragePtr->boxes[boxId][boxPosition], dst); } struct BoxPokemon *GetBoxedMonPtr(u8 boxId, u8 boxPosition) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) return &gPokemonStoragePtr->boxes[boxId][boxPosition]; else return NULL; } u8 *GetBoxNamePtr(u8 boxId) { if (boxId < TOTAL_BOXES_COUNT) return gPokemonStoragePtr->boxNames[boxId]; else return NULL; } static u8 GetBoxWallpaper(u8 boxId) { if (boxId < TOTAL_BOXES_COUNT) return gPokemonStoragePtr->boxWallpapers[boxId]; else return 0; } static void SetBoxWallpaper(u8 boxId, u8 wallpaperId) { if (boxId < TOTAL_BOXES_COUNT && wallpaperId < WALLPAPER_COUNT) gPokemonStoragePtr->boxWallpapers[boxId] = wallpaperId; } // For moving to the next Pokémon while viewing the summary screen s16 AdvanceStorageMonIndex(struct BoxPokemon *boxMons, u8 currIndex, u8 maxIndex, u8 mode) { s16 i; s16 direction = -1; if (mode == 0 || mode == 1) direction = 1; if (mode == 1 || mode == 3) { for (i = (s8)currIndex + direction; i >= 0 && i <= maxIndex; i += direction) { if (GetBoxMonData(&boxMons[i], MON_DATA_SPECIES) != SPECIES_NONE) return i; } } else { for (i = (s8)currIndex + direction; i >= 0 && i <= maxIndex; i += direction) { if (GetBoxMonData(&boxMons[i], MON_DATA_SPECIES) != SPECIES_NONE && !GetBoxMonData(&boxMons[i], MON_DATA_IS_EGG)) return i; } } return -1; } bool8 CheckFreePokemonStorageSpace(void) { s32 i, j; for (i = 0; i < TOTAL_BOXES_COUNT; i++) { for (j = 0; j < IN_BOX_COUNT; j++) { if (!GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_SANITY_HAS_SPECIES)) return TRUE; } } return FALSE; } bool32 CheckBoxMonSanityAt(u32 boxId, u32 boxPosition) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT && GetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], MON_DATA_SANITY_HAS_SPECIES) && !GetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], MON_DATA_SANITY_IS_EGG) && !GetBoxMonData(&gPokemonStoragePtr->boxes[boxId][boxPosition], MON_DATA_SANITY_IS_BAD_EGG)) return TRUE; else return FALSE; } u32 CountStorageNonEggMons(void) { s32 i, j; u32 count = 0; for (i = 0; i < TOTAL_BOXES_COUNT; i++) { for (j = 0; j < IN_BOX_COUNT; j++) { if (GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_SANITY_HAS_SPECIES) && !GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_SANITY_IS_EGG)) count++; } } return count; } u32 CountAllStorageMons(void) { s32 i, j; u32 count = 0; for (i = 0; i < TOTAL_BOXES_COUNT; i++) { for (j = 0; j < IN_BOX_COUNT; j++) { if (GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_SANITY_HAS_SPECIES) || GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_SANITY_IS_EGG)) count++; } } return count; } bool32 AnyStorageMonWithMove(u16 moveId) { u16 moves[] = {moveId, MOVES_COUNT}; s32 i, j; for (i = 0; i < TOTAL_BOXES_COUNT; i++) { for (j = 0; j < IN_BOX_COUNT; j++) { if (GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_SANITY_HAS_SPECIES) && !GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_SANITY_IS_EGG) && GetBoxMonData(&gPokemonStoragePtr->boxes[i][j], MON_DATA_KNOWN_MOVES, (u8*)moves)) return TRUE; } } return FALSE; } //------------------------------------------------------------------------------ // SECTION: Walda //------------------------------------------------------------------------------ void ResetWaldaWallpaper(void) { gSaveBlock1Ptr->waldaPhrase.iconId = 0; gSaveBlock1Ptr->waldaPhrase.patternId = 0; gSaveBlock1Ptr->waldaPhrase.patternUnlocked = FALSE; gSaveBlock1Ptr->waldaPhrase.colors[0] = RGB(21, 25, 30); gSaveBlock1Ptr->waldaPhrase.colors[1] = RGB(6, 12, 24); gSaveBlock1Ptr->waldaPhrase.text[0] = EOS; } void SetWaldaWallpaperLockedOrUnlocked(bool32 unlocked) { gSaveBlock1Ptr->waldaPhrase.patternUnlocked = unlocked; } bool32 IsWaldaWallpaperUnlocked(void) { return gSaveBlock1Ptr->waldaPhrase.patternUnlocked; } u32 GetWaldaWallpaperPatternId(void) { return gSaveBlock1Ptr->waldaPhrase.patternId; } void SetWaldaWallpaperPatternId(u8 id) { if (id < ARRAY_COUNT(sWaldaWallpapers)) gSaveBlock1Ptr->waldaPhrase.patternId = id; } u32 GetWaldaWallpaperIconId(void) { return gSaveBlock1Ptr->waldaPhrase.iconId; } void SetWaldaWallpaperIconId(u8 id) { if (id < ARRAY_COUNT(sWaldaWallpaperIcons)) gSaveBlock1Ptr->waldaPhrase.iconId = id; } u16 *GetWaldaWallpaperColorsPtr(void) { return gSaveBlock1Ptr->waldaPhrase.colors; } void SetWaldaWallpaperColors(u16 color1, u16 color2) { gSaveBlock1Ptr->waldaPhrase.colors[0] = color1; gSaveBlock1Ptr->waldaPhrase.colors[1] = color2; } u8 *GetWaldaPhrasePtr(void) { return gSaveBlock1Ptr->waldaPhrase.text; } void SetWaldaPhrase(const u8 *src) { StringCopy(gSaveBlock1Ptr->waldaPhrase.text, src); } bool32 IsWaldaPhraseEmpty(void) { return (gSaveBlock1Ptr->waldaPhrase.text[0] == EOS); } //------------------------------------------------------------------------------ // SECTION: TilemapUtil // // Handles 3 particular tilemaps in a somewhat unusual way. // For example, while the cursor is on the Close Box button it flashes between // two states alternately. Both these states are their own part of the same // tilemap that's always present. The utility shifts the tilemap up and down // to show/hide the states, and limits the view with a rectangle that only // reveals one at a time. // Each tilemap is tracked with a TILEMAPID that can be used to reference it. //------------------------------------------------------------------------------ struct TilemapUtil_RectData { s16 x; s16 y; u16 width; u16 height; s16 destX; s16 destY; }; struct TilemapUtil { struct TilemapUtil_RectData prev; // Only read in unused function struct TilemapUtil_RectData cur; const void *savedTilemap; // Only written in unused function const void *tilemap; u16 altWidth; u16 altHeight; // Never read u16 width; u16 height; // Never read u16 rowSize; // Never read u8 tileSize; u8 bg; bool8 active; }; EWRAM_DATA static struct TilemapUtil *sTilemapUtil = NULL; EWRAM_DATA static u16 sNumTilemapUtilIds = 0; static void TilemapUtil_Init(u8 count) { u16 i; sTilemapUtil = Alloc(sizeof(*sTilemapUtil) * count); sNumTilemapUtilIds = (sTilemapUtil == NULL) ? 0 : count; for (i = 0; i < sNumTilemapUtilIds; i++) { sTilemapUtil[i].savedTilemap = NULL; sTilemapUtil[i].active = FALSE; } } static void TilemapUtil_Free(void) { Free(sTilemapUtil); } // Unused static void TilemapUtil_UpdateAll(void) { s32 i; for (i = 0; i < sNumTilemapUtilIds; i++) { if (sTilemapUtil[i].active == TRUE) TilemapUtil_Update(i); } } struct { u16 width; u16 height; } static const sTilemapDimensions[][4] = { { { 256, 256}, { 512, 256}, { 256, 512}, { 512, 512}, }, { { 128, 128}, { 256, 256}, { 512, 512}, {1024, 1024}, }, }; static void TilemapUtil_SetMap(u8 id, u8 bg, const void *tilemap, u16 width, u16 height) { u16 bgScreenSize, bgType; if (id >= sNumTilemapUtilIds) return; sTilemapUtil[id].savedTilemap = NULL; sTilemapUtil[id].tilemap = tilemap; sTilemapUtil[id].bg = bg; sTilemapUtil[id].width = width; sTilemapUtil[id].height = height; bgScreenSize = GetBgAttribute(bg, BG_ATTR_SCREENSIZE); bgType = GetBgAttribute(bg, BG_ATTR_TYPE); sTilemapUtil[id].altWidth = sTilemapDimensions[bgType][bgScreenSize].width; sTilemapUtil[id].altHeight = sTilemapDimensions[bgType][bgScreenSize].height; if (bgType != 0) sTilemapUtil[id].tileSize = 1; else sTilemapUtil[id].tileSize = 2; sTilemapUtil[id].rowSize = sTilemapUtil[id].tileSize * width; sTilemapUtil[id].cur.width = width; sTilemapUtil[id].cur.height = height; sTilemapUtil[id].cur.x = 0; sTilemapUtil[id].cur.y = 0; sTilemapUtil[id].cur.destX = 0; sTilemapUtil[id].cur.destY = 0; sTilemapUtil[id].prev = sTilemapUtil[id].cur; sTilemapUtil[id].active = TRUE; } // Unused static void TilemapUtil_SetSavedMap(u8 id, const void *tilemap) { if (id >= sNumTilemapUtilIds) return; sTilemapUtil[id].savedTilemap = tilemap; sTilemapUtil[id].active = TRUE; } static void TilemapUtil_SetPos(u8 id, u16 x, u16 y) { if (id >= sNumTilemapUtilIds) return; sTilemapUtil[id].cur.destX = x; sTilemapUtil[id].cur.destY = y; sTilemapUtil[id].active = TRUE; } static void TilemapUtil_SetRect(u8 id, u16 x, u16 y, u16 width, u16 height) { if (id >= sNumTilemapUtilIds) return; sTilemapUtil[id].cur.x = x; sTilemapUtil[id].cur.y = y; sTilemapUtil[id].cur.width = width; sTilemapUtil[id].cur.height = height; sTilemapUtil[id].active = TRUE; } static void TilemapUtil_Move(u8 id, u8 mode, s8 val) { if (id >= sNumTilemapUtilIds) return; switch (mode) { case 0: sTilemapUtil[id].cur.destX += val; sTilemapUtil[id].cur.width -= val; break; case 1: sTilemapUtil[id].cur.x += val; sTilemapUtil[id].cur.width += val; break; case 2: sTilemapUtil[id].cur.destY += val; sTilemapUtil[id].cur.height -= val; break; case 3: sTilemapUtil[id].cur.y -= val; sTilemapUtil[id].cur.height += val; break; case 4: sTilemapUtil[id].cur.destX += val; break; case 5: sTilemapUtil[id].cur.destY += val; break; } sTilemapUtil[id].active = TRUE; } static void TilemapUtil_Update(u8 id) { if (id >= sNumTilemapUtilIds) return; if (sTilemapUtil[id].savedTilemap != NULL) TilemapUtil_DrawPrev(id); // Never called, above always FALSE TilemapUtil_Draw(id); sTilemapUtil[id].prev = sTilemapUtil[id].cur; } static void TilemapUtil_DrawPrev(u8 id) { s32 i; u32 adder = sTilemapUtil[id].tileSize * sTilemapUtil[id].altWidth; const void *tiles = (sTilemapUtil[id].savedTilemap + (adder * sTilemapUtil[id].prev.destY)) + (sTilemapUtil[id].tileSize * sTilemapUtil[id].prev.destX); for (i = 0; i < sTilemapUtil[id].prev.height; i++) { CopyToBgTilemapBufferRect(sTilemapUtil[id].bg, tiles, sTilemapUtil[id].prev.destX, sTilemapUtil[id].prev.destY + i, sTilemapUtil[id].prev.width, 1); tiles += adder; } } static void TilemapUtil_Draw(u8 id) { s32 i; u32 adder = sTilemapUtil[id].tileSize * sTilemapUtil[id].width; const void *tiles = (sTilemapUtil[id].tilemap + (adder * sTilemapUtil[id].cur.y)) + (sTilemapUtil[id].tileSize * sTilemapUtil[id].cur.x); for (i = 0; i < sTilemapUtil[id].cur.height; i++) { CopyToBgTilemapBufferRect(sTilemapUtil[id].bg, tiles, sTilemapUtil[id].cur.destX, sTilemapUtil[id].cur.destY + i, sTilemapUtil[id].cur.width, 1); tiles += adder; } } EWRAM_DATA static struct UnkStruct_2000020 *gUnknown_02039D8C = NULL; static void sub_80D2A90(struct UnkStruct_2000020 *arg0, struct UnkStruct_2000028 *arg1, u32 arg2) { gUnknown_02039D8C = arg0; arg0->unk_00 = arg1; arg0->unk_05 = arg2; arg0->unk_04 = 0; } static void sub_80D2AA4(void) { u16 i; if (gUnknown_02039D8C->unk_04) { for (i = 0; i < gUnknown_02039D8C->unk_04; i++) { struct UnkStruct_2000028 *unkStruct = &gUnknown_02039D8C->unk_00[i]; unkStruct->unk_0c(unkStruct); } gUnknown_02039D8C->unk_04 = 0; } } static bool8 sub_80D2AEC(u8 *dest, u16 dLeft, u16 dTop, const u8 *src, u16 sLeft, u16 sTop, u16 width, u16 height, u16 unkArg) { struct UnkStruct_2000028 *unkStruct; if (gUnknown_02039D8C->unk_04 >= gUnknown_02039D8C->unk_05) return FALSE; unkStruct = &gUnknown_02039D8C->unk_00[gUnknown_02039D8C->unk_04++]; unkStruct->unk_08 = width * 2; unkStruct->unk_04 = dest + 2 * (dTop * 32 + dLeft); unkStruct->unk_00 = src + 2 * (sTop * unkArg + sLeft); unkStruct->newField = height; unkStruct->unk_0a = unkArg; unkStruct->unk_0c = sub_80D2B88; return TRUE; } static void sub_80D2B88(struct UnkStruct_2000028 *unkStruct) { u16 i; for (i = 0; i < unkStruct->newField; i++) { CpuSet(unkStruct->unk_00, unkStruct->unk_04, (unkStruct->unk_08 / 2)); unkStruct->unk_04 += 64; unkStruct->unk_00 += (unkStruct->unk_0a * 2); } } static bool8 sub_80D2BC0(void *dest, u16 dLeft, u16 dTop, u16 width, u16 height) { struct UnkStruct_2000028 *unkStruct; if (gUnknown_02039D8C->unk_04 >= gUnknown_02039D8C->unk_05) return FALSE; unkStruct = &gUnknown_02039D8C->unk_00[gUnknown_02039D8C->unk_04++]; unkStruct->unk_08 = width * 2; unkStruct->unk_04 = dest + ((dTop * 32) + dLeft) * 2; unkStruct->newField = height; unkStruct->unk_0c = sub_80D2C1C; return TRUE; } static void sub_80D2C1C(struct UnkStruct_2000028 *unkStruct) { u16 i; for (i = 0; i < unkStruct->newField; i++) { Dma3FillLarge_(0, unkStruct->unk_04, unkStruct->unk_08, 16); unkStruct->unk_04 += 64; } }