CUTSCENE(...)/CUTSCENE_REFERENCE/CUTSCENE_CUTSCENE/CUTSCENE_INSERT existed to build a cutscene_t and its item array directly in C; real cutscenes are now authored exclusively as .jsonc files parsed via cutsceneParseDoc, so these are dead weight in production. Deletes the demo scene that still used them (testcutscene.h, wired to NPC #3) along with its now-dead include in npc.c. test_cutscenesystem.c - the only other user, building cutscenes in-memory for tests - now writes the equivalent struct literals directly, matching the convention already used by test_battle.c/test_entityinteract.c. Co-Authored-By: Claude Sonnet 5 <[email protected]>
535 lines
19 KiB
C
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[] = {
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_CALLBACK(recordOrderA),
|
|
CUTSCENE_WAIT(2.0f),
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_CALLBACK(recordOrder0),
|
|
CUTSCENE_WAIT(1.0f),
|
|
CUTSCENE_CALLBACK(recordOrder1),
|
|
{
|
|
.type = CUTSCENE_ITEM_TYPE_INSERT,
|
|
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_SNIPPET, .name = NULL }
|
|
},
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_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 }
|
|
},
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_CALLBACK(recordOrder0),
|
|
{
|
|
.type = CUTSCENE_ITEM_TYPE_INSERT,
|
|
.insert = { .cutscene = &CUTSCENE_TEST_INSERT_NESTED_MID, .name = NULL }
|
|
},
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_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[] = {
|
|
CUTSCENE_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);
|
|
}
|