sm64ex-coop/data/dynos_opt_render.cpp

310 lines
14 KiB
C++

#include "dynos.cpp.h"
extern "C" {
#include "course_table.h"
#include "game/game_init.h"
#include "game/ingame_menu.h"
#include "game/segment2.h"
#include "pc/controller/controller_api.h"
#include "gfx_dimensions.h"
}
extern s32 sBindingState;
#define DYNOS_TEXT_DYNOS_MENU { "DYNOS MENU", NULL }
#define DYNOS_TEXT_A { "([A]) >", NULL }
#define DYNOS_TEXT_OPEN_LEFT { "[Z] DynOS", NULL }
#define DYNOS_TEXT_CLOSE_LEFT { "[Z] Return", NULL }
#define DYNOS_TEXT_OPTIONS_MENU { "OPTIONS", NULL }
#define DYNOS_TEXT_DISABLED { "Disabled", NULL }
#define DYNOS_TEXT_ENABLED { "Enabled", NULL }
#define DYNOS_TEXT_NONE { "NONE", NULL }
#define DYNOS_TEXT_DOT_DOT_DOT { "...", NULL }
#define DYNOS_TEXT_OPEN_RIGHT { "[R] Options", NULL }
#define DYNOS_TEXT_CLOSE_RIGHT { "[R] Return", NULL }
static void RenderString(const u8 *aStr64, s32 aX, s32 aY) {
create_dl_translation_matrix(MENU_MTX_PUSH, aX, aY, 0);
for (; *aStr64 != DIALOG_CHAR_TERMINATOR; ++aStr64) {
if (*aStr64 != DIALOG_CHAR_SPACE) {
void **fontLUT = (void **) segmented_to_virtual(main_font_lut);
void *packedTexture = segmented_to_virtual(fontLUT[*aStr64]);
gDPPipeSync(gDisplayListHead++);
gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(packedTexture));
gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings);
}
create_dl_translation_matrix(MENU_MTX_NOPUSH, DynOS_String_WidthChar64(*aStr64), 0, 0);
}
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
}
static void PrintString(const Label& aLabel, s32 aX, s32 aY, u32 aFrontColorRGBA, u32 aBackColorRGBA, bool aAlignLeft) {
const u8 *_Str64 = (aLabel.second ? aLabel.second : DynOS_String_Convert(aLabel.first.begin(), false));
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
if ((aBackColorRGBA & 0xFF) != 0) {
gDPSetEnvColor(gDisplayListHead++, ((aBackColorRGBA >> 24) & 0xFF), ((aBackColorRGBA >> 16) & 0xFF), ((aBackColorRGBA >> 8) & 0xFF), ((aBackColorRGBA >> 0) & 0xFF));
if (aAlignLeft) {
RenderString(_Str64, GFX_DIMENSIONS_FROM_LEFT_EDGE(aX) + 1, aY - 1);
} else {
RenderString(_Str64, GFX_DIMENSIONS_FROM_RIGHT_EDGE(aX + DynOS_String_Width(_Str64) - 1), aY - 1);
}
}
if ((aFrontColorRGBA & 0xFF) != 0) {
gDPSetEnvColor(gDisplayListHead++, ((aFrontColorRGBA >> 24) & 0xFF), ((aFrontColorRGBA >> 16) & 0xFF), ((aFrontColorRGBA >> 8) & 0xFF), ((aFrontColorRGBA >> 0) & 0xFF));
if (aAlignLeft) {
RenderString(_Str64, GFX_DIMENSIONS_FROM_LEFT_EDGE(aX), aY);
} else {
RenderString(_Str64, GFX_DIMENSIONS_FROM_RIGHT_EDGE(aX + DynOS_String_Width(_Str64)), aY);
}
}
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
}
static void PrintBox(s32 aX, s32 aY, s32 aWidth, s32 aHeight, u32 aColorRGBA, bool aAlignLeft) {
if ((aColorRGBA & 0xFF) != 0) {
Mtx *_Matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
if (!_Matrix) return;
if (aAlignLeft) {
create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(aX), aY + aHeight, 0);
} else {
create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_RIGHT_EDGE(aX + aWidth), aY + aHeight, 0);
}
guScale(_Matrix, (f32) aWidth / 130.f, (f32) aHeight / 80.f, 1.f);
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(_Matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
gDPSetEnvColor(gDisplayListHead++, ((aColorRGBA >> 24) & 0xFF), ((aColorRGBA >> 16) & 0xFF), ((aColorRGBA >> 8) & 0xFF), ((aColorRGBA >> 0) & 0xFF));
gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
}
}
static const char *IntToString(const char *fmt, s32 x) {
static char sBuffer[16];
snprintf(sBuffer, 16, fmt, x);
return sBuffer;
}
#define get_label(opt) (opt->mLabel)
#define get_title(opt) (opt->mTitle)
#define get_choice(opt) (opt->mChoice.mChoices[*opt->mChoice.mIndex])
#define get_dec_number(n) { "", DynOS_String_Convert(IntToString("%d", n), false) }
#define get_hex_number(n) { "", DynOS_String_Convert(IntToString("%04X", n), false) }
#define get_level(opt) { "", DynOS_Level_GetName(DynOS_Level_GetList()[*opt->mChoice.mIndex], true, true) }
#define get_star(opt) { "", DynOS_Level_GetActName(DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")], *opt->mChoice.mIndex + 1, true, true) }
#define get_param(opt) { DynOS_Warp_GetParamName(DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")], *opt->mChoice.mIndex), NULL }
static s32 GetCurrentOptionCount(DynosOption *aCurrentOpt) {
s32 _Count = 0;
while (aCurrentOpt->mPrev) { aCurrentOpt = aCurrentOpt->mPrev; }
while (aCurrentOpt) { aCurrentOpt = aCurrentOpt->mNext; _Count++; }
return _Count;
}
static s32 GetCurrentOptionIndex(DynosOption *aCurrentOpt) {
s32 _Index = 0;
while (aCurrentOpt->mPrev) { aCurrentOpt = aCurrentOpt->mPrev; _Index++; }
return _Index;
}
#define PREV(opt) (opt == NULL ? NULL : opt->mPrev)
#define NEXT(opt) (opt == NULL ? NULL : opt->mNext)
static DynosOption **GetCurrentOptions(DynosOption *aCurrentOpt) {
static DynosOption *sOptionList[13];
sOptionList[6] = aCurrentOpt;
sOptionList[5] = PREV(sOptionList[6]);
sOptionList[4] = PREV(sOptionList[5]);
sOptionList[3] = PREV(sOptionList[4]);
sOptionList[2] = PREV(sOptionList[3]);
sOptionList[1] = PREV(sOptionList[2]);
sOptionList[0] = PREV(sOptionList[1]);
sOptionList[7] = NEXT(sOptionList[6]);
sOptionList[8] = NEXT(sOptionList[7]);
sOptionList[9] = NEXT(sOptionList[8]);
sOptionList[10] = NEXT(sOptionList[9]);
sOptionList[11] = NEXT(sOptionList[10]);
sOptionList[12] = NEXT(sOptionList[11]);
s32 _StartIndex = 12, _EndIndex = 0;
for (s32 i = 0; i != 13; ++i) {
if (sOptionList[i] != NULL) {
_StartIndex = MIN(_StartIndex, i);
_EndIndex = MAX(_EndIndex, i);
}
}
if (_EndIndex - _StartIndex < 7) {
return &sOptionList[_StartIndex];
}
if (_EndIndex <= 9) {
return &sOptionList[_EndIndex - 6];
}
if (_StartIndex >= 3) {
return &sOptionList[_StartIndex];
}
return &sOptionList[3];
}
#undef PREV
#undef NEXT
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_BLACK 0x000000FF
#define COLOR_GRAY 0xA0A0A0FF
#define COLOR_DARK_GRAY 0x808080FF
#define COLOR_SELECT 0x80E0FFFF
#define COLOR_SELECT_BOX 0x00FFFF20
#define COLOR_ENABLED 0x20E020FF
#define COLOR_DISABLED 0xFF2020FF
#define OFFSET_FROM_LEFT_EDGE (20.f * sqr(GFX_DIMENSIONS_ASPECT_RATIO))
#define OFFSET_FROM_RIGHT_EDGE (20.f * sqr(GFX_DIMENSIONS_ASPECT_RATIO))
#define SCROLL_BAR_SIZE ((s32) (45.f * GFX_DIMENSIONS_ASPECT_RATIO))
static void DynOS_Opt_DrawOption(DynosOption *aOpt, DynosOption *aCurrentOpt, s32 aY) {
if (aOpt == NULL) {
return;
}
// Selected box
if (aOpt == aCurrentOpt) {
u8 _Alpha = (u8) ((coss(gGlobalTimer * 0x800) + 1.f) * 0x20);
PrintBox(OFFSET_FROM_LEFT_EDGE - 4, aY - 2, GFX_DIMENSIONS_FROM_RIGHT_EDGE(OFFSET_FROM_RIGHT_EDGE) - GFX_DIMENSIONS_FROM_LEFT_EDGE(OFFSET_FROM_LEFT_EDGE) + 8, 20, COLOR_SELECT_BOX + _Alpha, 1);
}
// Label
if (aOpt == aCurrentOpt) {
PrintString(get_label(aOpt), OFFSET_FROM_LEFT_EDGE, aY, COLOR_SELECT, COLOR_BLACK, 1);
} else {
PrintString(get_label(aOpt), OFFSET_FROM_LEFT_EDGE, aY, COLOR_WHITE, COLOR_BLACK, 1);
}
// Values
switch (aOpt->mType) {
case DOPT_TOGGLE: {
if (*aOpt->mToggle.mTog) {
PrintString(DYNOS_TEXT_ENABLED, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_ENABLED, COLOR_BLACK, 0);
} else {
PrintString(DYNOS_TEXT_DISABLED, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_DISABLED, COLOR_BLACK, 0);
}
} break;
case DOPT_CHOICE: {
PrintString(get_choice(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} break;
case DOPT_CHOICELEVEL: {
PrintString(get_level(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} break;
case DOPT_CHOICEAREA: {
s32 _Level = DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")];
s32 _Area = *aOpt->mChoice.mIndex + 1;
const u8 *_Name = DynOS_Level_GetAreaName(_Level, _Area, true);
if (DynOS_Level_GetWarpEntry(_Level, _Area)) {
PrintString({ "", _Name }, OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} else {
PrintString({ "", _Name }, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_GRAY, COLOR_BLACK, 0);
}
} break;
case DOPT_CHOICESTAR: {
s32 _Course = DynOS_Level_GetCourse(DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")]);
if (_Course >= COURSE_MIN && _Course <= COURSE_STAGES_MAX) {
PrintString(get_star(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
}
} break;
case DOPT_CHOICEPARAM: {
PrintString(get_param(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} break;
case DOPT_SCROLL: {
s32 _Width = (s32) (SCROLL_BAR_SIZE * (f32) (*aOpt->mScroll.mValue - aOpt->mScroll.mMin) / (f32) (aOpt->mScroll.mMax - aOpt->mScroll.mMin));
PrintString(get_dec_number(*aOpt->mScroll.mValue), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
PrintBox(OFFSET_FROM_RIGHT_EDGE + 28, aY + 4, SCROLL_BAR_SIZE + 2, 8, COLOR_DARK_GRAY, 0);
PrintBox(OFFSET_FROM_RIGHT_EDGE + 29 + SCROLL_BAR_SIZE - _Width, aY + 5, _Width, 6, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, 0);
} break;
case DOPT_BIND: {
for (s32 i = 0; i != 3; ++i) {
u32 _Bind = aOpt->mBind.mBinds[i];
if (aOpt == aCurrentOpt && i == aOpt->mBind.mIndex) {
if (sBindingState != 0) {
PrintString(DYNOS_TEXT_DOT_DOT_DOT, OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_SELECT, COLOR_BLACK, 0);
} else if (_Bind == VK_INVALID) {
PrintString(DYNOS_TEXT_NONE, OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_SELECT, COLOR_BLACK, 0);
} else {
PrintString(get_hex_number(_Bind), OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_SELECT, COLOR_BLACK, 0);
}
} else {
if (_Bind == VK_INVALID) {
PrintString(DYNOS_TEXT_NONE, OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_GRAY, COLOR_BLACK, 0);
} else {
PrintString(get_hex_number(_Bind), OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_WHITE, COLOR_BLACK, 0);
}
}
}
} break;
case DOPT_BUTTON: {
} break;
case DOPT_SUBMENU: {
if (aOpt == aCurrentOpt) {
PrintString(DYNOS_TEXT_A, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_SELECT, COLOR_BLACK, 0);
}
} break;
}
}
void DynOS_Opt_DrawMenu(DynosOption *aCurrentOption, DynosOption *aCurrentMenu, DynosOption *aOptionsMenu, DynosOption *aDynosMenu) {
if (aCurrentMenu == NULL) {
return;
}
// Colorful label
Label _Title;
if (aCurrentOption->mParent) {
_Title = get_title(aCurrentOption->mParent);
} else if (aCurrentMenu == aDynosMenu) {
_Title = DYNOS_TEXT_DYNOS_MENU;
} else if (aCurrentMenu == aOptionsMenu) {
_Title = DYNOS_TEXT_OPTIONS_MENU;
}
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
if (!_Title.second) _Title.second = DynOS_String_Convert(_Title.first.begin(), false);
print_hud_lut_string(HUD_LUT_GLOBAL, (SCREEN_WIDTH / 2 - DynOS_String_Length(_Title.second) * 6), 40, _Title.second);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
// Display options
DynosOption **_Options = GetCurrentOptions(aCurrentOption);
for (s32 i = 0; i != 7; ++i) {
DynOS_Opt_DrawOption(_Options[i], aCurrentOption, 156 - 20 * i);
}
// Scroll bar
s32 _OptCount = GetCurrentOptionCount(aCurrentOption);
s32 _OptIndex = GetCurrentOptionIndex(aCurrentOption);
if (_OptCount > 7) {
s32 _Height = (s32) (134.f * sqrtf(1.f / (_OptCount - 6)));
s32 _Y = 37 + (134 - _Height) * (1.f - MAX(0.f, MIN(1.f, (f32)(_OptIndex - 3) / (f32)(_OptCount - 6))));
PrintBox(OFFSET_FROM_RIGHT_EDGE - 16, 36, 8, 136, COLOR_DARK_GRAY, 0);
PrintBox(OFFSET_FROM_RIGHT_EDGE - 15, _Y, 6, _Height, COLOR_WHITE, 0);
}
}
#define PROMPT_OFFSET (56.25f * GFX_DIMENSIONS_ASPECT_RATIO)
void DynOS_Opt_DrawPrompt(DynosOption *aCurrentMenu, DynosOption *aOptionsMenu, DynosOption *aDynosMenu) {
if (aCurrentMenu == aOptionsMenu) {
PrintString(DYNOS_TEXT_OPEN_LEFT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 1);
PrintString(DYNOS_TEXT_CLOSE_RIGHT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 0);
} else if (aCurrentMenu == aDynosMenu) {
PrintString(DYNOS_TEXT_CLOSE_LEFT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 1);
PrintString(DYNOS_TEXT_OPEN_RIGHT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 0);
} else {
PrintString(DYNOS_TEXT_OPEN_LEFT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 1);
PrintString(DYNOS_TEXT_OPEN_RIGHT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 0);
}
}