Switch cutscene system to a set of pre deffed buffers
This commit is contained in:
@@ -27,17 +27,7 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
)
|
||||
|
||||
set(DUSK_BACKTRACE ON CACHE BOOL "Enable backtrace support for assert failures.")
|
||||
|
||||
# Records the file/line of every memoryAllocate() call into a fixed-size
|
||||
# tracking table (removed again on memoryFree()) so a leak still present at
|
||||
# engineDispose()'s memoryGetAllocatedCount() check can be traced back to
|
||||
# where it was allocated. Linux only for now.
|
||||
set(DUSK_TRACK_MEMORY OFF CACHE BOOL "Track memoryAllocate() call sites to help find memory leaks.")
|
||||
if(DUSK_TRACK_MEMORY)
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_TRACK_MEMORY
|
||||
)
|
||||
endif()
|
||||
set(DUSK_TRACK_MEMORY ON CACHE BOOL "Track memoryAllocate() call sites to help find memory leaks.")
|
||||
|
||||
# Define platform-specific macros.
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
@@ -56,5 +46,4 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_TIME_DYNAMIC
|
||||
DUSK_NETWORK_IPV6
|
||||
DUSK_THREAD_PTHREAD
|
||||
DUSK_TRACK_MEMORY
|
||||
)
|
||||
@@ -50,6 +50,12 @@ if(DUSK_BACKTRACE)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(DUSK_TRACK_MEMORY)
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_TRACK_MEMORY
|
||||
)
|
||||
endif()
|
||||
|
||||
# Includes
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
|
||||
@@ -76,20 +76,13 @@ size_t assetCutsceneComputePoolSize(yyjson_val *itemsArr) {
|
||||
return size;
|
||||
}
|
||||
|
||||
void assetCutsceneFreeParsed(assetcutsceneoutput_t *out) {
|
||||
if(out->items != NULL) {
|
||||
memoryFree(out->items);
|
||||
out->items = NULL;
|
||||
}
|
||||
if(out->pool != NULL) {
|
||||
memoryFree(out->pool);
|
||||
out->pool = NULL;
|
||||
}
|
||||
if(out->doc != NULL) {
|
||||
yyjson_doc_free(out->doc);
|
||||
out->doc = NULL;
|
||||
}
|
||||
}
|
||||
// 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");
|
||||
@@ -113,9 +106,10 @@ errorret_t assetCutsceneLoaderAsync(assetloading_t *loading) {
|
||||
}
|
||||
|
||||
size_t fileSize = (size_t)file->size;
|
||||
uint8_t *buffer = memoryAllocate(fileSize);
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
assetLoaderErrorChain(loading, assetFileRead(file, buffer, fileSize));
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileRead(file, CUTSCENE_FILE_BUFFER, fileSize)
|
||||
);
|
||||
assertTrue(
|
||||
file->lastRead == file->size,
|
||||
"Failed to read entire cutscene file."
|
||||
@@ -127,48 +121,55 @@ errorret_t assetCutsceneLoaderAsync(assetloading_t *loading) {
|
||||
// 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.
|
||||
assetcutsceneoutput_t *out = &loading->entry->data.cutscene;
|
||||
|
||||
yyjson_doc *doc = yyjson_read(
|
||||
(char *)buffer, fileSize,
|
||||
(char *)CUTSCENE_FILE_BUFFER, fileSize,
|
||||
YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS
|
||||
);
|
||||
memoryFree(buffer);
|
||||
|
||||
if(doc == NULL) {
|
||||
assetLoaderErrorThrow(loading, "Failed to parse cutscene JSON");
|
||||
}
|
||||
out->doc = doc;
|
||||
|
||||
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||
yyjson_val *itemsArr = yyjson_obj_get(root, "items");
|
||||
if(!yyjson_is_arr(itemsArr)) {
|
||||
assetCutsceneFreeParsed(out);
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(loading, "Cutscene JSON is missing an 'items' array");
|
||||
}
|
||||
|
||||
size_t itemCount = yyjson_arr_size(itemsArr);
|
||||
if(itemCount > UINT8_MAX) {
|
||||
assetCutsceneFreeParsed(out);
|
||||
assetLoaderErrorThrow(loading, "Cutscene has more than 255 items");
|
||||
if(itemCount > CUTSCENE_LOADED_ITEMS_MAX) {
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene has more than CUTSCENE_LOADED_ITEMS_MAX items"
|
||||
);
|
||||
}
|
||||
|
||||
cutscenepause_t pause = CUTSCENE_PAUSE_DEFAULT;
|
||||
yyjson_val *pauseArr = yyjson_obj_get(root, "pause");
|
||||
if(yyjson_is_arr(pauseArr)) {
|
||||
if(!cutsceneJsonParsePauseFlags(pauseArr, &pause)) {
|
||||
assetCutsceneFreeParsed(out);
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene JSON has an invalid 'pause' flag"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
out->items = memoryAllocate(itemCount * sizeof(cutsceneitem_t));
|
||||
memoryZero(out->items, itemCount * sizeof(cutsceneitem_t));
|
||||
|
||||
size_t poolSize = assetCutsceneComputePoolSize(itemsArr);
|
||||
out->pool = poolSize > 0 ? memoryAllocate(poolSize) : NULL;
|
||||
if(poolSize > CUTSCENE_LOADED_POOL_SIZE_MAX) {
|
||||
yyjson_doc_free(doc);
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Cutscene needs more pool space than CUTSCENE_LOADED_POOL_SIZE_MAX"
|
||||
);
|
||||
}
|
||||
|
||||
// Everything structural has validated by this point, so it's safe to
|
||||
// start overwriting the shared CUTSCENE_LOADED_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), so nothing keeps running against stale data.
|
||||
memoryZero(CUTSCENE_LOADED_ITEMS, itemCount * sizeof(cutsceneitem_t));
|
||||
|
||||
size_t poolOffset = 0;
|
||||
yyjson_arr_iter itemIter = yyjson_arr_iter_with(itemsArr);
|
||||
@@ -176,19 +177,24 @@ errorret_t assetCutsceneLoaderAsync(assetloading_t *loading) {
|
||||
uint8_t index = 0;
|
||||
while((itemVal = yyjson_arr_iter_next(&itemIter)) != NULL) {
|
||||
errorret_t itemResult = assetCutsceneParseItem(
|
||||
loading, itemVal, &out->items[index], out->pool, &poolOffset, index
|
||||
loading, itemVal, &CUTSCENE_LOADED_ITEMS[index],
|
||||
CUTSCENE_LOADED_POOL, &poolOffset, index
|
||||
);
|
||||
if(errorIsNotOk(itemResult)) {
|
||||
assetCutsceneFreeParsed(out);
|
||||
yyjson_doc_free(doc);
|
||||
errorChain(itemResult);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
out->cutscene.items = out->items;
|
||||
out->cutscene.itemCount = (uint8_t)itemCount;
|
||||
out->cutscene.pause = pause;
|
||||
out->cutscene.dataSize = 0;
|
||||
// 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.
|
||||
cutsceneLoadedSetDoc(doc);
|
||||
CUTSCENE_LOADED_SCENE.itemCount = (uint8_t)itemCount;
|
||||
CUTSCENE_LOADED_SCENE.pause = pause;
|
||||
CUTSCENE_LOADED_SCENE.dataSize = 0;
|
||||
|
||||
loading->loading.cutscene.state = ASSET_CUTSCENE_LOADING_STATE_PARSE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
@@ -224,7 +230,10 @@ errorret_t assetCutsceneDispose(assetentry_t *entry) {
|
||||
assertTrue(entry->type == ASSET_LOADER_TYPE_CUTSCENE, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
assetCutsceneFreeParsed(&entry->data.cutscene);
|
||||
// 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();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#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"
|
||||
@@ -33,10 +34,13 @@ typedef struct {
|
||||
} assetcutsceneloaderloading_t;
|
||||
|
||||
typedef struct {
|
||||
cutscene_t cutscene;
|
||||
cutsceneitem_t *items;
|
||||
uint8_t *pool;
|
||||
yyjson_doc *doc;
|
||||
// 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;
|
||||
|
||||
/**
|
||||
@@ -67,22 +71,15 @@ errorret_t assetCutsceneParseItem(
|
||||
const uint8_t index
|
||||
);
|
||||
|
||||
/**
|
||||
* Frees whatever of out->items/out->pool/out->doc is currently non-NULL
|
||||
* and clears them - shared by both the normal disposer and every parse
|
||||
* failure path's cleanup.
|
||||
*
|
||||
* @param out The output to free.
|
||||
*/
|
||||
void assetCutsceneFreeParsed(assetcutsceneoutput_t *out);
|
||||
|
||||
/**
|
||||
* Asynchronous loader for cutscene assets. Reads the raw JSONC file bytes
|
||||
* and, in the same call, parses them into a heap-allocated cutsceneitem_t
|
||||
* array + pool - 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.
|
||||
* and, in the same call, parses them directly into the fixed, shared
|
||||
* CUTSCENE_LOADED_ITEMS/CUTSCENE_LOADED_POOL buffers (see cutscenesystem.h)
|
||||
* - no heap allocation at all, since only one cutscene is ever resident.
|
||||
* 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.
|
||||
|
||||
@@ -15,6 +15,21 @@
|
||||
|
||||
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
|
||||
};
|
||||
static yyjson_doc *CUTSCENE_LOADED_DOC = NULL;
|
||||
|
||||
void cutsceneLoadedSetDoc(yyjson_doc *doc) {
|
||||
if(CUTSCENE_LOADED_DOC != NULL) yyjson_doc_free(CUTSCENE_LOADED_DOC);
|
||||
CUTSCENE_LOADED_DOC = doc;
|
||||
}
|
||||
|
||||
void cutsceneSystemInit() {
|
||||
memoryZero(&CUTSCENE_SYSTEM, sizeof(cutscenesystem_t));
|
||||
}
|
||||
@@ -69,13 +84,18 @@ void cutsceneSystemLoad(const char_t *file) {
|
||||
|
||||
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_CUTSCENE, 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)) {
|
||||
errorCatch(errorPrint(result));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
cutsceneSystemStartCutscene(&entry->data.cutscene.cutscene);
|
||||
cutsceneSystemStartCutscene(&CUTSCENE_LOADED_SCENE);
|
||||
}
|
||||
|
||||
void cutsceneRestart(void) {
|
||||
@@ -259,4 +279,6 @@ void cutsceneSystemDispose() {
|
||||
CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED;
|
||||
CUTSCENE_SYSTEM.textCache[0] = '\0';
|
||||
CUTSCENE_SYSTEM.onComplete = NULL;
|
||||
|
||||
cutsceneLoadedSetDoc(NULL);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,39 @@ typedef struct entity_s entity_t;
|
||||
// CUTSCENE_ITEM_TYPE_KEYBOARD item is never truncated caching it here.
|
||||
#define CUTSCENE_TEXT_CACHE_MAX 64
|
||||
|
||||
// Only one JSON-authored cutscene is ever loaded/playing at a time - a
|
||||
// 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.
|
||||
#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.
|
||||
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.
|
||||
*
|
||||
* @param doc The new doc to take ownership of (NULL just frees/clears the
|
||||
* current one).
|
||||
*/
|
||||
void cutsceneLoadedSetDoc(yyjson_doc *doc);
|
||||
|
||||
typedef struct {
|
||||
const cutscene_t *scene;
|
||||
uint8_t currentItem;
|
||||
|
||||
@@ -373,10 +373,13 @@ void cutsceneCutsceneStart(
|
||||
) {
|
||||
const cutscene_t *cutscene = item->cutsceneRef.cutscene;
|
||||
if(cutscene == NULL && item->cutsceneRef.name != NULL) {
|
||||
// Not cached back into item - with only one shared, fixed load buffer
|
||||
// (see cutscenesystem.h's CUTSCENE_LOADED_ITEMS_MAX doc comment),
|
||||
// every JSON-authored reference resolves to the same fixed address
|
||||
// every time anyway, and item itself lives inside that very buffer -
|
||||
// caching a pointer into it here would go stale (or, worse, get
|
||||
// clobbered mid-resolve) the moment a later load overwrites it.
|
||||
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);
|
||||
}
|
||||
@@ -394,13 +397,16 @@ const cutscene_t * cutsceneCutsceneResolve(const char_t *name) {
|
||||
|
||||
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_CUTSCENE, 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)) {
|
||||
errorCatch(errorPrint(result));
|
||||
uiFatalErrorOpen(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &entry->data.cutscene.cutscene;
|
||||
return &CUTSCENE_LOADED_SCENE;
|
||||
}
|
||||
|
||||
errorret_t cutsceneCutsceneLoad(
|
||||
|
||||
@@ -22,8 +22,8 @@ ENTITY_GLOBAL_CALLBACK(3) {
|
||||
}
|
||||
|
||||
ENTITY_GLOBAL_CALLBACK(4) {
|
||||
create->entity->interact.type = ENTITY_INTERACT_CUTSCENE;
|
||||
create->entity->interact.data.cutscene = cutsceneCutsceneResolve("battletest");
|
||||
create->entity->interact.type = ENTITY_INTERACT_CUTSCENE_NAME;
|
||||
create->entity->interact.data.cutsceneName = "battletest";
|
||||
}
|
||||
|
||||
static const entityglobaldef_t ENTITY_GLOBAL_LIST[] = {
|
||||
|
||||
@@ -27,6 +27,15 @@ void entityInteractWith(entity_t *player, entity_t *target) {
|
||||
);
|
||||
break;
|
||||
|
||||
case ENTITY_INTERACT_CUTSCENE_NAME: {
|
||||
const cutscene_t *cutscene =
|
||||
cutsceneCutsceneResolve(target->interact.data.cutsceneName);
|
||||
if(cutscene != NULL) {
|
||||
cutsceneSystemStartCutsceneWith(cutscene, player, target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ENTITY_INTERACT_PRINT:
|
||||
uiTextboxMainSetText(target->interact.data.message);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef enum {
|
||||
ENTITY_INTERACT_NULL = 0,
|
||||
|
||||
ENTITY_INTERACT_CUTSCENE,
|
||||
ENTITY_INTERACT_CUTSCENE_NAME,
|
||||
ENTITY_INTERACT_PRINT,
|
||||
ENTITY_INTERACT_CALLBACK,
|
||||
|
||||
@@ -28,7 +29,18 @@ typedef enum {
|
||||
* Per-type data for an entity's interact component.
|
||||
*/
|
||||
typedef union {
|
||||
// A compile-time cutscene_t* (e.g. CUTSCENE_REFERENCE(...)) - safe to
|
||||
// hold onto indefinitely, since C-authored cutscenes are compiled
|
||||
// directly into their own static const arrays, never the shared
|
||||
// CUTSCENE_LOADED_ITEMS buffer (see cutscenesystem.h).
|
||||
const cutscene_t *cutscene;
|
||||
// A bare cutscene name (as passed to cutsceneCutsceneResolve), resolved
|
||||
// lazily at interact time rather than once up front - only one
|
||||
// JSON-authored cutscene is ever resident at a time (see
|
||||
// CUTSCENE_LOADED_ITEMS_MAX's doc comment), so resolving early and
|
||||
// holding onto the result here would go stale the moment any other
|
||||
// cutscene loads in the meantime.
|
||||
const char_t *cutsceneName;
|
||||
const char_t *message;
|
||||
void (*callback)(entity_t *player, entity_t *target);
|
||||
} entityinteractdata_t;
|
||||
|
||||
@@ -26,21 +26,15 @@
|
||||
#define UI_MAIN_MENU_INDEX_QUIT 2
|
||||
|
||||
uimainmenu_t UI_MAIN_MENU;
|
||||
assetentry_t *MAIN_MENU_CUTSCENE_ENTRY = NULL;
|
||||
|
||||
cutscene_t *uiMainMenuLoadCutscene(void) {
|
||||
if(MAIN_MENU_CUTSCENE_ENTRY == NULL) {
|
||||
MAIN_MENU_CUTSCENE_ENTRY = assetLock(
|
||||
"cutscenes/main_menu.jsonc", ASSET_LOADER_TYPE_CUTSCENE, NULL
|
||||
);
|
||||
}
|
||||
errorret_t result = assetRequireLoaded(MAIN_MENU_CUTSCENE_ENTRY);
|
||||
if(errorIsNotOk(result)) {
|
||||
errorCatch(errorPrint(result));
|
||||
assertTrue(false, "Failed to load main menu cutscene asset");
|
||||
}
|
||||
|
||||
return &MAIN_MENU_CUTSCENE_ENTRY->data.cutscene.cutscene;
|
||||
const cutscene_t *uiMainMenuLoadCutscene(void) {
|
||||
// Delegates entirely to cutsceneCutsceneResolve rather than keeping our
|
||||
// own locked-forever entry, since only one JSON-authored cutscene is
|
||||
// ever resident at a time (see CUTSCENE_LOADED_ITEMS_MAX's doc comment
|
||||
// in cutscenesystem.h) - caching an entry pointer here across calls
|
||||
// would go stale the moment any other cutscene loads in the meantime.
|
||||
// May return NULL on failure (already reported via uiFatalErrorOpen).
|
||||
return cutsceneCutsceneResolve("main_menu");
|
||||
}
|
||||
|
||||
void uiMainMenuSelectSaveResult(const uint8_t slotIndex, void *user) {
|
||||
@@ -79,9 +73,9 @@ void uiMainMenuOpenSelectSave(void *userData) {
|
||||
|
||||
void uiMainMenuStartGame(void) {
|
||||
if(CUTSCENE_SYSTEM.scene == NULL) {
|
||||
cutsceneSystemStartCutsceneAndGoToMarker(
|
||||
uiMainMenuLoadCutscene(), "NEW_GAME"
|
||||
);
|
||||
const cutscene_t *cutscene = uiMainMenuLoadCutscene();
|
||||
if(cutscene == NULL) return;
|
||||
cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "NEW_GAME");
|
||||
} else {
|
||||
cutsceneGoTo("NEW_GAME");
|
||||
}
|
||||
@@ -91,9 +85,9 @@ void uiMainMenuStartGame(void) {
|
||||
|
||||
void uiMainMenuOptions(void) {
|
||||
if(CUTSCENE_SYSTEM.scene == NULL) {
|
||||
cutsceneSystemStartCutsceneAndGoToMarker(
|
||||
uiMainMenuLoadCutscene(), "OPTIONS"
|
||||
);
|
||||
const cutscene_t *cutscene = uiMainMenuLoadCutscene();
|
||||
if(cutscene == NULL) return;
|
||||
cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "OPTIONS");
|
||||
} else {
|
||||
cutsceneGoTo("OPTIONS");
|
||||
}
|
||||
@@ -101,7 +95,9 @@ void uiMainMenuOptions(void) {
|
||||
|
||||
void uiMainMenuQuit(void) {
|
||||
if(CUTSCENE_SYSTEM.scene == NULL) {
|
||||
cutsceneSystemStartCutsceneAndGoToMarker(uiMainMenuLoadCutscene(), "QUIT");
|
||||
const cutscene_t *cutscene = uiMainMenuLoadCutscene();
|
||||
if(cutscene == NULL) return;
|
||||
cutsceneSystemStartCutsceneAndGoToMarker(cutscene, "QUIT");
|
||||
} else {
|
||||
cutsceneGoTo("QUIT");
|
||||
}
|
||||
|
||||
@@ -39,17 +39,18 @@ errorret_t uiMainMenuInit(void);
|
||||
void uiMainMenuOpen(void);
|
||||
|
||||
/**
|
||||
* Lazily locks/loads the main menu cutscene asset (assets/cutscenes/
|
||||
* Loads (or re-loads) the main menu cutscene asset (assets/cutscenes/
|
||||
* main_menu.jsonc, parsed directly at load time rather than compiled in)
|
||||
* and returns it, asserting on failure. Kept resident for the rest of the
|
||||
* process once loaded - the persistent cutscene is what actually drives
|
||||
* this whole screen (opening this panel is its first item's job, see
|
||||
* UI_SHOW/rpg/cutscene/item/ui/cutsceneuishow.h), this is just the shared
|
||||
* accessor for the "Start Game" flow below to jump/restart it.
|
||||
* and returns it - the shared accessor for the "Start Game"/Options/Quit
|
||||
* flows below to jump/restart it. Only one JSON-authored cutscene is ever
|
||||
* resident at a time (see CUTSCENE_LOADED_ITEMS_MAX's doc comment in
|
||||
* cutscenesystem.h), so this resolves fresh on every call rather than
|
||||
* caching anything.
|
||||
*
|
||||
* @return The main menu cutscene.
|
||||
* @return The main menu cutscene, or NULL on failure (already reported via
|
||||
* uiFatalErrorOpen).
|
||||
*/
|
||||
cutscene_t *uiMainMenuLoadCutscene(void);
|
||||
const cutscene_t *uiMainMenuLoadCutscene(void);
|
||||
|
||||
/**
|
||||
* Starts the "Start Game" flow: jumps the already-running main menu
|
||||
|
||||
Reference in New Issue
Block a user