1bd73d69fe
- keyframeGetValue now returns the last keyframe's value for times at or beyond it, fixes a missing util/math.h include, and asserts keyframes are sorted by time; adds test/animation/test_keyframe.c - Adds mainmenu scene/UI and a battle HUD UI frame - Adds save autosave-related fields and battle scene tweaks - Adds headless test coverage for cutscenes, entities, and map areas
193 lines
6.2 KiB
C
193 lines
6.2 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.
|
|
CUTSCENE(TEST_INNER, 0, NONE,
|
|
CUTSCENE_CALLBACK(recordCallback)
|
|
);
|
|
|
|
CUTSCENE(TEST_OUTER, 0, DEFAULT,
|
|
CUTSCENE_WAIT(0.5f),
|
|
CUTSCENE_CUTSCENE(TEST_INNER)
|
|
);
|
|
|
|
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);
|
|
}
|
|
|
|
CUTSCENE(TEST_SINGLE_WAIT, 0, NONE,
|
|
CUTSCENE_WAIT(1.0f)
|
|
);
|
|
|
|
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);
|
|
}
|
|
|
|
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),
|
|
};
|
|
|
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
}
|