Move cutscene item type enum and authoring macros to their own headers

cutsceneitemtype_t moves from cutsceneitem.h into cutsceneitembase.h,
alongside a new CUTSCENE_ITEM(TYPE, UNION_NAME, ...) shorthand that
fills in a cutsceneitem_t literal's .type and union member together.

Every CUTSCENE_* authoring macro (CUTSCENE_WAIT, CUTSCENE_TEXT,
CUTSCENE_AUDIO_PLAY, etc.) moves out of the single, ever-growing
cutscene.h into the header of the item type it actually authors,
built on top of CUTSCENE_ITEM. cutscene.h now only holds cutscene_t
itself plus the CUTSCENE/CUTSCENE_REFERENCE/CUTSCENE_CUTSCENE macros,
which aren't tied to any one item type. Every relocated macro also
gained a proper JSDoc-style comment block with @param tags.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-09-12 22:30:23 -05:00
co-authored by Claude Sonnet 5
parent ffb375faeb
commit 083deb8357
45 changed files with 845 additions and 530 deletions
+27 -476
View File
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2025 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
@@ -19,6 +19,18 @@ typedef struct cutscene_s {
size_t dataSize;
} cutscene_t;
/**
* Declares a standalone, named cutscene_t (and its backing item array) -
* the entry point for a C-authored cutscene.
*
* @param NAME Suffix for the generated CUTSCENE_##NAME cutscene/items
* statics.
* @param SIZE Bytes of custom user data this cutscene needs, carved out
* of CUTSCENE_SYSTEM.data while it runs.
* @param PAUSE_TYPE Suffix of the CUTSCENE_PAUSE_* flag(s) to apply
* while this cutscene runs (e.g. NONE, ALL).
* @param ... The cutscene's cutsceneitem_t entries, in order.
*/
#define CUTSCENE(NAME, SIZE, PAUSE_TYPE, ...) \
static const cutsceneitem_t CUTSCENE_##NAME##_ITEMS[] = { __VA_ARGS__ }; \
static const cutscene_t CUTSCENE_##NAME = { \
@@ -28,486 +40,25 @@ typedef struct cutscene_s {
.dataSize = SIZE \
};
/**
* References a CUTSCENE(NAME, ...)-declared cutscene by name.
*
* @param CUTSCENE The cutscene's NAME, as passed to CUTSCENE(...).
*/
#define CUTSCENE_REFERENCE(CUTSCENE) \
&CUTSCENE_##CUTSCENE
#define CUTSCENE_TEXT(TEXT) \
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = TEXT } }
#define CUTSCENE_TEXT_MINI(TEXT, X, Y, Z, DURATION) \
{ \
.type = CUTSCENE_ITEM_TYPE_TEXT_MINI, \
.textMini = { \
.text = TEXT, \
.position = { X, Y, Z }, \
.duration = DURATION \
} \
}
#define CUTSCENE_TEXT_MINI_HIDE(INDEX) \
{ \
.type = CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE, \
.textMiniHide = { .index = INDEX } \
}
#define CUTSCENE_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 }
// Blocks the cutscene here indefinitely - it never completes on its own,
// only cutsceneGoTo (called externally, e.g. from a UI callback) can move
// execution past it.
#define CUTSCENE_IDLE() \
{ .type = CUTSCENE_ITEM_TYPE_IDLE }
// Opens a full-screen UI panel (e.g. the main menu) declaratively, then
// immediately continues on to whatever follows this item.
#define CUTSCENE_UI_SHOW(SCREEN) \
{ .type = CUTSCENE_ITEM_TYPE_UI_SHOW, .uiShow = { .screen = SCREEN } }
// 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 } }
/**
* Hands control over to another CUTSCENE(NAME, ...)-declared cutscene.
* Has no dedicated item header of its own - cutscenecutsceneref_t is
* defined inline in cutsceneitem.h, tightly bound to the asset/loader
* system, so this shorthand stays here too.
*
* @param CUTSCENE The referenced cutscene's NAME, as passed to
* CUTSCENE(...).
*/
#define CUTSCENE_CUTSCENE(CUTSCENE) \
{ \
.type = CUTSCENE_ITEM_TYPE_CUTSCENE, \
.cutsceneRef = { .cutscene = CUTSCENE_REFERENCE(CUTSCENE), .name = NULL } \
}
#define CUTSCENE_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.
// TITLE and MESSAGE are each displayed as-is unless they match a
// locale message ID, in which case the translated string is shown
// instead - see uiModalLocalize.
#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.
// Back/cancel input is disabled while it's open (see
// uiMenuSetDisableBack), so it must be dismissed by picking one; CALLBACK
// then fires with the selected option index 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 by this item, so they must stay valid
// until the modal opens (string literals are fine). Like TITLE and
// MESSAGE, each option is translated if it matches a locale message ID.
#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 \
} \
}
// Same as CUTSCENE_MODAL_OPTIONS, but fixed to exactly one option and
// MARKER1 to jump straight to via cutsceneGoTo once it's selected - 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. 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
// on to whatever follows this item in the sequence.
#define CUTSCENE_MODAL_CLOSE() \
{ .type = CUTSCENE_ITEM_TYPE_MODAL_CLOSE }
// Opens the on-screen keyboard and blocks the cutscene until it closes,
// then caches whatever was typed - see cutsceneSystemGetTextCache to
// read it back afterwards. __VA_ARGS__ are uikeyboardopen_t designated
// initializers, e.g. CUTSCENE_KEYBOARD(.cancel = true, .maxLength = 8).
#define CUTSCENE_KEYBOARD(...) \
{ \
.type = CUTSCENE_ITEM_TYPE_KEYBOARD, \
.keyboard = { .open = { __VA_ARGS__ } } \
}
// (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, \
.entityWalkTo = { \
.entityIndex = ENTITY_INDEX, \
.positions = (const worldpos_t[]){ { X, Y, Z } }, \
.count = 1, \
.walkAround = true \
} \
}
#define CUTSCENE_ENTITY_WALK_PATH(NAME, ENTITY_INDEX, ...) \
static const worldpos_t CUTSCENE_##NAME##_POSITIONS[] = { __VA_ARGS__ }; \
static const cutsceneitem_t CUTSCENE_##NAME = { \
.type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \
.entityWalkTo = { \
.entityIndex = ENTITY_INDEX, \
.positions = CUTSCENE_##NAME##_POSITIONS, \
.count = sizeof(CUTSCENE_##NAME##_POSITIONS) / sizeof(worldpos_t), \
.walkAround = true \
} \
}
#define CUTSCENE_ENTITY_REMOVE(ENTITY_INDEX) \
{ \
.type = CUTSCENE_ITEM_TYPE_ENTITY_REMOVE, \
.entityRemove = { .entityIndex = ENTITY_INDEX } \
}
#define CUTSCENE_ENTITY_ADD(TYPE, X, Y, Z) \
{ \
.type = CUTSCENE_ITEM_TYPE_ENTITY_ADD, \
.entityAdd = { .entityType = TYPE, .position = { X, Y, Z } } \
}
#define CUTSCENE_ENTITY_TURN(ENTITY_INDEX, DIRECTION) \
{ \
.type = CUTSCENE_ITEM_TYPE_ENTITY_TURN, \
.entityTurn = { .entityIndex = ENTITY_INDEX, .direction = DIRECTION } \
}
// Walks ENTITY_INDEX to stand beside TARGET_ENTITY_INDEX, offset by
// (OFFSET_X, OFFSET_Y) on the 2D plane. The destination Z is resolved
// from nearby terrain each frame, so ramps between the two entities are
// accounted for automatically.
#define CUTSCENE_ENTITY_WALK_TO_ENTITY( \
ENTITY_INDEX, TARGET_ENTITY_INDEX, OFFSET_X, OFFSET_Y \
) \
{ \
.type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY, \
.entityWalkToEntity = { \
.entityIndex = ENTITY_INDEX, \
.targetEntityIndex = TARGET_ENTITY_INDEX, \
.offsetX = OFFSET_X, \
.offsetY = OFFSET_Y \
} \
}
#define CUTSCENE_ENTITY_TELEPORT(ENTITY_INDEX, X, Y, Z) \
{ \
.type = CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT, \
.entityTeleport = { .entityIndex = ENTITY_INDEX, .target = { X, Y, Z } } \
}
#define CUTSCENE_FADE(FROM, TO, DURATION, EASING) \
{ \
.type = CUTSCENE_ITEM_TYPE_FADE, \
.fade = { .from = FROM, .to = TO, .duration = DURATION, .easing = EASING } \
}
#define CUTSCENE_FADE_TO_BLACK(DURATION) \
CUTSCENE_FADE(COLOR_TRANSPARENT_BLACK, COLOR_BLACK, DURATION, EASING_LINEAR)
#define CUTSCENE_FADE_FROM_BLACK(DURATION) \
CUTSCENE_FADE(COLOR_BLACK, COLOR_TRANSPARENT_BLACK, DURATION, EASING_LINEAR)
#define CUTSCENE_FADE_TO_WHITE(DURATION) \
CUTSCENE_FADE(COLOR_TRANSPARENT_WHITE, COLOR_WHITE, DURATION, EASING_LINEAR)
#define CUTSCENE_FADE_FROM_WHITE(DURATION) \
CUTSCENE_FADE(COLOR_WHITE, COLOR_TRANSPARENT_WHITE, DURATION, EASING_LINEAR)
#define CUTSCENE_EMOJI(ENTITY_INDEX, EMOJI_TYPE, DURATION) \
{ \
.type = CUTSCENE_ITEM_TYPE_EMOJI, \
.emoji = { \
.entityIndex = ENTITY_INDEX, \
.emojiType = EMOJI_TYPE, \
.duration = DURATION \
} \
}
// AMOUNT ranges 0 (no shake) to 4 (three tiles): 1 is half a tile, 2 is
// a full tile, 3 is two tiles, and 4 is three tiles.
#define CUTSCENE_SHAKE(AMOUNT, DURATION) \
{ \
.type = CUTSCENE_ITEM_TYPE_SHAKE, \
.shake = { .amount = AMOUNT, .duration = DURATION } \
}
// Waits until BATTLE.state reaches STATE. Put this BEFORE
// CUTSCENE_SET_PAUSE(CUTSCENE_PAUSE_BATTLE), not after -- pausing first
// freezes BATTLE.state wherever it already is, so it would never reach
// STATE on its own to satisfy the wait. Waiting unpaused, then pausing the
// moment it's satisfied, catches the battle right at STATE before it can
// advance further.
#define CUTSCENE_BATTLE_WAIT_STATE(STATE) \
{ \
.type = CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE, \
.battleWaitState = { .state = STATE } \
}
// Immediately queues an attack for FIGHTER_INDEX against TARGET_INDEX,
// bypassing normal player/AI selection for that fighter this round.
#define CUTSCENE_BATTLE_FORCE_ACTION(FIGHTER_INDEX, TARGET_INDEX) \
{ \
.type = CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION, \
.battleForceAction = { \
.fighterIndex = FIGHTER_INDEX, .targetIndex = TARGET_INDEX \
} \
}
#define CUTSCENE_SET_PAUSE(FLAGS) \
{ .type = CUTSCENE_ITEM_TYPE_SET_PAUSE, .setPause = (FLAGS) }
#define CUTSCENE_ITEM_GIVE(ITEM_ID, QUANTITY) \
{ \
.type = CUTSCENE_ITEM_TYPE_ITEM_GIVE, \
.itemGive = { .item = ITEM_ID, .quantity = QUANTITY } \
}
// Runs all listed items simultaneously and waits until all are done.
// Concurrent items cannot be nested inside another CUTSCENE_CONCURRENT.
#define CUTSCENE_CONCURRENT(...) \
{ \
.type = CUTSCENE_ITEM_TYPE_CONCURRENT, \
.concurrent = { \
.items = (const cutsceneitem_t[]){ __VA_ARGS__ }, \
.count = (uint8_t)( \
sizeof((cutsceneitem_t[]){ __VA_ARGS__ }) / \
sizeof(cutsceneitem_t) \
) \
} \
}
#define CUTSCENE_MAP_AREA_ADD( \
MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, CALLBACK, NOTIFY, TRIGGER \
) \
{ \
.type = CUTSCENE_ITEM_TYPE_MAP_AREA_ADD, \
.mapAreaAdd = { \
.min = { MIN_X, MIN_Y, MIN_Z }, \
.max = { MAX_X, MAX_Y, MAX_Z }, \
.callback = CALLBACK, \
.notify = NOTIFY, \
.trigger = TRIGGER \
} \
}
#define CUTSCENE_MAP_AREA_REMOVE(AREA_ID) \
{ \
.type = CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE, \
.mapAreaRemove = { .areaId = AREA_ID } \
}
// Waits until any one of the given map area IDs has its callback invoked.
// Accepts CUTSCENE_AREA_LAST_CREATED in place of a literal area ID.
#define CUTSCENE_MAP_AREA_WAIT(...) \
{ \
.type = CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT, \
.mapAreaWait = { \
.areaIds = (const uint8_t[]){ __VA_ARGS__ }, \
.count = (uint8_t)( \
sizeof((const uint8_t[]){ __VA_ARGS__ }) / sizeof(uint8_t) \
) \
} \
}
// Adds a map area, waits for it to be triggered once, then removes it
// before the cutscene continues. Uses a no-op callback since the wait is
// driven by the area's trigger count rather than callback logic.
#define CUTSCENE_MAP_AREA_TRIGGER_ONCE( \
MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, NOTIFY, TRIGGER \
) \
CUTSCENE_MAP_AREA_ADD( \
MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, \
mapAreaNoopCallback, NOTIFY, TRIGGER \
), \
CUTSCENE_MAP_AREA_WAIT(CUTSCENE_AREA_LAST_CREATED), \
CUTSCENE_MAP_AREA_REMOVE(CUTSCENE_AREA_LAST_CREATED)
// Longhand - every audioMixerQueuePlay() parameter. See
// CUTSCENE_AUDIO_PLAY_SIMPLE/CUTSCENE_AUDIO_PLAY_LOOPED below for the
// shorthands.
#define CUTSCENE_AUDIO_PLAY( \
FILE, CHANNEL, VOLUME, PAN, LOOPING, LOOP_COUNT, LOOP_START, LOOP_TO \
) \
{ \
.type = CUTSCENE_ITEM_TYPE_AUDIO_PLAY, \
.audioPlay = { \
.file = FILE, \
.channel = CHANNEL, \
.volume = VOLUME, \
.pan = PAN, \
.looping = LOOPING, \
.loopCount = LOOP_COUNT, \
.loopStart = LOOP_START, \
.loopTo = LOOP_TO \
} \
}
// Shorthand: play FILE on CHANNEL once, full volume, centered (0.0f, same
// as AUDIO_STREAM_CENTER), no loop.
#define CUTSCENE_AUDIO_PLAY_SIMPLE(FILE, CHANNEL) \
CUTSCENE_AUDIO_PLAY(FILE, CHANNEL, 1.0f, 0.0f, false, 0, -1.0f, 0.0f)
// Shorthand: play FILE on CHANNEL looping the whole clip forever, full
// volume, centered (0.0f, same as AUDIO_STREAM_CENTER).
#define CUTSCENE_AUDIO_PLAY_LOOPED(FILE, CHANNEL) \
CUTSCENE_AUDIO_PLAY(FILE, CHANNEL, 1.0f, 0.0f, true, 0, -1.0f, 0.0f)
#define CUTSCENE_AUDIO_STOP(CHANNEL) \
{ .type = CUTSCENE_ITEM_TYPE_AUDIO_STOP, .audioStopChannel = CHANNEL }
#define CUTSCENE_AUDIO_PAUSE(CHANNEL) \
{ .type = CUTSCENE_ITEM_TYPE_AUDIO_PAUSE, .audioPauseChannel = CHANNEL }
#define CUTSCENE_AUDIO_RESUME(CHANNEL) \
{ .type = CUTSCENE_ITEM_TYPE_AUDIO_RESUME, .audioResumeChannel = CHANNEL }
// Longhand - starts CHANNEL fading from FROM to TO over DURATION seconds,
// eased by EASING. Doesn't wait for it - pair with
// CUTSCENE_AUDIO_FADE_WAIT(CHANNEL) to block until it finishes. See
// CUTSCENE_AUDIO_FADE_OUT/CUTSCENE_AUDIO_FADE_IN below for the shorthands;
// use this directly for a custom range, e.g.
// CUTSCENE_AUDIO_FADE(CHANNEL, 1.0f, 0.3f, 5.0f, EASING_LINEAR) to fade
// down to "playing quietly" rather than silent.
#define CUTSCENE_AUDIO_FADE(CHANNEL, FROM, TO, DURATION, EASING) \
{ \
.type = CUTSCENE_ITEM_TYPE_AUDIO_FADE, \
.audioFade = { \
.channel = CHANNEL, \
.from = FROM, \
.to = TO, \
.duration = DURATION, \
.easing = EASING \
} \
}
// Shorthand: fade CHANNEL from full volume to silent over DURATION seconds.
#define CUTSCENE_AUDIO_FADE_OUT(CHANNEL, DURATION) \
CUTSCENE_AUDIO_FADE(CHANNEL, 1.0f, 0.0f, DURATION, EASING_LINEAR)
// Shorthand: fade CHANNEL from silent to full volume over DURATION seconds.
#define CUTSCENE_AUDIO_FADE_IN(CHANNEL, DURATION) \
CUTSCENE_AUDIO_FADE(CHANNEL, 0.0f, 1.0f, DURATION, EASING_LINEAR)
// Waits until CHANNEL's fade (started elsewhere by CUTSCENE_AUDIO_FADE)
// has finished - a no-op wait if CHANNEL isn't fading.
#define CUTSCENE_AUDIO_FADE_WAIT(CHANNEL) \
{ \
.type = CUTSCENE_ITEM_TYPE_AUDIO_FADE_WAIT, \
.audioFadeWaitChannel = CHANNEL \
}
#define CUTSCENE_AUDIO_SET_PAN(CHANNEL, PAN) \
{ \
.type = CUTSCENE_ITEM_TYPE_AUDIO_SET_PAN, \
.audioSetPan = { .channel = CHANNEL, .pan = PAN } \
}
#define CUTSCENE_AUDIO_SET_LOOP( \
CHANNEL, LOOPING, LOOP_COUNT, LOOP_START, LOOP_TO \
) \
{ \
.type = CUTSCENE_ITEM_TYPE_AUDIO_SET_LOOP, \
.audioSetLoop = { \
.channel = CHANNEL, \
.looping = LOOPING, \
.loopCount = LOOP_COUNT, \
.loopStart = LOOP_START, \
.loopTo = LOOP_TO \
} \
}
// Combined form of CUTSCENE_AUDIO_SET_PAN + CUTSCENE_AUDIO_SET_LOOP, for
// setting both at once rather than as two separate items.
#define CUTSCENE_AUDIO_SET( \
CHANNEL, PAN, LOOPING, LOOP_COUNT, LOOP_START, LOOP_TO \
) \
{ \
.type = CUTSCENE_ITEM_TYPE_AUDIO_SET, \
.audioSet = { \
.channel = CHANNEL, \
.pan = PAN, \
.looping = LOOPING, \
.loopCount = LOOP_COUNT, \
.loopStart = LOOP_START, \
.loopTo = LOOP_TO \
} \
}
@@ -18,6 +18,50 @@ typedef struct {
easingtype_t easing;
} cutsceneaudiofade_t;
/**
* Longhand - starts CHANNEL fading from FROM to TO over DURATION seconds,
* eased by EASING. Doesn't wait for it - pair with
* CUTSCENE_AUDIO_FADE_WAIT(CHANNEL) to block until it finishes. See
* CUTSCENE_AUDIO_FADE_OUT/CUTSCENE_AUDIO_FADE_IN below for the
* shorthands; use this directly for a custom range, e.g.
* CUTSCENE_AUDIO_FADE(CHANNEL, 1.0f, 0.3f, 5.0f, EASING_LINEAR) to fade
* down to "playing quietly" rather than silent.
*
* @param CHANNEL audiomixerchannel_t to fade.
* @param FROM Starting volume (0.0-1.0).
* @param TO Ending volume (0.0-1.0).
* @param DURATION Duration in seconds.
* @param EASING easingtype_t to apply.
*/
#define CUTSCENE_AUDIO_FADE(CHANNEL, FROM, TO, DURATION, EASING) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_FADE, audioFade, { \
.channel = CHANNEL, \
.from = FROM, \
.to = TO, \
.duration = DURATION, \
.easing = EASING \
})
/**
* Shorthand: fade CHANNEL from full volume to silent over DURATION
* seconds.
*
* @param CHANNEL audiomixerchannel_t to fade.
* @param DURATION Duration in seconds.
*/
#define CUTSCENE_AUDIO_FADE_OUT(CHANNEL, DURATION) \
CUTSCENE_AUDIO_FADE(CHANNEL, 1.0f, 0.0f, DURATION, EASING_LINEAR)
/**
* Shorthand: fade CHANNEL from silent to full volume over DURATION
* seconds.
*
* @param CHANNEL audiomixerchannel_t to fade.
* @param DURATION Duration in seconds.
*/
#define CUTSCENE_AUDIO_FADE_IN(CHANNEL, DURATION) \
CUTSCENE_AUDIO_FADE(CHANNEL, 0.0f, 1.0f, DURATION, EASING_LINEAR)
/**
* Starts an audio fade step (starts the fade via audioMixerFadeTo() -
* doesn't wait for it; pair with CUTSCENE_AUDIO_FADE_WAIT to block on it).
@@ -9,6 +9,15 @@
#include "audio/mixer/audiomixerchannel.h"
#include "rpg/cutscene/item/cutsceneitembase.h"
/**
* Waits until CHANNEL's fade (started elsewhere by CUTSCENE_AUDIO_FADE)
* has finished - a no-op wait if CHANNEL isn't fading.
*
* @param CHANNEL audiomixerchannel_t to wait on.
*/
#define CUTSCENE_AUDIO_FADE_WAIT(CHANNEL) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_FADE_WAIT, audioFadeWaitChannel, CHANNEL)
/**
* Updates an audio fade-wait step, completing once the watched channel's
* fade (started by a CUTSCENE_AUDIO_FADE elsewhere) finishes. Has no Start
@@ -9,6 +9,14 @@
#include "audio/mixer/audiomixerchannel.h"
#include "rpg/cutscene/item/cutsceneitembase.h"
/**
* Pauses CHANNEL immediately.
*
* @param CHANNEL audiomixerchannel_t to pause.
*/
#define CUTSCENE_AUDIO_PAUSE(CHANNEL) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_PAUSE, audioPauseChannel, CHANNEL)
/**
* Starts an audio pause step (pauses the channel immediately).
*
@@ -20,6 +20,55 @@ typedef struct {
float_t loopTo;
} cutsceneaudioplay_t;
/**
* Longhand - queues a sound via audioMixerQueuePlay() (see its own
* comment on when it actually starts). See CUTSCENE_AUDIO_PLAY_SIMPLE/
* CUTSCENE_AUDIO_PLAY_LOOPED below for the shorthands.
*
* @param FILE Audio file path.
* @param CHANNEL audiomixerchannel_t to play on.
* @param VOLUME Playback volume (0.0-1.0).
* @param PAN Stereo pan (-1.0 left to 1.0 right, 0.0 centered).
* @param LOOPING Whether the clip loops.
* @param LOOP_COUNT Number of loop repeats (0 = infinite, if LOOPING).
* @param LOOP_START Loop region start in seconds, or -1.0f for the clip
* start.
* @param LOOP_TO Loop region end/return point in seconds.
*/
#define CUTSCENE_AUDIO_PLAY( \
FILE, CHANNEL, VOLUME, PAN, LOOPING, LOOP_COUNT, LOOP_START, LOOP_TO \
) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_PLAY, audioPlay, { \
.file = FILE, \
.channel = CHANNEL, \
.volume = VOLUME, \
.pan = PAN, \
.looping = LOOPING, \
.loopCount = LOOP_COUNT, \
.loopStart = LOOP_START, \
.loopTo = LOOP_TO \
})
/**
* Shorthand: play FILE on CHANNEL once, full volume, centered (0.0f, same
* as AUDIO_STREAM_CENTER), no loop.
*
* @param FILE Audio file path.
* @param CHANNEL audiomixerchannel_t to play on.
*/
#define CUTSCENE_AUDIO_PLAY_SIMPLE(FILE, CHANNEL) \
CUTSCENE_AUDIO_PLAY(FILE, CHANNEL, 1.0f, 0.0f, false, 0, -1.0f, 0.0f)
/**
* Shorthand: play FILE on CHANNEL looping the whole clip forever, full
* volume, centered (0.0f, same as AUDIO_STREAM_CENTER).
*
* @param FILE Audio file path.
* @param CHANNEL audiomixerchannel_t to play on.
*/
#define CUTSCENE_AUDIO_PLAY_LOOPED(FILE, CHANNEL) \
CUTSCENE_AUDIO_PLAY(FILE, CHANNEL, 1.0f, 0.0f, true, 0, -1.0f, 0.0f)
/**
* Starts an audio play step (queues the sound via audioMixerQueuePlay() -
* see its own comment on when it actually starts).
@@ -9,6 +9,14 @@
#include "audio/mixer/audiomixerchannel.h"
#include "rpg/cutscene/item/cutsceneitembase.h"
/**
* Resumes CHANNEL immediately.
*
* @param CHANNEL audiomixerchannel_t to resume.
*/
#define CUTSCENE_AUDIO_RESUME(CHANNEL) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_RESUME, audioResumeChannel, CHANNEL)
/**
* Starts an audio resume step (resumes the channel immediately).
*
@@ -18,6 +18,30 @@ typedef struct {
float_t loopTo;
} cutsceneaudioset_t;
/**
* Combined form of CUTSCENE_AUDIO_SET_PAN + CUTSCENE_AUDIO_SET_LOOP, for
* setting both at once rather than as two separate items.
*
* @param CHANNEL audiomixerchannel_t to adjust.
* @param PAN Stereo pan (-1.0 left to 1.0 right, 0.0 centered).
* @param LOOPING Whether the clip loops.
* @param LOOP_COUNT Number of loop repeats (0 = infinite, if LOOPING).
* @param LOOP_START Loop region start in seconds, or -1.0f for the clip
* start.
* @param LOOP_TO Loop region end/return point in seconds.
*/
#define CUTSCENE_AUDIO_SET( \
CHANNEL, PAN, LOOPING, LOOP_COUNT, LOOP_START, LOOP_TO \
) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_SET, audioSet, { \
.channel = CHANNEL, \
.pan = PAN, \
.looping = LOOPING, \
.loopCount = LOOP_COUNT, \
.loopStart = LOOP_START, \
.loopTo = LOOP_TO \
})
/**
* Starts an audio set step - applies pan and looping behaviour together in
* one item (via audioMixerSetPan()/audioMixerSetLoop()), for when you want
@@ -17,6 +17,27 @@ typedef struct {
float_t loopTo;
} cutsceneaudiosetloop_t;
/**
* Applies new looping behaviour immediately, via audioMixerSetLoop().
*
* @param CHANNEL audiomixerchannel_t to adjust.
* @param LOOPING Whether the clip loops.
* @param LOOP_COUNT Number of loop repeats (0 = infinite, if LOOPING).
* @param LOOP_START Loop region start in seconds, or -1.0f for the clip
* start.
* @param LOOP_TO Loop region end/return point in seconds.
*/
#define CUTSCENE_AUDIO_SET_LOOP( \
CHANNEL, LOOPING, LOOP_COUNT, LOOP_START, LOOP_TO \
) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_SET_LOOP, audioSetLoop, { \
.channel = CHANNEL, \
.looping = LOOPING, \
.loopCount = LOOP_COUNT, \
.loopStart = LOOP_START, \
.loopTo = LOOP_TO \
})
/**
* Starts an audio set-loop step (applies the new looping behaviour
* immediately, via audioMixerSetLoop()).
@@ -14,6 +14,17 @@ typedef struct {
float_t pan;
} cutsceneaudiosetpan_t;
/**
* Applies a new pan immediately, via audioMixerSetPan().
*
* @param CHANNEL audiomixerchannel_t to adjust.
* @param PAN Stereo pan (-1.0 left to 1.0 right, 0.0 centered).
*/
#define CUTSCENE_AUDIO_SET_PAN(CHANNEL, PAN) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_SET_PAN, audioSetPan, { \
.channel = CHANNEL, .pan = PAN \
})
/**
* Starts an audio set-pan step (applies the new pan immediately, via
* audioMixerSetPan()).
@@ -9,6 +9,14 @@
#include "audio/mixer/audiomixerchannel.h"
#include "rpg/cutscene/item/cutsceneitembase.h"
/**
* Stops CHANNEL immediately.
*
* @param CHANNEL audiomixerchannel_t to stop.
*/
#define CUTSCENE_AUDIO_STOP(CHANNEL) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_AUDIO_STOP, audioStopChannel, CHANNEL)
/**
* Starts an audio stop step (stops the channel immediately).
*
@@ -14,6 +14,18 @@ typedef struct {
uint8_t targetIndex;
} cutscenebattleforceaction_t;
/**
* Immediately queues an attack for FIGHTER_INDEX against TARGET_INDEX,
* bypassing normal player/AI selection for that fighter this round.
*
* @param FIGHTER_INDEX Attacking fighter's battle index.
* @param TARGET_INDEX Target fighter's battle index.
*/
#define CUTSCENE_BATTLE_FORCE_ACTION(FIGHTER_INDEX, TARGET_INDEX) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION, battleForceAction, { \
.fighterIndex = FIGHTER_INDEX, .targetIndex = TARGET_INDEX \
})
/**
* Starts a battle force-action step: immediately queues an attack for the
* given fighter against the given target, bypassing normal player/AI
@@ -13,6 +13,21 @@ typedef struct {
battlestate_t state;
} cutscenebattlewaitstate_t;
/**
* Waits until BATTLE.state reaches STATE. Put this BEFORE
* CUTSCENE_SET_PAUSE(CUTSCENE_PAUSE_BATTLE), not after -- pausing first
* freezes BATTLE.state wherever it already is, so it would never reach
* STATE on its own to satisfy the wait. Waiting unpaused, then pausing
* the moment it's satisfied, catches the battle right at STATE before it
* can advance further.
*
* @param STATE battlestate_t to wait for.
*/
#define CUTSCENE_BATTLE_WAIT_STATE(STATE) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE, battleWaitState, { \
.state = STATE \
})
/**
* Updates a battle wait-state step, completing once BATTLE.state reaches
* the watched state. Has no Start callback -- there's nothing to do until
@@ -13,6 +13,22 @@
/** Maximum number of items that may run inside a CUTSCENE_CONCURRENT. */
#define CUTSCENE_CONCURRENT_MAX 8
/**
* Runs all listed items simultaneously and waits until all are done.
* Concurrent items cannot be nested inside another CUTSCENE_CONCURRENT.
*
* @param ... Child cutsceneitem_t entries (e.g. CUTSCENE_WAIT(...),
* CUTSCENE_ENTITY_WALK_TO(...)) to run at the same time.
*/
#define CUTSCENE_CONCURRENT(...) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_CONCURRENT, concurrent, { \
.items = (const cutsceneitem_t[]){ __VA_ARGS__ }, \
.count = (uint8_t)( \
sizeof((cutsceneitem_t[]){ __VA_ARGS__ }) / \
sizeof(cutsceneitem_t) \
) \
})
/**
* Static (const) data for a concurrent cutscene item.
*/
@@ -8,6 +8,14 @@
#pragma once
#include "rpg/cutscene/item/cutsceneitembase.h"
/**
* Blocks the cutscene here indefinitely - it never completes on its own,
* only cutsceneGoTo (called externally, e.g. from a UI callback) can move
* execution past it.
*/
#define CUTSCENE_IDLE() \
{ .type = CUTSCENE_ITEM_TYPE_IDLE }
/**
* Updates an idle item. Never completes on its own - the cutscene blocks
* here indefinitely until something external (e.g. a UI callback) calls
@@ -12,6 +12,17 @@ typedef struct {
const char_t *name;
} cutscenemarker_t;
/**
* A named, otherwise no-op position in the item list that cutsceneGoTo
* can jump execution straight to.
*
* @param NAME Marker name, matched with stringEquals (not pointer
* identity) - safe to use separate string literals with the same
* contents at the marker and at each call site.
*/
#define CUTSCENE_MARKER(NAME) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_MARKER, marker, { .name = NAME })
/**
* Starts a marker item. A marker does nothing on its own - it exists
* purely as a named position for cutsceneGoTo to jump to.
@@ -8,6 +8,13 @@
#pragma once
#include "rpg/cutscene/item/cutsceneitembase.h"
/**
* Restarts the currently running cutscene from its first item,
* preserving whatever interact/interacted entities triggered it.
*/
#define CUTSCENE_RESTART() \
{ .type = CUTSCENE_ITEM_TYPE_RESTART }
/**
* Starts a restart item (restarts the currently running cutscene from
* its first item via cutsceneRestart).
@@ -13,6 +13,17 @@ typedef struct {
scenetype_t type;
} cutscenescene_t;
/**
* 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.
*
* @param TYPE Target scenetype_t to switch to.
*/
#define CUTSCENE_SCENE(TYPE) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_SCENE, sceneChange, { .type = TYPE })
/**
* Starts a scene item (requests a switch to the given scene via
* sceneSet). The switch itself doesn't happen until the next
@@ -9,6 +9,14 @@
#include "rpg/cutscene/cutscenepause.h"
#include "rpg/cutscene/item/cutsceneitembase.h"
/**
* Applies the given pause flags immediately.
*
* @param FLAGS CUTSCENE_PAUSE_* flags, OR'd together.
*/
#define CUTSCENE_SET_PAUSE(FLAGS) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_SET_PAUSE, setPause, (FLAGS))
/**
* Starts a set-pause item (applies the new pause flags immediately).
*
@@ -11,6 +11,14 @@
typedef float_t cutscenewait_t;
typedef float_t cutscenewaitdata_t;
/**
* Waits WAIT seconds before continuing.
*
* @param WAIT Duration in seconds.
*/
#define CUTSCENE_WAIT(WAIT) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_WAIT, wait, WAIT)
/**
* Starts a wait item (stores the duration in data).
*
@@ -10,6 +10,15 @@
typedef void (*cutscenecallback_t)(void *userData);
/**
* Invokes CALLBACK immediately, then continues on to whatever follows
* this item.
*
* @param CALLBACK A cutscenecallback_t function pointer.
*/
#define CUTSCENE_CALLBACK(CALLBACK) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_CALLBACK, callback, CALLBACK)
/**
* Starts a callback item (invokes the callback immediately).
*
+1 -54
View File
@@ -8,6 +8,7 @@
#pragma once
#include "error/error.h"
#include "yyjson.h"
#include "cutsceneitembase.h"
#include "cutscenecallback.h"
#include "cutsceneprint.h"
#include "control/cutscenewait.h"
@@ -70,60 +71,6 @@ typedef struct {
const char_t *name;
} cutscenecutsceneref_t;
typedef enum {
CUTSCENE_ITEM_TYPE_NULL,
CUTSCENE_ITEM_TYPE_TEXT,
CUTSCENE_ITEM_TYPE_TEXT_MINI,
CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE,
CUTSCENE_ITEM_TYPE_CALLBACK,
CUTSCENE_ITEM_TYPE_WAIT,
CUTSCENE_ITEM_TYPE_CUTSCENE,
CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT,
CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO,
CUTSCENE_ITEM_TYPE_FADE,
CUTSCENE_ITEM_TYPE_SET_PAUSE,
CUTSCENE_ITEM_TYPE_CONCURRENT,
CUTSCENE_ITEM_TYPE_ITEM_GIVE,
CUTSCENE_ITEM_TYPE_ENTITY_REMOVE,
CUTSCENE_ITEM_TYPE_ENTITY_ADD,
CUTSCENE_ITEM_TYPE_ENTITY_TURN,
CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY,
CUTSCENE_ITEM_TYPE_MAP_AREA_ADD,
CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE,
CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT,
CUTSCENE_ITEM_TYPE_START_BATTLE,
CUTSCENE_ITEM_TYPE_EMOJI,
CUTSCENE_ITEM_TYPE_SHAKE,
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_QUIT_GAME,
CUTSCENE_ITEM_TYPE_SCENE,
CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK,
CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS,
CUTSCENE_ITEM_TYPE_KEYBOARD,
CUTSCENE_ITEM_TYPE_AUDIO_PLAY,
CUTSCENE_ITEM_TYPE_AUDIO_STOP,
CUTSCENE_ITEM_TYPE_AUDIO_PAUSE,
CUTSCENE_ITEM_TYPE_AUDIO_RESUME,
CUTSCENE_ITEM_TYPE_AUDIO_FADE,
CUTSCENE_ITEM_TYPE_AUDIO_FADE_WAIT,
CUTSCENE_ITEM_TYPE_AUDIO_SET_PAN,
CUTSCENE_ITEM_TYPE_AUDIO_SET_LOOP,
CUTSCENE_ITEM_TYPE_AUDIO_SET,
CUTSCENE_ITEM_TYPE_IDLE,
CUTSCENE_ITEM_TYPE_UI_SHOW,
CUTSCENE_ITEM_TYPE_REGULAR_BATTLE,
CUTSCENE_ITEM_TYPE_COUNT
} cutsceneitemtype_t;
struct cutsceneitem_s {
cutsceneitemtype_t type;
@@ -13,6 +13,75 @@
typedef struct cutsceneitem_s cutsceneitem_t;
typedef union cutsceneitemdata_u cutsceneitemdata_t;
typedef enum {
CUTSCENE_ITEM_TYPE_NULL,
CUTSCENE_ITEM_TYPE_TEXT,
CUTSCENE_ITEM_TYPE_TEXT_MINI,
CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE,
CUTSCENE_ITEM_TYPE_CALLBACK,
CUTSCENE_ITEM_TYPE_WAIT,
CUTSCENE_ITEM_TYPE_CUTSCENE,
CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT,
CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO,
CUTSCENE_ITEM_TYPE_FADE,
CUTSCENE_ITEM_TYPE_SET_PAUSE,
CUTSCENE_ITEM_TYPE_CONCURRENT,
CUTSCENE_ITEM_TYPE_ITEM_GIVE,
CUTSCENE_ITEM_TYPE_ENTITY_REMOVE,
CUTSCENE_ITEM_TYPE_ENTITY_ADD,
CUTSCENE_ITEM_TYPE_ENTITY_TURN,
CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY,
CUTSCENE_ITEM_TYPE_MAP_AREA_ADD,
CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE,
CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT,
CUTSCENE_ITEM_TYPE_START_BATTLE,
CUTSCENE_ITEM_TYPE_EMOJI,
CUTSCENE_ITEM_TYPE_SHAKE,
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_QUIT_GAME,
CUTSCENE_ITEM_TYPE_SCENE,
CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK,
CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS,
CUTSCENE_ITEM_TYPE_KEYBOARD,
CUTSCENE_ITEM_TYPE_AUDIO_PLAY,
CUTSCENE_ITEM_TYPE_AUDIO_STOP,
CUTSCENE_ITEM_TYPE_AUDIO_PAUSE,
CUTSCENE_ITEM_TYPE_AUDIO_RESUME,
CUTSCENE_ITEM_TYPE_AUDIO_FADE,
CUTSCENE_ITEM_TYPE_AUDIO_FADE_WAIT,
CUTSCENE_ITEM_TYPE_AUDIO_SET_PAN,
CUTSCENE_ITEM_TYPE_AUDIO_SET_LOOP,
CUTSCENE_ITEM_TYPE_AUDIO_SET,
CUTSCENE_ITEM_TYPE_IDLE,
CUTSCENE_ITEM_TYPE_UI_SHOW,
CUTSCENE_ITEM_TYPE_REGULAR_BATTLE,
CUTSCENE_ITEM_TYPE_COUNT
} cutsceneitemtype_t;
/**
* Shorthand for a cutsceneitem_t literal - used by each item type's own
* authoring macro (e.g. CUTSCENE_WAIT in cutscenewait.h) to fill in .type
* and its union member together.
*
* @param TYPE The item's CUTSCENE_ITEM_TYPE_* enum value.
* @param UNION_NAME Name of the cutsceneitem_t union member to fill.
* @param ... The union member's value. Passed through __VA_ARGS__ (rather
* than a single named parameter) so it may itself be a brace initializer
* with top-level commas, e.g.
* CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_FADE, fade, { .from = A, .to = B }).
*/
#define CUTSCENE_ITEM(TYPE, UNION_NAME, ...) \
{ .type = TYPE, .UNION_NAME = __VA_ARGS__ }
/**
* Shared no-op Load for item types with no JSON fields of their own to
* parse (IDLE, RESTART, MODAL_CLOSE) - always succeeds without touching
@@ -14,6 +14,14 @@ typedef struct {
char_t text[CUTSCENE_PRINT_MAX_CHARS];
} cutsceneprint_t;
/**
* Prints TEXT to the console.
*
* @param TEXT Text to print.
*/
#define CUTSCENE_PRINT(TEXT) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_PRINT, print, { .text = TEXT })
/**
* Starts a print item (prints the item's text to the console).
*
@@ -15,6 +15,19 @@ typedef struct {
worldpos_t position;
} cutsceneentityadd_t;
/**
* Spawns a new entity into the world immediately.
*
* @param TYPE Entity type to spawn.
* @param X Spawn world X.
* @param Y Spawn world Y.
* @param Z Spawn world Z.
*/
#define CUTSCENE_ENTITY_ADD(TYPE, X, Y, Z) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ENTITY_ADD, entityAdd, { \
.entityType = TYPE, .position = { X, Y, Z } \
})
/**
* Starts an entity add step (spawns the entity into the world immediately).
*
@@ -12,6 +12,16 @@ typedef struct {
uint8_t entityIndex;
} cutsceneentityremove_t;
/**
* Removes an entity from the world immediately.
*
* @param ENTITY_INDEX Entity index to remove.
*/
#define CUTSCENE_ENTITY_REMOVE(ENTITY_INDEX) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ENTITY_REMOVE, entityRemove, { \
.entityIndex = ENTITY_INDEX \
})
/**
* Starts an entity remove step (removes the entity from the world immediately).
*
@@ -14,6 +14,19 @@ typedef struct {
worldpos_t target;
} cutsceneentityteleport_t;
/**
* Teleports an entity to a world position immediately.
*
* @param ENTITY_INDEX Entity index to teleport.
* @param X Target world X.
* @param Y Target world Y.
* @param Z Target world Z.
*/
#define CUTSCENE_ENTITY_TELEPORT(ENTITY_INDEX, X, Y, Z) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT, entityTeleport, { \
.entityIndex = ENTITY_INDEX, .target = { X, Y, Z } \
})
/**
* Starts an entity teleport item (teleports the entity immediately).
*
@@ -14,6 +14,17 @@ typedef struct {
entitydir_t direction;
} cutsceneentityturn_t;
/**
* Turns an entity to face a direction.
*
* @param ENTITY_INDEX Entity index to turn.
* @param DIRECTION Target entitydir_t to face.
*/
#define CUTSCENE_ENTITY_TURN(ENTITY_INDEX, DIRECTION) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ENTITY_TURN, entityTurn, { \
.entityIndex = ENTITY_INDEX, .direction = DIRECTION \
})
/**
* Starts an entity turn step. The turn itself is driven from Update, since
* the entity may still be finishing a previous action.
@@ -20,6 +20,45 @@ typedef struct {
uint8_t currentIndex;
} cutsceneentitywalktodata_t;
/**
* Walks an entity to a single world position, navigating around
* obstacles.
*
* @param ENTITY_INDEX Entity index to move.
* @param X Target world X.
* @param Y Target world Y.
* @param Z Target world Z.
*/
#define CUTSCENE_ENTITY_WALK_TO(ENTITY_INDEX, X, Y, Z) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, entityWalkTo, { \
.entityIndex = ENTITY_INDEX, \
.positions = (const worldpos_t[]){ { X, Y, Z } }, \
.count = 1, \
.walkAround = true \
})
/**
* Declares a standalone, named cutsceneitem_t (not a list entry) that
* walks an entity through a fixed sequence of waypoints.
*
* @param NAME Suffix for the generated CUTSCENE_##NAME item/positions
* statics.
* @param ENTITY_INDEX Entity index to move.
* @param ... One or more worldpos_t-shaped waypoint initializers, e.g.
* { X, Y, Z }, { X2, Y2, Z2 }.
*/
#define CUTSCENE_ENTITY_WALK_PATH(NAME, ENTITY_INDEX, ...) \
static const worldpos_t CUTSCENE_##NAME##_POSITIONS[] = { __VA_ARGS__ }; \
static const cutsceneitem_t CUTSCENE_##NAME = { \
.type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \
.entityWalkTo = { \
.entityIndex = ENTITY_INDEX, \
.positions = CUTSCENE_##NAME##_POSITIONS, \
.count = sizeof(CUTSCENE_##NAME##_POSITIONS) / sizeof(worldpos_t), \
.walkAround = true \
} \
};
/**
* Starts an entity walk-to item (resets the waypoint index).
*
@@ -16,6 +16,27 @@ typedef struct {
worldunit_t offsetY;
} cutsceneentitywalktoentity_t;
/**
* Walks ENTITY_INDEX to stand beside TARGET_ENTITY_INDEX, offset by
* (OFFSET_X, OFFSET_Y) on the 2D plane. The destination Z is resolved
* from nearby terrain each frame, so ramps between the two entities are
* accounted for automatically.
*
* @param ENTITY_INDEX Entity index to move.
* @param TARGET_ENTITY_INDEX Entity index to walk toward.
* @param OFFSET_X X offset from the target entity's position.
* @param OFFSET_Y Y offset from the target entity's position.
*/
#define CUTSCENE_ENTITY_WALK_TO_ENTITY( \
ENTITY_INDEX, TARGET_ENTITY_INDEX, OFFSET_X, OFFSET_Y \
) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY, entityWalkToEntity, { \
.entityIndex = ENTITY_INDEX, \
.targetEntityIndex = TARGET_ENTITY_INDEX, \
.offsetX = OFFSET_X, \
.offsetY = OFFSET_Y \
})
/**
* Starts an entity walk-to-entity item. No setup is needed, the destination
* is recomputed from the target's live position every Update.
@@ -14,6 +14,17 @@ typedef struct {
uint8_t quantity;
} cutsceneitemgive_t;
/**
* Adds an item to the player's backpack immediately.
*
* @param ITEM_ID Item ID to give.
* @param QUANTITY Quantity to give.
*/
#define CUTSCENE_ITEM_GIVE(ITEM_ID, QUANTITY) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ITEM_GIVE, itemGive, { \
.item = ITEM_ID, .quantity = QUANTITY \
})
/**
* Starts a give-item step (adds the item to the player's backpack immediately).
*
@@ -8,6 +8,8 @@
#pragma once
#include "rpg/cutscene/item/cutsceneitembase.h"
#include "rpg/overworld/maparea.h"
#include "rpg/cutscene/item/maparea/cutscenemapareawait.h"
#include "rpg/cutscene/item/maparea/cutscenemaparearemove.h"
typedef struct {
worldpos_t min;
@@ -17,6 +19,55 @@ typedef struct {
uint8_t trigger;
} cutscenemapareaadd_t;
/**
* Adds a map area immediately, storing its ID in
* CUTSCENE_SYSTEM.areaLastCreated.
*
* @param MIN_X Area bounding box minimum world X.
* @param MIN_Y Area bounding box minimum world Y.
* @param MIN_Z Area bounding box minimum world Z.
* @param MAX_X Area bounding box maximum world X.
* @param MAX_Y Area bounding box maximum world Y.
* @param MAX_Z Area bounding box maximum world Z.
* @param CALLBACK mapareacallback_t to invoke when the area is triggered.
* @param NOTIFY Notify count/threshold passed to the area.
* @param TRIGGER Trigger count/threshold passed to the area.
*/
#define CUTSCENE_MAP_AREA_ADD( \
MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, CALLBACK, NOTIFY, TRIGGER \
) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_MAP_AREA_ADD, mapAreaAdd, { \
.min = { MIN_X, MIN_Y, MIN_Z }, \
.max = { MAX_X, MAX_Y, MAX_Z }, \
.callback = CALLBACK, \
.notify = NOTIFY, \
.trigger = TRIGGER \
})
/**
* Adds a map area, waits for it to be triggered once, then removes it
* before the cutscene continues. Uses a no-op callback since the wait is
* driven by the area's trigger count rather than callback logic.
*
* @param MIN_X Area bounding box minimum world X.
* @param MIN_Y Area bounding box minimum world Y.
* @param MIN_Z Area bounding box minimum world Z.
* @param MAX_X Area bounding box maximum world X.
* @param MAX_Y Area bounding box maximum world Y.
* @param MAX_Z Area bounding box maximum world Z.
* @param NOTIFY Notify count/threshold passed to the area.
* @param TRIGGER Trigger count/threshold passed to the area.
*/
#define CUTSCENE_MAP_AREA_TRIGGER_ONCE( \
MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, NOTIFY, TRIGGER \
) \
CUTSCENE_MAP_AREA_ADD( \
MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, \
mapAreaNoopCallback, NOTIFY, TRIGGER \
), \
CUTSCENE_MAP_AREA_WAIT(CUTSCENE_AREA_LAST_CREATED), \
CUTSCENE_MAP_AREA_REMOVE(CUTSCENE_AREA_LAST_CREATED)
/**
* Starts a map area add step (adds the area immediately, storing its ID
* in CUTSCENE_SYSTEM.areaLastCreated).
@@ -12,6 +12,17 @@ typedef struct {
uint8_t areaId;
} cutscenemaparearemove_t;
/**
* Removes a map area immediately.
*
* @param AREA_ID Area ID to remove, or CUTSCENE_AREA_LAST_CREATED in
* place of a literal area ID.
*/
#define CUTSCENE_MAP_AREA_REMOVE(AREA_ID) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE, mapAreaRemove, { \
.areaId = AREA_ID \
})
/**
* Starts a map area remove step (removes the area immediately). Accepts
* CUTSCENE_AREA_LAST_CREATED in place of a literal area ID.
@@ -20,6 +20,20 @@ typedef struct {
uint32_t baseline[CUTSCENE_MAP_AREA_WAIT_MAX];
} cutscenemapareawaitdata_t;
/**
* Waits until any one of the given map area IDs has its callback invoked.
*
* @param ... One or more area IDs to watch. Accepts
* CUTSCENE_AREA_LAST_CREATED in place of a literal area ID.
*/
#define CUTSCENE_MAP_AREA_WAIT(...) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT, mapAreaWait, { \
.areaIds = (const uint8_t[]){ __VA_ARGS__ }, \
.count = (uint8_t)( \
sizeof((const uint8_t[]){ __VA_ARGS__ }) / sizeof(uint8_t) \
) \
})
/**
* Starts a map area wait step, snapshotting each watched area's current
* trigger count.
@@ -13,6 +13,22 @@ typedef struct {
const char_t *failureMarker;
} cutscenesavedevicecheck_t;
/**
* (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.
*
* @param SUCCESS_MARKER Marker to jump to if a device is available.
* @param FAILURE_MARKER Marker to jump to if no device is available.
*/
#define CUTSCENE_SAVE_DEVICE_CHECK(SUCCESS_MARKER, FAILURE_MARKER) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK, saveDeviceCheck, { \
.successMarker = SUCCESS_MARKER, \
.failureMarker = FAILURE_MARKER \
})
/**
* Starts a save-device-check item: (re)requests an available save
* device via saveFindAvailableDevice. Never completes on its own - see
@@ -13,6 +13,22 @@ typedef struct {
const char_t *failureMarker;
} cutscenesaveloadallslots_t;
/**
* (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.
*
* @param SUCCESS_MARKER Marker to jump to on success.
* @param FAILURE_MARKER Marker to jump to on failure.
*/
#define CUTSCENE_SAVE_LOAD_ALL_SLOTS(SUCCESS_MARKER, FAILURE_MARKER) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS, saveLoadAllSlots, { \
.successMarker = SUCCESS_MARKER, \
.failureMarker = FAILURE_MARKER \
})
/**
* Starts a save-load-all-slots item: calls saveLoadAllSlots() and jumps
* straight to the item's successMarker or failureMarker via
@@ -15,6 +15,21 @@ typedef struct {
uiemojitype_t emojiType;
} cutsceneemoji_t;
/**
* Shows an emoji above an entity for a duration, then completes
* immediately.
*
* @param ENTITY_INDEX Entity index to show the emoji above.
* @param EMOJI_TYPE uiemojitype_t to show.
* @param DURATION Duration in seconds to show it.
*/
#define CUTSCENE_EMOJI(ENTITY_INDEX, EMOJI_TYPE, DURATION) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_EMOJI, emoji, { \
.entityIndex = ENTITY_INDEX, \
.emojiType = EMOJI_TYPE, \
.duration = DURATION \
})
/**
* Starts an emoji step (shows an emoji above the entity for the given
* duration, then completes immediately).
@@ -17,6 +17,52 @@ typedef struct {
easingtype_t easing;
} cutscenefade_t;
/**
* Longhand - begins a full-screen overlay transition between two colors.
* See the CUTSCENE_FADE_(TO|FROM)_(BLACK|WHITE) shorthands below.
*
* @param FROM Starting color_t.
* @param TO Ending color_t.
* @param DURATION Duration in seconds.
* @param EASING easingtype_t to apply.
*/
#define CUTSCENE_FADE(FROM, TO, DURATION, EASING) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_FADE, fade, { \
.from = FROM, .to = TO, .duration = DURATION, .easing = EASING \
})
/**
* Shorthand: fades from transparent to black over DURATION seconds.
*
* @param DURATION Duration in seconds.
*/
#define CUTSCENE_FADE_TO_BLACK(DURATION) \
CUTSCENE_FADE(COLOR_TRANSPARENT_BLACK, COLOR_BLACK, DURATION, EASING_LINEAR)
/**
* Shorthand: fades from black to transparent over DURATION seconds.
*
* @param DURATION Duration in seconds.
*/
#define CUTSCENE_FADE_FROM_BLACK(DURATION) \
CUTSCENE_FADE(COLOR_BLACK, COLOR_TRANSPARENT_BLACK, DURATION, EASING_LINEAR)
/**
* Shorthand: fades from transparent to white over DURATION seconds.
*
* @param DURATION Duration in seconds.
*/
#define CUTSCENE_FADE_TO_WHITE(DURATION) \
CUTSCENE_FADE(COLOR_TRANSPARENT_WHITE, COLOR_WHITE, DURATION, EASING_LINEAR)
/**
* Shorthand: fades from white to transparent over DURATION seconds.
*
* @param DURATION Duration in seconds.
*/
#define CUTSCENE_FADE_FROM_WHITE(DURATION) \
CUTSCENE_FADE(COLOR_WHITE, COLOR_TRANSPARENT_WHITE, DURATION, EASING_LINEAR)
/**
* Starts a fade item (begins the overlay transition).
*
@@ -14,6 +14,19 @@ typedef struct {
uikeyboardopen_t open;
} cutscenekeyboard_t;
/**
* Opens the on-screen keyboard and blocks the cutscene until it closes,
* then caches whatever was typed - see cutsceneSystemGetTextCache to
* read it back afterwards.
*
* @param ... uikeyboardopen_t designated initializers, e.g.
* CUTSCENE_KEYBOARD(.cancel = true, .maxLength = 8).
*/
#define CUTSCENE_KEYBOARD(...) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_KEYBOARD, keyboard, { \
.open = { __VA_ARGS__ } \
})
/**
* Starts a keyboard item (opens the on-screen keyboard with the item's
* open parameters, unmodified - see uiKeyboardOpen).
@@ -41,6 +41,59 @@ typedef struct {
cutscenemodaloptioncallback_t callback;
} cutscenemodal_t;
/**
* 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.
*
* @param TITLE Modal title - displayed as-is unless it matches a locale
* message ID, in which case the translated string is shown - see
* uiModalLocalize.
* @param MESSAGE Modal message, same locale-matching rule as TITLE.
*/
#define CUTSCENE_MODAL(TITLE, MESSAGE) \
CUTSCENE_ITEM(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.
* Back/cancel input is disabled while it's open (see
* uiMenuSetDisableBack), so it must be dismissed by picking one; CALLBACK
* then fires with the selected option index once the dialog closes.
*
* @param TITLE Modal title (locale-matched, see CUTSCENE_MODAL).
* @param MESSAGE Modal message (locale-matched, see CUTSCENE_MODAL).
* @param CALLBACK cutscenemodaloptioncallback_t fired with the selected
* option index once the dialog closes.
* @param ... Option label strings, e.g.
* CUTSCENE_MODAL_OPTIONS(title, message, callback, "Retry", "Cancel") -
* not copied by this item, so they must stay valid until the modal opens
* (string literals are fine). Each is translated if it matches a locale
* message ID, same as TITLE/MESSAGE.
*/
#define CUTSCENE_MODAL_OPTIONS(TITLE, MESSAGE, CALLBACK, ...) \
CUTSCENE_ITEM(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 }
/**
* Starts a modal item (shows the modal dialog with the item's title,
* message, and options, wiring callback to fire with the selected
@@ -7,6 +7,7 @@
#pragma once
#include "rpg/cutscene/item/cutsceneitembase.h"
#include "rpg/cutscene/item/ui/cutscenemodal.h"
// Backs CUTSCENE_MODAL_OPTIONS_ONE and CUTSCENE_MODAL_OPTIONS_TWO, so two
// is the most either macro ever needs.
@@ -25,6 +26,49 @@ typedef struct {
uint8_t optionCount;
} cutscenemodaloptionsmarkers_t;
/**
* Same as CUTSCENE_MODAL_OPTIONS, but fixed to exactly one option and
* MARKER1 to jump straight to via cutsceneGoTo once it's selected - no
* callback function to write. Does not fall through to whatever follows
* this item, so MARKER1 must be scripted elsewhere in the same cutscene.
*
* @param TITLE Modal title (locale-matched, see CUTSCENE_MODAL).
* @param MESSAGE Modal message (locale-matched, see CUTSCENE_MODAL).
* @param OPTION1 The single option's label.
* @param MARKER1 Marker to jump to when OPTION1 is selected.
*/
#define CUTSCENE_MODAL_OPTIONS_ONE(TITLE, MESSAGE, OPTION1, MARKER1) \
CUTSCENE_ITEM(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. Does not fall through to whatever follows this item, so both
* markers must be scripted elsewhere in the same cutscene.
*
* @param TITLE Modal title (locale-matched, see CUTSCENE_MODAL).
* @param MESSAGE Modal message (locale-matched, see CUTSCENE_MODAL).
* @param OPTION1 First option's label.
* @param OPTION1_MARKER Marker to jump to when OPTION1 is selected.
* @param OPTION2 Second option's label.
* @param OPTION2_MARKER Marker to jump to when OPTION2 is selected.
*/
#define CUTSCENE_MODAL_OPTIONS_TWO( \
TITLE, MESSAGE, OPTION1, OPTION1_MARKER, OPTION2, OPTION2_MARKER \
) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS, modalOptionsMarkers, { \
.title = TITLE, .message = MESSAGE, \
.options = { OPTION1, OPTION2 }, \
.markers = { OPTION1_MARKER, OPTION2_MARKER }, \
.optionCount = 2 \
})
/**
* Starts a modal-options-markers item (shows the modal dialog with the
* item's title, message, and options).
@@ -13,6 +13,18 @@ typedef struct {
float_t duration;
} cutsceneshake_t;
/**
* Kicks off a camera shake immediately.
*
* @param AMOUNT Shake amount, 0 (no shake) to 4 (three tiles): 1 is half
* a tile, 2 is a full tile, 3 is two tiles, and 4 is three tiles.
* @param DURATION Duration in seconds.
*/
#define CUTSCENE_SHAKE(AMOUNT, DURATION) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_SHAKE, shake, { \
.amount = AMOUNT, .duration = DURATION \
})
/**
* Starts a camera shake item (kicks off the shake on the RPG camera).
*
@@ -14,6 +14,14 @@ typedef struct {
char_t text[CUTSCENE_TEXT_MAX_CHARS];
} cutscenetext_t;
/**
* Shows the textbox with TEXT, blocking until it's dismissed.
*
* @param TEXT Text to display.
*/
#define CUTSCENE_TEXT(TEXT) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_TEXT, text, { .text = TEXT })
/**
* Starts a text item (shows the textbox with the item's text).
*
@@ -16,6 +16,23 @@ typedef struct {
float_t duration;
} cutscenetextmini_t;
/**
* Shows a mini textbox at a world position for a duration, then
* completes immediately.
*
* @param TEXT Text to display.
* @param X World X of the mini textbox.
* @param Y World Y of the mini textbox.
* @param Z World Z of the mini textbox.
* @param DURATION Duration in seconds to show it.
*/
#define CUTSCENE_TEXT_MINI(TEXT, X, Y, Z, DURATION) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_TEXT_MINI, textMini, { \
.text = TEXT, \
.position = { X, Y, Z }, \
.duration = DURATION \
})
/**
* Starts a mini text item (shows a mini textbox at the given world
* position for the given duration, then completes immediately).
@@ -12,6 +12,16 @@ typedef struct {
uint8_t index;
} cutscenetextminihide_t;
/**
* Closes a mini textbox immediately.
*
* @param INDEX Mini textbox index to close.
*/
#define CUTSCENE_TEXT_MINI_HIDE(INDEX) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE, textMiniHide, { \
.index = INDEX \
})
/**
* Starts a mini text hide step (closes the mini textbox immediately).
*
@@ -21,6 +21,15 @@ typedef struct {
uiscreentype_t screen;
} cutsceneuishow_t;
/**
* Opens a full-screen UI panel (e.g. the main menu) declaratively, then
* immediately continues on to whatever follows this item.
*
* @param SCREEN uiscreentype_t to open.
*/
#define CUTSCENE_UI_SHOW(SCREEN) \
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_UI_SHOW, uiShow, { .screen = SCREEN })
/**
* Starts a UI_SHOW item, opening the requested full-screen UI panel.
*