Add cutscene flow control and a scriptable modal dialog item
uimodal gains an onOpen hook and lets uiModalClose take a one-shot callback, since options are now stored as raw (uncopied) string pointers instead of a fixed char buffer. New cutscene items: - CUTSCENE_MODAL / CUTSCENE_MODAL_OPTIONS / CUTSCENE_MODAL_CLOSE: opens a message-only or option-driven uimodal. A message-only modal advances the cutscene immediately; one with options blocks indefinitely since only its option callback (or something it triggers) should decide what happens next. - CUTSCENE_MARKER + cutsceneGoTo: a named, otherwise no-op position that execution can jump straight to from anywhere in the same cutscene (e.g. from a CUTSCENE_CALLBACK), matched by name rather than pointer identity. - CUTSCENE_RESTART + cutsceneRestart: restarts the running cutscene from its first item, preserving its interact/interacted entities. - CUTSCENE_SCENE: requests a scene switch via sceneSet as a cutscene step. - CUTSCENE_PRINT: prints a line to the console as a cutscene step. sceneinitial.c's boot-time save device check is rebuilt on top of these: show a modal, kick off the async device search, then branch via markers/goto to a retry/continue prompt depending on the result. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,25 @@ typedef struct cutscene_s {
|
|||||||
#define CUTSCENE_WAIT(WAIT) \
|
#define CUTSCENE_WAIT(WAIT) \
|
||||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = WAIT }
|
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = WAIT }
|
||||||
|
|
||||||
|
// A named, otherwise no-op position in the item list that cutsceneGoTo
|
||||||
|
// can jump execution straight to. NAME is matched with stringEquals,
|
||||||
|
// not pointer identity, so it's safe to use separate string literals
|
||||||
|
// with the same contents at the marker and at each call site.
|
||||||
|
#define CUTSCENE_MARKER(NAME) \
|
||||||
|
{ .type = CUTSCENE_ITEM_TYPE_MARKER, .marker = { .name = NAME } }
|
||||||
|
|
||||||
|
// Restarts the currently running cutscene from its first item,
|
||||||
|
// preserving whatever interact/interacted entities triggered it.
|
||||||
|
#define CUTSCENE_RESTART() \
|
||||||
|
{ .type = CUTSCENE_ITEM_TYPE_RESTART }
|
||||||
|
|
||||||
|
// Requests a switch to a different SCENE_TYPE via sceneSet, then
|
||||||
|
// immediately continues on to whatever follows this item - the switch
|
||||||
|
// itself doesn't happen until the next sceneUpdate() tick, so it does
|
||||||
|
// not take effect this frame.
|
||||||
|
#define CUTSCENE_SCENE(TYPE) \
|
||||||
|
{ .type = CUTSCENE_ITEM_TYPE_SCENE, .sceneChange = { .type = TYPE } }
|
||||||
|
|
||||||
#define CUTSCENE_CUTSCENE(CUTSCENE) \
|
#define CUTSCENE_CUTSCENE(CUTSCENE) \
|
||||||
{ \
|
{ \
|
||||||
.type = CUTSCENE_ITEM_TYPE_CUTSCENE, \
|
.type = CUTSCENE_ITEM_TYPE_CUTSCENE, \
|
||||||
@@ -62,6 +81,47 @@ typedef struct cutscene_s {
|
|||||||
#define CUTSCENE_CALLBACK(CALLBACK) \
|
#define CUTSCENE_CALLBACK(CALLBACK) \
|
||||||
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = CALLBACK }
|
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = CALLBACK }
|
||||||
|
|
||||||
|
#define CUTSCENE_PRINT(TEXT) \
|
||||||
|
{ .type = CUTSCENE_ITEM_TYPE_PRINT, .print = { .text = TEXT } }
|
||||||
|
|
||||||
|
// Shows a message-only modal (no option buttons) and immediately
|
||||||
|
// continues on to whatever follows this item - it does not wait for
|
||||||
|
// the dialog to be dismissed. Script the rest of the interaction (e.g.
|
||||||
|
// CUTSCENE_CALLBACK to kick off work, CUTSCENE_WAIT, then
|
||||||
|
// CUTSCENE_MODAL_CLOSE) as later items in the same cutscene.
|
||||||
|
#define CUTSCENE_MODAL(TITLE, MESSAGE) \
|
||||||
|
{ \
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_MODAL, \
|
||||||
|
.modal = { .title = TITLE, .message = MESSAGE } \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows a modal with option buttons and immediately continues on, same
|
||||||
|
// as CUTSCENE_MODAL - it does not block waiting for a selection.
|
||||||
|
// CALLBACK fires with the selected option index (or UI_MODAL_RESULT_NONE
|
||||||
|
// if backed out of) once the dialog closes. Option labels are passed as
|
||||||
|
// trailing arguments, e.g. CUTSCENE_MODAL_OPTIONS(title, message,
|
||||||
|
// callback, "Retry", "Cancel") - their strings are not copied, so they
|
||||||
|
// must outlive the modal (string literals are fine).
|
||||||
|
#define CUTSCENE_MODAL_OPTIONS(TITLE, MESSAGE, CALLBACK, ...) \
|
||||||
|
{ \
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_MODAL, \
|
||||||
|
.modal = { \
|
||||||
|
.title = TITLE, .message = MESSAGE, \
|
||||||
|
.options = (const char_t *[]){ __VA_ARGS__ }, \
|
||||||
|
.optionCount = (uint8_t)( \
|
||||||
|
sizeof((const char_t *[]){ __VA_ARGS__ }) / sizeof(const char_t *) \
|
||||||
|
), \
|
||||||
|
.callback = CALLBACK \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// on to whatever follows this item in the sequence.
|
||||||
|
#define CUTSCENE_MODAL_CLOSE() \
|
||||||
|
{ .type = CUTSCENE_ITEM_TYPE_MODAL_CLOSE }
|
||||||
|
|
||||||
#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, \
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "cutscenesystem.h"
|
#include "cutscenesystem.h"
|
||||||
#include "rpg/entity/entity.h"
|
#include "rpg/entity/entity.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
|
|
||||||
cutscenesystem_t CUTSCENE_SYSTEM;
|
cutscenesystem_t CUTSCENE_SYSTEM;
|
||||||
@@ -42,6 +43,17 @@ void cutsceneSystemStartCutsceneWith(
|
|||||||
cutsceneSystemNext();
|
cutsceneSystemNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cutsceneRestart(void) {
|
||||||
|
assertNotNull(
|
||||||
|
CUTSCENE_SYSTEM.scene, "cutsceneRestart called with no cutscene running"
|
||||||
|
);
|
||||||
|
cutsceneSystemStartCutsceneWith(
|
||||||
|
CUTSCENE_SYSTEM.scene,
|
||||||
|
CUTSCENE_SYSTEM.entityInteract,
|
||||||
|
CUTSCENE_SYSTEM.entityInteracted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void cutsceneSystemUpdate() {
|
void cutsceneSystemUpdate() {
|
||||||
if(CUTSCENE_SYSTEM.scene == NULL) return;
|
if(CUTSCENE_SYSTEM.scene == NULL) return;
|
||||||
|
|
||||||
@@ -76,6 +88,27 @@ void cutsceneSystemNext() {
|
|||||||
cutsceneItemStart(item, &CUTSCENE_SYSTEM.data);
|
cutsceneItemStart(item, &CUTSCENE_SYSTEM.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cutsceneGoTo(const char_t *name) {
|
||||||
|
assertNotNull(
|
||||||
|
CUTSCENE_SYSTEM.scene, "cutsceneGoTo called with no cutscene running"
|
||||||
|
);
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < CUTSCENE_SYSTEM.scene->itemCount; i++) {
|
||||||
|
const cutsceneitem_t *item = &CUTSCENE_SYSTEM.scene->items[i];
|
||||||
|
if(
|
||||||
|
item->type == CUTSCENE_ITEM_TYPE_MARKER &&
|
||||||
|
stringEquals(item->marker.name, name)
|
||||||
|
) {
|
||||||
|
CUTSCENE_SYSTEM.currentItem = i;
|
||||||
|
memoryZero(&CUTSCENE_SYSTEM.data, sizeof(CUTSCENE_SYSTEM.data));
|
||||||
|
cutsceneItemStart(item, &CUTSCENE_SYSTEM.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(false, "cutsceneGoTo: no marker found with that name");
|
||||||
|
}
|
||||||
|
|
||||||
const cutsceneitem_t * cutsceneSystemGetCurrentItem() {
|
const cutsceneitem_t * cutsceneSystemGetCurrentItem() {
|
||||||
if(CUTSCENE_SYSTEM.scene == NULL) return NULL;
|
if(CUTSCENE_SYSTEM.scene == NULL) return NULL;
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,13 @@ void cutsceneSystemStartCutsceneWith(
|
|||||||
entity_t *interacted
|
entity_t *interacted
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the currently running cutscene from its first item,
|
||||||
|
* preserving whatever interact/interacted entities triggered it.
|
||||||
|
* Asserts if no cutscene is running.
|
||||||
|
*/
|
||||||
|
void cutsceneRestart(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a raw entity index (or sentinel) to an entity pointer.
|
* Resolves a raw entity index (or sentinel) to an entity pointer.
|
||||||
* Handles CUTSCENE_ENTITY_INTERACT, CUTSCENE_ENTITY_INTERACTED,
|
* Handles CUTSCENE_ENTITY_INTERACT, CUTSCENE_ENTITY_INTERACTED,
|
||||||
@@ -102,6 +109,18 @@ uint8_t cutsceneSystemGetTextMiniId(const uint8_t index);
|
|||||||
*/
|
*/
|
||||||
void cutsceneSystemNext();
|
void cutsceneSystemNext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jumps the running cutscene directly to the CUTSCENE_MARKER item with
|
||||||
|
* the given name and starts it immediately, as if cutsceneSystemNext()
|
||||||
|
* had advanced straight to it. Intended to be called from within
|
||||||
|
* another item's start/update (e.g. a CUTSCENE_CALLBACK) to implement
|
||||||
|
* flow control. Asserts if no cutscene is running or no marker with
|
||||||
|
* that name exists in it.
|
||||||
|
*
|
||||||
|
* @param name Marker name to search for, matched with stringEquals.
|
||||||
|
*/
|
||||||
|
void cutsceneGoTo(const char_t *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the cutscene system for one frame.
|
* Update the cutscene system for one frame.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
PUBLIC
|
PUBLIC
|
||||||
cutsceneitem.c
|
cutsceneitem.c
|
||||||
cutscenecallback.c
|
cutscenecallback.c
|
||||||
|
cutsceneprint.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(control)
|
add_subdirectory(control)
|
||||||
|
|||||||
@@ -8,4 +8,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
cutscenewait.c
|
cutscenewait.c
|
||||||
cutscenesetpause.c
|
cutscenesetpause.c
|
||||||
cutsceneconcurrent.c
|
cutsceneconcurrent.c
|
||||||
|
cutscenemarker.c
|
||||||
|
cutscenerestart.c
|
||||||
|
cutscenescene.c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||||
|
|
||||||
|
void cutsceneMarkerStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneMarkerUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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 *name;
|
||||||
|
} cutscenemarker_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a marker item. A marker does nothing on its own - it exists
|
||||||
|
* purely as a named position for cutsceneGoTo to jump to.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutsceneMarkerStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a marker item (always completes immediately).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns true always.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneMarkerUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
void cutsceneRestartStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
cutsceneRestart();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneRestartUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a restart item (restarts the currently running cutscene from
|
||||||
|
* its first item via cutsceneRestart).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutsceneRestartStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a restart item. By the time this would run, the cutscene has
|
||||||
|
* already restarted from its first item, so this always reports
|
||||||
|
* incomplete (mirrors cutsceneCutsceneUpdate).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns false always.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneRestartUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 "scene/scene.h"
|
||||||
|
|
||||||
|
void cutsceneSceneStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
sceneSet(item->sceneChange.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneSceneUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
#include "scene/scenetype.h"
|
||||||
|
|
||||||
|
typedef struct cutsceneitem_s cutsceneitem_t;
|
||||||
|
typedef union cutsceneitemdata_u cutsceneitemdata_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scenetype_t type;
|
||||||
|
} cutscenescene_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a scene item (requests a switch to the given scene via
|
||||||
|
* sceneSet). The switch itself doesn't happen until the next
|
||||||
|
* sceneUpdate() tick, so the rest of the current frame - including
|
||||||
|
* whatever else this cutscene does after this item - still runs
|
||||||
|
* against the scene that's being left.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutsceneSceneStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a scene item (always completes immediately).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns true always.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneSceneUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
@@ -127,6 +127,36 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
|||||||
[CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION] = {
|
[CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION] = {
|
||||||
.init = cutsceneBattleForceActionStart,
|
.init = cutsceneBattleForceActionStart,
|
||||||
.update = cutsceneBattleForceActionUpdate
|
.update = cutsceneBattleForceActionUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_MODAL] = {
|
||||||
|
.init = cutsceneModalStart,
|
||||||
|
.update = cutsceneModalUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_MODAL_CLOSE] = {
|
||||||
|
.init = cutsceneModalCloseStart,
|
||||||
|
.update = cutsceneModalCloseUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_PRINT] = {
|
||||||
|
.init = cutscenePrintStart,
|
||||||
|
.update = cutscenePrintUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_MARKER] = {
|
||||||
|
.init = cutsceneMarkerStart,
|
||||||
|
.update = cutsceneMarkerUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_RESTART] = {
|
||||||
|
.init = cutsceneRestartStart,
|
||||||
|
.update = cutsceneRestartUpdate
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_SCENE] = {
|
||||||
|
.init = cutsceneSceneStart,
|
||||||
|
.update = cutsceneSceneUpdate
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "cutscenecallback.h"
|
#include "cutscenecallback.h"
|
||||||
|
#include "cutsceneprint.h"
|
||||||
#include "control/cutscenewait.h"
|
#include "control/cutscenewait.h"
|
||||||
#include "control/cutscenesetpause.h"
|
#include "control/cutscenesetpause.h"
|
||||||
#include "control/cutsceneconcurrent.h"
|
#include "control/cutsceneconcurrent.h"
|
||||||
|
#include "control/cutscenemarker.h"
|
||||||
|
#include "control/cutscenerestart.h"
|
||||||
|
#include "control/cutscenescene.h"
|
||||||
#include "entity/cutsceneentityteleport.h"
|
#include "entity/cutsceneentityteleport.h"
|
||||||
#include "entity/cutsceneentitywalkto.h"
|
#include "entity/cutsceneentitywalkto.h"
|
||||||
#include "entity/cutsceneentityremove.h"
|
#include "entity/cutsceneentityremove.h"
|
||||||
@@ -22,6 +26,7 @@
|
|||||||
#include "ui/cutscenefade.h"
|
#include "ui/cutscenefade.h"
|
||||||
#include "ui/cutsceneemoji.h"
|
#include "ui/cutsceneemoji.h"
|
||||||
#include "ui/cutsceneshake.h"
|
#include "ui/cutsceneshake.h"
|
||||||
|
#include "ui/cutscenemodal.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"
|
||||||
@@ -59,6 +64,12 @@ typedef enum {
|
|||||||
CUTSCENE_ITEM_TYPE_SHAKE,
|
CUTSCENE_ITEM_TYPE_SHAKE,
|
||||||
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_CLOSE,
|
||||||
|
CUTSCENE_ITEM_TYPE_PRINT,
|
||||||
|
CUTSCENE_ITEM_TYPE_MARKER,
|
||||||
|
CUTSCENE_ITEM_TYPE_RESTART,
|
||||||
|
CUTSCENE_ITEM_TYPE_SCENE,
|
||||||
|
|
||||||
CUTSCENE_ITEM_TYPE_COUNT
|
CUTSCENE_ITEM_TYPE_COUNT
|
||||||
} cutsceneitemtype_t;
|
} cutsceneitemtype_t;
|
||||||
@@ -91,6 +102,10 @@ struct cutsceneitem_s {
|
|||||||
cutsceneshake_t shake;
|
cutsceneshake_t shake;
|
||||||
cutscenebattlewaitstate_t battleWaitState;
|
cutscenebattlewaitstate_t battleWaitState;
|
||||||
cutscenebattleforceaction_t battleForceAction;
|
cutscenebattleforceaction_t battleForceAction;
|
||||||
|
cutscenemodal_t modal;
|
||||||
|
cutsceneprint_t print;
|
||||||
|
cutscenemarker_t marker;
|
||||||
|
cutscenescene_t sceneChange;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cutsceneitem.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
void cutscenePrintStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
consolePrint("%s", item->print.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutscenePrintUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
#define CUTSCENE_PRINT_MAX_CHARS 128
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_t text[CUTSCENE_PRINT_MAX_CHARS];
|
||||||
|
} cutsceneprint_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a print item (prints the item's text to the console).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutscenePrintStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a print item (always completes immediately).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns true always.
|
||||||
|
*/
|
||||||
|
bool_t cutscenePrintUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
@@ -11,4 +11,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
cutscenefade.c
|
cutscenefade.c
|
||||||
cutsceneemoji.c
|
cutsceneemoji.c
|
||||||
cutsceneshake.c
|
cutsceneshake.c
|
||||||
|
cutscenemodal.c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* 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 "assert/assert.h"
|
||||||
|
#include "ui/frame/uimodal.h"
|
||||||
|
|
||||||
|
void cutsceneModalStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
const cutscenemodal_t *modal = &item->modal;
|
||||||
|
assertTrue(
|
||||||
|
modal->optionCount <= CUTSCENE_MODAL_OPTIONS_MAX,
|
||||||
|
"Too many options for cutscene modal"
|
||||||
|
);
|
||||||
|
|
||||||
|
uiModalOpen(
|
||||||
|
modal->title,
|
||||||
|
modal->message,
|
||||||
|
modal->options,
|
||||||
|
modal->optionCount,
|
||||||
|
modal->callback,
|
||||||
|
NULL,
|
||||||
|
CUTSCENE_SYSTEM.userData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneModalUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return item->modal.optionCount == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cutsceneModalCloseStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
uiModalClose(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneModalCloseUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
#define CUTSCENE_MODAL_TITLE_MAX_CHARS 64
|
||||||
|
#define CUTSCENE_MODAL_MESSAGE_MAX_CHARS 256
|
||||||
|
#define CUTSCENE_MODAL_OPTIONS_MAX 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked with the selected option once a cutscene modal
|
||||||
|
* item's dialog closes.
|
||||||
|
*
|
||||||
|
* @param optionIndex Index into the options array that was selected,
|
||||||
|
* or UI_MODAL_RESULT_NONE if backed out of without selecting one.
|
||||||
|
* @param userData CUTSCENE_SYSTEM.userData for the running cutscene.
|
||||||
|
*/
|
||||||
|
typedef void (*cutscenemodaloptioncallback_t)(
|
||||||
|
const uint8_t optionIndex, void *userData
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_t title[CUTSCENE_MODAL_TITLE_MAX_CHARS];
|
||||||
|
char_t message[CUTSCENE_MODAL_MESSAGE_MAX_CHARS];
|
||||||
|
|
||||||
|
// NOT copied - these pointers are stored directly by the underlying
|
||||||
|
// buttons, so they must stay valid for as long as the modal is open
|
||||||
|
// (e.g. string literals, as CUTSCENE_MODAL_OPTIONS produces).
|
||||||
|
const char_t **options;
|
||||||
|
uint8_t optionCount;
|
||||||
|
|
||||||
|
cutscenemodaloptioncallback_t callback;
|
||||||
|
} cutscenemodal_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a modal item (shows the modal dialog with the item's title,
|
||||||
|
* message, and options, wiring callback to fire with the selected
|
||||||
|
* option once it closes). optionCount may be 0 for a message-only
|
||||||
|
* dialog with no option buttons.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutsceneModalStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a modal item. A message-only modal (optionCount == 0, e.g.
|
||||||
|
* from CUTSCENE_MODAL) always completes immediately - the cutscene
|
||||||
|
* continues on to whatever follows this item right away, it does not
|
||||||
|
* wait for the dialog to be dismissed. Script what should happen while
|
||||||
|
* it's up, and how it gets closed, as later items in the same cutscene
|
||||||
|
* (e.g. CUTSCENE_CALLBACK, CUTSCENE_WAIT, CUTSCENE_MODAL_CLOSE).
|
||||||
|
*
|
||||||
|
* A modal with options (optionCount > 0, e.g. from
|
||||||
|
* CUTSCENE_MODAL_OPTIONS) never completes on its own - the cutscene
|
||||||
|
* blocks here indefinitely. The option callback fires once the user
|
||||||
|
* picks something, and it alone is responsible for moving the cutscene
|
||||||
|
* on from there (typically via cutsceneGoTo or cutsceneRestart).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns true once immediately for a message-only modal; false
|
||||||
|
* always for a modal with options.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneModalUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a modal-close item (closes the currently open modal, if any).
|
||||||
|
* Whatever should happen next belongs in the cutscene's own item
|
||||||
|
* sequence (e.g. a CUTSCENE_CALLBACK or CUTSCENE_PRINT placed right
|
||||||
|
* after this item), not on this item itself.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutsceneModalCloseStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a modal-close item (always completes immediately).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns true always.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneModalCloseUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
@@ -9,42 +9,65 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
|
#include "display/screen/screen.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "save/save.h"
|
#include "save/save.h"
|
||||||
|
#include "ui/frame/uimodal.h"
|
||||||
|
#include "rpg/cutscene/cutscene.h"
|
||||||
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
|
|
||||||
int32_t testData = 69;
|
void sceneInitialAvailableDeviceCallback(savedevice_t *device, void *user);
|
||||||
|
void sceneInitialFindDevices(void *userData);
|
||||||
|
void sceneInitialSaveDeviceRetryCallback(const uint8_t opt, void *u);
|
||||||
|
|
||||||
void testCallback(savedevice_t *device, void *user) {
|
CUTSCENE(INITIAL, 0, DEFAULT,
|
||||||
if(device == NULL) {
|
CUTSCENE_MODAL(
|
||||||
consolePrint("No save device found.");
|
"Checking for save data...",
|
||||||
} else {
|
"Checking for save data... Please wait."
|
||||||
uint8_t index = (uint8_t)(device - &SAVE.devices[0]);
|
),
|
||||||
consolePrint(
|
CUTSCENE_WAIT(1.0f),
|
||||||
"Found save device %u: %s", (uint32_t)index, device->reasonKey
|
CUTSCENE_CALLBACK(sceneInitialFindDevices),
|
||||||
|
|
||||||
|
CUTSCENE_MARKER("NO_DEVICE"),
|
||||||
|
CUTSCENE_MODAL_CLOSE(),
|
||||||
|
CUTSCENE_MODAL_OPTIONS(
|
||||||
|
"No save device found",
|
||||||
|
"Please ensure a save device is connected and try again.",
|
||||||
|
sceneInitialSaveDeviceRetryCallback,
|
||||||
|
"Retry", "Continue without saving"
|
||||||
|
),
|
||||||
|
|
||||||
|
CUTSCENE_MARKER("RETRY"),
|
||||||
|
CUTSCENE_MODAL_CLOSE(),
|
||||||
|
CUTSCENE_RESTART(),
|
||||||
|
|
||||||
|
CUTSCENE_MARKER("CONTINUE"),
|
||||||
|
CUTSCENE_MODAL_CLOSE(),
|
||||||
|
CUTSCENE_SCENE(SCENE_TYPE_MAIN_MENU)
|
||||||
);
|
);
|
||||||
|
|
||||||
for(uint8_t i = 0; i < SAVE_SLOT_COUNT; i++) {
|
void sceneInitialAvailableDeviceCallback(savedevice_t *device, void *user) {
|
||||||
if(saveSlotInUse(&SAVE.caches[i])) {
|
cutsceneGoTo(device == NULL ? "NO_DEVICE" : "CONTINUE");
|
||||||
consolePrint(
|
|
||||||
"Slot %u is in use: %s", (uint32_t)i, SAVE.caches[i].name
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
consolePrint("Slot %u is not in use.", (uint32_t)i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
consolePrint("Going to find a save device.");
|
// Set background color to black for the initial scene
|
||||||
saveFindAvailableDevice(
|
SCREEN.background = COLOR_BLACK;
|
||||||
testCallback,
|
|
||||||
&testData
|
cutsceneSystemStartCutscene(&CUTSCENE_INITIAL);
|
||||||
);
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "scene/scenebase.h"
|
#include "scene/scenebase.h"
|
||||||
|
|
||||||
// No per-scene state needed - the save globals and the two modal UI
|
|
||||||
// elements (see ui/frame/initial/) carry everything this scene cares
|
|
||||||
// about. A byte placeholder keeps the struct non-empty for portability.
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t reserved;
|
// uint32_t callbackState;
|
||||||
|
void *nothing;
|
||||||
} sceneinitial_t;
|
} sceneinitial_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ void uiModalSelected(
|
|||||||
const uimenuitem_t *item
|
const uimenuitem_t *item
|
||||||
) {
|
) {
|
||||||
UI_MODAL.result = index;
|
UI_MODAL.result = index;
|
||||||
uiModalClose();
|
uiModalClose(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiModalClosed(const uimenu_t *menu) {
|
void uiModalClosed(const uimenu_t *menu) {
|
||||||
@@ -53,7 +53,9 @@ errorret_t uiModalInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t uiModalDraw(void) {
|
errorret_t uiModalDraw(void) {
|
||||||
if(!uiMenuIsActive(&UI_MODAL.menu)) errorOk();
|
if(!UI_MODAL.open) errorOk();
|
||||||
|
|
||||||
|
bool_t hasOptions = UI_MODAL.menu.itemCount > 0;
|
||||||
|
|
||||||
spritebatchsprite_t backdropSprite = {
|
spritebatchsprite_t backdropSprite = {
|
||||||
.min = { 0.0f, 0.0f, 0.0f },
|
.min = { 0.0f, 0.0f, 0.0f },
|
||||||
@@ -82,8 +84,8 @@ errorret_t uiModalDraw(void) {
|
|||||||
float_t width = contentWidth + (UI_FRAME_START_X * 2);
|
float_t width = contentWidth + (UI_FRAME_START_X * 2);
|
||||||
float_t height = (UI_FRAME_START_Y * 2)
|
float_t height = (UI_FRAME_START_Y * 2)
|
||||||
+ (float_t)UI_MODAL.titleLabel.height + UI_FRAME_PADDING_Y
|
+ (float_t)UI_MODAL.titleLabel.height + UI_FRAME_PADDING_Y
|
||||||
+ (float_t)UI_MODAL.messageLabel.height + UI_FRAME_PADDING_Y
|
+ (float_t)UI_MODAL.messageLabel.height
|
||||||
+ rowHeight;
|
+ (hasOptions ? UI_FRAME_PADDING_Y + rowHeight : 0.0f);
|
||||||
|
|
||||||
float_t x = (float_t)SCREEN.scanX +
|
float_t x = (float_t)SCREEN.scanX +
|
||||||
((float_t)SCREEN.scanWidth - width) * 0.5f;
|
((float_t)SCREEN.scanWidth - width) * 0.5f;
|
||||||
@@ -105,18 +107,20 @@ errorret_t uiModalDraw(void) {
|
|||||||
uiLabelSetY(&UI_MODAL.messageLabel, messageY);
|
uiLabelSetY(&UI_MODAL.messageLabel, messageY);
|
||||||
errorChain(uiLabelRender(&UI_MODAL.messageLabel, COLOR_WHITE));
|
errorChain(uiLabelRender(&UI_MODAL.messageLabel, COLOR_WHITE));
|
||||||
|
|
||||||
|
if(hasOptions) {
|
||||||
float_t buttonsY = messageY + (float_t)UI_MODAL.messageLabel.height +
|
float_t buttonsY = messageY + (float_t)UI_MODAL.messageLabel.height +
|
||||||
UI_FRAME_PADDING_Y;
|
UI_FRAME_PADDING_Y;
|
||||||
errorChain(
|
errorChain(
|
||||||
uiMenuDraw(&UI_MODAL.menu, contentX, buttonsY, contentWidth, rowHeight)
|
uiMenuDraw(&UI_MODAL.menu, contentX, buttonsY, contentWidth, rowHeight)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
errorChain(spriteBatchFlush());
|
errorChain(spriteBatchFlush());
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool_t uiModalIsOpen(void) {
|
bool_t uiModalIsOpen(void) {
|
||||||
return uiMenuIsActive(&UI_MODAL.menu);
|
return UI_MODAL.open;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t uiModalGetResult(void) {
|
uint8_t uiModalGetResult(void) {
|
||||||
@@ -128,13 +132,16 @@ void uiModalOpen(
|
|||||||
const char_t *message,
|
const char_t *message,
|
||||||
const char_t **options,
|
const char_t **options,
|
||||||
const uint8_t optionCount,
|
const uint8_t optionCount,
|
||||||
uimodalcallback_t callback,
|
uimodaloptioncallback_t callback,
|
||||||
|
uimodalopenedcallback_t onOpen,
|
||||||
void *user
|
void *user
|
||||||
) {
|
) {
|
||||||
assertNotNull(title, "Title cannot be NULL");
|
assertNotNull(title, "Title cannot be NULL");
|
||||||
assertNotNull(message, "Message cannot be NULL");
|
assertNotNull(message, "Message cannot be NULL");
|
||||||
assertNotNull(options, "Options cannot be NULL");
|
assertTrue(
|
||||||
assertTrue(optionCount > 0, "Must have at least one option");
|
options != NULL || optionCount == 0,
|
||||||
|
"Options cannot be NULL when optionCount > 0"
|
||||||
|
);
|
||||||
assertTrue(
|
assertTrue(
|
||||||
optionCount <= UI_MODAL_OPTIONS_MAX, "Too many options for modal"
|
optionCount <= UI_MODAL_OPTIONS_MAX, "Too many options for modal"
|
||||||
);
|
);
|
||||||
@@ -148,9 +155,12 @@ void uiModalOpen(
|
|||||||
uiLabelRebuffer(&UI_MODAL.messageLabel);
|
uiLabelRebuffer(&UI_MODAL.messageLabel);
|
||||||
|
|
||||||
UI_MODAL.callback = callback;
|
UI_MODAL.callback = callback;
|
||||||
|
UI_MODAL.onOpen = onOpen;
|
||||||
UI_MODAL.user = user;
|
UI_MODAL.user = user;
|
||||||
UI_MODAL.result = UI_MODAL_RESULT_NONE;
|
UI_MODAL.result = UI_MODAL_RESULT_NONE;
|
||||||
|
UI_MODAL.open = true;
|
||||||
|
|
||||||
|
if(options != NULL) {
|
||||||
MENU_BEGIN(
|
MENU_BEGIN(
|
||||||
&UI_MODAL.menu, UI_MODAL.options, uiModalSelected, uiModalClosed, NULL
|
&UI_MODAL.menu, UI_MODAL.options, uiModalSelected, uiModalClosed, NULL
|
||||||
);
|
);
|
||||||
@@ -160,10 +170,28 @@ void uiModalOpen(
|
|||||||
MENU_END(UI_MODAL.options, menuIndex);
|
MENU_END(UI_MODAL.options, menuIndex);
|
||||||
|
|
||||||
uiMenuOpen(&UI_MODAL.menu);
|
uiMenuOpen(&UI_MODAL.menu);
|
||||||
|
} else {
|
||||||
|
uiMenuInit(&UI_MODAL.menu, uiModalSelected, uiModalClosed, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiModalClose(void) {
|
if(UI_MODAL.onOpen != NULL) {
|
||||||
|
UI_MODAL.onOpen(UI_MODAL.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiModalClose(uimodalclosedcallback_t callback) {
|
||||||
|
if(!UI_MODAL.open) return;
|
||||||
|
UI_MODAL.open = false;
|
||||||
|
|
||||||
|
if(uiMenuIsActive(&UI_MODAL.menu)) {
|
||||||
uiMenuClose(&UI_MODAL.menu);
|
uiMenuClose(&UI_MODAL.menu);
|
||||||
|
} else {
|
||||||
|
uiModalClosed(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(callback != NULL) {
|
||||||
|
callback(UI_MODAL.user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t uiModalDispose(void) {
|
errorret_t uiModalDispose(void) {
|
||||||
|
|||||||
@@ -26,7 +26,22 @@
|
|||||||
* selecting an option.
|
* selecting an option.
|
||||||
* @param user Arbitrary pointer passed to uiModalOpen.
|
* @param user Arbitrary pointer passed to uiModalOpen.
|
||||||
*/
|
*/
|
||||||
typedef void (*uimodalcallback_t)(const uint8_t optionIndex, void *user);
|
typedef void (*uimodaloptioncallback_t)(const uint8_t optionIndex, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked when a modal opens. Fired immediately for now, but
|
||||||
|
* gives a hook point for a future open transition to key off of.
|
||||||
|
*
|
||||||
|
* @param user Arbitrary pointer passed to uiModalOpen.
|
||||||
|
*/
|
||||||
|
typedef void (*uimodalopenedcallback_t)(void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked when a modal closes, passed to uiModalClose.
|
||||||
|
*
|
||||||
|
* @param user Arbitrary pointer passed to uiModalOpen.
|
||||||
|
*/
|
||||||
|
typedef void (*uimodalclosedcallback_t)(void *user);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uilabel_t titleLabel;
|
uilabel_t titleLabel;
|
||||||
@@ -40,9 +55,11 @@ typedef struct {
|
|||||||
uimenu_t menu;
|
uimenu_t menu;
|
||||||
uimenuitem_t options[UI_MODAL_OPTIONS_MAX];
|
uimenuitem_t options[UI_MODAL_OPTIONS_MAX];
|
||||||
|
|
||||||
uimodalcallback_t callback;
|
uimodaloptioncallback_t callback;
|
||||||
|
uimodalopenedcallback_t onOpen;
|
||||||
void *user;
|
void *user;
|
||||||
uint8_t result;
|
uint8_t result;
|
||||||
|
bool_t open;
|
||||||
} uimodal_t;
|
} uimodal_t;
|
||||||
|
|
||||||
extern uimodal_t UI_MODAL;
|
extern uimodal_t UI_MODAL;
|
||||||
@@ -88,25 +105,33 @@ uint8_t uiModalGetResult(void);
|
|||||||
* @param options Array of option label strings; NOT copied internally,
|
* @param options Array of option label strings; NOT copied internally,
|
||||||
* the pointers are stored directly by the underlying buttons, so they
|
* the pointers are stored directly by the underlying buttons, so they
|
||||||
* must remain valid for as long as the modal is open (e.g. string
|
* must remain valid for as long as the modal is open (e.g. string
|
||||||
* literals or locale-owned strings).
|
* literals or locale-owned strings). May be NULL for a message-only
|
||||||
* @param optionCount Number of options, from 1 to UI_MODAL_OPTIONS_MAX.
|
* dialog with no option buttons, in which case optionCount must be 0.
|
||||||
|
* @param optionCount Number of options, from 0 to UI_MODAL_OPTIONS_MAX.
|
||||||
* @param callback Called with the result once the dialog closes. May be
|
* @param callback Called with the result once the dialog closes. May be
|
||||||
* NULL.
|
* NULL.
|
||||||
* @param user Arbitrary pointer passed through to callback.
|
* @param onOpen Called once the modal has opened. May be NULL.
|
||||||
|
* @param user Arbitrary pointer passed through to callback and onOpen,
|
||||||
|
* and to whatever callback is later passed to uiModalClose.
|
||||||
*/
|
*/
|
||||||
void uiModalOpen(
|
void uiModalOpen(
|
||||||
const char_t *title,
|
const char_t *title,
|
||||||
const char_t *message,
|
const char_t *message,
|
||||||
const char_t **options,
|
const char_t **options,
|
||||||
const uint8_t optionCount,
|
const uint8_t optionCount,
|
||||||
uimodalcallback_t callback,
|
uimodaloptioncallback_t callback,
|
||||||
|
uimodalopenedcallback_t onOpen,
|
||||||
void *user
|
void *user
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the modal dialog. No-op when already closed.
|
* Closes the modal dialog. No-op when already closed.
|
||||||
|
*
|
||||||
|
* @param callback Called with the modal's user pointer once the dialog
|
||||||
|
* has closed, after the option-result callback set by uiModalOpen. May
|
||||||
|
* be NULL. Not called if the modal was already closed.
|
||||||
*/
|
*/
|
||||||
void uiModalClose(void);
|
void uiModalClose(uimodalclosedcallback_t callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of the modal dialog.
|
* Disposes of the modal dialog.
|
||||||
|
|||||||
Reference in New Issue
Block a user