1627 lines
48 KiB
C
1627 lines
48 KiB
C
|
#include "global.h"
|
||
|
#include "malloc.h"
|
||
|
#include "battle_main.h"
|
||
|
#include "contest_effect.h"
|
||
|
#include "data.h"
|
||
|
#include "decompress.h"
|
||
|
#include "gpu_regs.h"
|
||
|
#include "graphics.h"
|
||
|
#include "menu.h"
|
||
|
#include "international_string_util.h"
|
||
|
#include "menu.h"
|
||
|
#include "menu_specialized.h"
|
||
|
#include "move_relearner.h"
|
||
|
#include "palette.h"
|
||
|
#include "player_pc.h"
|
||
|
#include "pokemon_summary_screen.h"
|
||
|
#include "pokemon_storage_system.h"
|
||
|
#include "scanline_effect.h"
|
||
|
#include "sound.h"
|
||
|
#include "strings.h"
|
||
|
#include "string_util.h"
|
||
|
#include "text.h"
|
||
|
#include "text_window.h"
|
||
|
#include "trig.h"
|
||
|
#include "window.h"
|
||
|
#include "constants/songs.h"
|
||
|
#include "gba/io_reg.h"
|
||
|
|
||
|
extern const struct CompressedSpriteSheet gMonFrontPicTable[];
|
||
|
|
||
|
EWRAM_DATA static u8 sMailboxWindowIds[MAILBOXWIN_COUNT] = {0};
|
||
|
EWRAM_DATA static struct ListMenuItem *sMailboxList = NULL;
|
||
|
|
||
|
static void MailboxMenu_MoveCursorFunc(s32, bool8, struct ListMenu *);
|
||
|
static void ConditionGraph_CalcRightHalf(struct ConditionGraph *);
|
||
|
static void ConditionGraph_CalcLeftHalf(struct ConditionGraph *);
|
||
|
static void MoveRelearnerCursorCallback(s32, bool8, struct ListMenu *);
|
||
|
static void MoveRelearnerDummy(void);
|
||
|
static void SetNextConditionSparkle(struct Sprite *);
|
||
|
static void SpriteCB_ConditionSparkle(struct Sprite *);
|
||
|
static void ShowAllConditionSparkles(struct Sprite *);
|
||
|
|
||
|
static const struct WindowTemplate sWindowTemplates_MailboxMenu[MAILBOXWIN_COUNT] =
|
||
|
{
|
||
|
[MAILBOXWIN_TITLE] = {
|
||
|
.bg = 0,
|
||
|
.tilemapLeft = 1,
|
||
|
.tilemapTop = 1,
|
||
|
.width = 8,
|
||
|
.height = 2,
|
||
|
.paletteNum = 15,
|
||
|
.baseBlock = 0x8
|
||
|
},
|
||
|
[MAILBOXWIN_LIST] = {
|
||
|
.bg = 0,
|
||
|
.tilemapLeft = 21,
|
||
|
.tilemapTop = 1,
|
||
|
.width = 8,
|
||
|
.height = 18,
|
||
|
.paletteNum = 15,
|
||
|
.baseBlock = 0x18
|
||
|
},
|
||
|
[MAILBOXWIN_OPTIONS] = {
|
||
|
.bg = 0,
|
||
|
.tilemapLeft = 1,
|
||
|
.tilemapTop = 1,
|
||
|
.width = 11,
|
||
|
.height = 8,
|
||
|
.paletteNum = 15,
|
||
|
.baseBlock = 0x18
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const u8 sPlayerNameTextColors[] =
|
||
|
{
|
||
|
TEXT_COLOR_WHITE, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY
|
||
|
};
|
||
|
|
||
|
static const u8 sEmptyItemName[] = _("");
|
||
|
|
||
|
static const struct ScanlineEffectParams sConditionGraphScanline =
|
||
|
{
|
||
|
.dmaDest = ®_WIN0H,
|
||
|
.dmaControl = SCANLINE_EFFECT_DMACNT_32BIT,
|
||
|
.initState = 1,
|
||
|
};
|
||
|
|
||
|
static const u8 sConditionToLineLength[MAX_CONDITION + 1] =
|
||
|
{
|
||
|
4, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 13,
|
||
|
13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17,
|
||
|
17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19,
|
||
|
19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21,
|
||
|
21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23,
|
||
|
23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||
|
24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26,
|
||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27,
|
||
|
27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||
|
28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||
|
29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
|
||
|
30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
|
||
|
31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||
|
32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33,
|
||
|
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34,
|
||
|
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35
|
||
|
};
|
||
|
|
||
|
|
||
|
static const struct WindowTemplate sMoveRelearnerWindowTemplates[] =
|
||
|
{
|
||
|
{
|
||
|
.bg = 1,
|
||
|
.tilemapLeft = 1,
|
||
|
.tilemapTop = 1,
|
||
|
.width = 16,
|
||
|
.height = 12,
|
||
|
.paletteNum = 0xF,
|
||
|
.baseBlock = 0xA
|
||
|
},
|
||
|
{
|
||
|
.bg = 1,
|
||
|
.tilemapLeft = 1,
|
||
|
.tilemapTop = 1,
|
||
|
.width = 16,
|
||
|
.height = 12,
|
||
|
.paletteNum = 0xF,
|
||
|
.baseBlock = 0xCA
|
||
|
},
|
||
|
{
|
||
|
.bg = 1,
|
||
|
.tilemapLeft = 19,
|
||
|
.tilemapTop = 1,
|
||
|
.width = 10,
|
||
|
.height = 12,
|
||
|
.paletteNum = 0xF,
|
||
|
.baseBlock = 0x18A
|
||
|
},
|
||
|
{
|
||
|
.bg = 1,
|
||
|
.tilemapLeft = 4,
|
||
|
.tilemapTop = 15,
|
||
|
.width = 22,
|
||
|
.height = 4,
|
||
|
.paletteNum = 0xF,
|
||
|
.baseBlock = 0x202
|
||
|
},
|
||
|
{
|
||
|
.bg = 0,
|
||
|
.tilemapLeft = 22,
|
||
|
.tilemapTop = 8,
|
||
|
.width = 5,
|
||
|
.height = 4,
|
||
|
.paletteNum = 0xF,
|
||
|
.baseBlock = 0x25A
|
||
|
},
|
||
|
DUMMY_WIN_TEMPLATE
|
||
|
};
|
||
|
|
||
|
static const struct WindowTemplate sMoveRelearnerYesNoMenuTemplate =
|
||
|
{
|
||
|
.bg = 0,
|
||
|
.tilemapLeft = 22,
|
||
|
.tilemapTop = 8,
|
||
|
.width = 5,
|
||
|
.height = 4,
|
||
|
.paletteNum = 0xF,
|
||
|
.baseBlock = 0x25A
|
||
|
};
|
||
|
|
||
|
|
||
|
static const struct ListMenuTemplate sMoveRelearnerMovesListTemplate =
|
||
|
{
|
||
|
.items = NULL,
|
||
|
.moveCursorFunc = MoveRelearnerCursorCallback,
|
||
|
.itemPrintFunc = NULL,
|
||
|
.totalItems = 0,
|
||
|
.maxShowed = 0,
|
||
|
.windowId = 2,
|
||
|
.header_X = 0,
|
||
|
.item_X = 8,
|
||
|
.cursor_X = 0,
|
||
|
.upText_Y = 1,
|
||
|
.cursorPal = 2,
|
||
|
.fillValue = 1,
|
||
|
.cursorShadowPal = 3,
|
||
|
.lettersSpacing = 0,
|
||
|
.itemVerticalPadding = 0,
|
||
|
.scrollMultiple = LIST_NO_MULTIPLE_SCROLL,
|
||
|
.fontId = FONT_NORMAL,
|
||
|
.cursorKind = 0
|
||
|
};
|
||
|
|
||
|
//--------------
|
||
|
// Mailbox menu
|
||
|
//--------------
|
||
|
|
||
|
bool8 MailboxMenu_Alloc(u8 count)
|
||
|
{
|
||
|
u8 i;
|
||
|
|
||
|
// + 1 to count for 'Cancel'
|
||
|
sMailboxList = Alloc((count + 1) * sizeof(*sMailboxList));
|
||
|
if (sMailboxList == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
for (i = 0; i < ARRAY_COUNT(sMailboxWindowIds); i++)
|
||
|
sMailboxWindowIds[i] = WINDOW_NONE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
u8 MailboxMenu_AddWindow(u8 windowIdx)
|
||
|
{
|
||
|
if (sMailboxWindowIds[windowIdx] == WINDOW_NONE)
|
||
|
{
|
||
|
if (windowIdx == MAILBOXWIN_OPTIONS)
|
||
|
{
|
||
|
struct WindowTemplate template = sWindowTemplates_MailboxMenu[windowIdx];
|
||
|
template.width = GetMaxWidthInMenuTable(&gMailboxMailOptions[0], 4);
|
||
|
sMailboxWindowIds[windowIdx] = AddWindow(&template);
|
||
|
}
|
||
|
else // MAILBOXWIN_TITLE or MAILBOXWIN_LIST
|
||
|
{
|
||
|
sMailboxWindowIds[windowIdx] = AddWindow(&sWindowTemplates_MailboxMenu[windowIdx]);
|
||
|
}
|
||
|
SetStandardWindowBorderStyle(sMailboxWindowIds[windowIdx], 0);
|
||
|
}
|
||
|
return sMailboxWindowIds[windowIdx];
|
||
|
}
|
||
|
|
||
|
void MailboxMenu_RemoveWindow(u8 windowIdx)
|
||
|
{
|
||
|
ClearStdWindowAndFrameToTransparent(sMailboxWindowIds[windowIdx], 0);
|
||
|
ClearWindowTilemap(sMailboxWindowIds[windowIdx]);
|
||
|
RemoveWindow(sMailboxWindowIds[windowIdx]);
|
||
|
sMailboxWindowIds[windowIdx] = WINDOW_NONE;
|
||
|
}
|
||
|
|
||
|
// Unused
|
||
|
static u8 MailboxMenu_GetWindowId(u8 windowIdx)
|
||
|
{
|
||
|
return sMailboxWindowIds[windowIdx];
|
||
|
}
|
||
|
|
||
|
static void MailboxMenu_ItemPrintFunc(u8 windowId, u32 itemId, u8 y)
|
||
|
{
|
||
|
u8 buffer[30];
|
||
|
u16 length;
|
||
|
|
||
|
if (itemId == LIST_CANCEL)
|
||
|
return;
|
||
|
|
||
|
StringCopy(buffer, gSaveBlock1Ptr->mail[PARTY_SIZE + itemId].playerName);
|
||
|
ConvertInternationalPlayerName(buffer);
|
||
|
length = StringLength(buffer);
|
||
|
if (length < PLAYER_NAME_LENGTH - 1)
|
||
|
ConvertInternationalString(buffer, LANGUAGE_JAPANESE);
|
||
|
AddTextPrinterParameterized4(windowId, FONT_NORMAL, 8, y, 0, 0, sPlayerNameTextColors, TEXT_SKIP_DRAW, buffer);
|
||
|
}
|
||
|
|
||
|
u8 MailboxMenu_CreateList(struct PlayerPCItemPageStruct *page)
|
||
|
{
|
||
|
u16 i;
|
||
|
for (i = 0; i < page->count; i++)
|
||
|
{
|
||
|
sMailboxList[i].name = sEmptyItemName;
|
||
|
sMailboxList[i].id = i;
|
||
|
}
|
||
|
|
||
|
sMailboxList[i].name = gText_Cancel2;
|
||
|
sMailboxList[i].id = LIST_CANCEL;
|
||
|
|
||
|
gMultiuseListMenuTemplate.items = sMailboxList;
|
||
|
gMultiuseListMenuTemplate.totalItems = page->count + 1;
|
||
|
gMultiuseListMenuTemplate.windowId = sMailboxWindowIds[MAILBOXWIN_LIST];
|
||
|
gMultiuseListMenuTemplate.header_X = 0;
|
||
|
gMultiuseListMenuTemplate.item_X = 8;
|
||
|
gMultiuseListMenuTemplate.cursor_X = 0;
|
||
|
gMultiuseListMenuTemplate.maxShowed = 8;
|
||
|
gMultiuseListMenuTemplate.upText_Y = 9;
|
||
|
gMultiuseListMenuTemplate.cursorPal = 2;
|
||
|
gMultiuseListMenuTemplate.fillValue = 1;
|
||
|
gMultiuseListMenuTemplate.cursorShadowPal = 3;
|
||
|
gMultiuseListMenuTemplate.moveCursorFunc = MailboxMenu_MoveCursorFunc;
|
||
|
gMultiuseListMenuTemplate.itemPrintFunc = MailboxMenu_ItemPrintFunc;
|
||
|
gMultiuseListMenuTemplate.fontId = FONT_NORMAL;
|
||
|
gMultiuseListMenuTemplate.cursorKind = 0;
|
||
|
gMultiuseListMenuTemplate.lettersSpacing = 0;
|
||
|
gMultiuseListMenuTemplate.itemVerticalPadding = 0;
|
||
|
gMultiuseListMenuTemplate.scrollMultiple = LIST_NO_MULTIPLE_SCROLL;
|
||
|
return ListMenuInit(&gMultiuseListMenuTemplate, page->itemsAbove, page->cursorPos);
|
||
|
}
|
||
|
|
||
|
static void MailboxMenu_MoveCursorFunc(s32 itemIndex, bool8 onInit, struct ListMenu *list)
|
||
|
{
|
||
|
if (onInit != TRUE)
|
||
|
PlaySE(SE_SELECT);
|
||
|
}
|
||
|
|
||
|
void MailboxMenu_AddScrollArrows(struct PlayerPCItemPageStruct *page)
|
||
|
{
|
||
|
page->scrollIndicatorTaskId = AddScrollIndicatorArrowPairParameterized(2, 0xC8, 12, 0x94, page->count - page->pageItems + 1, 0x6E, 0x6E, &page->itemsAbove);
|
||
|
}
|
||
|
|
||
|
void MailboxMenu_Free(void)
|
||
|
{
|
||
|
Free(sMailboxList);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------
|
||
|
// Condition graph
|
||
|
//
|
||
|
// This is the graph in the Pokénav and
|
||
|
// Pokéblock case that shows a Pokémon's
|
||
|
// conditions (Beauty, Tough, etc.).
|
||
|
// It works by using scanlines to
|
||
|
// selectively reveal a bg that has been
|
||
|
// filled with the graph color.
|
||
|
//---------------------------------------
|
||
|
|
||
|
#define SHIFT_RIGHT_ADJUSTED(n, s)(((n) >> (s)) + (((n) >> ((s) - 1)) & 1))
|
||
|
|
||
|
void ConditionGraph_Init(struct ConditionGraph *graph)
|
||
|
{
|
||
|
u8 i, j;
|
||
|
|
||
|
for (j = 0; j < CONDITION_COUNT; j++)
|
||
|
{
|
||
|
for (i = 0; i < CONDITION_GRAPH_UPDATE_STEPS; i++)
|
||
|
{
|
||
|
graph->newPositions[i][j].x = 0;
|
||
|
graph->newPositions[i][j].y = 0;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < CONDITION_GRAPH_LOAD_MAX; i++)
|
||
|
{
|
||
|
graph->conditions[i][j] = 0;
|
||
|
graph->savedPositions[i][j].x = CONDITION_GRAPH_CENTER_X;
|
||
|
graph->savedPositions[i][j].y = CONDITION_GRAPH_CENTER_Y;
|
||
|
}
|
||
|
|
||
|
graph->curPositions[j].x = 0;
|
||
|
graph->curPositions[j].y = 0;
|
||
|
}
|
||
|
|
||
|
graph->needsDraw = FALSE;
|
||
|
graph->updateCounter = 0;
|
||
|
}
|
||
|
|
||
|
// Fills the newPositions array with incremental positions between
|
||
|
// old and new for the graph transition when switching between Pokémon.
|
||
|
void ConditionGraph_SetNewPositions(struct ConditionGraph *graph, struct UCoords16 *old, struct UCoords16 *new)
|
||
|
{
|
||
|
u16 i, j;
|
||
|
s32 coord, increment;
|
||
|
|
||
|
for (i = 0; i < CONDITION_COUNT; i++)
|
||
|
{
|
||
|
coord = old[i].x << 8;
|
||
|
increment = ((new[i].x - old[i].x) << 8) / CONDITION_GRAPH_UPDATE_STEPS;
|
||
|
for (j = 0; j < CONDITION_GRAPH_UPDATE_STEPS - 1; j++)
|
||
|
{
|
||
|
graph->newPositions[j][i].x = SHIFT_RIGHT_ADJUSTED(coord, 8);
|
||
|
coord += increment;
|
||
|
}
|
||
|
graph->newPositions[j][i].x = new[i].x;
|
||
|
|
||
|
coord = old[i].y << 8;
|
||
|
increment = ((new[i].y - old[i].y) << 8) / CONDITION_GRAPH_UPDATE_STEPS;
|
||
|
for (j = 0; j < CONDITION_GRAPH_UPDATE_STEPS - 1; j++)
|
||
|
{
|
||
|
graph->newPositions[j][i].y = SHIFT_RIGHT_ADJUSTED(coord, 8);
|
||
|
coord += increment;
|
||
|
}
|
||
|
graph->newPositions[j][i].y = new[i].y;
|
||
|
}
|
||
|
|
||
|
graph->updateCounter = 0;
|
||
|
}
|
||
|
|
||
|
bool8 ConditionGraph_TryUpdate(struct ConditionGraph *graph)
|
||
|
{
|
||
|
if (graph->updateCounter < CONDITION_GRAPH_UPDATE_STEPS)
|
||
|
{
|
||
|
ConditionGraph_Update(graph);
|
||
|
return (++graph->updateCounter != CONDITION_GRAPH_UPDATE_STEPS);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ConditionGraph_InitResetScanline(struct ConditionGraph *graph)
|
||
|
{
|
||
|
graph->scanlineResetState = 0;
|
||
|
}
|
||
|
|
||
|
bool8 ConditionGraph_ResetScanline(struct ConditionGraph *graph)
|
||
|
{
|
||
|
struct ScanlineEffectParams params;
|
||
|
|
||
|
switch (graph->scanlineResetState)
|
||
|
{
|
||
|
case 0:
|
||
|
ScanlineEffect_Clear();
|
||
|
graph->scanlineResetState++;
|
||
|
return TRUE;
|
||
|
case 1:
|
||
|
params = sConditionGraphScanline;
|
||
|
ScanlineEffect_SetParams(params);
|
||
|
graph->scanlineResetState++;
|
||
|
return FALSE;
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ConditionGraph_Draw(struct ConditionGraph *graph)
|
||
|
{
|
||
|
u16 i;
|
||
|
|
||
|
if (!graph->needsDraw)
|
||
|
return;
|
||
|
|
||
|
ConditionGraph_CalcRightHalf(graph);
|
||
|
ConditionGraph_CalcLeftHalf(graph);
|
||
|
|
||
|
for (i = 0; i < CONDITION_GRAPH_HEIGHT; i++)
|
||
|
{
|
||
|
// Draw right half
|
||
|
gScanlineEffectRegBuffers[1][(i + CONDITION_GRAPH_TOP_Y - 1) * 2 + 0] = // double assignment
|
||
|
gScanlineEffectRegBuffers[0][(i + CONDITION_GRAPH_TOP_Y - 1) * 2 + 0] = (graph->scanlineRight[i][0] << 8) | (graph->scanlineRight[i][1]);
|
||
|
// Draw left half
|
||
|
gScanlineEffectRegBuffers[1][(i + CONDITION_GRAPH_TOP_Y - 1) * 2 + 1] = // double assignment
|
||
|
gScanlineEffectRegBuffers[0][(i + CONDITION_GRAPH_TOP_Y - 1) * 2 + 1] = (graph->scanlineLeft[i][0] << 8) | (graph->scanlineLeft[i][1]);
|
||
|
}
|
||
|
|
||
|
graph->needsDraw = FALSE;
|
||
|
}
|
||
|
|
||
|
void ConditionGraph_InitWindow(u8 bg)
|
||
|
{
|
||
|
u32 flags;
|
||
|
|
||
|
if (bg >= NUM_BACKGROUNDS)
|
||
|
bg = 0;
|
||
|
|
||
|
// Unset the WINOUT flag for the bg.
|
||
|
flags = (WINOUT_WIN01_BG_ALL | WINOUT_WIN01_OBJ) & ~(1 << bg);
|
||
|
|
||
|
// Set limits for graph data
|
||
|
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE( 0, DISPLAY_WIDTH)); // Right side horizontal
|
||
|
SetGpuReg(REG_OFFSET_WIN1H, WIN_RANGE( 0, CONDITION_GRAPH_CENTER_X)); // Left side horizontal
|
||
|
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(CONDITION_GRAPH_TOP_Y, CONDITION_GRAPH_BOTTOM_Y)); // Right side vertical
|
||
|
SetGpuReg(REG_OFFSET_WIN1V, WIN_RANGE(CONDITION_GRAPH_TOP_Y, CONDITION_GRAPH_BOTTOM_Y)); // Left side vertical
|
||
|
SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR | WININ_WIN1_BG_ALL | WININ_WIN1_OBJ | WININ_WIN1_CLR);
|
||
|
SetGpuReg(REG_OFFSET_WINOUT, flags);
|
||
|
}
|
||
|
|
||
|
void ConditionGraph_Update(struct ConditionGraph *graph)
|
||
|
{
|
||
|
u16 i;
|
||
|
for (i = 0; i < CONDITION_COUNT; i++)
|
||
|
graph->curPositions[i] = graph->newPositions[graph->updateCounter][i];
|
||
|
|
||
|
graph->needsDraw = TRUE;
|
||
|
}
|
||
|
|
||
|
static void ConditionGraph_CalcLine(struct ConditionGraph *graph, u16 *scanline, struct UCoords16 *pos1, struct UCoords16 *pos2, bool8 dir, u16 *overflowScanline)
|
||
|
{
|
||
|
u16 i, height, top, bottom, x2;
|
||
|
u16 *ptr;
|
||
|
s32 x, xIncrement = 0;
|
||
|
|
||
|
if (pos1->y < pos2->y)
|
||
|
{
|
||
|
top = pos1->y;
|
||
|
bottom = pos2->y;
|
||
|
x = pos1->x << 10;
|
||
|
x2 = pos2->x;
|
||
|
height = bottom - top;
|
||
|
if (height != 0)
|
||
|
xIncrement = ((x2 - pos1->x) << 10) / height;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bottom = pos1->y;
|
||
|
top = pos2->y;
|
||
|
x = pos2->x << 10;
|
||
|
x2 = pos1->x;
|
||
|
height = bottom - top;
|
||
|
if (height != 0)
|
||
|
xIncrement = ((x2 - pos2->x) << 10) / height;
|
||
|
}
|
||
|
|
||
|
height++;
|
||
|
if (overflowScanline == NULL)
|
||
|
{
|
||
|
scanline += (top - CONDITION_GRAPH_TOP_Y) * 2;
|
||
|
for (i = 0; i < height; i++)
|
||
|
{
|
||
|
scanline[dir] = SHIFT_RIGHT_ADJUSTED(x, 10) + dir;
|
||
|
x += xIncrement;
|
||
|
scanline += 2;
|
||
|
}
|
||
|
|
||
|
ptr = scanline - 2;
|
||
|
}
|
||
|
else if (xIncrement > 0)
|
||
|
{
|
||
|
overflowScanline += (top - CONDITION_GRAPH_TOP_Y) * 2;
|
||
|
// Less readable than the other loops, but it has to be written this way to match.
|
||
|
for (i = 0; i < height; overflowScanline[dir] = SHIFT_RIGHT_ADJUSTED(x, 10) + dir, x += xIncrement, overflowScanline += 2, i++)
|
||
|
{
|
||
|
if (x >= (CONDITION_GRAPH_CENTER_X << 10))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
graph->bottom = top + i;
|
||
|
scanline += (graph->bottom - CONDITION_GRAPH_TOP_Y) * 2;
|
||
|
for (; i < height; i++)
|
||
|
{
|
||
|
scanline[dir] = SHIFT_RIGHT_ADJUSTED(x, 10) + dir;
|
||
|
x += xIncrement;
|
||
|
scanline += 2;
|
||
|
}
|
||
|
|
||
|
ptr = scanline - 2;
|
||
|
}
|
||
|
else if (xIncrement < 0)
|
||
|
{
|
||
|
scanline += (top - CONDITION_GRAPH_TOP_Y) * 2;
|
||
|
for (i = 0; i < height; i++)
|
||
|
{
|
||
|
scanline[dir] = SHIFT_RIGHT_ADJUSTED(x, 10) + dir;
|
||
|
if (x < (CONDITION_GRAPH_CENTER_X << 10))
|
||
|
{
|
||
|
scanline[dir] = CONDITION_GRAPH_CENTER_X;
|
||
|
break;
|
||
|
}
|
||
|
x += xIncrement;
|
||
|
scanline += 2;
|
||
|
}
|
||
|
|
||
|
graph->bottom = top + i;
|
||
|
overflowScanline += (graph->bottom - CONDITION_GRAPH_TOP_Y) * 2;
|
||
|
for (; i < height; i++)
|
||
|
{
|
||
|
overflowScanline[dir] = SHIFT_RIGHT_ADJUSTED(x, 10) + dir;
|
||
|
x += xIncrement;
|
||
|
overflowScanline += 2;
|
||
|
}
|
||
|
|
||
|
ptr = overflowScanline - 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
graph->bottom = top;
|
||
|
scanline += (top - CONDITION_GRAPH_TOP_Y) * 2;
|
||
|
overflowScanline += (top - CONDITION_GRAPH_TOP_Y) * 2;
|
||
|
scanline[1] = pos1->x + 1;
|
||
|
overflowScanline[0] = pos2->x;
|
||
|
overflowScanline[1] = CONDITION_GRAPH_CENTER_X;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ptr[dir] = dir + x2;
|
||
|
}
|
||
|
|
||
|
static void ConditionGraph_CalcRightHalf(struct ConditionGraph *graph)
|
||
|
{
|
||
|
u16 i, y, bottom;
|
||
|
|
||
|
// Calculate Cool -> Beauty line
|
||
|
if (graph->curPositions[GRAPH_COOL].y < graph->curPositions[GRAPH_BEAUTY].y)
|
||
|
{
|
||
|
y = graph->curPositions[GRAPH_COOL].y;
|
||
|
ConditionGraph_CalcLine(graph, graph->scanlineRight[0], &graph->curPositions[GRAPH_COOL], &graph->curPositions[GRAPH_BEAUTY], TRUE, NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
y = graph->curPositions[GRAPH_BEAUTY].y;
|
||
|
ConditionGraph_CalcLine(graph, graph->scanlineRight[0], &graph->curPositions[GRAPH_BEAUTY], &graph->curPositions[GRAPH_COOL], FALSE, NULL);
|
||
|
}
|
||
|
|
||
|
// Calculate Beauty -> Cute line
|
||
|
// No need for conditional, positions on the Beauty line are always above the Cute line
|
||
|
ConditionGraph_CalcLine(graph, graph->scanlineRight[0], &graph->curPositions[GRAPH_BEAUTY], &graph->curPositions[GRAPH_CUTE], TRUE, NULL);
|
||
|
|
||
|
// Calculate Cute -> Tough line (includes left scanline because this crosses the halfway point)
|
||
|
i = (graph->curPositions[GRAPH_CUTE].y <= graph->curPositions[GRAPH_SMART].y);
|
||
|
ConditionGraph_CalcLine(graph, graph->scanlineRight[0], &graph->curPositions[GRAPH_CUTE], &graph->curPositions[GRAPH_SMART], i, graph->scanlineLeft[0]);
|
||
|
|
||
|
// Clear down to new top
|
||
|
for (i = CONDITION_GRAPH_TOP_Y; i < y; i++)
|
||
|
{
|
||
|
graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][0] = 0;
|
||
|
graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][1] = 0;
|
||
|
}
|
||
|
|
||
|
for (i = graph->curPositions[GRAPH_COOL].y; i <= graph->bottom; i++)
|
||
|
graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][0] = CONDITION_GRAPH_CENTER_X;
|
||
|
|
||
|
// Clear after new bottom
|
||
|
bottom = max(graph->bottom, graph->curPositions[GRAPH_CUTE].y);
|
||
|
for (i = bottom + 1; i <= CONDITION_GRAPH_BOTTOM_Y; i++)
|
||
|
{
|
||
|
graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][0] = 0;
|
||
|
graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][1] = 0;
|
||
|
}
|
||
|
|
||
|
for (i = CONDITION_GRAPH_TOP_Y; i <= CONDITION_GRAPH_BOTTOM_Y; i++)
|
||
|
{
|
||
|
if (graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][0] == 0
|
||
|
&& graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][1] != 0)
|
||
|
graph->scanlineRight[i - CONDITION_GRAPH_TOP_Y][0] = CONDITION_GRAPH_CENTER_X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ConditionGraph_CalcLeftHalf(struct ConditionGraph *graph)
|
||
|
{
|
||
|
s32 i, y, bottom;
|
||
|
|
||
|
// Calculate Cool -> Tough line
|
||
|
if (graph->curPositions[GRAPH_COOL].y < graph->curPositions[GRAPH_TOUGH].y)
|
||
|
{
|
||
|
y = graph->curPositions[GRAPH_COOL].y;
|
||
|
ConditionGraph_CalcLine(graph, graph->scanlineLeft[0], &graph->curPositions[GRAPH_COOL], &graph->curPositions[GRAPH_TOUGH], FALSE, NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
y = graph->curPositions[GRAPH_TOUGH].y;
|
||
|
ConditionGraph_CalcLine(graph, graph->scanlineLeft[0], &graph->curPositions[GRAPH_TOUGH], &graph->curPositions[GRAPH_COOL], TRUE, NULL);
|
||
|
}
|
||
|
|
||
|
// Calculate Tough -> Smart line
|
||
|
// No need for conditional, positions on the Tough line are always above the Smart line
|
||
|
ConditionGraph_CalcLine(graph, graph->scanlineLeft[0], &graph->curPositions[GRAPH_TOUGH], &graph->curPositions[GRAPH_SMART], FALSE, NULL);
|
||
|
|
||
|
// Clear down to new top
|
||
|
for (i = CONDITION_GRAPH_TOP_Y; i < y; i++)
|
||
|
{
|
||
|
graph->scanlineLeft[i - CONDITION_GRAPH_TOP_Y][0] = 0;
|
||
|
graph->scanlineLeft[i - CONDITION_GRAPH_TOP_Y][1] = 0;
|
||
|
}
|
||
|
|
||
|
for (i = graph->curPositions[GRAPH_COOL].y; i <= graph->bottom; i++)
|
||
|
graph->scanlineLeft[i - CONDITION_GRAPH_TOP_Y][1] = CONDITION_GRAPH_CENTER_X;
|
||
|
|
||
|
// Clear after new bottom
|
||
|
bottom = max(graph->bottom, graph->curPositions[GRAPH_SMART].y + 1);
|
||
|
for (i = bottom; i <= CONDITION_GRAPH_BOTTOM_Y; i++)
|
||
|
{
|
||
|
graph->scanlineLeft[i - CONDITION_GRAPH_TOP_Y][0] = 0;
|
||
|
graph->scanlineLeft[i - CONDITION_GRAPH_TOP_Y][1] = 0;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < CONDITION_GRAPH_HEIGHT; i++)
|
||
|
{
|
||
|
if (graph->scanlineLeft[i][0] >= graph->scanlineLeft[i][1])
|
||
|
{
|
||
|
graph->scanlineLeft[i][1] = 0;
|
||
|
graph->scanlineLeft[i][0] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ConditionGraph_CalcPositions(u8 *conditions, struct UCoords16 *positions)
|
||
|
{
|
||
|
u8 lineLength, sinIdx;
|
||
|
s8 posIdx;
|
||
|
u16 i;
|
||
|
|
||
|
// Cool is straight up-and-down (not angled), so no need for Sin
|
||
|
lineLength = sConditionToLineLength[*(conditions++)];
|
||
|
positions[GRAPH_COOL].x = CONDITION_GRAPH_CENTER_X;
|
||
|
positions[GRAPH_COOL].y = CONDITION_GRAPH_CENTER_Y - lineLength;
|
||
|
|
||
|
sinIdx = 64;
|
||
|
posIdx = GRAPH_COOL;
|
||
|
for (i = 1; i < CONDITION_COUNT; i++)
|
||
|
{
|
||
|
sinIdx += 51;
|
||
|
if (--posIdx < 0)
|
||
|
posIdx = CONDITION_COUNT - 1;
|
||
|
|
||
|
if (posIdx == GRAPH_CUTE)
|
||
|
sinIdx++;
|
||
|
|
||
|
lineLength = sConditionToLineLength[*(conditions++)];
|
||
|
positions[posIdx].x = CONDITION_GRAPH_CENTER_X + ((lineLength * gSineTable[64 + sinIdx]) >> 8);
|
||
|
positions[posIdx].y = CONDITION_GRAPH_CENTER_Y - ((lineLength * gSineTable[sinIdx]) >> 8);
|
||
|
|
||
|
if (posIdx <= GRAPH_CUTE && (lineLength != 32 || posIdx != GRAPH_CUTE))
|
||
|
positions[posIdx].x++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------
|
||
|
// Move relearner
|
||
|
//----------------
|
||
|
|
||
|
void InitMoveRelearnerWindows(bool8 useContextWindow)
|
||
|
{
|
||
|
u8 i;
|
||
|
|
||
|
InitWindows(sMoveRelearnerWindowTemplates);
|
||
|
DeactivateAllTextPrinters();
|
||
|
LoadUserWindowBorderGfx(0, 1, 0xE0);
|
||
|
LoadPalette(gStandardMenuPalette, 0xF0, 0x20);
|
||
|
|
||
|
for (i = 0; i < ARRAY_COUNT(sMoveRelearnerWindowTemplates) - 1; i++)
|
||
|
FillWindowPixelBuffer(i, PIXEL_FILL(1));
|
||
|
|
||
|
if (!useContextWindow)
|
||
|
{
|
||
|
PutWindowTilemap(0);
|
||
|
DrawStdFrameWithCustomTileAndPalette(0, 0, 0x1, 0xE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PutWindowTilemap(1);
|
||
|
DrawStdFrameWithCustomTileAndPalette(1, 0, 1, 0xE);
|
||
|
}
|
||
|
PutWindowTilemap(2);
|
||
|
PutWindowTilemap(3);
|
||
|
DrawStdFrameWithCustomTileAndPalette(2, 0, 1, 0xE);
|
||
|
DrawStdFrameWithCustomTileAndPalette(3, 0, 1, 0xE);
|
||
|
MoveRelearnerDummy();
|
||
|
ScheduleBgCopyTilemapToVram(1);
|
||
|
}
|
||
|
|
||
|
static void MoveRelearnerDummy(void)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
u8 LoadMoveRelearnerMovesList(const struct ListMenuItem *items, u16 numChoices)
|
||
|
{
|
||
|
gMultiuseListMenuTemplate = sMoveRelearnerMovesListTemplate;
|
||
|
gMultiuseListMenuTemplate.totalItems = numChoices;
|
||
|
gMultiuseListMenuTemplate.items = items;
|
||
|
|
||
|
if (numChoices < 6)
|
||
|
gMultiuseListMenuTemplate.maxShowed = numChoices;
|
||
|
else
|
||
|
gMultiuseListMenuTemplate.maxShowed = 6;
|
||
|
|
||
|
return gMultiuseListMenuTemplate.maxShowed;
|
||
|
}
|
||
|
|
||
|
static void MoveRelearnerLoadBattleMoveDescription(u32 chosenMove)
|
||
|
{
|
||
|
s32 x;
|
||
|
const struct BattleMove *move;
|
||
|
u8 buffer[32];
|
||
|
const u8 *str;
|
||
|
|
||
|
FillWindowPixelBuffer(0, PIXEL_FILL(1));
|
||
|
str = gText_MoveRelearnerBattleMoves;
|
||
|
x = GetStringCenterAlignXOffset(FONT_NORMAL, str, 0x80);
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, str, x, 1, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
str = gText_MoveRelearnerPP;
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, str, 4, 0x29, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
str = gText_MoveRelearnerPower;
|
||
|
x = GetStringRightAlignXOffset(FONT_NORMAL, str, 0x6A);
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, str, x, 0x19, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
str = gText_MoveRelearnerAccuracy;
|
||
|
x = GetStringRightAlignXOffset(FONT_NORMAL, str, 0x6A);
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, str, x, 0x29, TEXT_SKIP_DRAW, NULL);
|
||
|
if (chosenMove == LIST_CANCEL)
|
||
|
{
|
||
|
CopyWindowToVram(0, COPYWIN_GFX);
|
||
|
return;
|
||
|
}
|
||
|
move = &gBattleMoves[chosenMove];
|
||
|
str = gTypeNames[move->type];
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, str, 4, 0x19, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
x = 4 + GetStringWidth(FONT_NORMAL, gText_MoveRelearnerPP, 0);
|
||
|
ConvertIntToDecimalStringN(buffer, move->pp, STR_CONV_MODE_LEFT_ALIGN, 2);
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, buffer, x, 0x29, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
if (move->power < 2)
|
||
|
{
|
||
|
str = gText_ThreeDashes;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ConvertIntToDecimalStringN(buffer, move->power, STR_CONV_MODE_LEFT_ALIGN, 3);
|
||
|
str = buffer;
|
||
|
}
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, str, 0x6A, 0x19, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
if (move->accuracy == 0)
|
||
|
{
|
||
|
str = gText_ThreeDashes;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ConvertIntToDecimalStringN(buffer, move->accuracy, STR_CONV_MODE_LEFT_ALIGN, 3);
|
||
|
str = buffer;
|
||
|
}
|
||
|
AddTextPrinterParameterized(0, FONT_NORMAL, str, 0x6A, 0x29, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
str = gMoveDescriptionPointers[chosenMove - 1];
|
||
|
AddTextPrinterParameterized(0, FONT_NARROW, str, 0, 0x41, 0, NULL);
|
||
|
}
|
||
|
|
||
|
static void MoveRelearnerMenuLoadContestMoveDescription(u32 chosenMove)
|
||
|
{
|
||
|
s32 x;
|
||
|
const u8 *str;
|
||
|
const struct ContestMove *move;
|
||
|
|
||
|
MoveRelearnerShowHideHearts(chosenMove);
|
||
|
FillWindowPixelBuffer(1, PIXEL_FILL(1));
|
||
|
str = gText_MoveRelearnerContestMovesTitle;
|
||
|
x = GetStringCenterAlignXOffset(FONT_NORMAL, str, 0x80);
|
||
|
AddTextPrinterParameterized(1, FONT_NORMAL, str, x, 1, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
str = gText_MoveRelearnerAppeal;
|
||
|
x = GetStringRightAlignXOffset(FONT_NORMAL, str, 0x5C);
|
||
|
AddTextPrinterParameterized(1, FONT_NORMAL, str, x, 0x19, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
str = gText_MoveRelearnerJam;
|
||
|
x = GetStringRightAlignXOffset(FONT_NORMAL, str, 0x5C);
|
||
|
AddTextPrinterParameterized(1, FONT_NORMAL, str, x, 0x29, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
if (chosenMove == MENU_NOTHING_CHOSEN)
|
||
|
{
|
||
|
CopyWindowToVram(1, COPYWIN_GFX);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
move = &gContestMoves[chosenMove];
|
||
|
str = gContestMoveTypeTextPointers[move->contestCategory];
|
||
|
AddTextPrinterParameterized(1, FONT_NORMAL, str, 4, 0x19, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
str = gContestEffectDescriptionPointers[move->effect];
|
||
|
AddTextPrinterParameterized(1, FONT_NARROW, str, 0, 0x41, TEXT_SKIP_DRAW, NULL);
|
||
|
|
||
|
CopyWindowToVram(1, COPYWIN_GFX);
|
||
|
}
|
||
|
|
||
|
static void MoveRelearnerCursorCallback(s32 itemIndex, bool8 onInit, struct ListMenu *list)
|
||
|
{
|
||
|
if (onInit != TRUE)
|
||
|
PlaySE(SE_SELECT);
|
||
|
MoveRelearnerLoadBattleMoveDescription(itemIndex);
|
||
|
MoveRelearnerMenuLoadContestMoveDescription(itemIndex);
|
||
|
}
|
||
|
|
||
|
void MoveRelearnerPrintText(u8 *str)
|
||
|
{
|
||
|
u8 speed;
|
||
|
|
||
|
FillWindowPixelBuffer(3, PIXEL_FILL(1));
|
||
|
gTextFlags.canABSpeedUpPrint = TRUE;
|
||
|
speed = GetPlayerTextSpeedDelay();
|
||
|
AddTextPrinterParameterized2(3, FONT_NORMAL, str, speed, NULL, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, 3);
|
||
|
}
|
||
|
|
||
|
bool16 MoveRelearnerRunTextPrinters(void)
|
||
|
{
|
||
|
RunTextPrinters();
|
||
|
return IsTextPrinterActive(3);
|
||
|
}
|
||
|
|
||
|
void MoveRelearnerCreateYesNoMenu(void)
|
||
|
{
|
||
|
CreateYesNoMenu(&sMoveRelearnerYesNoMenuTemplate, 1, 0xE, 0);
|
||
|
}
|
||
|
|
||
|
//----------------
|
||
|
// Condition menu
|
||
|
//----------------
|
||
|
|
||
|
s32 GetBoxOrPartyMonData(u16 boxId, u16 monId, s32 request, u8 *dst)
|
||
|
{
|
||
|
s32 ret;
|
||
|
|
||
|
if (boxId == TOTAL_BOXES_COUNT) // Party mon.
|
||
|
{
|
||
|
if (request == MON_DATA_NICKNAME || request == MON_DATA_OT_NAME)
|
||
|
ret = GetMonData(&gPlayerParty[monId], request, dst);
|
||
|
else
|
||
|
ret = GetMonData(&gPlayerParty[monId], request);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (request == MON_DATA_NICKNAME || request == MON_DATA_OT_NAME)
|
||
|
ret = GetAndCopyBoxMonDataAt(boxId, monId, request, dst);
|
||
|
else
|
||
|
ret = GetBoxMonDataAt(boxId, monId, request);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// Gets the name/gender/level string for the condition menu
|
||
|
static u8 *GetConditionMenuMonString(u8 *dst, u16 boxId, u16 monId)
|
||
|
{
|
||
|
u16 box, mon, species, level, gender;
|
||
|
struct BoxPokemon *boxMon;
|
||
|
u8 *str;
|
||
|
|
||
|
box = boxId;
|
||
|
mon = monId;
|
||
|
*(dst++) = EXT_CTRL_CODE_BEGIN;
|
||
|
*(dst++) = EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW;
|
||
|
*(dst++) = TEXT_COLOR_BLUE;
|
||
|
*(dst++) = TEXT_COLOR_TRANSPARENT;
|
||
|
*(dst++) = TEXT_COLOR_LIGHT_BLUE;
|
||
|
if (GetBoxOrPartyMonData(box, mon, MON_DATA_IS_EGG, NULL))
|
||
|
return StringCopyPadded(dst, gText_EggNickname, 0, 12);
|
||
|
GetBoxOrPartyMonData(box, mon, MON_DATA_NICKNAME, dst);
|
||
|
StringGet_Nickname(dst);
|
||
|
species = GetBoxOrPartyMonData(box, mon, MON_DATA_SPECIES, NULL);
|
||
|
if (box == TOTAL_BOXES_COUNT) // Party mon.
|
||
|
{
|
||
|
level = GetMonData(&gPlayerParty[mon], MON_DATA_LEVEL);
|
||
|
gender = GetMonGender(&gPlayerParty[mon]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
boxMon = GetBoxedMonPtr(box, mon);
|
||
|
gender = GetBoxMonGender(boxMon);
|
||
|
level = GetLevelFromBoxMonExp(boxMon);
|
||
|
}
|
||
|
|
||
|
if ((species == SPECIES_NIDORAN_F || species == SPECIES_NIDORAN_M) && !StringCompare(dst, gSpeciesNames[species]))
|
||
|
gender = MON_GENDERLESS;
|
||
|
|
||
|
for (str = dst; *str != EOS; str++)
|
||
|
;
|
||
|
|
||
|
*(str++) = EXT_CTRL_CODE_BEGIN;
|
||
|
*(str++) = EXT_CTRL_CODE_SKIP;
|
||
|
*(str++) = 60;
|
||
|
|
||
|
switch (gender)
|
||
|
{
|
||
|
default:
|
||
|
*(str++) = CHAR_SPACE;
|
||
|
break;
|
||
|
case MON_MALE:
|
||
|
*(str++) = EXT_CTRL_CODE_BEGIN;
|
||
|
*(str++) = EXT_CTRL_CODE_COLOR;
|
||
|
*(str++) = TEXT_COLOR_RED;
|
||
|
*(str++) = EXT_CTRL_CODE_BEGIN;
|
||
|
*(str++) = EXT_CTRL_CODE_SHADOW;
|
||
|
*(str++) = TEXT_COLOR_LIGHT_RED;
|
||
|
*(str++) = CHAR_MALE;
|
||
|
break;
|
||
|
case MON_FEMALE:
|
||
|
*(str++) = EXT_CTRL_CODE_BEGIN;
|
||
|
*(str++) = EXT_CTRL_CODE_COLOR;
|
||
|
*(str++) = TEXT_COLOR_GREEN;
|
||
|
*(str++) = EXT_CTRL_CODE_BEGIN;
|
||
|
*(str++) = EXT_CTRL_CODE_SHADOW;
|
||
|
*(str++) = TEXT_COLOR_LIGHT_GREEN;
|
||
|
*(str++) = CHAR_FEMALE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*(str++) = EXT_CTRL_CODE_BEGIN;
|
||
|
*(str++) = EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW;
|
||
|
*(str++) = TEXT_COLOR_BLUE;
|
||
|
*(str++) = TEXT_COLOR_TRANSPARENT;
|
||
|
*(str++) = TEXT_COLOR_LIGHT_BLUE;
|
||
|
*(str++) = CHAR_SLASH;
|
||
|
*(str++) = CHAR_EXTRA_SYMBOL;
|
||
|
*(str++) = CHAR_LV_2;
|
||
|
str = ConvertIntToDecimalStringN(str, level, STR_CONV_MODE_LEFT_ALIGN, 3);
|
||
|
*(str++) = CHAR_SPACE;
|
||
|
*str = EOS;
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
// Buffers the string in src to dest up to n chars. If src is less than n chars, fill with spaces
|
||
|
static u8 *BufferConditionMenuSpacedStringN(u8 *dst, const u8 *src, s16 n)
|
||
|
{
|
||
|
while (*src != EOS)
|
||
|
{
|
||
|
*(dst++) = *(src++);
|
||
|
n--;
|
||
|
}
|
||
|
while (n-- > 0)
|
||
|
*(dst++) = CHAR_SPACE;
|
||
|
|
||
|
*dst = EOS;
|
||
|
return dst;
|
||
|
}
|
||
|
|
||
|
void GetConditionMenuMonNameAndLocString(u8 *locationDst, u8 *nameDst, u16 boxId, u16 monId, u16 partyId, u16 numMons, bool8 excludesCancel)
|
||
|
{
|
||
|
u16 i;
|
||
|
u16 box = boxId;
|
||
|
u16 mon = monId;
|
||
|
|
||
|
// In this and the below 2 functions, numMons is passed as the number of menu selections (which includes Cancel)
|
||
|
// To indicate that the Cancel needs to be subtracted they pass an additional bool
|
||
|
// Unclear why they didn't just subtract 1 when it gets passed instead
|
||
|
if (!excludesCancel)
|
||
|
numMons--;
|
||
|
|
||
|
if (partyId != numMons)
|
||
|
{
|
||
|
GetConditionMenuMonString(nameDst, box, mon);
|
||
|
locationDst[0] = EXT_CTRL_CODE_BEGIN;
|
||
|
locationDst[1] = EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW;
|
||
|
locationDst[2] = TEXT_COLOR_BLUE;
|
||
|
locationDst[3] = TEXT_COLOR_TRANSPARENT;
|
||
|
locationDst[4] = TEXT_COLOR_LIGHT_BLUE;
|
||
|
if (box == TOTAL_BOXES_COUNT) // Party mon.
|
||
|
BufferConditionMenuSpacedStringN(&locationDst[5], gText_InParty, 8);
|
||
|
else
|
||
|
BufferConditionMenuSpacedStringN(&locationDst[5], GetBoxNamePtr(box), BOX_NAME_LENGTH);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (i = 0; i < POKEMON_NAME_LENGTH + 2; i++)
|
||
|
nameDst[i] = CHAR_SPACE;
|
||
|
nameDst[i] = EOS;
|
||
|
for (i = 0; i < 8; i++)
|
||
|
locationDst[i] = CHAR_SPACE;
|
||
|
locationDst[i] = EOS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GetConditionMenuMonConditions(struct ConditionGraph *graph, u8 *numSparkles, u16 boxId, u16 monId, u16 partyId, u16 id, u16 numMons, bool8 excludesCancel)
|
||
|
{
|
||
|
u16 i;
|
||
|
|
||
|
if (!excludesCancel)
|
||
|
numMons--;
|
||
|
|
||
|
if (partyId != numMons)
|
||
|
{
|
||
|
graph->conditions[id][CONDITION_COOL] = GetBoxOrPartyMonData(boxId, monId, MON_DATA_COOL, NULL);
|
||
|
graph->conditions[id][CONDITION_TOUGH] = GetBoxOrPartyMonData(boxId, monId, MON_DATA_TOUGH, NULL);
|
||
|
graph->conditions[id][CONDITION_SMART] = GetBoxOrPartyMonData(boxId, monId, MON_DATA_SMART, NULL);
|
||
|
graph->conditions[id][CONDITION_CUTE] = GetBoxOrPartyMonData(boxId, monId, MON_DATA_CUTE, NULL);
|
||
|
graph->conditions[id][CONDITION_BEAUTY] = GetBoxOrPartyMonData(boxId, monId, MON_DATA_BEAUTY, NULL);
|
||
|
|
||
|
numSparkles[id] = GET_NUM_CONDITION_SPARKLES(GetBoxOrPartyMonData(boxId, monId, MON_DATA_SHEEN, NULL));
|
||
|
|
||
|
ConditionGraph_CalcPositions(graph->conditions[id], graph->savedPositions[id]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (i = 0; i < CONDITION_COUNT; i++)
|
||
|
{
|
||
|
graph->conditions[id][i] = 0;
|
||
|
graph->savedPositions[id][i].x = CONDITION_GRAPH_CENTER_X;
|
||
|
graph->savedPositions[id][i].y = CONDITION_GRAPH_CENTER_Y;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GetConditionMenuMonGfx(void *tilesDst, void *palDst, u16 boxId, u16 monId, u16 partyId, u16 numMons, bool8 excludesCancel)
|
||
|
{
|
||
|
if (!excludesCancel)
|
||
|
numMons--;
|
||
|
|
||
|
if (partyId != numMons)
|
||
|
{
|
||
|
u16 species = GetBoxOrPartyMonData(boxId, monId, MON_DATA_SPECIES2, NULL);
|
||
|
u32 trainerId = GetBoxOrPartyMonData(boxId, monId, MON_DATA_OT_ID, NULL);
|
||
|
u32 personality = GetBoxOrPartyMonData(boxId, monId, MON_DATA_PERSONALITY, NULL);
|
||
|
|
||
|
LoadSpecialPokePic(&gMonFrontPicTable[species], tilesDst, species, personality, TRUE);
|
||
|
LZ77UnCompWram(GetMonSpritePalFromSpeciesAndPersonality(species, trainerId, personality), palDst);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool8 MoveConditionMonOnscreen(s16 *x)
|
||
|
{
|
||
|
*x += 24;
|
||
|
if (*x > 0)
|
||
|
*x = 0;
|
||
|
|
||
|
return (*x != 0);
|
||
|
}
|
||
|
|
||
|
bool8 MoveConditionMonOffscreen(s16 *x)
|
||
|
{
|
||
|
*x -= 24;
|
||
|
if (*x < -80)
|
||
|
*x = -80;
|
||
|
|
||
|
return (*x != -80);
|
||
|
}
|
||
|
|
||
|
bool8 ConditionMenu_UpdateMonEnter(struct ConditionGraph *graph, s16 *x)
|
||
|
{
|
||
|
bool8 graphUpdating = ConditionGraph_TryUpdate(graph);
|
||
|
bool8 monUpdating = MoveConditionMonOnscreen(x);
|
||
|
|
||
|
return (graphUpdating || monUpdating);
|
||
|
}
|
||
|
|
||
|
bool8 ConditionMenu_UpdateMonExit(struct ConditionGraph *graph, s16 *x)
|
||
|
{
|
||
|
bool8 graphUpdating = ConditionGraph_TryUpdate(graph);
|
||
|
bool8 monUpdating = MoveConditionMonOffscreen(x);
|
||
|
|
||
|
return (graphUpdating || monUpdating);
|
||
|
}
|
||
|
|
||
|
static const u32 sConditionPokeball_Gfx[] = INCBIN_U32("graphics/pokenav/condition/pokeball.4bpp");
|
||
|
static const u32 sConditionPokeballPlaceholder_Gfx[] = INCBIN_U32("graphics/pokenav/condition/pokeball_placeholder.4bpp");
|
||
|
static const u16 sConditionSparkle_Gfx[] = INCBIN_U16("graphics/pokenav/condition/sparkle.gbapal");
|
||
|
static const u32 sConditionSparkle_Pal[] = INCBIN_U32("graphics/pokenav/condition/sparkle.4bpp");
|
||
|
|
||
|
static const struct OamData sOam_ConditionMonPic =
|
||
|
{
|
||
|
.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 = 1,
|
||
|
.paletteNum = 0,
|
||
|
.affineParam = 0
|
||
|
};
|
||
|
|
||
|
static const struct OamData sOam_ConditionSelectionIcon =
|
||
|
{
|
||
|
.y = 0,
|
||
|
.affineMode = ST_OAM_AFFINE_OFF,
|
||
|
.objMode = ST_OAM_OBJ_NORMAL,
|
||
|
.mosaic = 0,
|
||
|
.bpp = ST_OAM_4BPP,
|
||
|
.shape = SPRITE_SHAPE(16x16),
|
||
|
.x = 0,
|
||
|
.matrixNum = 0,
|
||
|
.size = SPRITE_SIZE(16x16),
|
||
|
.tileNum = 0,
|
||
|
.priority = 2,
|
||
|
.paletteNum = 0,
|
||
|
.affineParam = 0
|
||
|
};
|
||
|
|
||
|
static const union AnimCmd sAnim_ConditionSelectionIcon_Selected[] =
|
||
|
{
|
||
|
ANIMCMD_FRAME(0, 5),
|
||
|
ANIMCMD_END
|
||
|
};
|
||
|
|
||
|
static const union AnimCmd sAnim_ConditionSelectionIcon_Unselected[] =
|
||
|
{
|
||
|
ANIMCMD_FRAME(4, 5),
|
||
|
ANIMCMD_END
|
||
|
};
|
||
|
|
||
|
static const union AnimCmd *const sAnims_ConditionSelectionIcon[] =
|
||
|
{
|
||
|
[CONDITION_ICON_SELECTED] = sAnim_ConditionSelectionIcon_Selected,
|
||
|
[CONDITION_ICON_UNSELECTED] = sAnim_ConditionSelectionIcon_Unselected
|
||
|
};
|
||
|
|
||
|
// Just loads the generic data, up to the caller to load the actual sheet/pal for the specific mon
|
||
|
void LoadConditionMonPicTemplate(struct SpriteSheet *sheet, struct SpriteTemplate *template, struct SpritePalette *pal)
|
||
|
{
|
||
|
struct SpriteSheet dataSheet = {NULL, MON_PIC_SIZE, TAG_CONDITION_MON};
|
||
|
|
||
|
struct SpriteTemplate dataTemplate =
|
||
|
{
|
||
|
.tileTag = TAG_CONDITION_MON,
|
||
|
.paletteTag = TAG_CONDITION_MON,
|
||
|
.oam = &sOam_ConditionMonPic,
|
||
|
.anims = gDummySpriteAnimTable,
|
||
|
.images = NULL,
|
||
|
.affineAnims = gDummySpriteAffineAnimTable,
|
||
|
.callback = SpriteCallbackDummy,
|
||
|
};
|
||
|
|
||
|
struct SpritePalette dataPal = {NULL, TAG_CONDITION_MON};
|
||
|
|
||
|
*sheet = dataSheet;
|
||
|
*template = dataTemplate;
|
||
|
*pal = dataPal;
|
||
|
}
|
||
|
|
||
|
void LoadConditionSelectionIcons(struct SpriteSheet *sheets, struct SpriteTemplate * template, struct SpritePalette *pals)
|
||
|
{
|
||
|
u8 i;
|
||
|
|
||
|
struct SpriteSheet dataSheets[] =
|
||
|
{
|
||
|
{sConditionPokeball_Gfx, 0x100, TAG_CONDITION_BALL},
|
||
|
{sConditionPokeballPlaceholder_Gfx, 0x20, TAG_CONDITION_BALL_PLACEHOLDER},
|
||
|
{gPokenavConditionCancel_Gfx, 0x100, TAG_CONDITION_CANCEL},
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
struct SpritePalette dataPals[] =
|
||
|
{
|
||
|
{gPokenavConditionCancel_Pal, TAG_CONDITION_BALL},
|
||
|
{gPokenavConditionCancel_Pal + 16, TAG_CONDITION_CANCEL},
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
// Tag is overwritten for the other selection icons
|
||
|
struct SpriteTemplate dataTemplate =
|
||
|
{
|
||
|
.tileTag = TAG_CONDITION_BALL,
|
||
|
.paletteTag = TAG_CONDITION_BALL,
|
||
|
.oam = &sOam_ConditionSelectionIcon,
|
||
|
.anims = sAnims_ConditionSelectionIcon,
|
||
|
.images = NULL,
|
||
|
.affineAnims = gDummySpriteAffineAnimTable,
|
||
|
.callback = SpriteCallbackDummy,
|
||
|
};
|
||
|
|
||
|
for (i = 0; i < ARRAY_COUNT(dataSheets); i++)
|
||
|
*(sheets++) = dataSheets[i];
|
||
|
|
||
|
*template = dataTemplate;
|
||
|
|
||
|
for (i = 0; i < ARRAY_COUNT(dataPals); i++)
|
||
|
*(pals++) = dataPals[i];
|
||
|
}
|
||
|
|
||
|
#define sSparkleId data[0]
|
||
|
#define sDelayTimer data[1]
|
||
|
#define sNumExtraSparkles data[2]
|
||
|
#define sCurSparkleId data[3]
|
||
|
#define sMonSpriteId data[4]
|
||
|
#define sNextSparkleSpriteId data[5]
|
||
|
|
||
|
void LoadConditionSparkle(struct SpriteSheet *sheet, struct SpritePalette *pal)
|
||
|
{
|
||
|
struct SpriteSheet dataSheet = {sConditionSparkle_Pal, 0x380, TAG_CONDITION_SPARKLE};
|
||
|
struct SpritePalette dataPal = {sConditionSparkle_Gfx, TAG_CONDITION_SPARKLE};
|
||
|
|
||
|
*sheet = dataSheet;
|
||
|
*pal = dataPal;
|
||
|
}
|
||
|
|
||
|
static void SpriteCB_ConditionSparkle_DoNextAfterDelay(struct Sprite *sprite)
|
||
|
{
|
||
|
if (++sprite->sDelayTimer > 60)
|
||
|
{
|
||
|
sprite->sDelayTimer = 0;
|
||
|
SetNextConditionSparkle(sprite);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void SpriteCB_ConditionSparkle_WaitForAllAnim(struct Sprite *sprite)
|
||
|
{
|
||
|
if (sprite->animEnded)
|
||
|
{
|
||
|
sprite->sDelayTimer = 0;
|
||
|
sprite->callback = SpriteCB_ConditionSparkle_DoNextAfterDelay;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static const struct OamData sOam_ConditionSparkle =
|
||
|
{
|
||
|
.y = 0,
|
||
|
.affineMode = ST_OAM_AFFINE_OFF,
|
||
|
.objMode = ST_OAM_OBJ_NORMAL,
|
||
|
.bpp = ST_OAM_4BPP,
|
||
|
.shape = SPRITE_SHAPE(16x16),
|
||
|
.x = 0,
|
||
|
.size = SPRITE_SIZE(16x16),
|
||
|
.priority = 0,
|
||
|
};
|
||
|
|
||
|
static const union AnimCmd sAnim_ConditionSparkle[] =
|
||
|
{
|
||
|
ANIMCMD_FRAME(0, 5),
|
||
|
ANIMCMD_FRAME(4, 5),
|
||
|
ANIMCMD_FRAME(8, 5),
|
||
|
ANIMCMD_FRAME(12, 5),
|
||
|
ANIMCMD_FRAME(16, 5),
|
||
|
ANIMCMD_FRAME(20, 5),
|
||
|
ANIMCMD_FRAME(24, 5),
|
||
|
ANIMCMD_END
|
||
|
};
|
||
|
|
||
|
static const union AnimCmd *const sAnims_ConditionSparkle[] =
|
||
|
{
|
||
|
&sAnim_ConditionSparkle[0], // Only this entry is used
|
||
|
&sAnim_ConditionSparkle[2],
|
||
|
&sAnim_ConditionSparkle[4],
|
||
|
&sAnim_ConditionSparkle[6],
|
||
|
&sAnim_ConditionSparkle[8], // Here below OOB, will crash if used
|
||
|
&sAnim_ConditionSparkle[10],
|
||
|
&sAnim_ConditionSparkle[12],
|
||
|
};
|
||
|
|
||
|
static const struct SpriteTemplate sSpriteTemplate_ConditionSparkle =
|
||
|
{
|
||
|
.tileTag = TAG_CONDITION_SPARKLE,
|
||
|
.paletteTag = TAG_CONDITION_SPARKLE,
|
||
|
.oam = &sOam_ConditionSparkle,
|
||
|
.anims = sAnims_ConditionSparkle,
|
||
|
.images = NULL,
|
||
|
.affineAnims = gDummySpriteAffineAnimTable,
|
||
|
.callback = SpriteCB_ConditionSparkle,
|
||
|
};
|
||
|
|
||
|
static const s16 sConditionSparkleCoords[MAX_CONDITION_SPARKLES][2] =
|
||
|
{
|
||
|
{ 0, -35},
|
||
|
{ 20, -28},
|
||
|
{ 33, -10},
|
||
|
{ 33, 10},
|
||
|
{ 20, 28},
|
||
|
{ 0, 35},
|
||
|
{-20, 28},
|
||
|
{-33, 10},
|
||
|
{-33, -10},
|
||
|
{-20, -28},
|
||
|
};
|
||
|
|
||
|
static void SetConditionSparklePosition(struct Sprite *sprite)
|
||
|
{
|
||
|
struct Sprite *mon = &gSprites[sprite->sMonSpriteId];
|
||
|
|
||
|
if (mon != NULL)
|
||
|
{
|
||
|
sprite->x = mon->x + mon->x2 + sConditionSparkleCoords[sprite->sSparkleId][0];
|
||
|
sprite->y = mon->y + mon->y2 + sConditionSparkleCoords[sprite->sSparkleId][1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprite->x = sConditionSparkleCoords[sprite->sSparkleId][0] + 40;
|
||
|
sprite->y = sConditionSparkleCoords[sprite->sSparkleId][1] + 104;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void InitConditionSparkles(u8 count, bool8 allowFirstShowAll, struct Sprite **sprites)
|
||
|
{
|
||
|
u16 i;
|
||
|
|
||
|
for (i = 0; i < MAX_CONDITION_SPARKLES; i++)
|
||
|
{
|
||
|
if (sprites[i] != NULL)
|
||
|
{
|
||
|
sprites[i]->sSparkleId = i;
|
||
|
sprites[i]->sDelayTimer = (i * 16) + 1;
|
||
|
sprites[i]->sNumExtraSparkles = count;
|
||
|
sprites[i]->sCurSparkleId = i;
|
||
|
if (!allowFirstShowAll || count != MAX_CONDITION_SPARKLES - 1)
|
||
|
{
|
||
|
sprites[i]->callback = SpriteCB_ConditionSparkle;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetConditionSparklePosition(sprites[i]);
|
||
|
ShowAllConditionSparkles(sprites[i]);
|
||
|
sprites[i]->callback = SpriteCB_ConditionSparkle_WaitForAllAnim;
|
||
|
sprites[i]->invisible = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void SetNextConditionSparkle(struct Sprite *sprite)
|
||
|
{
|
||
|
u16 i;
|
||
|
u8 id = sprite->sNextSparkleSpriteId;
|
||
|
for (i = 0; i < sprite->sNumExtraSparkles + 1; i++)
|
||
|
{
|
||
|
gSprites[id].sDelayTimer = (gSprites[id].sSparkleId * 16) + 1;
|
||
|
gSprites[id].callback = SpriteCB_ConditionSparkle;
|
||
|
id = gSprites[id].sNextSparkleSpriteId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ResetConditionSparkleSprites(struct Sprite **sprites)
|
||
|
{
|
||
|
u8 i;
|
||
|
|
||
|
for (i = 0; i < MAX_CONDITION_SPARKLES; i++)
|
||
|
sprites[i] = NULL;
|
||
|
}
|
||
|
|
||
|
void CreateConditionSparkleSprites(struct Sprite **sprites, u8 monSpriteId, u8 _count)
|
||
|
{
|
||
|
u16 i, spriteId, firstSpriteId = 0;
|
||
|
u8 count = _count;
|
||
|
|
||
|
for (i = 0; i < count + 1; i++)
|
||
|
{
|
||
|
spriteId = CreateSprite(&sSpriteTemplate_ConditionSparkle, 0, 0, 0);
|
||
|
if (spriteId != MAX_SPRITES)
|
||
|
{
|
||
|
sprites[i] = &gSprites[spriteId];
|
||
|
sprites[i]->invisible = TRUE;
|
||
|
sprites[i]->sMonSpriteId = monSpriteId;
|
||
|
if (i != 0)
|
||
|
sprites[i - 1]->sNextSparkleSpriteId = spriteId;
|
||
|
else
|
||
|
firstSpriteId = spriteId;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sprites[count]->sNextSparkleSpriteId = firstSpriteId;
|
||
|
InitConditionSparkles(count, TRUE, sprites);
|
||
|
}
|
||
|
|
||
|
void DestroyConditionSparkleSprites(struct Sprite **sprites)
|
||
|
{
|
||
|
u16 i;
|
||
|
|
||
|
for (i = 0; i < MAX_CONDITION_SPARKLES; i++)
|
||
|
{
|
||
|
if (sprites[i] != NULL)
|
||
|
{
|
||
|
DestroySprite(sprites[i]);
|
||
|
sprites[i] = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FreeConditionSparkles(struct Sprite **sprites)
|
||
|
{
|
||
|
DestroyConditionSparkleSprites(sprites);
|
||
|
FreeSpriteTilesByTag(TAG_CONDITION_SPARKLE);
|
||
|
FreeSpritePaletteByTag(TAG_CONDITION_SPARKLE);
|
||
|
}
|
||
|
|
||
|
static void SpriteCB_ConditionSparkle(struct Sprite *sprite)
|
||
|
{
|
||
|
// Delay, then do sparkle anim
|
||
|
if (sprite->sDelayTimer != 0)
|
||
|
{
|
||
|
if (--sprite->sDelayTimer != 0)
|
||
|
return;
|
||
|
|
||
|
SeekSpriteAnim(sprite, 0);
|
||
|
sprite->invisible = FALSE;
|
||
|
}
|
||
|
|
||
|
SetConditionSparklePosition(sprite);
|
||
|
|
||
|
// Set up next sparkle
|
||
|
if (sprite->animEnded)
|
||
|
{
|
||
|
sprite->invisible = TRUE;
|
||
|
if (sprite->sCurSparkleId == sprite->sNumExtraSparkles)
|
||
|
{
|
||
|
if (sprite->sCurSparkleId == MAX_CONDITION_SPARKLES - 1)
|
||
|
{
|
||
|
ShowAllConditionSparkles(sprite);
|
||
|
sprite->callback = SpriteCB_ConditionSparkle_WaitForAllAnim;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprite->callback = SpriteCB_ConditionSparkle_DoNextAfterDelay;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprite->callback = SpriteCallbackDummy;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ShowAllConditionSparkles(struct Sprite *sprite)
|
||
|
{
|
||
|
u8 i, id = sprite->sNextSparkleSpriteId;
|
||
|
|
||
|
for (i = 0; i < sprite->sNumExtraSparkles + 1; i++)
|
||
|
{
|
||
|
SeekSpriteAnim(&gSprites[id], 0);
|
||
|
gSprites[id].invisible = FALSE;
|
||
|
id = gSprites[id].sNextSparkleSpriteId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#undef sSparkleId
|
||
|
#undef sDelayTimer
|
||
|
#undef sNumExtraSparkles
|
||
|
#undef sCurSparkleId
|
||
|
#undef sMonSpriteId
|
||
|
#undef sNextSparkleSpriteId
|
||
|
|
||
|
static const u8 *const sLvlUpStatStrings[NUM_STATS] =
|
||
|
{
|
||
|
gText_MaxHP,
|
||
|
gText_Attack,
|
||
|
gText_Defense,
|
||
|
gText_SpAtk,
|
||
|
gText_SpDef,
|
||
|
gText_Speed
|
||
|
};
|
||
|
|
||
|
void DrawLevelUpWindowPg1(u16 windowId, u16 *statsBefore, u16 *statsAfter, u8 bgClr, u8 fgClr, u8 shadowClr)
|
||
|
{
|
||
|
u16 i, x;
|
||
|
s16 statsDiff[NUM_STATS];
|
||
|
u8 text[12];
|
||
|
u8 color[3];
|
||
|
|
||
|
FillWindowPixelBuffer(windowId, PIXEL_FILL(bgClr));
|
||
|
|
||
|
statsDiff[0] = statsAfter[STAT_HP] - statsBefore[STAT_HP];
|
||
|
statsDiff[1] = statsAfter[STAT_ATK] - statsBefore[STAT_ATK];
|
||
|
statsDiff[2] = statsAfter[STAT_DEF] - statsBefore[STAT_DEF];
|
||
|
statsDiff[3] = statsAfter[STAT_SPATK] - statsBefore[STAT_SPATK];
|
||
|
statsDiff[4] = statsAfter[STAT_SPDEF] - statsBefore[STAT_SPDEF];
|
||
|
statsDiff[5] = statsAfter[STAT_SPEED] - statsBefore[STAT_SPEED];
|
||
|
|
||
|
color[0] = bgClr;
|
||
|
color[1] = fgClr;
|
||
|
color[2] = shadowClr;
|
||
|
|
||
|
for (i = 0; i < NUM_STATS; i++)
|
||
|
{
|
||
|
|
||
|
AddTextPrinterParameterized3(windowId,
|
||
|
FONT_NORMAL,
|
||
|
0,
|
||
|
15 * i,
|
||
|
color,
|
||
|
-1,
|
||
|
sLvlUpStatStrings[i]);
|
||
|
|
||
|
StringCopy(text, (statsDiff[i] >= 0) ? gText_Plus : gText_Dash);
|
||
|
AddTextPrinterParameterized3(windowId,
|
||
|
FONT_NORMAL,
|
||
|
56,
|
||
|
15 * i,
|
||
|
color,
|
||
|
-1,
|
||
|
text);
|
||
|
if (abs(statsDiff[i]) <= 9)
|
||
|
x = 18;
|
||
|
else
|
||
|
x = 12;
|
||
|
|
||
|
ConvertIntToDecimalStringN(text, abs(statsDiff[i]), STR_CONV_MODE_LEFT_ALIGN, 2);
|
||
|
AddTextPrinterParameterized3(windowId,
|
||
|
FONT_NORMAL,
|
||
|
56 + x,
|
||
|
15 * i,
|
||
|
color,
|
||
|
-1,
|
||
|
text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DrawLevelUpWindowPg2(u16 windowId, u16 *currStats, u8 bgClr, u8 fgClr, u8 shadowClr)
|
||
|
{
|
||
|
u16 i, numDigits, x;
|
||
|
s16 stats[NUM_STATS];
|
||
|
u8 text[12];
|
||
|
u8 color[3];
|
||
|
|
||
|
FillWindowPixelBuffer(windowId, PIXEL_FILL(bgClr));
|
||
|
|
||
|
stats[0] = currStats[STAT_HP];
|
||
|
stats[1] = currStats[STAT_ATK];
|
||
|
stats[2] = currStats[STAT_DEF];
|
||
|
stats[3] = currStats[STAT_SPATK];
|
||
|
stats[4] = currStats[STAT_SPDEF];
|
||
|
stats[5] = currStats[STAT_SPEED];
|
||
|
|
||
|
color[0] = bgClr;
|
||
|
color[1] = fgClr;
|
||
|
color[2] = shadowClr;
|
||
|
|
||
|
for (i = 0; i < NUM_STATS; i++)
|
||
|
{
|
||
|
if (stats[i] > 99)
|
||
|
numDigits = 3;
|
||
|
else if (stats[i] > 9)
|
||
|
numDigits = 2;
|
||
|
else
|
||
|
numDigits = 1;
|
||
|
|
||
|
ConvertIntToDecimalStringN(text, stats[i], STR_CONV_MODE_LEFT_ALIGN, numDigits);
|
||
|
x = 6 * (4 - numDigits);
|
||
|
|
||
|
AddTextPrinterParameterized3(windowId,
|
||
|
FONT_NORMAL,
|
||
|
0,
|
||
|
15 * i,
|
||
|
color,
|
||
|
-1,
|
||
|
sLvlUpStatStrings[i]);
|
||
|
|
||
|
AddTextPrinterParameterized3(windowId,
|
||
|
FONT_NORMAL,
|
||
|
56 + x,
|
||
|
15 * i,
|
||
|
color,
|
||
|
-1,
|
||
|
text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GetMonLevelUpWindowStats(struct Pokemon *mon, u16 *currStats)
|
||
|
{
|
||
|
currStats[STAT_HP] = GetMonData(mon, MON_DATA_MAX_HP);
|
||
|
currStats[STAT_ATK] = GetMonData(mon, MON_DATA_ATK);
|
||
|
currStats[STAT_DEF] = GetMonData(mon, MON_DATA_DEF);
|
||
|
currStats[STAT_SPEED] = GetMonData(mon, MON_DATA_SPEED);
|
||
|
currStats[STAT_SPATK] = GetMonData(mon, MON_DATA_SPATK);
|
||
|
currStats[STAT_SPDEF] = GetMonData(mon, MON_DATA_SPDEF);
|
||
|
}
|