Fixed cutscene item cutscene trying to recursively load
This commit is contained in:
@@ -86,7 +86,7 @@ typedef struct cutscene_s {
|
|||||||
#define CUTSCENE_CUTSCENE(CUTSCENE) \
|
#define CUTSCENE_CUTSCENE(CUTSCENE) \
|
||||||
{ \
|
{ \
|
||||||
.type = CUTSCENE_ITEM_TYPE_CUTSCENE, \
|
.type = CUTSCENE_ITEM_TYPE_CUTSCENE, \
|
||||||
.cutscene = CUTSCENE_REFERENCE(CUTSCENE) \
|
.cutsceneRef = { .cutscene = CUTSCENE_REFERENCE(CUTSCENE), .name = NULL } \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CUTSCENE_CALLBACK(CALLBACK) \
|
#define CUTSCENE_CALLBACK(CALLBACK) \
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "asset/assetfile.h"
|
#include "asset/assetfile.h"
|
||||||
#include "asset/loader/assetloader.h"
|
#include "asset/loader/assetloader.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
#include "ui/overlay/uifatalerror.h"
|
||||||
|
|
||||||
cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
||||||
[CUTSCENE_ITEM_TYPE_NULL] = { 0 },
|
[CUTSCENE_ITEM_TYPE_NULL] = { 0 },
|
||||||
@@ -364,7 +365,14 @@ void cutsceneCutsceneStart(
|
|||||||
const cutsceneitem_t *item,
|
const cutsceneitem_t *item,
|
||||||
cutsceneitemdata_t *data
|
cutsceneitemdata_t *data
|
||||||
) {
|
) {
|
||||||
if(item->cutscene != NULL) cutsceneSystemStartCutscene(item->cutscene);
|
const cutscene_t *cutscene = item->cutsceneRef.cutscene;
|
||||||
|
if(cutscene == NULL && item->cutsceneRef.name != NULL) {
|
||||||
|
cutscene = cutsceneCutsceneResolve(item->cutsceneRef.name);
|
||||||
|
// Cache the resolved pointer back so replaying this same item (e.g.
|
||||||
|
// a looping intro) never re-resolves/re-locks the asset again.
|
||||||
|
((cutsceneitem_t *)item)->cutsceneRef.cutscene = cutscene;
|
||||||
|
}
|
||||||
|
if(cutscene != NULL) cutsceneSystemStartCutscene(cutscene);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool_t cutsceneCutsceneUpdate(
|
bool_t cutsceneCutsceneUpdate(
|
||||||
@@ -374,6 +382,21 @@ bool_t cutsceneCutsceneUpdate(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
errorret_t result = assetRequireLoaded(entry);
|
||||||
|
if(errorIsNotOk(result)) {
|
||||||
|
errorCatch(errorPrint(result));
|
||||||
|
uiFatalErrorOpen(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &entry->data.cutscene.cutscene;
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t cutsceneCutsceneLoad(
|
errorret_t cutsceneCutsceneLoad(
|
||||||
yyjson_val *itemObj,
|
yyjson_val *itemObj,
|
||||||
cutsceneitem_t *item,
|
cutsceneitem_t *item,
|
||||||
@@ -385,23 +408,16 @@ errorret_t cutsceneCutsceneLoad(
|
|||||||
errorThrow("Cutscene item (CUTSCENE): missing 'name'");
|
errorThrow("Cutscene item (CUTSCENE): missing 'name'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Referenced cutscene is a sibling asset (assets/cutscenes/<name>.jsonc),
|
// Only validate that the eventual "cutscenes/<name>.jsonc" path would
|
||||||
// resolved to a stable pointer right now rather than deferred to
|
// fit - the reference itself is not resolved/locked until this item
|
||||||
// runtime - assetRequireLoaded re-enters assetUpdate() safely from
|
// actually runs (see cutsceneCutsceneStart/cutsceneCutsceneResolve).
|
||||||
// within this sync load (see its ASSET_ENTRY_STATE_LOADING_SYNC case),
|
|
||||||
// and the entry is locked forever (never unlocked) so the pointer
|
|
||||||
// stays valid for the process lifetime, same convention as
|
|
||||||
// ui/screen/mainmenu/uimainmenu.c's MAIN_MENU_CUTSCENE_ENTRY.
|
|
||||||
char_t path[ASSET_FILE_NAME_MAX];
|
|
||||||
int32_t pathLen = stringFormat(NULL, 0, "cutscenes/%s.jsonc", name);
|
int32_t pathLen = stringFormat(NULL, 0, "cutscenes/%s.jsonc", name);
|
||||||
if(pathLen < 0 || (size_t)pathLen >= ASSET_FILE_NAME_MAX) {
|
if(pathLen < 0 || (size_t)pathLen >= ASSET_FILE_NAME_MAX) {
|
||||||
errorThrow("Cutscene item (CUTSCENE): 'name' too long");
|
errorThrow("Cutscene item (CUTSCENE): 'name' too long");
|
||||||
}
|
}
|
||||||
stringFormat(path, sizeof(path), "cutscenes/%s.jsonc", name);
|
|
||||||
|
|
||||||
assetentry_t *refEntry = assetLock(path, ASSET_LOADER_TYPE_CUTSCENE, NULL);
|
item->cutsceneRef.cutscene = NULL;
|
||||||
errorChain(assetRequireLoaded(refEntry));
|
item->cutsceneRef.name = name;
|
||||||
item->cutscene = &refEntry->data.cutscene.cutscene;
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,20 @@
|
|||||||
|
|
||||||
typedef struct cutscene_s cutscene_t;
|
typedef struct cutscene_s cutscene_t;
|
||||||
|
|
||||||
|
// A CUTSCENE item's target. C-authored cutscenes (the CUTSCENE_CUTSCENE
|
||||||
|
// macro in cutscene.h) resolve cutscene at compile time and leave name
|
||||||
|
// NULL. JSON-authored cutscenes (cutsceneCutsceneLoad) do the opposite -
|
||||||
|
// only name is set at parse time, since resolving/locking the referenced
|
||||||
|
// asset there would eagerly load every cutscene a cutscene merely
|
||||||
|
// references, even ones never played. cutsceneCutsceneStart resolves
|
||||||
|
// name to cutscene the first time this item actually runs (via
|
||||||
|
// cutsceneCutsceneResolve) and caches the result back into this field so
|
||||||
|
// replaying the same item never re-resolves it.
|
||||||
|
typedef struct {
|
||||||
|
const cutscene_t *cutscene;
|
||||||
|
const char_t *name;
|
||||||
|
} cutscenecutsceneref_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CUTSCENE_ITEM_TYPE_NULL,
|
CUTSCENE_ITEM_TYPE_NULL,
|
||||||
|
|
||||||
@@ -117,7 +131,7 @@ struct cutsceneitem_s {
|
|||||||
cutscenetextminihide_t textMiniHide;
|
cutscenetextminihide_t textMiniHide;
|
||||||
cutscenecallback_t callback;
|
cutscenecallback_t callback;
|
||||||
cutscenewait_t wait;
|
cutscenewait_t wait;
|
||||||
const cutscene_t *cutscene;
|
cutscenecutsceneref_t cutsceneRef;
|
||||||
cutsceneentityteleport_t entityTeleport;
|
cutsceneentityteleport_t entityTeleport;
|
||||||
cutsceneentitywalkto_t entityWalkTo;
|
cutsceneentitywalkto_t entityWalkTo;
|
||||||
cutscenefade_t fade;
|
cutscenefade_t fade;
|
||||||
@@ -235,7 +249,9 @@ bool_t cutsceneItemLookupType(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a nested-cutscene item, handing control over to the
|
* Starts a nested-cutscene item, handing control over to the
|
||||||
* referenced cutscene.
|
* referenced cutscene. If item->cutsceneRef.cutscene hasn't been
|
||||||
|
* resolved yet (a JSON-authored reference, only known by name so far),
|
||||||
|
* resolves and caches it first via cutsceneCutsceneResolve.
|
||||||
*
|
*
|
||||||
* @param item The cutscene item.
|
* @param item The cutscene item.
|
||||||
* @param data Runtime data storage.
|
* @param data Runtime data storage.
|
||||||
@@ -245,6 +261,22 @@ void cutsceneCutsceneStart(
|
|||||||
cutsceneitemdata_t *data
|
cutsceneitemdata_t *data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a JSON-authored CUTSCENE item's referenced cutscene by name,
|
||||||
|
* locking (forever - never unlocked, matching cutsceneCutsceneLoad's old
|
||||||
|
* convention) and blocking on assets/cutscenes/<name>.jsonc. Called
|
||||||
|
* lazily from cutsceneCutsceneStart the first time the item actually
|
||||||
|
* runs, not at parse time, so a cutscene referencing many others doesn't
|
||||||
|
* load every one of them up front. On failure, opens the fatal error
|
||||||
|
* overlay (matching cutsceneSystemLoad's runtime-failure handling) and
|
||||||
|
* returns NULL.
|
||||||
|
*
|
||||||
|
* @param name Bare cutscene name (without "cutscenes/" prefix or
|
||||||
|
* ".jsonc" suffix).
|
||||||
|
* @return The resolved cutscene, or NULL on failure.
|
||||||
|
*/
|
||||||
|
const cutscene_t * cutsceneCutsceneResolve(const char_t *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a nested-cutscene item. By the time this would run, control
|
* Updates a nested-cutscene item. By the time this would run, control
|
||||||
* has already moved on to the referenced cutscene, so this always
|
* has already moved on to the referenced cutscene, so this always
|
||||||
@@ -262,10 +294,12 @@ bool_t cutsceneCutsceneUpdate(
|
|||||||
/**
|
/**
|
||||||
* Parses a CUTSCENE cutscene item's JSON fields into item. Kept inline here
|
* Parses a CUTSCENE cutscene item's JSON fields into item. Kept inline here
|
||||||
* (rather than its own file) since it's tightly bound to the asset/loader
|
* (rather than its own file) since it's tightly bound to the asset/loader
|
||||||
* system itself - it resolves its 'name' to a sibling
|
* system itself. Only stores the 'name' string (which points into the
|
||||||
* assets/cutscenes/<name>.jsonc asset at parse time, mirroring how
|
* cutscene asset's still-alive yyjson doc, so no copy is needed) -
|
||||||
* cutsceneCutsceneStart/cutsceneCutsceneUpdate stay inline in
|
* resolving it to the referenced assets/cutscenes/<name>.jsonc asset is
|
||||||
* cutsceneitem.c rather than getting their own file.
|
* deferred to cutsceneCutsceneStart, the first time this item actually
|
||||||
|
* runs, so a cutscene referencing many others doesn't pay to load every
|
||||||
|
* one of them up front.
|
||||||
*
|
*
|
||||||
* @param itemObj The item's JSON object.
|
* @param itemObj The item's JSON object.
|
||||||
* @param item Destination item, already zeroed by the caller.
|
* @param item Destination item, already zeroed by the caller.
|
||||||
|
|||||||
Reference in New Issue
Block a user