diff --git a/assets/locale/en_US.po b/assets/locale/en_US.po index 5f57d69a..e48db954 100644 --- a/assets/locale/en_US.po +++ b/assets/locale/en_US.po @@ -38,6 +38,33 @@ msgstr "Quit Game" msgid "main_menu.quit_confirm" msgstr "Are you sure you want to quit?" +msgid "main_menu.checking_save.title" +msgstr "Checking for save data" + +msgid "main_menu.checking_save.message" +msgstr "Please wait..." + +msgid "main_menu.no_device.title" +msgstr "No Save Device Found" + +msgid "main_menu.no_device.message" +msgstr "Could not find a save device, ensure it is connected and try again. You can continue, but progress will not be saved." + +msgid "main_menu.no_device.retry" +msgstr "Try again" + +msgid "main_menu.no_device.continue" +msgstr "Continue without saving" + +msgid "main_menu.save_load_error.title" +msgstr "Error" + +msgid "main_menu.save_load_error.message" +msgstr "Failed to load save data. Please try again." + +msgid "main_menu.save_load_error.retry" +msgstr "Try Again" + # Select Save Screen msgid "ui.select_save.title" diff --git a/src/dusk/rpg/cutscene/cutscene.h b/src/dusk/rpg/cutscene/cutscene.h index 81966305..b0dae769 100644 --- a/src/dusk/rpg/cutscene/cutscene.h +++ b/src/dusk/rpg/cutscene/cutscene.h @@ -120,6 +120,42 @@ typedef struct cutscene_s { } \ } +// Same as CUTSCENE_MODAL_OPTIONS, but fixed to exactly one option and +// MARKER1 to jump straight to via cutsceneGoTo once it's selected (or +// the dialog is backed out of) - no callback function to write. Does +// not fall through to whatever follows this item, so MARKER1 must be +// scripted elsewhere in the same cutscene. +#define CUTSCENE_MODAL_OPTIONS_ONE(TITLE, MESSAGE, OPTION1, MARKER1) \ + { \ + .type = CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS, \ + .modalOptionsMarkers = { \ + .title = TITLE, .message = MESSAGE, \ + .options = { OPTION1 }, \ + .markers = { MARKER1 }, \ + .optionCount = 1 \ + } \ + } + +// Same as CUTSCENE_MODAL_OPTIONS, but fixed to exactly two options, +// jumping straight to OPTION1_MARKER or OPTION2_MARKER via cutsceneGoTo +// once the corresponding option is selected - no callback function to +// write. Backing out without selecting (UI_MODAL_RESULT_NONE) is +// treated the same as selecting option 2. Does not fall through to +// whatever follows this item, so both markers must be scripted +// elsewhere in the same cutscene. +#define CUTSCENE_MODAL_OPTIONS_TWO( \ + TITLE, MESSAGE, OPTION1, OPTION1_MARKER, OPTION2, OPTION2_MARKER \ +) \ + { \ + .type = CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS, \ + .modalOptionsMarkers = { \ + .title = TITLE, .message = MESSAGE, \ + .options = { OPTION1, OPTION2 }, \ + .markers = { OPTION1_MARKER, OPTION2_MARKER }, \ + .optionCount = 2 \ + } \ + } + // Closes the currently open modal (if any). Useful when a modal was // opened outside of a blocking CUTSCENE_MODAL item (e.g. directly via // uiModalOpen) and this cutscene just needs to dismiss it and continue @@ -127,6 +163,34 @@ typedef struct cutscene_s { #define CUTSCENE_MODAL_CLOSE() \ { .type = CUTSCENE_ITEM_TYPE_MODAL_CLOSE } +// (Re)requests an available save device and jumps straight to +// SUCCESS_MARKER or FAILURE_MARKER once it resolves, same as a +// CUTSCENE_MODAL_OPTIONS callback - it does not fall through to +// whatever follows this item, so both markers must be scripted +// elsewhere in the same cutscene. +#define CUTSCENE_SAVE_DEVICE_CHECK(SUCCESS_MARKER, FAILURE_MARKER) \ + { \ + .type = CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK, \ + .saveDeviceCheck = { \ + .successMarker = SUCCESS_MARKER, \ + .failureMarker = FAILURE_MARKER \ + } \ + } + +// (Re)loads every save slot via saveLoadAllSlots() and jumps straight to +// SUCCESS_MARKER or FAILURE_MARKER once it resolves, same shape as +// CUTSCENE_SAVE_DEVICE_CHECK - it does not fall through to whatever +// follows this item, so both markers must be scripted elsewhere in the +// same cutscene. +#define CUTSCENE_SAVE_LOAD_ALL_SLOTS(SUCCESS_MARKER, FAILURE_MARKER) \ + { \ + .type = CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS, \ + .saveLoadAllSlots = { \ + .successMarker = SUCCESS_MARKER, \ + .failureMarker = FAILURE_MARKER \ + } \ + } + #define CUTSCENE_ENTITY_WALK_TO(ENTITY_INDEX, X, Y, Z) \ { \ .type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \ diff --git a/src/dusk/rpg/cutscene/item/CMakeLists.txt b/src/dusk/rpg/cutscene/item/CMakeLists.txt index d00c8e53..7de5c398 100755 --- a/src/dusk/rpg/cutscene/item/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/item/CMakeLists.txt @@ -16,3 +16,4 @@ add_subdirectory(item) add_subdirectory(maparea) add_subdirectory(ui) add_subdirectory(battle) +add_subdirectory(save) diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.c b/src/dusk/rpg/cutscene/item/cutsceneitem.c index 1a1ff3f2..7e38413b 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.c +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.c @@ -134,6 +134,11 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { .update = cutsceneModalUpdate }, + [CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS] = { + .init = cutsceneModalOptionsMarkersStart, + .update = cutsceneModalOptionsMarkersUpdate + }, + [CUTSCENE_ITEM_TYPE_MODAL_CLOSE] = { .init = cutsceneModalCloseStart, .update = cutsceneModalCloseUpdate @@ -157,6 +162,16 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { [CUTSCENE_ITEM_TYPE_SCENE] = { .init = cutsceneSceneStart, .update = cutsceneSceneUpdate + }, + + [CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK] = { + .init = cutsceneSaveDeviceCheckStart, + .update = cutsceneSaveDeviceCheckUpdate + }, + + [CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS] = { + .init = cutsceneSaveLoadAllSlotsStart, + .update = cutsceneSaveLoadAllSlotsUpdate } }; diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.h b/src/dusk/rpg/cutscene/item/cutsceneitem.h index 532a9f16..53b4baf7 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.h +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.h @@ -27,6 +27,7 @@ #include "ui/cutsceneemoji.h" #include "ui/cutsceneshake.h" #include "ui/cutscenemodal.h" +#include "ui/cutscenemodaloptionsmarkers.h" #include "item/cutsceneitemgive.h" #include "maparea/cutscenemapareaadd.h" #include "maparea/cutscenemaparearemove.h" @@ -34,6 +35,8 @@ #include "battle/cutscenestartbattle.h" #include "battle/cutscenebattlewaitstate.h" #include "battle/cutscenebattleforceaction.h" +#include "save/cutscenesavedevicecheck.h" +#include "save/cutscenesaveloadallslots.h" typedef struct cutscene_s cutscene_t; @@ -65,11 +68,14 @@ typedef enum { CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE, CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION, CUTSCENE_ITEM_TYPE_MODAL, + CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS, CUTSCENE_ITEM_TYPE_MODAL_CLOSE, CUTSCENE_ITEM_TYPE_PRINT, CUTSCENE_ITEM_TYPE_MARKER, CUTSCENE_ITEM_TYPE_RESTART, CUTSCENE_ITEM_TYPE_SCENE, + CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK, + CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS, CUTSCENE_ITEM_TYPE_COUNT } cutsceneitemtype_t; @@ -103,9 +109,12 @@ struct cutsceneitem_s { cutscenebattlewaitstate_t battleWaitState; cutscenebattleforceaction_t battleForceAction; cutscenemodal_t modal; + cutscenemodaloptionsmarkers_t modalOptionsMarkers; cutsceneprint_t print; cutscenemarker_t marker; cutscenescene_t sceneChange; + cutscenesavedevicecheck_t saveDeviceCheck; + cutscenesaveloadallslots_t saveLoadAllSlots; }; }; diff --git a/src/dusk/rpg/cutscene/item/save/CMakeLists.txt b/src/dusk/rpg/cutscene/item/save/CMakeLists.txt new file mode 100644 index 00000000..7ca216ee --- /dev/null +++ b/src/dusk/rpg/cutscene/item/save/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + cutscenesavedevicecheck.c + cutscenesaveloadallslots.c +) diff --git a/src/dusk/rpg/cutscene/item/save/cutscenesavedevicecheck.c b/src/dusk/rpg/cutscene/item/save/cutscenesavedevicecheck.c new file mode 100644 index 00000000..33bf87d9 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/save/cutscenesavedevicecheck.c @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg/cutscene/item/cutsceneitem.h" +#include "rpg/cutscene/cutscenesystem.h" +#include "save/save.h" + +void cutsceneSaveDeviceCheckCallback(savedevice_t *device, void *user) { + const cutsceneitem_t *item = cutsceneSystemGetCurrentItem(); + cutsceneGoTo( + device != NULL ? + item->saveDeviceCheck.successMarker : + item->saveDeviceCheck.failureMarker + ); +} + +void cutsceneSaveDeviceCheckStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + saveFindAvailableDevice(cutsceneSaveDeviceCheckCallback, NULL); +} + +bool_t cutsceneSaveDeviceCheckUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + return false; +} diff --git a/src/dusk/rpg/cutscene/item/save/cutscenesavedevicecheck.h b/src/dusk/rpg/cutscene/item/save/cutscenesavedevicecheck.h new file mode 100644 index 00000000..17cfa901 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/save/cutscenesavedevicecheck.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct cutsceneitem_s cutsceneitem_t; +typedef union cutsceneitemdata_u cutsceneitemdata_t; + +typedef struct { + const char_t *successMarker; + const char_t *failureMarker; +} cutscenesavedevicecheck_t; + +/** + * Starts a save-device-check item: (re)requests an available save + * device via saveFindAvailableDevice. Never completes on its own - see + * cutsceneSaveDeviceCheckUpdate. + * + * @param item The cutscene item. + * @param data Runtime data storage. + */ +void cutsceneSaveDeviceCheckStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); + +/** + * Updates a save-device-check item. Like a CUTSCENE_MODAL_OPTIONS item, + * this never completes on its own - once saveFindAvailableDevice's + * callback fires, it jumps straight to the item's successMarker or + * failureMarker via cutsceneGoTo. + * + * @param item The cutscene item. + * @param data Runtime data storage. + * @returns false always. + */ +bool_t cutsceneSaveDeviceCheckUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); diff --git a/src/dusk/rpg/cutscene/item/save/cutscenesaveloadallslots.c b/src/dusk/rpg/cutscene/item/save/cutscenesaveloadallslots.c new file mode 100644 index 00000000..28958a3c --- /dev/null +++ b/src/dusk/rpg/cutscene/item/save/cutscenesaveloadallslots.c @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg/cutscene/item/cutsceneitem.h" +#include "rpg/cutscene/cutscenesystem.h" +#include "save/save.h" + +void cutsceneSaveLoadAllSlotsStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + errorret_t result = saveLoadAllSlots(); + if(errorIsNotOk(result)) { + errorCatch(errorPrint(result)); + cutsceneGoTo(item->saveLoadAllSlots.failureMarker); + return; + } + + cutsceneGoTo(item->saveLoadAllSlots.successMarker); +} + +bool_t cutsceneSaveLoadAllSlotsUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + return false; +} diff --git a/src/dusk/rpg/cutscene/item/save/cutscenesaveloadallslots.h b/src/dusk/rpg/cutscene/item/save/cutscenesaveloadallslots.h new file mode 100644 index 00000000..22f49243 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/save/cutscenesaveloadallslots.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct cutsceneitem_s cutsceneitem_t; +typedef union cutsceneitemdata_u cutsceneitemdata_t; + +typedef struct { + const char_t *successMarker; + const char_t *failureMarker; +} cutscenesaveloadallslots_t; + +/** + * Starts a save-load-all-slots item: calls saveLoadAllSlots() and jumps + * straight to the item's successMarker or failureMarker via + * cutsceneGoTo, depending on the result. + * + * @param item The cutscene item. + * @param data Runtime data storage. + */ +void cutsceneSaveLoadAllSlotsStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); + +/** + * Updates a save-load-all-slots item. Never completes on its own - + * Start already jumps to whichever marker applies before this would + * ever run. + * + * @param item The cutscene item. + * @param data Runtime data storage. + * @returns false always. + */ +bool_t cutsceneSaveLoadAllSlotsUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); diff --git a/src/dusk/rpg/cutscene/item/ui/CMakeLists.txt b/src/dusk/rpg/cutscene/item/ui/CMakeLists.txt index 1d966039..b359087a 100644 --- a/src/dusk/rpg/cutscene/item/ui/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/item/ui/CMakeLists.txt @@ -12,4 +12,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} cutsceneemoji.c cutsceneshake.c cutscenemodal.c + cutscenemodaloptionsmarkers.c ) diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenemodaloptionsmarkers.c b/src/dusk/rpg/cutscene/item/ui/cutscenemodaloptionsmarkers.c new file mode 100644 index 00000000..db31da53 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/ui/cutscenemodaloptionsmarkers.c @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg/cutscene/item/cutsceneitem.h" +#include "rpg/cutscene/cutscenesystem.h" +#include "ui/widget/uimodal.h" + +void cutsceneModalOptionsMarkersCallback( + const uint8_t optionIndex, + void *userData +) { + const cutscenemodaloptionsmarkers_t *options = + &cutsceneSystemGetCurrentItem()->modalOptionsMarkers; + + // A backed-out-of dialog (UI_MODAL_RESULT_NONE) falls back to the + // last option's marker, same as a hand-written two-option callback + // (e.g. sceneInitialSaveDeviceRetryCallback) treating "backed out" as + // the safe/continue choice rather than the destructive one. + const uint8_t index = + optionIndex < options->optionCount ? optionIndex : options->optionCount - 1; + cutsceneGoTo(options->markers[index]); +} + +void cutsceneModalOptionsMarkersStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + const cutscenemodaloptionsmarkers_t *options = &item->modalOptionsMarkers; + + uiModalOpen( + options->title, + options->message, + options->options, + options->optionCount, + cutsceneModalOptionsMarkersCallback, + NULL, + CUTSCENE_SYSTEM.userData + ); +} + +bool_t cutsceneModalOptionsMarkersUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + return false; +} diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenemodaloptionsmarkers.h b/src/dusk/rpg/cutscene/item/ui/cutscenemodaloptionsmarkers.h new file mode 100644 index 00000000..f096f4ea --- /dev/null +++ b/src/dusk/rpg/cutscene/item/ui/cutscenemodaloptionsmarkers.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct cutsceneitem_s cutsceneitem_t; +typedef union cutsceneitemdata_u cutsceneitemdata_t; + +// Backs CUTSCENE_MODAL_OPTIONS_ONE and CUTSCENE_MODAL_OPTIONS_TWO, so two +// is the most either macro ever needs. +#define CUTSCENE_MODAL_OPTIONS_MARKERS_MAX 2 + +typedef struct { + // Display text, or a locale message ID to translate - see + // uiModalLocalize. + char_t title[CUTSCENE_MODAL_TITLE_MAX_CHARS]; + char_t message[CUTSCENE_MODAL_MESSAGE_MAX_CHARS]; + + // Each option's display text/locale message ID, and the marker to + // cutsceneGoTo when that option is selected. + const char_t *options[CUTSCENE_MODAL_OPTIONS_MARKERS_MAX]; + const char_t *markers[CUTSCENE_MODAL_OPTIONS_MARKERS_MAX]; + uint8_t optionCount; +} cutscenemodaloptionsmarkers_t; + +/** + * Starts a modal-options-markers item (shows the modal dialog with the + * item's title, message, and options). + * + * @param item The cutscene item. + * @param data Runtime data storage. + */ +void cutsceneModalOptionsMarkersStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); + +/** + * Updates a modal-options-markers item. Never completes on its own - + * the cutscene blocks here indefinitely. Once an option is selected (or + * the dialog is backed out of, which is treated as selecting the last + * option), it jumps straight to that option's marker via cutsceneGoTo. + * + * @param item The cutscene item. + * @param data Runtime data storage. + * @returns false always. + */ +bool_t cutsceneModalOptionsMarkersUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); diff --git a/src/dusk/scene/initial/sceneinitial.c b/src/dusk/scene/initial/sceneinitial.c index 075f9e7b..9b01a53e 100644 --- a/src/dusk/scene/initial/sceneinitial.c +++ b/src/dusk/scene/initial/sceneinitial.c @@ -11,29 +11,24 @@ #include "error/error.h" #include "display/screen/screen.h" #include "console/console.h" -#include "save/save.h" #include "ui/widget/uimodal.h" #include "rpg/cutscene/cutscene.h" #include "rpg/cutscene/cutscenesystem.h" -void sceneInitialAvailableDeviceCallback(savedevice_t *device, void *user); -void sceneInitialFindDevices(void *userData); -void sceneInitialSaveDeviceRetryCallback(const uint8_t opt, void *u); - CUTSCENE(INITIAL, 0, DEFAULT, CUTSCENE_MODAL( "initial.checking_save.title", "initial.checking_save.message" ), CUTSCENE_WAIT(1.0f), - CUTSCENE_CALLBACK(sceneInitialFindDevices), + CUTSCENE_SAVE_DEVICE_CHECK("CONTINUE", "NO_DEVICE"), CUTSCENE_MARKER("NO_DEVICE"), CUTSCENE_MODAL_CLOSE(), - CUTSCENE_MODAL_OPTIONS( + CUTSCENE_MODAL_OPTIONS_TWO( "initial.no_device.title", "initial.no_device.message", - sceneInitialSaveDeviceRetryCallback, - "initial.no_device.retry", "initial.no_device.continue" + "initial.no_device.retry", "RETRY", + "initial.no_device.continue", "CONTINUE" ), CUTSCENE_MARKER("RETRY"), @@ -45,18 +40,6 @@ CUTSCENE(INITIAL, 0, DEFAULT, CUTSCENE_SCENE(SCENE_TYPE_MAIN_MENU) ); -void sceneInitialAvailableDeviceCallback(savedevice_t *device, void *user) { - cutsceneGoTo(device == NULL ? "NO_DEVICE" : "CONTINUE"); -} - -void sceneInitialFindDevices(void *userData) { - saveFindAvailableDevice(sceneInitialAvailableDeviceCallback, NULL); -} - -void sceneInitialSaveDeviceRetryCallback(const uint8_t opt, void *u) { - cutsceneGoTo(opt == 0 ? "RETRY" : "CONTINUE"); -} - errorret_t sceneInitialInit(scenedata_t *sceneData) { assertNotNull(sceneData, "Scene data cannot be null"); memoryZero(&sceneData->initial, sizeof(sceneinitial_t)); diff --git a/src/dusk/ui/screen/mainmenu/uimainmenu.c b/src/dusk/ui/screen/mainmenu/uimainmenu.c index 7810c4c6..6b4771af 100644 --- a/src/dusk/ui/screen/mainmenu/uimainmenu.c +++ b/src/dusk/ui/screen/mainmenu/uimainmenu.c @@ -17,13 +17,57 @@ #include "asset/loader/locale/assetlocaleloader.h" #include "ui/dialog/uiconfirm.h" #include "ui/dialog/save/uiselectsave.h" +#include "rpg/cutscene/cutscene.h" +#include "rpg/cutscene/cutscenesystem.h" #include "scene/scene.h" -#include "save/save.h" #define UI_MAIN_MENU_INDEX_START_GAME 0 #define UI_MAIN_MENU_INDEX_OPTIONS 1 #define UI_MAIN_MENU_INDEX_QUIT 2 +void uiMainMenuOpenSelectSave(void *userData); + +// Loads every save slot before opening the load-game picker, same +// checking/retry-on-error shape as the initial scene's device lookup +// (see scene/initial/sceneinitial.c) - the error branch loops back via +// CUTSCENE_RESTART. +CUTSCENE(MAIN_MENU_START_GAME, 0, DEFAULT, + CUTSCENE_MODAL( + "main_menu.checking_save.title", + "main_menu.checking_save.message" + ), + CUTSCENE_WAIT(1.0f), + CUTSCENE_SAVE_DEVICE_CHECK("CONTINUE", "NO_DEVICE"), + + CUTSCENE_MARKER("NO_DEVICE"), + CUTSCENE_MODAL_CLOSE(), + CUTSCENE_MODAL_OPTIONS_TWO( + "main_menu.no_device.title", + "main_menu.no_device.message", + "main_menu.no_device.retry", "RETRY", + "main_menu.no_device.continue", "CONTINUE" + ), + + CUTSCENE_MARKER("RETRY"), + CUTSCENE_MODAL_CLOSE(), + CUTSCENE_RESTART(), + + CUTSCENE_MARKER("CONTINUE"), + CUTSCENE_MODAL_CLOSE(), + CUTSCENE_SAVE_LOAD_ALL_SLOTS("LOADED", "LOAD_ERROR"), + + CUTSCENE_MARKER("LOAD_ERROR"), + CUTSCENE_MODAL_OPTIONS_ONE( + "main_menu.save_load_error.title", + "main_menu.save_load_error.message", + "main_menu.save_load_error.retry", + "RETRY" + ), + + CUTSCENE_MARKER("LOADED"), + CUTSCENE_CALLBACK(uiMainMenuOpenSelectSave) +); + uimainmenu_t UI_MAIN_MENU; void uiMainMenuQuitConfirmed(const bool_t result, void *user) { @@ -40,6 +84,21 @@ void uiMainMenuSelectSaveResult(const uint8_t slotIndex, void *user) { sceneSet(SCENE_TYPE_OVERWORLD); } +void uiMainMenuOpenSelectSave(void *userData) { + uiSelectSaveOpen( + UI_SELECT_SAVE_TYPE_LOAD, uiMainMenuSelectSaveResult, NULL + ); +} + +void uiMainMenuClosed(const uimenu_t *menu) { + if(UI_MAIN_MENU.suppressClosedSceneChange) { + UI_MAIN_MENU.suppressClosedSceneChange = false; + return; + } + + sceneSet(SCENE_TYPE_INITIAL); +} + void uiMainMenuSelected( const uimenu_t *menu, const uint8_t index, @@ -48,13 +107,9 @@ void uiMainMenuSelected( switch(index) { case UI_MAIN_MENU_INDEX_START_GAME: + UI_MAIN_MENU.suppressClosedSceneChange = true; uiMenuClose(&UI_MAIN_MENU.menu); - - errorCatch(errorPrint(saveLoadAllSlots())); - - uiSelectSaveOpen( - UI_SELECT_SAVE_TYPE_LOAD, uiMainMenuSelectSaveResult, NULL - ); + cutsceneSystemStartCutscene(&CUTSCENE_MAIN_MENU_START_GAME); break; case UI_MAIN_MENU_INDEX_OPTIONS: @@ -96,7 +151,8 @@ errorret_t uiMainMenuInit(void) { )); MENU_BEGIN( - &UI_MAIN_MENU.menu, UI_MAIN_MENU.items, uiMainMenuSelected, NULL, NULL + &UI_MAIN_MENU.menu, UI_MAIN_MENU.items, uiMainMenuSelected, + uiMainMenuClosed, NULL ); MENU_BUTTON(UI_MAIN_MENU.startGameLabel); MENU_BUTTON(UI_MAIN_MENU.optionsLabel); diff --git a/src/dusk/ui/screen/mainmenu/uimainmenu.h b/src/dusk/ui/screen/mainmenu/uimainmenu.h index 3a53c9ee..ce06ae3f 100644 --- a/src/dusk/ui/screen/mainmenu/uimainmenu.h +++ b/src/dusk/ui/screen/mainmenu/uimainmenu.h @@ -20,6 +20,12 @@ typedef struct { char_t startGameLabel[UI_MAIN_MENU_LABEL_MAX]; char_t optionsLabel[UI_MAIN_MENU_LABEL_MAX]; char_t quitLabel[UI_MAIN_MENU_LABEL_MAX]; + + // Set right before a uiMenuClose() call that hands off to something + // else (e.g. starting the start-game cutscene) rather than backing + // all the way out, so the menu's closed handler knows to skip + // returning to the initial scene for that one close. + bool_t suppressClosedSceneChange; } uimainmenu_t; extern uimainmenu_t UI_MAIN_MENU;