Files
dusk/test/rpg/cutscene/test_cutscenesystem.c
T
YourWishesandClaude Sonnet 5 68cebce04a Remove per-item cutscene authoring macros
Every CUTSCENE_XXX(...) shorthand (CUTSCENE_WAIT, CUTSCENE_TEXT,
CUTSCENE_ENTITY_WALK_TO, etc.) existed to build a cutsceneitem_t literal
in C; real cutscenes are now authored exclusively as .jsonc files, and
the only other users were test fixtures. Deletes all ~55 of these
macros (and the shared CUTSCENE_ITEM(...) helper they built on) along
with their doc comments, leaving each item's struct/enum/Start/Update/
Load declarations untouched.

test_battle.c, test_cutscenecontrol.c, test_cutscenemaparea.c,
test_cutscenesystem.c and test_entityinteract.c now build their
cutsceneitem_t fixtures as plain struct literals instead.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-14 11:20:58 -05:00

535 lines
19 KiB
C

/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusktest.h"
#include "rpg/cutscene/cutscenesystem.h"
#include "rpg/entity/entity.h"
#include "time/time.h"
static uint8_t callbackFired;
static void recordCallback(void *userData) {
callbackFired++;
}
// INNER is jumped into by OUTER's second item, never returning -- this is
// 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[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordCallback }
};
static const cutscene_t CUTSCENE_TEST_INNER = {
.items = CUTSCENE_TEST_INNER_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INNER_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_OUTER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 0.5f },
{
.type = CUTSCENE_ITEM_TYPE_CUTSCENE,
.cutsceneRef = { .cutscene = &CUTSCENE_TEST_INNER, .name = NULL }
}
};
static const cutscene_t CUTSCENE_TEST_OUTER = {
.items = CUTSCENE_TEST_OUTER_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_OUTER_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_DEFAULT
};
static void test_cutsceneSystemStartSetsUpInitialItem(void **state) {
cutsceneSystemInit();
cutsceneSystemStartCutscene(&CUTSCENE_TEST_OUTER);
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_OUTER);
assert_int_equal(CUTSCENE_SYSTEM.currentItem, 0);
assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_DEFAULT);
}
static void test_cutsceneSystemNestedCutsceneIsOneWayJump(void **state) {
cutsceneSystemInit();
callbackFired = 0;
cutsceneSystemStartCutscene(&CUTSCENE_TEST_OUTER);
TIME.delta = 0.1f;
cutsceneSystemUpdate();// wait not elapsed yet
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_OUTER);
assert_int_equal(callbackFired, 0);
// The wait elapses, advancing to the nested-cutscene item, which jumps
// straight into INNER and starts its first item (the callback) -- all
// within this single update call.
TIME.delta = 1.0f;
cutsceneSystemUpdate();
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INNER);
assert_int_equal(CUTSCENE_SYSTEM.currentItem, 0);
assert_int_equal(callbackFired, 1);
// INNER's only item (the callback) always reports complete -- one more
// update ends the whole cutscene.
cutsceneSystemUpdate();
assert_null(CUTSCENE_SYSTEM.scene);
assert_int_equal(CUTSCENE_SYSTEM.currentItem, 0xFF);
assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NONE);
}
static void test_cutsceneSystemUpdateIsNoopWithNoActiveCutscene(void **state) {
cutsceneSystemInit();
cutsceneSystemUpdate();// should not crash
assert_null(CUTSCENE_SYSTEM.scene);
}
static const cutsceneitem_t CUTSCENE_TEST_SINGLE_WAIT_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }
};
static const cutscene_t CUTSCENE_TEST_SINGLE_WAIT = {
.items = CUTSCENE_TEST_SINGLE_WAIT_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_SINGLE_WAIT_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static void test_cutsceneSystemStartWithSetsInteractEntities(void **state) {
cutsceneSystemInit();
entityInit(&ENTITIES[0], ENTITY_TYPE_PLAYER);
entityInit(&ENTITIES[1], ENTITY_TYPE_NPC);
cutsceneSystemStartCutsceneWith(
&CUTSCENE_TEST_SINGLE_WAIT, &ENTITIES[0], &ENTITIES[1]
);
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteract, &ENTITIES[0]);
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteracted, &ENTITIES[1]);
assert_ptr_equal(
cutsceneSystemGetEntity(CUTSCENE_ENTITY_INTERACT), &ENTITIES[0]
);
assert_ptr_equal(
cutsceneSystemGetEntity(CUTSCENE_ENTITY_INTERACTED), &ENTITIES[1]
);
TIME.delta = 2.0f;
cutsceneSystemUpdate();// ends the cutscene
assert_null(CUTSCENE_SYSTEM.entityInteract);// reset on end
assert_null(CUTSCENE_SYSTEM.entityInteracted);
}
static void test_cutsceneSystemGetEntitySentinelsRequireBeingSet(
void **state
) {
cutsceneSystemInit();
expect_assert_failure(cutsceneSystemGetEntity(CUTSCENE_ENTITY_INTERACT));
expect_assert_failure(cutsceneSystemGetEntity(CUTSCENE_ENTITY_INTERACTED));
expect_assert_failure(cutsceneSystemGetEntity(CUTSCENE_ENTITY_LAST_CREATED));
expect_assert_failure(cutsceneSystemGetEntity(CUTSCENE_ENTITY_LAST_REF));
}
static void test_cutsceneSystemGetEntityDirectIndexUpdatesLastRef(
void **state
) {
cutsceneSystemInit();
entityInit(&ENTITIES[2], ENTITY_TYPE_NPC);
entity_t *resolved = cutsceneSystemGetEntity(2);
assert_ptr_equal(resolved, &ENTITIES[2]);
// Resolving by direct index also updates LAST_REF.
assert_ptr_equal(
cutsceneSystemGetEntity(CUTSCENE_ENTITY_LAST_REF), &ENTITIES[2]
);
}
static void test_cutsceneSystemGetAreaId(void **state) {
cutsceneSystemInit();
// cutsceneSystemInit() zero-inits the field -- only actually starting a
// cutscene sets it to the "nothing created yet" sentinel.
CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED;
expect_assert_failure(cutsceneSystemGetAreaId(CUTSCENE_AREA_LAST_CREATED));
assert_int_equal(cutsceneSystemGetAreaId(5), 5);// direct IDs pass through
CUTSCENE_SYSTEM.areaLastCreated = 3;
assert_int_equal(cutsceneSystemGetAreaId(CUTSCENE_AREA_LAST_CREATED), 3);
}
static void test_cutsceneSystemGetTextMiniId(void **state) {
cutsceneSystemInit();
CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED;
expect_assert_failure(
cutsceneSystemGetTextMiniId(CUTSCENE_TEXT_MINI_LAST_CREATED)
);
assert_int_equal(cutsceneSystemGetTextMiniId(4), 4);
CUTSCENE_SYSTEM.textMiniLastCreated = 1;
assert_int_equal(
cutsceneSystemGetTextMiniId(CUTSCENE_TEXT_MINI_LAST_CREATED), 1
);
}
static void test_cutsceneSystemDisposeResetsState(void **state) {
cutsceneSystemInit();
entityInit(&ENTITIES[0], ENTITY_TYPE_PLAYER);
cutsceneSystemStartCutsceneWith(
&CUTSCENE_TEST_SINGLE_WAIT, &ENTITIES[0], &ENTITIES[0]
);
cutsceneSystemDispose();
assert_null(CUTSCENE_SYSTEM.scene);
assert_int_equal(CUTSCENE_SYSTEM.currentItem, 0xFF);
assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NONE);
assert_null(CUTSCENE_SYSTEM.entityInteract);
assert_null(CUTSCENE_SYSTEM.entityInteracted);
}
// --- CUTSCENE_INSERT ---------------------------------------------------
static uint8_t insertOrderLog[8];
static uint8_t insertOrderLogCount;
static void recordOrder0(void *userData) { insertOrderLog[insertOrderLogCount++] = 0; }
static void recordOrder1(void *userData) { insertOrderLog[insertOrderLogCount++] = 1; }
static void recordOrder2(void *userData) { insertOrderLog[insertOrderLogCount++] = 2; }
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[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderA },
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f },
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderB }
};
static const cutscene_t CUTSCENE_TEST_INSERT_SNIPPET = {
.items = CUTSCENE_TEST_INSERT_SNIPPET_ITEMS,
.itemCount =
sizeof(CUTSCENE_TEST_INSERT_SNIPPET_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const 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 },
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_SNIPPET, .name = NULL }
},
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder2 }
};
static const cutscene_t CUTSCENE_TEST_INSERT_OUTER = {
.items = CUTSCENE_TEST_INSERT_OUTER_ITEMS,
.itemCount =
sizeof(CUTSCENE_TEST_INSERT_OUTER_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static void test_cutsceneInsertSplicesItemsInPlaceThenResumes(void **state) {
insertOrderLogCount = 0;
cutsceneSystemInit();
cutsceneSystemStartCutscene(&CUTSCENE_TEST_INSERT_OUTER);
TIME.delta = 1.5f;// elapses the outer WAIT(1.0)
cutsceneSystemUpdate();// order0 done -> starts the outer WAIT
cutsceneSystemUpdate();// the WAIT elapses -> starts order1
cutsceneSystemUpdate();// order1 done -> starts the INSERT, cascading into orderA
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_SNIPPET);
TIME.delta = 2.5f;// elapses the snippet's WAIT(2.0)
cutsceneSystemUpdate();// orderA done -> starts snippet's WAIT
cutsceneSystemUpdate();// snippet's WAIT elapses -> starts orderB
cutsceneSystemUpdate();// orderB done -> pops back, starts order2
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_OUTER);
cutsceneSystemUpdate();// order2 done -> cutscene ends naturally
assert_null(CUTSCENE_SYSTEM.scene);
const uint8_t expected[] = { 0, 1, 3, 4, 2 };
assert_int_equal(insertOrderLogCount, sizeof(expected));
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
}
static const 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 = {
.items = CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_OUTER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder0 },
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = {
.cutscene = &CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET, .name = NULL
}
}
};
static const cutscene_t CUTSCENE_TEST_INSERT_AS_LAST_ITEM_OUTER = {
.items = CUTSCENE_TEST_INSERT_AS_LAST_ITEM_OUTER_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_AS_LAST_ITEM_OUTER_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static void test_cutsceneInsertAsLastItemEndsCutsceneNaturally(
void **state
) {
insertOrderLogCount = 0;
cutsceneSystemInit();
cutsceneSystemStartCutscene(&CUTSCENE_TEST_INSERT_AS_LAST_ITEM_OUTER);
cutsceneSystemUpdate();// order0 done -> starts INSERT, cascades to orderA
assert_ptr_equal(
CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_AS_LAST_ITEM_SNIPPET
);
// orderA is the snippet's only item, and the snippet is the outer's last
// item -- finishing it should pop straight through to the natural end
// of the whole cutscene, not get stuck.
cutsceneSystemUpdate();
assert_null(CUTSCENE_SYSTEM.scene);
const uint8_t expected[] = { 0, 3 };
assert_int_equal(insertOrderLogCount, sizeof(expected));
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
}
static const cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_INNER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderA }
};
static const cutscene_t CUTSCENE_TEST_INSERT_NESTED_INNER = {
.items = CUTSCENE_TEST_INSERT_NESTED_INNER_ITEMS,
.itemCount =
sizeof(CUTSCENE_TEST_INSERT_NESTED_INNER_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
// 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[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_NESTED_INNER, .name = NULL }
},
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderB }
};
static const cutscene_t CUTSCENE_TEST_INSERT_NESTED_MID = {
.items = CUTSCENE_TEST_INSERT_NESTED_MID_ITEMS,
.itemCount =
sizeof(CUTSCENE_TEST_INSERT_NESTED_MID_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_NESTED_OUTER_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder0 },
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_NESTED_MID, .name = NULL }
},
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrder2 }
};
static const cutscene_t CUTSCENE_TEST_INSERT_NESTED_OUTER = {
.items = CUTSCENE_TEST_INSERT_NESTED_OUTER_ITEMS,
.itemCount =
sizeof(CUTSCENE_TEST_INSERT_NESTED_OUTER_ITEMS) / sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static void test_cutsceneInsertNestsThroughMultipleLevels(void **state) {
insertOrderLogCount = 0;
cutsceneSystemInit();
cutsceneSystemStartCutscene(&CUTSCENE_TEST_INSERT_NESTED_OUTER);
// order0 done -> starts INSERT(MID), cascading through INSERT(INNER) ->
// orderA, all within this one Update call's Start chain.
cutsceneSystemUpdate();
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_NESTED_INNER);
cutsceneSystemUpdate();// orderA done -> pops to MID, starts orderB
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_NESTED_MID);
cutsceneSystemUpdate();// orderB done -> pops to OUTER, starts order2
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_NESTED_OUTER);
cutsceneSystemUpdate();// order2 done -> ends naturally
assert_null(CUTSCENE_SYSTEM.scene);
const uint8_t expected[] = { 0, 3, 4, 2 };
assert_int_equal(insertOrderLogCount, sizeof(expected));
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
}
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordOrderA }
};
static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5 = {
.items = CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL5, .name = NULL }
}
};
static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4 = {
.items = CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL4, .name = NULL }
}
};
static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3 = {
.items = CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL3, .name = NULL }
}
};
static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2 = {
.items = CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL2, .name = NULL }
}
};
static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1 = {
.items = CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL0_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL1, .name = NULL }
}
};
static const cutscene_t CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL0 = {
.items = CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL0_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL0_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NONE
};
static void test_cutsceneInsertAssertsWhenNestingExceedsStackMax(
void **state
) {
cutsceneSystemInit();
// 5 levels of INSERT deep, one past CUTSCENE_INSERT_STACK_MAX (4) --
// all cascade synchronously within this one call, since each level's
// only item is the INSERT starting immediately.
expect_assert_failure(
cutsceneSystemStartCutscene(&CUTSCENE_TEST_INSERT_OVERFLOW_LEVEL0)
);
}
static void noopInsertCallback(void *userData) {}
static const cutsceneitem_t CUTSCENE_TEST_INSERT_PERSIST_SNIPPET_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = noopInsertCallback }
};
static const cutscene_t CUTSCENE_TEST_INSERT_PERSIST_SNIPPET = {
.items = CUTSCENE_TEST_INSERT_PERSIST_SNIPPET_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_PERSIST_SNIPPET_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_ALL
};
static const cutsceneitem_t CUTSCENE_TEST_INSERT_PERSIST_OUTER_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_INSERT,
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_PERSIST_SNIPPET, .name = NULL }
}
};
static const cutscene_t CUTSCENE_TEST_INSERT_PERSIST_OUTER = {
.items = CUTSCENE_TEST_INSERT_PERSIST_OUTER_ITEMS,
.itemCount = sizeof(CUTSCENE_TEST_INSERT_PERSIST_OUTER_ITEMS) /
sizeof(cutsceneitem_t),
.pause = CUTSCENE_PAUSE_NPC
};
static void test_cutsceneInsertDoesNotResetPauseOrInteractEntities(
void **state
) {
cutsceneSystemInit();
entityInit(&ENTITIES[3], ENTITY_TYPE_PLAYER);
entityInit(&ENTITIES[4], ENTITY_TYPE_NPC);
cutsceneSystemStartCutsceneWith(
&CUTSCENE_TEST_INSERT_PERSIST_OUTER, &ENTITIES[3], &ENTITIES[4]
);
// The INSERT item's Start already cascaded into the snippet by now.
assert_ptr_equal(
CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_PERSIST_SNIPPET
);
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteract, &ENTITIES[3]);
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteracted, &ENTITIES[4]);
// The snippet declares ALL, but INSERT doesn't apply it -- the outer
// cutscene's NPC pause, already in effect, stands.
assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NPC);
}
int main(int argc, char** argv) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_cutsceneSystemStartSetsUpInitialItem),
cmocka_unit_test(test_cutsceneSystemNestedCutsceneIsOneWayJump),
cmocka_unit_test(test_cutsceneSystemUpdateIsNoopWithNoActiveCutscene),
cmocka_unit_test(test_cutsceneSystemStartWithSetsInteractEntities),
cmocka_unit_test(test_cutsceneSystemGetEntitySentinelsRequireBeingSet),
cmocka_unit_test(test_cutsceneSystemGetEntityDirectIndexUpdatesLastRef),
cmocka_unit_test(test_cutsceneSystemGetAreaId),
cmocka_unit_test(test_cutsceneSystemGetTextMiniId),
cmocka_unit_test(test_cutsceneSystemDisposeResetsState),
cmocka_unit_test(test_cutsceneInsertSplicesItemsInPlaceThenResumes),
cmocka_unit_test(test_cutsceneInsertAsLastItemEndsCutsceneNaturally),
cmocka_unit_test(test_cutsceneInsertNestsThroughMultipleLevels),
cmocka_unit_test(test_cutsceneInsertAssertsWhenNestingExceedsStackMax),
cmocka_unit_test(test_cutsceneInsertDoesNotResetPauseOrInteractEntities),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}