Add cutsceneAppend/cutsceneAppendNext for mutating a running cutscene

cutsceneAppend adds items to the end of a cutscene_t's item array;
cutsceneAppendNext inserts them at a given index instead (e.g.
CUTSCENE_SYSTEM.currentItem + 1, to run immediately after the
currently-executing item), shifting everything from that index
onward to make room. Both are capacity-bounded against the caller-
supplied itemsMax, matching cutsceneParseDoc's convention of never
assuming ownership of the backing buffer's size.

cutscene_t.items drops its const qualifier to allow this in-place
mutation; cutsceneitem_t backing arrays in the test fixtures that get
assigned into a mutable cutscene_t.items are no longer declared const
either, since a const source would otherwise silently violate the
field's new type.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-09-15 13:16:02 -05:00
co-authored by Claude Sonnet 5
parent dbb5301ade
commit 44594f7011
5 changed files with 97 additions and 21 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
// Fighter slots as added by test_battleCutsceneForceActionOverridesTarget:
// 0 = allyA, 1 = allyB, 2 = enemy.
static const cutsceneitem_t CUTSCENE_TEST_SCRATCH_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_SCRATCH_ITEMS[] = {
// Waiting BEFORE pausing is the correct order: pausing first would
// freeze BATTLE.state wherever it happened to be and it would never
// reach PRE_ROUND on its own to satisfy the wait.
+18 -18
View File
@@ -20,7 +20,7 @@ static void recordCallback(void *userData) {
// the "nested cutscene is a one-way jump" behavior: cutsceneCutsceneStart
// replaces CUTSCENE_SYSTEM.scene outright, and the callback item's effect
// fires in Start, not Update, so it fires the same frame the jump happens.
static const cutsceneitem_t CUTSCENE_TEST_INNER_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INNER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordCallback }
};
static const cutscene_t CUTSCENE_TEST_INNER = {
@@ -29,7 +29,7 @@ static const cutscene_t CUTSCENE_TEST_INNER = {
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_OUTER_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_OUTER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 0.5f },
{
.type = CUTSCENE_ITEM_TYPE_CUTSCENE,
@@ -88,7 +88,7 @@ static void test_cutsceneSystemUpdateIsNoopWithNoActiveCutscene(void **state) {
assert_null(CUTSCENE_SYSTEM.scene);
}
static const cutsceneitem_t CUTSCENE_TEST_SINGLE_WAIT_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_SINGLE_WAIT_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }
};
static const cutscene_t CUTSCENE_TEST_SINGLE_WAIT = {
@@ -206,7 +206,7 @@ static void recordOrder2(void *userData) { insertOrderLog[insertOrderLogCount++]
static void recordOrderA(void *userData) { insertOrderLog[insertOrderLogCount++] = 3; }
static void recordOrderB(void *userData) { insertOrderLog[insertOrderLogCount++] = 4; }
static const cutsceneitem_t CUTSCENE_TEST_INSERT_SNIPPET_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_SNIPPET_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderA },
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f },
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderB }
@@ -218,7 +218,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_SNIPPET = {
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OUTER_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_OUTER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder0 },
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f },
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder1 },
@@ -263,7 +263,7 @@ static void test_cutsceneInsertSplicesItemsInPlaceThenResumes(void **state) {
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
}
static const cutsceneitem_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderA }
};
static const cutscene_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET = {
@@ -273,7 +273,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET = {
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_OUTER_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_OUTER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder0 },
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
@@ -313,7 +313,7 @@ static void test_cutsceneInsertAsLastItemEndsCutsceneNaturally(
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
}
static const cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_INNER_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_INNER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderA }
};
static const cutscene_t CUTSCENE_TEST_INSERT_NESTED_INNER = {
@@ -326,7 +326,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_NESTED_INNER = {
// INSERT is MID's first item, so entering MID (from OUTER) cascades
// straight through into INNER within the same Start chain, one level
// deeper than a single insert.
static const cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_MID_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_MID_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_NESTED_INNER, .name = NULL }
@@ -340,7 +340,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_NESTED_MID = {
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_OUTER_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_OUTER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder0 },
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
@@ -380,7 +380,7 @@ static void test_cutsceneInsertNestsThroughMultipleLevels(void **state) {
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
}
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderA }
};
static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5 = {
@@ -389,7 +389,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5 = {
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5, .name = NULL }
@@ -401,7 +401,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4 = {
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4, .name = NULL }
@@ -413,7 +413,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3 = {
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3, .name = NULL }
@@ -425,7 +425,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2 = {
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2, .name = NULL }
@@ -437,7 +437,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1 = {
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL0_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL0_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1, .name = NULL }
@@ -466,7 +466,7 @@ static void test_cutsceneInsertAssertsWhenNestingExceedsStackMax(
static void noopInsertCallback(void *userData) {}
static const cutsceneitem_t CUTSCENE_TEST_INSERT_PERSIST_SNIPPET_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_PERSIST_SNIPPET_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = noopInsertCallback }
};
static const cutscene_t CUTSCENE_TEST_INSERT_PERSIST_SNIPPET = {
@@ -476,7 +476,7 @@ static const cutscene_t CUTSCENE_TEST_INSERT_PERSIST_SNIPPET = {
.pause = CUTSCENE_PAUSE_ALL
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_PERSIST_OUTER_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INSERT_PERSIST_OUTER_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_PERSIST_SNIPPET, .name = NULL }
+1 -1
View File
@@ -11,7 +11,7 @@
#include "ui/focus/uifocus.h"
#include "rpg/cutscene/cutscenesystem.h"
static const cutsceneitem_t CUTSCENE_TEST_INTERACT_ITEMS[] = {
static cutsceneitem_t CUTSCENE_TEST_INTERACT_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }
};
static const cutscene_t CUTSCENE_TEST_INTERACT = {