Sunset the dedicated cutscene asset loader and the item pool
Cutscenes now load through the generic ASSET_LOADER_TYPE_JSON loader and parse straight into fixed static buffers via new cutsceneParseDoc/ cutsceneParseItem/cutsceneCountItems helpers (cutscene.c), removing the bespoke ASSET_LOADER_TYPE_CUTSCENE pipeline entirely. Only one cutscene (plus one INSERT target) is ever resident, so each resolve locks, steals the parsed doc, unlocks, and force-reaps immediately - fixing a stale-cache-reuse bug where revisiting a cutscene name after a different one could return data from the shared buffer's previous occupant. ENTITY_WALK_TO and MAP_AREA_WAIT now store their waypoints/area ids in fixed-size arrays inline on their own item struct instead of a shared bump-allocated pool, so pool/poolOffset are gone from every item type's Load callback. cutscene_t.dataSize (and the CUTSCENE(...) macro's SIZE parameter) is removed along with it - it sized entries out of that same now-deleted pool and had no other reader. NPC #4's battletest cutscene and the main menu's cutscene now resolve their target by name on interact instead of eagerly at init, so a resolved-once pointer is never held past the point its backing buffer can be reused for something else. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -17,6 +17,5 @@ add_subdirectory(locale)
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(chunk)
|
||||
add_subdirectory(dmf)
|
||||
add_subdirectory(cutscene)
|
||||
add_subdirectory(wav)
|
||||
add_subdirectory(mp3)
|
||||
@@ -52,12 +52,6 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
|
||||
.dispose = assetChunkDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_CUTSCENE] = {
|
||||
.loadSync = assetCutsceneLoaderSync,
|
||||
.loadAsync = assetCutsceneLoaderAsync,
|
||||
.dispose = assetCutsceneDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_WAV] = {
|
||||
.loadSync = assetWavLoaderSync,
|
||||
.loadAsync = assetWavLoaderAsync,
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
#include "asset/loader/json/assetjsonloader.h"
|
||||
#include "asset/loader/chunk/assetchunkloader.h"
|
||||
#include "asset/loader/cutscene/assetcutsceneloader.h"
|
||||
#include "asset/loader/wav/assetwavloader.h"
|
||||
#include "asset/loader/mp3/assetmp3loader.h"
|
||||
|
||||
@@ -27,7 +26,6 @@ typedef enum {
|
||||
ASSET_LOADER_TYPE_LOCALE,
|
||||
ASSET_LOADER_TYPE_JSON,
|
||||
ASSET_LOADER_TYPE_CHUNK,
|
||||
ASSET_LOADER_TYPE_CUTSCENE,
|
||||
ASSET_LOADER_TYPE_WAV,
|
||||
ASSET_LOADER_TYPE_MP3,
|
||||
|
||||
@@ -42,7 +40,6 @@ typedef union {
|
||||
assetlocaleloaderloading_t locale;
|
||||
assetjsonloaderloading_t json;
|
||||
assetchunkloaderloading_t chunk;
|
||||
assetcutsceneloaderloading_t cutscene;
|
||||
assetwavloaderloading_t wav;
|
||||
assetmp3loaderloading_t mp3;
|
||||
} assetloaderloading_t;
|
||||
@@ -55,7 +52,6 @@ typedef union {
|
||||
assetlocaleoutput_t locale;
|
||||
assetjsonoutput_t json;
|
||||
assetchunkoutput_t chunk;
|
||||
assetcutsceneoutput_t cutscene;
|
||||
assetwavoutput_t wav;
|
||||
assetmp3output_t mp3;
|
||||
} assetloaderoutput_t;
|
||||
@@ -66,7 +62,6 @@ typedef union {
|
||||
assetlocaleloaderinput_t locale;
|
||||
assetjsonloaderinput_t json;
|
||||
assetchunkloaderinput_t chunk;
|
||||
assetcutsceneloaderinput_t cutscene;
|
||||
assetwavloaderinput_t wav;
|
||||
assetmp3loaderinput_t mp3;
|
||||
} assetloaderinput_t;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
assetcutsceneloader.c
|
||||
)
|
||||
@@ -1,258 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetcutsceneloader.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
#include "asset/loader/assetloader.h"
|
||||
#include "asset/asset.h"
|
||||
#include "rpg/cutscene/item/json/cutscenejsonpauseflags.h"
|
||||
|
||||
errorret_t assetCutsceneParseItem(
|
||||
assetloading_t *loading,
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset,
|
||||
const uint8_t index
|
||||
) {
|
||||
if(!yyjson_is_obj(itemObj)) {
|
||||
assetLoaderErrorThrow(
|
||||
loading,
|
||||
"Cutscene item %u is not an object",
|
||||
(uint32_t)index
|
||||
);
|
||||
}
|
||||
|
||||
const char_t *typeName = yyjson_get_str(yyjson_obj_get(itemObj, "type"));
|
||||
if(!cutsceneItemLookupType(typeName, &item->type)) {
|
||||
assetLoaderErrorThrow(
|
||||
loading,
|
||||
"Cutscene item %u: missing/unsupported item type %s",
|
||||
(uint32_t)index,
|
||||
typeName
|
||||
);
|
||||
}
|
||||
|
||||
cutsceneitemloadcallback_t *load = CUTSCENE_ITEM_CALLBACKS[item->type].load;
|
||||
if(load == NULL) {
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene item %u: unhandled item type %u", (uint32_t)index,
|
||||
(uint32_t)item->type
|
||||
);
|
||||
}
|
||||
|
||||
errorret_t result = load(itemObj, item, pool, poolOffset);
|
||||
if(errorIsNotOk(result)) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
errorChain(result);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
size_t assetCutsceneComputePoolSize(yyjson_val *itemsArr) {
|
||||
size_t size = 0;
|
||||
yyjson_arr_iter iter = yyjson_arr_iter_with(itemsArr);
|
||||
yyjson_val *itemVal;
|
||||
while((itemVal = yyjson_arr_iter_next(&iter)) != NULL) {
|
||||
const char_t *typeName = yyjson_get_str(yyjson_obj_get(itemVal, "type"));
|
||||
cutsceneitemtype_t type;
|
||||
if(!cutsceneItemLookupType(typeName, &type)) continue;
|
||||
|
||||
cutsceneitempoolsizecallback_t *poolSize =
|
||||
CUTSCENE_ITEM_CALLBACKS[type].poolSize;
|
||||
if(poolSize == NULL) continue;
|
||||
|
||||
size = cutsceneJsonPoolAlign(size) + poolSize(itemVal);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// Raw file-read scratch space - fixed-size (ASSET_CUTSCENE_FILE_SIZE_MAX is
|
||||
// already a hard cap below) rather than malloc'd per load. Safe to share:
|
||||
// ASSET only ever runs one async load at a time on its single load thread
|
||||
// (see assetUpdateAsync), and yyjson_read copies everything it needs out
|
||||
// of this buffer into the doc before this function returns, so it's free
|
||||
// to be overwritten by the next load.
|
||||
static uint8_t CUTSCENE_FILE_BUFFER[ASSET_CUTSCENE_FILE_SIZE_MAX];
|
||||
|
||||
errorret_t assetCutsceneLoaderAsync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertNotMainThread("Should be called from an async thread.");
|
||||
|
||||
if(
|
||||
loading->loading.cutscene.state != ASSET_CUTSCENE_LOADING_STATE_READ_FILE
|
||||
) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
const assetcutsceneloaderinput_t *input = loading->entry->input != NULL
|
||||
? &loading->entry->input->cutscene
|
||||
: NULL;
|
||||
const bool_t toInsert =
|
||||
input != NULL && input->dest == ASSET_CUTSCENE_LOAD_DEST_INSERT;
|
||||
const char_t *filePath = (input != NULL && input->path[0] != '\0')
|
||||
? input->path
|
||||
: loading->entry->name;
|
||||
|
||||
cutsceneitem_t *targetItems =
|
||||
toInsert ? CUTSCENE_INSERT_ITEMS : CUTSCENE_LOADED_ITEMS;
|
||||
const size_t targetItemsMax =
|
||||
toInsert ? CUTSCENE_INSERT_ITEMS_MAX : CUTSCENE_LOADED_ITEMS_MAX;
|
||||
uint8_t *targetPool = toInsert ? CUTSCENE_INSERT_POOL : CUTSCENE_LOADED_POOL;
|
||||
const size_t targetPoolMax =
|
||||
toInsert ? CUTSCENE_INSERT_POOL_SIZE_MAX : CUTSCENE_LOADED_POOL_SIZE_MAX;
|
||||
cutscene_t *targetScene = toInsert ? &CUTSCENE_INSERT_SCENE : &CUTSCENE_LOADED_SCENE;
|
||||
|
||||
assetfile_t *file = &loading->loading.cutscene.file;
|
||||
assetLoaderErrorChain(loading,
|
||||
assetFileInit(file, filePath, NULL, NULL)
|
||||
);
|
||||
|
||||
if(file->size > ASSET_CUTSCENE_FILE_SIZE_MAX) {
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene JSON exceeds maximum allowed size"
|
||||
);
|
||||
}
|
||||
|
||||
size_t fileSize = (size_t)file->size;
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileRead(file, CUTSCENE_FILE_BUFFER, fileSize)
|
||||
);
|
||||
assertTrue(
|
||||
file->lastRead == file->size,
|
||||
"Failed to read entire cutscene file."
|
||||
);
|
||||
assetLoaderErrorChain(loading, assetFileClose(file));
|
||||
assetLoaderErrorChain(loading, assetFileDispose(file));
|
||||
|
||||
// Parsing is pure CPU work with no main-thread dependency (every item
|
||||
// type's load callback just parses fields - see cutsceneCutsceneLoad's
|
||||
// doc comment for how CUTSCENE items avoid needing one), so it happens
|
||||
// right here instead of bouncing to the sync phase.
|
||||
yyjson_doc *doc = yyjson_read(
|
||||
(char *)CUTSCENE_FILE_BUFFER, fileSize,
|
||||
YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS
|
||||
);
|
||||
if(doc == NULL) {
|
||||
assetLoaderErrorThrow(loading, "Failed to parse cutscene JSON");
|
||||
}
|
||||
|
||||
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||
yyjson_val *itemsArr = yyjson_obj_get(root, "items");
|
||||
if(!yyjson_is_arr(itemsArr)) {
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(loading, "Cutscene JSON is missing an 'items' array");
|
||||
}
|
||||
|
||||
size_t itemCount = yyjson_arr_size(itemsArr);
|
||||
if(itemCount > targetItemsMax) {
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene has more items than its target buffer allows"
|
||||
);
|
||||
}
|
||||
|
||||
cutscenepause_t pause = CUTSCENE_PAUSE_DEFAULT;
|
||||
yyjson_val *pauseArr = yyjson_obj_get(root, "pause");
|
||||
if(yyjson_is_arr(pauseArr)) {
|
||||
if(!cutsceneJsonParsePauseFlags(pauseArr, &pause)) {
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene JSON has an invalid 'pause' flag"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
size_t poolSize = assetCutsceneComputePoolSize(itemsArr);
|
||||
if(poolSize > targetPoolMax) {
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene needs more pool space than its target buffer allows"
|
||||
);
|
||||
}
|
||||
|
||||
// Everything structural has validated by this point, so it's safe to
|
||||
// start overwriting the target's shared items/pool buffers - a per-item
|
||||
// parse failure past this point still leaves them partially overwritten,
|
||||
// but that's fine: any load failure here already sends the game into the
|
||||
// fatal-error overlay (see cutsceneSystemLoad/cutsceneCutsceneResolve/
|
||||
// cutsceneInsertResolve), so nothing keeps running against stale data.
|
||||
memoryZero(targetItems, itemCount * sizeof(cutsceneitem_t));
|
||||
|
||||
size_t poolOffset = 0;
|
||||
yyjson_arr_iter itemIter = yyjson_arr_iter_with(itemsArr);
|
||||
yyjson_val *itemVal;
|
||||
uint8_t index = 0;
|
||||
while((itemVal = yyjson_arr_iter_next(&itemIter)) != NULL) {
|
||||
errorret_t itemResult = assetCutsceneParseItem(
|
||||
loading, itemVal, &targetItems[index],
|
||||
targetPool, &poolOffset, index
|
||||
);
|
||||
if(errorIsNotOk(itemResult)) {
|
||||
yyjson_doc_free(doc);
|
||||
errorChain(itemResult);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// Takes ownership of doc - marker/cutscene-reference name fields point
|
||||
// directly into it (see cutsceneCutsceneRefLoad/cutsceneMarkerLoad), so
|
||||
// it must outlive this load, replacing whatever the previously loaded
|
||||
// cutscene left behind for this same destination.
|
||||
if(toInsert) cutsceneInsertSetDoc(doc);
|
||||
else cutsceneLoadedSetDoc(doc);
|
||||
targetScene->itemCount = (uint8_t)itemCount;
|
||||
targetScene->pause = pause;
|
||||
targetScene->dataSize = 0;
|
||||
|
||||
loading->loading.cutscene.state = ASSET_CUTSCENE_LOADING_STATE_PARSE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetCutsceneLoaderSync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertTrue(loading->type == ASSET_LOADER_TYPE_CUTSCENE, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
if(loading->loading.cutscene.state == ASSET_CUTSCENE_LOADING_STATE_INITIAL) {
|
||||
loading->loading.cutscene.state = ASSET_CUTSCENE_LOADING_STATE_READ_FILE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Everything else already happened in the async phase - see
|
||||
// assetCutsceneLoaderAsync. This flip still has to happen from here
|
||||
// rather than the async phase setting LOADED directly, since
|
||||
// assetUpdate() only fires an entry's onLoaded callback for entries
|
||||
// that finish via this sync path.
|
||||
assertTrue(
|
||||
loading->loading.cutscene.state == ASSET_CUTSCENE_LOADING_STATE_PARSE,
|
||||
"Unexpected cutscene loading state."
|
||||
);
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetCutsceneDispose(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type == ASSET_LOADER_TYPE_CUTSCENE, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
// Nothing to free here - a loaded cutscene's data lives in the fixed,
|
||||
// shared CUTSCENE_LOADED_SCENE/ITEMS/POOL globals (see cutscenesystem.h),
|
||||
// not per-entry storage; this entry only ever drove the read+parse
|
||||
// pipeline into them.
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/assetfile.h"
|
||||
#include "rpg/cutscene/cutscene.h"
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||
#include "rpg/cutscene/item/json/cutscenejsonpool.h"
|
||||
#include "yyjson.h"
|
||||
|
||||
#define ASSET_CUTSCENE_FILE_SIZE_MAX (1024 * 64)
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
// Which fixed, shared buffer set (see cutscenesystem.h) a load should
|
||||
// parse into - MAIN for the one JSON-authored cutscene resident via
|
||||
// cutsceneSystemLoad/cutsceneCutsceneResolve, INSERT for the one resident
|
||||
// via cutsceneInsertResolve (see CUTSCENE_ITEM_TYPE_INSERT).
|
||||
typedef enum {
|
||||
ASSET_CUTSCENE_LOAD_DEST_MAIN,
|
||||
ASSET_CUTSCENE_LOAD_DEST_INSERT
|
||||
} assetcutsceneloaddest_t;
|
||||
|
||||
typedef struct {
|
||||
assetcutsceneloaddest_t dest;
|
||||
|
||||
// Optional file path override, same trick as assetjsonloaderinput_t -
|
||||
// when path[0] is non-zero this is read instead of loading->entry->name,
|
||||
// so a synthetic cache key (e.g. "insert:cutscenes/foo.jsonc", used to
|
||||
// avoid a single entry being shared - and its dest silently ignored -
|
||||
// between two different logical loads of the same file, see
|
||||
// asset.c/assetGetEntry's name-only dedup) can still point at the real
|
||||
// file.
|
||||
char_t path[ASSET_FILE_NAME_MAX];
|
||||
} assetcutsceneloaderinput_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_CUTSCENE_LOADING_STATE_INITIAL,
|
||||
ASSET_CUTSCENE_LOADING_STATE_READ_FILE,
|
||||
ASSET_CUTSCENE_LOADING_STATE_PARSE
|
||||
} assetcutsceneloadingstate_t;
|
||||
|
||||
typedef struct {
|
||||
assetfile_t file;
|
||||
assetcutsceneloadingstate_t state;
|
||||
} assetcutsceneloaderloading_t;
|
||||
|
||||
typedef struct {
|
||||
// Nothing - a loaded cutscene's data lives entirely in the fixed,
|
||||
// shared CUTSCENE_LOADED_SCENE/ITEMS/POOL globals (see cutscenesystem.h)
|
||||
// that assetCutsceneLoaderAsync parses directly into, not per-entry
|
||||
// storage. This entry only ever exists to drive the read+parse
|
||||
// pipeline, and is unlocked (see cutsceneSystemLoad/
|
||||
// cutsceneCutsceneResolve) the instant that finishes.
|
||||
uint8_t unused;
|
||||
} assetcutsceneoutput_t;
|
||||
|
||||
/**
|
||||
* Parses one cutscene item object into item, writing any pool-backed
|
||||
* array data (entityWalkTo positions, mapAreaWait areaIds) into pool at
|
||||
* *poolOffset and advancing it. Every field/enum name is validated as
|
||||
* real (untrusted, authored) file content - failures are real errors, not
|
||||
* asserts. Dispatches to the matching type's Load callback (see
|
||||
* CUTSCENE_ITEM_CALLBACKS in rpg/cutscene/item/cutsceneitem.h) - the
|
||||
* per-type parsing logic itself lives there now, colocated with that
|
||||
* type's runtime behavior.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Base pointer of the entry's pool allocation (may be NULL if
|
||||
* assetCutsceneComputePoolSize returned 0).
|
||||
* @param poolOffset In/out cursor into pool.
|
||||
* @param index Index of this item within the cutscene, for error messages.
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t assetCutsceneParseItem(
|
||||
assetloading_t *loading,
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset,
|
||||
const uint8_t index
|
||||
);
|
||||
|
||||
/**
|
||||
* Asynchronous loader for cutscene assets. Reads the raw JSONC file bytes
|
||||
* and, in the same call, parses them directly into one of the fixed,
|
||||
* shared buffer sets in cutscenesystem.h - CUTSCENE_LOADED_ITEMS/POOL by
|
||||
* default, or CUTSCENE_INSERT_ITEMS/POOL when
|
||||
* loading->entry->input->cutscene.dest is ASSET_CUTSCENE_LOAD_DEST_INSERT -
|
||||
* no heap allocation at all, since only one cutscene is ever resident per
|
||||
* destination. None of that work touches the main thread (every item
|
||||
* type's load callback is pure parsing now; CUTSCENE items defer their
|
||||
* nested asset resolution to cutsceneCutsceneStart), so doing it all here
|
||||
* keeps a large cutscene from ever blocking a frame.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
*/
|
||||
errorret_t assetCutsceneLoaderAsync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Synchronous loader for cutscene assets. All actual work already
|
||||
* happened in the async phase - this only flips the entry to LOADED,
|
||||
* which must happen through this sync callback (rather than the async
|
||||
* phase setting it directly) so assetUpdate()'s onLoaded dispatch, which
|
||||
* only fires for entries finishing via the sync path, still runs.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
*/
|
||||
errorret_t assetCutsceneLoaderSync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Disposer for cutscene assets.
|
||||
*
|
||||
* @param entry Asset entry containing the cutscene data to dispose.
|
||||
* @return Error code indicating success or failure of the dispose operation.
|
||||
*/
|
||||
errorret_t assetCutsceneDispose(assetentry_t *entry);
|
||||
@@ -12,11 +12,10 @@
|
||||
|
||||
// Forward-declared rather than including asset/asset.h - this header only
|
||||
// ever needs a pointer to it, and pulling in the full asset module here
|
||||
// creates a real include cycle for anything that reaches this header via
|
||||
// asset/loader/cutscene/assetcutsceneloader.h (asset.h -> ... ->
|
||||
// assetloader.h -> assetcutsceneloader.h -> cutscene.h -> cutsceneitem.h
|
||||
// -> the audio cutscene items -> this header -> asset/asset.h again,
|
||||
// mid-parse). audiomixerchannel.c itself still includes the full header.
|
||||
// risks an include cycle for anything that reaches this header via a
|
||||
// cutscene item (cutscene.h -> cutsceneitem.h -> the audio cutscene items
|
||||
// -> this header -> asset/asset.h, mid-parse). audiomixerchannel.c itself
|
||||
// still includes the full header.
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef struct audiostream_s audiostream_t;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
cutscene.c
|
||||
cutscenesystem.c
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "cutscene.h"
|
||||
#include "item/json/cutscenejsonpauseflags.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
errorret_t cutsceneParseItem(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
const uint8_t index
|
||||
) {
|
||||
assertNotNull(itemObj, "Item object cannot be NULL");
|
||||
assertNotNull(item, "Item cannot be NULL");
|
||||
|
||||
if(!yyjson_is_obj(itemObj)) {
|
||||
errorThrow("Cutscene item %u is not an object", (uint32_t)index);
|
||||
}
|
||||
|
||||
const char_t *typeName = yyjson_get_str(yyjson_obj_get(itemObj, "type"));
|
||||
if(!cutsceneItemLookupType(typeName, &item->type)) {
|
||||
errorThrow(
|
||||
"Cutscene item %u: missing/unsupported item type %s",
|
||||
(uint32_t)index, typeName
|
||||
);
|
||||
}
|
||||
|
||||
cutsceneitemloadcallback_t *load = CUTSCENE_ITEM_CALLBACKS[item->type].load;
|
||||
if(load == NULL) {
|
||||
errorThrow(
|
||||
"Cutscene item %u: unhandled item type %u",
|
||||
(uint32_t)index, (uint32_t)item->type
|
||||
);
|
||||
}
|
||||
|
||||
errorret_t result = load(itemObj, item);
|
||||
if(errorIsNotOk(result)) errorChain(result);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
size_t cutsceneCountItems(yyjson_doc *doc) {
|
||||
assertNotNull(doc, "Doc cannot be NULL");
|
||||
|
||||
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||
yyjson_val *itemsArr = yyjson_obj_get(root, "items");
|
||||
if(!yyjson_is_arr(itemsArr)) return 0;
|
||||
return yyjson_arr_size(itemsArr);
|
||||
}
|
||||
|
||||
errorret_t cutsceneParseDoc(
|
||||
yyjson_doc *doc,
|
||||
cutscene_t *scene,
|
||||
cutsceneitem_t *items,
|
||||
size_t itemsMax
|
||||
) {
|
||||
assertNotNull(doc, "Doc cannot be NULL");
|
||||
assertNotNull(scene, "Scene cannot be NULL");
|
||||
assertNotNull(items, "Items cannot be NULL");
|
||||
assertTrue(itemsMax > 0, "Items max cannot be 0");
|
||||
|
||||
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||
yyjson_val *itemsArr = yyjson_obj_get(root, "items");
|
||||
if(!yyjson_is_arr(itemsArr)) {
|
||||
errorThrow("Cutscene JSON is missing an 'items' array");
|
||||
}
|
||||
|
||||
size_t itemCount = yyjson_arr_size(itemsArr);
|
||||
if(itemCount > itemsMax) {
|
||||
errorThrow("Cutscene has more items than its target buffer allows");
|
||||
}
|
||||
|
||||
cutscenepause_t pause = CUTSCENE_PAUSE_DEFAULT;
|
||||
yyjson_val *pauseArr = yyjson_obj_get(root, "pause");
|
||||
if(yyjson_is_arr(pauseArr)) {
|
||||
if(!cutsceneJsonParsePauseFlags(pauseArr, &pause)) {
|
||||
errorThrow("Cutscene JSON has an invalid 'pause' flag");
|
||||
}
|
||||
}
|
||||
|
||||
// Everything structural has validated by this point, so it's safe to
|
||||
// start overwriting items - a per-item parse failure past this point
|
||||
// still leaves it partially overwritten, but that's fine: any load
|
||||
// failure here already sends the game into the fatal-error overlay
|
||||
// (see cutsceneSystemLoad/cutsceneCutsceneResolve/cutsceneInsertResolve),
|
||||
// so nothing keeps running against stale data.
|
||||
memoryZero(items, itemCount * sizeof(cutsceneitem_t));
|
||||
|
||||
yyjson_arr_iter itemIter = yyjson_arr_iter_with(itemsArr);
|
||||
yyjson_val *itemVal;
|
||||
uint8_t index = 0;
|
||||
while((itemVal = yyjson_arr_iter_next(&itemIter)) != NULL) {
|
||||
errorret_t itemResult = cutsceneParseItem(itemVal, &items[index], index);
|
||||
if(errorIsNotOk(itemResult)) errorChain(itemResult);
|
||||
index++;
|
||||
}
|
||||
|
||||
scene->itemCount = (uint8_t)itemCount;
|
||||
scene->pause = pause;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -13,10 +13,6 @@ typedef struct cutscene_s {
|
||||
const cutsceneitem_t *items;
|
||||
uint8_t itemCount;
|
||||
cutscenepause_t pause;
|
||||
|
||||
// Size in bytes of this cutscene's custom user data, carved out of
|
||||
// CUTSCENE_SYSTEM.data while the cutscene is running.
|
||||
size_t dataSize;
|
||||
} cutscene_t;
|
||||
|
||||
/**
|
||||
@@ -25,19 +21,16 @@ typedef struct cutscene_s {
|
||||
*
|
||||
* @param NAME Suffix for the generated CUTSCENE_##NAME cutscene/items
|
||||
* statics.
|
||||
* @param SIZE Bytes of custom user data this cutscene needs, carved out
|
||||
* of CUTSCENE_SYSTEM.data while it runs.
|
||||
* @param PAUSE_TYPE Suffix of the CUTSCENE_PAUSE_* flag(s) to apply
|
||||
* while this cutscene runs (e.g. NONE, ALL).
|
||||
* @param ... The cutscene's cutsceneitem_t entries, in order.
|
||||
*/
|
||||
#define CUTSCENE(NAME, SIZE, PAUSE_TYPE, ...) \
|
||||
#define CUTSCENE(NAME, PAUSE_TYPE, ...) \
|
||||
static const cutsceneitem_t CUTSCENE_##NAME##_ITEMS[] = { __VA_ARGS__ }; \
|
||||
static const cutscene_t CUTSCENE_##NAME = { \
|
||||
.items = CUTSCENE_##NAME##_ITEMS, \
|
||||
.itemCount = sizeof(CUTSCENE_##NAME##_ITEMS) / sizeof(cutsceneitem_t), \
|
||||
.pause = CUTSCENE_PAUSE_##PAUSE_TYPE, \
|
||||
.dataSize = SIZE \
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -79,3 +72,55 @@ typedef struct cutscene_s {
|
||||
.type = CUTSCENE_ITEM_TYPE_INSERT, \
|
||||
.insert = { .cutscene = CUTSCENE_REFERENCE(CUTSCENE), .name = NULL } \
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses one cutscene item object into item. Every field/enum name is
|
||||
* validated as real (untrusted, authored) file content - failures are
|
||||
* real errors, not asserts. Dispatches to the matching type's Load
|
||||
* callback (see CUTSCENE_ITEM_CALLBACKS in item/cutsceneitem.h) - the
|
||||
* per-type parsing logic itself lives there, colocated with that type's
|
||||
* runtime behavior.
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param index Index of this item within the cutscene, for error messages.
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneParseItem(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
const uint8_t index
|
||||
);
|
||||
|
||||
/**
|
||||
* Counts how many entries are in doc's top-level "items" array, without
|
||||
* parsing any of them - e.g. so a caller that wants to malloc its own
|
||||
* items array instead of using a fixed one could size it first (we don't
|
||||
* do that ourselves, but the option stays open).
|
||||
*
|
||||
* @param doc Already-parsed JSON document.
|
||||
* @return Number of entries in "items", or 0 if it's missing/not an array.
|
||||
*/
|
||||
size_t cutsceneCountItems(yyjson_doc *doc);
|
||||
|
||||
/**
|
||||
* Parses doc's "items" (and optional "pause") into items, and updates
|
||||
* scene's itemCount/pause - scene->items is left untouched, since the
|
||||
* caller already points it at items. Purely a parser: doesn't touch the
|
||||
* asset system, doesn't lock/load anything, and doesn't take ownership of
|
||||
* doc - the caller is responsible for both loading doc beforehand and for
|
||||
* its lifetime afterward (name/string fields written into items point
|
||||
* directly into it, so it must outlive items' use).
|
||||
*
|
||||
* @param doc Already-parsed JSON document to read from.
|
||||
* @param scene Destination scene - itemCount/pause are overwritten.
|
||||
* @param items Destination item array, capacity itemsMax.
|
||||
* @param itemsMax Capacity of items.
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneParseDoc(
|
||||
yyjson_doc *doc,
|
||||
cutscene_t *scene,
|
||||
cutsceneitem_t *items,
|
||||
size_t itemsMax
|
||||
);
|
||||
|
||||
@@ -11,17 +11,16 @@
|
||||
#include "util/string.h"
|
||||
#include "assert/assert.h"
|
||||
#include "asset/asset.h"
|
||||
#include "asset/loader/assetloader.h"
|
||||
#include "ui/overlay/uifatalerror.h"
|
||||
|
||||
cutscenesystem_t CUTSCENE_SYSTEM;
|
||||
|
||||
cutsceneitem_t CUTSCENE_LOADED_ITEMS[CUTSCENE_LOADED_ITEMS_MAX];
|
||||
uint8_t CUTSCENE_LOADED_POOL[CUTSCENE_LOADED_POOL_SIZE_MAX];
|
||||
cutscene_t CUTSCENE_LOADED_SCENE = {
|
||||
.items = CUTSCENE_LOADED_ITEMS,
|
||||
.itemCount = 0,
|
||||
.pause = CUTSCENE_PAUSE_NONE,
|
||||
.dataSize = 0
|
||||
.pause = CUTSCENE_PAUSE_NONE
|
||||
};
|
||||
static yyjson_doc *CUTSCENE_LOADED_DOC = NULL;
|
||||
|
||||
@@ -31,12 +30,10 @@ void cutsceneLoadedSetDoc(yyjson_doc *doc) {
|
||||
}
|
||||
|
||||
cutsceneitem_t CUTSCENE_INSERT_ITEMS[CUTSCENE_INSERT_ITEMS_MAX];
|
||||
uint8_t CUTSCENE_INSERT_POOL[CUTSCENE_INSERT_POOL_SIZE_MAX];
|
||||
cutscene_t CUTSCENE_INSERT_SCENE = {
|
||||
.items = CUTSCENE_INSERT_ITEMS,
|
||||
.itemCount = 0,
|
||||
.pause = CUTSCENE_PAUSE_NONE,
|
||||
.dataSize = 0
|
||||
.pause = CUTSCENE_PAUSE_NONE
|
||||
};
|
||||
static yyjson_doc *CUTSCENE_INSERT_DOC = NULL;
|
||||
|
||||
@@ -54,11 +51,6 @@ void cutsceneSystemPrepare(
|
||||
entity_t *interact,
|
||||
entity_t *interacted
|
||||
) {
|
||||
assertTrue(
|
||||
cutscene->dataSize < CUTSCENE_SYSTEM_SIZE_MAX,
|
||||
"Cutscene data size exceeds CUTSCENE_SYSTEM_SIZE_MAX"
|
||||
);
|
||||
|
||||
CUTSCENE_SYSTEM.scene = cutscene;
|
||||
CUTSCENE_SYSTEM.pause = cutscene->pause;
|
||||
CUTSCENE_SYSTEM.entityInteract = interact;
|
||||
@@ -98,19 +90,42 @@ void cutsceneSystemLoad(const char_t *file) {
|
||||
char_t path[ASSET_FILE_NAME_MAX];
|
||||
stringFormat(path, sizeof(path), "cutscenes/%s", file);
|
||||
|
||||
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_CUTSCENE, NULL);
|
||||
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_JSON, NULL);
|
||||
errorret_t result = assetRequireLoaded(entry);
|
||||
// Released the instant loading finishes - the loader already parsed
|
||||
// everything we need into CUTSCENE_LOADED_SCENE/ITEMS/POOL, so nothing
|
||||
// further depends on this entry (see cutscenesystem.h's doc comment on
|
||||
// CUTSCENE_LOADED_ITEMS_MAX for why only one cutscene stays resident).
|
||||
assetUnlockEntry(entry);
|
||||
if(errorIsNotOk(result)) {
|
||||
assetUnlockEntry(entry);
|
||||
errorCatch(errorPrint(result));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Steal the parsed doc before unlocking - assetJsonDispose would
|
||||
// otherwise free it out from under CUTSCENE_LOADED_ITEMS' name pointers
|
||||
// the moment this entry gets reaped.
|
||||
yyjson_doc *doc = entry->data.json;
|
||||
entry->data.json = NULL;
|
||||
assetUnlockEntry(entry);
|
||||
// Force this now-zero-ref entry to actually go away right away, rather
|
||||
// than leaving it languishing as LOADED until something else happens to
|
||||
// trigger a reap - assetRequireLoaded treats an already-LOADED entry as
|
||||
// an instant cache hit with no reparsing, so a stale leftover entry for
|
||||
// this same path would otherwise make a later load of it wrongly skip
|
||||
// reloading, still returning CUTSCENE_LOADED_SCENE even after some
|
||||
// other cutscene has since overwritten it.
|
||||
assetReapUnused();
|
||||
|
||||
errorret_t parseResult = cutsceneParseDoc(
|
||||
doc, &CUTSCENE_LOADED_SCENE, CUTSCENE_LOADED_ITEMS,
|
||||
CUTSCENE_LOADED_ITEMS_MAX
|
||||
);
|
||||
if(errorIsNotOk(parseResult)) {
|
||||
yyjson_doc_free(doc);
|
||||
errorCatch(errorPrint(parseResult));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
cutsceneLoadedSetDoc(doc);
|
||||
cutsceneSystemStartCutscene(&CUTSCENE_LOADED_SCENE);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ typedef struct entity_s entity_t;
|
||||
#define CUTSCENE_AREA_LAST_CREATED ((uint8_t)0xFF)
|
||||
#define CUTSCENE_TEXT_MINI_LAST_CREATED ((uint8_t)0xFA)
|
||||
|
||||
// Maximum number of bytes a running cutscene may request via
|
||||
// cutscene_t.dataSize.
|
||||
// Size in bytes of cutscenesystem_t.userData, the running cutscene's
|
||||
// scratch space.
|
||||
#define CUTSCENE_SYSTEM_SIZE_MAX 8192
|
||||
|
||||
// Size of CUTSCENE_SYSTEM.textCache - keep >= UI_KEYBOARD_TEXT_MAX (see
|
||||
@@ -30,61 +30,59 @@ typedef struct entity_s entity_t;
|
||||
// CUTSCENE item hands control off completely (see cutsceneCutsceneStart)
|
||||
// and cutsceneSystemLoad/cutsceneCutsceneResolve release their asset lock
|
||||
// the instant loading finishes - so there's no benefit to malloc'ing a
|
||||
// fresh items/pool buffer per load. Instead the asset loader
|
||||
// (assetCutsceneLoaderAsync) parses directly into these fixed, static
|
||||
// buffers. Bump these if a real cutscene ever needs more; C-authored
|
||||
// cutscenes (the CUTSCENE(...) macro) don't use these at all - they're
|
||||
// compiled directly into their own static const arrays.
|
||||
// fresh items buffer per load. Instead those functions parse directly
|
||||
// into this fixed, static buffer via cutsceneParseDoc (see cutscene.h),
|
||||
// once the raw JSON itself has been read+parsed via the generic
|
||||
// ASSET_LOADER_TYPE_JSON asset loader. Bump this if a real cutscene ever
|
||||
// needs more; C-authored cutscenes (the CUTSCENE(...) macro) don't use
|
||||
// this at all - they're compiled directly into their own static const
|
||||
// arrays.
|
||||
#define CUTSCENE_LOADED_ITEMS_MAX 128
|
||||
#define CUTSCENE_LOADED_POOL_SIZE_MAX 4096
|
||||
|
||||
extern cutsceneitem_t CUTSCENE_LOADED_ITEMS[CUTSCENE_LOADED_ITEMS_MAX];
|
||||
extern uint8_t CUTSCENE_LOADED_POOL[CUTSCENE_LOADED_POOL_SIZE_MAX];
|
||||
|
||||
// The cutscene_t for whatever was last parsed into CUTSCENE_LOADED_ITEMS/
|
||||
// CUTSCENE_LOADED_POOL - .items always points at CUTSCENE_LOADED_ITEMS;
|
||||
// itemCount/pause/dataSize are overwritten by the loader on each load.
|
||||
// The cutscene_t for whatever was last parsed into CUTSCENE_LOADED_ITEMS -
|
||||
// .items always points at CUTSCENE_LOADED_ITEMS; itemCount/pause are
|
||||
// overwritten by the loader on each load.
|
||||
extern cutscene_t CUTSCENE_LOADED_SCENE;
|
||||
|
||||
/**
|
||||
* Frees whatever yyjson_doc currently backs CUTSCENE_LOADED_ITEMS' string
|
||||
* fields (marker/cutscene-reference names point directly into it - see
|
||||
* cutsceneCutsceneRefLoad/cutsceneMarkerLoad) and takes ownership of doc
|
||||
* instead. Called by the cutscene asset loader once a fresh parse into
|
||||
* CUTSCENE_LOADED_ITEMS/CUTSCENE_LOADED_POOL completes successfully, and
|
||||
* from cutsceneSystemDispose to release the last one at shutdown.
|
||||
* instead. Called by cutsceneSystemLoad/cutsceneCutsceneResolve once a
|
||||
* fresh parse into CUTSCENE_LOADED_ITEMS completes successfully, and from
|
||||
* cutsceneSystemDispose to release the last one at shutdown.
|
||||
*
|
||||
* @param doc The new doc to take ownership of (NULL just frees/clears the
|
||||
* current one).
|
||||
*/
|
||||
void cutsceneLoadedSetDoc(yyjson_doc *doc);
|
||||
|
||||
// A second, much smaller buffer set dedicated to CUTSCENE_ITEM_TYPE_INSERT
|
||||
// A second, much smaller buffer dedicated to CUTSCENE_ITEM_TYPE_INSERT
|
||||
// (see cutsceneSystemInsertCutscene) - kept entirely separate from
|
||||
// CUTSCENE_LOADED_ITEMS/POOL so that a JSON-authored INSERT item resolving
|
||||
// its target by name (via cutsceneInsertResolve) never overwrites the
|
||||
// buffer the currently-running outer cutscene's own items live in. Sized
|
||||
// small deliberately - an inserted snippet is meant to be a short splice,
|
||||
// not a full cutscene.
|
||||
// CUTSCENE_LOADED_ITEMS so that a JSON-authored INSERT item resolving its
|
||||
// target by name (via cutsceneInsertResolve) never overwrites the buffer
|
||||
// the currently-running outer cutscene's own items live in. Sized small
|
||||
// deliberately - an inserted snippet is meant to be a short splice, not a
|
||||
// full cutscene.
|
||||
#define CUTSCENE_INSERT_ITEMS_MAX 16
|
||||
#define CUTSCENE_INSERT_POOL_SIZE_MAX 512
|
||||
|
||||
extern cutsceneitem_t CUTSCENE_INSERT_ITEMS[CUTSCENE_INSERT_ITEMS_MAX];
|
||||
extern uint8_t CUTSCENE_INSERT_POOL[CUTSCENE_INSERT_POOL_SIZE_MAX];
|
||||
|
||||
// The cutscene_t for whatever was last parsed into CUTSCENE_INSERT_ITEMS/
|
||||
// CUTSCENE_INSERT_POOL - .items always points at CUTSCENE_INSERT_ITEMS.
|
||||
// Only one JSON-authored INSERT target is ever resident at a time, same
|
||||
// restriction as CUTSCENE_LOADED_SCENE - a JSON-authored INSERT whose
|
||||
// target itself contains a JSON-authored INSERT would overwrite this out
|
||||
// from under itself, so that isn't supported (compile-time CUTSCENE_INSERT
|
||||
// nesting is unaffected, since it never touches asset loading).
|
||||
// The cutscene_t for whatever was last parsed into CUTSCENE_INSERT_ITEMS -
|
||||
// .items always points at CUTSCENE_INSERT_ITEMS. Only one JSON-authored
|
||||
// INSERT target is ever resident at a time, same restriction as
|
||||
// CUTSCENE_LOADED_SCENE - a JSON-authored INSERT whose target itself
|
||||
// contains a JSON-authored INSERT would overwrite this out from under
|
||||
// itself, so that isn't supported (compile-time CUTSCENE_INSERT nesting is
|
||||
// unaffected, since it never touches asset loading).
|
||||
extern cutscene_t CUTSCENE_INSERT_SCENE;
|
||||
|
||||
/**
|
||||
* Same as cutsceneLoadedSetDoc, but for whatever yyjson_doc currently
|
||||
* backs CUTSCENE_INSERT_ITEMS' string fields. Called by the cutscene asset
|
||||
* loader once a fresh parse into CUTSCENE_INSERT_ITEMS/CUTSCENE_INSERT_POOL
|
||||
* backs CUTSCENE_INSERT_ITEMS' string fields. Called by
|
||||
* cutsceneInsertResolve once a fresh parse into CUTSCENE_INSERT_ITEMS
|
||||
* completes successfully, and from cutsceneSystemDispose to release the
|
||||
* last one at shutdown.
|
||||
*
|
||||
@@ -125,8 +123,7 @@ typedef struct {
|
||||
// Data (used by the current item).
|
||||
cutsceneitemdata_t data;
|
||||
|
||||
// Custom user data for the running cutscene, sized per-scene by
|
||||
// cutscene_t.dataSize.
|
||||
// Custom user data for the running cutscene.
|
||||
uint8_t userData[CUTSCENE_SYSTEM_SIZE_MAX];
|
||||
|
||||
// See cutsceneSystemSetOnComplete.
|
||||
@@ -192,7 +189,7 @@ void cutsceneSystemStartCutsceneAndGoToMarker(
|
||||
* "last created" state, the text cache or userData - those all keep
|
||||
* whatever the outer cutscene set, since this is meant to feel like
|
||||
* pasting cutscene's items in place rather than starting an independent
|
||||
* cutscene. Because of this, cutscene's own .pause/.dataSize are ignored.
|
||||
* cutscene. Because of this, cutscene's own .pause is ignored.
|
||||
* Asserts if no cutscene is currently running, or if inserts are already
|
||||
* nested CUTSCENE_INSERT_STACK_MAX deep.
|
||||
*
|
||||
@@ -206,15 +203,15 @@ void cutsceneSystemStartCutsceneAndGoToMarker(
|
||||
void cutsceneSystemInsertCutscene(const cutscene_t *cutscene);
|
||||
|
||||
/**
|
||||
* Locks, loads and immediately starts a cutscene asset by file name,
|
||||
* e.g. cutsceneSystemLoad("main_menu.jsonc") loads and starts
|
||||
* assets/cutscenes/main_menu.jsonc - same lock/assetRequireLoaded/start
|
||||
* sequence as sceneInitialInit's INITIAL_CUTSCENE_ENTRY. The entry is
|
||||
* locked forever (never unlocked), same lifetime convention as
|
||||
* LOCALE.entry - it's fine to call this again later for the same file,
|
||||
* it'll just resolve to the same already-loaded entry. Opens the fatal
|
||||
* error overlay (see uiFatalErrorOpen) instead of starting anything if
|
||||
* the asset fails to load.
|
||||
* Loads and immediately starts a cutscene asset by file name, e.g.
|
||||
* cutsceneSystemLoad("main_menu.jsonc") loads and starts
|
||||
* assets/cutscenes/main_menu.jsonc, parsing it into
|
||||
* CUTSCENE_LOADED_SCENE/ITEMS - the underlying JSON asset entry is
|
||||
* unlocked and reaped immediately once that's done (see
|
||||
* CUTSCENE_LOADED_ITEMS_MAX's doc comment for why), so it's always
|
||||
* re-read+re-parsed fresh, never assumed still resident from a previous
|
||||
* call. Opens the fatal error overlay (see uiFatalErrorOpen) instead of
|
||||
* starting anything if the asset fails to load.
|
||||
*
|
||||
* @param file Cutscene file name (with .jsonc extension), relative to
|
||||
* assets/cutscenes/.
|
||||
|
||||
@@ -33,9 +33,7 @@ bool_t cutsceneAudioFadeUpdate(
|
||||
|
||||
errorret_t cutsceneAudioFadeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
audiomixerchannel_t channelValue;
|
||||
if(!cutsceneJsonLookupAudioChannel(
|
||||
|
||||
@@ -92,13 +92,9 @@ bool_t cutsceneAudioFadeUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_FADE needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_FADE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioFadeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -18,9 +18,7 @@ bool_t cutsceneAudioFadeWaitUpdate(
|
||||
|
||||
errorret_t cutsceneAudioFadeWaitLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
return cutsceneJsonLoadAudioChannelOnly(itemObj, &item->audioFadeWaitChannel);
|
||||
}
|
||||
|
||||
@@ -37,13 +37,9 @@ bool_t cutsceneAudioFadeWaitUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_FADE_WAIT needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_FADE_WAIT needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioFadeWaitLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneAudioPauseUpdate(
|
||||
|
||||
errorret_t cutsceneAudioPauseLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
return cutsceneJsonLoadAudioChannelOnly(itemObj, &item->audioPauseChannel);
|
||||
}
|
||||
|
||||
@@ -45,13 +45,9 @@ bool_t cutsceneAudioPauseUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_PAUSE needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_PAUSE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioPauseLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -34,9 +34,7 @@ bool_t cutsceneAudioPlayUpdate(
|
||||
|
||||
errorret_t cutsceneAudioPlayLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonCopyString(
|
||||
itemObj, "file", NULL, item->audioPlay.file, AUDIO_PATH_MAX
|
||||
|
||||
@@ -99,13 +99,9 @@ bool_t cutsceneAudioPlayUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_PLAY needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_PLAY needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioPlayLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneAudioResumeUpdate(
|
||||
|
||||
errorret_t cutsceneAudioResumeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
return cutsceneJsonLoadAudioChannelOnly(itemObj, &item->audioResumeChannel);
|
||||
}
|
||||
|
||||
@@ -45,13 +45,9 @@ bool_t cutsceneAudioResumeUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_RESUME needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_RESUME needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioResumeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -33,9 +33,7 @@ bool_t cutsceneAudioSetUpdate(
|
||||
|
||||
errorret_t cutsceneAudioSetLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
audiomixerchannel_t channelValue;
|
||||
if(!cutsceneJsonLookupAudioChannel(
|
||||
|
||||
@@ -73,13 +73,9 @@ bool_t cutsceneAudioSetUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_SET needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_SET needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioSetLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -32,9 +32,7 @@ bool_t cutsceneAudioSetLoopUpdate(
|
||||
|
||||
errorret_t cutsceneAudioSetLoopLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
audiomixerchannel_t channelValue;
|
||||
if(!cutsceneJsonLookupAudioChannel(
|
||||
|
||||
@@ -67,13 +67,9 @@ bool_t cutsceneAudioSetLoopUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_SET_LOOP needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_SET_LOOP needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioSetLoopLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -26,9 +26,7 @@ bool_t cutsceneAudioSetPanUpdate(
|
||||
|
||||
errorret_t cutsceneAudioSetPanLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
audiomixerchannel_t channelValue;
|
||||
if(!cutsceneJsonLookupAudioChannel(
|
||||
|
||||
@@ -54,13 +54,9 @@ bool_t cutsceneAudioSetPanUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_SET_PAN needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_SET_PAN needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioSetPanLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneAudioStopUpdate(
|
||||
|
||||
errorret_t cutsceneAudioStopLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
return cutsceneJsonLoadAudioChannelOnly(itemObj, &item->audioStopChannel);
|
||||
}
|
||||
|
||||
@@ -45,13 +45,9 @@ bool_t cutsceneAudioStopUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (AUDIO_STOP needs no pool storage).
|
||||
* @param poolOffset Unused (AUDIO_STOP needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneAudioStopLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -26,9 +26,7 @@ bool_t cutsceneBattleForceActionUpdate(
|
||||
|
||||
errorret_t cutsceneBattleForceActionLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
item->battleForceAction.fighterIndex =
|
||||
(uint8_t)yyjson_get_int(yyjson_obj_get(itemObj, "fighterIndex"));
|
||||
|
||||
@@ -56,13 +56,9 @@ bool_t cutsceneBattleForceActionUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (BATTLE_FORCE_ACTION needs no pool storage).
|
||||
* @param poolOffset Unused (BATTLE_FORCE_ACTION needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneBattleForceActionLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -17,9 +17,7 @@ bool_t cutsceneBattleWaitStateUpdate(
|
||||
|
||||
errorret_t cutsceneBattleWaitStateLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
battlestate_t stateValue;
|
||||
if(!cutsceneJsonLookupBattleState(
|
||||
|
||||
@@ -47,13 +47,9 @@ bool_t cutsceneBattleWaitStateUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (BATTLE_WAIT_STATE needs no pool storage).
|
||||
* @param poolOffset Unused (BATTLE_WAIT_STATE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneBattleWaitStateLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -84,9 +84,7 @@ bool_t cutsceneStartBattleUpdate(
|
||||
|
||||
errorret_t cutsceneStartBattleLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
battleencountertype_t encounterValue;
|
||||
if(!cutsceneJsonLookupBattleEncounter(
|
||||
|
||||
@@ -64,13 +64,9 @@ bool_t cutsceneStartBattleUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (START_BATTLE needs no pool storage).
|
||||
* @param poolOffset Unused (START_BATTLE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneStartBattleLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -22,9 +22,7 @@ bool_t cutsceneMarkerUpdate(
|
||||
|
||||
errorret_t cutsceneMarkerLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
const char_t *name = yyjson_get_str(yyjson_obj_get(itemObj, "name"));
|
||||
if(name == NULL) {
|
||||
|
||||
@@ -52,13 +52,9 @@ bool_t cutsceneMarkerUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (MARKER needs no pool storage).
|
||||
* @param poolOffset Unused (MARKER needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneMarkerLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneSceneUpdate(
|
||||
|
||||
errorret_t cutsceneSceneLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
scenetype_t sceneValue;
|
||||
if(!cutsceneJsonLookupSceneType(
|
||||
|
||||
@@ -56,13 +56,9 @@ bool_t cutsceneSceneUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (SCENE needs no pool storage).
|
||||
* @param poolOffset Unused (SCENE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneSceneLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneSetPauseUpdate(
|
||||
|
||||
errorret_t cutsceneSetPauseLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
yyjson_val *flags = yyjson_obj_get(itemObj, "flags");
|
||||
cutscenepause_t value;
|
||||
|
||||
@@ -45,13 +45,9 @@ bool_t cutsceneSetPauseUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (SET_PAUSE needs no pool storage).
|
||||
* @param poolOffset Unused (SET_PAUSE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneSetPauseLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -26,9 +26,7 @@ bool_t cutsceneWaitUpdate(
|
||||
|
||||
errorret_t cutsceneWaitLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
item->wait = cutsceneJsonGetFloat(itemObj, "seconds", 0.0f);
|
||||
|
||||
|
||||
@@ -47,13 +47,9 @@ bool_t cutsceneWaitUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (WAIT needs no pool storage).
|
||||
* @param poolOffset Unused (WAIT needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneWaitLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
@@ -68,8 +68,7 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
||||
.init = cutsceneEntityWalkToStart,
|
||||
.update = cutsceneEntityWalkToUpdate,
|
||||
.jsonName = "ENTITY_WALK_TO",
|
||||
.load = cutsceneEntityWalkToLoad,
|
||||
.poolSize = cutsceneEntityWalkToPoolSize
|
||||
.load = cutsceneEntityWalkToLoad
|
||||
},
|
||||
|
||||
[CUTSCENE_ITEM_TYPE_FADE] = {
|
||||
@@ -142,8 +141,7 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
||||
.init = cutsceneMapAreaWaitStart,
|
||||
.update = cutsceneMapAreaWaitUpdate,
|
||||
.jsonName = "MAP_AREA_WAIT",
|
||||
.load = cutsceneMapAreaWaitLoad,
|
||||
.poolSize = cutsceneMapAreaWaitPoolSize
|
||||
.load = cutsceneMapAreaWaitLoad
|
||||
},
|
||||
|
||||
[CUTSCENE_ITEM_TYPE_START_BATTLE] = {
|
||||
@@ -403,25 +401,48 @@ const cutscene_t * cutsceneCutsceneResolve(const char_t *name) {
|
||||
char_t path[ASSET_FILE_NAME_MAX];
|
||||
stringFormat(path, sizeof(path), "cutscenes/%s.jsonc", name);
|
||||
|
||||
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_CUTSCENE, NULL);
|
||||
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_JSON, NULL);
|
||||
errorret_t result = assetRequireLoaded(entry);
|
||||
// Released the instant loading finishes - see cutsceneSystemLoad's doc
|
||||
// comment for why nothing further depends on this entry once loaded.
|
||||
assetUnlockEntry(entry);
|
||||
if(errorIsNotOk(result)) {
|
||||
assetUnlockEntry(entry);
|
||||
errorCatch(errorPrint(result));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Steal the parsed doc before unlocking - assetJsonDispose would
|
||||
// otherwise free it out from under CUTSCENE_LOADED_ITEMS' name pointers
|
||||
// the moment this entry gets reaped.
|
||||
yyjson_doc *doc = entry->data.json;
|
||||
entry->data.json = NULL;
|
||||
assetUnlockEntry(entry);
|
||||
// Force this now-zero-ref entry to actually go away right away, rather
|
||||
// than leaving it languishing as LOADED until something else happens to
|
||||
// trigger a reap - assetRequireLoaded treats an already-LOADED entry as
|
||||
// an instant cache hit with no reparsing, so a stale leftover entry for
|
||||
// this same path would otherwise make a later resolve of it wrongly
|
||||
// skip loading, still returning CUTSCENE_LOADED_SCENE even after some
|
||||
// other cutscene has since overwritten it.
|
||||
assetReapUnused();
|
||||
|
||||
errorret_t parseResult = cutsceneParseDoc(
|
||||
doc, &CUTSCENE_LOADED_SCENE, CUTSCENE_LOADED_ITEMS,
|
||||
CUTSCENE_LOADED_ITEMS_MAX
|
||||
);
|
||||
if(errorIsNotOk(parseResult)) {
|
||||
yyjson_doc_free(doc);
|
||||
errorCatch(errorPrint(parseResult));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cutsceneLoadedSetDoc(doc);
|
||||
return &CUTSCENE_LOADED_SCENE;
|
||||
}
|
||||
|
||||
errorret_t cutsceneCutsceneLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
return cutsceneCutsceneRefLoad(itemObj, &item->cutsceneRef, "CUTSCENE");
|
||||
}
|
||||
@@ -477,33 +498,44 @@ const cutscene_t * cutsceneInsertResolve(const char_t *name) {
|
||||
char_t path[ASSET_FILE_NAME_MAX];
|
||||
stringFormat(path, sizeof(path), "cutscenes/%s.jsonc", name);
|
||||
|
||||
char_t key[ASSET_FILE_NAME_MAX];
|
||||
stringFormat(key, sizeof(key), "insert:%s", path);
|
||||
|
||||
assetloaderinput_t input;
|
||||
memoryZero(&input, sizeof(input));
|
||||
input.cutscene.dest = ASSET_CUTSCENE_LOAD_DEST_INSERT;
|
||||
stringCopy(input.cutscene.path, path, ASSET_FILE_NAME_MAX);
|
||||
|
||||
assetentry_t *entry = assetLock(key, ASSET_LOADER_TYPE_CUTSCENE, &input);
|
||||
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_JSON, NULL);
|
||||
errorret_t result = assetRequireLoaded(entry);
|
||||
// Released the instant loading finishes - see cutsceneSystemLoad's doc
|
||||
// comment for why nothing further depends on this entry once loaded.
|
||||
assetUnlockEntry(entry);
|
||||
if(errorIsNotOk(result)) {
|
||||
assetUnlockEntry(entry);
|
||||
errorCatch(errorPrint(result));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Steal the parsed doc before unlocking - assetJsonDispose would
|
||||
// otherwise free it out from under CUTSCENE_INSERT_ITEMS' name pointers
|
||||
// the moment this entry gets reaped.
|
||||
yyjson_doc *doc = entry->data.json;
|
||||
entry->data.json = NULL;
|
||||
assetUnlockEntry(entry);
|
||||
// See cutsceneCutsceneResolve's matching comment - forces a stale
|
||||
// leftover entry for this same path out immediately, so a later resolve
|
||||
// never wrongly skips reloading it.
|
||||
assetReapUnused();
|
||||
|
||||
errorret_t parseResult = cutsceneParseDoc(
|
||||
doc, &CUTSCENE_INSERT_SCENE, CUTSCENE_INSERT_ITEMS,
|
||||
CUTSCENE_INSERT_ITEMS_MAX
|
||||
);
|
||||
if(errorIsNotOk(parseResult)) {
|
||||
yyjson_doc_free(doc);
|
||||
errorCatch(errorPrint(parseResult));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cutsceneInsertSetDoc(doc);
|
||||
return &CUTSCENE_INSERT_SCENE;
|
||||
}
|
||||
|
||||
errorret_t cutsceneInsertLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
return cutsceneCutsceneRefLoad(itemObj, &item->insert, "INSERT");
|
||||
}
|
||||
|
||||
@@ -150,22 +150,14 @@ typedef bool_t (cutsceneitemupdatecallback_t)(
|
||||
|
||||
typedef errorret_t (cutsceneitemloadcallback_t)(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
// Returns how many pool bytes (see cutsceneJsonPoolAlign) this JSON item
|
||||
// needs - only ENTITY_WALK_TO/MAP_AREA_WAIT need this; every other type's
|
||||
// poolSize is NULL (0 bytes).
|
||||
typedef size_t (cutsceneitempoolsizecallback_t)(yyjson_val *itemObj);
|
||||
|
||||
typedef struct {
|
||||
cutsceneiteminitcallback_t *init;
|
||||
cutsceneitemupdatecallback_t *update;
|
||||
const char_t *jsonName; // NULL if not JSON-authorable
|
||||
cutsceneitemloadcallback_t *load; // NULL if not JSON-authorable
|
||||
cutsceneitempoolsizecallback_t *poolSize; // NULL if this type needs no pool
|
||||
} cutsceneitemcallbacks_t;
|
||||
|
||||
extern cutsceneitemcallbacks_t
|
||||
@@ -264,15 +256,11 @@ bool_t cutsceneCutsceneUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (CUTSCENE needs no pool storage).
|
||||
* @param poolOffset Unused (CUTSCENE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneCutsceneLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -301,8 +289,8 @@ errorret_t cutsceneCutsceneRefLoad(
|
||||
* been resolved yet (a JSON-authored reference, only known by name so
|
||||
* far), resolves it first via cutsceneInsertResolve - not
|
||||
* cutsceneCutsceneResolve, so the referenced cutscene lands in the
|
||||
* dedicated CUTSCENE_INSERT_ITEMS/POOL buffer rather than overwriting
|
||||
* CUTSCENE_LOADED_ITEMS/POOL out from under whatever outer cutscene is
|
||||
* dedicated CUTSCENE_INSERT_ITEMS buffer rather than overwriting
|
||||
* CUTSCENE_LOADED_ITEMS out from under whatever outer cutscene is
|
||||
* currently running (see cutscenesystem.h's CUTSCENE_INSERT_ITEMS_MAX doc
|
||||
* comment). Not cached back into item, same reasoning as
|
||||
* cutsceneCutsceneStart.
|
||||
@@ -316,15 +304,14 @@ void cutsceneInsertStart(
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolves a JSON-authored INSERT item's referenced cutscene by name,
|
||||
* locking (under a synthetic "insert:cutscenes/<name>.jsonc" cache key,
|
||||
* distinct from the plain "cutscenes/<name>.jsonc" key cutsceneCutsceneResolve
|
||||
* uses for the same file, so a load driven by an INSERT item is never
|
||||
* silently shared with - or overwritten by - a load driven by a CUTSCENE
|
||||
* item, or vice versa - see assetGetEntry's name-only dedup) and blocking
|
||||
* on assets/cutscenes/<name>.jsonc, parsed into CUTSCENE_INSERT_ITEMS/POOL
|
||||
* rather than CUTSCENE_LOADED_ITEMS/POOL. On failure, opens the fatal
|
||||
* error overlay and returns NULL.
|
||||
* Resolves a JSON-authored INSERT item's referenced cutscene by name -
|
||||
* locks+loads assets/cutscenes/<name>.jsonc (as a plain
|
||||
* ASSET_LOADER_TYPE_JSON asset, same path/key convention as
|
||||
* cutsceneCutsceneResolve - which destination buffer it lands in is
|
||||
* decided purely by which resolve function is called, not stored on the
|
||||
* asset entry itself), then parses it into CUTSCENE_INSERT_ITEMS rather
|
||||
* than CUTSCENE_LOADED_ITEMS. On failure, opens the fatal error overlay
|
||||
* and returns NULL.
|
||||
*
|
||||
* @param name Bare cutscene name (without "cutscenes/" prefix or
|
||||
* ".jsonc" suffix).
|
||||
@@ -350,13 +337,9 @@ bool_t cutsceneInsertUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (INSERT needs no pool storage).
|
||||
* @param poolOffset Unused (INSERT needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneInsertLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
|
||||
errorret_t cutsceneItemLoadNoop(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -90,13 +90,9 @@ typedef enum {
|
||||
*
|
||||
* @param itemObj The item's JSON object (unused).
|
||||
* @param item Destination item (unused).
|
||||
* @param pool Unused.
|
||||
* @param poolOffset Unused.
|
||||
* @return Always errorOk().
|
||||
*/
|
||||
errorret_t cutsceneItemLoadNoop(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutscenePrintUpdate(
|
||||
|
||||
errorret_t cutscenePrintLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonCopyString(
|
||||
itemObj, "text", NULL, item->print.text, CUTSCENE_PRINT_MAX_CHARS
|
||||
|
||||
@@ -50,13 +50,9 @@ bool_t cutscenePrintUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (PRINT needs no pool storage).
|
||||
* @param poolOffset Unused (PRINT needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutscenePrintLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -38,9 +38,7 @@ bool_t cutsceneEntityAddUpdate(
|
||||
|
||||
errorret_t cutsceneEntityAddLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
entitytype_t entityTypeValue;
|
||||
if(!cutsceneJsonLookupEntityType(
|
||||
|
||||
@@ -56,13 +56,9 @@ bool_t cutsceneEntityAddUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (ENTITY_ADD needs no pool storage).
|
||||
* @param poolOffset Unused (ENTITY_ADD needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneEntityAddLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -29,9 +29,7 @@ bool_t cutsceneEntityRemoveUpdate(
|
||||
|
||||
errorret_t cutsceneEntityRemoveLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonGetEntityIndex(
|
||||
yyjson_obj_get(itemObj, "entityIndex"), &item->entityRemove.entityIndex
|
||||
|
||||
@@ -50,13 +50,9 @@ bool_t cutsceneEntityRemoveUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (ENTITY_REMOVE needs no pool storage).
|
||||
* @param poolOffset Unused (ENTITY_REMOVE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneEntityRemoveLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -30,9 +30,7 @@ bool_t cutsceneEntityTeleportUpdate(
|
||||
|
||||
errorret_t cutsceneEntityTeleportLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonGetEntityIndex(
|
||||
yyjson_obj_get(itemObj, "entityIndex"),
|
||||
|
||||
@@ -55,13 +55,9 @@ bool_t cutsceneEntityTeleportUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (ENTITY_TELEPORT needs no pool storage).
|
||||
* @param poolOffset Unused (ENTITY_TELEPORT needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneEntityTeleportLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -33,9 +33,7 @@ bool_t cutsceneEntityTurnUpdate(
|
||||
|
||||
errorret_t cutsceneEntityTurnLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonGetEntityIndex(
|
||||
yyjson_obj_get(itemObj, "entityIndex"), &item->entityTurn.entityIndex
|
||||
|
||||
@@ -55,13 +55,9 @@ bool_t cutsceneEntityTurnUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (ENTITY_TURN needs no pool storage).
|
||||
* @param poolOffset Unused (ENTITY_TURN needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneEntityTurnLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -10,12 +10,16 @@
|
||||
#include "rpg/entity/entitypathstep.h"
|
||||
#include "rpg/cutscene/item/json/cutscenejsonentityindex.h"
|
||||
#include "rpg/cutscene/item/json/cutscenejsonfield.h"
|
||||
#include "rpg/cutscene/item/json/cutscenejsonpool.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void cutsceneEntityWalkToStart(
|
||||
const cutsceneitem_t *item,
|
||||
cutsceneitemdata_t *data
|
||||
) {
|
||||
assertTrue(
|
||||
item->entityWalkTo.count <= CUTSCENE_ENTITY_WALK_TO_MAX,
|
||||
"Too many waypoints in CUTSCENE_ENTITY_WALK_TO"
|
||||
);
|
||||
data->entityWalkTo.currentIndex = 0;
|
||||
}
|
||||
|
||||
@@ -40,9 +44,7 @@ bool_t cutsceneEntityWalkToUpdate(
|
||||
|
||||
errorret_t cutsceneEntityWalkToLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonGetEntityIndex(
|
||||
yyjson_obj_get(itemObj, "entityIndex"), &item->entityWalkTo.entityIndex
|
||||
@@ -54,20 +56,22 @@ errorret_t cutsceneEntityWalkToLoad(
|
||||
|
||||
yyjson_val *positions = yyjson_obj_get(itemObj, "positions");
|
||||
size_t count = yyjson_arr_size(positions);
|
||||
if(!yyjson_is_arr(positions) || count == 0 || count > UINT8_MAX) {
|
||||
if(
|
||||
!yyjson_is_arr(positions) || count == 0 ||
|
||||
count > CUTSCENE_ENTITY_WALK_TO_MAX
|
||||
) {
|
||||
errorThrow(
|
||||
"Cutscene item (ENTITY_WALK_TO): 'positions' must be a non-empty "
|
||||
"array of [x, y, z]"
|
||||
"Cutscene item (ENTITY_WALK_TO): 'positions' must have 1-%u entries "
|
||||
"of [x, y, z]",
|
||||
(uint32_t)CUTSCENE_ENTITY_WALK_TO_MAX
|
||||
);
|
||||
}
|
||||
|
||||
*poolOffset = cutsceneJsonPoolAlign(*poolOffset);
|
||||
worldpos_t *dest = (worldpos_t *)(pool + *poolOffset);
|
||||
yyjson_arr_iter iter = yyjson_arr_iter_with(positions);
|
||||
yyjson_val *posVal;
|
||||
size_t i = 0;
|
||||
while((posVal = yyjson_arr_iter_next(&iter)) != NULL) {
|
||||
if(!cutsceneJsonReadWorldPos(posVal, &dest[i])) {
|
||||
if(!cutsceneJsonReadWorldPos(posVal, &item->entityWalkTo.positions[i])) {
|
||||
errorThrow(
|
||||
"Cutscene item (ENTITY_WALK_TO): position %u must be [x, y, z]",
|
||||
(uint32_t)i
|
||||
@@ -75,15 +79,7 @@ errorret_t cutsceneEntityWalkToLoad(
|
||||
}
|
||||
i++;
|
||||
}
|
||||
item->entityWalkTo.positions = dest;
|
||||
item->entityWalkTo.count = (uint8_t)count;
|
||||
*poolOffset += count * sizeof(worldpos_t);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
size_t cutsceneEntityWalkToPoolSize(yyjson_val *itemObj) {
|
||||
yyjson_val *positions = yyjson_obj_get(itemObj, "positions");
|
||||
if(!yyjson_is_arr(positions)) return 0;
|
||||
return yyjson_arr_size(positions) * sizeof(worldpos_t);
|
||||
}
|
||||
|
||||
@@ -9,9 +9,14 @@
|
||||
#include "rpg/overworld/worldpos.h"
|
||||
#include "rpg/cutscene/item/cutsceneitembase.h"
|
||||
|
||||
// Maximum number of waypoints a single ENTITY_WALK_TO/
|
||||
// CUTSCENE_ENTITY_WALK_PATH may hold - generous relative to every current
|
||||
// asset/C-authored cutscene (none use more than a couple waypoints).
|
||||
#define CUTSCENE_ENTITY_WALK_TO_MAX 16
|
||||
|
||||
typedef struct {
|
||||
uint8_t entityIndex;
|
||||
const worldpos_t *positions;
|
||||
worldpos_t positions[CUTSCENE_ENTITY_WALK_TO_MAX];
|
||||
uint8_t count;
|
||||
bool_t walkAround;
|
||||
} cutsceneentitywalkto_t;
|
||||
@@ -32,29 +37,29 @@ typedef struct {
|
||||
#define CUTSCENE_ENTITY_WALK_TO(ENTITY_INDEX, X, Y, Z) \
|
||||
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, entityWalkTo, { \
|
||||
.entityIndex = ENTITY_INDEX, \
|
||||
.positions = (const worldpos_t[]){ { X, Y, Z } }, \
|
||||
.positions = { { X, Y, Z } }, \
|
||||
.count = 1, \
|
||||
.walkAround = true \
|
||||
})
|
||||
|
||||
/**
|
||||
* Declares a standalone, named cutsceneitem_t (not a list entry) that
|
||||
* walks an entity through a fixed sequence of waypoints.
|
||||
* walks an entity through a fixed sequence of waypoints. ... must not
|
||||
* exceed CUTSCENE_ENTITY_WALK_TO_MAX entries - the compiler rejects the
|
||||
* initializer with "excess elements in array initializer" if it does.
|
||||
*
|
||||
* @param NAME Suffix for the generated CUTSCENE_##NAME item/positions
|
||||
* statics.
|
||||
* @param NAME Suffix for the generated CUTSCENE_##NAME static.
|
||||
* @param ENTITY_INDEX Entity index to move.
|
||||
* @param ... One or more worldpos_t-shaped waypoint initializers, e.g.
|
||||
* { X, Y, Z }, { X2, Y2, Z2 }.
|
||||
* { X, Y, Z }, { X2, Y2, Z2 } - at most CUTSCENE_ENTITY_WALK_TO_MAX.
|
||||
*/
|
||||
#define CUTSCENE_ENTITY_WALK_PATH(NAME, ENTITY_INDEX, ...) \
|
||||
static const worldpos_t CUTSCENE_##NAME##_POSITIONS[] = { __VA_ARGS__ }; \
|
||||
static const cutsceneitem_t CUTSCENE_##NAME = { \
|
||||
.type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \
|
||||
.entityWalkTo = { \
|
||||
.entityIndex = ENTITY_INDEX, \
|
||||
.positions = CUTSCENE_##NAME##_POSITIONS, \
|
||||
.count = sizeof(CUTSCENE_##NAME##_POSITIONS) / sizeof(worldpos_t), \
|
||||
.positions = { __VA_ARGS__ }, \
|
||||
.count = sizeof((const worldpos_t[]){ __VA_ARGS__ }) / sizeof(worldpos_t), \
|
||||
.walkAround = true \
|
||||
} \
|
||||
};
|
||||
@@ -84,26 +89,14 @@ bool_t cutsceneEntityWalkToUpdate(
|
||||
|
||||
/**
|
||||
* Parses an ENTITY_WALK_TO cutscene item's JSON fields into item, writing
|
||||
* its "positions" array into pool at *poolOffset and advancing it.
|
||||
* its "positions" array directly into item->entityWalkTo.positions (at
|
||||
* most CUTSCENE_ENTITY_WALK_TO_MAX entries).
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Base pointer of the entry's pool allocation.
|
||||
* @param poolOffset In/out cursor into pool.
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneEntityWalkToLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
/**
|
||||
* Computes how many pool bytes this ENTITY_WALK_TO item's "positions"
|
||||
* array will need.
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @return Pool bytes required (0 if "positions" is missing/not an array).
|
||||
*/
|
||||
size_t cutsceneEntityWalkToPoolSize(yyjson_val *itemObj);
|
||||
|
||||
@@ -43,9 +43,7 @@ bool_t cutsceneEntityWalkToEntityUpdate(
|
||||
|
||||
errorret_t cutsceneEntityWalkToEntityLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonGetEntityIndex(
|
||||
yyjson_obj_get(itemObj, "entityIndex"),
|
||||
|
||||
@@ -69,13 +69,9 @@ bool_t cutsceneEntityWalkToEntityUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (ENTITY_WALK_TO_ENTITY needs no pool storage).
|
||||
* @param poolOffset Unused (ENTITY_WALK_TO_ENTITY needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneEntityWalkToEntityLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneItemGiveUpdate(
|
||||
|
||||
errorret_t cutsceneItemGiveLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
item->itemGive.item =
|
||||
(itemid_t)yyjson_get_int(yyjson_obj_get(itemObj, "item"));
|
||||
|
||||
@@ -53,13 +53,9 @@ bool_t cutsceneItemGiveUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (ITEM_GIVE needs no pool storage).
|
||||
* @param poolOffset Unused (ITEM_GIVE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneItemGiveLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
cutscenejsonfield.c
|
||||
cutscenejsonentityindex.c
|
||||
cutscenejsonareaid.c
|
||||
cutscenejsonpool.c
|
||||
cutscenejsonpauseflags.c
|
||||
cutscenejsoneasing.c
|
||||
cutscenejsonemoji.c
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
/**
|
||||
* Parses a JSON array of pause flag name strings, OR-ing each into out.
|
||||
* Shared by the top-level "pause" field (assetCutsceneLoaderSync) and the
|
||||
* Shared by the top-level "pause" field (cutsceneParseDoc) and the
|
||||
* SET_PAUSE item's "flags" field - both authored as the same shape.
|
||||
*
|
||||
* @param arr JSON array of pause flag name strings.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "cutscenejsonpool.h"
|
||||
|
||||
size_t cutsceneJsonPoolAlign(const size_t size) {
|
||||
return (size + 3) & ~((size_t)3);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
/**
|
||||
* Rounds size up to the next multiple of 4 - every pool entry starts
|
||||
* 4-byte aligned so worldpos_t/multi-byte reads out of it never trap on
|
||||
* alignment-sensitive hardware (e.g. PSP's MIPS core).
|
||||
*
|
||||
* @param size Size to round up.
|
||||
* @return size rounded up to the next multiple of 4.
|
||||
*/
|
||||
size_t cutsceneJsonPoolAlign(const size_t size);
|
||||
@@ -26,9 +26,7 @@ bool_t cutsceneMapAreaRemoveUpdate(
|
||||
|
||||
errorret_t cutsceneMapAreaRemoveLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonGetAreaId(
|
||||
yyjson_obj_get(itemObj, "areaId"), &item->mapAreaRemove.areaId
|
||||
|
||||
@@ -52,13 +52,9 @@ bool_t cutsceneMapAreaRemoveUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (MAP_AREA_REMOVE needs no pool storage).
|
||||
* @param poolOffset Unused (MAP_AREA_REMOVE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneMapAreaRemoveLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "rpg/overworld/maparea.h"
|
||||
#include "assert/assert.h"
|
||||
#include "rpg/cutscene/item/json/cutscenejsonareaid.h"
|
||||
#include "rpg/cutscene/item/json/cutscenejsonpool.h"
|
||||
|
||||
void cutsceneMapAreaWaitStart(
|
||||
const cutsceneitem_t *item,
|
||||
@@ -43,9 +42,7 @@ bool_t cutsceneMapAreaWaitUpdate(
|
||||
|
||||
errorret_t cutsceneMapAreaWaitLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
yyjson_val *areaIds = yyjson_obj_get(itemObj, "areaIds");
|
||||
size_t count = yyjson_arr_size(areaIds);
|
||||
@@ -58,28 +55,18 @@ errorret_t cutsceneMapAreaWaitLoad(
|
||||
(uint32_t)CUTSCENE_MAP_AREA_WAIT_MAX
|
||||
);
|
||||
}
|
||||
*poolOffset = cutsceneJsonPoolAlign(*poolOffset);
|
||||
uint8_t *dest = pool + *poolOffset;
|
||||
yyjson_arr_iter iter = yyjson_arr_iter_with(areaIds);
|
||||
yyjson_val *idVal;
|
||||
size_t i = 0;
|
||||
while((idVal = yyjson_arr_iter_next(&iter)) != NULL) {
|
||||
if(!cutsceneJsonGetAreaId(idVal, &dest[i])) {
|
||||
if(!cutsceneJsonGetAreaId(idVal, &item->mapAreaWait.areaIds[i])) {
|
||||
errorThrow(
|
||||
"Cutscene item (MAP_AREA_WAIT): areaIds[%u] is invalid", (uint32_t)i
|
||||
);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
item->mapAreaWait.areaIds = dest;
|
||||
item->mapAreaWait.count = (uint8_t)count;
|
||||
*poolOffset += count;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
size_t cutsceneMapAreaWaitPoolSize(yyjson_val *itemObj) {
|
||||
yyjson_val *areaIds = yyjson_obj_get(itemObj, "areaIds");
|
||||
if(!yyjson_is_arr(areaIds)) return 0;
|
||||
return yyjson_arr_size(areaIds);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#define CUTSCENE_MAP_AREA_WAIT_MAX 4
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *areaIds;
|
||||
uint8_t areaIds[CUTSCENE_MAP_AREA_WAIT_MAX];
|
||||
uint8_t count;
|
||||
} cutscenemapareawait_t;
|
||||
|
||||
@@ -28,7 +28,7 @@ typedef struct {
|
||||
*/
|
||||
#define CUTSCENE_MAP_AREA_WAIT(...) \
|
||||
CUTSCENE_ITEM(CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT, mapAreaWait, { \
|
||||
.areaIds = (const uint8_t[]){ __VA_ARGS__ }, \
|
||||
.areaIds = { __VA_ARGS__ }, \
|
||||
.count = (uint8_t)( \
|
||||
sizeof((const uint8_t[]){ __VA_ARGS__ }) / sizeof(uint8_t) \
|
||||
) \
|
||||
@@ -61,26 +61,13 @@ bool_t cutsceneMapAreaWaitUpdate(
|
||||
|
||||
/**
|
||||
* Parses a MAP_AREA_WAIT cutscene item's JSON fields into item, writing its
|
||||
* "areaIds" array into pool at *poolOffset and advancing it.
|
||||
* "areaIds" array directly into item->mapAreaWait.areaIds.
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Base pointer of the entry's pool allocation.
|
||||
* @param poolOffset In/out cursor into pool.
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneMapAreaWaitLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
/**
|
||||
* Computes how many pool bytes this MAP_AREA_WAIT item's "areaIds" array
|
||||
* will need.
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @return Pool bytes required (0 if "areaIds" is missing/not an array).
|
||||
*/
|
||||
size_t cutsceneMapAreaWaitPoolSize(yyjson_val *itemObj);
|
||||
|
||||
@@ -34,9 +34,7 @@ bool_t cutsceneSaveDeviceCheckUpdate(
|
||||
|
||||
errorret_t cutsceneSaveDeviceCheckLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
const char_t *successMarker =
|
||||
yyjson_get_str(yyjson_obj_get(itemObj, "successMarker"));
|
||||
|
||||
@@ -62,13 +62,9 @@ bool_t cutsceneSaveDeviceCheckUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (SAVE_DEVICE_CHECK needs no pool storage).
|
||||
* @param poolOffset Unused (SAVE_DEVICE_CHECK needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneSaveDeviceCheckLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -32,9 +32,7 @@ bool_t cutsceneSaveLoadAllSlotsUpdate(
|
||||
|
||||
errorret_t cutsceneSaveLoadAllSlotsLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
const char_t *successMarker =
|
||||
yyjson_get_str(yyjson_obj_get(itemObj, "successMarker"));
|
||||
|
||||
@@ -61,13 +61,9 @@ bool_t cutsceneSaveLoadAllSlotsUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (SAVE_LOAD_ALL_SLOTS needs no pool storage).
|
||||
* @param poolOffset Unused (SAVE_LOAD_ALL_SLOTS needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneSaveLoadAllSlotsLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -29,9 +29,7 @@ bool_t cutsceneEmojiUpdate(
|
||||
|
||||
errorret_t cutsceneEmojiLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonGetEntityIndex(
|
||||
yyjson_obj_get(itemObj, "entityIndex"), &item->emoji.entityIndex
|
||||
|
||||
@@ -59,13 +59,9 @@ bool_t cutsceneEmojiUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (EMOJI needs no pool storage).
|
||||
* @param poolOffset Unused (EMOJI needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneEmojiLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -35,9 +35,7 @@ bool_t cutsceneFadeUpdate(
|
||||
|
||||
errorret_t cutsceneFadeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
yyjson_val *from = yyjson_obj_get(itemObj, "from");
|
||||
yyjson_val *to = yyjson_obj_get(itemObj, "to");
|
||||
|
||||
@@ -91,13 +91,9 @@ bool_t cutsceneFadeUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (FADE needs no pool storage).
|
||||
* @param poolOffset Unused (FADE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneFadeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -55,9 +55,7 @@ bool_t cutsceneModalCloseUpdate(
|
||||
|
||||
errorret_t cutsceneModalLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(yyjson_obj_get(itemObj, "options") != NULL) {
|
||||
errorThrow(
|
||||
|
||||
@@ -137,15 +137,11 @@ bool_t cutsceneModalUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (MODAL needs no pool storage).
|
||||
* @param poolOffset Unused (MODAL needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneModalLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,9 +52,7 @@ bool_t cutsceneModalOptionsMarkersUpdate(
|
||||
|
||||
errorret_t cutsceneModalOptionsMarkersLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
cutsceneJsonCopyString(
|
||||
itemObj, "title", "", item->modalOptionsMarkers.title,
|
||||
|
||||
@@ -102,13 +102,9 @@ bool_t cutsceneModalOptionsMarkersUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (MODAL_OPTIONS_MARKERS needs no pool storage).
|
||||
* @param poolOffset Unused (MODAL_OPTIONS_MARKERS needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneModalOptionsMarkersLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneShakeUpdate(
|
||||
|
||||
errorret_t cutsceneShakeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
item->shake.amount = (uint8_t)cutsceneJsonGetInt(itemObj, "amount", 0);
|
||||
item->shake.duration = cutsceneJsonGetFloat(itemObj, "duration", 0.0f);
|
||||
|
||||
@@ -54,13 +54,9 @@ bool_t cutsceneShakeUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (SHAKE needs no pool storage).
|
||||
* @param poolOffset Unused (SHAKE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneShakeLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ bool_t cutsceneTextUpdate(
|
||||
|
||||
errorret_t cutsceneTextLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonCopyString(
|
||||
itemObj, "text", NULL, item->text.text, CUTSCENE_TEXT_MAX_CHARS
|
||||
|
||||
@@ -50,13 +50,9 @@ bool_t cutsceneTextUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (TEXT needs no pool storage).
|
||||
* @param poolOffset Unused (TEXT needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneTextLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
@@ -35,9 +35,7 @@ bool_t cutsceneTextMiniUpdate(
|
||||
|
||||
errorret_t cutsceneTextMiniLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
if(!cutsceneJsonCopyString(
|
||||
itemObj, "text", NULL, item->textMini.text, CUTSCENE_TEXT_MINI_MAX_CHARS
|
||||
|
||||
@@ -62,13 +62,9 @@ bool_t cutsceneTextMiniUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (TEXT_MINI needs no pool storage).
|
||||
* @param poolOffset Unused (TEXT_MINI needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneTextMiniLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -26,9 +26,7 @@ bool_t cutsceneTextMiniHideUpdate(
|
||||
|
||||
errorret_t cutsceneTextMiniHideLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
item->textMiniHide.index =
|
||||
(uint8_t)yyjson_get_int(yyjson_obj_get(itemObj, "index"));
|
||||
|
||||
@@ -50,13 +50,9 @@ bool_t cutsceneTextMiniHideUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (TEXT_MINI_HIDE needs no pool storage).
|
||||
* @param poolOffset Unused (TEXT_MINI_HIDE needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneTextMiniHideLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -29,9 +29,7 @@ bool_t cutsceneUIShowUpdate(
|
||||
|
||||
errorret_t cutsceneUIShowLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
) {
|
||||
uiscreentype_t screenValue;
|
||||
if(!cutsceneJsonLookupUiScreenType(
|
||||
|
||||
@@ -58,13 +58,9 @@ bool_t cutsceneUIShowUpdate(
|
||||
*
|
||||
* @param itemObj The item's JSON object.
|
||||
* @param item Destination item, already zeroed by the caller.
|
||||
* @param pool Unused (UI_SHOW needs no pool storage).
|
||||
* @param poolOffset Unused (UI_SHOW needs no pool storage).
|
||||
* @return Error code indicating success or failure of the parse.
|
||||
*/
|
||||
errorret_t cutsceneUIShowLoad(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
uint8_t *pool,
|
||||
size_t *poolOffset
|
||||
cutsceneitem_t *item
|
||||
);
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "rpg/cutscene/cutscene.h"
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
|
||||
CUTSCENE(TEST_ONE, 0, DEFAULT,
|
||||
CUTSCENE(TEST_ONE, DEFAULT,
|
||||
CUTSCENE_TEXT("Test One."),
|
||||
);
|
||||
|
||||
CUTSCENE(TEST_TWO, 0, DEFAULT,
|
||||
CUTSCENE(TEST_TWO, DEFAULT,
|
||||
CUTSCENE_TEXT("Test Two."),
|
||||
CUTSCENE_ENTITY_ADD(ENTITY_TYPE_NPC, 4, 4, 0),
|
||||
CUTSCENE_TEXT_MINI("Hello!", 4, 4, 0, 3.0f),
|
||||
|
||||
@@ -24,8 +24,7 @@ static const cutsceneitem_t CUTSCENE_TEST_SCRATCH_ITEMS[] = {
|
||||
static const cutscene_t CUTSCENE_TEST_SCRATCH = {
|
||||
.items = CUTSCENE_TEST_SCRATCH_ITEMS,
|
||||
.itemCount = sizeof(CUTSCENE_TEST_SCRATCH_ITEMS) / sizeof(cutsceneitem_t),
|
||||
.pause = CUTSCENE_PAUSE_NONE,
|
||||
.dataSize = 0
|
||||
.pause = CUTSCENE_PAUSE_NONE
|
||||
};
|
||||
|
||||
static battlefighter_t *addFighter(
|
||||
|
||||
@@ -20,11 +20,11 @@ static void recordCallback(void *userData) {
|
||||
// the "nested cutscene is a one-way jump" behavior: cutsceneCutsceneStart
|
||||
// replaces CUTSCENE_SYSTEM.scene outright, and the callback item's effect
|
||||
// fires in Start, not Update, so it fires the same frame the jump happens.
|
||||
CUTSCENE(TEST_INNER, 0, NONE,
|
||||
CUTSCENE(TEST_INNER, NONE,
|
||||
CUTSCENE_CALLBACK(recordCallback)
|
||||
);
|
||||
|
||||
CUTSCENE(TEST_OUTER, 0, DEFAULT,
|
||||
CUTSCENE(TEST_OUTER, DEFAULT,
|
||||
CUTSCENE_WAIT(0.5f),
|
||||
CUTSCENE_CUTSCENE(TEST_INNER)
|
||||
);
|
||||
@@ -75,7 +75,7 @@ static void test_cutsceneSystemUpdateIsNoopWithNoActiveCutscene(void **state) {
|
||||
assert_null(CUTSCENE_SYSTEM.scene);
|
||||
}
|
||||
|
||||
CUTSCENE(TEST_SINGLE_WAIT, 0, NONE,
|
||||
CUTSCENE(TEST_SINGLE_WAIT, NONE,
|
||||
CUTSCENE_WAIT(1.0f)
|
||||
);
|
||||
|
||||
@@ -188,13 +188,13 @@ static void recordOrder2(void *userData) { insertOrderLog[insertOrderLogCount++]
|
||||
static void recordOrderA(void *userData) { insertOrderLog[insertOrderLogCount++] = 3; }
|
||||
static void recordOrderB(void *userData) { insertOrderLog[insertOrderLogCount++] = 4; }
|
||||
|
||||
CUTSCENE(TEST_INSERT_SNIPPET, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_SNIPPET, NONE,
|
||||
CUTSCENE_CALLBACK(recordOrderA),
|
||||
CUTSCENE_WAIT(2.0f),
|
||||
CUTSCENE_CALLBACK(recordOrderB)
|
||||
);
|
||||
|
||||
CUTSCENE(TEST_INSERT_OUTER, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_OUTER, NONE,
|
||||
CUTSCENE_CALLBACK(recordOrder0),
|
||||
CUTSCENE_WAIT(1.0f),
|
||||
CUTSCENE_CALLBACK(recordOrder1),
|
||||
@@ -230,11 +230,11 @@ static void test_cutsceneInsertSplicesItemsInPlaceThenResumes(void **state) {
|
||||
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
|
||||
}
|
||||
|
||||
CUTSCENE(TEST_INSERT_AS_LAST_ITEM_SNIPPET, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_AS_LAST_ITEM_SNIPPET, NONE,
|
||||
CUTSCENE_CALLBACK(recordOrderA)
|
||||
);
|
||||
|
||||
CUTSCENE(TEST_INSERT_AS_LAST_ITEM_OUTER, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_AS_LAST_ITEM_OUTER, NONE,
|
||||
CUTSCENE_CALLBACK(recordOrder0),
|
||||
CUTSCENE_INSERT(TEST_INSERT_AS_LAST_ITEM_SNIPPET)
|
||||
);
|
||||
@@ -263,19 +263,19 @@ static void test_cutsceneInsertAsLastItemEndsCutsceneNaturally(
|
||||
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
|
||||
}
|
||||
|
||||
CUTSCENE(TEST_INSERT_NESTED_INNER, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_NESTED_INNER, NONE,
|
||||
CUTSCENE_CALLBACK(recordOrderA)
|
||||
);
|
||||
|
||||
// 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.
|
||||
CUTSCENE(TEST_INSERT_NESTED_MID, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_NESTED_MID, NONE,
|
||||
CUTSCENE_INSERT(TEST_INSERT_NESTED_INNER),
|
||||
CUTSCENE_CALLBACK(recordOrderB)
|
||||
);
|
||||
|
||||
CUTSCENE(TEST_INSERT_NESTED_OUTER, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_NESTED_OUTER, NONE,
|
||||
CUTSCENE_CALLBACK(recordOrder0),
|
||||
CUTSCENE_INSERT(TEST_INSERT_NESTED_MID),
|
||||
CUTSCENE_CALLBACK(recordOrder2)
|
||||
@@ -306,22 +306,22 @@ static void test_cutsceneInsertNestsThroughMultipleLevels(void **state) {
|
||||
assert_memory_equal(insertOrderLog, expected, sizeof(expected));
|
||||
}
|
||||
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL5, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL5, NONE,
|
||||
CUTSCENE_CALLBACK(recordOrderA)
|
||||
);
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL4, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL4, NONE,
|
||||
CUTSCENE_INSERT(TEST_INSERT_OVERFLOW_LEVEL5)
|
||||
);
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL3, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL3, NONE,
|
||||
CUTSCENE_INSERT(TEST_INSERT_OVERFLOW_LEVEL4)
|
||||
);
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL2, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL2, NONE,
|
||||
CUTSCENE_INSERT(TEST_INSERT_OVERFLOW_LEVEL3)
|
||||
);
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL1, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL1, NONE,
|
||||
CUTSCENE_INSERT(TEST_INSERT_OVERFLOW_LEVEL2)
|
||||
);
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL0, 0, NONE,
|
||||
CUTSCENE(TEST_INSERT_OVERFLOW_LEVEL0, NONE,
|
||||
CUTSCENE_INSERT(TEST_INSERT_OVERFLOW_LEVEL1)
|
||||
);
|
||||
|
||||
@@ -341,11 +341,11 @@ static void test_cutsceneInsertAssertsWhenNestingExceedsStackMax(
|
||||
|
||||
static void noopInsertCallback(void *userData) {}
|
||||
|
||||
CUTSCENE(TEST_INSERT_PERSIST_SNIPPET, 0, ALL,
|
||||
CUTSCENE(TEST_INSERT_PERSIST_SNIPPET, ALL,
|
||||
CUTSCENE_CALLBACK(noopInsertCallback)
|
||||
);
|
||||
|
||||
CUTSCENE(TEST_INSERT_PERSIST_OUTER, 0, NPC,
|
||||
CUTSCENE(TEST_INSERT_PERSIST_OUTER, NPC,
|
||||
CUTSCENE_INSERT(TEST_INSERT_PERSIST_SNIPPET)
|
||||
);
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ static const cutsceneitem_t CUTSCENE_TEST_INTERACT_ITEMS[] = {
|
||||
static const cutscene_t CUTSCENE_TEST_INTERACT = {
|
||||
.items = CUTSCENE_TEST_INTERACT_ITEMS,
|
||||
.itemCount = 1,
|
||||
.pause = CUTSCENE_PAUSE_NONE,
|
||||
.dataSize = 0
|
||||
.pause = CUTSCENE_PAUSE_NONE
|
||||
};
|
||||
|
||||
static entity_t *player;
|
||||
|
||||
Reference in New Issue
Block a user