First pass cleaning cutscene system.

This commit is contained in:
2026-09-25 09:57:48 -05:00
parent fed04b66b8
commit 6c180372fe
24 changed files with 203 additions and 237 deletions
+1
View File
@@ -7,6 +7,7 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
cutscene.c cutscene.c
cutscenerefs.c
cutscenesystem.c cutscenesystem.c
) )
+3 -21
View File
@@ -148,26 +148,6 @@ void cutsceneAppendNext(
cutsceneAppendAt(scene, (uint8_t)(currentIndex + 1), items, count); cutsceneAppendAt(scene, (uint8_t)(currentIndex + 1), items, count);
} }
void cutsceneRemoveFront(
cutscene_t *scene,
const uint8_t count
) {
assertNotNull(scene, "Scene cannot be NULL");
assertTrue(count <= scene->itemCount, "count exceeds itemCount");
const size_t tailCount = (size_t)scene->itemCount - count;
// count == 0 has nothing to shift (and would pass dest == src into
// memoryMove, which asserts against that as a no-op call).
if(count > 0 && tailCount > 0) {
memoryMove(
&scene->items[0],
&scene->items[count],
tailCount * sizeof(cutsceneitem_t)
);
}
scene->itemCount -= count;
}
errorret_t cutsceneLoadParse( errorret_t cutsceneLoadParse(
const char_t *path, const char_t *path,
cutscene_t *scene, cutscene_t *scene,
@@ -189,7 +169,9 @@ errorret_t cutsceneLoadParse(
// cutsceneitem_t field that could reference it owns its string data by // cutsceneitem_t field that could reference it owns its string data by
// value, so nothing needs to survive past this call and the entry can // value, so nothing needs to survive past this call and the entry can
// just be unlocked like any other asset once parsing finishes. // just be unlocked like any other asset once parsing finishes.
errorret_t parseResult = cutsceneParseDoc(entry->data.json, scene, items, itemsMax); errorret_t parseResult = cutsceneParseDoc(
entry->data.json, scene, items, itemsMax
);
assetUnlockEntry(entry); assetUnlockEntry(entry);
errorChain(parseResult); errorChain(parseResult);
+1 -18
View File
@@ -128,30 +128,13 @@ void cutsceneAppendNext(
const uint8_t count const uint8_t count
); );
/**
* Removes count items from the front of scene->items, shifting the
* remainder down to index 0 and shrinking scene->itemCount to match. Used
* by cutsceneSystemNext/cutsceneGoTo to reclaim already-run items' slots
* instead of leaving them as dead weight forever - see
* CUTSCENE_SYSTEM.currentItem's doc comment, since the currently-running
* item is always scene->items[0] once a cutscene has started.
*
* @param scene Cutscene to remove from.
* @param count Number of items to remove from the front - must be
* <= scene->itemCount.
*/
void cutsceneRemoveFront(
cutscene_t *scene,
const uint8_t count
);
/** /**
* Loads a cutscene JSON asset by path and parses it into scene/items (see * Loads a cutscene JSON asset by path and parses it into scene/items (see
* cutsceneParseDoc) - the common "lock the asset, require it loaded, * cutsceneParseDoc) - the common "lock the asset, require it loaded,
* parse its 'items' array, unlock it again" sequence shared by every * parse its 'items' array, unlock it again" sequence shared by every
* place that resolves a cutscene by file path (cutsceneSystemLoad, * place that resolves a cutscene by file path (cutsceneSystemLoad,
* cutsceneCutsceneResolve, cutsceneInsertResolveAndSplice, * cutsceneCutsceneResolve, cutsceneInsertResolveAndSplice,
* battleStateExecutingInit, cutsceneGoTo's reload). Purely propagates the * battleStateExecutingInit, cutsceneSystemGoTo's reload). Purely propagates the
* error on failure (like cutsceneParseDoc) - this is core cutscene logic, * error on failure (like cutsceneParseDoc) - this is core cutscene logic,
* not UI, so it doesn't reach for uiFatalErrorOpen itself; each caller * not UI, so it doesn't reach for uiFatalErrorOpen itself; each caller
* decides how to surface the failure (typically errorCatch(errorPrint()) * decides how to surface the failure (typically errorCatch(errorPrint())
+24
View File
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "cutscenerefs.h"
#include "assert/assert.h"
void cutsceneRefsReset(
cutscenesystemrefs_t *refs,
entity_t *interact,
entity_t *interacted
) {
assertNotNull(refs, "Refs cannot be null");
refs->entityInteract = interact;
refs->entityInteracted = interacted;
refs->entityLastCreated = NULL;
refs->entityLastRef = NULL;
refs->areaLastCreated = CUTSCENE_AREA_LAST_CREATED;
refs->textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED;
}
+46
View File
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct entity_s entity_t;
#define CUTSCENE_AREA_LAST_CREATED ((uint8_t)0xFF)
#define CUTSCENE_TEXT_MINI_LAST_CREATED ((uint8_t)0xFA)
/**
* Common cutscene references that should survive a cutsceneRestart -
* grouped into one struct (rather than loose fields on cutscenesystem_t)
* so cutsceneRestart can snapshot/restore all of them in one copy instead
* of field-by-field, and so anything added here later comes along for the
* ride automatically.
*/
typedef struct {
entity_t *entityInteract;
entity_t *entityInteracted;
entity_t *entityLastCreated;
entity_t *entityLastRef;
uint8_t areaLastCreated;
uint8_t textMiniLastCreated;
} cutscenesystemrefs_t;
/**
* Resets refs to a fresh state: interact/interacted set to the given
* entities, everything else (last-created entity, last-referenced entity,
* last-created map area, last-created mini textbox) cleared back to its
* "nothing yet" sentinel.
*
* @param refs Refs to reset.
* @param interact The entity that initiated the interaction, or NULL.
* @param interacted The entity being interacted with, or NULL.
*/
void cutsceneRefsReset(
cutscenesystemrefs_t *refs,
entity_t *interact,
entity_t *interacted
);
+73 -116
View File
@@ -21,16 +21,18 @@ void cutsceneSystemInit() {
CUTSCENE_SYSTEM.loadedScene.itemsMax = CUTSCENE_LOADED_ITEMS_MAX; CUTSCENE_SYSTEM.loadedScene.itemsMax = CUTSCENE_LOADED_ITEMS_MAX;
} }
void cutsceneSystemUpdate() {
if(CUTSCENE_SYSTEM.scene == NULL) return;
const cutsceneitem_t *item = cutsceneSystemGetCurrentItem();
if(cutsceneItemUpdate(item, &CUTSCENE_SYSTEM.data)) cutsceneSystemNext();
}
void cutsceneSystemDispose() { void cutsceneSystemDispose() {
CUTSCENE_SYSTEM.scene = NULL; CUTSCENE_SYSTEM.scene = NULL;
CUTSCENE_SYSTEM.currentItem = 0xFF; CUTSCENE_SYSTEM.currentItem = CUTSCENE_CURRENT_ITEM_NONE;
CUTSCENE_SYSTEM.pause = CUTSCENE_PAUSE_NONE; CUTSCENE_SYSTEM.pause = CUTSCENE_PAUSE_NONE;
CUTSCENE_SYSTEM.entityInteract = NULL; cutsceneRefsReset(&CUTSCENE_SYSTEM.refs, NULL, NULL);
CUTSCENE_SYSTEM.entityInteracted = NULL;
CUTSCENE_SYSTEM.entityLastCreated = NULL;
CUTSCENE_SYSTEM.entityLastRef = NULL;
CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED;
CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED;
CUTSCENE_SYSTEM.textCache[0] = '\0'; CUTSCENE_SYSTEM.textCache[0] = '\0';
CUTSCENE_SYSTEM.onComplete = NULL; CUTSCENE_SYSTEM.onComplete = NULL;
CUTSCENE_SYSTEM.loadedFile[0] = '\0'; CUTSCENE_SYSTEM.loadedFile[0] = '\0';
@@ -45,25 +47,14 @@ void cutsceneSystemPrepare(
CUTSCENE_SYSTEM.scene = cutscene; CUTSCENE_SYSTEM.scene = cutscene;
CUTSCENE_SYSTEM.pause = cutscene->pause; CUTSCENE_SYSTEM.pause = cutscene->pause;
CUTSCENE_SYSTEM.entityInteract = interact; cutsceneRefsReset(&CUTSCENE_SYSTEM.refs, interact, interacted);
CUTSCENE_SYSTEM.entityInteracted = interacted;
CUTSCENE_SYSTEM.entityLastCreated = NULL;
CUTSCENE_SYSTEM.entityLastRef = NULL;
CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED;
CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED;
CUTSCENE_SYSTEM.textCache[0] = '\0'; CUTSCENE_SYSTEM.textCache[0] = '\0';
CUTSCENE_SYSTEM.currentItem = 0xFF;// Set to 0xFF so Next wraps to 0.
CUTSCENE_SYSTEM.onComplete = NULL; CUTSCENE_SYSTEM.onComplete = NULL;
// Only invalidate the known source file when switching away from // cutsceneNext will cause overflow to index 0 (intentional).
// loadedScene entirely. Starting loadedScene itself means CUTSCENE_SYSTEM.currentItem = CUTSCENE_CURRENT_ITEM_NONE;
// cutsceneSystemLoad/cutsceneCutsceneResolve just populated it - for
// cutsceneCutsceneResolve (see cutsceneCutsceneStart), that already // Clear loaded file if cutscene not running from file.
// happened before this call, so clearing loadedFile here would
// immediately undo it. cutsceneGoTo needs loadedFile intact to reload
// a marker behind the current position; cutsceneSystemLoad re-stamps
// it itself right after this call anyway, so it's unaffected either
// way.
if(cutscene != &CUTSCENE_SYSTEM.loadedScene) { if(cutscene != &CUTSCENE_SYSTEM.loadedScene) {
CUTSCENE_SYSTEM.loadedFile[0] = '\0'; CUTSCENE_SYSTEM.loadedFile[0] = '\0';
} }
@@ -84,9 +75,9 @@ void cutsceneSystemStartCutsceneWith(
"Cutscene itemCount exceeds itemsMax" "Cutscene itemCount exceeds itemsMax"
); );
// Prep cutscene, save it to the save slot, and advance, aka overflow to 0.
cutsceneSystemPrepare(cutscene, interact, interacted); cutsceneSystemPrepare(cutscene, interact, interacted);
saveSlotSetCurrentCutscene(CUTSCENE_SYSTEM.loadedFile); saveSlotSetCurrentCutscene(CUTSCENE_SYSTEM.loadedFile);
cutsceneSystemNext(); cutsceneSystemNext();
} }
@@ -96,8 +87,9 @@ void cutsceneSystemStartCutsceneAndGoToMarker(
) { ) {
assertNotNull(marker, "Marker cannot be NULL"); assertNotNull(marker, "Marker cannot be NULL");
// Restarts the cutscene
cutsceneSystemPrepare(cutscene, NULL, NULL); cutsceneSystemPrepare(cutscene, NULL, NULL);
cutsceneGoTo(marker); cutsceneSystemGoTo(marker);
} }
void cutsceneSystemInsertItemsNext( void cutsceneSystemInsertItemsNext(
@@ -109,7 +101,6 @@ void cutsceneSystemInsertItemsNext(
CUTSCENE_SYSTEM.scene, CUTSCENE_SYSTEM.scene,
"cutsceneSystemInsertItemsNext called with no cutscene running" "cutsceneSystemInsertItemsNext called with no cutscene running"
); );
cutsceneAppendNext( cutsceneAppendNext(
CUTSCENE_SYSTEM.scene, CUTSCENE_SYSTEM.currentItem, items, itemCount CUTSCENE_SYSTEM.scene, CUTSCENE_SYSTEM.currentItem, items, itemCount
); );
@@ -128,31 +119,27 @@ void cutsceneSystemInsertCutscene(const cutscene_t *cutscene) {
cutsceneSystemInsertItems(cutscene->items, cutscene->itemCount); cutsceneSystemInsertItems(cutscene->items, cutscene->itemCount);
} }
void cutsceneSystemDiscardQueued(void) { void cutsceneSystemClearAfter(const uint8_t index) {
assertNotNull( assertNotNull(
CUTSCENE_SYSTEM.scene, CUTSCENE_SYSTEM.scene,
"cutsceneSystemDiscardQueued called with no cutscene running" "cutsceneSystemClearAfter called with no cutscene running"
);
assertTrue(
index < CUTSCENE_SYSTEM.scene->itemCount,
"cutsceneSystemClearAfter index out of range"
); );
// currentItem is always 0 once a cutscene has started (see its doc CUTSCENE_SYSTEM.scene->itemCount = index + 1;
// comment) - keep just that one item, drop everything queued after it.
CUTSCENE_SYSTEM.scene->itemCount = 1;
} }
void cutsceneSystemLoad(const char_t *file) { void cutsceneSystemLoad(const char_t *file) {
assertNotNull(file, "File cannot be NULL"); assertNotNull(file, "File cannot be NULL");
// Copy file out before it can be invalidated - every caller today // Take a copy of file, as it may be a part of the cutscene itself.
// passes a string literal, but file could in principle point into
// CUTSCENE_SYSTEM.loadedItems (a running item's own name field, if the
// running cutscene is loadedScene), which both cutsceneLoadParse and
// cutsceneSystemStartCutscene below overwrite/re-enter (see
// cutsceneCutsceneResolve's matching comment for the exact bug this
// guards against - initial.jsonc's closing CUTSCENE item into
// main_menu.jsonc hit it there).
char_t fileCopy[ASSET_FILE_NAME_MAX]; char_t fileCopy[ASSET_FILE_NAME_MAX];
stringCopy(fileCopy, file, ASSET_FILE_NAME_MAX - 1); stringCopy(fileCopy, file, ASSET_FILE_NAME_MAX - 1);
// Load the cutscene asset, parse it, and start it.
errorret_t result = cutsceneLoadParse( errorret_t result = cutsceneLoadParse(
fileCopy, &CUTSCENE_SYSTEM.loadedScene, CUTSCENE_SYSTEM.loadedItems, fileCopy, &CUTSCENE_SYSTEM.loadedScene, CUTSCENE_SYSTEM.loadedItems,
CUTSCENE_LOADED_ITEMS_MAX CUTSCENE_LOADED_ITEMS_MAX
@@ -163,12 +150,7 @@ void cutsceneSystemLoad(const char_t *file) {
return; return;
} }
// Stamped before starting (not after) - cutsceneSystemStartCutsceneWith // Store the loaded file so we can restart if needed.
// reads loadedFile to persist the in-progress cutscene to the save (see
// saveSlotSetCurrentCutscene), so it must already be correct by then.
// Safe: cutsceneSystemPrepare (called from within Start) only clears
// loadedFile when switching away from loadedScene entirely, which this
// isn't.
stringCopy(CUTSCENE_SYSTEM.loadedFile, fileCopy, ASSET_FILE_NAME_MAX - 1); stringCopy(CUTSCENE_SYSTEM.loadedFile, fileCopy, ASSET_FILE_NAME_MAX - 1);
cutsceneSystemStartCutscene(&CUTSCENE_SYSTEM.loadedScene); cutsceneSystemStartCutscene(&CUTSCENE_SYSTEM.loadedScene);
} }
@@ -178,9 +160,13 @@ void cutsceneRestart(void) {
CUTSCENE_SYSTEM.scene, "cutsceneRestart called with no cutscene running" CUTSCENE_SYSTEM.scene, "cutsceneRestart called with no cutscene running"
); );
// Snapshotted as one struct (rather than field-by-field) so anything
// added to cutscenesystemrefs_t later - entities, map areas, mini
// textboxes, etc - survives a restart automatically instead of needing
// its own line here. cutsceneSystemPrepare (called from within both
// branches below) resets refs to a fresh state, so it's restored after.
cutscenecallback_t onComplete = CUTSCENE_SYSTEM.onComplete; cutscenecallback_t onComplete = CUTSCENE_SYSTEM.onComplete;
entity_t *entityInteract = CUTSCENE_SYSTEM.entityInteract; cutscenesystemrefs_t refs = CUTSCENE_SYSTEM.refs;
entity_t *entityInteracted = CUTSCENE_SYSTEM.entityInteracted;
if(CUTSCENE_SYSTEM.loadedFile[0] != '\0') { if(CUTSCENE_SYSTEM.loadedFile[0] != '\0') {
char_t file[ASSET_FILE_NAME_MAX]; char_t file[ASSET_FILE_NAME_MAX];
@@ -188,13 +174,12 @@ void cutsceneRestart(void) {
cutsceneSystemLoad(file); cutsceneSystemLoad(file);
} else { } else {
cutsceneSystemStartCutsceneWith( cutsceneSystemStartCutsceneWith(
CUTSCENE_SYSTEM.scene, entityInteract, entityInteracted CUTSCENE_SYSTEM.scene, refs.entityInteract, refs.entityInteracted
); );
} }
CUTSCENE_SYSTEM.onComplete = onComplete; CUTSCENE_SYSTEM.onComplete = onComplete;
CUTSCENE_SYSTEM.entityInteract = entityInteract; CUTSCENE_SYSTEM.refs = refs;
CUTSCENE_SYSTEM.entityInteracted = entityInteracted;
} }
void cutsceneSystemSetOnComplete(cutscenecallback_t onComplete) { void cutsceneSystemSetOnComplete(cutscenecallback_t onComplete) {
@@ -210,28 +195,28 @@ entity_t * cutsceneSystemGetEntity(const uint8_t entityIndex) {
if(entityIndex == CUTSCENE_ENTITY_INTERACT) { if(entityIndex == CUTSCENE_ENTITY_INTERACT) {
assertNotNull( assertNotNull(
CUTSCENE_SYSTEM.entityInteract, CUTSCENE_SYSTEM.refs.entityInteract,
"CUTSCENE_ENTITY_INTERACT used but no interact entity is set" "CUTSCENE_ENTITY_INTERACT used but no interact entity is set"
); );
entity = CUTSCENE_SYSTEM.entityInteract; entity = CUTSCENE_SYSTEM.refs.entityInteract;
} else if(entityIndex == CUTSCENE_ENTITY_INTERACTED) { } else if(entityIndex == CUTSCENE_ENTITY_INTERACTED) {
assertNotNull( assertNotNull(
CUTSCENE_SYSTEM.entityInteracted, CUTSCENE_SYSTEM.refs.entityInteracted,
"CUTSCENE_ENTITY_INTERACTED used but no interacted entity is set" "CUTSCENE_ENTITY_INTERACTED used but no interacted entity is set"
); );
entity = CUTSCENE_SYSTEM.entityInteracted; entity = CUTSCENE_SYSTEM.refs.entityInteracted;
} else if(entityIndex == CUTSCENE_ENTITY_LAST_CREATED) { } else if(entityIndex == CUTSCENE_ENTITY_LAST_CREATED) {
assertNotNull( assertNotNull(
CUTSCENE_SYSTEM.entityLastCreated, CUTSCENE_SYSTEM.refs.entityLastCreated,
"CUTSCENE_ENTITY_LAST_CREATED used but no entity has been created" "CUTSCENE_ENTITY_LAST_CREATED used but no entity has been created"
); );
entity = CUTSCENE_SYSTEM.entityLastCreated; entity = CUTSCENE_SYSTEM.refs.entityLastCreated;
} else if(entityIndex == CUTSCENE_ENTITY_LAST_REF) { } else if(entityIndex == CUTSCENE_ENTITY_LAST_REF) {
assertNotNull( assertNotNull(
CUTSCENE_SYSTEM.entityLastRef, CUTSCENE_SYSTEM.refs.entityLastRef,
"CUTSCENE_ENTITY_LAST_REF used but no entity has been referenced" "CUTSCENE_ENTITY_LAST_REF used but no entity has been referenced"
); );
entity = CUTSCENE_SYSTEM.entityLastRef; entity = CUTSCENE_SYSTEM.refs.entityLastRef;
} else { } else {
assertTrue( assertTrue(
entityIndex < ENTITY_COUNT, entityIndex < ENTITY_COUNT,
@@ -240,17 +225,17 @@ entity_t * cutsceneSystemGetEntity(const uint8_t entityIndex) {
entity = &ENTITIES[entityIndex]; entity = &ENTITIES[entityIndex];
} }
CUTSCENE_SYSTEM.entityLastRef = entity; CUTSCENE_SYSTEM.refs.entityLastRef = entity;
return entity; return entity;
} }
uint8_t cutsceneSystemGetAreaId(const uint8_t areaId) { uint8_t cutsceneSystemGetAreaId(const uint8_t areaId) {
if(areaId == CUTSCENE_AREA_LAST_CREATED) { if(areaId == CUTSCENE_AREA_LAST_CREATED) {
assertTrue( assertTrue(
CUTSCENE_SYSTEM.areaLastCreated != CUTSCENE_AREA_LAST_CREATED, CUTSCENE_SYSTEM.refs.areaLastCreated != CUTSCENE_AREA_LAST_CREATED,
"CUTSCENE_AREA_LAST_CREATED used but no map area has been created" "CUTSCENE_AREA_LAST_CREATED used but no map area has been created"
); );
return CUTSCENE_SYSTEM.areaLastCreated; return CUTSCENE_SYSTEM.refs.areaLastCreated;
} }
return areaId; return areaId;
} }
@@ -258,11 +243,12 @@ uint8_t cutsceneSystemGetAreaId(const uint8_t areaId) {
uint8_t cutsceneSystemGetTextMiniId(const uint8_t index) { uint8_t cutsceneSystemGetTextMiniId(const uint8_t index) {
if(index == CUTSCENE_TEXT_MINI_LAST_CREATED) { if(index == CUTSCENE_TEXT_MINI_LAST_CREATED) {
assertTrue( assertTrue(
CUTSCENE_SYSTEM.textMiniLastCreated != CUTSCENE_TEXT_MINI_LAST_CREATED, CUTSCENE_SYSTEM.refs.textMiniLastCreated !=
CUTSCENE_TEXT_MINI_LAST_CREATED,
"CUTSCENE_TEXT_MINI_LAST_CREATED used but no mini textbox has been " "CUTSCENE_TEXT_MINI_LAST_CREATED used but no mini textbox has been "
"shown" "shown"
); );
return CUTSCENE_SYSTEM.textMiniLastCreated; return CUTSCENE_SYSTEM.refs.textMiniLastCreated;
} }
return index; return index;
} }
@@ -282,68 +268,49 @@ void cutsceneSystemSetTextCache(const char_t *text) {
void cutsceneSystemNext() { void cutsceneSystemNext() {
if(CUTSCENE_SYSTEM.scene == NULL) return; if(CUTSCENE_SYSTEM.scene == NULL) return;
// Pop the just-finished item off the front rather than advancing an // Pop the just-finished item off the front.
// index further into the array - see CUTSCENE_SYSTEM.currentItem's doc if(CUTSCENE_SYSTEM.currentItem == CUTSCENE_CURRENT_ITEM_NONE) {
// comment. The very first call (currentItem still 0xFF) has nothing to
// pop yet; it just marks the cutscene as started.
if(CUTSCENE_SYSTEM.currentItem == 0xFF) {
CUTSCENE_SYSTEM.currentItem = 0; CUTSCENE_SYSTEM.currentItem = 0;
} else { } else {
cutsceneRemoveFront(CUTSCENE_SYSTEM.scene, 1); cutscene_t *scene = CUTSCENE_SYSTEM.scene;
const size_t tailCount = (size_t)scene->itemCount - 1;
if(tailCount > 0) {
memoryMove(
&scene->items[0], &scene->items[1], tailCount * sizeof(cutsceneitem_t)
);
}
scene->itemCount--;
} }
// End of the cutscene? Note that a CUTSCENE_ITEM_TYPE_INSERT item's // End of the cutscene?
// spliced-in items are physically part of this same array (see
// cutsceneSystemInsertCutscene), so there's no separate "parent scene"
// to fall back into here - itemCount already accounts for them.
if(CUTSCENE_SYSTEM.scene->itemCount == 0) { if(CUTSCENE_SYSTEM.scene->itemCount == 0) {
// Saved and cleared before firing so a callback that immediately
// starts another cutscene (or sets its own onComplete) isn't clobbered
// by this function's own cleanup running after it - same reentrancy
// hazard as uiFocusPop, see src/dusk/ui/focus/uifocus.c.
cutscenecallback_t onComplete = CUTSCENE_SYSTEM.onComplete; cutscenecallback_t onComplete = CUTSCENE_SYSTEM.onComplete;
CUTSCENE_SYSTEM.scene = NULL; CUTSCENE_SYSTEM.scene = NULL;
CUTSCENE_SYSTEM.currentItem = 0xFF;
CUTSCENE_SYSTEM.pause = CUTSCENE_PAUSE_NONE; CUTSCENE_SYSTEM.pause = CUTSCENE_PAUSE_NONE;
CUTSCENE_SYSTEM.entityInteract = NULL;
CUTSCENE_SYSTEM.entityInteracted = NULL;
CUTSCENE_SYSTEM.entityLastCreated = NULL;
CUTSCENE_SYSTEM.entityLastRef = NULL;
CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED;
CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED;
CUTSCENE_SYSTEM.textCache[0] = '\0';
CUTSCENE_SYSTEM.onComplete = NULL;
if(onComplete != NULL) onComplete(NULL); if(onComplete != NULL) onComplete(NULL);
return; return;
} }
// Start item. // Start next (now current) item
const cutsceneitem_t *item = cutsceneSystemGetCurrentItem(); const cutsceneitem_t *item = cutsceneSystemGetCurrentItem();
memset(&CUTSCENE_SYSTEM.data, 0, sizeof(CUTSCENE_SYSTEM.data)); memset(&CUTSCENE_SYSTEM.data, 0, sizeof(CUTSCENE_SYSTEM.data));
cutsceneItemStart(item, &CUTSCENE_SYSTEM.data); cutsceneItemStart(item, &CUTSCENE_SYSTEM.data);
} }
void cutsceneGoTo(const char_t *name) { void cutsceneSystemGoTo(const char_t *name) {
assertNotNull(name, "Name cannot be NULL"); assertNotNull(name, "Name cannot be NULL");
assertNotNull( assertNotNull(
CUTSCENE_SYSTEM.scene, "cutsceneGoTo called with no cutscene running" CUTSCENE_SYSTEM.scene, "cutsceneSystemGoTo called with no cutscene running"
); );
// A marker behind the current position can't be found in the live
// queue - already-run items are evicted from its front as the
// cutscene advances (see cutsceneSystemNext/cutsceneRemoveFront), so
// the untouched original has to be re-parsed from source instead. Only
// supported when the running cutscene is the one cutsceneSystemLoad/
// cutsceneCutsceneResolve populated - a C-authored cutscene_t, or
// content spliced in via INSERT/battleStateExecutingInit, has no
// source to reload from, so a backward jump into those isn't supported
// yet.
assertTrue( assertTrue(
CUTSCENE_SYSTEM.scene == &CUTSCENE_SYSTEM.loadedScene && CUTSCENE_SYSTEM.scene == &CUTSCENE_SYSTEM.loadedScene,
CUTSCENE_SYSTEM.loadedFile[0] != '\0', "cutsceneSystemGoTo requires the running cutscene file reference (1)"
"cutsceneGoTo requires the running cutscene to have come from " );
"cutsceneSystemLoad/cutsceneCutsceneResolve" assertTrue(
CUTSCENE_SYSTEM.loadedFile[0] != '\0',
"cutsceneSystemGoTo requires the running cutscene file reference (2)"
); );
cutscene_t freshScene = { .items = CUTSCENE_SYSTEM.gotoScratchItems }; cutscene_t freshScene = { .items = CUTSCENE_SYSTEM.gotoScratchItems };
@@ -357,6 +324,7 @@ void cutsceneGoTo(const char_t *name) {
return; return;
} }
// Search for the marker with the given name...
for(uint8_t i = 0; i < freshScene.itemCount; i++) { for(uint8_t i = 0; i < freshScene.itemCount; i++) {
const cutsceneitem_t *item = &freshScene.items[i]; const cutsceneitem_t *item = &freshScene.items[i];
if( if(
@@ -364,10 +332,8 @@ void cutsceneGoTo(const char_t *name) {
!stringEquals(item->marker.name, name) !stringEquals(item->marker.name, name)
) continue; ) continue;
// Commit: replace the live queue outright with marker-onward from // Found, so splice the cutscene at that point and start it, the remaining
// the fresh parse. Nothing before the marker (including whatever was // items being the rest of the cutscene after the marker.
// running) gets Started - only the marker item itself does, same as
// the old in-place jump's semantics.
const uint8_t remaining = freshScene.itemCount - i; const uint8_t remaining = freshScene.itemCount - i;
memoryCopy( memoryCopy(
CUTSCENE_SYSTEM.loadedItems, &freshScene.items[i], CUTSCENE_SYSTEM.loadedItems, &freshScene.items[i],
@@ -383,19 +349,10 @@ void cutsceneGoTo(const char_t *name) {
return; return;
} }
assertTrue(false, "cutsceneGoTo: no marker found with that name"); assertTrue(false, "cutsceneSystemGoTo: no marker found with that name");
}
void cutsceneSystemUpdate() {
if(CUTSCENE_SYSTEM.scene == NULL) return;
const cutsceneitem_t *item = cutsceneSystemGetCurrentItem();
if(cutsceneItemUpdate(item, &CUTSCENE_SYSTEM.data)) cutsceneSystemNext();
} }
const cutsceneitem_t * cutsceneSystemGetCurrentItem() { const cutsceneitem_t * cutsceneSystemGetCurrentItem() {
if(CUTSCENE_SYSTEM.scene == NULL) return NULL; if(CUTSCENE_SYSTEM.scene == NULL) return NULL;
// Always index 0 - see CUTSCENE_SYSTEM.currentItem's doc comment.
return &CUTSCENE_SYSTEM.scene->items[0]; return &CUTSCENE_SYSTEM.scene->items[0];
} }
+16 -43
View File
@@ -7,15 +7,13 @@
#pragma once #pragma once
#include "cutscene.h" #include "cutscene.h"
#include "cutscenerefs.h"
typedef struct entity_s entity_t;
#define CUTSCENE_ENTITY_INTERACT ((uint8_t)0xFE) #define CUTSCENE_ENTITY_INTERACT ((uint8_t)0xFE)
#define CUTSCENE_ENTITY_INTERACTED ((uint8_t)0xFD) #define CUTSCENE_ENTITY_INTERACTED ((uint8_t)0xFD)
#define CUTSCENE_ENTITY_LAST_CREATED ((uint8_t)0xFC) #define CUTSCENE_ENTITY_LAST_CREATED ((uint8_t)0xFC)
#define CUTSCENE_ENTITY_LAST_REF ((uint8_t)0xFB) #define CUTSCENE_ENTITY_LAST_REF ((uint8_t)0xFB)
#define CUTSCENE_AREA_LAST_CREATED ((uint8_t)0xFF) #define CUTSCENE_CURRENT_ITEM_NONE ((uint8_t)0xFF)
#define CUTSCENE_TEXT_MINI_LAST_CREATED ((uint8_t)0xFA)
#define CUTSCENE_TEXT_CACHE_MAX 64 #define CUTSCENE_TEXT_CACHE_MAX 64
#define CUTSCENE_LOADED_ITEMS_MAX 128 #define CUTSCENE_LOADED_ITEMS_MAX 128
@@ -23,21 +21,10 @@ typedef struct entity_s entity_t;
typedef struct { typedef struct {
cutscene_t *scene; cutscene_t *scene;
// 0xFF if nothing has started running yet, otherwise always 0 - the
// currently-running item is always scene->items[0]; cutsceneSystemNext
// removes it from the front (see cutsceneRemoveFront) rather than
// advancing an index further into the array, so an item's slot is
// reclaimed the moment it finishes instead of sitting there as dead
// weight for the rest of the cutscene's run.
uint8_t currentItem; uint8_t currentItem;
cutscenepause_t pause; cutscenepause_t pause;
entity_t *entityInteract;
entity_t *entityInteracted; cutscenesystemrefs_t refs;
entity_t *entityLastCreated;
entity_t *entityLastRef;
uint8_t areaLastCreated;
uint8_t textMiniLastCreated;
// Free-form text cache for the running cutscene - see // Free-form text cache for the running cutscene - see
// cutsceneSystemGetTextCache/cutsceneSystemSetTextCache. // cutsceneSystemGetTextCache/cutsceneSystemSetTextCache.
@@ -49,17 +36,12 @@ typedef struct {
// See cutsceneSystemSetOnComplete. // See cutsceneSystemSetOnComplete.
cutscenecallback_t onComplete; cutscenecallback_t onComplete;
// Cache and storage used for the dynamically loaded cutscenes.
cutsceneitem_t loadedItems[CUTSCENE_LOADED_ITEMS_MAX]; cutsceneitem_t loadedItems[CUTSCENE_LOADED_ITEMS_MAX];
cutscene_t loadedScene; cutscene_t loadedScene;
// Filename last passed to cutsceneSystemLoad - see cutsceneRestart.
char_t loadedFile[ASSET_FILE_NAME_MAX]; char_t loadedFile[ASSET_FILE_NAME_MAX];
// Scratch space for cutsceneGoTo's re-parse of loadedFile - a fixed, // Cutscene items scratch, mostly used for CUTSCENE_INSERT items.
// persistent buffer rather than a local one since cutsceneitem_t is
// large (e.g. cutscenemodal_t alone carries a 256-byte message buffer),
// making a CUTSCENE_LOADED_ITEMS_MAX-sized array too big to put on the
// stack on constrained platforms (PSP/GameCube/Wii).
cutsceneitem_t gotoScratchItems[CUTSCENE_LOADED_ITEMS_MAX]; cutsceneitem_t gotoScratchItems[CUTSCENE_LOADED_ITEMS_MAX];
} cutscenesystem_t; } cutscenesystem_t;
@@ -157,15 +139,19 @@ void cutsceneSystemInsertItems(
void cutsceneSystemInsertCutscene(const cutscene_t *cutscene); void cutsceneSystemInsertCutscene(const cutscene_t *cutscene);
/** /**
* Discards every item still queued behind the currently-running one, * Discards every item queued after index (e.g. index 0 keeps only the
* without disturbing that item's own progress - e.g. so a callback that * currently-running item and drops everything spliced in behind it),
* without disturbing index's own progress - e.g. so a callback that
* decides the rest of a batch-spliced sequence is now moot (a battle * decides the rest of a batch-spliced sequence is now moot (a battle
* ending mid-round, with other fighters' still-queued move cutscenes and * ending mid-round, with other fighters' still-queued move cutscenes and
* the round's trailing state transition left behind) can drop it before * the round's trailing state transition left behind) can drop it before
* splicing in its own follow-up items, instead of leaving it to run * splicing in its own follow-up items, instead of leaving it to run
* afterward against torn-down state. * afterward against torn-down state.
*
* @param index Index to keep - everything after it is discarded. Must be
* < the running cutscene's current item count.
*/ */
void cutsceneSystemDiscardQueued(void); void cutsceneSystemClearAfter(const uint8_t index);
/** /**
* Loads and immediately starts a cutscene asset by file name. * Loads and immediately starts a cutscene asset by file name.
@@ -235,25 +221,12 @@ void cutsceneSystemSetTextCache(const char_t *text);
void cutsceneSystemNext(); void cutsceneSystemNext();
/** /**
* Jumps the running cutscene directly to the marker with the given name * Jumps to a cutscene marker directly. If the cutscene is a loaded cutscene,
* and starts it immediately. Re-parses the cutscene fresh from its * this will result in it being reloaded (due to unshift nature of cutscenes).
* source file (see cutsceneLoadParse) and replaces the live queue with
* marker-onward from that fresh parse - items already run may have been
* evicted from the front of the live queue (see
* cutsceneSystemNext/cutsceneRemoveFront), so a marker behind the
* current position can only be found in the untouched original. Nothing
* before the marker gets Started, only the marker item itself.
*
* Requires the running cutscene to be the one cutsceneSystemLoad/
* cutsceneCutsceneResolve populated (i.e. CUTSCENE_SYSTEM.scene ==
* &CUTSCENE_SYSTEM.loadedScene) - asserts otherwise. A C-authored
* cutscene_t, or content spliced in via INSERT/battleStateExecutingInit,
* has no source file to reload from, so backward jumps into those
* aren't supported yet.
* *
* @param name Marker name to search for. * @param name Marker name to search for.
*/ */
void cutsceneGoTo(const char_t *name); void cutsceneSystemGoTo(const char_t *name);
/** /**
* Update the cutscene system for one frame. * Update the cutscene system for one frame.
@@ -39,7 +39,7 @@ void cutsceneBattlePostMoveStart(
// still-pending move cutscenes, the trailing BATTLE_SET_STATE(PRE_ROUND) - // still-pending move cutscenes, the trailing BATTLE_SET_STATE(PRE_ROUND) -
// see battleStateExecutingInit) - the battle is over, so none of that // see battleStateExecutingInit) - the battle is over, so none of that
// should run against the WON/LOST/ENDING transition about to replace it. // should run against the WON/LOST/ENDING transition about to replace it.
cutsceneSystemDiscardQueued(); cutsceneSystemClearAfter(CUTSCENE_SYSTEM.currentItem);
const cutsceneitem_t items[] = { const cutsceneitem_t items[] = {
{ {
@@ -11,7 +11,7 @@
/** /**
* Updates an idle item. Never completes on its own - the cutscene blocks * Updates an idle item. Never completes on its own - the cutscene blocks
* here indefinitely until something external (e.g. a UI callback) calls * here indefinitely until something external (e.g. a UI callback) calls
* cutsceneGoTo to jump elsewhere. * cutsceneSystemGoTo to jump elsewhere.
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
@@ -19,7 +19,7 @@ typedef struct {
/** /**
* Starts a marker item. A marker does nothing on its own - it exists * Starts a marker item. A marker does nothing on its own - it exists
* purely as a named position for cutsceneGoTo to jump to. * purely as a named position for cutsceneSystemGoTo to jump to.
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
+2 -2
View File
@@ -481,9 +481,9 @@ cutscene_t * cutsceneCutsceneResolve(const char_t *name) {
} }
// Shares loadedScene/loadedItems with cutsceneSystemLoad, so track the // Shares loadedScene/loadedItems with cutsceneSystemLoad, so track the
// source the same way it does - cutsceneGoTo needs this to reload the // source the same way it does - cutsceneSystemGoTo needs this to reload the
// untouched original when jumping to a marker behind the current // untouched original when jumping to a marker behind the current
// position (see cutsceneGoTo's doc comment). // position (see cutsceneSystemGoTo's doc comment).
stringCopy(CUTSCENE_SYSTEM.loadedFile, nameCopy, ASSET_FILE_NAME_MAX - 1); stringCopy(CUTSCENE_SYSTEM.loadedFile, nameCopy, ASSET_FILE_NAME_MAX - 1);
return &CUTSCENE_SYSTEM.loadedScene; return &CUTSCENE_SYSTEM.loadedScene;
@@ -25,8 +25,8 @@ void cutsceneEntityAddStart(
entityInit(entity, item->entityAdd.entityType); entityInit(entity, item->entityAdd.entityType);
entityPositionSet(entity, item->entityAdd.position);// Also assigns chunk. entityPositionSet(entity, item->entityAdd.position);// Also assigns chunk.
CUTSCENE_SYSTEM.entityLastCreated = entity; CUTSCENE_SYSTEM.refs.entityLastCreated = entity;
CUTSCENE_SYSTEM.entityLastRef = entity; CUTSCENE_SYSTEM.refs.entityLastRef = entity;
} }
bool_t cutsceneEntityAddUpdate( bool_t cutsceneEntityAddUpdate(
@@ -12,7 +12,7 @@ void cutsceneMapAreaAddStart(
const cutsceneitem_t *item, const cutsceneitem_t *item,
cutsceneitemdata_t *data cutsceneitemdata_t *data
) { ) {
CUTSCENE_SYSTEM.areaLastCreated = mapAreaAdd( CUTSCENE_SYSTEM.refs.areaLastCreated = mapAreaAdd(
item->mapAreaAdd.min, item->mapAreaAdd.min,
item->mapAreaAdd.max, item->mapAreaAdd.max,
item->mapAreaAdd.callback, item->mapAreaAdd.callback,
@@ -21,7 +21,7 @@ typedef struct {
/** /**
* Starts a map area add step (adds the area immediately, storing its ID * Starts a map area add step (adds the area immediately, storing its ID
* in CUTSCENE_SYSTEM.areaLastCreated). * in CUTSCENE_SYSTEM.refs.areaLastCreated).
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
@@ -12,7 +12,7 @@
void cutsceneSaveDeviceCheckCallback(savedevice_t *device, void *user) { void cutsceneSaveDeviceCheckCallback(savedevice_t *device, void *user) {
const cutsceneitem_t *item = cutsceneSystemGetCurrentItem(); const cutsceneitem_t *item = cutsceneSystemGetCurrentItem();
cutsceneGoTo( cutsceneSystemGoTo(
device != NULL ? device != NULL ?
item->saveDeviceCheck.successMarker : item->saveDeviceCheck.successMarker :
item->saveDeviceCheck.failureMarker item->saveDeviceCheck.failureMarker
@@ -31,7 +31,7 @@ void cutsceneSaveDeviceCheckStart(
* Updates a save-device-check item. Like a CUTSCENE_MODAL_OPTIONS item, * Updates a save-device-check item. Like a CUTSCENE_MODAL_OPTIONS item,
* this never completes on its own - once saveFindAvailableDevice's * this never completes on its own - once saveFindAvailableDevice's
* callback fires, it jumps straight to the item's successMarker or * callback fires, it jumps straight to the item's successMarker or
* failureMarker via cutsceneGoTo. * failureMarker via cutsceneSystemGoTo.
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
@@ -17,11 +17,11 @@ void cutsceneSaveLoadAllSlotsStart(
errorret_t result = saveLoadAllSlots(); errorret_t result = saveLoadAllSlots();
if(errorIsNotOk(result)) { if(errorIsNotOk(result)) {
errorCatch(errorPrint(result)); errorCatch(errorPrint(result));
cutsceneGoTo(item->saveLoadAllSlots.failureMarker); cutsceneSystemGoTo(item->saveLoadAllSlots.failureMarker);
return; return;
} }
cutsceneGoTo(item->saveLoadAllSlots.successMarker); cutsceneSystemGoTo(item->saveLoadAllSlots.successMarker);
} }
bool_t cutsceneSaveLoadAllSlotsUpdate( bool_t cutsceneSaveLoadAllSlotsUpdate(
@@ -17,7 +17,7 @@ typedef struct {
/** /**
* Starts a save-load-all-slots item: calls saveLoadAllSlots() and jumps * Starts a save-load-all-slots item: calls saveLoadAllSlots() and jumps
* straight to the item's successMarker or failureMarker via * straight to the item's successMarker or failureMarker via
* cutsceneGoTo, depending on the result. * cutsceneSystemGoTo, depending on the result.
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
@@ -75,7 +75,7 @@ void cutsceneModalStart(
* CUTSCENE_MODAL_OPTIONS) always blocks regardless of the blocking * CUTSCENE_MODAL_OPTIONS) always blocks regardless of the blocking
* field - it's dismissed by the player picking one of them, and the * field - it's dismissed by the player picking one of them, and the
* option callback is responsible for moving the cutscene on from there * option callback is responsible for moving the cutscene on from there
* (typically via cutsceneGoTo or cutsceneRestart). * (typically via cutsceneSystemGoTo or cutsceneRestart).
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
@@ -23,7 +23,7 @@ void cutsceneModalOptionsMarkersCallback(
// the modal directly (uiModalClose) before an option is picked. // the modal directly (uiModalClose) before an option is picked.
const uint8_t index = const uint8_t index =
optionIndex < options->optionCount ? optionIndex : options->optionCount - 1; optionIndex < options->optionCount ? optionIndex : options->optionCount - 1;
cutsceneGoTo(options->markers[index]); cutsceneSystemGoTo(options->markers[index]);
} }
void cutsceneModalOptionsMarkersStart( void cutsceneModalOptionsMarkersStart(
@@ -22,7 +22,7 @@ typedef struct {
char_t message[CUTSCENE_MODAL_MESSAGE_MAX_CHARS]; char_t message[CUTSCENE_MODAL_MESSAGE_MAX_CHARS];
// Each option's display text/locale message ID, and the marker to // Each option's display text/locale message ID, and the marker to
// cutsceneGoTo when that option is selected. Copied by value at parse // cutsceneSystemGoTo when that option is selected. Copied by value at parse
// time (see cutsceneModalOptionsMarkersLoad), so they stay valid // time (see cutsceneModalOptionsMarkersLoad), so they stay valid
// regardless of the backing JSON doc's lifetime. // regardless of the backing JSON doc's lifetime.
char_t options[CUTSCENE_MODAL_OPTIONS_MARKERS_MAX][UI_MODAL_OPTION_TEXT_MAX]; char_t options[CUTSCENE_MODAL_OPTIONS_MARKERS_MAX][UI_MODAL_OPTION_TEXT_MAX];
@@ -47,7 +47,7 @@ void cutsceneModalOptionsMarkersStart(
* the cutscene blocks here indefinitely. Back/cancel input is disabled * the cutscene blocks here indefinitely. Back/cancel input is disabled
* while it's open (see uiMenuSetDisableBack), so it must be dismissed * while it's open (see uiMenuSetDisableBack), so it must be dismissed
* by picking an option, which jumps straight to that option's marker * by picking an option, which jumps straight to that option's marker
* via cutsceneGoTo. * via cutsceneSystemGoTo.
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
@@ -23,7 +23,7 @@ void cutsceneTextMiniStart(
NULL, NULL,
NULL NULL
); );
CUTSCENE_SYSTEM.textMiniLastCreated = index; CUTSCENE_SYSTEM.refs.textMiniLastCreated = index;
} }
bool_t cutsceneTextMiniUpdate( bool_t cutsceneTextMiniUpdate(
+3 -3
View File
@@ -81,7 +81,7 @@ void uiMainMenuStartGame(void) {
if(cutscene == NULL) return; if(cutscene == NULL) return;
cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "NEW_GAME"); cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "NEW_GAME");
} else { } else {
cutsceneGoTo("NEW_GAME"); cutsceneSystemGoTo("NEW_GAME");
} }
cutsceneSystemSetOnComplete(uiMainMenuOpenSelectSave); cutsceneSystemSetOnComplete(uiMainMenuOpenSelectSave);
@@ -93,7 +93,7 @@ void uiMainMenuOptions(void) {
if(cutscene == NULL) return; if(cutscene == NULL) return;
cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "OPTIONS"); cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "OPTIONS");
} else { } else {
cutsceneGoTo("OPTIONS"); cutsceneSystemGoTo("OPTIONS");
} }
} }
@@ -103,7 +103,7 @@ void uiMainMenuQuit(void) {
if(cutscene == NULL) return; if(cutscene == NULL) return;
cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "QUIT"); cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "QUIT");
} else { } else {
cutsceneGoTo("QUIT"); cutsceneSystemGoTo("QUIT");
} }
} }
+14 -14
View File
@@ -111,8 +111,8 @@ static void test_cutsceneSystemStartWithSetsInteractEntities(void **state) {
&CUTSCENE_TEST_SINGLE_WAIT, &ENTITIES[0], &ENTITIES[1] &CUTSCENE_TEST_SINGLE_WAIT, &ENTITIES[0], &ENTITIES[1]
); );
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteract, &ENTITIES[0]); assert_ptr_equal(CUTSCENE_SYSTEM.refs.entityInteract, &ENTITIES[0]);
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteracted, &ENTITIES[1]); assert_ptr_equal(CUTSCENE_SYSTEM.refs.entityInteracted, &ENTITIES[1]);
assert_ptr_equal( assert_ptr_equal(
cutsceneSystemGetEntity(CUTSCENE_ENTITY_INTERACT), &ENTITIES[0] cutsceneSystemGetEntity(CUTSCENE_ENTITY_INTERACT), &ENTITIES[0]
); );
@@ -122,8 +122,8 @@ static void test_cutsceneSystemStartWithSetsInteractEntities(void **state) {
TIME.delta = 2.0f; TIME.delta = 2.0f;
cutsceneSystemUpdate();// ends the cutscene cutsceneSystemUpdate();// ends the cutscene
assert_null(CUTSCENE_SYSTEM.entityInteract);// reset on end assert_null(CUTSCENE_SYSTEM.refs.entityInteract);// reset on end
assert_null(CUTSCENE_SYSTEM.entityInteracted); assert_null(CUTSCENE_SYSTEM.refs.entityInteracted);
} }
static void test_cutsceneSystemGetEntitySentinelsRequireBeingSet( static void test_cutsceneSystemGetEntitySentinelsRequireBeingSet(
@@ -158,26 +158,26 @@ static void test_cutsceneSystemGetAreaId(void **state) {
cutsceneSystemInit(); cutsceneSystemInit();
// cutsceneSystemInit() zero-inits the field -- only actually starting a // cutsceneSystemInit() zero-inits the field -- only actually starting a
// cutscene sets it to the "nothing created yet" sentinel. // cutscene sets it to the "nothing created yet" sentinel.
CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED; CUTSCENE_SYSTEM.refs.areaLastCreated = CUTSCENE_AREA_LAST_CREATED;
expect_assert_failure(cutsceneSystemGetAreaId(CUTSCENE_AREA_LAST_CREATED)); expect_assert_failure(cutsceneSystemGetAreaId(CUTSCENE_AREA_LAST_CREATED));
assert_int_equal(cutsceneSystemGetAreaId(5), 5);// direct IDs pass through assert_int_equal(cutsceneSystemGetAreaId(5), 5);// direct IDs pass through
CUTSCENE_SYSTEM.areaLastCreated = 3; CUTSCENE_SYSTEM.refs.areaLastCreated = 3;
assert_int_equal(cutsceneSystemGetAreaId(CUTSCENE_AREA_LAST_CREATED), 3); assert_int_equal(cutsceneSystemGetAreaId(CUTSCENE_AREA_LAST_CREATED), 3);
} }
static void test_cutsceneSystemGetTextMiniId(void **state) { static void test_cutsceneSystemGetTextMiniId(void **state) {
cutsceneSystemInit(); cutsceneSystemInit();
CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED; CUTSCENE_SYSTEM.refs.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED;
expect_assert_failure( expect_assert_failure(
cutsceneSystemGetTextMiniId(CUTSCENE_TEXT_MINI_LAST_CREATED) cutsceneSystemGetTextMiniId(CUTSCENE_TEXT_MINI_LAST_CREATED)
); );
assert_int_equal(cutsceneSystemGetTextMiniId(4), 4); assert_int_equal(cutsceneSystemGetTextMiniId(4), 4);
CUTSCENE_SYSTEM.textMiniLastCreated = 1; CUTSCENE_SYSTEM.refs.textMiniLastCreated = 1;
assert_int_equal( assert_int_equal(
cutsceneSystemGetTextMiniId(CUTSCENE_TEXT_MINI_LAST_CREATED), 1 cutsceneSystemGetTextMiniId(CUTSCENE_TEXT_MINI_LAST_CREATED), 1
); );
@@ -195,15 +195,15 @@ static void test_cutsceneSystemDisposeResetsState(void **state) {
assert_null(CUTSCENE_SYSTEM.scene); assert_null(CUTSCENE_SYSTEM.scene);
assert_int_equal(CUTSCENE_SYSTEM.currentItem, 0xFF); assert_int_equal(CUTSCENE_SYSTEM.currentItem, 0xFF);
assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NONE); assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NONE);
assert_null(CUTSCENE_SYSTEM.entityInteract); assert_null(CUTSCENE_SYSTEM.refs.entityInteract);
assert_null(CUTSCENE_SYSTEM.entityInteracted); assert_null(CUTSCENE_SYSTEM.refs.entityInteracted);
} }
// --- loadedFile survival across cutsceneSystemPrepare ------------------- // --- loadedFile survival across cutsceneSystemPrepare -------------------
// //
// cutsceneCutsceneResolve (and cutsceneSystemLoad) stamp // cutsceneCutsceneResolve (and cutsceneSystemLoad) stamp
// CUTSCENE_SYSTEM.loadedFile with what they just parsed loadedScene from // CUTSCENE_SYSTEM.loadedFile with what they just parsed loadedScene from
// - cutsceneGoTo needs that to survive so it can reload the same source // - cutsceneSystemGoTo needs that to survive so it can reload the same source
// later. cutsceneCutsceneResolve's caller stamps it BEFORE calling // later. cutsceneCutsceneResolve's caller stamps it BEFORE calling
// cutsceneSystemStartCutscene(&CUTSCENE_SYSTEM.loadedScene), so // cutsceneSystemStartCutscene(&CUTSCENE_SYSTEM.loadedScene), so
// cutsceneSystemPrepare must not blindly clear loadedFile on every // cutsceneSystemPrepare must not blindly clear loadedFile on every
@@ -212,7 +212,7 @@ static void test_cutsceneSystemDisposeResetsState(void **state) {
// regression-tests the exact bug hit via initial.jsonc's CUTSCENE item // regression-tests the exact bug hit via initial.jsonc's CUTSCENE item
// into main_menu.jsonc: cutsceneCutsceneResolve stamped loadedFile, then // into main_menu.jsonc: cutsceneCutsceneResolve stamped loadedFile, then
// the immediately following cutsceneSystemStartCutscene call wiped it // the immediately following cutsceneSystemStartCutscene call wiped it
// straight back out, so a later cutsceneGoTo on the still-running // straight back out, so a later cutsceneSystemGoTo on the still-running
// cutscene had nothing to reload from. // cutscene had nothing to reload from.
static void test_cutsceneSystemPrepareKeepsLoadedFileWhenReenteringLoadedScene( static void test_cutsceneSystemPrepareKeepsLoadedFileWhenReenteringLoadedScene(
void **state void **state
@@ -551,8 +551,8 @@ static void test_cutsceneInsertDoesNotResetPauseOrInteractEntities(
// been popped off the front, only the snippet's one item is left. // been popped off the front, only the snippet's one item is left.
assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_PERSIST_OUTER); assert_ptr_equal(CUTSCENE_SYSTEM.scene, &CUTSCENE_TEST_INSERT_PERSIST_OUTER);
assert_int_equal(CUTSCENE_SYSTEM.scene->itemCount, 1); assert_int_equal(CUTSCENE_SYSTEM.scene->itemCount, 1);
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteract, &ENTITIES[3]); assert_ptr_equal(CUTSCENE_SYSTEM.refs.entityInteract, &ENTITIES[3]);
assert_ptr_equal(CUTSCENE_SYSTEM.entityInteracted, &ENTITIES[4]); assert_ptr_equal(CUTSCENE_SYSTEM.refs.entityInteracted, &ENTITIES[4]);
// The snippet declares ALL, but INSERT doesn't apply it -- the outer // The snippet declares ALL, but INSERT doesn't apply it -- the outer
// cutscene's NPC pause, already in effect, stands. // cutscene's NPC pause, already in effect, stands.
assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NPC); assert_int_equal(CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NPC);