tumbledemerald-legacy/src/pokemon_storage_system.c

10080 lines
279 KiB
C

#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/moves.h"
#include "constants/rgb.h"
#include "constants/songs.h"
/*
NOTE: This file is large. Some general groups of functions have
been labeled with commented headers to make navigation easier.
Search for "SECTION:" to locate them. These sections are not
hard and fast rules, but give a basic idea of where certain
types of functions are likely located.
*/
// 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_MISC_1,
PALTAG_MARKING_COMBO,
PALTAG_BOX_TITLE,
PALTAG_MISC_2,
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,
};
// IDs for TilemapUtil
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 UnkUtilData
{
const u8 *src;
u8 *dest;
u16 size;
u16 unk;
u16 height;
void (*func)(struct UnkUtilData *data);
};
struct UnkUtil
{
struct UnkUtilData *data;
u8 numActive;
u8 max;
};
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 isReopening;
u8 taskId;
struct UnkUtil unkUtil;
struct UnkUtilData unkUtilData[8];
u16 partyMenuTilemapBuffer[0x108];
u16 partyMenuUnused1; // Never read
u16 partyMenuY;
u8 partyMenuUnused2; // 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 filler1[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 wallpaperPalBits;
u8 filler2[80]; // Unused
u16 unkUnused1; // 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 **shiftMonSpritePtr;
struct Sprite **releaseMonSpritePtr;
u16 numIconsPerSpecies[MAX_MON_ICONS];
u16 iconSpeciesList[MAX_MON_ICONS];
u16 boxSpecies[IN_BOX_COUNT];
u32 boxPersonalities[IN_BOX_COUNT];
u8 incomingBoxId;
u8 shiftTimer;
u8 numPartyToCompact;
u16 iconScrollDistance;
s16 iconScrollPos;
s16 iconScrollSpeed;
u16 iconScrollNumIncoming;
u8 iconScrollCurColumn;
s8 iconScrollDirection; // Unnecessary duplicate of scrollDirection
u8 iconScrollState;
u8 iconScrollToBoxId; // Unnecessary duplicate of scrollToBoxId
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 shiftBoxId;
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 unkUnused2; // Unused
u16 displayMonPalOffset;
u16 *displayMonTilePtr;
struct Sprite *displayMonSprite;
u16 displayMonPalBuffer[0x40];
u8 tileBuffer[0x800];
u8 unusedBuffer[0x1800]; // Unused
u8 itemIconBuffer[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 *sStorage = 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;
// Main tasks
static void EnterPokeStorage(u8);
static void Task_InitPokeStorage(u8);
static void Task_PlaceMon(u8);
static void Task_ChangeScreen(u8);
static void Task_ShowPokeStorage(u8);
static void Task_OnBPressed(u8);
static void Task_HandleBoxOptions(u8);
static void Task_OnSelectedMon(u8);
static void Task_OnCloseBoxPressed(u8);
static void Task_HidePartyPokemon(u8);
static void Task_DepositMenu(u8);
static void Task_MoveMon(u8);
static void Task_GiveMovingItemToMon(u8);
static void Task_SwitchSelectedItem(u8);
static void Task_TakeItemForMoving(u8);
static void Task_WithdrawMon(u8);
static void Task_ShiftMon(u8);
static void Task_ShowPartyPokemon(u8);
static void Task_ShowItemInfo(u8);
static void Task_GiveItemFromBag(u8);
static void Task_ItemToBag(u8);
static void Task_TakeItemForMoving(u8);
static void Task_ShowMarkMenu(u8);
static void Task_ShowMonSummary(u8);
static void Task_ReleaseMon(u8);
static void Task_ReshowPokeStorage(u8);
static void Task_PokeStorageMain(u8);
static void Task_JumpBox(u8);
static void Task_HandleWallpapers(u8);
static void Task_NameBox(u8);
static void Task_PrintCantStoreMail(u8);
static void Task_HandleMovingMonFromParty(u8);
// Input handlers
static u8 InBoxInput_Normal(void);
static u8 InBoxInput_MovingMultiple(void);
static u8 InBoxInput_SelectingMultiple(void);
static u8 HandleInput(void);
static void AddBoxOptionsMenu(void);
static u8 SetSelectionMenuTexts(void);
static bool8 SetMenuTexts_Mon(void);
static bool8 SetMenuTexts_Item(void);
// Choose box menu
static void ChooseBoxMenu_CreateSprites(u8);
static void ChooseBoxMenu_DestroySprites(void);
static void ChooseBoxMenu_MoveLeft(void);
static void ChooseBoxMenu_MoveRight(void);
static void ChooseBoxMenu_PrintInfo(void);
static void SpriteCB_ChooseBoxArrow(struct Sprite *);
// Options menus
static void InitMenu(void);
static void SetMenuText(u8);
static s8 GetMenuItemTextId(u8);
static void AddMenu(void);
static bool8 IsMenuLoading(void);
static s16 HandleMenuInput(void);
static void RemoveMenu(void);
// Pokémon sprites
static void InitMonIconFields(void);
static void SpriteCB_BoxMonIconScrollOut(struct Sprite *);
static void GetIncomingBoxMonData(u8);
static void CreatePartyMonsSprites(bool8);
static void CompactPartySprites(void);
static u8 GetNumPartySpritesCompacting(void);
static void MovePartySpriteToNextSlot(struct Sprite *, u16);
static void SpriteCB_MovePartyMonToNextSlot(struct Sprite *);
static void MovePartySprites(s16);
static void DestroyAllPartyMonIcons(void);
static void ReshowReleaseMon(void);
static bool8 ResetReleaseMonSpritePtr(void);
static void SetMovingMonPriority(u8);
static void SpriteCB_HeldMon(struct Sprite *);
static struct Sprite *CreateMonIconSprite(u16, u32, s16, s16, u8, u8);
static void DestroyBoxMonIcon(struct Sprite *);
// Pokémon data
static void MoveMon(void);
static void PlaceMon(void);
static void RefreshDisplayMon(void);
static void SetMovingMonData(u8, u8);
static void SetPlacedMonData(u8, u8);
static void PurgeMonOrBoxMon(u8, u8);
static void SetShiftedMonData(u8, u8);
static bool8 TryStorePartyMonInBox(u8);
static void ResetSelectionAfterDeposit(void);
static void InitReleaseMon(void);
static bool8 TryHideReleaseMon(void);
static void InitCanReleaseMonVars(void);
static void ReleaseMon(void);
static bool32 AtLeastThreeUsableMons(void);
static s8 RunCanReleaseMon(void);
static void SaveMovingMon(void);
static void LoadSavedMovingMon(void);
static void InitSummaryScreenData(void);
static void SetSelectionAfterSummaryScreen(void);
static void SetMonMarkings(u8);
static bool8 IsRemovingLastPartyMon(void);
static bool8 CanShiftMon(void);
static bool8 IsMonBeingMoved(void);
static void TryRefreshDisplayMon(void);
static void ReshowDisplayMon(void);
static void SetDisplayMonData(void *, u8);
// 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);
// 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 CreateItemIconSprites(void);
static void TryLoadItemIconAtPos(u8, u8);
static void TryHideItemIconAtPos(u8, u8);
static void TakeItemFromMon(u8, u8);
static void InitItemIconInCursor(u16);
static void SwapItemsWithMon(u8, u8);
static void GiveItemToMon(u8, u8);
static void MoveItemFromMonToBag(u8, u8);
static void MoveItemFromCursorToBag(void);
static void MoveHeldItemWithPartyMenu(void);
static bool8 IsItemIconAnimActive(void);
static bool8 IsMovingItem(void);
static const u8 *GetMovingItemName(void);
static u16 GetMovingItemId(void);
static void PrintItemDescription(void);
static void InitItemInfoWindow(void);
static bool8 UpdateItemInfoWindowSlideIn(void);
static bool8 UpdateItemInfoWindowSlideOut(void);
static void DrawItemInfoWindow(u32);
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 *);
// Cursor
static void CreateCursorSprites(void);
static void ToggleCursorAutoAction(void);
static u8 GetCursorPosition(void);
static void StartCursorAnim(u8);
static void TryHideItemAtCursor(void);
static void TryShowItemAtCursor(void);
static void InitCursor(void);
static void InitCursorOnReopen(void);
static void GetCursorCoordsByPos(u8, u8, u16 *, u16 *);
static bool8 UpdateCursorPos(void);
static void DoCursorNewPosUpdate(void);
static void SetCursorInParty(void);
static void SetCursorBoxPosition(u8);
static void ClearSavedCursorPos(void);
static void SaveCursorPos(void);
static u8 GetSavedCursorPos(void);
static void InitMonPlaceChange(u8);
static bool8 DoMonPlaceChange(void);
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 bool8 MonPlaceChange_CursorDown(void);
static bool8 MonPlaceChange_CursorUp(void);
static void TrySetCursorFistAnim(void);
static bool8 IsCursorOnCloseBox(void);
static bool8 IsCursorOnBoxTitle(void);
static bool8 IsCursorInBox(void);
// Scroll arrows
static void CreateBoxScrollArrows(void);
static void StartBoxScrollArrowsSlide(s8);
static void StopBoxScrollArrowsSlide(void);
static void AnimateBoxScrollArrows(bool8);
static void SpriteCB_Arrow(struct Sprite *);
static struct Sprite *CreateChooseBoxArrows(u16, u16, u8, u8, u8);
// Box title
static void InitBoxTitle(u8);
static void CreateIncomingBoxTitle(u8, s8);
static void CycleBoxTitleSprites(void);
static void SpriteCB_IncomingBoxTitle(struct Sprite *);
static void SpriteCB_OutgoingBoxTitle(struct Sprite *);
static void CycleBoxTitleColor(void);
static s16 GetBoxTitleBaseX(const u8 *);
// Wallpaper
static void SetWallpaperForCurrentBox(u8);
static bool8 DoWallpaperGfxChange(void);
static void LoadWallpaperGfx(u8, s8);
static bool32 WaitForWallpaperGfxLoad(void);
static void DrawWallpaper(const void *, s8, u8);
static void TrimOldWallpaper(void *);
static void AddWallpaperSetsMenu(void);
static void AddWallpapersMenu(u8);
static u8 GetBoxWallpaper(u8);
static void SetBoxWallpaper(u8, u8);
// General box
static void CreateInitBoxTask(u8);
static bool8 IsInitBoxActive(void);
static void Task_InitBox(u8);
static void SetUpScrollToBox(u8);
static bool8 ScrollToBox(void);
static s8 DetermineBoxScrollDirection(u8);
static void SetCurrentBox(u8);
// Misc
static void CreateMainMenu(u8, s16 *);
static u8 GetCurrentBoxOption(void);
static void ScrollBackground(void);
static void UpdateCloseBoxButtonFlash(void);
static void GiveChosenBagItem(void);
static void SetUpHidePartyMenu(void);
static void LoadPokeStorageMenuGfx(void);
static void LoadWaveformSpritePalette(void);
static void InitPokeStorageBg0(void);
static void SetScrollingBackground(void);
static void UpdateBoxToSendMons(void);
static void InitCursorItemIcon(void);
static void InitPalettesAndSprites(void);
static void RefreshDisplayMonData(void);
static void CreateDisplayMonSprite(void);
static void CreateMarkingComboSprite(void);
static void CreateWaveformSprites(void);
static void ClearBottomWindow(void);
static void InitSupplementalTilemaps(void);
static void PrintDisplayMonInfo(void);
static void UpdateWaveformAnimation(void);
static void SetPartySlotTilemaps(void);
static void StopFlashingCloseBoxButton(void);
static void FreePokeStorageData(void);
static void UpdatePartySlotColors(void);
static void StartFlashingCloseBoxButton(void);
static void SetUpDoShowPartyMenu(void);
static void StartDisplayMonMosaicEffect(void);
static bool8 InitPokeStorageWindows(void);
static bool8 DoShowPartyMenu(void);
static bool8 HidePartyMenu(void);
static bool8 IsDisplayMosaicActive(void);
static void ShowYesNoWindow(s8);
static void UpdateCloseBoxButtonTilemap(bool8);
static void PrintMessage(u8 id);
static void LoadDisplayMonGfx(u16, u32);
static void SpriteCB_DisplayMonMosaic(struct Sprite *);
static void SetPartySlotTilemap(u8, bool8);
// Tilemap 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);
// Unknown utility
static void UnkUtil_Init(struct UnkUtil *, struct UnkUtilData *, u32);
static void UnkUtil_Run(void);
static void UnkUtil_CpuRun(struct UnkUtilData *);
static void UnkUtil_DmaRun(struct UnkUtilData *);
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 sUnused_Pal[] = INCBIN_U16("graphics/pokemon_storage/unused.gbapal");
static const u16 sUnknown_Pal[] = INCBIN_U16("graphics/pokemon_storage/unknown.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_MISC_2
};
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_MISC_2,
.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_MISC_2,
.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");
//------------------------------------------------------------------------------
// SECTION: Misc utility
//------------------------------------------------------------------------------
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, FONT_NORMAL, 0, 1, 0, 0, txtColor, TEXT_SKIP_DRAW, 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, FONT_NORMAL, 0, 2, 0, 0, txtColor, TEXT_SKIP_DRAW, 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;
}
// Unused
static void UnusedWriteRectCpu(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;
}
}
// Unused
static void UnusedWriteRectDma(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);
}
//------------------------------------------------------------------------------
// SECTION: Main menu
//
// The below functions generally handle the PC main menu where the main
// options can be selected (Withdraw, Deposit, etc.), as well as exiting
// Pokémon Storage back to this menu.
//------------------------------------------------------------------------------
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, FONT_NORMAL, sMainMenuTexts[task->tSelectedOption].desc, TEXT_SKIP_DRAW, NULL, 2, 1, 3);
CopyWindowToVram(0, COPYWIN_FULL);
CopyWindowToVram(task->tWindowId, COPYWIN_FULL);
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, FONT_NORMAL, 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, FONT_NORMAL, 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, FONT_NORMAL, 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, FONT_NORMAL, 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, FONT_NORMAL, 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, FONT_NORMAL, sMainMenuTexts[task->tSelectedOption].desc, 0, NULL, 2, 1, 3);
task->tState = STATE_HANDLE_INPUT;
}
break;
case STATE_ENTER_PC:
if (!gPaletteFade.active)
{
CleanupOverworldWindowsAndTilemaps();
EnterPokeStorage(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 FieldTask_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);
InitMenuInUpperLeftCornerNormal(windowId, OPTIONS_COUNT, whichMenu);
*windowIdPtr = windowId;
}
static void CB2_ExitPokeStorage(void)
{
sPreviousBoxOption = GetCurrentBoxOption();
gFieldCallback = FieldTask_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();
}
//------------------------------------------------------------------------------
// SECTION: Choose Box menu
//
// The below functions handle the popup menu that allows the player to cycle
// through the boxes and select one. Used when storing Pokémon in Deposit mode
// and for the Jump feature.
//------------------------------------------------------------------------------
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]->x = 196;
anim = 2;
}
if (i & 1)
{
sChooseBoxMenu->menuSideSprites[i]->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(FONT_NORMAL, boxName, 64);
AddTextPrinterParameterized3(windowId, FONT_NORMAL, center, 1, sChooseBoxMenu_TextColors, TEXT_SKIP_DRAW, 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(FONT_NORMAL, numBoxMonsText, 64);
AddTextPrinterParameterized3(windowId, FONT_NORMAL, center, 17, sChooseBoxMenu_TextColors, TEXT_SKIP_DRAW, 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->x2 += sprite->data[0];
if (++sprite->data[2] > 5)
{
sprite->data[2] = 0;
sprite->x2 = 0;
}
}
}
//------------------------------------------------------------------------------
// SECTION: Main tasks
//
// Below are the main task callbacks that handle the primary actions the
// player can take in the PC. The most 'important' of these tasks is the
// primary one, Task_PokeStorageMain. Also included are some basic
// initialization functions.
//------------------------------------------------------------------------------
static void VBlankCB_PokeStorage(void)
{
LoadOam();
ProcessSpriteCopyRequests();
UnkUtil_Run();
TransferPlttBuffer();
SetGpuReg(REG_OFFSET_BG2HOFS, sStorage->bg2_X);
}
static void CB2_PokeStorage(void)
{
RunTasks();
DoScheduledBgTilemapCopiesToVram();
ScrollBackground();
UpdateCloseBoxButtonFlash();
AnimateSprites();
BuildOamBuffer();
}
static void EnterPokeStorage(u8 boxOption)
{
ResetTasks();
sCurrentBoxOption = boxOption;
sStorage = Alloc(sizeof(*sStorage));
if (sStorage == NULL)
{
SetMainCallback2(CB2_ExitPokeStorage);
}
else
{
sStorage->boxOption = boxOption;
sStorage->isReopening = FALSE;
sMovingItemId = ITEM_NONE;
sStorage->state = 0;
sStorage->taskId = CreateTask(Task_InitPokeStorage, 3);
sLastUsedBox = StorageGetCurrentBox();
SetMainCallback2(CB2_PokeStorage);
}
}
static void CB2_ReturnToPokeStorage(void)
{
ResetTasks();
sStorage = Alloc(sizeof(*sStorage));
if (sStorage == NULL)
{
SetMainCallback2(CB2_ExitPokeStorage);
}
else
{
sStorage->boxOption = sCurrentBoxOption;
sStorage->isReopening = TRUE;
sStorage->state = 0;
sStorage->taskId = CreateTask(Task_InitPokeStorage, 3);
SetMainCallback2(CB2_PokeStorage);
}
}
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 ResetForPokeStorage(void)
{
ResetPaletteFade();
ResetSpriteData();
FreeSpriteTileRanges();
FreeAllSpritePalettes();
ClearDma3Requests();
gReservedSpriteTileCount = 0x280;
UnkUtil_Init(&sStorage->unkUtil, sStorage->unkUtilData, ARRAY_COUNT(sStorage->unkUtilData));
gKeyRepeatStartDelay = 20;
ClearScheduledBgCopiesToVram();
TilemapUtil_Init(TILEMAPID_COUNT);
TilemapUtil_SetMap(TILEMAPID_PKMN_DATA, 1, sPkmnData_Tilemap, 8, 4);
TilemapUtil_SetPos(TILEMAPID_PKMN_DATA, 1, 0);
sStorage->closeBoxFlashing = FALSE;
}
static void InitStartingPosData(void)
{
ClearSavedCursorPos();
sInPartyMenu = (sStorage->boxOption == OPTION_DEPOSIT);
sDepositBoxId = 0;
}
static void SetMonIconTransparency(void)
{
if (sStorage->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 SetPokeStorageTask(TaskFunc newFunc)
{
gTasks[sStorage->taskId].func = newFunc;
sStorage->state = 0;
}
static void Task_InitPokeStorage(u8 taskId)
{
switch (sStorage->state)
{
case 0:
SetVBlankCallback(NULL);
SetGpuReg(REG_OFFSET_DISPCNT, 0);
ResetForPokeStorage();
if (sStorage->isReopening)
{
switch (sWhichToReshow)
{
case SCREEN_CHANGE_NAME_BOX - 1:
// Return from naming box
LoadSavedMovingMon();
break;
case SCREEN_CHANGE_SUMMARY_SCREEN - 1:
// Return from summary screen
SetSelectionAfterSummaryScreen();
break;
case SCREEN_CHANGE_ITEM_FROM_BAG - 1:
// Return from bag menu
GiveChosenBagItem();
break;
}
}
LoadPokeStorageMenuGfx();
LoadWaveformSpritePalette();
break;
case 1:
if (!InitPokeStorageWindows())
{
SetPokeStorageTask(Task_ChangeScreen);
return;
}
break;
case 2:
PutWindowTilemap(0);
ClearWindowTilemap(1);
CpuFill32(0, (void *)VRAM, 0x200);
LoadUserWindowBorderGfx(1, 0xB, 0xE0);
break;
case 3:
ResetAllBgCoords();
if (!sStorage->isReopening)
InitStartingPosData();
break;
case 4:
InitMonIconFields();
if (!sStorage->isReopening)
InitCursor();
else
InitCursorOnReopen();
break;
case 5:
if (!MultiMove_Init())
{
SetPokeStorageTask(Task_ChangeScreen);
return;
}
else
{
SetScrollingBackground();
InitPokeStorageBg0();
}
break;
case 6:
InitPalettesAndSprites();
break;
case 7:
InitSupplementalTilemaps();
break;
case 8:
CreateInitBoxTask(StorageGetCurrentBox());
break;
case 9:
if (IsInitBoxActive())
return;
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
{
sStorage->markMenu.baseTileTag = GFXTAG_MARKING_MENU;
sStorage->markMenu.basePaletteTag = PALTAG_MARKING_MENU;
InitMonMarkingsMenu(&sStorage->markMenu);
BufferMonMarkingsMenuTiles();
}
else
{
CreateItemIconSprites();
InitCursorItemIcon();
}
break;
case 10:
SetMonIconTransparency();
if (!sStorage->isReopening)
{
BlendPalettes(PALETTES_ALL, 16, RGB_BLACK);
SetPokeStorageTask(Task_ShowPokeStorage);
}
else
{
BlendPalettes(PALETTES_ALL, 16, RGB_BLACK);
SetPokeStorageTask(Task_ReshowPokeStorage);
}
SetVBlankCallback(VBlankCB_PokeStorage);
return;
default:
return;
}
sStorage->state++;
}
static void Task_ShowPokeStorage(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PlaySE(SE_PC_LOGIN);
ComputerScreenOpenEffect(20, 0, 1);
sStorage->state++;
break;
case 1:
if (!IsComputerScreenOpenEffectActive())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
static void Task_ReshowPokeStorage(u8 taskId)
{
switch (sStorage->state)
{
case 0:
BeginNormalPaletteFade(PALETTES_ALL, -1, 0x10, 0, RGB_BLACK);
sStorage->state++;
break;
case 1:
if (!UpdatePaletteFade())
{
if (sWhichToReshow == SCREEN_CHANGE_ITEM_FROM_BAG - 1 && gSpecialVar_ItemId != ITEM_NONE)
{
PrintMessage(MSG_ITEM_IS_HELD);
sStorage->state++;
}
else
{
SetPokeStorageTask(Task_PokeStorageMain);
}
}
break;
case 2:
if (!IsDma3ManagerBusyWithBgCopy() && JOY_NEW(A_BUTTON | B_BUTTON))
{
ClearBottomWindow();
sStorage->state++;
}
break;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
// States for the outer switch in Task_PokeStorageMain
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 Task_PokeStorageMain(u8 taskId)
{
switch (sStorage->state)
{
case MSTATE_HANDLE_INPUT:
switch (HandleInput())
{
case INPUT_MOVE_CURSOR:
PlaySE(SE_SELECT);
sStorage->state = MSTATE_MOVE_CURSOR;
break;
case INPUT_SHOW_PARTY:
if (sStorage->boxOption != OPTION_MOVE_MONS && sStorage->boxOption != OPTION_MOVE_ITEMS)
{
PrintMessage(MSG_WHICH_ONE_WILL_TAKE);
sStorage->state = MSTATE_WAIT_MSG;
}
else
{
ClearSavedCursorPos();
SetPokeStorageTask(Task_ShowPartyPokemon);
}
break;
case INPUT_HIDE_PARTY:
if (sStorage->boxOption == OPTION_MOVE_MONS)
{
if (IsMonBeingMoved() && ItemIsMail(sStorage->displayMonItemId))
sStorage->state = MSTATE_ERROR_HAS_MAIL;
else
SetPokeStorageTask(Task_HidePartyPokemon);
}
else if (sStorage->boxOption == OPTION_MOVE_ITEMS)
{
SetPokeStorageTask(Task_HidePartyPokemon);
}
break;
case INPUT_CLOSE_BOX:
SetPokeStorageTask(Task_OnCloseBoxPressed);
break;
case INPUT_PRESSED_B:
SetPokeStorageTask(Task_OnBPressed);
break;
case INPUT_BOX_OPTIONS:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_HandleBoxOptions);
break;
case INPUT_IN_MENU:
SetPokeStorageTask(Task_OnSelectedMon);
break;
case INPUT_SCROLL_RIGHT:
PlaySE(SE_SELECT);
sStorage->newCurrBoxId = StorageGetCurrentBox() + 1;
if (sStorage->newCurrBoxId >= TOTAL_BOXES_COUNT)
sStorage->newCurrBoxId = 0;
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
{
SetUpScrollToBox(sStorage->newCurrBoxId);
sStorage->state = MSTATE_SCROLL_BOX;
}
else
{
TryHideItemAtCursor();
sStorage->state = MSTATE_SCROLL_BOX_ITEM;
}
break;
case INPUT_SCROLL_LEFT:
PlaySE(SE_SELECT);
sStorage->newCurrBoxId = StorageGetCurrentBox() - 1;
if (sStorage->newCurrBoxId < 0)
sStorage->newCurrBoxId = TOTAL_BOXES_COUNT - 1;
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
{
SetUpScrollToBox(sStorage->newCurrBoxId);
sStorage->state = MSTATE_SCROLL_BOX;
}
else
{
TryHideItemAtCursor();
sStorage->state = MSTATE_SCROLL_BOX_ITEM;
}
break;
case INPUT_DEPOSIT:
if (!IsRemovingLastPartyMon())
{
if (ItemIsMail(sStorage->displayMonItemId))
{
sStorage->state = MSTATE_ERROR_HAS_MAIL;
}
else
{
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_DepositMenu);
}
}
else
{
sStorage->state = MSTATE_ERROR_LAST_PARTY_MON;
}
break;
case INPUT_MOVE_MON:
if (IsRemovingLastPartyMon())
{
sStorage->state = MSTATE_ERROR_LAST_PARTY_MON;
}
else
{
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_MoveMon);
}
break;
case INPUT_SHIFT_MON:
if (!CanShiftMon())
{
sStorage->state = MSTATE_ERROR_LAST_PARTY_MON;
}
else
{
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_ShiftMon);
}
break;
case INPUT_WITHDRAW:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_WithdrawMon);
break;
case INPUT_PLACE_MON:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_PlaceMon);
break;
case INPUT_TAKE_ITEM:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_TakeItemForMoving);
break;
case INPUT_GIVE_ITEM:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_GiveMovingItemToMon);
break;
case INPUT_SWITCH_ITEMS:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_SwitchSelectedItem);
break;
case INPUT_MULTIMOVE_START:
PlaySE(SE_SELECT);
MultiMove_SetFunction(MULTIMOVE_START);
sStorage->state = MSTATE_MULTIMOVE_RUN;
break;
case INPUT_MULTIMOVE_SINGLE:
MultiMove_SetFunction(MULTIMOVE_CANCEL);
sStorage->state = MSTATE_MULTIMOVE_RUN_CANCEL;
break;
case INPUT_MULTIMOVE_CHANGE_SELECTION:
PlaySE(SE_SELECT);
MultiMove_SetFunction(MULTIMOVE_CHANGE_SELECTION);
sStorage->state = MSTATE_MULTIMOVE_RUN_MOVED;
break;
case INPUT_MULTIMOVE_GRAB_SELECTION:
MultiMove_SetFunction(MULTIMOVE_GRAB_SELECTION);
sStorage->state = MSTATE_MULTIMOVE_RUN;
break;
case INPUT_MULTIMOVE_MOVE_MONS:
PlaySE(SE_SELECT);
MultiMove_SetFunction(MULTIMOVE_MOVE_MONS);
sStorage->state = MSTATE_MULTIMOVE_RUN_MOVED;
break;
case INPUT_MULTIMOVE_PLACE_MONS:
PlaySE(SE_SELECT);
MultiMove_SetFunction(MULTIMOVE_PLACE_MONS);
sStorage->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 (sStorage->setMosaic)
StartDisplayMonMosaicEffect();
sStorage->state = MSTATE_HANDLE_INPUT;
}
break;
case MSTATE_SCROLL_BOX:
if (!ScrollToBox())
{
SetCurrentBox(sStorage->newCurrBoxId);
if (!sInPartyMenu && !IsMonBeingMoved())
{
RefreshDisplayMon();
StartDisplayMonMosaicEffect();
}
if (sStorage->boxOption == OPTION_MOVE_ITEMS)
{
TryShowItemAtCursor();
sStorage->state = MSTATE_WAIT_ITEM_ANIM;
}
else
{
sStorage->state = MSTATE_HANDLE_INPUT;
}
}
break;
case MSTATE_WAIT_MSG:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
sStorage->state = MSTATE_HANDLE_INPUT;
}
break;
case MSTATE_ERROR_LAST_PARTY_MON:
PlaySE(SE_FAILURE);
PrintMessage(MSG_LAST_POKE);
sStorage->state = MSTATE_WAIT_ERROR_MSG;
break;
case MSTATE_ERROR_HAS_MAIL:
PlaySE(SE_FAILURE);
PrintMessage(MSG_PLEASE_REMOVE_MAIL);
sStorage->state = MSTATE_WAIT_ERROR_MSG;
break;
case MSTATE_WAIT_ERROR_MSG:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
case MSTATE_MULTIMOVE_RUN:
if (!MultiMove_RunFunction())
sStorage->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())
SetPokeStorageTask(Task_MoveMon);
break;
case MSTATE_MULTIMOVE_RUN_MOVED:
if (!MultiMove_RunFunction())
{
if (sStorage->setMosaic)
StartDisplayMonMosaicEffect();
sStorage->state = MSTATE_HANDLE_INPUT;
}
break;
case MSTATE_SCROLL_BOX_ITEM:
if (!IsItemIconAnimActive())
{
SetUpScrollToBox(sStorage->newCurrBoxId);
sStorage->state = MSTATE_SCROLL_BOX;
}
break;
case MSTATE_WAIT_ITEM_ANIM:
if (!IsItemIconAnimActive())
sStorage->state = MSTATE_HANDLE_INPUT;
break;
}
}
static void Task_ShowPartyPokemon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
SetUpDoShowPartyMenu();
sStorage->state++;
break;
case 1:
if (!DoShowPartyMenu())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
static void Task_HidePartyPokemon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PlaySE(SE_SELECT);
SetUpHidePartyMenu();
sStorage->state++;
break;
case 1:
if (!HidePartyMenu())
{
SetCursorBoxPosition(GetSavedCursorPos());
sStorage->state++;
}
break;
case 2:
if (!UpdateCursorPos())
{
if (sStorage->setMosaic)
StartDisplayMonMosaicEffect();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_OnSelectedMon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
if (!IsDisplayMosaicActive())
{
PlaySE(SE_SELECT);
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
PrintMessage(MSG_IS_SELECTED);
else if (IsMovingItem() || sStorage->displayMonItemId != ITEM_NONE)
PrintMessage(MSG_IS_SELECTED2);
else
PrintMessage(MSG_GIVE_TO_MON);
AddMenu();
sStorage->state = 1;
}
break;
case 1:
if (!IsMenuLoading())
sStorage->state = 2;
break;
case 2:
switch (HandleMenuInput())
{
case MENU_B_PRESSED:
case MENU_CANCEL:
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
break;
case MENU_MOVE:
if (IsRemovingLastPartyMon())
{
sStorage->state = 3;
}
else
{
PlaySE(SE_SELECT);
ClearBottomWindow();
SetPokeStorageTask(Task_MoveMon);
}
break;
case MENU_PLACE:
PlaySE(SE_SELECT);
ClearBottomWindow();
SetPokeStorageTask(Task_PlaceMon);
break;
case MENU_SHIFT:
if (!CanShiftMon())
{
sStorage->state = 3;
}
else
{
PlaySE(SE_SELECT);
ClearBottomWindow();
SetPokeStorageTask(Task_ShiftMon);
}
break;
case MENU_WITHDRAW:
PlaySE(SE_SELECT);
ClearBottomWindow();
SetPokeStorageTask(Task_WithdrawMon);
break;
case MENU_STORE:
if (IsRemovingLastPartyMon())
{
sStorage->state = 3;
}
else if (ItemIsMail(sStorage->displayMonItemId))
{
sStorage->state = 4;
}
else
{
PlaySE(SE_SELECT);
ClearBottomWindow();
SetPokeStorageTask(Task_DepositMenu);
}
break;
case MENU_RELEASE:
if (IsRemovingLastPartyMon())
{
sStorage->state = 3;
}
else if (sStorage->displayMonIsEgg)
{
sStorage->state = 5; // Cannot release an Egg.
}
else if (ItemIsMail(sStorage->displayMonItemId))
{
sStorage->state = 4;
}
else
{
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_ReleaseMon);
}
break;
case MENU_SUMMARY:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_ShowMonSummary);
break;
case MENU_MARK:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_ShowMarkMenu);
break;
case MENU_TAKE:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_TakeItemForMoving);
break;
case MENU_GIVE:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_GiveMovingItemToMon);
break;
case MENU_BAG:
SetPokeStorageTask(Task_ItemToBag);
break;
case MENU_SWITCH:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_SwitchSelectedItem);
break;
case MENU_GIVE_2:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_GiveItemFromBag);
break;
case MENU_INFO:
SetPokeStorageTask(Task_ShowItemInfo);
break;
}
break;
case 3:
PlaySE(SE_FAILURE);
PrintMessage(MSG_LAST_POKE);
sStorage->state = 6;
break;
case 5:
PlaySE(SE_FAILURE);
PrintMessage(MSG_CANT_RELEASE_EGG);
sStorage->state = 6;
break;
case 4:
PlaySE(SE_FAILURE);
PrintMessage(MSG_PLEASE_REMOVE_MAIL);
sStorage->state = 6;
break;
case 6:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_MoveMon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
InitMonPlaceChange(CHANGE_GRAB);
sStorage->state++;
break;
case 1:
if (!DoMonPlaceChange())
{
if (sInPartyMenu)
SetPokeStorageTask(Task_HandleMovingMonFromParty);
else
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_PlaceMon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
InitMonPlaceChange(CHANGE_PLACE);
sStorage->state++;
break;
case 1:
if (!DoMonPlaceChange())
{
if (sInPartyMenu)
SetPokeStorageTask(Task_HandleMovingMonFromParty);
else
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_ShiftMon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
InitMonPlaceChange(CHANGE_SHIFT);
sStorage->state++;
break;
case 1:
if (!DoMonPlaceChange())
{
StartDisplayMonMosaicEffect();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_WithdrawMon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
if (CalculatePlayerPartyCount() == PARTY_SIZE)
{
PrintMessage(MSG_PARTY_FULL);
sStorage->state = 1;
}
else
{
SaveCursorPos();
InitMonPlaceChange(CHANGE_GRAB);
sStorage->state = 2;
}
break;
case 1:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
case 2:
if (!DoMonPlaceChange())
{
SetMovingMonPriority(1);
SetUpDoShowPartyMenu();
sStorage->state++;
}
break;
case 3:
if (!DoShowPartyMenu())
{
InitMonPlaceChange(CHANGE_PLACE);
sStorage->state++;
}
break;
case 4:
if (!DoMonPlaceChange())
{
UpdatePartySlotColors();
sStorage->state++;
}
break;
case 5:
SetPokeStorageTask(Task_HidePartyPokemon);
break;
}
}
static void Task_DepositMenu(u8 taskId)
{
u8 boxId;
switch (sStorage->state)
{
case 0:
PrintMessage(MSG_DEPOSIT_IN_WHICH_BOX);
LoadChooseBoxMenuGfx(&sStorage->chooseBoxMenu, GFXTAG_CHOOSE_BOX_MENU, PALTAG_MISC_1, 3, FALSE);
CreateChooseBoxMenuSprites(sDepositBoxId);
sStorage->state++;
break;
case 1:
boxId = HandleChooseBoxMenuInput();
switch (boxId)
{
case BOXID_NONE_CHOSEN:
break;
case BOXID_CANCELED:
ClearBottomWindow();
DestroyChooseBoxMenuSprites();
FreeChooseBoxMenu();
SetPokeStorageTask(Task_PokeStorageMain);
break;
default:
if (TryStorePartyMonInBox(boxId))
{
sDepositBoxId = boxId;
ClearBottomWindow();
DestroyChooseBoxMenuSprites();
FreeChooseBoxMenu();
sStorage->state = 2;
}
else
{
PrintMessage(MSG_BOX_IS_FULL);
sStorage->state = 4;
}
break;
}
break;
case 2:
CompactPartySlots();
CompactPartySprites();
sStorage->state++;
break;
case 3:
if (GetNumPartySpritesCompacting() == 0)
{
ResetSelectionAfterDeposit();
StartDisplayMonMosaicEffect();
UpdatePartySlotColors();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
case 4:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
PrintMessage(MSG_DEPOSIT_IN_WHICH_BOX);
sStorage->state = 1;
}
break;
}
}
static void Task_ReleaseMon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PrintMessage(MSG_RELEASE_POKE);
ShowYesNoWindow(1);
sStorage->state++;
// fallthrough
case 1:
switch (Menu_ProcessInputNoWrapClearOnChoose())
{
case MENU_B_PRESSED:
case 1: // No
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
break;
case 0: // Yes
ClearBottomWindow();
InitCanReleaseMonVars();
InitReleaseMon();
sStorage->state++;
break;
}
break;
case 2:
RunCanReleaseMon();
if (!TryHideReleaseMon())
{
while (1)
{
s8 canRelease = RunCanReleaseMon();
if (canRelease == TRUE)
{
sStorage->state++;
break;
}
else if (!canRelease)
{
sStorage->state = 8;
break;
}
}
}
break;
case 3:
ReleaseMon();
RefreshDisplayMonData();
PrintMessage(MSG_WAS_RELEASED);
sStorage->state++;
break;
case 4:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
PrintMessage(MSG_BYE_BYE);
sStorage->state++;
}
break;
case 5:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
if (sInPartyMenu)
{
CompactPartySlots();
CompactPartySprites();
sStorage->state++;
}
else
{
sStorage->state = 7;
}
}
break;
case 6:
if (GetNumPartySpritesCompacting() == 0)
{
RefreshDisplayMon();
StartDisplayMonMosaicEffect();
UpdatePartySlotColors();
sStorage->state++;
}
break;
case 7:
SetPokeStorageTask(Task_PokeStorageMain);
break;
case 8:
// Start "can't release" sequence
PrintMessage(MSG_WAS_RELEASED);
sStorage->state++;
break;
case 9:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
PrintMessage(MSG_SURPRISE);
sStorage->state++;
}
break;
case 10:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
ReshowReleaseMon();
sStorage->state++;
}
break;
case 11:
if (!ResetReleaseMonSpritePtr())
{
TrySetCursorFistAnim();
PrintMessage(MSG_CAME_BACK);
sStorage->state++;
}
break;
case 12:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
PrintMessage(MSG_WORRIED);
sStorage->state++;
}
break;
case 13:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_ShowMarkMenu(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PrintMessage(MSG_MARK_POKE);
sStorage->markMenu.markings = sStorage->displayMonMarkings;
OpenMonMarkingsMenu(sStorage->displayMonMarkings, 0xb0, 0x10);
sStorage->state++;
break;
case 1:
if (!HandleMonMarkingsMenuInput())
{
FreeMonMarkingsMenu();
ClearBottomWindow();
SetMonMarkings(sStorage->markMenu.markings);
RefreshDisplayMonData();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_TakeItemForMoving(u8 taskId)
{
switch (sStorage->state)
{
case 0:
if (!ItemIsMail(sStorage->displayMonItemId))
{
ClearBottomWindow();
sStorage->state++;
}
else
{
SetPokeStorageTask(Task_PrintCantStoreMail);
}
break;
case 1:
StartCursorAnim(CURSOR_ANIM_OPEN);
TakeItemFromMon(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition());
sStorage->state++;
break;
case 2:
if (!IsItemIconAnimActive())
{
StartCursorAnim(CURSOR_ANIM_FIST);
ClearBottomWindow();
RefreshDisplayMon();
PrintDisplayMonInfo();
sStorage->state++;
}
break;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
static void Task_GiveMovingItemToMon(u8 taskId)
{
switch (sStorage->state)
{
case 0:
ClearBottomWindow();
sStorage->state++;
break;
case 1:
StartCursorAnim(CURSOR_ANIM_OPEN);
GiveItemToMon(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition());
sStorage->state++;
break;
case 2:
if (!IsItemIconAnimActive())
{
StartCursorAnim(CURSOR_ANIM_BOUNCE);
RefreshDisplayMon();
PrintDisplayMonInfo();
PrintMessage(MSG_ITEM_IS_HELD);
sStorage->state++;
}
break;
case 3:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
sStorage->state++;
}
break;
case 4:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
static void Task_ItemToBag(u8 taskId)
{
switch (sStorage->state)
{
case 0:
if (!AddBagItem(sStorage->displayMonItemId, 1))
{
PlaySE(SE_FAILURE);
PrintMessage(MSG_BAG_FULL);
sStorage->state = 3;
}
else
{
PlaySE(SE_SELECT);
MoveItemFromMonToBag(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition());
sStorage->state = 1;
}
break;
case 1:
if (!IsItemIconAnimActive())
{
PrintMessage(MSG_PLACED_IN_BAG);
sStorage->state = 2;
}
break;
case 2:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
RefreshDisplayMon();
PrintDisplayMonInfo();
sStorage->state = 4;
}
break;
case 4:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
case 3:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_SwitchSelectedItem(u8 taskId)
{
switch (sStorage->state)
{
case 0:
if (!ItemIsMail(sStorage->displayMonItemId))
{
ClearBottomWindow();
sStorage->state++;
}
else
{
SetPokeStorageTask(Task_PrintCantStoreMail);
}
break;
case 1:
StartCursorAnim(CURSOR_ANIM_OPEN);
SwapItemsWithMon(sInPartyMenu ? CURSOR_AREA_IN_PARTY : CURSOR_AREA_IN_BOX, GetCursorPosition());
sStorage->state++;
break;
case 2:
if (!IsItemIconAnimActive())
{
StartCursorAnim(CURSOR_ANIM_FIST);
RefreshDisplayMon();
PrintDisplayMonInfo();
PrintMessage(MSG_CHANGED_TO_ITEM);
sStorage->state++;
}
break;
case 3:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
sStorage->state++;
}
break;
case 4:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
static void Task_ShowItemInfo(u8 taskId)
{
switch (sStorage->state)
{
case 0:
ClearBottomWindow();
sStorage->state++;
break;
case 1:
if (!IsDma3ManagerBusyWithBgCopy())
{
PlaySE(SE_WIN_OPEN);
PrintItemDescription();
InitItemInfoWindow();
sStorage->state++;
}
break;
case 2:
if (!UpdateItemInfoWindowSlideIn())
sStorage->state++;
break;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
sStorage->state++;
break;
case 4:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
PlaySE(SE_WIN_OPEN);
sStorage->state++;
}
break;
case 5:
if (!UpdateItemInfoWindowSlideOut())
sStorage->state++;
break;
case 6:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
static void Task_CloseBoxWhileHoldingItem(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PlaySE(SE_SELECT);
PrintMessage(MSG_PUT_IN_BAG);
ShowYesNoWindow(0);
sStorage->state = 1;
break;
case 1:
switch (Menu_ProcessInputNoWrapClearOnChoose())
{
case MENU_B_PRESSED:
case 1: // No
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
break;
case 0:// Yes
if (AddBagItem(sStorage->movingItemId, 1) == TRUE)
{
ClearBottomWindow();
sStorage->state = 3;
}
else
{
PrintMessage(MSG_BAG_FULL);
sStorage->state = 2;
}
break;
}
break;
case 2:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
sStorage->state = 5;
}
break;
case 3:
MoveItemFromCursorToBag();
sStorage->state = 4;
break;
case 4:
if (!IsItemIconAnimActive())
{
StartCursorAnim(CURSOR_ANIM_BOUNCE);
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
case 5:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
static void Task_HandleMovingMonFromParty(u8 taskId)
{
switch (sStorage->state)
{
case 0:
CompactPartySlots();
CompactPartySprites();
sStorage->state++;
break;
case 1:
if (GetNumPartySpritesCompacting() == 0)
{
UpdatePartySlotColors();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_PrintCantStoreMail(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PrintMessage(MSG_CANT_STORE_MAIL);
sStorage->state++;
break;
case 1:
if (!IsDma3ManagerBusyWithBgCopy())
sStorage->state++;
break;
case 2:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
sStorage->state++;
}
break;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
SetPokeStorageTask(Task_PokeStorageMain);
break;
}
}
// Handle options menu that shows when the box title bar is selected
static void Task_HandleBoxOptions(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PrintMessage(MSG_WHAT_YOU_DO);
AddMenu();
sStorage->state++;
break;
case 1:
if (IsMenuLoading())
return;
sStorage->state++;
case 2:
switch (HandleMenuInput())
{
case MENU_B_PRESSED:
case MENU_CANCEL:
AnimateBoxScrollArrows(TRUE);
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
break;
case MENU_NAME:
PlaySE(SE_SELECT);
SetPokeStorageTask(Task_NameBox);
break;
case MENU_WALLPAPER:
PlaySE(SE_SELECT);
ClearBottomWindow();
SetPokeStorageTask(Task_HandleWallpapers);
break;
case MENU_JUMP:
PlaySE(SE_SELECT);
ClearBottomWindow();
SetPokeStorageTask(Task_JumpBox);
break;
}
break;
}
}
static void Task_HandleWallpapers(u8 taskId)
{
switch (sStorage->state)
{
case 0:
AddWallpaperSetsMenu();
PrintMessage(MSG_PICK_A_THEME);
sStorage->state++;
break;
case 1:
if (!IsMenuLoading())
sStorage->state++;
break;
case 2:
sStorage->wallpaperSetId = HandleMenuInput();
switch (sStorage->wallpaperSetId)
{
case MENU_B_PRESSED:
AnimateBoxScrollArrows(TRUE);
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
break;
case MENU_SCENERY_1:
case MENU_SCENERY_2:
case MENU_SCENERY_3:
case MENU_ETCETERA:
PlaySE(SE_SELECT);
RemoveMenu();
sStorage->wallpaperSetId -= MENU_WALLPAPER_SETS_START;
sStorage->state++;
break;
case MENU_FRIENDS:
// New wallpaper from Walda.
PlaySE(SE_SELECT);
sStorage->wallpaperId = WALLPAPER_FRIENDS;
RemoveMenu();
ClearBottomWindow();
sStorage->state = 6;
break;
}
break;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
{
AddWallpapersMenu(sStorage->wallpaperSetId);
PrintMessage(MSG_PICK_A_WALLPAPER);
sStorage->state++;
}
break;
case 4:
sStorage->wallpaperId = HandleMenuInput();
switch (sStorage->wallpaperId)
{
case MENU_NOTHING_CHOSEN:
break;
case MENU_B_PRESSED:
ClearBottomWindow();
sStorage->state = 0;
break;
default:
PlaySE(SE_SELECT);
ClearBottomWindow();
sStorage->wallpaperId -= MENU_WALLPAPERS_START;
SetWallpaperForCurrentBox(sStorage->wallpaperId);
sStorage->state++;
break;
}
break;
case 5:
if (!DoWallpaperGfxChange())
{
AnimateBoxScrollArrows(TRUE);
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
case 6:
if (!IsDma3ManagerBusyWithBgCopy())
{
SetWallpaperForCurrentBox(sStorage->wallpaperId);
sStorage->state = 5;
}
break;
}
}
static void Task_JumpBox(u8 taskId)
{
switch (sStorage->state)
{
case 0:
PrintMessage(MSG_JUMP_TO_WHICH_BOX);
LoadChooseBoxMenuGfx(&sStorage->chooseBoxMenu, GFXTAG_CHOOSE_BOX_MENU, PALTAG_MISC_1, 3, FALSE);
CreateChooseBoxMenuSprites(StorageGetCurrentBox());
sStorage->state++;
break;
case 1:
sStorage->newCurrBoxId = HandleChooseBoxMenuInput();
switch (sStorage->newCurrBoxId)
{
case BOXID_NONE_CHOSEN:
break;
default:
ClearBottomWindow();
DestroyChooseBoxMenuSprites();
FreeChooseBoxMenu();
if (sStorage->newCurrBoxId == BOXID_CANCELED || sStorage->newCurrBoxId == StorageGetCurrentBox())
{
AnimateBoxScrollArrows(TRUE);
SetPokeStorageTask(Task_PokeStorageMain);
}
else
{
sStorage->state++;
}
break;
}
break;
case 2:
SetUpScrollToBox(sStorage->newCurrBoxId);
sStorage->state++;
break;
case 3:
if (!ScrollToBox())
{
SetCurrentBox(sStorage->newCurrBoxId);
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
}
}
static void Task_NameBox(u8 taskId)
{
switch (sStorage->state)
{
case 0:
SaveMovingMon();
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
sStorage->state++;
break;
case 1:
if (!UpdatePaletteFade())
{
sWhichToReshow = SCREEN_CHANGE_NAME_BOX - 1;
sStorage->screenChangeType = SCREEN_CHANGE_NAME_BOX;
SetPokeStorageTask(Task_ChangeScreen);
}
break;
}
}
static void Task_ShowMonSummary(u8 taskId)
{
switch (sStorage->state)
{
case 0:
InitSummaryScreenData();
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
sStorage->state++;
break;
case 1:
if (!UpdatePaletteFade())
{
sWhichToReshow = SCREEN_CHANGE_SUMMARY_SCREEN - 1;
sStorage->screenChangeType = SCREEN_CHANGE_SUMMARY_SCREEN;
SetPokeStorageTask(Task_ChangeScreen);
}
break;
}
}
static void Task_GiveItemFromBag(u8 taskId)
{
switch (sStorage->state)
{
case 0:
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
sStorage->state++;
break;
case 1:
if (!UpdatePaletteFade())
{
sWhichToReshow = SCREEN_CHANGE_ITEM_FROM_BAG - 1;
sStorage->screenChangeType = SCREEN_CHANGE_ITEM_FROM_BAG;
SetPokeStorageTask(Task_ChangeScreen);
}
break;
}
}
static void Task_OnCloseBoxPressed(u8 taskId)
{
switch (sStorage->state)
{
case 0:
if (IsMonBeingMoved())
{
PlaySE(SE_FAILURE);
PrintMessage(MSG_HOLDING_POKE);
sStorage->state = 1;
}
else if (IsMovingItem())
{
SetPokeStorageTask(Task_CloseBoxWhileHoldingItem);
}
else
{
PlaySE(SE_SELECT);
PrintMessage(MSG_EXIT_BOX);
ShowYesNoWindow(0);
sStorage->state = 2;
}
break;
case 1:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
case 2:
switch (Menu_ProcessInputNoWrapClearOnChoose())
{
case MENU_B_PRESSED:
case 1:
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
break;
case 0:
PlaySE(SE_PC_OFF);
ClearBottomWindow();
sStorage->state++;
break;
}
break;
case 3:
ComputerScreenCloseEffect(20, 0, 1);
sStorage->state++;
break;
case 4:
if (!IsComputerScreenCloseEffectActive())
{
UpdateBoxToSendMons();
gPlayerPartyCount = CalculatePlayerPartyCount();
sStorage->screenChangeType = SCREEN_CHANGE_EXIT_BOX;
SetPokeStorageTask(Task_ChangeScreen);
}
break;
}
}
static void Task_OnBPressed(u8 taskId)
{
switch (sStorage->state)
{
case 0:
if (IsMonBeingMoved())
{
PlaySE(SE_FAILURE);
PrintMessage(MSG_HOLDING_POKE);
sStorage->state = 1;
}
else if (IsMovingItem())
{
SetPokeStorageTask(Task_CloseBoxWhileHoldingItem);
}
else
{
PlaySE(SE_SELECT);
PrintMessage(MSG_CONTINUE_BOX);
ShowYesNoWindow(0);
sStorage->state = 2;
}
break;
case 1:
if (JOY_NEW(A_BUTTON | B_BUTTON | DPAD_ANY))
{
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
}
break;
case 2:
switch (Menu_ProcessInputNoWrapClearOnChoose())
{
case 0:
ClearBottomWindow();
SetPokeStorageTask(Task_PokeStorageMain);
break;
case 1:
case MENU_B_PRESSED:
PlaySE(SE_PC_OFF);
ClearBottomWindow();
sStorage->state++;
break;
}
break;
case 3:
ComputerScreenCloseEffect(20, 0, 0);
sStorage->state++;
break;
case 4:
if (!IsComputerScreenCloseEffectActive())
{
UpdateBoxToSendMons();
gPlayerPartyCount = CalculatePlayerPartyCount();
sStorage->screenChangeType = SCREEN_CHANGE_EXIT_BOX;
SetPokeStorageTask(Task_ChangeScreen);
}
break;
}
}
static void Task_ChangeScreen(u8 taskId)
{
struct BoxPokemon *boxMons;
u8 mode, monIndex, maxMonIndex;
u8 screenChangeType = sStorage->screenChangeType;
if (sStorage->boxOption == OPTION_MOVE_ITEMS && IsMovingItem() == TRUE)
sMovingItemId = GetMovingItemId();
else
sMovingItemId = ITEM_NONE;
switch (screenChangeType)
{
case SCREEN_CHANGE_EXIT_BOX:
default:
FreePokeStorageData();
SetMainCallback2(CB2_ExitPokeStorage);
break;
case SCREEN_CHANGE_SUMMARY_SCREEN:
boxMons = sStorage->summaryMon.box;
monIndex = sStorage->summaryStartPos;
maxMonIndex = sStorage->summaryMaxPos;
mode = sStorage->summaryScreenMode;
FreePokeStorageData();
if (mode == SUMMARY_MODE_NORMAL && boxMons == &sSavedMovingMon.box)
ShowPokemonSummaryScreenSet40EF(mode, boxMons, monIndex, maxMonIndex, CB2_ReturnToPokeStorage);
else
ShowPokemonSummaryScreen(mode, boxMons, monIndex, maxMonIndex, CB2_ReturnToPokeStorage);
break;
case SCREEN_CHANGE_NAME_BOX:
FreePokeStorageData();
DoNamingScreen(NAMING_SCREEN_BOX, GetBoxNamePtr(StorageGetCurrentBox()), 0, 0, 0, CB2_ReturnToPokeStorage);
break;
case SCREEN_CHANGE_ITEM_FROM_BAG:
FreePokeStorageData();
GoToBagMenu(ITEMMENULOCATION_PCBOX, 0, CB2_ReturnToPokeStorage);
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 FreePokeStorageData(void)
{
TilemapUtil_Free();
MultiMove_Free();
FREE_AND_SET_NULL(sStorage);
FreeAllWindowBuffers();
}
//------------------------------------------------------------------------------
// SECTION: Misc
//
// No real uniform section below. Misc functions including more initialization,
// showing/hiding the party menu, updating the Close Box button, printing
// messages, doing the mosaic effect when transitioning between Pokémon, etc.
//------------------------------------------------------------------------------
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, BG_COORD_ADD);
ChangeBgY(3, 128, BG_COORD_SUB);
}
static void LoadPokeStorageMenuGfx(void)
{
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
DecompressAndLoadBgGfxUsingHeap(1, gStorageSystemMenu_Gfx, 0, 0, 0);
LZ77UnCompWram(sDisplayMenu_Tilemap, sStorage->displayMenuTilemapBuffer);
SetBgTilemapBuffer(1, sStorage->displayMenuTilemapBuffer);
ShowBg(1);
ScheduleBgCopyTilemapToVram(1);
}
static bool8 InitPokeStorageWindows(void)
{
if (!InitWindows(sWindowTemplates))
{
return FALSE;
}
else
{
DeactivateAllTextPrinters();
return TRUE;
}
}
static void LoadWaveformSpritePalette(void)
{
LoadSpritePalette(&gWaveformSpritePalette);
}
static void InitPalettesAndSprites(void)
{
LoadPalette(sInterface_Pal, 0, sizeof(sInterface_Pal));
LoadPalette(sPkmnDataGray_Pal, 0x20, sizeof(sPkmnDataGray_Pal));
LoadPalette(sUnknown_Pal, 0xF0, sizeof(sUnknown_Pal));
if (sStorage->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)
{
sStorage->markingComboSprite = CreateMonMarkingComboSprite(GFXTAG_MARKING_COMBO, PALTAG_MARKING_COMBO, NULL);
sStorage->markingComboSprite->oam.priority = 1;
sStorage->markingComboSprite->subpriority = 1;
sStorage->markingComboSprite->x = 40;
sStorage->markingComboSprite->y = 150;
sStorage->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(sStorage->waveformSprites); i++)
{
u8 spriteId = CreateSprite(&sSpriteTemplate_Waveform, i * 63 + 8, 9, 2);
sStorage->waveformSprites[i] = &gSprites[spriteId];
}
}
static void RefreshDisplayMonData(void)
{
LoadDisplayMonGfx(sStorage->displayMonSpecies, sStorage->displayMonPersonality);
PrintDisplayMonInfo();
UpdateWaveformAnimation();
ScheduleBgCopyTilemapToVram(0);
}
static void StartDisplayMonMosaicEffect(void)
{
RefreshDisplayMonData();
if (sStorage->displayMonSprite)
{
sStorage->displayMonSprite->oam.mosaic = TRUE;
sStorage->displayMonSprite->data[0] = 10;
sStorage->displayMonSprite->data[1] = 1;
sStorage->displayMonSprite->callback = SpriteCB_DisplayMonMosaic;
SetGpuReg(REG_OFFSET_MOSAIC, (sStorage->displayMonSprite->data[0] << 12) | (sStorage->displayMonSprite->data[0] << 8));
}
}
static u8 IsDisplayMosaicActive(void)
{
return sStorage->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 = {sStorage->tileBuffer, MON_PIC_SIZE, GFXTAG_DISPLAY_MON};
struct SpritePalette palette = {sStorage->displayMonPalBuffer, PALTAG_DISPLAY_MON};
struct SpriteTemplate template = sSpriteTemplate_DisplayMon;
for (i = 0; i < MON_PIC_SIZE; i++)
sStorage->tileBuffer[i] = 0;
for (i = 0; i < 16; i++)
sStorage->displayMonPalBuffer[i] = 0;
sStorage->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;
sStorage->displayMonSprite = &gSprites[spriteId];
sStorage->displayMonPalOffset = palSlot * 16 + 0x100;
sStorage->displayMonTilePtr = (void*) OBJ_VRAM0 + tileStart * 32;
} while (0);
if (sStorage->displayMonSprite == NULL)
{
FreeSpriteTilesByTag(GFXTAG_DISPLAY_MON);
FreeSpritePaletteByTag(PALTAG_DISPLAY_MON);
}
}
static void LoadDisplayMonGfx(u16 species, u32 pid)
{
if (sStorage->displayMonSprite == NULL)
return;
if (species != SPECIES_NONE)
{
LoadSpecialPokePic(&gMonFrontPicTable[species], sStorage->tileBuffer, species, pid, TRUE);
LZ77UnCompWram(sStorage->displayMonPalette, sStorage->displayMonPalBuffer);
CpuCopy32(sStorage->tileBuffer, sStorage->displayMonTilePtr, MON_PIC_SIZE);
LoadPalette(sStorage->displayMonPalBuffer, sStorage->displayMonPalOffset, 0x20);
sStorage->displayMonSprite->invisible = FALSE;
}
else
{
sStorage->displayMonSprite->invisible = TRUE;
}
}
static void PrintDisplayMonInfo(void)
{
FillWindowPixelBuffer(0, PIXEL_FILL(1));
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
{
AddTextPrinterParameterized(0, FONT_NORMAL, sStorage->displayMonNameText, 6, 0, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(0, FONT_SHORT, sStorage->displayMonSpeciesName, 6, 15, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(0, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 29, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(0, FONT_SMALL, sStorage->displayMonItemName, 6, 43, TEXT_SKIP_DRAW, NULL);
}
else
{
AddTextPrinterParameterized(0, FONT_SMALL, sStorage->displayMonItemName, 6, 0, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(0, FONT_NORMAL, sStorage->displayMonNameText, 6, 13, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(0, FONT_SHORT, sStorage->displayMonSpeciesName, 6, 28, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(0, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 42, TEXT_SKIP_DRAW, NULL);
}
CopyWindowToVram(0, COPYWIN_GFX);
if (sStorage->displayMonSpecies != SPECIES_NONE)
{
UpdateMonMarkingTiles(sStorage->displayMonMarkings, sStorage->markingComboTilesPtr);
sStorage->markingComboSprite->invisible = FALSE;
}
else
{
sStorage->markingComboSprite->invisible = TRUE;
}
}
// Turn the wave animation on the sides of "Pkmn Data" on/off
static void UpdateWaveformAnimation(void)
{
u16 i;
if (sStorage->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(sStorage->waveformSprites); i++)
StartSpriteAnimIfDifferent(sStorage->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(sStorage->waveformSprites); i++)
StartSpriteAnim(sStorage->waveformSprites[i], i * 2);
}
TilemapUtil_Update(TILEMAPID_PKMN_DATA);
ScheduleBgCopyTilemapToVram(1);
}
static void InitSupplementalTilemaps(void)
{
LZ77UnCompWram(gStorageSystemPartyMenu_Tilemap, sStorage->partyMenuTilemapBuffer);
LoadPalette(gStorageSystemPartyMenu_Pal, 0x10, 0x20);
TilemapUtil_SetMap(TILEMAPID_PARTY_MENU, 1, sStorage->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);
sStorage->closeBoxFlashing = FALSE;
}
static void SetUpShowPartyMenu(void)
{
sStorage->partyMenuUnused1 = 20;
sStorage->partyMenuY = 2;
sStorage->partyMenuMoveTimer = 0;
CreatePartyMonsSprites(FALSE);
}
static bool8 ShowPartyMenu(void)
{
if (sStorage->partyMenuMoveTimer == 20)
return FALSE;
sStorage->partyMenuUnused1--;
sStorage->partyMenuY++;
TilemapUtil_Move(TILEMAPID_PARTY_MENU, 3, 1);
TilemapUtil_Update(TILEMAPID_PARTY_MENU);
ScheduleBgCopyTilemapToVram(1);
MovePartySprites(8);
if (++sStorage->partyMenuMoveTimer == 20)
{
sInPartyMenu = TRUE;
return FALSE;
}
else
{
return TRUE;
}
}
static void SetUpHidePartyMenu(void)
{
sStorage->partyMenuUnused1 = 0;
sStorage->partyMenuY = 22;
sStorage->partyMenuMoveTimer = 0;
if (sStorage->boxOption == OPTION_MOVE_ITEMS)
MoveHeldItemWithPartyMenu();
}
static bool8 HidePartyMenu(void)
{
if (sStorage->partyMenuMoveTimer != 20)
{
sStorage->partyMenuUnused1++;
sStorage->partyMenuY--;
TilemapUtil_Move(TILEMAPID_PARTY_MENU, 3, -1);
TilemapUtil_Update(TILEMAPID_PARTY_MENU);
FillBgTilemapBufferRect_Palette0(1, 0x100, 10, sStorage->partyMenuY, 12, 1);
MovePartySprites(-8);
if (++sStorage->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)
{
sStorage->closeBoxFlashing = TRUE;
sStorage->closeBoxFlashTimer = 30;
sStorage->closeBoxFlashState = TRUE;
}
static void StopFlashingCloseBoxButton(void)
{
if (sStorage->closeBoxFlashing)
{
sStorage->closeBoxFlashing = FALSE;
UpdateCloseBoxButtonTilemap(TRUE);
}
}
static void UpdateCloseBoxButtonFlash(void)
{
if (sStorage->closeBoxFlashing && ++sStorage->closeBoxFlashTimer > 30)
{
sStorage->closeBoxFlashTimer = 0;
sStorage->closeBoxFlashState = (sStorage->closeBoxFlashState == FALSE);
UpdateCloseBoxButtonTilemap(sStorage->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++)
sStorage->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)
{
sStorage->showPartyMenuState = 0;
PlaySE(SE_WIN_OPEN);
SetUpShowPartyMenu();
}
static bool8 DoShowPartyMenu(void)
{
switch (sStorage->showPartyMenuState)
{
case 0:
if (!ShowPartyMenu())
{
SetCursorInParty();
sStorage->showPartyMenuState++;
}
break;
case 1:
if (!UpdateCursorPos())
{
if (sStorage->setMosaic)
StartDisplayMonMosaicEffect();
sStorage->showPartyMenuState++;
}
break;
case 2:
return FALSE;
}
return TRUE;
}
static void UpdateBoxToSendMons(void)
{
if (sLastUsedBox != StorageGetCurrentBox())
{
FlagClear(FLAG_SHOWN_BOX_WAS_FULL_MESSAGE);
VarSet(VAR_PC_BOX_TO_SEND_MON, StorageGetCurrentBox());
}
}
static void InitPokeStorageBg0(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, sStorage->displayMonName);
break;
case MSG_VAR_RELEASE_MON_1:
case MSG_VAR_RELEASE_MON_2:
case MSG_VAR_RELEASE_MON_3:
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sStorage->releaseMonName);
break;
case MSG_VAR_ITEM_NAME:
if (IsMovingItem())
txtPtr = StringCopy(sStorage->itemName, GetMovingItemName());
else
txtPtr = StringCopy(sStorage->itemName, sStorage->displayMonItemName);
while (*(txtPtr - 1) == CHAR_SPACE)
txtPtr--;
*txtPtr = EOS;
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sStorage->itemName);
break;
}
DynamicPlaceholderTextUtil_ExpandPlaceholders(sStorage->messageText, sMessages[id].text);
FillWindowPixelBuffer(1, PIXEL_FILL(1));
AddTextPrinterParameterized(1, FONT_NORMAL, sStorage->messageText, 0, 1, TEXT_SKIP_DRAW, NULL);
DrawTextBorderOuter(1, 2, 14);
PutWindowTilemap(1);
CopyWindowToVram(1, COPYWIN_GFX);
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 InitCursorItemIcon(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);
}
}
//------------------------------------------------------------------------------
// SECTION: Pokémon sprites
//
// The below functions generally handle the Pokémon icon sprites, including
// moving them with a scrolling box, shifting the party sprites, and
// animating released Pokémon.
//------------------------------------------------------------------------------
static void InitMonIconFields(void)
{
u16 i;
LoadMonIconPalettes();
for (i = 0; i < MAX_MON_ICONS; i++)
sStorage->numIconsPerSpecies[i] = 0;
for (i = 0; i < MAX_MON_ICONS; i++)
sStorage->iconSpeciesList[i] = SPECIES_NONE;
for (i = 0; i < PARTY_SIZE; i++)
sStorage->partySprites[i] = NULL;
for (i = 0; i < IN_BOX_COUNT; i++)
sStorage->boxMonsSprites[i] = NULL;
sStorage->movingMonSprite = NULL;
sStorage->unkUnused1 = 0;
}
static u8 GetMonIconPriorityByCursorPos(void)
{
return (IsCursorInBox() ? 2 : 1);
}
static void CreateMovingMonIcon(void)
{
u32 personality = GetMonData(&sStorage->movingMon, MON_DATA_PERSONALITY);
u16 species = GetMonData(&sStorage->movingMon, MON_DATA_SPECIES2);
u8 priority = GetMonIconPriorityByCursorPos();
sStorage->movingMonSprite = CreateMonIconSprite(species, personality, 0, 0, priority, 7);
sStorage->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);
sStorage->boxMonsSprites[count] = CreateMonIconSprite(species, personality, 8 * (3 * j) + 100, 8 * (3 * i) + 44, 2, 19 - j);
}
else
{
sStorage->boxMonsSprites[count] = NULL;
}
boxPosition++;
count++;
}
}
// If in item mode, set all Pokémon icons with no item to be transparent
if (sStorage->boxOption == OPTION_MOVE_ITEMS)
{
for (boxPosition = 0; boxPosition < IN_BOX_COUNT; boxPosition++)
{
if (GetBoxMonDataAt(boxId, boxPosition, MON_DATA_HELD_ITEM) == ITEM_NONE)
sStorage->boxMonsSprites[boxPosition]->oam.objMode = ST_OAM_OBJ_BLEND;
}
}
}
static void CreateBoxMonIconAtPos(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);
sStorage->boxMonsSprites[boxPosition] = CreateMonIconSprite(species, personality, x, y, 2, 19 - (boxPosition % IN_BOX_COLUMNS));
if (sStorage->boxOption == OPTION_MOVE_ITEMS)
sStorage->boxMonsSprites[boxPosition]->oam.objMode = ST_OAM_OBJ_BLEND;
}
}
#define sDistance data[1]
#define sSpeed data[2]
#define sScrollInDestX data[3]
#define sDelay data[4]
#define sScrollOutX data[5]
static void StartBoxMonIconsScrollOut(s16 speed)
{
u16 i;
for (i = 0; i < IN_BOX_COUNT; i++)
{
if (sStorage->boxMonsSprites[i] != NULL)
{
sStorage->boxMonsSprites[i]->sSpeed = speed;
sStorage->boxMonsSprites[i]->sDelay = 1;
sStorage->boxMonsSprites[i]->callback = SpriteCB_BoxMonIconScrollOut;
}
}
}
static void SpriteCB_BoxMonIconScrollIn(struct Sprite *sprite)
{
if (sprite->sDistance != 0)
{
// Icon moving
sprite->sDistance--;
sprite->x += sprite->sSpeed;
}
else
{
// Icon arrived
sStorage->iconScrollNumIncoming--;
sprite->x = sprite->sScrollInDestX;
sprite->callback = SpriteCallbackDummy;
}
}
static void SpriteCB_BoxMonIconScrollOut(struct Sprite *sprite)
{
if (sprite->sDelay != 0)
{
sprite->sDelay--;
}
else
{
// Icon moving
sprite->x += sprite->sSpeed;
sprite->sScrollOutX = sprite->x + sprite->x2;
// Check if icon offscreen
if (sprite->sScrollOutX <= 68 || sprite->sScrollOutX >= 252)
sprite->callback = SpriteCallbackDummy;
}
}
// Sprites for Pokémon icons are destroyed during
// the box scroll once they've gone offscreen
static void DestroyBoxMonIconsInColumn(u8 column)
{
u16 row;
u8 boxPosition = column;
for (row = 0; row < IN_BOX_ROWS; row++)
{
if (sStorage->boxMonsSprites[boxPosition] != NULL)
{
DestroyBoxMonIcon(sStorage->boxMonsSprites[boxPosition]);
sStorage->boxMonsSprites[boxPosition] = NULL;
}
boxPosition += IN_BOX_COLUMNS;
}
}
// Create the appearing icons for the incoming scrolling box
static u8 CreateBoxMonIconsInColumn(u8 column, u16 distance, s16 speed)
{
s32 i;
u16 y = 44;
s16 xDest = 8 * (3 * column) + 100;
u16 x = xDest - ((distance + 1) * speed);
u8 subpriority = 19 - column;
u8 iconsCreated = 0;
u8 boxPosition = column;
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
{
for (i = 0; i < IN_BOX_ROWS; i++)
{
if (sStorage->boxSpecies[boxPosition] != SPECIES_NONE)
{
sStorage->boxMonsSprites[boxPosition] = CreateMonIconSprite(sStorage->boxSpecies[boxPosition],
sStorage->boxPersonalities[boxPosition],
x, y, 2, subpriority);
if (sStorage->boxMonsSprites[boxPosition] != NULL)
{
sStorage->boxMonsSprites[boxPosition]->sDistance = distance;
sStorage->boxMonsSprites[boxPosition]->sSpeed = speed;
sStorage->boxMonsSprites[boxPosition]->sScrollInDestX = xDest;
sStorage->boxMonsSprites[boxPosition]->callback = SpriteCB_BoxMonIconScrollIn;
iconsCreated++;
}
}
boxPosition += IN_BOX_COLUMNS;
y += 24;
}
}
else
{
// Separate case for Move Items mode is used
// to create the icons with the proper blend
for (i = 0; i < IN_BOX_ROWS; i++)
{
if (sStorage->boxSpecies[boxPosition] != SPECIES_NONE)
{
sStorage->boxMonsSprites[boxPosition] = CreateMonIconSprite(sStorage->boxSpecies[boxPosition],
sStorage->boxPersonalities[boxPosition],
x, y, 2, subpriority);
if (sStorage->boxMonsSprites[boxPosition] != NULL)
{
sStorage->boxMonsSprites[boxPosition]->sDistance = distance;
sStorage->boxMonsSprites[boxPosition]->sSpeed = speed;
sStorage->boxMonsSprites[boxPosition]->sScrollInDestX = xDest;
sStorage->boxMonsSprites[boxPosition]->callback = SpriteCB_BoxMonIconScrollIn;
if (GetBoxMonDataAt(sStorage->incomingBoxId, boxPosition, MON_DATA_HELD_ITEM) == ITEM_NONE)
sStorage->boxMonsSprites[boxPosition]->oam.objMode = ST_OAM_OBJ_BLEND;
iconsCreated++;
}
}
boxPosition += IN_BOX_COLUMNS;
y += 24;
}
}
return iconsCreated;
}
#undef sDistance
#undef sSpeed
#undef sScrollInDestX
#undef sDelay
#undef sScrollOutX
static void InitBoxMonIconScroll(u8 boxId, s8 direction)
{
sStorage->iconScrollState = 0;
sStorage->iconScrollToBoxId = boxId;
sStorage->iconScrollDirection = direction;
sStorage->iconScrollDistance = 32;
sStorage->iconScrollSpeed = -(6 * direction);
sStorage->iconScrollNumIncoming = 0;
GetIncomingBoxMonData(boxId);
if (direction > 0)
sStorage->iconScrollCurColumn = 0;
else
sStorage->iconScrollCurColumn = IN_BOX_COLUMNS - 1;
sStorage->iconScrollPos = (24 * sStorage->iconScrollCurColumn) + 100;
StartBoxMonIconsScrollOut(sStorage->iconScrollSpeed);
}
static bool8 UpdateBoxMonIconScroll(void)
{
if (sStorage->iconScrollDistance != 0)
sStorage->iconScrollDistance--;
switch (sStorage->iconScrollState)
{
case 0:
sStorage->iconScrollPos += sStorage->iconScrollSpeed;
if (sStorage->iconScrollPos <= 64 || sStorage->iconScrollPos >= 252)
{
// A column of icons has gone offscreen, destroy them
DestroyBoxMonIconsInColumn(sStorage->iconScrollCurColumn);
sStorage->iconScrollPos += sStorage->iconScrollDirection * 24;
sStorage->iconScrollState++;
}
break;
case 1:
// Create the new incoming column of icons
sStorage->iconScrollPos += sStorage->iconScrollSpeed;
sStorage->iconScrollNumIncoming += CreateBoxMonIconsInColumn(sStorage->iconScrollCurColumn, sStorage->iconScrollDistance, sStorage->iconScrollSpeed);
if ((sStorage->iconScrollDirection > 0 && sStorage->iconScrollCurColumn == IN_BOX_COLUMNS - 1)
|| (sStorage->iconScrollDirection < 0 && sStorage->iconScrollCurColumn == 0))
{
// Scroll has reached final column
sStorage->iconScrollState++;
}
else
{
// Continue scrolling
sStorage->iconScrollCurColumn += sStorage->iconScrollDirection;
sStorage->iconScrollState = 0;
}
break;
case 2:
// Wait to make sure all icons have arrived
if (sStorage->iconScrollNumIncoming == 0)
{
sStorage->iconScrollDistance++;
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
static void GetIncomingBoxMonData(u8 boxId)
{
s32 i, j, boxPosition;
boxPosition = 0;
for (i = 0; i < IN_BOX_ROWS; i++)
{
for (j = 0; j < IN_BOX_COLUMNS; j++)
{
sStorage->boxSpecies[boxPosition] = GetBoxMonDataAt(boxId, boxPosition, MON_DATA_SPECIES2);
if (sStorage->boxSpecies[boxPosition] != SPECIES_NONE)
sStorage->boxPersonalities[boxPosition] = GetBoxMonDataAt(boxId, boxPosition, MON_DATA_PERSONALITY);
boxPosition++;
}
}
sStorage->incomingBoxId = boxId;
}
static void DestroyBoxMonIconAtPosition(u8 boxPosition)
{
if (sStorage->boxMonsSprites[boxPosition] != NULL)
{
DestroyBoxMonIcon(sStorage->boxMonsSprites[boxPosition]);
sStorage->boxMonsSprites[boxPosition] = NULL;
}
}
static void SetBoxMonIconObjMode(u8 boxPosition, u8 objMode)
{
if (sStorage->boxMonsSprites[boxPosition] != NULL)
sStorage->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);
sStorage->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);
sStorage->partySprites[i] = CreateMonIconSprite(species, personality, 152, 8 * (3 * (i - 1)) + 16, 1, 12);
count++;
}
else
{
sStorage->partySprites[i] = NULL;
}
}
if (!visible)
{
for (i = 0; i < count; i++)
{
sStorage->partySprites[i]->y -= DISPLAY_HEIGHT;
sStorage->partySprites[i]->invisible = TRUE;
}
}
if (sStorage->boxOption == OPTION_MOVE_ITEMS)
{
for (i = 0; i < PARTY_SIZE; i++)
{
if (sStorage->partySprites[i] != NULL && GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) == ITEM_NONE)
sStorage->partySprites[i]->oam.objMode = ST_OAM_OBJ_BLEND;
}
}
}
static void CompactPartySprites(void)
{
u16 i, targetSlot;
sStorage->numPartyToCompact = 0;
for (i = 0, targetSlot = 0; i < PARTY_SIZE; i++)
{
if (sStorage->partySprites[i] != NULL)
{
if (i != targetSlot)
{
MovePartySpriteToNextSlot(sStorage->partySprites[i], targetSlot);
sStorage->partySprites[i] = NULL;
sStorage->numPartyToCompact++;
}
targetSlot++;
}
}
}
static u8 GetNumPartySpritesCompacting(void)
{
return sStorage->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->x) * 8;
sprite->sMonY = (u16)(sprite->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->x = x / 8u;
sprite->y = y / 8u;
sprite->sMoveSteps--;
}
else
{
if (sprite->sPartyId == 0)
{
sprite->x = 104;
sprite->y = 64;
}
else
{
sprite->x = 152;
sprite->y = 8 * (3 * (sprite->sPartyId - 1)) + 16;
}
sprite->callback = SpriteCallbackDummy;
sStorage->partySprites[sprite->sPartyId] = sprite;
sStorage->numPartyToCompact--;
}
}
#undef sPartyId
#undef sMonX
#undef sMonY
#undef sSpeedX
#undef sSpeedY
#undef sMoveSteps
static void DestroyMovingMonIcon(void)
{
if (sStorage->movingMonSprite != NULL)
{
DestroyBoxMonIcon(sStorage->movingMonSprite);
sStorage->movingMonSprite = NULL;
}
}
static void MovePartySprites(s16 yDelta)
{
u16 i, posY;
for (i = 0; i < PARTY_SIZE; i++)
{
if (sStorage->partySprites[i] != NULL)
{
sStorage->partySprites[i]->y += yDelta;
posY = sStorage->partySprites[i]->y + sStorage->partySprites[i]->y2 + sStorage->partySprites[i]->centerToCornerVecY;
posY += 16;
if (posY > 192)
sStorage->partySprites[i]->invisible = TRUE;
else
sStorage->partySprites[i]->invisible = FALSE;
}
}
}
static void DestroyPartyMonIcon(u8 partyId)
{
if (sStorage->partySprites[partyId] != NULL)
{
DestroyBoxMonIcon(sStorage->partySprites[partyId]);
sStorage->partySprites[partyId] = NULL;
}
}
static void DestroyAllPartyMonIcons(void)
{
u16 i;
for (i = 0; i < PARTY_SIZE; i++)
{
if (sStorage->partySprites[i] != NULL)
{
DestroyBoxMonIcon(sStorage->partySprites[i]);
sStorage->partySprites[i] = NULL;
}
}
}
static void SetPartyMonIconObjMode(u8 partyId, u8 objMode)
{
if (sStorage->partySprites[partyId] != NULL)
{
sStorage->partySprites[partyId]->oam.objMode = objMode;
}
}
static void SetMovingMonSprite(u8 mode, u8 id)
{
if (mode == MODE_PARTY)
{
sStorage->movingMonSprite = sStorage->partySprites[id];
sStorage->partySprites[id] = NULL;
}
else if (mode == MODE_BOX)
{
sStorage->movingMonSprite = sStorage->boxMonsSprites[id];
sStorage->boxMonsSprites[id] = NULL;
}
else
{
return;
}
sStorage->movingMonSprite->callback = SpriteCB_HeldMon;
sStorage->movingMonSprite->oam.priority = GetMonIconPriorityByCursorPos();
sStorage->movingMonSprite->subpriority = 7;
}
static void SetPlacedMonSprite(u8 boxId, u8 position)
{
if (boxId == TOTAL_BOXES_COUNT) // party mon
{
sStorage->partySprites[position] = sStorage->movingMonSprite;
sStorage->partySprites[position]->oam.priority = 1;
sStorage->partySprites[position]->subpriority = 12;
}
else
{
sStorage->boxMonsSprites[position] = sStorage->movingMonSprite;
sStorage->boxMonsSprites[position]->oam.priority = 2;
sStorage->boxMonsSprites[position]->subpriority = 19 - (position % IN_BOX_COLUMNS);
}
sStorage->movingMonSprite->callback = SpriteCallbackDummy;
sStorage->movingMonSprite = NULL;
}
static void SaveMonSpriteAtPos(u8 boxId, u8 position)
{
if (boxId == TOTAL_BOXES_COUNT) // party mon
sStorage->shiftMonSpritePtr = &sStorage->partySprites[position];
else
sStorage->shiftMonSpritePtr = &sStorage->boxMonsSprites[position];
sStorage->movingMonSprite->callback = SpriteCallbackDummy;
sStorage->shiftTimer = 0;
}
static bool8 MoveShiftingMons(void)
{
if (sStorage->shiftTimer == 16)
return FALSE;
sStorage->shiftTimer++;
if (sStorage->shiftTimer & 1)
{
(*sStorage->shiftMonSpritePtr)->y--;
sStorage->movingMonSprite->y++;
}
(*sStorage->shiftMonSpritePtr)->x2 = gSineTable[sStorage->shiftTimer * 8] / 16;
sStorage->movingMonSprite->x2 = -(gSineTable[sStorage->shiftTimer * 8] / 16);
if (sStorage->shiftTimer == 8)
{
sStorage->movingMonSprite->oam.priority = (*sStorage->shiftMonSpritePtr)->oam.priority;
sStorage->movingMonSprite->subpriority = (*sStorage->shiftMonSpritePtr)->subpriority;
(*sStorage->shiftMonSpritePtr)->oam.priority = GetMonIconPriorityByCursorPos();
(*sStorage->shiftMonSpritePtr)->subpriority = 7;
}
if (sStorage->shiftTimer == 16)
{
struct Sprite *sprite = sStorage->movingMonSprite;
sStorage->movingMonSprite = (*sStorage->shiftMonSpritePtr);
*sStorage->shiftMonSpritePtr = sprite;
sStorage->movingMonSprite->callback = SpriteCB_HeldMon;
(*sStorage->shiftMonSpritePtr)->callback = SpriteCallbackDummy;
}
return TRUE;
}
static void SetReleaseMon(u8 mode, u8 position)
{
switch (mode)
{
case MODE_PARTY:
sStorage->releaseMonSpritePtr = &sStorage->partySprites[position];
break;
case MODE_BOX:
sStorage->releaseMonSpritePtr = &sStorage->boxMonsSprites[position];
break;
case MODE_MOVE:
sStorage->releaseMonSpritePtr = &sStorage->movingMonSprite;
break;
default:
return;
}
if (*sStorage->releaseMonSpritePtr != NULL)
{
InitSpriteAffineAnim(*sStorage->releaseMonSpritePtr);
(*sStorage->releaseMonSpritePtr)->oam.affineMode = ST_OAM_AFFINE_NORMAL;
(*sStorage->releaseMonSpritePtr)->affineAnims = sAffineAnims_ReleaseMon;
StartSpriteAffineAnim(*sStorage->releaseMonSpritePtr, RELEASE_ANIM_RELEASE);
}
}
static bool8 TryHideReleaseMonSprite(void)
{
if (*sStorage->releaseMonSpritePtr == NULL
|| (*sStorage->releaseMonSpritePtr)->invisible)
return FALSE;
if ((*sStorage->releaseMonSpritePtr)->affineAnimEnded)
(*sStorage->releaseMonSpritePtr)->invisible = TRUE;
return TRUE;
}
static void DestroyReleaseMonIcon(void)
{
if (*sStorage->releaseMonSpritePtr != NULL)
{
FreeOamMatrix((*sStorage->releaseMonSpritePtr)->oam.matrixNum);
DestroyBoxMonIcon(*sStorage->releaseMonSpritePtr);
*sStorage->releaseMonSpritePtr = NULL;
}
}
static void ReshowReleaseMon(void)
{
if (*sStorage->releaseMonSpritePtr != NULL)
{
(*sStorage->releaseMonSpritePtr)->invisible = FALSE;
StartSpriteAffineAnim(*sStorage->releaseMonSpritePtr, RELEASE_ANIM_CAME_BACK);
}
}
static bool8 ResetReleaseMonSpritePtr(void)
{
if (sStorage->releaseMonSpritePtr == NULL)
return FALSE;
if ((*sStorage->releaseMonSpritePtr)->affineAnimEnded)
sStorage->releaseMonSpritePtr = NULL;
return TRUE;
}
static void SetMovingMonPriority(u8 priority)
{
sStorage->movingMonSprite->oam.priority = priority;
}
static void SpriteCB_HeldMon(struct Sprite *sprite)
{
sprite->x = sStorage->cursorSprite->x;
sprite->y = sStorage->cursorSprite->y + sStorage->cursorSprite->y2 + 4;
}
static u16 TryLoadMonIconTiles(u16 species)
{
u16 i, offset;
// Search icon list for this species
for (i = 0; i < MAX_MON_ICONS; i++)
{
if (sStorage->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 (sStorage->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
sStorage->iconSpeciesList[i] = species;
sStorage->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 (sStorage->iconSpeciesList[i] == species)
{
if (--sStorage->numIconsPerSpecies[i] == 0)
sStorage->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);
}
//------------------------------------------------------------------------------
// SECTION: General box
//
// Some basic box functions, including initializing the box and scrolling.
//------------------------------------------------------------------------------
#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:
sStorage->wallpaperOffset = 0;
sStorage->bg2_X = 0;
task->tDmaIdx = RequestDma3Fill(0, sStorage->wallpaperBgTilemapBuffer, sizeof(sStorage->wallpaperBgTilemapBuffer), 1);
break;
case 1:
if (CheckForSpaceForDma3Request(task->tDmaIdx) == -1)
return;
SetBgTilemapBuffer(2, sStorage->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);
sStorage->scrollSpeed = (direction > 0) ? 6 : -6;
sStorage->scrollUnused1 = (direction > 0) ? 1 : 2;
sStorage->scrollTimer = 32;
sStorage->scrollToBoxIdUnused = boxId;
sStorage->scrollUnused2 = (direction <= 0) ? 5 : 0;
sStorage->scrollDirectionUnused = direction;
sStorage->scrollUnused3 = (direction > 0) ? 264 : 56;
sStorage->scrollUnused4 = (direction <= 0) ? 5 : 0;
sStorage->scrollUnused5 = 0;
sStorage->scrollUnused6 = 2;
sStorage->scrollToBoxId = boxId;
sStorage->scrollDirection = direction;
sStorage->scrollState = 0;
}
static bool8 ScrollToBox(void)
{
bool8 iconsScrolling;
switch (sStorage->scrollState)
{
case 0:
LoadWallpaperGfx(sStorage->scrollToBoxId, sStorage->scrollDirection);
sStorage->scrollState++;
case 1:
if (!WaitForWallpaperGfxLoad())
return TRUE;
InitBoxMonIconScroll(sStorage->scrollToBoxId, sStorage->scrollDirection);
CreateIncomingBoxTitle(sStorage->scrollToBoxId, sStorage->scrollDirection);
StartBoxScrollArrowsSlide(sStorage->scrollDirection);
break;
case 2:
iconsScrolling = UpdateBoxMonIconScroll();
if (sStorage->scrollTimer != 0)
{
sStorage->bg2_X += sStorage->scrollSpeed;
if (--sStorage->scrollTimer != 0)
return TRUE;
CycleBoxTitleSprites();
StopBoxScrollArrowsSlide();
}
return iconsScrolling;
}
sStorage->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;
}
//------------------------------------------------------------------------------
// SECTION: Wallpaper gfx
//------------------------------------------------------------------------------
static void SetWallpaperForCurrentBox(u8 wallpaperId)
{
u8 boxId = StorageGetCurrentBox();
SetBoxWallpaper(boxId, wallpaperId);
sStorage->wallpaperChangeState = 0;
}
static bool8 DoWallpaperGfxChange(void)
{
switch (sStorage->wallpaperChangeState)
{
case 0:
BeginNormalPaletteFade(sStorage->wallpaperPalBits, 1, 0, 16, RGB_WHITEALPHA);
sStorage->wallpaperChangeState++;
break;
case 1:
if (!UpdatePaletteFade())
{
u8 curBox = StorageGetCurrentBox();
LoadWallpaperGfx(curBox, 0);
sStorage->wallpaperChangeState++;
}
break;
case 2:
if (WaitForWallpaperGfxLoad() == TRUE)
{
CycleBoxTitleColor();
BeginNormalPaletteFade(sStorage->wallpaperPalBits, 1, 16, 0, RGB_WHITEALPHA);
sStorage->wallpaperChangeState++;
}
break;
case 3:
if (!UpdatePaletteFade())
sStorage->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;
sStorage->wallpaperLoadState = 0;
sStorage->wallpaperLoadBoxId = boxId;
sStorage->wallpaperLoadDir = direction;
if (sStorage->wallpaperLoadDir != 0)
{
sStorage->wallpaperOffset = (sStorage->wallpaperOffset == 0);
TrimOldWallpaper(sStorage->wallpaperBgTilemapBuffer);
}
wallpaperId = GetBoxWallpaper(sStorage->wallpaperLoadBoxId);
if (wallpaperId != WALLPAPER_FRIENDS)
{
wallpaper = &sWallpapers[wallpaperId];
LZ77UnCompWram(wallpaper->tilemap, sStorage->wallpaperTilemap);
DrawWallpaper(sStorage->wallpaperTilemap, sStorage->wallpaperLoadDir, sStorage->wallpaperOffset);
if (sStorage->wallpaperLoadDir != 0)
LoadPalette(wallpaper->palettes, (sStorage->wallpaperOffset * 32) + 0x40, 0x40);
else
CpuCopy16(wallpaper->palettes, &gPlttBufferUnfaded[(sStorage->wallpaperOffset * 32) + 0x40], 0x40);
sStorage->wallpaperTiles = malloc_and_decompress(wallpaper->tiles, &tilesSize);
LoadBgTiles(2, sStorage->wallpaperTiles, tilesSize, sStorage->wallpaperOffset << 8);
}
else
{
wallpaper = &sWaldaWallpapers[GetWaldaWallpaperPatternId()];
LZ77UnCompWram(wallpaper->tilemap, sStorage->wallpaperTilemap);
DrawWallpaper(sStorage->wallpaperTilemap, sStorage->wallpaperLoadDir, sStorage->wallpaperOffset);
CpuCopy16(wallpaper->palettes, sStorage->wallpaperTilemap, 0x40);
CpuCopy16(GetWaldaWallpaperColorsPtr(), &sStorage->wallpaperTilemap[1], 4);
CpuCopy16(GetWaldaWallpaperColorsPtr(), &sStorage->wallpaperTilemap[17], 4);
if (sStorage->wallpaperLoadDir != 0)
LoadPalette(sStorage->wallpaperTilemap, (sStorage->wallpaperOffset * 32) + 0x40, 0x40);
else
CpuCopy16(sStorage->wallpaperTilemap, &gPlttBufferUnfaded[(sStorage->wallpaperOffset * 32) + 0x40], 0x40);
sStorage->wallpaperTiles = malloc_and_decompress(wallpaper->tiles, &tilesSize);
iconGfx = malloc_and_decompress(sWaldaWallpaperIcons[GetWaldaWallpaperIconId()], &iconSize);
CpuCopy32(iconGfx, sStorage->wallpaperTiles + 0x800, iconSize);
Free(iconGfx);
LoadBgTiles(2, sStorage->wallpaperTiles, tilesSize, sStorage->wallpaperOffset << 8);
}
CopyBgTilemapBufferToVram(2);
}
static bool32 WaitForWallpaperGfxLoad(void)
{
if (IsDma3ManagerBusyWithBgCopy())
return FALSE;
if (sStorage->wallpaperTiles != NULL)
FREE_AND_SET_NULL(sStorage->wallpaperTiles);
return TRUE;
}
static void DrawWallpaper(const void *tilemap, s8 direction, u8 offset)
{
s16 tileOffset = offset * 256;
s16 paletteNum = (offset * 2) + 3;
s16 x = ((sStorage->bg2_X / 8 + 10) + (direction * 24)) & 0x3F;
CopyRectToBgTilemapBufferRect(2, tilemap, 0, 0, 20, 18, x, 2, 20, 18, 17, tileOffset, paletteNum);
if (direction == 0)
return;
if (direction > 0)
x += 20;
else
x -= 4;
FillBgTilemapBufferRect(2, 0, x, 2, 4, 0x12, 0x11);
}
static void TrimOldWallpaper(void *tilemap)
{
u16 i;
u16 *dest = tilemap;
s16 r3 = ((sStorage->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;
}
}
//------------------------------------------------------------------------------
// SECTION: Box Title
//------------------------------------------------------------------------------
static void InitBoxTitle(u8 boxId)
{
u8 tagIndex;
s16 x;
u16 i;
struct SpriteSheet spriteSheet = {sStorage->boxTitleTiles, 0x200, GFXTAG_BOX_TITLE};
struct SpritePalette palettes[] = {
{sStorage->boxTitlePal, PALTAG_BOX_TITLE},
{}
};
u16 wallpaperId = GetBoxWallpaper(boxId);
sStorage->boxTitlePal[14] = sBoxTitleColors[wallpaperId][0]; // Shadow color
sStorage->boxTitlePal[15] = sBoxTitleColors[wallpaperId][1]; // Text Color
LoadSpritePalettes(palettes);
sStorage->wallpaperPalBits = 0x3f0;
tagIndex = IndexOfSpritePaletteTag(PALTAG_BOX_TITLE);
sStorage->boxTitlePalOffset = 0x10e + 16 * tagIndex;
sStorage->wallpaperPalBits |= 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);
sStorage->boxTitleAltPalOffset = 0x10e + 16 * tagIndex;
sStorage->wallpaperPalBits |= 0x10000 << tagIndex;
StringCopyPadded(sStorage->boxTitleText, GetBoxNamePtr(boxId), 0, BOX_NAME_LENGTH);
DrawTextWindowAndBufferTiles(sStorage->boxTitleText, sStorage->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);
sStorage->curBoxTitleSprites[i] = &gSprites[spriteId];
StartSpriteAnim(sStorage->curBoxTitleSprites[i], i);
}
sStorage->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 = {sStorage->boxTitleTiles, 0x200, GFXTAG_BOX_TITLE};
struct SpriteTemplate template = sSpriteTemplate_BoxTitle;
sStorage->boxTitleCycleId = (sStorage->boxTitleCycleId == 0);
if (sStorage->boxTitleCycleId == 0)
{
spriteSheet.tag = GFXTAG_BOX_TITLE;
palOffset = sStorage->boxTitlePalOffset;
}
else
{
spriteSheet.tag = GFXTAG_BOX_TITLE_ALT;
palOffset = sStorage->boxTitlePalOffset;
template.tileTag = GFXTAG_BOX_TITLE_ALT;
template.paletteTag = PALTAG_BOX_TITLE;
}
StringCopyPadded(sStorage->boxTitleText, GetBoxNamePtr(boxId), 0, BOX_NAME_LENGTH);
DrawTextWindowAndBufferTiles(sStorage->boxTitleText, sStorage->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);
sStorage->nextBoxTitleSprites[i] = &gSprites[spriteId];
sStorage->nextBoxTitleSprites[i]->sSpeed = (-direction) * 6;
sStorage->nextBoxTitleSprites[i]->sIncomingX = i * 32 + x;
sStorage->nextBoxTitleSprites[i]->sIncomingDelay = 0;
sStorage->nextBoxTitleSprites[i]->callback = SpriteCB_IncomingBoxTitle;
StartSpriteAnim(sStorage->nextBoxTitleSprites[i], i);
sStorage->curBoxTitleSprites[i]->sSpeed = (-direction) * 6;
sStorage->curBoxTitleSprites[i]->sOutgoingDelay = 1;
sStorage->curBoxTitleSprites[i]->callback = SpriteCB_OutgoingBoxTitle;
}
}
static void CycleBoxTitleSprites(void)
{
if (sStorage->boxTitleCycleId == 0)
FreeSpriteTilesByTag(GFXTAG_BOX_TITLE_ALT);
else
FreeSpriteTilesByTag(GFXTAG_BOX_TITLE);
sStorage->curBoxTitleSprites[0] = sStorage->nextBoxTitleSprites[0];
sStorage->curBoxTitleSprites[1] = sStorage->nextBoxTitleSprites[1];
}
static void SpriteCB_IncomingBoxTitle(struct Sprite *sprite)
{
if (sprite->sIncomingDelay != 0)
sprite->sIncomingDelay--;
else if ((sprite->x += sprite->sSpeed) == sprite->sIncomingX)
sprite->callback = SpriteCallbackDummy;
}
static void SpriteCB_OutgoingBoxTitle(struct Sprite *sprite)
{
if (sprite->sOutgoingDelay != 0)
{
sprite->sOutgoingDelay--;
}
else
{
sprite->x += sprite->sSpeed;
sprite->sOutgoingX = sprite->x + sprite->x2;
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 (sStorage->boxTitleCycleId == 0)
CpuCopy16(sBoxTitleColors[wallpaperId], gPlttBufferUnfaded + sStorage->boxTitlePalOffset, 4);
else
CpuCopy16(sBoxTitleColors[wallpaperId], gPlttBufferUnfaded + sStorage->boxTitleAltPalOffset, 4);
}
static s16 GetBoxTitleBaseX(const u8 *string)
{
return DISPLAY_WIDTH - 64 - GetStringWidth(FONT_NORMAL, string, 0) / 2;
}
//------------------------------------------------------------------------------
// SECTION: Scroll arrows
//------------------------------------------------------------------------------
// 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;
sStorage->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++)
{
sStorage->arrowSprites[i]->x2 = 0;
sStorage->arrowSprites[i]->sState = 2;
}
if (direction < 0)
{
sStorage->arrowSprites[0]->sTimer = 29;
sStorage->arrowSprites[1]->sTimer = 5;
sStorage->arrowSprites[0]->data[2] = 72;
sStorage->arrowSprites[1]->data[2] = 72;
}
else
{
sStorage->arrowSprites[0]->sTimer = 5;
sStorage->arrowSprites[1]->sTimer = 29;
sStorage->arrowSprites[0]->data[2] = DISPLAY_WIDTH + 8;
sStorage->arrowSprites[1]->data[2] = DISPLAY_WIDTH + 8;
}
sStorage->arrowSprites[0]->data[7] = 0;
sStorage->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++)
{
sStorage->arrowSprites[i]->x = 136 * i + 92;
sStorage->arrowSprites[i]->x2 = 0;
sStorage->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++)
{
sStorage->arrowSprites[i]->sState = 1;
sStorage->arrowSprites[i]->sTimer = 0;
sStorage->arrowSprites[i]->data[2] = 0;
sStorage->arrowSprites[i]->data[4] = 0;
}
}
else
{
// Stop arrows moving
for (i = 0; i < 2; i++)
sStorage->arrowSprites[i]->sState = 0;
}
}
static void SpriteCB_Arrow(struct Sprite *sprite)
{
switch (sprite->sState)
{
case 0:
sprite->x2 = 0;
break;
case 1:
if (++sprite->sTimer > 3)
{
sprite->sTimer = 0;
sprite->x2 += sprite->sSpeed;
if (++sprite->data[2] > 5)
{
sprite->data[2] = 0;
sprite->x2 = 0;
}
}
break;
case 2:
sprite->sState = 3;
break;
case 3:
sprite->x -= sStorage->scrollSpeed;
if (sprite->x <= 72 || sprite->x >= DISPLAY_WIDTH + 8)
sprite->invisible = TRUE;
if (--sprite->sTimer == 0)
{
sprite->x = sprite->data[2];
sprite->invisible = FALSE;
sprite->sState = 4;
}
break;
case 4:
sprite->x -= sStorage->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];
}
//------------------------------------------------------------------------------
// SECTION: Cursor movement
//
// The functions below generally handle the cursor's movement, including
// moving around the box and picking up/putting down Pokémon.
//------------------------------------------------------------------------------
static void InitCursor(void)
{
if (sStorage->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();
sStorage->cursorPrevHorizPos = 1;
sStorage->inBoxMovingMode = MOVE_MODE_NORMAL;
TryRefreshDisplayMon();
}
static void InitCursorOnReopen(void)
{
CreateCursorSprites();
ReshowDisplayMon();
sStorage->cursorPrevHorizPos = 1;
sStorage->inBoxMovingMode = MOVE_MODE_NORMAL;
if (sIsMonBeingMoved)
{
sStorage->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 (sStorage->cursorMoveSteps == 0)
{
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
return FALSE;
else
return IsItemIconAnimActive();
}
else if (--sStorage->cursorMoveSteps != 0)
{
// Update position toward target
sStorage->cursorNewX += sStorage->cursorSpeedX;
sStorage->cursorNewY += sStorage->cursorSpeedY;
sStorage->cursorSprite->x = sStorage->cursorNewX >> 8;
sStorage->cursorSprite->y = sStorage->cursorNewY >> 8;
// Limit cursor on right
if (sStorage->cursorSprite->x > DISPLAY_WIDTH + 16)
{
tmp = sStorage->cursorSprite->x - (DISPLAY_WIDTH + 16);
sStorage->cursorSprite->x = tmp + 64;
}
// Limit cursor on left
if (sStorage->cursorSprite->x < 64)
{
tmp = 64 - sStorage->cursorSprite->x;
sStorage->cursorSprite->x = DISPLAY_WIDTH + 16 - tmp;
}
// Limit cursor on bottom
if (sStorage->cursorSprite->y > DISPLAY_HEIGHT + 16)
{
tmp = sStorage->cursorSprite->y - (DISPLAY_HEIGHT + 16);
sStorage->cursorSprite->y = tmp - 16;
}
// Limit cursor on top
if (sStorage->cursorSprite->y < -16)
{
tmp = -16 - sStorage->cursorSprite->y;
sStorage->cursorSprite->y = DISPLAY_HEIGHT + 16 - tmp;
}
// Cursor flips vertically when moving on/off the top buttons
if (sStorage->cursorFlipTimer && --sStorage->cursorFlipTimer == 0)
sStorage->cursorSprite->vFlip = (sStorage->cursorSprite->vFlip == FALSE);
}
else
{
// Time is up for cursor movement, make sure it's exactly at target
sStorage->cursorSprite->x = sStorage->cursorTargetX;
sStorage->cursorSprite->y = sStorage->cursorTargetY;
DoCursorNewPosUpdate();
}
return TRUE;
}
static void InitNewCursorPos(u8 newCursorArea, u8 newCursorPosition)
{
u16 x, y;
GetCursorCoordsByPos(newCursorArea, newCursorPosition, &x, &y);
sStorage->newCursorArea = newCursorArea;
sStorage->newCursorPosition = newCursorPosition;
sStorage->cursorTargetX = x;
sStorage->cursorTargetY = y;
}
static void InitCursorMove(void)
{
int yDistance, xDistance;
if (sStorage->cursorVerticalWrap != 0 || sStorage->cursorHorizontalWrap != 0)
sStorage->cursorMoveSteps = 12;
else
sStorage->cursorMoveSteps = 6;
if (sStorage->cursorFlipTimer)
sStorage->cursorFlipTimer = sStorage->cursorMoveSteps >> 1;
switch (sStorage->cursorVerticalWrap)
{
default: // No wrap
yDistance = sStorage->cursorTargetY - sStorage->cursorSprite->y;
break;
case -1: // Wrap from top to bottom
yDistance = sStorage->cursorTargetY - 192 - sStorage->cursorSprite->y;
break;
case 1: // Wrap from bottom to top
yDistance = sStorage->cursorTargetY + 192 - sStorage->cursorSprite->y;
break;
}
switch (sStorage->cursorHorizontalWrap)
{
default: // No Wrap
xDistance = sStorage->cursorTargetX - sStorage->cursorSprite->x;
break;
case -1: // Wrap from left to right
xDistance = sStorage->cursorTargetX - 192 - sStorage->cursorSprite->x;
break;
case 1: // Wrap from right to left
xDistance = sStorage->cursorTargetX + 192 - sStorage->cursorSprite->x;
break;
}
yDistance <<= 8;
xDistance <<= 8;
sStorage->cursorSpeedX = xDistance / sStorage->cursorMoveSteps;
sStorage->cursorSpeedY = yDistance / sStorage->cursorMoveSteps;
sStorage->cursorNewX = sStorage->cursorSprite->x << 8;
sStorage->cursorNewY = sStorage->cursorSprite->y << 8;
}
static void SetCursorPosition(u8 newCursorArea, u8 newCursorPosition)
{
InitNewCursorPos(newCursorArea, newCursorPosition);
InitCursorMove();
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
{
if (sStorage->inBoxMovingMode == MOVE_MODE_NORMAL && !sIsMonBeingMoved)
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_STILL);
}
else
{
if (!IsMovingItem())
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_STILL);
}
if (sStorage->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)
{
sStorage->cursorPrevHorizPos = 1;
sStorage->cursorShadowSprite->invisible = TRUE;
}
switch (newCursorArea)
{
case CURSOR_AREA_IN_PARTY:
case CURSOR_AREA_BOX_TITLE:
case CURSOR_AREA_BUTTONS:
sStorage->cursorSprite->oam.priority = 1;
sStorage->cursorShadowSprite->invisible = TRUE;
sStorage->cursorShadowSprite->oam.priority = 1;
break;
case CURSOR_AREA_IN_BOX:
if (sStorage->inBoxMovingMode != MOVE_MODE_NORMAL)
{
sStorage->cursorSprite->oam.priority = 0;
sStorage->cursorShadowSprite->invisible = TRUE;
}
else
{
sStorage->cursorSprite->oam.priority = 2;
if (sCursorArea == CURSOR_AREA_IN_BOX && sIsMonBeingMoved)
SetMovingMonPriority(2);
}
break;
}
}
static void DoCursorNewPosUpdate(void)
{
sCursorArea = sStorage->newCursorArea;
sCursorPosition = sStorage->newCursorPosition;
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
{
if (sStorage->inBoxMovingMode == MOVE_MODE_NORMAL && !sIsMonBeingMoved)
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_BOUNCE);
}
else
{
if (!IsMovingItem())
StartSpriteAnim(sStorage->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:
sStorage->cursorShadowSprite->subpriority = 13;
SetMovingMonPriority(1);
break;
case CURSOR_AREA_IN_BOX:
if (sStorage->inBoxMovingMode == MOVE_MODE_NORMAL)
{
sStorage->cursorSprite->oam.priority = 1;
sStorage->cursorShadowSprite->oam.priority = 2;
sStorage->cursorShadowSprite->subpriority = 21;
sStorage->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 (sStorage->cursorSprite->vFlip)
sStorage->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,
};
sStorage->monPlaceChangeFunc = placeChangeFuncs[type];
sStorage->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)
sStorage->monPlaceChangeFunc = MultiMonPlaceChange_Down;
else
sStorage->monPlaceChangeFunc = MultiMonPlaceChange_Up;
sStorage->monPlaceChangeState = 0;
}
static bool8 DoMonPlaceChange(void)
{
return sStorage->monPlaceChangeFunc();
}
static bool8 MonPlaceChange_Grab(void)
{
switch (sStorage->monPlaceChangeState)
{
case 0:
if (sIsMonBeingMoved)
return FALSE;
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_OPEN);
sStorage->monPlaceChangeState++;
break;
case 1:
if (!MonPlaceChange_CursorDown())
{
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_FIST);
MoveMon();
sStorage->monPlaceChangeState++;
}
break;
case 2:
if (!MonPlaceChange_CursorUp())
sStorage->monPlaceChangeState++;
break;
case 3:
return FALSE;
}
return TRUE;
}
static bool8 MonPlaceChange_Place(void)
{
switch (sStorage->monPlaceChangeState)
{
case 0:
if (!MonPlaceChange_CursorDown())
{
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_OPEN);
PlaceMon();
sStorage->monPlaceChangeState++;
}
break;
case 1:
if (!MonPlaceChange_CursorUp())
{
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_BOUNCE);
sStorage->monPlaceChangeState++;
}
break;
case 2:
return FALSE;
}
return TRUE;
}
static bool8 MonPlaceChange_Shift(void)
{
switch (sStorage->monPlaceChangeState)
{
case 0:
switch (sCursorArea)
{
case CURSOR_AREA_IN_PARTY:
sStorage->shiftBoxId = TOTAL_BOXES_COUNT;
break;
case CURSOR_AREA_IN_BOX:
sStorage->shiftBoxId = StorageGetCurrentBox();
break;
default:
return FALSE;
}
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_OPEN);
SaveMonSpriteAtPos(sStorage->shiftBoxId, sCursorPosition);
sStorage->monPlaceChangeState++;
break;
case 1:
if (!MoveShiftingMons())
{
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_FIST);
SetShiftedMonData(sStorage->shiftBoxId, sCursorPosition);
sStorage->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 (sStorage->cursorSprite->y2)
{
default:
sStorage->cursorSprite->y2++;
break;
case 0:
sStorage->cursorSprite->y2++;
break;
case 8: // Cursor has reached bottom
return FALSE;
}
return TRUE;
}
static bool8 MonPlaceChange_CursorUp(void)
{
switch (sStorage->cursorSprite->y2)
{
case 0: // Cursor has reached top
return FALSE;
default:
sStorage->cursorSprite->y2--;
break;
}
return TRUE;
}
//------------------------------------------------------------------------------
// SECTION: Pokémon data
//
// The functions below handle moving Pokémon data around while using the PC,
// including changing the positions of Pokémon, releasing Pokémon, viewing the
// summary screen, and updating the display of the currently selected Pokémon.
//------------------------------------------------------------------------------
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 (sStorage->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);
SetPlacedMonSprite(TOTAL_BOXES_COUNT, sCursorPosition);
break;
case CURSOR_AREA_IN_BOX:
boxId = StorageGetCurrentBox();
SetPlacedMonData(boxId, sCursorPosition);
SetPlacedMonSprite(boxId, sCursorPosition);
break;
default:
return;
}
sIsMonBeingMoved = FALSE;
}
static void RefreshDisplayMon(void)
{
TryRefreshDisplayMon();
}
static void SetMovingMonData(u8 boxId, u8 position)
{
if (boxId == TOTAL_BOXES_COUNT)
sStorage->movingMon = gPlayerParty[sCursorPosition];
else
BoxMonAtToMon(boxId, position, &sStorage->movingMon);
PurgeMonOrBoxMon(boxId, position);
sMovingMonOrigBoxId = boxId;
sMovingMonOrigBoxPos = position;
}
static void SetPlacedMonData(u8 boxId, u8 position)
{
if (boxId == TOTAL_BOXES_COUNT)
{
gPlayerParty[position] = sStorage->movingMon;
}
else
{
BoxMonRestorePP(&sStorage->movingMon.box);
SetBoxMonAt(boxId, position, &sStorage->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)
sStorage->tempMon = gPlayerParty[position];
else
BoxMonAtToMon(boxId, position, &sStorage->tempMon);
SetPlacedMonData(boxId, position);
sStorage->movingMon = sStorage->tempMon;
SetDisplayMonData(&sStorage->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())
CreateBoxMonIconAtPos(boxPosition);
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_STILL);
return TRUE;
}
static void ResetSelectionAfterDeposit(void)
{
StartSpriteAnim(sStorage->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(sStorage->releaseMonName, sStorage->displayMonName);
}
static bool8 TryHideReleaseMon(void)
{
if (!TryHideReleaseMonSprite())
{
StartSpriteAnim(sStorage->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(sStorage->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
sStorage->releaseStatusResolved = TRUE;
sStorage->canReleaseMon = FALSE;
return;
}
if (sIsMonBeingMoved)
{
sStorage->tempMon = sStorage->movingMon;
sStorage->releaseBoxId = -1;
sStorage->releaseBoxPos = -1;
}
else
{
if (sCursorArea == CURSOR_AREA_IN_PARTY)
{
sStorage->tempMon = gPlayerParty[sCursorPosition];
sStorage->releaseBoxId = TOTAL_BOXES_COUNT;
}
else
{
BoxMonAtToMon(StorageGetCurrentBox(), sCursorPosition, &sStorage->tempMon);
sStorage->releaseBoxId = StorageGetCurrentBox();
}
sStorage->releaseBoxPos = sCursorPosition;
}
GetRestrictedReleaseMoves(sStorage->restrictedMoveList);
sStorage->restrictedReleaseMonMoves = GetMonData(&sStorage->tempMon, MON_DATA_KNOWN_MOVES, (u8*)sStorage->restrictedMoveList);
if (sStorage->restrictedReleaseMonMoves != 0)
{
// Pokémon knows at least one restricted release move
// Need to check if another Pokémon has this move first
sStorage->releaseStatusResolved = FALSE;
}
else
{
// Pokémon knows no restricted moves, can be released
sStorage->releaseStatusResolved = TRUE;
sStorage->canReleaseMon = TRUE;
}
sStorage->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 (sStorage->releaseStatusResolved)
return sStorage->canReleaseMon;
switch (sStorage->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 (sStorage->releaseBoxId != TOTAL_BOXES_COUNT || sStorage->releaseBoxPos != i)
{
knownMoves = GetMonData(&gPlayerParty[i], MON_DATA_KNOWN_MOVES, (u8*)sStorage->restrictedMoveList);
sStorage->restrictedReleaseMonMoves &= ~(knownMoves);
}
}
if (sStorage->restrictedReleaseMonMoves == 0)
{
// No restricted moves on release Pokémon that
// aren't resolved by the party, it can be released.
sStorage->releaseStatusResolved = TRUE;
sStorage->canReleaseMon = TRUE;
}
else
{
// Release Pokémon has restricted moves not resolved by the party.
// Continue and check the PC next
sStorage->releaseCheckBoxId = 0;
sStorage->releaseCheckBoxPos = 0;
sStorage->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(sStorage->releaseCheckBoxId, sStorage->releaseCheckBoxPos, MON_DATA_KNOWN_MOVES, (u8*)sStorage->restrictedMoveList);
if (knownMoves != 0 && !(sStorage->releaseBoxId == sStorage->releaseCheckBoxId
&& sStorage->releaseBoxPos == sStorage->releaseCheckBoxPos))
{
// Found PC Pokémon with restricted move, clear move from list
sStorage->restrictedReleaseMonMoves &= ~(knownMoves);
if (sStorage->restrictedReleaseMonMoves == 0)
{
// No restricted moves on release Pokémon that
// aren't resolved, it can be released.
sStorage->releaseStatusResolved = TRUE;
sStorage->canReleaseMon = TRUE;
break;
}
}
if (++sStorage->releaseCheckBoxPos >= IN_BOX_COUNT)
{
sStorage->releaseCheckBoxPos = 0;
if (++sStorage->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.
sStorage->releaseStatusResolved = TRUE;
sStorage->canReleaseMon = FALSE;
}
}
}
break;
}
return -1;
}
static void SaveMovingMon(void)
{
if (sIsMonBeingMoved)
sSavedMovingMon = sStorage->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)
sStorage->movingMon = sSavedMovingMon;
else
sStorage->movingMon.box = sSavedMovingMon.box;
}
}
static void InitSummaryScreenData(void)
{
if (sIsMonBeingMoved)
{
SaveMovingMon();
sStorage->summaryMon.mon = &sSavedMovingMon;
sStorage->summaryStartPos = 0;
sStorage->summaryMaxPos = 0;
sStorage->summaryScreenMode = SUMMARY_MODE_NORMAL;
}
else if (sCursorArea == CURSOR_AREA_IN_PARTY)
{
sStorage->summaryMon.mon = gPlayerParty;
sStorage->summaryStartPos = sCursorPosition;
sStorage->summaryMaxPos = CountPartyMons() - 1;
sStorage->summaryScreenMode = SUMMARY_MODE_NORMAL;
}
else
{
sStorage->summaryMon.box = GetBoxedMonPtr(StorageGetCurrentBox(), 0);
sStorage->summaryStartPos = sCursorPosition;
sStorage->summaryMaxPos = IN_BOX_COUNT - 1;
sStorage->summaryScreenMode = SUMMARY_MODE_BOX;
}
}
static void SetSelectionAfterSummaryScreen(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)
{
sStorage->displayMonMarkings = markings;
if (sIsMonBeingMoved)
{
SetMonData(&sStorage->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 (sStorage->displayMonIsEgg || GetMonData(&sStorage->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.
sStorage->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;
sStorage->displayMonItemId = ITEM_NONE;
gender = MON_MALE;
sanityIsBadEgg = FALSE;
if (mode == MODE_PARTY)
{
struct Pokemon *mon = (struct Pokemon *)pokemon;
sStorage->displayMonSpecies = GetMonData(mon, MON_DATA_SPECIES2);
if (sStorage->displayMonSpecies != SPECIES_NONE)
{
sanityIsBadEgg = GetMonData(mon, MON_DATA_SANITY_IS_BAD_EGG);
if (sanityIsBadEgg)
sStorage->displayMonIsEgg = TRUE;
else
sStorage->displayMonIsEgg = GetMonData(mon, MON_DATA_IS_EGG);
GetMonData(mon, MON_DATA_NICKNAME, sStorage->displayMonName);
StringGet_Nickname(sStorage->displayMonName);
sStorage->displayMonLevel = GetMonData(mon, MON_DATA_LEVEL);
sStorage->displayMonMarkings = GetMonData(mon, MON_DATA_MARKINGS);
sStorage->displayMonPersonality = GetMonData(mon, MON_DATA_PERSONALITY);
sStorage->displayMonPalette = GetMonFrontSpritePal(mon);
gender = GetMonGender(mon);
sStorage->displayMonItemId = GetMonData(mon, MON_DATA_HELD_ITEM);
}
}
else if (mode == MODE_BOX)
{
struct BoxPokemon *boxMon = (struct BoxPokemon *)pokemon;
sStorage->displayMonSpecies = GetBoxMonData(pokemon, MON_DATA_SPECIES2);
if (sStorage->displayMonSpecies != SPECIES_NONE)
{
u32 otId = GetBoxMonData(boxMon, MON_DATA_OT_ID);
sanityIsBadEgg = GetBoxMonData(boxMon, MON_DATA_SANITY_IS_BAD_EGG);
if (sanityIsBadEgg)
sStorage->displayMonIsEgg = TRUE;
else
sStorage->displayMonIsEgg = GetBoxMonData(boxMon, MON_DATA_IS_EGG);
GetBoxMonData(boxMon, MON_DATA_NICKNAME, sStorage->displayMonName);
StringGet_Nickname(sStorage->displayMonName);
sStorage->displayMonLevel = GetLevelFromBoxMonExp(boxMon);
sStorage->displayMonMarkings = GetBoxMonData(boxMon, MON_DATA_MARKINGS);
sStorage->displayMonPersonality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY);
sStorage->displayMonPalette = GetMonSpritePalFromSpeciesAndPersonality(sStorage->displayMonSpecies, otId, sStorage->displayMonPersonality);
gender = GetGenderFromSpeciesAndPersonality(sStorage->displayMonSpecies, sStorage->displayMonPersonality);
sStorage->displayMonItemId = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM);
}
}
else
{
sStorage->displayMonSpecies = SPECIES_NONE;
sStorage->displayMonItemId = ITEM_NONE;
}
if (sStorage->displayMonSpecies == SPECIES_NONE)
{
StringFill(sStorage->displayMonName, CHAR_SPACE, 5);
StringFill(sStorage->displayMonNameText, CHAR_SPACE, 8);
StringFill(sStorage->displayMonSpeciesName, CHAR_SPACE, 8);
StringFill(sStorage->displayMonGenderLvlText, CHAR_SPACE, 8);
StringFill(sStorage->displayMonItemName, CHAR_SPACE, 8);
}
else if (sStorage->displayMonIsEgg)
{
if (sanityIsBadEgg)
StringCopyPadded(sStorage->displayMonNameText, sStorage->displayMonName, CHAR_SPACE, 5);
else
StringCopyPadded(sStorage->displayMonNameText, gText_EggNickname, CHAR_SPACE, 8);
StringFill(sStorage->displayMonSpeciesName, CHAR_SPACE, 8);
StringFill(sStorage->displayMonGenderLvlText, CHAR_SPACE, 8);
StringFill(sStorage->displayMonItemName, CHAR_SPACE, 8);
}
else
{
if (sStorage->displayMonSpecies == SPECIES_NIDORAN_F || sStorage->displayMonSpecies == SPECIES_NIDORAN_M)
gender = MON_GENDERLESS;
StringCopyPadded(sStorage->displayMonNameText, sStorage->displayMonName, CHAR_SPACE, 5);
txtPtr = sStorage->displayMonSpeciesName;
*(txtPtr)++ = CHAR_SLASH;
StringCopyPadded(txtPtr, gSpeciesNames[sStorage->displayMonSpecies], CHAR_SPACE, 5);
txtPtr = sStorage->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_SPACER; // 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, sStorage->displayMonLevel, STR_CONV_MODE_LEFT_ALIGN, 3);
txtPtr[0] = CHAR_SPACE;
txtPtr[1] = EOS;
if (sStorage->displayMonItemId != ITEM_NONE)
StringCopyPadded(sStorage->displayMonItemName, ItemId_GetName(sStorage->displayMonItemId), CHAR_SPACE, 8);
else
StringFill(sStorage->displayMonItemName, CHAR_SPACE, 8);
}
}
//------------------------------------------------------------------------------
// SECTION: Input handlers
//
// The functions below process context-dependent input
//------------------------------------------------------------------------------
static u8 HandleInput_InBox(void)
{
switch (sStorage->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;
sStorage->cursorVerticalWrap = 0;
sStorage->cursorHorizontalWrap = 0;
sStorage->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;
sStorage->cursorVerticalWrap = 1;
sStorage->cursorFlipTimer = 1;
}
break;
}
else if (JOY_REPEAT(DPAD_LEFT))
{
retVal = INPUT_MOVE_CURSOR;
if (sCursorPosition % IN_BOX_COLUMNS != 0)
{
cursorPosition--;
}
else
{
sStorage->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
{
sStorage->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 (sStorage->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
{
sStorage->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
sStorage->inBoxMovingMode = MOVE_MODE_NORMAL;
sStorage->cursorShadowSprite->invisible = FALSE;
return INPUT_MULTIMOVE_SINGLE;
}
else
{
sIsMonBeingMoved = (sStorage->displayMonSpecies != SPECIES_NONE);
sStorage->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;
sStorage->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;
sStorage->cursorHorizontalWrap = 0;
sStorage->cursorVerticalWrap = 0;
sStorage->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;
sStorage->cursorPrevHorizPos = sCursorPosition;
cursorPosition = 0;
break;
}
else if (JOY_REPEAT(DPAD_RIGHT))
{
if (sCursorPosition == 0)
{
retVal = INPUT_MOVE_CURSOR;
cursorPosition = sStorage->cursorPrevHorizPos;
}
else
{
retVal = INPUT_HIDE_PARTY;
cursorArea = CURSOR_AREA_IN_BOX;
cursorPosition = 0;
}
break;
}
if (JOY_NEW(A_BUTTON))
{
if (sCursorPosition == PARTY_SIZE)
{
if (sStorage->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 (sStorage->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
{
sStorage->cursorHorizontalWrap = 0;
sStorage->cursorVerticalWrap = 0;
sStorage->cursorFlipTimer = 0;
if (JOY_REPEAT(DPAD_UP))
{
retVal = INPUT_MOVE_CURSOR;
cursorArea = CURSOR_AREA_BUTTONS;
cursorPosition = 0;
sStorage->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);
AddBoxOptionsMenu();
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;
sStorage->cursorHorizontalWrap = 0;
sStorage->cursorVerticalWrap = 0;
sStorage->cursorFlipTimer = 0;
if (JOY_REPEAT(DPAD_UP))
{
retVal = INPUT_MOVE_CURSOR;
cursorArea = CURSOR_AREA_IN_BOX;
sStorage->cursorVerticalWrap = -1;
if (sCursorPosition == 0)
cursorPosition = IN_BOX_COUNT - 1 - 5;
else
cursorPosition = IN_BOX_COUNT - 1;
sStorage->cursorFlipTimer = 1;
break;
}
if (JOY_REPEAT(DPAD_DOWN | START_BUTTON))
{
retVal = INPUT_MOVE_CURSOR;
cursorArea = CURSOR_AREA_BOX_TITLE;
cursorPosition = 0;
sStorage->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 AddBoxOptionsMenu(void)
{
InitMenu();
SetMenuText(MENU_JUMP);
SetMenuText(MENU_WALLPAPER);
SetMenuText(MENU_NAME);
SetMenuText(MENU_CANCEL);
}
static u8 SetSelectionMenuTexts(void)
{
InitMenu();
if (sStorage->boxOption != OPTION_MOVE_ITEMS)
return SetMenuTexts_Mon();
else
return SetMenuTexts_Item();
}
static bool8 SetMenuTexts_Mon(void)
{
u16 species = GetSpeciesAtCursorPosition();
switch (sStorage->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 (sStorage->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 (sStorage->displayMonSpecies == SPECIES_EGG)
return FALSE;
if (!IsMovingItem())
{
if (sStorage->displayMonItemId == ITEM_NONE)
{
if (sStorage->displayMonSpecies == SPECIES_NONE)
return FALSE;
SetMenuText(MENU_GIVE_2);
}
else
{
if (!ItemIsMail(sStorage->displayMonItemId))
{
SetMenuText(MENU_TAKE);
SetMenuText(MENU_BAG);
}
SetMenuText(MENU_INFO);
}
}
else
{
if (sStorage->displayMonItemId == ITEM_NONE)
{
if (sStorage->displayMonSpecies == SPECIES_NONE)
return FALSE;
SetMenuText(MENU_GIVE);
}
else
{
if (ItemIsMail(sStorage->displayMonItemId) == TRUE)
return FALSE;
SetMenuText(MENU_SWITCH);
}
}
SetMenuText(MENU_CANCEL);
return TRUE;
}
//------------------------------------------------------------------------------
// SECTION: Cursor
//
// The functions below handle a few of the generic cursor features.
//------------------------------------------------------------------------------
static void SpriteCB_CursorShadow(struct Sprite *sprite)
{
sprite->x = sStorage->cursorSprite->x;
sprite->y = sStorage->cursorSprite->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_MISC_1},
{}
};
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_MISC_2,
.oam = &sOamData_Cursor,
.anims = sAnims_Cursor,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct SpriteTemplate sSpriteTemplate_CursorShadow =
{
.tileTag = GFXTAG_CURSOR_SHADOW,
.paletteTag = PALTAG_MISC_2,
.oam = &sOamData_CursorShadow,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_CursorShadow,
};
LoadSpriteSheets(spriteSheets);
LoadSpritePalettes(spritePalettes);
sStorage->cursorPalNums[0] = IndexOfSpritePaletteTag(PALTAG_MISC_2); // White hand, normal
sStorage->cursorPalNums[1] = IndexOfSpritePaletteTag(PALTAG_MISC_1); // Yellow hand, when auto-action is on
GetCursorCoordsByPos(sCursorArea, sCursorPosition, &x, &y);
spriteId = CreateSprite(&sSpriteTemplate_Cursor, x, y, 6);
if (spriteId != MAX_SPRITES)
{
sStorage->cursorSprite = &gSprites[spriteId];
sStorage->cursorSprite->oam.paletteNum = sStorage->cursorPalNums[sAutoActionOn];
sStorage->cursorSprite->oam.priority = 1;
if (sIsMonBeingMoved)
StartSpriteAnim(sStorage->cursorSprite, CURSOR_ANIM_FIST);
}
else
{
sStorage->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)
{
sStorage->cursorShadowSprite = &gSprites[spriteId];
sStorage->cursorShadowSprite->oam.priority = priority;
if (sCursorArea)
sStorage->cursorShadowSprite->invisible = TRUE;
}
else
{
sStorage->cursorShadowSprite = NULL;
}
}
static void ToggleCursorAutoAction(void)
{
sAutoActionOn = !sAutoActionOn;
sStorage->cursorSprite->oam.paletteNum = sStorage->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(sStorage->cursorSprite, animNum);
}
// Unused
static u8 GetMovingMonOriginalBoxId(void)
{
return sMovingMonOrigBoxId;
}
static void SetCursorPriorityTo1(void)
{
sStorage->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);
}
//------------------------------------------------------------------------------
// SECTION: Menu
//
// The functions below handle the generic options menu that comes up whenever
// something in the PC is selected.
//------------------------------------------------------------------------------
static void InitMenu(void)
{
sStorage->menuItemsCount = 0;
sStorage->menuWidth = 0;
sStorage->menuWindow.bg = 0;
sStorage->menuWindow.paletteNum = 15;
sStorage->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 (sStorage->menuItemsCount < ARRAY_COUNT(sStorage->menuItems))
{
u8 len;
struct StorageMenu *menu = &sStorage->menuItems[sStorage->menuItemsCount];
menu->text = sMenuTexts[textId];
menu->textId = textId;
len = StringLength(menu->text);
if (len > sStorage->menuWidth)
sStorage->menuWidth = len;
sStorage->menuItemsCount++;
}
}
static s8 GetMenuItemTextId(u8 menuIdx)
{
if (menuIdx >= sStorage->menuItemsCount)
return -1;
else
return sStorage->menuItems[menuIdx].textId;
}
static void AddMenu(void)
{
sStorage->menuWindow.width = sStorage->menuWidth + 2;
sStorage->menuWindow.height = 2 * sStorage->menuItemsCount;
sStorage->menuWindow.tilemapLeft = 29 - sStorage->menuWindow.width;
sStorage->menuWindow.tilemapTop = 15 - sStorage->menuWindow.height;
sStorage->menuWindowId = AddWindow(&sStorage->menuWindow);
ClearWindowTilemap(sStorage->menuWindowId);
DrawStdFrameWithCustomTileAndPalette(sStorage->menuWindowId, FALSE, 11, 14);
PrintMenuTable(sStorage->menuWindowId, sStorage->menuItemsCount, (void*)sStorage->menuItems);
InitMenuInUpperLeftCornerNormal(sStorage->menuWindowId, sStorage->menuItemsCount, 0);
ScheduleBgCopyTilemapToVram(0);
sStorage->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 = sStorage->menuItems[input].textId;
return input;
}
static void RemoveMenu(void)
{
ClearStdWindowAndFrameToTransparent(sStorage->menuWindowId, TRUE);
RemoveWindow(sStorage->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)
{
sStorage->multiMoveWindowId = AddWindow8Bit(&sWindowTemplate_MultiMove);
if (sStorage->multiMoveWindowId != WINDOW_NONE)
{
FillWindowPixelBuffer(sStorage->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);
TryLoadAllMonIconPalettesAtOffset(0x80);
sMultiMove->state++;
break;
case 1:
GetCursorBoxColumnAndRow(&sMultiMove->fromColumn, &sMultiMove->fromRow);
sMultiMove->toColumn = sMultiMove->fromColumn;
sMultiMove->toRow = sMultiMove->fromRow;
ChangeBgX(0, -1024, BG_COORD_SET);
ChangeBgY(0, -1024, BG_COORD_SET);
FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 0x20, 0x20);
FillWindowPixelBuffer8Bit(sStorage->multiMoveWindowId, PIXEL_FILL(0));
MultiMove_SetIconToBg(sMultiMove->fromColumn, sMultiMove->fromRow);
SetBgAttribute(0, BG_ATTR_PALETTEMODE, 1);
PutWindowTilemap(sStorage->multiMoveWindowId);
CopyWindowToVram8Bit(sStorage->multiMoveWindowId, COPYWIN_FULL);
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(sStorage->multiMoveWindowId, COPYWIN_GFX);
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(sStorage->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(sStorage->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, BG_COORD_ADD);
ChangeBgY(0, sMultiMove->bgY, BG_COORD_ADD);
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))
CreateBoxMonIconAtPos(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, BG_COORD_SET);
ChangeBgY(0, 0, BG_COORD_SET);
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 (sStorage->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);
sStorage->itemIcons[i].tiles = GetSpriteTileStartByTag(spriteSheet.tag) * 32 + (void*)(OBJ_VRAM0);
sStorage->itemIcons[i].palIndex = AllocSpritePalette(PALTAG_ITEM_ICON_0 + i);
sStorage->itemIcons[i].palIndex *= 16;
sStorage->itemIcons[i].palIndex += 0x100;
spriteTemplate.tileTag = GFXTAG_ITEM_ICON_0 + i;
spriteTemplate.paletteTag = PALTAG_ITEM_ICON_0 + i;
spriteId = CreateSprite(&spriteTemplate, 0, 0, 11);
sStorage->itemIcons[i].sprite = &gSprites[spriteId];
sStorage->itemIcons[i].sprite->invisible = TRUE;
sStorage->itemIcons[i].active = FALSE;
}
}
sStorage->movingItemId = ITEM_NONE;
}
static void TryLoadItemIconAtPos(u8 cursorArea, u8 cursorPos)
{
u16 heldItem;
if (sStorage->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 (sStorage->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 (sStorage->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);
}
sStorage->movingItemId = sStorage->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);
sStorage->movingItemId = itemId;
}
static void SwapItemsWithMon(u8 cursorArea, u8 cursorPos)
{
u8 id;
u16 itemId;
if (sStorage->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, &sStorage->movingItemId);
sStorage->movingItemId = itemId;
}
else
{
itemId = GetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM);
SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &sStorage->movingItemId);
sStorage->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 (sStorage->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, &sStorage->movingItemId);
SetBoxMonIconObjMode(cursorPos, 0);
}
else
{
SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &sStorage->movingItemId);
SetPartyMonIconObjMode(cursorPos, 0);
}
}
static void MoveItemFromMonToBag(u8 cursorArea, u8 cursorPos)
{
u8 id;
u16 itemId;
if (sStorage->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 (sStorage->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 (sStorage->boxOption != OPTION_MOVE_ITEMS)
return;
for (i = 0; i < MAX_ITEM_ICONS; i++)
{
if (sStorage->itemIcons[i].active
&& sStorage->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 (sStorage->itemIcons[i].active)
{
if (!sStorage->itemIcons[i].sprite->affineAnimEnded
&& sStorage->itemIcons[i].sprite->affineAnimBeginning)
return TRUE;
if (sStorage->itemIcons[i].sprite->callback != SpriteCallbackDummy
&& sStorage->itemIcons[i].sprite->callback != SpriteCB_ItemIcon_SetPosToCursor)
return TRUE;
}
}
return FALSE;
}
static bool8 IsMovingItem(void)
{
s32 i;
if (sStorage->boxOption == OPTION_MOVE_ITEMS)
{
for (i = 0; i < MAX_ITEM_ICONS; i++)
{
if (sStorage->itemIcons[i].active
&& sStorage->itemIcons[i].area == CURSOR_AREA_IN_HAND)
return TRUE;
}
}
return FALSE;
}
static const u8 *GetMovingItemName(void)
{
return ItemId_GetName(sStorage->movingItemId);
}
static u16 GetMovingItemId(void)
{
return sStorage->movingItemId;
}
static u8 GetNewItemIconIdx(void)
{
u8 i;
for (i = 0; i < MAX_ITEM_ICONS; i++)
{
if (!sStorage->itemIcons[i].active)
{
sStorage->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 (sStorage->itemIcons[i].active
&& sStorage->itemIcons[i].area == cursorArea
&& sStorage->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 (sStorage->itemIcons[i].active
&& sStorage->itemIcons[i].area == cursorArea
&& sStorage->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 (sStorage->itemIcons[i].active
&& sStorage->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;
sStorage->itemIcons[id].sprite->x = (24 * x) + 112;
sStorage->itemIcons[id].sprite->y = (24 * y) + 56;
sStorage->itemIcons[id].sprite->oam.priority = 2;
break;
case CURSOR_AREA_IN_PARTY:
if (cursorPos == 0)
{
sStorage->itemIcons[id].sprite->x = 116;
sStorage->itemIcons[id].sprite->y = 76;
}
else
{
sStorage->itemIcons[id].sprite->x = 164;
sStorage->itemIcons[id].sprite->y = 24 * (cursorPos - 1) + 28;
}
sStorage->itemIcons[id].sprite->oam.priority = 1;
break;
}
sStorage->itemIcons[id].area = cursorArea;
sStorage->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, sStorage->itemIconBuffer, 0x200);
LZ77UnCompWram(itemTiles, sStorage->tileBuffer);
for (i = 0; i < 3; i++)
CpuFastCopy(&sStorage->tileBuffer[i * 0x60], &sStorage->itemIconBuffer[i * 0x80], 0x60);
CpuFastCopy(sStorage->itemIconBuffer, sStorage->itemIcons[id].tiles, 0x200);
LZ77UnCompWram(itemPal, sStorage->itemIconBuffer);
LoadPalette(sStorage->itemIconBuffer, sStorage->itemIcons[id].palIndex, 0x20);
}
static void SetItemIconAffineAnim(u8 id, u8 animNum)
{
if (id >= MAX_ITEM_ICONS)
return;
StartSpriteAffineAnim(sStorage->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:
sStorage->itemIcons[id].sprite->sItemIconId = id;
sStorage->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_WaitAnim;
break;
case ITEM_CB_TO_HAND:
sStorage->itemIcons[id].sprite->sState = 0;
sStorage->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_ToHand;
break;
case ITEM_CB_TO_MON:
sStorage->itemIcons[id].sprite->sState = 0;
sStorage->itemIcons[id].sprite->sCursorArea = cursorArea;
sStorage->itemIcons[id].sprite->sCursorPos = cursorPos;
sStorage->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_ToMon;
break;
case ITEM_CB_SWAP_TO_HAND:
sStorage->itemIcons[id].sprite->sState = 0;
sStorage->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_SwapToHand;
sStorage->itemIcons[id].sprite->sCursorArea = cursorArea;
sStorage->itemIcons[id].sprite->sCursorPos = cursorPos;
break;
case ITEM_CB_SWAP_TO_MON:
sStorage->itemIcons[id].sprite->sState = 0;
sStorage->itemIcons[id].sprite->sCursorArea = cursorArea;
sStorage->itemIcons[id].sprite->sCursorPos = cursorPos;
sStorage->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
sStorage->itemIcons[id].sprite->callback = SpriteCB_ItemIcon_HideParty;
break;
}
}
static void SetItemIconActive(u8 id, bool8 active)
{
if (id >= MAX_ITEM_ICONS)
return;
sStorage->itemIcons[id].active = active;
sStorage->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(sStorage->movingItemId);
else
description = ItemId_GetDescription(sStorage->displayMonItemId);
FillWindowPixelBuffer(2, PIXEL_FILL(1));
AddTextPrinterParameterized5(2, FONT_NORMAL, description, 4, 0, 0, NULL, 0, 1);
}
static void InitItemInfoWindow(void)
{
sStorage->itemInfoWindowOffset = 21;
LoadBgTiles(0, sItemInfoFrame_Gfx, 0x80, 0x13A);
DrawItemInfoWindow(0);
}
static bool8 UpdateItemInfoWindowSlideIn(void)
{
s32 i, pos;
if (sStorage->itemInfoWindowOffset == 0)
return FALSE;
sStorage->itemInfoWindowOffset--;
pos = 21 - sStorage->itemInfoWindowOffset;
for (i = 0; i < pos; i++)
WriteSequenceToBgTilemapBuffer(0, GetBgAttribute(0, BG_ATTR_BASETILE) + 0x14 + sStorage->itemInfoWindowOffset + i, i, 13, 1, 7, 15, 21);
DrawItemInfoWindow(pos);
return (sStorage->itemInfoWindowOffset != 0);
}
static bool8 UpdateItemInfoWindowSlideOut(void)
{
s32 i, pos;
if (sStorage->itemInfoWindowOffset == 22)
return FALSE;
if (sStorage->itemInfoWindowOffset == 0)
FillBgTilemapBufferRect(0, 0, 21, 12, 1, 9, 17);
sStorage->itemInfoWindowOffset++;
pos = 21 - sStorage->itemInfoWindowOffset;
for (i = 0; i < pos; i++)
{
WriteSequenceToBgTilemapBuffer(0, GetBgAttribute(0, BG_ATTR_BASETILE) + 0x14 + sStorage->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->x << 4;
sprite->data[2] = sprite->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->x = sprite->data[1] >> 4;
sprite->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->x = sStorage->cursorSprite->x + 4;
sprite->y = sStorage->cursorSprite->y + sStorage->cursorSprite->y2 + 8;
sprite->oam.priority = sStorage->cursorSprite->oam.priority;
}
static void SpriteCB_ItemIcon_ToMon(struct Sprite *sprite)
{
switch (sprite->sState)
{
case 0:
sprite->data[1] = sprite->x << 4;
sprite->data[2] = sprite->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->x = sprite->data[1] >> 4;
sprite->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->x << 4;
sprite->data[2] = sprite->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->x = sprite->data[1] >> 4;
sprite->y = sprite->data[2] >> 4;
sprite->x2 = gSineTable[sprite->data[5] * 8] >> 4;
if (++sprite->data[5] > 11)
{
SetItemIconPosition(GetItemIconIdxBySprite(sprite), sprite->sCursorArea, sprite->sCursorPos);
sprite->x2 = 0;
sprite->callback = SpriteCB_ItemIcon_SetPosToCursor;
}
break;
}
}
static void SpriteCB_ItemIcon_SwapToMon(struct Sprite *sprite)
{
switch (sprite->sState)
{
case 0:
sprite->data[1] = sprite->x << 4;
sprite->data[2] = sprite->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->x = sprite->data[1] >> 4;
sprite->y = sprite->data[2] >> 4;
sprite->x2 = -(gSineTable[sprite->data[5] * 8] >> 4);
if (++sprite->data[5] > 11)
{
SetItemIconPosition(GetItemIconIdxBySprite(sprite), sprite->sCursorArea, sprite->sCursorPos);
sprite->callback = SpriteCallbackDummy;
sprite->x2 = 0;
}
break;
}
}
static void SpriteCB_ItemIcon_HideParty(struct Sprite *sprite)
{
sprite->y -= 8;
if (sprite->y + sprite->y2 < -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] =
{
[BG_TYPE_NORMAL] = {
{ 256, 256},
{ 512, 256},
{ 256, 512},
{ 512, 512},
},
[BG_TYPE_AFFINE] = {
{ 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 != BG_TYPE_NORMAL)
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;
}
}
//------------------------------------------------------------------------------
// SECTION: UnkUtil
//
// Some data transfer utility that goes functionally unused.
// It gets initialized with UnkUtil_Init, and run every vblank in Pokémon
// Storage with UnkUtil_Run, but neither of the Add functions are ever used,
// so UnkUtil_Run performs no actions.
//------------------------------------------------------------------------------
EWRAM_DATA static struct UnkUtil *sUnkUtil = NULL;
static void UnkUtil_Init(struct UnkUtil *util, struct UnkUtilData *data, u32 max)
{
sUnkUtil = util;
util->data = data;
util->max = max;
util->numActive = 0;
}
static void UnkUtil_Run(void)
{
u16 i;
if (sUnkUtil->numActive)
{
for (i = 0; i < sUnkUtil->numActive; i++)
{
struct UnkUtilData *data = &sUnkUtil->data[i];
data->func(data);
}
sUnkUtil->numActive = 0;
}
}
// Unused
static bool8 UnkUtil_CpuAdd(u8 *dest, u16 dLeft, u16 dTop, const u8 *src, u16 sLeft, u16 sTop, u16 width, u16 height, u16 unkArg)
{
struct UnkUtilData *data;
if (sUnkUtil->numActive >= sUnkUtil->max)
return FALSE;
data = &sUnkUtil->data[sUnkUtil->numActive++];
data->size = width * 2;
data->dest = dest + 2 * (dTop * 32 + dLeft);
data->src = src + 2 * (sTop * unkArg + sLeft);
data->height = height;
data->unk = unkArg;
data->func = UnkUtil_CpuRun;
return TRUE;
}
// Functionally unused
static void UnkUtil_CpuRun(struct UnkUtilData *data)
{
u16 i;
for (i = 0; i < data->height; i++)
{
CpuSet(data->src, data->dest, data->size / 2);
data->dest += 64;
data->src += data->unk * 2;
}
}
// Unused
static bool8 UnkUtil_DmaAdd(void *dest, u16 dLeft, u16 dTop, u16 width, u16 height)
{
struct UnkUtilData *data;
if (sUnkUtil->numActive >= sUnkUtil->max)
return FALSE;
data = &sUnkUtil->data[sUnkUtil->numActive++];
data->size = width * 2;
data->dest = dest + (dTop * 32 + dLeft) * 2;
data->height = height;
data->func = UnkUtil_DmaRun;
return TRUE;
}
// Functionally unused
static void UnkUtil_DmaRun(struct UnkUtilData *data)
{
u16 i;
for (i = 0; i < data->height; i++)
{
Dma3FillLarge16_(0, data->dest, data->size);
data->dest += 64;
}
}