sm64coopdx/src/menu/custom_menu_system.c
Victor Diego Alejandro Diaz Urbaneja 26928ac6f5
overlay error (#88)
* overlay error

fixed compilation error

* Update custom_menu_system.c

Co-authored-by: djoslin0 <djoslin0@users.noreply.github.com>
2020-10-16 23:36:31 -07:00

412 lines
14 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "custom_menu_system.h"
#include "custom_menu.h"
#include "game/object_list_processor.h"
#include "game/object_helpers.h"
#include "game/ingame_menu.h"
#include "game/game_init.h"
#include "game/segment2.h"
#include "object_fields.h"
#include "model_ids.h"
#include "behavior_data.h"
#include "audio_defines.h"
#include "audio/external.h"
#include "gfx_dimensions.h"
#include "config.h"
static struct CustomMenu* sHead = NULL;
static struct CustomMenu* sCurrentMenu = NULL;
static struct CustomMenu* sLastMenu = NULL;
struct CustomMenuButtonScale gButtonScale = {
.small = 0.08111111f,
.medium = 0.09511111f,
.large = 0.11111111f,
};
u8 gMenuStringAlpha = 255;
struct ErrorDialog {
u8* dialog;
struct ErrorDialog* next;
};
static struct ErrorDialog* sErrorDialog = NULL;
struct CustomMenuButton* custom_menu_create_button(struct CustomMenu* parent, char* label, u16 x, u16 y, f32 scale, s32 clickSound, void (*on_click)(void)) {
struct CustomMenuButton* button = calloc(1, sizeof(struct CustomMenuButton));
if (parent->buttons == NULL) {
parent->buttons = button;
} else {
struct CustomMenuButton* parentButton = parent->buttons;
while (parentButton->next != NULL) { parentButton = parentButton->next; }
parentButton->next = button;
}
button->label = calloc(strlen(label), sizeof(char) + 1);
strcpy(button->label, label);
button->on_click = on_click;
button->clickSound = clickSound;
struct Object* obj = spawn_object_rel_with_rot(parent->me->object, MODEL_MAIN_MENU_MARIO_NEW_BUTTON, bhvMenuButton, x * -1, y, -1, 0, 0x8000, 0);
obj->oMenuButtonScale = scale;
obj->oFaceAngleRoll = 0;
obj->oMenuButtonTimer = 0;
obj->oMenuButtonOrigPosX = obj->oParentRelativePosX;
obj->oMenuButtonOrigPosY = obj->oParentRelativePosY;
obj->oMenuButtonOrigPosZ = obj->oParentRelativePosZ;
obj->oMenuButtonIsCustom = 1;
obj->oMenuButtonState = MENU_BUTTON_STATE_DEFAULT;
button->object = obj;
return button;
}
struct CustomMenu* custom_menu_create(struct CustomMenu* parent, char* label, u16 x, u16 y, f32 scale) {
struct CustomMenuButton* button = custom_menu_create_button(parent, label, x, y, scale, SOUND_MENU_CAMERA_ZOOM_IN, NULL);
struct CustomMenu* menu = calloc(1, sizeof(struct CustomMenu));
menu->parent = parent;
menu->depth = parent->depth + 1;
menu->headerY = 25;
menu->me = button;
button->menu = menu;
return menu;
}
void custom_menu_system_init(void) {
// allocate the main menu and set it to current
sHead = calloc(1, sizeof(struct CustomMenu));
sHead->me = calloc(1, sizeof(struct CustomMenuButton));
sCurrentMenu = sHead;
// spawn the main menu game object
struct Object* obj = spawn_object_rel_with_rot(gCurrentObject, MODEL_MAIN_MENU_GREEN_SCORE_BUTTON, bhvMenuButton, 0, 0, 0, 0, 0, 0);
obj->oParentRelativePosZ += 1000;
obj->oMenuButtonState = MENU_BUTTON_STATE_FULLSCREEN;
obj->oFaceAngleYaw = 0x8000;
obj->oFaceAngleRoll = 0;
obj->oMenuButtonScale = 9.0f;
obj->oMenuButtonOrigPosZ = obj->oPosZ;
obj->oMenuButtonOrigPosX = 99999;
obj->oMenuButtonIsCustom = 1;
sHead->me->object = obj;
custom_menu_init(sHead);
}
void custom_menu_destroy(void) {
// TODO: clean up all of the calloc()'d memory
sHead = NULL;
sCurrentMenu = NULL;
sLastMenu = NULL;
}
void custom_menu_system_loop(void) {
custom_menu_loop();
}
static void button_force_instant_close(struct Object* obj) {
obj->oFaceAngleYaw = 0x8000;
obj->oFaceAnglePitch = 0;
obj->oParentRelativePosX = obj->oMenuButtonOrigPosX;
obj->oParentRelativePosY = obj->oMenuButtonOrigPosY;
obj->oParentRelativePosZ = obj->oMenuButtonOrigPosZ;
obj->oMenuButtonScale = 0.11111111f;
obj->oMenuButtonState = MENU_BUTTON_STATE_DEFAULT;
obj->oMenuButtonTimer = 0;
}
static void button_force_instant_open(struct Object* obj) {
obj->oFaceAngleYaw = 0;
obj->oFaceAnglePitch = 0;
obj->oParentRelativePosX = 0.0f;
obj->oParentRelativePosY = 0.0f;
obj->oParentRelativePosZ = -801;
obj->oMenuButtonScale = 0.623111;
obj->oMenuButtonState = MENU_BUTTON_STATE_FULLSCREEN;
obj->oMenuButtonTimer = 0;
}
void custom_menu_open(struct CustomMenu* menu) {
if (sCurrentMenu == menu) { return; }
if (menu == NULL) { return; }
// force instant-close all parents if not a direct route
{
// check all buttons of current menu to see if the desired menu is directly beneath it
struct CustomMenuButton* onButton = sCurrentMenu->buttons;
u8 foundMenu = FALSE;
while (onButton != NULL) {
if (onButton == menu->me) { foundMenu = TRUE; break; }
onButton = onButton->next;
}
// if not direct route, force close all the way to the main menu
if (!foundMenu) {
struct CustomMenu* onMenu = sCurrentMenu;
while (onMenu != NULL && onMenu != sHead) {
struct Object* obj = onMenu->me->object;
if (obj->oMenuButtonState != MENU_BUTTON_STATE_FULLSCREEN) { break; }
button_force_instant_close(obj);
if (onMenu->on_close != NULL) { onMenu->on_close(); }
onMenu = onMenu->parent;
}
}
}
// force instant-open all parents
{
struct CustomMenu* onMenu = menu->parent;
while (onMenu != NULL) {
struct Object* obj = onMenu->me->object;
if (obj->oMenuButtonState == MENU_BUTTON_STATE_FULLSCREEN) { break; }
button_force_instant_open(obj);
onMenu = onMenu->parent;
}
}
struct Object* obj = menu->me->object;
obj->oMenuButtonState = MENU_BUTTON_STATE_GROWING;
obj->oMenuButtonTimer = 0;
gMenuStringAlpha = 0;
sLastMenu = sCurrentMenu;
sCurrentMenu = menu;
}
void custom_menu_close(void) {
struct Object* obj = sCurrentMenu->me->object;
obj->oMenuButtonState = MENU_BUTTON_STATE_SHRINKING;
obj->oMenuButtonTimer = 0;
gMenuStringAlpha = 0;
if (sCurrentMenu->on_close != NULL) { sCurrentMenu->on_close(); }
sLastMenu = sCurrentMenu;
if (sCurrentMenu->parent != NULL) {
sCurrentMenu = sCurrentMenu->parent;
}
}
void custom_menu_close_system(void) {
sHead->me->object->oMenuButtonState = MENU_BUTTON_STATE_SHRINKING;
gInCustomMenu = FALSE;
}
static s32 cursor_inside_button(struct CustomMenuButton* button, f32 cursorX, f32 cursorY) {
f32 x = button->object->oParentRelativePosX;
f32 y = button->object->oParentRelativePosY;
f32 scale = button->object->oMenuButtonScale;
x *= -0.137f;
y *= 0.137f;
s16 maxX = x + scale * 185.0f;
s16 minX = x - scale * 185.0f;
s16 maxY = y + scale * 185.0f;
s16 minY = y - scale * 101.0f;
return (cursorX < maxX && minX < cursorX && cursorY < maxY && minY < cursorY);
}
void custom_menu_cursor_click(f32 cursorX, f32 cursorY) {
#ifdef VERSION_EU
u16 cursorClickButtons = (A_BUTTON | B_BUTTON | START_BUTTON | Z_TRIG);
#else
u16 cursorClickButtons = (A_BUTTON | B_BUTTON | START_BUTTON);
#endif
if (!(gPlayer3Controller->buttonPressed & cursorClickButtons)) { return; }
if (sCurrentMenu->me->object->oMenuButtonState != MENU_BUTTON_STATE_FULLSCREEN) { return; }
if (sErrorDialog != NULL) {
struct ErrorDialog* current = sErrorDialog;
sErrorDialog = sErrorDialog->next;
free(current->dialog);
free(current);
play_sound(SOUND_ACTION_BONK, gDefaultSoundArgs);
if (sErrorDialog != NULL) {
play_sound(SOUND_MARIO_OOOF2, gDefaultSoundArgs);
}
return;
}
struct CustomMenuButton* button = sCurrentMenu->buttons;
while (button != NULL) {
if (cursor_inside_button(button, cursorX, cursorY)) {
u8 didSomething = FALSE;
if (button->menu != NULL) {
custom_menu_open(button->menu);
didSomething = TRUE;
}
if (button->on_click != NULL) {
button->on_click();
didSomething = TRUE;
}
if (button->clickSound != 0) {
play_sound(button->clickSound, gDefaultSoundArgs);
}
if (didSomething) { break; }
}
button = button->next;
}
}
static void button_label_pos(struct CustomMenuButton* button, s16* outX, s16* outY) {
f32 x = button->object->oParentRelativePosX;
f32 y = button->object->oParentRelativePosY;
x -= strlen(button->label) * -27.0f;
y += -163.0f;
*outX = 165.0f + x * -0.137f;
*outY = 110.0f + y * 0.137f;
}
void custom_menu_print_strings(void) {
// figure out alpha
struct Object* curObj = sCurrentMenu->me->object;
struct Object* lastObj = (sLastMenu != NULL) ? sLastMenu->me->object : NULL;
if (curObj != NULL && lastObj != NULL) {
if (curObj->oMenuButtonState == MENU_BUTTON_STATE_FULLSCREEN && lastObj->oMenuButtonState != MENU_BUTTON_STATE_SHRINKING) {
if (gMenuStringAlpha < 250) {
gMenuStringAlpha += 10;
} else {
gMenuStringAlpha = 255;
}
}
}
// print header
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuStringAlpha);
char* headerLabel = sCurrentMenu->me->label;
u16 headerLabelLen = strlen(headerLabel);
u16 headerX = (u16)(159.66f - (headerLabelLen * 5.66f));
unsigned char header[64];
str_ascii_to_dialog(headerLabel, header, headerLabelLen);
print_hud_lut_string(HUD_LUT_DIFF, headerX, sCurrentMenu->headerY, header);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
// print text
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
struct CustomMenuButton* button = sCurrentMenu->buttons;
while (button != NULL) {
gDPSetEnvColor(gDisplayListHead++, 222, 222, 222, gMenuStringAlpha);
s16 x, y;
button_label_pos(button, &x, &y);
print_generic_ascii_string(x, y, button->label);
button = button->next;
}
if (sCurrentMenu->draw_strings != NULL) { sCurrentMenu->draw_strings(); }
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
}
void custom_menu_render_top(void) {
// print error message
if (sErrorDialog != NULL) {
// black screen
create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(0), 240.0f, 0);
create_dl_scale_matrix(MENU_MTX_NOPUSH, GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT / 130.0f, 3.0f, 1.0f);
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 240);
gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
// print text
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
f32 textWidth = get_generic_dialog_width(sErrorDialog->dialog);
f32 textHeight = get_generic_dialog_height(sErrorDialog->dialog);
f32 xPos = (SCREEN_WIDTH - textWidth) / 2.0f;
f32 yPos = (SCREEN_HEIGHT + textHeight) / 2.0f;
gDPSetEnvColor(gDisplayListHead++, 30, 30, 30, 255);
print_generic_string(xPos, yPos, sErrorDialog->dialog);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
print_generic_string((xPos - 1), (yPos + 1), sErrorDialog->dialog);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
}
}
/**
* Grow from submenu, used by selecting a file in the score menu.
*/
void bhv_menu_button_growing_from_custom(struct Object* button) {
if (button->oMenuButtonTimer < 16) {
button->oFaceAngleYaw += 0x800;
}
if (button->oMenuButtonTimer < 8) {
button->oFaceAnglePitch += 0x800;
}
if (button->oMenuButtonTimer >= 8 && button->oMenuButtonTimer < 16) {
button->oFaceAnglePitch -= 0x800;
}
button->oParentRelativePosX -= button->oMenuButtonOrigPosX / 16.0;
button->oParentRelativePosY -= button->oMenuButtonOrigPosY / 16.0;
button->oParentRelativePosZ -= 50;
button->oMenuButtonScale += 0.032f;
button->oMenuButtonTimer++;
if (button->oMenuButtonTimer == 16) {
button->oParentRelativePosX = 0.0f;
button->oParentRelativePosY = 0.0f;
button->oMenuButtonState = MENU_BUTTON_STATE_FULLSCREEN;
button->oMenuButtonTimer = 0;
}
}
/**
* Shrink back to submenu, used to return back while inside a score save menu.
*/
void bhv_menu_button_shrinking_to_custom(struct Object* button) {
if (button->oMenuButtonTimer < 16) {
button->oFaceAngleYaw -= 0x800;
}
if (button->oMenuButtonTimer < 8) {
button->oFaceAnglePitch -= 0x800;
}
if (button->oMenuButtonTimer >= 8 && button->oMenuButtonTimer < 16) {
button->oFaceAnglePitch += 0x800;
}
button->oParentRelativePosX += button->oMenuButtonOrigPosX / 16.0;
button->oParentRelativePosY += button->oMenuButtonOrigPosY / 16.0;
button->oParentRelativePosZ += 50;
button->oMenuButtonScale -= 0.032f;
button->oMenuButtonTimer++;
if (button->oMenuButtonTimer == 16) {
button->oParentRelativePosX = button->oMenuButtonOrigPosX;
button->oParentRelativePosY = button->oMenuButtonOrigPosY;
button->oMenuButtonScale = 0.11111111f;
button->oMenuButtonState = MENU_BUTTON_STATE_DEFAULT;
button->oMenuButtonTimer = 0;
}
}
void custom_menu_error(char* message) {
struct ErrorDialog* errorDialog = malloc(sizeof(struct ErrorDialog));
memset(errorDialog, 0, sizeof(struct ErrorDialog));
errorDialog->dialog = malloc(sizeof(u8) * (strlen(message) + 1));
str_ascii_to_dialog(message, errorDialog->dialog, strlen(message));
if (sErrorDialog == NULL) {
sErrorDialog = errorDialog;
play_sound(SOUND_MARIO_OOOF2, gDefaultSoundArgs);
} else {
struct ErrorDialog* item = sErrorDialog;
while (item != NULL) {
if (item->next == NULL) {
item->next = errorDialog;
break;
}
item = item->next;
}
}
}