diff --git a/src/dusk/rpg/cutscene/cutscene.c b/src/dusk/rpg/cutscene/cutscene.c index 9827a379..7e562b10 100644 --- a/src/dusk/rpg/cutscene/cutscene.c +++ b/src/dusk/rpg/cutscene/cutscene.c @@ -105,3 +105,41 @@ errorret_t cutsceneParseDoc( errorOk(); } + +void cutsceneAppend( + cutscene_t *scene, + const size_t itemsMax, + const cutsceneitem_t *items, + const uint8_t count +) { + assertNotNull(scene, "Scene cannot be NULL"); + cutsceneAppendNext(scene, itemsMax, scene->itemCount, items, count); +} + +void cutsceneAppendNext( + cutscene_t *scene, + const size_t itemsMax, + const uint8_t atIndex, + const cutsceneitem_t *items, + const uint8_t count +) { + assertNotNull(scene, "Scene cannot be NULL"); + assertNotNull(items, "Items cannot be NULL"); + assertTrue(count > 0, "Count cannot be 0"); + assertTrue(atIndex <= scene->itemCount, "atIndex out of bounds"); + assertTrue( + (size_t)scene->itemCount + count <= itemsMax, + "Cutscene item append exceeds itemsMax" + ); + + const size_t tailCount = (size_t)scene->itemCount - atIndex; + if(tailCount > 0) { + memoryMove( + &scene->items[atIndex + count], + &scene->items[atIndex], + tailCount * sizeof(cutsceneitem_t) + ); + } + memoryCopy(&scene->items[atIndex], items, count * sizeof(cutsceneitem_t)); + scene->itemCount += count; +} diff --git a/src/dusk/rpg/cutscene/cutscene.h b/src/dusk/rpg/cutscene/cutscene.h index 96cd1b2a..f2b69da5 100644 --- a/src/dusk/rpg/cutscene/cutscene.h +++ b/src/dusk/rpg/cutscene/cutscene.h @@ -10,7 +10,7 @@ #include "rpg/cutscene/cutscenepause.h" typedef struct cutscene_s { - const cutsceneitem_t *items; + cutsceneitem_t *items; uint8_t itemCount; cutscenepause_t pause; } cutscene_t; @@ -66,3 +66,41 @@ errorret_t cutsceneParseDoc( cutsceneitem_t *items, size_t itemsMax ); + +/** + * Appends items onto the end of scene->items, growing scene->itemCount. + * scene->items must point at a buffer with capacity itemsMax - asserts if + * appending would exceed it. + * + * @param scene Cutscene to append to. + * @param itemsMax Capacity of scene->items. + * @param items Items to append, copied in order. + * @param count Number of entries in items. + */ +void cutsceneAppend( + cutscene_t *scene, + const size_t itemsMax, + const cutsceneitem_t *items, + const uint8_t count +); + +/** + * Same as cutsceneAppend, but inserts items starting at atIndex instead of + * at the end - e.g. pass CUTSCENE_SYSTEM.currentItem + 1 to make these + * items run immediately after the currently-executing one. Every existing + * item from atIndex onward shifts right by count to make room. + * + * @param scene Cutscene to insert into. + * @param itemsMax Capacity of scene->items. + * @param atIndex Index the first inserted item should end up at - must be + * <= scene->itemCount. + * @param items Items to insert, copied in order. + * @param count Number of entries in items. + */ +void cutsceneAppendNext( + cutscene_t *scene, + const size_t itemsMax, + const uint8_t atIndex, + const cutsceneitem_t *items, + const uint8_t count +); diff --git a/test/rpg/battle/test_battle.c b/test/rpg/battle/test_battle.c index badd657d..2693ac9b 100644 --- a/test/rpg/battle/test_battle.c +++ b/test/rpg/battle/test_battle.c @@ -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. diff --git a/test/rpg/cutscene/test_cutscenesystem.c b/test/rpg/cutscene/test_cutscenesystem.c index 88116e9f..8be8bf04 100644 --- a/test/rpg/cutscene/test_cutscenesystem.c +++ b/test/rpg/cutscene/test_cutscenesystem.c @@ -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 } diff --git a/test/rpg/entity/test_entityinteract.c b/test/rpg/entity/test_entityinteract.c index cdb920ee..cf032bd2 100644 --- a/test/rpg/entity/test_entityinteract.c +++ b/test/rpg/entity/test_entityinteract.c @@ -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 = {