Add declarative cutscene items for save checks and marker-based modals
CUTSCENE_SAVE_DEVICE_CHECK and CUTSCENE_SAVE_LOAD_ALL_SLOTS wrap the existing device-lookup/slot-load calls and jump straight to a success/failure marker, and CUTSCENE_MODAL_OPTIONS_ONE/TWO do the same for one/two-option modals, removing the need for a hand-written goTo-only callback per use. Main menu's "Start Game" now runs a cutscene (mirroring the initial scene's save-check flow) that loads all slots before opening the load-game picker, retrying on error; the initial scene's own device check is migrated onto the same items. Backing out of the main menu now returns to the initial scene. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,33 @@ msgstr "Quit Game"
|
|||||||
msgid "main_menu.quit_confirm"
|
msgid "main_menu.quit_confirm"
|
||||||
msgstr "Are you sure you want to quit?"
|
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
|
# Select Save Screen
|
||||||
msgid "ui.select_save.title"
|
msgid "ui.select_save.title"
|
||||||
|
|||||||
@@ -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
|
// Closes the currently open modal (if any). Useful when a modal was
|
||||||
// opened outside of a blocking CUTSCENE_MODAL item (e.g. directly via
|
// opened outside of a blocking CUTSCENE_MODAL item (e.g. directly via
|
||||||
// uiModalOpen) and this cutscene just needs to dismiss it and continue
|
// uiModalOpen) and this cutscene just needs to dismiss it and continue
|
||||||
@@ -127,6 +163,34 @@ typedef struct cutscene_s {
|
|||||||
#define CUTSCENE_MODAL_CLOSE() \
|
#define CUTSCENE_MODAL_CLOSE() \
|
||||||
{ .type = CUTSCENE_ITEM_TYPE_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) \
|
#define CUTSCENE_ENTITY_WALK_TO(ENTITY_INDEX, X, Y, Z) \
|
||||||
{ \
|
{ \
|
||||||
.type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \
|
.type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ add_subdirectory(item)
|
|||||||
add_subdirectory(maparea)
|
add_subdirectory(maparea)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
add_subdirectory(battle)
|
add_subdirectory(battle)
|
||||||
|
add_subdirectory(save)
|
||||||
|
|||||||
@@ -134,6 +134,11 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
|||||||
.update = cutsceneModalUpdate
|
.update = cutsceneModalUpdate
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS] = {
|
||||||
|
.init = cutsceneModalOptionsMarkersStart,
|
||||||
|
.update = cutsceneModalOptionsMarkersUpdate
|
||||||
|
},
|
||||||
|
|
||||||
[CUTSCENE_ITEM_TYPE_MODAL_CLOSE] = {
|
[CUTSCENE_ITEM_TYPE_MODAL_CLOSE] = {
|
||||||
.init = cutsceneModalCloseStart,
|
.init = cutsceneModalCloseStart,
|
||||||
.update = cutsceneModalCloseUpdate
|
.update = cutsceneModalCloseUpdate
|
||||||
@@ -157,6 +162,16 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
|||||||
[CUTSCENE_ITEM_TYPE_SCENE] = {
|
[CUTSCENE_ITEM_TYPE_SCENE] = {
|
||||||
.init = cutsceneSceneStart,
|
.init = cutsceneSceneStart,
|
||||||
.update = cutsceneSceneUpdate
|
.update = cutsceneSceneUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK] = {
|
||||||
|
.init = cutsceneSaveDeviceCheckStart,
|
||||||
|
.update = cutsceneSaveDeviceCheckUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS] = {
|
||||||
|
.init = cutsceneSaveLoadAllSlotsStart,
|
||||||
|
.update = cutsceneSaveLoadAllSlotsUpdate
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "ui/cutsceneemoji.h"
|
#include "ui/cutsceneemoji.h"
|
||||||
#include "ui/cutsceneshake.h"
|
#include "ui/cutsceneshake.h"
|
||||||
#include "ui/cutscenemodal.h"
|
#include "ui/cutscenemodal.h"
|
||||||
|
#include "ui/cutscenemodaloptionsmarkers.h"
|
||||||
#include "item/cutsceneitemgive.h"
|
#include "item/cutsceneitemgive.h"
|
||||||
#include "maparea/cutscenemapareaadd.h"
|
#include "maparea/cutscenemapareaadd.h"
|
||||||
#include "maparea/cutscenemaparearemove.h"
|
#include "maparea/cutscenemaparearemove.h"
|
||||||
@@ -34,6 +35,8 @@
|
|||||||
#include "battle/cutscenestartbattle.h"
|
#include "battle/cutscenestartbattle.h"
|
||||||
#include "battle/cutscenebattlewaitstate.h"
|
#include "battle/cutscenebattlewaitstate.h"
|
||||||
#include "battle/cutscenebattleforceaction.h"
|
#include "battle/cutscenebattleforceaction.h"
|
||||||
|
#include "save/cutscenesavedevicecheck.h"
|
||||||
|
#include "save/cutscenesaveloadallslots.h"
|
||||||
|
|
||||||
typedef struct cutscene_s cutscene_t;
|
typedef struct cutscene_s cutscene_t;
|
||||||
|
|
||||||
@@ -65,11 +68,14 @@ typedef enum {
|
|||||||
CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE,
|
CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE,
|
||||||
CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION,
|
CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION,
|
||||||
CUTSCENE_ITEM_TYPE_MODAL,
|
CUTSCENE_ITEM_TYPE_MODAL,
|
||||||
|
CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS,
|
||||||
CUTSCENE_ITEM_TYPE_MODAL_CLOSE,
|
CUTSCENE_ITEM_TYPE_MODAL_CLOSE,
|
||||||
CUTSCENE_ITEM_TYPE_PRINT,
|
CUTSCENE_ITEM_TYPE_PRINT,
|
||||||
CUTSCENE_ITEM_TYPE_MARKER,
|
CUTSCENE_ITEM_TYPE_MARKER,
|
||||||
CUTSCENE_ITEM_TYPE_RESTART,
|
CUTSCENE_ITEM_TYPE_RESTART,
|
||||||
CUTSCENE_ITEM_TYPE_SCENE,
|
CUTSCENE_ITEM_TYPE_SCENE,
|
||||||
|
CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK,
|
||||||
|
CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS,
|
||||||
|
|
||||||
CUTSCENE_ITEM_TYPE_COUNT
|
CUTSCENE_ITEM_TYPE_COUNT
|
||||||
} cutsceneitemtype_t;
|
} cutsceneitemtype_t;
|
||||||
@@ -103,9 +109,12 @@ struct cutsceneitem_s {
|
|||||||
cutscenebattlewaitstate_t battleWaitState;
|
cutscenebattlewaitstate_t battleWaitState;
|
||||||
cutscenebattleforceaction_t battleForceAction;
|
cutscenebattleforceaction_t battleForceAction;
|
||||||
cutscenemodal_t modal;
|
cutscenemodal_t modal;
|
||||||
|
cutscenemodaloptionsmarkers_t modalOptionsMarkers;
|
||||||
cutsceneprint_t print;
|
cutsceneprint_t print;
|
||||||
cutscenemarker_t marker;
|
cutscenemarker_t marker;
|
||||||
cutscenescene_t sceneChange;
|
cutscenescene_t sceneChange;
|
||||||
|
cutscenesavedevicecheck_t saveDeviceCheck;
|
||||||
|
cutscenesaveloadallslots_t saveLoadAllSlots;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
@@ -12,4 +12,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
cutsceneemoji.c
|
cutsceneemoji.c
|
||||||
cutsceneshake.c
|
cutsceneshake.c
|
||||||
cutscenemodal.c
|
cutscenemodal.c
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
@@ -11,29 +11,24 @@
|
|||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "save/save.h"
|
|
||||||
#include "ui/widget/uimodal.h"
|
#include "ui/widget/uimodal.h"
|
||||||
#include "rpg/cutscene/cutscene.h"
|
#include "rpg/cutscene/cutscene.h"
|
||||||
#include "rpg/cutscene/cutscenesystem.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(INITIAL, 0, DEFAULT,
|
||||||
CUTSCENE_MODAL(
|
CUTSCENE_MODAL(
|
||||||
"initial.checking_save.title", "initial.checking_save.message"
|
"initial.checking_save.title", "initial.checking_save.message"
|
||||||
),
|
),
|
||||||
CUTSCENE_WAIT(1.0f),
|
CUTSCENE_WAIT(1.0f),
|
||||||
CUTSCENE_CALLBACK(sceneInitialFindDevices),
|
CUTSCENE_SAVE_DEVICE_CHECK("CONTINUE", "NO_DEVICE"),
|
||||||
|
|
||||||
CUTSCENE_MARKER("NO_DEVICE"),
|
CUTSCENE_MARKER("NO_DEVICE"),
|
||||||
CUTSCENE_MODAL_CLOSE(),
|
CUTSCENE_MODAL_CLOSE(),
|
||||||
CUTSCENE_MODAL_OPTIONS(
|
CUTSCENE_MODAL_OPTIONS_TWO(
|
||||||
"initial.no_device.title",
|
"initial.no_device.title",
|
||||||
"initial.no_device.message",
|
"initial.no_device.message",
|
||||||
sceneInitialSaveDeviceRetryCallback,
|
"initial.no_device.retry", "RETRY",
|
||||||
"initial.no_device.retry", "initial.no_device.continue"
|
"initial.no_device.continue", "CONTINUE"
|
||||||
),
|
),
|
||||||
|
|
||||||
CUTSCENE_MARKER("RETRY"),
|
CUTSCENE_MARKER("RETRY"),
|
||||||
@@ -45,18 +40,6 @@ CUTSCENE(INITIAL, 0, DEFAULT,
|
|||||||
CUTSCENE_SCENE(SCENE_TYPE_MAIN_MENU)
|
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) {
|
errorret_t sceneInitialInit(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
memoryZero(&sceneData->initial, sizeof(sceneinitial_t));
|
memoryZero(&sceneData->initial, sizeof(sceneinitial_t));
|
||||||
|
|||||||
@@ -17,13 +17,57 @@
|
|||||||
#include "asset/loader/locale/assetlocaleloader.h"
|
#include "asset/loader/locale/assetlocaleloader.h"
|
||||||
#include "ui/dialog/uiconfirm.h"
|
#include "ui/dialog/uiconfirm.h"
|
||||||
#include "ui/dialog/save/uiselectsave.h"
|
#include "ui/dialog/save/uiselectsave.h"
|
||||||
|
#include "rpg/cutscene/cutscene.h"
|
||||||
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "save/save.h"
|
|
||||||
|
|
||||||
#define UI_MAIN_MENU_INDEX_START_GAME 0
|
#define UI_MAIN_MENU_INDEX_START_GAME 0
|
||||||
#define UI_MAIN_MENU_INDEX_OPTIONS 1
|
#define UI_MAIN_MENU_INDEX_OPTIONS 1
|
||||||
#define UI_MAIN_MENU_INDEX_QUIT 2
|
#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;
|
uimainmenu_t UI_MAIN_MENU;
|
||||||
|
|
||||||
void uiMainMenuQuitConfirmed(const bool_t result, void *user) {
|
void uiMainMenuQuitConfirmed(const bool_t result, void *user) {
|
||||||
@@ -40,6 +84,21 @@ void uiMainMenuSelectSaveResult(const uint8_t slotIndex, void *user) {
|
|||||||
sceneSet(SCENE_TYPE_OVERWORLD);
|
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(
|
void uiMainMenuSelected(
|
||||||
const uimenu_t *menu,
|
const uimenu_t *menu,
|
||||||
const uint8_t index,
|
const uint8_t index,
|
||||||
@@ -48,13 +107,9 @@ void uiMainMenuSelected(
|
|||||||
|
|
||||||
switch(index) {
|
switch(index) {
|
||||||
case UI_MAIN_MENU_INDEX_START_GAME:
|
case UI_MAIN_MENU_INDEX_START_GAME:
|
||||||
|
UI_MAIN_MENU.suppressClosedSceneChange = true;
|
||||||
uiMenuClose(&UI_MAIN_MENU.menu);
|
uiMenuClose(&UI_MAIN_MENU.menu);
|
||||||
|
cutsceneSystemStartCutscene(&CUTSCENE_MAIN_MENU_START_GAME);
|
||||||
errorCatch(errorPrint(saveLoadAllSlots()));
|
|
||||||
|
|
||||||
uiSelectSaveOpen(
|
|
||||||
UI_SELECT_SAVE_TYPE_LOAD, uiMainMenuSelectSaveResult, NULL
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UI_MAIN_MENU_INDEX_OPTIONS:
|
case UI_MAIN_MENU_INDEX_OPTIONS:
|
||||||
@@ -96,7 +151,8 @@ errorret_t uiMainMenuInit(void) {
|
|||||||
));
|
));
|
||||||
|
|
||||||
MENU_BEGIN(
|
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.startGameLabel);
|
||||||
MENU_BUTTON(UI_MAIN_MENU.optionsLabel);
|
MENU_BUTTON(UI_MAIN_MENU.optionsLabel);
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ typedef struct {
|
|||||||
char_t startGameLabel[UI_MAIN_MENU_LABEL_MAX];
|
char_t startGameLabel[UI_MAIN_MENU_LABEL_MAX];
|
||||||
char_t optionsLabel[UI_MAIN_MENU_LABEL_MAX];
|
char_t optionsLabel[UI_MAIN_MENU_LABEL_MAX];
|
||||||
char_t quitLabel[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;
|
} uimainmenu_t;
|
||||||
|
|
||||||
extern uimainmenu_t UI_MAIN_MENU;
|
extern uimainmenu_t UI_MAIN_MENU;
|
||||||
|
|||||||
Reference in New Issue
Block a user