Files
perfect_dark/src/game/menuitem.c
T
Matthew Coppola ea464ca6ac Allow dragging sliders with the mouse
Additional code was introduced to handle edge cases. Sliders are always
dimmed when clicked with the mouse, now. When the mouse button is
release, dimmed mode is turned off. This was done to prevent adjacent
slider items from being accidentally modified by moving the mouse over
them with the left mouse button held down, as well as to allow proper
functioning of deferred sliders with the mouse.
2025-02-24 00:05:51 -05:00

4691 lines
127 KiB
C

#include <ultra64.h>
#include "constants.h"
#include "game/camdraw.h"
#include "game/game_006900.h"
#include "game/objectives.h"
#include "game/tex.h"
#include "game/menugfx.h"
#include "game/menuitem.h"
#include "game/menu.h"
#include "game/mainmenu.h"
#include "game/objectives.h"
#include "game/game_1531a0.h"
#include "game/lv.h"
#include "game/challenge.h"
#include "game/training.h"
#include "game/lang.h"
#include "game/mplayer/mplayer.h"
#include "game/options.h"
#include "bss.h"
#include "lib/vi.h"
#include "lib/main.h"
#include "lib/str.h"
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "input.h"
#define MENU_KEYBOARD_ROWS 6
#else
#define MENU_KEYBOARD_ROWS 5
#endif
u8 g_MpSelectedPlayersForStats[MAX_PLAYERS];
#if VERSION >= VERSION_NTSC_1_0
s32 g_ScissorX1 = 0;
s32 g_ScissorX2 = 0;
s32 g_ScissorY1 = 0;
s32 g_ScissorY2 = 0;
#endif
s32 var800711a0 = 0;
s32 var800711a4 = 0;
s32 g_LineHeight = LINEHEIGHT;
u8 g_KeyboardKeys[5][10] = {
{ '0','1','2','3','4','5','6','7','8','9' },
{ 'A','B','C','D','E','F','G','H','I','J' },
{ 'K','L','M','N','O','P','Q','R','S','T' },
{ 'U','V','W','X','Y','Z',' ','?','!','.' },
{ '1','2','1','2','1','2','3','1','2','3' },
};
static s32 deferredindex = -1;
s32 func0f0e5ce0(s32 value)
{
if (value < var800711a4) {
var800711a0++;
if (var800711a0 > SCREEN_320) {
var800711a0 = 0;
}
}
var800711a4 = value;
return var800711a0;
}
s32 menuitem0f0e5d2c(s32 arg0, struct menuitem *item)
{
union handlerdata data;
s32 s0;
s32 s1;
s32 a0;
bool done;
s32 numgroups;
if (item->handler == NULL) {
return 0;
}
data.list.value = 0;
data.list.unk0c = 0;
item->handler(MENUOP_GETOPTGROUPCOUNT, item, &data);
if (data.list.value == 0) {
s1 = arg0 / g_LineHeight;
} else {
numgroups = data.list.value;
s0 = arg0;
done = false;
s1 = 0;
data.list.values32 = 0;
data.list.unk04 = 0;
while (!done) {
if (data.list.values32 < numgroups) {
item->handler(MENUOP_GETGROUPSTARTINDEX, item, &data);
a0 = data.list.groupstartindex;
} else {
a0 = 9999;
}
data.list.value++;
if (s1 + s0 / g_LineHeight >= a0) {
s0 = s0 - (a0 - s1) * g_LineHeight - LINEHEIGHT;
s1 += a0 - s1;
} else {
s1 += s0 / g_LineHeight;
done = true;
}
}
}
if (s1 < 0) {
s1 = 0;
}
return s1;
}
s16 menuitemListGetOffsetY(s16 optionindex, struct menuitem *item)
{
union handlerdata data;
bool done;
s32 numlines;
s32 numgroups;
if (item->handler == NULL) {
return 0;
}
if (optionindex < 0) {
optionindex = 0;
}
data.list.value = 0;
data.list.unk0c = 0;
item->handler(MENUOP_GETOPTGROUPCOUNT, item, &data);
if (data.list.value == 0) {
return optionindex * g_LineHeight;
}
done = false;
numlines = 0;
numgroups = data.list.value;
data.list.unk04 = 0;
for (data.list.value = 0; !done && data.list.values32 < numgroups; data.list.value++) {
item->handler(MENUOP_GETGROUPSTARTINDEX, item, &data);
if (optionindex >= data.list.groupstartindex) {
numlines++;
} else {
done = true;
}
}
return optionindex * g_LineHeight + numlines * LINEHEIGHT;
}
Gfx *menuitemListRenderHeader(Gfx *gdl, s16 x1, s16 y1, s16 width, s16 arg4, s16 height, char *text, struct menudialog *dialog)
{
s32 x;
s32 y;
u32 colour;
colour = MIXCOLOUR(dialog, listgroup_headerbg);
if (dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x2c) & 0xffffff00) | (colour & 0xff);
}
gdl = text0f153780(gdl);
#if VERSION >= VERSION_NTSC_1_0
gdl = menugfxDrawListGroupHeader(gdl, x1, y1, x1 + width, y1 + height, x1 + arg4, colour & 0xff);
#else
gdl = menugfxDrawListGroupHeader(gdl, x1, y1, x1 + width, y1 + height, x1 + arg4);
#endif
gdl = text0f153628(gdl);
x = x1 + 3;
y = y1 + 2;
colour = MIXCOLOUR(dialog, listgroup_headerfg);
if (dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x2c) & 0xffffff00) | (colour & 0xff);
}
textSetWaveColours(g_MenuWave2Colours[dialog->type].listgroup_headerfg, g_MenuWave1Colours[dialog->type].listgroup_headerfg);
gdl = textRenderProjected(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, width, height, 0, 0);
return gdl;
}
Gfx *menuitemListOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2)
{
gDPFillRectangleScaled(gdl++, x, y, x + x2, y + y2);
return gdl;
}
Gfx *menuitemListRender(Gfx *gdl, struct menurendercontext *context)
{
struct menuitemdata_list *itemdata = &context->data->list;
union handlerdata sp15c;
union handlerdata sp14c;
union handlerdata sp13c;
char *text2;
s32 firstonscreenoptionindex;
s32 x;
s32 y;
s32 sp128;
s32 halfheight;
bool done2 = false;
s32 left = context->x + 2;
s32 tmp;
s32 s4;
char *text;
s32 height;
s32 numoptions;
s32 sp104;
s32 numgroups;
s32 nextgroupstartindex = 9999;
s32 optionindex;
struct font *font = g_FontHandelGothicSm;
u32 selectedindex;
s16 width;
u8 donestickyheader = false;
u8 done;
struct fontchar *chars = g_CharsHandelGothicSm;
u32 colour;
union handlerdata spd4;
char *title;
if (context->item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
context->item->handler(MENUOP_GETOPTIONHEIGHT, context->item, &spd4);
g_LineHeight = spd4.list.value;
} else {
g_LineHeight = LINEHEIGHT;
}
width = context->dialog->width;
if (context->item->flags & MENUITEMFLAG_LIST_AUTOWIDTH) {
width = context->width;
}
#if VERSION >= VERSION_NTSC_1_0
g_ScissorX1 = context->x * g_ScaleX;
g_ScissorX2 = (context->x + width) * g_ScaleX;
g_ScissorY1 = context->y;
g_ScissorY2 = context->y + context->height;
if (g_ScissorX1 < 0) {
g_ScissorX1 = 0;
}
if (g_ScissorX2 < 0) {
g_ScissorX2 = 0;
}
if (g_ScissorY1 < 0) {
g_ScissorY1 = 0;
}
if (g_ScissorY2 < 0) {
g_ScissorY2 = 0;
}
if (g_ScissorX1 > viGetBufWidth()) {
g_ScissorX1 = viGetBufWidth();
}
if (g_ScissorX2 > viGetBufWidth()) {
g_ScissorX2 = viGetBufWidth();
}
if (g_ScissorY1 > viGetBufHeight()) {
g_ScissorY1 = viGetBufHeight();
}
if (g_ScissorY2 > viGetBufHeight()) {
g_ScissorY2 = viGetBufHeight();
}
if (g_ScissorX1 > g_ScissorX2) {
g_ScissorX2 = g_ScissorX1;
}
if (g_ScissorY1 > g_ScissorY2) {
g_ScissorY2 = g_ScissorY1;
}
gDPSetScissor(gdl++, G_SC_NON_INTERLACE, g_ScissorX1, g_ScissorY1, g_ScissorX2, g_ScissorY2);
#else
gDPSetScissor(gdl++, G_SC_NON_INTERLACE,
context->x * g_ScaleX, context->y,
(context->x + width) * g_ScaleX, context->y + context->height);
#endif
halfheight = context->height / 2;
halfheight /= g_LineHeight;
halfheight *= g_LineHeight;
itemdata->viewheight = context->height;
if (context->item->type == MENUITEMTYPE_DROPDOWN || context->item->type == MENUITEMTYPE_PLAYERSTATS) {
gdl = menugfxDrawDropdownBackground(gdl, context->x, context->y, context->x + context->width, context->y + context->height);
gdl = menugfxDrawShimmer(gdl, context->x, context->y, context->x + 1, context->y + context->height, 0x0000007f, true, 15, true);
gdl = menugfxDrawShimmer(gdl, context->x + context->width, context->y, context->x + context->width + 1, context->y + context->height, 0x0000007f, false, 15, true);
gdl = menugfxDrawShimmer(gdl, context->x, context->y, context->x + context->width, context->y + 1, 0x0000007f, false, 15, true);
gdl = menugfxDrawShimmer(gdl, context->x, context->y + context->height, context->x + context->width, context->y + context->height + 1, 0x0000007f, false, 15, false);
}
if (context->item->handler == NULL) {
return gdl;
}
sp15c.list.value = 0;
context->item->handler(MENUOP_GETSELECTEDINDEX, context->item, &sp15c);
selectedindex = sp15c.list.value;
if (selectedindex >= 0x10000) {
selectedindex = -1;
}
sp104 = context->y + 1;
context->item->handler(MENUOP_GETOPTIONCOUNT, context->item, &sp15c);
numoptions = sp15c.list.value;
tmp = itemdata->curoffsety - halfheight;
firstonscreenoptionindex = menuitem0f0e5d2c(itemdata->curoffsety - halfheight, context->item);
y = context->y + tmp;
gdl = text0f153628(gdl);
optionindex = firstonscreenoptionindex;
sp15c.list.unk04 = 0;
s4 = menuitemListGetOffsetY(optionindex, context->item) + halfheight - itemdata->curoffsety;
sp14c.list.value = 0;
sp14c.list.unk0c = 0;
context->item->handler(MENUOP_GETOPTGROUPCOUNT, context->item, &sp14c);
numgroups = sp14c.list.values32 & 0xffffffff;
if (numoptions > 0) {
// If this list uses option groups, draw a sticky group header
// if the player has scroll down far enough
if (numgroups != 0) {
s32 spc8;
done = false;
spc8 = 0;
nextgroupstartindex = 0;
sp14c.list.values32 = 0;
sp14c.list.unk04 = 0;
// Iterate all groups ascending until we find the first one that's
// on-screen, then take the one before it
while (!done && sp14c.list.values32 < numgroups) {
context->item->handler(MENUOP_GETGROUPSTARTINDEX, context->item, &sp14c);
tmp = sp14c.list.groupstartindex & 0xffffffff;
if (tmp <= firstonscreenoptionindex) {
spc8 = sp14c.list.values32;
nextgroupstartindex = tmp;
sp14c.list.values32++;
} else {
done = true;
}
}
sp13c.list.values32 = spc8;
sp13c.list.unk04 = 0;
sp13c.list.unk0c = sp14c.list.unk0c;
if (nextgroupstartindex < firstonscreenoptionindex || s4 < LINEHEIGHT) {
text = (char *) context->item->handler(MENUOP_GETOPTGROUPTEXT, context->item, &sp13c);
if (s4 + g_LineHeight > 0) {
gdl = menuitemListRenderHeader(gdl, context->x, context->y, context->width, width, LINEHEIGHT, text, context->dialog);
donestickyheader = true;
}
sp104 += LINEHEIGHT;
sp13c.list.value++;
if (sp14c.list.values32 < numgroups) {
context->item->handler(MENUOP_GETGROUPSTARTINDEX, context->item, &sp14c);
tmp = sp14c.list.groupstartindex & 0xffffffff;
nextgroupstartindex = tmp;
sp14c.list.values32++;
} else {
nextgroupstartindex = 9999;
}
}
}
if (firstonscreenoptionindex == nextgroupstartindex) {
s4 -= LINEHEIGHT;
}
// Iterate the options to be drawn
while (!done2) {
colour = MIXCOLOUR(context->dialog, item_unfocused);
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0x00000000, 127) & 0xffffff00) | (colour & 0xff);
}
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_unfocused, g_MenuWave1Colours[context->dialog->type].item_unfocused);
if (optionindex == nextgroupstartindex) {
// Draw a group header. Note that optionindex is not incremented
// in this branch, but nextgroupstartindex is changed so next time the loop
// iterates it will enter the else branch.
if (context->item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
gdl = menuApplyScissor(gdl);
}
title = (char *) context->item->handler(MENUOP_GETOPTGROUPTEXT, context->item, &sp13c);
sp13c.list.value++;
height = context->height - s4;
if (height > LINEHEIGHT) {
height = LINEHEIGHT;
}
gdl = menuitemListRenderHeader(gdl, context->x, context->y + s4, context->width, width, height, title, context->dialog);
if (sp14c.list.values32 < numgroups) {
context->item->handler(MENUOP_GETGROUPSTARTINDEX, context->item, &sp14c);
tmp = sp14c.list.groupstartindex & 0xffffffff;
nextgroupstartindex = tmp;
sp14c.list.value++;
} else {
nextgroupstartindex = 9999;
}
s4 += LINEHEIGHT;
} else {
if (optionindex < numoptions) {
// Draw an option
union handlerdata spb8;
bool spb4 = false;
if ((s32)selectedindex == optionindex) {
colour |= 0xffffff00;
}
if (optionindex == itemdata->index && context->focused) {
u32 spb0;
u32 weight = menuGetSinOscFrac(40.0f) * 255.0f;
spb0 = MIXCOLOUR(context->dialog, item_focused_inner);
colour = colourBlend(colour, colour & 0xff, 127);
colour = colourBlend(colour, spb0, weight);
#if VERSION >= VERSION_NTSC_1_0
// If not transitioning the dialog type
if ((!(context->dialog->transitionfrac >= 0.0f) || context->dialog->type2 != 0)
&& (!(context->dialog->transitionfrac < 0.0f) || context->dialog->type != 0)) {
text0f156024(1);
spb4 = true;
}
#else
text0f156024(1);
spb4 = true;
#endif
}
if (context->item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
// The handler wants to override the default rendering
struct menuitemrenderdata renderdata;
s32 sp94left;
s32 sp90top;
s32 sp8cright;
s32 sp88bottom;
renderdata.x = context->x;
renderdata.y = context->y + s4;
renderdata.width = context->width;
renderdata.colour = colour;
renderdata.unk10 = optionindex == itemdata->index;
sp94left = renderdata.x;
sp90top = renderdata.y;
sp8cright = renderdata.x + renderdata.width;
sp88bottom = renderdata.y + g_LineHeight;
if (sp90top < context->y) {
sp90top = context->y;
}
if (sp88bottom > context->y + context->height - 1) {
sp88bottom = context->y + context->height - 1;
}
if (donestickyheader && sp90top < context->y + LINEHEIGHT) {
sp90top = context->y + LINEHEIGHT;
}
if (sp94left < 0) {
sp94left = 0;
}
if (sp8cright < 0) {
sp8cright = 0;
}
// This logic doesn't look right...
if (sp94left / g_ScaleX > viGetWidth()) {
sp94left = viGetWidth() / g_ScaleX;
}
if (sp8cright / g_ScaleX > viGetWidth()) {
sp8cright = viGetWidth() / g_ScaleX;
}
gDPPipeSync(gdl++);
#if VERSION >= VERSION_NTSC_1_0
g_ScissorX1 = sp94left * g_ScaleX;
g_ScissorX2 = sp8cright * g_ScaleX;
g_ScissorY1 = sp90top;
g_ScissorY2 = sp88bottom;
if (g_ScissorX1 < 0) {
g_ScissorX1 = 0;
}
if (g_ScissorX2 < 0) {
g_ScissorX2 = 0;
}
if (g_ScissorY1 < 0) {
g_ScissorY1 = 0;
}
if (g_ScissorY2 < 0) {
g_ScissorY2 = 0;
}
if (g_ScissorX1 > viGetBufWidth()) {
g_ScissorX1 = viGetBufWidth();
}
if (g_ScissorX2 > viGetBufWidth()) {
g_ScissorX2 = viGetBufWidth();
}
if (g_ScissorY1 > viGetBufHeight()) {
g_ScissorY1 = viGetBufHeight();
}
if (g_ScissorY2 > viGetBufHeight()) {
g_ScissorY2 = viGetBufHeight();
}
if (g_ScissorX2 < g_ScissorX1) {
g_ScissorX2 = g_ScissorX1;
}
if (g_ScissorY2 < g_ScissorY1) {
g_ScissorY2 = g_ScissorY1;
}
gDPSetScissor(gdl++, G_SC_NON_INTERLACE, g_ScissorX1, g_ScissorY1, g_ScissorX2, g_ScissorY2);
#else
gDPSetScissor(gdl++, G_SC_NON_INTERLACE, sp94left * g_ScaleX, sp90top, sp8cright * g_ScaleX, sp88bottom);
#endif
spb8.type19.gdl = gdl;
spb8.type19.unk04 = optionindex;
spb8.type19.renderdata2 = (void *)((uintptr_t)&renderdata);
spb8.type19.unk0c = sp15c.list.unk04;
gdl = (Gfx *) context->item->handler(MENUOP_RENDER, context->item, &spb8);
sp15c.list.unk04 = spb8.type19.unk0c;
} else {
// Default/simple option (label and optional checkbox)
sp15c.list.value = optionindex;
text2 = (char *) context->item->handler(MENUOP_GETOPTIONTEXT, context->item, &sp15c);
sp128 = 0;
y = context->y + s4 + 1;
if (context->item->type == MENUITEMTYPE_DROPDOWN || context->item->type == MENUITEMTYPE_PLAYERSTATS) {
x = left;
} else {
x = left + 8;
}
if (y < sp104) {
sp128 = y - sp104;
y = sp104;
}
height = context->y + context->height - y;
if (height < 0) {
height = 0;
}
gdl = textRenderProjected(gdl, &x, &y, text2, chars, font, colour, context->width - left + context->x, height, sp128, 0);
// Consider a checkbox
spb8.list.value = optionindex;
spb8.list.unk04 = 255;
context->item->handler(MENUOP_GETLISTITEMCHECKBOX, context->item, &spb8);
if (spb8.list.unk04 != 255) {
gdl = menugfxDrawCheckbox(gdl, left, context->y + s4 + 1, 6, spb8.list.unk04, colour, 0xff00007f);
}
}
if (spb4) {
text0f156024(0);
}
}
optionindex++;
s4 += g_LineHeight;
if (optionindex >= numoptions) {
done2 = true;
}
}
if (context->height < s4) {
done2 = true;
}
}
gdl = menuApplyScissor(gdl);
} else {
// The list has no options in it
colour = MIXCOLOUR(context->dialog, item_unfocused);
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0x00000000, 0x0000007f) & 0xffffff00) | (colour & 0xff);
}
x = left + 8;
y = context->y + context->height / 2;
// "< Empty >"
gdl = textRenderProjected(gdl, &x, &y, langGet(L_OPTIONS_313), chars, font, colour, context->width - left + context->x, viGetHeight(), sp128, 0);
}
gdl = text0f153780(gdl);
return gdl;
}
bool menuitemListTick(struct menuitem *item, struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
f32 f0;
union handlerdata handlerdata;
union handlerdata handlerdata2;
s16 min;
s16 max;
s32 tmp;
s32 i;
u32 last;
s16 prev2;
s16 prev;
if (item->handler == NULL) {
return true;
}
if (item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
item->handler(MENUOP_GETOPTIONHEIGHT, item, &handlerdata2);
g_LineHeight = handlerdata2.list.value;
} else {
g_LineHeight = LINEHEIGHT;
}
if (item->type == MENUITEMTYPE_DROPDOWN || item->type == MENUITEMTYPE_PLAYERSTATS) {
min = data->list.viewheight / 2;
min = min / g_LineHeight;
min *= g_LineHeight;
data->list.targetoffsety = menuitemListGetOffsetY(data->list.index, item);
if (data->list.targetoffsety < min) {
data->list.targetoffsety = min;
}
item->handler(MENUOP_GETOPTIONCOUNT, item, &handlerdata);
max = (s16) handlerdata.list.value * g_LineHeight - data->list.viewheight + min;
if (data->list.targetoffsety > max) {
data->list.targetoffsety = max;
}
}
if (data->list.curoffsety != data->list.targetoffsety) {
f0 = data->list.curoffsety;
prev = data->list.curoffsety;
for (i = 0; i < g_Vars.diffframe60; i++) {
f0 = data->list.targetoffsety * (PAL ? 0.404f : 0.35f) + (PAL ? 0.59599995613098f : 0.65f) * f0;
}
data->list.curoffsety = f0;
if (data->list.curoffsety != data->list.targetoffsety && prev == data->list.curoffsety) {
if (data->list.curoffsety < data->list.targetoffsety) {
data->list.curoffsety++;
} else {
data->list.curoffsety--;
}
}
}
if (tickflags & MENUTICKFLAG_ITEMISFOCUSED) {
item->handler(MENUOP_GETOPTIONCOUNT, item, &handlerdata);
if (handlerdata.list.value) {
last = handlerdata.list.value - 1;
if (data->list.index > last) {
data->list.index = last;
data->list.targetoffsety = menuitemListGetOffsetY(data->list.index, item);
}
#ifdef PLATFORM_N64
if (inputs->updown) {
prev2 = data->list.index;
data->list.index += inputs->updown;
#else
if (inputs->updown || inputs->mousescroll) {
prev2 = data->list.index;
data->list.index += inputs->updown + inputs->mousescroll;
#endif
if (data->list.index < 0) {
data->list.index = handlerdata.list.value - 1;
}
if (data->list.index >= (s16)handlerdata.list.value) {
data->list.index = 0;
}
data->list.targetoffsety = menuitemListGetOffsetY(data->list.index, item);
if (prev2 != data->list.index) {
handlerdata.list.value = data->list.index;
item->handler(MENUOP_LISTITEMFOCUS, item, &handlerdata);
menuPlaySound(MENUSOUND_SUBFOCUS);
}
}
if (inputs->select) {
handlerdata.list.value = data->list.index;
handlerdata.list.unk04 = 0;
if (inputs->start) {
handlerdata.list.unk04 = 1;
}
item->handler(MENUOP_SET, item, &handlerdata);
menuPlaySound(MENUSOUND_SELECT);
if (handlerdata.list.unk04 == 2) {
inputs->start = false;
}
}
inputs->updown = false;
}
}
tmp = data->list.index;
handlerdata.list.value = tmp;
handlerdata.list.unk04 = 1;
handlerdata.list.unk0c = tmp;
handlerdata.list.groupstartindex = (tickflags & MENUTICKFLAG_ITEMISFOCUSED) ? 1 : 0;
item->handler(MENUOP_25, item, &handlerdata);
if (handlerdata.list.unk0c != handlerdata.list.value) {
data->list.index = handlerdata.list.value;
data->list.targetoffsety = menuitemListGetOffsetY(data->list.index, item);
}
return true;
}
void menuitemDropdownInit(struct menuitem *item, union menuitemdata *data)
{
uintptr_t (*handler)(s32 operation, struct menuitem *item, union handlerdata *data);
union handlerdata handlerdata;
union handlerdata handlerdata2;
data->dropdown.list.curoffsety = 0;
data->dropdown.list.index = 0;
data->dropdown.unk08 = 0;
data->dropdown.unk0a = 0;
handlerdata.dropdown.value = 0;
handler = item->handler;
if (item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
handler(MENUOP_GETOPTIONHEIGHT, item, &handlerdata2);
g_LineHeight = handlerdata2.dropdown.value;
} else {
g_LineHeight = LINEHEIGHT;
}
item->handler(MENUOP_GETSELECTEDINDEX, item, &handlerdata);
if (handlerdata.dropdown.value < 0xffff) {
data->dropdown.list.index = (u16) handlerdata.dropdown.value;
} else {
// The value won't fit in unk02.
// Maybe MENUOP_25 is getting a scaled-down value?
// But then how does it know the value is scaled?
handlerdata.dropdown.value = 0;
handlerdata.dropdown.unk04 = 0;
item->handler(MENUOP_25, item, &handlerdata);
data->dropdown.list.index = handlerdata.dropdown.value;
}
data->dropdown.list.targetoffsety = menuitemListGetOffsetY(data->dropdown.list.index, item);
item->handler(MENUOP_LISTITEMFOCUS, item, &handlerdata);
}
Gfx *menuitemDropdownRender(Gfx *gdl, struct menurendercontext *context)
{
u32 colour;
char *text;
s32 x;
s32 y;
union handlerdata data;
x = context->x;
y = context->y;
text = menuResolveParam2Text(context->item);
colour = MIXCOLOUR(context->dialog, item_unfocused);
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x7f) & 0xffffff00) | (colour & 0xff);
}
if (context->focused != 0) {
if ((context->focused & 2) != 0) {
u32 tmpcolour;
u32 weight = menuGetSinOscFrac(20) * 255.0f; \
tmpcolour = MIXCOLOUR(context->dialog, item_focused_inner);
colour = colourBlend(colour, colour & 0xff, 0x7f);
colour = colourBlend(colour, tmpcolour, weight);
} else {
u32 tmpcolour;
u32 weight = menuGetSinOscFrac(40) * 255.0f; \
tmpcolour = MIXCOLOUR(context->dialog, item_focused_inner);
colour = colourBlend(colour, colour & 0xff, 0x7f);
colour = colourBlend(colour, tmpcolour, weight);
}
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_focused_inner, g_MenuWave1Colours[context->dialog->type].item_focused_inner);
} else {
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_unfocused, g_MenuWave1Colours[context->dialog->type].item_unfocused);
}
if (menuIsItemDisabled(context->item, context->dialog)) {
colour = MIXCOLOUR(context->dialog, item_disabled);
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x7f) & 0xffffff00) | (colour & 0xff);
}
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_disabled, g_MenuWave1Colours[context->dialog->type].item_disabled);
}
gdl = text0f153628(gdl);
x = context->x + 10;
y = context->y + 2;
gdl = textRenderProjected(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, context->width, context->height, 0, 0);
if (context->dialog->unk6e) {
x = context->x + 80;
} else {
x = context->x + context->width - 60;
}
if (context->item->flags & MENUITEMFLAG_DROPDOWN_BELOW) {
x = context->x + 30;
}
y = context->y + 2;
if (context->item->handler != NULL) {
s32 textheight;
s32 textwidth;
data.list.value = 0;
context->item->handler(MENUOP_GETSELECTEDINDEX, context->item, &data);
data.list.unk04 = 0;
text = (char *)context->item->handler(MENUOP_GETOPTIONTEXT, context->item, &data);
textMeasure(&textheight, &textwidth, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
x = context->x + context->width - textwidth - 10;
gdl = textRenderProjected(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, context->width, context->height, 0, 0);
}
gdl = text0f153780(gdl);
return gdl;
}
bool menuitemDropdownTick(struct menuitem *item, struct menudialog *dialog, struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
u32 stack;
if ((tickflags & MENUTICKFLAG_ITEMISFOCUSED) && item->handler) {
if (dialog->dimmed && item == dialog->focuseditem) {
menuitemListTick(item, inputs, tickflags, data);
if (mpIsPlayerLockedOut(g_MpPlayerNum)) {
if ((item->flags & MENUITEMFLAG_LOCKABLEMAJOR) || (dialog->definition->flags & MENUDIALOGFLAG_MPLOCKABLE)) {
dialog->dimmed = false;
}
}
}
if (inputs->back && dialog->dimmed) {
dialog->dimmed = false;
inputs->back = false;
menuPlaySound(MENUSOUND_TOGGLEOFF);
}
if (inputs->select) {
if (dialog->dimmed) {
dialog->dimmed = false;
} else {
union handlerdata handlerdata;
dialog->dimmed = true;
menuitemDropdownInit(item, data);
handlerdata.dropdown.value = 0;
item->handler(MENUOP_GETSELECTEDINDEX, item, &handlerdata);
data->dropdown.unk0e = (u32)handlerdata.dropdown.value * g_LineHeight;
menuPlaySound(MENUSOUND_TOGGLEOFF);
}
}
}
return true;
}
Gfx *menuitemDropdownOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2, struct menuitem *item, struct menudialog *dialog, union menuitemdata *data)
{
s32 stack;
s32 textwidth;
s32 textheight;
s32 numoptions;
s32 i;
char *text;
struct menurendercontext context;
#if VERSION >= VERSION_NTSC_1_0
if (dialog->unk6e) {
context.x = x + 78;
} else if (x2 < 0) {
context.x = x + 2;
} else {
context.x = x + x2 - 62;
}
#else
if (dialog->unk6e) {
context.x = x + 78;
} else {
context.x = x + x2 - 62;
}
#endif
if (item->flags & MENUITEMFLAG_DROPDOWN_BELOW) {
context.x = x + 30;
}
context.y = y + 1;
context.width = 56;
context.item = item;
context.focused = true;
context.dialog = dialog;
context.data = data;
if (dialog->dimmed && item->handler && dialog->focuseditem == item) {
union handlerdata handlerdata;
if (item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
union handlerdata handlerdata2;
item->handler(MENUOP_GETOPTIONHEIGHT, item, &handlerdata2); \
g_LineHeight = handlerdata2.dropdown.value; \
} else { \
g_LineHeight = LINEHEIGHT;
}
item->handler(MENUOP_GETOPTIONCOUNT, item, &handlerdata);
numoptions = handlerdata.dropdown.value;
context.width = 0;
context.height = g_LineHeight * numoptions;
for (i = 0; i != numoptions; i++) {
handlerdata.dropdown.value = i;
text = (char *)item->handler(MENUOP_GETOPTIONTEXT, item, &handlerdata);
textMeasure(&textheight, &textwidth, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
textwidth += 6;
if (textwidth > context.width) {
context.width = textwidth;
}
}
#if VERSION >= VERSION_NTSC_1_0
if (x2 > 0) {
context.x = x + x2 - context.width - 7;
}
#else
context.x = x + x2 - context.width - 7;
#endif
if (context.y + context.height > dialog->y + dialog->height + 2) {
if (context.height > dialog->height) {
i = dialog->height;
i = i / (u32)g_LineHeight;
i = i * g_LineHeight;
context.height = i;
}
context.y = dialog->y + dialog->height - context.height + 2;
}
gdl = menuitemListRender(gdl, &context);
}
return gdl;
}
bool menuitemKeyboardIsStringEmptyOrSpaces(char *text)
{
s32 i;
if (text[0] == '\0') {
return true;
}
i = 0;
while (text[i] != '\0') {
if (text[i] != ' ') {
return false;
}
i++;
}
return true;
}
/**
* Keyboard reference:
* +---------------------------------------+
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
* |---+---+---+---+---+---+---+---+---+---|
* | a | b | c | d | e | f | g | h | i | j |
* |---+---+---+---+---+---+---+---+---+---|
* | k | l | m | n | o | p | q | r | s | t |
* |---+---+---+---+---+---+---+---+---+---|
* | u | v | w | x | y | z | | ? | ! | . |
* |-------+-----------+-----------+-------|
* | DEL | CAPS | CANCEL | OK |
* +-------+-----------+-----------+-------+
*/
Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context)
{
char label[8];
s32 x;
s32 y;
struct menuitemdata_keyboard *data = &context->data->keyboard;
u32 cursorcolour;
u32 textcolour;
u32 tmpcolour;
s32 buttonwidth;
s32 row;
s32 col;
s32 rowspan;
u32 alpha;
s32 textheight;
s32 textwidth;
label[1] = '\n';
label[2] = '\0';
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_unfocused,
g_MenuWave1Colours[context->dialog->type].item_unfocused);
// Draw input field background
gdl = textSetPrimColour(gdl, 0x0000ff7f);
if (context->item->param3 == 0) {
// Half width
gDPFillRectangleScaled(gdl++,
context->x + 4, context->y + 1,
context->x + 63, context->y + 10);
} else {
// Full width
gDPFillRectangleScaled(gdl++,
context->x + 4, context->y + 1,
context->x + 125, context->y + 10);
}
// Render text value
gdl = text0f153838(gdl);
x = context->x + 4;
y = context->y + 2;
gdl = text0f153628(gdl);
gdl = textRenderProjected(gdl, &x, &y, data->string, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0xffffffff, context->width, context->height, 0, 0);
gdl = text0f153780(gdl);
// Render cursor
alpha = menuGetSinOscFrac(40) * 255;
if (context->dialog->transitionfrac < 0) {
cursorcolour = g_MenuColours[context->dialog->type].item_focused_inner;
} else {
cursorcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_focused_inner,
g_MenuColours[context->dialog->type].item_focused_inner,
context->dialog->colourweight);
}
cursorcolour = colourBlend(colourBlend(0x0000ffff, 0x000000ff, 127), cursorcolour, alpha);
gdl = textSetPrimColour(gdl, cursorcolour);
gDPFillRectangleScaled(gdl++, x + 1, context->y + 2, x + 3, context->y + 9);
gdl = text0f153838(gdl);
// Render horizontal grid lines
for (row = 0; row < MENU_KEYBOARD_ROWS + 1; row++) {
gdl = menugfxDrawFilledRect(gdl, context->x + 4, context->y + row * 11 + 13,
context->x + 124, context->y + row * 11 + 14, 0x00ffff7f, 0x00ffff7f);
}
// Render vertical grid lines
for (col = 0; col < 11; col++) {
rowspan = 5;
if (col == 1 || col == 3 || col == 4 || col == 6 || col == 7 || col == 9) {
rowspan = 4;
}
gdl = menugfxDrawFilledRect(gdl, context->x + col * 12 + 4, context->y + 13,
context->x + col * 12 + 5, context->y + rowspan * 11 + 14, 0x00ffff7f, 0x00ffff7f);
}
#if MENU_KEYBOARD_ROWS > 5
gdl = menugfxDrawFilledRect(gdl, context->x + 4, context->y + 5 * 11 + 13,
context->x + 5, context->y + 6 * 11 + 14, 0x00ffff7f, 0x00ffff7f);
gdl = menugfxDrawFilledRect(gdl, context->x + 10 * 12 + 4, context->y + 5 * 11 + 13,
context->x + 10 * 12 + 5, context->y + 6 * 11 + 14, 0x00ffff7f, 0x00ffff7f);
#endif
gdl = text0f153628(gdl);
x = context->x + 10;
y = context->y + 2;
for (col = 0; col < 10; col++) {
for (row = 0; row < MENU_KEYBOARD_ROWS; row++) {
if (context->dialog->transitionfrac < 0) {
textcolour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
textcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
textcolour = (colourBlend(textcolour, 0x00000000, 127) & 0xffffff00) | (textcolour & 0xff);
}
if (data->capseffective && col == 2 && row == 4) {
// CAPS button - make it yellow
textcolour = (textcolour & 0xff) | 0xffff0000;
}
// If this button is the focused one, set highlighted colour
if (col == data->col && row == data->row) {
alpha = menuGetSinOscFrac(40) * 255;
if (context->dialog->transitionfrac < 0) {
tmpcolour = g_MenuColours[context->dialog->type].item_focused_inner;
} else {
tmpcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_focused_inner,
g_MenuColours[context->dialog->type].item_focused_inner,
context->dialog->colourweight);
}
textcolour = colourBlend(colourBlend(textcolour, textcolour & 0xff, 127), tmpcolour, alpha);
}
x = context->x + col * 12 + 4;
y = context->y + row * 11 + 15;
if (row == 4) {
u16 labels[] = {
L_OPTIONS_314, // "DEL"
L_OPTIONS_315, // "CAPS"
L_OPTIONS_316, // "CANCEL"
L_OPTIONS_317, // "OK"
};
s32 index = -1;
if (col == 0) {
index = 0;
}
if (col == 2) {
index = 1;
}
if (col == 5) {
index = 2;
}
if (col == 8) {
index = 3;
}
if (index >= 0) {
buttonwidth = 24;
y++;
if (index == 1 || index == 2) {
buttonwidth = 36;
}
textMeasure(&textheight, &textwidth, langGet(labels[index]), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
x = (buttonwidth - textwidth) / 2 + x;
// Dim the OK button if string is empty
if (index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) {
if (context->dialog->transitionfrac < 0) {
textcolour = g_MenuColours[context->dialog->type].item_disabled;
} else {
textcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_disabled,
g_MenuColours[context->dialog->type].item_disabled,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
textcolour = (colourBlend(textcolour, 0, 127) & 0xffffff00) | (textcolour & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_disabled,
g_MenuWave1Colours[context->dialog->type].item_disabled);
}
#ifndef PLATFORM_N64
// Dim the Caps button if keyboard typing is enabled
if (index == 1 && g_MenuKeyboardPlayer == g_MpPlayerNum) {
textcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_disabled,
g_MenuColours[context->dialog->type].item_disabled,
context->dialog->colourweight);
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_disabled,
g_MenuWave1Colours[context->dialog->type].item_disabled);
}
#endif
gdl = textRenderProjected(gdl, &x, &y, langGet(labels[index]), g_CharsHandelGothicXs, g_FontHandelGothicXs, textcolour, context->width, context->height, 0, 0);
if (index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) {
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_unfocused,
g_MenuWave1Colours[context->dialog->type].item_unfocused);
}
}
} else if (row == 5) {
#ifndef PLATFORM_N64
if (col == 0) {
char *kbtext;
// make the button yellow if it's active
if (g_MenuKeyboardPlayer == g_MpPlayerNum) {
textcolour = (textcolour & 0xff) | 0xffff0000;
kbtext = (char *)"ESC: CANCEL ENTER: OK";
} else {
kbtext = (char *)"TYPE WITH KEYBOARD";
}
++y;
textMeasure(&textheight, &textwidth, kbtext, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
x = context->x + (context->width - textwidth) / 2;
gdl = textRenderProjected(gdl, &x, &y, kbtext, g_CharsHandelGothicXs, g_FontHandelGothicXs, textcolour, context->width, context->height, 0, 0);
}
#endif
} else {
// Alpha-numeric cell
label[0] = g_KeyboardKeys[row][col];
if (!data->capseffective && label[0] >= 'A' && label[0] <= 'Z') {
// Make lowercase
label[0] += 32;
}
textMeasure(&textheight, &textwidth, label, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
x = (12 - textwidth) / 2 + x;
gdl = textRenderProjected(gdl, &x, &y, label, g_CharsHandelGothicSm, g_FontHandelGothicSm, textcolour, context->width, context->height, 0, 0);
}
}
}
gdl = text0f153780(gdl);
// Highlight border of focused button
{
s32 x1 = context->x + data->col * 12 + 4;
s32 x2 = context->x + data->col * 12 + 16;
s32 y1 = context->y + data->row * 11 + 13;
s32 y2 = context->y + data->row * 11 + 24;
if (data->row == 4) {
if (data->col == 8) {
x2 += 12;
}
if (data->col == 5) {
x2 += 24;
}
if (data->col == 2) {
x2 += 24;
}
if (data->col == 0) {
x2 += 12;
}
} else if (data->row == 5) {
x2 = context->x + 9 * 12 + 16;
}
gdl = menugfxDrawLine(gdl, x1, y1, x2, y1 + 1, -1, -1); // top
gdl = menugfxDrawLine(gdl, x2, y1, x2 + 1, y2 + 1, -1, -1); // right
gdl = menugfxDrawLine(gdl, x1, y2, x2, y2 + 1, -1, -1); // bottom
gdl = menugfxDrawLine(gdl, x1, y1, x1 + 1, y2 + 1, -1, -1); // left
}
return gdl;
}
/**
* Keyboard reference:
* +---------------------------------------+
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
* |---+---+---+---+---+---+---+---+---+---|
* | a | b | c | d | e | f | g | h | i | j |
* |---+---+---+---+---+---+---+---+---+---|
* | k | l | m | n | o | p | q | r | s | t |
* |---+---+---+---+---+---+---+---+---+---|
* | u | v | w | x | y | z | | ? | ! | . |
* |-------+-----------+-----------+-------|
* | DEL | CAPS | CANCEL | OK |
* +-------+-----------+-----------+-------+
*/
bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
struct menuitemdata_keyboard *kb = &data->keyboard;
union handlerdata handlerdata;
s32 delete = false;
if (tickflags & MENUTICKFLAG_ITEMISFOCUSED) {
s16 prevcol = kb->col;
s16 prevrow = kb->row;
#ifndef PLATFORM_N64
// handle mouse
struct menudialog *dialog = g_Menus[g_MpPlayerNum].curdialog;
if (dialog && g_MenuUsingMouse && !inputs->leftright && !inputs->updown) {
const s32 dleft = dialog->x + 4;
const s32 dright = dleft + 12 * 10;
const s32 dtop = menuitemGetTop(item, dialog) + 12;
const s32 dbottom = dtop + 11 * 6;
const s32 mx = inputs->mousex;
const s32 my = inputs->mousey;
if (mx > dleft && mx < dright && my > dtop && my < dbottom) {
kb->row = (my - dtop) / 11;
kb->col = (mx - dleft) / 12;
if (kb->row == 4) {
if (kb->col < 2) {
kb->col = 0;
} else if (kb->col < 5) {
kb->col = 2;
} else if (kb->col < 8) {
kb->col = 5;
} else {
kb->col = 8;
}
} else if (kb->row == 5) {
kb->col = 0;
}
}
}
#endif
// Handle left/right movement
// In most cases the loop only runs once, but on row 4 the buttons span
// across multiple columns so the loop will run again until the column
// number is valid.
if (inputs->leftright != 0) {
do {
kb->col += inputs->leftright;
if (kb->col < 0) {
kb->col = 9;
}
if (kb->col > 9) {
kb->col = 0;
}
} while (kb->row == 4 && kb->col != 0 && kb->col != 2 && kb->col != 5 && kb->col != 8);
}
// Handle up/down movement
if (inputs->updown) {
kb->row += inputs->updown;
if (kb->row < 0) {
kb->row = MENU_KEYBOARD_ROWS - 1;
}
if (kb->row > MENU_KEYBOARD_ROWS - 1) {
kb->row = 0;
}
// If moving onto row 4, bump column to a valid one
if (kb->row == 4) {
if (kb->col == 9) {
kb->col = 8;
}
if (kb->col == 7 || kb->col == 6) {
kb->col = 5;
}
if (kb->col == 3 || kb->col == 4) {
kb->col = 2;
}
if (kb->col == 1) {
kb->col = 0;
}
}
}
if (kb->row == 5) {
kb->col = 0;
}
if (prevcol != kb->col || prevrow != kb->row) {
menuPlaySound(MENUSOUND_KEYBOARDFOCUS);
}
if (inputs->back2) {
delete = true;
}
if (inputs->start) {
if (item->handler && !menuitemKeyboardIsStringEmptyOrSpaces(kb->string)) {
menuPlaySound(MENUSOUND_SELECT);
handlerdata.keyboard.string = kb->string;
item->handler(MENUOP_SETTEXT, item, &handlerdata);
menuPopDialog();
item->handler(MENUOP_SET, item, &handlerdata);
}
inputs->start = false;
}
#ifndef PLATFORM_N64
if (g_MenuKeyboardPlayer == g_MpPlayerNum) {
// match caps state to keyboard shift/caps if typing with keyboard
const u32 kmod = inputGetKeyModState();
kb->capslock = (kmod & KM_CAPS) != 0;
if (kmod & KM_SHIFT) {
kb->capslock = !kb->capslock;
}
// handle text input
s32 prevpos = strlen(kb->string);
s32 pos = prevpos;
s32 result = inputTextHandler(kb->string, sizeof(kb->string), &pos, true);
if (result == -1) {
// cancel
kb->row = 5;
kb->col = 0;
inputs->select = true;
} else if (result == 1) {
// accept
kb->row = 4;
kb->col = 8;
inputs->select = true;
g_MenuKeyboardPlayer = -1;
inputStopTextInput();
} else if (prevpos < pos) {
// added char
menuPlaySound(MENUSOUND_FOCUS);
}
}
#endif
if (inputs->select) {
if (kb->row == 4) {
// DEL
if (kb->col == 0) {
delete = true;
}
// CAPS
if (kb->col == 2) {
kb->capslock = 1 - kb->capslock;
}
// OK
if (kb->col == 8) {
if (item->handler && !menuitemKeyboardIsStringEmptyOrSpaces(kb->string)) {
handlerdata.keyboard.string = kb->string;
item->handler(MENUOP_SETTEXT, item, &handlerdata);
}
}
// OK or CANCEL
if (kb->col == 8 || kb->col == 5) {
s32 ok = (kb->col == 8);
if (kb->col == 5 || !menuitemKeyboardIsStringEmptyOrSpaces(kb->string)) {
menuPopDialog();
if (ok) {
item->handler(MENUOP_SET, item, &handlerdata);
menuPlaySound(MENUSOUND_SELECT);
} else {
menuPlaySound(MENUSOUND_KEYBOARDCANCEL);
}
inputs->select = false;
}
}
} else if (kb->row == 5) {
#ifndef PLATFORM_N64
if (g_MenuKeyboardPlayer == -1) {
g_MenuKeyboardPlayer = g_MpPlayerNum;
inputClearLastTextChar();
inputStartTextInput();
menuPlaySound(MENUSOUND_SELECT);
} else {
g_MenuKeyboardPlayer = -1;
inputStopTextInput();
menuPlaySound(MENUSOUND_KEYBOARDCANCEL);
}
#endif
} else {
// Pressed A on number or letter
s32 appended = false;
s32 i;
s32 textwidth;
s32 textheight;
if (kb->string[9] == '\0') {
// String is not full
i = 0;
while (!appended) {
if (kb->string[i] == '\0') {
u8 key = g_KeyboardKeys[kb->row][kb->col];
appended = true;
if (kb->capseffective == 0 && key >= 'A' && key <= 'Z') {
// Make lowercase
key += 32;
}
kb->string[i] = key;
}
i++;
}
textMeasure(&textheight, &textwidth, kb->string, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
if (item->param3 == 0 && textwidth > 58) {
delete = true;
}
if (!delete) {
menuPlaySound(MENUSOUND_FOCUS);
}
}
}
}
// Handle deleting
if (delete && kb->string[0] != '\0') {
s32 deleted = false;
s32 i = 10;
menuPlaySound(MENUSOUND_FOCUS);
while (!deleted) {
if (kb->string[i] != '\0') {
kb->string[i] = '\0';
deleted = true;
}
i--;
}
}
}
// Update caps
if (tickflags & MENUTICKFLAG_ITEMISFOCUSED) {
u32 prev = kb->capseffective;
kb->capseffective = kb->capslock;
if (inputs->shoulder) {
// Invert
kb->capseffective = 1 - kb->capseffective;
}
if (kb->capseffective != prev) {
if (kb->capseffective) {
menuPlaySound(MENUSOUND_TOGGLEON);
} else {
menuPlaySound(MENUSOUND_TOGGLEOFF);
}
}
}
return true;
}
void menuitemKeyboardInit(struct menuitem *item, union menuitemdata *data)
{
u32 stack;
s32 i;
for (i = 0; i < ARRAYCOUNT(data->keyboard.string);) {
data->keyboard.string[i++] = '\0';
}
if (item->handler) {
union handlerdata handlerdata;
handlerdata.keyboard.string = data->keyboard.string;
item->handler(MENUOP_GETTEXT, item, &handlerdata);
}
data->keyboard.col = 0;
data->keyboard.row = 4;
data->keyboard.capseffective = 0;
data->keyboard.capslock = 0;
}
Gfx *menuitemSeparatorRender(Gfx *gdl, struct menurendercontext *context)
{
u32 colour;
if (context->dialog->transitionfrac < 0) {
colour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
colour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
colour = (colour & 0xffffff00) | 0x3f;
#if VERSION >= VERSION_JPN_FINAL
return menugfxDrawFilledRect(gdl, context->x, context->y, context->x + context->width, context->y + 1, colour, colour);
#else
return menugfxDrawFilledRect(gdl, context->x, context->y + 2, context->x + context->width, context->y + 3, colour, colour);
#endif
}
u32 var800711e8 = 0x00000000;
Gfx *menuitemObjectivesRenderOne(Gfx *gdl, struct menudialog *dialog, s32 index, s32 position, s16 objx, s16 objy, s16 width, s16 height, bool withstatus, bool narrow)
{
u32 sp12c;
s32 x;
s32 y;
char *sp120;
char buffer[80];
char *spcc;
u32 spc8;
s32 textwidth;
s32 textheight;
s32 spbc;
s32 tmp;
s32 spb4;
s32 spb0;
s32 spac;
s32 spa8;
u32 stack3[2];
s32 sp9c;
s32 sp98;
u32 s0;
s32 stack[4];
s32 sp80;
s32 sp7c;
s32 sp78;
s32 sp74;
s32 stack2;
s32 sp6c;
s32 sp58;
s32 sp54;
spbc = 0;
if (narrow) {
#if VERSION == VERSION_JPN_FINAL
spbc = 14;
#elif VERSION >= VERSION_PAL_FINAL
spbc = 16;
#else
spbc = 12;
#endif
}
mainOverrideVariable("brcol", &var800711e8);
sp120 = langGet(g_Briefing.objectivenames[index]);
y = objy;
sp12c = MIXCOLOUR(dialog, item_unfocused);
if (dialog->dimmed) {
sp12c = (colourBlend(sp12c, 0, 0x2c) & 0xffffff00) | (sp12c & 0xff);
}
textSetWaveColours(g_MenuWave2Colours[dialog->type].item_unfocused, g_MenuWave1Colours[dialog->type].item_unfocused);
buffer[0] = '\0';
// Render objective number
gdl = text0f153628(gdl);
sprintf(buffer, "%d: ", position);
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
x = objx - textwidth + 25;
gdl = textRenderProjected(gdl, &x, &y, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, sp12c, width, height, 0, 0);
x = objx + 25;
if (narrow) {
textWrap(85, sp120, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs);
gdl = textRenderProjected(gdl, &x, &y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, sp12c, width, height, 0, 0);
} else {
sprintf(buffer, "%s", sp120);
gdl = textRenderProjected(gdl, &x, &y, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, sp12c, width, height, 0, 0);
}
if (withstatus) {
switch (objectiveCheck(index)) {
case OBJECTIVE_INCOMPLETE:
spcc = langGet(L_OPTIONS_001); // "Incomplete"
spc8 = 0xffff00ff;
break;
case OBJECTIVE_COMPLETE:
spcc = langGet(L_OPTIONS_000); // "Complete"
spc8 = 0x00ff00ff;
break;
case OBJECTIVE_FAILED:
spcc = langGet(L_OPTIONS_002); // "Failed"
spc8 = 0xff4040ff;
break;
}
if (dialog != g_Menus[g_MpPlayerNum].curdialog) {
spc8 = g_MenuColours[0].item_unfocused;
}
textMeasure(&textheight, &textwidth, spcc, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
if (var800711e8 != 0) {
spc8 = var800711e8;
}
x = objx + width - textwidth - 10;
#if VERSION == VERSION_JPN_FINAL
y = objy + spbc;
y += 10;
#else
y = objy + spbc + 9;
#endif
gdl = textRenderProjected(gdl, &x, &y, spcc, g_CharsHandelGothicXs, g_FontHandelGothicXs, spc8, width, height, 0, 0);
#if VERSION != VERSION_JPN_FINAL
x = objx + width - textwidth - 10;
y = objy + spbc + 9;
gdl = textRenderProjected(gdl, &x, &y, spcc, g_CharsHandelGothicXs, g_FontHandelGothicXs, spc8 & 0xffffff7f, width, height, 0, 0);
#endif
x = objx + width - textwidth - 13;
y = objy + 9;
gdl = text0f153780(gdl);
spb4 = objx + 22;
spb0 = objy - 2;
spac = objy + 8;
spa8 = y + spbc + 2;
tmp = (objx * 3 + objx + 66) / 4;
sp9c = x;
sp98 = tmp + 19 + (sp9c - tmp - 24) * (position - 1) / 5;
sp58 = (objx * 3 + objx + 66) / 4 - 1;
sp54 = (objx * 3 + objx + 66) / 4 + 14;
#if VERSION == VERSION_JPN_FINAL
spa8 += 3;
#endif
gdl = menugfx0f0e2498(gdl);
// Blue lines
gdl = menugfxDrawProjectedLine(gdl, objx, spb0, spb4, spb0 + 1, sp12c & 0xffffff00, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, spb4, spb0, spb4 + 1, spac, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, objx, spac, sp58, spac + 1, sp12c & 0xffffff00, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp58 + 3, spac, spb4 + 1, spac + 1, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp58, spac, sp58 + 1, spa8 + 2, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp58 + 2, spac, sp58 + 3, spa8, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
spb4 = (spc8 & 0xffffff00) | 0x3f;
gdl = menugfxDrawProjectedLine(gdl, sp58 + 2, spa8, sp54, spa8 + 1, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp58, spa8 + 2, sp54, spa8 + 3, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp54, spa8, sp98, spa8 + 1, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp54, spa8 + 2, sp98, spa8 + 3, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
// Coloured lines
gdl = menugfxDrawProjectedLine(gdl, sp98, spa8, sp98 + 1, spa8 + 3, spb4, spb4);
gdl = menugfxDrawProjectedLine(gdl, sp98 + 1, spa8 + 1, sp9c - 4, spa8 + 2, spb4, spb4);
gdl = menugfxDrawProjectedLine(gdl, sp9c - 4, spa8 + 1, sp9c, spa8 + 2, spb4, (spc8 & 0xffffff00) | 0xcf);
} else {
// Render lines without status
sp80 = objx + 22;
sp7c = objy - 2;
sp78 = objy + 8;
sp74 = y + 1;
textMeasure(&textheight, &textwidth, sp120, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
#if VERSION >= VERSION_JPN_FINAL
sp74 -= 2;
#endif
sp6c = objx + textwidth + 25;
sp58 = (objx * 3 + objx + 66) / 4 - 1;
gdl = text0f153780(gdl);
gdl = menugfx0f0e2498(gdl);
gdl = menugfxDrawProjectedLine(gdl, objx, sp7c, sp80, sp7c + 1, sp12c & 0xffffff00, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp80, sp7c, sp80 + 1, sp78, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, objx, sp78, sp58, sp78 + 1, sp12c & 0xffffff00, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp58 + 3, sp78, sp80 + 1, sp78 + 1, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
#if VERSION == VERSION_JPN_FINAL
gdl = menugfxDrawProjectedLine(gdl, sp58, sp78, sp58 + 1, sp74 + 3, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
#else
gdl = menugfxDrawProjectedLine(gdl, sp58, sp78, sp58 + 1, sp74 + 2, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
#endif
gdl = menugfxDrawProjectedLine(gdl, sp58 + 2, sp78, sp58 + 3, sp74, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
gdl = menugfxDrawProjectedLine(gdl, sp58 + 2, sp74, sp6c, sp74 + 1, (sp12c & 0xffffff00) | 0x3f, (sp12c & 0xffffff00) | 0x3f);
}
return gdl;
}
Gfx *menuitemObjectivesRender(Gfx *gdl, struct menurendercontext *context)
{
s32 y = context->y + 5;
s32 position = 1;
s32 i;
for (i = 0; i < ARRAYCOUNT(g_Briefing.objectivenames); i++) {
if (g_Briefing.objectivenames[i] && g_Briefing.objectivedifficulties[i] & (1 << lvGetDifficulty())) {
gdl = menuitemObjectivesRenderOne(gdl,
context->dialog,
i, position, context->x, y, context->width, context->height,
context->item->param != 1,
context->item->param == 2);
position++;
#if VERSION >= VERSION_JPN_FINAL
if (context->item->param == 0) {
y += 24;
} else if (context->item->param == 2) {
y += 36;
} else {
y += 16;
}
#else
if (context->item->param == 0) {
y += 18;
} else if (context->item->param == 2) {
y += VERSION >= VERSION_PAL_FINAL ? 34 : 30;
} else {
y += 14;
}
#endif
}
}
return gdl;
}
Gfx *menuitemModelRender(Gfx *gdl, struct menurendercontext *context)
{
if (context->item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
struct menuitemrenderdata renderdata;
union handlerdata data;
renderdata.x = context->x;
renderdata.y = context->y;
renderdata.width = context->width;
if (context->dialog->transitionfrac < 0) {
renderdata.colour = g_MenuColours[context->dialog->type].item_focused_inner;
} else {
renderdata.colour = colourBlend(
g_MenuColours[context->dialog->type2].item_focused_inner,
g_MenuColours[context->dialog->type].item_focused_inner,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
renderdata.colour = (colourBlend(renderdata.colour, 0, 127) & 0xffffff00) | (renderdata.colour & 0xff);
}
renderdata.unk10 = true;
data.type19.gdl = gdl;
data.type19.unk04 = (intptr_t)&renderdata;
data.type19.renderdata2 = &renderdata;
gdl = (Gfx *)context->item->handler(MENUOP_RENDER, context->item, &data);
}
return gdl;
}
Gfx *menuitemLabelRender(Gfx *gdl, struct menurendercontext *context)
{
u32 colour1;
u32 colour2;
char *text;
s32 x;
s32 y;
struct menudfc *menudfc;
u8 savedvalue = var8007fb9c;
struct fontchar *font1 = g_CharsHandelGothicSm;
struct font *font2 = g_FontHandelGothicSm;
x = context->x + 10;
y = context->y + 2;
if (context->item->flags & MENUITEMFLAG_LESSLEFTPADDING) {
x -= 6;
}
text = menuResolveParam2Text(context->item);
if (!text) {
return gdl;
}
#if VERSION != VERSION_JPN_FINAL
if (context->item->flags & MENUITEMFLAG_SMALLFONT) {
font1 = g_CharsHandelGothicXs;
font2 = g_FontHandelGothicXs;
y -= 2;
}
#endif
if (context->item->flags & MENUITEMFLAG_SELECTABLE_CENTRE) {
// Center
s32 textheight;
s32 textwidth;
textMeasure(&textheight, &textwidth, text, font1, font2, 0);
x = context->x + (context->width - textwidth) / 2;
}
if (context->item->flags & MENUITEMFLAG_LABEL_ALTCOLOUR) {
if (context->dialog->transitionfrac < 0) {
colour1 = g_MenuColours[context->dialog->type].checkbox_checked_unfocused;
} else {
colour1 = colourBlend(
g_MenuColours[context->dialog->type2].checkbox_checked_unfocused,
g_MenuColours[context->dialog->type].checkbox_checked_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
colour1 = (colourBlend(colour1, 0, 127) & 0xffffff00) | (colour1 & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].checkbox_checked_unfocused,
g_MenuWave1Colours[context->dialog->type].checkbox_checked_unfocused);
} else {
if (context->dialog->transitionfrac < 0) {
colour1 = g_MenuColours[context->dialog->type].item_unfocused;
} else {
colour1 = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
colour1 = (colourBlend(colour1, 0, 127) & 0xffffff00) | (colour1 & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_unfocused,
g_MenuWave1Colours[context->dialog->type].item_unfocused);
}
if (menuIsItemDisabled(context->item, context->dialog)) {
if (context->dialog->transitionfrac < 0) {
colour1 = g_MenuColours[context->dialog->type].item_disabled;
} else {
colour1 = colourBlend(
g_MenuColours[context->dialog->type2].item_disabled,
g_MenuColours[context->dialog->type].item_disabled,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
colour1 = (colourBlend(colour1, 0, 127) & 0xffffff00) | (colour1 & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_disabled,
g_MenuWave1Colours[context->dialog->type].item_disabled);
}
menudfc = func0f0f1338(context->item);
if (menudfc) {
if (menudfc->unk04 < 0) {
return gdl;
}
textBackupDiagonalBlendSettings();
textSetDiagonalBlend(x, y, menudfc->unk04 * 300, 0);
var8007fb9c = true;
}
colour2 = colour1;
if (context->item->flags & MENUITEMFLAG_LABEL_CUSTOMCOLOUR) {
union handlerdata data;
data.label.colour2 = colour2;
data.label.colour1 = colour1;
if (context->item->handlervoid) {
context->item->handlervoid(MENUOP_GETCOLOUR, context->item, &data);
}
if (context->item->handlervoid) {
}
colour2 = data.label.colour2;
colour1 = data.label.colour1;
}
gdl = text0f153628(gdl);
gdl = textRenderProjected(gdl, &x, &y, text,
font1, font2, colour1, context->width, context->height, 0, 0);
if ((context->item->flags & MENUITEMFLAG_LABEL_HASRIGHTTEXT) == 0) {
// Right side text
text = menuResolveText(context->item->param3, context->item);
// This is not how you check if a string is empty...
if (text != NULL && text != "") {
s32 textheight;
s32 textwidth;
y = context->y + 2;
#if VERSION != VERSION_JPN_FINAL
if (context->item->flags & MENUITEMFLAG_SMALLFONT) {
y -= 2;
}
#endif
textMeasure(&textheight, &textwidth, text, font1, font2, 0);
x = context->x + context->width - textwidth - 10;
if (context->item->flags & MENUITEMFLAG_LESSLEFTPADDING) {
x += 6;
}
gdl = textRenderProjected(gdl, &x, &y, text,
font1, font2, colour2, context->width, context->height, 0, 0);
}
}
gdl = text0f153780(gdl);
if (menudfc) {
if (context->width + 200 < menudfc->unk04 * 300 && context->dialog->redrawtimer < 0) {
func0f0f13ec(context->item);
}
var8007fb9c = savedvalue;
textRestoreDiagonalBlendSettings();
}
if (context->item->flags & MENUITEMFLAG_LIST_CUSTOMRENDER) {
struct menuitemrenderdata renderdata;
union handlerdata data;
renderdata.x = context->x;
renderdata.y = context->y;
renderdata.width = context->width;
renderdata.colour = colour1;
renderdata.unk10 = false;
data.type19.gdl = gdl;
data.type19.unk04 = 0;
data.type19.renderdata2 = &renderdata;
gdl = (Gfx *) context->item->handler(MENUOP_RENDER, context->item, &data);
}
return gdl;
}
/**
* Renders two yellow bars and an optional label. Suspected to be a resource
* meter but with the calculations ifdeffed out, so the widths are static.
*/
Gfx *menuitemMeterRender(Gfx *gdl, struct menurendercontext *context)
{
u32 a = 9;
char *text;
s32 x;
s32 y;
u32 colour1;
u32 colour2;
u32 colour;
s32 x1;
s32 x2;
s32 x3;
if (context->dialog->transitionfrac < 0) {
colour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
colour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 127) & 0xffffff00) | (colour & 0xff);
}
colour2 = 0xffff0000 | (colour & 0xff);
colour1 = colourBlend(colour2, colour2 & 0xff, 127);
x1 = context->x + 32;
x2 = x1 + a;
x3 = x2 + 6;
gdl = textSetPrimColour(gdl, colour1);
gDPFillRectangleScaled(gdl++, x1, context->y, x2, context->y + 5);
gdl = text0f153838(gdl);
gdl = textSetPrimColour(gdl, colour2);
gDPFillRectangleScaled(gdl++, x2, context->y, x3, context->y + 5);
gdl = text0f153838(gdl);
text = menuResolveParam2Text(context->item);
if (text) {
gdl = text0f153628(gdl);
x = context->x;
y = context->y - 1;
gdl = textRenderProjected(gdl, &x, &y, text, g_CharsHandelGothicXs, g_FontHandelGothicXs,
colour2 & 0xffffff7f, context->width, context->height, 0, 0);
gdl = text0f153780(gdl);
}
return gdl;
}
#ifndef PLATFORM_N64
// Draws a colored box which fills the background of the menu item.
Gfx* menuitemColorBoxRender(Gfx *gdl, struct menurendercontext *context)
{
u32 width = context->width;
u32 height = context->height;
u32 colour1;
s32 x1;
s32 x2;
x1 = context->x;
x2 = x1 + width;
union handlerdata data;
if (context->item->handlervoid) {
context->item->handlervoid(MENUOP_GETCOLOUR, context->item, &data);
}
colour1 = data.label.colour1;
gdl = textSetPrimColour(gdl, colour1);
gDPFillRectangleScaled(gdl++, x1, context->y, x2, context->y + height);
gdl = text0f153838(gdl);
return gdl;
}
#endif
Gfx *menuitemSelectableRender(Gfx *gdl, struct menurendercontext *context)
{
u32 leftcolour;
u32 rightcolour;
char *text;
s32 x = context->x + 10;
s32 y = context->y + 2;
struct fontchar *font1 = g_CharsHandelGothicSm;
struct font *font2 = g_FontHandelGothicSm;
if (context->item->flags & MENUITEMFLAG_LESSLEFTPADDING) {
x -= 6;
}
if (context->item->flags & MENUITEMFLAG_BIGFONT) {
font1 = g_CharsHandelGothicMd;
font2 = g_FontHandelGothicMd;
}
text = menuResolveParam2Text(context->item);
if (context->dialog->transitionfrac < 0) {
leftcolour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
leftcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
leftcolour = (colourBlend(leftcolour, 0, 127) & 0xffffff00) | (leftcolour & 0xff);
}
rightcolour = leftcolour;
if (context->focused) {
// Mismatch: The addiu and lui at eb030 and eb034 are swapped. The addiu
// is adding the lower half of the address of g_MenuColours for
// use in the else branch. The lui is loading a value into $at for the
// float to u32 conversion for weight.
u32 colour2;
u32 weight = menuGetSinOscFrac(40) * 255; \
if (context->dialog->transitionfrac < 0) { \
colour2 = g_MenuColours[context->dialog->type].item_focused_inner; \
} else { \
colour2 = colourBlend(
g_MenuColours[context->dialog->type2].item_focused_inner,
g_MenuColours[context->dialog->type].item_focused_inner,
context->dialog->colourweight);
}
leftcolour = colourBlend(colourBlend(leftcolour, leftcolour & 0x000000ff, 127), colour2, weight);
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_focused_inner,
g_MenuWave1Colours[context->dialog->type].item_focused_inner);
} else {
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_unfocused,
g_MenuWave1Colours[context->dialog->type].item_unfocused);
}
if (menuIsItemDisabled(context->item, context->dialog)) {
if (context->dialog->transitionfrac < 0) {
leftcolour = g_MenuColours[context->dialog->type].item_disabled;
} else {
leftcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_disabled,
g_MenuColours[context->dialog->type].item_disabled,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
leftcolour = (colourBlend(leftcolour, 0x00000000, 127) & 0xffffff00) | (leftcolour & 0xff);
}
rightcolour = leftcolour;
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_disabled,
g_MenuWave1Colours[context->dialog->type].item_disabled);
}
if (context->item->flags & MENUITEMFLAG_SELECTABLE_CENTRE) {
// Center text
s32 textheight;
s32 textwidth;
textMeasure(&textheight, &textwidth, text, font1, font2, 0);
x = context->x + (context->width - textwidth) / 2;
}
if (context->item->flags & MENUITEMFLAG_BIGFONT) {
x += 35;
y += 6;
}
gdl = text0f153628(gdl);
gdl = textRenderProjected(gdl, &x, &y, text, font1, font2,
leftcolour, context->width, context->height, 0, 0);
if ((context->item->flags & (MENUITEMFLAG_LABEL_HASRIGHTTEXT | MENUITEMFLAG_BIGFONT)) == 0) {
// Right side text
text = menuResolveText(context->item->param3, context->item);
// This is not how you check if a string is empty...
if (text != NULL && text != "") {
s32 textheight;
s32 textwidth;
y = context->y + 2;
textMeasure(&textheight, &textwidth, text, font1, font2, 0);
x = context->x + context->width - textwidth - 10;
gdl = textRenderProjected(gdl, &x, &y, text, font1, font2,
rightcolour, context->width, context->height, 0, 0);
}
}
return text0f153780(gdl);
}
bool menuitemSelectableTick(struct menuitem *item, struct menuinputs *inputs, u32 tickflags)
{
if ((tickflags & MENUTICKFLAG_ITEMISFOCUSED) && inputs->select) {
menuPlaySound(MENUSOUND_SELECT);
if (item->flags & MENUITEMFLAG_SELECTABLE_CLOSESDIALOG) {
menuPopDialog();
}
if (item->flags & MENUITEMFLAG_SELECTABLE_OPENSDIALOG) {
menuPushDialog((struct menudialogdef *)item->handler);
} else if (item->handler) {
union handlerdata data;
item->handler(MENUOP_SET, item, &data);
}
}
return true;
}
Gfx *menuitemSliderRender(Gfx *gdl, struct menurendercontext *context)
{
u32 colour;
char *label;
char buffer[16];
s32 x;
s32 y;
s32 extray;
s32 textwidth;
s32 textheight;
union handlerdata data;
s32 slidervalue;
s32 markerx;
extray = 0;
if (context->item->handler != NULL) {
if ((context->item->flags & MENUITEMFLAG_SLIDER_DEFERRED) &&
deferredindex != -1 &&
context->dialog->dimmed &&
context->focused) {
slidervalue = (s16) deferredindex;
} else {
context->item->handler(MENUOP_GETSLIDER, context->item, &data);
slidervalue = (s16) data.slider.value;
}
} else {
slidervalue = 0;
}
if (context->dialog->unk6e != 0) {
extray = 10;
}
if (context->item->flags & MENUITEMFLAG_SLIDER_ALTSIZE) {
extray = 10;
}
x = context->x + 10;
y = context->y + 2;
if (context->item->flags & MENUITEMFLAG_LESSLEFTPADDING) {
x -= 6;
}
label = menuResolveParam2Text(context->item);
markerx = context->x + context->width + slidervalue * 75 / context->item->param3 - 82;
colour = MIXCOLOUR(context->dialog, item_unfocused);
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x7f) & 0xffffff00) | (colour & 0xff);
}
if (context->focused) {
if (context->focused & 2) {
u32 tmpcolour;
u32 weight = menuGetSinOscFrac(40) * 255.0f;
tmpcolour = MIXCOLOUR(context->dialog, item_focused_inner);
colour = colourBlend(colour, colour & 0xff, 0x7f);
colour = colourBlend(colour, tmpcolour, weight) | 0xff;
}
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_focused_inner, g_MenuWave1Colours[context->dialog->type].item_focused_inner);
} else {
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_unfocused, g_MenuWave1Colours[context->dialog->type].item_unfocused);
}
gdl = menugfxRenderSlider(gdl, context->x + context->width - 82, context->y + extray + 5, context->x + context->width - 7, context->y + extray + 11, markerx, colour);
colour = MIXCOLOUR(context->dialog, item_unfocused);
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x7f) & 0xffffff00) | (colour & 0xff);
}
if (context->focused != 0) {
if (context->focused & 2) {
u32 tmpcolour;
u32 weight = menuGetSinOscFrac(20) * 255.0f;
tmpcolour = MIXCOLOUR(context->dialog, item_focused_inner);
colour = colourBlend(colour, colour & 0xff, 0x7f);
colour = colourBlend(colour, tmpcolour, weight);
} else {
u32 tmpcolour;
u32 weight = menuGetSinOscFrac(40) * 255.0f;
tmpcolour = MIXCOLOUR(context->dialog, item_focused_inner);
colour = colourBlend(colour, colour & 0xff, 0x7f);
colour = colourBlend(colour, tmpcolour, weight);
}
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_focused_inner, g_MenuWave1Colours[context->dialog->type].item_focused_inner);
} else {
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_unfocused, g_MenuWave1Colours[context->dialog->type].item_unfocused);
}
gdl = text0f153628(gdl);
gdl = textRenderProjected(gdl, &x, &y, label, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, context->width, context->height, 0, 0);
if ((context->item->flags & MENUITEMFLAG_SLIDER_HIDEVALUE) == 0) {
strcpy(buffer, "");
sprintf(buffer, "%d\n", slidervalue);
if (context->item->handler != NULL) {
data.slider.value = slidervalue;
data.slider.label = buffer;
context->item->handler(MENUOP_GETSLIDERLABEL, context->item, &data);
}
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
x = context->x + context->width - textwidth - 7;
y = context->y + 2;
colour = MIXCOLOUR(context->dialog, item_unfocused);
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x7f) & 0xffffff00) | (colour & 0xff);
}
textSetWaveColours(g_MenuWave2Colours[context->dialog->type].item_unfocused, g_MenuWave1Colours[context->dialog->type].item_unfocused);
colour = (colour & 0xffffff00) | ((colour & 0xff) >> 1);
gdl = textRenderProjected(gdl, &x, &y, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, context->width, context->height, 0, 0);
}
gdl = text0f153780(gdl);
return gdl;
}
bool menuitemSliderTick(struct menuitem *item, struct menudialog *dialog, struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
s32 index;
union handlerdata handlerdata;
s32 tmp;
f32 f0;
f32 f2;
f32 f14;
if ((tickflags & MENUTICKFLAG_ITEMISFOCUSED)) {
#ifndef PLATFORM_N64
if (g_AllowMouseHeld && g_MenuUsingMouse && (inputs->select || inputs->mouseheld)) {
// handle mouse
struct menudialog *dialog = g_Menus[g_MpPlayerNum].curdialog;
if (dialog) {
const s32 left = dialog->x + dialog->width - 82;
const s32 right = dialog->x + dialog->width - 7;
const s32 size = right - left;
const s32 delta = inputs->mousex - left;
if (delta >= -8 && delta <= size + 8) {
if ((tickflags & MENUTICKFLAG_DIALOGISDIMMED) == 0) {
g_MouseDimmedMode = true;
} else {
index = (delta / (f32)size) * item->param3;
if (index < 0) {
index = 0;
}
if (index > item->param3) {
index = item->param3;
}
if (item->handler) {
if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) {
deferredindex = index;
} else {
item->handler(MENUOP_GET, item, &handlerdata);
handlerdata.slider.value = index;
item->handler(MENUOP_SET, item, &handlerdata);
}
}
return true;
}
}
}
}
#endif
if (tickflags & MENUTICKFLAG_DIALOGISDIMMED) {
if (item->handler) {
if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) {
if (deferredindex == -1) {
item->handler(MENUOP_GETSLIDER, item, &handlerdata);
deferredindex = (s16) handlerdata.slider.value;
}
index = deferredindex;
} else {
item->handler(MENUOP_GETSLIDER, item, &handlerdata);
index = (s16) handlerdata.slider.value;
}
} else {
index = 0;
}
#ifndef PLATFORM_N64
if (g_MenuUsingMouse && inputs->mousescroll) {
if ((item->flags & MENUITEMFLAG_SLIDER_FAST) == 0) {
index += -inputs->mousescroll;
} else {
s32 mult = item->param3 / 40;
index += -inputs->mousescroll * (mult ? mult : 1);
}
}
#endif
if ((item->flags & MENUITEMFLAG_SLIDER_FAST) == 0
&& g_Menus[g_MpPlayerNum].xrepeatmode == MENUREPEATMODE_SLOW) {
index = index + inputs->leftright;
} else {
f0 = data->slider.multiplier / 1000.0f;
f0 = f0 * 100.0f / item->param3;
#if VERSION >= VERSION_PAL_BETA
f0 = f0 + inputs->leftrightheld * g_Vars.diffframe60freal;
#else
f0 = f0 + inputs->leftrightheld * g_Vars.diffframe60;
#endif
f0 = item->param3 * f0 / 100.0f;
tmp = f0;
f0 -= tmp;
index += tmp;
data->slider.multiplier = f0 * 1000.0f;
}
if (inputs->xaxis < 0) {
f14 = inputs->xaxis;
f2 = -f14;
} else {
f14 = inputs->xaxis;
f2 = f14;
}
if ((item->flags & MENUITEMFLAG_SLIDER_FAST) == 0 && f2 < 40) {
if (g_Menus[g_MpPlayerNum].xrepeatmode != MENUREPEATMODE_SLOW) {
index = index + inputs->leftright;
}
} else {
f0 = data->slider.multiplier / 1000.0f;
f0 = f0 * 100.0f / item->param3;
if (inputs->xaxis < 0) {
f2 = -f14;
} else {
f2 = f14;
}
if (f2 > 20) {
f2 = (f2 - 20) / 16.0f;
#if VERSION >= VERSION_PAL_BETA
f2 *= g_Vars.diffframe60freal;
#else
f2 *= g_Vars.diffframe60f;
#endif
if (inputs->xaxis < 0) {
f0 -= f2;
} else {
f0 += f2;
}
}
f0 = item->param3 * f0 / 100.0f;
tmp = f0;
f0 -= tmp;
index += tmp;
data->slider.multiplier = f0 * 1000.0f;
}
if (index < 0) {
index = 0;
}
if (index > item->param3) {
index = item->param3;
}
inputs->leftright = 0;
handlerdata.slider.value = index;
if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) {
deferredindex = index;
} else {
if (item->handler) {
item->handler(MENUOP_SET, item, &handlerdata);
}
}
if (inputs->select || g_MouseEndDeferredSlider) {
g_MouseEndDeferredSlider = false;
if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) {
deferredindex = -1;
if (item->handler) {
item->handler(MENUOP_SET, item, &handlerdata);
}
}
dialog->dimmed = false;
}
} else {
if (inputs->select) {
dialog->dimmed = true;
}
}
}
return true;
}
void menuitemSliderInit(union menuitemdata *data)
{
data->slider.multiplier = 0;
}
Gfx *menuitemCarouselRender(Gfx *gdl, struct menurendercontext *context)
{
u32 colour = 0xff0000ff;
if (context->focused) {
u32 colour1;
u32 weight = menuGetSinOscFrac(40) * 255;
if (context->dialog->transitionfrac < 0) {
colour1 = g_MenuColours[context->dialog->type].item_focused_inner;
} else {
colour1 = colourBlend(
g_MenuColours[context->dialog->type2].item_focused_inner,
g_MenuColours[context->dialog->type].item_focused_inner,
context->dialog->colourweight);
}
colour = colourBlend(colourBlend(colour, 0x000000ff, 127), colour1, weight);
}
#ifdef PLATFORM_N64
s16 chevronOffset = 0;
#else
s16 chevronOffset = 3;
#endif
// Left arrow
gdl = menugfxDrawCarouselChevron(gdl, context->x + chevronOffset, context->y + context->height / 2, 8, 1, -1, colour);
// Right arrow
gdl = menugfxDrawCarouselChevron(gdl, context->x + context->width - chevronOffset, context->y + context->height / 2, 8, 3, -1, colour);
// This part of the function is unused because param2 is always zero.
// Setting it to 0x7b causes a crash.
// 0x7c and 0x7d don't do anything with the option value, so it's probable
// that the original source has a commented function call in that block.
if (context->item->param2 == 0x7b && context->item->handler) {
union handlerdata data;
s32 headorbodynum = 0;
context->item->handler(MENUOP_GETSELECTEDINDEX, context->item, &data);
headorbodynum += data.carousel.value;
gdl = func0f14f07c(gdl, headorbodynum,
context->x + context->width / 2 - 32, context->y,
context->x + context->width / 2 + 32, context->y + 64);
} else if ((context->item->param2 == 0x7c || context->item->param2 == 0x7d) && context->item->handler) {
union handlerdata data;
context->item->handler(MENUOP_GETSELECTEDINDEX, context->item, &data);
}
return gdl;
}
bool menuitemCarouselTick(struct menuitem *item, struct menuinputs *inputs, u32 tickflags)
{
union handlerdata data;
s32 index;
s32 numoptions;
bool done;
u32 stack;
if (((tickflags & MENUTICKFLAG_ITEMISFOCUSED) || (item->flags & MENUITEMFLAG_CAROUSEL_04000000)) && item->handler) {
if (inputs->leftright != 0) {
if (mpIsPlayerLockedOut(g_MpPlayerNum) == 0 || (item->flags & MENUITEMFLAG_LOCKABLEMINOR) == 0) {
done = false;
item->handler(MENUOP_GETOPTIONCOUNT, item, &data);
numoptions = data.carousel.value;
item->handler(MENUOP_GETSELECTEDINDEX, item, &data);
index = data.carousel.value;
while (!done) {
index = index + inputs->leftright;
if (index >= numoptions) {
index = 0;
}
if (index < 0) {
index = numoptions - 1;
}
// Some kind of option-is-locked check?
data.carousel.value = index;
if (!item->handler(MENUOP_21, item, &data)) {
done = true;
}
}
data.carousel.value = index;
#if VERSION >= VERSION_NTSC_1_0
data.carousel.unk04 = inputs->shoulder;
#endif
item->handler(MENUOP_SET, item, &data);
}
}
if (item->handler) {
item->handler(MENUOP_11, item, &data);
}
}
return true;
}
Gfx *menuitemCheckboxRender(Gfx *gdl, struct menurendercontext *context)
{
u32 maincolour = 0x00000000;
char *text;
s32 x;
s32 y;
u8 data[3];
bool checked = false;
u32 fillcolour = 0xff002faf;
struct font *font2 = g_FontHandelGothicSm;
struct fontchar *font1 = g_CharsHandelGothicSm;
if (context->item->flags & MENUITEMFLAG_SMALLFONT) {
font2 = g_FontHandelGothicXs;
font1 = g_CharsHandelGothicXs;
}
x = context->x;
y = context->y;
text = menuResolveParam2Text(context->item);
data[0] = 0;
if (context->item->handler
&& context->item->handler(MENUOP_GET, context->item, (union handlerdata *)data) == true) {
checked = true;
if (context->dialog->transitionfrac < 0) {
maincolour = g_MenuColours[context->dialog->type].checkbox_checked_unfocused;
} else {
maincolour = colourBlend(
g_MenuColours[context->dialog->type2].checkbox_checked_unfocused,
g_MenuColours[context->dialog->type].checkbox_checked_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
maincolour = (colourBlend(maincolour, 0, 127) & 0xffffff00) | (maincolour & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].checkbox_checked_unfocused,
g_MenuWave1Colours[context->dialog->type].checkbox_checked_unfocused);
} else {
if (context->dialog->transitionfrac < 0) {
maincolour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
maincolour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
maincolour = (colourBlend(maincolour, 0, 127) & 0xffffff00) | (maincolour & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_unfocused,
g_MenuWave1Colours[context->dialog->type].item_unfocused);
}
gdl = text0f153628(gdl);
if (context->focused) {
// Mismatch: The addiu and lui at ec8fc and ec900 are swapped. The addiu
// is adding the lower half of the address of g_MenuColours for
// use in the else branch. The lui is loading a value into $at for the
// float to u32 conversion for weight.
u32 focuscolour;
u32 weight = menuGetSinOscFrac(40) * 255; \
if (context->dialog->transitionfrac < 0) { \
focuscolour = g_MenuColours[context->dialog->type].item_focused_inner;
} else {
focuscolour = colourBlend(
g_MenuColours[context->dialog->type2].item_focused_inner,
g_MenuColours[context->dialog->type].item_focused_inner,
context->dialog->colourweight);
}
maincolour = colourBlend(colourBlend(maincolour, maincolour & 0xff, 127), focuscolour, weight);
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_focused_inner,
g_MenuWave1Colours[context->dialog->type].item_focused_inner);
}
if (menuIsItemDisabled(context->item, context->dialog)) {
if (context->dialog->transitionfrac < 0) {
maincolour = g_MenuColours[context->dialog->type].item_disabled;
} else {
maincolour = colourBlend(
g_MenuColours[context->dialog->type2].item_disabled,
g_MenuColours[context->dialog->type].item_disabled,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
maincolour = (colourBlend(maincolour, 0, 127) & 0xffffff00) | (maincolour & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_disabled,
g_MenuWave1Colours[context->dialog->type].item_disabled);
fillcolour = 0x7f002faf;
}
#if VERSION == VERSION_JPN_FINAL
gdl = menugfxDrawCheckbox(gdl, context->x + context->width - 19, context->y + 2, 9, checked, maincolour, fillcolour);
#else
gdl = menugfxDrawCheckbox(gdl, context->x + context->width - 16, context->y + 2, 6, checked, maincolour, fillcolour);
#endif
x = context->x + 10;
y = context->y + 2;
gdl = textRenderProjected(gdl, &x, &y, text, font1, font2,
maincolour, context->width, context->height, 0, 0);
return text0f153780(gdl);
}
bool menuitemCheckboxTick(struct menuitem *item, struct menuinputs *inputs, u32 tickflags)
{
union handlerdata data;
if ((tickflags & MENUTICKFLAG_ITEMISFOCUSED) && inputs->select) {
if (item->handler && item->handler(MENUOP_GET, item, &data) == 1) {
data.checkbox.value = 0;
menuPlaySound(MENUSOUND_TOGGLEOFF);
} else {
data.checkbox.value = 1;
menuPlaySound(MENUSOUND_TOGGLEON);
}
if (item->handler) {
item->handler(MENUOP_SET, item, &data);
}
}
return true;
}
char *menuitemScrollableGetText(u32 type)
{
switch (type) {
case DESCRIPTION_MPCONFIG:
return challengeGetConfigDescription(g_Menus[g_MpPlayerNum].training.mpconfig);
case DESCRIPTION_MPCHALLENGE:
if (!challengeIsLoaded()) {
g_Menus[g_MpPlayerNum].menumodel.curparams = 0x4fac5ace;
challengeLoadAndStoreCurrent(g_Menus[g_MpPlayerNum].menumodel.allocstart, g_Menus[g_MpPlayerNum].menumodel.alloclen);
}
return challengeGetCurrentDescription();
case DESCRIPTION_CHRBIO: return ciGetChrBioDescription();
case DESCRIPTION_MISCBIO: return ciGetMiscBioDescription();
case DESCRIPTION_DEVICETRAINING: return dtGetDescription();
case DESCRIPTION_FRWEAPON: return frGetWeaponDescription();
case DESCRIPTION_HANGARBIO: return ciGetHangarBioDescription();
case DESCRIPTION_HOLOTRAINING: return htGetDescription();
case DESCRIPTION_HOLOTIP1: return htGetTip1();
case DESCRIPTION_HOLOTIP2: return htGetTip2();
case DESCRIPTION_DEVICETIP1: return dtGetTip1();
case DESCRIPTION_DEVICETIP2: return dtGetTip2();
}
return langGet(g_Briefing.briefingtextnum);
}
/**
* Render a scrollable menu item, such as briefing paragraphs and challenge
* descriptions.
*
* The text returned from menuitemScrollableGetText is first wrapped to fit the
* width of the dialog. It is then separated into two strings:
*
* headings - which are started with the pipe character and ended with a
* double line break
* body text - which is anything else
*
* Headings are rendered in red, with a dropshadow and are outdented.
*/
Gfx *menuitemScrollableRender(Gfx *gdl, struct menurendercontext *context)
{
char alltext[8000] = "";
char headingtext[8000];
char bodytext[8000];
bool prevwaslinebreak;
char *streams[2];
char *inptr;
s32 inheading;
struct menuitemdata_scrollable *data = &context->data->scrollable;
u32 colour;
s32 x;
s32 y;
char *rawtext;
s32 paddingright = 24;
rawtext = menuitemScrollableGetText(context->item->param);
if (menuIsScrollableUnscrollable(context->item)) {
paddingright = 10;
}
if (rawtext) {
textWrap(context->width - paddingright, rawtext, alltext, g_CharsHandelGothicSm, g_FontHandelGothicSm);
}
inptr = alltext;
streams[1] = headingtext;
streams[0] = bodytext;
inheading = 0;
prevwaslinebreak = false;
while (*inptr != '\0') {
if (*inptr == '|') {
// Pipe character - don't append it to either string,
// but switch the active string to the heading one
inheading = 1;
} else if (*inptr == '\n') {
// Copy line breaks to both strings
*streams[0] = *inptr;
streams[0]++;
*streams[1] = *inptr;
streams[1]++;
if (prevwaslinebreak) {
// Got two line breaks in a row, so switch the active string
// back to the body one
inheading = 0;
}
prevwaslinebreak = true;
} else {
// Append to whichever string is the active one
prevwaslinebreak = false;
*streams[inheading] = *inptr;
streams[inheading]++;
}
inptr++;
}
*streams[0] = '\0';
*streams[1] = '\0';
x = context->x + 2;
y = context->y + 2;
if (context->dialog->transitionfrac < 0) {
colour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
colour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 0x7f) & 0xffffff00) | (colour & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[context->dialog->type].item_unfocused,
g_MenuWave1Colours[context->dialog->type].item_unfocused);
gdl = text0f153628(gdl);
// Heading text shadow
x = context->x + 3;
y = context->y + 3;
gdl = textRenderProjected(gdl, &x, &y, headingtext, g_CharsHandelGothicSm, g_FontHandelGothicSm,
0x000000ff, context->width - 4, context->height - 4, -data->scrolloffset, 0);
// Heading text (red)
x = context->x + 2;
y = context->y + 2;
gdl = textRenderProjected(gdl, &x, &y, headingtext, g_CharsHandelGothicSm, g_FontHandelGothicSm,
0xff4444ff, context->width - 4, context->height - 4, -data->scrolloffset, 0);
// Body text
x = menuIsScrollableUnscrollable(context->item) ? context->x + 5 : context->x + 12;
y = context->y + 2;
gdl = textRenderProjected(gdl, &x, &y, bodytext, g_CharsHandelGothicSm, g_FontHandelGothicSm,
colour, context->width - 4, context->height - 1, -data->scrolloffset, 0);
return text0f153780(gdl);
}
bool menuitemScrollableTick(struct menuitem *item, struct menudialog *dialog, struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
u32 stack;
#if VERSION >= VERSION_PAL_BETA
if ((s16)dialog->height != data->scrollable.dialogheight || data->scrollable.language != g_LanguageId) {
#else
if ((s16)dialog->height != data->scrollable.dialogheight) {
#endif
char wrapped[8000] = "";
char *rawtext;
s32 width;
s32 height;
s32 rowindex;
s32 colindex;
s16 colwidth;
s16 rowheight;
#if VERSION >= VERSION_PAL_BETA
data->scrollable.language = g_LanguageId;
#endif
dialogFindItem(dialog, item, &rowindex, &colindex);
colwidth = g_Menus[g_MpPlayerNum].cols[colindex].width;
rowheight = g_Menus[g_MpPlayerNum].rows[rowindex].height;
if (menuIsScrollableUnscrollable(item)) {
width = colwidth - 10;
} else {
width = colwidth - 24;
}
rawtext = menuitemScrollableGetText(item->param);
if (rawtext) {
textWrap(width, rawtext, wrapped, g_CharsHandelGothicSm, g_FontHandelGothicSm);
}
textMeasure(&height, &width, wrapped, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
data->scrollable.maxscrolloffset = height - rowheight + 5;
if (data->scrollable.maxscrolloffset < -10) {
data->scrollable.maxscrolloffset = -10;
}
data->scrollable.dialogheight = dialog->height;
}
if (menuIsScrollableUnscrollable(item)) {
data->scrollable.scrolloffset = 0;
} else if (tickflags & MENUTICKFLAG_ITEMISFOCUSED) {
f32 floatval;
s32 intval = 0;
if (inputs->yaxis < 0) {
floatval = -(f32)inputs->yaxis;
} else {
floatval = inputs->yaxis;
}
if (floatval > 20) {
floatval = (floatval - 20) / 5;
#if VERSION >= VERSION_PAL_BETA
floatval *= g_Vars.diffframe60freal;
#else
floatval *= g_Vars.diffframe60f;
#endif
if (inputs->yaxis < 0) {
intval = floatval;
} else {
intval = -(s32)floatval;
}
}
#if VERSION >= VERSION_PAL_BETA
intval = intval + (s32)(((f32)inputs->updownheld + (f32)inputs->updownheld) * g_Vars.diffframe60freal);
#else
intval += inputs->updownheld * 2 * g_Vars.diffframe60;
#endif
data->scrollable.scrolloffset += intval;
if (data->scrollable.scrolloffset < -10) {
data->scrollable.scrolloffset = -10;
}
if (data->scrollable.scrolloffset > data->scrollable.maxscrolloffset) {
data->scrollable.scrolloffset = data->scrollable.maxscrolloffset;
}
}
return true;
}
void menuitemScrollableInit(union menuitemdata *data)
{
data->scrollable.dialogheight = -1;
data->scrollable.scrolloffset = -10;
}
Gfx *menuitemMarqueeRender(Gfx *gdl, struct menurendercontext *context)
{
struct menuitemdata_marquee *data = &context->data->marquee;
char *text = menuResolveParam2Text(context->item);
s32 x;
s32 y;
u32 colour;
s32 i = 0;
s32 textwidth;
s32 textheight;
#if VERSION == VERSION_JPN_FINAL
char string[] = {' ', '\0', '\0'};
#else
char string[] = {' ', '\0'};
#endif
bool hasmore;
struct font *font2;
struct fontchar *font1;
s32 zero;
#if VERSION == VERSION_JPN_FINAL
font1 = g_CharsHandelGothicMd;
font2 = g_FontHandelGothicMd;
#else
font2 = g_FontHandelGothicSm;
font1 = g_CharsHandelGothicSm;
#endif
zero = 0;
#if VERSION != VERSION_JPN_FINAL
if (context->item->flags & MENUITEMFLAG_SMALLFONT) {
font2 = g_FontHandelGothicXs;
font1 = g_CharsHandelGothicXs;
}
#endif
if (!text) {
return gdl;
}
if (context->dialog->transitionfrac < 0) {
colour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
colour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
colour = (colourBlend(colour, 0, 127) & 0xffffff00) | (colour & 0xff);
}
x = context->x + context->width - data->totalmoved;
y = context->y + 2;
hasmore = true;
while (hasmore == true) {
if (text[i] == '\0') {
hasmore = false;
} else {
string[0] = text[i];
#if VERSION == VERSION_JPN_FINAL
if (string[0] < 0x80) {
string[1] = '\0';
} else {
string[1] = text[i + 1];
if ((string[0] & 0x7f) == 0x3e) {
string[0]--;
}
if ((string[0] & 0x7f) == 0x04) {
string[0]--;
}
}
#endif
textMeasure(&textheight, &textwidth, string, font1, font2, 0);
if (x + textwidth > context->x) {
hasmore = false;
} else {
hasmore = true;
x += textwidth;
i++;
#if VERSION == VERSION_JPN_FINAL
if (string[1] != '\0') {
i++;
}
#endif
}
}
textwidth += zero;
}
#if VERSION >= VERSION_NTSC_1_0
g_ScissorX1 = context->x * g_ScaleX;
g_ScissorX2 = (context->x + context->width) * g_ScaleX;
g_ScissorY1 = context->y;
g_ScissorY2 = context->y + context->height - 1;
if (g_ScissorX1 < 0) {
g_ScissorX1 = 0;
}
if (g_ScissorX2 < 0) {
g_ScissorX2 = 0;
}
if (g_ScissorY1 < 0) {
g_ScissorY1 = 0;
}
if (g_ScissorY2 < 0) {
g_ScissorY2 = 0;
}
if (g_ScissorX1 > viGetBufWidth()) {
g_ScissorX1 = viGetBufWidth();
}
if (g_ScissorX2 > viGetBufWidth()) {
g_ScissorX2 = viGetBufWidth();
}
if (g_ScissorY1 > viGetBufHeight()) {
g_ScissorY1 = viGetBufHeight();
}
if (g_ScissorY2 > viGetBufHeight()) {
g_ScissorY2 = viGetBufHeight();
}
if (g_ScissorX2 < g_ScissorX1) {
g_ScissorX2 = g_ScissorX1;
}
if (g_ScissorY2 < g_ScissorY1) {
g_ScissorY2 = g_ScissorY1;
}
gDPSetScissor(gdl++, G_SC_NON_INTERLACE, g_ScissorX1, g_ScissorY1, g_ScissorX2, g_ScissorY2);
#else
gDPSetScissor(gdl++, G_SC_NON_INTERLACE,
context->x * g_ScaleX, context->y,
(context->x + context->width) * g_ScaleX, context->y + context->height - 1);
#endif
textBackupAndResetBlends();
if (context->item->flags & MENUITEMFLAG_MARQUEE_FADEBOTHSIDES) {
#if VERSION == VERSION_JPN_FINAL
textSetHorizontalBlend(context->x, context->x + context->width, 28);
#else
textSetHorizontalBlend(context->x, context->x + context->width, 14);
#endif
} else {
textSetHorizontalBlend(context->x, context->x, 14);
}
gdl = text0f153628(gdl);
gdl = textRenderProjected(gdl, &x, &y, &text[i], font1, font2, colour,
context->width + context->x - x, context->height, 0, 0);
gdl = text0f153780(gdl);
gdl = menuApplyScissor(gdl);
textRestoreBlends();
data->viewwidth = context->width;
return gdl;
}
u32 var800711f0 = 0x00000002;
bool menuitemMarqueeTick(struct menuitem *item, union menuitemdata *data)
{
s32 i;
s32 textheight;
s32 textwidth;
struct fontchar *font1;
struct font *font2;
s32 increment;
u16 texthash = 0;
char *text = menuResolveParam2Text(item);
s32 limit;
#if VERSION == VERSION_JPN_FINAL
font1 = g_CharsHandelGothicMd;
font2 = g_FontHandelGothicMd;
#else
font2 = g_FontHandelGothicSm;
font1 = g_CharsHandelGothicSm;
if (item->flags & MENUITEMFLAG_SMALLFONT) {
font2 = g_FontHandelGothicXs;
font1 = g_CharsHandelGothicXs;
}
#endif
if (!text) {
return true;
}
i = 0;
while (text[i] != '\0') {
texthash += text[i];
i++;
}
if (data->marquee.texthash != texthash) {
data->marquee.totalmoved = 0;
data->marquee.texthash = texthash;
}
textMeasure(&textheight, &textwidth, text, font1, font2, 0);
limit = data->marquee.viewwidth + textwidth;
increment = g_Vars.diffframe60 / 2;
if (increment <= 0) {
increment = 1;
}
data->marquee.totalmoved += increment;
if (data->marquee.totalmoved > limit) {
data->marquee.totalmoved = 0;
}
return true;
}
void menuitemMarqueeInit(union menuitemdata *data)
{
data->marquee.totalmoved = 0;
data->marquee.viewwidth = 50;
data->marquee.unk06 = 0;
}
Gfx *menuitem07Render(Gfx *gdl)
{
return gdl;
}
Gfx *menuitemRankingRender(Gfx *gdl, struct menurendercontext *context)
{
struct ranking rankings[MAX_MPCHRS];
s32 numrows;
u32 textcolour;
s32 x;
s32 y;
u32 linecolour2; // ultimately unused
s32 textwidth;
s32 textheight;
s32 team = false;
u32 linecolour1;
struct menuitemdata_ranking *data = &context->data->ranking;
struct menudialog *dialog;
s32 gap;
s32 i;
if (context->item->param2 == 1) {
team = true;
numrows = mpGetTeamRankings(rankings);
} else {
numrows = mpGetPlayerRankings(rankings);
}
gdl = text0f153628(gdl);
// Gap from last item to bottom of dialog + header height
#if VERSION >= VERSION_JPN_FINAL
gap = numrows * (LINEHEIGHT - 1) - context->height + 22;
#else
gap = numrows * (LINEHEIGHT - 1) - context->height + 18;
#endif
if (gap < 0) {
gap = 0;
}
if (data->scrolloffset > gap) {
data->scrolloffset = gap;
}
if (context->dialog->transitionfrac < 0) {
textcolour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
textcolour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
textcolour = (colourBlend(textcolour, 0, 127) & 0xffffff00) | (textcolour & 0xff);
}
if (!team) {
// "Deaths"
textMeasure(&textheight, &textwidth, langGet(L_MPMENU_277), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
x = (context->x - textwidth) + 91;
y = context->y + 1;
gdl = textRenderProjected(gdl, &x, &y, langGet(L_MPMENU_277), g_CharsHandelGothicXs, g_FontHandelGothicXs,
textcolour, context->width, context->height, 0, 0);
}
// "Score"
textMeasure(&textheight, &textwidth, langGet(L_MPMENU_278), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
x = (context->x - textwidth) + 120;
y = context->y + 1;
gdl = textRenderProjected(gdl, &x, &y, langGet(L_MPMENU_278), g_CharsHandelGothicXs, g_FontHandelGothicXs,
textcolour, context->width, context->height, 0, 0);
gdl = text0f153780(gdl);
dialog = context->dialog;
if (dialog->transitionfrac < 0) {
linecolour1 = g_MenuColours[dialog->type].dialog_border1;
} else {
linecolour1 = colourBlend(
g_MenuColours[dialog->type2].dialog_border1,
g_MenuColours[dialog->type].dialog_border1,
dialog->colourweight);
}
if (dialog->dimmed) {
linecolour1 = (colourBlend(linecolour1, 0, 44) & 0xffffff00) | (linecolour1 & 0xff);
}
if (dialog->transitionfrac < 0) {
linecolour2 = g_MenuColours[dialog->type].dialog_border2;
} else {
linecolour2 = colourBlend(
g_MenuColours[dialog->type2].dialog_border2,
g_MenuColours[dialog->type].dialog_border2,
dialog->colourweight);
}
if (dialog->dimmed) {
linecolour2 = (colourBlend(linecolour2, 0, 44) & 0xffffff00) | (linecolour2 & 0xff);
}
linecolour1 = (textApplyProjectionColour(context->x, context->y + 2, -129) & 0xff) | (linecolour1 & 0xffffff00);
linecolour2 = (textApplyProjectionColour(context->x + context->width, context->y + 2, -129) & 0xff) | (linecolour2 & 0xffffff00);
// Horizontal line between header and body
#if VERSION == VERSION_JPN_FINAL
gdl = menugfxDrawFilledRect(gdl, context->x, context->y + 13, context->x + context->width, context->y + 14, linecolour1, linecolour1);
#else
gdl = menugfxDrawFilledRect(gdl, context->x, context->y + 9, context->x + context->width, context->y + 10, linecolour1, linecolour1);
#endif
gDPPipeSync(gdl++);
#if VERSION >= VERSION_NTSC_1_0
g_ScissorX1 = context->x * g_ScaleX;
g_ScissorX2 = (context->x + context->width) * g_ScaleX;
g_ScissorY1 = context->y + (VERSION == VERSION_JPN_FINAL ? 14 : 10);
g_ScissorY2 = context->y + context->height - 1;
if (g_ScissorX1 < 0) {
g_ScissorX1 = 0;
}
if (g_ScissorX2 < 0) {
g_ScissorX2 = 0;
}
if (g_ScissorY1 < 0) {
g_ScissorY1 = 0;
}
if (g_ScissorY2 < 0) {
g_ScissorY2 = 0;
}
if (g_ScissorX1 > viGetBufWidth()) {
g_ScissorX1 = viGetBufWidth();
}
if (g_ScissorX2 > viGetBufWidth()) {
g_ScissorX2 = viGetBufWidth();
}
if (g_ScissorY1 > viGetBufHeight()) {
g_ScissorY1 = viGetBufHeight();
}
if (g_ScissorY2 > viGetBufHeight()) {
g_ScissorY2 = viGetBufHeight();
}
if (g_ScissorX2 < g_ScissorX1) {
g_ScissorX2 = g_ScissorX1;
}
if (g_ScissorY2 < g_ScissorY1) {
g_ScissorY2 = g_ScissorY1;
}
gDPSetScissor(gdl++, G_SC_NON_INTERLACE, g_ScissorX1, g_ScissorY1, g_ScissorX2, g_ScissorY2);
#else
gDPSetScissor(gdl++, G_SC_NON_INTERLACE,
context->x * g_ScaleX,
context->y + 10,
(context->x + context->width) * g_ScaleX,
context->y + context->height - 1);
#endif
gdl = text0f153628(gdl);
for (i = 0; i < numrows; i++) {
struct ranking *ranking = &rankings[i];
u32 weight = 0;
char valuebuffer[8];
#if VERSION < VERSION_NTSC_1_0
u32 stack;
#endif
if (numrows >= 2) {
weight = i / (f32)(numrows - 1) * 255;
}
textcolour = colourBlend(0x008888ff, 0x00ffffff, weight);
x = context->x + 5;
#if VERSION >= VERSION_JPN_FINAL
y = context->y + i * 13 - data->scrolloffset + 18;
#else
y = context->y + i * 10 - data->scrolloffset + 14;
#endif
if (team) {
gdl = textRenderProjected(gdl, &x, &y, g_BossFile.teamnames[ranking->teamnum],
g_CharsHandelGothicSm, g_FontHandelGothicSm, textcolour, context->width, context->height, 0, 0);
} else {
gdl = textRenderProjected(gdl, &x, &y, ranking->mpchr->name,
g_CharsHandelGothicSm, g_FontHandelGothicSm, textcolour, context->width, context->height, 0, 0);
}
if (!team) {
// Deaths value (red)
textcolour = colourBlend(0xcf0000ff, 0xff4040ff, weight);
sprintf(valuebuffer, "%d\n", ranking->mpchr->numdeaths);
textMeasure(&textheight, &textwidth, valuebuffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
x = context->x - textwidth + 91;
#if VERSION >= VERSION_JPN_FINAL
y = context->y + i * 13 - data->scrolloffset + 18;
#else
y = context->y + i * 10 - data->scrolloffset + 14;
#endif
gdl = textRenderProjected(gdl, &x, &y, valuebuffer, g_CharsHandelGothicSm, g_FontHandelGothicSm,
textcolour, context->width, context->height, 0, 0);
}
// Score value (green)
textcolour = colourBlend(0x009f00ff, 0x00ff00ff, weight);
sprintf(valuebuffer, "%d\n", ranking->score);
textMeasure(&textheight, &textwidth, valuebuffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
x = context->x - textwidth + 120;
#if VERSION >= VERSION_JPN_FINAL
y = context->y + i * 13 - data->scrolloffset + 18;
#else
y = context->y + i * 10 - data->scrolloffset + 14;
#endif
gdl = textRenderProjected(gdl, &x, &y, valuebuffer, g_CharsHandelGothicSm, g_FontHandelGothicSm,
textcolour, context->width, context->height, 0, 0);
}
return text0f153780(gdl);
}
bool menuitemRankingTick(struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
f32 floatval;
s32 intval;
if (tickflags & MENUTICKFLAG_ITEMISFOCUSED) {
intval = 0;
floatval = inputs->yaxis < 0 ? -(f32)inputs->yaxis : inputs->yaxis;
if (floatval > 20) {
floatval = (floatval - 20) / 5;
#if VERSION >= VERSION_PAL_BETA
floatval *= g_Vars.diffframe60freal;
#else
floatval *= g_Vars.diffframe60f;
#endif
intval = inputs->yaxis < 0 ? (s32)floatval : -(s32)floatval;
}
#if VERSION >= VERSION_PAL_BETA
intval = intval + (s32)(((f32)inputs->updownheld + (f32)inputs->updownheld) * g_Vars.diffframe60freal);
#else
intval += inputs->updownheld * 2 * g_Vars.diffframe60;
#endif
data->ranking.scrolloffset += intval;
if (data->ranking.scrolloffset < 0) {
data->ranking.scrolloffset = 0;
}
}
return true;
}
void menuitemRankingInit(union menuitemdata *data)
{
data->ranking.scrolloffset = 0;
}
Gfx *menuitemPlayerStatsRender(Gfx *gdl, struct menurendercontext *context)
{
struct menuitemdata_dropdown *data = &context->data->dropdown;
s32 x;
s32 y;
u32 maincolour;
s32 playernum = g_MpSelectedPlayersForStats[g_MpPlayerNum];
struct mpchrconfig *mpchr;
u32 selectioncolour;
s32 textheight;
s32 textwidth;
s32 numchrs = mpGetNumChrs();
char buffer[12];
s32 i;
u32 weight;
s32 gap;
s32 ypos = 0;
mpchr = MPCHR(playernum);
gdl = text0f153628(gdl);
// Write selected player's name
weight = menuGetSinOscFrac(40) * 255;
if (context->dialog->transitionfrac < 0) {
selectioncolour = g_MenuColours[context->dialog->type].item_focused_inner;
} else {
selectioncolour = colourBlend(
g_MenuColours[context->dialog->type2].item_focused_inner,
g_MenuColours[context->dialog->type].item_focused_inner,
context->dialog->colourweight);
}
selectioncolour = colourBlend(colourBlend(0xffffffff, 0x000000ff, 127), selectioncolour, weight);
x = context->x + 2;
y = context->y + 1;
gdl = textRenderProjected(gdl, &x, &y, mpchr->name, g_CharsHandelGothicSm, g_FontHandelGothicSm,
selectioncolour, context->width, context->height, 0, 0);
// "Suicides" heading
if (context->dialog->transitionfrac < 0) {
maincolour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
maincolour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
maincolour = (colourBlend(maincolour, 0x00000000, 127) & 0xffffff00) | (maincolour & 0xff);
}
textMeasure(&textheight, &textwidth, langGet(L_MPMENU_281), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
#if VERSION >= VERSION_NTSC_1_0
x = context->x - textwidth + 121;
#else
x = context->x + 56;
#endif
y = context->y + 1;
gdl = textRenderProjected(gdl, &x, &y, langGet(L_MPMENU_281), g_CharsHandelGothicXs, g_FontHandelGothicXs,
maincolour, context->width, context->height, 0, 0);
// Num suicides
#if VERSION >= VERSION_NTSC_1_0
x = context->x - textwidth + 119;
#endif
sprintf(buffer, "%d\n", mpchr->killcounts[playernum]);
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
#if VERSION >= VERSION_NTSC_1_0
x -= textwidth;
#else
x = context->x + 95;
#endif
y = context->y + 1;
#if VERSION == VERSION_JPN_FINAL
y++;
#endif
gdl = textRenderProjected(gdl, &x, &y, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm,
0xffff00ff, context->width, context->height, 0, 0);
// Move ypos past top row
#if VERSION >= VERSION_JPN_FINAL
ypos += 14;
#else
ypos += 12;
#endif
if (mpGetNumChrs() >= 2) {
if (context->dialog->transitionfrac < 0) {
maincolour = g_MenuColours[context->dialog->type].item_unfocused;
} else {
maincolour = colourBlend(
g_MenuColours[context->dialog->type2].item_unfocused,
g_MenuColours[context->dialog->type].item_unfocused,
context->dialog->colourweight);
}
if (context->dialog->dimmed) {
maincolour = (colourBlend(maincolour, 0, 127) & 0xffffff00) | (maincolour & 0xff);
}
// "Deaths" heading
textMeasure(&textheight, &textwidth, langGet(L_MPMENU_282), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
x = context->x - textwidth + 120;
y = context->y + ypos;
gdl = textRenderProjected(gdl, &x, &y, langGet(L_MPMENU_282), g_CharsHandelGothicXs, g_FontHandelGothicXs,
maincolour, context->width, context->height, 0, 0);
// "Kills" heading
textMeasure(&textheight, &textwidth, langGet(L_MPMENU_283), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
#if VERSION >= VERSION_JPN_FINAL
x = context->x + 4;
#else
x = context->x - textwidth + 25;
#endif
y = context->y + ypos;
gdl = textRenderProjected(gdl, &x, &y, langGet(L_MPMENU_283), g_CharsHandelGothicXs, g_FontHandelGothicXs,
maincolour, context->width, context->height, 0, 0);
// Move ypos past heading row
#if VERSION >= VERSION_JPN_FINAL
ypos += 12;
#else
ypos += 7;
#endif
// Prepare scissor for table
gap = numchrs * (LINEHEIGHT - 1) - context->height + ypos;
#if VERSION >= VERSION_JPN_FINAL
gap -= 13;
#else
gap -= 10;
#endif
if (gap < 0) {
gap = 0;
}
if (data->scrolloffset > gap) {
data->scrolloffset = gap;
}
gDPPipeSync(gdl++);
#if VERSION >= VERSION_NTSC_1_0
g_ScissorX1 = context->x * g_ScaleX;
g_ScissorX2 = (context->x + context->width) * g_ScaleX;
g_ScissorY1 = context->y + ypos;
g_ScissorY2 = context->y + context->height;
if (g_ScissorX1 < 0) {
g_ScissorX1 = 0;
}
if (g_ScissorX2 < 0) {
g_ScissorX2 = 0;
}
if (g_ScissorY1 < 0) {
g_ScissorY1 = 0;
}
if (g_ScissorY2 < 0) {
g_ScissorY2 = 0;
}
if (g_ScissorX1 > viGetBufWidth()) {
g_ScissorX1 = viGetBufWidth();
}
if (g_ScissorX2 > viGetBufWidth()) {
g_ScissorX2 = viGetBufWidth();
}
if (g_ScissorY1 > viGetBufHeight()) {
g_ScissorY1 = viGetBufHeight();
}
if (g_ScissorY2 > viGetBufHeight()) {
g_ScissorY2 = viGetBufHeight();
}
if (g_ScissorX2 < g_ScissorX1) {
g_ScissorX2 = g_ScissorX1;
}
if (g_ScissorY2 < g_ScissorY1) {
g_ScissorY2 = g_ScissorY1;
}
gDPSetScissor(gdl++, G_SC_NON_INTERLACE, g_ScissorX1, g_ScissorY1, g_ScissorX2, g_ScissorY2);
#else
gDPSetScissor(gdl++, G_SC_NON_INTERLACE,
context->x * g_ScaleX,
context->y + ypos,
(context->x + context->width) * g_ScaleX,
context->y + context->height);
#endif
ypos -= data->scrolloffset;
for (i = 0; i < MAX_MPCHRS; i++) {
if (g_MpSetup.chrslots & (1 << i)) {
struct mpchrconfig *loopmpchr = MPCHR(i);
if (i != playernum) {
// Name
x = context->x + 29;
y = context->y + ypos;
gdl = textRenderProjected(gdl, &x, &y, loopmpchr->name, g_CharsHandelGothicSm, g_FontHandelGothicSm,
0x00ffffff, context->width, context->height, 0, 0);
// Num deaths
sprintf(buffer, "%d\n", loopmpchr->killcounts[playernum]);
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
x = context->x - textwidth + 120;
y = context->y + ypos;
gdl = textRenderProjected(gdl, &x, &y, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm,
0xff4040ff, context->width, context->height, 0, 0);
// Num kills
sprintf(buffer, "%d\n", mpchr->killcounts[i]);
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
#if VERSION == VERSION_JPN_FINAL
x = context->x + 4;
#else
x = context->x - textwidth + 25;
#endif
y = context->y + ypos;
gdl = textRenderProjected(gdl, &x, &y, buffer, g_CharsHandelGothicSm, g_FontHandelGothicSm,
0x00ff00ff, context->width, context->height, 0, 0);
#if VERSION == VERSION_JPN_FINAL
ypos += 13;
#else
ypos += 10;
#endif
}
}
}
}
return text0f153780(gdl);
}
bool menuitemPlayerStatsTick(struct menuitem *item, struct menudialog *dialog, struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
f32 floatval;
s32 intval;
if ((tickflags & MENUTICKFLAG_ITEMISFOCUSED) && !dialog->dimmed) {
intval = 0;
floatval = inputs->yaxis < 0 ? -(f32)inputs->yaxis : inputs->yaxis;
if (floatval > 20) {
floatval = (floatval - 20) / 5;
#if VERSION >= VERSION_PAL_BETA
floatval *= g_Vars.diffframe60freal;
#else
floatval *= g_Vars.diffframe60f;
#endif
intval = inputs->yaxis < 0 ? (s32)floatval : -(s32)floatval;
}
#if VERSION >= VERSION_PAL_BETA
intval = intval + (s32)(((f32)inputs->updownheld + (f32)inputs->updownheld) * g_Vars.diffframe60freal);
#else
intval += inputs->updownheld * 2 * g_Vars.diffframe60;
#endif
data->dropdown.scrolloffset += intval;
if (data->dropdown.scrolloffset < 0) {
data->dropdown.scrolloffset = 0;
}
}
return menuitemDropdownTick(item, dialog, inputs, tickflags, data);
}
Gfx *menuitemPlayerStatsOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2, struct menuitem *item, struct menudialog *dialog, union menuitemdata *data)
{
#if VERSION >= VERSION_NTSC_1_0
return menuitemDropdownOverlay(gdl, x + 1, y, -1, y2, item, dialog, data);
#else
return menuitemDropdownOverlay(gdl, x, y, 56, y2, item, dialog, data);
#endif
}
void menuitemPlayerStatsInit(struct menuitem *item, union menuitemdata *data)
{
data->dropdown.scrolloffset = 0;
g_MpSelectedPlayersForStats[g_MpPlayerNum] = g_MpPlayerNum;
menuitemDropdownInit(item, data);
}
Gfx *menuitemControllerRenderLine(Gfx *gdl, s32 speed, s32 x1, s32 y1, s32 x2, s32 y2)
{
speed = speed + (x1 % 4);
gSPTextureRectangle(gdl++,
x1 * 4 * g_ScaleX, y1 * 4,
x2 * 4 * g_ScaleX, y2 * 4,
G_TX_RENDERTILE,
speed * 32,
(y1 % 4) * 32,
1024 / g_ScaleX, 1024);
return gdl;
}
Gfx *menuitemControllerRenderTexture(Gfx *gdl, s32 x, s32 y, s32 texturenum, u32 alpha)
{
gDPPipeSync(gdl++);
gDPSetTexturePersp(gdl++, G_TP_NONE);
gDPSetAlphaCompare(gdl++, G_AC_NONE);
gDPSetTextureLOD(gdl++, G_TL_TILE);
gDPSetTextureConvert(gdl++, G_TC_FILT);
texSelect(&gdl, &g_TexGeneralConfigs[texturenum], 2, 0, 2, 1, NULL);
gDPSetCycleType(gdl++, G_CYC_1CYCLE);
gDPSetTextureFilter(gdl++, G_TF_POINT);
gDPSetCombineMode(gdl++, G_CC_DECALRGBA, G_CC_DECALRGBA);
gDPSetCombineLERP(gdl++,
TEXEL0, 0, ENVIRONMENT, 0,
TEXEL0, 0, ENVIRONMENT, 0,
TEXEL0, 0, ENVIRONMENT, 0,
TEXEL0, 0, ENVIRONMENT, 0);
gDPSetColor(gdl++, G_SETENVCOLOR, 0xffffff00 | alpha);
gSPTextureRectangle(gdl++,
(x << 2) * g_ScaleX, y << 2,
((x + 32) << 2) * g_ScaleX, (y + 32) << 2,
0, 16, 1008, 1024 / g_ScaleX, 0xfc00);
return gdl;
}
struct lineconfig {
s32 x1;
s32 y1;
s32 x2;
s32 y2;
};
Gfx *menuitemControllerRenderLines(Gfx *gdl, struct menurendercontext *context, s32 firstindex, s32 lastindex, s32 padx, s32 pady, u32 alpha)
{
s32 speed;
s32 i;
static struct lineconfig linecfgs[] = {
#if VERSION == VERSION_JPN_FINAL
/* 0*/ { 12, 4, -1, 31 },
/* 1*/ { 51, 4, -1, 31 },
/* 2*/ { 12, 4, 75, -1 },
/* 3*/ { 55, 40, 64, -1 },
/* 4*/ { 63, 40, -1, 16 },
/* 5*/ { 63, 16, 75, -1 },
/* 6*/ { 51, 44, 55, -1 },
/* 7*/ { 59, 44, 68, -1 },
/* 8*/ { 67, 44, -1, 28 },
/* 9*/ { 67, 28, 75, -1 },
/*10*/ { 55, 48, 72, -1 },
/*11*/ { 71, 48, -1, 40 },
/*12*/ { 71, 40, 75, -1 },
/*13*/ { 49, 52, 75, -1 },
/*14*/ { 43, 49, -1, 64 },
/*15*/ { 43, 64, 75, -1 },
/*16*/ { 30, 56, 37, -1 },
/*17*/ { 37, 56, -1, 76 },
/*18*/ { 37, 76, 75, -1 },
/*19*/ { 34, 73, 34, 88 },
/*20*/ { 34, 88, 75, -1 },
/*21*/ { 12, 51, -1, 100 },
/*22*/ { 12, 100, 75, -1 },
#elif PAL
/* 0*/ { 12, -1, -1, 10 },
/* 1*/ { 51, -1, -1, 10 },
/* 2*/ { 12, -1, 75, -1 },
/* 3*/ { 55, 19, 64, -1 },
/* 4*/ { 63, 19, -1, 7 },
/* 5*/ { 63, 7, 75, -1 },
/* 6*/ { 51, 23, 55, -1 },
/* 7*/ { 59, 23, 68, -1 },
/* 8*/ { 67, 23, -1, 15 },
/* 9*/ { 67, 15, 75, -1 },
/*10*/ { 55, 27, 72, -1 },
/*11*/ { 71, 27, -1, 23 },
/*12*/ { 71, 23, 75, -1 },
/*13*/ { 49, 31, 75, -1 },
/*14*/ { 43, 28, -1, 39 },
/*15*/ { 43, 39, 75, -1 },
/*16*/ { 30, 35, 37, -1 },
/*17*/ { 37, 35, -1, 47 },
/*18*/ { 37, 47, 75, -1 },
/*19*/ { 34, 55, 75, -1 },
/*20*/ { 12, 30, -1, 63 },
/*21*/ { 12, 63, 75, -1 },
#else
/* 0*/ { 12, 10, -1, 3 },
/* 1*/ { 51, 10, -1, 3 },
/* 2*/ { 12, 3, 75, -1 },
/* 3*/ { 55, 19, 64, -1 },
/* 4*/ { 63, 19, -1, 10 },
/* 5*/ { 63, 10, 75, -1 },
/* 6*/ { 51, 23, 55, -1 },
/* 7*/ { 59, 23, 68, -1 },
/* 8*/ { 67, 23, -1, 17 },
/* 9*/ { 67, 17, 75, -1 },
/*10*/ { 55, 27, 72, -1 },
/*11*/ { 71, 27, -1, 24 },
/*12*/ { 71, 24, 75, -1 },
/*13*/ { 49, 31, 75, -1 },
/*14*/ { 43, 28, -1, 38 },
/*15*/ { 43, 38, 75, -1 },
/*16*/ { 30, 35, 37, -1 },
/*17*/ { 37, 35, -1, 45 },
/*18*/ { 37, 45, 75, -1 },
/*19*/ { 34, 52, 75, -1 },
/*20*/ { 12, 30, -1, 59 },
/*21*/ { 12, 59, 75, -1 },
#endif
};
speed = (s32)(-g_20SecIntervalFrac * 4.0f * 50.0f) % 4;
alpha = alpha >> 1;
gDPPipeSync(gdl++);
gDPSetTexturePersp(gdl++, G_TP_NONE);
gDPSetAlphaCompare(gdl++, G_AC_NONE);
gDPSetTextureLOD(gdl++, G_TL_TILE);
gDPSetTextureConvert(gdl++, G_TC_FILT);
texSelect(&gdl, &g_TexGeneralConfigs[55], 2, 0, 2, 1, NULL);
gDPSetCycleType(gdl++, G_CYC_1CYCLE);
gDPSetTextureFilter(gdl++, G_TF_POINT);
gDPSetCombineLERP(gdl++,
TEXEL0, 0, ENVIRONMENT, 0, TEXEL0, 0, ENVIRONMENT, 0,
TEXEL0, 0, ENVIRONMENT, 0, TEXEL0, 0, ENVIRONMENT, 0);
gDPSetEnvColorViaWord(gdl++, 0xffffff00 | alpha);
for (i = firstindex; i <= lastindex; i++) {
s32 thisspeed = speed;
s32 tmp;
if (linecfgs[i].y2 == -1) {
linecfgs[i].y2 = linecfgs[i].y1 + 1;
}
if (linecfgs[i].x2 == -1) {
linecfgs[i].x2 = linecfgs[i].x1 + 1;
}
if (linecfgs[i].x2 == linecfgs[i].x1) {
linecfgs[i].x2 = linecfgs[i].x1 + 1;
}
if (linecfgs[i].y2 == linecfgs[i].y1) {
linecfgs[i].y2 = linecfgs[i].y1 + 1;
}
if (linecfgs[i].x2 < linecfgs[i].x1) {
tmp = linecfgs[i].x1;
linecfgs[i].x1 = linecfgs[i].x2;
linecfgs[i].x2 = tmp;
}
if (linecfgs[i].y2 < linecfgs[i].y1) {
tmp = linecfgs[i].y1;
linecfgs[i].y1 = linecfgs[i].y2;
linecfgs[i].y2 = tmp;
}
switch (i) {
case 14:
case 17:
#if VERSION >= VERSION_JPN_FINAL
case 19:
case 21:
#else
case 20:
#endif
thisspeed = -thisspeed;
break;
}
gdl = menuitemControllerRenderLine(gdl, thisspeed,
linecfgs[i].x1 + context->x + padx, linecfgs[i].y1 + context->y + pady,
linecfgs[i].x2 + context->x + padx, linecfgs[i].y2 + context->y + pady);
}
return gdl;
}
// These are the action names, like "AIM", and "LOOK UP"
u16 var80071354[][9] = {
/*0*/ { L_MPWEAPONS_194, L_MPWEAPONS_196, L_MPWEAPONS_202, L_MPWEAPONS_197, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_206, L_MPWEAPONS_205, L_MPWEAPONS_210 },
/*1*/ { L_MPWEAPONS_194, L_MPWEAPONS_200, L_MPWEAPONS_202, L_MPWEAPONS_201, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_207, L_MPWEAPONS_205, L_MPWEAPONS_210 },
/*2*/ { L_MPWEAPONS_203, L_MPWEAPONS_196, L_MPWEAPONS_202, L_MPWEAPONS_197, L_MPWEAPONS_205, L_MPWEAPONS_204, L_MPWEAPONS_206, L_MPWEAPONS_194, L_MPWEAPONS_210 },
/*3*/ { L_MPWEAPONS_203, L_MPWEAPONS_200, L_MPWEAPONS_202, L_MPWEAPONS_201, L_MPWEAPONS_205, L_MPWEAPONS_204, L_MPWEAPONS_207, L_MPWEAPONS_194, L_MPWEAPONS_210 },
/*4*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_211, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_206, L_MPWEAPONS_205, L_OPTIONS_003 },
/*5*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_211, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_207, L_MPWEAPONS_205, L_OPTIONS_003 },
/*6*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_211, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_206, L_MPWEAPONS_194, L_OPTIONS_003 },
/*7*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_211, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_207, L_MPWEAPONS_194, L_OPTIONS_003 },
/*8*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_212, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_209, L_MPWEAPONS_194, L_OPTIONS_003 },
/*9*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_212, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_208, L_MPWEAPONS_194, L_OPTIONS_003 },
/*10*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_212, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_209, L_MPWEAPONS_205, L_OPTIONS_003 },
/*11*/ { L_OPTIONS_003, L_OPTIONS_003, L_MPWEAPONS_212, L_OPTIONS_003, L_MPWEAPONS_203, L_MPWEAPONS_204, L_MPWEAPONS_208, L_MPWEAPONS_205, L_OPTIONS_003 },
};
u16 menuitemControllerGetButtonAction(s32 mode, s32 buttonnum)
{
u32 textid = var80071354[mode][buttonnum];
if (textid == L_MPWEAPONS_194 // "AIM"
&& optionsGetAimControl(g_Menus[g_MpPlayerNum].main.mpindex) == AIMCONTROL_TOGGLE) {
textid = L_MPWEAPONS_195; // "AIM TOGGLE"
}
if (textid == L_MPWEAPONS_196 // "LOOK UP"
&& optionsGetForwardPitch(g_Menus[g_MpPlayerNum].main.mpindex) == false) {
textid = L_MPWEAPONS_198; // "LOOK DOWN"
}
if (textid == L_MPWEAPONS_197 // "LOOK DOWN"
&& optionsGetForwardPitch(g_Menus[g_MpPlayerNum].main.mpindex) == false) {
textid = L_MPWEAPONS_199; // "LOOK UP"
}
return textid;
}
/**
* Renders the button labels and actions for the control style dialog.
*
* When switching modes (control styles), a fade effect is used to fade in the
* action names, but only if they differ from the previous mode.
*
* Note that the valuecolour argument is mostly unused - only the alpha channel
* is used because the rest is bitwise or'ed to white.
*/
Gfx *menuitemControllerRenderText(Gfx *gdl, s32 curmode, struct menurendercontext *context, s32 padx, s32 pady, u32 valuecolour, u32 labelcolour, s8 prevmode)
{
s32 rx;
s32 ry;
u16 textnum;
u32 colour;
u16 labels[] = {
/*0*/ L_MPWEAPONS_185, // "L/R BUTTONS:"
/*1*/ L_MPWEAPONS_186, // "UP C BUTTON:"
/*2*/ L_MPWEAPONS_187, // "LEFT/RIGHT C BUTTONS:"
/*3*/ L_MPWEAPONS_188, // "DOWN C BUTTON:"
/*4*/ L_MPWEAPONS_189, // "A BUTTON:"
/*5*/ L_MPWEAPONS_190, // "B BUTTON:"
/*6*/ L_MPWEAPONS_191, // "CONTROL STICK:"
/*7*/ L_MPWEAPONS_192, // "Z BUTTON:"
/*8*/ L_MPWEAPONS_193, // "+ CONTROL PAD:"
};
s32 i;
gdl = text0f153628(gdl);
for (i = 0; i < ARRAYCOUNT(labels); i++) {
#if VERSION == VERSION_JPN_FINAL
ry = i * 12 + context->y + pady + 1;
#elif VERSION >= VERSION_PAL_BETA
ry = i * 8 + context->y + pady - 4;
#elif VERSION >= VERSION_NTSC_1_0
ry = i * 7 + context->y + pady;
#endif
// For the 2.x styles, only labels 4-7 are shown
if ((curmode < CONTROLMODE_21 || curmode == CONTROLMODE_PC) || (i >= 4 && i <= 7)) {
// Rendering a label such as "L/R BUTTONS:"
rx = context->x + padx + 76;
#if VERSION < VERSION_NTSC_1_0
ry = i * 7 + context->y + pady;
#endif
gdl = textRenderProjected(gdl, &rx, &ry, langGet(labels[i]),
g_CharsHandelGothicXs, g_FontHandelGothicXs, labelcolour, viGetWidth(), viGetHeight(), 0, 0);
}
textnum = menuitemControllerGetButtonAction(curmode, i);
colour = valuecolour;
// If there's a prevmode, get the text ID that was in this position for
// prevmode. If it's the same text as curmode, don't fade the text.
if (prevmode >= 0) {
// I don't see how curmode can ever be > CONTROLMODE_24. Perhaps
// during development the second player in the 2.x styles had to
// choose their control style separately to player 1, in which case
// there would have been 2.5, 2.6, 2.7 and 2.8 for player 2.
if (curmode > CONTROLMODE_24 && curmode != CONTROLMODE_PC) {
if (textnum == menuitemControllerGetButtonAction(prevmode + 4, i)) {
colour = labelcolour;
}
} else {
if (textnum == menuitemControllerGetButtonAction(prevmode, i)) {
colour = labelcolour;
}
}
}
if (curmode >= CONTROLMODE_21 && curmode < CONTROLMODE_PC && i == 2) {
// Rendering a "CONTROLLER 1" or "CONTROLLER 2" heading
rx = context->x + padx + 63;
colour = labelcolour;
} else {
// Rendering a value such as "WALK/TURN"
// Make it white but preserve alpha
colour |= 0xffffff00;
}
gdl = textRenderProjected(gdl, &rx, &ry, langGet(textnum),
g_CharsHandelGothicXs, g_FontHandelGothicXs, colour, viGetWidth(), viGetHeight(), 0, 0);
}
return text0f153780(gdl);
}
Gfx *menuitemControllerRenderPad(Gfx *gdl, struct menurendercontext *context, s32 padx, s32 pady, s32 curmode, u32 alpha, u32 colour1, u32 colour2, s8 prevmode)
{
s32 rx = context->x + padx;
s32 ry = context->y + pady + (VERSION == VERSION_JPN_FINAL ? 25 : 4);
// The controller graphic is split into 4 textures
gdl = menuitemControllerRenderTexture(gdl, rx, ry, 0x33, alpha);
gdl = menuitemControllerRenderTexture(gdl, rx + 32, ry, 0x34, alpha);
gdl = menuitemControllerRenderTexture(gdl, rx, ry + 32, 0x35, alpha);
gdl = menuitemControllerRenderTexture(gdl, rx + 32, ry + 32, 0x36, alpha);
#if VERSION >= VERSION_JPN_FINAL
if (curmode >= CONTROLMODE_21 && curmode < CONTROLMODE_PC) {
gdl = menuitemControllerRenderLines(gdl, context, 13, 20, padx, pady, alpha);
} else {
gdl = menuitemControllerRenderLines(gdl, context, 0, 22, padx, pady, alpha);
}
#else
if (curmode >= CONTROLMODE_21 && curmode < CONTROLMODE_PC) {
gdl = menuitemControllerRenderLines(gdl, context, 13, 19, padx, pady, alpha);
} else {
gdl = menuitemControllerRenderLines(gdl, context, 0, 21, padx, pady, alpha);
}
#endif
return menuitemControllerRenderText(gdl, curmode, context, padx, pady, colour1, colour2, prevmode);
}
Gfx *menuitemControllerRender(Gfx *gdl, struct menurendercontext *context)
{
struct menuitemdata_controller *data = &context->data->controller;
u32 colour;
u32 textcolour;
s32 x;
s32 y;
char text[32];
struct menudialog *dialog = context->dialog;
u32 contalpha;
u32 textalpha;
textBackupAndResetBlends();
// If changing modes within the same group (eg. within 1.x)
if (data->curmode != (g_Menus[g_MpPlayerNum].main.controlmode & 0xff)) {
data->prevmode = data->curmode;
data->curmode = g_Menus[g_MpPlayerNum].main.controlmode;
data->textfadetimer = 0;
}
// If changing control group (eg. 1.4 -> 2.1)
if (data->controlgroup == 1) {
if (g_Menus[g_MpPlayerNum].main.controlmode < CONTROLMODE_21 || g_Menus[g_MpPlayerNum].main.controlmode == CONTROLMODE_PC) {
data->controlgroup = 2;
data->contfadetimer = 0;
data->prevmode = -1;
}
} else {
if (g_Menus[g_MpPlayerNum].main.controlmode >= CONTROLMODE_21 && g_Menus[g_MpPlayerNum].main.controlmode != CONTROLMODE_PC) {
data->controlgroup = 1;
data->contfadetimer = 0;
data->prevmode = -1;
}
}
// If text is fading in (eg. recently changed 1.1 -> 1.2)
if (data->textfadetimer < 255) {
s32 value = data->textfadetimer + g_Vars.diffframe240 * 2;
if (value > 255) {
value = 255;
}
data->textfadetimer = value;
}
// If controller is fading in (eg. recently changed 1.4 -> 2.1)
if (data->contfadetimer < 255) {
s32 value = data->contfadetimer + g_Vars.diffframe240 * 2;
if (value > 255) {
value = 255;
}
data->contfadetimer = value;
}
textalpha = data->textfadetimer;
contalpha = data->contfadetimer;
gdl = text0f153628(gdl);
if (dialog->transitionfrac < 0) {
colour = g_MenuColours[dialog->type].item_unfocused;
} else {
colour = colourBlend(
g_MenuColours[dialog->type2].item_unfocused,
g_MenuColours[dialog->type].item_unfocused,
dialog->colourweight);
}
if (dialog->dimmed) {
colour = (colourBlend(colour, 0, 44) & 0xffffff00) | (colour & 0xff);
}
textSetWaveColours(
g_MenuWave2Colours[dialog->type].item_unfocused,
g_MenuWave1Colours[dialog->type].item_unfocused);
if (g_Menus[g_MpPlayerNum].main.controlmode == CONTROLMODE_PC) {
sprintf(text, langGet(L_MPWEAPONS_213), // "Control Style %s %s"
"Ext",
langGet(L_MPWEAPONS_215)); // "(Two-Handed)"
} else if (g_Menus[g_MpPlayerNum].main.controlmode >= CONTROLMODE_21) {
sprintf(text, langGet(L_MPWEAPONS_213), // "Control Style %s %s"
langGet(g_ControlStyleOptions[g_Menus[g_MpPlayerNum].main.controlmode]),
langGet(L_MPWEAPONS_215)); // "(Two-Handed)"
} else {
sprintf(text, langGet(L_MPWEAPONS_213), // "Control Style %s %s"
langGet(g_ControlStyleOptions[g_Menus[g_MpPlayerNum].main.controlmode]),
langGet(L_MPWEAPONS_214)); // "(One-Handed)"
}
x = context->x + 2;
y = context->y + 2;
gdl = textRenderProjected(gdl, &x, &y, text,
g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, viGetWidth(), viGetHeight(), 0, 0);
gdl = text0f153780(gdl);
textcolour = colourBlend(colour, colour & 0xffffff00, textalpha);
colour = colourBlend(colour, colour & 0xffffff00, contalpha);
if (g_Menus[g_MpPlayerNum].main.controlmode == CONTROLMODE_PC) {
x = context->x;
y = context->y + 92;
#if VERSION == VERSION_JPN_FINAL
y += 34;
#endif
gdl = text0f153628(gdl);
gdl = textRenderProjected(gdl, &x, &y,
"PC Port control scheme.\n"
"Only change to another control scheme\n"
"if you want to use original N64 controls.\n\n"
"See Extended Key Bindings for more info.",
g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, viGetWidth(), viGetHeight(), 0, 0);
gdl = text0f153780(gdl);
} else if (g_Menus[g_MpPlayerNum].main.controlmode >= CONTROLMODE_21) {
gdl = menuitemControllerRenderPad(gdl, context, 0, VERSION == VERSION_JPN_FINAL ? -4 : 12,
g_Menus[g_MpPlayerNum].main.controlmode,
contalpha, textcolour, colour, data->prevmode);
gdl = menuitemControllerRenderPad(gdl, context, 0, VERSION == VERSION_JPN_FINAL ? 74 : 80,
g_Menus[g_MpPlayerNum].main.controlmode + 4,
contalpha, textcolour, colour, data->prevmode);
} else {
gdl = menuitemControllerRenderPad(gdl, context, 0, PAL ? 19 : 15,
g_Menus[g_MpPlayerNum].main.controlmode,
contalpha, textcolour, colour, data->prevmode);
x = context->x;
y = context->y + 92;
#if VERSION == VERSION_JPN_FINAL
y += 34;
#endif
gdl = text0f153628(gdl);
gdl = textRenderProjected(gdl, &x, &y, langGet(L_MPWEAPONS_216), // "Hold weapon button for ..."
g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, viGetWidth(), viGetHeight(), 0, 0);
gdl = text0f153780(gdl);
}
textRestoreBlends();
return gdl;
}
void menuitemControllerInit(union menuitemdata *data)
{
data->controller.textfadetimer = 0;
data->controller.contfadetimer = 0;
data->controller.curmode = 255;
data->controller.controlgroup = 255;
data->controller.prevmode = -1;
}
Gfx *menuitemRender(Gfx *gdl, struct menurendercontext *context)
{
switch (context->item->type) {
case MENUITEMTYPE_LIST: return menuitemListRender(gdl, context);
case MENUITEMTYPE_SELECTABLE: return menuitemSelectableRender(gdl, context);
case MENUITEMTYPE_SLIDER: return menuitemSliderRender(gdl, context);
case MENUITEMTYPE_CHECKBOX: return menuitemCheckboxRender(gdl, context);
case MENUITEMTYPE_SCROLLABLE: return menuitemScrollableRender(gdl, context);
case MENUITEMTYPE_MARQUEE: return menuitemMarqueeRender(gdl, context);
case MENUITEMTYPE_LABEL: return menuitemLabelRender(gdl, context);
case MENUITEMTYPE_METER: return menuitemMeterRender(gdl, context);
case MENUITEMTYPE_SEPARATOR: return menuitemSeparatorRender(gdl, context);
case MENUITEMTYPE_OBJECTIVES: return menuitemObjectivesRender(gdl, context);
case MENUITEMTYPE_07: return menuitem07Render(gdl);
case MENUITEMTYPE_DROPDOWN: return menuitemDropdownRender(gdl, context);
case MENUITEMTYPE_KEYBOARD: return menuitemKeyboardRender(gdl, context);
case MENUITEMTYPE_RANKING: return menuitemRankingRender(gdl, context);
case MENUITEMTYPE_PLAYERSTATS: return menuitemPlayerStatsRender(gdl, context);
case MENUITEMTYPE_CAROUSEL: return menuitemCarouselRender(gdl, context);
case MENUITEMTYPE_MODEL: return menuitemModelRender(gdl, context);
case MENUITEMTYPE_CONTROLLER: return menuitemControllerRender(gdl, context);
#ifndef PLATFORM_N64
case MENUITEMTYPE_COLORBOX: return menuitemColorBoxRender(gdl, context);
#endif
}
return gdl;
}
/**
* Return true if default up/down/left/right/back behaviour should be used.
*/
bool menuitemTick(struct menuitem *item, struct menudialog *dialog, struct menuinputs *inputs, u32 tickflags, union menuitemdata *data)
{
switch (item->type) {
case MENUITEMTYPE_LIST: return menuitemListTick(item, inputs, tickflags, data);
case MENUITEMTYPE_SELECTABLE: return menuitemSelectableTick(item, inputs, tickflags);
case MENUITEMTYPE_SLIDER: return menuitemSliderTick(item, dialog, inputs, tickflags, data);
case MENUITEMTYPE_CHECKBOX: return menuitemCheckboxTick(item, inputs, tickflags);
case MENUITEMTYPE_SCROLLABLE: return menuitemScrollableTick(item, dialog, inputs, tickflags, data);
case MENUITEMTYPE_MARQUEE: return menuitemMarqueeTick(item, data);
case MENUITEMTYPE_RANKING: return menuitemRankingTick(inputs, tickflags, data);
case MENUITEMTYPE_DROPDOWN: return menuitemDropdownTick(item, dialog, inputs, tickflags, data);
case MENUITEMTYPE_KEYBOARD: return menuitemKeyboardTick(item, inputs, tickflags, data);
case MENUITEMTYPE_CAROUSEL: return menuitemCarouselTick(item, inputs, tickflags);
case MENUITEMTYPE_PLAYERSTATS: return menuitemPlayerStatsTick(item, dialog, inputs, tickflags, data);
}
return true;
}
void menuitemInit(struct menuitem *item, union menuitemdata *data)
{
switch (item->type) {
case MENUITEMTYPE_LIST:
case MENUITEMTYPE_DROPDOWN:
menuitemDropdownInit(item, data);
break;
case MENUITEMTYPE_SCROLLABLE:
menuitemScrollableInit(data);
break;
case MENUITEMTYPE_MARQUEE:
menuitemMarqueeInit(data);
break;
case MENUITEMTYPE_RANKING:
menuitemRankingInit(data);
break;
case MENUITEMTYPE_SLIDER:
menuitemSliderInit(data);
break;
case MENUITEMTYPE_PLAYERSTATS:
menuitemPlayerStatsInit(item, data);
break;
case MENUITEMTYPE_KEYBOARD:
menuitemKeyboardInit(item, data);
break;
case MENUITEMTYPE_CONTROLLER:
menuitemControllerInit(data);
break;
}
}
Gfx *menuitemOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2, struct menuitem *item, struct menudialog *dialog, union menuitemdata *data)
{
switch (item->type) {
case MENUITEMTYPE_LIST: return menuitemListOverlay(gdl, x, y, x2, y2);
case MENUITEMTYPE_DROPDOWN: return menuitemDropdownOverlay(gdl, x, y, x2, y2, item, dialog, data);
case MENUITEMTYPE_PLAYERSTATS: return menuitemPlayerStatsOverlay(gdl, x, y, x2, y2, item, dialog, data);
}
return gdl;
}
#ifndef PLATFORM_N64
s32 menuitemGetTop(struct menuitem *item, struct menudialog *dialog)
{
struct menu *menu = &g_Menus[g_MpPlayerNum];
s32 dtop = dialog->y + LINEHEIGHT + 1;
for (s32 i = 0; i < dialog->numcols; ++i) {
const s32 colindex = i + dialog->colstart;
for (s32 j = 0; j < menu->cols[colindex].numrows; ++j) {
const s32 rowindex = j + menu->cols[colindex].rowstart;
struct menuitem *pitem = &dialog->definition->items[menu->rows[rowindex].itemindex];
if (pitem == item) {
return dtop;
}
dtop += menu->rows[rowindex].height;
}
}
return dtop;
}
#endif