pokeemerald/src/pokenav_list.c
2022-07-29 10:17:58 -04:00

1012 lines
32 KiB
C

#include "global.h"
#include "pokenav.h"
#include "window.h"
#include "strings.h"
#include "text.h"
#include "bg.h"
#include "menu.h"
#include "decompress.h"
#include "international_string_util.h"
#define GFXTAG_ARROW 10
#define PALTAG_ARROW 20
struct PokenavListMenuWindow {
u8 bg;
u8 fillValue;
u8 x;
u8 y;
u8 width;
u8 fontId;
u16 tileOffset;
u16 windowId;
u16 unkA;
u16 numPrinted;
u16 numToPrint;
};
struct PokenavListWindowState {
// The index of the element at the top of the window.
u16 windowTopIndex;
u16 listLength;
u16 entriesOffscreen;
// The index of the cursor, relative to the top of the window.
u16 selectedIndexOffset;
u16 entriesOnscreen;
u32 listItemSize;
void * listPtr;
};
struct PokenavListSub
{
struct PokenavListMenuWindow listWindow;
u32 printStart;
u32 printIndex;
u32 itemSize;
void * listPtr;
s32 startBgY;
s32 endBgY;
u32 loopedTaskId;
s32 moveDelta;
u32 bgMoveType;
PokenavListBufferItemFunc bufferItemFunc;
void (*iconDrawFunc)(u16, u32, u32);
struct Sprite *rightArrow;
struct Sprite *upArrow;
struct Sprite *downArrow;
u8 itemTextBuffer[64];
};
struct PokenavList
{
struct PokenavListSub sub;
u8 tilemapBuffer[BG_SCREEN_SIZE];
struct PokenavListWindowState windowState;
s32 eraseIndex;
u32 loopedTaskId;
};
static void InitPokenavListBg(struct PokenavList *);
static bool32 CopyPokenavListMenuTemplate(struct PokenavListSub *, const struct BgTemplate *, struct PokenavListTemplate *, s32);
static void InitPokenavListWindowState(struct PokenavListWindowState *, struct PokenavListTemplate *);
static void SpriteCB_UpArrow(struct Sprite *);
static void SpriteCB_DownArrow(struct Sprite *);
static void SpriteCB_RightArrow(struct Sprite *);
static void ToggleListArrows(struct PokenavListSub *, bool32);
static void DestroyListArrows(struct PokenavListSub *);
static void CreateListArrowSprites(struct PokenavListWindowState *, struct PokenavListSub *);
static void LoadListArrowGfx(void);
static void PrintMatchCallFlavorText(struct PokenavListWindowState *, struct PokenavListSub *, u32);
static void PrintMatchCallFieldNames(struct PokenavListSub *, u32);
static void PrintMatchCallListTrainerName(struct PokenavListWindowState *, struct PokenavListSub *);
static void PrintCheckPageTrainerName(struct PokenavListWindowState *, struct PokenavListSub *);
static void EraseListEntry(struct PokenavListMenuWindow *, s32, s32);
static void CreateMoveListWindowTask(s32, struct PokenavListSub *);
static void PrintListItems(void *, u32, u32, u32, u32, struct PokenavListSub *);
static void InitListItems(struct PokenavListWindowState *, struct PokenavListSub *);
static void InitPokenavListWindow(struct PokenavListMenuWindow *);
static u32 LoopedTask_CreatePokenavList(s32);
static bool32 IsPrintListItemsTaskActive(void);
static u32 LoopedTask_PrintListItems(s32);
static u32 LoopedTask_MoveListWindow(s32);
static u32 LoopedTask_EraseListForCheckPage(s32);
static u32 LoopedTask_ReshowListFromCheckPage(s32);
static u32 LoopedTask_PrintCheckPageInfo(s32);
static const u16 sListArrow_Pal[] = INCBIN_U16("graphics/pokenav/list_arrows.gbapal");
static const u32 sListArrow_Gfx[] = INCBIN_U32("graphics/pokenav/list_arrows.4bpp.lz");
static EWRAM_DATA u32 sMoveWindowDownIndex = 0; // Read, but pointlessly
bool32 CreatePokenavList(const struct BgTemplate *bgTemplate, struct PokenavListTemplate *listTemplate, s32 tileOffset)
{
struct PokenavList *list = AllocSubstruct(POKENAV_SUBSTRUCT_LIST, sizeof(struct PokenavList));
if (list == NULL)
return FALSE;
InitPokenavListWindowState(&list->windowState, listTemplate);
if (!CopyPokenavListMenuTemplate(&list->sub, bgTemplate, listTemplate, tileOffset))
return FALSE;
CreateLoopedTask(LoopedTask_CreatePokenavList, 6);
return TRUE;
}
bool32 IsCreatePokenavListTaskActive(void)
{
return FuncIsActiveLoopedTask(LoopedTask_CreatePokenavList);
}
void DestroyPokenavList(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
DestroyListArrows(&list->sub);
RemoveWindow(list->sub.listWindow.windowId);
FreePokenavSubstruct(POKENAV_SUBSTRUCT_LIST);
}
static u32 LoopedTask_CreatePokenavList(s32 state)
{
struct PokenavList *list;
if (IsDma3ManagerBusyWithBgCopy())
return LT_PAUSE;
list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
switch (state)
{
case 0:
InitPokenavListBg(list);
return LT_INC_AND_PAUSE;
case 1:
InitPokenavListWindow(&list->sub.listWindow);
return LT_INC_AND_PAUSE;
case 2:
InitListItems(&list->windowState, &list->sub);
return LT_INC_AND_PAUSE;
case 3:
if (IsPrintListItemsTaskActive())
{
return LT_PAUSE;
}
else
{
LoadListArrowGfx();
return LT_INC_AND_CONTINUE;
}
case 4:
CreateListArrowSprites(&list->windowState, &list->sub);
return LT_FINISH;
default:
return LT_FINISH;
}
}
static void InitPokenavListBg(struct PokenavList *list)
{
u16 tileNum = (list->sub.listWindow.fillValue << 12) | list->sub.listWindow.tileOffset;
BgDmaFill(list->sub.listWindow.bg, PIXEL_FILL(1), list->sub.listWindow.tileOffset, 1);
BgDmaFill(list->sub.listWindow.bg, PIXEL_FILL(4), list->sub.listWindow.tileOffset + 1, 1);
SetBgTilemapBuffer(list->sub.listWindow.bg, list->tilemapBuffer);
FillBgTilemapBufferRect_Palette0(list->sub.listWindow.bg, tileNum, 0, 0, 32, 32);
ChangeBgY(list->sub.listWindow.bg, 0, BG_COORD_SET);
ChangeBgX(list->sub.listWindow.bg, 0, BG_COORD_SET);
ChangeBgY(list->sub.listWindow.bg, list->sub.listWindow.y << 11, BG_COORD_SUB);
CopyBgTilemapBufferToVram(list->sub.listWindow.bg);
}
static void InitPokenavListWindow(struct PokenavListMenuWindow *listWindow)
{
FillWindowPixelBuffer(listWindow->windowId, PIXEL_FILL(1));
PutWindowTilemap(listWindow->windowId);
CopyWindowToVram(listWindow->windowId, COPYWIN_MAP);
}
static void InitListItems(struct PokenavListWindowState *windowState, struct PokenavListSub *subPtr)
{
s32 numToPrint = windowState->listLength - windowState->windowTopIndex;
if (numToPrint > windowState->entriesOnscreen)
numToPrint = windowState->entriesOnscreen;
PrintListItems(windowState->listPtr, windowState->windowTopIndex, numToPrint, windowState->listItemSize, 0, subPtr);
}
static void PrintListItems(void * listPtr, u32 topIndex, u32 numItems, u32 itemSize, u32 printStart, struct PokenavListSub *list)
{
if (numItems == 0)
return;
list->listPtr = listPtr + topIndex * itemSize;
list->itemSize = itemSize;
list->listWindow.numPrinted = 0;
list->listWindow.numToPrint = numItems;
list->printIndex = topIndex;
list->printStart = printStart;
CreateLoopedTask(LoopedTask_PrintListItems, 5);
}
static bool32 IsPrintListItemsTaskActive(void)
{
return FuncIsActiveLoopedTask(LoopedTask_PrintListItems);
}
static u32 LoopedTask_PrintListItems(s32 state)
{
u32 row;
struct PokenavListSub *listSub = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
switch (state)
{
case 0:
row = (listSub->listWindow.unkA + listSub->listWindow.numPrinted + listSub->printStart) & 0xF;
listSub->bufferItemFunc(listSub->listPtr, listSub->itemTextBuffer);
if (listSub->iconDrawFunc != NULL)
listSub->iconDrawFunc(listSub->listWindow.windowId, listSub->printIndex, row);
AddTextPrinterParameterized(listSub->listWindow.windowId, listSub->listWindow.fontId, listSub->itemTextBuffer, 8, (row << 4) + 1, TEXT_SKIP_DRAW, NULL);
if (++listSub->listWindow.numPrinted >= listSub->listWindow.numToPrint)
{
// Finished printing items. If icons were being drawn, draw the
// window tilemap and graphics. Otherwise just do the graphics
if (listSub->iconDrawFunc != NULL)
CopyWindowToVram(listSub->listWindow.windowId, COPYWIN_FULL);
else
CopyWindowToVram(listSub->listWindow.windowId, COPYWIN_GFX);
return LT_INC_AND_PAUSE;
}
else
{
listSub->listPtr += listSub->itemSize;
listSub->printIndex++;
return LT_CONTINUE;
}
case 1:
if (IsDma3ManagerBusyWithBgCopy())
return LT_PAUSE;
return LT_FINISH;
}
return LT_FINISH;
}
static bool32 ShouldShowUpArrow(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
return (list->windowState.windowTopIndex != 0);
}
static bool32 ShouldShowDownArrow(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
struct PokenavListWindowState *windowState = &list->windowState;
return (windowState->windowTopIndex + windowState->entriesOnscreen < windowState->listLength);
}
static void MoveListWindow(s32 delta, bool32 printItems)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
struct PokenavListWindowState *windowState = &list->windowState;
if (delta < 0)
{
if (windowState->windowTopIndex + delta < 0)
delta = -1 * windowState->windowTopIndex;
if (printItems)
PrintListItems(windowState->listPtr, windowState->windowTopIndex + delta, delta * -1, windowState->listItemSize, delta, &list->sub);
}
else if (printItems)
{
s32 index = sMoveWindowDownIndex = windowState->windowTopIndex + windowState->entriesOnscreen;
if (index + delta >= windowState->listLength)
delta = windowState->listLength - index;
PrintListItems(windowState->listPtr, index, delta, windowState->listItemSize, windowState->entriesOnscreen, &list->sub);
}
CreateMoveListWindowTask(delta, &list->sub);
windowState->windowTopIndex += delta;
}
static void CreateMoveListWindowTask(s32 delta, struct PokenavListSub *list)
{
list->startBgY = GetBgY(list->listWindow.bg);
list->endBgY = list->startBgY + (delta << 12);
if (delta > 0)
list->bgMoveType = BG_COORD_ADD;
else
list->bgMoveType = BG_COORD_SUB;
list->moveDelta = delta;
list->loopedTaskId = CreateLoopedTask(LoopedTask_MoveListWindow, 6);
}
static u32 LoopedTask_MoveListWindow(s32 state)
{
s32 oldY, newY;
bool32 finished;
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
struct PokenavListSub *subPtr = &list->sub;
switch (state)
{
case 0:
if (!IsPrintListItemsTaskActive())
return LT_INC_AND_CONTINUE;
return LT_PAUSE;
case 1:
finished = FALSE;
oldY = GetBgY(subPtr->listWindow.bg);
newY = ChangeBgY(subPtr->listWindow.bg, 0x1000, subPtr->bgMoveType);
if (subPtr->bgMoveType == BG_COORD_SUB)
{
if ((oldY > subPtr->endBgY || oldY <= subPtr->startBgY) && newY <= subPtr->endBgY)
finished = TRUE;
}
else // BG_COORD_ADD
{
if ((oldY < subPtr->endBgY || oldY >= subPtr->startBgY) && newY >= subPtr->endBgY)
finished = TRUE;
}
if (finished)
{
subPtr->listWindow.unkA = (subPtr->listWindow.unkA + subPtr->moveDelta) & 0xF;
ChangeBgY(subPtr->listWindow.bg, subPtr->endBgY, BG_COORD_SET);
return LT_FINISH;
}
return LT_PAUSE;
}
return LT_FINISH;
}
bool32 PokenavList_IsMoveWindowTaskActive(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
return IsLoopedTaskActive(list->sub.loopedTaskId);
}
static struct PokenavListWindowState *GetPokenavListWindowState(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
return &list->windowState;
}
int PokenavList_MoveCursorUp(void)
{
struct PokenavListWindowState *windowState = GetPokenavListWindowState();
if (windowState->selectedIndexOffset != 0)
{
windowState->selectedIndexOffset--;
return 1;
}
if (ShouldShowUpArrow())
{
MoveListWindow(-1, TRUE);
return 2;
}
return 0;
}
int PokenavList_MoveCursorDown(void)
{
struct PokenavListWindowState *windowState = GetPokenavListWindowState();
if (windowState->windowTopIndex + windowState->selectedIndexOffset >= windowState->listLength - 1)
return 0;
if (windowState->selectedIndexOffset < windowState->entriesOnscreen - 1)
{
windowState->selectedIndexOffset++;
return 1;
}
if (ShouldShowDownArrow())
{
MoveListWindow(1, TRUE);
return 2;
}
return 0;
}
int PokenavList_PageUp(void)
{
s32 scroll;
struct PokenavListWindowState *windowState = GetPokenavListWindowState();
if (ShouldShowUpArrow())
{
if (windowState->windowTopIndex >= windowState->entriesOnscreen)
scroll = windowState->entriesOnscreen;
else
scroll = windowState->windowTopIndex;
MoveListWindow(scroll * -1, TRUE);
return 2;
}
else if (windowState->selectedIndexOffset != 0)
{
windowState->selectedIndexOffset = 0;
return 1;
}
return 0;
}
int PokenavList_PageDown(void)
{
struct PokenavListWindowState *windowState = GetPokenavListWindowState();
if (ShouldShowDownArrow())
{
s32 windowBottomIndex = windowState->windowTopIndex + windowState->entriesOnscreen;
s32 scroll = windowState->entriesOffscreen - windowState->windowTopIndex;
if (windowBottomIndex <= windowState->entriesOffscreen)
scroll = windowState->entriesOnscreen;
MoveListWindow(scroll, TRUE);
return 2;
}
else
{
s32 cursor, lastVisibleIndex;
if (windowState->listLength >= windowState->entriesOnscreen)
{
cursor = windowState->selectedIndexOffset;
lastVisibleIndex = windowState->entriesOnscreen;
}
else
{
cursor = windowState->selectedIndexOffset;
lastVisibleIndex = windowState->listLength;
}
lastVisibleIndex -= 1;
if (cursor >= lastVisibleIndex)
return 0;
windowState->selectedIndexOffset = lastVisibleIndex;
return 1;
}
}
u32 PokenavList_GetSelectedIndex(void)
{
struct PokenavListWindowState *windowState = GetPokenavListWindowState();
return windowState->windowTopIndex + windowState->selectedIndexOffset;
}
u32 PokenavList_GetTopIndex(void)
{
struct PokenavListWindowState *windowState = GetPokenavListWindowState();
return windowState->windowTopIndex;
}
void PokenavList_EraseListForCheckPage(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
list->eraseIndex = 0;
list->loopedTaskId = CreateLoopedTask(LoopedTask_EraseListForCheckPage, 6);
}
void PrintCheckPageInfo(s16 delta)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
list->windowState.windowTopIndex += delta;
list->eraseIndex = 0;
list->loopedTaskId = CreateLoopedTask(LoopedTask_PrintCheckPageInfo, 6);
}
void PokenavList_ReshowListFromCheckPage(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
list->eraseIndex = 0;
list->loopedTaskId = CreateLoopedTask(LoopedTask_ReshowListFromCheckPage, 6);
}
bool32 PokenavList_IsTaskActive(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
return IsLoopedTaskActive(list->loopedTaskId);
}
void PokenavList_DrawCurrentItemIcon(void)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
struct PokenavListWindowState *windowState = &list->windowState;
list->sub.iconDrawFunc(list->sub.listWindow.windowId, windowState->windowTopIndex + windowState->selectedIndexOffset, (list->sub.listWindow.unkA + windowState->selectedIndexOffset) & 0xF);
CopyWindowToVram(list->sub.listWindow.windowId, COPYWIN_MAP);
}
static u32 LoopedTask_EraseListForCheckPage(s32 state)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
switch (state)
{
case 0:
ToggleListArrows(&list->sub, TRUE);
// fall-through
case 1:
if (list->eraseIndex != list->windowState.selectedIndexOffset)
EraseListEntry(&list->sub.listWindow, list->eraseIndex, 1);
list->eraseIndex++;
return LT_INC_AND_PAUSE;
case 2:
if (!IsDma3ManagerBusyWithBgCopy())
{
if (list->eraseIndex != list->windowState.entriesOnscreen)
return LT_SET_STATE(1);
if (list->windowState.selectedIndexOffset != 0)
EraseListEntry(&list->sub.listWindow, list->eraseIndex, list->windowState.selectedIndexOffset);
return LT_INC_AND_PAUSE;
}
return LT_PAUSE;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
{
if (list->windowState.selectedIndexOffset != 0)
{
MoveListWindow(list->windowState.selectedIndexOffset, FALSE);
return LT_INC_AND_PAUSE;
}
return LT_FINISH;
}
return LT_PAUSE;
case 4:
if (PokenavList_IsMoveWindowTaskActive())
return LT_PAUSE;
list->windowState.selectedIndexOffset = 0;
return LT_FINISH;
}
return LT_FINISH;
}
static u32 LoopedTask_PrintCheckPageInfo(s32 state)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
if (IsDma3ManagerBusyWithBgCopy())
return LT_PAUSE;
switch (state)
{
case 0:
PrintCheckPageTrainerName(&list->windowState, &list->sub);
break;
case 1:
PrintMatchCallFieldNames(&list->sub, 0);
break;
case 2:
PrintMatchCallFlavorText(&list->windowState, &list->sub, CHECK_PAGE_STRATEGY);
break;
case 3:
PrintMatchCallFieldNames(&list->sub, 1);
break;
case 4:
PrintMatchCallFlavorText(&list->windowState, &list->sub, CHECK_PAGE_POKEMON);
break;
case 5:
PrintMatchCallFieldNames(&list->sub, 2);
break;
case 6:
PrintMatchCallFlavorText(&list->windowState, &list->sub, CHECK_PAGE_INTRO_1);
break;
case 7:
PrintMatchCallFlavorText(&list->windowState, &list->sub, CHECK_PAGE_INTRO_2);
break;
default:
return LT_FINISH;
}
return LT_INC_AND_PAUSE;
}
static u32 LoopedTask_ReshowListFromCheckPage(s32 state)
{
struct PokenavList *list;
struct PokenavListWindowState *windowState;
struct PokenavListSub *subPtr;
s32 r5, *ptr;
if (IsDma3ManagerBusyWithBgCopy())
return LT_PAUSE;
list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
windowState = &list->windowState;
subPtr = &list->sub;
switch (state)
{
case 0:
// Rewrite the name of the trainer whose check page was just being viewed.
// This is done to erase the red background it had.
PrintMatchCallListTrainerName(windowState, subPtr);
return LT_INC_AND_PAUSE;
case 1:
ptr = &list->eraseIndex;
if (++(*ptr) < list->windowState.entriesOnscreen)
{
EraseListEntry(&subPtr->listWindow, *ptr, 1);
return LT_PAUSE;
}
*ptr = 0;
if (windowState->listLength <= windowState->entriesOnscreen)
{
if (windowState->windowTopIndex != 0)
{
s32 r4 = windowState->windowTopIndex;
r5 = -r4;
EraseListEntry(&subPtr->listWindow, r5, r4);
windowState->selectedIndexOffset = r4;
*ptr = r5;
return LT_INC_AND_PAUSE;
}
}
else
{
if (windowState->windowTopIndex + windowState->entriesOnscreen > windowState->listLength)
{
s32 r4 = windowState->windowTopIndex + windowState->entriesOnscreen - windowState->listLength;
r5 = -r4;
EraseListEntry(&subPtr->listWindow, r5, r4);
windowState->selectedIndexOffset = r4;
*ptr = r5;
return LT_INC_AND_PAUSE;
}
}
return LT_SET_STATE(4);
case 2:
MoveListWindow(list->eraseIndex, FALSE);
return LT_INC_AND_PAUSE;
case 3:
if (!PokenavList_IsMoveWindowTaskActive())
{
list->eraseIndex = 0;
return LT_INC_AND_CONTINUE;
}
return LT_PAUSE;
case 4:
PrintListItems(windowState->listPtr, windowState->windowTopIndex + list->eraseIndex, 1, windowState->listItemSize, list->eraseIndex, &list->sub);
return LT_INC_AND_PAUSE;
case 5:
if (IsPrintListItemsTaskActive())
return LT_PAUSE;
if (++list->eraseIndex >= windowState->listLength || list->eraseIndex >= windowState->entriesOnscreen)
return LT_INC_AND_CONTINUE;
return LT_SET_STATE(4);
case 6:
ToggleListArrows(subPtr, FALSE);
return LT_FINISH;
}
return LT_FINISH;
}
static void EraseListEntry(struct PokenavListMenuWindow *listWindow, s32 offset, s32 entries)
{
u8 *tileData = (u8 *)GetWindowAttribute(listWindow->windowId, WINDOW_TILE_DATA);
u32 width = listWindow->width * 64;
offset = (listWindow->unkA + offset) & 0xF;
if (offset + entries <= 16)
{
CpuFastFill8(PIXEL_FILL(1), tileData + offset * width, entries * width);
CopyWindowToVram(listWindow->windowId, COPYWIN_GFX);
}
else
{
u32 v3 = 16 - offset;
u32 v4 = entries - v3;
CpuFastFill8(PIXEL_FILL(1), tileData + offset * width, v3 * width);
CpuFastFill8(PIXEL_FILL(1), tileData, v4 * width);
CopyWindowToVram(listWindow->windowId, COPYWIN_GFX);
}
for (entries--; entries != -1; offset = (offset + 1) & 0xF, entries--)
ClearRematchPokeballIcon(listWindow->windowId, offset);
CopyWindowToVram(listWindow->windowId, COPYWIN_MAP);
}
// Pointless
static void SetListMarginTile(struct PokenavListMenuWindow *listWindow, bool32 draw)
{
u16 var;
u16 *tilemapBuffer = (u16 *)GetBgTilemapBuffer(GetWindowAttribute(listWindow->windowId, WINDOW_BG));
tilemapBuffer += (listWindow->unkA << 6) + listWindow->x - 1;
if (draw)
var = (listWindow->fillValue << 12) | (listWindow->tileOffset + 1);
else
var = (listWindow->fillValue << 12) | (listWindow->tileOffset);
tilemapBuffer[0] = var;
tilemapBuffer[0x20] = var;
}
// Print the trainer's name and title at the top of their check page
static void PrintCheckPageTrainerName(struct PokenavListWindowState *state, struct PokenavListSub *list)
{
u8 colors[3] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_RED};
list->bufferItemFunc(state->listPtr + state->listItemSize * state->windowTopIndex, list->itemTextBuffer);
list->iconDrawFunc(list->listWindow.windowId, state->windowTopIndex, list->listWindow.unkA);
FillWindowPixelRect(list->listWindow.windowId, PIXEL_FILL(4), 0, list->listWindow.unkA * 16, list->listWindow.width * 8, 16);
AddTextPrinterParameterized3(list->listWindow.windowId, list->listWindow.fontId, 8, (list->listWindow.unkA * 16) + 1, colors, TEXT_SKIP_DRAW, list->itemTextBuffer);
SetListMarginTile(&list->listWindow, TRUE);
CopyWindowRectToVram(list->listWindow.windowId, COPYWIN_FULL, 0, list->listWindow.unkA * 2, list->listWindow.width, 2);
}
// Print the trainer's name and title for the list (to replace the check page name and title, which has a red background)
static void PrintMatchCallListTrainerName(struct PokenavListWindowState *state, struct PokenavListSub *list)
{
list->bufferItemFunc(state->listPtr + state->listItemSize * state->windowTopIndex, list->itemTextBuffer);
FillWindowPixelRect(list->listWindow.windowId, PIXEL_FILL(1), 0, list->listWindow.unkA * 16, list->listWindow.width * 8, 16);
AddTextPrinterParameterized(list->listWindow.windowId, list->listWindow.fontId, list->itemTextBuffer, 8, list->listWindow.unkA * 16 + 1, TEXT_SKIP_DRAW, NULL);
SetListMarginTile(&list->listWindow, FALSE);
CopyWindowToVram(list->listWindow.windowId, COPYWIN_FULL);
}
static void PrintMatchCallFieldNames(struct PokenavListSub *list, u32 fieldId)
{
const u8 *fieldNames[] = {
gText_PokenavMatchCall_Strategy,
gText_PokenavMatchCall_TrainerPokemon,
gText_PokenavMatchCall_SelfIntroduction
};
u8 colors[3] = {TEXT_COLOR_WHITE, TEXT_COLOR_RED, TEXT_COLOR_LIGHT_RED};
u32 top = (list->listWindow.unkA + 1 + (fieldId * 2)) & 0xF;
FillWindowPixelRect(list->listWindow.windowId, PIXEL_FILL(1), 0, top << 4, list->listWindow.width, 16);
AddTextPrinterParameterized3(list->listWindow.windowId, FONT_NARROW, 2, (top << 4) + 1, colors, TEXT_SKIP_DRAW, fieldNames[fieldId]);
CopyWindowRectToVram(list->listWindow.windowId, COPYWIN_GFX, 0, top << 1, list->listWindow.width, 2);
}
static void PrintMatchCallFlavorText(struct PokenavListWindowState *windowState, struct PokenavListSub *list, u32 checkPageEntry)
{
// lines 1, 3, and 5 are the field names printed by PrintMatchCallFieldNames
static const u8 lineOffsets[CHECK_PAGE_ENTRY_COUNT] = {
[CHECK_PAGE_STRATEGY] = 2,
[CHECK_PAGE_POKEMON] = 4,
[CHECK_PAGE_INTRO_1] = 6,
[CHECK_PAGE_INTRO_2] = 7
};
u32 r6 = (list->listWindow.unkA + lineOffsets[checkPageEntry]) & 0xF;
const u8 *str = GetMatchCallFlavorText(windowState->windowTopIndex, checkPageEntry);
if (str != NULL)
{
FillWindowTilesByRow(list->listWindow.windowId, 1, r6 * 2, list->listWindow.width - 1, 2);
AddTextPrinterParameterized(list->listWindow.windowId, FONT_NARROW, str, 2, (r6 << 4) + 1, TEXT_SKIP_DRAW, NULL);
CopyWindowRectToVram(list->listWindow.windowId, COPYWIN_GFX, 0, r6 * 2, list->listWindow.width, 2);
}
}
static const struct CompressedSpriteSheet sListArrowSpriteSheets[] =
{
{
.data = sListArrow_Gfx,
.size = 0xC0,
.tag = GFXTAG_ARROW
}
};
static const struct SpritePalette sListArrowPalettes[] =
{
{
.data = sListArrow_Pal,
.tag = PALTAG_ARROW
},
{}
};
static const struct OamData sOamData_RightArrow =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(8x16),
.x = 0,
.size = SPRITE_SIZE(8x16),
.tileNum = 0,
.priority = 2,
.paletteNum = 0
};
static const struct SpriteTemplate sSpriteTemplate_RightArrow =
{
.tileTag = GFXTAG_ARROW,
.paletteTag = PALTAG_ARROW,
.oam = &sOamData_RightArrow,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_RightArrow
};
static const struct OamData sOamData_UpDownArrow =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x8),
.x = 0,
.size = SPRITE_SIZE(16x8),
.tileNum = 0,
.priority = 2,
.paletteNum = 0
};
static const struct SpriteTemplate sSpriteTemplate_UpDownArrow =
{
.tileTag = GFXTAG_ARROW,
.paletteTag = PALTAG_ARROW,
.oam = &sOamData_UpDownArrow,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
static void LoadListArrowGfx(void)
{
u32 i;
const struct CompressedSpriteSheet *ptr;
for (i = 0, ptr = sListArrowSpriteSheets; i < ARRAY_COUNT(sListArrowSpriteSheets); ptr++, i++)
LoadCompressedSpriteSheet(ptr);
Pokenav_AllocAndLoadPalettes(sListArrowPalettes);
}
static void CreateListArrowSprites(struct PokenavListWindowState *windowState, struct PokenavListSub *list)
{
u32 spriteId;
s16 x;
spriteId = CreateSprite(&sSpriteTemplate_RightArrow, list->listWindow.x * 8 + 3, (list->listWindow.y + 1) * 8, 7);
list->rightArrow = &gSprites[spriteId];
x = list->listWindow.x * 8 + (list->listWindow.width - 1) * 4;
spriteId = CreateSprite(&sSpriteTemplate_UpDownArrow, x, list->listWindow.y * 8 + windowState->entriesOnscreen * 16, 7);
list->downArrow = &gSprites[spriteId];
list->downArrow->oam.tileNum += 2;
list->downArrow->callback = SpriteCB_DownArrow;
spriteId = CreateSprite(&sSpriteTemplate_UpDownArrow, x, list->listWindow.y * 8, 7);
list->upArrow = &gSprites[spriteId];
list->upArrow->oam.tileNum += 4;
list->upArrow->callback = SpriteCB_UpArrow;
}
static void DestroyListArrows(struct PokenavListSub *list)
{
DestroySprite(list->rightArrow);
DestroySprite(list->upArrow);
DestroySprite(list->downArrow);
FreeSpriteTilesByTag(GFXTAG_ARROW);
FreeSpritePaletteByTag(PALTAG_ARROW);
}
static void ToggleListArrows(struct PokenavListSub *list, bool32 invisible)
{
if (invisible)
{
list->rightArrow->callback = SpriteCallbackDummy;
list->upArrow->callback = SpriteCallbackDummy;
list->downArrow->callback = SpriteCallbackDummy;
}
else
{
list->rightArrow->callback = SpriteCB_RightArrow;
list->upArrow->callback = SpriteCB_UpArrow;
list->downArrow->callback = SpriteCB_DownArrow;
}
list->rightArrow->invisible = invisible;
list->upArrow->invisible = invisible;
list->downArrow->invisible = invisible;
}
static void SpriteCB_RightArrow(struct Sprite *sprite)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
sprite->y2 = list->windowState.selectedIndexOffset << 4;
}
#define sTimer data[0]
#define sOffset data[1]
#define sInvisible data[7]
static void SpriteCB_DownArrow(struct Sprite *sprite)
{
if (!sprite->sInvisible && ShouldShowDownArrow())
sprite->invisible = FALSE;
else
sprite->invisible = TRUE;
if (++sprite->sTimer > 3)
{
s16 offset;
sprite->sTimer = 0;
offset = (sprite->sOffset + 1) & 7;
sprite->sOffset = offset;
sprite->y2 = offset;
}
}
static void SpriteCB_UpArrow(struct Sprite *sprite)
{
if (!sprite->sInvisible && ShouldShowUpArrow())
sprite->invisible = FALSE;
else
sprite->invisible = TRUE;
if (++sprite->sTimer > 3)
{
s16 offset;
sprite->sTimer = 0;
offset = (sprite->sOffset + 1) & 7;
sprite->sOffset = offset;
sprite->y2 = -1 * offset;
}
}
void PokenavList_ToggleVerticalArrows(bool32 invisible)
{
struct PokenavList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_LIST);
list->sub.upArrow->sInvisible = invisible;
list->sub.downArrow->sInvisible = invisible;
}
#undef sTimer
#undef sOffset
#undef sInvisible
static void InitPokenavListWindowState(struct PokenavListWindowState *dst, struct PokenavListTemplate *template)
{
dst->listPtr = template->list;
dst->windowTopIndex = template->startIndex;
dst->listLength = template->count;
dst->listItemSize = template->itemSize;
dst->entriesOnscreen = template->maxShowed;
if (dst->entriesOnscreen >= dst->listLength)
{
dst->windowTopIndex = 0;
dst->entriesOffscreen = 0;
dst->selectedIndexOffset = template->startIndex;
}
else
{
dst->entriesOffscreen = dst->listLength - dst->entriesOnscreen;
if (dst->windowTopIndex + dst->entriesOnscreen > dst->listLength)
{
dst->selectedIndexOffset = dst->windowTopIndex + dst->entriesOnscreen - dst->listLength;
dst->windowTopIndex = template->startIndex - dst->selectedIndexOffset;
}
else
{
dst->selectedIndexOffset = 0;
}
}
}
static bool32 CopyPokenavListMenuTemplate(struct PokenavListSub *dest, const struct BgTemplate *bgTemplate, struct PokenavListTemplate *template, s32 tileOffset)
{
struct WindowTemplate window;
dest->listWindow.bg = bgTemplate->bg;
dest->listWindow.tileOffset = tileOffset;
dest->bufferItemFunc = template->bufferItemFunc;
dest->iconDrawFunc = template->iconDrawFunc;
dest->listWindow.fillValue = template->fillValue;
dest->listWindow.x = template->item_X;
dest->listWindow.y = template->listTop;
dest->listWindow.width = template->windowWidth;
dest->listWindow.fontId = template->fontId;
window.bg = bgTemplate->bg;
window.tilemapLeft = template->item_X;
window.tilemapTop = 0;
window.width = template->windowWidth;
window.height = 32;
window.paletteNum = template->fillValue;
window.baseBlock = tileOffset + 2;
dest->listWindow.windowId = AddWindow(&window);
if (dest->listWindow.windowId == WINDOW_NONE)
return FALSE;
dest->listWindow.unkA = 0;
dest->rightArrow = NULL;
dest->upArrow = NULL;
dest->downArrow = NULL;
return 1;
}