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:
@@ -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).
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user