/** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "uigamemenu.h" #include "ui/frame/uiframe.h" #include "ui/frame/settings/uisettings.h" #include "ui/frame/backpack/uibackpack.h" #include "ui/rpg/textbox/uitextboxmain.h" #include "util/memory.h" #include "display/spritebatch/spritebatch.h" #include "display/screen/screen.h" #include "assert/assert.h" #include "locale/localemanager.h" #include "asset/loader/locale/assetlocaleloader.h" #include "save/save.h" #include "error/error.h" #include "util/string.h" #define UI_GAME_MENU_INDEX_CHARACTERS 0 #define UI_GAME_MENU_INDEX_ITEMS 1 #define UI_GAME_MENU_INDEX_SETTINGS 2 #define UI_GAME_MENU_INDEX_SAVE 3 // Only one save slot is exposed through this menu for now - there is no // slot-select UI yet, and SAVE_FILE_COUNT_MAX > 1 exists for later. #define UI_GAME_MENU_SAVE_SLOT 0 static void uiGameMenuSaveComplete(errorret_t result, void *user) { if(errorIsNotOk(result)) { // Generously sized - stringFormat asserts (crashes) rather than // truncating if the message doesn't fit, so this must comfortably fit // the longest platform save-error message plus this prefix. char_t msg[256]; stringFormat( msg, sizeof(msg), "Save failed: %s", result.state->message ); errorCatch(result); uiTextboxMainSetText(msg); return; } uiTextboxMainSetText("Game saved."); } static void uiGameMenuSave(void) { if(!saveIsAvailable()) { uiTextboxMainSetText("Can't save - no save device found."); return; } if(saveIsBusy()) return;// A save/load dialog (e.g. on PSP) is already up. saveWrite(UI_GAME_MENU_SAVE_SLOT, uiGameMenuSaveComplete, NULL); } uigamemenu_t UI_GAME_MENU; void uiGameMenuSelected( const uimenu_t *menu, const uint8_t index, const uimenuitem_t *item ) { if(index == UI_GAME_MENU_INDEX_ITEMS) uiBackpackOpen(); if(index == UI_GAME_MENU_INDEX_SETTINGS) uiSettingsOpen(); if(index == UI_GAME_MENU_INDEX_SAVE) uiGameMenuSave(); } errorret_t uiGameMenuInit(void) { memoryZero(&UI_GAME_MENU, sizeof(uigamemenu_t)); errorChain(assetLocaleGetString( &LOCALE.entry->data.locale, "ui.game_menu.characters", 0, UI_GAME_MENU.charactersLabel, UI_GAME_MENU_LABEL_MAX )); errorChain(assetLocaleGetString( &LOCALE.entry->data.locale, "ui.game_menu.items", 0, UI_GAME_MENU.itemsLabel, UI_GAME_MENU_LABEL_MAX )); errorChain(assetLocaleGetString( &LOCALE.entry->data.locale, "ui.game_menu.settings", 0, UI_GAME_MENU.settingsLabel, UI_GAME_MENU_LABEL_MAX )); errorChain(assetLocaleGetString( &LOCALE.entry->data.locale, "ui.game_menu.save", 0, UI_GAME_MENU.saveLabel, UI_GAME_MENU_LABEL_MAX )); MENU_BEGIN( &UI_GAME_MENU.menu, UI_GAME_MENU.items, uiGameMenuSelected, NULL, NULL ); MENU_BUTTON(UI_GAME_MENU.charactersLabel); MENU_BUTTON(UI_GAME_MENU.itemsLabel); MENU_BUTTON(UI_GAME_MENU.settingsLabel); MENU_BUTTON(UI_GAME_MENU.saveLabel); MENU_END(UI_GAME_MENU.items, 1); errorOk(); } errorret_t uiGameMenuDraw(void) { if(!uiMenuIsActive(&UI_GAME_MENU.menu)) errorOk(); const float_t width = UI_GAME_MENU_WIDTH; const float_t height = (float_t)SCREEN.scanHeight; const float_t x = (float_t)(SCREEN.scanX + SCREEN.scanWidth) - width; const float_t y = (float_t)SCREEN.scanY; errorChain(uiFrameDraw(x, y, width, height)); errorChain(uiMenuDraw( &UI_GAME_MENU.menu, x + UI_FRAME_START_X, y + UI_FRAME_START_Y, width - (UI_FRAME_START_X * 2), height - (UI_FRAME_START_Y * 2) )); errorChain(spriteBatchFlush()); errorOk(); } bool_t uiGameMenuIsOpen(void) { return uiMenuIsActive(&UI_GAME_MENU.menu); } void uiGameMenuOpen() { uiMenuOpen(&UI_GAME_MENU.menu); } void uiGameMenuClose() { uiMenuClose(&UI_GAME_MENU.menu); } errorret_t uiGameMenuDispose(void) { errorOk(); }